129
|
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
|