From 40ac47647937113ae1b9b9e0811ec2b6ce2d8496 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 22 Apr 2016 11:15:43 -0400 Subject: [PATCH 1/5] move History back/forward out of ZoomSettingsPane. cleanup and comments --- .../autopsy/timeline/actions/Back.java.orig | 57 +++++ .../timeline/actions/Forward.java.orig | 57 +++++ .../zooming/ZoomSettingsPane.java.orig | 217 ++++++++++++++++++ 3 files changed, 331 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/actions/Back.java.orig create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/actions/Forward.java.orig create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java.orig diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/Back.java.orig b/Core/src/org/sleuthkit/autopsy/timeline/actions/Back.java.orig new file mode 100644 index 0000000000..85b3ee3dd0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/Back.java.orig @@ -0,0 +1,57 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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.actions; + +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import org.controlsfx.control.action.Action; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.timeline.TimeLineController; + +/** + * An action that navigates back through the history stack. + */ +//TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm +public class Back extends Action { + + private static final Image BACK_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow-180.png", 16, 16, true, true, true); // NON-NLS + + private final TimeLineController controller; + + @NbBundle.Messages({"Back.text=Back", + "# {0} - action accelerator keys ", + "Back.longText=Back: {0}\nGo back to the last view settings."}) + public Back(TimeLineController controller) { + super(Bundle.Back_text()); + this.controller = controller; + + setGraphic(new ImageView(BACK_IMAGE)); + setAccelerator(new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN)); + setLongText(Bundle.Back_longText(getAccelerator().getDisplayText())); + setEventHandler(actionEvent -> controller.retreat()); + +<<<<<<< HEAD + disabledProperty().bind(controller.canRetreatProperty().not()); +======= + disabledProperty().bind(controller.getCanRetreat().not()); +>>>>>>> move History back/forward out of ZoomSettingsPane. cleanup and comments + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/Forward.java.orig b/Core/src/org/sleuthkit/autopsy/timeline/actions/Forward.java.orig new file mode 100644 index 0000000000..263922f524 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/Forward.java.orig @@ -0,0 +1,57 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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.actions; + +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import org.controlsfx.control.action.Action; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.timeline.TimeLineController; + +/** + * An action that navigates forward through the history. + */ +//TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm +public class Forward extends Action { + + private static final Image FORWARD_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow.png", 16, 16, true, true, true); // NON-NLS + + private final TimeLineController controller; + + @NbBundle.Messages({"Forward.text=Forward", + "# {0} - action accelerator keys ", + "Forward.longText=Forward: {0}\nGo forward to the next view settings."}) + public Forward(TimeLineController controller) { + super(Bundle.Forward_text()); + this.controller = controller; + + setGraphic(new ImageView(FORWARD_IMAGE)); + setAccelerator(new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN)); + setLongText(Bundle.Forward_longText(getAccelerator().getDisplayText())); + setEventHandler(actionEvent -> controller.advance()); + +<<<<<<< HEAD + disabledProperty().bind(controller.canAdvanceProperty().not()); +======= + disabledProperty().bind(controller.getCanAdvance().not()); +>>>>>>> move History back/forward out of ZoomSettingsPane. cleanup and comments + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java.orig b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java.orig new file mode 100644 index 0000000000..691e6bf9fe --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java.orig @@ -0,0 +1,217 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit 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; + +import java.time.temporal.ChronoUnit; +import javafx.application.Platform; +import javafx.beans.InvalidationListener; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.control.Slider; +import javafx.scene.control.TitledPane; +import javafx.util.StringConverter; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.timeline.FXMLConstructor; +import org.sleuthkit.autopsy.timeline.TimeLineController; +import org.sleuthkit.autopsy.timeline.VisualizationMode; +import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.utils.IntervalUtils; +import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; + +/** + * A Panel that acts as a view for a given +<<<<<<< HEAD + * TimeLineController/FilteredEventsModel. It has sliders to provide +======= + * TimeLineController/FilteredEvetnsModel. It has sliders to provide +>>>>>>> move History back/forward out of ZoomSettingsPane. cleanup and comments + * context/control over three axes of zooming (timescale, event hierarchy, and + * description detail). + */ +public class ZoomSettingsPane extends TitledPane { + + @FXML + private Slider descrLODSlider; + + @FXML + private Slider typeZoomSlider; + + @FXML + private Slider timeUnitSlider; + + @FXML + private Label descrLODLabel; + + @FXML + private Label typeZoomLabel; + + @FXML + private Label timeUnitLabel; + + @FXML + private Label zoomLabel; + + private final TimeLineController controller; +<<<<<<< HEAD +======= + +>>>>>>> move History back/forward out of ZoomSettingsPane. cleanup and comments + private final FilteredEventsModel filteredEvents; + + /** + * Constructor + * + * @param controller TimeLineController this panel functions as a view for. + */ + public ZoomSettingsPane(TimeLineController controller) { + this.controller = controller; + this.filteredEvents = controller.getEventsModel(); + FXMLConstructor.construct(this, "ZoomSettingsPane.fxml"); // NON-NLS + } + + @NbBundle.Messages({ + "ZoomSettingsPane.descrLODLabel.text=Description Detail:", + "ZoomSettingsPane.typeZoomLabel.text=Event Type:", + "ZoomSettingsPane.timeUnitLabel.text=Time Units:", + "ZoomSettingsPane.zoomLabel.text=Zoom"}) + 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()); + + initializeSlider(timeUnitSlider, + () -> { + TimeUnits requestedUnit = TimeUnits.values()[new Double(timeUnitSlider.getValue()).intValue()]; + if (requestedUnit == TimeUnits.FOREVER) { + controller.showFullRange(); + } else { + controller.pushTimeRange(IntervalUtils.getIntervalAround(IntervalUtils.middleOf(ZoomSettingsPane.this.filteredEvents.timeRangeProperty().get()), requestedUnit.getPeriod())); + } + }, + 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.disableProperty().bind(controller.viewModeProperty().isEqualTo(VisualizationMode.COUNTS)); + } + + /** + * setup a slider that with a listener that is added and removed to avoid + * circular updates. + * + * @param the type of the driving property + * @param slider the slider that will have its change handlers + * setup + * @param sliderChangeHandler the runnable that will be executed whenever + * the slider value has changed and is not + * currently changing + * @param driver the property that drives updates to this + * slider + * @param driverChangHandler the code to update the slider bases on the + * value of the driving property. This will be + * wrapped in a remove/add-listener pair to + * prevent circular updates. + */ + private void initializeSlider(Slider slider, Runnable sliderChangeHandler, ReadOnlyObjectProperty driver, Runnable driverChangHandler) { + final InvalidationListener sliderListener = observable -> { + if (slider.isValueChanging() == false) { + sliderChangeHandler.run(); + } + }; + slider.valueProperty().addListener(sliderListener); + slider.valueChangingProperty().addListener(sliderListener); + + Platform.runLater(driverChangHandler); + + driver.addListener(observable -> { + slider.valueProperty().removeListener(sliderListener); + slider.valueChangingProperty().removeListener(sliderListener); + + Platform.runLater(() -> { + driverChangHandler.run(); + slider.valueProperty().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 { + + @Override + public String toString(Double object) { + return TimeUnits.values()[Math.min(TimeUnits.values().length - 1, object.intValue() + 1)].getDisplayName(); + } + + @Override + public Double fromString(String string) { + return new Integer(TimeUnits.valueOf(string).ordinal()).doubleValue(); + } + } + + private static class TypeZoomConverter extends StringConverter { + + @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 { + + @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(); + } + } +} From 82f27c61dcb52c2373bcdbb0229a6e33ce5c5ce4 Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 25 Apr 2016 09:52:41 -0400 Subject: [PATCH 2/5] cleanup in ZoomSettingsPane --- .../autopsy/timeline/TimeLineController.java | 13 +- .../autopsy/timeline/actions/Back.java.orig | 57 ----- .../timeline/actions/Forward.java.orig | 57 ----- .../datamodel/FilteredEventsModel.java | 4 + .../autopsy/timeline/utils/IntervalUtils.java | 4 + .../timeline/zooming/DescriptionLoD.java | 2 +- .../timeline/zooming/DisplayNameProvider.java | 14 ++ .../timeline/zooming/EventTypeZoomLevel.java | 2 +- .../autopsy/timeline/zooming/TimeUnits.java | 5 +- .../timeline/zooming/ZoomSettingsPane.java | 165 ++++++------- .../zooming/ZoomSettingsPane.java.orig | 217 ------------------ 11 files changed, 114 insertions(+), 426 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/actions/Back.java.orig delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/actions/Forward.java.orig create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/zooming/DisplayNameProvider.java delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java.orig diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index e2b8771ad2..93fb1dbe71 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -88,6 +88,7 @@ import org.sleuthkit.autopsy.timeline.filters.TypeFilter; import org.sleuthkit.autopsy.timeline.utils.IntervalUtils; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel; +import org.sleuthkit.autopsy.timeline.zooming.TimeUnits; import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; import org.sleuthkit.datamodel.Content; @@ -433,9 +434,8 @@ public class TimeLineController { /** * Show the entire range of the timeline. */ - public void showFullRange() { synchronized (filteredEvents) { - pushTimeRange(filteredEvents.getSpanningInterval()); + return pushTimeRange(filteredEvents.getSpanningInterval()); } } @@ -652,6 +652,15 @@ public class TimeLineController { } } + @SuppressWarnings("AssignmentToMethodParameter") //clamp timerange to case + 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", "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?"}) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/Back.java.orig b/Core/src/org/sleuthkit/autopsy/timeline/actions/Back.java.orig deleted file mode 100644 index 85b3ee3dd0..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/Back.java.orig +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 Basis Technology Corp. - * Contact: carrier sleuthkit 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.actions; - -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyCodeCombination; -import org.controlsfx.control.action.Action; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.timeline.TimeLineController; - -/** - * An action that navigates back through the history stack. - */ -//TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm -public class Back extends Action { - - private static final Image BACK_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow-180.png", 16, 16, true, true, true); // NON-NLS - - private final TimeLineController controller; - - @NbBundle.Messages({"Back.text=Back", - "# {0} - action accelerator keys ", - "Back.longText=Back: {0}\nGo back to the last view settings."}) - public Back(TimeLineController controller) { - super(Bundle.Back_text()); - this.controller = controller; - - setGraphic(new ImageView(BACK_IMAGE)); - setAccelerator(new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN)); - setLongText(Bundle.Back_longText(getAccelerator().getDisplayText())); - setEventHandler(actionEvent -> controller.retreat()); - -<<<<<<< HEAD - disabledProperty().bind(controller.canRetreatProperty().not()); -======= - disabledProperty().bind(controller.getCanRetreat().not()); ->>>>>>> move History back/forward out of ZoomSettingsPane. cleanup and comments - } -} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/Forward.java.orig b/Core/src/org/sleuthkit/autopsy/timeline/actions/Forward.java.orig deleted file mode 100644 index 263922f524..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/Forward.java.orig +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 Basis Technology Corp. - * Contact: carrier sleuthkit 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.actions; - -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyCodeCombination; -import org.controlsfx.control.action.Action; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.timeline.TimeLineController; - -/** - * An action that navigates forward through the history. - */ -//TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm -public class Forward extends Action { - - private static final Image FORWARD_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow.png", 16, 16, true, true, true); // NON-NLS - - private final TimeLineController controller; - - @NbBundle.Messages({"Forward.text=Forward", - "# {0} - action accelerator keys ", - "Forward.longText=Forward: {0}\nGo forward to the next view settings."}) - public Forward(TimeLineController controller) { - super(Bundle.Forward_text()); - this.controller = controller; - - setGraphic(new ImageView(FORWARD_IMAGE)); - setAccelerator(new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN)); - setLongText(Bundle.Forward_longText(getAccelerator().getDisplayText())); - setEventHandler(actionEvent -> controller.advance()); - -<<<<<<< HEAD - disabledProperty().bind(controller.canAdvanceProperty().not()); -======= - disabledProperty().bind(controller.getCanAdvance().not()); ->>>>>>> move History back/forward out of ZoomSettingsPane. cleanup and comments - } -} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java index d53316ef4d..75de500a71 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java @@ -189,6 +189,10 @@ public final class FilteredEventsModel { return requestedTypeZoom.getReadOnlyProperty(); } + synchronized public Interval getTimeRange() { + return timeRangeProperty().get(); + } + synchronized public DescriptionLoD getDescriptionLOD() { return requestedLOD.get(); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java index c53e57e7a9..ed7fc72e67 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java @@ -67,4 +67,8 @@ public class IntervalUtils { final Interval newInterval = new Interval(middleOf.minus(halfRange), middleOf.plus(halfRange)); return newInterval; } + + static public Interval getIntervalAroundMiddle(Interval interval, ReadablePeriod period) { + return getIntervalAround(middleOf(interval), period); + } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/DescriptionLoD.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/DescriptionLoD.java index 50d612b210..b9fb9116ca 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/DescriptionLoD.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/DescriptionLoD.java @@ -23,7 +23,7 @@ import org.openide.util.NbBundle; /** * Enumeration of all description levels of detail. */ -public enum DescriptionLoD { +public enum DescriptionLoD implements DisplayNameProvider { SHORT(NbBundle.getMessage(DescriptionLoD.class, "DescriptionLOD.short")), MEDIUM(NbBundle.getMessage(DescriptionLoD.class, "DescriptionLOD.medium")), diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/DisplayNameProvider.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/DisplayNameProvider.java new file mode 100644 index 0000000000..145e2363a8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/DisplayNameProvider.java @@ -0,0 +1,14 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.timeline.zooming; + +/** + * + */ +public interface DisplayNameProvider { + + String getDisplayName(); +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java index bbe14e2031..71cd86c781 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java @@ -24,7 +24,7 @@ import org.openide.util.NbBundle; * * */ -public enum EventTypeZoomLevel { +public enum EventTypeZoomLevel implements DisplayNameProvider{ ROOT_TYPE(NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.rootType")), BASE_TYPE( NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.baseType")), SUB_TYPE( diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/TimeUnits.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/TimeUnits.java index 8ac7ba69f4..a40a32837c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/TimeUnits.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/TimeUnits.java @@ -30,7 +30,7 @@ import org.joda.time.Years; /** * 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), YEARS(Years.ONE.toPeriod(), ChronoUnit.YEARS), @@ -88,7 +88,8 @@ public enum TimeUnits { this.cu = cu; } - String getDisplayName() { + @Override + public String getDisplayName() { return toString(); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java index a8f5b77e4c..0f28f60f3c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-16 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,8 @@ */ 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.beans.InvalidationListener; import javafx.beans.property.ReadOnlyObjectProperty; @@ -32,7 +33,6 @@ import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.VisualizationMode; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; -import org.sleuthkit.autopsy.timeline.utils.IntervalUtils; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; /** @@ -84,126 +84,113 @@ public class ZoomSettingsPane extends TitledPane { "ZoomSettingsPane.timeUnitLabel.text=Time Units:", "ZoomSettingsPane.zoomLabel.text=Zoom"}) 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()); - initializeSlider(timeUnitSlider, - () -> { - TimeUnits requestedUnit = TimeUnits.values()[new Double(timeUnitSlider.getValue()).intValue()]; - if (requestedUnit == TimeUnits.FOREVER) { - controller.showFullRange(); - } else { - controller.pushTimeRange(IntervalUtils.getIntervalAround(IntervalUtils.middleOf(ZoomSettingsPane.this.filteredEvents.timeRangeProperty().get()), requestedUnit.getPeriod())); - } - }, - this.filteredEvents.timeRangeProperty(), - () -> { - RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(this.filteredEvents.timeRangeProperty().get()); - ChronoUnit chronoUnit = rangeInfo.getPeriodSize().getChronoUnit(); - timeUnitSlider.setValue(TimeUnits.fromChronoUnit(chronoUnit).ordinal() - 1); - }); + timeUnitSlider.setMax(TimeUnits.values().length - 1); + timeUnitLabel.setText(Bundle.ZoomSettingsPane_timeUnitLabel_text()); + configureSliderListeners(timeUnitSlider, + controller::pushTimeUnit, + filteredEvents.timeRangeProperty(), + modelTimeRange -> RangeDivisionInfo.getRangeDivisionInfo(modelTimeRange).getPeriodSize().ordinal() - 1, + TimeUnits.class, + dbl -> Math.min(TimeUnits.values().length - 1, dbl.intValue() + 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())); + typeZoomSlider.setMin(0); + typeZoomSlider.setMin(1); + typeZoomSlider.setMax(EventTypeZoomLevel.values().length - 1); + typeZoomLabel.setText(Bundle.ZoomSettingsPane_typeZoomLabel_text()); + configureSliderListeners(typeZoomSlider, + controller::pushEventTypeZoom, + filteredEvents.eventTypeZoomProperty(), + EventTypeZoomLevel::ordinal, + EventTypeZoomLevel.class, + Double::intValue); + descrLODSlider.setMax(DescriptionLoD.values().length - 1); + descrLODLabel.setText(Bundle.ZoomSettingsPane_descrLODLabel_text()); + configureSliderListeners(descrLODSlider, + controller::pushDescrLOD, + filteredEvents.descriptionLODProperty(), + DescriptionLoD::ordinal, + DescriptionLoD.class, + Double::intValue); descrLODSlider.disableProperty().bind(controller.viewModeProperty().isEqualTo(VisualizationMode.COUNTS)); } /** - * setup a slider that with a listener that is added and removed to avoid - * circular updates. + * Configure the listeners that keep the sliders in sync with model changes, + * and react to user input on the sliders. The listener attached to the + * slider is added and removed to avoid circular updates. * - * @param the type of the driving property - * @param slider the slider that will have its change handlers - * setup - * @param sliderChangeHandler the runnable that will be executed whenever - * the slider value has changed and is not - * currently changing - * @param driver the property that drives updates to this - * slider - * @param driverChangHandler the code to update the slider bases on the - * value of the driving property. This will be - * wrapped in a remove/add-listener pair to - * prevent circular updates. + * @param The type of the driving model property + * @param The type of the enum that is represented along + * 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 driverValueMapper A Function that maps from driver values of + * type T to Integers representing the ordinal + * index of the corresponding EnumType + * @param enumClass A type token for EnumType, ie Class + * @param converterMapper A Function that maps from Double (slider + * value) to Integers representing the ordinal + * index of the corresponding EnumType */ - private void initializeSlider(Slider slider, Runnable sliderChangeHandler, ReadOnlyObjectProperty driver, Runnable driverChangHandler) { + private & DisplayNameProvider> + void configureSliderListeners(Slider slider, + Consumer sliderValueConsumer, + ReadOnlyObjectProperty modelProperty, + Function driverValueMapper, + final Class enumClass, + final Function converterMapper) { + + slider.setLabelFormatter(new EnumSliderConverter<>(enumClass, converterMapper)); + final InvalidationListener sliderListener = observable -> { if (slider.isValueChanging() == false) { - sliderChangeHandler.run(); + sliderValueConsumer.accept(enumClass.getEnumConstants()[Math.round(slider.valueProperty().floatValue())]); } }; slider.valueProperty().addListener(sliderListener); slider.valueChangingProperty().addListener(sliderListener); - Platform.runLater(driverChangHandler); - - driver.addListener(observable -> { - slider.valueProperty().removeListener(sliderListener); - slider.valueChangingProperty().removeListener(sliderListener); + Platform.runLater(() -> slider.setValue(driverValueMapper.apply(modelProperty.get()))); + modelProperty.addListener(observable -> { Platform.runLater(() -> { - driverChangHandler.run(); + slider.valueProperty().removeListener(sliderListener); + slider.valueChangingProperty().removeListener(sliderListener); + + slider.setValue(driverValueMapper.apply(modelProperty.get())); + slider.valueProperty().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 { + static private class EnumSliderConverter & DisplayNameProvider> extends StringConverter { - @Override - public String toString(Double object) { - return TimeUnits.values()[Math.min(TimeUnits.values().length - 1, object.intValue() + 1)].getDisplayName(); + private final Class clazz; + private final Function indexAdjsuter; + + EnumSliderConverter(Class clazz, Function indexMapper) { + this.clazz = clazz; + this.indexAdjsuter = indexMapper; } @Override public Double fromString(String string) { - return new Integer(TimeUnits.valueOf(string).ordinal()).doubleValue(); + return new Double(EnumType.valueOf(clazz, string).ordinal()); } - } - - private static class TypeZoomConverter extends StringConverter { @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 { - - @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(); + return clazz.getEnumConstants()[indexAdjsuter.apply(object)].getDisplayName(); } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java.orig b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java.orig deleted file mode 100644 index 691e6bf9fe..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java.orig +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2013 Basis Technology Corp. - * Contact: carrier sleuthkit 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; - -import java.time.temporal.ChronoUnit; -import javafx.application.Platform; -import javafx.beans.InvalidationListener; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.control.Slider; -import javafx.scene.control.TitledPane; -import javafx.util.StringConverter; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.timeline.FXMLConstructor; -import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.VisualizationMode; -import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; -import org.sleuthkit.autopsy.timeline.utils.IntervalUtils; -import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; - -/** - * A Panel that acts as a view for a given -<<<<<<< HEAD - * TimeLineController/FilteredEventsModel. It has sliders to provide -======= - * TimeLineController/FilteredEvetnsModel. It has sliders to provide ->>>>>>> move History back/forward out of ZoomSettingsPane. cleanup and comments - * context/control over three axes of zooming (timescale, event hierarchy, and - * description detail). - */ -public class ZoomSettingsPane extends TitledPane { - - @FXML - private Slider descrLODSlider; - - @FXML - private Slider typeZoomSlider; - - @FXML - private Slider timeUnitSlider; - - @FXML - private Label descrLODLabel; - - @FXML - private Label typeZoomLabel; - - @FXML - private Label timeUnitLabel; - - @FXML - private Label zoomLabel; - - private final TimeLineController controller; -<<<<<<< HEAD -======= - ->>>>>>> move History back/forward out of ZoomSettingsPane. cleanup and comments - private final FilteredEventsModel filteredEvents; - - /** - * Constructor - * - * @param controller TimeLineController this panel functions as a view for. - */ - public ZoomSettingsPane(TimeLineController controller) { - this.controller = controller; - this.filteredEvents = controller.getEventsModel(); - FXMLConstructor.construct(this, "ZoomSettingsPane.fxml"); // NON-NLS - } - - @NbBundle.Messages({ - "ZoomSettingsPane.descrLODLabel.text=Description Detail:", - "ZoomSettingsPane.typeZoomLabel.text=Event Type:", - "ZoomSettingsPane.timeUnitLabel.text=Time Units:", - "ZoomSettingsPane.zoomLabel.text=Zoom"}) - 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()); - - initializeSlider(timeUnitSlider, - () -> { - TimeUnits requestedUnit = TimeUnits.values()[new Double(timeUnitSlider.getValue()).intValue()]; - if (requestedUnit == TimeUnits.FOREVER) { - controller.showFullRange(); - } else { - controller.pushTimeRange(IntervalUtils.getIntervalAround(IntervalUtils.middleOf(ZoomSettingsPane.this.filteredEvents.timeRangeProperty().get()), requestedUnit.getPeriod())); - } - }, - 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.disableProperty().bind(controller.viewModeProperty().isEqualTo(VisualizationMode.COUNTS)); - } - - /** - * setup a slider that with a listener that is added and removed to avoid - * circular updates. - * - * @param the type of the driving property - * @param slider the slider that will have its change handlers - * setup - * @param sliderChangeHandler the runnable that will be executed whenever - * the slider value has changed and is not - * currently changing - * @param driver the property that drives updates to this - * slider - * @param driverChangHandler the code to update the slider bases on the - * value of the driving property. This will be - * wrapped in a remove/add-listener pair to - * prevent circular updates. - */ - private void initializeSlider(Slider slider, Runnable sliderChangeHandler, ReadOnlyObjectProperty driver, Runnable driverChangHandler) { - final InvalidationListener sliderListener = observable -> { - if (slider.isValueChanging() == false) { - sliderChangeHandler.run(); - } - }; - slider.valueProperty().addListener(sliderListener); - slider.valueChangingProperty().addListener(sliderListener); - - Platform.runLater(driverChangHandler); - - driver.addListener(observable -> { - slider.valueProperty().removeListener(sliderListener); - slider.valueChangingProperty().removeListener(sliderListener); - - Platform.runLater(() -> { - driverChangHandler.run(); - slider.valueProperty().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 { - - @Override - public String toString(Double object) { - return TimeUnits.values()[Math.min(TimeUnits.values().length - 1, object.intValue() + 1)].getDisplayName(); - } - - @Override - public Double fromString(String string) { - return new Integer(TimeUnits.valueOf(string).ordinal()).doubleValue(); - } - } - - private static class TypeZoomConverter extends StringConverter { - - @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 { - - @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(); - } - } -} From 373f5cc333bb6267c863a4d458b62fd2af63df22 Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 27 Apr 2016 12:15:31 -0400 Subject: [PATCH 3/5] cleanup merge --- .../autopsy/timeline/TimeLineController.java | 10 ++++++- .../datamodel/FilteredEventsModel.java | 10 +++++-- .../autopsy/timeline/utils/IntervalUtils.java | 11 ++++++++ .../timeline/zooming/DisplayNameProvider.java | 28 +++++++++++++++---- .../timeline/zooming/EventTypeZoomLevel.java | 11 ++++---- 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 93fb1dbe71..e41623cbd2 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -434,6 +434,7 @@ public class TimeLineController { /** * Show the entire range of the timeline. */ + public boolean showFullRange() { synchronized (filteredEvents) { return pushTimeRange(filteredEvents.getSpanningInterval()); } @@ -652,7 +653,14 @@ public class TimeLineController { } } - @SuppressWarnings("AssignmentToMethodParameter") //clamp timerange to case + /** + * 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(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java index 75de500a71..b9d4f85447 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java @@ -167,8 +167,9 @@ public final class FilteredEventsModel { } /** - * @return a read only view of the time range requested via - * {@link #requestTimeRange(org.joda.time.Interval)} + * Get a read only view of the time range currently in view. + * + * @return A read only view of the time range currently in view. */ synchronized public ReadOnlyObjectProperty timeRangeProperty() { if (requestedTimeRange.get() == null) { @@ -189,6 +190,11 @@ public final class FilteredEventsModel { return requestedTypeZoom.getReadOnlyProperty(); } + /** + * The time range currently in view. + * + * @return The time range currently in view. + */ synchronized public Interval getTimeRange() { return timeRangeProperty().get(); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java index ed7fc72e67..7cc8205c65 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java @@ -68,6 +68,17 @@ public class IntervalUtils { 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); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/DisplayNameProvider.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/DisplayNameProvider.java index 145e2363a8..1dc7654ac6 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/DisplayNameProvider.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/DisplayNameProvider.java @@ -1,14 +1,32 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2016 Basis Technology Corp. + * Contact: carrier sleuthkit 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 */ -public interface DisplayNameProvider { +interface DisplayNameProvider { + /** + * Get the display name of this object + * + * @return The display name. + */ String getDisplayName(); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java index 71cd86c781..9f68681561 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java @@ -21,15 +21,16 @@ package org.sleuthkit.autopsy.timeline.zooming; import org.openide.util.NbBundle; /** - * + * Enum of event type zoom levels * */ -public enum EventTypeZoomLevel implements DisplayNameProvider{ +public enum EventTypeZoomLevel implements DisplayNameProvider { - ROOT_TYPE(NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.rootType")), BASE_TYPE( - NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.baseType")), SUB_TYPE( - NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.subType")); + ROOT_TYPE(NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.rootType")), + BASE_TYPE(NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.baseType")), + SUB_TYPE(NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.subType")); + @Override public String getDisplayName() { return displayName; } From 6b9f30c57a6730b228c8cea3c1ef5785d99bdc00 Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 27 Apr 2016 12:23:16 -0400 Subject: [PATCH 4/5] use NbBundle.messages for EventTypeZoomLevel --- .../autopsy/timeline/zooming/Bundle.properties | 4 +--- .../autopsy/timeline/zooming/EventTypeZoomLevel.java | 10 +++++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/Bundle.properties b/Core/src/org/sleuthkit/autopsy/timeline/zooming/Bundle.properties index 4e6e8c45bf..432a15dc7c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/Bundle.properties @@ -1,6 +1,4 @@ DescriptionLOD.short=Short DescriptionLOD.medium=Medium DescriptionLOD.full=Full -EventTypeZoomLevel.rootType=Root Type -EventTypeZoomLevel.baseType=Base Type -EventTypeZoomLevel.subType=Sub Type + diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java index 9f68681561..0f4b1aeb6b 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/EventTypeZoomLevel.java @@ -24,11 +24,15 @@ import org.openide.util.NbBundle; * Enum of event type zoom levels * */ +@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(NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.baseType")), - SUB_TYPE(NbBundle.getMessage(EventTypeZoomLevel.class, "EventTypeZoomLevel.subType")); + ROOT_TYPE(Bundle.EventTypeZoomLevel_rootType()), + BASE_TYPE(Bundle.EventTypeZoomLevel_baseType()), + SUB_TYPE(Bundle.EventTypeZoomLevel_subType()); @Override public String getDisplayName() { From 5be4cf98ce69364f96a808c0361b23f465c688cd Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 27 Apr 2016 15:34:23 -0400 Subject: [PATCH 5/5] more cleanup and comments in ZoomSettingsPane.java --- .../timeline/zooming/ZoomSettingsPane.java | 177 ++++++++++++------ 1 file changed, 115 insertions(+), 62 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java index 0f28f60f3c..59e74248f3 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java @@ -38,31 +38,28 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; /** * A Panel that acts as a view for a given * TimeLineController/FilteredEventsModel. It has sliders to provide - * context/control over three axes of zooming (timescale, event hierarchy, and - * description detail). + * context/control over three axes of zooming (timescale, event hierarchy level, + * and description level of detail). */ public class ZoomSettingsPane extends TitledPane { + @FXML + private Label zoomLabel; + + @FXML + private Label descrLODLabel; @FXML private Slider descrLODSlider; + @FXML + private Label typeZoomLabel; @FXML private Slider typeZoomSlider; - @FXML - private Slider timeUnitSlider; - - @FXML - private Label descrLODLabel; - - @FXML - private Label typeZoomLabel; - @FXML private Label timeUnitLabel; - @FXML - private Label zoomLabel; + private Slider timeUnitSlider; private final TimeLineController controller; private final FilteredEventsModel filteredEvents; @@ -86,111 +83,167 @@ public class ZoomSettingsPane extends TitledPane { public void initialize() { zoomLabel.setText(Bundle.ZoomSettingsPane_zoomLabel_text()); - timeUnitSlider.setMax(TimeUnits.values().length - 1); - timeUnitLabel.setText(Bundle.ZoomSettingsPane_timeUnitLabel_text()); - configureSliderListeners(timeUnitSlider, - controller::pushTimeUnit, - filteredEvents.timeRangeProperty(), - modelTimeRange -> RangeDivisionInfo.getRangeDivisionInfo(modelTimeRange).getPeriodSize().ordinal() - 1, - TimeUnits.class, - dbl -> Math.min(TimeUnits.values().length - 1, dbl.intValue() + 1) - ); - - typeZoomSlider.setMin(0); - typeZoomSlider.setMin(1); + typeZoomSlider.setMin(1); //don't show ROOT_TYPE typeZoomSlider.setMax(EventTypeZoomLevel.values().length - 1); - typeZoomLabel.setText(Bundle.ZoomSettingsPane_typeZoomLabel_text()); configureSliderListeners(typeZoomSlider, controller::pushEventTypeZoom, filteredEvents.eventTypeZoomProperty(), - EventTypeZoomLevel::ordinal, EventTypeZoomLevel.class, - Double::intValue); + EventTypeZoomLevel::ordinal, + Function.identity()); + typeZoomLabel.setText(Bundle.ZoomSettingsPane_typeZoomLabel_text()); descrLODSlider.setMax(DescriptionLoD.values().length - 1); - descrLODLabel.setText(Bundle.ZoomSettingsPane_descrLODLabel_text()); configureSliderListeners(descrLODSlider, controller::pushDescrLOD, filteredEvents.descriptionLODProperty(), - DescriptionLoD::ordinal, DescriptionLoD.class, - Double::intValue); + 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)); + + /** + * 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()); } /** - * Configure the listeners that keep the sliders in sync with model changes, - * and react to user input on the sliders. The listener attached to the - * slider is added and removed to avoid circular updates. + * Configure the listeners that keep the given slider in sync with model + * 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 The type of the driving model property - * @param The type of the enum that is represented along + * Because Sliders work in terms of Doubles but represent ordered Enums that + * are indexed by Integers, and because the model properties may not be of + * the same type as the Enum(timeUnitSlider relates to an Interval in the + * filteredEvents model, rather than the TimeUnits shown on the Slider), a + * mapper is needed to convert between DriverType and Integer + * indices(driverValueMapper). Another mapper is used to modifiy the mapping + * from Integer index to Enum value displayed as the slider tick + * label(labelIndexMapper). + * + * @param The type of the driving model property. + * @param The type of the Enum that is represented along * the slider. - * @param slider The slider that we are configuring + * + * + * @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) + * 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 + * * @param driverValueMapper A Function that maps from driver values of - * type T to Integers representing the ordinal - * index of the corresponding EnumType - * @param enumClass A type token for EnumType, ie Class - * @param converterMapper A Function that maps from Double (slider - * value) to Integers representing the ordinal - * index of the corresponding EnumType + * 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 & DisplayNameProvider> - void configureSliderListeners(Slider slider, - Consumer sliderValueConsumer, - ReadOnlyObjectProperty modelProperty, - Function driverValueMapper, - final Class enumClass, - final Function converterMapper) { + private static & DisplayNameProvider> void configureSliderListeners( + Slider slider, + Consumer sliderValueConsumer, + ReadOnlyObjectProperty modelProperty, + Class enumClass, + Function driverValueMapper, + Function labelIndexMapper) { - slider.setLabelFormatter(new EnumSliderConverter<>(enumClass, converterMapper)); + //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 -> { + //only process event if the slider value is not changing (user has released slider thumb) if (slider.isValueChanging() == false) { - sliderValueConsumer.accept(enumClass.getEnumConstants()[Math.round(slider.valueProperty().floatValue())]); + //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.valueChangingProperty().addListener(sliderListener); - Platform.runLater(() -> slider.setValue(driverValueMapper.apply(modelProperty.get()))); + //set intial value of slider + slider.setValue(driverValueMapper.apply(modelProperty.get())); - modelProperty.addListener(observable -> { + modelProperty.addListener(modelProp -> { + //handle changes in the model property Platform.runLater(() -> { + //remove listener to avoid circular updates slider.valueProperty().removeListener(sliderListener); slider.valueChangingProperty().removeListener(sliderListener); + //sync value of slider to model property value slider.setValue(driverValueMapper.apply(modelProperty.get())); + //reattach listener slider.valueProperty().addListener(sliderListener); slider.valueChangingProperty().addListener(sliderListener); }); }); } - static private class EnumSliderConverter & DisplayNameProvider> extends StringConverter { + /** + * 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 The type of Enum that this converter works with. + */ + static private class EnumSliderLabelFormatter & DisplayNameProvider> extends StringConverter { + /** + * A Type token for the class of Enum that this converter works with. + */ private final Class clazz; - private final Function indexAdjsuter; + /** + * + * 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 indexAdjsuter; - EnumSliderConverter(Class clazz, Function indexMapper) { + EnumSliderLabelFormatter(Class clazz, Function indexMapper) { this.clazz = clazz; this.indexAdjsuter = indexMapper; } @Override - public Double fromString(String string) { - return new Double(EnumType.valueOf(clazz, string).ordinal()); + public String toString(Double dbl) { + //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 - public String toString(Double object) { - return clazz.getEnumConstants()[indexAdjsuter.apply(object)].getDisplayName(); + public Double fromString(String string) { + throw new UnsupportedOperationException("This method should not be used. This EnumSliderLabelFormatter is being used in an unintended way."); } } }