19 package org.sleuthkit.autopsy.timeline.ui.listvew;
 
   21 import com.google.common.collect.Iterables;
 
   22 import com.google.common.math.DoubleMath;
 
   23 import java.math.RoundingMode;
 
   24 import java.time.Instant;
 
   25 import java.time.ZoneId;
 
   26 import java.time.ZonedDateTime;
 
   27 import java.time.temporal.ChronoField;
 
   28 import java.time.temporal.TemporalUnit;
 
   29 import java.util.ArrayList;
 
   30 import java.util.Arrays;
 
   31 import java.util.Collection;
 
   32 import java.util.Collections;
 
   33 import java.util.Comparator;
 
   34 import java.util.List;
 
   35 import java.util.Objects;
 
   37 import java.util.SortedSet;
 
   38 import java.util.TreeSet;
 
   39 import java.util.concurrent.ConcurrentSkipListSet;
 
   40 import java.util.function.Consumer;
 
   41 import java.util.function.Function;
 
   42 import java.util.logging.Level;
 
   43 import java.util.stream.Collectors;
 
   44 import javafx.application.Platform;
 
   45 import javafx.beans.binding.Bindings;
 
   46 import javafx.beans.binding.IntegerBinding;
 
   47 import javafx.beans.binding.StringBinding;
 
   48 import javafx.beans.property.SimpleObjectProperty;
 
   49 import javafx.beans.value.ObservableValue;
 
   50 import javafx.collections.ListChangeListener;
 
   51 import javafx.event.ActionEvent;
 
   52 import javafx.fxml.FXML;
 
   53 import javafx.geometry.Pos;
 
   54 import javafx.scene.Node;
 
   55 import javafx.scene.control.Button;
 
   56 import javafx.scene.control.ComboBox;
 
   57 import javafx.scene.control.ContextMenu;
 
   58 import javafx.scene.control.Label;
 
   59 import javafx.scene.control.MenuItem;
 
   60 import javafx.scene.control.OverrunStyle;
 
   61 import javafx.scene.control.SelectionMode;
 
   62 import javafx.scene.control.SeparatorMenuItem;
 
   63 import javafx.scene.control.TableCell;
 
   64 import javafx.scene.control.TableColumn;
 
   65 import javafx.scene.control.TableRow;
 
   66 import javafx.scene.control.TableView;
 
   67 import javafx.scene.control.Tooltip;
 
   68 import javafx.scene.image.Image;
 
   69 import javafx.scene.image.ImageView;
 
   70 import javafx.scene.layout.BorderPane;
 
   71 import javafx.scene.layout.HBox;
 
   72 import javafx.scene.layout.VBox;
 
   73 import javafx.util.Callback;
 
   74 import javax.swing.Action;
 
   75 import javax.swing.JMenuItem;
 
   76 import org.controlsfx.control.Notifications;
 
   77 import org.controlsfx.control.action.ActionUtils;
 
   78 import org.openide.awt.Actions;
 
   79 import org.openide.util.NbBundle;
 
   80 import org.openide.util.actions.Presenter;
 
  106 class ListTimeline 
