/**
   * Called on editor settings change. Current model is expected to drop all cached information
   * about the settings if any.
   */
  public void reinitSettings() {
    boolean softWrapsUsedBefore = myUseSoftWraps;
    myUseSoftWraps = areSoftWrapsEnabledInEditor();

    int tabWidthBefore = myTabWidth;
    myTabWidth = EditorUtil.getTabSize(myEditor);

    boolean fontsChanged = false;
    if (!myFontPreferences.equals(myEditor.getColorsScheme().getFontPreferences())
        && myEditorTextRepresentationHelper instanceof DefaultEditorTextRepresentationHelper) {
      fontsChanged = true;
      myEditor.getColorsScheme().getFontPreferences().copyTo(myFontPreferences);
      ((DefaultEditorTextRepresentationHelper) myEditorTextRepresentationHelper)
          .clearSymbolWidthCache();
      myPainter.reinit();
    }

    if ((myUseSoftWraps ^ softWrapsUsedBefore)
        || (tabWidthBefore >= 0 && myTabWidth != tabWidthBefore)
        || fontsChanged) {
      myApplianceManager.reset();
      myDeferredFoldRegions.clear();
      myStorage.removeAll();
      myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
    }
  }
  private void paintSelectionOnFirstSoftWrapLineIfNecessary(
      Graphics2D g, int columnStart, float xStart, float xEnd, int y) {
    VisualPosition selectionStartPosition =
        myEditor.getSelectionModel().getSelectionStartPosition();
    VisualPosition selectionEndPosition = myEditor.getSelectionModel().getSelectionEndPosition();
    int visualLine = myView.yToVisualLine(y);

    if (selectionStartPosition.equals(selectionEndPosition)
        || visualLine < selectionStartPosition.line
        || visualLine > selectionEndPosition.line
        || visualLine == selectionEndPosition.line && selectionEndPosition.column <= columnStart) {
      return;
    }

    float startX =
        selectionStartPosition.line == visualLine && selectionStartPosition.column > columnStart
            ? myView.visualPositionToXY(selectionStartPosition).x
            : xStart;
    float endX =
        selectionEndPosition.line == visualLine
            ? myView.visualPositionToXY(selectionEndPosition).x
            : xEnd;

    paintBackground(
        g,
        myEditor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR),
        startX,
        y,
        endX - startX);
  }
 private void paintVirtualSelectionIfNecessary(
     Graphics2D g,
     Map<Integer, Couple<Integer>> virtualSelectionMap,
     int columnStart,
     float xStart,
     float xEnd,
     int y) {
   int visualLine = myView.yToVisualLine(y);
   Couple<Integer> selectionRange = virtualSelectionMap.get(visualLine);
   if (selectionRange == null || selectionRange.second <= columnStart) return;
   float startX =
       selectionRange.first <= columnStart
           ? xStart
           : myView.visualPositionToXY(new VisualPosition(visualLine, selectionRange.first)).x;
   float endX =
       Math.min(
           xEnd,
           myView.visualPositionToXY(new VisualPosition(visualLine, selectionRange.second)).x);
   paintBackground(
       g,
       myEditor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR),
       startX,
       y,
       endX - startX);
 }
  private boolean paintPlaceholderText(Graphics2D g) {
    CharSequence hintText = myEditor.getPlaceholder();
    EditorComponentImpl editorComponent = myEditor.getContentComponent();
    if (myDocument.getTextLength() > 0
        || hintText == null
        || hintText.length() == 0
        || KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() == editorComponent
            && !myEditor.getShowPlaceholderWhenFocused()) {
      return false;
    }

    hintText =
        SwingUtilities.layoutCompoundLabel(
            g.getFontMetrics(),
            hintText.toString(),
            null,
            0,
            0,
            0,
            0,
            editorComponent.getBounds(),
            new Rectangle(),
            new Rectangle(),
            0);
    g.setColor(myEditor.getFoldingModel().getPlaceholderAttributes().getForegroundColor());
    g.setFont(myEditor.getColorsScheme().getFont(EditorFontType.PLAIN));
    g.drawString(hintText.toString(), 0, myView.getAscent());
    return true;
  }
  public SoftWrapModelImpl(@NotNull EditorImpl editor) {
    myEditor = editor;
    myStorage = new SoftWrapsStorage();
    myPainter = new CompositeSoftWrapPainter(editor);
    myEditorTextRepresentationHelper = new DefaultEditorTextRepresentationHelper(editor);
    myDataMapper = new CachingSoftWrapDataMapper(editor, myStorage);
    myApplianceManager = new SoftWrapApplianceManager(myStorage, editor, myPainter, myDataMapper);
    myVisualSizeManager = new SoftWrapAwareVisualSizeManager(myPainter);

    myApplianceManager.addListener(myVisualSizeManager);
    myApplianceManager.addListener(
        new SoftWrapAwareDocumentParsingListenerAdapter() {
          @Override
          public void recalculationEnds() {
            for (SoftWrapChangeListener listener : mySoftWrapListeners) {
              listener.recalculationEnds();
            }
          }
        });
    myUseSoftWraps = areSoftWrapsEnabledInEditor();
    myEditor.getColorsScheme().getFontPreferences().copyTo(myFontPreferences);

    editor.addPropertyChangeListener(this, this);

    myApplianceManager.addListener(myDataMapper);
  }
 private void paintBackground(Graphics2D g, Color color, float x, int y, float width) {
   if (width <= 0
       || color == null
       || color.equals(myEditor.getColorsScheme().getDefaultBackground())
       || color.equals(myEditor.getBackgroundColor())) return;
   g.setColor(color);
   g.fillRect((int) x, y, (int) width, myView.getLineHeight());
 }
  private void paintCaret(Graphics2D g_) {
    EditorImpl.CaretRectangle[] locations = myEditor.getCaretLocations(true);
    if (locations == null) return;

    Graphics2D g = IdeBackgroundUtil.getOriginalGraphics(g_);
    int lineHeight = myView.getLineHeight();
    EditorSettings settings = myEditor.getSettings();
    Color caretColor = myEditor.getColorsScheme().getColor(EditorColors.CARET_COLOR);
    if (caretColor == null) caretColor = new JBColor(CARET_DARK, CARET_LIGHT);
    g.setColor(caretColor);
    for (EditorImpl.CaretRectangle location : locations) {
      int x = location.myPoint.x;
      int y = location.myPoint.y;
      Caret caret = location.myCaret;
      boolean isRtl = location.myIsRtl;
      if (myEditor.isInsertMode() != settings.isBlockCursor()) {
        int lineWidth = JBUI.scale(settings.getLineCursorWidth());
        g.fillRect(x, y, lineWidth, lineHeight);
        if (myDocument.getTextLength() > 0
            && caret != null
            && !myView.getLineLayout(caret.getLogicalPosition().line).isLtr()) {
          g.fillPolygon(
              new int[] {
                isRtl ? x + lineWidth : x,
                isRtl ? x + lineWidth - CARET_DIRECTION_MARK_SIZE : x + CARET_DIRECTION_MARK_SIZE,
                isRtl ? x + lineWidth : x
              },
              new int[] {y, y, y + CARET_DIRECTION_MARK_SIZE},
              3);
        }
      } else {
        int width = location.myWidth;
        int startX = Math.max(0, isRtl ? x - width : x);
        g.fillRect(startX, y, width, lineHeight - 1);
        if (myDocument.getTextLength() > 0 && caret != null) {
          int targetVisualColumn = caret.getVisualPosition().column;
          for (VisualLineFragmentsIterator.Fragment fragment :
              VisualLineFragmentsIterator.create(myView, caret.getVisualLineStart(), false)) {
            int startVisualColumn = fragment.getStartVisualColumn();
            int endVisualColumn = fragment.getEndVisualColumn();
            if (startVisualColumn < targetVisualColumn && endVisualColumn > targetVisualColumn
                || startVisualColumn == targetVisualColumn && !isRtl
                || endVisualColumn == targetVisualColumn && isRtl) {
              g.setColor(ColorUtil.isDark(caretColor) ? CARET_LIGHT : CARET_DARK);
              fragment.draw(
                  g,
                  startX,
                  y + myView.getAscent(),
                  targetVisualColumn - startVisualColumn - (isRtl ? 1 : 0),
                  targetVisualColumn - startVisualColumn + (isRtl ? 0 : 1));
              break;
            }
          }
        }
      }
    }
  }
  private void paintRightMargin(Graphics g, Rectangle clip) {
    EditorSettings settings = myEditor.getSettings();
    Color rightMargin = myEditor.getColorsScheme().getColor(EditorColors.RIGHT_MARGIN_COLOR);
    if (!settings.isRightMarginShown() || rightMargin == null) return;

    int x = settings.getRightMargin(myEditor.getProject()) * myView.getPlainSpaceWidth();
    g.setColor(rightMargin);
    UIUtil.drawLine(g, x, clip.y, x, clip.y + clip.height);
  }
  public TextAttributes getTextAttributes() {
    if (myTextAttributes == null) {
      myTextAttributes = new TextAttributes();
      myTextAttributes.setBackgroundColor(
          myEditor.getColorsScheme().getColor(EditorColors.CARET_ROW_COLOR));
    }

    return myTextAttributes;
  }
  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 paintComposedTextDecoration(Graphics2D g) {
    TextRange composedTextRange = myEditor.getComposedTextRange();
    if (composedTextRange != null) {
      Point p1 =
          myView.offsetToXY(
              Math.min(composedTextRange.getStartOffset(), myDocument.getTextLength()),
              true,
              false);
      Point p2 =
          myView.offsetToXY(
              Math.min(composedTextRange.getEndOffset(), myDocument.getTextLength()), false, true);

      int y = p1.y + myView.getAscent() + 1;

      g.setStroke(IME_COMPOSED_TEXT_UNDERLINE_STROKE);
      g.setColor(myEditor.getColorsScheme().getDefaultForeground());
      UIUtil.drawLine(g, p1.x, y, p2.x, 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);
    }
  }
 public void refreshSettings() {
   myFoldTextAttributes =
       myEditor.getColorsScheme().getAttributes(EditorColors.FOLDED_TEXT_ATTRIBUTES);
 }