/* * Copyright (c) 2018 Karl Tauber <karl at jformdesigner dot com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.markdownwriterfx.controls; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.collections.ObservableList; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.input.MouseButton; /** * A tree view of directories and files. * * Refreshes the file tree on window activation. * * @author Karl Tauber */ public class FileTreeView extends TreeView<File> { // need a hard reference to avoid GC private final BooleanBinding windowFocusedProperty; public FileTreeView() { setCellFactory(treeView -> new FileTreeCell()); windowFocusedProperty = Bindings.selectBoolean(sceneProperty(), "window", "focused"); // use runLater() for adding listener to avoid unnecessary refresh after initial creation Platform.runLater(() -> { windowFocusedProperty.addListener((observer, oldFocused, newFocused) -> { if (newFocused) Platform.runLater(() -> refreshFiles()); }); }); } protected void handleClicks(TreeItem<File> item, MouseButton button, int clickCount) { } public void refreshFiles() { if (getRoot() instanceof FileTreeItem) ((FileTreeItem)getRoot()).refresh(); } public List<File> getExpandedDirectories() { if (getRoot() == null) return Collections.emptyList(); return items2files(findItems(item -> item.isExpanded())); } public void setExpandedDirectories(List<File> expandedDirectories) { if (getRoot() == null) return; HashSet<File> expandedDirectoriesSet = new HashSet<>(expandedDirectories); expandDirectories(getRoot(), expandedDirectoriesSet); } private void expandDirectories(TreeItem<File> item, HashSet<File> expandedDirectoriesSet) { getLoadedChildren(item).forEach(child -> { if (!child.isLeaf()) { if (expandedDirectoriesSet.contains(child.getValue())) child.setExpanded(true); expandDirectories(child, expandedDirectoriesSet); } }); } public List<TreeItem<File>> findItems(Predicate<TreeItem<File>> predicate) { if (getRoot() == null) return Collections.emptyList(); ArrayList<TreeItem<File>> items = new ArrayList<>(); findItemsRecur(predicate, getRoot(), items); return items; } private void findItemsRecur(Predicate<TreeItem<File>> predicate, TreeItem<File> item, List<TreeItem<File>> items) { if (predicate.test(item)) items.add(item); getLoadedChildren(item).forEach(child -> { findItemsRecur(predicate, child, items); }); } private List<File> items2files(List<TreeItem<File>> items) { return items.stream() .map(item -> item.getValue()) .collect(Collectors.toList()); } private ObservableList<TreeItem<File>> getLoadedChildren(TreeItem<File> item) { return (item instanceof FileTreeItem && !item.isExpanded()) ? ((FileTreeItem)item).getLoadedChildren() : item.getChildren(); } }