mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 10:17:41 +00:00
extend DetailViewNode interface to support more features of old visualization in new mode.
This commit is contained in:
parent
874703030d
commit
2250b01a56
@ -22,8 +22,9 @@ package org.sleuthkit.autopsy.timeline.ui.detailview;
|
||||
* Level of description shown in UI NOTE: this is a separate concept form
|
||||
* {@link DescriptionLOD}
|
||||
*/
|
||||
enum DescriptionVisibility {
|
||||
|
||||
HIDDEN, COUNT_ONLY, SHOWN;
|
||||
public enum DescriptionVisibility {
|
||||
|
||||
HIDDEN,
|
||||
COUNT_ONLY,
|
||||
SHOWN;
|
||||
}
|
||||
|
@ -7,22 +7,21 @@ package org.sleuthkit.autopsy.timeline.ui.detailview;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javafx.scene.layout.Pane;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.EventBundle;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface DetailViewNode {
|
||||
public interface DetailViewNode<S extends DetailViewNode<S>> {
|
||||
|
||||
long getStartMillis();
|
||||
public long getStartMillis();
|
||||
|
||||
long getEndMillis();
|
||||
public long getEndMillis();
|
||||
|
||||
public void setDescriptionVisibility(DescriptionVisibility get);
|
||||
|
||||
public Pane getSubNodePane();
|
||||
public List<S> getSubNodes();
|
||||
|
||||
public void setSpanWidths(List<Double> spanWidths);
|
||||
|
||||
@ -32,10 +31,16 @@ public interface DetailViewNode {
|
||||
|
||||
public Set<Long> getEventIDs();
|
||||
|
||||
public void applySelectionEffect(boolean applied);
|
||||
|
||||
public String getDescription();
|
||||
|
||||
public EventBundle getBundleDescriptor();
|
||||
|
||||
/**
|
||||
* apply the 'effect' to visually indicate highlighted nodes
|
||||
*
|
||||
* @param applied true to apply the highlight 'effect', false to remove it
|
||||
*/
|
||||
void applyHighlightEffect(boolean applied);
|
||||
|
||||
public void applySelectionEffect(boolean applied);
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.scene.Cursor;
|
||||
@ -101,7 +100,7 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
|
||||
* TODO: refactor common code out of this class and CountsChartPane into
|
||||
* {@link AbstractVisualization}
|
||||
*/
|
||||
public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster, DetailViewNode, EventDetailChart> {
|
||||
public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster, DetailViewNode<?>, EventDetailChart> {
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(CountsViewPane.class.getName());
|
||||
|
||||
@ -121,7 +120,7 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
||||
|
||||
private final ObservableList<EventCluster> aggregatedEvents = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
|
||||
|
||||
private final ObservableList<EventClusterNode> highlightedNodes = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
|
||||
private final ObservableList<DetailViewNode<?>> highlightedNodes = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
|
||||
|
||||
public ObservableList<EventCluster> getAggregatedEvents() {
|
||||
return aggregatedEvents;
|
||||
@ -150,7 +149,7 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
||||
vertScrollBar.visibleAmountProperty().bind(chart.heightProperty().multiply(100).divide(chart.getMaxVScroll()));
|
||||
requestLayout();
|
||||
|
||||
highlightedNodes.addListener((ListChangeListener.Change<? extends EventClusterNode> change) -> {
|
||||
highlightedNodes.addListener((ListChangeListener.Change<? extends DetailViewNode<?>> change) -> {
|
||||
while (change.next()) {
|
||||
change.getAddedSubList().forEach(aeNode -> {
|
||||
aeNode.applyHighlightEffect(true);
|
||||
@ -167,7 +166,7 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
||||
|
||||
//These scroll related handlers don't affect any other view or the model, so they are handled internally
|
||||
//mouse wheel scroll handler
|
||||
this.onScrollProperty().set((EventHandler<ScrollEvent>) (ScrollEvent t) -> {
|
||||
this.onScrollProperty().set((ScrollEvent t) -> {
|
||||
vertScrollBar.valueProperty().set(Math.max(0, Math.min(100, vertScrollBar.getValue() - t.getDeltaY() / 200.0)));
|
||||
});
|
||||
|
||||
@ -213,8 +212,8 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
||||
selectedNodes.addListener((Observable observable) -> {
|
||||
highlightedNodes.clear();
|
||||
selectedNodes.stream().forEach((tn) -> {
|
||||
for (EventClusterNode n : chart.getNodes((EventClusterNode t)
|
||||
-> t.getEvent().getDescription().equals(tn.getDescription()))) {
|
||||
for (DetailViewNode<?> n : chart.getNodes((DetailViewNode<?> t)
|
||||
-> t.getDescription().equals(tn.getDescription()))) {
|
||||
highlightedNodes.add(n);
|
||||
}
|
||||
});
|
||||
@ -237,8 +236,8 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
||||
treeSelectionModel.getSelectedItems().addListener((Observable observable) -> {
|
||||
highlightedNodes.clear();
|
||||
for (TreeItem<NavTreeNode> tn : treeSelectionModel.getSelectedItems()) {
|
||||
for (EventClusterNode n : chart.getNodes((EventClusterNode t)
|
||||
-> t.getEvent().getDescription().equals(tn.getValue().getDescription()))) {
|
||||
for (DetailViewNode<?> n : chart.getNodes((DetailViewNode<?> t)
|
||||
-> t.getDescription().equals(tn.getValue().getDescription()))) {
|
||||
highlightedNodes.add(n);
|
||||
}
|
||||
}
|
||||
@ -358,7 +357,7 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applySelectionEffect(DetailViewNode c1, Boolean applied) {
|
||||
protected void applySelectionEffect(DetailViewNode<?> c1, Boolean applied) {
|
||||
c1.applySelectionEffect(applied);
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
/**
|
||||
* Represents an {@link EventCluster} in a {@link EventDetailChart}.
|
||||
*/
|
||||
public class EventClusterNode extends StackPane implements DetailViewNode {
|
||||
public class EventClusterNode extends StackPane implements DetailViewNode<EventClusterNode> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(EventClusterNode.class.getName());
|
||||
|
||||
@ -148,18 +148,23 @@ public class EventClusterNode extends StackPane implements DetailViewNode {
|
||||
|
||||
private final Button plusButton = new Button(null, new ImageView(PLUS)) {
|
||||
{
|
||||
setMinSize(16, 16);
|
||||
setMaxSize(16, 16);
|
||||
setPrefSize(16, 16);
|
||||
configureLODButton(this);
|
||||
}
|
||||
};
|
||||
private final Button minusButton = new Button(null, new ImageView(MINUS)) {
|
||||
{
|
||||
setMinSize(16, 16);
|
||||
setMaxSize(16, 16);
|
||||
setPrefSize(16, 16);
|
||||
configureLODButton(this);
|
||||
}
|
||||
};
|
||||
|
||||
private static void configureLODButton(Button b) {
|
||||
b.setMinSize(16, 16);
|
||||
b.setMaxSize(16, 16);
|
||||
b.setPrefSize(16, 16);
|
||||
b.setVisible(false);
|
||||
b.setManaged(false);
|
||||
}
|
||||
|
||||
private final EventDetailChart chart;
|
||||
|
||||
private SimpleObjectProperty<DescriptionLOD> descLOD = new SimpleObjectProperty<>();
|
||||
@ -196,10 +201,6 @@ public class EventClusterNode extends StackPane implements DetailViewNode {
|
||||
hBox.setPadding(new Insets(2, 5, 2, 5));
|
||||
hBox.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
minusButton.setVisible(false);
|
||||
plusButton.setVisible(false);
|
||||
minusButton.setManaged(false);
|
||||
plusButton.setManaged(false);
|
||||
final BorderPane borderPane = new BorderPane(subNodePane, hBox, null, null, null);
|
||||
BorderPane.setAlignment(subNodePane, Pos.TOP_LEFT);
|
||||
borderPane.setPrefWidth(USE_COMPUTED_SIZE);
|
||||
@ -322,8 +323,10 @@ public class EventClusterNode extends StackPane implements DetailViewNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pane getSubNodePane() {
|
||||
return subNodePane;
|
||||
public List<EventClusterNode> getSubNodes() {
|
||||
return subNodePane.getChildrenUnmodifiable().stream()
|
||||
.map(EventClusterNode.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
synchronized public EventCluster getEvent() {
|
||||
@ -413,7 +416,7 @@ public class EventClusterNode extends StackPane implements DetailViewNode {
|
||||
*
|
||||
* @param applied true to apply the highlight 'effect', false to remove it
|
||||
*/
|
||||
synchronized void applyHighlightEffect(boolean applied) {
|
||||
public synchronized void applyHighlightEffect(boolean applied) {
|
||||
|
||||
if (applied) {
|
||||
descrLabel.setStyle("-fx-font-weight: bold;"); // NON-NLS
|
||||
@ -457,7 +460,7 @@ public class EventClusterNode extends StackPane implements DetailViewNode {
|
||||
* @param newDescriptionLOD
|
||||
*/
|
||||
synchronized private void loadSubClusters(DescriptionLOD newDescriptionLOD) {
|
||||
getSubNodePane().getChildren().clear();
|
||||
subNodePane.getChildren().clear();
|
||||
if (newDescriptionLOD == aggEvent.getDescriptionLOD()) {
|
||||
chart.setRequiresLayout(true);
|
||||
chart.requestChartLayout();
|
||||
@ -494,7 +497,7 @@ public class EventClusterNode extends StackPane implements DetailViewNode {
|
||||
try {
|
||||
chart.setCursor(Cursor.WAIT);
|
||||
//assign subNodes and request chart layout
|
||||
getSubNodePane().getChildren().setAll(get());
|
||||
subNodePane.getChildren().setAll(get());
|
||||
setDescriptionVisibility(descrVis);
|
||||
chart.setRequiresLayout(true);
|
||||
chart.requestChartLayout();
|
||||
|
@ -31,6 +31,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
@ -167,7 +168,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
*/
|
||||
private final SimpleBooleanProperty oneEventPerRow = new SimpleBooleanProperty(false);
|
||||
|
||||
private final ObservableMap<DetailViewNode, Line> projectionMap = FXCollections.observableHashMap();
|
||||
private final ObservableMap<DetailViewNode<?>, Line> projectionMap = FXCollections.observableHashMap();
|
||||
|
||||
/**
|
||||
* flag indicating whether this chart actually needs a layout pass
|
||||
@ -175,7 +176,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
@GuardedBy(value = "this")
|
||||
private boolean requiresLayout = true;
|
||||
|
||||
final ObservableList<DetailViewNode> selectedNodes;
|
||||
final ObservableList<DetailViewNode<?>> selectedNodes;
|
||||
|
||||
/**
|
||||
* list of series of data added to this chart TODO: replace this with a map
|
||||
@ -205,7 +206,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
private final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0);
|
||||
private final SimpleBooleanProperty alternateLayout = new SimpleBooleanProperty(true);
|
||||
|
||||
EventDetailChart(DateAxis dateAxis, final Axis<EventCluster> verticalAxis, ObservableList<DetailViewNode> selectedNodes) {
|
||||
EventDetailChart(DateAxis dateAxis, final Axis<EventCluster> verticalAxis, ObservableList<DetailViewNode<?>> selectedNodes) {
|
||||
super(dateAxis, verticalAxis);
|
||||
dateAxis.setAutoRanging(false);
|
||||
|
||||
@ -285,7 +286,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
setOnMouseReleased(dragHandler);
|
||||
setOnMouseDragged(dragHandler);
|
||||
|
||||
projectionMap.addListener((MapChangeListener.Change<? extends DetailViewNode, ? extends Line> change) -> {
|
||||
projectionMap.addListener((MapChangeListener.Change<? extends DetailViewNode<?>, ? extends Line> change) -> {
|
||||
final Line valueRemoved = change.getValueRemoved();
|
||||
if (valueRemoved != null) {
|
||||
getChartChildren().removeAll(valueRemoved);
|
||||
@ -298,12 +299,12 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
|
||||
this.selectedNodes = selectedNodes;
|
||||
this.selectedNodes.addListener((
|
||||
ListChangeListener.Change<? extends DetailViewNode> c) -> {
|
||||
ListChangeListener.Change<? extends DetailViewNode<?>> c) -> {
|
||||
while (c.next()) {
|
||||
c.getRemoved().forEach((DetailViewNode t) -> {
|
||||
c.getRemoved().forEach((DetailViewNode<?> t) -> {
|
||||
projectionMap.remove(t);
|
||||
});
|
||||
c.getAddedSubList().forEach((DetailViewNode t) -> {
|
||||
c.getAddedSubList().forEach((DetailViewNode<?> t) -> {
|
||||
Line line = new Line(dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(t.getStartMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET,
|
||||
dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(t.getEndMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET
|
||||
);
|
||||
@ -316,7 +317,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
}
|
||||
|
||||
this.controller.selectEventIDs(selectedNodes.stream()
|
||||
.flatMap((DetailViewNode aggNode) -> aggNode.getEventIDs().stream())
|
||||
.flatMap(detailNode -> detailNode.getEventIDs().stream())
|
||||
.collect(Collectors.toList()));
|
||||
});
|
||||
|
||||
@ -420,7 +421,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
return EventStripe.merge(u, v);
|
||||
}
|
||||
);
|
||||
EventStripeNode clusterNode = new EventStripeNode(eventCluster,null, EventDetailChart.this);
|
||||
EventStripeNode clusterNode = new EventStripeNode(eventCluster, null, EventDetailChart.this);
|
||||
stripeNodeMap.put(eventCluster, clusterNode);
|
||||
nodeGroup.getChildren().add(clusterNode);
|
||||
} else {
|
||||
@ -486,11 +487,11 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
if (bandByType.get() == false) {
|
||||
if (alternateLayout.get() == true) {
|
||||
List<EventStripeNode> nodes = new ArrayList<>(stripeNodeMap.values());
|
||||
Collections.sort(nodes, Comparator.comparing(DetailViewNode::getStartMillis));
|
||||
nodes.sort(Comparator.comparing(DetailViewNode<?>::getStartMillis));
|
||||
layoutNodes(nodes, minY, 0);
|
||||
} else {
|
||||
List<EventClusterNode> nodes = new ArrayList<>(nodeMap.values());
|
||||
Collections.sort(nodes, Comparator.comparing(DetailViewNode::getStartMillis));
|
||||
nodes.sort(Comparator.comparing(DetailViewNode<?>::getStartMillis));
|
||||
layoutNodes(nodes, minY, 0);
|
||||
}
|
||||
|
||||
@ -545,22 +546,28 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
return descrVisibility;
|
||||
}
|
||||
|
||||
synchronized ReadOnlyDoubleProperty
|
||||
getMaxVScroll() {
|
||||
synchronized ReadOnlyDoubleProperty getMaxVScroll() {
|
||||
return maxY.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
Iterable<EventClusterNode> getNodes(Predicate<EventClusterNode> p) {
|
||||
List<EventClusterNode> nodes = new ArrayList<>();
|
||||
Iterable<DetailViewNode<?>> getNodes(Predicate<DetailViewNode<?>> p) {
|
||||
Collection<? extends DetailViewNode<?>> values = alternateLayout.get()
|
||||
? stripeNodeMap.values()
|
||||
: nodeMap.values();
|
||||
|
||||
for (EventClusterNode node : nodeMap.values()) {
|
||||
checkNode(node, p, nodes);
|
||||
}
|
||||
|
||||
return nodes;
|
||||
//collapse tree of DetailViewNoeds to list and then filter on given predicate
|
||||
return values.stream()
|
||||
.flatMap(EventDetailChart::flatten)
|
||||
.filter(p).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
Iterable<EventClusterNode> getAllNodes() {
|
||||
public static Stream<? extends DetailViewNode<?>> flatten(DetailViewNode<?> node) {
|
||||
return Stream.concat(
|
||||
Stream.of(node),
|
||||
node.getSubNodes().stream().flatMap(EventDetailChart::flatten));
|
||||
}
|
||||
|
||||
Iterable<DetailViewNode<?>> getAllNodes() {
|
||||
return getNodes(x -> true);
|
||||
}
|
||||
|
||||
@ -576,17 +583,6 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
nodeGroup.setTranslateY(-d * h);
|
||||
}
|
||||
|
||||
private static void checkNode(EventClusterNode node, Predicate<EventClusterNode> p, List<EventClusterNode> nodes) {
|
||||
if (node != null) {
|
||||
if (p.test(node)) {
|
||||
nodes.add(node);
|
||||
}
|
||||
for (Node n : node.getSubNodePane().getChildrenUnmodifiable()) {
|
||||
checkNode((EventClusterNode) n, p, nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearGuideLine() {
|
||||
getChartChildren().remove(guideLine);
|
||||
guideLine = null;
|
||||
@ -599,12 +595,12 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
* @param nodes
|
||||
* @param minY
|
||||
*/
|
||||
private synchronized <D extends Region & DetailViewNode> double layoutNodes(final Collection<D> nodes, final double minY, final double xOffset) {
|
||||
private synchronized <DVRegion extends Region & DetailViewNode<DVRegion>> double layoutNodes(final Collection<DVRegion> nodes, final double minY, final double xOffset) {
|
||||
//hash map from y value to right most occupied x value. This tells you for a given 'row' what is the first avaialable slot
|
||||
Map<Integer, Double> maxXatY = new HashMap<>();
|
||||
double localMax = minY;
|
||||
//for each node lay size it and position it in first available slot
|
||||
for (D n : nodes) {
|
||||
for (DVRegion n : nodes) {
|
||||
n.setDescriptionVisibility(descrVisibility.get());
|
||||
double rawDisplayPosition = getXAxis().getDisplayPosition(new DateTime(n.getStartMillis()));
|
||||
|
||||
@ -613,27 +609,19 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
double layoutNodesResultHeight = 0;
|
||||
|
||||
double span = 0;
|
||||
List<DVRegion> subNodes = n.getSubNodes();
|
||||
if (subNodes.isEmpty() == false) {
|
||||
subNodes.sort(Comparator.comparing((DVRegion t) -> t.getStartMillis()));
|
||||
layoutNodesResultHeight = layoutNodes(subNodes, 0, rawDisplayPosition);
|
||||
}
|
||||
|
||||
if (n instanceof EventClusterNode) {
|
||||
if (n.getSubNodePane().getChildren().isEmpty() == false) {
|
||||
List<EventClusterNode> children = n.getSubNodePane().getChildren().stream()
|
||||
.map(EventClusterNode.class::cast)
|
||||
.sorted(Comparator.comparing(DetailViewNode::getStartMillis))
|
||||
.collect(Collectors.toList());
|
||||
layoutNodesResultHeight = layoutNodes(children, 0, rawDisplayPosition);
|
||||
}
|
||||
double endX = getXAxis().getDisplayPosition(new DateTime(n.getEndMillis())) - xOffset;
|
||||
span = endX - startX;
|
||||
|
||||
//size timespan border
|
||||
n.setSpanWidths(Arrays.asList(span));
|
||||
} else {
|
||||
if (n.getSubNodePane().getChildren().isEmpty() == false) {
|
||||
List<EventStripeNode> children = n.getSubNodePane().getChildren().stream()
|
||||
.map(EventStripeNode.class::cast)
|
||||
.sorted(Comparator.comparing(DetailViewNode::getStartMillis))
|
||||
.collect(Collectors.toList());
|
||||
layoutNodesResultHeight = layoutNodes(children, 0, rawDisplayPosition);
|
||||
}
|
||||
|
||||
EventStripeNode cn = (EventStripeNode) n;
|
||||
List<Double> spanWidths = new ArrayList<>();
|
||||
double x = getXAxis().getDisplayPosition(new DateTime(cn.getStartMillis()));;
|
||||
@ -720,8 +708,8 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
private static final int DEFAULT_ROW_HEIGHT = 24;
|
||||
|
||||
private void layoutProjectionMap() {
|
||||
for (final Map.Entry<DetailViewNode, Line> entry : projectionMap.entrySet()) {
|
||||
final DetailViewNode aggNode = entry.getKey();
|
||||
for (final Map.Entry<DetailViewNode<?>, Line> entry : projectionMap.entrySet()) {
|
||||
final DetailViewNode<?> aggNode = entry.getKey();
|
||||
final Line line = entry.getValue();
|
||||
|
||||
line.setStartX(getParentXForValue(new DateTime(aggNode.getStartMillis(), TimeLineController.getJodaTimeZone())));
|
||||
@ -760,7 +748,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
return alternateLayout;
|
||||
}
|
||||
|
||||
private static class StartTimeComparator<T extends Node & DetailViewNode> implements Comparator<T> {
|
||||
private static class StartTimeComparator<T extends Node & DetailViewNode<?>> implements Comparator<T> {
|
||||
|
||||
@Override
|
||||
public int compare(T n1, T n2) {
|
||||
|
@ -18,6 +18,7 @@ import javafx.event.EventHandler;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.OverrunStyle;
|
||||
@ -29,7 +30,6 @@ import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.Background;
|
||||
import javafx.scene.layout.BackgroundFill;
|
||||
import javafx.scene.layout.Border;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.BorderStroke;
|
||||
import javafx.scene.layout.BorderStrokeStyle;
|
||||
import javafx.scene.layout.BorderWidths;
|
||||
@ -41,6 +41,7 @@ import javafx.scene.layout.Region;
|
||||
import static javafx.scene.layout.Region.USE_COMPUTED_SIZE;
|
||||
import static javafx.scene.layout.Region.USE_PREF_SIZE;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
@ -64,7 +65,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class EventStripeNode extends StackPane implements DetailViewNode {
|
||||
public class EventStripeNode extends StackPane implements DetailViewNode<EventStripeNode> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(EventClusterNode.class.getName());
|
||||
|
||||
@ -94,20 +95,28 @@ public class EventStripeNode extends StackPane implements DetailViewNode {
|
||||
private final ImageView tagIV = new ImageView(TAG);
|
||||
private final Button plusButton = new Button(null, new ImageView(PLUS)) {
|
||||
{
|
||||
setMinSize(16, 16);
|
||||
setMaxSize(16, 16);
|
||||
setPrefSize(16, 16);
|
||||
configureLODButton(this);
|
||||
}
|
||||
};
|
||||
private final Button minusButton = new Button(null, new ImageView(MINUS)) {
|
||||
{
|
||||
setMinSize(16, 16);
|
||||
setMaxSize(16, 16);
|
||||
setPrefSize(16, 16);
|
||||
configureLODButton(this);
|
||||
}
|
||||
};
|
||||
|
||||
private static void configureLODButton(Button b) {
|
||||
b.setMinSize(16, 16);
|
||||
b.setMaxSize(16, 16);
|
||||
b.setPrefSize(16, 16);
|
||||
show(b, false);
|
||||
}
|
||||
|
||||
private static void show(Node b, boolean show) {
|
||||
b.setVisible(show);
|
||||
b.setManaged(show);
|
||||
}
|
||||
private DescriptionVisibility descrVis;
|
||||
private final HBox spanRegion = new HBox();
|
||||
private final HBox spansHBox = new HBox();
|
||||
/**
|
||||
* The IamgeView used to show the icon for this node's event's type
|
||||
*/
|
||||
@ -127,43 +136,36 @@ public class EventStripeNode extends StackPane implements DetailViewNode {
|
||||
final Region spacer = new Region();
|
||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||
|
||||
final HBox hBox = new HBox(descrLabel, countLabel, spacer, hashIV, tagIV, minusButton, plusButton);
|
||||
final HBox header = new HBox(descrLabel, countLabel, hashIV, tagIV, spacer, minusButton, plusButton);
|
||||
if (cluster.getEventIDsWithHashHits().isEmpty()) {
|
||||
hashIV.setManaged(false);
|
||||
hashIV.setVisible(false);
|
||||
show(hashIV, false);
|
||||
}
|
||||
if (cluster.getEventIDsWithTags().isEmpty()) {
|
||||
tagIV.setManaged(false);
|
||||
tagIV.setVisible(false);
|
||||
show(tagIV, false);
|
||||
}
|
||||
hBox.setPrefWidth(USE_COMPUTED_SIZE);
|
||||
hBox.setMinWidth(USE_PREF_SIZE);
|
||||
hBox.setPadding(new Insets(2, 5, 2, 5));
|
||||
hBox.setAlignment(Pos.CENTER_LEFT);
|
||||
header.setMinWidth(USE_PREF_SIZE);
|
||||
header.setPadding(new Insets(2, 5, 2, 5));
|
||||
header.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
minusButton.setVisible(false);
|
||||
plusButton.setVisible(false);
|
||||
minusButton.setManaged(false);
|
||||
plusButton.setManaged(false);
|
||||
final BorderPane borderPane = new BorderPane(subNodePane, hBox, null, null, null);
|
||||
BorderPane.setAlignment(subNodePane, Pos.TOP_LEFT);
|
||||
borderPane.setPrefWidth(USE_COMPUTED_SIZE);
|
||||
final VBox internalVBox = new VBox(header, subNodePane);
|
||||
internalVBox.setAlignment(Pos.CENTER_LEFT);
|
||||
final Color evtColor = cluster.getType().getColor();
|
||||
|
||||
spanFill = new Background(new BackgroundFill(evtColor.deriveColor(0, 1, 1, .2), CORNER_RADII, Insets.EMPTY));
|
||||
for (Range<Long> r : cluster.getRanges()) {
|
||||
Region region = new Region();
|
||||
region.setStyle("-fx-border-width:2 1 2 1; -fx-border-radius: 1; -fx-border-color: " + ColorUtilities.getRGBCode(evtColor.deriveColor(0, 1, 1, .3)) + ";"); // NON-NLS
|
||||
region.setBackground(spanFill);
|
||||
spanRegion.getChildren().addAll(region, new Region());
|
||||
Region spanRegion = new Region();
|
||||
spanRegion.setStyle("-fx-border-width:2 1 2 1; -fx-border-radius: 1; -fx-border-color: " + ColorUtilities.getRGBCode(evtColor.deriveColor(0, 1, 1, .3)) + ";"); // NON-NLS
|
||||
spanRegion.setBackground(spanFill);
|
||||
spansHBox.getChildren().addAll(spanRegion, new Region());
|
||||
}
|
||||
spanRegion.getChildren().remove(spanRegion.getChildren().size() - 1);
|
||||
|
||||
getChildren().addAll(spanRegion, borderPane);
|
||||
setBackground(new Background(new BackgroundFill(evtColor.deriveColor(0, 1, 1, .1), CORNER_RADII, Insets.EMPTY)));
|
||||
spansHBox.getChildren().remove(spansHBox.getChildren().size() - 1);
|
||||
spansHBox.setMaxWidth(USE_PREF_SIZE);
|
||||
setMaxWidth(USE_PREF_SIZE);
|
||||
getChildren().addAll(spansHBox, internalVBox);
|
||||
setBackground(new Background(new BackgroundFill(evtColor.deriveColor(0, 1, 1, .05), CORNER_RADII, Insets.EMPTY)));
|
||||
setAlignment(Pos.TOP_LEFT);
|
||||
setMinHeight(24);
|
||||
minWidthProperty().bind(spanRegion.widthProperty());
|
||||
minWidthProperty().bind(spansHBox.widthProperty());
|
||||
setPrefHeight(USE_COMPUTED_SIZE);
|
||||
setMaxHeight(USE_PREF_SIZE);
|
||||
|
||||
@ -189,20 +191,20 @@ public class EventStripeNode extends StackPane implements DetailViewNode {
|
||||
setOnMouseEntered((MouseEvent e) -> {
|
||||
//defer tooltip creation till needed, this had a surprisingly large impact on speed of loading the chart
|
||||
// installTooltip();
|
||||
spanRegion.setEffect(new DropShadow(10, evtColor));
|
||||
minusButton.setVisible(true);
|
||||
plusButton.setVisible(true);
|
||||
minusButton.setManaged(true);
|
||||
plusButton.setManaged(true);
|
||||
spansHBox.setEffect(new DropShadow(10, evtColor));
|
||||
show(spacer, true);
|
||||
show(minusButton, true);
|
||||
show(plusButton, true);
|
||||
|
||||
toFront();
|
||||
});
|
||||
|
||||
setOnMouseExited((MouseEvent e) -> {
|
||||
spanRegion.setEffect(null);
|
||||
minusButton.setVisible(false);
|
||||
plusButton.setVisible(false);
|
||||
minusButton.setManaged(false);
|
||||
plusButton.setManaged(false);
|
||||
spansHBox.setEffect(null);
|
||||
show(spacer, false);
|
||||
show(minusButton, false);
|
||||
show(plusButton, false);
|
||||
|
||||
});
|
||||
|
||||
plusButton.disableProperty().bind(descLOD.isEqualTo(DescriptionLOD.FULL));
|
||||
@ -232,11 +234,11 @@ public class EventStripeNode extends StackPane implements DetailViewNode {
|
||||
@Override
|
||||
public void setSpanWidths(List<Double> spanWidths) {
|
||||
for (int i = 0; i < spanWidths.size(); i++) {
|
||||
Region get = (Region) spanRegion.getChildren().get(i);
|
||||
Region spanRegion = (Region) spansHBox.getChildren().get(i);
|
||||
Double w = spanWidths.get(i);
|
||||
get.setPrefWidth(w);
|
||||
get.setMaxWidth(w);
|
||||
get.setMinWidth(Math.max(2, w));
|
||||
spanRegion.setPrefWidth(w);
|
||||
spanRegion.setMaxWidth(w);
|
||||
spanRegion.setMinWidth(Math.max(2, w));
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,8 +282,10 @@ public class EventStripeNode extends StackPane implements DetailViewNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pane getSubNodePane() {
|
||||
return subNodePane;
|
||||
public List<EventStripeNode> getSubNodes() {
|
||||
return subNodePane.getChildrenUnmodifiable().stream()
|
||||
.map(EventStripeNode.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -339,6 +343,26 @@ public class EventStripeNode extends StackPane implements DetailViewNode {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* apply the 'effect' to visually indicate highlighted nodes
|
||||
*
|
||||
* @param applied true to apply the highlight 'effect', false to remove it
|
||||
*/
|
||||
@Override
|
||||
public synchronized void applyHighlightEffect(boolean applied) {
|
||||
if (applied) {
|
||||
descrLabel.setStyle("-fx-font-weight: bold;"); // NON-NLS
|
||||
spanFill = new Background(new BackgroundFill(cluster.getType().getColor().deriveColor(0, 1, 1, .3), CORNER_RADII, Insets.EMPTY));
|
||||
spansHBox.setBackground(spanFill);
|
||||
setBackground(new Background(new BackgroundFill(cluster.getType().getColor().deriveColor(0, 1, 1, .2), CORNER_RADII, Insets.EMPTY)));
|
||||
} else {
|
||||
descrLabel.setStyle("-fx-font-weight: normal;"); // NON-NLS
|
||||
spanFill = new Background(new BackgroundFill(cluster.getType().getColor().deriveColor(0, 1, 1, .1), CORNER_RADII, Insets.EMPTY));
|
||||
spansHBox.setBackground(spanFill);
|
||||
setBackground(new Background(new BackgroundFill(cluster.getType().getColor().deriveColor(0, 1, 1, .1), CORNER_RADII, Insets.EMPTY)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return cluster.getDescription();
|
||||
@ -355,11 +379,14 @@ public class EventStripeNode extends StackPane implements DetailViewNode {
|
||||
* @param newDescriptionLOD
|
||||
*/
|
||||
synchronized private void loadSubClusters(DescriptionLOD newDescriptionLOD) {
|
||||
getSubNodePane().getChildren().clear();
|
||||
subNodePane.getChildren().clear();
|
||||
|
||||
if (newDescriptionLOD == cluster.getDescriptionLOD()) {
|
||||
spansHBox.setVisible(true);
|
||||
chart.setRequiresLayout(true);
|
||||
chart.requestChartLayout();
|
||||
} else {
|
||||
spansHBox.setVisible(false);
|
||||
RootFilter combinedFilter = eventsModel.filterProperty().get().copyOf();
|
||||
//make a new filter intersecting the global filter with text(description) and type filters to restrict sub-clusters
|
||||
combinedFilter.getSubFilters().addAll(new TextFilter(cluster.getDescription()),
|
||||
@ -402,7 +429,7 @@ public class EventStripeNode extends StackPane implements DetailViewNode {
|
||||
try {
|
||||
chart.setCursor(Cursor.WAIT);
|
||||
//assign subNodes and request chart layout
|
||||
getSubNodePane().getChildren().setAll(get());
|
||||
subNodePane.getChildren().setAll(get());
|
||||
setDescriptionVisibility(descrVis);
|
||||
chart.setRequiresLayout(true);
|
||||
chart.requestChartLayout();
|
||||
|
Loading…
x
Reference in New Issue
Block a user