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);
|
group = new DrawableGroup(groupKey, fileIDs, groupSeen);
|
||||||
controller.getCategoryManager().registerListener(group);
|
controller.getCategoryManager().registerListener(group);
|
||||||
group.seenProperty().addListener((o, oldSeen, newSeen) -> {
|
group.seenProperty().addListener((o, oldSeen, newSeen) -> {
|
||||||
markGroupSeen(group, newSeen);
|
Platform.runLater(() -> markGroupSeen(group, newSeen));
|
||||||
});
|
});
|
||||||
groupMap.put(groupKey, group);
|
groupMap.put(groupKey, group);
|
||||||
}
|
}
|
||||||
@ -719,12 +719,12 @@ public class GroupManager {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
@NbBundle.Messages({"# {0} - groupBy attribute Name",
|
@NbBundle.Messages({"# {0} - groupBy attribute Name",
|
||||||
"# {1} - sortBy name",
|
"# {1} - sortBy name",
|
||||||
"# {2} - sort Order",
|
"# {2} - sort Order",
|
||||||
"ReGroupTask.displayTitle=regrouping files by {0} sorted by {1} in {2} order",
|
"ReGroupTask.displayTitle=regrouping files by {0} sorted by {1} in {2} order",
|
||||||
"# {0} - groupBy attribute Name",
|
"# {0} - groupBy attribute Name",
|
||||||
"# {1} - atribute value",
|
"# {1} - atribute value",
|
||||||
"ReGroupTask.progressUpdate=regrouping files by {0} : {1}"})
|
"ReGroupTask.progressUpdate=regrouping files by {0} : {1}"})
|
||||||
private class ReGroupTask<A extends Comparable<A>> extends LoggedTask<Void> {
|
private class ReGroupTask<A extends Comparable<A>> extends LoggedTask<Void> {
|
||||||
|
|
||||||
private ProgressHandle groupProgress;
|
private ProgressHandle groupProgress;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
.groupTreeCell{
|
.groupCell{
|
||||||
|
|
||||||
-fx-indent:5; /* default indent is 10 */
|
-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);
|
BooleanBinding groupedByPath = Bindings.equal(getGroupManager().getGroupByProperty(), DrawableAttribute.PATH);
|
||||||
getToolBar().visibleProperty().bind(groupedByPath.not());
|
getToolBar().visibleProperty().bind(groupedByPath.not());
|
||||||
getToolBar().managedProperty().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);
|
groupTree.setShowRoot(false);
|
||||||
|
|
||||||
getGroupManager().getAnalyzedGroups().addListener((ListChangeListener.Change<? extends DrawableGroup> change) -> {
|
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);
|
getBorderPane().setCenter(groupList);
|
||||||
sorted = getController().getGroupManager().getAnalyzedGroups().filtered((DrawableGroup t) -> t.getHashSetHitsCount() > 0).sorted(getDefaultComparator());
|
sorted = getController().getGroupManager().getAnalyzedGroups().filtered((DrawableGroup t) -> t.getHashSetHitsCount() > 0).sorted(getDefaultComparator());
|
||||||
|
GroupCellFactory groupCellFactory = new GroupCellFactory(getController(), getSortByBox().getSelectionModel().selectedItemProperty());
|
||||||
groupList.setCellFactory(treeView -> new GroupListCell(getController(), getSortByBox().getSelectionModel().selectedItemProperty()));
|
groupList.setCellFactory(groupCellFactory::getListCell);
|
||||||
|
|
||||||
groupList.setItems(sorted);
|
groupList.setItems(sorted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user