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.Cursor;
45 import java.awt.Desktop;
46 import java.awt.Dimension;
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.beans.PropertyVetoException;
59 import java.io.IOException;
60 import java.nio.file.Files;
61 import java.nio.file.Path;
62 import java.nio.file.Paths;
63 import java.text.DecimalFormat;
64 import java.text.SimpleDateFormat;
65 import java.util.Arrays;
66 import java.util.Date;
67 import java.util.EnumSet;
68 import java.util.HashMap;
69 import java.util.HashSet;
70 import java.util.List;
73 import java.util.concurrent.ExecutionException;
74 import java.util.concurrent.Future;
75 import java.util.function.BiConsumer;
76 import java.util.logging.Level;
77 import java.util.stream.Collectors;
78 import java.util.stream.Stream;
79 import javafx.application.Platform;
80 import javafx.embed.swing.JFXPanel;
81 import javafx.scene.Scene;
82 import javafx.scene.layout.Pane;
83 import javax.swing.AbstractAction;
84 import javax.swing.ImageIcon;
85 import javax.swing.JButton;
86 import javax.swing.JLabel;
87 import javax.swing.JMenuItem;
88 import javax.swing.JOptionPane;
89 import javax.swing.JPanel;
90 import javax.swing.JPopupMenu;
91 import javax.swing.JSplitPane;
92 import javax.swing.JTextArea;
93 import javax.swing.JTextField;
94 import javax.swing.JToolBar;
95 import javax.swing.SwingConstants;
96 import javax.swing.SwingUtilities;
97 import javax.swing.SwingWorker;
98 import org.apache.commons.lang3.StringUtils;
99 import org.controlsfx.control.Notifications;
100 import org.jdesktop.layout.GroupLayout;
101 import org.jdesktop.layout.LayoutStyle;
102 import org.openide.explorer.ExplorerManager;
103 import org.openide.explorer.ExplorerUtils;
104 import org.openide.nodes.Node;
105 import org.openide.util.Lookup;
106 import org.openide.util.NbBundle;
107 import org.openide.util.lookup.ProxyLookup;
108 import org.openide.windows.WindowManager;
130 @SuppressWarnings(
"PMD.SingularField")
133 private static final long serialVersionUID = 1L;
135 private static final String BASE_IMAGE_PATH =
"/org/sleuthkit/autopsy/communications/images";
136 static final private ImageIcon unlockIcon
138 static final private ImageIcon lockIcon
141 @NbBundle.Messages(
"VisualizationPanel.cancelButton.text=Cancel")
142 private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
144 private final ExplorerManager vizEM =
new ExplorerManager();
145 private final ExplorerManager gacEM =
new ExplorerManager();
153 private final CommunicationsGraph
graph;
155 private final mxUndoManager undoManager =
new mxUndoManager();
159 private SwingWorker<?, ?> worker;
160 private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
161 private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
164 private NamedGraphLayout currentLayout;
166 @NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
170 notificationsJFXPanel.setScene(
new Scene(
new Pane()));
172 graph =
new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
180 graphComponent =
new mxGraphComponent(graph) {
182 protected mxGraphComponent.mxGraphControl createGraphControl() {
184 return new mxGraphControl() {
187 public void paint(Graphics graphics) {
189 super.paint(graphics);
190 }
catch (NullPointerException ex) {
196 logger.log(Level.WARNING,
"There was a NPE while painting the VisualizationPanel", ex);
203 graphComponent.setAutoExtend(
true);
204 graphComponent.setAutoScroll(
true);
205 graphComponent.setAutoscrolls(
true);
206 graphComponent.setConnectable(
false);
207 graphComponent.setDragEnabled(
false);
208 graphComponent.setKeepSelectionVisibleOnZoom(
true);
209 graphComponent.setOpaque(
true);
210 graphComponent.setToolTips(
true);
211 graphComponent.setBackground(Color.WHITE);
212 borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
215 rubberband =
new mxRubberband(graphComponent);
217 lockedVertexModel.registerhandler(
this);
219 final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
220 -> zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
221 graph.getView().addListener(mxEvent.SCALE, scaleListener);
222 graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
225 graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
226 graphComponent.getGraphControl().addMouseListener(graphMouseListener);
229 splitPane.setRightComponent(messageBrowser);
230 proxyLookup =
new ProxyLookup(
231 ExplorerUtils.createLookup(vizEM, getActionMap()),
237 final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
238 -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty(
"edit"));
240 graph.getModel().addListener(mxEvent.UNDO, undoListener);
241 graph.getView().addListener(mxEvent.UNDO, undoListener);
246 organicLayout.setMaxIterations(10);
250 BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
251 layoutButtons.put(layout, layoutButton);
252 layoutButton.addActionListener(event -> applyLayout(layout));
255 configure.accept(circleLayoutButton, circleLayout);
256 configure.accept(organicLayoutButton, organicLayout);
257 configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
258 configure.accept(hierarchyLayoutButton, hierarchyLayout);
260 applyLayout(fastOrganicLayout);
269 void handle(LockedVertexModel.VertexLockEvent event) {
270 final Set<mxCell> vertices =
event.getVertices();
271 mxGraphView view = graph.getView();
272 vertices.forEach(vertex -> {
273 final mxCellState state = view.getState(vertex,
true);
274 view.updateLabel(state);
275 view.updateLabelBounds(state);
276 view.updateBoundingBox(state);
277 graphComponent.redraw(state);
282 void handle(
final CVTEvents.UnpinAccountsEvent pinEvent) {
283 graph.getModel().beginUpdate();
284 pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
288 graph.getModel().endUpdate();
292 void handle(
final CVTEvents.PinAccountsEvent pinEvent) {
293 graph.getModel().beginUpdate();
294 if (pinEvent.isReplace()) {
297 pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances());
300 graph.getModel().endUpdate();
304 void handle(
final CVTEvents.FilterChangeEvent filterChangeEvent) {
305 graph.getModel().beginUpdate();
307 currentFilter = filterChangeEvent.getNewFilter();
310 graph.getModel().endUpdate();
313 @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
314 private
void rebuildGraph() {
315 if (pinnedAccountModel.isEmpty()) {
316 borderLayoutPanel.remove(graphComponent);
317 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
320 borderLayoutPanel.remove(placeHolderPanel);
321 borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
322 if (worker != null) {
328 worker = graph.rebuild(progress, commsManager, currentFilter);
329 cancelationListener.configure(worker, progress);
330 worker.addPropertyChangeListener((
final PropertyChangeEvent evt) -> {
331 if (worker.isDone()) {
332 if (worker.isCancelled()) {
336 applyLayout(currentLayout);
347 windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class,
this);
351 }
catch (TskCoreException ex) {
352 logger.log(Level.SEVERE,
"Error getting CommunicationsManager for the current case.", ex);
354 logger.log(Level.SEVERE,
"Can't get CommunicationsManager when there is no case open.", ex);
358 graph.getModel().beginUpdate();
362 graph.getModel().endUpdate();
364 if (evt.getNewValue() == null) {
367 Case currentCase = (
Case) evt.getNewValue();
370 }
catch (TskCoreException ex) {
371 logger.log(Level.SEVERE,
"Error getting CommunicationsManager for the current case.", ex);
382 @SuppressWarnings(
"unchecked")
384 private
void initComponents() {
386 splitPane =
new JSplitPane();
387 borderLayoutPanel =
new JPanel();
388 placeHolderPanel =
new JPanel();
389 jTextArea1 =
new JTextArea();
390 toolbar =
new JPanel();
391 jLabel1 =
new JLabel();
392 hierarchyLayoutButton =
new JButton();
393 fastOrganicLayoutButton =
new JButton();
394 organicLayoutButton =
new JButton();
395 circleLayoutButton =
new JButton();
396 jSeparator1 =
new JToolBar.Separator();
397 zoomOutButton =
new JButton();
398 zoomInButton =
new JButton();
399 zoomActualButton =
new JButton();
400 fitZoomButton =
new JButton();
401 jLabel2 =
new JLabel();
402 zoomLabel =
new JLabel();
403 clearVizButton =
new JButton();
404 jSeparator2 =
new JToolBar.Separator();
405 snapshotButton =
new JButton();
406 jSeparator3 =
new JToolBar.Separator();
407 notificationsJFXPanel =
new JFXPanel();
409 setLayout(
new BorderLayout());
411 splitPane.setDividerLocation(800);
412 splitPane.setResizeWeight(0.5);
414 borderLayoutPanel.setLayout(
new BorderLayout());
416 jTextArea1.setBackground(
new Color(240, 240, 240));
417 jTextArea1.setColumns(20);
418 jTextArea1.setLineWrap(
true);
419 jTextArea1.setRows(5);
420 jTextArea1.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.jTextArea1.text"));
422 GroupLayout placeHolderPanelLayout =
new GroupLayout(placeHolderPanel);
423 placeHolderPanel.setLayout(placeHolderPanelLayout);
424 placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
425 .add(placeHolderPanelLayout.createSequentialGroup()
426 .addContainerGap(268, Short.MAX_VALUE)
427 .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE)
428 .addContainerGap(445, Short.MAX_VALUE))
430 placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
431 .add(placeHolderPanelLayout.createSequentialGroup()
432 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
433 .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 47, GroupLayout.PREFERRED_SIZE)
434 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
437 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
439 jLabel1.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.jLabel1.text"));
441 hierarchyLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.hierarchyLayoutButton.text"));
442 hierarchyLayoutButton.setFocusable(
false);
443 hierarchyLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
444 hierarchyLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
446 fastOrganicLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.text"));
447 fastOrganicLayoutButton.setFocusable(
false);
448 fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
449 fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
451 organicLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.organicLayoutButton.text"));
452 organicLayoutButton.setFocusable(
false);
453 organicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
454 organicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
456 circleLayoutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.circleLayoutButton.text"));
457 circleLayoutButton.setFocusable(
false);
458 circleLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
459 circleLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
461 jSeparator1.setOrientation(SwingConstants.VERTICAL);
463 zoomOutButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png")));
464 zoomOutButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.text"));
465 zoomOutButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.toolTipText"));
466 zoomOutButton.setFocusable(
false);
467 zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
468 zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
469 zoomOutButton.addActionListener(
new ActionListener() {
470 public void actionPerformed(ActionEvent evt) {
471 zoomOutButtonActionPerformed(evt);
475 zoomInButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png")));
476 zoomInButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.text"));
477 zoomInButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.toolTipText"));
478 zoomInButton.setFocusable(
false);
479 zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
480 zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
481 zoomInButton.addActionListener(
new ActionListener() {
482 public void actionPerformed(ActionEvent evt) {
483 zoomInButtonActionPerformed(evt);
487 zoomActualButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png")));
488 zoomActualButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.text"));
489 zoomActualButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.toolTipText"));
490 zoomActualButton.setFocusable(
false);
491 zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
492 zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
493 zoomActualButton.addActionListener(
new ActionListener() {
494 public void actionPerformed(ActionEvent evt) {
495 zoomActualButtonActionPerformed(evt);
499 fitZoomButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png")));
500 fitZoomButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.text"));
501 fitZoomButton.setToolTipText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.toolTipText"));
502 fitZoomButton.setFocusable(
false);
503 fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
504 fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
505 fitZoomButton.addActionListener(
new ActionListener() {
506 public void actionPerformed(ActionEvent evt) {
507 fitZoomButtonActionPerformed(evt);
511 jLabel2.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.jLabel2.text"));
513 zoomLabel.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomLabel.text"));
515 clearVizButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/broom.png")));
516 clearVizButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.text_1"));
517 clearVizButton.addActionListener(
new ActionListener() {
518 public void actionPerformed(ActionEvent evt) {
519 clearVizButtonActionPerformed(evt);
523 jSeparator2.setOrientation(SwingConstants.VERTICAL);
525 snapshotButton.setIcon(
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/report/images/image.png")));
526 snapshotButton.setText(NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.snapshotButton.text_1"));
527 snapshotButton.addActionListener(
new ActionListener() {
528 public void actionPerformed(ActionEvent evt) {
529 snapshotButtonActionPerformed(evt);
533 jSeparator3.setOrientation(SwingConstants.VERTICAL);
535 GroupLayout toolbarLayout =
new GroupLayout(toolbar);
536 toolbar.setLayout(toolbarLayout);
537 toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
538 .add(toolbarLayout.createSequentialGroup()
542 .add(jSeparator1, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
545 .addPreferredGap(LayoutStyle.RELATED)
546 .add(fastOrganicLayoutButton)
547 .addPreferredGap(LayoutStyle.RELATED)
548 .add(organicLayoutButton)
549 .addPreferredGap(LayoutStyle.RELATED)
550 .add(hierarchyLayoutButton)
551 .addPreferredGap(LayoutStyle.RELATED)
552 .add(circleLayoutButton)
553 .addPreferredGap(LayoutStyle.RELATED)
554 .add(jSeparator2, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
555 .addPreferredGap(LayoutStyle.RELATED)
557 .addPreferredGap(LayoutStyle.RELATED)
559 .addPreferredGap(LayoutStyle.RELATED)
560 .add(zoomOutButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
561 .addPreferredGap(LayoutStyle.RELATED)
562 .add(zoomInButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
563 .addPreferredGap(LayoutStyle.RELATED)
564 .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE)
565 .addPreferredGap(LayoutStyle.RELATED)
566 .add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
567 .addPreferredGap(LayoutStyle.RELATED)
568 .add(jSeparator3, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
569 .addPreferredGap(LayoutStyle.RELATED)
571 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
573 toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
574 .add(toolbarLayout.createSequentialGroup()
576 .add(toolbarLayout.createParallelGroup(GroupLayout.CENTER)
577 .add(jLabel1, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE)
578 .add(hierarchyLayoutButton)
579 .add(fastOrganicLayoutButton)
580 .add(organicLayoutButton)
581 .add(circleLayoutButton)
582 .add(jSeparator1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
584 .add(zoomInButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
585 .add(zoomActualButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
586 .add(fitZoomButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
590 .add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
592 .add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
596 borderLayoutPanel.add(toolbar, BorderLayout.PAGE_START);
597 borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
599 splitPane.setLeftComponent(borderLayoutPanel);
601 add(splitPane, BorderLayout.CENTER);
609 graphComponent.zoomActual();
613 graphComponent.zoomIn();
617 graphComponent.zoomOut();
626 @NbBundle.Messages({
"VisualizationPanel.computingLayout=Computing Layout",
627 "# {0} - layout name",
628 "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
629 "# {0} - layout name",
630 "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
632 currentLayout = layout;
633 layoutButtons.forEach((layoutKey, button)
634 -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
637 progressIndicator.
start(Bundle.VisualizationPanel_computingLayout());
639 new SwingWorker<Void, Void>() {
641 protected Void doInBackground() {
642 graph.getModel().beginUpdate();
644 layout.execute(graph.getDefaultParent());
647 graph.getModel().endUpdate();
648 progressIndicator.
finish();
654 protected void done() {
657 }
catch (InterruptedException | ExecutionException ex) {
658 logger.log(Level.WARNING,
"CVT graph layout failed.", ex);
659 String message = (lockedVertexModel.isEmpty())
660 ? Bundle.VisualizationPanel_layoutFail_text(layout.
getDisplayName())
661 : Bundle.VisualizationPanel_layoutFailWithLockedVertices_text(layout.
getDisplayName());
664 -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
674 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
675 graph.getModel().beginUpdate();
676 pinnedAccountModel.clear();
680 graph.getModel().endUpdate();
681 setCursor(Cursor.getDefaultCursor());
685 "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation."
689 handleSnapshotEvent();
691 logger.log(Level.SEVERE,
"Unable to create communications snapsot report", ex);
694 -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
695 .text(Bundle.VisualizationPanel_snapshot_report_failure())
697 }
catch( TskCoreException ex) {
698 logger.log(Level.WARNING,
"Unable to add report to currenct case", ex);
703 graphComponent.zoomTo(1,
true);
704 mxPoint translate = graph.getView().getTranslate();
705 if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
706 translate =
new mxPoint();
709 mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
710 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
711 boundsForCells =
new mxRectangle(0, 0, 1, 1);
713 final mxPoint mxPoint =
new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
715 graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(),
false,
false);
717 boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
718 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
719 boundsForCells =
new mxRectangle(0, 0, 1, 1);
722 final Dimension size = graphComponent.getSize();
723 final double widthFactor = size.getWidth() / boundsForCells.getWidth();
724 final double heightFactor = size.getHeight() / boundsForCells.getHeight();
726 graphComponent.zoom((heightFactor + widthFactor) / 2.0);
736 "VisualizationPanel_action_dialogs_title=Communications",
737 "VisualizationPanel_module_name=Communications",
738 "VisualizationPanel_action_name_text=Snapshot Report",
739 "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
740 "VisualizationPane_reportName=Communications Snapshot",
741 "# {0} - default name",
742 "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
743 "VisualizationPane_blank_report_title=Blank Report Name",
744 "# {0} - report name",
745 "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}"
749 Date generationDate =
new Date();
753 final JTextField text =
new JTextField(50);
754 final JPanel panel =
new JPanel(
new GridLayout(2, 1));
755 panel.add(
new JLabel(Bundle.VisualizationPane_fileName_prompt()));
758 text.setText(defaultReportName);
760 int result = JOptionPane.showConfirmDialog(graphComponent, panel,
761 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
763 if (result == JOptionPane.OK_OPTION) {
764 String enteredReportName = text.getText();
766 if(enteredReportName.trim().isEmpty()){
767 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
768 if(result != JOptionPane.OK_OPTION) {
773 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
775 if (Files.exists(reportPath)) {
776 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
777 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
779 if (result == JOptionPane.OK_OPTION) {
781 createReport(currentCase, reportName);
784 createReport(currentCase, reportName);
785 currentCase.
addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
800 "VisualizationPane_DisplayName=Open Report",
801 "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
802 "VisualizationPane_MessageBoxTitle=Open Report Failure",
803 "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
804 "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
805 "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
806 "# {0} - report path",
807 "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
808 "VisualizationPane_Report_OK_Button=OK",
809 "VisualizationPane_Open_Report=Open Report",})
813 Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName, Bundle.VisualizationPane_reportName());
814 BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE,
true, null);
818 String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
819 String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
821 int result = JOptionPane.showOptionDialog(graphComponent, message,
822 Bundle.VisualizationPanel_action_dialogs_title(),
823 JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
824 null, buttons, buttons[1]);
825 if (result == JOptionPane.YES_NO_OPTION) {
827 Desktop.getDesktop().open(reportPath.toFile());
828 }
catch (IOException ex) {
829 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
830 Bundle.VisualizationPane_NoAssociatedEditorMessage(),
831 Bundle.VisualizationPane_MessageBoxTitle(),
832 JOptionPane.ERROR_MESSAGE);
833 }
catch (UnsupportedOperationException ex) {
834 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
835 Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
836 Bundle.VisualizationPane_MessageBoxTitle(),
837 JOptionPane.ERROR_MESSAGE);
838 }
catch (IllegalArgumentException ex) {
839 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
840 Bundle.VisualizationPane_MissingReportFileMessage(),
841 Bundle.VisualizationPane_MessageBoxTitle(),
842 JOptionPane.ERROR_MESSAGE);
843 }
catch (SecurityException ex) {
844 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
845 Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
846 Bundle.VisualizationPane_MessageBoxTitle(),
847 JOptionPane.ERROR_MESSAGE);
883 @SuppressWarnings(
"unchecked")
885 public void invoke(Object sender, mxEventObject evt) {
886 Object[] selectionCells = graph.getSelectionCells();
887 Node rootNode = Node.EMPTY;
888 Node[] selectedNodes =
new Node[0];
889 if (selectionCells.length > 0) {
890 mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(
new mxCell[selectionCells.length]);
891 HashSet<Content> relationshipSources =
new HashSet<>();
892 HashSet<AccountDeviceInstanceKey> adis =
new HashSet<>();
893 for (mxICell cell : selectedCells) {
895 mxICell source = (mxICell) graph.getModel().getTerminal(cell,
true);
896 AccountDeviceInstanceKey account1 = (AccountDeviceInstanceKey) source.getValue();
897 mxICell target = (mxICell) graph.getModel().getTerminal(cell,
false);
898 AccountDeviceInstanceKey account2 = (AccountDeviceInstanceKey) target.getValue();
900 final List<Content> relationshipSources1 = commsManager.getRelationshipSources(
901 account1.getAccountDeviceInstance(),
902 account2.getAccountDeviceInstance(),
904 relationshipSources.addAll(relationshipSources1);
905 }
catch (TskCoreException tskCoreException) {
906 logger.log(Level.SEVERE,
" Error getting relationsips....", tskCoreException);
908 }
else if (cell.isVertex()) {
909 adis.add((AccountDeviceInstanceKey) cell.getValue());
913 rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager);
914 selectedNodes =
new Node[]{rootNode};
916 vizEM.setRootContext(rootNode);
918 vizEM.setSelectedNodes(selectedNodes);
919 }
catch (PropertyVetoException ex) {
920 logger.log(Level.SEVERE,
"Selection vetoed.", ex);
930 String getDisplayName();
944 return super.isVertexIgnored(vertex)
945 || lockedVertexModel.isVertexLocked((mxCell) vertex);
950 if (isVertexIgnored(vertex)) {
951 return getVertexBounds(vertex);
953 return super.setVertexLocation(vertex, x, y);
959 return "Fast Organic";
975 return super.isVertexIgnored(vertex)
976 || lockedVertexModel.isVertexLocked((mxCell) vertex);
981 if (isVertexIgnored(vertex)) {
982 return getVertexBounds(vertex);
984 return super.setVertexLocation(vertex, x, y);
1001 setResetEdges(
true);
1006 return super.isVertexIgnored(vertex)
1007 || lockedVertexModel.isVertexLocked((mxCell) vertex);
1012 if (isVertexIgnored(vertex)) {
1013 return getVertexBounds(vertex);
1015 return super.setVertexLocation(vertex, x, y);
1036 return super.isVertexIgnored(vertex)
1037 || lockedVertexModel.isVertexLocked((mxCell) vertex);
1042 if (isVertexIgnored(vertex)) {
1043 return getVertexBounds(vertex);
1045 return super.setVertexLocation(vertex, x, y);
1051 return "Hierarchical";
1065 this.cancellable = cancellable;
1066 this.progress = progress;
1072 cancellable.cancel(
true);
1090 super.mouseWheelMoved(event);
1091 if (event.getPreciseWheelRotation() < 0) {
1092 graphComponent.zoomIn();
1093 }
else if (event.getPreciseWheelRotation() > 0) {
1094 graphComponent.zoomOut();
1105 super.mouseClicked(event);
1106 if (SwingUtilities.isRightMouseButton(event)) {
1107 final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(),
event.getY());
1108 if (cellAt != null && cellAt.isVertex()) {
1109 final JPopupMenu jPopupMenu =
new JPopupMenu();
1110 final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
1112 Set<mxCell> selectedVertices
1113 = Stream.of(graph.getSelectionModel().getCells())
1114 .map(mxCell.class::cast)
1115 .filter(mxCell::isVertex)
1116 .collect(Collectors.toSet());
1118 if (lockedVertexModel.isVertexLocked(cellAt)) {
1119 jPopupMenu.add(
new JMenuItem(
new UnlockAction(selectedVertices)));
1121 jPopupMenu.add(
new JMenuItem(
new LockAction(selectedVertices)));
1123 if (pinnedAccountModel.isAccountPinned(adiKey)) {
1124 jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
1126 jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
1127 jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
1129 jPopupMenu.show(graphComponent.getGraphControl(),
event.getX(),
event.getY());
1138 @NbBundle.Messages({
1139 "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
1140 "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
1146 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
1148 this.selectedVertices = selectedVertices;
1154 lockedVertexModel.unlock(selectedVertices);
1161 @NbBundle.Messages({
1162 "VisualizationPanel.lockAction.singularText=Lock Selected Account",
1163 "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
1169 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
1171 this.selectedVertices = selectedVertices;
1176 lockedVertexModel.lock(selectedVertices);
void zoomOutButtonActionPerformed(ActionEvent evt)
final Set< mxCell > selectedVertices
boolean isVertexIgnored(Object vertex)
void invoke(Object sender, mxEventObject evt)
JFXPanel notificationsJFXPanel
void mouseClicked(final MouseEvent event)
void createReport(Case currentCase, String reportName)
CommunicationsManager commsManager
boolean isVertexIgnored(Object vertex)
void applyLayout(NamedGraphLayout layout)
void actionPerformed(final ActionEvent event)
final mxRubberband rubberband
static boolean deleteFileDir(File path)
String getReportDirectory()
JToolBar.Separator jSeparator3
void addReport(String localPath, String srcModuleName, String reportName)
JToolBar.Separator jSeparator2
ModalDialogProgressIndicator progress
mxRectangle setVertexLocation(Object vertex, double x, double y)
JButton hierarchyLayoutButton
synchronized void start(String message, int totalWorkUnits)
JButton organicLayoutButton
final ProxyLookup proxyLookup
synchronized void setCancelling(String cancellingMessage)
final CommunicationsGraph graph
void actionPerformed(final ActionEvent event)
boolean isVertexIgnored(Object vertex)
CommunicationsFilter currentFilter
void fitZoomButtonActionPerformed(ActionEvent evt)
SleuthkitCase getSleuthkitCase()
void mouseWheelMoved(final MouseWheelEvent event)
JButton circleLayoutButton
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)
final Set< mxCell > selectedVertices
void zoomActualButtonActionPerformed(ActionEvent evt)
void clearVizButtonActionPerformed(ActionEvent evt)
void snapshotButtonActionPerformed(ActionEvent evt)
synchronized void finish()