diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java index cebcd6c222..e1f497a295 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java @@ -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> extends LoggedTask { private ProgressHandle groupProgress; diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTreeCell.css b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupCell.css similarity index 87% rename from ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTreeCell.css rename to ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupCell.css index 2302510cc7..6d7c52b302 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTreeCell.css +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupCell.css @@ -1,4 +1,4 @@ -.groupTreeCell{ +.groupCell{ -fx-indent:5; /* default indent is 10 */ } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupCellFactory.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupCellFactory.java new file mode 100644 index 0000000000..d21b76d018 --- /dev/null +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupCellFactory.java @@ -0,0 +1,306 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2016 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.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 or + * TreeView + */ +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> sortOrder; + private final ImageGalleryController controller; + + GroupCellFactory(ImageGalleryController controller, ReadOnlyObjectProperty> sortOrderProperty) { + this.controller = controller; + this.sortOrder = sortOrderProperty; + } + + GroupListCell getListCell(ListView 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 & 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 & 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 > 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 implements GroupCell> { + + 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 getView() { + return getTreeView(); + } + + @Override + public String getGroupName() { + return Optional.ofNullable(getItem()) + .map(treeNode -> StringUtils.defaultIfBlank(treeNode.getPath(), DrawableGroup.getBlankGroupName())) + .orElse(""); + } + + @Override + public Optional 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 implements GroupCell> { + + 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 getView() { + return getListView(); + } + + @Override + public String getGroupName() { + return Optional.ofNullable(getItem()) + .map(group -> group.getGroupByValueDislpayName()) + .orElse(""); + } + + @Override + public Optional 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 { + + String getGroupName(); + + X getView(); + + Optional getGroup(); + + InvalidationListener getGroupListener(); + } + + private class GroupListener> 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); + }); + } + } +} diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupListCell.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupListCell.java deleted file mode 100644 index 6a48dc78b4..0000000000 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupListCell.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2015-16 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.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 { - - /** - * 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> sortOrder; - private final ImageGalleryController controller; - - GroupListCell(ImageGalleryController controller, ReadOnlyObjectProperty> 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 - } -} diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTree.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTree.java index 0813c91190..5062f1ba37 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTree.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTree.java @@ -71,8 +71,9 @@ final public class GroupTree extends NavPanel> { 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 change) -> { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTreeCell.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTreeCell.java deleted file mode 100644 index e81633ad16..0000000000 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTreeCell.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2013-16 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.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 { - - /** - * 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> sortOrder; - private final ImageGalleryController controller; - - GroupTreeCell(ImageGalleryController controller, ReadOnlyObjectProperty> 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 - } -} diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/HashHitGroupList.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/HashHitGroupList.java index 790262b78e..a7fd97723b 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/HashHitGroupList.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/HashHitGroupList.java @@ -78,9 +78,8 @@ final public class HashHitGroupList extends NavPanel { 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); }