com.intellij.util.ui.tree.AbstractFileTreeTable.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.util.ui.tree.AbstractFileTreeTable.java

Source

/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * 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 com.intellij.util.ui.tree;

import com.intellij.icons.AllIcons;
import com.intellij.ide.CommonActionsManager;
import com.intellij.ide.DefaultTreeExpander;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileFilter;
import com.intellij.ui.TableUtil;
import com.intellij.ui.TreeTableSpeedSearch;
import com.intellij.ui.treeStructure.treetable.TreeTable;
import com.intellij.ui.treeStructure.treetable.TreeTableCellRenderer;
import com.intellij.ui.treeStructure.treetable.TreeTableModel;
import com.intellij.util.IconUtil;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.UIUtil;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.table.TableColumn;
import javax.swing.tree.*;
import java.awt.*;
import java.util.*;
import java.util.List;

public abstract class AbstractFileTreeTable<T> extends TreeTable {
    private final MyModel<T> myModel;
    private final Project myProject;

    public AbstractFileTreeTable(@NotNull Project project, @NotNull Class<T> valueClass, @NotNull String valueTitle,
            @NotNull VirtualFileFilter filter, boolean showProjectNode) {
        super(new MyModel<T>(project, valueClass, valueTitle, filter));
        myProject = project;

        myModel = (MyModel) getTableModel();
        myModel.setTreeTable(this);

        new TreeTableSpeedSearch(this, new Convertor<TreePath, String>() {
            @Override
            public String convert(final TreePath o) {
                final DefaultMutableTreeNode node = (DefaultMutableTreeNode) o.getLastPathComponent();
                final Object userObject = node.getUserObject();
                if (userObject == null) {
                    return getProjectNodeText();
                }
                if (userObject instanceof VirtualFile) {
                    return ((VirtualFile) userObject).getName();
                }
                return node.toString();
            }
        });
        final DefaultTreeExpander treeExpander = new DefaultTreeExpander(getTree());
        CommonActionsManager.getInstance().createExpandAllAction(treeExpander, this);
        CommonActionsManager.getInstance().createCollapseAllAction(treeExpander, this);

        getTree().setShowsRootHandles(true);
        getTree().setLineStyleAngled();
        getTree().setRootVisible(showProjectNode);
        getTree().setCellRenderer(new DefaultTreeCellRenderer() {
            @Override
            public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean sel,
                    final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) {
                super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
                if (value instanceof ProjectRootNode) {
                    setText(getProjectNodeText());
                    setIcon(AllIcons.Nodes.ProjectTab);
                    return this;
                }
                FileNode fileNode = (FileNode) value;
                VirtualFile file = fileNode.getObject();
                if (fileNode.getParent() instanceof FileNode) {
                    setText(file.getName());
                } else {
                    setText(file.getPresentableUrl());
                }

                Icon icon = file.isDirectory() ? AllIcons.Nodes.TreeClosed : IconUtil.getIcon(file, 0, null);
                setIcon(icon);
                return this;
            }
        });
        getTableHeader().setReorderingAllowed(false);

        setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        setPreferredScrollableViewportSize(new Dimension(300, getRowHeight() * 10));

