com.intellij.ide.util.TreeFileChooserDialog.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.ide.util.TreeFileChooserDialog.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.ide.util;

import com.intellij.ide.IdeBundle;
import com.intellij.ide.projectView.BaseProjectTreeBuilder;
import com.intellij.ide.projectView.ProjectViewNode;
import com.intellij.ide.projectView.TreeStructureProvider;
import com.intellij.ide.projectView.impl.AbstractProjectTreeStructure;
import com.intellij.ide.projectView.impl.ProjectAbstractTreeStructureBase;
import com.intellij.ide.projectView.impl.ProjectTreeBuilder;
import com.intellij.ide.projectView.impl.nodes.PsiFileNode;
import com.intellij.ide.util.gotoByName.ChooseByNameModel;
import com.intellij.ide.util.gotoByName.ChooseByNamePanel;
import com.intellij.ide.util.gotoByName.ChooseByNamePopupComponent;
import com.intellij.ide.util.gotoByName.GotoFileCellRenderer;
import com.intellij.ide.util.treeView.AlphaComparator;
import com.intellij.ide.util.treeView.NodeRenderer;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.FileTypeIndex;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.DoubleClickListener;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.TabbedPaneWrapper;
import com.intellij.ui.TreeSpeedSearch;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;

/**
 * @author Anton Katilin
 * @author Vladimir Kondratyev
 */
public final class TreeFileChooserDialog extends DialogWrapper implements TreeFileChooser {
    private Tree myTree;
    private PsiFile mySelectedFile = null;
    private final Project myProject;
    private BaseProjectTreeBuilder myBuilder;
    private TabbedPaneWrapper myTabbedPane;
    private ChooseByNamePanel myGotoByNamePanel;
    @Nullable
    private final PsiFile myInitialFile;
    @Nullable
    private final PsiFileFilter myFilter;
    @Nullable
    private final FileType myFileType;

    private final boolean myDisableStructureProviders;
    private final boolean myShowLibraryContents;
    private boolean mySelectSearchByNameTab = false;

