Exemple #1
0
  public StringBuilder appendHTMLRepresentation(
      StringBuilder sb, RSyntaxTextArea textArea, boolean fontFamily, boolean tabsToSpaces) {

    SyntaxScheme colorScheme = textArea.getSyntaxScheme();
    Style scheme = colorScheme.getStyle(getType());
    Font font = textArea.getFontForTokenType(getType()); // scheme.font;

    if (font.isBold()) sb.append("<b>");
    if (font.isItalic()) sb.append("<em>");
    if (scheme.underline || isHyperlink()) sb.append("<u>");

    sb.append("<font");
    if (fontFamily) {
      sb.append(" face=\"").append(font.getFamily()).append("\"");
    }
    sb.append(" color=\"").append(getHTMLFormatForColor(scheme.foreground)).append("\">");

    // NOTE: Don't use getLexeme().trim() because whitespace tokens will
    // be turned into NOTHING.
    appendHtmlLexeme(textArea, sb, tabsToSpaces);

    sb.append("</font>");
    if (scheme.underline || isHyperlink()) sb.append("</u>");
    if (font.isItalic()) sb.append("</em>");
    if (font.isBold()) sb.append("</b>");

    return sb;
  }
  /**
   * Draws the passed-in text using syntax highlighting for the current language. It is assumed that
   * the entire line is either not in a selected region, or painting with a selection-foreground
   * color is turned off.
   *
   * @param painter The painter to render the tokens.
   * @param token The list of tokens to draw.
   * @param g The graphics context in which to draw.
   * @param x The x-coordinate at which to draw.
   * @param y The y-coordinate at which to draw.
   * @return The x-coordinate representing the end of the painted text.
   */
  private float drawLine(
      TokenPainter painter, Token token, Graphics2D g, float x, float y, int line) {

    float nextX = x; // The x-value at the end of our text.
    boolean paintBG = host.getPaintTokenBackgrounds(line, y);

    while (token != null && token.isPaintable() && nextX < clipEnd) {
      nextX = painter.paint(token, g, nextX, y, host, this, clipStart, paintBG);
      token = token.getNextToken();
    }

    // NOTE: We should re-use code from Token (paintBackground()) here,
    // but don't because I'm just too lazy.
    if (host.getEOLMarkersVisible()) {
      g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
      g.setFont(host.getFontForTokenType(Token.WHITESPACE));
      g.drawString("\u00B6", nextX, y);
    }

    // Return the x-coordinate at the end of the painted text.
    return nextX;
  }
  /**
   * Paints this token, using special symbols for whitespace characters.
   *
   * @param g The graphics context in which to paint.
   * @param x The x-coordinate at which to paint.
   * @param y The y-coordinate at which to paint.
   * @param host The text area this token is in.
   * @param e How to expand tabs.
   * @param clipStart The left boundary of the clip rectangle in which we're painting. This
   *     optimizes painting by allowing us to not paint not paint when this token is "to the left"
   *     of the clip rectangle.
   * @return The x-coordinate representing the end of the painted text.
   */
  public final float paint(
      Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart) {

    int origX = (int) x;
    int end = textOffset + textCount;
    float nextX = x;
    int flushLen = 0;
    int flushIndex = textOffset;
    Color fg = host.getForegroundForToken(this);
    Color bg = host.getBackgroundForTokenType(type);
    g.setFont(host.getFontForTokenType(type));
    FontMetrics fm = host.getFontMetricsForTokenType(type);

    int ascent = fm.getAscent();
    int height = fm.getHeight();

    for (int i = textOffset; i < end; i++) {

      switch (text[i]) {
        case '\t':

          // Fill in background.
          nextX = x + fm.charsWidth(text, flushIndex, flushLen);
          float nextNextX = e.nextTabStop(nextX, 0);
          if (bg != null) {
            paintBackground(x, y, nextNextX - x, height, g, ascent, host, bg);
          }
          g.setColor(fg);

          // Paint chars cached before the tab.
          if (flushLen > 0) {
            g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
            flushLen = 0;
          }
          flushIndex = i + 1;

          // Draw an arrow representing the tab.
          int halfHeight = height / 2;
          int quarterHeight = halfHeight / 2;
          int ymid = (int) y - ascent + halfHeight;
          g.drawLine((int) nextX, ymid, (int) nextNextX, ymid);
          g.drawLine((int) nextNextX, ymid, (int) nextNextX - 4, ymid - quarterHeight);
          g.drawLine((int) nextNextX, ymid, (int) nextNextX - 4, ymid + quarterHeight);

          x = nextNextX;
          break;

        case ' ':

          // NOTE:  There is a little bit of a "fudge factor"
          // here when "smooth text" is enabled, as "width"
          // below may well not be the width given to the space
          // by fm.charsWidth() (it depends on how it places the
          // space with respect to the preceding character).
          // But, we assume the approximation is close enough for
          // our drawing a dot for the space.

          // "flushLen+1" ensures text is aligned correctly (or,
          // aligned the same as in getWidth()).
          nextX = x + fm.charsWidth(text, flushIndex, flushLen + 1);
          int width = fm.charWidth(' ');

          // Paint background.
          if (bg != null) {
            paintBackground(x, y, nextX - x, height, g, ascent, host, bg);
          }
          g.setColor(fg);

          // Paint chars before space.
          if (flushLen > 0) {
            g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
            flushLen = 0;
          }

          // Paint a dot representing the space.
          dotRect.x = nextX - width / 2.0f; // "2.0f" for FindBugs
          dotRect.y = y - ascent + height / 2.0f; // Ditto
          g.fill(dotRect);
          flushIndex = i + 1;
          x = nextX;
          break;

        case '\f':
          // ???
          // fall-through for now.

        default:
          flushLen += 1;
          break;
      }
    }

    nextX = x + fm.charsWidth(text, flushIndex, flushLen);

    if (flushLen > 0 && nextX >= clipStart) {
      if (bg != null) {
        paintBackground(x, y, nextX - x, height, g, ascent, host, bg);
      }
      g.setColor(fg);
      g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
    }

    if (host.getUnderlineForToken(this)) {
      g.setColor(fg);
      int y2 = (int) (y + 1);
      g.drawLine(origX, y2, (int) nextX, y2);
    }

    // Don't check if it's whitespace - some TokenMakers may return types
    // other than Token.WHITESPACE for spaces (such as Token.IDENTIFIER).
    // This also allows us to paint tab lines for MLC's.
    if (host.getPaintTabLines() && origX == host.getMargin().left) { // && isWhitespace()) {
      paintTabLines(origX, (int) y, (int) nextX, g, e, host);
    }

    return nextX;
  }
  /**
   * Draws a single view (i.e., a line of text for a wrapped view), wrapping the text onto multiple
   * lines if necessary. Any selected text is rendered with the editor's "selected text" color.
   *
   * @param painter The painter to use to render tokens.
   * @param g The graphics context in which to paint.
   * @param r The rectangle in which to paint.
   * @param view The <code>View</code> to paint.
   * @param fontHeight The height of the font being used.
   * @param y The y-coordinate at which to begin painting.
   * @param selStart The start of the selection.
   * @param selEnd The end of the selection.
   */
  protected void drawViewWithSelection(
      TokenPainter painter,
      Graphics2D g,
      Rectangle r,
      View view,
      int fontHeight,
      int y,
      int selStart,
      int selEnd) {

    float x = r.x;

    LayeredHighlighter h = (LayeredHighlighter) host.getHighlighter();

    RSyntaxDocument document = (RSyntaxDocument) getDocument();
    Element map = getElement();

    int p0 = view.getStartOffset();
    int lineNumber = map.getElementIndex(p0);
    int p1 = view.getEndOffset(); // - 1;

    setSegment(p0, p1 - 1, document, drawSeg);
    // System.err.println("drawSeg=='" + drawSeg + "' (p0/p1==" + p0 + "/" + p1 + ")");
    int start = p0 - drawSeg.offset;
    Token token = document.getTokenListForLine(lineNumber);

    // If this line is an empty line, then the token list is simply a
    // null token.  In this case, the line highlight will be skipped in
    // the loop below, so unfortunately we must manually do it here.
    if (token != null && token.getType() == Token.NULL) {
      h.paintLayeredHighlights(g, p0, p1, r, host, this);
      return;
    }

    // Loop through all tokens in this view and paint them!
    while (token != null && token.isPaintable()) {

      int p = calculateBreakPosition(p0, token, x);
      x = r.x;

      h.paintLayeredHighlights(g, p0, p, r, host, this);

      while (token != null && token.isPaintable() && token.getEndOffset() - 1 < p) { // <=p) {

        // Selection starts in this token
        if (token.containsPosition(selStart)) {

          if (selStart > token.getOffset()) {
            tempToken.copyFrom(token);
            tempToken.textCount = selStart - tempToken.getOffset();
            x = painter.paint(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(selStart);
            // Clone required since token and tempToken must be
            // different tokens for else statement below
            token = new TokenImpl(tempToken);
          }

          int selCount = Math.min(token.length(), selEnd - token.getOffset());
          if (selCount == token.length()) {
            x = painter.paintSelected(token, g, x, y, host, this);
          } else {
            tempToken.copyFrom(token);
            tempToken.textCount = selCount;
            x = painter.paintSelected(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(token.getOffset() + selCount);
            token = tempToken;
            x = painter.paint(token, g, x, y, host, this);
          }

        }

        // Selection ends in this token
        else if (token.containsPosition(selEnd)) {
          tempToken.copyFrom(token);
          tempToken.textCount = selEnd - tempToken.getOffset();
          x = painter.paintSelected(tempToken, g, x, y, host, this);
          tempToken.textCount = token.length();
          tempToken.makeStartAt(selEnd);
          token = tempToken;
          x = painter.paint(token, g, x, y, host, this);
        }

        // This token is entirely selected
        else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) {
          x = painter.paintSelected(token, g, x, y, host, this);
        }

        // This token is entirely unselected
        else {
          x = painter.paint(token, g, x, y, host, this);
        }

        token = token.getNextToken();
      }

      // If there's a token that's going to be split onto the next line
      if (token != null && token.isPaintable() && token.getOffset() < p) {

        int tokenOffset = token.getOffset();
        Token orig = token;
        token =
            new TokenImpl(
                drawSeg, tokenOffset - start, p - 1 - start, tokenOffset, token.getType());

        // Selection starts in this token
        if (token.containsPosition(selStart)) {

          if (selStart > token.getOffset()) {
            tempToken.copyFrom(token);
            tempToken.textCount = selStart - tempToken.getOffset();
            x = painter.paint(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(selStart);
            // Clone required since token and tempToken must be
            // different tokens for else statement below
            token = new TokenImpl(tempToken);
          }

          int selCount = Math.min(token.length(), selEnd - token.getOffset());
          if (selCount == token.length()) {
            x = painter.paintSelected(token, g, x, y, host, this);
          } else {
            tempToken.copyFrom(token);
            tempToken.textCount = selCount;
            x = painter.paintSelected(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(token.getOffset() + selCount);
            token = tempToken;
            x = painter.paint(token, g, x, y, host, this);
          }

        }

        // Selection ends in this token
        else if (token.containsPosition(selEnd)) {
          tempToken.copyFrom(token);
          tempToken.textCount = selEnd - tempToken.getOffset();
          x = painter.paintSelected(tempToken, g, x, y, host, this);
          tempToken.textCount = token.length();
          tempToken.makeStartAt(selEnd);
          token = tempToken;
          x = painter.paint(token, g, x, y, host, this);
        }

        // This token is entirely selected
        else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) {
          x = painter.paintSelected(token, g, x, y, host, this);
        }

        // This token is entirely unselected
        else {
          x = painter.paint(token, g, x, y, host, this);
        }

        token = new TokenImpl(orig);
        ((TokenImpl) token).makeStartAt(p);
      }

      p0 = (p == p0) ? p1 : p;
      y += fontHeight;
    } // End of while (token!=null && token.isPaintable()).

    // NOTE: We should re-use code from Token (paintBackground()) here,
    // but don't because I'm just too lazy.
    if (host.getEOLMarkersVisible()) {
      g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
      g.setFont(host.getFontForTokenType(Token.WHITESPACE));
      g.drawString("\u00B6", x, y - fontHeight);
    }
  }
  /**
   * Draws a single view (i.e., a line of text for a wrapped view), wrapping the text onto multiple
   * lines if necessary.
   *
   * @param painter The painter to use to render tokens.
   * @param g The graphics context in which to paint.
   * @param r The rectangle in which to paint.
   * @param view The <code>View</code> to paint.
   * @param fontHeight The height of the font being used.
   * @param y The y-coordinate at which to begin painting.
   */
  protected void drawView(
      TokenPainter painter, Graphics2D g, Rectangle r, View view, int fontHeight, int y) {

    float x = r.x;

    LayeredHighlighter h = (LayeredHighlighter) host.getHighlighter();

    RSyntaxDocument document = (RSyntaxDocument) getDocument();
    Element map = getElement();

    int p0 = view.getStartOffset();
    int lineNumber = map.getElementIndex(p0);
    int p1 = view.getEndOffset(); // - 1;

    setSegment(p0, p1 - 1, document, drawSeg);
    // System.err.println("drawSeg=='" + drawSeg + "' (p0/p1==" + p0 + "/" + p1 + ")");
    int start = p0 - drawSeg.offset;
    Token token = document.getTokenListForLine(lineNumber);

    // If this line is an empty line, then the token list is simply a
    // null token.  In this case, the line highlight will be skipped in
    // the loop below, so unfortunately we must manually do it here.
    if (token != null && token.getType() == Token.NULL) {
      h.paintLayeredHighlights(g, p0, p1, r, host, this);
      return;
    }

    // Loop through all tokens in this view and paint them!
    while (token != null && token.isPaintable()) {

      int p = calculateBreakPosition(p0, token, x);
      x = r.x;

      h.paintLayeredHighlights(g, p0, p, r, host, this);

      while (token != null && token.isPaintable() && token.getEndOffset() - 1 < p) { // <=p) {
        x = painter.paint(token, g, x, y, host, this);
        token = token.getNextToken();
      }

      if (token != null && token.isPaintable() && token.getOffset() < p) {
        int tokenOffset = token.getOffset();
        tempToken.set(
            drawSeg.array, tokenOffset - start, p - 1 - start, tokenOffset, token.getType());
        painter.paint(tempToken, g, x, y, host, this);
        tempToken.copyFrom(token);
        tempToken.makeStartAt(p);
        token = new TokenImpl(tempToken);
      }

      p0 = (p == p0) ? p1 : p;
      y += fontHeight;
    } // End of while (token!=null && token.isPaintable()).

    // NOTE: We should re-use code from Token (paintBackground()) here,
    // but don't because I'm just too lazy.
    if (host.getEOLMarkersVisible()) {
      g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
      g.setFont(host.getFontForTokenType(Token.WHITESPACE));
      g.drawString("\u00B6", x, y - fontHeight);
    }
  }
  /**
   * Draws the passed-in text using syntax highlighting for the current language. Tokens are checked
   * for being in a selected region, and are rendered appropriately if they are.
   *
   * @param painter The painter to render the tokens.
   * @param token The list of tokens to draw.
   * @param g The graphics context in which to draw.
   * @param x The x-coordinate at which to draw.
   * @param y The y-coordinate at which to draw.
   * @param selStart The start of the selection.
   * @param selEnd The end of the selection.
   * @return The x-coordinate representing the end of the painted text.
   */
  private float drawLineWithSelection(
      TokenPainter painter, Token token, Graphics2D g, float x, float y, int selStart, int selEnd) {

    float nextX = x; // The x-value at the end of our text.
    boolean useSTC = host.getUseSelectedTextColor();

    while (token != null && token.isPaintable() && nextX < clipEnd) {

      // Selection starts in this token
      if (token.containsPosition(selStart)) {

        if (selStart > token.getOffset()) {
          tempToken.copyFrom(token);
          tempToken.textCount = selStart - tempToken.getOffset();
          nextX = painter.paint(tempToken, g, nextX, y, host, this, clipStart);
          tempToken.textCount = token.length();
          tempToken.makeStartAt(selStart);
          // Clone required since token and tempToken must be
          // different tokens for else statement below
          token = new TokenImpl(tempToken);
        }

        int tokenLen = token.length();
        int selCount = Math.min(tokenLen, selEnd - token.getOffset());
        if (selCount == tokenLen) {
          nextX = painter.paintSelected(token, g, nextX, y, host, this, clipStart, useSTC);
        } else {
          tempToken.copyFrom(token);
          tempToken.textCount = selCount;
          nextX = painter.paintSelected(tempToken, g, nextX, y, host, this, clipStart, useSTC);
          tempToken.textCount = token.length();
          tempToken.makeStartAt(token.getOffset() + selCount);
          token = tempToken;
          nextX = painter.paint(token, g, nextX, y, host, this, clipStart);
        }

      }

      // Selection ends in this token
      else if (token.containsPosition(selEnd)) {
        tempToken.copyFrom(token);
        tempToken.textCount = selEnd - tempToken.getOffset();
        nextX = painter.paintSelected(tempToken, g, nextX, y, host, this, clipStart, useSTC);
        tempToken.textCount = token.length();
        tempToken.makeStartAt(selEnd);
        token = tempToken;
        nextX = painter.paint(token, g, nextX, y, host, this, clipStart);
      }

      // This token is entirely selected
      else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) {
        nextX = painter.paintSelected(token, g, nextX, y, host, this, clipStart, useSTC);
      }

      // This token is entirely unselected
      else {
        nextX = painter.paint(token, g, nextX, y, host, this, clipStart);
      }

      token = token.getNextToken();
    }

    // NOTE: We should re-use code from Token (paintBackground()) here,
    // but don't because I'm just too lazy.
    if (host.getEOLMarkersVisible()) {
      g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
      g.setFont(host.getFontForTokenType(Token.WHITESPACE));
      g.drawString("\u00B6", nextX, y);
    }

    // Return the x-coordinate at the end of the painted text.
    return nextX;
  }