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

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