129
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2007, 2008 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Port to the D programming language:
|
|
11 * Frank Benoit <benoit@tionex.de>
|
|
12 *******************************************************************************/
|
|
13 module dwtx.jface.internal.text.StickyHoverManager;
|
|
14
|
|
15 import dwt.dwthelper.utils;
|
|
16
|
|
17
|
|
18
|
|
19 import dwt.DWT;
|
|
20 import dwt.events.ControlEvent;
|
|
21 import dwt.events.ControlListener;
|
|
22 import dwt.events.FocusEvent;
|
|
23 import dwt.events.FocusListener;
|
|
24 import dwt.events.KeyEvent;
|
|
25 import dwt.events.KeyListener;
|
|
26 import dwt.events.MouseEvent;
|
|
27 import dwt.events.MouseListener;
|
|
28 import dwt.graphics.Point;
|
|
29 import dwt.graphics.Rectangle;
|
|
30 import dwt.widgets.Control;
|
|
31 import dwt.widgets.Display;
|
|
32 import dwt.widgets.Event;
|
|
33 import dwt.widgets.Listener;
|
|
34 import dwtx.jface.text.IInformationControl;
|
|
35 import dwtx.jface.text.IInformationControlExtension3;
|
|
36 import dwtx.jface.text.IInformationControlExtension5;
|
|
37 import dwtx.jface.text.IViewportListener;
|
|
38 import dwtx.jface.text.IWidgetTokenKeeper;
|
|
39 import dwtx.jface.text.IWidgetTokenKeeperExtension;
|
|
40 import dwtx.jface.text.IWidgetTokenOwner;
|
|
41 import dwtx.jface.text.TextViewer;
|
|
42 import dwtx.jface.util.Geometry;
|
|
43
|
|
44
|
|
45 /**
|
|
46 * Implements a sticky hover control, i.e. a control that replaces a hover
|
|
47 * with an enriched and focusable control.
|
|
48 * <p>
|
|
49 * The information control is made visible on request by calling
|
|
50 * {@link #showInformationControl(Rectangle)}.
|
|
51 * </p>
|
|
52 * <p>
|
|
53 * Clients usually instantiate and configure this class before using it. The configuration
|
|
54 * must be consistent: This means the used {@link dwtx.jface.text.IInformationControlCreator}
|
|
55 * must create an information control expecting information in the same format the configured
|
|
56 * {@link dwtx.jface.text.information.IInformationProvider}s use to encode the information they provide.
|
|
57 * </p>
|
|
58 *
|
|
59 * @since 3.4
|
|
60 */
|
|
61 public class StickyHoverManager : InformationControlReplacer , IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
|
|
62
|
|
63 /**
|
|
64 * Priority of the info controls managed by this sticky hover manager.
|
|
65 * <p>
|
|
66 * Note: Only applicable when info control does not have focus.
|
|
67 * -5 as value has been chosen in order to be beaten by the hovers of TextViewerHoverManager.
|
|
68 * </p>
|
|
69 */
|
|
70 private static final int WIDGET_PRIORITY= -5;
|
|
71
|
|
72
|
|
73 /**
|
|
74 * Internal information control closer. Listens to several events issued by its subject control
|
|
75 * and closes the information control when necessary.
|
|
76 */
|
|
77 class Closer : IInformationControlCloser, ControlListener, MouseListener, IViewportListener, KeyListener, FocusListener, Listener {
|
|
78 //TODO: Catch 'Esc' key in fInformationControlToClose: Don't dispose, just hideInformationControl().
|
|
79 // This would allow to reuse the information control also when the user explicitly closes it.
|
|
80
|
|
81 //TODO: if subject control is a Scrollable, should add selection listeners to both scroll bars
|
|
82 // (and remove the ViewPortListener, which only listens to vertical scrolling)
|
|
83
|
|
84 /** The subject control. */
|
|
85 private Control fSubjectControl;
|
|
86 /** Indicates whether this closer is active. */
|
|
87 private bool fIsActive= false;
|
|
88 /** The display. */
|
|
89 private Display fDisplay;
|
|
90
|
|
91 /*
|
|
92 * @see IInformationControlCloser#setSubjectControl(Control)
|
|
93 */
|
|
94 public void setSubjectControl(Control control) {
|
|
95 fSubjectControl= control;
|
|
96 }
|
|
97
|
|
98 /*
|
|
99 * @see IInformationControlCloser#setInformationControl(IInformationControl)
|
|
100 */
|
|
101 public void setInformationControl(IInformationControl control) {
|
|
102 // NOTE: we use getCurrentInformationControl2() from the outer class
|
|
103 }
|
|
104
|
|
105 /*
|
|
106 * @see IInformationControlCloser#start(Rectangle)
|
|
107 */
|
|
108 public void start(Rectangle informationArea) {
|
|
109
|
|
110 if (fIsActive)
|
|
111 return;
|
|
112 fIsActive= true;
|
|
113
|
|
114 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
|
|
115 fSubjectControl.addControlListener(this);
|
|
116 fSubjectControl.addMouseListener(this);
|
|
117 fSubjectControl.addKeyListener(this);
|
|
118 }
|
|
119
|
|
120 fTextViewer.addViewportListener(this);
|
|
121
|
|
122 IInformationControl fInformationControlToClose= getCurrentInformationControl2();
|
|
123 if (fInformationControlToClose !is null)
|
|
124 fInformationControlToClose.addFocusListener(this);
|
|
125
|
|
126 fDisplay= fSubjectControl.getDisplay();
|
|
127 if (!fDisplay.isDisposed()) {
|
|
128 fDisplay.addFilter(DWT.MouseMove, this);
|
|
129 fDisplay.addFilter(DWT.FocusOut, this);
|
|
130 }
|
|
131 }
|
|
132
|
|
133 /*
|
|
134 * @see IInformationControlCloser#stop()
|
|
135 */
|
|
136 public void stop() {
|
|
137
|
|
138 if (!fIsActive)
|
|
139 return;
|
|
140 fIsActive= false;
|
|
141
|
|
142 fTextViewer.removeViewportListener(this);
|
|
143
|
|
144 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
|
|
145 fSubjectControl.removeControlListener(this);
|
|
146 fSubjectControl.removeMouseListener(this);
|
|
147 fSubjectControl.removeKeyListener(this);
|
|
148 }
|
|
149
|
|
150 IInformationControl fInformationControlToClose= getCurrentInformationControl2();
|
|
151 if (fInformationControlToClose !is null)
|
|
152 fInformationControlToClose.removeFocusListener(this);
|
|
153
|
|
154 if (fDisplay !is null && !fDisplay.isDisposed()) {
|
|
155 fDisplay.removeFilter(DWT.MouseMove, this);
|
|
156 fDisplay.removeFilter(DWT.FocusOut, this);
|
|
157 }
|
|
158
|
|
159 fDisplay= null;
|
|
160 }
|
|
161
|
|
162 /*
|
|
163 * @see ControlListener#controlResized(ControlEvent)
|
|
164 */
|
|
165 public void controlResized(ControlEvent e) {
|
|
166 hideInformationControl();
|
|
167 }
|
|
168
|
|
169 /*
|
|
170 * @see ControlListener#controlMoved(ControlEvent)
|
|
171 */
|
|
172 public void controlMoved(ControlEvent e) {
|
|
173 hideInformationControl();
|
|
174 }
|
|
175
|
|
176 /*
|
|
177 * @see MouseListener#mouseDown(MouseEvent)
|
|
178 */
|
|
179 public void mouseDown(MouseEvent e) {
|
|
180 hideInformationControl();
|
|
181 }
|
|
182
|
|
183 /*
|
|
184 * @see MouseListener#mouseUp(MouseEvent)
|
|
185 */
|
|
186 public void mouseUp(MouseEvent e) {
|
|
187 }
|
|
188
|
|
189 /*
|
|
190 * @see MouseListener#mouseDoubleClick(MouseEvent)
|
|
191 */
|
|
192 public void mouseDoubleClick(MouseEvent e) {
|
|
193 hideInformationControl();
|
|
194 }
|
|
195
|
|
196 /*
|
|
197 * @see IViewportListenerListener#viewportChanged(int)
|
|
198 */
|
|
199 public void viewportChanged(int topIndex) {
|
|
200 hideInformationControl();
|
|
201 }
|
|
202
|
|
203 /*
|
|
204 * @see KeyListener#keyPressed(KeyEvent)
|
|
205 */
|
|
206 public void keyPressed(KeyEvent e) {
|
|
207 hideInformationControl();
|
|
208 }
|
|
209
|
|
210 /*
|
|
211 * @see KeyListener#keyReleased(KeyEvent)
|
|
212 */
|
|
213 public void keyReleased(KeyEvent e) {
|
|
214 }
|
|
215
|
|
216 /*
|
|
217 * @see dwt.events.FocusListener#focusGained(dwt.events.FocusEvent)
|
|
218 */
|
|
219 public void focusGained(FocusEvent e) {
|
|
220 }
|
|
221
|
|
222 /*
|
|
223 * @see dwt.events.FocusListener#focusLost(dwt.events.FocusEvent)
|
|
224 */
|
|
225 public void focusLost(FocusEvent e) {
|
|
226 if (DEBUG) System.out.println("StickyHoverManager.Closer.focusLost(): " + e); //$NON-NLS-1$
|
|
227 Display d= fSubjectControl.getDisplay();
|
|
228 d.asyncExec(new Runnable() {
|
|
229 // Without the asyncExec, mouse clicks to the workbench window are swallowed.
|
|
230 public void run() {
|
|
231 hideInformationControl();
|
|
232 }
|
|
233 });
|
|
234 }
|
|
235
|
|
236 /*
|
|
237 * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event)
|
|
238 */
|
|
239 public void handleEvent(Event event) {
|
|
240 if (event.type is DWT.MouseMove) {
|
|
241 if (!(event.widget instanceof Control) || event.widget.isDisposed())
|
|
242 return;
|
|
243
|
|
244 IInformationControl infoControl= getCurrentInformationControl2();
|
|
245 if (infoControl !is null && !infoControl.isFocusControl() && infoControl instanceof IInformationControlExtension3) {
|
|
246 // if (DEBUG) System.out.println("StickyHoverManager.Closer.handleEvent(): activeShell= " + fDisplay.getActiveShell()); //$NON-NLS-1$
|
|
247 IInformationControlExtension3 iControl3= (IInformationControlExtension3) infoControl;
|
|
248 Rectangle controlBounds= iControl3.getBounds();
|
|
249 if (controlBounds !is null) {
|
|
250 Point mouseLoc= event.display.map((Control) event.widget, null, event.x, event.y);
|
|
251 int margin= getKeepUpMargin();
|
|
252 Geometry.expand(controlBounds, margin, margin, margin, margin);
|
|
253 if (!controlBounds.contains(mouseLoc)) {
|
|
254 hideInformationControl();
|
|
255 }
|
|
256 }
|
|
257
|
|
258 } else {
|
|
259 /*
|
|
260 * TODO: need better understanding of why/if this is needed.
|
|
261 * Looks like the same panic code we have in dwtx.jface.text.AbstractHoverInformationControlManager.Closer.handleMouseMove(Event)
|
|
262 */
|
|
263 if (fDisplay !is null && !fDisplay.isDisposed())
|
|
264 fDisplay.removeFilter(DWT.MouseMove, this);
|
|
265 }
|
|
266
|
|
267 } else if (event.type is DWT.FocusOut) {
|
|
268 if (DEBUG) System.out.println("StickyHoverManager.Closer.handleEvent(): focusOut: " + event); //$NON-NLS-1$
|
|
269 IInformationControl iControl= getCurrentInformationControl2();
|
|
270 if (iControl !is null && ! iControl.isFocusControl())
|
|
271 hideInformationControl();
|
|
272 }
|
|
273 }
|
|
274 }
|
|
275
|
|
276
|
|
277 private final TextViewer fTextViewer;
|
|
278
|
|
279
|
|
280 /**
|
|
281 * Creates a new sticky hover manager.
|
|
282 *
|
|
283 * @param textViewer the text viewer
|
|
284 */
|
|
285 public StickyHoverManager(TextViewer textViewer) {
|
|
286 super(new DefaultInformationControlCreator());
|
|
287
|
|
288 fTextViewer= textViewer;
|
|
289 setCloser(new Closer());
|
|
290
|
|
291 install(fTextViewer.getTextWidget());
|
|
292 }
|
|
293
|
|
294 /*
|
|
295 * @see AbstractInformationControlManager#showInformationControl(Rectangle)
|
|
296 */
|
|
297 protected void showInformationControl(Rectangle subjectArea) {
|
|
298 if (fTextViewer !is null && fTextViewer.requestWidgetToken(this, WIDGET_PRIORITY))
|
|
299 super.showInformationControl(subjectArea);
|
|
300 else
|
|
301 if (DEBUG)
|
|
302 System.out.println("cancelled StickyHoverManager.showInformationControl(..): did not get widget token (with prio)"); //$NON-NLS-1$
|
|
303 }
|
|
304
|
|
305 /*
|
|
306 * @see AbstractInformationControlManager#hideInformationControl()
|
|
307 */
|
|
308 public void hideInformationControl() {
|
|
309 try {
|
|
310 super.hideInformationControl();
|
|
311 } finally {
|
|
312 if (fTextViewer !is null)
|
|
313 fTextViewer.releaseWidgetToken(this);
|
|
314 }
|
|
315 }
|
|
316
|
|
317 /*
|
|
318 * @see AbstractInformationControlManager#handleInformationControlDisposed()
|
|
319 */
|
|
320 protected void handleInformationControlDisposed() {
|
|
321 try {
|
|
322 super.handleInformationControlDisposed();
|
|
323 } finally {
|
|
324 if (fTextViewer !is null)
|
|
325 fTextViewer.releaseWidgetToken(this);
|
|
326 }
|
|
327 }
|
|
328
|
|
329 /*
|
|
330 * @see dwtx.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
|
|
331 */
|
|
332 public bool requestWidgetToken(IWidgetTokenOwner owner) {
|
|
333 hideInformationControl();
|
|
334 if (DEBUG)
|
|
335 System.out.println("StickyHoverManager gave up widget token (no prio)"); //$NON-NLS-1$
|
|
336 return true;
|
|
337 }
|
|
338
|
|
339 /*
|
|
340 * @see dwtx.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner, int)
|
|
341 */
|
|
342 public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
|
|
343 if (getCurrentInformationControl2() !is null) {
|
|
344 if (getCurrentInformationControl2().isFocusControl()) {
|
|
345 if (DEBUG)
|
|
346 System.out.println("StickyHoverManager kept widget token (focused)"); //$NON-NLS-1$
|
|
347 return false;
|
|
348 } else if (priority > WIDGET_PRIORITY) {
|
|
349 hideInformationControl();
|
|
350 if (DEBUG)
|
|
351 System.out.println("StickyHoverManager gave up widget token (prio)"); //$NON-NLS-1$
|
|
352 return true;
|
|
353 } else {
|
|
354 if (DEBUG)
|
|
355 System.out.println("StickyHoverManager kept widget token (prio)"); //$NON-NLS-1$
|
|
356 return false;
|
|
357 }
|
|
358 }
|
|
359 if (DEBUG)
|
|
360 System.out.println("StickyHoverManager gave up widget token (no iControl)"); //$NON-NLS-1$
|
|
361 return true;
|
|
362 }
|
|
363
|
|
364 /*
|
|
365 * @see dwtx.jface.text.IWidgetTokenKeeperExtension#setFocus(dwtx.jface.text.IWidgetTokenOwner)
|
|
366 */
|
|
367 public bool setFocus(IWidgetTokenOwner owner) {
|
|
368 IInformationControl iControl= getCurrentInformationControl2();
|
|
369 if (iControl instanceof IInformationControlExtension5) {
|
|
370 IInformationControlExtension5 iControl5= (IInformationControlExtension5) iControl;
|
|
371 if (iControl5.isVisible()) {
|
|
372 iControl.setFocus();
|
|
373 return iControl.isFocusControl();
|
|
374 }
|
|
375 return false;
|
|
376 }
|
|
377 iControl.setFocus();
|
|
378 return iControl.isFocusControl();
|
|
379 }
|
|
380
|
|
381 }
|