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.ExecutionException;
74 import java.util.concurrent.Future;
75 import java.util.function.BiConsumer;
76 import java.util.logging.Level;
77 import java.util.stream.Collectors;
78 import java.util.stream.Stream;
79 import javafx.application.Platform;
80 import javafx.embed.swing.JFXPanel;
81 import javafx.scene.Scene;
82 import javafx.scene.layout.Pane;
83 import javax.swing.AbstractAction;
84 import javax.swing.ImageIcon;
85 import javax.swing.JButton;
86 import javax.swing.JLabel;
87 import javax.swing.JMenuItem;
88 import javax.swing.JOptionPane;
89 import javax.swing.JPanel;
90 import javax.swing.JPopupMenu;
91 import javax.swing.JTextField;
92 import javax.swing.JTextPane;
93 import javax.swing.JToolBar;
94 import javax.swing.SwingConstants;
95 import javax.swing.SwingUtilities;
96 import javax.swing.SwingWorker;
97 import org.apache.commons.lang3.StringUtils;
98 import org.controlsfx.control.Notifications;
99 import org.openide.util.NbBundle;
100 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.")
164 this.relationshipBrowser = relationshipBrowser;
167 notificationsJFXPanel.setScene(
new Scene(
new Pane()));
169 graph =
new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
177 graphComponent =
new mxGraphComponent(graph) {
179 protected mxGraphComponent.mxGraphControl createGraphControl() {
181 return new mxGraphControl() {
184 public void paint(Graphics graphics) {
186 super.paint(graphics);
187 }
catch (NullPointerException ex) {
193 logger.log(Level.WARNING,
"There was a NPE while painting the VisualizationPanel", ex);
200 graphComponent.setAutoExtend(
true);
201 graphComponent.setAutoScroll(
true);
202 graphComponent.setAutoscrolls(
true);
203 graphComponent.setConnectable(
false);
204 graphComponent.setDragEnabled(
false);
205 graphComponent.setKeepSelectionVisibleOnZoom(
true);
206 graphComponent.setOpaque(
true);
207 graphComponent.setToolTips(
true);
208 graphComponent.setBackground(Color.WHITE);
209 borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
212 rubberband =
new mxRubberband(graphComponent);
214 lockedVertexModel.registerhandler(
this);
216 final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
217 -> zoomPercentLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
218 graph.getView().addListener(mxEvent.SCALE, scaleListener);
219 graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
222 graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
223 graphComponent.getGraphControl().addMouseListener(graphMouseListener);
227 final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
228 -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty(
"edit"));
230 graph.getModel().addListener(mxEvent.UNDO, undoListener);
231 graph.getView().addListener(mxEvent.UNDO, undoListener);
236 BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
237 layoutButtons.put(layout, layoutButton);
238 layoutButton.addActionListener(event -> applyLayout(layout));
241 configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
243 applyLayout(fastOrganicLayout);
245 stateManager =
new StateManager(pinnedAccountModel);
247 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() {
375 GridBagConstraints gridBagConstraints;
377 borderLayoutPanel =
new JPanel();
378 placeHolderPanel =
new JPanel();
379 jTextPane1 =
new JTextPane();
380 notificationsJFXPanel =
new JFXPanel();
381 toolbar =
new JToolBar();
382 backButton =
new JButton();
383 forwardButton =
new JButton();
384 jSeparator3 =
new JToolBar.Separator();
385 clearVizButton =
new JButton();
386 fastOrganicLayoutButton =
new JButton();
387 jSeparator2 =
new JToolBar.Separator();
388 zoomLabel =
new JLabel();
389 zoomPercentLabel =
new JLabel();
390 zoomOutButton =
new JButton();
391 fitZoomButton =
new JButton();
392 zoomActualButton =
new JButton();
393 zoomInButton =
new JButton();
394 jSeparator1 =
new JToolBar.Separator();
395 snapshotButton =
new JButton();
397 setLayout(
new BorderLayout());
399 borderLayoutPanel.setLayout(
new BorderLayout());
401 placeHolderPanel.setLayout(
new GridBagLayout());
403 jTextPane1.setEditable(
false);
404 jTextPane1.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.jTextPane1.text"));
405 jTextPane1.setOpaque(
false);
406 gridBagConstraints =
new GridBagConstraints();
407 gridBagConstraints.anchor = GridBagConstraints.NORTH;
408 gridBagConstraints.weighty = 1.0;
409 gridBagConstraints.insets =
new Insets(50, 0, 0, 0);
410 placeHolderPanel.add(jTextPane1, gridBagConstraints);
412 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
413 borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
415 add(borderLayoutPanel, BorderLayout.CENTER);
417 toolbar.setRollover(
true);
419 backButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_previous.png")));
420 backButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.backButton.text_1"));
421 backButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.backButton.toolTipText"));
422 backButton.setFocusable(
false);
423 backButton.setHorizontalTextPosition(SwingConstants.CENTER);
424 backButton.setVerticalTextPosition(SwingConstants.BOTTOM);
425 backButton.addActionListener(
new ActionListener() {
426 public void actionPerformed(ActionEvent evt) {
427 backButtonActionPerformed(evt);
430 toolbar.add(backButton);
432 forwardButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_next.png")));
433 forwardButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.forwardButton.text"));
434 forwardButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.forwardButton.toolTipText"));
435 forwardButton.setFocusable(
false);
436 forwardButton.setHorizontalTextPosition(SwingConstants.CENTER);
437 forwardButton.setVerticalTextPosition(SwingConstants.BOTTOM);
438 forwardButton.addActionListener(
new ActionListener() {
439 public void actionPerformed(ActionEvent evt) {
440 forwardButtonActionPerformed(evt);
443 toolbar.add(forwardButton);
444 toolbar.add(jSeparator3);
446 clearVizButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/broom.png")));
447 clearVizButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.text_1"));
448 clearVizButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.toolTipText"));
449 clearVizButton.setActionCommand(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.actionCommand"));
450 clearVizButton.setFocusable(
false);
451 clearVizButton.setHorizontalTextPosition(SwingConstants.CENTER);
452 clearVizButton.setVerticalTextPosition(SwingConstants.BOTTOM);
453 clearVizButton.addActionListener(
new ActionListener() {
454 public void actionPerformed(ActionEvent evt) {
455 clearVizButtonActionPerformed(evt);
458 toolbar.add(clearVizButton);
460 fastOrganicLayoutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png")));
461 fastOrganicLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.text"));
462 fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.toolTipText"));
463 fastOrganicLayoutButton.setFocusable(
false);
464 fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
465 fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
466 fastOrganicLayoutButton.addActionListener(
new ActionListener() {
467 public void actionPerformed(ActionEvent evt) {
468 fastOrganicLayoutButtonActionPerformed(evt);
471 toolbar.add(fastOrganicLayoutButton);
472 toolbar.add(jSeparator2);
474 zoomLabel.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomLabel.text"));
475 toolbar.add(zoomLabel);
477 zoomPercentLabel.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomPercentLabel.text"));
478 toolbar.add(zoomPercentLabel);
480 zoomOutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png")));
481 zoomOutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.text"));
482 zoomOutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.toolTipText"));
483 zoomOutButton.setFocusable(
false);
484 zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
485 zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
486 zoomOutButton.addActionListener(
new ActionListener() {
487 public void actionPerformed(ActionEvent evt) {
488 zoomOutButtonActionPerformed(evt);
491 toolbar.add(zoomOutButton);
493 fitZoomButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png")));
494 fitZoomButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.text"));
495 fitZoomButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.toolTipText"));
496 fitZoomButton.setFocusable(
false);
497 fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
498 fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
499 fitZoomButton.addActionListener(
new ActionListener() {
500 public void actionPerformed(ActionEvent evt) {
501 fitZoomButtonActionPerformed(evt);
504 toolbar.add(fitZoomButton);
506 zoomActualButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png")));
507 zoomActualButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.text"));
508 zoomActualButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.toolTipText"));
509 zoomActualButton.setFocusable(
false);
510 zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
511 zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
512 zoomActualButton.addActionListener(
new ActionListener() {
513 public void actionPerformed(ActionEvent evt) {
514 zoomActualButtonActionPerformed(evt);
517 toolbar.add(zoomActualButton);
519 zoomInButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png")));
520 zoomInButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.text"));
521 zoomInButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.toolTipText"));
522 zoomInButton.setFocusable(
false);
523 zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
524 zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
525 zoomInButton.addActionListener(
new ActionListener() {
526 public void actionPerformed(ActionEvent evt) {
527 zoomInButtonActionPerformed(evt);
530 toolbar.add(zoomInButton);
531 toolbar.add(jSeparator1);
533 snapshotButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/report/images/image.png")));
534 snapshotButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.snapshotButton.text_1"));
535 snapshotButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.snapshotButton.toolTipText"));
536 snapshotButton.setFocusable(
false);
537 snapshotButton.setHorizontalTextPosition(SwingConstants.CENTER);
538 snapshotButton.setVerticalTextPosition(SwingConstants.BOTTOM);
539 snapshotButton.addActionListener(
new ActionListener() {
540 public void actionPerformed(ActionEvent evt) {
541 snapshotButtonActionPerformed(evt);
544 toolbar.add(snapshotButton);
546 add(toolbar, BorderLayout.NORTH);
554 graphComponent.zoomActual();
555 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
559 graphComponent.zoomIn();
560 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
564 graphComponent.zoomOut();
565 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
574 @NbBundle.Messages({
"VisualizationPanel.computingLayout=Computing Layout",
575 "# {0} - layout name",
576 "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
577 "# {0} - layout name",
578 "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
580 currentLayout = layout;
581 layoutButtons.forEach((layoutKey, button)
582 -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
585 progressIndicator.
start(Bundle.VisualizationPanel_computingLayout());
587 new SwingWorker<Void, Void>() {
589 protected Void doInBackground() {
590 graph.getModel().beginUpdate();
592 layout.execute(graph.getDefaultParent());
595 graph.getModel().endUpdate();
596 progressIndicator.
finish();
602 protected void done() {
605 }
catch (InterruptedException | ExecutionException ex) {
606 logger.log(Level.WARNING,
"CVT graph layout failed.", ex);
613 CVTEvents.getCVTEventBus().post(
new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts()));
617 handleStateChange(stateManager.advance());
621 handleStateChange(stateManager.retreat());
630 if(newState == null) {
635 if(newState.isZoomChange()) {
636 graph.getView().setScale(newState.getZoomValue());
641 CVTEvents.getCVTEventBus().post(
new CVTEvents.StateChangeEvent(newState));
642 setStateButtonsEnabled();
644 graph.getModel().beginUpdate();
647 if(newState.getPinnedList() != null) {
648 pinnedAccountModel.pinAccount(newState.getPinnedList());
650 pinnedAccountModel.clear();
653 currentFilter = newState.getCommunicationsFilter();
657 graph.getModel().endUpdate();
664 backButton.setEnabled(stateManager.canRetreat());
665 forwardButton.setEnabled(stateManager.canAdvance());
669 "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation."
673 handleSnapshotEvent();
675 logger.log(Level.SEVERE,
"Unable to create communications snapsot report", ex);
678 -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
679 .text(Bundle.VisualizationPanel_snapshot_report_failure())
681 }
catch( TskCoreException ex) {
682 logger.log(Level.WARNING,
"Unable to add report to currenct case", ex);
691 graphComponent.zoomTo(1,
true);
692 mxPoint translate = graph.getView().getTranslate();
693 if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
694 translate =
new mxPoint();
697 mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
698 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
699 boundsForCells =
new mxRectangle(0, 0, 1, 1);
701 final mxPoint mxPoint =
new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
703 graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(),
false,
false);
705 boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
706 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
707 boundsForCells =
new mxRectangle(0, 0, 1, 1);
710 final Dimension size = graphComponent.getSize();
711 final double widthFactor = size.getWidth() / boundsForCells.getWidth();
712 final double heightFactor = size.getHeight() / boundsForCells.getHeight();
714 graphComponent.zoom((heightFactor + widthFactor) / 2.0);
724 "VisualizationPanel_action_dialogs_title=Communications",
725 "VisualizationPanel_module_name=Communications",
726 "VisualizationPanel_action_name_text=Snapshot Report",
727 "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
728 "VisualizationPane_reportName=Communications Snapshot",
729 "# {0} - default name",
730 "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
731 "VisualizationPane_blank_report_title=Blank Report Name",
732 "# {0} - report name",
733 "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}"
737 Date generationDate =
new Date();
741 final JTextField text =
new JTextField(50);
742 final JPanel panel =
new JPanel(
new GridLayout(2, 1));
743 panel.add(
new JLabel(Bundle.VisualizationPane_fileName_prompt()));
746 text.setText(defaultReportName);
748 int result = JOptionPane.showConfirmDialog(graphComponent, panel,
749 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
751 if (result == JOptionPane.OK_OPTION) {
752 String enteredReportName = text.getText();
754 if(enteredReportName.trim().isEmpty()){
755 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
756 if(result != JOptionPane.OK_OPTION) {
761 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
763 if (Files.exists(reportPath)) {
764 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
765 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
767 if (result == JOptionPane.OK_OPTION) {
769 createReport(currentCase, reportName);
772 createReport(currentCase, reportName);
773 currentCase.
addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
788 "VisualizationPane_DisplayName=Open Report",
789 "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
790 "VisualizationPane_MessageBoxTitle=Open Report Failure",
791 "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
792 "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
793 "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
794 "# {0} - report path",
795 "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
796 "VisualizationPane_Report_OK_Button=OK",
797 "VisualizationPane_Open_Report=Open Report",})
801 Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName, Bundle.VisualizationPane_reportName());
802 BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE,
true, null);
806 String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
807 String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
809 int result = JOptionPane.showOptionDialog(graphComponent, message,
810 Bundle.VisualizationPanel_action_dialogs_title(),
811 JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
812 null, buttons, buttons[1]);
813 if (result == JOptionPane.YES_NO_OPTION) {
815 Desktop.getDesktop().open(reportPath.toFile());
816 }
catch (IOException ex) {
817 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
818 Bundle.VisualizationPane_NoAssociatedEditorMessage(),
819 Bundle.VisualizationPane_MessageBoxTitle(),
820 JOptionPane.ERROR_MESSAGE);
821 }
catch (UnsupportedOperationException ex) {
822 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
823 Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
824 Bundle.VisualizationPane_MessageBoxTitle(),
825 JOptionPane.ERROR_MESSAGE);
826 }
catch (IllegalArgumentException ex) {
827 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
828 Bundle.VisualizationPane_MissingReportFileMessage(),
829 Bundle.VisualizationPane_MessageBoxTitle(),
830 JOptionPane.ERROR_MESSAGE);
831 }
catch (SecurityException ex) {
832 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
833 Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
834 Bundle.VisualizationPane_MessageBoxTitle(),
835 JOptionPane.ERROR_MESSAGE);
868 @SuppressWarnings(
"unchecked")
870 public void invoke(Object sender, mxEventObject evt) {
871 Object[] selectionCells = graph.getSelectionCells();
872 if (selectionCells.length > 0) {
873 mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(
new mxCell[selectionCells.length]);
874 HashSet<AccountDeviceInstance> selectedNodes =
new HashSet<>();
876 for (mxICell cell : selectedCells) {
878 mxICell source = (mxICell) graph.getModel().getTerminal(cell,
true);
879 mxICell target = (mxICell) graph.getModel().getTerminal(cell,
false);
881 selectedEdges.add(
new SelectionInfo.
GraphEdge(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance(),
882 ((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance()));
884 }
else if (cell.isVertex()) {
885 selectedNodes.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
889 relationshipBrowser.setSelectionInfo(
new SelectionInfo(selectedNodes, selectedEdges, currentFilter));
891 relationshipBrowser.setSelectionInfo(
new SelectionInfo(
new HashSet<>(),
new HashSet<>(), currentFilter));
901 String getDisplayName();
915 return super.isVertexIgnored(vertex)
916 || lockedVertexModel.isVertexLocked((mxCell) vertex);
921 if (isVertexIgnored(vertex)) {
922 return getVertexBounds(vertex);
924 return super.setVertexLocation(vertex, x, y);
930 return "Fast Organic";
946 return super.isVertexIgnored(vertex)
947 || lockedVertexModel.isVertexLocked((mxCell) vertex);
952 if (isVertexIgnored(vertex)) {
953 return getVertexBounds(vertex);
955 return super.setVertexLocation(vertex, x, y);
977 return super.isVertexIgnored(vertex)
978 || lockedVertexModel.isVertexLocked((mxCell) vertex);
983 if (isVertexIgnored(vertex)) {
984 return getVertexBounds(vertex);
986 return super.setVertexLocation(vertex, x, y);
1007 return super.isVertexIgnored(vertex)
1008 || lockedVertexModel.isVertexLocked((mxCell) vertex);
1013 if (isVertexIgnored(vertex)) {
1014 return getVertexBounds(vertex);
1016 return super.setVertexLocation(vertex, x, y);
1022 return "Hierarchical";
1036 this.cancellable = cancellable;
1037 this.progress = progress;
1043 cancellable.cancel(
true);
1061 super.mouseWheelMoved(event);
1062 if (event.getPreciseWheelRotation() < 0) {
1063 graphComponent.zoomIn();
1064 }
else if (event.getPreciseWheelRotation() > 0) {
1065 graphComponent.zoomOut();
1068 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
1078 super.mouseClicked(event);
1079 if (SwingUtilities.isRightMouseButton(event)) {
1080 final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(),
event.getY());
1081 if (cellAt != null && cellAt.isVertex()) {
1082 final JPopupMenu jPopupMenu =
new JPopupMenu();
1083 final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
1085 Set<mxCell> selectedVertices
1086 = Stream.of(graph.getSelectionModel().getCells())
1087 .map(mxCell.class::cast)
1088 .filter(mxCell::isVertex)
1089 .collect(Collectors.toSet());
1091 if (lockedVertexModel.isVertexLocked(cellAt)) {
1092 jPopupMenu.add(
new JMenuItem(
new UnlockAction(selectedVertices)));
1094 jPopupMenu.add(
new JMenuItem(
new LockAction(selectedVertices)));
1096 if (pinnedAccountModel.isAccountPinned(adiKey.getAccountDeviceInstance())) {
1097 jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
1099 jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
1100 jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
1102 jPopupMenu.show(graphComponent.getGraphControl(),
event.getX(),
event.getY());
1111 @NbBundle.Messages({
1112 "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
1113 "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
1119 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
1121 this.selectedVertices = selectedVertices;
1127 lockedVertexModel.unlock(selectedVertices);
1134 @NbBundle.Messages({
1135 "VisualizationPanel.lockAction.singularText=Lock Selected Account",
1136 "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
1142 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
1144 this.selectedVertices = selectedVertices;
1149 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()