extend DetailViewNode interface to support more features of old visualization in new mode.

This commit is contained in:
jmillman 2015-09-02 15:56:46 -04:00
parent 874703030d
commit 2250b01a56
6 changed files with 163 additions and 140 deletions

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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) {

View File

@ -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();