always show tag notification

This commit is contained in:
jmillman 2016-05-03 14:21:36 -04:00
parent 076ee69394
commit dfce250309
3 changed files with 128 additions and 111 deletions

View File

@ -1,7 +1,20 @@
/* /*
* To change this license header, choose License Headers in Project Properties. * Autopsy Forensic Browser
* To change this template file, choose Tools | Templates *
* and open the template in the editor. * Copyright 2014-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.sleuthkit.autopsy.timeline.events; package org.sleuthkit.autopsy.timeline.events;

View File

@ -36,6 +36,5 @@ abstract public class TagsUpdatedEvent {
public TagsUpdatedEvent(Set<Long> updatedEventIDs) { public TagsUpdatedEvent(Set<Long> updatedEventIDs) {
this.updatedEventIDs = updatedEventIDs; this.updatedEventIDs = updatedEventIDs;
} }
} }

View File

@ -31,7 +31,6 @@ import javafx.beans.InvalidationListener;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.control.Button; import javafx.scene.control.Button;
@ -81,8 +80,8 @@ import org.sleuthkit.autopsy.timeline.actions.ZoomIn;
import org.sleuthkit.autopsy.timeline.actions.ZoomOut; import org.sleuthkit.autopsy.timeline.actions.ZoomOut;
import org.sleuthkit.autopsy.timeline.actions.ZoomToEvents; import org.sleuthkit.autopsy.timeline.actions.ZoomToEvents;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent;
import org.sleuthkit.autopsy.timeline.events.TagsUpdatedEvent; import org.sleuthkit.autopsy.timeline.events.TagsUpdatedEvent;
import org.sleuthkit.autopsy.timeline.filters.TagsFilter;
import org.sleuthkit.autopsy.timeline.ui.countsview.CountsViewPane; import org.sleuthkit.autopsy.timeline.ui.countsview.CountsViewPane;
import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane; import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane;
import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree; import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree;
@ -97,16 +96,16 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
* TODO: refactor common code out of histogram and CountsView? -jm * TODO: refactor common code out of histogram and CountsView? -jm
*/ */
final public class VisualizationPanel extends BorderPane { final public class VisualizationPanel extends BorderPane {
private static final Logger LOGGER = Logger.getLogger(VisualizationPanel.class.getName()); private static final Logger LOGGER = Logger.getLogger(VisualizationPanel.class.getName());
private static final Image INFORMATION = new Image("org/sleuthkit/autopsy/timeline/images/information.png", 16, 16, true, true); // NON-NLS private static final Image INFORMATION = new Image("org/sleuthkit/autopsy/timeline/images/information.png", 16, 16, true, true); // NON-NLS
private static final Image REFRESH = new Image("org/sleuthkit/autopsy/timeline/images/arrow-circle-double-135.png"); // NON-NLS private static final Image REFRESH = new Image("org/sleuthkit/autopsy/timeline/images/arrow-circle-double-135.png"); // NON-NLS
private static final Background background = new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY)); private static final Background background = new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY));
@GuardedBy("this") @GuardedBy("this")
private LoggedTask<Void> histogramTask; private LoggedTask<Void> histogramTask;
private final EventsTree eventsTree; private final EventsTree eventsTree;
private AbstractVisualizationPane<?, ?, ?, ?> visualization; private AbstractVisualizationPane<?, ?, ?, ?> visualization;
//// range slider and histogram componenets //// range slider and histogram componenets
@ -121,13 +120,13 @@ final public class VisualizationPanel extends BorderPane {
*/ */
@FXML @FXML
private StackPane rangeHistogramStack; private StackPane rangeHistogramStack;
private final RangeSlider rangeSlider = new RangeSlider(0, 1.0, .25, .75); private final RangeSlider rangeSlider = new RangeSlider(0, 1.0, .25, .75);
//// time range selection components //// time range selection components
@FXML @FXML
private MenuButton zoomMenuButton; private MenuButton zoomMenuButton;
@FXML @FXML
private Button zoomOutButton; private Button zoomOutButton;
@FXML @FXML
@ -165,7 +164,7 @@ final public class VisualizationPanel extends BorderPane {
* wraps contained visualization so that we can show notifications over it. * wraps contained visualization so that we can show notifications over it.
*/ */
private final NotificationPane notificationPane = new NotificationPane(); private final NotificationPane notificationPane = new NotificationPane();
private final TimeLineController controller; private final TimeLineController controller;
private final FilteredEventsModel filteredEvents; private final FilteredEventsModel filteredEvents;
@ -216,26 +215,26 @@ final public class VisualizationPanel extends BorderPane {
} }
/** /**
* convert the given epoch millis to a LocalDateTime USING THE CURERNT * Convert the given epoch millis to a LocalDateTime USING THE CURERNT
* TIMEZONE FROM TIMELINECONTROLLER * TIMEZONE FROM TIMELINECONTROLLER
* *
* @param millis * @param millis The milliseconds to convert.
* *
* @return the given epoch millis as a LocalDateTime * @return The given epoch millis as a LocalDateTime
*/ */
private static LocalDateTime epochMillisToLocalDateTime(long millis) { private static LocalDateTime epochMillisToLocalDateTime(long millis) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), TimeLineController.getTimeZoneID()); return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), TimeLineController.getTimeZoneID());
} }
public VisualizationPanel(@Nonnull TimeLineController controller, @Nonnull EventsTree eventsTree) { public VisualizationPanel(@Nonnull TimeLineController controller, @Nonnull EventsTree eventsTree) {
this.controller = controller; this.controller = controller;
this.filteredEvents = controller.getEventsModel(); this.filteredEvents = controller.getEventsModel();
this.eventsTree = eventsTree; this.eventsTree = eventsTree;
FXMLConstructor.construct(this, "VisualizationPanel.fxml"); // NON-NLS FXMLConstructor.construct(this, "VisualizationPanel.fxml"); // NON-NLS
} }
@FXML // This method is called by the FXMLLoader when initialization is complete @FXML // This method is called by the FXMLLoader when initialization is complete
@NbBundle.Messages({"VisualizationPanel.refresh=refresh", @NbBundle.Messages({
"VisualizationPanel.visualizationModeLabel.text=Visualization Mode:", "VisualizationPanel.visualizationModeLabel.text=Visualization Mode:",
"VisualizationPanel.startLabel.text=Start:", "VisualizationPanel.startLabel.text=Start:",
"VisualizationPanel.endLabel.text=End:", "VisualizationPanel.endLabel.text=End:",
@ -252,15 +251,6 @@ final public class VisualizationPanel extends BorderPane {
//configure notification pane //configure notification pane
notificationPane.getStyleClass().add(NotificationPane.STYLE_CLASS_DARK); notificationPane.getStyleClass().add(NotificationPane.STYLE_CLASS_DARK);
notificationPane.getActions().setAll(new Action(Bundle.VisualizationPanel_refresh()) {
{
setGraphic(new ImageView(REFRESH));
setEventHandler((ActionEvent t) -> {
filteredEvents.refresh();
notificationPane.hide();
});
}
});
setCenter(notificationPane); setCenter(notificationPane);
//configure visualization mode toggle //configure visualization mode toggle
@ -276,7 +266,7 @@ final public class VisualizationPanel extends BorderPane {
controller.setViewMode(VisualizationMode.DETAIL); controller.setViewMode(VisualizationMode.DETAIL);
} }
}; };
if (countsToggle.getToggleGroup() != null) { if (countsToggle.getToggleGroup() != null) {
countsToggle.getToggleGroup().selectedToggleProperty().addListener(toggleListener); countsToggle.getToggleGroup().selectedToggleProperty().addListener(toggleListener);
} else { } else {
@ -353,9 +343,9 @@ final public class VisualizationPanel extends BorderPane {
} }
}); });
refreshHistorgram(); refreshHistorgram();
} }
private void setViewMode(VisualizationMode visualizationMode) { private void setViewMode(VisualizationMode visualizationMode) {
switch (visualizationMode) { switch (visualizationMode) {
case COUNTS: case COUNTS:
@ -368,7 +358,7 @@ final public class VisualizationPanel extends BorderPane {
break; break;
} }
} }
private synchronized void setVisualization(final AbstractVisualizationPane<?, ?, ?, ?> newViz) { private synchronized void setVisualization(final AbstractVisualizationPane<?, ?, ?, ?> newViz) {
Platform.runLater(() -> { Platform.runLater(() -> {
synchronized (VisualizationPanel.this) { synchronized (VisualizationPanel.this) {
@ -376,11 +366,11 @@ final public class VisualizationPanel extends BorderPane {
toolBar.getItems().removeAll(visualization.getSettingsNodes()); toolBar.getItems().removeAll(visualization.getSettingsNodes());
visualization.dispose(); visualization.dispose();
} }
visualization = newViz; visualization = newViz;
visualization.update(); visualization.update();
toolBar.getItems().addAll(newViz.getSettingsNodes()); toolBar.getItems().addAll(newViz.getSettingsNodes());
notificationPane.setContent(visualization); notificationPane.setContent(visualization);
if (visualization instanceof DetailViewPane) { if (visualization instanceof DetailViewPane) {
Platform.runLater(() -> { Platform.runLater(() -> {
@ -390,7 +380,7 @@ final public class VisualizationPanel extends BorderPane {
} }
visualization.hasEvents.addListener((observable, oldValue, newValue) -> { visualization.hasEvents.addListener((observable, oldValue, newValue) -> {
if (newValue == false) { if (newValue == false) {
notificationPane.setContent( notificationPane.setContent(
new StackPane(visualization, new StackPane(visualization,
new Region() { new Region() {
@ -407,75 +397,79 @@ final public class VisualizationPanel extends BorderPane {
} }
}); });
} }
@Subscribe @Subscribe
@NbBundle.Messages("VisualizationPanel.tagsAddedOrDeleted=Tags have been created and/or deleted. The visualization may not be up to date.") @NbBundle.Messages("VisualizationPanel.tagsAddedOrDeleted=Tags have been created and/or deleted. The visualization may not be up to date.")
public void handleTimeLineTagEvent(TagsUpdatedEvent event) { public void handleTimeLineTagEvent(TagsUpdatedEvent event) {
TagsFilter tagsFilter = filteredEvents.getFilter().getTagsFilter(); Platform.runLater(() -> {
if (tagsFilter.isSelected() && tagsFilter.isDisabled() == false) { notificationPane.setCloseButtonVisible(false);
Platform.runLater(() -> { notificationPane.getActions().setAll(new Refresh());
notificationPane.show(Bundle.VisualizationPanel_tagsAddedOrDeleted(), new ImageView(INFORMATION)); notificationPane.show(Bundle.VisualizationPanel_tagsAddedOrDeleted(), new ImageView(INFORMATION));
}); });
}
} }
@Subscribe
public void handleRefreshRequestedEvent(RefreshRequestedEvent event) {
Platform.runLater(notificationPane::hide);
}
synchronized private void refreshHistorgram() { synchronized private void refreshHistorgram() {
if (histogramTask != null) { if (histogramTask != null) {
histogramTask.cancel(true); histogramTask.cancel(true);
} }
histogramTask = new LoggedTask<Void>( histogramTask = new LoggedTask<Void>(
NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.title"), true) { // NON-NLS NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.title"), true) { // NON-NLS
private final Lighting lighting = new Lighting(); private final Lighting lighting = new Lighting();
@Override @Override
protected Void call() throws Exception { protected Void call() throws Exception {
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.preparing")); // NON-NLS
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.preparing")); // NON-NLS long max = 0;
final RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.getSpanningInterval());
final long lowerBound = rangeInfo.getLowerBound();
final long upperBound = rangeInfo.getUpperBound();
Interval timeRange = new Interval(new DateTime(lowerBound, TimeLineController.getJodaTimeZone()), new DateTime(upperBound, TimeLineController.getJodaTimeZone()));
long max = 0; //extend range to block bounderies (ie day, month, year)
final RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.getSpanningInterval()); int p = 0; // progress counter
final long lowerBound = rangeInfo.getLowerBound();
final long upperBound = rangeInfo.getUpperBound();
Interval timeRange = new Interval(new DateTime(lowerBound, TimeLineController.getJodaTimeZone()), new DateTime(upperBound, TimeLineController.getJodaTimeZone()));
//extend range to block bounderies (ie day, month, year) //clear old data, and reset ranges and series
int p = 0; // progress counter Platform.runLater(() -> {
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.resetUI")); // NON-NLS
//clear old data, and reset ranges and series });
Platform.runLater(() -> {
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.resetUI")); // NON-NLS ArrayList<Long> bins = new ArrayList<>();
DateTime start = timeRange.getStart();
while (timeRange.contains(start)) {
if (isCancelled()) {
return null;
}
DateTime end = start.plus(rangeInfo.getPeriodSize().getPeriod());
final Interval interval = new Interval(start, end);
//increment for next iteration
}); start = end;
ArrayList<Long> bins = new ArrayList<>(); updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.queryDb")); // NON-NLS
//query for current range
DateTime start = timeRange.getStart(); long count = filteredEvents.getEventCounts(interval).values().stream().mapToLong(Long::valueOf).sum();
while (timeRange.contains(start)) { bins.add(count);
if (isCancelled()) {
return null; max = Math.max(count, max);
}
DateTime end = start.plus(rangeInfo.getPeriodSize().getPeriod()); final double fMax = Math.log(max);
final Interval interval = new Interval(start, end); final ArrayList<Long> fbins = new ArrayList<>(bins);
//increment for next iteration Platform.runLater(() -> {
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.updateUI2")); // NON-NLS
start = end;
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.queryDb")); // NON-NLS
//query for current range
long count = filteredEvents.getEventCounts(interval).values().stream().mapToLong(Long::valueOf).sum();
bins.add(count);
max = Math.max(count, max);
final double fMax = Math.log(max);
final ArrayList<Long> fbins = new ArrayList<>(bins);
Platform.runLater(() -> {
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.updateUI2")); // NON-NLS
histogramBox.getChildren().clear();
histogramBox.getChildren().clear();
for (Long bin : fbins) { for (Long bin : fbins) {
if (isCancelled()) { if (isCancelled()) {
break; break;
@ -498,41 +492,41 @@ final public class VisualizationPanel extends BorderPane {
} }
return null; return null;
} }
}; };
new Thread(histogramTask).start(); new Thread(histogramTask).start();
controller.monitorTask(histogramTask); controller.monitorTask(histogramTask);
} }
private void refreshTimeUI() { private void refreshTimeUI() {
refreshTimeUI(filteredEvents.timeRangeProperty().get()); refreshTimeUI(filteredEvents.timeRangeProperty().get());
} }
private void refreshTimeUI(Interval interval) { private void refreshTimeUI(Interval interval) {
RangeDivisionInfo rangeDivisionInfo = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.getSpanningInterval()); RangeDivisionInfo rangeDivisionInfo = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.getSpanningInterval());
final long minTime = rangeDivisionInfo.getLowerBound(); final long minTime = rangeDivisionInfo.getLowerBound();
final long maxTime = rangeDivisionInfo.getUpperBound(); final long maxTime = rangeDivisionInfo.getUpperBound();
long startMillis = interval.getStartMillis(); long startMillis = interval.getStartMillis();
long endMillis = interval.getEndMillis(); long endMillis = interval.getEndMillis();
if (minTime > 0 && maxTime > minTime) { if (minTime > 0 && maxTime > minTime) {
Platform.runLater(() -> { Platform.runLater(() -> {
startPicker.localDateTimeProperty().removeListener(startListener); startPicker.localDateTimeProperty().removeListener(startListener);
endPicker.localDateTimeProperty().removeListener(endListener); endPicker.localDateTimeProperty().removeListener(endListener);
rangeSlider.highValueChangingProperty().removeListener(rangeSliderListener); rangeSlider.highValueChangingProperty().removeListener(rangeSliderListener);
rangeSlider.lowValueChangingProperty().removeListener(rangeSliderListener); rangeSlider.lowValueChangingProperty().removeListener(rangeSliderListener);
rangeSlider.setMax((maxTime - minTime)); rangeSlider.setMax((maxTime - minTime));
rangeSlider.setLowValue(startMillis - minTime); rangeSlider.setLowValue(startMillis - minTime);
rangeSlider.setHighValue(endMillis - minTime); rangeSlider.setHighValue(endMillis - minTime);
startPicker.setLocalDateTime(epochMillisToLocalDateTime(startMillis)); startPicker.setLocalDateTime(epochMillisToLocalDateTime(startMillis));
endPicker.setLocalDateTime(epochMillisToLocalDateTime(endMillis)); endPicker.setLocalDateTime(epochMillisToLocalDateTime(endMillis));
rangeSlider.highValueChangingProperty().addListener(rangeSliderListener); rangeSlider.highValueChangingProperty().addListener(rangeSliderListener);
rangeSlider.lowValueChangingProperty().addListener(rangeSliderListener); rangeSlider.lowValueChangingProperty().addListener(rangeSliderListener);
startPicker.localDateTimeProperty().addListener(startListener); startPicker.localDateTimeProperty().addListener(startListener);
@ -540,10 +534,10 @@ final public class VisualizationPanel extends BorderPane {
}); });
} }
} }
@NbBundle.Messages("NoEventsDialog.titledPane.text=No Visible Events") @NbBundle.Messages("NoEventsDialog.titledPane.text=No Visible Events")
private class NoEventsDialog extends StackPane { private class NoEventsDialog extends StackPane {
@FXML @FXML
private TitledPane titledPane; private TitledPane titledPane;
@FXML @FXML
@ -556,14 +550,14 @@ final public class VisualizationPanel extends BorderPane {
private Button zoomButton; private Button zoomButton;
@FXML @FXML
private Label noEventsDialogLabel; private Label noEventsDialogLabel;
private final Runnable closeCallback; private final Runnable closeCallback;
private NoEventsDialog(Runnable closeCallback) { private NoEventsDialog(Runnable closeCallback) {
this.closeCallback = closeCallback; this.closeCallback = closeCallback;
FXMLConstructor.construct(this, "NoEventsDialog.fxml"); // NON-NLS FXMLConstructor.construct(this, "NoEventsDialog.fxml"); // NON-NLS
} }
@FXML @FXML
void initialize() { void initialize() {
assert resetFiltersButton != null : "fx:id=\"resetFiltersButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; // NON-NLS assert resetFiltersButton != null : "fx:id=\"resetFiltersButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; // NON-NLS
@ -574,7 +568,7 @@ final public class VisualizationPanel extends BorderPane {
noEventsDialogLabel.setText(NbBundle.getMessage(NoEventsDialog.class, "VisualizationPanel.noEventsDialogLabel.text")); // NON-NLS noEventsDialogLabel.setText(NbBundle.getMessage(NoEventsDialog.class, "VisualizationPanel.noEventsDialogLabel.text")); // NON-NLS
dismissButton.setOnAction(actionEvent -> closeCallback.run()); dismissButton.setOnAction(actionEvent -> closeCallback.run());
ActionUtils.configureButton(new ZoomToEvents(controller), zoomButton); ActionUtils.configureButton(new ZoomToEvents(controller), zoomButton);
ActionUtils.configureButton(new Back(controller), backButton); ActionUtils.configureButton(new Back(controller), backButton);
ActionUtils.configureButton(new ResetFilters(controller), resetFiltersButton); ActionUtils.configureButton(new ResetFilters(controller), resetFiltersButton);
@ -586,15 +580,15 @@ final public class VisualizationPanel extends BorderPane {
* the selected LocalDateTime as start/end to the timelinecontroller. * the selected LocalDateTime as start/end to the timelinecontroller.
*/ */
private class PickerListener implements InvalidationListener { private class PickerListener implements InvalidationListener {
private final BiFunction< Interval, Long, Interval> intervalMapper; private final BiFunction< Interval, Long, Interval> intervalMapper;
private final Supplier<LocalDateTimeTextField> pickerSupplier; private final Supplier<LocalDateTimeTextField> pickerSupplier;
PickerListener(Supplier<LocalDateTimeTextField> pickerSupplier, BiFunction<Interval, Long, Interval> intervalMapper) { PickerListener(Supplier<LocalDateTimeTextField> pickerSupplier, BiFunction<Interval, Long, Interval> intervalMapper) {
this.pickerSupplier = pickerSupplier; this.pickerSupplier = pickerSupplier;
this.intervalMapper = intervalMapper; this.intervalMapper = intervalMapper;
} }
@Override @Override
public void invalidated(Observable observable) { public void invalidated(Observable observable) {
LocalDateTime pickerTime = pickerSupplier.get().getLocalDateTime(); LocalDateTime pickerTime = pickerSupplier.get().getLocalDateTime();
@ -609,7 +603,7 @@ final public class VisualizationPanel extends BorderPane {
* callback that disabled date/times outside the span of the current case. * callback that disabled date/times outside the span of the current case.
*/ */
private class LocalDateDisabler implements Callback<LocalDateTimePicker.LocalDateTimeRange, Void> { private class LocalDateDisabler implements Callback<LocalDateTimePicker.LocalDateTimeRange, Void> {
@Override @Override
public Void call(LocalDateTimePicker.LocalDateTimeRange viewedRange) { public Void call(LocalDateTimePicker.LocalDateTimeRange viewedRange) {
startPicker.disabledLocalDateTimes().clear(); startPicker.disabledLocalDateTimes().clear();
@ -619,7 +613,7 @@ final public class VisualizationPanel extends BorderPane {
Interval spanningInterval = filteredEvents.getSpanningInterval(); Interval spanningInterval = filteredEvents.getSpanningInterval();
long spanStartMillis = spanningInterval.getStartMillis(); long spanStartMillis = spanningInterval.getStartMillis();
long spaneEndMillis = spanningInterval.getEndMillis(); long spaneEndMillis = spanningInterval.getEndMillis();
LocalDate rangeStartLocalDate = viewedRange.getStartLocalDateTime().toLocalDate(); LocalDate rangeStartLocalDate = viewedRange.getStartLocalDateTime().toLocalDate();
LocalDate rangeEndLocalDate = viewedRange.getEndLocalDateTime().toLocalDate().plusDays(1); LocalDate rangeEndLocalDate = viewedRange.getEndLocalDateTime().toLocalDate().plusDays(1);
//iterate over days of the displayed range and disable ones not in spanning interval //iterate over days of the displayed range and disable ones not in spanning interval
@ -647,11 +641,11 @@ final public class VisualizationPanel extends BorderPane {
* picker to reset if invalid info was entered * picker to reset if invalid info was entered
*/ */
private final LocalDateTimeTextField picker; private final LocalDateTimeTextField picker;
LocalDateTimeValidator(LocalDateTimeTextField picker) { LocalDateTimeValidator(LocalDateTimeTextField picker) {
this.picker = picker; this.picker = picker;
} }
@Override @Override
public Boolean call(LocalDateTime param) { public Boolean call(LocalDateTime param) {
long epochMilli = localDateTimeToEpochMilli(param); long epochMilli = localDateTimeToEpochMilli(param);
@ -666,4 +660,15 @@ final public class VisualizationPanel extends BorderPane {
} }
} }
} }
private class Refresh extends Action {
@NbBundle.Messages({"VisualizationPanel.refresh=refresh"})
Refresh() {
super(Bundle.VisualizationPanel_refresh());
setGraphic(new ImageView(REFRESH));
setEventHandler(actionEvent -> filteredEvents.refresh());
}
}
} }