0001: /*
0002: * Sun Public License Notice
0003: *
0004: * The contents of this file are subject to the Sun Public License
0005: * Version 1.0 (the "License"). You may not use this file except in
0006: * compliance with the License. A copy of the License is available at
0007: * http://www.sun.com/
0008: *
0009: * The Original Code is NetBeans. The Initial Developer of the Original
0010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
0011: * Microsystems, Inc. All Rights Reserved.
0012: */
0013:
0014: package org.netbeans.editor;
0015:
0016: import java.awt.Color;
0017: import java.awt.Font;
0018: import java.awt.FontMetrics;
0019: import java.awt.Graphics;
0020: import java.awt.Graphics2D;
0021: import java.awt.Insets;
0022: import java.awt.Rectangle;
0023:
0024: import javax.swing.text.BadLocationException;
0025: import javax.swing.text.JTextComponent;
0026:
0027: /**
0028: * Class responsible for drawing the editor component.
0029: *
0030: * @author Miloslav Metelka
0031: * @version 1.00
0032: */
0033: class DrawEngine {
0034:
0035: /** Whether debug messages should be displayed */
0036: private static final boolean debug = Boolean
0037: .getBoolean("netbeans.debug.editor.draw"); // NOI18N
0038: /** Whether debug messages for each token fragment should be displayed */
0039: private static final boolean debugFragment = Boolean
0040: .getBoolean("netbeans.debug.editor.draw.fragment"); // NOI18N
0041:
0042: /** Initial size of mark array in <CODE>DrawMarkRenderer</CODE>. */
0043: private static final int DEFAULT_DRAW_MARK_RENDERER_SIZE = 20;
0044:
0045: /** Only one instance of draw-engine */
0046: private static DrawEngine drawEngine;
0047:
0048: private static final char[] SPACE = new char[] { ' ' };
0049:
0050: /** Prevent creation */
0051: private DrawEngine() {
0052: }
0053:
0054: /** Get the static instance of draw-engine */
0055: public static DrawEngine getDrawEngine() {
0056: if (drawEngine == null) {
0057: drawEngine = new DrawEngine();
0058: }
0059: return drawEngine;
0060: }
0061:
0062: private void initLineNumbering(DrawInfo ctx) {
0063: // Resolve whether line numbers will be painted
0064: ctx.lineNumbering = ctx.editorUI.lineNumberVisible
0065: && ctx.drawGraphics.supportsLineNumbers();
0066:
0067: // create buffer for showing line numbers
0068: if (ctx.lineNumbering) {
0069: try {
0070: ctx.startLineNumber = Utilities.getLineOffset(ctx.doc,
0071: ctx.startOffset) + 1;
0072: } catch (BadLocationException e) {
0073: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0074: e.printStackTrace();
0075: }
0076: }
0077:
0078: ctx.lineNumberColoring = ctx.editorUI
0079: .getColoring(SettingsNames.LINE_NUMBER_COLORING);
0080: if (ctx.lineNumberColoring == null) {
0081: ctx.lineNumberColoring = ctx.defaultColoring; // no number
0082: // coloring
0083: // found
0084:
0085: } else { // lineNumberColoring not null
0086: ctx.lineNumberColoring = ctx.lineNumberColoring
0087: .apply(ctx.defaultColoring);
0088: }
0089:
0090: Font lnFont = ctx.lineNumberColoring.getFont();
0091: if (lnFont == null) {
0092: lnFont = ctx.defaultColoring.getFont();
0093: }
0094:
0095: Color lnBackColor = ctx.lineNumberColoring.getBackColor();
0096: if (lnBackColor == null) {
0097: lnBackColor = ctx.defaultColoring.getBackColor();
0098: }
0099:
0100: Color lnForeColor = ctx.lineNumberColoring.getForeColor();
0101: if (lnForeColor == null) {
0102: lnForeColor = ctx.defaultColoring.getForeColor();
0103: }
0104:
0105: ctx.lineNumberChars = new char[Math.max(
0106: ctx.editorUI.lineNumberMaxDigitCount, 1)];
0107: if (ctx.graphics == null) {
0108: ctx.syncedLineNumbering = true;
0109:
0110: } else { // non-synced line numbering - need to remember line start
0111: // offsets
0112: try {
0113: int endLineNumber = Utilities.getLineOffset(
0114: ctx.doc, ctx.endOffset) + 1;
0115: ctx.lineStartOffsets = new int[endLineNumber
0116: - ctx.startLineNumber + 2]; // reserve
0117: // more
0118: } catch (BadLocationException e) {
0119: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0120: e.printStackTrace();
0121: }
0122: }
0123: }
0124: }
0125: }
0126:
0127: private void initInfo(DrawInfo ctx) throws BadLocationException {
0128: ctx.x = ctx.startX;
0129: ctx.y = ctx.startY;
0130: ctx.lineHeight = ctx.editorUI.getLineHeight();
0131: ctx.defaultColoring = ctx.editorUI.getDefaultColoring();
0132: ctx.tabSize = ctx.doc.getTabSize();
0133: ctx.fragmentOffset = ctx.startOffset; // actual painting position
0134: ctx.graphics = ctx.drawGraphics.getGraphics();
0135:
0136: if (ctx.graphics != null) {
0137: if (ctx.editorUI.renderingHints != null) {
0138: ((Graphics2D) ctx.graphics)
0139: .setRenderingHints(ctx.editorUI.renderingHints);
0140: }
0141:
0142: if (ctx.editorUI.textLimitLineVisible) { // draw limit line
0143: int lineX = ctx.startX + ctx.editorUI.textLimitWidth
0144: * ctx.editorUI.defaultSpaceWidth;
0145: ctx.graphics.setColor(ctx.editorUI.textLimitLineColor);
0146: Rectangle clip = ctx.graphics.getClipBounds();
0147: // ctx.graphics.drawRect(lineX, clip.y, 1, clip.height);
0148: ctx.graphics.drawLine(lineX, clip.y, lineX, clip.y
0149: + clip.height);
0150: }
0151: }
0152:
0153: initLineNumbering(ctx);
0154:
0155: // Initialize draw context
0156: ctx.foreColor = ctx.defaultColoring.getForeColor();
0157: ctx.backColor = ctx.defaultColoring.getBackColor();
0158: ctx.font = ctx.defaultColoring.getFont();
0159: ctx.bol = true; // draw must always start at line begin
0160:
0161: // Init draw graphics
0162: ctx.drawGraphics.init(ctx);
0163: ctx.drawGraphics.setDefaultBackColor(ctx.defaultColoring
0164: .getBackColor());
0165: ctx.drawGraphics.setLineHeight(ctx.lineHeight);
0166: ctx.drawGraphics.setLineAscent(ctx.editorUI.getLineAscent());
0167: ctx.drawGraphics.setX(ctx.x);
0168: ctx.drawGraphics.setY(ctx.y);
0169:
0170: // Init all draw-layers
0171: ctx.layers = ctx.editorUI.getDrawLayerList().currentLayers();
0172: int layersLength = ctx.layers.length;
0173: ctx.layerActives = new boolean[layersLength];
0174: ctx.layerActivityChangeOffsets = new int[layersLength];
0175:
0176: for (int i = 0; i < layersLength; i++) {
0177: ctx.layers[i].init(ctx); // init all layers
0178: }
0179:
0180: // Get all the draw marks in draw area through renderer
0181: ctx.doc.op.renderMarks(ctx); // no synch needed
0182:
0183: // Get current draw mark
0184: ctx.drawMarkOffset = Integer.MAX_VALUE;
0185: if (ctx.rangeMarkCount > 0) {
0186: ctx.drawMark = ctx.rangeDrawMarkArray[ctx.drawMarkIndex];
0187: ctx.drawMarkOffset = ctx.rangeOffsetArray[ctx.drawMarkIndex++];
0188: if (ctx.drawMarkOffset < ctx.updateOffset) {
0189: ctx.updateOffset = ctx.drawMarkOffset;
0190: ctx.drawMarkUpdate = true;
0191: }
0192: }
0193:
0194: // Prepare syntax scanner and then cycle through all the syntax segments
0195: ctx.doc.op.prepareSyntax(ctx.slot, ctx.syntax, ctx.doc.op
0196: .getLeftSyntaxMark(ctx.startOffset), ctx.startOffset,
0197: ctx.endTokenMarkOffset - ctx.startOffset, false);
0198:
0199: ctx.slotArray = ctx.slot.array;
0200: ctx.buffer = ctx.slotArray;
0201: ctx.bufferStartOffset = ctx.startOffset
0202: - ctx.syntax.getOffset();
0203: ctx.drawGraphics.setBuffer(ctx.slotArray);
0204:
0205: ctx.continueDraw = true;
0206: }
0207:
0208: private void handleBOL(DrawInfo ctx) {
0209: if (ctx.lineNumbering) {
0210: if (ctx.syncedLineNumbering) {
0211: // Draw line numbers synchronously at begining of each line
0212:
0213: // Init context
0214: ctx.foreColor = ctx.lineNumberColoring.getForeColor();
0215: ctx.backColor = ctx.lineNumberColoring.getBackColor();
0216: ctx.font = ctx.lineNumberColoring.getFont();
0217: ctx.strikeThroughColor = null;
0218: ctx.underlineColor = null;
0219:
0220: int lineNumber = ctx.startLineNumber + ctx.lineIndex;
0221: // Update line-number by layers
0222: int layersLength = ctx.layers.length;
0223: for (int i = 0; i < layersLength; i++) {
0224: lineNumber = ctx.layers[i].updateLineNumberContext(
0225: lineNumber, ctx);
0226: }
0227:
0228: // Fill the buffer with digit chars
0229: int i = Math.max(ctx.lineNumberChars.length - 1, 0);
0230: do {
0231: ctx.lineNumberChars[i--] = (char) ('0' + (lineNumber % 10));
0232: lineNumber /= 10;
0233: } while (lineNumber != 0 && i >= 0);
0234:
0235: // Fill the rest with spaces
0236: while (i >= 0) {
0237: ctx.lineNumberChars[i--] = ' ';
0238: }
0239:
0240: // Fill the DG's attributes and draw
0241: int numX = ctx.x - ctx.editorUI.lineNumberWidth;
0242: if (ctx.editorUI.lineNumberMargin != null) {
0243: numX += ctx.editorUI.lineNumberMargin.left;
0244: }
0245: ctx.drawGraphics.setX(numX);
0246:
0247: ctx.drawGraphics.setBuffer(ctx.lineNumberChars);
0248: ctx.drawGraphics.setForeColor(ctx.foreColor);
0249: ctx.drawGraphics.setBackColor(ctx.backColor);
0250: ctx.drawGraphics.setFont(ctx.font);
0251: ctx.drawGraphics.drawChars(0,
0252: ctx.lineNumberChars.length,
0253: ctx.editorUI.lineNumberWidth,
0254: ctx.strikeThroughColor, ctx.underlineColor);
0255:
0256: // When printing there should be an additional space between
0257: // line number and the text
0258: if (ctx.drawGraphics.getGraphics() == null) {
0259: ctx.drawGraphics.setBuffer(SPACE);
0260: ctx.drawGraphics.drawChars(0, 1,
0261: ctx.editorUI.lineNumberDigitWidth, null,
0262: null);
0263: }
0264:
0265: ctx.drawGraphics.setX(ctx.x);
0266: ctx.drawGraphics.setBuffer(ctx.slotArray);
0267:
0268: } else { // non-synced line numbering
0269: ctx.lineStartOffsets[ctx.lineIndex] = ctx.fragmentOffset; // store
0270: // the
0271: // line
0272: // number
0273: }
0274: }
0275:
0276: ctx.lineIndex++;
0277: }
0278:
0279: /** Handle the end-of-line */
0280: private void handleEOL(DrawInfo ctx) {
0281: ctx.drawGraphics.eol(); // sign EOL to DG
0282: ctx.widestWidth = Math.max(ctx.widestWidth, ctx.x); // update widest
0283: // width
0284: ctx.visualColumn = 0;
0285: ctx.x = ctx.startX;
0286: ctx.y += ctx.lineHeight;
0287:
0288: ctx.drawGraphics.setX(ctx.x);
0289: ctx.drawGraphics.setY(ctx.y);
0290:
0291: }
0292:
0293: /**
0294: * Called when the current fragment starts at the offset that corresponds to
0295: * the update-offset.
0296: */
0297: private void updateOffsetReached(DrawInfo ctx) {
0298: if (ctx.drawMarkUpdate) { // update because of draw mark
0299: // means no-mark update yet performed
0300: int layersLength = ctx.layers.length;
0301: for (int i = 0; i < layersLength; i++) {
0302: DrawLayer l = ctx.layers[i];
0303: if (l.getName().equals(ctx.drawMark.layerName)
0304: && (ctx.drawMark.isDocumentMark() || ctx.editorUI == ctx.drawMark
0305: .getEditorUI())) {
0306: ctx.layerActives[i] = l.isActive(ctx, ctx.drawMark);
0307: int naco = l.getNextActivityChangeOffset(ctx);
0308: ctx.layerActivityChangeOffsets[i] = naco;
0309: if (naco > ctx.fragmentOffset
0310: && naco < ctx.layerUpdateOffset) {
0311: ctx.layerUpdateOffset = naco;
0312: }
0313: }
0314: }
0315:
0316: // Get next mark
0317: if (ctx.drawMarkIndex < ctx.rangeMarkCount) {
0318: ctx.drawMark = ctx.rangeDrawMarkArray[ctx.drawMarkIndex];
0319: ctx.drawMarkOffset = ctx.rangeOffsetArray[ctx.drawMarkIndex++];
0320:
0321: } else { // no more draw marks
0322: ctx.drawMark = null;
0323: ctx.drawMarkOffset = Integer.MAX_VALUE;
0324: }
0325:
0326: } else { // update because activity-change-offset set in some layer
0327: ctx.layerUpdateOffset = Integer.MAX_VALUE;
0328: int layersLength = ctx.layers.length;
0329: for (int i = 0; i < layersLength; i++) {
0330: // Update only layers with the same offset as fragmentOffset
0331: int naco = ctx.layerActivityChangeOffsets[i];
0332: if (naco == ctx.fragmentOffset) {
0333: DrawLayer l = ctx.layers[i];
0334: ctx.layerActives[i] = l.isActive(ctx, null);
0335: naco = l.getNextActivityChangeOffset(ctx);
0336: ctx.layerActivityChangeOffsets[i] = naco;
0337: }
0338:
0339: if (naco > ctx.fragmentOffset
0340: && naco < ctx.layerUpdateOffset) {
0341: ctx.layerUpdateOffset = naco;
0342: }
0343: }
0344: }
0345:
0346: // Check next update position
0347: if (ctx.drawMarkOffset < ctx.layerUpdateOffset) {
0348: ctx.drawMarkUpdate = true;
0349: ctx.updateOffset = ctx.drawMarkOffset;
0350:
0351: } else {
0352: ctx.drawMarkUpdate = false;
0353: ctx.updateOffset = ctx.layerUpdateOffset;
0354: }
0355:
0356: }
0357:
0358: /** Compute the length of the fragment. */
0359: private void computeFragmentLength(DrawInfo ctx) {
0360: // Compute initial fragment (of token) length
0361: ctx.fragmentStartIndex = ctx.fragmentOffset
0362: - ctx.bufferStartOffset;
0363: ctx.fragmentLength = Math
0364: .min(ctx.updateOffset - ctx.fragmentOffset,
0365: ctx.tokenLength - ctx.drawnLength);
0366:
0367: // Find first TAB or LF
0368: int stopIndex = Analyzer.findFirstTabOrLF(ctx.slotArray,
0369: ctx.fragmentStartIndex, ctx.fragmentLength);
0370:
0371: // There must be extra EOL at the end of the document
0372: ctx.eol = (ctx.fragmentOffset == ctx.docLen);
0373: ctx.tabsFragment = false;
0374:
0375: // Check whether there are no tabs in the fragment and possibly shrink
0376: // Get the first offset of the tab character or -1 if no tabs in
0377: // fragment
0378: if (stopIndex >= 0) { // either '\t' or '\n' found
0379: if (stopIndex == ctx.fragmentStartIndex) { // since fragment start
0380: if (ctx.slotArray[stopIndex] == '\t') { //
0381: ctx.tabsFragment = true;
0382: // Find first non-tab char
0383: int ntInd = Analyzer.findFirstNonTab(ctx.slotArray,
0384: ctx.fragmentStartIndex, ctx.fragmentLength);
0385:
0386: if (ntInd != -1) { // not whole fragment are tabs
0387: ctx.fragmentLength = ntInd
0388: - ctx.fragmentStartIndex;
0389: }
0390:
0391: } else { // '\n' found
0392: ctx.eol = true;
0393: ctx.fragmentLength = 1; // only one EOL in fragment
0394: }
0395:
0396: } else { // inside fragment start
0397: ctx.fragmentLength = stopIndex - ctx.fragmentStartIndex; // shrink
0398: // fragment
0399: // size
0400: }
0401: }
0402: }
0403:
0404: /** Compute the display width of the fragment */
0405: private void computeFragmentDisplayWidth(DrawInfo ctx) {
0406: // First go through all layers to update draw context
0407: // to get up-to-date fonts and colors
0408: if (!ctx.eol) { // handled later
0409: int layersLength = ctx.layers.length;
0410: for (int i = 0; i < layersLength; i++) {
0411: DrawLayer l = ctx.layers[i];
0412: if (ctx.layerActives[i]) {
0413: ctx.layers[i].updateContext(ctx); // Let the layer to
0414: // update the context
0415: }
0416: }
0417: }
0418:
0419: // Handle possible white space expansion and compute display width
0420: FontMetricsCache.Info fmcInfo = FontMetricsCache
0421: .getInfo(ctx.font);
0422: ctx.spaceWidth = (ctx.component != null) ? fmcInfo
0423: .getSpaceWidth(ctx.component)
0424: : ctx.editorUI.defaultSpaceWidth;
0425:
0426: // Compute real count of chars in fragment - can differ if tabs
0427: ctx.fragmentCharCount = ctx.fragmentLength;
0428: if (ctx.tabsFragment) { // tabs in fragment
0429: ctx.fragmentCharCount = Analyzer.getColumn(ctx.slotArray,
0430: ctx.fragmentStartIndex, ctx.fragmentLength,
0431: ctx.tabSize, ctx.visualColumn)
0432: - ctx.visualColumn;
0433: ctx.fragmentWidth = ctx.fragmentCharCount * ctx.spaceWidth;
0434:
0435: } else if (ctx.eol) { // EOL will have the spaceWidth
0436: ctx.fragmentWidth = ctx.spaceWidth;
0437:
0438: } else { // regular fragment
0439: if (ctx.fragmentLength > 0) {
0440: if (ctx.component != null) {
0441: ctx.fragmentWidth = FontMetricsCache
0442: .getFontMetrics(ctx.font, ctx.component)
0443: .charsWidth(ctx.slotArray,
0444: ctx.fragmentStartIndex,
0445: ctx.fragmentLength);
0446:
0447: } else { // non-valid component
0448: ctx.fragmentWidth = ctx.fragmentLength
0449: * ctx.spaceWidth;
0450: }
0451:
0452: } else {
0453: ctx.fragmentWidth = 0; // empty fragment
0454: }
0455: }
0456:
0457: }
0458:
0459: /**
0460: * Draw the fragment. Handle the EOL in special way as it needs to care
0461: * about the empty-lines.
0462: */
0463: private void drawFragment(DrawInfo ctx) {
0464: if (ctx.eol) { // special handling for EOL
0465: int layersLength = ctx.layers.length;
0466: boolean emptyLine = false;
0467: int blankWidth = ctx.fragmentWidth;
0468:
0469: /**
0470: * Need to do one or two cycles. In the first pass the check is
0471: * performed whether the line is empty. If so all the layers that
0472: * extend the empty line are called to update the context and the
0473: * resulting half-space is drawn. In the second pass all the layers
0474: * that extend EOL are called to update the context and the
0475: * resulting whitespace is drawn.
0476: */
0477: do {
0478: blankWidth = 0;
0479: if (ctx.bol) { // empty line found
0480: if (!emptyLine) { // not yet processed
0481: for (int i = 0; i < layersLength; i++) {
0482: if (ctx.layerActives[i]) {
0483: DrawLayer l = ctx.layers[i];
0484: if (l.extendsEmptyLine()) {
0485: emptyLine = true; // for at least one
0486: // layer
0487: l.updateContext(ctx);
0488: }
0489: }
0490: }
0491:
0492: if (emptyLine) { // count only if necessary
0493: blankWidth = ctx.spaceWidth / 2; // display half
0494: // of char
0495: }
0496: } else { // already went through the cycle once for empty
0497: // line
0498: emptyLine = false;
0499: }
0500: }
0501:
0502: if (!emptyLine) { // EOL and currently not servicing empty
0503: // line
0504: boolean extendEOL = false;
0505: for (int i = 0; i < layersLength; i++) {
0506: if (ctx.layerActives[i]) {
0507: DrawLayer l = ctx.layers[i];
0508: if (l.extendsEOL()) {
0509: extendEOL = true; // for at least one layer
0510: l.updateContext(ctx);
0511: }
0512: }
0513: }
0514: if (extendEOL && ctx.component != null) {
0515: blankWidth = ctx.component.getWidth();
0516: }
0517: }
0518:
0519: if (blankWidth > 0) {
0520: ctx.drawGraphics.setBackColor(ctx.backColor);
0521: ctx.drawGraphics.fillRect(blankWidth);
0522: if (emptyLine) {
0523: ctx.x += blankWidth;
0524: }
0525: }
0526: } while (emptyLine);
0527:
0528: } else { // Draw regular fragment
0529:
0530: ctx.drawGraphics.setBackColor(ctx.backColor);
0531: ctx.drawGraphics.setForeColor(ctx.foreColor);
0532: ctx.drawGraphics.setFont(ctx.font);
0533:
0534: if (ctx.tabsFragment) {
0535: ctx.drawGraphics.drawTabs(ctx.fragmentStartIndex,
0536: ctx.fragmentLength, ctx.fragmentCharCount,
0537: ctx.fragmentWidth, ctx.strikeThroughColor,
0538: ctx.underlineColor);
0539:
0540: } else { // non-tabs
0541: ctx.drawGraphics.drawChars(ctx.fragmentStartIndex,
0542: ctx.fragmentLength, ctx.fragmentWidth,
0543: ctx.strikeThroughColor, ctx.underlineColor);
0544: }
0545: }
0546: }
0547:
0548: /** Check whether the target offset was reached. */
0549: private void checkTargetOffsetReached(DrawInfo ctx) {
0550: ctx.continueDraw = true;
0551:
0552: // Check whether at the end of the line
0553: if (ctx.eol
0554: && (ctx.targetOffset == ctx.fragmentOffset || (ctx.targetOffset == -1))) {
0555: /**
0556: * Special case for the emulating the EOL at the end of the document
0557: * The EOL is emulated to process the layers that extend empty-line
0558: * or EOL
0559: */
0560: char ch = '\n';
0561: ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
0562: ctx.fragmentOffset, ch, ctx.x, ctx.spaceWidth, ctx);
0563:
0564: // Check whether targeting all characters
0565: } else if (ctx.targetOffset == -1 && ctx.fragmentLength > 0 // Not sure
0566: // whether
0567: // it's
0568: // necessary
0569: ) { // When targeting all chars
0570: FontMetrics fm = FontMetricsCache.getFontMetrics(ctx.font,
0571: ctx.component);
0572:
0573: // Use binary search to find the right offset
0574: int low = -1;
0575: int high = ctx.fragmentLength - 1;
0576:
0577: // Cache the widths and first check whether past the end of fragment
0578: int lastMid = high;
0579: int lastWidth; // cache
0580: if (ctx.tabsFragment) { // fragment contains tabs
0581: int spaceCount = Analyzer.getColumn(ctx.slotArray,
0582: ctx.fragmentStartIndex, high, ctx.tabSize,
0583: ctx.visualColumn)
0584: - ctx.visualColumn;
0585: lastWidth = spaceCount * ctx.spaceWidth;
0586:
0587: } else { // no tabs inside fragment
0588: lastWidth = fm.charsWidth(ctx.slotArray,
0589: ctx.fragmentStartIndex, high);
0590: }
0591:
0592: int lastWidthP1; // plus one char
0593: if (ctx.tabsFragment) { // fragment contains tabs
0594: int spaceCount = Analyzer.getColumn(ctx.slotArray,
0595: ctx.fragmentStartIndex, ctx.fragmentLength,
0596: ctx.tabSize, ctx.visualColumn)
0597: - ctx.visualColumn;
0598: lastWidthP1 = spaceCount * ctx.spaceWidth;
0599:
0600: } else { // no tabs inside fragment
0601: lastWidthP1 = fm.charsWidth(ctx.slotArray,
0602: ctx.fragmentStartIndex, ctx.fragmentLength);
0603: }
0604:
0605: // Test whether the end of the fragment is accepted
0606: ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
0607: ctx.fragmentOffset + high,
0608: ctx.slotArray[ctx.fragmentStartIndex + high], ctx.x
0609: + lastWidth, lastWidthP1 - lastWidth, ctx);
0610:
0611: if (!ctx.continueDraw) {
0612: // Binary search of the first offset that returns false follows
0613: while (low <= high) {
0614: int mid = (low + high) / 2;
0615:
0616: // Compute width that will be passed as x coordinate
0617: int width = 0;
0618: if (mid == lastMid + 1) { // try to use cached value
0619: width = lastWidthP1;
0620:
0621: } else {
0622: if (ctx.tabsFragment) { // fragment contains tabs
0623: int spaceCount = Analyzer.getColumn(
0624: ctx.slotArray,
0625: ctx.fragmentStartIndex, mid,
0626: ctx.tabSize, ctx.visualColumn)
0627: - ctx.visualColumn;
0628: width = spaceCount * ctx.spaceWidth;
0629:
0630: } else { // no tabs inside fragment
0631: width = fm.charsWidth(ctx.slotArray,
0632: ctx.fragmentStartIndex, mid);
0633: }
0634: }
0635:
0636: // Compute width plus one char and substract the previous
0637: // width
0638: // to get the width of the char
0639: int widthP1 = 0;
0640: if (mid == lastMid - 1) { // try to use cached value
0641: widthP1 = lastWidth;
0642:
0643: } else {
0644: if (ctx.tabsFragment) { // fragment contains tabs
0645: int spaceCount = Analyzer.getColumn(
0646: ctx.slotArray,
0647: ctx.fragmentStartIndex, mid + 1,
0648: ctx.tabSize, ctx.visualColumn)
0649: - ctx.visualColumn;
0650: widthP1 = spaceCount * ctx.spaceWidth;
0651:
0652: } else { // no tabs inside fragment
0653: widthP1 = fm.charsWidth(ctx.slotArray,
0654: ctx.fragmentStartIndex, mid + 1);
0655: }
0656: }
0657:
0658: lastWidth = width;
0659: lastWidthP1 = widthP1;
0660: lastMid = mid;
0661:
0662: ctx.continueDraw = ctx.drawGraphics
0663: .targetOffsetReached(
0664: ctx.fragmentOffset + mid,
0665: ctx.slotArray[ctx.fragmentStartIndex
0666: + mid], ctx.x + width,
0667: widthP1 - width, ctx);
0668:
0669: if (ctx.continueDraw) {
0670: low = mid + 1;
0671: } else {
0672: if (mid > low + 1) {
0673: high = mid; // last that rejected
0674:
0675: } else { // mid = low + 1 -> mid is first rejected
0676: break;
0677: }
0678: }
0679: }
0680: }
0681:
0682: // Check whether target offset is inside current fragment
0683: } else if (ctx.targetOffset < ctx.fragmentOffset
0684: + ctx.fragmentLength
0685: && ctx.fragmentOffset <= ctx.targetOffset) {
0686: int curWidth;
0687: int prevWidth = 0;
0688: int i = (ctx.targetOffset - ctx.fragmentOffset);
0689:
0690: if (i > 0) {
0691: if (ctx.tabsFragment) { // fragment contains tabs
0692: int spaceCount = Analyzer.getColumn(ctx.slotArray,
0693: ctx.fragmentStartIndex, i, ctx.tabSize,
0694: ctx.visualColumn)
0695: - ctx.visualColumn;
0696: prevWidth = spaceCount * ctx.spaceWidth;
0697:
0698: } else { // no tabs inside fragment
0699: prevWidth = FontMetricsCache.getFontMetrics(
0700: ctx.font, ctx.component).charsWidth(
0701: ctx.slotArray, ctx.fragmentStartIndex, i);
0702: }
0703: }
0704:
0705: if (ctx.tabsFragment) { // fragment contains tabs
0706: int spaceCount = Analyzer.getColumn(ctx.slotArray,
0707: ctx.fragmentStartIndex, i + 1, ctx.tabSize,
0708: ctx.visualColumn)
0709: - ctx.visualColumn;
0710: curWidth = spaceCount * ctx.spaceWidth;
0711:
0712: } else { // no tabs inside fragment
0713: curWidth = FontMetricsCache.getFontMetrics(ctx.font,
0714: ctx.component).charsWidth(ctx.slotArray,
0715: ctx.fragmentStartIndex, i + 1);
0716: }
0717:
0718: ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
0719: ctx.fragmentOffset + i,
0720: ctx.slotArray[ctx.fragmentStartIndex + i], ctx.x
0721: + prevWidth, curWidth - prevWidth, ctx);
0722: }
0723: }
0724:
0725: /** Draw current fragment of the token. */
0726: private void drawCurrentTokenFragment(DrawInfo ctx) {
0727: // Fill in the draw context
0728: ctx.foreColor = ctx.defaultColoring.getForeColor();
0729: ctx.backColor = ctx.defaultColoring.getBackColor();
0730: ctx.font = ctx.defaultColoring.getFont();
0731: ctx.strikeThroughColor = null;
0732: ctx.underlineColor = null;
0733:
0734: if (ctx.bol) { // if we are on the line begining
0735: handleBOL(ctx);
0736: }
0737:
0738: // Check for status updates in planes at the begining of this fragment
0739: while (ctx.fragmentOffset == ctx.updateOffset) {
0740: updateOffsetReached(ctx); // while() because can be more marks at
0741: // same pos
0742: }
0743:
0744: // Compute the length of the fragment
0745: computeFragmentLength(ctx);
0746:
0747: // Compute the display width of the fragment
0748: computeFragmentDisplayWidth(ctx);
0749:
0750: // Draw the fragment
0751: drawFragment(ctx);
0752:
0753: if (debugFragment) {
0754: System.err.println("DrawEngine: FRAGMENT='" // NOI18N
0755: + EditorDebug.debugChars(ctx.buffer,
0756: ctx.fragmentStartIndex, ctx.fragmentLength)
0757: + "', at pos=" + ctx.fragmentOffset // NOI18N
0758: + ", bol=" + ctx.bol + ", eol=" + ctx.eol // NOI18N
0759: );
0760: }
0761:
0762: // Check whether target-offset was reached
0763: if (ctx.component != null) {
0764: checkTargetOffsetReached(ctx);
0765: }
0766:
0767: // Move the variables to the next fragment in token
0768: ctx.fragmentOffset += ctx.fragmentLength;
0769: ctx.drawnLength += ctx.fragmentLength;
0770: ctx.visualColumn += ctx.fragmentCharCount;
0771: ctx.x += ctx.fragmentWidth;
0772: ctx.bol = false;
0773:
0774: // Update coordinates at the end of each line
0775: if (ctx.eol) {
0776: handleEOL(ctx);
0777: ctx.bol = true; // now at BOL
0778: }
0779:
0780: if (ctx.fragmentOffset >= ctx.endOffset
0781: && ctx.endOffset < ctx.docLen) {
0782: ctx.continueDraw = false;
0783: }
0784: }
0785:
0786: /**
0787: * Draw one token. This is repeatedly called until all the tokens were
0788: * drawn.
0789: *
0790: * @return true when the drawing of the next token should be done or false
0791: * when the drawing should stop.
0792: */
0793: private void drawCurrentToken(DrawInfo ctx) {
0794: // Get the token
0795: if (ctx.tokenID != null) {
0796: ctx.tokenContextPath = ctx.syntax.getTokenContextPath();
0797: ctx.tokenOffset = ctx.syntax.getTokenOffset()
0798: + ctx.bufferStartOffset;
0799: ctx.tokenLength = ctx.syntax.getTokenLength();
0800:
0801: /*
0802: * Check whether the token isn't totally before the area to be
0803: * drawn. It must cover it at least by one character. For the more
0804: * complicated lexical analyzers it's possible that they return
0805: * several tokens that will cover only the prescan area.
0806: */
0807: if (ctx.tokenOffset + ctx.tokenLength <= ctx.startOffset) {
0808: return;
0809: }
0810:
0811: } else { // end of drawing area
0812: ctx.tokenContextPath = null;
0813: ctx.tokenOffset = ctx.fragmentOffset;
0814: ctx.tokenLength = 0;
0815: }
0816:
0817: // Ask all the contexts first to possibly find out
0818: // the first fragment length
0819: if (ctx.tokenOffset <= ctx.startOffset) {
0820: ctx.layerUpdateOffset = Integer.MAX_VALUE;
0821: int layersLength = ctx.layers.length;
0822: for (int i = 0; i < layersLength; i++) { // update status of all
0823: // layers
0824: DrawLayer l = ctx.layers[i];
0825: ctx.layerActives[i] = l.isActive(ctx, null);
0826:
0827: int naco = l.getNextActivityChangeOffset(ctx);
0828: ctx.layerActivityChangeOffsets[i] = naco;
0829: if (naco > ctx.fragmentOffset
0830: && naco < ctx.layerUpdateOffset) {
0831: ctx.layerUpdateOffset = naco;
0832: }
0833: }
0834: ctx.updateOffset = Math.min(ctx.layerUpdateOffset,
0835: ctx.drawMarkOffset);
0836: }
0837:
0838: ctx.drawnLength = ctx.fragmentOffset - ctx.tokenOffset;
0839: ctx.fragmentLength = 0; // length of current token fragment
0840:
0841: if (debug) {
0842: System.err.println("DrawEngine: TOKEN='" // NOI18N
0843: + EditorDebug.debugChars(ctx.getBuffer(), ctx
0844: .getTokenOffset()
0845: - ctx.getBufferStartOffset(), ctx
0846: .getTokenLength())
0847: + "', tokenID=<"
0848: + (ctx.getTokenID() == null ? "null" : ctx.tokenID
0849: .getName()) // NOI18N
0850: + ">, tcp=" + ctx.getTokenContextPath() // NOI18N
0851: + ", pos=" + ctx.getTokenOffset() // NOI18N
0852: );
0853: }
0854:
0855: // Process all the fragments of one token
0856: do {
0857: drawCurrentTokenFragment(ctx);
0858: } while (ctx.continueDraw && ctx.drawnLength < ctx.tokenLength);
0859:
0860: }
0861:
0862: private void graphicsSpecificUpdates(DrawInfo ctx) {
0863: Rectangle bounds = ctx.editorUI.getExtentBounds();
0864: Rectangle clip = ctx.graphics.getClipBounds();
0865: Insets textMargin = ctx.editorUI.getTextMargin();
0866: int leftMarginWidth = textMargin.left
0867: - ctx.editorUI.lineNumberWidth
0868: - ctx.editorUI.textLeftMarginWidth;
0869:
0870: // Draw line numbers bar and all the line nummbers
0871: if (ctx.lineNumbering && !ctx.syncedLineNumbering) {
0872: Color lnBackColor = ctx.lineNumberColoring.getBackColor();
0873: int numY = ctx.startY;
0874: int lnBarX = bounds.x + leftMarginWidth;
0875: if (!lnBackColor.equals(ctx.defaultColoring.getBackColor())
0876: || bounds.x > 0) {
0877: ctx.graphics.setColor(lnBackColor);
0878: ctx.graphics.fillRect(lnBarX, numY,
0879: ctx.editorUI.lineNumberWidth, ctx.lineIndex
0880: * ctx.lineHeight); // can't
0881: // use
0882: // dg
0883: // because
0884: // of
0885: // height
0886: }
0887:
0888: ctx.drawGraphics.setDefaultBackColor(lnBackColor); // will paint
0889: // into bar
0890:
0891: int lastDigitInd = Math.max(ctx.lineNumberChars.length - 1,
0892: 0);
0893: int numX = lnBarX;
0894: if (ctx.editorUI.lineNumberMargin != null) {
0895: numX += ctx.editorUI.lineNumberMargin.left;
0896: }
0897:
0898: ctx.bol = true; //
0899: for (int j = 0; j < ctx.lineIndex; j++) { // draw all line numbers
0900:
0901: // Init the context
0902: ctx.fragmentOffset = ctx.lineStartOffsets[j];
0903: ctx.foreColor = ctx.lineNumberColoring.getForeColor();
0904: ctx.backColor = lnBackColor;
0905: ctx.font = ctx.lineNumberColoring.getFont();
0906: ctx.strikeThroughColor = null;
0907: ctx.underlineColor = null;
0908:
0909: int lineNumber = ctx.startLineNumber + j;
0910: // Update line-number by layers
0911: int layersLength = ctx.layers.length;
0912: for (int i = 0; i < layersLength; i++) {
0913: lineNumber = ctx.layers[i].updateLineNumberContext(
0914: lineNumber, ctx);
0915: }
0916:
0917: int i = lastDigitInd;
0918: // Fill in the digit chars
0919: do {
0920: ctx.lineNumberChars[i--] = (char) ('0' + (lineNumber % 10));
0921: lineNumber /= 10;
0922: } while (lineNumber != 0 && i >= 0);
0923: // Fill in the spaces
0924: while (i >= 0) {
0925: ctx.lineNumberChars[i--] = ' ';
0926: }
0927:
0928: ctx.drawGraphics.setY(numY);
0929: ctx.drawGraphics.setBuffer(ctx.lineNumberChars);
0930: ctx.drawGraphics.setForeColor(ctx.foreColor);
0931: ctx.drawGraphics.setBackColor(ctx.backColor);
0932: ctx.drawGraphics.setFont(ctx.font);
0933:
0934: ctx.drawGraphics.setX(lnBarX);
0935: ctx.drawGraphics.fillRect(ctx.editorUI.lineNumberWidth);
0936: ctx.drawGraphics.setX(numX);
0937:
0938: ctx.drawGraphics.drawChars(0,
0939: ctx.lineNumberChars.length,
0940: ctx.lineNumberChars.length
0941: * ctx.editorUI.lineNumberDigitWidth,
0942: ctx.strikeThroughColor, ctx.underlineColor);
0943:
0944: ctx.drawGraphics.setBuffer(null); // will do changes in buffer
0945:
0946: numY += ctx.lineHeight;
0947: }
0948: }
0949:
0950: // Clear margins
0951: ctx.graphics.setColor(ctx.defaultColoring.getBackColor());
0952:
0953: /**
0954: * The margin is cleared only in case the line is scrolled horizontally
0955: * and therefore the margin is dirty because of the previous text on the
0956: * line. Otherwise the margin is not cleared. The condition (bounds.x gt
0957: * 0) gives the answer.
0958: */
0959: // Left margin
0960: if (leftMarginWidth > 0 && bounds.x > 0) {
0961: ctx.graphics.fillRect(bounds.x, ctx.startY,
0962: leftMarginWidth, ctx.lineIndex * ctx.lineHeight);
0963: }
0964:
0965: // Text left margin
0966: if (ctx.editorUI.textLeftMarginWidth > 0 && bounds.x > 0) {
0967: ctx.graphics.fillRect(bounds.x + textMargin.left
0968: - ctx.editorUI.textLeftMarginWidth, ctx.startY,
0969: ctx.editorUI.textLeftMarginWidth, ctx.lineIndex
0970: * ctx.lineHeight);
0971: }
0972:
0973: // Right margin
0974: if (textMargin.right > 0) {
0975: ctx.graphics.fillRect(bounds.x + bounds.width
0976: - textMargin.right, ctx.startY, textMargin.right,
0977: ctx.lineIndex * ctx.lineHeight);
0978: }
0979:
0980: // Top margin
0981: /**
0982: * The margin is cleared only in case the extent bounds are high enough
0983: * so that the margin could be dirty because of drawing too much to the
0984: * right.
0985: */
0986: if (textMargin.top > 0 && clip.y < bounds.y + textMargin.top) {
0987: ctx.graphics.fillRect(bounds.x, bounds.y, bounds.width,
0988: textMargin.top);
0989: }
0990:
0991: // Bottom margin
0992: int bY = bounds.y + bounds.height - textMargin.bottom;
0993: if (textMargin.bottom > 0 && clip.y + clip.height > bY) {
0994: ctx.graphics.fillRect(bounds.x, bY, bounds.width,
0995: textMargin.bottom);
0996: }
0997: }
0998:
0999: /**
1000: * Draw on the specified area.
1001: *
1002: * @param drawGraphics
1003: * draw graphics through which the drawing is done
1004: * @param editorUI
1005: * extended UI to use
1006: * @param startOffset
1007: * position from which the drawing starts. It must BOL of the
1008: * first line to be drawn.
1009: * @param endOffset
1010: * position where the drawing stops. It must be EOL of the last
1011: * line to be drawn.
1012: * @param startX
1013: * x-coordinate at which the drawing starts
1014: * @param startY
1015: * x-coordinate at which the drawing starts
1016: * @param targetOffset
1017: * position where the targetOffsetReached() method of
1018: * drawGraphics is called. This is useful for caret update or
1019: * modelToView. The Integer.MAX_VALUE can be passed to ignore
1020: * that behavior. The -1 value has special meaning there so that
1021: * it calls targetOffsetReached() after each character processed.
1022: * This is used by viewToModel to find the position for some
1023: * point.
1024: */
1025: void draw(DrawGraphics drawGraphics, EditorUI editorUI,
1026: int startOffset, int endOffset, int startX, int startY,
1027: int targetOffset) throws BadLocationException {
1028: // Some correctness tests at the begining
1029: if (startOffset < 0 || endOffset < 0 || startOffset > endOffset
1030: || startX < 0 || startY < 0) {
1031: return;
1032: }
1033:
1034: if (debug) {
1035: System.err
1036: .println("DrawEngine:------------------ DRAWING ------------------------"); // NOI18N
1037: }
1038:
1039: // Draw-context and other info
1040: DrawInfo ctx = new DrawInfo();
1041: ctx.drawGraphics = drawGraphics;
1042: ctx.editorUI = editorUI;
1043: ctx.startOffset = startOffset;
1044: ctx.endOffset = endOffset;
1045: ctx.startX = startX;
1046: ctx.startY = startY;
1047: ctx.targetOffset = targetOffset;
1048:
1049: synchronized (editorUI) { // lock operations manipulating draw layer
1050: // chain
1051: ctx.doc = editorUI.getDocument();
1052: if (ctx.doc == null) { // no base-document available
1053: return;
1054: }
1055:
1056: ctx.slot = SyntaxSeg.getFreeSlot();
1057: ctx.syntax = ctx.doc.getFreeSyntax();
1058: ctx.doc.readLock();
1059:
1060: try {
1061: ctx.component = editorUI.getComponent();
1062: ctx.docLen = ctx.doc.getLength();
1063:
1064: if (ctx.startOffset > ctx.docLen
1065: || ctx.endOffset > ctx.docLen) {
1066: return;
1067: }
1068:
1069: /*
1070: * Correct the ending position to be at the begining of the next
1071: * line. The only exception is when the endOffset is equal to
1072: * docLen.
1073: */
1074: if (ctx.endOffset < ctx.docLen) {
1075: ctx.endOffset++;
1076: }
1077:
1078: // Initialize the draw-info
1079: initInfo(ctx);
1080:
1081: // Cycle through all the tokens found in the buffer
1082: do {
1083: ctx.tokenID = ctx.syntax.nextToken();
1084:
1085: // Draw the current token
1086: drawCurrentToken(ctx);
1087:
1088: } while (ctx.continueDraw && ctx.tokenID != null);
1089:
1090: ctx.editorUI.updateVirtualWidth(ctx.widestWidth
1091: + ctx.editorUI.lineNumberWidth + 2
1092: * ctx.editorUI.defaultSpaceWidth);
1093:
1094: // When drawing to graphics, the line numbers and insets will be
1095: // drawn now
1096: if (ctx.graphics != null) {
1097: graphicsSpecificUpdates(ctx);
1098: }
1099:
1100: } finally {
1101: ctx.drawGraphics.setBuffer(null);
1102: ctx.drawGraphics.finish();
1103:
1104: ctx.doc.releaseSyntax(ctx.syntax);
1105: SyntaxSeg.releaseSlot(ctx.slot);
1106: ctx.doc.readUnlock();
1107:
1108: }
1109: } // synchronized on editorUI
1110: }
1111:
1112: class DrawInfo extends DocMarks.Renderer implements DrawContext {
1113:
1114: // DrawContext -----------------------------------------------
1115: /** Current foreground color. */
1116: Color foreColor;
1117:
1118: /** Current background color. */
1119: Color backColor;
1120:
1121: /** Current background color. */
1122: Color underlineColor;
1123:
1124: /** Color of the strike-through line or null. */
1125: Color strikeThroughColor;
1126:
1127: /** Current font. */
1128: Font font;
1129:
1130: /** Starting position of the drawing. */
1131: int startOffset;
1132:
1133: /** Ending position of the drawing. */
1134: int endOffset;
1135:
1136: /** Whether we are currently at the line begining. */
1137: boolean bol;
1138:
1139: /** Whether we are currently at the line end. */
1140: boolean eol;
1141:
1142: /** Editor-UI of the component for which we are drawing. */
1143: EditorUI editorUI;
1144:
1145: /** Buffer from which the chars are being drawn. */
1146: char[] buffer;
1147:
1148: /** Starting poisition of the buffer inside the document. */
1149: int bufferStartOffset;
1150:
1151: /** Token-id of the token being drawn. */
1152: TokenID tokenID;
1153:
1154: /** Token-context-path of the token being drawn. */
1155: TokenContextPath tokenContextPath;
1156:
1157: /** Position of the token in the document */
1158: int tokenOffset;
1159:
1160: /** Length of the token's text. */
1161: int tokenLength;
1162:
1163: /** Position of the fragment of the token being drawn in the document. */
1164: int fragmentOffset;
1165:
1166: /** Length of the fragment of the token in the document. */
1167: int fragmentLength;
1168:
1169: // Other variables ---------------------------------------------
1170:
1171: /** Draw graphics */
1172: DrawGraphics drawGraphics;
1173:
1174: /**
1175: * Target document position for the drawing or -1 if all the positions
1176: * are the potential targets.
1177: */
1178: int targetOffset;
1179:
1180: /** Syntax-segment slot being used by drawing. */
1181: SyntaxSeg.Slot slot;
1182:
1183: /** Char array in the slot. */
1184: char[] slotArray;
1185:
1186: /** Syntax scanning the input. */
1187: Syntax syntax;
1188:
1189: /** Component being painted */
1190: JTextComponent component;
1191:
1192: /** Document of the component. */
1193: BaseDocument doc;
1194:
1195: /** Current length of the document */
1196: int docLen;
1197:
1198: /** Current visual column. */
1199: int visualColumn;
1200:
1201: /** Current x-coordinate. */
1202: int x;
1203:
1204: /** Current y-coordinate. */
1205: int y;
1206:
1207: /** Starting x-coordinate. */
1208: int startX;
1209:
1210: /** Starting y-coordinate. */
1211: int startY;
1212:
1213: /** Height of the line being drawn. */
1214: int lineHeight;
1215:
1216: /** Default coloring of the component. */
1217: Coloring defaultColoring;
1218:
1219: /** Size of the TAB character being drawn. */
1220: int tabSize;
1221:
1222: /** Widest width of the line that was painted. */
1223: int widestWidth;
1224:
1225: /** Whether the draw should continue or not. */
1226: boolean continueDraw;
1227:
1228: /** Line number of the first painted line. */
1229: int startLineNumber;
1230:
1231: /**
1232: * Index of the line being drawn. It is added to the startLineNumber to
1233: * form the resulting line number.
1234: */
1235: int lineIndex;
1236:
1237: /** Array of the start positions of all the lines drawn. */
1238: int[] lineStartOffsets;
1239:
1240: /**
1241: * Characters forming the line-number. It is reused for drawing all the
1242: * lines.
1243: */
1244: char[] lineNumberChars;
1245:
1246: /** Coloring for the line-number. */
1247: Coloring lineNumberColoring;
1248:
1249: /** Graphics object. It can be null when not drawing to the component. */
1250: Graphics graphics;
1251:
1252: /** Whether line-numbers are visible and allowed by the draw-graphics. */
1253: boolean lineNumbering;
1254:
1255: /**
1256: * Whether the line-numbers should be painted after each is painted. By
1257: * default the line-numbers are drawn as one block at the end of the
1258: * drawing.
1259: */
1260: boolean syncedLineNumbering;
1261:
1262: /** Array of the draw-layers to be used in the painting */
1263: DrawLayer[] layers;
1264:
1265: /** Whether the particular layer is currently active or not. */
1266: boolean[] layerActives;
1267:
1268: /**
1269: * Next position where the layer will be asked whether it's active or
1270: * not.
1271: */
1272: int[] layerActivityChangeOffsets;
1273:
1274: /**
1275: * Position where either the next draw-mark is or which matches the
1276: * activity-change-offset of one or more layers.
1277: */
1278: int updateOffset;
1279:
1280: /** Next activity-change-offset of one or more layers. */
1281: int layerUpdateOffset;
1282:
1283: /**
1284: * Update of the layers because of the draw-mark is at the given
1285: * position. False means the update is because the
1286: * activity-change-offset was reached.
1287: */
1288: boolean drawMarkUpdate;
1289:
1290: /** Current mark index in the rangeDrawMarkArray. */
1291: int drawMarkIndex;
1292:
1293: /** Current draw-mark */
1294: MarkFactory.DrawMark drawMark;
1295:
1296: /** Position of the current draw-mark */
1297: int drawMarkOffset;
1298:
1299: /** Length of the current token that was already drawn. */
1300: int drawnLength;
1301:
1302: /** Offset of the fragment starting character in the buffer */
1303: int fragmentStartIndex;
1304:
1305: /** Whether the fragment contains TABs only. */
1306: boolean tabsFragment;
1307:
1308: /** Width of one space character for the current context font. */
1309: int spaceWidth;
1310:
1311: /** Display width of the fragment */
1312: int fragmentWidth;
1313:
1314: /**
1315: * Number of characters that the fragment stands for. It can differ from
1316: * fragmentLength for tabsFragment.
1317: */
1318: int fragmentCharCount;
1319:
1320: /**
1321: * This is the offset of the first mark that follows the ending offset
1322: * of the drawing and in which the prescan is low enough so that it's
1323: * clear that the last token will not reach this offset. It's computed
1324: * by mark renderer.
1325: */
1326: int endTokenMarkOffset;
1327:
1328: // Doc-marks renderer ------------------------------------------
1329:
1330: /** Array of draw-marks that were found in draw area. */
1331: MarkFactory.DrawMark rangeDrawMarkArray[] = new MarkFactory.DrawMark[DEFAULT_DRAW_MARK_RENDERER_SIZE];
1332:
1333: /**
1334: * Array of positions of the draw-marks that were found in the draw
1335: * area.
1336: */
1337: int rangeOffsetArray[] = new int[DEFAULT_DRAW_MARK_RENDERER_SIZE];
1338:
1339: /** Total count of found marks (and also positions) */
1340: int rangeMarkCount;
1341:
1342: public Color getForeColor() {
1343: return foreColor;
1344: }
1345:
1346: public void setForeColor(Color foreColor) {
1347: this .foreColor = foreColor;
1348: }
1349:
1350: public Color getBackColor() {
1351: return backColor;
1352: }
1353:
1354: public void setBackColor(Color backColor) {
1355: this .backColor = backColor;
1356: }
1357:
1358: public Color getUnderlineColor() {
1359: return underlineColor;
1360: }
1361:
1362: public void setUnderlineColor(Color underlineColor) {
1363: this .underlineColor = underlineColor;
1364: }
1365:
1366: public Color getStrikeThroughColor() {
1367: return strikeThroughColor;
1368: }
1369:
1370: public void setStrikeThroughColor(Color strikeThroughColor) {
1371: this .strikeThroughColor = strikeThroughColor;
1372: }
1373:
1374: public Font getFont() {
1375: return font;
1376: }
1377:
1378: public void setFont(Font font) {
1379: this .font = font;
1380: }
1381:
1382: public int getStartOffset() {
1383: return startOffset;
1384: }
1385:
1386: public int getEndOffset() {
1387: return endOffset;
1388: }
1389:
1390: public boolean isBOL() {
1391: return bol;
1392: }
1393:
1394: public boolean isEOL() {
1395: return eol;
1396: }
1397:
1398: public EditorUI getEditorUI() {
1399: return editorUI;
1400: }
1401:
1402: public char[] getBuffer() {
1403: return buffer;
1404: }
1405:
1406: public int getBufferStartOffset() {
1407: return bufferStartOffset;
1408: }
1409:
1410: public TokenID getTokenID() {
1411: return tokenID;
1412: }
1413:
1414: public TokenContextPath getTokenContextPath() {
1415: return tokenContextPath;
1416: }
1417:
1418: public int getTokenOffset() {
1419: return tokenOffset;
1420: }
1421:
1422: public int getTokenLength() {
1423: return tokenLength;
1424: }
1425:
1426: public int getFragmentOffset() {
1427: return fragmentOffset;
1428: }
1429:
1430: public int getFragmentLength() {
1431: return fragmentLength;
1432: }
1433:
1434: /** Render the marks to get all the draw-marks in a given range. */
1435: public void render() {
1436: Mark mark = getMarks().getLeftMark(startOffset, null);
1437: rangeMarkCount = 0;
1438: Mark markArray[] = getMarkArray();
1439: int srcIndex = getMarkIndex(mark);
1440: int pos = getMarkOffset(mark);
1441:
1442: while (pos < startOffset) { // go to next mark
1443: srcIndex = getNextIndex(srcIndex);
1444: if (srcIndex >= getMarkArrayLength()) {
1445: break;
1446: }
1447: mark = markArray[srcIndex];
1448: pos = getMarkOffset(mark);
1449: }
1450:
1451: while (pos <= endOffset) { // will include even end-of-doc marks
1452: if (mark instanceof MarkFactory.DrawMark) {
1453: MarkFactory.DrawMark dm = (MarkFactory.DrawMark) mark;
1454: if (!dm.removeInvalid()) { // remove if not valid
1455: // Check array ranges
1456: if (rangeOffsetArray.length < rangeMarkCount + 1) {
1457: MarkFactory.DrawMark rma[] = new MarkFactory.DrawMark[2 * rangeDrawMarkArray.length];
1458: System.arraycopy(rangeDrawMarkArray, 0,
1459: rma, 0, rangeMarkCount);
1460: rangeDrawMarkArray = rma;
1461:
1462: int rpa[] = new int[rma.length];
1463: System.arraycopy(rangeOffsetArray, 0, rpa,
1464: 0, rangeMarkCount);
1465: rangeOffsetArray = rpa;
1466: }
1467:
1468: rangeDrawMarkArray[rangeMarkCount] = (MarkFactory.DrawMark) mark;
1469: rangeOffsetArray[rangeMarkCount++] = pos;
1470: }
1471: }
1472: srcIndex = getNextIndex(srcIndex);
1473: if (srcIndex < getMarkArrayLength()) {
1474: mark = markArray[srcIndex];
1475: pos = getMarkOffset(mark);
1476: } else {
1477: break;
1478: }
1479: }
1480:
1481: // Compute end token mark offset by searching for syntax-mark
1482: endTokenMarkOffset = docLen;
1483: while (true) {
1484: if (mark instanceof MarkFactory.SyntaxMark) {
1485: MarkFactory.SyntaxMark sm = (MarkFactory.SyntaxMark) mark;
1486: Syntax.StateInfo si = sm.getStateInfo();
1487: if (si == null) {
1488: /*
1489: * This is a situation where there's a long token at the
1490: * end of the area to be drawn. Normally the
1491: * prepareSyntax() should update some area around but if
1492: * the token is long the stateinfo can be empty which is
1493: * this situation. It has no sense to go on because all
1494: * the following state infos are null. So in this case
1495: * the the prepareSyntax() is made for the end of the
1496: * document to be sure all the marks are updated. The
1497: * slot must be (and it should be) empty at this time.
1498: */
1499: try {
1500: doc.op.prepareSyntax(slot, syntax, doc.op
1501: .getLeftSyntaxMark(startOffset),
1502: startOffset, pos - startOffset,
1503: true);
1504: } catch (BadLocationException e) {
1505: if (System
1506: .getProperty("netbeans.debug.exceptions") != null) { // NOI18N
1507: e.printStackTrace();
1508: }
1509: }
1510:
1511: // Info in syntax mark should be non-null now
1512: si = sm.getStateInfo();
1513: }
1514:
1515: if (pos - si.getPreScan() > endOffset) {
1516: endTokenMarkOffset = pos; // Found lower end position
1517: break;
1518: }
1519: }
1520:
1521: // Get next mark or break
1522: srcIndex = getNextIndex(srcIndex);
1523: if (srcIndex < getMarkArrayLength()) {
1524: mark = markArray[srcIndex];
1525: pos = getMarkOffset(mark);
1526:
1527: } else {
1528: break;
1529: }
1530: }
1531:
1532: }
1533:
1534: }
1535:
1536: }
|