PnnedEventsChart is better integrated interms of respecting settings and selection

This commit is contained in:
jmillman 2016-02-24 15:28:43 -05:00
parent 38af730c58
commit 4acaed2557
9 changed files with 172 additions and 152 deletions

View File

@ -0,0 +1,91 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.sleuthkit.autopsy.timeline.ui.detailview;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
/**
*
*/
public class DetailViewLayoutSettings {
/**
* true == truncate all the labels to the greater of the size of their
* timespan indicator or the value of truncateWidth. false == don't truncate
* the labels, alow them to extend past the timespan indicator and off the
* edge of the screen
*/
final SimpleBooleanProperty truncateAll = new SimpleBooleanProperty(false);
/**
* the width to truncate all labels to if truncateAll is true. adjustable
* via slider if truncateAll is true
*/
final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0);
/**
* true == layout each event type in its own band, false == mix all the
* events together during layout
*/
private final SimpleBooleanProperty bandByType = new SimpleBooleanProperty(false);
/**
* true == enforce that no two events can share the same 'row', leading to
* sparser but possibly clearer layout. false == put unrelated events in the
* same 'row', creating a denser more compact layout
*/
private final SimpleBooleanProperty oneEventPerRow = new SimpleBooleanProperty(false);
/**
* how much detail of the description to show in the ui
*/
private final SimpleObjectProperty<DescriptionVisibility> descrVisibility =
new SimpleObjectProperty<>(DescriptionVisibility.SHOWN);
public synchronized SimpleBooleanProperty bandByTypeProperty() {
return bandByType;
}
SimpleBooleanProperty oneEventPerRowProperty() {
return oneEventPerRow;
}
SimpleDoubleProperty truncateWidthProperty() {
return truncateWidth;
}
SimpleBooleanProperty truncateAllProperty() {
return truncateAll;
}
SimpleObjectProperty< DescriptionVisibility> descrVisibilityProperty() {
return descrVisibility;
}
synchronized void setBandByType(Boolean t1) {
bandByType.set(t1);
}
boolean getBandByType() {
return bandByType.get();
}
boolean getTruncateAll() {
return truncateAll.get();
}
double getTruncateWidth() {
return truncateWidth.get();
}
boolean getOneEventPerRow() {
return oneEventPerRow.get();
}
DescriptionVisibility getDescrVisibility() {
return descrVisibility.get();
}
}

View File

