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;
 
   44 import java.awt.Desktop;
 
   45 import java.awt.Dimension;
 
   47 import java.awt.Frame;
 
   48 import java.awt.Graphics;
 
   49 import java.awt.GridLayout;
 
   50 import java.awt.event.ActionEvent;
 
   51 import java.awt.event.ActionListener;
 
   52 import java.awt.event.MouseAdapter;
 
   53 import java.awt.event.MouseEvent;
 
   54 import java.awt.event.MouseWheelEvent;
 
   55 import java.awt.image.BufferedImage;
 
   56 import java.beans.PropertyChangeEvent;
 
   57 import java.io.IOException;
 
   58 import java.nio.file.Files;
 
   59 import java.nio.file.Path;
 
   60 import java.nio.file.Paths;
 
   61 import java.text.DecimalFormat;
 
   62 import java.text.SimpleDateFormat;
 
   63 import java.util.Arrays;
 
   64 import java.util.Collections;
 
   65 import java.util.Date;
 
   66 import java.util.EnumSet;
 
   67 import java.util.HashMap;
 
   68 import java.util.HashSet;
 
   71 import java.util.concurrent.ExecutionException;
 
   72 import java.util.concurrent.Future;
 
   73 import java.util.function.BiConsumer;
 
   74 import java.util.logging.Level;
 
   75 import java.util.stream.Collectors;
 
   76 import java.util.stream.Stream;
 
   77 import javafx.application.Platform;
 
   78 import javafx.embed.swing.JFXPanel;
 
   79 import javafx.scene.Scene;
 
   80 import javafx.scene.layout.Pane;
 
   81 import javax.swing.AbstractAction;
 
   82 import javax.swing.ImageIcon;
 
   83 import javax.swing.JButton;
 
   84 import javax.swing.JLabel;
 
   85 import javax.swing.JMenuItem;
 
   86 import javax.swing.JOptionPane;
 
   87 import javax.swing.JPanel;
 
   88 import javax.swing.JPopupMenu;
 
   89 import javax.swing.JSplitPane;
 
   90 import javax.swing.JTextArea;
 
   91 import javax.swing.JTextField;
 
   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.jdesktop.layout.GroupLayout;
 
   99 import org.jdesktop.layout.LayoutStyle;
 
  100 import org.openide.util.NbBundle;
 
  101 import org.openide.windows.WindowManager;
 
  125 @SuppressWarnings(
"PMD.SingularField") 
 
  128     private static final long serialVersionUID = 1L;
 
  130     private static final String BASE_IMAGE_PATH = 
"/org/sleuthkit/autopsy/communications/images";
 
  131     static final private ImageIcon unlockIcon
 
  133     static final private ImageIcon lockIcon
 
  136     @NbBundle.Messages(
"VisualizationPanel.cancelButton.text=Cancel")
 
  137     private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
 
  145     private final CommunicationsGraph 
graph;
 
  147     private final mxUndoManager undoManager = 
new mxUndoManager();
 
  151     private SwingWorker<?, ?> worker;
 
  152     private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
 
  153     private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
 
  156     private NamedGraphLayout currentLayout;
 
  160     private final StateManager stateManager;
 
  162     @NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
 
  166         notificationsJFXPanel.setScene(
new Scene(
new Pane()));
 
  168         graph = 
new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
 
  176         graphComponent = 
