19 package org.sleuthkit.autopsy.communications;
21 import com.google.common.eventbus.Subscribe;
22 import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
23 import com.mxgraph.layout.mxCircleLayout;
24 import com.mxgraph.layout.mxFastOrganicLayout;
25 import com.mxgraph.layout.mxIGraphLayout;
26 import com.mxgraph.layout.mxOrganicLayout;
27 import com.mxgraph.model.mxCell;
28 import com.mxgraph.model.mxICell;
29 import com.mxgraph.swing.handler.mxRubberband;
30 import com.mxgraph.swing.mxGraphComponent;
31 import com.mxgraph.util.mxCellRenderer;
32 import com.mxgraph.util.mxEvent;
33 import com.mxgraph.util.mxEventObject;
34 import com.mxgraph.util.mxEventSource;
35 import com.mxgraph.util.mxPoint;
36 import com.mxgraph.util.mxRectangle;
37 import com.mxgraph.util.mxUndoManager;
38 import com.mxgraph.util.mxUndoableEdit;
39 import com.mxgraph.view.mxCellState;
40 import com.mxgraph.view.mxGraph;
41 import com.mxgraph.view.mxGraphView;
42 import java.awt.BorderLayout;
43 import java.awt.Color;
44 import java.awt.Desktop;
45 import java.awt.Dimension;
47 import java.awt.Frame;
48 import java.awt.Graphics;
49 import java.awt.GridBagConstraints;
50 import java.awt.GridBagLayout;
51 import java.awt.GridLayout;
52 import java.awt.Insets;
53 import java.awt.event.ActionEvent;
54 import java.awt.event.ActionListener;
55 import java.awt.event.MouseAdapter;
56 import java.awt.event.MouseEvent;
57 import java.awt.event.MouseWheelEvent;
58 import java.awt.image.BufferedImage;
59 import java.beans.PropertyChangeEvent;
60 import java.io.IOException;
61 import java.nio.file.Files;
62 import java.nio.file.Path;
63 import java.nio.file.Paths;
64 import java.text.DecimalFormat;
65 import java.text.SimpleDateFormat;
66 import java.util.Arrays;
67 import java.util.Date;
68 import java.util.EnumSet;
69 import java.util.HashMap;
70 import java.util.HashSet;
73 import java.util.concurrent.Future;
74 import java.util.function.BiConsumer;
75 import java.util.logging.Level;
76 import java.util.stream.Collectors;
77 import java.util.stream.Stream;
78 import javafx.application.Platform;
79 import javafx.embed.swing.JFXPanel;
80 import javafx.scene.Scene;
81 import javafx.scene.layout.Pane;
82 import javax.swing.AbstractAction;
83 import javax.swing.ImageIcon;
84 import javax.swing.JButton;
85 import javax.swing.JLabel;
86 import javax.swing.JMenuItem;
87 import javax.swing.JOptionPane;
88 import javax.swing.JPanel;
89 import javax.swing.JPopupMenu;
90 import javax.swing.JTextField;
91 import javax.swing.JTextPane;
92 import javax.swing.JToolBar;
93 import javax.swing.SwingConstants;
94 import javax.swing.SwingUtilities;
95 import javax.swing.SwingWorker;
96 import org.apache.commons.lang3.StringUtils;
97 import org.controlsfx.control.Notifications;
98 import org.openide.util.NbBundle;
99 import org.openide.windows.WindowManager;
124 @SuppressWarnings(
"PMD.SingularField")
127 private static final long serialVersionUID = 1L;
129 private static final String BASE_IMAGE_PATH =
"/org/sleuthkit/autopsy/communications/images";
130 static final private ImageIcon unlockIcon
132 static final private ImageIcon lockIcon
135 @NbBundle.Messages(
"VisualizationPanel.cancelButton.text=Cancel")
136 private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
144 private final CommunicationsGraph
graph;
146 private final mxUndoManager undoManager =
new mxUndoManager();
150 private SwingWorker<?, ?> worker;
151 private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
152 private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
155 private NamedGraphLayout currentLayout;
159 private final StateManager stateManager;
161 @NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
163 this.relationshipBrowser = relationshipBrowser;
166 notificationsJFXPanel.setScene(
new Scene(
new Pane()));
168 graph =
new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
176 graphComponent =
new mxGraphComponent(graph) {
178 protected mxGraphComponent.mxGraphControl createGraphControl() {
180 return new mxGraphControl() {
183 public void paint(Graphics graphics) {
185 super.paint(graphics);
186 }
catch (NullPointerException ex) {
192 logger.log(Level.WARNING,
"There was a NPE while painting the VisualizationPanel", ex);
199 graphComponent.setAutoExtend(
true);
200 graphComponent.setAutoScroll(
true);
201 graphComponent.setAutoscrolls(
true);
202 graphComponent.setConnectable(
false);
203 graphComponent.setDragEnabled(
false);
204 graphComponent.setKeepSelectionVisibleOnZoom(
true);
205 graphComponent.setOpaque(
true);
206 graphComponent.setToolTips(
true);
207 graphComponent.setBackground(Color.WHITE);
208 borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
211 rubberband =
new mxRubberband(graphComponent);
213 lockedVertexModel.registerhandler(
this);
215 final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
216 -> zoomPercentLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
217 graph.getView().addListener(mxEvent.SCALE, scaleListener);
218 graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
221 graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
222 graphComponent.getGraphControl().addMouseListener(graphMouseListener);
226 final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
227 -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty(
"edit"));
229 graph.getModel().addListener(mxEvent.UNDO, undoListener);
230 graph.getView().addListener(mxEvent.UNDO, undoListener);
235 BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
236 layoutButtons.put(layout, layoutButton);
237 layoutButton.addActionListener(event -> applyLayout(layout));
240 configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
242 applyLayout(fastOrganicLayout);
244 stateManager =
new StateManager(pinnedAccountModel);
246 setStateButtonsEnabled();
252 void handle(LockedVertexModel.VertexLockEvent event) {
253 final Set<mxCell> vertices =
event.getVertices();
254 mxGraphView view = graph.getView();
255 vertices.forEach(vertex -> {
256 final mxCellState state = view.getState(vertex,
true);
257 view.updateLabel(state);
258 view.updateLabelBounds(state);
259 view.updateBoundingBox(state);
260 graphComponent.redraw(state);
265 void handle(
final CVTEvents.UnpinAccountsEvent pinEvent) {
266 graph.getModel().beginUpdate();
267 pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
271 graph.getModel().endUpdate();
273 setStateButtonsEnabled();
277 void handle(
final CVTEvents.PinAccountsEvent pinEvent) {
278 graph.getModel().beginUpdate();
279 if (pinEvent.isReplace()) {
282 pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances());
285 graph.getModel().endUpdate();
287 setStateButtonsEnabled();
291 void handle(
final CVTEvents.FilterChangeEvent filterChangeEvent) {
292 graph.getModel().beginUpdate();
294 currentFilter = filterChangeEvent.getNewFilter();
297 graph.getModel().endUpdate();
299 setStateButtonsEnabled();
302 @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
303 private
void rebuildGraph() {
304 if (pinnedAccountModel.isEmpty()) {
305 borderLayoutPanel.remove(graphComponent);
306 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
309 borderLayoutPanel.remove(placeHolderPanel);
310 borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
311 if (worker != null) {
317 worker = graph.rebuild(progress, commsManager, currentFilter);
318 cancelationListener.configure(worker, progress);
319 worker.addPropertyChangeListener((
final PropertyChangeEvent evt) -> {
320 if (worker.isDone()) {
321 if (worker.isCancelled()) {
325 applyLayout(currentLayout);
336 windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class,
this);
340 }
catch (TskCoreException ex) {
341 logger.log(Level.SEVERE,
"Error getting CommunicationsManager for the current case.", ex);
343 logger.log(Level.SEVERE,
"Can't get CommunicationsManager when there is no case open.", ex);
347 graph.getModel().beginUpdate();
351 graph.getModel().endUpdate();
353 if (evt.getNewValue() == null) {
356 Case currentCase = (
Case) evt.getNewValue();
359 }
catch (TskCoreException ex) {
360 logger.log(Level.SEVERE,
"Error getting CommunicationsManager for the current case.", ex);
371 @SuppressWarnings(
"unchecked")
373 private
void initComponents() {
374 GridBagConstraints gridBagConstraints;
376 borderLayoutPanel =
new JPanel();
377 placeHolderPanel =
new JPanel();
378 jTextPane1 =
new JTextPane();
379 notificationsJFXPanel =
new JFXPanel();
380 toolbar =
new JToolBar();
381 backButton =
new JButton();
382 forwardButton =
new JButton();
383 jSeparator3 =
new JToolBar.Separator();
384 clearVizButton =
new JButton();
385 fastOrganicLayoutButton =
new JButton();
386 jSeparator2 =
new JToolBar.Separator();
387 zoomLabel =
new JLabel();
388 zoomPercentLabel =
new JLabel();
389 zoomOutButton =
new JButton();
390 fitZoomButton =
new JButton();
391 zoomActualButton =
new JButton();
392 zoomInButton =
new JButton();
393 jSeparator1 =
new JToolBar.Separator();
394 snapshotButton =
new JButton();
396 setLayout(
new BorderLayout());
398 borderLayoutPanel.setLayout(
new BorderLayout());
400 placeHolderPanel.setLayout(
new GridBagLayout());
402 jTextPane1.setEditable(
false);
403 jTextPane1.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.jTextPane1.text"));
404 jTextPane1.setOpaque(
false);
405 gridBagConstraints =
new GridBagConstraints();
406 gridBagConstraints.anchor = GridBagConstraints.NORTH;
407 gridBagConstraints.weighty = 1.0;
408 gridBagConstraints.insets =
new Insets(50, 0, 0, 0);
409 placeHolderPanel.add(jTextPane1, gridBagConstraints);
411 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
412 borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
414 add(borderLayoutPanel, BorderLayout.CENTER);
416 toolbar.setRollover(
true);
418 backButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_previous.png")));
419 backButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.backButton.text_1"));
420 backButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.backButton.toolTipText"));
421 backButton.setFocusable(
false);
422 backButton.setHorizontalTextPosition(SwingConstants.CENTER);
423 backButton.setVerticalTextPosition(SwingConstants.BOTTOM);
424 backButton.addActionListener(
new ActionListener() {
425 public void actionPerformed(ActionEvent evt) {
426 backButtonActionPerformed(evt);
429 toolbar.add(backButton);
431 forwardButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_next.png")));
432 forwardButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.forwardButton.text"));
433 forwardButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.forwardButton.toolTipText"));
434 forwardButton.setFocusable(
false);
435 forwardButton.setHorizontalTextPosition(SwingConstants.CENTER);
436 forwardButton.setVerticalTextPosition(SwingConstants.BOTTOM);
437 forwardButton.addActionListener(
new ActionListener() {
438 public void actionPerformed(ActionEvent evt) {
439 forwardButtonActionPerformed(evt);
442 toolbar.add(forwardButton);
443 toolbar.add(jSeparator3);
445 clearVizButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/broom.png")));
446 clearVizButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.text_1"));
447 clearVizButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.toolTipText"));
448 clearVizButton.setActionCommand(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.actionCommand"));
449 clearVizButton.setFocusable(
false);
450 clearVizButton.setHorizontalTextPosition(SwingConstants.CENTER);
451 clearVizButton.setVerticalTextPosition(SwingConstants.BOTTOM);
452 clearVizButton.addActionListener(
new ActionListener() {
453 public void actionPerformed(ActionEvent evt) {
454 clearVizButtonActionPerformed(evt);
457 toolbar.add(clearVizButton);
459 fastOrganicLayoutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png")));
460 fastOrganicLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.text"));
461 fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.toolTipText"));
462 fastOrganicLayoutButton.setFocusable(
false);
463 fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
464 fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
465 fastOrganicLayoutButton.addActionListener(
new ActionListener() {
466 public void actionPerformed(ActionEvent evt) {
467 fastOrganicLayoutButtonActionPerformed(evt);
470 toolbar.add(fastOrganicLayoutButton);
471 toolbar.add(jSeparator2);
473 zoomLabel.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomLabel.text"));
474 toolbar.add(zoomLabel);
476 zoomPercentLabel.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomPercentLabel.text"));
477 toolbar.add(zoomPercentLabel);
479 zoomOutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png")));
480 zoomOutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.text"));
481 zoomOutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.toolTipText"));
482 zoomOutButton.setFocusable(
false);
483 zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
484 zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
485 zoomOutButton.addActionListener(
new ActionListener() {
486 public void actionPerformed(ActionEvent evt) {
487 zoomOutButtonActionPerformed(evt);
490 toolbar.add(zoomOutButton);
492 fitZoomButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png")));
493 fitZoomButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.text"));
494 fitZoomButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.toolTipText"));
495 fitZoomButton.setFocusable(
false);
496 fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
497 fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
498 fitZoomButton.addActionListener(
new ActionListener() {
499 public void actionPerformed(ActionEvent evt) {
500 fitZoomButtonActionPerformed(evt);
503 toolbar.add(fitZoomButton);
505 zoomActualButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png")));
506 zoomActualButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.text"));
507 zoomActualButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.toolTipText"));
508 zoomActualButton.setFocusable(
false);
509 zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
510 zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
511 zoomActualButton.addActionListener(
new ActionListener() {
512 public void actionPerformed(ActionEvent evt) {
513 zoomActualButtonActionPerformed(evt);
516 toolbar.add(zoomActualButton);
518 zoomInButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png")));
519 zoomInButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.text"));
520 zoomInButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.toolTipText"));
521 zoomInButton.setFocusable(
false);
522 zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
523 zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
524 zoomInButton.addActionListener(
new ActionListener() {
525 public void actionPerformed(ActionEvent evt) {
526 zoomInButtonActionPerformed(evt);
529 toolbar.add(zoomInButton);
530 toolbar.add(jSeparator1);
532 snapshotButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/report/images/image.png")));
533 snapshotButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.snapshotButton.text_1"));
534 snapshotButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.snapshotButton.toolTipText"));
535 snapshotButton.setFocusable(
false);
536 snapshotButton.setHorizontalTextPosition(SwingConstants.CENTER);
537 snapshotButton.setVerticalTextPosition(SwingConstants.BOTTOM);
538 snapshotButton.addActionListener(
new ActionListener() {
539 public void actionPerformed(ActionEvent evt) {
540 snapshotButtonActionPerformed(evt);
543 toolbar.add(snapshotButton);
545 add(toolbar, BorderLayout.NORTH);
553 graphComponent.zoomActual();
554 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
558 graphComponent.zoomIn();
559 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
563 graphComponent.zoomOut();
564 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
573 @NbBundle.Messages({
"VisualizationPanel.computingLayout=Computing Layout",
574 "# {0} - layout name",
575 "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
576 "# {0} - layout name",
577 "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
579 currentLayout = layout;
580 layoutButtons.forEach((layoutKey, button)
581 -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
584 progressIndicator.
start(Bundle.VisualizationPanel_computingLayout());
585 graph.getModel().beginUpdate();
587 layout.execute(graph.getDefaultParent());
590 graph.getModel().endUpdate();
591 progressIndicator.
finish();
596 CVTEvents.getCVTEventBus().post(
new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts()));
600 handleStateChange(stateManager.advance());
604 handleStateChange(stateManager.retreat());
613 if(newState == null) {
618 if(newState.isZoomChange()) {
619 graph.getView().setScale(newState.getZoomValue());
624 CVTEvents.getCVTEventBus().post(
new CVTEvents.StateChangeEvent(newState));
625 setStateButtonsEnabled();
627 graph.getModel().beginUpdate();
630 if(newState.getPinnedList() != null) {
631 pinnedAccountModel.pinAccount(newState.getPinnedList());
633 pinnedAccountModel.clear();
636 currentFilter = newState.getCommunicationsFilter();
640 graph.getModel().endUpdate();
647 backButton.setEnabled(stateManager.canRetreat());
648 forwardButton.setEnabled(stateManager.canAdvance());
652 "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation."
656 handleSnapshotEvent();
658 logger.log(Level.SEVERE,
"Unable to create communications snapsot report", ex);
661 -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
662 .text(Bundle.VisualizationPanel_snapshot_report_failure())
664 }
catch( TskCoreException ex) {
665 logger.log(Level.WARNING,
"Unable to add report to currenct case", ex);
674 graphComponent.zoomTo(1,
true);
675 mxPoint translate = graph.getView().getTranslate();
676 if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
677 translate =
new mxPoint();
680 mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
681 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
682 boundsForCells =
new mxRectangle(0, 0, 1, 1);
684 final mxPoint mxPoint =
new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
686 graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(),
false,
false);
688 boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
689 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
690 boundsForCells =
new mxRectangle(0, 0, 1, 1);
693 final Dimension size = graphComponent.getSize();
694 final double widthFactor = size.getWidth() / boundsForCells.getWidth();
695 final double heightFactor = size.getHeight() / boundsForCells.getHeight();
697 graphComponent.zoom((heightFactor + widthFactor) / 2.0);
707 "VisualizationPanel_action_dialogs_title=Communications",
708 "VisualizationPanel_module_name=Communications",
709 "VisualizationPanel_action_name_text=Snapshot Report",
710 "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
711 "VisualizationPane_reportName=Communications Snapshot",
712 "# {0} - default name",
713 "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
714 "VisualizationPane_blank_report_title=Blank Report Name",
715 "# {0} - report name",
716 "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}"
720 Date generationDate =
new Date();
724 final JTextField text =
new JTextField(50);
725 final JPanel panel =
new JPanel(
new GridLayout(2, 1));
726 panel.add(
new JLabel(Bundle.VisualizationPane_fileName_prompt()));
729 text.setText(defaultReportName);
731 int result = JOptionPane.showConfirmDialog(graphComponent, panel,
732 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
734 if (result == JOptionPane.OK_OPTION) {
735 String enteredReportName = text.getText();
737 if(enteredReportName.trim().isEmpty()){
738 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
739 if(result != JOptionPane.OK_OPTION) {
744 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
746 if (Files.exists(reportPath)) {
747 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
748 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
750 if (result == JOptionPane.OK_OPTION) {
752 createReport(currentCase, reportName);
755 createReport(currentCase, reportName);
756 currentCase.
addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
771 "VisualizationPane_DisplayName=Open Report",
772 "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
773 "VisualizationPane_MessageBoxTitle=Open Report Failure",
774 "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
775 "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
776 "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
777 "# {0} - report path",
778 "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
779 "VisualizationPane_Report_OK_Button=OK",
780 "VisualizationPane_Open_Report=Open Report",})
784 Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName, Bundle.VisualizationPane_reportName());
785 BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE,
true, null);
789 String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
790 String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
792 int result = JOptionPane.showOptionDialog(graphComponent, message,
793 Bundle.VisualizationPanel_action_dialogs_title(),
794 JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
795 null, buttons, buttons[1]);
796 if (result == JOptionPane.YES_NO_OPTION) {
798 Desktop.getDesktop().open(reportPath.toFile());
799 }
catch (IOException ex) {
800 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
801 Bundle.VisualizationPane_NoAssociatedEditorMessage(),
802 Bundle.VisualizationPane_MessageBoxTitle(),
803 JOptionPane.ERROR_MESSAGE);
804 }
catch (UnsupportedOperationException ex) {
805 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
806 Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
807 Bundle.VisualizationPane_MessageBoxTitle(),
808 JOptionPane.ERROR_MESSAGE);
809 }
catch (IllegalArgumentException ex) {
810 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
811 Bundle.VisualizationPane_MissingReportFileMessage(),
812 Bundle.VisualizationPane_MessageBoxTitle(),
813 JOptionPane.ERROR_MESSAGE);
814 }
catch (SecurityException ex) {
815 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
816 Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
817 Bundle.VisualizationPane_MessageBoxTitle(),
818 JOptionPane.ERROR_MESSAGE);
851 @SuppressWarnings(
"unchecked")
853 public void invoke(Object sender, mxEventObject evt) {
854 Object[] selectionCells = graph.getSelectionCells();
855 if (selectionCells.length > 0) {
856 mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(
new mxCell[selectionCells.length]);
857 HashSet<AccountDeviceInstance> selectedNodes =
new HashSet<>();
859 for (mxICell cell : selectedCells) {
861 mxICell source = (mxICell) graph.getModel().getTerminal(cell,
true);
862 mxICell target = (mxICell) graph.getModel().getTerminal(cell,
false);
864 selectedEdges.add(
new SelectionInfo.
GraphEdge(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance(),
865 ((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance()));
867 }
else if (cell.isVertex()) {
868 selectedNodes.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
872 relationshipBrowser.setSelectionInfo(
new SelectionInfo(selectedNodes, selectedEdges, currentFilter));
874 relationshipBrowser.setSelectionInfo(
new SelectionInfo(
new HashSet<>(),
new HashSet<>(), currentFilter));
884 String getDisplayName();
898 return super.isVertexIgnored(vertex)
899 || lockedVertexModel.isVertexLocked((mxCell) vertex);
904 if (isVertexIgnored(vertex)) {
905 return getVertexBounds(vertex);
907 return super.setVertexLocation(vertex, x, y);
913 return "Fast Organic";
929 return super.isVertexIgnored(vertex)
930 || lockedVertexModel.isVertexLocked((mxCell) vertex);
935 if (isVertexIgnored(vertex)) {
936 return getVertexBounds(vertex);
938 return super.setVertexLocation(vertex, x, y);
960 return super.isVertexIgnored(vertex)
961 || lockedVertexModel.isVertexLocked((mxCell) vertex);
966 if (isVertexIgnored(vertex)) {
967 return getVertexBounds(vertex);
969 return super.setVertexLocation(vertex, x, y);
990 return super.isVertexIgnored(vertex)
991 || lockedVertexModel.isVertexLocked((mxCell) vertex);
996 if (isVertexIgnored(vertex)) {
997 return getVertexBounds(vertex);
999 return super.setVertexLocation(vertex, x, y);
1005 return "Hierarchical";
1019 this.cancellable = cancellable;
1020 this.progress = progress;
1026 cancellable.cancel(
true);
1044 super.mouseWheelMoved(event);
1045 if (event.getPreciseWheelRotation() < 0) {
1046 graphComponent.zoomIn();
1047 }
else if (event.getPreciseWheelRotation() > 0) {
1048 graphComponent.zoomOut();
1051 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
1061 super.mouseClicked(event);
1062 if (SwingUtilities.isRightMouseButton(event)) {
1063 final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(),
event.getY());
1064 if (cellAt != null && cellAt.isVertex()) {
1065 final JPopupMenu jPopupMenu =
new JPopupMenu();
1066 final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
1068 Set<mxCell> selectedVertices
1069 = Stream.of(graph.getSelectionModel().getCells())
1070 .map(mxCell.class::cast)
1071 .filter(mxCell::isVertex)
1072 .collect(Collectors.toSet());
1074 if (lockedVertexModel.isVertexLocked(cellAt)) {
1075 jPopupMenu.add(
new JMenuItem(
new UnlockAction(selectedVertices)));
1077 jPopupMenu.add(
new JMenuItem(
new LockAction(selectedVertices)));
1079 if (pinnedAccountModel.isAccountPinned(adiKey.getAccountDeviceInstance())) {
1080 jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
1082 jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
1083 jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
1085 jPopupMenu.show(graphComponent.getGraphControl(),
event.getX(),
event.getY());
1094 @NbBundle.Messages({
1095 "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
1096 "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
1102 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
1104 this.selectedVertices = selectedVertices;
1110 lockedVertexModel.unlock(selectedVertices);
1117 @NbBundle.Messages({
1118 "VisualizationPanel.lockAction.singularText=Lock Selected Account",
1119 "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
1125 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
1127 this.selectedVertices = selectedVertices;
1132 lockedVertexModel.lock(selectedVertices);
void zoomOutButtonActionPerformed(ActionEvent evt)
final Set< mxCell > selectedVertices
boolean isVertexIgnored(Object vertex)
void invoke(Object sender, mxEventObject evt)
JFXPanel notificationsJFXPanel
void forwardButtonActionPerformed(ActionEvent evt)
void mouseClicked(final MouseEvent event)
void createReport(Case currentCase, String reportName)
CommunicationsManager commsManager
boolean isVertexIgnored(Object vertex)
void handleStateChange(StateManager.CommunicationsState newState)
void applyLayout(NamedGraphLayout layout)
void actionPerformed(final ActionEvent event)
final mxRubberband rubberband
static boolean deleteFileDir(File path)
String getReportDirectory()
JToolBar.Separator jSeparator3
void fastOrganicLayoutButtonActionPerformed(ActionEvent evt)
void addReport(String localPath, String srcModuleName, String reportName)
JToolBar.Separator jSeparator2
ModalDialogProgressIndicator progress
mxRectangle setVertexLocation(Object vertex, double x, double y)
synchronized void start(String message, int totalWorkUnits)
synchronized void setCancelling(String cancellingMessage)
final CommunicationsGraph graph
void actionPerformed(final ActionEvent event)
boolean isVertexIgnored(Object vertex)
CommunicationsFilter currentFilter
void backButtonActionPerformed(ActionEvent evt)
void fitZoomButtonActionPerformed(ActionEvent evt)
SleuthkitCase getSleuthkitCase()
void mouseWheelMoved(final MouseWheelEvent event)
JToolBar.Separator jSeparator1
void handleSnapshotEvent()
static String escapeFileName(String fileName)
mxRectangle setVertexLocation(Object vertex, double x, double y)
boolean isVertexIgnored(Object vertex)
mxRectangle setVertexLocation(Object vertex, double x, double y)
final mxGraphComponent graphComponent
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
void actionPerformed(ActionEvent event)
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
mxRectangle setVertexLocation(Object vertex, double x, double y)
JButton fastOrganicLayoutButton
void zoomInButtonActionPerformed(ActionEvent evt)
void setStateButtonsEnabled()
final Set< mxCell > selectedVertices
void zoomActualButtonActionPerformed(ActionEvent evt)
void clearVizButtonActionPerformed(ActionEvent evt)
void snapshotButtonActionPerformed(ActionEvent evt)
synchronized void finish()