editeng/qa/unit/core-test.cxx | 112 ++++++++++ editeng/source/editeng/impedit4.cxx | 14 + sw/qa/extras/uiwriter/uiwriter3.cxx | 8 sw/qa/extras/uiwriter/uiwriter4.cxx | 163 ++++++++++++++++ sw/source/core/doc/DocumentContentOperationsManager.cxx | 26 +- sw/source/core/txtnode/txtedt.cxx | 22 +- 6 files changed, 326 insertions(+), 19 deletions(-)
New commits: commit cb699fd9c749d5fe621406918fa6458896c09239 Author: Michael Warner <michael.warner.ut+libreoff...@gmail.com> AuthorDate: Fri Aug 27 17:37:28 2021 -0400 Commit: Heiko Tietze <heiko.tie...@documentfoundation.org> CommitDate: Fri Nov 26 16:40:55 2021 +0100 tdf#49033 Honor Selection When Applying Sentence Case Format Prevents Sentence Case formatting from occuring outside of a user's selection. This commit does not prevent Title Case formatting from adjusting the first letter of a word, even if the selection does not include that letter. Change-Id: Id3f1a5b0da973d24ef73f1e640306a65e1b4c561 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121966 Tested-by: Jenkins Reviewed-by: Heiko Tietze <heiko.tie...@documentfoundation.org> diff --git a/editeng/qa/unit/core-test.cxx b/editeng/qa/unit/core-test.cxx index 8215c451118a..8bdd43d926f1 100644 --- a/editeng/qa/unit/core-test.cxx +++ b/editeng/qa/unit/core-test.cxx @@ -31,6 +31,8 @@ #include <svl/srchitem.hxx> #include <editeng/fontitem.hxx> #include <editeng/fhgtitem.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <comphelper/processfactory.hxx> #include <com/sun/star/text/textfield/Type.hpp> @@ -1745,15 +1747,117 @@ void Test::testLargeParaCopyPaste() CPPUNIT_ASSERT_EQUAL( aTenthPara, rDoc.GetParaAsString(sal_Int32(11)) ); } +OUString lcl_translitTest(EditEngine& aEditEngine, const OUString& text, const ESelection& esel, const TransliterationFlags nType) +{ + aEditEngine.SetText(text); + aEditEngine.TransliterateText(esel, nType); + return aEditEngine.GetText(); +} + + void Test::testTransliterate() { // Create EditEngine's instance - EditEngine aEditEngine( mpItemPool.get() ); + EditEngine editEng( mpItemPool.get() ); OUString sText("one (two) three"); - aEditEngine.SetText(sText); - aEditEngine.TransliterateText(ESelection(0, 0, 0, sText.getLength()), TransliterationFlags::TITLE_CASE); - CPPUNIT_ASSERT_EQUAL(OUString("One (Two) Three"), aEditEngine.GetText()); + editEng.SetText(sText); + editEng.TransliterateText(ESelection(0, 0, 0, sText.getLength()), TransliterationFlags::TITLE_CASE); + CPPUNIT_ASSERT_EQUAL(OUString("One (Two) Three"), editEng.GetText()); + + using TF = TransliterationFlags; + const OUString sText2 = "Mary Jones met joe Smith. Time Passed."; + int selStart = 12; + int selEnd = 12; + ESelection esel(0, selStart, 0, selEnd); + + /* DocumentContentOperationsManager checks if the cursor is inside of a word before transliterating, + * but Edit Engine has no such check. Therefore, behavior is different between these two when the + * cursor is on a word boundary. */ + + /* No selection tests. Cursor between the ' ' and 'm' before 'met'. */ + CPPUNIT_ASSERT_EQUAL(OUString(""), editEng.GetText(esel)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary jones met joe smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones Met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones MET joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::UPPERCASE_LOWERCASE)); + + /* No selection tests. Cursor between the 't' and the ' ' after 'met'. */ + selStart = 14; + selEnd = 14; + esel = ESelection(0, selStart, 0, selEnd); + CPPUNIT_ASSERT_EQUAL(OUString(""), editEng.GetText(esel)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary jones met joe smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::UPPERCASE_LOWERCASE)); + + /* No selection tests. Cursor between the 'h' and the '.' after 'Smith'. */ + selStart = 24; + selEnd = 24; + esel = ESelection(0, selStart, 0, selEnd); + CPPUNIT_ASSERT_EQUAL(OUString(""), editEng.GetText(esel)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary jones met joe smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::UPPERCASE_LOWERCASE)); + + /* No selection tests. Cursor between the 'm' and 'e' in 'met'. */ + selStart = 12; + selEnd = 12; + esel = ESelection(0, selStart, 0, selEnd); + CPPUNIT_ASSERT_EQUAL(OUString(""), editEng.GetText(esel)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary jones met joe smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones Met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones MET joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::UPPERCASE_LOWERCASE)); + + /* Test behavior when there is a selection that does not cross a word boundary: "met" */ + selStart = 11; + selEnd = 14; + esel = ESelection(0, selStart, 0, selEnd); + CPPUNIT_ASSERT_EQUAL(OUString("met"), editEng.GetText(esel)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones Met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones Met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones MET joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::UPPERCASE_LOWERCASE)); + + /* Test behavior when there is a selection that crosses a word boundary: "nes met joe Sm" */ + selStart = 7; + selEnd = 21; + esel = ESelection(0, selStart, 0, selEnd); + CPPUNIT_ASSERT_EQUAL(OUString("nes met joe Sm"), editEng.GetText(esel)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary JoNes met joe smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones Met Joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary JoNES MET JOE SMith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::UPPERCASE_LOWERCASE)); + + /* Test behavior when there is a selection that crosses a sentence boundary: "joe Smith. Time Passed." */ + selStart = 15; + selEnd = 38; + esel = ESelection(0, selStart, 0, selEnd); + editEng.SetText(sText2); + CPPUNIT_ASSERT_EQUAL(OUString("joe Smith. Time Passed."), editEng.GetText(esel)); + + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met Joe smith. Time passed."), lcl_translitTest(editEng, sText2, esel, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met Joe Smith. Time Passed."), lcl_translitTest(editEng, sText2, esel, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met JOE SMITH. TIME PASSED."), lcl_translitTest(editEng, sText2, esel, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe smith. time passed."), lcl_translitTest(editEng, sText2, esel, TF::UPPERCASE_LOWERCASE)); + + /* Test behavior when sentence ends with a capital that is not selected: "CURRENT IS EQUAL TO 10 A" */ + selStart = 0; + selEnd = 19; + esel = ESelection(0, selStart, 0, selEnd); + const OUString sText3("CURRENT IS EQUAL TO 10 A"); + editEng.SetText(sText3); + CPPUNIT_ASSERT_EQUAL(OUString("CURRENT IS EQUAL TO"), editEng.GetText(esel)); + + CPPUNIT_ASSERT_EQUAL(OUString("Current is equal to 10 A"), lcl_translitTest(editEng, sText3, esel, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Current Is Equal To 10 A"), lcl_translitTest(editEng, sText3, esel, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("CURRENT IS EQUAL TO 10 A"), lcl_translitTest(editEng, sText3, esel, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("current is equal to 10 A"), lcl_translitTest(editEng, sText3, esel, TF::UPPERCASE_LOWERCASE)); + + } CPPUNIT_TEST_SUITE_REGISTRATION(Test); diff --git a/editeng/source/editeng/impedit4.cxx b/editeng/source/editeng/impedit4.cxx index dd3f13cf0f47..e160f3896f37 100644 --- a/editeng/source/editeng/impedit4.cxx +++ b/editeng/source/editeng/impedit4.cxx @@ -2661,7 +2661,13 @@ EditSelection ImpEditEngine::TransliterateText( const EditSelection& rSelection, aSel.Adjust( aEditDoc ); if ( !aSel.HasRange() ) - aSel = SelectWord( aSel ); + { + /* Cursor is inside of a word */ + if (nTransliterationMode == TransliterationFlags::SENTENCE_CASE) + aSel = SelectSentence( aSel ); + else + aSel = SelectWord( aSel ); + } // tdf#107176: if there's still no range, just return aSel if ( !aSel.HasRange() ) @@ -2826,6 +2832,12 @@ EditSelection ImpEditEngine::TransliterateText( const EditSelection& rSelection, nCurrentEnd = nLastEnd; } + // prevent making any change outside of the user's selection + nCurrentStart = std::max(aSel.Min().GetIndex(), nCurrentStart); + nCurrentEnd = std::min(aSel.Max().GetIndex(), nCurrentEnd); + nLastStart = std::max(aSel.Min().GetIndex(), nLastStart); + nLastEnd = std::min(aSel.Max().GetIndex(), nLastEnd); + while (nCurrentStart < nLastEnd) { const sal_Int32 nLen = nCurrentEnd - nCurrentStart; diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx index 80874d041267..7214b63e5811 100644 --- a/sw/qa/extras/uiwriter/uiwriter3.cxx +++ b/sw/qa/extras/uiwriter/uiwriter3.cxx @@ -1883,24 +1883,28 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf116315) pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_SHIFT | KEY_F3); Scheduler::ProcessEventsToIdle(); + // Title Case CPPUNIT_ASSERT_EQUAL(OUString("This is a Test"), getParagraph(1)->getString()); pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_SHIFT | KEY_F3); Scheduler::ProcessEventsToIdle(); + // Sentence Case // Without the fix in place, this test would have failed with - // - Expected: This is a test + // - Expected: This is a Test // - Actual : This is a TEST - CPPUNIT_ASSERT_EQUAL(OUString("This is a test"), getParagraph(1)->getString()); + CPPUNIT_ASSERT_EQUAL(OUString("This is a Test"), getParagraph(1)->getString()); pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_SHIFT | KEY_F3); Scheduler::ProcessEventsToIdle(); + // Upper Case CPPUNIT_ASSERT_EQUAL(OUString("This is a TEST"), getParagraph(1)->getString()); pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_SHIFT | KEY_F3); Scheduler::ProcessEventsToIdle(); + // Lower Case CPPUNIT_ASSERT_EQUAL(OUString("This is a test"), getParagraph(1)->getString()); } } diff --git a/sw/qa/extras/uiwriter/uiwriter4.cxx b/sw/qa/extras/uiwriter/uiwriter4.cxx index 0b76f34ec616..f9cb31778e16 100644 --- a/sw/qa/extras/uiwriter/uiwriter4.cxx +++ b/sw/qa/extras/uiwriter/uiwriter4.cxx @@ -141,6 +141,7 @@ #include <unotxdoc.hxx> #include <comphelper/processfactory.hxx> #include <rootfrm.hxx> +#include <unotools/transliterationwrapper.hxx> namespace { @@ -166,6 +167,7 @@ class SwUiWriterTest4 : public SwModelTestBase, public HtmlTestTools { public: void testTdf96515(); + void testTdf49033(); void testTdf96943(); void testTdf96536(); void testTdf96479(); @@ -287,6 +289,7 @@ public: CPPUNIT_TEST_SUITE(SwUiWriterTest4); CPPUNIT_TEST(testTdf96515); + CPPUNIT_TEST(testTdf49033); CPPUNIT_TEST(testTdf96943); CPPUNIT_TEST(testTdf96536); CPPUNIT_TEST(testTdf96479); @@ -433,6 +436,166 @@ void SwUiWriterTest4::testTdf96515() CPPUNIT_ASSERT_EQUAL(1, getPages()); } +static OUString lcl_translitTest(SwDoc& rDoc, const SwPaM& rPaM, TransliterationFlags const nType) +{ + utl::TransliterationWrapper aTrans(::comphelper::getProcessComponentContext(), nType); + rDoc.getIDocumentContentOperations().TransliterateText(rPaM, aTrans); + //SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + return rPaM.GetNode(false).GetTextNode()->GetText(); +} + +void SwUiWriterTest4::testTdf49033() +{ + SwDoc* pDoc = createSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + + // Insert the test text at the end of the document. + pWrtShell->SttEndDoc(/*bStt=*/false); + pWrtShell->Insert("Mary Jones met joe Smith. Time Passed."); + pWrtShell->StartOfSection(); + SwShellCursor* pCursor = pWrtShell->getShellCursor(false); + + using TF = TransliterationFlags; + + /* -- Test behavior when there is no selection -- */ + + /* Move cursor between the 't' and the ' ' after 'met', nothing should change */ + for (int i = 0; i < 14; i++) + pCursor->Move(fnMoveForward); + + CPPUNIT_ASSERT_EQUAL(false, pCursor->HasMark()); + CPPUNIT_ASSERT_EQUAL(false, pWrtShell->IsSelection()); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::UPPERCASE_LOWERCASE)); + + /* Move cursor between the 'h' and the '.' after 'Smith', nothing should change */ + for (int i = 0; i < 10; i++) + pCursor->Move(fnMoveForward); + + CPPUNIT_ASSERT_EQUAL(false, pCursor->HasMark()); + CPPUNIT_ASSERT_EQUAL(false, pWrtShell->IsSelection()); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::UPPERCASE_LOWERCASE)); + + /* Move cursor between the 'm' and the 'e' in 'met' */ + for (int i = 0; i < 12; i++) + pCursor->Move(fnMoveBackward); + + CPPUNIT_ASSERT_EQUAL(false, pCursor->HasMark()); + CPPUNIT_ASSERT_EQUAL(false, pWrtShell->IsSelection()); + CPPUNIT_ASSERT_EQUAL(OUString("Mary jones met joe smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::SENTENCE_CASE)); + + /* Undo the sentence case change to reset for the following tests */ + pDoc->GetIDocumentUndoRedo().Undo(); + + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones Met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones MET joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::UPPERCASE_LOWERCASE)); + + /* -- Test behavior when there is a selection that does not cross a word boundary -- */ + pCursor->Move(fnMoveBackward); + pWrtShell->SelWrd(); + CPPUNIT_ASSERT_EQUAL(true, pCursor->HasMark()); + CPPUNIT_ASSERT_EQUAL(true, pWrtShell->IsSelection()); + + OUString currentSelectedText; + pWrtShell->GetSelectedText(currentSelectedText); + CPPUNIT_ASSERT_EQUAL(OUString("met"), currentSelectedText); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones Met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones Met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones MET joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::UPPERCASE_LOWERCASE)); + + /* -- Test behavior when there is a selection that crosses a word boundary -- */ + for (int i = 0; i < 7; i++) + pCursor->Move(fnMoveBackward); + pCursor->SetMark(); + for (int i = 0; i < 14; i++) + pCursor->Move(fnMoveForward); + + pWrtShell->GetSelectedText(currentSelectedText); + CPPUNIT_ASSERT_EQUAL(OUString("nes met joe Sm"), currentSelectedText); + CPPUNIT_ASSERT_EQUAL(OUString("Mary JoNes met joe smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones Met Joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary JoNES MET JOE SMith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::UPPERCASE_LOWERCASE)); + + /* Reset the 's' to upper-case for the next test */ + for (int i = 0; i < 2; i++) + pCursor->Move(fnMoveBackward); + pCursor->SetMark(); + pCursor->Move(fnMoveForward); + pDoc->getIDocumentContentOperations().ReplaceRange(*pCursor, OUString('S'), false); + + /* -- Test behavior when there is a selection that crosses a sentence boundary -- */ + for (int i = 0; i < 4; i++) + pCursor->Move(fnMoveBackward); + pCursor->SetMark(); + for (int i = 0; i < 22; i++) + pCursor->Move(fnMoveForward); + pWrtShell->GetSelectedText(currentSelectedText); + CPPUNIT_ASSERT_EQUAL(OUString("joe Smith. Time Passed"), currentSelectedText); + + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met Joe smith. Time passed."), + lcl_translitTest(*pDoc, *pCursor, TF::SENTENCE_CASE)); + + /* Undo the sentence case change to reset for the following tests */ + pDoc->GetIDocumentUndoRedo().Undo(); + + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met Joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met JOE SMITH. TIME PASSED."), + lcl_translitTest(*pDoc, *pCursor, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe smith. time passed."), + lcl_translitTest(*pDoc, *pCursor, TF::UPPERCASE_LOWERCASE)); + + /* Undo the previous changes to reset for the following tests */ + pDoc->GetIDocumentUndoRedo().Undo(); + pDoc->GetIDocumentUndoRedo().Undo(); + pDoc->GetIDocumentUndoRedo().Undo(); + + /* -- Test behavior when there is a selection that does not reach end of sentence -- */ + for (int i = 0; i < 37; i++) + pCursor->Move(fnMoveBackward); + pCursor->SetMark(); + for (int i = 0; i < 10; i++) + pCursor->Move(fnMoveForward); + pWrtShell->GetSelectedText(currentSelectedText); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones"), currentSelectedText); + CPPUNIT_ASSERT_EQUAL(OUString("Mary jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::SENTENCE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("Mary Jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::TITLE_CASE)); + CPPUNIT_ASSERT_EQUAL(OUString("MARY JONES met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::LOWERCASE_UPPERCASE)); + CPPUNIT_ASSERT_EQUAL(OUString("mary jones met joe Smith. Time Passed."), + lcl_translitTest(*pDoc, *pCursor, TF::UPPERCASE_LOWERCASE)); +} + void SwUiWriterTest4::testTdf96943() { // Enable hide whitespace mode. diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx index 5311bfb5e3b8..ab3bd4ca9cd8 100644 --- a/sw/source/core/doc/DocumentContentOperationsManager.cxx +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -2932,10 +2932,9 @@ void DocumentContentOperationsManager::TransliterateText( sal_Int32 nEndCnt = pEnd->nContent.GetIndex(); SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode(); - if( pStt == pEnd && pTNd ) // no selection? + if( (pStt == pEnd) && pTNd ) // no selection? { - // set current word as 'area of effect' - + /* Check if cursor is inside of a word */ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is()); Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary( pTNd->GetText(), nSttCnt, @@ -2945,8 +2944,23 @@ void DocumentContentOperationsManager::TransliterateText( if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos ) { - nSttCnt = aBndry.startPos; - nEndCnt = aBndry.endPos; + /* Cursor is inside of a word */ + if (rTrans.getType() == TransliterationFlags::SENTENCE_CASE) { + /* set current sentence as 'area of effect' */ + nSttCnt = g_pBreakIt->GetBreakIter()->beginOfSentence( + pTNd->GetText(), nSttCnt, + g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ) ); + nEndCnt = g_pBreakIt->GetBreakIter()->endOfSentence( + pTNd->GetText(), nEndCnt, + g_pBreakIt->GetLocale( pTNd->GetLang( nEndCnt ) ) ); + } else { + /* Set current word as 'area of effect' */ + nSttCnt = aBndry.startPos; + nEndCnt = aBndry.endPos; + } + } else { + /* Cursor is not inside of a word. Nothing should happen. */ + return; } } @@ -2990,7 +3004,7 @@ void DocumentContentOperationsManager::TransliterateText( if( nSttNd != nEndNd ) // is more than one text node involved? { - // iterate over all effected text nodes, the first and the last one + // iterate over all affected text nodes, the first and the last one // may be incomplete because the selection starts and/or ends there SwNodeIndex aIdx( pStt->nNode ); diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx index 9fabb33aa6e1..f4d43feb3cdc 100644 --- a/sw/source/core/txtnode/txtedt.cxx +++ b/sw/source/core/txtnode/txtedt.cxx @@ -1699,6 +1699,9 @@ void SwTextNode::TransliterateText( if (nStt >= nEnd) return; + const sal_Int32 selStart = nStt; + const sal_Int32 selEnd = nEnd; + // since we don't use Hiragana/Katakana or half-width/full-width transliterations here // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will // occasionally miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES @@ -1781,8 +1784,9 @@ void SwTextNode::TransliterateText( } else if (rTrans.getType() == TransliterationFlags::SENTENCE_CASE) { - // for 'sentence case' we need to iterate sentence by sentence - + // For 'sentence case' we need to iterate sentence by sentence. + // nLastStart and nLastEnd are the boundaries of the last sentence in + // the user's selection. sal_Int32 nLastStart = g_pBreakIt->GetBreakIter()->beginOfSentence( GetText(), nEnd, g_pBreakIt->GetLocale( GetLang( nEnd ) ) ); @@ -1790,10 +1794,10 @@ void SwTextNode::TransliterateText( GetText(), nLastStart, g_pBreakIt->GetLocale( GetLang( nLastStart ) ) ); - // extend nStt, nEnd to the current sentence boundaries - sal_Int32 nCurrentStart = g_pBreakIt->GetBreakIter()->beginOfSentence( - GetText(), nStt, - g_pBreakIt->GetLocale( GetLang( nStt ) ) ); + // Begin with the starting point of the user's selection (it may not be + // the beginning of a sentence)... + sal_Int32 nCurrentStart = nStt; + // ...And extend to the end of the first sentence sal_Int32 nCurrentEnd = g_pBreakIt->GetBreakIter()->endOfSentence( GetText(), nCurrentStart, g_pBreakIt->GetLocale( GetLang( nCurrentStart ) ) ); @@ -1836,6 +1840,11 @@ void SwTextNode::TransliterateText( nCurrentEnd = nLastEnd; } + // Prevent going outside of the user's selection + nCurrentStart = std::max(selStart, nCurrentStart); + nCurrentEnd = std::min(selEnd, nCurrentEnd); + nLastEnd = std::min(selEnd, nLastEnd); + while (nCurrentStart < nLastEnd) { sal_Int32 nLen = nCurrentEnd - nCurrentStart; @@ -1924,6 +1933,7 @@ void SwTextNode::TransliterateText( // call to ReplaceTextOnly swTransliterationChgData & rData = aChanges[ aChanges.size() - 1 - i ]; + nSum += rData.sChanged.getLength() - rData.nLen; if (nSum > o3tl::make_unsigned(GetSpaceLeft())) {