19package org.sleuthkit.autopsy.timeline.ui;
21import java.util.Comparator;
22import java.util.HashMap;
24import javafx.application.Platform;
25import javafx.beans.binding.DoubleBinding;
26import javafx.collections.FXCollections;
27import javafx.collections.ListChangeListener;
28import javafx.collections.ObservableList;
29import javafx.collections.transformation.SortedList;
30import javafx.geometry.Pos;
31import javafx.scene.Node;
32import javafx.scene.chart.Axis;
33import javafx.scene.chart.XYChart;
34import javafx.scene.control.Label;
35import javafx.scene.control.OverrunStyle;
36import javafx.scene.control.Tooltip;
37import javafx.scene.layout.Border;
38import javafx.scene.layout.BorderStroke;
39import javafx.scene.layout.BorderStrokeStyle;
40import javafx.scene.layout.BorderWidths;
41import javafx.scene.layout.CornerRadii;
42import javafx.scene.layout.HBox;
43import javafx.scene.layout.Pane;
44import javafx.scene.layout.Region;
45import javafx.scene.layout.VBox;
46import javafx.scene.paint.Color;
47import javafx.scene.text.Font;
48import javafx.scene.text.FontWeight;
49import javafx.scene.text.Text;
50import javafx.scene.text.TextAlignment;
51import javax.annotation.concurrent.Immutable;
52import org.apache.commons.lang3.StringUtils;
53import org.openide.util.NbBundle;
54import org.sleuthkit.autopsy.coreutils.Logger;
55import org.sleuthkit.autopsy.coreutils.ThreadConfined;
56import org.sleuthkit.autopsy.timeline.TimeLineController;
57import org.sleuthkit.datamodel.TimelineEventType;
77 @NbBundle.Messages(
"AbstractTimelineChart.defaultTooltip.text=Drag the mouse to select a time interval to zoom into.\nRight-click for more actions.")
78 private static final Tooltip
DEFAULT_TOOLTIP =
new Tooltip(Bundle.AbstractTimelineChart_defaultTooltip_text());
79 private static final Border
ONLY_LEFT_BORDER =
new Border(
new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY,
new BorderWidths(0, 0, 0, 1)));
104 protected final ObservableList<XYChart.Series<X, Y>>
dataSeries = FXCollections.<XYChart.Series<X, Y>>observableArrayList();
113 private final Region
spacer =
new Region();
115 final private ObservableList<NodeType>
selectedNodes = FXCollections.observableArrayList();
233 XYChart.Series<X, Y> series =
new XYChart.Series<>();
234 series.setName(eventType.getDisplayName());
248 protected final XYChart.Series<X, Y>
getSeries(
final TimelineEventType eventType) {
259 Platform.runLater(
new Runnable() {
263 vBox.setFillWidth(
false);
265 hBox.setFillHeight(
false);
268 getSpacer().minWidthProperty().bind(spacerSize);
269 getSpacer().prefWidthProperty().bind(spacerSize);
270 getSpacer().maxWidthProperty().bind(spacerSize);
276 selectedNodes.addListener((ListChangeListener.Change<? extends NodeType> change) -> {
277 while (change.next()) {
278 change.getRemoved().forEach(node -> applySelectionEffect(node, false));
279 change.getAddedSubList().forEach(node -> applySelectionEffect(node, true));
313 SortedList<Axis.TickMark<X>> tickMarks =
getXAxis().getTickMarks().sorted(Comparator.comparing(Axis.TickMark::getPosition));
315 if (tickMarks.isEmpty()) {
322 Platform.runLater(this::layoutDateLabels);
329 String lastSeenContextLabel = dateTime.
context;
332 double specificLabelX = 0;
334 if (dateTime.
context.isEmpty()) {
336 for (Axis.TickMark<X> t : tickMarks) {
342 specificLabelX += spacing;
347 double contextLabelX = 0;
348 double contextLabelWidth = 0;
350 for (Axis.TickMark<X> t : tickMarks) {
355 if (lastSeenContextLabel.equals(dateTime.
context)) {
357 contextLabelWidth += spacing;
359 addContextLabel(lastSeenContextLabel, contextLabelWidth, contextLabelX);
361 lastSeenContextLabel = dateTime.
context;
362 contextLabelX += contextLabelWidth;
363 contextLabelWidth = spacing;
369 specificLabelX += spacing;
372 addContextLabel(lastSeenContextLabel, contextLabelWidth, contextLabelX);
376 requestParentLayout();
390 private synchronized void addSpecificLabel(String labelText,
double labelWidth,
double labelX,
boolean bold) {
391 Text label =
new Text(
" " + labelText +
" ");
392 label.setTextAlignment(TextAlignment.CENTER);
393 label.setFont(Font.font(
null, bold ? FontWeight.BOLD : FontWeight.NORMAL, 10));
395 label.relocate(labelX + labelWidth / 2 - label.getBoundsInLocal().getWidth() / 2, 0);
406 if (
false == lastLabel.getBoundsInParent().intersects(label.getBoundsInParent())) {
421 private synchronized void addContextLabel(String labelText,
double labelWidth,
double labelX) {
422 Label label =
new Label(labelText);
423 label.setAlignment(Pos.CENTER);
424 label.setTextAlignment(TextAlignment.CENTER);
425 label.setFont(Font.font(10));
428 label.setTextOverrun(OverrunStyle.LEADING_ELLIPSIS);
430 label.setMinWidth(labelWidth);
431 label.setPrefWidth(labelWidth);
432 label.setMaxWidth(labelWidth);
433 label.relocate(labelX, 0);
436 label.setBorder(
null);
452 private static final class TwoPartDateTime {
470 TwoPartDateTime(String dateString) {
472 int splitIndex = StringUtils.lastIndexOfAny(dateString,
" ",
"-",
":");
473 if (splitIndex < 0) {
477 specifics = StringUtils.substring(dateString, splitIndex + 1);
478 context = StringUtils.substring(dateString, 0, splitIndex);
synchronized static Logger getLogger(String name)
void setStatusMessage(String string)
TimeLineController getController()
EventsModel getEventsModel()
final TimeLineController controller
AbstractTimeLineView(TimeLineController controller)
final ObservableList< NodeType > selectedNodes
final ObservableList< XYChart.Series< X, Y > > dataSeries
synchronized void addSpecificLabel(String labelText, double labelWidth, double labelX, boolean bold)
abstract double getTickSpacing()
ObservableList< NodeType > getSelectedNodes()
final void createSeries()
Pane getContextLabelPane()
abstract void applySelectionEffect(NodeType node, Boolean applied)
static Tooltip getDefaultTooltip()
void removeSelectionEffect(NodeType node)
abstract String getTickMarkLabel(X tickValue)
AbstractTimelineChart(TimeLineController controller)
abstract Boolean isTickBold(X value)
synchronized void layoutDateLabels()
static final Tooltip DEFAULT_TOOLTIP
void setChart(ChartType chart)
synchronized void addContextLabel(String labelText, double labelWidth, double labelX)
static final Logger logger
abstract Axis< X > getXAxis()
final Map< TimelineEventType, XYChart.Series< X, Y > > eventTypeToSeriesMap
void applySelectionEffect(NodeType node)
final XYChart.Series< X, Y > getSeries(final TimelineEventType eventType)
abstract Axis< Y > getYAxis()
final Pane contextLabelPane
static final Border ONLY_LEFT_BORDER
abstract double getAxisMargin()
final Pane specificLabelPane
Pane getSpecificLabelPane()