Mercurial > projects > dwt-addons
comparison dwtx/jface/text/information/InformationPresenter.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) 2000, 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.text.information.InformationPresenter; | |
14 | |
15 import dwt.dwthelper.utils; | |
16 | |
17 import java.util.HashMap; | |
18 import java.util.Map; | |
19 | |
20 import dwt.custom.StyledText; | |
21 import dwt.events.ControlEvent; | |
22 import dwt.events.ControlListener; | |
23 import dwt.events.FocusEvent; | |
24 import dwt.events.FocusListener; | |
25 import dwt.events.KeyEvent; | |
26 import dwt.events.KeyListener; | |
27 import dwt.events.MouseEvent; | |
28 import dwt.events.MouseListener; | |
29 import dwt.graphics.Point; | |
30 import dwt.graphics.Rectangle; | |
31 import dwt.widgets.Control; | |
32 import dwt.widgets.Display; | |
33 import dwtx.core.runtime.Assert; | |
34 import dwtx.jface.text.AbstractInformationControlManager; | |
35 import dwtx.jface.text.BadLocationException; | |
36 import dwtx.jface.text.IDocumentExtension3; | |
37 import dwtx.jface.text.IInformationControl; | |
38 import dwtx.jface.text.IInformationControlCreator; | |
39 import dwtx.jface.text.IRegion; | |
40 import dwtx.jface.text.ITextViewer; | |
41 import dwtx.jface.text.ITextViewerExtension5; | |
42 import dwtx.jface.text.IViewportListener; | |
43 import dwtx.jface.text.IWidgetTokenKeeper; | |
44 import dwtx.jface.text.IWidgetTokenKeeperExtension; | |
45 import dwtx.jface.text.IWidgetTokenOwner; | |
46 import dwtx.jface.text.IWidgetTokenOwnerExtension; | |
47 import dwtx.jface.text.Region; | |
48 import dwtx.jface.text.TextUtilities; | |
49 | |
50 | |
51 /** | |
52 * Standard implementation of <code>IInformationPresenter</code>. | |
53 * This implementation extends <code>AbstractInformationControlManager</code>. | |
54 * The information control is made visible on request by calling | |
55 * {@link #showInformationControl(Rectangle)}. | |
56 * <p> | |
57 * Usually, clients instantiate this class and configure it before using it. The configuration | |
58 * must be consistent: This means the used {@link dwtx.jface.text.IInformationControlCreator} | |
59 * must create an information control expecting information in the same format the configured | |
60 * {@link dwtx.jface.text.information.IInformationProvider}s use to encode the information they provide. | |
61 * </p> | |
62 * | |
63 * @since 2.0 | |
64 */ | |
65 public class InformationPresenter : AbstractInformationControlManager , IInformationPresenter, IInformationPresenterExtension, IWidgetTokenKeeper, IWidgetTokenKeeperExtension { | |
66 | |
67 | |
68 /** | |
69 * Priority of the info controls managed by this information presenter. | |
70 * Default value: <code>5</code>. | |
71 * | |
72 * @since 3.0 | |
73 */ | |
74 /* | |
75 * 5 as value has been chosen in order to beat the hovers of {@link dwtx.jface.text.TextViewerHoverManager} | |
76 */ | |
77 public static final int WIDGET_PRIORITY= 5; | |
78 | |
79 | |
80 /** | |
81 * Internal information control closer. Listens to several events issued by its subject control | |
82 * and closes the information control when necessary. | |
83 */ | |
84 class Closer : IInformationControlCloser, ControlListener, MouseListener, FocusListener, IViewportListener, KeyListener { | |
85 | |
86 /** The subject control. */ | |
87 private Control fSubjectControl; | |
88 /** The information control. */ | |
89 private IInformationControl fInformationControlToClose; | |
90 /** Indicates whether this closer is active. */ | |
91 private bool fIsActive= false; | |
92 | |
93 /* | |
94 * @see IInformationControlCloser#setSubjectControl(Control) | |
95 */ | |
96 public void setSubjectControl(Control control) { | |
97 fSubjectControl= control; | |
98 } | |
99 | |
100 /* | |
101 * @see IInformationControlCloser#setInformationControl(IInformationControl) | |
102 */ | |
103 public void setInformationControl(IInformationControl control) { | |
104 fInformationControlToClose= control; | |
105 } | |
106 | |
107 /* | |
108 * @see IInformationControlCloser#start(Rectangle) | |
109 */ | |
110 public void start(Rectangle informationArea) { | |
111 | |
112 if (fIsActive) | |
113 return; | |
114 fIsActive= true; | |
115 | |
116 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) { | |
117 fSubjectControl.addControlListener(this); | |
118 fSubjectControl.addMouseListener(this); | |
119 fSubjectControl.addFocusListener(this); | |
120 fSubjectControl.addKeyListener(this); | |
121 } | |
122 | |
123 if (fInformationControlToClose !is null) | |
124 fInformationControlToClose.addFocusListener(this); | |
125 | |
126 fTextViewer.addViewportListener(this); | |
127 } | |
128 | |
129 /* | |
130 * @see IInformationControlCloser#stop() | |
131 */ | |
132 public void stop() { | |
133 | |
134 if (!fIsActive) | |
135 return; | |
136 fIsActive= false; | |
137 | |
138 fTextViewer.removeViewportListener(this); | |
139 | |
140 if (fInformationControlToClose !is null) | |
141 fInformationControlToClose.removeFocusListener(this); | |
142 | |
143 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) { | |
144 fSubjectControl.removeControlListener(this); | |
145 fSubjectControl.removeMouseListener(this); | |
146 fSubjectControl.removeFocusListener(this); | |
147 fSubjectControl.removeKeyListener(this); | |
148 } | |
149 } | |
150 | |
151 /* | |
152 * @see ControlListener#controlResized(ControlEvent) | |
153 */ | |
154 public void controlResized(ControlEvent e) { | |
155 hideInformationControl(); | |
156 } | |
157 | |
158 /* | |
159 * @see ControlListener#controlMoved(ControlEvent) | |
160 */ | |
161 public void controlMoved(ControlEvent e) { | |
162 hideInformationControl(); | |
163 } | |
164 | |
165 /* | |
166 * @see MouseListener#mouseDown(MouseEvent) | |
167 */ | |
168 public void mouseDown(MouseEvent e) { | |
169 hideInformationControl(); | |
170 } | |
171 | |
172 /* | |
173 * @see MouseListener#mouseUp(MouseEvent) | |
174 */ | |
175 public void mouseUp(MouseEvent e) { | |
176 } | |
177 | |
178 /* | |
179 * @see MouseListener#mouseDoubleClick(MouseEvent) | |
180 */ | |
181 public void mouseDoubleClick(MouseEvent e) { | |
182 hideInformationControl(); | |
183 } | |
184 | |
185 /* | |
186 * @see FocusListener#focusGained(FocusEvent) | |
187 */ | |
188 public void focusGained(FocusEvent e) { | |
189 } | |
190 | |
191 /* | |
192 * @see FocusListener#focusLost(FocusEvent) | |
193 */ | |
194 public void focusLost(FocusEvent e) { | |
195 Display d= fSubjectControl.getDisplay(); | |
196 d.asyncExec(new Runnable() { | |
197 // Without the asyncExec, mouse clicks to the workbench window are swallowed. | |
198 public void run() { | |
199 if (fInformationControlToClose is null || !fInformationControlToClose.isFocusControl()) | |
200 hideInformationControl(); | |
201 } | |
202 }); | |
203 } | |
204 | |
205 /* | |
206 * @see IViewportListenerListener#viewportChanged(int) | |
207 */ | |
208 public void viewportChanged(int topIndex) { | |
209 hideInformationControl(); | |
210 } | |
211 | |
212 /* | |
213 * @see KeyListener#keyPressed(KeyEvent) | |
214 */ | |
215 public void keyPressed(KeyEvent e) { | |
216 hideInformationControl(); | |
217 } | |
218 | |
219 /* | |
220 * @see KeyListener#keyReleased(KeyEvent) | |
221 */ | |
222 public void keyReleased(KeyEvent e) { | |
223 } | |
224 } | |
225 | |
226 | |
227 /** The text viewer this information presenter works on */ | |
228 private ITextViewer fTextViewer; | |
229 /** The map of <code>IInformationProvider</code> objects */ | |
230 private Map fProviders; | |
231 /** The offset to override selection. */ | |
232 private int fOffset= -1; | |
233 /** | |
234 * The document partitioning for this information presenter. | |
235 * @since 3.0 | |
236 */ | |
237 private String fPartitioning; | |
238 | |
239 /** | |
240 * Creates a new information presenter that uses the given information control creator. | |
241 * The presenter is not installed on any text viewer yet. By default, an information | |
242 * control closer is set that closes the information control in the event of key strokes, | |
243 * resizing, moves, focus changes, mouse clicks, and disposal - all of those applied to | |
244 * the information control's parent control. Also, the setup ensures that the information | |
245 * control when made visible will request the focus. By default, the default document | |
246 * partitioning {@link IDocumentExtension3#DEFAULT_PARTITIONING} is used. | |
247 * | |
248 * @param creator the information control creator to be used | |
249 */ | |
250 public InformationPresenter(IInformationControlCreator creator) { | |
251 super(creator); | |
252 setCloser(new Closer()); | |
253 takesFocusWhenVisible(true); | |
254 fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING; | |
255 } | |
256 | |
257 /** | |
258 * Sets the document partitioning to be used by this information presenter. | |
259 * | |
260 * @param partitioning the document partitioning to be used by this information presenter | |
261 * @since 3.0 | |
262 */ | |
263 public void setDocumentPartitioning(String partitioning) { | |
264 Assert.isNotNull(partitioning); | |
265 fPartitioning= partitioning; | |
266 } | |
267 | |
268 /* | |
269 * @see dwtx.jface.text.information.IInformationPresenterExtension#getDocumentPartitioning() | |
270 * @since 3.0 | |
271 */ | |
272 public String getDocumentPartitioning() { | |
273 return fPartitioning; | |
274 } | |
275 | |
276 /** | |
277 * Registers a given information provider for a particular content type. | |
278 * If there is already a provider registered for this type, the new provider | |
279 * is registered instead of the old one. | |
280 * | |
281 * @param provider the information provider to register, or <code>null</code> to remove an existing one | |
282 * @param contentType the content type under which to register | |
283 */ | |
284 public void setInformationProvider(IInformationProvider provider, String contentType) { | |
285 | |
286 Assert.isNotNull(contentType); | |
287 | |
288 if (fProviders is null) | |
289 fProviders= new HashMap(); | |
290 | |
291 if (provider is null) | |
292 fProviders.remove(contentType); | |
293 else | |
294 fProviders.put(contentType, provider); | |
295 } | |
296 | |
297 /* | |
298 * @see IInformationPresenter#getInformationProvider(String) | |
299 */ | |
300 public IInformationProvider getInformationProvider(String contentType) { | |
301 if (fProviders is null) | |
302 return null; | |
303 | |
304 return (IInformationProvider) fProviders.get(contentType); | |
305 } | |
306 | |
307 /** | |
308 * Sets a offset to override the selection. Setting the value to <code>-1</code> will disable | |
309 * overriding. | |
310 * | |
311 * @param offset the offset to override selection or <code>-1</code> | |
312 */ | |
313 public void setOffset(int offset) { | |
314 fOffset= offset; | |
315 } | |
316 | |
317 /* | |
318 * @see AbstractInformationControlManager#computeInformation() | |
319 */ | |
320 protected void computeInformation() { | |
321 | |
322 int offset= fOffset < 0 ? fTextViewer.getSelectedRange().x : fOffset; | |
323 if (offset is -1) | |
324 return; | |
325 | |
326 fOffset= -1; | |
327 | |
328 IInformationProvider provider= null; | |
329 try { | |
330 String contentType= TextUtilities.getContentType(fTextViewer.getDocument(), getDocumentPartitioning(), offset, true); | |
331 provider= getInformationProvider(contentType); | |
332 } catch (BadLocationException x) { | |
333 } | |
334 if (provider is null) | |
335 return; | |
336 | |
337 IRegion subject= provider.getSubject(fTextViewer, offset); | |
338 if (subject is null) | |
339 return; | |
340 | |
341 Object info; | |
342 if (provider instanceof IInformationProviderExtension) { | |
343 IInformationProviderExtension extension= (IInformationProviderExtension) provider; | |
344 info= extension.getInformation2(fTextViewer, subject); | |
345 } else { | |
346 // backward compatibility code | |
347 info= provider.getInformation(fTextViewer, subject); | |
348 } | |
349 | |
350 if (provider instanceof IInformationProviderExtension2) | |
351 setCustomInformationControlCreator(((IInformationProviderExtension2) provider).getInformationPresenterControlCreator()); | |
352 else | |
353 setCustomInformationControlCreator(null); | |
354 | |
355 setInformation(info, computeArea(subject)); | |
356 } | |
357 | |
358 /** | |
359 * Determines the graphical area covered by the given text region. | |
360 * | |
361 * @param region the region whose graphical extend must be computed | |
362 * @return the graphical extend of the given region | |
363 */ | |
364 private Rectangle computeArea(IRegion region) { | |
365 | |
366 int start= 0; | |
367 int end= 0; | |
368 | |
369 IRegion widgetRegion= modelRange2WidgetRange(region); | |
370 if (widgetRegion !is null) { | |
371 start= widgetRegion.getOffset(); | |
372 end= widgetRegion.getOffset() + widgetRegion.getLength(); | |
373 } | |
374 | |
375 StyledText styledText= fTextViewer.getTextWidget(); | |
376 Rectangle bounds; | |
377 if (end > 0 && start < end) | |
378 bounds= styledText.getTextBounds(start, end - 1); | |
379 else { | |
380 Point loc= styledText.getLocationAtOffset(start); | |
381 bounds= new Rectangle(loc.x, loc.y, 0, styledText.getLineHeight(start)); | |
382 } | |
383 | |
384 return bounds; | |
385 } | |
386 | |
387 /** | |
388 * Translated the given range in the viewer's document into the corresponding | |
389 * range of the viewer's widget. | |
390 * | |
391 * @param region the range in the viewer's document | |
392 * @return the corresponding widget range | |
393 * @since 2.1 | |
394 */ | |
395 private IRegion modelRange2WidgetRange(IRegion region) { | |
396 if (fTextViewer instanceof ITextViewerExtension5) { | |
397 ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer; | |
398 return extension.modelRange2WidgetRange(region); | |
399 } | |
400 | |
401 IRegion visibleRegion= fTextViewer.getVisibleRegion(); | |
402 int start= region.getOffset() - visibleRegion.getOffset(); | |
403 int end= start + region.getLength(); | |
404 if (end > visibleRegion.getLength()) | |
405 end= visibleRegion.getLength(); | |
406 | |
407 return new Region(start, end - start); | |
408 } | |
409 | |
410 /* | |
411 * @see IInformationPresenter#install(ITextViewer) | |
412 */ | |
413 public void install(ITextViewer textViewer) { | |
414 fTextViewer= textViewer; | |
415 install(fTextViewer.getTextWidget()); | |
416 } | |
417 | |
418 /* | |
419 * @see IInformationPresenter#uninstall() | |
420 */ | |
421 public void uninstall() { | |
422 dispose(); | |
423 } | |
424 | |
425 /* | |
426 * @see AbstractInformationControlManager#showInformationControl(Rectangle) | |
427 */ | |
428 protected void showInformationControl(Rectangle subjectArea) { | |
429 if (fTextViewer instanceof IWidgetTokenOwnerExtension && fTextViewer instanceof IWidgetTokenOwner) { | |
430 IWidgetTokenOwnerExtension extension= (IWidgetTokenOwnerExtension) fTextViewer; | |
431 if (extension.requestWidgetToken(this, WIDGET_PRIORITY)) | |
432 super.showInformationControl(subjectArea); | |
433 } else if (fTextViewer instanceof IWidgetTokenOwner) { | |
434 IWidgetTokenOwner owner= (IWidgetTokenOwner) fTextViewer; | |
435 if (owner.requestWidgetToken(this)) | |
436 super.showInformationControl(subjectArea); | |
437 | |
438 } else | |
439 super.showInformationControl(subjectArea); | |
440 } | |
441 | |
442 /* | |
443 * @see AbstractInformationControlManager#hideInformationControl() | |
444 */ | |
445 protected void hideInformationControl() { | |
446 try { | |
447 super.hideInformationControl(); | |
448 } finally { | |
449 if (fTextViewer instanceof IWidgetTokenOwner) { | |
450 IWidgetTokenOwner owner= (IWidgetTokenOwner) fTextViewer; | |
451 owner.releaseWidgetToken(this); | |
452 } | |
453 } | |
454 } | |
455 | |
456 /* | |
457 * @see AbstractInformationControlManager#handleInformationControlDisposed() | |
458 */ | |
459 protected void handleInformationControlDisposed() { | |
460 try { | |
461 super.handleInformationControlDisposed(); | |
462 } finally { | |
463 if (fTextViewer instanceof IWidgetTokenOwner) { | |
464 IWidgetTokenOwner owner= (IWidgetTokenOwner) fTextViewer; | |
465 owner.releaseWidgetToken(this); | |
466 } | |
467 } | |
468 } | |
469 | |
470 /* | |
471 * @see dwtx.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner) | |
472 */ | |
473 public bool requestWidgetToken(IWidgetTokenOwner owner) { | |
474 return false; | |
475 } | |
476 | |
477 /* | |
478 * @see dwtx.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner, int) | |
479 * @since 3.0 | |
480 */ | |
481 public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) { | |
482 return false; | |
483 } | |
484 | |
485 /* | |
486 * @see dwtx.jface.text.IWidgetTokenKeeperExtension#setFocus(dwtx.jface.text.IWidgetTokenOwner) | |
487 * @since 3.0 | |
488 */ | |
489 public bool setFocus(IWidgetTokenOwner owner) { | |
490 return false; | |
491 } | |
492 } | |
493 |