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