19 package org.sleuthkit.autopsy.timeline.ui.detailview;
21 import com.google.common.collect.ImmutableList;
22 import java.util.List;
23 import java.util.Objects;
24 import java.util.function.Function;
25 import java.util.function.Predicate;
26 import java.util.logging.Level;
27 import java.util.stream.Collectors;
28 import javafx.application.Platform;
29 import javafx.beans.InvalidationListener;
30 import javafx.beans.Observable;
31 import javafx.collections.ObservableList;
32 import javafx.concurrent.Task;
33 import javafx.fxml.FXML;
34 import javafx.scene.Node;
35 import javafx.scene.chart.Axis;
36 import javafx.scene.control.Alert;
37 import javafx.scene.control.ButtonBar;
38 import javafx.scene.control.ButtonType;
39 import javafx.scene.control.CheckBox;
40 import javafx.scene.control.Label;
41 import javafx.scene.control.MenuButton;
42 import javafx.scene.control.RadioButton;
43 import javafx.scene.control.Slider;
44 import javafx.scene.control.ToggleButton;
45 import javafx.scene.control.ToggleGroup;
46 import javafx.scene.layout.HBox;
47 import javafx.stage.Modality;
48 import org.apache.commons.lang3.StringUtils;
49 import org.controlsfx.control.action.Action;
50 import org.joda.time.DateTime;
51 import org.joda.time.Interval;
52 import org.openide.util.NbBundle;
89 @NbBundle.Messages(
"DetailViewPane.primaryLaneLabel.text=All Events (Filtered)")
90 private final Axis<EventStripe>
verticalAxis =
new EventAxis<>(Bundle.DetailViewPane_primaryLaneLabel_text());
116 setChart(
new DetailsChart(detailsViewModel, controller, detailsChartDateAxis, pinnedDateAxis, verticalAxis,
getSelectedNodes()));
119 detailsChartDateAxis.getTickMarks().addListener((Observable observable) ->
layoutDateLabels());
120 detailsChartDateAxis.getTickSpacing().addListener(observable ->
layoutDateLabels());
121 verticalAxis.setAutoRanging(
false);
130 .flatMap(detailNode -> detailNode.getEventIDs().stream())
131 .collect(Collectors.toList()));
132 }
catch (TskCoreException ex) {
133 logger.log(Level.SEVERE,
"Error selecting nodes.", ex);
134 new Alert(Alert.AlertType.ERROR,
"Error selecting nodes").showAndWait();
145 return getChart().getAllNestedEvents();
163 highlightedEvents.addListener((Observable observable) -> {
168 Predicate<EventNodeBase<?>> highlightPredicate
169 = highlightedEvents.stream()
173 public Predicate<EventNodeBase<?>> apply(String description) {
174 return eventNode -> StringUtils.equalsIgnoreCase(eventNode.getDescription(), description);
178 getChart().setHighlightPredicate(highlightPredicate);
196 return new UnhideDescriptionAction(description, descriptionLoD,
getChart());
208 return new HideDescriptionAction(description, descriptionLoD,
getChart());
229 return detailsChartDateAxis.getTickSpacing().get();
234 return detailsChartDateAxis.getTickMarkLabel(value);
269 return ImmutableList.of();
319 "DetailViewPane.truncateSliderLabel.text=max description width (px):",
320 "DetailViewPane.advancedLayoutOptionsButtonLabel.text=Advanced Layout Options",
321 "DetailViewPane.bandByTypeBox.text=Band by Type",
322 "DetailViewPane.oneEventPerRowBox.text=One Per Row",
323 "DetailViewPane.truncateAllBox.text=Truncate Descriptions",
324 "DetailViewPane.showRadio.text=Show Full Description",
325 "DetailViewPane.countsRadio.text=Show Counts Only",
326 "DetailViewPane.hiddenRadio.text=Hide Description"})
329 assert bandByTypeBox != null :
"fx:id=\"bandByTypeBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'.";
330 assert oneEventPerRowBox != null :
"fx:id=\"oneEventPerRowBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'.";
331 assert truncateAllBox != null :
"fx:id=\"truncateAllBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'.";
332 assert truncateWidthSlider != null :
"fx:id=\"truncateAllSlider\" was not injected: check your FXML file 'DetailViewSettings.fxml'.";
333 assert pinnedEventsToggle != null :
"fx:id=\"pinnedEventsToggle\" was not injected: check your FXML file 'DetailViewSettings.fxml'.";
336 bandByTypeBox.selectedProperty().bindBidirectional(layoutSettings.bandByTypeProperty());
338 oneEventPerRowBox.selectedProperty().bindBidirectional(layoutSettings.oneEventPerRowProperty());
339 truncateAllBox.selectedProperty().bindBidirectional(layoutSettings.truncateAllProperty());
340 truncateSliderLabel.disableProperty().bind(truncateAllBox.selectedProperty().not());
341 pinnedEventsToggle.selectedProperty().bindBidirectional(layoutSettings.pinnedLaneShowing());
343 final InvalidationListener sliderListener = observable -> {
344 if (truncateWidthSlider.isValueChanging() ==
false) {
345 layoutSettings.truncateWidthProperty().set(truncateWidthSlider.getValue());
348 truncateWidthSlider.valueProperty().addListener(sliderListener);
349 truncateWidthSlider.valueChangingProperty().addListener(sliderListener);
351 descrVisibility.selectedToggleProperty().addListener((observable, oldToggle, newToggle) -> {
352 if (newToggle == countsRadio) {
353 layoutSettings.descrVisibilityProperty().set(DescriptionVisibility.COUNT_ONLY);
354 }
else if (newToggle == showRadio) {
355 layoutSettings.descrVisibilityProperty().set(DescriptionVisibility.SHOWN);
356 }
else if (newToggle == hiddenRadio) {
357 layoutSettings.descrVisibilityProperty().set(DescriptionVisibility.HIDDEN);
362 truncateSliderLabel.setText(Bundle.DetailViewPane_truncateSliderLabel_text());
363 advancedLayoutOptionsButtonLabel.setText(Bundle.DetailViewPane_advancedLayoutOptionsButtonLabel_text());
364 bandByTypeBox.setText(Bundle.DetailViewPane_bandByTypeBox_text());
365 oneEventPerRowBox.setText(Bundle.DetailViewPane_oneEventPerRowBox_text());
366 truncateAllBox.setText(Bundle.DetailViewPane_truncateAllBox_text());
367 showRadio.setText(Bundle.DetailViewPane_showRadio_text());
368 countsRadio.setText(Bundle.DetailViewPane_countsRadio_text());
369 hiddenRadio.setText(Bundle.DetailViewPane_hiddenRadio_text());
374 "DetailViewPane.loggedTask.queryDb=Retrieving event data",
375 "DetailViewPane.loggedTask.name=Updating Details View",
376 "DetailViewPane.loggedTask.updateUI=Populating view",
377 "DetailViewPane.loggedTask.continueButton=Continue",
378 "DetailViewPane.loggedTask.backButton=Back (Cancel)",
379 "# {0} - number of events",
380 "DetailViewPane.loggedTask.prompt=You are about to show details for {0} events. This might be very slow and could exhaust available memory.\n\nDo you want to continue?"})
384 super(Bundle.DetailViewPane_loggedTask_name(),
true);
388 protected Boolean
call() throws Exception {
398 if (
needsRefresh() ==
false && Objects.equals(currentZoom, newZoom)) {
402 updateMessage(Bundle.DetailViewPane_loggedTask_queryDb());
405 List<EventStripe> eventStripes = detailsViewModel.
getEventStripes(newZoom);
406 final int size = eventStripes.size();
409 Task<ButtonType> task =
new Task<ButtonType>() {
411 protected ButtonType call()
throws Exception {
412 ButtonType ContinueButtonType =
new ButtonType(Bundle.DetailViewPane_loggedTask_continueButton(), ButtonBar.ButtonData.OK_DONE);
413 ButtonType back =
new ButtonType(Bundle.DetailViewPane_loggedTask_backButton(), ButtonBar.ButtonData.CANCEL_CLOSE);
415 Alert alert =
new Alert(Alert.AlertType.WARNING, Bundle.DetailViewPane_loggedTask_prompt(size), ContinueButtonType, back);
416 alert.setHeaderText(
"");
417 alert.initModality(Modality.APPLICATION_MODAL);
418 alert.initOwner(getScene().getWindow());
419 ButtonType userResponse = alert.showAndWait().orElse(back);
420 if (userResponse == back) {
427 Platform.runLater(task);
434 currentZoom = newZoom;
439 updateMessage(Bundle.DetailViewPane_loggedTask_updateUI());
442 for (
int i = 0; i < size; i++) {
446 updateProgress(i, size);
448 Platform.runLater(() ->
getChart().addStripe(stripe));
451 return eventStripes.isEmpty() ==
false;
462 detailsChartDateAxis.setRange(timeRange,
true);
463 pinnedDateAxis.setRange(timeRange,
true);
Action newUnhideDescriptionAction(String description, TimelineLevelOfDetail descriptionLoD)
TimeLineController getController()
boolean hasCustomTimeNavigationControls()
static final Logger logger
void setHighLightedEvents(ObservableList< DetailViewEvent > highlightedEvents)
final MappedList< DetailViewEvent, EventNodeBase<?> > selectedEvents
final DateAxis getXAxis()
ToggleButton pinnedEventsToggle
final ViewMode getViewMode()
ObservableList< DetailViewEvent > getSelectedEvents()
EventsModel getEventsModel()
synchronized EventsModelParams getModelParams()
EventsModelParams currentZoom
ImmutableList< Node > getSettingsControls()
ObservableList< NodeType > getSelectedNodes()
List< EventStripe > getEventStripes(EventsModelParams zoom)
final DateAxis detailsChartDateAxis
ToggleGroup descrVisibility
void setDateValues(Interval timeRange)
MenuButton advancedLayoutOptionsButtonLabel
DetailViewPane(TimeLineController controller)
Boolean isTickBold(DateTime value)
final synchronized void selectEventIDs(Collection< Long > eventIDs)
Label truncateSliderLabel
final DetailsViewModel detailsViewModel
String getTickMarkLabel(DateTime value)
final DetailsChartLayoutSettings layoutSettings
void applySelectionEffect(EventNodeBase<?> c1, Boolean selected)
final TimeLineController controller
synchronized void retreat()
void applySelectionEffect(boolean applied)
CheckBox oneEventPerRowBox
synchronized static Logger getLogger(String name)
Action newHideDescriptionAction(String description, TimelineLevelOfDetail descriptionLoD)
ImmutableList< Node > getTimeNavigationControls()
final Axis< EventStripe > getYAxis()
ObservableList< DetailViewEvent > getAllNestedEvents()
static void construct(Node node, String fxmlFileName)
synchronized void layoutDateLabels()
void setChart(ChartType chart)
Task< Boolean > getNewUpdateTask()
final Axis< EventStripe > verticalAxis
synchronized Interval getTimeRange()
final DateAxis pinnedDateAxis
Slider truncateWidthSlider