private void paintBackground( Graphics2D g, Rectangle clip, int startVisualLine, int endVisualLine) { int lineCount = myEditor.getVisibleLineCount(); final Map<Integer, Couple<Integer>> virtualSelectionMap = createVirtualSelectionMap(startVisualLine, endVisualLine); for (int visualLine = startVisualLine; visualLine <= endVisualLine; visualLine++) { int y = myView.visualLineToY(visualLine); LineLayout prefixLayout = myView.getPrefixLayout(); if (visualLine == 0 && prefixLayout != null) { paintBackground(g, myView.getPrefixAttributes(), 0, y, prefixLayout.getWidth()); } if (visualLine >= lineCount) break; paintLineFragments( g, clip, visualLine, y, new LineFragmentPainter() { @Override public void paintBeforeLineStart( Graphics2D g, TextAttributes attributes, int columnEnd, float xEnd, int y) { paintBackground(g, attributes, 0, y, xEnd); paintSelectionOnSecondSoftWrapLineIfNecessary(g, columnEnd, xEnd, y); } @Override public void paint( Graphics2D g, VisualLineFragmentsIterator.Fragment fragment, int start, int end, TextAttributes attributes, float xStart, float xEnd, int y) { paintBackground(g, attributes, xStart, y, xEnd - xStart); } @Override public void paintAfterLineEnd( Graphics2D g, Rectangle clip, IterationState it, int columnStart, float x, int y) { paintBackground( g, it.getPastLineEndBackgroundAttributes(), x, y, clip.x + clip.width - x); int offset = it.getEndOffset(); SoftWrap softWrap = myEditor.getSoftWrapModel().getSoftWrap(offset); if (softWrap == null) { paintVirtualSelectionIfNecessary( g, virtualSelectionMap, columnStart, x, clip.x + clip.width, y); } else { paintSelectionOnFirstSoftWrapLineIfNecessary( g, columnStart, x, clip.x + clip.width, y); } } }); } }
private void paintLineMarkerSeparator(RangeHighlighter marker, Rectangle clip, Graphics g) { Color separatorColor = marker.getLineSeparatorColor(); LineSeparatorRenderer lineSeparatorRenderer = marker.getLineSeparatorRenderer(); if (separatorColor == null && lineSeparatorRenderer == null) { return; } int line = myDocument.getLineNumber( marker.getLineSeparatorPlacement() == SeparatorPlacement.TOP ? marker.getStartOffset() : marker.getEndOffset()); int visualLine = myView.logicalToVisualPosition( new LogicalPosition( line + (marker.getLineSeparatorPlacement() == SeparatorPlacement.TOP ? 0 : 1), 0), false) .line; int y = myView.visualLineToY(visualLine) - 1; int endShift = clip.x + clip.width; EditorSettings settings = myEditor.getSettings(); if (settings.isRightMarginShown() && myEditor.getColorsScheme().getColor(EditorColors.RIGHT_MARGIN_COLOR) != null) { endShift = Math.min( endShift, settings.getRightMargin(myEditor.getProject()) * myView.getPlainSpaceWidth()); } g.setColor(separatorColor); if (lineSeparatorRenderer != null) { lineSeparatorRenderer.drawLine(g, 0, endShift, y); } else { UIUtil.drawLine(g, 0, y, endShift, y); } }
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); } } } }
private void paintTextWithEffects( Graphics2D g, Rectangle clip, int startVisualLine, int endVisualLine) { final CharSequence text = myDocument.getImmutableCharSequence(); final EditorImpl.LineWhitespacePaintingStrategy whitespacePaintingStrategy = myEditor.new LineWhitespacePaintingStrategy(); int lineCount = myEditor.getVisibleLineCount(); for (int visualLine = startVisualLine; visualLine <= endVisualLine; visualLine++) { int y = myView.visualLineToY(visualLine) + myView.getAscent(); LineLayout prefixLayout = myView.getPrefixLayout(); if (visualLine == 0 && prefixLayout != null) { g.setColor(myView.getPrefixAttributes().getForegroundColor()); paintLineLayoutWithEffect( g, prefixLayout, 0, y, myView.getPrefixAttributes().getEffectColor(), myView.getPrefixAttributes().getEffectType()); } if (visualLine >= lineCount) break; final int[] currentLogicalLine = new int[] {-1}; paintLineFragments( g, clip, visualLine, y, new LineFragmentPainter() { @Override public void paintBeforeLineStart( Graphics2D g, TextAttributes attributes, int columnEnd, float xEnd, int y) { SoftWrapModelImpl softWrapModel = myEditor.getSoftWrapModel(); int symbolWidth = softWrapModel.getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP); softWrapModel.paint( g, SoftWrapDrawingType.AFTER_SOFT_WRAP, (int) xEnd - symbolWidth, y - myView.getAscent(), myView.getLineHeight()); } @Override public void paint( Graphics2D g, VisualLineFragmentsIterator.Fragment fragment, int start, int end, TextAttributes attributes, float xStart, float xEnd, int y) { if (attributes != null && attributes.getForegroundColor() != null) { g.setColor(attributes.getForegroundColor()); fragment.draw(g, xStart, y, start, end); } if (fragment.getCurrentFoldRegion() == null) { int logicalLine = fragment.getStartLogicalLine(); if (logicalLine != currentLogicalLine[0]) { whitespacePaintingStrategy.update( text, myDocument.getLineStartOffset(logicalLine), myDocument.getLineEndOffset(logicalLine)); currentLogicalLine[0] = logicalLine; } paintWhitespace( g, text, xStart, y, start, end, whitespacePaintingStrategy, fragment); } if (attributes != null && hasTextEffect(attributes.getEffectColor(), attributes.getEffectType())) { paintTextEffect( g, xStart, xEnd, y, attributes.getEffectColor(), attributes.getEffectType()); } } @Override public void paintAfterLineEnd( Graphics2D g, Rectangle clip, IterationState iterationState, int columnStart, float x, int y) { int offset = iterationState.getEndOffset(); SoftWrapModelImpl softWrapModel = myEditor.getSoftWrapModel(); if (softWrapModel.getSoftWrap(offset) == null) { int logicalLine = myDocument.getLineNumber(offset); paintLineExtensions(g, logicalLine, x, y); } else { softWrapModel.paint( g, SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED, (int) x, y - myView.getAscent(), myView.getLineHeight()); } } }); } }