From 0097e610515dab9738c19847646ac909def409cf Mon Sep 17 00:00:00 2001 From: "sergey.vasiliev" Date: Tue, 16 Feb 2010 10:57:45 +0300 Subject: [PATCH] NavigationGutterIconBuilder --- .../navigation/NavigationGutterIconBuilder.java | 263 +++++++++++++++++++++ .../navigation/NavigationGutterIconRenderer.java | 126 ++++++++++ 2 files changed, 389 insertions(+) create mode 100644 xml/dom-openapi/src/com/intellij/codeInsight/navigation/NavigationGutterIconBuilder.java create mode 100644 xml/dom-openapi/src/com/intellij/codeInsight/navigation/NavigationGutterIconRenderer.java diff --git a/xml/dom-openapi/src/com/intellij/codeInsight/navigation/NavigationGutterIconBuilder.java b/xml/dom-openapi/src/com/intellij/codeInsight/navigation/NavigationGutterIconBuilder.java new file mode 100644 index 0000000000..5066558334 --- /dev/null +++ b/xml/dom-openapi/src/com/intellij/codeInsight/navigation/NavigationGutterIconBuilder.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2000-2007 JetBrains s.r.o. All Rights Reserved. + */ +package com.intellij.codeInsight.navigation; + +import com.intellij.codeHighlighting.Pass; +import com.intellij.codeInsight.daemon.LineMarkerInfo; +import com.intellij.ide.util.DefaultPsiElementCellRenderer; +import com.intellij.ide.util.PsiElementListCellRenderer; +import com.intellij.lang.annotation.Annotation; +import com.intellij.lang.annotation.AnnotationHolder; +import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.openapi.editor.markup.GutterIconRenderer; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.NotNullLazyValue; +import com.intellij.psi.PsiElement; +import com.intellij.psi.SmartPointerManager; +import com.intellij.psi.SmartPsiElementPointer; +import com.intellij.util.NotNullFunction; +import com.intellij.util.NullableFunction; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.ElementPresentationManager; +import com.intellij.util.xml.highlighting.DomElementAnnotationHolder; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.text.MessageFormat; +import java.util.*; + +/** + * @author peter + */ +public class NavigationGutterIconBuilder { + @NonNls private static final String PATTERN = "    {0}"; + private static final NotNullFunction> DEFAULT_PSI_CONVERTOR = new NotNullFunction>() { + @NotNull + public Collection fun(final PsiElement element) { + return ContainerUtil.createMaybeSingletonList(element); + } + }; + + private final Icon myIcon; + private final NotNullFunction> myConvertor; + + private NotNullLazyValue> myTargets; + private boolean myLazy; + private String myTooltipText; + private String myPopupTitle; + private String myEmptyText; + private String myTooltipTitle; + private GutterIconRenderer.Alignment myAlignment = GutterIconRenderer.Alignment.CENTER; + private PsiElementListCellRenderer myCellRenderer; + private NullableFunction myNamer = (NullableFunction)ElementPresentationManager.NAMER; + public static final NotNullFunction> DEFAULT_DOM_CONVERTOR = new NotNullFunction>() { + @NotNull + public Collection fun(final DomElement o) { + return ContainerUtil.createMaybeSingletonList(o.getXmlElement()); + } + }; + + protected NavigationGutterIconBuilder(@NotNull final Icon icon, NotNullFunction> convertor) { + myIcon = icon; + myConvertor = convertor; + } + + public static NavigationGutterIconBuilder create(@NotNull final Icon icon) { + return create(icon, DEFAULT_PSI_CONVERTOR); + } + + public static NavigationGutterIconBuilder create(@NotNull final Icon icon, NotNullFunction> convertor) { + return new NavigationGutterIconBuilder(icon, convertor); + } + + public NavigationGutterIconBuilder setTarget(@Nullable T target) { + return setTargets(ContainerUtil.createMaybeSingletonList(target)); + } + + public NavigationGutterIconBuilder setTargets(@NotNull T... targets) { + return setTargets(Arrays.asList(targets)); + } + + public NavigationGutterIconBuilder setTargets(@NotNull final NotNullLazyValue> targets) { + myTargets = targets; + myLazy = true; + return this; + } + + public NavigationGutterIconBuilder setTargets(@NotNull final Collection targets) { + myTargets = new NotNullLazyValue>() { + @NotNull + public Collection compute() { + return targets; + } + }; + return this; + } + + public NavigationGutterIconBuilder setTooltipText(@NotNull String tooltipText) { + myTooltipText = tooltipText; + return this; + } + + public NavigationGutterIconBuilder setAlignment(@NotNull final GutterIconRenderer.Alignment alignment) { + myAlignment = alignment; + return this; + } + + public NavigationGutterIconBuilder setPopupTitle(@NotNull String popupTitle) { + myPopupTitle = popupTitle; + return this; + } + + public NavigationGutterIconBuilder setEmptyPopupText(@NotNull String emptyText) { + myEmptyText = emptyText; + return this; + } + + public NavigationGutterIconBuilder setTooltipTitle(@NotNull final String tooltipTitle) { + myTooltipTitle = tooltipTitle; + return this; + } + + public NavigationGutterIconBuilder setNamer(@NotNull NullableFunction namer) { + myNamer = namer; + return this; + } + + public NavigationGutterIconBuilder setCellRenderer(@NotNull final PsiElementListCellRenderer cellRenderer) { + myCellRenderer = cellRenderer; + return this; + } + + @Nullable + public Annotation install(@NotNull DomElementAnnotationHolder holder, @Nullable DomElement element) { + if (!myLazy && myTargets.getValue().isEmpty() || element == null) return null; + return doInstall(holder.createAnnotation(element, HighlightSeverity.INFORMATION, null), element.getManager().getProject()); + } + + @Nullable + public Annotation install(@NotNull AnnotationHolder holder, @Nullable PsiElement element) { + if (!myLazy && myTargets.getValue().isEmpty() || element == null) return null; + return doInstall(holder.createInfoAnnotation(element, null), element.getProject()); + } + + private Annotation doInstall(final Annotation annotation, final Project project) { + final MyNavigationGutterIconRenderer renderer = createGutterIconRenderer(project); + annotation.setGutterIconRenderer(renderer); + annotation.setNeedsUpdateOnTyping(false); + return annotation; + } + + public LineMarkerInfo createLineMarkerInfo(PsiElement element) { + final MyNavigationGutterIconRenderer renderer = createGutterIconRenderer(element.getProject()); + return new LineMarkerInfo(element, element.getTextRange(), renderer.getIcon(), Pass.UPDATE_OVERRIDEN_MARKERS, new NullableFunction() { + public String fun(final PsiElement element) { + return renderer.getTooltipText(); + } + }, renderer, renderer.getAlignment()); + } + + private MyNavigationGutterIconRenderer createGutterIconRenderer(final Project project) { + final SmartPointerManager manager = SmartPointerManager.getInstance(project); + + NotNullLazyValue> pointers = new NotNullLazyValue>() { + @NotNull + public List compute() { + Set elements = new THashSet(); + final ArrayList list = new ArrayList(); + for (final T target : myTargets.getValue()) { + for (final PsiElement psiElement : myConvertor.fun(target)) { + if (elements.add(psiElement)) { + list.add(manager.createSmartPsiElementPointer(psiElement)); + } + } + } + return list; + } + }; + + if (!myLazy) { + pointers.getValue(); + } + + if (myTooltipText == null && !myLazy) { + final SortedSet names = new TreeSet(); + for (T t : myTargets.getValue()) { + final String text = myNamer.fun(t); + if (text != null) { + names.add(MessageFormat.format(PATTERN, text)); + } + } + @NonNls StringBuilder sb = new StringBuilder(""); + if (myTooltipTitle != null) { + sb.append(myTooltipTitle).append("
"); + } + for (String name : names) { + sb.append(name).append("
"); + } + sb.append(""); + myTooltipText = sb.toString(); + } + + if (myCellRenderer == null) { + myCellRenderer = new DefaultPsiElementCellRenderer(); + } + + final MyNavigationGutterIconRenderer renderer = new MyNavigationGutterIconRenderer(this, myAlignment, myIcon, myTooltipText, pointers); + return renderer; + } + + private static class MyNavigationGutterIconRenderer extends NavigationGutterIconRenderer { + private final Alignment myAlignment; + private final Icon myIcon; + private final String myTooltipText; + + public MyNavigationGutterIconRenderer(final NavigationGutterIconBuilder builder, final Alignment alignment, final Icon icon, final String tooltipText, final NotNullLazyValue> pointers) { + super(builder.myPopupTitle, builder.myEmptyText, builder.myCellRenderer, pointers); + myAlignment = alignment; + myIcon = icon; + myTooltipText = tooltipText; + } + + @NotNull + public Icon getIcon() { + return myIcon; + } + + @Nullable + public String getTooltipText() { + return myTooltipText; + } + + public Alignment getAlignment() { + return myAlignment; + } + + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + final MyNavigationGutterIconRenderer that = (MyNavigationGutterIconRenderer)o; + + if (myAlignment != that.myAlignment) return false; + if (myIcon != null ? !myIcon.equals(that.myIcon) : that.myIcon != null) return false; + if (myTooltipText != null ? !myTooltipText.equals(that.myTooltipText) : that.myTooltipText != null) return false; + + return true; + } + + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (myAlignment != null ? myAlignment.hashCode() : 0); + result = 31 * result + (myIcon != null ? myIcon.hashCode() : 0); + result = 31 * result + (myTooltipText != null ? myTooltipText.hashCode() : 0); + return result; + } + } +} diff --git a/xml/dom-openapi/src/com/intellij/codeInsight/navigation/NavigationGutterIconRenderer.java b/xml/dom-openapi/src/com/intellij/codeInsight/navigation/NavigationGutterIconRenderer.java new file mode 100644 index 0000000000..d1c3665eb4 --- /dev/null +++ b/xml/dom-openapi/src/com/intellij/codeInsight/navigation/NavigationGutterIconRenderer.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.navigation; + +import com.intellij.codeInsight.hint.HintUtil; +import com.intellij.codeInsight.daemon.GutterIconNavigationHandler; +import com.intellij.ide.util.PsiElementListCellRenderer; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.editor.markup.GutterIconRenderer; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.util.NotNullLazyValue; +import com.intellij.psi.PsiElement; +import com.intellij.psi.SmartPsiElementPointer; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.util.NullableFunction; +import com.intellij.util.PsiNavigateUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.event.MouseEvent; +import java.util.List; + +/** + * @author peter + */ +public abstract class NavigationGutterIconRenderer extends GutterIconRenderer implements GutterIconNavigationHandler{ + private final String myPopupTitle; + private final String myEmptyText; + private final PsiElementListCellRenderer myCellRenderer; + private final NotNullLazyValue> myPointers; + + protected NavigationGutterIconRenderer(final String popupTitle, final String emptyText, final PsiElementListCellRenderer cellRenderer, + final NotNullLazyValue> pointers) { + myPopupTitle = popupTitle; + myEmptyText = emptyText; + myCellRenderer = cellRenderer; + myPointers = pointers; + } + + public boolean isNavigateAction() { + return true; + } + + public List getTargetElements() { + return ContainerUtil.mapNotNull(myPointers.getValue(), new NullableFunction() { + public PsiElement fun(final SmartPsiElementPointer smartPsiElementPointer) { + return smartPsiElementPointer.getElement(); + } + }); + } + + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final NavigationGutterIconRenderer renderer = (NavigationGutterIconRenderer)o; + + if (myCellRenderer != null ? !myCellRenderer.equals(renderer.myCellRenderer) : renderer.myCellRenderer != null) return false; + if (myEmptyText != null ? !myEmptyText.equals(renderer.myEmptyText) : renderer.myEmptyText != null) return false; + if (!myPointers.equals(renderer.myPointers)) return false; + if (myPopupTitle != null ? !myPopupTitle.equals(renderer.myPopupTitle) : renderer.myPopupTitle != null) return false; + + return true; + } + + public int hashCode() { + int result; + result = (myPopupTitle != null ? myPopupTitle.hashCode() : 0); + result = 31 * result + (myEmptyText != null ? myEmptyText.hashCode() : 0); + result = 31 * result + (myCellRenderer != null ? myCellRenderer.hashCode() : 0); + result = 31 * result + myPointers.hashCode(); + return result; + } + + public PsiElementListCellRenderer getCellRenderer() { + return myCellRenderer; + } + + @Nullable + public AnAction getClickAction() { + return new AnAction() { + public void actionPerformed(AnActionEvent e) { + navigate(e == null ? null : (MouseEvent)e.getInputEvent(), null); + } + }; + } + + public void navigate(final MouseEvent event, final PsiElement elt) { + final List list = getTargetElements(); + if (list.isEmpty()) { + if (myEmptyText != null) { + final JLabel renderer = HintUtil.createErrorLabel(myEmptyText); + final JBPopup popup = JBPopupFactory.getInstance().createComponentPopupBuilder(renderer, renderer).createPopup(); + if (event != null) { + popup.show(new RelativePoint(event)); + } + } + return; + } + if (list.size() == 1) { + PsiNavigateUtil.navigate(list.iterator().next()); + } + else { + final JBPopup popup = NavigationUtil.getPsiElementPopup(list.toArray(new PsiElement[list.size()]), myCellRenderer, myPopupTitle); + if (event != null) { + popup.show(new RelativePoint(event)); + } + } + } +} -- 2.11.4.GIT