19 package org.sleuthkit.autopsy.timeline.ui;
 
   21 import java.util.Collections;
 
   22 import java.util.List;
 
   23 import java.util.concurrent.ExecutionException;
 
   24 import javafx.beans.InvalidationListener;
 
   25 import javafx.beans.Observable;
 
   26 import javafx.beans.property.ReadOnlyListProperty;
 
   27 import javafx.beans.property.ReadOnlyListWrapper;
 
   28 import javafx.beans.property.SimpleBooleanProperty;
 
   29 import javafx.collections.FXCollections;
 
   30 import javafx.collections.ListChangeListener;
 
   31 import javafx.collections.ObservableList;
 
   32 import javafx.concurrent.Task;
 
   33 import javafx.geometry.Pos;
 
   34 import javafx.scene.Node;
 
   35 import javafx.scene.chart.Axis;
 
   36 import javafx.scene.chart.BarChart;
 
   37 import javafx.scene.chart.Chart;
 
   38 import javafx.scene.chart.XYChart;
 
   39 import javafx.scene.control.Label;
 
   40 import javafx.scene.control.OverrunStyle;
 
   41 import javafx.scene.effect.Effect;
 
   42 import javafx.scene.input.MouseButton;
 
   43 import javafx.scene.input.MouseEvent;
 
   44 import javafx.scene.layout.BorderPane;
 
   45 import javafx.scene.layout.Pane;
 
   46 import javafx.scene.layout.Region;
 
   47 import javafx.scene.text.Font;
 
   48 import javafx.scene.text.FontWeight;
 
   49 import javafx.scene.text.Text;
 
   50 import javafx.scene.text.TextAlignment;
 
   51 import javax.annotation.concurrent.Immutable;
 
   52 import org.apache.commons.lang3.StringUtils;
 
   53 import org.openide.util.Exceptions;
 
   73     protected final SimpleBooleanProperty 
hasEvents = 
new SimpleBooleanProperty(
true);
 
   75     protected final ObservableList<BarChart.Series<X, Y>> 
dataSets = FXCollections.<BarChart.Series<X, Y>>observableArrayList();
 
   93     protected ReadOnlyListWrapper<N> 
selectedNodes = 
new ReadOnlyListWrapper<>(FXCollections.observableArrayList());
 
   96         return selectedNodes.getReadOnlyProperty();
 
  106         return Collections.unmodifiableList(settingsNodes);
 
  114     protected abstract Boolean 
isTickBold(X value);
 
  147     protected abstract Axis<X> 
getXAxis();
 
  150     protected abstract Axis<Y> 
