Autopsy  4.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-15 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  */
19 package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
20 
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;
57 
64 final public class EventsTree extends BorderPane {
65 
67 
69 
70  @FXML
71  private TreeView<TimeLineEvent> eventsTree;
72 
73  @FXML
74  private Label eventsTreeLabel;
75 
76  @FXML
77  private ComboBox<TreeComparator> sortByBox;
78  private final ObservableList<TimeLineEvent> selectedEvents = FXCollections.observableArrayList();
79 
80  public EventsTree(TimeLineController controller) {
81  this.controller = controller;
82  FXMLConstructor.construct(this, "EventsTree.fxml"); // NON-NLS
83  }
84 
85  public void setDetailViewPane(DetailViewPane detailViewPane) {
86  this.detailViewPane = detailViewPane;
87 
88  detailViewPane.getAllNestedEvents().addListener((ListChangeListener.Change<? extends TimeLineEvent> c) -> {
89  //on jfx thread
90  while (c.next()) {
91  c.getRemoved().forEach(getRoot()::remove);
92  c.getAddedSubList().forEach(getRoot()::insert);
93  }
94  });
95 
96  setRoot();
97 
98  detailViewPane.getSelectedEvents().addListener((Observable observable) -> {
99  eventsTree.getSelectionModel().clearSelection();
100  detailViewPane.getSelectedEvents().forEach(event -> {
101  eventsTree.getSelectionModel().select(getRoot().findTreeItemForEvent(event));
102  });
103  });
104 
105  }
106 
107  private RootItem getRoot() {
108  return (RootItem) eventsTree.getRoot();
109  }
110 
112  private void setRoot() {
113  RootItem root = new RootItem(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
114  detailViewPane.getAllNestedEvents().forEach(root::insert);
115  eventsTree.setRoot(root);
116  }
117 
118  @FXML
119  @NbBundle.Messages("EventsTree.Label.text=Sort By:")
120  void initialize() {
121  assert sortByBox != null : "fx:id=\"sortByBox\" was not injected: check your FXML file 'NavPanel.fxml'."; // NON-NLS
122 
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);
129  });
130 
131  eventsTree.setShowRoot(false);
132  eventsTree.setCellFactory(treeView -> new EventTreeCell());
133  eventsTree.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
134 
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);
139  }
140  });
141 
142  eventsTreeLabel.setText(Bundle.EventsTree_Label_text());
143  }
144 
145  public ObservableList<TimeLineEvent> getSelectedEvents() {
146  return selectedEvents;
147  }
148 
153  private class EventTreeCell extends TreeCell<TimeLineEvent> {
154 
155  private static final double HIDDEN_MULTIPLIER = .6;
156  private final Rectangle rect = new Rectangle(24, 24);
157  private final ImageView imageView = new ImageView();
158  private InvalidationListener filterStateChangeListener;
159  private final SimpleBooleanProperty hidden = new SimpleBooleanProperty(false);
160 
161  EventTreeCell() {
162  rect.setArcHeight(5);
163  rect.setArcWidth(5);
164  rect.setStrokeWidth(2);
165  }
166 
167  @Override
168  protected void updateItem(TimeLineEvent item, boolean empty) {
169  super.updateItem(item, empty);
170  if (empty) {
171  setText(null);
172  setTooltip(null);
173  setGraphic(null);
174  deRegisterListeners(controller.getQuickHideFilters());
175  } else {
176  EventsTreeItem treeItem = (EventsTreeItem) getTreeItem();
177  String text = treeItem.getDisplayText();
178  setText(text);
179  setTooltip(new Tooltip(text));
180 
181  imageView.setImage(treeItem.getEventType().getFXImage());
182  setGraphic(new StackPane(rect, imageView));
183  updateHiddenState(treeItem);
184  deRegisterListeners(controller.getQuickHideFilters());
185 
186  if (item != null) {
187  filterStateChangeListener = (filterState) -> updateHiddenState(treeItem);
188  controller.getQuickHideFilters().addListener((ListChangeListener.Change<? extends DescriptionFilter> listChange) -> {
189  while (listChange.next()) {
190  deRegisterListeners(listChange.getRemoved());
191  registerListeners(listChange.getAddedSubList(), item);
192  }
193  updateHiddenState(treeItem);
194  });
195  registerListeners(controller.getQuickHideFilters(), item);
196  setOnMouseClicked((MouseEvent event) -> {
197  if (event.getButton() == MouseButton.SECONDARY) {
198  Action action = hidden.get()
199  ? detailViewPane.newUnhideDescriptionAction(item.getDescription(), item.getDescriptionLoD())
200  : detailViewPane.newHideDescriptionAction(item.getDescription(), item.getDescriptionLoD());
201 
202  ActionUtils.createContextMenu(ImmutableList.of(action))
203  .show(this, event.getScreenX(), event.getScreenY());
204  }
205  });
206  } else {
207  setOnMouseClicked(null);
208  }
209  }
210  }
211 
212  private void registerListeners(Collection<? extends DescriptionFilter> filters, TimeLineEvent item) {
213  for (DescriptionFilter filter : filters) {
214  if (filter.getDescription().equals(item.getDescription())) {
215  filter.activeProperty().addListener(filterStateChangeListener);
216  }
217  }
218  }
219 
220  private void deRegisterListeners(Collection<? extends DescriptionFilter> filters) {
221  if (Objects.nonNull(filterStateChangeListener)) {
222  for (DescriptionFilter filter : filters) {
223  filter.activeProperty().removeListener(filterStateChangeListener);
224  }
225  }
226  }
227 
228  private void updateHiddenState(EventsTreeItem treeItem) {
229  TimeLineEvent event = treeItem.getValue();
230  hidden.set(event != null && controller.getQuickHideFilters().stream().
231  filter(DescriptionFilter::isActive)
232  .anyMatch(filter -> StringUtils.equalsIgnoreCase(filter.getDescription(), event.getDescription())));
233  if (hidden.get()) {
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));
239  } else {
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));
244  }
245  }
246  }
247 
248  static private class TreeComparatorCell extends ListCell<TreeComparator> {
249 
250  @Override
251  protected void updateItem(TreeComparator item, boolean empty) {
252  super.updateItem(item, empty);
253  if (empty || item == null) {
254  setText(null);
255  } else {
256  setText(item.getDisplayName());
257  }
258  }
259  }
260 }
Action newHideDescriptionAction(String description, DescriptionLoD descriptionLoD)
void deRegisterListeners(Collection<?extends DescriptionFilter > filters)
void setDetailViewPane(DetailViewPane detailViewPane)
Definition: EventsTree.java:85
void registerListeners(Collection<?extends DescriptionFilter > filters, TimeLineEvent item)
final ObservableList< TimeLineEvent > selectedEvents
Definition: EventsTree.java:78
static void construct(Node node, String fxmlFileName)
ObservableList< DescriptionFilter > getQuickHideFilters()
Action newUnhideDescriptionAction(String description, DescriptionLoD descriptionLoD)

Copyright © 2012-2016 Basis Technology. Generated on: Tue Oct 25 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.