Mercurial > projects > dwt-addons
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 } |