Autopsy  4.4
Graphical digital forensics platform for The Sleuth Kit and other tools.
TimeLineTopComponent.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2016 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.timeline;
20 
21 import java.beans.PropertyVetoException;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.logging.Level;
25 import javafx.application.Platform;
26 import javafx.beans.InvalidationListener;
27 import javafx.beans.Observable;
28 import javafx.scene.Scene;
29 import javafx.scene.control.SplitPane;
30 import javafx.scene.control.Tab;
31 import javafx.scene.control.TabPane;
32 import javafx.scene.image.ImageView;
33 import javafx.scene.input.KeyCode;
34 import javafx.scene.input.KeyCodeCombination;
35 import javafx.scene.input.KeyEvent;
36 import javafx.scene.layout.Priority;
37 import javafx.scene.layout.VBox;
38 import javax.swing.JComponent;
39 import javax.swing.SwingUtilities;
40 import org.controlsfx.control.Notifications;
41 import org.joda.time.Interval;
42 import org.joda.time.format.DateTimeFormatter;
43 import org.openide.explorer.ExplorerManager;
44 import org.openide.explorer.ExplorerUtils;
45 import org.openide.nodes.AbstractNode;
46 import org.openide.nodes.Children;
47 import org.openide.nodes.Node;
48 import org.openide.util.NbBundle;
49 import org.openide.windows.Mode;
50 import org.openide.windows.TopComponent;
51 import static org.openide.windows.TopComponent.PROP_UNDOCKING_DISABLED;
52 import org.openide.windows.WindowManager;
69 import org.sleuthkit.datamodel.TskCoreException;
70 
74 @TopComponent.Description(
75  preferredID = "TimeLineTopComponent",
76  //iconBase="SET/PATH/TO/ICON/HERE",
77  persistenceType = TopComponent.PERSISTENCE_NEVER)
78 @TopComponent.Registration(mode = "timeline", openAtStartup = false)
79 public final class TimeLineTopComponent extends TopComponent implements ExplorerManager.Provider {
80 
81  private static final Logger LOGGER = Logger.getLogger(TimeLineTopComponent.class.getName());
82 
85 
86  @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
88 
89  @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
90  private final ExplorerManager em = new ExplorerManager();
91 
93 
98  @NbBundle.Messages({"TimelineTopComponent.selectedEventListener.errorMsg=There was a problem getting the content for the selected event."})
99  private final InvalidationListener selectedEventsListener = new InvalidationListener() {
100  @Override
101  public void invalidated(Observable observable) {
102  List<Long> selectedEventIDs = controller.getSelectedEventIDs();
103 
104  //depending on the active view mode, we either update the dataResultPanel, or update the contentViewerPanel directly.
105  switch (controller.getViewMode()) {
106  case LIST:
107 
108  //make an array of EventNodes for the selected events
109  EventNode[] childArray = new EventNode[selectedEventIDs.size()];
110  try {
111  for (int i = 0; i < selectedEventIDs.size(); i++) {
112  childArray[i] = EventNode.createEventNode(selectedEventIDs.get(i), controller.getEventsModel());
113  }
114  Children children = new Children.Array();
115  children.add(childArray);
116 
117  SwingUtilities.invokeLater(() -> {
118  //set generic container node as root context
119  em.setRootContext(new AbstractNode(children));
120  try {
121  //set selected nodes for actions
122  em.setSelectedNodes(childArray);
123  } catch (PropertyVetoException ex) {
124  //I don't know why this would ever happen.
125  LOGGER.log(Level.SEVERE, "Selecting the event node was vetoed.", ex); // NON-NLS
126  }
127  //if there is only one event selected push it into content viewer.
128  if (childArray.length == 1) {
129  contentViewerPanel.setNode(childArray[0]);
130  } else {
132  }
133  });
134  } catch (IllegalStateException ex) {
135  //Since the case is closed, the user probably doesn't care about this, just log it as a precaution.
136  LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS
137  } catch (TskCoreException ex) {
138  LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS
139  Platform.runLater(() -> {
140  Notifications.create()
141  .owner(jFXViewPanel.getScene().getWindow())
142  .text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg())
143  .showError();
144  });
145  }
146 
147  break;
148  case COUNTS:
149  case DETAIL:
150  //make a root node with nodes for the selected events as children and push it to the result viewer.
151  EventRootNode rootNode = new EventRootNode(selectedEventIDs, controller.getEventsModel());
152  SwingUtilities.invokeLater(() -> {
154  dataResultPanel.setNode(rootNode);
155  });
156  break;
157  default:
158  throw new UnsupportedOperationException("Unknown view mode: " + controller.getViewMode());
159  }
160  }
161  };
162 
163  private void syncViewMode() {
164  switch (controller.getViewMode()) {
165  case COUNTS:
166  case DETAIL:
167  /*
168  * For counts and details mode, restore the result table at the
169  * bottom left.
170  */
171  SwingUtilities.invokeLater(() -> {
173  if ((horizontalSplitPane.getParent() == splitYPane) == false) {
174  splitYPane.setBottomComponent(horizontalSplitPane);
175  horizontalSplitPane.setRightComponent(contentViewerPanel);
176  }
177  });
178  break;
179  case LIST:
180  /*
181  * For list mode, remove the result table, and let the content
182  * viewer expand across the bottom.
183  */
184  SwingUtilities.invokeLater(() -> {
185  splitYPane.setBottomComponent(contentViewerPanel);
186  });
187  break;
188  default:
189  throw new UnsupportedOperationException("Unknown ViewMode: " + controller.getViewMode());
190  }
191  }
192 
199  initComponents();
200  associateLookup(ExplorerUtils.createLookup(em, getActionMap()));
201  setName(NbBundle.getMessage(TimeLineTopComponent.class, "CTL_TimeLineTopComponent"));
202  setToolTipText(NbBundle.getMessage(TimeLineTopComponent.class, "HINT_TimeLineTopComponent"));
203  setIcon(WindowManager.getDefault().getMainWindow().getIconImage()); //use the same icon as main application
204 
205  getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(AddBookmarkTagAction.BOOKMARK_SHORTCUT, "addBookmarkTag"); //NON-NLS
206  getActionMap().put("addBookmarkTag", new AddBookmarkTagAction()); //NON-NLS
207 
208  this.controller = controller;
209 
210  //create linked result and content views
213 
214  //add them to bottom splitpane
215  horizontalSplitPane.setLeftComponent(dataResultPanel);
216  horizontalSplitPane.setRightComponent(contentViewerPanel);
217 
218  dataResultPanel.open(); //get the explorermanager
219 
220  Platform.runLater(this::initFXComponents);
221 
222  //set up listeners
224  controller.getSelectedEventIDs().addListener(selectedEventsListener);
225 
226  //Listen to ViewMode and adjust GUI componenets as needed.
227  controller.viewModeProperty().addListener(viewMode -> syncViewMode());
228  syncViewMode();
229  }
230 
234  @NbBundle.Messages({
235  "TimeLineTopComponent.eventsTab.name=Events",
236  "TimeLineTopComponent.filterTab.name=Filters"})
237  @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
238  void initFXComponents() {
240  final TimeZonePanel timeZonePanel = new TimeZonePanel();
241  VBox.setVgrow(timeZonePanel, Priority.SOMETIMES);
242  HistoryToolBar historyToolBar = new HistoryToolBar(controller);
243  final ZoomSettingsPane zoomSettingsPane = new ZoomSettingsPane(controller);
244 
245  //set up filter tab
246  final Tab filterTab = new Tab(Bundle.TimeLineTopComponent_filterTab_name(), new FilterSetPanel(controller));
247  filterTab.setClosable(false);
248  filterTab.setGraphic(new ImageView("org/sleuthkit/autopsy/timeline/images/funnel.png")); // NON-NLS
249 
250  //set up events tab
251  final EventsTree eventsTree = new EventsTree(controller);
252  final Tab eventsTreeTab = new Tab(Bundle.TimeLineTopComponent_eventsTab_name(), eventsTree);
253  eventsTreeTab.setClosable(false);
254  eventsTreeTab.setGraphic(new ImageView("org/sleuthkit/autopsy/timeline/images/timeline_marker.png")); // NON-NLS
255  eventsTreeTab.disableProperty().bind(controller.viewModeProperty().isNotEqualTo(ViewMode.DETAIL));
256 
257  final TabPane leftTabPane = new TabPane(filterTab, eventsTreeTab);
258  VBox.setVgrow(leftTabPane, Priority.ALWAYS);
259  controller.viewModeProperty().addListener(viewMode -> {
260  if (controller.getViewMode().equals(ViewMode.DETAIL) == false) {
261  //if view mode is not details, switch back to the filter tab
262  leftTabPane.getSelectionModel().select(filterTab);
263  }
264  });
265 
266  //assemble left column
267  final VBox leftVBox = new VBox(5, timeZonePanel, historyToolBar, zoomSettingsPane, leftTabPane);
268  SplitPane.setResizableWithParent(leftVBox, Boolean.FALSE);
269 
270  final ViewFrame viewFrame = new ViewFrame(controller, eventsTree);
271  final SplitPane mainSplitPane = new SplitPane(leftVBox, viewFrame);
272  mainSplitPane.setDividerPositions(0);
273 
274  final Scene scene = new Scene(mainSplitPane);
275  scene.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
276  if (new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) {
277  new Back(controller).handle(null);
278  } else if (new KeyCodeCombination(KeyCode.BACK_SPACE).match(keyEvent)) {
279  new Back(controller).handle(null);
280  } else if (new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) {
281  new Forward(controller).handle(null);
282  } else if (new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCodeCombination.SHIFT_DOWN).match(keyEvent)) {
283  new Forward(controller).handle(null);
284  }
285  });
286 
287  //add ui componenets to JFXPanels
288  jFXViewPanel.setScene(scene);
289  jFXstatusPanel.setScene(new Scene(new StatusBar(controller)));
290  }
291 
292  @Override
293  public List<Mode> availableModes(List<Mode> modes) {
294  return Collections.emptyList();
295  }
296 
302  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
303  private void initComponents() {
304 
305  jFXstatusPanel = new javafx.embed.swing.JFXPanel();
306  splitYPane = new javax.swing.JSplitPane();
307  jFXViewPanel = new javafx.embed.swing.JFXPanel();
308  horizontalSplitPane = new javax.swing.JSplitPane();
309  leftFillerPanel = new javax.swing.JPanel();
310  rightfillerPanel = new javax.swing.JPanel();
311 
312  jFXstatusPanel.setPreferredSize(new java.awt.Dimension(100, 16));
313 
314  splitYPane.setDividerLocation(420);
315  splitYPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
316  splitYPane.setResizeWeight(0.9);
317  splitYPane.setPreferredSize(new java.awt.Dimension(1024, 400));
318  splitYPane.setLeftComponent(jFXViewPanel);
319 
320  horizontalSplitPane.setDividerLocation(600);
321  horizontalSplitPane.setResizeWeight(0.5);
322  horizontalSplitPane.setPreferredSize(new java.awt.Dimension(1200, 300));
323  horizontalSplitPane.setRequestFocusEnabled(false);
324 
325  javax.swing.GroupLayout leftFillerPanelLayout = new javax.swing.GroupLayout(leftFillerPanel);
326  leftFillerPanel.setLayout(leftFillerPanelLayout);
327  leftFillerPanelLayout.setHorizontalGroup(
328  leftFillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
329  .addGap(0, 599, Short.MAX_VALUE)
330  );
331  leftFillerPanelLayout.setVerticalGroup(
332  leftFillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
333  .addGap(0, 54, Short.MAX_VALUE)
334  );
335 
336  horizontalSplitPane.setLeftComponent(leftFillerPanel);
337 
338  javax.swing.GroupLayout rightfillerPanelLayout = new javax.swing.GroupLayout(rightfillerPanel);
339  rightfillerPanel.setLayout(rightfillerPanelLayout);
340  rightfillerPanelLayout.setHorizontalGroup(
341  rightfillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
342  .addGap(0, 364, Short.MAX_VALUE)
343  );
344  rightfillerPanelLayout.setVerticalGroup(
345  rightfillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
346  .addGap(0, 54, Short.MAX_VALUE)
347  );
348 
349  horizontalSplitPane.setRightComponent(rightfillerPanel);
350 
351  splitYPane.setRightComponent(horizontalSplitPane);
352 
353  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
354  this.setLayout(layout);
355  layout.setHorizontalGroup(
356  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
357  .addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 972, Short.MAX_VALUE)
358  .addComponent(jFXstatusPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
359  );
360  layout.setVerticalGroup(
361  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
362  .addGroup(layout.createSequentialGroup()
363  .addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 482, Short.MAX_VALUE)
364  .addGap(0, 0, 0)
365  .addComponent(jFXstatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE))
366  );
367  }// </editor-fold>//GEN-END:initComponents
368 
369  // Variables declaration - do not modify//GEN-BEGIN:variables
370  private javax.swing.JSplitPane horizontalSplitPane;
371  private javafx.embed.swing.JFXPanel jFXViewPanel;
372  private javafx.embed.swing.JFXPanel jFXstatusPanel;
373  private javax.swing.JPanel leftFillerPanel;
374  private javax.swing.JPanel rightfillerPanel;
375  private javax.swing.JSplitPane splitYPane;
376  // End of variables declaration//GEN-END:variables
377 
378  @Override
379  public void componentOpened() {
380  WindowManager.getDefault().setTopComponentFloating(this, true);
381  putClientProperty(PROP_UNDOCKING_DISABLED, true);
382  }
383 
384  @Override
385  public ExplorerManager getExplorerManager() {
386  return em;
387  }
388 
395  @NbBundle.Messages({
396  "# {0} - start of date range",
397  "# {1} - end of date range",
398  "TimeLineResultView.startDateToEndDate.text={0} to {1}"})
399  private String getResultViewerSummaryString() {
400  Interval selectedTimeRange = controller.getSelectedTimeRange();
401  if (selectedTimeRange == null) {
402  return "";
403  } else {
404  final DateTimeFormatter zonedFormatter = TimeLineController.getZonedFormatter();
405  String start = selectedTimeRange.getStart()
407  .toString(zonedFormatter);
408  String end = selectedTimeRange.getEnd()
410  .toString(zonedFormatter);
411  return Bundle.TimeLineResultView_startDateToEndDate_text(start, end);
412  }
413  }
414 }
static EventNode createEventNode(final Long eventID, FilteredEventsModel eventsModel)
Definition: EventNode.java:218
static ReadOnlyObjectProperty< TimeZone > getTimeZone()
synchronized ObservableList< Long > getSelectedEventIDs()
static DataResultPanel createInstanceUninitialized(String title, String pathText, Node rootNode, int totalMatches, DataContent customContentView)
synchronized ReadOnlyObjectProperty< ViewMode > viewModeProperty()
synchronized static Logger getLogger(String name)
Definition: Logger.java:161

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