19 package org.sleuthkit.autopsy.communications;
 
   21 import com.google.common.eventbus.Subscribe;
 
   22 import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
 
   23 import com.mxgraph.layout.mxCircleLayout;
 
   24 import com.mxgraph.layout.mxFastOrganicLayout;
 
   25 import com.mxgraph.layout.mxIGraphLayout;
 
   26 import com.mxgraph.layout.mxOrganicLayout;
 
   27 import com.mxgraph.model.mxCell;
 
   28 import com.mxgraph.model.mxICell;
 
   29 import com.mxgraph.swing.handler.mxRubberband;
 
   30 import com.mxgraph.swing.mxGraphComponent;
 
   31 import com.mxgraph.util.mxCellRenderer;
 
   32 import com.mxgraph.util.mxEvent;
 
   33 import com.mxgraph.util.mxEventObject;
 
   34 import com.mxgraph.util.mxEventSource;
 
   35 import com.mxgraph.util.mxPoint;
 
   36 import com.mxgraph.util.mxRectangle;
 
   37 import com.mxgraph.util.mxUndoManager;
 
   38 import com.mxgraph.util.mxUndoableEdit;
 
   39 import com.mxgraph.view.mxCellState;
 
   40 import com.mxgraph.view.mxGraph;
 
   41 import com.mxgraph.view.mxGraphView;
 
   42 import java.awt.BorderLayout;
 
   43 import java.awt.Color;
 
   45 import java.awt.Dimension;
 
   47 import java.awt.Frame;
 
   48 import java.awt.Graphics;
 
   49 import java.awt.GridBagConstraints;
 
   50 import java.awt.GridBagLayout;
 
   51 import java.awt.GridLayout;
 
   52 import java.awt.Insets;
 
   53 import java.awt.event.ActionEvent;
 
   54 import java.awt.event.ActionListener;
 
   55 import java.awt.event.MouseAdapter;
 
   56 import java.awt.event.MouseEvent;
 
   57 import java.awt.event.MouseWheelEvent;
 
   58 import java.awt.image.BufferedImage;
 
   59 import java.beans.PropertyChangeEvent;
 
   60 import java.io.IOException;
 
   61 import java.nio.file.Files;
 
   62 import java.nio.file.Path;
 
   63 import java.nio.file.Paths;
 
   64 import java.text.DecimalFormat;
 
   65 import java.text.SimpleDateFormat;
 
   66 import java.util.Arrays;
 
   67 import java.util.Date;
 
   68 import java.util.EnumSet;
 
   69 import java.util.HashMap;
 
   70 import java.util.HashSet;
 
   73 import java.util.concurrent.Future;
 
   74 import java.util.function.BiConsumer;
 
   75 import java.util.logging.Level;
 
   76 import java.util.stream.Collectors;
 
   77 import java.util.stream.Stream;
 
   78 import javafx.application.Platform;
 
   79 import javafx.embed.swing.JFXPanel;
 
   80 import javafx.scene.Scene;
 
   81 import javafx.scene.layout.Pane;
 
   82 import javax.swing.AbstractAction;
 
   83 import javax.swing.ImageIcon;
 
   84 import javax.swing.JButton;
 
   85 import javax.swing.JLabel;
 
   86 import javax.swing.JMenuItem;
 
   87 import javax.swing.JOptionPane;
 
   88 import javax.swing.JPanel;
 
   89 import javax.swing.JPopupMenu;
 
   90 import javax.swing.JTextField;
 
   91 import javax.swing.JTextPane;
 
   92 import javax.swing.JToolBar;
 
   93 import javax.swing.SwingConstants;
 
   94 import javax.swing.SwingUtilities;
 
   95 import javax.swing.SwingWorker;
 
   96 import org.apache.commons.lang3.StringUtils;
 
   97 import org.controlsfx.control.Notifications;
 
   98 import org.openide.util.NbBundle;
 
   99 import org.openide.windows.WindowManager;
 
  124 @SuppressWarnings(
"PMD.SingularField") 
 
  127     private static final long serialVersionUID = 1L;
 
  129     private static final String BASE_IMAGE_PATH = 
"/org/sleuthkit/autopsy/communications/images";
 
  130     static final private ImageIcon unlockIcon
 
  132     static final private ImageIcon lockIcon
 
  135     @NbBundle.Messages(
"VisualizationPanel.cancelButton.text=Cancel")
 
  136     private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
 
  144     private final CommunicationsGraph 
