2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com
.intellij
.uiDesigner
.designSurface
;
19 import com
.intellij
.ide
.DataManager
;
20 import com
.intellij
.openapi
.actionSystem
.AnAction
;
21 import com
.intellij
.openapi
.actionSystem
.AnActionEvent
;
22 import com
.intellij
.openapi
.actionSystem
.DataContext
;
23 import com
.intellij
.openapi
.actionSystem
.DefaultActionGroup
;
24 import com
.intellij
.openapi
.diagnostic
.Logger
;
25 import com
.intellij
.openapi
.ui
.popup
.JBPopupFactory
;
26 import com
.intellij
.openapi
.ui
.popup
.ListPopup
;
27 import com
.intellij
.openapi
.util
.Comparing
;
28 import com
.intellij
.openapi
.util
.IconLoader
;
29 import com
.intellij
.pom
.Navigatable
;
30 import com
.intellij
.psi
.*;
31 import com
.intellij
.psi
.controlFlow
.DefUseUtil
;
32 import com
.intellij
.psi
.presentation
.java
.ClassPresentationUtil
;
33 import com
.intellij
.psi
.search
.LocalSearchScope
;
34 import com
.intellij
.psi
.search
.searches
.ReferencesSearch
;
35 import com
.intellij
.psi
.util
.PsiTreeUtil
;
36 import com
.intellij
.uiDesigner
.FormEditingUtil
;
37 import com
.intellij
.uiDesigner
.UIDesignerBundle
;
38 import com
.intellij
.uiDesigner
.lw
.IRootContainer
;
39 import com
.intellij
.uiDesigner
.radComponents
.RadComponent
;
40 import com
.intellij
.util
.Processor
;
41 import org
.jetbrains
.annotations
.Nullable
;
45 import java
.awt
.event
.ActionEvent
;
46 import java
.awt
.event
.ActionListener
;
47 import java
.beans
.BeanInfo
;
48 import java
.beans
.EventSetDescriptor
;
49 import java
.beans
.IntrospectionException
;
50 import java
.beans
.Introspector
;
55 public class ListenerNavigateButton
extends JButton
implements ActionListener
{
56 private static final Logger LOG
= Logger
.getInstance("#com.intellij.uiDesigner.designSurface.ListenerNavigateButton");
58 private final RadComponent myComponent
;
60 public ListenerNavigateButton(RadComponent component
) {
61 myComponent
= component
;
62 setIcon(IconLoader
.getIcon("/com/intellij/uiDesigner/icons/listener.png"));
65 setBorderPainted(false);
66 setSize(new Dimension(getIcon().getIconWidth(), getIcon().getIconHeight()));
67 addActionListener(this);
70 public void actionPerformed(ActionEvent e
) {
71 showNavigatePopup(myComponent
, false);
74 public static void showNavigatePopup(final RadComponent component
, final boolean showIfEmpty
) {
75 final DefaultActionGroup actionGroup
= prepareActionGroup(component
);
76 if (actionGroup
!= null && actionGroup
.getChildrenCount() == 0 && showIfEmpty
) {
77 actionGroup
.add(new MyNavigateAction(UIDesignerBundle
.message("navigate.to.listener.empty"), null));
79 if (actionGroup
!= null && actionGroup
.getChildrenCount() > 0) {
80 final DataContext context
= DataManager
.getInstance().getDataContext(component
.getDelegee());
81 final JBPopupFactory factory
= JBPopupFactory
.getInstance();
82 final ListPopup popup
= factory
.createActionGroupPopup(UIDesignerBundle
.message("navigate.to.listener.title"), actionGroup
, context
,
83 JBPopupFactory
.ActionSelectionAid
.NUMBERING
, true);
84 FormEditingUtil
.showPopupUnderComponent(popup
, component
);
89 public static DefaultActionGroup
prepareActionGroup(final RadComponent component
) {
90 final IRootContainer root
= FormEditingUtil
.getRoot(component
);
91 final String classToBind
= root
== null ?
null : root
.getClassToBind();
92 if (classToBind
!= null) {
93 final PsiClass aClass
= FormEditingUtil
.findClassToBind(component
.getModule(), classToBind
);
95 final PsiField boundField
= aClass
.findFieldByName(component
.getBinding(), false);
96 if (boundField
!= null) {
97 return buildNavigateActionGroup(component
, boundField
);
104 private static DefaultActionGroup
buildNavigateActionGroup(RadComponent component
, final PsiField boundField
) {
105 final DefaultActionGroup actionGroup
= new DefaultActionGroup();
106 final EventSetDescriptor
[] eventSetDescriptors
;
108 BeanInfo beanInfo
= Introspector
.getBeanInfo(component
.getComponentClass());
109 eventSetDescriptors
= beanInfo
.getEventSetDescriptors();
111 catch (IntrospectionException e
) {
115 final LocalSearchScope scope
= new LocalSearchScope(boundField
.getContainingFile());
116 ReferencesSearch
.search(boundField
, scope
).forEach(new Processor
<PsiReference
>() {
117 public boolean process(final PsiReference ref
) {
118 final PsiElement element
= ref
.getElement();
119 if (element
.getParent() instanceof PsiReferenceExpression
) {
120 PsiReferenceExpression refExpr
= (PsiReferenceExpression
) element
.getParent();
121 if (refExpr
.getParent() instanceof PsiMethodCallExpression
) {
122 PsiMethodCallExpression methodCall
= (PsiMethodCallExpression
) refExpr
.getParent();
123 final PsiElement psiElement
= refExpr
.resolve();
124 if (psiElement
instanceof PsiMethod
) {
125 PsiMethod method
= (PsiMethod
) psiElement
;
126 for(EventSetDescriptor eventSetDescriptor
: eventSetDescriptors
) {
127 if (Comparing
.equal(eventSetDescriptor
.getAddListenerMethod().getName(), method
.getName())) {
128 final String eventName
= eventSetDescriptor
.getName();
129 final PsiExpression
[] args
= methodCall
.getArgumentList().getExpressions();
130 if (args
.length
> 0) {
131 addListenerRef(actionGroup
, eventName
, args
[0]);
145 private static void addListenerRef(final DefaultActionGroup actionGroup
, final String eventName
, final PsiExpression listenerArg
) {
146 final PsiType type
= listenerArg
.getType();
147 if (type
instanceof PsiClassType
) {
148 PsiClass listenerClass
= ((PsiClassType
) type
).resolve();
149 if (listenerClass
!= null) {
150 if (!isAbstractOrInterface(listenerClass
)) {
151 actionGroup
.add(new MyNavigateAction(eventName
+ ": " + ClassPresentationUtil
.getNameForClass(listenerClass
, false),
155 else if (listenerArg
instanceof PsiReferenceExpression
) {
156 final PsiElement psiElement
= ((PsiReferenceExpression
)listenerArg
).resolve();
157 if (psiElement
instanceof PsiVariable
) {
158 PsiCodeBlock codeBlock
= PsiTreeUtil
.getParentOfType(listenerArg
, PsiCodeBlock
.class);
159 final PsiElement
[] defs
= DefUseUtil
.getDefs(codeBlock
, (PsiVariable
)psiElement
, listenerArg
);
160 if (defs
.length
== 1) {
161 final PsiElement def
= defs
[0];
162 if (def
instanceof PsiVariable
) {
163 PsiVariable var
= (PsiVariable
) def
;
164 if (var
.getInitializer() != listenerArg
) {
165 addListenerRef(actionGroup
, eventName
, var
.getInitializer());
169 else if (def
.getParent() instanceof PsiAssignmentExpression
) {
170 final PsiAssignmentExpression assignmentExpr
= (PsiAssignmentExpression
)def
.getParent();
171 if (def
.equals(assignmentExpr
.getLExpression())) {
172 addListenerRef(actionGroup
, eventName
, assignmentExpr
.getRExpression());
181 actionGroup
.add(new MyNavigateAction(eventName
+ ": " + listenerArg
.getText(), listenerArg
));
184 private static boolean isAbstractOrInterface(final PsiClass element
) {
185 return element
.isInterface() ||
186 element
.hasModifierProperty(PsiModifier
.ABSTRACT
);
189 private static class MyNavigateAction
extends AnAction
{
190 private final PsiElement myElement
;
192 public MyNavigateAction(final String name
, PsiElement element
) {
197 public void actionPerformed(AnActionEvent e
) {
198 if (myElement
instanceof Navigatable
) {
199 ((Navigatable
) myElement
).navigate(true);
203 @Override public void update(AnActionEvent e
) {
204 e
.getPresentation().setEnabled(myElement
!= null &&
205 (!(myElement
instanceof PsiClass
) || !isAbstractOrInterface((PsiClass
)myElement
)));