Autopsy  4.19.3
Graphical digital forensics platform for The Sleuth Kit and other tools.
FilterSetPanel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.timeline.ui.filtering;
20 
21 import java.util.Arrays;
22 import javafx.application.Platform;
23 import javafx.beans.InvalidationListener;
24 import javafx.beans.binding.Bindings;
25 import javafx.collections.FXCollections;
26 import javafx.collections.ObservableMap;
27 import javafx.fxml.FXML;
28 import javafx.scene.control.Button;
29 import javafx.scene.control.Cell;
30 import javafx.scene.control.ListCell;
31 import javafx.scene.control.ListView;
32 import javafx.scene.control.SplitPane;
33 import javafx.scene.control.TitledPane;
34 import javafx.scene.control.TreeTableColumn;
35 import javafx.scene.control.TreeTableView;
36 import javafx.scene.image.Image;
37 import javafx.scene.image.ImageView;
38 import javafx.scene.layout.BorderPane;
39 import org.controlsfx.control.action.Action;
40 import org.controlsfx.control.action.ActionUtils;
41 import org.openide.util.NbBundle;
49 
56 final public class FilterSetPanel extends BorderPane {
57 
58  private static final Image TICK = new Image("org/sleuthkit/autopsy/timeline/images/tick.png"); //NON-NLS
59 
60  @FXML
61  private Button applyButton;
62 
63  @FXML
64  private Button defaultButton;
65 
66  @FXML
67  private TreeTableView<FilterState<?>> filterTreeTable;
68 
69  @FXML
70  private TreeTableColumn< FilterState< ?>, FilterState<?>> treeColumn;
71 
72  @FXML
73  private TreeTableColumn<FilterState<?>, FilterState<?>> legendColumn;
74 
75  @FXML
76  private ListView<DescriptionFilterState> hiddenDescriptionsListView;
77  @FXML
78  private TitledPane hiddenDescriptionsPane;
79  @FXML
80  private SplitPane splitPane;
81 
82  private final EventsModel filteredEvents;
84 
89  private final ObservableMap< Object, Boolean> expansionMap = FXCollections.observableHashMap();
90  private double dividerPosition;
91 
92  @NbBundle.Messages({
93  "FilterSetPanel.defaultButton.text=Reset",
94  "FilsetSetPanel.hiddenDescriptionsPane.displayName=Hidden Descriptions"})
95  @FXML
96  void initialize() {
97  assert applyButton != null : "fx:id=\"applyButton\" was not injected: check your FXML file 'FilterSetPanel.fxml'."; // NON-NLS
98 
99  ActionUtils.configureButton(new ApplyFiltersAction(), applyButton);
100  ActionUtils.configureButton(new ResetFilters(Bundle.FilterSetPanel_defaultButton_text(), controller), defaultButton);
101 
102  hiddenDescriptionsPane.setText(Bundle.FilsetSetPanel_hiddenDescriptionsPane_displayName());
103  //remove column headers via css.
104  filterTreeTable.getStylesheets().addAll(FilterSetPanel.class.getResource("FilterTable.css").toExternalForm()); // NON-NLS
105 
106  //use row factory as hook to attach context menus to.
107  filterTreeTable.setRowFactory(ttv -> new FilterTreeTableRow());
108 
109  //configure tree column to show name of filter and checkbox
110  treeColumn.setCellValueFactory(cellDataFeatures -> cellDataFeatures.getValue().valueProperty());
111  treeColumn.setCellFactory(col -> new FilterCheckBoxCellFactory<>().forTreeTable(col));
112 
113  //configure legend column to show legend (or othe supplamantal ui, eg, text field for text filter)
114  legendColumn.setCellValueFactory(cellDataFeatures -> cellDataFeatures.getValue().valueProperty());
115  legendColumn.setCellFactory(col -> new LegendCell(this.controller));
116 
117  //type is the only filter expanded initialy
118  expansionMap.put(filteredEvents.getEventFilterState().getFilter(), true);
119  expansionMap.put(filteredEvents.getEventFilterState().getEventTypeFilterState().getFilter(), true);
120 
121  InvalidationListener applyFiltersListener = observable -> applyFilters();
122 
123  filteredEvents.eventTypesHierarchyLevelProperty().addListener(applyFiltersListener);
124  filteredEvents.descriptionLODProperty().addListener(applyFiltersListener);
125  filteredEvents.timeRangeProperty().addListener(applyFiltersListener);
126 
127  filteredEvents.eventFilterProperty().addListener(observable -> refreshFilterUI());
128  refreshFilterUI();
129 
130  hiddenDescriptionsListView.setItems(controller.getQuickHideFilters());
131  hiddenDescriptionsListView.setCellFactory(listView -> getNewDiscriptionFilterListCell());
132 
133  //show and hide the "hidden descriptions" panel depending on the current view mode
134  controller.viewModeProperty().addListener(observable -> {
135  switch (controller.getViewMode()) {
136  case COUNTS:
137  case LIST:
138  //hide for counts and lists, but remember divider position
139  dividerPosition = splitPane.getDividerPositions()[0];
140  splitPane.setDividerPositions(1);
141  hiddenDescriptionsPane.setExpanded(false);
142  hiddenDescriptionsPane.setCollapsible(false);
143  hiddenDescriptionsPane.setDisable(true);
144  break;
145  case DETAIL:
146  //show and restore divider position.
147  splitPane.setDividerPositions(dividerPosition);
148  hiddenDescriptionsPane.setDisable(false);
149  hiddenDescriptionsPane.setCollapsible(true);
150  hiddenDescriptionsPane.setExpanded(true);
151  hiddenDescriptionsPane.setCollapsible(false);
152  break;
153  default:
154  throw new UnsupportedOperationException("Unknown ViewMode: " + controller.getViewMode());
155  }
156  });
157  }
158 
159  public FilterSetPanel(TimeLineController controller) {
160  this.controller = controller;
161  this.filteredEvents = controller.getEventsModel();
162  FXMLConstructor.construct(this, "FilterSetPanel.fxml"); // NON-NLS
163  }
164 
165  private void refreshFilterUI() {
166  Platform.runLater(()
167  -> filterTreeTable.setRoot(new FilterTreeItem(filteredEvents.eventFilterProperty().get().copyOf(), expansionMap)));
168  }
169 
170  private void applyFilters() {
171  Platform.runLater(() -> {
172  controller.pushFilters(((RootFilterState) filterTreeTable.getRoot().getValue().copyOf()));
173  });
174  }
175 
176  private ListCell<DescriptionFilterState> getNewDiscriptionFilterListCell() {
177  final ListCell<DescriptionFilterState> cell = new FilterCheckBoxCellFactory< DescriptionFilterState>().forList();
178  cell.itemProperty().addListener(itemProperty -> {
179  if (cell.getItem() == null) {
180  cell.setContextMenu(null);
181  } else {
182  cell.setContextMenu(ActionUtils.createContextMenu(Arrays.asList(
183  new RemoveDescriptionFilterAction(controller, cell))
184  ));
185  }
186  });
187  return cell;
188  }
189 
190  @NbBundle.Messages({"FilterSetPanel.applyButton.text=Apply",
191  "FilterSetPanel.applyButton.longText=(Re)Apply filters"})
192  private class ApplyFiltersAction extends Action {
193 
195  super(Bundle.FilterSetPanel_applyButton_text());
196  setLongText(Bundle.FilterSetPanel_applyButton_longText());
197  setGraphic(new ImageView(TICK));
198  setEventHandler(actionEvent -> applyFilters());
199  }
200  }
201 
202  @NbBundle.Messages({
203  "FilterSetPanel.hiddenDescriptionsListView.unhideAndRemove=Unhide and remove from list",
204  "FilterSetPanel.hiddenDescriptionsListView.remove=Remove from list",})
205  private static class RemoveDescriptionFilterAction extends Action {
206 
207  private static final Image SHOW = new Image("/org/sleuthkit/autopsy/timeline/images/eye--plus.png"); // NON-NLS
208 
209  RemoveDescriptionFilterAction(TimeLineController controller, Cell<DescriptionFilterState> cell) {
210  super(actionEvent -> controller.getQuickHideFilters().remove(cell.getItem()));
211  setGraphic(new ImageView(SHOW));
212  textProperty().bind(
213  Bindings.when(cell.getItem().selectedProperty())
214  .then(Bundle.FilterSetPanel_hiddenDescriptionsListView_unhideAndRemove())
215  .otherwise(Bundle.FilterSetPanel_hiddenDescriptionsListView_remove()));
216  }
217  }
218 }
final ReadOnlyObjectWrapper< TimelineEventType.HierarchyLevel > eventTypesHierarchyLevelProperty
synchronized RootFilterState getEventFilterState()
ListCell< DescriptionFilterState > getNewDiscriptionFilterListCell()
ListView< DescriptionFilterState > hiddenDescriptionsListView
TreeTableColumn< FilterState< ?>, FilterState<?> > treeColumn
final ReadOnlyObjectWrapper< Interval > timeRangeProperty
CompoundFilterState< EventTypeFilter, EventTypeFilter > getEventTypeFilterState()
synchronized void pushFilters(RootFilterState filter)
synchronized ReadOnlyObjectProperty< ViewMode > viewModeProperty()
ObservableList< DescriptionFilterState > getQuickHideFilters()
synchronized ReadOnlyObjectProperty< TimelineLevelOfDetail > descriptionLODProperty()
final ObservableMap< Object, Boolean > expansionMap
static void construct(Node node, String fxmlFileName)
synchronized ReadOnlyObjectProperty< RootFilterState > eventFilterProperty()
TreeTableColumn< FilterState<?>, FilterState<?> > legendColumn

Copyright © 2012-2022 Basis Technology. Generated on: Thu Dec 8 2022
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.