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