19 package org.sleuthkit.autopsy.timeline.ui.countsview;
21 import com.google.common.collect.Lists;
22 import java.util.ArrayList;
23 import java.util.List;
25 import java.util.function.Function;
26 import javafx.application.Platform;
27 import javafx.beans.Observable;
28 import javafx.beans.property.SimpleObjectProperty;
29 import javafx.collections.FXCollections;
30 import javafx.concurrent.Task;
31 import javafx.fxml.FXML;
32 import javafx.scene.Node;
33 import javafx.scene.chart.CategoryAxis;
34 import javafx.scene.chart.NumberAxis;
35 import javafx.scene.chart.StackedBarChart;
36 import javafx.scene.chart.XYChart;
37 import javafx.scene.control.Label;
38 import javafx.scene.control.RadioButton;
39 import javafx.scene.control.ToggleGroup;
40 import javafx.scene.control.Tooltip;
41 import javafx.scene.effect.Effect;
42 import javafx.scene.layout.HBox;
43 import javafx.scene.layout.Pane;
44 import javafx.scene.layout.Region;
45 import org.joda.time.Interval;
46 import org.openide.util.NbBundle;
81 private final NumberAxis
countAxis =
new NumberAxis();
82 private final CategoryAxis
dateAxis =
new CategoryAxis(FXCollections.<String>observableArrayList());
88 return labelValueString;
93 return dataSeries.stream().flatMap((series) -> series.getData().stream())
94 .anyMatch((data) -> data.getXValue().equals(value) && data.getYValue().intValue() > 0);
103 super(controller, partPane, contextPane, spacer);
113 dateAxis.getTickMarks().addListener((Observable observable) -> {
116 dateAxis.categorySpacingProperty().addListener((Observable observable) -> {
119 dateAxis.getCategories().addListener((Observable observable) -> {
123 spacer.minWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2)));
124 spacer.prefWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2)));
125 spacer.maxWidthProperty().bind(countAxis.widthProperty().add(countAxis.tickLengthProperty()).add(dateAxis.startMarginProperty().multiply(2)));
127 scale.addListener(o -> {
128 countAxis.tickLabelsVisibleProperty().bind(scale.isEqualTo(
ScaleType.
LINEAR));
129 countAxis.tickMarkVisibleProperty().bind(scale.isEqualTo(
ScaleType.
LINEAR));
130 countAxis.minorTickVisibleProperty().bind(scale.isEqualTo(
ScaleType.
LINEAR));
148 return dateAxis.getCategorySpacing();
153 return chart.getSelectionEffect();
182 assert logRadio != null :
"fx:id=\"logRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'.";
183 assert linearRadio != null :
"fx:id=\"linearRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'.";
184 logRadio.setSelected(
true);
185 scaleGroup.selectedToggleProperty().addListener(observable -> {
186 if (scaleGroup.getSelectedToggle() ==
linearRadio) {
189 if (scaleGroup.getSelectedToggle() ==
logRadio) {
194 logRadio.setText(NbBundle.getMessage(
CountsViewPane.class,
"CountsViewPane.logRadio.text"));
195 linearRadio.setText(NbBundle.getMessage(
CountsViewPane.class,
"CountsViewPane.linearRadio.text"));
196 scaleLabel.setText(NbBundle.getMessage(
CountsViewPane.class,
"CountsViewPane.scaleLabel.text"));
207 Platform.runLater(() -> {
208 for (XYChart.Series<String, Number> s :
dataSeries) {
218 private static enum ScaleType implements Function<Long, Double> {
223 private final Function<Long, Double>
func;
231 return func.apply(t);
236 "CountsViewPane.loggedTask.name=Updating Counts View",
237 "CountsViewPane.loggedTask.updatingCounts=Populating visualization"})
241 super(Bundle.CountsViewPane_loggedTask_name(),
true);
245 protected Boolean
call() throws Exception {
252 chart.setRangeInfo(rangeInfo);
255 List<String> categories = Lists.transform(intervals, rangeInfo::formatForTick);
258 resetChart(categories);
260 updateMessage(Bundle.CountsViewPane_loggedTask_updatingCounts());
262 int numIntervals = intervals.size();
272 for (
int i = 0; i < numIntervals; i++) {
276 updateProgress(i, numIntervals);
277 final Interval interval = intervals.get(i);
278 int maxPerInterval = 0;
284 for (
final EventType eventType : eventCounts.keySet()) {
289 final Long count = eventCounts.get(eventType);
291 final String intervalCategory = rangeInfo.
formatForTick(interval);
292 final double adjustedCount = scale.get().apply(count);
294 final XYChart.Data<String, Number> dataItem =
295 new XYChart.Data<>(intervalCategory, adjustedCount,
296 new EventCountsChart.ExtraData(interval, eventType, count));
297 Platform.runLater(() ->
getSeries(eventType).getData().add(dataItem));
298 maxPerInterval += adjustedCount;
301 chartMax = Math.max(chartMax, maxPerInterval);
304 double countAxisUpperbound = 1 + chartMax * 1.2;
306 ? Math.pow(10, Math.max(0, Math.floor(Math.log10(chartMax)) - 1))
308 Platform.runLater(() -> {
309 countAxis.setTickUnit(tickUnit);
310 countAxis.setUpperBound(countAxisUpperbound);
317 dateAxis.getCategories().setAll(categories);
final ObservableList< NodeType > selectedNodes
void setChartClickHandler()
final void createSeries()
final Function< Long, Double > func
void setDateAxisValues(List< String > categories)
String formatForTick(Interval interval)
Task< Boolean > getUpdateTask()
final CategoryAxis dateAxis
final ObservableList< XYChart.Series< X, Y > > dataSeries
static final Logger LOGGER
static Tooltip getDefaultTooltip()
final Map< EventType, XYChart.Series< X, Y > > eventTypeToSeriesMap
Map< EventType, Long > getEventCounts(Interval timeRange)
List< Node > settingsNodes
final NumberAxis countAxis
final SimpleObjectProperty< ScaleType > scale
synchronized void layoutDateLabels()
static RangeDivisionInfo getRangeDivisionInfo(Interval timeRange)
final TimeLineController controller
Effect getSelectionEffect()
Boolean isTickBold(String value)
String getTickMarkLabel(String labelValueString)
final FilteredEventsModel filteredEvents
final XYChart.Series< X, Y > getSeries(final EventType et)
synchronized static Logger getLogger(String name)
ScaleType(Function< Long, Double > func)
static void construct(Node node, String fxmlFileName)
void applySelectionEffect(Node c1, Boolean applied)
synchronized List< Interval > getIntervals()
CountsViewPane(TimeLineController controller, Pane partPane, Pane contextPane, Region spacer)
final synchronized void update()