19 package org.sleuthkit.autopsy.timeline;
21 import com.google.common.collect.ImmutableList;
22 import java.awt.BorderLayout;
23 import java.awt.Component;
24 import java.awt.KeyboardFocusManager;
25 import java.beans.PropertyChangeEvent;
26 import java.beans.PropertyChangeListener;
27 import java.beans.PropertyVetoException;
28 import java.util.List;
29 import java.util.logging.Level;
30 import java.util.stream.Collectors;
31 import javafx.application.Platform;
32 import javafx.beans.InvalidationListener;
33 import javafx.beans.Observable;
34 import javafx.scene.Scene;
35 import javafx.scene.control.SplitPane;
36 import javafx.scene.control.Tab;
37 import javafx.scene.control.TabPane;
38 import javafx.scene.image.ImageView;
39 import javafx.scene.input.KeyCode;
40 import javafx.scene.input.KeyCodeCombination;
41 import javafx.scene.input.KeyEvent;
42 import javafx.scene.layout.Priority;
43 import javafx.scene.layout.VBox;
44 import javax.swing.JComponent;
45 import javax.swing.JPanel;
46 import javax.swing.SwingUtilities;
47 import static javax.swing.SwingUtilities.isDescendingFrom;
48 import org.controlsfx.control.Notifications;
49 import org.joda.time.Interval;
50 import org.joda.time.format.DateTimeFormatter;
51 import org.openide.explorer.ExplorerManager;
52 import static org.openide.explorer.ExplorerUtils.createLookup;
53 import org.openide.nodes.AbstractNode;
54 import org.openide.nodes.Children;
55 import org.openide.nodes.Node;
56 import org.openide.util.NbBundle;
57 import org.openide.windows.Mode;
58 import org.openide.windows.RetainLocation;
59 import org.openide.windows.TopComponent;
60 import org.openide.windows.WindowManager;
88 @TopComponent.Description(
89 preferredID =
"TimeLineTopComponent",
91 persistenceType = TopComponent.PERSISTENCE_NEVER)
92 @TopComponent.Registration(mode =
"timeline", openAtStartup =
false)
93 @RetainLocation(
"timeline")
94 @SuppressWarnings(
"PMD.SingularField")
97 private static final long serialVersionUID = 1L;
107 private final ExplorerManager explorerManager = new ExplorerManager();
114 private final ModifiableProxyLookup proxyLookup = new ModifiableProxyLookup();
116 private final PropertyChangeListener focusPropertyListener = new PropertyChangeListener() {
141 public void propertyChange(
final PropertyChangeEvent focusEvent) {
142 if (focusEvent.getPropertyName().equalsIgnoreCase(
"focusOwner")) {
143 final Component newFocusOwner = (Component) focusEvent.getNewValue();
145 if (newFocusOwner == null) {
148 if (isDescendingFrom(newFocusOwner, contentViewerPanel)) {
150 proxyLookup.setNewLookups(createLookup(contentViewerPanel.getExplorerManager(), getActionMap()));
153 proxyLookup.setNewLookups(createLookup(explorerManager, getActionMap()));
160 @NbBundle.Messages({
"TimelineTopComponent.selectedEventListener.errorMsg=There was a problem getting the content for the selected event."})
161 private final InvalidationListener selectedEventsListener =
new InvalidationListener() {
170 public void invalidated(Observable observable) {
173 List<Long> selectedEventIDs = ImmutableList.copyOf(controller.getSelectedEventIDs());
176 switch (controller.getViewMode()) {
181 for (
int i = 0; i < selectedEventIDs.size(); i++) {
184 Children children =
new Children.Array();
185 children.add(childArray);
187 SwingUtilities.invokeLater(() -> {
189 explorerManager.setRootContext(
new AbstractNode(children));
192 explorerManager.setSelectedNodes(childArray);
193 }
catch (PropertyVetoException ex) {
195 logger.log(Level.SEVERE,
"Selecting the event node was vetoed.", ex);
198 if (childArray.length == 1) {
199 contentViewerPanel.setNode(childArray[0]);
201 contentViewerPanel.setNode(null);
204 }
catch (TskCoreException ex) {
205 logger.log(Level.SEVERE,
"Failed to lookup Sleuthkit object backing a SingleEvent.", ex);
206 Platform.runLater(() -> {
207 Notifications.create()
208 .owner(jFXViewPanel.getScene().getWindow())
209 .text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg())
219 SwingUtilities.invokeLater(() -> {
220 dataResultPanel.setPath(getResultViewerSummaryString());
221 dataResultPanel.setNode(tableFilterNode);
225 throw new UnsupportedOperationException(
"Unknown view mode: " + controller.getViewMode());
231 switch (controller.getViewMode()) {
238 SwingUtilities.invokeLater(() -> {
239 splitYPane.remove(contentViewerPanel);
240 if (horizontalSplitPane.getParent() != splitYPane) {
241 splitYPane.setBottomComponent(horizontalSplitPane);
242 horizontalSplitPane.setRightComponent(contentViewerPanel);
251 SwingUtilities.invokeLater(() -> splitYPane.setBottomComponent(contentViewerPanel));
254 throw new UnsupportedOperationException(
"Unknown ViewMode: " + controller.getViewMode());
267 associateLookup(proxyLookup);
280 horizontalSplitPane.setLeftComponent(dataResultPanel);
281 horizontalSplitPane.setRightComponent(contentViewerPanel);
283 dataResultPanel.
open();
284 contentViewerPanel.initialize();
295 this.controller = controller;
297 Platform.runLater(this::initFXComponents);
308 KeyboardFocusManager.getCurrentKeyboardFocusManager()
309 .addPropertyChangeListener(
"focusOwner", focusPropertyListener);
316 "TimeLineTopComponent.eventsTab.name=Events",
317 "TimeLineTopComponent.filterTab.name=Filters"})
318 @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
319 void initFXComponents() {
321 final TimeZonePanel timeZonePanel =
new TimeZonePanel();
322 VBox.setVgrow(timeZonePanel, Priority.SOMETIMES);
323 HistoryToolBar historyToolBar =
new HistoryToolBar(controller);
324 final ZoomSettingsPane zoomSettingsPane =
new ZoomSettingsPane(controller);
327 final Tab filterTab =
new Tab(Bundle.TimeLineTopComponent_filterTab_name(),
new FilterSetPanel(controller));
328 filterTab.setClosable(
false);
329 filterTab.setGraphic(
new ImageView(
"org/sleuthkit/autopsy/timeline/images/funnel.png"));
332 final EventsTree eventsTree =
new EventsTree(controller);
333 final Tab eventsTreeTab =
new Tab(Bundle.TimeLineTopComponent_eventsTab_name(), eventsTree);
334 eventsTreeTab.setClosable(
false);
335 eventsTreeTab.setGraphic(
new ImageView(
"org/sleuthkit/autopsy/timeline/images/timeline_marker.png"));
336 eventsTreeTab.disableProperty().bind(controller.viewModeProperty().isNotEqualTo(ViewMode.DETAIL));
340 final TabPane leftTabPane =
new TabPane(filterTab);
341 VBox.setVgrow(leftTabPane, Priority.ALWAYS);
342 controller.viewModeProperty().addListener(viewMode -> {
343 if (controller.getViewMode() != ViewMode.DETAIL) {
345 leftTabPane.getSelectionModel().select(filterTab);
350 final VBox leftVBox =
new VBox(5, timeZonePanel, historyToolBar, zoomSettingsPane, leftTabPane);
351 SplitPane.setResizableWithParent(leftVBox, Boolean.FALSE);
353 final ViewFrame viewFrame =
new ViewFrame(controller, eventsTree);
354 final SplitPane mainSplitPane =
new SplitPane(leftVBox, viewFrame);
355 mainSplitPane.setDividerPositions(0);
357 final Scene scene =
new Scene(mainSplitPane);
358 scene.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
359 if (new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) {
360 new Back(controller).handle(null);
361 }
else if (
new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) {
362 new Forward(controller).handle(null);
367 jFXViewPanel.setScene(scene);
368 jFXstatusPanel.setScene(
new Scene(
new StatusBar(controller)));
378 return modes.stream().filter(mode -> mode.getName().equals(
"timeline") || mode.getName().equals(
"ImageGallery"))
379 .collect(Collectors.toList());
390 jFXstatusPanel =
new javafx.embed.swing.JFXPanel();
391 splitYPane =
new javax.swing.JSplitPane();
392 jFXViewPanel =
new javafx.embed.swing.JFXPanel();
393 horizontalSplitPane =
new javax.swing.JSplitPane();
394 leftFillerPanel =
new javax.swing.JPanel();
395 rightfillerPanel =
new javax.swing.JPanel();
397 jFXstatusPanel.setPreferredSize(
new java.awt.Dimension(100, 16));
399 splitYPane.setDividerLocation(420);
400 splitYPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
401 splitYPane.setResizeWeight(0.9);
402 splitYPane.setPreferredSize(
new java.awt.Dimension(1024, 400));
403 splitYPane.setLeftComponent(jFXViewPanel);
405 horizontalSplitPane.setDividerLocation(600);
406 horizontalSplitPane.setResizeWeight(0.5);
407 horizontalSplitPane.setPreferredSize(
new java.awt.Dimension(1200, 300));
408 horizontalSplitPane.setRequestFocusEnabled(
false);
410 javax.swing.GroupLayout leftFillerPanelLayout =
new javax.swing.GroupLayout(leftFillerPanel);
411 leftFillerPanel.setLayout(leftFillerPanelLayout);
412 leftFillerPanelLayout.setHorizontalGroup(
413 leftFillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
414 .addGap(0, 599, Short.MAX_VALUE)
416 leftFillerPanelLayout.setVerticalGroup(
417 leftFillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
418 .addGap(0, 54, Short.MAX_VALUE)
421 horizontalSplitPane.setLeftComponent(leftFillerPanel);
423 javax.swing.GroupLayout rightfillerPanelLayout =
new javax.swing.GroupLayout(rightfillerPanel);
424 rightfillerPanel.setLayout(rightfillerPanelLayout);
425 rightfillerPanelLayout.setHorizontalGroup(
426 rightfillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
427 .addGap(0, 364, Short.MAX_VALUE)
429 rightfillerPanelLayout.setVerticalGroup(
430 rightfillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
431 .addGap(0, 54, Short.MAX_VALUE)
434 horizontalSplitPane.setRightComponent(rightfillerPanel);
436 splitYPane.setRightComponent(horizontalSplitPane);
438 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
439 this.setLayout(layout);
440 layout.setHorizontalGroup(
441 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
442 .addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 972, Short.MAX_VALUE)
443 .addComponent(jFXstatusPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
445 layout.setVerticalGroup(
446 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
447 .addGroup(layout.createSequentialGroup()
448 .addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 482, Short.MAX_VALUE)
450 .addComponent(jFXstatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE))
463 @NbBundle.Messages ({
464 "Timeline.old.version= This Case was created with an older version of Autopsy.\nThe Timeline with not show events from data sources added with the older version of Autopsy"
468 super.componentOpened();
469 WindowManager.getDefault().setTopComponentFloating(
this,
true);
472 KeyboardFocusManager.getCurrentKeyboardFocusManager()
473 .addPropertyChangeListener(
"focusOwner", focusPropertyListener);
476 int major = version.getMajor();
477 int minor = version.getMinor();
479 if(major < 8 || (major == 8 && minor <= 2)) {
480 Platform.runLater(() -> {
481 Notifications.create()
482 .owner(jFXViewPanel.getScene().getWindow())
483 .text(Bundle.Timeline_old_version()).showInformation();
490 super.componentClosed();
491 KeyboardFocusManager.getCurrentKeyboardFocusManager()
492 .removePropertyChangeListener(
"focusOwner", focusPropertyListener);
497 return explorerManager;
507 "# {0} - start of date range",
508 "# {1} - end of date range",
509 "TimeLineResultView.startDateToEndDate.text={0} to {1}"})
511 Interval selectedTimeRange = controller.getSelectedTimeRange();
512 if (selectedTimeRange == null) {
516 String start = selectedTimeRange.getStart()
518 .toString(zonedFormatter);
519 String end = selectedTimeRange.getEnd()
521 .toString(zonedFormatter);
522 return Bundle.TimeLineResultView_startDateToEndDate_text(start, end);
537 private final ExplorerManager explorerManager =
new ExplorerManager();
541 super(
new BorderLayout());
547 return explorerManager;
568 add(wrapped, BorderLayout.CENTER);
TimeLineTopComponent(TimeLineController controller)
static final KeyStroke BOOKMARK_SHORTCUT
void setNode(Node selectedNode)
String getResultViewerSummaryString()
DataContentExplorerPanel()
static EventNode createEventNode(final Long eventID, EventsModel eventsModel)
javafx.embed.swing.JFXPanel jFXstatusPanel
ExplorerManager getExplorerManager()
javax.swing.JSplitPane splitYPane
static DataResultPanel createInstanceUninitialized(String title, String description, Node currentRootNode, int childNodeCount, DataContent customContentView)
static final KeyStroke EXTERNAL_VIEWER_SHORTCUT
static ReadOnlyObjectProperty< TimeZone > timeZoneProperty()
void setNode(Node selectedNode)
List< Mode > availableModes(List< Mode > modes)
static DataContentPanel createInstance()
javafx.embed.swing.JFXPanel jFXViewPanel
SleuthkitCase getSleuthkitCase()
synchronized ObservableList< Long > getSelectedEventIDs()
static DateTimeZone getJodaTimeZone()
javax.swing.JSplitPane horizontalSplitPane
static synchronized ExternalViewerShortcutAction getInstance()
synchronized ReadOnlyObjectProperty< ViewMode > viewModeProperty()
static Case getCurrentCase()
void propertyChange(PropertyChangeEvent evt)
ExplorerManager getExplorerManager()
synchronized static Logger getLogger(String name)
final DataContentPanel wrapped
void propertyChange(PropertyChangeEvent evt)
static DateTimeFormatter getZonedFormatter()
javax.swing.JPanel rightfillerPanel
javax.swing.JPanel leftFillerPanel