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()