From 54da8d314515a03eb0d09409d73c93d49b355f46 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 14 Feb 2018 17:02:51 +0100 Subject: [PATCH] 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(); } }