![JAR search and dependency download from the Maven repository](/logo.png)
org.netbeans.editor.DrawGraphics Maven / Gradle / Ivy
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor;
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
import javax.swing.text.JTextComponent;
import org.netbeans.editor.Annotations;
import java.awt.Graphics2D;
import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Shape;
import java.awt.Rectangle;
import javax.swing.text.View;
/** Draw graphics functions as abstraction over various kinds of drawing. It's used
* for drawing into classic graphics, printing and measuring.
* Generally there are only the setters for some properties because
* the draw-engine doesn't retrieve the values that it previously
* set.
*
* @author Miloslav Metelka
* @version 1.00
*/
interface DrawGraphics {
/** Set foreground color */
public void setForeColor(Color foreColor);
/** Set background color */
public void setBackColor(Color backColor);
/** Inform the draw-graphics about the current
* background color of the component.
*/
public void setDefaultBackColor(Color defaultBackColor);
public void setStrikeThroughColor(Color strikeThroughColor);
public void setUnderlineColor(Color underlineColor);
public void setWaveUnderlineColor(Color waveUnderlineColor);
/** Set current font */
public void setFont(Font font);
/** Set the current x-coordinate */
public void setX(int x);
/** Set the current y-coordinate */
public void setY(int y);
/** Set the height of the line. */
public void setLineHeight(int lineHeight);
/** Set the ascent of the line. */
public void setLineAscent(int lineAscent);
/** Get the AWT-graphics to determine whether this draws to a graphics.
* This is useful for fast line numbering and others.
*/
public Graphics getGraphics();
/** Whether draw graphics supports displaying of line numbers.
* If not line number displaying is not done.
*/
public boolean supportsLineNumbers();
/** Initialize this draw graphics before drawing */
public void init(DrawContext ctx);
/** Called when whole drawing ends. Can be used to deallocate
* some resources etc.
*/
public void finish();
/** Fill rectangle at the current [x, y] with the current
* background color.
* @param width width of the rectangle to fill in points. The current x-coordinate
* must be increased by width automatically.
*/
public void fillRect(int width);
/** Draw characters from the specified offset in the buffer
* @param offset offset in the buffer for drawn text; if the text contains
* tabs, then offset is set to -1 and length contains the count
* of the space characters that correspond to the expanded tabs
* @param length length of the text being drawn
* @param width width of the text being drawn in points. The current
* x-coordinate must be increased by width automatically.
*/
public void drawChars(int offset, int length, int width);
/** Draw the expanded tab characters.
* @param offset offset in the buffer where the tab characters start.
* @param length number of the tab characters
* @param spaceCount number of spaces that replace the tabs
* @param width width of the spaces in points. The current x-coordinate
* must be increased by width automatically.
*/
public void drawTabs(int offset, int length, int spaceCount, int width);
/** Set character buffer from which the characters are drawn. */
public void setBuffer(char[] buffer);
/** This method is called to notify this draw graphics in response
* from targetPos parameter passed to draw().
* @param offset position that was reached during the drawing.
* @param ch character at offset
* @param charWidth visual width of the character ch
* @param ctx current draw context containing
* @return whether the drawing should continue or not. If it returns
* false it's guaranteed that this method will not be called again
* and the whole draw() method will be stopped.
The only
* exception is when the -1 is used as the target offset
* when draw() is called which means that every offset
* is a potential target offset and must be checked.
* In this case the binary search is used when finding
* the target offset inside painted fragment. That greatly
* improves performance for long fragments because
* the font metrics measurements are relatively expensive.
*/
public boolean targetOffsetReached(int offset, char ch, int x,
int charWidth, DrawContext ctx);
/** EOL encountered and should be handled. */
public void eol();
/** Setter for painted view */
public void setView(javax.swing.text.View view);
/** Abstract draw-graphics that maintains a fg and bg color, font,
* current x and y coordinates.
*/
static abstract class AbstractDG implements DrawGraphics {
/** Current foreground color */
Color foreColor;
/** Current background color */
Color backColor;
/** Default background color */
Color defaultBackColor;
/** Current font */
Font font;
/** Character buffer from which the data are drawn */
char[] buffer;
/** Current x-coordinate */
int x;
/** Current y-coordinate */
int y;
/** Height of the line being drawn */
int lineHeight;
/** Ascent of the line being drawn */
int lineAscent;
public Color getForeColor() {
return foreColor;
}
public void setForeColor(Color foreColor) {
this.foreColor = foreColor;
}
public Color getBackColor() {
return backColor;
}
public void setBackColor(Color backColor) {
this.backColor = backColor;
}
public Color getDefaultBackColor() {
return defaultBackColor;
}
public void setDefaultBackColor(Color defaultBackColor) {
this.defaultBackColor = defaultBackColor;
}
public Font getFont() {
return font;
}
public void setFont(Font font) {
this.font = font;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getLineHeight() {
return lineHeight;
}
public void setLineHeight(int lineHeight) {
this.lineHeight = lineHeight;
}
public int getLineAscent() {
return lineAscent;
}
public void setLineAscent(int lineAscent) {
this.lineAscent = lineAscent;
}
public char[] getBuffer() {
return buffer;
}
public void setBuffer(char[] buffer) {
this.buffer = buffer;
}
public void drawChars(int offset, int length, int width) {
x += width;
}
public void drawTabs(int offset, int length, int spaceCount, int width) {
x += width;
}
public void setStrikeThroughColor(Color strikeThroughColor) {
}
public void setUnderlineColor(Color underlineColor) {
}
public void setWaveUnderlineColor(Color waveUnderlineColor) {
}
public void setView(javax.swing.text.View view) {
}
}
static class SimpleDG extends AbstractDG {
public Graphics getGraphics() {
return null;
}
public boolean supportsLineNumbers() {
return false;
}
public void init(DrawContext ctx) {
}
public void finish() {
}
public void fillRect(int width) {
}
public boolean targetOffsetReached(int offset, char ch, int x,
int charWidth, DrawContext ctx) {
return true; // shouldn't reach this place
}
public void eol() {
}
}
/** Implementation of DrawGraphics to delegate to some Graphics.
* It optimizes the drawing by joining together the pieces of
* the text drawn with the same font and fg/bg color.
*/
static final class GraphicsDG extends SimpleDG {
/** Whether debug messages should be displayed */
private static final boolean debug
= Boolean.getBoolean("netbeans.debug.editor.draw.graphics"); // NOI18N
private Graphics graphics;
/** Start of the chars that were not drawn yet. It can be -1
* to indicate the buffered characters were just flushed.
*/
private int startOffset = -1;
/** End of the chars that were not drawn yet */
private int endOffset;
/** X coordinate where the drawing of chars should occur */
private int startX;
/** Y coordinate where the drawing of chars should occur */
private int startY;
private int width;
private Color strikeThroughColor;
private Color underlineColor;
private Color waveUnderlineColor;
/** Whether annotations were drawn on the current line already */
private int lastDrawnAnnosY;
private int lastDrawnAnnosX;
/** Annotation description cached for the lastDrawnAnnosY */
private AnnotationDesc[] passiveAnnosAtY;
/** Alpha used for drawing the glyphs on the background */
private AlphaComposite alpha = null;
/** Access to annotations for this document which will be
* drawn on the background */
private Annotations annos = null;
private boolean drawTextLimitLine;
private int textLimitWidth;
private int defaultSpaceWidth;
private Color textLimitLineColor;
private int absoluteX;
private int maxWidth;
private View view;
GraphicsDG(Graphics graphics) {
this.graphics = graphics;
// #33165 - set invalid y initially
this.y = -1;
}
public void setForeColor(Color foreColor) {
if (!foreColor.equals(this.foreColor)) {
flush();
this.foreColor = foreColor;
}
}
public void setBackColor(Color backColor) {
if (!backColor.equals(this.backColor)) {
flush();
this.backColor = backColor;
}
}
public void setStrikeThroughColor(Color strikeThroughColor) {
if ((strikeThroughColor != this.strikeThroughColor)
&& (strikeThroughColor == null
|| !strikeThroughColor.equals(this.strikeThroughColor))
) {
flush();
this.strikeThroughColor = strikeThroughColor;
}
}
public void setUnderlineColor(Color underlineColor) {
if ((underlineColor != this.underlineColor)
&& (underlineColor == null
|| !underlineColor.equals(this.underlineColor))
) {
flush();
this.underlineColor = underlineColor;
}
}
public void setWaveUnderlineColor(Color waveUnderlineColor) {
if ((waveUnderlineColor != this.waveUnderlineColor)
&& (waveUnderlineColor == null
|| !waveUnderlineColor.equals(this.waveUnderlineColor))
) {
flush();
this.waveUnderlineColor = waveUnderlineColor;
}
}
public void setFont(Font font) {
if (!font.equals(this.font)) {
flush();
this.font = font;
}
}
public void setX(int x) {
if (x != this.x) {
flush();
this.x = x;
}
}
public void setY(int y) {
if (y != this.y) {
flush();
this.y = y;
}
}
public void init(DrawContext ctx) {
JTextComponent c = ctx.getEditorUI().getComponent();
// initialize reference to annotations
annos = ctx.getEditorUI().getDocument().getAnnotations();
drawTextLimitLine = ctx.getEditorUI().textLimitLineVisible;
textLimitWidth = ctx.getEditorUI().textLimitWidth;
defaultSpaceWidth = ctx.getEditorUI().defaultSpaceWidth;
textLimitLineColor = ctx.getEditorUI().textLimitLineColor;
absoluteX = ctx.getEditorUI().getTextMargin().left;
maxWidth = ctx.getEditorUI().getExtentBounds().width;
}
public void finish() {
flush();
}
public void setView(View view){
this.view = view;
}
private void flush() {
flush(false);
}
private void flush(boolean atEOL) {
if (y < 0) { // not yet initialized
return ;
}
if (startOffset >= 0 && startOffset != endOffset) { // some text on the line
// First possibly fill the rectangle
fillRectImpl(startX, startY, x - startX);
}
// #33165 - for each fragment getPasiveAnnotations() was called
// but it can done just once per line.
if (lastDrawnAnnosY != y) {
lastDrawnAnnosY = y;
lastDrawnAnnosX = 0;
if (AnnotationTypes.getTypes().isBackgroundDrawing().booleanValue()) {
if (alpha == null)
alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, AnnotationTypes.getTypes().getBackgroundGlyphAlpha().intValue() / 100f);
if (view!=null){
passiveAnnosAtY = annos.getPassiveAnnotations(view.getStartOffset());
}
} else {
passiveAnnosAtY = null;
}
}
int glyphX=2;
if (passiveAnnosAtY != null) {
Graphics2D g2d = (Graphics2D) graphics;
Shape shape = graphics.getClip();
// set alpha composite
Composite origin = g2d.getComposite();
g2d.setComposite(alpha);
// clip the drawing area
int endX = atEOL ? Integer.MAX_VALUE : x;
int startX = Math.min(lastDrawnAnnosX, this.startX);
Rectangle r = new Rectangle(startX, y, endX - startX, lineHeight);
lastDrawnAnnosX = endX;
r = r.intersection(shape.getBounds());
graphics.setClip(r);
for (int i=0; i < passiveAnnosAtY.length; i++) {
g2d.drawImage(passiveAnnosAtY[i].getGlyph(), glyphX, y, null);
glyphX += passiveAnnosAtY[i].getGlyph().getWidth(null)+1;
}
// restore original clip region
graphics.setClip(shape);
// restore original ocmposite
g2d.setComposite(origin);
}
// If no text on the line then return
if (startOffset < 0 || startOffset == endOffset) {
startOffset = -1;
return;
}
if (drawTextLimitLine) { // draw limit line
Rectangle clip = graphics.getClipBounds();
int lineX = absoluteX + textLimitWidth * defaultSpaceWidth;
if (lineX >= startX && lineX <= x){
Color bakColor = graphics.getColor();
graphics.setColor(textLimitLineColor);
graphics.drawLine(lineX, startY, lineX, startY + lineHeight);
graphics.setColor(bakColor);
}
}
// Check whether the graphics uses right color
graphics.setColor(foreColor);
// Check whether the graphics uses right font
graphics.setFont(font);
if (debug) {
String text = new String(buffer, startOffset, endOffset - startOffset);
System.out.println("DrawGraphics: text='" + text // NOI18N
+ "', text.length=" + text.length() // NOI18N
+ ", x=" + startX + ", y=" + startY // NOI18N
+ ", ascent=" + lineAscent // NOI18N
+ ", clip=" + graphics.getClipBounds() // NOI18N
+ ", color=" + graphics.getColor() // NOI18N
);
}
graphics.drawChars(buffer, startOffset, endOffset - startOffset,
startX, startY + lineAscent);
if (strikeThroughColor != null) { // draw strike-through
FontMetricsCache.Info fmcInfo = FontMetricsCache.getInfo(font);
graphics.setColor(strikeThroughColor);
graphics.fillRect(startX,
startY + (int)(fmcInfo.getStrikethroughOffset(graphics) + lineAscent + 0.5),
x - startX,
(int)(fmcInfo.getStrikethroughThickness(graphics) + 0.5)
);
}
if (waveUnderlineColor != null) { // draw wave underline
FontMetricsCache.Info fmcInfo = FontMetricsCache.getInfo(font);
graphics.setColor(waveUnderlineColor);
int waveLength = x - startX;
if (waveLength > 0) {
int[] wf = {0, +1, 0, -1};
int[] xArray = new int[waveLength + 1];
int[] yArray = new int[waveLength + 1];
int yBase = (int)(startY + fmcInfo.getUnderlineOffset(graphics) + lineAscent + 1 + 0.5);
for (int i=0;i<=waveLength;i++) {
xArray[i]=startX + i;
yArray[i]=yBase + wf[xArray[i] % 4];
}
graphics.drawPolyline(xArray, yArray, waveLength);
}
}
if (underlineColor != null) { // draw underline
FontMetricsCache.Info fmcInfo = FontMetricsCache.getInfo(font);
graphics.setColor(underlineColor);
// Add one pixel to the underline offset
graphics.fillRect(startX,
startY + (int)(fmcInfo.getUnderlineOffset(graphics) + lineAscent + 1.5),
x - startX,
(int)(fmcInfo.getUnderlineThickness(graphics) + 0.5)
);
}
startOffset = -1; // signal no characters to draw
}
public Graphics getGraphics() {
return graphics;
}
public boolean supportsLineNumbers() {
return true;
}
public void fillRect(int width) {
fillRectImpl(x, y, width);
x += width;
}
private void fillRectImpl(int rx, int ry, int width) {
if (width > 0) { // only for non-zero width
// only fill for different color than current background
if (!backColor.equals(defaultBackColor)) {
graphics.setColor(backColor);
graphics.fillRect(rx, ry, width, lineHeight);
}
}
}
public void drawChars(int offset, int length, int width) {
if (length >= 0) {
if (startOffset < 0) { // no token yet
startOffset = offset;
endOffset = offset + length;
this.startX = x;
this.startY = y;
this.width = width;
} else { // already token before
endOffset += length;
}
}
x += width;
}
public void drawTabs(int offset, int length, int spaceCount, int width) {
if (width > 0) {
flush();
fillRectImpl(x, y, width);
x += width;
}
}
public void setBuffer(char[] buffer) {
flush();
this.buffer = buffer;
startOffset = -1;
}
public void eol() {
if (drawTextLimitLine) { // draw limit line
int lineX = absoluteX + textLimitWidth * defaultSpaceWidth;
if (lineX >= x-defaultSpaceWidth){
Color bakColor = graphics.getColor();
graphics.setColor(textLimitLineColor);
Rectangle clipB = graphics.getClipBounds();
if (clipB.width + clipB.x <= lineX){
graphics.setClip(clipB.x, clipB.y, maxWidth, clipB.height);
graphics.drawLine(lineX, y, lineX, y + lineHeight);
graphics.setClip(clipB.x, clipB.y, clipB.width, clipB.height);
}else{
graphics.drawLine(lineX, y, lineX, y + lineHeight);
}
graphics.setColor(bakColor);
}
}
flush(true);
}
}
static final class PrintDG extends SimpleDG {
PrintContainer container;
/** Whether there were some paints already on the line */
boolean lineInited;
/** Construct the new print graphics
* @param container print container to which the tokens
* are added.
*/
public PrintDG(PrintContainer container) {
this.container = container;
}
public boolean supportsLineNumbers() {
return true;
}
public void drawChars(int offset, int length, int width) {
if (length > 0) {
lineInited = true; // Fixed 42536
char[] chars = new char[length];
System.arraycopy(buffer, offset, chars, 0, length);
container.add(chars, font, foreColor, backColor);
}
}
private void printSpaces(int spaceCount) {
char[] chars = new char[spaceCount];
System.arraycopy(Analyzer.getSpacesBuffer(spaceCount), 0, chars, 0, spaceCount);
container.add(chars, font, foreColor, backColor);
}
public void drawTabs(int offset, int length, int spaceCount, int width) {
lineInited = true; // Fixed 42536
printSpaces(spaceCount);
}
public void eol() {
if (!lineInited && container.initEmptyLines()) {
printSpaces(1);
}
container.eol();
lineInited = false; // signal that the next line is not inited yet
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy