summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Stahl <Michael.Stahl@cib.de>2019-09-24 18:11:45 +0200
committerMichael Stahl <michael.stahl@cib.de>2019-10-23 12:47:40 +0200
commitb522fc0646915d4da94df38dd249c88b28f25be7 (patch)
tree2fd2ba18a58287621022bc90ccf6a07ee143801d
parentec0a3db0cbb5413d10e70c9843e679bbf2327c15 (diff)
sw: maintain fieldmarks in DeleteRange()/DeleteAndJoin()/ReplaceRange()
Avoid overlapping deletes of the CH_TXT_ATR_FIELD*. Change-Id: I0cbfcfbc8f39c029e7faefa39e75fce7e7e4b9e2 Reviewed-on: https://gerrit.libreoffice.org/80070 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.stahl@cib.de>
-rw-r--r--sw/source/core/doc/DocumentContentOperationsManager.cxx144
1 files changed, 114 insertions, 30 deletions
diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx
index 4d18f7ee605c..ad031b9e702d 100644
--- a/sw/source/core/doc/DocumentContentOperationsManager.cxx
+++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx
@@ -480,37 +480,113 @@ namespace
namespace
{
void
- lcl_CalcBreaks( std::vector<sal_Int32> & rBreaks, SwPaM const & rPam )
+ lcl_CalcBreaks(std::vector<std::pair<sal_uLong, sal_Int32>> & rBreaks, SwPaM const & rPam)
{
- SwTextNode const * const pTextNode(
- rPam.End()->nNode.GetNode().GetTextNode() );
- if (!pTextNode)
- return; // left-overlap only possible at end of selection...
+ sal_uLong const nStartNode(rPam.Start()->nNode.GetIndex());
+ sal_uLong const nEndNode(rPam.End()->nNode.GetIndex());
+ SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
+ IDocumentMarkAccess const& rIDMA(*rPam.GetDoc()->getIDocumentMarkAccess());
- const sal_Int32 nStart(rPam.Start()->nContent.GetIndex());
- const sal_Int32 nEnd (rPam.End ()->nContent.GetIndex());
- if (nEnd == pTextNode->Len())
- return; // paragraph selected until the end
+ std::stack<sw::mark::IFieldmark const*> startedFields;
- for (sal_Int32 i = nStart; i < nEnd; ++i)
+ for (sal_uLong n = nStartNode; n <= nEndNode; ++n)
{
- const sal_Unicode c(pTextNode->GetText()[i]);
- if ((CH_TXTATR_INWORD == c) || (CH_TXTATR_BREAKWORD == c))
+ SwNode *const pNode(rNodes[n]);
+ if (pNode->IsTextNode())
{
- SwTextAttr const * const pAttr( pTextNode->GetTextAttrForCharAt(i) );
- if (pAttr && pAttr->End() && (*pAttr->End() > nEnd))
+ SwTextNode & rTextNode(*pNode->GetTextNode());
+ sal_Int32 const nStart(n == nStartNode
+ ? rPam.Start()->nContent.GetIndex()
+ : 0);
+ sal_Int32 const nEnd(n == nEndNode
+ ? rPam.End()->nContent.GetIndex()
+ : rTextNode.Len());
+ for (sal_Int32 i = nStart; i < nEnd; ++i)
{
- OSL_ENSURE(pAttr->HasDummyChar(), "GetTextAttrForCharAt broken?");
- rBreaks.push_back(i);
+ const sal_Unicode c(rTextNode.GetText()[i]);
+ switch (c)
+ {
+ // note: CH_TXT_ATR_FORMELEMENT does not need handling
+ // not sure how CH_TXT_ATR_INPUTFIELDSTART/END are currently handled
+ case CH_TXTATR_INWORD:
+ case CH_TXTATR_BREAKWORD:
+ {
+ // META hints only have dummy char at the start, not
+ // at the end, so no need to check in nStartNode
+ if (n == nEndNode)
+ {
+ SwTextAttr const*const pAttr(rTextNode.GetTextAttrForCharAt(i));
+ if (pAttr && pAttr->End() && (nEnd < *pAttr->End()))
+ {
+ assert(pAttr->HasDummyChar());
+ rBreaks.emplace_back(n, i);
+ }
+ }
+ break;
+ }
+ case CH_TXT_ATR_FIELDSTART:
+ {
+ auto const pFieldMark(rIDMA.getFieldmarkAt(SwPosition(rTextNode, i)));
+ startedFields.push(pFieldMark);
+ break;
+ }
+ case CH_TXT_ATR_FIELDSEP:
+ {
+ if (startedFields.empty())
+ {
+ rBreaks.emplace_back(n, i);
+ }
+ else
+ { // no way to find the field via MarkManager...
+ assert(startedFields.top()->IsCoveringPosition(SwPosition(rTextNode, i)));
+ }
+ break;
+ }
+ case CH_TXT_ATR_FIELDEND:
+ {
+ if (startedFields.empty())
+ {
+ rBreaks.emplace_back(n, i);
+ }
+ else
+ { // fieldmarks must not overlap => stack
+ assert(startedFields.top() == rIDMA.getFieldmarkAt(SwPosition(rTextNode, i)));
+ startedFields.pop();
+ }
+ break;
+ }
+ }
}
}
+ else if (pNode->IsStartNode())
+ {
+ if (pNode->EndOfSectionIndex() <= nEndNode)
+ { // fieldmark cannot overlap node section
+ n = pNode->EndOfSectionIndex();
+ }
+ }
+ else
+ { // EndNode can actually happen with sections :(
+ assert(pNode->IsEndNode() || pNode->IsNoTextNode());
+ }
+ }
+ while (!startedFields.empty())
+ {
+ auto const& pField(startedFields.top());
+ startedFields.pop();
+ SwPosition const& rStart(pField->GetMarkStart());
+ std::pair<sal_uLong, sal_Int32> const pos(
+ rStart.nNode.GetIndex(), rStart.nContent.GetIndex());
+ auto it = std::lower_bound(rBreaks.begin(), rBreaks.end(), pos);
+ assert(it == rBreaks.end() || *it != pos);
+ rBreaks.insert(it, pos);
}
}
bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager & rDocumentContentOperations, SwPaM & rPam,
bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, bool), const bool bForceJoinNext = false)
{
- std::vector<sal_Int32> Breaks;
+ std::vector<std::pair<sal_uLong, sal_Int32>> Breaks;
lcl_CalcBreaks(Breaks, rPam);
@@ -528,24 +604,27 @@ namespace
bool bRet( true );
// iterate from end to start, to avoid invalidating the offsets!
- std::vector<sal_Int32>::reverse_iterator iter( Breaks.rbegin() );
+ auto iter( Breaks.rbegin() );
+ sal_uLong nOffset(0);
+ SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
SwPosition & rEnd( *aPam.End() );
SwPosition & rStart( *aPam.Start() );
while (iter != Breaks.rend())
{
- rStart.nContent = *iter + 1;
- if (rEnd.nContent > rStart.nContent) // check if part is empty
+ rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
+ if (rStart < rEnd) // check if part is empty
{
bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext);
+ nOffset = iter->first - rStart.nNode.GetIndex(); // deleted fly nodes...
}
- rEnd.nContent = *iter;
+ rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
++iter;
}
rStart = *rPam.Start(); // set to original start
- if (rEnd.nContent > rStart.nContent) // check if part is empty
+ if (rStart < rEnd) // check if part is empty
{
bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext);
}
@@ -957,6 +1036,7 @@ namespace
case CH_TXT_ATR_INPUTFIELDSTART:
case CH_TXT_ATR_INPUTFIELDEND:
case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
case CH_TXT_ATR_FIELDEND:
case CH_TXT_ATR_FORMELEMENT:
return false;
@@ -3055,7 +3135,7 @@ bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString
// unfortunately replace works slightly differently from delete,
// so we cannot use lcl_DoWithBreaks here...
- std::vector<sal_Int32> Breaks;
+ std::vector<std::pair<sal_uLong, sal_Int32>> Breaks;
SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
aPam.Normalize(false);
@@ -3068,7 +3148,8 @@ bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString
lcl_CalcBreaks(Breaks, aPam);
while (!Breaks.empty() // skip over prefix of dummy chars
- && (aPam.GetMark()->nContent.GetIndex() == *Breaks.begin()) )
+ && (aPam.GetMark()->nNode.GetIndex() == Breaks.begin()->first)
+ && (aPam.GetMark()->nContent.GetIndex() == Breaks.begin()->second))
{
// skip!
++aPam.GetMark()->nContent; // always in bounds if Breaks valid
@@ -3091,7 +3172,9 @@ bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString
bool bRet( true );
// iterate from end to start, to avoid invalidating the offsets!
- std::vector<sal_Int32>::reverse_iterator iter( Breaks.rbegin() );
+ auto iter( Breaks.rbegin() );
+ sal_uLong nOffset(0);
+ SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
OSL_ENSURE(aPam.GetPoint() == aPam.End(), "wrong!");
SwPosition & rEnd( *aPam.End() );
SwPosition & rStart( *aPam.Start() );
@@ -3102,20 +3185,21 @@ bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString
while (iter != Breaks.rend())
{
- rStart.nContent = *iter + 1;
- if (rEnd.nContent != rStart.nContent) // check if part is empty
+ rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
+ if (rStart < rEnd) // check if part is empty
{
bRet &= (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
? DeleteAndJoinWithRedlineImpl(aPam)
: DeleteAndJoinImpl(aPam, false);
+ nOffset = iter->first - rStart.nNode.GetIndex(); // deleted fly nodes...
}
- rEnd.nContent = *iter;
+ rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
++iter;
}
rStart = *rPam.Start(); // set to original start
- OSL_ENSURE(rEnd.nContent > rStart.nContent, "replace part empty!");
- if (rEnd.nContent > rStart.nContent) // check if part is empty
+ assert(rStart < rEnd && "replace part empty!");
+ if (rStart < rEnd) // check if part is empty
{
bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace);
}