alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 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.Graphics2D;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Color;
import java.awt.Component;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Insets;
import java.text.AttributedCharacterIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.Segment;
import javax.swing.text.View;

/**
* Class responsible for drawing the editor component.
*
* @author Miloslav Metelka
* @version 1.00
*/
class DrawEngine {

    /** Whether debug messages should be displayed */
    private static final boolean debug
        = Boolean.getBoolean("netbeans.debug.editor.draw"); // NOI18N
    /** Whether debug messages for each token fragment should be displayed */
    private static final boolean debugFragment
        = Boolean.getBoolean("netbeans.debug.editor.draw.fragment"); // NOI18N

    /** Only one instance of draw-engine */
    private static DrawEngine drawEngine;
    
    private static final char[] SPACE = new char[] { ' ' };

    /** Prevent creation */
    private DrawEngine() {
    }

    /** Get the static instance of draw-engine */
    public static DrawEngine getDrawEngine() {
        if (drawEngine == null) {
            drawEngine = new DrawEngine();
        }
        return drawEngine;
    }
    
    public PreinitializedDrawEngine getDrawEngine(View view,
    DrawGraphics drawGraphics, EditorUI editorUI, int startOffset, int endOffset,
    int startX, int startY, int targetOffset) throws BadLocationException
    {
                  
        // Some correctness tests at the begining
        if (startOffset < 0 || endOffset < 0 || startOffset > endOffset
                || startX < 0 || startY < 0
           ) {
            return null;
        }
                  
        BaseDocument doc = (BaseDocument)((view != null) ? view.getDocument() : editorUI.getDocument());
        PreinitializedDrawEngine preinitializedDrawEngine = new PreinitializedDrawEngine(drawGraphics);
        preinitializedDrawEngine.preinitialize(doc, editorUI, startOffset, endOffset, startX, startY, targetOffset);
        return preinitializedDrawEngine;
    }
    

    private void initLineNumbering(DrawInfo ctx) {
        // Resolve whether line numbers will be painted
        ctx.lineNumbering = ctx.editorUI.lineNumberVisible
                            && ctx.drawGraphics.supportsLineNumbers();

        // create buffer for showing line numbers
        if (ctx.lineNumbering) {
            try {
                ctx.startLineNumber = Utilities.getLineOffset(ctx.doc, ctx.startOffset) + 1;
            } catch (BadLocationException e) {
                Utilities.annotateLoggable(e);
            }

            ctx.lineNumberColoring = ctx.editorUI.getColoring(SettingsNames.LINE_NUMBER_COLORING);
            if (ctx.lineNumberColoring == null) {
                ctx.lineNumberColoring = ctx.defaultColoring; // no number coloring found

            } else { // lineNumberColoring not null
                ctx.lineNumberColoring = ctx.lineNumberColoring.apply(ctx.defaultColoring);
            }

            Font lnFont = ctx.lineNumberColoring.getFont();
            if (lnFont == null) {
                lnFont = ctx.defaultColoring.getFont();
            }

            Color lnBackColor = ctx.lineNumberColoring.getBackColor();
            if (lnBackColor == null) {
                lnBackColor = ctx.defaultColoring.getBackColor();
            }

            Color lnForeColor = ctx.lineNumberColoring.getForeColor();
            if (lnForeColor == null) {
                lnForeColor = ctx.defaultColoring.getForeColor();
            }

            ctx.lineNumberChars = new char[Math.max(ctx.editorUI.lineNumberMaxDigitCount, 1)];
            if (ctx.graphics == null) {
                ctx.syncedLineNumbering = true;

            } else { // non-synced line numbering - need to remember line start offsets
                try {
                    int endLineNumber = Utilities.getLineOffset(ctx.doc, ctx.endOffset) + 1;
                    ctx.lineStartOffsets = new int[endLineNumber - ctx.startLineNumber + 2]; // reserve more
                } catch (BadLocationException e) {
                    Utilities.annotateLoggable(e);
                }
            }
        }
    }

    private void initInfo(DrawInfo ctx) throws BadLocationException {
        ctx.x = ctx.startX;
        ctx.y = ctx.startY;
        ctx.lineHeight = ctx.editorUI.getLineHeight();
        ctx.defaultColoring = ctx.editorUI.getDefaultColoring();
        ctx.tabSize = ctx.doc.getTabSize();
        ctx.fragmentOffset = ctx.startOffset; // actual painting position
        ctx.graphics = ctx.drawGraphics.getGraphics();

        if (ctx.graphics != null) {
            if (ctx.editorUI.renderingHints != null) {
                ((Graphics2D)ctx.graphics).setRenderingHints(ctx.editorUI.renderingHints);
            }
        }

        initLineNumbering(ctx);

        // Initialize draw context
        ctx.foreColor = ctx.defaultColoring.getForeColor();
        ctx.backColor = ctx.defaultColoring.getBackColor();
        ctx.font = ctx.defaultColoring.getFont();
        ctx.bol = true; // draw must always start at line begin

        // Init draw graphics
        ctx.drawGraphics.init(ctx);
        ctx.drawGraphics.setDefaultBackColor(ctx.defaultColoring.getBackColor());
        ctx.drawGraphics.setLineHeight(ctx.lineHeight);
        ctx.drawGraphics.setLineAscent(ctx.editorUI.getLineAscent());
        ctx.drawGraphics.setX(ctx.x);
        ctx.drawGraphics.setY(ctx.y);

        // Init all draw-layers
        ctx.layers = ctx.editorUI.getDrawLayerList().currentLayers();
        int layersLength = ctx.layers.length;
        ctx.layerActives = new boolean[layersLength];
        ctx.layerActivityChangeOffsets = new int[layersLength];

        for (int i = 0; i < layersLength; i++) {
            ctx.layers[i].init(ctx); // init all layers
        }

        ctx.drawMarkList = new ArrayList();
        Map docMarks = ctx.doc.marks;
        MarkVector docMarksStorage = ctx.doc.marksStorage;
        synchronized (docMarks) {
            int offset = ctx.startOffset;
            int low = 0;
            int markCount = docMarksStorage.getMarkCount();
            int high = markCount - 1;

            while (low <= high) {
                int mid = (low + high) >> 1;
                int cmp = docMarksStorage.getMarkOffsetInternal(mid) - offset;

                if (cmp < 0)
                    low = mid + 1;
                else if (cmp > 0)
                    high = mid - 1;
                else { // found
                    while (--mid >= 0
                        && docMarksStorage.getMarkOffsetInternal(mid) == offset
                    ) { }
                    low = mid + 1;
                    break;
                }

            }

            // the low contains first mark to consider
            offset = ctx.endOffset;
            while (low < markCount) {
                MultiMark m = (MultiMark)docMarksStorage.getMark(low);
                if (m.isValid()) {
                    if (m.getOffset() > offset) {
                        break;
                    }

                    Mark mark = (Mark)docMarks.get(m);
                    if (mark == null) {
                        throw new IllegalStateException("No mark for m=" + m); // NOI18N
                    }
                    if (mark instanceof MarkFactory.DrawMark) {
                        ctx.drawMarkList.add(mark);
                    }
                }

                low++;
            }
        }
        
        
        // Get current draw mark
        ctx.drawMarkIndex = 0;
        ctx.drawMarkOffset = Integer.MAX_VALUE;
        if (ctx.drawMarkList.size() > 0) {
            ctx.drawMark = (MarkFactory.DrawMark)ctx.drawMarkList.get(ctx.drawMarkIndex++);
            try {
                ctx.drawMarkOffset = ctx.drawMark.getOffset();
            } catch (InvalidMarkException e) {
                throw new IllegalStateException(e.toString());
            }
            if (ctx.drawMarkOffset < ctx.updateOffset) {
                ctx.updateOffset = ctx.drawMarkOffset;
                ctx.drawMarkUpdate = true;
            }
        }

        // Prepare syntax scanner and then cycle through all the syntax segments
        int endTokenSafeOffset = ctx.doc.getTokenSafeOffset(ctx.endOffset);
        ctx.doc.prepareSyntax(ctx.text, ctx.syntax,
            ctx.startOffset, endTokenSafeOffset - ctx.startOffset,
            false, false
        );

        ctx.textArray = ctx.text.array;
        ctx.buffer = ctx.textArray;
        ctx.bufferStartOffset = ctx.startOffset - ctx.syntax.getOffset();
        ctx.drawGraphics.setBuffer(ctx.textArray);

        ctx.continueDraw = true;
    }

