ability to show "Current File" always (WEB-20644)
[idea/community.git] / platform / lang-impl / src / com / intellij / ide / util / scopeChooser / ScopeChooserCombo.java
1 /*
2  * Copyright 2000-2016 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.openapi.Disposable;
20 import com.intellij.openapi.actionSystem.DataContext;
21 import com.intellij.openapi.extensions.Extensions;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.util.Condition;
24 import com.intellij.packageDependencies.ChangeListsScopesProvider;
25 import com.intellij.packageDependencies.DependencyValidationManager;
26 import com.intellij.psi.search.GlobalSearchScope;
27 import com.intellij.psi.search.GlobalSearchScopesCore;
28 import com.intellij.psi.search.PredefinedSearchScopeProvider;
29 import com.intellij.psi.search.SearchScope;
30 import com.intellij.psi.search.scope.packageSet.NamedScope;
31 import com.intellij.psi.search.scope.packageSet.NamedScopeManager;
32 import com.intellij.psi.search.scope.packageSet.NamedScopesHolder;
33 import com.intellij.ui.ComboboxSpeedSearch;
34 import com.intellij.ui.ComboboxWithBrowseButton;
35 import com.intellij.ui.ListCellRendererWrapper;
36 import org.jetbrains.annotations.Nullable;
37
38 import javax.swing.*;
39 import java.awt.*;
40 import java.awt.event.ActionEvent;
41 import java.awt.event.ActionListener;
42 import java.util.ArrayList;
43 import java.util.List;
44
45 public class ScopeChooserCombo extends ComboboxWithBrowseButton implements Disposable {
46   private Project myProject;
47   private boolean mySuggestSearchInLibs;
48   private boolean myPrevSearchFiles;
49   private NamedScopesHolder.ScopeListener myScopeListener;
50   private NamedScopeManager myNamedScopeManager;
51   private DependencyValidationManager myValidationManager;
52   private boolean myCurrentSelection = true;
53   private boolean myUsageView = true;
54   private Condition<ScopeDescriptor> myScopeFilter;
55   private boolean myShowEmptyScopes = false;
56
57   public ScopeChooserCombo() {
58     super(new IgnoringComboBox(){
59       @Override
60       protected boolean isIgnored(Object item) {
61         return item instanceof ScopeSeparator;
62       }
63     });
64   }
65
66   public ScopeChooserCombo(final Project project, boolean suggestSearchInLibs, boolean prevSearchWholeFiles, String preselect) {
67     this();
68     init(project, suggestSearchInLibs, prevSearchWholeFiles,  preselect);
69   }
70
71   public void init(final Project project, final String preselect){
72     init(project, false, true, preselect);
73   }
74
75   public void init(final Project project, final boolean suggestSearchInLibs, final boolean prevSearchWholeFiles, final String preselect) {
76     init(project, suggestSearchInLibs, prevSearchWholeFiles, preselect, null);
77   }
78
79   public void init(final Project project,
80                    final boolean suggestSearchInLibs,
81                    final boolean prevSearchWholeFiles,
82                    final String preselect,
83                    @Nullable Condition<ScopeDescriptor> scopeFilter) {
84     mySuggestSearchInLibs = suggestSearchInLibs;
85     myPrevSearchFiles = prevSearchWholeFiles;
86     myProject = project;
87     myScopeListener = new NamedScopesHolder.ScopeListener() {
88       @Override
89       public void scopesChanged() {
90         final SearchScope selectedScope = getSelectedScope();
91         rebuildModel();
92         if (selectedScope != null) {
93           selectScope(selectedScope.getDisplayName());
94         }
95       }
96     };
97     myScopeFilter = scopeFilter;
98     myNamedScopeManager = NamedScopeManager.getInstance(project);
99     myNamedScopeManager.addScopeListener(myScopeListener);
100     myValidationManager = DependencyValidationManager.getInstance(project);
101     myValidationManager.addScopeListener(myScopeListener);
102     addActionListener(createScopeChooserListener());
103
104     final JComboBox combo = getComboBox();
105     combo.setRenderer(new ScopeDescriptionWithDelimiterRenderer());
106
107     rebuildModel();
108
109     selectScope(preselect);
110     new ComboboxSpeedSearch(combo) {
111       @Override
112       protected String getElementText(Object element) {
113         if (element instanceof ScopeDescriptor) {
114           final ScopeDescriptor descriptor = (ScopeDescriptor)element;
115           return descriptor.getDisplay();
116         }
117         return null;
118       }
119     };
120   }
121
122   public void setCurrentSelection(boolean currentSelection) {
123     myCurrentSelection = currentSelection;
124   }
125
126   public void setUsageView(boolean usageView) {
127     myUsageView = usageView;
128   }
129
130   @Override
131   public void dispose() {
132     super.dispose();
133     if (myValidationManager != null) {
134       myValidationManager.removeScopeListener(myScopeListener);
135       myValidationManager = null;
136     }
137     if (myNamedScopeManager != null) {
138       myNamedScopeManager.removeScopeListener(myScopeListener);
139       myNamedScopeManager = null;
140     }
141     myScopeListener = null;
142   }
143
144   private void selectScope(String preselect) {
145     if (preselect != null) {
146       final JComboBox combo = getComboBox();
147       DefaultComboBoxModel model = (DefaultComboBoxModel)combo.getModel();
148       for (int i = 0; i < model.getSize(); i++) {
149         ScopeDescriptor descriptor = (ScopeDescriptor)model.getElementAt(i);
150         if (preselect.equals(descriptor.getDisplay())) {
151           combo.setSelectedIndex(i);
152           break;
153         }
154       }
155     }
156   }
157
158   private ActionListener createScopeChooserListener() {
159     return new ActionListener() {
160       @Override
161       public void actionPerformed(ActionEvent e) {
162         final String selection = getSelectedScopeName();
163         final EditScopesDialog dlg = EditScopesDialog.showDialog(myProject, selection);
164         if (dlg.isOK()){
165           rebuildModel();
166           final NamedScope namedScope = dlg.getSelectedScope();
167           if (namedScope != null) {
168             selectScope(namedScope.getName());
169           }
170         }
171       }
172     };
173   }
174
175   private void rebuildModel() {
176     getComboBox().setModel(createModel());
177   }
178
179   private DefaultComboBoxModel createModel() {
180     final DefaultComboBoxModel model = new DefaultComboBoxModel();
181
182     createPredefinedScopeDescriptors(model);
183
184     final List<NamedScope> changeLists = ChangeListsScopesProvider.getInstance(myProject).getFilteredScopes();
185     if (!changeLists.isEmpty()) {
186       model.addElement(new ScopeSeparator("VCS Scopes"));
187       for (NamedScope changeListScope : changeLists) {
188         final GlobalSearchScope scope = GlobalSearchScopesCore.filterScope(myProject, changeListScope);
189         addScopeDescriptor(model, new ScopeDescriptor(scope));
190       }
191     }
192
193     final List<ScopeDescriptor> customScopes = new ArrayList<ScopeDescriptor>();
194     final NamedScopesHolder[] holders = NamedScopesHolder.getAllNamedScopeHolders(myProject);
195     for (NamedScopesHolder holder : holders) {
196       final NamedScope[] scopes = holder.getEditableScopes();  // predefined scopes already included
197       for (NamedScope scope : scopes) {
198         final GlobalSearchScope searchScope = GlobalSearchScopesCore.filterScope(myProject, scope);
199         customScopes.add(new ScopeDescriptor(searchScope));
200       }
201     }
202     if (!customScopes.isEmpty()) {
203       model.addElement(new ScopeSeparator("Custom Scopes"));
204       for (ScopeDescriptor scope : customScopes) {
205         addScopeDescriptor(model, scope);
206       }
207     }
208
209     return model;
210   }
211
212   @Override
213   public Dimension getPreferredSize() {
214     if (isPreferredSizeSet()) {
215       return super.getPreferredSize();
216     }
217     Dimension preferredSize = super.getPreferredSize();
218     return new Dimension(Math.min(400, preferredSize.width), preferredSize.height);
219   }
220
221   @Override
222   public Dimension getMinimumSize() {
223     if (isMinimumSizeSet()) {
224       return super.getMinimumSize();
225     }
226     Dimension minimumSize = super.getMinimumSize();
227     return new Dimension(Math.min(200, minimumSize.width), minimumSize.height);
228   }
229
230   private void createPredefinedScopeDescriptors(DefaultComboBoxModel model) {
231     @SuppressWarnings("deprecation") final DataContext context = DataManager.getInstance().getDataContext();
232     for (SearchScope scope : PredefinedSearchScopeProvider.getInstance().getPredefinedScopes(myProject, context, mySuggestSearchInLibs,
233                                                                                              myPrevSearchFiles, myCurrentSelection,
234                                                                                              myUsageView, myShowEmptyScopes)) {
235       addScopeDescriptor(model, new ScopeDescriptor(scope));
236     }
237     for (ScopeDescriptorProvider provider : Extensions.getExtensions(ScopeDescriptorProvider.EP_NAME)) {
238       for (ScopeDescriptor scopeDescriptor : provider.getScopeDescriptors(myProject)) {
239         if(myScopeFilter == null || myScopeFilter.value(scopeDescriptor)) {
240           model.addElement(scopeDescriptor);
241         }
242       }
243     }
244   }
245
246   private void addScopeDescriptor(DefaultComboBoxModel model, ScopeDescriptor scopeDescriptor) {
247     if (myScopeFilter == null || myScopeFilter.value(scopeDescriptor)) {
248       model.addElement(scopeDescriptor);
249     }
250   }
251
252   public void setShowEmptyScopes(boolean showEmptyScopes) {
253     myShowEmptyScopes = showEmptyScopes;
254   }
255
256   @Nullable
257   public SearchScope getSelectedScope() {
258     final JComboBox combo = getComboBox();
259     int idx = combo.getSelectedIndex();
260     return idx < 0 ? null : ((ScopeDescriptor)combo.getSelectedItem()).getScope();
261   }
262
263   @Nullable
264   public String getSelectedScopeName() {
265     final JComboBox combo = getComboBox();
266     int idx = combo.getSelectedIndex();
267     return idx < 0 ? null : ((ScopeDescriptor)combo.getSelectedItem()).getDisplay();
268   }
269
270   private static class ScopeSeparator extends ScopeDescriptor {
271     private final String myText;
272
273     public ScopeSeparator(final String text) {
274       super(null);
275       myText = text;
276     }
277
278     @Override
279     public String getDisplay() {
280       return myText;
281     }
282   }
283
284   private static class ScopeDescriptionWithDelimiterRenderer extends ListCellRendererWrapper<ScopeDescriptor> {
285     @Override
286     public void customize(JList list, ScopeDescriptor value, int index, boolean selected, boolean hasFocus) {
287       setText(value.getDisplay());
288       if (value instanceof ScopeSeparator) {
289         setSeparator();
290       }
291     }
292   }
293 }