better painting for bread crumbs
[idea/community.git] / plugins / ByteCodeViewer / src / com / intellij / byteCodeViewer / ShowByteCodeAction.java
1 /*
2  * Copyright 2000-2013 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.byteCodeViewer;
17
18 import com.intellij.codeInsight.documentation.DocumentationManager;
19 import com.intellij.codeInsight.lookup.LookupManager;
20 import com.intellij.icons.AllIcons;
21 import com.intellij.openapi.actionSystem.AnAction;
22 import com.intellij.openapi.actionSystem.AnActionEvent;
23 import com.intellij.openapi.actionSystem.CommonDataKeys;
24 import com.intellij.openapi.actionSystem.DataContext;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.compiler.CompilerManager;
27 import com.intellij.openapi.editor.Editor;
28 import com.intellij.openapi.progress.ProgressIndicator;
29 import com.intellij.openapi.progress.ProgressManager;
30 import com.intellij.openapi.progress.Task;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.roots.ProjectRootManager;
33 import com.intellij.openapi.ui.Messages;
34 import com.intellij.openapi.ui.popup.JBPopup;
35 import com.intellij.openapi.ui.popup.JBPopupFactory;
36 import com.intellij.openapi.util.Computable;
37 import com.intellij.openapi.util.Disposer;
38 import com.intellij.openapi.vfs.VirtualFile;
39 import com.intellij.psi.*;
40 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
41 import com.intellij.psi.util.PsiUtilBase;
42 import com.intellij.psi.util.PsiUtilCore;
43 import com.intellij.ui.awt.RelativePoint;
44 import com.intellij.util.Processor;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
47
48 /**
49  * @author anna
50  * @since 5/4/12
51  */
52 public class ShowByteCodeAction extends AnAction {
53   @Override
54   public void update(AnActionEvent e) {
55     e.getPresentation().setEnabled(false);
56     e.getPresentation().setIcon(AllIcons.Toolwindows.Documentation);
57     final Project project = e.getData(CommonDataKeys.PROJECT);
58     if (project != null) {
59       final PsiElement psiElement = getPsiElement(e.getDataContext(), project, e.getData(CommonDataKeys.EDITOR));
60       if (psiElement != null) {
61         if (psiElement.getContainingFile() instanceof PsiClassOwner &&
62             ByteCodeViewerManager.getContainingClass(psiElement) != null) {
63           e.getPresentation().setEnabled(true);
64         }
65       }
66     }
67   }
68
69   @Override
70   public void actionPerformed(AnActionEvent e) {
71     final DataContext dataContext = e.getDataContext();
72     final Project project = CommonDataKeys.PROJECT.getData(dataContext);
73     if (project == null) return;
74     final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
75
76     final PsiElement psiElement = getPsiElement(dataContext, project, editor);
77     if (psiElement == null) return;
78
79     final String psiElementTitle = ByteCodeViewerManager.getInstance(project).getTitle(psiElement);
80
81     final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(psiElement);
82     if (virtualFile == null) return;
83
84     final RelativePoint bestPopupLocation = JBPopupFactory.getInstance().guessBestPopupLocation(dataContext);
85
86     final SmartPsiElementPointer element = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(psiElement);
87     ProgressManager.getInstance().run(new Task.Backgroundable(project, "Searching byte code...") {
88       private String myByteCode;
89       private String myErrorMessage;
90       private String myErrorTitle;
91
92       @Override
93       public void run(@NotNull ProgressIndicator indicator) {
94         if (ProjectRootManager.getInstance(project).getFileIndex().isInContent(virtualFile) && isMarkedForCompilation(project, virtualFile)) {
95           myErrorMessage = "Unable to show byte code for '" + psiElementTitle + "'. Class file does not exist or is out-of-date.";
96           myErrorTitle = "Class File Out-Of-Date";
97         }
98         else {
99           myByteCode = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
100             @Override
101             public String compute() {
102               return ByteCodeViewerManager.getByteCode(psiElement);
103             }
104           });
105         }
106       }
107
108       @Override
109       public void onSuccess() {
110         if (project.isDisposed()) return;
111
112         if (myErrorMessage != null && myTitle != null) {
113           Messages.showWarningDialog(project, myErrorMessage, myErrorTitle);
114           return;
115         }
116         final PsiElement targetElement = element.getElement();
117         if (targetElement == null) return;
118
119         final ByteCodeViewerManager codeViewerManager = ByteCodeViewerManager.getInstance(project);
120         if (codeViewerManager.hasActiveDockedDocWindow()) {
121           codeViewerManager.doUpdateComponent(targetElement, myByteCode);
122         }
123         else {
124           if (myByteCode == null) {
125             Messages.showErrorDialog(project, "Unable to parse class file for '" + psiElementTitle + "'.", "Byte Code not Found");
126             return;
127           }
128           final ByteCodeViewerComponent component = new ByteCodeViewerComponent(project, null);
129           component.setText(myByteCode, targetElement);
130           Processor<JBPopup> pinCallback = new Processor<JBPopup>() {
131             @Override
132             public boolean process(JBPopup popup) {
133               codeViewerManager.recreateToolWindow(targetElement, targetElement);
134               popup.cancel();
135               return false;
136             }
137           };
138
139           final JBPopup popup = JBPopupFactory.getInstance().createComponentPopupBuilder(component, null)
140             .setProject(project)
141             .setDimensionServiceKey(project, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false)
142             .setResizable(true)
143             .setMovable(true)
144             .setRequestFocus(LookupManager.getActiveLookup(editor) == null)
145             .setTitle(psiElementTitle + " Bytecode")
146             .setCouldPin(pinCallback)
147             .createPopup();
148           Disposer.register(popup, component);
149
150           if (editor != null) {
151             popup.showInBestPositionFor(editor);
152           } else {
153             popup.show(bestPopupLocation);
154           }
155         }
156       }
157     });
158   }
159
160   private static boolean isMarkedForCompilation(Project project, VirtualFile virtualFile) {
161     final CompilerManager compilerManager = CompilerManager.getInstance(project);
162     return !compilerManager.isUpToDate(compilerManager.createFilesCompileScope(new VirtualFile[]{virtualFile}));
163   }
164
165   @Nullable
166   private static PsiElement getPsiElement(DataContext dataContext, Project project, Editor editor) {
167     PsiElement psiElement = null;
168     if (editor == null) {
169       psiElement = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
170     } else {
171       final PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, project);
172       final Editor injectedEditor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(editor, file);
173       if (injectedEditor != null) {
174         PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(injectedEditor, project);
175         psiElement = psiFile != null ? psiFile.findElementAt(injectedEditor.getCaretModel().getOffset()) : null;
176       }
177
178       if (file != null && psiElement == null) {
179         psiElement = file.findElementAt(editor.getCaretModel().getOffset());
180       }
181     }
182
183     return psiElement;
184   }
185 }