Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 0e5df9419c66966da46065a30aaceaa8dce6862a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
 *  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);
	}

}

Back to the top