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