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);
 
  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);
 
  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)
 
CaseDbSchemaVersionNumber getDBSchemaCreationVersion()
 
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