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

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