19package org.sleuthkit.autopsy.timeline;
21import com.google.common.collect.ImmutableList;
22import java.awt.BorderLayout;
23import java.awt.Component;
24import java.awt.KeyboardFocusManager;
25import java.beans.PropertyChangeEvent;
26import java.beans.PropertyChangeListener;
27import java.beans.PropertyVetoException;
29import java.util.logging.Level;
30import java.util.stream.Collectors;
31import javafx.application.Platform;
32import javafx.beans.InvalidationListener;
33import javafx.beans.Observable;
34import javafx.scene.Scene;
35import javafx.scene.control.SplitPane;
36import javafx.scene.control.Tab;
37import javafx.scene.control.TabPane;
38import javafx.scene.image.ImageView;
39import javafx.scene.input.KeyCode;
40import javafx.scene.input.KeyCodeCombination;
41import javafx.scene.input.KeyEvent;
42import javafx.scene.layout.Priority;
43import javafx.scene.layout.VBox;
44import javax.swing.JComponent;
45import javax.swing.JPanel;
46import javax.swing.SwingUtilities;
47import static javax.swing.SwingUtilities.isDescendingFrom;
48import org.controlsfx.control.Notifications;
49import org.joda.time.Interval;
50import org.joda.time.format.DateTimeFormatter;
51import org.openide.explorer.ExplorerManager;
52import static org.openide.explorer.ExplorerUtils.createLookup;
53import org.openide.nodes.AbstractNode;
54import org.openide.nodes.Children;
55import org.openide.nodes.Node;
56import org.openide.util.NbBundle;
57import org.openide.windows.Mode;
58import org.openide.windows.RetainLocation;
59import org.openide.windows.TopComponent;
60import org.openide.windows.WindowManager;
61import org.sleuthkit.autopsy.actions.AddBookmarkTagAction;
62import org.sleuthkit.autopsy.casemodule.Case;
63import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
64import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
65import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
66import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
67import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
68import org.sleuthkit.autopsy.coreutils.Logger;
69import org.sleuthkit.autopsy.coreutils.ThreadConfined;
70import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
71import org.sleuthkit.autopsy.timeline.actions.Back;
72import org.sleuthkit.autopsy.timeline.actions.Forward;
73import org.sleuthkit.autopsy.timeline.explorernodes.EventNode;
74import org.sleuthkit.autopsy.timeline.explorernodes.EventRootNode;
75import org.sleuthkit.autopsy.timeline.ui.HistoryToolBar;
76import org.sleuthkit.autopsy.timeline.ui.StatusBar;
77import org.sleuthkit.autopsy.timeline.ui.TimeZonePanel;
78import org.sleuthkit.autopsy.timeline.ui.ViewFrame;
79import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree;
80import org.sleuthkit.autopsy.timeline.ui.filtering.FilterSetPanel;
81import org.sleuthkit.autopsy.timeline.zooming.ZoomSettingsPane;
82import org.sleuthkit.datamodel.TskCoreException;
83import org.sleuthkit.datamodel.VersionNumber;
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")
114 private final ModifiableProxyLookup
proxyLookup = new ModifiableProxyLookup();
141 public void propertyChange(
final PropertyChangeEvent focusEvent) {
142 if (focusEvent.getPropertyName().equalsIgnoreCase(
"focusOwner")) {
143 final Component newFocusOwner = (Component) focusEvent.getNewValue();
145 if (newFocusOwner ==
null) {
160 @NbBundle.Messages({
"TimelineTopComponent.selectedEventListener.errorMsg=There was a problem getting the content for the selected event."})
170 public void invalidated(Observable observable) {
173 List<Long> selectedEventIDs = ImmutableList.copyOf(
controller.getSelectedEventIDs());
181 for (
int i = 0; i < selectedEventIDs.size(); i++) {
184 Children children =
new Children.Array();
185 children.add(childArray);
187 SwingUtilities.invokeLater(() -> {
193 }
catch (PropertyVetoException ex) {
195 logger.log(Level.SEVERE,
"Selecting the event node was vetoed.", ex);
198 if (childArray.length == 1) {
204 }
catch (TskCoreException ex) {
205 logger.log(Level.SEVERE,
"Failed to lookup Sleuthkit object backing a SingleEvent.", ex);
206 Platform.runLater(() -> {
207 Notifications.create()
209 .text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg())
219 SwingUtilities.invokeLater(() -> {
225 throw new UnsupportedOperationException(
"Unknown view mode: " +
controller.getViewMode());
238 SwingUtilities.invokeLater(() -> {
254 throw new UnsupportedOperationException(
"Unknown ViewMode: " +
controller.getViewMode());
297 Platform.runLater(this::initFXComponents);
308 KeyboardFocusManager.getCurrentKeyboardFocusManager()
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());
400 splitYPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
402 splitYPane.setPreferredSize(
new java.awt.Dimension(1024, 400));
410 javax.swing.GroupLayout leftFillerPanelLayout =
new javax.swing.GroupLayout(
leftFillerPanel);
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)
423 javax.swing.GroupLayout rightfillerPanelLayout =
new javax.swing.GroupLayout(
rightfillerPanel);
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)
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()
476 int major = version.getMajor();
477 int minor = version.getMinor();
479 if(major < 8 || (major == 8 && minor <= 2)) {
480 Platform.runLater(() -> {
481 Notifications.create()
483 .text(Bundle.Timeline_old_version()).showInformation();
490 super.componentClosed();
491 KeyboardFocusManager.getCurrentKeyboardFocusManager()
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);
541 super(
new BorderLayout());
568 add(
wrapped, BorderLayout.CENTER);
static final KeyStroke BOOKMARK_SHORTCUT
SleuthkitCase getSleuthkitCase()
static Case getCurrentCase()
static DataContentPanel createInstance()
static DataResultPanel createInstanceUninitialized(String title, String description, Node currentRootNode, int childNodeCount, DataContent customContentView)
synchronized static Logger getLogger(String name)
static final KeyStroke EXTERNAL_VIEWER_SHORTCUT
static synchronized ExternalViewerShortcutAction getInstance()
static DateTimeZone getJodaTimeZone()
static ReadOnlyObjectProperty< TimeZone > timeZoneProperty()
static DateTimeFormatter getZonedFormatter()
DataContentExplorerPanel()
void setNode(Node selectedNode)
ExplorerManager getExplorerManager()
void propertyChange(PropertyChangeEvent evt)
final ExplorerManager explorerManager
final DataContentPanel wrapped
javax.swing.JPanel leftFillerPanel
String getResultViewerSummaryString()
javax.swing.JSplitPane splitYPane
final DataResultPanel dataResultPanel
javafx.embed.swing.JFXPanel jFXViewPanel
static final long serialVersionUID
javax.swing.JPanel rightfillerPanel
final PropertyChangeListener focusPropertyListener
final InvalidationListener selectedEventsListener
javafx.embed.swing.JFXPanel jFXstatusPanel
List< Mode > availableModes(List< Mode > modes)
final DataContentExplorerPanel contentViewerPanel
TimeLineController controller
TimeLineTopComponent(TimeLineController controller)
final ExplorerManager explorerManager
final ModifiableProxyLookup proxyLookup
static final Logger logger
javax.swing.JSplitPane horizontalSplitPane
ExplorerManager getExplorerManager()
static EventNode createEventNode(final Long eventID, EventsModel eventsModel)