IDEA-66333 Quick documentation lookup on mouse hover

This commit is contained in:
Denis.Zhdanov
2012-07-03 08:43:53 +04:00
parent 99290d8104
commit 02eb01409e
12 changed files with 510 additions and 106 deletions
@@ -304,7 +304,7 @@
</component>
</children>
</grid>
<grid id="7f3f5" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="7f3f5" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
@@ -339,9 +339,27 @@
</component>
<vspacer id="82d85">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="9558b" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="2006d" class="javax.swing.JCheckBox" binding="myCbShowQuickDocOnCheckBox" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/ApplicationBundle" key="checkbox.show.quick.doc.on.mouse.over"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
<grid id="5df80" binding="myHighlightSettingsPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
@@ -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());
@@ -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) {
@@ -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<DocumentationComp
myUpdateDocAlarm = new Alarm(Alarm.ThreadToUse.OWN_THREAD,myProject);
}
public void showJavaDocInfo(@NotNull Editor editor,
@NotNull final PsiElement element,
@NotNull final PsiElement original,
@Nullable Runnable closeCallback)
{
myEditor = editor;
showJavaDocInfo(element, original, closeCallback);
}
public void showJavaDocInfo(@NotNull final PsiElement element, final PsiElement original) {
showJavaDocInfo(element, original, null);
}
public void showJavaDocInfo(@NotNull final PsiElement element, final PsiElement original, @Nullable Runnable closeCallback) {
PopupUpdateProcessor updateProcessor = new PopupUpdateProcessor(element.getProject()) {
public void updatePopup(Object lookupItemObject) {
if (lookupItemObject instanceof PsiElement) {
@@ -163,7 +176,7 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp
}
};
doShowJavaDocInfo(element, false, updateProcessor, original, false);
doShowJavaDocInfo(element, false, updateProcessor, original, false, closeCallback);
}
public void showJavaDocInfo(final Editor editor, @Nullable final PsiFile file, boolean requestFocus) {
@@ -251,7 +264,18 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp
}
private void doShowJavaDocInfo(final PsiElement element, boolean requestFocus, PopupUpdateProcessor updateProcessor,
final PsiElement originalElement, final boolean autoupdate) {
final PsiElement originalElement, final boolean autoupdate)
{
doShowJavaDocInfo(element, requestFocus, updateProcessor, originalElement, autoupdate, null);
}
private void doShowJavaDocInfo(final PsiElement element,
boolean requestFocus,
PopupUpdateProcessor updateProcessor,
final PsiElement originalElement,
final boolean autoupdate,
@Nullable final Runnable closeCallback)
{
Project project = getProject(element);
if (myToolWindow == null && PropertiesComponent.getInstance().isTrueValue(SHOW_DOCUMENTATION_IN_TOOL_WINDOW)) {
@@ -267,10 +291,12 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp
fetchDocInfo(getDefaultCollector(element, originalElement), component, true);
if (!myToolWindow.isVisible()) myToolWindow.show(null);
return;
} else {
}
else {
if (element != null && !autoupdate) {
restorePopupBehavior();
} else {
}
else {
return;
}
}
@@ -289,7 +315,7 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp
@Override
public void consume(PsiElement psiElement) {
final AbstractPopup jbPopup = (AbstractPopup)getDocInfoHint();
if(jbPopup != null) {
if (jbPopup != null) {
final String title = getTitle(psiElement, false);
jbPopup.setCaption(title);
}
@@ -304,7 +330,8 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp
};
final KeyboardShortcut keyboardShortcut = ActionManagerEx.getInstanceEx().getKeyboardShortcut("QuickJavaDoc");
final List<Pair<ActionListener, KeyStroke>> actions = Collections.singletonList(Pair.<ActionListener, KeyStroke>create(new ActionListener() {
final List<Pair<ActionListener, KeyStroke>> actions =
Collections.singletonList(Pair.<ActionListener, KeyStroke>create(new ActionListener() {
public void actionPerformed(ActionEvent e) {
createToolWindow(element, originalElement);
final JBPopup hint = getDocInfoHint();
@@ -314,63 +341,66 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp
boolean hasLookup = LookupManager.getActiveLookup(myEditor) != null;
final JBPopup hint = JBPopupFactory.getInstance().createComponentPopupBuilder(component, component)
.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<Boolean>() {
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<Boolean>() {
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<JBPopup>(hint);
myPreviouslyFocused = WindowManagerEx.getInstanceEx().getFocusedComponent(project);
myDocInfoHintRef = new WeakReference<JBPopup>(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<DocumentationComp
@Nullable
public PsiElement findTargetElement(final Editor editor, @Nullable final PsiFile file, PsiElement contextElement) {
PsiElement element = editor != null ? TargetElementUtilBase.findTargetElement(editor, ourFlagsForTargetElements) : null;
return findTargetElement(editor, editor.getCaretModel().getOffset(), file, contextElement);
}
@Nullable
public PsiElement findTargetElement(final Editor editor, int offset, @Nullable final PsiFile file, PsiElement contextElement) {
TargetElementUtilBase util = TargetElementUtilBase.getInstance();
PsiElement element = editor != null ? util.findTargetElement(editor, ourFlagsForTargetElements, offset)
: null;
assertSameProject(element);
// Allow context doc over xml tag content
if (element != null || contextElement != null) {
final PsiElement adjusted = TargetElementUtilBase.getInstance()
.adjustElement(editor, ourFlagsForTargetElements, element, contextElement);
final PsiElement adjusted = util.adjustElement(editor, ourFlagsForTargetElements, element, contextElement);
if (adjusted != null) {
element = adjusted;
assertSameProject(element);
@@ -410,10 +446,10 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp
assertSameProject(element);
if (element == null) {
final PsiReference ref = TargetElementUtilBase.findReference(editor, editor.getCaretModel().getOffset());
final PsiReference ref = util.findReference(editor, offset);
if (ref != null) {
element = TargetElementUtilBase.getInstance().adjustReference(ref);
element = util.adjustReference(ref);
assertSameProject(element);
if (element == null && ref instanceof PsiPolyVariantReference) {
element = ref.getElement();
@@ -0,0 +1,269 @@
/*
* 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.application.Application;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.ui.popup.PopupFactoryImpl;
import com.intellij.util.Alarm;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
/**
* Serves as a facade to the 'show quick doc on mouse over an element' functionality.
* <p/>
* 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<Editor, PsiElement /** PSI element which is located under the current mouse position */> myActiveElements
= new HashMap<Editor, PsiElement>();
/** Holds a reference (if any) to the documentation manager used last time to show an 'auto quick doc' popup. */
@Nullable private WeakReference<DocumentationManager> 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<DocumentationManager> 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<DocumentationManager> 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<DocumentationManager>(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);
}
}
}
@@ -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);
}
}
}
@@ -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 <code>'parentDisposable'</code> 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.
@@ -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;
}
@@ -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.*;
@@ -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.
* <p/>
* Primary intention for this key is to hint popup position for the non-caret location.
*/
public static final Key<VisualPosition> 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<String>(null, new String[]{text}));
return createListPopup(new BaseListPopupStep<String>(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<String> step = new BaseListPopupStep<String>(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<String> step = new BaseListPopupStep<String>(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();
@@ -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:
@@ -733,7 +733,10 @@
<applicationService serviceInterface="com.intellij.ide.todo.TodoConfiguration"
serviceImplementation="com.intellij.ide.todo.TodoConfiguration"/>
<indexPatternProvider implementation="com.intellij.ide.todo.TodoIndexPatternProvider"/>
<applicationService serviceImplementation="com.intellij.codeInsight.documentation.QuickDocOnMouseOverManager"/>
<postStartupActivity implementation="com.intellij.codeInsight.documentation.QuickDocOnMouseOverStartupActivity"/>
<hectorComponentProvider implementation="com.intellij.codeInsight.daemon.PowerSaveHectorProvider"/>
<copyPastePostProcessor implementation="com.intellij.codeInsight.editorActions.CopyPasteIndentProcessor"/>