blob: 31d9276b8b70e1e892a60abbf0fe278e78c9dc3d [file] [log] [blame]
/*
* Copyright 2000-2009 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.uiDesigner.designSurface;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.Comparing;
import com.intellij.pom.Navigatable;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.DefUseUtil;
import com.intellij.psi.presentation.java.ClassPresentationUtil;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.uiDesigner.FormEditingUtil;
import com.intellij.uiDesigner.UIDesignerBundle;
import com.intellij.uiDesigner.lw.IRootContainer;
import com.intellij.uiDesigner.radComponents.RadComponent;
import com.intellij.util.Processor;
import icons.UIDesignerIcons;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
/**
* @author yole
*/
public class ListenerNavigateButton extends JButton implements ActionListener {
private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.designSurface.ListenerNavigateButton");
private final RadComponent myComponent;
public ListenerNavigateButton(RadComponent component) {
myComponent = component;
setIcon(UIDesignerIcons.Listener);
setOpaque(false);
setFocusable(false);
setBorderPainted(false);
setSize(new Dimension(getIcon().getIconWidth(), getIcon().getIconHeight()));
addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
showNavigatePopup(myComponent, false);
}
public static void showNavigatePopup(final RadComponent component, final boolean showIfEmpty) {
final DefaultActionGroup actionGroup = prepareActionGroup(component);
if (actionGroup != null && actionGroup.getChildrenCount() == 0 && showIfEmpty) {
actionGroup.add(new MyNavigateAction(UIDesignerBundle.message("navigate.to.listener.empty"), null));
}
if (actionGroup != null && actionGroup.getChildrenCount() > 0) {
final DataContext context = DataManager.getInstance().getDataContext(component.getDelegee());
final JBPopupFactory factory = JBPopupFactory.getInstance();
final ListPopup popup = factory.createActionGroupPopup(UIDesignerBundle.message("navigate.to.listener.title"), actionGroup, context,
JBPopupFactory.ActionSelectionAid.NUMBERING, true);
FormEditingUtil.showPopupUnderComponent(popup, component);
}
}
@Nullable
public static DefaultActionGroup prepareActionGroup(final RadComponent component) {
final IRootContainer root = FormEditingUtil.getRoot(component);
final String classToBind = root == null ? null : root.getClassToBind();
if (classToBind != null) {
final PsiClass aClass = FormEditingUtil.findClassToBind(component.getModule(), classToBind);
if (aClass != null) {
final PsiField boundField = aClass.findFieldByName(component.getBinding(), false);
if (boundField != null) {
return buildNavigateActionGroup(component, boundField);
}
}
}
return null;
}
private static DefaultActionGroup buildNavigateActionGroup(RadComponent component, final PsiField boundField) {
final DefaultActionGroup actionGroup = new DefaultActionGroup();
final EventSetDescriptor[] eventSetDescriptors;
try {
BeanInfo beanInfo = Introspector.getBeanInfo(component.getComponentClass());
eventSetDescriptors = beanInfo.getEventSetDescriptors();
}
catch (IntrospectionException e) {
LOG.error(e);
return null;
}
final LocalSearchScope scope = new LocalSearchScope(boundField.getContainingFile());
ReferencesSearch.search(boundField, scope).forEach(new Processor<PsiReference>() {
public boolean process(final PsiReference ref) {
final PsiElement element = ref.getElement();
if (element.getParent() instanceof PsiReferenceExpression) {
PsiReferenceExpression refExpr = (PsiReferenceExpression) element.getParent();
if (refExpr.getParent() instanceof PsiMethodCallExpression) {
PsiMethodCallExpression methodCall = (PsiMethodCallExpression) refExpr.getParent();
final PsiElement psiElement = refExpr.resolve();
if (psiElement instanceof PsiMethod) {
PsiMethod method = (PsiMethod) psiElement;
for(EventSetDescriptor eventSetDescriptor: eventSetDescriptors) {
if (Comparing.equal(eventSetDescriptor.getAddListenerMethod().getName(), method.getName())) {
final String eventName = eventSetDescriptor.getName();
final PsiExpression[] args = methodCall.getArgumentList().getExpressions();
if (args.length > 0) {
addListenerRef(actionGroup, eventName, args[0]);
}
}
}
}
}
}
return true;
}
});
return actionGroup;
}
private static void addListenerRef(final DefaultActionGroup actionGroup, final String eventName, final PsiExpression listenerArg) {
final PsiType type = listenerArg.getType();
if (type instanceof PsiClassType) {
PsiClass listenerClass = ((PsiClassType) type).resolve();
if (listenerClass != null) {
if (!isAbstractOrInterface(listenerClass)) {
actionGroup.add(new MyNavigateAction(eventName + ": " + ClassPresentationUtil.getNameForClass(listenerClass, false),
listenerClass));
return;
}
else if (listenerArg instanceof PsiReferenceExpression) {
final PsiElement psiElement = ((PsiReferenceExpression)listenerArg).resolve();
if (psiElement instanceof PsiVariable) {
PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(listenerArg, PsiCodeBlock.class);
final PsiElement[] defs = DefUseUtil.getDefs(codeBlock, (PsiVariable)psiElement, listenerArg);
if (defs.length == 1) {
final PsiElement def = defs[0];
if (def instanceof PsiVariable) {
PsiVariable var = (PsiVariable) def;
if (var.getInitializer() != listenerArg) {
addListenerRef(actionGroup, eventName, var.getInitializer());
return;
}
}
else if (def.getParent() instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression assignmentExpr = (PsiAssignmentExpression)def.getParent();
if (def.equals(assignmentExpr.getLExpression())) {
addListenerRef(actionGroup, eventName, assignmentExpr.getRExpression());
return;
}
}
}
}
}
}
}
actionGroup.add(new MyNavigateAction(eventName + ": " + listenerArg.getText(), listenerArg));
}
private static boolean isAbstractOrInterface(final PsiClass element) {
return element.isInterface() ||
element.hasModifierProperty(PsiModifier.ABSTRACT);
}
private static class MyNavigateAction extends AnAction {
private final PsiElement myElement;
public MyNavigateAction(final String name, PsiElement element) {
super(name);
myElement = element;
}
public void actionPerformed(AnActionEvent e) {
if (myElement instanceof Navigatable) {
((Navigatable) myElement).navigate(true);
}
}
@Override public void update(AnActionEvent e) {
e.getPresentation().setEnabled(myElement != null &&
(!(myElement instanceof PsiClass) || !isAbstractOrInterface((PsiClass)myElement)));
}
}
}