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