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")
94 private static final long serialVersionUID = 1L;
104 private final ExplorerManager explorerManager = new ExplorerManager();
111 private final ModifiableProxyLookup proxyLookup = new ModifiableProxyLookup();
113 private final PropertyChangeListener focusPropertyListener = new PropertyChangeListener() {
138 public void propertyChange(
final PropertyChangeEvent focusEvent) {
139 if (focusEvent.getPropertyName().equalsIgnoreCase(
"focusOwner")) {
140 final Component newFocusOwner = (Component) focusEvent.getNewValue();
142 if (newFocusOwner == null) {
145 if (isDescendingFrom(newFocusOwner, contentViewerPanel)) {
147 proxyLookup.setNewLookups(createLookup(contentViewerPanel.getExplorerManager(), getActionMap()));
150 proxyLookup.setNewLookups(createLookup(explorerManager, getActionMap()));
157 @NbBundle.Messages({
"TimelineTopComponent.selectedEventListener.errorMsg=There was a problem getting the content for the selected event."})
158 private final InvalidationListener selectedEventsListener =
new InvalidationListener() {
167 public void invalidated(Observable observable) {
171 switch (controller.getViewMode()) {
176 for (
int i = 0; i < selectedEventIDs.size(); i++) {
179 Children children =
new Children.Array();
180 children.add(childArray);
182 SwingUtilities.invokeLater(() -> {
184 explorerManager.setRootContext(
new AbstractNode(children));
187 explorerManager.setSelectedNodes(childArray);
188 }
catch (PropertyVetoException ex) {
190 logger.log(Level.SEVERE,
"Selecting the event node was vetoed.", ex);
193 if (childArray.length == 1) {
194 contentViewerPanel.setNode(childArray[0]);
196 contentViewerPanel.setNode(null);
201 logger.log(Level.SEVERE,
"There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex);
202 }
catch (TskCoreException ex) {
203 logger.log(Level.SEVERE,
"Failed to lookup Sleuthkit object backing a SingleEvent.", ex);
204 Platform.runLater(() -> {
205 Notifications.create()
206 .owner(jFXViewPanel.getScene().getWindow())
207 .text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg())
217 SwingUtilities.invokeLater(() -> {
218 dataResultPanel.setPath(getResultViewerSummaryString());
219 dataResultPanel.setNode(tableFilterNode);
223 throw new UnsupportedOperationException(
"Unknown view mode: " + controller.getViewMode());
229 switch (controller.getViewMode()) {
236 SwingUtilities.invokeLater(() -> {
237 splitYPane.remove(contentViewerPanel);
238 if (horizontalSplitPane.getParent() != splitYPane) {
239 splitYPane.setBottomComponent(horizontalSplitPane);
240 horizontalSplitPane.setRightComponent(contentViewerPanel);
249 SwingUtilities.invokeLater(() -> splitYPane.setBottomComponent(contentViewerPanel));
252 throw new UnsupportedOperationException(
"Unknown ViewMode: " + controller.getViewMode());
264 associateLookup(proxyLookup);
277 horizontalSplitPane.setLeftComponent(dataResultPanel);
278 horizontalSplitPane.setRightComponent(contentViewerPanel);
280 dataResultPanel.
open();
281 contentViewerPanel.initialize();
292 this.controller = controller;
294 Platform.runLater(this::initFXComponents);
305 KeyboardFocusManager.getCurrentKeyboardFocusManager()
306 .addPropertyChangeListener(
"focusOwner", focusPropertyListener);
313 "TimeLineTopComponent.eventsTab.name=Events",
314 "TimeLineTopComponent.filterTab.name=Filters"})
315 @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
316 void initFXComponents() {
318 final TimeZonePanel timeZonePanel =
new TimeZonePanel();
319 VBox.setVgrow(timeZonePanel, Priority.SOMETIMES);
320 HistoryToolBar historyToolBar =
new HistoryToolBar(controller);
321 final ZoomSettingsPane zoomSettingsPane =
new ZoomSettingsPane(controller);
324 final Tab filterTab =
new Tab(Bundle.TimeLineTopComponent_filterTab_name(),
new FilterSetPanel(controller));
325 filterTab.setClosable(
false);
326 filterTab.setGraphic(
new ImageView(
"org/sleuthkit/autopsy/timeline/images/funnel.png"));
329 final EventsTree eventsTree =
new EventsTree(controller);
330 final Tab eventsTreeTab =
new Tab(Bundle.TimeLineTopComponent_eventsTab_name(), eventsTree);
331 eventsTreeTab.setClosable(
false);
332 eventsTreeTab.setGraphic(
new ImageView(
"org/sleuthkit/autopsy/timeline/images/timeline_marker.png"));
333 eventsTreeTab.disableProperty().bind(controller.viewModeProperty().isNotEqualTo(ViewMode.DETAIL));
335 final TabPane leftTabPane =
new TabPane(filterTab, eventsTreeTab);
336 VBox.setVgrow(leftTabPane, Priority.ALWAYS);
337 controller.viewModeProperty().addListener(viewMode -> {
338 if (controller.getViewMode() != ViewMode.DETAIL) {
340 leftTabPane.getSelectionModel().select(filterTab);
345 final VBox leftVBox =
new VBox(5, timeZonePanel, historyToolBar, zoomSettingsPane, leftTabPane);
346 SplitPane.setResizableWithParent(leftVBox, Boolean.FALSE);
348 final ViewFrame viewFrame =
new ViewFrame(controller, eventsTree);
349 final SplitPane mainSplitPane =
new SplitPane(leftVBox, viewFrame);
350 mainSplitPane.setDividerPositions(0);
352 final Scene scene =
new Scene(mainSplitPane);
353 scene.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
354 if (new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) {
355 new Back(controller).handle(null);
356 }
else if (
new KeyCodeCombination(KeyCode.BACK_SPACE).match(keyEvent)) {
357 new Back(controller).handle(null);
358 }
else if (
new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) {
359 new Forward(controller).handle(null);
360 }
else if (
new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCodeCombination.SHIFT_DOWN).match(keyEvent)) {
361 new Forward(controller).handle(null);
366 jFXViewPanel.setScene(scene);
367 jFXstatusPanel.setScene(
new Scene(
new StatusBar(controller)));
377 return modes.stream().filter(mode -> mode.getName().equals(
"timeline") || mode.getName().equals(
"ImageGallery"))
378 .collect(Collectors.toList());
389 jFXstatusPanel =
new javafx.embed.swing.JFXPanel();
390 splitYPane =
new javax.swing.JSplitPane();
391 jFXViewPanel =
new javafx.embed.swing.JFXPanel();
392 horizontalSplitPane =
new javax.swing.JSplitPane();
393 leftFillerPanel =
new javax.swing.JPanel();
394 rightfillerPanel =
new javax.swing.JPanel();
396 jFXstatusPanel.setPreferredSize(
new java.awt.Dimension(100, 16));
398 splitYPane.setDividerLocation(420);
399 splitYPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
400 splitYPane.setResizeWeight(0.9);
401 splitYPane.setPreferredSize(
new java.awt.Dimension(1024, 400));
402 splitYPane.setLeftComponent(jFXViewPanel);
404 horizontalSplitPane.setDividerLocation(600);
405 horizontalSplitPane.setResizeWeight(0.5);
406 horizontalSplitPane.setPreferredSize(
new java.awt.Dimension(1200, 300));
407 horizontalSplitPane.setRequestFocusEnabled(
false);
409 javax.swing.GroupLayout leftFillerPanelLayout =
new javax.swing.GroupLayout(leftFillerPanel);
410 leftFillerPanel.setLayout(leftFillerPanelLayout);
411 leftFillerPanelLayout.setHorizontalGroup(
412 leftFillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
413 .addGap(0, 599, Short.MAX_VALUE)
415 leftFillerPanelLayout.setVerticalGroup(
416 leftFillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
417 .addGap(0, 54, Short.MAX_VALUE)
420 horizontalSplitPane.setLeftComponent(leftFillerPanel);
422 javax.swing.GroupLayout rightfillerPanelLayout =
new javax.swing.GroupLayout(rightfillerPanel);
423 rightfillerPanel.setLayout(rightfillerPanelLayout);
424 rightfillerPanelLayout.setHorizontalGroup(
425 rightfillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
426 .addGap(0, 364, Short.MAX_VALUE)
428 rightfillerPanelLayout.setVerticalGroup(
429 rightfillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
430 .addGap(0, 54, Short.MAX_VALUE)
433 horizontalSplitPane.setRightComponent(rightfillerPanel);
435 splitYPane.setRightComponent(horizontalSplitPane);
437 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
438 this.setLayout(layout);
439 layout.setHorizontalGroup(
440 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
441 .addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 972, Short.MAX_VALUE)
442 .addComponent(jFXstatusPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
444 layout.setVerticalGroup(
445 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
446 .addGroup(layout.createSequentialGroup()
447 .addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 482, Short.MAX_VALUE)
449 .addComponent(jFXstatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE))
464 super.componentOpened();
465 WindowManager.getDefault().setTopComponentFloating(
this,
true);
468 KeyboardFocusManager.getCurrentKeyboardFocusManager()
469 .addPropertyChangeListener(
"focusOwner", focusPropertyListener);
474 super.componentClosed();
475 KeyboardFocusManager.getCurrentKeyboardFocusManager()
476 .removePropertyChangeListener(
"focusOwner", focusPropertyListener);
481 return explorerManager;
491 "# {0} - start of date range",
492 "# {1} - end of date range",
493 "TimeLineResultView.startDateToEndDate.text={0} to {1}"})
495 Interval selectedTimeRange = controller.getSelectedTimeRange();
496 if (selectedTimeRange == null) {
500 String start = selectedTimeRange.getStart()
502 .toString(zonedFormatter);
503 String end = selectedTimeRange.getEnd()
505 .toString(zonedFormatter);
506 return Bundle.TimeLineResultView_startDateToEndDate_text(start, end);
521 private final ExplorerManager explorerManager =
new ExplorerManager();
525 super(
new BorderLayout());
531 return explorerManager;
552 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