layout optimization WIP

This commit is contained in:
jmillman 2015-10-21 16:20:49 -04:00
parent d6543dd967
commit 31cc6ebb95
9 changed files with 102 additions and 55 deletions

View File

@ -87,7 +87,7 @@ public interface ArtifactEventType extends EventType {
/**
* bundles the per event information derived from a BlackBoard Artifact into
* one object. Primarily used to have a single return value for {@link SubType#buildEventDescription(org.sleuthkit.datamodel.BlackboardArtifact).
* one object. Primarily used to have a single return value for null {@link SubType#buildEventDescription(org.sleuthkit.datamodel.BlackboardArtifact).
*/
static class AttributeEventDescription {
@ -187,6 +187,8 @@ public interface ArtifactEventType extends EventType {
}
}
public static class EmptyExtractor implements BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> {
@Override

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.timeline.datamodel.eventtype;
import com.google.common.net.InternetDomainName;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -38,7 +39,7 @@ public enum WebTypes implements EventType, ArtifactEventType {
"downloads.png", // NON-NLS
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN),
TopPrivateDomainExtractor.getInstance(),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL)) {
@ -67,7 +68,7 @@ public enum WebTypes implements EventType, ArtifactEventType {
"cookies.png", // NON-NLS
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN),
TopPrivateDomainExtractor.getInstance(),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE)),
//TODO: review description separators
@ -75,7 +76,7 @@ public enum WebTypes implements EventType, ArtifactEventType {
"bookmarks.png", // NON-NLS
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN),
TopPrivateDomainExtractor.getInstance(),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE)),
//TODO: review description separators
@ -83,7 +84,7 @@ public enum WebTypes implements EventType, ArtifactEventType {
"history.png", // NON-NLS
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN),
TopPrivateDomainExtractor.getInstance(),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE)),
//TODO: review description separators
@ -92,7 +93,7 @@ public enum WebTypes implements EventType, ArtifactEventType {
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN),
TopPrivateDomainExtractor.getInstance(),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME));
private final BlackboardAttribute.ATTRIBUTE_TYPE dateTimeAttributeType;
@ -186,4 +187,30 @@ public enum WebTypes implements EventType, ArtifactEventType {
return Collections.emptyList();
}
private static class TopPrivateDomainExtractor extends AttributeExtractor {
final private static TopPrivateDomainExtractor instance = new TopPrivateDomainExtractor();
static TopPrivateDomainExtractor getInstance() {
return instance;
}
@Override
public String apply(BlackboardArtifact artf, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attrMap) {
String domainString = StringUtils.substringBefore(super.apply(artf, attrMap), "/");
if (InternetDomainName.isValid(domainString)) {
InternetDomainName domain = InternetDomainName.from(domainString);
return (domain.isUnderPublicSuffix())
? domain.topPrivateDomain().toString()
: domain.toString();
} else {
return domainString;
}
}
TopPrivateDomainExtractor() {
super(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN);
}
}
}

View File

@ -332,7 +332,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventClu
}
Platform.runLater(() -> {
setCursor(Cursor.NONE);
setCursor(Cursor.DEFAULT);
layoutDateLabels();
updateProgress(1, 1);
});

View File

