tdf#140731: sw transliteration: avoid too many redlines
As a workaround for the performance regression
from commit 2d3c77e9b10f20091ef338e262ba7756eb280ce9
(tdf#109266 sw change tracking: track transliteration),
switch off redlining to avoid ~freezing, if a single
transliteration could result too many (>~500) redlines.
A single transliteration creates n redlines
for n paragraphs of the selected text, except in
the case of transliterating to title case, where it
creates n redlines for n words. It's very easy
to freeze Writer, because Writer's slowing down with
n redlines is described by an O(n²) (quadratic) time
complexity. Eg. in an experiment, title casing
~660 words was 6 sec, but ~3000 words was 85 sec,
regarding to creating 660 vs 3000 redlines.
Note: this is a partial revert of commit
2d3c77e9b10f20091ef338e262ba7756eb280ce9, if the
selection contains more than 500 paragraphs (or in the
case transliterating to title case, ~500 words).
Change-Id: Iad98943cc9e1ed64aa9779e49ee3e941abad02ac
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111637
Tested-by: Jenkins
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx
index 5dbbbc72..50986f3 100644
--- a/sw/inc/ndtxt.hxx
+++ b/sw/inc/ndtxt.hxx
@@ -746,7 +746,7 @@ public:
/// change text to Upper/Lower/Hiragana/Katakana/...
void TransliterateText( utl::TransliterationWrapper& rTrans,
sal_Int32 nStart, sal_Int32 nEnd,
SwUndoTransliterate* pUndo );
SwUndoTransliterate* pUndo, bool bUseRedlining = false );
/// count words in given range - returns true if we refreshed out count
bool CountWords( SwDocStat& rStat, sal_Int32 nStart, sal_Int32 nEnd ) const;
diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx
index b9846d8..e66ed8e 100644
--- a/sw/source/core/doc/DocumentContentOperationsManager.cxx
+++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx
@@ -74,6 +74,8 @@
#include <sal/log.hxx>
#include <unotools/charclass.hxx>
#include <unotools/configmgr.hxx>
#include <unotools/transliterationwrapper.hxx>
#include <i18nutil/transliteration.hxx>
#include <sfx2/Metadatable.hxx>
#include <sot/exchange.hxx>
#include <svl/stritem.hxx>
@@ -2821,6 +2823,44 @@ void DocumentContentOperationsManager::TransliterateText(
}
}
bool bUseRedlining = m_rDoc.getIDocumentRedlineAccess().IsRedlineOn();
// as a workaround for a known performance problem, switch off redlining
// to avoid freezing, if transliteration could result too many redlines
if ( bUseRedlining )
{
const sal_uLong nMaxRedlines = 500;
const bool bIsTitleCase = rTrans.getType() == TransliterationFlags::TITLE_CASE;
sal_uLong nAffectedNodes = 0;
sal_uLong nAffectedChars = nEndCnt;
SwNodeIndex aIdx( pStt->nNode );
for( ; aIdx.GetIndex() <= nEndNd; ++aIdx )
{
SwTextNode* pAffectedNode = aIdx.GetNode().GetTextNode();
// don't count not text nodes or empty text nodes
if( !pAffectedNode || pAffectedNode->GetText().isEmpty() )
continue;
nAffectedNodes++;
// count characters of the node (the last - maybe partially
// selected - node was counted at initialization of nAffectedChars)
if( aIdx.GetIndex() < nEndNd )
nAffectedChars += pAffectedNode->GetText().getLength();
// transliteration creates n redlines for n nodes, except in the
// case of title case, where it creates n redlines for n words
if( nAffectedNodes > nMaxRedlines ||
// estimate word count based on the character count, where
// 6 = average English word length is ~5 letters + space
( bIsTitleCase && (nAffectedChars - nSttCnt)/6 > nMaxRedlines ) )
{
bUseRedlining = false;
break;
}
}
}
if( nSttNd != nEndNd ) // is more than one text node involved?
{
// iterate over all effected text nodes, the first and the last one
@@ -2831,8 +2871,10 @@ void DocumentContentOperationsManager::TransliterateText(
{
++aIdx;
if( pTNd )
{
pTNd->TransliterateText(
rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo.get());
rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining);
}
}
for( ; aIdx.GetIndex() < nEndNd; ++aIdx )
@@ -2841,16 +2883,19 @@ void DocumentContentOperationsManager::TransliterateText(
if (pTNd)
{
pTNd->TransliterateText(
rTrans, 0, pTNd->GetText().getLength(), pUndo.get());
rTrans, 0, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining);
}
}
if( nEndCnt && nullptr != ( pTNd = pEnd->nNode.GetNode().GetTextNode() ))
pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo.get() );
{
pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo.get(), bUseRedlining );
}
}
else if( pTNd && nSttCnt < nEndCnt )
pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo.get() );
{
pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo.get(), bUseRedlining );
}
if( pUndo && pUndo->HasData() )
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx
index ba25c5f..e734e47 100644
--- a/sw/source/core/txtnode/txtedt.cxx
+++ b/sw/source/core/txtnode/txtedt.cxx
@@ -1684,7 +1684,7 @@ namespace
void SwTextNode::TransliterateText(
utl::TransliterationWrapper& rTrans,
sal_Int32 nStt, sal_Int32 nEnd,
SwUndoTransliterate* pUndo )
SwUndoTransliterate* pUndo, bool bUseRedlining )
{
if (nStt >= nEnd)
return;
@@ -1908,7 +1908,7 @@ void SwTextNode::TransliterateText(
// now apply the changes from end to start to leave the offsets of the
// yet unchanged text parts remain the same.
size_t nSum(0);
bool bIsRedlineOn(GetDoc().getIDocumentRedlineAccess().IsRedlineOn());
for (size_t i = 0; i < aChanges.size(); ++i)
{ // check this here since AddChanges cannot be moved below
// call to ReplaceTextOnly
@@ -1922,7 +1922,7 @@ void SwTextNode::TransliterateText(
return;
}
if ( bIsRedlineOn )
if ( bUseRedlining )
{
// create SwPaM with mark & point spanning the attributed text
//SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different