Autopsy  4.9.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
ViewFrame.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-16 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;
20 
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.eventbus.Subscribe;
23 import java.time.Instant;
24 import java.time.LocalDate;
25 import java.time.LocalDateTime;
26 import java.time.ZoneOffset;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.function.BiFunction;
30 import java.util.function.Supplier;
31 import javafx.application.Platform;
32 import javafx.beans.InvalidationListener;
33 import javafx.beans.Observable;
34 import javafx.collections.FXCollections;
35 import javafx.collections.ObservableList;
36 import javafx.fxml.FXML;
37 import javafx.geometry.Insets;
38 import javafx.scene.Node;
39 import javafx.scene.control.Button;
40 import javafx.scene.control.Label;
41 import javafx.scene.control.MenuButton;
42 import javafx.scene.control.TitledPane;
43 import javafx.scene.control.ToggleButton;
44 import javafx.scene.control.ToolBar;
45 import javafx.scene.control.Tooltip;
46 import javafx.scene.effect.Lighting;
47 import javafx.scene.image.Image;
48 import javafx.scene.image.ImageView;
49 import javafx.scene.input.MouseEvent;
50 import javafx.scene.layout.Background;
51 import javafx.scene.layout.BackgroundFill;
52 import javafx.scene.layout.BorderPane;
53 import javafx.scene.layout.CornerRadii;
54 import javafx.scene.layout.HBox;
55 import javafx.scene.layout.Priority;
56 import javafx.scene.layout.Region;
57 import static javafx.scene.layout.Region.USE_PREF_SIZE;
58 import javafx.scene.layout.StackPane;
59 import javafx.scene.paint.Color;
60 import javafx.util.Callback;
61 import javax.annotation.Nonnull;
62 import javax.annotation.concurrent.GuardedBy;
63 import jfxtras.scene.control.LocalDateTimePicker;
64 import jfxtras.scene.control.LocalDateTimeTextField;
65 import jfxtras.scene.control.ToggleGroupValue;
66 import org.controlsfx.control.NotificationPane;
67 import org.controlsfx.control.RangeSlider;
68 import org.controlsfx.control.SegmentedButton;
69 import org.controlsfx.control.action.Action;
70 import org.controlsfx.control.action.ActionUtils;
71 import org.joda.time.DateTime;
72 import org.joda.time.Interval;
73 import org.openide.util.NbBundle;
93 import static org.sleuthkit.autopsy.timeline.ui.Bundle.*;
99 import org.sleuthkit.datamodel.Content;
100 
109 final public class ViewFrame extends BorderPane {
110 
111  private static final Logger LOGGER = Logger.getLogger(ViewFrame.class.getName());
112 
113  private static final Image INFORMATION = new Image("org/sleuthkit/autopsy/timeline/images/information.png", 16, 16, true, true); //NON-NLS
114  private static final Image WARNING = new Image("org/sleuthkit/autopsy/timeline/images/warning_triangle.png", 16, 16, true, true); //NON-NLS
115  private static final Image REFRESH = new Image("org/sleuthkit/autopsy/timeline/images/arrow-circle-double-135.png"); //NON-NLS
116  private static final Background GRAY_BACKGROUND = new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY));
117 
123  private final static Region NO_EVENTS_BACKGROUND = new Region() {
124  {
125  setBackground(GRAY_BACKGROUND);
126  setOpacity(.3);
127  }
128  };
129 
134  private static final int SETTINGS_TOOLBAR_INSERTION_INDEX = 2;
135 
140  private static final int TIME_TOOLBAR_INSERTION_INDEX = 2;
141 
142  @GuardedBy("this")
143  private LoggedTask<Void> histogramTask;
144 
145  private final EventsTree eventsTree;
147 
148  /*
149  * HBox that contains the histogram bars.
150  *
151  * //TODO: Abstract this into a seperate class, and/or use a real bar
152  * chart? -jm
153  */
154  @FXML
155  private HBox histogramBox;
156  /*
157  * Stack pane that superimposes rangeslider over histogram
158  */
159  @FXML
160  private StackPane rangeHistogramStack;
161 
162  private final RangeSlider rangeSlider = new RangeSlider(0, 1.0, .25, .75);
163 
167  @FXML
168  private ToolBar timeRangeToolBar;
169 
174  @FXML
175  private HBox zoomInOutHBox;
176 
178  @FXML
179  private MenuButton zoomMenuButton;
180  @FXML
181  private Button zoomOutButton;
182  @FXML
183  private Button zoomInButton;
184  @FXML
185  private LocalDateTimeTextField startPicker;
186  @FXML
187  private LocalDateTimeTextField endPicker;
188  @FXML
189  private Label startLabel;
190  @FXML
191  private Label endLabel;
192 
194  @FXML
195  private ToolBar toolBar;
196 
197  private ToggleGroupValue<ViewMode> viewModeToggleGroup;
198  @FXML
199  private Label viewModeLabel;
200  @FXML
201  private SegmentedButton modeSegButton;
202  @FXML
203  private ToggleButton countsToggle;
204  @FXML
205  private ToggleButton detailsToggle;
206  @FXML
207  private ToggleButton listToggle;
208 
209  @FXML
210  private Button snapShotButton;
211  @FXML
212  private Button refreshButton;
213  @FXML
214  private Button updateDBButton;
215 
216  /*
217  * Default zoom in/out buttons provided by the ViewFrame, some views replace
218  * these with other nodes (eg, list view)
219  */
220  @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
221  private ImmutableList<Node> defaultTimeNavigationNodes;
222 
223  /*
224  * The settings nodes for the current view.
225  */
226  @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
227  private final ObservableList<Node> settingsNodes = FXCollections.observableArrayList();
228 
229  /*
230  * The time nagivation nodes for the current view.
231  */
232  @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
233  private final ObservableList<Node> timeNavigationNodes = FXCollections.observableArrayList();
234 
239  private final NotificationPane notificationPane = new NotificationPane();
240 
243 
248  private final InvalidationListener rangeSliderListener = new InvalidationListener() {
249  @Override
250  public void invalidated(Observable observable) {
251  if (rangeSlider.isHighValueChanging() == false
252  && rangeSlider.isLowValueChanging() == false) {
253  Long minTime = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.getSpanningInterval()).getLowerBound();
254  if (false == controller.pushTimeRange(new Interval(
255  (long) (rangeSlider.getLowValue() + minTime),
256  (long) (rangeSlider.getHighValue() + minTime + 1000)))) {
257  refreshTimeUI();
258  }
259  }
260  }
261  };
262 
266  private final InvalidationListener zoomListener = any -> handleRefreshRequested(null);
267 
271  private final InvalidationListener endListener = new PickerListener(() -> endPicker, Interval::withEndMillis);
272 
276  private final InvalidationListener startListener = new PickerListener(() -> startPicker, Interval::withStartMillis);
277 
287  private static long localDateTimeToEpochMilli(LocalDateTime localDateTime) {
288  return localDateTime.atZone(TimeLineController.getTimeZoneID()).toInstant().toEpochMilli();
289  }
290 
299  private static LocalDateTime epochMillisToLocalDateTime(long millis) {
300  return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), TimeLineController.getTimeZoneID());
301  }
302 
310  this.controller = controller;
311  this.filteredEvents = controller.getEventsModel();
312  this.eventsTree = eventsTree;
313  FXMLConstructor.construct(this, "ViewFrame.fxml"); //NON-NLS
314 
315  }
316 
317  @FXML
318  @NbBundle.Messages({
319  "ViewFrame.viewModeLabel.text=View Mode:",
320  "ViewFrame.startLabel.text=Start:",
321  "ViewFrame.endLabel.text=End:",
322  "ViewFrame.countsToggle.text=Counts",
323  "ViewFrame.detailsToggle.text=Details",
324  "ViewFrame.listToggle.text=List",
325  "ViewFrame.zoomMenuButton.text=Zoom in/out to",
326  "ViewFrame.tagsAddedOrDeleted=Tags have been created and/or deleted. The view may not be up to date."
327  })
328  void initialize() {
329  assert endPicker != null : "fx:id=\"endPicker\" was not injected: check your FXML file 'ViewWrapper.fxml'."; //NON-NLS
330  assert histogramBox != null : "fx:id=\"histogramBox\" was not injected: check your FXML file 'ViewWrapper.fxml'."; //NON-NLS
331  assert startPicker != null : "fx:id=\"startPicker\" was not injected: check your FXML file 'ViewWrapper.fxml'."; //NON-NLS
332  assert rangeHistogramStack != null : "fx:id=\"rangeHistogramStack\" was not injected: check your FXML file 'ViewWrapper.fxml'."; //NON-NLS
333  assert countsToggle != null : "fx:id=\"countsToggle\" was not injected: check your FXML file 'VisToggle.fxml'."; //NON-NLS
334  assert detailsToggle != null : "fx:id=\"eventsToggle\" was not injected: check your FXML file 'VisToggle.fxml'."; //NON-NLS
335 
338 
339  //configure notification pane
340  notificationPane.getStyleClass().add(NotificationPane.STYLE_CLASS_DARK);
341  setCenter(notificationPane);
342 
343  //configure view mode toggle
344  viewModeLabel.setText(Bundle.ViewFrame_viewModeLabel_text());
345  countsToggle.setText(Bundle.ViewFrame_countsToggle_text());
346  detailsToggle.setText(Bundle.ViewFrame_detailsToggle_text());
347  listToggle.setText(Bundle.ViewFrame_listToggle_text());
348  viewModeToggleGroup = new ToggleGroupValue<>();
352  modeSegButton.setToggleGroup(viewModeToggleGroup);
353  viewModeToggleGroup.valueProperty().addListener((observable, oldViewMode, newViewVode)
354  -> controller.setViewMode(newViewVode != null ? newViewVode : (oldViewMode != null ? oldViewMode : ViewMode.COUNTS))
355  );
356 
357  controller.viewModeProperty().addListener(viewMode -> syncViewMode());
358  syncViewMode();
359 
360  ActionUtils.configureButton(new SaveSnapshotAsReport(controller, notificationPane::getContent), snapShotButton);
361  ActionUtils.configureButton(new UpdateDB(controller), updateDBButton);
362 
364  startLabel.setText(Bundle.ViewFrame_startLabel_text());
365  endLabel.setText(Bundle.ViewFrame_endLabel_text());
366 
367  //suppress stacktraces on malformed input
368  //TODO: should we do anything else? show a warning?
369  startPicker.setParseErrorCallback(throwable -> null);
370  endPicker.setParseErrorCallback(throwable -> null);
371 
372  //disable dates outside scope of case
373  LocalDateDisabler localDateDisabler = new LocalDateDisabler();
374  startPicker.setLocalDateTimeRangeCallback(localDateDisabler);
375  endPicker.setLocalDateTimeRangeCallback(localDateDisabler);
376 
377  //prevent selection of (date/)times outside the scope of this case
378  startPicker.setValueValidationCallback(new LocalDateTimeValidator(startPicker));
379  endPicker.setValueValidationCallback(new LocalDateTimeValidator(endPicker));
380 
381  //setup rangeslider
382  rangeSlider.setOpacity(.7);
383  rangeSlider.setMin(0);
384  rangeSlider.setBlockIncrement(1);
385  rangeHistogramStack.getChildren().add(rangeSlider);
386 
387  /*
388  * This padding attempts to compensates for the fact that the
389  * rangeslider track doesn't extend to edge of node,and so the
390  * histrogram doesn't quite line up with the rangeslider
391  */
392  histogramBox.setStyle(" -fx-padding: 0,0.5em,0,.5em; "); //NON-NLS
393 
394  //configure zoom buttons
395  zoomMenuButton.getItems().clear();
396  for (ZoomRanges zoomRange : ZoomRanges.values()) {
397  zoomMenuButton.getItems().add(ActionUtils.createMenuItem(
398  new Action(zoomRange.getDisplayName(), event -> controller.pushPeriod(zoomRange.getPeriod()))
399  ));
400  }
401  zoomMenuButton.setText(Bundle.ViewFrame_zoomMenuButton_text());
402  ActionUtils.configureButton(new ZoomOut(controller), zoomOutButton);
403  ActionUtils.configureButton(new ZoomIn(controller), zoomInButton);
404 
405  //register for EventBus events (tags)
407 
408  //listen for changes in the time range / zoom params
409  TimeLineController.getTimeZone().addListener(timeZoneProp -> refreshTimeUI());
410  filteredEvents.timeRangeProperty().addListener(timeRangeProp -> refreshTimeUI());
411  filteredEvents.zoomParametersProperty().addListener(zoomListener);
412  refreshTimeUI(); //populate the view
413 
415 
416  }
417 
426  @Subscribe
428  hostedView.setOutOfDate();
429  Platform.runLater(() -> {
430  if (notificationPane.isShowing() == false) {
431  notificationPane.getActions().setAll(new Refresh());
432  notificationPane.show(Bundle.ViewFrame_tagsAddedOrDeleted(), new ImageView(INFORMATION));
433  }
434  });
435  }
436 
446  @Subscribe
448  Platform.runLater(() -> {
449  if (Bundle.ViewFrame_tagsAddedOrDeleted().equals(notificationPane.getText())) {
450  notificationPane.hide();
451  }
452  });
453  }
454 
463  @Subscribe
464  public void handleDBUpdated(DBUpdatedEvent event) {
467  Platform.runLater(notificationPane::hide);
468  }
469 
479  @Subscribe
480  @NbBundle.Messages({
481  "# {0} - datasource name",
482  "ViewFrame.notification.newDataSource={0} has been added as a new datasource. The Timeline DB may be out of date."})
484  Platform.runLater(() -> {
485  notificationPane.getActions().setAll(new UpdateDB(controller));
486  notificationPane.show(Bundle.ViewFrame_notification_newDataSource(event.getDataSource().getName()), new ImageView(WARNING));
487  });
488  }
489 
499  @Subscribe
500  @NbBundle.Messages({
501  "# {0} - datasource name",
502  "ViewFrame.notification.analysisComplete=Analysis has finished for {0}. The Timeline DB may be out of date."})
504  Platform.runLater(() -> {
505  Content dataSource = event.getDataSource();
506  if (dataSource != null) {
507  notificationPane.getActions().setAll(new UpdateDB(controller));
508  notificationPane.show(Bundle.ViewFrame_notification_analysisComplete(dataSource.getName()), new ImageView(WARNING));
509  }
510  });
511  }
512 
516  @NbBundle.Messages({"ViewFrame.histogramTask.title=Rebuilding Histogram",
517  "ViewFrame.histogramTask.preparing=Preparing",
518  "ViewFrame.histogramTask.resetUI=Resetting UI",
519  "ViewFrame.histogramTask.queryDb=Querying FB",
520  "ViewFrame.histogramTask.updateUI2=Updating UI"})
521  synchronized private void refreshHistorgram() {
522  if (histogramTask != null) {
523  histogramTask.cancel(true);
524  }
525 
526  histogramTask = new LoggedTask<Void>(Bundle.ViewFrame_histogramTask_title(), true) {
527  private final Lighting lighting = new Lighting();
528 
529  @Override
530  protected Void call() throws Exception {
531 
532  updateMessage(ViewFrame_histogramTask_preparing());
533 
534  long max = 0;
536  final long lowerBound = rangeInfo.getLowerBound();
537  final long upperBound = rangeInfo.getUpperBound();
538  Interval timeRange = new Interval(new DateTime(lowerBound, TimeLineController.getJodaTimeZone()), new DateTime(upperBound, TimeLineController.getJodaTimeZone()));
539 
540  //extend range to block bounderies (ie day, month, year)
541  int p = 0; // progress counter
542 
543  //clear old data, and reset ranges and series
544  Platform.runLater(() -> {
545  updateMessage(ViewFrame_histogramTask_resetUI());
546 
547  });
548 
549  ArrayList<Long> bins = new ArrayList<>();
550 
551  DateTime start = timeRange.getStart();
552  while (timeRange.contains(start)) {
553  if (isCancelled()) {
554  return null;
555  }
556  DateTime end = start.plus(rangeInfo.getPeriodSize().getPeriod());
557  final Interval interval = new Interval(start, end);
558  //increment for next iteration
559 
560  start = end;
561 
562  updateMessage(ViewFrame_histogramTask_queryDb());
563  //query for current range
564  long count = filteredEvents.getEventCounts(interval).values().stream().mapToLong(Long::valueOf).sum();
565  bins.add(count);
566 
567  max = Math.max(count, max);
568 
569  final double fMax = Math.log(max);
570  final ArrayList<Long> fbins = new ArrayList<>(bins);
571  Platform.runLater(() -> {
572  updateMessage(ViewFrame_histogramTask_updateUI2());
573 
574  histogramBox.getChildren().clear();
575 
576  for (Long bin : fbins) {
577  if (isCancelled()) {
578  break;
579  }
580  Region bar = new Region();
581  //scale them to fit in histogram height
582  bar.prefHeightProperty().bind(histogramBox.heightProperty().multiply(Math.log(bin)).divide(fMax));
583  bar.setMaxHeight(USE_PREF_SIZE);
584  bar.setMinHeight(USE_PREF_SIZE);
585  bar.setBackground(GRAY_BACKGROUND);
586  bar.setOnMouseEntered((MouseEvent event) -> {
587  Tooltip.install(bar, new Tooltip(bin.toString()));
588  });
589  bar.setEffect(lighting);
590  //they each get equal width to fill the histogram horizontally
591  HBox.setHgrow(bar, Priority.ALWAYS);
592  histogramBox.getChildren().add(bar);
593  }
594  });
595  }
596  return null;
597  }
598 
599  };
600  new Thread(histogramTask).start();
602  }
603 
607  private void refreshTimeUI() {
609  final long minTime = rangeDivisionInfo.getLowerBound();
610  final long maxTime = rangeDivisionInfo.getUpperBound();
611 
612  long startMillis = filteredEvents.getTimeRange().getStartMillis();
613  long endMillis = filteredEvents.getTimeRange().getEndMillis();
614 
615  if (minTime > 0 && maxTime > minTime) {
616  Platform.runLater(() -> {
617  startPicker.localDateTimeProperty().removeListener(startListener);
618  endPicker.localDateTimeProperty().removeListener(endListener);
619  rangeSlider.highValueChangingProperty().removeListener(rangeSliderListener);
620  rangeSlider.lowValueChangingProperty().removeListener(rangeSliderListener);
621 
622  rangeSlider.setMax((maxTime - minTime));
623 
624  rangeSlider.setLowValue(startMillis - minTime);
625  rangeSlider.setHighValue(endMillis - minTime);
626  startPicker.setLocalDateTime(epochMillisToLocalDateTime(startMillis));
627  endPicker.setLocalDateTime(epochMillisToLocalDateTime(endMillis));
628 
629  rangeSlider.highValueChangingProperty().addListener(rangeSliderListener);
630  rangeSlider.lowValueChangingProperty().addListener(rangeSliderListener);
631  startPicker.localDateTimeProperty().addListener(startListener);
632  endPicker.localDateTimeProperty().addListener(endListener);
633  });
634  }
635  }
636 
642  private void syncViewMode() {
643  ViewMode newViewMode = controller.getViewMode();
644 
645  //clear out old view.
646  if (hostedView != null) {
647  hostedView.dispose();
648  }
649 
650  //Set a new AbstractTimeLineView as the one hosted by this ViewFrame.
651  switch (newViewMode) {
652  case LIST:
654  //TODO: should remove listeners from events tree
655  break;
656  case COUNTS:
658  //TODO: should remove listeners from events tree
659  break;
660  case DETAIL:
661  DetailViewPane detailViewPane = new DetailViewPane(controller);
662  //link events tree to detailview instance.
664  eventsTree.setDetailViewPane(detailViewPane);
665  hostedView = detailViewPane;
666  break;
667  default:
668  throw new IllegalArgumentException("Unknown ViewMode: " + newViewMode.toString());//NON-NLS
669  }
671 
672  viewModeToggleGroup.setValue(newViewMode); //this selects the right toggle automatically
673 
674  //configure settings and time navigation nodes
679 
680  //do further setup of new view.
681  ActionUtils.configureButton(new Refresh(), refreshButton);//configure new refresh action for new view
683  notificationPane.setContent(hostedView);
684  //listen to has events property and show "dialog" if it is false.
685  hostedView.hasVisibleEventsProperty().addListener(hasEvents -> {
687  ? hostedView
688  : new StackPane(hostedView,
689  NO_EVENTS_BACKGROUND,
690  new NoEventsDialog(() -> notificationPane.setContent(hostedView))
691  )
692  );
693  });
694  }
695 
703  private void setViewSettingsControls(List<Node> newSettingsNodes) {
704  toolBar.getItems().removeAll(this.settingsNodes); //remove old nodes
705  this.settingsNodes.setAll(newSettingsNodes);
706  toolBar.getItems().addAll(SETTINGS_TOOLBAR_INSERTION_INDEX, settingsNodes);
707  }
708 
717  timeRangeToolBar.getItems().removeAll(this.timeNavigationNodes); //remove old nodes
718  this.timeNavigationNodes.setAll(timeNavigationNodes);
719  timeRangeToolBar.getItems().addAll(TIME_TOOLBAR_INSERTION_INDEX, timeNavigationNodes);
720  }
721 
722  @NbBundle.Messages("NoEventsDialog.titledPane.text=No Visible Events")
723  private class NoEventsDialog extends StackPane {
724 
725  @FXML
726  private TitledPane titledPane;
727  @FXML
728  private Button backButton;
729  @FXML
730  private Button resetFiltersButton;
731  @FXML
732  private Button dismissButton;
733  @FXML
734  private Button zoomButton;
735  @FXML
736  private Label noEventsDialogLabel;
737 
738  private final Runnable closeCallback;
739 
740  private NoEventsDialog(Runnable closeCallback) {
741  this.closeCallback = closeCallback;
742  FXMLConstructor.construct(this, "NoEventsDialog.fxml"); //NON-NLS
743  }
744 
745  @FXML
746  @NbBundle.Messages("ViewFrame.noEventsDialogLabel.text=There are no events visible with the current zoom / filter settings.")
747  void initialize() {
748  assert resetFiltersButton != null : "fx:id=\"resetFiltersButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; //NON-NLS
749  assert dismissButton != null : "fx:id=\"dismissButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; //NON-NLS
750  assert zoomButton != null : "fx:id=\"zoomButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; //NON-NLS
751 
752  titledPane.setText(Bundle.NoEventsDialog_titledPane_text());
753  noEventsDialogLabel.setText(Bundle.ViewFrame_noEventsDialogLabel_text());
754 
755  dismissButton.setOnAction(actionEvent -> closeCallback.run());
756 
757  ActionUtils.configureButton(new ZoomToEvents(controller), zoomButton);
758  ActionUtils.configureButton(new Back(controller), backButton);
759  ActionUtils.configureButton(new ResetFilters(controller), resetFiltersButton);
760  }
761  }
762 
767  private class PickerListener implements InvalidationListener {
768 
769  private final BiFunction< Interval, Long, Interval> intervalMapper;
770  private final Supplier<LocalDateTimeTextField> pickerSupplier;
771 
772  PickerListener(Supplier<LocalDateTimeTextField> pickerSupplier, BiFunction<Interval, Long, Interval> intervalMapper) {
773  this.pickerSupplier = pickerSupplier;
774  this.intervalMapper = intervalMapper;
775  }
776 
777  @Override
778  public void invalidated(Observable observable) {
779  LocalDateTime pickerTime = pickerSupplier.get().getLocalDateTime();
780  if (pickerTime != null) {
781  controller.pushTimeRange(intervalMapper.apply(filteredEvents.timeRangeProperty().get(), localDateTimeToEpochMilli(pickerTime)));
782  Platform.runLater(ViewFrame.this::refreshTimeUI);
783  }
784  }
785  }
786 
790  private class LocalDateDisabler implements Callback<LocalDateTimePicker.LocalDateTimeRange, Void> {
791 
792  @Override
793  public Void call(LocalDateTimePicker.LocalDateTimeRange viewedRange) {
794  startPicker.disabledLocalDateTimes().clear();
795  endPicker.disabledLocalDateTimes().clear();
796 
797  //all events in the case are contained in this interval
798  Interval spanningInterval = filteredEvents.getSpanningInterval();
799  long spanStartMillis = spanningInterval.getStartMillis();
800  long spaneEndMillis = spanningInterval.getEndMillis();
801 
802  LocalDate rangeStartLocalDate = viewedRange.getStartLocalDateTime().toLocalDate();
803  LocalDate rangeEndLocalDate = viewedRange.getEndLocalDateTime().toLocalDate().plusDays(1);
804  //iterate over days of the displayed range and disable ones not in spanning interval
805  for (LocalDate dt = rangeStartLocalDate; false == dt.isAfter(rangeEndLocalDate); dt = dt.plusDays(1)) {
806  long startOfDay = dt.atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli();
807  long endOfDay = dt.plusDays(1).atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli();
808  //if no part of day is within spanning interval, add that date the list of disabled dates.
809  if (endOfDay < spanStartMillis || startOfDay > spaneEndMillis) {
810  startPicker.disabledLocalDateTimes().add(dt.atStartOfDay());
811  endPicker.disabledLocalDateTimes().add(dt.atStartOfDay());
812  }
813  }
814  return null;
815  }
816  }
817 
823  private class LocalDateTimeValidator implements Callback<LocalDateTime, Boolean> {
824 
828  private final LocalDateTimeTextField picker;
829 
830  LocalDateTimeValidator(LocalDateTimeTextField picker) {
831  this.picker = picker;
832  }
833 
834  @Override
835  public Boolean call(LocalDateTime param) {
836  long epochMilli = localDateTimeToEpochMilli(param);
837  if (filteredEvents.getSpanningInterval().contains(epochMilli)) {
838  return true;
839  } else {
840  if (picker.isPickerShowing() == false) {
841  //if the user typed an in valid date, reset the text box to the selected date.
842  picker.setDisplayedLocalDateTime(picker.getLocalDateTime());
843  }
844  return false;
845  }
846  }
847  }
848 
852  private class Refresh extends Action {
853 
854  @NbBundle.Messages({
855  "ViewFrame.refresh.text=Refresh View",
856  "ViewFrame.refresh.longText=Refresh the view to include information that is in the DB but not displayed, such as newly updated tags."})
857  Refresh() {
858  super(Bundle.ViewFrame_refresh_text());
859  setLongText(Bundle.ViewFrame_refresh_longText());
860  setGraphic(new ImageView(REFRESH));
861  setEventHandler(actionEvent -> filteredEvents.postRefreshRequest());
862  disabledProperty().bind(hostedView.outOfDateProperty().not());
863  }
864  }
865 }
Void call(LocalDateTimePicker.LocalDateTimeRange viewedRange)
Definition: ViewFrame.java:793
final InvalidationListener zoomListener
Definition: ViewFrame.java:266
void setViewSettingsControls(List< Node > newSettingsNodes)
Definition: ViewFrame.java:703
void handleDBUpdated(DBUpdatedEvent event)
Definition: ViewFrame.java:464
abstract ImmutableList< Node > getTimeNavigationControls()
final ObservableList< Node > settingsNodes
Definition: ViewFrame.java:227
synchronized void setViewMode(ViewMode viewMode)
final FilteredEventsModel filteredEvents
Definition: ViewFrame.java:242
Map< EventType, Long > getEventCounts(Interval timeRange)
synchronized ReadOnlyObjectProperty< ZoomParams > zoomParametersProperty()
final ObservableList< Node > timeNavigationNodes
Definition: ViewFrame.java:233
abstract ImmutableList< Node > getSettingsControls()
void handleTimeLineTagUpdate(TagsUpdatedEvent event)
Definition: ViewFrame.java:427
synchronized boolean pushTimeRange(Interval timeRange)
void setTimeNavigationControls(List< Node > timeNavigationNodes)
Definition: ViewFrame.java:716
static RangeDivisionInfo getRangeDivisionInfo(Interval timeRange)
final InvalidationListener endListener
Definition: ViewFrame.java:271
final InvalidationListener rangeSliderListener
Definition: ViewFrame.java:248
synchronized void monitorTask(final Task<?> task)
synchronized void pushPeriod(ReadablePeriod period)
final InvalidationListener startListener
Definition: ViewFrame.java:276
void setHighLightedEvents(ObservableList< TimeLineEvent > highlightedEvents)
ViewFrame(@Nonnull TimeLineController controller,@Nonnull EventsTree eventsTree)
Definition: ViewFrame.java:309
static LocalDateTime epochMillisToLocalDateTime(long millis)
Definition: ViewFrame.java:299
void handleAnalysisCompleted(DataSourceAnalysisCompletedEvent event)
Definition: ViewFrame.java:503
void handleRefreshRequested(RefreshRequestedEvent event)
Definition: ViewFrame.java:447
ToggleGroupValue< ViewMode > viewModeToggleGroup
Definition: ViewFrame.java:197
final BiFunction< Interval, Long, Interval > intervalMapper
Definition: ViewFrame.java:769
synchronized ReadOnlyObjectProperty< ViewMode > viewModeProperty()
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void setDetailViewPane(DetailViewPane detailViewPane)
Definition: EventsTree.java:85
final Supplier< LocalDateTimeTextField > pickerSupplier
Definition: ViewFrame.java:770
static void construct(Node node, String fxmlFileName)
synchronized ReadOnlyObjectProperty< Interval > timeRangeProperty()
ImmutableList< Node > defaultTimeNavigationNodes
Definition: ViewFrame.java:221
static long localDateTimeToEpochMilli(LocalDateTime localDateTime)
Definition: ViewFrame.java:287
void handlDataSourceAdded(DataSourceAddedEvent event)
Definition: ViewFrame.java:483

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