Autopsy  4.0
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.ListChangeListener;
29 import javafx.fxml.FXML;
30 import javafx.scene.control.ComboBox;
31 import javafx.scene.control.Label;
32 import javafx.scene.control.ListCell;
33 import javafx.scene.control.SelectionMode;
34 import javafx.scene.control.Tooltip;
35 import javafx.scene.control.TreeCell;
36 import javafx.scene.control.TreeItem;
37 import javafx.scene.control.TreeView;
38 import javafx.scene.image.ImageView;
39 import javafx.scene.input.MouseButton;
40 import javafx.scene.input.MouseEvent;
41 import javafx.scene.layout.BorderPane;
42 import javafx.scene.layout.StackPane;
43 import javafx.scene.paint.Color;
44 import javafx.scene.shape.Rectangle;
45 import org.apache.commons.lang3.StringUtils;
46 import org.controlsfx.control.action.ActionUtils;
47 import org.openide.util.NbBundle;
55 
62 final public class EventsTree extends BorderPane {
63 
65 
67 
68  @FXML
69  private TreeView<EventBundle<?>> eventsTree;
70 
71  @FXML
72  private Label eventsTreeLabel;
73 
74  @FXML
75  private ComboBox<TreeComparator> sortByBox;
76 
77  public EventsTree(TimeLineController controller) {
78  this.controller = controller;
79 
80  FXMLConstructor.construct(this, "EventsTree.fxml"); // NON-NLS
81  }
82 
83  public void setDetailViewPane(DetailViewPane detailViewPane) {
84  this.detailViewPane = detailViewPane;
85  detailViewPane.setSelectionModel(eventsTree.getSelectionModel());
86 
87  detailViewPane.getEventStripes().addListener((ListChangeListener.Change<? extends EventBundle<?>> c) -> {
88  //on jfx thread
89  while (c.next()) {
90  for (EventBundle<?> bundle : c.getAddedSubList()) {
91  getRoot().insert(bundle);
92  }
93  for (EventBundle<?> bundle : c.getRemoved()) {
94  getRoot().remove(bundle);
95  }
96  }
97  });
98 
99  setRoot();
100 
101  detailViewPane.getSelectedNodes().addListener((Observable observable) -> {
102  eventsTree.getSelectionModel().clearSelection();
103  detailViewPane.getSelectedNodes().forEach(eventBundleNode -> {
104  eventsTree.getSelectionModel().select(getRoot().findTreeItemForEvent(eventBundleNode.getEventBundle()));
105  });
106  });
107 
108  }
109 
110  private RootItem getRoot() {
111  return (RootItem) eventsTree.getRoot();
112  }
113 
115  private void setRoot() {
116  RootItem root = new RootItem(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
117  for (EventBundle<?> bundle : detailViewPane.getEventStripes()) {
118  root.insert(bundle);
119  }
120  eventsTree.setRoot(root);
121 
122  }
123 
124  @FXML
125  @NbBundle.Messages("EventsTree.Label.text=Sort By:")
126  void initialize() {
127  assert sortByBox != null : "fx:id=\"sortByBox\" was not injected: check your FXML file 'NavPanel.fxml'."; // NON-NLS
128 
129  sortByBox.getItems().setAll(Arrays.asList(TreeComparator.Description, TreeComparator.Count));
130  sortByBox.getSelectionModel().select(TreeComparator.Description);
131  sortByBox.setCellFactory(listView -> new TreeComparatorCell());
132  sortByBox.setButtonCell(new TreeComparatorCell());
133  sortByBox.getSelectionModel().selectedItemProperty().addListener((Observable o) -> {
134  getRoot().resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()), true);
135  });
136  eventsTree.setShowRoot(false);
137 
138  eventsTree.setCellFactory(treeView -> new EventBundleTreeCell());
139  eventsTree.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
140 
141  eventsTreeLabel.setText(Bundle.EventsTree_Label_text());
142  }
143 
148  private class EventBundleTreeCell extends TreeCell<EventBundle<?>> {
149 
150  private static final double HIDDEN_MULTIPLIER = .6;
151  private final Rectangle rect = new Rectangle(24, 24);
152  private final ImageView imageView = new ImageView();
153  private InvalidationListener filterStateChangeListener;
154  SimpleBooleanProperty hidden = new SimpleBooleanProperty(false);
155 
157  rect.setArcHeight(5);
158  rect.setArcWidth(5);
159  rect.setStrokeWidth(2);
160  }
161 
162  @Override
163  protected void updateItem(EventBundle<?> item, boolean empty) {
164  super.updateItem(item, empty);
165  if (item == null || empty) {
166  setText(null);
167  setTooltip(null);
168  setGraphic(null);
169  setContextMenu(null);
170  deRegisterListeners(controller.getQuickHideFilters());
171  } else {
172  filterStateChangeListener = (filterState) -> updateHiddenState(item);
173  controller.getQuickHideFilters().addListener((ListChangeListener.Change<? extends DescriptionFilter> listChange) -> {
174  while (listChange.next()) {
175  deRegisterListeners(listChange.getRemoved());
176  registerListeners(listChange.getAddedSubList(), item);
177  }
178  updateHiddenState(item);
179  });
180  registerListeners(controller.getQuickHideFilters(), item);
181  String text;
182  if (getTreeItem() instanceof EventTypeTreeItem) {
183  text = item.getEventType().getDisplayName();
184  setDisable(true);
185  } else {
186  setDisable(false);
187  text = item.getDescription() + " (" + item.getCount() + ")"; // NON-NLS
188  TreeItem<EventBundle<?>> parent = getTreeItem().getParent();
189  if (parent != null && parent.getValue() != null && (parent instanceof EventDescriptionTreeItem)) {
190  text = StringUtils.substringAfter(text, parent.getValue().getDescription());
191  }
192  }
193  setText(text);
194  setTooltip(new Tooltip(text));
195  imageView.setImage(item.getEventType().getFXImage());
196  setGraphic(new StackPane(rect, imageView));
197  updateHiddenState(item);
198  if (getTreeItem() instanceof EventDescriptionTreeItem) {
199  setOnMouseClicked((MouseEvent event) -> {
200  if (event.getButton() == MouseButton.SECONDARY) {
201  if (hidden.get()) {
202  ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newUnhideDescriptionAction(item.getDescription(), item.getDescriptionLoD())))
203  .show(EventBundleTreeCell.this, event.getScreenX(), event.getScreenY());
204  } else {
205  ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newHideDescriptionAction(item.getDescription(), item.getDescriptionLoD())))
206  .show(EventBundleTreeCell.this, event.getScreenX(), event.getScreenY());
207  }
208  }
209  });
210  } else {
211  setOnMouseClicked(null);
212  }
213  }
214  }
215 
216  private void registerListeners(Collection<? extends DescriptionFilter> filters, EventBundle<?> item) {
217  for (DescriptionFilter filter : filters) {
218  if (filter.getDescription().equals(item.getDescription())) {
219  filter.activeProperty().addListener(filterStateChangeListener);
220  }
221  }
222  }
223 
224  private void deRegisterListeners(Collection<? extends DescriptionFilter> filters) {
225  if (Objects.nonNull(filterStateChangeListener)) {
226  for (DescriptionFilter filter : filters) {
227  filter.activeProperty().removeListener(filterStateChangeListener);
228  }
229  }
230  }
231 
232  private void updateHiddenState(EventBundle<?> item) {
233  TreeItem<EventBundle<?>> treeItem = getTreeItem();
234 
235  hidden.set(controller.getQuickHideFilters().stream().
237  .anyMatch(filter -> filter.getDescription().equals(item.getDescription())));
238  if (hidden.get()) {
239  if (treeItem != null) {
240  treeItem.setExpanded(false);
241  }
242  setTextFill(Color.gray(0, HIDDEN_MULTIPLIER));
243  imageView.setOpacity(HIDDEN_MULTIPLIER);
244  rect.setStroke(item.getEventType().getColor().deriveColor(0, HIDDEN_MULTIPLIER, 1, HIDDEN_MULTIPLIER));
245  rect.setFill(item.getEventType().getColor().deriveColor(0, HIDDEN_MULTIPLIER, HIDDEN_MULTIPLIER, 0.1));
246  } else {
247  setTextFill(Color.BLACK);
248  imageView.setOpacity(1);
249  rect.setStroke(item.getEventType().getColor());
250  rect.setFill(item.getEventType().getColor().deriveColor(0, 1, 1, 0.1));
251  }
252  }
253  }
254 
255  static private class TreeComparatorCell extends ListCell<TreeComparator> {
256 
257  @Override
258  protected void updateItem(TreeComparator item, boolean empty) {
259  super.updateItem(item, empty);
260  if (empty || item == null) {
261  setText(null);
262  } else {
263  setText(item.getDisplayName());
264  }
265  }
266  }
267 }
Action newHideDescriptionAction(String description, DescriptionLoD descriptionLoD)
void registerListeners(Collection<?extends DescriptionFilter > filters, EventBundle<?> item)
void deRegisterListeners(Collection<?extends DescriptionFilter > filters)
void setDetailViewPane(DetailViewPane detailViewPane)
Definition: EventsTree.java:83
static void construct(Node node, String fxmlFileName)
void setSelectionModel(MultipleSelectionModel< TreeItem< EventBundle<?>>> selectionModel)
ObservableList< DescriptionFilter > getQuickHideFilters()
Action newUnhideDescriptionAction(String description, DescriptionLoD descriptionLoD)

Copyright © 2012-2015 Basis Technology. Generated on: Wed Apr 6 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.