blob: 23d4f70680572276cf48671b1cf7f7ff577031e3 [file] [log] [blame]
/**
* 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();
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 getTextWidget().getLineAtOffset(widgetOffset) == getTextWidget().getLineAtOffset(widgetOffset - 1);
}
}