mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-16 01:37:43 +00:00
WIP
This commit is contained in:
parent
19b6a3ee03
commit
c44febaf93
@ -137,6 +137,7 @@ public class TimeLineController {
|
||||
|
||||
private final Case autoCase;
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private final ObservableList<DescriptionFilter> quickHideMasks = FXCollections.observableArrayList();
|
||||
|
||||
public ObservableList<DescriptionFilter> getQuickHideMasks() {
|
||||
|
@ -6,6 +6,7 @@
|
||||
package org.sleuthkit.autopsy.timeline.datamodel;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||
@ -33,4 +34,5 @@ public interface EventBundle {
|
||||
|
||||
Iterable<Range<Long>> getRanges();
|
||||
|
||||
Optional<EventBundle> getParentBundle();
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.timeline.datamodel;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import org.joda.time.Interval;
|
||||
@ -36,6 +38,13 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||
@Immutable
|
||||
public class EventCluster implements EventBundle {
|
||||
|
||||
final private EventBundle parent;
|
||||
|
||||
@Override
|
||||
public Optional<EventBundle> getParentBundle() {
|
||||
return Optional.ofNullable(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* the smallest time interval containing all the aggregated events
|
||||
*/
|
||||
@ -73,7 +82,7 @@ public class EventCluster implements EventBundle {
|
||||
*/
|
||||
private final Set<Long> hashHits;
|
||||
|
||||
public EventCluster(Interval spanningInterval, EventType type, Set<Long> eventIDs, Set<Long> hashHits, Set<Long> tagged, String description, DescriptionLOD lod) {
|
||||
private EventCluster(Interval spanningInterval, EventType type, Set<Long> eventIDs, Set<Long> hashHits, Set<Long> tagged, String description, DescriptionLOD lod, EventBundle parent) {
|
||||
|
||||
this.span = spanningInterval;
|
||||
this.type = type;
|
||||
@ -82,6 +91,11 @@ public class EventCluster implements EventBundle {
|
||||
this.description = description;
|
||||
this.eventIDs = eventIDs;
|
||||
this.lod = lod;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public EventCluster(Interval spanningInterval, EventType type, Set<Long> eventIDs, Set<Long> hashHits, Set<Long> tagged, String description, DescriptionLOD lod) {
|
||||
this(spanningInterval, type, eventIDs, hashHits, tagged, description, lod, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,30 +105,37 @@ public class EventCluster implements EventBundle {
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartMillis() {
|
||||
return span.getStartMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEndMillis() {
|
||||
return span.getEndMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getEventIDs() {
|
||||
return Collections.unmodifiableSet(eventIDs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getEventIDsWithHashHits() {
|
||||
return Collections.unmodifiableSet(hashHits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getEventIDsWithTags() {
|
||||
return Collections.unmodifiableSet(tagged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventType getEventType() {
|
||||
return type;
|
||||
}
|
||||
@ -162,4 +183,20 @@ public class EventCluster implements EventBundle {
|
||||
return Collections.singletonList(getRange());
|
||||
}
|
||||
|
||||
/**
|
||||
* return a new EventCluster identical to this one, except with the given
|
||||
* EventBundle as the parent.
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* @return a new EventCluster identical to this one, except with the given
|
||||
* EventBundle as the parent.
|
||||
*/
|
||||
public EventCluster withParent(EventBundle parent) {
|
||||
if (Objects.nonNull(this.parent)) {
|
||||
throw new IllegalStateException("Event Cluster already has a parent!");
|
||||
}
|
||||
|
||||
return new EventCluster(span, type, eventIDs, hashHits, tagged, description, lod, parent);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import com.google.common.collect.TreeRangeMap;
|
||||
import com.google.common.collect.TreeRangeSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import org.python.google.common.base.Objects;
|
||||
@ -25,6 +26,13 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||
@Immutable
|
||||
public final class EventStripe implements EventBundle {
|
||||
|
||||
final private EventBundle parent;
|
||||
|
||||
@Override
|
||||
public Optional<EventBundle> getParentBundle() {
|
||||
return Optional.ofNullable(parent);
|
||||
}
|
||||
|
||||
private final RangeSet<Long> spans = TreeRangeSet.create();
|
||||
private final RangeMap<Long, EventCluster> spanMap = TreeRangeMap.create();
|
||||
|
||||
@ -69,6 +77,7 @@ public final class EventStripe implements EventBundle {
|
||||
eventIDs.addAll(cluster.getEventIDs());
|
||||
tagged.addAll(cluster.getEventIDsWithTags());
|
||||
hashHits.addAll(cluster.getEventIDsWithHashHits());
|
||||
parent = cluster.getParentBundle().orElse(null);
|
||||
}
|
||||
|
||||
private EventStripe(EventStripe u, EventStripe v) {
|
||||
@ -85,6 +94,7 @@ public final class EventStripe implements EventBundle {
|
||||
tagged.addAll(v.getEventIDsWithTags());
|
||||
hashHits.addAll(u.getEventIDsWithHashHits());
|
||||
hashHits.addAll(v.getEventIDsWithHashHits());
|
||||
parent = u.getParentBundle().orElse(null);
|
||||
}
|
||||
|
||||
public static EventStripe merge(EventStripe u, EventStripe v) {
|
||||
@ -93,6 +103,7 @@ public final class EventStripe implements EventBundle {
|
||||
Preconditions.checkArgument(Objects.equal(u.description, v.description));
|
||||
Preconditions.checkArgument(Objects.equal(u.lod, v.lod));
|
||||
Preconditions.checkArgument(Objects.equal(u.type, v.type));
|
||||
Preconditions.checkArgument(Objects.equal(u.parent, v.parent));
|
||||
return new EventStripe(u, v);
|
||||
}
|
||||
|
||||
|
@ -14,18 +14,18 @@ import static org.sleuthkit.autopsy.timeline.filters.CompoundFilter.areSubFilter
|
||||
*
|
||||
*/
|
||||
public class DescriptionsExclusionFilter extends IntersectionFilter<DescriptionFilter> {
|
||||
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages("descriptionsExclusionFilter.displayName.text=Exclude Descriptions")
|
||||
public String getDisplayName() {
|
||||
return Bundle.descriptionsExclusionFilter_displayName_text();
|
||||
}
|
||||
|
||||
|
||||
public DescriptionsExclusionFilter() {
|
||||
getDisabledProperty().bind(Bindings.size(getSubFilters()).lessThan(1));
|
||||
setSelected(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DescriptionsExclusionFilter copyOf() {
|
||||
DescriptionsExclusionFilter filterCopy = new DescriptionsExclusionFilter();
|
||||
@ -36,7 +36,7 @@ public class DescriptionsExclusionFilter extends IntersectionFilter<DescriptionF
|
||||
});
|
||||
return filterCopy;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getHTMLReportString() {
|
||||
//move this logic into SaveSnapshot
|
||||
@ -46,12 +46,12 @@ public class DescriptionsExclusionFilter extends IntersectionFilter<DescriptionF
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 7;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
@ -61,18 +61,20 @@ public class DescriptionsExclusionFilter extends IntersectionFilter<DescriptionF
|
||||
return false;
|
||||
}
|
||||
final DescriptionsExclusionFilter other = (DescriptionsExclusionFilter) obj;
|
||||
|
||||
|
||||
if (isSelected() != other.isSelected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return areSubFiltersEqual(this, other);
|
||||
}
|
||||
|
||||
public void addSubFilter(DescriptionFilter hashSetFilter) {
|
||||
if (getSubFilters().contains(hashSetFilter) == false) {
|
||||
getSubFilters().add(hashSetFilter);
|
||||
|
||||
public void addSubFilter(DescriptionFilter descriptionFilter) {
|
||||
if (getSubFilters().contains(descriptionFilter) == false) {
|
||||
getSubFilters().add(descriptionFilter);
|
||||
getSubFilters().sort(Comparator.comparing(DescriptionFilter::getDisplayName));
|
||||
} else {
|
||||
getSubFilters().filtered(descriptionFilter::equals).get(0).setSelected(descriptionFilter.isSelected());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
||||
|
||||
private final CollapseBundleAction collapseClusterAction;
|
||||
private final ExpandClusterAction expandClusterAction;
|
||||
private final EventDetailChart.HideBundleAction hideClusterAction;
|
||||
private final EventDetailChart.HideDescriptionAction hideClusterAction;
|
||||
|
||||
public AbstractDetailViewNode(EventDetailChart chart, T bundle, S parentEventNode) {
|
||||
this.eventBundle = bundle;
|
||||
@ -187,7 +187,7 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
||||
show(tagIV, false);
|
||||
}
|
||||
|
||||
hideClusterAction = chart.new HideBundleAction(getEventBundle());
|
||||
hideClusterAction = chart.new HideDescriptionAction(getDescription(), bundle.getDescriptionLOD());
|
||||
hideButton = ActionUtils.createButton(hideClusterAction, ActionUtils.ActionTextBehavior.HIDE);
|
||||
configureLODButton(hideButton);
|
||||
|
||||
@ -405,15 +405,14 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
||||
final EventTypeZoomLevel eventTypeZoomLevel = eventsModel.eventTypeZoomProperty().get();
|
||||
final ZoomParams zoomParams = new ZoomParams(subClusterSpan, eventTypeZoomLevel, subClusterFilter, getDescLOD());
|
||||
|
||||
LoggedTask<List<S>> loggedTask;
|
||||
loggedTask = new LoggedTask<List<S>>(
|
||||
LoggedTask<Collection<T>> loggedTask = new LoggedTask<Collection<T>>(
|
||||
NbBundle.getMessage(this.getClass(), "AggregateEventNode.loggedTask.name"), true) {
|
||||
private Collection<T> bundles;
|
||||
private volatile DescriptionLOD loadedDescriptionLoD = getDescLOD().withRelativeDetail(relativeDetail);
|
||||
private DescriptionLOD next = loadedDescriptionLoD;
|
||||
|
||||
@Override
|
||||
protected List<S> call() throws Exception {
|
||||
protected Collection<T> call() throws Exception {
|
||||
do {
|
||||
loadedDescriptionLoD = next;
|
||||
if (loadedDescriptionLoD == getEventBundle().getDescriptionLOD()) {
|
||||
@ -427,20 +426,26 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
||||
} while (bundles.size() == 1 && nonNull(next));
|
||||
|
||||
// return list of AbstractDetailViewNodes representing sub-bundles
|
||||
return bundles.stream()
|
||||
.map(AbstractDetailViewNode.this::getNodeForBundle)
|
||||
.collect(Collectors.toList());
|
||||
return bundles;
|
||||
}
|
||||
|
||||
private Collection<T> loadBundles() {
|
||||
return makeBundlesFromClusters(eventsModel.getEventClusters(zoomParams.withDescrLOD(loadedDescriptionLoD)));
|
||||
List<EventCluster> eventClusters
|
||||
= eventsModel.getEventClusters(zoomParams.withDescrLOD(loadedDescriptionLoD)).stream()
|
||||
.map(cluster -> cluster.withParent(getEventBundle()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return makeBundlesFromClusters(eventClusters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void succeeded() {
|
||||
chart.setCursor(Cursor.WAIT);
|
||||
try {
|
||||
List<S> subBundleNodes = get();
|
||||
List<S> subBundleNodes = get().stream()
|
||||
.map(AbstractDetailViewNode.this::getNodeForBundle)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (subBundleNodes.isEmpty()) {
|
||||
showSpans(true);
|
||||
} else {
|
||||
@ -455,7 +460,9 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error loading subnodes", ex);
|
||||
}
|
||||
chart.setCursor(null);
|
||||
|
||||
chart.setCursor(
|
||||
null);
|
||||
}
|
||||
};
|
||||
|
||||
@ -487,7 +494,7 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
||||
case SHOWN:
|
||||
String description = getEventBundle().getDescription();
|
||||
description = getParentNode() != null
|
||||
? " ..." + StringUtils.substringAfter(description, getParentNode().getDescription())
|
||||
? " ... " + StringUtils.substringAfter(description, getParentNode().getDescription())
|
||||
: description;
|
||||
descrLabel.setText(description);
|
||||
countLabel.setText(((size == 1) ? "" : " (" + size + ")")); // NON-NLS
|
||||
@ -495,7 +502,8 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
|
||||
}
|
||||
}
|
||||
|
||||
abstract S getNodeForBundle(T bundle);
|
||||
abstract S
|
||||
getNodeForBundle(T bundle);
|
||||
|
||||
/**
|
||||
* event handler used for mouse events on {@link AggregateEventNode}s
|
||||
|
@ -62,6 +62,7 @@ import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.controlsfx.control.action.Action;
|
||||
import org.joda.time.DateTime;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.LoggedTask;
|
||||
@ -77,6 +78,7 @@ import org.sleuthkit.autopsy.timeline.ui.AbstractVisualization;
|
||||
import org.sleuthkit.autopsy.timeline.ui.countsview.CountsViewPane;
|
||||
import org.sleuthkit.autopsy.timeline.ui.detailview.tree.NavTreeNode;
|
||||
import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||
|
||||
/**
|
||||
* FXML Controller class for a {@link EventDetailChart} based implementation of
|
||||
@ -212,8 +214,8 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
||||
selectedNodes.addListener((Observable observable) -> {
|
||||
highlightedNodes.clear();
|
||||
selectedNodes.stream().forEach((tn) -> {
|
||||
for (DetailViewNode<?> n : chart.getNodes((DetailViewNode<?> t)
|
||||
-> t.getDescription().equals(tn.getDescription()))) {
|
||||
for (DetailViewNode<?> n : chart.getNodes((DetailViewNode<?> t) ->
|
||||
t.getDescription().equals(tn.getDescription()))) {
|
||||
highlightedNodes.add(n);
|
||||
}
|
||||
});
|
||||
@ -236,8 +238,8 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
||||
treeSelectionModel.getSelectedItems().addListener((Observable observable) -> {
|
||||
highlightedNodes.clear();
|
||||
for (TreeItem<NavTreeNode> tn : treeSelectionModel.getSelectedItems()) {
|
||||
for (DetailViewNode<?> n : chart.getNodes((DetailViewNode<?> t)
|
||||
-> t.getDescription().equals(tn.getValue().getDescription()))) {
|
||||
for (DetailViewNode<?> n : chart.getNodes((DetailViewNode<?> t) ->
|
||||
t.getDescription().equals(tn.getValue().getDescription()))) {
|
||||
highlightedNodes.add(n);
|
||||
}
|
||||
}
|
||||
@ -348,7 +350,7 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
||||
layoutDateLabels();
|
||||
updateProgress(1, 1);
|
||||
});
|
||||
return chart.getEventBundles().isEmpty() == false;
|
||||
return eventClusters.isEmpty() == false;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -484,11 +486,11 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
|
||||
|
||||
}
|
||||
|
||||
public EventDetailChart.UnhideBundleAction newUnhideBundleAction(String description) {
|
||||
return chart.new UnhideBundleAction(description);
|
||||
public Action newUnhideDescriptionAction(String description, DescriptionLOD descriptionLoD) {
|
||||
return chart.new UnhideDescriptionAction(description, descriptionLoD);
|
||||
}
|
||||
|
||||
public EventDetailChart.HideBundleAction newHideBundleAction(EventBundle bundle) {
|
||||
return chart.new HideBundleAction(bundle);
|
||||
public Action newHideDescriptionAction(String description, DescriptionLOD descriptionLoD) {
|
||||
return chart.new HideDescriptionAction(description, descriptionLoD);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -169,7 +168,7 @@ public class EventClusterNode extends AbstractDetailViewNode<EventCluster, Event
|
||||
}
|
||||
|
||||
@Override
|
||||
Collection<EventCluster> makeBundlesFromClusters(List<EventCluster> eventClusters) {
|
||||
List<EventCluster> makeBundlesFromClusters(List<EventCluster> eventClusters) {
|
||||
return eventClusters;
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
||||
import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter;
|
||||
import org.sleuthkit.autopsy.timeline.ui.TimeLineChart;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||
|
||||
/**
|
||||
* Custom implementation of {@link XYChart} to graph events on a horizontal
|
||||
@ -512,14 +513,14 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
.collect(Collectors.partitioningBy(node -> getController().getQuickHideMasks().stream()
|
||||
.anyMatch(mask -> mask.getDescription().equals(node.getDescription()))));
|
||||
|
||||
layoutNodesHelper(hiddenPartition.get(true), hiddenPartition.get(false), minY, nodeGroup.getChildren(), 0);
|
||||
layoutNodesHelper(hiddenPartition.get(true), hiddenPartition.get(false), minY, 0);
|
||||
minY = maxY.get();
|
||||
}
|
||||
} else {
|
||||
hiddenPartition = nodeMap.values().stream()
|
||||
.collect(Collectors.partitioningBy(node -> getController().getQuickHideMasks().stream()
|
||||
.anyMatch(mask -> mask.getDescription().equals(node.getDescription()))));
|
||||
layoutNodesHelper(hiddenPartition.get(true), hiddenPartition.get(false), 0, nodeGroup.getChildren(), 0);
|
||||
layoutNodesHelper(hiddenPartition.get(true), hiddenPartition.get(false), 0, 0);
|
||||
}
|
||||
setCursor(null);
|
||||
requiresLayout = false;
|
||||
@ -537,16 +538,20 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
*
|
||||
* @return the double
|
||||
*/
|
||||
private double layoutNodesHelper(List<AbstractDetailViewNode<?, ?>> hiddenNodes, List<AbstractDetailViewNode<?, ?>> shownNodes, double minY, List<Node> children, final double xOffset) {
|
||||
private double layoutNodesHelper(List<AbstractDetailViewNode<?, ?>> hiddenNodes, List<AbstractDetailViewNode<?, ?>> shownNodes, double minY, final double xOffset) {
|
||||
|
||||
hiddenNodes.forEach((AbstractDetailViewNode<?, ?> t) -> {
|
||||
children.remove(t);
|
||||
// children.remove(t);
|
||||
t.setVisible(false);
|
||||
t.setManaged(false);
|
||||
});
|
||||
|
||||
shownNodes.forEach((AbstractDetailViewNode<?, ?> t) -> {
|
||||
if (false == children.contains(t)) {
|
||||
children.add(t);
|
||||
}
|
||||
// if (false == children.contains(t)) {
|
||||
// children.add(t);
|
||||
// }
|
||||
t.setVisible(true);
|
||||
t.setManaged(true);
|
||||
});
|
||||
|
||||
shownNodes.sort(Comparator.comparing(DetailViewNode<?>::getStartMillis));
|
||||
@ -640,17 +645,13 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
double span = 0;
|
||||
@SuppressWarnings("unchecked")
|
||||
List<AbstractDetailViewNode<?, ?>> subNodes = (List<AbstractDetailViewNode<?, ?>>) node.getSubBundleNodes();
|
||||
if (subNodes.isEmpty() == false) {
|
||||
Map<Boolean, List<AbstractDetailViewNode<?, ?>>> hiddenPartition = subNodes.stream()
|
||||
.collect(Collectors.partitioningBy(testNode -> getController().getQuickHideMasks().stream()
|
||||
.anyMatch(mask -> mask.getDescription().equals(testNode.getDescription()))));
|
||||
|
||||
Map<Boolean, List<AbstractDetailViewNode<?, ?>>> hiddenPartition = subNodes.stream()
|
||||
.collect(Collectors.partitioningBy(testNode -> getController().getQuickHideMasks().stream()
|
||||
.anyMatch(mask -> mask.getDescription().equals(testNode.getDescription()))));
|
||||
|
||||
layoutNodesResultHeight = layoutNodesHelper(hiddenPartition.get(true), hiddenPartition.get(false), minY, node.getSubNodes(), rawDisplayPosition);
|
||||
// if (subNodes.isEmpty() == false) {
|
||||
// subNodes.sort(new DetailViewNode.StartTimeComparator());
|
||||
// layoutNodes(subNodes, 0, rawDisplayPosition);
|
||||
// }
|
||||
|
||||
layoutNodesResultHeight = layoutNodesHelper(hiddenPartition.get(true), hiddenPartition.get(false), minY, rawDisplayPosition);
|
||||
}
|
||||
if (alternateLayout.get() == false) {
|
||||
double endX = getXAxis().getDisplayPosition(new DateTime(node.getEndMillis())) - xOffset;
|
||||
span = endX - startX;
|
||||
@ -822,32 +823,36 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
|
||||
c1.applySelectionEffect(selected);
|
||||
}
|
||||
|
||||
class HideBundleAction extends Action {
|
||||
class HideDescriptionAction extends Action {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param description the value of description
|
||||
*/
|
||||
public HideBundleAction(final EventBundle bundle) {
|
||||
HideDescriptionAction(String description, DescriptionLOD descriptionLoD) {
|
||||
super("Hide");
|
||||
setGraphic(new ImageView(HIDE));
|
||||
setEventHandler((ActionEvent t) -> {
|
||||
DescriptionFilter descriptionFilter = new DescriptionFilter(bundle.getDescriptionLOD(), bundle.getDescription(), DescriptionFilter.FilterMode.EXCLUDE);
|
||||
getController().getQuickHideMasks().add(descriptionFilter);
|
||||
getController().getQuickHideMasks().add(
|
||||
new DescriptionFilter(descriptionLoD,
|
||||
description,
|
||||
DescriptionFilter.FilterMode.EXCLUDE));
|
||||
setRequiresLayout(true);
|
||||
requestChartLayout();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class UnhideBundleAction extends Action {
|
||||
class UnhideDescriptionAction extends Action {
|
||||
|
||||
public UnhideBundleAction(String description) {
|
||||
UnhideDescriptionAction(String description, DescriptionLOD descriptionLoD) {
|
||||
|
||||
super("Unhide");
|
||||
setGraphic(new ImageView(SHOW));
|
||||
setEventHandler((ActionEvent t) -> {
|
||||
getController().getQuickHideMasks().removeIf((DescriptionFilter t1) -> t1.getDescription().equals(description));
|
||||
getController().getQuickHideMasks().removeIf(descriptionFilter ->
|
||||
descriptionFilter.getDescriptionLoD().equals(descriptionLoD)
|
||||
&& descriptionFilter.getDescription().equals(description));
|
||||
setRequiresLayout(true);
|
||||
requestChartLayout();
|
||||
});
|
||||
|
@ -19,11 +19,12 @@
|
||||
package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.scene.control.TreeItem;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.EventBundle;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -34,10 +35,14 @@ class EventDescriptionTreeItem extends NavTreeItem {
|
||||
* maps a description to the child item of this item with that description
|
||||
*/
|
||||
private final Map<String, EventDescriptionTreeItem> childMap = new ConcurrentHashMap<>();
|
||||
private final DescriptionLOD descriptionLoD;
|
||||
private final EventBundle bundle;
|
||||
|
||||
public EventBundle getEventBundle() {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
EventDescriptionTreeItem(EventBundle g) {
|
||||
descriptionLoD = g.getDescriptionLOD();
|
||||
bundle = g;
|
||||
setValue(new NavTreeNode(g.getEventType().getBaseType(), g.getDescription(), g.getDescriptionLOD(), g.getEventIDs().size()));
|
||||
}
|
||||
|
||||
@ -46,37 +51,25 @@ class EventDescriptionTreeItem extends NavTreeItem {
|
||||
return getValue().getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(EventBundle g) {
|
||||
NavTreeNode value = getValue();
|
||||
if (value.getType().getBaseType().equals(g.getEventType().getBaseType())
|
||||
&& g.getDescription().startsWith(value.getDescription())) {
|
||||
throw new IllegalArgumentException();
|
||||
public void insert(Deque<EventBundle> path) {
|
||||
EventBundle head = path.removeFirst();
|
||||
EventDescriptionTreeItem treeItem = childMap.get(head.getDescription());
|
||||
if (treeItem == null) {
|
||||
treeItem = new EventDescriptionTreeItem(head);
|
||||
treeItem.setExpanded(true);
|
||||
childMap.put(head.getDescription(), treeItem);
|
||||
getChildren().add(treeItem);
|
||||
FXCollections.sort(getChildren(), TreeComparator.Description);
|
||||
}
|
||||
|
||||
switch (descriptionLoD.getDetailLevelRelativeTo(g.getDescriptionLOD())) {
|
||||
case LESS:
|
||||
EventDescriptionTreeItem get = childMap.get(g.getDescription());
|
||||
if (get == null) {
|
||||
EventDescriptionTreeItem eventDescriptionTreeItem = new EventDescriptionTreeItem(g);
|
||||
childMap.put(g.getDescription(), eventDescriptionTreeItem);
|
||||
getChildren().add(eventDescriptionTreeItem);
|
||||
} else {
|
||||
get.insert(g);
|
||||
}
|
||||
break;
|
||||
case EQUAL:
|
||||
setValue(new NavTreeNode(value.getType().getBaseType(), value.getDescription(), value.getDescriptionLoD(), value.getCount() + g.getEventIDs().size()));
|
||||
break;
|
||||
case MORE:
|
||||
throw new IllegalArgumentException();
|
||||
if (path.isEmpty() == false) {
|
||||
treeItem.insert(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resort(Comparator<TreeItem<NavTreeNode>> comp) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
FXCollections.sort(getChildren(), comp);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,9 +19,9 @@
|
||||
package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.scene.control.TreeItem;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.EventBundle;
|
||||
@ -44,36 +44,21 @@ class EventTypeTreeItem extends NavTreeItem {
|
||||
return getValue().getCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive method to add a grouping at a given path.
|
||||
*
|
||||
* @param path Full path (or subset not yet added) to add
|
||||
* @param g Group to add
|
||||
* @param tree True if it is part of a tree (versus a list)
|
||||
*/
|
||||
@Override
|
||||
public void insert(EventBundle g) {
|
||||
public void insert(Deque<EventBundle> path) {
|
||||
|
||||
EventDescriptionTreeItem treeItem = childMap.get(g.getDescription());
|
||||
EventBundle head = path.removeFirst();
|
||||
EventDescriptionTreeItem treeItem = childMap.get(head.getDescription());
|
||||
if (treeItem == null) {
|
||||
final EventDescriptionTreeItem newTreeItem = new EventDescriptionTreeItem(g);
|
||||
newTreeItem.setExpanded(true);
|
||||
childMap.put(g.getDescription(), newTreeItem);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
synchronized (getChildren()) {
|
||||
getChildren().add(newTreeItem);
|
||||
FXCollections.sort(getChildren(), comparator);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
treeItem.insert(g);
|
||||
treeItem = new EventDescriptionTreeItem(head);
|
||||
treeItem.setExpanded(true);
|
||||
childMap.put(head.getDescription(), treeItem);
|
||||
getChildren().add(treeItem);
|
||||
FXCollections.sort(getChildren(), comparator);
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
NavTreeNode value1 = getValue();
|
||||
setValue(new NavTreeNode(value1.getType().getBaseType(), value1.getType().getBaseType().getDisplayName(),value1.getDescriptionLoD(), childMap.values().stream().mapToInt(EventDescriptionTreeItem::getCount).sum()));
|
||||
});
|
||||
|
||||
if (path.isEmpty() == false) {
|
||||
treeItem.insert(path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -21,10 +21,10 @@ package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.Observable;
|
||||
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,8 +36,10 @@ import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.controlsfx.control.action.ActionUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.timeline.FXMLConstructor;
|
||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
import org.sleuthkit.autopsy.timeline.TimeLineView;
|
||||
@ -83,14 +85,14 @@ public class NavPanel extends BorderPane implements TimeLineView {
|
||||
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private void setRoot() {
|
||||
RootItem root = new RootItem();
|
||||
for (EventBundle bundle : detailViewPane.getEventBundles()) {
|
||||
root.insert(bundle);
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
eventsTree.setRoot(root);
|
||||
});
|
||||
eventsTree.setRoot(root);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -130,8 +132,17 @@ public class NavPanel extends BorderPane implements TimeLineView {
|
||||
@Override
|
||||
protected void updateItem(NavTreeNode item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null) {
|
||||
final String text = item.getDescription() + " (" + item.getCount() + ")"; // NON-NLS
|
||||
if (item == null || empty) {
|
||||
setText(null);
|
||||
setTooltip(null);
|
||||
setGraphic(null);
|
||||
setContextMenu(null);
|
||||
} else {
|
||||
String text = item.getDescription() + " (" + item.getCount() + ")"; // NON-NLS
|
||||
TreeItem<NavTreeNode> 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));
|
||||
Rectangle rect = new Rectangle(24, 24);
|
||||
@ -146,28 +157,34 @@ public class NavPanel extends BorderPane implements TimeLineView {
|
||||
});
|
||||
configureHiddenState(item, rect, imageView);
|
||||
|
||||
} else {
|
||||
setText(null);
|
||||
setTooltip(null);
|
||||
setGraphic(null);
|
||||
setContextMenu(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void configureHiddenState(NavTreeNode item, Rectangle rect, ImageView imageView) {
|
||||
TreeItem<NavTreeNode> treeItem = getTreeItem();
|
||||
ContextMenu newMenu;
|
||||
if (controller.getQuickHideMasks().stream().anyMatch(mask -> mask.getDescription().equals(item.getDescription()))) {
|
||||
setTextFill(Color.gray(0, .6));
|
||||
imageView.setOpacity(.6);
|
||||
rect.setStroke(item.getType().getColor().deriveColor(0, .6, 1, .6));
|
||||
rect.setFill(item.getType().getColor().deriveColor(0, .6, .6, 0.1));
|
||||
setContextMenu(ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newUnhideBundleAction(item.getDescription()))));
|
||||
if (treeItem != null) {
|
||||
treeItem.setExpanded(false);
|
||||
}
|
||||
newMenu = ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newUnhideDescriptionAction(item.getDescription(), item.getDescriptionLod())));
|
||||
} else {
|
||||
setTextFill(Color.BLACK);
|
||||
imageView.setOpacity(1);
|
||||
rect.setStroke(item.getType().getColor());
|
||||
rect.setFill(item.getType().getColor().deriveColor(0, 1, 1, 0.1));
|
||||
// setContextMenu(ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newHideBundleAction(item.getDescription()))));
|
||||
newMenu = ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newHideDescriptionAction(item.getDescription(), item.getDescriptionLod())));
|
||||
}
|
||||
|
||||
if (treeItem instanceof EventDescriptionTreeItem) {
|
||||
setContextMenu(newMenu);
|
||||
} else {
|
||||
setContextMenu(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.EventBundle;
|
||||
*/
|
||||
abstract class NavTreeItem extends TreeItem<NavTreeNode> {
|
||||
|
||||
abstract void insert(EventBundle g);
|
||||
|
||||
// abstract void insert(EventBundle g);
|
||||
abstract int getCount();
|
||||
|
||||
abstract void resort(Comparator<TreeItem<NavTreeNode>> comp);
|
||||
|
@ -29,11 +29,10 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||
@Immutable
|
||||
public class NavTreeNode {
|
||||
|
||||
final private DescriptionLOD descriptionLoD;
|
||||
|
||||
final private EventType type;
|
||||
|
||||
final private String Description;
|
||||
private final DescriptionLOD descriptionLoD;
|
||||
|
||||
|
||||
final private int count;
|
||||
|
||||
@ -44,7 +43,7 @@ public class NavTreeNode {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public DescriptionLOD getDescriptionLoD() {
|
||||
public DescriptionLOD getDescriptionLod() {
|
||||
return descriptionLoD;
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,12 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javafx.application.Platform;
|
||||
import java.util.Optional;
|
||||
import javafx.scene.control.TreeItem;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.EventBundle;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
||||
@ -54,25 +56,31 @@ class RootItem extends NavTreeItem {
|
||||
*
|
||||
* @param g Group to add
|
||||
*/
|
||||
@Override
|
||||
public void insert(EventBundle g) {
|
||||
|
||||
EventTypeTreeItem treeItem = childMap.get(g.getEventType().getBaseType());
|
||||
if (treeItem == null) {
|
||||
final EventTypeTreeItem newTreeItem = new EventTypeTreeItem(g);
|
||||
newTreeItem.setExpanded(true);
|
||||
childMap.put(g.getEventType().getBaseType(), newTreeItem);
|
||||
newTreeItem.insert(g);
|
||||
treeItem = new EventTypeTreeItem(g);
|
||||
treeItem.setExpanded(true);
|
||||
childMap.put(g.getEventType().getBaseType(), treeItem);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
synchronized (getChildren()) {
|
||||
getChildren().add(newTreeItem);
|
||||
getChildren().sort(TreeComparator.Type);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
treeItem.insert(g);
|
||||
getChildren().add(treeItem);
|
||||
getChildren().sort(TreeComparator.Type);
|
||||
}
|
||||
treeItem.insert(getTreePath(g));
|
||||
}
|
||||
|
||||
static Deque<EventBundle> getTreePath(EventBundle g) {
|
||||
Deque<EventBundle> path = new ArrayDeque<>();
|
||||
Optional<EventBundle> p = Optional.of(g);
|
||||
|
||||
while (p.isPresent()) {
|
||||
EventBundle parent = p.get();
|
||||
path.addFirst(parent);
|
||||
p = parent.getParentBundle();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user