19 package org.sleuthkit.autopsy.timeline;
 
   21 import java.awt.BorderLayout;
 
   22 import java.awt.Component;
 
   23 import java.awt.KeyboardFocusManager;
 
   24 import java.beans.PropertyChangeEvent;
 
   25 import java.beans.PropertyChangeListener;
 
   26 import java.beans.PropertyVetoException;
 
   27 import java.util.List;
 
   28 import java.util.logging.Level;
 
   29 import java.util.stream.Collectors;
 
   30 import javafx.application.Platform;
 
   31 import javafx.beans.InvalidationListener;
 
   32 import javafx.beans.Observable;
 
   33 import javafx.scene.Scene;
 
   34 import javafx.scene.control.SplitPane;
 
   35 import javafx.scene.control.Tab;
 
   36 import javafx.scene.control.TabPane;
 
   37 import javafx.scene.image.ImageView;
 
   38 import javafx.scene.input.KeyCode;
 
   39 import javafx.scene.input.KeyCodeCombination;
 
   40 import javafx.scene.input.KeyEvent;
 
   41 import javafx.scene.layout.Priority;
 
   42 import javafx.scene.layout.VBox;
 
   43 import javax.swing.JComponent;
 
   44 import javax.swing.JPanel;
 
   45 import javax.swing.SwingUtilities;
 
   46 import static javax.swing.SwingUtilities.isDescendingFrom;
 
   47 import org.controlsfx.control.Notifications;
 
   48 import org.joda.time.Interval;
 
   49 import org.joda.time.format.DateTimeFormatter;
 
   50 import org.openide.explorer.ExplorerManager;
 
   51 import static org.openide.explorer.ExplorerUtils.createLookup;
 
   52 import org.openide.nodes.AbstractNode;
 
   53 import org.openide.nodes.Children;
 
   54 import org.openide.nodes.Node;
 
   55 import org.openide.util.NbBundle;
 
   56 import org.openide.windows.Mode;
 
   57 import org.openide.windows.RetainLocation;
 
   58 import org.openide.windows.TopComponent;
 
   59 import org.openide.windows.WindowManager;
 
   85 @TopComponent.Description(
 
   86         preferredID = 
"TimeLineTopComponent",
 
   88         persistenceType = TopComponent.PERSISTENCE_NEVER)
 
   89 @TopComponent.Registration(mode = 
"timeline", openAtStartup = 
false)
 
   90 @RetainLocation(
"timeline")
 
   91 @SuppressWarnings(
"PMD.SingularField") 
 
  103     private final ExplorerManager explorerManager = new ExplorerManager();
 
  110     private final ModifiableProxyLookup proxyLookup = new ModifiableProxyLookup();
 
  112     private final PropertyChangeListener focusPropertyListener = new PropertyChangeListener() {
 
  137         public void propertyChange(
final PropertyChangeEvent focusEvent) {
 
  138             if (focusEvent.getPropertyName().equalsIgnoreCase(
"focusOwner")) {
 
  139                 final Component newFocusOwner = (Component) focusEvent.getNewValue();
 
  141                 if (newFocusOwner == null) {
 
  144                 if (isDescendingFrom(newFocusOwner, contentViewerPanel)) {
 
  146                     proxyLookup.setNewLookups(createLookup(contentViewerPanel.getExplorerManager(), getActionMap()));
 
  149                     proxyLookup.setNewLookups(createLookup(explorerManager, getActionMap()));
 
  156     @NbBundle.Messages({
"TimelineTopComponent.selectedEventListener.errorMsg=There was a problem getting the content for the selected event."})
 
  157     private final InvalidationListener selectedEventsListener = 
new InvalidationListener() {
 
  166         public void invalidated(Observable observable) {
 
  170             switch (controller.getViewMode()) {
 
  175                         for (
int i = 0; i < selectedEventIDs.size(); i++) {
 
  178                         Children children = 
new Children.Array();
 
  179                         children.add(childArray);
 
  181                         SwingUtilities.invokeLater(() -> {
 
  183                             explorerManager.setRootContext(
new AbstractNode(children));
 
  186                                 explorerManager.setSelectedNodes(childArray);
 
  187                             } 
catch (PropertyVetoException ex) {
 
  189                                 logger.log(Level.SEVERE, 
"Selecting the event node was vetoed.", ex); 
 
  192                             if (childArray.length == 1) {
 
  193                                 contentViewerPanel.setNode(childArray[0]);
 
  195                                 contentViewerPanel.setNode(null);
 
  200                         logger.log(Level.SEVERE, 
"There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); 
 
  201                     } 
catch (TskCoreException ex) {
 
  202                         logger.log(Level.SEVERE, 
"Failed to lookup Sleuthkit object backing a SingleEvent.", ex); 
 
  203                         Platform.runLater(() -> {
 
  204                             Notifications.create()
 
  205                                     .owner(jFXViewPanel.getScene().getWindow())
 
  206                                     .text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg())
 
  216                     SwingUtilities.invokeLater(() -> {
 
  217                         dataResultPanel.setPath(getResultViewerSummaryString());
 
  218                         dataResultPanel.setNode(tableFilterNode);
 
  222                     throw new UnsupportedOperationException(
"Unknown view mode: " + controller.getViewMode());
 
  228         switch (controller.getViewMode()) {
 
  235                 SwingUtilities.invokeLater(() -> {
 
  236                     splitYPane.remove(contentViewerPanel);
 
  237                     if (horizontalSplitPane.getParent() != splitYPane) {
 
  238                         splitYPane.setBottomComponent(horizontalSplitPane);
 
  239                         horizontalSplitPane.setRightComponent(contentViewerPanel);
 
  248                 SwingUtilities.invokeLater(() -> splitYPane.setBottomComponent(contentViewerPanel));
 
  251                 throw new UnsupportedOperationException(
"Unknown ViewMode: " + controller.getViewMode());
 
  262         associateLookup(proxyLookup);
 
  269         this.controller = controller;
 
  276         horizontalSplitPane.setLeftComponent(dataResultPanel);
 
  277         horizontalSplitPane.setRightComponent(contentViewerPanel);
 
  279         dataResultPanel.
open(); 
 
  280         contentViewerPanel.initialize();
 
  282         Platform.runLater(this::initFXComponents);
 
  293         KeyboardFocusManager.getCurrentKeyboardFocusManager()
 
  294                 .addPropertyChangeListener(
"focusOwner", focusPropertyListener);
 
  301         "TimeLineTopComponent.eventsTab.name=Events",
 
  302         "TimeLineTopComponent.filterTab.name=Filters"})
 
  303     @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
 
  304     void initFXComponents() {
 
  306         final TimeZonePanel timeZonePanel = 
new TimeZonePanel();
 
  307         VBox.setVgrow(timeZonePanel, Priority.SOMETIMES);
 
  308         HistoryToolBar historyToolBar = 
new HistoryToolBar(controller);
 
  309         final ZoomSettingsPane zoomSettingsPane = 
new ZoomSettingsPane(controller);
 
  312         final Tab filterTab = 
new Tab(Bundle.TimeLineTopComponent_filterTab_name(), 
new FilterSetPanel(controller));
 
  313         filterTab.setClosable(
false);
 
  314         filterTab.setGraphic(
new ImageView(
"org/sleuthkit/autopsy/timeline/images/funnel.png")); 
 
  317         final EventsTree eventsTree = 
new EventsTree(controller);
 
  318         final Tab eventsTreeTab = 
new Tab(Bundle.TimeLineTopComponent_eventsTab_name(), eventsTree);
 
  319         eventsTreeTab.setClosable(
false);
 
  320         eventsTreeTab.setGraphic(
new ImageView(
"org/sleuthkit/autopsy/timeline/images/timeline_marker.png")); 
 
  321         eventsTreeTab.disableProperty().bind(controller.viewModeProperty().isNotEqualTo(ViewMode.DETAIL));
 
  323         final TabPane leftTabPane = 
new TabPane(filterTab, eventsTreeTab);
 
  324         VBox.setVgrow(leftTabPane, Priority.ALWAYS);
 
  325         controller.viewModeProperty().addListener(viewMode -> {
 
  326             if (controller.getViewMode() != ViewMode.DETAIL) {
 
  328                 leftTabPane.getSelectionModel().select(filterTab);
 
  333         final VBox leftVBox = 
new VBox(5, timeZonePanel, historyToolBar, zoomSettingsPane, leftTabPane);
 
  334         SplitPane.setResizableWithParent(leftVBox, Boolean.FALSE);
 
  336         final ViewFrame viewFrame = 
new ViewFrame(controller, eventsTree);
 
  337         final SplitPane mainSplitPane = 
new SplitPane(leftVBox, viewFrame);
 
  338         mainSplitPane.setDividerPositions(0);
 
  340         final Scene scene = 
new Scene(mainSplitPane);
 
  341         scene.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
 
  342             if (new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) {
 
  343                 new Back(controller).handle(null);
 
  344             } 
else if (
new KeyCodeCombination(KeyCode.BACK_SPACE).match(keyEvent)) {
 
  345                 new Back(controller).handle(null);
 
  346             } 
else if (
new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) {
 
  347                 new Forward(controller).handle(null);
 
  348             } 
else if (
new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCodeCombination.SHIFT_DOWN).match(keyEvent)) {
 
  349                 new Forward(controller).handle(null);
 
  354         jFXViewPanel.setScene(scene);
 
  355         jFXstatusPanel.setScene(
new Scene(
new StatusBar(controller)));
 
  365         return modes.stream().filter(mode -> mode.getName().equals(
"timeline") || mode.getName().equals(
"ImageGallery"))
 
  366                 .collect(Collectors.toList());
 
  377         jFXstatusPanel = 
new javafx.embed.swing.JFXPanel();
 
  378         splitYPane = 
new javax.swing.JSplitPane();
 
  379         jFXViewPanel = 
new javafx.embed.swing.JFXPanel();
 
  380         horizontalSplitPane = 
new javax.swing.JSplitPane();
 
  381         leftFillerPanel = 
new javax.swing.JPanel();
 
  382         rightfillerPanel = 
new javax.swing.JPanel();
 
  384         jFXstatusPanel.setPreferredSize(
new java.awt.Dimension(100, 16));
 
  386         splitYPane.setDividerLocation(420);
 
  387         splitYPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
 
  388         splitYPane.setResizeWeight(0.9);
 
  389         splitYPane.setPreferredSize(
new java.awt.Dimension(1024, 400));
 
  390         splitYPane.setLeftComponent(jFXViewPanel);
 
  392         horizontalSplitPane.setDividerLocation(600);
 
  393         horizontalSplitPane.setResizeWeight(0.5);
 
  394         horizontalSplitPane.setPreferredSize(
new java.awt.Dimension(1200, 300));
 
  395         horizontalSplitPane.setRequestFocusEnabled(
false);
 
  397         javax.swing.GroupLayout leftFillerPanelLayout = 
new javax.swing.GroupLayout(leftFillerPanel);
 
  398         leftFillerPanel.setLayout(leftFillerPanelLayout);
 
  399         leftFillerPanelLayout.setHorizontalGroup(
 
  400             leftFillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  401             .addGap(0, 599, Short.MAX_VALUE)
 
  403         leftFillerPanelLayout.setVerticalGroup(
 
  404             leftFillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  405             .addGap(0, 54, Short.MAX_VALUE)
 
  408         horizontalSplitPane.setLeftComponent(leftFillerPanel);
 
  410         javax.swing.GroupLayout rightfillerPanelLayout = 
new javax.swing.GroupLayout(rightfillerPanel);
 
  411         rightfillerPanel.setLayout(rightfillerPanelLayout);
 
  412         rightfillerPanelLayout.setHorizontalGroup(
 
  413             rightfillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  414             .addGap(0, 364, Short.MAX_VALUE)
 
  416         rightfillerPanelLayout.setVerticalGroup(
 
  417             rightfillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  418             .addGap(0, 54, Short.MAX_VALUE)
 
  421         horizontalSplitPane.setRightComponent(rightfillerPanel);
 
  423         splitYPane.setRightComponent(horizontalSplitPane);
 
  425         javax.swing.GroupLayout layout = 
new javax.swing.GroupLayout(
this);
 
  426         this.setLayout(layout);
 
  427         layout.setHorizontalGroup(
 
  428             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  429             .addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 972, Short.MAX_VALUE)
 
  430             .addComponent(jFXstatusPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  432         layout.setVerticalGroup(
 
  433             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  434             .addGroup(layout.createSequentialGroup()
 
  435                 .addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 482, Short.MAX_VALUE)
 
  437                 .addComponent(jFXstatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE))
 
  452         super.componentOpened();
 
  453         WindowManager.getDefault().setTopComponentFloating(
this, 
true);
 
  456         KeyboardFocusManager.getCurrentKeyboardFocusManager()
 
  457                 .addPropertyChangeListener(
"focusOwner", focusPropertyListener);
 
  462         super.componentClosed();
 
  463         KeyboardFocusManager.getCurrentKeyboardFocusManager()
 
  464                 .removePropertyChangeListener(
"focusOwner", focusPropertyListener);
 
  469         return explorerManager;
 
  479         "# {0} - start of date range",
 
  480         "# {1} - end of date range",
 
  481         "TimeLineResultView.startDateToEndDate.text={0} to {1}"})
 
  483         Interval selectedTimeRange = controller.getSelectedTimeRange();
 
  484         if (selectedTimeRange == null) {
 
  488             String start = selectedTimeRange.getStart()
 
  490                     .toString(zonedFormatter);
 
  491             String end = selectedTimeRange.getEnd()
 
  493                     .toString(zonedFormatter);
 
  494             return Bundle.TimeLineResultView_startDateToEndDate_text(start, end);
 
  509         private final ExplorerManager explorerManager = 
new ExplorerManager();
 
  513             super(
new BorderLayout());
 
  519             return explorerManager;
 
  540             add(wrapped, BorderLayout.CENTER);
 
TimeLineTopComponent(TimeLineController controller)
static final KeyStroke BOOKMARK_SHORTCUT
static EventNode createEventNode(final Long eventID, FilteredEventsModel eventsModel)
void setNode(Node selectedNode)
static ReadOnlyObjectProperty< TimeZone > getTimeZone()
String getResultViewerSummaryString()
DataContentExplorerPanel()
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
void setNode(Node selectedNode)
List< Mode > availableModes(List< Mode > modes)
static DataContentPanel createInstance()
javafx.embed.swing.JFXPanel jFXViewPanel
synchronized ObservableList< Long > getSelectedEventIDs()
static DateTimeZone getJodaTimeZone()
javax.swing.JSplitPane horizontalSplitPane
static synchronized ExternalViewerShortcutAction getInstance()
synchronized ReadOnlyObjectProperty< ViewMode > viewModeProperty()
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