| /* |
| * Copyright 2000-2013 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 org.jetbrains.plugins.javaFX.fxml.codeInsight; |
| |
| import com.intellij.codeHighlighting.Pass; |
| import com.intellij.codeInsight.daemon.GutterIconNavigationHandler; |
| import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo; |
| import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider; |
| import com.intellij.codeInsight.navigation.NavigationUtil; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.navigation.GotoRelatedItem; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.markup.GutterIconRenderer; |
| import com.intellij.openapi.ui.popup.JBPopup; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.search.searches.ReferencesSearch; |
| import com.intellij.psi.xml.XmlAttribute; |
| import com.intellij.psi.xml.XmlAttributeValue; |
| import com.intellij.ui.awt.RelativePoint; |
| import com.intellij.util.Function; |
| import com.intellij.util.Processor; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.plugins.javaFX.JavaFxControllerClassIndex; |
| import org.jetbrains.plugins.javaFX.fxml.FxmlConstants; |
| import org.jetbrains.plugins.javaFX.fxml.JavaFxFileTypeFactory; |
| import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; |
| |
| import java.awt.event.MouseEvent; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| /** |
| * User: anna |
| */ |
| public class JavaFxRelatedItemLineMarkerProvider extends RelatedItemLineMarkerProvider { |
| private static final Logger LOG = Logger.getInstance("#" + JavaFxRelatedItemLineMarkerProvider.class.getName()); |
| |
| @Override |
| protected void collectNavigationMarkers(@NotNull PsiElement element, final Collection<? super RelatedItemLineMarkerInfo> result) { |
| if (element instanceof PsiField) { |
| final PsiField field = (PsiField)element; |
| if (JavaFxPsiUtil.isVisibleInFxml(field) && !field.hasModifierProperty(PsiModifier.STATIC) && !field.hasModifierProperty(PsiModifier.FINAL)) { |
| final PsiClass containingClass = field.getContainingClass(); |
| if (containingClass != null && containingClass.hasModifierProperty(PsiModifier.PUBLIC) && containingClass.getQualifiedName() != null) { |
| final PsiMethod[] constructors = containingClass.getConstructors(); |
| boolean defaultConstructor = constructors.length == 0; |
| for (PsiMethod constructor : constructors) { |
| if (constructor.getParameterList().getParametersCount() == 0) { |
| defaultConstructor = true; |
| break; |
| } |
| } |
| if (!defaultConstructor) return; |
| final ArrayList<GotoRelatedItem> targets = new ArrayList<GotoRelatedItem>(); |
| collectTargets(field, targets, new Function<PsiElement, GotoRelatedItem>() { |
| @Override |
| public GotoRelatedItem fun(PsiElement element) { |
| return new GotoRelatedItem(element); |
| } |
| }, true); |
| if (targets.isEmpty()) return; |
| |
| result.add(new RelatedItemLineMarkerInfo<PsiField>(field, field.getNameIdentifier().getTextRange(), |
| AllIcons.FileTypes.Xml, Pass.UPDATE_OVERRIDEN_MARKERS, null, |
| new JavaFXIdIconNavigationHandler(), GutterIconRenderer.Alignment.LEFT, |
| targets)); |
| } |
| } |
| } |
| } |
| |
| private static <T> void collectTargets(PsiField field, final ArrayList<T> targets, final Function<PsiElement, T> fun, final boolean stopAtFirst) { |
| final PsiClass containingClass = field.getContainingClass(); |
| LOG.assertTrue(containingClass != null); |
| final String qualifiedName = containingClass.getQualifiedName(); |
| LOG.assertTrue(qualifiedName != null); |
| final List<VirtualFile> fxmls = JavaFxControllerClassIndex.findFxmlsWithController(field.getProject(), qualifiedName); |
| if (fxmls.isEmpty()) return; |
| ReferencesSearch.search(field, GlobalSearchScope.filesScope(field.getProject(), fxmls)).forEach( |
| new Processor<PsiReference>() { |
| @Override |
| public boolean process(PsiReference reference) { |
| final PsiElement referenceElement = reference.getElement(); |
| if (referenceElement == null) return true; |
| final PsiFile containingFile = referenceElement.getContainingFile(); |
| if (containingFile == null) return true; |
| if (!JavaFxFileTypeFactory.isFxml(containingFile)) return true; |
| if (!(referenceElement instanceof XmlAttributeValue)) return true; |
| final XmlAttributeValue attributeValue = (XmlAttributeValue)referenceElement; |
| final PsiElement parent = attributeValue.getParent(); |
| if (!(parent instanceof XmlAttribute)) return true; |
| if (!FxmlConstants.FX_ID.equals(((XmlAttribute)parent).getName())) return true; |
| targets.add(fun.fun(parent)); |
| return !stopAtFirst; |
| } |
| }); |
| } |
| |
| private static class JavaFXIdIconNavigationHandler implements GutterIconNavigationHandler<PsiField> { |
| @Override |
| public void navigate(MouseEvent e, PsiField field) { |
| final ArrayList<PsiElement> relatedItems = new ArrayList<PsiElement>(); |
| collectTargets(field, relatedItems, Function.ID, false); |
| if (relatedItems.size() == 1) { |
| NavigationUtil.activateFileWithPsiElement(relatedItems.get(0)); |
| return; |
| } |
| final JBPopup popup = NavigationUtil |
| .getPsiElementPopup(relatedItems.toArray(new PsiElement[relatedItems.size()]), "<html>Choose component with fx:id <b>" + field.getName() + "<b></html>"); |
| popup.show(new RelativePoint(e)); |
| } |
| } |
| } |