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