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()