19 package org.sleuthkit.autopsy.timeline.ui.detailview;
21 import java.util.List;
22 import java.util.concurrent.ExecutionException;
23 import java.util.stream.Collectors;
24 import javafx.application.Platform;
25 import javafx.beans.property.SimpleObjectProperty;
26 import javafx.event.EventHandler;
27 import javafx.geometry.Insets;
28 import javafx.geometry.Pos;
29 import javafx.scene.Cursor;
30 import javafx.scene.control.Button;
31 import javafx.scene.control.ContextMenu;
32 import javafx.scene.control.Label;
33 import javafx.scene.control.OverrunStyle;
34 import javafx.scene.control.Tooltip;
35 import javafx.scene.effect.DropShadow;
36 import javafx.scene.image.Image;
37 import javafx.scene.image.ImageView;
38 import javafx.scene.input.MouseButton;
39 import javafx.scene.input.MouseEvent;
40 import javafx.scene.layout.Background;
41 import javafx.scene.layout.BackgroundFill;
42 import javafx.scene.layout.Border;
43 import javafx.scene.layout.BorderPane;
44 import javafx.scene.layout.BorderStroke;
45 import javafx.scene.layout.BorderStrokeStyle;
46 import javafx.scene.layout.BorderWidths;
47 import javafx.scene.layout.CornerRadii;
48 import javafx.scene.layout.HBox;
49 import javafx.scene.layout.Pane;
50 import javafx.scene.layout.Priority;
51 import javafx.scene.layout.Region;
52 import javafx.scene.layout.StackPane;
53 import javafx.scene.paint.Color;
54 import org.apache.commons.lang3.StringUtils;
55 import org.joda.time.DateTime;
56 import org.joda.time.Interval;
57 import org.openide.util.Exceptions;
58 import org.openide.util.NbBundle;
72 private final static Image
PLUS =
new Image(
"/org/sleuthkit/autopsy/timeline/images/plus-button.png");
73 private final static Image
MINUS =
new Image(
"/org/sleuthkit/autopsy/timeline/images/minus-button.png");
75 private static final CornerRadii
CORNER_RADII =
new CornerRadii(3);
78 private static final Border
selectionBorder =
new Border(
new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CORNER_RADII,
new BorderWidths(2)));
109 private final SimpleObjectProperty<ContextMenu>
contextMenu =
new SimpleObjectProperty<>();
115 private final Button
plusButton =
new Button(null,
new ImageView(PLUS)) {
122 private final Button
minusButton =
new Button(null,
new ImageView(MINUS)) {
131 private SimpleObjectProperty<DescriptionLOD>
descLOD =
new SimpleObjectProperty<>();
136 descLOD.set(event.
getLOD());
139 final Region region =
new Region();
140 HBox.setHgrow(region, Priority.ALWAYS);
141 final HBox hBox =
new HBox(descrLabel, countLabel, region, minusButton, plusButton);
142 hBox.setPrefWidth(USE_COMPUTED_SIZE);
143 hBox.setMinWidth(USE_PREF_SIZE);
144 hBox.setPadding(
new Insets(2, 5, 2, 5));
145 hBox.setAlignment(Pos.CENTER_LEFT);
147 minusButton.setVisible(
false);
148 plusButton.setVisible(
false);
149 minusButton.setManaged(
false);
150 plusButton.setManaged(
false);
151 final BorderPane borderPane =
new BorderPane(subNodePane, hBox, null, null, null);
152 BorderPane.setAlignment(subNodePane, Pos.TOP_LEFT);
153 borderPane.setPrefWidth(USE_COMPUTED_SIZE);
155 getChildren().addAll(spanRegion, borderPane);
157 setAlignment(Pos.TOP_LEFT);
159 minWidthProperty().bind(spanRegion.widthProperty());
160 setPrefHeight(USE_COMPUTED_SIZE);
161 setMaxHeight(USE_PREF_SIZE);
164 subNodePane.setPrefHeight(USE_COMPUTED_SIZE);
165 subNodePane.setMinHeight(USE_PREF_SIZE);
166 subNodePane.setMinWidth(USE_PREF_SIZE);
167 subNodePane.setMaxHeight(USE_PREF_SIZE);
168 subNodePane.setMaxWidth(USE_PREF_SIZE);
169 subNodePane.setPickOnBounds(
false);
173 descrLabel.setGraphic(eventTypeImageView);
174 descrLabel.setPrefWidth(USE_COMPUTED_SIZE);
175 descrLabel.setTextOverrun(OverrunStyle.CENTER_ELLIPSIS);
177 descrLabel.setMouseTransparent(
true);
178 setDescriptionVisibility(chart.getDescrVisibility().get());
181 final Color evtColor =
event.getType().getColor();
182 spanFill =
new Background(
new BackgroundFill(evtColor.deriveColor(0, 1, 1, .1),
CORNER_RADII, Insets.EMPTY));
183 setBackground(
new Background(
new BackgroundFill(evtColor.deriveColor(0, 1, 1, .1),
CORNER_RADII, Insets.EMPTY)));
184 setCursor(Cursor.HAND);
185 spanRegion.setStyle(
"-fx-border-width:2 0 2 2; -fx-border-radius: 2; -fx-border-color: " +
ColorUtilities.
getRGBCode(evtColor) +
";");
186 spanRegion.setBackground(spanFill);
189 setOnMouseEntered((MouseEvent e) -> {
192 spanRegion.setEffect(
new DropShadow(10, evtColor));
193 minusButton.setVisible(
true);
194 plusButton.setVisible(
true);
195 minusButton.setManaged(
true);
196 plusButton.setManaged(
true);
201 setOnMouseExited((MouseEvent e) -> {
202 spanRegion.setEffect(null);
203 minusButton.setVisible(
false);
204 plusButton.setVisible(
false);
205 minusButton.setManaged(
false);
206 plusButton.setManaged(
false);
213 minusButton.disableProperty().bind(descLOD.isEqualTo(event.
getLOD()));
215 plusButton.setOnMouseClicked(e -> {
222 minusButton.setOnMouseClicked(e -> {
224 if (previous != null) {
226 descLOD.set(previous);
233 NbBundle.getMessage(
this.getClass(),
"AggregateEventNode.installTooltip.text",
254 spanRegion.setPrefWidth(w);
255 spanRegion.setMaxWidth(w);
256 spanRegion.setMinWidth(Math.max(2, w));
264 descrLabel.setMaxWidth(w);
268 final void setDescriptionVisibility(DescriptionVisibility descrVis) {
270 final int size =
event.getEventIDs().size();
275 descrLabel.setText(
"");
276 countLabel.setText(String.valueOf(size));
279 countLabel.setText(
"");
280 descrLabel.setText(
"");
284 String description =
event.getDescription();
285 description = parentEventNode != null
288 descrLabel.setText(description);
289 countLabel.setText(((size == 1) ?
"" :
" (" + size +
")"));
298 void applySelectionEffect(
final boolean applied) {
299 Platform.runLater(() -> {
301 setBorder(selectionBorder);
312 void applyHighlightEffect(
boolean applied) {
315 descrLabel.setStyle(
"-fx-font-weight: bold;");
316 spanFill =
new Background(
new BackgroundFill(
getEvent().getType().getColor().deriveColor(0, 1, 1, .3), CORNER_RADII, Insets.EMPTY));
317 spanRegion.setBackground(spanFill);
318 setBackground(
new Background(
new BackgroundFill(
getEvent().getType().getColor().deriveColor(0, 1, 1, .2), CORNER_RADII, Insets.EMPTY)));
320 descrLabel.setStyle(
"-fx-font-weight: normal;");
321 spanFill =
new Background(
new BackgroundFill(
getEvent().getType().getColor().deriveColor(0, 1, 1, .1), CORNER_RADII, Insets.EMPTY));
322 spanRegion.setBackground(spanFill);
323 setBackground(
new Background(
new BackgroundFill(
getEvent().getType().getColor().deriveColor(0, 1, 1, .1), CORNER_RADII, Insets.EMPTY)));
327 String getDisplayedDescription() {
328 return descrLabel.getText();
331 double getLayoutXCompensation() {
332 return (parentEventNode != null ? parentEventNode.getLayoutXCompensation() : 0)
333 + getBoundsInParent().getMinX();
340 return contextMenu.get();
347 this.contextMenu.set(contextMenu);
357 if (newLOD == event.
getLOD()) {
359 chart.setRequiresLayout(
true);
368 final Interval span =
event.getSpan().withEndMillis(event.
getSpan().getEndMillis() + 1000);
372 NbBundle.getMessage(this.getClass(),
"AggregateEventNode.loggedTask.name"),
true) {
375 protected List<AggregateEventNode> call()
throws Exception {
384 subNode.setLayoutX(chart.getXAxis().getDisplayPosition(
new DateTime(t.
getSpan().getStartMillis())) - getLayoutXCompensation());
386 }).collect(Collectors.toList());
390 protected void succeeded() {
392 chart.setCursor(Cursor.WAIT);
395 setDescriptionVisibility(descrVis);
396 chart.setRequiresLayout(
true);
398 chart.setCursor(null);
399 }
catch (InterruptedException | ExecutionException ex) {
400 Exceptions.printStackTrace(ex);
415 if (t.getButton() == MouseButton.PRIMARY) {
417 if (t.isShiftDown()) {
421 }
else if (t.isShortcutDown()) {
423 }
else if (t.getClickCount() > 1) {
void setDescriptionWidth(double w)
static String getRGBCode(Color color)
List< AggregateEvent > getAggregatedEvents()
void setContextMenu(ContextMenu contextMenu)
void requestChartLayout()
static IntersectionFilter intersect(ObservableList< Filter > filters)
synchronized ReadOnlyObjectProperty< Filter > filter()
TimeLineController getController()
final ImageView eventTypeImageView
final EventDetailChart chart
final AggregateEvent event
DescriptionLOD previous()
final SimpleObjectProperty< ContextMenu > contextMenu
ContextMenu getContextMenu()
FilteredEventsModel getFilteredEvents()
synchronized void monitorTask(final Task<?> task)
DescriptionVisibility descrVis
final AggregateEventNode parentEventNode
void handle(MouseEvent t)
void setSpanWidth(double w)
void loadSubClusters(DescriptionLOD newLOD)
AggregateEventNode(final AggregateEvent event, AggregateEventNode parentEventNode, EventDetailChart chart)
SimpleObjectProperty< DescriptionLOD > descLOD
static final Border selectionBorder
synchronized ReadOnlyObjectProperty< EventTypeZoomLevel > eventTypeZoom()
AggregateEvent getEvent()
Set< Long > getEventIDs()
static final CornerRadii CORNER_RADII
static DateTimeFormatter getZonedFormatter()