Move ModulePointerManagerImpl to projectModel-impl
[idea/community.git] / platform / lang-impl / src / com / intellij / ide / favoritesTreeView / FavoritesViewTreeBuilder.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.ide.favoritesTreeView;
17
18 import com.intellij.ProjectTopics;
19 import com.intellij.ide.CopyPasteUtil;
20 import com.intellij.ide.projectView.BaseProjectTreeBuilder;
21 import com.intellij.ide.projectView.ProjectView;
22 import com.intellij.ide.projectView.ProjectViewPsiTreeChangeListener;
23 import com.intellij.ide.projectView.impl.ProjectAbstractTreeStructureBase;
24 import com.intellij.ide.util.treeView.AbstractTreeNode;
25 import com.intellij.ide.util.treeView.AbstractTreeStructure;
26 import com.intellij.ide.util.treeView.AbstractTreeUpdater;
27 import com.intellij.ide.util.treeView.NodeDescriptor;
28 import com.intellij.openapi.fileTypes.StdFileTypes;
29 import com.intellij.openapi.ide.CopyPasteManager;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.roots.ModuleRootAdapter;
32 import com.intellij.openapi.roots.ModuleRootEvent;
33 import com.intellij.openapi.util.ActionCallback;
34 import com.intellij.openapi.util.Comparing;
35 import com.intellij.openapi.vcs.FileStatusListener;
36 import com.intellij.openapi.vcs.FileStatusManager;
37 import com.intellij.openapi.vfs.VirtualFile;
38 import com.intellij.psi.PsiElement;
39 import com.intellij.psi.PsiFile;
40 import com.intellij.psi.PsiManager;
41 import com.intellij.psi.SmartPsiElementPointer;
42 import com.intellij.util.messages.MessageBusConnection;
43 import com.intellij.util.ui.tree.TreeUtil;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
46
47 import javax.swing.*;
48 import javax.swing.tree.DefaultMutableTreeNode;
49 import javax.swing.tree.DefaultTreeModel;
50
51 /**
52  * @author Konstantin Bulenkov
53  */
54 public class FavoritesViewTreeBuilder extends BaseProjectTreeBuilder {
55   private final ProjectViewPsiTreeChangeListener myPsiTreeChangeListener;
56   private final FileStatusListener myFileStatusListener;
57   private final CopyPasteUtil.DefaultCopyPasteListener myCopyPasteListener;
58   private final FavoritesListener myFavoritesListener;
59
60   public FavoritesViewTreeBuilder(Project project,
61                                   JTree tree,
62                                   DefaultTreeModel treeModel,
63                                   ProjectAbstractTreeStructureBase treeStructure) {
64     super(project, 
65           tree, 
66           treeModel, 
67           treeStructure, 
68           new FavoritesComparator(ProjectView.getInstance(project), FavoritesProjectViewPane.ID));
69     final MessageBusConnection bus = myProject.getMessageBus().connect(this);
70     myPsiTreeChangeListener = new ProjectViewPsiTreeChangeListener(myProject) {
71       protected DefaultMutableTreeNode getRootNode() {
72         return FavoritesViewTreeBuilder.this.getRootNode();
73       }
74
75       protected AbstractTreeUpdater getUpdater() {
76         return FavoritesViewTreeBuilder.this.getUpdater();
77       }
78
79       protected boolean isFlattenPackages() {
80         return getStructure().isFlattenPackages();
81       }
82
83       protected void childrenChanged(PsiElement parent, final boolean stopProcessingForThisModificationCount) {
84         if (findNodeByElement(parent) == null){
85           queueUpdate(true);
86         } else {
87           super.childrenChanged(parent, true);
88         }
89       }
90     };
91     bus.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
92       public void rootsChanged(ModuleRootEvent event) {
93         queueUpdate(true);
94       }
95     });
96     PsiManager.getInstance(myProject).addPsiTreeChangeListener(myPsiTreeChangeListener);
97     myFileStatusListener = new MyFileStatusListener();
98     FileStatusManager.getInstance(myProject).addFileStatusListener(myFileStatusListener);
99     myCopyPasteListener = new CopyPasteUtil.DefaultCopyPasteListener(getUpdater());
100     CopyPasteManager.getInstance().addContentChangedListener(myCopyPasteListener);
101
102     myFavoritesListener = new FavoritesListener() {
103       public void rootsChanged(String listName) {
104         //if (myListName.equals(listName)) { //todo[kb]: add optimizations?
105           updateFromRoot();
106         //}
107       }
108
109       public void listAdded(String listName) {
110         updateFromRoot();
111       }
112
113       public void listRemoved(String listName) {
114         updateFromRoot();
115       }
116     };
117     FavoritesManager.getInstance(myProject).addFavoritesListener(myFavoritesListener);
118     initRootNode();
119   }
120
121   @NotNull
122   public FavoritesTreeStructure getStructure() {
123     final AbstractTreeStructure structure = getTreeStructure();
124     assert structure instanceof FavoritesTreeStructure;
125     return (FavoritesTreeStructure)structure;
126   }
127   
128   public AbstractTreeNode getRoot() {
129     final Object rootElement = getRootElement();
130     assert rootElement instanceof AbstractTreeNode;
131     return (AbstractTreeNode)rootElement;
132   }
133
134   public void updateFromRoot() {
135     updateFromRootCB();
136   }
137
138   @NotNull
139   public ActionCallback updateFromRootCB() {
140     getStructure().rootsChanged();
141     if (isDisposed()) return new ActionCallback.Done();
142     getUpdater().cancelAllRequests();
143     return super.updateFromRootCB();
144   }
145
146   public ActionCallback select(Object element, VirtualFile file, boolean requestFocus) {
147     final DefaultMutableTreeNode node = findSmartFirstLevelNodeByElement(element);
148     if (node != null){
149       return TreeUtil.selectInTree(node, requestFocus, getTree());
150     }
151     return super.select(element, file, requestFocus);
152   }
153
154   @Nullable
155   private static DefaultMutableTreeNode findFirstLevelNodeWithObject(final DefaultMutableTreeNode aRoot, final Object aObject) {
156     for (int i = 0; i < aRoot.getChildCount(); i++) {
157       final DefaultMutableTreeNode child = (DefaultMutableTreeNode)aRoot.getChildAt(i);
158       Object userObject = child.getUserObject();
159       if (userObject instanceof FavoritesTreeNodeDescriptor) {
160         if (Comparing.equal(((FavoritesTreeNodeDescriptor)userObject).getElement(), aObject)) {
161           return child;
162         }
163       }
164     }
165     return null;
166   }
167
168   protected Object findNodeByElement(Object element) {
169     final Object node = findSmartFirstLevelNodeByElement(element);
170     if (node != null) return node;
171     return super.findNodeByElement(element);
172   }
173   
174   @Nullable
175   DefaultMutableTreeNode findSmartFirstLevelNodeByElement(final Object element) {
176     for (Object child : getRoot().getChildren()) {
177       AbstractTreeNode favorite = (AbstractTreeNode)child;
178       Object currentValue = favorite.getValue();
179       if (currentValue instanceof SmartPsiElementPointer){
180         currentValue = ((SmartPsiElementPointer)favorite.getValue()).getElement();
181       }
182        /*else if (currentValue instanceof PsiJavaFile) {
183         final PsiClass[] classes = ((PsiJavaFile)currentValue).getClasses();
184         if (classes.length > 0) {
185           currentValue = classes[0];
186         }
187       }*/
188       if (Comparing.equal(element, currentValue)){
189         final DefaultMutableTreeNode nodeWithObject = findFirstLevelNodeWithObject((DefaultMutableTreeNode)getTree().getModel().getRoot(), favorite);
190         if (nodeWithObject != null){
191           return nodeWithObject;
192         }
193       }
194     }
195     return null;
196   }
197
198   public final void dispose() {
199     super.dispose();
200     FavoritesManager.getInstance(myProject).removeFavoritesListener(myFavoritesListener);
201
202     PsiManager.getInstance(myProject).removePsiTreeChangeListener(myPsiTreeChangeListener);
203     FileStatusManager.getInstance(myProject).removeFileStatusListener(myFileStatusListener);
204     CopyPasteManager.getInstance().removeContentChangedListener(myCopyPasteListener);
205   }
206
207   protected boolean isAlwaysShowPlus(NodeDescriptor nodeDescriptor) {
208     final Object[] childElements = getStructure().getChildElements(nodeDescriptor);
209     return childElements != null && childElements.length > 0;
210   }
211
212   protected boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) {
213     return nodeDescriptor.getParentDescriptor() == null;
214   }
215
216   private final class MyFileStatusListener implements FileStatusListener {
217     public void fileStatusesChanged() {
218       queueUpdateFrom(getRootNode(), false);
219     }
220
221     public void fileStatusChanged(@NotNull VirtualFile vFile) {
222       PsiElement element;
223       PsiManager psiManager = PsiManager.getInstance(myProject);
224       if (vFile.isDirectory()) {
225         element = psiManager.findDirectory(vFile);
226       }
227       else {
228         element = psiManager.findFile(vFile);
229       }
230
231       if (!getUpdater().addSubtreeToUpdateByElement(element) && element instanceof PsiFile && ((PsiFile) element).getFileType() == StdFileTypes.JAVA) {
232         getUpdater().addSubtreeToUpdateByElement(((PsiFile)element).getContainingDirectory());
233       }
234     }
235   }
236
237 }
238