        getColumnModel().getColumn(0).setPreferredWidth(280);
        getColumnModel().getColumn(1).setPreferredWidth(60);
    }

    protected boolean isNullObject(final T value) {
        return false;
    }

    private static String getProjectNodeText() {
        return "Project";
    }

    public Project getProject() {
        return myProject;
    }

    public TableColumn getValueColumn() {
        return getColumnModel().getColumn(1);
    }

    protected boolean isValueEditableForFile(final VirtualFile virtualFile) {
        return true;
    }

    public static void press(final Container comboComponent) {
        if (comboComponent instanceof JButton) {
            final JButton button = (JButton) comboComponent;
            button.doClick();
        } else {
            for (int i = 0; i < comboComponent.getComponentCount(); i++) {
                Component child = comboComponent.getComponent(i);
                if (child instanceof Container) {
                    press((Container) child);
                }
            }
        }
    }

    public boolean clearSubdirectoriesOnDemandOrCancel(final VirtualFile parent, final String message,
            final String title) {
        Map<VirtualFile, T> mappings = myModel.myCurrentMapping;
        Map<VirtualFile, T> subdirectoryMappings = new THashMap<VirtualFile, T>();
        for (VirtualFile file : mappings.keySet()) {
            if (file != null && (parent == null || VfsUtilCore.isAncestor(parent, file, true))) {
                subdirectoryMappings.put(file, mappings.get(file));
            }
        }
        if (subdirectoryMappings.isEmpty()) {
            return true;
        }
        int ret = Messages.showYesNoCancelDialog(myProject, message, title, "Override", "Do Not Override", "Cancel",
                Messages.getWarningIcon());
        if (ret == 0) {
            for (VirtualFile file : subdirectoryMappings.keySet()) {
                myModel.setValueAt(null, new DefaultMutableTreeNode(file), 1);
            }
        }
        return ret != 2;
    }

    @NotNull
    public Map<VirtualFile, T> getValues() {
        return myModel.getValues();
    }

    @Override
    public TreeTableCellRenderer createTableRenderer(TreeTableModel treeTableModel) {
        TreeTableCellRenderer tableRenderer = super.createTableRenderer(treeTableModel);
        UIUtil.setLineStyleAngled(tableRenderer);
        tableRenderer.setRootVisible(false);
        tableRenderer.setShowsRootHandles(true);

        return tableRenderer;
    }

    public void reset(@NotNull Map<VirtualFile, T> mappings) {
        myModel.reset(mappings);
        final TreeNode root = (TreeNode) myModel.getRoot();
        myModel.nodeChanged(root);
        getTree().setModel(null);
        getTree().setModel(myModel);
        TreeUtil.expandRootChildIfOnlyOne(getTree());
    }

    public void select(@Nullable final VirtualFile toSelect) {
        if (toSelect != null) {
            select(toSelect, (TreeNode) myModel.getRoot());
        }
    }

    private void select(@NotNull VirtualFile toSelect, final TreeNode root) {
        for (int i = 0; i < root.getChildCount(); i++) {
            TreeNode child = root.getChildAt(i);
            VirtualFile file = ((FileNode) child).getObject();
            if (VfsUtilCore.isAncestor(file, toSelect, false)) {
                if (Comparing.equal(file, toSelect)) {
                    TreeUtil.selectNode(getTree(), child);
                    getSelectionModel().clearSelection();
                    addSelectedPath(TreeUtil.getPathFromRoot(child));
                    TableUtil.scrollSelectionToVisible(this);
                } else {
                    select(toSelect, child);
                }
                return;
            }
        }
    }

    private static class MyModel<T> extends DefaultTreeModel implements TreeTableModel {
        private final Map<VirtualFile, T> myCurrentMapping = new HashMap<VirtualFile, T>();
        private final Class<T> myValueClass;
        private final String myValueTitle;
        private AbstractFileTreeTable<T> myTreeTable;

        private MyModel(@NotNull Project project, @NotNull Class<T> valueClass, @NotNull String valueTitle,
                @NotNull VirtualFileFilter filter) {
            super(new ProjectRootNode(project, filter));
            myValueClass = valueClass;
            myValueTitle = valueTitle;
        }

        private Map<VirtualFile, T> getValues() {
            return new HashMap<VirtualFile, T>(myCurrentMapping);
        }

        @Override
        public void setTree(JTree tree) {
        }

        @Override
        public int getColumnCount() {
            return 2;
        }

        @Override
        public String getColumnName(final int column) {
            switch (column) {
            case 0:
                return "File/Directory";
            case 1:
                return myValueTitle;
            default:
                throw new RuntimeException("invalid column " + column);
            }
        }

        @Override
        public Class getColumnClass(final int column) {
            switch (column) {
            case 0:
                return TreeTableModel.class;
            case 1:
                return myValueClass;
            default:
                throw new RuntimeException("invalid column " + column);
            }
        }

        @Override
        public Object getValueAt(final Object node, final int column) {
            Object userObject = ((DefaultMutableTreeNode) node).getUserObject();
            if (userObject instanceof Project) {
                switch (column) {
                case 0:
                    return userObject;
                case 1:
                    return myCurrentMapping.get(null);
                }
            }
            VirtualFile file = (VirtualFile) userObject;
            switch (column) {
            case 0:
                return file;
            case 1:
                return myCurrentMapping.get(file);
            default:
                throw new RuntimeException("invalid column " + column);
            }
        }

        @Override
        public boolean isCellEditable(final Object node, final int column) {
            switch (column) {
            case 0:
                return false;
            case 1:
                final Object userObject = ((DefaultMutableTreeNode) node).getUserObject();
                return !(userObject instanceof VirtualFile || userObject == null)
                        || myTreeTable.isValueEditableForFile((VirtualFile) userObject);
            default:
                throw new RuntimeException("invalid column " + column);
            }
        }

        @Override
        public void setValueAt(final Object aValue, final Object node, final int column) {
            final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
            final Object userObject = treeNode.getUserObject();
            if (userObject instanceof Project)
                return;
            final VirtualFile file = (VirtualFile) userObject;
            final T t = (T) aValue;
            if (t == null || myTreeTable.isNullObject(t)) {
                myCurrentMapping.remove(file);
            } else {
                myCurrentMapping.put(file, t);
            }
            fireTreeNodesChanged(this, new Object[] { getRoot() }, null, null);
        }

        public void reset(@NotNull Map<VirtualFile, T> mappings) {
            myCurrentMapping.clear();
            myCurrentMapping.putAll(mappings);
            ((ProjectRootNode) getRoot()).clearCachedChildren();
        }

        void setTreeTable(final AbstractFileTreeTable<T> treeTable) {
            myTreeTable = treeTable;
        }
    }

    public static class ProjectRootNode extends ConvenientNode<Project> {
        private VirtualFileFilter myFilter;

        public ProjectRootNode(@NotNull Project project) {
            this(project, VirtualFileFilter.ALL);
        }

        public ProjectRootNode(@NotNull Project project, @NotNull VirtualFileFilter filter) {
            super(project);
            myFilter = filter;
        }

        @Override
        protected void appendChildrenTo(@NotNull final Collection<ConvenientNode> children) {
            Project project = getObject();
            VirtualFile[] roots = ProjectRootManager.getInstance(project).getContentRoots();

            NextRoot: for (VirtualFile root : roots) {
                for (VirtualFile candidate : roots) {
                    if (VfsUtilCore.isAncestor(candidate, root, true))
                        continue NextRoot;
                }
                if (myFilter.accept(root)) {
                    children.add(new FileNode(root, project, myFilter));
                }
            }
        }
    }

    public abstract static class ConvenientNode<T> extends DefaultMutableTreeNode {
        private final T myObject;

        private ConvenientNode(T object) {
            myObject = object;
        }

        public T getObject() {
            return myObject;
        }

        protected abstract void appendChildrenTo(@NotNull Collection<ConvenientNode> children);

        @Override
        public int getChildCount() {
            init();
            return super.getChildCount();
        }

        @Override
        public TreeNode getChildAt(final int childIndex) {
            init();
            return super.getChildAt(childIndex);
        }

        @Override
        public Enumeration children() {
            init();
            return super.children();
        }

        private void init() {
            if (getUserObject() == null) {
                setUserObject(myObject);
                final List<ConvenientNode> children = new ArrayList<ConvenientNode>();
                appendChildrenTo(children);
                Collections.sort(children, new Comparator<ConvenientNode>() {
                    @Override
                    public int compare(final ConvenientNode node1, final ConvenientNode node2) {
                        Object o1 = node1.getObject();
                        Object o2 = node2.getObject();
                        if (o1 == o2)
                            return 0;
                        if (o1 instanceof Project)
                            return -1;
                        if (o2 instanceof Project)
                            return 1;
                        VirtualFile file1 = (VirtualFile) o1;
                        VirtualFile file2 = (VirtualFile) o2;
                        if (file1.isDirectory() != file2.isDirectory()) {
                            return file1.isDirectory() ? -1 : 1;
                        }
                        return file1.getName().compareTo(file2.getName());
                    }
                });
                int i = 0;
                for (ConvenientNode child : children) {
                    insert(child, i++);
                }
            }
        }

        public void clearCachedChildren() {
            if (children != null) {
                for (Object child : children) {
                    ConvenientNode<T> node = (ConvenientNode<T>) child;
                    node.clearCachedChildren();
                }
            }
            removeAllChildren();
            setUserObject(null);
        }
    }

    public static class FileNode extends ConvenientNode<VirtualFile> {
        private final Project myProject;
        private VirtualFileFilter myFilter;

        public FileNode(@NotNull VirtualFile file, @NotNull final Project project) {
            this(file, project, VirtualFileFilter.ALL);
        }

        public FileNode(@NotNull VirtualFile file, @NotNull final Project project,
                @NotNull VirtualFileFilter filter) {
            super(file);
            myProject = project;
            myFilter = filter;
        }

        @Override
        protected void appendChildrenTo(@NotNull final Collection<ConvenientNode> children) {
            VirtualFile[] childrenf = getObject().getChildren();
            ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
            for (VirtualFile child : childrenf) {
                if (myFilter.accept(child) && fileIndex.isInContent(child)) {
                    children.add(new FileNode(child, myProject, myFilter));
                }
            }
        }
    }

}