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)