19package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
21import com.google.common.collect.ImmutableList;
22import java.util.Arrays;
23import java.util.Collection;
24import java.util.Objects;
25import javafx.beans.InvalidationListener;
26import javafx.beans.Observable;
27import javafx.beans.property.SimpleBooleanProperty;
28import javafx.collections.FXCollections;
29import javafx.collections.ListChangeListener;
30import javafx.collections.ObservableList;
31import javafx.fxml.FXML;
32import javafx.scene.control.ComboBox;
33import javafx.scene.control.Label;
34import javafx.scene.control.ListCell;
35import javafx.scene.control.SelectionMode;
36import javafx.scene.control.Tooltip;
37import javafx.scene.control.TreeCell;
38import javafx.scene.control.TreeItem;
39import javafx.scene.control.TreeView;
40import javafx.scene.image.Image;
41import javafx.scene.image.ImageView;
42import javafx.scene.input.MouseButton;
43import javafx.scene.input.MouseEvent;
44import javafx.scene.layout.BorderPane;
45import javafx.scene.layout.StackPane;
46import javafx.scene.paint.Color;
47import javafx.scene.shape.Rectangle;
48import org.apache.commons.lang3.StringUtils;
49import org.controlsfx.control.action.Action;
50import org.controlsfx.control.action.ActionUtils;
51import org.openide.util.NbBundle;
52import org.sleuthkit.autopsy.coreutils.ThreadConfined;
53import org.sleuthkit.autopsy.timeline.FXMLConstructor;
54import org.sleuthkit.autopsy.timeline.TimeLineController;
55import static org.sleuthkit.autopsy.timeline.ui.EventTypeUtils.getColor;
56import static org.sleuthkit.autopsy.timeline.ui.EventTypeUtils.getImagePath;
57import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane;
58import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.DetailViewEvent;
59import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.DescriptionFilter;
60import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState;
81 private final ObservableList<DetailViewEvent>
selectedEvents = FXCollections.observableArrayList();
83 @SuppressWarnings(
"this-escape")
94 while (change.next()) {
95 change.getRemoved().forEach(getRoot()::remove);
96 change.getAddedSubList().forEach(getRoot()::insert);
103 eventsTree.getSelectionModel().clearSelection();
123 @NbBundle.Messages(
"EventsTree.Label.text=Sort By:")
125 assert sortByBox != null :
"fx:id=\"sortByBox\" was not injected: check your FXML file 'NavPanel.fxml'.";
129 sortByBox.setCellFactory(listView ->
new TreeComparatorCell());
130 sortByBox.setButtonCell(
new TreeComparatorCell());
131 sortByBox.getSelectionModel().selectedItemProperty().addListener(selectedItemProperty -> {
132 getRoot().sort(
TreeComparator.
Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()),
true);
135 eventsTree.setShowRoot(
false);
136 eventsTree.setCellFactory(treeView ->
new EventTreeCell());
137 eventsTree.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
139 eventsTree.getSelectionModel().getSelectedItems().addListener((ListChangeListener.Change<? extends TreeItem<DetailViewEvent>> change) -> {
140 while (change.next()) {
141 change.getRemoved().stream()
142 .filter(Objects::nonNull)
143 .map(TreeItem<DetailViewEvent>::getValue)
144 .filter(Objects::nonNull)
145 .forEach(selectedEvents::remove);
146 change.getAddedSubList().stream()
147 .filter(Objects::nonNull)
148 .map(TreeItem<DetailViewEvent>::getValue)
149 .filter(Objects::nonNull)
150 .forEach(selectedEvents::add);
154 eventsTreeLabel.setText(Bundle.EventsTree_Label_text());
165 private class EventTreeCell
extends TreeCell<DetailViewEvent> {
168 private final Rectangle
rect =
new Rectangle(24, 24);
171 private final SimpleBooleanProperty
hidden =
new SimpleBooleanProperty(
false);
174 rect.setArcHeight(5);
176 rect.setStrokeWidth(2);
181 super.updateItem(item, empty);
188 EventsTreeItem treeItem = (EventsTreeItem) getTreeItem();
189 String text = treeItem.getDisplayText();
191 setTooltip(
new Tooltip(text));
193 imageView.setImage(
new Image(getImagePath(treeItem.getEventType())));
201 while (listChange.next()) {
202 deRegisterListeners(listChange.getRemoved());
203 registerListeners(listChange.getAddedSubList(), item);
208 setOnMouseClicked((MouseEvent event) -> {
209 if (event.getButton() == MouseButton.SECONDARY) {
210 Action action =
hidden.get()
214 ActionUtils.createContextMenu(ImmutableList.of(action))
215 .show(
this, event.getScreenX(), event.getScreenY());
219 setOnMouseClicked(
null);
226 if (filter.getFilter().getDescription().equals(item.
getDescription())) {
244 .anyMatch(filter -> StringUtils.equalsIgnoreCase(filter.getFilter().getDescription(), event.getDescription())));
245 Color color = getColor(treeItem.getEventType());
247 treeItem.setExpanded(
false);
253 setTextFill(Color.BLACK);
255 rect.setStroke(color);
256 rect.setFill(color.deriveColor(0, 1, 1, 0.1));
268 super.updateItem(item, empty);
269 if (empty || item ==
null) {
static void construct(Node node, String fxmlFileName)
ObservableList< DetailViewEvent > getSelectedEvents()
static final double HIDDEN_MULTIPLIER
void deRegisterListeners(Collection<? extends FilterState< DescriptionFilter > > filters)
final ImageView imageView
InvalidationListener filterStateChangeListener
void updateItem(DetailViewEvent item, boolean empty)
final SimpleBooleanProperty hidden
void registerListeners(Collection<? extends FilterState< DescriptionFilter > > filters, DetailViewEvent item)
void updateHiddenState(EventsTreeItem treeItem)
void updateItem(TreeComparator item, boolean empty)
TreeView< DetailViewEvent > eventsTree
void setDetailViewPane(DetailViewPane detailViewPane)
final ObservableList< DetailViewEvent > selectedEvents
ComboBox< TreeComparator > sortByBox
final TimeLineController controller
EventsTree(TimeLineController controller)
ObservableList< DetailViewEvent > getSelectedEvents()
DetailViewPane detailViewPane
TimelineLevelOfDetail getDescriptionLevel()