diff --git a/Src/views/VwSelection.cpp b/Src/views/VwSelection.cpp index e5eb3f1c17..4fa25937b3 100644 --- a/Src/views/VwSelection.cpp +++ b/Src/views/VwSelection.cpp @@ -5417,6 +5417,72 @@ bool CheckForParaBreak(const wchar * pchw, int ich, int cch, int * pichNext) return false; } +/// +/// Check if the selected text (the paste target) is a complete single paragraph. +/// +/// +/// True: If the selection contains exactly one complete paragraph, which means that the selection starts +/// at the beginning of a paragraph and ends at the end of the same paragraph. +/// False: If the selection contains text from more than one paragraph or only contains part of a paragraph. +/// +bool VwTextSelection::SelectedTextIsSingleParagraph() +{ + VwParagraphBox* notUsed; + int targetMin; + int targetLim; // One past the last selected character, relative to the last paragraph. + GetFirstAndLast(¬Used, ¬Used, &targetMin, &targetLim); + + if (!m_pvpboxEnd && + targetMin == 0 && + m_pvpbox->Source()->CStrings() == 1) + { + ITsString* firstStr = NULL; + m_pvpbox->Source()->StringAtIndex(0, &firstStr); + int firstStrLen = 0; + CheckHr(firstStr->get_Length(&firstStrLen)); + + if (targetLim == firstStrLen) + { + return true; + } + } + return false; +} + +/// +/// Check if the replacement text is a single paragraph. +/// +/// The replacement text. +/// +/// True: If the replacement text contains exactly one paragraph, which means that the text ends with +/// a '\n' and contains no other '\n' characters. +/// False: If the replacement text contains more than one '\n' or if the '\n' is not at the end. +/// +bool VwTextSelection::ReplacementTextIsSingleParagraph(ITsString* replacementTstr) +{ + int replacementCharCount; + SmartBstr replacementBstr; + CheckHr(replacementTstr->get_Length(&replacementCharCount)); + CheckHr(replacementTstr->get_Text(&replacementBstr)); + const wchar* replacementText = replacementBstr.Chars(); + for (int ii = 0; ii < replacementCharCount; ii++) + { + if (replacementText[ii] == '\n') + { + // The last character is a '\n' and there are no other '\n' characters in the string. + if (ii == replacementCharCount - 1) + { + return true; + } + else + { + break; + } + } + } + return false; +} + /*---------------------------------------------------------------------------------------------- Replace what is selected with a TsString. If the string contains newlines, the properties associated with the Newline become @@ -5449,6 +5515,21 @@ STDMETHODIMP VwTextSelection::ReplaceWithTsString(ITsString * ptss) CheckHr(ptss->get_NormalizedForm(knmNFD, &qtssNorm)); ptss = qtssNorm; + // If the selected text (the paste target) is a complete single paragraph, and the replacement + // text is a complete single paragraph (has exactly one terminal paragraph marker with no interior + // paragraph breaks) then trim that marker so that we do character replacement instead of paragraph + // replacement. Paragraph replacement fails in this case: LT-20857 (and many others). + if (SelectedTextIsSingleParagraph() && ReplacementTextIsSingleParagraph(ptss)) + { + int replacementCharCount; + CheckHr(ptss->get_Length(&replacementCharCount)); + + // Remove the trailing '\n'. + ITsStringPtr trimmedReplacementText; + MakeSubString(ptss, 0, replacementCharCount - 1, &trimmedReplacementText); + ptss = trimmedReplacementText; + } + // default: only position changed; set to 2 if para changes VwSelChangeType nHowChanged = ksctSamePara; try diff --git a/Src/views/VwSelection.h b/Src/views/VwSelection.h index bd701d06e9..2161f47dc4 100644 --- a/Src/views/VwSelection.h +++ b/Src/views/VwSelection.h @@ -541,6 +541,8 @@ class VwTextSelection : public VwSelection IVwViewConstructor * pvvcEdit, int fragEdit, ITsStrBldr * ptsb, bool * pfOk); virtual bool RuinedByDeleting(VwBox * pbox, VwBox * pboxReplacement=0); void MakeSubString(ITsString * ptss, int ichMin, int ichLim, ITsString ** pptssSub); + bool SelectedTextIsSingleParagraph(); + bool ReplacementTextIsSingleParagraph(ITsString* ptss); void SetInsertionProps(ITsTextProps * pttp) {