tdf#57879 - Expand word boundaries to include connector punctuations

In the IDE expand word boundaries for strings in order to fully select
names for double clicks and Ctrl+Shft+Left/Right.

Change-Id: I4662b2170fdd5891dc020c08b9a9d8db8d477541
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/86597
Tested-by: Jenkins
Tested-by: Noel Grandin <noel.grandin@collabora.co.uk>
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/include/vcl/texteng.hxx b/include/vcl/texteng.hxx
index 60aa359..ee0ae62 100644
--- a/include/vcl/texteng.hxx
+++ b/include/vcl/texteng.hxx
@@ -278,7 +278,7 @@ public:

    TextPaM             GetPaM( const Point& rDocPos );
    tools::Rectangle    PaMtoEditCursor( const TextPaM& rPaM, bool bSpecial = false );
    OUString            GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord = nullptr );
    OUString            GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord = nullptr, TextPaM* pEndOfWord = nullptr );

    const TextAttrib*       FindAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const;
    const TextCharAttrib*   FindCharAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const;
diff --git a/vcl/source/edit/texteng.cxx b/vcl/source/edit/texteng.cxx
index 9ceb0c8..8e0f7a1 100644
--- a/vcl/source/edit/texteng.cxx
+++ b/vcl/source/edit/texteng.cxx
@@ -2350,7 +2350,7 @@ bool TextEngine::CreateLines( sal_uInt32 nPara )
    return nOldLineCount != pTEParaPortion->GetLines().size();
}

OUString TextEngine::GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord )
OUString TextEngine::GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord, TextPaM* pEndOfWord )
{
    OUString aWord;
    if ( rCursorPos.GetPara() < mpDoc->GetNodes().size() )
@@ -2359,11 +2359,36 @@ OUString TextEngine::GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord )
        TextNode* pNode = mpDoc->GetNodes()[ rCursorPos.GetPara() ].get();
        uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
        i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rCursorPos.GetIndex(), GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
        // tdf#57879 - expand selection to the left to include connector punctuations and search for additional word boundaries
        if (aBoundary.startPos > 0 && aBoundary.startPos < pNode->GetText().getLength() && u_charType(pNode->GetText()[aBoundary.startPos]) == U_CONNECTOR_PUNCTUATION)
        {
            aBoundary.startPos = xBI->getWordBoundary(pNode->GetText(), aBoundary.startPos - 1,
                GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true).startPos;
        }
        while (aBoundary.startPos > 0 && u_charType(pNode->GetText()[aBoundary.startPos - 1]) == U_CONNECTOR_PUNCTUATION)
        {
            aBoundary.startPos = std::min(aBoundary.startPos,
                xBI->getWordBoundary( pNode->GetText(), aBoundary.startPos - 2,
                    GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true).startPos);
        }
        // tdf#57879 - expand selection to the right to include connector punctuations and search for additional word boundaries
        if (aBoundary.endPos < pNode->GetText().getLength() && u_charType(pNode->GetText()[aBoundary.endPos - 1]) == U_CONNECTOR_PUNCTUATION)
        {
            aBoundary.endPos = xBI->getWordBoundary(pNode->GetText(), aBoundary.endPos,
                GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true).endPos;
        }
        while (aBoundary.endPos < pNode->GetText().getLength() && u_charType(pNode->GetText()[aBoundary.endPos]) == U_CONNECTOR_PUNCTUATION)
        {
            aBoundary.endPos = xBI->getWordBoundary(pNode->GetText(), aBoundary.endPos + 1,
                GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true).endPos;
        }
        aSel.GetStart().GetIndex() = aBoundary.startPos;
        aSel.GetEnd().GetIndex() = aBoundary.endPos;
        aWord = pNode->GetText().copy( aSel.GetStart().GetIndex(), aSel.GetEnd().GetIndex() - aSel.GetStart().GetIndex() );
        if ( pStartOfWord )
            *pStartOfWord = aSel.GetStart();
        if (pEndOfWord)
            *pEndOfWord = aSel.GetEnd();
    }
    return aWord;
}
diff --git a/vcl/source/edit/textview.cxx b/vcl/source/edit/textview.cxx
index 6d59216..02cfa74 100644
--- a/vcl/source/edit/textview.cxx
+++ b/vcl/source/edit/textview.cxx
@@ -730,12 +730,9 @@ void TextView::MouseButtonDown( const MouseEvent& rMouseEvent )
            if ( mpImpl->maSelection.GetEnd().GetIndex() < mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection.GetEnd().GetPara() ) )
            {
                HideSelection();
                TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[  mpImpl->maSelection.GetEnd().GetPara() ].get();
                css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
                css::i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
                TextSelection aNewSel( mpImpl->maSelection );
                aNewSel.GetStart().GetIndex() = aBoundary.startPos;
                aNewSel.GetEnd().GetIndex() = aBoundary.endPos;
                // tdf#57879 - expand selection to include connector punctuations
                TextSelection aNewSel;
                mpImpl->mpTextEngine->GetWord( mpImpl->maSelection.GetEnd(), &aNewSel.GetStart(), &aNewSel.GetEnd() );
                ImpSetSelection( aNewSel );
                ShowSelection();
                ShowCursor();
@@ -1251,12 +1248,18 @@ TextPaM TextView::CursorWordLeft( const TextPaM& rPaM )

    if ( aPaM.GetIndex() )
    {
        TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get();
        css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
        css::i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
        if ( aBoundary.startPos >= rPaM.GetIndex() )
            aBoundary = xBI->previousWord( pNode->GetText(), rPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES );
        aPaM.GetIndex() = ( aBoundary.startPos != -1 ) ? aBoundary.startPos : 0;
        // tdf#57879 - expand selection to the left to include connector punctuations
        mpImpl->mpTextEngine->GetWord( rPaM, &aPaM );
        if ( aPaM.GetIndex() >= rPaM.GetIndex() )
        {
            TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes()[ aPaM.GetPara() ].get();
            css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
            aPaM.GetIndex() = xBI->previousWord( pNode->GetText(), rPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES ).startPos;
            if ( aPaM.GetIndex() > 0 )
                mpImpl->mpTextEngine->GetWord( aPaM, &aPaM );
            else
                aPaM.GetIndex() = 0;
        }
    }
    else if ( aPaM.GetPara() )
    {
@@ -1275,8 +1278,8 @@ TextPaM TextView::CursorWordRight( const TextPaM& rPaM )
    if ( aPaM.GetIndex() < pNode->GetText().getLength() )
    {
        css::uno::Reference < css::i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
        css::i18n::Boundary aBoundary = xBI->nextWord(  pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES );
        aPaM.GetIndex() = aBoundary.startPos;
        aPaM.GetIndex() = xBI->nextWord(  pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES ).endPos;
        mpImpl->mpTextEngine->GetWord( aPaM, nullptr, &aPaM );
    }
    else if ( aPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().size()-1) )
    {