summaryrefslogtreecommitdiff
path: root/platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java')
-rw-r--r--platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java256
1 files changed, 255 insertions, 1 deletions
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java
index 3c0e84a11656..3617c944e888 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java
+++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/NavigationUtil.java
@@ -19,30 +19,46 @@ package com.intellij.codeInsight.navigation;
import com.intellij.ide.util.DefaultPsiElementCellRenderer;
import com.intellij.ide.util.EditSourceUtil;
import com.intellij.ide.util.PsiElementListCellRenderer;
+import com.intellij.navigation.GotoRelatedItem;
+import com.intellij.navigation.GotoRelatedProvider;
import com.intellij.navigation.NavigationItem;
+import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.markup.HighlighterTargetArea;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
+import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.impl.EditorHistoryManager;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.ElementBase;
import com.intellij.psi.search.PsiElementProcessor;
-import com.intellij.ui.JBListWithHintProvider;
+import com.intellij.ui.*;
+import com.intellij.ui.popup.list.ListPopupImpl;
+import com.intellij.ui.popup.list.PopupListElementRenderer;
+import com.intellij.util.Processor;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.util.*;
+import java.util.List;
/**
* @author ven
@@ -220,4 +236,242 @@ public final class NavigationUtil {
}
return attributes;
}
+
+ @NotNull
+ public static JBPopup getRelatedItemsPopup(final List<? extends GotoRelatedItem> items, String title) {
+ Object[] elements = new Object[items.size()];
+ //todo[nik] move presentation logic to GotoRelatedItem class
+ final Map<PsiElement, GotoRelatedItem> itemsMap = new HashMap<PsiElement, GotoRelatedItem>();
+ for (int i = 0; i < items.size(); i++) {
+ GotoRelatedItem item = items.get(i);
+ elements[i] = item.getElement() != null ? item.getElement() : item;
+ itemsMap.put(item.getElement(), item);
+ }
+
+ return getPsiElementPopup(elements, itemsMap, title, new Processor<Object>() {
+ @Override
+ public boolean process(Object element) {
+ if (element instanceof PsiElement) {
+ //noinspection SuspiciousMethodCalls
+ itemsMap.get(element).navigate();
+ }
+ else {
+ ((GotoRelatedItem)element).navigate();
+ }
+ return true;
+ }
+ }
+ );
+ }
+
+ private static JBPopup getPsiElementPopup(final Object[] elements, final Map<PsiElement, GotoRelatedItem> itemsMap,
+ final String title, final Processor<Object> processor) {
+
+ final Ref<Boolean> hasMnemonic = Ref.create(false);
+ final DefaultPsiElementCellRenderer renderer = new DefaultPsiElementCellRenderer() {
+ {
+ setFocusBorderEnabled(false);
+ }
+
+ @Override
+ public String getElementText(PsiElement element) {
+ String customName = itemsMap.get(element).getCustomName();
+ return (customName != null ? customName : super.getElementText(element));
+ }
+
+ @Override
+ protected Icon getIcon(PsiElement element) {
+ Icon customIcon = itemsMap.get(element).getCustomIcon();
+ return customIcon != null ? customIcon : super.getIcon(element);
+ }
+
+ @Override
+ public String getContainerText(PsiElement element, String name) {
+ String customContainerName = itemsMap.get(element).getCustomContainerName();
+
+ if (customContainerName != null) {
+ return customContainerName;
+ }
+ PsiFile file = element.getContainingFile();
+ return file != null && !getElementText(element).equals(file.getName())
+ ? "(" + file.getName() + ")"
+ : null;
+ }
+
+ @Override
+ protected DefaultListCellRenderer getRightCellRenderer(Object value) {
+ return null;
+ }
+
+ @Override
+ protected boolean customizeNonPsiElementLeftRenderer(ColoredListCellRenderer renderer,
+ JList list,
+ Object value,
+ int index,
+ boolean selected,
+ boolean hasFocus) {
+ final GotoRelatedItem item = (GotoRelatedItem)value;
+ Color color = list.getForeground();
+ final SimpleTextAttributes nameAttributes = new SimpleTextAttributes(Font.PLAIN, color);
+ final String name = item.getCustomName();
+ if (name == null) return false;
+ renderer.append(name, nameAttributes);
+ renderer.setIcon(item.getCustomIcon());
+ return true;
+ }
+
+ @Override
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ final JPanel component = (JPanel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ if (!hasMnemonic.get()) return component;
+
+ final JPanel panelWithMnemonic = new JPanel(new BorderLayout());
+ final int mnemonic = getMnemonic(value, itemsMap);
+ final JLabel label = new JLabel("");
+ if (mnemonic != -1) {
+ label.setText(mnemonic + ".");
+ label.setDisplayedMnemonicIndex(0);
+ }
+ label.setPreferredSize(new JLabel("8.").getPreferredSize());
+
+ final JComponent leftRenderer = (JComponent)component.getComponents()[0];
+ component.remove(leftRenderer);
+ panelWithMnemonic.setBorder(BorderFactory.createEmptyBorder(0, 7, 0, 0));
+ panelWithMnemonic.setBackground(leftRenderer.getBackground());
+ label.setBackground(leftRenderer.getBackground());
+ panelWithMnemonic.add(label, BorderLayout.WEST);
+ panelWithMnemonic.add(leftRenderer, BorderLayout.CENTER);
+ component.add(panelWithMnemonic);
+ return component;
+ }
+ };
+ final ListPopupImpl popup = new ListPopupImpl(new BaseListPopupStep<Object>(title, Arrays.asList(elements)) {
+ @Override
+ public boolean isSpeedSearchEnabled() {
+ return true;
+ }
+
+ @Override
+ public String getIndexedString(Object value) {
+ if (value instanceof GotoRelatedItem) {
+ //noinspection ConstantConditions
+ return ((GotoRelatedItem)value).getCustomName();
+ }
+ final PsiElement element = (PsiElement)value;
+ return renderer.getElementText(element) + " " + renderer.getContainerText(element, null);
+ }
+
+ @Override
+ public PopupStep onChosen(Object selectedValue, boolean finalChoice) {
+ processor.process(selectedValue);
+ return super.onChosen(selectedValue, finalChoice);
+ }
+ }) {
+ };
+ popup.getList().setCellRenderer(new PopupListElementRenderer(popup) {
+ Map<Object, String> separators = new HashMap<Object, String>();
+ {
+ final ListModel model = popup.getList().getModel();
+ String current = null;
+ boolean hasTitle = false;
+ for (int i = 0; i < model.getSize(); i++) {
+ final Object element = model.getElementAt(i);
+ final GotoRelatedItem item = itemsMap.get(element);
+ if (item != null && !StringUtil.equals(current, item.getGroup())) {
+ current = item.getGroup();
+ separators.put(element, current);
+ if (!hasTitle && !StringUtil.isEmpty(current)) {
+ hasTitle = true;
+ }
+ }
+ }
+
+ if (!hasTitle) {
+ separators.remove(model.getElementAt(0));
+ }
+ }
+ @Override
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ final Component component = renderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ final String separator = separators.get(value);
+
+ if (separator != null) {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(component, BorderLayout.CENTER);
+ final SeparatorWithText sep = new SeparatorWithText() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ g.setColor(new JBColor(Color.WHITE, UIUtil.getSeparatorColor()));
+ g.fillRect(0,0,getWidth(), getHeight());
+ super.paintComponent(g);
+ }
+ };
+ sep.setCaption(separator);
+ panel.add(sep, BorderLayout.NORTH);
+ return panel;
+ }
+ return component;
+ }
+ });
+
+ popup.setMinimumSize(new Dimension(200, -1));
+
+ for (Object item : elements) {
+ final int mnemonic = getMnemonic(item, itemsMap);
+ if (mnemonic != -1) {
+ final Action action = createNumberAction(mnemonic, popup, itemsMap, processor);
+ popup.registerAction(mnemonic + "Action", KeyStroke.getKeyStroke(String.valueOf(mnemonic)), action);
+ popup.registerAction(mnemonic + "Action", KeyStroke.getKeyStroke("NUMPAD" + String.valueOf(mnemonic)), action);
+ hasMnemonic.set(true);
+ }
+ }
+ return popup;
+ }
+
+ private static Action createNumberAction(final int mnemonic,
+ final ListPopupImpl listPopup,
+ final Map<PsiElement, GotoRelatedItem> itemsMap,
+ final Processor<Object> processor) {
+ return new AbstractAction() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ for (final Object item : listPopup.getListStep().getValues()) {
+ if (getMnemonic(item, itemsMap) == mnemonic) {
+ listPopup.setFinalRunnable(new Runnable() {
+ @Override
+ public void run() {
+ processor.process(item);
+ }
+ });
+ listPopup.closeOk(null);
+ }
+ }
+ }
+ };
+ }
+
+ private static int getMnemonic(Object item, Map<PsiElement, GotoRelatedItem> itemsMap) {
+ return (item instanceof GotoRelatedItem ? (GotoRelatedItem)item : itemsMap.get((PsiElement)item)).getMnemonic();
+ }
+
+ @NotNull
+ public static List<GotoRelatedItem> collectRelatedItems(@NotNull PsiElement contextElement, @Nullable DataContext dataContext) {
+ Set<GotoRelatedItem> items = ContainerUtil.newLinkedHashSet();
+ for (GotoRelatedProvider provider : Extensions.getExtensions(GotoRelatedProvider.EP_NAME)) {
+ items.addAll(provider.getItems(contextElement));
+ if (dataContext != null) {
+ items.addAll(provider.getItems(dataContext));
+ }
+ }
+ GotoRelatedItem[] result = items.toArray(new GotoRelatedItem[items.size()]);
+ Arrays.sort(result, new Comparator<GotoRelatedItem>() {
+ @Override
+ public int compare(GotoRelatedItem i1, GotoRelatedItem i2) {
+ String o1 = i1.getGroup();
+ String o2 = i2.getGroup();
+ return StringUtil.isEmpty(o1) ? 1 : StringUtil.isEmpty(o2) ? -1 : o1.compareTo(o2);
+ }
+ });
+ return Arrays.asList(result);
+ }
}