    private void handleBOL(DrawInfo ctx) {
        if (ctx.lineNumbering) {
            if (ctx.syncedLineNumbering) {
                // Draw line numbers synchronously at begining of each line

                // Init context
                ctx.foreColor = ctx.lineNumberColoring.getForeColor();
                ctx.backColor = ctx.lineNumberColoring.getBackColor();
                ctx.font = ctx.lineNumberColoring.getFont();
                ctx.strikeThroughColor = null;
                ctx.underlineColor = null;
                ctx.waveUnderlineColor = null;

                int lineNumber = ctx.startLineNumber + ctx.lineIndex;
                // Update line-number by layers
                int layersLength = ctx.layers.length;
                for (int i = 0; i < layersLength; i++) {
                    lineNumber = ctx.layers[i].updateLineNumberContext(lineNumber, ctx);
                }

                // Fill the buffer with digit chars
                int i = Math.max(ctx.lineNumberChars.length - 1, 0);
                do {
                    ctx.lineNumberChars[i--] = (char)('0' + (lineNumber % 10));
                    lineNumber /= 10;
                } while (lineNumber != 0 && i >= 0);

                // Fill the rest with spaces
                while (i >= 0) {
                    ctx.lineNumberChars[i--] = ' ';
                }

                // Fill the DG's attributes and draw
                int numX = ctx.x - ctx.editorUI.lineNumberWidth;
                if (ctx.editorUI.getLineNumberMargin() != null) {
                    numX += ctx.editorUI.getLineNumberMargin().left;
                }
                ctx.drawGraphics.setX(numX);

                ctx.drawGraphics.setBuffer(ctx.lineNumberChars);
                ctx.drawGraphics.setForeColor(ctx.foreColor);
                ctx.drawGraphics.setBackColor(ctx.backColor);
                ctx.drawGraphics.setStrikeThroughColor(ctx.strikeThroughColor);
                ctx.drawGraphics.setUnderlineColor(ctx.underlineColor);
                ctx.drawGraphics.setWaveUnderlineColor(ctx.waveUnderlineColor);
                ctx.drawGraphics.setFont(ctx.font);
                ctx.drawGraphics.drawChars(0, ctx.lineNumberChars.length,
                    ctx.editorUI.lineNumberWidth);

                // When printing there should be an additional space between
                // line number and the text
                if (ctx.drawGraphics.getGraphics() == null) {
                    ctx.drawGraphics.setBuffer(SPACE);
                    ctx.drawGraphics.drawChars(0, 1,
                        ctx.editorUI.lineNumberDigitWidth);
                }

                ctx.drawGraphics.setX(ctx.x);
                ctx.drawGraphics.setBuffer(ctx.textArray);

            } else { // non-synced line numbering
                ctx.lineStartOffsets[ctx.lineIndex] = ctx.fragmentOffset; // store the line number
            }
        }

        ctx.lineIndex++;
    }


    /** Handle the end-of-line */
    private void handleEOL(DrawInfo ctx) {
        ctx.drawGraphics.setX(ctx.x);
        ctx.drawGraphics.setY(ctx.y);
        
        ctx.drawGraphics.eol(); // sign EOL to DG
        ctx.widestWidth = Math.max(ctx.widestWidth, ctx.x); // update widest width
        ctx.visualColumn = 0;
        ctx.x = ctx.startX;
        ctx.y += ctx.lineHeight;

        ctx.drawGraphics.setX(ctx.x);
        ctx.drawGraphics.setY(ctx.y);

    }


    /** Called when the current fragment starts at the offset that corresponds
    * to the update-offset.
    */
    private void updateOffsetReached(DrawInfo ctx) {
        if (ctx.drawMarkUpdate) { // update because of draw mark
            // means no-mark update yet performed
            int layersLength = ctx.layers.length;
            for (int i = 0; i < layersLength; i++) {
                DrawLayer l = ctx.layers[i];
                if (l.getName().equals(ctx.drawMark.layerName)
                        && (ctx.drawMark.isDocumentMark()
                            || ctx.editorUI == ctx.drawMark.getEditorUI())
                   ) {
                    ctx.layerActives[i] = l.isActive(ctx, ctx.drawMark);
                    int naco = l.getNextActivityChangeOffset(ctx);
                    ctx.layerActivityChangeOffsets[i] = naco;
                    if (naco > ctx.fragmentOffset && naco < ctx.layerUpdateOffset) {
                        ctx.layerUpdateOffset = naco;
                    }
                }
            }

            // Get next mark
            if (ctx.drawMarkIndex < ctx.drawMarkList.size()) {
                ctx.drawMark = (MarkFactory.DrawMark)ctx.drawMarkList.get(ctx.drawMarkIndex++);
                try {
                    ctx.drawMarkOffset = ctx.drawMark.getOffset();
                } catch (InvalidMarkException e) {
                    throw new IllegalStateException(e.toString());
                }

            } else { // no more draw marks
                ctx.drawMark = null;
                ctx.drawMarkOffset = Integer.MAX_VALUE;
            }

        } else { // update because activity-change-offset set in some layer
            ctx.layerUpdateOffset = Integer.MAX_VALUE;
            int layersLength = ctx.layers.length;
            for (int i = 0; i < layersLength; i++) {
                // Update only layers with the same offset as fragmentOffset
                int naco = ctx.layerActivityChangeOffsets[i];
                if (naco == ctx.fragmentOffset) {
                    DrawLayer l = ctx.layers[i];
                    ctx.layerActives[i] = l.isActive(ctx, null);
                    naco = l.getNextActivityChangeOffset(ctx);
                    ctx.layerActivityChangeOffsets[i] = naco;
                }

                if (naco > ctx.fragmentOffset && naco < ctx.layerUpdateOffset) {
                    ctx.layerUpdateOffset = naco;
                }
            }
        }

        // Check next update position
        if (ctx.drawMarkOffset < ctx.layerUpdateOffset) {
            ctx.drawMarkUpdate = true;
            ctx.updateOffset = ctx.drawMarkOffset;

        } else {
            ctx.drawMarkUpdate = false;
            ctx.updateOffset = ctx.layerUpdateOffset;
        }

    }

