quick-hide/filter WIP 3

This commit is contained in:
jmillman 2015-09-23 16:02:57 -04:00
parent 56358568f0
commit b290534f07
13 changed files with 88 additions and 64 deletions

View File

@ -18,7 +18,6 @@
*/ */
package org.sleuthkit.autopsy.coreutils; package org.sleuthkit.autopsy.coreutils;
import java.util.Objects;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.ReadOnlyBooleanWrapper;

View File

@ -1076,6 +1076,7 @@ public class EventDB {
+ "\n GROUP BY interval, " + typeColumn + " , " + descriptionColumn // NON-NLS + "\n GROUP BY interval, " + typeColumn + " , " + descriptionColumn // NON-NLS
+ "\n ORDER BY min(time)"; // NON-NLS + "\n ORDER BY min(time)"; // NON-NLS
System.out.println(query);
// perform query and map results to AggregateEvent objects // perform query and map results to AggregateEvent objects
List<EventCluster> events = new ArrayList<>(); List<EventCluster> events = new ArrayList<>();

View File

@ -89,6 +89,4 @@ public abstract class CompoundFilter<SubFilterType extends Filter> extends Abstr
} }
return true; return true;
} }
} }

View File

@ -48,7 +48,7 @@ public class DescriptionFilter extends AbstractFilter {
@Override @Override
public String getDisplayName() { public String getDisplayName() {
return getFilterMode().getDisplayName() + " Description"; return getFilterMode().getDisplayName() + " " + getDescription();
} }
@Override @Override
@ -86,10 +86,6 @@ public class DescriptionFilter extends AbstractFilter {
} }
} }
public boolean test(String t) {
return (filterMode == FilterMode.INCLUDE) == getDescription().equals(t);
}
@Override @Override
public int hashCode() { public int hashCode() {
int hash = 7; int hash = 7;

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

View File

@ -304,7 +304,7 @@ public abstract class AbstractVisualization<X, Y, N, C extends XYChart<X, Y> & T
//x-positions (pixels) of the current branch and leaf labels //x-positions (pixels) of the current branch and leaf labels
double leafLabelX = 0; double leafLabelX = 0;
if (dateTime.branch.equals("")) { if (dateTime.branch.isEmpty()) {
//if there is only one part to the date (ie only year), just add a label for each tick //if there is only one part to the date (ie only year), just add a label for each tick
for (Axis.TickMark<X> t : tickMarks) { for (Axis.TickMark<X> t : tickMarks) {
assignLeafLabel(new TwoPartDateTime(getTickMarkLabel(t.getValue())).leaf, assignLeafLabel(new TwoPartDateTime(getTickMarkLabel(t.getValue())).leaf,

View File

@ -85,7 +85,7 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
static final Image HASH_PIN = new Image("/org/sleuthkit/autopsy/images/hashset_hits.png"); static final Image HASH_PIN = new Image("/org/sleuthkit/autopsy/images/hashset_hits.png");
static final Image PLUS = new Image("/org/sleuthkit/autopsy/timeline/images/plus-button.png"); // NON-NLS static final Image PLUS = new Image("/org/sleuthkit/autopsy/timeline/images/plus-button.png"); // NON-NLS
static final Image MINUS = new Image("/org/sleuthkit/autopsy/timeline/images/minus-button.png"); // NON-NLS static final Image MINUS = new Image("/org/sleuthkit/autopsy/timeline/images/minus-button.png"); // NON-NLS
static final Image HIDE = new Image("/org/sleuthkit/autopsy/timeline/images/funnel.png"); // NON-NLS
static final Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); // NON-NLS static final Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); // NON-NLS
static final CornerRadii CORNER_RADII = new CornerRadii(3); static final CornerRadii CORNER_RADII = new CornerRadii(3);
/** /**
@ -166,9 +166,9 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
private final Region spacer = new Region(); private final Region spacer = new Region();
private final CollapseClusterAction collapseClusterAction; private final CollapseBundleAction collapseClusterAction;
private final ExpandClusterAction expandClusterAction; private final ExpandClusterAction expandClusterAction;
private final HideClusterAction hideClusterAction; private final EventDetailChart.HideBundleAction hideClusterAction;
public AbstractDetailViewNode(EventDetailChart chart, T bundle, S parentEventNode) { public AbstractDetailViewNode(EventDetailChart chart, T bundle, S parentEventNode) {
this.eventBundle = bundle; this.eventBundle = bundle;
@ -186,7 +186,7 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
show(tagIV, false); show(tagIV, false);
} }
hideClusterAction = new HideClusterAction(); hideClusterAction = chart.new HideBundleAction(getEventBundle());
hideButton = ActionUtils.createButton(hideClusterAction, ActionUtils.ActionTextBehavior.HIDE); hideButton = ActionUtils.createButton(hideClusterAction, ActionUtils.ActionTextBehavior.HIDE);
configureLODButton(hideButton); configureLODButton(hideButton);
@ -194,7 +194,7 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
plusButton = ActionUtils.createButton(expandClusterAction, ActionUtils.ActionTextBehavior.HIDE); plusButton = ActionUtils.createButton(expandClusterAction, ActionUtils.ActionTextBehavior.HIDE);
configureLODButton(plusButton); configureLODButton(plusButton);
collapseClusterAction = new CollapseClusterAction(); collapseClusterAction = new CollapseBundleAction();
minusButton = ActionUtils.createButton(collapseClusterAction, ActionUtils.ActionTextBehavior.HIDE); minusButton = ActionUtils.createButton(collapseClusterAction, ActionUtils.ActionTextBehavior.HIDE);
configureLODButton(minusButton); configureLODButton(minusButton);
@ -545,9 +545,9 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
} }
} }
private class CollapseClusterAction extends Action { private class CollapseBundleAction extends Action {
CollapseClusterAction() { CollapseBundleAction() {
super("Collapse"); super("Collapse");
setGraphic(new ImageView(MINUS)); setGraphic(new ImageView(MINUS));
@ -561,20 +561,4 @@ public abstract class AbstractDetailViewNode< T extends EventBundle, S extends A
} }
} }
private class HideClusterAction extends Action {
HideClusterAction() {
super("Hide");
setGraphic(new ImageView(HIDE));
setEventHandler((ActionEvent t) -> {
DescriptionFilter descriptionFilter = new DescriptionFilter(getDescLOD(), getDescription(), DescriptionFilter.FilterMode.EXCLUDE);
chart.getBundleFilters().add(descriptionFilter);
RootFilter rootFilter = eventsModel.getFilter();
rootFilter.getSubFilters().add(descriptionFilter);
chart.getController().pushFilters(rootFilter.copyOf());
chart.setRequiresLayout(true);
chart.requestChartLayout();
});
}
}
} }

View File

@ -68,10 +68,10 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; 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.datamodel.EventBundle;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; 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.ui.AbstractVisualization; 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;
@ -243,8 +243,8 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
}); });
} }
public ObservableList<DescriptionFilter> getBundleFilters() { public ObservableList<String> getQuickHideMasks() {
return chart.getBundleFilters(); return chart.getQuickHideMasks();
} }
@Override @Override
@ -301,11 +301,12 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
if (isCancelled()) { if (isCancelled()) {
return null; return null;
} }
Platform.runLater(() -> {
if (isCancelled() == false) { if (isCancelled() == false) {
Platform.runLater(() -> {
setCursor(Cursor.WAIT); setCursor(Cursor.WAIT);
} });
}); }
updateProgress(-1, 1); updateProgress(-1, 1);
updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.preparing")); updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.preparing"));
@ -315,6 +316,7 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
final long upperBound = rangeInfo.getUpperBound(); final long upperBound = rangeInfo.getUpperBound();
updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.queryDb")); updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.queryDb"));
getQuickHideMasks().clear();
aggregatedEvents.setAll(filteredEvents.getAggregatedEvents()); aggregatedEvents.setAll(filteredEvents.getAggregatedEvents());
Platform.runLater(() -> { Platform.runLater(() -> {
@ -336,12 +338,11 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
updateProgress(i++, size); updateProgress(i++, size);
updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.updateUI")); updateMessage(NbBundle.getMessage(this.getClass(), "DetailViewPane.loggedTask.updateUI"));
final XYChart.Data<DateTime, EventCluster> xyData = new BarChart.Data<>(new DateTime(e.getSpan().getStartMillis()), e); final XYChart.Data<DateTime, EventCluster> xyData = new BarChart.Data<>(new DateTime(e.getSpan().getStartMillis()), e);
if (isCancelled() == false) {
Platform.runLater(() -> { Platform.runLater(() -> {
if (isCancelled() == false) {
getSeries(e.getEventType()).getData().add(xyData); getSeries(e.getEventType()).getData().add(xyData);
} });
}); }
} }
Platform.runLater(() -> { Platform.runLater(() -> {
@ -485,4 +486,11 @@ public class DetailViewPane extends AbstractVisualization<DateTime, EventCluster
} }
public EventDetailChart.UnhideBundleAction newUnhideBundleAction(String description) {
return chart.new UnhideBundleAction(description);
}
public EventDetailChart.HideBundleAction newHideBundleAction(EventBundle bundle) {
return chart.new HideBundleAction(bundle);
}
} }

View File

@ -98,6 +98,9 @@ import org.sleuthkit.autopsy.timeline.ui.TimeLineChart;
*/ */
public final class EventDetailChart extends XYChart<DateTime, EventCluster> implements TimeLineChart<DateTime> { public final class EventDetailChart extends XYChart<DateTime, EventCluster> implements TimeLineChart<DateTime> {
static final Image HIDE = new Image("/org/sleuthkit/autopsy/timeline/images/eye--minus.png"); // NON-NLS
static final Image SHOW = new Image("/org/sleuthkit/autopsy/timeline/images/eye--plus.png"); // NON-NLS
private static final int PROJECTED_LINE_Y_OFFSET = 5; private static final int PROJECTED_LINE_Y_OFFSET = 5;
private static final int PROJECTED_LINE_STROKE_WIDTH = 5; private static final int PROJECTED_LINE_STROKE_WIDTH = 5;
@ -203,7 +206,7 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
*/ */
private final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0); private final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0);
private final SimpleBooleanProperty alternateLayout = new SimpleBooleanProperty(true); private final SimpleBooleanProperty alternateLayout = new SimpleBooleanProperty(true);
private ObservableList<DescriptionFilter> bundleFilters = FXCollections.observableArrayList(); private ObservableList<String> quickHideMasks = FXCollections.observableArrayList();
EventDetailChart(DateAxis dateAxis, final Axis<EventCluster> verticalAxis, ObservableList<DetailViewNode<?>> selectedNodes) { EventDetailChart(DateAxis dateAxis, final Axis<EventCluster> verticalAxis, ObservableList<DetailViewNode<?>> selectedNodes) {
super(dateAxis, verticalAxis); super(dateAxis, verticalAxis);
@ -499,8 +502,8 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
shownPartition = bundleStream shownPartition = bundleStream
.map(nodeMap::get) .map(nodeMap::get)
.sorted(Comparator.comparing(AbstractDetailViewNode<?, ?>::getStartMillis)) .sorted(Comparator.comparing(AbstractDetailViewNode<?, ?>::getStartMillis))
.collect(Collectors.partitioningBy(node -> getBundleFilters().stream() .collect(Collectors.partitioningBy(node -> getQuickHideMasks().stream()
.allMatch(filter -> filter.test(node.getDescription())))); .anyMatch(mask -> mask.equals(node.getDescription()))));
layoutNodesHelper(shownPartition.get(false), shownPartition.get(true), minY); layoutNodesHelper(shownPartition.get(false), shownPartition.get(true), minY);
minY = maxY.get(); minY = maxY.get();
@ -508,9 +511,9 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
} else { } else {
shownPartition = nodeMap.values().stream() shownPartition = nodeMap.values().stream()
.sorted(Comparator.comparing(AbstractDetailViewNode<?, ?>::getStartMillis)) .sorted(Comparator.comparing(AbstractDetailViewNode<?, ?>::getStartMillis))
.collect(Collectors.partitioningBy(node -> getBundleFilters().stream() .collect(Collectors.partitioningBy(node -> getQuickHideMasks().stream()
.allMatch(filter -> filter.test(node.getDescription())))); .anyMatch(mask -> mask.equals(node.getDescription()))));
layoutNodesHelper(shownPartition.get(false), shownPartition.get(true), 0); layoutNodesHelper(shownPartition.get(true), shownPartition.get(false), 0);
} }
setCursor(null); setCursor(null);
requiresLayout = false; requiresLayout = false;
@ -756,8 +759,8 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
return alternateLayout; return alternateLayout;
} }
ObservableList<DescriptionFilter> getBundleFilters() { ObservableList<String> getQuickHideMasks() {
return bundleFilters; return quickHideMasks;
} }
private class DetailIntervalSelector extends IntervalSelector<DateTime> { private class DetailIntervalSelector extends IntervalSelector<DateTime> {
@ -798,4 +801,36 @@ public final class EventDetailChart extends XYChart<DateTime, EventCluster> impl
void applySelectionEffect(DetailViewNode<?> c1, Boolean selected) { void applySelectionEffect(DetailViewNode<?> c1, Boolean selected) {
c1.applySelectionEffect(selected); c1.applySelectionEffect(selected);
} }
public class HideBundleAction extends Action {
/**
*
* @param description the value of description
*/
public HideBundleAction(final EventBundle bundle) {
super("Hide");
setGraphic(new ImageView(HIDE));
setEventHandler((ActionEvent t) -> {
getQuickHideMasks().add(bundle.getDescription());
filteredEvents.getFilter().getSubFilters().add(new DescriptionFilter(bundle.getDescriptionLOD(), bundle.getDescription(), DescriptionFilter.FilterMode.EXCLUDE));
setRequiresLayout(true);
requestChartLayout();
});
}
}
public class UnhideBundleAction extends Action {
public UnhideBundleAction(String description) {
super("Unhide");
setGraphic(new ImageView(SHOW));
setEventHandler((ActionEvent t) -> {
getQuickHideMasks().removeAll(description);
setRequiresLayout(true);
requestChartLayout();
});
}
}
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.timeline.ui.detailview.tree; package org.sleuthkit.autopsy.timeline.ui.detailview.tree;
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.application.Platform;
@ -36,13 +37,13 @@ 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.controlsfx.control.action.ActionUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
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;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter;
import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewNode; import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewNode;
import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane; import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane;
@ -144,30 +145,35 @@ public class NavPanel extends BorderPane implements TimeLineView {
ImageView imageView = new ImageView(item.getType().getFXImage()); ImageView imageView = new ImageView(item.getType().getFXImage());
setGraphic(new StackPane(rect, imageView)); setGraphic(new StackPane(rect, imageView));
detailViewPane.getBundleFilters().addListener((Observable observable) -> { detailViewPane.getQuickHideMasks().addListener((Observable observable) -> {
asdasd(item, rect, imageView); configureHiddenState(item, rect, imageView);
}); });
asdasd(item, rect, imageView); configureHiddenState(item, rect, imageView);
} else { } else {
setText(null); setText(null);
setTooltip(null); setTooltip(null);
setGraphic(null); setGraphic(null);
setContextMenu(null);
} }
} }
private void asdasd(NavTreeNode item, Rectangle rect, ImageView imageView) { private void configureHiddenState(NavTreeNode item, Rectangle rect, ImageView imageView) {
if (detailViewPane.getBundleFilters().stream().allMatch((DescriptionFilter t) -> t.test(item.getDescription())) == false) { if (detailViewPane.getQuickHideMasks().stream().anyMatch(mask -> mask.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()))));
} 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()))));
} }
} }
} }
} }

