Mercurial > projects > dwt-addons
comparison dwtx/jface/text/hyperlink/HyperlinkManager.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 * Steffen Pingel <steffen.pingel@tasktop.com> (Tasktop Technologies Inc.) - [navigation] hyperlink decoration is not erased when mouse is moved out of Text widget - https://bugs.eclipse.org/bugs/show_bug.cgi?id=100278 | |
11 * Port to the D programming language: | |
12 * Frank Benoit <benoit@tionex.de> | |
13 *******************************************************************************/ | |
14 module dwtx.jface.text.hyperlink.HyperlinkManager; | |
15 | |
16 import dwt.dwthelper.utils; | |
17 | |
18 import java.util.ArrayList; | |
19 import java.util.Arrays; | |
20 import java.util.Iterator; | |
21 import java.util.List; | |
22 | |
23 import dwt.DWT; | |
24 import dwt.custom.StyledText; | |
25 import dwt.events.FocusEvent; | |
26 import dwt.events.FocusListener; | |
27 import dwt.events.KeyEvent; | |
28 import dwt.events.KeyListener; | |
29 import dwt.events.MouseEvent; | |
30 import dwt.events.MouseListener; | |
31 import dwt.events.MouseMoveListener; | |
32 import dwt.events.MouseTrackListener; | |
33 import dwt.graphics.Point; | |
34 import dwt.widgets.Display; | |
35 import dwt.widgets.Event; | |
36 import dwt.widgets.Listener; | |
37 import dwtx.core.runtime.Assert; | |
38 import dwtx.jface.text.IRegion; | |
39 import dwtx.jface.text.ITextListener; | |
40 import dwtx.jface.text.ITextViewer; | |
41 import dwtx.jface.text.ITextViewerExtension5; | |
42 import dwtx.jface.text.Region; | |
43 import dwtx.jface.text.TextEvent; | |
44 | |
45 | |
46 /** | |
47 * Default implementation of a hyperlink manager. | |
48 * | |
49 * @since 3.1 | |
50 */ | |
51 public class HyperlinkManager : ITextListener, Listener, KeyListener, MouseListener, MouseMoveListener, FocusListener, MouseTrackListener { | |
52 | |
53 /** | |
54 * Detection strategy. | |
55 */ | |
56 public static final class DETECTION_STRATEGY { | |
57 | |
58 String fName; | |
59 | |
60 private DETECTION_STRATEGY(String name) { | |
61 fName= name; | |
62 } | |
63 | |
64 /* | |
65 * @see java.lang.Object#toString() | |
66 */ | |
67 public String toString() { | |
68 return fName; | |
69 } | |
70 } | |
71 | |
72 | |
73 /** | |
74 * The first detected hyperlink is passed to the | |
75 * hyperlink presenter and no further detector | |
76 * is consulted. | |
77 */ | |
78 public static final DETECTION_STRATEGY FIRST= new DETECTION_STRATEGY("first"); //$NON-NLS-1$ | |
79 | |
80 /** | |
81 * All detected hyperlinks from all detectors are collected | |
82 * and passed to the hyperlink presenter. | |
83 * <p> | |
84 * This strategy is only allowed if {@link IHyperlinkPresenter#canShowMultipleHyperlinks()} | |
85 * returns <code>true</code>. | |
86 * </p> | |
87 */ | |
88 public static final DETECTION_STRATEGY ALL= new DETECTION_STRATEGY("all"); //$NON-NLS-1$ | |
89 | |
90 /** | |
91 * All detected hyperlinks from all detectors are collected | |
92 * and all those with the longest region are passed to the | |
93 * hyperlink presenter. | |
94 * <p> | |
95 * This strategy is only allowed if {@link IHyperlinkPresenter#canShowMultipleHyperlinks()} | |
96 * returns <code>true</code>. | |
97 * </p> | |
98 */ | |
99 public static final DETECTION_STRATEGY LONGEST_REGION_ALL= new DETECTION_STRATEGY("all with same longest region"); //$NON-NLS-1$ | |
100 | |
101 /** | |
102 * All detected hyperlinks from all detectors are collected | |
103 * and form all those with the longest region only the first | |
104 * one is passed to the hyperlink presenter. | |
105 */ | |
106 public static final DETECTION_STRATEGY LONGEST_REGION_FIRST= new DETECTION_STRATEGY("first with longest region"); //$NON-NLS-1$ | |
107 | |
108 | |
109 /** The text viewer on which this hyperlink manager works. */ | |
110 private ITextViewer fTextViewer; | |
111 /** The session is active. */ | |
112 private bool fActive; | |
113 /** The key modifier mask. */ | |
114 private int fHyperlinkStateMask; | |
115 /** | |
116 * The active key modifier mask. | |
117 * @since 3.3 | |
118 */ | |
119 private int fActiveHyperlinkStateMask; | |
120 /** The active hyperlinks. */ | |
121 private IHyperlink[] fActiveHyperlinks; | |
122 /** The hyperlink detectors. */ | |
123 private IHyperlinkDetector[] fHyperlinkDetectors; | |
124 /** The hyperlink presenter. */ | |
125 private IHyperlinkPresenter fHyperlinkPresenter; | |
126 /** The detection strategy. */ | |
127 private final DETECTION_STRATEGY fDetectionStrategy; | |
128 | |
129 | |
130 /** | |
131 * Creates a new hyperlink manager. | |
132 * | |
133 * @param detectionStrategy the detection strategy one of {{@link #ALL}, {@link #FIRST}, {@link #LONGEST_REGION_ALL}, {@link #LONGEST_REGION_FIRST}} | |
134 */ | |
135 public HyperlinkManager(DETECTION_STRATEGY detectionStrategy) { | |
136 Assert.isNotNull(detectionStrategy); | |
137 fDetectionStrategy= detectionStrategy; | |
138 } | |
139 | |
140 /** | |
141 * Installs this hyperlink manager with the given arguments. | |
142 * | |
143 * @param textViewer the text viewer | |
144 * @param hyperlinkPresenter the hyperlink presenter | |
145 * @param hyperlinkDetectors the array of hyperlink detectors, must not be empty | |
146 * @param eventStateMask the DWT event state mask to activate hyperlink mode | |
147 */ | |
148 public void install(ITextViewer textViewer, IHyperlinkPresenter hyperlinkPresenter, IHyperlinkDetector[] hyperlinkDetectors, int eventStateMask) { | |
149 Assert.isNotNull(textViewer); | |
150 Assert.isNotNull(hyperlinkPresenter); | |
151 fTextViewer= textViewer; | |
152 fHyperlinkPresenter= hyperlinkPresenter; | |
153 Assert.isLegal(fHyperlinkPresenter.canShowMultipleHyperlinks() || fDetectionStrategy is FIRST || fDetectionStrategy is LONGEST_REGION_FIRST); | |
154 setHyperlinkDetectors(hyperlinkDetectors); | |
155 setHyperlinkStateMask(eventStateMask); | |
156 | |
157 StyledText text= fTextViewer.getTextWidget(); | |
158 if (text is null || text.isDisposed()) | |
159 return; | |
160 | |
161 text.getDisplay().addFilter(DWT.KeyUp, this); | |
162 text.addKeyListener(this); | |
163 text.addMouseListener(this); | |
164 text.addMouseMoveListener(this); | |
165 text.addFocusListener(this); | |
166 text.addMouseTrackListener(this); | |
167 | |
168 fTextViewer.addTextListener(this); | |
169 | |
170 fHyperlinkPresenter.install(fTextViewer); | |
171 } | |
172 | |
173 /** | |
174 * Sets the hyperlink detectors for this hyperlink manager. | |
175 * <p> | |
176 * It is allowed to call this method after this | |
177 * hyperlink manger has been installed. | |
178 * </p> | |
179 * | |
180 * @param hyperlinkDetectors and array of hyperlink detectors, must not be empty | |
181 */ | |
182 public void setHyperlinkDetectors(IHyperlinkDetector[] hyperlinkDetectors) { | |
183 Assert.isTrue(hyperlinkDetectors !is null && hyperlinkDetectors.length > 0); | |
184 if (fHyperlinkDetectors is null) | |
185 fHyperlinkDetectors= hyperlinkDetectors; | |
186 else { | |
187 synchronized (fHyperlinkDetectors) { | |
188 fHyperlinkDetectors= hyperlinkDetectors; | |
189 } | |
190 } | |
191 } | |
192 | |
193 /** | |
194 * Sets the DWT event state mask which in combination | |
195 * with the left mouse button triggers the hyperlink mode. | |
196 * <p> | |
197 * It is allowed to call this method after this | |
198 * hyperlink manger has been installed. | |
199 * </p> | |
200 * | |
201 * @param eventStateMask the DWT event state mask to activate hyperlink mode | |
202 */ | |
203 public void setHyperlinkStateMask(int eventStateMask) { | |
204 fHyperlinkStateMask= eventStateMask; | |
205 } | |
206 | |
207 /** | |
208 * Uninstalls this hyperlink manager. | |
209 */ | |
210 public void uninstall() { | |
211 deactivate(); | |
212 | |
213 StyledText text= fTextViewer.getTextWidget(); | |
214 if (text !is null && !text.isDisposed()) { | |
215 text.removeKeyListener(this); | |
216 text.getDisplay().removeFilter(DWT.KeyUp, this); | |
217 text.removeMouseListener(this); | |
218 text.removeMouseMoveListener(this); | |
219 text.removeFocusListener(this); | |
220 text.removeMouseTrackListener(this); | |
221 } | |
222 fTextViewer.removeTextListener(this); | |
223 | |
224 fHyperlinkPresenter.uninstall(); | |
225 | |
226 fHyperlinkPresenter= null; | |
227 fTextViewer= null; | |
228 fHyperlinkDetectors= null; | |
229 } | |
230 | |
231 /** | |
232 * Deactivates the currently shown hyperlinks. | |
233 */ | |
234 protected void deactivate() { | |
235 fHyperlinkPresenter.hideHyperlinks(); | |
236 fActive= false; | |
237 } | |
238 | |
239 /** | |
240 * Finds hyperlinks at the current offset. | |
241 * | |
242 * @return the hyperlinks or <code>null</code> if none. | |
243 */ | |
244 protected IHyperlink[] findHyperlinks() { | |
245 int offset= getCurrentTextOffset(); | |
246 if (offset is -1) | |
247 return null; | |
248 | |
249 bool canShowMultipleHyperlinks= fHyperlinkPresenter.canShowMultipleHyperlinks(); | |
250 IRegion region= new Region(offset, 0); | |
251 List allHyperlinks= new ArrayList(fHyperlinkDetectors.length * 2); | |
252 synchronized (fHyperlinkDetectors) { | |
253 for (int i= 0, length= fHyperlinkDetectors.length; i < length; i++) { | |
254 IHyperlinkDetector detector= fHyperlinkDetectors[i]; | |
255 if (detector is null) | |
256 continue; | |
257 | |
258 if (detector instanceof IHyperlinkDetectorExtension2) { | |
259 int stateMask= ((IHyperlinkDetectorExtension2)detector).getStateMask(); | |
260 if (stateMask !is -1 && stateMask !is fActiveHyperlinkStateMask) | |
261 continue; | |
262 else if (stateMask is -1 && fActiveHyperlinkStateMask !is fHyperlinkStateMask) | |
263 continue; | |
264 } else if (fActiveHyperlinkStateMask !is fHyperlinkStateMask) | |
265 continue; | |
266 | |
267 IHyperlink[] hyperlinks= detector.detectHyperlinks(fTextViewer, region, canShowMultipleHyperlinks); | |
268 if (hyperlinks is null) | |
269 continue; | |
270 | |
271 Assert.isLegal(hyperlinks.length > 0); | |
272 | |
273 if (fDetectionStrategy is FIRST) { | |
274 if (hyperlinks.length is 1) | |
275 return hyperlinks; | |
276 return new IHyperlink[] {hyperlinks[0]}; | |
277 } | |
278 allHyperlinks.addAll(Arrays.asList(hyperlinks)); | |
279 } | |
280 } | |
281 | |
282 if (allHyperlinks.isEmpty()) | |
283 return null; | |
284 | |
285 if (fDetectionStrategy !is ALL) { | |
286 int maxLength= computeLongestHyperlinkLength(allHyperlinks); | |
287 Iterator iter= new ArrayList(allHyperlinks).iterator(); | |
288 while (iter.hasNext()) { | |
289 IHyperlink hyperlink= (IHyperlink)iter.next(); | |
290 if (hyperlink.getHyperlinkRegion().getLength() < maxLength) | |
291 allHyperlinks.remove(hyperlink); | |
292 } | |
293 } | |
294 | |
295 if (fDetectionStrategy is LONGEST_REGION_FIRST) | |
296 return new IHyperlink[] {(IHyperlink)allHyperlinks.get(0)}; | |
297 | |
298 return (IHyperlink[])allHyperlinks.toArray(new IHyperlink[allHyperlinks.size()]); | |
299 | |
300 } | |
301 | |
302 /** | |
303 * Computes the length of the longest detected | |
304 * hyperlink. | |
305 * | |
306 * @param hyperlinks | |
307 * @return the length of the longest detected | |
308 */ | |
309 protected int computeLongestHyperlinkLength(List hyperlinks) { | |
310 Assert.isLegal(hyperlinks !is null && !hyperlinks.isEmpty()); | |
311 Iterator iter= hyperlinks.iterator(); | |
312 int length= Integer.MIN_VALUE; | |
313 while (iter.hasNext()) { | |
314 IRegion region= ((IHyperlink)iter.next()).getHyperlinkRegion(); | |
315 if (region.getLength() < length) | |
316 continue; | |
317 length= region.getLength(); | |
318 } | |
319 return length; | |
320 } | |
321 | |
322 /** | |
323 * Returns the current text offset. | |
324 * | |
325 * @return the current text offset | |
326 */ | |
327 protected int getCurrentTextOffset() { | |
328 | |
329 try { | |
330 StyledText text= fTextViewer.getTextWidget(); | |
331 if (text is null || text.isDisposed()) | |
332 return -1; | |
333 | |
334 Display display= text.getDisplay(); | |
335 Point absolutePosition= display.getCursorLocation(); | |
336 Point relativePosition= text.toControl(absolutePosition); | |
337 | |
338 int widgetOffset= text.getOffsetAtLocation(relativePosition); | |
339 Point p= text.getLocationAtOffset(widgetOffset); | |
340 if (p.x > relativePosition.x) | |
341 widgetOffset--; | |
342 | |
343 if (fTextViewer instanceof ITextViewerExtension5) { | |
344 ITextViewerExtension5 extension= (ITextViewerExtension5)fTextViewer; | |
345 return extension.widgetOffset2ModelOffset(widgetOffset); | |
346 } | |
347 | |
348 return widgetOffset + fTextViewer.getVisibleRegion().getOffset(); | |
349 | |
350 } catch (IllegalArgumentException e) { | |
351 return -1; | |
352 } | |
353 } | |
354 | |
355 /* | |
356 * @see dwt.events.KeyListener#keyPressed(dwt.events.KeyEvent) | |
357 */ | |
358 public void keyPressed(KeyEvent event) { | |
359 | |
360 if (fActive) { | |
361 deactivate(); | |
362 return; | |
363 } | |
364 | |
365 if (!isRegisteredStateMask(event.keyCode)) { | |
366 deactivate(); | |
367 return; | |
368 } | |
369 | |
370 fActive= true; | |
371 fActiveHyperlinkStateMask= event.keyCode; | |
372 | |
373 // removed for #25871 (hyperlinks could interact with typing) | |
374 // | |
375 // ITextViewer viewer= getSourceViewer(); | |
376 // if (viewer is null) | |
377 // return; | |
378 // | |
379 // IRegion region= getCurrentTextRegion(viewer); | |
380 // if (region is null) | |
381 // return; | |
382 // | |
383 // highlightRegion(viewer, region); | |
384 // activateCursor(viewer); | |
385 } | |
386 | |
387 /* | |
388 * @see dwt.events.KeyListener#keyReleased(dwt.events.KeyEvent) | |
389 */ | |
390 public void keyReleased(KeyEvent event) { | |
391 } | |
392 | |
393 /* | |
394 * @see dwt.events.MouseListener#mouseDoubleClick(dwt.events.MouseEvent) | |
395 */ | |
396 public void mouseDoubleClick(MouseEvent e) { | |
397 | |
398 } | |
399 | |
400 /* | |
401 * @see dwt.events.MouseListener#mouseDown(dwt.events.MouseEvent) | |
402 */ | |
403 public void mouseDown(MouseEvent event) { | |
404 | |
405 if (!fActive) | |
406 return; | |
407 | |
408 if (event.stateMask !is fActiveHyperlinkStateMask) { | |
409 deactivate(); | |
410 return; | |
411 } | |
412 | |
413 if (event.button !is 1) { | |
414 deactivate(); | |
415 return; | |
416 } | |
417 } | |
418 | |
419 /* | |
420 * @see dwt.events.MouseListener#mouseUp(dwt.events.MouseEvent) | |
421 */ | |
422 public void mouseUp(MouseEvent e) { | |
423 | |
424 if (!fActive) { | |
425 fActiveHyperlinks= null; | |
426 return; | |
427 } | |
428 | |
429 if (e.button !is 1) | |
430 fActiveHyperlinks= null; | |
431 | |
432 deactivate(); | |
433 | |
434 if (fActiveHyperlinks !is null) | |
435 fActiveHyperlinks[0].open(); | |
436 } | |
437 | |
438 /* | |
439 * @see dwt.events.MouseMoveListener#mouseMove(dwt.events.MouseEvent) | |
440 */ | |
441 public void mouseMove(MouseEvent event) { | |
442 if (fHyperlinkPresenter instanceof IHyperlinkPresenterExtension) { | |
443 if (!((IHyperlinkPresenterExtension)fHyperlinkPresenter).canHideHyperlinks()) | |
444 return; | |
445 } | |
446 | |
447 if (!isRegisteredStateMask(event.stateMask)) { | |
448 if (fActive) | |
449 deactivate(); | |
450 | |
451 return; | |
452 } | |
453 | |
454 fActive= true; | |
455 fActiveHyperlinkStateMask= event.stateMask; | |
456 | |
457 StyledText text= fTextViewer.getTextWidget(); | |
458 if (text is null || text.isDisposed()) { | |
459 deactivate(); | |
460 return; | |
461 } | |
462 | |
463 if ((event.stateMask & DWT.BUTTON1) !is 0 && text.getSelectionCount() !is 0) { | |
464 deactivate(); | |
465 return; | |
466 } | |
467 | |
468 fActiveHyperlinks= findHyperlinks(); | |
469 if (fActiveHyperlinks is null || fActiveHyperlinks.length is 0) { | |
470 fHyperlinkPresenter.hideHyperlinks(); | |
471 return; | |
472 } | |
473 | |
474 fHyperlinkPresenter.showHyperlinks(fActiveHyperlinks); | |
475 | |
476 } | |
477 | |
478 /** | |
479 * Checks whether the given state mask is registered. | |
480 * | |
481 * @param stateMask | |
482 * @return <code>true</code> if a detector is registered for the given state mask | |
483 * @since 3.3 | |
484 */ | |
485 private bool isRegisteredStateMask(int stateMask) { | |
486 if (stateMask is fHyperlinkStateMask) | |
487 return true; | |
488 | |
489 synchronized (fHyperlinkDetectors) { | |
490 for (int i= 0; i < fHyperlinkDetectors.length; i++) { | |
491 if (fHyperlinkDetectors[i] instanceof IHyperlinkDetectorExtension2) { | |
492 if (stateMask is ((IHyperlinkDetectorExtension2)fHyperlinkDetectors[i]).getStateMask()) | |
493 return true; | |
494 } | |
495 } | |
496 } | |
497 return false; | |
498 } | |
499 | |
500 /* | |
501 * @see dwt.events.FocusListener#focusGained(dwt.events.FocusEvent) | |
502 */ | |
503 public void focusGained(FocusEvent e) {} | |
504 | |
505 /* | |
506 * @see dwt.events.FocusListener#focusLost(dwt.events.FocusEvent) | |
507 */ | |
508 public void focusLost(FocusEvent event) { | |
509 deactivate(); | |
510 } | |
511 | |
512 /* | |
513 * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event) | |
514 * @since 3.2 | |
515 */ | |
516 public void handleEvent(Event event) { | |
517 //key up | |
518 deactivate(); | |
519 } | |
520 | |
521 /* | |
522 * @see dwtx.jface.text.ITextListener#textChanged(TextEvent) | |
523 * @since 3.2 | |
524 */ | |
525 public void textChanged(TextEvent event) { | |
526 if (event.getDocumentEvent() !is null) | |
527 deactivate(); | |
528 } | |
529 | |
530 /** | |
531 * {@inheritDoc} | |
532 * | |
533 * @since 3.4 | |
534 */ | |
535 public void mouseExit(MouseEvent e) { | |
536 if (fHyperlinkPresenter instanceof IHyperlinkPresenterExtension) { | |
537 if (!((IHyperlinkPresenterExtension)fHyperlinkPresenter).canHideHyperlinks()) | |
538 return; | |
539 } | |
540 deactivate(); | |
541 } | |
542 | |
543 /** | |
544 * {@inheritDoc} | |
545 * | |
546 * @since 3.4 | |
547 */ | |
548 public void mouseEnter(MouseEvent e) { | |
549 } | |
550 | |
551 /** | |
552 * {@inheritDoc} | |
553 * | |
554 * @since 3.4 | |
555 */ | |
556 public void mouseHover(MouseEvent e) { | |
557 } | |
558 | |
559 } |