diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.form b/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.form index aab91296ed1b..c2c5ecf9f70b 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.form +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.form @@ -304,7 +304,7 @@ - + @@ -339,9 +339,27 @@ - + + + + + + + + + + + + + + + + + + + diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java b/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java index 362c056a8d2c..ddf8dcddd2a6 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorOptionsPanel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -21,8 +21,10 @@ import com.intellij.application.options.OptionsApplicabilityFilter; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.daemon.impl.IdentifierHighlighterPass; +import com.intellij.codeInsight.documentation.QuickDocOnMouseOverManager; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; @@ -83,6 +85,7 @@ public class EditorOptionsPanel { private JTextField myCustomSoftWrapIndent; private JCheckBox myCbShowAllSoftWraps; private JCheckBox myPreselectCheckBox; + private JCheckBox myCbShowQuickDocOnCheckBox; private final ErrorHighlightingPanel myErrorHighlightingPanel = new ErrorHighlightingPanel(); private final MyConfigurable myConfigurable; @@ -156,6 +159,7 @@ public class EditorOptionsPanel { } myCbEnsureBlankLineBeforeCheckBox.setSelected(editorSettings.isEnsureNewLineAtEOF()); + myCbShowQuickDocOnCheckBox.setSelected(editorSettings.isShowQuickDocOnMouseOverElement()); // Advanced mouse myCbEnableDnD.setSelected(editorSettings.isDndEnabled()); @@ -235,6 +239,12 @@ public class EditorOptionsPanel { editorSettings.setEnsureNewLineAtEOF(myCbEnsureBlankLineBeforeCheckBox.isSelected()); + if (myCbShowQuickDocOnCheckBox.isSelected() ^ editorSettings.isShowQuickDocOnMouseOverElement()) { + boolean enabled = myCbShowQuickDocOnCheckBox.isSelected(); + editorSettings.setShowQuickDocOnMouseOverElement(enabled); + ServiceManager.getService(QuickDocOnMouseOverManager.class).setEnabled(enabled); + } + editorSettings.setDndEnabled(myCbEnableDnD.isSelected()); editorSettings.setWheelFontChangeEnabled(myCbEnableWheelFontChange.isSelected()); @@ -341,6 +351,7 @@ public class EditorOptionsPanel { // Strip trailing spaces, ensure EOL on EOF on save isModified |= !getStripTrailingSpacesValue().equals(editorSettings.getStripTrailingSpaces()); isModified |= isModified(myCbEnsureBlankLineBeforeCheckBox, editorSettings.isEnsureNewLineAtEOF()); + isModified |= isModified(myCbShowQuickDocOnCheckBox, editorSettings.isShowQuickDocOnMouseOverElement()); // advanced mouse isModified |= isModified(myCbEnableDnD, editorSettings.isDndEnabled()); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java index f356fa43587e..393e399f8254 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java @@ -146,7 +146,7 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr } { - enableEvents(KeyEvent.KEY_EVENT_MASK); + enableEvents(AWTEvent.KEY_EVENT_MASK); } protected void processKeyEvent(KeyEvent e) { 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 86c02c4ebaff..892e53cac7dc 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * 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. @@ -154,7 +154,20 @@ public class DocumentationManager extends DockablePopupManager> actions = Collections.singletonList(Pair.create(new ActionListener() { + final List> actions = + Collections.singletonList(Pair.create(new ActionListener() { public void actionPerformed(ActionEvent e) { createToolWindow(element, originalElement); final JBPopup hint = getDocInfoHint(); @@ -314,63 +341,66 @@ public class DocumentationManager extends DockablePopupManager() { - public Boolean compute() { - if (fromQuickSearch()) { - ((ChooseByNameBase.JPanelProvider)myPreviouslyFocused.getParent()).unregisterHint(); - } - - Disposer.dispose(component); - myEditor = null; - myPreviouslyFocused = null; - myParameterInfoController = null; - return Boolean.TRUE; - } - }) - .createPopup(); - - - AbstractPopup oldHint = (AbstractPopup)getDocInfoHint(); - if (oldHint != null) { - DocumentationComponent oldComponent = (DocumentationComponent)oldHint.getComponent(); - PsiElement element1 = oldComponent.getElement(); - if (Comparing.equal(element, element1)) { - if (requestFocus) { - component.getComponent().requestFocus(); + .setRequestFocusCondition(project, NotLookupOrSearchCondition.INSTANCE) + .setProject(project) + .addListener(updateProcessor) + .addUserData(updateProcessor) + .setKeyboardActions(actions) + .setDimensionServiceKey(myProject, JAVADOC_LOCATION_AND_SIZE, false) + .setResizable(true) + .setMovable(true) + .setRequestFocus(requestFocus) + .setCancelOnClickOutside(!hasLookup) // otherwise selecting lookup items by mouse would close the doc + .setTitle(getTitle(element, false)) + .setCouldPin(pinCallback) + .setCancelCallback(new Computable() { + public Boolean compute() { + if (closeCallback != null) { + closeCallback.run(); } - return; + if (fromQuickSearch()) { + ((ChooseByNameBase.JPanelProvider)myPreviouslyFocused.getParent()).unregisterHint(); + } + + Disposer.dispose(component); + myEditor = null; + myPreviouslyFocused = null; + myParameterInfoController = null; + return Boolean.TRUE; } - oldHint.cancel(); + }) + .createPopup(); + + + AbstractPopup oldHint = (AbstractPopup)getDocInfoHint(); + if (oldHint != null) { + DocumentationComponent oldComponent = (DocumentationComponent)oldHint.getComponent(); + PsiElement element1 = oldComponent.getElement(); + if (Comparing.equal(element, element1)) { + if (requestFocus) { + component.getComponent().requestFocus(); + } + return; } + oldHint.cancel(); + } - component.setHint(hint); + component.setHint(hint); - if (myEditor == null) { - // subsequent invocation of javadoc popup from completion will have myEditor == null because of cancel invoked, - // so reevaluate the editor for proper popup placement - Lookup lookup = LookupManager.getInstance(myProject).getActiveLookup(); - myEditor = lookup != null ? lookup.getEditor() : null; - } - fetchDocInfo(getDefaultCollector(element, originalElement), component); + if (myEditor == null) { + // subsequent invocation of javadoc popup from completion will have myEditor == null because of cancel invoked, + // so reevaluate the editor for proper popup placement + Lookup lookup = LookupManager.getInstance(myProject).getActiveLookup(); + myEditor = lookup != null ? lookup.getEditor() : null; + } + fetchDocInfo(getDefaultCollector(element, originalElement), component); - myDocInfoHintRef = new WeakReference(hint); - myPreviouslyFocused = WindowManagerEx.getInstanceEx().getFocusedComponent(project); + myDocInfoHintRef = new WeakReference(hint); + myPreviouslyFocused = WindowManagerEx.getInstanceEx().getFocusedComponent(project); - if (fromQuickSearch()) { - ((ChooseByNameBase.JPanelProvider)myPreviouslyFocused.getParent()).registerHint(hint); - } + if (fromQuickSearch()) { + ((ChooseByNameBase.JPanelProvider)myPreviouslyFocused.getParent()).registerHint(hint); + } } private static String getTitle(@NotNull final PsiElement element, final boolean _short) { @@ -392,13 +422,19 @@ public class DocumentationManager extends DockablePopupManager + * Not thread-safe. + * + * @author Denis Zhdanov + * @since 7/2/12 9:09 AM + */ +public class QuickDocOnMouseOverManager { + + private static final long QUICK_DOC_DELAY_MILLIS; + static { + long delay = 500; + String property = System.getProperty("editor.auto.quick.doc.delay.ms"); + if (property != null) { + try { + long parsed = Long.parseLong(property); + if (parsed > 0) { + delay = parsed; + } + } + catch (Exception e) { + // Ignore. + } + } + QUICK_DOC_DELAY_MILLIS = delay; + } + + @NotNull private final EditorMouseMotionListener myEditorListener = new MyEditorMouseListener(); + @NotNull private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); + @NotNull private final Runnable myRequest = new MyShowQuickDocRequest(); + @NotNull private final Runnable myHintCloseCallback = new Runnable() { + @Override + public void run() { + myActiveElements.clear(); + myDocumentationManager = null; + } + }; + + private final Map myActiveElements + = new HashMap(); + + /** Holds a reference (if any) to the documentation manager used last time to show an 'auto quick doc' popup. */ + @Nullable private WeakReference myDocumentationManager; + + @Nullable private DelayedQuickDocInfo myDelayedQuickDocInfo; + private boolean myEnabled; + + public QuickDocOnMouseOverManager(@NotNull Application application) { + EditorFactory factory = EditorFactory.getInstance(); + if (factory != null) { + factory.addEditorFactoryListener(new MyEditorFactoryListener(), application); + } + } + + /** + * Instructs the manager to enable or disable 'show quick doc automatically when the mouse goes over an editor element' mode. + * + * @param enabled flag that identifies if quick doc should be automatically shown + */ + public void setEnabled(boolean enabled) { + myEnabled = enabled; + if (!enabled) { + closeAutoQuickDocComponentIfNecessary(); + myAlarm.cancelAllRequests(); + } + EditorFactory factory = EditorFactory.getInstance(); + if (factory == null) { + return; + } + for (Editor editor : factory.getAllEditors()) { + if (enabled) { + editor.addEditorMouseMotionListener(myEditorListener); + } + else { + editor.removeEditorMouseMotionListener(myEditorListener); + } + } + } + + private void processMouseMove(@NotNull EditorMouseEvent e) { + if (e.getArea() != EditorMouseEventArea.EDITING_AREA) { + // Skip if the mouse is not at the editing area. + closeAutoQuickDocComponentIfNecessary(); + return; + } + + Editor editor = e.getEditor(); + Project project = editor.getProject(); + if (project == null) { + return; + } + + DocumentationManager documentationManager = DocumentationManager.getInstance(project); + JBPopup hint = documentationManager.getDocInfoHint(); + if (hint != null) { + + // Skip the event if the control is shown because of explicit 'show quick doc' action call. + WeakReference ref = myDocumentationManager; + if (ref == null || ref.get() == null) { + return; + } + + // Skip the event if the mouse is under the opened quick doc control. + Point hintLocation = hint.getLocationOnScreen(); + Dimension hintSize = hint.getSize(); + int mouseX = e.getMouseEvent().getXOnScreen(); + int mouseY = e.getMouseEvent().getYOnScreen(); + if (mouseX >= hintLocation.x && mouseX <= hintLocation.x + hintSize.width && mouseY >= hintLocation.y + && mouseY <= hintLocation.y + hintSize.height) + { + return; + } + } + + PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); + if (psiFile == null) { + closeAutoQuickDocComponentIfNecessary(); + return; + } + + int mouseOffset = editor.logicalPositionToOffset(editor.xyToLogicalPosition(e.getMouseEvent().getPoint())); + PsiElement elementUnderMouse = psiFile.findElementAt(mouseOffset); + if (elementUnderMouse == null || elementUnderMouse instanceof PsiWhiteSpace) { + closeAutoQuickDocComponentIfNecessary(); + return; + } + + PsiElement targetElementUnderMouse = documentationManager.findTargetElement(editor, mouseOffset, psiFile, elementUnderMouse); + if (targetElementUnderMouse == null) { + // No PSI element is located under the current mouse position - close quick doc if any. + closeAutoQuickDocComponentIfNecessary(); + return; + } + + PsiElement activeElement = myActiveElements.get(editor); + if (targetElementUnderMouse.equals(activeElement) + && (myAlarm.getActiveRequestCount() > 0 // Request to show documentation for the target component has been already queued. + || hint != null)) // Documentation for the target component is being shown. + { + return; + } + closeAutoQuickDocComponentIfNecessary(); + myActiveElements.put(editor, targetElementUnderMouse); + myDelayedQuickDocInfo = new DelayedQuickDocInfo(documentationManager, editor, targetElementUnderMouse, elementUnderMouse); + + myAlarm.cancelAllRequests(); + myAlarm.addRequest(myRequest, QUICK_DOC_DELAY_MILLIS); + } + + private void closeAutoQuickDocComponentIfNecessary() { + myAlarm.cancelAllRequests(); + WeakReference ref = myDocumentationManager; + if (ref == null) { + return; + } + + DocumentationManager docManager = ref.get(); + if (docManager == null) { + return; + } + + JBPopup hint = docManager.getDocInfoHint(); + if (hint == null) { + return; + } + + hint.cancel(); + } + + private static class DelayedQuickDocInfo { + + @NotNull public final DocumentationManager docManager; + @NotNull public final Editor editor; + @NotNull public final PsiElement targetElement; + @NotNull public final PsiElement originalElement; + + private DelayedQuickDocInfo(@NotNull DocumentationManager docManager, + @NotNull Editor editor, @NotNull PsiElement targetElement, + @NotNull PsiElement originalElement) + { + this.docManager = docManager; + this.editor = editor; + this.targetElement = targetElement; + this.originalElement = originalElement; + } + } + + private class MyShowQuickDocRequest implements Runnable { + @Override + public void run() { + myAlarm.cancelAllRequests(); + + DelayedQuickDocInfo info = myDelayedQuickDocInfo; + if (info == null || !info.targetElement.equals(myActiveElements.get(info.editor))) { + return; + } + + info.editor.putUserData(PopupFactoryImpl.ANCHOR_POPUP_POSITION, + info.editor.offsetToVisualPosition(info.originalElement.getTextRange().getStartOffset())); + try { + info.docManager.showJavaDocInfo(info.editor, info.targetElement, info.originalElement, myHintCloseCallback); + myDocumentationManager = new WeakReference(info.docManager); + } + finally { + info.editor.putUserData(PopupFactoryImpl.ANCHOR_POPUP_POSITION, null); + } + } + } + + private class MyEditorFactoryListener implements EditorFactoryListener { + @Override + public void editorCreated(@NotNull EditorFactoryEvent event) { + if (myEnabled) { + event.getEditor().addEditorMouseMotionListener(myEditorListener); + } + } + + @Override + public void editorReleased(@NotNull EditorFactoryEvent event) { + event.getEditor().removeEditorMouseMotionListener(myEditorListener); + } + } + + private class MyEditorMouseListener extends EditorMouseMotionAdapter { + + @Override + public void mouseMoved(EditorMouseEvent e) { + processMouseMove(e); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocOnMouseOverStartupActivity.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocOnMouseOverStartupActivity.java new file mode 100644 index 000000000000..72ffe987ed41 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocOnMouseOverStartupActivity.java @@ -0,0 +1,35 @@ +/* + * 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.openapi.components.ServiceManager; +import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupActivity; + +/** + * @author Denis Zhdanov + * @since 7/2/12 9:44 AM + */ +public class QuickDocOnMouseOverStartupActivity implements StartupActivity { + + @Override + public void runActivity(Project project) { + if (EditorSettingsExternalizable.getInstance().isShowQuickDocOnMouseOverElement()) { + ServiceManager.getService(QuickDocOnMouseOverManager.class).setEnabled(true); + } + } +} diff --git a/platform/platform-api/src/com/intellij/openapi/editor/EditorFactory.java b/platform/platform-api/src/com/intellij/openapi/editor/EditorFactory.java index 78c7ce26b9b0..9a231c30fbd2 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/EditorFactory.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/EditorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * 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. @@ -36,6 +36,7 @@ public abstract class EditorFactory implements ApplicationComponent { * * @return the editor factory instance. */ + @Nullable public static EditorFactory getInstance() { final Application application = ApplicationManager.getApplication(); return application == null ? null : application.getComponent(EditorFactory.class); @@ -168,7 +169,7 @@ public abstract class EditorFactory implements ApplicationComponent { /** * Registers a listener for receiving notifications when editor instances are created and released - * and removes the listener when {@link parentDisposable} get disposed. + * and removes the listener when the 'parentDisposable' gets disposed. * * @param listener the listener instance. * @param parentDisposable the Disposable which triggers the removal of the listener @@ -176,7 +177,7 @@ public abstract class EditorFactory implements ApplicationComponent { public abstract void addEditorFactoryListener(@NotNull EditorFactoryListener listener, @NotNull Disposable parentDisposable); /** - * Unregisters a listener for receiving notifications when editor instances are created + * Un-registers a listener for receiving notifications when editor instances are created * and released. * * @param listener the listener instance. diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java index 20ee29d35212..b99fa22dce8c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -19,6 +19,7 @@ import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.ExportableApplicationComponent; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces; import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.util.DefaultJDOMExternalizer; @@ -50,6 +51,7 @@ public class EditorSettingsExternalizable implements NamedJDOMExternalizable, Ex public boolean IS_CARET_INSIDE_TABS; @NonNls public String STRIP_TRAILING_SPACES = "Changed"; public boolean IS_ENSURE_NEWLINE_AT_EOF = false; + public boolean SHOW_QUICK_DOC_ON_MOUSE_OVER_ELEMENT = false; public boolean IS_CARET_BLINKING = true; public int CARET_BLINKING_PERIOD = 500; public boolean IS_RIGHT_MARGIN_SHOWN = true; @@ -356,7 +358,7 @@ public class EditorSettingsExternalizable implements NamedJDOMExternalizable, Ex public void setEnsureNewLineAtEOF(boolean ensure) { myOptions.IS_ENSURE_NEWLINE_AT_EOF = ensure; } - + public String getStripTrailingSpaces() { return myOptions.STRIP_TRAILING_SPACES; } // TODO: move to CodeEditorManager or something else @@ -365,6 +367,14 @@ public class EditorSettingsExternalizable implements NamedJDOMExternalizable, Ex myOptions.STRIP_TRAILING_SPACES = stripTrailingSpaces; } + public boolean isShowQuickDocOnMouseOverElement() { + return myOptions.SHOW_QUICK_DOC_ON_MOUSE_OVER_ELEMENT; + } + + public void setShowQuickDocOnMouseOverElement(boolean show) { + myOptions.SHOW_QUICK_DOC_ON_MOUSE_OVER_ELEMENT = show; + } + public boolean isRefrainFromScrolling() { return myOptions.REFRAIN_FROM_SCROLLING; } diff --git a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java index b2e4a8e5fed1..ea31ba758f63 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -28,6 +28,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.VisualPosition; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.*; diff --git a/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java index 414c5ece580e..54cac050b66e 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -36,6 +36,7 @@ import com.intellij.openapi.ui.popup.*; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.EmptyRunnable; +import com.intellij.openapi.util.Key; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.openapi.wm.impl.IdeFrameImpl; @@ -65,6 +66,14 @@ import java.util.HashMap; import java.util.List; public class PopupFactoryImpl extends JBPopupFactory { + + /** + * Allows to get an editor position for which a popup with auxiliary information might be shown. + *

+ * Primary intention for this key is to hint popup position for the non-caret location. + */ + public static final Key ANCHOR_POPUP_POSITION = Key.create("popup.anchor.position"); + private static final Logger LOG = Logger.getInstance("#com.intellij.ui.popup.PopupFactoryImpl"); private static final Icon QUICK_LIST_ICON = AllIcons.Actions.QuickList; @@ -78,7 +87,7 @@ public class PopupFactoryImpl extends JBPopupFactory { } public JBPopup createMessage(String text) { - return createListPopup(new BaseListPopupStep(null, new String[]{text})); + return createListPopup(new BaseListPopupStep(null, new String[]{text})); } @Override @@ -98,28 +107,34 @@ public class PopupFactoryImpl extends JBPopupFactory { return null; } - public ListPopup createConfirmation(String title, final String yesText, String noText, final Runnable onYes, final Runnable onNo, int defaultOptionIndex) { + public ListPopup createConfirmation(String title, + final String yesText, + String noText, + final Runnable onYes, + final Runnable onNo, + int defaultOptionIndex) + { - final BaseListPopupStep step = new BaseListPopupStep(title, new String[]{yesText, noText}) { - public PopupStep onChosen(String selectedValue, final boolean finalChoice) { - if (selectedValue.equals(yesText)) { - onYes.run(); - } - else { - onNo.run(); - } - return FINAL_CHOICE; + final BaseListPopupStep step = new BaseListPopupStep(title, new String[]{yesText, noText}) { + public PopupStep onChosen(String selectedValue, final boolean finalChoice) { + if (selectedValue.equals(yesText)) { + onYes.run(); } - - public void canceled() { + else { onNo.run(); } + return FINAL_CHOICE; + } - public boolean isMnemonicsNavigationEnabled() { - return true; - } - }; - step.setDefaultOptionIndex(defaultOptionIndex); + public void canceled() { + onNo.run(); + } + + public boolean isMnemonicsNavigationEnabled() { + return true; + } + }; + step.setDefaultOptionIndex(defaultOptionIndex); final ApplicationEx app = ApplicationManagerEx.getApplicationEx(); return app == null || !app.isUnitTestMode() ? new ListPopupImpl(step) : new MockConfirmation(step, yesText); @@ -127,13 +142,13 @@ public class PopupFactoryImpl extends JBPopupFactory { private static ListPopup createActionGroupPopup(final String title, - final ActionGroup actionGroup, - @NotNull DataContext dataContext, - boolean showNumbers, - boolean useAlphaAsNumbers, - boolean showDisabledActions, - boolean honorActionMnemonics, - final Runnable disposeCallback, + final ActionGroup actionGroup, + @NotNull DataContext dataContext, + boolean showNumbers, + boolean useAlphaAsNumbers, + boolean showDisabledActions, + boolean honorActionMnemonics, + final Runnable disposeCallback, final int maxRowCount) { return createActionGroupPopup(title, actionGroup, dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, honorActionMnemonics, disposeCallback, maxRowCount, null, null); @@ -458,14 +473,18 @@ public class PopupFactoryImpl extends JBPopupFactory { } public RelativePoint guessBestPopupLocation(Editor editor) { - CaretModel caretModel = editor.getCaretModel(); - final VisualPosition visualPosition; - if (caretModel.isUpToDate()) { - visualPosition = caretModel.getVisualPosition(); - } - else { - visualPosition = editor.offsetToVisualPosition(caretModel.getOffset()); + VisualPosition visualPosition = editor.getUserData(ANCHOR_POPUP_POSITION); + + if (visualPosition == null) { + CaretModel caretModel = editor.getCaretModel(); + if (caretModel.isUpToDate()) { + visualPosition = caretModel.getVisualPosition(); + } + else { + visualPosition = editor.offsetToVisualPosition(caretModel.getOffset()); + } } + Point p = editor.visualPositionToXY(new VisualPosition(visualPosition.line + 1, visualPosition.column)); final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea(); diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 977f08eb1ca7..3ecbbf068091 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -372,6 +372,7 @@ checkbox.show.virtual.space.at.file.bottom=Show virtual space at file bottom checkbox.optimize.imports.on.the.fly=Optimize imports on the fly checkbox.add.unambiguous.imports.on.the.fly=Add unambiguous imports on the fly combobox.strip.trailing.spaces.on.save=Strip trailing spaces on Save: +checkbox.show.quick.doc.on.mouse.over=Show quick doc on mouse over element group.limits=Limits editbox.recent.files.limit=Recent files limit: editbox.console.history.limit=Console commands history size: diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 14c14f44a482..054d202363db 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -733,7 +733,10 @@ - + + + +