summaryrefslogtreecommitdiff
path: root/sw/source
diff options
context:
space:
mode:
authorMichael Stahl <Michael.Stahl@cib.de>2019-12-05 16:49:33 +0100
committerMichael Stahl <michael.stahl@cib.de>2019-12-06 13:26:32 +0100
commit91b2325808a75174f284c48c8b8afc118fad74e4 (patch)
treed456e57e6d0d0532592030cb0d3d20585b8fe7a4 /sw/source
parent6ed12ab2d0742f86ce25defec3c776562dbfad9a (diff)
tdf#121300 sw: consistent fly at-pargraph selection
As a follow-up to commit 28b77c89dfcafae82cf2a6d85731b643ff9290e5, add IsSelectFrameAnchoredAtPara() function and use it to harmonize at-para fly selection across all relevant operations: * CopyImpl: - it had a pre-existing bugs that would lose a fly anchored to the 2nd (1st fully selected) node of a redline - remove a bunch of code for finding the last node of the body content, which doesn't matter for the remaining at-fly checks - flys that already existed at the insert position need to have their anchors corrected * DeleteRange: - get rid of the bDelFwrd checks * MoveRange: - the ALLFLYS flag would be obsoleted by the new selection, were it not for the writerfiler "special hack", see below * also adapt A11y and UI selection, SwRootFrame::CalcFrameRects() The selection behavior is changed: * the bDelFwrd case is quite odd, some code was checking whether a deletion was "forward" or "backward" and removing only the flys at the point node while retaining the flys at the mark node; this worked in a very non-obvious way relying on sw_GetJoinFlags actually calling Exchange() on the cursor, and then SwUndoDelete stored it in m_bJoinNext, but it turns out that only SwUndoMove also has a m_bJoinNext and it's dead, and no other Undo has such a flag, so this only worked for "delete". It's not obvious what the value of this is, let's just ignore the "direction". * Selections exclude the start and end position except if it's a fully selected node or at the start or end of a section (i.e. Ctrl+A should also select every at-para fly). * An exception is made in case the selection looks like it's a backspace/delete joining 2 paragraphs; flys are not deleted in that case because it seemed annoying (and as it happens, Word does not appear to delete flys in that case), particularly if both of the nodes are already empty. This is done with a heuristic, it's conceivable to pass down some flag from DelLeft()/DelRight() but probably this is good enough. A special hack is needed to keep writerfilter happy for now; it likes to anchor flys at nodes which it then deletes in RemoveLastParagraph(), which likely could be improved there. The ALLFLYS usage in SwRangeRedline::MoveFromSection() could be removed (because the end-of-section check already handles the case) except for this, because it turns out that the ODF import runs SetRedlineFlags with a temporarily reset IsInXMLImport() flag because of its effect in thints.cxx, so during the move IsSelectFrameAnchoredAtPara() can't check it. tdf#108124 scenario works better, now everything that's selected is both copied and deleted. Fixes the problem where an at-para fly at the 2nd node of a redline where the 1st node is partially deleted was lost on ODF export. Change-Id: I168013665e70ff0a5f198e09f3fa3afc06ba0449 Reviewed-on: https://gerrit.libreoffice.org/84576 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.stahl@cib.de>
Diffstat (limited to 'sw/source')
-rw-r--r--sw/source/core/access/accframebase.cxx3
-rw-r--r--sw/source/core/access/accmap.cxx18
-rw-r--r--sw/source/core/doc/DocumentContentOperationsManager.cxx45
-rw-r--r--sw/source/core/doc/docedt.cxx57
-rw-r--r--sw/source/core/edit/edglbldc.cxx2
-rw-r--r--sw/source/core/layout/trvlfrm.cxx17
-rw-r--r--sw/source/core/undo/undobj.cxx96
-rw-r--r--sw/source/core/undo/untblk.cxx17
8 files changed, 117 insertions, 138 deletions
diff --git a/sw/source/core/access/accframebase.cxx b/sw/source/core/access/accframebase.cxx
index fba6ca85fcc6..cfbfe9ecf678 100644
--- a/sw/source/core/access/accframebase.cxx
+++ b/sw/source/core/access/accframebase.cxx
@@ -319,8 +319,7 @@ bool SwAccessibleFrameBase::GetSelectedState( )
}
else if( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA )
{
- if( ((nHere > nStartIndex) || pStart->nContent.GetIndex() ==0 )
- && (nHere < nEndIndex ) )
+ if (IsSelectFrameAnchoredAtPara(*pPos, *pStart, *pEnd))
return true;
}
else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
diff --git a/sw/source/core/access/accmap.cxx b/sw/source/core/access/accmap.cxx
index df641292b4f0..0662ec7ddf26 100644
--- a/sw/source/core/access/accmap.cxx
+++ b/sw/source/core/access/accmap.cxx
@@ -1283,19 +1283,17 @@ void SwAccessibleMap::InvalidateShapeInParaSelection()
}
else if( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA )
{
- if (((nStartIndex < nFirstNode) ||
- (nFirstNode == nStartIndex && pStart->nContent.GetIndex() == 0))
- && (nLastNode < nEndIndex))
+ uno::Reference<XAccessible> const xAcc((*aIter).second);
+ if (xAcc.is())
{
- uno::Reference < XAccessible > xAcc( (*aIter).second );
- if( xAcc.is() )
+ if (IsSelectFrameAnchoredAtPara(*pPos, *pStart, *pEnd))
+ {
static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );
- }
- else
- {
- uno::Reference < XAccessible > xAcc( (*aIter).second );
- if(xAcc.is())
+ }
+ else
+ {
static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+ }
}
}
else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx
index 731bac5b7b49..4b9ee39a1ed4 100644
--- a/sw/source/core/doc/DocumentContentOperationsManager.cxx
+++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx
@@ -3466,9 +3466,11 @@ void DocumentContentOperationsManager::CopyFlyInFlyImpl(
++nStart;
break;
case RndStdIds::FLY_AT_PARA:
- // FIXME TODO why exclude start node, this seems very questionable and causes data loss on export
- if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove())
- ++nStart;
+ {
+ bAdd = IsSelectFrameAnchoredAtPara(*pAPos,
+ pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
+ pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd));
+ }
break;
case RndStdIds::FLY_AT_CHAR:
{
@@ -3480,7 +3482,7 @@ void DocumentContentOperationsManager::CopyFlyInFlyImpl(
default:
continue;
}
- if (RndStdIds::FLY_AT_CHAR != pAnchor->GetAnchorId())
+ if (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId())
{
if (nStart > nSkipAfter)
continue;
@@ -3495,31 +3497,6 @@ void DocumentContentOperationsManager::CopyFlyInFlyImpl(
bAdd = true;
if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
{
- bool bEmptyNode = false;
- bool bLastNode = false;
- // is the node empty?
- const SwNodes& rNodes = pAPos->nNode.GetNodes();
- SwTextNode *const pTextNode = pAPos->nNode.GetNode().GetTextNode();
- if (nullptr != pTextNode)
- {
- bEmptyNode = pTextNode->GetText().isEmpty();
- if (bEmptyNode)
- {
- //last node information is only necessary to know for the last TextNode
- SwNodeIndex aTmp( pAPos->nNode );
- ++aTmp;//goto next node
- while (aTmp.GetNode().IsEndNode())
- {
- if (aTmp == rNodes.GetEndOfContent().GetIndex())
- {
- bLastNode = true;
- break;
- }
- ++aTmp;
- }
- }
- }
- bAdd = bLastNode && bEmptyNode;
if (!bAdd)
{
// technically old code checked nContent of AT_FLY which is pointless
@@ -4857,6 +4834,8 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo
// at-char anchors post SplitNode are on index 0 of 2nd node and will
// remain there - move them back to the start (end would also work?)
+ // ... also for at-para anchors; here start is preferable because
+ // it's consistent with SplitNode from SwUndoInserts::RedoImpl()
if (pFlysAtInsPos)
{
// init *again* - because CopyWithFlyInFly moved startPos
@@ -4869,6 +4848,8 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo
startPos = *temp.GetPoint();
}
assert(startPos.nNode.GetNode().IsContentNode());
+ SwPosition startPosAtPara(startPos);
+ startPosAtPara.nContent.Assign(nullptr, 0);
for (SwFrameFormat * pFly : *pFlysAtInsPos)
{
@@ -4879,6 +4860,12 @@ bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPo
anchor.SetAnchor( &startPos );
pFly->SetFormatAttr(anchor);
}
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ SwFormatAnchor anchor(*pAnchor);
+ anchor.SetAnchor( &startPosAtPara );
+ pFly->SetFormatAttr(anchor);
+ }
}
}
diff --git a/sw/source/core/doc/docedt.cxx b/sw/source/core/doc/docedt.cxx
index 3cc2e8b6144c..7edd87e12850 100644
--- a/sw/source/core/doc/docedt.cxx
+++ b/sw/source/core/doc/docedt.cxx
@@ -138,14 +138,14 @@ void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos,
const SwPosition* pPos = rPam.Start();
const SwNodeIndex& rSttNdIdx = pPos->nNode;
- short nSttOff = (!bMoveAllFlys && rSttNdIdx.GetNode().IsContentNode() &&
- pPos->nContent.GetIndex()) ? 1 : 0;
- pPos = rPam.GetPoint() == pPos ? rPam.GetMark() : rPam.GetPoint();
- const SwNodeIndex& rEndNdIdx = pPos->nNode;
- short nOff = ( bMoveAllFlys || ( rEndNdIdx.GetNode().IsContentNode() &&
- pPos->nContent == rEndNdIdx.GetNode().GetContentNode()->Len() ))
- ? 0 : 1;
+ SwPosition atParaEnd(*rPam.End());
+ if (bMoveAllFlys)
+ {
+ assert(rPam.End()->nContent.GetIndex() == rPam.End()->nNode.GetNode().GetTextNode()->Len());
+ ++atParaEnd.nNode;
+ atParaEnd.nContent.Assign(atParaEnd.nNode.GetNode().GetContentNode(), 0);
+ }
for( SwFrameFormats::size_type n = 0; n < rFormats.size(); ++n )
{
@@ -163,27 +163,13 @@ void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos,
{
bool bInsPos = false;
- if (!bMoveAllFlys
- && RndStdIds::FLY_AT_CHAR != pAnchor->GetAnchorId()
- && rEndNdIdx == pAPos->nNode)
- {
- // Do not touch Anchor, if only a part of the EndNode
- // or the whole EndNode is identical with the SttNode
- if( rSttNdIdx != pAPos->nNode )
- {
- // Only attach an anchor to the beginning or end
- SwPosition aPos( rSttNdIdx );
- SwFormatAnchor aAnchor( *pAnchor );
- aAnchor.SetAnchor( &aPos );
- pFormat->SetFormatAttr( aAnchor );
- }
- }
- else if ( (//bMoveAllFlys ... no do not check - all callers are actually from redline code, from the MoveToSection case; so check bMoveAllFlys only for AT_PARA!
- (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())
+ if ( (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
&& IsDestroyFrameAnchoredAtChar(*pAPos, *rPam.Start(), *rPam.End()))
|| (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
- && rSttNdIdx.GetIndex() + nSttOff <= pAPos->nNode.GetIndex()
- && pAPos->nNode.GetIndex() <= rEndNdIdx.GetIndex() - nOff)
+ && IsSelectFrameAnchoredAtPara(*pAPos, *rPam.Start(), atParaEnd,
+ bMoveAllFlys
+ ? DelContentType::CheckNoCntnt|DelContentType::AllMask
+ : DelContentType::AllMask))
|| (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
&& (bInsPos = (rInsPos.nNode == pAPos->nNode)))
|| (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
@@ -224,7 +210,6 @@ void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
: SwPosition(rMkNdIdx));
SwPosition const& rStart = mark <= point ? mark : point;
SwPosition const& rEnd = mark <= point ? point : mark;
- const bool bDelFwrd = rMkNdIdx.GetIndex() <= rPtNdIdx.GetIndex();
SwDoc* pDoc = rMkNdIdx.GetNode().GetDoc();
SwFrameFormats& rTable = *pDoc->GetSpzFrameFormats();
@@ -235,25 +220,14 @@ void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
SwPosition const*const pAPos = rAnch.GetContentAnchor();
if (pAPos &&
(((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
- && (bDelFwrd
- ? rMkNdIdx < pAPos->nNode && pAPos->nNode <= rPtNdIdx
- : rPtNdIdx <= pAPos->nNode && pAPos->nNode < rMkNdIdx))
+ && IsSelectFrameAnchoredAtPara(*pAPos, rStart, rEnd, pPtIdx
+ ? DelContentType::AllMask
+ : DelContentType::AllMask|DelContentType::CheckNoCntnt))
|| ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
&& IsDestroyFrameAnchoredAtChar(*pAPos, rStart, rEnd, pPtIdx
? DelContentType::AllMask
: DelContentType::AllMask|DelContentType::CheckNoCntnt))))
{
- // Only move the Anchor??
- if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
- && rPtNdIdx == pAPos->nNode )
- {
- SwFormatAnchor aAnch( pFormat->GetAnchor() );
- SwPosition aPos( rMkNdIdx );
- aAnch.SetAnchor( &aPos );
- pFormat->SetFormatAttr( aAnch );
- }
- else
- {
// If the Fly is deleted, all Flys in its content have to be deleted too.
const SwFormatContent &rContent = pFormat->GetContent();
// But only fly formats own their content, not draw formats.
@@ -274,7 +248,6 @@ void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
// DelLayoutFormat can also trigger the deletion of objects.
if( i > rTable.size() )
i = rTable.size();
- }
}
}
}
diff --git a/sw/source/core/edit/edglbldc.cxx b/sw/source/core/edit/edglbldc.cxx
index a49b1ca8c235..bbc10e095f80 100644
--- a/sw/source/core/edit/edglbldc.cxx
+++ b/sw/source/core/edit/edglbldc.cxx
@@ -325,7 +325,7 @@ bool SwEditShell::MoveGlobalDocContent( const SwGlblDocContents& rArr ,
aInsPos = pMyDoc->GetNodes().GetEndOfContent();
bool bRet = pMyDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aInsPos,
- SwMoveFlags::ALLFLYS | SwMoveFlags::CREATEUNDOOBJ );
+ SwMoveFlags::CREATEUNDOOBJ );
EndAllAction();
return bRet;
diff --git a/sw/source/core/layout/trvlfrm.cxx b/sw/source/core/layout/trvlfrm.cxx
index fe131dbc51c1..b0a46504e66a 100644
--- a/sw/source/core/layout/trvlfrm.cxx
+++ b/sw/source/core/layout/trvlfrm.cxx
@@ -2584,22 +2584,7 @@ void SwRootFrame::CalcFrameRects(SwShellCursor &rCursor)
&& ( (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
&& IsDestroyFrameAnchoredAtChar(*anchoredAt, *pStartPos, *pEndPos))
|| (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
- && *pStartPos <= *anchoredAt
- && *anchoredAt < *pEndPos)));
- if (anchoredAt != nullptr
- && rAnchor.GetAnchorId() != RndStdIds::FLY_AT_CHAR
- && *anchoredAt == *pEndPos)
- {
- const SwNodes& nodes = anchoredAt->GetDoc()->GetNodes();
- if( *pEndPos == SwPosition( nodes.GetEndOfContent()))
- inSelection = true;
- else
- {
- SwNodeIndex idx( nodes.GetEndOfContent());
- if( SwContentNode* last = SwNodes::GoPrevious( &idx ))
- inSelection = *pEndPos == SwPosition( *last, last->Len());
- }
- }
+ && IsSelectFrameAnchoredAtPara(*anchoredAt, *pStartPos, *pEndPos))));
if( inSelection )
Add( aRegion, pFly->getFrameArea() );
else if ( !pFly->IsAnLower( pStartFrame ) &&
diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx
index ef6dc1ae90c6..7ddeb7a15e3e 100644
--- a/sw/source/core/undo/undobj.cxx
+++ b/sw/source/core/undo/undobj.cxx
@@ -931,7 +931,6 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
const SwFrameFormats& rSpzArr = *pDoc->GetSpzFrameFormats();
if( !rSpzArr.empty() )
{
- const bool bDelFwrd = rMark.nNode.GetIndex() <= rPoint.nNode.GetIndex();
SwFrameFormat* pFormat;
const SwFormatAnchor* pAnchor;
size_t n = rSpzArr.size();
@@ -965,28 +964,25 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
case RndStdIds::FLY_AT_PARA:
{
pAPos = pAnchor->GetContentAnchor();
- if( pAPos )
+ if (pAPos &&
+ pStt->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode)
{
- bool bTmp;
- if( DelContentType::CheckNoCntnt & nDelContentType )
- bTmp = pStt->nNode <= pAPos->nNode && pAPos->nNode < pEnd->nNode;
- else
- {
- if (bDelFwrd)
- bTmp = rMark.nNode < pAPos->nNode &&
- pAPos->nNode <= rPoint.nNode;
- else
- bTmp = rPoint.nNode <= pAPos->nNode &&
- pAPos->nNode < rMark.nNode;
- }
-
- if (bTmp)
- {
if( !m_pHistory )
m_pHistory.reset( new SwHistory );
+ if (IsSelectFrameAnchoredAtPara(*pAPos, *pStt, *pEnd, nDelContentType))
+ {
+ m_pHistory->AddDeleteFly(*pFormat, nChainInsPos);
+ // reset n so that no Format is skipped
+ n = n >= rSpzArr.size()
+ ? rSpzArr.size() : n+1;
+ }
// Moving the anchor?
- if( !( DelContentType::CheckNoCntnt & nDelContentType ) &&
+ else if (!((DelContentType::CheckNoCntnt|DelContentType::ExcludeFlyAtStartEnd)
+ & nDelContentType) &&
+ // at least for calls from SwUndoDelete,
+ // this should work - other Undos don't
+ // remember the order of the cursor
(rPoint.nNode.GetIndex() == pAPos->nNode.GetIndex())
// Do not try to move the anchor to a table!
&& rMark.nNode.GetNode().IsTextNode())
@@ -997,14 +993,6 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
aAnch.SetAnchor( &aPos );
pFormat->SetFormatAttr( aAnch );
}
- else
- {
- m_pHistory->AddDeleteFly(*pFormat, nChainInsPos );
- // reset n so that no Format is skipped
- n = n >= rSpzArr.size() ?
- rSpzArr.size() : n+1;
- }
- }
}
}
break;
@@ -1021,7 +1009,7 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
}
else if (!((DelContentType::CheckNoCntnt |
- DelContentType::ExcludeAtCharFlyAtStartEnd)
+ DelContentType::ExcludeFlyAtStartEnd)
& nDelContentType))
{
if( *pStt <= *pAPos && *pAPos < *pEnd )
@@ -1547,6 +1535,15 @@ static bool IsAtStartOfSection(SwPosition const& rAnchorPos)
return node == rAnchorPos.nNode && rAnchorPos.nContent == 0;
}
+static bool IsNotBackspaceHeuristic(
+ SwPosition const& rStart, SwPosition const& rEnd)
+{
+ // check if the selection is backspace/delete created by DelLeft/DelRight
+ return rStart.nNode.GetIndex() + 1 != rEnd.nNode.GetIndex()
+ || rEnd.nContent != 0
+ || rStart.nContent != rStart.nNode.GetNode().GetTextNode()->Len();
+}
+
bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos,
SwPosition const & rStart, SwPosition const & rEnd,
DelContentType const nDelContentType)
@@ -1566,16 +1563,57 @@ bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos,
// in general, exclude the start and end position
return ((rStart < rAnchorPos)
|| (rStart == rAnchorPos
- && !(nDelContentType & DelContentType::ExcludeAtCharFlyAtStartEnd)
+ && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
// special case: fully deleted node
&& ((rStart.nNode != rEnd.nNode && rStart.nContent == 0)
|| IsAtStartOfSection(rAnchorPos))))
&& ((rAnchorPos < rEnd)
|| (rAnchorPos == rEnd
- && !(nDelContentType & DelContentType::ExcludeAtCharFlyAtStartEnd)
+ && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
// special case: fully deleted node
&& ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len())
|| IsAtEndOfSection(rAnchorPos))));
}
+bool IsSelectFrameAnchoredAtPara(SwPosition const & rAnchorPos,
+ SwPosition const & rStart, SwPosition const & rEnd,
+ DelContentType const nDelContentType)
+{
+ assert(rStart <= rEnd);
+
+ // CheckNoCntnt means DelFullPara which is obvious to handle
+ if (DelContentType::CheckNoCntnt & nDelContentType)
+ { // exclude selection end node because it won't be deleted
+ return (rAnchorPos.nNode < rEnd.nNode)
+ && (rStart.nNode <= rAnchorPos.nNode);
+ }
+
+ if (rAnchorPos.GetDoc()->IsInReading())
+ { // FIXME hack for writerfilter RemoveLastParagraph(); can't test file format more specific?
+ // but it MUST NOT be done during the SetRedlineFlags at the end of ODF
+ // import, where the IsInXMLImport() cannot be checked because the
+ // stupid code temp. overrides it - instead rely on setting the ALLFLYS
+ // flag in MoveFromSection() and converting that to CheckNoCntnt with
+ // adjusted cursor!
+ return (rStart.nNode < rAnchorPos.nNode) && (rAnchorPos.nNode < rEnd.nNode);
+ }
+
+ // in general, exclude the start and end position
+ return ((rStart.nNode < rAnchorPos.nNode)
+ || (rStart.nNode == rAnchorPos.nNode
+ && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+ // special case: fully deleted node
+ && ((rStart.nNode != rEnd.nNode && rStart.nContent == 0
+ // but not if the selection is backspace/delete!
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || IsAtStartOfSection(rStart))))
+ && ((rAnchorPos.nNode < rEnd.nNode)
+ || (rAnchorPos.nNode == rEnd.nNode
+ && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+ // special case: fully deleted node
+ && ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len()
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || IsAtEndOfSection(rEnd))));
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/untblk.cxx b/sw/source/core/undo/untblk.cxx
index 46f7b7a769c5..365736807059 100644
--- a/sw/source/core/undo/untblk.cxx
+++ b/sw/source/core/undo/untblk.cxx
@@ -200,15 +200,14 @@ SwUndoInserts::~SwUndoInserts()
// But storing absolute indices leads to crashes if some action in Undo fails to roll back some modifications.
// Has following main steps:
-// 1. DelContentIndex to delete footnotes, flys, bookmarks (see comment for this function)
+// 1. m_FlyUndos removes flys anchored to first and last paragraph in Undo range.
+// This array may be empty.
+// 2. DelContentIndex to delete footnotes, flys, bookmarks (see comment for this function)
// Deleted flys are stored in pHistory array.
-// First and last paragraphs flys are handled later in this function! They are not deleted by DelContentIndex!
-// For flys anchored to last paragraph, DelContentIndex re-anchors them to the last paragraph that will remain after Undo.
-// This is not fully correct, as everything between nSttNode and nEndNode should be deleted (these nodes marks range of inserted nodes).
-// But due to bug in paste (probably there), during paste all flys are anchored to last paragraph (see https://bugs.documentfoundation.org/show_bug.cgi?id=94225#c38).
-// So they should be re-anchored.
-// 2. MoveToUndoNds moves nodes to Undo nodes array and removes them from document.
-// 3. m_FlyUndos removes flys anchored to first and last paragraph in Undo range. This array may be empty.
+// First and last paragraphs flys are not deleted by DelContentIndex!
+// For flys anchored to last paragraph, DelContentIndex re-anchors them to
+// the last paragraph that will remain after Undo.
+// 3. MoveToUndoNds moves nodes to Undo nodes array and removes them from document.
// 4. Lastly (starting from if(pTextNode)), text from last paragraph is joined to last remaining paragraph and FormatColl for last paragraph is restored.
// Format coll for last paragraph is removed during execution of UndoImpl
@@ -266,7 +265,7 @@ void SwUndoInserts::UndoImpl(::sw::UndoRedoContext & rContext)
m_nSetPos = m_pHistory->Count();
sal_uLong nTmp = rPam.GetMark()->nNode.GetIndex();
DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
- DelContentType::AllMask|DelContentType::ExcludeAtCharFlyAtStartEnd);
+ DelContentType::AllMask|DelContentType::ExcludeFlyAtStartEnd);
m_nNodeDiff += nTmp - rPam.GetMark()->nNode.GetIndex();
if( *rPam.GetPoint() != *rPam.GetMark() )
{