Merge pull request #3436 from millmanorama/cancellable-graph-loading

Cancellable graph loading
This commit is contained in:
Richard Cordovano 2018-02-14 13:36:04 -05:00 committed by GitHub
commit 4ba29921ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 151 additions and 268 deletions

View File

@ -24,7 +24,6 @@ VisualizationPanel.jButton6.text=Hierarchy
VisualizationPanel.jButton7.text=Circle VisualizationPanel.jButton7.text=Circle
VisualizationPanel.jButton8.text=Organic VisualizationPanel.jButton8.text=Organic
VisualizationPanel.fitGraphButton.text= VisualizationPanel.fitGraphButton.text=
VisualizationPanel.statusLabel.text=\
VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin.
VisualizationPanel.jLabel1.text=Layouts: VisualizationPanel.jLabel1.text=Layouts:
VisualizationPanel.fitZoomButton.text= VisualizationPanel.fitZoomButton.text=

View File

@ -40,6 +40,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
@ -51,25 +52,25 @@ import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
final class mxGraphImpl extends mxGraph { final class CommunicationsGraph extends mxGraph {
private static final Logger logger = Logger.getLogger(CommunicationsGraph.class.getName());
private static final URL MARKER_PIN_URL = CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png");
private static final URL LOCK_URL = CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/lock_large_locked.png");
private static final Logger logger = Logger.getLogger(mxGraphImpl.class.getName());
private static final URL MARKER_PIN_URL = mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png");
private static final URL LOCK_URL = mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/lock_large_locked.png");
/** /**
* mustache.java template * mustache.java template
*/ */
private final static Mustache labelMustache; private final static Mustache labelMustache;
static { static {
// String labelTemplatePath = "/org/sleuthkit/autopsy/communications/Vertex_Label_template.html"; InputStream templateStream = CommunicationsGraph.class.getResourceAsStream("/org/sleuthkit/autopsy/communications/Vertex_Label_template.html");
InputStream templateStream = mxGraphImpl.class.getResourceAsStream("/org/sleuthkit/autopsy/communications/Vertex_Label_template.html");
labelMustache = new DefaultMustacheFactory().compile(new InputStreamReader(templateStream), "Vertex_Label"); labelMustache = new DefaultMustacheFactory().compile(new InputStreamReader(templateStream), "Vertex_Label");
} }
static final private mxStylesheet mxStylesheet = new mxStylesheet(); static final private mxStylesheet mxStylesheet = new mxStylesheet();
private final HashSet<AccountDeviceInstanceKey> pinnedAccountDevices = new HashSet<>(); private final Set<AccountDeviceInstanceKey> pinnedAccountDevices = new HashSet<>();
private final HashSet<mxCell> lockedVertices = new HashSet<>(); private final Set<mxCell> lockedVertices = new HashSet<>();
private final Map<String, mxCell> nodeMap = new HashMap<>(); private final Map<String, mxCell> nodeMap = new HashMap<>();
private final Multimap<Content, mxCell> edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); private final Multimap<Content, mxCell> edgeMap = MultimapBuilder.hashKeys().hashSetValues().build();
@ -87,7 +88,7 @@ final class mxGraphImpl extends mxGraph {
mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE);
} }
mxGraphImpl() { CommunicationsGraph() {
super(mxStylesheet); super(mxStylesheet);
setAutoSizeCells(true); setAutoSizeCells(true);
setCellsCloneable(false); setCellsCloneable(false);
@ -129,7 +130,7 @@ final class mxGraphImpl extends mxGraph {
scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID());
scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5)); scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5));
scopes.put("iconFileName", mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/"
+ Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType())));
scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); scopes.put("pinned", pinnedAccountDevices.contains(adiKey));
scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL);
@ -155,7 +156,7 @@ final class mxGraphImpl extends mxGraph {
scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID());
scopes.put("size", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5)); scopes.put("size", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5));
scopes.put("iconFileName", mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/"
+ Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType())));
scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); scopes.put("pinned", pinnedAccountDevices.contains(adiKey));
scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL);
@ -264,7 +265,6 @@ final class mxGraphImpl extends mxGraph {
if (edgesBetween.length == 0) { if (edgesBetween.length == 0) {
final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); final String edgeName = vertex1.getId() + " <-> " + vertex2.getId();
final HashSet<Content> hashSet = new HashSet<>(relSources); final HashSet<Content> hashSet = new HashSet<>(relSources);
// edgeMap.put(relSource, edge);
edge = (mxCell) insertEdge(getDefaultParent(), edgeName, hashSet, vertex1, vertex2, edge = (mxCell) insertEdge(getDefaultParent(), edgeName, hashSet, vertex1, vertex2,
"strokeWidth=" + (Math.log(hashSet.size()) + 1)); "strokeWidth=" + (Math.log(hashSet.size()) + 1));
} else { } else {
@ -304,11 +304,13 @@ final class mxGraphImpl extends mxGraph {
this.progress = progress; this.progress = progress;
this.currentFilter = currentFilter; this.currentFilter = currentFilter;
this.commsManager = commsManager; this.commsManager = commsManager;
} }
@Override @Override
protected Void doInBackground() throws Exception { protected Void doInBackground() throws Exception {
progress.start("Loading accounts", pinnedAccountDevices.size()); progress.start("Loading accounts");
// progress.switchToDeterminate("Loading accounts", 0,pinnedAccountDevices.size());
int i = 0; int i = 0;
try { try {
/** /**
@ -316,6 +318,9 @@ final class mxGraphImpl extends mxGraph {
*/ */
Set<AccountDeviceInstanceKey> relatedAccounts = new HashSet<>(); Set<AccountDeviceInstanceKey> relatedAccounts = new HashSet<>();
for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) {
if (isCancelled()) {
break;
}
List<AccountDeviceInstance> relatedAccountDeviceInstances = List<AccountDeviceInstance> relatedAccountDeviceInstances =
commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter);
relatedAccounts.add(adiKey); relatedAccounts.add(adiKey);
@ -336,7 +341,9 @@ final class mxGraphImpl extends mxGraph {
for (i = 0; i < relatedAccountsList.size(); i++) { for (i = 0; i < relatedAccountsList.size(); i++) {
AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i);
for (int j = i; j < relatedAccountsList.size(); j++) { for (int j = i; j < relatedAccountsList.size(); j++) {
if (isCancelled()) {
break;
}
AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j);
List<Content> relationships = commsManager.getRelationshipSources( List<Content> relationships = commsManager.getRelationshipSources(
adiKey1.getAccountDeviceInstance(), adiKey1.getAccountDeviceInstance(),
@ -353,6 +360,7 @@ final class mxGraphImpl extends mxGraph {
logger.log(Level.SEVERE, "Error", tskCoreException); logger.log(Level.SEVERE, "Error", tskCoreException);
} finally { } finally {
} }
return null; return null;
} }
@ -363,6 +371,8 @@ final class mxGraphImpl extends mxGraph {
get(); get();
} catch (InterruptedException | ExecutionException ex) { } catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Error building graph visualization. ", ex); logger.log(Level.SEVERE, "Error building graph visualization. ", ex);
} catch (CancellationException ex) {
logger.log(Level.INFO, "Graph visualization cancelled");
} finally { } finally {
progress.finish(); progress.finish();
} }

View File

@ -11,7 +11,7 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-7,0,0,3,-119"/> <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-1,0,0,3,-123"/>
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
@ -38,65 +38,6 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents> <SubComponents>
<Container class="javax.swing.JToolBar" name="jToolBar2">
<Properties>
<Property name="floatable" type="boolean" value="false"/>
<Property name="rollover" type="boolean" value="true"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Last"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="statusLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.statusLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="progresPanel">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="447" max="32767" attributes="0"/>
<Component id="progressBar" min="-2" pref="350" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="progressBar" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JProgressBar" name="progressBar">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 14]"/>
</Property>
<Property name="stringPainted" type="boolean" value="true"/>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="placeHolderPanel"> <Container class="javax.swing.JPanel" name="placeHolderPanel">
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
@ -108,18 +49,18 @@
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<EmptySpace pref="213" max="32767" attributes="0"/> <EmptySpace pref="208" max="32767" attributes="0"/>
<Component id="jTextArea1" min="-2" pref="372" max="-2" attributes="0"/> <Component id="jTextArea1" min="-2" pref="372" max="-2" attributes="0"/>
<EmptySpace pref="214" max="32767" attributes="0"/> <EmptySpace pref="209" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<EmptySpace pref="200" max="32767" attributes="0"/> <EmptySpace pref="213" max="32767" attributes="0"/>
<Component id="jTextArea1" min="-2" pref="43" max="-2" attributes="0"/> <Component id="jTextArea1" min="-2" pref="43" max="-2" attributes="0"/>
<EmptySpace pref="200" max="32767" attributes="0"/> <EmptySpace pref="214" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>

View File

@ -34,17 +34,19 @@ import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxEventSource;
import com.mxgraph.util.mxPoint; import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle; import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUndoManager;
import com.mxgraph.util.mxUndoableEdit;
import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraph;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelEvent;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException; import java.beans.PropertyVetoException;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.Arrays; import java.util.Arrays;
@ -52,15 +54,16 @@ import static java.util.Collections.singleton;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Future;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JSplitPane; import javax.swing.JSplitPane;
import javax.swing.JTextArea; import javax.swing.JTextArea;
import javax.swing.JToolBar; import javax.swing.JToolBar;
@ -78,7 +81,8 @@ import org.openide.util.lookup.ProxyLookup;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.CommunicationsManager;
@ -94,6 +98,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* CVTTopComponent when this tab is active allowing for context sensitive * CVTTopComponent when this tab is active allowing for context sensitive
* actions to work correctly. * actions to work correctly.
*/ */
@NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel")
final public class VisualizationPanel extends JPanel implements Lookup.Provider { final public class VisualizationPanel extends JPanel implements Lookup.Provider {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -110,27 +115,35 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
static final private ImageIcon lockIcon = static final private ImageIcon lockIcon =
new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_locked.png")); new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_locked.png"));
private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
private final ExplorerManager vizEM = new ExplorerManager(); private final ExplorerManager vizEM = new ExplorerManager();
private final ExplorerManager gacEM = new ExplorerManager(); private final ExplorerManager gacEM = new ExplorerManager();
private final ProxyLookup proxyLookup = new ProxyLookup( private final ProxyLookup proxyLookup = new ProxyLookup(
ExplorerUtils.createLookup(gacEM, getActionMap()), ExplorerUtils.createLookup(gacEM, getActionMap()),
ExplorerUtils.createLookup(vizEM, getActionMap())); ExplorerUtils.createLookup(vizEM, getActionMap()));
private final mxGraphComponent graphComponent; private Frame windowAncestor;
private final mxGraphImpl graph;
private CommunicationsManager commsManager; private CommunicationsManager commsManager;
private CommunicationsFilter currentFilter; private CommunicationsFilter currentFilter;
private final mxGraphComponent graphComponent;
private final CommunicationsGraph graph;
protected mxUndoManager undoManager = new mxUndoManager();
private final mxRubberband rubberband; private final mxRubberband rubberband;
private final mxFastOrganicLayout fastOrganicLayout; private final mxFastOrganicLayout fastOrganicLayout;
private final mxCircleLayout circleLayout; private final mxCircleLayout circleLayout;
private final mxOrganicLayout organicLayout; private final mxOrganicLayout organicLayout;
private final mxHierarchicalLayout hierarchicalLayout; private final mxHierarchicalLayout hierarchicalLayout;
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private SwingWorker<?, ?> worker; private SwingWorker<?, ?> worker;
public VisualizationPanel() { public VisualizationPanel() {
initComponents(); initComponents();
graph = new mxGraphImpl(); graph = new CommunicationsGraph();
fastOrganicLayout = new mxFastOrganicLayoutImpl(graph); fastOrganicLayout = new mxFastOrganicLayoutImpl(graph);
circleLayout = new mxCircleLayoutImpl(graph); circleLayout = new mxCircleLayoutImpl(graph);
@ -148,7 +161,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
graphComponent.setToolTips(true); graphComponent.setToolTips(true);
graphComponent.setBackground(Color.WHITE); graphComponent.setBackground(Color.WHITE);
borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
progressBar.setVisible(false);
//install rubber band selection handler //install rubber band selection handler
rubberband = new mxRubberband(graphComponent); rubberband = new mxRubberband(graphComponent);
@ -176,10 +188,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
public void mouseClicked(MouseEvent e) { public void mouseClicked(MouseEvent e) {
super.mouseClicked(e); super.mouseClicked(e);
if (SwingUtilities.isRightMouseButton(e)) { if (SwingUtilities.isRightMouseButton(e)) {
mxCell cellAt = (mxCell) graphComponent.getCellAt(e.getX(), e.getY()); final mxCell cellAt = (mxCell) graphComponent.getCellAt(e.getX(), e.getY());
if (cellAt != null && cellAt.isVertex()) { if (cellAt != null && cellAt.isVertex()) {
JPopupMenu jPopupMenu = new JPopupMenu(); final JPopupMenu jPopupMenu = new JPopupMenu();
AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue(); final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
if (graph.isVertexLocked(cellAt)) { if (graph.isVertexLocked(cellAt)) {
jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock " + cellAt.getId(), unlockIcon) { jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock " + cellAt.getId(), unlockIcon) {
@ -188,7 +200,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
graph.unlockVertex(cellAt); graph.unlockVertex(cellAt);
} }
})); }));
} else { } else {
jPopupMenu.add(new JMenuItem(new AbstractAction("Lock " + cellAt.getId(), lockIcon) { jPopupMenu.add(new JMenuItem(new AbstractAction("Lock " + cellAt.getId(), lockIcon) {
@Override @Override
@ -196,7 +207,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
graph.lockVertex(cellAt); graph.lockVertex(cellAt);
} }
})); }));
} }
if (graph.isAccountPinned(adiKey)) { if (graph.isAccountPinned(adiKey)) {
jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin " + cellAt.getId(), unpinIcon) { jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin " + cellAt.getId(), unpinIcon) {
@ -206,7 +216,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
} }
})); }));
} else { } else {
jPopupMenu.add(new JMenuItem(new AbstractAction("Pin " + cellAt.getId(), addPinIcon) { jPopupMenu.add(new JMenuItem(new AbstractAction("Pin " + cellAt.getId(), addPinIcon) {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
@ -220,7 +229,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
} }
})); }));
} }
jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY()); jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY());
} }
} }
@ -231,6 +239,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
//feed selection to explorermanager //feed selection to explorermanager
graph.getSelectionModel().addListener(null, new SelectionListener()); graph.getSelectionModel().addListener(null, new SelectionListener());
final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt) ->
undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty("edit"));
graph.getModel().addListener(mxEvent.UNDO, undoListener);
graph.getView().addListener(mxEvent.UNDO, undoListener);
} }
@Override @Override
@ -241,35 +254,24 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
@Subscribe @Subscribe
void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) { void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) {
graph.getModel().beginUpdate(); graph.getModel().beginUpdate();
try {
graph.unpinAccount(pinEvent.getAccountDeviceInstances()); graph.unpinAccount(pinEvent.getAccountDeviceInstances());
graph.clear(); graph.clear();
rebuildGraph(); rebuildGraph();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error pinning accounts", ex);
} finally {
// Updates the display // Updates the display
graph.getModel().endUpdate(); graph.getModel().endUpdate();
}
} }
@Subscribe @Subscribe
void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) {
graph.getModel().beginUpdate(); graph.getModel().beginUpdate();
try {
if (pinEvent.isReplace()) { if (pinEvent.isReplace()) {
graph.resetGraph(); graph.resetGraph();
} }
graph.pinAccount(pinEvent.getAccountDeviceInstances()); graph.pinAccount(pinEvent.getAccountDeviceInstances());
rebuildGraph(); rebuildGraph();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error pinning accounts", ex);
} finally {
// Updates the display // Updates the display
graph.getModel().endUpdate(); graph.getModel().endUpdate();
}
} }
@ -277,43 +279,47 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) {
graph.getModel().beginUpdate(); graph.getModel().beginUpdate();
try {
graph.clear(); graph.clear();
currentFilter = filterChangeEvent.getNewFilter(); currentFilter = filterChangeEvent.getNewFilter();
rebuildGraph(); rebuildGraph();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error filtering accounts", ex);
} finally {
// Updates the display // Updates the display
graph.getModel().endUpdate(); graph.getModel().endUpdate();
}
} }
private void rebuildGraph() throws TskCoreException { @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void rebuildGraph() {
if (graph.isEmpty()) { if (graph.isEmpty()) {
borderLayoutPanel.remove(graphComponent); borderLayoutPanel.remove(graphComponent);
borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
repaint();
} else { } else {
borderLayoutPanel.remove(placeHolderPanel); borderLayoutPanel.remove(placeHolderPanel);
borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
if (worker != null) { if (worker != null) {
worker.cancel(true); worker.cancel(true);
} }
worker = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter);
worker.addPropertyChangeListener(new PropertyChangeListener() { CancelationListener cancelationListener = new CancelationListener();
@Override ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Loading Visualization", new String[]{CANCEL}, CANCEL, cancelationListener);
public void propertyChange(PropertyChangeEvent evt) { worker = graph.rebuild(progress, commsManager, currentFilter);
cancelationListener.configure(worker, progress);
worker.addPropertyChangeListener((final PropertyChangeEvent evt) -> {
if (worker.isDone()) { if (worker.isDone()) {
if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { if (worker.isCancelled()) {
graph.resetGraph();
rebuildGraph();
} else if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) {
applyOrganicLayout(10); applyOrganicLayout(10);
} else { } else {
statusLabel.setText("Too many cells, layout aborted."); JOptionPane.showMessageDialog(VisualizationPanel.this,
} "Too many accounts, layout aborted.",
"Autopsy",
JOptionPane.WARNING_MESSAGE);
} }
} }
}); });
worker.execute(); worker.execute();
} }
@ -322,10 +328,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
// IngestManager.getInstance().addIngestModuleEventListener(ingestListener); windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, this);
try { try {
commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex); logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
@ -357,7 +363,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
@Override @Override
public void removeNotify() { public void removeNotify() {
super.removeNotify(); super.removeNotify();
// IngestManager.getInstance().removeIngestModuleEventListener(ingestListener);
} }
/** /**
@ -371,10 +376,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
splitPane = new JSplitPane(); splitPane = new JSplitPane();
borderLayoutPanel = new JPanel(); borderLayoutPanel = new JPanel();
jToolBar2 = new JToolBar();
statusLabel = new JLabel();
progresPanel = new JPanel();
progressBar = new JProgressBar();
placeHolderPanel = new JPanel(); placeHolderPanel = new JPanel();
jTextArea1 = new JTextArea(); jTextArea1 = new JTextArea();
toolbar = new JPanel(); toolbar = new JPanel();
@ -398,35 +399,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
borderLayoutPanel.setLayout(new BorderLayout()); borderLayoutPanel.setLayout(new BorderLayout());
jToolBar2.setFloatable(false);
jToolBar2.setRollover(true);
statusLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.statusLabel.text")); // NOI18N
jToolBar2.add(statusLabel);
progresPanel.setMinimumSize(new Dimension(0, 20));
progresPanel.setName(""); // NOI18N
progressBar.setMaximumSize(new Dimension(200, 14));
progressBar.setStringPainted(true);
GroupLayout progresPanelLayout = new GroupLayout(progresPanel);
progresPanel.setLayout(progresPanelLayout);
progresPanelLayout.setHorizontalGroup(progresPanelLayout.createParallelGroup(GroupLayout.LEADING)
.add(GroupLayout.TRAILING, progresPanelLayout.createSequentialGroup()
.add(0, 447, Short.MAX_VALUE)
.add(progressBar, GroupLayout.PREFERRED_SIZE, 350, GroupLayout.PREFERRED_SIZE))
);
progresPanelLayout.setVerticalGroup(progresPanelLayout.createParallelGroup(GroupLayout.LEADING)
.add(GroupLayout.TRAILING, progresPanelLayout.createSequentialGroup()
.add(0, 0, 0)
.add(progressBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
);
jToolBar2.add(progresPanel);
borderLayoutPanel.add(jToolBar2, BorderLayout.PAGE_END);
jTextArea1.setBackground(new Color(240, 240, 240)); jTextArea1.setBackground(new Color(240, 240, 240));
jTextArea1.setColumns(20); jTextArea1.setColumns(20);
jTextArea1.setLineWrap(true); jTextArea1.setLineWrap(true);
@ -437,15 +409,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
placeHolderPanel.setLayout(placeHolderPanelLayout); placeHolderPanel.setLayout(placeHolderPanelLayout);
placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
.add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup()
.addContainerGap(213, Short.MAX_VALUE) .addContainerGap(208, Short.MAX_VALUE)
.add(jTextArea1, GroupLayout.PREFERRED_SIZE, 372, GroupLayout.PREFERRED_SIZE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 372, GroupLayout.PREFERRED_SIZE)
.addContainerGap(214, Short.MAX_VALUE)) .addContainerGap(209, Short.MAX_VALUE))
); );
placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
.add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup()
.addContainerGap(200, Short.MAX_VALUE) .addContainerGap(213, Short.MAX_VALUE)
.add(jTextArea1, GroupLayout.PREFERRED_SIZE, 43, GroupLayout.PREFERRED_SIZE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 43, GroupLayout.PREFERRED_SIZE)
.addContainerGap(200, Short.MAX_VALUE)) .addContainerGap(214, Short.MAX_VALUE))
); );
borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
@ -638,7 +610,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
} }
private void fitGraph() { private void fitGraph() {
graphComponent.zoomTo(1, true); graphComponent.zoomTo(1, true);
mxPoint translate = graph.getView().getTranslate(); mxPoint translate = graph.getView().getTranslate();
if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) { if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
@ -651,7 +622,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
} }
final mxPoint mxPoint = new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY()); final mxPoint mxPoint = new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
// graph.getView().setTranslate(mxPoint);
graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(), false, false); graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(), false, false);
boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true); boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true);
@ -662,7 +632,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
Dimension size = graphComponent.getSize(); Dimension size = graphComponent.getSize();
double widthFactor = size.getWidth() / boundsForCells.getWidth(); double widthFactor = size.getWidth() / boundsForCells.getWidth();
graphComponent.zoom(widthFactor);//, true); graphComponent.zoom(widthFactor);
} }
@ -670,28 +640,48 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
// layout using morphing // layout using morphing
graph.getModel().beginUpdate(); graph.getModel().beginUpdate();
progressBar.setVisible(true); CancelationListener cancelationListener = new CancelationListener();
progressBar.setIndeterminate(true); ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Computing layout", new String[]{CANCEL}, CANCEL, cancelationListener);
progressBar.setString("applying layout"); SwingWorker<Void, Void> morphWorker = new SwingWorker<Void, Void>() {
new SwingWorker<Void, Void>() {
@Override @Override
protected Void doInBackground() throws Exception { protected Void doInBackground() throws Exception {
progress.start("Computing layout");
layout.execute(graph.getDefaultParent()); layout.execute(graph.getDefaultParent());
mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); if (isCancelled()) {
progress.finish();
return null;
}
mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20) {
@Override
public void updateAnimation() {
fireEvent(new mxEventObject(mxEvent.EXECUTE));
super.updateAnimation(); //To change body of generated methods, choose Tools | Templates.
}
};
morph.addListener(mxEvent.EXECUTE, (Object sender, mxEventObject evt) -> {
if (isCancelled()) {
morph.stopAnimation();
}
});
morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> {
graph.getModel().endUpdate(); graph.getModel().endUpdate();
if (isCancelled()) {
undoManager.undo();
} else {
fitGraph(); fitGraph();
progressBar.setVisible(false); }
progressBar.setValue(0); progress.finish();
}); });
morph.startAnimation(); morph.startAnimation();
return null; return null;
}
}.execute();
} }
};
cancelationListener.configure(morphWorker, progress);
morphWorker.execute();
}
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private JPanel borderLayoutPanel; private JPanel borderLayoutPanel;
@ -703,13 +693,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
private JLabel jLabel2; private JLabel jLabel2;
private JToolBar.Separator jSeparator1; private JToolBar.Separator jSeparator1;
private JTextArea jTextArea1; private JTextArea jTextArea1;
private JToolBar jToolBar2;
private JButton organicLayoutButton; private JButton organicLayoutButton;
private JPanel placeHolderPanel; private JPanel placeHolderPanel;
private JPanel progresPanel;
private JProgressBar progressBar;
private JSplitPane splitPane; private JSplitPane splitPane;
private JLabel statusLabel;
private JPanel toolbar; private JPanel toolbar;
private JButton zoomActualButton; private JButton zoomActualButton;
private JButton zoomInButton; private JButton zoomInButton;
@ -748,79 +734,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
} }
} }
final private class ProgressIndicatorImpl implements ProgressIndicator {
@Override
public void start(String message, int totalWorkUnits) {
SwingUtilities.invokeLater(() -> {
progressBar.setVisible(true);
progressBar.setIndeterminate(false);
progressBar.setString(message);
progressBar.setMaximum(totalWorkUnits);
progressBar.setValue(0);
});
}
@Override
public void start(String message) {
SwingUtilities.invokeLater(() -> {
progressBar.setVisible(true);
progressBar.setString(message);
progressBar.setIndeterminate(true);
});
}
@Override
public void switchToIndeterminate(String message) {
SwingUtilities.invokeLater(() -> {
progressBar.setVisible(true);
progressBar.setIndeterminate(true);
progressBar.setString(message);
});
}
@Override
public void switchToDeterminate(String message, int workUnitsCompleted, int totalWorkUnits) {
SwingUtilities.invokeLater(() -> {
progressBar.setVisible(true);
progressBar.setIndeterminate(false);
progressBar.setString(message);
progressBar.setMaximum(totalWorkUnits);
progressBar.setValue(workUnitsCompleted);
});
}
@Override
public void progress(String message) {
SwingUtilities.invokeLater(() -> {
progressBar.setString(message);
});
}
@Override
public void progress(int workUnitsCompleted) {
SwingUtilities.invokeLater(() -> {
progressBar.setValue(workUnitsCompleted);
});
}
@Override
public void progress(String message, int workUnitsCompleted) {
SwingUtilities.invokeLater(() -> {
progressBar.setString(message);
progressBar.setValue(workUnitsCompleted);
});
}
@Override
public void finish() {
SwingUtilities.invokeLater(() -> {
progressBar.setValue(progressBar.getValue());
progressBar.setVisible(false);
});
}
}
final private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout { final private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout {
mxFastOrganicLayoutImpl(mxGraph graph) { mxFastOrganicLayoutImpl(mxGraph graph) {
@ -910,4 +823,23 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
} }
} }
} }
private class CancelationListener implements ActionListener {
private Future<?> cancellable;
private ModalDialogProgressIndicator progress;
void configure(Future<?> cancellable, ModalDialogProgressIndicator progress) {
this.cancellable = cancellable;
this.progress = progress;
}
@Override
public void actionPerformed(ActionEvent e) {
progress.setCancelling("Cancelling...");
cancellable.cancel(true);
progress.finish();
}
}
} }

View File

@ -247,7 +247,7 @@ public final class ModalDialogProgressIndicator implements ProgressIndicator {
DialogDescriptor.BOTTOM_ALIGN, DialogDescriptor.BOTTOM_ALIGN,
HelpCtx.DEFAULT_HELP, HelpCtx.DEFAULT_HELP,
buttonListener); buttonListener);
dialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor); dialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor, parent);
} else { } else {
/* /*
* Dialog without buttons. * Dialog without buttons.
@ -256,6 +256,7 @@ public final class ModalDialogProgressIndicator implements ProgressIndicator {
dialog.add(progressPanel); dialog.add(progressPanel);
dialog.pack(); dialog.pack();
} }
dialog.setResizable(false);
dialog.setLocationRelativeTo(parent); dialog.setLocationRelativeTo(parent);
this.dialog.setVisible(true); this.dialog.setVisible(true);
} }