@ -27,11 +27,11 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javafx.beans.Observable;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
@ -126,7 +126,6 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
this.eventBundle = eventBundle;
this.parentNode = parentNode;
this.chart = chart;
this.descLOD.set(eventBundle.getDescriptionLoD());
sleuthkitCase = chart.getController().getAutopsyCase().getSleuthkitCase();
eventsModel = chart.getController().getEventsModel();
@ -145,22 +144,24 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
setAlignment(Pos.TOP_LEFT);
setPrefHeight(USE_COMPUTED_SIZE);
heightProperty().addListener((Observable observable) -> {
chart.layoutPlotChildren();
heightProperty().addListener(heightProp -> {
chart.requestChartLayout();
});
setMaxHeight(USE_PREF_SIZE);
setMinWidth(USE_PREF_SIZE);
setMaxWidth(USE_PREF_SIZE);
setLayoutX(chart.getXAxis().getDisplayPosition(new DateTime(eventBundle.getStartMillis())) - getLayoutXCompensation());
//initialize info hbox
infoHBox.setMinWidth(USE_PREF_SIZE);
infoHBox.setMaxWidth(USE_PREF_SIZE);
infoHBox.setPadding(new Insets(2, 5, 2, 5));
infoHBox.setPadding(new Insets(2, 3, 2, 3));
infoHBox.setAlignment(Pos.TOP_LEFT);
//set up subnode pane sizing contraints
subNodePane.setPrefHeight(USE_COMPUTED_SIZE);
subNodePane.setMaxHeight(USE_PREF_SIZE);
subNodePane.setPrefHeight(USE_COMPUTED_SIZE);
subNodePane.setMinHeight(24);
subNodePane.setPrefWidth(USE_COMPUTED_SIZE);
subNodePane.setMinWidth(USE_PREF_SIZE);
subNodePane.setMaxWidth(USE_PREF_SIZE);
@ -177,7 +178,6 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
Tooltip.uninstall(chart, AbstractVisualizationPane.getDragTooltip());
showHoverControls(true);
toFront();
});
setOnMouseExited((MouseEvent event) -> {
showHoverControls(false);
@ -269,7 +269,7 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
}
}
};
new Thread(tooltTipTask).start();
chart.getController().monitorTask(tooltTipTask);
}
}
@ -329,6 +329,11 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
return getEventBundle().getEventIDs();
}
@Override
public Orientation getContentBias() {
return Orientation.HORIZONTAL;
}
@Override
protected void layoutChildren() {
chart.layoutEventBundleNodes(subNodes, 0);

View File

@ -114,7 +114,7 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
}
@Override
void setDescriptionVisibiltiyImpl(DescriptionVisibility descrVis) {
void setDescriptionVisibiltiyImpl(DescriptionVisibility descrVis) {
final int size = getEventBundle().getEventIDs().size();
switch (descrVis) {
case HIDDEN:
@ -212,11 +212,12 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
} catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.SEVERE, "Error loading subnodes", ex);
}
chart.layoutPlotChildren();
chart.requestChartLayout();
chart.setCursor(null);
}
};
new Thread(loggedTask).start();
//start task
chart.getController().monitorTask(loggedTask);
}
@ -233,8 +234,7 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
protected void layoutChildren() {
double chartX = chart.getXAxis().getDisplayPosition(new DateTime(getStartMillis()));
double w = chart.getXAxis().getDisplayPosition(new DateTime(getEndMillis())) - chartX;
subNodePane.setPrefWidth(w);
subNodePane.setMinWidth(Math.max(1, w));
subNodePane.setPrefWidth(Math.max(1, w));
super.layoutChildren();
}

View File

