private void paintWhitespace(
      Graphics2D g,
      CharSequence text,
      float x,
      int y,
      int start,
      int end,
      EditorImpl.LineWhitespacePaintingStrategy whitespacePaintingStrategy,
      VisualLineFragmentsIterator.Fragment fragment) {
    g.setColor(myEditor.getColorsScheme().getColor(EditorColors.WHITESPACES_COLOR));
    boolean isRtl = fragment.isRtl();
    int baseStartOffset = fragment.getStartOffset();
    int startOffset = isRtl ? baseStartOffset - start : baseStartOffset + start;
    for (int i = start; i < end; i++) {
      int charOffset = isRtl ? baseStartOffset - i - 1 : baseStartOffset + i;
      char c = text.charAt(charOffset);
      if (" \t\u3000".indexOf(c) >= 0
          && whitespacePaintingStrategy.showWhitespaceAtOffset(charOffset)) {
        int startX =
            (int)
                fragment.offsetToX(
                    x, startOffset, isRtl ? baseStartOffset - i : baseStartOffset + i);
        int endX =
            (int)
                fragment.offsetToX(
                    x, startOffset, isRtl ? baseStartOffset - i - 1 : baseStartOffset + i + 1);

        if (c == ' ') {
          g.fillRect((startX + endX) / 2, y, 1, 1);
        } else if (c == '\t') {
          endX -= myView.getPlainSpaceWidth() / 4;
          int height = myView.getCharHeight();
          int halfHeight = height / 2;
          int mid = y - halfHeight;
          int top = y - height;
          UIUtil.drawLine(g, startX, mid, endX, mid);
          UIUtil.drawLine(g, endX, y, endX, top);
          g.fillPolygon(
              new int[] {endX - halfHeight, endX - halfHeight, endX},
              new int[] {y, y - height, y - halfHeight},
              3);
        } else if (c == '\u3000') { // ideographic space
          final int charHeight = myView.getCharHeight();
          g.drawRect(startX + 2, y - charHeight, endX - startX - 4, charHeight);
        }
      }
    }
  }
  private void paintBorderEffect(
      Graphics2D g,
      ClipDetector clipDetector,
      int startOffset,
      int endOffset,
      TextAttributes attributes) {
    if (!clipDetector.rangeCanBeVisible(startOffset, endOffset)) return;
    int startLine = myDocument.getLineNumber(startOffset);
    int endLine = myDocument.getLineNumber(endOffset);
    if (startLine + 1 == endLine
        && startOffset == myDocument.getLineStartOffset(startLine)
        && endOffset == myDocument.getLineStartOffset(endLine)) {
      // special case of line highlighters
      endLine--;
      endOffset = myDocument.getLineEndOffset(endLine);
    }

    boolean rounded = attributes.getEffectType() == EffectType.ROUNDED_BOX;
    int lineHeight = myView.getLineHeight() - 1;
    g.setColor(attributes.getEffectColor());
    VisualPosition startPosition = myView.offsetToVisualPosition(startOffset, true, false);
    VisualPosition endPosition = myView.offsetToVisualPosition(endOffset, false, true);
    if (startPosition.line == endPosition.line) {
      int y = myView.visualLineToY(startPosition.line);
      TFloatArrayList ranges = adjustedLogicalRangeToVisualRanges(startOffset, endOffset);
      for (int i = 0; i < ranges.size() - 1; i += 2) {
        int startX = (int) ranges.get(i);
        int endX = (int) ranges.get(i + 1);
        if (rounded) {
          UIUtil.drawRectPickedOut(g, startX, y, endX - startX, lineHeight);
        } else {
          g.drawRect(startX, y, endX - startX, lineHeight);
        }
      }
    } else {
      int maxWidth = myView.getMaxWidthInLineRange(startPosition.line, endPosition.line) - 1;
      TFloatArrayList leadingRanges =
          adjustedLogicalRangeToVisualRanges(
              startOffset,
              myView.visualPositionToOffset(
                  new VisualPosition(startPosition.line, Integer.MAX_VALUE, true)));
      TFloatArrayList trailingRanges =
          adjustedLogicalRangeToVisualRanges(
              myView.visualPositionToOffset(new VisualPosition(endPosition.line, 0)), endOffset);
      if (!leadingRanges.isEmpty() && !trailingRanges.isEmpty()) {
        boolean containsInnerLines = endPosition.line > startPosition.line + 1;
        int leadingTopY = myView.visualLineToY(startPosition.line);
        int leadingBottomY = leadingTopY + lineHeight;
        int trailingTopY = myView.visualLineToY(endPosition.line);
        int trailingBottomY = trailingTopY + lineHeight;
        float start = 0;
        float end = 0;
        float leftGap = leadingRanges.get(0) - (containsInnerLines ? 0 : trailingRanges.get(0));
        int adjustY =
            leftGap == 0 ? 2 : leftGap > 0 ? 1 : 0; // avoiding 1-pixel gap between aligned lines
        for (int i = 0; i < leadingRanges.size() - 1; i += 2) {
          start = leadingRanges.get(i);
          end = leadingRanges.get(i + 1);
          if (i > 0) {
            drawLine(g, leadingRanges.get(i - 1), leadingBottomY, start, leadingBottomY, rounded);
          }
          drawLine(g, start, leadingBottomY + (i == 0 ? adjustY : 0), start, leadingTopY, rounded);
          if ((i + 2) < leadingRanges.size()) {
            drawLine(g, start, leadingTopY, end, leadingTopY, rounded);
            drawLine(g, end, leadingTopY, end, leadingBottomY, rounded);
          }
        }
        end = Math.max(end, maxWidth);
        drawLine(g, start, leadingTopY, end, leadingTopY, rounded);
        drawLine(g, end, leadingTopY, end, trailingTopY - 1, rounded);
        float targetX = trailingRanges.get(trailingRanges.size() - 1);
        drawLine(g, end, trailingTopY - 1, targetX, trailingTopY - 1, rounded);
        adjustY =
            end == targetX
                ? -2
                : -1; // for lastX == targetX we need to avoid a gap when rounding is used
        for (int i = trailingRanges.size() - 2; i >= 0; i -= 2) {
          start = trailingRanges.get(i);
          end = trailingRanges.get(i + 1);

          drawLine(g, end, trailingTopY + (i == 0 ? adjustY : 0), end, trailingBottomY, rounded);
          drawLine(g, end, trailingBottomY, start, trailingBottomY, rounded);
          drawLine(g, start, trailingBottomY, start, trailingTopY, rounded);
          if (i > 0) {
            drawLine(g, start, trailingTopY, trailingRanges.get(i - 1), trailingTopY, rounded);
          }
        }
        float lastX = start;
        if (containsInnerLines) {
          if (start > 0) {
            drawLine(g, start, trailingTopY, start, trailingTopY - 1, rounded);
            drawLine(g, start, trailingTopY - 1, 0, trailingTopY - 1, rounded);
            drawLine(g, 0, trailingTopY - 1, 0, leadingBottomY + 1, rounded);
          } else {
            drawLine(g, start, trailingTopY, 0, leadingBottomY + 1, rounded);
          }
          lastX = 0;
        }
        targetX = leadingRanges.get(0);
        if (lastX < targetX) {
          drawLine(g, lastX, leadingBottomY + 1, targetX, leadingBottomY + 1, rounded);
        } else {
          drawLine(g, lastX, leadingBottomY + 1, lastX, leadingBottomY, rounded);
          drawLine(g, lastX, leadingBottomY, targetX, leadingBottomY, rounded);
        }
      }
    }
  }