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;
46 import java.awt.FlowLayout;
48 import java.awt.Frame;
49 import java.awt.Graphics;
50 import java.awt.GridLayout;
51 import java.awt.event.ActionEvent;
52 import java.awt.event.ActionListener;
53 import java.awt.event.MouseAdapter;
54 import java.awt.event.MouseEvent;
55 import java.awt.event.MouseWheelEvent;
56 import java.awt.image.BufferedImage;
57 import java.beans.PropertyChangeEvent;
58 import java.io.IOException;
59 import java.nio.file.Files;
60 import java.nio.file.Path;
61 import java.nio.file.Paths;
62 import java.text.DecimalFormat;
63 import java.text.SimpleDateFormat;
64 import java.util.Arrays;
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.JTextArea;
90 import javax.swing.JTextField;
91 import javax.swing.JToolBar;
92 import javax.swing.SwingConstants;
93 import javax.swing.SwingUtilities;
94 import javax.swing.SwingWorker;
95 import org.apache.commons.lang3.StringUtils;
96 import org.controlsfx.control.Notifications;
97 import org.jdesktop.layout.GroupLayout;
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();
248 toolbar.setLayout(
new WrapLayout(FlowLayout.LEFT));
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() {
375 borderLayoutPanel =
new JPanel();
376 placeHolderPanel =
new JPanel();
377 jTextArea1 =
new JTextArea();
378 notificationsJFXPanel =
new JFXPanel();
379 toolbar =
new JToolBar();
380 backButton =
new JButton();
381 forwardButton =
new JButton();
382 jSeparator3 =
new JToolBar.Separator();
383 clearVizButton =
new JButton();
384 fastOrganicLayoutButton =
new JButton();
385 jSeparator2 =
new JToolBar.Separator();
386 zoomLabel =
new JLabel();
387 zoomPercentLabel =
new JLabel();
388 zoomOutButton =
new JButton();
389 fitZoomButton =
new JButton();
390 zoomActualButton =
new JButton();
391 zoomInButton =
new JButton();
392 jSeparator1 =
new JToolBar.Separator();
393 snapshotButton =
new JButton();
395 setLayout(
new BorderLayout());
397 borderLayoutPanel.setLayout(
new BorderLayout());
399 jTextArea1.setBackground(
new Color(240, 240, 240));
400 jTextArea1.setColumns(20);
401 jTextArea1.setLineWrap(
true);
402 jTextArea1.setRows(5);
403 jTextArea1.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.jTextArea1.text"));
405 GroupLayout placeHolderPanelLayout =
new GroupLayout(placeHolderPanel);
406 placeHolderPanel.setLayout(placeHolderPanelLayout);
407 placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
408 .add(placeHolderPanelLayout.createSequentialGroup()
409 .addContainerGap(316, Short.MAX_VALUE)
410 .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE)
411 .addContainerGap(481, Short.MAX_VALUE))
413 placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
414 .add(placeHolderPanelLayout.createSequentialGroup()
415 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
416 .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 47, GroupLayout.PREFERRED_SIZE)
417 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
420 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
421 borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
423 add(borderLayoutPanel, BorderLayout.CENTER);
425 toolbar.setRollover(
true);
427 backButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_previous.png")));
428 backButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.backButton.text_1"));
429 backButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.backButton.toolTipText"));
430 backButton.setFocusable(
false);
431 backButton.setHorizontalTextPosition(SwingConstants.CENTER);
432 backButton.setVerticalTextPosition(SwingConstants.BOTTOM);
433 backButton.addActionListener(
new ActionListener() {
434 public void actionPerformed(ActionEvent evt) {
435 backButtonActionPerformed(evt);
438 toolbar.add(backButton);
440 forwardButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_next.png")));
441 forwardButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.forwardButton.text"));
442 forwardButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.forwardButton.toolTipText"));
443 forwardButton.setFocusable(
false);
444 forwardButton.setHorizontalTextPosition(SwingConstants.CENTER);
445 forwardButton.setVerticalTextPosition(SwingConstants.BOTTOM);
446 forwardButton.addActionListener(
new ActionListener() {
447 public void actionPerformed(ActionEvent evt) {
448 forwardButtonActionPerformed(evt);
451 toolbar.add(forwardButton);
452 toolbar.add(jSeparator3);
454 clearVizButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/broom.png")));
455 clearVizButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.text_1"));
456 clearVizButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.toolTipText"));
457 clearVizButton.setActionCommand(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.actionCommand"));
458 clearVizButton.setFocusable(
false);
459 clearVizButton.setHorizontalTextPosition(SwingConstants.CENTER);
460 clearVizButton.setVerticalTextPosition(SwingConstants.BOTTOM);
461 clearVizButton.addActionListener(
new ActionListener() {
462 public void actionPerformed(ActionEvent evt) {
463 clearVizButtonActionPerformed(evt);
466 toolbar.add(clearVizButton);
468 fastOrganicLayoutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png")));
469 fastOrganicLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.text"));
470 fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.toolTipText"));
471 fastOrganicLayoutButton.setFocusable(
false);
472 fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
473 fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
474 fastOrganicLayoutButton.addActionListener(
new ActionListener() {
475 public void actionPerformed(ActionEvent evt) {
476 fastOrganicLayoutButtonActionPerformed(evt);
479 toolbar.add(fastOrganicLayoutButton);
480 toolbar.add(jSeparator2);
482 zoomLabel.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomLabel.text"));
483 toolbar.add(zoomLabel);
485 zoomPercentLabel.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomPercentLabel.text"));
486 toolbar.add(zoomPercentLabel);
488 zoomOutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png")));
489 zoomOutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.text"));
490 zoomOutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.toolTipText"));
491 zoomOutButton.setFocusable(
false);
492 zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
493 zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
494 zoomOutButton.addActionListener(
new ActionListener() {
495 public void actionPerformed(ActionEvent evt) {
496 zoomOutButtonActionPerformed(evt);
499 toolbar.add(zoomOutButton);
501 fitZoomButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png")));
502 fitZoomButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.text"));
503 fitZoomButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.toolTipText"));
504 fitZoomButton.setFocusable(
false);
505 fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
506 fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
507 fitZoomButton.addActionListener(
new ActionListener() {
508 public void actionPerformed(ActionEvent evt) {
509 fitZoomButtonActionPerformed(evt);
512 toolbar.add(fitZoomButton);
514 zoomActualButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png")));
515 zoomActualButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.text"));
516 zoomActualButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.toolTipText"));
517 zoomActualButton.setFocusable(
false);
518 zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
519 zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
520 zoomActualButton.addActionListener(
new ActionListener() {
521 public void actionPerformed(ActionEvent evt) {
522 zoomActualButtonActionPerformed(evt);
525 toolbar.add(zoomActualButton);
527 zoomInButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png")));
528 zoomInButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.text"));
529 zoomInButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.toolTipText"));
530 zoomInButton.setFocusable(
false);
531 zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
532 zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
533 zoomInButton.addActionListener(
new ActionListener() {
534 public void actionPerformed(ActionEvent evt) {
535 zoomInButtonActionPerformed(evt);
538 toolbar.add(zoomInButton);
539 toolbar.add(jSeparator1);
541 snapshotButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/report/images/image.png")));
542 snapshotButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.snapshotButton.text_1"));
543 snapshotButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.snapshotButton.toolTipText"));
544 snapshotButton.setFocusable(
false);
545 snapshotButton.setHorizontalTextPosition(SwingConstants.CENTER);
546 snapshotButton.setVerticalTextPosition(SwingConstants.BOTTOM);
547 snapshotButton.addActionListener(
new ActionListener() {
548 public void actionPerformed(ActionEvent evt) {
549 snapshotButtonActionPerformed(evt);
552 toolbar.add(snapshotButton);
554 add(toolbar, BorderLayout.NORTH);
562 graphComponent.zoomActual();
563 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
567 graphComponent.zoomIn();
568 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
572 graphComponent.zoomOut();
573 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
582 @NbBundle.Messages({
"VisualizationPanel.computingLayout=Computing Layout",
583 "# {0} - layout name",
584 "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
585 "# {0} - layout name",
586 "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
588 currentLayout = layout;
589 layoutButtons.forEach((layoutKey, button)
590 -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
593 progressIndicator.
start(Bundle.VisualizationPanel_computingLayout());
595 new SwingWorker<Void, Void>() {
597 protected Void doInBackground() {
598 graph.getModel().beginUpdate();
600 layout.execute(graph.getDefaultParent());
603 graph.getModel().endUpdate();
604 progressIndicator.
finish();
610 protected void done() {
613 }
catch (InterruptedException | ExecutionException ex) {
614 logger.log(Level.WARNING,
"CVT graph layout failed.", ex);
621 CVTEvents.getCVTEventBus().post(
new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts()));
625 handleStateChange(stateManager.advance());
629 handleStateChange(stateManager.retreat());
638 if(newState == null) {
643 if(newState.isZoomChange()) {
644 graph.getView().setScale(newState.getZoomValue());
649 CVTEvents.getCVTEventBus().post(
new CVTEvents.StateChangeEvent(newState));
650 setStateButtonsEnabled();
652 graph.getModel().beginUpdate();
655 if(newState.getPinnedList() != null) {
656 pinnedAccountModel.pinAccount(newState.getPinnedList());
658 pinnedAccountModel.clear();
661 currentFilter = newState.getCommunicationsFilter();
665 graph.getModel().endUpdate();
672 backButton.setEnabled(stateManager.canRetreat());
673 forwardButton.setEnabled(stateManager.canAdvance());
677 "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation."
681 handleSnapshotEvent();
683 logger.log(Level.SEVERE,
"Unable to create communications snapsot report", ex);
686 -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
687 .text(Bundle.VisualizationPanel_snapshot_report_failure())
689 }
catch( TskCoreException ex) {
690 logger.log(Level.WARNING,
"Unable to add report to currenct case", ex);
699 graphComponent.zoomTo(1,
true);
700 mxPoint translate = graph.getView().getTranslate();
701 if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
702 translate =
new mxPoint();
705 mxRectangle 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);
709 final mxPoint mxPoint =
new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
711 graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(),
false,
false);
713 boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
714 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
715 boundsForCells =
new mxRectangle(0, 0, 1, 1);
718 final Dimension size = graphComponent.getSize();
719 final double widthFactor = size.getWidth() / boundsForCells.getWidth();
720 final double heightFactor = size.getHeight() / boundsForCells.getHeight();
722 graphComponent.zoom((heightFactor + widthFactor) / 2.0);
732 "VisualizationPanel_action_dialogs_title=Communications",
733 "VisualizationPanel_module_name=Communications",
734 "VisualizationPanel_action_name_text=Snapshot Report",
735 "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
736 "VisualizationPane_reportName=Communications Snapshot",
737 "# {0} - default name",
738 "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
739 "VisualizationPane_blank_report_title=Blank Report Name",
740 "# {0} - report name",
741 "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}"
745 Date generationDate =
new Date();
749 final JTextField text =
new JTextField(50);
750 final JPanel panel =
new JPanel(
new GridLayout(2, 1));
751 panel.add(
new JLabel(Bundle.VisualizationPane_fileName_prompt()));
754 text.setText(defaultReportName);
756 int result = JOptionPane.showConfirmDialog(graphComponent, panel,
757 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
759 if (result == JOptionPane.OK_OPTION) {
760 String enteredReportName = text.getText();
762 if(enteredReportName.trim().isEmpty()){
763 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
764 if(result != JOptionPane.OK_OPTION) {
769 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
771 if (Files.exists(reportPath)) {
772 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
773 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
775 if (result == JOptionPane.OK_OPTION) {
777 createReport(currentCase, reportName);
780 createReport(currentCase, reportName);
781 currentCase.
addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
796 "VisualizationPane_DisplayName=Open Report",
797 "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
798 "VisualizationPane_MessageBoxTitle=Open Report Failure",
799 "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
800 "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
801 "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
802 "# {0} - report path",
803 "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
804 "VisualizationPane_Report_OK_Button=OK",
805 "VisualizationPane_Open_Report=Open Report",})
809 Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName, Bundle.VisualizationPane_reportName());
810 BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE,
true, null);
814 String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
815 String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
817 int result = JOptionPane.showOptionDialog(graphComponent, message,
818 Bundle.VisualizationPanel_action_dialogs_title(),
819 JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
820 null, buttons, buttons[1]);
821 if (result == JOptionPane.YES_NO_OPTION) {
823 Desktop.getDesktop().open(reportPath.toFile());
824 }
catch (IOException ex) {
825 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
826 Bundle.VisualizationPane_NoAssociatedEditorMessage(),
827 Bundle.VisualizationPane_MessageBoxTitle(),
828 JOptionPane.ERROR_MESSAGE);
829 }
catch (UnsupportedOperationException ex) {
830 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
831 Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
832 Bundle.VisualizationPane_MessageBoxTitle(),
833 JOptionPane.ERROR_MESSAGE);
834 }
catch (IllegalArgumentException ex) {
835 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
836 Bundle.VisualizationPane_MissingReportFileMessage(),
837 Bundle.VisualizationPane_MessageBoxTitle(),
838 JOptionPane.ERROR_MESSAGE);
839 }
catch (SecurityException ex) {
840 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
841 Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
842 Bundle.VisualizationPane_MessageBoxTitle(),
843 JOptionPane.ERROR_MESSAGE);
876 @SuppressWarnings(
"unchecked")
878 public void invoke(Object sender, mxEventObject evt) {
879 Object[] selectionCells = graph.getSelectionCells();
880 if (selectionCells.length > 0) {
881 mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(
new mxCell[selectionCells.length]);
882 HashSet<AccountDeviceInstance> selectedNodes =
new HashSet<>();
884 for (mxICell cell : selectedCells) {
886 mxICell source = (mxICell) graph.getModel().getTerminal(cell,
true);
887 mxICell target = (mxICell) graph.getModel().getTerminal(cell,
false);
889 selectedEdges.add(
new SelectionInfo.
GraphEdge(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance(),
890 ((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance()));
892 }
else if (cell.isVertex()) {
893 selectedNodes.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
897 relationshipBrowser.setSelectionInfo(
new SelectionInfo(selectedNodes, selectedEdges, currentFilter));
899 relationshipBrowser.setSelectionInfo(
new SelectionInfo(
new HashSet<>(),
new HashSet<>(), currentFilter));
909 String getDisplayName();
923 return super.isVertexIgnored(vertex)
924 || lockedVertexModel.isVertexLocked((mxCell) vertex);
929 if (isVertexIgnored(vertex)) {
930 return getVertexBounds(vertex);
932 return super.setVertexLocation(vertex, x, y);
938 return "Fast Organic";
954 return super.isVertexIgnored(vertex)
955 || lockedVertexModel.isVertexLocked((mxCell) vertex);
960 if (isVertexIgnored(vertex)) {
961 return getVertexBounds(vertex);
963 return super.setVertexLocation(vertex, x, y);
985 return super.isVertexIgnored(vertex)
986 || lockedVertexModel.isVertexLocked((mxCell) vertex);
991 if (isVertexIgnored(vertex)) {
992 return getVertexBounds(vertex);
994 return super.setVertexLocation(vertex, x, y);
1015 return super.isVertexIgnored(vertex)
1016 || lockedVertexModel.isVertexLocked((mxCell) vertex);
1021 if (isVertexIgnored(vertex)) {
1022 return getVertexBounds(vertex);
1024 return super.setVertexLocation(vertex, x, y);
1030 return "Hierarchical";
1044 this.cancellable = cancellable;
1045 this.progress = progress;
1051 cancellable.cancel(
true);
1069 super.mouseWheelMoved(event);
1070 if (event.getPreciseWheelRotation() < 0) {
1071 graphComponent.zoomIn();
1072 }
else if (event.getPreciseWheelRotation() > 0) {
1073 graphComponent.zoomOut();
1076 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
1086 super.mouseClicked(event);
1087 if (SwingUtilities.isRightMouseButton(event)) {
1088 final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(),
event.getY());
1089 if (cellAt != null && cellAt.isVertex()) {
1090 final JPopupMenu jPopupMenu =
new JPopupMenu();
1091 final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
1093 Set<mxCell> selectedVertices
1094 = Stream.of(graph.getSelectionModel().getCells())
1095 .map(mxCell.class::cast)
1096 .filter(mxCell::isVertex)
1097 .collect(Collectors.toSet());
1099 if (lockedVertexModel.isVertexLocked(cellAt)) {
1100 jPopupMenu.add(
new JMenuItem(
new UnlockAction(selectedVertices)));
1102 jPopupMenu.add(
new JMenuItem(
new LockAction(selectedVertices)));
1104 if (pinnedAccountModel.isAccountPinned(adiKey)) {
1105 jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
1107 jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
1108 jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
1110 jPopupMenu.show(graphComponent.getGraphControl(),
event.getX(),
event.getY());
1119 @NbBundle.Messages({
1120 "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
1121 "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
1127 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
1129 this.selectedVertices = selectedVertices;
1135 lockedVertexModel.unlock(selectedVertices);
1142 @NbBundle.Messages({
1143 "VisualizationPanel.lockAction.singularText=Lock Selected Account",
1144 "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
1150 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
1152 this.selectedVertices = selectedVertices;
1157 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()