da3c035e0e8e0e37fb223cae968f5b3af41698bd
[idea/community.git] / platform / lang-impl / src / com / intellij / ide / util / scopeChooser / ScopeChooserCombo.java
1 /*
2  * Copyright 2000-2009 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.util.scopeChooser;
17
18 import com.intellij.ide.DataManager;
19 import com.intellij.ide.IdeBundle;
20 import com.intellij.ide.favoritesTreeView.FavoritesManager;
21 import com.intellij.ide.ui.ListCellRendererWrapper;
22 import com.intellij.openapi.Disposable;
23 import com.intellij.openapi.actionSystem.DataContext;
24 import com.intellij.openapi.actionSystem.LangDataKeys;
25 import com.intellij.openapi.actionSystem.PlatformDataKeys;
26 import com.intellij.openapi.editor.Editor;
27 import com.intellij.openapi.extensions.Extensions;
28 import com.intellij.openapi.fileEditor.FileEditorManager;
29 import com.intellij.openapi.module.Module;
30 import com.intellij.openapi.module.ModuleUtil;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.vfs.VirtualFile;
33 import com.intellij.packageDependencies.ChangeListsScopesProvider;
34 import com.intellij.packageDependencies.DependencyValidationManager;
35 import com.intellij.psi.PsiDocumentManager;
36 import com.intellij.psi.PsiElement;
37 import com.intellij.psi.PsiFile;
38 import com.intellij.psi.PsiWhiteSpace;
39 import com.intellij.psi.search.*;
40 import com.intellij.psi.search.scope.packageSet.NamedScope;
41 import com.intellij.psi.search.scope.packageSet.NamedScopeManager;
42 import com.intellij.psi.search.scope.packageSet.NamedScopesHolder;
43 import com.intellij.psi.util.PsiTreeUtil;
44 import com.intellij.psi.util.PsiUtilBase;
45 import com.intellij.psi.util.PsiUtilCore;
46 import com.intellij.ui.ComboboxWithBrowseButton;
47 import com.intellij.usages.Usage;
48 import com.intellij.usages.UsageView;
49 import com.intellij.usages.UsageViewManager;
50 import com.intellij.usages.rules.PsiElementUsage;
51 import com.intellij.util.ArrayUtil;
52 import com.intellij.util.PlatformUtils;
53 import org.jetbrains.annotations.NotNull;
54 import org.jetbrains.annotations.Nullable;
55
56 import javax.swing.*;
57 import java.awt.event.ActionEvent;
58 import java.awt.event.ActionListener;
59 import java.util.*;
60
61 public class ScopeChooserCombo extends ComboboxWithBrowseButton implements Disposable {
62   private Project myProject;
63   private boolean mySuggestSearchInLibs;
64   private boolean myPrevSearchFiles;
65
66   private NamedScopesHolder.ScopeListener myScopeListener;
67   private NamedScopeManager myNamedScopeManager;
68   private DependencyValidationManager myValidationManager;
69
70   public ScopeChooserCombo() {
71   }
72
73   public ScopeChooserCombo(final Project project, boolean suggestSearchInLibs, boolean prevSearchWholeFiles, String preselect) {
74     init(project, suggestSearchInLibs, prevSearchWholeFiles,  preselect);
75   }
76
77   public void init(final Project project, final String preselect){
78     init(project, false, true, preselect);    
79   }
80
81   public void init(final Project project, final boolean suggestSearchInLibs, final boolean prevSearchWholeFiles,  final String preselect) {
82     mySuggestSearchInLibs = suggestSearchInLibs;
83     myPrevSearchFiles = prevSearchWholeFiles;
84     final JComboBox combo = getComboBox();
85     myProject = project;
86     myScopeListener = new NamedScopesHolder.ScopeListener() {
87       public void scopesChanged() {
88         final SearchScope selectedScope = getSelectedScope();
89         rebuildModel();
90         if (selectedScope != null) {
91           selectScope(selectedScope.getDisplayName());
92         }
93       }
94     };
95     myNamedScopeManager = NamedScopeManager.getInstance(project);
96     myNamedScopeManager.addScopeListener(myScopeListener);
97     myValidationManager = DependencyValidationManager.getInstance(project);
98     myValidationManager.addScopeListener(myScopeListener);
99     addActionListener(createScopeChooserListener());
100
101     combo.setRenderer(new ListCellRendererWrapper<ScopeDescriptor>(combo.getRenderer()) {
102       @Override
103       public void customize(final JList list, final ScopeDescriptor value, final int index, final boolean selected, final boolean hasFocus) {
104         if (value != null) setText(value.getDisplay());
105       }
106     });
107
108     rebuildModel();
109
110     selectScope(preselect);
111   }
112
113   @Override
114   public void dispose() {
115     super.dispose();
116     if (myValidationManager != null) {
117       myValidationManager.removeScopeListener(myScopeListener);
118       myValidationManager = null;
119     }
120     if (myNamedScopeManager != null) {
121       myNamedScopeManager.removeScopeListener(myScopeListener);
122       myNamedScopeManager = null;
123     }
124     myScopeListener = null;
125   }
126
127   private void selectScope(String preselect) {
128     if (preselect != null) {
129       final JComboBox combo = getComboBox();
130       DefaultComboBoxModel model = (DefaultComboBoxModel)combo.getModel();
131       for (int i = 0; i < model.getSize(); i++) {
132         ScopeDescriptor descriptor = (ScopeDescriptor)model.getElementAt(i);
133         if (preselect.equals(descriptor.getDisplay())) {
134           combo.setSelectedIndex(i);
135           break;
136         }
137       }
138     }
139   }
140
141   private ActionListener createScopeChooserListener() {
142     return new ActionListener() {
143       public void actionPerformed(ActionEvent e) {
144         final String selection = getSelectedScopeName();
145         final EditScopesDialog dlg = EditScopesDialog.showDialog(myProject, selection);
146         if (dlg.isOK()){
147           rebuildModel();
148           final NamedScope namedScope = dlg.getSelectedScope();
149           if (namedScope != null) {
150             selectScope(namedScope.getName());
151           }
152         }
153       }
154     };
155   }
156
157   private void rebuildModel() {
158     getComboBox().setModel(createModel());
159   }
160
161   private DefaultComboBoxModel createModel() {
162     DefaultComboBoxModel model = new DefaultComboBoxModel();
163     createPredefinedScopeDescriptors(model);
164
165     List<NamedScope> changeLists = ChangeListsScopesProvider.getInstance(myProject).getCustomScopes();
166     for (NamedScope changeListScope : changeLists) {
167       model.addElement(new ScopeDescriptor(GlobalSearchScopes.filterScope(myProject, changeListScope)));
168     }
169     final NamedScopesHolder[] holders = NamedScopesHolder.getAllNamedScopeHolders(myProject);
170     for (NamedScopesHolder holder : holders) {
171       NamedScope[] scopes = holder.getEditableScopes(); //predefined scopes already included
172       for (NamedScope scope : scopes) {
173         model.addElement(new ScopeDescriptor(GlobalSearchScopes.filterScope(myProject, scope)));
174       }
175     }
176
177     return model;
178   }
179
180
181   private void createPredefinedScopeDescriptors(DefaultComboBoxModel model) {
182     for (SearchScope scope : getPredefinedScopes(myProject, DataManager.getInstance().getDataContext(), mySuggestSearchInLibs, myPrevSearchFiles, true, true)) {
183       model.addElement(new ScopeDescriptor(scope));
184     }
185     for (ScopeDescriptorProvider provider : Extensions.getExtensions(ScopeDescriptorProvider.EP_NAME)) {
186       for (ScopeDescriptor scopeDescriptor : provider.getScopeDescriptors(myProject)) {
187         model.addElement(scopeDescriptor);
188       }
189     }
190   }
191
192   public static List<SearchScope> getPredefinedScopes(@NotNull final Project project,
193                                                       @Nullable final DataContext dataContext,
194                                                       boolean suggestSearchInLibs,
195                                                       boolean prevSearchFiles,
196                                                       boolean currentSelection,
197                                                       boolean usageView) {
198     ArrayList<SearchScope> result = new ArrayList<SearchScope>();
199     result.add(GlobalSearchScope.projectScope(project));
200     if (suggestSearchInLibs) {
201       result.add(GlobalSearchScope.allScope(project));
202     }
203
204     if (!PlatformUtils.isCidr()) { // TODO: fix these scopes in AppCode
205       result.add(GlobalSearchScopes.projectProductionScope(project));
206       result.add(GlobalSearchScopes.projectTestScope(project));
207     }
208
209     if (dataContext != null) {
210       PsiElement dataContextElement = getDataContextElement(dataContext);
211
212       if (dataContextElement != null) {
213         if (!PlatformUtils.isCidr()) { // TODO: have an API to disable module scopes.
214           Module module = ModuleUtil.findModuleForPsiElement(dataContextElement);
215           if (module == null) {
216             module = LangDataKeys.MODULE.getData(dataContext);
217           }
218           if (module != null) {
219             result.add(module.getModuleScope());
220           }
221         }
222         if (dataContextElement.getContainingFile() != null) {
223           result.add(new LocalSearchScope(dataContextElement, IdeBundle.message("scope.current.file")));
224         }
225       }
226     }
227
228     if (currentSelection) {
229       FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
230       final Editor selectedTextEditor = fileEditorManager.getSelectedTextEditor();
231       if (selectedTextEditor != null) {
232         final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(selectedTextEditor.getDocument());
233         if (psiFile != null) {
234           if (selectedTextEditor.getSelectionModel().hasSelection()) {
235             final PsiElement startElement = psiFile.findElementAt(selectedTextEditor.getSelectionModel().getSelectionStart());
236             if (startElement != null) {
237               final PsiElement endElement = psiFile.findElementAt(selectedTextEditor.getSelectionModel().getSelectionEnd());
238               if (endElement != null) {
239                 final PsiElement parent = PsiTreeUtil.findCommonParent(startElement, endElement);
240                 if (parent != null) {
241                   final List<PsiElement> elements = new ArrayList<PsiElement>();
242                   final PsiElement[] children = parent.getChildren();
243                   for (PsiElement child : children) {
244                     if (!(child instanceof PsiWhiteSpace) && child.getContainingFile() != null) {
245                       elements.add(child);
246                     }
247                   }
248                   if (!elements.isEmpty()) {
249                     SearchScope local = new LocalSearchScope(PsiUtilCore.toPsiElementArray(elements), IdeBundle.message("scope.selection"));
250                     result.add(local);
251                   }
252                 }
253               }
254             }
255           }
256         }
257       }
258     }
259
260     if (usageView) {
261       UsageView selectedUsageView = UsageViewManager.getInstance(project).getSelectedUsageView();
262       if (selectedUsageView != null && !selectedUsageView.isSearchInProgress()) {
263         final Set<Usage> usages = selectedUsageView.getUsages();
264         final List<PsiElement> results = new ArrayList<PsiElement>(usages.size());
265
266         if (prevSearchFiles) {
267           final Set<VirtualFile> files = new HashSet<VirtualFile>();
268           for (Usage usage : usages) {
269             if (usage instanceof PsiElementUsage) {
270               PsiElement psiElement = ((PsiElementUsage)usage).getElement();
271               if (psiElement != null && psiElement.isValid()) {
272                 PsiFile psiFile = psiElement.getContainingFile();
273                 if (psiFile != null) {
274                   VirtualFile file = psiFile.getVirtualFile();
275                   if (file != null) files.add(file);
276                 }
277               }
278             }
279           }
280           if (!files.isEmpty()) {
281             GlobalSearchScope prev = new GlobalSearchScope(project) {
282               public String getDisplayName() {
283                 return IdeBundle.message("scope.files.in.previous.search.result");
284               }
285
286               public boolean contains(VirtualFile file) {
287                 return files.contains(file);
288               }
289
290               public int compare(VirtualFile file1, VirtualFile file2) {
291                 return 0;
292               }
293
294               public boolean isSearchInModuleContent(@NotNull Module aModule) {
295                 return true;
296               }
297
298               public boolean isSearchInLibraries() {
299                 return true;
300               }
301             };
302             result.add(prev);
303           }
304         }
305         else {
306           for (Usage usage : usages) {
307             if (usage instanceof PsiElementUsage) {
308               final PsiElement element = ((PsiElementUsage)usage).getElement();
309               if (element != null && element.isValid() && element.getContainingFile() != null) {
310                 results.add(element);
311               }
312             }
313           }
314
315           if (!results.isEmpty()) {
316             result.add(new LocalSearchScope(PsiUtilBase.toPsiElementArray(results), IdeBundle.message("scope.previous.search.results")));
317           }
318         }
319       }
320     }
321
322     final FavoritesManager favoritesManager = FavoritesManager.getInstance(project);
323     String[] favoritesLists = favoritesManager == null ? ArrayUtil.EMPTY_STRING_ARRAY : favoritesManager.getAvailableFavoritesLists();
324     for (final String favorite : favoritesLists) {
325       result.add(new GlobalSearchScope(project) {
326         @Override
327         public String getDisplayName() {
328           return "Favorite \'" + favorite + "\'";
329         }
330
331         @Override
332         public boolean contains(final VirtualFile file) {
333           return favoritesManager.contains(favorite, file);
334         }
335
336         @Override
337         public int compare(final VirtualFile file1, final VirtualFile file2) {
338           return 0;
339         }
340
341         @Override
342         public boolean isSearchInModuleContent(@NotNull final Module aModule) {
343           return true;
344         }
345
346         @Override
347         public boolean isSearchInLibraries() {
348           return true;
349         }
350       });
351     }
352
353     if (dataContext != null) {
354       final VirtualFile[] files = PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);
355       if (files != null) {
356         final List<VirtualFile> openFiles = Arrays.asList(files);
357         result.add(new DelegatingGlobalSearchScope(GlobalSearchScope.filesScope(project, openFiles)){
358           @Override
359           public String getDisplayName() {
360             return "Selected files";
361           }
362         });
363       }
364     }
365
366     return result;
367   }
368
369   public static PsiElement getDataContextElement(DataContext dataContext) {
370     PsiElement dataContextElement = LangDataKeys.PSI_FILE.getData(dataContext);
371
372     if (dataContextElement == null) {
373       dataContextElement = LangDataKeys.PSI_ELEMENT.getData(dataContext);
374     }
375     return dataContextElement;
376   }
377
378
379   @Nullable
380   public SearchScope getSelectedScope() {
381     JComboBox combo = getComboBox();
382     int idx = combo.getSelectedIndex();
383     if (idx < 0) return null;
384     return ((ScopeDescriptor)combo.getSelectedItem()).getScope();
385   }
386
387   @Nullable
388   public String getSelectedScopeName() {
389     JComboBox combo = getComboBox();
390     int idx = combo.getSelectedIndex();
391     if (idx < 0) return null;
392     return ((ScopeDescriptor)combo.getSelectedItem()).getDisplay();
393   }
394 }