NavigationGutterIconBuilder
authorsergey.vasiliev <sergey.vasiliev@jetbrains.com>
Tue, 16 Feb 2010 07:57:45 +0000 (16 10:57 +0300)
committersergey.vasiliev <sergey.vasiliev@jetbrains.com>
Tue, 16 Feb 2010 08:00:47 +0000 (16 11:00 +0300)
xml/dom-openapi/src/com/intellij/codeInsight/navigation/NavigationGutterIconBuilder.java [new file with mode: 0644]
xml/dom-openapi/src/com/intellij/codeInsight/navigation/NavigationGutterIconRenderer.java [new file with mode: 0644]

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 (file)
index 0000000..5066558
--- /dev/null
@@ -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<T> {
+  @NonNls private static final String PATTERN = "&nbsp;&nbsp;&nbsp;&nbsp;{0}";
+  private static final NotNullFunction<PsiElement,Collection<? extends PsiElement>> DEFAULT_PSI_CONVERTOR = new NotNullFunction<PsiElement, Collection<? extends PsiElement>>() {
+    @NotNull
+    public Collection<? extends PsiElement> fun(final PsiElement element) {
+      return ContainerUtil.createMaybeSingletonList(element);
+    }
+  };
+
+  private final Icon myIcon;
+  private final NotNullFunction<T,Collection<? extends PsiElement>> myConvertor;
+
+  private NotNullLazyValue<Collection<? extends T>> 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<T,String> myNamer = (NullableFunction<T, String>)ElementPresentationManager.NAMER;
+  public static final NotNullFunction<DomElement,Collection<? extends PsiElement>> DEFAULT_DOM_CONVERTOR = new NotNullFunction<DomElement, Collection<? extends PsiElement>>() {
+    @NotNull
+    public Collection<? extends PsiElement> fun(final DomElement o) {
+      return ContainerUtil.createMaybeSingletonList(o.getXmlElement());
+    }
+  };
+
+  protected NavigationGutterIconBuilder(@NotNull final Icon icon, NotNullFunction<T,Collection<? extends PsiElement>> convertor) {
+    myIcon = icon;
+    myConvertor = convertor;
+  }
+
+  public static NavigationGutterIconBuilder<PsiElement> create(@NotNull final Icon icon) {
+    return create(icon, DEFAULT_PSI_CONVERTOR);
+  }
+
+  public static <T> NavigationGutterIconBuilder<T> create(@NotNull final Icon icon, NotNullFunction<T, Collection<? extends PsiElement>> convertor) {
+    return new NavigationGutterIconBuilder<T>(icon, convertor);
+  }
+
+  public NavigationGutterIconBuilder<T> setTarget(@Nullable T target) {
+    return setTargets(ContainerUtil.createMaybeSingletonList(target));
+  }
+
+  public NavigationGutterIconBuilder<T> setTargets(@NotNull T... targets) {
+    return setTargets(Arrays.asList(targets));
+  }
+
+  public NavigationGutterIconBuilder<T> setTargets(@NotNull final NotNullLazyValue<Collection<? extends T>> targets) {
+    myTargets = targets;
+    myLazy = true;
+    return this;
+  }
+
+  public NavigationGutterIconBuilder<T> setTargets(@NotNull final Collection<? extends T> targets) {
+    myTargets = new NotNullLazyValue<Collection<? extends T>>() {
+      @NotNull
+      public Collection<? extends T> compute() {
+        return targets;
+      }
+    };
+    return this;
+  }
+
+  public NavigationGutterIconBuilder<T> setTooltipText(@NotNull String tooltipText) {
+    myTooltipText = tooltipText;
+    return this;
+  }
+
+  public NavigationGutterIconBuilder<T> setAlignment(@NotNull final GutterIconRenderer.Alignment alignment) {
+    myAlignment = alignment;
+    return this;
+  }
+
+  public NavigationGutterIconBuilder<T> setPopupTitle(@NotNull String popupTitle) {
+    myPopupTitle = popupTitle;
+    return this;
+  }
+
+  public NavigationGutterIconBuilder<T> setEmptyPopupText(@NotNull String emptyText) {
+    myEmptyText = emptyText;
+    return this;
+  }
+
+  public NavigationGutterIconBuilder<T> setTooltipTitle(@NotNull final String tooltipTitle) {
+    myTooltipTitle = tooltipTitle;
+    return this;
+  }
+
+  public NavigationGutterIconBuilder<T> setNamer(@NotNull NullableFunction<T,String> namer) {
+    myNamer = namer;
+    return this;
+  }
+
+  public NavigationGutterIconBuilder<T> 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<PsiElement>(element, element.getTextRange(), renderer.getIcon(), Pass.UPDATE_OVERRIDEN_MARKERS, new NullableFunction<PsiElement, String>() {
+      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<List<SmartPsiElementPointer>> pointers = new NotNullLazyValue<List<SmartPsiElementPointer>>() {
+      @NotNull
+      public List<SmartPsiElementPointer> compute() {
+        Set<PsiElement> elements = new THashSet<PsiElement>();
+        final ArrayList<SmartPsiElementPointer> list = new ArrayList<SmartPsiElementPointer>();
+        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<String> names = new TreeSet<String>();
+      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("<html><body>");
+      if (myTooltipTitle != null) {
+        sb.append(myTooltipTitle).append("<br>");
+      }
+      for (String name : names) {
+        sb.append(name).append("<br>");
+      }
+      sb.append("</body></html>");
+      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<List<SmartPsiElementPointer>> 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 (file)
index 0000000..d1c3665
--- /dev/null
@@ -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<PsiElement>{
+  private final String myPopupTitle;
+  private final String myEmptyText;
+  private final PsiElementListCellRenderer myCellRenderer;
+  private final NotNullLazyValue<List<SmartPsiElementPointer>> myPointers;
+
+  protected NavigationGutterIconRenderer(final String popupTitle, final String emptyText, final PsiElementListCellRenderer cellRenderer,
+                                         final NotNullLazyValue<List<SmartPsiElementPointer>> pointers) {
+    myPopupTitle = popupTitle;
+    myEmptyText = emptyText;
+    myCellRenderer = cellRenderer;
+    myPointers = pointers;
+  }
+
+  public boolean isNavigateAction() {
+    return true;
+  }
+
+  public List<PsiElement> getTargetElements() {
+    return ContainerUtil.mapNotNull(myPointers.getValue(), new NullableFunction<SmartPsiElementPointer, PsiElement>() {
+      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<PsiElement> 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));
+      }
+    }
+  }
+}