diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 271d5b3b5a..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= diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java similarity index 90% rename from Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java rename to Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index 72210e37ff..b7559141ec 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.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; @@ -51,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(); @@ -87,7 +88,7 @@ final class mxGraphImpl extends mxGraph { mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); } - mxGraphImpl() { + CommunicationsGraph() { super(mxStylesheet); setAutoSizeCells(true); setCellsCloneable(false); @@ -129,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); @@ -155,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); @@ -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 { @@ -304,11 +304,13 @@ final class mxGraphImpl extends mxGraph { this.progress = progress; this.currentFilter = currentFilter; this.commsManager = commsManager; + } @Override 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; try { /** @@ -316,6 +318,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 +341,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(), @@ -353,6 +360,7 @@ final class mxGraphImpl extends mxGraph { logger.log(Level.SEVERE, "Error", tskCoreException); } finally { } + return null; } @@ -363,6 +371,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(); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index fa4b00c17a..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,65 +38,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -108,18 +49,18 @@ - + - + - + - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 217bbb168b..0d0991f840 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -34,17 +34,19 @@ 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; import java.awt.Dimension; +import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.text.DecimalFormat; import java.util.Arrays; @@ -52,15 +54,16 @@ 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; @@ -78,7 +81,8 @@ 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; @@ -94,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; @@ -110,27 +115,35 @@ 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; private final mxOrganicLayout organicLayout; private final mxHierarchicalLayout hierarchicalLayout; + + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private SwingWorker worker; public VisualizationPanel() { initComponents(); - graph = new mxGraphImpl(); + graph = new CommunicationsGraph(); fastOrganicLayout = new mxFastOrganicLayoutImpl(graph); circleLayout = new mxCircleLayoutImpl(graph); @@ -148,7 +161,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.setToolTips(true); graphComponent.setBackground(Color.WHITE); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); - progressBar.setVisible(false); //install rubber band selection handler rubberband = new mxRubberband(graphComponent); @@ -176,10 +188,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) { @@ -188,7 +200,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 @@ -196,7 +207,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) { @@ -206,7 +216,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) { @@ -220,7 +229,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } })); } - 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 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 @@ -241,35 +254,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,43 +279,47 @@ 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 { + @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); } - worker = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter); - worker.addPropertyChangeListener(new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (worker.isDone()) { - if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { - applyOrganicLayout(10); - } else { - statusLabel.setText("Too many cells, layout aborted."); - } + CancelationListener cancelationListener = new CancelationListener(); + 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); } } }); + worker.execute(); } @@ -322,10 +328,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void addNotify() { super.addNotify(); -// IngestManager.getInstance().addIngestModuleEventListener(ingestListener); + 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) { @@ -357,7 +363,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void removeNotify() { super.removeNotify(); -// IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); } /** @@ -371,10 +376,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane = new JSplitPane(); borderLayoutPanel = new JPanel(); - jToolBar2 = new JToolBar(); - statusLabel = new JLabel(); - progresPanel = new JPanel(); - progressBar = new JProgressBar(); placeHolderPanel = new JPanel(); jTextArea1 = new JTextArea(); toolbar = new JPanel(); @@ -398,35 +399,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); - - 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.setColumns(20); jTextArea1.setLineWrap(true); @@ -437,15 +409,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(208, Short.MAX_VALUE) .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) .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(200, Short.MAX_VALUE) + .addContainerGap(213, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 43, GroupLayout.PREFERRED_SIZE) - .addContainerGap(200, Short.MAX_VALUE)) + .addContainerGap(214, Short.MAX_VALUE)) ); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); @@ -638,7 +610,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 +622,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 +632,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,27 +640,47 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // layout using morphing graph.getModel().beginUpdate(); - progressBar.setVisible(true); - progressBar.setIndeterminate(true); - progressBar.setString("applying layout"); - - 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(); - progressBar.setVisible(false); - progressBar.setValue(0); + if (isCancelled()) { + undoManager.undo(); + } else { + fitGraph(); + } + progress.finish(); }); morph.startAnimation(); return null; - } - }.execute(); + } + }; + cancelationListener.configure(morphWorker, progress); + morphWorker.execute(); } // Variables declaration - do not modify//GEN-BEGIN:variables @@ -703,13 +693,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 JPanel progresPanel; - private JProgressBar progressBar; private JSplitPane splitPane; - private JLabel statusLabel; private JPanel toolbar; private JButton zoomActualButton; 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 { 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(); + } + + } } 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); }