@ -89,6 +89,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
private final ObservableList<EventNodeBase<?>> highlightedNodes = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
private final ScrollingWrapper<EventStripe, EventDetailsChart> mainView;
private final ScrollingWrapper<TimeLineEvent, PinnedEventsChart> pinnedView;
private final DetailViewLayoutSettings layoutSettings;
public ObservableList<EventStripe> getEventStripes() {
return chart.getEventStripes();
@ -107,10 +108,12 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
public DetailViewPane(TimeLineController controller, Pane partPane, Pane contextPane, Region bottomLeftSpacer) {
super(controller, partPane, contextPane, bottomLeftSpacer);
layoutSettings = new DetailViewLayoutSettings();
//initialize chart;
chart = new EventDetailsChart(controller, detailsChartDateAxis, verticalAxis, selectedNodes);
chart = new EventDetailsChart(controller, detailsChartDateAxis, verticalAxis, selectedNodes, layoutSettings);
mainView = new ScrollingWrapper<>(chart);
PinnedEventsChart pinnedChart = new PinnedEventsChart(controller, pinnedDateAxis, new EventAxis<>());
PinnedEventsChart pinnedChart = new PinnedEventsChart(controller, pinnedDateAxis, new EventAxis<>(), selectedNodes, layoutSettings);
pinnedView = new ScrollingWrapper<>(pinnedChart);
pinnedChart.setMinSize(100, 100);
setChartClickHandler(); //can we push this into chart
@ -270,14 +273,14 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
assert oneEventPerRowBox != null : "fx:id=\"oneEventPerRowBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
assert truncateAllBox != null : "fx:id=\"truncateAllBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
assert truncateWidthSlider != null : "fx:id=\"truncateAllSlider\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
bandByTypeBox.selectedProperty().bindBidirectional(chart.bandByTypeProperty());
truncateAllBox.selectedProperty().bindBidirectional(chart.truncateAllProperty());
oneEventPerRowBox.selectedProperty().bindBidirectional(chart.oneEventPerRowProperty());
bandByTypeBox.selectedProperty().bindBidirectional(layoutSettings.bandByTypeProperty());
truncateAllBox.selectedProperty().bindBidirectional(layoutSettings.truncateAllProperty());
oneEventPerRowBox.selectedProperty().bindBidirectional(layoutSettings.oneEventPerRowProperty());
truncateSliderLabel.disableProperty().bind(truncateAllBox.selectedProperty().not());
truncateSliderLabel.setText(NbBundle.getMessage(DetailViewPane.class, "DetailViewPane.truncateSliderLabel.text"));
final InvalidationListener sliderListener = o -> {
if (truncateWidthSlider.isValueChanging() == false) {
chart.getTruncateWidth().set(truncateWidthSlider.getValue());
layoutSettings.truncateWidthProperty().set(truncateWidthSlider.getValue());
}
};
truncateWidthSlider.valueProperty().addListener(sliderListener);
@ -285,11 +288,11 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
descrVisibility.selectedToggleProperty().addListener((observable, oldToggle, newToggle) -> {
if (newToggle == countsRadio) {
chart.descrVisibilityProperty().set(DescriptionVisibility.COUNT_ONLY);
layoutSettings.descrVisibilityProperty().set(DescriptionVisibility.COUNT_ONLY);
} else if (newToggle == showRadio) {
chart.descrVisibilityProperty().set(DescriptionVisibility.SHOWN);
layoutSettings.descrVisibilityProperty().set(DescriptionVisibility.SHOWN);
} else if (newToggle == hiddenRadio) {
chart.descrVisibilityProperty().set(DescriptionVisibility.HIDDEN);
layoutSettings.descrVisibilityProperty().set(DescriptionVisibility.HIDDEN);
}
});

View File

@ -101,16 +101,10 @@ final public class EventClusterNode extends MultiEventNodeBase<EventCluster, Eve
getChildren().addAll(subNodePane, infoHBox);
if (parentNode == null) {
setDescriptionVisibiltiyImpl(DescriptionVisibility.SHOWN);
}
}
@Override
void applyHighlightEffect(boolean applied) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
void setDescriptionVisibiltiyImpl(DescriptionVisibility descrVis) {
final int size = getEventCluster().getSize();

View File

@ -38,9 +38,6 @@ import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
@ -113,6 +110,7 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
private Set<String> activeQuickHidefilters;
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)//at start of layout pass
private double descriptionWidth;
private final DetailViewLayoutSettings layoutSettings;
@Override
public ContextMenu getChartContextMenu() {
@ -157,40 +155,9 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
private final ObservableList< EventNodeBase<?>> sortedStripeNodes = stripeNodes.sorted(Comparator.comparing(EventNodeBase<?>::getStartMillis));
private final Map<EventCluster, Line> projectionMap = new ConcurrentHashMap<>();
/**
* true == layout each event type in its own band, false == mix all the
* events together during layout
*/
private final SimpleBooleanProperty bandByType = new SimpleBooleanProperty(false);
/**
* true == enforce that no two events can share the same 'row', leading to
* sparser but possibly clearer layout. false == put unrelated events in the
* same 'row', creating a denser more compact layout
*/
private final SimpleBooleanProperty oneEventPerRow = new SimpleBooleanProperty(false);
/**
* how much detail of the description to show in the ui
*/
private final SimpleObjectProperty<DescriptionVisibility> descrVisibility =
new SimpleObjectProperty<>(DescriptionVisibility.SHOWN);
/**
* true == truncate all the labels to the greater of the size of their
* timespan indicator or the value of truncateWidth. false == don't truncate
* the labels, alow them to extend past the timespan indicator and off the
* edge of the screen
*/
final SimpleBooleanProperty truncateAll = new SimpleBooleanProperty(false);
/**
* the width to truncate all labels to if truncateAll is true. adjustable
* via slider if truncateAll is true
*/
final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0);
EventDetailsChart(TimeLineController controller, DateAxis dateAxis, final Axis<EventStripe> verticalAxis, ObservableList<EventNodeBase<?>> selectedNodes) {
EventDetailsChart(TimeLineController controller, DateAxis dateAxis, final Axis<EventStripe> verticalAxis, ObservableList<EventNodeBase<?>> selectedNodes, DetailViewLayoutSettings layoutSettings) {
super(dateAxis, verticalAxis);
this.layoutSettings = layoutSettings;
this.controller = controller;
this.filteredEvents = this.controller.getEventsModel();
@ -224,11 +191,11 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
getPlotChildren().add(nodeGroup);
//add listener for events that should trigger layout
bandByType.addListener(layoutInvalidationListener);
oneEventPerRow.addListener(layoutInvalidationListener);
truncateAll.addListener(layoutInvalidationListener);
truncateWidth.addListener(layoutInvalidationListener);
descrVisibility.addListener(layoutInvalidationListener);
layoutSettings.bandByTypeProperty().addListener(layoutInvalidationListener);
layoutSettings.oneEventPerRowProperty().addListener(layoutInvalidationListener);
layoutSettings.truncateAllProperty().addListener(layoutInvalidationListener);
layoutSettings.truncateAllProperty().addListener(layoutInvalidationListener);
layoutSettings.descrVisibilityProperty().addListener(layoutInvalidationListener);
getController().getQuickHideFilters().addListener(layoutInvalidationListener);
//this is needed to allow non circular binding of the guideline and timerangeRect heights to the height of the chart
@ -289,19 +256,11 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
intervalSelector = null;
}
public synchronized SimpleBooleanProperty bandByTypeProperty() {
return bandByType;
}
@Override
public IntervalSelector<DateTime> newIntervalSelector() {
return new DetailIntervalSelector(this);
}
synchronized void setBandByType(Boolean t1) {
bandByType.set(t1);
}
/**
* get the DateTime along the x-axis that corresponds to the given
* x-coordinate in the coordinate system of this {@link EventDetailsChart}
@ -326,22 +285,6 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
getChartChildren().add(getIntervalSelector());
}
SimpleBooleanProperty oneEventPerRowProperty() {
return oneEventPerRow;
}
SimpleDoubleProperty getTruncateWidth() {
return truncateWidth;
}
SimpleBooleanProperty truncateAllProperty() {
return truncateAll;
}
SimpleObjectProperty< DescriptionVisibility> descrVisibilityProperty() {
return descrVisibility;
}
/**
* @see note in main section of class JavaDoc
*
@ -446,9 +389,9 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
.collect(Collectors.toSet());
//This dosn't change during a layout pass and is expensive to compute per node. So we do it once at the start
descriptionWidth = truncateAll.get() ? truncateWidth.get() : USE_PREF_SIZE;
descriptionWidth = layoutSettings.getTruncateAll() ? layoutSettings.getTruncateWidth() : USE_PREF_SIZE;
if (bandByType.get()) {
if (layoutSettings.getBandByType()) {
sortedStripeNodes.stream()
.collect(Collectors.groupingBy(EventNodeBase<?>::getEventType)).values()
.forEach(inputNodes -> maxY.set(layoutEventBundleNodes(inputNodes, maxY.get())));
@ -547,7 +490,7 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
double xRight = xLeft + w + MINIMUM_EVENT_NODE_GAP;
//initial test position
double yTop = (oneEventPerRow.get())
double yTop = (layoutSettings.getOneEventPerRow())
? (localMax + MINIMUM_EVENT_NODE_GAP)// if onePerRow, just put it at end
: computeYTop(minY, h, maxXatY, xLeft, xRight);
@ -607,19 +550,19 @@ public final class EventDetailsChart extends XYChart<DateTime, EventStripe> impl
*
* Set layout paramaters on the given node and layout its children
*
* @param bundleNode the Node to layout
* @param eventNode the Node to layout
* @param descriptionWdith the maximum width for the description text
*/
private void layoutBundleHelper(final EventNodeBase< ?> bundleNode) {
private void layoutBundleHelper(final EventNodeBase< ?> eventNode) {
//make sure it is shown
bundleNode.setVisible(true);
bundleNode.setManaged(true);
eventNode.setVisible(true);
eventNode.setManaged(true);
//apply advanced layout description visibility options
bundleNode.setDescriptionVisibility(descrVisibility.get());
bundleNode.setMaxDescriptionWidth(descriptionWidth);
eventNode.setDescriptionVisibility(layoutSettings.getDescrVisibility());
eventNode.setMaxDescriptionWidth(descriptionWidth);
//do recursive layout
bundleNode.layoutChildren();
eventNode.layoutChildren();
}
/**

View File

@ -277,7 +277,20 @@ public abstract class EventNodeBase<Type extends TimeLineEvent> extends StackPan
abstract void setDescriptionVisibiltiyImpl(DescriptionVisibility get);
abstract void applyHighlightEffect(boolean b);
/**
* apply the 'effect' to visually indicate highlighted nodes
*
* @param applied true to apply the highlight 'effect', false to remove it
*/
synchronized void applyHighlightEffect(boolean applied) {
if (applied) {
descrLabel.setStyle("-fx-font-weight: bold;"); // NON-NLS
setBackground(highlightedBackground);
} else {
descrLabel.setStyle("-fx-font-weight: normal;"); // NON-NLS
setBackground(defaultBackground);
}
}
void applyHighlightEffect() {
applyHighlightEffect(true);

View File

@ -84,21 +84,7 @@ final public class EventStripeNode extends MultiEventNodeBase<EventStripe, Event
return getEventBundle();
}
/**
* 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
setBackground(highlightedBackground);
} else {
descrLabel.setStyle("-fx-font-weight: normal;"); // NON-NLS
setBackground(defaultBackground);
}
}
@Override
void installActionButtons() {

View File

@ -225,13 +225,6 @@ public abstract class MultiEventNodeBase< BundleType extends MultiEvent<ParentTy
}
}
/**
* apply the 'effect' to visually indicate highlighted nodes
*
* @param applied true to apply the highlight 'effect', false to remove it
*/
abstract void applyHighlightEffect(boolean applied);
@SuppressWarnings("unchecked")
public List<EventNodeBase<?>> getSubNodes() {
return subNodes;

View File

@ -26,7 +26,10 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.collections.FXCollections;
@ -94,6 +97,14 @@ public final class PinnedEventsChart extends XYChart<DateTime, TimeLineEvent> im
* the maximum y value used so far during the most recent layout pass
*/
private final ReadOnlyDoubleWrapper maxY = new ReadOnlyDoubleWrapper(0.0);
private final ObservableList<EventNodeBase<?>> selectedNodes;
private final DetailViewLayoutSettings layoutSettings;
/**
* listener that triggers chart layout pass
*/
private final InvalidationListener layoutInvalidationListener = (Observable o) -> {
layoutPlotChildren();
};
/**
*
@ -102,9 +113,9 @@ public final class PinnedEventsChart extends XYChart<DateTime, TimeLineEvent> im
* @param verticalAxis the value of verticalAxis
* @param selectedNodes1 the value of selectedNodes1
*/
PinnedEventsChart(TimeLineController controller, DateAxis dateAxis, final Axis<TimeLineEvent> verticalAxis) {
PinnedEventsChart(TimeLineController controller, DateAxis dateAxis, final Axis<TimeLineEvent> verticalAxis, ObservableList<EventNodeBase<?>> selectedNodes, DetailViewLayoutSettings layoutSettings) {
super(dateAxis, verticalAxis);
this.layoutSettings = layoutSettings;
this.controller = controller;
this.filteredEvents = this.controller.getEventsModel();
@ -134,11 +145,12 @@ public final class PinnedEventsChart extends XYChart<DateTime, TimeLineEvent> im
setData(FXCollections.observableArrayList());
getData().add(series);
// //add listener for events that should trigger layout
// bandByType.addListener(layoutInvalidationListener);
// oneEventPerRow.addListener(layoutInvalidationListener);
// truncateAll.addListener(layoutInvalidationListener);
// truncateWidth.addListener(layoutInvalidationListener);
// descrVisibility.addListener(layoutInvalidationListener);
layoutSettings.bandByTypeProperty().addListener(layoutInvalidationListener);
layoutSettings.oneEventPerRowProperty().addListener(layoutInvalidationListener);
layoutSettings.truncateAllProperty().addListener(layoutInvalidationListener);
layoutSettings.truncateAllProperty().addListener(layoutInvalidationListener);
layoutSettings.descrVisibilityProperty().addListener(layoutInvalidationListener);
getController().getQuickHideFilters().addListener(layoutInvalidationListener);
// getController().getQuickHideFilters().addListener(layoutInvalidationListener);
// //this is needed to allow non circular binding of the guideline and timerangeRect heights to the height of the chart
@ -168,9 +180,7 @@ public final class PinnedEventsChart extends XYChart<DateTime, TimeLineEvent> im
requestChartLayout();
});
// this.selectedNodes = selectedNodes;
// selectedNodes.addListener(new SelectionChangeHandler());
this.selectedNodes = selectedNodes;
}
@Override
@ -233,7 +243,7 @@ public final class PinnedEventsChart extends XYChart<DateTime, TimeLineEvent> im
@Override
public ObservableList<EventNodeBase<?>> getSelectedNodes() {
return FXCollections.observableArrayList();
return selectedNodes;
}
@Override
@ -257,7 +267,9 @@ public final class PinnedEventsChart extends XYChart<DateTime, TimeLineEvent> im
double xRight = xLeft + w + MINIMUM_EVENT_NODE_GAP;
//initial test position
double yTop = computeYTop(minY, h, maxXatY, xLeft, xRight);
double yTop = (layoutSettings.getOneEventPerRow())
? (localMax + MINIMUM_EVENT_NODE_GAP)// if onePerRow, just put it at end
: computeYTop(minY, h, maxXatY, xLeft, xRight);
localMax = Math.max(yTop + h, localMax);
@ -301,17 +313,15 @@ public final class PinnedEventsChart extends XYChart<DateTime, TimeLineEvent> im
// .map(DescriptionFilter::getDescription)
// .collect(Collectors.toSet());
//This dosn't change during a layout pass and is expensive to compute per node. So we do it once at the start
descriptionWidth = /*
* truncateAll.get() ? truncateWidth.get() :
*/ USE_PREF_SIZE;
descriptionWidth = layoutSettings.getTruncateAll() ? layoutSettings.getTruncateWidth() : USE_PREF_SIZE;
// if (bandByType.get()) {
// sortedStripeNodes.stream()
// .collect(Collectors.groupingBy(EventStripeNode::getEventType)).values()
// .forEach(inputNodes -> maxY.set(layoutEventBundleNodes(inputNodes, maxY.get())));
// } else {
maxY.set(layoutEventBundleNodes(sortedEventNodes.sorted(Comparator.comparing(EventNodeBase::getStartMillis)), 0));
// }
if (layoutSettings.getBandByType()) {
sortedEventNodes.stream()
.collect(Collectors.groupingBy(EventNodeBase<?>::getEventType)).values()
.forEach(inputNodes -> maxY.set(layoutEventBundleNodes(inputNodes, maxY.get())));
} else {
maxY.set(layoutEventBundleNodes(sortedEventNodes.sorted(Comparator.comparing(EventNodeBase<?>::getStartMillis)), 0));
}
setCursor(null);
}
@ -326,6 +336,7 @@ public final class PinnedEventsChart extends XYChart<DateTime, TimeLineEvent> im
public synchronized void setVScroll(double vScrollValue) {
nodeGroup.setTranslateY(-vScrollValue);
}
public ReadOnlyDoubleProperty maxVScrollProperty() {
return maxY.getReadOnlyProperty();
}
@ -402,8 +413,8 @@ public final class PinnedEventsChart extends XYChart<DateTime, TimeLineEvent> im
eventNode.setVisible(true);
eventNode.setManaged(true);
//apply advanced layout description visibility options
eventNode.setDescriptionVisibility(DescriptionVisibility.SHOWN);
eventNode.setMaxDescriptionWidth(USE_PREF_SIZE);
eventNode.setDescriptionVisibility(layoutSettings.getDescrVisibility());
eventNode.setMaxDescriptionWidth(descriptionWidth);
//do recursive layout
eventNode.layoutChildren();

View File

@ -127,21 +127,7 @@ final class SingleEventNode extends EventNodeBase<SingleEvent> {
chart.requestTimelineChartLayout();
}
/**
* 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
setBackground(highlightedBackground);
} else {
descrLabel.setStyle("-fx-font-weight: normal;"); // NON-NLS
setBackground(defaultBackground);
}
}
/**
* @param w the maximum width the description label should have