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)