19 package org.sleuthkit.autopsy.timeline.ui.detailview;
 
   21 import java.util.Arrays;
 
   22 import java.util.MissingResourceException;
 
   23 import java.util.function.Predicate;
 
   24 import javafx.beans.property.SimpleObjectProperty;
 
   25 import javafx.collections.FXCollections;
 
   26 import javafx.collections.ObservableList;
 
   27 import javafx.collections.ObservableSet;
 
   28 import javafx.collections.SetChangeListener;
 
   29 import javafx.geometry.Side;
 
   30 import javafx.scene.chart.Axis;
 
   31 import javafx.scene.control.ContextMenu;
 
   32 import javafx.scene.control.Control;
 
   33 import javafx.scene.control.Skin;
 
   34 import javafx.scene.control.SkinBase;
 
   35 import javafx.scene.image.Image;
 
   36 import javafx.scene.image.ImageView;
 
   37 import javafx.scene.input.MouseEvent;
 
   38 import javafx.scene.layout.Pane;
 
   39 import org.controlsfx.control.MasterDetailPane;
 
   40 import org.controlsfx.control.action.Action;
 
   41 import org.controlsfx.control.action.ActionUtils;
 
   42 import org.joda.time.DateTime;
 
   43 import org.joda.time.Interval;
 
   44 import org.openide.util.NbBundle;
 
   56 final class DetailsChart 
