| /* |
| * 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.projectView.impl.ModuleGroup; |
| import com.intellij.ide.projectView.impl.ModuleGroupUtil; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.module.ModuleType; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.roots.ProjectFileIndex; |
| import com.intellij.openapi.roots.ProjectRootManager; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiDirectory; |
| import com.intellij.ui.ColoredTreeCellRenderer; |
| import com.intellij.ui.SimpleTextAttributes; |
| import com.intellij.ui.TreeSpeedSearch; |
| import com.intellij.ui.treeStructure.Tree; |
| import com.intellij.util.Consumer; |
| import com.intellij.util.Function; |
| import com.intellij.util.PlatformIcons; |
| import com.intellij.util.containers.Convertor; |
| import com.intellij.util.containers.HashMap; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.TreeSelectionEvent; |
| import javax.swing.event.TreeSelectionListener; |
| import javax.swing.tree.*; |
| import java.util.*; |
| |
| /** |
| * @author dsl |
| */ |
| public class DirectoryChooserModuleTreeView implements DirectoryChooserView { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.DirectoryChooserModuleTreeView"); |
| |
| private final Tree myTree; |
| private final List<DirectoryChooser.ItemWrapper> myItems = new ArrayList<DirectoryChooser.ItemWrapper>(); |
| private final Map<DirectoryChooser.ItemWrapper, DefaultMutableTreeNode> myItemNodes = new HashMap<DirectoryChooser.ItemWrapper, DefaultMutableTreeNode>(); |
| private final Map<Module, DefaultMutableTreeNode> myModuleNodes = new HashMap<Module, DefaultMutableTreeNode>(); |
| private final Map<ModuleGroup, DefaultMutableTreeNode> myModuleGroupNodes = new HashMap<ModuleGroup, DefaultMutableTreeNode>(); |
| private final DefaultMutableTreeNode myRootNode; |
| private final ProjectFileIndex myFileIndex; |
| private final Project myProject; |
| |
| public DirectoryChooserModuleTreeView(@NotNull Project project) { |
| myRootNode = new DefaultMutableTreeNode(); |
| myTree = new Tree(myRootNode); |
| myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); |
| myFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); |
| myProject = project; |
| myTree.setRootVisible(false); |
| myTree.setShowsRootHandles(true); |
| myTree.setCellRenderer(new MyTreeCellRenderer()); |
| new TreeSpeedSearch(myTree, new Convertor<TreePath, String>() { |
| @Override |
| public String convert(final TreePath o) { |
| final Object userObject = ((DefaultMutableTreeNode)o.getLastPathComponent()).getUserObject(); |
| if (userObject instanceof Module) { |
| return ((Module)userObject).getName(); |
| } |
| else { |
| if (userObject == null) return ""; |
| return userObject.toString(); |
| } |
| } |
| }, true); |
| } |
| |
| @Override |
| public void clearItems() { |
| myRootNode.removeAllChildren(); |
| myItems.clear(); |
| myItemNodes.clear(); |
| myModuleNodes.clear(); |
| myModuleGroupNodes.clear(); |
| myRootNode.removeAllChildren(); |
| ((DefaultTreeModel)myTree.getModel()).nodeStructureChanged(myRootNode); |
| } |
| |
| @Override |
| public JComponent getComponent() { |
| return myTree; |
| } |
| |
| @Override |
| public void onSelectionChange(final Runnable runnable) { |
| myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { |
| @Override |
| public void valueChanged(TreeSelectionEvent e) { |
| runnable.run(); |
| } |
| }); |
| } |
| |
| @Override |
| public DirectoryChooser.ItemWrapper getItemByIndex(int i) { |
| return myItems.get(i); |
| } |
| |
| @Override |
| public void clearSelection() { |
| myTree.clearSelection(); |
| } |
| |
| @Override |
| public void selectItemByIndex(int selectionIndex) { |
| if (selectionIndex < 0) { |
| myTree.clearSelection(); |
| } else { |
| final DirectoryChooser.ItemWrapper itemWrapper = myItems.get(selectionIndex); |
| final DefaultMutableTreeNode node = myItemNodes.get(itemWrapper); |
| final TreePath treePath = expandNode(node); |
| myTree.setSelectionPath(treePath); |
| myTree.scrollPathToVisible(treePath); |
| } |
| } |
| |
| private TreePath expandNode(final DefaultMutableTreeNode node) { |
| final TreeNode[] path = node.getPath(); |
| final TreePath treePath = new TreePath(path); |
| TreePath expandPath = treePath; |
| if (myTree.getModel().isLeaf(expandPath.getLastPathComponent())) { |
| expandPath = expandPath.getParentPath(); |
| } |
| myTree.expandPath(expandPath); |
| return treePath; |
| } |
| |
| @Override |
| public void addItem(DirectoryChooser.ItemWrapper itemWrapper) { |
| myItems.add(itemWrapper); |
| final PsiDirectory directory = itemWrapper.getDirectory(); |
| final Module module = myFileIndex.getModuleForFile(directory.getVirtualFile()); |
| DefaultMutableTreeNode node = myModuleNodes.get(module); |
| if (node == null) { |
| node = new DefaultMutableTreeNode(module, true); |
| final String[] groupPath = module != null ? ModuleManager.getInstance(myProject).getModuleGroupPath(module) : null; |
| if (groupPath == null || groupPath.length == 0){ |
| insertNode(node, myRootNode); |
| } else { |
| final DefaultMutableTreeNode parentNode = ModuleGroupUtil.buildModuleGroupPath(new ModuleGroup(groupPath), |
| myRootNode, |
| myModuleGroupNodes, |
| new Consumer<ModuleGroupUtil.ParentChildRelation<DefaultMutableTreeNode>>() { |
| @Override |
| public void consume(final ModuleGroupUtil.ParentChildRelation<DefaultMutableTreeNode> parentChildRelation) { |
| insertNode(parentChildRelation.getChild(), parentChildRelation.getParent()); |
| } |
| }, |
| new Function<ModuleGroup, DefaultMutableTreeNode>() { |
| @Override |
| public DefaultMutableTreeNode fun(final ModuleGroup moduleGroup) { |
| return new DefaultMutableTreeNode(moduleGroup, true); |
| } |
| }); |
| insertNode(node, parentNode); |
| } |
| myModuleNodes.put(module, node); |
| } |
| final DefaultMutableTreeNode itemNode = new DefaultMutableTreeNode(itemWrapper, false); |
| myItemNodes.put(itemWrapper, itemNode); |
| insertNode(itemNode, node); |
| ((DefaultTreeModel)myTree.getModel()).nodeStructureChanged(node); |
| } |
| |
| private void insertNode(final DefaultMutableTreeNode nodeToInsert, DefaultMutableTreeNode rootNode) { |
| final Enumeration enumeration = rootNode.children(); |
| ArrayList children = Collections.list(enumeration); |
| final int index = Collections.binarySearch(children, nodeToInsert, new Comparator<DefaultMutableTreeNode>() { |
| @Override |
| public int compare(DefaultMutableTreeNode node1, DefaultMutableTreeNode node2) { |
| final Object o1 = node1.getUserObject(); |
| final Object o2 = node2.getUserObject(); |
| if (o1 instanceof Module && o2 instanceof Module) { |
| return ((Module)o1).getName().compareToIgnoreCase(((Module)o2).getName()); |
| } |
| if (o1 instanceof ModuleGroup && o2 instanceof ModuleGroup){ |
| return o1.toString().compareToIgnoreCase(o2.toString()); |
| } |
| if (o1 instanceof ModuleGroup) return -1; |
| if (o1 instanceof DirectoryChooser.ItemWrapper && o2 instanceof DirectoryChooser.ItemWrapper) { |
| final VirtualFile virtualFile1 = ((DirectoryChooser.ItemWrapper)o1).getDirectory().getVirtualFile(); |
| final VirtualFile virtualFile2 = ((DirectoryChooser.ItemWrapper)o2).getDirectory().getVirtualFile(); |
| return Comparing.compare(virtualFile1.getPath(), virtualFile2.getPath()); |
| } |
| return 1; |
| } |
| }); |
| final int insertionPoint = -(index+1); |
| if (insertionPoint < 0 || insertionPoint > rootNode.getChildCount()) { |
| LOG.error("insertionPoint = " + insertionPoint + "; children=" + children + "; node=" + nodeToInsert); |
| return; |
| } |
| rootNode.insert(nodeToInsert, insertionPoint); |
| ((DefaultTreeModel)myTree.getModel()).nodeStructureChanged(rootNode); |
| } |
| |
| @Override |
| public void listFilled() { |
| if (myModuleNodes.size() == 1) { |
| final Iterator<DefaultMutableTreeNode> iterator = myItemNodes.values().iterator(); |
| if (iterator.hasNext()){ |
| final DefaultMutableTreeNode node = iterator.next(); |
| expandNode(node); |
| } |
| } |
| } |
| |
| @Override |
| public int getItemsSize() { |
| return myItems.size(); |
| } |
| |
| @Override |
| @Nullable |
| public DirectoryChooser.ItemWrapper getSelectedItem() { |
| final TreePath selectionPath = myTree.getSelectionPath(); |
| if (selectionPath == null) return null; |
| final DefaultMutableTreeNode node = (DefaultMutableTreeNode)selectionPath.getLastPathComponent(); |
| return node.getUserObject() instanceof DirectoryChooser.ItemWrapper ? (DirectoryChooser.ItemWrapper)node.getUserObject() : null; |
| } |
| |
| |
| private class MyTreeCellRenderer extends ColoredTreeCellRenderer { |
| @Override |
| public void customizeCellRenderer(JTree tree, Object nodeValue, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { |
| final Object value = ((DefaultMutableTreeNode)nodeValue).getUserObject(); |
| if (value instanceof DirectoryChooser.ItemWrapper) { |
| DirectoryChooser.ItemWrapper wrapper = (DirectoryChooser.ItemWrapper)value; |
| DirectoryChooser.PathFragment[] fragments = wrapper.getFragments(); |
| for (DirectoryChooser.PathFragment fragment : fragments) { |
| append(fragment.getText(), |
| fragment.isCommon() ? SimpleTextAttributes.REGULAR_ATTRIBUTES : SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); |
| } |
| setIcon(wrapper.getIcon(myFileIndex)); |
| } |
| else if (value instanceof Module) { |
| final Module module = (Module)value; |
| append(module.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| setIcon(ModuleType.get(module).getIcon()); |
| } else if (value instanceof ModuleGroup) { |
| append(value.toString(), SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| setIcon(PlatformIcons.CLOSED_MODULE_GROUP_ICON); |
| } |
| } |
| } |
| } |
| |