new mxGraphComponent(graph) {
 
  178             protected mxGraphComponent.mxGraphControl createGraphControl() {
 
  180                 return new mxGraphControl() {
 
  183                     public void paint(Graphics graphics) {
 
  185                             super.paint(graphics);
 
  186                         } 
catch (NullPointerException ex) { 
 
  192                             logger.log(Level.WARNING, 
"There was a NPE while painting the VisualizationPanel", ex);
 
  199         graphComponent.setAutoExtend(
true);
 
  200         graphComponent.setAutoScroll(
true);
 
  201         graphComponent.setAutoscrolls(
true);
 
  202         graphComponent.setConnectable(
false);
 
  203         graphComponent.setDragEnabled(
false);
 
  204         graphComponent.setKeepSelectionVisibleOnZoom(
true);
 
  205         graphComponent.setOpaque(
true);
 
  206         graphComponent.setToolTips(
true);
 
  207         graphComponent.setBackground(Color.WHITE);
 
  208         borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
 
  211         rubberband = 
new mxRubberband(graphComponent);
 
  213         lockedVertexModel.registerhandler(
this);
 
  215         final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
 
  216                 -> zoomPercentLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
 
  217         graph.getView().addListener(mxEvent.SCALE, scaleListener);
 
  218         graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
 
  221         graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
 
  222         graphComponent.getGraphControl().addMouseListener(graphMouseListener);
 
  225         splitPane.setRightComponent(relationshipBrowser);
 
  229         final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
 
  230                 -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty(
"edit"));
 
  232         graph.getModel().addListener(mxEvent.UNDO, undoListener);
 
  233         graph.getView().addListener(mxEvent.UNDO, undoListener);
 
  238         BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
 
  239             layoutButtons.put(layout, layoutButton);
 
  240             layoutButton.addActionListener(event -> applyLayout(layout));
 
  243         configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
 
  245         applyLayout(fastOrganicLayout);
 
  247         stateManager = 
new StateManager(pinnedAccountModel);
 
  249         setStateButtonsEnabled();
 
  253     void handle(LockedVertexModel.VertexLockEvent event) {
 
  254         final Set<mxCell> vertices = 
event.getVertices();
 
  255         mxGraphView view = graph.getView();
 
  256         vertices.forEach(vertex -> {
 
  257             final mxCellState state = view.getState(vertex, 
true);
 
  258             view.updateLabel(state);
 
  259             view.updateLabelBounds(state);
 
  260             view.updateBoundingBox(state);
 
  261             graphComponent.redraw(state);
 
  266     void handle(
final CVTEvents.UnpinAccountsEvent pinEvent) {
 
  267         graph.getModel().beginUpdate();
 
  268         pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
 
  272         graph.getModel().endUpdate();
 
  274         setStateButtonsEnabled();
 
  278     void handle(
final CVTEvents.PinAccountsEvent pinEvent) {
 
  279         graph.getModel().beginUpdate();
 
  280         if (pinEvent.isReplace()) {
 
  283         pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances());
 
  286         graph.getModel().endUpdate();
 
  288         setStateButtonsEnabled();
 
  292     void handle(
final CVTEvents.FilterChangeEvent filterChangeEvent) {
 
  293         graph.getModel().beginUpdate();
 
  295         currentFilter = filterChangeEvent.getNewFilter();
 
  298         graph.getModel().endUpdate();
 
  300         setStateButtonsEnabled();
 
  303     @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
 
  304     private 
void rebuildGraph() {
 
  305         if (pinnedAccountModel.isEmpty()) {
 
  306             borderLayoutPanel.remove(graphComponent);
 
  307             borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
 
  310             borderLayoutPanel.remove(placeHolderPanel);
 
  311             borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
 
  312             if (worker != null) {
 
  318             worker = graph.rebuild(progress, commsManager, currentFilter);
 
  319             cancelationListener.configure(worker, progress);
 
  320             worker.addPropertyChangeListener((
final PropertyChangeEvent evt) -> {
 
  321                 if (worker.isDone()) {
 
  322                     if (worker.isCancelled()) {
 
  326                     applyLayout(currentLayout);
 
  337         windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, 
this);
 
  341         } 
catch (TskCoreException ex) {
 
  342             logger.log(Level.SEVERE, 
"Error getting CommunicationsManager for the current case.", ex); 
 
  344             logger.log(Level.SEVERE, 
"Can't get CommunicationsManager when there is no case open.", ex); 
 
  348             graph.getModel().beginUpdate();
 
  352                 graph.getModel().endUpdate();
 
  354             if (evt.getNewValue() == null) {
 
  357                 Case currentCase = (
Case) evt.getNewValue();
 
  360                 } 
catch (TskCoreException ex) {
 
  361                     logger.log(Level.SEVERE, 
"Error getting CommunicationsManager for the current case.", ex); 
 
  372     @SuppressWarnings(
"unchecked")
 
  374     private 
void initComponents() {
 
  376         splitPane = 
new JSplitPane();
 
  377         borderLayoutPanel = 
new JPanel();
 
  378         placeHolderPanel = 
new JPanel();
 
  379         jTextArea1 = 
new JTextArea();
 
  380         toolbar = 
new JPanel();
 
  381         fastOrganicLayoutButton = 
new JButton();
 
  382         zoomOutButton = 
new JButton();
 
  383         zoomInButton = 
new JButton();
 
  384         zoomActualButton = 
new JButton();
 
  385         fitZoomButton = 
new JButton();
 
  386         zoomLabel = 
new JLabel();
 
  387         zoomPercentLabel = 
new JLabel();
 
  388         clearVizButton = 
new JButton();
 
  389         jSeparator2 = 
new JToolBar.Separator();
 
  390         backButton = 
new JButton();
 
  391         forwardButton = 
new JButton();
 
  392         snapshotButton = 
new JButton();
 
  393         jSeparator3 = 
new JToolBar.Separator();
 
  394         jSeparator4 = 
new JToolBar.Separator();
 
  395         notificationsJFXPanel = 
new JFXPanel();
 
  397         setLayout(
new BorderLayout());
 
  399         splitPane.setDividerLocation(800);
 
  400         splitPane.setResizeWeight(0.5);
 
  402         borderLayoutPanel.setLayout(
new BorderLayout());
 
  404         jTextArea1.setBackground(
new Color(240, 240, 240));
 
  405         jTextArea1.setColumns(20);
 
  406         jTextArea1.setLineWrap(
true);
 
  407         jTextArea1.setRows(5);
 
  408         jTextArea1.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.jTextArea1.text")); 
 
  410         GroupLayout placeHolderPanelLayout = 
new GroupLayout(placeHolderPanel);
 
  411         placeHolderPanel.setLayout(placeHolderPanelLayout);
 
  412         placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
 
  413             .add(placeHolderPanelLayout.createSequentialGroup()
 
  414                 .addContainerGap(250, Short.MAX_VALUE)
 
  415                 .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE)
 
  416                 .addContainerGap(423, Short.MAX_VALUE))
 
  418         placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
 
  419             .add(placeHolderPanelLayout.createSequentialGroup()
 
  420                 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  421                 .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 47, GroupLayout.PREFERRED_SIZE)
 
  422                 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
 
  425         borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
 
  427         fastOrganicLayoutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); 
 
  428         fastOrganicLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fastOrganicLayoutButton.text")); 
 
  429         fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fastOrganicLayoutButton.toolTipText")); 
 
  430         fastOrganicLayoutButton.setFocusable(
false);
 
  431         fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  433         zoomOutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); 
 
  434         zoomOutButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomOutButton.text")); 
 
  435         zoomOutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomOutButton.toolTipText")); 
 
  436         zoomOutButton.setFocusable(
false);
 
  437         zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  438         zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  439         zoomOutButton.addActionListener(
new ActionListener() {
 
  440             public void actionPerformed(ActionEvent evt) {
 
  441                 zoomOutButtonActionPerformed(evt);
 
  445         zoomInButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); 
 
  446         zoomInButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomInButton.text")); 
 
  447         zoomInButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomInButton.toolTipText")); 
 
  448         zoomInButton.setFocusable(
false);
 
  449         zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  450         zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  451         zoomInButton.addActionListener(
new ActionListener() {
 
  452             public void actionPerformed(ActionEvent evt) {
 
  453                 zoomInButtonActionPerformed(evt);
 
  457         zoomActualButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); 
 
  458         zoomActualButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomActualButton.text")); 
 
  459         zoomActualButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomActualButton.toolTipText")); 
 
  460         zoomActualButton.setFocusable(
false);
 
  461         zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  462         zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  463         zoomActualButton.addActionListener(
new ActionListener() {
 
  464             public void actionPerformed(ActionEvent evt) {
 
  465                 zoomActualButtonActionPerformed(evt);
 
  469         fitZoomButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); 
 
  470         fitZoomButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fitZoomButton.text")); 
 
  471         fitZoomButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fitZoomButton.toolTipText")); 
 
  472         fitZoomButton.setFocusable(
false);
 
  473         fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  474         fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  475         fitZoomButton.addActionListener(
new ActionListener() {
 
  476             public void actionPerformed(ActionEvent evt) {
 
  477                 fitZoomButtonActionPerformed(evt);
 
  481         zoomLabel.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomLabel.text")); 
 
  483         zoomPercentLabel.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomPercentLabel.text")); 
 
  485         clearVizButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/broom.png"))); 
 
  486         clearVizButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.clearVizButton.text_1")); 
 
  487         clearVizButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.clearVizButton.toolTipText")); 
 
  488         clearVizButton.setActionCommand(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.clearVizButton.actionCommand")); 
 
  489         clearVizButton.addActionListener(
new ActionListener() {
 
  490             public void actionPerformed(ActionEvent evt) {
 
  491                 clearVizButtonActionPerformed(evt);
 
  495         jSeparator2.setOrientation(SwingConstants.VERTICAL);
 
  497         backButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_previous.png"))); 
 
  498         backButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.backButton.text_1")); 
 
  499         backButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.backButton.toolTipText")); 
 
  500         backButton.addActionListener(
new ActionListener() {
 
  501             public void actionPerformed(ActionEvent evt) {
 
  502                 backButtonActionPerformed(evt);
 
  506         forwardButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_next.png"))); 
 
  507         forwardButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.forwardButton.text")); 
 
  508         forwardButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.forwardButton.toolTipText")); 
 
  509         forwardButton.setHorizontalTextPosition(SwingConstants.LEADING);
 
  510         forwardButton.addActionListener(
new ActionListener() {
 
  511             public void actionPerformed(ActionEvent evt) {
 
  512                 forwardButtonActionPerformed(evt);
 
  516         snapshotButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/report/images/image.png"))); 
 
  517         snapshotButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.snapshotButton.text_1")); 
 
  518         snapshotButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.snapshotButton.toolTipText")); 
 
  519         snapshotButton.addActionListener(
new ActionListener() {
 
  520             public void actionPerformed(ActionEvent evt) {
 
  521                 snapshotButtonActionPerformed(evt);
 
  525         jSeparator3.setOrientation(SwingConstants.VERTICAL);
 
  527         jSeparator4.setOrientation(SwingConstants.VERTICAL);
 
  529         GroupLayout toolbarLayout = 
new GroupLayout(toolbar);
 
  530         toolbar.setLayout(toolbarLayout);
 
  531         toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
 
  532             .add(toolbarLayout.createSequentialGroup()
 
  535                 .addPreferredGap(LayoutStyle.RELATED)
 
  537                 .addPreferredGap(LayoutStyle.RELATED)
 
  538                 .add(jSeparator4, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
 
  539                 .addPreferredGap(LayoutStyle.RELATED)
 
  540                 .add(fastOrganicLayoutButton)
 
  541                 .addPreferredGap(LayoutStyle.RELATED)
 
  543                 .addPreferredGap(LayoutStyle.RELATED)
 
  544                 .add(jSeparator2, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
 
  545                 .addPreferredGap(LayoutStyle.RELATED)
 
  547                 .addPreferredGap(LayoutStyle.RELATED)
 
  548                 .add(zoomPercentLabel)
 
  549                 .addPreferredGap(LayoutStyle.RELATED)
 
  550                 .add(zoomOutButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
 
  551                 .addPreferredGap(LayoutStyle.RELATED)
 
  552                 .add(zoomInButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
 
  553                 .addPreferredGap(LayoutStyle.RELATED)
 
  554                 .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE)
 
  555                 .addPreferredGap(LayoutStyle.RELATED)
 
  556                 .add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
 
  557                 .addPreferredGap(LayoutStyle.RELATED)
 
  558                 .add(jSeparator3, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
 
  559                 .addPreferredGap(LayoutStyle.RELATED)
 
  561                 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
 
  563         toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
 
  564             .add(toolbarLayout.createSequentialGroup()
 
  566                 .add(toolbarLayout.createParallelGroup(GroupLayout.CENTER)
 
  567                     .add(fastOrganicLayoutButton)
 
  569                     .add(zoomInButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  570                     .add(zoomActualButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  571                     .add(fitZoomButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  573                     .add(zoomPercentLabel)
 
  575                     .add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  579                     .add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  580                     .add(jSeparator4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
 
  584         borderLayoutPanel.add(toolbar, BorderLayout.PAGE_START);
 
  585         borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
 
  587         splitPane.setLeftComponent(borderLayoutPanel);
 
  589         add(splitPane, BorderLayout.CENTER);
 
  597         graphComponent.zoomActual();
 
  598         CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
  602         graphComponent.zoomIn();
 
  603         CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
  607         graphComponent.zoomOut();
 
  608         CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
  617     @NbBundle.Messages({
"VisualizationPanel.computingLayout=Computing Layout",
 
  618         "# {0} - layout name",
 
  619         "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
 
  620         "# {0} -  layout name",
 
  621         "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
 
  623         currentLayout = layout;
 
  624         layoutButtons.forEach((layoutKey, button)
 
  625                 -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
 
  628         progressIndicator.
start(Bundle.VisualizationPanel_computingLayout());
 
  630         new SwingWorker<Void, Void>() {
 
  632             protected Void doInBackground() {
 
  633                 graph.getModel().beginUpdate();
 
  635                     layout.execute(graph.getDefaultParent());
 
  638                     graph.getModel().endUpdate();
 
  639                     progressIndicator.
finish();
 
  645             protected void done() {
 
  648                 } 
catch (InterruptedException | ExecutionException ex) {
 
  649                     logger.log(Level.WARNING, 
"CVT graph layout failed.", ex);
 
  656         CVTEvents.getCVTEventBus().post(
new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts()));
 
  660         handleStateChange(stateManager.advance());
 
  664         handleStateChange(stateManager.retreat());
 
  673         if(newState == null) {
 
  678         if(newState.isZoomChange()) {
 
  679             graph.getView().setScale(newState.getZoomValue());
 
  684         CVTEvents.getCVTEventBus().post(
new CVTEvents.StateChangeEvent(newState));
 
  685         setStateButtonsEnabled();
 
  687         graph.getModel().beginUpdate();
 
  690         if(newState.getPinnedList() != null) {
 
  691             pinnedAccountModel.pinAccount(newState.getPinnedList());
 
  693             pinnedAccountModel.clear();
 
  696         currentFilter = newState.getCommunicationsFilter();
 
  700         graph.getModel().endUpdate();
 
  707         backButton.setEnabled(stateManager.canRetreat());
 
  708         forwardButton.setEnabled(stateManager.canAdvance());
 
  712          "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation." 
  716             handleSnapshotEvent();
 
  718             logger.log(Level.SEVERE, 
"Unable to create communications snapsot report", ex); 
 
  721                     -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
 
  722                             .text(Bundle.VisualizationPanel_snapshot_report_failure())
 
  724         } 
catch( TskCoreException ex) {
 
  725             logger.log(Level.WARNING, 
"Unable to add report to currenct case", ex); 
 
  730         graphComponent.zoomTo(1, 
true);
 
  731         mxPoint translate = graph.getView().getTranslate();
 
  732         if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
 
  733             translate = 
new mxPoint();
 
  736         mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(), 
true, 
true, 
true);
 
  737         if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
 
  738             boundsForCells = 
new mxRectangle(0, 0, 1, 1);
 
  740         final mxPoint mxPoint = 
new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
 
  742         graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(), 
false, 
false);
 
  744         boundsForCells = graph.getCellBounds(graph.getDefaultParent(), 
true, 
true, 
true);
 
  745         if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
 
  746             boundsForCells = 
new mxRectangle(0, 0, 1, 1);
 
  749         final Dimension size = graphComponent.getSize();
 
  750         final double widthFactor = size.getWidth() / boundsForCells.getWidth();
 
  751         final double heightFactor = size.getHeight() / boundsForCells.getHeight();
 
  753         graphComponent.zoom((heightFactor + widthFactor) / 2.0);
 
  763         "VisualizationPanel_action_dialogs_title=Communications",
 
  764         "VisualizationPanel_module_name=Communications",
 
  765         "VisualizationPanel_action_name_text=Snapshot Report",
 
  766         "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
 
  767         "VisualizationPane_reportName=Communications Snapshot",
 
  768         "# {0} -  default name",
 
  769         "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
 
  770         "VisualizationPane_blank_report_title=Blank Report Name",
 
  771         "# {0} -  report name",
 
  772         "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}" 
  776         Date generationDate = 
new Date();
 
  780         final JTextField text = 
new JTextField(50);
 
  781         final JPanel panel = 
new JPanel(
new GridLayout(2, 1));
 
  782         panel.add(
new JLabel(Bundle.VisualizationPane_fileName_prompt()));
 
  785         text.setText(defaultReportName);
 
  787         int result = JOptionPane.showConfirmDialog(graphComponent, panel,
 
  788                 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
 
  790         if (result == JOptionPane.OK_OPTION) {
 
  791             String enteredReportName = text.getText();
 
  793             if(enteredReportName.trim().isEmpty()){
 
  794                 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
 
  795                 if(result != JOptionPane.OK_OPTION) {
 
  800             String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
 
  802             if (Files.exists(reportPath)) {
 
  803                 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
 
  804                         Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
 
  806                 if (result == JOptionPane.OK_OPTION) {
 
  808                     createReport(currentCase, reportName);
 
  811                 createReport(currentCase, reportName);
 
  812                 currentCase.
addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
 
  827         "VisualizationPane_DisplayName=Open Report",
 
  828         "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
 
  829         "VisualizationPane_MessageBoxTitle=Open Report Failure",
 
  830         "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
 
  831         "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
 
  832         "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
 
  833         "# {0} -  report path",
 
  834         "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
 
  835         "VisualizationPane_Report_OK_Button=OK",
 
  836         "VisualizationPane_Open_Report=Open Report",})
 
  840         Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName, Bundle.VisualizationPane_reportName()); 
 
  841         BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE, 
true, null);
 
  845         String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
 
  846         String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
 
  848         int result = JOptionPane.showOptionDialog(graphComponent, message,
 
  849                 Bundle.VisualizationPanel_action_dialogs_title(),
 
  850                 JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
 
  851                 null, buttons, buttons[1]);
 
  852         if (result == JOptionPane.YES_NO_OPTION) {
 
  854                 Desktop.getDesktop().open(reportPath.toFile());
 
  855             } 
catch (IOException ex) {
 
  856                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  857                         Bundle.VisualizationPane_NoAssociatedEditorMessage(),
 
  858                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  859                         JOptionPane.ERROR_MESSAGE);
 
  860             } 
catch (UnsupportedOperationException ex) {
 
  861                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  862                         Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
 
  863                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  864                         JOptionPane.ERROR_MESSAGE);
 
  865             } 
catch (IllegalArgumentException ex) {
 
  866                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  867                         Bundle.VisualizationPane_MissingReportFileMessage(),
 
  868                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  869                         JOptionPane.ERROR_MESSAGE);
 
  870             } 
catch (SecurityException ex) {
 
  871                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  872                         Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
 
  873                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  874                         JOptionPane.ERROR_MESSAGE);
 
  908         @SuppressWarnings(
"unchecked")
 
  910         public void invoke(Object sender, mxEventObject evt) {
 
  911             Object[] selectionCells = graph.getSelectionCells();
 
  912             if (selectionCells.length > 0) {
 
  913                 mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(
new mxCell[selectionCells.length]);
 
  914                 HashSet<AccountDeviceInstance> selectedNodes = 
new HashSet<>();
 
  916                 for (mxICell cell : selectedCells) {
 
  918                         mxICell source = (mxICell) graph.getModel().getTerminal(cell, 
true);
 
  919                         mxICell target = (mxICell) graph.getModel().getTerminal(cell, 
false);
 
  921                         selectedEdges.add(
new SelectionInfo.
GraphEdge(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance(),
 
  922                             ((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance()));
 
  924                     } 
else if (cell.isVertex()) {
 
  925                         selectedNodes.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
 
  929                 relationshipBrowser.setSelectionInfo(
new SelectionInfo(selectedNodes, selectedEdges, currentFilter));
 
  931                 relationshipBrowser.setSelectionInfo(
new SelectionInfo(
new HashSet<>(), 
new HashSet<>(), currentFilter));
 
  941         String getDisplayName();
 
  955             return super.isVertexIgnored(vertex)
 
  956                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
  961             if (isVertexIgnored(vertex)) {
 
  962                 return getVertexBounds(vertex);
 
  964                 return super.setVertexLocation(vertex, x, y);
 
  970             return "Fast Organic";
 
  986             return super.isVertexIgnored(vertex)
 
  987                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
  992             if (isVertexIgnored(vertex)) {
 
  993                 return getVertexBounds(vertex);
 
  995                 return super.setVertexLocation(vertex, x, y);
 
 1012             setResetEdges(
true);
 
 1017             return super.isVertexIgnored(vertex)
 
 1018                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
 1023             if (isVertexIgnored(vertex)) {
 
 1024                 return getVertexBounds(vertex);
 
 1026                 return super.setVertexLocation(vertex, x, y);
 
 1047             return super.isVertexIgnored(vertex)
 
 1048                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
 1053             if (isVertexIgnored(vertex)) {
 
 1054                 return getVertexBounds(vertex);
 
 1056                 return super.setVertexLocation(vertex, x, y);
 
 1062             return "Hierarchical";
 
 1076             this.cancellable = cancellable;
 
 1077             this.progress = progress;
 
 1083             cancellable.cancel(
true);
 
 1101             super.mouseWheelMoved(event);
 
 1102             if (event.getPreciseWheelRotation() < 0) {
 
 1103                 graphComponent.zoomIn();
 
 1104             } 
else if (event.getPreciseWheelRotation() > 0) {
 
 1105                 graphComponent.zoomOut();
 
 1108             CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
 1118             super.mouseClicked(event);
 
 1119             if (SwingUtilities.isRightMouseButton(event)) {
 
 1120                 final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(), 
event.getY());
 
 1121                 if (cellAt != null && cellAt.isVertex()) {
 
 1122                     final JPopupMenu jPopupMenu = 
new JPopupMenu();
 
 1123                     final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
 
 1125                     Set<mxCell> selectedVertices
 
 1126                             = Stream.of(graph.getSelectionModel().getCells())
 
 1127                                     .map(mxCell.class::cast)
 
 1128                                     .filter(mxCell::isVertex)
 
 1129                                     .collect(Collectors.toSet());
 
 1131                     if (lockedVertexModel.isVertexLocked(cellAt)) {
 
 1132                         jPopupMenu.add(
new JMenuItem(
new UnlockAction(selectedVertices)));
 
 1134                         jPopupMenu.add(
new JMenuItem(
new LockAction(selectedVertices)));
 
 1136                     if (pinnedAccountModel.isAccountPinned(adiKey)) {
 
 1137                         jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
 
 1139                         jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
 
 1140                         jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
 
 1142                     jPopupMenu.show(graphComponent.getGraphControl(), 
event.getX(), 
event.getY());
 
 1151     @NbBundle.Messages({
 
 1152         "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
 
 1153         "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
 
 1159             super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
 
 1161             this.selectedVertices = selectedVertices;
 
 1167             lockedVertexModel.unlock(selectedVertices);
 
 1174     @NbBundle.Messages({
 
 1175         "VisualizationPanel.lockAction.singularText=Lock Selected Account",
 
 1176         "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
 
 1182             super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
 
 1184             this.selectedVertices = selectedVertices;
 
 1189             lockedVertexModel.lock(selectedVertices);
 
void zoomOutButtonActionPerformed(ActionEvent evt)
 
final Set< mxCell > selectedVertices
 
boolean isVertexIgnored(Object vertex)
 
void invoke(Object sender, mxEventObject evt)
 
JFXPanel notificationsJFXPanel
 
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 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 jSeparator4
 
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()
 
final Set< mxCell > selectedVertices
 
void zoomActualButtonActionPerformed(ActionEvent evt)
 
void clearVizButtonActionPerformed(ActionEvent evt)
 
void snapshotButtonActionPerformed(ActionEvent evt)
 
synchronized void finish()