From 3b60a74ba0cdddbfac2322cd6743d790d16af3f9 Mon Sep 17 00:00:00 2001 From: angelozerr Date: Thu, 23 Nov 2017 17:31:44 +0100 Subject: Bug 527675 - [CodeMining] Provide inline annotations support Change-Id: I699f425123580cddfa771f91aa0b6c08c7c245c7 Signed-off-by: angelozerr --- org.eclipse.jface.text/META-INF/MANIFEST.MF | 1 + .../source/inlined/AbstractInlinedAnnotation.java | 102 ++++++ .../inlined/InlinedAnnotationDrawingStrategy.java | 169 ++++++++++ .../source/inlined/InlinedAnnotationSupport.java | 352 +++++++++++++++++++++ .../text/source/inlined/LineContentAnnotation.java | 67 ++++ .../text/source/inlined/LineHeaderAnnotation.java | 44 +++ .../jface/text/source/inlined/Positions.java | 62 ++++ 7 files changed, 797 insertions(+) create mode 100644 org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java create mode 100644 org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java create mode 100644 org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationSupport.java create mode 100644 org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java create mode 100644 org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineHeaderAnnotation.java create mode 100644 org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/Positions.java (limited to 'org.eclipse.jface.text') diff --git a/org.eclipse.jface.text/META-INF/MANIFEST.MF b/org.eclipse.jface.text/META-INF/MANIFEST.MF index dff65ab29c5..ca017ba5b68 100644 --- a/org.eclipse.jface.text/META-INF/MANIFEST.MF +++ b/org.eclipse.jface.text/META-INF/MANIFEST.MF @@ -25,6 +25,7 @@ Export-Package: org.eclipse.jface.text.revisions, org.eclipse.jface.text.rules, org.eclipse.jface.text.source, + org.eclipse.jface.text.source.inlined, org.eclipse.jface.text.source.projection, org.eclipse.jface.text.source.projection.images, org.eclipse.jface.text.templates, diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java new file mode 100644 index 00000000000..6bf0c5c12e8 --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2017 Angelo ZERR. + * 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: + * Angelo Zerr - [CodeMining] Provide inline annotations support - Bug 527675 + */ +package org.eclipse.jface.text.source.inlined; + +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; + +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; + +/** + * Abstract class for inlined annotation. + * + * @since 3.13.0 + * @noimplement This interface is not intended to be implemented by clients. + */ +public abstract class AbstractInlinedAnnotation extends Annotation { + + /** + * The type of inlined annotations. + */ + public static final String TYPE= "org.eclipse.jface.text.source.inlined"; //$NON-NLS-1$ + + /** + * The position where the annotation must be drawn. + */ + private final Position position; + + /** + * The {@link StyledText} widget where the annotation must be drawn. + */ + private final StyledText textWidget; + + /** + * Inlined annotation constructor. + * + * @param position the position where the annotation must be drawn. + * @param textWidget the {@link StyledText} widget where the annotation must be drawn. + */ + protected AbstractInlinedAnnotation(Position position, StyledText textWidget) { + super(TYPE, false, ""); //$NON-NLS-1$ + this.position= position; + this.textWidget= textWidget; + } + + /** + * Returns the position where the annotation must be drawn. + * + * @return the position where the annotation must be drawn. + */ + public Position getPosition() { + return position; + } + + /** + * Returns the {@link StyledText} widget where the annotation must be drawn. + * + * @return the {@link StyledText} widget where the annotation must be drawn. + */ + public StyledText getTextWidget() { + return textWidget; + } + + /** + * Redraw the inlined annotation. + */ + public void redraw() { + StyledText text= getTextWidget(); + InlinedAnnotationSupport.runInUIThread(text, (t) -> { + Position pos= getPosition(); + InlinedAnnotationDrawingStrategy.draw(this, null, t, pos.getOffset(), pos.getLength(), null); + }); + } + + /** + * Draw the inlined annotation. By default it draw the text of the annotation with gray color. + * User can override this method to draw anything. + * + * @param gc the graphics context + * @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 + * @param x the x position of the annotation + * @param y the y position of the annotation + */ + public void draw(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { + gc.setForeground(color); + gc.setBackground(textWidget.getBackground()); + gc.drawText(getText(), x, y); + } + +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java new file mode 100644 index 00000000000..85d90bb0e4c --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2017 Angelo ZERR. + * 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: + * Angelo Zerr - [CodeMining] Provide inline annotations support - Bug 527675 + */ +package org.eclipse.jface.text.source.inlined; + +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.GlyphMetrics; +import org.eclipse.swt.graphics.Rectangle; + +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy; + +/** + * {@link IDrawingStrategy} implementation to render {@link AbstractInlinedAnnotation}. + * + * @since 3.13.0 + */ +class InlinedAnnotationDrawingStrategy implements IDrawingStrategy { + + @Override + public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) { + if (!(annotation instanceof AbstractInlinedAnnotation)) { + return; + } + InlinedAnnotationDrawingStrategy.draw((AbstractInlinedAnnotation) annotation, gc, textWidget, offset, length, + color); + } + + /** + * Draw the inlined annotation. + * + * @param annotation the annotation to be drawn + * @param gc the graphics context, null 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 + */ + public static void draw(AbstractInlinedAnnotation annotation, GC gc, StyledText textWidget, int offset, int length, + Color color) { + if (annotation.isMarkedDeleted()) { + // When annotation is deleted, redraw the styled text to hide old draw of + // annotations + textWidget.redraw(); + // update caret offset since line spacing has changed. + textWidget.setCaretOffset(textWidget.getCaretOffset()); + return; + } + if (annotation instanceof LineHeaderAnnotation) { + draw((LineHeaderAnnotation) annotation, gc, textWidget, offset, length, color); + } else { + draw((LineContentAnnotation) annotation, gc, textWidget, offset, length, color); + } + } + + /** + * Draw the line header annotation in the line spacing of the previous line. + * + * @param annotation the annotation to be drawn + * @param gc the graphics context, null 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 + */ + private static void draw(LineHeaderAnnotation annotation, GC gc, StyledText textWidget, int offset, int length, + Color color) { + // compute current, previous line index. + int lineIndex= -1; + try { + lineIndex= textWidget.getLineAtOffset(offset); + } catch (Exception e) { + return; + } + int previousLineIndex= lineIndex - 1; + if (gc != null) { + // Compute the location of the annotation + int x= textWidget.getLocationAtOffset(offset).x; + int y= 0; + int height= annotation.getHeight(); + if (lineIndex > 0) { + int previousOffset= textWidget.getOffsetAtLine(previousLineIndex); + y= textWidget.getLocationAtOffset(previousOffset).y + height; + } + Rectangle clipping= gc.getClipping(); + if (clipping.contains(x, y)) { + // GC clipping contains the x, y where annotation must be drawn. + + // Colorize line spacing area with the background of StyledText to avoid having highlighted line color + gc.setBackground(textWidget.getBackground()); + Rectangle client= textWidget.getClientArea(); + textWidget.drawBackground(gc, x, y, client.width, height); + + // draw the annotation + annotation.draw(gc, textWidget, offset, length, color, x, y); + return; + } else { + if (!(clipping.y - height == y)) { + // Clipping doesn't include the y of previous line spacing, stop the redraw + // range. + return; + } + } + } + + if (previousLineIndex < 0) { + // There are none previous line, do nothing + return; + } + // refresh the previous line range where line header annotation must be drawn. + int previousOffset= textWidget.getOffsetAtLine(previousLineIndex); + int lineLength= offset - previousOffset; + textWidget.redrawRange(previousOffset, lineLength, true); + } + + /** + * Draw the line content annotation inside line in the empty area computed by + * {@link GlyphMetrics}. + * + * @param annotation the annotation to be drawn + * @param gc the graphics context, null 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 + */ + private static void draw(LineContentAnnotation annotation, GC gc, StyledText textWidget, int offset, int length, + Color color) { + if (gc != null) { + // Compute the location of the annotation + FontMetrics fontMetrics= gc.getFontMetrics(); + Rectangle bounds= textWidget.getTextBounds(offset, offset); + int x= bounds.x + fontMetrics.getLeading(); + int y= bounds.y + fontMetrics.getDescent(); + + // Draw the line content annotation + annotation.draw(gc, textWidget, offset, length, color, x, y); + + // The inline annotation replaces one character by taking a place width + // GlyphMetrics + // Here we need to redraw this first character because GlyphMetrics clip this + // character. + String s= textWidget.getText(offset, offset); + StyleRange style= textWidget.getStyleRangeAtOffset(offset); + if (style != null) { + if (style.background != null) { + gc.setBackground(style.background); + } + if (style.foreground != null) { + gc.setForeground(style.foreground); + } + } + gc.drawString(s, bounds.x + bounds.width - gc.stringExtent(s).x, bounds.y, true); + } else { + textWidget.redrawRange(offset, length, true); + } + } +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationSupport.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationSupport.java new file mode 100644 index 00000000000..bf6b9c4074a --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationSupport.java @@ -0,0 +1,352 @@ +/** + * Copyright (c) 2017 Angelo ZERR. + * 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: + * Angelo Zerr - [CodeMining] Provide inline annotations support - Bug 527675 + */ +package org.eclipse.jface.text.source.inlined; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.Consumer; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.custom.StyledTextLineSpacingProvider; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Display; + +import org.eclipse.core.runtime.Assert; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ISynchronizable; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.AnnotationPainter; +import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IAnnotationModelExtension; +import org.eclipse.jface.text.source.IAnnotationModelExtension2; +import org.eclipse.jface.text.source.ISourceViewer; + +/** + * Support to draw inlined annotations: + * + *
    + *
  • line header annotation with {@link LineHeaderAnnotation}.
  • + *
  • line content annotation with {@link LineContentAnnotation}.
  • + *
+ * + * @since 3.13.0 + */ +public class InlinedAnnotationSupport implements StyledTextLineSpacingProvider { + + /** + * The annotation inlined strategy singleton. + */ + private static final IDrawingStrategy INLINED_STRATEGY= new InlinedAnnotationDrawingStrategy(); + + /** + * The annotation inlined strategy ID. + */ + private static final String INLINED_STRATEGY_ID= "inlined"; //$NON-NLS-1$ + + /** + * The source viewer + */ + private ISourceViewer fViewer; + + /** + * The annotation painter to use to draw the inlined annotations. + */ + private AnnotationPainter fPainter; + + /** + * Holds the current inlined annotations. + */ + private Set fInlinedAnnotations; + + /** + * Install the inlined annotation support for the given viewer. + * + * @param viewer the source viewer + * @param painter the annotation painter to use to draw the inlined annotations. + */ + public void install(ISourceViewer viewer, AnnotationPainter painter) { + Assert.isNotNull(viewer); + Assert.isNotNull(painter); + fViewer= viewer; + fPainter= painter; + initPainter(); + StyledText text= fViewer.getTextWidget(); + if (text == null || text.isDisposed()) { + return; + } + setColor(text.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY)); + text.setLineSpacingProvider(this); + } + + /** + * Initialize painter with inlined drawing strategy. + */ + private void initPainter() { + fPainter.addDrawingStrategy(INLINED_STRATEGY_ID, INLINED_STRATEGY); + fPainter.addAnnotationType(AbstractInlinedAnnotation.TYPE, INLINED_STRATEGY_ID); + } + + /** + * Set the color to use to draw the inlined annotations. + * + * @param color the color to use to draw the inlined annotations. + */ + public void setColor(Color color) { + fPainter.setAnnotationTypeColor(AbstractInlinedAnnotation.TYPE, color); + } + + /** + * Unisntall the inlined annotation support + */ + public void uninstall() { + fViewer= null; + fPainter= null; + } + + /** + * Update the given inlined annotation. + * + * @param annotations the inlined annotation. + */ + public void updateAnnotations(Set annotations) { + IDocument document= fViewer != null ? fViewer.getDocument() : null; + if (document == null) { + // this case comes from when editor is closed before rendered is done. + return; + } + IAnnotationModel annotationModel= fViewer.getAnnotationModel(); + if (annotationModel == null) { + return; + } + StyledText styledText= fViewer.getTextWidget(); + Map annotationsToAdd= new HashMap<>(); + List annotationsToRemove= fInlinedAnnotations != null + ? new ArrayList<>(fInlinedAnnotations) + : Collections.emptyList(); + // Loop for annotations to update + for (AbstractInlinedAnnotation ann : annotations) { + if (!annotationsToRemove.remove(ann)) { + // The annotation was not created, add it + annotationsToAdd.put(ann, ann.getPosition()); + } + if (ann instanceof LineContentAnnotation) { + // Create metrics with well width to add space where the inline annotation must + // be drawn. + runInUIThread(styledText, (text) -> { + StyleRange s= new StyleRange(); + s.start= ann.getPosition().getOffset(); + s.length= 1; + s.metrics= ((LineContentAnnotation) ann).createMetrics(); + text.setStyleRange(s); + }); + } + } + // Process annotations to remove + for (AbstractInlinedAnnotation ann : annotationsToRemove) { + // Mark annotation as deleted to ignore the draw + ann.markDeleted(true); + if (ann instanceof LineContentAnnotation) { + // Set metrics to null to remove space of the inline annotation + runInUIThread(styledText, (text) -> { + StyleRange s= new StyleRange(); + s.start= ann.getPosition().getOffset(); + s.length= 1; + s.metrics= null; + text.setStyleRange(s); + }); + } + } + // Update annotation model + synchronized (getLockObject(annotationModel)) { + if (annotationsToAdd.size() == 0 && annotationsToRemove.size() == 0) { + // None change, do nothing. Here the user could change position of codemining + // range + // (ex: user key press + // "Enter"), but we don't need to redraw the viewer because change of position + // is done by AnnotationPainter. + } else { + if (annotationModel instanceof IAnnotationModelExtension) { + ((IAnnotationModelExtension) annotationModel).replaceAnnotations( + annotationsToRemove.toArray(new Annotation[annotationsToRemove.size()]), annotationsToAdd); + } else { + removeInlinedAnnotations(); + Iterator> iter= annotationsToAdd.entrySet().iterator(); + while (iter.hasNext()) { + Entry mapEntry= iter.next(); + annotationModel.addAnnotation(mapEntry.getKey(), mapEntry.getValue()); + } + } + } + fInlinedAnnotations= annotations; + } + } + + /** + * Returns the existing codemining annotation with the given position information and null + * otherwise. + * + * @param pos the position + * @return the existing codemining annotation with the given position information and null + * otherwise. + */ + @SuppressWarnings("unchecked") + public T findExistingAnnotation(Position pos) { + if (fInlinedAnnotations == null) { + return null; + } + for (AbstractInlinedAnnotation ann : fInlinedAnnotations) { + if (ann.getPosition().offset == pos.offset) { + try { + return (T) ann; + } catch (ClassCastException e) { + // Do nothing + } + } + } + return null; + } + + /** + * Returns the lock object for the given annotation model. + * + * @param annotationModel the annotation model + * @return the annotation model's lock object + */ + private Object getLockObject(IAnnotationModel annotationModel) { + if (annotationModel instanceof ISynchronizable) { + Object lock= ((ISynchronizable) annotationModel).getLockObject(); + if (lock != null) + return lock; + } + return annotationModel; + } + + /** + * Remove the inlined annotations. + */ + private void removeInlinedAnnotations() { + + IAnnotationModel annotationModel= fViewer.getAnnotationModel(); + if (annotationModel == null || fInlinedAnnotations == null) + return; + + synchronized (getLockObject(annotationModel)) { + if (annotationModel instanceof IAnnotationModelExtension) { + ((IAnnotationModelExtension) annotationModel).replaceAnnotations( + fInlinedAnnotations.toArray(new Annotation[fInlinedAnnotations.size()]), null); + } else { + for (AbstractInlinedAnnotation annotation : fInlinedAnnotations) + annotationModel.removeAnnotation(annotation); + } + fInlinedAnnotations= null; + } + } + + /** + * Returns the line spacing from the given line index with the codemining annotations height and + * null otherwise. + */ + @SuppressWarnings("boxing") + @Override + public Integer getLineSpacing(int lineIndex) { + AbstractInlinedAnnotation annotation= getInlinedAnnotationAtLine(fViewer, lineIndex); + return (annotation instanceof LineHeaderAnnotation) + ? ((LineHeaderAnnotation) annotation).getHeight() + : null; + } + + /** + * Returns the {@link AbstractInlinedAnnotation} from the given line index and null otherwise. + * + * @param viewer the source viewer + * @param lineIndex the line index. + * @return the {@link AbstractInlinedAnnotation} from the given line index and null otherwise. + */ + public static AbstractInlinedAnnotation getInlinedAnnotationAtLine(ISourceViewer viewer, int lineIndex) { + if (viewer == null) { + return null; + } + IAnnotationModel annotationModel= viewer.getAnnotationModel(); + if (annotationModel == null) { + return null; + } + IDocument document= viewer.getDocument(); + int lineNumber= lineIndex + 1; + if (lineNumber > document.getNumberOfLines()) { + return null; + } + try { + IRegion line= document.getLineInformation(lineNumber); + Iterator iter= (annotationModel instanceof IAnnotationModelExtension2) + ? ((IAnnotationModelExtension2) annotationModel).getAnnotationIterator(line.getOffset(), + line.getLength(), true, true) + : annotationModel.getAnnotationIterator(); + while (iter.hasNext()) { + Annotation ann= iter.next(); + if (ann instanceof AbstractInlinedAnnotation) { + Position p= annotationModel.getPosition(ann); + if (p != null) { + if (p.overlapsWith(line.getOffset(), line.getLength())) { + return (AbstractInlinedAnnotation) ann; + } + } + } + } + } catch (BadLocationException e) { + return null; + } + return null; + } + + /** + * Execute UI {@link StyledText} function which requires UI Thread. + * + * @param text the styled text + * @param fn the function to execute. + */ + static void runInUIThread(StyledText text, Consumer fn) { + if (text == null || text.isDisposed()) { + return; + } + Display display= text.getDisplay(); + if (display.getThread() == Thread.currentThread()) { + try { + fn.accept(text); + } catch (Exception e) { + // Ignore UI error + } + } else { + display.asyncExec(() -> { + if (text.isDisposed()) { + return; + } + try { + fn.accept(text); + } catch (Exception e) { + // Ignore UI error + } + }); + } + } +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java new file mode 100644 index 00000000000..88f33f67e3d --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2017 Angelo ZERR. + * 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: + * Angelo Zerr - [CodeMining] Provide inline annotations support - Bug 527675 + */ +package org.eclipse.jface.text.source.inlined; + +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.GlyphMetrics; + +import org.eclipse.jface.text.Position; + +/** + * Inlined annotation which is drawn in the line content and which takes some place with a given + * width. + * + * @since 3.13.0 + */ +public class LineContentAnnotation extends AbstractInlinedAnnotation { + + /** + * Line content annotation constructor. + * + * @param position the position where the annotation must be drawn. + * @param textWidget the {@link StyledText} widget where the annotation must be drawn. + */ + public LineContentAnnotation(Position position, StyledText textWidget) { + super(position, textWidget); + } + + /** + * Returns an instance of GlyphMetrics used to takes 'width' place when the annotation is drawn + * inside the line. + * + * @return an instance of GlyphMetrics used to takes 'width' place when the annotation is drawn + * inside the line. + */ + public GlyphMetrics createMetrics() { + return new GlyphMetrics(0, 0, getWidth()); + } + + /** + * Returns the annotation width. By default it computes the well width for the text annotation. + * + * @return the annotation width. + */ + public int getWidth() { + String text= super.getText(); + if (text == null) { + return 0; + } + int nbChars= text.length() + 1; + StyledText styledText= super.getTextWidget(); + GC gc= new GC(styledText); + FontMetrics fontMetrics= gc.getFontMetrics(); + int width= nbChars * fontMetrics.getAverageCharWidth(); + gc.dispose(); + return width; + } +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineHeaderAnnotation.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineHeaderAnnotation.java new file mode 100644 index 00000000000..e9f3dc7f3ec --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineHeaderAnnotation.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2017 Angelo ZERR. + * 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: + * Angelo Zerr - [CodeMining] Provide inline annotations support - Bug 527675 + */ +package org.eclipse.jface.text.source.inlined; + +import org.eclipse.swt.custom.StyledText; + +import org.eclipse.jface.text.Position; + +/** + * Inlined annotation which is drawn before a line and which takes some place with a given height. + * + * @since 3.13.0 + */ +public class LineHeaderAnnotation extends AbstractInlinedAnnotation { + + /** + * Line header annotation constructor. + * + * @param position the position where the annotation must be drawn. + * @param textWidget the {@link StyledText} widget where the annotation must be drawn. + */ + public LineHeaderAnnotation(Position position, StyledText textWidget) { + super(position, textWidget); + } + + /** + * Returns the annotation height. By default, returns the {@link StyledText#getLineHeight()}. + * + * @return the annotation height. + */ + public int getHeight() { + StyledText styledText= super.getTextWidget(); + return styledText.getLineHeight(); + } + +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/Positions.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/Positions.java new file mode 100644 index 00000000000..5ae9c9855c4 --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/Positions.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2017 Angelo ZERR. + * 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: + * Angelo Zerr - [CodeMining] Provide inline annotations support - Bug 527675 + */ +package org.eclipse.jface.text.source.inlined; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; + +/** + * Utilities class to retrieve position. + * + * @since 3.13.0 + */ +public class Positions { + + /** + * Returns the line position by taking care or not of of leading spaces. + * + * @param lineIndex the line index + * @param document the document + * @param leadingSpaces true if line spacing must take care of and not otherwise. + * @return the line position by taking care of leading spaces. + * @throws BadLocationException if the line number is invalid in this document + */ + public static Position of(int lineIndex, IDocument document, boolean leadingSpaces) throws BadLocationException { + int offset= document.getLineOffset(lineIndex); + int lineLength= document.getLineLength(lineIndex); + String line= document.get(offset, lineLength); + if (leadingSpaces) { + offset+= getLeadingSpaces(line); + } + return new Position(offset, 1); + } + + /** + * Returns the leading spaces of the given line text. + * + * @param line the line text. + * @return the leading spaces of the given line text. + */ + private static int getLeadingSpaces(String line) { + int counter= 0; + char[] chars= line.toCharArray(); + for (char c : chars) { + if (c == '\t') + counter++; + else if (c == ' ') + counter++; + else + break; + } + return counter; + } +} -- cgit v1.2.3