tdf#133524 AutoCorrect: fix Romanian nested quotations

typing " inside primary quotation marks: use
the correct order of the double angle quotes:

    „... «quote» ...”

Add also Aragonese, Asturian and Catalan to the
"<<" and ">>" replacement.

See commit 57f07b1d7378d218648667c5b1315cc8ad905875
(tdf#133524 AutoCorrect: support double angle quotes).

Change-Id: I2e80cc45768eefa3eb62b446ca822ee6c46f7242
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/97970
Tested-by: Jenkins
Reviewed-by: László Németh <nemeth@numbertext.org>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/98140
Reviewed-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
diff --git a/editeng/source/misc/svxacorr.cxx b/editeng/source/misc/svxacorr.cxx
index f991370..217143f 100644
--- a/editeng/source/misc/svxacorr.cxx
+++ b/editeng/source/misc/svxacorr.cxx
@@ -318,8 +318,10 @@ static constexpr sal_Unicode cLeftSingleAngleQuote = 0x2039;
static constexpr sal_Unicode cRightSingleAngleQuote = 0x203A;
// stop characters for searching preceding quotes
// (the first character is also the opening quote we are looking for)
const sal_Unicode aStopDoubleAngleQuoteStart[] = { 0x201E, 0x201D, 0 }; // preceding ,,
const sal_Unicode aStopDoubleAngleQuoteStart[] = { 0x201E, 0x201D, 0x201C, 0 }; // preceding ,,
const sal_Unicode aStopDoubleAngleQuoteEnd[] = { cRightDoubleAngleQuote, cLeftDoubleAngleQuote, 0x201D, 0x201E, 0 }; // preceding >>
// preceding << for Romanian, handle also alternative primary closing quotation mark U+201C
const sal_Unicode aStopDoubleAngleQuoteEndRo[] = { cLeftDoubleAngleQuote, cRightDoubleAngleQuote, 0x201D, 0x201E, 0x201C, 0 };
const sal_Unicode aStopSingleQuoteEnd[] = { 0x201A, 0x2018, 0x201C, 0x201E, 0 };
const sal_Unicode aStopSingleQuoteEndRuUa[] = { 0x201E, 0x201C, cRightDoubleAngleQuote, cLeftDoubleAngleQuote, 0 };

@@ -1212,7 +1214,12 @@ void SvxAutoCorrect::InsertQuote( SvxAutoCorrDoc& rDoc, sal_Int32 nInsPos,
    if ( eType == ACQuotes::DoubleAngleQuote )
    {
        bool bSwiss = eLang == LANGUAGE_FRENCH_SWISS;
        cRet = ( '<' == cInsChar || ('\"' == cInsChar && !bSttQuote) )
        // pressing " inside a quotation -> use second level angle quotes
        bool bLeftQuote = '\"' == cInsChar &&
                // start position and Romanian OR
                // not start position and Hungarian
                bSttQuote == (eLang != LANGUAGE_HUNGARIAN);
        cRet = ( '<' == cInsChar || bLeftQuote )
                ? ( bSwiss ? cLeftSingleAngleQuote : cLeftDoubleAngleQuote )
                : ( bSwiss ? cRightSingleAngleQuote : cRightDoubleAngleQuote );
    }
@@ -1348,13 +1355,23 @@ void SvxAutoCorrect::DoAutoCorrect( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
                    {
                        eType = ACQuotes::CapitalizeIAm;
                    }
                    // tdf#133524 support << and >> in Hungarian and Romanian
                    else if ( !bSingle && nInsPos && eLang.anyOf( LANGUAGE_HUNGARIAN, LANGUAGE_ROMANIAN ) &&
                        lcl_HasPrecedingChar( rTxt, nInsPos,
                    // tdf#133524 support >>Hungarian<< and <<Romanian>> secondary level quotations
                    else if ( !bSingle && nInsPos &&
                        ( ( eLang == LANGUAGE_HUNGARIAN &&
                            lcl_HasPrecedingChar( rTxt, nInsPos,
                                bSttQuote ? aStopDoubleAngleQuoteStart[0] : aStopDoubleAngleQuoteEnd[0],
                                bSttQuote ? aStopDoubleAngleQuoteStart + 1 : aStopDoubleAngleQuoteEnd + 1 ) )
                                bSttQuote ? aStopDoubleAngleQuoteStart + 1 : aStopDoubleAngleQuoteEnd + 1 ) ) ||
                          ( eLang.anyOf(
                                LANGUAGE_ROMANIAN,
                                LANGUAGE_ROMANIAN_MOLDOVA ) &&
                            lcl_HasPrecedingChar( rTxt, nInsPos,
                                bSttQuote ? aStopDoubleAngleQuoteStart[0] : aStopDoubleAngleQuoteEndRo[0],
                                bSttQuote ? aStopDoubleAngleQuoteStart + 1 : aStopDoubleAngleQuoteEndRo + 1 ) ) ) )
                    {
                        eType = ACQuotes::DoubleAngleQuote;
                        LocaleDataWrapper& rLcl = GetLocaleDataWrapper( eLang );
                        // only if the opening double quotation mark is the default one
                        if ( rLcl.getDoubleQuotationMarkStart() == OUStringChar(aStopDoubleAngleQuoteStart[0]) )
                            eType = ACQuotes::DoubleAngleQuote;
                    }
                    else if ( bSingle && nInsPos && !bSttQuote &&
                        // tdf#128860 use apostrophe outside of second level quotation in Czech, German, Icelandic,
@@ -1403,9 +1420,11 @@ void SvxAutoCorrect::DoAutoCorrect( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
            {
                const LanguageType eLang = GetDocLanguage( rDoc, nInsPos );
                if ( eLang.anyOf(
                        LANGUAGE_CATALAN,              // primary level
                        LANGUAGE_CATALAN_VALENCIAN,    // primary level
                        LANGUAGE_FINNISH,              // alternative primary level
                        LANGUAGE_FRENCH_SWISS,         // second level
                        LANGUAGE_GALICIAN,
                        LANGUAGE_GALICIAN,             // primary level
                        LANGUAGE_HUNGARIAN,            // second level
                        LANGUAGE_POLISH,               // second level
                        LANGUAGE_PORTUGUESE,           // primary level
@@ -1414,7 +1433,9 @@ void SvxAutoCorrect::DoAutoCorrect( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
                        LANGUAGE_ROMANIAN_MOLDOVA,     // second level
                        LANGUAGE_SWEDISH,              // alternative primary level
                        LANGUAGE_SWEDISH_FINLAND,      // alternative primary level
                        LANGUAGE_UKRAINIAN ) ||        // primary level
                        LANGUAGE_UKRAINIAN,            // primary level
                        LANGUAGE_USER_ARAGONESE,       // primary level
                        LANGUAGE_USER_ASTURIAN ) ||    // primary level
                    primary(eLang) == primary(LANGUAGE_GERMAN) ||  // alternative primary level
                    primary(eLang) == primary(LANGUAGE_SPANISH) )  // primary level
                {
diff --git a/sw/qa/extras/uiwriter/data/tdf133524_ro.fodt b/sw/qa/extras/uiwriter/data/tdf133524_ro.fodt
new file mode 100644
index 0000000..591db1f
--- /dev/null
+++ b/sw/qa/extras/uiwriter/data/tdf133524_ro.fodt
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
 <office:styles>
  <style:style style:name="Standard" style:family="paragraph" style:class="text"/>
  <style:default-style style:family="paragraph">
   <style:text-properties fo:language="ro" fo:country="RO"/>
  </style:default-style>
 </office:styles>
 <office:body>
  <office:text>
   <text:p text:style-name="Standard"></text:p>
  </office:text>
 </office:body>
</office:document>
diff --git a/sw/qa/extras/uiwriter/uiwriter.cxx b/sw/qa/extras/uiwriter/uiwriter.cxx
index 2e295b8..f3d92a4 100644
--- a/sw/qa/extras/uiwriter/uiwriter.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter.cxx
@@ -370,6 +370,7 @@ public:
    void testTdf38394();
    void testTdf59666();
    void testTdf133524();
    void testTdf133524_Romanian();
    void testTdf128860();
    void testTdf123786();
#if ENABLE_LIBNUMBERTEXT
@@ -591,6 +592,7 @@ public:
    CPPUNIT_TEST(testTdf38394);
    CPPUNIT_TEST(testTdf59666);
    CPPUNIT_TEST(testTdf133524);
    CPPUNIT_TEST(testTdf133524_Romanian);
    CPPUNIT_TEST(testTdf128860);
    CPPUNIT_TEST(testTdf123786);
#if ENABLE_LIBNUMBERTEXT
@@ -7193,7 +7195,6 @@ void SwUiWriterTest::testTdf38394()
    // tdf#132301 autocorrect of qu'«
    pWrtShell->Insert(u" qu\u2019");
    pWrtShell->AutoCorrect(corr, cChar);
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u" qu\u2019« ";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
}
@@ -7228,7 +7229,6 @@ void SwUiWriterTest::testTdf133524()
    // <<
    pWrtShell->Insert(u"word<");
    pWrtShell->AutoCorrect(corr, '<');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u"word«";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // 2. Testing autocorrect of " to >> and << inside „...”
@@ -7236,29 +7236,64 @@ void SwUiWriterTest::testTdf133524()
    // opening primary level quote
    pWrtShell->Insert(u" ");
    pWrtShell->AutoCorrect(corr, '"');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u" „";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // opening second level quote
    pWrtShell->Insert(u"Sentence and ");
    pWrtShell->AutoCorrect(corr, '"');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u"Sentence and »";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // closing second level quote
    pWrtShell->Insert(u"word");
    pWrtShell->AutoCorrect(corr, '"');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u"word«";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // closing primary level quote
    pWrtShell->Insert(u".");
    pWrtShell->AutoCorrect(corr, '"');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u".”";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
}

void SwUiWriterTest::testTdf133524_Romanian()
{
    SwDoc* pDoc = createDoc("tdf133524_ro.fodt");
    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
    SwAutoCorrect corr(*SvxAutoCorrCfg::Get().GetAutoCorrect());
    // 1. Testing autocorrect of " to << and >> inside „...”
    // Example: „Sentence and «word».”
    // opening primary level quote
    pWrtShell->AutoCorrect(corr, '"');
    sal_uLong nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    OUString sReplaced(u"„");
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // opening second level quote
    pWrtShell->Insert(u"Sentence and ");
    pWrtShell->AutoCorrect(corr, '"');
    sReplaced += u"Sentence and «";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // closing second level quote
    pWrtShell->Insert(u"word");
    pWrtShell->AutoCorrect(corr, '"');
    sReplaced += u"word»";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // closing primary level quote
    pWrtShell->Insert(u".");
    pWrtShell->AutoCorrect(corr, '"');
    sReplaced += u".”";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // 2. Testing recognition of closing double quotation mark ”
    pWrtShell->Insert(u" ");
    pWrtShell->AutoCorrect(corr, '"');
    sReplaced += u" „";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // 3. Testing recognition of alternative closing double quotation mark “
    pWrtShell->Insert(u"Alternative.“ ");
    pWrtShell->AutoCorrect(corr, '"');
    sReplaced += u"Alternative.“ „";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
}

void SwUiWriterTest::testTdf128860()
{
    SwDoc* pDoc = createDoc("tdf128860.fodt");
@@ -7273,13 +7308,11 @@ void SwUiWriterTest::testTdf128860()
    // Us apostrophe without preceding starting quote: word' -> word’
    pWrtShell->Insert(u" word");
    pWrtShell->AutoCorrect(corr, '\'');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u" word’";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // But only after letters: word.' -> word.‘
    pWrtShell->Insert(u" word.");
    pWrtShell->AutoCorrect(corr, '\'');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u" word.‘";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
}
@@ -7298,13 +7331,11 @@ void SwUiWriterTest::testTdf123786()
    // Us apostrophe without preceding starting quote: word' -> word’
    pWrtShell->Insert(u" слово");
    pWrtShell->AutoCorrect(corr, '\'');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u" слово’";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // But only after letters: word.' -> word.“
    pWrtShell->Insert(u" слово.");
    pWrtShell->AutoCorrect(corr, '\'');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u" слово.“";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
}
@@ -7325,7 +7356,6 @@ void SwUiWriterTest::testTdf133589()
    // disambiguate consonants: asszony -> asz|szony
    pWrtShell->Insert(u"asszony");
    pWrtShell->AutoCorrect(corr, ' ');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u"𐳀𐳥𐳥𐳛𐳚 ";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // disambiguate consonants: kosszarv -> kos|szarv
@@ -7334,13 +7364,11 @@ void SwUiWriterTest::testTdf133589()
    // pWrtShell->Insert(u"kosszarv");
    pWrtShell->Insert(u"kos​szarv");
    pWrtShell->AutoCorrect(corr, ' ');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u"𐳓𐳛𐳤𐳥𐳀𐳢𐳮 ";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
    // transliterate numbers to Old Hungarian
    pWrtShell->Insert(u"2020");
    pWrtShell->AutoCorrect(corr, ' ');
    nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
    sReplaced += u"𐳺𐳺𐳿𐳼𐳼 ";
    CPPUNIT_ASSERT_EQUAL(sReplaced, static_cast<SwTextNode*>(pDoc->GetNodes()[nIndex])->GetText());
}