mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 18:17:43 +00:00
Merge branch 'develop' of https://github.com/sleuthkit/autopsy into forRC
This commit is contained in:
commit
a81e9bd0c0
@ -188,4 +188,5 @@ public class EventCluster implements EventBundle<EventStripe> {
|
||||
public SortedSet< EventCluster> getClusters() {
|
||||
return ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis)).add(this).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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() + '}';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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.
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,4 +35,6 @@ abstract class NavTreeItem extends TreeItem<EventBundle<?>> {
|
||||
abstract void resort(Comparator<TreeItem<EventBundle<?>>> comp);
|
||||
|
||||
abstract NavTreeItem findTreeItemForEvent(EventBundle<?> t);
|
||||
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user