| /** |
| * 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 <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.FontMetrics; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.GlyphMetrics; |
| import org.eclipse.swt.graphics.Rectangle; |
| |
| import org.eclipse.jface.text.ITextViewer; |
| import org.eclipse.jface.text.ITextViewerExtension5; |
| import org.eclipse.jface.text.JFaceTextUtil; |
| 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 |
| */ |
| 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, <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 |
| */ |
| 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, <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 |
| */ |
| 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; |
| // check the previous line index where annotation must be drawn in line spacing is not hidden |
| ITextViewer viewer= annotation.getViewer(); |
| int firstLineIndex= JFaceTextUtil.getPartialTopIndex(viewer); |
| if (viewer instanceof ITextViewerExtension5) { |
| firstLineIndex= ((ITextViewerExtension5) viewer).modelLine2WidgetLine(firstLineIndex); |
| } |
| 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, <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 |
| */ |
| 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); |
| } |
| } |
| } |