summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Stahl <Michael.Stahl@cib.de>2019-07-11 18:37:28 +0200
committerMichael Stahl <Michael.Stahl@cib.de>2019-07-22 08:32:07 +0200
commit28b77c89dfcafae82cf2a6d85731b643ff9290e5 (patch)
tree4d26e493cebd1d83025094a3a97b9e878bc2b930
parent22f2ecbcabf3928d5486690ca6465b7b37bc8a10 (diff)
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>
-rw-r--r--sw/inc/undobj.hxx15
-rw-r--r--sw/qa/uitest/writer_tests6/tdf107975.py39
-rw-r--r--sw/source/core/doc/DocumentContentOperationsManager.cxx217
-rw-r--r--sw/source/core/doc/DocumentLayoutManager.cxx2
-rw-r--r--sw/source/core/doc/doccomp.cxx2
-rw-r--r--sw/source/core/doc/docdesc.cxx4
-rw-r--r--sw/source/core/doc/docedt.cxx99
-rw-r--r--sw/source/core/doc/docfmt.cxx2
-rw-r--r--sw/source/core/doc/docglbl.cxx2
-rw-r--r--sw/source/core/doc/docredln.cxx2
-rw-r--r--sw/source/core/doc/tblcpy.cxx2
-rw-r--r--sw/source/core/doc/tblrwcl.cxx2
-rw-r--r--sw/source/core/docnode/section.cxx2
-rw-r--r--sw/source/core/inc/DocumentContentOperationsManager.hxx3
-rw-r--r--sw/source/core/inc/frmtool.hxx6
-rw-r--r--sw/source/core/inc/mvsave.hxx18
-rw-r--r--sw/source/core/layout/frmtool.cxx105
-rw-r--r--sw/source/core/layout/wsfrm.cxx13
-rw-r--r--sw/source/core/txtnode/atrftn.cxx2
-rw-r--r--sw/source/core/undo/undobj.cxx58
-rw-r--r--sw/source/core/undo/untblk.cxx139
-rw-r--r--sw/source/filter/basflt/shellio.cxx24
22 files changed, 549 insertions, 209 deletions
diff --git a/sw/inc/undobj.hxx b/sw/inc/undobj.hxx
index 38cab61b0b58..3128ffc788d5 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 202e7426bbaa..333c0f77b820 100644
--- a/sw/qa/uitest/writer_tests6/tdf107975.py
+++ b/sw/qa/uitest/writer_tests6/tdf107975.py
@@ -27,6 +27,34 @@ 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")
+ #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(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")
@@ -34,9 +62,20 @@ 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(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 a4e0e677597a..bc632acfdb1e 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
- {
- 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 (RndStdIds::FLY_AT_CHAR != pAnchor->GetAnchorId())
+ {
+ 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 68e08bad1b80..11ed4010f7ff 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 3054d17940f3..0df9cdf55068 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 ca14991405c5..c7a59f30e955 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 baf32805d516..1388bdc1b53a 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 5bd36426a600..10bbb0d097c8 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 d4522f54714e..771512cd2ede 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 6b420cf1351e..d18a30541dcf 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 43e06fce73e2..4975217f16e1 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 1084e4c77475..b7b28213e2ca 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 8ea531804045..be3d20e48869 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 f02bf2fb46e7..d6f8f8eb2a09 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 4340e40babc3..a127579c59ad 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 f070a11a5bd6..c8ff124af161 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 5564576079bc..416037b25fbd 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)
+ {
+ // 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
{
- return true;
+ 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 cfd0e4ff5024..95af7d95dda2 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 81afed72030b..18bf9c5d05f4 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 ac36268b40d7..612ca3500bc3 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 47465620b882..8b206e064873 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);
+ }
+
+ // ... 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 6fc17d4f9858..66c9d1d0ea7d 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()) &&