Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
VisualizationPanel.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2017-2021 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.autopsy.communications;
20
21import com.google.common.eventbus.Subscribe;
22import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
23import com.mxgraph.layout.mxCircleLayout;
24import com.mxgraph.layout.mxFastOrganicLayout;
25import com.mxgraph.layout.mxIGraphLayout;
26import com.mxgraph.layout.mxOrganicLayout;
27import com.mxgraph.model.mxCell;
28import com.mxgraph.model.mxICell;
29import com.mxgraph.swing.handler.mxRubberband;
30import com.mxgraph.swing.mxGraphComponent;
31import com.mxgraph.util.mxCellRenderer;
32import com.mxgraph.util.mxEvent;
33import com.mxgraph.util.mxEventObject;
34import com.mxgraph.util.mxEventSource;
35import com.mxgraph.util.mxPoint;
36import com.mxgraph.util.mxRectangle;
37import com.mxgraph.util.mxUndoManager;
38import com.mxgraph.util.mxUndoableEdit;
39import com.mxgraph.view.mxCellState;
40import com.mxgraph.view.mxGraph;
41import com.mxgraph.view.mxGraphView;
42import java.awt.BorderLayout;
43import java.awt.Color;
44import org.sleuthkit.autopsy.coreutils.Desktop;
45import java.awt.Dimension;
46import java.awt.Font;
47import java.awt.Frame;
48import java.awt.Graphics;
49import java.awt.GridBagConstraints;
50import java.awt.GridBagLayout;
51import java.awt.GridLayout;
52import java.awt.Insets;
53import java.awt.event.ActionEvent;
54import java.awt.event.ActionListener;
55import java.awt.event.MouseAdapter;
56import java.awt.event.MouseEvent;
57import java.awt.event.MouseWheelEvent;
58import java.awt.image.BufferedImage;
59import java.beans.PropertyChangeEvent;
60import java.io.IOException;
61import java.nio.file.Files;
62import java.nio.file.Path;
63import java.nio.file.Paths;
64import java.text.DecimalFormat;
65import java.text.SimpleDateFormat;
66import java.util.Arrays;
67import java.util.Date;
68import java.util.EnumSet;
69import java.util.HashMap;
70import java.util.HashSet;
71import java.util.Map;
72import java.util.Set;
73import java.util.concurrent.Future;
74import java.util.function.BiConsumer;
75import java.util.logging.Level;
76import java.util.stream.Collectors;
77import java.util.stream.Stream;
78import javafx.application.Platform;
79import javafx.embed.swing.JFXPanel;
80import javafx.scene.Scene;
81import javafx.scene.layout.Pane;
82import javax.swing.AbstractAction;
83import javax.swing.ImageIcon;
84import javax.swing.JButton;
85import javax.swing.JLabel;
86import javax.swing.JMenuItem;
87import javax.swing.JOptionPane;
88import javax.swing.JPanel;
89import javax.swing.JPopupMenu;
90import javax.swing.JTextField;
91import javax.swing.JTextPane;
92import javax.swing.JToolBar;
93import javax.swing.SwingConstants;
94import javax.swing.SwingUtilities;
95import javax.swing.SwingWorker;
96import org.apache.commons.lang3.StringUtils;
97import org.controlsfx.control.Notifications;
98import org.openide.util.NbBundle;
99import org.openide.windows.WindowManager;
100import org.sleuthkit.autopsy.casemodule.Case;
101import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
102import org.sleuthkit.autopsy.communications.relationships.RelationshipBrowser;
103import org.sleuthkit.autopsy.communications.relationships.SelectionInfo;
104import org.sleuthkit.autopsy.communications.snapshot.CommSnapShotReportWriter;
105import org.sleuthkit.autopsy.coreutils.FileUtil;
106import org.sleuthkit.autopsy.coreutils.Logger;
107import org.sleuthkit.autopsy.coreutils.ThreadConfined;
108import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
109import org.sleuthkit.datamodel.AccountDeviceInstance;
110import org.sleuthkit.datamodel.CommunicationsFilter;
111import org.sleuthkit.datamodel.CommunicationsManager;
112import org.sleuthkit.datamodel.TskCoreException;
113import org.sleuthkit.autopsy.guiutils.WrapLayout;
124@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
125final public class VisualizationPanel extends JPanel {
126
127 private static final long serialVersionUID = 1L;
128 private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName());
129 private static final String BASE_IMAGE_PATH = "/org/sleuthkit/autopsy/communications/images";
130 static final private ImageIcon unlockIcon
131 = new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_unlocked.png"));
132 static final private ImageIcon lockIcon
133 = new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_locked.png"));
134
135 @NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel")
136 private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
137
138 private Frame windowAncestor;
139
140 private CommunicationsManager commsManager;
141 private CommunicationsFilter currentFilter;
142
143 private final mxGraphComponent graphComponent;
144 private final CommunicationsGraph graph;
145
146 private final mxUndoManager undoManager = new mxUndoManager();
147 private final mxRubberband rubberband; //NOPMD We keep a referenec as insurance to prevent garbage collection
148
150 private SwingWorker<?, ?> worker;
151 private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
152 private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
153
154 private final Map<NamedGraphLayout, JButton> layoutButtons = new HashMap<>();
156
158
159 private final StateManager stateManager;
160
161 @NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
163 this.relationshipBrowser = relationshipBrowser;
165 //initialize invisible JFXPanel that is used to show JFXNotifications over this window.
166 Platform.runLater(() -> {
167 notificationsJFXPanel.setScene(new Scene(new Pane()));
168 });
169
170 graph = new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
171
172 /*
173 * custom implementation of mxGraphComponent that uses... a custom
174 * implementation of mxGraphControl ... that overrides paint so we can
175 * catch the NPEs we are getting and deal with them. For now that means
176 * just ignoring them.
177 */
178 graphComponent = new mxGraphComponent(graph) {
179 @Override
180 protected mxGraphComponent.mxGraphControl createGraphControl() {
181
182 return new mxGraphControl() {
183
184 @Override
185 public void paint(Graphics graphics) {
186 try {
187 super.paint(graphics);
188 } catch (NullPointerException ex) { //NOPMD
189 /* We can't find the underlying cause of the NPE in
190 * jgraphx, but it doesn't seem to cause any
191 * noticeable problems, so we are just logging it
192 * and moving on.
193 */
194 logger.log(Level.WARNING, "There was a NPE while painting the VisualizationPanel", ex);
195 }
196 }
197
198 };
199 }
200 };
201 graphComponent.setAutoExtend(true);
202 graphComponent.setAutoScroll(true);
203 graphComponent.setAutoscrolls(true);
204 graphComponent.setConnectable(false);
205 graphComponent.setDragEnabled(false);
206 graphComponent.setKeepSelectionVisibleOnZoom(true);
207 graphComponent.setOpaque(true);
208 graphComponent.setToolTips(true);
209 graphComponent.setBackground(Color.WHITE);
210 borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
211
212 //install rubber band other handlers
213 rubberband = new mxRubberband(graphComponent);
214
215 lockedVertexModel.registerhandler(this);
216
217 final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
218 -> zoomPercentLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
219 graph.getView().addListener(mxEvent.SCALE, scaleListener);
220 graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
221
222 final GraphMouseListener graphMouseListener = new GraphMouseListener();
223 graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
224 graphComponent.getGraphControl().addMouseListener(graphMouseListener);
225
226 //feed selection to explorermanager
227 graph.getSelectionModel().addListener(mxEvent.CHANGE, new SelectionListener());
228 final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
229 -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty("edit"));
230
231 graph.getModel().addListener(mxEvent.UNDO, undoListener);
232 graph.getView().addListener(mxEvent.UNDO, undoListener);
233
234 FastOrganicLayoutImpl fastOrganicLayout = new FastOrganicLayoutImpl(graph);
235
236 //local method to configure layout buttons
237 BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
238 layoutButtons.put(layout, layoutButton);
239 layoutButton.addActionListener(event -> applyLayout(layout));
240 };
241 //configure layout buttons.
242 configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
243
244 applyLayout(fastOrganicLayout);
245
246 stateManager = new StateManager(pinnedAccountModel);
247
249
250 toolbar.setLayout(new WrapLayout());
251 }
252
253 @Subscribe
254 void handle(LockedVertexModel.VertexLockEvent event) {
255 final Set<mxCell> vertices = event.getVertices();
256 mxGraphView view = graph.getView();
257 vertices.forEach(vertex -> {
258 final mxCellState state = view.getState(vertex, true);
259 view.updateLabel(state);
260 view.updateLabelBounds(state);
261 view.updateBoundingBox(state);
262 graphComponent.redraw(state);
263 });
264 }
265
266 @Subscribe
267 void handle(final CVTEvents.UnpinAccountsEvent pinEvent) {
268 graph.getModel().beginUpdate();
269 pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
270 graph.clear();
271 rebuildGraph();
272 // Updates the display
273 graph.getModel().endUpdate();
274
275 setStateButtonsEnabled();
276 }
277
278 @Subscribe
279 void handle(final CVTEvents.PinAccountsEvent pinEvent) {
280 graph.getModel().beginUpdate();
281 if (pinEvent.isReplace()) {
282 graph.resetGraph();
283 }
284 pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances());
285 rebuildGraph();
286 // Updates the display
287 graph.getModel().endUpdate();
288
289 setStateButtonsEnabled();
290 }
291
292 @Subscribe
293 void handle(final CVTEvents.FilterChangeEvent filterChangeEvent) {
294 graph.getModel().beginUpdate();
295 graph.clear();
296 currentFilter = filterChangeEvent.getNewFilter();
297 rebuildGraph();
298 // Updates the display
299 graph.getModel().endUpdate();
300
301 setStateButtonsEnabled();
302 }
303
305 private void rebuildGraph() {
306 if (pinnedAccountModel.isEmpty()) {
307 borderLayoutPanel.remove(graphComponent);
308 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
309 repaint();
310 } else {
311 borderLayoutPanel.remove(placeHolderPanel);
312 borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
313 if (worker != null) {
314 worker.cancel(true);
315 }
316
317 final CancelationListener cancelationListener = new CancelationListener();
318 final ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Loading Visualization", new String[]{CANCEL}, CANCEL, cancelationListener);
320 cancelationListener.configure(worker, progress);
321 worker.addPropertyChangeListener((final PropertyChangeEvent evt) -> {
322 if (worker.isDone()) {
323 if (worker.isCancelled()) {
324 graph.resetGraph();
325 rebuildGraph();
326 }
328 }
329 });
330
331 worker.execute();
332 }
333 }
334
335 @Override
336 public void addNotify() {
337 super.addNotify();
338 windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, this);
339
340 try {
341 commsManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
342 } catch (TskCoreException ex) {
343 logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); //NON-NLS
344 } catch (NoCurrentCaseException ex) {
345 logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex); //NON-NLS
346 }
347
349 graph.getModel().beginUpdate();
350 try {
351 graph.resetGraph();
352 } finally {
353 graph.getModel().endUpdate();
354 }
355 if (evt.getNewValue() == null) {
356 commsManager = null;
357 } else {
358 Case currentCase = (Case) evt.getNewValue();
359 try {
360 commsManager = currentCase.getSleuthkitCase().getCommunicationsManager();
361 } catch (TskCoreException ex) {
362 logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); //NON-NLS
363 }
364 }
365 });
366 }
367
373 @SuppressWarnings("unchecked")
374 // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
375 private void initComponents() {
376 GridBagConstraints gridBagConstraints;
377
378 borderLayoutPanel = new JPanel();
379 placeHolderPanel = new JPanel();
380 jTextPane1 = new JTextPane();
381 notificationsJFXPanel = new JFXPanel();
382 toolbar = new JToolBar();
383 backButton = new JButton();
384 forwardButton = new JButton();
385 jSeparator3 = new JToolBar.Separator();
386 clearVizButton = new JButton();
387 fastOrganicLayoutButton = new JButton();
388 jSeparator2 = new JToolBar.Separator();
389 zoomLabel = new JLabel();
390 zoomPercentLabel = new JLabel();
391 zoomOutButton = new JButton();
392 fitZoomButton = new JButton();
393 zoomActualButton = new JButton();
394 zoomInButton = new JButton();
395 jSeparator1 = new JToolBar.Separator();
396 snapshotButton = new JButton();
397
398 setLayout(new BorderLayout());
399
400 borderLayoutPanel.setLayout(new BorderLayout());
401
402 placeHolderPanel.setLayout(new GridBagLayout());
403
404 jTextPane1.setEditable(false);
405 jTextPane1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextPane1.text")); // NOI18N
406 jTextPane1.setOpaque(false);
407 gridBagConstraints = new GridBagConstraints();
408 gridBagConstraints.anchor = GridBagConstraints.NORTH;
409 gridBagConstraints.weighty = 1.0;
410 gridBagConstraints.insets = new Insets(50, 0, 0, 0);
411 placeHolderPanel.add(jTextPane1, gridBagConstraints);
412
413 borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
414 borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
415
416 add(borderLayoutPanel, BorderLayout.CENTER);
417
418 toolbar.setRollover(true);
419
420 backButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_previous.png"))); // NOI18N
421 backButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.text_1")); // NOI18N
422 backButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.toolTipText")); // NOI18N
423 backButton.setFocusable(false);
424 backButton.setHorizontalTextPosition(SwingConstants.CENTER);
425 backButton.setVerticalTextPosition(SwingConstants.BOTTOM);
426 backButton.addActionListener(new ActionListener() {
427 public void actionPerformed(ActionEvent evt) {
429 }
430 });
431 toolbar.add(backButton);
432
433 forwardButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_next.png"))); // NOI18N
434 forwardButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.text")); // NOI18N
435 forwardButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.toolTipText")); // NOI18N
436 forwardButton.setFocusable(false);
437 forwardButton.setHorizontalTextPosition(SwingConstants.CENTER);
438 forwardButton.setVerticalTextPosition(SwingConstants.BOTTOM);
439 forwardButton.addActionListener(new ActionListener() {
440 public void actionPerformed(ActionEvent evt) {
442 }
443 });
445 toolbar.add(jSeparator3);
446
447 clearVizButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/broom.png"))); // NOI18N
448 clearVizButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.text_1")); // NOI18N
449 clearVizButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.toolTipText")); // NOI18N
450 clearVizButton.setActionCommand(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.actionCommand")); // NOI18N
451 clearVizButton.setFocusable(false);
452 clearVizButton.setHorizontalTextPosition(SwingConstants.CENTER);
453 clearVizButton.setVerticalTextPosition(SwingConstants.BOTTOM);
454 clearVizButton.addActionListener(new ActionListener() {
455 public void actionPerformed(ActionEvent evt) {
457 }
458 });
460
461 fastOrganicLayoutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N
462 fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N
463 fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.toolTipText")); // NOI18N
464 fastOrganicLayoutButton.setFocusable(false);
465 fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
466 fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
467 fastOrganicLayoutButton.addActionListener(new ActionListener() {
468 public void actionPerformed(ActionEvent evt) {
470 }
471 });
473 toolbar.add(jSeparator2);
474
475 zoomLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomLabel.text")); // NOI18N
476 toolbar.add(zoomLabel);
477
478 zoomPercentLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomPercentLabel.text")); // NOI18N
480
481 zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N
482 zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N
483 zoomOutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N
484 zoomOutButton.setFocusable(false);
485 zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
486 zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
487 zoomOutButton.addActionListener(new ActionListener() {
488 public void actionPerformed(ActionEvent evt) {
490 }
491 });
493
494 fitZoomButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N
495 fitZoomButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N
496 fitZoomButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.toolTipText")); // NOI18N
497 fitZoomButton.setFocusable(false);
498 fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
499 fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
500 fitZoomButton.addActionListener(new ActionListener() {
501 public void actionPerformed(ActionEvent evt) {
503 }
504 });
506
507 zoomActualButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N
508 zoomActualButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N
509 zoomActualButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.toolTipText")); // NOI18N
510 zoomActualButton.setFocusable(false);
511 zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
512 zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
513 zoomActualButton.addActionListener(new ActionListener() {
514 public void actionPerformed(ActionEvent evt) {
516 }
517 });
519
520 zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N
521 zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N
522 zoomInButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.toolTipText")); // NOI18N
523 zoomInButton.setFocusable(false);
524 zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
525 zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
526 zoomInButton.addActionListener(new ActionListener() {
527 public void actionPerformed(ActionEvent evt) {
529 }
530 });
532 toolbar.add(jSeparator1);
533
534 snapshotButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/report/images/image.png"))); // NOI18N
535 snapshotButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.text_1")); // NOI18N
536 snapshotButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.toolTipText")); // NOI18N
537 snapshotButton.setFocusable(false);
538 snapshotButton.setHorizontalTextPosition(SwingConstants.CENTER);
539 snapshotButton.setVerticalTextPosition(SwingConstants.BOTTOM);
540 snapshotButton.addActionListener(new ActionListener() {
541 public void actionPerformed(ActionEvent evt) {
543 }
544 });
546
547 add(toolbar, BorderLayout.NORTH);
548 }// </editor-fold>//GEN-END:initComponents
549
550 private void fitZoomButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitZoomButtonActionPerformed
551 fitGraph();
552 }//GEN-LAST:event_fitZoomButtonActionPerformed
553
554 private void zoomActualButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomActualButtonActionPerformed
555 graphComponent.zoomActual();
556 CVTEvents.getCVTEventBus().post(new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
557 }//GEN-LAST:event_zoomActualButtonActionPerformed
558
559 private void zoomInButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
560 graphComponent.zoomIn();
561 CVTEvents.getCVTEventBus().post(new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
562 }//GEN-LAST:event_zoomInButtonActionPerformed
563
564 private void zoomOutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
565 graphComponent.zoomOut();
566 CVTEvents.getCVTEventBus().post(new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
567 }//GEN-LAST:event_zoomOutButtonActionPerformed
568
575 @NbBundle.Messages({"VisualizationPanel.computingLayout=Computing Layout",
576 "# {0} - layout name",
577 "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
578 "# {0} - layout name",
579 "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
580 private void applyLayout(NamedGraphLayout layout) {
581 currentLayout = layout;
582 layoutButtons.forEach((layoutKey, button)
583 -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
584
585 ModalDialogProgressIndicator progressIndicator = new ModalDialogProgressIndicator(windowAncestor, Bundle.VisualizationPanel_computingLayout());
586 progressIndicator.start(Bundle.VisualizationPanel_computingLayout());
587 graph.getModel().beginUpdate();
588 try {
589 layout.execute(graph.getDefaultParent());
590 fitGraph();
591 } finally {
592 graph.getModel().endUpdate();
593 progressIndicator.finish();
594 }
595 }
596
597 private void clearVizButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_clearVizButtonActionPerformed
598 CVTEvents.getCVTEventBus().post(new CVTEvents.UnpinAccountsEvent(pinnedAccountModel.getPinnedAccounts()));
599 }//GEN-LAST:event_clearVizButtonActionPerformed
600
601 private void forwardButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_forwardButtonActionPerformed
603 }//GEN-LAST:event_forwardButtonActionPerformed
604
605 private void backButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
607 }//GEN-LAST:event_backButtonActionPerformed
608
614 private void handleStateChange(StateManager.CommunicationsState newState ){
615 if(newState == null) {
616 return;
617 }
618
619 // If the zoom was changed, only change the zoom.
620 if(newState.isZoomChange()) {
621 graph.getView().setScale(newState.getZoomValue());
622 return;
623 }
624
625 // This will cause the FilterPane to update its controls
626 CVTEvents.getCVTEventBus().post(new CVTEvents.StateChangeEvent(newState));
628
629 graph.getModel().beginUpdate();
630 graph.resetGraph();
631
632 if(newState.getPinnedList() != null) {
633 pinnedAccountModel.pinAccount(newState.getPinnedList());
634 } else {
635 pinnedAccountModel.clear();
636 }
637
638 currentFilter = newState.getCommunicationsFilter();
639
640 rebuildGraph();
641 // Updates the display
642 graph.getModel().endUpdate();
643
644 fitGraph();
645
646 }
647
648 private void setStateButtonsEnabled() {
649 backButton.setEnabled(stateManager.canRetreat());
650 forwardButton.setEnabled(stateManager.canAdvance());
651 }
652
653 @NbBundle.Messages({
654 "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation."
655 })
656 private void snapshotButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_snapshotButtonActionPerformed
657 try {
659 } catch (NoCurrentCaseException | IOException ex) {
660 logger.log(Level.SEVERE, "Unable to create communications snapsot report", ex); //NON-NLS
661
662 Platform.runLater(()
663 -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
664 .text(Bundle.VisualizationPanel_snapshot_report_failure())
665 .showWarning());
666 } catch( TskCoreException ex) {
667 logger.log(Level.WARNING, "Unable to add report to currenct case", ex); //NON-NLS
668 }
669 }//GEN-LAST:event_snapshotButtonActionPerformed
670
671 private void fastOrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fastOrganicLayoutButtonActionPerformed
672 // TODO add your handling code here:
673 }//GEN-LAST:event_fastOrganicLayoutButtonActionPerformed
674
675 private void fitGraph() {
676 graphComponent.zoomTo(1, true);
677 mxPoint translate = graph.getView().getTranslate();
678 if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
679 translate = new mxPoint();
680 }
681
682 mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true);
683 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
684 boundsForCells = new mxRectangle(0, 0, 1, 1);
685 }
686 final mxPoint mxPoint = new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
687
688 graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(), false, false);
689
690 boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true);
691 if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
692 boundsForCells = new mxRectangle(0, 0, 1, 1);
693 }
694
695 final Dimension size = graphComponent.getSize();
696 final double widthFactor = size.getWidth() / boundsForCells.getWidth();
697 final double heightFactor = size.getHeight() / boundsForCells.getHeight();
698
699 graphComponent.zoom((heightFactor + widthFactor) / 2.0);
700 }
701
708 @NbBundle.Messages({
709 "VisualizationPanel_action_dialogs_title=Communications",
710 "VisualizationPanel_module_name=Communications",
711 "VisualizationPanel_action_name_text=Snapshot Report",
712 "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
713 "VisualizationPane_reportName=Communications Snapshot",
714 "# {0} - default name",
715 "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
716 "VisualizationPane_blank_report_title=Blank Report Name",
717 "# {0} - report name",
718 "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}"
719 })
720 private void handleSnapshotEvent() throws NoCurrentCaseException, IOException, TskCoreException {
721 Case currentCase = Case.getCurrentCaseThrows();
722 Date generationDate = new Date();
723
724 final String defaultReportName = FileUtil.escapeFileName(currentCase.getDisplayName() + " " + new SimpleDateFormat("MMddyyyyHHmmss").format(generationDate)); //NON_NLS
725
726 final JTextField text = new JTextField(50);
727 final JPanel panel = new JPanel(new GridLayout(2, 1));
728 panel.add(new JLabel(Bundle.VisualizationPane_fileName_prompt()));
729 panel.add(text);
730
731 text.setText(defaultReportName);
732
733 int result = JOptionPane.showConfirmDialog(graphComponent, panel,
734 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
735
736 if (result == JOptionPane.OK_OPTION) {
737 String enteredReportName = text.getText();
738
739 if(enteredReportName.trim().isEmpty()){
740 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
741 if(result != JOptionPane.OK_OPTION) {
742 return;
743 }
744 }
745
746 String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
747 Path reportPath = Paths.get(currentCase.getReportDirectory(), reportName);
748 if (Files.exists(reportPath)) {
749 result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
750 Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
751
752 if (result == JOptionPane.OK_OPTION) {
753 FileUtil.deleteFileDir(reportPath.toFile());
754 createReport(currentCase, reportName);
755 }
756 } else {
757 createReport(currentCase, reportName);
758 currentCase.addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
759
760 }
761 }
762 }
763
772 @NbBundle.Messages({
773 "VisualizationPane_DisplayName=Open Report",
774 "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
775 "VisualizationPane_MessageBoxTitle=Open Report Failure",
776 "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
777 "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
778 "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
779 "# {0} - report path",
780 "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
781 "VisualizationPane_Report_OK_Button=OK",
782 "VisualizationPane_Open_Report=Open Report",})
783 private void createReport(Case currentCase, String reportName) throws IOException {
784
785 // Create the report.
786 Path reportFolderPath = Paths.get(currentCase.getReportDirectory(), reportName, Bundle.VisualizationPane_reportName()); //NON_NLS
787 BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE, true, null);
788 Path reportPath = new CommSnapShotReportWriter(currentCase, reportFolderPath, reportName, new Date(), image, currentFilter).writeReport();
789
790 // Report success to the user and offer to open the report.
791 String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
792 String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
793
794 int result = JOptionPane.showOptionDialog(graphComponent, message,
795 Bundle.VisualizationPanel_action_dialogs_title(),
796 JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
797 null, buttons, buttons[1]);
798 if (result == JOptionPane.YES_NO_OPTION) {
799 try {
800 Desktop.getDesktop().open(reportPath.toFile());
801 } catch (IOException ex) {
802 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
803 Bundle.VisualizationPane_NoAssociatedEditorMessage(),
804 Bundle.VisualizationPane_MessageBoxTitle(),
805 JOptionPane.ERROR_MESSAGE);
806 } catch (UnsupportedOperationException ex) {
807 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
808 Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
809 Bundle.VisualizationPane_MessageBoxTitle(),
810 JOptionPane.ERROR_MESSAGE);
811 } catch (IllegalArgumentException ex) {
812 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
813 Bundle.VisualizationPane_MissingReportFileMessage(),
814 Bundle.VisualizationPane_MessageBoxTitle(),
815 JOptionPane.ERROR_MESSAGE);
816 } catch (SecurityException ex) {
817 JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
818 Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
819 Bundle.VisualizationPane_MessageBoxTitle(),
820 JOptionPane.ERROR_MESSAGE);
821 }
822 }
823 }
824
825 // Variables declaration - do not modify//GEN-BEGIN:variables
826 private JButton backButton;
827 private JPanel borderLayoutPanel;
828 private JButton clearVizButton;
829 private JButton fastOrganicLayoutButton;
830 private JButton fitZoomButton;
831 private JButton forwardButton;
832 private JToolBar.Separator jSeparator1;
833 private JToolBar.Separator jSeparator2;
834 private JToolBar.Separator jSeparator3;
835 private JTextPane jTextPane1;
836 private JFXPanel notificationsJFXPanel;
837 private JPanel placeHolderPanel;
838 private JButton snapshotButton;
839 private JToolBar toolbar;
840 private JButton zoomActualButton;
841 private JButton zoomInButton;
842 private JLabel zoomLabel;
843 private JButton zoomOutButton;
844 private JLabel zoomPercentLabel;
845 // End of variables declaration//GEN-END:variables
846
851 final private class SelectionListener implements mxEventSource.mxIEventListener {
852
853 @SuppressWarnings("unchecked")
854 @Override
855 public void invoke(Object sender, mxEventObject evt) {
856 Object[] selectionCells = graph.getSelectionCells();
857 if (selectionCells.length > 0) {
858 mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]);
859 HashSet<AccountDeviceInstance> selectedNodes = new HashSet<>();
860 HashSet<SelectionInfo.GraphEdge> selectedEdges = new HashSet<>();
861 for (mxICell cell : selectedCells) {
862 if (cell.isEdge()) {
863 mxICell source = (mxICell) graph.getModel().getTerminal(cell, true);
864 mxICell target = (mxICell) graph.getModel().getTerminal(cell, false);
865
866 selectedEdges.add(new SelectionInfo.GraphEdge(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance(),
867 ((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance()));
868
869 } else if (cell.isVertex()) {
870 selectedNodes.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
871 }
872 }
873
874 relationshipBrowser.setSelectionInfo(new SelectionInfo(selectedNodes, selectedEdges, currentFilter));
875 } else {
876 relationshipBrowser.setSelectionInfo(new SelectionInfo(new HashSet<>(), new HashSet<>(), currentFilter));
877 }
878 }
879 }
880
884 private interface NamedGraphLayout extends mxIGraphLayout {
885
887 }
888
892 final private class FastOrganicLayoutImpl extends mxFastOrganicLayout implements NamedGraphLayout {
893
894 FastOrganicLayoutImpl(mxGraph graph) {
895 super(graph);
896 }
897
898 @Override
899 public boolean isVertexIgnored(Object vertex) {
900 return super.isVertexIgnored(vertex)
901 || lockedVertexModel.isVertexLocked((mxCell) vertex);
902 }
903
904 @Override
905 public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
906 if (isVertexIgnored(vertex)) {
907 return getVertexBounds(vertex);
908 } else {
909 return super.setVertexLocation(vertex, x, y);
910 }
911 }
912
913 @Override
914 public String getDisplayName() {
915 return "Fast Organic";
916 }
917 }
918
922 final private class CircleLayoutImpl extends mxCircleLayout implements NamedGraphLayout {
923
924 CircleLayoutImpl(mxGraph graph) {
925 super(graph);
926 setResetEdges(true);
927 }
928
929 @Override
930 public boolean isVertexIgnored(Object vertex) {
931 return super.isVertexIgnored(vertex)
932 || lockedVertexModel.isVertexLocked((mxCell) vertex);
933 }
934
935 @Override
936 public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
937 if (isVertexIgnored(vertex)) {
938 return getVertexBounds(vertex);
939 } else {
940 return super.setVertexLocation(vertex, x, y);
941 }
942 }
943
944 @Override
945 public String getDisplayName() {
946 return "Circle";
947 }
948 }
949
953 final private class OrganicLayoutImpl extends mxOrganicLayout implements NamedGraphLayout {
954
955 OrganicLayoutImpl(mxGraph graph) {
956 super(graph);
957 setResetEdges(true);
958 }
959
960 @Override
961 public boolean isVertexIgnored(Object vertex) {
962 return super.isVertexIgnored(vertex)
963 || lockedVertexModel.isVertexLocked((mxCell) vertex);
964 }
965
966 @Override
967 public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
968 if (isVertexIgnored(vertex)) {
969 return getVertexBounds(vertex);
970 } else {
971 return super.setVertexLocation(vertex, x, y);
972 }
973 }
974
975 @Override
976 public String getDisplayName() {
977 return "Organic";
978 }
979 }
980
984 final private class HierarchicalLayoutImpl extends mxHierarchicalLayout implements NamedGraphLayout {
985
986 HierarchicalLayoutImpl(mxGraph graph) {
987 super(graph);
988 }
989
990 @Override
991 public boolean isVertexIgnored(Object vertex) {
992 return super.isVertexIgnored(vertex)
993 || lockedVertexModel.isVertexLocked((mxCell) vertex);
994 }
995
996 @Override
997 public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
998 if (isVertexIgnored(vertex)) {
999 return getVertexBounds(vertex);
1000 } else {
1001 return super.setVertexLocation(vertex, x, y);
1002 }
1003 }
1004
1005 @Override
1006 public String getDisplayName() {
1007 return "Hierarchical";
1008 }
1009 }
1010
1015 private class CancelationListener implements ActionListener {
1016
1017 private Future<?> cancellable;
1019
1020 void configure(Future<?> cancellable, ModalDialogProgressIndicator progress) {
1021 this.cancellable = cancellable;
1022 this.progress = progress;
1023 }
1024
1025 @Override
1026 public void actionPerformed(ActionEvent event) {
1027 progress.setCancelling("Cancelling...");
1028 cancellable.cancel(true);
1029 progress.finish();
1030 }
1031 }
1032
1037 private class GraphMouseListener extends MouseAdapter {
1038
1044 @Override
1045 public void mouseWheelMoved(final MouseWheelEvent event) {
1046 super.mouseWheelMoved(event);
1047 if (event.getPreciseWheelRotation() < 0) {
1048 graphComponent.zoomIn();
1049 } else if (event.getPreciseWheelRotation() > 0) {
1050 graphComponent.zoomOut();
1051 }
1052
1053 CVTEvents.getCVTEventBus().post(new CVTEvents.ScaleChangeEvent(graph.getView().getScale()));
1054 }
1055
1061 @Override
1062 public void mouseClicked(final MouseEvent event) {
1063 super.mouseClicked(event);
1064 if (SwingUtilities.isRightMouseButton(event)) {
1065 final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(), event.getY());
1066 if (cellAt != null && cellAt.isVertex()) {
1067 final JPopupMenu jPopupMenu = new JPopupMenu();
1068 final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
1069
1070 Set<mxCell> selectedVertices
1071 = Stream.of(graph.getSelectionModel().getCells())
1072 .map(mxCell.class::cast)
1073 .filter(mxCell::isVertex)
1074 .collect(Collectors.toSet());
1075
1076 if (lockedVertexModel.isVertexLocked(cellAt)) {
1077 jPopupMenu.add(new JMenuItem(new UnlockAction(selectedVertices)));
1078 } else {
1079 jPopupMenu.add(new JMenuItem(new LockAction(selectedVertices)));
1080 }
1081 if (pinnedAccountModel.isAccountPinned(adiKey.getAccountDeviceInstance())) {
1082 jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
1083 } else {
1084 jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
1085 jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
1086 }
1087 jPopupMenu.show(graphComponent.getGraphControl(), event.getX(), event.getY());
1088 }
1089 }
1090 }
1091 }
1092
1096 @NbBundle.Messages({
1097 "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
1098 "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
1099 private final class UnlockAction extends AbstractAction {
1100
1101 private final Set<mxCell> selectedVertices;
1102
1103 UnlockAction(Set<mxCell> selectedVertices) {
1104 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
1105 unlockIcon);
1106 this.selectedVertices = selectedVertices;
1107 }
1108
1109 @Override
1110
1111 public void actionPerformed(final ActionEvent event) {
1113 }
1114 }
1115
1119 @NbBundle.Messages({
1120 "VisualizationPanel.lockAction.singularText=Lock Selected Account",
1121 "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
1122 private final class LockAction extends AbstractAction {
1123
1124 private final Set<mxCell> selectedVertices;
1125
1126 LockAction(Set<mxCell> selectedVertices) {
1127 super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
1128 lockIcon);
1129 this.selectedVertices = selectedVertices;
1130 }
1131
1132 @Override
1133 public void actionPerformed(final ActionEvent event) {
1135 }
1136 }
1137}
void addReport(String localPath, String srcModuleName, String reportName)
Definition Case.java:1929
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:712
VisualizationPanel(RelationshipBrowser relationshipBrowser)
void createReport(Case currentCase, String reportName)
void handleStateChange(StateManager.CommunicationsState newState)
static String escapeFileName(String fileName)
static boolean deleteFileDir(File path)
Definition FileUtil.java:87
synchronized static Logger getLogger(String name)
Definition Logger.java:124
synchronized void start(String message, int totalWorkUnits)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.