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); } } } }