add scale to counts y axis label. cleanup in CountsViewPane.java and related code.

This commit is contained in:
jmillman 2016-04-29 17:04:00 -04:00
parent 076ee69394
commit a23c6d19f8
6 changed files with 165 additions and 168 deletions

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014-15 Basis Technology Corp. * Copyright 2014-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");
@ -44,7 +44,6 @@ import javafx.scene.chart.XYChart;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.OverrunStyle; import javafx.scene.control.OverrunStyle;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.effect.Effect;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
@ -143,62 +142,70 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
} }
/** /**
* @param value a value along this visualization's x axis * Should the tick mark at the given value be bold, because it has
* interesting data associated with it?
* *
* @return true if the tick label for the given value should be bold ( has * @param value A value along this visualization's x axis
* relevant data), false* otherwise *
* @return True if the tick label for the given value should be bold ( has
* relevant data), false otherwise
*/ */
abstract protected Boolean isTickBold(X value); abstract protected Boolean isTickBold(X value);
/** /**
* apply this visualization's 'selection effect' to the given node * Apply this visualization's 'selection effect' to the given node
* *
* @param node the node to apply the 'effect' to * @param node The node to apply the 'effect' to
* @param applied true if the effect should be applied, false if the effect * @param applied True if the effect should be applied, false if the effect
* should * should not
*/ */
abstract protected void applySelectionEffect(NodeType node, Boolean applied); abstract protected void applySelectionEffect(NodeType node, Boolean applied);
/** /**
* @return a task to execute on a background thread to reload this * Get a background Task that fetches the appropriate data and loads it into
* this visualization.
*
* @return A task to execute on a background thread to reload this
* visualization with different data. * visualization with different data.
*/ */
abstract protected Task<Boolean> getUpdateTask(); abstract protected Task<Boolean> getUpdateTask();
/** /**
* @return return the {@link Effect} applied to 'selected nodes' in this * Get the label that should be used for a tick mark at the given value.
* visualization, or null if selection is visualized via another
* mechanism
*/
abstract protected Effect getSelectionEffect();
/**
* @param tickValue
* *
* @return a String to use for a tick mark label given a tick value * @param tickValue The value to get a label for.
*
* @return a String to use for a tick mark label given a tick value.
*/ */
abstract protected String getTickMarkLabel(X tickValue); abstract protected String getTickMarkLabel(X tickValue);
/** /**
* the spacing (in pixels) between tick marks of the horizontal axis. This * Get the spacing, in pixels, between tick marks of the horizontal axis.
* will be used to layout the decluttered replacement labels. * This will be used to layout the decluttered replacement labels.
* *
* @return the spacing in pixels between tick marks of the horizontal axis * @return The spacing, in pixels, between tick marks of the horizontal axis
*/ */
abstract protected double getTickSpacing(); abstract protected double getTickSpacing();
/** /**
* Get the X-Axis of this Visualization's chart
*
* @return the horizontal axis used by this Visualization's chart * @return the horizontal axis used by this Visualization's chart
*/ */
abstract protected Axis<X> getXAxis(); abstract protected Axis<X> getXAxis();
/** /**
* Get the Y-Axis of this Visualization's chart
*
* @return the vertical axis used by this Visualization's chart * @return the vertical axis used by this Visualization's chart
*/ */
abstract protected Axis<Y> getYAxis(); abstract protected Axis<Y> getYAxis();
/**
* Clear all data items from this chart.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
abstract protected void resetData(); abstract protected void clearChartData();
/** /**
* update this visualization based on current state of zoom / filters. * update this visualization based on current state of zoom / filters.
@ -243,7 +250,7 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
} }
/** /**
* make a series for each event type in a consistent order * Make a series for each event type in a consistent order
*/ */
protected final void createSeries() { protected final void createSeries() {
for (EventType eventType : EventType.allTypes) { for (EventType eventType : EventType.allTypes) {
@ -538,16 +545,15 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
} }
/** /**
* Clears the chart data and sets the horisontal axis range. For use * Clears the chart data and sets the horizontal axis range. For use
* within the derived implementation of the call() method. * within the derived implementation of the call() method.
* *
* @param axisValues * @param axisValues
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.NOT_UI) @ThreadConfined(type = ThreadConfined.ThreadType.NOT_UI)
protected void resetChart(AxisValuesType axisValues) { protected void resetChart(AxisValuesType axisValues) {
Platform.runLater(() -> { Platform.runLater(() -> {
resetData(); clearChartData();
setDateAxisValues(axisValues); setDateAxisValues(axisValues);
}); });
} }

View File

@ -1,24 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-15 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.
*/
#this is the label for the vetical axis
CountsChartPane.numberOfEvents=Number of Events
CountsViewPane.scaleLabel.text=Scale\:
CountsViewPane.logRadio.text=Logarithmic
CountsViewPane.linearRadio.text=Linear

View File

@ -1,14 +1,14 @@
CountsChartPane.numberOfEvents=\u30A4\u30D9\u30F3\u30C8\u6570 CountsViewPane.numberOfEvents=\u30a4\u30d9\u30f3\u30c8\u6570
CountsViewPane.detailSwitchMessage=\u79D2\u3088\u308A\u5C0F\u3055\u3044\u5358\u4F4D\u306F\u3042\u308A\u307E\u305B\u3093\u3002\n\u8A73\u7D30\u30D3\u30E5\u30FC\u306B\u5909\u66F4\u3057\u307E\u3059\u304B\uFF1F CountsViewPane.detailSwitchMessage=\u79d2\u3088\u308a\u5c0f\u3055\u3044\u5358\u4f4d\u306f\u3042\u308a\u307e\u305b\u3093\u3002\n\u8a73\u7d30\u30d3\u30e5\u30fc\u306b\u5909\u66f4\u3057\u307e\u3059\u304b\uff1f
CountsViewPane.detailSwitchTitle=\u8A73\u7D30\u30D3\u30E5\u30FC\u306B\u5909\u66F4\u3057\u307E\u3059\u304B\uFF1F CountsViewPane.detailSwitchTitle=\u8a73\u7d30\u30d3\u30e5\u30fc\u306b\u5909\u66f4\u3057\u307e\u3059\u304b\uff1f
Timeline.ui.countsview.menuItem.selectEventType=\u30A4\u30D9\u30F3\u30C8\u30BF\u30A4\u30D7\u3092\u9078\u629E Timeline.ui.countsview.menuItem.selectEventType=\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7\u3092\u9078\u629e
Timeline.ui.countsview.menuItem.selectTimeandType=\u6642\u9593\u3068\u30BF\u30A4\u30D7\u3092\u9078\u629E Timeline.ui.countsview.menuItem.selectTimeandType=\u6642\u9593\u3068\u30bf\u30a4\u30d7\u3092\u9078\u629e
Timeline.ui.countsview.menuItem.selectTimeRange=\u6642\u9593\u7BC4\u56F2\u3092\u9078\u629E Timeline.ui.countsview.menuItem.selectTimeRange=\u6642\u9593\u7bc4\u56f2\u3092\u9078\u629e
Timeline.ui.countsview.menuItem.zoomIntoTimeRange=\u6642\u9593\u7BC4\u56F2\u3078\u30BA\u30FC\u30E0\u30A4\u30F3 Timeline.ui.countsview.menuItem.zoomIntoTimeRange=\u6642\u9593\u7bc4\u56f2\u3078\u30ba\u30fc\u30e0\u30a4\u30f3
CountsViewPane.loggedTask.name=\u30AB\u30A6\u30F3\u30C8\u30D3\u30E5\u30FC\u3092\u66F4\u65B0\u4E2D CountsViewPane.loggedTask.name=\u30ab\u30a6\u30f3\u30c8\u30d3\u30e5\u30fc\u3092\u66f4\u65b0\u4e2d
CountsViewPane.loggedTask.updatingCounts=\u30D3\u30B8\u30E5\u30A2\u30E9\u30A4\u30BC\u30FC\u30B7\u30E7\u30F3\uFF08\u53EF\u8996\u5316\uFF09\u3092\u5165\u529B\u4E2D CountsViewPane.loggedTask.updatingCounts=\u30d3\u30b8\u30e5\u30a2\u30e9\u30a4\u30bc\u30fc\u30b7\u30e7\u30f3\uff08\u53ef\u8996\u5316\uff09\u3092\u5165\u529b\u4e2d
CountsViewPane.tooltip.text={0} {1} \u30A4\u30D9\u30F3\u30C8\n{2}\u3068\n{3}\u306E\u9593 CountsViewPane.tooltip.text={0} {1} \u30a4\u30d9\u30f3\u30c8\n{2}\u3068\n{3}\u306e\u9593
CountsViewPane.linearRadio.text=\u30EA\u30CB\u30A2 CountsViewPane.linearRadio.text=\u30ea\u30cb\u30a2
CountsViewPane.logRadio.text=\u5BFE\u6570\u7684 CountsViewPane.logRadio.text=\u5bfe\u6570\u7684
CountsViewPane.scaleLabel.text=\u30B9\u30B1\u30FC\u30EB\uFF1A CountsViewPane.scaleLabel.text=\u30b9\u30b1\u30fc\u30eb\uff1a
*=Autopsy\u30D5\u30A9\u30EC\u30F3\u30B8\u30C3\u30AF\u30D6\u30E9\u30A6\u30B6 *=Autopsy\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30d6\u30e9\u30a6\u30b6

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014 Basis Technology Corp. * Copyright 2014-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");
@ -25,6 +25,7 @@ import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.concurrent.Task; import javafx.concurrent.Task;
@ -32,13 +33,11 @@ import javafx.fxml.FXML;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.chart.CategoryAxis; import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis; import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart; import javafx.scene.chart.XYChart;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.RadioButton; import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup; import javafx.scene.control.ToggleGroup;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.effect.Effect;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
@ -48,32 +47,26 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane; import static org.sleuthkit.autopsy.timeline.ui.countsview.Bundle.*;
import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
/** /**
* FXML Controller class for a {@link StackedBarChart<String,Number>} based * FXML Controller class for a StackedBarChart<String,Number> based
* implementation of a {@link TimeLineView}. * implementation of a TimeLineChart.
* *
* This class listens to changes in the assigned {@link FilteredEventsModel} and * This class listens to changes in the assigned FilteredEventsModel and updates
* updates the internal {@link StackedBarChart} to reflect the currently * the internal EventCountsChart to reflect the currently requested events.
* requested events.
* *
* This class captures input from the user in the form of mouse clicks on graph * This class captures input from the user in the form of mouse clicks on graph
* bars, and forwards them to the assigned {@link TimeLineController} * * bars, and forwards them to the assigned TimeLineController
* *
* Concurrency Policy: Access to the private members stackedBarChart, countAxis, * Concurrency Policy: Access to the private members stackedBarChart, countAxis,
* dateAxis, EventTypeMap, and dataSets affects the stackedBarChart so they all * dateAxis, EventTypeMap, and dataSets affects the stackedBarChart so they all
* must only be manipulated on the JavaFx thread (through {@link Platform#runLater(java.lang.Runnable)} * must only be manipulated on the JavaFx thread (through
* * Platform.runLater(java.lang.Runnable). The FilteredEventsModel should
* {@link CountsChartPane#filteredEvents} should encapsulate all need * encapsulate all need synchronization internally.
* synchronization internally.
*
* TODO: refactor common code out of this class and {@link DetailViewPane} into
* {@link AbstractVisualizationPane}
*/ */
public class CountsViewPane extends AbstractVisualizationPane<String, Number, Node, EventCountsChart> { public class CountsViewPane extends AbstractVisualizationPane<String, Number, Node, EventCountsChart> {
@ -82,7 +75,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
private final NumberAxis countAxis = new NumberAxis(); private final NumberAxis countAxis = new NumberAxis();
private final CategoryAxis dateAxis = new CategoryAxis(FXCollections.<String>observableArrayList()); private final CategoryAxis dateAxis = new CategoryAxis(FXCollections.<String>observableArrayList());
private final SimpleObjectProperty<ScaleType> scale = new SimpleObjectProperty<>(ScaleType.LOGARITHMIC); private final SimpleObjectProperty<Scale> scaleProp = new SimpleObjectProperty<>(Scale.LOGARITHMIC);
@Override @Override
protected String getTickMarkLabel(String labelValueString) { protected String getTickMarkLabel(String labelValueString) {
@ -100,38 +93,42 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
return new CountsUpdateTask(); return new CountsUpdateTask();
} }
/**
* Constructor
*
* @param controller The TimelineController for this visualization
* @param partPane
* @param contextPane
* @param spacer
*/
@NbBundle.Messages({"CountsViewPane.numberOfEvents=Number of Events ({0})"})
public CountsViewPane(TimeLineController controller, Pane partPane, Pane contextPane, Region spacer) { public CountsViewPane(TimeLineController controller, Pane partPane, Pane contextPane, Region spacer) {
super(controller, partPane, contextPane, spacer); super(controller, partPane, contextPane, spacer);
chart = new EventCountsChart(controller, dateAxis, countAxis, selectedNodes); chart = new EventCountsChart(controller, dateAxis, countAxis, selectedNodes);
chart.setData(dataSeries); chart.setData(dataSeries);
setCenter(chart); setCenter(chart);
Tooltip.install(chart, getDefaultTooltip()); Tooltip.install(chart, getDefaultTooltip());
settingsNodes = new ArrayList<>(new CountsViewSettingsPane().getChildrenUnmodifiable()); settingsNodes = new ArrayList<>(new CountsViewSettingsPane().getChildrenUnmodifiable());
dateAxis.getTickMarks().addListener((Observable observable) -> { dateAxis.getTickMarks().addListener((Observable tickMarks) -> layoutDateLabels());
layoutDateLabels(); dateAxis.categorySpacingProperty().addListener((Observable spacing) -> layoutDateLabels());
}); dateAxis.getCategories().addListener((Observable categories) -> layoutDateLabels());
dateAxis.categorySpacingProperty().addListener((Observable observable) -> {
layoutDateLabels();
});
dateAxis.getCategories().addListener((Observable observable) -> {
layoutDateLabels();
});
spacer.minWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2))); spacer.minWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2)));
spacer.prefWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2))); spacer.prefWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2)));
spacer.maxWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2))); spacer.maxWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2)));
scale.addListener(o -> { //bind tick visibility to scaleProp
countAxis.tickLabelsVisibleProperty().bind(scale.isEqualTo(ScaleType.LINEAR)); BooleanBinding scaleIsLinear = scaleProp.isEqualTo(Scale.LINEAR);
countAxis.tickMarkVisibleProperty().bind(scale.isEqualTo(ScaleType.LINEAR)); countAxis.tickLabelsVisibleProperty().bind(scaleIsLinear);
countAxis.minorTickVisibleProperty().bind(scale.isEqualTo(ScaleType.LINEAR)); countAxis.tickMarkVisibleProperty().bind(scaleIsLinear);
countAxis.minorTickVisibleProperty().bind(scaleIsLinear);
scaleProp.addListener(scale -> {
update(); update();
countAxis.setLabel(Bundle.CountsViewPane_numberOfEvents(scaleProp.get().getDisplayName()));
}); });
countAxis.setLabel(Bundle.CountsViewPane_numberOfEvents(scaleProp.get().getDisplayName()));
} }
@Override @Override
@ -150,28 +147,74 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
} }
@Override @Override
protected Effect getSelectionEffect() { protected void applySelectionEffect(Node c1, Boolean applied) {
return chart.getSelectionEffect(); c1.setEffect(applied ? chart.getSelectionEffect() : null);
} }
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
@Override @Override
protected void applySelectionEffect(Node c1, Boolean applied) { protected void clearChartData() {
if (applied) { for (XYChart.Series<String, Number> series : dataSeries) {
c1.setEffect(getSelectionEffect()); series.getData().clear();
} else { }
c1.setEffect(null); dataSeries.clear();
eventTypeToSeriesMap.clear();
createSeries();
}
/**
* Enum for the Scales available in the Counts View.
*/
@NbBundle.Messages({
"ScaleType.Linear=Linear",
"ScaleType.Logarithmic=Logarithmic"
})
private static enum Scale implements Function<Long, Double> {
LINEAR(Bundle.ScaleType_Linear()) {
@Override
public Double apply(Long inValue) {
return inValue.doubleValue();
}
},
LOGARITHMIC(Bundle.ScaleType_Logarithmic()) {
@Override
public Double apply(Long inValue) {
return Math.log10(inValue) + 1;
}
};
private final String displayName;
/**
* Constructor
*
* @param displayName The display name for this Scale.
*/
Scale(String displayName) {
this.displayName = displayName;
}
/**
* Get the display name of this ScaleType
*
* @return The display name.
*/
public String getDisplayName() {
return displayName;
} }
} }
/*
* A Pane that contains widgets to adjust settings specific to a
* CountsViewPane
*/
private class CountsViewSettingsPane extends HBox { private class CountsViewSettingsPane extends HBox {
@FXML @FXML
private RadioButton logRadio; private RadioButton logRadio;
@FXML @FXML
private RadioButton linearRadio; private RadioButton linearRadio;
@FXML @FXML
private ToggleGroup scaleGroup; private ToggleGroup scaleGroup;
@ -179,58 +222,40 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
private Label scaleLabel; private Label scaleLabel;
@FXML @FXML
@NbBundle.Messages({
"CountsViewPane.logRadio.text=Logarithmic",
"CountsViewPane.scaleLabel.text=Scale:",
"CountsViewPane.linearRadio.text=Linear"})
void initialize() { void initialize() {
assert logRadio != null : "fx:id=\"logRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'."; // NON-NLS assert logRadio != null : "fx:id=\"logRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'."; // NON-NLS
assert linearRadio != null : "fx:id=\"linearRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'."; // NON-NLS assert linearRadio != null : "fx:id=\"linearRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'."; // NON-NLS
logRadio.setSelected(true); logRadio.setSelected(true);
scaleGroup.selectedToggleProperty().addListener(observable -> { scaleGroup.selectedToggleProperty().addListener(observable -> {
if (scaleGroup.getSelectedToggle() == linearRadio) { if (scaleGroup.getSelectedToggle() == linearRadio) {
scale.set(ScaleType.LINEAR); scaleProp.set(Scale.LINEAR);
} } else if (scaleGroup.getSelectedToggle() == logRadio) {
if (scaleGroup.getSelectedToggle() == logRadio) { scaleProp.set(Scale.LOGARITHMIC);
scale.set(ScaleType.LOGARITHMIC);
} }
}); });
logRadio.setText(NbBundle.getMessage(CountsViewPane.class, "CountsViewPane.logRadio.text")); logRadio.setText(CountsViewPane_logRadio_text());
linearRadio.setText(NbBundle.getMessage(CountsViewPane.class, "CountsViewPane.linearRadio.text")); linearRadio.setText(CountsViewPane_linearRadio_text());
scaleLabel.setText(NbBundle.getMessage(CountsViewPane.class, "CountsViewPane.scaleLabel.text")); scaleLabel.setText(CountsViewPane_scaleLabel_text());
} }
/**
* Constructor
*/
CountsViewSettingsPane() { CountsViewSettingsPane() {
FXMLConstructor.construct(this, "CountsViewSettingsPane.fxml"); // NON-NLS FXMLConstructor.construct(this, "CountsViewSettingsPane.fxml"); // NON-NLS
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) /**
@Override * Task that clears the Chart, fetches new data according to the current
protected void resetData() { * ZoomParams and loads it into the Chart
for (XYChart.Series<String, Number> s : dataSeries) { *
s.getData().clear(); */
}
dataSeries.clear();
eventTypeToSeriesMap.clear();
createSeries();
}
private static enum ScaleType implements Function<Long, Double> {
LINEAR(Long::doubleValue),
LOGARITHMIC(t -> Math.log10(t) + 1);
private final Function<Long, Double> func;
ScaleType(Function<Long, Double> func) {
this.func = func;
}
@Override
public Double apply(Long t) {
return func.apply(t);
}
}
@NbBundle.Messages({ @NbBundle.Messages({
"CountsViewPane.loggedTask.name=Updating Counts View", "CountsViewPane.loggedTask.name=Updating Counts View",
"CountsViewPane.loggedTask.updatingCounts=Populating visualization"}) "CountsViewPane.loggedTask.updatingCounts=Populating visualization"})
@ -249,24 +274,21 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
final RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(getTimeRange()); final RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(getTimeRange());
chart.setRangeInfo(rangeInfo); //do we need this. It seems like a hack. chart.setRangeInfo(rangeInfo); //do we need this. It seems like a hack.
List<Interval> intervals = rangeInfo.getIntervals(); List<Interval> intervals = rangeInfo.getIntervals();
List<String> categories = Lists.transform(intervals, rangeInfo::formatForTick);
//clear old data, and reset ranges and series //clear old data, and reset ranges and series
resetChart(categories); resetChart(Lists.transform(intervals, rangeInfo::formatForTick));
updateMessage(Bundle.CountsViewPane_loggedTask_updatingCounts()); updateMessage(Bundle.CountsViewPane_loggedTask_updatingCounts());
int chartMax = 0; int chartMax = 0;
int numIntervals = intervals.size(); int numIntervals = intervals.size();
Scale activeScale = scaleProp.get();
/* /*
* for each interval query database for event counts and add to * For each interval, query the database for event counts and add
* chart. * the counts to the chart. Doing this in chunks might seem
* * inefficient but it lets us reuse more cached results as the user
* Doing this in chunks might seem inefficient but it lets us reuse * navigates to overlapping views.
* more cached results as the user navigates to overlapping viewws
*
* //TODO: implement similar chunked caching in DetailsView -jm
*/ */
for (int i = 0; i < numIntervals; i++) { for (int i = 0; i < numIntervals; i++) {
if (isCancelled()) { if (isCancelled()) {
@ -288,7 +310,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
final Long count = eventCounts.get(eventType); final Long count = eventCounts.get(eventType);
if (count > 0) { if (count > 0) {
final String intervalCategory = rangeInfo.formatForTick(interval); final String intervalCategory = rangeInfo.formatForTick(interval);
final double adjustedCount = scale.get().apply(count); final double adjustedCount = activeScale.apply(count);
final XYChart.Data<String, Number> dataItem = final XYChart.Data<String, Number> dataItem =
new XYChart.Data<>(intervalCategory, adjustedCount, new XYChart.Data<>(intervalCategory, adjustedCount,
@ -299,9 +321,10 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
} }
chartMax = Math.max(chartMax, maxPerInterval); chartMax = Math.max(chartMax, maxPerInterval);
} }
//adjust vertical axis according to scale type and max counts //adjust vertical axis according to scale type and max counts
double countAxisUpperbound = 1 + chartMax * 1.2; double countAxisUpperbound = 1 + chartMax * 1.2;
double tickUnit = ScaleType.LINEAR.equals(scale.get()) double tickUnit = Scale.LINEAR.equals(activeScale)
? Math.pow(10, Math.max(0, Math.floor(Math.log10(chartMax)) - 1)) ? Math.pow(10, Math.max(0, Math.floor(Math.log10(chartMax)) - 1))
: Double.MAX_VALUE; : Double.MAX_VALUE;
Platform.runLater(() -> { Platform.runLater(() -> {

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014-15 Basis Technology Corp. * Copyright 2014-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");
@ -87,7 +87,6 @@ final class EventCountsChart extends StackedBarChart<String, Number> implements
dateAxis.setTickLabelsVisible(false); dateAxis.setTickLabelsVisible(false);
dateAxis.setTickLabelGap(0); dateAxis.setTickLabelGap(0);
countAxis.setLabel(NbBundle.getMessage(CountsViewPane.class, "CountsChartPane.numberOfEvents"));
countAxis.setAutoRanging(false); countAxis.setAutoRanging(false);
countAxis.setLowerBound(0); countAxis.setLowerBound(0);
countAxis.setAnimated(true); countAxis.setAnimated(true);
@ -167,6 +166,7 @@ final class EventCountsChart extends StackedBarChart<String, Number> implements
return new CountsIntervalSelector(this); return new CountsIntervalSelector(this);
} }
@Override
public ObservableList<Node> getSelectedNodes() { public ObservableList<Node> getSelectedNodes() {
return selectedNodes; return selectedNodes;
} }

View File

@ -40,7 +40,6 @@ import javafx.scene.control.RadioButton;
import javafx.scene.control.Slider; import javafx.scene.control.Slider;
import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup; import javafx.scene.control.ToggleGroup;
import javafx.scene.effect.Effect;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
@ -57,8 +56,6 @@ import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
import org.sleuthkit.autopsy.timeline.ui.detailview.HideDescriptionAction;
import org.sleuthkit.autopsy.timeline.ui.detailview.UnhideDescriptionAction;
import org.sleuthkit.autopsy.timeline.utils.MappedList; import org.sleuthkit.autopsy.timeline.utils.MappedList;
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
@ -205,7 +202,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
@Override @Override
protected void resetData() { protected void clearChartData() {
chart.reset(); chart.reset();
} }
@ -234,11 +231,6 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
return new DetailsUpdateTask(); return new DetailsUpdateTask();
} }
@Override
protected Effect getSelectionEffect() {
return null;
}
@Override @Override
protected void applySelectionEffect(EventNodeBase<?> c1, Boolean selected) { protected void applySelectionEffect(EventNodeBase<?> c1, Boolean selected) {
c1.applySelectionEffect(selected); c1.applySelectionEffect(selected);