diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventDescriptionTreeItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventDescriptionTreeItem.java index 6feef28f8f..0d172a73ba 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventDescriptionTreeItem.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventDescriptionTreeItem.java @@ -59,7 +59,6 @@ class EventDescriptionTreeItem extends NavTreeItem { treeItem.setExpanded(true); childMap.put(head.getDescription(), treeItem); getChildren().add(treeItem); - FXCollections.sort(getChildren(), TreeComparator.Description); } if (path.isEmpty() == false) { @@ -67,6 +66,18 @@ class EventDescriptionTreeItem extends NavTreeItem { } } + void remove(Deque> path) { + EventBundle head = path.removeFirst(); + EventDescriptionTreeItem descTreeItem = childMap.get(head.getDescription()); + if (path.isEmpty() == false) { + descTreeItem.remove(path); + } + if (descTreeItem.getChildren().isEmpty()) { + childMap.remove(head.getDescription()); + getChildren().remove(descTreeItem); + } + } + @Override public void resort(Comparator>> comp) { FXCollections.sort(getChildren(), comp); @@ -88,4 +99,5 @@ class EventDescriptionTreeItem extends NavTreeItem { } return null; } + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventTypeTreeItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventTypeTreeItem.java index e921991191..7d63fda0c2 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventTypeTreeItem.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventTypeTreeItem.java @@ -52,7 +52,6 @@ class EventTypeTreeItem extends NavTreeItem { treeItem.setExpanded(true); childMap.put(head.getDescription(), treeItem); getChildren().add(treeItem); - FXCollections.sort(getChildren(), comparator); } if (path.isEmpty() == false) { @@ -60,6 +59,21 @@ class EventTypeTreeItem extends NavTreeItem { } } + void remove(Deque> path) { + + EventBundle head = path.removeFirst(); + EventDescriptionTreeItem descTreeItem = childMap.get(head.getDescription()); + if (descTreeItem != null) { + if (path.isEmpty() == false) { + descTreeItem.remove(path); + } else if (descTreeItem.getChildren().isEmpty()) { + childMap.remove(head.getDescription()); + getChildren().remove(descTreeItem); + + } + } + } + @Override public NavTreeItem findTreeItemForEvent(EventBundle t) { if (t.getEventType().getBaseType() == getValue().getEventType().getBaseType()) { @@ -75,7 +89,8 @@ class EventTypeTreeItem extends NavTreeItem { } @Override - public void resort(Comparator>> comp) { + public void resort(Comparator>> comp + ) { FXCollections.sort(getChildren(), comp); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java index 154ab9fab0..d212e49a5f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/EventsTree.java @@ -25,10 +25,10 @@ import java.util.Comparator; import java.util.Objects; import javafx.beans.InvalidationListener; import javafx.beans.Observable; +import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.ListChangeListener; import javafx.fxml.FXML; import javafx.scene.control.ComboBox; -import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.SelectionMode; import javafx.scene.control.Tooltip; @@ -36,6 +36,8 @@ import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.image.ImageView; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; @@ -61,6 +63,7 @@ final public class EventsTree extends BorderPane { private final TimeLineController controller; + private DetailViewPane detailViewPane; @FXML @@ -74,7 +77,7 @@ final public class EventsTree extends BorderPane { public EventsTree(TimeLineController controller) { this.controller = controller; - + FXMLConstructor.construct(this, "EventsTree.fxml"); // NON-NLS } @@ -82,9 +85,18 @@ final public class EventsTree extends BorderPane { this.detailViewPane = detailViewPane; detailViewPane.setSelectionModel(eventsTree.getSelectionModel()); - detailViewPane.getEventBundles().addListener((Observable observable) -> { - setRoot(); + detailViewPane.getEventBundles().addListener((ListChangeListener.Change> c) -> { + while (c.next()) { + for (EventBundle bundle : c.getAddedSubList()) { + getRoot().insert(bundle); + } + for (EventBundle bundle : c.getRemoved()) { + getRoot().remove(bundle); + } + } + getRoot().resort(sortByBox.getSelectionModel().getSelectedItem()); }); + setRoot(); detailViewPane.getSelectedNodes().addListener((Observable observable) -> { @@ -96,8 +108,8 @@ final public class EventsTree extends BorderPane { } - private NavTreeItem getRoot() { - return (NavTreeItem) eventsTree.getRoot(); + private RootItem getRoot() { + return (RootItem) eventsTree.getRoot(); } @ThreadConfined(type = ThreadConfined.ThreadType.JFX) @@ -106,6 +118,7 @@ final public class EventsTree extends BorderPane { for (EventBundle bundle : detailViewPane.getEventBundles()) { root.insert(bundle); } + root.resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem())); eventsTree.setRoot(root); } @@ -118,7 +131,7 @@ final public class EventsTree extends BorderPane { sortByBox.getItems().setAll(Arrays.asList(TreeComparator.Description, TreeComparator.Count)); sortByBox.getSelectionModel().select(TreeComparator.Description); sortByBox.getSelectionModel().selectedItemProperty().addListener((Observable o) -> { - getRoot().resort(sortByBox.getSelectionModel().getSelectedItem()); + getRoot().resort(TreeComparator.Type.reversed().thenComparing(sortByBox.getSelectionModel().getSelectedItem())); }); eventsTree.setShowRoot(false); eventsTree.setCellFactory((TreeView> p) -> new EventBundleTreeCell()); @@ -137,6 +150,7 @@ final public class EventsTree extends BorderPane { private final Rectangle rect = new Rectangle(24, 24); private final ImageView imageView = new ImageView(); private InvalidationListener filterStateChangeListener; + SimpleBooleanProperty hidden = new SimpleBooleanProperty(false); EventBundleTreeCell() { rect.setArcHeight(5); @@ -173,6 +187,21 @@ final public class EventsTree extends BorderPane { imageView.setImage(item.getEventType().getFXImage()); setGraphic(new StackPane(rect, imageView)); updateHiddenState(item); + if (getTreeItem() instanceof EventDescriptionTreeItem) { + setOnMouseClicked((MouseEvent event) -> { + if (event.getButton() == MouseButton.SECONDARY) { + if (hidden.get()) { + ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newUnhideDescriptionAction(item.getDescription(), item.getDescriptionLoD()))) + .show(EventBundleTreeCell.this, event.getScreenX(), event.getScreenY()); + } else { + ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newHideDescriptionAction(item.getDescription(), item.getDescriptionLoD()))) + .show(EventBundleTreeCell.this, event.getScreenX(), event.getScreenY()); + } + } + }); + } else { + setOnMouseClicked(null); + } } } @@ -194,10 +223,11 @@ final public class EventsTree extends BorderPane { private void updateHiddenState(EventBundle item) { TreeItem> treeItem = getTreeItem(); - ContextMenu newMenu; - if (controller.getQuickHideFilters().stream(). + + hidden.set(controller.getQuickHideFilters().stream(). filter(AbstractFilter::isActive) - .anyMatch(filter -> filter.getDescription().equals(item.getDescription()))) { + .anyMatch(filter -> filter.getDescription().equals(item.getDescription()))); + if (hidden.get()) { if (treeItem != null) { treeItem.setExpanded(false); } @@ -205,18 +235,11 @@ final public class EventsTree extends BorderPane { imageView.setOpacity(HIDDEN_MULTIPLIER); rect.setStroke(item.getEventType().getColor().deriveColor(0, HIDDEN_MULTIPLIER, 1, HIDDEN_MULTIPLIER)); rect.setFill(item.getEventType().getColor().deriveColor(0, HIDDEN_MULTIPLIER, HIDDEN_MULTIPLIER, 0.1)); - newMenu = ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newUnhideDescriptionAction(item.getDescription(), item.getDescriptionLoD()))); } else { setTextFill(Color.BLACK); imageView.setOpacity(1); rect.setStroke(item.getEventType().getColor()); rect.setFill(item.getEventType().getColor().deriveColor(0, 1, 1, 0.1)); - newMenu = ActionUtils.createContextMenu(ImmutableList.of(detailViewPane.newHideDescriptionAction(item.getDescription(), item.getDescriptionLoD()))); - } - if (treeItem instanceof EventDescriptionTreeItem) { - setContextMenu(newMenu); - } else { - setContextMenu(null); } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/NavTreeItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/NavTreeItem.java index bed99d2270..30d1fdd9ea 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/NavTreeItem.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/NavTreeItem.java @@ -35,4 +35,6 @@ abstract class NavTreeItem extends TreeItem> { abstract void resort(Comparator>> comp); abstract NavTreeItem findTreeItemForEvent(EventBundle t); + + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/RootItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/RootItem.java index 571758a037..b59b11aa55 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/RootItem.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/tree/RootItem.java @@ -54,19 +54,30 @@ class RootItem extends NavTreeItem { /** * Recursive method to add a grouping at a given path. * - * @param g Group to add + * @param bundle bundle to add */ - public void insert(EventBundle g) { + public void insert(EventBundle bundle) { - EventTypeTreeItem treeItem = childMap.computeIfAbsent(g.getEventType().getBaseType(), + EventTypeTreeItem treeItem = childMap.computeIfAbsent(bundle.getEventType().getBaseType(), baseType -> { - EventTypeTreeItem newTreeItem = new EventTypeTreeItem(g); + EventTypeTreeItem newTreeItem = new EventTypeTreeItem(bundle); newTreeItem.setExpanded(true); getChildren().add(newTreeItem); - getChildren().sort(TreeComparator.Type); return newTreeItem; }); - treeItem.insert(getTreePath(g)); + treeItem.insert(getTreePath(bundle)); + } + + void remove(EventBundle bundle) { + EventTypeTreeItem typeTreeItem = childMap.get(bundle.getEventType().getBaseType()); + if (typeTreeItem != null) { + typeTreeItem.remove(getTreePath(bundle)); + + if (typeTreeItem.getChildren().isEmpty()) { + childMap.remove(bundle.getEventType().getBaseType()); + getChildren().remove(typeTreeItem); + } + } } static Deque< EventBundle> getTreePath(EventBundle g) { @@ -97,4 +108,5 @@ class RootItem extends NavTreeItem { } return null; } + }