    /** Compute the length of the fragment. */
    private void computeFragmentLength(DrawInfo ctx) {
        // Compute initial fragment (of token) length
        ctx.fragmentStartIndex = ctx.fragmentOffset - ctx.bufferStartOffset;
        ctx.fragmentLength = Math.min(ctx.updateOffset - ctx.fragmentOffset,
                                      ctx.tokenLength - ctx.drawnLength);

        // Find first TAB or LF
        int stopIndex = Analyzer.findFirstTabOrLF(ctx.textArray,
                        ctx.fragmentStartIndex, ctx.fragmentLength);

        // There must be extra EOL at the end of the document
        ctx.eol = (ctx.fragmentOffset == ctx.docLen);
        ctx.tabsFragment = false;

        // Check whether there are no tabs in the fragment and possibly shrink
        // Get the first offset of the tab character or -1 if no tabs in fragment
        if (stopIndex >= 0) { // either '\t' or '\n' found
            if (stopIndex == ctx.fragmentStartIndex) { // since fragment start
                if (ctx.textArray[stopIndex] == '\t') { //
                    ctx.tabsFragment = true;
                    // Find first non-tab char
                    int ntInd = Analyzer.findFirstNonTab(ctx.textArray, ctx.fragmentStartIndex,
                                                         ctx.fragmentLength);

                    if (ntInd != -1) { // not whole fragment are tabs
                        ctx.fragmentLength = ntInd - ctx.fragmentStartIndex;
                    }


                } else { // '\n' found
                    ctx.eol = true;
                    ctx.fragmentLength = 1; // only one EOL in fragment
                }

            } else { // inside fragment start
                ctx.fragmentLength = stopIndex - ctx.fragmentStartIndex; // shrink fragment size
            }
        }
    }

    /** Compute the display width of the fragment */
    private void computeFragmentDisplayWidth(DrawInfo ctx) {
        // First go through all layers to update draw context
        // to get up-to-date fonts and colors
        if (!ctx.eol) { // handled later
            int layersLength = ctx.layers.length;
            for (int i = 0; i < layersLength; i++) {
                DrawLayer l = ctx.layers[i];
                if (ctx.layerActives[i]) {
                    ctx.layers[i].updateContext(ctx); // Let the layer to update the context
                }
            }
        }

        // Handle possible white space expansion and compute display width
        FontMetricsCache.Info fmcInfo = FontMetricsCache.getInfo(ctx.font);
        ctx.spaceWidth = (ctx.component != null)
            ? fmcInfo.getSpaceWidth(ctx.component) : ctx.editorUI.defaultSpaceWidth;

        // Compute real count of chars in fragment - can differ if tabs
        ctx.fragmentCharCount = ctx.fragmentLength;
        if (ctx.tabsFragment) { // tabs in fragment
            ctx.fragmentCharCount = Analyzer.getColumn(ctx.textArray,
                                    ctx.fragmentStartIndex, ctx.fragmentLength, ctx.tabSize, ctx.visualColumn) - ctx.visualColumn;
            ctx.fragmentWidth = ctx.fragmentCharCount * ctx.spaceWidth;

        } else if (ctx.eol) { // EOL will have the spaceWidth
            ctx.fragmentWidth = ctx.spaceWidth;

        } else { // regular fragment
            if (ctx.fragmentLength > 0) {
                if (ctx.component != null) {
                    ctx.fragmentWidth = FontMetricsCache.getFontMetrics(ctx.font, ctx.component).charsWidth(
                                            ctx.textArray, ctx.fragmentStartIndex, ctx.fragmentLength);

                } else { // non-valid component
                    ctx.fragmentWidth = ctx.fragmentLength * ctx.spaceWidth;
                }

            } else {
                ctx.fragmentWidth = 0; // empty fragment
            }
        }

    }


    /** Draw the fragment. Handle the EOL in special way
    * as it needs to care about the empty-lines.
    */
    private void drawFragment(DrawInfo ctx) {
        if (ctx.eol) { // special handling for EOL
            int layersLength = ctx.layers.length;
            boolean emptyLine = false;
            int blankWidth = ctx.fragmentWidth;

            /** Need to do one or two cycles.
            * In the first pass
            * the check is performed whether the line is empty.
            * If so all the layers that extend the empty line are
            * called to update the context and the resulting half-space
            * is drawn.
            * In the second pass all the layers that extend EOL
            * are called to update the context and the resulting
            * whitespace is drawn.
            */
            do {
                blankWidth = 0;
                if (ctx.bol) { // empty line found
                    if (!emptyLine) { // not yet processed
                        for (int i = 0; i < layersLength; i++) {
                            if (ctx.layerActives[i]) {
                                DrawLayer l = ctx.layers[i];
                                if (l.extendsEmptyLine()) {
                                    emptyLine = true; // for at least one layer
                                    l.updateContext(ctx);
                                }
                            }
                        }

                        if (emptyLine) { // count only if necessary
                            blankWidth = ctx.spaceWidth / 2; // display half of char
                        }
                    } else { // already went through the cycle once for empty line
                        emptyLine = false;
                    }
                }

                if (!emptyLine) { // EOL and currently not servicing empty line
                    boolean extendEOL = false;
                    for (int i = 0; i < layersLength; i++) {
                        if (ctx.layerActives[i]) {
                            DrawLayer l = ctx.layers[i];
                            if (l.extendsEOL()) {
                                extendEOL = true; // for at least one layer
                                l.updateContext(ctx);
                            }
                        }
                    }
                    if (extendEOL && ctx.component != null) {
                        // Disable the underlines and strikethrough for EOL extension
                        ctx.drawGraphics.setStrikeThroughColor(null);
                        ctx.drawGraphics.setUnderlineColor(null);
                        ctx.drawGraphics.setWaveUnderlineColor(null);
                        blankWidth = ctx.component.getWidth();
                    }
                }

                if (blankWidth > 0) {
                    ctx.drawGraphics.setBackColor(ctx.backColor);
                    ctx.drawGraphics.fillRect(blankWidth);
                    if (emptyLine) {
                        ctx.x += blankWidth;
                    }
                }
            } while (emptyLine);

        } else { // Draw regular fragment

            ctx.drawGraphics.setBackColor(ctx.backColor);
            ctx.drawGraphics.setForeColor(ctx.foreColor);
            ctx.drawGraphics.setStrikeThroughColor(ctx.strikeThroughColor);
            ctx.drawGraphics.setUnderlineColor(ctx.underlineColor);
            ctx.drawGraphics.setWaveUnderlineColor(ctx.waveUnderlineColor);
            ctx.drawGraphics.setFont(ctx.font);

            if (ctx.tabsFragment) {
                ctx.drawGraphics.drawTabs(ctx.fragmentStartIndex,
                    ctx.fragmentLength, ctx.fragmentCharCount, ctx.fragmentWidth);

            } else { // non-tabs
                ctx.drawGraphics.drawChars(ctx.fragmentStartIndex,
                    ctx.fragmentLength, ctx.fragmentWidth);
            }
        }
    }


