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.ImageView;
 
   41 import javafx.scene.input.MouseButton;
 
   42 import javafx.scene.input.MouseEvent;
 
   43 import javafx.scene.layout.BorderPane;
 
   44 import javafx.scene.layout.StackPane;
 
   45 import javafx.scene.paint.Color;
 
   46 import javafx.scene.shape.Rectangle;
 
   47 import org.apache.commons.lang3.StringUtils;
 
   48 import org.controlsfx.control.action.Action;
 
   49 import org.controlsfx.control.action.ActionUtils;
 
   50 import org.openide.util.NbBundle;
 
   78     private final ObservableList<TimeLineEvent> 
selectedEvents = FXCollections.observableArrayList();
 
   91                 c.getRemoved().forEach(getRoot()::remove);
 
   92                 c.getAddedSubList().forEach(getRoot()::insert);
 
   99             eventsTree.getSelectionModel().clearSelection();
 
  101                 eventsTree.getSelectionModel().select(
getRoot().findTreeItemForEvent(event));
 
  108         return (RootItem) eventsTree.getRoot();
 
  112     private 
void setRoot() {
 
  113         RootItem root = 
new RootItem(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
 
  115         eventsTree.setRoot(root);
 
  119     @NbBundle.Messages(
"EventsTree.Label.text=Sort By:")
 
  121         assert sortByBox != null : 
"fx:id=\"sortByBox\" was not injected: check your FXML file 'NavPanel.fxml'."; 
 
  123         sortByBox.getItems().setAll(Arrays.asList(TreeComparator.Description, TreeComparator.Count));
 
  124         sortByBox.getSelectionModel().select(TreeComparator.Description);
 
  125         sortByBox.setCellFactory(listView -> 
new TreeComparatorCell());
 
  126         sortByBox.setButtonCell(
new TreeComparatorCell());
 
  127         sortByBox.getSelectionModel().selectedItemProperty().addListener(selectedItemProperty -> {
 
  128             getRoot().sort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()), 
true);
 
  131         eventsTree.setShowRoot(
false);
 
  132         eventsTree.setCellFactory(treeView -> 
new EventTreeCell());
 
  133         eventsTree.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
 
  135         eventsTree.getSelectionModel().getSelectedItems().addListener((ListChangeListener.Change<? extends TreeItem<TimeLineEvent>> change) -> {
 
  136             while (change.next()) {
 
  137                 change.getRemoved().stream().map(TreeItem<TimeLineEvent>::getValue).forEach(selectedEvents::remove);
 
  138                 change.getAddedSubList().stream().map(TreeItem<TimeLineEvent>::getValue).filter(Objects::nonNull).forEach(selectedEvents::add);
 
  142         eventsTreeLabel.setText(Bundle.EventsTree_Label_text());
 
  146         return selectedEvents;
 
  155         private static final double HIDDEN_MULTIPLIER = .6;
 
  156         private final Rectangle rect = 
new Rectangle(24, 24);
 
  157         private final ImageView imageView = 
new ImageView();
 
  159         private final SimpleBooleanProperty hidden = 
new SimpleBooleanProperty(
false);
 
  162             rect.setArcHeight(5);
 
  164             rect.setStrokeWidth(2);
 
  169             super.updateItem(item, empty);
 
  176                 EventsTreeItem treeItem = (EventsTreeItem) getTreeItem();
 
  177                 String text = treeItem.getDisplayText();
 
  179                 setTooltip(
new Tooltip(text));
 
  181                 imageView.setImage(treeItem.getEventType().getFXImage());
 
  182                 setGraphic(
new StackPane(rect, imageView));
 
  183                 updateHiddenState(treeItem);
 
  187                     filterStateChangeListener = (filterState) -> updateHiddenState(treeItem);
 
  189                         while (listChange.next()) {
 
  190                             deRegisterListeners(listChange.getRemoved());
 
  191                             registerListeners(listChange.getAddedSubList(), item);
 
  193                         updateHiddenState(treeItem);
 
  196                     setOnMouseClicked((MouseEvent event) -> {
 
  197                         if (event.getButton() == MouseButton.SECONDARY) {
 
  198                             Action action = hidden.get()
 
  202                             ActionUtils.createContextMenu(ImmutableList.of(action))
 
  203                                     .show(
this, event.getScreenX(), 
event.getScreenY());
 
  207                     setOnMouseClicked(null);
 
  215                     filter.activeProperty().addListener(filterStateChangeListener);
 
  221             if (Objects.nonNull(filterStateChangeListener)) {
 
  223                     filter.activeProperty().removeListener(filterStateChangeListener);
 
  231                     filter(DescriptionFilter::isActive)
 
  232                     .anyMatch(filter -> StringUtils.equalsIgnoreCase(filter.getDescription(), 
event.getDescription())));
 
  234                 treeItem.setExpanded(
false);
 
  235                 setTextFill(Color.gray(0, HIDDEN_MULTIPLIER));
 
  236                 imageView.setOpacity(HIDDEN_MULTIPLIER);
 
  237                 rect.setStroke(treeItem.getEventType().getColor().deriveColor(0, HIDDEN_MULTIPLIER, 1, HIDDEN_MULTIPLIER));
 
  238                 rect.setFill(treeItem.getEventType().getColor().deriveColor(0, HIDDEN_MULTIPLIER, HIDDEN_MULTIPLIER, 0.1));
 
  240                 setTextFill(Color.BLACK);
 
  241                 imageView.setOpacity(1);
 
  242                 rect.setStroke(treeItem.getEventType().getColor());
 
  243                 rect.setFill(treeItem.getEventType().getColor().deriveColor(0, 1, 1, 0.1));
 
  251         protected void updateItem(TreeComparator item, 
boolean empty) {
 
  252             super.updateItem(item, empty);
 
  253             if (empty || item == null) {
 
  256                 setText(item.getDisplayName());
 
Action newHideDescriptionAction(String description, DescriptionLoD descriptionLoD)
void updateHiddenState(EventsTreeItem treeItem)
TreeView< TimeLineEvent > eventsTree
void deRegisterListeners(Collection<?extends DescriptionFilter > filters)
ObservableList< TimeLineEvent > getSelectedEvents()
ObservableList< TimeLineEvent > getAllNestedEvents()
ComboBox< TreeComparator > sortByBox
DescriptionLoD getDescriptionLoD()
ObservableList< TimeLineEvent > getSelectedEvents()
InvalidationListener filterStateChangeListener
void updateItem(TreeComparator item, boolean empty)
void updateItem(TimeLineEvent item, boolean empty)
DetailViewPane detailViewPane
EventsTree(TimeLineController controller)
void setDetailViewPane(DetailViewPane detailViewPane)
void registerListeners(Collection<?extends DescriptionFilter > filters, TimeLineEvent item)
final ObservableList< TimeLineEvent > selectedEvents
static void construct(Node node, String fxmlFileName)
ObservableList< DescriptionFilter > getQuickHideFilters()
final TimeLineController controller
Action newUnhideDescriptionAction(String description, DescriptionLoD descriptionLoD)