summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Stahl <Michael.Stahl@cib.de>2019-04-25 18:17:29 +0200
committerMichael Stahl <Michael.Stahl@cib.de>2019-04-26 15:55:23 +0200
commit41df42a8dc758571dd7635c358a09bf412051649 (patch)
tree96934a79a39bbc90d8454ef6317f4e5cf9f896d6
parenteca4f30e430bf34dd52f7583f962860012933413 (diff)
tdf#38703 i#42807 tdf#123313 sw: add Undo of ToX Update
* SwTOXBaseSection::Update(): refactor this to *first* collect the info from the document (creating CrossRefHeadingBookmark as a side effect) and *then* do the node deletion/insertion of the ToX itself * add new SwUndoUpdateIndex class for the update itself; it does 3 of the 4 required steps, the last one is done by a SwUndoDelSection that is already created when the original title section is deleted * SwUndoInsSection::RedoImpl() for a ToX should not Update the ToX because that's the job of SwUndoUpdateIndex * SwUndoSaveSection::RestoreSection()/MoveFromUndoNds() need to create frames for the moved nodes; not sure why other users of RestoreSection() don't do this currently so add a flag * SwUndoSaveContent::MoveToUndoNds() should *always* delete frames; seriously, why would leaving frames alive ever be a good idea? * SwUndoSaveSection::SaveSection() should not delete the surrounding section node of the ToX, so add a flag to prevent it This fixes tdf#123313, the SwUndoInsBookmark are now appended at the right time. This should fix the crash in i#42807 properly. In order to fix these bugs, implementing the feature requested in tdf#38703 was necessary. Change-Id: Ia976a118eb81ca37b0c48678825a1f5cd4031789 Reviewed-on: https://gerrit.libreoffice.org/71323 Tested-by: Jenkins Reviewed-by: Michael Stahl <Michael.Stahl@cib.de>
-rw-r--r--sw/inc/undobj.hxx7
-rw-r--r--sw/source/core/doc/doctxm.cxx118
-rw-r--r--sw/source/core/inc/UndoSection.hxx22
-rw-r--r--sw/source/core/undo/undobj.cxx20
-rw-r--r--sw/source/core/undo/unsect.cxx89
5 files changed, 196 insertions, 60 deletions
diff --git a/sw/inc/undobj.hxx b/sw/inc/undobj.hxx
index 8610ab3d38a6..38cab61b0b58 100644
--- a/sw/inc/undobj.hxx
+++ b/sw/inc/undobj.hxx
@@ -163,7 +163,8 @@ protected:
sal_uLong* pEndNdIdx = nullptr );
static void MoveFromUndoNds( SwDoc& rDoc, sal_uLong nNodeIdx,
SwPosition& rInsPos,
- const sal_uLong* pEndNdIdx = nullptr );
+ const sal_uLong* pEndNdIdx = nullptr,
+ bool bForceCreateFrames = false);
// These two methods move the SPoint back/forth from PaM. With it
// a range can be spanned for Undo/Redo. (In this case the SPoint
@@ -199,9 +200,9 @@ public:
~SwUndoSaveSection();
void SaveSection( const SwNodeIndex& rSttIdx );
- void SaveSection( const SwNodeRange& rRange );
+ void SaveSection(const SwNodeRange& rRange, bool bExpandNodes = true);
void RestoreSection( SwDoc* pDoc, SwNodeIndex* pIdx, sal_uInt16 nSectType );
- void RestoreSection( SwDoc* pDoc, const SwNodeIndex& rInsPos );
+ void RestoreSection(SwDoc* pDoc, const SwNodeIndex& rInsPos, bool bForceCreateFrames = false);
const SwHistory* GetHistory() const { return pHistory.get(); }
SwHistory* GetHistory() { return pHistory.get(); }
diff --git a/sw/source/core/doc/doctxm.cxx b/sw/source/core/doc/doctxm.cxx
index d1dd7e59139d..13ca079c2804 100644
--- a/sw/source/core/doc/doctxm.cxx
+++ b/sw/source/core/doc/doctxm.cxx
@@ -51,6 +51,7 @@
#include <txtfrm.hxx>
#include <rootfrm.hxx>
#include <UndoAttribute.hxx>
+#include <UndoSection.hxx>
#include <swundo.hxx>
#include <mdiexp.hxx>
#include <docary.hxx>
@@ -870,8 +871,50 @@ void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
SwNode2LayoutSaveUpperFrames aN2L(*pSectNd);
const_cast<SwSectionNode*>(pSectNd)->DelFrames();
+ // This would be a good time to update the Numbering
+ pDoc->UpdateNumRule();
+
+ if( GetCreateType() & SwTOXElement::Mark )
+ UpdateMarks( aIntl, pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::OutlineLevel )
+ UpdateOutline( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Template )
+ UpdateTemplate( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Ole ||
+ TOX_OBJECTS == SwTOXBase::GetType())
+ UpdateContent( SwTOXElement::Ole, pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Table ||
+ (TOX_TABLES == SwTOXBase::GetType() && IsFromObjectNames()) )
+ UpdateTable( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Graphic ||
+ (TOX_ILLUSTRATIONS == SwTOXBase::GetType() && IsFromObjectNames()))
+ UpdateContent( SwTOXElement::Graphic, pOwnChapterNode, pLayout );
+
+ if( !GetSequenceName().isEmpty() && !IsFromObjectNames() &&
+ (TOX_TABLES == SwTOXBase::GetType() ||
+ TOX_ILLUSTRATIONS == SwTOXBase::GetType() ) )
+ UpdateSequence( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Frame )
+ UpdateContent( SwTOXElement::Frame, pOwnChapterNode, pLayout );
+
+ if(TOX_AUTHORITIES == SwTOXBase::GetType())
+ UpdateAuthorities( aIntl, pLayout );
+
+ // Insert AlphaDelimitters if needed (just for keywords)
+ if( TOX_INDEX == SwTOXBase::GetType() &&
+ ( GetOptions() & SwTOIOptions::AlphaDelimiter ) )
+ InsertAlphaDelimitter( aIntl );
+
// remove old content an insert one empty textnode (to hold the layout!)
SwTextNode* pFirstEmptyNd;
+
+ SwUndoUpdateIndex * pUndo(nullptr);
{
pDoc->getIDocumentRedlineAccess().DeleteRedline( *pSectNd, true, USHRT_MAX );
@@ -886,6 +929,8 @@ void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
const SwContentNode* pCNd = aNxtIdx.GetNode().GetContentNode();
if( !pCNd )
pCNd = pDoc->GetNodes().GoNext( &aNxtIdx );
+ assert(pCNd != pFirstEmptyNd);
+ assert(pCNd->GetIndex() < pFirstEmptyNd->GetIndex());
if( pCNd->HasSwAttrSet() )
{
SfxItemSet aBrkSet( pDoc->GetAttrPool(), aBreakSetRange );
@@ -894,16 +939,28 @@ void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
pFirstEmptyNd->SetAttr( aBrkSet );
}
}
- --aEndIdx;
- SwPosition aPos( aEndIdx, SwIndex( pFirstEmptyNd, 0 ));
- SwDoc::CorrAbs( aSttIdx, aEndIdx, aPos, true );
- // delete flys in whole range including start node which requires
- // giving the node before start node as Mark parameter, hence -1.
- // (flys must be deleted because the anchor nodes are removed)
- DelFlyInRange( SwNodeIndex(aSttIdx, -1), aEndIdx );
+ if (pDoc->GetIDocumentUndoRedo().DoesUndo())
+ {
+ // note: this will first append a SwUndoDelSection from the ctor...
+ pUndo = new SwUndoUpdateIndex(*this);
+ // tdf#123313 insert Undo *after* all CrossRefBookmark Undos have
+ // been inserted by the Update*() functions
+ pDoc->GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndoUpdateIndex>(pUndo));
+ }
+ else
+ {
+ --aEndIdx;
+ SwPosition aPos( aEndIdx, SwIndex( pFirstEmptyNd, 0 ));
+ SwDoc::CorrAbs( aSttIdx, aEndIdx, aPos, true );
+
+ // delete flys in whole range including start node which requires
+ // giving the node before start node as Mark parameter, hence -1.
+ // (flys must be deleted because the anchor nodes are removed)
+ DelFlyInRange( SwNodeIndex(aSttIdx, -1), aEndIdx );
- pDoc->GetNodes().Delete( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() );
+ pDoc->GetNodes().Delete( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() );
+ }
}
// insert title of TOX
@@ -922,47 +979,12 @@ void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
SwSectionFormat* pSectFormat = pDoc->MakeSectionFormat();
pDoc->GetNodes().InsertTextSection(
aStt, *pSectFormat, headerData, nullptr, &aIdx, true, false);
- }
-
- // This would be a good time to update the Numbering
- pDoc->UpdateNumRule();
-
- if( GetCreateType() & SwTOXElement::Mark )
- UpdateMarks( aIntl, pOwnChapterNode, pLayout );
-
- if( GetCreateType() & SwTOXElement::OutlineLevel )
- UpdateOutline( pOwnChapterNode, pLayout );
-
- if( GetCreateType() & SwTOXElement::Template )
- UpdateTemplate( pOwnChapterNode, pLayout );
-
- if( GetCreateType() & SwTOXElement::Ole ||
- TOX_OBJECTS == SwTOXBase::GetType())
- UpdateContent( SwTOXElement::Ole, pOwnChapterNode, pLayout );
-
- if( GetCreateType() & SwTOXElement::Table ||
- (TOX_TABLES == SwTOXBase::GetType() && IsFromObjectNames()) )
- UpdateTable( pOwnChapterNode, pLayout );
- if( GetCreateType() & SwTOXElement::Graphic ||
- (TOX_ILLUSTRATIONS == SwTOXBase::GetType() && IsFromObjectNames()))
- UpdateContent( SwTOXElement::Graphic, pOwnChapterNode, pLayout );
-
- if( !GetSequenceName().isEmpty() && !IsFromObjectNames() &&
- (TOX_TABLES == SwTOXBase::GetType() ||
- TOX_ILLUSTRATIONS == SwTOXBase::GetType() ) )
- UpdateSequence( pOwnChapterNode, pLayout );
-
- if( GetCreateType() & SwTOXElement::Frame )
- UpdateContent( SwTOXElement::Frame, pOwnChapterNode, pLayout );
-
- if(TOX_AUTHORITIES == SwTOXBase::GetType())
- UpdateAuthorities( aIntl, pLayout );
-
- // Insert AlphaDelimitters if needed (just for keywords)
- if( TOX_INDEX == SwTOXBase::GetType() &&
- ( GetOptions() & SwTOIOptions::AlphaDelimiter ) )
- InsertAlphaDelimitter( aIntl );
+ if (pUndo)
+ {
+ pUndo->TitleSectionInserted(*pSectFormat);
+ }
+ }
// Sort the List of all TOC Marks and TOC Sections
std::vector<SwTextFormatColl*> aCollArr( GetTOXForm().GetFormMax(), nullptr );
diff --git a/sw/source/core/inc/UndoSection.hxx b/sw/source/core/inc/UndoSection.hxx
index ef8847dae1e0..985259728b64 100644
--- a/sw/source/core/inc/UndoSection.hxx
+++ b/sw/source/core/inc/UndoSection.hxx
@@ -70,6 +70,28 @@ std::unique_ptr<SwUndo> MakeUndoDelSection(SwSectionFormat const&);
std::unique_ptr<SwUndo> MakeUndoUpdateSection(SwSectionFormat const&, bool const);
+
+class SwTOXBaseSection;
+class SwUndoDelSection;
+
+class SwUndoUpdateIndex : public SwUndo
+{
+private:
+ std::unique_ptr<SwUndoDelSection> m_pTitleSectionUpdated;
+ std::unique_ptr<SwUndoSaveSection> const m_pSaveSectionOriginal;
+ std::unique_ptr<SwUndoSaveSection> const m_pSaveSectionUpdated;
+ sal_uLong const m_nStartIndex;
+
+public:
+ SwUndoUpdateIndex(SwTOXBaseSection &);
+ virtual ~SwUndoUpdateIndex() override;
+
+ void TitleSectionInserted(SwSectionFormat & rSectionFormat);
+
+ virtual void UndoImpl(::sw::UndoRedoContext &) override;
+ virtual void RedoImpl(::sw::UndoRedoContext &) override;
+};
+
#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOSECTION_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx
index 0d17568e3b09..a294c7e7e1c5 100644
--- a/sw/source/core/undo/undobj.cxx
+++ b/sw/source/core/undo/undobj.cxx
@@ -726,7 +726,7 @@ void SwUndoSaveContent::MoveToUndoNds( SwPaM& rPaM, SwNodeIndex* pNodeIdx,
if( pCpyNd || pEndNdIdx )
{
SwNodeRange aRg( pStt->nNode, 0, pEnd->nNode, 1 );
- rDoc.GetNodes().MoveNodes( aRg, rNds, aPos.nNode, false );
+ rDoc.GetNodes().MoveNodes( aRg, rNds, aPos.nNode, true );
aPos.nContent = 0;
--aPos.nNode;
}
@@ -745,7 +745,7 @@ void SwUndoSaveContent::MoveToUndoNds( SwPaM& rPaM, SwNodeIndex* pNodeIdx,
void SwUndoSaveContent::MoveFromUndoNds( SwDoc& rDoc, sal_uLong nNodeIdx,
SwPosition& rInsPos,
- const sal_uLong* pEndNdIdx )
+ const sal_uLong* pEndNdIdx, bool const bForceCreateFrames)
{
// here comes the recovery
SwNodes & rNds = rDoc.GetUndoManager().GetUndoNodes();
@@ -793,7 +793,7 @@ void SwUndoSaveContent::MoveFromUndoNds( SwDoc& rDoc, sal_uLong nNodeIdx,
SwNodeRange aRg( rNds, nNodeIdx, rNds, (pEndNdIdx
? ((*pEndNdIdx) + 1)
: rNds.GetEndOfExtras().GetIndex() ) );
- rNds.MoveNodes( aRg, rDoc.GetNodes(), rInsPos.nNode, nullptr == pEndNdIdx );
+ rNds.MoveNodes(aRg, rDoc.GetNodes(), rInsPos.nNode, nullptr == pEndNdIdx || bForceCreateFrames);
}
else {
@@ -1205,7 +1205,7 @@ void SwUndoSaveSection::SaveSection( const SwNodeIndex& rSttIdx )
}
void SwUndoSaveSection::SaveSection(
- const SwNodeRange& rRange )
+ const SwNodeRange& rRange, bool const bExpandNodes)
{
SwPaM aPam( rRange.aStart, rRange.aEnd );
@@ -1231,8 +1231,11 @@ void SwUndoSaveSection::SaveSection(
nStartPos = rRange.aStart.GetIndex();
- --aPam.GetPoint()->nNode;
- ++aPam.GetMark()->nNode;
+ if (bExpandNodes)
+ {
+ --aPam.GetPoint()->nNode;
+ ++aPam.GetMark()->nNode;
+ }
SwContentNode* pCNd = aPam.GetContentNode( false );
if( pCNd )
@@ -1266,13 +1269,14 @@ void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, SwNodeIndex* pIdx,
}
}
-void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, const SwNodeIndex& rInsPos )
+void SwUndoSaveSection::RestoreSection(
+ SwDoc *const pDoc, const SwNodeIndex& rInsPos, bool bForceCreateFrames)
{
if( ULONG_MAX != nStartPos ) // was there any content?
{
SwPosition aInsPos( rInsPos );
sal_uLong nEnd = m_pMovedStart->GetIndex() + nMvLen - 1;
- MoveFromUndoNds(*pDoc, m_pMovedStart->GetIndex(), aInsPos, &nEnd);
+ MoveFromUndoNds(*pDoc, m_pMovedStart->GetIndex(), aInsPos, &nEnd, bForceCreateFrames);
// destroy indices again, content was deleted from UndoNodes array
m_pMovedStart.reset();
diff --git a/sw/source/core/undo/unsect.cxx b/sw/source/core/undo/unsect.cxx
index f449639a7937..ef84c67a138f 100644
--- a/sw/source/core/undo/unsect.cxx
+++ b/sw/source/core/undo/unsect.cxx
@@ -29,6 +29,8 @@
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <poolfmt.hxx>
#include <docary.hxx>
#include <swundo.hxx>
#include <pam.hxx>
@@ -204,7 +206,8 @@ void SwUndoInsSection::RedoImpl(::sw::UndoRedoContext & rContext)
pLayout = pLayoutToReset;
}
pUpdateTOX = rDoc.InsertTableOf( *rPam.GetPoint(),
- *m_pTOXBase->first, m_pAttrSet.get(), true, pLayout);
+ // don't expand: will be done by SwUndoUpdateIndex::RedoImpl()
+ *m_pTOXBase->first, m_pAttrSet.get(), false, pLayout);
}
else
{
@@ -507,4 +510,88 @@ void SwUndoUpdateSection::RedoImpl(::sw::UndoRedoContext & rContext)
UndoImpl(rContext);
}
+
+SwUndoUpdateIndex::SwUndoUpdateIndex(SwTOXBaseSection & rTOX)
+ : SwUndo(SwUndoId::INSSECTION, rTOX.GetFormat()->GetDoc())
+ , m_pSaveSectionOriginal(new SwUndoSaveSection)
+ , m_pSaveSectionUpdated(new SwUndoSaveSection)
+ , m_nStartIndex(rTOX.GetFormat()->GetSectionNode()->GetIndex() + 1)
+{
+ SwDoc & rDoc(*rTOX.GetFormat()->GetDoc());
+ assert(rDoc.GetNodes()[m_nStartIndex-1]->IsSectionNode());
+ assert(rDoc.GetNodes()[rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex()-1]->IsTextNode()); // -1 for extra empty node
+ // note: title is optional
+ assert(rDoc.GetNodes()[m_nStartIndex]->IsTextNode()
+ || rDoc.GetNodes()[m_nStartIndex]->IsSectionNode());
+ SwNodeIndex const first(rDoc.GetNodes(), m_nStartIndex);
+ if (first.GetNode().IsSectionNode())
+ {
+ SwSectionFormat & rSectionFormat(*first.GetNode().GetSectionNode()->GetSection().GetFormat());
+ // note: DelSectionFormat will create & append SwUndoDelSection!
+ rDoc.DelSectionFormat(& rSectionFormat); // remove inner section nodes
+ }
+ assert(first.GetNode().IsTextNode()); // invariant: ToX section is *never* empty
+ SwNodeIndex const last(rDoc.GetNodes(), rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex() - 2); // skip empty node
+ assert(last.GetNode().IsTextNode());
+ m_pSaveSectionOriginal->SaveSection(SwNodeRange(first, last), false);
+}
+
+SwUndoUpdateIndex::~SwUndoUpdateIndex() = default;
+
+void SwUndoUpdateIndex::TitleSectionInserted(SwSectionFormat & rFormat)
+{
+ SwNodeIndex const tmp(rFormat.GetDoc()->GetNodes(), m_nStartIndex); // title inserted before empty node
+ assert(tmp.GetNode().IsSectionNode());
+ assert(tmp.GetNode().GetSectionNode()->GetSection().GetFormat() == &rFormat);
+ m_pTitleSectionUpdated.reset(static_cast<SwUndoDelSection*>(MakeUndoDelSection(rFormat).release()));
+}
+
+void SwUndoUpdateIndex::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ if (m_pTitleSectionUpdated)
+ {
+ m_pTitleSectionUpdated->RedoImpl(rContext);
+ }
+ SwNodeIndex const first(rDoc.GetNodes(), m_nStartIndex);
+ assert(first.GetNode().IsTextNode()); // invariant: ToX section is *never* empty
+ SwNodeIndex const last(rDoc.GetNodes(), rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex() - 1);
+ assert(last.GetNode().IsTextNode());
+ // dummy node so that SaveSection doesn't remove ToX section...
+ SwTextNode *const pDeletionPrevention = rDoc.GetNodes().MakeTextNode(
+ SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode()),
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT));
+ m_pSaveSectionUpdated->SaveSection(SwNodeRange(first, last), false);
+ m_pSaveSectionOriginal->RestoreSection(&rDoc, first, true);
+ // delete before restoring nested undo, so its node indexes match
+ SwNodeIndex const del(*pDeletionPrevention);
+ SwDoc::CorrAbs(del, del, SwPosition(SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode())), true);
+ rDoc.GetNodes().Delete(del);
+ // original title section will be restored by next Undo, see ctor!
+}
+
+void SwUndoUpdateIndex::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ // original title section was deleted by previous Undo, see ctor!
+ SwNodeIndex const first(rDoc.GetNodes(), m_nStartIndex);
+ assert(first.GetNode().IsTextNode()); // invariant: ToX section is *never* empty
+ SwNodeIndex const last(rDoc.GetNodes(), rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex() - 1);
+ assert(last.GetNode().IsTextNode());
+ // dummy node so that SaveSection doesn't remove ToX section...
+ SwTextNode *const pDeletionPrevention = rDoc.GetNodes().MakeTextNode(
+ SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode()),
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT));
+ m_pSaveSectionOriginal->SaveSection(SwNodeRange(first, last), false);
+ m_pSaveSectionUpdated->RestoreSection(&rDoc, first, true);
+ // delete before restoring nested undo, so its node indexes match
+ SwNodeIndex const del(*pDeletionPrevention);
+ SwDoc::CorrAbs(del, del, SwPosition(SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode())), true);
+ rDoc.GetNodes().Delete(del);
+ if (m_pTitleSectionUpdated)
+ {
+ m_pTitleSectionUpdated->UndoImpl(rContext);
+ }
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */