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 3b0391ecd073..aab91296ed1b 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 @@ - + @@ -317,7 +317,7 @@ - + @@ -333,44 +333,15 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 10e79040227f..334745b88800 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 @@ -21,10 +21,8 @@ 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; @@ -41,7 +39,6 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -49,11 +46,11 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; public class EditorOptionsPanel { - private JPanel myBehaviourPanel; + private JPanel myBehaviourPanel; private JCheckBox myCbHighlightBraces; private static final String STRIP_CHANGED = ApplicationBundle.message("combobox.strip.modified.lines"); - private static final String STRIP_ALL = ApplicationBundle.message("combobox.strip.all"); + private static final String STRIP_ALL = ApplicationBundle.message("combobox.strip.all"); private static final String STRIP_NONE = ApplicationBundle.message("combobox.strip.none"); private JComboBox myStripTrailingSpacesCombo; @@ -66,34 +63,32 @@ public class EditorOptionsPanel { private JCheckBox myCbHighlightScope; private JTextField myClipboardContentLimitTextField; - private JCheckBox myCbSmoothScrolling; - private JCheckBox myCbVirtualPageAtBottom; - private JCheckBox myCbEnableDnD; - private JCheckBox myCbEnableWheelFontChange; - private JCheckBox myCbHonorCamelHumpsWhenSelectingByClicking; + private JCheckBox myCbSmoothScrolling; + private JCheckBox myCbVirtualPageAtBottom; + private JCheckBox myCbEnableDnD; + private JCheckBox myCbEnableWheelFontChange; + private JCheckBox myCbHonorCamelHumpsWhenSelectingByClicking; - private JPanel myHighlightSettingsPanel; + private JPanel myHighlightSettingsPanel; private JRadioButton myRbPreferScrolling; private JRadioButton myRbPreferMovingCaret; - private JCheckBox myCbRenameLocalVariablesInplace; - private JCheckBox myCbHighlightIdentifierUnderCaret; - private JCheckBox myCbEnsureBlankLineBeforeCheckBox; - private JCheckBox myShowReformatCodeDialogCheckBox; - private JCheckBox myShowOptimizeImportsDialogCheckBox; - private JCheckBox myCbUseSoftWrapsAtEditor; - private JCheckBox myCbUseSoftWrapsAtConsole; - private JCheckBox myCbUseCustomSoftWrapIndent; - private JTextField myCustomSoftWrapIndent; - private JCheckBox myCbShowAllSoftWraps; - private JCheckBox myPreselectCheckBox; - private JCheckBox myCbShowQuickDocOnCheckBox; - private JTextField myQuickDocDelayTextField; + private JCheckBox myCbRenameLocalVariablesInplace; + private JCheckBox myCbHighlightIdentifierUnderCaret; + private JCheckBox myCbEnsureBlankLineBeforeCheckBox; + private JCheckBox myShowReformatCodeDialogCheckBox; + private JCheckBox myShowOptimizeImportsDialogCheckBox; + private JCheckBox myCbUseSoftWrapsAtEditor; + private JCheckBox myCbUseSoftWrapsAtConsole; + private JCheckBox myCbUseCustomSoftWrapIndent; + private JTextField myCustomSoftWrapIndent; + private JCheckBox myCbShowAllSoftWraps; + private JCheckBox myPreselectCheckBox; private final ErrorHighlightingPanel myErrorHighlightingPanel = new ErrorHighlightingPanel(); private final MyConfigurable myConfigurable; - public EditorOptionsPanel() { + public EditorOptionsPanel(){ if (SystemInfo.isMac) { myCbEnableWheelFontChange.setText(ApplicationBundle.message("checkbox.enable.ctrl.mousewheel.changes.font.size.macos")); } @@ -111,7 +106,6 @@ public class EditorOptionsPanel { myCbRenameLocalVariablesInplace.setVisible(OptionsApplicabilityFilter.isApplicable(OptionId.RENAME_IN_PLACE)); myConfigurable = new MyConfigurable(); - initQuickDocProcessing(); initSoftWrapsSettingsProcessing(); } @@ -162,9 +156,6 @@ public class EditorOptionsPanel { } myCbEnsureBlankLineBeforeCheckBox.setSelected(editorSettings.isEnsureNewLineAtEOF()); - myCbShowQuickDocOnCheckBox.setSelected(editorSettings.isShowQuickDocOnMouseOverElement()); - myQuickDocDelayTextField.setText(Long.toString(editorSettings.getQuickDocOnMouseOverElementDelayMillis())); - myQuickDocDelayTextField.setEnabled(editorSettings.isShowQuickDocOnMouseOverElement()); // Advanced mouse myCbEnableDnD.setSelected(editorSettings.isDndEnabled()); @@ -244,17 +235,6 @@ 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); - } - - Long quickDocDelay = getQuickDocDelayFromGui(); - if (quickDocDelay != null) { - editorSettings.setQuickDocOnMouseOverElementDelayMillis(quickDocDelay); - } - editorSettings.setDndEnabled(myCbEnableDnD.isSelected()); editorSettings.setWheelFontChangeEnabled(myCbEnableWheelFontChange.isSelected()); @@ -288,23 +268,6 @@ public class EditorOptionsPanel { restartDaemons(); } - @Nullable - private Long getQuickDocDelayFromGui() { - String quickDocDelayAsText = myQuickDocDelayTextField.getText(); - if (StringUtil.isEmptyOrSpaces(quickDocDelayAsText)) { - return null; - } - - try { - long delay = Long.parseLong(quickDocDelayAsText); - return delay > 0 ? delay : null; - } - catch (NumberFormatException e) { - // Ignore incorrect value. - return null; - } - } - public static void restartDaemons() { Project[] projects = ProjectManager.getInstance().getOpenProjects(); for (Project project : projects) { @@ -378,11 +341,6 @@ 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()); - Long quickDocDelay = getQuickDocDelayFromGui(); - if (quickDocDelay != null && !quickDocDelay.equals(Long.valueOf(editorSettings.getQuickDocOnMouseOverElementDelayMillis()))) { - return true; - } // advanced mouse isModified |= isModified(myCbEnableDnD, editorSettings.isDndEnabled()); @@ -403,9 +361,7 @@ public class EditorOptionsPanel { isModified |= myErrorHighlightingPanel.isModified(); return isModified; } - - - + private static boolean isModified(JToggleButton checkBox, boolean value) { return checkBox.isSelected() != value; } @@ -448,15 +404,6 @@ public class EditorOptionsPanel { return defaultIndent; } - private void initQuickDocProcessing() { - myCbShowQuickDocOnCheckBox.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - myQuickDocDelayTextField.setEnabled(myCbShowQuickDocOnCheckBox.isSelected()); - } - }); - } - private void initSoftWrapsSettingsProcessing() { ItemListener listener = new ItemListener() { @Override 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 512b28c3ea51..b42eeb14e60f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -325,7 +325,7 @@ public class DocumentationManager extends DockablePopupManager - * Not thread-safe. - * - * @author Denis Zhdanov - * @since 7/2/12 9:09 AM - */ -public class QuickDocOnMouseOverManager { - - @NotNull private final EditorMouseMotionListener myMouseListener = new MyEditorMouseListener(); - @NotNull private final VisibleAreaListener myVisibleAreaListener = new MyVisibleAreaListener(); - @NotNull private final CaretListener myCaretListener = new MyCaretListener(); - @NotNull private final DocumentListener myDocumentListener = new MyDocumentListener(); - @NotNull private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); - @NotNull private final Runnable myRequest = new MyShowQuickDocRequest(); - @NotNull private final Runnable myHintCloseCallback = new MyCloseDocCallback(); - @NotNull private final Map myMonitoredDocuments = new WeakHashMap(); - - private final Map myActiveElements - = new WeakHashMap(); - - /** 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; - private boolean myApplicationActive; - - public QuickDocOnMouseOverManager(@NotNull Application application) { - EditorFactory factory = EditorFactory.getInstance(); - if (factory != null) { - factory.addEditorFactoryListener(new MyEditorFactoryListener(), application); - } - - ApplicationManager.getApplication().getMessageBus().connect().subscribe( - ApplicationActivationListener.TOPIC, - new ApplicationActivationListener() { - @Override - public void applicationActivated(IdeFrame ideFrame) { - myApplicationActive = true; - } - - @Override - public void applicationDeactivated(IdeFrame ideFrame) { - myApplicationActive = false; - } - }); - } - - /** - * 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; - myApplicationActive = enabled; - if (!enabled) { - closeQuickDocIfPossible(); - myAlarm.cancelAllRequests(); - } - EditorFactory factory = EditorFactory.getInstance(); - if (factory == null) { - return; - } - for (Editor editor : factory.getAllEditors()) { - if (enabled) { - registerListeners(editor); - } - else { - unRegisterListeners(editor); - } - } - } - - private void registerListeners(@NotNull Editor editor) { - editor.addEditorMouseMotionListener(myMouseListener); - editor.getScrollingModel().addVisibleAreaListener(myVisibleAreaListener); - editor.getCaretModel().addCaretListener(myCaretListener); - - Document document = editor.getDocument(); - if (myMonitoredDocuments.put(document, Boolean.TRUE) == null) { - document.addDocumentListener(myDocumentListener); - } - } - - private void unRegisterListeners(@NotNull Editor editor) { - editor.removeEditorMouseMotionListener(myMouseListener); - editor.getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener); - editor.getCaretModel().removeCaretListener(myCaretListener); - - Document document = editor.getDocument(); - if (myMonitoredDocuments.remove(document) != null) { - document.removeDocumentListener(myDocumentListener); - } - } - - private void processMouseMove(@NotNull EditorMouseEvent e) { - if (!myApplicationActive || e.getArea() != EditorMouseEventArea.EDITING_AREA) { - // Skip if the mouse is not at the editing area. - closeQuickDocIfPossible(); - return; - } - - if (e.getMouseEvent().getModifiers() != 0) { - // Don't show the control when any modifier is active (e.g. Ctrl or Alt is hold). There is a common situation that a user - // wants to navigate via Ctrl+click or perform quick evaluate by Alt+click. - return; - } - - Editor editor = e.getEditor(); - if (editor.isOneLineMode()) { - // Don't want auto quick doc to mess at, say, editor used for debugger condition. - return; - } - - 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) { - return; - } - DocumentationManager manager = ref.get(); - if (manager == null || !manager.isCloseOnSneeze()) { - 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) { - closeQuickDocIfPossible(); - return; - } - - int mouseOffset = editor.logicalPositionToOffset(editor.xyToLogicalPosition(e.getMouseEvent().getPoint())); - PsiElement elementUnderMouse = psiFile.findElementAt(mouseOffset); - if (elementUnderMouse == null || elementUnderMouse instanceof PsiWhiteSpace) { - closeQuickDocIfPossible(); - 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. - closeQuickDocIfPossible(); - 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; - } - allowUpdateFromContext(false); - closeQuickDocIfPossible(); - myActiveElements.put(editor, targetElementUnderMouse); - myDelayedQuickDocInfo = new DelayedQuickDocInfo(documentationManager, editor, targetElementUnderMouse, elementUnderMouse); - - myAlarm.cancelAllRequests(); - myAlarm.addRequest(myRequest, EditorSettingsExternalizable.getInstance().getQuickDocOnMouseOverElementDelayMillis()); - } - - private void closeQuickDocIfPossible() { - myAlarm.cancelAllRequests(); - DocumentationManager docManager = getDocManager(); - if (docManager == null) { - return; - } - - JBPopup hint = docManager.getDocInfoHint(); - if (hint == null) { - return; - } - - hint.cancel(); - myDocumentationManager = null; - } - - private void allowUpdateFromContext(boolean allow) { - DocumentationManager documentationManager = getDocManager(); - if (documentationManager != null) { - documentationManager.setAllowContentUpdateFromContext(allow); - } - } - - @Nullable - private DocumentationManager getDocManager() { - WeakReference ref = myDocumentationManager; - if (ref == null) { - return null; - } - - DocumentationManager docManager = ref.get(); - if (docManager == null) { - return null; - } - return docManager; - } - - 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 { - - private final HintManager myHintManager = HintManager.getInstance(); - - @Override - public void run() { - myAlarm.cancelAllRequests(); - - // Skip the request if it's outdated (the mouse is moved other another element). - DelayedQuickDocInfo info = myDelayedQuickDocInfo; - if (info == null || !info.targetElement.equals(myActiveElements.get(info.editor))) { - return; - } - - // Skip the request if there is a control shown as a result of explicit 'show quick doc' (Ctrl + Q) invocation. - if (info.docManager.getDocInfoHint() != null && !info.docManager.isCloseOnSneeze()) { - return; - } - - // We don't want to show a quick doc control if there is an active hint (e.g. the mouse is under an invalid element - // and corresponding error info is shown). - if (!info.docManager.hasActiveDockedDocWindow() && myHintManager.hasShownHintsThatWillHideByOtherHint(false)) { - myAlarm.addRequest(this, EditorSettingsExternalizable.getInstance().getQuickDocOnMouseOverElementDelayMillis()); - 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, true, true); - myDocumentationManager = new WeakReference(info.docManager); - } - finally { - info.editor.putUserData(PopupFactoryImpl.ANCHOR_POPUP_POSITION, null); - } - } - } - - private class MyCloseDocCallback implements Runnable { - @Override - public void run() { - myActiveElements.clear(); - myDocumentationManager = null; - } - } - - private class MyEditorFactoryListener implements EditorFactoryListener { - @Override - public void editorCreated(@NotNull EditorFactoryEvent event) { - if (myEnabled) { - registerListeners(event.getEditor()); - } - } - - @Override - public void editorReleased(@NotNull EditorFactoryEvent event) { - if (myEnabled) { - // We do this in the 'if' block because editor logs an error on attempt to remove already released listener. - unRegisterListeners(event.getEditor()); - } - } - } - - private class MyEditorMouseListener extends EditorMouseMotionAdapter { - - @Override - public void mouseMoved(EditorMouseEvent e) { - processMouseMove(e); - } - } - - private class MyVisibleAreaListener implements VisibleAreaListener { - @Override - public void visibleAreaChanged(VisibleAreaEvent e) { - closeQuickDocIfPossible(); - } - } - - private class MyCaretListener implements CaretListener { - @Override - public void caretPositionChanged(CaretEvent e) { - allowUpdateFromContext(true); - closeQuickDocIfPossible(); - } - } - - private class MyDocumentListener extends DocumentAdapter { - @Override - public void documentChanged(DocumentEvent e) { - closeQuickDocIfPossible(); - } - } -} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocOnMouseOverStartupActivity.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocOnMouseOverStartupActivity.java deleted file mode 100644 index 7c9de68b4362..000000000000 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/QuickDocOnMouseOverStartupActivity.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java index 739d682c6660..17385cf42106 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java @@ -29,9 +29,9 @@ import com.intellij.ide.util.EditSourceUtil; import com.intellij.lang.documentation.DocumentationProvider; import com.intellij.navigation.ItemPresentation; import com.intellij.navigation.NavigationItem; -import com.intellij.openapi.actionSystem.IdeActions; -import com.intellij.openapi.actionSystem.MouseShortcut; -import com.intellij.openapi.actionSystem.Shortcut; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.impl.ActionButton; +import com.intellij.openapi.actionSystem.impl.PresentationFactory; import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; @@ -59,6 +59,8 @@ import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -72,6 +74,7 @@ import com.intellij.usageView.UsageViewShortNameLocation; import com.intellij.usageView.UsageViewTypeLocation; import com.intellij.util.Processor; import org.intellij.lang.annotations.JdkConstants; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; @@ -87,7 +90,13 @@ import java.util.List; public class CtrlMouseHandler extends AbstractProjectComponent { - private static final int ourQuickDocRowsNumber = getIntProperty("quick.doc.desired.rows.number", 1); + public static final DataKey> + ELEMENT_UNDER_MOUSE_INFO_KEY = DataKey.create("ElementUnderMouseInfo"); + + private static final AnAction[] ourTooltipActions = { + new ShowQuickDocFromTooltipAction(), new ShowQuickDocAtPinnedWindowFromTooltipAction() + }; + private static final int ourQuickDocRowsNumber = getIntProperty("quick.doc.desired.rows.number", 1); private final TextAttributes ourReferenceAttributes; private HighlightersSet myHighlighter; @@ -276,7 +285,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent { String fullText = documentationProvider.generateDoc(element, atPointer); String qName = element instanceof PsiQualifiedNamedElement ? ((PsiQualifiedNamedElement)element).getQualifiedName() : null; String text = DocPreviewUtil.buildPreview(result, qName, fullText, ourQuickDocRowsNumber); - return new DocInfo(text, documentationProvider, atPointer); + return new DocInfo(text, documentationProvider, element); } return DocInfo.EMPTY; } @@ -638,18 +647,43 @@ public class CtrlMouseHandler extends AbstractProjectComponent { info.showDocInfo(myDocumentationManager); } - HyperlinkListener listener = (docInfo.docProvider == null || docInfo.context == null) + HyperlinkListener hyperlinkListener = docInfo.docProvider == null ? null - : new QuickDocHyperlinkListener(myProject, myDocumentationManager, docInfo.docProvider, docInfo.context); - JComponent label = HintUtil.createInformationLabel(docInfo.text, listener); - final LightweightHint hint = new LightweightHint(label); + : new QuickDocHyperlinkListener(myProject, myDocumentationManager, docInfo.docProvider, + info.myElementAtPointer); + final Ref quickDocPaneRef = new Ref(); + MouseListener mouseListener = new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + QuickDocInfoPane pane = quickDocPaneRef.get(); + if (pane != null) { + pane.mouseEntered(e); + } + } + + @Override + public void mouseExited(MouseEvent e) { + QuickDocInfoPane pane = quickDocPaneRef.get(); + if (pane != null) { + pane.mouseExited(e); + } + } + }; + JComponent label = HintUtil.createInformationLabel(docInfo.text, hyperlinkListener, mouseListener); + QuickDocInfoPane quickDocPane = null; + if (docInfo.documentationAnchor != null) { + quickDocPane = new QuickDocInfoPane(docInfo.documentationAnchor, info.myElementAtPointer, label); + quickDocPaneRef.set(quickDocPane); + } + + JComponent hintContent = quickDocPane == null ? label : quickDocPane; + final LightweightHint hint = new LightweightHint(hintContent); final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl(); Point p = HintManagerImpl.getHintPosition(hint, myEditor, myPosition, HintManager.ABOVE); hintManager.showEditorHint(hint, myEditor, p, HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, 0, false, HintManagerImpl.createHintHint(myEditor, p, hint, HintManager.ABOVE).setContentActive(false)); } - } private HighlightersSet installHighlighterSet(Info info, Editor editor) { @@ -708,12 +742,88 @@ public class CtrlMouseHandler extends AbstractProjectComponent { @Nullable public final String text; @Nullable public final DocumentationProvider docProvider; - @Nullable public final PsiElement context; + @Nullable public final PsiElement documentationAnchor; - DocInfo(@Nullable String text, @Nullable DocumentationProvider provider, @Nullable PsiElement context) { + DocInfo(@Nullable String text, @Nullable DocumentationProvider provider, @Nullable PsiElement documentationAnchor) { this.text = text; docProvider = provider; - this.context = context; + this.documentationAnchor = documentationAnchor; + } + } + + private class QuickDocInfoPane extends JLayeredPane implements DataProvider { + + @NotNull private final List myButtons = new ArrayList(); + @NotNull private final Pair myElementUnderMouseInfo; + @NotNull private final JComponent myBaseDocControl; + + QuickDocInfoPane(@NotNull PsiElement documentationAnchor, @NotNull PsiElement elementUnderMouse, @NotNull JComponent baseDocControl) { + myElementUnderMouseInfo = Pair.create(documentationAnchor, elementUnderMouse); + myBaseDocControl = baseDocControl; + + PresentationFactory presentationFactory = new PresentationFactory(); + for (AnAction action : ourTooltipActions) { + Icon icon = action.getTemplatePresentation().getIcon(); + Dimension minSize = new Dimension(icon.getIconWidth(), icon.getIconHeight()); + myButtons.add(new ActionButton(action, presentationFactory.getPresentation(action), IdeTooltipManager.IDE_TOOLTIP_PLACE, minSize)); + } + Collections.reverse(myButtons); + + setPreferredSize(baseDocControl.getPreferredSize()); + setMaximumSize(baseDocControl.getMaximumSize()); + setMinimumSize(baseDocControl.getMinimumSize()); + setBackground(baseDocControl.getBackground()); + + add(baseDocControl, Integer.valueOf(0)); + for (JComponent button : myButtons) { + button.setBorder(null); + button.setBackground(baseDocControl.getBackground()); + add(button, Integer.valueOf(1)); + button.setVisible(false); + } + } + + @Override + public Object getData(@NonNls String dataId) { + return ELEMENT_UNDER_MOUSE_INFO_KEY.is(dataId) ? myElementUnderMouseInfo : null; + } + + @Override + public void doLayout() { + Rectangle bounds = getBounds(); + myBaseDocControl.setBounds(bounds); + + final int buttonsHGap = 5; + int x = bounds.width; + for (JComponent button : myButtons) { + Dimension buttonSize = button.getPreferredSize(); + x -= buttonSize.width; + button.setBounds(x, 0, buttonSize.width, buttonSize.height); + x -= buttonsHGap; + } + } + + public void mouseEntered(@NotNull MouseEvent e) { + processStateChangeIfNecessary(e.getLocationOnScreen(), true); + } + + public void mouseExited(@NotNull MouseEvent e) { + processStateChangeIfNecessary(e.getLocationOnScreen(), false); + } + + private void processStateChangeIfNecessary(@NotNull Point mouseScreenLocation, boolean mouseEntered) { + // Don't show 'view quick doc' buttons if docked quick doc control is already active. + if (myDocumentationManager.hasActiveDockedDocWindow()) { + return; + } + + // Skip event triggered when mouse leaves action button area. + if (!mouseEntered && new Rectangle(getLocationOnScreen(), getSize()).contains(mouseScreenLocation)) { + return; + } + for (JComponent button : myButtons) { + button.setVisible(mouseEntered); + } } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/ShowQuickDocAtPinnedWindowFromTooltipAction.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/ShowQuickDocAtPinnedWindowFromTooltipAction.java new file mode 100644 index 000000000000..3e3652cd9267 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/ShowQuickDocAtPinnedWindowFromTooltipAction.java @@ -0,0 +1,38 @@ +/* + * 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.navigation; + +import com.intellij.codeInsight.documentation.DocumentationManager; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +/** + * @author Denis Zhdanov + * @since 7/13/12 11:43 AM + */ +public class ShowQuickDocAtPinnedWindowFromTooltipAction extends ShowQuickDocFromTooltipAction { + + public ShowQuickDocAtPinnedWindowFromTooltipAction() { + super(AllIcons.General.Pin_tab); + } + + @Override + protected void doActionPerformed(@NotNull Pair docInfo, @NotNull DocumentationManager docManager) { + docManager.createToolWindow(docInfo.first, docInfo.second); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/ShowQuickDocFromTooltipAction.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/ShowQuickDocFromTooltipAction.java new file mode 100644 index 000000000000..c044e8b9ec98 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/ShowQuickDocFromTooltipAction.java @@ -0,0 +1,108 @@ +/* + * 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.navigation; + +import com.intellij.codeInsight.documentation.DocumentationManager; +import com.intellij.icons.AllIcons; +import com.intellij.ide.DataManager; +import com.intellij.ide.IdeTooltip; +import com.intellij.ide.IdeTooltipManager; +import com.intellij.idea.ActionsBundle; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.lang.ref.WeakReference; + +/** + * @author Denis Zhdanov + * @since 7/13/12 10:00 AM + */ +public class ShowQuickDocFromTooltipAction extends AnAction { + + @NotNull private final IdeTooltipManager myTooltipManager = IdeTooltipManager.getInstance(); + @NotNull private final DataManager myDataManager = DataManager.getInstance(); + + private WeakReference> myInfo; + + public ShowQuickDocFromTooltipAction() { + this(AllIcons.Actions.Find); + } + + public ShowQuickDocFromTooltipAction(@NotNull Icon icon) { + String className = getClass().getName(); + String actionId = className.substring(0, className.lastIndexOf("Action")); + getTemplatePresentation().setText(ActionsBundle.actionText(actionId)); + getTemplatePresentation().setDescription(ActionsBundle.actionDescription(actionId)); + getTemplatePresentation().setIcon(icon); + } + + @Override + public void update(AnActionEvent e) { + + // We can't use data context from the given event because it's built from the focused component and IDE tooltip doesn't have focus. + IdeTooltip tooltip = myTooltipManager.getCurrentTooltip(); + if (tooltip == null) { + return; + } + + JComponent component = tooltip.getTipComponent(); + if (component == null) { + return; + } + + Pair info = CtrlMouseHandler.ELEMENT_UNDER_MOUSE_INFO_KEY.getData(myDataManager.getDataContext(component)); + if (info != null) { + // Target info is retrieved during AnAction.update() processing because IDE tooltip is closed on action activation, + // i.e. IdeTooltipManager.getCurrentComponent() returns null during AnAction.actionPerformed() execution. + myInfo = new WeakReference>(info); + } + } + + @Override + public void actionPerformed(AnActionEvent e) { + WeakReference> infoRef = myInfo; + if (infoRef == null) { + return; + } + Pair info = infoRef.get(); + if (info == null) { + return; + } + + Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext()); + if (project == null) { + return; + } + + myInfo = null; + doActionPerformed(info, DocumentationManager.getInstance(project)); + } + + protected void doActionPerformed(@NotNull Pair docInfo, + @NotNull DocumentationManager docManager) + { + docManager.showJavaDocInfo(docInfo.first, docInfo.second, true, null); + } +} diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java index d048ba4c5f11..fa155286244f 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java @@ -33,6 +33,9 @@ import javax.swing.border.EmptyBorder; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; public class HintUtil { public static final Color INFORMATION_COLOR = new Color(253, 254, 226); @@ -49,10 +52,13 @@ public class HintUtil { } public static JComponent createInformationLabel(@NotNull String text) { - return createInformationLabel(text, null); + return createInformationLabel(text, null, null); } - - public static JComponent createInformationLabel(@NotNull String text, @Nullable HyperlinkListener listener) { + + public static JComponent createInformationLabel(@NotNull String text, + @Nullable HyperlinkListener hyperlinkListener, + @Nullable MouseListener mouseListener) + { HintHint hintHint = new HintHint().setTextBg(INFORMATION_COLOR).setTextFg(Color.black).setFont(getBoldFont()).setAwtTooltip(true); HintLabel label = new HintLabel(); @@ -67,8 +73,11 @@ public class HintUtil { label.setOpaque(true); } - if (listener != null) { - label.myPane.addHyperlinkListener(listener); + if (hyperlinkListener != null) { + label.myPane.addHyperlinkListener(hyperlinkListener); + } + if (mouseListener != null) { + label.myPane.addMouseListener(mouseListener); } return label; diff --git a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java index 9a80da99d7f0..f033ac7a01b2 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java @@ -57,6 +57,8 @@ import java.awt.event.MouseEvent; public class IdeTooltipManager implements ApplicationComponent, AWTEventListener { + public static final String IDE_TOOLTIP_PLACE = "IdeTooltip"; + public static final Color GRAPHITE_COLOR = new Color(100, 100, 100, 230); private RegistryValue myIsEnabled; @@ -64,8 +66,8 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener private Component myQueuedComponent; private BalloonImpl myCurrentTipUi; - private MouseEvent myCurrentEvent; - private boolean myCurrentTipIsCentered; + private MouseEvent myCurrentEvent; + private boolean myCurrentTipIsCentered; private Runnable myHideRunnable; @@ -75,12 +77,12 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener private final Alarm myAlarm = new Alarm(); - private int myX; - private int myY; + private int myX; + private int myY; private RegistryValue myMode; private IdeTooltip myCurrentTooltip; - private Runnable myShowRequest; + private Runnable myShowRequest; private IdeTooltip myQueuedTooltip; @@ -341,7 +343,11 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener } }, tooltip.getDismissDelay()); } - + + @Nullable + public IdeTooltip getCurrentTooltip() { + return myCurrentTooltip; + } public Color getTextForeground(boolean awtTooltip) { return useGraphite(awtTooltip) ? Color.white : UIUtil.getToolTipForeground(); 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 476863b6adaf..0be8fe3d7f4d 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 @@ -19,7 +19,6 @@ 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; @@ -51,8 +50,6 @@ 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 long QUICK_DOC_ON_MOUSE_OVER_DELAY_MS = 500; public boolean IS_CARET_BLINKING = true; public int CARET_BLINKING_PERIOD = 500; public boolean IS_RIGHT_MARGIN_SHOWN = true; @@ -368,28 +365,6 @@ 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 long getQuickDocOnMouseOverElementDelayMillis() { - return myOptions.QUICK_DOC_ON_MOUSE_OVER_DELAY_MS; - } - - public void setQuickDocOnMouseOverElementDelayMillis(long delay) throws IllegalArgumentException { - if (delay <= 0) { - throw new IllegalArgumentException(String.format( - "Non-positive delay for the 'show quick doc on mouse over element' value detected! Expected positive value but got %d", - delay - )); - } - myOptions.QUICK_DOC_ON_MOUSE_OVER_DELAY_MS = delay; - } - public boolean isRefrainFromScrolling() { return myOptions.REFRAIN_FROM_SCROLLING; } diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 4f3403375faf..a4d30ecdfa98 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -201,6 +201,10 @@ action.CompareTwoFiles.text=Compare Two _Files action.CompareTwoFiles.description=Compare two selected files action.CompareFileWithEditor.text=Co_mpare File with Editor action.CompareFileWithEditor.description=Compare selected file with editor +action.ShowQuickDocFromTooltip.text=Full documentation +action.ShowQuickDocFromTooltip.description=Show full documentation +action.ShowQuickDocAtPinnedWindowFromTooltip.text=Full documentation in pinned window +action.ShowQuickDocAtPinnedWindowFromTooltip.description=Show full documentation in pinned window group.LocalHistory.text=Local _History action.LocalHistory.ShowHistory.text=Show _History diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 9e224fcc49ed..d051b1750bac 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -734,9 +734,6 @@ serviceImplementation="com.intellij.ide.todo.TodoConfiguration"/> - - -