diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/AbstractFXCellFactory.java b/Core/src/org/sleuthkit/autopsy/coreutils/AbstractFXCellFactory.java new file mode 100644 index 0000000000..169447d183 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/AbstractFXCellFactory.java @@ -0,0 +1,74 @@ +/** + * ************************************************************************* + ** This data and information is proprietary to, and a valuable trade secret * + * of, Basis Technology Corp. It is given in confidence by Basis Technology * + * and may only be used as permitted under the license agreement under which * + * it has been distributed, and in no other way. * * Copyright (c) 2014 Basis + * Technology Corp. All rights reserved. * * The technical data and information + * provided herein are provided with * `limited rights', and the computer + * software provided herein is provided * with `restricted rights' as those + * terms are defined in DAR and ASPR * 7-104.9(a). + * ************************************************************************* + */ +package org.sleuthkit.autopsy.coreutils; + +import java.util.function.Supplier; +import javafx.scene.control.IndexedCell; +import javafx.scene.control.ListCell; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TreeTableCell; +import javafx.scene.control.TreeTableColumn; + +/** + * an abstract base class for Cell factories. This class provides the basic + * infrustructure for implementations to be able to create similar cells for + * listview, tableviews or treetableviews via the appropriate method call. + * Implementations need only implement the abstract configureCell method in the + * same spirit as IndexedCell.updateItem + */ +public abstract class AbstractFXCellFactory { + + public TreeTableCell< X, Y> forTreeTable(TreeTableColumn< X, Y> column) { + return new AbstractTreeTableCell(); + } + + public TableCell forTable(TableColumn column) { + return new AbstractTableCell(); + } + + public ListCell< Y> forList() { + return new AbstractListCell(); + } + + protected abstract void configureCell(IndexedCell cell, Y item, boolean empty, Supplier supplier); + + private class AbstractTableCell extends TableCell { + + @Override + @SuppressWarnings({"unchecked"}) //we know it will be X but there is a flaw in getTableRow return type + protected void updateItem(Y item, boolean empty) { + super.updateItem(item, empty); + configureCell(this, item, empty, (() -> (X) this.getTableRow().getItem())); + } + } + + private class AbstractTreeTableCell extends TreeTableCell { + + @Override + protected void updateItem(Y item, boolean empty) { + super.updateItem(item, empty); + configureCell(this, item, empty, (() -> this.getTreeTableRow().getItem())); + } + } + + private class AbstractListCell extends ListCell< Y> { + + @Override + @SuppressWarnings("unchecked") //for a list X should always equal Y + protected void updateItem(Y item, boolean empty) { + super.updateItem(item, empty); + configureCell(this, item, empty, () -> (X) this.getItem()); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java index bc9675cca2..787307916f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java @@ -30,7 +30,7 @@ public abstract class AbstractFilter implements Filter { private final SimpleBooleanProperty disabled = new SimpleBooleanProperty(false); @Override - public SimpleBooleanProperty getSelectedProperty() { + public SimpleBooleanProperty selectedProperty() { return selected; } @@ -64,7 +64,7 @@ public abstract class AbstractFilter implements Filter { return "[" + (isSelected() ? "x" : " ") + "]"; // NON-NLS } - final boolean isActive() { + public final boolean isActive() { return isSelected() && (isDisabled() == false); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/CompoundFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/CompoundFilter.java index c1964b7087..bf4c6a1b07 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/CompoundFilter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/CompoundFilter.java @@ -72,7 +72,7 @@ public abstract class CompoundFilter extends Abstr private void addSubFilterListeners(List newSubfilters) { for (SubFilterType sf : newSubfilters) { //if a subfilter changes active state - sf.getSelectedProperty().addListener((Observable observable) -> { + sf.selectedProperty().addListener((Observable observable) -> { //set this filter acttive af any of the subfilters are active. setSelected(getSubFilters().parallelStream().anyMatch(Filter::isSelected)); }); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java index 95226e9371..03c1c01efa 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java @@ -66,7 +66,7 @@ public interface Filter { void setSelected(Boolean act); - SimpleBooleanProperty getSelectedProperty(); + SimpleBooleanProperty selectedProperty(); /* * TODO: disabled state only affects the state of the checkboxes in the ui diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java index b47c4d481f..f5e5b30731 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java @@ -33,7 +33,7 @@ public class HideKnownFilter extends AbstractFilter { public HideKnownFilter() { super(); - getSelectedProperty().set(false); + selectedProperty().set(false); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java index 0d0357fc5e..9dde499205 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java @@ -79,6 +79,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; +import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter; import org.sleuthkit.autopsy.timeline.ui.TimeLineChart; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD; @@ -325,6 +326,7 @@ public final class EventDetailChart extends XYChart impl public synchronized void setController(TimeLineController controller) { this.controller = controller; setModel(this.controller.getEventsModel()); + getController().getQuickHideMasks().addListener(layoutInvalidationListener); } @Override @@ -477,6 +479,7 @@ public final class EventDetailChart extends XYChart impl for (Series series : sortedSeriesList) { hiddenPartition = series.getData().stream().map(Data::getNode).map(EventStripeNode.class::cast) .collect(Collectors.partitioningBy(node -> getController().getQuickHideMasks().stream() + .filter(AbstractFilter::isActive) .anyMatch(mask -> mask.getDescription().equals(node.getDescription())))); layoutNodesHelper(hiddenPartition.get(true), hiddenPartition.get(false), minY, 0); @@ -485,6 +488,7 @@ public final class EventDetailChart extends XYChart impl } else { hiddenPartition = stripeNodeMap.values().stream() .collect(Collectors.partitioningBy(node -> getController().getQuickHideMasks().stream() + .filter(AbstractFilter::isActive) .anyMatch(mask -> mask.getDescription().equals(node.getDescription())))); layoutNodesHelper(hiddenPartition.get(true), hiddenPartition.get(false), 0, 0); } @@ -779,20 +783,15 @@ public final class EventDetailChart extends XYChart impl class HideDescriptionAction extends Action { - /** - * - * @param description the value of description - */ HideDescriptionAction(String description, DescriptionLOD descriptionLoD) { super("Hide"); setGraphic(new ImageView(HIDE)); setEventHandler((ActionEvent t) -> { - getController().getQuickHideMasks().add( - new DescriptionFilter(descriptionLoD, - description, - DescriptionFilter.FilterMode.EXCLUDE)); - setRequiresLayout(true); - requestChartLayout(); + DescriptionFilter descriptionFilter = new DescriptionFilter(descriptionLoD, + description, + DescriptionFilter.FilterMode.EXCLUDE); + descriptionFilter.selectedProperty().addListener(layoutInvalidationListener); + getController().getQuickHideMasks().add(descriptionFilter); }); } } @@ -807,8 +806,6 @@ public final class EventDetailChart extends XYChart impl getController().getQuickHideMasks().removeIf(descriptionFilter -> descriptionFilter.getDescriptionLoD().equals(descriptionLoD) && descriptionFilter.getDescription().equals(description)); - setRequiresLayout(true); - requestChartLayout(); }); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterCheckBoxCell.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterCheckBoxCell.java deleted file mode 100644 index 795e4b66b1..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterCheckBoxCell.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2015 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.timeline.ui.filtering; - -import javafx.application.Platform; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.scene.control.CheckBox; -import javafx.scene.control.TreeTableCell; -import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; - -/** - * A {@link TreeTableCell} that represents the active state of a - * {@link AbstractFilter} as a checkbox - */ -class FilterCheckBoxCell extends TreeTableCell { - - private final CheckBox checkBox = new CheckBox(); - private SimpleBooleanProperty activeProperty; - private final TimeLineController controller; - - FilterCheckBoxCell(TimeLineController controller) { - this.controller = controller; - - } - - @Override - protected void updateItem(AbstractFilter item, boolean empty) { - super.updateItem(item, empty); - Platform.runLater(() -> { - if (activeProperty != null) { - checkBox.selectedProperty().unbindBidirectional(activeProperty); - } - checkBox.disableProperty().unbind(); - if (item == null) { - setText(null); - setGraphic(null); - } else { - setText(item.getDisplayName()); - activeProperty = item.getSelectedProperty(); - checkBox.selectedProperty().bindBidirectional(activeProperty); - checkBox.disableProperty().bind(item.getDisabledProperty()); - setGraphic(checkBox); - } - }); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterCheckBoxCellFactory.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterCheckBoxCellFactory.java new file mode 100644 index 0000000000..54d848b988 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterCheckBoxCellFactory.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.timeline.ui.filtering; + +import java.util.function.Supplier; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.scene.control.CheckBox; +import javafx.scene.control.IndexedCell; +import org.sleuthkit.autopsy.coreutils.AbstractFXCellFactory; +import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; + +class FilterCheckBoxCellFactory extends AbstractFXCellFactory { + + private final CheckBox checkBox = new CheckBox(); + private SimpleBooleanProperty selectedProperty; + private SimpleBooleanProperty disabledProperty; + + @Override + protected void configureCell(IndexedCell cell, X item, boolean empty, Supplier supplier) { + if (selectedProperty != null) { + checkBox.selectedProperty().unbindBidirectional(selectedProperty); + } + if (disabledProperty != null) { + checkBox.disableProperty().unbindBidirectional(disabledProperty); + } + if (item == null) { + cell.setText(null); + cell.setGraphic(null); + } else { + cell.setText(item.getDisplayName()); + selectedProperty = item.selectedProperty(); + checkBox.selectedProperty().bindBidirectional(selectedProperty); + disabledProperty = item.getDisabledProperty(); + checkBox.disableProperty().bindBidirectional(disabledProperty); + cell.setGraphic(checkBox); + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index ba78fd3bf5..cabc79dbc9 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -156,7 +156,7 @@ final public class FilterSetPanel extends BorderPane implements TimeLineView { //configure tree column to show name of filter and checkbox treeColumn.setCellValueFactory(param -> param.getValue().valueProperty()); - treeColumn.setCellFactory(col -> new FilterCheckBoxCell(controller)); + treeColumn.setCellFactory(col -> new FilterCheckBoxCellFactory().forTreeTable(col)); //configure legend column to show legend (or othe supplamantal ui, eg, text field for text filter) legendColumn.setCellValueFactory(param -> param.getValue().valueProperty()); @@ -194,18 +194,18 @@ final public class FilterSetPanel extends BorderPane implements TimeLineView { this.setModel(timeLineController.getEventsModel()); hiddenDescriptionsListView.setItems(controller.getQuickHideMasks()); - hiddenDescriptionsListView.setCellFactory((ListView param) -> new ListCellImpl()); + hiddenDescriptionsListView.setCellFactory((ListView param) -> { + final ListCell forList = new FilterCheckBoxCellFactory().forList(); + forList.setContextMenu(new ContextMenu(new MenuItem("unhide and remove from list") { + { + setOnAction((ActionEvent event) -> { + controller.getQuickHideMasks().remove(forList.getItem()); + }); + } + })); + return forList; + }); -// .addListener((ListChangeListener.Change c) -> { -// while (c.next()) { -// DescriptionsExclusionFilter descriptionExclusionFilter = ((RootFilter) filterTreeTable.getRoot().getValue()).getDescriptionsExclusionfilter(); -// -// for (DescriptionFilter filter : c.getAddedSubList()) { -// descriptionExclusionFilter.setSelected(true); -// descriptionExclusionFilter.addSubFilter(filter); -// } -// } -// }); controller.viewModeProperty().addListener(observable -> { applyFilters(); });