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

Copyright © 2012-2016 Basis Technology. Generated on: Fri Sep 29 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.