0fa219926195d499c56df13c0ad2735dfb57b426
[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.roots.ModuleRootListener;
34 import com.intellij.openapi.util.ActionCallback;
35 import com.intellij.openapi.util.Comparing;
36 import com.intellij.openapi.vcs.FileStatusListener;
37 import com.intellij.openapi.vcs.FileStatusManager;
38 import com.intellij.openapi.vfs.VirtualFile;
39 import com.intellij.psi.PsiElement;
40 import com.intellij.psi.PsiFile;
41 import com.intellij.psi.PsiManager;
42 import com.intellij.psi.SmartPsiElementPointer;
43 import com.intellij.util.messages.MessageBusConnection;
44 import com.intellij.util.ui.tree.TreeUtil;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
47
48 import javax.swing.*;
49 import javax.swing.tree.DefaultMutableTreeNode;
50 import javax.swing.tree.DefaultTreeModel;
51
52 /**
53  * @author Konstantin Bulenkov
54  */
55 public class FavoritesViewTreeBuilder extends BaseProjectTreeBuilder {
56   private final ProjectViewPsiTreeChangeListener myPsiTreeChangeListener;
57   private final FileStatusListener myFileStatusListener;
58   private final CopyPasteUtil.DefaultCopyPasteListener myCopyPasteListener;
59   private final FavoritesListener myFavoritesListener;
60
61   public FavoritesViewTreeBuilder(Project project,
62                                   JTree tree,
63                                   DefaultTreeModel treeModel,
64                                   ProjectAbstractTreeStructureBase treeStructure) {
65     super(project, 
66           tree, 
67           treeModel, 
68           treeStructure, 
69           new FavoritesComparator(ProjectView.getInstance(project), FavoritesProjectViewPane.ID));
70     final MessageBusConnection bus = myProject.getMessageBus().connect(this);
71     myPsiTreeChangeListener = new ProjectViewPsiTreeChangeListener(myProject) {
72       protected DefaultMutableTreeNode getRootNode() {
73         return FavoritesViewTreeBuilder.this.getRootNode();
74       }
75
76       protected AbstractTreeUpdater getUpdater() {
77         return FavoritesViewTreeBuilder.this.getUpdater();
78       }
79
80       protected boolean isFlattenPackages() {
81         return getStructure().isFlattenPackages();
82       }
83
84       protected void childrenChanged(PsiElement parent, final boolean stopProcessingForThisModificationCount) {
85         if (findNodeByElement(parent) == null){
86           queueUpdate(true);
87         } else {
88           super.childrenChanged(parent, true);
89         }
90       }
91     };
92     bus.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
93       public void rootsChanged(ModuleRootEvent event) {
94         queueUpdate(true);
95       }
96     });
97     PsiManager.getInstance(myProject).addPsiTreeChangeListener(myPsiTreeChangeListener);
98     myFileStatusListener = new MyFileStatusListener();
99     FileStatusManager.getInstance(myProject).addFileStatusListener(myFileStatusListener);
100     myCopyPasteListener = new CopyPasteUtil.DefaultCopyPasteListener(getUpdater());
101     CopyPasteManager.getInstance().addContentChangedListener(myCopyPasteListener);
102
103     myFavoritesListener = new FavoritesListener() {
104       public void rootsChanged(String listName) {
105         //if (myListName.equals(listName)) { //todo[kb]: add optimizations?
106           updateFromRoot();
107         //}
108       }
109
110       public void listAdded(String listName) {
111         updateFromRoot();
112       }
113
114       public void listRemoved(String listName) {
115         updateFromRoot();
116       }
117     };
118     FavoritesManager.getInstance(myProject).addFavoritesListener(myFavoritesListener);
119     initRootNode();
120   }
121
122   @NotNull
123   public FavoritesTreeStructure getStructure() {
124     final AbstractTreeStructure structure = getTreeStructure();
125     assert structure instanceof FavoritesTreeStructure;
126     return (FavoritesTreeStructure)structure;
127   }
128   
129   public AbstractTreeNode getRoot() {
130     final Object rootElement = getRootElement();
131     assert rootElement instanceof AbstractTreeNode;
132     return (AbstractTreeNode)rootElement;
133   }
134
135   public void updateFromRoot() {
136     updateFromRootCB();
137   }
138
139   @NotNull
140   public ActionCallback updateFromRootCB() {
141     getStructure().rootsChanged();
142     if (isDisposed()) return new ActionCallback.Done();
143     getUpdater().cancelAllRequests();
144     return super.updateFromRootCB();
145   }
146
147   public ActionCallback select(Object element, VirtualFile file, boolean requestFocus) {
148     final DefaultMutableTreeNode node = findSmartFirstLevelNodeByElement(element);
149     if (node != null){
150       return TreeUtil.selectInTree(node, requestFocus, getTree());
151     }
152     return super.select(element, file, requestFocus);
153   }
154
155   @Nullable
156   private static DefaultMutableTreeNode findFirstLevelNodeWithObject(final DefaultMutableTreeNode aRoot, final Object aObject) {
157     for (int i = 0; i < aRoot.getChildCount(); i++) {
158       final DefaultMutableTreeNode child = (DefaultMutableTreeNode)aRoot.getChildAt(i);
159       Object userObject = child.getUserObject();
160       if (userObject instanceof FavoritesTreeNodeDescriptor) {
161         if (Comparing.equal(((FavoritesTreeNodeDescriptor)userObject).getElement(), aObject)) {
162           return child;
163         }
164       }
165     }
166     return null;
167   }
168
169   protected Object findNodeByElement(Object element) {
170     final Object node = findSmartFirstLevelNodeByElement(element);
171     if (node != null) return node;
172     return super.findNodeByElement(element);
173   }
174   
175   @Nullable
176   DefaultMutableTreeNode findSmartFirstLevelNodeByElement(final Object element) {
177     for (Object child : getRoot().getChildren()) {
178       AbstractTreeNode favorite = (AbstractTreeNode)child;
179       Object currentValue = favorite.getValue();
180       if (currentValue instanceof SmartPsiElementPointer){
181         currentValue = ((SmartPsiElementPointer)favorite.getValue()).getElement();
182       }
183        /*else if (currentValue instanceof PsiJavaFile) {
184         final PsiClass[] classes = ((PsiJavaFile)currentValue).getClasses();
185         if (classes.length > 0) {
186           currentValue = classes[0];
187         }
188       }*/
189       if (Comparing.equal(element, currentValue)){
190         final DefaultMutableTreeNode nodeWithObject = findFirstLevelNodeWithObject((DefaultMutableTreeNode)getTree().getModel().getRoot(), favorite);
191         if (nodeWithObject != null){
192           return nodeWithObject;
193         }
194       }
195     }
196     return null;
197   }
198
199   public final void dispose() {
200     super.dispose();
201     FavoritesManager.getInstance(myProject).removeFavoritesListener(myFavoritesListener);
202
203     PsiManager.getInstance(myProject).removePsiTreeChangeListener(myPsiTreeChangeListener);
204     FileStatusManager.getInstance(myProject).removeFileStatusListener(myFileStatusListener);
205     CopyPasteManager.getInstance().removeContentChangedListener(myCopyPasteListener);
206   }
207
208   protected boolean isAlwaysShowPlus(NodeDescriptor nodeDescriptor) {
209     final Object[] childElements = getStructure().getChildElements(nodeDescriptor);
210     return childElements != null && childElements.length > 0;
211   }
212
213   protected boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) {
214     return nodeDescriptor.getParentDescriptor() == null;
215   }
216
217   private final class MyFileStatusListener implements FileStatusListener {
218     public void fileStatusesChanged() {
219       queueUpdateFrom(getRootNode(), false);
220     }
221
222     public void fileStatusChanged(@NotNull VirtualFile vFile) {
223       PsiElement element;
224       PsiManager psiManager = PsiManager.getInstance(myProject);
225       if (vFile.isDirectory()) {
226         element = psiManager.findDirectory(vFile);
227       }
228       else {
229         element = psiManager.findFile(vFile);
230       }
231
232       if (!getUpdater().addSubtreeToUpdateByElement(element) && element instanceof PsiFile && ((PsiFile) element).getFileType() == StdFileTypes.JAVA) {
233         getUpdater().addSubtreeToUpdateByElement(((PsiFile)element).getContainingDirectory());
234       }
235     }
236   }
237
238 }
239