IDEA-141464 Debugger popup dissappears when trying to hover mouse on "+"
[idea/community.git] / platform / xdebugger-impl / src / com / intellij / xdebugger / impl / evaluate / quick / common / ValueLookupManager.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * Class ValueLookupManager
19  * @author Jeka
20  */
21 package com.intellij.xdebugger.impl.evaluate.quick.common;
22
23 import com.intellij.openapi.components.ServiceManager;
24 import com.intellij.openapi.editor.Editor;
25 import com.intellij.openapi.editor.EditorFactory;
26 import com.intellij.openapi.editor.event.EditorMouseAdapter;
27 import com.intellij.openapi.editor.event.EditorMouseEvent;
28 import com.intellij.openapi.editor.event.EditorMouseEventArea;
29 import com.intellij.openapi.editor.event.EditorMouseMotionListener;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.util.Key;
32 import com.intellij.openapi.util.registry.Registry;
33 import com.intellij.util.Alarm;
34 import com.intellij.xdebugger.impl.DebuggerSupport;
35 import org.jetbrains.annotations.NotNull;
36
37 import java.awt.*;
38
39 public class ValueLookupManager extends EditorMouseAdapter implements EditorMouseMotionListener {
40   /**
41    * @see com.intellij.xdebugger.XDebuggerUtil#disableValueLookup(com.intellij.openapi.editor.Editor)
42    */
43   public static final Key<Boolean> DISABLE_VALUE_LOOKUP = Key.create("DISABLE_VALUE_LOOKUP");
44
45   private final Project myProject;
46   private final Alarm myAlarm;
47   private AbstractValueHint myRequest = null;
48   private final DebuggerSupport[] mySupports;
49   private boolean myListening;
50
51   public ValueLookupManager(Project project) {
52     myProject = project;
53     mySupports = DebuggerSupport.getDebuggerSupports();
54     myAlarm = new Alarm(project);
55   }
56
57   public void startListening() {
58     if (!myListening) {
59       myListening = true;
60       EditorFactory.getInstance().getEventMulticaster().addEditorMouseMotionListener(this, myProject);
61       EditorFactory.getInstance().getEventMulticaster().addEditorMouseListener(this, myProject);
62     }
63   }
64
65   @Override
66   public void mouseDragged(EditorMouseEvent e) {
67   }
68
69   @Override
70   public void mouseExited(EditorMouseEvent e) {
71     myAlarm.cancelAllRequests();
72   }
73
74   @Override
75   public void mouseMoved(EditorMouseEvent e) {
76     if (e.isConsumed()) {
77       return;
78     }
79
80     Editor editor = e.getEditor();
81     if (editor.getProject() != null && editor.getProject() != myProject) {
82       return;
83     }
84
85     ValueHintType type = AbstractValueHint.getHintType(e);
86     if (e.getArea() != EditorMouseEventArea.EDITING_AREA ||
87         DISABLE_VALUE_LOOKUP.get(editor) == Boolean.TRUE ||
88         type == null) {
89       myAlarm.cancelAllRequests();
90       return;
91     }
92
93     Point point = e.getMouseEvent().getPoint();
94     if (myRequest != null && !myRequest.isKeepHint(editor, point)) {
95       hideHint();
96     }
97     else if (type == ValueHintType.MOUSE_OVER_HINT && myRequest != null && !myRequest.isHintHidden() && myRequest.isInsideCurrentRange(editor, point)) {
98       return;
99     }
100
101     for (DebuggerSupport support : mySupports) {
102       QuickEvaluateHandler handler = support.getQuickEvaluateHandler();
103       if (handler.isEnabled(myProject)) {
104         requestHint(handler, editor, point, type);
105         break;
106       }
107     }
108   }
109
110   private void requestHint(final QuickEvaluateHandler handler, final Editor editor, final Point point, @NotNull final ValueHintType type) {
111     final Rectangle area = editor.getScrollingModel().getVisibleArea();
112     myAlarm.cancelAllRequests();
113     if (type == ValueHintType.MOUSE_OVER_HINT) {
114       if (Registry.is("debugger.valueTooltipAutoShow")) {
115         myAlarm.addRequest(new Runnable() {
116           @Override
117           public void run() {
118             if (area.equals(editor.getScrollingModel().getVisibleArea())) {
119               showHint(handler, editor, point, type);
120             }
121           }
122         }, getDelay(handler));
123       }
124     }
125     else {
126       showHint(handler, editor, point, type);
127     }
128   }
129
130   private int getDelay(QuickEvaluateHandler handler) {
131     int delay = handler.getValueLookupDelay(myProject);
132     if (myRequest != null && !myRequest.isHintHidden()) {
133       delay = Math.max(100, delay); // if hint is showing, delay should not be too small, see IDEA-141464
134     }
135     return delay;
136   }
137
138   public void hideHint() {
139     if (myRequest != null) {
140       myRequest.hideHint();
141       myRequest = null;
142     }
143   }
144
145   public void showHint(@NotNull QuickEvaluateHandler handler, @NotNull Editor editor, @NotNull Point point, @NotNull ValueHintType type) {
146     myAlarm.cancelAllRequests();
147     if (editor.isDisposed() || !handler.canShowHint(myProject)) {
148       return;
149     }
150
151     final AbstractValueHint request = handler.createValueHint(myProject, editor, point, type);
152     if (request != null) {
153       if (myRequest != null && myRequest.equals(request)) {
154         return;
155       }
156
157       if (!request.canShowHint()) {
158         return;
159       }
160       if (myRequest != null && myRequest.isInsideHint(editor, point)) {
161         return;
162       }
163
164       hideHint();
165
166       myRequest = request;
167       myRequest.invokeHint(new Runnable() {
168         @Override
169         public void run() {
170           if (myRequest != null && myRequest == request) {
171             myRequest = null;
172           }
173         }
174       });
175     }
176   }
177
178   public static ValueLookupManager getInstance(Project project) {
179     return ServiceManager.getService(project, ValueLookupManager.class);
180   }
181 }