Autopsy  4.7.0
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-2018 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  */
19 package org.sleuthkit.autopsy.communications;
20 
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.mxEvent;
32 import com.mxgraph.util.mxEventObject;
33 import com.mxgraph.util.mxEventSource;
34 import com.mxgraph.util.mxPoint;
35 import com.mxgraph.util.mxRectangle;
36 import com.mxgraph.util.mxUndoManager;
37 import com.mxgraph.util.mxUndoableEdit;
38 import com.mxgraph.view.mxCellState;
39 import com.mxgraph.view.mxGraph;
40 import com.mxgraph.view.mxGraphView;
41 import java.awt.BorderLayout;
42 import java.awt.Color;
43 import java.awt.Cursor;
44 import java.awt.Dimension;
45 import java.awt.Font;
46 import java.awt.Frame;
47 import java.awt.Graphics;
48 import java.awt.event.ActionEvent;
49 import java.awt.event.ActionListener;
50 import java.awt.event.MouseAdapter;
51 import java.awt.event.MouseEvent;
52 import java.awt.event.MouseWheelEvent;
53 import java.beans.PropertyChangeEvent;
54 import java.beans.PropertyVetoException;
55 import java.text.DecimalFormat;
56 import java.util.Arrays;
57 import java.util.EnumSet;
58 import java.util.HashMap;
59 import java.util.HashSet;
60 import java.util.List;
61 import java.util.Map;
62 import java.util.Set;
63 import java.util.concurrent.ExecutionException;
64 import java.util.concurrent.Future;
65 import java.util.function.BiConsumer;
66 import java.util.logging.Level;
67 import java.util.stream.Collectors;
68 import java.util.stream.Stream;
69 import javafx.application.Platform;
70 import javafx.embed.swing.JFXPanel;
71 import javafx.scene.Scene;
72 import javafx.scene.layout.Pane;
73 import javax.swing.AbstractAction;
74 import javax.swing.ImageIcon;
75 import javax.swing.JButton;
76 import javax.swing.JLabel;
77 import javax.swing.JMenuItem;
78 import javax.swing.JPanel;
79 import javax.swing.JPopupMenu;
80 import javax.swing.JSplitPane;
81 import javax.swing.JTextArea;
82 import javax.swing.JToolBar;
83 import javax.swing.SwingConstants;
84 import javax.swing.SwingUtilities;
85 import javax.swing.SwingWorker;
86 import org.controlsfx.control.Notifications;
87 import org.jdesktop.layout.GroupLayout;
88 import org.jdesktop.layout.LayoutStyle;
89 import org.openide.explorer.ExplorerManager;
90 import org.openide.explorer.ExplorerUtils;
91 import org.openide.nodes.Node;
92 import org.openide.util.Lookup;
93 import org.openide.util.NbBundle;
94 import org.openide.util.lookup.ProxyLookup;
100 import org.sleuthkit.datamodel.CommunicationsFilter;
101 import org.sleuthkit.datamodel.CommunicationsManager;
102 import org.sleuthkit.datamodel.Content;
103 import org.sleuthkit.datamodel.TskCoreException;
104 
115 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
116 final public class VisualizationPanel extends JPanel implements Lookup.Provider {
117 
118  private static final long serialVersionUID = 1L;
119  private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName());
120  private static final String BASE_IMAGE_PATH = "/org/sleuthkit/autopsy/communications/images";
121  static final private ImageIcon unlockIcon
122  = new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_unlocked.png"));
123  static final private ImageIcon lockIcon
124  = new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_locked.png"));
125 
126  @NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel")
127  private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
128 
129  private final ExplorerManager vizEM = new ExplorerManager();
130  private final ExplorerManager gacEM = new ExplorerManager();
131  private final ProxyLookup proxyLookup;
132  private Frame windowAncestor;
133 
134  private CommunicationsManager commsManager;
135  private CommunicationsFilter currentFilter;
136 
137  private final mxGraphComponent graphComponent;
138  private final CommunicationsGraph graph;
139 
140  private final mxUndoManager undoManager = new mxUndoManager();
141  private final mxRubberband rubberband; //NOPMD We keep a referenec as insurance to prevent garbage collection
142 
144  private SwingWorker<?, ?> worker;
145  private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
146  private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
147 
148  private final Map<NamedGraphLayout, JButton> layoutButtons = new HashMap<>();
149  private NamedGraphLayout currentLayout;
150 
151  @NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
153  initComponents();
154  //initialize invisible JFXPanel that is used to show JFXNotifications over this window.
155  notificationsJFXPanel.setScene(new Scene(new Pane()));
156 
157  graph = new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
158 
159  /*
160  * custom implementation of mxGraphComponent that uses... a custom
161  * implementation of mxGraphControl ... that overrides paint so we can
162  * catch the NPEs we are getting and deal with them. For now that means
163  * just ignoring them.
164  */
165  graphComponent = new mxGraphComponent(graph) {
166  @Override
167  protected mxGraphComponent.mxGraphControl createGraphControl() {
168 
169  return new mxGraphControl() {
170 
171  @Override
172  public void paint(Graphics graphics) {
173  try {
174  super.paint(graphics);
175  } catch (NullPointerException ex) { //NOPMD
176  /* We can't find the underlying cause of the NPE in
177  * jgraphx, but it doesn't seem to cause any
178  * noticeable problems, so we are just logging it
179  * and moving on.
180  */
181  logger.log(Level.WARNING, "There was a NPE while painting the VisualizationPanel", ex);
182  }
183  }
184 
185  };
186  }
187  };
188  graphComponent.setAutoExtend(true);
189  graphComponent.setAutoScroll(true);
190  graphComponent.setAutoscrolls(true);
191  graphComponent.setConnectable(false);
192  graphComponent.setDragEnabled(false);
193  graphComponent.setKeepSelectionVisibleOnZoom(true);
194  graphComponent.setOpaque(true);
195  graphComponent.setToolTips(true);
196  graphComponent.setBackground(Color.WHITE);
197  borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
198 
199  //install rubber band other handlers
200  rubberband = new mxRubberband(graphComponent);
201 
202  lockedVertexModel.registerhandler(this);
203 
204  final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
205  -> zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
206  graph.getView().addListener(mxEvent.SCALE, scaleListener);
207  graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
208 
209  final GraphMouseListener graphMouseListener = new GraphMouseListener();
210  graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
211  graphComponent.getGraphControl().addMouseListener(graphMouseListener);
212 
213  final MessageBrowser messageBrowser = new MessageBrowser(vizEM, gacEM);
214  splitPane.setRightComponent(messageBrowser);
215  proxyLookup = new ProxyLookup(
216  ExplorerUtils.createLookup(vizEM, getActionMap()),
217  messageBrowser.getLookup()
218  );
219 
220  //feed selection to explorermanager
221  graph.getSelectionModel().addListener(mxEvent.CHANGE, new SelectionListener());
222  final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
223  -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty("edit"));
224 
225  graph.getModel().addListener(mxEvent.UNDO, undoListener);
226  graph.getView().addListener(mxEvent.UNDO, undoListener);
227 
228  FastOrganicLayoutImpl fastOrganicLayout = new FastOrganicLayoutImpl(graph);
229  CircleLayoutImpl circleLayout = new CircleLayoutImpl(graph);
230  OrganicLayoutImpl organicLayout = new OrganicLayoutImpl(graph);
231  organicLayout.setMaxIterations(10);
232  HierarchicalLayoutImpl hierarchyLayout = new HierarchicalLayoutImpl(graph);
233 
234  //local method to configure layout buttons
235  BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
236  layoutButtons.put(layout, layoutButton);
237  layoutButton.addActionListener(event -> applyLayout(layout));
238  };
239  //configure layout buttons.
240  configure.accept(circleLayoutButton, circleLayout);
241  configure.accept(organicLayoutButton, organicLayout);
242  configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
243  configure.accept(hierarchyLayoutButton, hierarchyLayout);
244 
245  applyLayout(fastOrganicLayout);
246  }
247 
248  @Override
249  public Lookup getLookup() {
250  return proxyLookup;
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 
276  @Subscribe
277  void handle(final CVTEvents.PinAccountsEvent pinEvent) {
278  graph.getModel().beginUpdate();
279  if (pinEvent.isReplace()) {
280  graph.resetGraph();
281  }
282  pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances());
283  rebuildGraph();
284  // Updates the display
285  graph.getModel().endUpdate();
286  }
287 
288  @Subscribe
289  void handle(final CVTEvents.FilterChangeEvent filterChangeEvent) {
290  graph.getModel().beginUpdate();
291  graph.clear();
292  currentFilter = filterChangeEvent.getNewFilter();
293  rebuildGraph();
294  // Updates the display
295  graph.getModel().endUpdate();
296  }
297 
298  @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
299  private void rebuildGraph() {
300  if (pinnedAccountModel.isEmpty()) {
301  borderLayoutPanel.remove(graphComponent);
302  borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
303  repaint();
304  } else {
305  borderLayoutPanel.remove(placeHolderPanel);
306  borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
307  if (worker != null) {
308  worker.cancel(true);
309  }
310 
311  final CancelationListener cancelationListener = new CancelationListener();
312  final ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Loading Visualization", new String[]{CANCEL}, CANCEL, cancelationListener);
313  worker = graph.rebuild(progress, commsManager, currentFilter);
314  cancelationListener.configure(worker, progress);
315  worker.addPropertyChangeListener((final PropertyChangeEvent evt) -> {
316  if (worker.isDone()) {
317  if (worker.isCancelled()) {
318  graph.resetGraph();
319  rebuildGraph();
320  }
321  applyLayout(currentLayout);
322  }
323  });
324 
325  worker.execute();
326  }
327  }
328 
329  @Override
330  public void addNotify() {
331  super.addNotify();
332  windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, this);
333 
334  try {
335  commsManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
336  } catch (TskCoreException ex) {
337  logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex);
338  } catch (NoCurrentCaseException ex) {
339  logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex);
340  }
341 
342  Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> {
343  graph.getModel().beginUpdate();
344  try {
345  graph.resetGraph();
346  } finally {
347  graph.getModel().endUpdate();
348  }
349  if (evt.getNewValue() == null) {
350  commsManager = null;
351  } else {
352  Case currentCase = (Case) evt.getNewValue();
353  try {
354  commsManager = currentCase.getSleuthkitCase().getCommunicationsManager();
355  } catch (TskCoreException ex) {
356  logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex);
357  }
358  }
359  });
360  }
361 
367  @SuppressWarnings("unchecked")
368  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
369  private void initComponents() {
370 
371  splitPane = new JSplitPane();
372  borderLayoutPanel = new JPanel();
373  placeHolderPanel = new JPanel();
374  jTextArea1 = new JTextArea();
375  toolbar = new JPanel();
376  jLabel1 = new JLabel();
377  hierarchyLayoutButton = new JButton();
378  fastOrganicLayoutButton = new JButton();
379  organicLayoutButton = new JButton();
380  circleLayoutButton = new JButton();
381  jSeparator1 = new JToolBar.Separator();
382  zoomOutButton = new JButton();
383  zoomInButton = new JButton();
384  zoomActualButton = new JButton();
385  fitZoomButton = new JButton();
386  jLabel2 = new JLabel();
387  zoomLabel = new JLabel();
388  clearVizButton = new JButton();
389  jSeparator2 = new JToolBar.Separator();
390  notificationsJFXPanel = new JFXPanel();
391 
392  setLayout(new BorderLayout());
393 
394  splitPane.setDividerLocation(800);
395  splitPane.setResizeWeight(0.5);
396 
397  borderLayoutPanel.setLayout(new BorderLayout());
398 
399  jTextArea1.setBackground(new Color(240, 240, 240));
400  jTextArea1.setColumns(20);
401  jTextArea1.setLineWrap(true);
402  jTextArea1.setRows(5);
403  jTextArea1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N
404 
405  GroupLayout placeHolderPanelLayout = new GroupLayout(placeHolderPanel);
406  placeHolderPanel.setLayout(placeHolderPanelLayout);
407  placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
408  .add(placeHolderPanelLayout.createSequentialGroup()
409  .addContainerGap(71, Short.MAX_VALUE)
410  .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE)
411  .addContainerGap(248, Short.MAX_VALUE))
412  );
413  placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
414  .add(placeHolderPanelLayout.createSequentialGroup()
415  .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
416  .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 47, GroupLayout.PREFERRED_SIZE)
417  .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
418  );
419 
420  borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
421 
422  jLabel1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel1.text")); // NOI18N
423 
424  hierarchyLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.hierarchyLayoutButton.text")); // NOI18N
425  hierarchyLayoutButton.setFocusable(false);
426  hierarchyLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
427  hierarchyLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
428 
429  fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N
430  fastOrganicLayoutButton.setFocusable(false);
431  fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
432  fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
433 
434  organicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.organicLayoutButton.text")); // NOI18N
435  organicLayoutButton.setFocusable(false);
436  organicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
437  organicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
438 
439  circleLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.circleLayoutButton.text")); // NOI18N
440  circleLayoutButton.setFocusable(false);
441  circleLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
442  circleLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
443 
444  jSeparator1.setOrientation(SwingConstants.VERTICAL);
445 
446  zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N
447  zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N
448  zoomOutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N
449  zoomOutButton.setFocusable(false);
450  zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
451  zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
452  zoomOutButton.addActionListener(new ActionListener() {
453  public void actionPerformed(ActionEvent evt) {
454  zoomOutButtonActionPerformed(evt);
455  }
456  });
457 
458  zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N
459  zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N
460  zoomInButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.toolTipText")); // NOI18N
461  zoomInButton.setFocusable(false);
462  zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
463  zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
464  zoomInButton.addActionListener(new ActionListener() {
465  public void actionPerformed(ActionEvent evt) {
466  zoomInButtonActionPerformed(evt);
467  }
468  });
469 
470  zoomActualButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N
471  zoomActualButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N
472  zoomActualButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.toolTipText")); // NOI18N
473  zoomActualButton.setFocusable(false);
474  zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
475  zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
476  zoomActualButton.addActionListener(new ActionListener() {
477  public void actionPerformed(ActionEvent evt) {
478  zoomActualButtonActionPerformed(evt);
479  }
480  });
481 
482  fitZoomButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N
483  fitZoomButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N
484  fitZoomButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.toolTipText")); // NOI18N
485  fitZoomButton.setFocusable(false);
486  fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
487  fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
488  fitZoomButton.addActionListener(new ActionListener() {
489  public void actionPerformed(ActionEvent evt) {
490  fitZoomButtonActionPerformed(evt);
491  }
492  });
493 
494  jLabel2.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel2.text")); // NOI18N
495 
496  zoomLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomLabel.text")); // NOI18N
497 
498  clearVizButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/broom.png"))); // NOI18N
499  clearVizButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.text_1")); // NOI18N
500  clearVizButton.addActionListener(new ActionListener() {
501  public void actionPerformed(ActionEvent evt) {
502  clearVizButtonActionPerformed(evt);
503  }
504  });
505 
506  jSeparator2.setOrientation(SwingConstants.VERTICAL);
507 
508  GroupLayout toolbarLayout = new GroupLayout(toolbar);
509  toolbar.setLayout(toolbarLayout);
510  toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
511  .add(toolbarLayout.createSequentialGroup()
512  .addContainerGap()
513  .add(clearVizButton)
514  .add(3, 3, 3)
515  .add(jSeparator1, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
516  .add(5, 5, 5)
517  .add(jLabel1)
518  .addPreferredGap(LayoutStyle.RELATED)
519  .add(fastOrganicLayoutButton)
520  .addPreferredGap(LayoutStyle.RELATED)
521  .add(organicLayoutButton)
522  .addPreferredGap(LayoutStyle.RELATED)
523  .add(hierarchyLayoutButton)
524  .addPreferredGap(LayoutStyle.RELATED)
525  .add(circleLayoutButton)
526  .addPreferredGap(LayoutStyle.RELATED)
527  .add(jSeparator2, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
528  .addPreferredGap(LayoutStyle.RELATED)
529  .add(jLabel2)
530  .addPreferredGap(LayoutStyle.RELATED)
531  .add(zoomLabel)
532  .addPreferredGap(LayoutStyle.RELATED)
533  .add(zoomOutButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
534  .addPreferredGap(LayoutStyle.RELATED)
535  .add(zoomInButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
536  .addPreferredGap(LayoutStyle.RELATED)
537  .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE)
538  .addPreferredGap(LayoutStyle.RELATED)
539  .add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
540  .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
541  );
542  toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
543  .add(toolbarLayout.createSequentialGroup()
544  .add(3, 3, 3)
545  .add(toolbarLayout.createParallelGroup(GroupLayout.CENTER)
546  .add(jLabel1, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE)
547  .add(hierarchyLayoutButton)
548  .add(fastOrganicLayoutButton)
549  .add(organicLayoutButton)
550  .add(circleLayoutButton)
551  .add(jSeparator1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
552  .add(zoomOutButton)
553  .add(zoomInButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
554  .add(zoomActualButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
555  .add(fitZoomButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
556  .add(jLabel2)
557  .add(zoomLabel)
558  .add(clearVizButton)
559  .add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
560  .add(3, 3, 3))
561  );
562 
563  borderLayoutPanel.add(toolbar, BorderLayout.PAGE_START);
564  borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
565 
566  splitPane.setLeftComponent(borderLayoutPanel);
567 
568  add(splitPane, BorderLayout.CENTER);
569  }// </editor-fold>//GEN-END:initComponents
570 
571  private void fitZoomButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitZoomButtonActionPerformed
572  fitGraph();
573  }//GEN-LAST:event_fitZoomButtonActionPerformed
574 
575  private void zoomActualButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomActualButtonActionPerformed
576  graphComponent.zoomActual();
577  }//GEN-LAST:event_zoomActualButtonActionPerformed
578 
579  private void zoomInButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
580  graphComponent.zoomIn();
581  }//GEN-LAST:event_zoomInButtonActionPerformed
582 
583  private void zoomOutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
584  graphComponent.zoomOut();
585  }//GEN-LAST:event_zoomOutButtonActionPerformed
586 
593  @NbBundle.Messages({"VisualizationPanel.computingLayout=Computing Layout",
594  "# {0} - layout name",
595  "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
596  "# {0} - layout name",
597  "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
598  private void applyLayout(NamedGraphLayout layout) {
599  currentLayout = layout;
600  layoutButtons.forEach((layoutKey, button)
601  -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
602 
603  ModalDialogProgressIndicator progressIndicator = new ModalDialogProgressIndicator(windowAncestor, Bundle.VisualizationPanel_computingLayout());
604  progressIndicator.start(Bundle.VisualizationPanel_computingLayout());
605 
606  new SwingWorker<Void, Void>() {
607  @Override
608  protected Void doInBackground() {
609  graph.getModel().beginUpdate();
610  try {
611  layout.execute(graph.getDefaultParent());
612  fitGraph();
613  } finally {
614  graph.getModel().endUpdate();
615  progressIndicator.finish();
616  }
617  return null;
618  }
619 
620  @Override
621  protected void done() {
622  try {
623  get();
624  } catch (InterruptedException | ExecutionException ex) {
625  logger.log(Level.WARNING, "CVT graph layout failed.", ex);
626  String message = (lockedVertexModel.isEmpty())
627  ? Bundle.VisualizationPanel_layoutFail_text(layout.getDisplayName())
628  : Bundle.VisualizationPanel_layoutFailWithLockedVertices_text(layout.getDisplayName());
629 
630  Platform.runLater(()
631  -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
632  .text(message)
633  .showWarning()
634  );
635  }
636  }
637  }.execute();
638  }
639 
640  private void clearVizButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_clearVizButtonActionPerformed
641  setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
642  graph.getModel().beginUpdate();
643  pinnedAccountModel.clear();
644  graph.clear();
645  rebuildGraph();
646  // Updates the display
647  graph.getModel().endUpdate();
648  setCursor(Cursor.getDefaultCursor());
649  }//GEN-LAST:event_clearVizButtonActionPerformed
650 
651  private void fitGraph() {
652  graphComponent.zoomTo(1, true);
653  mxPoint translate = graph.getView().getTranslate();
654  if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
655  translate = new mxPoint();
656  }
657 
658  mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true);
659  if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
660  boundsForCells = new mxRectangle(0, 0, 1, 1);
661  }
662  final mxPoint mxPoint = new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
663 
664  graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(), false, false);
665 
666  boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true);
667  if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
668  boundsForCells = new mxRectangle(0, 0, 1, 1);
669  }
670 
671  final Dimension size = graphComponent.getSize();
672  final double widthFactor = size.getWidth() / boundsForCells.getWidth();
673  final double heightFactor = size.getHeight() / boundsForCells.getHeight();
674 
675  graphComponent.zoom((heightFactor + widthFactor) / 2.0);
676  }
677 
678 
679  // Variables declaration - do not modify//GEN-BEGIN:variables
680  private JPanel borderLayoutPanel;
681  private JButton circleLayoutButton;
682  private JButton clearVizButton;
683  private JButton fastOrganicLayoutButton;
684  private JButton fitZoomButton;
685  private JButton hierarchyLayoutButton;
686  private JLabel jLabel1;
687  private JLabel jLabel2;
688  private JToolBar.Separator jSeparator1;
689  private JToolBar.Separator jSeparator2;
690  private JTextArea jTextArea1;
691  private JFXPanel notificationsJFXPanel;
692  private JButton organicLayoutButton;
693  private JPanel placeHolderPanel;
694  private JSplitPane splitPane;
695  private JPanel toolbar;
696  private JButton zoomActualButton;
697  private JButton zoomInButton;
698  private JLabel zoomLabel;
699  private JButton zoomOutButton;
700  // End of variables declaration//GEN-END:variables
701 
706  final private class SelectionListener implements mxEventSource.mxIEventListener {
707 
708  @SuppressWarnings("unchecked")
709  @Override
710  public void invoke(Object sender, mxEventObject evt) {
711  Object[] selectionCells = graph.getSelectionCells();
712  Node rootNode = Node.EMPTY;
713  Node[] selectedNodes = new Node[0];
714  if (selectionCells.length > 0) {
715  mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]);
716  HashSet<Content> relationshipSources = new HashSet<>();
717  HashSet<AccountDeviceInstanceKey> adis = new HashSet<>();
718  for (mxICell cell : selectedCells) {
719  if (cell.isEdge()) {
720  mxICell source = (mxICell) graph.getModel().getTerminal(cell, true);
721  AccountDeviceInstanceKey account1 = (AccountDeviceInstanceKey) source.getValue();
722  mxICell target = (mxICell) graph.getModel().getTerminal(cell, false);
723  AccountDeviceInstanceKey account2 = (AccountDeviceInstanceKey) target.getValue();
724  try {
725  final List<Content> relationshipSources1 = commsManager.getRelationshipSources(
726  account1.getAccountDeviceInstance(),
727  account2.getAccountDeviceInstance(),
728  currentFilter);
729  relationshipSources.addAll(relationshipSources1);
730  } catch (TskCoreException tskCoreException) {
731  logger.log(Level.SEVERE, " Error getting relationsips....", tskCoreException);
732  }
733  } else if (cell.isVertex()) {
734  adis.add((AccountDeviceInstanceKey) cell.getValue());
735  }
736  }
737 
738  rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager);
739  selectedNodes = new Node[]{rootNode};
740  }
741  vizEM.setRootContext(rootNode);
742  try {
743  vizEM.setSelectedNodes(selectedNodes);
744  } catch (PropertyVetoException ex) {
745  logger.log(Level.SEVERE, "Selection vetoed.", ex);
746  }
747  }
748  }
749 
753  private interface NamedGraphLayout extends mxIGraphLayout {
754 
755  String getDisplayName();
756  }
757 
761  final private class FastOrganicLayoutImpl extends mxFastOrganicLayout implements NamedGraphLayout {
762 
763  FastOrganicLayoutImpl(mxGraph graph) {
764  super(graph);
765  }
766 
767  @Override
768  public boolean isVertexIgnored(Object vertex) {
769  return super.isVertexIgnored(vertex)
770  || lockedVertexModel.isVertexLocked((mxCell) vertex);
771  }
772 
773  @Override
774  public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
775  if (isVertexIgnored(vertex)) {
776  return getVertexBounds(vertex);
777  } else {
778  return super.setVertexLocation(vertex, x, y);
779  }
780  }
781 
782  @Override
783  public String getDisplayName() {
784  return "Fast Organic";
785  }
786  }
787 
791  final private class CircleLayoutImpl extends mxCircleLayout implements NamedGraphLayout {
792 
793  CircleLayoutImpl(mxGraph graph) {
794  super(graph);
795  setResetEdges(true);
796  }
797 
798  @Override
799  public boolean isVertexIgnored(Object vertex) {
800  return super.isVertexIgnored(vertex)
801  || lockedVertexModel.isVertexLocked((mxCell) vertex);
802  }
803 
804  @Override
805  public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
806  if (isVertexIgnored(vertex)) {
807  return getVertexBounds(vertex);
808  } else {
809  return super.setVertexLocation(vertex, x, y);
810  }
811  }
812 
813  @Override
814  public String getDisplayName() {
815  return "Circle";
816  }
817  }
818 
822  final private class OrganicLayoutImpl extends mxOrganicLayout implements NamedGraphLayout {
823 
824  OrganicLayoutImpl(mxGraph graph) {
825  super(graph);
826  setResetEdges(true);
827  }
828 
829  @Override
830  public boolean isVertexIgnored(Object vertex) {
831  return super.isVertexIgnored(vertex)
832  || lockedVertexModel.isVertexLocked((mxCell) vertex);
833  }
834 
835  @Override
836  public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
837  if (isVertexIgnored(vertex)) {
838  return getVertexBounds(vertex);
839  } else {
840  return super.setVertexLocation(vertex, x, y);
841  }
842  }
843 
844  @Override
845  public String getDisplayName() {
846  return "Organic";
847  }
848  }
849 
853  final private class HierarchicalLayoutImpl extends mxHierarchicalLayout implements NamedGraphLayout {
854 
855  HierarchicalLayoutImpl(mxGraph graph) {
856  super(graph);
857  }
858 
859  @Override
860  public boolean isVertexIgnored(Object vertex) {
861  return super.isVertexIgnored(vertex)
862  || lockedVertexModel.isVertexLocked((mxCell) vertex);
863  }
864 
865  @Override
866  public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
867  if (isVertexIgnored(vertex)) {
868  return getVertexBounds(vertex);
869  } else {
870  return super.setVertexLocation(vertex, x, y);
871  }
872  }
873 
874  @Override
875  public String getDisplayName() {
876  return "Hierarchical";
877  }
878  }
879 
884  private class CancelationListener implements ActionListener {
885 
886  private Future<?> cancellable;
888 
889  void configure(Future<?> cancellable, ModalDialogProgressIndicator progress) {
890  this.cancellable = cancellable;
891  this.progress = progress;
892  }
893 
894  @Override
895  public void actionPerformed(ActionEvent event) {
896  progress.setCancelling("Cancelling...");
897  cancellable.cancel(true);
898  progress.finish();
899  }
900  }
901 
906  private class GraphMouseListener extends MouseAdapter {
907 
913  @Override
914  public void mouseWheelMoved(final MouseWheelEvent event) {
915  super.mouseWheelMoved(event);
916  if (event.getPreciseWheelRotation() < 0) {
917  graphComponent.zoomIn();
918  } else if (event.getPreciseWheelRotation() > 0) {
919  graphComponent.zoomOut();
920  }
921  }
922 
928  @Override
929  public void mouseClicked(final MouseEvent event) {
930  super.mouseClicked(event);
931  if (SwingUtilities.isRightMouseButton(event)) {
932  final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(), event.getY());
933  if (cellAt != null && cellAt.isVertex()) {
934  final JPopupMenu jPopupMenu = new JPopupMenu();
935  final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
936 
937  Set<mxCell> selectedVertices
938  = Stream.of(graph.getSelectionModel().getCells())
939  .map(mxCell.class::cast)
940  .filter(mxCell::isVertex)
941  .collect(Collectors.toSet());
942 
943  if (lockedVertexModel.isVertexLocked(cellAt)) {
944  jPopupMenu.add(new JMenuItem(new UnlockAction(selectedVertices)));
945  } else {
946  jPopupMenu.add(new JMenuItem(new LockAction(selectedVertices)));
947  }
948  if (pinnedAccountModel.isAccountPinned(adiKey)) {
949  jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
950  } else {
951  jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
952  jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
953  }
954  jPopupMenu.show(graphComponent.getGraphControl(), event.getX(), event.getY());
955  }
956  }
957  }
958  }
959 
963  @NbBundle.Messages({
964  "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
965  "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
966  private final class UnlockAction extends AbstractAction {
967 
968  private final Set<mxCell> selectedVertices;
969 
970  UnlockAction(Set<mxCell> selectedVertices) {
971  super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
972  unlockIcon);
973  this.selectedVertices = selectedVertices;
974  }
975 
976  @Override
977 
978  public void actionPerformed(final ActionEvent event) {
979  lockedVertexModel.unlock(selectedVertices);
980  }
981  }
982 
986  @NbBundle.Messages({
987  "VisualizationPanel.lockAction.singularText=Lock Selected Account",
988  "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
989  private final class LockAction extends AbstractAction {
990 
991  private final Set<mxCell> selectedVertices;
992 
993  LockAction(Set<mxCell> selectedVertices) {
994  super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
995  lockIcon);
996  this.selectedVertices = selectedVertices;
997  }
998 
999  @Override
1000  public void actionPerformed(final ActionEvent event) {
1001  lockedVertexModel.lock(selectedVertices);
1002  }
1003  }
1004 }
synchronized void start(String message, int totalWorkUnits)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:420

Copyright © 2012-2016 Basis Technology. Generated on: Mon Jun 18 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.