View File

@ -22,7 +22,6 @@ import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.scene.control.TreeItem; import javafx.scene.control.TreeItem;
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
import org.sleuthkit.autopsy.timeline.datamodel.EventBundle; import org.sleuthkit.autopsy.timeline.datamodel.EventBundle;
@ -69,8 +68,7 @@ class RootItem extends NavTreeItem {
Platform.runLater(() -> { Platform.runLater(() -> {
synchronized (getChildren()) { synchronized (getChildren()) {
getChildren().add(newTreeItem); getChildren().add(newTreeItem);
getChildren().sort(TreeComparator.Type);
FXCollections.sort(getChildren(), TreeComparator.Type);
} }
}); });
} else { } else {

View File

@ -89,7 +89,7 @@ final public class FilterSetPanel extends BorderPane implements TimeLineView {
assert applyButton != null : "fx:id=\"applyButton\" was not injected: check your FXML file 'FilterSetPanel.fxml'."; // NON-NLS assert applyButton != null : "fx:id=\"applyButton\" was not injected: check your FXML file 'FilterSetPanel.fxml'."; // NON-NLS
applyButton.setOnAction(e -> { applyButton.setOnAction(e -> {
controller.pushFilters((RootFilter) filterTreeTable.getRoot().getValue().copyOf()); controller.pushFilters((RootFilter) filterTreeTable.getRoot().getValue());
}); });
applyButton.setText(Bundle.FilterSetPanel_applyButton_text()); applyButton.setText(Bundle.FilterSetPanel_applyButton_text());
defaultButton.setText(Bundle.FilterSetPanel_defaultButton_text()); defaultButton.setText(Bundle.FilterSetPanel_defaultButton_text());
@ -173,7 +173,6 @@ final public class FilterSetPanel extends BorderPane implements TimeLineView {
@Override @Override
public void setModel(FilteredEventsModel filteredEvents) { public void setModel(FilteredEventsModel filteredEvents) {
this.filteredEvents = filteredEvents; this.filteredEvents = filteredEvents;
filteredEvents.registerForEvents(this);
refresh(); refresh();
this.filteredEvents.filterProperty().addListener((Observable o) -> { this.filteredEvents.filterProperty().addListener((Observable o) -> {
refresh(); refresh();
@ -182,7 +181,7 @@ final public class FilterSetPanel extends BorderPane implements TimeLineView {
private void refresh() { private void refresh() {
Platform.runLater(() -> { Platform.runLater(() -> {
filterTreeTable.setRoot(new FilterTreeItem(filteredEvents.getFilter().copyOf(), expansionMap)); filterTreeTable.setRoot(new FilterTreeItem(filteredEvents.getFilter(), expansionMap));
}); });
} }
} }