popups before show() doesn't require disposal
[fedora-idea.git] / lang-impl / src / com / intellij / find / actions / ShowUsagesAction.java
blob6807017691d648e73ba9334fd0c9eb7a5d408910
1 package com.intellij.find.actions;
3 import com.intellij.codeInsight.hint.HintManager;
4 import com.intellij.codeInsight.hint.HintUtil;
5 import com.intellij.featureStatistics.FeatureUsageTracker;
6 import com.intellij.find.FindBundle;
7 import com.intellij.find.FindManager;
8 import com.intellij.find.findUsages.FindUsagesHandler;
9 import com.intellij.find.findUsages.FindUsagesManager;
10 import com.intellij.find.findUsages.PsiElement2UsageTargetAdapter;
11 import com.intellij.find.impl.FindManagerImpl;
12 import com.intellij.openapi.actionSystem.AnAction;
13 import com.intellij.openapi.actionSystem.AnActionEvent;
14 import com.intellij.openapi.actionSystem.PlatformDataKeys;
15 import com.intellij.openapi.editor.Editor;
16 import com.intellij.openapi.fileEditor.FileEditor;
17 import com.intellij.openapi.fileEditor.FileEditorLocation;
18 import com.intellij.openapi.fileEditor.TextEditor;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.ui.popup.JBPopup;
21 import com.intellij.openapi.ui.popup.JBPopupFactory;
22 import com.intellij.openapi.ui.popup.PopupChooserBuilder;
23 import com.intellij.openapi.util.Disposer;
24 import com.intellij.openapi.vfs.VirtualFile;
25 import com.intellij.psi.PsiDocumentManager;
26 import com.intellij.psi.PsiElement;
27 import com.intellij.psi.search.ProjectScope;
28 import com.intellij.psi.search.PsiElementProcessor;
29 import com.intellij.psi.search.SearchScope;
30 import com.intellij.ui.ColoredListCellRenderer;
31 import com.intellij.ui.ListSpeedSearch;
32 import com.intellij.ui.SimpleTextAttributes;
33 import com.intellij.ui.SpeedSearchBase;
34 import com.intellij.ui.awt.RelativePoint;
35 import com.intellij.usages.*;
36 import com.intellij.usages.impl.GroupNode;
37 import com.intellij.usages.impl.UsageNode;
38 import com.intellij.usages.impl.UsageViewImpl;
39 import com.intellij.util.CommonProcessors;
40 import gnu.trove.THashSet;
41 import org.jetbrains.annotations.NotNull;
43 import javax.swing.*;
44 import java.awt.*;
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.Set;
48 import java.util.Vector;
50 public class ShowUsagesAction extends AnAction {
51 public ShowUsagesAction() {
52 setInjectedContext(true);
55 public void actionPerformed(AnActionEvent e) {
56 final Project project = e.getData(PlatformDataKeys.PROJECT);
57 if (project == null) return;
58 final RelativePoint popupPosition = JBPopupFactory.getInstance().guessBestPopupLocation(e.getDataContext());
59 PsiDocumentManager.getInstance(project).commitAllDocuments();
60 FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.goto.usages");
62 UsageTarget[] usageTargets = e.getData(UsageView.USAGE_TARGETS_KEY);
63 final Editor editor = e.getData(PlatformDataKeys.EDITOR);
64 if (usageTargets == null) {
65 FindUsagesAction.chooseAmbiguousTargetAndPerform(project, editor, new PsiElementProcessor<PsiElement>() {
66 public boolean execute(final PsiElement element) {
67 showElementUsages(project, element, editor, popupPosition);
68 return false;
70 });
72 else {
73 PsiElement element = ((PsiElement2UsageTargetAdapter)usageTargets[0]).getElement();
74 showElementUsages(project, element, editor, popupPosition);
78 private static void showElementUsages(@NotNull Project project, final PsiElement element, Editor editor, final RelativePoint popupPosition) {
79 ArrayList<Usage> usages = new ArrayList<Usage>();
80 CommonProcessors.CollectProcessor<Usage> collect = new CommonProcessors.CollectProcessor<Usage>(usages);
81 FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager();
82 FindUsagesHandler handler = findUsagesManager.getFindUsagesHandler(element, false);
83 UsageViewPresentation presentation = findUsagesManager.processUsages(element, collect, handler);
84 if (presentation == null) return;
85 if (usages.isEmpty()) {
86 String text = FindBundle.message("no.usages.found.in", searchScopePresentableName(element, handler));
87 if (editor != null) {
88 HintManager.getInstance().showInformationHint(editor, text);
90 else {
91 JLabel label = HintUtil.createInformationLabel(text);
92 HintManager.getInstance().showHint(label, popupPosition, HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, 0);
95 else {
96 final String title = presentation.getTabText();
97 JBPopup popup = getUsagePopup(usages, title, project, element, handler);
98 if (popup != null) {
99 popup.show(popupPosition);
104 private static String searchScopePresentableName(PsiElement element, final FindUsagesHandler handler) {
105 SearchScope searchScope = FindUsagesManager.getCurrentSearchScope(handler);
106 if (searchScope == null) searchScope = ProjectScope.getAllScope(element.getProject());
107 return searchScope.getDisplayName();
110 private static JBPopup getUsagePopup(List<Usage> usages, final String title, final Project project, PsiElement element,
111 final FindUsagesHandler handler) {
112 Usage[] arr = usages.toArray(new Usage[usages.size()]);
113 UsageViewPresentation presentation = new UsageViewPresentation();
114 presentation.setDetachedMode(true);
115 final UsageViewImpl usageView = (UsageViewImpl)UsageViewManager.getInstance(project).createUsageView(new UsageTarget[0], arr, presentation, null);
117 GroupNode root = usageView.getRoot();
118 List<UsageNode> nodes = new ArrayList<UsageNode>();
119 Set<Usage> filteredUsages = new THashSet<Usage>();
121 addUsageNodes(root, nodes, usageView, filteredUsages);
122 if (nodes.size() == 1) {
123 // usage view can filter usages down to one
124 Usage usage = nodes.get(0).getUsage();
125 if (filteredUsages.size() == 1) {
126 navigateAndHint(usage, FindBundle.message("show.usages.only.usage", searchScopePresentableName(element, handler)));
128 else {
129 navigateAndHint(usage, FindBundle.message("all.usages.are.in.this.line", filteredUsages.size(), searchScopePresentableName(element, handler)));
131 Disposer.dispose(usageView);
132 return null;
135 final JList list = new JList(new Vector<UsageNode>(nodes));
136 list.setCellRenderer(new ListCellRenderer(){
137 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
138 JPanel panel = new JPanel(new GridBagLayout());
139 UsageNode usageNode = (UsageNode)value;
140 int seq = appendGroupText((GroupNode)usageNode.getParent(), panel,list, value, index, isSelected);
142 ColoredListCellRenderer usageRenderer = new ColoredListCellRenderer() {
143 protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
144 UsageNode usageNode = (UsageNode)value;
145 Usage usage = usageNode.getUsage();
146 UsagePresentation presentation = usage.getPresentation();
147 setIcon(presentation.getIcon());
149 TextChunk[] text = presentation.getText();
150 for (TextChunk textChunk : text) {
151 append(textChunk.getText(), SimpleTextAttributes.fromTextAttributes(textChunk.getAttributes()));
155 usageRenderer.setIpad(new Insets(0,0,0,0));
156 usageRenderer.setBorder(null);
157 usageRenderer.getListCellRendererComponent(list, value, index, isSelected, false);
158 panel.add(usageRenderer, new GridBagConstraints(seq, 0, GridBagConstraints.REMAINDER, 0, 1, 0,
159 GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 1));
160 panel.setBackground(list.getBackground());
161 return panel;
163 private int appendGroupText(final GroupNode node, JPanel panel, JList list, Object value, int index, boolean isSelected) {
164 if (node != null && node.getGroup() != null) {
165 int seq = appendGroupText((GroupNode)node.getParent(), panel, list, value, index, isSelected);
166 if (node.canNavigateToSource()) {
167 ColoredListCellRenderer renderer = new ColoredListCellRenderer() {
168 protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
169 UsageGroup group = node.getGroup();
170 setIcon(group.getIcon(false));
171 append(group.getText(usageView), SimpleTextAttributes.REGULAR_ATTRIBUTES);
172 append(" ", SimpleTextAttributes.REGULAR_ATTRIBUTES);
175 renderer.setIpad(new Insets(0,0,0,0));
176 renderer.setBorder(null);
177 renderer.getListCellRendererComponent(list, value, index, isSelected, false);
178 panel.add(renderer, new GridBagConstraints(seq, 0, 1, 0, 0, 0,
179 GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 1));
180 return seq+1;
183 return 0;
187 final Runnable runnable = new Runnable() {
188 public void run() {
189 Object element = list.getSelectedValue();
190 if (element == null) return;
191 UsageNode node = (UsageNode)element;
192 Usage usage = node.getUsage();
193 navigateAndHint(usage, null);
197 ListSpeedSearch speedSearch = new ListSpeedSearch(list) {
198 protected String getElementText(final Object element) {
199 StringBuilder text = new StringBuilder();
200 UsageNode node = (UsageNode)element;
201 Usage usage = node.getUsage();
202 VirtualFile virtualFile = UsageListCellRenderer.getVirtualFile(usage);
203 if (virtualFile != null) {
204 text.append(virtualFile.getName());
206 TextChunk[] chunks = usage.getPresentation().getText();
207 for (TextChunk chunk : chunks) {
208 text.append(chunk.getText());
210 return text.toString();
213 speedSearch.setComparator(new SpeedSearchBase.SpeedSearchComparator() {
214 public void translatePattern(final StringBuilder buf, final String pattern) {
215 final int len = pattern.length();
216 for (int i = 0; i < len; ++i) {
217 translateCharacter(buf, pattern.charAt(i));
222 PopupChooserBuilder builder = new PopupChooserBuilder(list);
223 if (title != null) {
224 builder.setTitle(title + " " +FindBundle.message("some.usages.found", usages.size()));
227 final JBPopup popup = builder.setItemChoosenCallback(runnable).createPopup();
228 Disposer.register(popup, usageView);
229 return popup;
232 private static void addUsageNodes(GroupNode root, List<UsageNode> outNodes, final UsageViewImpl usageView, final Set<Usage> filteredUsages) {
233 for (UsageNode node : root.getUsageNodes()) {
234 Usage usage = node.getUsage();
235 if (usageView.isVisible(usage)) {
236 node.setParent(root);
237 outNodes.add(node);
238 filteredUsages.add(usage);
241 for (GroupNode groupNode : root.getSubGroups()) {
242 groupNode.setParent(root);
243 addUsageNodes(groupNode, outNodes, usageView, filteredUsages);
247 public void update(AnActionEvent e){
248 FindUsagesInFileAction.updateFindUsagesAction(e);
251 private static void navigateAndHint(Usage usage, final String hint) {
252 usage.navigate(true);
253 if (hint == null) return;
254 FileEditorLocation location = usage.getLocation();
255 FileEditor newFileEditor = location == null ? null : location.getEditor();
256 final Editor newEditor = newFileEditor instanceof TextEditor ? ((TextEditor)newFileEditor).getEditor() : null;
257 if (newEditor != null) {
258 //opening editor is performing in invokeLater
259 SwingUtilities.invokeLater(new Runnable() {
260 public void run() {
261 newEditor.getScrollingModel().runActionOnScrollingFinished(new Runnable() {
262 public void run() {
263 // after new editor created, some editor resizing events are still bubbling. To prevent hiding hint, invokeLater this
264 SwingUtilities.invokeLater(new Runnable() {
265 public void run() {
266 HintManager.getInstance().showInformationHint(newEditor, hint);