Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
EventsTree.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2013-18 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
20
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;
61
67final public class EventsTree extends BorderPane {
68
70
72
73 @FXML
74 private TreeView<DetailViewEvent> eventsTree;
75
76 @FXML
77 private Label eventsTreeLabel;
78
79 @FXML
80 private ComboBox<TreeComparator> sortByBox;
81 private final ObservableList<DetailViewEvent> selectedEvents = FXCollections.observableArrayList();
82
83 @SuppressWarnings("this-escape")
85 this.controller = controller;
86 FXMLConstructor.construct(this, "EventsTree.fxml"); // NON-NLS
87 }
88
90 this.detailViewPane = detailViewPane;
91
92 detailViewPane.getAllNestedEvents().addListener((ListChangeListener.Change<? extends DetailViewEvent> change) -> {
93 //on jfx thread
94 while (change.next()) {
95 change.getRemoved().forEach(getRoot()::remove);
96 change.getAddedSubList().forEach(getRoot()::insert);
97 }
98 });
99
100 setRoot();
101
102 detailViewPane.getSelectedEvents().addListener((Observable observable) -> {
103 eventsTree.getSelectionModel().clearSelection();
104 detailViewPane.getSelectedEvents().forEach(event -> {
105 eventsTree.getSelectionModel().select(getRoot().findTreeItemForEvent(event));
106 });
107 });
108
109 }
110
111 private RootItem getRoot() {
112 return (RootItem) eventsTree.getRoot();
113 }
114
116 private void setRoot() {
117 RootItem root = new RootItem(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
118 detailViewPane.getAllNestedEvents().forEach(root::insert);
119 eventsTree.setRoot(root);
120 }
121
122 @FXML
123 @NbBundle.Messages("EventsTree.Label.text=Sort By:")
124 void initialize() {
125 assert sortByBox != null : "fx:id=\"sortByBox\" was not injected: check your FXML file 'NavPanel.fxml'."; // NON-NLS
126
127 sortByBox.getItems().setAll(Arrays.asList(TreeComparator.Description, TreeComparator.Count));
128 sortByBox.getSelectionModel().select(TreeComparator.Description);
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);
133 });
134
135 eventsTree.setShowRoot(false);
136 eventsTree.setCellFactory(treeView -> new EventTreeCell());
137 eventsTree.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
138
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);
151 }
152 });
153
154 eventsTreeLabel.setText(Bundle.EventsTree_Label_text());
155 }
156
157 public ObservableList<DetailViewEvent> getSelectedEvents() {
158 return selectedEvents;
159 }
160
165 private class EventTreeCell extends TreeCell<DetailViewEvent> {
166
167 private static final double HIDDEN_MULTIPLIER = .6;
168 private final Rectangle rect = new Rectangle(24, 24);
169 private final ImageView imageView = new ImageView();
170 private InvalidationListener filterStateChangeListener;
171 private final SimpleBooleanProperty hidden = new SimpleBooleanProperty(false);
172
173 EventTreeCell() {
174 rect.setArcHeight(5);
175 rect.setArcWidth(5);
176 rect.setStrokeWidth(2);
177 }
178
179 @Override
180 protected void updateItem(DetailViewEvent item, boolean empty) {
181 super.updateItem(item, empty);
182 if (empty) {
183 setText(null);
184 setTooltip(null);
185 setGraphic(null);
186 deRegisterListeners(controller.getQuickHideFilters());
187 } else {
188 EventsTreeItem treeItem = (EventsTreeItem) getTreeItem();
189 String text = treeItem.getDisplayText();
190 setText(text);
191 setTooltip(new Tooltip(text));
192
193 imageView.setImage(new Image(getImagePath(treeItem.getEventType())));
194 setGraphic(new StackPane(rect, imageView));
195 updateHiddenState(treeItem);
196 deRegisterListeners(controller.getQuickHideFilters());
197
198 if (item != null) {
199 filterStateChangeListener = (filterState) -> updateHiddenState(treeItem);
200 controller.getQuickHideFilters().addListener((ListChangeListener.Change<? extends FilterState<DescriptionFilter>> listChange) -> {
201 while (listChange.next()) {
202 deRegisterListeners(listChange.getRemoved());
203 registerListeners(listChange.getAddedSubList(), item);
204 }
205 updateHiddenState(treeItem);
206 });
207 registerListeners(controller.getQuickHideFilters(), item);
208 setOnMouseClicked((MouseEvent event) -> {
209 if (event.getButton() == MouseButton.SECONDARY) {
210 Action action = hidden.get()
211 ? detailViewPane.newUnhideDescriptionAction(item.getDescription(), item.getDescriptionLevel())
212 : detailViewPane.newHideDescriptionAction(item.getDescription(), item.getDescriptionLevel());
213
214 ActionUtils.createContextMenu(ImmutableList.of(action))
215 .show(this, event.getScreenX(), event.getScreenY());
216 }
217 });
218 } else {
219 setOnMouseClicked(null);
220 }
221 }
222 }
223
224 private void registerListeners(Collection<? extends FilterState<DescriptionFilter>> filters, DetailViewEvent item) {
225 for (FilterState<DescriptionFilter> filter : filters) {
226 if (filter.getFilter().getDescription().equals(item.getDescription())) {
227 filter.activeProperty().addListener(filterStateChangeListener);
228 }
229 }
230 }
231
232 private void deRegisterListeners(Collection<? extends FilterState<DescriptionFilter>> filters) {
233 if (Objects.nonNull(filterStateChangeListener)) {
234 for (FilterState<DescriptionFilter> filter : filters) {
235 filter.activeProperty().removeListener(filterStateChangeListener);
236 }
237 }
238 }
239
240 private void updateHiddenState(EventsTreeItem treeItem) {
241 DetailViewEvent event = treeItem.getValue();
242 hidden.set(event != null && controller.getQuickHideFilters().stream().
244 .anyMatch(filter -> StringUtils.equalsIgnoreCase(filter.getFilter().getDescription(), event.getDescription())));
245 Color color = getColor(treeItem.getEventType());
246 if (hidden.get()) {
247 treeItem.setExpanded(false);
248 setTextFill(Color.gray(0, HIDDEN_MULTIPLIER));
249 imageView.setOpacity(HIDDEN_MULTIPLIER);
250 rect.setStroke(color.deriveColor(0, HIDDEN_MULTIPLIER, 1, HIDDEN_MULTIPLIER));
251 rect.setFill(color.deriveColor(0, HIDDEN_MULTIPLIER, HIDDEN_MULTIPLIER, 0.1));
252 } else {
253 setTextFill(Color.BLACK);
254 imageView.setOpacity(1);
255 rect.setStroke(color);
256 rect.setFill(color.deriveColor(0, 1, 1, 0.1));
257 }
258 }
259 }
260
264 static private class TreeComparatorCell extends ListCell<TreeComparator> {
265
266 @Override
267 protected void updateItem(TreeComparator item, boolean empty) {
268 super.updateItem(item, empty);
269 if (empty || item == null) {
270 setText(null);
271 } else {
272 setText(item.getDisplayName());
273 }
274 }
275 }
276}
static void construct(Node node, String fxmlFileName)
void deRegisterListeners(Collection<? extends FilterState< DescriptionFilter > > filters)
void registerListeners(Collection<? extends FilterState< DescriptionFilter > > filters, DetailViewEvent item)
void setDetailViewPane(DetailViewPane detailViewPane)
final ObservableList< DetailViewEvent > selectedEvents

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.