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