diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java new file mode 100644 index 000000000000..64d195a51b97 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DockablePopupManager.java @@ -0,0 +1,228 @@ +/* + * Copyright 2000-2012 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.documentation; + +import com.intellij.ide.DataManager; +import com.intellij.ide.IdeEventQueue; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.AsyncResult; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowAnchor; +import com.intellij.openapi.wm.ToolWindowType; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.openapi.wm.ex.ToolWindowManagerEx; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; +import com.intellij.psi.util.PsiUtilBase; +import com.intellij.ui.content.*; +import com.intellij.util.ui.update.Activatable; +import com.intellij.util.ui.update.UiNotifyConnector; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; + +/** + * User: anna + * Date: 5/7/12 + */ +public abstract class DockablePopupManager { + protected ToolWindow myToolWindow = null; + protected boolean myAutoUpdateDocumentation = PropertiesComponent.getInstance().isTrueValue(getAutoUpdateEnabledProperty()); + protected Runnable myAutoUpdateRequest; + protected final Project myProject; + + public DockablePopupManager(Project project) { + myProject = project; + } + + protected abstract String getShowInToolWindowProperty(); + protected abstract String getAutoUpdateEnabledProperty(); + + protected abstract String getAutoUpdateTitle(); + protected abstract String getRestorePopupDescription(); + protected abstract String getAutoUpdateDescription(); + + protected abstract T createComponent(); + protected abstract void doUpdateComponent(PsiElement element, PsiElement originalElement, T component); + protected abstract void doUpdateComponent(Editor injectedEditor, PsiFile psiFile); + protected abstract void doUpdateComponent(@NotNull PsiElement element); + + protected abstract String getTitle(PsiElement element); + protected abstract String getToolwindowId(); + + + + public void createToolWindow(final PsiElement element, PsiElement originalElement) { + assert myToolWindow == null; + + final T component = createComponent(); + + final ToolWindowManagerEx toolWindowManagerEx = ToolWindowManagerEx.getInstanceEx(myProject); + final ToolWindow toolWindow = toolWindowManagerEx.getToolWindow(getToolwindowId()); + myToolWindow = toolWindow == null + ? toolWindowManagerEx.registerToolWindow(getToolwindowId(), true, ToolWindowAnchor.RIGHT, myProject) + : toolWindow; + myToolWindow.setIcon(IconLoader.getIcon("/general/documentation.png")); + + myToolWindow.setAvailable(true, null); + myToolWindow.setToHideOnEmptyContent(false); + myToolWindow.setAutoHide(false); + + final Rectangle rectangle = WindowManager.getInstance().getIdeFrame(myProject).suggestChildFrameBounds(); + myToolWindow.setDefaultState(ToolWindowAnchor.RIGHT, ToolWindowType.FLOATING, rectangle); + + final ContentManager contentManager = myToolWindow.getContentManager(); + final ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); + final Content content = contentFactory.createContent(component, getTitle(element), false); + contentManager.addContent(content); + + contentManager.addContentManagerListener(new ContentManagerAdapter() { + @Override + public void contentRemoved(ContentManagerEvent event) { + if (contentManager.getContentCount() == 0) { + final JComponent c = event.getContent().getComponent(); + if (c instanceof Disposable) { + Disposer.dispose((Disposable)c); + } + + restorePopupBehavior(); + } + } + }); + + new UiNotifyConnector(component, new Activatable() { + public void showNotify() { + restartAutoUpdate(myAutoUpdateDocumentation); + } + + public void hideNotify() { + restartAutoUpdate(false); + } + }); + + myToolWindow.show(null); + PropertiesComponent.getInstance().setValue(getShowInToolWindowProperty(), Boolean.TRUE.toString()); + restartAutoUpdate(PropertiesComponent.getInstance().getBoolean(getAutoUpdateEnabledProperty(), true)); + doUpdateComponent(element, originalElement, component); + } + + + protected AnAction[] createActions() { + return new AnAction[]{ + new ToggleAction(getAutoUpdateTitle(), getAutoUpdateDescription(), + IconLoader.getIcon("/general/autoscrollFromSource.png")) { + @Override + public boolean isSelected(AnActionEvent e) { + return myAutoUpdateDocumentation; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + PropertiesComponent.getInstance().setValue(getAutoUpdateEnabledProperty(), Boolean.TRUE.toString()); + myAutoUpdateDocumentation = state; + restartAutoUpdate(state); + } + }, + new AnAction("Restore Popup", getRestorePopupDescription(), IconLoader.getIcon("/actions/cancel.png")) { + @Override + public void actionPerformed(AnActionEvent e) { + restorePopupBehavior(); + } + }}; + } + + + private void restartAutoUpdate(final boolean state) { + if (state && myToolWindow != null) { + if (myAutoUpdateRequest == null) { + myAutoUpdateRequest = new Runnable() { + public void run() { + if (myProject.isDisposed()) return; + + AsyncResult asyncResult = DataManager.getInstance().getDataContextFromFocus(); + DataContext dataContext = asyncResult.getResult(); + if (dataContext == null) { + return; + } + + if (PlatformDataKeys.PROJECT.getData(dataContext) != myProject) { + return; + } + + final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext); + if (editor == null) { + PsiElement element = LangDataKeys.PSI_ELEMENT.getData(dataContext); + if (element != null) { + doUpdateComponent(element); + } + return; + } + + final PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, myProject); + + final Editor injectedEditor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(editor, file); + if (injectedEditor != null) { + final PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(injectedEditor, myProject); + if (psiFile != null) { + doUpdateComponent(injectedEditor, psiFile); + return; + } + } + + if (file != null) { + doUpdateComponent(editor, file); + } + } + }; + + IdeEventQueue.getInstance().addIdleListener(myAutoUpdateRequest, 500); + } + } + else { + if (myAutoUpdateRequest != null) { + IdeEventQueue.getInstance().removeIdleListener(myAutoUpdateRequest); + myAutoUpdateRequest = null; + } + } + } + + + protected void restorePopupBehavior() { + if (myToolWindow != null) { + PropertiesComponent.getInstance().setValue(getShowInToolWindowProperty(), Boolean.FALSE.toString()); + + final Content[] contents = myToolWindow.getContentManager().getContents(); + for (final Content content : contents) { + final JComponent c = content.getComponent(); + if (c instanceof Disposable) { + Disposer.dispose((Disposable)c); + } + } + + ToolWindowManagerEx.getInstanceEx(myProject).unregisterToolWindow(getToolwindowId()); + myToolWindow = null; + restartAutoUpdate(false); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java index f6ebcecc4a9e..faf7635fbc72 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -25,7 +25,6 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.ide.BrowserUtil; import com.intellij.ide.DataManager; -import com.intellij.ide.IdeEventQueue; import com.intellij.ide.util.PropertiesComponent; import com.intellij.ide.util.gotoByName.ChooseByNameBase; import com.intellij.lang.Language; @@ -50,15 +49,13 @@ import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.wm.*; -import com.intellij.openapi.wm.ex.ToolWindowManagerEx; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.psi.*; -import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.psi.presentation.java.SymbolPresentationUtil; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtilBase; -import com.intellij.ui.content.*; +import com.intellij.ui.content.Content; import com.intellij.ui.popup.AbstractPopup; import com.intellij.ui.popup.NotLookupOrSearchCondition; import com.intellij.ui.popup.PopupPositionManager; @@ -66,8 +63,6 @@ import com.intellij.ui.popup.PopupUpdateProcessor; import com.intellij.util.Alarm; import com.intellij.util.Consumer; import com.intellij.util.Processor; -import com.intellij.util.ui.update.Activatable; -import com.intellij.util.ui.update.UiNotifyConnector; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -80,7 +75,7 @@ import java.lang.ref.WeakReference; import java.util.*; import java.util.List; -public class DocumentationManager { +public class DocumentationManager extends DockablePopupManager { @NonNls public static final String JAVADOC_LOCATION_AND_SIZE = "javadoc.popup"; public static final DataKey SELECTED_QUICK_DOC_TEXT = DataKey.create("QUICK_DOC.SELECTED_TEXT"); @@ -88,8 +83,7 @@ public class DocumentationManager { private static final Logger LOG = Logger.getInstance("#" + DocumentationManager.class.getName()); private static final String SHOW_DOCUMENTATION_IN_TOOL_WINDOW = "ShowDocumentationInToolWindow"; private static final String DOCUMENTATION_AUTO_UPDATE_ENABLED = "DocumentationAutoUpdateEnabled"; - - private final Project myProject; + private Editor myEditor = null; private ParameterInfoController myParameterInfoController; private final Alarm myUpdateDocAlarm; @@ -98,20 +92,37 @@ public class DocumentationManager { public static final Key ORIGINAL_ELEMENT_KEY = Key.create("Original element"); @NonNls public static final String PSI_ELEMENT_PROTOCOL = "psi_element://"; @NonNls private static final String DOC_ELEMENT_PROTOCOL = "doc_element://"; - private ToolWindow myToolWindow = null; private final ActionManagerEx myActionManagerEx; private static final int ourFlagsForTargetElements = TargetElementUtilBase.getInstance().getAllAccepted(); - private boolean myAutoUpdateDocumentation = PropertiesComponent.getInstance().isTrueValue(DOCUMENTATION_AUTO_UPDATE_ENABLED); - private Runnable myAutoUpdateRequest; + + protected String getToolwindowId() { + return ToolWindowId.DOCUMENTATION; + } + + protected DocumentationComponent createComponent() { + return new DocumentationComponent(this, createActions()); + } + + protected String getRestorePopupDescription() { + return "Restore documentation popup behavior"; + } + + protected String getAutoUpdateDescription() { + return "Show documentation for current element automatically"; + } + + protected String getAutoUpdateTitle() { + return "Auto Show Documentation for Selected Element"; + } public static DocumentationManager getInstance(Project project) { return ServiceManager.getService(project, DocumentationManager.class); } public DocumentationManager(Project project, ActionManagerEx managerEx) { - myProject = project; + super(project); myActionManagerEx = managerEx; final AnActionListener actionListener = new AnActionListener() { public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { @@ -362,144 +373,6 @@ public class DocumentationManager { } } - private void createToolWindow(final PsiElement element, PsiElement originalElement) { - assert myToolWindow == null; - - final DocumentationComponent component = new DocumentationComponent(this, new AnAction[]{ - new ToggleAction("Auto Show Documentation for Selected Element", "Show documentation for current element automatically", - IconLoader.getIcon("/general/autoscrollFromSource.png")) { - @Override - public boolean isSelected(AnActionEvent e) { - return myAutoUpdateDocumentation; - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - PropertiesComponent.getInstance().setValue(DOCUMENTATION_AUTO_UPDATE_ENABLED, Boolean.TRUE.toString()); - myAutoUpdateDocumentation = state; - restartAutoUpdate(state); - } - }, - new AnAction("Restore Popup", "Restore documentation popup behavior", IconLoader.getIcon("/actions/cancel.png")) { - @Override - public void actionPerformed(AnActionEvent e) { - restorePopupBehavior(); - } - }}); - - final ToolWindowManagerEx toolWindowManagerEx = ToolWindowManagerEx.getInstanceEx(myProject); - final ToolWindow toolWindow = toolWindowManagerEx.getToolWindow(ToolWindowId.DOCUMENTATION); - myToolWindow = toolWindow == null ? toolWindowManagerEx.registerToolWindow(ToolWindowId.DOCUMENTATION, true, ToolWindowAnchor.RIGHT, myProject) : toolWindow; - myToolWindow.setIcon(IconLoader.getIcon("/general/documentation.png")); - - myToolWindow.setAvailable(true, null); - myToolWindow.setToHideOnEmptyContent(false); - myToolWindow.setAutoHide(false); - - final Rectangle rectangle = WindowManager.getInstance().getIdeFrame(myProject).suggestChildFrameBounds(); - myToolWindow.setDefaultState(ToolWindowAnchor.RIGHT, ToolWindowType.FLOATING, rectangle); - - final ContentManager contentManager = myToolWindow.getContentManager(); - final ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); - final Content content = contentFactory.createContent(component, getTitle(element, true), false); - contentManager.addContent(content); - - contentManager.addContentManagerListener(new ContentManagerAdapter() { - @Override - public void contentRemoved(ContentManagerEvent event) { - if (contentManager.getContentCount() == 0) { - final JComponent c = event.getContent().getComponent(); - if (c instanceof DocumentationComponent) { - Disposer.dispose((DocumentationComponent) c); - } - - restorePopupBehavior(); - } - } - }); - - new UiNotifyConnector(component, new Activatable() { - public void showNotify() { - restartAutoUpdate(myAutoUpdateDocumentation); - } - - public void hideNotify() { - restartAutoUpdate(false); - } - }); - - myToolWindow.show(null); - PropertiesComponent.getInstance().setValue(SHOW_DOCUMENTATION_IN_TOOL_WINDOW, Boolean.TRUE.toString()); - restartAutoUpdate(PropertiesComponent.getInstance().getBoolean(DOCUMENTATION_AUTO_UPDATE_ENABLED, true)); - fetchDocInfo(getDefaultCollector(element, originalElement), component); - } - - private void restartAutoUpdate(final boolean state) { - if (state && myToolWindow != null) { - if (myAutoUpdateRequest == null) { - myAutoUpdateRequest = new Runnable() { - public void run() { - if (myProject.isDisposed()) return; - - AsyncResult asyncResult = DataManager.getInstance().getDataContextFromFocus(); - DataContext dataContext = asyncResult.getResult(); - if (dataContext == null) { - return; - } - final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext); - if (editor == null) { - return; - } - - if (PlatformDataKeys.PROJECT.getData(dataContext) != myProject) { - return; - } - - final PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, myProject); - - final Editor injectedEditor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(editor, file); - if (injectedEditor != null) { - final PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(injectedEditor, myProject); - if (psiFile != null) { - showJavaDocInfo(injectedEditor, psiFile, false, true); - return; - } - } - - if (file != null) { - showJavaDocInfo(editor, file, false, true); - } - } - }; - - IdeEventQueue.getInstance().addIdleListener(myAutoUpdateRequest, 500); - } - } else { - if (myAutoUpdateRequest != null) { - IdeEventQueue.getInstance().removeIdleListener(myAutoUpdateRequest); - myAutoUpdateRequest = null; - } - } - } - - private void restorePopupBehavior() { - if (myToolWindow != null) { - PropertiesComponent.getInstance().setValue(SHOW_DOCUMENTATION_IN_TOOL_WINDOW, Boolean.FALSE.toString()); - - final Content[] contents = myToolWindow.getContentManager().getContents(); - for (final Content content : contents) { - final JComponent c = content.getComponent(); - if (c instanceof DocumentationComponent) { - Disposer.dispose((DocumentationComponent) c); - } - } - - ToolWindowManagerEx.getInstanceEx(myProject).unregisterToolWindow(ToolWindowId.DOCUMENTATION); - myToolWindow = null; - restartAutoUpdate(false); - } - } - private static String getTitle(@NotNull final PsiElement element, final boolean _short) { final String title = SymbolPresentationUtil.getSymbolPresentableText(element); return _short ? title != null ? title : element.getText() : CodeInsightBundle.message("javadoc.info.title", title != null ? title : element.getText()); @@ -975,6 +848,34 @@ public class DocumentationManager { buffer.append(""); } + @Override + public String getShowInToolWindowProperty() { + return SHOW_DOCUMENTATION_IN_TOOL_WINDOW; + } + + @Override + public String getAutoUpdateEnabledProperty() { + return DOCUMENTATION_AUTO_UPDATE_ENABLED; + } + + protected void doUpdateComponent(PsiElement element, PsiElement originalElement, DocumentationComponent component) { + fetchDocInfo(getDefaultCollector(element, originalElement), component); + } + + protected void doUpdateComponent(Editor injectedEditor, PsiFile psiFile) { + showJavaDocInfo(injectedEditor, psiFile, false, true); + } + + @Override + protected void doUpdateComponent(@NotNull PsiElement element) { + showJavaDocInfo(element, element); + } + + @Override + protected String getTitle(PsiElement element) { + return getTitle(element, true); + } + private interface DocumentationCollector { @Nullable String getDocumentation() throws Exception;