mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Merge pull request #2095 from millmanorama/Tl-ZoomSettings-cleanup
TL zoom settings cleanup
This commit is contained in:
commit
79a4d0b24f
@ -88,6 +88,7 @@ import org.sleuthkit.autopsy.timeline.filters.TypeFilter;
|
|||||||
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
|
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
|
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
|
||||||
|
import org.sleuthkit.autopsy.timeline.zooming.TimeUnits;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
@ -433,9 +434,9 @@ public class TimeLineController {
|
|||||||
/**
|
/**
|
||||||
* Show the entire range of the timeline.
|
* Show the entire range of the timeline.
|
||||||
*/
|
*/
|
||||||
public void showFullRange() {
|
public boolean showFullRange() {
|
||||||
synchronized (filteredEvents) {
|
synchronized (filteredEvents) {
|
||||||
pushTimeRange(filteredEvents.getSpanningInterval());
|
return pushTimeRange(filteredEvents.getSpanningInterval());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,6 +653,22 @@ public class TimeLineController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the view by setting a new time range that is the length of
|
||||||
|
* timeUnit and centered at the current center.
|
||||||
|
*
|
||||||
|
* @param timeUnit The unit of time to view
|
||||||
|
*
|
||||||
|
* @return true if the view actually changed.
|
||||||
|
*/
|
||||||
|
synchronized public boolean pushTimeUnit(TimeUnits timeUnit) {
|
||||||
|
if (timeUnit == TimeUnits.FOREVER) {
|
||||||
|
return showFullRange();
|
||||||
|
} else {
|
||||||
|
return pushTimeRange(IntervalUtils.getIntervalAroundMiddle(filteredEvents.getTimeRange(), timeUnit.getPeriod()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NbBundle.Messages({"# {0} - the number of events",
|
@NbBundle.Messages({"# {0} - the number of events",
|
||||||
"Timeline.pushDescrLOD.confdlg.msg=You are about to show details for {0} events. This might be very slow or even crash Autopsy.\n\nDo you want to continue?",
|
"Timeline.pushDescrLOD.confdlg.msg=You are about to show details for {0} events. This might be very slow or even crash Autopsy.\n\nDo you want to continue?",
|
||||||
"Timeline.pushDescrLOD.confdlg.title=Change description level of detail?"})
|
"Timeline.pushDescrLOD.confdlg.title=Change description level of detail?"})
|
||||||
|
@ -167,8 +167,9 @@ public final class FilteredEventsModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a read only view of the time range requested via
|
* Get a read only view of the time range currently in view.
|
||||||
* {@link #requestTimeRange(org.joda.time.Interval)}
|
*
|
||||||
|
* @return A read only view of the time range currently in view.
|
||||||
*/
|
*/
|
||||||
synchronized public ReadOnlyObjectProperty<Interval> timeRangeProperty() {
|
synchronized public ReadOnlyObjectProperty<Interval> timeRangeProperty() {
|
||||||
if (requestedTimeRange.get() == null) {
|
if (requestedTimeRange.get() == null) {
|
||||||
@ -189,6 +190,15 @@ public final class FilteredEventsModel {
|
|||||||
return requestedTypeZoom.getReadOnlyProperty();
|
return requestedTypeZoom.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time range currently in view.
|
||||||
|
*
|
||||||
|
* @return The time range currently in view.
|
||||||
|
*/
|
||||||
|
synchronized public Interval getTimeRange() {
|
||||||
|
return timeRangeProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
synchronized public DescriptionLoD getDescriptionLOD() {
|
synchronized public DescriptionLoD getDescriptionLOD() {
|
||||||
return requestedLOD.get();
|
return requestedLOD.get();
|
||||||
}
|
}
|
||||||
|
@ -67,4 +67,19 @@ public class IntervalUtils {
|
|||||||
final Interval newInterval = new Interval(middleOf.minus(halfRange), middleOf.plus(halfRange));
|
final Interval newInterval = new Interval(middleOf.minus(halfRange), middleOf.plus(halfRange));
|
||||||
return newInterval;
|
return newInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an interval the length of the given period, centered around the
|
||||||
|
* center of the given interval.
|
||||||
|
*
|
||||||
|
* @param interval The interval whose center will be the center of the new
|
||||||
|
* interval.
|
||||||
|
* @param period The length of the new interval
|
||||||
|
*
|
||||||
|
* @return An interval the length of the given period, centered around the
|
||||||
|
* center of the given interval.
|
||||||
|
*/
|
||||||
|
static public Interval getIntervalAroundMiddle(Interval interval, ReadablePeriod period) {
|
||||||
|
return getIntervalAround(middleOf(interval), period);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
DescriptionLOD.short=Short
|
DescriptionLOD.short=Short
|
||||||
DescriptionLOD.medium=Medium
|
DescriptionLOD.medium=Medium
|
||||||
DescriptionLOD.full=Full
|
DescriptionLOD.full=Full
|
||||||
EventTypeZoomLevel.rootType=Root Type
|
|
||||||
EventTypeZoomLevel.baseType=Base Type
|
|
||||||
EventTypeZoomLevel.subType=Sub Type
|
|
||||||
|
@ -23,7 +23,7 @@ import org.openide.util.NbBundle;
|
|||||||
/**
|
/**
|
||||||
* Enumeration of all description levels of detail.
|
* Enumeration of all description levels of detail.
|
||||||
*/
|
*/
|
||||||
public enum DescriptionLoD {
|
public enum DescriptionLoD implements DisplayNameProvider {
|
||||||
|
|
||||||
SHORT(NbBundle.getMessage(DescriptionLoD.class, "DescriptionLOD.short")),
|
SHORT(NbBundle.getMessage(DescriptionLoD.class, "DescriptionLOD.short")),
|
||||||
MEDIUM(NbBundle.getMessage(DescriptionLoD.class, "DescriptionLOD.medium")),
|
MEDIUM(NbBundle.getMessage(DescriptionLoD.class, "DescriptionLOD.medium")),
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 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.zooming;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for objects with a display name
|
||||||
|
*/
|
||||||
|
interface DisplayNameProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the display name of this object
|
||||||
|
*
|
||||||
|
* @return The display name.
|
||||||
|
*/
|
||||||
|
String getDisplayName();
|
||||||
|
}
|
@ -21,15 +21,20 @@ package org.sleuthkit.autopsy.timeline.zooming;
|
|||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Enum of event type zoom levels
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public enum EventTypeZoomLevel {
|
@NbBundle.Messages({
|
||||||
|
"EventTypeZoomLevel.rootType=Root Type",
|
||||||
|
"EventTypeZoomLevel.baseType=Base Type",
|
||||||
|
"EventTypeZoomLevel.subType=Sub Type"})
|
||||||
|
public enum EventTypeZoomLevel implements DisplayNameProvider {
|
||||||
|
|
||||||
ROOT_TYPE(NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.rootType")), BASE_TYPE(
|
ROOT_TYPE(Bundle.EventTypeZoomLevel_rootType()),
|
||||||
NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.baseType")), SUB_TYPE(
|
BASE_TYPE(Bundle.EventTypeZoomLevel_baseType()),
|
||||||
NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.subType"));
|
SUB_TYPE(Bundle.EventTypeZoomLevel_subType());
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import org.joda.time.Years;
|
|||||||
/**
|
/**
|
||||||
* predefined units of time for use in choosing axis labels and sub intervals.
|
* predefined units of time for use in choosing axis labels and sub intervals.
|
||||||
*/
|
*/
|
||||||
public enum TimeUnits {
|
public enum TimeUnits implements DisplayNameProvider {
|
||||||
|
|
||||||
FOREVER(null, ChronoUnit.FOREVER),
|
FOREVER(null, ChronoUnit.FOREVER),
|
||||||
YEARS(Years.ONE.toPeriod(), ChronoUnit.YEARS),
|
YEARS(Years.ONE.toPeriod(), ChronoUnit.YEARS),
|
||||||
@ -88,7 +88,8 @@ public enum TimeUnits {
|
|||||||
this.cu = cu;
|
this.cu = cu;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getDisplayName() {
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
return toString();
|
return toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013 Basis Technology Corp.
|
* Copyright 2013-16 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -18,7 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.timeline.zooming;
|
package org.sleuthkit.autopsy.timeline.zooming;
|
||||||
|
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.InvalidationListener;
|
import javafx.beans.InvalidationListener;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
@ -32,37 +33,33 @@ import org.sleuthkit.autopsy.timeline.FXMLConstructor;
|
|||||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||||
import org.sleuthkit.autopsy.timeline.VisualizationMode;
|
import org.sleuthkit.autopsy.timeline.VisualizationMode;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||||
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
|
|
||||||
import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
|
import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Panel that acts as a view for a given
|
* A Panel that acts as a view for a given
|
||||||
* TimeLineController/FilteredEventsModel. It has sliders to provide
|
* TimeLineController/FilteredEventsModel. It has sliders to provide
|
||||||
* context/control over three axes of zooming (timescale, event hierarchy, and
|
* context/control over three axes of zooming (timescale, event hierarchy level,
|
||||||
* description detail).
|
* and description level of detail).
|
||||||
*/
|
*/
|
||||||
public class ZoomSettingsPane extends TitledPane {
|
public class ZoomSettingsPane extends TitledPane {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label zoomLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label descrLODLabel;
|
||||||
@FXML
|
@FXML
|
||||||
private Slider descrLODSlider;
|
private Slider descrLODSlider;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label typeZoomLabel;
|
||||||
@FXML
|
@FXML
|
||||||
private Slider typeZoomSlider;
|
private Slider typeZoomSlider;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private Slider timeUnitSlider;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private Label descrLODLabel;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private Label typeZoomLabel;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label timeUnitLabel;
|
private Label timeUnitLabel;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label zoomLabel;
|
private Slider timeUnitSlider;
|
||||||
|
|
||||||
private final TimeLineController controller;
|
private final TimeLineController controller;
|
||||||
private final FilteredEventsModel filteredEvents;
|
private final FilteredEventsModel filteredEvents;
|
||||||
@ -84,126 +81,169 @@ public class ZoomSettingsPane extends TitledPane {
|
|||||||
"ZoomSettingsPane.timeUnitLabel.text=Time Units:",
|
"ZoomSettingsPane.timeUnitLabel.text=Time Units:",
|
||||||
"ZoomSettingsPane.zoomLabel.text=Zoom"})
|
"ZoomSettingsPane.zoomLabel.text=Zoom"})
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
timeUnitSlider.setMax(TimeUnits.values().length - 2);
|
|
||||||
timeUnitSlider.setLabelFormatter(new TimeUnitConverter());
|
|
||||||
|
|
||||||
typeZoomSlider.setMin(1);
|
|
||||||
typeZoomSlider.setMax(2);
|
|
||||||
typeZoomSlider.setLabelFormatter(new TypeZoomConverter());
|
|
||||||
descrLODSlider.setMax(DescriptionLoD.values().length - 1);
|
|
||||||
descrLODSlider.setLabelFormatter(new DescrLODConverter());
|
|
||||||
descrLODLabel.setText(Bundle.ZoomSettingsPane_descrLODLabel_text());
|
|
||||||
typeZoomLabel.setText(Bundle.ZoomSettingsPane_typeZoomLabel_text());
|
|
||||||
timeUnitLabel.setText(Bundle.ZoomSettingsPane_timeUnitLabel_text());
|
|
||||||
zoomLabel.setText(Bundle.ZoomSettingsPane_zoomLabel_text());
|
zoomLabel.setText(Bundle.ZoomSettingsPane_zoomLabel_text());
|
||||||
|
|
||||||
initializeSlider(timeUnitSlider,
|
typeZoomSlider.setMin(1); //don't show ROOT_TYPE
|
||||||
() -> {
|
typeZoomSlider.setMax(EventTypeZoomLevel.values().length - 1);
|
||||||
TimeUnits requestedUnit = TimeUnits.values()[new Double(timeUnitSlider.getValue()).intValue()];
|
configureSliderListeners(typeZoomSlider,
|
||||||
if (requestedUnit == TimeUnits.FOREVER) {
|
controller::pushEventTypeZoom,
|
||||||
controller.showFullRange();
|
filteredEvents.eventTypeZoomProperty(),
|
||||||
} else {
|
EventTypeZoomLevel.class,
|
||||||
controller.pushTimeRange(IntervalUtils.getIntervalAround(IntervalUtils.middleOf(ZoomSettingsPane.this.filteredEvents.timeRangeProperty().get()), requestedUnit.getPeriod()));
|
EventTypeZoomLevel::ordinal,
|
||||||
}
|
Function.identity());
|
||||||
},
|
typeZoomLabel.setText(Bundle.ZoomSettingsPane_typeZoomLabel_text());
|
||||||
this.filteredEvents.timeRangeProperty(),
|
|
||||||
() -> {
|
|
||||||
RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(this.filteredEvents.timeRangeProperty().get());
|
|
||||||
ChronoUnit chronoUnit = rangeInfo.getPeriodSize().getChronoUnit();
|
|
||||||
timeUnitSlider.setValue(TimeUnits.fromChronoUnit(chronoUnit).ordinal() - 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
initializeSlider(descrLODSlider,
|
|
||||||
() -> controller.pushDescrLOD(DescriptionLoD.values()[Math.round(descrLODSlider.valueProperty().floatValue())]),
|
|
||||||
this.filteredEvents.descriptionLODProperty(), () -> {
|
|
||||||
descrLODSlider.setValue(this.filteredEvents.descriptionLODProperty().get().ordinal());
|
|
||||||
});
|
|
||||||
|
|
||||||
initializeSlider(typeZoomSlider,
|
|
||||||
() -> controller.pushEventTypeZoom(EventTypeZoomLevel.values()[Math.round(typeZoomSlider.valueProperty().floatValue())]),
|
|
||||||
this.filteredEvents.eventTypeZoomProperty(),
|
|
||||||
() -> typeZoomSlider.setValue(this.filteredEvents.eventTypeZoomProperty().get().ordinal()));
|
|
||||||
|
|
||||||
|
descrLODSlider.setMax(DescriptionLoD.values().length - 1);
|
||||||
|
configureSliderListeners(descrLODSlider,
|
||||||
|
controller::pushDescrLOD,
|
||||||
|
filteredEvents.descriptionLODProperty(),
|
||||||
|
DescriptionLoD.class,
|
||||||
|
DescriptionLoD::ordinal,
|
||||||
|
Function.identity());
|
||||||
|
descrLODLabel.setText(Bundle.ZoomSettingsPane_descrLODLabel_text());
|
||||||
|
//the description slider is only usefull in the detail view
|
||||||
descrLODSlider.disableProperty().bind(controller.viewModeProperty().isEqualTo(VisualizationMode.COUNTS));
|
descrLODSlider.disableProperty().bind(controller.viewModeProperty().isEqualTo(VisualizationMode.COUNTS));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In order for the selected value in the time unit slider to correspond
|
||||||
|
* to the amount of time used as units along the x-axis of the
|
||||||
|
* visualization, and since we don't want to show "forever" as a time
|
||||||
|
* unit, the range of the slider is restricted, and there is an offset
|
||||||
|
* of 1 between the "real" value, and what is shown in the slider
|
||||||
|
* labels.
|
||||||
|
*/
|
||||||
|
timeUnitSlider.setMax(TimeUnits.values().length - 2);
|
||||||
|
configureSliderListeners(timeUnitSlider,
|
||||||
|
controller::pushTimeUnit,
|
||||||
|
filteredEvents.timeRangeProperty(),
|
||||||
|
TimeUnits.class,
|
||||||
|
//for the purposes of this slider we want the TimeUnit one bigger than RangeDivisionInfo indicates
|
||||||
|
modelTimeRange -> RangeDivisionInfo.getRangeDivisionInfo(modelTimeRange).getPeriodSize().ordinal() - 1,
|
||||||
|
index -> index + 1); //compensate for the -1 above when mapping to the Enum whose displayName will be shown at index
|
||||||
|
timeUnitLabel.setText(Bundle.ZoomSettingsPane_timeUnitLabel_text());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setup a slider that with a listener that is added and removed to avoid
|
* Configure the listeners that keep the given slider in sync with model
|
||||||
* circular updates.
|
* property changes, and that handle user input on the slider. The listener
|
||||||
|
* attached to the slider is added and removed to avoid circular updates.
|
||||||
*
|
*
|
||||||
* @param <T> the type of the driving property
|
* Because Sliders work in terms of Doubles but represent ordered Enums that
|
||||||
* @param slider the slider that will have its change handlers
|
* are indexed by Integers, and because the model properties may not be of
|
||||||
* setup
|
* the same type as the Enum(timeUnitSlider relates to an Interval in the
|
||||||
* @param sliderChangeHandler the runnable that will be executed whenever
|
* filteredEvents model, rather than the TimeUnits shown on the Slider), a
|
||||||
* the slider value has changed and is not
|
* mapper is needed to convert between DriverType and Integer
|
||||||
* currently changing
|
* indices(driverValueMapper). Another mapper is used to modifiy the mapping
|
||||||
* @param driver the property that drives updates to this
|
* from Integer index to Enum value displayed as the slider tick
|
||||||
* slider
|
* label(labelIndexMapper).
|
||||||
* @param driverChangHandler the code to update the slider bases on the
|
*
|
||||||
* value of the driving property. This will be
|
* @param <DriverType> The type of the driving model property.
|
||||||
* wrapped in a remove/add-listener pair to
|
* @param <EnumType> The type of the Enum that is represented along
|
||||||
* prevent circular updates.
|
* the slider.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param slider The slider that we are configuring.
|
||||||
|
*
|
||||||
|
* @param sliderValueConsumer The consumer that will get passed the newly
|
||||||
|
* selected slider value (mapped to EnumType
|
||||||
|
* automatically).
|
||||||
|
*
|
||||||
|
* @param modelProperty The readonly model property that this slider
|
||||||
|
* should be synced to.
|
||||||
|
*
|
||||||
|
* @param enumClass A type token for EnumType, ie value of type
|
||||||
|
* Class<EnumType>
|
||||||
|
*
|
||||||
|
* @param driverValueMapper A Function that maps from driver values of
|
||||||
|
* type DriverType to Integers representing the
|
||||||
|
* index of the corresponding EnumType.
|
||||||
|
*
|
||||||
|
* @param labelIndexMapper A Function that maps from Integer (narrowed
|
||||||
|
* slider value) to Integers representing the
|
||||||
|
* index of the corresponding EnumType. Used to
|
||||||
|
* compensate for slider values that do not
|
||||||
|
* lineup exactly with the Enum value indices to
|
||||||
|
* use as tick Labels.
|
||||||
*/
|
*/
|
||||||
private <T> void initializeSlider(Slider slider, Runnable sliderChangeHandler, ReadOnlyObjectProperty<T> driver, Runnable driverChangHandler) {
|
private static <DriverType, EnumType extends Enum<EnumType> & DisplayNameProvider> void configureSliderListeners(
|
||||||
|
Slider slider,
|
||||||
|
Consumer<EnumType> sliderValueConsumer,
|
||||||
|
ReadOnlyObjectProperty<DriverType> modelProperty,
|
||||||
|
Class<EnumType> enumClass,
|
||||||
|
Function<DriverType, Integer> driverValueMapper,
|
||||||
|
Function<Integer, Integer> labelIndexMapper) {
|
||||||
|
|
||||||
|
//set the tick labels to the enum displayNames
|
||||||
|
slider.setLabelFormatter(new EnumSliderLabelFormatter<>(enumClass, labelIndexMapper));
|
||||||
|
|
||||||
|
//make a listener to responds to slider value changes (by updating the visualization)
|
||||||
final InvalidationListener sliderListener = observable -> {
|
final InvalidationListener sliderListener = observable -> {
|
||||||
|
//only process event if the slider value is not changing (user has released slider thumb)
|
||||||
if (slider.isValueChanging() == false) {
|
if (slider.isValueChanging() == false) {
|
||||||
sliderChangeHandler.run();
|
//convert slider value to EnumType and pass to consumer
|
||||||
|
EnumType sliderValueAsEnum = enumClass.getEnumConstants()[Math.round((float) slider.getValue())];
|
||||||
|
sliderValueConsumer.accept(sliderValueAsEnum);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
//attach listener
|
||||||
slider.valueProperty().addListener(sliderListener);
|
slider.valueProperty().addListener(sliderListener);
|
||||||
slider.valueChangingProperty().addListener(sliderListener);
|
slider.valueChangingProperty().addListener(sliderListener);
|
||||||
|
|
||||||
Platform.runLater(driverChangHandler);
|
//set intial value of slider
|
||||||
|
slider.setValue(driverValueMapper.apply(modelProperty.get()));
|
||||||
|
|
||||||
driver.addListener(observable -> {
|
modelProperty.addListener(modelProp -> {
|
||||||
|
//handle changes in the model property
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
//remove listener to avoid circular updates
|
||||||
slider.valueProperty().removeListener(sliderListener);
|
slider.valueProperty().removeListener(sliderListener);
|
||||||
slider.valueChangingProperty().removeListener(sliderListener);
|
slider.valueChangingProperty().removeListener(sliderListener);
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
//sync value of slider to model property value
|
||||||
driverChangHandler.run();
|
slider.setValue(driverValueMapper.apply(modelProperty.get()));
|
||||||
|
|
||||||
|
//reattach listener
|
||||||
slider.valueProperty().addListener(sliderListener);
|
slider.valueProperty().addListener(sliderListener);
|
||||||
slider.valueChangingProperty().addListener(sliderListener);
|
slider.valueChangingProperty().addListener(sliderListener);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Can these be abstracted to a sort of Enum converter for use in a potential enumslider
|
/**
|
||||||
private static class TimeUnitConverter extends StringConverter<Double> {
|
* StringConverter for the tick Labels of a Slider that is "backed" by an
|
||||||
|
* Enum that extends DisplayNameProvider. Narrows the Slider's Double value
|
||||||
|
* to an Integer and then uses that as the index of the Enum value whose
|
||||||
|
* displayName will be shown as the tick Label
|
||||||
|
*
|
||||||
|
* @param <EnumType> The type of Enum that this converter works with.
|
||||||
|
*/
|
||||||
|
static private class EnumSliderLabelFormatter<EnumType extends Enum<EnumType> & DisplayNameProvider> extends StringConverter<Double> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Type token for the class of Enum that this converter works with.
|
||||||
|
*/
|
||||||
|
private final Class<EnumType> clazz;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* A Function that can be used to adjust the narrowed slider value if it
|
||||||
|
* doesn't correspond exactly to the Enum value index.
|
||||||
|
*/
|
||||||
|
private final Function<Integer, Integer> indexAdjsuter;
|
||||||
|
|
||||||
|
EnumSliderLabelFormatter(Class<EnumType> clazz, Function<Integer, Integer> indexMapper) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
this.indexAdjsuter = indexMapper;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(Double object) {
|
public String toString(Double dbl) {
|
||||||
return TimeUnits.values()[Math.min(TimeUnits.values().length - 1, object.intValue() + 1)].getDisplayName();
|
//get the displayName of the EnumType whose index is the given dbl after it has been narrowed and then adjusted
|
||||||
|
return clazz.getEnumConstants()[indexAdjsuter.apply(dbl.intValue())].getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Double fromString(String string) {
|
public Double fromString(String string) {
|
||||||
return new Integer(TimeUnits.valueOf(string).ordinal()).doubleValue();
|
throw new UnsupportedOperationException("This method should not be used. This EnumSliderLabelFormatter is being used in an unintended way.");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TypeZoomConverter extends StringConverter<Double> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(Double object) {
|
|
||||||
return EventTypeZoomLevel.values()[object.intValue()].getDisplayName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Double fromString(String string) {
|
|
||||||
return new Integer(EventTypeZoomLevel.valueOf(string).ordinal()).doubleValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DescrLODConverter extends StringConverter<Double> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(Double object) {
|
|
||||||
return DescriptionLoD.values()[object.intValue()].getDisplayName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Double fromString(String string) {
|
|
||||||
return new Integer(DescriptionLoD.valueOf(string).ordinal()).doubleValue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user