Mercurial > projects > dwt-addons
annotate dwtx/jface/text/source/AnnotationPainter.d @ 162:1a5b8f8129df
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 08 Sep 2008 00:51:37 +0200 |
parents | 7926b636c282 |
children |
rev | line source |
---|---|
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.source.AnnotationPainter; | |
14 | |
131 | 15 import dwtx.jface.text.source.ISharedTextColors; // packageimport |
16 import dwtx.jface.text.source.ILineRange; // packageimport | |
17 import dwtx.jface.text.source.IAnnotationPresentation; // packageimport | |
18 import dwtx.jface.text.source.IVerticalRulerInfoExtension; // packageimport | |
19 import dwtx.jface.text.source.ICharacterPairMatcher; // packageimport | |
20 import dwtx.jface.text.source.TextInvocationContext; // packageimport | |
21 import dwtx.jface.text.source.LineChangeHover; // packageimport | |
22 import dwtx.jface.text.source.IChangeRulerColumn; // packageimport | |
23 import dwtx.jface.text.source.IAnnotationMap; // packageimport | |
24 import dwtx.jface.text.source.IAnnotationModelListenerExtension; // packageimport | |
25 import dwtx.jface.text.source.ISourceViewerExtension2; // packageimport | |
26 import dwtx.jface.text.source.IAnnotationHover; // packageimport | |
27 import dwtx.jface.text.source.ContentAssistantFacade; // packageimport | |
28 import dwtx.jface.text.source.IAnnotationAccess; // packageimport | |
29 import dwtx.jface.text.source.IVerticalRulerExtension; // packageimport | |
30 import dwtx.jface.text.source.IVerticalRulerColumn; // packageimport | |
31 import dwtx.jface.text.source.LineNumberRulerColumn; // packageimport | |
32 import dwtx.jface.text.source.MatchingCharacterPainter; // packageimport | |
33 import dwtx.jface.text.source.IAnnotationModelExtension; // packageimport | |
34 import dwtx.jface.text.source.ILineDifferExtension; // packageimport | |
35 import dwtx.jface.text.source.DefaultCharacterPairMatcher; // packageimport | |
36 import dwtx.jface.text.source.LineNumberChangeRulerColumn; // packageimport | |
37 import dwtx.jface.text.source.IAnnotationAccessExtension; // packageimport | |
38 import dwtx.jface.text.source.ISourceViewer; // packageimport | |
39 import dwtx.jface.text.source.AnnotationModel; // packageimport | |
40 import dwtx.jface.text.source.ILineDifferExtension2; // packageimport | |
41 import dwtx.jface.text.source.IAnnotationModelListener; // packageimport | |
42 import dwtx.jface.text.source.IVerticalRuler; // packageimport | |
43 import dwtx.jface.text.source.DefaultAnnotationHover; // packageimport | |
44 import dwtx.jface.text.source.SourceViewer; // packageimport | |
45 import dwtx.jface.text.source.SourceViewerConfiguration; // packageimport | |
46 import dwtx.jface.text.source.AnnotationBarHoverManager; // packageimport | |
47 import dwtx.jface.text.source.CompositeRuler; // packageimport | |
48 import dwtx.jface.text.source.ImageUtilities; // packageimport | |
49 import dwtx.jface.text.source.VisualAnnotationModel; // packageimport | |
50 import dwtx.jface.text.source.IAnnotationModel; // packageimport | |
51 import dwtx.jface.text.source.ISourceViewerExtension3; // packageimport | |
52 import dwtx.jface.text.source.ILineDiffInfo; // packageimport | |
53 import dwtx.jface.text.source.VerticalRulerEvent; // packageimport | |
54 import dwtx.jface.text.source.ChangeRulerColumn; // packageimport | |
55 import dwtx.jface.text.source.ILineDiffer; // packageimport | |
56 import dwtx.jface.text.source.AnnotationModelEvent; // packageimport | |
57 import dwtx.jface.text.source.AnnotationColumn; // packageimport | |
58 import dwtx.jface.text.source.AnnotationRulerColumn; // packageimport | |
59 import dwtx.jface.text.source.IAnnotationHoverExtension; // packageimport | |
60 import dwtx.jface.text.source.AbstractRulerColumn; // packageimport | |
61 import dwtx.jface.text.source.ISourceViewerExtension; // packageimport | |
62 import dwtx.jface.text.source.AnnotationMap; // packageimport | |
63 import dwtx.jface.text.source.IVerticalRulerInfo; // packageimport | |
64 import dwtx.jface.text.source.IAnnotationModelExtension2; // packageimport | |
65 import dwtx.jface.text.source.LineRange; // packageimport | |
66 import dwtx.jface.text.source.IAnnotationAccessExtension2; // packageimport | |
67 import dwtx.jface.text.source.VerticalRuler; // packageimport | |
68 import dwtx.jface.text.source.JFaceTextMessages; // packageimport | |
69 import dwtx.jface.text.source.IOverviewRuler; // packageimport | |
70 import dwtx.jface.text.source.Annotation; // packageimport | |
71 import dwtx.jface.text.source.IVerticalRulerListener; // packageimport | |
72 import dwtx.jface.text.source.ISourceViewerExtension4; // packageimport | |
73 import dwtx.jface.text.source.IAnnotationHoverExtension2; // packageimport | |
74 import dwtx.jface.text.source.OverviewRuler; // packageimport | |
75 import dwtx.jface.text.source.OverviewRulerHoverManager; // packageimport | |
76 | |
129 | 77 import dwt.dwthelper.utils; |
153
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
147
diff
changeset
|
78 import dwtx.dwtxhelper.Collection; |
162 | 79 import tango.text.convert.Format; |
80 import tango.io.Stdout; | |
129 | 81 |
82 import dwt.DWT; | |
83 import dwt.DWTException; | |
84 import dwt.custom.StyleRange; | |
85 import dwt.custom.StyledText; | |
86 import dwt.events.PaintEvent; | |
87 import dwt.events.PaintListener; | |
88 import dwt.graphics.Color; | |
89 import dwt.graphics.GC; | |
90 import dwt.graphics.Point; | |
91 import dwt.graphics.Rectangle; | |
92 import dwt.graphics.TextStyle; | |
93 import dwt.widgets.Display; | |
94 import dwtx.core.runtime.Assert; | |
95 import dwtx.core.runtime.Platform; | |
96 import dwtx.jface.text.BadLocationException; | |
97 import dwtx.jface.text.IDocument; | |
98 import dwtx.jface.text.IPaintPositionManager; | |
99 import dwtx.jface.text.IPainter; | |
100 import dwtx.jface.text.IRegion; | |
101 import dwtx.jface.text.ITextInputListener; | |
102 import dwtx.jface.text.ITextPresentationListener; | |
103 import dwtx.jface.text.ITextViewerExtension2; | |
104 import dwtx.jface.text.ITextViewerExtension5; | |
105 import dwtx.jface.text.JFaceTextUtil; | |
106 import dwtx.jface.text.Position; | |
107 import dwtx.jface.text.Region; | |
108 import dwtx.jface.text.TextPresentation; | |
109 | |
110 | |
111 /** | |
112 * A drawing strategy draws the decoration for an annotation onto the text widget. | |
113 * | |
114 * @since 3.0 | |
115 */ | |
116 public interface IDrawingStrategy { | |
117 /** | |
118 * Draws a decoration for an annotation onto the specified GC at the given text range. There | |
119 * are two different invocation modes of the <code>draw</code> method: | |
120 * <ul> | |
121 * <li><strong>drawing mode:</strong> the passed GC is the graphics context of a paint | |
122 * event occurring on the text widget. The strategy should draw the decoration onto the | |
123 * graphics context, such that the decoration appears at the given range in the text | |
124 * widget.</li> | |
125 * <li><strong>clearing mode:</strong> the passed GC is <code>null</code>. In this case | |
126 * the strategy must invalidate enough of the text widget's client area to cover any | |
127 * decoration drawn in drawing mode. This can usually be accomplished by calling | |
128 * {@linkplain StyledText#redrawRange(int, int, bool) textWidget.redrawRange(offset, length, true)}.</li> | |
129 * </ul> | |
145 | 130 * |
129 | 131 * @param annotation the annotation to be drawn |
132 * @param gc the graphics context, <code>null</code> when in clearing mode | |
133 * @param textWidget the text widget to draw on | |
134 * @param offset the offset of the line | |
135 * @param length the length of the line | |
136 * @param color the color of the line | |
137 */ | |
138 void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color); | |
139 } | |
140 | |
158 | 141 alias IDrawingStrategy AnnotationPainter_IDrawingStrategy; |
142 | |
143 /** | |
144 * Paints decorations for annotations provided by an annotation model and/or | |
145 * highlights them in the associated source viewer. | |
146 * <p> | |
147 * The annotation painter can be configured with drawing strategies. A drawing | |
148 * strategy defines the visual presentation of a particular type of annotation | |
149 * decoration.</p> | |
150 * <p> | |
151 * Clients usually instantiate and configure objects of this class.</p> | |
152 * | |
153 * @since 2.1 | |
154 */ | |
155 public class AnnotationPainter : IPainter, PaintListener, IAnnotationModelListener, IAnnotationModelListenerExtension, ITextPresentationListener { | |
156 | |
129 | 157 /** |
158 * Squiggles drawing strategy. | |
145 | 159 * |
129 | 160 * @since 3.0 |
161 * @deprecated As of 3.4, replaced by {@link AnnotationPainter.UnderlineStrategy} | |
162 */ | |
163 public static class SquigglesStrategy : IDrawingStrategy { | |
164 | |
165 /* | |
166 * @see dwtx.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(dwtx.jface.text.source.Annotation, dwt.graphics.GC, dwt.custom.StyledText, int, int, dwt.graphics.Color) | |
167 * @since 3.0 | |
168 */ | |
169 public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) { | |
170 if (gc !is null) { | |
171 | |
172 if (length < 1) | |
173 return; | |
174 | |
175 Point left= textWidget.getLocationAtOffset(offset); | |
176 Point right= textWidget.getLocationAtOffset(offset + length); | |
177 Rectangle rect= textWidget.getTextBounds(offset, offset + length - 1); | |
178 left.x= rect.x; | |
179 right.x= rect.x + rect.width; | |
180 | |
181 int[] polyline= computePolyline(left, right, textWidget.getBaseline(offset), textWidget.getLineHeight(offset)); | |
145 | 182 |
129 | 183 gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance |
184 gc.setLineStyle(DWT.LINE_SOLID); | |
185 gc.setForeground(color); | |
186 gc.drawPolyline(polyline); | |
187 | |
188 } else { | |
189 textWidget.redrawRange(offset, length, true); | |
190 } | |
191 } | |
192 | |
193 /** | |
194 * Computes an array of alternating x and y values which are the corners of the squiggly line of the | |
195 * given height between the given end points. | |
196 * | |
197 * @param left the left end point | |
198 * @param right the right end point | |
199 * @param baseline the font's baseline | |
200 * @param lineHeight the height of the line | |
201 * @return the array of alternating x and y values which are the corners of the squiggly line | |
202 */ | |
203 private int[] computePolyline(Point left, Point right, int baseline, int lineHeight) { | |
204 | |
205 final int WIDTH= 4; // must be even | |
206 final int HEIGHT= 2; // can be any number | |
207 | |
208 int peaks= (right.x - left.x) / WIDTH; | |
209 if (peaks is 0 && right.x - left.x > 2) | |
210 peaks= 1; | |
211 | |
212 int leftX= left.x; | |
213 | |
214 // compute (number of point) * 2 | |
162 | 215 int length_= ((2 * peaks) + 1) * 2; |
216 if (length_ < 0) | |
129 | 217 return new int[0]; |
218 | |
162 | 219 int[] coordinates= new int[length_]; |
129 | 220 |
221 // cache peeks' y-coordinates | |
222 int top= left.y + Math.min(baseline + 1, lineHeight - HEIGHT - 1); | |
223 int bottom= top + HEIGHT; | |
224 | |
225 // populate array with peek coordinates | |
226 for (int i= 0; i < peaks; i++) { | |
227 int index= 4 * i; | |
228 coordinates[index]= leftX + (WIDTH * i); | |
229 coordinates[index+1]= bottom; | |
230 coordinates[index+2]= coordinates[index] + WIDTH/2; | |
231 coordinates[index+3]= top; | |
232 } | |
233 | |
234 // the last down flank is missing | |
162 | 235 coordinates[length_-2]= Math.min(Math.max(0, right.x - 1), left.x + (WIDTH * peaks)); |
236 coordinates[length_-1]= bottom; | |
129 | 237 |
238 return coordinates; | |
239 } | |
240 } | |
241 | |
242 /** | |
243 * Drawing strategy that does nothing. | |
244 * | |
245 * @since 3.0 | |
246 */ | |
247 public static final class NullStrategy : IDrawingStrategy { | |
248 | |
249 /* | |
250 * @see dwtx.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(dwtx.jface.text.source.Annotation, dwt.graphics.GC, dwt.custom.StyledText, int, int, dwt.graphics.Color) | |
251 * @since 3.0 | |
252 */ | |
253 public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) { | |
254 // do nothing | |
255 } | |
256 } | |
145 | 257 |
258 | |
129 | 259 /** |
260 * A text style painting strategy draws the decoration for an annotation | |
261 * onto the text widget by applying a {@link TextStyle} on a given | |
262 * {@link StyleRange}. | |
145 | 263 * |
129 | 264 * @since 3.4 |
265 */ | |
266 public interface ITextStyleStrategy { | |
267 | |
268 /** | |
269 * Applies a text style on the given <code>StyleRange</code>. | |
145 | 270 * |
129 | 271 * @param styleRange the style range on which to apply the text style |
272 * @param annotationColor the color of the annotation | |
273 */ | |
274 void applyTextStyle(StyleRange styleRange, Color annotationColor); | |
275 } | |
145 | 276 |
129 | 277 |
278 /** | |
279 * @since 3.4 | |
280 */ | |
281 public static final class HighlightingStrategy : ITextStyleStrategy { | |
282 public void applyTextStyle(StyleRange styleRange, Color annotationColor) { | |
283 styleRange.background= annotationColor; | |
284 } | |
285 } | |
286 | |
145 | 287 |
129 | 288 /** |
289 * Underline text style strategy. | |
145 | 290 * |
129 | 291 * @since 3.4 |
292 */ | |
293 public static final class UnderlineStrategy : ITextStyleStrategy { | |
145 | 294 |
129 | 295 int fUnderlineStyle; |
296 | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
297 public this(int style) { |
129 | 298 Assert.isLegal(style is DWT.UNDERLINE_SINGLE || style is DWT.UNDERLINE_DOUBLE || style is DWT.UNDERLINE_ERROR || style is DWT.UNDERLINE_SQUIGGLE); |
299 fUnderlineStyle= style; | |
300 } | |
301 | |
302 public void applyTextStyle(StyleRange styleRange, Color annotationColor) { | |
303 styleRange.underline= true; | |
304 styleRange.underlineStyle= fUnderlineStyle; | |
305 styleRange.underlineColor= annotationColor; | |
306 } | |
307 } | |
308 | |
145 | 309 |
129 | 310 /** |
311 * Box text style strategy. | |
145 | 312 * |
129 | 313 * @since 3.4 |
314 */ | |
315 public static final class BoxStrategy : ITextStyleStrategy { | |
145 | 316 |
129 | 317 int fBorderStyle; |
318 | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
319 public this(int style) { |
129 | 320 Assert.isLegal(style is DWT.BORDER_DASH || style is DWT.BORDER_DASH || style is DWT.BORDER_SOLID); |
321 fBorderStyle= style; | |
322 } | |
145 | 323 |
129 | 324 public void applyTextStyle(StyleRange styleRange, Color annotationColor) { |
325 styleRange.borderStyle= fBorderStyle; | |
326 styleRange.borderColor= annotationColor; | |
327 } | |
328 } | |
145 | 329 |
129 | 330 |
331 /** | |
332 * Implementation of <code>IRegion</code> that can be reused | |
333 * by setting the offset and the length. | |
334 */ | |
162 | 335 private static class ReusableRegion : Position , IRegion { |
336 public override int getOffset(){ | |
337 return super.getOffset(); | |
338 } | |
339 public override int getLength(){ | |
340 return super.getLength(); | |
341 } | |
342 } | |
129 | 343 |
344 /** | |
345 * Tells whether this class is in debug mode. | |
346 * @since 3.0 | |
347 */ | |
159 | 348 private static bool DEBUG_; |
349 private static bool DEBUG_init = false; | |
350 private static bool DEBUG(){ | |
351 if( !DEBUG_init ){ | |
352 DEBUG_init = true; | |
353 DEBUG_ = "true".equalsIgnoreCase(Platform.getDebugOption("dwtx.jface.text/debug/AnnotationPainter")); //$NON-NLS-1$//$NON-NLS-2$ | |
354 } | |
355 return DEBUG_; | |
356 } | |
357 | |
129 | 358 /** |
359 * The squiggly painter strategy. | |
360 * @since 3.0 | |
361 */ | |
162 | 362 private static IDrawingStrategy SQUIGGLES_STRATEGY_; |
363 private static IDrawingStrategy SQUIGGLES_STRATEGY(){ | |
364 if( SQUIGGLES_STRATEGY_ is null ){ | |
365 synchronized( AnnotationPainter.classinfo ){ | |
366 if( SQUIGGLES_STRATEGY_ is null ){ | |
367 SQUIGGLES_STRATEGY_ = new SquigglesStrategy(); | |
368 } | |
369 } | |
370 } | |
371 return SQUIGGLES_STRATEGY_; | |
372 } | |
373 | |
129 | 374 |
375 /** | |
376 * This strategy is used to mark the <code>null</code> value in the chache | |
377 * maps. | |
145 | 378 * |
129 | 379 * @since 3.4 |
380 */ | |
162 | 381 private static IDrawingStrategy NULL_STRATEGY_; |
382 private static IDrawingStrategy NULL_STRATEGY(){ | |
383 if( NULL_STRATEGY_ is null ){ | |
384 synchronized( AnnotationPainter.classinfo ){ | |
385 if( NULL_STRATEGY_ is null ){ | |
386 NULL_STRATEGY_= new NullStrategy(); | |
387 } | |
388 } | |
389 } | |
390 return NULL_STRATEGY_; | |
391 } | |
129 | 392 /** |
393 * The squiggles painter id. | |
394 * @since 3.0 | |
395 */ | |
162 | 396 private static Object SQUIGGLES_; |
397 private static Object SQUIGGLES(){ | |
398 if( SQUIGGLES_ is null ){ | |
399 synchronized( AnnotationPainter.classinfo ){ | |
400 if( SQUIGGLES_ is null ){ | |
401 SQUIGGLES_= new Object(); | |
402 } | |
403 } | |
404 } | |
405 return SQUIGGLES_; | |
406 } | |
129 | 407 /** |
408 * The squiggly painter strategy. | |
145 | 409 * |
129 | 410 * @since 3.4 |
411 */ | |
162 | 412 private static ITextStyleStrategy HIGHLIGHTING_STRATEGY_; |
413 private static ITextStyleStrategy HIGHLIGHTING_STRATEGY(){ | |
414 if( HIGHLIGHTING_STRATEGY_ is null ){ | |
415 synchronized( AnnotationPainter.classinfo ){ | |
416 if( HIGHLIGHTING_STRATEGY_ is null ){ | |
417 HIGHLIGHTING_STRATEGY_= new HighlightingStrategy(); | |
418 } | |
419 } | |
420 } | |
421 return HIGHLIGHTING_STRATEGY_; | |
422 } | |
129 | 423 |
424 /** | |
425 * The highlighting text style strategy id. | |
145 | 426 * |
129 | 427 * @since 3.4 |
428 */ | |
162 | 429 private static Object HIGHLIGHTING_; |
430 private static Object HIGHLIGHTING(){ | |
431 if( HIGHLIGHTING_ is null ){ | |
432 synchronized( AnnotationPainter.classinfo ){ | |
433 if( HIGHLIGHTING_ is null ){ | |
434 HIGHLIGHTING_= new Object(); | |
435 } | |
436 } | |
437 } | |
438 return HIGHLIGHTING_; | |
439 } | |
129 | 440 |
441 /** | |
442 * The presentation information (decoration) for an annotation. Each such | |
443 * object represents one decoration drawn on the text area, such as squiggly lines | |
444 * and underlines. | |
445 */ | |
446 private static class Decoration { | |
447 /** The position of this decoration */ | |
448 private Position fPosition; | |
449 /** The color of this decoration */ | |
450 private Color fColor; | |
451 /** | |
452 * The annotation's layer | |
453 * @since 3.0 | |
454 */ | |
455 private int fLayer; | |
456 /** | |
457 * The painting strategy for this decoration. | |
458 * @since 3.0 | |
459 */ | |
460 private Object fPaintingStrategy; | |
461 } | |
462 | |
463 | |
464 /** Indicates whether this painter is active */ | |
465 private bool fIsActive= false; | |
466 /** Indicates whether this painter is managing decorations */ | |
467 private bool fIsPainting= false; | |
468 /** Indicates whether this painter is setting its annotation model */ | |
157
7f75eaa8103a
volatile and cast Map.Entry
Frank Benoit <benoit@tionex.de>
parents:
153
diff
changeset
|
469 private /+volatile+/ bool fIsSettingModel= false; |
129 | 470 /** The associated source viewer */ |
471 private ISourceViewer fSourceViewer; | |
472 /** The cached widget of the source viewer */ | |
473 private StyledText fTextWidget; | |
474 /** The annotation model providing the annotations to be drawn */ | |
475 private IAnnotationModel fModel; | |
476 /** The annotation access */ | |
477 private IAnnotationAccess fAnnotationAccess; | |
478 /** | |
479 * The map with decorations | |
480 * @since 3.0 | |
481 */ | |
162 | 482 private Map fDecorationsMap; // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=50767 |
129 | 483 /** |
484 * The map with of highlighted decorations. | |
485 * @since 3.0 | |
486 */ | |
162 | 487 private Map fHighlightedDecorationsMap; |
129 | 488 /** |
489 * Mutex for highlighted decorations map. | |
490 * @since 3.0 | |
491 */ | |
162 | 492 private Object fDecorationMapLock; |
129 | 493 /** |
494 * Mutex for for decorations map. | |
495 * @since 3.0 | |
496 */ | |
162 | 497 private Object fHighlightedDecorationsMapLock; |
129 | 498 /** |
499 * Maps an annotation type to its registered color. | |
145 | 500 * |
129 | 501 * @see #setAnnotationTypeColor(Object, Color) |
502 */ | |
162 | 503 private Map fAnnotationType2Color; |
129 | 504 |
505 /** | |
506 * Cache that maps the annotation type to its color. | |
507 * @since 3.4 | |
508 */ | |
162 | 509 private Map fCachedAnnotationType2Color; |
129 | 510 /** |
511 * The range in which the current highlight annotations can be found. | |
512 * @since 3.0 | |
513 */ | |
514 private Position fCurrentHighlightAnnotationRange= null; | |
515 /** | |
516 * The range in which all added, removed and changed highlight | |
517 * annotations can be found since the last world change. | |
518 * @since 3.0 | |
519 */ | |
520 private Position fTotalHighlightAnnotationRange= null; | |
521 /** | |
522 * The range in which the currently drawn annotations can be found. | |
523 * @since 3.3 | |
524 */ | |
525 private Position fCurrentDrawRange= null; | |
526 /** | |
527 * The range in which all added, removed and changed drawn | |
528 * annotations can be found since the last world change. | |
529 * @since 3.3 | |
530 */ | |
531 private Position fTotalDrawRange= null; | |
532 /** | |
533 * The text input listener. | |
534 * @since 3.0 | |
535 */ | |
536 private ITextInputListener fTextInputListener; | |
537 /** | |
538 * Flag which tells that a new document input is currently being set. | |
539 * @since 3.0 | |
540 */ | |
541 private bool fInputDocumentAboutToBeChanged; | |
542 /** | |
543 * Maps annotation types to painting strategy identifiers. | |
145 | 544 * |
129 | 545 * @see #addAnnotationType(Object, Object) |
546 * @since 3.0 | |
547 */ | |
162 | 548 private Map fAnnotationType2PaintingStrategyId; |
129 | 549 /** |
550 * Maps annotation types to painting strategy identifiers. | |
551 * @since 3.4 | |
552 */ | |
162 | 553 private Map fCachedAnnotationType2PaintingStrategy; |
129 | 554 |
555 /** | |
556 * Maps painting strategy identifiers to painting strategies. | |
145 | 557 * |
129 | 558 * @since 3.0 |
559 */ | |
162 | 560 private Map fPaintingStrategyId2PaintingStrategy; |
129 | 561 |
562 /** | |
563 * Reuse this region for performance reasons. | |
564 * @since 3.3 | |
565 */ | |
162 | 566 private ReusableRegion fReusableRegion; |
145 | 567 |
129 | 568 /** |
569 * Creates a new annotation painter for the given source viewer and with the | |
570 * given annotation access. The painter is not initialized, i.e. no | |
571 * annotation types are configured to be painted. | |
572 * | |
573 * @param sourceViewer the source viewer for this painter | |
574 * @param access the annotation access for this painter | |
575 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
576 public this(ISourceViewer sourceViewer, IAnnotationAccess access) { |
162 | 577 fDecorationsMap= new HashMap(); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=50767 |
578 fHighlightedDecorationsMap= new HashMap(); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=50767 | |
579 fDecorationMapLock= new Object(); | |
580 fHighlightedDecorationsMapLock= new Object(); | |
581 fAnnotationType2Color= new HashMap(); | |
582 fCachedAnnotationType2Color= new HashMap(); | |
583 fReusableRegion= new ReusableRegion(); | |
584 fAnnotationType2PaintingStrategyId= new HashMap(); | |
585 fCachedAnnotationType2PaintingStrategy= new HashMap(); | |
586 fPaintingStrategyId2PaintingStrategy= new HashMap(); | |
587 | |
129 | 588 fSourceViewer= sourceViewer; |
589 fAnnotationAccess= access; | |
590 fTextWidget= sourceViewer.getTextWidget(); | |
591 | |
592 // default drawing strategies: squiggles were the only decoration style before version 3.0 | |
162 | 593 fPaintingStrategyId2PaintingStrategy.put(SQUIGGLES, cast(Object)SQUIGGLES_STRATEGY); |
594 fPaintingStrategyId2PaintingStrategy.put(HIGHLIGHTING, cast(Object)HIGHLIGHTING_STRATEGY); | |
129 | 595 } |
596 | |
597 /** | |
598 * Returns whether this painter has to draw any squiggles. | |
599 * | |
600 * @return <code>true</code> if there are squiggles to be drawn, <code>false</code> otherwise | |
601 */ | |
602 private bool hasDecorations() { | |
603 synchronized (fDecorationMapLock) { | |
604 return !fDecorationsMap.isEmpty(); | |
605 } | |
606 } | |
607 | |
608 /** | |
609 * Enables painting. This painter registers a paint listener with the | |
610 * source viewer's widget. | |
611 */ | |
612 private void enablePainting() { | |
613 if (!fIsPainting && hasDecorations()) { | |
614 fIsPainting= true; | |
615 fTextWidget.addPaintListener(this); | |
616 handleDrawRequest(null); | |
617 } | |
618 } | |
619 | |
620 /** | |
621 * Disables painting, if is has previously been enabled. Removes | |
622 * any paint listeners registered with the source viewer's widget. | |
623 * | |
624 * @param redraw <code>true</code> if the widget should be redrawn after disabling | |
625 */ | |
626 private void disablePainting(bool redraw) { | |
627 if (fIsPainting) { | |
628 fIsPainting= false; | |
629 fTextWidget.removePaintListener(this); | |
630 if (redraw && hasDecorations()) | |
631 handleDrawRequest(null); | |
632 } | |
633 } | |
634 | |
635 /** | |
636 * Sets the annotation model for this painter. Registers this painter | |
637 * as listener of the give model, if the model is not <code>null</code>. | |
638 * | |
639 * @param model the annotation model | |
640 */ | |
641 private void setModel(IAnnotationModel model) { | |
642 if (fModel !is model) { | |
643 if (fModel !is null) | |
644 fModel.removeAnnotationModelListener(this); | |
645 fModel= model; | |
646 if (fModel !is null) { | |
647 try { | |
648 fIsSettingModel= true; | |
649 fModel.addAnnotationModelListener(this); | |
650 } finally { | |
651 fIsSettingModel= false; | |
652 } | |
653 } | |
654 } | |
655 } | |
656 | |
657 /** | |
658 * Updates the set of decorations based on the current state of | |
659 * the painter's annotation model. | |
660 * | |
661 * @param event the annotation model event | |
662 */ | |
663 private void catchupWithModel(AnnotationModelEvent event) { | |
664 | |
665 synchronized (fDecorationMapLock) { | |
666 if (fDecorationsMap is null) | |
667 return; | |
668 } | |
669 | |
670 IRegion clippingRegion= computeClippingRegion(null, true); | |
671 IDocument document= fSourceViewer.getDocument(); | |
672 | |
673 int highlightAnnotationRangeStart= Integer.MAX_VALUE; | |
674 int highlightAnnotationRangeEnd= -1; | |
145 | 675 |
129 | 676 int drawRangeStart= Integer.MAX_VALUE; |
677 int drawRangeEnd= -1; | |
678 | |
679 if (fModel !is null) { | |
680 | |
681 Map decorationsMap; | |
682 Map highlightedDecorationsMap; | |
683 | |
684 // Clone decoration maps | |
685 synchronized (fDecorationMapLock) { | |
686 decorationsMap= new HashMap(fDecorationsMap); | |
687 } | |
688 synchronized (fHighlightedDecorationsMapLock) { | |
689 highlightedDecorationsMap= new HashMap(fHighlightedDecorationsMap); | |
690 } | |
691 | |
692 bool isWorldChange= false; | |
693 | |
694 Iterator e; | |
695 if (event is null || event.isWorldChange()) { | |
696 isWorldChange= true; | |
697 | |
698 if (DEBUG && event is null) | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
699 System.out_.println("AP: INTERNAL CHANGE"); //$NON-NLS-1$ |
129 | 700 |
701 Iterator iter= decorationsMap.entrySet().iterator(); | |
702 while (iter.hasNext()) { | |
157
7f75eaa8103a
volatile and cast Map.Entry
Frank Benoit <benoit@tionex.de>
parents:
153
diff
changeset
|
703 Map.Entry entry= cast(Map.Entry)iter.next(); |
134 | 704 Annotation annotation= cast(Annotation)entry.getKey(); |
705 Decoration decoration= cast(Decoration)entry.getValue(); | |
129 | 706 drawDecoration(decoration, null, annotation, clippingRegion, document); |
707 } | |
145 | 708 |
129 | 709 decorationsMap.clear(); |
145 | 710 |
129 | 711 highlightedDecorationsMap.clear(); |
712 | |
713 e= fModel.getAnnotationIterator(); | |
714 | |
715 | |
716 } else { | |
717 | |
718 // Remove annotations | |
719 Annotation[] removedAnnotations= event.getRemovedAnnotations(); | |
720 for (int i=0, length= removedAnnotations.length; i < length; i++) { | |
721 Annotation annotation= removedAnnotations[i]; | |
134 | 722 Decoration decoration= cast(Decoration)highlightedDecorationsMap.remove(annotation); |
129 | 723 if (decoration !is null) { |
724 Position position= decoration.fPosition; | |
725 if (position !is null) { | |
726 highlightAnnotationRangeStart= Math.min(highlightAnnotationRangeStart, position.offset); | |
727 highlightAnnotationRangeEnd= Math.max(highlightAnnotationRangeEnd, position.offset + position.length); | |
728 } | |
729 } | |
134 | 730 decoration= cast(Decoration)decorationsMap.remove(annotation); |
129 | 731 if (decoration !is null) { |
732 drawDecoration(decoration, null, annotation, clippingRegion, document); | |
733 Position position= decoration.fPosition; | |
734 if (position !is null) { | |
735 drawRangeStart= Math.min(drawRangeStart, position.offset); | |
736 drawRangeEnd= Math.max(drawRangeEnd, position.offset + position.length); | |
737 } | |
738 } | |
145 | 739 |
129 | 740 } |
741 | |
742 // Update existing annotations | |
743 Annotation[] changedAnnotations= event.getChangedAnnotations(); | |
744 for (int i=0, length= changedAnnotations.length; i < length; i++) { | |
745 Annotation annotation= changedAnnotations[i]; | |
746 | |
747 bool isHighlighting= false; | |
145 | 748 |
134 | 749 Decoration decoration= cast(Decoration)highlightedDecorationsMap.get(annotation); |
129 | 750 |
751 if (decoration !is null) { | |
752 isHighlighting= true; | |
753 // The call below updates the decoration - no need to create new decoration | |
754 decoration= getDecoration(annotation, decoration); | |
755 if (decoration is null) | |
756 highlightedDecorationsMap.remove(annotation); | |
757 } else { | |
758 decoration= getDecoration(annotation, decoration); | |
145 | 759 if (decoration !is null && cast(ITextStyleStrategy)decoration.fPaintingStrategy ) { |
129 | 760 highlightedDecorationsMap.put(annotation, decoration); |
761 isHighlighting= true; | |
762 } | |
763 } | |
764 | |
765 bool usesDrawingStrategy= !isHighlighting && decoration !is null; | |
766 | |
767 Position position= null; | |
768 if (decoration is null) | |
769 position= fModel.getPosition(annotation); | |
770 else | |
771 position= decoration.fPosition; | |
772 | |
773 if (position !is null && !position.isDeleted()) { | |
774 if (isHighlighting) { | |
775 highlightAnnotationRangeStart= Math.min(highlightAnnotationRangeStart, position.offset); | |
776 highlightAnnotationRangeEnd= Math.max(highlightAnnotationRangeEnd, position.offset + position.length); | |
777 } | |
778 if (usesDrawingStrategy) { | |
779 drawRangeStart= Math.min(drawRangeStart, position.offset); | |
780 drawRangeEnd= Math.max(drawRangeEnd, position.offset + position.length); | |
781 } | |
782 } else { | |
783 highlightedDecorationsMap.remove(annotation); | |
784 } | |
785 | |
786 if (usesDrawingStrategy) { | |
134 | 787 Decoration oldDecoration= cast(Decoration)decorationsMap.get(annotation); |
129 | 788 if (oldDecoration !is null) { |
789 drawDecoration(oldDecoration, null, annotation, clippingRegion, document); | |
145 | 790 |
129 | 791 if (decoration !is null) |
792 decorationsMap.put(annotation, decoration); | |
793 else if (oldDecoration !is null) | |
794 decorationsMap.remove(annotation); | |
795 } | |
796 } | |
797 } | |
798 | |
799 e= Arrays.asList(event.getAddedAnnotations()).iterator(); | |
800 } | |
801 | |
802 // Add new annotations | |
803 while (e.hasNext()) { | |
134 | 804 Annotation annotation= cast(Annotation) e.next(); |
129 | 805 Decoration pp= getDecoration(annotation, null); |
806 if (pp !is null) { | |
145 | 807 if (cast(IDrawingStrategy)pp.fPaintingStrategy ) { |
129 | 808 decorationsMap.put(annotation, pp); |
809 drawRangeStart= Math.min(drawRangeStart, pp.fPosition.offset); | |
810 drawRangeEnd= Math.max(drawRangeEnd, pp.fPosition.offset + pp.fPosition.length); | |
145 | 811 } else if (cast(ITextStyleStrategy)pp.fPaintingStrategy ) { |
129 | 812 highlightedDecorationsMap.put(annotation, pp); |
813 highlightAnnotationRangeStart= Math.min(highlightAnnotationRangeStart, pp.fPosition.offset); | |
814 highlightAnnotationRangeEnd= Math.max(highlightAnnotationRangeEnd, pp.fPosition.offset + pp.fPosition.length); | |
815 } | |
145 | 816 |
129 | 817 } |
818 } | |
819 | |
820 synchronized (fDecorationMapLock) { | |
821 fDecorationsMap= decorationsMap; | |
822 updateDrawRanges(drawRangeStart, drawRangeEnd, isWorldChange); | |
823 } | |
824 | |
825 synchronized (fHighlightedDecorationsMapLock) { | |
826 fHighlightedDecorationsMap= highlightedDecorationsMap; | |
827 updateHighlightRanges(highlightAnnotationRangeStart, highlightAnnotationRangeEnd, isWorldChange); | |
828 } | |
829 } else { | |
830 // annotation model is null -> clear all | |
831 synchronized (fDecorationMapLock) { | |
832 fDecorationsMap.clear(); | |
833 } | |
834 synchronized (fHighlightedDecorationsMapLock) { | |
835 fHighlightedDecorationsMap.clear(); | |
836 } | |
837 } | |
838 } | |
839 | |
840 /** | |
841 * Updates the remembered highlight ranges. | |
842 * | |
843 * @param highlightAnnotationRangeStart the start of the range | |
844 * @param highlightAnnotationRangeEnd the end of the range | |
845 * @param isWorldChange tells whether the range belongs to a annotation model event reporting a world change | |
846 * @since 3.0 | |
847 */ | |
848 private void updateHighlightRanges(int highlightAnnotationRangeStart, int highlightAnnotationRangeEnd, bool isWorldChange) { | |
849 if (highlightAnnotationRangeStart !is Integer.MAX_VALUE) { | |
850 | |
851 int maxRangeStart= highlightAnnotationRangeStart; | |
852 int maxRangeEnd= highlightAnnotationRangeEnd; | |
853 | |
854 if (fTotalHighlightAnnotationRange !is null) { | |
855 maxRangeStart= Math.min(maxRangeStart, fTotalHighlightAnnotationRange.offset); | |
856 maxRangeEnd= Math.max(maxRangeEnd, fTotalHighlightAnnotationRange.offset + fTotalHighlightAnnotationRange.length); | |
857 } | |
858 | |
859 if (fTotalHighlightAnnotationRange is null) | |
860 fTotalHighlightAnnotationRange= new Position(0); | |
861 if (fCurrentHighlightAnnotationRange is null) | |
862 fCurrentHighlightAnnotationRange= new Position(0); | |
863 | |
864 if (isWorldChange) { | |
865 fTotalHighlightAnnotationRange.offset= highlightAnnotationRangeStart; | |
866 fTotalHighlightAnnotationRange.length= highlightAnnotationRangeEnd - highlightAnnotationRangeStart; | |
867 fCurrentHighlightAnnotationRange.offset= maxRangeStart; | |
868 fCurrentHighlightAnnotationRange.length= maxRangeEnd - maxRangeStart; | |
869 } else { | |
870 fTotalHighlightAnnotationRange.offset= maxRangeStart; | |
871 fTotalHighlightAnnotationRange.length= maxRangeEnd - maxRangeStart; | |
872 fCurrentHighlightAnnotationRange.offset=highlightAnnotationRangeStart; | |
873 fCurrentHighlightAnnotationRange.length= highlightAnnotationRangeEnd - highlightAnnotationRangeStart; | |
874 } | |
875 } else { | |
876 if (isWorldChange) { | |
877 fCurrentHighlightAnnotationRange= fTotalHighlightAnnotationRange; | |
878 fTotalHighlightAnnotationRange= null; | |
879 } else { | |
880 fCurrentHighlightAnnotationRange= null; | |
881 } | |
882 } | |
883 | |
884 adaptToDocumentLength(fCurrentHighlightAnnotationRange); | |
885 adaptToDocumentLength(fTotalHighlightAnnotationRange); | |
886 } | |
145 | 887 |
129 | 888 /** |
889 * Updates the remembered decoration ranges. | |
890 * | |
891 * @param drawRangeStart the start of the range | |
892 * @param drawRangeEnd the end of the range | |
893 * @param isWorldChange tells whether the range belongs to a annotation model event reporting a world change | |
894 * @since 3.3 | |
895 */ | |
896 private void updateDrawRanges(int drawRangeStart, int drawRangeEnd, bool isWorldChange) { | |
897 if (drawRangeStart !is Integer.MAX_VALUE) { | |
145 | 898 |
129 | 899 int maxRangeStart= drawRangeStart; |
900 int maxRangeEnd= drawRangeEnd; | |
145 | 901 |
129 | 902 if (fTotalDrawRange !is null) { |
903 maxRangeStart= Math.min(maxRangeStart, fTotalDrawRange.offset); | |
904 maxRangeEnd= Math.max(maxRangeEnd, fTotalDrawRange.offset + fTotalDrawRange.length); | |
905 } | |
145 | 906 |
129 | 907 if (fTotalDrawRange is null) |
908 fTotalDrawRange= new Position(0); | |
909 if (fCurrentDrawRange is null) | |
910 fCurrentDrawRange= new Position(0); | |
145 | 911 |
129 | 912 if (isWorldChange) { |
913 fTotalDrawRange.offset= drawRangeStart; | |
914 fTotalDrawRange.length= drawRangeEnd - drawRangeStart; | |
915 fCurrentDrawRange.offset= maxRangeStart; | |
916 fCurrentDrawRange.length= maxRangeEnd - maxRangeStart; | |
917 } else { | |
918 fTotalDrawRange.offset= maxRangeStart; | |
919 fTotalDrawRange.length= maxRangeEnd - maxRangeStart; | |
920 fCurrentDrawRange.offset=drawRangeStart; | |
921 fCurrentDrawRange.length= drawRangeEnd - drawRangeStart; | |
922 } | |
923 } else { | |
924 if (isWorldChange) { | |
925 fCurrentDrawRange= fTotalDrawRange; | |
926 fTotalDrawRange= null; | |
927 } else { | |
928 fCurrentDrawRange= null; | |
929 } | |
930 } | |
145 | 931 |
129 | 932 adaptToDocumentLength(fCurrentDrawRange); |
933 adaptToDocumentLength(fTotalDrawRange); | |
934 } | |
935 | |
936 /** | |
937 * Adapts the given position to the document length. | |
938 * | |
939 * @param position the position to adapt | |
940 * @since 3.0 | |
941 */ | |
942 private void adaptToDocumentLength(Position position) { | |
943 if (position is null) | |
944 return; | |
945 | |
946 int length= fSourceViewer.getDocument().getLength(); | |
947 position.offset= Math.min(position.offset, length); | |
948 position.length= Math.min(position.length, length - position.offset); | |
949 } | |
950 | |
951 /** | |
952 * Returns a decoration for the given annotation if this | |
953 * annotation is valid and shown by this painter. | |
954 * | |
955 * @param annotation the annotation | |
956 * @param decoration the decoration to be adapted and returned or <code>null</code> if a new one must be created | |
957 * @return the decoration or <code>null</code> if there's no valid one | |
958 * @since 3.0 | |
959 */ | |
960 private Decoration getDecoration(Annotation annotation, Decoration decoration) { | |
961 | |
962 if (annotation.isMarkedDeleted()) | |
963 return null; | |
964 | |
965 String type= annotation.getType(); | |
966 | |
967 Object paintingStrategy= getPaintingStrategy(type); | |
144 | 968 if (paintingStrategy is null || cast(NullStrategy)paintingStrategy ) |
129 | 969 return null; |
970 | |
162 | 971 Color color= getColor(stringcast(type)); |
129 | 972 if (color is null) |
973 return null; | |
974 | |
975 Position position= fModel.getPosition(annotation); | |
976 if (position is null || position.isDeleted()) | |
977 return null; | |
145 | 978 |
129 | 979 if (decoration is null) |
980 decoration= new Decoration(); | |
981 | |
982 decoration.fPosition= position; | |
983 decoration.fColor= color; | |
138 | 984 if ( cast(IAnnotationAccessExtension)fAnnotationAccess ) { |
134 | 985 IAnnotationAccessExtension extension= cast(IAnnotationAccessExtension) fAnnotationAccess; |
129 | 986 decoration.fLayer= extension.getLayer(annotation); |
987 } else { | |
988 decoration.fLayer= IAnnotationAccessExtension.DEFAULT_LAYER; | |
989 } | |
145 | 990 |
129 | 991 decoration.fPaintingStrategy= paintingStrategy; |
145 | 992 |
129 | 993 return decoration; |
994 } | |
995 | |
996 /** | |
997 * Returns the painting strategy for the given annotation. | |
145 | 998 * |
129 | 999 * @param type the annotation type |
1000 * @return the annotation painter | |
1001 * @since 3.0 | |
1002 */ | |
158 | 1003 private Object getPaintingStrategy(String type) { |
129 | 1004 Object strategy= fCachedAnnotationType2PaintingStrategy.get(type); |
1005 if (strategy !is null) | |
1006 return strategy; | |
1007 | |
1008 strategy= fPaintingStrategyId2PaintingStrategy.get(fAnnotationType2PaintingStrategyId.get(type)); | |
1009 if (strategy !is null) { | |
1010 fCachedAnnotationType2PaintingStrategy.put(type, strategy); | |
1011 return strategy; | |
1012 } | |
1013 | |
138 | 1014 if ( cast(IAnnotationAccessExtension)fAnnotationAccess ) { |
134 | 1015 IAnnotationAccessExtension ext = cast(IAnnotationAccessExtension) fAnnotationAccess; |
162 | 1016 Object[] sts = ext.getSupertypes(stringcast(type)); |
129 | 1017 for (int i= 0; i < sts.length; i++) { |
1018 strategy= fPaintingStrategyId2PaintingStrategy.get(fAnnotationType2PaintingStrategyId.get(sts[i])); | |
1019 if (strategy !is null) { | |
1020 fCachedAnnotationType2PaintingStrategy.put(type, strategy); | |
1021 return strategy; | |
1022 } | |
1023 } | |
1024 } | |
1025 | |
162 | 1026 fCachedAnnotationType2PaintingStrategy.put(type, cast(Object)NULL_STRATEGY); |
129 | 1027 return null; |
1028 | |
1029 } | |
1030 | |
1031 /** | |
1032 * Returns the color for the given annotation type | |
1033 * | |
1034 * @param annotationType the annotation type | |
1035 * @return the color | |
1036 * @since 3.0 | |
1037 */ | |
158 | 1038 private Color getColor(Object annotationType) { |
134 | 1039 Color color= cast(Color)fCachedAnnotationType2Color.get(annotationType); |
129 | 1040 if (color !is null) |
1041 return color; | |
1042 | |
134 | 1043 color= cast(Color)fAnnotationType2Color.get(annotationType); |
129 | 1044 if (color !is null) { |
1045 fCachedAnnotationType2Color.put(annotationType, color); | |
1046 return color; | |
1047 } | |
1048 | |
138 | 1049 if ( cast(IAnnotationAccessExtension)fAnnotationAccess ) { |
134 | 1050 IAnnotationAccessExtension extension= cast(IAnnotationAccessExtension) fAnnotationAccess; |
129 | 1051 Object[] superTypes= extension.getSupertypes(annotationType); |
1052 if (superTypes !is null) { | |
1053 for (int i= 0; i < superTypes.length; i++) { | |
134 | 1054 color= cast(Color)fAnnotationType2Color.get(superTypes[i]); |
129 | 1055 if (color !is null) { |
1056 fCachedAnnotationType2Color.put(annotationType, color); | |
1057 return color; | |
1058 } | |
1059 } | |
1060 } | |
1061 } | |
1062 | |
1063 return null; | |
1064 } | |
1065 | |
1066 /** | |
1067 * Recomputes the squiggles to be drawn and redraws them. | |
1068 * | |
1069 * @param event the annotation model event | |
1070 * @since 3.0 | |
1071 */ | |
1072 private void updatePainting(AnnotationModelEvent event) { | |
1073 disablePainting(event is null); | |
1074 | |
1075 catchupWithModel(event); | |
1076 | |
1077 if (!fInputDocumentAboutToBeChanged) | |
1078 invalidateTextPresentation(); | |
1079 | |
1080 enablePainting(); | |
1081 } | |
1082 | |
1083 private void invalidateTextPresentation() { | |
1084 IRegion r= null; | |
1085 synchronized (fHighlightedDecorationsMapLock) { | |
1086 if (fCurrentHighlightAnnotationRange !is null) | |
1087 r= new Region(fCurrentHighlightAnnotationRange.getOffset(), fCurrentHighlightAnnotationRange.getLength()); | |
1088 } | |
1089 if (r is null) | |
1090 return; | |
1091 | |
138 | 1092 if ( cast(ITextViewerExtension2)fSourceViewer ) { |
145 | 1093 if (DEBUG) |
162 | 1094 System.out_.println(Format("AP: invalidating offset: {}, length= {}", r.getOffset(), r.getLength())); //$NON-NLS-1$ //$NON-NLS-2$ |
129 | 1095 |
134 | 1096 (cast(ITextViewerExtension2)fSourceViewer).invalidateTextPresentation(r.getOffset(), r.getLength()); |
129 | 1097 |
1098 } else { | |
1099 fSourceViewer.invalidateTextPresentation(); | |
1100 } | |
1101 } | |
1102 | |
1103 /* | |
1104 * @see dwtx.jface.text.ITextPresentationListener#applyTextPresentation(dwtx.jface.text.TextPresentation) | |
1105 * @since 3.0 | |
1106 */ | |
1107 public void applyTextPresentation(TextPresentation tp) { | |
1108 Set decorations; | |
1109 | |
1110 synchronized (fHighlightedDecorationsMapLock) { | |
1111 if (fHighlightedDecorationsMap is null || fHighlightedDecorationsMap.isEmpty()) | |
1112 return; | |
1113 | |
1114 decorations= new HashSet(fHighlightedDecorationsMap.entrySet()); | |
1115 } | |
1116 | |
1117 IRegion region= tp.getExtent(); | |
1118 | |
145 | 1119 if (DEBUG) |
162 | 1120 System.out_.println(Format("AP: applying text presentation offset: {}, length= {}", region.getOffset(), region.getLength())); //$NON-NLS-1$ //$NON-NLS-2$ |
129 | 1121 |
1122 for (int layer= 0, maxLayer= 1; layer < maxLayer; layer++) { | |
1123 | |
1124 for (Iterator iter= decorations.iterator(); iter.hasNext();) { | |
157
7f75eaa8103a
volatile and cast Map.Entry
Frank Benoit <benoit@tionex.de>
parents:
153
diff
changeset
|
1125 Map.Entry entry= cast(Map.Entry)iter.next(); |
129 | 1126 |
134 | 1127 Annotation a= cast(Annotation)entry.getKey(); |
129 | 1128 if (a.isMarkedDeleted()) |
1129 continue; | |
1130 | |
134 | 1131 Decoration pp = cast(Decoration)entry.getValue(); |
129 | 1132 |
1133 maxLayer= Math.max(maxLayer, pp.fLayer + 1); // dynamically update layer maximum | |
1134 if (pp.fLayer !is layer) // wrong layer: skip annotation | |
1135 continue; | |
1136 | |
1137 Position p= pp.fPosition; | |
138 | 1138 if ( cast(ITextViewerExtension5)fSourceViewer ) { |
134 | 1139 ITextViewerExtension5 extension3= cast(ITextViewerExtension5) fSourceViewer; |
129 | 1140 if (null is extension3.modelRange2WidgetRange(new Region(p.getOffset(), p.getLength()))) |
1141 continue; | |
1142 } else if (!fSourceViewer.overlapsWithVisibleRegion(p.offset, p.length)) { | |
1143 continue; | |
1144 } | |
1145 | |
1146 int regionEnd= region.getOffset() + region.getLength(); | |
1147 int pEnd= p.getOffset() + p.getLength(); | |
1148 if (pEnd >= region.getOffset() && regionEnd > p.getOffset()) { | |
1149 int start= Math.max(p.getOffset(), region.getOffset()); | |
1150 int end= Math.min(regionEnd, pEnd); | |
1151 int length= Math.max(end - start, 0); | |
1152 StyleRange styleRange= new StyleRange(start, length, null, null); | |
134 | 1153 (cast(ITextStyleStrategy)pp.fPaintingStrategy).applyTextStyle(styleRange, pp.fColor); |
129 | 1154 tp.mergeStyleRange(styleRange); |
1155 } | |
1156 } | |
1157 } | |
1158 } | |
1159 | |
1160 /* | |
1161 * @see dwtx.jface.text.source.IAnnotationModelListener#modelChanged(dwtx.jface.text.source.IAnnotationModel) | |
1162 */ | |
158 | 1163 public synchronized void modelChanged(IAnnotationModel model) { |
145 | 1164 if (DEBUG) |
129 | 1165 System.err.println("AP: OLD API of AnnotationModelListener called"); //$NON-NLS-1$ |
1166 | |
1167 modelChanged(new AnnotationModelEvent(model)); | |
1168 } | |
1169 | |
1170 /* | |
1171 * @see dwtx.jface.text.source.IAnnotationModelListenerExtension#modelChanged(dwtx.jface.text.source.AnnotationModelEvent) | |
1172 */ | |
158 | 1173 public void modelChanged(AnnotationModelEvent event) { |
129 | 1174 Display textWidgetDisplay; |
1175 try { | |
1176 StyledText textWidget= fTextWidget; | |
1177 if (textWidget is null || textWidget.isDisposed()) | |
1178 return; | |
1179 textWidgetDisplay= textWidget.getDisplay(); | |
1180 } catch (DWTException ex) { | |
1181 if (ex.code is DWT.ERROR_WIDGET_DISPOSED) | |
1182 return; | |
1183 throw ex; | |
1184 } | |
1185 | |
1186 if (fIsSettingModel) { | |
1187 // inside the UI thread -> no need for posting | |
1188 if (textWidgetDisplay is Display.getCurrent()) | |
1189 updatePainting(event); | |
1190 else { | |
1191 /* | |
1192 * we can throw away the changes since | |
1193 * further update painting will happen | |
1194 */ | |
1195 return; | |
1196 } | |
1197 } else { | |
1198 if (DEBUG && event !is null && event.isWorldChange()) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1199 System.out_.println("AP: WORLD CHANGED, stack trace follows:"); //$NON-NLS-1$ |
145 | 1200 ExceptionPrintStackTrace( new Exception(""), Stdout ); |
129 | 1201 } |
1202 | |
1203 // XXX: posting here is a problem for annotations that are being | |
1204 // removed and the positions of which are not updated to document | |
1205 // changes any more. If the document gets modified between | |
1206 // now and running the posted runnable, the position information | |
1207 // is not accurate any longer. | |
162 | 1208 textWidgetDisplay.asyncExec( dgRunnable( (AnnotationModelEvent event_){ |
158 | 1209 if (fTextWidget !is null && !fTextWidget.isDisposed()) |
1210 updatePainting(event_); | |
1211 }, event )); | |
129 | 1212 } |
1213 } | |
1214 | |
1215 /** | |
1216 * Sets the color in which the squiggly for the given annotation type should be drawn. | |
1217 * | |
1218 * @param annotationType the annotation type | |
1219 * @param color the color | |
1220 */ | |
1221 public void setAnnotationTypeColor(Object annotationType, Color color) { | |
1222 if (color !is null) | |
1223 fAnnotationType2Color.put(annotationType, color); | |
1224 else | |
1225 fAnnotationType2Color.remove(annotationType); | |
1226 fCachedAnnotationType2Color.clear(); | |
1227 } | |
1228 | |
1229 /** | |
1230 * Adds the given annotation type to the list of annotation types whose | |
1231 * annotations should be painted by this painter using squiggly drawing. If the annotation type | |
1232 * is already in this list, this method is without effect. | |
1233 * | |
1234 * @param annotationType the annotation type | |
1235 */ | |
1236 public void addAnnotationType(Object annotationType) { | |
1237 addAnnotationType(annotationType, SQUIGGLES); | |
1238 } | |
1239 | |
1240 /** | |
1241 * Adds the given annotation type to the list of annotation types whose | |
1242 * annotations should be painted by this painter using the given drawing strategy. | |
1243 * If the annotation type is already in this list, the old drawing strategy gets replaced. | |
1244 * | |
1245 * @param annotationType the annotation type | |
1246 * @param drawingStrategyID the id of the drawing strategy that should be used for this annotation type | |
1247 * @since 3.0 | |
1248 */ | |
1249 public void addAnnotationType(Object annotationType, Object drawingStrategyID) { | |
1250 fAnnotationType2PaintingStrategyId.put(annotationType, drawingStrategyID); | |
1251 fCachedAnnotationType2PaintingStrategy.clear(); | |
1252 | |
1253 if (fTextInputListener is null) { | |
135 | 1254 fTextInputListener= new class() ITextInputListener { |
129 | 1255 |
1256 /* | |
1257 * @see dwtx.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument) | |
1258 */ | |
1259 public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { | |
1260 fInputDocumentAboutToBeChanged= true; | |
1261 } | |
1262 | |
1263 /* | |
1264 * @see dwtx.jface.text.ITextInputListener#inputDocumentChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument) | |
1265 */ | |
1266 public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { | |
1267 fInputDocumentAboutToBeChanged= false; | |
1268 } | |
1269 }; | |
1270 fSourceViewer.addTextInputListener(fTextInputListener); | |
1271 } | |
145 | 1272 |
129 | 1273 } |
1274 | |
1275 /** | |
1276 * Registers a new drawing strategy under the given ID. If there is already a | |
1277 * strategy registered under <code>id</code>, the old strategy gets replaced. | |
1278 * <p>The given id can be referenced when adding annotation types, see | |
1279 * {@link #addAnnotationType(Object, Object)}.</p> | |
1280 * | |
1281 * @param id the identifier under which the strategy can be referenced, not <code>null</code> | |
1282 * @param strategy the new strategy | |
1283 * @since 3.0 | |
1284 */ | |
1285 public void addDrawingStrategy(Object id, IDrawingStrategy strategy) { | |
1286 // don't permit null as null is used to signal that an annotation type is not | |
1287 // registered with a specific strategy, and that its annotation hierarchy should be searched | |
1288 if (id is null) | |
162 | 1289 throw new IllegalArgumentException(null); |
1290 fPaintingStrategyId2PaintingStrategy.put(id, cast(Object)strategy); | |
129 | 1291 fCachedAnnotationType2PaintingStrategy.clear(); |
1292 } | |
1293 | |
1294 /** | |
1295 * Registers a new drawing strategy under the given ID. If there is already | |
1296 * a strategy registered under <code>id</code>, the old strategy gets | |
1297 * replaced. | |
1298 * <p> | |
1299 * The given id can be referenced when adding annotation types, see | |
1300 * {@link #addAnnotationType(Object, Object)}. | |
1301 * </p> | |
145 | 1302 * |
129 | 1303 * @param id the identifier under which the strategy can be referenced, not <code>null</code> |
1304 * @param strategy the new strategy | |
1305 * @since 3.4 | |
1306 */ | |
1307 public void addTextStyleStrategy(Object id, ITextStyleStrategy strategy) { | |
1308 // don't permit null as null is used to signal that an annotation type is not | |
1309 // registered with a specific strategy, and that its annotation hierarchy should be searched | |
1310 if (id is null) | |
162 | 1311 throw new IllegalArgumentException(null); |
1312 fPaintingStrategyId2PaintingStrategy.put(id, cast(Object)strategy); | |
129 | 1313 fCachedAnnotationType2PaintingStrategy.clear(); |
1314 } | |
1315 | |
1316 /** | |
1317 * Adds the given annotation type to the list of annotation types whose | |
1318 * annotations should be highlighted this painter. If the annotation type | |
1319 * is already in this list, this method is without effect. | |
1320 * | |
1321 * @param annotationType the annotation type | |
1322 * @since 3.0 | |
1323 */ | |
1324 public void addHighlightAnnotationType(Object annotationType) { | |
1325 addAnnotationType(annotationType, HIGHLIGHTING); | |
1326 } | |
1327 | |
1328 /** | |
1329 * Removes the given annotation type from the list of annotation types whose | |
1330 * annotations are painted by this painter. If the annotation type is not | |
1331 * in this list, this method is without effect. | |
1332 * | |
1333 * @param annotationType the annotation type | |
1334 */ | |
1335 public void removeAnnotationType(Object annotationType) { | |
1336 fCachedAnnotationType2PaintingStrategy.clear(); | |
1337 fAnnotationType2PaintingStrategyId.remove(annotationType); | |
1338 if (fAnnotationType2PaintingStrategyId.isEmpty() && fTextInputListener !is null) { | |
1339 fSourceViewer.removeTextInputListener(fTextInputListener); | |
1340 fTextInputListener= null; | |
1341 fInputDocumentAboutToBeChanged= false; | |
1342 } | |
1343 } | |
1344 | |
1345 /** | |
1346 * Removes the given annotation type from the list of annotation types whose | |
1347 * annotations are highlighted by this painter. If the annotation type is not | |
1348 * in this list, this method is without effect. | |
1349 * | |
1350 * @param annotationType the annotation type | |
1351 * @since 3.0 | |
1352 */ | |
1353 public void removeHighlightAnnotationType(Object annotationType) { | |
1354 removeAnnotationType(annotationType); | |
1355 } | |
1356 | |
1357 /** | |
1358 * Clears the list of annotation types whose annotations are | |
1359 * painted by this painter. | |
1360 */ | |
1361 public void removeAllAnnotationTypes() { | |
1362 fCachedAnnotationType2PaintingStrategy.clear(); | |
1363 fAnnotationType2PaintingStrategyId.clear(); | |
1364 if (fTextInputListener !is null) { | |
1365 fSourceViewer.removeTextInputListener(fTextInputListener); | |
1366 fTextInputListener= null; | |
1367 } | |
1368 } | |
1369 | |
1370 /** | |
1371 * Returns whether the list of annotation types whose annotations are painted | |
1372 * by this painter contains at least on element. | |
1373 * | |
1374 * @return <code>true</code> if there is an annotation type whose annotations are painted | |
1375 */ | |
1376 public bool isPaintingAnnotations() { | |
1377 return !fAnnotationType2PaintingStrategyId.isEmpty(); | |
1378 } | |
1379 | |
1380 /* | |
1381 * @see dwtx.jface.text.IPainter#dispose() | |
1382 */ | |
1383 public void dispose() { | |
1384 | |
1385 if (fAnnotationType2Color !is null) { | |
1386 fAnnotationType2Color.clear(); | |
1387 fAnnotationType2Color= null; | |
1388 } | |
1389 | |
1390 if (fCachedAnnotationType2Color !is null) { | |
1391 fCachedAnnotationType2Color.clear(); | |
1392 fCachedAnnotationType2Color= null; | |
1393 } | |
1394 | |
1395 if (fCachedAnnotationType2PaintingStrategy !is null) { | |
1396 fCachedAnnotationType2PaintingStrategy.clear(); | |
1397 fCachedAnnotationType2PaintingStrategy= null; | |
1398 } | |
145 | 1399 |
129 | 1400 if (fAnnotationType2PaintingStrategyId !is null) { |
1401 fAnnotationType2PaintingStrategyId.clear(); | |
1402 fAnnotationType2PaintingStrategyId= null; | |
1403 } | |
145 | 1404 |
129 | 1405 fTextWidget= null; |
1406 fSourceViewer= null; | |
1407 fAnnotationAccess= null; | |
1408 fModel= null; | |
1409 synchronized (fDecorationMapLock) { | |
1410 fDecorationsMap= null; | |
1411 } | |
1412 synchronized (fHighlightedDecorationsMapLock) { | |
1413 fHighlightedDecorationsMap= null; | |
1414 } | |
1415 } | |
1416 | |
1417 /** | |
1418 * Returns the document offset of the upper left corner of the source viewer's view port, | |
1419 * possibly including partially visible lines. | |
1420 * | |
1421 * @return the document offset if the upper left corner of the view port | |
1422 */ | |
1423 private int getInclusiveTopIndexStartOffset() { | |
1424 | |
1425 if (fTextWidget !is null && !fTextWidget.isDisposed()) { | |
1426 int top= JFaceTextUtil.getPartialTopIndex(fSourceViewer); | |
1427 try { | |
1428 IDocument document= fSourceViewer.getDocument(); | |
1429 return document.getLineOffset(top); | |
1430 } catch (BadLocationException x) { | |
1431 } | |
1432 } | |
1433 | |
1434 return -1; | |
1435 } | |
1436 | |
1437 /** | |
1438 * Returns the first invisible document offset of the lower right corner of the source viewer's view port, | |
1439 * possibly including partially visible lines. | |
1440 * | |
1441 * @return the first invisible document offset of the lower right corner of the view port | |
1442 */ | |
1443 private int getExclusiveBottomIndexEndOffset() { | |
1444 | |
1445 if (fTextWidget !is null && !fTextWidget.isDisposed()) { | |
1446 int bottom= JFaceTextUtil.getPartialBottomIndex(fSourceViewer); | |
1447 try { | |
1448 IDocument document= fSourceViewer.getDocument(); | |
1449 | |
1450 if (bottom >= document.getNumberOfLines()) | |
1451 bottom= document.getNumberOfLines() - 1; | |
1452 | |
1453 return document.getLineOffset(bottom) + document.getLineLength(bottom); | |
1454 } catch (BadLocationException x) { | |
1455 } | |
1456 } | |
1457 | |
1458 return -1; | |
1459 } | |
1460 | |
1461 /* | |
1462 * @see dwt.events.PaintListener#paintControl(dwt.events.PaintEvent) | |
1463 */ | |
1464 public void paintControl(PaintEvent event) { | |
1465 if (fTextWidget !is null) | |
1466 handleDrawRequest(event); | |
1467 } | |
1468 | |
1469 /** | |
1470 * Handles the request to draw the annotations using the given graphical context. | |
1471 * | |
1472 * @param event the paint event or <code>null</code> | |
1473 */ | |
1474 private void handleDrawRequest(PaintEvent event) { | |
1475 | |
1476 if (fTextWidget is null) { | |
1477 // is already disposed | |
1478 return; | |
1479 } | |
1480 | |
1481 IRegion clippingRegion= computeClippingRegion(event, false); | |
1482 if (clippingRegion is null) | |
1483 return; | |
145 | 1484 |
129 | 1485 int vOffset= clippingRegion.getOffset(); |
1486 int vLength= clippingRegion.getLength(); | |
145 | 1487 |
129 | 1488 final GC gc= event !is null ? event.gc : null; |
1489 | |
1490 // Clone decorations | |
1491 Collection decorations; | |
1492 synchronized (fDecorationMapLock) { | |
1493 decorations= new ArrayList(fDecorationsMap.size()); | |
1494 decorations.addAll(fDecorationsMap.entrySet()); | |
1495 } | |
1496 | |
1497 /* | |
1498 * Create a new list of annotations to be drawn, since removing from decorations is more | |
1499 * expensive. One bucket per drawing layer. Use linked lists as addition is cheap here. | |
1500 */ | |
1501 ArrayList toBeDrawn= new ArrayList(10); | |
1502 for (Iterator e = decorations.iterator(); e.hasNext();) { | |
157
7f75eaa8103a
volatile and cast Map.Entry
Frank Benoit <benoit@tionex.de>
parents:
153
diff
changeset
|
1503 Map.Entry entry= cast(Map.Entry)e.next(); |
145 | 1504 |
134 | 1505 Annotation a= cast(Annotation)entry.getKey(); |
1506 Decoration pp = cast(Decoration)entry.getValue(); | |
129 | 1507 // prune any annotation that is not drawable or does not need drawing |
1508 if (!(a.isMarkedDeleted() || skip(a) || !pp.fPosition.overlapsWith(vOffset, vLength))) { | |
1509 // ensure sized appropriately | |
1510 for (int i= toBeDrawn.size(); i <= pp.fLayer; i++) | |
1511 toBeDrawn.add(new LinkedList()); | |
162 | 1512 (cast(List) toBeDrawn.get(pp.fLayer)).add(cast(Object)entry); |
129 | 1513 } |
1514 } | |
1515 IDocument document= fSourceViewer.getDocument(); | |
1516 for (Iterator it= toBeDrawn.iterator(); it.hasNext();) { | |
134 | 1517 List layer= cast(List) it.next(); |
129 | 1518 for (Iterator e = layer.iterator(); e.hasNext();) { |
157
7f75eaa8103a
volatile and cast Map.Entry
Frank Benoit <benoit@tionex.de>
parents:
153
diff
changeset
|
1519 Map.Entry entry= cast(Map.Entry)e.next(); |
134 | 1520 Annotation a= cast(Annotation)entry.getKey(); |
1521 Decoration pp = cast(Decoration)entry.getValue(); | |
129 | 1522 drawDecoration(pp, gc, a, clippingRegion, document); |
1523 } | |
1524 } | |
1525 } | |
145 | 1526 |
129 | 1527 private void drawDecoration(Decoration pp, GC gc, Annotation annotation, IRegion clippingRegion, IDocument document) { |
1528 if (clippingRegion is null) | |
1529 return; | |
1530 | |
145 | 1531 if (!(cast(IDrawingStrategy)pp.fPaintingStrategy )) |
129 | 1532 return; |
1533 | |
134 | 1534 IDrawingStrategy drawingStrategy= cast(IDrawingStrategy)pp.fPaintingStrategy; |
129 | 1535 |
1536 int clippingOffset= clippingRegion.getOffset(); | |
1537 int clippingLength= clippingRegion.getLength(); | |
1538 | |
1539 Position p= pp.fPosition; | |
1540 try { | |
1541 | |
1542 int startLine= document.getLineOfOffset(p.getOffset()); | |
1543 int lastInclusive= Math.max(p.getOffset(), p.getOffset() + p.getLength() - 1); | |
1544 int endLine= document.getLineOfOffset(lastInclusive); | |
1545 | |
1546 for (int i= startLine; i <= endLine; i++) { | |
1547 int lineOffset= document.getLineOffset(i); | |
1548 int paintStart= Math.max(lineOffset, p.getOffset()); | |
1549 String lineDelimiter= document.getLineDelimiter(i); | |
1550 int delimiterLength= lineDelimiter !is null ? lineDelimiter.length() : 0; | |
1551 int paintLength= Math.min(lineOffset + document.getLineLength(i) - delimiterLength, p.getOffset() + p.getLength()) - paintStart; | |
1552 if (paintLength >= 0 && overlapsWith(paintStart, paintLength, clippingOffset, clippingLength)) { | |
1553 // otherwise inside a line delimiter | |
1554 IRegion widgetRange= getWidgetRange(paintStart, paintLength); | |
1555 if (widgetRange !is null) { | |
1556 drawingStrategy.draw(annotation, gc, fTextWidget, widgetRange.getOffset(), widgetRange.getLength(), pp.fColor); | |
1557 } | |
1558 } | |
1559 } | |
1560 | |
1561 } catch (BadLocationException x) { | |
1562 } | |
1563 } | |
145 | 1564 |
129 | 1565 /** |
1566 * Computes the model (document) region that is covered by the paint event's clipping region. If | |
1567 * <code>event</code> is <code>null</code>, the model range covered by the visible editor | |
1568 * area (viewport) is returned. | |
145 | 1569 * |
129 | 1570 * @param event the paint event or <code>null</code> to use the entire viewport |
1571 * @param isClearing tells whether the clipping is need for clearing an annotation | |
1572 * @return the model region comprised by either the paint event's clipping region or the | |
1573 * viewport | |
1574 * @since 3.2 | |
1575 */ | |
1576 private IRegion computeClippingRegion(PaintEvent event, bool isClearing) { | |
1577 if (event is null) { | |
145 | 1578 |
129 | 1579 if (!isClearing && fCurrentDrawRange !is null) |
1580 return new Region(fCurrentDrawRange.offset, fCurrentDrawRange.length); | |
145 | 1581 |
129 | 1582 // trigger a repaint of the entire viewport |
1583 int vOffset= getInclusiveTopIndexStartOffset(); | |
1584 if (vOffset is -1) | |
1585 return null; | |
145 | 1586 |
129 | 1587 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17147 |
1588 int vLength= getExclusiveBottomIndexEndOffset() - vOffset; | |
145 | 1589 |
129 | 1590 return new Region(vOffset, vLength); |
1591 } | |
145 | 1592 |
129 | 1593 int widgetOffset; |
1594 try { | |
1595 int widgetClippingStartOffset= fTextWidget.getOffsetAtLocation(new Point(0, event.y)); | |
1596 int firstWidgetLine= fTextWidget.getLineAtOffset(widgetClippingStartOffset); | |
1597 widgetOffset= fTextWidget.getOffsetAtLine(firstWidgetLine); | |
1598 } catch (IllegalArgumentException ex1) { | |
1599 try { | |
1600 int firstVisibleLine= JFaceTextUtil.getPartialTopIndex(fTextWidget); | |
1601 widgetOffset= fTextWidget.getOffsetAtLine(firstVisibleLine); | |
1602 } catch (IllegalArgumentException ex2) { // above try code might fail too | |
1603 widgetOffset= 0; | |
1604 } | |
1605 } | |
145 | 1606 |
129 | 1607 int widgetEndOffset; |
1608 try { | |
1609 int widgetClippingEndOffset= fTextWidget.getOffsetAtLocation(new Point(0, event.y + event.height)); | |
1610 int lastWidgetLine= fTextWidget.getLineAtOffset(widgetClippingEndOffset); | |
1611 widgetEndOffset= fTextWidget.getOffsetAtLine(lastWidgetLine + 1); | |
1612 } catch (IllegalArgumentException ex1) { | |
1613 // happens if the editor is not "full", e.g. the last line of the document is visible in the editor | |
1614 try { | |
1615 int lastVisibleLine= JFaceTextUtil.getPartialBottomIndex(fTextWidget); | |
1616 if (lastVisibleLine is fTextWidget.getLineCount() - 1) | |
1617 // last line | |
1618 widgetEndOffset= fTextWidget.getCharCount(); | |
1619 else | |
1620 widgetEndOffset= fTextWidget.getOffsetAtLine(lastVisibleLine + 1) - 1; | |
1621 } catch (IllegalArgumentException ex2) { // above try code might fail too | |
1622 widgetEndOffset= fTextWidget.getCharCount(); | |
1623 } | |
1624 } | |
145 | 1625 |
129 | 1626 IRegion clippingRegion= getModelRange(widgetOffset, widgetEndOffset - widgetOffset); |
145 | 1627 |
129 | 1628 return clippingRegion; |
1629 } | |
1630 | |
1631 /** | |
1632 * Should the given annotation be skipped when handling draw requests? | |
1633 * | |
1634 * @param annotation the annotation | |
1635 * @return <code>true</code> iff the given annotation should be | |
1636 * skipped when handling draw requests | |
1637 * @since 3.0 | |
1638 */ | |
1639 protected bool skip(Annotation annotation) { | |
1640 return false; | |
1641 } | |
1642 | |
1643 /** | |
1644 * Returns the widget region that corresponds to the | |
1645 * given offset and length in the viewer's document. | |
145 | 1646 * |
129 | 1647 * @param modelOffset the model offset |
1648 * @param modelLength the model length | |
1649 * @return the corresponding widget region | |
1650 */ | |
1651 private IRegion getWidgetRange(int modelOffset, int modelLength) { | |
1652 fReusableRegion.setOffset(modelOffset); | |
1653 fReusableRegion.setLength(modelLength); | |
1654 | |
1655 if (fReusableRegion is null || fReusableRegion.getOffset() is Integer.MAX_VALUE) | |
1656 return null; | |
1657 | |
138 | 1658 if ( cast(ITextViewerExtension5)fSourceViewer ) { |
134 | 1659 ITextViewerExtension5 extension= cast(ITextViewerExtension5) fSourceViewer; |
129 | 1660 return extension.modelRange2WidgetRange(fReusableRegion); |
1661 } | |
1662 | |
1663 IRegion region= fSourceViewer.getVisibleRegion(); | |
1664 int offset= region.getOffset(); | |
1665 int length= region.getLength(); | |
1666 | |
1667 if (overlapsWith(fReusableRegion, region)) { | |
1668 int p1= Math.max(offset, fReusableRegion.getOffset()); | |
1669 int p2= Math.min(offset + length, fReusableRegion.getOffset() + fReusableRegion.getLength()); | |
1670 return new Region(p1 - offset, p2 - p1); | |
1671 } | |
1672 return null; | |
1673 } | |
1674 | |
1675 /** | |
1676 * Returns the model region that corresponds to the given region in the | |
1677 * viewer's text widget. | |
1678 * | |
1679 * @param offset the offset in the viewer's widget | |
1680 * @param length the length in the viewer's widget | |
1681 * @return the corresponding document region | |
1682 * @since 3.2 | |
1683 */ | |
1684 private IRegion getModelRange(int offset, int length) { | |
1685 if (offset is Integer.MAX_VALUE) | |
1686 return null; | |
1687 | |
138 | 1688 if ( cast(ITextViewerExtension5)fSourceViewer ) { |
134 | 1689 ITextViewerExtension5 extension= cast(ITextViewerExtension5) fSourceViewer; |
129 | 1690 return extension.widgetRange2ModelRange(new Region(offset, length)); |
1691 } | |
145 | 1692 |
129 | 1693 IRegion region= fSourceViewer.getVisibleRegion(); |
1694 return new Region(region.getOffset() + offset, length); | |
1695 } | |
145 | 1696 |
129 | 1697 /** |
1698 * Checks whether the intersection of the given text ranges | |
1699 * is empty or not. | |
1700 * | |
1701 * @param range1 the first range to check | |
1702 * @param range2 the second range to check | |
1703 * @return <code>true</code> if intersection is not empty | |
1704 */ | |
1705 private bool overlapsWith(IRegion range1, IRegion range2) { | |
1706 return overlapsWith(range1.getOffset(), range1.getLength(), range2.getOffset(), range2.getLength()); | |
1707 } | |
1708 | |
1709 /** | |
1710 * Checks whether the intersection of the given text ranges | |
1711 * is empty or not. | |
1712 * | |
1713 * @param offset1 offset of the first range | |
1714 * @param length1 length of the first range | |
1715 * @param offset2 offset of the second range | |
1716 * @param length2 length of the second range | |
1717 * @return <code>true</code> if intersection is not empty | |
1718 */ | |
1719 private bool overlapsWith(int offset1, int length1, int offset2, int length2) { | |
1720 int end= offset2 + length2; | |
1721 int thisEnd= offset1 + length1; | |
1722 | |
1723 if (length2 > 0) { | |
1724 if (length1 > 0) | |
1725 return offset1 < end && offset2 < thisEnd; | |
1726 return offset2 <= offset1 && offset1 < end; | |
1727 } | |
1728 | |
1729 if (length1 > 0) | |
1730 return offset1 <= offset2 && offset2 < thisEnd; | |
1731 return offset1 is offset2; | |
1732 } | |
1733 | |
1734 /* | |
1735 * @see dwtx.jface.text.IPainter#deactivate(bool) | |
1736 */ | |
1737 public void deactivate(bool redraw) { | |
1738 if (fIsActive) { | |
1739 fIsActive= false; | |
1740 disablePainting(redraw); | |
1741 setModel(null); | |
1742 catchupWithModel(null); | |
1743 } | |
1744 } | |
1745 | |
1746 /** | |
1747 * Returns whether the given reason causes a repaint. | |
1748 * | |
1749 * @param reason the reason | |
1750 * @return <code>true</code> if repaint reason, <code>false</code> otherwise | |
1751 * @since 3.0 | |
1752 */ | |
1753 protected bool isRepaintReason(int reason) { | |
1754 return CONFIGURATION is reason || INTERNAL is reason; | |
1755 } | |
1756 | |
1757 /** | |
1758 * Retrieves the annotation model from the given source viewer. | |
1759 * | |
1760 * @param sourceViewer the source viewer | |
1761 * @return the source viewer's annotation model or <code>null</code> if none can be found | |
1762 * @since 3.0 | |
1763 */ | |
1764 protected IAnnotationModel findAnnotationModel(ISourceViewer sourceViewer) { | |
1765 if(sourceViewer !is null) | |
1766 return sourceViewer.getAnnotationModel(); | |
1767 return null; | |
1768 } | |
1769 | |
1770 /* | |
1771 * @see dwtx.jface.text.IPainter#paint(int) | |
1772 */ | |
1773 public void paint(int reason) { | |
1774 if (fSourceViewer.getDocument() is null) { | |
1775 deactivate(false); | |
1776 return; | |
1777 } | |
1778 | |
1779 if (!fIsActive) { | |
1780 IAnnotationModel model= findAnnotationModel(fSourceViewer); | |
1781 if (model !is null) { | |
1782 fIsActive= true; | |
1783 setModel(model); | |
1784 } | |
1785 } else if (isRepaintReason(reason)) | |
1786 updatePainting(null); | |
1787 } | |
1788 | |
1789 /* | |
1790 * @see dwtx.jface.text.IPainter#setPositionManager(dwtx.jface.text.IPaintPositionManager) | |
1791 */ | |
1792 public void setPositionManager(IPaintPositionManager manager) { | |
1793 } | |
1794 } |