graph;
 
  146     private final mxUndoManager undoManager = 
new mxUndoManager();
 
  150     private SwingWorker<?, ?> worker;
 
  151     private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
 
  152     private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
 
  155     private NamedGraphLayout currentLayout;
 
  159     private final StateManager stateManager;
 
  161     @NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
 
  163         this.relationshipBrowser = relationshipBrowser;
 
  166         Platform.runLater(() -> {
 
  167             notificationsJFXPanel.setScene(
new Scene(
new Pane()));
 
  170         graph = 
new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
 
  178         graphComponent = 
new mxGraphComponent(graph) {
 
  180             protected mxGraphComponent.mxGraphControl createGraphControl() {
 
  182                 return new mxGraphControl() {
 
  185                     public void paint(Graphics graphics) {
 
  187                             super.paint(graphics);
 
  188                         } 
catch (NullPointerException ex) { 
 
  194                             logger.log(Level.WARNING, 
"There was a NPE while painting the VisualizationPanel", ex);
 
  201         graphComponent.setAutoExtend(
true);
 
  202         graphComponent.setAutoScroll(
true);
 
  203         graphComponent.setAutoscrolls(
true);
 
  204         graphComponent.setConnectable(
false);
 
  205         graphComponent.setDragEnabled(
false);
 
  206         graphComponent.setKeepSelectionVisibleOnZoom(
true);
 
  207         graphComponent.setOpaque(
true);
 
  208         graphComponent.setToolTips(
true);
 
  209         graphComponent.setBackground(Color.WHITE);
 
  210         borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
 
  213         rubberband = 
new mxRubberband(graphComponent);
 
  215         lockedVertexModel.registerhandler(
this);
 
  217         final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
 
  218                 -> zoomPercentLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
 
  219         graph.getView().addListener(mxEvent.SCALE, scaleListener);
 
  220         graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
 
  223         graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
 
  224         graphComponent.getGraphControl().addMouseListener(graphMouseListener);
 
  228         final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
 
  229                 -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty(
"edit"));
 
  231         graph.getModel().addListener(mxEvent.UNDO, undoListener);
 
  232         graph.getView().addListener(mxEvent.UNDO, undoListener);
 
  237         BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
 
  238             layoutButtons.put(layout, layoutButton);
 
  239             layoutButton.addActionListener(event -> applyLayout(layout));
 
  242         configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
 
  244         applyLayout(fastOrganicLayout);
 
  246         stateManager = 
new StateManager(pinnedAccountModel);
 
  248         setStateButtonsEnabled();
 
  254     void handle(LockedVertexModel.VertexLockEvent event) {
 
  255         final Set<mxCell> vertices = 
event.getVertices();
 
  256         mxGraphView view = graph.getView();
 
  257         vertices.forEach(vertex -> {
 
  258             final mxCellState state = view.getState(vertex, 
true);
 
  259             view.updateLabel(state);
 
  260             view.updateLabelBounds(state);
 
  261             view.updateBoundingBox(state);
 
  262             graphComponent.redraw(state);
 
  267     void handle(
final CVTEvents.UnpinAccountsEvent pinEvent) {
 
  268         graph.getModel().beginUpdate();
 
  269         pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
 
  273         graph.getModel().endUpdate();
 
  275         setStateButtonsEnabled();
 
  279     void handle(
final CVTEvents.PinAccountsEvent pinEvent) {
 
  280         graph.getModel().beginUpdate();
 
  281         if (pinEvent.isReplace()) {
 
  284         pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances());
 
  287         graph.getModel().endUpdate();
 
  289         setStateButtonsEnabled();
 
  293     void handle(
final CVTEvents.FilterChangeEvent filterChangeEvent) {
 
  294         graph.getModel().beginUpdate();
 
  296         currentFilter = filterChangeEvent.getNewFilter();
 
  299         graph.getModel().endUpdate();
 
  301         setStateButtonsEnabled();
 
  304     @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
 
  305     private 
void rebuildGraph() {
 
  306         if (pinnedAccountModel.isEmpty()) {
 
  307             borderLayoutPanel.remove(graphComponent);
 
  308             borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
 
  311             borderLayoutPanel.remove(placeHolderPanel);
 
  312             borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
 
  313             if (worker != null) {
 
  319             worker = graph.rebuild(progress, commsManager, currentFilter);
 
  320             cancelationListener.configure(worker, progress);
 
  321             worker.addPropertyChangeListener((
final PropertyChangeEvent evt) -> {
 
  322                 if (worker.isDone()) {
 
  323                     if (worker.isCancelled()) {
 
  327                     applyLayout(currentLayout);
 
  338         windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, 
this);
 
  343             logger.log(Level.SEVERE, 
"Error getting CommunicationsManager for the current case.", ex); 
 
  345             logger.log(Level.SEVERE, 
"Can't get CommunicationsManager when there is no case open.", ex); 
 
  349             graph.getModel().beginUpdate();
 
  353                 graph.getModel().endUpdate();
 
  355             if (evt.getNewValue() == null) {
 
  358                 Case currentCase = (
Case) evt.getNewValue();
 
  362                     logger.log(Level.SEVERE, 
"Error getting CommunicationsManager for the current case.", ex); 
 
  373     @SuppressWarnings(
"unchecked")
 
  375     private 
void initComponents() {
 
  376         GridBagConstraints gridBagConstraints;
 
  378         borderLayoutPanel = 
new JPanel();
 
  379         placeHolderPanel = 
new JPanel();
 
  380         jTextPane1 = 
new JTextPane();
 
  381         notificationsJFXPanel = 
new JFXPanel();
 
  382         toolbar = 
new JToolBar();
 
  383         backButton = 
new JButton();
 
  384         forwardButton = 
new JButton();
 
  385         jSeparator3 = 
new JToolBar.Separator();
 
  386         clearVizButton = 
new JButton();
 
  387         fastOrganicLayoutButton = 
new JButton();
 
  388         jSeparator2 = 
new JToolBar.Separator();
 
  389         zoomLabel = 
new JLabel();
 
  390         zoomPercentLabel = 
new JLabel();
 
  391         zoomOutButton = 
new JButton();
 
  392         fitZoomButton = 
new JButton();
 
  393         zoomActualButton = 
new JButton();
 
  394         zoomInButton = 
new JButton();
 
  395         jSeparator1 = 
new JToolBar.Separator();
 
  396         snapshotButton = 
new JButton();
 
  398         setLayout(
new BorderLayout());
 
  400         borderLayoutPanel.setLayout(
new BorderLayout());
 
  402         placeHolderPanel.setLayout(
new GridBagLayout());
 
  404         jTextPane1.setEditable(
false);
 
  405         jTextPane1.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.jTextPane1.text")); 
 
  406         jTextPane1.setOpaque(
false);
 
  407         gridBagConstraints = 
new GridBagConstraints();
 
  408         gridBagConstraints.anchor = GridBagConstraints.NORTH;
 
  409         gridBagConstraints.weighty = 1.0;
 
  410         gridBagConstraints.insets = 
new Insets(50, 0, 0, 0);
 
  411         placeHolderPanel.add(jTextPane1, gridBagConstraints);
 
  413         borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
 
  414         borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
 
  416         add(borderLayoutPanel, BorderLayout.CENTER);
 
  418         toolbar.setRollover(
true);
 
  420         backButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_previous.png"))); 
 
  421         backButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.backButton.text_1")); 
 
  422         backButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.backButton.toolTipText")); 
 
  423         backButton.setFocusable(
false);
 
  424         backButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  425         backButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  426         backButton.addActionListener(
new ActionListener() {
 
  427             public void actionPerformed(ActionEvent evt) {
 
  428                 backButtonActionPerformed(evt);
 
  431         toolbar.add(backButton);
 
  433         forwardButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_next.png"))); 
 
  434         forwardButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.forwardButton.text")); 
 
  435         forwardButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.forwardButton.toolTipText")); 
 
  436         forwardButton.setFocusable(
false);
 
  437         forwardButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  438         forwardButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  439         forwardButton.addActionListener(
new ActionListener() {
 
  440             public void actionPerformed(ActionEvent evt) {
 
  441                 forwardButtonActionPerformed(evt);
 
  444         toolbar.add(forwardButton);
 
  445         toolbar.add(jSeparator3);
 
  447         clearVizButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/broom.png"))); 
 
  448         clearVizButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.clearVizButton.text_1")); 
 
  449         clearVizButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.clearVizButton.toolTipText")); 
 
  450         clearVizButton.setActionCommand(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.clearVizButton.actionCommand")); 
 
  451         clearVizButton.setFocusable(
false);
 
  452         clearVizButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  453         clearVizButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  454         clearVizButton.addActionListener(
new ActionListener() {
 
  455             public void actionPerformed(ActionEvent evt) {
 
  456                 clearVizButtonActionPerformed(evt);
 
  459         toolbar.add(clearVizButton);
 
  461         fastOrganicLayoutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); 
 
  462         fastOrganicLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fastOrganicLayoutButton.text")); 
 
  463         fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fastOrganicLayoutButton.toolTipText")); 
 
  464         fastOrganicLayoutButton.setFocusable(
false);
 
  465         fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  466         fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  467         fastOrganicLayoutButton.addActionListener(
new ActionListener() {
 
  468             public void actionPerformed(ActionEvent evt) {
 
  469                 fastOrganicLayoutButtonActionPerformed(evt);
 
  472         toolbar.add(fastOrganicLayoutButton);
 
  473         toolbar.add(jSeparator2);
 
  475         zoomLabel.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomLabel.text")); 
 
  476         toolbar.add(zoomLabel);
 
  478         zoomPercentLabel.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomPercentLabel.text")); 
 
  479         toolbar.add(zoomPercentLabel);
 
  481         zoomOutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); 
 
  482         zoomOutButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomOutButton.text")); 
 
  483         zoomOutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomOutButton.toolTipText")); 
 
  484         zoomOutButton.setFocusable(
false);
 
  485         zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  486         zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  487         zoomOutButton.addActionListener(
new ActionListener() {
 
  488             public void actionPerformed(ActionEvent evt) {
 
  489                 zoomOutButtonActionPerformed(evt);
 
  492         toolbar.add(zoomOutButton);
 
  494         fitZoomButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); 
 
  495         fitZoomButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fitZoomButton.text")); 
 
  496         fitZoomButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fitZoomButton.toolTipText")); 
 
  497         fitZoomButton.setFocusable(
false);
 
  498         fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  499         fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  500         fitZoomButton.addActionListener(
new ActionListener() {
 
  501             public void actionPerformed(ActionEvent evt) {
 
  502                 fitZoomButtonActionPerformed(evt);
 
  505         toolbar.add(fitZoomButton);
 
  507         zoomActualButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); 
 
  508         zoomActualButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomActualButton.text")); 
 
  509         zoomActualButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomActualButton.toolTipText")); 
 
  510         zoomActualButton.setFocusable(
false);
 
  511         zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  512         zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  513         zoomActualButton.addActionListener(
new ActionListener() {
 
  514             public void actionPerformed(ActionEvent evt) {
 
  515                 zoomActualButtonActionPerformed(evt);
 
  518         toolbar.add(zoomActualButton);
 
  520         zoomInButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); 
 
  521         zoomInButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomInButton.text")); 
 
  522         zoomInButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomInButton.toolTipText")); 
 
  523         zoomInButton.setFocusable(
false);
 
  524         zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  525         zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  526         zoomInButton.addActionListener(
new ActionListener() {
 
  527             public void actionPerformed(ActionEvent evt) {
 
  528                 zoomInButtonActionPerformed(evt);
 
  531         toolbar.add(zoomInButton);
 
  532         toolbar.add(jSeparator1);
 
  534         snapshotButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/report/images/image.png"))); 
 
  535         snapshotButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.snapshotButton.text_1")); 
 
  536         snapshotButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.snapshotButton.toolTipText")); 
 
  537         snapshotButton.setFocusable(
false);
 
  538         snapshotButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  539         snapshotButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  540         snapshotButton.addActionListener(
new ActionListener() {
 
  541             public void actionPerformed(ActionEvent evt) {
 
  542                 snapshotButtonActionPerformed(evt);
 
  545         toolbar.add(snapshotButton);
 
  547         add(toolbar, BorderLayout.NORTH);
 
  555         graphComponent.zoomActual();
 
  556         CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
  560         graphComponent.zoomIn();
 
  561         CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
  565         graphComponent.zoomOut();
 
  566         CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
  575     @NbBundle.Messages({
"VisualizationPanel.computingLayout=Computing Layout",
 
  576         "# {0} - layout name",
 
  577         "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
 
  578         "# {0} -  layout name",
 
  579         "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
 
  581         currentLayout = layout;
 
  582         layoutButtons.forEach((layoutKey, button)
 
  583                 -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
 
  586         progressIndicator.
start(Bundle.VisualizationPanel_computingLayout());
 
  587         graph.getModel().beginUpdate();
 
  589             layout.execute(graph.getDefaultParent());
 
  592             graph.getModel().endUpdate();
 
  593             progressIndicator.
finish();
 
  598         CVTEvents.getCVTEventBus().post(
new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts()));
 
  602         handleStateChange(stateManager.advance());
 
  606         handleStateChange(stateManager.retreat());
 
  615         if(newState == null) {
 
  620         if(newState.isZoomChange()) {
 
  621             graph.getView().setScale(newState.getZoomValue());
 
  626         CVTEvents.getCVTEventBus().post(
new CVTEvents.StateChangeEvent(newState));
 
  627         setStateButtonsEnabled();
 
  629         graph.getModel().beginUpdate();
 
  632         if(newState.getPinnedList() != null) {
 
  633             pinnedAccountModel.pinAccount(newState.getPinnedList());
 
  635             pinnedAccountModel.clear();
 
  638         currentFilter = newState.getCommunicationsFilter();
 
  642         graph.getModel().endUpdate();
 
  649         backButton.setEnabled(stateManager.canRetreat());
 
  650         forwardButton.setEnabled(stateManager.canAdvance());
 
  654          "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation." 
  658             handleSnapshotEvent();
 
  660             logger.log(Level.SEVERE, 
"Unable to create communications snapsot report", ex); 
 
  663                     -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
 
  664                             .text(Bundle.VisualizationPanel_snapshot_report_failure())
 
  667             logger.log(Level.WARNING, 
"Unable to add report to currenct case", ex); 
 
  676         graphComponent.zoomTo(1, 
true);
 
  677         mxPoint translate = graph.getView().getTranslate();
 
  678         if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
 
  679             translate = 
new mxPoint();
 
  682         mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(), 
true, 
true, 
true);
 
  683         if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
 
  684             boundsForCells = 
new mxRectangle(0, 0, 1, 1);
 
  686         final mxPoint mxPoint = 
new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
 
  688         graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(), 
false, 
false);
 
  690         boundsForCells = graph.getCellBounds(graph.getDefaultParent(), 
true, 
true, 
true);
 
  691         if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
 
  692             boundsForCells = 
new mxRectangle(0, 0, 1, 1);
 
  695         final Dimension size = graphComponent.getSize();
 
  696         final double widthFactor = size.getWidth() / boundsForCells.getWidth();
 
  697         final double heightFactor = size.getHeight() / boundsForCells.getHeight();
 
  699         graphComponent.zoom((heightFactor + widthFactor) / 2.0);
 
  709         "VisualizationPanel_action_dialogs_title=Communications",
 
  710         "VisualizationPanel_module_name=Communications",
 
  711         "VisualizationPanel_action_name_text=Snapshot Report",
 
  712         "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
 
  713         "VisualizationPane_reportName=Communications Snapshot",
 
  714         "# {0} -  default name",
 
  715         "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
 
  716         "VisualizationPane_blank_report_title=Blank Report Name",
 
  717         "# {0} -  report name",
 
  718         "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}" 
  722         Date generationDate = 
new Date();
 
  726         final JTextField text = 
new JTextField(50);
 
  727         final JPanel panel = 
new JPanel(
new GridLayout(2, 1));
 
  728         panel.add(
new JLabel(Bundle.VisualizationPane_fileName_prompt()));
 
  731         text.setText(defaultReportName);
 
  733         int result = JOptionPane.showConfirmDialog(graphComponent, panel,
 
  734                 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
 
  736         if (result == JOptionPane.OK_OPTION) {
 
  737             String enteredReportName = text.getText();
 
  739             if(enteredReportName.trim().isEmpty()){
 
  740                 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
 
  741                 if(result != JOptionPane.OK_OPTION) {
 
  746             String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
 
  748             if (Files.exists(reportPath)) {
 
  749                 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
 
  750                         Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
 
  752                 if (result == JOptionPane.OK_OPTION) {
 
  754                     createReport(currentCase, reportName);
 
  757                 createReport(currentCase, reportName);
 
  758                 currentCase.
addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
 
  773         "VisualizationPane_DisplayName=Open Report",
 
  774         "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
 
  775         "VisualizationPane_MessageBoxTitle=Open Report Failure",
 
  776         "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
 
  777         "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
 
  778         "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
 
  779         "# {0} -  report path",
 
  780         "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
 
  781         "VisualizationPane_Report_OK_Button=OK",
 
  782         "VisualizationPane_Open_Report=Open Report",})
 
  786         Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName, Bundle.VisualizationPane_reportName()); 
 
  787         BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE, 
true, null);
 
  791         String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
 
  792         String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
 
  794         int result = JOptionPane.showOptionDialog(graphComponent, message,
 
  795                 Bundle.VisualizationPanel_action_dialogs_title(),
 
  796                 JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
 
  797                 null, buttons, buttons[1]);
 
  798         if (result == JOptionPane.YES_NO_OPTION) {
 
  801             } 
catch (IOException ex) {
 
  802                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  803                         Bundle.VisualizationPane_NoAssociatedEditorMessage(),
 
  804                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  805                         JOptionPane.ERROR_MESSAGE);
 
  806             } 
catch (UnsupportedOperationException ex) {
 
  807                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  808                         Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
 
  809                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  810                         JOptionPane.ERROR_MESSAGE);
 
  811             } 
catch (IllegalArgumentException ex) {
 
  812                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  813                         Bundle.VisualizationPane_MissingReportFileMessage(),
 
  814                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  815                         JOptionPane.ERROR_MESSAGE);
 
  816             } 
catch (SecurityException ex) {
 
  817                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  818                         Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
 
  819                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  820                         JOptionPane.ERROR_MESSAGE);
 
  853         @SuppressWarnings(
"unchecked")
 
  855         public void invoke(Object sender, mxEventObject evt) {
 
  856             Object[] selectionCells = graph.getSelectionCells();
 
  857             if (selectionCells.length > 0) {
 
  858                 mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(
new mxCell[selectionCells.length]);
 
  859                 HashSet<AccountDeviceInstance> selectedNodes = 
new HashSet<>();
 
  861                 for (mxICell cell : selectedCells) {
 
  863                         mxICell source = (mxICell) graph.getModel().getTerminal(cell, 
true);
 
  864                         mxICell target = (mxICell) graph.getModel().getTerminal(cell, 
false);
 
  866                         selectedEdges.add(
new SelectionInfo.
GraphEdge(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance(),
 
  867                             ((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance()));
 
  869                     } 
else if (cell.isVertex()) {
 
  870                         selectedNodes.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
 
  874                 relationshipBrowser.setSelectionInfo(
new SelectionInfo(selectedNodes, selectedEdges, currentFilter));
 
  876                 relationshipBrowser.setSelectionInfo(
new SelectionInfo(
new HashSet<>(), 
new HashSet<>(), currentFilter));
 
  886         String getDisplayName();
 
  900             return super.isVertexIgnored(vertex)
 
  901                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
  906             if (isVertexIgnored(vertex)) {
 
  907                 return getVertexBounds(vertex);
 
  909                 return super.setVertexLocation(vertex, x, y);
 
  915             return "Fast Organic";
 
  931             return super.isVertexIgnored(vertex)
 
  932                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
  937             if (isVertexIgnored(vertex)) {
 
  938                 return getVertexBounds(vertex);
 
  940                 return super.setVertexLocation(vertex, x, y);
 
  962             return super.isVertexIgnored(vertex)
 
  963                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
  968             if (isVertexIgnored(vertex)) {
 
  969                 return getVertexBounds(vertex);
 
  971                 return super.setVertexLocation(vertex, x, y);
 
  992             return super.isVertexIgnored(vertex)
 
  993                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
  998             if (isVertexIgnored(vertex)) {
 
  999                 return getVertexBounds(vertex);
 
 1001                 return super.setVertexLocation(vertex, x, y);
 
 1007             return "Hierarchical";
 
 1021             this.cancellable = cancellable;
 
 1022             this.progress = progress;
 
 1028             cancellable.cancel(
true);
 
 1046             super.mouseWheelMoved(event);
 
 1047             if (event.getPreciseWheelRotation() < 0) {
 
 1048                 graphComponent.zoomIn();
 
 1049             } 
else if (event.getPreciseWheelRotation() > 0) {
 
 1050                 graphComponent.zoomOut();
 
 1053             CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
 1063             super.mouseClicked(event);
 
 1064             if (SwingUtilities.isRightMouseButton(event)) {
 
 1065                 final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(), 
event.getY());
 
 1066                 if (cellAt != null && cellAt.isVertex()) {
 
 1067                     final JPopupMenu jPopupMenu = 
new JPopupMenu();
 
 1068                     final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
 
 1070                     Set<mxCell> selectedVertices
 
 1071                             = Stream.of(graph.getSelectionModel().getCells())
 
 1072                                     .map(mxCell.class::cast)
 
 1073                                     .filter(mxCell::isVertex)
 
 1074                                     .collect(Collectors.toSet());
 
 1076                     if (lockedVertexModel.isVertexLocked(cellAt)) {
 
 1077                         jPopupMenu.add(
new JMenuItem(
new UnlockAction(selectedVertices)));
 
 1079                         jPopupMenu.add(
new JMenuItem(
new LockAction(selectedVertices)));
 
 1081                     if (pinnedAccountModel.isAccountPinned(adiKey.getAccountDeviceInstance())) {
 
 1082                         jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
 
 1084                         jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
 
 1085                         jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
 
 1087                     jPopupMenu.show(graphComponent.getGraphControl(), 
event.getX(), 
event.getY());
 
 1096     @NbBundle.Messages({
 
 1097         "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
 
 1098         "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
 
 1104             super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
 
 1106             this.selectedVertices = selectedVertices;
 
 1112             lockedVertexModel.unlock(selectedVertices);
 
 1119     @NbBundle.Messages({
 
 1120         "VisualizationPanel.lockAction.singularText=Lock Selected Account",
 
 1121         "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
 
 1127             super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
 
 1129             this.selectedVertices = selectedVertices;
 
 1134             lockedVertexModel.lock(selectedVertices);
 
void zoomOutButtonActionPerformed(ActionEvent evt)
final Set< mxCell > selectedVertices
boolean isVertexIgnored(Object vertex)
void invoke(Object sender, mxEventObject evt)
JFXPanel notificationsJFXPanel
CommunicationsManager getCommunicationsManager()
void forwardButtonActionPerformed(ActionEvent evt)
void mouseClicked(final MouseEvent event)
void createReport(Case currentCase, String reportName)
CommunicationsManager commsManager
boolean isVertexIgnored(Object vertex)
void handleStateChange(StateManager.CommunicationsState newState)
void applyLayout(NamedGraphLayout layout)
void actionPerformed(final ActionEvent event)
final mxRubberband rubberband
static boolean deleteFileDir(File path)
String getReportDirectory()
JToolBar.Separator jSeparator3
void fastOrganicLayoutButtonActionPerformed(ActionEvent evt)
void addReport(String localPath, String srcModuleName, String reportName)
JToolBar.Separator jSeparator2
ModalDialogProgressIndicator progress
mxRectangle setVertexLocation(Object vertex, double x, double y)
synchronized void start(String message, int totalWorkUnits)
synchronized void setCancelling(String cancellingMessage)
final CommunicationsGraph graph
void actionPerformed(final ActionEvent event)
boolean isVertexIgnored(Object vertex)
CommunicationsFilter currentFilter
void backButtonActionPerformed(ActionEvent evt)
void fitZoomButtonActionPerformed(ActionEvent evt)
SleuthkitCase getSleuthkitCase()
void mouseWheelMoved(final MouseWheelEvent event)
JToolBar.Separator jSeparator1
void handleSnapshotEvent()
static String escapeFileName(String fileName)
mxRectangle setVertexLocation(Object vertex, double x, double y)
boolean isVertexIgnored(Object vertex)
mxRectangle setVertexLocation(Object vertex, double x, double y)
final mxGraphComponent graphComponent
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
void actionPerformed(ActionEvent event)
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
mxRectangle setVertexLocation(Object vertex, double x, double y)
JButton fastOrganicLayoutButton
void zoomInButtonActionPerformed(ActionEvent evt)
void setStateButtonsEnabled()
static Desktop getDesktop()
final Set< mxCell > selectedVertices
void zoomActualButtonActionPerformed(ActionEvent evt)
void clearVizButtonActionPerformed(ActionEvent evt)
void snapshotButtonActionPerformed(ActionEvent evt)
synchronized void finish()