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