mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-18 02:27:42 +00:00
fix description generation and clustering
- rename getAggregatredEvents to getEventClusters - make more members private - improve logic to get subclusters - introduce notion of RelativeDetail for navigating DescriptionLoDs
This commit is contained in:
parent
ce930506bb
commit
bd1e9a58d8
@ -337,7 +337,7 @@ public final class FilteredEventsModel {
|
|||||||
zoom = requestedTypeZoom.get();
|
zoom = requestedTypeZoom.get();
|
||||||
lod = requestedLOD.get();
|
lod = requestedLOD.get();
|
||||||
}
|
}
|
||||||
return repo.getAggregatedEvents(new ZoomParams(range, zoom, filter, lod));
|
return repo.getEventClusters(new ZoomParams(range, zoom, filter, lod));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -347,8 +347,8 @@ public final class FilteredEventsModel {
|
|||||||
* range and pass the requested filter, using the given aggregation
|
* range and pass the requested filter, using the given aggregation
|
||||||
* to control the grouping of events
|
* to control the grouping of events
|
||||||
*/
|
*/
|
||||||
public List<EventCluster> getAggregatedEvents(ZoomParams params) {
|
public List<EventCluster> getEventClusters(ZoomParams params) {
|
||||||
return repo.getAggregatedEvents(params);
|
return repo.getEventClusters(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized public boolean handleContentTagAdded(ContentTagAddedEvent evt) {
|
synchronized public boolean handleContentTagAdded(ContentTagAddedEvent evt) {
|
||||||
|
@ -98,7 +98,7 @@ public class EventsRepository {
|
|||||||
|
|
||||||
private final LoadingCache<Long, TimeLineEvent> idToEventCache;
|
private final LoadingCache<Long, TimeLineEvent> idToEventCache;
|
||||||
private final LoadingCache<ZoomParams, Map<EventType, Long>> eventCountsCache;
|
private final LoadingCache<ZoomParams, Map<EventType, Long>> eventCountsCache;
|
||||||
private final LoadingCache<ZoomParams, List<EventCluster>> aggregateEventsCache;
|
private final LoadingCache<ZoomParams, List<EventCluster>> eventClusterCache;
|
||||||
|
|
||||||
private final ObservableMap<Long, String> datasourcesMap = FXCollections.observableHashMap();
|
private final ObservableMap<Long, String> datasourcesMap = FXCollections.observableHashMap();
|
||||||
private final ObservableMap<Long, String> hashSetMap = FXCollections.observableHashMap();
|
private final ObservableMap<Long, String> hashSetMap = FXCollections.observableHashMap();
|
||||||
@ -146,7 +146,7 @@ public class EventsRepository {
|
|||||||
.maximumSize(1000L)
|
.maximumSize(1000L)
|
||||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||||
.build(CacheLoader.from(eventDB::countEventsByType));
|
.build(CacheLoader.from(eventDB::countEventsByType));
|
||||||
aggregateEventsCache = CacheBuilder.newBuilder()
|
eventClusterCache = CacheBuilder.newBuilder()
|
||||||
.maximumSize(1000L)
|
.maximumSize(1000L)
|
||||||
.expireAfterAccess(10, TimeUnit.MINUTES
|
.expireAfterAccess(10, TimeUnit.MINUTES
|
||||||
).build(CacheLoader.from(eventDB::getClusteredEvents));
|
).build(CacheLoader.from(eventDB::getClusteredEvents));
|
||||||
@ -206,8 +206,8 @@ public class EventsRepository {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized public List<EventCluster> getAggregatedEvents(ZoomParams params) {
|
synchronized public List<EventCluster> getEventClusters(ZoomParams params) {
|
||||||
return aggregateEventsCache.getUnchecked(params);
|
return eventClusterCache.getUnchecked(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized public Map<EventType, Long> countEvents(ZoomParams params) {
|
synchronized public Map<EventType, Long> countEvents(ZoomParams params) {
|
||||||
@ -218,7 +218,7 @@ public class EventsRepository {
|
|||||||
minCache.invalidateAll();
|
minCache.invalidateAll();
|
||||||
maxCache.invalidateAll();
|
maxCache.invalidateAll();
|
||||||
eventCountsCache.invalidateAll();
|
eventCountsCache.invalidateAll();
|
||||||
aggregateEventsCache.invalidateAll();
|
eventClusterCache.invalidateAll();
|
||||||
idToEventCache.invalidateAll();
|
idToEventCache.invalidateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +292,7 @@ public class EventsRepository {
|
|||||||
|
|
||||||
synchronized private void invalidateCaches(Set<Long> updatedEventIDs) {
|
synchronized private void invalidateCaches(Set<Long> updatedEventIDs) {
|
||||||
eventCountsCache.invalidateAll();
|
eventCountsCache.invalidateAll();
|
||||||
aggregateEventsCache.invalidateAll();
|
eventClusterCache.invalidateAll();
|
||||||
idToEventCache.invalidateAll(updatedEventIDs);
|
idToEventCache.invalidateAll(updatedEventIDs);
|
||||||
try {
|
try {
|
||||||
tagNames.setAll(autoCase.getSleuthkitCase().getTagNamesInUse());
|
tagNames.setAll(autoCase.getSleuthkitCase().getTagNamesInUse());
|
||||||
|
@ -1,14 +1,29 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
* Autopsy Forensic Browser
|
||||||
* To change this template file, choose Tools | Templates
|
*
|
||||||
* and open the template in the editor.
|
* Copyright 2015 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import static java.util.Objects.nonNull;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -61,6 +76,7 @@ import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter;
|
|||||||
import org.sleuthkit.autopsy.timeline.filters.RootFilter;
|
import org.sleuthkit.autopsy.timeline.filters.RootFilter;
|
||||||
import org.sleuthkit.autopsy.timeline.filters.TypeFilter;
|
import org.sleuthkit.autopsy.timeline.filters.TypeFilter;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||||
|
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
|
||||||
@ -89,11 +105,11 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
b.setVisible(show);
|
b.setVisible(show);
|
||||||
b.setManaged(show);
|
b.setManaged(show);
|
||||||
}
|
}
|
||||||
Map<EventType, DropShadow> dropShadowMap = new HashMap<>();
|
private final Map<EventType, DropShadow> dropShadowMap = new HashMap<>();
|
||||||
final Color evtColor;
|
final Color evtColor;
|
||||||
|
|
||||||
private final S parentNode;
|
private final S parentNode;
|
||||||
DescriptionVisibility descrVis;
|
private DescriptionVisibility descrVis;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pane that contains AggregateEventNodes of any 'subevents' if they are
|
* Pane that contains AggregateEventNodes of any 'subevents' if they are
|
||||||
@ -102,7 +118,11 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
* //TODO: move more of the control of subnodes/events here and out of
|
* //TODO: move more of the control of subnodes/events here and out of
|
||||||
* EventDetail Chart
|
* EventDetail Chart
|
||||||
*/
|
*/
|
||||||
final Pane subNodePane = new Pane();
|
private final Pane subNodePane = new Pane();
|
||||||
|
|
||||||
|
Pane getSubNodePane() {
|
||||||
|
return subNodePane;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ImageView used to show the icon for this node's event's type
|
* The ImageView used to show the icon for this node's event's type
|
||||||
@ -121,16 +141,28 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
|
|
||||||
private final T eventBundle;
|
private final T eventBundle;
|
||||||
private final EventDetailChart chart;
|
private final EventDetailChart chart;
|
||||||
final SleuthkitCase sleuthkitCase;
|
private final SleuthkitCase sleuthkitCase;
|
||||||
final FilteredEventsModel eventsModel;
|
|
||||||
|
|
||||||
final Button plusButton;
|
SleuthkitCase getSleuthkitCase() {
|
||||||
final Button minusButton;
|
return sleuthkitCase;
|
||||||
|
}
|
||||||
|
|
||||||
final SimpleObjectProperty<DescriptionLOD> descLOD = new SimpleObjectProperty<>();
|
FilteredEventsModel getEventsModel() {
|
||||||
|
return eventsModel;
|
||||||
|
}
|
||||||
|
private final FilteredEventsModel eventsModel;
|
||||||
|
|
||||||
|
private final Button plusButton;
|
||||||
|
private final Button minusButton;
|
||||||
|
|
||||||
|
private final SimpleObjectProperty<DescriptionLOD> descLOD = new SimpleObjectProperty<>();
|
||||||
final HBox header;
|
final HBox header;
|
||||||
|
|
||||||
final Region spacer = new Region();
|
Region getSpacer() {
|
||||||
|
return spacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Region spacer = new Region();
|
||||||
|
|
||||||
private final CollapseClusterAction collapseClusterAction;
|
private final CollapseClusterAction collapseClusterAction;
|
||||||
private final ExpandClusterAction expandClusterAction;
|
private final ExpandClusterAction expandClusterAction;
|
||||||
@ -160,9 +192,7 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
configureLODButton(minusButton);
|
configureLODButton(minusButton);
|
||||||
|
|
||||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
header = new HBox(getDescrLabel(), getCountLabel(), hashIV, tagIV, /*
|
header = new HBox(getDescrLabel(), getCountLabel(), hashIV, tagIV, minusButton, plusButton);
|
||||||
* spacer,
|
|
||||||
*/ minusButton, plusButton);
|
|
||||||
|
|
||||||
header.setMinWidth(USE_PREF_SIZE);
|
header.setMinWidth(USE_PREF_SIZE);
|
||||||
header.setPadding(new Insets(2, 5, 2, 5));
|
header.setPadding(new Insets(2, 5, 2, 5));
|
||||||
@ -244,12 +274,17 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
show(plusButton, showControls);
|
show(plusButton, showControls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make a new filter intersecting the global filter with description and
|
||||||
|
* type filters to restrict sub-clusters
|
||||||
|
*
|
||||||
|
*/
|
||||||
RootFilter getSubClusterFilter() {
|
RootFilter getSubClusterFilter() {
|
||||||
RootFilter combinedFilter = eventsModel.filterProperty().get().copyOf();
|
RootFilter subClusterFilter = eventsModel.filterProperty().get().copyOf();
|
||||||
//make a new filter intersecting the global filter with description and type filters to restrict sub-clusters
|
subClusterFilter.getSubFilters().addAll(
|
||||||
combinedFilter.getSubFilters().addAll(new DescriptionFilter(getEventBundle().getDescriptionLOD(), getDescription()),
|
new DescriptionFilter(getEventBundle().getDescriptionLOD(), getDescription()),
|
||||||
new TypeFilter(getEventType()));
|
new TypeFilter(getEventType()));
|
||||||
return combinedFilter;
|
return subClusterFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract Collection<T> makeBundlesFromClusters(List<EventCluster> eventClusters);
|
abstract Collection<T> makeBundlesFromClusters(List<EventCluster> eventClusters);
|
||||||
@ -324,59 +359,84 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* loads sub-clusters at the given Description LOD
|
* loads sub-bundles at the given Description LOD, continues
|
||||||
*
|
*
|
||||||
* @param newDescriptionLOD
|
* @param requestedDescrLoD
|
||||||
|
* @param expand
|
||||||
*/
|
*/
|
||||||
final synchronized void loadSubClusters(DescriptionLOD newDescriptionLOD) {
|
private synchronized void loadSubBundles(DescriptionLOD.RelativeDetail relativeDetail) {
|
||||||
subNodePane.getChildren().clear();
|
subNodePane.getChildren().clear();
|
||||||
if (newDescriptionLOD == getEventBundle().getDescriptionLOD()) {
|
if (descLOD.get().withRelativeDetail(relativeDetail) == getEventBundle().getDescriptionLOD()) {
|
||||||
|
descLOD.set(getEventBundle().getDescriptionLOD());
|
||||||
showSpans(true);
|
showSpans(true);
|
||||||
getChart().setRequiresLayout(true);
|
chart.setRequiresLayout(true);
|
||||||
getChart().requestChartLayout();
|
chart.requestChartLayout();
|
||||||
} else {
|
} else {
|
||||||
showSpans(false);
|
showSpans(false);
|
||||||
RootFilter combinedFilter = getSubClusterFilter();
|
|
||||||
|
|
||||||
//make a new end inclusive span (to 'filter' with)
|
// make new ZoomParams to query with
|
||||||
final Interval span = new Interval(getEventBundle().getStartMillis(), getEventBundle().getEndMillis() + 1000);
|
final RootFilter subClusterFilter = getSubClusterFilter();
|
||||||
|
/*
|
||||||
|
* We need to extend end time because for the query by one second,
|
||||||
|
* because it is treated as an open interval but we want to include
|
||||||
|
* events at exactly the time of the last event in this cluster
|
||||||
|
*/
|
||||||
|
final Interval subClusterSpan = new Interval(getEventBundle().getStartMillis(), getEventBundle().getEndMillis() + 1000);
|
||||||
|
final EventTypeZoomLevel eventTypeZoomLevel = eventsModel.eventTypeZoomProperty().get();
|
||||||
|
final ZoomParams zoomParams = new ZoomParams(subClusterSpan, eventTypeZoomLevel, subClusterFilter, getDescLOD());
|
||||||
|
|
||||||
//make a task to load the subnodes
|
LoggedTask<List<S>> loggedTask;
|
||||||
LoggedTask<List<S>> loggedTask = new LoggedTask<List<S>>(
|
loggedTask = new LoggedTask<List<S>>(
|
||||||
NbBundle.getMessage(this.getClass(), "AggregateEventNode.loggedTask.name"), true) {
|
NbBundle.getMessage(this.getClass(), "AggregateEventNode.loggedTask.name"), true) {
|
||||||
|
private Collection<T> bundles;
|
||||||
|
private volatile DescriptionLOD loadedDescriptionLoD = getDescLOD().withRelativeDetail(relativeDetail);
|
||||||
|
private DescriptionLOD next = loadedDescriptionLoD;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<S> call() throws Exception {
|
protected List<S> call() throws Exception {
|
||||||
//query for the sub-clusters
|
do {
|
||||||
List<EventCluster> aggregatedEvents = eventsModel.getAggregatedEvents(new ZoomParams(span,
|
loadedDescriptionLoD = next;
|
||||||
eventsModel.eventTypeZoomProperty().get(),
|
if (loadedDescriptionLoD == getEventBundle().getDescriptionLOD()) {
|
||||||
combinedFilter,
|
return Collections.emptyList();
|
||||||
newDescriptionLOD));
|
}
|
||||||
|
bundles = loadBundles();
|
||||||
|
next = loadedDescriptionLoD.withRelativeDetail(relativeDetail);
|
||||||
|
} while (bundles.size() == 1 && nonNull(next));
|
||||||
|
|
||||||
return makeBundlesFromClusters(aggregatedEvents).stream()
|
// return list of AbstractDetailViewNodes representing sub-bundles
|
||||||
.map(aggEvent -> {
|
return bundles.stream()
|
||||||
return getNodeForCluser(aggEvent);
|
.map(AbstractDetailViewNode.this::getNodeForBundle)
|
||||||
}).collect(Collectors.toList()); // return list of AggregateEventNodes representing subclusters
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<T> loadBundles() {
|
||||||
|
return makeBundlesFromClusters(eventsModel.getEventClusters(zoomParams.withDescrLOD(loadedDescriptionLoD)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void succeeded() {
|
protected void succeeded() {
|
||||||
|
chart.setCursor(Cursor.WAIT);
|
||||||
try {
|
try {
|
||||||
getChart().setCursor(Cursor.WAIT);
|
List<S> subBundleNodes = get();
|
||||||
|
if (subBundleNodes.isEmpty()) {
|
||||||
|
showSpans(true);
|
||||||
|
} else {
|
||||||
|
showSpans(false);
|
||||||
|
}
|
||||||
|
descLOD.set(loadedDescriptionLoD);
|
||||||
//assign subNodes and request chart layout
|
//assign subNodes and request chart layout
|
||||||
subNodePane.getChildren().setAll(get());
|
subNodePane.getChildren().setAll(subBundleNodes);
|
||||||
setDescriptionVisibility(descrVis);
|
chart.setRequiresLayout(true);
|
||||||
getChart().setRequiresLayout(true);
|
chart.requestChartLayout();
|
||||||
getChart().requestChartLayout();
|
|
||||||
getChart().setCursor(null);
|
|
||||||
} catch (InterruptedException | ExecutionException ex) {
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Error loading subnodes", ex);
|
LOGGER.log(Level.SEVERE, "Error loading subnodes", ex);
|
||||||
}
|
}
|
||||||
|
chart.setCursor(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//start task
|
//start task
|
||||||
getChart().getController().monitorTask(loggedTask);
|
chart.getController().monitorTask(loggedTask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,7 +450,7 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
this.descrVis = descrVis;
|
this.descrVis = descrVis;
|
||||||
final int size = getEventBundle().getEventIDs().size();
|
final int size = getEventBundle().getEventIDs().size();
|
||||||
|
|
||||||
switch (descrVis) {
|
switch (this.descrVis) {
|
||||||
case COUNT_ONLY:
|
case COUNT_ONLY:
|
||||||
descrLabel.setText("");
|
descrLabel.setText("");
|
||||||
countLabel.setText(String.valueOf(size));
|
countLabel.setText(String.valueOf(size));
|
||||||
@ -411,7 +471,7 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract S getNodeForCluser(T cluster);
|
abstract S getNodeForBundle(T bundle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* event handler used for mouse events on {@link AggregateEventNode}s
|
* event handler used for mouse events on {@link AggregateEventNode}s
|
||||||
@ -432,10 +492,10 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
} else if (t.isShortcutDown()) {
|
} else if (t.isShortcutDown()) {
|
||||||
chart.selectedNodes.removeAll(AbstractDetailViewNode.this);
|
chart.selectedNodes.removeAll(AbstractDetailViewNode.this);
|
||||||
} else if (t.getClickCount() > 1) {
|
} else if (t.getClickCount() > 1) {
|
||||||
final DescriptionLOD next = descLOD.get().next();
|
final DescriptionLOD next = descLOD.get().moreDetailed();
|
||||||
if (next != null) {
|
if (next != null) {
|
||||||
loadSubClusters(next);
|
loadSubBundles(DescriptionLOD.RelativeDetail.MORE);
|
||||||
descLOD.set(next);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chart.selectedNodes.setAll(AbstractDetailViewNode.this);
|
chart.selectedNodes.setAll(AbstractDetailViewNode.this);
|
||||||
@ -461,14 +521,15 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
|
|
||||||
private class ExpandClusterAction extends Action {
|
private class ExpandClusterAction extends Action {
|
||||||
|
|
||||||
public ExpandClusterAction() {
|
ExpandClusterAction() {
|
||||||
super("Expand");
|
super("Expand");
|
||||||
|
|
||||||
setGraphic(new ImageView(PLUS));
|
setGraphic(new ImageView(PLUS));
|
||||||
setEventHandler((ActionEvent t) -> {
|
setEventHandler((ActionEvent t) -> {
|
||||||
final DescriptionLOD next = descLOD.get().next();
|
final DescriptionLOD next = descLOD.get().moreDetailed();
|
||||||
if (next != null) {
|
if (next != null) {
|
||||||
loadSubClusters(next);
|
loadSubBundles(DescriptionLOD.RelativeDetail.MORE);
|
||||||
descLOD.set(next);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
disabledProperty().bind(descLOD.isEqualTo(DescriptionLOD.FULL));
|
disabledProperty().bind(descLOD.isEqualTo(DescriptionLOD.FULL));
|
||||||
@ -477,15 +538,14 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
|||||||
|
|
||||||
private class CollapseClusterAction extends Action {
|
private class CollapseClusterAction extends Action {
|
||||||
|
|
||||||
public CollapseClusterAction() {
|
CollapseClusterAction() {
|
||||||
super("Collapse");
|
super("Collapse");
|
||||||
|
|
||||||
setGraphic(new ImageView(MINUS));
|
setGraphic(new ImageView(MINUS));
|
||||||
setEventHandler((ActionEvent t) -> {
|
setEventHandler((ActionEvent t) -> {
|
||||||
final DescriptionLOD previous = descLOD.get().previous();
|
final DescriptionLOD previous = descLOD.get().lessDetailed();
|
||||||
if (previous != null) {
|
if (previous != null) {
|
||||||
loadSubClusters(previous);
|
loadSubBundles(DescriptionLOD.RelativeDetail.LESS);
|
||||||
descLOD.set(previous);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
disabledProperty().bind(descLOD.isEqualTo(getEventBundle().getDescriptionLOD()));
|
disabledProperty().bind(descLOD.isEqualTo(getEventBundle().getDescriptionLOD()));
|
||||||
|
@ -66,8 +66,8 @@ public class EventClusterNode extends AbstractDetailViewNode<EventCluster, Event
|
|||||||
minWidthProperty().bind(spanRegion.widthProperty());
|
minWidthProperty().bind(spanRegion.widthProperty());
|
||||||
header.setPrefWidth(USE_COMPUTED_SIZE);
|
header.setPrefWidth(USE_COMPUTED_SIZE);
|
||||||
|
|
||||||
final BorderPane borderPane = new BorderPane(subNodePane, header, null, null, null);
|
final BorderPane borderPane = new BorderPane(getSubNodePane(), header, null, null, null);
|
||||||
BorderPane.setAlignment(subNodePane, Pos.TOP_LEFT);
|
BorderPane.setAlignment(getSubNodePane(), Pos.TOP_LEFT);
|
||||||
borderPane.setPrefWidth(USE_COMPUTED_SIZE);
|
borderPane.setPrefWidth(USE_COMPUTED_SIZE);
|
||||||
|
|
||||||
getChildren().addAll(spanRegion, borderPane);
|
getChildren().addAll(spanRegion, borderPane);
|
||||||
@ -86,8 +86,8 @@ public class EventClusterNode extends AbstractDetailViewNode<EventCluster, Event
|
|||||||
if (!getEventCluster().getEventIDsWithHashHits().isEmpty()) {
|
if (!getEventCluster().getEventIDsWithHashHits().isEmpty()) {
|
||||||
hashSetCounts = new HashMap<>();
|
hashSetCounts = new HashMap<>();
|
||||||
try {
|
try {
|
||||||
for (TimeLineEvent tle : eventsModel.getEventsById(getEventCluster().getEventIDsWithHashHits())) {
|
for (TimeLineEvent tle : getEventsModel().getEventsById(getEventCluster().getEventIDsWithHashHits())) {
|
||||||
Set<String> hashSetNames = sleuthkitCase.getAbstractFileById(tle.getFileID()).getHashSetNames();
|
Set<String> hashSetNames = getSleuthkitCase().getAbstractFileById(tle.getFileID()).getHashSetNames();
|
||||||
for (String hashSetName : hashSetNames) {
|
for (String hashSetName : hashSetNames) {
|
||||||
hashSetCounts.merge(hashSetName, 1L, Long::sum);
|
hashSetCounts.merge(hashSetName, 1L, Long::sum);
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ public class EventClusterNode extends AbstractDetailViewNode<EventCluster, Event
|
|||||||
|
|
||||||
Map<String, Long> tagCounts = new HashMap<>();
|
Map<String, Long> tagCounts = new HashMap<>();
|
||||||
if (!getEventCluster().getEventIDsWithTags().isEmpty()) {
|
if (!getEventCluster().getEventIDsWithTags().isEmpty()) {
|
||||||
tagCounts.putAll(eventsModel.getTagCountsByTagName(getEventCluster().getEventIDsWithTags()));
|
tagCounts.putAll(getEventsModel().getTagCountsByTagName(getEventCluster().getEventIDsWithTags()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ public class EventClusterNode extends AbstractDetailViewNode<EventCluster, Event
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
EventClusterNode getNodeForCluser(EventCluster cluster) {
|
EventClusterNode getNodeForBundle(EventCluster cluster) {
|
||||||
return new EventClusterNode(cluster, this, getChart());
|
return new EventClusterNode(cluster, this, getChart());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ import javafx.scene.layout.Region;
|
|||||||
import static javafx.scene.layout.Region.USE_PREF_SIZE;
|
import static javafx.scene.layout.Region.USE_PREF_SIZE;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import org.sleuthkit.autopsy.coreutils.ColorUtilities;
|
import org.sleuthkit.autopsy.coreutils.ColorUtilities;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
|
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
|
||||||
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
|
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
|
||||||
import static org.sleuthkit.autopsy.timeline.ui.detailview.AbstractDetailViewNode.show;
|
import static org.sleuthkit.autopsy.timeline.ui.detailview.AbstractDetailViewNode.show;
|
||||||
@ -28,14 +27,12 @@ import static org.sleuthkit.autopsy.timeline.ui.detailview.AbstractDetailViewNod
|
|||||||
*/
|
*/
|
||||||
public class EventStripeNode extends AbstractDetailViewNode<EventStripe, EventStripeNode> {
|
public class EventStripeNode extends AbstractDetailViewNode<EventStripe, EventStripeNode> {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(EventClusterNode.class.getName());
|
|
||||||
|
|
||||||
private final HBox rangesHBox = new HBox();
|
private final HBox rangesHBox = new HBox();
|
||||||
|
|
||||||
EventStripeNode(EventStripe eventStripe, EventStripeNode parentNode, EventDetailChart chart) {
|
EventStripeNode(EventStripe eventStripe, EventStripeNode parentNode, EventDetailChart chart) {
|
||||||
super(chart, eventStripe, parentNode);
|
super(chart, eventStripe, parentNode);
|
||||||
minWidthProperty().bind(rangesHBox.widthProperty());
|
minWidthProperty().bind(rangesHBox.widthProperty());
|
||||||
final VBox internalVBox = new VBox(header, subNodePane);
|
final VBox internalVBox = new VBox(header, getSubNodePane());
|
||||||
internalVBox.setAlignment(Pos.CENTER_LEFT);
|
internalVBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
for (Range<Long> range : eventStripe.getRanges()) {
|
for (Range<Long> range : eventStripe.getRanges()) {
|
||||||
@ -57,7 +54,7 @@ public class EventStripeNode extends AbstractDetailViewNode<EventStripe, EventSt
|
|||||||
@Override
|
@Override
|
||||||
void showDescriptionLoDControls(final boolean showControls) {
|
void showDescriptionLoDControls(final boolean showControls) {
|
||||||
super.showDescriptionLoDControls(showControls);
|
super.showDescriptionLoDControls(showControls);
|
||||||
show(spacer, showControls);
|
show(getSpacer(), showControls);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -105,8 +102,7 @@ public class EventStripeNode extends AbstractDetailViewNode<EventStripe, EventSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
EventStripeNode getNodeForCluser(EventStripe cluster) {
|
EventStripeNode getNodeForBundle(EventStripe cluster) {
|
||||||
return new EventStripeNode(cluster, this, getChart());
|
return new EventStripeNode(cluster, this, getChart());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.timeline.zooming;
|
|||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Enumeration of all description levels of detail.
|
||||||
*/
|
*/
|
||||||
public enum DescriptionLOD {
|
public enum DescriptionLOD {
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ public enum DescriptionLOD {
|
|||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DescriptionLOD next() {
|
public DescriptionLOD moreDetailed() {
|
||||||
try {
|
try {
|
||||||
return values()[ordinal() + 1];
|
return values()[ordinal() + 1];
|
||||||
} catch (ArrayIndexOutOfBoundsException e) {
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
@ -47,11 +47,31 @@ public enum DescriptionLOD {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DescriptionLOD previous() {
|
public DescriptionLOD lessDetailed() {
|
||||||
try {
|
try {
|
||||||
return values()[ordinal() - 1];
|
return values()[ordinal() - 1];
|
||||||
} catch (ArrayIndexOutOfBoundsException e) {
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DescriptionLOD withRelativeDetail(RelativeDetail relativeDetail) {
|
||||||
|
switch (relativeDetail) {
|
||||||
|
case EQUAL:
|
||||||
|
return this;
|
||||||
|
case MORE:
|
||||||
|
return moreDetailed();
|
||||||
|
case LESS:
|
||||||
|
return lessDetailed();
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown RelativeDetail value " + relativeDetail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RelativeDetail {
|
||||||
|
|
||||||
|
EQUAL,
|
||||||
|
MORE,
|
||||||
|
LESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user