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.GridLayout;
50 import java.awt.event.ActionEvent;
51 import java.awt.event.ActionListener;
52 import java.awt.event.MouseAdapter;
53 import java.awt.event.MouseEvent;
54 import java.awt.event.MouseWheelEvent;
55 import java.awt.image.BufferedImage;
56 import java.beans.PropertyChangeEvent;
57 import java.io.IOException;
58 import java.nio.file.Files;
59 import java.nio.file.Path;
60 import java.nio.file.Paths;
61 import java.text.DecimalFormat;
62 import java.text.SimpleDateFormat;
63 import java.util.Arrays;
64 import java.util.Collections;
65 import java.util.Date;
66 import java.util.EnumSet;
67 import java.util.HashMap;
68 import java.util.HashSet;
71 import java.util.concurrent.ExecutionException;
72 import java.util.concurrent.Future;
73 import java.util.function.BiConsumer;
74 import java.util.logging.Level;
75 import java.util.stream.Collectors;
76 import java.util.stream.Stream;
77 import javafx.application.Platform;
78 import javafx.embed.swing.JFXPanel;
79 import javafx.scene.Scene;
80 import javafx.scene.layout.Pane;
81 import javax.swing.AbstractAction;
82 import javax.swing.ImageIcon;
83 import javax.swing.JButton;
84 import javax.swing.JLabel;
85 import javax.swing.JMenuItem;
86 import javax.swing.JOptionPane;
87 import javax.swing.JPanel;
88 import javax.swing.JPopupMenu;
89 import javax.swing.JSplitPane;
90 import javax.swing.JTextArea;
91 import javax.swing.JTextField;
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.jdesktop.layout.GroupLayout;
99 import org.jdesktop.layout.LayoutStyle;
100 import org.openide.util.NbBundle;
101 import org.openide.windows.WindowManager;
125 @SuppressWarnings(
"PMD.SingularField")
128 private static final long serialVersionUID = 1L;
130 private static final String BASE_IMAGE_PATH =
"/org/sleuthkit/autopsy/communications/images";
131 static final private ImageIcon unlockIcon
133 static final private ImageIcon lockIcon
136 @NbBundle.Messages(
"VisualizationPanel.cancelButton.text=Cancel")
137 private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
145 private final CommunicationsGraph
graph;
147 private final mxUndoManager undoManager =
new mxUndoManager();
151 private SwingWorker<?, ?> worker;
152 private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
153 private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
156 private NamedGraphLayout currentLayout;
160 private final StateManager stateManager;
162 @NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
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 -> zoomLabel.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);
225 splitPane.setRightComponent(relationshipBrowser);
229 final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
230 -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty(
"edit"));
232 graph.getModel().addListener(mxEvent.UNDO, undoListener);
233 graph.getView().addListener(mxEvent.UNDO, undoListener);
238 BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
239 layoutButtons.put(layout, layoutButton);
240 layoutButton.addActionListener(event -> applyLayout(layout));
243 configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
245 applyLayout(fastOrganicLayout);
247 stateManager =
new StateManager(pinnedAccountModel);
249 setStateButtonsEnabled();
253 void handle(LockedVertexModel.VertexLockEvent event) {
254 final Set<mxCell> vertices =
event.getVertices();
255 mxGraphView view = graph.getView();
256 vertices.forEach(vertex -> {
257 final mxCellState state = view.getState(vertex,
true);
258 view.updateLabel(state);
259 view.updateLabelBounds(state);
260 view.updateBoundingBox(state);
261 graphComponent.redraw(state);
266 void handle(
final CVTEvents.UnpinAccountsEvent pinEvent) {
267 graph.getModel().beginUpdate();
268 pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
272 graph.getModel().endUpdate();
274 setStateButtonsEnabled();
278 void handle(
final CVTEvents.PinAccountsEvent pinEvent) {
279 graph.getModel().beginUpdate();
280 if (pinEvent.isReplace()) {
283 pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances());
286 graph.getModel().endUpdate();
288 setStateButtonsEnabled();
292 void handle(
final CVTEvents.FilterChangeEvent filterChangeEvent) {
293 graph.getModel().beginUpdate();
295 currentFilter = filterChangeEvent.getNewFilter();
298 graph.getModel().endUpdate();
300 setStateButtonsEnabled();
303 @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
304 private
void rebuildGraph() {
305 if (pinnedAccountModel.isEmpty()) {
306 borderLayoutPanel.remove(graphComponent);
307 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
310 borderLayoutPanel.remove(placeHolderPanel);
311 borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
312 if (worker != null) {
318 worker = graph.rebuild(progress, commsManager, currentFilter);
319 cancelationListener.configure(worker, progress);
320 worker.addPropertyChangeListener((
final PropertyChangeEvent evt) -> {
321 if (worker.isDone()) {
322 if (worker.isCancelled()) {
326 applyLayout(currentLayout);
337 windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class,
this);
341 }
catch (TskCoreException ex) {
342 logger.log(Level.SEVERE,
"Error getting CommunicationsManager for the current case.", ex);
344 logger.log(Level.SEVERE,
"Can't get CommunicationsManager when there is no case open.", ex);
348 graph.getModel().beginUpdate();
352 graph.getModel().endUpdate();
354 if (evt.getNewValue() == null) {
357 Case currentCase = (
Case) evt.getNewValue();
360 }
catch (TskCoreException ex) {
361 logger.log(Level.SEVERE,
"Error getting CommunicationsManager for the current case.", ex);
372 @SuppressWarnings(
"unchecked")
374 private
void initComponents() {
376 splitPane =
new javax.swing.JSplitPane();
377 borderLayoutPanel =
new javax.swing.JPanel();
378 placeHolderPanel =
new javax.swing.JPanel();
379 jTextArea1 =
new javax.swing.JTextArea();
380 toolbar =
new javax.swing.JPanel();
381 fastOrganicLayoutButton =
new javax.swing.JButton();
382 zoomOutButton =
new javax.swing.JButton();
383 zoomInButton =
new javax.swing.JButton();
384 zoomActualButton =
new javax.swing.JButton();
385 fitZoomButton =
new javax.swing.JButton();
386 jLabel2 =
new javax.swing.JLabel();
387 zoomLabel =
new javax.swing.JLabel();
388 clearVizButton =
new javax.swing.JButton();
389 jSeparator2 =
new javax.swing.JToolBar.Separator();
390 backButton =
new javax.swing.JButton();
391 forwardButton =
new javax.swing.JButton();
392 snapshotButton =
new javax.swing.JButton();
393 jSeparator3 =
new javax.swing.JToolBar.Separator();
394 jSeparator4 =
new javax.swing.JToolBar.Separator();
395 notificationsJFXPanel =
new javafx.embed.swing.JFXPanel();
397 setLayout(
new java.awt.BorderLayout());
399 splitPane.setDividerLocation(800);
400 splitPane.setResizeWeight(0.5);
402 borderLayoutPanel.setLayout(
new java.awt.BorderLayout());
404 jTextArea1.setBackground(
new java.awt.Color(240, 240, 240));
405 jTextArea1.setColumns(20);
406 jTextArea1.setLineWrap(
true);
407 jTextArea1.setRows(5);
408 jTextArea1.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.jTextArea1.text"));
410 org.jdesktop.layout.GroupLayout placeHolderPanelLayout =
new org.jdesktop.layout.GroupLayout(placeHolderPanel);
411 placeHolderPanel.setLayout(placeHolderPanelLayout);
412 placeHolderPanelLayout.setHorizontalGroup(
413 placeHolderPanelLayout.createParallelGroup(
org.jdesktop.layout.GroupLayout.LEADING)
414 .add(placeHolderPanelLayout.createSequentialGroup()
415 .addContainerGap(250, Short.MAX_VALUE)
416 .add(jTextArea1,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 424,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
417 .addContainerGap(423, Short.MAX_VALUE))
419 placeHolderPanelLayout.setVerticalGroup(
420 placeHolderPanelLayout.createParallelGroup(
org.jdesktop.layout.GroupLayout.LEADING)
421 .add(placeHolderPanelLayout.createSequentialGroup()
422 .addContainerGap(
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
423 .add(jTextArea1,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 47,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
424 .addContainerGap(
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
427 borderLayoutPanel.add(placeHolderPanel, java.awt.BorderLayout.CENTER);
429 fastOrganicLayoutButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png")));
430 fastOrganicLayoutButton.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.text"));
431 fastOrganicLayoutButton.setToolTipText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fastOrganicLayoutButton.toolTipText"));
432 fastOrganicLayoutButton.setFocusable(
false);
433 fastOrganicLayoutButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
435 zoomOutButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png")));
436 zoomOutButton.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.text"));
437 zoomOutButton.setToolTipText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomOutButton.toolTipText"));
438 zoomOutButton.setFocusable(
false);
439 zoomOutButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
440 zoomOutButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
441 zoomOutButton.addActionListener(
new java.awt.event.ActionListener() {
442 public void actionPerformed(java.awt.event.ActionEvent evt) {
443 zoomOutButtonActionPerformed(evt);
447 zoomInButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png")));
448 zoomInButton.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.text"));
449 zoomInButton.setToolTipText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomInButton.toolTipText"));
450 zoomInButton.setFocusable(
false);
451 zoomInButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
452 zoomInButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
453 zoomInButton.addActionListener(
new java.awt.event.ActionListener() {
454 public void actionPerformed(java.awt.event.ActionEvent evt) {
455 zoomInButtonActionPerformed(evt);
459 zoomActualButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png")));
460 zoomActualButton.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.text"));
461 zoomActualButton.setToolTipText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomActualButton.toolTipText"));
462 zoomActualButton.setFocusable(
false);
463 zoomActualButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
464 zoomActualButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
465 zoomActualButton.addActionListener(
new java.awt.event.ActionListener() {
466 public void actionPerformed(java.awt.event.ActionEvent evt) {
467 zoomActualButtonActionPerformed(evt);
471 fitZoomButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png")));
472 fitZoomButton.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.text"));
473 fitZoomButton.setToolTipText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.fitZoomButton.toolTipText"));
474 fitZoomButton.setFocusable(
false);
475 fitZoomButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
476 fitZoomButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
477 fitZoomButton.addActionListener(
new java.awt.event.ActionListener() {
478 public void actionPerformed(java.awt.event.ActionEvent evt) {
479 fitZoomButtonActionPerformed(evt);
483 jLabel2.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.jLabel2.text"));
485 zoomLabel.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.zoomLabel.text"));
487 clearVizButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/communications/images/broom.png")));
488 clearVizButton.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.text_1"));
489 clearVizButton.setToolTipText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.toolTipText"));
490 clearVizButton.setActionCommand(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.clearVizButton.actionCommand"));
491 clearVizButton.addActionListener(
new java.awt.event.ActionListener() {
492 public void actionPerformed(java.awt.event.ActionEvent evt) {
493 clearVizButtonActionPerformed(evt);
497 jSeparator2.setOrientation(javax.swing.SwingConstants.VERTICAL);
499 backButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_previous.png")));
500 backButton.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.backButton.text_1"));
501 backButton.setToolTipText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.backButton.toolTipText"));
502 backButton.addActionListener(
new java.awt.event.ActionListener() {
503 public void actionPerformed(java.awt.event.ActionEvent evt) {
504 backButtonActionPerformed(evt);
508 forwardButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/resultset_next.png")));
509 forwardButton.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.forwardButton.text"));
510 forwardButton.setToolTipText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.forwardButton.toolTipText"));
511 forwardButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
512 forwardButton.addActionListener(
new java.awt.event.ActionListener() {
513 public void actionPerformed(java.awt.event.ActionEvent evt) {
514 forwardButtonActionPerformed(evt);
518 snapshotButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/report/images/image.png")));
519 snapshotButton.setText(
org.openide.util.NbBundle.getMessage(
VisualizationPanel.class,
"VisualizationPanel.snapshotButton.text_1"));
520 snapshotButton.addActionListener(
new java.awt.event.ActionListener() {
521 public void actionPerformed(java.awt.event.ActionEvent evt) {
522 snapshotButtonActionPerformed(evt);
526 jSeparator3.setOrientation(javax.swing.SwingConstants.VERTICAL);
528 jSeparator4.setOrientation(javax.swing.SwingConstants.VERTICAL);
530 org.jdesktop.layout.GroupLayout toolbarLayout =
new org.jdesktop.layout.GroupLayout(toolbar);
531 toolbar.setLayout(toolbarLayout);
532 toolbarLayout.setHorizontalGroup(
533 toolbarLayout.createParallelGroup(
org.jdesktop.layout.GroupLayout.LEADING)
534 .add(toolbarLayout.createSequentialGroup()
537 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
539 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
540 .add(jSeparator4,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
541 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
542 .add(fastOrganicLayoutButton)
543 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
545 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
546 .add(jSeparator2,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
547 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
549 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
551 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
552 .add(zoomOutButton,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 32,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
553 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
554 .add(zoomInButton,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 32,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
555 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
556 .add(zoomActualButton,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 33,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
557 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
558 .add(fitZoomButton,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 32,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
559 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
560 .add(jSeparator3,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
561 .addPreferredGap(
org.jdesktop.layout.LayoutStyle.RELATED)
563 .addContainerGap(
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
565 toolbarLayout.setVerticalGroup(
566 toolbarLayout.createParallelGroup(
org.jdesktop.layout.GroupLayout.LEADING)
567 .add(toolbarLayout.createSequentialGroup()
569 .add(toolbarLayout.createParallelGroup(
org.jdesktop.layout.GroupLayout.CENTER)
570 .add(fastOrganicLayoutButton)
572 .add(zoomInButton,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
573 .add(zoomActualButton,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
574 .add(fitZoomButton,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
578 .add(jSeparator2,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
582 .add(jSeparator3,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
583 .add(jSeparator4,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
587 borderLayoutPanel.add(toolbar, java.awt.BorderLayout.PAGE_START);
588 borderLayoutPanel.add(notificationsJFXPanel, java.awt.BorderLayout.PAGE_END);
590 splitPane.setLeftComponent(borderLayoutPanel);
592 add(splitPane, java.awt.BorderLayout.CENTER);
600 graphComponent.zoomActual();
601 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
605 graphComponent.zoomIn();
606 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
610 graphComponent.zoomOut();
611 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
620 @NbBundle.Messages({
"VisualizationPanel.computingLayout=Computing Layout",
621 "# {0} - layout name",
622 "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
623 "# {0} - layout name",
624 "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
626 currentLayout = layout;
627 layoutButtons.forEach((layoutKey, button)
628 -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
631 progressIndicator.
start(Bundle.VisualizationPanel_computingLayout());
633 new SwingWorker<Void, Void>() {
635 protected Void doInBackground() {
636 graph.getModel().beginUpdate();
638 layout.execute(graph.getDefaultParent());
641 graph.getModel().endUpdate();
642 progressIndicator.
finish();
648 protected void done() {
651 }
catch (InterruptedException | ExecutionException ex) {
652 logger.log(Level.WARNING,
"CVT graph layout failed.", ex);
659 CVTEvents.getCVTEventBus().post(
new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts()));
663 handleStateChange(stateManager.advance());
667 handleStateChange(stateManager.retreat());
676 if(newState == null) {
681 if(newState.isZoomChange()) {
682 graph.getView().setScale(newState.getZoomValue());
687 CVTEvents.getCVTEventBus().post(
new CVTEvents.StateChangeEvent(newState));
688 setStateButtonsEnabled();
690 graph.getModel().beginUpdate();
693 if(newState.getPinnedList() != null) {
694 pinnedAccountModel.pinAccount(newState.getPinnedList());
696 pinnedAccountModel.clear();
699 currentFilter = newState.getCommunicationsFilter();
703 graph.getModel().endUpdate();
710 backButton.setEnabled(stateManager.canRetreat());
711 forwardButton.setEnabled(stateManager.canAdvance());
715 "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation."
719 handleSnapshotEvent();
721 logger.log(Level.SEVERE,
"Unable to create communications snapsot report", ex);
724 -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
725 .text(Bundle.VisualizationPanel_snapshot_report_failure())
727 }
catch( TskCoreException ex) {
728 logger.log(Level.WARNING,
"Unable to add report to currenct case", ex);
733 graphComponent.zoomTo(1,
true);
734 mxPoint translate = graph.getView().getTranslate();
735 if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
736 translate =
new mxPoint();
739 mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
740 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
741 boundsForCells =
new mxRectangle(0, 0, 1, 1);
743 final mxPoint mxPoint =
new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
745 graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(),
false,
false);
747 boundsForCells = graph.getCellBounds(graph.getDefaultParent(),
true,
true,
true);
748 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
749 boundsForCells =
new mxRectangle(0, 0, 1, 1);
752 final Dimension size = graphComponent.getSize();
753 final double widthFactor = size.getWidth() / boundsForCells.getWidth();
754 final double heightFactor = size.getHeight() / boundsForCells.getHeight();
756 graphComponent.zoom((heightFactor + widthFactor) / 2.0);
766 "VisualizationPanel_action_dialogs_title=Communications",
767 "VisualizationPanel_module_name=Communications",
768 "VisualizationPanel_action_name_text=Snapshot Report",
769 "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
770 "VisualizationPane_reportName=Communications Snapshot",
771 "# {0} - default name",
772 "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
773 "VisualizationPane_blank_report_title=Blank Report Name",
774 "# {0} - report name",
775 "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}"
779 Date generationDate =
new Date();
783 final JTextField text =
new JTextField(50);
784 final JPanel panel =
new JPanel(
new GridLayout(2, 1));
785 panel.add(
new JLabel(Bundle.VisualizationPane_fileName_prompt()));
788 text.setText(defaultReportName);
790 int result = JOptionPane.showConfirmDialog(graphComponent, panel,
791 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
793 if (result == JOptionPane.OK_OPTION) {
794 String enteredReportName = text.getText();
796 if(enteredReportName.trim().isEmpty()){
797 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
798 if(result != JOptionPane.OK_OPTION) {
803 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
805 if (Files.exists(reportPath)) {
806 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
807 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
809 if (result == JOptionPane.OK_OPTION) {
811 createReport(currentCase, reportName);
814 createReport(currentCase, reportName);
815 currentCase.
addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
830 "VisualizationPane_DisplayName=Open Report",
831 "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
832 "VisualizationPane_MessageBoxTitle=Open Report Failure",
833 "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
834 "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
835 "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
836 "# {0} - report path",
837 "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
838 "VisualizationPane_Report_OK_Button=OK",
839 "VisualizationPane_Open_Report=Open Report",})
843 Path reportFolderPath = Paths.get(currentCase.
getReportDirectory(), reportName, Bundle.VisualizationPane_reportName());
844 BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE,
true, null);
848 String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
849 String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
851 int result = JOptionPane.showOptionDialog(graphComponent, message,
852 Bundle.VisualizationPanel_action_dialogs_title(),
853 JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
854 null, buttons, buttons[1]);
855 if (result == JOptionPane.YES_NO_OPTION) {
857 Desktop.getDesktop().open(reportPath.toFile());
858 }
catch (IOException ex) {
859 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
860 Bundle.VisualizationPane_NoAssociatedEditorMessage(),
861 Bundle.VisualizationPane_MessageBoxTitle(),
862 JOptionPane.ERROR_MESSAGE);
863 }
catch (UnsupportedOperationException ex) {
864 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
865 Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
866 Bundle.VisualizationPane_MessageBoxTitle(),
867 JOptionPane.ERROR_MESSAGE);
868 }
catch (IllegalArgumentException ex) {
869 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
870 Bundle.VisualizationPane_MissingReportFileMessage(),
871 Bundle.VisualizationPane_MessageBoxTitle(),
872 JOptionPane.ERROR_MESSAGE);
873 }
catch (SecurityException ex) {
874 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
875 Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
876 Bundle.VisualizationPane_MessageBoxTitle(),
877 JOptionPane.ERROR_MESSAGE);
911 @SuppressWarnings(
"unchecked")
913 public void invoke(Object sender, mxEventObject evt) {
914 Object[] selectionCells = graph.getSelectionCells();
915 if (selectionCells.length > 0) {
916 mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(
new mxCell[selectionCells.length]);
917 HashSet<AccountDeviceInstance> deviceInstances =
new HashSet<>();
918 for (mxICell cell : selectedCells) {
920 mxICell source = (mxICell) graph.getModel().getTerminal(cell,
true);
921 mxICell target = (mxICell) graph.getModel().getTerminal(cell,
false);
923 deviceInstances.add(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance());
924 deviceInstances.add(((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance());
926 }
else if (cell.isVertex()) {
927 deviceInstances.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
931 relationshipBrowser.setSelectionInfo(
new SelectionInfo(deviceInstances, currentFilter));
933 relationshipBrowser.setSelectionInfo(
new SelectionInfo(Collections.EMPTY_SET, currentFilter));
943 String getDisplayName();
957 return super.isVertexIgnored(vertex)
958 || lockedVertexModel.isVertexLocked((mxCell) vertex);
963 if (isVertexIgnored(vertex)) {
964 return getVertexBounds(vertex);
966 return super.setVertexLocation(vertex, x, y);
972 return "Fast Organic";
988 return super.isVertexIgnored(vertex)
989 || lockedVertexModel.isVertexLocked((mxCell) vertex);
994 if (isVertexIgnored(vertex)) {
995 return getVertexBounds(vertex);
997 return super.setVertexLocation(vertex, x, y);
1014 setResetEdges(
true);
1019 return super.isVertexIgnored(vertex)
1020 || lockedVertexModel.isVertexLocked((mxCell) vertex);
1025 if (isVertexIgnored(vertex)) {
1026 return getVertexBounds(vertex);
1028 return super.setVertexLocation(vertex, x, y);
1049 return super.isVertexIgnored(vertex)
1050 || lockedVertexModel.isVertexLocked((mxCell) vertex);
1055 if (isVertexIgnored(vertex)) {
1056 return getVertexBounds(vertex);
1058 return super.setVertexLocation(vertex, x, y);
1064 return "Hierarchical";
1078 this.cancellable = cancellable;
1079 this.progress = progress;
1085 cancellable.cancel(
true);
1103 super.mouseWheelMoved(event);
1104 if (event.getPreciseWheelRotation() < 0) {
1105 graphComponent.zoomIn();
1106 }
else if (event.getPreciseWheelRotation() > 0) {
1107 graphComponent.zoomOut();
1110 CVTEvents.getCVTEventBus().post(
new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
1120 super.mouseClicked(event);
1121 if (SwingUtilities.isRightMouseButton(event)) {
1122 final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(),
event.getY());
1123 if (cellAt != null && cellAt.isVertex()) {
1124 final JPopupMenu jPopupMenu =
new JPopupMenu();
1125 final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
1127 Set<mxCell> selectedVertices
1128 = Stream.of(graph.getSelectionModel().getCells())
1129 .map(mxCell.class::cast)
1130 .filter(mxCell::isVertex)
1131 .collect(Collectors.toSet());
1133 if (lockedVertexModel.isVertexLocked(cellAt)) {
1134 jPopupMenu.add(
new JMenuItem(
new UnlockAction(selectedVertices)));
1136 jPopupMenu.add(
new JMenuItem(
new LockAction(selectedVertices)));
1138 if (pinnedAccountModel.isAccountPinned(adiKey)) {
1139 jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
1141 jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
1142 jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
1144 jPopupMenu.show(graphComponent.getGraphControl(),
event.getX(),
event.getY());
1153 @NbBundle.Messages({
1154 "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
1155 "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
1161 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
1163 this.selectedVertices = selectedVertices;
1169 lockedVertexModel.unlock(selectedVertices);
1176 @NbBundle.Messages({
1177 "VisualizationPanel.lockAction.singularText=Lock Selected Account",
1178 "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
1184 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
1186 this.selectedVertices = selectedVertices;
1191 lockedVertexModel.lock(selectedVertices);
void zoomOutButtonActionPerformed(ActionEvent evt)
final Set< mxCell > selectedVertices
boolean isVertexIgnored(Object vertex)
void invoke(Object sender, mxEventObject evt)
void forwardButtonActionPerformed(ActionEvent evt)
void mouseClicked(final MouseEvent event)
javax.swing.JButton zoomOutButton
void createReport(Case currentCase, String reportName)
javax.swing.JLabel jLabel2
javax.swing.JButton fitZoomButton
javax.swing.JButton snapshotButton
CommunicationsManager commsManager
boolean isVertexIgnored(Object vertex)
void handleStateChange(StateManager.CommunicationsState newState)
void applyLayout(NamedGraphLayout layout)
void actionPerformed(final ActionEvent event)
final mxRubberband rubberband
javax.swing.JToolBar.Separator jSeparator4
javax.swing.JButton zoomActualButton
static boolean deleteFileDir(File path)
String getReportDirectory()
void addReport(String localPath, String srcModuleName, String reportName)
javax.swing.JButton forwardButton
ModalDialogProgressIndicator progress
mxRectangle setVertexLocation(Object vertex, double x, double y)
synchronized void start(String message, int totalWorkUnits)
javax.swing.JPanel borderLayoutPanel
javax.swing.JButton clearVizButton
javax.swing.JButton fastOrganicLayoutButton
synchronized void setCancelling(String cancellingMessage)
javax.swing.JButton backButton
final CommunicationsGraph graph
void actionPerformed(final ActionEvent event)
boolean isVertexIgnored(Object vertex)
CommunicationsFilter currentFilter
void backButtonActionPerformed(ActionEvent evt)
javax.swing.JLabel zoomLabel
javax.swing.JToolBar.Separator jSeparator3
void fitZoomButtonActionPerformed(ActionEvent evt)
javax.swing.JPanel placeHolderPanel
SleuthkitCase getSleuthkitCase()
void mouseWheelMoved(final MouseWheelEvent event)
javax.swing.JSplitPane splitPane
void handleSnapshotEvent()
static String escapeFileName(String fileName)
mxRectangle setVertexLocation(Object vertex, double x, double y)
boolean isVertexIgnored(Object vertex)
javax.swing.JTextArea jTextArea1
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)
javafx.embed.swing.JFXPanel notificationsJFXPanel
javax.swing.JButton zoomInButton
mxRectangle setVertexLocation(Object vertex, double x, double y)
javax.swing.JToolBar.Separator jSeparator2
void zoomInButtonActionPerformed(ActionEvent evt)
void setStateButtonsEnabled()
javax.swing.JPanel toolbar
final Set< mxCell > selectedVertices
void zoomActualButtonActionPerformed(ActionEvent evt)
void clearVizButtonActionPerformed(ActionEvent evt)
void snapshotButtonActionPerformed(ActionEvent evt)
synchronized void finish()