/** * If <code>quickEvaluationListener</code> is provided, quick approximate size evaluation becomes enabled, listener will be invoked * if approximation will in fact be used during width calculation. */ int getMaxWidthInLineRange(int startVisualLine, int endVisualLine, @Nullable Runnable quickEvaluationListener) { int maxWidth = 0; endVisualLine = Math.min(endVisualLine, myEditor.getVisibleLineCount() - 1); for (int i = startVisualLine; i <= endVisualLine; i++) { int startOffset = myMapper.visualLineToOffset(i); float x = 0; int maxOffset = 0; for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(this, startOffset, false, quickEvaluationListener)) { x = fragment.getEndX(); maxOffset = Math.max(maxOffset, fragment.getMaxOffset()); } if (myEditor.getSoftWrapModel().getSoftWrap(maxOffset) != null) { x += myEditor.getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); } maxWidth = Math.max(maxWidth, (int) x); } return maxWidth; }
@NotNull private SoftWrapImpl registerSoftWrap(int offset, int spaceSize, LogicalLineData lineData) { int indentInColumns = 0; int indentInPixels = myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP); if (myCustomIndentUsedLastTime) { indentInColumns = myCustomIndentValueUsedLastTime + lineData.indentInColumns; indentInPixels += lineData.indentInPixels + (myCustomIndentValueUsedLastTime * spaceSize); } SoftWrapImpl result = new SoftWrapImpl( new TextChangeImpl("\n" + StringUtil.repeatSymbol(' ', indentInColumns), offset, offset), indentInColumns + 1/* for 'after soft wrap' drawing */, indentInPixels ); myStorage.storeOrReplace(result); return result; }
@NotNull Point visualPositionToXY(@NotNull VisualPosition pos) { int visualLine = pos.line; int column = pos.column; int y = visualLineToY(visualLine); float x = getStartX(visualLine); int lastColumn = 0; if (visualLine < myView.getEditor().getVisibleLineCount()) { int visualLineStartOffset = visualLineToOffset(visualLine); int maxOffset = 0; for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false, null)) { if (column < fragment.getStartVisualColumn()) { break; } int endColumn = fragment.getEndVisualColumn(); if (column <= endColumn) { return new Point((int)fragment.visualColumnToX(column), y); } x = fragment.getEndX(); lastColumn = endColumn; maxOffset = Math.max(maxOffset, fragment.getMaxOffset()); } if (myView.getEditor().getSoftWrapModel().getSoftWrap(maxOffset) != null) { column--; x += myView.getEditor().getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); } } int additionalShift = column <= lastColumn ? 0 : (column - lastColumn) * myView.getPlainSpaceWidth(); return new Point((int)(x) + additionalShift, y); }
@Override public void beforeSoftWrapLineFeed(@NotNull EditorPosition position) { int newWidth = position.x + myPainter.getMinDrawingWidth(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); myLastLogicalLine = position.logicalLine; if (!myLineWidths.contains(position.logicalLine)) { myLineWidths.put(position.logicalLine, newWidth); return; } updateLineWidthIfNecessary(position.logicalLine, newWidth); }
@Override public int calcColumnNumber(@NotNull CharSequence text, int start, int offset, int tabSize) { IterationState state = new IterationState(this, start, start + offset, false); try { int fontType = state.getMergedAttributes().getFontType(); int column = 0; int x = 0; int spaceSize = EditorUtil.getSpaceWidth(fontType, this); for (int i = start; i < offset; i++) { if (i >= state.getEndOffset()) { state.advance(); fontType = state.getMergedAttributes().getFontType(); } SoftWrap softWrap = getSoftWrapModel().getSoftWrap(i); if (softWrap != null) { column++; // For 'after soft wrap' drawing. x = getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP); } char c = text.charAt(i); if (c == '\t') { int prevX = x; x = EditorUtil.nextTabStop(x, this); column += EditorUtil.columnsNumber(c, x, prevX, spaceSize); } else { x += EditorUtil.charWidth(c, fontType, this); column++; } } return column; } finally { state.dispose(); } }
@Nonnull Point2D visualPositionToXY(@Nonnull VisualPosition pos) { int visualLine = pos.line; int column = pos.column; int y = visualLineToY(visualLine); float x = getStartX(visualLine); int lastColumn = 0; if (visualLine < myView.getEditor().getVisibleLineCount()) { int visualLineStartOffset = visualLineToOffset(visualLine); int maxOffset = 0; for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLineStartOffset, false)) { int startVisualColumn = fragment.getStartVisualColumn(); if (column < startVisualColumn || column == startVisualColumn && !pos.leansRight) { break; } int endColumn = fragment.getEndVisualColumn(); if (column < endColumn || column == endColumn && !pos.leansRight) { return new Point2D.Float(fragment.visualColumnToX(column), y); } x = fragment.getEndX(); lastColumn = endColumn; maxOffset = Math.max(maxOffset, fragment.getMaxOffset()); } if (column > lastColumn && myView.getEditor().getSoftWrapModel().getSoftWrap(maxOffset) != null) { column--; x += myView.getEditor().getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); } } int additionalShift = column <= lastColumn ? 0 : (column - lastColumn) * myView.getPlainSpaceWidth(); return new Point2D.Float(x + additionalShift, y); }
/** * End user is allowed to perform selection by visual coordinates (e.g. by dragging mouse with left button hold). There is a possible * case that such a move intersects with soft wrap introduced virtual space. We want to draw corresponding selection background * there then. * <p/> * This method encapsulates functionality of drawing selection background on the first soft wrap line (e.g. on a visual line where * it is applied). * * @param g graphics to draw on * @param position current position (assumed to be position of soft wrap appliance) * @param clip target drawing area boundaries * @param defaultBackground default background * @param fontType current font type */ private void paintSelectionOnFirstSoftWrapLineIfNecessary(@NotNull Graphics g, @NotNull Point position, @NotNull Rectangle clip, @NotNull Color defaultBackground, @JdkConstants.FontStyle int fontType) { // There is a possible case that the user performed selection at soft wrap virtual space. We need to paint corresponding background // there then. VisualPosition selectionStartPosition = getSelectionStartPositionForPaint(); VisualPosition selectionEndPosition = getSelectionEndPositionForPaint(); if (selectionStartPosition.equals(selectionEndPosition)) { return; } int currentVisualLine = position.y / getLineHeight(); int lastColumn = EditorUtil.getLastVisualLineColumnNumber(this, currentVisualLine); // Check if the first soft wrap line is within the visual selection. if (currentVisualLine < selectionStartPosition.line || currentVisualLine > selectionEndPosition.line || currentVisualLine == selectionEndPosition.line && selectionEndPosition.column <= lastColumn) { return; } // Adjust 'x' if selection starts at soft wrap virtual space. final int columnsToSkip = selectionStartPosition.column - lastColumn; if (columnsToSkip > 0) { position.x += getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); position.x += (columnsToSkip - 1) * EditorUtil.getSpaceWidth(Font.PLAIN, this); } // Calculate selection width. final int width; if (selectionEndPosition.line > currentVisualLine) { width = clip.x + clip.width - position.x; } else if (selectionStartPosition.line < currentVisualLine || selectionStartPosition.column <= lastColumn) { width = getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED) + (selectionEndPosition.column - lastColumn - 1) * EditorUtil.getSpaceWidth(fontType, this); } else { width = (selectionEndPosition.column - selectionStartPosition.column) * EditorUtil.getSpaceWidth(fontType, this); } drawBackground(g, getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR), width, position, defaultBackground, clip); }
private void doRecalculateSoftWraps(IncrementalCacheUpdateEvent event) { myEventBeingProcessed = event; notifyListenersOnCacheUpdateStart(event); // Preparation. myContext.reset(); myOffset2fontType.clear(); myOffset2widthInPixels.clear(); // Define start of the visual line that holds target range start. final int start = event.getStartOffset(); final LogicalPosition logical = event.getStartLogicalPosition(); Document document = myEditor.getDocument(); myContext.text = document.getCharsSequence(); myContext.tokenStartOffset = start; IterationState iterationState = new IterationState(myEditor, start, document.getTextLength(), false); TextAttributes attributes = iterationState.getMergedAttributes(); myContext.fontType = attributes.getFontType(); myContext.rangeEndOffset = event.getMandatoryEndOffset(); EditorPosition position = myEditor.myUseNewRendering ? new EditorPosition(logical, event.getStartVisualPosition(), start, myEditor) : new EditorPosition(logical, start, myEditor); position.x = start == 0 ? myEditor.getPrefixTextWidthInPixels() : 0; int spaceWidth = EditorUtil.getSpaceWidth(myContext.fontType, myEditor); int plainSpaceWidth = EditorUtil.getSpaceWidth(Font.PLAIN, myEditor); myContext.logicalLineData.update(logical.line, spaceWidth, plainSpaceWidth); myContext.currentPosition = position; myContext.lineStartPosition = position.clone(); myContext.fontType2spaceWidth.put(myContext.fontType, spaceWidth); myContext.softWrapStartOffset = position.offset; myContext.reservedWidthInPixels = myPainter.getMinDrawingWidth(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); SoftWrap softWrapAtStartPosition = myStorage.getSoftWrap(start); if (softWrapAtStartPosition != null) { myContext.currentPosition.x = softWrapAtStartPosition.getIndentInPixels(); myContext.lineStartPosition.visualColumn = 0; myContext.lineStartPosition.softWrapColumnDiff -= softWrapAtStartPosition.getIndentInColumns(); myContext.softWrapStartOffset++; notifyListenersOnVisualLineStart(myContext.lineStartPosition); } // Perform soft wraps calculation. while (!iterationState.atEnd()) { FoldRegion currentFold = iterationState.getCurrentFold(); if (currentFold == null) { myContext.tokenEndOffset = iterationState.getEndOffset(); if (processNonFoldToken()) { break; } } else { if (processCollapsedFoldRegion(currentFold)) { break; } // 'myOffset2widthInPixels' contains information necessary to processing soft wraps that lay before the current offset. // We do know that soft wraps are not allowed to go backward after processed collapsed fold region, hence, we drop // information about processed symbols width. myOffset2widthInPixels.clear(); } iterationState.advance(); attributes = iterationState.getMergedAttributes(); myContext.fontType = attributes.getFontType(); myContext.tokenStartOffset = iterationState.getStartOffset(); myOffset2fontType.fill(myContext.tokenStartOffset, iterationState.getEndOffset(), myContext.fontType); } if (myContext.delayedSoftWrap != null) { myStorage.remove(myContext.delayedSoftWrap); } notifyListenersOnVisualLineEnd(); event.setActualEndOffset(myContext.currentPosition.offset); validateFinalPosition(event); notifyListenersOnCacheUpdateEnd(event); myEventBeingProcessed = null; }
@Override public int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { return 0; }
@Override public int getMinDrawingWidthInPixels(@NotNull SoftWrapDrawingType drawingType) { return 0; }
/** * End user is allowed to perform selection by visual coordinates (e.g. by dragging mouse with left button hold). There is a possible * case that such a move intersects with soft wrap introduced virtual space. We want to draw corresponding selection background * there then. * <p/> * This method encapsulates functionality of drawing selection background on the first soft wrap line (e.g. on a visual line where * it is applied). * * @param g graphics to draw on * @param position current position (assumed to be position of soft wrap appliance) * @param clip target drawing area boundaries * @param defaultBackground default background * @param fontType current font type */ private void paintSelectionOnFirstSoftWrapLineIfNecessary(@NotNull Graphics g, @NotNull Point position, @NotNull Rectangle clip, @NotNull Color defaultBackground, @JdkConstants.FontStyle int fontType) { // There is a possible case that the user performed selection at soft wrap virtual space. We need to paint corresponding background // there then. VisualPosition selectionStartPosition = getSelectionModel().getSelectionStartPosition(); VisualPosition selectionEndPosition = getSelectionModel().getSelectionEndPosition(); if (selectionStartPosition == null || selectionEndPosition == null || selectionStartPosition.equals(selectionEndPosition)) { return; } int currentVisualLine = position.y / getLineHeight(); int lastColumn = EditorUtil.getLastVisualLineColumnNumber(this, currentVisualLine); // Check if the first soft wrap line is within the visual selection. if (currentVisualLine < selectionStartPosition.line || currentVisualLine > selectionEndPosition.line || currentVisualLine == selectionEndPosition.line && selectionEndPosition.column <= lastColumn) { return; } // Adjust 'x' if selection starts at soft wrap virtual space. final int columnsToSkip = selectionStartPosition.column - lastColumn; if (columnsToSkip > 0) { position.x += getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); position.x += (columnsToSkip - 1) * EditorUtil.getSpaceWidth(Font.PLAIN, this); } // Calculate selection width. final int width; if (selectionEndPosition.line > currentVisualLine) { width = clip.x + clip.width - position.x; } else if (selectionStartPosition.line < currentVisualLine || selectionStartPosition.column <= lastColumn) { width = getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED) + (selectionEndPosition.column - lastColumn - 1) * EditorUtil.getSpaceWidth(fontType, this); } else { width = (selectionEndPosition.column - selectionStartPosition.column) * EditorUtil.getSpaceWidth(fontType, this); } drawBackground(g, getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR), width, position, defaultBackground, clip); }
@Override public int paint(@Nonnull Graphics g, @Nonnull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { return 0; }
@Override public int getMinDrawingWidthInPixels(@Nonnull SoftWrapDrawingType drawingType) { return 0; }
/** * Asks to paint drawing of target type at the given graphics buffer at the given position. * * @param g target graphics buffer to draw in * @param drawingType target drawing type * @param x target <code>'x'</code> coordinate to use * @param y target <code>'y'</code> coordinate to use * @param lineHeight line height used at editor * @return painted drawing width */ int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight);
/** * Allows to ask for the minimal width in pixels required for painting of the given type. * * @param drawingType target drawing type * @return width in pixels required for the painting of the given type */ int getMinDrawingWidthInPixels(@NotNull SoftWrapDrawingType drawingType);
/** * Asks to paint drawing of target type at the given graphics buffer at the given position. * * @param g target graphics buffer to draw in * @param drawingType target drawing type * @param x target <code>'x'</code> coordinate to use * @param y target <code>'y'</code> coordinate to use * @param lineHeight line height used at editor * @return painted drawing width */ int paint(@Nonnull Graphics g, @Nonnull SoftWrapDrawingType drawingType, int x, int y, int lineHeight);
/** * Allows to ask for the minimal width in pixels required for painting of the given type. * * @param drawingType target drawing type * @return width in pixels required for the painting of the given type */ int getMinDrawingWidthInPixels(@Nonnull SoftWrapDrawingType drawingType);