mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
refactored GroupListCell and GroupTreeCell into GroupCellFactory to reduce code duplciation.
cleanup GroupCellFactory and related
This commit is contained in:
parent
85037bb240
commit
4fb8635fb4
@ -666,7 +666,7 @@ public class GroupManager {
|
||||
group = new DrawableGroup(groupKey, fileIDs, groupSeen);
|
||||
controller.getCategoryManager().registerListener(group);
|
||||
group.seenProperty().addListener((o, oldSeen, newSeen) -> {
|
||||
markGroupSeen(group, newSeen);
|
||||
Platform.runLater(() -> markGroupSeen(group, newSeen));
|
||||
});
|
||||
groupMap.put(groupKey, group);
|
||||
}
|
||||
@ -719,12 +719,12 @@ public class GroupManager {
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@NbBundle.Messages({"# {0} - groupBy attribute Name",
|
||||
"# {1} - sortBy name",
|
||||
"# {2} - sort Order",
|
||||
"ReGroupTask.displayTitle=regrouping files by {0} sorted by {1} in {2} order",
|
||||
"# {0} - groupBy attribute Name",
|
||||
"# {1} - atribute value",
|
||||
"ReGroupTask.progressUpdate=regrouping files by {0} : {1}"})
|
||||
"# {1} - sortBy name",
|
||||
"# {2} - sort Order",
|
||||
"ReGroupTask.displayTitle=regrouping files by {0} sorted by {1} in {2} order",
|
||||
"# {0} - groupBy attribute Name",
|
||||
"# {1} - atribute value",
|
||||
"ReGroupTask.progressUpdate=regrouping files by {0} : {1}"})
|
||||
private class ReGroupTask<A extends Comparable<A>> extends LoggedTask<Void> {
|
||||
|
||||
private ProgressHandle groupProgress;
|
||||
|
@ -1,4 +1,4 @@
|
||||
.groupTreeCell{
|
||||
.groupCell{
|
||||
|
||||
-fx-indent:5; /* default indent is 10 */
|
||||
}
|
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2016 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> 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.imagegallery.gui.navpanel;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
import java.util.Optional;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Cell;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Labeled;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.OverrunStyle;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.control.TreeCell;
|
||||
import javafx.scene.control.TreeView;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
|
||||
/**
|
||||
* A Factory for Cells to use in a ListView<DrawableGroup> or
|
||||
* TreeView<GroupTreeNode>
|
||||
*/
|
||||
class GroupCellFactory {
|
||||
|
||||
/**
|
||||
* icon to use if a cell doesn't represent a group but just a folder(with no
|
||||
* DrawableFiles) in the file system hierarchy.
|
||||
*/
|
||||
private static final Image EMPTY_FOLDER_ICON = new Image("/org/sleuthkit/autopsy/imagegallery/images/folder.png"); //NON-NLS
|
||||
|
||||
private final ReadOnlyObjectProperty<GroupComparators<?>> sortOrder;
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
GroupCellFactory(ImageGalleryController controller, ReadOnlyObjectProperty<GroupComparators<?>> sortOrderProperty) {
|
||||
this.controller = controller;
|
||||
this.sortOrder = sortOrderProperty;
|
||||
}
|
||||
|
||||
GroupListCell getListCell(ListView<DrawableGroup> listview) {
|
||||
return initCell(new GroupListCell());
|
||||
}
|
||||
|
||||
GroupTreeCell getTreeCell(TreeView<?> treeView) {
|
||||
return initCell(new GroupTreeCell());
|
||||
}
|
||||
|
||||
/**
|
||||
* remove the listener when it is not needed any more
|
||||
*
|
||||
* @param listener
|
||||
* @param oldGroup
|
||||
*/
|
||||
private void removeListeners(InvalidationListener listener, DrawableGroup oldGroup) {
|
||||
sortOrder.removeListener(listener);
|
||||
oldGroup.getFileIDs().removeListener(listener);
|
||||
oldGroup.seenProperty().removeListener(listener);
|
||||
oldGroup.uncatCountProperty().removeListener(listener);
|
||||
oldGroup.hashSetHitsCountProperty().removeListener(listener);
|
||||
}
|
||||
|
||||
private void addListeners(InvalidationListener listener, DrawableGroup group) {
|
||||
//if the sort order changes, update the counts displayed to match the sorted by property
|
||||
sortOrder.addListener(listener);
|
||||
//if number of files in this group changes (eg a file is recategorized), update counts via listener
|
||||
group.getFileIDs().addListener(listener);
|
||||
group.uncatCountProperty().addListener(listener);
|
||||
group.hashSetHitsCountProperty().addListener(listener);
|
||||
//if the seen state of this group changes update its style
|
||||
group.seenProperty().addListener(listener);
|
||||
}
|
||||
|
||||
private <X extends Cell<?> & GroupCell<?>> X initCell(X cell) {
|
||||
/*
|
||||
* reduce indent of TreeCells to 5, default is 10 which uses up a lot of
|
||||
* space. Define seen and unseen styles
|
||||
*/
|
||||
cell.getStylesheets().add(GroupCellFactory.class.getResource("GroupCell.css").toExternalForm()); //NON-NLS
|
||||
cell.getStyleClass().add("groupCell"); //NON-NLS
|
||||
|
||||
//since end of path is probably more interesting put ellipsis at front
|
||||
cell.setTextOverrun(OverrunStyle.LEADING_ELLIPSIS);
|
||||
|
||||
Platform.runLater(() -> cell.prefWidthProperty().bind(cell.getView().widthProperty().subtract(15)));
|
||||
return cell;
|
||||
}
|
||||
|
||||
private <X extends Cell<?> & GroupCell<?>> void updateGroup(X cell, DrawableGroup group) {
|
||||
addListeners(cell.getGroupListener(), group);
|
||||
|
||||
//and use icon corresponding to group type
|
||||
final Node graphic = (group.getGroupByAttribute() == DrawableAttribute.TAGS)
|
||||
? controller.getTagsManager().getGraphic((TagName) group.getGroupByValue())
|
||||
: group.getGroupKey().getGraphic();
|
||||
final String text = getCellText(cell);
|
||||
final String style = getSeenStyleClass(cell);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
cell.setTooltip(new Tooltip(text));
|
||||
cell.setGraphic(graphic);
|
||||
cell.setText(text);
|
||||
cell.setStyle(style);
|
||||
});
|
||||
}
|
||||
|
||||
private <X extends Labeled & GroupCell<?>> void clearCell(X cell) {
|
||||
Platform.runLater(() -> {
|
||||
cell.setTooltip(null);
|
||||
cell.setText(null);
|
||||
cell.setGraphic(null);
|
||||
cell.setStyle("");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* return the styleClass to apply based on the assigned group's seen status
|
||||
*
|
||||
* @return the style class to apply
|
||||
*/
|
||||
private String getSeenStyleClass(GroupCell<?> cell) {
|
||||
return cell.getGroup()
|
||||
.map(DrawableGroup::isSeen)
|
||||
.map(seen -> seen ? "" : "-fx-font-weight:bold;") //NON-NLS
|
||||
.orElse(""); //if item is null or group is null
|
||||
}
|
||||
|
||||
/**
|
||||
* get the counts part of the text to apply to this cell, including
|
||||
* parentheses
|
||||
*
|
||||
* @return get the counts part of the text to apply to this cell
|
||||
*/
|
||||
private String getCountsText(GroupCell<?> cell) {
|
||||
return cell.getGroup()
|
||||
.map(group ->
|
||||
" (" + (sortOrder.get() == GroupComparators.ALPHABETICAL
|
||||
? group.getSize()
|
||||
: sortOrder.get().getFormattedValueOfGroup(group)) + ")"
|
||||
).orElse(""); //if item is null or group is null
|
||||
}
|
||||
|
||||
private String getCellText(GroupCell<?> cell) {
|
||||
return cell.getGroupName() + getCountsText(cell);
|
||||
}
|
||||
|
||||
private class GroupTreeCell extends TreeCell<GroupTreeNode> implements GroupCell<TreeView<GroupTreeNode>> {
|
||||
|
||||
private final InvalidationListener groupListener = new GroupListener<>(this);
|
||||
|
||||
/**
|
||||
* reference to group files listener that allows us to remove it from a
|
||||
* group when a new group is assigned to this Cell
|
||||
*/
|
||||
@Override
|
||||
public InvalidationListener getGroupListener() {
|
||||
return groupListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeView<GroupTreeNode> getView() {
|
||||
return getTreeView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupName() {
|
||||
return Optional.ofNullable(getItem())
|
||||
.map(treeNode -> StringUtils.defaultIfBlank(treeNode.getPath(), DrawableGroup.getBlankGroupName()))
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DrawableGroup> getGroup() {
|
||||
return Optional.ofNullable(getItem())
|
||||
.map(GroupTreeNode::getGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void updateItem(final GroupTreeNode newItem, boolean empty) {
|
||||
//if there was a previous group, remove the listeners
|
||||
getGroup().ifPresent(oldGroup -> removeListeners(getGroupListener(), oldGroup));
|
||||
|
||||
super.updateItem(newItem, empty);
|
||||
|
||||
if (isNull(newItem) || empty) {
|
||||
clearCell(this);
|
||||
} else {
|
||||
DrawableGroup newGroup = newItem.getGroup();
|
||||
if (isNull(newGroup)) {
|
||||
//this cod epath should only be invoked for non-group Tree
|
||||
final String groupName = getGroupName();
|
||||
//"dummy" group in file system tree <=> a folder with no drawables
|
||||
Platform.runLater(() -> {
|
||||
setTooltip(new Tooltip(groupName));
|
||||
setText(groupName);
|
||||
setGraphic(new ImageView(EMPTY_FOLDER_ICON));
|
||||
setStyle("");
|
||||
});
|
||||
|
||||
} else {
|
||||
updateGroup(this, newGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GroupListCell extends ListCell<DrawableGroup> implements GroupCell<ListView<DrawableGroup>> {
|
||||
|
||||
private final InvalidationListener groupListener = new GroupListener<>(this);
|
||||
|
||||
/**
|
||||
* reference to group files listener that allows us to remove it from a
|
||||
* group when a new group is assigned to this Cell
|
||||
*/
|
||||
@Override
|
||||
public InvalidationListener getGroupListener() {
|
||||
return groupListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListView<DrawableGroup> getView() {
|
||||
return getListView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupName() {
|
||||
return Optional.ofNullable(getItem())
|
||||
.map(group -> group.getGroupByValueDislpayName())
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DrawableGroup> getGroup() {
|
||||
return Optional.ofNullable(getItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void updateItem(final DrawableGroup newGroup, boolean empty) {
|
||||
//if there was a previous group, remove the listeners
|
||||
getGroup().ifPresent(oldGroup -> removeListeners(getGroupListener(), oldGroup));
|
||||
|
||||
super.updateItem(newGroup, empty);
|
||||
|
||||
if (isNull(newGroup) || empty) {
|
||||
clearCell(this);
|
||||
} else {
|
||||
updateGroup(this, newGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface GroupCell<X extends Control> {
|
||||
|
||||
String getGroupName();
|
||||
|
||||
X getView();
|
||||
|
||||
Optional<DrawableGroup> getGroup();
|
||||
|
||||
InvalidationListener getGroupListener();
|
||||
}
|
||||
|
||||
private class GroupListener<X extends Labeled & GroupCell<?>> implements InvalidationListener {
|
||||
|
||||
private final X cell;
|
||||
|
||||
GroupListener(X cell) {
|
||||
this.cell = cell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidated(Observable o) {
|
||||
final String text = getCellText(cell);
|
||||
final String style = getSeenStyleClass(cell);
|
||||
Platform.runLater(() -> {
|
||||
cell.setText(text);
|
||||
cell.setTooltip(new Tooltip(text));
|
||||
cell.setStyle(style);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015-16 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> 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.imagegallery.gui.navpanel;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
import java.util.Optional;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.OverrunStyle;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javax.annotation.Nonnull;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class GroupListCell extends ListCell<DrawableGroup> {
|
||||
|
||||
/**
|
||||
* icon to use if this cell's TreeNode doesn't represent a group but just a
|
||||
* folder(with no DrawableFiles) in the file system hierarchy.
|
||||
*/
|
||||
private static final Image EMPTY_FOLDER_ICON =
|
||||
new Image(GroupTreeCell.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/folder.png")); //NON-NLS
|
||||
|
||||
/**
|
||||
* reference to group files listener that allows us to remove it from a
|
||||
* group when a new group is assigned to this Cell
|
||||
*/
|
||||
private final InvalidationListener fileCountListener = (Observable o) -> {
|
||||
final String text = getGroupName() + getCountsText();
|
||||
Platform.runLater(() -> {
|
||||
setText(text);
|
||||
setTooltip(new Tooltip(text));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* reference to group seen listener that allows us to remove it from a group
|
||||
* when a new group is assigned to this Cell
|
||||
*/
|
||||
private final InvalidationListener seenListener = (Observable o) -> {
|
||||
final String style = getSeenStyleClass();
|
||||
Platform.runLater(() -> setStyle(style));
|
||||
};
|
||||
|
||||
private final ReadOnlyObjectProperty<GroupComparators<?>> sortOrder;
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
GroupListCell(ImageGalleryController controller, ReadOnlyObjectProperty<GroupComparators<?>> sortOrderProperty) {
|
||||
this.controller = controller;
|
||||
this.sortOrder = sortOrderProperty;
|
||||
getStylesheets().add(GroupTreeCell.class.getResource("GroupTreeCell.css").toExternalForm()); //NON-NLS
|
||||
getStyleClass().add("groupTreeCell"); //reduce indent to 5, default is 10 which uses up a lot of space. NON-NLS
|
||||
|
||||
//since end of path is probably more interesting put ellipsis at front
|
||||
setTextOverrun(OverrunStyle.LEADING_ELLIPSIS);
|
||||
Platform.runLater(() -> prefWidthProperty().bind(getListView().widthProperty().subtract(15)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void updateItem(final DrawableGroup group, boolean empty) {
|
||||
//if there was a previous group, remove the listeners
|
||||
Optional.ofNullable(getItem())
|
||||
.ifPresent(oldGroup -> {
|
||||
sortOrder.removeListener(fileCountListener);
|
||||
oldGroup.getFileIDs().removeListener(fileCountListener);
|
||||
oldGroup.seenProperty().removeListener(seenListener);
|
||||
oldGroup.uncatCountProperty().removeListener(fileCountListener);
|
||||
oldGroup.hashSetHitsCountProperty().removeListener(fileCountListener);
|
||||
});
|
||||
|
||||
super.updateItem(group, empty);
|
||||
|
||||
if (isNull(group) || empty) {
|
||||
Platform.runLater(() -> {
|
||||
setTooltip(null);
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
setStyle("");
|
||||
});
|
||||
} else {
|
||||
final String text = getGroupName() + getCountsText();
|
||||
String style;
|
||||
Node icon;
|
||||
if (isNull(group)) {
|
||||
//"dummy" group in file system tree <=> a folder with no drawables
|
||||
icon = new ImageView(EMPTY_FOLDER_ICON);
|
||||
style = "";
|
||||
} else {
|
||||
//if number of files in this group changes (eg a file is recategorized), update counts via listener
|
||||
group.getFileIDs().addListener(fileCountListener);
|
||||
group.uncatCountProperty().addListener(fileCountListener);
|
||||
group.hashSetHitsCountProperty().addListener(fileCountListener);
|
||||
sortOrder.addListener(fileCountListener);
|
||||
//if the seen state of this group changes update its style
|
||||
group.seenProperty().addListener(seenListener);
|
||||
|
||||
//and use icon corresponding to group type
|
||||
icon = (group.getGroupByAttribute() == DrawableAttribute.TAGS)
|
||||
? controller.getTagsManager().getGraphic((TagName) group.getGroupByValue())
|
||||
: group.getGroupKey().getGraphic();
|
||||
style = getSeenStyleClass();
|
||||
}
|
||||
|
||||
Platform.runLater(() -> {
|
||||
setTooltip(new Tooltip(text));
|
||||
setGraphic(icon);
|
||||
setText(text);
|
||||
setStyle(style);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private String getGroupName() {
|
||||
return Optional.ofNullable(getItem())
|
||||
.map(group -> group.getGroupByValueDislpayName())
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
/**
|
||||
* return the styleClass to apply based on the assigned group's seen status
|
||||
*
|
||||
* @return the style class to apply
|
||||
*/
|
||||
@Nonnull
|
||||
private String getSeenStyleClass() {
|
||||
return Optional.ofNullable(getItem())
|
||||
.map(DrawableGroup::isSeen)
|
||||
.map(seen -> seen ? "" : "-fx-font-weight:bold;") //NON-NLS
|
||||
.orElse(""); //if item is null or group is null
|
||||
}
|
||||
|
||||
/**
|
||||
* get the counts part of the text to apply to this cell, including
|
||||
* parentheses
|
||||
*
|
||||
* @return get the counts part of the text to apply to this cell
|
||||
*/
|
||||
@Nonnull
|
||||
private String getCountsText() {
|
||||
return Optional.ofNullable(getItem())
|
||||
.map(group ->
|
||||
" (" + (sortOrder.get() == GroupComparators.ALPHABETICAL
|
||||
? group.getSize()
|
||||
: sortOrder.get().getFormattedValueOfGroup(group)) + ")"
|
||||
).orElse(""); //if item is null or group is null
|
||||
}
|
||||
}
|
@ -71,8 +71,9 @@ final public class GroupTree extends NavPanel<TreeItem<GroupTreeNode>> {
|
||||
BooleanBinding groupedByPath = Bindings.equal(getGroupManager().getGroupByProperty(), DrawableAttribute.PATH);
|
||||
getToolBar().visibleProperty().bind(groupedByPath.not());
|
||||
getToolBar().managedProperty().bind(groupedByPath.not());
|
||||
GroupCellFactory groupCellFactory = new GroupCellFactory(getController(), getSortByBox().getSelectionModel().selectedItemProperty());
|
||||
|
||||
groupTree.setCellFactory(treeView -> new GroupTreeCell(getController(), getSortByBox().getSelectionModel().selectedItemProperty()));
|
||||
groupTree.setCellFactory(groupCellFactory::getTreeCell);
|
||||
groupTree.setShowRoot(false);
|
||||
|
||||
getGroupManager().getAnalyzedGroups().addListener((ListChangeListener.Change<? extends DrawableGroup> change) -> {
|
||||
|
@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-16 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> 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.imagegallery.gui.navpanel;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
import java.util.Optional;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.OverrunStyle;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.control.TreeCell;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javax.annotation.Nonnull;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
|
||||
/**
|
||||
* A cell in the NavPanel tree that listens to its associated group's fileids
|
||||
* and seen status,and updates GUI to reflect them.
|
||||
*
|
||||
* TODO: we should use getStyleClass().add() rather than setStyle but it didn't
|
||||
* seem to work properly
|
||||
*/
|
||||
class GroupTreeCell extends TreeCell<GroupTreeNode> {
|
||||
|
||||
/**
|
||||
* icon to use if this cell's TreeNode doesn't represent a group but just a
|
||||
* folder(with no DrawableFiles) in the file system hierarchy.
|
||||
*/
|
||||
private static final Image EMPTY_FOLDER_ICON =
|
||||
new Image(GroupTreeCell.class.getResourceAsStream("/org/sleuthkit/autopsy/imagegallery/images/folder.png")); //NON-NLS
|
||||
|
||||
/**
|
||||
* reference to group files listener that allows us to remove it from a
|
||||
* group when a new group is assigned to this Cell
|
||||
*/
|
||||
private final InvalidationListener fileCountListener = (Observable o) -> {
|
||||
final String text = getGroupName() + getCountsText();
|
||||
Platform.runLater(() -> {
|
||||
setText(text);
|
||||
setTooltip(new Tooltip(text));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* reference to group seen listener that allows us to remove it from a group
|
||||
* when a new group is assigned to this Cell
|
||||
*/
|
||||
private final InvalidationListener seenListener = (Observable o) -> {
|
||||
final String style = getSeenStyleClass();
|
||||
Platform.runLater(() -> {
|
||||
setStyle(style);
|
||||
});
|
||||
};
|
||||
private final ReadOnlyObjectProperty<GroupComparators<?>> sortOrder;
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
GroupTreeCell(ImageGalleryController controller, ReadOnlyObjectProperty<GroupComparators<?>> sortOrderProperty) {
|
||||
this.controller = controller;
|
||||
this.sortOrder = sortOrderProperty;
|
||||
getStylesheets().add(GroupTreeCell.class.getResource("GroupTreeCell.css").toExternalForm()); //NON-NLS
|
||||
getStyleClass().add("groupTreeCell"); //reduce indent to 5, default is 10 which uses up a lot of space. NON-NLS
|
||||
|
||||
//since end of path is probably more interesting put ellipsis at front
|
||||
setTextOverrun(OverrunStyle.LEADING_ELLIPSIS);
|
||||
Platform.runLater(() -> {
|
||||
prefWidthProperty().bind(getTreeView().widthProperty().subtract(15));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc }
|
||||
*/
|
||||
@Override
|
||||
protected synchronized void updateItem(final GroupTreeNode treeNode, boolean empty) {
|
||||
//if there was a previous group, remove the listeners
|
||||
Optional.ofNullable(getItem())
|
||||
.map(GroupTreeNode::getGroup)
|
||||
.ifPresent(group -> {
|
||||
sortOrder.addListener(fileCountListener);
|
||||
group.getFileIDs().removeListener(fileCountListener);
|
||||
group.hashSetHitsCountProperty().removeListener(fileCountListener);
|
||||
group.seenProperty().removeListener(seenListener);
|
||||
group.uncatCountProperty().removeListener(fileCountListener);
|
||||
});
|
||||
|
||||
super.updateItem(treeNode, empty);
|
||||
|
||||
if (isNull(treeNode) || empty) {
|
||||
Platform.runLater(() -> {
|
||||
setTooltip(null);
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
setStyle("");
|
||||
});
|
||||
} else {
|
||||
DrawableGroup group = treeNode.getGroup();
|
||||
if (isNull(group)) {
|
||||
final String text = getGroupName();
|
||||
//"dummy" group in file system tree <=> a folder with no drawables
|
||||
Platform.runLater(() -> {
|
||||
setTooltip(new Tooltip(text));
|
||||
setText(text);
|
||||
setGraphic(new ImageView(EMPTY_FOLDER_ICON));
|
||||
setStyle("");
|
||||
});
|
||||
|
||||
} else {
|
||||
//if number of files in this group changes (eg a file is recategorized), update counts via listener
|
||||
group.getFileIDs().addListener(fileCountListener);
|
||||
group.uncatCountProperty().addListener(fileCountListener);
|
||||
group.hashSetHitsCountProperty().addListener(fileCountListener);
|
||||
sortOrder.addListener(fileCountListener);
|
||||
//if the seen state of this group changes update its style
|
||||
group.seenProperty().addListener(seenListener);
|
||||
|
||||
//and use icon corresponding to group type
|
||||
Node icon = (group.getGroupByAttribute() == DrawableAttribute.TAGS)
|
||||
? controller.getTagsManager().getGraphic((TagName) group.getGroupByValue())
|
||||
: group.getGroupKey().getGraphic();
|
||||
final String text = getGroupName() + getCountsText();
|
||||
final String style = getSeenStyleClass();
|
||||
Platform.runLater(() -> {
|
||||
setTooltip(new Tooltip(text));
|
||||
setGraphic(icon);
|
||||
setText(text);
|
||||
setStyle(style);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getGroupName() {
|
||||
return Optional.ofNullable(getItem())
|
||||
.map(treeNode -> StringUtils.defaultIfBlank(treeNode.getPath(), DrawableGroup.getBlankGroupName()))
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
/**
|
||||
* return the styleClass to apply based on the assigned group's seen status
|
||||
*
|
||||
* @return the style class to apply
|
||||
*/
|
||||
@Nonnull
|
||||
private String getSeenStyleClass() {
|
||||
return Optional.ofNullable(getItem())
|
||||
.map(GroupTreeNode::getGroup)
|
||||
.map(DrawableGroup::isSeen)
|
||||
.map(seen -> seen ? "" : "-fx-font-weight:bold;") //NON-NLS
|
||||
.orElse(""); //if item is null or group is null
|
||||
}
|
||||
|
||||
/**
|
||||
* get the counts part of the text to apply to this cell, including
|
||||
* parentheses
|
||||
*
|
||||
* @return get the counts part of the text to apply to this cell
|
||||
*/
|
||||
@Nonnull
|
||||
private String getCountsText() {
|
||||
return Optional.ofNullable(getItem())
|
||||
.map(GroupTreeNode::getGroup)
|
||||
.map(group ->
|
||||
" (" + (sortOrder.get() == GroupComparators.ALPHABETICAL
|
||||
? group.getSize()
|
||||
: sortOrder.get().getFormattedValueOfGroup(group)) + ")"
|
||||
).orElse(""); //if item is null or group is null
|
||||
}
|
||||
}
|
@ -78,9 +78,8 @@ final public class HashHitGroupList extends NavPanel<DrawableGroup> {
|
||||
|
||||
getBorderPane().setCenter(groupList);
|
||||
sorted = getController().getGroupManager().getAnalyzedGroups().filtered((DrawableGroup t) -> t.getHashSetHitsCount() > 0).sorted(getDefaultComparator());
|
||||
|
||||
groupList.setCellFactory(treeView -> new GroupListCell(getController(), getSortByBox().getSelectionModel().selectedItemProperty()));
|
||||
|
||||
GroupCellFactory groupCellFactory = new GroupCellFactory(getController(), getSortByBox().getSelectionModel().selectedItemProperty());
|
||||
groupList.setCellFactory(groupCellFactory::getListCell);
|
||||
groupList.setItems(sorted);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user