comparison dwtx/jface/internal/text/StickyHoverManager.d @ 129:eb30df5ca28b

Added JFace Text sources
author Frank Benoit <benoit@tionex.de>
date Sat, 23 Aug 2008 19:10:48 +0200
parents
children c4fb132a086c
comparison
equal deleted inserted replaced
128:8df1d4193877 129:eb30df5ca28b
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 }