    /** Check whether the target offset was reached. */
    private void checkTargetOffsetReached(DrawInfo ctx) {
        ctx.continueDraw = true;

        // Check whether at the end of the line
        if (ctx.eol
            && (ctx.targetOffset == ctx.fragmentOffset || (ctx.targetOffset == -1))
        ) {
            /** Special case for the emulating the EOL at the end of the document
            * The EOL is emulated to process the layers that extend empty-line or EOL
            */
            char ch = '\n';
            ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
                    ctx.fragmentOffset, ch, ctx.x, ctx.spaceWidth, ctx);

        // Check whether targeting all characters
        } else if (ctx.targetOffset == -1
          && ctx.fragmentLength > 0 // Not sure whether it's necessary
        ) { // When targeting all chars
            FontMetrics fm = FontMetricsCache.getFontMetrics(ctx.font, ctx.component);
            
            // Use binary search to find the right offset
            int low = -1;
            int high = ctx.fragmentLength - 1;
            
            // Cache the widths and first check whether past the end of fragment
            int lastMid = high;
            int lastWidth; // cache 
            if (ctx.tabsFragment) { // fragment contains tabs
                int spaceCount = Analyzer.getColumn(ctx.textArray,
                    ctx.fragmentStartIndex, high, ctx.tabSize, ctx.visualColumn)
                        - ctx.visualColumn;
                lastWidth = spaceCount * ctx.spaceWidth;

            } else { // no tabs inside fragment
                lastWidth = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex,  high);
            }

