diff dwtx/jface/text/source/AnnotationPainter.d @ 129:eb30df5ca28b

Added JFace Text sources
author Frank Benoit <benoit@tionex.de>
date Sat, 23 Aug 2008 19:10:48 +0200
parents
children c4fb132a086c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/text/source/AnnotationPainter.d	Sat Aug 23 19:10:48 2008 +0200
@@ -0,0 +1,1664 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.text.source.AnnotationPainter;
+
+import dwt.dwthelper.utils;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.custom.StyleRange;
+import dwt.custom.StyledText;
+import dwt.events.PaintEvent;
+import dwt.events.PaintListener;
+import dwt.graphics.Color;
+import dwt.graphics.GC;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.graphics.TextStyle;
+import dwt.widgets.Display;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.Platform;
+import dwtx.jface.text.BadLocationException;
+import dwtx.jface.text.IDocument;
+import dwtx.jface.text.IPaintPositionManager;
+import dwtx.jface.text.IPainter;
+import dwtx.jface.text.IRegion;
+import dwtx.jface.text.ITextInputListener;
+import dwtx.jface.text.ITextPresentationListener;
+import dwtx.jface.text.ITextViewerExtension2;
+import dwtx.jface.text.ITextViewerExtension5;
+import dwtx.jface.text.JFaceTextUtil;
+import dwtx.jface.text.Position;
+import dwtx.jface.text.Region;
+import dwtx.jface.text.TextPresentation;
+
+
+/**
+ * Paints decorations for annotations provided by an annotation model and/or
+ * highlights them in the associated source viewer.
+ * <p>
+ * The annotation painter can be configured with drawing strategies. A drawing
+ * strategy defines the visual presentation of a particular type of annotation
+ * decoration.</p>
+ * <p>
+ * Clients usually instantiate and configure objects of this class.</p>
+ *
+ * @since 2.1
+ */
+public class AnnotationPainter : IPainter, PaintListener, IAnnotationModelListener, IAnnotationModelListenerExtension, ITextPresentationListener {
+
+
+    /**
+     * A drawing strategy draws the decoration for an annotation onto the text widget.
+     *
+     * @since 3.0
+     */
+    public interface IDrawingStrategy {
+        /**
+         * Draws a decoration for an annotation onto the specified GC at the given text range. There
+         * are two different invocation modes of the <code>draw</code> method:
+         * <ul>
+         * <li><strong>drawing mode:</strong> the passed GC is the graphics context of a paint
+         * event occurring on the text widget. The strategy should draw the decoration onto the
+         * graphics context, such that the decoration appears at the given range in the text
+         * widget.</li>
+         * <li><strong>clearing mode:</strong> the passed GC is <code>null</code>. In this case
+         * the strategy must invalidate enough of the text widget's client area to cover any
+         * decoration drawn in drawing mode. This can usually be accomplished by calling
+         * {@linkplain StyledText#redrawRange(int, int, bool) textWidget.redrawRange(offset, length, true)}.</li>
+         * </ul>
+         * 
+         * @param annotation the annotation to be drawn
+         * @param gc the graphics context, <code>null</code> when in clearing mode
+         * @param textWidget the text widget to draw on
+         * @param offset the offset of the line
+         * @param length the length of the line
+         * @param color the color of the line
+         */
+        void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color);
+    }
+
+    /**
+     * Squiggles drawing strategy.
+     * 
+     * @since 3.0
+     * @deprecated As of 3.4, replaced by {@link AnnotationPainter.UnderlineStrategy}
+     */
+    public static class SquigglesStrategy : IDrawingStrategy {
+
+        /*
+         * @see dwtx.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(dwtx.jface.text.source.Annotation, dwt.graphics.GC, dwt.custom.StyledText, int, int, dwt.graphics.Color)
+         * @since 3.0
+         */
+        public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {
+            if (gc !is null) {
+
+                if (length < 1)
+                    return;
+
+                Point left= textWidget.getLocationAtOffset(offset);
+                Point right= textWidget.getLocationAtOffset(offset + length);
+                Rectangle rect= textWidget.getTextBounds(offset, offset + length - 1);
+                left.x= rect.x;
+                right.x= rect.x + rect.width;
+
+                int[] polyline= computePolyline(left, right, textWidget.getBaseline(offset), textWidget.getLineHeight(offset));
+                
+                gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
+                gc.setLineStyle(DWT.LINE_SOLID);
+                gc.setForeground(color);
+                gc.drawPolyline(polyline);
+
+            } else {
+                textWidget.redrawRange(offset, length, true);
+            }
+        }
+
+        /**
+         * Computes an array of alternating x and y values which are the corners of the squiggly line of the
+         * given height between the given end points.
+         *
+         * @param left the left end point
+         * @param right the right end point
+         * @param baseline the font's baseline
+         * @param lineHeight the height of the line
+         * @return the array of alternating x and y values which are the corners of the squiggly line
+         */
+        private int[] computePolyline(Point left, Point right, int baseline, int lineHeight) {
+
+            final int WIDTH= 4; // must be even
+            final int HEIGHT= 2; // can be any number
+
+            int peaks= (right.x - left.x) / WIDTH;
+            if (peaks is 0 && right.x - left.x > 2)
+                peaks= 1;
+
+            int leftX= left.x;
+
+            // compute (number of point) * 2
+            int length= ((2 * peaks) + 1) * 2;
+            if (length < 0)
+                return new int[0];
+
+            int[] coordinates= new int[length];
+
+            // cache peeks' y-coordinates
+            int top= left.y + Math.min(baseline + 1, lineHeight - HEIGHT - 1);
+            int bottom= top + HEIGHT;
+
+            // populate array with peek coordinates
+            for (int i= 0; i < peaks; i++) {
+                int index= 4 * i;
+                coordinates[index]= leftX + (WIDTH * i);
+                coordinates[index+1]= bottom;
+                coordinates[index+2]= coordinates[index] + WIDTH/2;
+                coordinates[index+3]= top;
+            }
+
+            // the last down flank is missing
+            coordinates[length-2]= Math.min(Math.max(0, right.x - 1), left.x + (WIDTH * peaks));
+            coordinates[length-1]= bottom;
+
+            return coordinates;
+        }
+    }
+
+    /**
+     * Drawing strategy that does nothing.
+     *
+     * @since 3.0
+     */
+    public static final class NullStrategy : IDrawingStrategy {
+
+        /*
+         * @see dwtx.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(dwtx.jface.text.source.Annotation, dwt.graphics.GC, dwt.custom.StyledText, int, int, dwt.graphics.Color)
+         * @since 3.0
+         */
+        public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {
+            // do nothing
+        }
+    }
+    
+    
+    /**
+     * A text style painting strategy draws the decoration for an annotation
+     * onto the text widget by applying a {@link TextStyle} on a given
+     * {@link StyleRange}.
+     * 
+     * @since 3.4
+     */
+    public interface ITextStyleStrategy {
+
+        /**
+         * Applies a text style on the given <code>StyleRange</code>.
+         * 
+         * @param styleRange the style range on which to apply the text style
+         * @param annotationColor the color of the annotation
+         */
+        void applyTextStyle(StyleRange styleRange, Color annotationColor);
+    }
+    
+
+    /**
+     * @since 3.4
+     */
+    public static final class HighlightingStrategy : ITextStyleStrategy {
+        public void applyTextStyle(StyleRange styleRange, Color annotationColor) {
+            styleRange.background= annotationColor;
+        }
+    }
+
+    
+    /**
+     * Underline text style strategy.
+     * 
+     * @since 3.4
+     */
+    public static final class UnderlineStrategy : ITextStyleStrategy {
+        
+        int fUnderlineStyle;
+
+        public UnderlineStrategy(int style) {
+            Assert.isLegal(style is DWT.UNDERLINE_SINGLE || style is DWT.UNDERLINE_DOUBLE || style is DWT.UNDERLINE_ERROR || style is DWT.UNDERLINE_SQUIGGLE);
+            fUnderlineStyle= style;
+        }
+
+        public void applyTextStyle(StyleRange styleRange, Color annotationColor) {
+            styleRange.underline= true;
+            styleRange.underlineStyle= fUnderlineStyle;
+            styleRange.underlineColor= annotationColor;
+        }
+    }
+
+    
+    /**
+     * Box text style strategy.
+     * 
+     * @since 3.4
+     */
+    public static final class BoxStrategy : ITextStyleStrategy {
+        
+        int fBorderStyle;
+
+        public BoxStrategy(int style) {
+            Assert.isLegal(style is DWT.BORDER_DASH || style is DWT.BORDER_DASH || style is DWT.BORDER_SOLID);
+            fBorderStyle= style;
+        }
+        
+        public void applyTextStyle(StyleRange styleRange, Color annotationColor) {
+            styleRange.borderStyle= fBorderStyle;
+            styleRange.borderColor= annotationColor;
+        }
+    }
+    
+
+    /**
+     * Implementation of <code>IRegion</code> that can be reused
+     * by setting the offset and the length.
+     */
+    private static class ReusableRegion : Position , IRegion {}
+
+    /**
+     * Tells whether this class is in debug mode.
+     * @since 3.0
+     */
+    private static bool DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("dwtx.jface.text/debug/AnnotationPainter"));  //$NON-NLS-1$//$NON-NLS-2$
+    /**
+     * The squiggly painter strategy.
+     * @since 3.0
+     */
+    private static final IDrawingStrategy SQUIGGLES_STRATEGY= new SquigglesStrategy();
+
+    /**
+     * This strategy is used to mark the <code>null</code> value in the chache
+     * maps.
+     * 
+     * @since 3.4
+     */
+    private static final IDrawingStrategy NULL_STRATEGY= new NullStrategy();
+    /**
+     * The squiggles painter id.
+     * @since 3.0
+     */
+    private static final Object SQUIGGLES= new Object();
+    /**
+     * The squiggly painter strategy.
+     * 
+     * @since 3.4
+     */
+    private static final ITextStyleStrategy HIGHLIGHTING_STRATEGY= new HighlightingStrategy();
+
+    /**
+     * The highlighting text style strategy id.
+     * 
+     * @since 3.4
+     */
+    private static final Object HIGHLIGHTING= new Object();
+
+    /**
+     * The presentation information (decoration) for an annotation.  Each such
+     * object represents one decoration drawn on the text area, such as squiggly lines
+     * and underlines.
+     */
+    private static class Decoration {
+        /** The position of this decoration */
+        private Position fPosition;
+        /** The color of this decoration */
+        private Color fColor;
+        /**
+         * The annotation's layer
+         * @since 3.0
+         */
+        private int fLayer;
+        /**
+         * The painting strategy for this decoration.
+         * @since 3.0
+         */
+        private Object fPaintingStrategy;
+    }
+
+
+    /** Indicates whether this painter is active */
+    private bool fIsActive= false;
+    /** Indicates whether this painter is managing decorations */
+    private bool fIsPainting= false;
+    /** Indicates whether this painter is setting its annotation model */
+    private volatile bool  fIsSettingModel= false;
+    /** The associated source viewer */
+    private ISourceViewer fSourceViewer;
+    /** The cached widget of the source viewer */
+    private StyledText fTextWidget;
+    /** The annotation model providing the annotations to be drawn */
+    private IAnnotationModel fModel;
+    /** The annotation access */
+    private IAnnotationAccess fAnnotationAccess;
+    /**
+     * The map with decorations
+     * @since 3.0
+     */
+    private Map fDecorationsMap= new HashMap(); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=50767
+    /**
+     * The map with of highlighted decorations.
+     * @since 3.0
+     */
+    private Map fHighlightedDecorationsMap= new HashMap(); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=50767
+    /**
+     * Mutex for highlighted decorations map.
+     * @since 3.0
+     */
+    private Object fDecorationMapLock= new Object();
+    /**
+     * Mutex for for decorations map.
+     * @since 3.0
+     */
+    private Object fHighlightedDecorationsMapLock= new Object();
+    /**
+     * Maps an annotation type to its registered color.
+     * 
+     * @see #setAnnotationTypeColor(Object, Color)
+     */
+    private Map fAnnotationType2Color= new HashMap();
+
+    /**
+     * Cache that maps the annotation type to its color.
+     * @since 3.4
+     */
+    private Map fCachedAnnotationType2Color= new HashMap();
+    /**
+     * The range in which the current highlight annotations can be found.
+     * @since 3.0
+     */
+    private Position fCurrentHighlightAnnotationRange= null;
+    /**
+     * The range in which all added, removed and changed highlight
+     * annotations can be found since the last world change.
+     * @since 3.0
+     */
+    private Position fTotalHighlightAnnotationRange= null;
+    /**
+     * The range in which the currently drawn annotations can be found.
+     * @since 3.3
+     */
+    private Position fCurrentDrawRange= null;
+    /**
+     * The range in which all added, removed and changed drawn
+     * annotations can be found since the last world change.
+     * @since 3.3
+     */
+    private Position fTotalDrawRange= null;
+    /**
+     * The text input listener.
+     * @since 3.0
+     */
+    private ITextInputListener fTextInputListener;
+    /**
+     * Flag which tells that a new document input is currently being set.
+     * @since 3.0
+     */
+    private bool fInputDocumentAboutToBeChanged;
+    /**
+     * Maps annotation types to painting strategy identifiers.
+     * 
+     * @see #addAnnotationType(Object, Object)
+     * @since 3.0
+     */
+    private Map fAnnotationType2PaintingStrategyId= new HashMap();
+    /**
+     * Maps annotation types to painting strategy identifiers.
+     * @since 3.4
+     */
+    private Map fCachedAnnotationType2PaintingStrategy= new HashMap();
+
+    /**
+     * Maps painting strategy identifiers to painting strategies.
+     * 
+     * @since 3.0
+     */
+    private Map fPaintingStrategyId2PaintingStrategy= new HashMap();
+
+    /**
+     * Reuse this region for performance reasons.
+     * @since 3.3
+     */
+    private ReusableRegion fReusableRegion= new ReusableRegion();
+    
+    /**
+     * Creates a new annotation painter for the given source viewer and with the
+     * given annotation access. The painter is not initialized, i.e. no
+     * annotation types are configured to be painted.
+     *
+     * @param sourceViewer the source viewer for this painter
+     * @param access the annotation access for this painter
+     */
+    public AnnotationPainter(ISourceViewer sourceViewer, IAnnotationAccess access) {
+        fSourceViewer= sourceViewer;
+        fAnnotationAccess= access;
+        fTextWidget= sourceViewer.getTextWidget();
+
+        // default drawing strategies: squiggles were the only decoration style before version 3.0
+        fPaintingStrategyId2PaintingStrategy.put(SQUIGGLES, SQUIGGLES_STRATEGY);
+        fPaintingStrategyId2PaintingStrategy.put(HIGHLIGHTING, HIGHLIGHTING_STRATEGY);
+    }
+
+    /**
+     * Returns whether this painter has to draw any squiggles.
+     *
+     * @return <code>true</code> if there are squiggles to be drawn, <code>false</code> otherwise
+     */
+    private bool hasDecorations() {
+        synchronized (fDecorationMapLock) {
+            return !fDecorationsMap.isEmpty();
+        }
+    }
+
+    /**
+     * Enables painting. This painter registers a paint listener with the
+     * source viewer's widget.
+     */
+    private void enablePainting() {
+        if (!fIsPainting && hasDecorations()) {
+            fIsPainting= true;
+            fTextWidget.addPaintListener(this);
+            handleDrawRequest(null);
+        }
+    }
+
+    /**
+     * Disables painting, if is has previously been enabled. Removes
+     * any paint listeners registered with the source viewer's widget.
+     *
+     * @param redraw <code>true</code> if the widget should be redrawn after disabling
+     */
+    private void disablePainting(bool redraw) {
+        if (fIsPainting) {
+            fIsPainting= false;
+            fTextWidget.removePaintListener(this);
+            if (redraw && hasDecorations())
+                handleDrawRequest(null);
+        }
+    }
+
+    /**
+     * Sets the annotation model for this painter. Registers this painter
+     * as listener of the give model, if the model is not <code>null</code>.
+     *
+     * @param model the annotation model
+     */
+    private void setModel(IAnnotationModel model) {
+        if (fModel !is model) {
+            if (fModel !is null)
+                fModel.removeAnnotationModelListener(this);
+            fModel= model;
+            if (fModel !is null) {
+                try {
+                    fIsSettingModel= true;
+                    fModel.addAnnotationModelListener(this);
+                } finally {
+                    fIsSettingModel= false;
+                }
+            }
+        }
+    }
+
+    /**
+     * Updates the set of decorations based on the current state of
+     * the painter's annotation model.
+     *
+     * @param event the annotation model event
+     */
+    private void catchupWithModel(AnnotationModelEvent event) {
+
+        synchronized (fDecorationMapLock) {
+            if (fDecorationsMap is null)
+                return;
+        }
+
+        IRegion clippingRegion= computeClippingRegion(null, true);
+        IDocument document= fSourceViewer.getDocument();
+
+        int highlightAnnotationRangeStart= Integer.MAX_VALUE;
+        int highlightAnnotationRangeEnd= -1;
+        
+        int drawRangeStart= Integer.MAX_VALUE;
+        int drawRangeEnd= -1;
+
+        if (fModel !is null) {
+
+            Map decorationsMap;
+            Map highlightedDecorationsMap;
+
+            // Clone decoration maps
+            synchronized (fDecorationMapLock) {
+                decorationsMap= new HashMap(fDecorationsMap);
+            }
+            synchronized (fHighlightedDecorationsMapLock) {
+                highlightedDecorationsMap= new HashMap(fHighlightedDecorationsMap);
+            }
+
+            bool isWorldChange= false;
+
+            Iterator e;
+            if (event is null || event.isWorldChange()) {
+                isWorldChange= true;
+
+                if (DEBUG && event is null)
+                    System.out.println("AP: INTERNAL CHANGE"); //$NON-NLS-1$
+
+                Iterator iter= decorationsMap.entrySet().iterator();
+                while (iter.hasNext()) {
+                    Map.Entry entry= (Map.Entry)iter.next();
+                    Annotation annotation= (Annotation)entry.getKey();
+                    Decoration decoration= (Decoration)entry.getValue();
+                    drawDecoration(decoration, null, annotation, clippingRegion, document);
+                }
+                
+                decorationsMap.clear();
+                
+                highlightedDecorationsMap.clear();
+
+                e= fModel.getAnnotationIterator();
+
+
+            } else {
+
+                // Remove annotations
+                Annotation[] removedAnnotations= event.getRemovedAnnotations();
+                for (int i=0, length= removedAnnotations.length; i < length; i++) {
+                    Annotation annotation= removedAnnotations[i];
+                    Decoration decoration= (Decoration)highlightedDecorationsMap.remove(annotation);
+                    if (decoration !is null) {
+                        Position position= decoration.fPosition;
+                        if (position !is null) {
+                            highlightAnnotationRangeStart= Math.min(highlightAnnotationRangeStart, position.offset);
+                            highlightAnnotationRangeEnd= Math.max(highlightAnnotationRangeEnd, position.offset + position.length);
+                        }
+                    }
+                    decoration= (Decoration)decorationsMap.remove(annotation);
+                    if (decoration !is null) {
+                        drawDecoration(decoration, null, annotation, clippingRegion, document);
+                        Position position= decoration.fPosition;
+                        if (position !is null) {
+                            drawRangeStart= Math.min(drawRangeStart, position.offset);
+                            drawRangeEnd= Math.max(drawRangeEnd, position.offset + position.length);
+                        }
+                    }
+                    
+                }
+
+                // Update existing annotations
+                Annotation[] changedAnnotations= event.getChangedAnnotations();
+                for (int i=0, length= changedAnnotations.length; i < length; i++) {
+                    Annotation annotation= changedAnnotations[i];
+
+                    bool isHighlighting= false;
+                    
+                    Decoration decoration= (Decoration)highlightedDecorationsMap.get(annotation);
+
+                    if (decoration !is null) {
+                        isHighlighting= true;
+                        // The call below updates the decoration - no need to create new decoration
+                        decoration= getDecoration(annotation, decoration);
+                        if (decoration is null)
+                            highlightedDecorationsMap.remove(annotation);
+                    } else {
+                        decoration= getDecoration(annotation, decoration);
+                        if (decoration !is null && decoration.fPaintingStrategy instanceof ITextStyleStrategy) {
+                            highlightedDecorationsMap.put(annotation, decoration);
+                            isHighlighting= true;
+                        }
+                    }
+
+                    bool usesDrawingStrategy= !isHighlighting && decoration !is null;
+
+                    Position position= null;
+                    if (decoration is null)
+                        position= fModel.getPosition(annotation);
+                    else
+                        position= decoration.fPosition;
+
+                    if (position !is null && !position.isDeleted()) {
+                        if (isHighlighting) {
+                            highlightAnnotationRangeStart= Math.min(highlightAnnotationRangeStart, position.offset);
+                            highlightAnnotationRangeEnd= Math.max(highlightAnnotationRangeEnd, position.offset + position.length);
+                        }
+                        if (usesDrawingStrategy) {
+                            drawRangeStart= Math.min(drawRangeStart, position.offset);
+                            drawRangeEnd= Math.max(drawRangeEnd, position.offset + position.length);
+                        }
+                    } else {
+                        highlightedDecorationsMap.remove(annotation);
+                    }
+
+                    if (usesDrawingStrategy) {
+                        Decoration oldDecoration= (Decoration)decorationsMap.get(annotation);
+                        if (oldDecoration !is null) {
+                            drawDecoration(oldDecoration, null, annotation, clippingRegion, document);
+                        
+                        if (decoration !is null)
+                            decorationsMap.put(annotation, decoration);
+                        else if (oldDecoration !is null)
+                            decorationsMap.remove(annotation);
+                        }
+                    }
+                }
+
+                e= Arrays.asList(event.getAddedAnnotations()).iterator();
+            }
+
+            // Add new annotations
+            while (e.hasNext()) {
+                Annotation annotation= (Annotation) e.next();
+                Decoration pp= getDecoration(annotation, null);
+                if (pp !is null) {
+                    if (pp.fPaintingStrategy instanceof IDrawingStrategy) {
+                        decorationsMap.put(annotation, pp);
+                        drawRangeStart= Math.min(drawRangeStart, pp.fPosition.offset);
+                        drawRangeEnd= Math.max(drawRangeEnd, pp.fPosition.offset + pp.fPosition.length);
+                    } else if (pp.fPaintingStrategy instanceof ITextStyleStrategy) {
+                        highlightedDecorationsMap.put(annotation, pp);
+                        highlightAnnotationRangeStart= Math.min(highlightAnnotationRangeStart, pp.fPosition.offset);
+                        highlightAnnotationRangeEnd= Math.max(highlightAnnotationRangeEnd, pp.fPosition.offset + pp.fPosition.length);
+                    }
+                    
+                }
+            }
+
+            synchronized (fDecorationMapLock) {
+                fDecorationsMap= decorationsMap;
+                updateDrawRanges(drawRangeStart, drawRangeEnd, isWorldChange);
+            }
+
+            synchronized (fHighlightedDecorationsMapLock) {
+                fHighlightedDecorationsMap= highlightedDecorationsMap;
+                updateHighlightRanges(highlightAnnotationRangeStart, highlightAnnotationRangeEnd, isWorldChange);
+            }
+        } else {
+            // annotation model is null -> clear all
+            synchronized (fDecorationMapLock) {
+                fDecorationsMap.clear();
+            }
+            synchronized (fHighlightedDecorationsMapLock) {
+                fHighlightedDecorationsMap.clear();
+            }
+        }
+    }
+
+    /**
+     * Updates the remembered highlight ranges.
+     *
+     * @param highlightAnnotationRangeStart the start of the range
+     * @param highlightAnnotationRangeEnd   the end of the range
+     * @param isWorldChange                 tells whether the range belongs to a annotation model event reporting a world change
+     * @since 3.0
+     */
+    private void updateHighlightRanges(int highlightAnnotationRangeStart, int highlightAnnotationRangeEnd, bool isWorldChange) {
+        if (highlightAnnotationRangeStart !is Integer.MAX_VALUE) {
+
+            int maxRangeStart= highlightAnnotationRangeStart;
+            int maxRangeEnd= highlightAnnotationRangeEnd;
+
+            if (fTotalHighlightAnnotationRange !is null) {
+                maxRangeStart= Math.min(maxRangeStart, fTotalHighlightAnnotationRange.offset);
+                maxRangeEnd= Math.max(maxRangeEnd, fTotalHighlightAnnotationRange.offset + fTotalHighlightAnnotationRange.length);
+            }
+
+            if (fTotalHighlightAnnotationRange is null)
+                fTotalHighlightAnnotationRange= new Position(0);
+            if (fCurrentHighlightAnnotationRange is null)
+                fCurrentHighlightAnnotationRange= new Position(0);
+
+            if (isWorldChange) {
+                fTotalHighlightAnnotationRange.offset= highlightAnnotationRangeStart;
+                fTotalHighlightAnnotationRange.length= highlightAnnotationRangeEnd - highlightAnnotationRangeStart;
+                fCurrentHighlightAnnotationRange.offset= maxRangeStart;
+                fCurrentHighlightAnnotationRange.length= maxRangeEnd - maxRangeStart;
+            } else {
+                fTotalHighlightAnnotationRange.offset= maxRangeStart;
+                fTotalHighlightAnnotationRange.length= maxRangeEnd - maxRangeStart;
+                fCurrentHighlightAnnotationRange.offset=highlightAnnotationRangeStart;
+                fCurrentHighlightAnnotationRange.length= highlightAnnotationRangeEnd - highlightAnnotationRangeStart;
+            }
+        } else {
+            if (isWorldChange) {
+                fCurrentHighlightAnnotationRange= fTotalHighlightAnnotationRange;
+                fTotalHighlightAnnotationRange= null;
+            } else {
+                fCurrentHighlightAnnotationRange= null;
+            }
+        }
+
+        adaptToDocumentLength(fCurrentHighlightAnnotationRange);
+        adaptToDocumentLength(fTotalHighlightAnnotationRange);
+    }
+    
+    /**
+     * Updates the remembered decoration ranges.
+     *
+     * @param drawRangeStart    the start of the range
+     * @param drawRangeEnd      the end of the range
+     * @param isWorldChange     tells whether the range belongs to a annotation model event reporting a world change
+     * @since 3.3
+     */
+    private void updateDrawRanges(int drawRangeStart, int drawRangeEnd, bool isWorldChange) {
+        if (drawRangeStart !is Integer.MAX_VALUE) {
+            
+            int maxRangeStart= drawRangeStart;
+            int maxRangeEnd= drawRangeEnd;
+            
+            if (fTotalDrawRange !is null) {
+                maxRangeStart= Math.min(maxRangeStart, fTotalDrawRange.offset);
+                maxRangeEnd= Math.max(maxRangeEnd, fTotalDrawRange.offset + fTotalDrawRange.length);
+            }
+            
+            if (fTotalDrawRange is null)
+                fTotalDrawRange= new Position(0);
+            if (fCurrentDrawRange is null)
+                fCurrentDrawRange= new Position(0);
+            
+            if (isWorldChange) {
+                fTotalDrawRange.offset= drawRangeStart;
+                fTotalDrawRange.length= drawRangeEnd - drawRangeStart;
+                fCurrentDrawRange.offset= maxRangeStart;
+                fCurrentDrawRange.length= maxRangeEnd - maxRangeStart;
+            } else {
+                fTotalDrawRange.offset= maxRangeStart;
+                fTotalDrawRange.length= maxRangeEnd - maxRangeStart;
+                fCurrentDrawRange.offset=drawRangeStart;
+                fCurrentDrawRange.length= drawRangeEnd - drawRangeStart;
+            }
+        } else {
+            if (isWorldChange) {
+                fCurrentDrawRange= fTotalDrawRange;
+                fTotalDrawRange= null;
+            } else {
+                fCurrentDrawRange= null;
+            }
+        }
+        
+        adaptToDocumentLength(fCurrentDrawRange);
+        adaptToDocumentLength(fTotalDrawRange);
+    }
+
+    /**
+     * Adapts the given position to the document length.
+     *
+     * @param position the position to adapt
+     * @since 3.0
+     */
+    private void adaptToDocumentLength(Position position) {
+        if (position is null)
+            return;
+
+        int length= fSourceViewer.getDocument().getLength();
+        position.offset= Math.min(position.offset, length);
+        position.length= Math.min(position.length, length - position.offset);
+    }
+
+    /**
+     * Returns a decoration for the given annotation if this
+     * annotation is valid and shown by this painter.
+     *
+     * @param annotation            the annotation
+     * @param decoration            the decoration to be adapted and returned or <code>null</code> if a new one must be created
+     * @return the decoration or <code>null</code> if there's no valid one
+     * @since 3.0
+     */
+    private Decoration getDecoration(Annotation annotation, Decoration decoration) {
+
+        if (annotation.isMarkedDeleted())
+            return null;
+
+        String type= annotation.getType();
+
+        Object paintingStrategy= getPaintingStrategy(type);
+        if (paintingStrategy is null || paintingStrategy instanceof NullStrategy)
+            return null;
+
+        Color color= getColor(type);
+        if (color is null)
+            return null;
+
+        Position position= fModel.getPosition(annotation);
+        if (position is null || position.isDeleted())
+            return null;
+        
+        if (decoration is null)
+            decoration= new Decoration();
+
+        decoration.fPosition= position;
+        decoration.fColor= color;
+        if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
+            IAnnotationAccessExtension extension= (IAnnotationAccessExtension) fAnnotationAccess;
+            decoration.fLayer= extension.getLayer(annotation);
+        } else {
+            decoration.fLayer= IAnnotationAccessExtension.DEFAULT_LAYER;
+        }
+        
+        decoration.fPaintingStrategy= paintingStrategy;
+        
+        return decoration;
+    }
+
+    /**
+     * Returns the painting strategy for the given annotation.
+     * 
+     * @param type the annotation type
+     * @return the annotation painter
+     * @since 3.0
+     */
+    private Object getPaintingStrategy(final String type) {
+        Object strategy= fCachedAnnotationType2PaintingStrategy.get(type);
+        if (strategy !is null)
+            return strategy;
+
+        strategy= fPaintingStrategyId2PaintingStrategy.get(fAnnotationType2PaintingStrategyId.get(type));
+        if (strategy !is null) {
+            fCachedAnnotationType2PaintingStrategy.put(type, strategy);
+            return strategy;
+        }
+
+        if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
+            IAnnotationAccessExtension ext = (IAnnotationAccessExtension) fAnnotationAccess;
+            Object[] sts = ext.getSupertypes(type);
+            for (int i= 0; i < sts.length; i++) {
+                strategy= fPaintingStrategyId2PaintingStrategy.get(fAnnotationType2PaintingStrategyId.get(sts[i]));
+                if (strategy !is null) {
+                    fCachedAnnotationType2PaintingStrategy.put(type, strategy);
+                    return strategy;
+                }
+            }
+        }
+
+        fCachedAnnotationType2PaintingStrategy.put(type, NULL_STRATEGY);
+        return null;
+
+    }
+
+    /**
+     * Returns the color for the given annotation type
+     *
+     * @param annotationType the annotation type
+     * @return the color
+     * @since 3.0
+     */
+    private Color getColor(final Object annotationType) {
+        Color color= (Color)fCachedAnnotationType2Color.get(annotationType);
+        if (color !is null)
+            return color;
+
+        color= (Color)fAnnotationType2Color.get(annotationType);
+        if (color !is null) {
+            fCachedAnnotationType2Color.put(annotationType, color);
+            return color;
+        }
+
+        if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
+            IAnnotationAccessExtension extension= (IAnnotationAccessExtension) fAnnotationAccess;
+            Object[] superTypes= extension.getSupertypes(annotationType);
+            if (superTypes !is null) {
+                for (int i= 0; i < superTypes.length; i++) {
+                    color= (Color)fAnnotationType2Color.get(superTypes[i]);
+                    if (color !is null) {
+                        fCachedAnnotationType2Color.put(annotationType, color);
+                        return color;
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Recomputes the squiggles to be drawn and redraws them.
+     *
+     * @param event the annotation model event
+     * @since 3.0
+     */
+    private void updatePainting(AnnotationModelEvent event) {
+        disablePainting(event is null);
+
+        catchupWithModel(event);
+
+        if (!fInputDocumentAboutToBeChanged)
+            invalidateTextPresentation();
+
+        enablePainting();
+    }
+
+    private void invalidateTextPresentation() {
+        IRegion r= null;
+        synchronized (fHighlightedDecorationsMapLock) {
+            if (fCurrentHighlightAnnotationRange !is null)
+                r= new Region(fCurrentHighlightAnnotationRange.getOffset(), fCurrentHighlightAnnotationRange.getLength());
+        }
+        if (r is null)
+            return;
+
+        if (fSourceViewer instanceof ITextViewerExtension2) {
+            if (DEBUG)
+                System.out.println("AP: invalidating offset: " + r.getOffset() + ", length= " + r.getLength()); //$NON-NLS-1$ //$NON-NLS-2$
+
+            ((ITextViewerExtension2)fSourceViewer).invalidateTextPresentation(r.getOffset(), r.getLength());
+
+        } else {
+            fSourceViewer.invalidateTextPresentation();
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.ITextPresentationListener#applyTextPresentation(dwtx.jface.text.TextPresentation)
+     * @since 3.0
+     */
+    public void applyTextPresentation(TextPresentation tp) {
+        Set decorations;
+
+        synchronized (fHighlightedDecorationsMapLock) {
+            if (fHighlightedDecorationsMap is null || fHighlightedDecorationsMap.isEmpty())
+                return;
+
+            decorations= new HashSet(fHighlightedDecorationsMap.entrySet());
+        }
+
+        IRegion region= tp.getExtent();
+
+        if (DEBUG)
+            System.out.println("AP: applying text presentation offset: " + region.getOffset() + ", length= " + region.getLength()); //$NON-NLS-1$ //$NON-NLS-2$
+
+        for (int layer= 0, maxLayer= 1; layer < maxLayer; layer++) {
+
+            for (Iterator iter= decorations.iterator(); iter.hasNext();) {
+                Map.Entry entry= (Map.Entry)iter.next();
+
+                Annotation a= (Annotation)entry.getKey();
+                if (a.isMarkedDeleted())
+                    continue;
+
+                Decoration pp = (Decoration)entry.getValue();
+
+                maxLayer= Math.max(maxLayer, pp.fLayer + 1); // dynamically update layer maximum
+                if (pp.fLayer !is layer) // wrong layer: skip annotation
+                    continue;
+
+                Position p= pp.fPosition;
+                if (fSourceViewer instanceof ITextViewerExtension5) {
+                    ITextViewerExtension5 extension3= (ITextViewerExtension5) fSourceViewer;
+                    if (null is extension3.modelRange2WidgetRange(new Region(p.getOffset(), p.getLength())))
+                        continue;
+                } else if (!fSourceViewer.overlapsWithVisibleRegion(p.offset, p.length)) {
+                    continue;
+                }
+
+                int regionEnd= region.getOffset() + region.getLength();
+                int pEnd= p.getOffset() + p.getLength();
+                if (pEnd >= region.getOffset() && regionEnd > p.getOffset()) {
+                    int start= Math.max(p.getOffset(), region.getOffset());
+                    int end= Math.min(regionEnd, pEnd);
+                    int length= Math.max(end - start, 0);
+                    StyleRange styleRange= new StyleRange(start, length, null, null);
+                    ((ITextStyleStrategy)pp.fPaintingStrategy).applyTextStyle(styleRange, pp.fColor);
+                    tp.mergeStyleRange(styleRange);
+                }
+            }
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.source.IAnnotationModelListener#modelChanged(dwtx.jface.text.source.IAnnotationModel)
+     */
+    public synchronized void modelChanged(final IAnnotationModel model) {
+        if (DEBUG)
+            System.err.println("AP: OLD API of AnnotationModelListener called"); //$NON-NLS-1$
+
+        modelChanged(new AnnotationModelEvent(model));
+    }
+
+    /*
+     * @see dwtx.jface.text.source.IAnnotationModelListenerExtension#modelChanged(dwtx.jface.text.source.AnnotationModelEvent)
+     */
+    public void modelChanged(final AnnotationModelEvent event) {
+        Display textWidgetDisplay;
+        try {
+            StyledText textWidget= fTextWidget;
+            if (textWidget is null || textWidget.isDisposed())
+                return;
+            textWidgetDisplay= textWidget.getDisplay();
+        } catch (DWTException ex) {
+            if (ex.code is DWT.ERROR_WIDGET_DISPOSED)
+                return;
+            throw ex;
+        }
+
+        if (fIsSettingModel) {
+            // inside the UI thread -> no need for posting
+            if (textWidgetDisplay is Display.getCurrent())
+                updatePainting(event);
+            else {
+                /*
+                 * we can throw away the changes since
+                 * further update painting will happen
+                 */
+                return;
+            }
+        } else {
+            if (DEBUG && event !is null && event.isWorldChange()) {
+                System.out.println("AP: WORLD CHANGED, stack trace follows:"); //$NON-NLS-1$
+                new Throwable().printStackTrace(System.out);
+            }
+
+            // XXX: posting here is a problem for annotations that are being
+            // removed and the positions of which are not updated to document
+            // changes any more. If the document gets modified between
+            // now and running the posted runnable, the position information
+            // is not accurate any longer.
+            textWidgetDisplay.asyncExec(new Runnable() {
+                public void run() {
+                    if (fTextWidget !is null && !fTextWidget.isDisposed())
+                        updatePainting(event);
+                }
+            });
+        }
+    }
+
+    /**
+     * Sets the color in which the squiggly for the given annotation type should be drawn.
+     *
+     * @param annotationType the annotation type
+     * @param color the color
+     */
+    public void setAnnotationTypeColor(Object annotationType, Color color) {
+        if (color !is null)
+            fAnnotationType2Color.put(annotationType, color);
+        else
+            fAnnotationType2Color.remove(annotationType);
+        fCachedAnnotationType2Color.clear();
+    }
+
+    /**
+     * Adds the given annotation type to the list of annotation types whose
+     * annotations should be painted by this painter using squiggly drawing. If the annotation  type
+     * is already in this list, this method is without effect.
+     *
+     * @param annotationType the annotation type
+     */
+    public void addAnnotationType(Object annotationType) {
+        addAnnotationType(annotationType, SQUIGGLES);
+    }
+
+    /**
+     * Adds the given annotation type to the list of annotation types whose
+     * annotations should be painted by this painter using the given drawing strategy.
+     * If the annotation type is already in this list, the old drawing strategy gets replaced.
+     *
+     * @param annotationType the annotation type
+     * @param drawingStrategyID the id of the drawing strategy that should be used for this annotation type
+     * @since 3.0
+     */
+    public void addAnnotationType(Object annotationType, Object drawingStrategyID) {
+        fAnnotationType2PaintingStrategyId.put(annotationType, drawingStrategyID);
+        fCachedAnnotationType2PaintingStrategy.clear();
+
+        if (fTextInputListener is null) {
+            fTextInputListener= new ITextInputListener() {
+
+                /*
+                 * @see dwtx.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument)
+                 */
+                public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+                    fInputDocumentAboutToBeChanged= true;
+                }
+
+                /*
+                 * @see dwtx.jface.text.ITextInputListener#inputDocumentChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument)
+                 */
+                public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+                    fInputDocumentAboutToBeChanged= false;
+                }
+            };
+            fSourceViewer.addTextInputListener(fTextInputListener);
+        }
+        
+    }
+
+    /**
+     * Registers a new drawing strategy under the given ID. If there is already a
+     * strategy registered under <code>id</code>, the old strategy gets replaced.
+     * <p>The given id can be referenced when adding annotation types, see
+     * {@link #addAnnotationType(Object, Object)}.</p>
+     *
+     * @param id the identifier under which the strategy can be referenced, not <code>null</code>
+     * @param strategy the new strategy
+     * @since 3.0
+     */
+    public void addDrawingStrategy(Object id, IDrawingStrategy strategy) {
+        // don't permit null as null is used to signal that an annotation type is not
+        // registered with a specific strategy, and that its annotation hierarchy should be searched
+        if (id is null)
+            throw new IllegalArgumentException();
+        fPaintingStrategyId2PaintingStrategy.put(id, strategy);
+        fCachedAnnotationType2PaintingStrategy.clear();
+    }
+
+    /**
+     * Registers a new drawing strategy under the given ID. If there is already
+     * a strategy registered under <code>id</code>, the old strategy gets
+     * replaced.
+     * <p>
+     * The given id can be referenced when adding annotation types, see
+     * {@link #addAnnotationType(Object, Object)}.
+     * </p>
+     * 
+     * @param id the identifier under which the strategy can be referenced, not <code>null</code>
+     * @param strategy the new strategy
+     * @since 3.4
+     */
+    public void addTextStyleStrategy(Object id, ITextStyleStrategy strategy) {
+        // don't permit null as null is used to signal that an annotation type is not
+        // registered with a specific strategy, and that its annotation hierarchy should be searched
+        if (id is null)
+            throw new IllegalArgumentException();
+        fPaintingStrategyId2PaintingStrategy.put(id, strategy);
+        fCachedAnnotationType2PaintingStrategy.clear();
+    }
+
+    /**
+     * Adds the given annotation type to the list of annotation types whose
+     * annotations should be highlighted this painter. If the annotation  type
+     * is already in this list, this method is without effect.
+     *
+     * @param annotationType the annotation type
+     * @since 3.0
+     */
+    public void addHighlightAnnotationType(Object annotationType) {
+        addAnnotationType(annotationType, HIGHLIGHTING);
+    }
+
+    /**
+     * Removes the given annotation type from the list of annotation types whose
+     * annotations are painted by this painter. If the annotation type is not
+     * in this list, this method is without effect.
+     *
+     * @param annotationType the annotation type
+     */
+    public void removeAnnotationType(Object annotationType) {
+        fCachedAnnotationType2PaintingStrategy.clear();
+        fAnnotationType2PaintingStrategyId.remove(annotationType);
+        if (fAnnotationType2PaintingStrategyId.isEmpty() && fTextInputListener !is null) {
+            fSourceViewer.removeTextInputListener(fTextInputListener);
+            fTextInputListener= null;
+            fInputDocumentAboutToBeChanged= false;
+        }
+    }
+
+    /**
+     * Removes the given annotation type from the list of annotation types whose
+     * annotations are highlighted by this painter. If the annotation type is not
+     * in this list, this method is without effect.
+     *
+     * @param annotationType the annotation type
+     * @since 3.0
+     */
+    public void removeHighlightAnnotationType(Object annotationType) {
+        removeAnnotationType(annotationType);
+    }
+
+    /**
+     * Clears the list of annotation types whose annotations are
+     * painted by this painter.
+     */
+    public void removeAllAnnotationTypes() {
+        fCachedAnnotationType2PaintingStrategy.clear();
+        fAnnotationType2PaintingStrategyId.clear();
+        if (fTextInputListener !is null) {
+            fSourceViewer.removeTextInputListener(fTextInputListener);
+            fTextInputListener= null;
+        }
+    }
+
+    /**
+     * Returns whether the list of annotation types whose annotations are painted
+     * by this painter contains at least on element.
+     *
+     * @return <code>true</code> if there is an annotation type whose annotations are painted
+     */
+    public bool isPaintingAnnotations() {
+        return !fAnnotationType2PaintingStrategyId.isEmpty();
+    }
+
+    /*
+     * @see dwtx.jface.text.IPainter#dispose()
+     */
+    public void dispose() {
+
+        if (fAnnotationType2Color !is null) {
+            fAnnotationType2Color.clear();
+            fAnnotationType2Color= null;
+        }
+
+        if (fCachedAnnotationType2Color !is null) {
+            fCachedAnnotationType2Color.clear();
+            fCachedAnnotationType2Color= null;
+        }
+
+        if (fCachedAnnotationType2PaintingStrategy !is null) {
+            fCachedAnnotationType2PaintingStrategy.clear();
+            fCachedAnnotationType2PaintingStrategy= null;
+        }
+        
+        if (fAnnotationType2PaintingStrategyId !is null) {
+            fAnnotationType2PaintingStrategyId.clear();
+            fAnnotationType2PaintingStrategyId= null;
+        }
+        
+        fTextWidget= null;
+        fSourceViewer= null;
+        fAnnotationAccess= null;
+        fModel= null;
+        synchronized (fDecorationMapLock) {
+            fDecorationsMap= null;
+        }
+        synchronized (fHighlightedDecorationsMapLock) {
+            fHighlightedDecorationsMap= null;
+        }
+    }
+
+    /**
+     * Returns the document offset of the upper left corner of the source viewer's view port,
+     * possibly including partially visible lines.
+     *
+     * @return the document offset if the upper left corner of the view port
+     */
+    private int getInclusiveTopIndexStartOffset() {
+
+        if (fTextWidget !is null && !fTextWidget.isDisposed()) {
+            int top= JFaceTextUtil.getPartialTopIndex(fSourceViewer);
+            try {
+                IDocument document= fSourceViewer.getDocument();
+                return document.getLineOffset(top);
+            } catch (BadLocationException x) {
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns the first invisible document offset of the lower right corner of the source viewer's view port,
+     * possibly including partially visible lines.
+     *
+     * @return the first invisible document offset of the lower right corner of the view port
+     */
+    private int getExclusiveBottomIndexEndOffset() {
+
+        if (fTextWidget !is null && !fTextWidget.isDisposed()) {
+            int bottom= JFaceTextUtil.getPartialBottomIndex(fSourceViewer);
+            try {
+                IDocument document= fSourceViewer.getDocument();
+
+                if (bottom >= document.getNumberOfLines())
+                    bottom= document.getNumberOfLines() - 1;
+
+                return document.getLineOffset(bottom) + document.getLineLength(bottom);
+            } catch (BadLocationException x) {
+            }
+        }
+
+        return -1;
+    }
+
+    /*
+     * @see dwt.events.PaintListener#paintControl(dwt.events.PaintEvent)
+     */
+    public void paintControl(PaintEvent event) {
+        if (fTextWidget !is null)
+            handleDrawRequest(event);
+    }
+
+    /**
+     * Handles the request to draw the annotations using the given graphical context.
+     *
+     * @param event the paint event or <code>null</code>
+     */
+    private void handleDrawRequest(PaintEvent event) {
+
+        if (fTextWidget is null) {
+            // is already disposed
+            return;
+        }
+
+        IRegion clippingRegion= computeClippingRegion(event, false);
+        if (clippingRegion is null)
+            return;
+        
+        int vOffset= clippingRegion.getOffset();
+        int vLength= clippingRegion.getLength();
+        
+        final GC gc= event !is null ? event.gc : null;
+
+        // Clone decorations
+        Collection decorations;
+        synchronized (fDecorationMapLock) {
+            decorations= new ArrayList(fDecorationsMap.size());
+            decorations.addAll(fDecorationsMap.entrySet());
+        }
+
+        /*
+         * Create a new list of annotations to be drawn, since removing from decorations is more
+         * expensive. One bucket per drawing layer. Use linked lists as addition is cheap here.
+         */
+        ArrayList toBeDrawn= new ArrayList(10);
+        for (Iterator e = decorations.iterator(); e.hasNext();) {
+            Map.Entry entry= (Map.Entry)e.next();
+            
+            Annotation a= (Annotation)entry.getKey();
+            Decoration pp = (Decoration)entry.getValue();
+            // prune any annotation that is not drawable or does not need drawing
+            if (!(a.isMarkedDeleted() || skip(a) || !pp.fPosition.overlapsWith(vOffset, vLength))) {
+                // ensure sized appropriately
+                for (int i= toBeDrawn.size(); i <= pp.fLayer; i++)
+                    toBeDrawn.add(new LinkedList());
+                ((List) toBeDrawn.get(pp.fLayer)).add(entry);
+            }
+        }
+        IDocument document= fSourceViewer.getDocument();
+        for (Iterator it= toBeDrawn.iterator(); it.hasNext();) {
+            List layer= (List) it.next();
+            for (Iterator e = layer.iterator(); e.hasNext();) {
+                Map.Entry entry= (Map.Entry)e.next();
+                Annotation a= (Annotation)entry.getKey();
+                Decoration pp = (Decoration)entry.getValue();
+                drawDecoration(pp, gc, a, clippingRegion, document);
+            }
+        }
+    }
+    
+    private void drawDecoration(Decoration pp, GC gc, Annotation annotation, IRegion clippingRegion, IDocument document) {
+        if (clippingRegion is null)
+            return;
+
+        if (!(pp.fPaintingStrategy instanceof IDrawingStrategy))
+            return;
+
+        IDrawingStrategy drawingStrategy= (IDrawingStrategy)pp.fPaintingStrategy;
+
+        int clippingOffset= clippingRegion.getOffset();
+        int clippingLength= clippingRegion.getLength();
+
+        Position p= pp.fPosition;
+        try {
+
+            int startLine= document.getLineOfOffset(p.getOffset());
+            int lastInclusive= Math.max(p.getOffset(), p.getOffset() + p.getLength() - 1);
+            int endLine= document.getLineOfOffset(lastInclusive);
+
+            for (int i= startLine; i <= endLine; i++) {
+                int lineOffset= document.getLineOffset(i);
+                int paintStart= Math.max(lineOffset, p.getOffset());
+                String lineDelimiter= document.getLineDelimiter(i);
+                int delimiterLength= lineDelimiter !is null ? lineDelimiter.length() : 0;
+                int paintLength= Math.min(lineOffset + document.getLineLength(i) - delimiterLength, p.getOffset() + p.getLength()) - paintStart;
+                if (paintLength >= 0 && overlapsWith(paintStart, paintLength, clippingOffset, clippingLength)) {
+                    // otherwise inside a line delimiter
+                    IRegion widgetRange= getWidgetRange(paintStart, paintLength);
+                    if (widgetRange !is null) {
+                        drawingStrategy.draw(annotation, gc, fTextWidget, widgetRange.getOffset(), widgetRange.getLength(), pp.fColor);
+                    }
+                }
+            }
+
+        } catch (BadLocationException x) {
+        }
+    }
+    
+    /**
+     * Computes the model (document) region that is covered by the paint event's clipping region. If
+     * <code>event</code> is <code>null</code>, the model range covered by the visible editor
+     * area (viewport) is returned.
+     * 
+     * @param event the paint event or <code>null</code> to use the entire viewport
+     * @param isClearing tells whether the clipping is need for clearing an annotation
+     * @return the model region comprised by either the paint event's clipping region or the
+     *         viewport
+     * @since 3.2
+     */
+    private IRegion computeClippingRegion(PaintEvent event, bool isClearing) {
+        if (event is null) {
+            
+            if (!isClearing && fCurrentDrawRange !is null)
+                return new Region(fCurrentDrawRange.offset, fCurrentDrawRange.length);
+            
+            // trigger a repaint of the entire viewport
+            int vOffset= getInclusiveTopIndexStartOffset();
+            if (vOffset is -1)
+                return null;
+            
+            // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17147
+            int vLength= getExclusiveBottomIndexEndOffset() - vOffset;
+            
+            return new Region(vOffset, vLength);
+        }
+        
+        int widgetOffset;
+        try {
+            int widgetClippingStartOffset= fTextWidget.getOffsetAtLocation(new Point(0, event.y));
+            int firstWidgetLine= fTextWidget.getLineAtOffset(widgetClippingStartOffset);
+            widgetOffset= fTextWidget.getOffsetAtLine(firstWidgetLine);
+        } catch (IllegalArgumentException ex1) {
+            try {
+                int firstVisibleLine= JFaceTextUtil.getPartialTopIndex(fTextWidget);
+                widgetOffset= fTextWidget.getOffsetAtLine(firstVisibleLine);
+            } catch (IllegalArgumentException ex2) { // above try code might fail too
+                widgetOffset= 0;
+            }
+        }
+        
+        int widgetEndOffset;
+        try {
+            int widgetClippingEndOffset= fTextWidget.getOffsetAtLocation(new Point(0, event.y + event.height));
+            int lastWidgetLine= fTextWidget.getLineAtOffset(widgetClippingEndOffset);
+            widgetEndOffset= fTextWidget.getOffsetAtLine(lastWidgetLine + 1);
+        } catch (IllegalArgumentException ex1) {
+            // happens if the editor is not "full", e.g. the last line of the document is visible in the editor
+            try {
+                int lastVisibleLine= JFaceTextUtil.getPartialBottomIndex(fTextWidget);
+                if (lastVisibleLine is fTextWidget.getLineCount() - 1)
+                    // last line
+                    widgetEndOffset= fTextWidget.getCharCount();
+                else
+                    widgetEndOffset= fTextWidget.getOffsetAtLine(lastVisibleLine + 1) - 1;
+            } catch (IllegalArgumentException ex2) { // above try code might fail too
+                widgetEndOffset= fTextWidget.getCharCount();
+            }
+        }
+        
+        IRegion clippingRegion= getModelRange(widgetOffset, widgetEndOffset - widgetOffset);
+        
+        return clippingRegion;
+    }
+
+    /**
+     * Should the given annotation be skipped when handling draw requests?
+     *
+     * @param annotation the annotation
+     * @return <code>true</code> iff the given annotation should be
+     *         skipped when handling draw requests
+     * @since 3.0
+     */
+    protected bool skip(Annotation annotation) {
+        return false;
+    }
+
+    /**
+     * Returns the widget region that corresponds to the
+     * given offset and length in the viewer's document.
+     * 
+     * @param modelOffset the model offset
+     * @param modelLength the model length
+     * @return the corresponding widget region
+     */
+    private IRegion getWidgetRange(int modelOffset, int modelLength) {
+        fReusableRegion.setOffset(modelOffset);
+        fReusableRegion.setLength(modelLength);
+
+        if (fReusableRegion is null || fReusableRegion.getOffset() is Integer.MAX_VALUE)
+            return null;
+
+        if (fSourceViewer instanceof ITextViewerExtension5) {
+            ITextViewerExtension5 extension= (ITextViewerExtension5) fSourceViewer;
+            return extension.modelRange2WidgetRange(fReusableRegion);
+        }
+
+        IRegion region= fSourceViewer.getVisibleRegion();
+        int offset= region.getOffset();
+        int length= region.getLength();
+
+        if (overlapsWith(fReusableRegion, region)) {
+            int p1= Math.max(offset, fReusableRegion.getOffset());
+            int p2= Math.min(offset + length, fReusableRegion.getOffset() + fReusableRegion.getLength());
+            return new Region(p1 - offset, p2 - p1);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the model region that corresponds to the given region in the
+     * viewer's text widget.
+     *
+     * @param offset the offset in the viewer's widget
+     * @param length the length in the viewer's widget
+     * @return the corresponding document region
+     * @since 3.2
+     */
+    private IRegion getModelRange(int offset, int length) {
+        if (offset is Integer.MAX_VALUE)
+            return null;
+
+        if (fSourceViewer instanceof ITextViewerExtension5) {
+            ITextViewerExtension5 extension= (ITextViewerExtension5) fSourceViewer;
+            return extension.widgetRange2ModelRange(new Region(offset, length));
+        }
+        
+        IRegion region= fSourceViewer.getVisibleRegion();
+        return new Region(region.getOffset() + offset, length);
+    }
+    
+    /**
+     * Checks whether the intersection of the given text ranges
+     * is empty or not.
+     *
+     * @param range1 the first range to check
+     * @param range2 the second range to check
+     * @return <code>true</code> if intersection is not empty
+     */
+    private bool overlapsWith(IRegion range1, IRegion range2) {
+        return overlapsWith(range1.getOffset(), range1.getLength(), range2.getOffset(), range2.getLength());
+    }
+
+    /**
+     * Checks whether the intersection of the given text ranges
+     * is empty or not.
+     *
+     * @param offset1 offset of the first range
+     * @param length1 length of the first range
+     * @param offset2 offset of the second range
+     * @param length2 length of the second range
+     * @return <code>true</code> if intersection is not empty
+     */
+    private bool overlapsWith(int offset1, int length1, int offset2, int length2) {
+        int end= offset2 + length2;
+        int thisEnd= offset1 + length1;
+
+        if (length2 > 0) {
+            if (length1 > 0)
+                return offset1 < end && offset2 < thisEnd;
+            return  offset2 <= offset1 && offset1 < end;
+        }
+
+        if (length1 > 0)
+            return offset1 <= offset2 && offset2 < thisEnd;
+        return offset1 is offset2;
+    }
+
+    /*
+     * @see dwtx.jface.text.IPainter#deactivate(bool)
+     */
+    public void deactivate(bool redraw) {
+        if (fIsActive) {
+            fIsActive= false;
+            disablePainting(redraw);
+            setModel(null);
+            catchupWithModel(null);
+        }
+    }
+
+    /**
+     * Returns whether the given reason causes a repaint.
+     *
+     * @param reason the reason
+     * @return <code>true</code> if repaint reason, <code>false</code> otherwise
+     * @since 3.0
+     */
+    protected bool isRepaintReason(int reason) {
+        return CONFIGURATION is reason || INTERNAL is reason;
+    }
+
+    /**
+     * Retrieves the annotation model from the given source viewer.
+     *
+     * @param sourceViewer the source viewer
+     * @return the source viewer's annotation model or <code>null</code> if none can be found
+     * @since 3.0
+     */
+    protected IAnnotationModel findAnnotationModel(ISourceViewer sourceViewer) {
+        if(sourceViewer !is null)
+            return sourceViewer.getAnnotationModel();
+        return null;
+    }
+
+    /*
+     * @see dwtx.jface.text.IPainter#paint(int)
+     */
+    public void paint(int reason) {
+        if (fSourceViewer.getDocument() is null) {
+            deactivate(false);
+            return;
+        }
+
+        if (!fIsActive) {
+            IAnnotationModel model= findAnnotationModel(fSourceViewer);
+            if (model !is null) {
+                fIsActive= true;
+                setModel(model);
+            }
+        } else if (isRepaintReason(reason))
+            updatePainting(null);
+    }
+
+    /*
+     * @see dwtx.jface.text.IPainter#setPositionManager(dwtx.jface.text.IPaintPositionManager)
+     */
+    public void setPositionManager(IPaintPositionManager manager) {
+    }
+}