19 package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
 
   21 import com.google.common.collect.ImmutableList;
 
   22 import java.util.Arrays;
 
   23 import java.util.Collection;
 
   24 import java.util.Objects;
 
   25 import javafx.beans.InvalidationListener;
 
   26 import javafx.beans.Observable;
 
   27 import javafx.beans.property.SimpleBooleanProperty;
 
   28 import javafx.collections.FXCollections;
 
   29 import javafx.collections.ListChangeListener;
 
   30 import javafx.collections.ObservableList;
 
   31 import javafx.fxml.FXML;
 
   32 import javafx.scene.control.ComboBox;
 
   33 import javafx.scene.control.Label;
 
   34 import javafx.scene.control.ListCell;
 
   35 import javafx.scene.control.SelectionMode;
 
   36 import javafx.scene.control.Tooltip;
 
   37 import javafx.scene.control.TreeCell;
 
   38 import javafx.scene.control.TreeItem;
 
   39 import javafx.scene.control.TreeView;
 
   40 import javafx.scene.image.Image;
 
   41 import javafx.scene.image.ImageView;
 
   42 import javafx.scene.input.MouseButton;
 
   43 import javafx.scene.input.MouseEvent;
 
   44 import javafx.scene.layout.BorderPane;
 
   45 import javafx.scene.layout.StackPane;
 
   46 import javafx.scene.paint.Color;
 
   47 import javafx.scene.shape.Rectangle;
 
   48 import org.apache.commons.lang3.StringUtils;
 
   49 import org.controlsfx.control.action.Action;
 
   50 import org.controlsfx.control.action.ActionUtils;
 
   51 import org.openide.util.NbBundle;
 
   81     private final ObservableList<DetailViewEvent> 
selectedEvents = FXCollections.observableArrayList();
 
   93             while (change.next()) {
 
   94                 change.getRemoved().forEach(getRoot()::remove);
 
   95                 change.getAddedSubList().forEach(getRoot()::insert);
 
  102             eventsTree.getSelectionModel().clearSelection();
 
  104                 eventsTree.getSelectionModel().select(
getRoot().findTreeItemForEvent(event));
 
  111         return (RootItem) eventsTree.getRoot();
 
  115     private 
void setRoot() {
 
  116         RootItem root = 
new RootItem(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
 
  118         eventsTree.setRoot(root);
 
  122     @NbBundle.Messages(
"EventsTree.Label.text=Sort By:")
 
  124         assert sortByBox != null : 
"fx:id=\"sortByBox\" was not injected: check your FXML file 'NavPanel.fxml'."; 
 
  126         sortByBox.getItems().setAll(Arrays.asList(TreeComparator.Description, TreeComparator.Count));
 
  127         sortByBox.getSelectionModel().select(TreeComparator.Description);
 
  128         sortByBox.setCellFactory(listView -> 
new TreeComparatorCell());
 
  129         sortByBox.setButtonCell(
new TreeComparatorCell());
 
  130         sortByBox.getSelectionModel().selectedItemProperty().addListener(selectedItemProperty -> {
 
  131             getRoot().sort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()), 
true);
 
  134         eventsTree.setShowRoot(
false);
 
  135         eventsTree.setCellFactory(treeView -> 
new EventTreeCell());
 
  136         eventsTree.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
 
  138         eventsTree.getSelectionModel().getSelectedItems().addListener((ListChangeListener.Change<? extends TreeItem<DetailViewEvent>> change) -> {
 
  139             while (change.next()) {
 
  140                 change.getRemoved().stream()
 
  141                         .filter(Objects::nonNull)
 
  142                         .map(TreeItem<DetailViewEvent>::getValue)
 
  143                         .filter(Objects::nonNull)
 
  144                         .forEach(selectedEvents::remove);
 
  145                 change.getAddedSubList().stream()
 
  146                         .filter(Objects::nonNull)
 
  147                         .map(TreeItem<DetailViewEvent>::getValue)
 
  148                         .filter(Objects::nonNull)
 
  149                         .forEach(selectedEvents::add);
 
  153         eventsTreeLabel.setText(Bundle.EventsTree_Label_text());
 
  157         return selectedEvents;
 
  166         private static final double HIDDEN_MULTIPLIER = .6;
 
  167         private final Rectangle rect = 
new Rectangle(24, 24);
 
  168         private final ImageView imageView = 
new ImageView();
 
  170         private final SimpleBooleanProperty hidden = 
new SimpleBooleanProperty(
false);
 
  173             rect.setArcHeight(5);
 
  175             rect.setStrokeWidth(2);
 
  180             super.updateItem(item, empty);
 
  187                 EventsTreeItem treeItem = (EventsTreeItem) getTreeItem();
 
  188                 String text = treeItem.getDisplayText();
 
  190                 setTooltip(
new Tooltip(text));
 
  192                 imageView.setImage(
new Image(getImagePath(treeItem.getEventType())));
 
  193                 setGraphic(
new StackPane(rect, imageView));
 
  194                 updateHiddenState(treeItem);
 
  198                     filterStateChangeListener = (filterState) -> updateHiddenState(treeItem);
 
  200                         while (listChange.next()) {
 
  201                             deRegisterListeners(listChange.getRemoved());
 
  202                             registerListeners(listChange.getAddedSubList(), item);
 
  204                         updateHiddenState(treeItem);
 
  207                     setOnMouseClicked((MouseEvent event) -> {
 
  208                         if (event.getButton() == MouseButton.SECONDARY) {
 
  209                             Action action = hidden.get()
 
  213                             ActionUtils.createContextMenu(ImmutableList.of(action))
 
  214                                     .show(
this, event.getScreenX(), 
event.getScreenY());
 
  218                     setOnMouseClicked(null);
 
  225                 if (filter.getFilter().getDescription().equals(item.
getDescription())) {
 
  226                     filter.activeProperty().addListener(filterStateChangeListener);
 
  232             if (Objects.nonNull(filterStateChangeListener)) {
 
  234                     filter.activeProperty().removeListener(filterStateChangeListener);
 
  243                     .anyMatch(filter -> StringUtils.equalsIgnoreCase(filter.getFilter().getDescription(), 
event.getDescription())));
 
  244             Color color = getColor(treeItem.getEventType());
 
  246                 treeItem.setExpanded(
false);
 
  247                 setTextFill(Color.gray(0, HIDDEN_MULTIPLIER));
 
  248                 imageView.setOpacity(HIDDEN_MULTIPLIER);
 
  249                 rect.setStroke(color.deriveColor(0, HIDDEN_MULTIPLIER, 1, HIDDEN_MULTIPLIER));
 
  250                 rect.setFill(color.deriveColor(0, HIDDEN_MULTIPLIER, HIDDEN_MULTIPLIER, 0.1));
 
  252                 setTextFill(Color.BLACK);
 
  253                 imageView.setOpacity(1);
 
  254                 rect.setStroke(color);
 
  255                 rect.setFill(color.deriveColor(0, 1, 1, 0.1));
 
  266         protected void updateItem(TreeComparator item, 
boolean empty) {
 
  267             super.updateItem(item, empty);
 
  268             if (empty || item == null) {
 
  271                 setText(item.getDisplayName());
 
TreeView< DetailViewEvent > eventsTree
 
void updateHiddenState(EventsTreeItem treeItem)
 
Action newUnhideDescriptionAction(String description, TimelineLevelOfDetail descriptionLoD)
 
ComboBox< TreeComparator > sortByBox
 
void updateItem(DetailViewEvent item, boolean empty)
 
ObservableList< DetailViewEvent > getSelectedEvents()
 
InvalidationListener filterStateChangeListener
 
void updateItem(TreeComparator item, boolean empty)
 
DetailViewPane detailViewPane
 
void registerListeners(Collection<?extends FilterState< DescriptionFilter >> filters, DetailViewEvent item)
 
ObservableList< DetailViewEvent > getSelectedEvents()
 
EventsTree(TimeLineController controller)
 
void setDetailViewPane(DetailViewPane detailViewPane)
 
ObservableList< DescriptionFilterState > getQuickHideFilters()
 
Action newHideDescriptionAction(String description, TimelineLevelOfDetail descriptionLoD)
 
static String getImagePath(TimelineEventType type)
 
void deRegisterListeners(Collection<?extends FilterState< DescriptionFilter >> filters)
 
final ObservableList< DetailViewEvent > selectedEvents
 
ObservableList< DetailViewEvent > getAllNestedEvents()
 
static void construct(Node node, String fxmlFileName)
 
final TimeLineController controller
 
TimelineLevelOfDetail getDescriptionLevel()
 
static Color getColor(TimelineEventType type)