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.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         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);
 
  226         final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
 
  227                 -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty(
"edit"));
 
  229         graph.getModel().addListener(mxEvent.UNDO, undoListener);
 
  230         graph.getView().addListener(mxEvent.UNDO, undoListener);
 
  235         BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
 
  236             layoutButtons.put(layout, layoutButton);
 
  237             layoutButton.addActionListener(event -> applyLayout(layout));
 
  240         configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
 
  242         applyLayout(fastOrganicLayout);
 
  244         stateManager = 
new StateManager(pinnedAccountModel);
 
  246         setStateButtonsEnabled();
 
  252     void handle(LockedVertexModel.VertexLockEvent event) {
 
  253         final Set<mxCell> vertices = 
event.getVertices();
 
  254         mxGraphView view = graph.getView();
 
  255         vertices.forEach(vertex -> {
 
  256             final mxCellState state = view.getState(vertex, 
true);
 
  257             view.updateLabel(state);
 
  258             view.updateLabelBounds(state);
 
  259             view.updateBoundingBox(state);
 
  260             graphComponent.redraw(state);
 
  265     void handle(
final CVTEvents.UnpinAccountsEvent pinEvent) {
 
  266         graph.getModel().beginUpdate();
 
  267         pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
 
  271         graph.getModel().endUpdate();
 
  273         setStateButtonsEnabled();
 
  277     void handle(
final CVTEvents.PinAccountsEvent pinEvent) {
 
  278         graph.getModel().beginUpdate();
 
  279         if (pinEvent.isReplace()) {
 
  282         pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances());
 
  285         graph.getModel().endUpdate();
 
  287         setStateButtonsEnabled();
 
  291     void handle(
final CVTEvents.FilterChangeEvent filterChangeEvent) {
 
  292         graph.getModel().beginUpdate();
 
  294         currentFilter = filterChangeEvent.getNewFilter();
 
  297         graph.getModel().endUpdate();
 
  299         setStateButtonsEnabled();
 
  302     @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
 
  303     private 
void rebuildGraph() {
 
  304         if (pinnedAccountModel.isEmpty()) {
 
  305             borderLayoutPanel.remove(graphComponent);
 
  306             borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
 
  309             borderLayoutPanel.remove(placeHolderPanel);
 
  310             borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
 
  311             if (worker != null) {
 
  317             worker = graph.rebuild(progress, commsManager, currentFilter);
 
  318             cancelationListener.configure(worker, progress);
 
  319             worker.addPropertyChangeListener((
final PropertyChangeEvent evt) -> {
 
  320                 if (worker.isDone()) {
 
  321                     if (worker.isCancelled()) {
 
  325                     applyLayout(currentLayout);
 
  336         windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, 
this);
 
  340         } 
catch (TskCoreException ex) {
 
  341             logger.log(Level.SEVERE, 
"Error getting CommunicationsManager for the current case.", ex); 
 
  343             logger.log(Level.SEVERE, 
"Can't get CommunicationsManager when there is no case open.", ex); 
 
  347             graph.getModel().beginUpdate();
 
  351                 graph.getModel().endUpdate();
 
  353             if (evt.getNewValue() == null) {
 
  356                 Case currentCase = (
Case) evt.getNewValue();
 
  359                 } 
catch (TskCoreException ex) {
 
  360                     logger.log(Level.SEVERE, 
"Error getting CommunicationsManager for the current case.", ex); 
 
  371     @SuppressWarnings(
"unchecked")
 
  373     private 
void initComponents() {
 
  374         GridBagConstraints gridBagConstraints;
 
  376         borderLayoutPanel = 
new JPanel();
 
  377         placeHolderPanel = 
new JPanel();
 
  378         jTextPane1 = 
new JTextPane();
 
  379         notificationsJFXPanel = 
new JFXPanel();
 
  380         toolbar = 
new JToolBar();
 
  381         backButton = 
new JButton();
 
  382         forwardButton = 
new JButton();
 
  383         jSeparator3 = 
new JToolBar.Separator();
 
  384         clearVizButton = 
new JButton();
 
  385         fastOrganicLayoutButton = 
new JButton();
 
  386         jSeparator2 = 
new JToolBar.Separator();
 
  387         zoomLabel = 
new JLabel();
 
  388         zoomPercentLabel = 
new JLabel();
 
  389         zoomOutButton = 
new JButton();
 
  390         fitZoomButton = 
new JButton();
 
  391         zoomActualButton = 
new JButton();
 
  392         zoomInButton = 
new JButton();
 
  393         jSeparator1 = 
new JToolBar.Separator();
 
  394         snapshotButton = 
new JButton();
 
  396         setLayout(
new BorderLayout());
 
  398         borderLayoutPanel.setLayout(
new BorderLayout());
 
  400         placeHolderPanel.setLayout(
new GridBagLayout());
 
  402         jTextPane1.setEditable(
false);
 
  403         jTextPane1.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.jTextPane1.text")); 
 
  404         jTextPane1.setOpaque(
false);
 
  405         gridBagConstraints = 
new GridBagConstraints();
 
  406         gridBagConstraints.anchor = GridBagConstraints.NORTH;
 
  407         gridBagConstraints.weighty = 1.0;
 
  408         gridBagConstraints.insets = 
new Insets(50, 0, 0, 0);
 
  409         placeHolderPanel.add(jTextPane1, gridBagConstraints);
 
  411         borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
 
  412         borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
 
  414         add(borderLayoutPanel, BorderLayout.CENTER);
 
  416         toolbar.setRollover(
true);
 
  418         backButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_previous.png"))); 
 
  419         backButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.backButton.text_1")); 
 
  420         backButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.backButton.toolTipText")); 
 
  421         backButton.setFocusable(
false);
 
  422         backButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  423         backButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  424         backButton.addActionListener(
new ActionListener() {
 
  425             public void actionPerformed(ActionEvent evt) {
 
  426                 backButtonActionPerformed(evt);
 
  429         toolbar.add(backButton);
 
  431         forwardButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_next.png"))); 
 
  432         forwardButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.forwardButton.text")); 
 
  433         forwardButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.forwardButton.toolTipText")); 
 
  434         forwardButton.setFocusable(
false);
 
  435         forwardButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  436         forwardButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  437         forwardButton.addActionListener(
new ActionListener() {
 
  438             public void actionPerformed(ActionEvent evt) {
 
  439                 forwardButtonActionPerformed(evt);
 
  442         toolbar.add(forwardButton);
 
  443         toolbar.add(jSeparator3);
 
  445         clearVizButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/broom.png"))); 
 
  446         clearVizButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.clearVizButton.text_1")); 
 
  447         clearVizButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.clearVizButton.toolTipText")); 
 
  448         clearVizButton.setActionCommand(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.clearVizButton.actionCommand")); 
 
  449         clearVizButton.setFocusable(
false);
 
  450         clearVizButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  451         clearVizButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  452         clearVizButton.addActionListener(
new ActionListener() {
 
  453             public void actionPerformed(ActionEvent evt) {
 
  454                 clearVizButtonActionPerformed(evt);
 
  457         toolbar.add(clearVizButton);
 
  459         fastOrganicLayoutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); 
 
  460         fastOrganicLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fastOrganicLayoutButton.text")); 
 
  461         fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fastOrganicLayoutButton.toolTipText")); 
 
  462         fastOrganicLayoutButton.setFocusable(
false);
 
  463         fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  464         fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  465         fastOrganicLayoutButton.addActionListener(
new ActionListener() {
 
  466             public void actionPerformed(ActionEvent evt) {
 
  467                 fastOrganicLayoutButtonActionPerformed(evt);
 
  470         toolbar.add(fastOrganicLayoutButton);
 
  471         toolbar.add(jSeparator2);
 
  473         zoomLabel.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomLabel.text")); 
 
  474         toolbar.add(zoomLabel);
 
  476         zoomPercentLabel.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomPercentLabel.text")); 
 
  477         toolbar.add(zoomPercentLabel);
 
  479         zoomOutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); 
 
  480         zoomOutButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomOutButton.text")); 
 
  481         zoomOutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomOutButton.toolTipText")); 
 
  482         zoomOutButton.setFocusable(
false);
 
  483         zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  484         zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  485         zoomOutButton.addActionListener(
new ActionListener() {
 
  486             public void actionPerformed(ActionEvent evt) {
 
  487                 zoomOutButtonActionPerformed(evt);
 
  490         toolbar.add(zoomOutButton);
 
  492         fitZoomButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); 
 
  493         fitZoomButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fitZoomButton.text")); 
 
  494         fitZoomButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.fitZoomButton.toolTipText")); 
 
  495         fitZoomButton.setFocusable(
false);
 
  496         fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  497         fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  498         fitZoomButton.addActionListener(
new ActionListener() {
 
  499             public void actionPerformed(ActionEvent evt) {
 
  500                 fitZoomButtonActionPerformed(evt);
 
  503         toolbar.add(fitZoomButton);
 
  505         zoomActualButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); 
 
  506         zoomActualButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomActualButton.text")); 
 
  507         zoomActualButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomActualButton.toolTipText")); 
 
  508         zoomActualButton.setFocusable(
false);
 
  509         zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  510         zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  511         zoomActualButton.addActionListener(
new ActionListener() {
 
  512             public void actionPerformed(ActionEvent evt) {
 
  513                 zoomActualButtonActionPerformed(evt);
 
  516         toolbar.add(zoomActualButton);
 
  518         zoomInButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); 
 
  519         zoomInButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomInButton.text")); 
 
  520         zoomInButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.zoomInButton.toolTipText")); 
 
  521         zoomInButton.setFocusable(
false);
 
  522         zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  523         zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  524         zoomInButton.addActionListener(
new ActionListener() {
 
  525             public void actionPerformed(ActionEvent evt) {
 
  526                 zoomInButtonActionPerformed(evt);
 
  529         toolbar.add(zoomInButton);
 
  530         toolbar.add(jSeparator1);
 
  532         snapshotButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/report/images/image.png"))); 
 
  533         snapshotButton.setText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.snapshotButton.text_1")); 
 
  534         snapshotButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class, 
"VisualizationPanel.snapshotButton.toolTipText")); 
 
  535         snapshotButton.setFocusable(
false);
 
  536         snapshotButton.setHorizontalTextPosition(SwingConstants.CENTER);
 
  537         snapshotButton.setVerticalTextPosition(SwingConstants.BOTTOM);
 
  538         snapshotButton.addActionListener(
new ActionListener() {
 
  539             public void actionPerformed(ActionEvent evt) {
 
  540                 snapshotButtonActionPerformed(evt);
 
  543         toolbar.add(snapshotButton);
 
  545         add(toolbar, BorderLayout.NORTH);
 
  553         graphComponent.zoomActual();
 
  554         CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
  558         graphComponent.zoomIn();
 
  559         CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
  563         graphComponent.zoomOut();
 
  564         CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
  573     @NbBundle.Messages({
"VisualizationPanel.computingLayout=Computing Layout",
 
  574         "# {0} - layout name",
 
  575         "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
 
  576         "# {0} -  layout name",
 
  577         "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
 
  579         currentLayout = layout;
 
  580         layoutButtons.forEach((layoutKey, button)
 
  581                 -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
 
  584         progressIndicator.
start(Bundle.VisualizationPanel_computingLayout());
 
  585         graph.getModel().beginUpdate();
 
  587             layout.execute(graph.getDefaultParent());
 
  590             graph.getModel().endUpdate();
 
  591             progressIndicator.
finish();
 
  596         CVTEvents.getCVTEventBus().post(
new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts()));
 
  600         handleStateChange(stateManager.advance());
 
  604         handleStateChange(stateManager.retreat());
 
  613         if(newState == null) {
 
  618         if(newState.isZoomChange()) {
 
  619             graph.getView().setScale(newState.getZoomValue());
 
  624         CVTEvents.getCVTEventBus().post(
new CVTEvents.StateChangeEvent(newState));
 
  625         setStateButtonsEnabled();
 
  627         graph.getModel().beginUpdate();
 
  630         if(newState.getPinnedList() != null) {
 
  631             pinnedAccountModel.pinAccount(newState.getPinnedList());
 
  633             pinnedAccountModel.clear();
 
  636         currentFilter = newState.getCommunicationsFilter();
 
  640         graph.getModel().endUpdate();
 
  647         backButton.setEnabled(stateManager.canRetreat());
 
  648         forwardButton.setEnabled(stateManager.canAdvance());
 
  652          "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation." 
  656             handleSnapshotEvent();
 
  658             logger.log(Level.SEVERE, 
"Unable to create communications snapsot report", ex); 
 
  661                     -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
 
  662                             .text(Bundle.VisualizationPanel_snapshot_report_failure())
 
  664         } 
catch( TskCoreException ex) {
 
  665             logger.log(Level.WARNING, 
"Unable to add report to currenct case", ex); 
 
  674         graphComponent.zoomTo(1, 
true);
 
  675         mxPoint translate = graph.getView().getTranslate();
 
  676         if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
 
  677             translate = 
new mxPoint();
 
  680         mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(), 
true, 
true, 
true);
 
  681         if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
 
  682             boundsForCells = 
new mxRectangle(0, 0, 1, 1);
 
  684         final mxPoint mxPoint = 
new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
 
  686         graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(), 
false, 
false);
 
  688         boundsForCells = graph.getCellBounds(graph.getDefaultParent(), 
true, 
true, 
true);
 
  689         if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
 
  690             boundsForCells = 
new mxRectangle(0, 0, 1, 1);
 
  693         final Dimension size = graphComponent.getSize();
 
  694         final double widthFactor = size.getWidth() / boundsForCells.getWidth();
 
  695         final double heightFactor = size.getHeight() / boundsForCells.getHeight();
 
  697         graphComponent.zoom((heightFactor + widthFactor) / 2.0);
 
  707         "VisualizationPanel_action_dialogs_title=Communications",
 
  708         "VisualizationPanel_module_name=Communications",
 
  709         "VisualizationPanel_action_name_text=Snapshot Report",
 
  710         "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
 
  711         "VisualizationPane_reportName=Communications Snapshot",
 
  712         "# {0} -  default name",
 
  713         "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
 
  714         "VisualizationPane_blank_report_title=Blank Report Name",
 
  715         "# {0} -  report name",
 
  716         "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}" 
  720         Date generationDate = 
new Date();
 
  724         final JTextField text = 
new JTextField(50);
 
  725         final JPanel panel = 
new JPanel(
new GridLayout(2, 1));
 
  726         panel.add(
new JLabel(Bundle.VisualizationPane_fileName_prompt()));
 
  729         text.setText(defaultReportName);
 
  731         int result = JOptionPane.showConfirmDialog(graphComponent, panel,
 
  732                 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
 
  734         if (result == JOptionPane.OK_OPTION) {
 
  735             String enteredReportName = text.getText();
 
  737             if(enteredReportName.trim().isEmpty()){
 
  738                 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
 
  739                 if(result != JOptionPane.OK_OPTION) {
 
  744             String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
 
  746             if (Files.exists(reportPath)) {
 
  747                 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
 
  748                         Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
 
  750                 if (result == JOptionPane.OK_OPTION) {
 
  752                     createReport(currentCase, reportName);
 
  755                 createReport(currentCase, reportName);
 
  756                 currentCase.
addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
 
  771         "VisualizationPane_DisplayName=Open Report",
 
  772         "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
 
  773         "VisualizationPane_MessageBoxTitle=Open Report Failure",
 
  774         "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
 
  775         "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
 
  776         "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
 
  777         "# {0} -  report path",
 
  778         "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
 
  779         "VisualizationPane_Report_OK_Button=OK",
 
  780         "VisualizationPane_Open_Report=Open Report",})
 
  784         Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName, Bundle.VisualizationPane_reportName()); 
 
  785         BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE, 
true, null);
 
  789         String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
 
  790         String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
 
  792         int result = JOptionPane.showOptionDialog(graphComponent, message,
 
  793                 Bundle.VisualizationPanel_action_dialogs_title(),
 
  794                 JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
 
  795                 null, buttons, buttons[1]);
 
  796         if (result == JOptionPane.YES_NO_OPTION) {
 
  798                 Desktop.getDesktop().open(reportPath.toFile());
 
  799             } 
catch (IOException ex) {
 
  800                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  801                         Bundle.VisualizationPane_NoAssociatedEditorMessage(),
 
  802                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  803                         JOptionPane.ERROR_MESSAGE);
 
  804             } 
catch (UnsupportedOperationException ex) {
 
  805                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  806                         Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
 
  807                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  808                         JOptionPane.ERROR_MESSAGE);
 
  809             } 
catch (IllegalArgumentException ex) {
 
  810                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  811                         Bundle.VisualizationPane_MissingReportFileMessage(),
 
  812                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  813                         JOptionPane.ERROR_MESSAGE);
 
  814             } 
catch (SecurityException ex) {
 
  815                 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  816                         Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
 
  817                         Bundle.VisualizationPane_MessageBoxTitle(),
 
  818                         JOptionPane.ERROR_MESSAGE);
 
  851         @SuppressWarnings(
"unchecked")
 
  853         public void invoke(Object sender, mxEventObject evt) {
 
  854             Object[] selectionCells = graph.getSelectionCells();
 
  855             if (selectionCells.length > 0) {
 
  856                 mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(
new mxCell[selectionCells.length]);
 
  857                 HashSet<AccountDeviceInstance> selectedNodes = 
new HashSet<>();
 
  859                 for (mxICell cell : selectedCells) {
 
  861                         mxICell source = (mxICell) graph.getModel().getTerminal(cell, 
true);
 
  862                         mxICell target = (mxICell) graph.getModel().getTerminal(cell, 
false);
 
  864                         selectedEdges.add(
new SelectionInfo.
GraphEdge(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance(),
 
  865                             ((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance()));
 
  867                     } 
else if (cell.isVertex()) {
 
  868                         selectedNodes.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
 
  872                 relationshipBrowser.setSelectionInfo(
new SelectionInfo(selectedNodes, selectedEdges, currentFilter));
 
  874                 relationshipBrowser.setSelectionInfo(
new SelectionInfo(
new HashSet<>(), 
new HashSet<>(), currentFilter));
 
  884         String getDisplayName();
 
  898             return super.isVertexIgnored(vertex)
 
  899                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
  904             if (isVertexIgnored(vertex)) {
 
  905                 return getVertexBounds(vertex);
 
  907                 return super.setVertexLocation(vertex, x, y);
 
  913             return "Fast Organic";
 
  929             return super.isVertexIgnored(vertex)
 
  930                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
  935             if (isVertexIgnored(vertex)) {
 
  936                 return getVertexBounds(vertex);
 
  938                 return super.setVertexLocation(vertex, x, y);
 
  960             return super.isVertexIgnored(vertex)
 
  961                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
  966             if (isVertexIgnored(vertex)) {
 
  967                 return getVertexBounds(vertex);
 
  969                 return super.setVertexLocation(vertex, x, y);
 
  990             return super.isVertexIgnored(vertex)
 
  991                    || lockedVertexModel.isVertexLocked((mxCell) vertex);
 
  996             if (isVertexIgnored(vertex)) {
 
  997                 return getVertexBounds(vertex);
 
  999                 return super.setVertexLocation(vertex, x, y);
 
 1005             return "Hierarchical";
 
 1019             this.cancellable = cancellable;
 
 1020             this.progress = progress;
 
 1026             cancellable.cancel(
true);
 
 1044             super.mouseWheelMoved(event);
 
 1045             if (event.getPreciseWheelRotation() < 0) {
 
 1046                 graphComponent.zoomIn();
 
 1047             } 
else if (event.getPreciseWheelRotation() > 0) {
 
 1048                 graphComponent.zoomOut();
 
 1051             CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
 
 1061             super.mouseClicked(event);
 
 1062             if (SwingUtilities.isRightMouseButton(event)) {
 
 1063                 final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(), 
event.getY());
 
 1064                 if (cellAt != null && cellAt.isVertex()) {
 
 1065                     final JPopupMenu jPopupMenu = 
new JPopupMenu();
 
 1066                     final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
 
 1068                     Set<mxCell> selectedVertices
 
 1069                             = Stream.of(graph.getSelectionModel().getCells())
 
 1070                                     .map(mxCell.class::cast)
 
 1071                                     .filter(mxCell::isVertex)
 
 1072                                     .collect(Collectors.toSet());
 
 1074                     if (lockedVertexModel.isVertexLocked(cellAt)) {
 
 1075                         jPopupMenu.add(
new JMenuItem(
new UnlockAction(selectedVertices)));
 
 1077                         jPopupMenu.add(
new JMenuItem(
new LockAction(selectedVertices)));
 
 1079                     if (pinnedAccountModel.isAccountPinned(adiKey.getAccountDeviceInstance())) {
 
 1080                         jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
 
 1082                         jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
 
 1083                         jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
 
 1085                     jPopupMenu.show(graphComponent.getGraphControl(), 
event.getX(), 
event.getY());
 
 1094     @NbBundle.Messages({
 
 1095         "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
 
 1096         "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
 
 1102             super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
 
 1104             this.selectedVertices = selectedVertices;
 
 1110             lockedVertexModel.unlock(selectedVertices);
 
 1117     @NbBundle.Messages({
 
 1118         "VisualizationPanel.lockAction.singularText=Lock Selected Account",
 
 1119         "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
 
 1125             super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
 
 1127             this.selectedVertices = selectedVertices;
 
 1132             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 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()
final Set< mxCell > selectedVertices
void zoomActualButtonActionPerformed(ActionEvent evt)
void clearVizButtonActionPerformed(ActionEvent evt)
void snapshotButtonActionPerformed(ActionEvent evt)
synchronized void finish()