extends BorderPane {
 
  108     private static final Logger logger = Logger.getLogger(ListTimeline.class.getName());
 
  110     private static final Image HASH_HIT = 
new Image(
"/org/sleuthkit/autopsy/images/hashset_hits.png");  
 
  111     private static final Image TAG = 
new Image(
"/org/sleuthkit/autopsy/images/green-tag-icon-16.png");  
 
  112     private static final Image FIRST = 
new Image(
"/org/sleuthkit/autopsy/timeline/images/resultset_first.png");  
 
  113     private static final Image PREVIOUS = 
new Image(
"/org/sleuthkit/autopsy/timeline/images/resultset_previous.png");  
 
  114     private static final Image NEXT = 
new Image(
"/org/sleuthkit/autopsy/timeline/images/resultset_next.png");  
 
  115     private static final Image LAST = 
new Image(
"/org/sleuthkit/autopsy/timeline/images/resultset_last.png");  
 
  120     private static final Callback<TableColumn.CellDataFeatures<CombinedEvent, CombinedEvent>, ObservableValue<CombinedEvent>> CELL_VALUE_FACTORY = param -> 
new SimpleObjectProperty<>(param.getValue());
 
  122     private static final List<ChronoField> SCROLL_BY_UNITS = Arrays.asList(
 
  124             ChronoField.MONTH_OF_YEAR,
 
  125             ChronoField.DAY_OF_MONTH,
 
  126             ChronoField.HOUR_OF_DAY,
 
  127             ChronoField.MINUTE_OF_HOUR,
 
  128             ChronoField.SECOND_OF_MINUTE);
 
  130     private static final int DEFAULT_ROW_HEIGHT = 24;
 
  133     private HBox navControls;
 
  135     private ComboBox<ChronoField> scrollInrementComboBox;
 
  137     private Button firstButton;
 
  139     private Button previousButton;
 
  141     private Button nextButton;
 
  143     private Button lastButton;
 
  145     private Label eventCountLabel;
 
  147     private TableView<CombinedEvent> table;
 
  149     private TableColumn<CombinedEvent, CombinedEvent> idColumn;
 
  151     private TableColumn<CombinedEvent, CombinedEvent> dateTimeColumn;
 
  153     private TableColumn<CombinedEvent, CombinedEvent> descriptionColumn;
 
  155     private TableColumn<CombinedEvent, CombinedEvent> typeColumn;
 
  157     private TableColumn<CombinedEvent, CombinedEvent> taggedColumn;
 
  159     private TableColumn<CombinedEvent, CombinedEvent> hashHitColumn;
 
  165     private final SortedSet<CombinedEvent> visibleEvents;
 
  167     private final TimeLineController controller;
 
  168     private final SleuthkitCase sleuthkitCase;
 
  169     private final TagsManager tagsManager;
 
  176     private final ListChangeListener<CombinedEvent> selectedEventListener = 
new ListChangeListener<CombinedEvent>() {
 
  178         public void onChanged(ListChangeListener.Change<? extends CombinedEvent> c) {
 
  180                 controller.selectEventIDs(table.getSelectionModel().getSelectedItems().stream()
 
  181                         .filter(Objects::nonNull)
 
  183                         .collect(Collectors.toSet()));
 
  184             } 
catch (TskCoreException ex) {
 
  185                 logger.log(Level.SEVERE, 
"Error selecting events.", ex);
 
  186                 Notifications.create().owner(getScene().getWindow())
 
  187                         .text(
"Error selecting events.").showError();
 
  197     ListTimeline(TimeLineController controller) {
 
  198         this.controller = controller;
 
  199         sleuthkitCase = controller.getAutopsyCase().getSleuthkitCase();
 
  200         tagsManager = controller.getAutopsyCase().getServices().getTagsManager();
 
  201         FXMLConstructor.construct(
this, ListTimeline.class, 
"ListTimeline.fxml"); 
 
  207         "# {0} - the number of events",
 
  208         "ListTimeline.eventCountLabel.text={0} events"})
 
  210         assert eventCountLabel != null : 
"fx:id=\"eventCountLabel\" was not injected: check your FXML file 'ListViewPane.fxml'."; 
 
  211         assert table != null : 
"fx:id=\"table\" was not injected: check your FXML file 'ListViewPane.fxml'."; 
 
  212         assert idColumn != null : 
"fx:id=\"idColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; 
 
  213         assert dateTimeColumn != null : 
"fx:id=\"dateTimeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; 
 
  214         assert descriptionColumn != null : 
"fx:id=\"descriptionColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; 
 
  215         assert typeColumn != null : 
"fx:id=\"typeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; 
 
  218         scrollInrementComboBox.setButtonCell(
new ChronoFieldListCell());
 
  219         scrollInrementComboBox.setCellFactory(comboBox -> 
new ChronoFieldListCell());
 
  220         scrollInrementComboBox.getItems().setAll(SCROLL_BY_UNITS);
 
  221         scrollInrementComboBox.getSelectionModel().select(ChronoField.YEAR);
 
  222         ActionUtils.configureButton(
new ScrollToFirst(), firstButton);
 
  223         ActionUtils.configureButton(
new ScrollToPrevious(), previousButton);
 
  224         ActionUtils.configureButton(
new ScrollToNext(), nextButton);
 
  225         ActionUtils.configureButton(
new ScrollToLast(), lastButton);
 
  228         table.setRowFactory(tableView -> 
new EventRow());
 
  231         table.getColumns().remove(idColumn);
 
  234         dateTimeColumn.setCellValueFactory(CELL_VALUE_FACTORY);
 
  235         dateTimeColumn.setCellFactory(col -> 
new TextEventTableCell(singleEvent
 
  236                 -> TimeLineController.getZonedFormatter().print(singleEvent.getEventTimeInMs())));
 
  238         descriptionColumn.setCellValueFactory(CELL_VALUE_FACTORY);
 
  239         descriptionColumn.setCellFactory(col -> 
new TextEventTableCell(singleEvent
 
  240                 -> singleEvent.getDescription(TimelineLevelOfDetail.HIGH)));
 
  242         typeColumn.setCellValueFactory(CELL_VALUE_FACTORY);
 
  243         typeColumn.setCellFactory(col -> 
new EventTypeCell());
 
  245         taggedColumn.setCellValueFactory(CELL_VALUE_FACTORY);
 
  246         taggedColumn.setCellFactory(col -> 
new TaggedCell());
 
  248         hashHitColumn.setCellValueFactory(CELL_VALUE_FACTORY);
 
  249         hashHitColumn.setCellFactory(col -> 
new HashHitCell());
 
  252         eventCountLabel.textProperty().bind(
new StringBinding() {
 
  254                 bind(table.getItems());
 
  258             protected String computeValue() {
 
  259                 return Bundle.ListTimeline_eventCountLabel_text(table.getItems().size());
 
  264         table.getSelectionModel().getSelectedItems().addListener(selectedEventListener);
 
  265         table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
 
  266         selectEvents(controller.getSelectedEventIDs()); 
 
  274     @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
 
  275     void setCombinedEvents(Collection<CombinedEvent> events) {
 
  276         table.getItems().setAll(events);
 
  284     void selectEvents(Collection<Long> selectedEventIDs) {
 
  285         if (selectedEventIDs.isEmpty()) {
 
  287             table.getSelectionModel().clearSelection();
 
  299             table.getSelectionModel().getSelectedItems().removeListener(selectedEventListener);
 
  301             table.getSelectionModel().clearSelection();
 
  303             table.getSelectionModel().getSelectedItems().addListener(selectedEventListener);
 
  306             int[] selectedIndices = table.getItems().stream()
 
  307                     .filter(combinedEvent -> Collections.disjoint(combinedEvent.getEventIDs(), selectedEventIDs) == 
false)
 
  308                     .mapToInt(table.getItems()::indexOf)
 
  312             if (selectedIndices.length > 0) {
 
  313                 Integer firstSelectedIndex = selectedIndices[0];
 
  314                 table.getSelectionModel().selectIndices(firstSelectedIndex, selectedIndices);
 
  315                 scrollTo(firstSelectedIndex);
 
  316                 table.requestFocus(); 
 
  328     List<Node> getTimeNavigationControls() {
 
  329         return Collections.singletonList(navControls);
 
  339     private void scrollToAndFocus(Integer index) {
 
  340         table.requestFocus();
 
  342         table.getFocusModel().focus(index);
 
  350     private void scrollTo(Integer index) {
 
  351         if (visibleEvents.contains(table.getItems().get(index)) == 
false) {
 
  352             table.scrollTo(DoubleMath.roundToInt(index - ((table.getHeight() / DEFAULT_ROW_HEIGHT)) / 2, RoundingMode.HALF_EVEN));
 
  360     private abstract class EventTableCell extends TableCell<CombinedEvent, CombinedEvent> {
 
  373         @NbBundle.Messages({
"EventTableCell.updateItem.errorMessage=Error getting event by id."})
 
  375         protected void updateItem(CombinedEvent item, 
boolean empty) {
 
  376             super.updateItem(item, empty);
 
  378             if (empty || item == null) {
 
  385                     Notifications.create().owner(getScene().getWindow())
 
  386                             .text(Bundle.EventTableCell_updateItem_errorMessage()).showError();
 
  387                     logger.log(Level.SEVERE, 
"Error getting event by id.", ex);
 
  399             "ListView.EventTypeCell.modifiedTooltip=File Modified ( M )",
 
  400             "ListView.EventTypeCell.accessedTooltip=File Accessed ( A )",
 
  401             "ListView.EventTypeCell.createdTooltip=File Created ( B, for Born )",
 
  402             "ListView.EventTypeCell.changedTooltip=File Changed ( C )" 
  405         protected void updateItem(CombinedEvent item, 
boolean empty) {
 
  406             super.updateItem(item, empty);
 
  408             if (empty || item == null) {
 
  414                     String typeString = 
""; 
 
  415                     VBox toolTipVbox = 
new VBox(5);
 
  419                             if (type.equals(FILE_MODIFIED)) {
 
  421                                 toolTipVbox.getChildren().add(
new Label(Bundle.ListView_EventTypeCell_modifiedTooltip(),
 
  422                                         new ImageView(getImagePath(type))));
 
  423                             } 
else if (type.equals(FILE_ACCESSED)) {
 
  425                                 toolTipVbox.getChildren().add(
new Label(Bundle.ListView_EventTypeCell_accessedTooltip(),
 
  426                                         new ImageView(getImagePath(type))));
 
  427                             } 
else if (type.equals(FILE_CREATED)) {
 
  429                                 toolTipVbox.getChildren().add(
new Label(Bundle.ListView_EventTypeCell_createdTooltip(),
 
  430                                         new ImageView(getImagePath(type))));
 
  431                             } 
else if (type.equals(FILE_CHANGED)) {
 
  433                                 toolTipVbox.getChildren().add(
new Label(Bundle.ListView_EventTypeCell_changedTooltip(),
 
  434                                         new ImageView(getImagePath(type))));
 
  441                     setGraphic(
new ImageView(getImagePath(FILE_SYSTEM)));
 
  442                     Tooltip tooltip = 
new Tooltip();
 
  443                     tooltip.setGraphic(toolTipVbox);
 
  449                     setGraphic(
new ImageView(getImagePath(eventType)));
 
  465             setAlignment(Pos.CENTER);
 
  469             "ListTimeline.taggedTooltip.error=There was a problem getting the tag names for the selected event.",
 
  471             "ListTimeline.taggedTooltip.text=Tags:\n{0}"})
 
  473         protected void updateItem(CombinedEvent item, 
boolean empty) {
 
  474             super.updateItem(item, empty);
 
  476             if (empty || item == null || (getEvent().eventSourceIsTagged() == 
false)) {
 
  484                 setGraphic(
new ImageView(TAG));
 
  486                 SortedSet<String> tagNames = 
new TreeSet<>();
 
  489                     Content file = sleuthkitCase.getContentById(getEvent().getContentObjID());
 
  490                     tagsManager.getContentTagsByContent(file).stream()
 
  491                             .map(tag -> tag.getName().getDisplayName())
 
  492                             .forEach(tagNames::add);
 
  495                     logger.log(Level.SEVERE, 
"Failed to lookup tags for obj id " + getEvent().getContentObjID(), ex); 
 
  496                     Platform.runLater(() -> {
 
  497                         Notifications.create()
 
  498                                 .owner(getScene().getWindow())
 
  499                                 .text(Bundle.ListTimeline_taggedTooltip_error())
 
  507                         tagsManager.getBlackboardArtifactTagsByArtifact(artifact).stream()
 
  508                                 .map(tag -> tag.getName().getDisplayName())
 
  509                                 .forEach(tagNames::add);
 
  511                         logger.log(Level.SEVERE, 
"Failed to lookup tags for artifact id " + artifactID, ex); 
 
  512                         Platform.runLater(() -> {
 
  513                             Notifications.create()
 
  514                                     .owner(getScene().getWindow())
 
  515                                     .text(Bundle.ListTimeline_taggedTooltip_error())
 
  520                 Tooltip tooltip = 
new Tooltip(Bundle.ListTimeline_taggedTooltip_text(String.join(
"\n", tagNames))); 
 
  521                 tooltip.setGraphic(
new ImageView(TAG));
 
  537             setAlignment(Pos.CENTER);
 
  541             "ListTimeline.hashHitTooltip.error=There was a problem getting the hash set names for the selected event.",
 
  542             "# {0} - hash set names",
 
  543             "ListTimeline.hashHitTooltip.text=Hash Sets:\n{0}"})
 
  545         protected void updateItem(CombinedEvent item, 
boolean empty) {
 
  546             super.updateItem(item, empty);
 
  548             if (empty || item == null || (getEvent().eventSourceHasHashHits()== 
false)) {
 
  557                 setGraphic(
new ImageView(HASH_HIT));
 
  559                     Set<String> hashSetNames = 
new TreeSet<>(sleuthkitCase.getContentById(getEvent().getContentObjID()).getHashSetNames());
 
  560                     Tooltip tooltip = 
new Tooltip(Bundle.ListTimeline_hashHitTooltip_text(String.join(
"\n", hashSetNames))); 
 
  561                     tooltip.setGraphic(
new ImageView(HASH_HIT));
 
  564                     logger.log(Level.SEVERE, 
"Failed to lookup hash set names for obj id " + getEvent().getContentObjID(), ex); 
 
  565                     Platform.runLater(() -> {
 
  566                         Notifications.create()
 
  567                                 .owner(getScene().getWindow())
 
  568                                 .text(Bundle.ListTimeline_hashHitTooltip_error())
 
  592             setTextOverrun(OverrunStyle.CENTER_ELLIPSIS);
 
  593             setEllipsisString(
" ... "); 
 
  597         protected void updateItem(CombinedEvent item, 
boolean empty) {
 
  598             super.updateItem(item, empty);
 
  599             if (empty || item == null) {
 
  603                 String text = textSupplier.apply(getEvent());
 
  605                 setTooltip(
new Tooltip(text));
 
  613     private class EventRow extends TableRow<CombinedEvent> {
 
  616             "ListChart.errorMsg=There was a problem getting the content for the selected event.",
 
  617             "EventRow.updateItem.errorMessage=Error getting event by id."})
 
  619         protected void updateItem(CombinedEvent item, 
boolean empty) {
 
  620             CombinedEvent oldItem = getItem();
 
  621             if (oldItem != null) {
 
  622                 visibleEvents.remove(oldItem);
 
  624             super.updateItem(item, empty);
 
  626             if (empty || item == null) {
 
  627                 setOnContextMenuRequested(ListTimeline::NOOPConsumer);
 
  629                 visibleEvents.add(item);
 
  630                 setOnContextMenuRequested(contextMenuEvent -> {
 
  634                         List<MenuItem> menuItems = 
new ArrayList<>();
 
  637                         for (Action action : node.
getActions(
false)) {
 
  638                             if (action == null) {
 
  640                                 menuItems.add(
new SeparatorMenuItem());
 
  642                                 String actionName = Objects.toString(action.getValue(Action.NAME));
 
  644                                 if (Arrays.asList(
"&Properties", 
"Tools").contains(actionName) == 
false) { 
 
  645                                     if (action instanceof Presenter.Popup) {
 
  652                                         JMenuItem submenu = ((Presenter.Popup) action).getPopupPresenter();
 
  653                                         menuItems.add(SwingFXMenuUtils.createFXMenu(submenu));
 
  655                                         menuItems.add(SwingFXMenuUtils.createFXMenu(
new Actions.MenuItem(action, 
false)));
 
  662                         new ContextMenu(menuItems.toArray(
new MenuItem[menuItems.size()]))
 
  663                                 .show(
this, contextMenuEvent.getScreenX(), contextMenuEvent.getScreenY());
 
  665                         logger.log(Level.SEVERE, 
"Failed to lookup Sleuthkit object backing a TimelineEvent.", ex); 
 
  666                         Platform.runLater(() -> {
 
  667                             Notifications.create()
 
  668                                     .owner(getScene().getWindow())
 
  669                                     .text(Bundle.ListChart_errorMsg())
 
  678     public static <X> 
void NOOPConsumer(X event) {
 
  684             super(
"", 
new Consumer<ActionEvent>() { 
 
  686                 public void accept(ActionEvent actionEvent) {
 
  690             setGraphic(
new ImageView(FIRST));
 
  691             disabledProperty().bind(table.getFocusModel().focusedIndexProperty().lessThan(1));
 
  698             super(
"", 
new Consumer<ActionEvent>() {  
 
  700                 public void accept(ActionEvent actionEvent) {
 
  701                     scrollToAndFocus(table.getItems().size() - 1);
 
  704             setGraphic(
new ImageView(LAST));
 
  705             IntegerBinding size = Bindings.size(table.getItems());
 
  706             disabledProperty().bind(size.isEqualTo(0).or(
 
  707                     table.getFocusModel().focusedIndexProperty().greaterThanOrEqualTo(size.subtract(1))));
 
  714             super(
"", 
new Consumer<ActionEvent>() { 
 
  716                 public void accept(ActionEvent actionEvent) {
 
  717                     ChronoField selectedChronoField = scrollInrementComboBox.getSelectionModel().getSelectedItem();
 
  719                     TemporalUnit selectedUnit = selectedChronoField.getBaseUnit();
 
  721                     int focusedIndex = table.getFocusModel().getFocusedIndex();
 
  722                     CombinedEvent focusedItem = table.getFocusModel().getFocusedItem();
 
  723                     if (-1 == focusedIndex || null == focusedItem) {
 
  724                         focusedItem = visibleEvents.first();
 
  725                         focusedIndex = table.getItems().indexOf(focusedItem);
 
  728                     ZonedDateTime focusedDateTime = Instant.ofEpochMilli(focusedItem.
getStartMillis()).atZone(timeZoneID);
 
  729                     ZonedDateTime nextDateTime = focusedDateTime.plus(1, selectedUnit);
 
  730                     for (ChronoField field : SCROLL_BY_UNITS) {
 
  731                         if (field.getBaseUnit().getDuration().compareTo(selectedUnit.getDuration()) < 0) {
 
  732                             nextDateTime = nextDateTime.with(field, field.rangeRefinedBy(nextDateTime).getMinimum());
 
  735                     long nextMillis = nextDateTime.toInstant().toEpochMilli();
 
  737                     int nextIndex = table.getItems().size() - 1;
 
  738                     for (
int i = focusedIndex; i < table.getItems().size(); i++) {
 
  739                         if (table.getItems().get(i).getStartMillis() >= nextMillis) {
 
  744                     scrollToAndFocus(nextIndex);
 
  747             setGraphic(
new ImageView(NEXT));
 
  748             IntegerBinding size = Bindings.size(table.getItems());
 
  749             disabledProperty().bind(size.isEqualTo(0).or(
 
  750                     table.getFocusModel().focusedIndexProperty().greaterThanOrEqualTo(size.subtract(1))));
 
  758             super(
"", 
new Consumer<ActionEvent>() { 
 
  760                 public void accept(ActionEvent actionEvent) {
 
  762                     ChronoField selectedChronoField = scrollInrementComboBox.getSelectionModel().getSelectedItem();
 
  763                     TemporalUnit selectedUnit = selectedChronoField.getBaseUnit();
 
  765                     int focusedIndex = table.getFocusModel().getFocusedIndex();
 
  766                     CombinedEvent focusedItem = table.getFocusModel().getFocusedItem();
 
  767                     if (-1 == focusedIndex || null == focusedItem) {
 
  768                         focusedItem = visibleEvents.last();
 
  769                         focusedIndex = table.getItems().indexOf(focusedItem);
 
  772                     ZonedDateTime focusedDateTime = Instant.ofEpochMilli(focusedItem.
getStartMillis()).atZone(timeZoneID);
 
  773                     ZonedDateTime previousDateTime = focusedDateTime.minus(1, selectedUnit);
 
  775                     for (ChronoField field : SCROLL_BY_UNITS) {
 
  776                         if (field.getBaseUnit().getDuration().compareTo(selectedUnit.getDuration()) < 0) {
 
  777                             previousDateTime = previousDateTime.with(field, field.rangeRefinedBy(previousDateTime).getMaximum());
 
  780                     long previousMillis = previousDateTime.toInstant().toEpochMilli();
 
  782                     int previousIndex = 0;
 
  783                     for (
int i = focusedIndex; i > 0; i--) {
 
  784                         if (table.getItems().get(i).getStartMillis() <= previousMillis) {
 
  790                     scrollToAndFocus(previousIndex);
 
  793             setGraphic(
new ImageView(PREVIOUS));
 
  794             disabledProperty().bind(table.getFocusModel().focusedIndexProperty().lessThan(1));
 
Action[] getActions(boolean context)
TimelineEventType FILE_ACCESSED
SortedSet<?extends TimelineEventType > getChildren()
Long getRepresentativeEventID()
static EventNode createEventNode(final Long eventID, EventsModel eventsModel)
void updateItem(CombinedEvent item, boolean empty)
final Function< TimelineEvent, String > textSupplier
void updateItem(CombinedEvent item, boolean empty)
Optional< Long > getArtifactID()
TimelineEventType FILE_MODIFIED
void updateItem(CombinedEvent item, boolean empty)
void updateItem(CombinedEvent item, boolean empty)
void updateItem(CombinedEvent item, boolean empty)
static ZoneId getTimeZoneID()
Set< TimelineEventType > getEventTypes()
TimelineEventType FILE_CREATED
TimelineEventType FILE_SYSTEM
static String getImagePath(TimelineEventType type)
void updateItem(CombinedEvent item, boolean empty)
TimelineEventType FILE_CHANGED