            int lastWidthP1; // plus one char
            if (ctx.tabsFragment) { // fragment contains tabs
                int spaceCount = Analyzer.getColumn(ctx.textArray,
                    ctx.fragmentStartIndex, ctx.fragmentLength, ctx.tabSize, ctx.visualColumn)
                        - ctx.visualColumn;
                lastWidthP1 = spaceCount * ctx.spaceWidth;

            } else { // no tabs inside fragment
                lastWidthP1 = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex,  ctx.fragmentLength);
            }

            // Test whether the end of the fragment is accepted
            ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
                ctx.fragmentOffset + high, ctx.textArray[ctx.fragmentStartIndex + high],
                ctx.x + lastWidth, lastWidthP1 - lastWidth, ctx
            );
            
            if (!ctx.continueDraw) {
                // Binary search of the first offset that returns false follows
                while (low <= high) {
                    int mid =(low + high) / 2;

                    // Compute width that will be passed as x coordinate
                    int width = 0;
                    if (mid == lastMid + 1) { // try to use cached value
                        width = lastWidthP1;

                    } else {
                        if (ctx.tabsFragment) { // fragment contains tabs
                            int spaceCount = Analyzer.getColumn(ctx.textArray,
                                ctx.fragmentStartIndex, mid, ctx.tabSize, ctx.visualColumn)
                                - ctx.visualColumn;
                            width = spaceCount * ctx.spaceWidth;

                        } else { // no tabs inside fragment
                            width = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex,  mid);
                        }
                    }

                    // Compute width plus one char and substract the previous width
                    // to get the width of the char
                    int widthP1 = 0;
                    if (mid == lastMid - 1) { // try to use cached value
                        widthP1 = lastWidth;

                    } else {
                        if (ctx.tabsFragment) { // fragment contains tabs
                            int spaceCount = Analyzer.getColumn(ctx.textArray,
                                ctx.fragmentStartIndex, mid + 1, ctx.tabSize, ctx.visualColumn)
                                - ctx.visualColumn;
                            widthP1 = spaceCount * ctx.spaceWidth;

                        } else { // no tabs inside fragment
                            widthP1 = fm.charsWidth(ctx.textArray, ctx.fragmentStartIndex,  mid + 1);
                        }
                    }

                    lastWidth = width;
                    lastWidthP1 = widthP1;
                    lastMid = mid;

                    ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
                        ctx.fragmentOffset + mid, ctx.textArray[ctx.fragmentStartIndex + mid],
                        ctx.x + width, widthP1 - width, ctx
                    );

                    if (ctx.continueDraw) {
                        low = mid + 1;
                    } else {
                        //Bug #34130, search should not end when mid == low + 1
                        if (mid > low && mid != high) { 
                            high = mid; // last that rejected
                        } else {
                            break;
                        }
                    }
                }
            }
            


        // Check whether target offset is inside current fragment
        } else if (ctx.targetOffset < ctx.fragmentOffset + ctx.fragmentLength
                   && ctx.fragmentOffset <= ctx.targetOffset
        ) {
            int curWidth;
            int prevWidth = 0;
            int i = (ctx.targetOffset - ctx.fragmentOffset);

            if (i > 0) {
                if (ctx.tabsFragment) { // fragment contains tabs
                    int spaceCount = Analyzer.getColumn(ctx.textArray,
                        ctx.fragmentStartIndex, i, ctx.tabSize, ctx.visualColumn)
                            - ctx.visualColumn;
                    prevWidth = spaceCount * ctx.spaceWidth;

                } else { // no tabs inside fragment
                    prevWidth = FontMetricsCache.getFontMetrics(ctx.font,
                        ctx.component).charsWidth(ctx.textArray, ctx.fragmentStartIndex, i);
                }
            }

            if (ctx.tabsFragment) { // fragment contains tabs
                int spaceCount = Analyzer.getColumn(ctx.textArray,
                    ctx.fragmentStartIndex, i + 1, ctx.tabSize, ctx.visualColumn)
                        - ctx.visualColumn;
                curWidth = spaceCount * ctx.spaceWidth;

            } else { // no tabs inside fragment
                curWidth = FontMetricsCache.getFontMetrics(ctx.font,
                           ctx.component).charsWidth(ctx.textArray, ctx.fragmentStartIndex, i + 1);
            }

            ctx.continueDraw = ctx.drawGraphics.targetOffsetReached(
                ctx.fragmentOffset + i, ctx.textArray[ctx.fragmentStartIndex + i],
                ctx.x + prevWidth, curWidth - prevWidth, ctx);
        }
    }

    /** Draw current fragment of the token. */
    private void drawCurrentTokenFragment(DrawInfo ctx) {
        // Fill in the draw context
        ctx.foreColor = ctx.defaultColoring.getForeColor();
        ctx.backColor = ctx.defaultColoring.getBackColor();
        ctx.font = ctx.defaultColoring.getFont();
        ctx.strikeThroughColor = null;
        ctx.underlineColor = null;
        ctx.waveUnderlineColor = null;

        if (ctx.bol) { // if we are on the line begining
            Color fg = ctx.foreColor;
            Color bg = ctx.backColor;
            handleBOL(ctx);
            ctx.foreColor = fg;
            ctx.backColor = bg;
        }

        // Check for status updates in planes at the begining of this fragment
        while (ctx.fragmentOffset == ctx.updateOffset) {
            updateOffsetReached(ctx); // while() because can be more marks at same pos
        }

        // Compute the length of the fragment
        computeFragmentLength(ctx);

        // Compute the display width of the fragment
        computeFragmentDisplayWidth(ctx);

        // Draw the fragment
        drawFragment(ctx);

        if (debugFragment) {
            System.err.println("DrawEngine:   FRAGMENT='" // NOI18N
                    + EditorDebug.debugChars(ctx.buffer, ctx.fragmentStartIndex, ctx.fragmentLength)
                    + "', at pos=" + ctx.fragmentOffset // NOI18N
                    + ", bol=" + ctx.bol + ", eol=" + ctx.eol // NOI18N
            );
        }

        // Check whether target-offset was reached
        if (ctx.component != null) {
            checkTargetOffsetReached(ctx);
        }

        // Move the variables to the next fragment in token
        ctx.fragmentOffset += ctx.fragmentLength;
        ctx.drawnLength += ctx.fragmentLength;
        ctx.visualColumn += ctx.fragmentCharCount;
        ctx.x += ctx.fragmentWidth;
        ctx.bol = false;

        // Update coordinates at the end of each line
        if (ctx.eol) {
            handleEOL(ctx);
            ctx.bol = true; // now at BOL
        }

        if (ctx.fragmentOffset >= ctx.endOffset && ctx.endOffset < ctx.docLen) {
            ctx.continueDraw = false;
        }
    }


    /** Draw one token. This is repeatedly called until all the tokens
    * were drawn.
    * @return true when the drawing of the next token should be done
    *   or false when the drawing should stop.
    */
    private void drawCurrentToken(DrawInfo ctx) {
        // Get the token
        if (ctx.tokenID != null) {
            ctx.tokenContextPath = ctx.syntax.getTokenContextPath();
            ctx.tokenOffset = ctx.syntax.getTokenOffset() + ctx.bufferStartOffset;
            ctx.tokenLength = ctx.syntax.getTokenLength();

            /* Check whether the token isn't totally before the area to be drawn.
             * It must cover it at least by one character. For the more complicated lexical
             * analyzers it's possible that they return several tokens that
             * will cover only the prescan area.
             */
            if (ctx.tokenOffset + ctx.tokenLength <= ctx.startOffset) {
                return;
            }

        } else { // end of drawing area
            ctx.tokenContextPath = null;
            ctx.tokenOffset = ctx.fragmentOffset;
            ctx.tokenLength = 0;
        }

        // Ask all the contexts first to possibly find out
        // the first fragment length
        if (ctx.tokenOffset <= ctx.startOffset) {
            ctx.layerUpdateOffset = Integer.MAX_VALUE;
            int layersLength = ctx.layers.length;
            for (int i = 0; i < layersLength; i++) { // update status of all layers
                DrawLayer l = ctx.layers[i];
                ctx.layerActives[i] = l.isActive(ctx, null);

                int naco = l.getNextActivityChangeOffset(ctx);
                ctx.layerActivityChangeOffsets[i] = naco;
                if (naco > ctx.fragmentOffset && naco < ctx.layerUpdateOffset) {
                    ctx.layerUpdateOffset = naco;
                }
            }
            ctx.updateOffset = Math.min(ctx.layerUpdateOffset, ctx.drawMarkOffset);
        }

        ctx.drawnLength = ctx.fragmentOffset - ctx.tokenOffset;
        ctx.fragmentLength = 0; // length of current token fragment

        if (debug) {
            System.err.println("DrawEngine: TOKEN='" // NOI18N
                    + EditorDebug.debugChars(ctx.getBuffer(), ctx.getTokenOffset()
                        - ctx.getBufferStartOffset(), ctx.getTokenLength())
                    + "', tokenID=<" + (ctx.getTokenID() == null ? "null" : ctx.tokenID.getName()) // NOI18N
                    + ">, tcp=" + ctx.getTokenContextPath() // NOI18N
                    + ", pos=" + ctx.getTokenOffset() // NOI18N
            );
        }

        // Process all the fragments of one token
        do {
            drawCurrentTokenFragment(ctx);
        } while (ctx.continueDraw && ctx.drawnLength < ctx.tokenLength);

    }

    private void graphicsSpecificUpdates(DrawInfo ctx) {
        Rectangle bounds = ctx.editorUI.getExtentBounds();
        Rectangle clip = ctx.graphics.getClipBounds();
        Insets textMargin = ctx.editorUI.getTextMargin();
        int leftMarginWidth = textMargin.left - ctx.editorUI.lineNumberWidth
                              - ctx.editorUI.textLeftMarginWidth;

        // Draw line numbers bar and all the line nummbers
        if (ctx.lineNumbering && !ctx.syncedLineNumbering) {
            Color lnBackColor = ctx.lineNumberColoring.getBackColor();
            int numY = ctx.startY;
            int lnBarX = bounds.x + leftMarginWidth;
            if (!lnBackColor.equals(ctx.defaultColoring.getBackColor()) || bounds.x > 0) {
                ctx.graphics.setColor(lnBackColor);
                ctx.graphics.fillRect(lnBarX, numY, ctx.editorUI.lineNumberWidth,
                                      ctx.lineIndex * ctx.lineHeight); // can't use dg because of height
            }

            ctx.drawGraphics.setDefaultBackColor(lnBackColor); // will paint into bar

            int lastDigitInd = Math.max(ctx.lineNumberChars.length - 1, 0);
            int numX = lnBarX;
            if (ctx.editorUI.getLineNumberMargin() != null) {
                numX += ctx.editorUI.getLineNumberMargin().left;
            }

            ctx.bol = true; //
            for (int j = 0; j < ctx.lineIndex; j++) { // draw all line numbers

                // Init the context
                ctx.fragmentOffset = ctx.lineStartOffsets[j];
                ctx.foreColor = ctx.lineNumberColoring.getForeColor();
                ctx.backColor = lnBackColor;
                ctx.font = ctx.lineNumberColoring.getFont();
                ctx.strikeThroughColor = null;
                ctx.underlineColor = null;
                ctx.waveUnderlineColor = null;

                int lineNumber = ctx.startLineNumber + j;
                // Update line-number by layers
                int layersLength = ctx.layers.length;
                for (int i = 0; i < layersLength; i++) {
                    lineNumber = ctx.layers[i].updateLineNumberContext(lineNumber, ctx);
                }

                int i = lastDigitInd;
                // Fill in the digit chars
                do {
                    ctx.lineNumberChars[i--] = (char)('0' + (lineNumber % 10));
                    lineNumber /= 10;
                } while (lineNumber != 0 && i >= 0);
                // Fill in the spaces
                while (i >= 0) {
                    ctx.lineNumberChars[i--] = ' ';
                }

                ctx.drawGraphics.setY(numY);
                ctx.drawGraphics.setBuffer(ctx.lineNumberChars);
                ctx.drawGraphics.setForeColor(ctx.foreColor);
                ctx.drawGraphics.setBackColor(ctx.backColor);
                ctx.drawGraphics.setStrikeThroughColor(ctx.strikeThroughColor);
                ctx.drawGraphics.setUnderlineColor(ctx.underlineColor);
                ctx.drawGraphics.setWaveUnderlineColor(ctx.waveUnderlineColor);
                ctx.drawGraphics.setFont(ctx.font);

                ctx.drawGraphics.setX(lnBarX);
                ctx.drawGraphics.fillRect(ctx.editorUI.lineNumberWidth);
                ctx.drawGraphics.setX(numX);

                ctx.drawGraphics.drawChars(0, ctx.lineNumberChars.length,
                    ctx.lineNumberChars.length * ctx.editorUI.lineNumberDigitWidth);
                
                ctx.drawGraphics.setBuffer(null); // will do changes in buffer

                numY += ctx.lineHeight;
            }
        }

        // Clear margins
        ctx.graphics.setColor(ctx.defaultColoring.getBackColor());

        /** The margin is cleared only in case the line is scrolled
        * horizontally and therefore the margin is dirty because
        * of the previous text on the line. Otherwise the margin
        * is not cleared. The condition (bounds.x gt 0) gives the answer.
        */
        // Left margin
        if (leftMarginWidth > 0 && bounds.x > 0) {
            ctx.graphics.fillRect(bounds.x, ctx.startY, leftMarginWidth, ctx.lineIndex * ctx.lineHeight);
        }

        // Text left margin
        if (ctx.editorUI.textLeftMarginWidth > 0 && bounds.x > 0) {
            ctx.graphics.fillRect(bounds.x + textMargin.left - ctx.editorUI.textLeftMarginWidth,
                                  ctx.startY, ctx.editorUI.textLeftMarginWidth, ctx.lineIndex * ctx.lineHeight);
        }

        // Right margin
        if (textMargin.right > 0) {
            ctx.graphics.fillRect(bounds.x + bounds.width - textMargin.right,
                                  ctx.startY, textMargin.right, ctx.lineIndex * ctx.lineHeight);
        }

        // Top margin
        /** The margin is cleared only in case the extent bounds
        * are high enough so that the margin could be dirty
        * because of drawing too much to the right.
        */
        if (textMargin.top > 0 && clip.y < bounds.y + textMargin.top) {
            ctx.graphics.fillRect(bounds.x, bounds.y,
                                  bounds.width, textMargin.top);
        }

        // Bottom margin
        int bY = bounds.y + bounds.height - textMargin.bottom;
        if (textMargin.bottom > 0 && clip.y + clip.height > bY) {
            ctx.graphics.fillRect(bounds.x, bY,
                                  bounds.width, textMargin.bottom);
        }
    }

    /** Draw on the specified area.
     * @param drawGraphics draw graphics through which the drawing is done
     * @param editorUI extended UI to use
     * @param startOffset position from which the drawing starts.
     *  It must BOL of the first line to be drawn.
     * @param endOffset position where the drawing stops.
     *  It must be EOL of the last line to be drawn.
     * @param startX x-coordinate at which the drawing starts
     * @param startY x-coordinate at which the drawing starts
     * @param targetOffset position where the targetOffsetReached() method
     *   of drawGraphics is called. This is useful for caret update or modelToView.
     *   The Integer.MAX_VALUE can be passed to ignore that behavior. The -1 value
     *   has special meaning there so that it calls targetOffsetReached() after each
     *   character processed. This is used by viewToModel to find the position
     *   for some point.
     */
    void draw(DrawGraphics drawGraphics, EditorUI editorUI, int startOffset, int endOffset,
              int startX, int startY, int targetOffset) throws BadLocationException {
                  
        draw(null, drawGraphics, editorUI, startOffset, endOffset,
            startX, startY, targetOffset);
    }

    void draw(View view, DrawGraphics drawGraphics, EditorUI editorUI, int startOffset, int endOffset,
              int startX, int startY, int targetOffset) throws BadLocationException {
        // Some correctness tests at the begining
        if (startOffset < 0 || endOffset < 0 || startOffset > endOffset
                || startX < 0 || startY < 0
           ) {
            return;
        }

        BaseDocument doc = (BaseDocument)((view != null) ? view.getDocument() : editorUI.getDocument());
        if (debug) {
            javax.swing.text.Element lineRoot = doc.getParagraphElement(0).getParentElement();
            int startLine = lineRoot.getElementIndex(startOffset);
            int startLineOffset = lineRoot.getElement(startLine).getStartOffset();
            int endLine = lineRoot.getElementIndex(endOffset);
            int endLineOffset = lineRoot.getElement(endLine).getStartOffset();
            Graphics g = drawGraphics.getGraphics();
            
            System.err.println("DrawEngine:---------- DRAWING startOffset="
                + startOffset + ", startLine=" + startLine // NOI18N
                + "(o=" + startLineOffset + "), endOffset=" + endOffset // NOI18N
                + ", endLine=" + endLine + "(o=" + endLineOffset // NOI18N
                + "), clip=" + ((g != null) ? g.getClipBounds().toString() : "null") // NOI18N
                + "  ------------------"); // NOI18N
        }

        // Draw-context and other info
        DrawInfo ctx = new DrawInfo();
        ctx.drawGraphics = drawGraphics;
        ctx.drawGraphics.setView(view);
        ctx.editorUI = editorUI;
        ctx.startOffset = startOffset;
        ctx.endOffset = endOffset;
        ctx.startX = startX;
        ctx.startY = startY;
        ctx.targetOffset = targetOffset;

        synchronized (editorUI) { // lock operations manipulating draw layer chain
            ctx.doc = doc;
            if (ctx.doc == null) { // no base-document available
                return;
            }

            ctx.text = DocumentUtilities.SEGMENT_CACHE.getSegment();
            ctx.syntax = ctx.doc.getFreeSyntax();
            ctx.doc.readLock();

            try {
                ctx.component = editorUI.getComponent();
                ctx.docLen = ctx.doc.getLength();

                if (ctx.startOffset > ctx.docLen || ctx.endOffset > ctx.docLen) {
                    return;
                }

                /* Correct the ending position to be at the begining
                 * of the next line. The only exception is when the
                 * endOffset is equal to docLen.
                 */
                if (ctx.endOffset < ctx.docLen) {
                    ctx.endOffset++;
                }

                // Initialize the draw-info
                initInfo(ctx);

                // Cycle through all the tokens found in the buffer
                do {
                    ctx.tokenID = ctx.syntax.nextToken();

                    // Draw the current token
                    drawCurrentToken(ctx);

                } while (ctx.continueDraw && ctx.tokenID != null);

                if (ctx.endOffset == ctx.docLen) {
                    handleEOL(ctx);
                    // paint text limit line till the end of visible editor area
                    
                    if (ctx.editorUI.textLimitLineVisible) { // draw limit line
                        int lineX = ctx.startX + ctx.editorUI.textLimitWidth * ctx.editorUI.defaultSpaceWidth;
                        if (ctx.graphics !=null){
                            ctx.graphics.setColor(ctx.editorUI.textLimitLineColor);
                            Rectangle clip = ctx.graphics.getClipBounds();
                            if (clip.height>ctx.editorUI.getLineHeight()){
                                ctx.graphics.drawLine(lineX, ctx.y, lineX, ctx.y+clip.height);
                            }
                        }
                    }
                    
                }

                ctx.editorUI.updateVirtualWidth(ctx.widestWidth
                                                + ctx.editorUI.lineNumberWidth + 2 * ctx.editorUI.defaultSpaceWidth);

                // When drawing to graphics, the line numbers and insets will be drawn now
                if (ctx.graphics != null) {
                    graphicsSpecificUpdates(ctx);
                }

            } finally {
                ctx.drawGraphics.setBuffer(null);
                ctx.drawGraphics.finish();

                if (ctx.syntax != null) {
                    ctx.doc.releaseSyntax(ctx.syntax);
                }
                DocumentUtilities.SEGMENT_CACHE.releaseSegment(ctx.text);
                ctx.doc.readUnlock();

            }
        } // synchronized on editorUI
    }


    static class DrawInfo implements DrawContext {

        // DrawContext -----------------------------------------------
        /** Current foreground color. */
        Color foreColor;

        /** Current background color. */
        Color backColor;

        /** Current background color. */
        Color underlineColor;

        /** Current background color. */
        Color waveUnderlineColor;

        /** Color of the strike-through line or null. */
        Color strikeThroughColor;

        /** Current font. */
        Font font;

        /** Starting position of the drawing. */
        int startOffset;

        /** Ending position of the drawing. */
        int endOffset;

        /** Whether we are currently at the line begining. */
        boolean bol;

        /** Whether we are currently at the line end. */
        boolean eol;

        /** Editor-UI of the component for which we are drawing. */
        EditorUI editorUI;

        /** Buffer from which the chars are being drawn. */
        char[] buffer;

        /** Starting poisition of the buffer inside the document. */
        int bufferStartOffset;

        /** Token-id of the token being drawn. */
        TokenID tokenID;

        /** Token-context-path of the token being drawn. */
        TokenContextPath tokenContextPath;

        /** Position of the token in the document */
        int tokenOffset;

        /** Length of the token's text. */
        int tokenLength;

        /** Position of the fragment of the token being drawn in the document. */
        int fragmentOffset;

        /** Length of the fragment of the token in the document. */
        int fragmentLength;

        // Other variables ---------------------------------------------

        /** Draw graphics */
        DrawGraphics drawGraphics;

        /** Target document position for the drawing or -1 if all the positions
        * are the potential targets.
        */
        int targetOffset;

        /** Text of the document being used by drawing. */
        Segment text;

        /** Char array in the text segment. */
        char[] textArray;

        /** Syntax scanning the input. */
        Syntax syntax;

        /** Component being painted */
        JTextComponent component;

        /** Document of the component. */
        BaseDocument doc;

        /** Current length of the document */
        int docLen;

        /** Current visual column. */
        int visualColumn;

        /** Current x-coordinate. */
        int x;

        /** Current y-coordinate. */
        int y;

        /** Starting x-coordinate. */
        int startX;

        /** Starting y-coordinate. */
        int startY;

        /** Height of the line being drawn. */
        int lineHeight;

        /** Default coloring of the component. */
        Coloring defaultColoring;

        /** Size of the TAB character being drawn. */
        int tabSize;

        /** Widest width of the line that was painted. */
        int widestWidth;

        /** Whether the draw should continue or not. */
        boolean continueDraw;

        /** Line number of the first painted line. */
        int startLineNumber;

        /** Index of the line being drawn. It is added
        * to the startLineNumber to form the resulting line number.
        */
        int lineIndex;

        /** Array of the start positions of all the lines drawn. */
        int[] lineStartOffsets;

        /** Characters forming the line-number. It is reused for drawing
        * all the lines.
        */
        char[] lineNumberChars;

        /** Coloring for the line-number. */
        Coloring lineNumberColoring;

        /** Graphics object. It can be null when not drawing to the component. */
        Graphics graphics;

        /** Whether line-numbers are visible and allowed by the draw-graphics. */
        boolean lineNumbering;

        /** Whether the line-numbers should be painted after each is painted.
        * By default the line-numbers are drawn as one block at the end
        * of the drawing.
        */
        boolean syncedLineNumbering;

        /** Array of the draw-layers to be used in the painting */
        DrawLayer[] layers;

        /** Whether the particular layer is currently active or not. */
        boolean[] layerActives;

        /** Next position where the layer will be asked whether it's active
        * or not.
        */
        int[] layerActivityChangeOffsets;

        /** Position where either the next draw-mark is
        * or which matches the activity-change-offset of one or more layers.
        */
        int updateOffset;

        /** Next activity-change-offset of one or more layers. */
        int layerUpdateOffset;

        /** Update of the layers because of the draw-mark is at the given position.
        * False means the update is because the activity-change-offset
        * was reached.
        */
        boolean drawMarkUpdate;

        /** List of draw marks in the painted area. */
        List drawMarkList;
        
        /** Current index in the drawMarkList */
        int drawMarkIndex;

        /** Current draw-mark */
        MarkFactory.DrawMark drawMark;

        /** Position of the current draw-mark */
        int drawMarkOffset;

        /** Length of the current token that was already drawn. */
        int drawnLength;

        /** Offset of the fragment starting character in the buffer */
        int fragmentStartIndex;

        /** Whether the fragment contains TABs only. */
        boolean tabsFragment;

        /** Width of one space character for the current context font. */
        int spaceWidth;

        /** Display width of the fragment */
        int fragmentWidth;

        /** Number of characters that the fragment stands for. It can differ
        * from fragmentLength for tabsFragment.
        */
        int fragmentCharCount;

        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 getUnderlineColor() {
            return underlineColor;
        }

        public void setUnderlineColor(Color underlineColor) {
            this.underlineColor = underlineColor;
        }

        public Color getWaveUnderlineColor() {
            return waveUnderlineColor;
        }

        public void setWaveUnderlineColor(Color waveUnderlineColor) {
            this.waveUnderlineColor = waveUnderlineColor;
        }

        public Color getStrikeThroughColor() {
            return strikeThroughColor;
        }

        public void setStrikeThroughColor(Color strikeThroughColor) {
            this.strikeThroughColor = strikeThroughColor;
        }

        public Font getFont() {
            return font;
        }

        public void setFont(Font font) {
            this.font = font;
        }

        public int getStartOffset() {
            return startOffset;
        }

        public int getEndOffset() {
            return endOffset;
        }

        public boolean isBOL() {
            return bol;
        }

        public boolean isEOL() {
            return eol;
        }

        public EditorUI getEditorUI() {
            return editorUI;
        }

        public char[] getBuffer() {
            return buffer;
        }

        public int getBufferStartOffset() {
            return bufferStartOffset;
        }

        public TokenID getTokenID() {
            return tokenID;
        }

        public TokenContextPath getTokenContextPath() {
            return tokenContextPath;
        }

        public int getTokenOffset() {
            return tokenOffset;
        }

        public int getTokenLength() {
            return tokenLength;
        }

        public int getFragmentOffset() {
            return fragmentOffset;
        }

        public int getFragmentLength() {
            return fragmentLength;
        }

    }
    
    class PreinitializedDrawEngine extends DrawEngine{
        
        DrawGraphics drawGraphics;
        DrawInfo drawInfo;
        
        public PreinitializedDrawEngine(DrawGraphics drawGraphics){
            super();
            this.drawGraphics = drawGraphics;            
        }

        public void release(){
            drawInfo.drawGraphics.setBuffer(null);
            drawInfo.drawGraphics.finish();
            
            if (drawInfo.syntax != null) {
                drawInfo.doc.releaseSyntax(drawInfo.syntax);
            }
            DocumentUtilities.SEGMENT_CACHE.releaseSegment(drawInfo.text);
        }
        
        public void preinitialize(BaseDocument doc, EditorUI editorUI, int startOffset, int endOffset,
        int startX, int startY, int targetOffset) throws BadLocationException{
            // Draw-context and other info
            drawInfo = new DrawInfo();
            drawInfo.drawGraphics = drawGraphics;
            drawInfo.editorUI = editorUI;
            drawInfo.startOffset = startOffset;
            drawInfo.endOffset = endOffset;
            drawInfo.startX = startX;
            drawInfo.startY = startY;
            drawInfo.targetOffset = targetOffset;
            
            synchronized (editorUI) { // lock operations manipulating draw layer chain
                drawInfo.doc = doc;
                if (drawInfo.doc == null) { // no base-document available
                    return;
                }
                
                try{
                    drawInfo.text = DocumentUtilities.SEGMENT_CACHE.getSegment();
                    drawInfo.syntax = drawInfo.doc.getFreeSyntax();
                    drawInfo.doc.readLock();
                    
                    drawInfo.component = editorUI.getComponent();
                    drawInfo.docLen = drawInfo.doc.getLength();
                    
                    if (drawInfo.startOffset > drawInfo.docLen || drawInfo.endOffset > drawInfo.docLen) {
                        return;
                    }
                    
                    /* Correct the ending position to be at the begining
                     * of the next line. The only exception is when the
                     * endOffset is equal to docLen.
                     */
                    if (drawInfo.endOffset < drawInfo.docLen) {
                        drawInfo.endOffset++;
                    }
                    
                    // Initialize the draw-info
                    initInfo(drawInfo);
                } finally{
                    drawInfo.doc.readUnlock();
                }
            }
        }
        
        void draw(DrawGraphics dg, EditorUI editorUI, int startOffset, int endOffset,
        int startX, int startY, int targetOffset) throws BadLocationException {
            // Some correctness tests at the begining
            if (startOffset < 0 || endOffset < 0 || startOffset > endOffset
            || startX < 0 || startY < 0
            ) {
                return;
            }
            if (dg == null){
                dg = drawGraphics;
            }else{
                super.draw(dg, editorUI, startOffset, endOffset, startX, startY, targetOffset);
                return;
            }
            
            synchronized (editorUI){
                DrawInfo ctx = drawInfo;
                ctx.doc.readLock();
                try{
                    do {
                        ctx.tokenID = ctx.syntax.nextToken();

                        // Draw the current token
                        drawCurrentToken(ctx);

                    } while (ctx.continueDraw && ctx.tokenID != null);

                    if (ctx.endOffset == ctx.docLen) {
                        handleEOL(ctx);
                    }

                    ctx.editorUI.updateVirtualWidth(ctx.widestWidth
                    + ctx.editorUI.lineNumberWidth + 2 * ctx.editorUI.defaultSpaceWidth);

                    // When drawing to graphics, the line numbers and insets will be drawn now
                    if (ctx.graphics != null) {
                        graphicsSpecificUpdates(ctx);
                    }
                }finally{
                    ctx.doc.readUnlock();
                }
            }
        }
        
    }
    

}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.