org.sleuthkit.autopsy.imagegallery.gui.navpanel.GroupTreeCell.java Source code

Java tutorial

Introduction

Here is the source code for org.sleuthkit.autopsy.imagegallery.gui.navpanel.GroupTreeCell.java

Source

/*
 * Autopsy Forensic Browser
 *
 * Copyright 2013-15 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.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.datamodel.DrawableAttribute;
import org.sleuthkit.autopsy.imagegallery.grouping.DrawableGroup;

/**
 * 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<TreeNode> {

    /**
     * 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"));

    /**
     * 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);
        });
    };

    public GroupTreeCell() {
        getStylesheets().add(GroupTreeCell.class.getResource("GroupTreeCell.css").toExternalForm());
        getStyleClass().add("groupTreeCell"); //reduce  indent to 5, default is 10 which uses up a lot of space.

        //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 TreeNode treeNode, boolean empty) {
        //if there was a previous group, remove the listeners
        Optional.ofNullable(getItem()).map(TreeNode::getGroup).ifPresent(group -> {
            group.fileIds().removeListener(fileCountListener);
            group.seenProperty().removeListener(seenListener);
        });

        super.updateItem(treeNode, empty);

        if (isNull(treeNode) || empty) {
            Platform.runLater(() -> {
                setTooltip(null);
                setText(null);
                setGraphic(null);
                setStyle("");
            });
        } else {

            if (isNull(treeNode.getGroup())) {
                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 {
                //if number of files in this group changes (eg a file is recategorized), update counts via listener
                treeNode.getGroup().fileIds().addListener(fileCountListener);

                //if the seen state of this group changes update its style
                treeNode.getGroup().seenProperty().addListener(seenListener);

                //and use icon corresponding to group type
                final Image icon = treeNode.getGroup().groupKey.getAttribute().getIcon();
                final String text = getGroupName() + getCountsText();
                final String style = getSeenStyleClass();
                Platform.runLater(() -> {
                    setTooltip(new Tooltip(text));
                    setGraphic(new ImageView(icon));
                    setText(text);
                    setStyle(style);
                });
            }
        }
    }

    private String getGroupName() {
        return Optional.ofNullable(getItem())
                .map((TreeNode t) -> StringUtils.defaultIfBlank(t.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(TreeNode::getGroup).map(DrawableGroup::isSeen)
                .map(seen -> seen ? "" : "-fx-font-weight:bold;").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(TreeNode::getGroup)
                .map(group -> " (" + ((group.getGroupByAttribute() == DrawableAttribute.HASHSET)
                        ? Integer.toString(group.getSize())
                        : group.getHashSetHitsCount() + "/" + group.getSize()) + ")")
                .orElse(""); //if item is null or group is null
    }
}