tdf#117185 tdf#110442 sw: bring harmony & peace to fly at-char selection
Use IsDestroyFrameAnchoredAtChar() to harmonize the at-char fly
selection across all relevant operations:
* CopyImpl: this is the most tricky one:
- the code in CopyWithFlyInFly() and CopyFlyInFlyImpl() is quite con-
voluted as it needs to do some things ignoring a partially selected
start node, while including it in other cases
- it had pre-existing bugs too that would lose a fly anchored to the
2nd (1st fully selected) node of a redline
- now it needs to copy the flys in the selection if it is inside a
single node
- another complication is that flys that already existed at the
insert position need to have their anchors corrected
- SwUndoInsLayFormat need to be created for the appropriate flys
- SwUndoInserts Undo/Redo needs to run the nested SwUndoInsLayFormat
at the appropriate time
- SwUndoInserts::UndoImpl() needs a special case to *never* delete
flys at the start/end of the selection because those are handled by
nested SwUndoInsLayFormat
- Insert File (shellio.cxx) needs adapting to the SwUndoInserts change
* DeleteRange: this just needs to delete the flys via DelFlyInRange()
* MoveRange:
- this is used by the old SwRangeRedline Show/Hide, i.e. on ODF export
- the SaveFlyInRange()/RestFlyInRange() was rather inadequate and
didn't even restore content indexes at all...
* IsShown: the sw_redlinehide code needs to check visibility against
the (inverted) extents
The selection behavior is changed so that at-char flys in the start and
end node of the selection are also selected, instead of having their
anchor moved to a different content index by the operation. This appears
more obvious and user-friendly, fixes tdf#110442, and is also more like
what Word does.
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-char fly).
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 (disposing the SwXParagraph runs
into the same problem...).
Crashes fixed by this:
tdf#117185
tdf#117215 except comment#12
tdf#124720
tdf#124721
tdf#124739
Previously fixed bugs tested:
i#97570 plus the 2 bugs that already have UITests
Change-Id: I4fec2a3c15ca0e64e5c4e99acfb04f59bb2bcf64
Reviewed-on: https://gerrit.libreoffice.org/75516
Tested-by: Jenkins
Reviewed-by: Michael Stahl <Michael.Stahl@cib.de>
diff --git a/sw/inc/undobj.hxx b/sw/inc/undobj.hxx
index 38cab61..3128ffc 100644
--- a/sw/inc/undobj.hxx
+++ b/sw/inc/undobj.hxx
@@ -35,6 +35,7 @@ struct SwPosition;
class SwDoc;
class SwTextFormatColl;
class SwFrameFormat;
class SwFormatAnchor;
class SwNodeIndex;
class SwNodeRange;
class SwRedlineData;
@@ -134,10 +135,11 @@ enum class DelContentType : sal_uInt16
Fly = 0x02,
Bkm = 0x08,
AllMask = 0x0b,
ExcludeAtCharFlyAtStartEnd = 0x40,
CheckNoCntnt = 0x80,
};
namespace o3tl {
template<> struct typed_flags<DelContentType> : is_typed_flags<DelContentType, 0x8b> {};
template<> struct typed_flags<DelContentType> : is_typed_flags<DelContentType, 0xcb> {};
}
/// will DelContentIndex destroy a frame anchored at character at rAnchorPos?
@@ -227,6 +229,13 @@ public:
class SwUndoInsLayFormat;
namespace sw {
std::unique_ptr<std::vector<SwFrameFormat*>>
GetFlysAnchoredAt(SwDoc & rDoc, sal_uLong nSttNode);
}
// base class for insertion of Document, Glossaries and Copy
class SwUndoInserts : public SwUndo, public SwUndRng, private SwUndoSaveContent
{
@@ -252,6 +261,10 @@ public:
// Set destination range after reading.
void SetInsertRange( const SwPaM&, bool bScanFlys = true,
bool bSttWasTextNd = true );
static bool IsCreateUndoForNewFly(SwFormatAnchor const& rAnchor,
sal_uLong const nStartNode, sal_uLong const nEndNode);
std::vector<SwFrameFormat*> * GetFlysAnchoredAt() { return pFrameFormats.get(); }
};
class SwUndoInsDoc : public SwUndoInserts
diff --git a/sw/qa/uitest/writer_tests6/tdf107975.py b/sw/qa/uitest/writer_tests6/tdf107975.py
index 202e742..333c0f7 100644
--- a/sw/qa/uitest/writer_tests6/tdf107975.py
+++ b/sw/qa/uitest/writer_tests6/tdf107975.py
@@ -27,6 +27,8 @@ class tdf107975(UITestCase):
xWriterDoc = self.xUITest.getTopFocusWindow()
xWriterEdit = xWriterDoc.getChild("writer_edit")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 1)
#Press CTRL+A and + CTRL+C
self.xUITest.executeCommand(".uno:SelectAll")
self.xUITest.executeCommand(".uno:Copy")
@@ -34,9 +36,46 @@ class tdf107975(UITestCase):
xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "RIGHT"}))
#Paste CTRL+V
self.xUITest.executeCommand(".uno:Paste")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 2)
#Undo paste CTRL+Z -> Crash
self.xUITest.executeCommand(".uno:Undo")
self.assertEqual(document.Text.String[0:3], "ABC")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 1)
self.xUITest.executeCommand(".uno:Redo")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 2)
self.xUITest.executeCommand(".uno:Undo")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 1)
self.xUITest.executeCommand(".uno:Redo")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 2)
self.xUITest.executeCommand(".uno:Undo")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 1)
# try again with anchor at start of doc which is another special case
xShape = writer_doc.getGraphicObjects().getByIndex(0)
xStart = writer_doc.getText().getStart()
xShape.attach(xStart)
#Press CTRL+A and + CTRL+C
self.xUITest.executeCommand(".uno:SelectAll")
self.xUITest.executeCommand(".uno:Copy")
#Position the mouse cursor (caret) after "ABC" below the blue image
xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "RIGHT"}))
#Paste CTRL+V
self.xUITest.executeCommand(".uno:Paste")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 2)
#Undo paste CTRL+Z -> Crash
self.xUITest.executeCommand(".uno:Undo")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 1)
self.assertEqual(document.Text.String[0:3], "ABC")
self.xUITest.executeCommand(".uno:Redo")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 2)
self.xUITest.executeCommand(".uno:Undo")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 1)
self.xUITest.executeCommand(".uno:Redo")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 2)
self.xUITest.executeCommand(".uno:Undo")
self.assertEqual(writer_doc.getGraphicObjects().getCount(), 1)
self.ui_test.close_doc()
# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx
index a4e0e67..bc632acf 100644
--- a/sw/source/core/doc/DocumentContentOperationsManager.cxx
+++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx
@@ -2003,6 +2003,9 @@ bool DocumentContentOperationsManager::DelFullPara( SwPaM& rPam )
if (pAPos &&
((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
(RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
// note: here use <= not < like in
// IsDestroyFrameAnchoredAtChar() because of the increment
// of rPam in the bDoesUndo path above!
aRg.aStart <= pAPos->nNode && pAPos->nNode <= aRg.aEnd )
{
m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFly );
@@ -2044,7 +2047,7 @@ bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos,
// Save the paragraph anchored Flys, so that they can be moved.
SaveFlyArr aSaveFlyArr;
SaveFlyInRange( rPaM, rPos.nNode, aSaveFlyArr, bool( SwMoveFlags::ALLFLYS & eMvFlags ) );
SaveFlyInRange( rPaM, rPos, aSaveFlyArr, bool( SwMoveFlags::ALLFLYS & eMvFlags ) );
// save redlines (if DOC_MOVEREDLINES is used)
SaveRedlines_t aSaveRedl;
@@ -2285,7 +2288,10 @@ bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos,
*rPaM.GetPoint() = *aSavePam.End();
// Move the Flys to the new position.
RestFlyInRange( aSaveFlyArr, rPaM.Start()->nNode, &(rPos.nNode) );
// note: rPos is at the end here; can't really tell flys that used to be
// at the start of rPam from flys that used to be at the end of rPam
// unfortunately, so some of them are going to end up with wrong anchor...
RestFlyInRange( aSaveFlyArr, *rPaM.Start(), &(rPos.nNode) );
// restore redlines (if DOC_MOVEREDLINES is used)
if( !aSaveRedl.empty() )
@@ -2391,7 +2397,10 @@ bool DocumentContentOperationsManager::MoveNodeRange( SwNodeRange& rRange, SwNod
// move the Flys to the new position
if( !aSaveFlyArr.empty() )
RestFlyInRange( aSaveFlyArr, aIdx, nullptr );
{
SwPosition const tmp(aIdx);
RestFlyInRange(aSaveFlyArr, tmp, nullptr);
}
// Add the Bookmarks back to the Document
for(auto& rBkmk : aSaveBkmks)
@@ -3296,31 +3305,36 @@ void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(const SwPosition
}
// Copy method from SwDoc - "copy Flys in Flys"
/// note: rRg/rInsPos *exclude* a partially selected start text node;
/// pCopiedPaM *includes* a partially selected start text node
void DocumentContentOperationsManager::CopyWithFlyInFly(
const SwNodeRange& rRg,
const sal_Int32 nEndContentIndex,
const SwNodeIndex& rInsPos,
const std::pair<const SwPaM&, const SwPosition&>* pCopiedPaM /*and real insert pos*/,
const bool bMakeNewFrames,
const bool bDelRedlines,
const bool bCopyFlyAtFly ) const
{
assert(!pCopiedPaM || pCopiedPaM->first.End()->nContent == nEndContentIndex);
assert(!pCopiedPaM || pCopiedPaM->first.End()->nNode == rRg.aEnd);
assert(!pCopiedPaM || pCopiedPaM->second.nNode <= rInsPos);
SwDoc* pDest = rInsPos.GetNode().GetDoc();
SaveRedlEndPosForRestore aRedlRest( rInsPos, 0 );
SwNodeIndex aSavePos( rInsPos, -1 );
bool bEndIsEqualEndPos = rInsPos == rRg.aEnd;
m_rDoc.GetNodes().CopyNodes( rRg, rInsPos, bMakeNewFrames, true );
if (rRg.aStart != rRg.aEnd)
{
SaveRedlEndPosForRestore aRedlRest( rInsPos, 0 );
// insert behind the already copied start node
m_rDoc.GetNodes().CopyNodes( rRg, rInsPos, bMakeNewFrames, true );
aRedlRest.Restore();
}
++aSavePos;
if( bEndIsEqualEndPos )
const_cast<SwNodeIndex&>(rRg.aEnd) = aSavePos;
aRedlRest.Restore();
#if OSL_DEBUG_LEVEL > 0
{
//JP 17.06.99: Bug 66973 - check count only if the selection is in
@@ -3344,7 +3358,12 @@ void DocumentContentOperationsManager::CopyWithFlyInFly(
{
::sw::UndoGuard const undoGuard(pDest->GetIDocumentUndoRedo());
CopyFlyInFlyImpl( rRg, nEndContentIndex, aSavePos, bCopyFlyAtFly );
CopyFlyInFlyImpl(rRg, pCopiedPaM ? &pCopiedPaM->first : nullptr,
// see comment below regarding use of pCopiedPaM->second
(pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->nNode)
? pCopiedPaM->second.nNode
: aSavePos,
bCopyFlyAtFly);
}
SwNodeRange aCpyRange( aSavePos, rInsPos );
@@ -3376,18 +3395,16 @@ void DocumentContentOperationsManager::CopyWithFlyInFly(
pDest->GetNodes().DelDummyNodes( aCpyRange );
}
// TODO: there is a limitation here in that it's not possible to pass a start
// content index - which means that at-character anchored frames inside
// partial 1st paragraph of redline is not copied.
// But the DelFlyInRange() that is called from DelCopyOfSection() does not
// delete it either, and it also does not delete those on partial last para of
// redline, so copying those is suppressed here too ...
// note: for the redline Show/Hide this must be in sync with
// SwRangeRedline::CopyToSection()/DelCopyOfSection()/MoveFromSection()
void DocumentContentOperationsManager::CopyFlyInFlyImpl(
const SwNodeRange& rRg,
const sal_Int32 nEndContentIndex,
SwPaM const*const pCopiedPaM,
const SwNodeIndex& rStartIdx,
const bool bCopyFlyAtFly ) const
{
assert(!pCopiedPaM || pCopiedPaM->End()->nNode == rRg.aEnd);
// First collect all Flys, sort them according to their ordering number,
// and then only copy them. This maintains the ordering numbers (which are only
// managed in the DrawModel).
@@ -3404,9 +3421,9 @@ void DocumentContentOperationsManager::CopyFlyInFlyImpl(
SwFrameFormat* pFormat = (*m_rDoc.GetSpzFrameFormats())[n];
SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
SwPosition const*const pAPos = pAnchor->GetContentAnchor();
bool bAtContent = (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA);
if ( !pAPos )
continue;
bool bAdd = false;
sal_uLong nSkipAfter = pAPos->nNode.GetIndex();
sal_uLong nStart = rRg.aStart.GetIndex();
switch ( pAnchor->GetAnchorId() )
@@ -3417,59 +3434,66 @@ void DocumentContentOperationsManager::CopyFlyInFlyImpl(
else if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove())
++nStart;
break;
case RndStdIds::FLY_AT_CHAR:
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;
break;
case RndStdIds::FLY_AT_CHAR:
{
bAdd = IsDestroyFrameAnchoredAtChar(*pAPos,
pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd));
}
break;
default:
continue;
}
if ( nStart > nSkipAfter )
continue;
if ( pAPos->nNode > rRg.aEnd )
continue;
//frames at the last source node are not always copied:
//- if the node is empty and is the last node of the document or a table cell
// or a text frame then they have to be copied
//- if the content index in this node is > 0 then paragraph and frame bound objects are copied
//- to-character bound objects are copied if their index is <= nEndContentIndex
bool bAdd = false;
if( pAPos->nNode < rRg.aEnd )
bAdd = true;
if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
if (RndStdIds::FLY_AT_CHAR != pAnchor->GetAnchorId())
{
bool bEmptyNode = false;
bool bLastNode = false;
// is the node empty?
const SwNodes& rNodes = pAPos->nNode.GetNodes();
SwTextNode* pTextNode;
if( nullptr != ( pTextNode = pAPos->nNode.GetNode().GetTextNode() ))
if (nStart > nSkipAfter)
continue;
if (pAPos->nNode > rRg.aEnd)
continue;
//frames at the last source node are not always copied:
//- if the node is empty and is the last node of the document or a table cell
// or a text frame then they have to be copied
//- if the content index in this node is > 0 then paragraph and frame bound objects are copied
//- to-character bound objects are copied if their index is <= nEndContentIndex
if (pAPos->nNode < rRg.aEnd)
bAdd = true;
if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
{
bEmptyNode = pTextNode->GetText().isEmpty();
if( bEmptyNode )
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)
{
//last node information is only necessary to know for the last TextNode
SwNodeIndex aTmp( pAPos->nNode );
++aTmp;//goto next node
while (aTmp.GetNode().IsEndNode())
bEmptyNode = pTextNode->GetText().isEmpty();
if (bEmptyNode)
{
if( aTmp == rNodes.GetEndOfContent().GetIndex() )
//last node information is only necessary to know for the last TextNode
SwNodeIndex aTmp( pAPos->nNode );
++aTmp;//goto next node
while (aTmp.GetNode().IsEndNode())
{
bLastNode = true;
break;
if (aTmp == rNodes.GetEndOfContent().GetIndex())
{
bLastNode = true;
break;
}
++aTmp;
}
++aTmp;
}
}
}
bAdd = bLastNode && bEmptyNode;
if( !bAdd )
{
if( bAtContent )
bAdd = nEndContentIndex > 0;
else
bAdd = pAPos->nContent <= nEndContentIndex;
bAdd = bLastNode && bEmptyNode;
if (!bAdd)
{
// technically old code checked nContent of AT_FLY which is pointless
bAdd = pCopiedPaM && 0 < pCopiedPaM->End()->nContent.GetIndex();
}
}
}
if( bAdd )
@@ -3508,7 +3532,8 @@ void DocumentContentOperationsManager::CopyFlyInFlyImpl(
// Note: The anchor text node *have* to be inside the copied range.
sal_uLong nAnchorTextNdNumInRange( 0 );
bool bAnchorTextNdFound( false );
SwNodeIndex aIdx( rRg.aStart );
// start at the first node for which flys are copied
SwNodeIndex aIdx(pCopiedPaM ? pCopiedPaM->Start()->nNode : rRg.aStart);
while ( !bAnchorTextNdFound && aIdx <= rRg.aEnd )
{
if ( aIdx.GetNode().IsTextNode() )
@@ -3569,7 +3594,11 @@ void DocumentContentOperationsManager::CopyFlyInFlyImpl(
if ((RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) &&
newPos.nNode.GetNode().IsTextNode() )
{
newPos.nContent.Assign( newPos.nNode.GetNode().GetTextNode(), newPos.nContent.GetIndex() );
// only if pCopiedPaM: care about partially selected start node
sal_Int32 const nContent = pCopiedPaM && pCopiedPaM->Start()->nNode == aAnchor.GetContentAnchor()->nNode
? newPos.nContent.GetIndex() - pCopiedPaM->Start()->nContent.GetIndex()
: newPos.nContent.GetIndex();
newPos.nContent.Assign(newPos.nNode.GetNode().GetTextNode(), nContent);
}
else
{
@@ -3948,7 +3977,8 @@ bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam)
m_rDoc.getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any );
// Delete and move all "Flys at the paragraph", which are within the Selection
DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode,
&rPam.GetMark()->nContent, &rPam.GetPoint()->nContent);
DelBookmarks(
pStt->nNode,
pEnd->nNode,
@@ -4388,8 +4418,8 @@ bool DocumentContentOperationsManager::CopyImpl( SwPaM& rPam, SwPosition& rPos,
SwDoc* pDoc = rPos.nNode.GetNode().GetDoc();
const bool bColumnSel = pDoc->IsClipBoard() && pDoc->IsColumnSelection();
SwPosition* pStt = rPam.Start();
SwPosition* pEnd = rPam.End();
SwPosition const*const pStt = rPam.Start();
SwPosition *const pEnd = rPam.End();
// Catch when there's no copy to do.
if( !rPam.HasMark() || ( *pStt >= *pEnd && !bColumnSel ) ||
@@ -4409,11 +4439,19 @@ bool DocumentContentOperationsManager::CopyImpl( SwPaM& rPam, SwPosition& rPos,
std::shared_ptr<SwUnoCursor> const pCopyPam(pDoc->CreateUnoCursor(rPos));
SwTableNumFormatMerge aTNFM( m_rDoc, *pDoc );
std::unique_ptr<std::vector<SwFrameFormat*>> pFlys;
std::vector<SwFrameFormat*> const* pFlysAtInsPos;
if (pDoc->GetIDocumentUndoRedo().DoesUndo())
{
pUndo = new SwUndoCpyDoc(*pCopyPam);
pDoc->GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
pFlysAtInsPos = pUndo->GetFlysAnchoredAt();
}
else
{
pFlys = sw::GetFlysAnchoredAt(*pDoc, rPos.nNode.GetIndex());
pFlysAtInsPos = pFlys.get();
}
RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
@@ -4435,7 +4473,10 @@ bool DocumentContentOperationsManager::CopyImpl( SwPaM& rPam, SwPosition& rPos,
bAfterTable = true;
}
if( !bCanMoveBack )
{
pCopyPam->GetPoint()->nNode--;
assert(pCopyPam->GetPoint()->nContent.GetIndex() == 0);
}
SwNodeRange aRg( pStt->nNode, pEnd->nNode );
SwNodeIndex aInsPos( rPos.nNode );
@@ -4561,6 +4602,8 @@ bool DocumentContentOperationsManager::CopyImpl( SwPaM& rPam, SwPosition& rPos,
pEnd->nContent -= nCpyLen;
}
aRg.aStart++;
if( bOneNode )
{
if (bCopyCollFormat)
@@ -4569,10 +4612,12 @@ bool DocumentContentOperationsManager::CopyImpl( SwPaM& rPam, SwPosition& rPos,
POP_NUMRULE_STATE
}
// copy at-char flys in rPam
aInsPos = *pDestTextNd; // update to new (start) node for flys
CopyFlyInFlyImpl(aRg, &rPam, aInsPos, false);
break;
}
aRg.aStart++;
}
}
else if( pDestTextNd )
@@ -4670,9 +4715,9 @@ bool DocumentContentOperationsManager::CopyImpl( SwPaM& rPam, SwPosition& rPos,
}
}
SfxItemSet aBrkSet( pDoc->GetAttrPool(), aBreakSetRange );
if( bCopyAll || aRg.aStart != aRg.aEnd )
{
SfxItemSet aBrkSet( pDoc->GetAttrPool(), aBreakSetRange );
if (pSttTextNd && bCopyCollFormat && pDestTextNd->HasSwAttrSet())
{
aBrkSet.Put( *pDestTextNd->GetpSwAttrSet() );
@@ -4681,7 +4726,9 @@ bool DocumentContentOperationsManager::CopyImpl( SwPaM& rPam, SwPosition& rPos,
if( SfxItemState::SET == aBrkSet.GetItemState( RES_PAGEDESC, false ) )
pDestTextNd->ResetAttr( RES_PAGEDESC );
}
}
{
SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1),
SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode()));
if (bCanMoveBack)
@@ -4695,16 +4742,48 @@ bool DocumentContentOperationsManager::CopyImpl( SwPaM& rPam, SwPosition& rPos,
if( aInsPos == pEnd->nNode )
{
SwNodeIndex aSaveIdx( aInsPos, -1 );
CopyWithFlyInFly( aRg, 0, aInsPos, &tmp, bMakeNewFrames, false );
assert(pStt->nNode != pEnd->nNode);
pEnd->nContent = 0; // TODO why this?
CopyWithFlyInFly( aRg, aInsPos, &tmp, bMakeNewFrames, false );
++aSaveIdx;
pEnd->nNode = aSaveIdx;
pEnd->nContent.Assign( aSaveIdx.GetNode().GetTextNode(), 0 );
}
else
CopyWithFlyInFly( aRg, pEnd->nContent.GetIndex(), aInsPos, &tmp, bMakeNewFrames, false );
CopyWithFlyInFly( aRg, aInsPos, &tmp, bMakeNewFrames, false );
bCopyBookmarks = false;
}
// 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?)
if (pFlysAtInsPos)
{
// init *again* - because CopyWithFlyInFly moved startPos
SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1),
SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode()));
if (bCanMoveBack)
{ // pCopyPam is actually 1 before the copy range so move it fwd
SwPaM temp(*pCopyPam->GetPoint());
temp.Move(fnMoveForward, GoInContent);
startPos = *temp.GetPoint();
}
assert(startPos.nNode.GetNode().IsContentNode());
for (SwFrameFormat * pFly : *pFlysAtInsPos)
{
SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
{
SwFormatAnchor anchor(*pAnchor);
anchor.SetAnchor( &startPos );
pFly->SetFormatAttr(anchor);
}
}
}
if (bCopyAll || aRg.aStart != aRg.aEnd)
{
// Put the breaks back into the first node
if( aBrkSet.Count() && nullptr != ( pDestTextNd = pDoc->GetNodes()[
pCopyPam->GetPoint()->nNode.GetIndex()+1 ]->GetTextNode()))
diff --git a/sw/source/core/doc/DocumentLayoutManager.cxx b/sw/source/core/doc/DocumentLayoutManager.cxx
index 68e08ba..11ed401 100644
--- a/sw/source/core/doc/DocumentLayoutManager.cxx
+++ b/sw/source/core/doc/DocumentLayoutManager.cxx
@@ -426,7 +426,7 @@ SwFrameFormat *DocumentLayoutManager::CopyLayoutFormat(
//contact object itself. They should be managed by SwUndoInsLayFormat.
const ::sw::DrawUndoGuard drawUndoGuard(m_rDoc.GetIDocumentUndoRedo());
pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly( aRg, 0, aIdx, nullptr, false, true, true );
pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aIdx, nullptr, false, true, true);
}
else
{
diff --git a/sw/source/core/doc/doccomp.cxx b/sw/source/core/doc/doccomp.cxx
index 3054d17..0df9cdf 100644
--- a/sw/source/core/doc/doccomp.cxx
+++ b/sw/source/core/doc/doccomp.cxx
@@ -1532,7 +1532,7 @@ void CompareData::ShowDelete(
SwNodeIndex aInsPos( *pLineNd, nOffset );
SwNodeIndex aSavePos( aInsPos, -1 );
rData.rDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly( aRg, 0, aInsPos );
rData.rDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aInsPos);
rDoc.getIDocumentState().SetModified();
++aSavePos;
diff --git a/sw/source/core/doc/docdesc.cxx b/sw/source/core/doc/docdesc.cxx
index ca14991..c7a59f3 100644
--- a/sw/source/core/doc/docdesc.cxx
+++ b/sw/source/core/doc/docdesc.cxx
@@ -299,7 +299,7 @@ void SwDoc::CopyMasterHeader(const SwPageDesc &rChged, const SwFormatHeader &rHe
aTmp = *pSttNd->EndOfSectionNode();
GetNodes().Copy_( aRange, aTmp, false );
aTmp = *pSttNd;
GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRange, 0, aTmp);
GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRange, nullptr, aTmp);
pFormat->SetFormatAttr( SwFormatContent( pSttNd ) );
rDescFrameFormat.SetFormatAttr( SwFormatHeader( pFormat ) );
@@ -371,7 +371,7 @@ void SwDoc::CopyMasterFooter(const SwPageDesc &rChged, const SwFormatFooter &rFo
aTmp = *pSttNd->EndOfSectionNode();
GetNodes().Copy_( aRange, aTmp, false );
aTmp = *pSttNd;
GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRange, 0, aTmp);
GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRange, nullptr, aTmp);
pFormat->SetFormatAttr( SwFormatContent( pSttNd ) );
rDescFrameFormat.SetFormatAttr( SwFormatFooter( pFormat ) );
diff --git a/sw/source/core/doc/docedt.cxx b/sw/source/core/doc/docedt.cxx
index baf3280..1388bdc 100644
--- a/sw/source/core/doc/docedt.cxx
+++ b/sw/source/core/doc/docedt.cxx
@@ -45,6 +45,7 @@
#include <docedt.hxx>
#include <frmfmt.hxx>
#include <ndtxt.hxx>
#include <undobj.hxx>
#include <vector>
#include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
@@ -54,27 +55,47 @@ using namespace ::com::sun::star::linguistic2;
using namespace ::com::sun::star::i18n;
void RestFlyInRange( SaveFlyArr & rArr, const SwNodeIndex& rSttIdx,
void RestFlyInRange( SaveFlyArr & rArr, const SwPosition& rStartPos,
const SwNodeIndex* pInsertPos )
{
SwPosition aPos( rSttIdx );
SwPosition aPos(rStartPos);
for(SaveFly & rSave : rArr)
{
// create new anchor
SwFrameFormat* pFormat = rSave.pFrameFormat;
SwFormatAnchor aAnchor( pFormat->GetAnchor() );
if( rSave.bInsertPosition )
if (rSave.isAtInsertNode)
{
if( pInsertPos != nullptr )
aPos.nNode = *pInsertPos;
{
if (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA)
{
aPos.nNode = *pInsertPos;
aPos.nContent.Assign(dynamic_cast<SwIndexReg*>(&aPos.nNode.GetNode()),
rSave.nContentIndex);
}
else
{
assert(aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR);
aPos = rStartPos;
}
}
else
aPos.nNode = rSttIdx.GetIndex();
{
aPos.nNode = rStartPos.nNode;
aPos.nContent.Assign(dynamic_cast<SwIndexReg*>(&aPos.nNode.GetNode()), 0);
}
}
else
aPos.nNode = rSttIdx.GetIndex() + rSave.nNdDiff;
{
aPos.nNode = rStartPos.nNode.GetIndex() + rSave.nNdDiff;
aPos.nContent.Assign(dynamic_cast<SwIndexReg*>(&aPos.nNode.GetNode()),
rSave.nNdDiff == 0
? rStartPos.nContent.GetIndex() + rSave.nContentIndex
: rSave.nContentIndex);
}
aPos.nContent.Assign(dynamic_cast<SwIndexReg*>(&aPos.nNode.GetNode()), 0);
SwFormatAnchor aAnchor( pFormat->GetAnchor() );
aAnchor.SetAnchor( &aPos );
pFormat->GetDoc()->GetSpzFrameFormats()->push_back( pFormat );
// SetFormatAttr should call Modify() and add it to the node
@@ -83,7 +104,7 @@ void RestFlyInRange( SaveFlyArr & rArr, const SwNodeIndex& rSttIdx,
if (pCNd && pCNd->getLayoutFrame(pFormat->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr))
pFormat->MakeFrames();
}
sw::CheckAnchoredFlyConsistency(*rSttIdx.GetNode().GetDoc());
sw::CheckAnchoredFlyConsistency(*rStartPos.nNode.GetNode().GetDoc());
}
void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr )
@@ -100,6 +121,9 @@ void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr )
rRg.aStart <= pAPos->nNode && pAPos->nNode < rRg.aEnd )
{
SaveFly aSave( pAPos->nNode.GetIndex() - rRg.aStart.GetIndex(),
(RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())
? pAPos->nContent.GetIndex()
: 0,
pFormat, false );
rArr.push_back( aSave );
pFormat->DelFrames();
@@ -113,7 +137,7 @@ void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr )
sw::CheckAnchoredFlyConsistency(*rRg.aStart.GetNode().GetDoc());
}
void SaveFlyInRange( const SwPaM& rPam, const SwNodeIndex& rInsPos,
void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos,
SaveFlyArr& rArr, bool bMoveAllFlys )
{
SwFrameFormats& rFormats = *rPam.GetPoint()->nNode.GetNode().GetDoc()->GetSpzFrameFormats();
@@ -142,12 +166,14 @@ void SaveFlyInRange( const SwPaM& rPam, const SwNodeIndex& rInsPos,
(RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
// do not move if the InsPos is in the ContentArea of the Fly
( nullptr == ( pContentIdx = pFormat->GetContent().GetContentIdx() ) ||
!( *pContentIdx < rInsPos &&
rInsPos < pContentIdx->GetNode().EndOfSectionIndex() )) )
!(*pContentIdx < rInsPos.nNode &&
rInsPos.nNode < pContentIdx->GetNode().EndOfSectionIndex())))
{
bool bInsPos = false;
if( !bMoveAllFlys && rEndNdIdx == pAPos->nNode )
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
@@ -160,12 +186,23 @@ void SaveFlyInRange( const SwPaM& rPam, const SwNodeIndex& rInsPos,
pFormat->SetFormatAttr( aAnchor );
}
}
else if( ( rSttNdIdx.GetIndex() + nSttOff <= pAPos->nNode.GetIndex()
&& pAPos->nNode.GetIndex() <= rEndNdIdx.GetIndex() - nOff ) ||
( bInsPos = (rInsPos == pAPos->nNode) ))
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())
&& IsDestroyFrameAnchoredAtChar(*pAPos, *rPam.Start(), *rPam.End()))
|| (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
&& rSttNdIdx.GetIndex() + nSttOff <= pAPos->nNode.GetIndex()
&& pAPos->nNode.GetIndex() <= rEndNdIdx.GetIndex() - nOff)
|| (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
&& (bInsPos = (rInsPos.nNode == pAPos->nNode)))
|| (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
&& (bInsPos = (rInsPos == *pAPos))))
{
SaveFly aSave( pAPos->nNode.GetIndex() - rSttNdIdx.GetIndex(),
(RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())
? (pAPos->nNode == rSttNdIdx)
? pAPos->nContent.GetIndex() - rPam.Start()->nContent.GetIndex()
: pAPos->nContent.GetIndex()
: 0,
pFormat, bInsPos );
rArr.push_back( aSave );
pFormat->DelFrames();
@@ -183,8 +220,18 @@ void SaveFlyInRange( const SwPaM& rPam, const SwNodeIndex& rInsPos,
/// Delete and move all Flys at the paragraph, that are within the selection.
/// If there is a Fly at the SPoint, it is moved onto the Mark.
void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
const SwNodeIndex& rPtNdIdx )
const SwNodeIndex& rPtNdIdx,
SwIndex const*const pMkIdx, SwIndex const*const pPtIdx)
{
assert((pMkIdx == nullptr) == (pPtIdx == nullptr));
SwPosition const point(pPtIdx
? SwPosition(rPtNdIdx, *pPtIdx)
: SwPosition(rPtNdIdx));
SwPosition const mark(pPtIdx
? SwPosition(rMkNdIdx, *pMkIdx)
: 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();
@@ -195,14 +242,18 @@ void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
const SwFormatAnchor &rAnch = pFormat->GetAnchor();
SwPosition const*const pAPos = rAnch.GetContentAnchor();
if (pAPos &&
((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
(rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)) &&
( bDelFwrd
? rMkNdIdx < pAPos->nNode && pAPos->nNode <= rPtNdIdx
: rPtNdIdx <= pAPos->nNode && pAPos->nNode < rMkNdIdx ))
(((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
&& (bDelFwrd
? rMkNdIdx < pAPos->nNode && pAPos->nNode <= rPtNdIdx
: rPtNdIdx <= pAPos->nNode && pAPos->nNode < rMkNdIdx))
|| ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
&& IsDestroyFrameAnchoredAtChar(*pAPos, rStart, rEnd, pPtIdx
? DelContentType::AllMask
: DelContentType::AllMask|DelContentType::CheckNoCntnt))))
{
// Only move the Anchor??
if( rPtNdIdx == pAPos->nNode )
if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
&& rPtNdIdx == pAPos->nNode )
{
SwFormatAnchor aAnch( pFormat->GetAnchor() );
SwPosition aPos( rMkNdIdx );
diff --git a/sw/source/core/doc/docfmt.cxx b/sw/source/core/doc/docfmt.cxx
index 5bd3642..10bbb0d 100644
--- a/sw/source/core/doc/docfmt.cxx
+++ b/sw/source/core/doc/docfmt.cxx
@@ -1410,7 +1410,7 @@ void SwDoc::CopyPageDescHeaderFooterImpl( bool bCpyHeader,
aTmpIdx = *pSttNd->EndOfSectionNode();
rSrcNds.Copy_( aRg, aTmpIdx );
aTmpIdx = *pSttNd;
rSrcFormat.GetDoc()->GetDocumentContentOperationsManager().CopyFlyInFlyImpl( aRg, 0, aTmpIdx );
rSrcFormat.GetDoc()->GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRg, nullptr, aTmpIdx);
pNewFormat->SetFormatAttr( SwFormatContent( pSttNd ));
}
else
diff --git a/sw/source/core/doc/docglbl.cxx b/sw/source/core/doc/docglbl.cxx
index d4522f5..771512c 100644
--- a/sw/source/core/doc/docglbl.cxx
+++ b/sw/source/core/doc/docglbl.cxx
@@ -315,7 +315,7 @@ bool SwDoc::SplitDoc( sal_uInt16 eDocType, const OUString& rPath, bool bOutline,
pDoc->GetNodes().Delete( aIdx );
// All Flys in the section
GetDocumentContentOperationsManager().CopyFlyInFlyImpl( aRg, 0, aIdx );
GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRg, nullptr, aIdx);
// And what's with all the Bookmarks?
// ?????
diff --git a/sw/source/core/doc/docredln.cxx b/sw/source/core/doc/docredln.cxx
index 6b420cf..d18a3054 100644
--- a/sw/source/core/doc/docredln.cxx
+++ b/sw/source/core/doc/docredln.cxx
@@ -1419,7 +1419,7 @@ void SwRangeRedline::CopyToSection()
{
SwNodeIndex aInsPos( *pSttNd->EndOfSectionNode() );
SwNodeRange aRg( pStt->nNode, 0, pEnd->nNode, 1 );
pDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly( aRg, 0, aInsPos );
pDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aInsPos);
}
}
m_pContentSect = new SwNodeIndex( *pSttNd );
diff --git a/sw/source/core/doc/tblcpy.cxx b/sw/source/core/doc/tblcpy.cxx
index 43e06fc..4975217 100644
--- a/sw/source/core/doc/tblcpy.cxx
+++ b/sw/source/core/doc/tblcpy.cxx
@@ -518,7 +518,7 @@ static void lcl_CpyBox( const SwTable& rCpyTable, const SwTableBox* pCpyBox,
SwNodeIndex aSavePos( aInsIdx, -1 );
if (pRg)
pCpyDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly( *pRg, 0, aInsIdx, nullptr, false );
pCpyDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(*pRg, aInsIdx, nullptr, false);
else
pDoc->GetNodes().MakeTextNode( aInsIdx, pDoc->GetDfltTextFormatColl() );
++aSavePos;
diff --git a/sw/source/core/doc/tblrwcl.cxx b/sw/source/core/doc/tblrwcl.cxx
index 1084e4c..b7b2821 100644
--- a/sw/source/core/doc/tblrwcl.cxx
+++ b/sw/source/core/doc/tblrwcl.cxx
@@ -1896,7 +1896,7 @@ static void lcl_CopyBoxToDoc(FndBox_ const& rFndBox, CpyPara *const pCpyPara)
*rFndBox.GetBox()->GetSttNd()->EndOfSectionNode() );
SwNodeIndex aInsIdx( *pBox->GetSttNd(), 1 );
pFromDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly( aCpyRg, 0, aInsIdx, nullptr, false );
pFromDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aCpyRg, aInsIdx, nullptr, false);
// Delete the initial TextNode
pCpyPara->pDoc->GetNodes().Delete( aInsIdx );
}
diff --git a/sw/source/core/docnode/section.cxx b/sw/source/core/docnode/section.cxx
index 8ea5318..be3d20e 100644
--- a/sw/source/core/docnode/section.cxx
+++ b/sw/source/core/docnode/section.cxx
@@ -1352,7 +1352,7 @@ static void lcl_UpdateLinksInSect( SwBaseLink& rUpdLnk, SwSectionNode& rSectNd )
SwTableNumFormatMerge aTNFM( *pSrcDoc, *pDoc );
pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly( *pCpyRg, 0, rInsPos, nullptr, bCreateFrame );
pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(*pCpyRg, rInsPos, nullptr, bCreateFrame);
++aSave;
if( !bCreateFrame )
diff --git a/sw/source/core/inc/DocumentContentOperationsManager.hxx b/sw/source/core/inc/DocumentContentOperationsManager.hxx
index f02bf2f..d6f8f8e 100644
--- a/sw/source/core/inc/DocumentContentOperationsManager.hxx
+++ b/sw/source/core/inc/DocumentContentOperationsManager.hxx
@@ -102,14 +102,13 @@ public:
//Non-Interface methods
void CopyWithFlyInFly( const SwNodeRange& rRg,
const sal_Int32 nEndContentIndex,
const SwNodeIndex& rInsPos,
const std::pair<const SwPaM&, const SwPosition&> * pCopiedPaM = nullptr,
bool bMakeNewFrames = true,
bool bDelRedlines = true,
bool bCopyFlyAtFly = false ) const;
void CopyFlyInFlyImpl( const SwNodeRange& rRg,
const sal_Int32 nEndContentIndex,
SwPaM const*const pCopiedPaM,
const SwNodeIndex& rStartIdx,
const bool bCopyFlyAtFly = false ) const;
diff --git a/sw/source/core/inc/frmtool.hxx b/sw/source/core/inc/frmtool.hxx
index 4340e40..a127579 100644
--- a/sw/source/core/inc/frmtool.hxx
+++ b/sw/source/core/inc/frmtool.hxx
@@ -60,11 +60,13 @@ void AppendObjs( const SwFrameFormats *pTable, sal_uLong nIndex,
void AppendObjsOfNode(SwFrameFormats const* pTable, sal_uLong nIndex,
SwFrame * pFrame, SwPageFrame * pPage, SwDoc * pDoc,
std::vector<sw::Extent>::const_iterator const* pIter,
std::vector<sw::Extent>::const_iterator const* pEnd);
std::vector<sw::Extent>::const_iterator const* pEnd,
SwTextNode const* pFirstNode, SwTextNode const* pLastNode);
void RemoveHiddenObjsOfNode(SwTextNode const& rNode,
std::vector<sw::Extent>::const_iterator const* pIter,
std::vector<sw::Extent>::const_iterator const* pEnd);
std::vector<sw::Extent>::const_iterator const* pEnd,
SwTextNode const* pFirstNode, SwTextNode const* pLastNode);
bool IsAnchoredObjShown(SwTextFrame const& rFrame, SwFormatAnchor const& rAnchor);
diff --git a/sw/source/core/inc/mvsave.hxx b/sw/source/core/inc/mvsave.hxx
index f070a11..c8ff124 100644
--- a/sw/source/core/inc/mvsave.hxx
+++ b/sw/source/core/inc/mvsave.hxx
@@ -99,24 +99,30 @@ void DelBookmarks(const SwNodeIndex& rStt,
struct SaveFly
{
sal_uLong const nNdDiff; /// relative node difference
sal_Int32 const nContentIndex; ///< index in node
SwFrameFormat* const pFrameFormat; /// the fly's frame format
bool const bInsertPosition; /// if true, anchor _at_ insert position
bool const isAtInsertNode; ///< if true, anchor _at_ insert node index
SaveFly( sal_uLong nNodeDiff, SwFrameFormat* pFormat, bool bInsert )
: nNdDiff( nNodeDiff ), pFrameFormat( pFormat ), bInsertPosition( bInsert )
SaveFly( sal_uLong nNodeDiff, sal_Int32 const nCntntIdx, SwFrameFormat* pFormat, bool bInsert )
: nNdDiff(nNodeDiff)
, nContentIndex(nCntntIdx)
, pFrameFormat(pFormat)
, isAtInsertNode(bInsert)
{ }
};
typedef std::deque< SaveFly > SaveFlyArr;
void RestFlyInRange( SaveFlyArr& rArr, const SwNodeIndex& rSttIdx,
void RestFlyInRange( SaveFlyArr& rArr, const SwPosition& rSttIdx,
const SwNodeIndex* pInsPos );
void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr );
void SaveFlyInRange( const SwPaM& rPam, const SwNodeIndex& rInsPos,
void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos,
SaveFlyArr& rArr, bool bMoveAllFlys );
void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
const SwNodeIndex& rPtNdIdx );
const SwNodeIndex& rPtNdIdx,
SwIndex const* pMkIdx = nullptr,
SwIndex const* pPtIdx = nullptr);
class SwDataChanged
{
diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx
index 5564576..416037b 100644
--- a/sw/source/core/layout/frmtool.cxx
+++ b/sw/source/core/layout/frmtool.cxx
@@ -65,6 +65,7 @@
#include <objectformatter.hxx>
#include <calbck.hxx>
#include <ndtxt.hxx>
#include <undobj.hxx>
#include <DocumentSettingManager.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentTimerAccess.hxx>
@@ -1041,7 +1042,8 @@ void AppendObj(SwFrame *const pFrame, SwPageFrame *const pPage, SwFrameFormat *c
static bool IsShown(sal_uLong const nIndex,
const SwFormatAnchor & rAnch,
std::vector<sw::Extent>::const_iterator const*const pIter,
std::vector<sw::Extent>::const_iterator const*const pEnd)
std::vector<sw::Extent>::const_iterator const*const pEnd,
SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
{
assert(!pIter || *pIter == *pEnd || (*pIter)->pNode->GetIndex() == nIndex);
SwPosition const& rAnchor(*rAnch.GetContentAnchor());
@@ -1049,30 +1051,83 @@ static bool IsShown(sal_uLong const nIndex,
{
return false;
}
if (pIter && rAnch.GetAnchorId() != RndStdIds::FLY_AT_PARA
// sw_redlinehide: we want to hide AT_CHAR, but currently can't
// because Delete and Accept Redline don't delete them!
&& rAnch.GetAnchorId() != RndStdIds::FLY_AT_CHAR)
if (pIter && rAnch.GetAnchorId() != RndStdIds::FLY_AT_PARA)
{
// note: frames are not sorted by anchor position.
assert(pEnd);
assert(pFirstNode);
assert(pLastNode);
assert(rAnch.GetAnchorId() != RndStdIds::FLY_AT_FLY);
for (auto iter = *pIter; iter != *pEnd; ++iter)
{
assert(iter->nStart != iter->nEnd); // TODO possible?
assert(iter->pNode->GetIndex() == nIndex);
if (rAnchor.nContent.GetIndex() < iter->nStart)
{
return false;
}
// for AS_CHAR obviously must be <
// for AT_CHAR it is questionable whether < or <= should be used
// and there is the additional corner case of Len() to consider
// prefer < for now for symmetry (and inverted usage with
// "hidden") and handle special case explicitly
if (rAnchor.nContent.GetIndex() < iter->nEnd
|| iter->nEnd == iter->pNode->Len())
if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
{
return true;
// if there is an extent then obviously the node was not
// deleted fully...
// show if start <= pos <= end
// *or* if first-node/0 *and* not StartOfSection
// *or* if last-node/Len *and* not EndOfSection
// first determine the extent to compare to, then
// construct start/end positions for the deletion *before* the
// extent and compare once.
// the interesting corner cases are on the edge of the extent!
// no need to check for > the last extent because those
// are never visible.
if (rAnchor.nContent.GetIndex() <= iter->nEnd)
{
if (iter->nStart == 0)
{
return true;
}
else
{
SwPosition const start(
const_cast<SwTextNode&>(
iter == *pIter
? *pFirstNode // simplification
: *iter->pNode),
iter == *pIter // first extent?
? iter->pNode == pFirstNode
? 0 // at start of 1st node
: pFirstNode->Len() // previous node; simplification but should get right result
: (iter-1)->nEnd); // previous extent
SwPosition const end(*iter->pNode, iter->nStart);
return !IsDestroyFrameAnchoredAtChar(rAnchor, start, end);
}
}
else if (iter == *pEnd - 1) // special case: after last extent
{
if (iter->nEnd == iter->pNode->Len())
{
return true; // special case: end of node
}
else
{
SwPosition const start(*iter->pNode, iter->nEnd);
SwPosition const end(
const_cast<SwTextNode&>(*pLastNode), // simplification
iter->pNode == pLastNode
? iter->pNode->Len()
: 0);
return !IsDestroyFrameAnchoredAtChar(rAnchor, start, end);
}
}
}
else
{
assert(rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR);
// for AS_CHAR obviously must be <
if (rAnchor.nContent.GetIndex() < iter->nEnd)
{
return true;
}
}
}
return false;
@@ -1085,7 +1140,8 @@ static bool IsShown(sal_uLong const nIndex,
void RemoveHiddenObjsOfNode(SwTextNode const& rNode,
std::vector<sw::Extent>::const_iterator const*const pIter,
std::vector<sw::Extent>::const_iterator const*const pEnd)
std::vector<sw::Extent>::const_iterator const*const pEnd,
SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
{
std::vector<SwFrameFormat*> const*const pFlys(rNode.GetAnchoredFlys());
if (!pFlys)
@@ -1100,7 +1156,7 @@ void RemoveHiddenObjsOfNode(SwTextNode const& rNode,
&& RES_DRAWFRMFMT == pFrameFormat->Which()))
{
assert(rAnchor.GetContentAnchor()->nNode.GetIndex() == rNode.GetIndex());
if (!IsShown(rNode.GetIndex(), rAnchor, pIter, pEnd))
if (!IsShown(rNode.GetIndex(), rAnchor, pIter, pEnd, pFirstNode, pLastNode))
{
pFrameFormat->DelFrames();
}
@@ -1111,7 +1167,8 @@ void RemoveHiddenObjsOfNode(SwTextNode const& rNode,
void AppendObjsOfNode(SwFrameFormats const*const pTable, sal_uLong const nIndex,
SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc *const pDoc,
std::vector<sw::Extent>::const_iterator const*const pIter,
std::vector<sw::Extent>::const_iterator const*const pEnd)
std::vector<sw::Extent>::const_iterator const*const pEnd,
SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
{
#if OSL_DEBUG_LEVEL > 0
std::vector<SwFrameFormat*> checkFormats;
@@ -1120,7 +1177,7 @@ void AppendObjsOfNode(SwFrameFormats const*const pTable, sal_uLong const nIndex,
SwFrameFormat *pFormat = (*pTable)[i];
const SwFormatAnchor &rAnch = pFormat->GetAnchor();
if ( rAnch.GetContentAnchor() &&
IsShown(nIndex, rAnch, pIter, pEnd))
IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode))
{
checkFormats.push_back( pFormat );
}
@@ -1136,7 +1193,7 @@ void AppendObjsOfNode(SwFrameFormats const*const pTable, sal_uLong const nIndex,
SwFrameFormat *const pFormat = (*pFlys)[it];
const SwFormatAnchor &rAnch = pFormat->GetAnchor();
if ( rAnch.GetContentAnchor() &&
IsShown(nIndex, rAnch, pIter, pEnd))
IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode))
{
#if OSL_DEBUG_LEVEL > 0
std::vector<SwFrameFormat*>::iterator checkPos = std::find( checkFormats.begin(), checkFormats.end(), pFormat );
@@ -1170,7 +1227,8 @@ void AppendObjs(const SwFrameFormats *const pTable, sal_uLong const nIndex,
if (iter == pMerged->extents.end()
|| iter->pNode != pNode)
{
AppendObjsOfNode(pTable, pNode->GetIndex(), pFrame, pPage, pDoc, &iterFirst, &iter);
AppendObjsOfNode(pTable, pNode->GetIndex(), pFrame, pPage, pDoc,
&iterFirst, &iter, pMerged->pFirstNode, pMerged->pLastNode);
sal_uLong const until = iter == pMerged->extents.end()
? pMerged->pLastNode->GetIndex() + 1
: iter->pNode->GetIndex();
@@ -1181,7 +1239,7 @@ void AppendObjs(const SwFrameFormats *const pTable, sal_uLong const nIndex,
SwNode const*const pTmp(pNode->GetNodes()[i]);
if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
{
AppendObjsOfNode(pTable, pTmp->GetIndex(), pFrame, pPage, pDoc, &iter, &iter);
AppendObjsOfNode(pTable, pTmp->GetIndex(), pFrame, pPage, pDoc, &iter, &iter, pMerged->pFirstNode, pMerged->pLastNode);
}
}
if (iter == pMerged->extents.end())
@@ -1195,12 +1253,12 @@ void AppendObjs(const SwFrameFormats *const pTable, sal_uLong const nIndex,
}
else
{
return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, pDoc, nullptr, nullptr);
return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, pDoc, nullptr, nullptr, nullptr, nullptr);
}
}
else
{
return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, pDoc, nullptr, nullptr);
return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, pDoc, nullptr, nullptr, nullptr, nullptr);
}
}
@@ -1225,7 +1283,8 @@ bool IsAnchoredObjShown(SwTextFrame const& rFrame, SwFormatAnchor const& rAnchor
assert(pNode->GetRedlineMergeFlag() != SwNode::Merge::Hidden);
if (pNode == &pAnchor->nNode.GetNode())
{
ret = IsShown(pNode->GetIndex(), rAnchor, &iterFirst, &iter);
ret = IsShown(pNode->GetIndex(), rAnchor, &iterFirst, &iter,
pMergedPara->pFirstNode, pMergedPara->pLastNode);
break;
}
if (iter == pMergedPara->extents.end())
diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx
index cfd0e4f..95af7d9 100644
--- a/sw/source/core/layout/wsfrm.cxx
+++ b/sw/source/core/layout/wsfrm.cxx
@@ -4180,18 +4180,19 @@ static void AddRemoveFlysForNode(
SwPageFrame *const pPage,
SwTextNode const*const pNode,
std::vector<sw::Extent>::const_iterator & rIterFirst,
std::vector<sw::Extent>::const_iterator const& rIterEnd)
std::vector<sw::Extent>::const_iterator const& rIterEnd,
SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
{
if (pNode == &rTextNode)
{ // remove existing hidden at-char anchored flys
RemoveHiddenObjsOfNode(rTextNode, &rIterFirst, &rIterEnd);
RemoveHiddenObjsOfNode(rTextNode, &rIterFirst, &rIterEnd, pFirstNode, pLastNode);
}
else if (rTextNode.GetIndex() < pNode->GetIndex())
{
// pNode's frame has been deleted by CheckParaRedlineMerge()
AppendObjsOfNode(&rTable,
pNode->GetIndex(), &rFrame, pPage, rTextNode.GetDoc(),
&rIterFirst, &rIterEnd);
&rIterFirst, &rIterEnd, pFirstNode, pLastNode);
if (pSkipped)
{
// if a fly has been added by AppendObjsOfNode, it must be skipped; if not, then it doesn't matter if it's skipped or not because it has no frames and because of that it would be skipped anyway
@@ -4239,7 +4240,8 @@ void AddRemoveFlysAnchoredToFrameStartingAtNode(
|| iter->pNode != pNode)
{
AddRemoveFlysForNode(rFrame, rTextNode, pSkipped, rTable, pPage,
pNode, iterFirst, iter);
pNode, iterFirst, iter,
pMerged->pFirstNode, pMerged->pLastNode);
sal_uLong const until = iter == pMerged->extents.end()
? pMerged->pLastNode->GetIndex() + 1
: iter->pNode->GetIndex();
@@ -4251,7 +4253,8 @@ void AddRemoveFlysAnchoredToFrameStartingAtNode(
if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
{
AddRemoveFlysForNode(rFrame, rTextNode, pSkipped,
rTable, pPage, pTmp->GetTextNode(), iter, iter);
rTable, pPage, pTmp->GetTextNode(), iter, iter,
pMerged->pFirstNode, pMerged->pLastNode);
}
}
if (iter == pMerged->extents.end())
diff --git a/sw/source/core/txtnode/atrftn.cxx b/sw/source/core/txtnode/atrftn.cxx
index 81afed7..18bf9c5 100644
--- a/sw/source/core/txtnode/atrftn.cxx
+++ b/sw/source/core/txtnode/atrftn.cxx
@@ -398,7 +398,7 @@ void SwTextFootnote::CopyFootnote(
SwNodeIndex aEnd( *aStart.GetNode().EndOfSectionNode() );
sal_uLong nDestLen = aEnd.GetIndex() - aStart.GetIndex() - 1;
m_pTextNode->GetDoc()->GetDocumentContentOperationsManager().CopyWithFlyInFly( aRg, 0, aEnd );
m_pTextNode->GetDoc()->GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aEnd);
// in case the destination section was not empty, delete the old nodes
// before: Src: SxxxE, Dst: SnE
diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx
index ac36268..612ca35 100644
--- a/sw/source/core/undo/undobj.cxx
+++ b/sw/source/core/undo/undobj.cxx
@@ -1020,7 +1020,9 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
pHistory->Add( *static_cast<SwFlyFrameFormat *>(pFormat), nChainInsPos );
n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
}
else if( !( DelContentType::CheckNoCntnt & nDelContentType ) )
else if (!((DelContentType::CheckNoCntnt |
DelContentType::ExcludeAtCharFlyAtStartEnd)
& nDelContentType))
{
if( *pStt <= *pAPos && *pAPos < *pEnd )
{
@@ -1521,19 +1523,55 @@ OUString ShortenString(const OUString & rStr, sal_Int32 nLength, const OUString
+ rStr.copy(rStr.getLength() - nBackLen);
}
static bool IsAtEndOfSection(SwPosition const& rAnchorPos)
{
SwNodeIndex node(*rAnchorPos.nNode.GetNode().EndOfSectionNode());
SwContentNode *const pNode(SwNodes::GoPrevious(&node));
assert(pNode);
assert(rAnchorPos.nNode <= node); // last valid anchor pos is last content
return node == rAnchorPos.nNode && rAnchorPos.nContent == pNode->Len();
}
static bool IsAtStartOfSection(SwPosition const& rAnchorPos)
{
SwNodes const& rNodes(rAnchorPos.nNode.GetNodes());
SwNodeIndex node(*rAnchorPos.nNode.GetNode().StartOfSectionNode());
SwContentNode *const pNode(rNodes.GoNext(&node));
assert(pNode);
(void) pNode;
assert(node <= rAnchorPos.nNode);
return node == rAnchorPos.nNode && rAnchorPos.nContent == 0;
}
bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos,
SwPosition const & rStart, SwPosition const & rEnd,
DelContentType const nDelContentType)
{
// Here we identified the objects to destroy:
// - anchored between start and end of the selection
// - anchored in start of the selection with "CheckNoContent"
// - anchored in start of sel. and the selection start at pos 0
return (rAnchorPos.nNode < rEnd.nNode)
&& ( (DelContentType::CheckNoCntnt & nDelContentType)
|| (rStart.nNode < rAnchorPos.nNode)
|| !rStart.nContent.GetIndex()
);
// 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?
return (rStart < rAnchorPos) && (rAnchorPos < rEnd);
}
// in general, exclude the start and end position
return ((rStart < rAnchorPos)
|| (rStart == rAnchorPos
&& !(nDelContentType & DelContentType::ExcludeAtCharFlyAtStartEnd)
// special case: fully deleted node
&& ((rStart.nNode != rEnd.nNode && rStart.nContent == 0)
|| IsAtStartOfSection(rAnchorPos))))
&& ((rAnchorPos < rEnd)
|| (rAnchorPos == rEnd
&& !(nDelContentType & DelContentType::ExcludeAtCharFlyAtStartEnd)
// special case: fully deleted node
&& ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len())
|| IsAtEndOfSection(rAnchorPos))));
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/untblk.cxx b/sw/source/core/undo/untblk.cxx
index 4746562..8b206e0 100644
--- a/sw/source/core/undo/untblk.cxx
+++ b/sw/source/core/undo/untblk.cxx
@@ -32,6 +32,34 @@
#include <rolbck.hxx>
#include <redline.hxx>
namespace sw {
std::unique_ptr<std::vector<SwFrameFormat*>>
GetFlysAnchoredAt(SwDoc & rDoc, sal_uLong const nSttNode)
{
std::unique_ptr<std::vector<SwFrameFormat*>> pFrameFormats;
const size_t nArrLen = rDoc.GetSpzFrameFormats()->size();
for (size_t n = 0; n < nArrLen; ++n)
{
SwFrameFormat *const pFormat = (*rDoc.GetSpzFrameFormats())[n];
SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
SwPosition const*const pAPos = pAnchor->GetContentAnchor();
if (pAPos
&& nSttNode == pAPos->nNode.GetIndex()
&& ((pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
|| (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)))
{
if (!pFrameFormats)
pFrameFormats.reset( new std::vector<SwFrameFormat*> );
pFrameFormats->push_back( pFormat );
}
}
return pFrameFormats;
}
} // namespace sw
//note: parameter is SwPam just so we can init SwUndRng, the End is ignored!
SwUndoInserts::SwUndoInserts( SwUndoId nUndoId, const SwPaM& rPam )
: SwUndo( nUndoId, rPam.GetDoc() ), SwUndRng( rPam ),
pTextFormatColl( nullptr ), pLastNdColl(nullptr),
@@ -53,22 +81,7 @@ SwUndoInserts::SwUndoInserts( SwUndoId nUndoId, const SwPaM& rPam )
// These flys will be saved in pFrameFormats array (only flys which exist BEFORE insertion!)
// Then in SwUndoInserts::SetInsertRange the flys saved in pFrameFormats will NOT create Undos.
// m_FlyUndos will only be filled with newly inserted flys.
const size_t nArrLen = pDoc->GetSpzFrameFormats()->size();
for( size_t n = 0; n < nArrLen; ++n )
{
SwFrameFormat* pFormat = (*pDoc->GetSpzFrameFormats())[n];
SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
const SwPosition* pAPos = pAnchor->GetContentAnchor();
if (pAPos &&
(pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA) &&
nSttNode == pAPos->nNode.GetIndex() )
{
if( !pFrameFormats )
pFrameFormats.reset( new std::vector<SwFrameFormat*> );
pFrameFormats->push_back( pFormat );
}
}
pFrameFormats = sw::GetFlysAnchoredAt(*pDoc, nSttNode);
}
// consider Redline
if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
@@ -124,10 +137,7 @@ void SwUndoInserts::SetInsertRange( const SwPaM& rPam, bool bScanFlys,
{
SwFrameFormat* pFormat = (*pDoc->GetSpzFrameFormats())[n];
SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
SwPosition const*const pAPos = pAnchor->GetContentAnchor();
if (pAPos &&
(pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA) &&
(nSttNode == pAPos->nNode.GetIndex() || nEndNode == pAPos->nNode.GetIndex()))
if (IsCreateUndoForNewFly(*pAnchor, nSttNode, nEndNode))
{
std::vector<SwFrameFormat*>::iterator it;
if( !pFrameFormats ||
@@ -145,6 +155,29 @@ void SwUndoInserts::SetInsertRange( const SwPaM& rPam, bool bScanFlys,
}
}
/** This is not the same as IsDestroyFrameAnchoredAtChar()
and intentionally so: because the SwUndoInserts::UndoImpl() must remove
the flys at the start/end position that were inserted but not the ones
at the start/insert position that were already there;
handle all at-char flys at start/end node like this, even if they're
not *on* the start/end position, because it makes it easier to ensure
that the Undo/Redo run in inverse order.
*/
bool SwUndoInserts::IsCreateUndoForNewFly(SwFormatAnchor const& rAnchor,
sal_uLong const nStartNode, sal_uLong const nEndNode)
{
assert(nStartNode <= nEndNode);
// check all at-char flys at the start/end nodes:
// ExcludeAtCharFlyAtStartEnd will exclude them!
SwPosition const*const pAnchorPos = rAnchor.GetContentAnchor();
return pAnchorPos != nullptr
&& ( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
|| rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
&& ( nStartNode == pAnchorPos->nNode.GetIndex()
|| nEndNode == pAnchorPos->nNode.GetIndex());
}
SwUndoInserts::~SwUndoInserts()
{
if (m_pUndoNodeIndex) // delete also the section from UndoNodes array
@@ -185,6 +218,8 @@ void SwUndoInserts::UndoImpl(::sw::UndoRedoContext & rContext)
SwDoc& rDoc = rContext.GetDoc();
SwPaM& rPam = AddUndoRedoPaM(rContext);
nNdDiff = 0;
if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
@@ -204,14 +239,35 @@ void SwUndoInserts::UndoImpl(::sw::UndoRedoContext & rContext)
}
RemoveIdxFromRange(rPam, false);
SetPaM(rPam);
SetPaM(rPam);
}
// ... for consistency with the Insert File code in shellio.cxx, which
// creates separate SwUndoInsLayFormat for mysterious reasons, do this
// *before* anything else:
// after SetPaM but before MoveToUndoNds and DelContentIndex.
// note: there isn't an order dep wrt. initial Copy action because Undo
// overwrites the indexes but there is wrt. Redo because that uses the
// indexes
if (!m_FlyUndos.empty())
{
sal_uLong nTmp = rPam.GetPoint()->nNode.GetIndex();
for (size_t n = m_FlyUndos.size(); 0 < n; --n)
{
m_FlyUndos[ n-1 ]->UndoImpl(rContext);
}
nNdDiff += nTmp - rPam.GetPoint()->nNode.GetIndex();
}
if (nSttNode != nEndNode || nSttContent != nEndContent)
{
// are there Footnotes or ContentFlyFrames in text?
nSetPos = pHistory->Count();
nNdDiff = rPam.GetMark()->nNode.GetIndex();
DelContentIndex(*rPam.GetMark(), *rPam.GetPoint());
nNdDiff -= rPam.GetMark()->nNode.GetIndex();
sal_uLong nTmp = rPam.GetMark()->nNode.GetIndex();
DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
DelContentType::AllMask|DelContentType::ExcludeAtCharFlyAtStartEnd);
nNdDiff += nTmp - rPam.GetMark()->nNode.GetIndex();
if( *rPam.GetPoint() != *rPam.GetMark() )
{
m_pUndoNodeIndex.reset(
@@ -223,16 +279,6 @@ void SwUndoInserts::UndoImpl(::sw::UndoRedoContext & rContext)
}
}
if (!m_FlyUndos.empty())
{
sal_uLong nTmp = rPam.GetPoint()->nNode.GetIndex();
for (size_t n = m_FlyUndos.size(); 0 < n; --n)
{
m_FlyUndos[ n-1 ]->UndoImpl(rContext);
}
nNdDiff += nTmp - rPam.GetPoint()->nNode.GetIndex();
}
SwNodeIndex& rIdx = rPam.GetPoint()->nNode;
SwTextNode* pTextNode = rIdx.GetNode().GetTextNode();
if( pTextNode )
@@ -296,6 +342,9 @@ void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext)
// retrieve start position for rollback
if( ( nSttNode != nEndNode || nSttContent != nEndContent ) && m_pUndoNodeIndex)
{
auto const pFlysAtInsPos(sw::GetFlysAnchoredAt(*pDoc,
rPam.GetPoint()->nNode.GetIndex()));
const bool bMvBkwrd = MovePtBackward(rPam);
// re-insert content again (first detach m_pUndoNodeIndex!)
@@ -305,6 +354,22 @@ void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext)
if( bSttWasTextNd )
MovePtForward(rPam, bMvBkwrd);
rPam.Exchange();
// 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?)
if (pFlysAtInsPos)
{
for (SwFrameFormat * pFly : *pFlysAtInsPos)
{
SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
{
SwFormatAnchor anchor(*pAnchor);
anchor.SetAnchor( rPam.GetMark() );
pFly->SetFormatAttr(anchor);
}
}
}
}
if (pDoc->GetTextFormatColls()->IsAlive(pTextFormatColl))
@@ -323,6 +388,10 @@ void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext)
pTextNd->ChgFormatColl( pLastNdColl );
}
// tdf#108124 the SwHistoryChangeFlyAnchor/SwHistoryFlyCnt must run before
// m_FlyUndos as they were created by DelContentIndex()
pHistory->Rollback( pDoc, nSetPos );
// tdf#108124 (10/25/2017)
// During UNDO we call SwUndoInsLayFormat::UndoImpl in reverse order,
// firstly for m_FlyUndos[ m_FlyUndos.size()-1 ], etc.
@@ -336,8 +405,6 @@ void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext)
m_FlyUndos[n]->RedoImpl(rContext);
}
pHistory->Rollback( pDoc, nSetPos );
if( pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
{
RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
diff --git a/sw/source/filter/basflt/shellio.cxx b/sw/source/filter/basflt/shellio.cxx
index 6fc17d4..66c9d1d 100644
--- a/sw/source/filter/basflt/shellio.cxx
+++ b/sw/source/filter/basflt/shellio.cxx
@@ -240,27 +240,11 @@ ErrCode SwReader::Read( const Reader& rOptions )
// ok, here IsAlive is a misnomer...
if (!aFlyFrameArr.IsAlive(pFrameFormat))
{
SwPosition const*const pFrameAnchor(
rAnchor.GetContentAnchor());
if ( (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId())
|| ( pFrameAnchor
&& ( ( (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId())
&& ( (pUndoPam->GetPoint()->nNode ==
pFrameAnchor->nNode)
|| (pUndoPam->GetMark()->nNode ==
pFrameAnchor->nNode)
)
)
// #i97570# also check frames anchored AT char
|| ( (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())
&& !IsDestroyFrameAnchoredAtChar(
*pFrameAnchor,
*pUndoPam->GetPoint(),
*pUndoPam->GetMark())
)
)
)
)
// TODO: why is this not handled via SetInsertRange?
|| SwUndoInserts::IsCreateUndoForNewFly(rAnchor,
pUndoPam->GetPoint()->nNode.GetIndex(),
pUndoPam->GetMark()->nNode.GetIndex()))
{
if( bChkHeaderFooter &&
(RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) &&