Autopsy  4.10.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.mxCellRenderer;
32 import com.mxgraph.util.mxEvent;
33 import com.mxgraph.util.mxEventObject;
34 import com.mxgraph.util.mxEventSource;
35 import com.mxgraph.util.mxPoint;
36 import com.mxgraph.util.mxRectangle;
37 import com.mxgraph.util.mxUndoManager;
38 import com.mxgraph.util.mxUndoableEdit;
39 import com.mxgraph.view.mxCellState;
40 import com.mxgraph.view.mxGraph;
41 import com.mxgraph.view.mxGraphView;
42 import java.awt.BorderLayout;
43 import java.awt.Color;
44 import java.awt.Cursor;
45 import java.awt.Desktop;
46 import java.awt.Dimension;
47 import java.awt.Font;
48 import java.awt.Frame;
49 import java.awt.Graphics;
50 import java.awt.GridLayout;
51 import java.awt.event.ActionEvent;
52 import java.awt.event.ActionListener;
53 import java.awt.event.MouseAdapter;
54 import java.awt.event.MouseEvent;
55 import java.awt.event.MouseWheelEvent;
56 import java.awt.image.BufferedImage;
57 import java.beans.PropertyChangeEvent;
58 import java.beans.PropertyVetoException;
59 import java.io.IOException;
60 import java.nio.file.Files;
61 import java.nio.file.Path;
62 import java.nio.file.Paths;
63 import java.text.DecimalFormat;
64 import java.text.SimpleDateFormat;
65 import java.util.Arrays;
66 import java.util.Date;
67 import java.util.EnumSet;
68 import java.util.HashMap;
69 import java.util.HashSet;
70 import java.util.List;
71 import java.util.Map;
72 import java.util.Set;
73 import java.util.concurrent.ExecutionException;
74 import java.util.concurrent.Future;
75 import java.util.function.BiConsumer;
76 import java.util.logging.Level;
77 import java.util.stream.Collectors;
78 import java.util.stream.Stream;
79 import javafx.application.Platform;
80 import javafx.embed.swing.JFXPanel;
81 import javafx.scene.Scene;
82 import javafx.scene.layout.Pane;
83 import javax.swing.AbstractAction;
84 import javax.swing.ImageIcon;
85 import javax.swing.JButton;
86 import javax.swing.JLabel;
87 import javax.swing.JMenuItem;
88 import javax.swing.JOptionPane;
89 import javax.swing.JPanel;
90 import javax.swing.JPopupMenu;
91 import javax.swing.JSplitPane;
92 import javax.swing.JTextArea;
93 import javax.swing.JTextField;
94 import javax.swing.JToolBar;
95 import javax.swing.SwingConstants;
96 import javax.swing.SwingUtilities;
97 import javax.swing.SwingWorker;
98 import org.apache.commons.lang3.StringUtils;
99 import org.controlsfx.control.Notifications;
100 import org.jdesktop.layout.GroupLayout;
101 import org.jdesktop.layout.LayoutStyle;
102 import org.openide.explorer.ExplorerManager;
103 import org.openide.explorer.ExplorerUtils;
104 import org.openide.nodes.Node;
105 import org.openide.util.Lookup;
106 import org.openide.util.NbBundle;
107 import org.openide.util.lookup.ProxyLookup;
108 import org.openide.windows.WindowManager;
116 import org.sleuthkit.datamodel.CommunicationsFilter;
117 import org.sleuthkit.datamodel.CommunicationsManager;
118 import org.sleuthkit.datamodel.Content;
119 import org.sleuthkit.datamodel.TskCoreException;
130 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
131 final public class VisualizationPanel extends JPanel implements Lookup.Provider {
132 
133  private static final long serialVersionUID = 1L;
134  private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName());
135  private static final String BASE_IMAGE_PATH = "/org/sleuthkit/autopsy/communications/images";
136  static final private ImageIcon unlockIcon
137  = new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_unlocked.png"));
138  static final private ImageIcon lockIcon
139  = new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_locked.png"));
140 
141  @NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel")
142  private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
143 
144  private final ExplorerManager vizEM = new ExplorerManager();
145  private final ExplorerManager gacEM = new ExplorerManager();
146  private final ProxyLookup proxyLookup;
147  private Frame windowAncestor;
148 
149  private CommunicationsManager commsManager;
150  private CommunicationsFilter currentFilter;
151 
152  private final mxGraphComponent graphComponent;
153  private final CommunicationsGraph graph;
154 
155  private final mxUndoManager undoManager = new mxUndoManager();
156  private final mxRubberband rubberband; //NOPMD We keep a referenec as insurance to prevent garbage collection
157 
159  private SwingWorker<?, ?> worker;
160  private final PinnedAccountModel pinnedAccountModel = new PinnedAccountModel();
161  private final LockedVertexModel lockedVertexModel = new LockedVertexModel();
162 
163  private final Map<NamedGraphLayout, JButton> layoutButtons = new HashMap<>();
164  private NamedGraphLayout currentLayout;
165 
166  @NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
168  initComponents();
169  //initialize invisible JFXPanel that is used to show JFXNotifications over this window.
170  notificationsJFXPanel.setScene(new Scene(new Pane()));
171 
172  graph = new CommunicationsGraph(pinnedAccountModel, lockedVertexModel);
173 
174  /*
175  * custom implementation of mxGraphComponent that uses... a custom
176  * implementation of mxGraphControl ... that overrides paint so we can
177  * catch the NPEs we are getting and deal with them. For now that means
178  * just ignoring them.
179  */
180  graphComponent = new mxGraphComponent(graph) {
181  @Override
182  protected mxGraphComponent.mxGraphControl createGraphControl() {
183 
184  return new mxGraphControl() {
185 
186  @Override
187  public void paint(Graphics graphics) {
188  try {
189  super.paint(graphics);
190  } catch (NullPointerException ex) { //NOPMD
191  /* We can't find the underlying cause of the NPE in
192  * jgraphx, but it doesn't seem to cause any
193  * noticeable problems, so we are just logging it
194  * and moving on.
195  */
196  logger.log(Level.WARNING, "There was a NPE while painting the VisualizationPanel", ex);
197  }
198  }
199 
200  };
201  }
202  };
203  graphComponent.setAutoExtend(true);
204  graphComponent.setAutoScroll(true);
205  graphComponent.setAutoscrolls(true);
206  graphComponent.setConnectable(false);
207  graphComponent.setDragEnabled(false);
208  graphComponent.setKeepSelectionVisibleOnZoom(true);
209  graphComponent.setOpaque(true);
210  graphComponent.setToolTips(true);
211  graphComponent.setBackground(Color.WHITE);
212  borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
213 
214  //install rubber band other handlers
215  rubberband = new mxRubberband(graphComponent);
216 
217  lockedVertexModel.registerhandler(this);
218 
219  final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt)
220  -> zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale()));
221  graph.getView().addListener(mxEvent.SCALE, scaleListener);
222  graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener);
223 
224  final GraphMouseListener graphMouseListener = new GraphMouseListener();
225  graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
226  graphComponent.getGraphControl().addMouseListener(graphMouseListener);
227 
228  final MessageBrowser messageBrowser = new MessageBrowser(vizEM, gacEM);
229  splitPane.setRightComponent(messageBrowser);
230  proxyLookup = new ProxyLookup(
231  ExplorerUtils.createLookup(vizEM, getActionMap()),
232  messageBrowser.getLookup()
233  );
234 
235  //feed selection to explorermanager
236  graph.getSelectionModel().addListener(mxEvent.CHANGE, new SelectionListener());
237  final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt)
238  -> undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty("edit"));
239 
240  graph.getModel().addListener(mxEvent.UNDO, undoListener);
241  graph.getView().addListener(mxEvent.UNDO, undoListener);
242 
243  FastOrganicLayoutImpl fastOrganicLayout = new FastOrganicLayoutImpl(graph);
244  CircleLayoutImpl circleLayout = new CircleLayoutImpl(graph);
245  OrganicLayoutImpl organicLayout = new OrganicLayoutImpl(graph);
246  organicLayout.setMaxIterations(10);
247  HierarchicalLayoutImpl hierarchyLayout = new HierarchicalLayoutImpl(graph);
248 
249  //local method to configure layout buttons
250  BiConsumer<JButton, NamedGraphLayout> configure = (layoutButton, layout) -> {
251  layoutButtons.put(layout, layoutButton);
252  layoutButton.addActionListener(event -> applyLayout(layout));
253  };
254  //configure layout buttons.
255  configure.accept(circleLayoutButton, circleLayout);
256  configure.accept(organicLayoutButton, organicLayout);
257  configure.accept(fastOrganicLayoutButton, fastOrganicLayout);
258  configure.accept(hierarchyLayoutButton, hierarchyLayout);
259 
260  applyLayout(fastOrganicLayout);
261  }
262 
263  @Override
264  public Lookup getLookup() {
265  return proxyLookup;
266  }
267 
268  @Subscribe
269  void handle(LockedVertexModel.VertexLockEvent event) {
270  final Set<mxCell> vertices = event.getVertices();
271  mxGraphView view = graph.getView();
272  vertices.forEach(vertex -> {
273  final mxCellState state = view.getState(vertex, true);
274  view.updateLabel(state);
275  view.updateLabelBounds(state);
276  view.updateBoundingBox(state);
277  graphComponent.redraw(state);
278  });
279  }
280 
281  @Subscribe
282  void handle(final CVTEvents.UnpinAccountsEvent pinEvent) {
283  graph.getModel().beginUpdate();
284  pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances());
285  graph.clear();
286  rebuildGraph();
287  // Updates the display
288  graph.getModel().endUpdate();
289  }
290 
291  @Subscribe
292  void handle(final CVTEvents.PinAccountsEvent pinEvent) {
293  graph.getModel().beginUpdate();
294  if (pinEvent.isReplace()) {
295  graph.resetGraph();
296  }
297  pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances());
298  rebuildGraph();
299  // Updates the display
300  graph.getModel().endUpdate();
301  }
302 
303  @Subscribe
304  void handle(final CVTEvents.FilterChangeEvent filterChangeEvent) {
305  graph.getModel().beginUpdate();
306  graph.clear();
307  currentFilter = filterChangeEvent.getNewFilter();
308  rebuildGraph();
309  // Updates the display
310  graph.getModel().endUpdate();
311  }
312 
313  @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
314  private void rebuildGraph() {
315  if (pinnedAccountModel.isEmpty()) {
316  borderLayoutPanel.remove(graphComponent);
317  borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
318  repaint();
319  } else {
320  borderLayoutPanel.remove(placeHolderPanel);
321  borderLayoutPanel.add(graphComponent, BorderLayout.CENTER);
322  if (worker != null) {
323  worker.cancel(true);
324  }
325 
326  final CancelationListener cancelationListener = new CancelationListener();
327  final ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Loading Visualization", new String[]{CANCEL}, CANCEL, cancelationListener);
328  worker = graph.rebuild(progress, commsManager, currentFilter);
329  cancelationListener.configure(worker, progress);
330  worker.addPropertyChangeListener((final PropertyChangeEvent evt) -> {
331  if (worker.isDone()) {
332  if (worker.isCancelled()) {
333  graph.resetGraph();
334  rebuildGraph();
335  }
336  applyLayout(currentLayout);
337  }
338  });
339 
340  worker.execute();
341  }
342  }
343 
344  @Override
345  public void addNotify() {
346  super.addNotify();
347  windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, this);
348 
349  try {
350  commsManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
351  } catch (TskCoreException ex) {
352  logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex);
353  } catch (NoCurrentCaseException ex) {
354  logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex);
355  }
356 
357  Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> {
358  graph.getModel().beginUpdate();
359  try {
360  graph.resetGraph();
361  } finally {
362  graph.getModel().endUpdate();
363  }
364  if (evt.getNewValue() == null) {
365  commsManager = null;
366  } else {
367  Case currentCase = (Case) evt.getNewValue();
368  try {
369  commsManager = currentCase.getSleuthkitCase().getCommunicationsManager();
370  } catch (TskCoreException ex) {
371  logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex);
372  }
373  }
374  });
375  }
376 
382  @SuppressWarnings("unchecked")
383  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
384  private void initComponents() {
385 
386  splitPane = new JSplitPane();
387  borderLayoutPanel = new JPanel();
388  placeHolderPanel = new JPanel();
389  jTextArea1 = new JTextArea();
390  toolbar = new JPanel();
391  jLabel1 = new JLabel();
392  hierarchyLayoutButton = new JButton();
393  fastOrganicLayoutButton = new JButton();
394  organicLayoutButton = new JButton();
395  circleLayoutButton = new JButton();
396  jSeparator1 = new JToolBar.Separator();
397  zoomOutButton = new JButton();
398  zoomInButton = new JButton();
399  zoomActualButton = new JButton();
400  fitZoomButton = new JButton();
401  jLabel2 = new JLabel();
402  zoomLabel = new JLabel();
403  clearVizButton = new JButton();
404  jSeparator2 = new JToolBar.Separator();
405  snapshotButton = new JButton();
406  jSeparator3 = new JToolBar.Separator();
407  notificationsJFXPanel = new JFXPanel();
408 
409  setLayout(new BorderLayout());
410 
411  splitPane.setDividerLocation(800);
412  splitPane.setResizeWeight(0.5);
413 
414  borderLayoutPanel.setLayout(new BorderLayout());
415 
416  jTextArea1.setBackground(new Color(240, 240, 240));
417  jTextArea1.setColumns(20);
418  jTextArea1.setLineWrap(true);
419  jTextArea1.setRows(5);
420  jTextArea1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N
421 
422  GroupLayout placeHolderPanelLayout = new GroupLayout(placeHolderPanel);
423  placeHolderPanel.setLayout(placeHolderPanelLayout);
424  placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
425  .add(placeHolderPanelLayout.createSequentialGroup()
426  .addContainerGap(268, Short.MAX_VALUE)
427  .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE)
428  .addContainerGap(445, Short.MAX_VALUE))
429  );
430  placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
431  .add(placeHolderPanelLayout.createSequentialGroup()
432  .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
433  .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 47, GroupLayout.PREFERRED_SIZE)
434  .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
435  );
436 
437  borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
438 
439  jLabel1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel1.text")); // NOI18N
440 
441  hierarchyLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.hierarchyLayoutButton.text")); // NOI18N
442  hierarchyLayoutButton.setFocusable(false);
443  hierarchyLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
444  hierarchyLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
445 
446  fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N
447  fastOrganicLayoutButton.setFocusable(false);
448  fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
449  fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
450 
451  organicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.organicLayoutButton.text")); // NOI18N
452  organicLayoutButton.setFocusable(false);
453  organicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
454  organicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
455 
456  circleLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.circleLayoutButton.text")); // NOI18N
457  circleLayoutButton.setFocusable(false);
458  circleLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER);
459  circleLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
460 
461  jSeparator1.setOrientation(SwingConstants.VERTICAL);
462 
463  zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N
464  zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N
465  zoomOutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N
466  zoomOutButton.setFocusable(false);
467  zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
468  zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
469  zoomOutButton.addActionListener(new ActionListener() {
470  public void actionPerformed(ActionEvent evt) {
471  zoomOutButtonActionPerformed(evt);
472  }
473  });
474 
475  zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N
476  zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N
477  zoomInButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.toolTipText")); // NOI18N
478  zoomInButton.setFocusable(false);
479  zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
480  zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
481  zoomInButton.addActionListener(new ActionListener() {
482  public void actionPerformed(ActionEvent evt) {
483  zoomInButtonActionPerformed(evt);
484  }
485  });
486 
487  zoomActualButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N
488  zoomActualButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N
489  zoomActualButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.toolTipText")); // NOI18N
490  zoomActualButton.setFocusable(false);
491  zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
492  zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
493  zoomActualButton.addActionListener(new ActionListener() {
494  public void actionPerformed(ActionEvent evt) {
495  zoomActualButtonActionPerformed(evt);
496  }
497  });
498 
499  fitZoomButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N
500  fitZoomButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N
501  fitZoomButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.toolTipText")); // NOI18N
502  fitZoomButton.setFocusable(false);
503  fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
504  fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
505  fitZoomButton.addActionListener(new ActionListener() {
506  public void actionPerformed(ActionEvent evt) {
507  fitZoomButtonActionPerformed(evt);
508  }
509  });
510 
511  jLabel2.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel2.text")); // NOI18N
512 
513  zoomLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomLabel.text")); // NOI18N
514 
515  clearVizButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/broom.png"))); // NOI18N
516  clearVizButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.text_1")); // NOI18N
517  clearVizButton.addActionListener(new ActionListener() {
518  public void actionPerformed(ActionEvent evt) {
519  clearVizButtonActionPerformed(evt);
520  }
521  });
522 
523  jSeparator2.setOrientation(SwingConstants.VERTICAL);
524 
525  snapshotButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/report/images/image.png"))); // NOI18N
526  snapshotButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.text_1")); // NOI18N
527  snapshotButton.addActionListener(new ActionListener() {
528  public void actionPerformed(ActionEvent evt) {
529  snapshotButtonActionPerformed(evt);
530  }
531  });
532 
533  jSeparator3.setOrientation(SwingConstants.VERTICAL);
534 
535  GroupLayout toolbarLayout = new GroupLayout(toolbar);
536  toolbar.setLayout(toolbarLayout);
537  toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
538  .add(toolbarLayout.createSequentialGroup()
539  .addContainerGap()
540  .add(clearVizButton)
541  .add(3, 3, 3)
542  .add(jSeparator1, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
543  .add(5, 5, 5)
544  .add(jLabel1)
545  .addPreferredGap(LayoutStyle.RELATED)
546  .add(fastOrganicLayoutButton)
547  .addPreferredGap(LayoutStyle.RELATED)
548  .add(organicLayoutButton)
549  .addPreferredGap(LayoutStyle.RELATED)
550  .add(hierarchyLayoutButton)
551  .addPreferredGap(LayoutStyle.RELATED)
552  .add(circleLayoutButton)
553  .addPreferredGap(LayoutStyle.RELATED)
554  .add(jSeparator2, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
555  .addPreferredGap(LayoutStyle.RELATED)
556  .add(jLabel2)
557  .addPreferredGap(LayoutStyle.RELATED)
558  .add(zoomLabel)
559  .addPreferredGap(LayoutStyle.RELATED)
560  .add(zoomOutButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
561  .addPreferredGap(LayoutStyle.RELATED)
562  .add(zoomInButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
563  .addPreferredGap(LayoutStyle.RELATED)
564  .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE)
565  .addPreferredGap(LayoutStyle.RELATED)
566  .add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
567  .addPreferredGap(LayoutStyle.RELATED)
568  .add(jSeparator3, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
569  .addPreferredGap(LayoutStyle.RELATED)
570  .add(snapshotButton)
571  .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
572  );
573  toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
574  .add(toolbarLayout.createSequentialGroup()
575  .add(3, 3, 3)
576  .add(toolbarLayout.createParallelGroup(GroupLayout.CENTER)
577  .add(jLabel1, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE)
578  .add(hierarchyLayoutButton)
579  .add(fastOrganicLayoutButton)
580  .add(organicLayoutButton)
581  .add(circleLayoutButton)
582  .add(jSeparator1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
583  .add(zoomOutButton)
584  .add(zoomInButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
585  .add(zoomActualButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
586  .add(fitZoomButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
587  .add(jLabel2)
588  .add(zoomLabel)
589  .add(clearVizButton)
590  .add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
591  .add(snapshotButton)
592  .add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
593  .add(3, 3, 3))
594  );
595 
596  borderLayoutPanel.add(toolbar, BorderLayout.PAGE_START);
597  borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
598 
599  splitPane.setLeftComponent(borderLayoutPanel);
600 
601  add(splitPane, BorderLayout.CENTER);
602  }// </editor-fold>//GEN-END:initComponents
603 
604  private void fitZoomButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitZoomButtonActionPerformed
605  fitGraph();
606  }//GEN-LAST:event_fitZoomButtonActionPerformed
607 
608  private void zoomActualButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomActualButtonActionPerformed
609  graphComponent.zoomActual();
610  }//GEN-LAST:event_zoomActualButtonActionPerformed
611 
612  private void zoomInButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
613  graphComponent.zoomIn();
614  }//GEN-LAST:event_zoomInButtonActionPerformed
615 
616  private void zoomOutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
617  graphComponent.zoomOut();
618  }//GEN-LAST:event_zoomOutButtonActionPerformed
619 
626  @NbBundle.Messages({"VisualizationPanel.computingLayout=Computing Layout",
627  "# {0} - layout name",
628  "VisualizationPanel.layoutFailWithLockedVertices.text={0} layout failed with locked vertices. Unlock some vertices or try a different layout.",
629  "# {0} - layout name",
630  "VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout."})
631  private void applyLayout(NamedGraphLayout layout) {
632  currentLayout = layout;
633  layoutButtons.forEach((layoutKey, button)
634  -> button.setFont(button.getFont().deriveFont(layoutKey == layout ? Font.BOLD : Font.PLAIN)));
635 
636  ModalDialogProgressIndicator progressIndicator = new ModalDialogProgressIndicator(windowAncestor, Bundle.VisualizationPanel_computingLayout());
637  progressIndicator.start(Bundle.VisualizationPanel_computingLayout());
638 
639  new SwingWorker<Void, Void>() {
640  @Override
641  protected Void doInBackground() {
642  graph.getModel().beginUpdate();
643  try {
644  layout.execute(graph.getDefaultParent());
645  fitGraph();
646  } finally {
647  graph.getModel().endUpdate();
648  progressIndicator.finish();
649  }
650  return null;
651  }
652 
653  @Override
654  protected void done() {
655  try {
656  get();
657  } catch (InterruptedException | ExecutionException ex) {
658  logger.log(Level.WARNING, "CVT graph layout failed.", ex);
659  String message = (lockedVertexModel.isEmpty())
660  ? Bundle.VisualizationPanel_layoutFail_text(layout.getDisplayName())
661  : Bundle.VisualizationPanel_layoutFailWithLockedVertices_text(layout.getDisplayName());
662 
663  Platform.runLater(()
664  -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
665  .text(message)
666  .showWarning()
667  );
668  }
669  }
670  }.execute();
671  }
672 
673  private void clearVizButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_clearVizButtonActionPerformed
674  setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
675  graph.getModel().beginUpdate();
676  pinnedAccountModel.clear();
677  graph.clear();
678  rebuildGraph();
679  // Updates the display
680  graph.getModel().endUpdate();
681  setCursor(Cursor.getDefaultCursor());
682  }//GEN-LAST:event_clearVizButtonActionPerformed
683 
684  @NbBundle.Messages({
685  "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation."
686  })
687  private void snapshotButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_snapshotButtonActionPerformed
688  try {
689  handleSnapshotEvent();
690  } catch (NoCurrentCaseException | IOException ex) {
691  logger.log(Level.SEVERE, "Unable to create communications snapsot report", ex); //NON-NLS
692 
693  Platform.runLater(()
694  -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
695  .text(Bundle.VisualizationPanel_snapshot_report_failure())
696  .showWarning());
697  } catch( TskCoreException ex) {
698  logger.log(Level.WARNING, "Unable to add report to currenct case", ex); //NON-NLS
699  }
700  }//GEN-LAST:event_snapshotButtonActionPerformed
701 
702  private void fitGraph() {
703  graphComponent.zoomTo(1, true);
704  mxPoint translate = graph.getView().getTranslate();
705  if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) {
706  translate = new mxPoint();
707  }
708 
709  mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true);
710  if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
711  boundsForCells = new mxRectangle(0, 0, 1, 1);
712  }
713  final mxPoint mxPoint = new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY());
714 
715  graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(), false, false);
716 
717  boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true);
718  if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) {
719  boundsForCells = new mxRectangle(0, 0, 1, 1);
720  }
721 
722  final Dimension size = graphComponent.getSize();
723  final double widthFactor = size.getWidth() / boundsForCells.getWidth();
724  final double heightFactor = size.getHeight() / boundsForCells.getHeight();
725 
726  graphComponent.zoom((heightFactor + widthFactor) / 2.0);
727  }
728 
735  @NbBundle.Messages({
736  "VisualizationPanel_action_dialogs_title=Communications",
737  "VisualizationPanel_module_name=Communications",
738  "VisualizationPanel_action_name_text=Snapshot Report",
739  "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
740  "VisualizationPane_reportName=Communications Snapshot",
741  "# {0} - default name",
742  "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
743  "VisualizationPane_blank_report_title=Blank Report Name",
744  "# {0} - report name",
745  "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}"
746  })
747  private void handleSnapshotEvent() throws NoCurrentCaseException, IOException, TskCoreException {
748  Case currentCase = Case.getCurrentCaseThrows();
749  Date generationDate = new Date();
750 
751  final String defaultReportName = FileUtil.escapeFileName(currentCase.getDisplayName() + " " + new SimpleDateFormat("MMddyyyyHHmmss").format(generationDate)); //NON_NLS
752 
753  final JTextField text = new JTextField(50);
754  final JPanel panel = new JPanel(new GridLayout(2, 1));
755  panel.add(new JLabel(Bundle.VisualizationPane_fileName_prompt()));
756  panel.add(text);
757 
758  text.setText(defaultReportName);
759 
760  int result = JOptionPane.showConfirmDialog(graphComponent, panel,
761  Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
762 
763  if (result == JOptionPane.OK_OPTION) {
764  String enteredReportName = text.getText();
765 
766  if(enteredReportName.trim().isEmpty()){
767  result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
768  if(result != JOptionPane.OK_OPTION) {
769  return;
770  }
771  }
772 
773  String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
774  Path reportPath = Paths.get(currentCase.getReportDirectory(), reportName);
775  if (Files.exists(reportPath)) {
776  result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
777  Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
778 
779  if (result == JOptionPane.OK_OPTION) {
780  FileUtil.deleteFileDir(reportPath.toFile());
781  createReport(currentCase, reportName);
782  }
783  } else {
784  createReport(currentCase, reportName);
785  currentCase.addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
786 
787  }
788  }
789  }
790 
799  @NbBundle.Messages({
800  "VisualizationPane_DisplayName=Open Report",
801  "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
802  "VisualizationPane_MessageBoxTitle=Open Report Failure",
803  "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
804  "VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
805  "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
806  "# {0} - report path",
807  "VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
808  "VisualizationPane_Report_OK_Button=OK",
809  "VisualizationPane_Open_Report=Open Report",})
810  private void createReport(Case currentCase, String reportName) throws IOException {
811 
812  // Create the report.
813  Path reportFolderPath = Paths.get(currentCase.getReportDirectory(), reportName, Bundle.VisualizationPane_reportName()); //NON_NLS
814  BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE, true, null);
815  Path reportPath = new CommSnapShotReportWriter(currentCase, reportFolderPath, reportName, new Date(), image, currentFilter).writeReport();
816 
817  // Report success to the user and offer to open the report.
818  String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
819  String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
820 
821  int result = JOptionPane.showOptionDialog(graphComponent, message,
822  Bundle.VisualizationPanel_action_dialogs_title(),
823  JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
824  null, buttons, buttons[1]);
825  if (result == JOptionPane.YES_NO_OPTION) {
826  try {
827  Desktop.getDesktop().open(reportPath.toFile());
828  } catch (IOException ex) {
829  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
830  Bundle.VisualizationPane_NoAssociatedEditorMessage(),
831  Bundle.VisualizationPane_MessageBoxTitle(),
832  JOptionPane.ERROR_MESSAGE);
833  } catch (UnsupportedOperationException ex) {
834  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
835  Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
836  Bundle.VisualizationPane_MessageBoxTitle(),
837  JOptionPane.ERROR_MESSAGE);
838  } catch (IllegalArgumentException ex) {
839  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
840  Bundle.VisualizationPane_MissingReportFileMessage(),
841  Bundle.VisualizationPane_MessageBoxTitle(),
842  JOptionPane.ERROR_MESSAGE);
843  } catch (SecurityException ex) {
844  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
845  Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
846  Bundle.VisualizationPane_MessageBoxTitle(),
847  JOptionPane.ERROR_MESSAGE);
848  }
849  }
850  }
851 
852  // Variables declaration - do not modify//GEN-BEGIN:variables
853  private JPanel borderLayoutPanel;
854  private JButton circleLayoutButton;
855  private JButton clearVizButton;
856  private JButton fastOrganicLayoutButton;
857  private JButton fitZoomButton;
858  private JButton hierarchyLayoutButton;
859  private JLabel jLabel1;
860  private JLabel jLabel2;
861  private JToolBar.Separator jSeparator1;
862  private JToolBar.Separator jSeparator2;
863  private JToolBar.Separator jSeparator3;
864  private JTextArea jTextArea1;
865  private JFXPanel notificationsJFXPanel;
866  private JButton organicLayoutButton;
867  private JPanel placeHolderPanel;
868  private JButton snapshotButton;
869  private JSplitPane splitPane;
870  private JPanel toolbar;
871  private JButton zoomActualButton;
872  private JButton zoomInButton;
873  private JLabel zoomLabel;
874  private JButton zoomOutButton;
875  // End of variables declaration//GEN-END:variables
876 
881  final private class SelectionListener implements mxEventSource.mxIEventListener {
882 
883  @SuppressWarnings("unchecked")
884  @Override
885  public void invoke(Object sender, mxEventObject evt) {
886  Object[] selectionCells = graph.getSelectionCells();
887  Node rootNode = Node.EMPTY;
888  Node[] selectedNodes = new Node[0];
889  if (selectionCells.length > 0) {
890  mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]);
891  HashSet<Content> relationshipSources = new HashSet<>();
892  HashSet<AccountDeviceInstanceKey> adis = new HashSet<>();
893  for (mxICell cell : selectedCells) {
894  if (cell.isEdge()) {
895  mxICell source = (mxICell) graph.getModel().getTerminal(cell, true);
896  AccountDeviceInstanceKey account1 = (AccountDeviceInstanceKey) source.getValue();
897  mxICell target = (mxICell) graph.getModel().getTerminal(cell, false);
898  AccountDeviceInstanceKey account2 = (AccountDeviceInstanceKey) target.getValue();
899  try {
900  final List<Content> relationshipSources1 = commsManager.getRelationshipSources(
901  account1.getAccountDeviceInstance(),
902  account2.getAccountDeviceInstance(),
903  currentFilter);
904  relationshipSources.addAll(relationshipSources1);
905  } catch (TskCoreException tskCoreException) {
906  logger.log(Level.SEVERE, " Error getting relationsips....", tskCoreException);
907  }
908  } else if (cell.isVertex()) {
909  adis.add((AccountDeviceInstanceKey) cell.getValue());
910  }
911  }
912 
913  rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager);
914  selectedNodes = new Node[]{rootNode};
915  }
916  vizEM.setRootContext(rootNode);
917  try {
918  vizEM.setSelectedNodes(selectedNodes);
919  } catch (PropertyVetoException ex) {
920  logger.log(Level.SEVERE, "Selection vetoed.", ex);
921  }
922  }
923  }
924 
928  private interface NamedGraphLayout extends mxIGraphLayout {
929 
930  String getDisplayName();
931  }
932 
936  final private class FastOrganicLayoutImpl extends mxFastOrganicLayout implements NamedGraphLayout {
937 
938  FastOrganicLayoutImpl(mxGraph graph) {
939  super(graph);
940  }
941 
942  @Override
943  public boolean isVertexIgnored(Object vertex) {
944  return super.isVertexIgnored(vertex)
945  || lockedVertexModel.isVertexLocked((mxCell) vertex);
946  }
947 
948  @Override
949  public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
950  if (isVertexIgnored(vertex)) {
951  return getVertexBounds(vertex);
952  } else {
953  return super.setVertexLocation(vertex, x, y);
954  }
955  }
956 
957  @Override
958  public String getDisplayName() {
959  return "Fast Organic";
960  }
961  }
962 
966  final private class CircleLayoutImpl extends mxCircleLayout implements NamedGraphLayout {
967 
968  CircleLayoutImpl(mxGraph graph) {
969  super(graph);
970  setResetEdges(true);
971  }
972 
973  @Override
974  public boolean isVertexIgnored(Object vertex) {
975  return super.isVertexIgnored(vertex)
976  || lockedVertexModel.isVertexLocked((mxCell) vertex);
977  }
978 
979  @Override
980  public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
981  if (isVertexIgnored(vertex)) {
982  return getVertexBounds(vertex);
983  } else {
984  return super.setVertexLocation(vertex, x, y);
985  }
986  }
987 
988  @Override
989  public String getDisplayName() {
990  return "Circle";
991  }
992  }
993 
997  final private class OrganicLayoutImpl extends mxOrganicLayout implements NamedGraphLayout {
998 
999  OrganicLayoutImpl(mxGraph graph) {
1000  super(graph);
1001  setResetEdges(true);
1002  }
1003 
1004  @Override
1005  public boolean isVertexIgnored(Object vertex) {
1006  return super.isVertexIgnored(vertex)
1007  || lockedVertexModel.isVertexLocked((mxCell) vertex);
1008  }
1009 
1010  @Override
1011  public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
1012  if (isVertexIgnored(vertex)) {
1013  return getVertexBounds(vertex);
1014  } else {
1015  return super.setVertexLocation(vertex, x, y);
1016  }
1017  }
1018 
1019  @Override
1020  public String getDisplayName() {
1021  return "Organic";
1022  }
1023  }
1024 
1028  final private class HierarchicalLayoutImpl extends mxHierarchicalLayout implements NamedGraphLayout {
1029 
1030  HierarchicalLayoutImpl(mxGraph graph) {
1031  super(graph);
1032  }
1033 
1034  @Override
1035  public boolean isVertexIgnored(Object vertex) {
1036  return super.isVertexIgnored(vertex)
1037  || lockedVertexModel.isVertexLocked((mxCell) vertex);
1038  }
1039 
1040  @Override
1041  public mxRectangle setVertexLocation(Object vertex, double x, double y) { //NOPMD x, y are standard coordinate names
1042  if (isVertexIgnored(vertex)) {
1043  return getVertexBounds(vertex);
1044  } else {
1045  return super.setVertexLocation(vertex, x, y);
1046  }
1047  }
1048 
1049  @Override
1050  public String getDisplayName() {
1051  return "Hierarchical";
1052  }
1053  }
1054 
1059  private class CancelationListener implements ActionListener {
1060 
1061  private Future<?> cancellable;
1063 
1064  void configure(Future<?> cancellable, ModalDialogProgressIndicator progress) {
1065  this.cancellable = cancellable;
1066  this.progress = progress;
1067  }
1068 
1069  @Override
1070  public void actionPerformed(ActionEvent event) {
1071  progress.setCancelling("Cancelling...");
1072  cancellable.cancel(true);
1073  progress.finish();
1074  }
1075  }
1076 
1081  private class GraphMouseListener extends MouseAdapter {
1082 
1088  @Override
1089  public void mouseWheelMoved(final MouseWheelEvent event) {
1090  super.mouseWheelMoved(event);
1091  if (event.getPreciseWheelRotation() < 0) {
1092  graphComponent.zoomIn();
1093  } else if (event.getPreciseWheelRotation() > 0) {
1094  graphComponent.zoomOut();
1095  }
1096  }
1097 
1103  @Override
1104  public void mouseClicked(final MouseEvent event) {
1105  super.mouseClicked(event);
1106  if (SwingUtilities.isRightMouseButton(event)) {
1107  final mxCell cellAt = (mxCell) graphComponent.getCellAt(event.getX(), event.getY());
1108  if (cellAt != null && cellAt.isVertex()) {
1109  final JPopupMenu jPopupMenu = new JPopupMenu();
1110  final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue();
1111 
1112  Set<mxCell> selectedVertices
1113  = Stream.of(graph.getSelectionModel().getCells())
1114  .map(mxCell.class::cast)
1115  .filter(mxCell::isVertex)
1116  .collect(Collectors.toSet());
1117 
1118  if (lockedVertexModel.isVertexLocked(cellAt)) {
1119  jPopupMenu.add(new JMenuItem(new UnlockAction(selectedVertices)));
1120  } else {
1121  jPopupMenu.add(new JMenuItem(new LockAction(selectedVertices)));
1122  }
1123  if (pinnedAccountModel.isAccountPinned(adiKey)) {
1124  jPopupMenu.add(UnpinAccountsAction.getInstance().getPopupPresenter());
1125  } else {
1126  jPopupMenu.add(PinAccountsAction.getInstance().getPopupPresenter());
1127  jPopupMenu.add(ResetAndPinAccountsAction.getInstance().getPopupPresenter());
1128  }
1129  jPopupMenu.show(graphComponent.getGraphControl(), event.getX(), event.getY());
1130  }
1131  }
1132  }
1133  }
1134 
1138  @NbBundle.Messages({
1139  "VisualizationPanel.unlockAction.singularText=Unlock Selected Account",
1140  "VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts",})
1141  private final class UnlockAction extends AbstractAction {
1142 
1143  private final Set<mxCell> selectedVertices;
1144 
1145  UnlockAction(Set<mxCell> selectedVertices) {
1146  super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_unlockAction_pluralText() : Bundle.VisualizationPanel_unlockAction_singularText(),
1147  unlockIcon);
1148  this.selectedVertices = selectedVertices;
1149  }
1150 
1151  @Override
1152 
1153  public void actionPerformed(final ActionEvent event) {
1154  lockedVertexModel.unlock(selectedVertices);
1155  }
1156  }
1157 
1161  @NbBundle.Messages({
1162  "VisualizationPanel.lockAction.singularText=Lock Selected Account",
1163  "VisualizationPanel.lockAction.pluralText=Lock Selected Accounts"})
1164  private final class LockAction extends AbstractAction {
1165 
1166  private final Set<mxCell> selectedVertices;
1167 
1168  LockAction(Set<mxCell> selectedVertices) {
1169  super(selectedVertices.size() > 1 ? Bundle.VisualizationPanel_lockAction_pluralText() : Bundle.VisualizationPanel_lockAction_singularText(),
1170  lockIcon);
1171  this.selectedVertices = selectedVertices;
1172  }
1173 
1174  @Override
1175  public void actionPerformed(final ActionEvent event) {
1176  lockedVertexModel.lock(selectedVertices);
1177  }
1178  }
1179 }
void createReport(Case currentCase, String reportName)
static boolean deleteFileDir(File path)
Definition: FileUtil.java:87
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1690
synchronized void start(String message, int totalWorkUnits)
static String escapeFileName(String fileName)
Definition: FileUtil.java:169
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:437

Copyright © 2012-2018 Basis Technology. Generated on: Fri Mar 22 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.