mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-15 01:07:42 +00:00
cleanup, comments, minor refactoring
This commit is contained in:
parent
34a0b4f134
commit
cc926e4642
@ -25,7 +25,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
|||||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* A interface for groups of events that share some attributes in common.
|
||||||
*/
|
*/
|
||||||
public interface EventBundle<ParentType extends EventBundle<?>> {
|
public interface EventBundle<ParentType extends EventBundle<?>> {
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A 'collection' of {@link EventCluster}s, all having the same type,
|
* A 'collection' of {@link EventCluster}s, all having the same type,
|
||||||
* description, and zoom levels.
|
* description, and zoom levels, but not necessarily close together in time.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public final class EventStripe implements EventBundle<EventCluster> {
|
public final class EventStripe implements EventBundle<EventCluster> {
|
||||||
|
@ -91,6 +91,7 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
|||||||
*/
|
*/
|
||||||
public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster, EventBundleNodeBase<?, ?, ?>, EventDetailChart> {
|
public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster, EventBundleNodeBase<?, ?, ?>, EventDetailChart> {
|
||||||
|
|
||||||
|
|
||||||
private final static Logger LOGGER = Logger.getLogger(DetailViewPane.class.getName());
|
private final static Logger LOGGER = Logger.getLogger(DetailViewPane.class.getName());
|
||||||
|
|
||||||
private MultipleSelectionModel<TreeItem<EventBundle<?>>> treeSelectionModel;
|
private MultipleSelectionModel<TreeItem<EventBundle<?>>> treeSelectionModel;
|
||||||
@ -475,4 +476,6 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
|||||||
public Action newHideDescriptionAction(String description, DescriptionLoD descriptionLoD) {
|
public Action newHideDescriptionAction(String description, DescriptionLoD descriptionLoD) {
|
||||||
return chart.new HideDescriptionAction(description, descriptionLoD);
|
return chart.new HideDescriptionAction(description, descriptionLoD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
* Autopsy Forensic Browser
|
||||||
* To change this template file, choose Tools | Templates
|
*
|
||||||
* and open the template in the editor.
|
* Copyright 2015 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.MissingResourceException;
|
import java.util.MissingResourceException;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -38,7 +37,6 @@ import javafx.animation.KeyValue;
|
|||||||
import javafx.animation.Timeline;
|
import javafx.animation.Timeline;
|
||||||
import javafx.beans.InvalidationListener;
|
import javafx.beans.InvalidationListener;
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.property.Property;
|
|
||||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||||
import javafx.beans.property.ReadOnlyDoubleWrapper;
|
import javafx.beans.property.ReadOnlyDoubleWrapper;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
@ -48,7 +46,6 @@ import javafx.collections.FXCollections;
|
|||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.Cursor;
|
import javafx.scene.Cursor;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
@ -99,12 +96,12 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
|||||||
*/
|
*/
|
||||||
public final class EventDetailChart extends XYChart<DateTime, EventCluster> implements TimeLineChart<DateTime> {
|
public final class EventDetailChart extends XYChart<DateTime, EventCluster> implements TimeLineChart<DateTime> {
|
||||||
|
|
||||||
static final Image HIDE = new Image("/org/sleuthkit/autopsy/timeline/images/eye--minus.png"); // NON-NLS
|
private static final Image HIDE = new Image("/org/sleuthkit/autopsy/timeline/images/eye--minus.png"); // NON-NLS
|
||||||
static final Image SHOW = new Image("/org/sleuthkit/autopsy/timeline/images/eye--plus.png"); // NON-NLS
|
private static final Image SHOW = new Image("/org/sleuthkit/autopsy/timeline/images/eye--plus.png"); // NON-NLS
|
||||||
private static final Image MARKER = new Image("/org/sleuthkit/autopsy/timeline/images/marker.png", 16, 16, true, true, true);
|
private static final Image MARKER = new Image("/org/sleuthkit/autopsy/timeline/images/marker.png", 16, 16, true, true, true);
|
||||||
private static final int PROJECTED_LINE_Y_OFFSET = 5;
|
private static final int PROJECTED_LINE_Y_OFFSET = 5;
|
||||||
private static final int PROJECTED_LINE_STROKE_WIDTH = 5;
|
private static final int PROJECTED_LINE_STROKE_WIDTH = 5;
|
||||||
private static final int MINIMUM_GAP = 4;
|
private static final int MINIMUM_EVENT_NODE_GAP = 4;
|
||||||
private ContextMenu chartContextMenu;
|
private ContextMenu chartContextMenu;
|
||||||
|
|
||||||
private TimeLineController controller;
|
private TimeLineController controller;
|
||||||
@ -112,7 +109,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
private FilteredEventsModel filteredEvents;
|
private FilteredEventsModel filteredEvents;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a user position-able vertical line to help the compare events
|
* a user positionable vertical line to help compare events
|
||||||
*/
|
*/
|
||||||
private Line guideLine;
|
private Line guideLine;
|
||||||
|
|
||||||
@ -124,7 +121,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
private IntervalSelector<? extends DateTime> intervalSelector;
|
private IntervalSelector<? extends DateTime> intervalSelector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* listener that triggers layout pass
|
* listener that triggers chart layout pass
|
||||||
*/
|
*/
|
||||||
private final InvalidationListener layoutInvalidationListener = (Observable o) -> {
|
private final InvalidationListener layoutInvalidationListener = (Observable o) -> {
|
||||||
layoutPlotChildren();
|
layoutPlotChildren();
|
||||||
@ -154,11 +151,6 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
private final ObservableList<Series<DateTime, EventCluster>> seriesList =
|
private final ObservableList<Series<DateTime, EventCluster>> seriesList =
|
||||||
FXCollections.<Series<DateTime, EventCluster>>observableArrayList();
|
FXCollections.<Series<DateTime, EventCluster>>observableArrayList();
|
||||||
|
|
||||||
private final ObservableList<Series<DateTime, EventCluster>> sortedSeriesList = seriesList
|
|
||||||
.sorted((Series<DateTime, EventCluster> s1, Series<DateTime, EventCluster> s2) -> {
|
|
||||||
final List<String> eventTypeNames = EventType.allTypes.stream().map(EventType::getDisplayName).collect(Collectors.toList());
|
|
||||||
return Integer.compare(eventTypeNames.indexOf(s1.getName()), eventTypeNames.indexOf(s2.getName()));
|
|
||||||
});
|
|
||||||
/**
|
/**
|
||||||
* true == layout each event type in its own band, false == mix all the
|
* true == layout each event type in its own band, false == mix all the
|
||||||
* events together during layout
|
* events together during layout
|
||||||
@ -190,18 +182,16 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
* via slider if truncateAll is true
|
* via slider if truncateAll is true
|
||||||
*/
|
*/
|
||||||
final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0);
|
final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0);
|
||||||
private final SimpleBooleanProperty alternateLayout = new SimpleBooleanProperty(true);
|
|
||||||
|
|
||||||
EventDetailChart(DateAxis dateAxis, final Axis<EventCluster> verticalAxis, ObservableList<EventBundleNodeBase<?, ?, ?>> selectedNodes) {
|
EventDetailChart(DateAxis dateAxis, final Axis<EventCluster> verticalAxis, ObservableList<EventBundleNodeBase<?, ?, ?>> selectedNodes) {
|
||||||
super(dateAxis, verticalAxis);
|
super(dateAxis, verticalAxis);
|
||||||
dateAxis.setAutoRanging(false);
|
dateAxis.setAutoRanging(false);
|
||||||
|
|
||||||
verticalAxis.setVisible(false);//TODO: why doesn't this hide the vertical axis, instead we have to turn off all parts individually? -jm
|
verticalAxis.setVisible(false);//TODO: why doesn't this hide the vertical axis, instead we have to turn off all parts individually? -jm
|
||||||
|
|
||||||
verticalAxis.setTickLabelsVisible(false);
|
verticalAxis.setTickLabelsVisible(false);
|
||||||
verticalAxis.setTickMarkVisible(false);
|
verticalAxis.setTickMarkVisible(false);
|
||||||
|
|
||||||
setLegendVisible(false);
|
setLegendVisible(false);
|
||||||
|
|
||||||
setPadding(Insets.EMPTY);
|
setPadding(Insets.EMPTY);
|
||||||
setAlternativeColumnFillVisible(true);
|
setAlternativeColumnFillVisible(true);
|
||||||
|
|
||||||
@ -221,8 +211,8 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
setPrefHeight(boundsInLocalProperty().get().getHeight());
|
setPrefHeight(boundsInLocalProperty().get().getHeight());
|
||||||
});
|
});
|
||||||
|
|
||||||
//set up mouse listeners
|
///////set up mouse listeners
|
||||||
final EventHandler<MouseEvent> clickHandler = (MouseEvent clickEvent) -> {
|
setOnMouseClicked((MouseEvent clickEvent) -> {
|
||||||
if (chartContextMenu != null) {
|
if (chartContextMenu != null) {
|
||||||
chartContextMenu.hide();
|
chartContextMenu.hide();
|
||||||
}
|
}
|
||||||
@ -231,10 +221,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
chartContextMenu.show(EventDetailChart.this, clickEvent.getScreenX(), clickEvent.getScreenY());
|
chartContextMenu.show(EventDetailChart.this, clickEvent.getScreenX(), clickEvent.getScreenY());
|
||||||
clickEvent.consume();
|
clickEvent.consume();
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
setOnMouseClicked(clickHandler);
|
|
||||||
|
|
||||||
//use one handler with an if chain because it maintains state
|
//use one handler with an if chain because it maintains state
|
||||||
final ChartDragHandler<DateTime, EventDetailChart> dragHandler = new ChartDragHandler<>(this, getXAxis());
|
final ChartDragHandler<DateTime, EventDetailChart> dragHandler = new ChartDragHandler<>(this, getXAxis());
|
||||||
setOnMousePressed(dragHandler);
|
setOnMousePressed(dragHandler);
|
||||||
@ -242,36 +229,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
setOnMouseDragged(dragHandler);
|
setOnMouseDragged(dragHandler);
|
||||||
|
|
||||||
this.selectedNodes = selectedNodes;
|
this.selectedNodes = selectedNodes;
|
||||||
this.selectedNodes.addListener((
|
this.selectedNodes.addListener(new SelectionChangeHandler());
|
||||||
ListChangeListener.Change<? extends EventBundleNodeBase<?, ?, ?>> change) -> {
|
|
||||||
while (change.next()) {
|
|
||||||
change.getRemoved().forEach((EventBundleNodeBase<?, ?, ?> removedNode) -> {
|
|
||||||
removedNode.getEventBundle().getClusters().forEach(cluster -> {
|
|
||||||
Line removedLine = projectionMap.remove(cluster);
|
|
||||||
getChartChildren().removeAll(removedLine);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
change.getAddedSubList().forEach((EventBundleNodeBase<?, ?, ?> addedNode) -> {
|
|
||||||
|
|
||||||
for (EventCluster range : addedNode.getEventBundle().getClusters()) {
|
|
||||||
|
|
||||||
Line line = new Line(dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(range.getStartMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET,
|
|
||||||
dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(range.getEndMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET
|
|
||||||
);
|
|
||||||
line.setStroke(addedNode.getEventType().getColor().deriveColor(0, 1, 1, .5));
|
|
||||||
line.setStrokeWidth(PROJECTED_LINE_STROKE_WIDTH);
|
|
||||||
line.setStrokeLineCap(StrokeLineCap.ROUND);
|
|
||||||
projectionMap.put(range, line);
|
|
||||||
getChartChildren().add(line);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.controller.selectEventIDs(selectedNodes.stream()
|
|
||||||
.flatMap(detailNode -> detailNode.getEventIDs().stream())
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ObservableList<EventBundle<?>> getEventBundles() {
|
ObservableList<EventBundle<?>> getEventBundles() {
|
||||||
@ -430,7 +388,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
.sorted(Comparator.comparing(EventStripeNode::getStartMillis))
|
.sorted(Comparator.comparing(EventStripeNode::getStartMillis))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
maxY.set(maxY.get() + layoutEventBundleNodes(stripeNodes, maxY.get()));
|
maxY.set(layoutEventBundleNodes(stripeNodes, maxY.get()));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
List<EventStripeNode> stripeNodes = stripeNodeMap.values().stream()
|
List<EventStripeNode> stripeNodes = stripeNodeMap.values().stream()
|
||||||
@ -448,7 +406,6 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
dataItemAdded(series, j, series.getData().get(j));
|
dataItemAdded(series, j, series.getData().get(j));
|
||||||
}
|
}
|
||||||
seriesList.add(series);
|
seriesList.add(series);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -457,35 +414,26 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
dataItemRemoved(series.getData().get(j), series);
|
dataItemRemoved(series.getData().get(j), series);
|
||||||
}
|
}
|
||||||
seriesList.remove(series);
|
seriesList.remove(series);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlyDoubleProperty maxVScrollProperty() {
|
ReadOnlyDoubleProperty maxVScrollProperty() {
|
||||||
return maxY.getReadOnlyProperty();
|
return maxY.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
Function<EventClusterNode, Stream<EventBundleNodeBase<?, ?, ?>>> clusterFlattener =
|
/**
|
||||||
new Function<EventClusterNode, Stream<EventBundleNodeBase<?, ?, ?>>>() {
|
* @return all the nodes that pass the given predicate
|
||||||
@Override
|
*/
|
||||||
public Stream<EventBundleNodeBase<?, ?, ?>> apply(EventClusterNode node) {
|
|
||||||
return Stream.concat(
|
|
||||||
Stream.of(node),
|
|
||||||
node.getSubNodes().stream().flatMap(stripeFlattener::apply));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Function<EventStripeNode, Stream<EventBundleNodeBase<?, ?, ?>>> stripeFlattener =
|
|
||||||
new Function<EventStripeNode, Stream<EventBundleNodeBase<?, ?, ?>>>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<EventBundleNodeBase<?, ?, ?>> apply(EventStripeNode node) {
|
|
||||||
return Stream.concat(
|
|
||||||
Stream.of(node),
|
|
||||||
node.getSubNodes().stream().flatMap(clusterFlattener::apply));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Iterable<EventBundleNodeBase<?, ?, ?>> getNodes(Predicate<EventBundleNodeBase<?, ?, ?>> p) {
|
Iterable<EventBundleNodeBase<?, ?, ?>> getNodes(Predicate<EventBundleNodeBase<?, ?, ?>> p) {
|
||||||
|
//use this recursive function to flatten the tree of nodes into an iterable.
|
||||||
|
Function<EventBundleNodeBase<?, ?, ?>, Stream<EventBundleNodeBase<?, ?, ?>>> stripeFlattener =
|
||||||
|
new Function<EventBundleNodeBase<?, ?, ?>, Stream<EventBundleNodeBase<?, ?, ?>>>() {
|
||||||
|
@Override
|
||||||
|
public Stream<EventBundleNodeBase<?, ?, ?>> apply(EventBundleNodeBase<?, ?, ?> node) {
|
||||||
|
return Stream.concat(
|
||||||
|
Stream.of(node),
|
||||||
|
node.getSubNodes().stream().flatMap(this::apply));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return stripeNodeMap.values().stream()
|
return stripeNodeMap.values().stream()
|
||||||
.flatMap(stripeFlattener)
|
.flatMap(stripeFlattener)
|
||||||
@ -510,72 +458,79 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
* layout the nodes in the given list, starting form the given minimum y
|
* layout the nodes in the given list, starting form the given minimum y
|
||||||
* coordinate.
|
* coordinate.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* Layout the nodes representing events via the following algorithm.
|
* Layout the nodes representing events via the following algorithm.
|
||||||
*
|
*
|
||||||
* we start with a list of nodes (each representing an event) - sort the
|
* we start with a list of nodes (each representing an event) - sort the
|
||||||
* list of nodes by span start time of the underlying event - initialize
|
* list of nodes by span start time of the underlying event - initialize
|
||||||
* empty map (maxXatY) from y-position to max used x-value - for each node:
|
* empty map (maxXatY) from y-position to max used x-value - for each node:
|
||||||
* -- autosize the node (based on text label) -- get the event's start and
|
|
||||||
* end positions from the dateaxis -- size the capsule representing event
|
|
||||||
* duration -- starting from the top of the chart: --- (1)check if maxXatY
|
|
||||||
* is to the left of the start position: -------if maxXatY less than start
|
|
||||||
* position , good, put the current node here, mark end position as maxXatY,
|
|
||||||
* go to next node -------if maxXatY greater than start position, increment
|
|
||||||
* y position, do -------------check(1) again until maxXatY less than start
|
|
||||||
* position
|
|
||||||
*
|
*
|
||||||
* @param nodes
|
* -- size the node based on its children (recursively)
|
||||||
* @param minY
|
*
|
||||||
|
* -- get the event's start position from the dateaxis
|
||||||
|
*
|
||||||
|
* -- to position node (1)check if maxXatY is to the left of the left x
|
||||||
|
* coord: if maxXatY is less than the left x coord, good, put the current
|
||||||
|
* node here, mark right x coord as maxXatY, go to next node ; if maxXatY
|
||||||
|
* greater than start position, increment y position, do check(1) again
|
||||||
|
* until maxXatY less than start position
|
||||||
|
*
|
||||||
|
* @param nodes collection of nodes to layout
|
||||||
|
* @param minY the minimum y coordinate to position the nodes at.
|
||||||
*/
|
*/
|
||||||
synchronized double layoutEventBundleNodes(final Collection<? extends EventBundleNodeBase<?, ?, ?>> nodes, final double minY) {
|
synchronized double layoutEventBundleNodes(final Collection<? extends EventBundleNodeBase<?, ?, ?>> nodes, final double minY) {
|
||||||
/*
|
// map from y value (ranges) to right most occupied x value.
|
||||||
* map from y value (ranges) to right most occupied x value.
|
|
||||||
*/
|
|
||||||
TreeRangeMap<Double, Double> treeRangeMap = TreeRangeMap.create();
|
TreeRangeMap<Double, Double> treeRangeMap = TreeRangeMap.create();
|
||||||
|
// maximum y values occupied by any of the given nodes, updated as nodes are layed out.
|
||||||
double localMax = minY;
|
double localMax = minY;
|
||||||
//for each node size it and position it in first available slot
|
|
||||||
|
//for each node do a recursive layout to size it and then position it in first available slot
|
||||||
for (final EventBundleNodeBase<?, ?, ?> bundleNode : nodes) {
|
for (final EventBundleNodeBase<?, ?, ?> bundleNode : nodes) {
|
||||||
|
//is the node hiden by a quick hide filter?
|
||||||
boolean quickHide = getController().getQuickHideFilters().stream()
|
boolean quickHide = getController().getQuickHideFilters().stream()
|
||||||
.filter(AbstractFilter::isActive)
|
.filter(AbstractFilter::isActive)
|
||||||
.anyMatch(filter -> filter.getDescription().equals(bundleNode.getDescription()));
|
.anyMatch(filter -> filter.getDescription().equals(bundleNode.getDescription()));
|
||||||
if (quickHide) {
|
if (quickHide) {
|
||||||
|
//hide it and skip layout
|
||||||
bundleNode.setVisible(false);
|
bundleNode.setVisible(false);
|
||||||
bundleNode.setManaged(false);
|
bundleNode.setManaged(false);
|
||||||
} else {
|
} else {
|
||||||
|
//make sure it is shown
|
||||||
bundleNode.setVisible(true);
|
bundleNode.setVisible(true);
|
||||||
bundleNode.setManaged(true);
|
bundleNode.setManaged(true);
|
||||||
|
//apply advanced layout description visibility options
|
||||||
bundleNode.setDescriptionVisibility(descrVisibility.get());
|
bundleNode.setDescriptionVisibility(descrVisibility.get());
|
||||||
bundleNode.setDescriptionWidth(truncateAll.get()
|
bundleNode.setDescriptionWidth(truncateAll.get() ? truncateWidth.get() : USE_PREF_SIZE);
|
||||||
? truncateWidth.get()
|
|
||||||
: USE_PREF_SIZE);
|
//do recursive layout
|
||||||
bundleNode.layout();
|
bundleNode.layout();
|
||||||
|
//get computed height and width
|
||||||
double h = bundleNode.getBoundsInLocal().getHeight();
|
double h = bundleNode.getBoundsInLocal().getHeight();
|
||||||
double w = bundleNode.getBoundsInLocal().getWidth();
|
double w = bundleNode.getBoundsInLocal().getWidth();
|
||||||
double xLeft = getXAxis().getDisplayPosition(new DateTime(bundleNode.getStartMillis())) - bundleNode.getLayoutXCompensation();
|
//get left and right x coords from axis plus computed width
|
||||||
|
double xLeft = getXForEpochMillis(bundleNode.getStartMillis()) - bundleNode.getLayoutXCompensation();
|
||||||
double xRight = xLeft + w;
|
double xRight = xLeft + w;
|
||||||
|
|
||||||
//initial test position
|
//initial test position
|
||||||
double yTop = minY;
|
double yTop = minY;
|
||||||
double yBottom = yTop + h;
|
double yBottom = yTop + h;
|
||||||
|
|
||||||
if (oneEventPerRow.get()) {
|
if (oneEventPerRow.get()) {
|
||||||
// if onePerRow, just put it at end
|
// if onePerRow, just put it at end
|
||||||
yTop = (localMax + MINIMUM_GAP);
|
yTop = (localMax + MINIMUM_EVENT_NODE_GAP);
|
||||||
yBottom = yTop + h;
|
yBottom = yTop + h;
|
||||||
} else {
|
} else {
|
||||||
|
//until the node is not overlapping any others try moving it down.
|
||||||
boolean overlapping = true;
|
boolean overlapping = true;
|
||||||
while (overlapping) {
|
while (overlapping) {
|
||||||
//loop through y values looking for available slot.
|
|
||||||
overlapping = false;
|
overlapping = false;
|
||||||
//check each pixel from bottom to top.
|
//check each pixel from bottom to top.
|
||||||
for (double y = yBottom; y >= yTop; y--) {
|
for (double y = yBottom; y >= yTop; y--) {
|
||||||
final Double maxX = treeRangeMap.get(y);
|
final Double maxX = treeRangeMap.get(y);
|
||||||
if (maxX != null && maxX >= xLeft - MINIMUM_GAP) {
|
if (maxX != null && maxX >= xLeft - MINIMUM_EVENT_NODE_GAP) {
|
||||||
//if that pixel is already used
|
//if that pixel is already used
|
||||||
//jump top to this y value and repeat until free slot is found.
|
//jump top to this y value and repeat until free slot is found.
|
||||||
overlapping = true;
|
overlapping = true;
|
||||||
yTop = y + MINIMUM_GAP;
|
yTop = y + MINIMUM_EVENT_NODE_GAP;
|
||||||
yBottom = yTop + h;
|
yBottom = yTop + h;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -583,22 +538,25 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
}
|
}
|
||||||
treeRangeMap.put(Range.closed(yTop, yBottom), xRight);
|
treeRangeMap.put(Range.closed(yTop, yBottom), xRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
localMax = Math.max(yBottom, localMax);
|
localMax = Math.max(yBottom, localMax);
|
||||||
|
|
||||||
|
//animate node to new position
|
||||||
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(100),
|
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(100),
|
||||||
new KeyValue(bundleNode.layoutXProperty(), xLeft),
|
new KeyValue(bundleNode.layoutXProperty(), xLeft),
|
||||||
new KeyValue(bundleNode.layoutYProperty(), yTop)));
|
new KeyValue(bundleNode.layoutYProperty(), yTop)));
|
||||||
timeline.setOnFinished(new EventHandler<ActionEvent>() {
|
timeline.setOnFinished((ActionEvent event) -> {
|
||||||
@Override
|
requestChartLayout();
|
||||||
public void handle(ActionEvent event) {
|
|
||||||
requestChartLayout();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
timeline.play();
|
timeline.play();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return localMax - minY;
|
return localMax; //return new max
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getXForEpochMillis(Long millis) {
|
||||||
|
DateTime dateTime = new DateTime(millis, TimeLineController.getJodaTimeZone());
|
||||||
|
return getXAxis().getDisplayPosition(new DateTime(dateTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void layoutProjectionMap() {
|
private void layoutProjectionMap() {
|
||||||
@ -626,10 +584,6 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
return filteredEvents;
|
return filteredEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
Property<Boolean> alternateLayoutProperty() {
|
|
||||||
return alternateLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
static private class DetailIntervalSelector extends IntervalSelector<DateTime> {
|
static private class DetailIntervalSelector extends IntervalSelector<DateTime> {
|
||||||
|
|
||||||
DetailIntervalSelector(double x, double height, Axis<DateTime> axis, TimeLineController controller) {
|
DetailIntervalSelector(double x, double height, Axis<DateTime> axis, TimeLineController controller) {
|
||||||
@ -652,26 +606,6 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class LayoutResult {
|
|
||||||
|
|
||||||
private final double height;
|
|
||||||
private final Set<KeyValue> keys;
|
|
||||||
|
|
||||||
LayoutResult(double height, Set<KeyValue> keys) {
|
|
||||||
this.height = height;
|
|
||||||
this.keys = keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getHeight() {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<KeyValue> getKeys() {
|
|
||||||
return Collections.unmodifiableSet(keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PlaceMarkerAction extends Action {
|
private class PlaceMarkerAction extends Action {
|
||||||
|
|
||||||
PlaceMarkerAction(MouseEvent clickEvent) {
|
PlaceMarkerAction(MouseEvent clickEvent) {
|
||||||
@ -697,6 +631,45 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SelectionChangeHandler implements ListChangeListener<EventBundleNodeBase<?, ?, ?>> {
|
||||||
|
|
||||||
|
private final Axis<DateTime> dateAxis;
|
||||||
|
|
||||||
|
SelectionChangeHandler() {
|
||||||
|
dateAxis = getXAxis();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(ListChangeListener.Change<? extends EventBundleNodeBase<?, ?, ?>> change) {
|
||||||
|
while (change.next()) {
|
||||||
|
change.getRemoved().forEach((EventBundleNodeBase<?, ?, ?> removedNode) -> {
|
||||||
|
removedNode.getEventBundle().getClusters().forEach(cluster -> {
|
||||||
|
Line removedLine = projectionMap.remove(cluster);
|
||||||
|
getChartChildren().removeAll(removedLine);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
change.getAddedSubList().forEach((EventBundleNodeBase<?, ?, ?> addedNode) -> {
|
||||||
|
|
||||||
|
for (EventCluster range : addedNode.getEventBundle().getClusters()) {
|
||||||
|
|
||||||
|
Line line = new Line(dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(range.getStartMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET,
|
||||||
|
dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(range.getEndMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET
|
||||||
|
);
|
||||||
|
line.setStroke(addedNode.getEventType().getColor().deriveColor(0, 1, 1, .5));
|
||||||
|
line.setStrokeWidth(PROJECTED_LINE_STROKE_WIDTH);
|
||||||
|
line.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
projectionMap.put(range, line);
|
||||||
|
getChartChildren().add(line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
EventDetailChart.this.controller.selectEventIDs(selectedNodes.stream()
|
||||||
|
.flatMap(detailNode -> detailNode.getEventIDs().stream())
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class HideDescriptionAction extends Action {
|
class HideDescriptionAction extends Action {
|
||||||
|
|
||||||
HideDescriptionAction(String description, DescriptionLoD descriptionLoD) {
|
HideDescriptionAction(String description, DescriptionLoD descriptionLoD) {
|
||||||
@ -711,12 +684,13 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
DescriptionFilter descriptionFilter = getController().getQuickHideFilters().stream()
|
DescriptionFilter descriptionFilter = getController().getQuickHideFilters().stream()
|
||||||
.filter(testFilter::equals)
|
.filter(testFilter::equals)
|
||||||
.findFirst().orElseGet(() -> {
|
.findFirst().orElseGet(() -> {
|
||||||
testFilter.selectedProperty().addListener(layoutInvalidationListener);
|
testFilter.selectedProperty().addListener((Observable observable) -> {
|
||||||
|
layoutPlotChildren();
|
||||||
|
});
|
||||||
getController().getQuickHideFilters().add(testFilter);
|
getController().getQuickHideFilters().add(testFilter);
|
||||||
return testFilter;
|
return testFilter;
|
||||||
});
|
});
|
||||||
descriptionFilter.setSelected(true);
|
descriptionFilter.setSelected(true);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -724,7 +698,6 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
|||||||
class UnhideDescriptionAction extends Action {
|
class UnhideDescriptionAction extends Action {
|
||||||
|
|
||||||
UnhideDescriptionAction(String description, DescriptionLoD descriptionLoD) {
|
UnhideDescriptionAction(String description, DescriptionLoD descriptionLoD) {
|
||||||
|
|
||||||
super("Unhide");
|
super("Unhide");
|
||||||
setGraphic(new ImageView(SHOW));
|
setGraphic(new ImageView(SHOW));
|
||||||
setEventHandler((ActionEvent t) ->
|
setEventHandler((ActionEvent t) ->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user