/**
   * Paints the word-wrapped text.
   *
   * @param g The graphics context in which to paint.
   * @param a The shape (usually a rectangle) in which to paint.
   */
  public void paint(Graphics g, Shape a) {

    Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a : a.getBounds();
    tabBase = alloc.x;

    Graphics2D g2d = (Graphics2D) g;
    host = (RSyntaxTextArea) getContainer();
    int ascent = host.getMaxAscent();
    int fontHeight = host.getLineHeight();
    FoldManager fm = host.getFoldManager();
    TokenPainter painter = host.getTokenPainter();
    Element root = getElement();

    // Whether token styles should always be painted, even in selections
    int selStart = host.getSelectionStart();
    int selEnd = host.getSelectionEnd();
    boolean useSelectedTextColor = host.getUseSelectedTextColor();

    int n = getViewCount(); // Number of lines.
    int x = alloc.x + getLeftInset();
    tempRect.y = alloc.y + getTopInset();
    Rectangle clip = g.getClipBounds();
    for (int i = 0; i < n; i++) {

      tempRect.x = x + getOffset(X_AXIS, i);
      // tempRect.y = y + getOffset(Y_AXIS, i);
      tempRect.width = getSpan(X_AXIS, i);
      tempRect.height = getSpan(Y_AXIS, i);
      // System.err.println("For line " + i + ": tempRect==" + tempRect);

      if (tempRect.intersects(clip)) {
        Element lineElement = root.getElement(i);
        int startOffset = lineElement.getStartOffset();
        int endOffset = lineElement.getEndOffset() - 1; // Why always "-1"?
        View view = getView(i);
        if (!useSelectedTextColor
            || selStart == selEnd
            || (startOffset >= selEnd || endOffset < selStart)) {
          drawView(painter, g2d, alloc, view, fontHeight, tempRect.y + ascent);
        } else {
          // System.out.println("Drawing line with selection: " + i);
          drawViewWithSelection(
              painter, g2d, alloc, view, fontHeight, tempRect.y + ascent, selStart, selEnd);
        }
      }

      tempRect.y += tempRect.height;

      Fold possibleFold = fm.getFoldForLine(i);
      if (possibleFold != null && possibleFold.isCollapsed()) {
        i += possibleFold.getCollapsedLineCount();
        // Visible indicator of collapsed lines
        Color c = RSyntaxUtilities.getFoldedLineBottomColor(host);
        if (c != null) {
          g.setColor(c);
          g.drawLine(x, tempRect.y - 1, alloc.width, tempRect.y - 1);
        }
      }
    }
  }
  /** Overridden to allow for folded regions. */
  @Override
  protected View getViewAtPoint(int x, int y, Rectangle alloc) {

    int lineCount = getViewCount();
    int curY = alloc.y + getOffset(Y_AXIS, 0); // Always at least 1 line
    host = (RSyntaxTextArea) getContainer();
    FoldManager fm = host.getFoldManager();

    for (int line = 1; line < lineCount; line++) {
      int span = getSpan(Y_AXIS, line - 1);
      if (y < curY + span) {
        childAllocation2(line - 1, curY, alloc);
        return getView(line - 1);
      }
      curY += span;
      Fold fold = fm.getFoldForLine(line - 1);
      if (fold != null && fold.isCollapsed()) {
        line += fold.getCollapsedLineCount();
      }
    }

    // Not found - return last line's view.
    childAllocation2(lineCount - 1, curY, alloc);
    return getView(lineCount - 1);
  }
  /**
   * Returns the "deepest" open fold containing the specified offset. It is assumed that it's
   * already been verified that <code>offs</code> is indeed contained in this fold.
   *
   * @param offs The offset.
   * @return The fold, or <code>null</code> if no open fold contains the offset.
   * @see FoldManager#getDeepestOpenFoldContaining(int)
   */
  Fold getDeepestOpenFoldContaining(int offs) {

    Fold deepestFold = this;

    for (int i = 0; i < getChildCount(); i++) {
      Fold fold = getChild(i);
      if (fold.containsOffset(offs)) {
        if (fold.isCollapsed()) {
          break;
        }
        deepestFold = fold.getDeepestOpenFoldContaining(offs);
        break;
      }
    }

    return deepestFold;
  }
  /**
   * Overridden to show the content of a collapsed fold on mouse-overs.
   *
   * @param e The mouse location.
   */
  public String getToolTipText(MouseEvent e) {

    String text = null;

    RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
    if (rsta.isCodeFoldingEnabled()) {
      FoldManager fm = rsta.getFoldManager();
      int pos = rsta.viewToModel(new Point(0, e.getY()));
      if (pos >= 0) { // Not -1
        int line = 0;
        try {
          line = rsta.getLineOfOffset(pos);
        } catch (BadLocationException ble) {
          ble.printStackTrace(); // Never happens
          return null;
        }
        Fold fold = fm.getFoldForLine(line);
        if (fold != null && fold.isCollapsed()) {

          int endLine = fold.getEndLine();
          if (fold.getLineCount() > 25) { // Not too big
            endLine = fold.getStartLine() + 25;
          }

          StringBuffer sb = new StringBuffer("<html><nobr>");
          while (line <= endLine && line < rsta.getLineCount()) { // Sanity
            Token t = rsta.getTokenListForLine(line);
            while (t != null && t.isPaintable()) {
              t.appendHTMLRepresentation(sb, rsta, true, true);
              t = t.getNextToken();
            }
            sb.append("<br>");
            line++;
          }

          text = sb.toString();
        }
      }
    }

    return text;
  }
  /**
   * Fetches the allocation for the given child view to render into.
   *
   * <p>Overridden to account for lines hidden by collapsed folded regions.
   *
   * @param line The index of the child, >= 0 && < getViewCount()
   * @param a The allocation to this view
   * @return The allocation to the child
   */
  public Shape getChildAllocationImpl(int line, Shape a) {

    Rectangle alloc = getInsideAllocation(a);
    host = (RSyntaxTextArea) getContainer();
    FoldManager fm = host.getFoldManager();
    int y = alloc.y;

    // TODO: Make cached getOffset() calls for Y_AXIS valid even for
    // folding, to speed this up!
    for (int i = 0; i < line; i++) {
      y += getSpan(Y_AXIS, i);
      Fold fold = fm.getFoldForLine(i);
      if (fold != null && fold.isCollapsed()) {
        i += fold.getCollapsedLineCount();
      }
    }

    childAllocation2(line, y, alloc);
    return alloc;
  }
  /**
   * Paints the word-wrapped text.
   *
   * @param g The graphics context in which to paint.
   * @param a The shape (usually a rectangle) in which to paint.
   */
  public void paint(Graphics g, Shape a) {

    Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a : a.getBounds();
    tabBase = alloc.x;

    Graphics2D g2d = (Graphics2D) g;
    host = (RSyntaxTextArea) getContainer();
    int ascent = host.getMaxAscent();
    int fontHeight = host.getLineHeight();
    FoldManager fm = host.getFoldManager();

    int n = getViewCount(); // Number of lines.
    int x = alloc.x + getLeftInset();
    tempRect.y = alloc.y + getTopInset();
    Rectangle clip = g.getClipBounds();
    for (int i = 0; i < n; i++) {
      tempRect.x = x + getOffset(X_AXIS, i);
      // tempRect.y = y + getOffset(Y_AXIS, i);
      tempRect.width = getSpan(X_AXIS, i);
      tempRect.height = getSpan(Y_AXIS, i);
      // System.err.println("For line " + i + ": tempRect==" + tempRect);
      if (tempRect.intersects(clip)) {
        View view = getView(i);
        drawView(g2d, alloc, view, fontHeight, tempRect.y + ascent);
      }
      tempRect.y += tempRect.height;
      Fold possibleFold = fm.getFoldForLine(i);
      if (possibleFold != null && possibleFold.isCollapsed()) {
        i += possibleFold.getCollapsedLineCount();
        // Visible indicator of collapsed lines
        Color c = RSyntaxUtilities.getFoldedLineBottomColor(host);
        if (c != null) {
          g.setColor(c);
          g.drawLine(x, tempRect.y - 1, alloc.width, tempRect.y - 1);
        }
      }
    }
  }
  /**
   * Paints folding icons when line wrapping is enabled.
   *
   * @param g The graphics context.
   */
  private void paintComponentWrapped(Graphics g) {

    // The variables we use are as follows:
    // - visibleRect is the "visible" area of the text area; e.g.
    // [0,100, 300,100+(lineCount*cellHeight)-1].
    // actualTop.y is the topmost-pixel in the first logical line we
    // paint.  Note that we may well not paint this part of the logical
    // line, as it may be broken into many physical lines, with the first
    // few physical lines scrolled past.  Note also that this is NOT the
    // visible rect of this line number list; this line number list has
    // visible rect == [0,0, insets.left-1,visibleRect.height-1].
    // - offset (<=0) is the y-coordinate at which we begin painting when
    // we begin painting with the first logical line.  This can be
    // negative, signifying that we've scrolled past the actual topmost
    // part of this line.

    // The algorithm is as follows:
    // - Get the starting y-coordinate at which to paint.  This may be
    //   above the first visible y-coordinate as we're in line-wrapping
    //   mode, but we always paint entire logical lines.
    // - Paint that line's indicator, if appropriate.  Increment y to be
    //   just below the are we just painted (i.e., the beginning of the
    //   next logical line's view area).
    // - Get the ending visual position for that line.  We can now loop
    //   back, paint this line, and continue until our y-coordinate is
    //   past the last visible y-value.

    // We avoid using modelToView/viewToModel where possible, as these
    // methods trigger a parsing of the line into syntax tokens, which is
    // costly.  It's cheaper to just grab the child views' bounds.

    // Some variables we'll be using.
    int width = getWidth();

    RTextAreaUI ui = (RTextAreaUI) textArea.getUI();
    View v = ui.getRootView(textArea).getView(0);
    Document doc = textArea.getDocument();
    Element root = doc.getDefaultRootElement();
    int topPosition = textArea.viewToModel(new Point(visibleRect.x, visibleRect.y));
    int topLine = root.getElementIndex(topPosition);
    int cellHeight = textArea.getLineHeight();
    FoldManager fm = ((RSyntaxTextArea) textArea).getFoldManager();

    // Compute the y at which to begin painting text, taking into account
    // that 1 logical line => at least 1 physical line, so it may be that
    // y<0.  The computed y-value is the y-value of the top of the first
    // (possibly) partially-visible view.
    Rectangle visibleEditorRect = ui.getVisibleEditorRect();
    Rectangle r = LineNumberList.getChildViewBounds(v, topLine, visibleEditorRect);
    int y = r.y;
    y += (cellHeight - collapsedFoldIcon.getIconHeight()) / 2;

    int visibleBottom = visibleRect.y + visibleRect.height;
    int x = width - 10;
    int line = topLine;
    boolean paintingOutlineLine =
        foldWithOutlineShowing != null && foldWithOutlineShowing.containsLine(line);
    int lineCount = root.getElementCount();

    while (y < visibleBottom && line < lineCount) {

      int curLineH = LineNumberList.getChildViewBounds(v, line, visibleEditorRect).height;

      if (paintingOutlineLine) {
        g.setColor(getForeground());
        int w2 = width / 2;
        if (line == foldWithOutlineShowing.getEndLine()) {
          int y2 = y + curLineH - cellHeight / 2;
          g.drawLine(w2, y, w2, y2);
          g.drawLine(w2, y2, width - 2, y2);
          paintingOutlineLine = false;
        } else {
          g.drawLine(w2, y, w2, y + curLineH);
        }
      }
      Fold fold = fm.getFoldForLine(line);
      if (fold != null) {
        if (fold == foldWithOutlineShowing && !fold.isCollapsed()) {
          g.setColor(getForeground());
          int w2 = width / 2;
          g.drawLine(w2, y + cellHeight / 2, w2, y + curLineH);
          paintingOutlineLine = true;
        }
        if (fold.isCollapsed()) {
          collapsedFoldIcon.paintIcon(this, g, x, y);
          y += LineNumberList.getChildViewBounds(v, line, visibleEditorRect).height;
          line += fold.getLineCount() + 1;
        } else {
          expandedFoldIcon.paintIcon(this, g, x, y);
          y += curLineH;
          line++;
        }
      } else {
        y += curLineH;
        line++;
      }
    }
  }
  protected void paintComponent(Graphics g) {

    if (textArea == null) {
      return;
    }

    visibleRect = g.getClipBounds(visibleRect);
    if (visibleRect == null) { // ???
      visibleRect = getVisibleRect();
    }
    // System.out.println("IconRowHeader repainting: " + visibleRect);
    if (visibleRect == null) {
      return;
    }

    Color bg = getBackground();
    if (getGutter() != null) { // Should always be true
      bg = getGutter().getBackground();
    }
    g.setColor(bg);
    g.fillRect(0, visibleRect.y, getWidth(), visibleRect.height);

    RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
    if (!rsta.isCodeFoldingEnabled()) {
      return; // We should be hidden in this case, but still...
    }

    if (textArea.getLineWrap()) {
      paintComponentWrapped(g);
      return;
    }

    // Get where to start painting (top of the row).
    // We need to be "scrolled up" up just enough for the missing part of
    // the first line.
    int cellHeight = textArea.getLineHeight();
    int topLine = visibleRect.y / cellHeight;
    int y = topLine * cellHeight + (cellHeight - collapsedFoldIcon.getIconHeight()) / 2;
    textAreaInsets = textArea.getInsets(textAreaInsets);
    if (textAreaInsets != null) {
      y += textAreaInsets.top;
    }

    // Get the first and last lines to paint.
    FoldManager fm = rsta.getFoldManager();
    topLine += fm.getHiddenLineCountAbove(topLine, true);

    int width = getWidth();
    int x = width - 10;
    int line = topLine;
    boolean paintingOutlineLine =
        foldWithOutlineShowing != null && foldWithOutlineShowing.containsLine(line);

    while (y < visibleRect.y + visibleRect.height) {
      if (paintingOutlineLine) {
        g.setColor(getForeground());
        int w2 = width / 2;
        if (line == foldWithOutlineShowing.getEndLine()) {
          int y2 = y + cellHeight / 2;
          g.drawLine(w2, y, w2, y2);
          g.drawLine(w2, y2, width - 2, y2);
          paintingOutlineLine = false;
        } else {
          g.drawLine(w2, y, w2, y + cellHeight);
        }
      }
      Fold fold = fm.getFoldForLine(line);
      if (fold != null) {
        if (fold == foldWithOutlineShowing && !fold.isCollapsed()) {
          g.setColor(getForeground());
          int w2 = width / 2;
          g.drawLine(w2, y + cellHeight / 2, w2, y + cellHeight);
          paintingOutlineLine = true;
        }
        if (fold.isCollapsed()) {
          collapsedFoldIcon.paintIcon(this, g, x, y);
          // Skip to next line to paint, taking extra care for lines with
          // block ends and begins together, e.g. "} else {"
          do {
            line += fold.getLineCount();
            fold = fm.getFoldForLine(line);
          } while (fold != null && fold.isCollapsed());
        } else {
          expandedFoldIcon.paintIcon(this, g, x, y);
        }
      }
      line++;
      y += cellHeight;
    }
  }
  /**
   * Actually paints the text area. Only lines that have been damaged are repainted.
   *
   * @param g The graphics context with which to paint.
   * @param a The allocated region in which to render.
   */
  @Override
  public void paint(Graphics g, Shape a) {

    RSyntaxDocument document = (RSyntaxDocument) getDocument();

    Rectangle alloc = a.getBounds();

    tabBase = alloc.x;
    host = (RSyntaxTextArea) getContainer();

    Rectangle clip = g.getClipBounds();
    // An attempt to speed things up for files with long lines.  Note that
    // this will actually slow things down a bit for the common case of
    // regular-length lines, but it doesn't make a perceivable difference.
    clipStart = clip.x;
    clipEnd = clipStart + clip.width;

    lineHeight = host.getLineHeight();
    ascent = host.getMaxAscent(); // metrics.getAscent();
    int heightAbove = clip.y - alloc.y;
    int linesAbove = Math.max(0, heightAbove / lineHeight);

    FoldManager fm = host.getFoldManager();
    linesAbove += fm.getHiddenLineCountAbove(linesAbove, true);
    Rectangle lineArea = lineToRect(a, linesAbove);
    int y = lineArea.y + ascent;
    int x = lineArea.x;
    Element map = getElement();
    int lineCount = map.getElementCount();

    // Whether token styles should always be painted, even in selections
    int selStart = host.getSelectionStart();
    int selEnd = host.getSelectionEnd();

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

    Graphics2D g2d = (Graphics2D) g;
    Token token;
    // System.err.println("Painting lines: " + linesAbove + " to " + (endLine-1));

    TokenPainter painter = host.getTokenPainter();
    int line = linesAbove;
    // int count = 0;
    while (y < clip.y + clip.height + ascent && line < lineCount) {

      Fold fold = fm.getFoldForLine(line);
      Element lineElement = map.getElement(line);
      int startOffset = lineElement.getStartOffset();
      // int endOffset = (line==lineCount ? lineElement.getEndOffset()-1 :
      //							lineElement.getEndOffset()-1);
      int endOffset = lineElement.getEndOffset() - 1; // Why always "-1"?
      h.paintLayeredHighlights(g2d, startOffset, endOffset, a, host, this);

      // Paint a line of text.
      token = document.getTokenListForLine(line);
      if (selStart == selEnd || startOffset >= selEnd || endOffset < selStart) {
        drawLine(painter, token, g2d, x, y, line);
      } else {
        // System.out.println("Drawing line with selection: " + line);
        drawLineWithSelection(painter, token, g2d, x, y, selStart, selEnd);
      }

      if (fold != null && fold.isCollapsed()) {

        // Visible indicator of collapsed lines
        Color c = RSyntaxUtilities.getFoldedLineBottomColor(host);
        if (c != null) {
          g.setColor(c);
          g.drawLine(x, y + lineHeight - ascent - 1, host.getWidth(), y + lineHeight - ascent - 1);
        }

        // Skip to next line to paint, taking extra care for lines with
        // block ends and begins together, e.g. "} else {"
        do {
          int hiddenLineCount = fold.getLineCount();
          if (hiddenLineCount == 0) {
            // Fold parser identified a zero-line fold region.
            // This is really a bug, but we'll be graceful here
            // and avoid an infinite loop.
            break;
          }
          line += hiddenLineCount;
          fold = fm.getFoldForLine(line);
        } while (fold != null && fold.isCollapsed());
      }

      y += lineHeight;
      line++;
      // count++;

    }

    // System.out.println("SyntaxView: lines painted=" + count);

  }