19 package org.sleuthkit.autopsy.communications;
21 import com.google.common.eventbus.Subscribe;
22 import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
23 import com.mxgraph.layout.mxCircleLayout;
24 import com.mxgraph.layout.mxFastOrganicLayout;
25 import com.mxgraph.layout.mxIGraphLayout;
26 import com.mxgraph.layout.mxOrganicLayout;
27 import com.mxgraph.model.mxCell;
28 import com.mxgraph.model.mxICell;
29 import com.mxgraph.swing.handler.mxRubberband;
30 import com.mxgraph.swing.mxGraphComponent;
31 import com.mxgraph.util.mxCellRenderer;
32 import com.mxgraph.util.mxEvent;
33 import com.mxgraph.util.mxEventObject;
34 import com.mxgraph.util.mxEventSource;
35 import com.mxgraph.util.mxPoint;
36 import com.mxgraph.util.mxRectangle;
37 import com.mxgraph.util.mxUndoManager;
38 import com.mxgraph.util.mxUndoableEdit;
39 import com.mxgraph.view.mxCellState;
40 import com.mxgraph.view.mxGraph;
41 import com.mxgraph.view.mxGraphView;
42 import java.awt.BorderLayout;
43 import java.awt.Color;
44 import java.awt.Desktop;
45 import java.awt.Dimension;
47 import java.awt.Frame;
48 import java.awt.Graphics;
49 import java.awt.GridBagConstraints;
50 import java.awt.GridBagLayout;
51 import java.awt.GridLayout;
52 import java.awt.Insets;
53 import java.awt.event.ActionEvent;
54 import java.awt.event.ActionListener;
55 import java.awt.event.MouseAdapter;
56 import java.awt.event.MouseEvent;
57 import java.awt.event.MouseWheelEvent;
58 import java.awt.image.BufferedImage;
59 import java.beans.PropertyChangeEvent;
60 import java.io.IOException;
61 import java.nio.file.Files;
62 import java.nio.file.Path;
63 import java.nio.file.Paths;
64 import java.text.DecimalFormat;
65 import java.text.SimpleDateFormat;
66 import java.util.Arrays;
67 import java.util.Date;
68 import java.util.EnumSet;
69 import java.util.HashMap;
70 import java.util.HashSet;
73 import java.util.concurrent.Future;
74 import java.util.function.BiConsumer;
75 import java.util.logging.Level;
76 import java.util.stream.Collectors;
77 import java.util.stream.Stream;
78 import javafx.application.Platform;
79 import javafx.embed.swing.JFXPanel;
80 import javafx.scene.Scene;
81 import javafx.scene.layout.Pane;
82 import javax.swing.AbstractAction;
83 import javax.swing.ImageIcon;
84 import javax.swing.JButton;
85 import javax.swing.JLabel;
86 import javax.swing.JMenuItem;
87 import javax.swing.JOptionPane;
88 import javax.swing.JPanel;
89 import javax.swing.JPopupMenu;
90 import javax.swing.JTextField;
91 import javax.swing.JTextPane;
92 import javax.swing.JToolBar;
93 import javax.swing.SwingConstants;
94 import javax.swing.SwingUtilities;
95 import javax.swing.SwingWorker;
96 import org.apache.commons.lang3.StringUtils;
97 import org.controlsfx.control.Notifications;
98 import org.openide.util.NbBundle;
99 import org.openide.windows.WindowManager;
124 @SuppressWarnings(
"PMD.SingularField")
127 private static final long serialVersionUID = 1L;
129 private static final String BASE_IMAGE_PATH =
"/org/sleuthkit/autopsy/communications/images";
130 static final private ImageIcon unlockIcon
132 static final private ImageIcon lockIcon
135 @NbBundle.Messages(
"VisualizationPanel.cancelButton.text=Cancel")
136 private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
144 private final CommunicationsGraph
graph;
146 private final mxUndoManager undoManager =
new mxUndoManager();
150 private SwingWorker<?, ?> worker;
151 private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
152 private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
155 private NamedGraphLayout currentLayout;
159 private final StateManager stateManager;
161 @NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
163 this.relationshipBrowser = relationshipBrowser;
166 Platform.runLater(() -> {
167 notificationsJFXPanel.setScene(
new Scene(
new Pane()));
170 graph =
new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
178 graphComponent =
new mxGraphComponent(graph) {
180 protected mxGraphComponent.mxGraphControl createGraphControl() {
182 return new mxGraphControl() {
185 public void paint(Graphics graphics) {
187 super.paint(graphics);
188 }
catch (NullPointerException ex) {
194 logger.log(Level.WARNING,
"There was a NPE while painting the VisualizationPanel", ex);
201 graphComponent.setAutoExtend(
true);
202 graphComponent.setAutoScroll(
true);
203 graphComponent.setAutoscrolls(
true);
204 graphComponent.setConnectable(
false);
205 graphComponent.setDragEnabled(
false);
206 graphComponent.setKeepSelectionVisibleOnZoom(
true);
207 graphComponent.setOpaque(
true);
208 graphComponent.setToolTips(
true);
209 graphComponent.setBackground(Color.WHITE);
210 borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
213 rubberband =
new mxRubberband(graphComponent);
215 lockedVertexModel.registerhandler(
this);
217 final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
218 -> zoomPercentLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
219 graph.getView().addListener(mxEvent.SCALE, scaleListener);
220 graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
223 graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
224 graphComponent.getGraphControl().addMouseListener(graphMouseListener);
228 final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
229 -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty(
"edit"));
231 graph.getModel().addListener(mxEvent.UNDO, undoListener);
232 graph.getView().addListener(mxEvent.UNDO, undoListener);
237 BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
238 layoutButtons.put(layout, layoutButton);
239 layoutButton.addActionListener(event -> applyLayout(layout));
242 configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
244 applyLayout(fastOrganicLayout);
246 stateManager =
new StateManager(pinnedAccountModel);
248 setStateButtonsEnabled();
254 void handle(LockedVertexModel.VertexLockEvent event) {
255 final Set<mxCell> vertices =
event.getVertices();
256 mxGraphView view = graph.getView();
257 vertices.forEach(vertex -> {
258 final mxCellState state = view.getState(vertex,
true);
259 view.updateLabel(state);
260 view.updateLabelBounds(state);
261 view.updateBoundingBox(state);
262 graphComponent.redraw(state);
267 void handle(
final CVTEvents.UnpinAccountsEvent pinEvent) {
268 graph.getModel().beginUpdate();
269 pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
273 graph.getModel().endUpdate();
275 setStateButtonsEnabled();
279 void handle(
final CVTEvents.PinAccountsEvent pinEvent) {
280 graph.getModel().beginUpdate();
281 if (pinEvent.isReplace()) {
284 pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances());
287 graph.getModel().endUpdate();
289 setStateButtonsEnabled();
293 void handle(
final CVTEvents.FilterChangeEvent filterChangeEvent) {
294 graph.getModel().beginUpdate();
296 currentFilter = filterChangeEvent.getNewFilter();
299 graph.getModel().endUpdate();
301 setStateButtonsEnabled();
304 @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
305 private
void rebuildGraph() {
306 if (pinnedAccountModel.isEmpty()) {
307 borderLayoutPanel.remove(graphComponent);
308 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
311 borderLayoutPanel.remove(placeHolderPanel);
312 borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
313 if (worker != null) {
319 worker = graph.rebuild(progress, commsManager, currentFilter);
320 cancelationListener.configure(worker, progress);
321 worker.addPropertyChangeListener((
final PropertyChangeEvent evt) -> {
322 if (worker.isDone()) {
323 if (worker.isCancelled()) {
327 applyLayout(currentLayout);
338 windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class,
this);
342 }
catch (TskCoreException ex) {
343 logger.log(Level.SEVERE,
"Error getting CommunicationsManager for the current case.", ex);
345 logger.log(Level.SEVERE,
"Can't get CommunicationsManager when there is no case open.", ex);
349 graph.getModel().beginUpdate();
353 graph.getModel().endUpdate();
355 if (evt.getNewValue() == null) {
358 Case currentCase = (
Case) evt.getNewValue();
361 }
catch (TskCoreException ex) {
362 logger.log(Level.SEVERE,
"Error getting CommunicationsManager for the current case.", ex);
373 @SuppressWarnings(
"unchecked")
375 private
void initComponents() {
376 GridBagConstraints gridBagConstraints;
378 borderLayoutPanel =
new JPanel();
379 placeHolderPanel =
new JPanel();
380 jTextPane1 =
new JTextPane();
381 notificationsJFXPanel =
new JFXPanel();
382 toolbar =
new JToolBar();
383 backButton =
new JButton();
384 forwardButton =
new JButton();
385 jSeparator3 =
new JToolBar.Separator();
386 clearVizButton =
new JButton();
387 fastOrganicLayoutButton =
new JButton();
388 jSeparator2 =
new JToolBar.Separator();
389 zoomLabel =
new JLabel();
390 zoomPercentLabel =
new JLabel();
391 zoomOutButton =
new JButton();
392 fitZoomButton =
new JButton();
393 zoomActualButton =
new JButton();
394 zoomInButton =
new JButton();
395 jSeparator1 =
new JToolBar.Separator();
396 snapshotButton =
new JButton();
398 setLayout(
new BorderLayout());
400 borderLayoutPanel.setLayout(
new BorderLayout());
402 placeHolderPanel.setLayout(
new GridBagLayout());
404 jTextPane1.setEditable(
false);
405 jTextPane1.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.jTextPane1.text"));
406 jTextPane1.setOpaque(
false);
407 gridBagConstraints =
new GridBagConstraints();
408 gridBagConstraints.anchor = GridBagConstraints.NORTH;
409 gridBagConstraints.weighty = 1.0;
410 gridBagConstraints.insets =
new Insets(50, 0, 0, 0);
411 placeHolderPanel.add(jTextPane1, gridBagConstraints);
413 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
414 borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
416 add(borderLayoutPanel, BorderLayout.CENTER);
418 toolbar.setRollover(
true);
420 backButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_previous.png")));
421 backButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.backButton.text_1"));
422 backButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.backButton.toolTipText"));
423 backButton.setFocusable(
false);
424 backButton.setHorizontalTextPosition(SwingConstants.CENTER);
425 backButton.setVerticalTextPosition(SwingConstants.BOTTOM);
426 backButton.addActionListener(
new ActionListener() {
427 public void actionPerformed(ActionEvent evt) {
428 backButtonActionPerformed(evt);
431 toolbar.add(backButton);
433 forwardButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_next.png")));
434 forwardButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.forwardButton.text"));
435 forwardButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.forwardButton.toolTipText"));
436 forwardButton.setFocusable(
false);
437 forwardButton.setHorizontalTextPosition(SwingConstants.CENTER);
438 forwardButton.setVerticalTextPosition(SwingConstants.BOTTOM);
439 forwardButton.addActionListener(
new ActionListener() {
440 public void actionPerformed(ActionEvent evt) {
441 forwardButtonActionPerformed(evt);
444 toolbar.add(forwardButton);
445 toolbar.add(jSeparator3);
447 clearVizButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/broom.png")));
448 clearVizButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.text_1"));
449 clearVizButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.toolTipText"));
450 clearVizButton.setActionCommand(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.actionCommand"));
451 clearVizButton.setFocusable(
false);
452 clearVizButton.setHorizontalTextPosition(SwingConstants.CENTER);
453 clearVizButton.setVerticalTextPosition(SwingConstants.BOTTOM);
454 clearVizButton.addActionListener(
new ActionListener() {
455 public void actionPerformed(ActionEvent evt) {
456 clearVizButtonActionPerformed(evt);
459 toolbar.add(clearVizButton);
461 fastOrganicLayoutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png")));
462 fastOrganicLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.text"));
463 fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.toolTipText"));
464 fastOrganicLayoutButton.setFocusable(
false);
465 fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
466 fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
467 fastOrganicLayoutButton.addActionListener(
new ActionListener() {
468 public void actionPerformed(ActionEvent evt) {
469 fastOrganicLayoutButtonActionPerformed(evt);
472 toolbar.add(fastOrganicLayoutButton);
473 toolbar.add(jSeparator2);
475 zoomLabel.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomLabel.text"));
476 toolbar.add(zoomLabel);
478 zoomPercentLabel.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomPercentLabel.text"));
479 toolbar.add(zoomPercentLabel);
481 zoomOutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png")));
482 zoomOutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.text"));
483 zoomOutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.toolTipText"));
484 zoomOutButton.setFocusable(
false);
485 zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
486 zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
487 zoomOutButton.addActionListener(
new ActionListener() {
488 public void actionPerformed(ActionEvent evt) {
489 zoomOutButtonActionPerformed(evt);
492 toolbar.add(zoomOutButton);
494 fitZoomButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png")));
495 fitZoomButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.text"));
496 fitZoomButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.toolTipText"));
497 fitZoomButton.setFocusable(
false);
498 fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
499 fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
500 fitZoomButton.addActionListener(
new ActionListener() {
501 public void actionPerformed(ActionEvent evt) {
502 fitZoomButtonActionPerformed(evt);
505 toolbar.add(fitZoomButton);
507 zoomActualButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png")));
508 zoomActualButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.text"));
509 zoomActualButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.toolTipText"));
510 zoomActualButton.setFocusable(
false);
511 zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
512 zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
513 zoomActualButton.addActionListener(
new ActionListener() {
514 public void actionPerformed(ActionEvent evt) {
515 zoomActualButtonActionPerformed(evt);
518 toolbar.add(zoomActualButton);
520 zoomInButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png")));
521 zoomInButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.text"));
522 zoomInButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.toolTipText"));
523 zoomInButton.setFocusable(
false);
524 zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
525 zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
526 zoomInButton.addActionListener(
new ActionListener() {
527 public void actionPerformed(ActionEvent evt) {
528 zoomInButtonActionPerformed(evt);
531 toolbar.add(zoomInButton);
532 toolbar.add(jSeparator1);
534 snapshotButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/report/images/image.png")));
535 snapshotButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.snapshotButton.text_1"));
536 snapshotButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.snapshotButton.toolTipText"));
537 snapshotButton.setFocusable(
false);
538 snapshotButton.setHorizontalTextPosition(SwingConstants.CENTER);
539 snapshotButton.setVerticalTextPosition(SwingConstants.BOTTOM);
540 snapshotButton.addActionListener(
new ActionListener() {
541 public void actionPerformed(ActionEvent evt) {
542 snapshotButtonActionPerformed(evt);
545 toolbar.add(snapshotButton);
547 add(toolbar, BorderLayout.NORTH);
555 graphComponent.zoomActual();
556 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
560 graphComponent.zoomIn();
561 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
565 graphComponent.zoomOut();
566 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
575 @NbBundle.Messages({
"VisualizationPanel.computingLayout=Computing Layout",
576 "# {0} - layout name",
577 "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
578 "# {0} - layout name",
579 "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
581 currentLayout = layout;
582 layoutButtons.forEach((layoutKey, button)
583 -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
586 progressIndicator.
start(Bundle.VisualizationPanel_computingLayout());
587 graph.getModel().beginUpdate();
589 layout.execute(graph.getDefaultParent());
592 graph.getModel().endUpdate();
593 progressIndicator.
finish();
598 CVTEvents.getCVTEventBus().post(
new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts()));
602 handleStateChange(stateManager.advance());
606 handleStateChange(stateManager.retreat());
615 if(newState == null) {
620 if(newState.isZoomChange()) {
621 graph.getView().setScale(newState.getZoomValue());
626 CVTEvents.getCVTEventBus().post(
new CVTEvents.StateChangeEvent(newState));
627 setStateButtonsEnabled();
629 graph.getModel().beginUpdate();
632 if(newState.getPinnedList() != null) {
633 pinnedAccountModel.pinAccount(newState.getPinnedList());
635 pinnedAccountModel.clear();
638 currentFilter = newState.getCommunicationsFilter();
642 graph.getModel().endUpdate();
649 backButton.setEnabled(stateManager.canRetreat());
650 forwardButton.setEnabled(stateManager.canAdvance());
654 "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation."
658 handleSnapshotEvent();
660 logger.log(Level.SEVERE,
"Unable to create communications snapsot report", ex);
663 -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
664 .text(Bundle.VisualizationPanel_snapshot_report_failure())
666 }
catch( TskCoreException ex) {
667 logger.log(Level.WARNING,
"Unable to add report to currenct case", ex);
676 graphComponent.zoomTo(1,
true);
677 mxPoint translate = graph.getView().getTranslate();
678 if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
679 translate =
new mxPoint();
682 mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
683 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
684 boundsForCells =
new mxRectangle(0, 0, 1, 1);
686 final mxPoint mxPoint =
new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
688 graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(),
false,
false);
690 boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
691 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
692 boundsForCells =
new mxRectangle(0, 0, 1, 1);
695 final Dimension size = graphComponent.getSize();
696 final double widthFactor = size.getWidth() / boundsForCells.getWidth();
697 final double heightFactor = size.getHeight() / boundsForCells.getHeight();
699 graphComponent.zoom((heightFactor + widthFactor) / 2.0);
709 "VisualizationPanel_action_dialogs_title=Communications",
710 "VisualizationPanel_module_name=Communications",
711 "VisualizationPanel_action_name_text=Snapshot Report",
712 "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
713 "VisualizationPane_reportName=Communications Snapshot",
714 "# {0} - default name",
715 "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
716 "VisualizationPane_blank_report_title=Blank Report Name",
717 "# {0} - report name",
718 "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}"
722 Date generationDate =
new Date();
726 final JTextField text =
new JTextField(50);
727 final JPanel panel =
new JPanel(
new GridLayout(2, 1));
728 panel.add(
new JLabel(Bundle.VisualizationPane_fileName_prompt()));
731 text.setText(defaultReportName);
733 int result = JOptionPane.showConfirmDialog(graphComponent, panel,
734 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
736 if (result == JOptionPane.OK_OPTION) {
737 String enteredReportName = text.getText();
739 if(enteredReportName.trim().isEmpty()){
740 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
741 if(result != JOptionPane.OK_OPTION) {
746 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
748 if (Files.exists(reportPath)) {
749 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
750 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
752 if (result == JOptionPane.OK_OPTION) {
754 createReport(currentCase, reportName);
757 createReport(currentCase, reportName);
758 currentCase.
addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
773 "VisualizationPane_DisplayName=Open Report",
774 "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
775 "VisualizationPane_MessageBoxTitle=Open Report Failure",
776 "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
777 "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
778 "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
779 "# {0} - report path",
780 "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
781 "VisualizationPane_Report_OK_Button=OK",
782 "VisualizationPane_Open_Report=Open Report",})
786 Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName, Bundle.VisualizationPane_reportName());
787 BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE,
true, null);
791 String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
792 String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
794 int result = JOptionPane.showOptionDialog(graphComponent, message,
795 Bundle.VisualizationPanel_action_dialogs_title(),
796 JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
797 null, buttons, buttons[1]);
798 if (result == JOptionPane.YES_NO_OPTION) {
800 Desktop.getDesktop().open(reportPath.toFile());
801 }
catch (IOException ex) {
802 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
803 Bundle.VisualizationPane_NoAssociatedEditorMessage(),
804 Bundle.VisualizationPane_MessageBoxTitle(),
805 JOptionPane.ERROR_MESSAGE);
806 }
catch (UnsupportedOperationException ex) {
807 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
808 Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
809 Bundle.VisualizationPane_MessageBoxTitle(),
810 JOptionPane.ERROR_MESSAGE);
811 }
catch (IllegalArgumentException ex) {
812 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
813 Bundle.VisualizationPane_MissingReportFileMessage(),
814 Bundle.VisualizationPane_MessageBoxTitle(),
815 JOptionPane.ERROR_MESSAGE);
816 }
catch (SecurityException ex) {
817 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
818 Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
819 Bundle.VisualizationPane_MessageBoxTitle(),
820 JOptionPane.ERROR_MESSAGE);
853 @SuppressWarnings(
"unchecked")
855 public void invoke(Object sender, mxEventObject evt) {
856 Object[] selectionCells = graph.getSelectionCells();
857 if (selectionCells.length > 0) {
858 mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(
new mxCell[selectionCells.length]);
859 HashSet<AccountDeviceInstance> selectedNodes =
new HashSet<>();
861 for (mxICell cell : selectedCells) {
863 mxICell source = (mxICell) graph.getModel().getTerminal(cell,
true);
864 mxICell target = (mxICell) graph.getModel().getTerminal(cell,
false);
866 selectedEdges.add(
new SelectionInfo.
GraphEdge(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance(),
867 ((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance()));
869 }
else if (cell.isVertex()) {
870 selectedNodes.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
874 relationshipBrowser.setSelectionInfo(
new SelectionInfo(selectedNodes, selectedEdges, currentFilter));
876 relationshipBrowser.setSelectionInfo(
new SelectionInfo(
new HashSet<>(),
new HashSet<>(), currentFilter));
886 String getDisplayName();
900 return super.isVertexIgnored(vertex)
901 || lockedVertexModel.isVertexLocked((mxCell) vertex);
906 if (isVertexIgnored(vertex)) {
907 return getVertexBounds(vertex);
909 return super.setVertexLocation(vertex, x, y);
915 return "Fast Organic";
931 return super.isVertexIgnored(vertex)
932 || lockedVertexModel.isVertexLocked((mxCell) vertex);
937 if (isVertexIgnored(vertex)) {
938 return getVertexBounds(vertex);
940 return super.setVertexLocation(vertex, x, y);
962 return super.isVertexIgnored(vertex)
963 || lockedVertexModel.isVertexLocked((mxCell) vertex);
968 if (isVertexIgnored(vertex)) {
969 return getVertexBounds(vertex);
971 return super.setVertexLocation(vertex, x, y);
992 return super.isVertexIgnored(vertex)
993 || lockedVertexModel.isVertexLocked((mxCell) vertex);
998 if (isVertexIgnored(vertex)) {
999 return getVertexBounds(vertex);
1001 return super.setVertexLocation(vertex, x, y);
1007 return "Hierarchical";
1021 this.cancellable = cancellable;
1022 this.progress = progress;
1028 cancellable.cancel(
true);
1046 super.mouseWheelMoved(event);
1047 if (event.getPreciseWheelRotation() < 0) {
1048 graphComponent.zoomIn();
1049 }
else if (event.getPreciseWheelRotation() > 0) {
1050 graphComponent.zoomOut();
1053 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
1063 super.mouseClicked(event);
1064 if (SwingUtilities.isRightMouseButton(event)) {
1065 final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(),
event.getY());
1066 if (cellAt != null && cellAt.isVertex()) {
1067 final JPopupMenu jPopupMenu =
new JPopupMenu();
1068 final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
1070 Set<mxCell> selectedVertices
1071 = Stream.of(graph.getSelectionModel().getCells())
1072 .map(mxCell.class::cast)
1073 .filter(mxCell::isVertex)
1074 .collect(Collectors.toSet());
1076 if (lockedVertexModel.isVertexLocked(cellAt)) {
1077 jPopupMenu.add(
new JMenuItem(
new UnlockAction(selectedVertices)));
1079 jPopupMenu.add(
new JMenuItem(
new LockAction(selectedVertices)));
1081 if (pinnedAccountModel.isAccountPinned(adiKey.getAccountDeviceInstance())) {
1082 jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
1084 jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
1085 jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
1087 jPopupMenu.show(graphComponent.getGraphControl(),
event.getX(),
event.getY());
1096 @NbBundle.Messages({
1097 "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
1098 "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
1104 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
1106 this.selectedVertices = selectedVertices;
1112 lockedVertexModel.unlock(selectedVertices);
1119 @NbBundle.Messages({
1120 "VisualizationPanel.lockAction.singularText=Lock Selected Account",
1121 "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
1127 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
1129 this.selectedVertices = selectedVertices;
1134 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()