extends Control implements TimeLineChart<DateTime> {
 
   59     private final DateAxis detailsChartDateAxis;
 
   60     private final DateAxis pinnedDateAxis;
 
   61     private final Axis<EventStripe> verticalAxis;
 
   66     private final SimpleObjectProperty<IntervalSelector<? extends DateTime>> intervalSelectorProp = 
new SimpleObjectProperty<>();
 
   71     private final ObservableSet<GuideLine> guideLines = FXCollections.observableSet();
 
   79     private final SimpleObjectProperty<Predicate<EventNodeBase<?>>> highlightPredicate = 
new SimpleObjectProperty<>((x) -> 
false);
 
   84     private final ObservableList<EventNodeBase<?>> selectedNodes;
 
   91     private final ObservableList<TimeLineEvent> nestedEvents = FXCollections.observableArrayList();
 
   97     private final DetailsChartLayoutSettings layoutSettings;
 
  102     private final TimeLineController controller;
 
  107     @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
 
  108     private final ObservableList<EventStripe> rootEventStripes = FXCollections.observableArrayList();
 
  124     DetailsChart(TimeLineController controller, DateAxis detailsChartDateAxis, DateAxis pinnedDateAxis, Axis<EventStripe> verticalAxis, ObservableList<EventNodeBase<?>> selectedNodes) {
 
  125         this.controller = controller;
 
  126         this.layoutSettings = 
new DetailsChartLayoutSettings(controller);
 
  127         this.detailsChartDateAxis = detailsChartDateAxis;
 
  128         this.verticalAxis = verticalAxis;
 
  129         this.pinnedDateAxis = pinnedDateAxis;
 
  130         this.selectedNodes = selectedNodes;
 
  132         FilteredEventsModel eventsModel = getController().getEventsModel();
 
  138         eventsModel.timeRangeProperty().addListener(o -> clearTimeBasedUIElements());
 
  141         eventsModel.zoomParametersProperty().addListener(o -> getSelectedNodes().clear());
 
  152     DateTime getDateTimeForPosition(
double xPos) {
 
  153         return getXAxis().getValueForDisplay(getXAxis().parentToLocal(xPos, 0).getX());
 
  161     @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
 
  162     void addStripe(EventStripe stripe) {
 
  163         rootEventStripes.add(stripe);
 
  164         nestedEvents.add(stripe);
 
  172     void clearGuideLine(GuideLine guideLine) {
 
  173         guideLines.remove(guideLine);
 
  177     public ObservableList<EventNodeBase<?>> getSelectedNodes() {
 
  178         return selectedNodes;
 
  186     DetailsChartLayoutSettings getLayoutSettings() {
 
  187         return layoutSettings;
 
  199     void setHighlightPredicate(Predicate<
EventNodeBase<?>> highlightPredicate) {
 
  200         this.highlightPredicate.set(highlightPredicate);
 
  206     @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
 
  208         rootEventStripes.clear();
 
  209         nestedEvents.clear();
 
  215     public ObservableList<TimeLineEvent> getAllNestedEvents() {
 
  223     private void clearTimeBasedUIElements() {
 
  225         clearIntervalSelector();
 
  229     public void clearIntervalSelector() {
 
  230         intervalSelectorProp.set(null);
 
  234     public IntervalSelector<DateTime> newIntervalSelector() {
 
  235         return new DetailIntervalSelector(
this);
 
  239     public IntervalSelector<? extends DateTime> getIntervalSelector() {
 
  240         return intervalSelectorProp.get();
 
  244     public void setIntervalSelector(IntervalSelector<? extends DateTime> newIntervalSelector) {
 
  245         intervalSelectorProp.set(newIntervalSelector);
 
  249     public Axis<DateTime> getXAxis() {
 
  250         return detailsChartDateAxis;
 
  254     public TimeLineController getController() {
 
  259     public void clearContextMenu() {
 
  260         setContextMenu(null);
 
  264     public ContextMenu getContextMenu(MouseEvent mouseEvent) 
throws MissingResourceException {
 
  266         ContextMenu contextMenu = getContextMenu();
 
  267         if (contextMenu != null) {
 
  272         setContextMenu(ActionUtils.createContextMenu(Arrays.asList(
 
  273                 new PlaceMarkerAction(
this, mouseEvent),
 
  274                 ActionUtils.ACTION_SEPARATOR,
 
  275                 TimeLineChart.newZoomHistoyActionGroup(getController())
 
  277         return getContextMenu();
 
  281     protected Skin<?> createDefaultSkin() {
 
  282         return new DetailsChartSkin(
this);
 
  290     ObservableList<EventStripe> getRootEventStripes() {
 
  291         return rootEventStripes;
 
  329         private static final Image 
MARKER = 
new Image(
"/org/sleuthkit/autopsy/timeline/images/marker.png", 16, 16, 
true, 
true, 
true); 
 
  332         @NbBundle.Messages({
"PlaceMArkerAction.name=Place Marker"})
 
  334             super(Bundle.PlaceMArkerAction_name());
 
  336             setGraphic(
new ImageView(MARKER)); 
 
  337             setEventHandler(actionEvent -> {
 
  338                 if (guideLine == null) {
 
  339                     guideLine = 
new GuideLine(chart);
 
  340                     guideLine.relocate(chart.sceneToLocal(clickEvent.getSceneX(), 0).getX(), 0);
 
  341                     chart.guideLines.add(guideLine);
 
  344                     guideLine.relocate(chart.sceneToLocal(clickEvent.getSceneX(), 0).getX(), 0);
 
  402         @NbBundle.Messages(
"DetailViewPane.pinnedLaneLabel.text=Pinned Events")
 
  406             primaryLane = 
new PrimaryDetailsChartLane(chart, getSkinnable().detailsChartDateAxis, getSkinnable().verticalAxis);
 
  407             primaryView = 
new ScrollingLaneWrapper(primaryLane);
 
  408             pinnedLane = 
new PinnedEventsChartLane(chart, getSkinnable().pinnedDateAxis, 
new EventAxis<>(Bundle.DetailViewPane_pinnedLaneLabel_text()));
 
  409             pinnedView = 
new ScrollingLaneWrapper(pinnedLane);
 
  411             pinnedLane.setMinHeight(MIN_PINNED_LANE_HEIGHT);
 
  412             pinnedLane.maxVScrollProperty().addListener(maxVScroll -> 
syncPinnedHeight());
 
  415             masterDetailPane = 
new MasterDetailPane(Side.TOP, primaryView, pinnedView, 
false);
 
  416             masterDetailPane.setDividerPosition(dividerPosition);
 
  417             masterDetailPane.prefHeightProperty().bind(getSkinnable().heightProperty());
 
  418             masterDetailPane.prefWidthProperty().bind(getSkinnable().widthProperty());
 
  419             rootPane = 
new Pane(masterDetailPane);
 
  420             getChildren().add(rootPane);
 
  423             getSkinnable().highlightPredicate.addListener((observable, oldPredicate, newPredicate) -> {
 
  424                 primaryLane.getAllNodes().forEach(primaryNode -> primaryNode.applyHighlightEffect(newPredicate.test(primaryNode)));
 
  425                 pinnedLane.getAllNodes().forEach(pinnedNode -> pinnedNode.applyHighlightEffect(newPredicate.test(pinnedNode)));
 
  435             getSkinnable().getLayoutSettings().pinnedLaneShowing().addListener(observable -> 
syncPinnedLaneShowing());
 
  439             getSkinnable().intervalSelectorProp.addListener((observable, oldIntervalSelector, newIntervalSelector) -> {
 
  440                 rootPane.getChildren().remove(oldIntervalSelector);
 
  441                 if (null != newIntervalSelector) {
 
  442                     rootPane.getChildren().add(newIntervalSelector);
 
  447             getSkinnable().guideLines.addListener((SetChangeListener.Change<? extends GuideLine> change) -> {
 
  448                 if (change.wasRemoved()) {
 
  449                     rootPane.getChildren().remove(change.getElementRemoved());
 
  451                 if (change.wasAdded()) {
 
  452                     rootPane.getChildren().add(change.getElementAdded());
 
  462             pinnedView.setMinHeight(MIN_PINNED_LANE_HEIGHT);
 
  463             pinnedView.setMaxHeight(pinnedLane.maxVScrollProperty().get() + 30);
 
  476             chartLane.setOnMousePressed(chartDragHandler);
 
  477             chartLane.setOnMouseReleased(chartDragHandler);
 
  478             chartLane.setOnMouseDragged(chartDragHandler);
 
  479             chartLane.setOnMouseClicked(chartDragHandler);
 
  480             chartLane.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseClickedHandler);
 
  488             boolean pinnedLaneShowing = getSkinnable().getLayoutSettings().isPinnedLaneShowing();
 
  489             if (pinnedLaneShowing == 
false) {
 
  491                 dividerPosition = masterDetailPane.getDividerPosition();
 
  494             masterDetailPane.setShowDetailNode(pinnedLaneShowing);
 
  496             if (pinnedLaneShowing) {
 
  499                 masterDetailPane.setDividerPosition(dividerPosition);
 
final MasterDetailPane masterDetailPane
static final int MIN_PINNED_LANE_HEIGHT
final ScrollingLaneWrapper pinnedView
final ScrollingLaneWrapper primaryView
static void configureMouseListeners(final DetailsChartLane<?> chartLane, final TimeLineChart.MouseClickedHandler< DateTime, DetailsChart > mouseClickedHandler, final TimeLineChart.ChartDragHandler< DateTime, DetailsChart > chartDragHandler)
Interval adjustInterval(Interval i)
void syncPinnedLaneShowing()
DateTime parseDateTime(DateTime date)
final PinnedEventsChartLane pinnedLane
String formatSpan(DateTime date)
static final Image MARKER
final IntervalSelectorProvider< X > chart
static DateTimeFormatter getZonedFormatter()
final PrimaryDetailsChartLane primaryLane
void setIntervalSelector(IntervalSelector<?extends X > newIntervalSelector)