From b7a91a7a8c04a174fb6aeca3a900642233cf2fa4 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 12 Feb 2018 15:00:40 +0100 Subject: [PATCH 1/5] cleanup and fix codacy issues --- .../communications/Vertex_Label_template.html | 13 +-- .../communications/VisualizationPanel.java | 67 ++++++----- .../autopsy/communications/mxGraphImpl.java | 108 +++++++++++------- 3 files changed, 105 insertions(+), 83 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html b/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html index dbc3f85a2c..4e489c992a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html +++ b/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html @@ -1,10 +1,3 @@ -
- {{#pinned}} - - {{/pinned}} - {{#locked}} - - {{/locked}} - - {{accountName}} -
+ +
{{#pinned}}{{/pinned}}{{#locked}}{{/locked}}{{accountName}}
+ \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 4ac198f1c4..217bbb168b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -154,7 +154,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider rubberband = new mxRubberband(graphComponent); final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt) -> - zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getScale())); + zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale())); graph.getView().addListener(mxEvent.SCALE, scaleListener); graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener); @@ -164,9 +164,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public void mouseWheelMoved(final MouseWheelEvent e) { super.mouseWheelMoved(e); if (e.getPreciseWheelRotation() > 0) { - graphComponent.zoomTo(graph.getScale() / graphComponent.getZoomFactor(), true); + graphComponent.zoomIn(); } else if (e.getPreciseWheelRotation() < 0) { - graphComponent.zoomTo(graph.getScale() * graphComponent.getZoomFactor(), true); + graphComponent.zoomOut(); } } }); @@ -291,7 +291,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } private void rebuildGraph() throws TskCoreException { - if (graph.hasPinnedAccounts()) { + if (graph.isEmpty()) { + borderLayoutPanel.remove(graphComponent); + borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); + } else { borderLayoutPanel.remove(placeHolderPanel); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); if (worker != null) { @@ -303,15 +306,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void propertyChange(PropertyChangeEvent evt) { if (worker.isDone()) { - applyOrganicLayout(10); + if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { + applyOrganicLayout(10); + } else { + statusLabel.setText("Too many cells, layout aborted."); + } } } }); worker.execute(); - } else { - borderLayoutPanel.remove(graphComponent); - borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); } } @@ -665,22 +669,28 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void morph(mxIGraphLayout layout) { // layout using morphing graph.getModel().beginUpdate(); - try { - progressBar.setVisible(true); - progressBar.setIndeterminate(true); - progressBar.setString("applying layout"); - layout.execute(graph.getDefaultParent()); - } finally { - mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); - morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { - graph.getModel().endUpdate(); - fitGraph(); - progressBar.setVisible(false); - progressBar.setValue(0); - }); - morph.startAnimation(); - } + progressBar.setVisible(true); + progressBar.setIndeterminate(true); + progressBar.setString("applying layout"); + + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + layout.execute(graph.getDefaultParent()); + mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); + morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { + graph.getModel().endUpdate(); + fitGraph(); + progressBar.setVisible(false); + progressBar.setValue(0); + }); + + morph.startAnimation(); + return null; + } + }.execute(); + } // Variables declaration - do not modify//GEN-BEGIN:variables @@ -709,9 +719,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class SelectionListener implements mxEventSource.mxIEventListener { - @Override - @SuppressWarnings("unchecked") + @Override public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); Node rootNode = Node.EMPTY; @@ -814,7 +823,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout { - private mxFastOrganicLayoutImpl(mxGraph graph) { + mxFastOrganicLayoutImpl(mxGraph graph) { super(graph); } @@ -836,7 +845,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxCircleLayoutImpl extends mxCircleLayout { - private mxCircleLayoutImpl(mxGraph graph) { + mxCircleLayoutImpl(mxGraph graph) { super(graph); setResetEdges(true); } @@ -859,7 +868,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxOrganicLayoutImpl extends mxOrganicLayout { - private mxOrganicLayoutImpl(mxGraph graph) { + mxOrganicLayoutImpl(mxGraph graph) { super(graph); setResetEdges(true); } @@ -882,7 +891,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxHierarchicalLayoutImpl extends mxHierarchicalLayout { - private mxHierarchicalLayoutImpl(mxGraph graph) { + mxHierarchicalLayoutImpl(mxGraph graph) { super(graph); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 6b9a2b9391..72210e37ff 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -106,33 +106,6 @@ final class mxGraphImpl extends mxGraph { setKeepEdgesInBackground(true); setResetEdgesOnMove(true); setHtmlLabels(true); - -// new mxLayoutManager(graph) { -// final private mxOrganicLayout layout; -// private int counter; -// { -// this.layout = new mxOrganicLayout(graph); -// this.layout.setMaxIterations(1); -// } -// -// @Override -// protected void executeLayout(mxIGraphLayout layout, Object parent) { -// if (counter % 10 == 0) -// { -// super.executeLayout(layout, parent); -//// fitGraph(); -// } -// counter++; -// } -// -// @Override -// public mxIGraphLayout getLayout(Object parent) { -// if (graph.getModel().getChildCount(parent) > 0) { -// return layout; -// } -// return null; -// } -// }; } void clear() { @@ -156,10 +129,8 @@ final class mxGraphImpl extends mxGraph { scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5)); - - scopes.put("iconFileName", mxGraphImpl.class - .getResource("/org/sleuthkit/autopsy/communications/images/" - + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + scopes.put("iconFileName", mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" + + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); scopes.put("locked", lockedVertices.contains((mxCell) cell)); @@ -175,25 +146,69 @@ final class mxGraphImpl extends mxGraph { @Override public String getToolTipForCell(Object cell) { - return ((mxICell) cell).getId(); + final StringWriter stringWriter = new StringWriter(); + HashMap scopes = new HashMap<>(); + + Object value = getModel().getValue(cell); + if (value instanceof AccountDeviceInstanceKey) { + final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value; + + scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); + scopes.put("size", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5)); + scopes.put("iconFileName", mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" + + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); + scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); + scopes.put("locked", lockedVertices.contains((mxCell) cell)); + scopes.put("LOCK_URL", LOCK_URL); + + labelMustache.execute(stringWriter, scopes); + + return stringWriter.toString(); + } else { + return ((mxICell) cell).getId(); + } } + /** + * Unpin the given accounts from the graph. Pinned accounts will always be + * shown regardless of the filter state. Furthermore, accounts with + * relationships that pass the filters will also be shown. + * + * @param accountDeviceInstances The accounts to unpin. + */ void unpinAccount(ImmutableSet accountDeviceInstances) { pinnedAccountDevices.removeAll(accountDeviceInstances); } + /** + * Pin the given accounts to the graph. Pinned accounts will always be shown + * regardless of the filter state. Furthermore, accounts with relationships + * that pass the filters will also be shown. + * + * @param accountDeviceInstances The accounts to pin. + */ void pinAccount(ImmutableSet accountDeviceInstances) { pinnedAccountDevices.addAll(accountDeviceInstances); } + /** + * Lock the given vertex so that applying a layout algorithm doesn't move + * it. The user can still manually position the vertex. + * + * @param vertex The vertex to lock. + */ void lockVertex(mxCell vertex) { lockedVertices.add(vertex); - - final mxCellState state = getView().getState(vertex, true); getView().clear(vertex, true, true); getView().validate(); } + /** + * Lock the given vertex so that applying a layout algorithm can move it. + * + * @param vertex The vertex to unlock. + */ void unlockVertex(mxCell vertex) { lockedVertices.remove(vertex); @@ -204,8 +219,7 @@ final class mxGraphImpl extends mxGraph { } SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { - - return new SwingWorkerImpl(progress, commsManager, currentFilter); + return new RebuildWorker(progress, commsManager, currentFilter); } void resetGraph() { @@ -261,12 +275,14 @@ final class mxGraphImpl extends mxGraph { return edge; } - boolean hasPinnedAccounts() { - return pinnedAccountDevices.isEmpty() == false; - } - - double getScale() { - return getView().getScale(); + /** + * Are there any accounts in this graph? If there are no pinned accounts the + * graph will be empty. + * + * @return True if this graph is empty. + */ + boolean isEmpty() { + return pinnedAccountDevices.isEmpty(); } boolean isVertexLocked(mxCell vertex) { @@ -274,13 +290,17 @@ final class mxGraphImpl extends mxGraph { } - private class SwingWorkerImpl extends SwingWorker { + /** + * SwingWorker that loads the accounts and edges for this graph according to + * the pinned accounts and the current filters. + */ + private class RebuildWorker extends SwingWorker { private final ProgressIndicator progress; private final CommunicationsManager commsManager; private final CommunicationsFilter currentFilter; - SwingWorkerImpl(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + RebuildWorker(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { this.progress = progress; this.currentFilter = currentFilter; this.commsManager = commsManager; From a77ced5a4161d1a3440abb63cb66ad117354cc6f Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 12 Feb 2018 15:27:11 +0100 Subject: [PATCH 2/5] begin work on cancellable graph loading --- .../autopsy/communications/Bundle.properties | 1 + .../communications/VisualizationPanel.form | 34 +++++-- .../communications/VisualizationPanel.java | 91 +++++++++++-------- .../autopsy/communications/mxGraphImpl.java | 11 ++- 4 files changed, 87 insertions(+), 50 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 271d5b3b5a..0f9e5ce048 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -41,3 +41,4 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in VisualizationPanel.zoomActualButton.toolTipText=reset zoom VisualizationPanel.fitZoomButton.toolTipText=fit visualization VisualizationPanel.organicLayoutButton.text=Organic +VisualizationPanel.cancelButton.text= diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index fa4b00c17a..a4372087d1 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,7 +11,7 @@ - + @@ -58,7 +58,7 @@ - + @@ -70,16 +70,22 @@ - + + + + - - + + + + + @@ -93,6 +99,16 @@ + + + + + + + + + + @@ -108,18 +124,18 @@ - + - + - + - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 217bbb168b..d7148fde20 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -148,7 +148,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.setToolTips(true); graphComponent.setBackground(Color.WHITE); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); - progressBar.setVisible(false); + progressPanel.setVisible(false); //install rubber band selection handler rubberband = new mxRubberband(graphComponent); @@ -176,10 +176,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public void mouseClicked(MouseEvent e) { super.mouseClicked(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()) { - JPopupMenu jPopupMenu = new JPopupMenu(); - AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue(); + final JPopupMenu jPopupMenu = new JPopupMenu(); + final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue(); if (graph.isVertexLocked(cellAt)) { jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock " + cellAt.getId(), unlockIcon) { @@ -304,7 +304,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider worker.addPropertyChangeListener(new PropertyChangeListener() { @Override - public void propertyChange(PropertyChangeEvent evt) { + public void propertyChange(final PropertyChangeEvent evt) { if (worker.isDone()) { if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { applyOrganicLayout(10); @@ -314,6 +314,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } }); + + cancelButton.setAction(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + worker.cancel(true); + } + }); worker.execute(); } @@ -322,7 +329,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void addNotify() { super.addNotify(); -// IngestManager.getInstance().addIngestModuleEventListener(ingestListener); try { commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); @@ -357,7 +363,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void removeNotify() { super.removeNotify(); -// IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); } /** @@ -373,8 +378,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider borderLayoutPanel = new JPanel(); jToolBar2 = new JToolBar(); statusLabel = new JLabel(); - progresPanel = new JPanel(); + progressPanel = new JPanel(); progressBar = new JProgressBar(); + cancelButton = new JButton(); placeHolderPanel = new JPanel(); jTextArea1 = new JTextArea(); toolbar = new JPanel(); @@ -404,26 +410,34 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider statusLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.statusLabel.text")); // NOI18N jToolBar2.add(statusLabel); - progresPanel.setMinimumSize(new Dimension(0, 20)); - progresPanel.setName(""); // NOI18N + progressPanel.setMinimumSize(new Dimension(0, 20)); + progressPanel.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)) + cancelButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/cross-script.png"))); // NOI18N + cancelButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.cancelButton.text")); // NOI18N + + GroupLayout progressPanelLayout = new GroupLayout(progressPanel); + progressPanel.setLayout(progressPanelLayout); + progressPanelLayout.setHorizontalGroup(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, progressPanelLayout.createSequentialGroup() + .addContainerGap(415, Short.MAX_VALUE) + .add(progressBar, GroupLayout.PREFERRED_SIZE, 350, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(cancelButton, GroupLayout.PREFERRED_SIZE, 24, GroupLayout.PREFERRED_SIZE) + .add(5, 5, 5)) ); - progresPanelLayout.setVerticalGroup(progresPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, progresPanelLayout.createSequentialGroup() + progressPanelLayout.setVerticalGroup(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, progressPanelLayout.createSequentialGroup() .add(0, 0, 0) - .add(progressBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .add(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(cancelButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(progressBar, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) ); - jToolBar2.add(progresPanel); + jToolBar2.add(progressPanel); borderLayoutPanel.add(jToolBar2, BorderLayout.PAGE_END); @@ -437,15 +451,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider placeHolderPanel.setLayout(placeHolderPanelLayout); placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(213, Short.MAX_VALUE) + .addContainerGap(217, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 372, GroupLayout.PREFERRED_SIZE) - .addContainerGap(214, Short.MAX_VALUE)) + .addContainerGap(218, Short.MAX_VALUE)) ); placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(200, Short.MAX_VALUE) + .addContainerGap(387, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 43, GroupLayout.PREFERRED_SIZE) - .addContainerGap(200, Short.MAX_VALUE)) + .addContainerGap()) ); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); @@ -638,7 +652,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } private void fitGraph() { - graphComponent.zoomTo(1, true); mxPoint translate = graph.getView().getTranslate(); if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) { @@ -651,7 +664,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } 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); boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true); @@ -662,7 +674,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider Dimension size = graphComponent.getSize(); double widthFactor = size.getWidth() / boundsForCells.getWidth(); - graphComponent.zoom(widthFactor);//, true); + graphComponent.zoom(widthFactor); } @@ -670,9 +682,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // layout using morphing graph.getModel().beginUpdate(); - progressBar.setVisible(true); + progressPanel.setVisible(true); progressBar.setIndeterminate(true); - progressBar.setString("applying layout"); + progressBar.setString("Computing layout"); new SwingWorker() { @Override @@ -682,19 +694,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { graph.getModel().endUpdate(); fitGraph(); - progressBar.setVisible(false); + progressPanel.setVisible(false); progressBar.setValue(0); }); - + + SwingUtilities.invokeLater(() -> progressBar.setString("Applying layout")); morph.startAnimation(); return null; } }.execute(); - } // Variables declaration - do not modify//GEN-BEGIN:variables private JPanel borderLayoutPanel; + private JButton cancelButton; private JButton circleLayoutButton; private JButton fastOrganicLayoutButton; private JButton fitZoomButton; @@ -706,8 +719,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JToolBar jToolBar2; private JButton organicLayoutButton; private JPanel placeHolderPanel; - private JPanel progresPanel; private JProgressBar progressBar; + private JPanel progressPanel; private JSplitPane splitPane; private JLabel statusLabel; private JPanel toolbar; @@ -753,7 +766,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void start(String message, int totalWorkUnits) { SwingUtilities.invokeLater(() -> { - progressBar.setVisible(true); + progressPanel.setVisible(true); progressBar.setIndeterminate(false); progressBar.setString(message); progressBar.setMaximum(totalWorkUnits); @@ -764,7 +777,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void start(String message) { SwingUtilities.invokeLater(() -> { - progressBar.setVisible(true); + progressPanel.setVisible(true); progressBar.setString(message); progressBar.setIndeterminate(true); }); @@ -773,7 +786,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void switchToIndeterminate(String message) { SwingUtilities.invokeLater(() -> { - progressBar.setVisible(true); + progressPanel.setVisible(true); progressBar.setIndeterminate(true); progressBar.setString(message); }); @@ -782,7 +795,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void switchToDeterminate(String message, int workUnitsCompleted, int totalWorkUnits) { SwingUtilities.invokeLater(() -> { - progressBar.setVisible(true); + progressPanel.setVisible(true); progressBar.setIndeterminate(false); progressBar.setString(message); progressBar.setMaximum(totalWorkUnits); @@ -815,8 +828,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void finish() { SwingUtilities.invokeLater(() -> { - progressBar.setValue(progressBar.getValue()); - progressBar.setVisible(false); + progressBar.setValue(progressBar.getMaximum()); + progressPanel.setVisible(false); }); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 72210e37ff..46bec948c9 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -40,6 +40,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; @@ -264,7 +265,6 @@ final class mxGraphImpl extends mxGraph { if (edgesBetween.length == 0) { final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); final HashSet hashSet = new HashSet<>(relSources); - // edgeMap.put(relSource, edge); edge = (mxCell) insertEdge(getDefaultParent(), edgeName, hashSet, vertex1, vertex2, "strokeWidth=" + (Math.log(hashSet.size()) + 1)); } else { @@ -316,6 +316,9 @@ final class mxGraphImpl extends mxGraph { */ Set relatedAccounts = new HashSet<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { + if (isCancelled()) { + break; + } List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); relatedAccounts.add(adiKey); @@ -336,7 +339,9 @@ final class mxGraphImpl extends mxGraph { for (i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); for (int j = i; j < relatedAccountsList.size(); j++) { - + if (isCancelled()) { + break; + } AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); List relationships = commsManager.getRelationshipSources( adiKey1.getAccountDeviceInstance(), @@ -363,6 +368,8 @@ final class mxGraphImpl extends mxGraph { get(); } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, "Error building graph visualization. ", ex); + } catch (CancellationException ex) { + logger.log(Level.INFO, "Graph visualization cancelled"); } finally { progress.finish(); } From ae6dc361d19af1a401608f5311491a49603e2ca5 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 14 Feb 2018 11:15:11 +0100 Subject: [PATCH 3/5] Cancelation WIP --- .../communications/VisualizationPanel.java | 92 +++++++++++-------- .../autopsy/communications/mxGraphImpl.java | 18 ++-- 2 files changed, 67 insertions(+), 43 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index d7148fde20..02d365247e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -64,14 +64,19 @@ import javax.swing.JProgressBar; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JToolBar; +import javax.swing.ProgressMonitor; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.jdesktop.layout.GroupLayout; import org.jdesktop.layout.LayoutStyle; +import org.netbeans.api.progress.BaseProgressUtils; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressRunnable; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.Node; +import org.openide.util.Cancellable; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; @@ -241,35 +246,24 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Subscribe void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); - try { - graph.unpinAccount(pinEvent.getAccountDeviceInstances()); - graph.clear(); - rebuildGraph(); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error pinning accounts", ex); - } finally { - // Updates the display - graph.getModel().endUpdate(); - } + graph.unpinAccount(pinEvent.getAccountDeviceInstances()); + graph.clear(); + rebuildGraph(); + // Updates the display + graph.getModel().endUpdate(); } @Subscribe void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); - try { - if (pinEvent.isReplace()) { - graph.resetGraph(); - } - - graph.pinAccount(pinEvent.getAccountDeviceInstances()); - rebuildGraph(); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error pinning accounts", ex); - } finally { - // Updates the display - graph.getModel().endUpdate(); + if (pinEvent.isReplace()) { + graph.resetGraph(); } + graph.pinAccount(pinEvent.getAccountDeviceInstances()); + rebuildGraph(); + // Updates the display + graph.getModel().endUpdate(); } @@ -277,20 +271,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { graph.getModel().beginUpdate(); - try { - graph.clear(); - currentFilter = filterChangeEvent.getNewFilter(); - rebuildGraph(); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error filtering accounts", ex); - } finally { - // Updates the display - graph.getModel().endUpdate(); - } + graph.clear(); + currentFilter = filterChangeEvent.getNewFilter(); + rebuildGraph(); + // Updates the display + graph.getModel().endUpdate(); } - private void rebuildGraph() throws TskCoreException { + private void rebuildGraph() { if (graph.isEmpty()) { borderLayoutPanel.remove(graphComponent); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); @@ -300,8 +289,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider if (worker != null) { worker.cancel(true); } - worker = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter); - + ; + BaseProgressUtils.showProgressDialogAndRun(new ProgressRunnableImpl(), "Loading Visualization", true); worker.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent evt) { @@ -312,6 +301,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider statusLabel.setText("Too many cells, layout aborted."); } } + if (worker.isCancelled()) { + graph.resetGraph(); + rebuildGraph(); + } } }); @@ -685,7 +678,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressPanel.setVisible(true); progressBar.setIndeterminate(true); progressBar.setString("Computing layout"); - + final ProgressMonitor progressMonitor = new ProgressMonitor(this, "Computing layout", "", 0, 100); + progressMonitor.setProgress(0); new SwingWorker() { @Override protected Void doInBackground() throws Exception { @@ -696,8 +690,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider fitGraph(); progressPanel.setVisible(false); progressBar.setValue(0); + progressMonitor.close(); }); - + + progressMonitor.setProgress(50); SwingUtilities.invokeLater(() -> progressBar.setString("Applying layout")); morph.startAnimation(); return null; @@ -763,6 +759,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class ProgressIndicatorImpl implements ProgressIndicator { + ProgressMonitor progressMonitor = new ProgressMonitor(VisualizationPanel.this, "title", "detail", 0, 100); + @Override public void start(String message, int totalWorkUnits) { SwingUtilities.invokeLater(() -> { @@ -771,6 +769,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressBar.setString(message); progressBar.setMaximum(totalWorkUnits); progressBar.setValue(0); + }); } @@ -780,6 +779,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressPanel.setVisible(true); progressBar.setString(message); progressBar.setIndeterminate(true); + }); } @@ -815,6 +815,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider SwingUtilities.invokeLater(() -> { progressBar.setValue(workUnitsCompleted); }); + progressMonitor.setProgress(workUnitsCompleted); } @Override @@ -823,6 +824,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressBar.setString(message); progressBar.setValue(workUnitsCompleted); }); + progressMonitor.setProgress(workUnitsCompleted); } @Override @@ -831,6 +833,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressBar.setValue(progressBar.getMaximum()); progressPanel.setVisible(false); }); + progressMonitor.close(); } } @@ -923,4 +926,19 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } } + + private class ProgressRunnableImpl implements ProgressRunnable, Cancellable { + + @Override + public Void run(ProgressHandle ph) { + worker = graph.rebuild(ph, commsManager, currentFilter); + + return null; + } + + @Override + public boolean cancel() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 46bec948c9..6263c8c175 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -44,8 +44,8 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; +import org.netbeans.api.progress.ProgressHandle; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; @@ -219,7 +219,7 @@ final class mxGraphImpl extends mxGraph { getView().updateBoundingBox(state); } - SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + SwingWorker rebuild(ProgressHandle progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { return new RebuildWorker(progress, commsManager, currentFilter); } @@ -296,19 +296,22 @@ final class mxGraphImpl extends mxGraph { */ private class RebuildWorker extends SwingWorker { - private final ProgressIndicator progress; + private final ProgressHandle progress; private final CommunicationsManager commsManager; private final CommunicationsFilter currentFilter; - RebuildWorker(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + RebuildWorker(ProgressHandle progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { this.progress = progress; this.currentFilter = currentFilter; this.commsManager = commsManager; + } @Override protected Void doInBackground() throws Exception { - progress.start("Loading accounts", pinnedAccountDevices.size()); + progress.progress("Loading accounts"); + progress.switchToDeterminate(pinnedAccountDevices.size()); + progress.progress(0); int i = 0; try { /** @@ -317,6 +320,7 @@ final class mxGraphImpl extends mxGraph { Set relatedAccounts = new HashSet<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { if (isCancelled()) { + cancel(true); break; } List relatedAccountDeviceInstances = @@ -335,11 +339,12 @@ final class mxGraphImpl extends mxGraph { //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); - progress.switchToDeterminate("", 0, relatedAccountsList.size()); + progress.switchToDeterminate(relatedAccountsList.size()); for (i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); for (int j = i; j < relatedAccountsList.size(); j++) { if (isCancelled()) { + cancel(true); break; } AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); @@ -369,6 +374,7 @@ final class mxGraphImpl extends mxGraph { } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, "Error building graph visualization. ", ex); } catch (CancellationException ex) { + logger.log(Level.INFO, "Graph visualization cancelled"); } finally { progress.finish(); From 535a1feee31f73fd4566f75252eb2c3f7dc8a468 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 14 Feb 2018 15:21:47 +0100 Subject: [PATCH 4/5] show cancellable progress in a dialog. --- .../autopsy/communications/Bundle.properties | 2 - .../communications/VisualizationPanel.form | 85 +------ .../communications/VisualizationPanel.java | 239 +++++------------- .../autopsy/communications/mxGraphImpl.java | 19 +- .../ModalDialogProgressIndicator.java | 3 +- 5 files changed, 76 insertions(+), 272 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 0f9e5ce048..dfbdb97ed0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -24,7 +24,6 @@ VisualizationPanel.jButton6.text=Hierarchy VisualizationPanel.jButton7.text=Circle VisualizationPanel.jButton8.text=Organic VisualizationPanel.fitGraphButton.text= -VisualizationPanel.statusLabel.text=\ VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. VisualizationPanel.jLabel1.text=Layouts: VisualizationPanel.fitZoomButton.text= @@ -41,4 +40,3 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in VisualizationPanel.zoomActualButton.toolTipText=reset zoom VisualizationPanel.fitZoomButton.toolTipText=fit visualization VisualizationPanel.organicLayoutButton.text=Organic -VisualizationPanel.cancelButton.text= diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index a4372087d1..c6eba12459 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,7 +11,7 @@ - + @@ -38,81 +38,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -124,18 +49,18 @@ - + - + - + - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 02d365247e..43f24b5899 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -38,6 +38,7 @@ import com.mxgraph.view.mxGraph; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; +import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; @@ -52,38 +53,35 @@ import static java.util.Collections.singleton; import java.util.EnumSet; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.Future; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JMenuItem; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; -import javax.swing.JProgressBar; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JToolBar; -import javax.swing.ProgressMonitor; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.jdesktop.layout.GroupLayout; import org.jdesktop.layout.LayoutStyle; -import org.netbeans.api.progress.BaseProgressUtils; -import org.netbeans.api.progress.ProgressHandle; -import org.netbeans.api.progress.ProgressRunnable; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.Node; -import org.openide.util.Cancellable; 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; 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.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; @@ -131,7 +129,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final mxCircleLayout circleLayout; private final mxOrganicLayout organicLayout; private final mxHierarchicalLayout hierarchicalLayout; + + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private SwingWorker worker; + private Frame windowAncestor; public VisualizationPanel() { initComponents(); @@ -153,7 +154,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.setToolTips(true); graphComponent.setBackground(Color.WHITE); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); - progressPanel.setVisible(false); //install rubber band selection handler rubberband = new mxRubberband(graphComponent); @@ -193,7 +193,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.unlockVertex(cellAt); } })); - } else { jPopupMenu.add(new JMenuItem(new AbstractAction("Lock " + cellAt.getId(), lockIcon) { @Override @@ -201,7 +200,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.lockVertex(cellAt); } })); - } if (graph.isAccountPinned(adiKey)) { jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin " + cellAt.getId(), unpinIcon) { @@ -211,7 +209,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } })); } else { - jPopupMenu.add(new JMenuItem(new AbstractAction("Pin " + cellAt.getId(), addPinIcon) { @Override public void actionPerformed(ActionEvent e) { @@ -225,7 +222,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } })); } - jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY()); } } @@ -279,41 +275,42 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private void rebuildGraph() { if (graph.isEmpty()) { borderLayoutPanel.remove(graphComponent); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); + repaint(); } else { borderLayoutPanel.remove(placeHolderPanel); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); if (worker != null) { worker.cancel(true); } - ; - BaseProgressUtils.showProgressDialogAndRun(new ProgressRunnableImpl(), "Loading Visualization", true); + + CancelationListener cancelationListener = new CancelationListener(); + ModalDialogProgressIndicator modalDialogProgressIndicator = new ModalDialogProgressIndicator(windowAncestor, "Loading Visualization", new String[]{"Cancel"}, "Cancel", cancelationListener); + worker = graph.rebuild(modalDialogProgressIndicator, commsManager, currentFilter); + cancelationListener.setCancellable(worker); worker.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent evt) { 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); } else { - statusLabel.setText("Too many cells, layout aborted."); + JOptionPane.showMessageDialog(VisualizationPanel.this, + "Too many accounts, layout aborted.", + "Autopsy", + JOptionPane.WARNING_MESSAGE); } } - if (worker.isCancelled()) { - graph.resetGraph(); - rebuildGraph(); - } } }); - cancelButton.setAction(new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - worker.cancel(true); - } - }); worker.execute(); } @@ -322,9 +319,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void addNotify() { super.addNotify(); + windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, this); + 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) { @@ -369,11 +367,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane = new JSplitPane(); borderLayoutPanel = new JPanel(); - jToolBar2 = new JToolBar(); - statusLabel = new JLabel(); - progressPanel = new JPanel(); - progressBar = new JProgressBar(); - cancelButton = new JButton(); placeHolderPanel = new JPanel(); jTextArea1 = new JTextArea(); toolbar = new JPanel(); @@ -397,43 +390,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider borderLayoutPanel.setLayout(new BorderLayout()); - jToolBar2.setFloatable(false); - jToolBar2.setRollover(true); - - statusLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.statusLabel.text")); // NOI18N - jToolBar2.add(statusLabel); - - progressPanel.setMinimumSize(new Dimension(0, 20)); - progressPanel.setName(""); // NOI18N - - progressBar.setMaximumSize(new Dimension(200, 14)); - progressBar.setStringPainted(true); - - cancelButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/cross-script.png"))); // NOI18N - cancelButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.cancelButton.text")); // NOI18N - - GroupLayout progressPanelLayout = new GroupLayout(progressPanel); - progressPanel.setLayout(progressPanelLayout); - progressPanelLayout.setHorizontalGroup(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, progressPanelLayout.createSequentialGroup() - .addContainerGap(415, Short.MAX_VALUE) - .add(progressBar, GroupLayout.PREFERRED_SIZE, 350, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.RELATED) - .add(cancelButton, GroupLayout.PREFERRED_SIZE, 24, GroupLayout.PREFERRED_SIZE) - .add(5, 5, 5)) - ); - progressPanelLayout.setVerticalGroup(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, progressPanelLayout.createSequentialGroup() - .add(0, 0, 0) - .add(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(cancelButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(progressBar, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - ); - - jToolBar2.add(progressPanel); - - borderLayoutPanel.add(jToolBar2, BorderLayout.PAGE_END); - jTextArea1.setBackground(new Color(240, 240, 240)); jTextArea1.setColumns(20); jTextArea1.setLineWrap(true); @@ -444,15 +400,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider placeHolderPanel.setLayout(placeHolderPanelLayout); placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(217, Short.MAX_VALUE) + .addContainerGap(208, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 372, GroupLayout.PREFERRED_SIZE) - .addContainerGap(218, Short.MAX_VALUE)) + .addContainerGap(209, Short.MAX_VALUE)) ); placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(387, Short.MAX_VALUE) + .addContainerGap(213, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 43, GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + .addContainerGap(214, Short.MAX_VALUE)) ); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); @@ -675,35 +631,46 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // layout using morphing graph.getModel().beginUpdate(); - progressPanel.setVisible(true); - progressBar.setIndeterminate(true); - progressBar.setString("Computing layout"); - final ProgressMonitor progressMonitor = new ProgressMonitor(this, "Computing layout", "", 0, 100); - progressMonitor.setProgress(0); - new SwingWorker() { + CancelationListener cancelationListener = new CancelationListener(); + ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Computing layout", new String[]{"Cancel"}, "Cancel", cancelationListener); + SwingWorker morphWorker = new SwingWorker() { @Override protected Void doInBackground() throws Exception { + progress.start("Computing layout"); 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) -> { graph.getModel().endUpdate(); fitGraph(); - progressPanel.setVisible(false); - progressBar.setValue(0); - progressMonitor.close(); + progress.finish(); }); - progressMonitor.setProgress(50); - SwingUtilities.invokeLater(() -> progressBar.setString("Applying layout")); morph.startAnimation(); return null; } - }.execute(); + }; + cancelationListener.setCancellable(morphWorker); + morphWorker.execute(); } // Variables declaration - do not modify//GEN-BEGIN:variables private JPanel borderLayoutPanel; - private JButton cancelButton; private JButton circleLayoutButton; private JButton fastOrganicLayoutButton; private JButton fitZoomButton; @@ -712,13 +679,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JLabel jLabel2; private JToolBar.Separator jSeparator1; private JTextArea jTextArea1; - private JToolBar jToolBar2; private JButton organicLayoutButton; private JPanel placeHolderPanel; - private JProgressBar progressBar; - private JPanel progressPanel; private JSplitPane splitPane; - private JLabel statusLabel; private JPanel toolbar; private JButton zoomActualButton; private JButton zoomInButton; @@ -757,86 +720,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } - final private class ProgressIndicatorImpl implements ProgressIndicator { - - ProgressMonitor progressMonitor = new ProgressMonitor(VisualizationPanel.this, "title", "detail", 0, 100); - - @Override - public void start(String message, int totalWorkUnits) { - SwingUtilities.invokeLater(() -> { - progressPanel.setVisible(true); - progressBar.setIndeterminate(false); - progressBar.setString(message); - progressBar.setMaximum(totalWorkUnits); - progressBar.setValue(0); - - }); - } - - @Override - public void start(String message) { - SwingUtilities.invokeLater(() -> { - progressPanel.setVisible(true); - progressBar.setString(message); - progressBar.setIndeterminate(true); - - }); - } - - @Override - public void switchToIndeterminate(String message) { - SwingUtilities.invokeLater(() -> { - progressPanel.setVisible(true); - progressBar.setIndeterminate(true); - progressBar.setString(message); - }); - } - - @Override - public void switchToDeterminate(String message, int workUnitsCompleted, int totalWorkUnits) { - SwingUtilities.invokeLater(() -> { - progressPanel.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); - }); - progressMonitor.setProgress(workUnitsCompleted); - } - - @Override - public void progress(String message, int workUnitsCompleted) { - SwingUtilities.invokeLater(() -> { - progressBar.setString(message); - progressBar.setValue(workUnitsCompleted); - }); - progressMonitor.setProgress(workUnitsCompleted); - } - - @Override - public void finish() { - SwingUtilities.invokeLater(() -> { - progressBar.setValue(progressBar.getMaximum()); - progressPanel.setVisible(false); - }); - progressMonitor.close(); - } - } - final private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout { mxFastOrganicLayoutImpl(mxGraph graph) { @@ -927,18 +810,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } - private class ProgressRunnableImpl implements ProgressRunnable, Cancellable { + private class CancelationListener implements ActionListener { - @Override - public Void run(ProgressHandle ph) { - worker = graph.rebuild(ph, commsManager, currentFilter); + private Future cancellable; - return null; + void setCancellable(Future cancellable) { + this.cancellable = cancellable; } @Override - public boolean cancel() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + public void actionPerformed(ActionEvent e) { + cancellable.cancel(true); } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 6263c8c175..e4c3dc9746 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -44,8 +44,8 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; -import org.netbeans.api.progress.ProgressHandle; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; @@ -219,7 +219,7 @@ final class mxGraphImpl extends mxGraph { getView().updateBoundingBox(state); } - SwingWorker rebuild(ProgressHandle progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { return new RebuildWorker(progress, commsManager, currentFilter); } @@ -296,11 +296,11 @@ final class mxGraphImpl extends mxGraph { */ private class RebuildWorker extends SwingWorker { - private final ProgressHandle progress; + private final ProgressIndicator progress; private final CommunicationsManager commsManager; private final CommunicationsFilter currentFilter; - RebuildWorker(ProgressHandle progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + RebuildWorker(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { this.progress = progress; this.currentFilter = currentFilter; this.commsManager = commsManager; @@ -309,9 +309,8 @@ final class mxGraphImpl extends mxGraph { @Override protected Void doInBackground() throws Exception { - progress.progress("Loading accounts"); - progress.switchToDeterminate(pinnedAccountDevices.size()); - progress.progress(0); + progress.start("Loading accounts"); + progress.switchToDeterminate("Loading accounts", 0,pinnedAccountDevices.size()); int i = 0; try { /** @@ -320,7 +319,6 @@ final class mxGraphImpl extends mxGraph { Set relatedAccounts = new HashSet<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { if (isCancelled()) { - cancel(true); break; } List relatedAccountDeviceInstances = @@ -339,12 +337,11 @@ final class mxGraphImpl extends mxGraph { //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); - progress.switchToDeterminate(relatedAccountsList.size()); + progress.switchToDeterminate("",0,relatedAccountsList.size()); for (i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); for (int j = i; j < relatedAccountsList.size(); j++) { if (isCancelled()) { - cancel(true); break; } AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); @@ -363,6 +360,7 @@ final class mxGraphImpl extends mxGraph { logger.log(Level.SEVERE, "Error", tskCoreException); } finally { } + return null; } @@ -374,7 +372,6 @@ final class mxGraphImpl extends mxGraph { } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, "Error building graph visualization. ", ex); } catch (CancellationException ex) { - logger.log(Level.INFO, "Graph visualization cancelled"); } finally { progress.finish(); diff --git a/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java b/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java index d49574bf24..992a35acc0 100644 --- a/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java +++ b/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java @@ -247,7 +247,7 @@ public final class ModalDialogProgressIndicator implements ProgressIndicator { DialogDescriptor.BOTTOM_ALIGN, HelpCtx.DEFAULT_HELP, buttonListener); - dialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor); + dialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor, parent); } else { /* * Dialog without buttons. @@ -256,6 +256,7 @@ public final class ModalDialogProgressIndicator implements ProgressIndicator { dialog.add(progressPanel); dialog.pack(); } + dialog.setResizable(false); dialog.setLocationRelativeTo(parent); this.dialog.setVisible(true); } From 54da8d314515a03eb0d09409d73c93d49b355f46 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 14 Feb 2018 17:02:51 +0100 Subject: [PATCH 5/5] use undoManager to implement better layout cancelation --- ...raphImpl.java => CommunicationsGraph.java} | 28 ++++---- .../communications/VisualizationPanel.java | 72 ++++++++++++------- 2 files changed, 59 insertions(+), 41 deletions(-) rename Core/src/org/sleuthkit/autopsy/communications/{mxGraphImpl.java => CommunicationsGraph.java} (92%) diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java similarity index 92% rename from Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java rename to Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index e4c3dc9746..b7559141ec 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -52,25 +52,25 @@ import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; 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 */ private final static Mustache labelMustache; static { -// String labelTemplatePath = "/org/sleuthkit/autopsy/communications/Vertex_Label_template.html"; - InputStream templateStream = mxGraphImpl.class.getResourceAsStream("/org/sleuthkit/autopsy/communications/Vertex_Label_template.html"); + InputStream templateStream = CommunicationsGraph.class.getResourceAsStream("/org/sleuthkit/autopsy/communications/Vertex_Label_template.html"); labelMustache = new DefaultMustacheFactory().compile(new InputStreamReader(templateStream), "Vertex_Label"); } static final private mxStylesheet mxStylesheet = new mxStylesheet(); - private final HashSet pinnedAccountDevices = new HashSet<>(); - private final HashSet lockedVertices = new HashSet<>(); + private final Set pinnedAccountDevices = new HashSet<>(); + private final Set lockedVertices = new HashSet<>(); private final Map nodeMap = new HashMap<>(); private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); @@ -88,7 +88,7 @@ final class mxGraphImpl extends mxGraph { mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); } - mxGraphImpl() { + CommunicationsGraph() { super(mxStylesheet); setAutoSizeCells(true); setCellsCloneable(false); @@ -130,7 +130,7 @@ final class mxGraphImpl extends mxGraph { scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); 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()))); scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); @@ -156,7 +156,7 @@ final class mxGraphImpl extends mxGraph { scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); 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()))); scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); @@ -310,7 +310,7 @@ final class mxGraphImpl extends mxGraph { @Override protected Void doInBackground() throws Exception { progress.start("Loading accounts"); - progress.switchToDeterminate("Loading accounts", 0,pinnedAccountDevices.size()); +// progress.switchToDeterminate("Loading accounts", 0,pinnedAccountDevices.size()); int i = 0; try { /** @@ -337,7 +337,7 @@ final class mxGraphImpl extends mxGraph { //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); - progress.switchToDeterminate("",0,relatedAccountsList.size()); + progress.switchToDeterminate("", 0, relatedAccountsList.size()); for (i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); for (int j = i; j < relatedAccountsList.size(); j++) { @@ -360,7 +360,7 @@ final class mxGraphImpl extends mxGraph { logger.log(Level.SEVERE, "Error", tskCoreException); } finally { } - + return null; } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 43f24b5899..0d0991f840 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -34,6 +34,8 @@ import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxPoint; import com.mxgraph.util.mxRectangle; +import com.mxgraph.util.mxUndoManager; +import com.mxgraph.util.mxUndoableEdit; import com.mxgraph.view.mxGraph; import java.awt.BorderLayout; import java.awt.Color; @@ -45,7 +47,6 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.text.DecimalFormat; import java.util.Arrays; @@ -97,6 +98,7 @@ import org.sleuthkit.datamodel.TskCoreException; * CVTTopComponent when this tab is active allowing for context sensitive * actions to work correctly. */ +@NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel") final public class VisualizationPanel extends JPanel implements Lookup.Provider { private static final long serialVersionUID = 1L; @@ -113,17 +115,23 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider static final private ImageIcon lockIcon = 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 gacEM = new ExplorerManager(); private final ProxyLookup proxyLookup = new ProxyLookup( ExplorerUtils.createLookup(gacEM, getActionMap()), ExplorerUtils.createLookup(vizEM, getActionMap())); - private final mxGraphComponent graphComponent; - private final mxGraphImpl graph; + private Frame windowAncestor; private CommunicationsManager commsManager; private CommunicationsFilter currentFilter; + + private final mxGraphComponent graphComponent; + private final CommunicationsGraph graph; + + protected mxUndoManager undoManager = new mxUndoManager(); private final mxRubberband rubberband; private final mxFastOrganicLayout fastOrganicLayout; private final mxCircleLayout circleLayout; @@ -132,11 +140,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private SwingWorker worker; - private Frame windowAncestor; public VisualizationPanel() { initComponents(); - graph = new mxGraphImpl(); + graph = new CommunicationsGraph(); fastOrganicLayout = new mxFastOrganicLayoutImpl(graph); circleLayout = new mxCircleLayoutImpl(graph); @@ -232,6 +239,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider //feed selection to explorermanager 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 @@ -289,24 +301,21 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } CancelationListener cancelationListener = new CancelationListener(); - ModalDialogProgressIndicator modalDialogProgressIndicator = new ModalDialogProgressIndicator(windowAncestor, "Loading Visualization", new String[]{"Cancel"}, "Cancel", cancelationListener); - worker = graph.rebuild(modalDialogProgressIndicator, commsManager, currentFilter); - cancelationListener.setCancellable(worker); - worker.addPropertyChangeListener(new PropertyChangeListener() { - @Override - public void propertyChange(final PropertyChangeEvent evt) { - if (worker.isDone()) { - if (worker.isCancelled()) { - graph.resetGraph(); - rebuildGraph(); - } else if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { - applyOrganicLayout(10); - } else { - JOptionPane.showMessageDialog(VisualizationPanel.this, - "Too many accounts, layout aborted.", - "Autopsy", - JOptionPane.WARNING_MESSAGE); - } + ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Loading Visualization", new String[]{CANCEL}, CANCEL, cancelationListener); + worker = graph.rebuild(progress, commsManager, currentFilter); + cancelationListener.configure(worker, progress); + worker.addPropertyChangeListener((final PropertyChangeEvent evt) -> { + if (worker.isDone()) { + if (worker.isCancelled()) { + graph.resetGraph(); + rebuildGraph(); + } else if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { + applyOrganicLayout(10); + } else { + JOptionPane.showMessageDialog(VisualizationPanel.this, + "Too many accounts, layout aborted.", + "Autopsy", + JOptionPane.WARNING_MESSAGE); } } }); @@ -632,7 +641,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().beginUpdate(); CancelationListener cancelationListener = new CancelationListener(); - ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Computing layout", new String[]{"Cancel"}, "Cancel", cancelationListener); + ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Computing layout", new String[]{CANCEL}, CANCEL, cancelationListener); SwingWorker morphWorker = new SwingWorker() { @Override protected Void doInBackground() throws Exception { @@ -657,15 +666,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { graph.getModel().endUpdate(); - fitGraph(); + if (isCancelled()) { + undoManager.undo(); + } else { + fitGraph(); + } progress.finish(); }); morph.startAnimation(); return null; + } }; - cancelationListener.setCancellable(morphWorker); + cancelationListener.configure(morphWorker, progress); morphWorker.execute(); } @@ -813,14 +827,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private class CancelationListener implements ActionListener { private Future cancellable; + private ModalDialogProgressIndicator progress; - void setCancellable(Future cancellable) { + 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(); } }