Merge branch 'develop' of https://github.com/sleuthkit/autopsy into forRC

This commit is contained in:
Karl Mortensen 2015-11-20 09:49:29 -05:00
commit a81e9bd0c0
19 changed files with 295 additions and 214 deletions

View File

@ -188,4 +188,5 @@ public class EventCluster implements EventBundle<EventStripe> {
public SortedSet< EventCluster> getClusters() {
return ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis)).add(this).build();
}
}

View File

@ -83,6 +83,22 @@ public final class EventStripe implements EventBundle<EventCluster> {
*/
private final Set<Long> hashHits = new HashSet<>();
public EventStripe withParent(EventCluster parent) {
EventStripe eventStripe = new EventStripe(parent, this.type, this.description, this.lod);
eventStripe.clusters.addAll(clusters);
eventStripe.eventIDs.addAll(eventIDs);
eventStripe.tagged.addAll(tagged);
eventStripe.hashHits.addAll(hashHits);
return eventStripe;
}
private EventStripe(EventCluster parent, EventType type, String description, DescriptionLoD lod) {
this.parent = parent;
this.type = type;
this.description = description;
this.lod = lod;
}
public EventStripe(EventCluster cluster, EventCluster parent) {
clusters.add(cluster);
@ -155,7 +171,15 @@ public final class EventStripe implements EventBundle<EventCluster> {
return clusters.last().getEndMillis();
}
@Override
public SortedSet< EventCluster> getClusters() {
return Collections.unmodifiableSortedSet(clusters);
}
@Override
public String toString() {
return "EventStripe{" + "description=" + description + ", eventIDs=" + eventIDs.size() + '}';
}
}

View File

@ -327,7 +327,7 @@ public final class FilteredEventsModel {
* @return a list of event clusters at the requested zoom levels that are
* within the requested time range and pass the requested filter
*/
public List<EventCluster> getEventClusters() {
public List<EventStripe> getEventStripes() {
final Interval range;
final RootFilter filter;
final EventTypeZoomLevel zoom;
@ -338,7 +338,7 @@ public final class FilteredEventsModel {
zoom = requestedTypeZoom.get();
lod = requestedLOD.get();
}
return repo.getEventClusters(new ZoomParams(range, zoom, filter, lod));
return repo.getEventStripes(new ZoomParams(range, zoom, filter, lod));
}
/**
@ -348,8 +348,8 @@ public final class FilteredEventsModel {
* range and pass the requested filter, using the given aggregation
* to control the grouping of events
*/
public List<EventCluster> getEventClusters(ZoomParams params) {
return repo.getEventClusters(params);
public List<EventStripe> getEventClusters(ZoomParams params) {
return repo.getEventStripes(params);
}
synchronized public boolean handleContentTagAdded(ContentTagAddedEvent evt) {

View File

@ -48,6 +48,7 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.joda.time.Period;
@ -55,6 +56,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.BaseTypes;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
@ -1052,8 +1054,7 @@ public class EventDB {
}
/**
* get a list of {@link EventCluster}s, clustered according to the given
* zoom paramaters.
* get a list of {@link EventStripe}s, clustered according to the given * zoom paramaters.
*
* @param params the zoom params that determine the zooming, filtering and
* clustering.
@ -1062,7 +1063,7 @@ public class EventDB {
* the supplied filter, aggregated according to the given event type
* and description zoom levels
*/
List<EventCluster> getClusteredEvents(ZoomParams params) {
List<EventStripe> getEventStripes(ZoomParams params) {
//unpack params
Interval timeRange = params.getTimeRange();
RootFilter filter = params.getFilter();
@ -1101,11 +1102,9 @@ public class EventDB {
List<EventCluster> events = new ArrayList<>();
DBLock.lock();
try (Statement createStatement = con.createStatement();
ResultSet rs = createStatement.executeQuery(query)) {
while (rs.next()) {
events.add(eventClusterHelper(rs, useSubTypes, descriptionLOD, filter.getTagsFilter()));
}
} catch (SQLException ex) {
@ -1114,7 +1113,17 @@ public class EventDB {
DBLock.unlock();
}
return mergeEventClusters(rangeInfo.getPeriodSize().getPeriod(), events);
List<EventCluster> mergeEventClusters = mergeEventClusters(rangeInfo.getPeriodSize().getPeriod(), events);
//merge clusters to stripes
Map<ImmutablePair<EventType, String>, EventStripe> stripeDescMap = new HashMap<>();
for (EventCluster eventCluster : mergeEventClusters) {
stripeDescMap.merge(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()),
new EventStripe(eventCluster, null), EventStripe::merge);
}
return stripeDescMap.values().stream().sorted(Comparator.comparing(EventStripe::getStartMillis)).collect(Collectors.toList());
}
/**

View File

@ -31,6 +31,7 @@ import java.util.Map;
import static java.util.Objects.isNull;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@ -48,6 +49,7 @@ import org.apache.commons.lang3.StringUtils;
import org.joda.time.Interval;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
@ -56,7 +58,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.timeline.CancellationProgressTask;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType;
@ -106,7 +108,7 @@ public class EventsRepository {
private final LoadingCache<Object, Long> minCache;
private final LoadingCache<Long, TimeLineEvent> idToEventCache;
private final LoadingCache<ZoomParams, Map<EventType, Long>> eventCountsCache;
private final LoadingCache<ZoomParams, List<EventCluster>> eventClusterCache;
private final LoadingCache<ZoomParams, List<EventStripe>> eventStripeCache;
private final ObservableMap<Long, String> datasourcesMap = FXCollections.observableHashMap();
private final ObservableMap<Long, String> hashSetMap = FXCollections.observableHashMap();
@ -153,10 +155,10 @@ public class EventsRepository {
.maximumSize(1000L)
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(CacheLoader.from(eventDB::countEventsByType));
eventClusterCache = CacheBuilder.newBuilder()
eventStripeCache = CacheBuilder.newBuilder()
.maximumSize(1000L)
.expireAfterAccess(10, TimeUnit.MINUTES
).build(CacheLoader.from(eventDB::getClusteredEvents));
).build(CacheLoader.from(eventDB::getEventStripes));
maxCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMaxTime));
minCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMinTime));
this.modelInstance = new FilteredEventsModel(this, currentStateProperty);
@ -213,8 +215,13 @@ public class EventsRepository {
}
synchronized public List<EventCluster> getEventClusters(ZoomParams params) {
return eventClusterCache.getUnchecked(params);
synchronized public List<EventStripe> getEventStripes(ZoomParams params) {
try {
return eventStripeCache.get(params);
} catch (ExecutionException ex) {
Exceptions.printStackTrace(ex);
return Collections.emptyList();
}
}
synchronized public Map<EventType, Long> countEvents(ZoomParams params) {
@ -225,7 +232,7 @@ public class EventsRepository {
minCache.invalidateAll();
maxCache.invalidateAll();
eventCountsCache.invalidateAll();
eventClusterCache.invalidateAll();
eventStripeCache.invalidateAll();
idToEventCache.invalidateAll();
}
@ -299,7 +306,7 @@ public class EventsRepository {
synchronized private void invalidateCaches(Set<Long> updatedEventIDs) {
eventCountsCache.invalidateAll();
eventClusterCache.invalidateAll();
eventStripeCache.invalidateAll();
idToEventCache.invalidateAll(updatedEventIDs);
try {
tagNames.setAll(autoCase.getSleuthkitCase().getTagNamesInUse());

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.timeline.ui;
import com.google.common.eventbus.Subscribe;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
@ -33,7 +34,6 @@ import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.Chart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
@ -53,6 +53,7 @@ import javax.annotation.concurrent.Immutable;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent;
@ -81,7 +82,7 @@ public abstract class AbstractVisualizationPane<X, Y, N, C extends XYChart<X, Y>
}
protected final SimpleBooleanProperty hasEvents = new SimpleBooleanProperty(true);
protected final ObservableList<BarChart.Series<X, Y>> dataSets = FXCollections.<BarChart.Series<X, Y>>observableArrayList();
protected final ObservableList<XYChart.Series<X, Y>> dataSeries = FXCollections.<XYChart.Series<X, Y>> observableArrayList();
protected C chart;
@ -251,7 +252,7 @@ public abstract class AbstractVisualizationPane<X, Y, N, C extends XYChart<X, Y>
}
});
update();
// update();
}
@Subscribe
@ -274,9 +275,8 @@ public abstract class AbstractVisualizationPane<X, Y, N, C extends XYChart<X, Y>
*
* _________october___________|_____________september___________
*
*
* NOTE: This method should only be invoked on the JFX thread
*/
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
public synchronized void layoutDateLabels() {
//clear old labels
@ -285,7 +285,7 @@ public abstract class AbstractVisualizationPane<X, Y, N, C extends XYChart<X, Y>
//since the tickmarks aren't necessarily in value/position order,
//make a clone of the list sorted by position along axis
ObservableList<Axis.TickMark<X>> tickMarks = FXCollections.observableArrayList(getXAxis().getTickMarks());
tickMarks.sort((Axis.TickMark<X> t, Axis.TickMark<X> t1) -> Double.compare(t.getPosition(), t1.getPosition()));
tickMarks.sort(Comparator.comparing(Axis.TickMark::getPosition));
if (tickMarks.isEmpty() == false) {
//get the spacing between ticks in the underlying axis

View File

@ -61,6 +61,7 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ColorUtilities;
import org.sleuthkit.autopsy.coreutils.LoggedTask;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.VisualizationMode;
@ -111,7 +112,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
@Override
protected Boolean isTickBold(String value) {
return dataSets.stream().flatMap((series) -> series.getData().stream())
return dataSeries.stream().flatMap((series) -> series.getData().stream())
.anyMatch((data) -> data.getXValue().equals(value) && data.getYValue().intValue() > 0);
}
@ -144,7 +145,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
Platform.runLater(() -> {
updateMessage(NbBundle.getMessage(this.getClass(), "CountsViewPane.loggedTask.resetUI"));
eventTypeMap.clear();
dataSets.clear();
dataSeries.clear();
dateAxis.getCategories().clear();
DateTime start = timeRange.getStart();
@ -264,7 +265,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
super(controller, partPane, contextPane, spacer);
chart = new EventCountsChart(controller, dateAxis, countAxis);
setChartClickHandler();
chart.setData(dataSets);
chart.setData(dataSeries);
setCenter(chart);
Tooltip.install(chart, getDefaultTooltip());
@ -331,16 +332,20 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
* @return a Series object to contain all the events with the given
* EventType
*/
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private XYChart.Series<String, Number> getSeries(final EventType et) {
XYChart.Series<String, Number> series = eventTypeMap.get(et);
if (series == null) {
series = new XYChart.Series<>();
return eventTypeMap.computeIfAbsent(et, (EventType t) -> {
XYChart.Series<String, Number> series = new XYChart.Series<>();
series.setName(et.getDisplayName());
eventTypeMap.put(et, series);
dataSets.add(series);
}
return series;
dataSeries.add(series);
return series;
});
// XYChart.Series<String, Number> series = eventTypeMap.get(et);
// if (series == null) {
//
// }
// return series;
}
@ -395,7 +400,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
controller.selectTimeAndType(interval, RootEventType.getInstance());
selectedNodes.clear();
for (XYChart.Series<String, Number> s : dataSets) {
for (XYChart.Series<String, Number> s : dataSeries) {
s.getData().forEach((XYChart.Data<String, Number> d) -> {
if (startDateString.contains(d.getXValue())) {
selectedNodes.add(d.getNode());

View File

@ -6,8 +6,8 @@ DetailViewPane.truncateSliderLabel.text=max description width (px)\:
DetailViewPane.advancedLayoutOptionsButtonLabel.text=Advanced Layout Options
DetailViewPane.bandByTypeBox.text=Band by Type
DetailViewPane.bandByTypeBoxMenuItem.text=Band by Type
DetailViewPane.oneEventPerRowBox.text=One Event Per Row
DetailViewPane.oneEventPerRowBoxMenuItem.text=One Event Per Row
DetailViewPane.oneEventPerRowBox.text=One Per Row
DetailViewPane.oneEventPerRowBoxMenuItem.text=One Per Row
DetailViewPan.truncateAllBox.text=Truncate Descriptions
DetailViewPan.truncateAllBoxMenuItem.text=Truncate Descriptions
DetailViewPane.truncateSlideLabelMenuItem.text=max description width (px)

View File

@ -19,9 +19,9 @@
package org.sleuthkit.autopsy.timeline.ui.detailview;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
@ -33,7 +33,6 @@ import javafx.fxml.FXML;
import javafx.geometry.Orientation;
import javafx.scene.Cursor;
import javafx.scene.chart.Axis;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.CheckBox;
import javafx.scene.control.CustomMenuItem;
@ -68,7 +67,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.EventBundle;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
@ -87,7 +86,7 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
* EventTypeMap, and dataSets is all linked directly to the ClusterChart which
* must only be manipulated on the JavaFx thread.
*/
public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventCluster, EventBundleNodeBase<?, ?, ?>, EventDetailsChart> {
public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStripe, EventBundleNodeBase<?, ?, ?>, EventDetailsChart> {
private final static Logger LOGGER = Logger.getLogger(DetailViewPane.class.getName());
@ -95,7 +94,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventClu
private static final double PAGE_SCROLL_PERCENTAGE = .70;
private final DateAxis dateAxis = new DateAxis();
private final Axis<EventCluster> verticalAxis = new EventAxis();
private final Axis<EventStripe> verticalAxis = new EventAxis();
private final ScrollBar vertScrollBar = new ScrollBar();
private final Region scrollBarSpacer = new Region();
@ -103,23 +102,22 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventClu
private final ObservableList<EventBundleNodeBase<?, ?, ?>> highlightedNodes = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
//private access to barchart data
private final Map<EventType, XYChart.Series<DateTime, EventCluster>> eventTypeToSeriesMap = new ConcurrentHashMap<>();
private final Map<EventType, XYChart.Series<DateTime, EventStripe>> eventTypeToSeriesMap = new HashMap<>();
public ObservableList<EventBundle<?>> getEventBundles() {
return chart.getEventBundles();
public ObservableList<EventStripe> getEventStripes() {
return chart.getEventStripes();
}
public DetailViewPane(TimeLineController controller, Pane partPane, Pane contextPane, Region bottomLeftSpacer) {
super(controller, partPane, contextPane, bottomLeftSpacer);
//initialize chart;
chart = new EventDetailsChart(controller, dateAxis, verticalAxis, selectedNodes);
setChartClickHandler(); //can we push this into chart
chart.setData(dataSets);
chart.setData(dataSeries);
setCenter(chart);
settingsNodes = new ArrayList<>(new DetailViewSettingsPane().getChildrenUnmodifiable());
//bind layout fo axes and spacers
dateAxis.setTickLabelGap(0);
dateAxis.setAutoRanging(false);
@ -227,7 +225,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventClu
}
@Override
protected Axis<EventCluster> getYAxis() {
protected Axis<EventStripe> getYAxis() {
return verticalAxis;
}
@ -256,11 +254,11 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventClu
* EventType
*/
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private XYChart.Series<DateTime, EventCluster> getSeries(final EventType et) {
return eventTypeToSeriesMap.computeIfAbsent(et, (EventType t) -> {
XYChart.Series<DateTime, EventCluster> series = new XYChart.Series<>();
series.setName(et.getDisplayName());
dataSets.add(series);
private XYChart.Series<DateTime, EventStripe> getSeries(final EventType et) {
return eventTypeToSeriesMap.computeIfAbsent(et, eventType -> {
XYChart.Series<DateTime, EventStripe> series = new XYChart.Series<>();
series.setName(eventType.getDisplayName());
dataSeries.add(series);
return series;
});
}
@ -276,12 +274,6 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventClu
return null;
}
if (isCancelled() == false) {
Platform.runLater(() -> {
setCursor(Cursor.WAIT);
});
}
updateProgress(-1, 1);
updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.preparing"));
@ -292,41 +284,39 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventClu
updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.queryDb"));
Platform.runLater(() -> {
if (isCancelled()) {
return;
}
dataSeries.clear();
dateAxis.setLowerBound(new DateTime(lowerBound, TimeLineController.getJodaTimeZone()));
dateAxis.setUpperBound(new DateTime(upperBound, TimeLineController.getJodaTimeZone()));
vertScrollBar.setValue(0);
eventTypeToSeriesMap.clear();
dataSets.clear();
});
List<EventCluster> eventClusters = filteredEvents.getEventClusters();
List<EventStripe> eventStripes = filteredEvents.getEventStripes();
final int size = eventClusters.size();
final int size = eventStripes.size();
updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.updateUI"));
for (int i = 0; i < size; i++) {
if (isCancelled()) {
break;
}
final EventCluster cluster = eventClusters.get(i);
updateProgress(i, size);
updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.updateUI"));
final XYChart.Data<DateTime, EventCluster> xyData = new BarChart.Data<>(new DateTime(cluster.getSpan().getStartMillis()), cluster);
if (isCancelled() == false) {
Platform.runLater(() -> {
getSeries(cluster.getEventType()).getData().add(xyData);
});
}
final EventStripe cluster = eventStripes.get(i);
final XYChart.Data<DateTime, EventStripe> xyData = new XYChart.Data<>(new DateTime(cluster.getStartMillis()), cluster);
Platform.runLater(() -> {
getSeries(cluster.getEventType()).getData().add(xyData);
});
}
Platform.runLater(() -> {
setCursor(Cursor.DEFAULT);
layoutDateLabels();
updateProgress(1, 1);
});
return eventClusters.isEmpty() == false;
updateProgress(1, 1);
return eventStripes.isEmpty() == false;
}
@Override
protected void succeeded() {
super.succeeded();
layoutDateLabels();
setCursor(Cursor.DEFAULT);
}
};
}

View File

@ -22,21 +22,21 @@ import java.util.Collections;
import java.util.List;
import javafx.scene.chart.Axis;
import javafx.scene.chart.XYChart;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
/**
* No-Op axis that doesn't do anything usefull but is necessary to pass
* AggregateEvent as the second member of {@link XYChart.Data} objects
*/
class EventAxis extends Axis<EventCluster> {
class EventAxis extends Axis<EventStripe> {
@Override
public double getDisplayPosition(EventCluster value) {
public double getDisplayPosition(EventStripe value) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public EventCluster getValueForDisplay(double displayPosition) {
public EventStripe getValueForDisplay(double displayPosition) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@ -46,17 +46,17 @@ class EventAxis extends Axis<EventCluster> {
}
@Override
public boolean isValueOnAxis(EventCluster value) {
public boolean isValueOnAxis(EventStripe value) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public double toNumericValue(EventCluster value) {
public double toNumericValue(EventStripe value) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public EventCluster toRealValue(double value) {
public EventStripe toRealValue(double value) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@ -66,7 +66,7 @@ class EventAxis extends Axis<EventCluster> {
}
@Override
protected List<EventCluster> calculateTickValues(double length, Object range) {
protected List<EventStripe> calculateTickValues(double length, Object range) {
return Collections.emptyList();
}
@ -76,7 +76,7 @@ class EventAxis extends Axis<EventCluster> {
}
@Override
protected String getTickMarkLabel(EventCluster value) {
protected String getTickMarkLabel(EventStripe value) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}

View File

@ -27,6 +27,10 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
@ -55,6 +59,7 @@ 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.paint.Color;
import javafx.util.Duration;
import org.joda.time.DateTime;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -121,6 +126,7 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
final HBox infoHBox = new HBox(5, descrLabel, countLabel, hashIV, tagIV);
private final Tooltip tooltip = new Tooltip("loading...");
private Timeline timeline;
public EventBundleNodeBase(EventDetailsChart chart, BundleType eventBundle, ParentNodeType parentNode) {
this.eventBundle = eventBundle;
@ -356,4 +362,16 @@ public abstract class EventBundleNodeBase<BundleType extends EventBundle<ParentT
Tooltip.uninstall(this, tooltip);
}
}
void animateTo(double xLeft, double yTop) {
if (timeline != null) {
timeline.stop();
}
timeline = new Timeline(new KeyFrame(Duration.millis(100),
new KeyValue(layoutXProperty(), xLeft),
new KeyValue(layoutYProperty(), yTop))
);
timeline.setOnFinished(finished -> Platform.runLater(chart::requestChartLayout));
timeline.play();
}
}

View File

@ -141,7 +141,7 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
@NbBundle.Messages(value = "EventStripeNode.loggedTask.name=Load sub clusters")
private synchronized void loadSubBundles(DescriptionLoD.RelativeDetail relativeDetail) {
chart.setCursor(Cursor.WAIT);
chart.getEventBundles().removeIf(bundle ->
chart.getEventStripes().removeIf(bundle ->
subNodes.stream().anyMatch(subNode ->
bundle.equals(subNode.getEventStripe()))
);
@ -169,7 +169,7 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
@Override
protected Collection<EventStripe> call() throws Exception {
Collection<EventStripe> bundles;
Collection<EventStripe> bundles = null;
DescriptionLoD next = loadedDescriptionLoD;
do {
loadedDescriptionLoD = next;
@ -177,8 +177,8 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
return Collections.emptySet();
}
bundles = eventsModel.getEventClusters(zoomParams.withDescrLOD(loadedDescriptionLoD)).stream()
.collect(Collectors.toMap(EventCluster::getDescription, //key
(eventCluster) -> new EventStripe(eventCluster, getEventCluster()), //value
.collect(Collectors.toMap(EventStripe::getDescription, //key
eventStripe -> eventStripe.withParent(getEventCluster()), //value
EventStripe::merge) //merge method
).values();
next = loadedDescriptionLoD.withRelativeDetail(relativeDetail);
@ -200,7 +200,7 @@ final public class EventClusterNode extends EventBundleNodeBase<EventCluster, Ev
getChildren().setAll(subNodePane, infoHBox);
descLOD.set(getEventBundle().getDescriptionLoD());
} else {
chart.getEventBundles().addAll(bundles);
chart.getEventStripes().addAll(bundles);
subNodes.addAll(bundles.stream()
.map(EventClusterNode.this::createStripeNode)
.sorted(Comparator.comparing(EventStripeNode::getStartMillis))

View File

@ -24,18 +24,14 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
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;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.ReadOnlyDoubleProperty;
@ -61,8 +57,6 @@ import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Line;
import javafx.scene.shape.StrokeLineCap;
import javafx.util.Duration;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionUtils;
import org.joda.time.DateTime;
@ -70,11 +64,9 @@ import org.joda.time.Interval;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.EventBundle;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.filters.AbstractFilter;
import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter;
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
@ -96,7 +88,7 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
*
* //TODO: refactor the projected lines to a separate class. -jm
*/
public final class EventDetailsChart extends XYChart<DateTime, EventCluster> implements TimeLineChart<DateTime> {
public final class EventDetailsChart extends XYChart<DateTime, EventStripe> implements TimeLineChart<DateTime> {
private static final String styleSheet = GuideLine.class.getResource("EventsDetailsChart.css").toExternalForm();
private static final Image HIDE = new Image("/org/sleuthkit/autopsy/timeline/images/eye--minus.png"); // NON-NLS
@ -110,6 +102,7 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
private final FilteredEventsModel filteredEvents;
private ContextMenu chartContextMenu;
private Set<String> activeQuickHidefilters;
@Override
public ContextMenu getChartContextMenu() {
@ -147,18 +140,10 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
* by allowing a single translation of this group.
*/
private final Group nodeGroup = new Group();
private final ObservableList<EventBundle<?>> bundles = FXCollections.observableArrayList();
private final Map<ImmutablePair<EventType, String>, EventStripe> stripeDescMap = new HashMap<>();
private final Map<EventStripe, EventStripeNode> stripeNodeMap = new HashMap<>();
private final Map<EventCluster, Line> projectionMap = new HashMap<>();
/**
* list of series of data added to this chart
*
* TODO: replace this with a map from name to series? -jm
*/
private final ObservableList<Series<DateTime, EventCluster>> seriesList =
FXCollections.<Series<DateTime, EventCluster>>observableArrayList();
private final ObservableList<EventStripe> bundles = FXCollections.observableArrayList();
private final ObservableList< EventStripeNode> stripeNodes = FXCollections.observableArrayList();
private final ObservableList< EventStripeNode> sortedStripeNodes = stripeNodes.sorted(Comparator.comparing(EventStripeNode::getStartMillis));
private final Map<EventCluster, Line> projectionMap = new ConcurrentHashMap<>();
/**
* true == layout each event type in its own band, false == mix all the
@ -192,8 +177,9 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
*/
final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0);
EventDetailsChart(TimeLineController controller, DateAxis dateAxis, final Axis<EventCluster> verticalAxis, ObservableList<EventBundleNodeBase<?, ?, ?>> selectedNodes) {
EventDetailsChart(TimeLineController controller, DateAxis dateAxis, final Axis<EventStripe> verticalAxis, ObservableList<EventBundleNodeBase<?, ?, ?>> selectedNodes) {
super(dateAxis, verticalAxis);
this.controller = controller;
this.filteredEvents = this.controller.getEventsModel();
@ -250,7 +236,7 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
this.selectedNodes.addListener(new SelectionChangeHandler());
}
ObservableList<EventBundle<?>> getEventBundles() {
ObservableList<EventStripe> getEventStripes() {
return bundles;
}
@ -266,7 +252,6 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
}
chartContextMenu = ActionUtils.createContextMenu(Arrays.asList(new PlaceMarkerAction(clickEvent),
// new StartIntervalSelectionAction(clickEvent, dragHandler),
TimeLineChart.newZoomHistoyActionGroup(controller)));
chartContextMenu.setAutoHide(true);
return chartContextMenu;
@ -331,80 +316,66 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
return descrVisibility;
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
@Override
protected synchronized void dataItemAdded(Series<DateTime, EventCluster> series, int i, Data<DateTime, EventCluster> data) {
final EventCluster eventCluster = data.getYValue();
bundles.add(eventCluster);
EventStripe eventStripe = stripeDescMap.merge(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()),
new EventStripe(eventCluster, null),
(EventStripe u, EventStripe v) -> {
EventStripeNode remove = stripeNodeMap.remove(u);
nodeGroup.getChildren().remove(remove);
remove = stripeNodeMap.remove(v);
nodeGroup.getChildren().remove(remove);
return EventStripe.merge(u, v);
}
);
protected void dataItemAdded(Series<DateTime, EventStripe> series, int i, Data<DateTime, EventStripe> data) {
final EventStripe eventStripe = data.getYValue();
bundles.add(eventStripe);
EventStripeNode stripeNode = new EventStripeNode(EventDetailsChart.this, eventStripe, null);
stripeNodeMap.put(eventStripe, stripeNode);
stripeNodes.add(stripeNode);
nodeGroup.getChildren().add(stripeNode);
data.setNode(stripeNode);
}
@Override
protected synchronized void dataItemChanged(Data<DateTime, EventCluster> data) {
protected void dataItemChanged(Data<DateTime, EventStripe> data) {
//TODO: can we use this to help with local detail level adjustment -jm
throw new UnsupportedOperationException("Not supported yet."); // NON-NLS //To change body of generated methods, choose Tools | Templates.
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
@Override
protected synchronized void dataItemRemoved(Data<DateTime, EventCluster> data, Series<DateTime, EventCluster> series) {
EventCluster eventCluster = data.getYValue();
bundles.removeAll(eventCluster);
EventStripe removedStripe = stripeDescMap.remove(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()));
EventStripeNode removedNode = stripeNodeMap.remove(removedStripe);
nodeGroup.getChildren().remove(removedNode);
protected void dataItemRemoved(Data<DateTime, EventStripe> data, Series<DateTime, EventStripe> series) {
EventStripe removedStripe = data.getYValue();
bundles.removeAll(removedStripe);
EventStripeNode removedNode = (EventStripeNode) data.getNode();
stripeNodes.removeAll(removedNode);
nodeGroup.getChildren().removeAll(removedNode);
data.setNode(null);
}
@Override
protected synchronized void layoutPlotChildren() {
protected void layoutPlotChildren() {
setCursor(Cursor.WAIT);
maxY.set(0);
if (bandByType.get()) {
stripeNodeMap.values().stream()
.collect(Collectors.groupingBy(EventStripeNode::getEventType)).values()
.forEach(inputNodes -> {
List<EventStripeNode> stripeNodes = inputNodes.stream()
.sorted(Comparator.comparing(EventStripeNode::getStartMillis))
.collect(Collectors.toList());
activeQuickHidefilters = getController().getQuickHideFilters().stream()
.filter(AbstractFilter::isActive)
.map(DescriptionFilter::getDescription)
.collect(Collectors.toSet());
maxY.set(layoutEventBundleNodes(stripeNodes, maxY.get()));
});
if (bandByType.get()) {
sortedStripeNodes.stream()
.collect(Collectors.groupingBy(EventStripeNode::getEventType)).values()
.forEach(inputNodes -> maxY.set(layoutEventBundleNodes(inputNodes, maxY.get())));
} else {
List<EventStripeNode> stripeNodes = stripeNodeMap.values().stream()
.sorted(Comparator.comparing(EventStripeNode::getStartMillis))
.collect(Collectors.toList());
maxY.set(layoutEventBundleNodes(stripeNodes, 0));
maxY.set(layoutEventBundleNodes(sortedStripeNodes.sorted(Comparator.comparing(EventStripeNode::getStartMillis)), 0));
}
layoutProjectionMap();
setCursor(null);
}
@Override
protected synchronized void seriesAdded(Series<DateTime, EventCluster> series, int i) {
for (int j = 0; j < series.getData().size(); j++) {
dataItemAdded(series, j, series.getData().get(j));
}
seriesList.add(series);
protected void seriesAdded(Series<DateTime, EventStripe> series, int i) {
}
@Override
protected synchronized void seriesRemoved(Series<DateTime, EventCluster> series) {
for (int j = 0; j < series.getData().size(); j++) {
dataItemRemoved(series.getData().get(j), series);
protected void seriesRemoved(Series<DateTime, EventStripe> series) {
for (Data<DateTime, EventStripe> data : series.getData()) {
dataItemRemoved(data, series);
}
seriesList.remove(series);
}
ReadOnlyDoubleProperty maxVScrollProperty() {
@ -414,7 +385,7 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
/**
* @return all the nodes that pass the given predicate
*/
Iterable<EventBundleNodeBase<?, ?, ?>> getNodes(Predicate<EventBundleNodeBase<?, ?, ?>> p) {
synchronized Iterable<EventBundleNodeBase<?, ?, ?>> getNodes(Predicate<EventBundleNodeBase<?, ?, ?>> p) {
//use this recursive function to flatten the tree of nodes into an iterable.
Function<EventBundleNodeBase<?, ?, ?>, Stream<EventBundleNodeBase<?, ?, ?>>> stripeFlattener =
new Function<EventBundleNodeBase<?, ?, ?>, Stream<EventBundleNodeBase<?, ?, ?>>>() {
@ -426,7 +397,7 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
}
};
return stripeNodeMap.values().stream()
return sortedStripeNodes.stream()
.flatMap(stripeFlattener)
.filter(p).collect(Collectors.toList());
}
@ -473,10 +444,6 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
// 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 (EventBundleNodeBase<?, ?, ?> bundleNode : nodes) {
//is the node hiden by a quick hide filter?
@ -526,16 +493,10 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
localMax = Math.max(yTop + h, localMax);
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();
// bundleNode.relocate(xLeft, yTop);
// requestChartLayout();
// //animate node to new position
bundleNode.animateTo(xLeft, yTop);
}
}
}
@ -710,5 +671,4 @@ public final class EventDetailsChart extends XYChart<DateTime, EventCluster> imp
);
}
}
}

View File

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

View File

@ -59,7 +59,6 @@ class EventDescriptionTreeItem extends NavTreeItem {
treeItem.setExpanded(true);
childMap.put(head.getDescription(), treeItem);
getChildren().add(treeItem);
FXCollections.sort(getChildren(), TreeComparator.Description);
}
if (path.isEmpty() == false) {
@ -67,6 +66,18 @@ class EventDescriptionTreeItem extends NavTreeItem {
}
}
void remove(Deque<EventBundle<?>> path) {
EventBundle<?> head = path.removeFirst();
EventDescriptionTreeItem descTreeItem = childMap.get(head.getDescription());
if (path.isEmpty() == false) {
descTreeItem.remove(path);
}
if (descTreeItem.getChildren().isEmpty()) {
childMap.remove(head.getDescription());
getChildren().remove(descTreeItem);
}
}
@Override
public void resort(Comparator<TreeItem<EventBundle<?>>> comp) {
FXCollections.sort(getChildren(), comp);
@ -88,4 +99,5 @@ class EventDescriptionTreeItem extends NavTreeItem {
}
return null;
}
}

View File

@ -52,7 +52,6 @@ class EventTypeTreeItem extends NavTreeItem {
treeItem.setExpanded(true);
childMap.put(head.getDescription(), treeItem);
getChildren().add(treeItem);
FXCollections.sort(getChildren(), comparator);
}
if (path.isEmpty() == false) {
@ -60,6 +59,21 @@ class EventTypeTreeItem extends NavTreeItem {
}
}
void remove(Deque<EventBundle<?>> path) {
EventBundle<?> head = path.removeFirst();
EventDescriptionTreeItem descTreeItem = childMap.get(head.getDescription());
if (descTreeItem != null) {
if (path.isEmpty() == false) {
descTreeItem.remove(path);
} else if (descTreeItem.getChildren().isEmpty()) {
childMap.remove(head.getDescription());
getChildren().remove(descTreeItem);
}
}
}
@Override
public NavTreeItem findTreeItemForEvent(EventBundle<?> t) {
if (t.getEventType().getBaseType() == getValue().getEventType().getBaseType()) {
@ -75,7 +89,8 @@ class EventTypeTreeItem extends NavTreeItem {
}
@Override
public void resort(Comparator<TreeItem<EventBundle<?>>> comp) {
public void resort(Comparator<TreeItem<EventBundle<?>>> comp
) {
FXCollections.sort(getChildren(), comp);
}
}

View File

@ -25,10 +25,10 @@ import java.util.Comparator;
import java.util.Objects;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ListChangeListener;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.Tooltip;
@ -36,6 +36,8 @@ import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
@ -82,9 +84,18 @@ final public class EventsTree extends BorderPane {
this.detailViewPane = detailViewPane;
detailViewPane.setSelectionModel(eventsTree.getSelectionModel());
detailViewPane.getEventBundles().addListener((Observable observable) -> {
setRoot();
detailViewPane.getEventStripes().addListener((ListChangeListener.Change<? extends EventBundle<?>> c) -> {
while (c.next()) {
for (EventBundle<?> bundle : c.getAddedSubList()) {
getRoot().insert(bundle);
}
for (EventBundle<?> bundle : c.getRemoved()) {
getRoot().remove(bundle);
}
}
getRoot().resort(sortByBox.getSelectionModel().getSelectedItem());
});
setRoot();
detailViewPane.getSelectedNodes().addListener((Observable observable) -> {
@ -96,16 +107,17 @@ final public class EventsTree extends BorderPane {
}
private NavTreeItem getRoot() {
return (NavTreeItem) eventsTree.getRoot();
private RootItem getRoot() {
return (RootItem) eventsTree.getRoot();
}
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
private void setRoot() {
RootItem root = new RootItem();
for (EventBundle<?> bundle : detailViewPane.getEventBundles()) {
for (EventBundle<?> bundle : detailViewPane.getEventStripes()) {
root.insert(bundle);
}
root.resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
eventsTree.setRoot(root);
}
@ -118,7 +130,7 @@ final public class EventsTree extends BorderPane {
sortByBox.getItems().setAll(Arrays.asList(TreeComparator.Description, TreeComparator.Count));
sortByBox.getSelectionModel().select(TreeComparator.Description);
sortByBox.getSelectionModel().selectedItemProperty().addListener((Observable o) -> {
getRoot().resort(sortByBox.getSelectionModel().getSelectedItem());
getRoot().resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem()));
});
eventsTree.setShowRoot(false);
eventsTree.setCellFactory((TreeView<EventBundle<?>> p) -> new EventBundleTreeCell());
@ -137,6 +149,7 @@ final public class EventsTree extends BorderPane {
private final Rectangle rect = new Rectangle(24, 24);
private final ImageView imageView = new ImageView();
private InvalidationListener filterStateChangeListener;
SimpleBooleanProperty hidden = new SimpleBooleanProperty(false);
EventBundleTreeCell() {
rect.setArcHeight(5);
@ -163,16 +176,36 @@ final public class EventsTree extends BorderPane {
updateHiddenState(item);
});
registerListeners(controller.getQuickHideFilters(), item);
String text = item.getDescription() + " (" + item.getCount() + ")"; // NON-NLS
TreeItem<EventBundle<?>> parent = getTreeItem().getParent();
if (parent != null && parent.getValue() != null && (parent instanceof EventDescriptionTreeItem)) {
text = StringUtils.substringAfter(text, parent.getValue().getDescription());
String text;
if (getTreeItem() instanceof EventTypeTreeItem) {
text = item.getEventType().getDisplayName();
} else {
text = item.getDescription() + " (" + item.getCount() + ")"; // NON-NLS
TreeItem<EventBundle<?>> parent = getTreeItem().getParent();
if (parent != null && parent.getValue() != null && (parent instanceof EventDescriptionTreeItem)) {
text = StringUtils.substringAfter(text, parent.getValue().getDescription());
}
}
setText(text);
setTooltip(new Tooltip(text));
imageView.setImage(item.getEventType().getFXImage());
setGraphic(new StackPane(rect, imageView));
updateHiddenState(item);
if (getTreeItem() instanceof EventDescriptionTreeItem) {
setOnMouseClicked((MouseEvent event) -> {
if (event.getButton() == MouseButton.SECONDARY) {
if (hidden.get()) {
ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newUnhideDescriptionAction(item.getDescription(), item.getDescriptionLoD())))
.show(EventBundleTreeCell.this, event.getScreenX(), event.getScreenY());
} else {
ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newHideDescriptionAction(item.getDescription(), item.getDescriptionLoD())))
.show(EventBundleTreeCell.this, event.getScreenX(), event.getScreenY());
}
}
});
} else {
setOnMouseClicked(null);
}
}
}
@ -194,10 +227,11 @@ final public class EventsTree extends BorderPane {
private void updateHiddenState(EventBundle<?> item) {
TreeItem<EventBundle<?>> treeItem = getTreeItem();
ContextMenu newMenu;
if (controller.getQuickHideFilters().stream().
hidden.set(controller.getQuickHideFilters().stream().
filter(AbstractFilter::isActive)
.anyMatch(filter -> filter.getDescription().equals(item.getDescription()))) {
.anyMatch(filter -> filter.getDescription().equals(item.getDescription())));
if (hidden.get()) {
if (treeItem != null) {
treeItem.setExpanded(false);
}
@ -205,18 +239,11 @@ final public class EventsTree extends BorderPane {
imageView.setOpacity(HIDDEN_MULTIPLIER);
rect.setStroke(item.getEventType().getColor().deriveColor(0, HIDDEN_MULTIPLIER, 1, HIDDEN_MULTIPLIER));
rect.setFill(item.getEventType().getColor().deriveColor(0, HIDDEN_MULTIPLIER, HIDDEN_MULTIPLIER, 0.1));
newMenu = ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newUnhideDescriptionAction(item.getDescription(), item.getDescriptionLoD())));
} else {
setTextFill(Color.BLACK);
imageView.setOpacity(1);
rect.setStroke(item.getEventType().getColor());
rect.setFill(item.getEventType().getColor().deriveColor(0, 1, 1, 0.1));
newMenu = ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newHideDescriptionAction(item.getDescription(), item.getDescriptionLoD())));
}
if (treeItem instanceof EventDescriptionTreeItem) {
setContextMenu(newMenu);
} else {
setContextMenu(null);
}
}
}

View File

@ -35,4 +35,6 @@ abstract class NavTreeItem extends TreeItem<EventBundle<?>> {
abstract void resort(Comparator<TreeItem<EventBundle<?>>> comp);
abstract NavTreeItem findTreeItemForEvent(EventBundle<?> t);
}

View File

@ -54,19 +54,30 @@ class RootItem extends NavTreeItem {
/**
* Recursive method to add a grouping at a given path.
*
* @param g Group to add
* @param bundle bundle to add
*/
public void insert(EventBundle<?> g) {
public void insert(EventBundle<?> bundle) {
EventTypeTreeItem treeItem = childMap.computeIfAbsent(g.getEventType().getBaseType(),
EventTypeTreeItem treeItem = childMap.computeIfAbsent(bundle.getEventType().getBaseType(),
baseType -> {
EventTypeTreeItem newTreeItem = new EventTypeTreeItem(g);
EventTypeTreeItem newTreeItem = new EventTypeTreeItem(bundle);
newTreeItem.setExpanded(true);
getChildren().add(newTreeItem);
getChildren().sort(TreeComparator.Type);
return newTreeItem;
});
treeItem.insert(getTreePath(g));
treeItem.insert(getTreePath(bundle));
}
void remove(EventBundle<?> bundle) {
EventTypeTreeItem typeTreeItem = childMap.get(bundle.getEventType().getBaseType());
if (typeTreeItem != null) {
typeTreeItem.remove(getTreePath(bundle));
if (typeTreeItem.getChildren().isEmpty()) {
childMap.remove(bundle.getEventType().getBaseType());
getChildren().remove(typeTreeItem);
}
}
}
static Deque< EventBundle<?>> getTreePath(EventBundle<?> g) {
@ -97,4 +108,5 @@ class RootItem extends NavTreeItem {
}
return null;
}
}