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

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