Merge remote-tracking branch 'origin/master' into mark_folder_as
[idea/community.git] / platform / lang-impl / src / com / intellij / ide / projectView / impl / nodes / PsiDirectoryNode.java
1 /*
2  * Copyright 2000-2015 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.projectView.impl.nodes;
17
18 import com.intellij.ide.IconProvider;
19 import com.intellij.ide.bookmarks.Bookmark;
20 import com.intellij.ide.bookmarks.BookmarkManager;
21 import com.intellij.ide.projectView.PresentationData;
22 import com.intellij.ide.projectView.ProjectView;
23 import com.intellij.ide.projectView.ViewSettings;
24 import com.intellij.ide.projectView.impl.ProjectRootsUtil;
25 import com.intellij.ide.projectView.impl.ProjectViewImpl;
26 import com.intellij.ide.util.treeView.AbstractTreeNode;
27 import com.intellij.idea.ActionsBundle;
28 import com.intellij.openapi.extensions.Extensions;
29 import com.intellij.openapi.fileTypes.FileTypeRegistry;
30 import com.intellij.openapi.module.Module;
31 import com.intellij.openapi.module.ModuleUtil;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.roots.OrderEntry;
34 import com.intellij.openapi.roots.ProjectFileIndex;
35 import com.intellij.openapi.roots.ProjectRootManager;
36 import com.intellij.openapi.roots.libraries.LibraryUtil;
37 import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
38 import com.intellij.openapi.util.Comparing;
39 import com.intellij.openapi.util.registry.Registry;
40 import com.intellij.openapi.vfs.LocalFileSystem;
41 import com.intellij.openapi.vfs.VFileProperty;
42 import com.intellij.openapi.vfs.VfsUtilCore;
43 import com.intellij.openapi.vfs.VirtualFile;
44 import com.intellij.pom.NavigatableWithText;
45 import com.intellij.projectImport.ProjectAttachProcessor;
46 import com.intellij.psi.PsiDirectory;
47 import com.intellij.psi.impl.file.PsiDirectoryFactory;
48 import com.intellij.ui.LayeredIcon;
49 import com.intellij.ui.RowIcon;
50 import com.intellij.ui.SimpleTextAttributes;
51 import com.intellij.util.IconUtil;
52 import com.intellij.util.PathUtil;
53 import com.intellij.util.PlatformIcons;
54 import com.intellij.util.PlatformUtils;
55 import org.jetbrains.annotations.NotNull;
56
57 import javax.swing.*;
58 import java.util.Collection;
59
60 public class PsiDirectoryNode extends BasePsiNode<PsiDirectory> implements NavigatableWithText {
61   public PsiDirectoryNode(Project project, PsiDirectory value, ViewSettings viewSettings) {
62     super(project, value, viewSettings);
63   }
64
65   protected boolean shouldShowModuleName() {
66     return !PlatformUtils.isCidr();
67   }
68
69   protected boolean shouldShowSourcesRoot() {
70     return true;
71   }
72
73   @Override
74   protected void updateImpl(PresentationData data) {
75     Project project = getProject();
76     assert project != null : this;
77     PsiDirectory psiDirectory = getValue();
78     assert psiDirectory != null : this;
79     VirtualFile directoryFile = psiDirectory.getVirtualFile();
80     Object parentValue = getParentValue();
81
82     if (ProjectRootsUtil.isModuleContentRoot(directoryFile, project)) {
83       ProjectFileIndex fi = ProjectRootManager.getInstance(project).getFileIndex();
84       Module module = fi.getModuleForFile(directoryFile);
85
86       data.setPresentableText(directoryFile.getName());
87       if (module != null) {
88         if (!(parentValue instanceof Module)) {
89           if (!shouldShowModuleName()) {
90             data.addText(directoryFile.getName() + " ", SimpleTextAttributes.REGULAR_ATTRIBUTES);
91           }
92           else if (Comparing.equal(module.getName(), directoryFile.getName())) {
93             data.addText(directoryFile.getName(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
94           }
95           else {
96             data.addText(directoryFile.getName() + " ", SimpleTextAttributes.REGULAR_ATTRIBUTES);
97             data.addText("[" + module.getName() + "]", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
98           }
99         }
100         else {
101           data.addText(directoryFile.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
102         }
103
104         data.setLocationString(ProjectViewDirectoryHelper.getInstance(project).getLocationString(psiDirectory, true, shouldShowSourcesRoot()));
105         setupIcon(data, psiDirectory);
106
107         return;
108       }
109     }
110
111     String name = parentValue instanceof Project
112                   ? psiDirectory.getVirtualFile().getPresentableUrl()
113                   : ProjectViewDirectoryHelper.getInstance(psiDirectory.getProject()).getNodeName(getSettings(), parentValue, psiDirectory);
114     if (name == null) {
115       setValue(null);
116       return;
117     }
118
119     data.setPresentableText(name);
120     data.setLocationString(ProjectViewDirectoryHelper.getInstance(project).getLocationString(psiDirectory, false, false));
121
122     setupIcon(data, psiDirectory);
123   }
124
125   protected void setupIcon(PresentationData data, PsiDirectory psiDirectory) {
126     final VirtualFile virtualFile = psiDirectory.getVirtualFile();
127     if (PlatformUtils.isAppCode()) {
128       final Icon icon = IconUtil.getIcon(virtualFile, 0, myProject);
129       if (icon != null) {
130         data.setIcon(patchIcon(icon, virtualFile));
131       }
132     }
133     else {
134       for (final IconProvider provider : Extensions.getExtensions(IconProvider.EXTENSION_POINT_NAME)) {
135         final Icon icon = provider.getIcon(psiDirectory, 0);
136         if (icon != null) {
137           data.setIcon(patchIcon(icon, virtualFile));
138           return;
139         }
140       }
141     }
142   }
143
144   @Override
145   public Collection<AbstractTreeNode> getChildrenImpl() {
146     return ProjectViewDirectoryHelper.getInstance(myProject).getDirectoryChildren(getValue(), getSettings(), true);
147   }
148
149   @Override
150   @SuppressWarnings("deprecation")
151   public String getTestPresentation() {
152     return "PsiDirectory: " + getValue().getName();
153   }
154
155   public boolean isFQNameShown() {
156     return ProjectViewDirectoryHelper.getInstance(getProject()).isShowFQName(getSettings(), getParentValue(), getValue());
157   }
158
159   @Override
160   public boolean contains(@NotNull VirtualFile file) {
161     final PsiDirectory value = getValue();
162     if (value == null) {
163       return false;
164     }
165
166     VirtualFile directory = value.getVirtualFile();
167     if (directory.getFileSystem() instanceof LocalFileSystem) {
168       file = PathUtil.getLocalFile(file);
169     }
170
171     if (!VfsUtilCore.isAncestor(directory, file, false)) {
172       return false;
173     }
174
175     if (Registry.is("ide.hide.excluded.files")) {
176       final Project project = value.getProject();
177       final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
178       return !fileIndex.isExcluded(file);
179     }
180     else {
181       return !FileTypeRegistry.getInstance().isFileIgnored(file);
182     }
183   }
184
185   @Override
186   public VirtualFile getVirtualFile() {
187     PsiDirectory directory = getValue();
188     if (directory == null) return null;
189     return directory.getVirtualFile();
190   }
191
192   @Override
193   public boolean canRepresent(final Object element) {
194     if (super.canRepresent(element)) return true;
195     PsiDirectory directory = getValue();
196     if (directory == null) return false;
197     return ProjectViewDirectoryHelper.getInstance(getProject()).canRepresent(element, directory);
198   }
199
200   @Override
201   public boolean canNavigate() {
202     VirtualFile file = getVirtualFile();
203     Project project = getProject();
204
205     ProjectSettingsService service = ProjectSettingsService.getInstance(myProject);
206     return file != null && ((ProjectRootsUtil.isModuleContentRoot(file, project) && service.canOpenModuleSettings()) ||
207                             (ProjectRootsUtil.isModuleSourceRoot(file, project)  && service.canOpenContentEntriesSettings()) ||
208                             (ProjectRootsUtil.isLibraryRoot(file, project) && service.canOpenModuleLibrarySettings()));
209   }
210
211   @Override
212   public boolean canNavigateToSource() {
213     return false;
214   }
215
216   @Override
217   public void navigate(final boolean requestFocus) {
218     Module module = ModuleUtil.findModuleForPsiElement(getValue());
219     if (module != null) {
220       final VirtualFile file = getVirtualFile();
221       final Project project = getProject();
222       ProjectSettingsService service = ProjectSettingsService.getInstance(myProject);
223       if (ProjectRootsUtil.isModuleContentRoot(file, project)) {
224         service.openModuleSettings(module);
225       }
226       else if (ProjectRootsUtil.isLibraryRoot(file, project)) {
227         final OrderEntry orderEntry = LibraryUtil.findLibraryEntry(file, module.getProject());
228         if (orderEntry != null) {
229           service.openLibraryOrSdkSettings(orderEntry);
230         }
231       }
232       else {
233         service.openContentEntriesSettings(module);
234       }
235     }
236   }
237
238   @Override
239   public String getNavigateActionText(boolean focusEditor) {
240     VirtualFile file = getVirtualFile();
241     Project project = getProject();
242
243     if (file != null && project != null) {
244       if (ProjectRootsUtil.isModuleContentRoot(file, project) || ProjectRootsUtil.isModuleSourceRoot(file, project)) {
245         return ActionsBundle.message("action.ModuleSettings.navigate");
246       }
247       if (ProjectRootsUtil.isLibraryRoot(file, project)) {
248         return ActionsBundle.message("action.LibrarySettings.navigate");
249       }
250     }
251
252     return null;
253   }
254
255   @Override
256   public int getWeight() {
257     final ProjectView projectView = ProjectView.getInstance(myProject);
258     if (projectView instanceof ProjectViewImpl && !((ProjectViewImpl)projectView).isFoldersAlwaysOnTop()) {
259       return 20;
260     }
261     return isFQNameShown() ? 70 : 0;
262   }
263
264   @Override
265   public String getTitle() {
266     final PsiDirectory directory = getValue();
267     if (directory != null) {
268       return PsiDirectoryFactory.getInstance(getProject()).getQualifiedName(directory, true);
269     }
270     return super.getTitle();
271   }
272
273   protected Icon patchIcon(Icon original, VirtualFile file) {
274     Icon icon = original;
275
276     final Bookmark bookmarkAtFile = BookmarkManager.getInstance(myProject).findFileBookmark(file);
277     if (bookmarkAtFile != null) {
278       final RowIcon composite = new RowIcon(2, RowIcon.Alignment.CENTER);
279       composite.setIcon(icon, 0);
280       composite.setIcon(bookmarkAtFile.getIcon(), 1);
281       icon = composite;
282     }
283
284     if (!file.isWritable()) {
285       icon = LayeredIcon.create(icon, PlatformIcons.LOCKED_ICON);
286     }
287
288     if (file.is(VFileProperty.SYMLINK)) {
289       icon = LayeredIcon.create(icon, PlatformIcons.SYMLINK_ICON);
290     }
291
292     return icon;
293   }
294
295   @Override
296   public Comparable getSortKey() {
297     if (ProjectAttachProcessor.canAttachToProject()) {
298       // primary module is always on top; attached modules are sorted alphabetically
299       final VirtualFile file = getVirtualFile();
300       if (Comparing.equal(file, myProject.getBaseDir())) {
301         return "";    // sorts before any other name
302       }
303       return getTitle();
304     }
305     return null;
306   }
307
308   @Override
309   public Comparable getTypeSortKey() {
310     VirtualFile file = getVirtualFile();
311     if (file != null) {
312       String extension = file.getExtension();
313       if (extension != null) {
314         return new PsiFileNode.ExtensionSortKey(extension);
315       }
316     }
317     return null;
318   }
319
320   @Override
321   public String getQualifiedNameSortKey() {
322     final PsiDirectoryFactory factory = PsiDirectoryFactory.getInstance(getProject());
323     return factory.getQualifiedName(getValue(), true);
324   }
325
326   @Override
327   public int getTypeSortWeight(final boolean sortByType) {
328     return 3;
329   }
330
331   @Override
332   public boolean shouldDrillDownOnEmptyElement() {
333     return true;
334   }
335
336   @Override
337   public boolean isAlwaysShowPlus() {
338     final VirtualFile file = getVirtualFile();
339     return file == null || file.getChildren().length > 0;
340   }
341 }