getYAxis();
 
  156         if (updateTask != null) {
 
  157             updateTask.cancel(
true);
 
  161         updateTask.stateProperty().addListener((Observable observable) -> {
 
  162             switch (updateTask.getState()) {
 
  171                         this.hasEvents.set(updateTask.get());
 
  172                     } 
catch (InterruptedException | ExecutionException ex) {
 
  173                         Exceptions.printStackTrace(ex);
 
  182         if (updateTask != null) {
 
  183             updateTask.cancel(
true);
 
  190         this.leafPane = partPane;
 
  191         this.branchPane = contextPane;
 
  193         selectedNodes.addListener((ListChangeListener.Change<? extends N> c) -> {
 
  195                 c.getRemoved().forEach((N n) -> {
 
  196                     applySelectionEffect(n, false);
 
  199                 c.getAddedSubList().forEach((N c1) -> {
 
  200                     applySelectionEffect(c1, true);
 
  208         this.controller = controller;
 
  209         chart.setController(controller);
 
  219         this.filteredEvents = filteredEvents;
 
  225     protected InvalidationListener invalidationListener = (Observable observable) -> {
 
  250         branchPane.getChildren().clear();
 
  251         leafPane.getChildren().clear();
 
  254         ObservableList<Axis.TickMark<X>> tickMarks = FXCollections.observableArrayList(getXAxis().getTickMarks());
 
  255         tickMarks.sort((Axis.TickMark<X> t, Axis.TickMark<X> t1) -> Double.compare(t.getPosition(), t1.getPosition()));
 
  257         if (tickMarks.isEmpty() == 
false) {
 
  259             double spacing = getTickSpacing();
 
  262             TwoPartDateTime dateTime = 
new TwoPartDateTime(getTickMarkLabel(tickMarks.get(0).getValue()));
 
  263             String lastSeenBranchLabel = dateTime.branch;
 
  267             double leafLabelX = 0;
 
  269             if (dateTime.branch.equals(
"")) {
 
  271                 for (Axis.TickMark<X> t : tickMarks) {
 
  272                     assignLeafLabel(
new TwoPartDateTime(getTickMarkLabel(t.getValue())).leaf,
 
  275                                     isTickBold(t.getValue())
 
  278                     leafLabelX += spacing;  
 
  283                 double branchLabelX = 0;
 
  284                 double branchLabelWidth = 0;
 
  286                 for (Axis.TickMark<X> t : tickMarks) {               
 
  289                     dateTime = 
new TwoPartDateTime(getTickMarkLabel(t.getValue()));
 
  292                     if (lastSeenBranchLabel.equals(dateTime.branch)) {
 
  294                         branchLabelWidth += spacing;
 
  296                         assignBranchLabel(lastSeenBranchLabel, branchLabelWidth, branchLabelX);
 
  298                         lastSeenBranchLabel = dateTime.branch;
 
  299                         branchLabelX += branchLabelWidth;
 
  300                         branchLabelWidth = spacing;
 
  303                     assignLeafLabel(dateTime.leaf, spacing, leafLabelX, isTickBold(t.getValue()));
 
  306                     leafLabelX += spacing;
 
  309                 assignBranchLabel(lastSeenBranchLabel, branchLabelWidth, branchLabelX);
 
  313         requestParentLayout();
 
  317         chart.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
 
  318             if (event.getButton() == MouseButton.PRIMARY && event.isStillSincePress()) {
 
  319                 selectedNodes.clear();
 
  332     private synchronized void assignLeafLabel(String labelText, 
double labelWidth, 
double labelX, 
boolean bold) {
 
  334         Text label = 
new Text(
" " + labelText + 
" ");
 
  335         label.setTextAlignment(TextAlignment.CENTER);
 
  336         label.setFont(Font.font(null, bold ? FontWeight.BOLD : FontWeight.NORMAL, 10));
 
  338         label.relocate(labelX + labelWidth / 2 - label.getBoundsInLocal().getWidth() / 2, 0);
 
  341         if (leafPane.getChildren().isEmpty()) {
 
  343             leafPane.getChildren().add(label);
 
  346             final Text lastLabel = (Text) leafPane.getChildren().get(leafPane.getChildren().size() - 1);
 
  348             if (!lastLabel.getBoundsInParent().intersects(label.getBoundsInParent())) {
 
  349                 leafPane.getChildren().add(label);
 
  361     private synchronized void assignBranchLabel(String labelText, 
double labelWidth, 
double labelX) {
 
  363         Label label = 
new Label(labelText);
 
  364         label.setAlignment(Pos.CENTER);
 
  365         label.setTextAlignment(TextAlignment.CENTER);
 
  366         label.setFont(Font.font(10));
 
  369         label.setTextOverrun(OverrunStyle.LEADING_ELLIPSIS);
 
  371         label.setMinWidth(labelWidth);
 
  372         label.setPrefWidth(labelWidth);
 
  373         label.setMaxWidth(labelWidth);
 
  374         label.relocate(labelX, 0);
 
  377             label.setStyle(
"-fx-border-width: 0 0 0 0 ; -fx-border-color:black;"); 
 
  379             label.setStyle(
"-fx-border-width: 0 0 0 1; -fx-border-color:black;"); 
 
  382         branchPane.getChildren().add(label);
 
  403             int splitIndex = StringUtils.lastIndexOfAny(dateString, 
" ", 
"-", 
":");
 
  404             if (splitIndex < 0) { 
 
  408                 leaf = StringUtils.substring(dateString, splitIndex + 1);
 
  409                 branch = StringUtils.substring(dateString, 0, splitIndex);
 
abstract void applySelectionEffect(N node, Boolean applied)
 
List< Node > getSettingsNodes()
 
abstract Axis< X > getXAxis()
 
abstract Task< Boolean > getUpdateTask()
 
FilteredEventsModel getEventsModel()
 
void setChartClickHandler()
 
FilteredEventsModel filteredEvents
 
abstract Axis< Y > getYAxis()
 
static ReadOnlyObjectProperty< TimeZone > getTimeZone()
 
abstract Effect getSelectionEffect()
 
synchronized void setModel(FilteredEventsModel filteredEvents)
 
abstract String getTickMarkLabel(X tickValue)
 
final SimpleBooleanProperty hasEvents
 
synchronized void setController(TimeLineController controller)
 
synchronized void assignLeafLabel(String labelText, double labelWidth, double labelX, boolean bold)
 
ReadOnlyListWrapper< N > selectedNodes
 
Task< Boolean > updateTask
 
ReadOnlyListProperty< N > getSelectedNodes()
 
TimeLineController controller
 
final ObservableList< BarChart.Series< X, Y > > dataSets
 
AbstractVisualization(Pane partPane, Pane contextPane, Region spacer)
 
InvalidationListener invalidationListener
 
synchronized void monitorTask(final Task<?> task)
 
synchronized void dispose()
 
synchronized void layoutDateLabels()
 
abstract double getTickSpacing()
 
synchronized void assignBranchLabel(String labelText, double labelWidth, double labelX)
 
List< Node > settingsNodes
 
synchronized ReadOnlyObjectProperty< ZoomParams > getRequestedZoomParamters()
 
abstract Boolean isTickBold(X value)
 
synchronized void update()