19 package org.sleuthkit.autopsy.timeline.ui;
21 import java.util.Comparator;
22 import java.util.HashMap;
24 import javafx.application.Platform;
25 import javafx.beans.binding.DoubleBinding;
26 import javafx.collections.FXCollections;
27 import javafx.collections.ListChangeListener;
28 import javafx.collections.ObservableList;
29 import javafx.collections.transformation.SortedList;
30 import javafx.geometry.Pos;
31 import javafx.scene.Node;
32 import javafx.scene.chart.Axis;
33 import javafx.scene.chart.XYChart;
34 import javafx.scene.control.Label;
35 import javafx.scene.control.OverrunStyle;
36 import javafx.scene.control.Tooltip;
37 import javafx.scene.layout.Border;
38 import javafx.scene.layout.BorderStroke;
39 import javafx.scene.layout.BorderStrokeStyle;
40 import javafx.scene.layout.BorderWidths;
41 import javafx.scene.layout.CornerRadii;
42 import javafx.scene.layout.HBox;
43 import javafx.scene.layout.Pane;
44 import javafx.scene.layout.Region;
45 import javafx.scene.layout.VBox;
46 import javafx.scene.paint.Color;
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.NbBundle;
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();
176 abstract protected Boolean
isTickBold(X value);
210 abstract protected Axis<X>
getXAxis();
217 abstract protected Axis<Y>
getYAxis();
233 XYChart.Series<X, Y> series =
new XYChart.Series<>();
234 series.setName(eventType.getDisplayName());
235 eventTypeToSeriesMap.put(eventType, series);
236 dataSeries.add(series);
248 protected final XYChart.Series<X, Y>
getSeries(
final TimelineEventType eventType) {
249 return eventTypeToSeriesMap.get(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);
328 TwoPartDateTime dateTime =
new TwoPartDateTime(
getTickMarkLabel(tickMarks.get(0).getValue()));
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);
438 label.setBorder(ONLY_LEFT_BORDER);
472 int splitIndex = StringUtils.lastIndexOfAny(dateString,
" ",
"-",
":");
473 if (splitIndex < 0) {
474 specifics = dateString;
477 specifics = StringUtils.substring(dateString, splitIndex + 1);
478 context = StringUtils.substring(dateString, 0, splitIndex);
static final Border ONLY_LEFT_BORDER
final Pane specificLabelPane
TimeLineController getController()
void setChart(ChartType chart)
abstract Boolean isTickBold(X value)
synchronized void layoutDateLabels()
final ObservableList< XYChart.Series< X, Y > > dataSeries
static Tooltip getDefaultTooltip()
abstract String getTickMarkLabel(X tickValue)
final Map< TimelineEventType, XYChart.Series< X, Y > > eventTypeToSeriesMap
EventsModel getEventsModel()
synchronized void addContextLabel(String labelText, double labelWidth, double labelX)
void applySelectionEffect(NodeType node)
Pane getContextLabelPane()
AbstractTimelineChart(TimeLineController controller)
Pane getSpecificLabelPane()
synchronized void addSpecificLabel(String labelText, double labelWidth, double labelX, boolean bold)
abstract double getTickSpacing()
final ObservableList< NodeType > selectedNodes
final TimeLineController controller
abstract double getAxisMargin()
ObservableList< NodeType > getSelectedNodes()
static final Logger logger
static final Tooltip DEFAULT_TOOLTIP
synchronized static Logger getLogger(String name)
void removeSelectionEffect(NodeType node)
final XYChart.Series< X, Y > getSeries(final TimelineEventType eventType)
final void createSeries()
final Pane contextLabelPane
abstract Axis< Y > getYAxis()
abstract Axis< X > getXAxis()
void setStatusMessage(String string)