    public TreeFileChooserDialog(final Project project, String title, @Nullable final PsiFile initialFile,
            @Nullable FileType fileType, @Nullable PsiFileFilter filter, final boolean disableStructureProviders,
            final boolean showLibraryContents) {
        super(project, true);
        myInitialFile = initialFile;
        myFilter = filter;
        myFileType = fileType;
        myDisableStructureProviders = disableStructureProviders;
        myShowLibraryContents = showLibraryContents;
        setTitle(title);
        myProject = project;
        init();
        if (initialFile != null) {
            // dialog does not exist yet
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    selectFile(initialFile);
                }
            });
        }

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                handleSelectionChanged();
            }
        });
    }

    @Override
    protected JComponent createCenterPanel() {
        final DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode());
        myTree = new Tree(model);

        final ProjectAbstractTreeStructureBase treeStructure = new AbstractProjectTreeStructure(myProject) {
            @Override
            public boolean isFlattenPackages() {
                return false;
            }

            @Override
            public boolean isShowMembers() {
                return false;
            }

            @Override
            public boolean isHideEmptyMiddlePackages() {
                return true;
            }

            @Override
            public Object[] getChildElements(final Object element) {
                return filterFiles(super.getChildElements(element));
            }

            @Override
            public boolean isAbbreviatePackageNames() {
                return false;
            }

            @Override
            public boolean isShowLibraryContents() {
                return myShowLibraryContents;
            }

            @Override
            public boolean isShowModules() {
                return false;
            }

            @Override
            public List<TreeStructureProvider> getProviders() {
                return myDisableStructureProviders ? null : super.getProviders();
            }
        };
        myBuilder = new ProjectTreeBuilder(myProject, myTree, model, AlphaComparator.INSTANCE, treeStructure);

        myTree.setRootVisible(false);
        myTree.expandRow(0);
        myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        myTree.setCellRenderer(new NodeRenderer());
        UIUtil.setLineStyleAngled(myTree);

        final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTree);
        scrollPane.setPreferredSize(new Dimension(500, 300));

        myTree.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(final KeyEvent e) {
                if (KeyEvent.VK_ENTER == e.getKeyCode()) {
                    doOKAction();
                }
            }
        });

        new DoubleClickListener() {
            @Override
            protected boolean onDoubleClick(MouseEvent e) {
                final TreePath path = myTree.getPathForLocation(e.getX(), e.getY());
                if (path != null && myTree.isPathSelected(path)) {
                    doOKAction();
                    return true;
                }
                return false;
            }
        }.installOn(myTree);

        myTree.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(final TreeSelectionEvent e) {
                handleSelectionChanged();
            }
        });

        new TreeSpeedSearch(myTree);

        myTabbedPane = new TabbedPaneWrapper(getDisposable());

        final JPanel dummyPanel = new JPanel(new BorderLayout());
        String name = null;
        if (myInitialFile != null) {
            name = myInitialFile.getName();
        }
        PsiElement context = myInitialFile == null ? null : myInitialFile;
        myGotoByNamePanel = new ChooseByNamePanel(myProject, new MyGotoFileModel(), name, true, context) {
            @Override
            protected void close(final boolean isOk) {
                super.close(isOk);

                if (isOk) {
                    doOKAction();
                } else {
                    doCancelAction();
                }
            }

            @Override
            protected void initUI(final ChooseByNamePopupComponent.Callback callback,
                    final ModalityState modalityState, boolean allowMultipleSelection) {
                super.initUI(callback, modalityState, allowMultipleSelection);
                dummyPanel.add(myGotoByNamePanel.getPanel(), BorderLayout.CENTER);
                //IdeFocusTraversalPolicy.getPreferredFocusedComponent(myGotoByNamePanel.getPanel()).requestFocus();
                if (mySelectSearchByNameTab) {
                    myTabbedPane.setSelectedIndex(1);
                }
            }

            @Override
            protected void showTextFieldPanel() {
            }

            @Override
            protected void chosenElementMightChange() {
                handleSelectionChanged();
            }
        };

        myTabbedPane.addTab(IdeBundle.message("tab.chooser.project"), scrollPane);
        myTabbedPane.addTab(IdeBundle.message("tab.chooser.search.by.name"), dummyPanel);

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                myGotoByNamePanel.invoke(new MyCallback(), ModalityState.stateForComponent(getRootPane()), false);
            }
        });

        myTabbedPane.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(final ChangeEvent e) {
                handleSelectionChanged();
            }
        });

        return myTabbedPane.getComponent();
    }

    public void selectSearchByNameTab() {
        mySelectSearchByNameTab = true;
    }

    private void handleSelectionChanged() {
        final PsiFile selection = calcSelectedClass();
        setOKActionEnabled(selection != null);
    }

    @Override
    protected void doOKAction() {
        mySelectedFile = calcSelectedClass();
        if (mySelectedFile == null)
            return;
        super.doOKAction();
    }

    @Override
    public void doCancelAction() {
        mySelectedFile = null;
        super.doCancelAction();
    }

    @Override
    public PsiFile getSelectedFile() {
        return mySelectedFile;
    }

    @Override
    public void selectFile(@NotNull final PsiFile file) {
        // Select element in the tree
        ApplicationManager.getApplication().invokeLater(new Runnable() {
            @Override
            public void run() {
                if (myBuilder != null) {
                    myBuilder.select(file, file.getVirtualFile(), true);
                }
            }
        }, ModalityState.stateForComponent(getWindow()));
    }

    @Override
    public void showDialog() {
        show();
    }

    private PsiFile calcSelectedClass() {
        if (myTabbedPane.getSelectedIndex() == 1) {
            return (PsiFile) myGotoByNamePanel.getChosenElement();
        } else {
            final TreePath path = myTree.getSelectionPath();
            if (path == null)
                return null;
            final DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
            final Object userObject = node.getUserObject();
            if (!(userObject instanceof ProjectViewNode))
                return null;
            ProjectViewNode pvNode = (ProjectViewNode) userObject;
            VirtualFile vFile = pvNode.getVirtualFile();
            if (vFile != null && !vFile.isDirectory()) {
                return PsiManager.getInstance(myProject).findFile(vFile);
            }

            return null;
        }
    }

    @Override
    public void dispose() {
        if (myBuilder != null) {
            Disposer.dispose(myBuilder);
            myBuilder = null;
        }
        super.dispose();
    }

    @Override
    protected String getDimensionServiceKey() {
        return "#com.intellij.ide.util.TreeFileChooserDialog";
    }

    @Override
    public JComponent getPreferredFocusedComponent() {
        return myTree;
    }

    private final class MyGotoFileModel implements ChooseByNameModel {
        private final int myMaxSize = WindowManagerEx.getInstanceEx().getFrame(myProject).getSize().width;

        @Override
        @NotNull
        public Object[] getElementsByName(final String name, final boolean checkBoxState, final String pattern) {
            GlobalSearchScope scope = myShowLibraryContents ? GlobalSearchScope.allScope(myProject)
                    : GlobalSearchScope.projectScope(myProject);
            final PsiFile[] psiFiles = FilenameIndex.getFilesByName(myProject, name, scope);
            return filterFiles(psiFiles);
        }

        @Override
        public String getPromptText() {
            return IdeBundle.message("prompt.filechooser.enter.file.name");
        }

        @Override
        public String getCheckBoxName() {
            return null;
        }

        @Override
        public char getCheckBoxMnemonic() {
            return 0;
        }

        @Override
        public String getNotInMessage() {
            return "";
        }

        @Override
        public String getNotFoundMessage() {
            return "";
        }

        @Override
        public boolean loadInitialCheckBoxState() {
            return true;
        }

        @Override
        public void saveInitialCheckBoxState(final boolean state) {
        }

        @Override
        public PsiElementListCellRenderer getListCellRenderer() {
            return new GotoFileCellRenderer(myMaxSize);
        }

        @Override
        @NotNull
        public String[] getNames(final boolean checkBoxState) {
            final String[] fileNames;
            if (myFileType != null && myProject != null) {
                GlobalSearchScope scope = myShowLibraryContents ? GlobalSearchScope.allScope(myProject)
                        : GlobalSearchScope.projectScope(myProject);
                Collection<VirtualFile> virtualFiles = FileTypeIndex.getFiles(myFileType, scope);
                fileNames = ContainerUtil.map2Array(virtualFiles, String.class,
                        new Function<VirtualFile, String>() {
                            @Override
                            public String fun(VirtualFile file) {
                                return file.getName();
                            }
                        });
            } else {
                fileNames = FilenameIndex.getAllFilenames(myProject);
            }
            final Set<String> array = new THashSet<String>();
            for (String fileName : fileNames) {
                if (!array.contains(fileName)) {
                    array.add(fileName);
                }
            }

            final String[] result = ArrayUtil.toStringArray(array);
            Arrays.sort(result);
            return result;
        }

        @Override
        public boolean willOpenEditor() {
            return true;
        }

        @Override
        public String getElementName(final Object element) {
            if (!(element instanceof PsiFile))
                return null;
            return ((PsiFile) element).getName();
        }

        @Override
        @Nullable
        public String getFullName(final Object element) {
            if (element instanceof PsiFile) {
                final VirtualFile virtualFile = ((PsiFile) element).getVirtualFile();
                return virtualFile != null ? virtualFile.getPath() : null;
            }

            return getElementName(element);
        }

        @Override
        public String getHelpId() {
            return null;
        }

        @Override
        @NotNull
        public String[] getSeparators() {
            return new String[] { "/", "\\" };
        }

        @Override
        public boolean useMiddleMatching() {
            return false;
        }
    }

    private final class MyCallback extends ChooseByNamePopupComponent.Callback {
        @Override
        public void elementChosen(final Object element) {
            mySelectedFile = (PsiFile) element;
            close(OK_EXIT_CODE);
        }
    }

    private Object[] filterFiles(final Object[] list) {
        Condition<PsiFile> condition = new Condition<PsiFile>() {
            @Override
            public boolean value(final PsiFile psiFile) {
                if (myFilter != null && !myFilter.accept(psiFile)) {
                    return false;
                }
                boolean accepted = myFileType == null || psiFile.getFileType() == myFileType;
                VirtualFile virtualFile = psiFile.getVirtualFile();
                if (virtualFile != null && !accepted) {
                    accepted = virtualFile.getFileType() == myFileType;
                }
                return accepted;
            }
        };
        final List<Object> result = new ArrayList<Object>(list.length);
        for (Object o : list) {
            final PsiFile psiFile;
            if (o instanceof PsiFile) {
                psiFile = (PsiFile) o;
            } else if (o instanceof PsiFileNode) {
                psiFile = ((PsiFileNode) o).getValue();
            } else {
                psiFile = null;
            }
            if (psiFile != null && !condition.value(psiFile)) {
                continue;
            } else {
                if (o instanceof ProjectViewNode) {
                    final ProjectViewNode projectViewNode = (ProjectViewNode) o;
                    if (!projectViewNode.canHaveChildrenMatching(condition)) {
                        continue;
                    }
                }
            }
            result.add(o);
        }
        return ArrayUtil.toObjectArray(result);
    }
}