Mercurial > projects > dwt-addons
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) { + } +}