@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -103,14 +104,11 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
private static final int PROJECTED_LINE_STROKE_WIDTH = 5;
private static final int MINIMUM_EVENT_NODE_GAP = 4;
private final TimeLineController controller;
private final FilteredEventsModel filteredEvents;
private ContextMenu chartContextMenu;
public ContextMenu getChartContextMenu() {
return chartContextMenu;
}
@ -204,7 +202,6 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
});
Tooltip.install(this, AbstractVisualizationPane.getDragTooltip());
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
@ -342,7 +339,6 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
stripeNodeMap.put(eventStripe, stripeNode);
nodeGroup.getChildren().add(stripeNode);
data.setNode(stripeNode);
layoutPlotChildren();
}
@Override
@ -359,7 +355,6 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
EventStripeNode removedNode = stripeNodeMap.remove(removedStripe);
nodeGroup.getChildren().remove(removedNode);
data.setNode(null);
layoutPlotChildren();
}
@Override
@ -463,48 +458,42 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
* @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) {
// map from y value (ranges) to right most occupied x value.
double layoutEventBundleNodes(final Collection<? extends EventBundleNodeBase<?, ?, ?>> nodes, final double minY) {
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;
Set<String> activeQuickHidefilters = getController().getQuickHideFilters().stream()
.filter(AbstractFilter::isActive)
.map(DescriptionFilter::getDescription)
.collect(Collectors.toSet());
//for each node do a recursive layout to size it and then position it in first available slot
for (final EventBundleNodeBase<?, ?, ?> bundleNode : nodes) {
for (EventBundleNodeBase<?, ?, ?> bundleNode : nodes) {
//is the node hiden by a quick hide filter?
boolean quickHide = getController().getQuickHideFilters().stream()
.filter(AbstractFilter::isActive)
.anyMatch(filter -> filter.getDescription().equals(bundleNode.getDescription()));
boolean quickHide = activeQuickHidefilters.contains(bundleNode.getDescription());
if (quickHide) {
//hide it and skip layout
bundleNode.setVisible(false);
bundleNode.setManaged(false);
} else {
//make sure it is shown
bundleNode.setVisible(true);
bundleNode.setManaged(true);
//apply advanced layout description visibility options
bundleNode.setDescriptionVisibility(descrVisibility.get());
bundleNode.setDescriptionWidth(truncateAll.get() ? truncateWidth.get() : USE_PREF_SIZE);
//do recursive layout
bundleNode.layout();
bundleLayoutHelper(bundleNode);
//get computed height and width
double h = bundleNode.getBoundsInLocal().getHeight();
double w = bundleNode.getBoundsInLocal().getWidth();
//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 + MINIMUM_EVENT_NODE_GAP;
//initial test position
double yTop = minY;
double yBottom = yTop + h;
if (oneEventPerRow.get()) {
// if onePerRow, just put it at end
yTop = (localMax + MINIMUM_EVENT_NODE_GAP);
yBottom = yTop + h;
} else {
double yBottom = yTop + h;
//until the node is not overlapping any others try moving it down.
boolean overlapping = true;
while (overlapping) {
@ -525,21 +514,42 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
treeRangeMap.put(Range.closed(yTop, yBottom), xRight);
}
localMax = Math.max(yBottom, localMax);
localMax = Math.max(yTop + h, localMax);
//animate node to new position
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(100),
new KeyValue(bundleNode.layoutXProperty(), xLeft),
new KeyValue(bundleNode.layoutYProperty(), yTop)));
timeline.setOnFinished((ActionEvent event) -> {
requestChartLayout();
});
timeline.play();
if ((xLeft != bundleNode.getLayoutX()) || (yTop != bundleNode.getLayoutY())) {
//animate node to new position
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(100),
new KeyValue(bundleNode.layoutXProperty(), xLeft),
new KeyValue(bundleNode.layoutYProperty(), yTop))
);
timeline.setOnFinished((ActionEvent event) -> {
requestChartLayout();
});
timeline.play();
}
}
}
return localMax; //return new max
}
private void bundleLayoutHelper(final EventBundleNodeBase<?, ?, ?> bundleNode) {
//make sure it is shown
bundleNode.setVisible(true);
bundleNode.setManaged(true);
//apply advanced layout description visibility options
bundleNode.setDescriptionVisibility(descrVisibility.get());
bundleNode.setDescriptionWidth(truncateAll.get() ? truncateWidth.get() : USE_PREF_SIZE);
//do recursive layout
bundleNode.layoutChildren();
}
@Override
public void requestChartLayout() {
super.requestChartLayout(); //To change body of generated methods, choose Tools | Templates.
}
private double getXForEpochMillis(Long millis) {
DateTime dateTime = new DateTime(millis, TimeLineController.getJodaTimeZone());
return getXAxis().getDisplayPosition(new DateTime(dateTime));
@ -568,6 +578,7 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
*/
public FilteredEventsModel getFilteredEvents() {
return filteredEvents;
}
static private class DetailIntervalSelector extends IntervalSelector<DateTime> {
@ -673,7 +684,7 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
.filter(testFilter::equals)
.findFirst().orElseGet(() -> {
testFilter.selectedProperty().addListener((Observable observable) -> {
layoutPlotChildren();
requestChartLayout();
});
getController().getQuickHideFilters().add(testFilter);
return testFilter;

View File

@ -98,6 +98,8 @@ final public class EventStripeNode extends EventBundleNodeBase<EventStripe, Even
descrLabel.setMaxWidth(w);
}
/**
* apply the 'effect' to visually indicate highlighted nodes
*

View File

@ -1,5 +1,5 @@
#Updated by build script
#Mon, 26 Oct 2015 09:54:05 -0400
#Mon, 26 Oct 2015 14:50:57 -0400
LBL_splash_window_title=Starting Autopsy
SPLASH_HEIGHT=314
SPLASH_WIDTH=538

View File

@ -1,4 +1,4 @@
#Updated by build script
#Mon, 26 Oct 2015 09:54:05 -0400
#Mon, 26 Oct 2015 14:50:57 -0400
CTL_MainWindow_Title=Autopsy 4.0.0
CTL_MainWindow_Title_No_Project=Autopsy 4.0.0