| /** |
| * Copyright (c) 2017, 2018 Angelo ZERR. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Angelo Zerr <angelo.zerr@gmail.com> - [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.GC; |
| import org.eclipse.swt.graphics.GlyphMetrics; |
| |
| import org.eclipse.jface.text.ITextViewerExtension5; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.TextPresentation; |
| import org.eclipse.jface.text.source.ISourceViewer; |
| |
| /** |
| * Inlined annotation which is drawn in the line content and which takes some place with a given |
| * width. |
| * |
| * @since 3.13 |
| */ |
| public class LineContentAnnotation extends AbstractInlinedAnnotation { |
| |
| /** |
| * The annotation width |
| */ |
| private int width; |
| |
| private int redrawnCharacterWidth; |
| |
| /** |
| * Line content annotation constructor. |
| * |
| * @param position the position where the annotation must be drawn. |
| * @param viewer the {@link ISourceViewer} where the annotation must be drawn. |
| */ |
| public LineContentAnnotation(Position position, ISourceViewer viewer) { |
| super(position, viewer); |
| } |
| |
| /** |
| * Returns the annotation width. By default it computes the well width for the text annotation. |
| * |
| * @return the annotation width. |
| */ |
| public final int getWidth() { |
| return width; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * After drawn, compute the text width and update it. |
| * </p> |
| */ |
| @Override |
| public final void draw(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { |
| width= drawAndComputeWidth(gc, textWidget, offset, length, color, x, y); |
| } |
| |
| /** |
| * Draw the inlined annotation. By default it draws 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 |
| * @return the text width. |
| */ |
| protected int drawAndComputeWidth(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { |
| // Draw the text annotation and returns the width |
| super.draw(gc, textWidget, offset, length, color, x, y); |
| return (int) (gc.stringExtent(getText()).x + 2 * gc.getFontMetrics().getAverageCharacterWidth()); |
| } |
| |
| int getRedrawnCharacterWidth() { |
| return redrawnCharacterWidth; |
| } |
| |
| void setRedrawnCharacterWidth(int redrawnCharacterWidth) { |
| this.redrawnCharacterWidth= redrawnCharacterWidth; |
| } |
| |
| @Override |
| boolean contains(int x, int y) { |
| return (x >= this.fX && x <= this.fX + width && y >= this.fY && y <= this.fY + getTextWidget().getLineHeight()); |
| } |
| |
| /** |
| * Returns the style to apply with GlyphMetrics width only if needed. |
| * |
| * As it's using Widget position, the results can be passed directly to |
| * {@link StyledText#setStyleRange(StyleRange)} and family. However, in case of a Viewer |
| * providing project/folder with {@link ITextViewerExtension5}, the range must be transformed to |
| * model position before passing it to a {@link TextPresentation}. |
| * |
| * @param style the current style and null otherwise. |
| * @return the style to apply with GlyphMetrics width only if needed. It uses widget position, |
| * not model position. |
| */ |
| StyleRange updateStyle(StyleRange style) { |
| Position widgetPosition= computeWidgetPosition(); |
| if (widgetPosition == null) { |
| return null; |
| } |
| boolean usePreviousChar= drawRightToPreviousChar(widgetPosition.getOffset()); |
| if (width == 0 || getRedrawnCharacterWidth() == 0) { |
| return null; |
| } |
| int fullWidth= width + getRedrawnCharacterWidth(); |
| if (style == null) { |
| style= new StyleRange(); |
| style.start= widgetPosition.getOffset(); |
| if (usePreviousChar) { |
| style.start--; |
| } |
| style.length= 1; |
| } |
| GlyphMetrics metrics= style.metrics; |
| if (!isMarkedDeleted()) { |
| if (metrics == null) { |
| metrics= new GlyphMetrics(0, 0, fullWidth); |
| } else { |
| if (metrics.width == fullWidth) { |
| return null; |
| } |
| /** |
| * We must create a new GlyphMetrics instance because comparison with similarTo used |
| * later in StyledText#setStyleRange will compare the same (modified) and won't |
| * realize an update happened. |
| */ |
| metrics= new GlyphMetrics(0, 0, fullWidth); |
| } |
| } else { |
| metrics= null; |
| } |
| style.metrics= metrics; |
| return style; |
| } |
| |
| boolean drawRightToPreviousChar(int widgetOffset) { |
| return widgetOffset > 0 && |
| getTextWidget().getLineAtOffset(widgetOffset) == getTextWidget().getLineAtOffset(widgetOffset - 1); |
| } |
| |
| boolean isEndOfLine(int widgetOffset) { |
| StyledText text= getTextWidget(); |
| if (text.getCharCount() <= widgetOffset) { // Assuming widgetOffset >= 0 |
| return true; |
| } |
| int line= text.getLineAtOffset(widgetOffset); |
| int startOfLine= text.getOffsetAtLine(line); |
| int offsetInLine= widgetOffset - startOfLine; |
| return offsetInLine >= text.getLine(line).length(); |
| } |
| |
| } |