Autopsy  3.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
DetailViewPane.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2014 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;
20 
21 import java.net.URL;
22 import java.util.ArrayList;
23 import java.util.Map;
24 import java.util.ResourceBundle;
25 import java.util.concurrent.ConcurrentHashMap;
26 import javafx.application.Platform;
27 import javafx.beans.InvalidationListener;
28 import javafx.beans.Observable;
29 import javafx.collections.FXCollections;
30 import javafx.collections.ListChangeListener;
31 import javafx.collections.ObservableList;
32 import javafx.concurrent.Task;
33 import javafx.event.EventHandler;
34 import javafx.fxml.FXML;
35 import javafx.geometry.Orientation;
36 import javafx.scene.Cursor;
37 import javafx.scene.chart.Axis;
38 import javafx.scene.chart.BarChart;
39 import javafx.scene.chart.XYChart;
40 import javafx.scene.control.*;
41 import javafx.scene.effect.Effect;
42 import static javafx.scene.input.KeyCode.DOWN;
43 import static javafx.scene.input.KeyCode.KP_DOWN;
44 import static javafx.scene.input.KeyCode.KP_UP;
45 import static javafx.scene.input.KeyCode.PAGE_DOWN;
46 import static javafx.scene.input.KeyCode.PAGE_UP;
47 import static javafx.scene.input.KeyCode.UP;
48 import javafx.scene.input.KeyEvent;
49 import javafx.scene.input.MouseEvent;
50 import javafx.scene.input.ScrollEvent;
51 import javafx.scene.layout.HBox;
52 import javafx.scene.layout.Pane;
53 import javafx.scene.layout.Priority;
54 import javafx.scene.layout.Region;
55 import javafx.scene.layout.VBox;
56 import org.joda.time.DateTime;
57 import org.openide.util.NbBundle;
69 
92 public class DetailViewPane extends AbstractVisualization<DateTime, AggregateEvent, AggregateEventNode, EventDetailChart> {
93 
94  private final static Logger LOGGER = Logger.getLogger(CountsViewPane.class.getName());
95 
96  private MultipleSelectionModel<TreeItem<NavTreeNode>> treeSelectionModel;
97 
98  @FXML
99  protected ResourceBundle resources;
100 
101  @FXML
102  protected URL location;
103 
104  //these three could be injected from fxml but it was causing npe's
105  private final DateAxis dateAxis = new DateAxis();
106 
107  private final Axis<AggregateEvent> verticalAxis = new EventAxis();
108 
109  //private access to barchart data
110  private final Map<EventType, XYChart.Series<DateTime, AggregateEvent>> eventTypeToSeriesMap = new ConcurrentHashMap<>();
111 
112  private final ScrollBar vertScrollBar = new ScrollBar();
113 
114  private final Region region = new Region();
115 
116  private final ObservableList<AggregateEvent> aggregatedEvents = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
117 
118  private final ObservableList<AggregateEventNode> highlightedNodes = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
119 
120  public ObservableList<AggregateEvent> getAggregatedEvents() {
121  return aggregatedEvents;
122  }
123 
124  public DetailViewPane(Pane partPane, Pane contextPane, Region spacer) {
125  super(partPane, contextPane, spacer);
126  chart = new EventDetailChart(dateAxis, verticalAxis, selectedNodes);
128  chart.setData(dataSets);
129  setCenter(chart);
130 
131  chart.setPrefHeight(USE_COMPUTED_SIZE);
132 
133  settingsNodes = new ArrayList<>(new DetailViewSettingsPane().getChildrenUnmodifiable());
134 
135  vertScrollBar.setOrientation(Orientation.VERTICAL);
136  VBox vBox = new VBox();
137  VBox.setVgrow(vertScrollBar, Priority.ALWAYS);
138  vBox.getChildren().add(vertScrollBar);
139  vBox.getChildren().add(region);
140  setRight(vBox);
141 
142  dateAxis.setAutoRanging(false);
143  region.minHeightProperty().bind(dateAxis.heightProperty());
144  vertScrollBar.visibleAmountProperty().bind(chart.heightProperty().multiply(100).divide(chart.getMaxVScroll()));
145  requestLayout();
146 
147  highlightedNodes.addListener((ListChangeListener.Change<? extends AggregateEventNode> change) -> {
148  while (change.next()) {
149  change.getAddedSubList().forEach(aeNode -> {
150  aeNode.applyHighlightEffect(true);
151  });
152  change.getRemoved().forEach(aeNode -> {
153  aeNode.applyHighlightEffect(false);
154  });
155  }
156  });
157  //request focus for keyboard scrolling
158  setOnMouseClicked((MouseEvent t) -> {
159  requestFocus();
160  });
161 
162  //These scroll related handlers don't affect any other view or the model, so they are handled internally
163  //mouse wheel scroll handler
164  this.onScrollProperty().set((EventHandler<ScrollEvent>) (ScrollEvent t) -> {
165  vertScrollBar.valueProperty().set(Math.max(0, Math.min(100, vertScrollBar.getValue() - t.getDeltaY() / 200.0)));
166  });
167 
168  this.setOnKeyPressed((KeyEvent t) -> {
169  switch (t.getCode()) {
170  case PAGE_UP:
172  break;
173  case PAGE_DOWN:
175  break;
176  case KP_UP:
177  case UP:
179  break;
180  case KP_DOWN:
181  case DOWN:
183  break;
184  }
185  t.consume();
186  });
187 
188  //scrollbar handler
189  this.vertScrollBar.valueProperty().addListener((o, oldValue, newValue) -> {
190  chart.setVScroll(newValue.doubleValue() / 100.0);
191  });
192  spacer.minWidthProperty().bind(verticalAxis.widthProperty().add(verticalAxis.tickLengthProperty()));
193  spacer.prefWidthProperty().bind(verticalAxis.widthProperty().add(verticalAxis.tickLengthProperty()));
194  spacer.maxWidthProperty().bind(verticalAxis.widthProperty().add(verticalAxis.tickLengthProperty()));
195 
196  dateAxis.setTickLabelsVisible(false);
197 
198  dateAxis.getTickMarks().addListener((Observable observable) -> {
200  });
201  dateAxis.getTickSpacing().addListener((Observable observable) -> {
203  });
204 
205  dateAxis.setTickLabelGap(0);
206 
207  selectedNodes.addListener((Observable observable) -> {
208  highlightedNodes.clear();
209  selectedNodes.stream().forEach((tn) -> {
210  for (AggregateEventNode n : chart.getNodes((
211  AggregateEventNode t) -> t.getEvent().getDescription().equals(tn.getEvent().getDescription()))) {
212  highlightedNodes.add(n);
213  }
214  });
215  });
216 
217  }
218 
219  private void incrementScrollValue(int factor) {
220  vertScrollBar.valueProperty().set(Math.max(0, Math.min(100, vertScrollBar.getValue() + factor * (chart.getHeight() / chart.getMaxVScroll().get()))));
221  }
222 
223  public void setSelectionModel(MultipleSelectionModel<TreeItem<NavTreeNode>> selectionModel) {
224  this.treeSelectionModel = selectionModel;
225 
226  treeSelectionModel.getSelectedItems().addListener((Observable observable) -> {
227  highlightedNodes.clear();
228  for (TreeItem<NavTreeNode> tn : treeSelectionModel.getSelectedItems()) {
229  for (AggregateEventNode n : chart.getNodes((
231  -> t.getEvent().getDescription().equals(tn.getValue().getDescription()))) {
232  highlightedNodes.add(n);
233  }
234  }
235  });
236  }
237 
238  @Override
239  protected Boolean isTickBold(DateTime value) {
240  return false;
241  }
242 
243  @Override
244  protected Axis<AggregateEvent> getYAxis() {
245  return verticalAxis;
246  }
247 
248  @Override
249  protected Axis<DateTime> getXAxis() {
250  return dateAxis;
251  }
252 
253  @Override
254  protected double getTickSpacing() {
255  return dateAxis.getTickSpacing().get();
256  }
257 
258  @Override
259  protected String getTickMarkLabel(DateTime value) {
260  return dateAxis.getTickMarkLabel(value);
261  }
262 
270  private XYChart.Series<DateTime, AggregateEvent> getSeries(final EventType et) {
271  XYChart.Series<DateTime, AggregateEvent> series = eventTypeToSeriesMap.get(et);
272  if (series == null) {
273  series = new XYChart.Series<>();
274  series.setName(et.getDisplayName());
275  eventTypeToSeriesMap.put(et, series);
276  dataSets.add(series);
277  }
278  return series;
279  }
280 
281  @Override
282  protected Task<Boolean> getUpdateTask() {
283 
284  return new LoggedTask<Boolean>(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.name"), true) {
285 
286  @Override
287  protected Boolean call() throws Exception {
288  if (isCancelled()) {
289  return null;
290  }
291  Platform.runLater(() -> {
292  if (isCancelled() == false) {
293  setCursor(Cursor.WAIT);
294  }
295  });
296 
297  updateProgress(-1, 1);
298  updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.preparing"));
299 
300  final RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.timeRange().get());
301  final long lowerBound = rangeInfo.getLowerBound();
302  final long upperBound = rangeInfo.getUpperBound();
303 
304  updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.queryDb"));
305  aggregatedEvents.setAll(filteredEvents.getAggregatedEvents());
306 
307  Platform.runLater(() -> {
308  if (isCancelled()) {
309  return;
310  }
311  dateAxis.setLowerBound(new DateTime(lowerBound, TimeLineController.getJodaTimeZone()));
312  dateAxis.setUpperBound(new DateTime(upperBound, TimeLineController.getJodaTimeZone()));
313 // if (chart == null) {
314 // initializeClusterChart();
315 // }
316  vertScrollBar.setValue(0);
317  eventTypeToSeriesMap.clear();
318  dataSets.clear();
319  });
320  final int size = aggregatedEvents.size();
321  int i = 0;
322  for (final AggregateEvent e : aggregatedEvents) {
323  if (isCancelled()) {
324  break;
325  }
326  updateProgress(i++, size);
327  updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.updateUI"));
328  final XYChart.Data<DateTime, AggregateEvent> xyData = new BarChart.Data<>(new DateTime(e.getSpan().getStartMillis()), e);
329 
330  Platform.runLater(() -> {
331  if (isCancelled() == false) {
332  getSeries(e.getType()).getData().add(xyData);
333  }
334  });
335  }
336 
337  Platform.runLater(() -> {
338  setCursor(Cursor.NONE);
339  layoutDateLabels();
340  updateProgress(1, 1);
341  });
342  return aggregatedEvents.isEmpty() == false;
343  }
344  };
345  }
346 
347  @Override
348  protected Effect getSelectionEffect() {
349  return null;
350  }
351 
352  @Override
353  protected void applySelectionEffect(AggregateEventNode c1, Boolean applied) {
354  c1.applySelectionEffect(applied);
355  }
356 
357  private class DetailViewSettingsPane extends HBox {
358 
359  @FXML
360  private RadioButton hiddenRadio;
361 
362  @FXML
363  private RadioButton showRadio;
364 
365  @FXML
366  private ToggleGroup descrVisibility;
367 
368  @FXML
369  private RadioButton countsRadio;
370 
371  @FXML
372  private ResourceBundle resources;
373 
374  @FXML
375  private URL location;
376 
377  @FXML
378  private CheckBox bandByTypeBox;
379 
380  @FXML
381  private CheckBox oneEventPerRowBox;
382 
383  @FXML
384  private CheckBox truncateAllBox;
385 
386  @FXML
387  private Slider truncateWidthSlider;
388 
389  @FXML
390  private Label truncateSliderLabel;
391 
392  @FXML
394 
395  @FXML
396  private CustomMenuItem bandByTypeBoxMenuItem;
397 
398  @FXML
399  private CustomMenuItem oneEventPerRowBoxMenuItem;
400 
401  @FXML
402  private CustomMenuItem truncateAllBoxMenuItem;
403 
404  @FXML
405  private CustomMenuItem truncateSliderLabelMenuItem;
406 
407  @FXML
408  private CustomMenuItem showRadioMenuItem;
409 
410  @FXML
411  private CustomMenuItem countsRadioMenuItem;
412 
413  @FXML
414  private CustomMenuItem hiddenRadioMenuItem;
415 
416  @FXML
417  private SeparatorMenuItem descVisibilitySeparatorMenuItem;
418 
420  FXMLConstructor.construct(this, "DetailViewSettingsPane.fxml"); // NON-NLS
421  }
422 
423  @FXML
424  void initialize() {
425  assert bandByTypeBox != null : "fx:id=\"bandByTypeBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
426  assert oneEventPerRowBox != null : "fx:id=\"oneEventPerRowBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
427  assert truncateAllBox != null : "fx:id=\"truncateAllBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
428  assert truncateWidthSlider != null : "fx:id=\"truncateAllSlider\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
429  bandByTypeBox.selectedProperty().bindBidirectional(chart.getBandByType());
430  truncateAllBox.selectedProperty().bindBidirectional(chart.getTruncateAll());
431  oneEventPerRowBox.selectedProperty().bindBidirectional(chart.getOneEventPerRow());
432  truncateSliderLabel.disableProperty().bind(truncateAllBox.selectedProperty().not());
433  truncateSliderLabel.setText(NbBundle.getMessage(this.getClass(), "DetailViewPane.truncateSliderLabel.text"));
434  final InvalidationListener sliderListener = o -> {
435  if (truncateWidthSlider.isValueChanging() == false) {
436  chart.getTruncateWidth().set(truncateWidthSlider.getValue());
437  }
438  };
439  truncateWidthSlider.valueProperty().addListener(sliderListener);
440  truncateWidthSlider.valueChangingProperty().addListener(sliderListener);
441 
442  descrVisibility.selectedToggleProperty().addListener((observable, oldToggle, newToggle) -> {
443  if (newToggle == countsRadio) {
444  chart.getDescrVisibility().set(DescriptionVisibility.COUNT_ONLY);
445  } else if (newToggle == showRadio) {
446  chart.getDescrVisibility().set(DescriptionVisibility.SHOWN);
447  } else if (newToggle == hiddenRadio) {
448  chart.getDescrVisibility().set(DescriptionVisibility.HIDDEN);
449  }
450  });
451 
452  advancedLayoutOptionsButtonLabel.setText(
453  NbBundle.getMessage(this.getClass(), "DetailViewPane.advancedLayoutOptionsButtonLabel.text"));
454  bandByTypeBox.setText(NbBundle.getMessage(this.getClass(), "DetailViewPane.bandByTypeBox.text"));
455  bandByTypeBoxMenuItem.setText(
456  NbBundle.getMessage(this.getClass(), "DetailViewPane.bandByTypeBoxMenuItem.text"));
457  oneEventPerRowBox.setText(NbBundle.getMessage(this.getClass(), "DetailViewPane.oneEventPerRowBox.text"));
458  oneEventPerRowBoxMenuItem.setText(
459  NbBundle.getMessage(this.getClass(), "DetailViewPane.oneEventPerRowBoxMenuItem.text"));
460  truncateAllBox.setText(NbBundle.getMessage(this.getClass(), "DetailViewPan.truncateAllBox.text"));
461  truncateAllBoxMenuItem.setText(
462  NbBundle.getMessage(this.getClass(), "DetailViewPan.truncateAllBoxMenuItem.text"));
463  truncateSliderLabelMenuItem.setText(
464  NbBundle.getMessage(this.getClass(), "DetailViewPane.truncateSlideLabelMenuItem.text"));
465  descVisibilitySeparatorMenuItem.setText(
466  NbBundle.getMessage(this.getClass(), "DetailViewPane.descVisSeparatorMenuItem.text"));
467  showRadioMenuItem.setText(NbBundle.getMessage(this.getClass(), "DetailViewPane.showRadioMenuItem.text"));
468  showRadio.setText(NbBundle.getMessage(this.getClass(), "DetailViewPane.showRadio.text"));
469  countsRadioMenuItem.setText(NbBundle.getMessage(this.getClass(), "DetailViewPane.countsRadioMenuItem.text"));
470  countsRadio.setText(NbBundle.getMessage(this.getClass(), "DetailViewPane.countsRadio.text"));
471  hiddenRadioMenuItem.setText(NbBundle.getMessage(this.getClass(), "DetailViewPane.hiddenRadioMenuItem.text"));
472  hiddenRadio.setText(NbBundle.getMessage(this.getClass(), "DetailViewPane.hiddenRadio.text"));
473  }
474 
475  }
476 }
void setSelectionModel(MultipleSelectionModel< TreeItem< NavTreeNode >> selectionModel)
final ObservableList< AggregateEvent > aggregatedEvents
void applySelectionEffect(AggregateEventNode c1, Boolean applied)
final ObservableList< BarChart.Series< X, Y > > dataSets
static void construct(Node n, String fxmlFileName)
static RangeDivisionInfo getRangeDivisionInfo(Interval timeRange)
final Map< EventType, XYChart.Series< DateTime, AggregateEvent > > eventTypeToSeriesMap
MultipleSelectionModel< TreeItem< NavTreeNode > > treeSelectionModel
final ObservableList< AggregateEventNode > highlightedNodes
DetailViewPane(Pane partPane, Pane contextPane, Region spacer)
static Logger getLogger(String name)
Definition: Logger.java:131
XYChart.Series< DateTime, AggregateEvent > getSeries(final EventType et)

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