diff options
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.java | 256 |
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); + } } |