91 private static final Image
HASH_HIT =
new Image(
"/org/sleuthkit/autopsy/images/hashset_hits.png");
92 private static final Image
TAG =
new Image(
"/org/sleuthkit/autopsy/images/green-tag-icon-16.png");
93 private static final Image
PIN =
new Image(
"/org/sleuthkit/autopsy/timeline/images/marker--plus.png");
94 private static final Image
UNPIN =
new Image(
"/org/sleuthkit/autopsy/timeline/images/marker--minus.png");
96 private static final Map<TimelineEventType, Effect>
dropShadowMap =
new ConcurrentHashMap<>();
98 static void configureActionButton(ButtonBase b) {
100 b.setMaxSize(16, 16);
101 b.setPrefSize(16, 16);
104 static void show(Node b,
boolean show) {
113 final DetailsChartLane<?> chartLane;
114 final Background highlightedBackground;
115 final Background defaultBackground;
116 final Color evtColor;
118 final Label countLabel =
new Label();
119 final Label descrLabel =
new Label();
120 final ImageView hashIV =
new ImageView(
HASH_HIT);
121 final ImageView tagIV =
new ImageView(
TAG);
122 final ImageView eventTypeImageView =
new ImageView();
124 final Tooltip tooltip =
new Tooltip(Bundle.EventBundleNodeBase_toolTip_loading());
126 final HBox controlsHBox =
new HBox(5);
127 final HBox infoHBox =
new HBox(5, eventTypeImageView, hashIV, tagIV, descrLabel, countLabel, controlsHBox);
128 final SleuthkitCase sleuthkitCase;
135 this.chartLane = chartLane;
137 this.parentNode = parent;
140 eventsModel = chartLane.getController().getEventsModel();
141 eventTypeImageView.setImage(
new Image(getImagePath(getEventType())));
143 if (
tlEvent.getEventIDsWithHashHits().isEmpty()) {
147 if (
tlEvent.getEventIDsWithTags().isEmpty()) {
151 if (chartLane.getController().getEventsModel().getEventTypeZoom() == TimelineEventType.HierarchyLevel.CATEGORY) {
152 evtColor = getColor(getEventType());
154 evtColor = getColor(getEventType().getCategory());
156 SELECTION_BORDER =
new Border(
new BorderStroke(evtColor.darker().desaturate(), BorderStrokeStyle.SOLID, CORNER_RADII_3,
new BorderWidths(2)));
158 defaultBackground =
new Background(
new BackgroundFill(evtColor.deriveColor(0, 1, 1, .1), CORNER_RADII_3, Insets.EMPTY));
159 highlightedBackground =
new Background(
new BackgroundFill(evtColor.deriveColor(0, 1.1, 1.1, .3), CORNER_RADII_3, Insets.EMPTY));
160 setBackground(defaultBackground);
162 Tooltip.install(
this, this.tooltip);
165 setOnMouseEntered(mouseEntered -> {
167 showHoverControls(
true);
171 setOnMouseExited(mouseExited -> {
172 showHoverControls(
false);
180 show(controlsHBox,
false);
189 return chartLane.getController();
196 DetailsChartLane<?> getChartLane() {
204 descrLabel.setMaxWidth(w);
220 super.layoutChildren();
228 void installActionButtons() {
231 controlsHBox.getChildren().add(
pinButton);
236 final void showHoverControls(
final boolean showControls) {
237 Effect dropShadow =
dropShadowMap.computeIfAbsent(getEventType(),
238 eventType ->
new DropShadow(-10, getColor(eventType)));
239 setEffect(showControls ? dropShadow :
null);
241 enableTooltip(showControls);
242 installActionButtons();
248 new UnPinEventAction(controller,
tlEvent).handle(actionEvent);
249 showHoverControls(
true);
254 new PinEventAction(controller,
tlEvent).handle(actionEvent);
255 showHoverControls(
true);
260 show(controlsHBox, showControls);
270 @NbBundle.Messages({
"# {0} - counts",
271 "# {1} - event type",
272 "# {2} - description",
273 "# {3} - start date/time",
274 "# {4} - end date/time",
275 "EventNodeBase.tooltip.text={0} {1} events\n{2}\nbetween\t{3}\nand \t{4}",
276 "EventNodeBase.toolTip.loading2=loading tooltip",
277 "# {0} - hash set count string",
278 "EventNodeBase.toolTip.hashSetHits=\n\nHash Set Hits\n{0}",
279 "# {0} - tag count string",
280 "EventNodeBase.toolTip.tags=\n\nTags\n{0}"})
281 @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
282 void installTooltip() {
283 if (tooltip.getText().equalsIgnoreCase(Bundle.EventBundleNodeBase_toolTip_loading())) {
284 final Task<String> tooltTipTask =
new Task<String>() {
286 updateTitle(Bundle.EventNodeBase_toolTip_loading2());
290 protected String call() throws Exception {
291 return Bundle.EventNodeBase_tooltip_text(getEventIDs().size(), getEventType(), getDescription(),
292 TimeLineController.getZonedFormatter().print(getStartMillis()),
293 TimeLineController.getZonedFormatter().print(getEndMillis() + 1000));
297 protected void done() {
300 tooltip.setText(
get());
301 tooltip.setGraphic(
null);
302 }
catch (InterruptedException | ExecutionException ex) {
303 LOGGER.log(Level.SEVERE,
"Tooltip generation failed.", ex);
307 new Thread(tooltTipTask).start();
308 chartLane.getController().monitorTask(tooltTipTask);
312 void enableTooltip(
boolean toolTipEnabled) {
313 if (toolTipEnabled) {
314 Tooltip.install(
this, tooltip);
316 Tooltip.uninstall(
this, tooltip);
320 final TimelineEventType getEventType() {
324 long getStartMillis() {
325 return tlEvent.getStartMillis();
328 final long getEndMillis() {
332 final double getLayoutXCompensation() {
334 ? getChartLane().getXAxis().getDisplayPosition(
new DateTime(
parentNode.getStartMillis()))
338 abstract String getDescription();
340 void animateTo(
double xLeft,
double yTop) {
343 Platform.runLater(this::requestChartLayout);
345 timeline =
new Timeline(
new KeyFrame(Duration.millis(100),
346 new KeyValue(layoutXProperty(), xLeft),
347 new KeyValue(layoutYProperty(), yTop))
349 timeline.setOnFinished(finished -> Platform.runLater(this::requestChartLayout));
353 void requestChartLayout() {
354 getChartLane().requestChartLayout();
357 @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
358 void setDescriptionVisibility(DescriptionVisibility descrVis) {
359 final int size =
getEvent().getSize();
369 showFullDescription(size);
374 void showCountOnly(
final int size) {
375 descrLabel.setText(
"");
376 countLabel.setText(String.valueOf(size));
379 void hideDescription() {
380 countLabel.setText(
"");
381 descrLabel.setText(
"");
389 synchronized void applyHighlightEffect(
boolean applied) {
391 descrLabel.setStyle(
"-fx-font-weight: bold;");
392 setBackground(highlightedBackground);
394 descrLabel.setStyle(
"-fx-font-weight: normal;");
395 setBackground(defaultBackground);
399 void applyHighlightEffect() {
400 applyHighlightEffect(
true);
403 void clearHighlightEffect() {
404 applyHighlightEffect(
false);
407 abstract Collection<Long> getEventIDs();
409 abstract EventHandler<MouseEvent> getDoubleClickHandler();
411 Iterable<? extends Action> getActions() {
425 ContextMenu chartContextMenu = chartLane.getContextMenu(mouseEvent);
427 ContextMenu contextMenu = ActionUtils.createContextMenu(Lists.newArrayList(getActions()));
428 contextMenu.getItems().add(
new SeparatorMenuItem());
429 contextMenu.getItems().addAll(chartContextMenu.getItems());
430 contextMenu.setAutoHide(
true);
434 void showFullDescription(
final int size) {
435 countLabel.setText((size == 1) ?
"" :
" (" + size +
")");
437 ->
" ..." + StringUtils.substringAfter(
getEvent().getDescription(),
parentNode.getDescription()))
438 .orElseGet(
getEvent()::getDescription);
440 descrLabel.setText(description);
445 if (
false == Sets.intersection(
getEvent().getEventIDs(), event.getUpdatedEventIDs()).isEmpty()) {
446 Platform.runLater(() -> {
457 Sets.SetView<Long> difference = Sets.difference(
getEvent().getEventIDs(), event.getUpdatedEventIDs());
459 if (
false == difference.isEmpty()) {
460 Platform.runLater(() -> {
466 private static class PinEventAction
extends Action {
468 @NbBundle.Messages({
"PinEventAction.text=Pin"})
470 super(Bundle.PinEventAction_text());
471 setEventHandler(actionEvent -> controller.
pinEvent(event));
472 setGraphic(
new ImageView(
PIN));
476 private static class UnPinEventAction
extends Action {
478 @NbBundle.Messages({
"UnPinEventAction.text=Unpin"})
480 super(Bundle.UnPinEventAction_text());
481 setEventHandler(actionEvent -> controller.
unPinEvent(event));
482 setGraphic(
new ImageView(
UNPIN));
493 if (t.getButton() == MouseButton.PRIMARY) {
494 if (t.getClickCount() > 1) {
495 getDoubleClickHandler().handle(t);
496 }
else if (t.isShiftDown()) {
497 chartLane.getSelectedNodes().add(EventNodeBase.this);
498 }
else if (t.isShortcutDown()) {
499 chartLane.getSelectedNodes().removeAll(EventNodeBase.this);
501 chartLane.getSelectedNodes().setAll(EventNodeBase.this);
504 }
else if (t.isPopupTrigger() && t.isStillSincePress()) {
505 getContextMenu(t).show(EventNodeBase.this, t.getScreenX(), t.getScreenY());