summaryrefslogtreecommitdiffstats
path: root/sw/source/core/undo
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /sw/source/core/undo
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/undo')
-rw-r--r--sw/source/core/undo/SwRewriter.cxx71
-rw-r--r--sw/source/core/undo/SwUndoField.cxx150
-rw-r--r--sw/source/core/undo/SwUndoFmt.cxx466
-rw-r--r--sw/source/core/undo/SwUndoPageDesc.cxx341
-rw-r--r--sw/source/core/undo/SwUndoTOXChange.cxx85
-rw-r--r--sw/source/core/undo/docundo.cxx735
-rw-r--r--sw/source/core/undo/rolbck.cxx1485
-rw-r--r--sw/source/core/undo/unattr.cxx1055
-rw-r--r--sw/source/core/undo/unbkmk.cxx218
-rw-r--r--sw/source/core/undo/undel.cxx1317
-rw-r--r--sw/source/core/undo/undobj.cxx1682
-rw-r--r--sw/source/core/undo/undobj1.cxx714
-rw-r--r--sw/source/core/undo/undoflystrattr.cxx90
-rw-r--r--sw/source/core/undo/undraw.cxx559
-rw-r--r--sw/source/core/undo/unfmco.cxx87
-rw-r--r--sw/source/core/undo/unins.cxx1045
-rw-r--r--sw/source/core/undo/unmove.cxx308
-rw-r--r--sw/source/core/undo/unnum.cxx395
-rw-r--r--sw/source/core/undo/unoutl.cxx49
-rw-r--r--sw/source/core/undo/unovwr.cxx474
-rw-r--r--sw/source/core/undo/unredln.cxx527
-rw-r--r--sw/source/core/undo/unsect.cxx601
-rw-r--r--sw/source/core/undo/unsort.cxx253
-rw-r--r--sw/source/core/undo/unspnd.cxx197
-rw-r--r--sw/source/core/undo/untbl.cxx3155
-rw-r--r--sw/source/core/undo/untblk.cxx451
26 files changed, 16510 insertions, 0 deletions
diff --git a/sw/source/core/undo/SwRewriter.cxx b/sw/source/core/undo/SwRewriter.cxx
new file mode 100644
index 000000000..05566a1bd
--- /dev/null
+++ b/sw/source/core/undo/SwRewriter.cxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+#include <SwRewriter.hxx>
+
+using namespace std;
+
+SwRewriter::SwRewriter()
+{
+}
+
+void SwRewriter::AddRule(SwUndoArg eWhat, const OUString & rWith)
+{
+ SwRewriteRule aRule(eWhat, rWith);
+
+ vector<SwRewriteRule>::iterator aIt = find_if(
+ mRules.begin(), mRules.end(),
+ [&aRule](SwRewriteRule const & a) { return a.first == aRule.first; });
+
+ if (aIt != mRules.end())
+ *aIt = aRule;
+ else
+ mRules.push_back(aRule);
+}
+
+OUString SwRewriter::Apply(const OUString & rStr) const
+{
+ OUString aResult = rStr;
+
+ for (const auto& rRule : mRules)
+ {
+ aResult = aResult.replaceAll(GetPlaceHolder(rRule.first), rRule.second);
+ }
+
+ return aResult;
+}
+
+OUString SwRewriter::GetPlaceHolder(SwUndoArg eId)
+{
+ switch (eId)
+ {
+ case UndoArg1:
+ return "$1";
+ case UndoArg2:
+ return "$2";
+ case UndoArg3:
+ return "$3";
+ default:
+ break;
+ }
+ return "$1";
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/SwUndoField.cxx b/sw/source/core/undo/SwUndoField.cxx
new file mode 100644
index 000000000..1eac1fa41
--- /dev/null
+++ b/sw/source/core/undo/SwUndoField.cxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <SwUndoField.hxx>
+#include <swundo.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentFieldsManager.hxx>
+#include <txtfld.hxx>
+#include <fldbas.hxx>
+#include <fmtfld.hxx>
+#include <docsh.hxx>
+#include <pam.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star::uno;
+
+SwUndoField::SwUndoField(const SwPosition & rPos )
+ : SwUndo(SwUndoId::FIELD, rPos.GetDoc())
+{
+ nNodeIndex = rPos.nNode.GetIndex();
+ nOffset = rPos.nContent.GetIndex();
+ pDoc = rPos.GetDoc();
+}
+
+SwUndoField::~SwUndoField()
+{
+}
+
+SwPosition SwUndoField::GetPosition()
+{
+ SwNode * pNode = pDoc->GetNodes()[nNodeIndex];
+ SwNodeIndex aNodeIndex(*pNode);
+ SwIndex aIndex(pNode->GetContentNode(), nOffset);
+ SwPosition aResult(aNodeIndex, aIndex);
+
+ return aResult;
+}
+
+SwUndoFieldFromDoc::SwUndoFieldFromDoc(const SwPosition & rPos,
+ const SwField & rOldField,
+ const SwField & rNewField,
+ SwMsgPoolItem * _pHint, bool _bUpdate)
+ : SwUndoField(rPos)
+ , pOldField(rOldField.CopyField())
+ , pNewField(rNewField.CopyField())
+ , pHint(_pHint)
+ , bUpdate(_bUpdate)
+{
+ OSL_ENSURE(pOldField, "No old field!");
+ OSL_ENSURE(pNewField, "No new field!");
+ OSL_ENSURE(pDoc, "No document!");
+}
+
+SwUndoFieldFromDoc::~SwUndoFieldFromDoc()
+{
+}
+
+void SwUndoFieldFromDoc::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwTextField * pTextField = sw::DocumentFieldsManager::GetTextFieldAtPos(GetPosition());
+ const SwField * pField = pTextField ? pTextField->GetFormatField().GetField() : nullptr;
+
+ if (pField)
+ {
+ pDoc->getIDocumentFieldsAccess().UpdateField(pTextField, *pOldField, pHint, bUpdate);
+ }
+}
+
+void SwUndoFieldFromDoc::DoImpl()
+{
+ SwTextField * pTextField = sw::DocumentFieldsManager::GetTextFieldAtPos(GetPosition());
+ const SwField * pField = pTextField ? pTextField->GetFormatField().GetField() : nullptr;
+
+ if (pField)
+ {
+ pDoc->getIDocumentFieldsAccess().UpdateField(pTextField, *pNewField, pHint, bUpdate);
+ SwFormatField* pDstFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField());
+
+ if (pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(), false) == pDstFormatField->GetField()->GetTyp())
+ pDoc->GetDocShell()->Broadcast( SwFormatFieldHint( pDstFormatField, SwFormatFieldHintWhich::INSERTED ) );
+ }
+}
+
+void SwUndoFieldFromDoc::RedoImpl(::sw::UndoRedoContext &)
+{
+ DoImpl();
+}
+
+void SwUndoFieldFromDoc::RepeatImpl(::sw::RepeatContext &)
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+ DoImpl();
+}
+
+SwUndoFieldFromAPI::SwUndoFieldFromAPI(const SwPosition & rPos,
+ const Any & rOldVal, const Any & rNewVal,
+ sal_uInt16 _nWhich)
+ : SwUndoField(rPos), aOldVal(rOldVal), aNewVal(rNewVal), nWhich(_nWhich)
+{
+}
+
+SwUndoFieldFromAPI::~SwUndoFieldFromAPI()
+{
+}
+
+void SwUndoFieldFromAPI::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwField * pField = sw::DocumentFieldsManager::GetFieldAtPos(GetPosition());
+
+ if (pField)
+ pField->PutValue(aOldVal, nWhich);
+}
+
+void SwUndoFieldFromAPI::DoImpl()
+{
+ SwField * pField = sw::DocumentFieldsManager::GetFieldAtPos(GetPosition());
+
+ if (pField)
+ pField->PutValue(aNewVal, nWhich);
+}
+
+void SwUndoFieldFromAPI::RedoImpl(::sw::UndoRedoContext &)
+{
+ DoImpl();
+}
+
+void SwUndoFieldFromAPI::RepeatImpl(::sw::RepeatContext &)
+{
+ DoImpl();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/SwUndoFmt.cxx b/sw/source/core/undo/SwUndoFmt.cxx
new file mode 100644
index 000000000..d0e3798aa
--- /dev/null
+++ b/sw/source/core/undo/SwUndoFmt.cxx
@@ -0,0 +1,466 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <poolfmt.hxx>
+#include <charfmt.hxx>
+#include <frmfmt.hxx>
+#include <SwUndoFmt.hxx>
+#include <SwRewriter.hxx>
+#include <swundo.hxx>
+#include <undobj.hxx>
+#include <fmtcol.hxx>
+#include <doc.hxx>
+#include <strings.hrc>
+
+SwUndoFormatCreate::SwUndoFormatCreate
+(SwUndoId nUndoId, SwFormat * _pNew, SwFormat const * _pDerivedFrom, SwDoc * _pDoc)
+ : SwUndo(nUndoId, _pDoc), m_pNew(_pNew),
+ m_pDoc(_pDoc), m_pNewSet(nullptr), m_nId(0), m_bAuto(false)
+{
+ if (_pDerivedFrom)
+ m_sDerivedFrom = _pDerivedFrom->GetName();
+}
+
+SwUndoFormatCreate::~SwUndoFormatCreate()
+{
+}
+
+void SwUndoFormatCreate::UndoImpl(::sw::UndoRedoContext &)
+{
+ if (m_pNew)
+ {
+ if (m_sNewName.isEmpty())
+ m_sNewName = m_pNew->GetName();
+
+ if (!m_sNewName.isEmpty())
+ m_pNew = Find(m_sNewName);
+
+ if (m_pNew)
+ {
+ m_pNewSet = new SfxItemSet(m_pNew->GetAttrSet());
+ m_nId = m_pNew->GetPoolFormatId() & COLL_GET_RANGE_BITS;
+ m_bAuto = m_pNew->IsAuto();
+
+ Delete();
+ }
+ }
+}
+
+void SwUndoFormatCreate::RedoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pDerivedFrom = Find(m_sDerivedFrom);
+ SwFormat * pFormat = Create(pDerivedFrom);
+
+ if (pFormat && m_pNewSet)
+ {
+ pFormat->SetAuto(m_bAuto);
+ m_pDoc->ChgFormat(*pFormat, *m_pNewSet);
+ pFormat->SetPoolFormatId((pFormat->GetPoolFormatId()
+ & ~COLL_GET_RANGE_BITS)
+ | m_nId);
+
+ m_pNew = pFormat;
+ }
+ else
+ m_pNew = nullptr;
+}
+
+SwRewriter SwUndoFormatCreate::GetRewriter() const
+{
+ if (m_sNewName.isEmpty() && m_pNew)
+ m_sNewName = m_pNew->GetName();
+
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_sNewName);
+
+ return aRewriter;
+}
+
+SwUndoFormatDelete::SwUndoFormatDelete
+(SwUndoId nUndoId, SwFormat const * _pOld, SwDoc * _pDoc)
+ : SwUndo(nUndoId, _pDoc),
+ m_pDoc(_pDoc), m_sOldName(_pOld->GetName()),
+ m_aOldSet(_pOld->GetAttrSet())
+{
+ m_sDerivedFrom = _pOld->DerivedFrom()->GetName();
+ m_nId = _pOld->GetPoolFormatId() & COLL_GET_RANGE_BITS;
+ m_bAuto = _pOld->IsAuto();
+}
+
+SwUndoFormatDelete::~SwUndoFormatDelete()
+{
+}
+
+void SwUndoFormatDelete::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pDerivedFrom = Find(m_sDerivedFrom);
+
+ SwFormat * pFormat = Create(pDerivedFrom);
+
+ if (pFormat)
+ {
+ m_pDoc->ChgFormat(*pFormat, m_aOldSet);
+ pFormat->SetAuto(m_bAuto);
+ pFormat->SetPoolFormatId((pFormat->GetPoolFormatId() &
+ ~COLL_GET_RANGE_BITS)
+ | m_nId);
+ }
+}
+
+void SwUndoFormatDelete::RedoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pOld = Find(m_sOldName);
+
+ if (pOld)
+ {
+ Delete(pOld);
+ }
+}
+
+SwRewriter SwUndoFormatDelete::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_sOldName);
+
+ return aRewriter;
+}
+
+SwUndoRenameFormat::SwUndoRenameFormat(SwUndoId nUndoId,
+ const OUString & _sOldName,
+ const OUString & _sNewName,
+ SwDoc * _pDoc)
+ : SwUndo(nUndoId, _pDoc), m_sOldName(_sOldName),
+ m_sNewName(_sNewName), m_pDoc(_pDoc)
+{
+}
+
+SwUndoRenameFormat::~SwUndoRenameFormat()
+{
+}
+
+void SwUndoRenameFormat::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pFormat = Find(m_sNewName);
+
+ if (pFormat)
+ {
+ m_pDoc->RenameFormat(*pFormat, m_sOldName, true);
+ }
+}
+
+void SwUndoRenameFormat::RedoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pFormat = Find(m_sOldName);
+
+ if (pFormat)
+ {
+ m_pDoc->RenameFormat(*pFormat, m_sNewName, true);
+ }
+}
+
+SwRewriter SwUndoRenameFormat::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_sOldName);
+ aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
+ aRewriter.AddRule(UndoArg3, m_sNewName);
+
+ return aRewriter;
+}
+
+SwUndoTextFormatCollCreate::SwUndoTextFormatCollCreate
+(SwTextFormatColl * _pNew, SwTextFormatColl const * _pDerivedFrom, SwDoc * _pDoc)
+ : SwUndoFormatCreate(SwUndoId::TXTFMTCOL_CREATE, _pNew, _pDerivedFrom, _pDoc)
+{
+}
+
+SwFormat * SwUndoTextFormatCollCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_pDoc->MakeTextFormatColl(m_sNewName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+void SwUndoTextFormatCollCreate::Delete()
+{
+ m_pDoc->DelTextFormatColl(static_cast<SwTextFormatColl *>(m_pNew), true);
+}
+
+SwFormat * SwUndoTextFormatCollCreate::Find(const OUString & rName) const
+{
+ return m_pDoc->FindTextFormatCollByName(rName);
+}
+
+SwUndoTextFormatCollDelete::SwUndoTextFormatCollDelete(SwTextFormatColl const * _pOld,
+ SwDoc * _pDoc)
+ : SwUndoFormatDelete(SwUndoId::TXTFMTCOL_DELETE, _pOld, _pDoc)
+{
+}
+
+SwFormat * SwUndoTextFormatCollDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_pDoc->MakeTextFormatColl(m_sOldName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+void SwUndoTextFormatCollDelete::Delete(SwFormat * pOld)
+{
+ m_pDoc->DelTextFormatColl(static_cast<SwTextFormatColl *>(pOld), true);
+}
+
+SwFormat * SwUndoTextFormatCollDelete::Find(const OUString & rName) const
+{
+ return m_pDoc->FindTextFormatCollByName(rName);
+}
+
+SwUndoCondTextFormatCollCreate::SwUndoCondTextFormatCollCreate(SwConditionTextFormatColl *_pNew,
+ SwTextFormatColl const *_pDerivedFrom, SwDoc *_pDoc)
+ : SwUndoTextFormatCollCreate(_pNew, _pDerivedFrom, _pDoc)
+{
+}
+
+SwFormat * SwUndoCondTextFormatCollCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_pDoc->MakeCondTextFormatColl(m_sNewName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+SwUndoCondTextFormatCollDelete::SwUndoCondTextFormatCollDelete(SwTextFormatColl const * _pOld,
+ SwDoc * _pDoc)
+ : SwUndoTextFormatCollDelete(_pOld, _pDoc)
+{
+}
+
+SwFormat * SwUndoCondTextFormatCollDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_pDoc->MakeCondTextFormatColl(m_sOldName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+SwUndoRenameFormatColl::SwUndoRenameFormatColl(const OUString & sInitOldName,
+ const OUString & sInitNewName,
+ SwDoc * _pDoc)
+ : SwUndoRenameFormat(SwUndoId::TXTFMTCOL_RENAME, sInitOldName, sInitNewName, _pDoc)
+{
+}
+
+SwFormat * SwUndoRenameFormatColl::Find(const OUString & rName) const
+{
+ return m_pDoc->FindTextFormatCollByName(rName);
+}
+
+SwUndoCharFormatCreate::SwUndoCharFormatCreate(SwCharFormat * pNewFormat,
+ SwCharFormat const * pDerivedFrom,
+ SwDoc * pDocument)
+ : SwUndoFormatCreate(SwUndoId::CHARFMT_CREATE, pNewFormat, pDerivedFrom, pDocument)
+{
+}
+
+SwFormat * SwUndoCharFormatCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_pDoc->MakeCharFormat(m_sNewName, static_cast<SwCharFormat *>(pDerivedFrom), true);
+}
+
+void SwUndoCharFormatCreate::Delete()
+{
+ m_pDoc->DelCharFormat(static_cast<SwCharFormat *>(m_pNew), true);
+}
+
+SwFormat * SwUndoCharFormatCreate::Find(const OUString & rName) const
+{
+ return m_pDoc->FindCharFormatByName(rName);
+}
+
+SwUndoCharFormatDelete::SwUndoCharFormatDelete(SwCharFormat const * pOld, SwDoc * pDocument)
+ : SwUndoFormatDelete(SwUndoId::CHARFMT_DELETE, pOld, pDocument)
+{
+}
+
+SwFormat * SwUndoCharFormatDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_pDoc->MakeCharFormat(m_sOldName, static_cast<SwCharFormat *>(pDerivedFrom), true);
+}
+
+void SwUndoCharFormatDelete::Delete(SwFormat * pFormat)
+{
+ m_pDoc->DelCharFormat(static_cast<SwCharFormat *>(pFormat), true);
+}
+
+SwFormat * SwUndoCharFormatDelete::Find(const OUString & rName) const
+{
+ return m_pDoc->FindCharFormatByName(rName);
+}
+
+SwUndoRenameCharFormat::SwUndoRenameCharFormat(const OUString & sInitOldName,
+ const OUString & sInitNewName,
+ SwDoc * pDocument)
+ : SwUndoRenameFormat(SwUndoId::CHARFMT_RENAME, sInitOldName, sInitNewName, pDocument)
+{
+}
+
+SwFormat * SwUndoRenameCharFormat::Find(const OUString & rName) const
+{
+ return m_pDoc->FindCharFormatByName(rName);
+}
+
+SwUndoFrameFormatCreate::SwUndoFrameFormatCreate(SwFrameFormat * pNewFormat,
+ SwFrameFormat const * pDerivedFrom,
+ SwDoc * pDocument)
+ : SwUndoFormatCreate(SwUndoId::FRMFMT_CREATE, pNewFormat, pDerivedFrom, pDocument)
+{
+}
+
+SwFormat * SwUndoFrameFormatCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_pDoc->MakeFrameFormat(m_sNewName, static_cast<SwFrameFormat *>(pDerivedFrom), true, m_pNew->IsAuto());
+}
+
+void SwUndoFrameFormatCreate::Delete()
+{
+ m_pDoc->DelFrameFormat(static_cast<SwFrameFormat *>(m_pNew), true);
+}
+
+SwFormat * SwUndoFrameFormatCreate::Find(const OUString & rName) const
+{
+ return m_pDoc->FindFrameFormatByName(rName);
+}
+
+SwUndoFrameFormatDelete::SwUndoFrameFormatDelete(SwFrameFormat const * pOld, SwDoc * pDocument)
+ : SwUndoFormatDelete(SwUndoId::FRMFMT_DELETE, pOld, pDocument)
+{
+}
+
+SwFormat * SwUndoFrameFormatDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_pDoc->MakeFrameFormat(m_sOldName, static_cast<SwFrameFormat *>(pDerivedFrom), true);
+}
+
+void SwUndoFrameFormatDelete::Delete(SwFormat * pFormat)
+{
+ m_pDoc->DelFrameFormat(static_cast<SwFrameFormat *>(pFormat), true);
+}
+
+SwFormat * SwUndoFrameFormatDelete::Find(const OUString & rName) const
+{
+ return m_pDoc->FindFrameFormatByName(rName);
+}
+
+SwUndoRenameFrameFormat::SwUndoRenameFrameFormat(const OUString & sInitOldName,
+ const OUString & sInitNewName,
+ SwDoc * pDocument)
+ : SwUndoRenameFormat(SwUndoId::FRMFMT_RENAME, sInitOldName, sInitNewName, pDocument)
+{
+}
+
+SwFormat * SwUndoRenameFrameFormat::Find(const OUString & rName) const
+{
+ return m_pDoc->FindFrameFormatByName(rName);
+}
+
+SwUndoNumruleCreate::SwUndoNumruleCreate(const SwNumRule * _pNew,
+ SwDoc * _pDoc)
+ : SwUndo(SwUndoId::NUMRULE_CREATE, _pDoc), m_pNew(_pNew), m_aNew(*_pNew), m_pDoc(_pDoc),
+ m_bInitialized(false)
+{
+}
+
+void SwUndoNumruleCreate::UndoImpl(::sw::UndoRedoContext &)
+{
+ if (! m_bInitialized)
+ {
+ m_aNew = *m_pNew;
+ m_bInitialized = true;
+ }
+
+ m_pDoc->DelNumRule(m_aNew.GetName(), true);
+}
+
+void SwUndoNumruleCreate::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_pDoc->MakeNumRule(m_aNew.GetName(), &m_aNew, true);
+}
+
+SwRewriter SwUndoNumruleCreate::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ if (! m_bInitialized)
+ {
+ m_aNew = *m_pNew;
+ m_bInitialized = true;
+ }
+
+ aResult.AddRule(UndoArg1, m_aNew.GetName());
+
+ return aResult;
+}
+
+SwUndoNumruleDelete::SwUndoNumruleDelete(const SwNumRule & rRule,
+ SwDoc * _pDoc)
+ : SwUndo(SwUndoId::NUMRULE_DELETE, _pDoc), m_aOld(rRule), m_pDoc(_pDoc)
+{
+}
+
+void SwUndoNumruleDelete::UndoImpl(::sw::UndoRedoContext &)
+{
+ m_pDoc->MakeNumRule(m_aOld.GetName(), &m_aOld, true);
+}
+
+void SwUndoNumruleDelete::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_pDoc->DelNumRule(m_aOld.GetName(), true);
+}
+
+SwRewriter SwUndoNumruleDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, m_aOld.GetName());
+
+ return aResult;
+}
+
+SwUndoNumruleRename::SwUndoNumruleRename(const OUString & _aOldName,
+ const OUString & _aNewName,
+ SwDoc * _pDoc)
+ : SwUndo(SwUndoId::NUMRULE_RENAME, _pDoc), m_aOldName(_aOldName), m_aNewName(_aNewName),
+ m_pDoc(_pDoc)
+{
+}
+
+void SwUndoNumruleRename::UndoImpl(::sw::UndoRedoContext &)
+{
+ m_pDoc->RenameNumRule(m_aNewName, m_aOldName, true);
+}
+
+void SwUndoNumruleRename::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_pDoc->RenameNumRule(m_aOldName, m_aNewName, true);
+}
+
+SwRewriter SwUndoNumruleRename::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_aOldName);
+ aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
+ aRewriter.AddRule(UndoArg3, m_aNewName);
+
+ return aRewriter;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/SwUndoPageDesc.cxx b/sw/source/core/undo/SwUndoPageDesc.cxx
new file mode 100644
index 000000000..1ca66f9fb
--- /dev/null
+++ b/sw/source/core/undo/SwUndoPageDesc.cxx
@@ -0,0 +1,341 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <swundo.hxx>
+#include <pagedesc.hxx>
+#include <SwUndoPageDesc.hxx>
+#include <SwRewriter.hxx>
+#include <undobj.hxx>
+#include <strings.hrc>
+#include <fmtcntnt.hxx>
+#include <fmthdft.hxx>
+#include <osl/diagnose.h>
+
+SwUndoPageDesc::SwUndoPageDesc(const SwPageDesc & _aOld,
+ const SwPageDesc & _aNew,
+ SwDoc * _pDoc)
+ : SwUndo( _aOld.GetName() != _aNew.GetName() ?
+ SwUndoId::RENAME_PAGEDESC :
+ SwUndoId::CHANGE_PAGEDESC,
+ _pDoc ),
+ m_aOld(_aOld, _pDoc), m_aNew(_aNew, _pDoc), m_pDoc(_pDoc), m_bExchange( false )
+{
+ OSL_ENSURE(nullptr != m_pDoc, "no document?");
+
+ /*
+ The page description changes.
+ If there are no header/footer content changes like header on/off or change from shared content
+ to unshared etc., there is no reason to duplicate the content nodes (Crash i55547)
+ But this happens, this Undo Ctor will destroy the unnecessary duplicate and manipulate the
+ content pointer of the both page descriptions.
+ */
+ SwPageDesc &rOldDesc = m_aOld.m_PageDesc;
+ SwPageDesc &rNewDesc = m_aNew.m_PageDesc;
+ const SwFormatHeader& rOldHead = rOldDesc.GetMaster().GetHeader();
+ const SwFormatHeader& rNewHead = rNewDesc.GetMaster().GetHeader();
+ const SwFormatFooter& rOldFoot = rOldDesc.GetMaster().GetFooter();
+ const SwFormatFooter& rNewFoot = rNewDesc.GetMaster().GetFooter();
+ /* bExchange must not be set, if the old page descriptor will stay active.
+ Two known situations:
+ #i67735#: renaming a page descriptor
+ #i67334#: changing the follow style
+ If header/footer will be activated or deactivated, this undo will not work.
+ */
+ m_bExchange = ( m_aOld.GetName() == m_aNew.GetName() ) &&
+ ( _aOld.GetFollow() == _aNew.GetFollow() ) &&
+ ( rOldHead.IsActive() == rNewHead.IsActive() ) &&
+ ( rOldFoot.IsActive() == rNewFoot.IsActive() );
+ if( rOldHead.IsActive() && ( rOldDesc.IsHeaderShared() != rNewDesc.IsHeaderShared() ) )
+ m_bExchange = false;
+ if( rOldFoot.IsActive() && ( rOldDesc.IsFooterShared() != rNewDesc.IsFooterShared() ) )
+ m_bExchange = false;
+ if( ( rOldHead.IsActive() || rOldFoot.IsActive() ) && ( rOldDesc.IsFirstShared() != rNewDesc.IsFirstShared() ) )
+ m_bExchange = false;
+ if( m_bExchange )
+ {
+ if( rNewHead.IsActive() )
+ {
+ SwFrameFormat* pFormat = new SwFrameFormat( *rNewHead.GetHeaderFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatHeader aFormatHeader(pFormat);
+ (void)aFormatHeader;
+ if (!rNewDesc.IsHeaderShared())
+ {
+ pFormat = new SwFrameFormat( *rNewDesc.GetLeft().GetHeader().GetHeaderFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatHeader aLeftHeader(pFormat);
+ (void)aLeftHeader;
+ }
+ if (!rNewDesc.IsFirstShared())
+ {
+ pFormat = new SwFrameFormat( *rNewDesc.GetFirstMaster().GetHeader().GetHeaderFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatHeader aFirstHeader(pFormat);
+ (void)aFirstHeader;
+ }
+ }
+ // Same procedure for footers...
+ if( rNewFoot.IsActive() )
+ {
+ SwFrameFormat* pFormat = new SwFrameFormat( *rNewFoot.GetFooterFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatFooter aFormatFooter(pFormat);
+ (void)aFormatFooter;
+ if (!rNewDesc.IsFooterShared())
+ {
+ pFormat = new SwFrameFormat( *rNewDesc.GetLeft().GetFooter().GetFooterFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatFooter aLeftFooter(pFormat);
+ (void)aLeftFooter;
+ }
+ if (!rNewDesc.IsFirstShared())
+ {
+ pFormat = new SwFrameFormat( *rNewDesc.GetFirstMaster().GetFooter().GetFooterFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatFooter aFirstFooter(pFormat);
+ (void)aFirstFooter;
+ }
+ }
+
+ // After this exchange method the old page description will point to zero,
+ // the new one will point to the node position of the original content nodes.
+ ExchangeContentNodes( m_aOld.m_PageDesc, m_aNew.m_PageDesc );
+ }
+}
+
+SwUndoPageDesc::~SwUndoPageDesc()
+{
+}
+
+void SwUndoPageDesc::ExchangeContentNodes( SwPageDesc& rSource, SwPageDesc &rDest )
+{
+ OSL_ENSURE( m_bExchange, "You shouldn't do that." );
+ const SwFormatHeader& rDestHead = rDest.GetMaster().GetHeader();
+ const SwFormatHeader& rSourceHead = rSource.GetMaster().GetHeader();
+ if( rDestHead.IsActive() )
+ {
+ // Let the destination page description point to the source node position,
+ // from now on this descriptor is responsible for the content nodes!
+ const SfxPoolItem* pItem;
+ rDest.GetMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem );
+ std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone());
+ SwFrameFormat* pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( rSourceHead.GetHeaderFormat()->GetContent() );
+
+ // Let the source page description point to zero node position,
+ // it loses the responsible and can be destroyed without removing the content nodes.
+ rSource.GetMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+
+ if( !rDest.IsHeaderShared() )
+ {
+ // Same procedure for unshared header...
+ const SwFormatHeader& rSourceLeftHead = rSource.GetLeft().GetHeader();
+ rDest.GetLeft().GetAttrSet().GetItemState( RES_HEADER, false, &pItem );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( rSourceLeftHead.GetHeaderFormat()->GetContent() );
+ rSource.GetLeft().GetAttrSet().GetItemState( RES_HEADER, false, &pItem );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+ }
+ if (!rDest.IsFirstShared())
+ {
+ // Same procedure for unshared header...
+ const SwFormatHeader& rSourceFirstMasterHead = rSource.GetFirstMaster().GetHeader();
+ rDest.GetFirstMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( rSourceFirstMasterHead.GetHeaderFormat()->GetContent() );
+ rSource.GetFirstMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+ }
+ }
+ // Same procedure for footers...
+ const SwFormatFooter& rDestFoot = rDest.GetMaster().GetFooter();
+ const SwFormatFooter& rSourceFoot = rSource.GetMaster().GetFooter();
+ if( !rDestFoot.IsActive() )
+ return;
+
+ const SfxPoolItem* pItem;
+ rDest.GetMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem );
+ std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone());
+ SwFrameFormat *pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat();
+ pNewFormat->SetFormatAttr( rSourceFoot.GetFooterFormat()->GetContent() );
+
+ rSource.GetMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+
+ if( !rDest.IsFooterShared() )
+ {
+ const SwFormatFooter& rSourceLeftFoot = rSource.GetLeft().GetFooter();
+ rDest.GetLeft().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat();
+ pNewFormat->SetFormatAttr( rSourceLeftFoot.GetFooterFormat()->GetContent() );
+ rSource.GetLeft().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+ }
+ if (rDest.IsFirstShared())
+ return;
+
+ const SwFormatFooter& rSourceFirstMasterFoot = rSource.GetFirstMaster().GetFooter();
+ rDest.GetFirstMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat();
+ pNewFormat->SetFormatAttr( rSourceFirstMasterFoot.GetFooterFormat()->GetContent() );
+ rSource.GetFirstMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+}
+
+void SwUndoPageDesc::UndoImpl(::sw::UndoRedoContext &)
+{
+ // Move (header/footer)content node responsibility from new page descriptor to old one again.
+ if( m_bExchange )
+ ExchangeContentNodes( m_aNew.m_PageDesc, m_aOld.m_PageDesc );
+ m_pDoc->ChgPageDesc(m_aOld.GetName(), m_aOld);
+}
+
+void SwUndoPageDesc::RedoImpl(::sw::UndoRedoContext &)
+{
+ // Move (header/footer)content node responsibility from old page descriptor to new one again.
+ if( m_bExchange )
+ ExchangeContentNodes( m_aOld.m_PageDesc, m_aNew.m_PageDesc );
+ m_pDoc->ChgPageDesc(m_aNew.GetName(), m_aNew);
+}
+
+SwRewriter SwUndoPageDesc::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, m_aOld.GetName());
+ aResult.AddRule(UndoArg2, SwResId(STR_YIELDS));
+ aResult.AddRule(UndoArg3, m_aNew.GetName());
+
+ return aResult;
+}
+
+SwUndoPageDescCreate::SwUndoPageDescCreate(const SwPageDesc * pNew,
+ SwDoc * _pDoc)
+ : SwUndo(SwUndoId::CREATE_PAGEDESC, _pDoc), m_pDesc(pNew), m_aNew(*pNew, _pDoc),
+ m_pDoc(_pDoc)
+{
+ OSL_ENSURE(nullptr != m_pDoc, "no document?");
+}
+
+SwUndoPageDescCreate::~SwUndoPageDescCreate()
+{
+}
+
+void SwUndoPageDescCreate::UndoImpl(::sw::UndoRedoContext &)
+{
+ if (m_pDesc)
+ {
+ m_aNew = *m_pDesc;
+ m_pDesc = nullptr;
+ }
+
+ m_pDoc->DelPageDesc(m_aNew.GetName(), true);
+}
+
+void SwUndoPageDescCreate::DoImpl()
+{
+ SwPageDesc aPageDesc = m_aNew;
+ m_pDoc->MakePageDesc(m_aNew.GetName(), &aPageDesc, false, true);
+}
+
+void SwUndoPageDescCreate::RedoImpl(::sw::UndoRedoContext &)
+{
+ DoImpl();
+}
+
+void SwUndoPageDescCreate::RepeatImpl(::sw::RepeatContext &)
+{
+ ::sw::UndoGuard const undoGuard(m_pDoc->GetIDocumentUndoRedo());
+ DoImpl();
+}
+
+SwRewriter SwUndoPageDescCreate::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ if (m_pDesc)
+ aResult.AddRule(UndoArg1, m_pDesc->GetName());
+ else
+ aResult.AddRule(UndoArg1, m_aNew.GetName());
+
+ return aResult;
+}
+
+SwUndoPageDescDelete::SwUndoPageDescDelete(const SwPageDesc & _aOld,
+ SwDoc * _pDoc)
+ : SwUndo(SwUndoId::DELETE_PAGEDESC, _pDoc), m_aOld(_aOld, _pDoc), m_pDoc(_pDoc)
+{
+ OSL_ENSURE(nullptr != m_pDoc, "no document?");
+}
+
+SwUndoPageDescDelete::~SwUndoPageDescDelete()
+{
+}
+
+void SwUndoPageDescDelete::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwPageDesc aPageDesc = m_aOld;
+ m_pDoc->MakePageDesc(m_aOld.GetName(), &aPageDesc, false, true);
+}
+
+void SwUndoPageDescDelete::DoImpl()
+{
+ m_pDoc->DelPageDesc(m_aOld.GetName(), true);
+}
+
+void SwUndoPageDescDelete::RedoImpl(::sw::UndoRedoContext &)
+{
+ DoImpl();
+}
+
+void SwUndoPageDescDelete::RepeatImpl(::sw::RepeatContext &)
+{
+ ::sw::UndoGuard const undoGuard(m_pDoc->GetIDocumentUndoRedo());
+ DoImpl();
+}
+
+SwRewriter SwUndoPageDescDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, m_aOld.GetName());
+
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/SwUndoTOXChange.cxx b/sw/source/core/undo/SwUndoTOXChange.cxx
new file mode 100644
index 000000000..c88182b7a
--- /dev/null
+++ b/sw/source/core/undo/SwUndoTOXChange.cxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <SwUndoTOXChange.hxx>
+#include <swundo.hxx>
+#include <UndoCore.hxx>
+#include <doctxm.hxx>
+#include <doc.hxx>
+#include <node.hxx>
+
+namespace
+{
+ sal_uLong GetSectionNodeIndex(SwTOXBaseSection const& rTOX)
+ {
+ const SwSectionNode* pSectNd = rTOX.GetFormat()->GetSectionNode();
+ assert(pSectNd);
+ return pSectNd->GetIndex();
+ }
+}
+
+SwUndoTOXChange::SwUndoTOXChange(const SwDoc *pDoc,
+ SwTOXBaseSection const& rTOX, SwTOXBase const& rNew)
+ : SwUndo(SwUndoId::TOXCHANGE, pDoc)
+ , m_Old(rTOX)
+ , m_New(rNew)
+ , m_nNodeIndex(GetSectionNodeIndex(rTOX))
+{
+}
+
+SwUndoTOXChange::~SwUndoTOXChange()
+{
+}
+
+// get the current ToXBase, which is not necessarily the same instance that existed there before
+static SwTOXBase & GetTOX(SwDoc & rDoc, sal_uLong const nNodeIndex)
+{
+ SwSectionNode *const pNode(rDoc.GetNodes()[nNodeIndex]->GetSectionNode());
+ assert(pNode);
+ assert(dynamic_cast<SwTOXBaseSection*>(&pNode->GetSection()));
+ auto & rTOX(static_cast<SwTOXBaseSection&>(pNode->GetSection()));
+ return rTOX;
+}
+
+void SwUndoTOXChange::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ SwTOXBase & rTOX(GetTOX(rDoc, m_nNodeIndex));
+ rTOX = m_Old;
+}
+
+void SwUndoTOXChange::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ SwTOXBase & rTOX(GetTOX(rDoc, m_nNodeIndex));
+ rTOX = m_New;
+}
+
+void SwUndoTOXChange::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ SwTOXBase *const pTOX(SwDoc::GetCurTOX(*rContext.GetRepeatPaM().GetPoint()));
+ if (pTOX)
+ {
+ rDoc.ChangeTOX(*pTOX, m_New);
+ // intentionally limited to not Update because we'd need layout
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/docundo.cxx b/sw/source/core/undo/docundo.cxx
new file mode 100644
index 000000000..61629e087
--- /dev/null
+++ b/sw/source/core/undo/docundo.cxx
@@ -0,0 +1,735 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoManager.hxx>
+
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <view.hxx>
+#include <drawdoc.hxx>
+#include <ndarr.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <UndoCore.hxx>
+#include <editsh.hxx>
+#include <unobaseclass.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <comphelper/lok.hxx>
+#include <assert.h>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+
+using namespace ::com::sun::star;
+
+// the undo array should never grow beyond this limit:
+#define UNDO_ACTION_LIMIT (USHRT_MAX - 1000)
+
+namespace sw {
+
+UndoManager::UndoManager(std::shared_ptr<SwNodes> const & xUndoNodes,
+ IDocumentDrawModelAccess & rDrawModelAccess,
+ IDocumentRedlineAccess & rRedlineAccess,
+ IDocumentState & rState)
+ : m_rDrawModelAccess(rDrawModelAccess)
+ , m_rRedlineAccess(rRedlineAccess)
+ , m_rState(rState)
+ , m_xUndoNodes(xUndoNodes)
+ , m_bGroupUndo(true)
+ , m_bDrawUndo(true)
+ , m_bRepair(false)
+ , m_bLockUndoNoModifiedPosition(false)
+ , m_isAddWithIgnoreRepeat(false)
+ , m_UndoSaveMark(MARK_INVALID)
+ , m_pDocShell(nullptr)
+ , m_pView(nullptr)
+{
+ assert(bool(m_xUndoNodes));
+ // writer expects it to be disabled initially
+ // Undo is enabled by SwEditShell constructor
+ SdrUndoManager::EnableUndo(false);
+}
+
+SwNodes const& UndoManager::GetUndoNodes() const
+{
+ return *m_xUndoNodes;
+}
+
+SwNodes & UndoManager::GetUndoNodes()
+{
+ return *m_xUndoNodes;
+}
+
+bool UndoManager::IsUndoNodes(SwNodes const& rNodes) const
+{
+ return & rNodes == m_xUndoNodes.get();
+}
+
+void UndoManager::SetDocShell(SwDocShell* pDocShell)
+{
+ m_pDocShell = pDocShell;
+}
+
+void UndoManager::SetView(SwView* pView)
+{
+ m_pView = pView;
+}
+
+size_t UndoManager::GetUndoActionCount(const bool bCurrentLevel) const
+{
+ size_t nRet = SdrUndoManager::GetUndoActionCount(bCurrentLevel);
+ if (!comphelper::LibreOfficeKit::isActive() || !m_pView)
+ return nRet;
+
+ if (!nRet || !SdrUndoManager::GetUndoActionCount())
+ return nRet;
+
+ const SfxUndoAction* pAction = SdrUndoManager::GetUndoAction();
+ if (!pAction)
+ return nRet;
+
+ if (!m_bRepair)
+ {
+ // If another view created the last undo action, prevent undoing it from this view.
+ ViewShellId nViewShellId = m_pView->GetViewShellId();
+ if (pAction->GetViewShellId() != nViewShellId)
+ nRet = 0;
+ }
+
+ return nRet;
+}
+
+size_t UndoManager::GetRedoActionCount(const bool bCurrentLevel) const
+{
+ size_t nRet = SdrUndoManager::GetRedoActionCount(bCurrentLevel);
+ if (!comphelper::LibreOfficeKit::isActive() || !m_pView)
+ return nRet;
+
+ if (!nRet || !SdrUndoManager::GetRedoActionCount())
+ return nRet;
+
+ const SfxUndoAction* pAction = SdrUndoManager::GetRedoAction();
+ if (!pAction)
+ return nRet;
+
+ if (!m_bRepair)
+ {
+ // If another view created the first redo action, prevent redoing it from this view.
+ ViewShellId nViewShellId = m_pView->GetViewShellId();
+ if (pAction->GetViewShellId() != nViewShellId)
+ nRet = 0;
+ }
+
+ return nRet;
+}
+
+void UndoManager::DoUndo(bool const bDoUndo)
+{
+ if(!isTextEditActive())
+ {
+ EnableUndo(bDoUndo);
+
+ SwDrawModel*const pSdrModel = m_rDrawModelAccess.GetDrawModel();
+ if( pSdrModel )
+ {
+ pSdrModel->EnableUndo(bDoUndo);
+ }
+ }
+}
+
+bool UndoManager::DoesUndo() const
+{
+ if(isTextEditActive())
+ {
+ return false;
+ }
+ else
+ {
+ return IsUndoEnabled();
+ }
+}
+
+void UndoManager::DoGroupUndo(bool const bDoUndo)
+{
+ m_bGroupUndo = bDoUndo;
+}
+
+bool UndoManager::DoesGroupUndo() const
+{
+ return m_bGroupUndo;
+}
+
+void UndoManager::DoDrawUndo(bool const bDoUndo)
+{
+ m_bDrawUndo = bDoUndo;
+}
+
+bool UndoManager::DoesDrawUndo() const
+{
+ return m_bDrawUndo;
+}
+
+void UndoManager::DoRepair(bool bRepair)
+{
+ m_bRepair = bRepair;
+}
+
+bool UndoManager::DoesRepair() const
+{
+ return m_bRepair;
+}
+
+bool UndoManager::IsUndoNoResetModified() const
+{
+ return MARK_INVALID == m_UndoSaveMark;
+}
+
+void UndoManager::SetUndoNoResetModified()
+{
+ if (MARK_INVALID != m_UndoSaveMark)
+ {
+ RemoveMark(m_UndoSaveMark);
+ m_UndoSaveMark = MARK_INVALID;
+ }
+}
+
+void UndoManager::SetUndoNoModifiedPosition()
+{
+ if (!m_bLockUndoNoModifiedPosition)
+ {
+ m_UndoSaveMark = MarkTopUndoAction();
+ }
+}
+
+void UndoManager::LockUndoNoModifiedPosition()
+{
+ m_bLockUndoNoModifiedPosition = true;
+}
+
+void UndoManager::UnLockUndoNoModifiedPosition()
+{
+ m_bLockUndoNoModifiedPosition = false;
+}
+
+SwUndo* UndoManager::GetLastUndo()
+{
+ if (!SdrUndoManager::GetUndoActionCount())
+ {
+ return nullptr;
+ }
+ SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction() );
+ return dynamic_cast<SwUndo*>(pAction);
+}
+
+void UndoManager::AppendUndo(std::unique_ptr<SwUndo> pUndo)
+{
+ AddUndoAction(std::move(pUndo));
+}
+
+void UndoManager::ClearRedo()
+{
+ return SdrUndoManager::ImplClearRedo_NoLock(TopLevel);
+}
+
+void UndoManager::DelAllUndoObj()
+{
+ ::sw::UndoGuard const undoGuard(*this);
+
+ SdrUndoManager::ClearAllLevels();
+
+ m_UndoSaveMark = MARK_INVALID;
+}
+
+SwUndoId
+UndoManager::StartUndo(SwUndoId const i_eUndoId,
+ SwRewriter const*const pRewriter)
+{
+ if (!IsUndoEnabled())
+ {
+ return SwUndoId::EMPTY;
+ }
+
+ SwUndoId const eUndoId( (i_eUndoId == SwUndoId::EMPTY) ? SwUndoId::START : i_eUndoId );
+
+ assert(SwUndoId::END != eUndoId);
+ OUString comment( (SwUndoId::START == eUndoId)
+ ? OUString("??")
+ : GetUndoComment(eUndoId) );
+ if (pRewriter)
+ {
+ assert(SwUndoId::START != eUndoId);
+ comment = pRewriter->Apply(comment);
+ }
+
+ ViewShellId nViewShellId(-1);
+ if (m_pDocShell)
+ {
+ if (const SwView* pView = m_pDocShell->GetView())
+ nViewShellId = pView->GetViewShellId();
+ }
+ SdrUndoManager::EnterListAction(comment, comment, static_cast<sal_uInt16>(eUndoId), nViewShellId);
+
+ return eUndoId;
+}
+
+SwUndoId
+UndoManager::EndUndo(SwUndoId eUndoId, SwRewriter const*const pRewriter)
+{
+ if (!IsUndoEnabled())
+ {
+ return SwUndoId::EMPTY;
+ }
+
+ if ((eUndoId == SwUndoId::EMPTY) || (SwUndoId::START == eUndoId))
+ eUndoId = SwUndoId::END;
+ OSL_ENSURE(!((SwUndoId::END == eUndoId) && pRewriter),
+ "EndUndo(): no Undo ID, but rewriter given?");
+
+ SfxUndoAction *const pLastUndo(
+ (0 == SdrUndoManager::GetUndoActionCount())
+ ? nullptr : SdrUndoManager::GetUndoAction() );
+
+ int const nCount = LeaveListAction();
+
+ if (nCount) // otherwise: empty list action not inserted!
+ {
+ assert(pLastUndo);
+ assert(SwUndoId::START != eUndoId);
+ auto pListAction = dynamic_cast<SfxListUndoAction*>(SdrUndoManager::GetUndoAction());
+ assert(pListAction);
+ if (SwUndoId::END != eUndoId)
+ {
+ OSL_ENSURE(static_cast<SwUndoId>(pListAction->GetId()) == eUndoId,
+ "EndUndo(): given ID different from StartUndo()");
+ // comment set by caller of EndUndo
+ OUString comment = GetUndoComment(eUndoId);
+ if (pRewriter)
+ {
+ comment = pRewriter->Apply(comment);
+ }
+ pListAction->SetComment(comment);
+ }
+ else if (SwUndoId::START != static_cast<SwUndoId>(pListAction->GetId()))
+ {
+ // comment set by caller of StartUndo: nothing to do here
+ }
+ else if (pLastUndo)
+ {
+ // comment was not set at StartUndo or EndUndo:
+ // take comment of last contained action
+ // (note that this works recursively, i.e. the last contained
+ // action may be a list action created by StartUndo/EndUndo)
+ OUString const comment(pLastUndo->GetComment());
+ pListAction->SetComment(comment);
+ }
+ else
+ {
+ OSL_ENSURE(false, "EndUndo(): no comment?");
+ }
+ }
+
+ return eUndoId;
+}
+
+bool
+UndoManager::GetLastUndoInfo(
+ OUString *const o_pStr, SwUndoId *const o_pId, const SwView* pView) const
+{
+ // this is actually expected to work on the current level,
+ // but that was really not obvious from the previous implementation...
+ if (!SdrUndoManager::GetUndoActionCount())
+ {
+ return false;
+ }
+
+ SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction() );
+
+ if (comphelper::LibreOfficeKit::isActive() && !m_bRepair)
+ {
+ // If another view created the undo action, prevent undoing it from this view.
+ ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId();
+ if (pAction->GetViewShellId() != nViewShellId)
+ {
+ if (o_pId)
+ {
+ *o_pId = SwUndoId::CONFLICT;
+ }
+ return false;
+ }
+ }
+
+ if (o_pStr)
+ {
+ *o_pStr = pAction->GetComment();
+ }
+ if (o_pId)
+ {
+ if (auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction))
+ *o_pId = static_cast<SwUndoId>(pListAction->GetId());
+ else if (auto pSwAction = dynamic_cast<const SwUndo*>(pAction))
+ *o_pId = pSwAction->GetId();
+ else
+ *o_pId = SwUndoId::EMPTY;
+ }
+
+ return true;
+}
+
+SwUndoComments_t UndoManager::GetUndoComments() const
+{
+ OSL_ENSURE(!SdrUndoManager::IsInListAction(),
+ "GetUndoComments() called while in list action?");
+
+ SwUndoComments_t ret;
+ const size_t nUndoCount(SdrUndoManager::GetUndoActionCount(TopLevel));
+ for (size_t n = 0; n < nUndoCount; ++n)
+ {
+ OUString const comment(
+ SdrUndoManager::GetUndoActionComment(n, TopLevel));
+ ret.push_back(comment);
+ }
+
+ return ret;
+}
+
+bool UndoManager::GetFirstRedoInfo(OUString *const o_pStr,
+ SwUndoId *const o_pId,
+ const SwView* pView) const
+{
+ if (!SdrUndoManager::GetRedoActionCount())
+ {
+ return false;
+ }
+
+ SfxUndoAction *const pAction( SdrUndoManager::GetRedoAction() );
+ if ( pAction == nullptr )
+ {
+ return false;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() && !m_bRepair)
+ {
+ // If another view created the undo action, prevent redoing it from this view.
+ ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId();
+ if (pAction->GetViewShellId() != nViewShellId)
+ {
+ if (o_pId)
+ {
+ *o_pId = SwUndoId::CONFLICT;
+ }
+ return false;
+ }
+ }
+
+ if (o_pStr)
+ {
+ *o_pStr = pAction->GetComment();
+ }
+ if (o_pId)
+ {
+ if (auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction))
+ *o_pId = static_cast<SwUndoId>(pListAction->GetId());
+ else if (auto pSwAction = dynamic_cast<const SwUndo*>(pAction))
+ *o_pId = pSwAction->GetId();
+ else
+ *o_pId = SwUndoId::EMPTY;
+ }
+
+ return true;
+}
+
+SwUndoComments_t UndoManager::GetRedoComments() const
+{
+ OSL_ENSURE(!SdrUndoManager::IsInListAction(),
+ "GetRedoComments() called while in list action?");
+
+ SwUndoComments_t ret;
+ const size_t nRedoCount(SdrUndoManager::GetRedoActionCount(TopLevel));
+ for (size_t n = 0; n < nRedoCount; ++n)
+ {
+ OUString const comment(
+ SdrUndoManager::GetRedoActionComment(n, TopLevel));
+ ret.push_back(comment);
+ }
+
+ return ret;
+}
+
+SwUndoId UndoManager::GetRepeatInfo(OUString *const o_pStr) const
+{
+ SwUndoId nRepeatId(SwUndoId::EMPTY);
+ GetLastUndoInfo(o_pStr, & nRepeatId);
+ if( SwUndoId::REPEAT_START <= nRepeatId && SwUndoId::REPEAT_END > nRepeatId )
+ {
+ return nRepeatId;
+ }
+ if (o_pStr) // not repeatable -> clear comment
+ {
+ o_pStr->clear();
+ }
+ return SwUndoId::EMPTY;
+}
+
+SwUndo * UndoManager::RemoveLastUndo()
+{
+ if (SdrUndoManager::GetRedoActionCount() ||
+ SdrUndoManager::GetRedoActionCount(TopLevel))
+ {
+ OSL_ENSURE(false, "RemoveLastUndoAction(): there are Redo actions?");
+ return nullptr;
+ }
+ if (!SdrUndoManager::GetUndoActionCount())
+ {
+ OSL_ENSURE(false, "RemoveLastUndoAction(): no Undo actions");
+ return nullptr;
+ }
+ SfxUndoAction *const pLastUndo(GetUndoAction());
+ SdrUndoManager::RemoveLastUndoAction();
+ return dynamic_cast<SwUndo *>(pLastUndo);
+}
+
+// SfxUndoManager
+
+void UndoManager::AddUndoAction(std::unique_ptr<SfxUndoAction> pAction, bool bTryMerge)
+{
+ SwUndo *const pUndo( dynamic_cast<SwUndo *>(pAction.get()) );
+ if (pUndo)
+ {
+ if (RedlineFlags::NONE == pUndo->GetRedlineFlags())
+ {
+ pUndo->SetRedlineFlags( m_rRedlineAccess.GetRedlineFlags() );
+ }
+ if (m_isAddWithIgnoreRepeat)
+ {
+ pUndo->IgnoreRepeat();
+ }
+ }
+ SdrUndoManager::AddUndoAction(std::move(pAction), bTryMerge);
+ if (m_pDocShell)
+ {
+ SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst( m_pDocShell );
+ while ( pViewFrame )
+ {
+ pViewFrame->GetBindings().Invalidate( SID_UNDO );
+ pViewFrame->GetBindings().Invalidate( SID_REDO );
+ pViewFrame = SfxViewFrame::GetNext( *pViewFrame, m_pDocShell );
+ }
+ }
+
+ // if the undo nodes array is too large, delete some actions
+ while (UNDO_ACTION_LIMIT < GetUndoNodes().Count())
+ {
+ RemoveOldestUndoAction();
+ }
+}
+
+namespace {
+
+class CursorGuard
+{
+public:
+ CursorGuard(SwEditShell & rShell, bool const bSave)
+ : m_rShell(rShell)
+ , m_bSaveCursor(bSave)
+ {
+ if (m_bSaveCursor)
+ {
+ m_rShell.Push(); // prevent modification of current cursor
+ }
+ }
+ ~CursorGuard() COVERITY_NOEXCEPT_FALSE
+ {
+ if (m_bSaveCursor)
+ {
+ m_rShell.Pop(SwCursorShell::PopMode::DeleteCurrent);
+ }
+ }
+private:
+ SwEditShell & m_rShell;
+ bool const m_bSaveCursor;
+};
+
+}
+
+bool UndoManager::impl_DoUndoRedo(UndoOrRedoType undoOrRedo)
+{
+ SwDoc & rDoc(*GetUndoNodes().GetDoc());
+
+ UnoActionContext c(& rDoc); // exception-safe StartAllAction/EndAllAction
+
+ SwEditShell *const pEditShell( rDoc.GetEditShell() );
+
+ OSL_ENSURE(pEditShell, "sw::UndoManager needs a SwEditShell!");
+ if (!pEditShell)
+ {
+ throw uno::RuntimeException();
+ }
+
+ // in case the model has controllers locked, the Undo should not
+ // change the view cursors!
+ bool const bSaveCursors(pEditShell->CursorsLocked());
+ CursorGuard aCursorGuard(*pEditShell, bSaveCursors);
+ if (!bSaveCursors)
+ {
+ // (in case Undo was called via API) clear the cursors:
+ pEditShell->KillPams();
+ pEditShell->SetMark();
+ pEditShell->ClearMark();
+ }
+
+ bool bRet(false);
+
+ ::sw::UndoRedoContext context(rDoc, *pEditShell);
+
+ // N.B. these may throw!
+ if (UndoOrRedoType::Undo == undoOrRedo)
+ {
+ bRet = SdrUndoManager::UndoWithContext(context);
+ }
+ else
+ {
+ bRet = SdrUndoManager::RedoWithContext(context);
+ }
+
+ if (bRet)
+ {
+ // if we are at the "last save" position, the document is not modified
+ if (SdrUndoManager::HasTopUndoActionMark(m_UndoSaveMark))
+ {
+ m_rState.ResetModified();
+ }
+ else
+ {
+ m_rState.SetModified();
+ }
+ }
+
+ pEditShell->HandleUndoRedoContext(context);
+
+ return bRet;
+}
+
+bool UndoManager::Undo()
+{
+ if(isTextEditActive())
+ {
+ return SdrUndoManager::Undo();
+ }
+ else
+ {
+ return impl_DoUndoRedo(UndoOrRedoType::Undo);
+ }
+}
+
+bool UndoManager::Redo()
+{
+ if(isTextEditActive())
+ {
+ return SdrUndoManager::Redo();
+ }
+ else
+ {
+ return impl_DoUndoRedo(UndoOrRedoType::Redo);
+ }
+}
+
+void UndoManager::EmptyActionsChanged()
+{
+ if (m_pDocShell)
+ {
+ m_pDocShell->Broadcast(SfxHint(SfxHintId::DocumentRepair));
+ }
+}
+
+/** N.B.: this does _not_ call SdrUndoManager::Repeat because it is not
+ possible to wrap a list action around it:
+ calling EnterListAction here will cause SdrUndoManager::Repeat
+ to repeat the list action!
+ */
+bool UndoManager::Repeat(::sw::RepeatContext & rContext,
+ sal_uInt16 const nRepeatCount)
+{
+ if (SdrUndoManager::IsInListAction())
+ {
+ OSL_ENSURE(false, "repeat in open list action???");
+ return false;
+ }
+ if (!SdrUndoManager::GetUndoActionCount(TopLevel))
+ {
+ return false;
+ }
+ SfxUndoAction *const pRepeatAction(GetUndoAction());
+ assert(pRepeatAction);
+ if (!pRepeatAction->CanRepeat(rContext))
+ {
+ return false;
+ }
+
+ OUString const comment(pRepeatAction->GetComment());
+ OUString const rcomment(pRepeatAction->GetRepeatComment(rContext));
+ SwUndoId nId;
+ if (auto const* const pSwAction = dynamic_cast<SwUndo*>(pRepeatAction))
+ nId = pSwAction->GetId();
+ else if (auto const* const pListAction = dynamic_cast<SfxListUndoAction*>(pRepeatAction))
+ nId = static_cast<SwUndoId>(pListAction->GetId());
+ else
+ return false;
+ if (DoesUndo())
+ {
+ ViewShellId nViewShellId(-1);
+ if (m_pDocShell)
+ {
+ if (const SwView* pView = m_pDocShell->GetView())
+ nViewShellId = pView->GetViewShellId();
+ }
+ EnterListAction(comment, rcomment, static_cast<sal_uInt16>(nId), nViewShellId);
+ }
+
+ SwPaM* pTmp = rContext.m_pCurrentPaM;
+ for(SwPaM& rPaM : rContext.GetRepeatPaM().GetRingContainer())
+ { // iterate over ring
+ rContext.m_pCurrentPaM = &rPaM;
+ if (DoesUndo() && & rPaM != pTmp)
+ {
+ m_isAddWithIgnoreRepeat = true;
+ }
+ for (sal_uInt16 nRptCnt = nRepeatCount; nRptCnt > 0; --nRptCnt)
+ {
+ pRepeatAction->Repeat(rContext);
+ }
+ if (DoesUndo() && & rPaM != pTmp)
+ {
+ m_isAddWithIgnoreRepeat = false;
+ }
+ rContext.m_bDeleteRepeated = false; // reset for next PaM
+ }
+ rContext.m_pCurrentPaM = pTmp;
+
+ if (DoesUndo())
+ {
+ LeaveListAction();
+ }
+ return true;
+}
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/rolbck.cxx b/sw/source/core/undo/rolbck.cxx
new file mode 100644
index 000000000..206ad9343
--- /dev/null
+++ b/sw/source/core/undo/rolbck.cxx
@@ -0,0 +1,1485 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rolbck.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <hints.hxx>
+#include <hintids.hxx>
+#include <fmtftn.hxx>
+#include <fchrfmt.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtrfmrk.hxx>
+#include <fmtfld.hxx>
+#include <fmtpdsc.hxx>
+#include <txtfld.hxx>
+#include <txtrfmrk.hxx>
+#include <txttxmrk.hxx>
+#include <txtftn.hxx>
+#include <txtflcnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcnct.hxx>
+#include <frmfmt.hxx>
+#include <ftnidx.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <paratr.hxx>
+#include <cellatr.hxx>
+#include <fldbas.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <UndoCore.hxx>
+#include <IMark.hxx>
+#include <charfmt.hxx>
+#include <strings.hrc>
+#include <bookmrk.hxx>
+#include <frameformats.hxx>
+#include <memory>
+
+OUString SwHistoryHint::GetDescription() const
+{
+ return OUString();
+}
+
+SwHistorySetFormat::SwHistorySetFormat( const SfxPoolItem* pFormatHt, sal_uLong nNd )
+ : SwHistoryHint( HSTRY_SETFMTHNT )
+ , m_pAttr( pFormatHt->Clone() )
+ , m_nNodeIndex( nNd )
+{
+ switch ( m_pAttr->Which() )
+ {
+ case RES_PAGEDESC:
+ static_cast<SwFormatPageDesc&>(*m_pAttr).ChgDefinedIn( nullptr );
+ break;
+ case RES_PARATR_DROP:
+ static_cast<SwFormatDrop&>(*m_pAttr).ChgDefinedIn( nullptr );
+ break;
+ case RES_BOXATR_FORMULA:
+ {
+ // save formulas always in plain text
+ SwTableBoxFormula& rNew = static_cast<SwTableBoxFormula&>(*m_pAttr);
+ if ( rNew.IsIntrnlName() )
+ {
+ const SwTableBoxFormula& rOld =
+ *static_cast<const SwTableBoxFormula*>(pFormatHt);
+ const SwNode* pNd = rOld.GetNodeOfFormula();
+ if ( pNd )
+ {
+ const SwTableNode* pTableNode = pNd->FindTableNode();
+ if (pTableNode)
+ {
+ SwTableFormulaUpdate aMsgHint( &pTableNode->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXNAME;
+ rNew.ChgDefinedIn( rOld.GetDefinedIn() );
+ rNew.ChangeState( &aMsgHint );
+ }
+ }
+ }
+ rNew.ChgDefinedIn( nullptr );
+ }
+ break;
+ }
+}
+
+OUString SwHistorySetFormat::GetDescription() const
+{
+ OUString aResult;
+
+ switch (m_pAttr->Which())
+ {
+ case RES_BREAK:
+ switch (static_cast<SvxFormatBreakItem &>(*m_pAttr).GetBreak())
+ {
+ case SvxBreak::PageBefore:
+ case SvxBreak::PageAfter:
+ case SvxBreak::PageBoth:
+ aResult = SwResId(STR_UNDO_PAGEBREAKS);
+
+ break;
+ case SvxBreak::ColumnBefore:
+ case SvxBreak::ColumnAfter:
+ case SvxBreak::ColumnBoth:
+ aResult = SwResId(STR_UNDO_COLBRKS);
+
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return aResult;
+}
+
+void SwHistorySetFormat::SetInDoc( SwDoc* pDoc, bool bTmpSet )
+{
+ SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
+ if ( pNode->IsContentNode() )
+ {
+ static_cast<SwContentNode*>(pNode)->SetAttr( *m_pAttr );
+ }
+ else if ( pNode->IsTableNode() )
+ {
+ static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->SetFormatAttr(
+ *m_pAttr );
+ }
+ else if ( pNode->IsStartNode() && (SwTableBoxStartNode ==
+ static_cast<SwStartNode*>(pNode)->GetStartNodeType()) )
+ {
+ SwTableNode* pTNd = pNode->FindTableNode();
+ if ( pTNd )
+ {
+ SwTableBox* pBox = pTNd->GetTable().GetTableBox( m_nNodeIndex );
+ if (pBox)
+ {
+ pBox->ClaimFrameFormat()->SetFormatAttr( *m_pAttr );
+ }
+ }
+ }
+
+ if ( !bTmpSet )
+ {
+ m_pAttr.reset();
+ }
+}
+
+SwHistorySetFormat::~SwHistorySetFormat()
+{
+}
+
+SwHistoryResetFormat::SwHistoryResetFormat(const SfxPoolItem* pFormatHt, sal_uLong nNodeIdx)
+ : SwHistoryHint( HSTRY_RESETFMTHNT )
+ , m_nNodeIndex( nNodeIdx )
+ , m_nWhich( pFormatHt->Which() )
+{
+}
+
+void SwHistoryResetFormat::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
+ if ( pNode->IsContentNode() )
+ {
+ static_cast<SwContentNode*>(pNode)->ResetAttr( m_nWhich );
+ }
+ else if ( pNode->IsTableNode() )
+ {
+ static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->
+ ResetFormatAttr( m_nWhich );
+ }
+}
+
+SwHistorySetText::SwHistorySetText( SwTextAttr* pTextHt, sal_uLong nNodePos )
+ : SwHistoryHint( HSTRY_SETTXTHNT )
+ , m_nNodeIndex( nNodePos )
+ , m_nStart( pTextHt->GetStart() )
+ , m_nEnd( pTextHt->GetAnyEnd() )
+ , m_bFormatIgnoreStart(pTextHt->IsFormatIgnoreStart())
+ , m_bFormatIgnoreEnd (pTextHt->IsFormatIgnoreEnd ())
+{
+ // Caution: the following attributes generate no format attributes:
+ // - NoLineBreak, NoHyphen, Inserted, Deleted
+ // These cases must be handled separately !!!
+
+ // a little bit complicated but works: first assign a copy of the
+ // default value and afterwards the values from text attribute
+ if ( RES_TXTATR_CHARFMT == pTextHt->Which() )
+ {
+ m_pAttr.reset( new SwFormatCharFormat( pTextHt->GetCharFormat().GetCharFormat() ) );
+ }
+ else
+ {
+ m_pAttr.reset( pTextHt->GetAttr().Clone() );
+ }
+}
+
+SwHistorySetText::~SwHistorySetText()
+{
+}
+
+void SwHistorySetText::SetInDoc( SwDoc* pDoc, bool )
+{
+ if (!m_pAttr)
+ return;
+
+ if ( RES_TXTATR_CHARFMT == m_pAttr->Which() )
+ {
+ // ask the Doc if the CharFormat still exists
+ if (!pDoc->GetCharFormats()->IsAlive(static_cast<SwFormatCharFormat&>(*m_pAttr).GetCharFormat()))
+ return; // do not set, format does not exist
+ }
+
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistorySetText::SetInDoc: not a TextNode" );
+
+ if ( pTextNd )
+ {
+ SwTextAttr *const pAttr = pTextNd->InsertItem(*m_pAttr, m_nStart, m_nEnd,
+ SetAttrMode::NOTXTATRCHR |
+ SetAttrMode::NOHINTADJUST );
+ // shouldn't be possible to hit any error/merging path from here
+ assert(pAttr);
+ if (m_bFormatIgnoreStart)
+ {
+ pAttr->SetFormatIgnoreStart(true);
+ }
+ if (m_bFormatIgnoreEnd)
+ {
+ pAttr->SetFormatIgnoreEnd(true);
+ }
+ }
+}
+
+SwHistorySetTextField::SwHistorySetTextField( const SwTextField* pTextField, sal_uLong nNodePos )
+ : SwHistoryHint( HSTRY_SETTXTFLDHNT )
+ , m_pField( new SwFormatField( *pTextField->GetFormatField().GetField() ) )
+{
+ // only copy if not Sys-FieldType
+ SwDoc* pDoc = pTextField->GetTextNode().GetDoc();
+
+ m_nFieldWhich = m_pField->GetField()->GetTyp()->Which();
+ if (m_nFieldWhich == SwFieldIds::Database ||
+ m_nFieldWhich == SwFieldIds::User ||
+ m_nFieldWhich == SwFieldIds::SetExp ||
+ m_nFieldWhich == SwFieldIds::Dde ||
+ !pDoc->getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich ))
+ {
+ m_pFieldType = m_pField->GetField()->GetTyp()->Copy();
+ m_pField->GetField()->ChgTyp( m_pFieldType.get() ); // change field type
+ }
+ m_nNodeIndex = nNodePos;
+ m_nPos = pTextField->GetStart();
+}
+
+OUString SwHistorySetTextField::GetDescription() const
+{
+ return m_pField->GetField()->GetDescription();
+}
+
+SwHistorySetTextField::~SwHistorySetTextField()
+{
+}
+
+void SwHistorySetTextField::SetInDoc( SwDoc* pDoc, bool )
+{
+ if (!m_pField)
+ return;
+
+ SwFieldType* pNewFieldType = m_pFieldType.get();
+ if ( !pNewFieldType )
+ {
+ pNewFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich );
+ }
+ else
+ {
+ // register type with the document
+ pNewFieldType = pDoc->getIDocumentFieldsAccess().InsertFieldType( *m_pFieldType );
+ }
+
+ m_pField->GetField()->ChgTyp( pNewFieldType ); // change field type
+
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistorySetTextField: no TextNode" );
+
+ if ( pTextNd )
+ {
+ pTextNd->InsertItem( *m_pField, m_nPos, m_nPos,
+ SetAttrMode::NOTXTATRCHR );
+ }
+}
+
+SwHistorySetRefMark::SwHistorySetRefMark( const SwTextRefMark* pTextHt, sal_uLong nNodePos )
+ : SwHistoryHint( HSTRY_SETREFMARKHNT )
+ , m_RefName( pTextHt->GetRefMark().GetRefName() )
+ , m_nNodeIndex( nNodePos )
+ , m_nStart( pTextHt->GetStart() )
+ , m_nEnd( pTextHt->GetAnyEnd() )
+{
+}
+
+void SwHistorySetRefMark::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistorySetRefMark: no TextNode" );
+ if ( !pTextNd )
+ return;
+
+ SwFormatRefMark aRefMark( m_RefName );
+
+ // if a reference mark without an end already exists here: must not insert!
+ if ( m_nStart != m_nEnd ||
+ !pTextNd->GetTextAttrForCharAt( m_nStart, RES_TXTATR_REFMARK ) )
+ {
+ pTextNd->InsertItem( aRefMark, m_nStart, m_nEnd,
+ SetAttrMode::NOTXTATRCHR );
+ }
+}
+
+SwHistorySetTOXMark::SwHistorySetTOXMark( const SwTextTOXMark* pTextHt, sal_uLong nNodePos )
+ : SwHistoryHint( HSTRY_SETTOXMARKHNT )
+ , m_TOXMark( pTextHt->GetTOXMark() )
+ , m_TOXName( m_TOXMark.GetTOXType()->GetTypeName() )
+ , m_eTOXTypes( m_TOXMark.GetTOXType()->GetType() )
+ , m_nNodeIndex( nNodePos )
+ , m_nStart( pTextHt->GetStart() )
+ , m_nEnd( pTextHt->GetAnyEnd() )
+{
+ m_TOXMark.EndListeningAll();
+}
+
+SwTOXType* SwHistorySetTOXMark::GetSwTOXType(SwDoc& rDoc, TOXTypes eTOXTypes, const OUString& rTOXName)
+{
+ // search for respective TOX type
+ const sal_uInt16 nCnt = rDoc.GetTOXTypeCount(eTOXTypes);
+ SwTOXType* pToxType = nullptr;
+ for ( sal_uInt16 n = 0; n < nCnt; ++n )
+ {
+ pToxType = const_cast<SwTOXType*>(rDoc.GetTOXType(eTOXTypes, n));
+ if (pToxType->GetTypeName() == rTOXName)
+ break;
+ pToxType = nullptr;
+ }
+
+ if ( !pToxType ) // TOX type not found, create new
+ {
+ pToxType = const_cast<SwTOXType*>(
+ rDoc.InsertTOXType(SwTOXType(rDoc, eTOXTypes, rTOXName)));
+ }
+
+ return pToxType;
+}
+
+void SwHistorySetTOXMark::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistorySetTOXMark: no TextNode" );
+ if ( !pTextNd )
+ return;
+
+ SwTOXType* pToxType = GetSwTOXType(*pDoc, m_eTOXTypes, m_TOXName);
+
+ SwTOXMark aNew( m_TOXMark );
+ aNew.RegisterToTOXType( *pToxType );
+
+ pTextNd->InsertItem( aNew, m_nStart, m_nEnd,
+ SetAttrMode::NOTXTATRCHR );
+}
+
+bool SwHistorySetTOXMark::IsEqual( const SwTOXMark& rCmp ) const
+{
+ return m_TOXName == rCmp.GetTOXType()->GetTypeName() &&
+ m_eTOXTypes == rCmp.GetTOXType()->GetType() &&
+ m_TOXMark.GetAlternativeText() == rCmp.GetAlternativeText() &&
+ ( (TOX_INDEX == m_eTOXTypes)
+ ? ( m_TOXMark.GetPrimaryKey() == rCmp.GetPrimaryKey() &&
+ m_TOXMark.GetSecondaryKey() == rCmp.GetSecondaryKey() )
+ : m_TOXMark.GetLevel() == rCmp.GetLevel()
+ );
+}
+
+SwHistoryResetText::SwHistoryResetText( sal_uInt16 nWhich,
+ sal_Int32 nAttrStart, sal_Int32 nAttrEnd, sal_uLong nNodePos )
+ : SwHistoryHint( HSTRY_RESETTXTHNT )
+ , m_nNodeIndex( nNodePos ), m_nStart( nAttrStart ), m_nEnd( nAttrEnd )
+ , m_nAttr( nWhich )
+{
+}
+
+void SwHistoryResetText::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistoryResetText: no TextNode" );
+ if ( pTextNd )
+ {
+ pTextNd->DeleteAttributes( m_nAttr, m_nStart, m_nEnd );
+ }
+}
+
+SwHistorySetFootnote::SwHistorySetFootnote( SwTextFootnote* pTextFootnote, sal_uLong nNodePos )
+ : SwHistoryHint( HSTRY_SETFTNHNT )
+ , m_pUndo( new SwUndoSaveSection )
+ , m_FootnoteNumber( pTextFootnote->GetFootnote().GetNumStr() )
+ , m_nNodeIndex( nNodePos )
+ , m_nStart( pTextFootnote->GetStart() )
+ , m_bEndNote( pTextFootnote->GetFootnote().IsEndNote() )
+{
+ OSL_ENSURE( pTextFootnote->GetStartNode(),
+ "SwHistorySetFootnote: Footnote without Section" );
+
+ // keep the old NodePos (because who knows what later will be saved/deleted
+ // in SaveSection)
+ SwDoc* pDoc = const_cast<SwDoc*>(pTextFootnote->GetTextNode().GetDoc());
+ SwNode* pSaveNd = pDoc->GetNodes()[ m_nNodeIndex ];
+
+ // keep pointer to StartNode of FootnoteSection and reset its attribute for now
+ // (as a result, its/all Frames will be deleted automatically)
+ SwNodeIndex aSttIdx( *pTextFootnote->GetStartNode() );
+ pTextFootnote->SetStartNode( nullptr, false );
+
+ m_pUndo->SaveSection( aSttIdx );
+ m_nNodeIndex = pSaveNd->GetIndex();
+}
+
+SwHistorySetFootnote::SwHistorySetFootnote( const SwTextFootnote &rTextFootnote )
+ : SwHistoryHint( HSTRY_SETFTNHNT )
+ , m_FootnoteNumber( rTextFootnote.GetFootnote().GetNumStr() )
+ , m_nNodeIndex( SwTextFootnote_GetIndex( (&rTextFootnote) ) )
+ , m_nStart( rTextFootnote.GetStart() )
+ , m_bEndNote( rTextFootnote.GetFootnote().IsEndNote() )
+{
+ OSL_ENSURE( rTextFootnote.GetStartNode(),
+ "SwHistorySetFootnote: Footnote without Section" );
+}
+
+OUString SwHistorySetFootnote::GetDescription() const
+{
+ return SwResId(STR_FOOTNOTE);
+}
+
+SwHistorySetFootnote::~SwHistorySetFootnote()
+{
+}
+
+void SwHistorySetFootnote::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistorySetFootnote: no TextNode" );
+ if ( !pTextNd )
+ return;
+
+ if (m_pUndo)
+ {
+ // set the footnote in the TextNode
+ SwFormatFootnote aTemp( m_bEndNote );
+ SwFormatFootnote& rNew = const_cast<SwFormatFootnote&>(
+ pDoc->GetAttrPool().Put(aTemp) );
+ if ( !m_FootnoteNumber.isEmpty() )
+ {
+ rNew.SetNumStr( m_FootnoteNumber );
+ }
+ SwTextFootnote* pTextFootnote = new SwTextFootnote( rNew, m_nStart );
+
+ // create the section of the Footnote
+ SwNodeIndex aIdx( *pTextNd );
+ m_pUndo->RestoreSection( pDoc, &aIdx, SwFootnoteStartNode );
+ pTextFootnote->SetStartNode( &aIdx );
+ if ( m_pUndo->GetHistory() )
+ {
+ // create frames only now
+ m_pUndo->GetHistory()->Rollback( pDoc );
+ }
+
+ pTextNd->InsertHint( pTextFootnote );
+ }
+ else
+ {
+ SwTextFootnote * const pFootnote =
+ static_cast<SwTextFootnote*>(
+ pTextNd->GetTextAttrForCharAt( m_nStart ));
+ assert(pFootnote);
+ SwFormatFootnote &rFootnote = const_cast<SwFormatFootnote&>(pFootnote->GetFootnote());
+ rFootnote.SetNumStr( m_FootnoteNumber );
+ if ( rFootnote.IsEndNote() != m_bEndNote )
+ {
+ rFootnote.SetEndNote( m_bEndNote );
+ pFootnote->CheckCondColl();
+ }
+ }
+}
+
+SwHistoryChangeFormatColl::SwHistoryChangeFormatColl( SwFormatColl* pFormatColl, sal_uLong nNd,
+ SwNodeType nNodeWhich )
+ : SwHistoryHint( HSTRY_CHGFMTCOLL )
+ , m_pColl( pFormatColl )
+ , m_nNodeIndex( nNd )
+ , m_nNodeType( nNodeWhich )
+{
+}
+
+void SwHistoryChangeFormatColl::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwContentNode * pContentNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetContentNode();
+ OSL_ENSURE( pContentNd, "SwHistoryChangeFormatColl: no ContentNode" );
+
+ // before setting the format, check if it is still available in the
+ // document. if it has been deleted, there is no undo!
+ if ( pContentNd && m_nNodeType == pContentNd->GetNodeType() )
+ {
+ if ( SwNodeType::Text == m_nNodeType )
+ {
+ if (pDoc->GetTextFormatColls()->IsAlive(static_cast<SwTextFormatColl *>(m_pColl)))
+ {
+ pContentNd->ChgFormatColl( m_pColl );
+ }
+ }
+ else if (pDoc->GetGrfFormatColls()->IsAlive(static_cast<SwGrfFormatColl *>(m_pColl)))
+ {
+ pContentNd->ChgFormatColl( m_pColl );
+ }
+ }
+}
+
+SwHistoryTextFlyCnt::SwHistoryTextFlyCnt( SwFrameFormat* const pFlyFormat )
+ : SwHistoryHint( HSTRY_FLYCNT )
+ , m_pUndo( new SwUndoDelLayFormat( pFlyFormat ) )
+{
+ OSL_ENSURE( pFlyFormat, "SwHistoryTextFlyCnt: no Format" );
+ m_pUndo->ChgShowSel( false );
+}
+
+SwHistoryTextFlyCnt::~SwHistoryTextFlyCnt()
+{
+}
+
+void SwHistoryTextFlyCnt::SetInDoc( SwDoc* pDoc, bool )
+{
+ ::sw::IShellCursorSupplier *const pISCS(pDoc->GetIShellCursorSupplier());
+ assert(pISCS);
+ ::sw::UndoRedoContext context(*pDoc, *pISCS);
+ m_pUndo->UndoImpl(context);
+}
+
+SwHistoryBookmark::SwHistoryBookmark(
+ const ::sw::mark::IMark& rBkmk,
+ bool bSavePos,
+ bool bSaveOtherPos)
+ : SwHistoryHint(HSTRY_BOOKMARK)
+ , m_aName(rBkmk.GetName())
+ , m_aShortName()
+ , m_aKeycode()
+ , m_nNode(bSavePos ?
+ rBkmk.GetMarkPos().nNode.GetIndex() : 0)
+ , m_nOtherNode(bSaveOtherPos ?
+ rBkmk.GetOtherMarkPos().nNode.GetIndex() : 0)
+ , m_nContent(bSavePos ?
+ rBkmk.GetMarkPos().nContent.GetIndex() : 0)
+ , m_nOtherContent(bSaveOtherPos ?
+ rBkmk.GetOtherMarkPos().nContent.GetIndex() :0)
+ , m_bSavePos(bSavePos)
+ , m_bSaveOtherPos(bSaveOtherPos)
+ , m_bHadOtherPos(rBkmk.IsExpanded())
+ , m_eBkmkType(IDocumentMarkAccess::GetType(rBkmk))
+{
+ const ::sw::mark::IBookmark* const pBookmark = dynamic_cast< const ::sw::mark::IBookmark* >(&rBkmk);
+ if(pBookmark)
+ {
+ m_aKeycode = pBookmark->GetKeyCode();
+ m_aShortName = pBookmark->GetShortName();
+
+ ::sfx2::Metadatable const*const pMetadatable(
+ dynamic_cast< ::sfx2::Metadatable const* >(pBookmark));
+ if (pMetadatable)
+ {
+ m_pMetadataUndo = pMetadatable->CreateUndo();
+ }
+ }
+}
+
+void SwHistoryBookmark::SetInDoc( SwDoc* pDoc, bool )
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodes& rNds = pDoc->GetNodes();
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+ std::unique_ptr<SwPaM> pPam;
+ ::sw::mark::IMark* pMark = nullptr;
+
+ if(m_bSavePos)
+ {
+ SwContentNode* const pContentNd = rNds[m_nNode]->GetContentNode();
+ OSL_ENSURE(pContentNd,
+ "<SwHistoryBookmark::SetInDoc(..)>"
+ " - wrong node for a mark");
+
+ // #111660# don't crash when nNode1 doesn't point to content node.
+ if(pContentNd)
+ pPam.reset(new SwPaM(*pContentNd, m_nContent));
+ }
+ else
+ {
+ pMark = *pMarkAccess->findMark(m_aName);
+ pPam.reset(new SwPaM(pMark->GetMarkPos()));
+ }
+
+ if(m_bSaveOtherPos)
+ {
+ SwContentNode* const pContentNd = rNds[m_nOtherNode]->GetContentNode();
+ OSL_ENSURE(pContentNd,
+ "<SwHistoryBookmark::SetInDoc(..)>"
+ " - wrong node for a mark");
+
+ if (pPam != nullptr && pContentNd)
+ {
+ pPam->SetMark();
+ pPam->GetMark()->nNode = m_nOtherNode;
+ pPam->GetMark()->nContent.Assign(pContentNd, m_nOtherContent);
+ }
+ }
+ else if(m_bHadOtherPos)
+ {
+ if(!pMark)
+ pMark = *pMarkAccess->findMark(m_aName);
+ OSL_ENSURE(pMark->IsExpanded(),
+ "<SwHistoryBookmark::SetInDoc(..)>"
+ " - missing pos on old mark");
+ pPam->SetMark();
+ *pPam->GetMark() = pMark->GetOtherMarkPos();
+ }
+
+ if (pPam)
+ {
+ if ( pMark != nullptr )
+ {
+ pMarkAccess->deleteMark( pMark );
+ }
+ ::sw::mark::IBookmark* const pBookmark =
+ dynamic_cast<::sw::mark::IBookmark*>(
+ pMarkAccess->makeMark(*pPam, m_aName, m_eBkmkType, sw::mark::InsertMode::New));
+ if ( pBookmark != nullptr )
+ {
+ pBookmark->SetKeyCode(m_aKeycode);
+ pBookmark->SetShortName(m_aShortName);
+ if (m_pMetadataUndo)
+ {
+ ::sfx2::Metadatable * const pMeta(
+ dynamic_cast< ::sfx2::Metadatable* >(pBookmark));
+ OSL_ENSURE(pMeta, "metadata undo, but not metadatable?");
+ if (pMeta)
+ {
+ pMeta->RestoreMetadata(m_pMetadataUndo);
+ }
+ }
+ }
+ }
+}
+
+bool SwHistoryBookmark::IsEqualBookmark(const ::sw::mark::IMark& rBkmk)
+{
+ return m_nNode == rBkmk.GetMarkPos().nNode.GetIndex()
+ && m_nContent == rBkmk.GetMarkPos().nContent.GetIndex()
+ && m_aName == rBkmk.GetName();
+}
+
+SwHistoryNoTextFieldmark::SwHistoryNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark)
+ : SwHistoryHint(HSTRY_NOTEXTFIELDMARK)
+ , m_sType(rFieldMark.GetFieldname())
+ , m_nNode(rFieldMark.GetMarkPos().nNode.GetIndex())
+ , m_nContent(rFieldMark.GetMarkPos().nContent.GetIndex())
+{
+}
+
+void SwHistoryNoTextFieldmark::SetInDoc(SwDoc* pDoc, bool)
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodes& rNds = pDoc->GetNodes();
+ std::unique_ptr<SwPaM> pPam;
+
+ const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode();
+ if(pContentNd)
+ pPam.reset(new SwPaM(*pContentNd, m_nContent));
+
+ if (pPam)
+ {
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+ pMarkAccess->makeNoTextFieldBookmark(*pPam, OUString(), m_sType);
+ }
+}
+
+void SwHistoryNoTextFieldmark::ResetInDoc(SwDoc* pDoc)
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodes& rNds = pDoc->GetNodes();
+ std::unique_ptr<SwPaM> pPam;
+
+ const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode();
+ if(pContentNd)
+ pPam.reset(new SwPaM(*pContentNd, m_nContent-1));
+
+ if (pPam)
+ {
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+ pMarkAccess->deleteFieldmarkAt(*pPam->GetPoint());
+ }
+}
+
+SwHistoryTextFieldmark::SwHistoryTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark)
+ : SwHistoryHint(HSTRY_TEXTFIELDMARK)
+ , m_sName(rFieldMark.GetName())
+ , m_sType(rFieldMark.GetFieldname())
+ , m_nStartNode(rFieldMark.GetMarkStart().nNode.GetIndex())
+ , m_nStartContent(rFieldMark.GetMarkStart().nContent.GetIndex())
+ , m_nEndNode(rFieldMark.GetMarkEnd().nNode.GetIndex())
+ , m_nEndContent(rFieldMark.GetMarkEnd().nContent.GetIndex())
+{
+ SwPosition const sepPos(sw::mark::FindFieldSep(rFieldMark));
+ m_nSepNode = sepPos.nNode.GetIndex();
+ m_nSepContent = sepPos.nContent.GetIndex();
+}
+
+void SwHistoryTextFieldmark::SetInDoc(SwDoc* pDoc, bool)
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodes& rNds = pDoc->GetNodes();
+
+ assert(rNds[m_nStartNode]->IsContentNode());
+ assert(rNds[m_nEndNode]->IsContentNode());
+ assert(rNds[m_nSepNode]->IsContentNode());
+
+ SwPaM const pam(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent,
+ *rNds[m_nEndNode]->GetContentNode(),
+ // subtract 1 for the CH_TXT_ATR_FIELDEND itself,
+ // plus more if same node as other CH_TXT_ATR
+ m_nStartNode == m_nEndNode
+ ? (m_nEndContent - 3)
+ : m_nSepNode == m_nEndNode
+ ? (m_nEndContent - 2)
+ : (m_nEndContent - 1));
+ SwPosition const sepPos(*rNds[m_nSepNode]->GetContentNode(),
+ m_nStartNode == m_nSepNode ? (m_nSepContent - 1) : m_nSepContent);
+
+ IDocumentMarkAccess & rMarksAccess(*pDoc->getIDocumentMarkAccess());
+ rMarksAccess.makeFieldBookmark(pam, m_sName, m_sType, &sepPos);
+}
+
+void SwHistoryTextFieldmark::ResetInDoc(SwDoc* pDoc)
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodes& rNds = pDoc->GetNodes();
+
+ assert(rNds[m_nStartNode]->IsContentNode());
+ assert(rNds[m_nEndNode]->IsContentNode());
+ assert(rNds[m_nSepNode]->IsContentNode());
+
+ SwPosition const pos(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent);
+
+ IDocumentMarkAccess & rMarksAccess(*pDoc->getIDocumentMarkAccess());
+ rMarksAccess.deleteFieldmarkAt(pos);
+}
+
+SwHistorySetAttrSet::SwHistorySetAttrSet( const SfxItemSet& rSet,
+ sal_uLong nNodePos, const std::set<sal_uInt16> &rSetArr )
+ : SwHistoryHint( HSTRY_SETATTRSET )
+ , m_OldSet( rSet )
+ , m_ResetArray( 0, 4 )
+ , m_nNodeIndex( nNodePos )
+{
+ SfxItemIter aIter( m_OldSet ), aOrigIter( rSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem(),
+ * pOrigItem = aOrigIter.GetCurItem();
+ while (pItem && pOrigItem)
+ {
+ if( !rSetArr.count( pOrigItem->Which() ))
+ {
+ m_ResetArray.push_back( pOrigItem->Which() );
+ m_OldSet.ClearItem( pOrigItem->Which() );
+ }
+ else
+ {
+ switch ( pItem->Which() )
+ {
+ case RES_PAGEDESC:
+ static_cast<SwFormatPageDesc*>(
+ const_cast<SfxPoolItem*>(pItem))->ChgDefinedIn( nullptr );
+ break;
+
+ case RES_PARATR_DROP:
+ static_cast<SwFormatDrop*>(
+ const_cast<SfxPoolItem*>(pItem))->ChgDefinedIn( nullptr );
+ break;
+
+ case RES_BOXATR_FORMULA:
+ {
+ // When a formula is set, never save the value. It
+ // possibly must be recalculated!
+ // Save formulas always in plain text
+ m_OldSet.ClearItem( RES_BOXATR_VALUE );
+
+ SwTableBoxFormula& rNew =
+ *static_cast<SwTableBoxFormula*>(
+ const_cast<SfxPoolItem*>(pItem));
+ if ( rNew.IsIntrnlName() )
+ {
+ const SwTableBoxFormula& rOld =
+ rSet.Get( RES_BOXATR_FORMULA );
+ const SwNode* pNd = rOld.GetNodeOfFormula();
+ if ( pNd )
+ {
+ const SwTableNode* pTableNode
+ = pNd->FindTableNode();
+ if (pTableNode)
+ {
+ SwTableFormulaUpdate aMsgHint(
+ &pTableNode->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXNAME;
+ rNew.ChgDefinedIn( rOld.GetDefinedIn() );
+ rNew.ChangeState( &aMsgHint );
+ }
+ }
+ }
+ rNew.ChgDefinedIn( nullptr );
+ }
+ break;
+ }
+ }
+
+ pItem = aIter.NextItem();
+ pOrigItem = aOrigIter.NextItem();
+ }
+}
+
+void SwHistorySetAttrSet::SetInDoc( SwDoc* pDoc, bool )
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
+ if ( pNode->IsContentNode() )
+ {
+ static_cast<SwContentNode*>(pNode)->SetAttr( m_OldSet );
+ if ( !m_ResetArray.empty() )
+ {
+ static_cast<SwContentNode*>(pNode)->ResetAttr( m_ResetArray );
+ }
+ }
+ else if ( pNode->IsTableNode() )
+ {
+ SwFormat& rFormat =
+ *static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat();
+ rFormat.SetFormatAttr( m_OldSet );
+ if ( !m_ResetArray.empty() )
+ {
+ rFormat.ResetFormatAttr( m_ResetArray.front() );
+ }
+ }
+}
+
+SwHistoryChangeFlyAnchor::SwHistoryChangeFlyAnchor( SwFrameFormat& rFormat )
+ : SwHistoryHint( HSTRY_CHGFLYANCHOR )
+ , m_rFormat( rFormat )
+ , m_nOldNodeIndex( rFormat.GetAnchor().GetContentAnchor()->nNode.GetIndex() )
+ , m_nOldContentIndex( (RndStdIds::FLY_AT_CHAR == rFormat.GetAnchor().GetAnchorId())
+ ? rFormat.GetAnchor().GetContentAnchor()->nContent.GetIndex()
+ : COMPLETE_STRING )
+{
+}
+
+void SwHistoryChangeFlyAnchor::SetInDoc( SwDoc* pDoc, bool )
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ if (pDoc->GetSpzFrameFormats()->IsAlive(&m_rFormat)) // Format does still exist
+ {
+ SwFormatAnchor aTmp( m_rFormat.GetAnchor() );
+
+ SwNode* pNd = pDoc->GetNodes()[ m_nOldNodeIndex ];
+ SwContentNode* pCNd = pNd->GetContentNode();
+ SwPosition aPos( *pNd );
+ if ( COMPLETE_STRING != m_nOldContentIndex )
+ {
+ OSL_ENSURE(pCNd, "SwHistoryChangeFlyAnchor: no ContentNode");
+ if (pCNd)
+ {
+ aPos.nContent.Assign( pCNd, m_nOldContentIndex );
+ }
+ }
+ aTmp.SetAnchor( &aPos );
+
+ // so the Layout does not get confused
+ if (!pCNd || !pCNd->getLayoutFrame(pDoc->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr))
+ {
+ m_rFormat.DelFrames();
+ }
+
+ m_rFormat.SetFormatAttr( aTmp );
+ }
+}
+
+SwHistoryChangeFlyChain::SwHistoryChangeFlyChain( SwFlyFrameFormat& rFormat,
+ const SwFormatChain& rAttr )
+ : SwHistoryHint( HSTRY_CHGFLYCHAIN )
+ , m_pPrevFormat( rAttr.GetPrev() )
+ , m_pNextFormat( rAttr.GetNext() )
+ , m_pFlyFormat( &rFormat )
+{
+}
+
+void SwHistoryChangeFlyChain::SetInDoc( SwDoc* pDoc, bool )
+{
+ if (pDoc->GetSpzFrameFormats()->IsAlive(m_pFlyFormat))
+ {
+ SwFormatChain aChain;
+
+ if (m_pPrevFormat &&
+ pDoc->GetSpzFrameFormats()->IsAlive(m_pPrevFormat))
+ {
+ aChain.SetPrev( m_pPrevFormat );
+ SwFormatChain aTmp( m_pPrevFormat->GetChain() );
+ aTmp.SetNext( m_pFlyFormat );
+ m_pPrevFormat->SetFormatAttr( aTmp );
+ }
+
+ if (m_pNextFormat &&
+ pDoc->GetSpzFrameFormats()->IsAlive(m_pNextFormat))
+ {
+ aChain.SetNext( m_pNextFormat );
+ SwFormatChain aTmp( m_pNextFormat->GetChain() );
+ aTmp.SetPrev( m_pFlyFormat );
+ m_pNextFormat->SetFormatAttr( aTmp );
+ }
+
+ if ( aChain.GetNext() || aChain.GetPrev() )
+ {
+ m_pFlyFormat->SetFormatAttr( aChain );
+ }
+ }
+}
+
+// -> #i27615#
+SwHistoryChangeCharFormat::SwHistoryChangeCharFormat(const SfxItemSet & rSet,
+ const OUString & sFormat)
+ : SwHistoryHint(HSTRY_CHGCHARFMT)
+ , m_OldSet(rSet), m_Format(sFormat)
+{
+}
+
+void SwHistoryChangeCharFormat::SetInDoc(SwDoc * pDoc, bool )
+{
+ SwCharFormat * pCharFormat = pDoc->FindCharFormatByName(m_Format);
+
+ if (pCharFormat)
+ {
+ pCharFormat->SetFormatAttr(m_OldSet);
+ }
+}
+// <- #i27615#
+
+SwHistory::SwHistory()
+ : m_SwpHstry()
+ , m_nEndDiff( 0 )
+{
+}
+
+SwHistory::~SwHistory()
+{
+}
+
+void SwHistory::Add(
+ const SfxPoolItem* pOldValue,
+ const SfxPoolItem* pNewValue,
+ sal_uLong nNodeIdx)
+{
+ OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
+ const sal_uInt16 nWhich(pNewValue->Which());
+
+ // excluded values
+ if(nWhich == RES_TXTATR_FIELD || nWhich == RES_TXTATR_ANNOTATION)
+ {
+ return;
+ }
+
+ // no default Attribute?
+ std::unique_ptr<SwHistoryHint> pHt;
+
+ // To be able to include the DrawingLayer FillItems something more
+ // general has to be done to check if an Item is default than to check
+ // if its pointer equals that in Writer's global PoolDefaults (held in
+ // aAttrTab and used to fill the pool defaults in Writer - looks as if
+ // Writer is *older* than the SfxItemPool ?). I checked the possibility to
+ // get the SfxItemPool here (works), but decided to use the SfxPoolItem's
+ // global tooling aka IsDefaultItem(const SfxPoolItem*) for now
+ if(pOldValue && !IsDefaultItem(pOldValue))
+ {
+ pHt.reset( new SwHistorySetFormat( pOldValue, nNodeIdx ) );
+ }
+ else
+ {
+ pHt.reset( new SwHistoryResetFormat( pNewValue, nNodeIdx ) );
+ }
+
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+// FIXME: refactor the following "Add" methods (DRY)?
+void SwHistory::Add( SwTextAttr* pHint, sal_uLong nNodeIdx, bool bNewAttr )
+{
+ OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
+
+ std::unique_ptr<SwHistoryHint> pHt;
+ if( !bNewAttr )
+ {
+ switch ( pHint->Which() )
+ {
+ case RES_TXTATR_FTN:
+ pHt.reset( new SwHistorySetFootnote(
+ static_cast<SwTextFootnote*>(pHint), nNodeIdx ) );
+ break;
+ case RES_TXTATR_FLYCNT:
+ pHt.reset( new SwHistoryTextFlyCnt( static_cast<SwTextFlyCnt*>(pHint)
+ ->GetFlyCnt().GetFrameFormat() ) );
+ break;
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ pHt.reset( new SwHistorySetTextField(
+ static_txtattr_cast<SwTextField*>(pHint), nNodeIdx) );
+ break;
+ case RES_TXTATR_TOXMARK:
+ pHt.reset( new SwHistorySetTOXMark(
+ static_txtattr_cast<SwTextTOXMark*>(pHint), nNodeIdx) );
+ break;
+ case RES_TXTATR_REFMARK:
+ pHt.reset( new SwHistorySetRefMark(
+ static_txtattr_cast<SwTextRefMark*>(pHint), nNodeIdx) );
+ break;
+ default:
+ pHt.reset( new SwHistorySetText( pHint, nNodeIdx ) );
+ }
+ }
+ else
+ {
+ pHt.reset( new SwHistoryResetText( pHint->Which(), pHint->GetStart(),
+ pHint->GetAnyEnd(), nNodeIdx ) );
+ }
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+void SwHistory::Add( SwFormatColl* pColl, sal_uLong nNodeIdx, SwNodeType nWhichNd )
+{
+ OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
+
+ std::unique_ptr<SwHistoryHint> pHt(
+ new SwHistoryChangeFormatColl( pColl, nNodeIdx, nWhichNd ));
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+void SwHistory::Add(const ::sw::mark::IMark& rBkmk, bool bSavePos, bool bSaveOtherPos)
+{
+ OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
+
+ std::unique_ptr<SwHistoryHint> pHt;
+
+ switch (IDocumentMarkAccess::GetType(rBkmk))
+ {
+ case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
+ assert(bSavePos && bSaveOtherPos); // must be deleted completely!
+ pHt.reset(new SwHistoryTextFieldmark(dynamic_cast<sw::mark::IFieldmark const&>(rBkmk)));
+ break;
+ case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
+ assert(bSavePos && bSaveOtherPos); // must be deleted completely!
+ pHt.reset(new SwHistoryNoTextFieldmark(dynamic_cast<sw::mark::IFieldmark const&>(rBkmk)));
+ break;
+ default:
+ pHt.reset(new SwHistoryBookmark(rBkmk, bSavePos, bSaveOtherPos));
+ break;
+ }
+
+ assert(pHt);
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+void SwHistory::AddChangeFlyAnchor(SwFrameFormat& rFormat)
+{
+ std::unique_ptr<SwHistoryHint> pHt(new SwHistoryChangeFlyAnchor( rFormat ));
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+void SwHistory::AddDeleteFly(SwFrameFormat& rFormat, sal_uInt16& rSetPos)
+{
+ OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
+
+ const sal_uInt16 nWh = rFormat.Which();
+ (void) nWh;
+ // only Flys!
+ assert((RES_FLYFRMFMT == nWh && dynamic_cast<SwFlyFrameFormat*>(&rFormat))
+ || (RES_DRAWFRMFMT == nWh && dynamic_cast<SwDrawFrameFormat*>(&rFormat)));
+ {
+ std::unique_ptr<SwHistoryHint> pHint(new SwHistoryTextFlyCnt( &rFormat ));
+ m_SwpHstry.push_back( std::move(pHint) );
+
+ const SwFormatChain* pChainItem;
+ if( SfxItemState::SET == rFormat.GetItemState( RES_CHAIN, false,
+ reinterpret_cast<const SfxPoolItem**>(&pChainItem) ))
+ {
+ assert(RES_FLYFRMFMT == nWh); // not supported on SdrObjects
+ if( pChainItem->GetNext() || pChainItem->GetPrev() )
+ {
+ std::unique_ptr<SwHistoryHint> pHt(
+ new SwHistoryChangeFlyChain(static_cast<SwFlyFrameFormat&>(rFormat), *pChainItem));
+ m_SwpHstry.insert( m_SwpHstry.begin() + rSetPos++, std::move(pHt) );
+ if ( pChainItem->GetNext() )
+ {
+ SwFormatChain aTmp( pChainItem->GetNext()->GetChain() );
+ aTmp.SetPrev( nullptr );
+ pChainItem->GetNext()->SetFormatAttr( aTmp );
+ }
+ if ( pChainItem->GetPrev() )
+ {
+ SwFormatChain aTmp( pChainItem->GetPrev()->GetChain() );
+ aTmp.SetNext( nullptr );
+ pChainItem->GetPrev()->SetFormatAttr( aTmp );
+ }
+ }
+ rFormat.ResetFormatAttr( RES_CHAIN );
+ }
+ }
+}
+
+void SwHistory::Add( const SwTextFootnote& rFootnote )
+{
+ std::unique_ptr<SwHistoryHint> pHt(new SwHistorySetFootnote( rFootnote ));
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+// #i27615#
+void SwHistory::Add(const SfxItemSet & rSet, const SwCharFormat & rFormat)
+{
+ std::unique_ptr<SwHistoryHint> pHt(new SwHistoryChangeCharFormat(rSet, rFormat.GetName()));
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+bool SwHistory::Rollback( SwDoc* pDoc, sal_uInt16 nStart )
+{
+ if ( !Count() )
+ return false;
+
+ for ( sal_uInt16 i = Count(); i > nStart ; )
+ {
+ SwHistoryHint * pHHt = m_SwpHstry[ --i ].get();
+ pHHt->SetInDoc( pDoc, false );
+ }
+ m_SwpHstry.erase( m_SwpHstry.begin() + nStart, m_SwpHstry.end() );
+ m_nEndDiff = 0;
+ return true;
+}
+
+bool SwHistory::TmpRollback( SwDoc* pDoc, sal_uInt16 nStart, bool bToFirst )
+{
+ sal_uInt16 nEnd = Count() - m_nEndDiff;
+ if ( !Count() || !nEnd || nStart >= nEnd )
+ return false;
+
+ if ( bToFirst )
+ {
+ for ( ; nEnd > nStart; ++m_nEndDiff )
+ {
+ SwHistoryHint* pHHt = m_SwpHstry[ --nEnd ].get();
+ pHHt->SetInDoc( pDoc, true );
+ }
+ }
+ else
+ {
+ for ( ; nStart < nEnd; ++m_nEndDiff, ++nStart )
+ {
+ SwHistoryHint* pHHt = m_SwpHstry[ nStart ].get();
+ pHHt->SetInDoc( pDoc, true );
+ }
+ }
+ return true;
+}
+
+sal_uInt16 SwHistory::SetTmpEnd( sal_uInt16 nNewTmpEnd )
+{
+ OSL_ENSURE( nNewTmpEnd <= Count(), "SwHistory::SetTmpEnd: out of bounds" );
+
+ const sal_uInt16 nOld = Count() - m_nEndDiff;
+ m_nEndDiff = Count() - nNewTmpEnd;
+
+ // for every SwHistoryFlyCnt, call the Redo of its UndoObject.
+ // this saves the formats of the flys!
+ for ( sal_uInt16 n = nOld; n < nNewTmpEnd; n++ )
+ {
+ if ( HSTRY_FLYCNT == (*this)[ n ]->Which() )
+ {
+ static_cast<SwHistoryTextFlyCnt*>((*this)[ n ])
+ ->GetUDelLFormat()->RedoForRollback();
+ }
+ }
+
+ return nOld;
+}
+
+void SwHistory::CopyFormatAttr(
+ const SfxItemSet& rSet,
+ sal_uLong nNodeIdx)
+{
+ if(rSet.Count())
+ {
+ SfxItemIter aIter(rSet);
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ if(!IsInvalidItem(pItem))
+ {
+ Add(pItem, pItem, nNodeIdx);
+ }
+
+ pItem = aIter.NextItem();
+
+ } while(pItem);
+ }
+}
+
+void SwHistory::CopyAttr(
+ SwpHints const * pHts,
+ const sal_uLong nNodeIdx,
+ const sal_Int32 nStart,
+ const sal_Int32 nEnd,
+ const bool bCopyFields )
+{
+ if( !pHts )
+ return;
+
+ // copy all attributes of the TextNode in the area from nStart to nEnd
+ SwTextAttr* pHt;
+ for( size_t n = 0; n < pHts->Count(); ++n )
+ {
+ // nAttrStt must even be set when !pEndIdx
+ pHt = pHts->Get(n);
+ const sal_Int32 nAttrStt = pHt->GetStart();
+ const sal_Int32 * pEndIdx = pHt->GetEnd();
+ if( nullptr != pEndIdx && nAttrStt > nEnd )
+ break;
+
+ // never copy Flys and Footnote !!
+ bool bNextAttr = false;
+ switch( pHt->Which() )
+ {
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ if( !bCopyFields )
+ bNextAttr = true;
+ break;
+ case RES_TXTATR_FLYCNT:
+ case RES_TXTATR_FTN:
+ bNextAttr = true;
+ break;
+ }
+
+ if( bNextAttr )
+ continue;
+
+ // save all attributes that are somehow in this area
+ if ( nStart <= nAttrStt )
+ {
+ if ( nEnd > nAttrStt )
+ {
+ Add( pHt, nNodeIdx, false );
+ }
+ }
+ else if ( pEndIdx && nStart < *pEndIdx )
+ {
+ Add( pHt, nNodeIdx, false );
+ }
+ }
+}
+
+// Class to register the history at a Node, Format, HintsArray, ...
+SwRegHistory::SwRegHistory( SwHistory* pHst )
+ : SwClient( nullptr )
+ , m_pHistory( pHst )
+ , m_nNodeIndex( ULONG_MAX )
+{
+ MakeSetWhichIds();
+}
+
+SwRegHistory::SwRegHistory( SwModify* pRegIn, const SwNode& rNd,
+ SwHistory* pHst )
+ : SwClient( pRegIn )
+ , m_pHistory( pHst )
+ , m_nNodeIndex( rNd.GetIndex() )
+{
+ MakeSetWhichIds();
+}
+
+SwRegHistory::SwRegHistory( const SwNode& rNd, SwHistory* pHst )
+ : SwClient( nullptr )
+ , m_pHistory( pHst )
+ , m_nNodeIndex( rNd.GetIndex() )
+{
+ MakeSetWhichIds();
+}
+
+void SwRegHistory::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew )
+{
+ if ( m_pHistory && pNew && pOld != pNew )
+ {
+ if ( pNew->Which() < POOLATTR_END )
+ {
+ if(RES_UPDATE_ATTR == pNew->Which())
+ {
+ // const SfxItemPool& rPool = static_cast< const SwUpdateAttr* >(pNew)->GetSfxItemPool();
+
+ m_pHistory->Add(
+ // rPool,
+ pOld,
+ pNew,
+ m_nNodeIndex);
+ }
+ else
+ {
+ OSL_ENSURE(false, "Unexpected update attribute (!)");
+ }
+ }
+ else if (pOld && RES_ATTRSET_CHG == pNew->Which())
+ {
+ std::unique_ptr<SwHistoryHint> pNewHstr;
+ const SfxItemSet& rSet = *static_cast< const SwAttrSetChg* >(pOld)->GetChgSet();
+
+ if ( 1 < rSet.Count() )
+ {
+ pNewHstr.reset( new SwHistorySetAttrSet( rSet, m_nNodeIndex, m_WhichIdSet ) );
+ }
+ else if (const SfxPoolItem* pItem = SfxItemIter(rSet).GetCurItem())
+ {
+ if ( m_WhichIdSet.count( pItem->Which() ) )
+ {
+ pNewHstr.reset( new SwHistorySetFormat( pItem, m_nNodeIndex ) );
+ }
+ else
+ {
+ pNewHstr.reset( new SwHistoryResetFormat( pItem, m_nNodeIndex ) );
+ }
+ }
+
+ if (pNewHstr)
+ m_pHistory->m_SwpHstry.push_back( std::move(pNewHstr) );
+ }
+ }
+}
+
+void SwRegHistory::AddHint( SwTextAttr* pHt, const bool bNew )
+{
+ m_pHistory->Add( pHt, m_nNodeIndex, bNew );
+}
+
+bool SwRegHistory::InsertItems( const SfxItemSet& rSet,
+ sal_Int32 const nStart, sal_Int32 const nEnd, SetAttrMode const nFlags,
+ SwTextAttr **ppNewTextAttr )
+{
+ if( !rSet.Count() )
+ return false;
+
+ SwTextNode * const pTextNode =
+ dynamic_cast<SwTextNode *>(GetRegisteredIn());
+
+ OSL_ENSURE(pTextNode, "SwRegHistory not registered at text node?");
+ if (!pTextNode)
+ return false;
+
+ if (m_pHistory)
+ {
+ pTextNode->GetOrCreateSwpHints().Register(this);
+ }
+
+ const bool bInserted = pTextNode->SetAttr( rSet, nStart, nEnd, nFlags, ppNewTextAttr );
+
+ // Caution: The array can be deleted when inserting an attribute!
+ // This can happen when the value that should be added first deletes
+ // an existing attribute but does not need to be added itself because
+ // the paragraph attributes are identical
+ // ( -> bForgetAttr in SwpHints::Insert )
+ if ( pTextNode->GetpSwpHints() && m_pHistory )
+ {
+ pTextNode->GetpSwpHints()->DeRegister();
+ }
+
+#ifndef NDEBUG
+ if ( m_pHistory && bInserted )
+ {
+ SfxItemIter aIter(rSet);
+ for (SfxPoolItem const* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ { // check that the history recorded a hint to reset every item
+ sal_uInt16 const nWhich(pItem->Which());
+ sal_uInt16 const nExpected(
+ (isCHRATR(nWhich) || RES_TXTATR_UNKNOWN_CONTAINER == nWhich)
+ ? RES_TXTATR_AUTOFMT
+ : nWhich);
+ if (RES_TXTATR_AUTOFMT == nExpected)
+ continue; // special case, may get set on text node itself
+ // tdf#105077 even worse, node's set could cause
+ // nothing at all to be inserted
+ assert(std::any_of(
+ m_pHistory->m_SwpHstry.begin(), m_pHistory->m_SwpHstry.end(),
+ [nExpected](std::unique_ptr<SwHistoryHint> const& pHint) -> bool {
+ SwHistoryResetText const*const pReset(
+ dynamic_cast<SwHistoryResetText const*>(pHint.get()));
+ return pReset && (pReset->GetWhich() == nExpected);
+ }));
+ }
+ }
+#endif
+
+ return bInserted;
+}
+
+void SwRegHistory::RegisterInModify( SwModify* pRegIn, const SwNode& rNd )
+{
+ if ( m_pHistory && pRegIn )
+ {
+ pRegIn->Add( this );
+ m_nNodeIndex = rNd.GetIndex();
+ MakeSetWhichIds();
+ }
+ else
+ {
+ m_WhichIdSet.clear();
+ }
+}
+
+void SwRegHistory::MakeSetWhichIds()
+{
+ if (!m_pHistory) return;
+
+ m_WhichIdSet.clear();
+
+ if( GetRegisteredIn() )
+ {
+ const SfxItemSet* pSet = nullptr;
+ if( dynamic_cast< const SwContentNode *>( GetRegisteredIn() ) != nullptr )
+ {
+ pSet = static_cast<SwContentNode*>(
+ GetRegisteredIn())->GetpSwAttrSet();
+ }
+ else if ( dynamic_cast< const SwFormat *>( GetRegisteredIn() ) != nullptr )
+ {
+ pSet = &static_cast<SwFormat*>(
+ GetRegisteredIn())->GetAttrSet();
+ }
+ if( pSet && pSet->Count() )
+ {
+ SfxItemIter aIter( *pSet );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ sal_uInt16 nW = pItem->Which();
+ m_WhichIdSet.insert( nW );
+ }
+ }
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unattr.cxx b/sw/source/core/undo/unattr.cxx
new file mode 100644
index 000000000..bccbaba80
--- /dev/null
+++ b/sw/source/core/undo/unattr.cxx
@@ -0,0 +1,1055 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <utility>
+
+#include <UndoAttribute.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/tstpitem.hxx>
+#include <svx/svdobj.hxx>
+#include <hintids.hxx>
+#include <fmtflcnt.hxx>
+#include <txtftn.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <frmfmt.hxx>
+#include <fmtcntnt.hxx>
+#include <ftnidx.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <docary.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <swtblfmt.hxx>
+#include <UndoCore.hxx>
+#include <hints.hxx>
+#include <rolbck.hxx>
+#include <ndnotxt.hxx>
+#include <ftninfo.hxx>
+#include <redline.hxx>
+#include <section.hxx>
+#include <charfmt.hxx>
+#include <calbck.hxx>
+#include <frameformats.hxx>
+
+SwUndoFormatAttrHelper::SwUndoFormatAttrHelper( SwFormat& rFormat, bool bSvDrwPt )
+ : SwClient( &rFormat )
+ , m_bSaveDrawPt( bSvDrwPt )
+{
+}
+
+void SwUndoFormatAttrHelper::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew )
+{
+ if( pOld ) {
+ if ( pOld->Which() == RES_OBJECTDYING ) {
+ CheckRegistration( pOld );
+ } else if ( pNew ) {
+ const SwDoc& rDoc = *static_cast<SwFormat*>(GetRegisteredInNonConst())->GetDoc();
+ if( POOLATTR_END >= pOld->Which() ) {
+ if ( GetUndo() ) {
+ m_pUndo->PutAttr( *pOld, rDoc );
+ } else {
+ m_pUndo.reset( new SwUndoFormatAttr( *pOld,
+ *static_cast<SwFormat*>(GetRegisteredInNonConst()), m_bSaveDrawPt ) );
+ }
+ } else if ( RES_ATTRSET_CHG == pOld->Which() ) {
+ if ( GetUndo() ) {
+ SfxItemIter aIter(
+ *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() );
+ for (auto pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ m_pUndo->PutAttr( *pItem, rDoc );
+ }
+ } else {
+ m_pUndo.reset( new SwUndoFormatAttr(
+ *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet(),
+ *static_cast<SwFormat*>(GetRegisteredInNonConst()), m_bSaveDrawPt ) );
+ }
+ }
+ }
+ }
+}
+
+SwUndoFormatAttr::SwUndoFormatAttr( const SfxItemSet& rOldSet,
+ SwFormat& rChgFormat,
+ bool bSaveDrawPt )
+ : SwUndo( SwUndoId::INSFMTATTR, rChgFormat.GetDoc() )
+ , m_sFormatName ( rChgFormat.GetName() )
+ // #i56253#
+ , m_pOldSet( new SfxItemSet( rOldSet ) )
+ , m_nNodeIndex( 0 )
+ , m_nFormatWhich( rChgFormat.Which() )
+ , m_bSaveDrawPt( bSaveDrawPt )
+{
+ Init( rChgFormat );
+}
+
+SwUndoFormatAttr::SwUndoFormatAttr( const SfxPoolItem& rItem, SwFormat& rChgFormat,
+ bool bSaveDrawPt )
+ : SwUndo( SwUndoId::INSFMTATTR, rChgFormat.GetDoc() )
+ , m_sFormatName(rChgFormat.GetName())
+ , m_pOldSet( rChgFormat.GetAttrSet().Clone( false ) )
+ , m_nNodeIndex( 0 )
+ , m_nFormatWhich( rChgFormat.Which() )
+ , m_bSaveDrawPt( bSaveDrawPt )
+{
+ m_pOldSet->Put( rItem );
+ Init( rChgFormat );
+}
+
+void SwUndoFormatAttr::Init( const SwFormat & rFormat )
+{
+ // tdf#126017 never save SwNodeIndex, it will go stale
+ m_pOldSet->ClearItem(RES_CNTNT);
+ // treat change of anchor specially
+ if ( SfxItemState::SET == m_pOldSet->GetItemState( RES_ANCHOR, false )) {
+ SaveFlyAnchor( &rFormat, m_bSaveDrawPt );
+ } else if ( RES_FRMFMT == m_nFormatWhich ) {
+ const SwDoc* pDoc = rFormat.GetDoc();
+ if (pDoc->GetTableFrameFormats()->ContainsFormat(dynamic_cast<const SwFrameFormat&>(rFormat)))
+ {
+ // Table Format: save table position, table formats are volatile!
+ SwTable * pTable = SwIterator<SwTable,SwFormat>( rFormat ).First();
+ if ( pTable ) {
+ m_nNodeIndex = pTable->GetTabSortBoxes()[ 0 ]->GetSttNd()
+ ->FindTableNode()->GetIndex();
+ }
+ } else if (pDoc->GetSections().ContainsFormat(&rFormat)) {
+ m_nNodeIndex = rFormat.GetContent().GetContentIdx()->GetIndex();
+ } else if ( dynamic_cast< const SwTableBoxFormat* >( &rFormat ) != nullptr ) {
+ SwTableBox * pTableBox = SwIterator<SwTableBox,SwFormat>( rFormat ).First();
+ if ( pTableBox ) {
+ m_nNodeIndex = pTableBox->GetSttIdx();
+ }
+ }
+ }
+}
+
+SwUndoFormatAttr::~SwUndoFormatAttr()
+{
+}
+
+void SwUndoFormatAttr::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ // OD 2004-10-26 #i35443#
+ // Important note: <Undo(..)> also called by <ReDo(..)>
+
+ if (!m_pOldSet)
+ return;
+
+ SwFormat * pFormat = GetFormat(rContext.GetDoc());
+ if (!pFormat)
+ return;
+
+ // #i35443# - If anchor attribute has been successful
+ // restored, all other attributes are also restored.
+ // Thus, keep track of its restoration
+ bool bAnchorAttrRestored( false );
+ if ( SfxItemState::SET == m_pOldSet->GetItemState( RES_ANCHOR, false )) {
+ bAnchorAttrRestored = RestoreFlyAnchor(rContext);
+ if ( bAnchorAttrRestored ) {
+ // Anchor attribute successful restored.
+ // Thus, keep anchor position for redo
+ SaveFlyAnchor(pFormat);
+ } else {
+ // Anchor attribute not restored due to invalid anchor position.
+ // Thus, delete anchor attribute.
+ m_pOldSet->ClearItem( RES_ANCHOR );
+ }
+ }
+
+ if ( !bAnchorAttrRestored ) {
+ SwUndoFormatAttrHelper aTmp( *pFormat, m_bSaveDrawPt );
+ pFormat->SetFormatAttr( *m_pOldSet );
+ if ( aTmp.GetUndo() ) {
+ // transfer ownership of helper object's old set
+ m_pOldSet = std::move(aTmp.GetUndo()->m_pOldSet);
+ } else {
+ m_pOldSet->ClearItem();
+ }
+
+ if ( RES_FLYFRMFMT == m_nFormatWhich || RES_DRAWFRMFMT == m_nFormatWhich ) {
+ rContext.SetSelections(static_cast<SwFrameFormat*>(pFormat), nullptr);
+ }
+ }
+}
+
+// Check if it is still in Doc
+SwFormat* SwUndoFormatAttr::GetFormat( const SwDoc& rDoc )
+{
+ switch (m_nFormatWhich)
+ {
+ case RES_TXTFMTCOLL:
+ case RES_CONDTXTFMTCOLL:
+ return rDoc.FindTextFormatCollByName(m_sFormatName);
+
+ case RES_GRFFMTCOLL:
+ return SwDoc::FindFormatByName(*rDoc.GetGrfFormatColls(), m_sFormatName);
+
+ case RES_CHRFMT:
+ return rDoc.FindCharFormatByName(m_sFormatName);
+
+ case RES_FRMFMT:
+ if (m_nNodeIndex && (m_nNodeIndex < rDoc.GetNodes().Count()))
+ {
+ SwNode* pNd = rDoc.GetNodes()[m_nNodeIndex];
+ if (pNd->IsTableNode())
+ {
+ return static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat();
+ }
+ else if (pNd->IsSectionNode())
+ {
+ return static_cast<SwSectionNode*>(pNd)->GetSection().GetFormat();
+ }
+ else if (pNd->IsStartNode() && (SwTableBoxStartNode ==
+ static_cast<SwStartNode*>(pNd)->GetStartNodeType()))
+ {
+ SwTableNode* pTableNode = pNd->FindTableNode();
+ if (pTableNode)
+ {
+ SwTableBox* pBox = pTableNode->GetTable().GetTableBox(m_nNodeIndex);
+ if (pBox)
+ {
+ return pBox->GetFrameFormat();
+ }
+ }
+ }
+ }
+ [[fallthrough]];
+ case RES_DRAWFRMFMT:
+ case RES_FLYFRMFMT:
+ {
+ SwFormat * pFormat = SwDoc::FindFormatByName(*rDoc.GetSpzFrameFormats(), m_sFormatName);
+ if (pFormat)
+ return pFormat;
+ pFormat = SwDoc::FindFormatByName(*rDoc.GetFrameFormats(), m_sFormatName);
+ if (pFormat)
+ return pFormat;
+ }
+ break;
+ }
+
+ return nullptr;
+}
+
+void SwUndoFormatAttr::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ // #i35443# - Because the undo stores the attributes for
+ // redo, the same code as for <Undo(..)> can be applied for <Redo(..)>
+ UndoImpl(rContext);
+}
+
+void SwUndoFormatAttr::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ if (!m_pOldSet)
+ return;
+
+ SwDoc & rDoc(rContext.GetDoc());
+
+ SwFormat * pFormat = GetFormat(rDoc);
+ if (!pFormat)
+ return;
+
+ switch ( m_nFormatWhich ) {
+ case RES_GRFFMTCOLL: {
+ SwNoTextNode *const pNd =
+ rContext.GetRepeatPaM().GetNode().GetNoTextNode();
+ if( pNd ) {
+ rDoc.SetAttr( pFormat->GetAttrSet(), *pNd->GetFormatColl() );
+ }
+ }
+ break;
+
+ case RES_TXTFMTCOLL:
+ case RES_CONDTXTFMTCOLL:
+ {
+ SwTextNode *const pNd =
+ rContext.GetRepeatPaM().GetNode().GetTextNode();
+ if( pNd ) {
+ rDoc.SetAttr( pFormat->GetAttrSet(), *pNd->GetFormatColl() );
+ }
+ }
+ break;
+
+ case RES_FLYFRMFMT: {
+ // Check if the cursor is in a flying frame
+ // Steps: search in all FlyFrameFormats for the FlyContent attribute
+ // and validate if the cursor is in the respective section
+ SwFrameFormat *const pFly =
+ rContext.GetRepeatPaM().GetNode().GetFlyFormat();
+ if( pFly ) {
+ // Bug 43672: do not set all attributes!
+ if (SfxItemState::SET ==
+ pFormat->GetAttrSet().GetItemState( RES_CNTNT )) {
+ SfxItemSet aTmpSet( pFormat->GetAttrSet() );
+ aTmpSet.ClearItem( RES_CNTNT );
+ if( aTmpSet.Count() ) {
+ rDoc.SetAttr( aTmpSet, *pFly );
+ }
+ } else {
+ rDoc.SetAttr( pFormat->GetAttrSet(), *pFly );
+ }
+ }
+ break;
+ }
+ }
+}
+
+SwRewriter SwUndoFormatAttr::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_sFormatName);
+
+ return aRewriter;
+}
+
+void SwUndoFormatAttr::PutAttr( const SfxPoolItem& rItem, const SwDoc& rDoc )
+{
+ if (RES_CNTNT == rItem.Which())
+ {
+ return; // tdf#126017 never save SwNodeIndex, it will go stale
+ }
+ m_pOldSet->Put( rItem );
+ if ( RES_ANCHOR == rItem.Which() )
+ {
+ SwFormat * pFormat = GetFormat( rDoc );
+ SaveFlyAnchor( pFormat, m_bSaveDrawPt );
+ }
+}
+
+void SwUndoFormatAttr::SaveFlyAnchor( const SwFormat * pFormat, bool bSvDrwPt )
+{
+ // Format is valid, otherwise you would not reach this point here
+ if( bSvDrwPt ) {
+ if ( RES_DRAWFRMFMT == pFormat->Which() ) {
+ Point aPt( static_cast<const SwFrameFormat*>(pFormat)->FindSdrObject()
+ ->GetRelativePos() );
+ // store old value as attribute, to keep SwUndoFormatAttr small
+ m_pOldSet->Put( SwFormatFrameSize( SwFrameSize::Variable, aPt.X(), aPt.Y() ) );
+ }
+ }
+
+ const SwFormatAnchor& rAnchor =
+ m_pOldSet->Get( RES_ANCHOR, false );
+ if( !rAnchor.GetContentAnchor() )
+ return;
+
+ sal_Int32 nContent = 0;
+ switch( rAnchor.GetAnchorId() ) {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ nContent = rAnchor.GetContentAnchor()->nContent.GetIndex();
+ [[fallthrough]];
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ m_nNodeIndex = rAnchor.GetContentAnchor()->nNode.GetIndex();
+ break;
+ default:
+ return;
+ }
+
+ SwFormatAnchor aAnchor( rAnchor.GetAnchorId(), nContent );
+ m_pOldSet->Put( aAnchor );
+}
+
+// #i35443# - Add return value, type <bool>.
+// Return value indicates, if anchor attribute is restored.
+// Note: If anchor attribute is restored, all other existing attributes
+// are also restored.
+bool SwUndoFormatAttr::RestoreFlyAnchor(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwFrameFormat* pFrameFormat = static_cast<SwFrameFormat*>( GetFormat( *pDoc ) );
+ const SwFormatAnchor& rAnchor =
+ m_pOldSet->Get( RES_ANCHOR, false );
+
+ SwFormatAnchor aNewAnchor( rAnchor.GetAnchorId() );
+ if (RndStdIds::FLY_AT_PAGE != rAnchor.GetAnchorId()) {
+ SwNode* pNd = pDoc->GetNodes()[ m_nNodeIndex ];
+
+ if ( (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId())
+ ? ( !pNd->IsStartNode() || (SwFlyStartNode !=
+ static_cast<SwStartNode*>(pNd)->GetStartNodeType()) )
+ : !pNd->IsTextNode() ) {
+ // #i35443# - invalid position.
+ // Thus, anchor attribute not restored
+ return false;
+ }
+
+ SwPosition aPos( *pNd );
+ if ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) {
+ aPos.nContent.Assign( static_cast<SwTextNode*>(pNd), rAnchor.GetPageNum() );
+ if ( aPos.nContent.GetIndex() > pNd->GetTextNode()->GetText().getLength()) {
+ // #i35443# - invalid position.
+ // Thus, anchor attribute not restored
+ return false;
+ }
+ }
+ aNewAnchor.SetAnchor( &aPos );
+ } else
+ aNewAnchor.SetPageNum( rAnchor.GetPageNum() );
+
+ Point aDrawSavePt, aDrawOldPt;
+ if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) {
+ if( RES_DRAWFRMFMT == pFrameFormat->Which() ) {
+ // get the old cached value
+ const SwFormatFrameSize& rOldSize = m_pOldSet->Get( RES_FRM_SIZE );
+ aDrawSavePt.setX( rOldSize.GetWidth() );
+ aDrawSavePt.setY( rOldSize.GetHeight() );
+ m_pOldSet->ClearItem( RES_FRM_SIZE );
+
+ // write the current value into cache
+ aDrawOldPt = pFrameFormat->FindSdrObject()->GetRelativePos();
+ } else {
+ pFrameFormat->DelFrames(); // delete Frames
+ }
+ }
+
+ const SwFormatAnchor &rOldAnch = pFrameFormat->GetAnchor();
+ // #i54336#
+ // Consider case, that as-character anchored object has moved its anchor position.
+ if (RndStdIds::FLY_AS_CHAR == rOldAnch.GetAnchorId()) {
+ // With InContents it's tricky: the text attribute needs to be deleted.
+ // Unfortunately, this not only destroys the Frames but also the format.
+ // To prevent that, first detach the connection between attribute and
+ // format.
+ const SwPosition *pPos = rOldAnch.GetContentAnchor();
+ SwTextNode *pTextNode = static_cast<SwTextNode*>(&pPos->nNode.GetNode());
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = pPos->nContent.GetIndex();
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
+ assert(pHint && "Missing Hint.");
+ OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT,
+ "Missing FlyInCnt-Hint." );
+ OSL_ENSURE( pHint->GetFlyCnt().GetFrameFormat() == pFrameFormat,
+ "Wrong TextFlyCnt-Hint." );
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
+
+ // Connection is now detached, therefore the attribute can be deleted
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
+ }
+
+ {
+ m_pOldSet->Put( aNewAnchor );
+ SwUndoFormatAttrHelper aTmp( *pFrameFormat, m_bSaveDrawPt );
+ pFrameFormat->SetFormatAttr( *m_pOldSet );
+ if ( aTmp.GetUndo() ) {
+ m_nNodeIndex = aTmp.GetUndo()->m_nNodeIndex;
+ // transfer ownership of helper object's old set
+ m_pOldSet = std::move(aTmp.GetUndo()->m_pOldSet);
+ } else {
+ m_pOldSet->ClearItem();
+ }
+ }
+
+ if ( RES_DRAWFRMFMT == pFrameFormat->Which() )
+ {
+ // The Draw model also prepared an Undo object for its right positioning
+ // which unfortunately is relative. Therefore block here a position
+ // change of the Contact object by setting the anchor.
+ pFrameFormat->CallSwClientNotify(sw::RestoreFlyAnchorHint(aDrawSavePt));
+ // cache the old value again
+ m_pOldSet->Put(SwFormatFrameSize(SwFrameSize::Variable, aDrawOldPt.X(), aDrawOldPt.Y()));
+ }
+
+ if (RndStdIds::FLY_AS_CHAR == aNewAnchor.GetAnchorId()) {
+ const SwPosition* pPos = aNewAnchor.GetContentAnchor();
+ SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "no Text Node at position." );
+ SwFormatFlyCnt aFormat( pFrameFormat );
+ pTextNd->InsertItem( aFormat, pPos->nContent.GetIndex(), 0 );
+ }
+
+ if (RES_DRAWFRMFMT != pFrameFormat->Which())
+ pFrameFormat->MakeFrames();
+ else
+ {
+ pFrameFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::POST_RESTORE_FLY_ANCHOR));
+ }
+
+ rContext.SetSelections(pFrameFormat, nullptr);
+
+ // #i35443# - anchor attribute restored.
+ return true;
+}
+
+SwUndoFormatResetAttr::SwUndoFormatResetAttr( SwFormat& rChangedFormat,
+ const sal_uInt16 nWhichId )
+ : SwUndo( SwUndoId::RESETATTR, rChangedFormat.GetDoc() )
+ , m_pChangedFormat( &rChangedFormat )
+ , m_nWhichId( nWhichId )
+{
+ const SfxPoolItem* pItem = nullptr;
+ if (rChangedFormat.GetItemState(nWhichId, false, &pItem ) == SfxItemState::SET && pItem) {
+ m_pOldItem.reset( pItem->Clone() );
+ }
+}
+
+SwUndoFormatResetAttr::~SwUndoFormatResetAttr()
+{
+}
+
+void SwUndoFormatResetAttr::UndoImpl(::sw::UndoRedoContext &)
+{
+ if (m_pOldItem)
+ {
+ m_pChangedFormat->SetFormatAttr( *m_pOldItem );
+ }
+}
+
+void SwUndoFormatResetAttr::RedoImpl(::sw::UndoRedoContext &)
+{
+ if (m_pOldItem)
+ {
+ m_pChangedFormat->ResetFormatAttr( m_nWhichId );
+ }
+}
+
+SwUndoResetAttr::SwUndoResetAttr( const SwPaM& rRange, sal_uInt16 nFormatId )
+ : SwUndo( SwUndoId::RESETATTR, rRange.GetDoc() ), SwUndRng( rRange )
+ , m_pHistory( new SwHistory )
+ , m_nFormatId( nFormatId )
+{
+}
+
+SwUndoResetAttr::SwUndoResetAttr( const SwPosition& rPos, sal_uInt16 nFormatId )
+ : SwUndo( SwUndoId::RESETATTR, rPos.GetDoc() )
+ , m_pHistory( new SwHistory )
+ , m_nFormatId( nFormatId )
+{
+ m_nSttNode = m_nEndNode = rPos.nNode.GetIndex();
+ m_nSttContent = m_nEndContent = rPos.nContent.GetIndex();
+}
+
+SwUndoResetAttr::~SwUndoResetAttr()
+{
+}
+
+void SwUndoResetAttr::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ // reset old values
+ SwDoc & rDoc = rContext.GetDoc();
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ if ((RES_CONDTXTFMTCOLL == m_nFormatId) &&
+ (m_nSttNode == m_nEndNode) && (m_nSttContent == m_nEndContent)) {
+ SwTextNode* pTNd = rDoc.GetNodes()[ m_nSttNode ]->GetTextNode();
+ if( pTNd ) {
+ SwIndex aIdx( pTNd, m_nSttContent );
+ pTNd->DontExpandFormat( aIdx, false );
+ }
+ }
+
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoResetAttr::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+
+ switch ( m_nFormatId ) {
+ case RES_CHRFMT:
+ rDoc.RstTextAttrs(rPam);
+ break;
+ case RES_TXTFMTCOLL:
+ rDoc.ResetAttrs(rPam, false, m_Ids );
+ break;
+ case RES_CONDTXTFMTCOLL:
+ rDoc.ResetAttrs(rPam, true, m_Ids );
+
+ break;
+ case RES_TXTATR_TOXMARK:
+ // special treatment for TOXMarks
+ {
+ SwTOXMarks aArr;
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode );
+ SwPosition aPos( aIdx, SwIndex( aIdx.GetNode().GetContentNode(),
+ m_nSttContent ));
+
+ sal_uInt16 nCnt = SwDoc::GetCurTOXMark( aPos, aArr );
+ if( nCnt ) {
+ if( 1 < nCnt ) {
+ // search for the right one
+ SwHistoryHint* pHHint = (GetHistory())[ 0 ];
+ if( pHHint && HSTRY_SETTOXMARKHNT == pHHint->Which() ) {
+ while( nCnt ) {
+ if ( static_cast<SwHistorySetTOXMark*>(pHHint)
+ ->IsEqual( *aArr[ --nCnt ] ) ) {
+ ++nCnt;
+ break;
+ }
+ }
+ } else
+ nCnt = 0;
+ }
+ // found one, thus delete it
+ if( nCnt-- ) {
+ rDoc.DeleteTOXMark( aArr[ nCnt ] );
+ }
+ }
+ }
+ break;
+ }
+}
+
+void SwUndoResetAttr::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ if (m_nFormatId < RES_FMT_BEGIN) {
+ return;
+ }
+
+ switch ( m_nFormatId ) {
+ case RES_CHRFMT:
+ rContext.GetDoc().RstTextAttrs(rContext.GetRepeatPaM());
+ break;
+ case RES_TXTFMTCOLL:
+ rContext.GetDoc().ResetAttrs(rContext.GetRepeatPaM(), false, m_Ids);
+ break;
+ case RES_CONDTXTFMTCOLL:
+ rContext.GetDoc().ResetAttrs(rContext.GetRepeatPaM(), true, m_Ids);
+ break;
+ }
+}
+
+void SwUndoResetAttr::SetAttrs( const std::set<sal_uInt16> &rAttrs )
+{
+ m_Ids = rAttrs;
+}
+
+SwUndoAttr::SwUndoAttr( const SwPaM& rRange, const SfxPoolItem& rAttr,
+ const SetAttrMode nFlags )
+ : SwUndo( SwUndoId::INSATTR, rRange.GetDoc() ), SwUndRng( rRange )
+ , m_AttrSet( rRange.GetDoc()->GetAttrPool(), {{rAttr.Which(), rAttr.Which()}} )
+ , m_pHistory( new SwHistory )
+ , m_nNodeIndex( ULONG_MAX )
+ , m_nInsertFlags( nFlags )
+{
+ m_AttrSet.Put( rAttr );
+
+ // Save character style as a style name, not as a reference
+ const SfxPoolItem* pItem = m_AttrSet.GetItem(RES_TXTATR_CHARFMT);
+ if (pItem)
+ {
+ uno::Any aValue;
+ pItem->QueryValue(aValue, RES_TXTATR_CHARFMT);
+ aValue >>= m_aChrFormatName;
+ }
+}
+
+SwUndoAttr::SwUndoAttr( const SwPaM& rRange, const SfxItemSet& rSet,
+ const SetAttrMode nFlags )
+ : SwUndo( SwUndoId::INSATTR, rRange.GetDoc() ), SwUndRng( rRange )
+ , m_AttrSet( rSet )
+ , m_pHistory( new SwHistory )
+ , m_nNodeIndex( ULONG_MAX )
+ , m_nInsertFlags( nFlags )
+{
+ // Save character style as a style name, not as a reference
+ const SfxPoolItem* pItem = m_AttrSet.GetItem(RES_TXTATR_CHARFMT);
+ if (pItem)
+ {
+ uno::Any aValue;
+ pItem->QueryValue(aValue, RES_TXTATR_CHARFMT);
+ aValue >>= m_aChrFormatName;
+ }
+}
+
+SwUndoAttr::~SwUndoAttr()
+{
+}
+
+void SwUndoAttr::SaveRedlineData( const SwPaM& rPam, bool bIsContent )
+{
+ SwDoc* pDoc = rPam.GetDoc();
+ if ( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) {
+ m_pRedlineData.reset( new SwRedlineData( bIsContent
+ ? RedlineType::Insert
+ : RedlineType::Format,
+ pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ }
+
+ m_pRedlineSaveData.reset( new SwRedlineSaveDatas );
+ if ( !FillSaveDataForFormat( rPam, *m_pRedlineSaveData ))
+ m_pRedlineSaveData.reset();
+
+ SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() );
+ if ( bIsContent ) {
+ m_nNodeIndex = rPam.GetPoint()->nNode.GetIndex();
+ }
+}
+
+void SwUndoAttr::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ RemoveIdx( *pDoc );
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ) {
+ SwPaM aPam(pDoc->GetNodes().GetEndOfContent());
+ if ( ULONG_MAX != m_nNodeIndex ) {
+ aPam.DeleteMark();
+ aPam.GetPoint()->nNode = m_nNodeIndex;
+ aPam.GetPoint()->nContent.Assign( aPam.GetContentNode(), m_nSttContent );
+ aPam.SetMark();
+ ++aPam.GetPoint()->nContent;
+ pDoc->getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Any);
+ } else {
+ // remove all format redlines, will be recreated if needed
+ SetPaM(aPam);
+ pDoc->getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Format);
+ if (m_pRedlineSaveData)
+ {
+ SetSaveData( *pDoc, *m_pRedlineSaveData );
+ }
+ }
+ }
+
+ const bool bToLast = (1 == m_AttrSet.Count())
+ && (RES_TXTATR_FIELD <= *m_AttrSet.GetRanges())
+ && (*m_AttrSet.GetRanges() <= RES_TXTATR_ANNOTATION);
+
+ // restore old values
+ m_pHistory->TmpRollback( pDoc, 0, !bToLast );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ // set cursor onto Undo area
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoAttr::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // RefMarks are not repeat capable
+ if ( SfxItemState::SET != m_AttrSet.GetItemState( RES_TXTATR_REFMARK, false ) ) {
+ rContext.GetDoc().getIDocumentContentOperations().InsertItemSet( rContext.GetRepeatPaM(),
+ m_AttrSet, m_nInsertFlags );
+ } else if ( 1 < m_AttrSet.Count() ) {
+ SfxItemSet aTmpSet( m_AttrSet );
+ aTmpSet.ClearItem( RES_TXTATR_REFMARK );
+ rContext.GetDoc().getIDocumentContentOperations().InsertItemSet( rContext.GetRepeatPaM(),
+ aTmpSet, m_nInsertFlags );
+ }
+}
+
+void SwUndoAttr::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+
+ // Restore pointer to char format from name
+ if (!m_aChrFormatName.isEmpty())
+ {
+ SwCharFormat* pCharFormat = rDoc.FindCharFormatByName(m_aChrFormatName);
+ if (pCharFormat)
+ {
+ SwFormatCharFormat aFormat(pCharFormat);
+ m_AttrSet.Put(aFormat);
+ }
+ }
+
+ if ( m_pRedlineData &&
+ IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ) {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags::Ignore );
+ rDoc.getIDocumentContentOperations().InsertItemSet( rPam, m_AttrSet, m_nInsertFlags );
+
+ if ( ULONG_MAX != m_nNodeIndex ) {
+ rPam.SetMark();
+ if ( rPam.Move( fnMoveBackward ) ) {
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ),
+ true);
+ }
+ rPam.DeleteMark();
+ } else {
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ), true);
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ } else {
+ rDoc.getIDocumentContentOperations().InsertItemSet( rPam, m_AttrSet, m_nInsertFlags );
+ }
+}
+
+void SwUndoAttr::RemoveIdx( SwDoc& rDoc )
+{
+ if ( SfxItemState::SET != m_AttrSet.GetItemState( RES_TXTATR_FTN, false ))
+ return ;
+
+ SwNodes& rNds = rDoc.GetNodes();
+ for ( sal_uInt16 n = 0; n < m_pHistory->Count(); ++n ) {
+ sal_Int32 nContent = 0;
+ sal_uLong nNode = 0;
+ SwHistoryHint* pHstHint = (*m_pHistory)[ n ];
+ switch ( pHstHint->Which() ) {
+ case HSTRY_RESETTXTHNT: {
+ SwHistoryResetText * pHistoryHint
+ = static_cast<SwHistoryResetText*>(pHstHint);
+ if ( RES_TXTATR_FTN == pHistoryHint->GetWhich() ) {
+ nNode = pHistoryHint->GetNode();
+ nContent = pHistoryHint->GetContent();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if( nNode ) {
+ SwTextNode* pTextNd = rNds[ nNode ]->GetTextNode();
+ if( pTextNd ) {
+ SwTextAttr *const pTextHt =
+ pTextNd->GetTextAttrForCharAt(nContent, RES_TXTATR_FTN);
+ if( pTextHt ) {
+ // ok, so get values
+ SwTextFootnote* pFootnote = static_cast<SwTextFootnote*>(pTextHt);
+ RemoveIdxFromSection( rDoc, pFootnote->GetStartNode()->GetIndex() );
+ return ;
+ }
+ }
+ }
+ }
+}
+
+SwUndoDefaultAttr::SwUndoDefaultAttr( const SfxItemSet& rSet, const SwDoc* pDoc )
+ : SwUndo( SwUndoId::SETDEFTATTR, pDoc )
+{
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET == rSet.GetItemState( RES_PARATR_TABSTOP, false, &pItem ) ) {
+ // store separately, because it may change!
+ m_pTabStop.reset( static_cast<SvxTabStopItem*>(pItem->Clone()) );
+ if ( 1 != rSet.Count() ) { // are there more attributes?
+ m_pOldSet.reset( new SfxItemSet( rSet ) );
+ }
+ } else {
+ m_pOldSet.reset( new SfxItemSet( rSet ) );
+ }
+}
+
+SwUndoDefaultAttr::~SwUndoDefaultAttr()
+{
+}
+
+void SwUndoDefaultAttr::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if (m_pOldSet)
+ {
+ SwUndoFormatAttrHelper aTmp(
+ *rDoc.GetDfltTextFormatColl() );
+ rDoc.SetDefault( *m_pOldSet );
+ m_pOldSet.reset();
+ if ( aTmp.GetUndo() ) {
+ // transfer ownership of helper object's old set
+ m_pOldSet = std::move(aTmp.GetUndo()->m_pOldSet);
+ }
+ }
+ if (m_pTabStop)
+ {
+ std::unique_ptr<SvxTabStopItem> pOld(rDoc.GetDefault(RES_PARATR_TABSTOP).Clone());
+ rDoc.SetDefault( *m_pTabStop );
+ m_pTabStop = std::move( pOld );
+ }
+}
+
+void SwUndoDefaultAttr::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ UndoImpl(rContext);
+}
+
+SwUndoMoveLeftMargin::SwUndoMoveLeftMargin(
+ const SwPaM& rPam, bool bFlag, bool bMod )
+ : SwUndo( bFlag ? SwUndoId::INC_LEFTMARGIN : SwUndoId::DEC_LEFTMARGIN, rPam.GetDoc() )
+ , SwUndRng( rPam )
+ , m_pHistory( new SwHistory )
+ , m_bModulus( bMod )
+{
+}
+
+SwUndoMoveLeftMargin::~SwUndoMoveLeftMargin()
+{
+}
+
+void SwUndoMoveLeftMargin::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ // restore old values
+ m_pHistory->TmpRollback( & rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoMoveLeftMargin::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+
+ rDoc.MoveLeftMargin( rPam,
+ GetId() == SwUndoId::INC_LEFTMARGIN, m_bModulus,
+ rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
+}
+
+void SwUndoMoveLeftMargin::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ rDoc.MoveLeftMargin(rContext.GetRepeatPaM(), GetId() == SwUndoId::INC_LEFTMARGIN,
+ m_bModulus, rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
+}
+
+SwUndoChangeFootNote::SwUndoChangeFootNote(
+ const SwPaM& rRange, const OUString& rText,
+ bool const bIsEndNote)
+ : SwUndo( SwUndoId::CHGFTN, rRange.GetDoc() ), SwUndRng( rRange )
+ , m_pHistory( new SwHistory() )
+ , m_Text( rText )
+ , m_bEndNote( bIsEndNote )
+{
+}
+
+SwUndoChangeFootNote::~SwUndoChangeFootNote()
+{
+}
+
+void SwUndoChangeFootNote::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ rDoc.GetFootnoteIdxs().UpdateAllFootnote();
+
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoChangeFootNote::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc( rContext.GetDoc() );
+ SwPaM & rPaM = AddUndoRedoPaM(rContext);
+ rDoc.SetCurFootnote(rPaM, m_Text, m_bEndNote);
+ SetPaM(rPaM);
+}
+
+void SwUndoChangeFootNote::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ rDoc.SetCurFootnote(rContext.GetRepeatPaM(), m_Text, m_bEndNote);
+}
+
+SwUndoFootNoteInfo::SwUndoFootNoteInfo( const SwFootnoteInfo &rInfo, const SwDoc* pDoc )
+ : SwUndo( SwUndoId::FTNINFO, pDoc )
+ , m_pFootNoteInfo( new SwFootnoteInfo( rInfo ) )
+{
+}
+
+SwUndoFootNoteInfo::~SwUndoFootNoteInfo()
+{
+}
+
+void SwUndoFootNoteInfo::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwFootnoteInfo *pInf = new SwFootnoteInfo( rDoc.GetFootnoteInfo() );
+ rDoc.SetFootnoteInfo( *m_pFootNoteInfo );
+ m_pFootNoteInfo.reset( pInf );
+}
+
+void SwUndoFootNoteInfo::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwFootnoteInfo *pInf = new SwFootnoteInfo( rDoc.GetFootnoteInfo() );
+ rDoc.SetFootnoteInfo( *m_pFootNoteInfo );
+ m_pFootNoteInfo.reset( pInf );
+}
+
+SwUndoEndNoteInfo::SwUndoEndNoteInfo( const SwEndNoteInfo &rInfo, const SwDoc* pDoc )
+ : SwUndo( SwUndoId::FTNINFO, pDoc )
+ , m_pEndNoteInfo( new SwEndNoteInfo( rInfo ) )
+{
+}
+
+SwUndoEndNoteInfo::~SwUndoEndNoteInfo()
+{
+}
+
+void SwUndoEndNoteInfo::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwEndNoteInfo *pInf = new SwEndNoteInfo( rDoc.GetEndNoteInfo() );
+ rDoc.SetEndNoteInfo( *m_pEndNoteInfo );
+ m_pEndNoteInfo.reset( pInf );
+}
+
+void SwUndoEndNoteInfo::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwEndNoteInfo *pInf = new SwEndNoteInfo( rDoc.GetEndNoteInfo() );
+ rDoc.SetEndNoteInfo( *m_pEndNoteInfo );
+ m_pEndNoteInfo.reset( pInf );
+}
+
+SwUndoDontExpandFormat::SwUndoDontExpandFormat( const SwPosition& rPos )
+ : SwUndo( SwUndoId::DONTEXPAND, rPos.GetDoc() )
+ , m_nNodeIndex( rPos.nNode.GetIndex() )
+ , m_nContentIndex( rPos.nContent.GetIndex() )
+{
+}
+
+void SwUndoDontExpandFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ SwPosition& rPos = *pPam->GetPoint();
+ rPos.nNode = m_nNodeIndex;
+ rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), m_nContentIndex);
+ pDoc->DontExpandFormat( rPos, false );
+}
+
+void SwUndoDontExpandFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ SwPosition& rPos = *pPam->GetPoint();
+ rPos.nNode = m_nNodeIndex;
+ rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), m_nContentIndex);
+ pDoc->DontExpandFormat( rPos );
+}
+
+void SwUndoDontExpandFormat::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM & rPam = rContext.GetRepeatPaM();
+ SwDoc & rDoc = rContext.GetDoc();
+ rDoc.DontExpandFormat( *rPam.GetPoint() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unbkmk.cxx b/sw/source/core/undo/unbkmk.cxx
new file mode 100644
index 000000000..16d45a7b7
--- /dev/null
+++ b/sw/source/core/undo/unbkmk.cxx
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoBookmark.hxx>
+
+#include <strings.hrc>
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+
+#include <UndoCore.hxx>
+#include <IMark.hxx>
+#include <rolbck.hxx>
+
+#include <SwRewriter.hxx>
+
+SwUndoBookmark::SwUndoBookmark( SwUndoId nUndoId,
+ const ::sw::mark::IMark& rBkmk )
+ : SwUndo( nUndoId, rBkmk.GetMarkPos().GetDoc() )
+ , m_pHistoryBookmark(new SwHistoryBookmark(rBkmk, true, rBkmk.IsExpanded()))
+{
+}
+
+SwUndoBookmark::~SwUndoBookmark()
+{
+}
+
+void SwUndoBookmark::SetInDoc( SwDoc* pDoc )
+{
+ m_pHistoryBookmark->SetInDoc( pDoc, false );
+}
+
+void SwUndoBookmark::ResetInDoc( SwDoc* pDoc )
+{
+ IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess();
+ for ( IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getAllMarksBegin();
+ ppBkmk != pMarkAccess->getAllMarksEnd();
+ ++ppBkmk )
+ {
+ if ( m_pHistoryBookmark->IsEqualBookmark( **ppBkmk ) )
+ {
+ pMarkAccess->deleteMark( ppBkmk );
+ break;
+ }
+ }
+}
+
+SwRewriter SwUndoBookmark::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, m_pHistoryBookmark->GetName());
+
+ return aResult;
+}
+
+SwUndoInsBookmark::SwUndoInsBookmark( const ::sw::mark::IMark& rBkmk )
+ : SwUndoBookmark( SwUndoId::INSBOOKMARK, rBkmk )
+{
+}
+
+void SwUndoInsBookmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ ResetInDoc( &rContext.GetDoc() );
+}
+
+void SwUndoInsBookmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SetInDoc( &rContext.GetDoc() );
+}
+
+SwUndoDeleteBookmark::SwUndoDeleteBookmark( const ::sw::mark::IMark& rBkmk )
+ : SwUndoBookmark( SwUndoId::DELBOOKMARK, rBkmk )
+{
+}
+
+void SwUndoDeleteBookmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SetInDoc( &rContext.GetDoc() );
+}
+
+void SwUndoDeleteBookmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ ResetInDoc( &rContext.GetDoc() );
+}
+
+SwUndoRenameBookmark::SwUndoRenameBookmark( const OUString& rOldName, const OUString& rNewName, const SwDoc* pDoc )
+ : SwUndo( SwUndoId::BOOKMARK_RENAME, pDoc )
+ , m_sOldName( rOldName )
+ , m_sNewName( rNewName )
+{
+}
+
+SwUndoRenameBookmark::~SwUndoRenameBookmark()
+{
+}
+
+static OUString lcl_QuoteName(const OUString& rName)
+{
+ static const OUString sStart = SwResId(STR_START_QUOTE);
+ static const OUString sEnd = SwResId(STR_END_QUOTE);
+ return sStart + rName + sEnd;
+}
+
+SwRewriter SwUndoRenameBookmark::GetRewriter() const
+{
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, lcl_QuoteName(m_sOldName));
+ aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
+ aRewriter.AddRule(UndoArg3, lcl_QuoteName(m_sNewName));
+ return aRewriter;
+}
+
+void SwUndoRenameBookmark::Rename(::sw::UndoRedoContext const & rContext, const OUString& sFrom, const OUString& sTo)
+{
+ IDocumentMarkAccess* const pMarkAccess = rContext.GetDoc().getIDocumentMarkAccess();
+ IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findMark(sFrom);
+ if (ppBkmk != pMarkAccess->getAllMarksEnd())
+ {
+ pMarkAccess->renameMark( *ppBkmk, sTo );
+ }
+}
+
+void SwUndoRenameBookmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ Rename(rContext, m_sNewName, m_sOldName);
+}
+
+void SwUndoRenameBookmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ Rename(rContext, m_sOldName, m_sNewName);
+}
+
+SwUndoInsNoTextFieldmark::SwUndoInsNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark)
+ : SwUndo(SwUndoId::INSERT, rFieldmark.GetMarkPos().GetDoc())
+ , m_pHistoryNoTextFieldmark(new SwHistoryNoTextFieldmark(rFieldmark))
+{
+}
+
+void SwUndoInsNoTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryNoTextFieldmark->ResetInDoc(&rContext.GetDoc());
+}
+
+void SwUndoInsNoTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryNoTextFieldmark->SetInDoc(&rContext.GetDoc(), false);
+}
+
+SwUndoDelNoTextFieldmark::SwUndoDelNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark)
+ : SwUndo(SwUndoId::DELETE, rFieldmark.GetMarkPos().GetDoc())
+ , m_pHistoryNoTextFieldmark(new SwHistoryNoTextFieldmark(rFieldmark))
+{
+}
+
+SwUndoDelNoTextFieldmark::~SwUndoDelNoTextFieldmark() = default;
+
+void SwUndoDelNoTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryNoTextFieldmark->SetInDoc(&rContext.GetDoc(), false);
+}
+
+void SwUndoDelNoTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryNoTextFieldmark->ResetInDoc(&rContext.GetDoc());
+}
+
+SwUndoInsTextFieldmark::SwUndoInsTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark)
+ : SwUndo(SwUndoId::INSERT, rFieldmark.GetMarkPos().GetDoc())
+ , m_pHistoryTextFieldmark(new SwHistoryTextFieldmark(rFieldmark))
+{
+}
+
+void SwUndoInsTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryTextFieldmark->ResetInDoc(&rContext.GetDoc());
+}
+
+void SwUndoInsTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryTextFieldmark->SetInDoc(&rContext.GetDoc(), false);
+}
+
+SwUndoDelTextFieldmark::SwUndoDelTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark)
+ : SwUndo(SwUndoId::DELETE, rFieldmark.GetMarkPos().GetDoc())
+ , m_pHistoryTextFieldmark(new SwHistoryTextFieldmark(rFieldmark))
+{
+}
+
+SwUndoDelTextFieldmark::~SwUndoDelTextFieldmark() = default;
+
+void SwUndoDelTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryTextFieldmark->SetInDoc(&rContext.GetDoc(), false);
+}
+
+void SwUndoDelTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryTextFieldmark->ResetInDoc(&rContext.GetDoc());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undel.cxx b/sw/source/core/undo/undel.cxx
new file mode 100644
index 000000000..e1e519fa2
--- /dev/null
+++ b/sw/source/core/undo/undel.cxx
@@ -0,0 +1,1317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoDelete.hxx>
+#include <hintids.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <unotools/charclass.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <doc.hxx>
+#include <UndoManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <swtable.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <poolfmt.hxx>
+#include <mvsave.hxx>
+#include <docary.hxx>
+#include <frmtool.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <strings.hrc>
+#include <frameformats.hxx>
+#include <vector>
+
+// DELETE
+/* lcl_MakeAutoFrames has to call MakeFrames for objects bounded "AtChar"
+ ( == AUTO ), if the anchor frame has be moved via MoveNodes(..) and
+ DelFrames(..)
+*/
+static void lcl_MakeAutoFrames( const SwFrameFormats& rSpzArr, sal_uLong nMovedIndex )
+{
+ for( size_t n = 0; n < rSpzArr.size(); ++n )
+ {
+ SwFrameFormat * pFormat = rSpzArr[n];
+ const SwFormatAnchor* pAnchor = &pFormat->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ const SwPosition* pAPos = pAnchor->GetContentAnchor();
+ if( pAPos && nMovedIndex == pAPos->nNode.GetIndex() )
+ pFormat->MakeFrames();
+ }
+ }
+}
+
+static SwTextNode * FindFirstAndNextNode(SwDoc & rDoc, SwUndRng const& rRange,
+ SwRedlineSaveDatas const& rRedlineSaveData,
+ SwTextNode *& o_rpFirstMergedDeletedTextNode)
+{
+ // redlines are corrected now to exclude the deleted node
+ assert(rRange.m_nEndContent == 0);
+ sal_uLong nEndOfRedline = 0;
+ for (size_t i = 0; i < rRedlineSaveData.size(); ++i)
+ {
+ auto const& rRedline(rRedlineSaveData[i]);
+ if (rRedline.m_nSttNode <= rRange.m_nSttNode
+ && rRedline.m_nSttNode < rRange.m_nEndNode
+ && rRange.m_nEndNode <= rRedline.m_nEndNode
+ && rRedline.GetType() == RedlineType::Delete)
+ {
+ nEndOfRedline = rRedline.m_nEndNode;
+ o_rpFirstMergedDeletedTextNode = rDoc.GetNodes()[rRedline.m_nSttNode]->GetTextNode();
+ assert(rRange.m_nSttNode == rRange.m_nEndNode - 1); // otherwise this needs to iterate more RL to find the first node?
+ break;
+ }
+ }
+ if (nEndOfRedline)
+ {
+ assert(o_rpFirstMergedDeletedTextNode);
+ SwTextNode * pNextNode(nullptr);
+ for (sal_uLong i = rRange.m_nEndNode; /* i <= nEndOfRedline */; ++i)
+ {
+ SwNode *const pNode(rDoc.GetNodes()[i]);
+ assert(!pNode->IsEndNode()); // cannot be both leaving section here *and* overlapping redline
+ if (pNode->IsStartNode())
+ {
+ i = pNode->EndOfSectionIndex(); // will be incremented again
+ }
+ else if (pNode->IsTextNode())
+ {
+ pNextNode = pNode->GetTextNode();
+ break;
+ }
+ }
+ assert(pNextNode);
+ return pNextNode;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+static void DelFullParaMoveFrames(SwDoc & rDoc, SwUndRng const& rRange,
+ SwRedlineSaveDatas const& rRedlineSaveData)
+{
+ SwTextNode * pFirstMergedDeletedTextNode(nullptr);
+ SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, rRange,
+ rRedlineSaveData, pFirstMergedDeletedTextNode);
+ if (pNextNode)
+ {
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pFirstMergedDeletedTextNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->IsHideRedlines())
+ {
+ assert(pFrame->GetMergedPara());
+ assert(pFrame->GetMergedPara()->pFirstNode == pFirstMergedDeletedTextNode);
+ assert(pNextNode->GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex());
+ frames.push_back(pFrame);
+ }
+ }
+ for (SwTextFrame *const pFrame : frames)
+ {
+ // sw_redlinehide: don't need FrameMode::Existing here
+ // because everything from pNextNode onwards is already
+ // correctly hidden
+ pFrame->RegisterToNode(*pNextNode, true);
+ }
+ }
+}
+
+// SwUndoDelete has to perform a deletion and to record anything that is needed
+// to restore the situation before the deletion. Unfortunately a part of the
+// deletion will be done after calling this Ctor, this has to be kept in mind!
+// In this Ctor only the complete paragraphs will be deleted, the joining of
+// the first and last paragraph of the selection will be handled outside this
+// function.
+// Here are the main steps of the function:
+// 1. Deletion/recording of content indices of the selection: footnotes, fly
+// frames and bookmarks
+// Step 1 could shift all nodes by deletion of footnotes => nNdDiff will be set.
+// 2. If the paragraph where the selection ends, is the last content of a
+// section so that this section becomes empty when the paragraphs will be
+// joined we have to do some smart actions ;-) The paragraph will be moved
+// outside the section and replaced by a dummy text node, the complete
+// section will be deleted in step 3. The difference between replacement
+// dummy and original is nReplacementDummy.
+// 3. Moving complete selected nodes into the UndoArray. Before this happens the
+// selection has to be extended if there are sections which would become
+// empty otherwise. BTW: sections will be moved into the UndoArray if they
+// are complete part of the selection. Sections starting or ending outside
+// of the selection will not be removed from the DocNodeArray even they got
+// a "dummy"-copy in the UndoArray.
+// 4. We have to anticipate the joining of the two paragraphs if the start
+// paragraph is inside a section and the end paragraph not. Then we have to
+// move the paragraph into this section and to record this in nSectDiff.
+SwUndoDelete::SwUndoDelete(
+ SwPaM& rPam,
+ bool bFullPara,
+ bool bCalledByTableCpy )
+ : SwUndo(SwUndoId::DELETE, rPam.GetDoc()),
+ SwUndRng( rPam ),
+ m_nNode(0),
+ m_nNdDiff(0),
+ m_nSectDiff(0),
+ m_nReplaceDummy(0),
+ m_nSetPos(0),
+ m_bGroup( false ),
+ m_bBackSp( false ),
+ m_bJoinNext( false ),
+ m_bTableDelLastNd( false ),
+ // bFullPara is set e.g. if an empty paragraph before a table is deleted
+ m_bDelFullPara( bFullPara ),
+ m_bResetPgDesc( false ),
+ m_bResetPgBrk( false ),
+ m_bFromTableCopy( bCalledByTableCpy )
+{
+
+ m_bCacheComment = false;
+
+ SwDoc * pDoc = rPam.GetDoc();
+
+ if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ m_pRedlSaveData.reset(new SwRedlineSaveDatas);
+ if( !FillSaveData( rPam, *m_pRedlSaveData ))
+ {
+ m_pRedlSaveData.reset();
+ }
+ }
+
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+
+ // delete all footnotes for now
+ const SwPosition *pStt = rPam.Start(),
+ *pEnd = rPam.GetPoint() == pStt
+ ? rPam.GetMark()
+ : rPam.GetPoint();
+
+ // Step 1. deletion/record of content indices
+ if( m_bDelFullPara )
+ {
+ OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) );
+
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+ DelBookmarks(pStt->nNode, pEnd->nNode);
+ }
+ else
+ {
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() );
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+ if (m_nEndNode - m_nSttNode > 1) // check for fully selected nodes
+ {
+ SwNodeIndex const start(pStt->nNode, +1);
+ DelBookmarks(start, pEnd->nNode);
+ }
+ }
+
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+
+ // Is already anything deleted?
+ m_nNdDiff = m_nSttNode - pStt->nNode.GetIndex();
+
+ m_bJoinNext = !bFullPara && pEnd == rPam.GetPoint();
+ m_bBackSp = !bFullPara && !m_bJoinNext;
+
+ SwTextNode *pSttTextNd = nullptr, *pEndTextNd = nullptr;
+ if( !bFullPara )
+ {
+ pSttTextNd = pStt->nNode.GetNode().GetTextNode();
+ pEndTextNd = m_nSttNode == m_nEndNode
+ ? pSttTextNd
+ : pEnd->nNode.GetNode().GetTextNode();
+ }
+ else if (m_pRedlSaveData)
+ {
+ DelFullParaMoveFrames(*pDoc, *this, *m_pRedlSaveData);
+ }
+
+ bool bMoveNds = *pStt != *pEnd // any area still existent?
+ && ( SaveContent( pStt, pEnd, pSttTextNd, pEndTextNd ) || m_bFromTableCopy );
+
+ if( pSttTextNd && pEndTextNd && pSttTextNd != pEndTextNd )
+ {
+ // two different TextNodes, thus save also the TextFormatCollection
+ m_pHistory->Add( pSttTextNd->GetTextColl(),pStt->nNode.GetIndex(), SwNodeType::Text );
+ m_pHistory->Add( pEndTextNd->GetTextColl(),pEnd->nNode.GetIndex(), SwNodeType::Text );
+
+ if( !m_bJoinNext ) // Selection from bottom to top
+ {
+ // When using JoinPrev() all AUTO-PageBreak's will be copied
+ // correctly. To restore them with UNDO, Auto-PageBreak of the
+ // EndNode needs to be reset. Same for PageDesc and ColBreak.
+ if( pEndTextNd->HasSwAttrSet() )
+ {
+ SwRegHistory aRegHist( *pEndTextNd, m_pHistory.get() );
+ if( SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState(
+ RES_BREAK, false ) )
+ pEndTextNd->ResetAttr( RES_BREAK );
+ if( pEndTextNd->HasSwAttrSet() &&
+ SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState(
+ RES_PAGEDESC, false ) )
+ pEndTextNd->ResetAttr( RES_PAGEDESC );
+ }
+ }
+ }
+
+ // Move now also the PaM. The SPoint is at the beginning of a SSelection.
+ if( pEnd == rPam.GetPoint() && ( !bFullPara || pSttTextNd || pEndTextNd ) )
+ rPam.Exchange();
+
+ if( !pSttTextNd && !pEndTextNd )
+ --rPam.GetPoint()->nNode;
+ rPam.DeleteMark(); // the SPoint is in the selection
+
+ if( !pEndTextNd )
+ m_nEndContent = 0;
+ if( !pSttTextNd )
+ m_nSttContent = 0;
+
+ if( bMoveNds ) // Do Nodes exist that need to be moved?
+ {
+ SwNodes& rNds = pDoc->GetUndoManager().GetUndoNodes();
+ SwNodes& rDocNds = pDoc->GetNodes();
+ SwNodeRange aRg( rDocNds, m_nSttNode - m_nNdDiff,
+ rDocNds, m_nEndNode - m_nNdDiff );
+ if( !bFullPara && !pEndTextNd &&
+ &aRg.aEnd.GetNode() != &pDoc->GetNodes().GetEndOfContent() )
+ {
+ SwNode* pNode = aRg.aEnd.GetNode().StartOfSectionNode();
+ if( pNode->GetIndex() >= m_nSttNode - m_nNdDiff )
+ ++aRg.aEnd; // Deletion of a complete table
+ }
+ SwNode* pTmpNd;
+ // Step 2: Expand selection if necessary
+ if( m_bJoinNext || bFullPara )
+ {
+ // If all content of a section will be moved into Undo, the section
+ // itself should be moved completely.
+ while( aRg.aEnd.GetIndex() + 2 < rDocNds.Count() &&
+ ( (pTmpNd = rDocNds[ aRg.aEnd.GetIndex()+1 ])->IsEndNode() &&
+ pTmpNd->StartOfSectionNode()->IsSectionNode() &&
+ pTmpNd->StartOfSectionNode()->GetIndex() >= aRg.aStart.GetIndex() ) )
+ ++aRg.aEnd;
+ m_nReplaceDummy = aRg.aEnd.GetIndex() + m_nNdDiff - m_nEndNode;
+ if( m_nReplaceDummy )
+ { // The selection has been expanded, because
+ ++aRg.aEnd;
+ if( pEndTextNd )
+ {
+ // The end text node has to leave the (expanded) selection
+ // The dummy is needed because MoveNodes deletes empty
+ // sections
+ ++m_nReplaceDummy;
+ SwNodeRange aMvRg( *pEndTextNd, 0, *pEndTextNd, 1 );
+ SwPosition aSplitPos( *pEndTextNd );
+ ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
+ pDoc->getIDocumentContentOperations().SplitNode( aSplitPos, false );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd );
+ --aRg.aEnd;
+ }
+ else
+ m_nReplaceDummy = 0;
+ }
+ }
+ if( m_bBackSp || bFullPara )
+ {
+ // See above, the selection has to be expanded if there are "nearly
+ // empty" sections and a replacement dummy has to be set if needed.
+ while( 1 < aRg.aStart.GetIndex() &&
+ ( (pTmpNd = rDocNds[ aRg.aStart.GetIndex()-1 ])->IsSectionNode() &&
+ pTmpNd->EndOfSectionIndex() < aRg.aEnd.GetIndex() ) )
+ --aRg.aStart;
+ if( pSttTextNd )
+ {
+ m_nReplaceDummy = m_nSttNode - m_nNdDiff - aRg.aStart.GetIndex();
+ if( m_nReplaceDummy )
+ {
+ SwNodeRange aMvRg( *pSttTextNd, 0, *pSttTextNd, 1 );
+ SwPosition aSplitPos( *pSttTextNd );
+ ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
+ pDoc->getIDocumentContentOperations().SplitNode( aSplitPos, false );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart );
+ --aRg.aStart;
+ }
+ }
+ }
+
+ if( m_bFromTableCopy )
+ {
+ if( !pEndTextNd )
+ {
+ if( pSttTextNd )
+ ++aRg.aStart;
+ else if( !bFullPara && !aRg.aEnd.GetNode().IsContentNode() )
+ --aRg.aEnd;
+ }
+ }
+ else if (pSttTextNd && (pEndTextNd || pSttTextNd->GetText().getLength()))
+ ++aRg.aStart;
+
+ // Step 3: Moving into UndoArray...
+ m_nNode = rNds.GetEndOfContent().GetIndex();
+ rDocNds.MoveNodes( aRg, rNds, SwNodeIndex( rNds.GetEndOfContent() ));
+ m_pMvStt.reset( new SwNodeIndex( rNds, m_nNode ) );
+ // remember difference!
+ m_nNode = rNds.GetEndOfContent().GetIndex() - m_nNode;
+
+ if( pSttTextNd && pEndTextNd )
+ {
+ //Step 4: Moving around sections
+ m_nSectDiff = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
+ // nSect is the number of sections which starts(ends) between start
+ // and end node of the selection. The "loser" paragraph has to be
+ // moved into the section(s) of the "winner" paragraph
+ if( m_nSectDiff )
+ {
+ if( m_bJoinNext )
+ {
+ SwNodeRange aMvRg( *pEndTextNd, 0, *pEndTextNd, 1 );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart );
+ }
+ else
+ {
+ SwNodeRange aMvRg( *pSttTextNd, 0, *pSttTextNd, 1 );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd );
+ }
+ }
+ }
+ if( m_nSectDiff || m_nReplaceDummy )
+ lcl_MakeAutoFrames( *pDoc->GetSpzFrameFormats(),
+ m_bJoinNext ? pEndTextNd->GetIndex() : pSttTextNd->GetIndex() );
+ }
+ else
+ m_nNode = 0; // moved no node -> no difference at the end
+
+ // Are there any Nodes that got deleted before that (FootNotes
+ // have ContentNodes)?
+ if( !pSttTextNd && !pEndTextNd )
+ {
+ m_nNdDiff = m_nSttNode - rPam.GetPoint()->nNode.GetIndex() - (bFullPara ? 0 : 1);
+ rPam.Move( fnMoveForward, GoInNode );
+ }
+ else
+ {
+ m_nNdDiff = m_nSttNode;
+ if( m_nSectDiff && m_bBackSp )
+ m_nNdDiff += m_nSectDiff;
+ m_nNdDiff -= rPam.GetPoint()->nNode.GetIndex();
+ }
+
+ if( !rPam.GetNode().IsContentNode() )
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+
+ // is a history necessary here at all?
+ if( m_pHistory && !m_pHistory->Count() )
+ m_pHistory.reset();
+}
+
+bool SwUndoDelete::SaveContent( const SwPosition* pStt, const SwPosition* pEnd,
+ SwTextNode* pSttTextNd, SwTextNode* pEndTextNd )
+{
+ sal_uLong nNdIdx = pStt->nNode.GetIndex();
+ // 1 - copy start in Start-String
+ if( pSttTextNd )
+ {
+ bool bOneNode = m_nSttNode == m_nEndNode;
+ SwRegHistory aRHst( *pSttTextNd, m_pHistory.get() );
+ // always save all text atttibutes because of possibly overlapping
+ // areas of on/off
+ m_pHistory->CopyAttr( pSttTextNd->GetpSwpHints(), nNdIdx,
+ 0, pSttTextNd->GetText().getLength(), true );
+ if( !bOneNode && pSttTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pSttTextNd->GetpSwAttrSet(), nNdIdx );
+
+ // the length might have changed (!!Fields!!)
+ sal_Int32 nLen = (bOneNode
+ ? pEnd->nContent.GetIndex()
+ : pSttTextNd->GetText().getLength())
+ - pStt->nContent.GetIndex();
+
+ // delete now also the text (all attribute changes are added to
+ // UNDO history)
+ m_aSttStr = pSttTextNd->GetText().copy(m_nSttContent, nLen);
+ pSttTextNd->EraseText( pStt->nContent, nLen );
+ if( pSttTextNd->GetpSwpHints() )
+ pSttTextNd->GetpSwpHints()->DeRegister();
+
+ // METADATA: store
+ bool emptied( !m_aSttStr->isEmpty() && !pSttTextNd->Len() );
+ if (!bOneNode || emptied) // merging may overwrite xmlids...
+ {
+ m_pMetadataUndoStart = emptied
+ ? pSttTextNd->CreateUndoForDelete()
+ : pSttTextNd->CreateUndo();
+ }
+
+ if( bOneNode )
+ return false; // stop moving more nodes
+ }
+
+ // 2 - copy end into End-String
+ if( pEndTextNd )
+ {
+ SwIndex aEndIdx( pEndTextNd );
+ nNdIdx = pEnd->nNode.GetIndex();
+ SwRegHistory aRHst( *pEndTextNd, m_pHistory.get() );
+
+ // always save all text atttibutes because of possibly overlapping
+ // areas of on/off
+ m_pHistory->CopyAttr( pEndTextNd->GetpSwpHints(), nNdIdx, 0,
+ pEndTextNd->GetText().getLength(), true );
+
+ if( pEndTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pEndTextNd->GetpSwAttrSet(), nNdIdx );
+
+ // delete now also the text (all attribute changes are added to
+ // UNDO history)
+ m_aEndStr = pEndTextNd->GetText().copy( 0, pEnd->nContent.GetIndex() );
+ pEndTextNd->EraseText( aEndIdx, pEnd->nContent.GetIndex() );
+ if( pEndTextNd->GetpSwpHints() )
+ pEndTextNd->GetpSwpHints()->DeRegister();
+
+ // METADATA: store
+ bool emptied = !m_aEndStr->isEmpty() && !pEndTextNd->Len();
+
+ m_pMetadataUndoEnd = emptied
+ ? pEndTextNd->CreateUndoForDelete()
+ : pEndTextNd->CreateUndo();
+ }
+
+ // if there are only two Nodes then we're done
+ if( ( pSttTextNd || pEndTextNd ) && m_nSttNode + 1 == m_nEndNode )
+ return false; // do not move any Node
+
+ return true; // move Nodes lying in between
+}
+
+bool SwUndoDelete::CanGrouping( SwDoc* pDoc, const SwPaM& rDelPam )
+{
+ // Is Undo greater than one Node (that is Start and EndString)?
+ if( !m_aSttStr || m_aSttStr->isEmpty() || m_aEndStr )
+ return false;
+
+ // only the deletion of single char's can be condensed
+ if( m_nSttNode != m_nEndNode || ( !m_bGroup && m_nSttContent+1 != m_nEndContent ))
+ return false;
+
+ const SwPosition *pStt = rDelPam.Start(),
+ *pEnd = rDelPam.GetPoint() == pStt
+ ? rDelPam.GetMark()
+ : rDelPam.GetPoint();
+
+ if( pStt->nNode != pEnd->nNode ||
+ pStt->nContent.GetIndex()+1 != pEnd->nContent.GetIndex() ||
+ pEnd->nNode != m_nSttNode )
+ return false;
+
+ // Distinguish between BackSpace and Delete because the Undo array needs to
+ // be constructed differently!
+ if( pEnd->nContent == m_nSttContent )
+ {
+ if( m_bGroup && !m_bBackSp ) return false;
+ m_bBackSp = true;
+ }
+ else if( pStt->nContent == m_nSttContent )
+ {
+ if( m_bGroup && m_bBackSp ) return false;
+ m_bBackSp = false;
+ }
+ else
+ return false;
+
+ // are both Nodes (Node/Undo array) TextNodes at all?
+ SwTextNode * pDelTextNd = pStt->nNode.GetNode().GetTextNode();
+ if( !pDelTextNd ) return false;
+
+ sal_Int32 nUChrPos = m_bBackSp ? 0 : m_aSttStr->getLength()-1;
+ sal_Unicode cDelChar = pDelTextNd->GetText()[ pStt->nContent.GetIndex() ];
+ CharClass& rCC = GetAppCharClass();
+ if( ( CH_TXTATR_BREAKWORD == cDelChar || CH_TXTATR_INWORD == cDelChar ) ||
+ rCC.isLetterNumeric( OUString( cDelChar ), 0 ) !=
+ rCC.isLetterNumeric( *m_aSttStr, nUChrPos ) )
+ return false;
+
+ // tdf#132725 - if at-char/at-para flys would be deleted, don't group!
+ // DelContentIndex() would be called at the wrong time here, the indexes
+ // in the stored SwHistoryTextFlyCnt would be wrong when Undo is invoked
+ if (IsFlySelectedByCursor(*pDoc, *pStt, *pEnd))
+ {
+ return false;
+ }
+
+ {
+ SwRedlineSaveDatas aTmpSav;
+ const bool bSaved = FillSaveData( rDelPam, aTmpSav, false );
+
+ bool bOk = ( !m_pRedlSaveData && !bSaved ) ||
+ ( m_pRedlSaveData && bSaved &&
+ SwUndo::CanRedlineGroup( *m_pRedlSaveData, aTmpSav, m_bBackSp ));
+ // aTmpSav.DeleteAndDestroyAll();
+ if( !bOk )
+ return false;
+
+ pDoc->getIDocumentRedlineAccess().DeleteRedline( rDelPam, false, RedlineType::Any );
+ }
+
+ // Both 'deletes' can be consolidated, so 'move' the related character
+ if( m_bBackSp )
+ m_nSttContent--; // BackSpace: add char to array!
+ else
+ {
+ m_nEndContent++; // Delete: attach char at the end
+ nUChrPos++;
+ }
+ m_aSttStr = m_aSttStr->replaceAt( nUChrPos, 0, OUString(cDelChar) );
+ pDelTextNd->EraseText( pStt->nContent, 1 );
+
+ m_bGroup = true;
+ return true;
+}
+
+SwUndoDelete::~SwUndoDelete()
+{
+ if( m_pMvStt ) // Delete also the selection from UndoNodes array
+ {
+ // Insert saves content in IconSection
+ m_pMvStt->GetNode().GetNodes().Delete( *m_pMvStt, m_nNode );
+ m_pMvStt.reset();
+ }
+ m_pRedlSaveData.reset();
+}
+
+static SwRewriter lcl_RewriterFromHistory(SwHistory & rHistory)
+{
+ SwRewriter aRewriter;
+
+ bool bDone = false;
+
+ for ( sal_uInt16 n = 0; n < rHistory.Count(); n++)
+ {
+ OUString aDescr = rHistory[n]->GetDescription();
+
+ if (!aDescr.isEmpty())
+ {
+ aRewriter.AddRule(UndoArg2, aDescr);
+
+ bDone = true;
+ break;
+ }
+ }
+
+ if (! bDone)
+ {
+ aRewriter.AddRule(UndoArg2, SwResId(STR_FIELD));
+ }
+
+ return aRewriter;
+}
+
+static bool lcl_IsSpecialCharacter(sal_Unicode nChar)
+{
+ switch (nChar)
+ {
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ case CH_TXTATR_TAB:
+ case CH_TXTATR_NEWLINE:
+ case CH_TXT_ATR_INPUTFIELDSTART:
+ case CH_TXT_ATR_INPUTFIELDEND:
+ case CH_TXT_ATR_FORMELEMENT:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static OUString lcl_DenotedPortion(const OUString& rStr, sal_Int32 nStart, sal_Int32 nEnd)
+{
+ OUString aResult;
+
+ auto nCount = nEnd - nStart;
+ if (nCount > 0)
+ {
+ sal_Unicode cLast = rStr[nEnd - 1];
+ if (lcl_IsSpecialCharacter(cLast))
+ {
+ switch(cLast)
+ {
+ case CH_TXTATR_TAB:
+ aResult = SwResId(STR_UNDO_TABS, nCount);
+
+ break;
+ case CH_TXTATR_NEWLINE:
+ aResult = SwResId(STR_UNDO_NLS, nCount);
+
+ break;
+
+ case CH_TXTATR_INWORD:
+ case CH_TXTATR_BREAKWORD:
+ aResult = SwRewriter::GetPlaceHolder(UndoArg2);
+ break;
+
+ case CH_TXT_ATR_INPUTFIELDSTART:
+ case CH_TXT_ATR_INPUTFIELDEND:
+ case CH_TXT_ATR_FORMELEMENT:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ break; // nothing?
+
+ default:
+ assert(!"unexpected special character");
+ break;
+ }
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(nCount));
+ aResult = aRewriter.Apply(aResult);
+ }
+ else
+ {
+ aResult = SwResId(STR_START_QUOTE) +
+ rStr.copy(nStart, nCount) +
+ SwResId(STR_END_QUOTE);
+ }
+ }
+
+ return aResult;
+}
+
+OUString DenoteSpecialCharacters(const OUString & rStr)
+{
+ OUStringBuffer aResult;
+
+ if (!rStr.isEmpty())
+ {
+ bool bStart = false;
+ sal_Int32 nStart = 0;
+ sal_Unicode cLast = 0;
+
+ for( sal_Int32 i = 0; i < rStr.getLength(); i++)
+ {
+ if (lcl_IsSpecialCharacter(rStr[i]))
+ {
+ if (cLast != rStr[i])
+ bStart = true;
+
+ }
+ else
+ {
+ if (lcl_IsSpecialCharacter(cLast))
+ bStart = true;
+ }
+
+ if (bStart)
+ {
+ aResult.append(lcl_DenotedPortion(rStr, nStart, i));
+
+ nStart = i;
+ bStart = false;
+ }
+
+ cLast = rStr[i];
+ }
+
+ aResult.append(lcl_DenotedPortion(rStr, nStart, rStr.getLength()));
+ }
+ else
+ aResult = SwRewriter::GetPlaceHolder(UndoArg2);
+
+ return aResult.makeStringAndClear();
+}
+
+SwRewriter SwUndoDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ if (m_nNode != 0)
+ {
+ if (!m_sTableName.isEmpty())
+ {
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_START_QUOTE));
+ aRewriter.AddRule(UndoArg2, m_sTableName);
+ aRewriter.AddRule(UndoArg3, SwResId(STR_END_QUOTE));
+
+ OUString sTmp = aRewriter.Apply(SwResId(STR_TABLE_NAME));
+ aResult.AddRule(UndoArg1, sTmp);
+ }
+ else
+ aResult.AddRule(UndoArg1, SwResId(STR_PARAGRAPHS));
+ }
+ else
+ {
+ OUString aStr;
+
+ if (m_aSttStr && m_aEndStr && m_aSttStr->isEmpty() &&
+ m_aEndStr->isEmpty())
+ {
+ aStr = SwResId(STR_PARAGRAPH_UNDO);
+ }
+ else
+ {
+ std::optional<OUString> aTmpStr;
+ if (m_aSttStr)
+ aTmpStr = m_aSttStr;
+ else if (m_aEndStr)
+ aTmpStr = m_aEndStr;
+
+ if (aTmpStr)
+ {
+ aStr = DenoteSpecialCharacters(*aTmpStr);
+ }
+ else
+ {
+ aStr = SwRewriter::GetPlaceHolder(UndoArg2);
+ }
+ }
+
+ aStr = ShortenString(aStr, nUndoStringLength, SwResId(STR_LDOTS));
+ if (m_pHistory)
+ {
+ SwRewriter aRewriter = lcl_RewriterFromHistory(*m_pHistory);
+ aStr = aRewriter.Apply(aStr);
+ }
+
+ aResult.AddRule(UndoArg1, aStr);
+ }
+
+ return aResult;
+}
+
+// Every object, anchored "AtContent" will be reanchored at rPos
+static void lcl_ReAnchorAtContentFlyFrames( const SwFrameFormats& rSpzArr, SwPosition &rPos, sal_uLong nOldIdx )
+{
+ if( !rSpzArr.empty() )
+ {
+ SwFlyFrameFormat* pFormat;
+ const SwFormatAnchor* pAnchor;
+ const SwPosition* pAPos;
+ for( size_t n = 0; n < rSpzArr.size(); ++n )
+ {
+ pFormat = static_cast<SwFlyFrameFormat*>(rSpzArr[n]);
+ pAnchor = &pFormat->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ pAPos = pAnchor->GetContentAnchor();
+ if( pAPos && nOldIdx == pAPos->nNode.GetIndex() )
+ {
+ SwFormatAnchor aAnch( *pAnchor );
+ aAnch.SetAnchor( &rPos );
+ pFormat->SetFormatAttr( aAnch );
+ }
+ }
+ }
+ }
+}
+
+void SwUndoDelete::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+
+ sal_uLong nCalcStt = m_nSttNode - m_nNdDiff;
+
+ if( m_nSectDiff && m_bBackSp )
+ nCalcStt += m_nSectDiff;
+
+ SwNodeIndex aIdx(rDoc.GetNodes(), nCalcStt);
+ SwNode* pInsNd = &aIdx.GetNode();
+ SwNode* pMovedNode = nullptr;
+
+ { // code block so that SwPosition is detached when deleting a Node
+ SwPosition aPos( aIdx );
+ if( !m_bDelFullPara )
+ {
+ assert(!m_bTableDelLastNd || pInsNd->IsTextNode());
+ if( pInsNd->IsTableNode() )
+ {
+ pInsNd = rDoc.GetNodes().MakeTextNode( aIdx,
+ rDoc.GetDfltTextFormatColl() );
+ --aIdx;
+ aPos.nNode = aIdx;
+ aPos.nContent.Assign( pInsNd->GetContentNode(), m_nSttContent );
+ }
+ else
+ {
+ if( pInsNd->IsContentNode() )
+ aPos.nContent.Assign( static_cast<SwContentNode*>(pInsNd), m_nSttContent );
+ if( !m_bTableDelLastNd )
+ pInsNd = nullptr; // do not delete Node!
+ }
+ }
+ else
+ pInsNd = nullptr; // do not delete Node!
+
+ bool bNodeMove = 0 != m_nNode;
+
+ if( m_aEndStr )
+ {
+ // discard attributes since they all saved!
+ SwTextNode * pTextNd;
+ if (!m_bDelFullPara && aPos.nNode.GetNode().IsSectionNode())
+ { // tdf#134250 section node wasn't deleted; but aPos must point to it in bNodeMove case below
+ assert(m_nSttContent == 0);
+ assert(!m_aSttStr);
+ pTextNd = rDoc.GetNodes()[aPos.nNode.GetIndex() + 1]->GetTextNode();
+ }
+ else
+ {
+ pTextNd = aPos.nNode.GetNode().GetTextNode();
+ }
+
+ if( pTextNd && pTextNd->HasSwAttrSet() )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd && pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( true );
+
+ if( m_aSttStr && !m_bFromTableCopy )
+ {
+ sal_uLong nOldIdx = aPos.nNode.GetIndex();
+ rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
+ // After the split all objects are anchored at the first
+ // paragraph, but the pHistory of the fly frame formats relies
+ // on anchoring at the start of the selection
+ // => selection backwards needs a correction.
+ if( m_bBackSp )
+ lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx);
+ pTextNd = aPos.nNode.GetNode().GetTextNode();
+ }
+ assert(pTextNd); // else where does m_aEndStr come from?
+ if( pTextNd )
+ {
+ OUString const ins( pTextNd->InsertText(*m_aEndStr, aPos.nContent,
+ SwInsertFlags::NOHINTEXPAND) );
+ assert(ins.getLength() == m_aEndStr->getLength()); // must succeed
+ (void) ins;
+ // METADATA: restore
+ pTextNd->RestoreMetadata(m_pMetadataUndoEnd);
+ }
+ }
+ else if (m_aSttStr && bNodeMove && pInsNd == nullptr)
+ {
+ SwTextNode * pNd = aPos.nNode.GetNode().GetTextNode();
+ if( pNd )
+ {
+ if (m_nSttContent < pNd->GetText().getLength())
+ {
+ sal_uLong nOldIdx = aPos.nNode.GetIndex();
+ rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
+ if( m_bBackSp )
+ lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx);
+ }
+ else
+ ++aPos.nNode;
+ }
+ }
+ if( m_nSectDiff )
+ {
+ sal_uLong nMoveIndex = aPos.nNode.GetIndex();
+ int nDiff = 0;
+ if( m_bJoinNext )
+ {
+ nMoveIndex += m_nSectDiff + 1;
+ pMovedNode = &aPos.nNode.GetNode();
+ }
+ else
+ {
+ nMoveIndex -= m_nSectDiff + 1;
+ ++nDiff;
+ }
+ SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
+ SwNodeRange aRg( aPos.nNode, 0 - nDiff, aPos.nNode, 1 - nDiff );
+ --aPos.nNode;
+ if( !m_bJoinNext )
+ pMovedNode = &aPos.nNode.GetNode();
+ rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx);
+ ++aPos.nNode;
+ }
+
+ if( bNodeMove )
+ {
+ SwNodeRange aRange( *m_pMvStt, 0, *m_pMvStt, m_nNode );
+ SwNodeIndex aCopyIndex( aPos.nNode, -1 );
+ rDoc.GetUndoManager().GetUndoNodes().Copy_(aRange, aPos.nNode,
+ // sw_redlinehide: delay creating frames: the flags on the
+ // nodes aren't necessarily up-to-date, and the redlines
+ // from m_pRedlSaveData aren't applied yet...
+ false);
+
+ if( m_nReplaceDummy )
+ {
+ sal_uLong nMoveIndex;
+ if( m_bJoinNext )
+ {
+ nMoveIndex = m_nEndNode - m_nNdDiff;
+ aPos.nNode = nMoveIndex + m_nReplaceDummy;
+ }
+ else
+ {
+ aPos = SwPosition( aCopyIndex );
+ nMoveIndex = aPos.nNode.GetIndex() + m_nReplaceDummy + 1;
+ }
+ SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
+ SwNodeRange aRg( aPos.nNode, 0, aPos.nNode, 1 );
+ pMovedNode = &aPos.nNode.GetNode();
+ // tdf#131684 without deleting frames
+ rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx, false);
+ rDoc.GetNodes().Delete( aMvIdx);
+ }
+ }
+
+ if( m_aSttStr )
+ {
+ aPos.nNode = m_nSttNode - m_nNdDiff + ( m_bJoinNext ? 0 : m_nReplaceDummy );
+ SwTextNode * pTextNd = aPos.nNode.GetNode().GetTextNode();
+ // If more than a single Node got deleted, also all "Node"
+ // attributes were saved
+ if (pTextNd != nullptr)
+ {
+ if( pTextNd->HasSwAttrSet() && bNodeMove && !m_aEndStr )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( true );
+
+ // SectionNode mode and selection from top to bottom:
+ // -> in StartNode is still the rest of the Join => delete
+ aPos.nContent.Assign( pTextNd, m_nSttContent );
+ OUString const ins( pTextNd->InsertText(*m_aSttStr, aPos.nContent,
+ SwInsertFlags::NOHINTEXPAND) );
+ assert(ins.getLength() == m_aSttStr->getLength()); // must succeed
+ (void) ins;
+ // METADATA: restore
+ pTextNd->RestoreMetadata(m_pMetadataUndoStart);
+ }
+ }
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback(&rDoc, m_nSetPos, false);
+ if( m_nSetPos ) // there were Footnodes/FlyFrames
+ {
+ // are there others than these ones?
+ if( m_nSetPos < m_pHistory->Count() )
+ {
+ // if so save the attributes of the others
+ SwHistory aHstr;
+ aHstr.Move( 0, m_pHistory.get(), m_nSetPos );
+ m_pHistory->Rollback(&rDoc);
+ m_pHistory->Move( 0, &aHstr );
+ }
+ else
+ {
+ m_pHistory->Rollback(&rDoc);
+ m_pHistory.reset();
+ }
+ }
+ }
+
+ if( m_bResetPgDesc || m_bResetPgBrk )
+ {
+ sal_uInt16 nStt = m_bResetPgDesc ? sal_uInt16(RES_PAGEDESC) : sal_uInt16(RES_BREAK);
+ sal_uInt16 nEnd = m_bResetPgBrk ? sal_uInt16(RES_BREAK) : sal_uInt16(RES_PAGEDESC);
+
+ SwNode* pNode = rDoc.GetNodes()[ m_nEndNode + 1 ];
+ if( pNode->IsContentNode() )
+ static_cast<SwContentNode*>(pNode)->ResetAttr( nStt, nEnd );
+ else if( pNode->IsTableNode() )
+ static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->ResetFormatAttr( nStt, nEnd );
+ }
+ }
+ // delete the temporarily added Node
+ if (pInsNd && !m_bTableDelLastNd)
+ {
+ assert(&aIdx.GetNode() == pInsNd);
+ rDoc.GetNodes().Delete( aIdx );
+ }
+ if( m_pRedlSaveData )
+ SetSaveData(rDoc, *m_pRedlSaveData);
+
+ sal_uLong delFullParaEndNode(m_nEndNode);
+ if (m_bDelFullPara && m_pRedlSaveData)
+ {
+ SwTextNode * pFirstMergedDeletedTextNode(nullptr);
+ SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, *this,
+ *m_pRedlSaveData, pFirstMergedDeletedTextNode);
+ if (pNextNode)
+ {
+ bool bNonMerged(false);
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNextNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->IsHideRedlines())
+ {
+ frames.push_back(pFrame);
+ }
+ else
+ {
+ bNonMerged = true;
+ }
+ }
+ for (SwTextFrame *const pFrame : frames)
+ {
+ // could either destroy the text frames, or move them...
+ // destroying them would have the advantage that we don't
+ // need special code to *exclude* pFirstMergedDeletedTextNode
+ // from MakeFrames for the layouts in Hide mode but not
+ // layouts in Show mode ...
+ // ... except that MakeFrames won't create them then :(
+ pFrame->RegisterToNode(*pFirstMergedDeletedTextNode);
+ assert(pFrame->GetMergedPara());
+ assert(!bNonMerged); // delFullParaEndNode is such an awful hack
+ (void) bNonMerged;
+ delFullParaEndNode = pFirstMergedDeletedTextNode->GetIndex();
+ }
+ }
+ }
+ else if (m_aSttStr && (!m_bFromTableCopy || 0 != m_nNode))
+ {
+ // only now do we have redlines in the document again; fix up the split
+ // frames
+ SwTextNode *const pStartNode(aIdx.GetNodes()[m_nSttNode]->GetTextNode());
+ assert(pStartNode);
+ sw::RecreateStartTextFrames(*pStartNode);
+ }
+
+ // create frames after SetSaveData has recreated redlines
+ if (0 != m_nNode)
+ {
+ // tdf#136453 only if section nodes at the start
+ if (m_bBackSp && m_nReplaceDummy != 0)
+ {
+ // tdf#134252 *first* create outer section frames
+ // note: text node m_nSttNode currently has frame with an upper;
+ // there's a hack in InsertCnt_() to move it below new section frame
+ SwNodeIndex const start(rDoc.GetNodes(), m_nSttNode - m_nReplaceDummy);
+ SwNodeIndex const end(rDoc.GetNodes(), m_nSttNode); // exclude m_nSttNode
+ ::MakeFrames(&rDoc, start, end);
+ }
+ // tdf#121031 if the start node is a text node, it already has a frame;
+ // if it's a table, it does not
+ // tdf#109376 exception: end on non-text-node -> start node was inserted
+ assert(!m_bDelFullPara || (m_nSectDiff == 0));
+ SwNodeIndex const start(rDoc.GetNodes(), m_nSttNode +
+ ((m_bDelFullPara || !rDoc.GetNodes()[m_nSttNode]->IsTextNode() || pInsNd)
+ ? 0 : 1));
+ // don't include end node in the range: it may have been merged already
+ // by the start node, or it may be merged by one of the moved nodes,
+ // but if it isn't merged, its current frame(s) should be good...
+ SwNodeIndex const end(rDoc.GetNodes(), m_bDelFullPara ? delFullParaEndNode : m_nEndNode);
+ ::MakeFrames(&rDoc, start, end);
+ }
+
+ if (pMovedNode)
+ { // probably better do this after creating all frames
+ lcl_MakeAutoFrames(*rDoc.GetSpzFrameFormats(), pMovedNode->GetIndex());
+ }
+
+ // tdf#134021 only after MakeFrames(), because it may be the only node
+ // that has layout frames
+ if (pInsNd && m_bTableDelLastNd)
+ {
+ assert(&aIdx.GetNode() == pInsNd);
+ SwPaM tmp(aIdx, aIdx);
+ rDoc.getIDocumentContentOperations().DelFullPara(tmp);
+ }
+
+ AddUndoRedoPaM(rContext, true);
+}
+
+void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+ SwDoc& rDoc = *rPam.GetDoc();
+
+ if( m_pRedlSaveData )
+ {
+ const bool bSuccess = FillSaveData(rPam, *m_pRedlSaveData);
+ OSL_ENSURE(bSuccess,
+ "SwUndoDelete::Redo: used to have redline data, but now none?");
+ if (!bSuccess)
+ {
+ m_pRedlSaveData.reset();
+ }
+ }
+
+ if( !m_bDelFullPara )
+ {
+ // tdf#128739 correct cursors but do not delete bookmarks yet
+ ::PaMCorrAbs(rPam, *rPam.End());
+ SetPaM(rPam);
+
+ if( !m_bJoinNext ) // then restore selection from bottom to top
+ rPam.Exchange();
+ }
+
+ if( m_pHistory ) // are the attributes saved?
+ {
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ SwHistory aHstr;
+ aHstr.Move( 0, m_pHistory.get() );
+
+ if( m_bDelFullPara )
+ {
+ OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) );
+
+ DelBookmarks(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
+ }
+ else
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() );
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+
+ m_pHistory->Move( m_nSetPos, &aHstr );
+ }
+ else
+ {
+ if( m_bDelFullPara )
+ {
+ OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) );
+
+ DelBookmarks( rPam.GetMark()->nNode, rPam.GetPoint()->nNode );
+ }
+ else
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() );
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+ }
+
+ if( !m_aSttStr && !m_aEndStr )
+ {
+ if (m_bDelFullPara && m_pRedlSaveData)
+ {
+ DelFullParaMoveFrames(rDoc, *this, *m_pRedlSaveData);
+ }
+
+ SwNodeIndex aSttIdx = ( m_bDelFullPara || m_bJoinNext )
+ ? rPam.GetMark()->nNode
+ : rPam.GetPoint()->nNode;
+ SwTableNode* pTableNd = aSttIdx.GetNode().GetTableNode();
+ if( pTableNd )
+ {
+ if( m_bTableDelLastNd )
+ {
+ // than add again a Node at the end
+ const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
+ rDoc.GetNodes().MakeTextNode( aTmpIdx,
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
+ }
+
+ SwContentNode* pNextNd = rDoc.GetNodes()[
+ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+ }
+ pTableNd->DelFrames();
+ }
+ else if (*rPam.GetMark() == *rPam.GetPoint())
+ { // paragraph with only footnote or as-char fly, delete that
+ // => DelContentIndex has already deleted it! nothing to do here
+ assert(m_nEndNode == m_nSttNode);
+ return;
+ }
+
+ // avoid asserts from ~SwIndexReg for deleted nodes
+ SwPaM aTmp(*rPam.End());
+ if (!aTmp.Move(fnMoveForward, GoInNode))
+ {
+ *aTmp.GetPoint() = *rPam.Start();
+ aTmp.Move(fnMoveBackward, GoInNode);
+ }
+ assert(aTmp.GetPoint()->nNode != rPam.GetPoint()->nNode
+ && aTmp.GetPoint()->nNode != rPam.GetMark()->nNode);
+ ::PaMCorrAbs(rPam, *aTmp.GetPoint());
+
+ rPam.DeleteMark();
+
+ rDoc.GetNodes().Delete( aSttIdx, m_nEndNode - m_nSttNode );
+ }
+ else if( m_bDelFullPara )
+ {
+ assert(!"dead code");
+ // The Pam was incremented by one at Point (== end) to provide space
+ // for UNDO. This now needs to be reverted!
+ --rPam.End()->nNode;
+ if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode )
+ *rPam.GetMark() = *rPam.GetPoint();
+ rDoc.getIDocumentContentOperations().DelFullPara( rPam );
+ }
+ else
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( rPam );
+}
+
+void SwUndoDelete::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // this action does not seem idempotent,
+ // so make sure it is only executed once on repeat
+ if (rContext.m_bDeleteRepeated)
+ return;
+
+ SwPaM & rPam = rContext.GetRepeatPaM();
+ SwDoc& rDoc = *rPam.GetDoc();
+ ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ if( !rPam.HasMark() )
+ {
+ rPam.SetMark();
+ rPam.Move( fnMoveForward, GoInContent );
+ }
+ if( m_bDelFullPara )
+ rDoc.getIDocumentContentOperations().DelFullPara( rPam );
+ else
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( rPam );
+ rContext.m_bDeleteRepeated = true;
+}
+
+void SwUndoDelete::SetTableName(const OUString & rName)
+{
+ m_sTableName = rName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx
new file mode 100644
index 000000000..db2fee97b
--- /dev/null
+++ b/sw/source/core/undo/undobj.cxx
@@ -0,0 +1,1682 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <IShellCursorSupplier.hxx>
+#include <txtftn.hxx>
+#include <fmtanchr.hxx>
+#include <ftnidx.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <UndoManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <docary.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <ndnotxt.hxx>
+#include <IMark.hxx>
+#include <mvsave.hxx>
+#include <redline.hxx>
+#include <crossrefbookmark.hxx>
+#include <strings.hrc>
+#include <docsh.hxx>
+#include <view.hxx>
+#include <frameformats.hxx>
+#include <sal/log.hxx>
+
+// This class saves the Pam as integers and can recompose those into a PaM
+SwUndRng::SwUndRng()
+ : m_nSttNode( 0 ), m_nEndNode( 0 ), m_nSttContent( 0 ), m_nEndContent( 0 )
+{
+}
+
+SwUndRng::SwUndRng( const SwPaM& rPam )
+{
+ SetValues( rPam );
+}
+
+void SwUndRng::SetValues( const SwPaM& rPam )
+{
+ const SwPosition *pStt = rPam.Start();
+ if( rPam.HasMark() )
+ {
+ const SwPosition *pEnd = rPam.GetPoint() == pStt
+ ? rPam.GetMark()
+ : rPam.GetPoint();
+ m_nEndNode = pEnd->nNode.GetIndex();
+ m_nEndContent = pEnd->nContent.GetIndex();
+ }
+ else
+ {
+ // no selection !!
+ m_nEndNode = 0;
+ m_nEndContent = COMPLETE_STRING;
+ }
+
+ m_nSttNode = pStt->nNode.GetIndex();
+ m_nSttContent = pStt->nContent.GetIndex();
+}
+
+void SwUndRng::SetPaM( SwPaM & rPam, bool bCorrToContent ) const
+{
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = m_nSttNode;
+ SwNode& rNd = rPam.GetNode();
+ if( rNd.IsContentNode() )
+ rPam.GetPoint()->nContent.Assign( rNd.GetContentNode(), m_nSttContent );
+ else if( bCorrToContent )
+ rPam.Move( fnMoveForward, GoInContent );
+ else
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+
+ if( !m_nEndNode && COMPLETE_STRING == m_nEndContent ) // no selection
+ return ;
+
+ rPam.SetMark();
+ if( m_nSttNode == m_nEndNode && m_nSttContent == m_nEndContent )
+ return; // nothing left to do
+
+ rPam.GetPoint()->nNode = m_nEndNode;
+ if( rPam.GetNode().IsContentNode() )
+ rPam.GetPoint()->nContent.Assign( rPam.GetNode().GetContentNode(), m_nEndContent );
+ else if( bCorrToContent )
+ rPam.Move( fnMoveBackward, GoInContent );
+ else
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+}
+
+SwPaM & SwUndRng::AddUndoRedoPaM(
+ ::sw::UndoRedoContext & rContext, bool const bCorrToContent) const
+{
+ SwPaM & rPaM( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ SetPaM( rPaM, bCorrToContent );
+ return rPaM;
+}
+
+void SwUndo::RemoveIdxFromSection( SwDoc& rDoc, sal_uLong nSttIdx,
+ const sal_uLong* pEndIdx )
+{
+ SwNodeIndex aIdx( rDoc.GetNodes(), nSttIdx );
+ SwNodeIndex aEndIdx( rDoc.GetNodes(), pEndIdx ? *pEndIdx
+ : aIdx.GetNode().EndOfSectionIndex() );
+ SwPosition aPos( rDoc.GetNodes().GetEndOfPostIts() );
+ SwDoc::CorrAbs( aIdx, aEndIdx, aPos, true );
+}
+
+void SwUndo::RemoveIdxFromRange( SwPaM& rPam, bool bMoveNext )
+{
+ const SwPosition* pEnd = rPam.End();
+ if( bMoveNext )
+ {
+ if( pEnd != rPam.GetPoint() )
+ rPam.Exchange();
+
+ SwNodeIndex aStt( rPam.GetMark()->nNode );
+ SwNodeIndex aEnd( rPam.GetPoint()->nNode );
+
+ if( !rPam.Move( fnMoveForward ) )
+ {
+ rPam.Exchange();
+ if( !rPam.Move( fnMoveBackward ) )
+ {
+ rPam.GetPoint()->nNode = rPam.GetDoc()->GetNodes().GetEndOfPostIts();
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+ }
+ }
+
+ SwDoc::CorrAbs( aStt, aEnd, *rPam.GetPoint(), true );
+ }
+ else
+ SwDoc::CorrAbs( rPam, *pEnd, true );
+}
+
+void SwUndo::RemoveIdxRel( sal_uLong nIdx, const SwPosition& rPos )
+{
+ // Move only the Cursor. Bookmarks/TOXMarks/etc. are done by the corresponding
+ // JoinNext/JoinPrev
+ SwNodeIndex aIdx( rPos.nNode.GetNode().GetNodes(), nIdx );
+ ::PaMCorrRel( aIdx, rPos );
+}
+
+SwUndo::SwUndo(SwUndoId const nId, const SwDoc* pDoc)
+ : m_nId(nId), m_nOrigRedlineFlags(RedlineFlags::NONE)
+ , m_nViewShellId(CreateViewShellId(pDoc))
+ , m_isRepeatIgnored(false)
+ , m_bCacheComment(true)
+{
+}
+
+ViewShellId SwUndo::CreateViewShellId(const SwDoc* pDoc)
+{
+ ViewShellId nRet(-1);
+
+ if (const SwDocShell* pDocShell = pDoc->GetDocShell())
+ {
+ if (const SwView* pView = pDocShell->GetView())
+ nRet = pView->GetViewShellId();
+ }
+
+ return nRet;
+}
+
+bool SwUndo::IsDelBox() const
+{
+ return GetId() == SwUndoId::COL_DELETE || GetId() == SwUndoId::ROW_DELETE ||
+ GetId() == SwUndoId::TABLE_DELBOX;
+}
+
+SwUndo::~SwUndo()
+{
+}
+
+namespace {
+
+class UndoRedoRedlineGuard
+{
+public:
+ UndoRedoRedlineGuard(::sw::UndoRedoContext const & rContext, SwUndo const & rUndo)
+ : m_rRedlineAccess(rContext.GetDoc().getIDocumentRedlineAccess())
+ , m_eMode(m_rRedlineAccess.GetRedlineFlags())
+ {
+ RedlineFlags const eTmpMode = rUndo.GetRedlineFlags();
+ if ((RedlineFlags::ShowMask & eTmpMode) != (RedlineFlags::ShowMask & m_eMode))
+ {
+ m_rRedlineAccess.SetRedlineFlags( eTmpMode );
+ }
+ m_rRedlineAccess.SetRedlineFlags_intern( eTmpMode | RedlineFlags::Ignore );
+ }
+ ~UndoRedoRedlineGuard()
+ {
+ m_rRedlineAccess.SetRedlineFlags(m_eMode);
+ }
+private:
+ IDocumentRedlineAccess & m_rRedlineAccess;
+ RedlineFlags const m_eMode;
+};
+
+}
+
+void SwUndo::Undo()
+{
+ assert(false); // SwUndo::Undo(): ERROR: must call UndoWithContext instead
+}
+
+void SwUndo::Redo()
+{
+ assert(false); // SwUndo::Redo(): ERROR: must call RedoWithContext instead
+}
+
+void SwUndo::UndoWithContext(SfxUndoContext & rContext)
+{
+ ::sw::UndoRedoContext *const pContext(
+ dynamic_cast< ::sw::UndoRedoContext * >(& rContext));
+ assert(pContext);
+ const UndoRedoRedlineGuard aUndoRedoRedlineGuard(*pContext, *this);
+ UndoImpl(*pContext);
+}
+
+void SwUndo::RedoWithContext(SfxUndoContext & rContext)
+{
+ ::sw::UndoRedoContext *const pContext(
+ dynamic_cast< ::sw::UndoRedoContext * >(& rContext));
+ assert(pContext);
+ const UndoRedoRedlineGuard aUndoRedoRedlineGuard(*pContext, *this);
+ RedoImpl(*pContext);
+}
+
+void SwUndo::Repeat(SfxRepeatTarget & rContext)
+{
+ if (m_isRepeatIgnored)
+ {
+ return; // ignore Repeat for multi-selections
+ }
+ ::sw::RepeatContext *const pRepeatContext(
+ dynamic_cast< ::sw::RepeatContext * >(& rContext));
+ assert(pRepeatContext);
+ RepeatImpl(*pRepeatContext);
+}
+
+bool SwUndo::CanRepeat(SfxRepeatTarget & rContext) const
+{
+ assert(dynamic_cast< ::sw::RepeatContext * >(& rContext));
+ (void)rContext;
+ // a MultiSelection action that doesn't do anything must still return true
+ return (SwUndoId::REPEAT_START <= GetId()) && (GetId() < SwUndoId::REPEAT_END);
+}
+
+void SwUndo::RepeatImpl( ::sw::RepeatContext & )
+{
+}
+
+OUString GetUndoComment(SwUndoId eId)
+{
+ const char *pId = nullptr;
+ switch (eId)
+ {
+ case SwUndoId::EMPTY:
+ pId = STR_CANT_UNDO;
+ break;
+ case SwUndoId::START:
+ case SwUndoId::END:
+ break;
+ case SwUndoId::DELETE:
+ pId = STR_DELETE_UNDO;
+ break;
+ case SwUndoId::INSERT:
+ pId = STR_INSERT_UNDO;
+ break;
+ case SwUndoId::OVERWRITE:
+ pId = STR_OVR_UNDO;
+ break;
+ case SwUndoId::SPLITNODE:
+ pId = STR_SPLITNODE_UNDO;
+ break;
+ case SwUndoId::INSATTR:
+ pId = STR_INSATTR_UNDO;
+ break;
+ case SwUndoId::SETFMTCOLL:
+ pId = STR_SETFMTCOLL_UNDO;
+ break;
+ case SwUndoId::RESETATTR:
+ pId = STR_RESET_ATTR_UNDO;
+ break;
+ case SwUndoId::INSFMTATTR:
+ pId = STR_INSFMT_ATTR_UNDO;
+ break;
+ case SwUndoId::INSDOKUMENT:
+ pId = STR_INSERT_DOC_UNDO;
+ break;
+ case SwUndoId::COPY:
+ pId = STR_COPY_UNDO;
+ break;
+ case SwUndoId::INSTABLE:
+ pId = STR_INSTABLE_UNDO;
+ break;
+ case SwUndoId::TABLETOTEXT:
+ pId = STR_TABLETOTEXT_UNDO;
+ break;
+ case SwUndoId::TEXTTOTABLE:
+ pId = STR_TEXTTOTABLE_UNDO;
+ break;
+ case SwUndoId::SORT_TXT:
+ pId = STR_SORT_TXT;
+ break;
+ case SwUndoId::INSLAYFMT:
+ pId = STR_INSERTFLY;
+ break;
+ case SwUndoId::TABLEHEADLINE:
+ pId = STR_TABLEHEADLINE;
+ break;
+ case SwUndoId::INSSECTION:
+ pId = STR_INSERTSECTION;
+ break;
+ case SwUndoId::OUTLINE_LR:
+ pId = STR_OUTLINE_LR;
+ break;
+ case SwUndoId::OUTLINE_UD:
+ pId = STR_OUTLINE_UD;
+ break;
+ case SwUndoId::INSNUM:
+ pId = STR_INSNUM;
+ break;
+ case SwUndoId::NUMUP:
+ pId = STR_NUMUP;
+ break;
+ case SwUndoId::MOVENUM:
+ pId = STR_MOVENUM;
+ break;
+ case SwUndoId::INSDRAWFMT:
+ pId = STR_INSERTDRAW;
+ break;
+ case SwUndoId::NUMORNONUM:
+ pId = STR_NUMORNONUM;
+ break;
+ case SwUndoId::INC_LEFTMARGIN:
+ pId = STR_INC_LEFTMARGIN;
+ break;
+ case SwUndoId::DEC_LEFTMARGIN:
+ pId = STR_DEC_LEFTMARGIN;
+ break;
+ case SwUndoId::INSERTLABEL:
+ pId = STR_INSERTLABEL;
+ break;
+ case SwUndoId::SETNUMRULESTART:
+ pId = STR_SETNUMRULESTART;
+ break;
+ case SwUndoId::CHGFTN:
+ pId = STR_CHANGEFTN;
+ break;
+ case SwUndoId::REDLINE:
+ SAL_INFO("sw.core", "Should NEVER be used/translated");
+ return "$1";
+ case SwUndoId::ACCEPT_REDLINE:
+ pId = STR_ACCEPT_REDLINE;
+ break;
+ case SwUndoId::REJECT_REDLINE:
+ pId = STR_REJECT_REDLINE;
+ break;
+ case SwUndoId::SPLIT_TABLE:
+ pId = STR_SPLIT_TABLE;
+ break;
+ case SwUndoId::DONTEXPAND:
+ pId = STR_DONTEXPAND;
+ break;
+ case SwUndoId::AUTOCORRECT:
+ pId = STR_AUTOCORRECT;
+ break;
+ case SwUndoId::MERGE_TABLE:
+ pId = STR_MERGE_TABLE;
+ break;
+ case SwUndoId::TRANSLITERATE:
+ pId = STR_TRANSLITERATE;
+ break;
+ case SwUndoId::PASTE_CLIPBOARD:
+ pId = STR_PASTE_CLIPBOARD_UNDO;
+ break;
+ case SwUndoId::TYPING:
+ pId = STR_TYPING_UNDO;
+ break;
+ case SwUndoId::MOVE:
+ pId = STR_MOVE_UNDO;
+ break;
+ case SwUndoId::INSGLOSSARY:
+ pId = STR_INSERT_GLOSSARY;
+ break;
+ case SwUndoId::DELBOOKMARK:
+ pId = STR_DELBOOKMARK;
+ break;
+ case SwUndoId::INSBOOKMARK:
+ pId = STR_INSBOOKMARK;
+ break;
+ case SwUndoId::SORT_TBL:
+ pId = STR_SORT_TBL;
+ break;
+ case SwUndoId::DELLAYFMT:
+ pId = STR_DELETEFLY;
+ break;
+ case SwUndoId::AUTOFORMAT:
+ pId = STR_AUTOFORMAT;
+ break;
+ case SwUndoId::REPLACE:
+ pId = STR_REPLACE;
+ break;
+ case SwUndoId::DELSECTION:
+ pId = STR_DELETESECTION;
+ break;
+ case SwUndoId::CHGSECTION:
+ pId = STR_CHANGESECTION;
+ break;
+ case SwUndoId::SETDEFTATTR:
+ pId = STR_CHANGEDEFATTR;
+ break;
+ case SwUndoId::DELNUM:
+ pId = STR_DELNUM;
+ break;
+ case SwUndoId::DRAWUNDO:
+ pId = STR_DRAWUNDO;
+ break;
+ case SwUndoId::DRAWGROUP:
+ pId = STR_DRAWGROUP;
+ break;
+ case SwUndoId::DRAWUNGROUP:
+ pId = STR_DRAWUNGROUP;
+ break;
+ case SwUndoId::DRAWDELETE:
+ pId = STR_DRAWDELETE;
+ break;
+ case SwUndoId::REREAD:
+ pId = STR_REREAD;
+ break;
+ case SwUndoId::DELGRF:
+ pId = STR_DELGRF;
+ break;
+ case SwUndoId::TABLE_ATTR:
+ pId = STR_TABLE_ATTR;
+ break;
+ case SwUndoId::TABLE_AUTOFMT:
+ pId = STR_UNDO_TABLE_AUTOFMT;
+ break;
+ case SwUndoId::TABLE_INSCOL:
+ pId = STR_UNDO_TABLE_INSCOL;
+ break;
+ case SwUndoId::TABLE_INSROW:
+ pId = STR_UNDO_TABLE_INSROW;
+ break;
+ case SwUndoId::TABLE_DELBOX:
+ pId = STR_UNDO_TABLE_DELBOX;
+ break;
+ case SwUndoId::TABLE_SPLIT:
+ pId = STR_UNDO_TABLE_SPLIT;
+ break;
+ case SwUndoId::TABLE_MERGE:
+ pId = STR_UNDO_TABLE_MERGE;
+ break;
+ case SwUndoId::TBLNUMFMT:
+ pId = STR_TABLE_NUMFORMAT;
+ break;
+ case SwUndoId::INSTOX:
+ pId = STR_INSERT_TOX;
+ break;
+ case SwUndoId::CLEARTOXRANGE:
+ pId = STR_CLEAR_TOX_RANGE;
+ break;
+ case SwUndoId::TBLCPYTBL:
+ pId = STR_TABLE_TBLCPYTBL;
+ break;
+ case SwUndoId::CPYTBL:
+ pId = STR_TABLE_CPYTBL;
+ break;
+ case SwUndoId::INS_FROM_SHADOWCRSR:
+ pId = STR_INS_FROM_SHADOWCRSR;
+ break;
+ case SwUndoId::CHAINE:
+ pId = STR_UNDO_CHAIN;
+ break;
+ case SwUndoId::UNCHAIN:
+ pId = STR_UNDO_UNCHAIN;
+ break;
+ case SwUndoId::FTNINFO:
+ pId = STR_UNDO_FTNINFO;
+ break;
+ case SwUndoId::COMPAREDOC:
+ pId = STR_UNDO_COMPAREDOC;
+ break;
+ case SwUndoId::SETFLYFRMFMT:
+ pId = STR_UNDO_SETFLYFRMFMT;
+ break;
+ case SwUndoId::SETRUBYATTR:
+ pId = STR_UNDO_SETRUBYATTR;
+ break;
+ case SwUndoId::TOXCHANGE:
+ pId = STR_TOXCHANGE;
+ break;
+ case SwUndoId::CREATE_PAGEDESC:
+ pId = STR_UNDO_PAGEDESC_CREATE;
+ break;
+ case SwUndoId::CHANGE_PAGEDESC:
+ pId = STR_UNDO_PAGEDESC;
+ break;
+ case SwUndoId::DELETE_PAGEDESC:
+ pId = STR_UNDO_PAGEDESC_DELETE;
+ break;
+ case SwUndoId::HEADER_FOOTER:
+ pId = STR_UNDO_HEADER_FOOTER;
+ break;
+ case SwUndoId::FIELD:
+ pId = STR_UNDO_FIELD;
+ break;
+ case SwUndoId::TXTFMTCOL_CREATE:
+ pId = STR_UNDO_TXTFMTCOL_CREATE;
+ break;
+ case SwUndoId::TXTFMTCOL_DELETE:
+ pId = STR_UNDO_TXTFMTCOL_DELETE;
+ break;
+ case SwUndoId::TXTFMTCOL_RENAME:
+ pId = STR_UNDO_TXTFMTCOL_RENAME;
+ break;
+ case SwUndoId::CHARFMT_CREATE:
+ pId = STR_UNDO_CHARFMT_CREATE;
+ break;
+ case SwUndoId::CHARFMT_DELETE:
+ pId = STR_UNDO_CHARFMT_DELETE;
+ break;
+ case SwUndoId::CHARFMT_RENAME:
+ pId = STR_UNDO_CHARFMT_RENAME;
+ break;
+ case SwUndoId::FRMFMT_CREATE:
+ pId = STR_UNDO_FRMFMT_CREATE;
+ break;
+ case SwUndoId::FRMFMT_DELETE:
+ pId = STR_UNDO_FRMFMT_DELETE;
+ break;
+ case SwUndoId::FRMFMT_RENAME:
+ pId = STR_UNDO_FRMFMT_RENAME;
+ break;
+ case SwUndoId::NUMRULE_CREATE:
+ pId = STR_UNDO_NUMRULE_CREATE;
+ break;
+ case SwUndoId::NUMRULE_DELETE:
+ pId = STR_UNDO_NUMRULE_DELETE;
+ break;
+ case SwUndoId::NUMRULE_RENAME:
+ pId = STR_UNDO_NUMRULE_RENAME;
+ break;
+ case SwUndoId::BOOKMARK_RENAME:
+ pId = STR_UNDO_BOOKMARK_RENAME;
+ break;
+ case SwUndoId::INDEX_ENTRY_INSERT:
+ pId = STR_UNDO_INDEX_ENTRY_INSERT;
+ break;
+ case SwUndoId::INDEX_ENTRY_DELETE:
+ pId = STR_UNDO_INDEX_ENTRY_DELETE;
+ break;
+ case SwUndoId::COL_DELETE:
+ pId = STR_UNDO_COL_DELETE;
+ break;
+ case SwUndoId::ROW_DELETE:
+ pId = STR_UNDO_ROW_DELETE;
+ break;
+ case SwUndoId::RENAME_PAGEDESC:
+ pId = STR_UNDO_PAGEDESC_RENAME;
+ break;
+ case SwUndoId::NUMDOWN:
+ pId = STR_NUMDOWN;
+ break;
+ case SwUndoId::FLYFRMFMT_TITLE:
+ pId = STR_UNDO_FLYFRMFMT_TITLE;
+ break;
+ case SwUndoId::FLYFRMFMT_DESCRIPTION:
+ pId = STR_UNDO_FLYFRMFMT_DESCRIPTION;
+ break;
+ case SwUndoId::TBLSTYLE_CREATE:
+ pId = STR_UNDO_TBLSTYLE_CREATE;
+ break;
+ case SwUndoId::TBLSTYLE_DELETE:
+ pId = STR_UNDO_TBLSTYLE_DELETE;
+ break;
+ case SwUndoId::TBLSTYLE_UPDATE:
+ pId = STR_UNDO_TBLSTYLE_UPDATE;
+ break;
+ case SwUndoId::UI_REPLACE:
+ pId = STR_REPLACE_UNDO;
+ break;
+ case SwUndoId::UI_INSERT_PAGE_BREAK:
+ pId = STR_INSERT_PAGE_BREAK_UNDO;
+ break;
+ case SwUndoId::UI_INSERT_COLUMN_BREAK:
+ pId = STR_INSERT_COLUMN_BREAK_UNDO;
+ break;
+ case SwUndoId::UI_INSERT_ENVELOPE:
+ pId = STR_INSERT_ENV_UNDO;
+ break;
+ case SwUndoId::UI_DRAG_AND_COPY:
+ pId = STR_DRAG_AND_COPY;
+ break;
+ case SwUndoId::UI_DRAG_AND_MOVE:
+ pId = STR_DRAG_AND_MOVE;
+ break;
+ case SwUndoId::UI_INSERT_CHART:
+ pId = STR_INSERT_CHART;
+ break;
+ case SwUndoId::UI_INSERT_FOOTNOTE:
+ pId = STR_INSERT_FOOTNOTE;
+ break;
+ case SwUndoId::UI_INSERT_URLBTN:
+ pId = STR_INSERT_URLBTN;
+ break;
+ case SwUndoId::UI_INSERT_URLTXT:
+ pId = STR_INSERT_URLTXT;
+ break;
+ case SwUndoId::UI_DELETE_INVISIBLECNTNT:
+ pId = STR_DELETE_INVISIBLECNTNT;
+ break;
+ case SwUndoId::UI_REPLACE_STYLE:
+ pId = STR_REPLACE_STYLE;
+ break;
+ case SwUndoId::UI_DELETE_PAGE_BREAK:
+ pId = STR_DELETE_PAGE_BREAK;
+ break;
+ case SwUndoId::UI_TEXT_CORRECTION:
+ pId = STR_TEXT_CORRECTION;
+ break;
+ case SwUndoId::UI_TABLE_DELETE:
+ pId = STR_UNDO_TABLE_DELETE;
+ break;
+ case SwUndoId::CONFLICT:
+ break;
+ case SwUndoId::PARA_SIGN_ADD:
+ pId = STR_PARAGRAPH_SIGN_UNDO;
+ break;
+ case SwUndoId::INSERT_FORM_FIELD:
+ pId = STR_UNDO_INSERT_FORM_FIELD;
+ break;
+ }
+
+ assert(pId);
+ return SwResId(pId);
+}
+
+OUString SwUndo::GetComment() const
+{
+ OUString aResult;
+
+ if (m_bCacheComment)
+ {
+ if (! maComment)
+ {
+ maComment = GetUndoComment(GetId());
+
+ SwRewriter aRewriter = GetRewriter();
+
+ maComment = aRewriter.Apply(*maComment);
+ }
+
+ aResult = *maComment;
+ }
+ else
+ {
+ aResult = GetUndoComment(GetId());
+
+ SwRewriter aRewriter = GetRewriter();
+
+ aResult = aRewriter.Apply(aResult);
+ }
+
+ return aResult;
+}
+
+ViewShellId SwUndo::GetViewShellId() const
+{
+ return m_nViewShellId;
+}
+
+SwRewriter SwUndo::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ return aResult;
+}
+
+SwUndoSaveContent::SwUndoSaveContent()
+{}
+
+SwUndoSaveContent::~SwUndoSaveContent() COVERITY_NOEXCEPT_FALSE
+{
+}
+
+// This is needed when deleting content. For REDO all contents will be moved
+// into the UndoNodesArray. These methods always create a new node to insert
+// content. As a result, the attributes will not be expanded.
+// - MoveTo moves from NodesArray into UndoNodesArray
+// - MoveFrom moves from UndoNodesArray into NodesArray
+
+// If pEndNdIdx is given, Undo/Redo calls -Ins/DelFly. In that case the whole
+// section should be moved.
+void SwUndoSaveContent::MoveToUndoNds( SwPaM& rPaM, SwNodeIndex* pNodeIdx,
+ sal_uLong* pEndNdIdx )
+{
+ SwDoc& rDoc = *rPaM.GetDoc();
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwNoTextNode* pCpyNd = rPaM.GetNode().GetNoTextNode();
+
+ // here comes the actual delete (move)
+ SwNodes & rNds = rDoc.GetUndoManager().GetUndoNodes();
+ SwPosition aPos( pEndNdIdx ? rNds.GetEndOfPostIts()
+ : rNds.GetEndOfExtras() );
+
+ const SwPosition* pStt = rPaM.Start(), *pEnd = rPaM.End();
+
+ sal_uLong nTmpMvNode = aPos.nNode.GetIndex();
+
+ if( pCpyNd || pEndNdIdx )
+ {
+ SwNodeRange aRg( pStt->nNode, 0, pEnd->nNode, 1 );
+ rDoc.GetNodes().MoveNodes( aRg, rNds, aPos.nNode, true );
+ aPos.nContent = 0;
+ --aPos.nNode;
+ }
+ else
+ {
+ rDoc.GetNodes().MoveRange( rPaM, aPos, rNds );
+ }
+ if( pEndNdIdx )
+ *pEndNdIdx = aPos.nNode.GetIndex();
+
+ // old position
+ aPos.nNode = nTmpMvNode;
+ if( pNodeIdx )
+ *pNodeIdx = aPos.nNode;
+}
+
+void SwUndoSaveContent::MoveFromUndoNds( SwDoc& rDoc, sal_uLong nNodeIdx,
+ SwPosition& rInsPos,
+ const sal_uLong* pEndNdIdx, bool const bForceCreateFrames)
+{
+ // here comes the recovery
+ SwNodes & rNds = rDoc.GetUndoManager().GetUndoNodes();
+ if( nNodeIdx == rNds.GetEndOfPostIts().GetIndex() )
+ return; // nothing saved
+
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwPaM aPaM( rInsPos );
+ if( pEndNdIdx ) // than get the section from it
+ aPaM.GetPoint()->nNode.Assign( rNds, *pEndNdIdx );
+ else
+ {
+ aPaM.GetPoint()->nNode = rNds.GetEndOfExtras();
+ GoInContent( aPaM, fnMoveBackward );
+ }
+
+ SwTextNode* pTextNd = aPaM.GetNode().GetTextNode();
+ if (!pEndNdIdx && pTextNd)
+ {
+ aPaM.SetMark();
+ aPaM.GetPoint()->nNode = nNodeIdx;
+ aPaM.GetPoint()->nContent.Assign(aPaM.GetContentNode(), 0);
+
+ SaveRedlEndPosForRestore aRedlRest( rInsPos.nNode, rInsPos.nContent.GetIndex() );
+
+ rNds.MoveRange( aPaM, rInsPos, rDoc.GetNodes() );
+
+ // delete the last Node as well
+ if( !aPaM.GetPoint()->nContent.GetIndex() ||
+ ( aPaM.GetPoint()->nNode++ && // still empty Nodes at the end?
+ &rNds.GetEndOfExtras() != &aPaM.GetPoint()->nNode.GetNode() ))
+ {
+ aPaM.GetPoint()->nContent.Assign( nullptr, 0 );
+ aPaM.SetMark();
+ rNds.Delete( aPaM.GetPoint()->nNode,
+ rNds.GetEndOfExtras().GetIndex() -
+ aPaM.GetPoint()->nNode.GetIndex() );
+ }
+
+ aRedlRest.Restore();
+ }
+ else
+ {
+ SwNodeRange aRg( rNds, nNodeIdx, rNds, (pEndNdIdx
+ ? ((*pEndNdIdx) + 1)
+ : rNds.GetEndOfExtras().GetIndex() ) );
+ rNds.MoveNodes(aRg, rDoc.GetNodes(), rInsPos.nNode, nullptr == pEndNdIdx || bForceCreateFrames);
+
+ }
+}
+
+// These two methods move the Point of Pam backwards/forwards. With that, one
+// can span an area for a Undo/Redo. (The Point is then positioned in front of
+// the area to manipulate!)
+// The flag indicates if there is still content in front of Point.
+bool SwUndoSaveContent::MovePtBackward( SwPaM& rPam )
+{
+ rPam.SetMark();
+ if( rPam.Move( fnMoveBackward ))
+ return true;
+
+ // If there is no content onwards, set Point simply to the previous position
+ // (Node and Content, so that Content will be detached!)
+ --rPam.GetPoint()->nNode;
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+ return false;
+}
+
+void SwUndoSaveContent::MovePtForward( SwPaM& rPam, bool bMvBkwrd )
+{
+ // Was there content before this position?
+ if( bMvBkwrd )
+ rPam.Move( fnMoveForward );
+ else
+ {
+ ++rPam.GetPoint()->nNode;
+ SwContentNode* pCNd = rPam.GetContentNode();
+ if( pCNd )
+ pCNd->MakeStartIndex( &rPam.GetPoint()->nContent );
+ else
+ rPam.Move( fnMoveForward );
+ }
+}
+
+// Delete all objects that have ContentIndices to the given area.
+// Currently (1994) these exist:
+// - Footnotes
+// - Flys
+// - Bookmarks
+
+// #i81002# - extending method
+// delete certain (not all) cross-reference bookmarks at text node of <rMark>
+// and at text node of <rPoint>, if these text nodes aren't the same.
+void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
+ const SwPosition& rPoint,
+ DelContentType nDelContentType )
+{
+ const SwPosition *pStt = rMark < rPoint ? &rMark : &rPoint,
+ *pEnd = &rMark == pStt ? &rPoint : &rMark;
+
+ SwDoc* pDoc = rMark.nNode.GetNode().GetDoc();
+
+ // if it's not in the doc array, probably missing some invalidation somewhere
+ assert(&rPoint.nNode.GetNodes() == &pDoc->GetNodes());
+ assert(&rMark.nNode.GetNodes() == &pDoc->GetNodes());
+
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ // 1. Footnotes
+ if( DelContentType::Ftn & nDelContentType )
+ {
+ SwFootnoteIdxs& rFootnoteArr = pDoc->GetFootnoteIdxs();
+ if( !rFootnoteArr.empty() )
+ {
+ const SwNode* pFootnoteNd;
+ size_t nPos = 0;
+ rFootnoteArr.SeekEntry( pStt->nNode, &nPos );
+ SwTextFootnote* pSrch;
+
+ // for now delete all that come afterwards
+ while( nPos < rFootnoteArr.size() && ( pFootnoteNd =
+ &( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex()
+ <= pEnd->nNode.GetIndex() )
+ {
+ const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
+ if( (DelContentType::CheckNoCntnt & nDelContentType )
+ ? (&pEnd->nNode.GetNode() == pFootnoteNd )
+ : (( &pStt->nNode.GetNode() == pFootnoteNd &&
+ pStt->nContent.GetIndex() > nFootnoteSttIdx) ||
+ ( &pEnd->nNode.GetNode() == pFootnoteNd &&
+ nFootnoteSttIdx >= pEnd->nContent.GetIndex() )) )
+ {
+ ++nPos; // continue searching
+ continue;
+ }
+
+// FIXME: duplicated code here and below -> refactor?
+ // Unfortunately an index needs to be created. Otherwise there
+ // will be problems with TextNode because the index will be
+ // deleted in the DTOR of SwFootnote!
+ SwTextNode* pTextNd = const_cast<SwTextNode*>(static_cast<const SwTextNode*>(pFootnoteNd));
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ SwTextAttr* const pFootnoteHint =
+ pTextNd->GetTextAttrForCharAt( nFootnoteSttIdx );
+ assert(pFootnoteHint);
+ SwIndex aIdx( pTextNd, nFootnoteSttIdx );
+ m_pHistory->Add( pFootnoteHint, pTextNd->GetIndex(), false );
+ pTextNd->EraseText( aIdx, 1 );
+ }
+
+ while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )->
+ GetTextNode())->GetIndex() >= pStt->nNode.GetIndex() )
+ {
+ const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
+ if( !(DelContentType::CheckNoCntnt & nDelContentType) && (
+ ( &pStt->nNode.GetNode() == pFootnoteNd &&
+ pStt->nContent.GetIndex() > nFootnoteSttIdx ) ||
+ ( &pEnd->nNode.GetNode() == pFootnoteNd &&
+ nFootnoteSttIdx >= pEnd->nContent.GetIndex() )))
+ continue; // continue searching
+
+ // Unfortunately an index needs to be created. Otherwise there
+ // will be problems with TextNode because the index will be
+ // deleted in the DTOR of SwFootnote!
+ SwTextNode* pTextNd = const_cast<SwTextNode*>(static_cast<const SwTextNode*>(pFootnoteNd));
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ SwTextAttr* const pFootnoteHint =
+ pTextNd->GetTextAttrForCharAt( nFootnoteSttIdx );
+ assert(pFootnoteHint);
+ SwIndex aIdx( pTextNd, nFootnoteSttIdx );
+ m_pHistory->Add( pFootnoteHint, pTextNd->GetIndex(), false );
+ pTextNd->EraseText( aIdx, 1 );
+ }
+ }
+ }
+
+ // 2. Flys
+ if( DelContentType::Fly & nDelContentType )
+ {
+ sal_uInt16 nChainInsPos = m_pHistory ? m_pHistory->Count() : 0;
+ const SwFrameFormats& rSpzArr = *pDoc->GetSpzFrameFormats();
+ if( !rSpzArr.empty() )
+ {
+ SwFrameFormat* pFormat;
+ const SwFormatAnchor* pAnchor;
+ size_t n = rSpzArr.size();
+ const SwPosition* pAPos;
+
+ while( n && !rSpzArr.empty() )
+ {
+ pFormat = rSpzArr[--n];
+ pAnchor = &pFormat->GetAnchor();
+ switch( pAnchor->GetAnchorId() )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) &&
+ (( DelContentType::CheckNoCntnt & nDelContentType )
+ ? ( pStt->nNode <= pAPos->nNode &&
+ pAPos->nNode < pEnd->nNode )
+ : ( *pStt <= *pAPos && *pAPos < *pEnd )) )
+ {
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ SwTextNode *const pTextNd =
+ pAPos->nNode.GetNode().GetTextNode();
+ SwTextAttr* const pFlyHint = pTextNd->GetTextAttrForCharAt(
+ pAPos->nContent.GetIndex());
+ assert(pFlyHint);
+ m_pHistory->Add( pFlyHint, 0, false );
+ // reset n so that no Format is skipped
+ n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
+ }
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ {
+ pAPos = pAnchor->GetContentAnchor();
+ if (pAPos &&
+ pStt->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode)
+ {
+ if (!m_pHistory)
+ m_pHistory.reset( new SwHistory );
+
+ if (!(DelContentType::Replace & nDelContentType)
+ && IsSelectFrameAnchoredAtPara(*pAPos, *pStt, *pEnd, nDelContentType))
+ {
+ m_pHistory->AddDeleteFly(*pFormat, nChainInsPos);
+ // reset n so that no Format is skipped
+ n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
+ }
+ // Moving the anchor?
+ else if (!((DelContentType::CheckNoCntnt|DelContentType::ExcludeFlyAtStartEnd)
+ & nDelContentType) &&
+ // at least for calls from SwUndoDelete,
+ // this should work - other Undos don't
+ // remember the order of the cursor
+ (rPoint.nNode.GetIndex() == pAPos->nNode.GetIndex())
+ // Do not try to move the anchor to a table!
+ && rMark.nNode.GetNode().IsTextNode())
+ {
+ m_pHistory->AddChangeFlyAnchor(*pFormat);
+ SwFormatAnchor aAnch( *pAnchor );
+ SwPosition aPos( rMark.nNode );
+ aAnch.SetAnchor( &aPos );
+ pFormat->SetFormatAttr( aAnch );
+ }
+ }
+ }
+ break;
+ case RndStdIds::FLY_AT_CHAR:
+ if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) &&
+ ( pStt->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode ) )
+ {
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ if (!(DelContentType::Replace & nDelContentType)
+ && IsDestroyFrameAnchoredAtChar(
+ *pAPos, *pStt, *pEnd, nDelContentType))
+ {
+ m_pHistory->AddDeleteFly(*pFormat, nChainInsPos);
+ n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
+ }
+ else if (!((DelContentType::CheckNoCntnt |
+ DelContentType::ExcludeFlyAtStartEnd)
+ & nDelContentType))
+ {
+ if( *pStt <= *pAPos && *pAPos < *pEnd )
+ {
+ // These are the objects anchored
+ // between section start and end position
+ // Do not try to move the anchor to a table!
+ if( rMark.nNode.GetNode().GetTextNode() )
+ {
+ m_pHistory->AddChangeFlyAnchor(*pFormat);
+ SwFormatAnchor aAnch( *pAnchor );
+ aAnch.SetAnchor( &rMark );
+ pFormat->SetFormatAttr( aAnch );
+ }
+ }
+ }
+ }
+ break;
+ case RndStdIds::FLY_AT_FLY:
+
+ if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) &&
+ pStt->nNode == pAPos->nNode )
+ {
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+
+ m_pHistory->AddDeleteFly(*pFormat, nChainInsPos);
+
+ // reset n so that no Format is skipped
+ n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+
+ // 3. Bookmarks
+ if( DelContentType::Bkm & nDelContentType )
+ {
+ IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess();
+ if( pMarkAccess->getAllMarksCount() )
+ {
+ for( sal_Int32 n = 0; n < pMarkAccess->getAllMarksCount(); ++n )
+ {
+ // #i81002#
+ bool bSavePos = false;
+ bool bSaveOtherPos = false;
+ const ::sw::mark::IMark *const pBkmk = pMarkAccess->getAllMarksBegin()[n];
+ auto const type(IDocumentMarkAccess::GetType(*pBkmk));
+
+ if( DelContentType::CheckNoCntnt & nDelContentType )
+ {
+ if ( pStt->nNode <= pBkmk->GetMarkPos().nNode
+ && pBkmk->GetMarkPos().nNode < pEnd->nNode )
+ {
+ bSavePos = true;
+ }
+ if ( pBkmk->IsExpanded()
+ && pStt->nNode <= pBkmk->GetOtherMarkPos().nNode
+ && pBkmk->GetOtherMarkPos().nNode < pEnd->nNode )
+ {
+ bSaveOtherPos = true;
+ }
+ }
+ else
+ {
+ // #i92125#
+ // keep cross-reference bookmarks, if content inside one paragraph is deleted.
+ if ( rMark.nNode == rPoint.nNode
+ && ( type == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
+ || type == IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK))
+ {
+ continue;
+ }
+
+ bool bMaybe = false;
+ if ( *pStt <= pBkmk->GetMarkPos() && pBkmk->GetMarkPos() <= *pEnd )
+ {
+ if ( pBkmk->GetMarkPos() == *pEnd
+ || ( *pStt == pBkmk->GetMarkPos() && pBkmk->IsExpanded() ) )
+ bMaybe = true;
+ else
+ bSavePos = true;
+ }
+ if( pBkmk->IsExpanded() &&
+ *pStt <= pBkmk->GetOtherMarkPos() && pBkmk->GetOtherMarkPos() <= *pEnd )
+ {
+ assert(!bSaveOtherPos);
+ if ( bSavePos
+ || (*pStt < pBkmk->GetOtherMarkPos() && pBkmk->GetOtherMarkPos() < *pEnd)
+ || (bMaybe
+ && ( type == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
+ || type == IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
+ || type == IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
+ || type == IDocumentMarkAccess::MarkType::DATE_FIELDMARK)))
+ {
+ if( bMaybe )
+ bSavePos = true;
+ bSaveOtherPos = true;
+ }
+ }
+
+ if ( !bSavePos && !bSaveOtherPos
+ && dynamic_cast< const ::sw::mark::CrossRefBookmark* >(pBkmk) )
+ {
+ // certain special handling for cross-reference bookmarks
+ const bool bDifferentTextNodesAtMarkAndPoint =
+ rMark.nNode != rPoint.nNode
+ && rMark.nNode.GetNode().GetTextNode()
+ && rPoint.nNode.GetNode().GetTextNode();
+ if ( bDifferentTextNodesAtMarkAndPoint )
+ {
+ // delete cross-reference bookmark at <pStt>, if only part of
+ // <pEnd> text node content is deleted.
+ if( pStt->nNode == pBkmk->GetMarkPos().nNode
+ && pEnd->nContent.GetIndex() != pEnd->nNode.GetNode().GetTextNode()->Len() )
+ {
+ bSavePos = true;
+ bSaveOtherPos = false; // cross-reference bookmarks are not expanded
+ }
+ // delete cross-reference bookmark at <pEnd>, if only part of
+ // <pStt> text node content is deleted.
+ else if( pEnd->nNode == pBkmk->GetMarkPos().nNode &&
+ pStt->nContent.GetIndex() != 0 )
+ {
+ bSavePos = true;
+ bSaveOtherPos = false; // cross-reference bookmarks are not expanded
+ }
+ }
+ }
+ else if (type == IDocumentMarkAccess::MarkType::ANNOTATIONMARK)
+ {
+ // delete annotation marks, if its end position is covered by the deletion
+ const SwPosition& rAnnotationEndPos = pBkmk->GetMarkEnd();
+ if ( *pStt < rAnnotationEndPos && rAnnotationEndPos <= *pEnd )
+ {
+ bSavePos = true;
+ bSaveOtherPos = pBkmk->IsExpanded(); //tdf#90138, only save the other pos if there is one
+ }
+ }
+ }
+
+ if ( bSavePos || bSaveOtherPos )
+ {
+ if (type != IDocumentMarkAccess::MarkType::UNO_BOOKMARK)
+ {
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ m_pHistory->Add( *pBkmk, bSavePos, bSaveOtherPos );
+ }
+ if ( bSavePos
+ && ( bSaveOtherPos
+ || !pBkmk->IsExpanded() ) )
+ {
+ pMarkAccess->deleteMark(pMarkAccess->getAllMarksBegin()+n);
+ n--;
+ }
+ }
+ }
+ }
+ }
+}
+
+// save a complete section into UndoNodes array
+SwUndoSaveSection::SwUndoSaveSection()
+ : m_nMoveLen( 0 ), m_nStartPos( ULONG_MAX )
+{
+}
+
+SwUndoSaveSection::~SwUndoSaveSection()
+{
+ if (m_pMovedStart) // delete also the section from UndoNodes array
+ {
+ // SaveSection saves the content in the PostIt section.
+ SwNodes& rUNds = m_pMovedStart->GetNode().GetNodes();
+ rUNds.Delete( *m_pMovedStart, m_nMoveLen );
+
+ m_pMovedStart.reset();
+ }
+ m_pRedlineSaveData.reset();
+}
+
+void SwUndoSaveSection::SaveSection( const SwNodeIndex& rSttIdx )
+{
+ SwNodeRange aRg( rSttIdx.GetNode(), *rSttIdx.GetNode().EndOfSectionNode() );
+ SaveSection( aRg );
+}
+
+void SwUndoSaveSection::SaveSection(
+ const SwNodeRange& rRange, bool const bExpandNodes)
+{
+ SwPaM aPam( rRange.aStart, rRange.aEnd );
+
+ // delete all footnotes, fly frames, bookmarks
+ DelContentIndex( *aPam.GetMark(), *aPam.GetPoint() );
+
+ // redlines *before* CorrAbs, because DelBookmarks will make them 0-length
+ // but *after* DelContentIndex because that also may use FillSaveData (in
+ // flys) and that will be restored *after* this one...
+ m_pRedlineSaveData.reset( new SwRedlineSaveDatas );
+ if (!SwUndo::FillSaveData( aPam, *m_pRedlineSaveData ))
+ {
+ m_pRedlineSaveData.reset();
+ }
+
+ {
+ // move certain indexes out of deleted range
+ SwNodeIndex aSttIdx( aPam.Start()->nNode.GetNode() );
+ SwNodeIndex aEndIdx( aPam.End()->nNode.GetNode() );
+ SwNodeIndex aMvStt( aEndIdx, 1 );
+ SwDoc::CorrAbs( aSttIdx, aEndIdx, SwPosition( aMvStt ), true );
+ }
+
+ m_nStartPos = rRange.aStart.GetIndex();
+
+ if (bExpandNodes)
+ {
+ --aPam.GetPoint()->nNode;
+ ++aPam.GetMark()->nNode;
+ }
+
+ SwContentNode* pCNd = aPam.GetContentNode( false );
+ if( pCNd )
+ aPam.GetMark()->nContent.Assign( pCNd, 0 );
+ if( nullptr != ( pCNd = aPam.GetContentNode()) )
+ aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+
+ // Keep positions as SwIndex so that this section can be deleted in DTOR
+ sal_uLong nEnd;
+ m_pMovedStart.reset(new SwNodeIndex(rRange.aStart));
+ MoveToUndoNds(aPam, m_pMovedStart.get(), &nEnd);
+ m_nMoveLen = nEnd - m_pMovedStart->GetIndex() + 1;
+}
+
+void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, SwNodeIndex* pIdx,
+ sal_uInt16 nSectType )
+{
+ if( ULONG_MAX != m_nStartPos ) // was there any content?
+ {
+ // check if the content is at the old position
+ SwNodeIndex aSttIdx( pDoc->GetNodes(), m_nStartPos );
+
+ // move the content from UndoNodes array into Fly
+ SwStartNode* pSttNd = SwNodes::MakeEmptySection( aSttIdx,
+ static_cast<SwStartNodeType>(nSectType) );
+
+ RestoreSection( pDoc, SwNodeIndex( *pSttNd->EndOfSectionNode() ));
+
+ if( pIdx )
+ *pIdx = *pSttNd;
+ }
+}
+
+void SwUndoSaveSection::RestoreSection(
+ SwDoc *const pDoc, const SwNodeIndex& rInsPos, bool bForceCreateFrames)
+{
+ if( ULONG_MAX != m_nStartPos ) // was there any content?
+ {
+ SwPosition aInsPos( rInsPos );
+ sal_uLong nEnd = m_pMovedStart->GetIndex() + m_nMoveLen - 1;
+ MoveFromUndoNds(*pDoc, m_pMovedStart->GetIndex(), aInsPos, &nEnd, bForceCreateFrames);
+
+ // destroy indices again, content was deleted from UndoNodes array
+ m_pMovedStart.reset();
+ m_nMoveLen = 0;
+
+ if( m_pRedlineSaveData )
+ {
+ SwUndo::SetSaveData( *pDoc, *m_pRedlineSaveData );
+ m_pRedlineSaveData.reset();
+ }
+ }
+}
+
+// save and set the RedlineData
+SwRedlineSaveData::SwRedlineSaveData(
+ SwComparePosition eCmpPos,
+ const SwPosition& rSttPos,
+ const SwPosition& rEndPos,
+ SwRangeRedline& rRedl,
+ bool bCopyNext )
+ : SwUndRng( rRedl )
+ , SwRedlineData( rRedl.GetRedlineData(), bCopyNext )
+{
+ assert( SwComparePosition::Outside == eCmpPos ||
+ !rRedl.GetContentIdx() ); // "Redline with Content"
+
+ switch (eCmpPos)
+ {
+ case SwComparePosition::OverlapBefore: // Pos1 overlaps Pos2 at the beginning
+ m_nEndNode = rEndPos.nNode.GetIndex();
+ m_nEndContent = rEndPos.nContent.GetIndex();
+ break;
+
+ case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at the end
+ m_nSttNode = rSttPos.nNode.GetIndex();
+ m_nSttContent = rSttPos.nContent.GetIndex();
+ break;
+
+ case SwComparePosition::Inside: // Pos1 lays completely in Pos2
+ m_nSttNode = rSttPos.nNode.GetIndex();
+ m_nSttContent = rSttPos.nContent.GetIndex();
+ m_nEndNode = rEndPos.nNode.GetIndex();
+ m_nEndContent = rEndPos.nContent.GetIndex();
+ break;
+
+ case SwComparePosition::Outside: // Pos2 lays completely in Pos1
+ if ( rRedl.GetContentIdx() )
+ {
+ // than move section into UndoArray and memorize it
+ SaveSection( *rRedl.GetContentIdx() );
+ rRedl.SetContentIdx( nullptr );
+ }
+ break;
+
+ case SwComparePosition::Equal: // Pos1 is exactly as big as Pos2
+ break;
+
+ default:
+ assert(false);
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ m_nRedlineCount = rSttPos.nNode.GetNode().GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().size();
+#endif
+}
+
+SwRedlineSaveData::~SwRedlineSaveData()
+{
+}
+
+void SwRedlineSaveData::RedlineToDoc( SwPaM const & rPam )
+{
+ SwDoc& rDoc = *rPam.GetDoc();
+ SwRangeRedline* pRedl = new SwRangeRedline( *this, rPam );
+
+ if( GetMvSttIdx() )
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes() );
+ RestoreSection( &rDoc, &aIdx, SwNormalStartNode );
+ if( GetHistory() )
+ GetHistory()->Rollback( &rDoc );
+ pRedl->SetContentIdx( &aIdx );
+ }
+ SetPaM( *pRedl );
+ // First, delete the "old" so that in an Append no unexpected things will
+ // happen, e.g. a delete in an insert. In the latter case the just restored
+ // content will be deleted and not the one you originally wanted.
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( *pRedl, false, RedlineType::Any );
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::DontCombineRedlines );
+ //#i92154# let UI know about a new redline with comment
+ if (rDoc.GetDocShell() && (!pRedl->GetComment().isEmpty()) )
+ rDoc.GetDocShell()->Broadcast(SwRedlineHint());
+
+ auto const result(rDoc.getIDocumentRedlineAccess().AppendRedline(pRedl, true));
+ assert(result != IDocumentRedlineAccess::AppendResult::IGNORED); // SwRedlineSaveData::RedlineToDoc: insert redline failed
+ (void) result; // unused in non-debug
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+bool SwUndo::FillSaveData(
+ const SwPaM& rRange,
+ SwRedlineSaveDatas& rSData,
+ bool bDelRange,
+ bool bCopyNext )
+{
+ rSData.clear();
+
+ const SwPosition* pStt = rRange.Start();
+ const SwPosition* pEnd = rRange.End();
+ const SwRedlineTable& rTable = rRange.GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ SwRedlineTable::size_type n = 0;
+ rRange.GetDoc()->getIDocumentRedlineAccess().GetRedline( *pStt, &n );
+ for ( ; n < rTable.size(); ++n )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+
+ const SwComparePosition eCmpPos =
+ ComparePosition( *pStt, *pEnd, *pRedl->Start(), *pRedl->End() );
+ if ( eCmpPos != SwComparePosition::Before
+ && eCmpPos != SwComparePosition::Behind
+ && eCmpPos != SwComparePosition::CollideEnd
+ && eCmpPos != SwComparePosition::CollideStart )
+ {
+
+ rSData.push_back(std::unique_ptr<SwRedlineSaveData, o3tl::default_delete<SwRedlineSaveData>>(new SwRedlineSaveData(eCmpPos, *pStt, *pEnd, *pRedl, bCopyNext)));
+ }
+ }
+ if( !rSData.empty() && bDelRange )
+ {
+ rRange.GetDoc()->getIDocumentRedlineAccess().DeleteRedline( rRange, false, RedlineType::Any );
+ }
+ return !rSData.empty();
+}
+
+bool SwUndo::FillSaveDataForFormat(
+ const SwPaM& rRange,
+ SwRedlineSaveDatas& rSData )
+{
+ rSData.clear();
+
+ const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
+ const SwRedlineTable& rTable = rRange.GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ SwRedlineTable::size_type n = 0;
+ rRange.GetDoc()->getIDocumentRedlineAccess().GetRedline( *pStt, &n );
+ for ( ; n < rTable.size(); ++n )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ if ( RedlineType::Format == pRedl->GetType() )
+ {
+ const SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRedl->Start(), *pRedl->End() );
+ if ( eCmpPos != SwComparePosition::Before
+ && eCmpPos != SwComparePosition::Behind
+ && eCmpPos != SwComparePosition::CollideEnd
+ && eCmpPos != SwComparePosition::CollideStart )
+ {
+ rSData.push_back(std::unique_ptr<SwRedlineSaveData, o3tl::default_delete<SwRedlineSaveData>>(new SwRedlineSaveData(eCmpPos, *pStt, *pEnd, *pRedl, true)));
+ }
+
+ }
+ }
+ return !rSData.empty();
+}
+
+
+void SwUndo::SetSaveData( SwDoc& rDoc, SwRedlineSaveDatas& rSData )
+{
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+ SwPaM aPam( rDoc.GetNodes().GetEndOfContent() );
+
+ for( size_t n = rSData.size(); n; )
+ rSData[ --n ].RedlineToDoc( aPam );
+
+#if OSL_DEBUG_LEVEL > 0
+ // check redline count against count saved in RedlineSaveData object
+ assert(rSData.empty() ||
+ (rSData[0].m_nRedlineCount == rDoc.getIDocumentRedlineAccess().GetRedlineTable().size()));
+ // "redline count not restored properly"
+#endif
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+bool SwUndo::HasHiddenRedlines( const SwRedlineSaveDatas& rSData )
+{
+ for( size_t n = rSData.size(); n; )
+ if( rSData[ --n ].GetMvSttIdx() )
+ return true;
+ return false;
+}
+
+bool SwUndo::CanRedlineGroup( SwRedlineSaveDatas& rCurr,
+ const SwRedlineSaveDatas& rCheck, bool bCurrIsEnd )
+{
+ if( rCurr.size() != rCheck.size() )
+ return false;
+
+ for( size_t n = 0; n < rCurr.size(); ++n )
+ {
+ const SwRedlineSaveData& rSet = rCurr[ n ];
+ const SwRedlineSaveData& rGet = rCheck[ n ];
+ if( rSet.m_nSttNode != rGet.m_nSttNode ||
+ rSet.GetMvSttIdx() || rGet.GetMvSttIdx() ||
+ ( bCurrIsEnd ? rSet.m_nSttContent != rGet.m_nEndContent
+ : rSet.m_nEndContent != rGet.m_nSttContent ) ||
+ !rGet.CanCombine( rSet ) )
+ {
+ return false;
+ }
+ }
+
+ for( size_t n = 0; n < rCurr.size(); ++n )
+ {
+ SwRedlineSaveData& rSet = rCurr[ n ];
+ const SwRedlineSaveData& rGet = rCheck[ n ];
+ if( bCurrIsEnd )
+ rSet.m_nSttContent = rGet.m_nSttContent;
+ else
+ rSet.m_nEndContent = rGet.m_nEndContent;
+ }
+ return true;
+}
+
+OUString ShortenString(const OUString & rStr, sal_Int32 nLength, const OUString & rFillStr)
+{
+ assert(nLength - rFillStr.getLength() >= 2);
+
+ if (rStr.getLength() <= nLength)
+ return rStr;
+
+ nLength -= rFillStr.getLength();
+ if ( nLength < 2 )
+ nLength = 2;
+
+ const sal_Int32 nFrontLen = nLength - nLength / 2;
+ const sal_Int32 nBackLen = nLength - nFrontLen;
+
+ return rStr.copy(0, nFrontLen)
+ + rFillStr
+ + 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
+ // at-para fly has no SwIndex!
+ && (rAnchorPos.nContent == pNode->Len() || rAnchorPos.nContent.GetIdxReg() == nullptr);
+}
+
+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;
+}
+
+/// passed start / end position could be on section start / end node
+static bool IsAtEndOfSection2(SwPosition const& rPos)
+{
+ return rPos.nNode.GetNode().IsEndNode()
+ || IsAtEndOfSection(rPos);
+}
+
+static bool IsAtStartOfSection2(SwPosition const& rPos)
+{
+ return rPos.nNode.GetNode().IsStartNode()
+ || IsAtStartOfSection(rPos);
+}
+
+static bool IsNotBackspaceHeuristic(
+ SwPosition const& rStart, SwPosition const& rEnd)
+{
+ // check if the selection is backspace/delete created by DelLeft/DelRight
+ return rStart.nNode.GetIndex() + 1 != rEnd.nNode.GetIndex()
+ || rEnd.nContent != 0
+ || rStart.nContent != rStart.nNode.GetNode().GetTextNode()->Len();
+}
+
+bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos,
+ SwPosition const & rStart, SwPosition const & rEnd,
+ DelContentType const nDelContentType)
+{
+ assert(rStart <= rEnd);
+
+ // CheckNoCntnt means DelFullPara which is obvious to handle
+ if (DelContentType::CheckNoCntnt & nDelContentType)
+ { // exclude selection end node because it won't be deleted
+ return (rAnchorPos.nNode < rEnd.nNode)
+ && (rStart.nNode <= rAnchorPos.nNode);
+ }
+
+ if ((nDelContentType & DelContentType::WriterfilterHack)
+ && rAnchorPos.GetDoc()->IsInWriterfilterImport())
+ { // FIXME hack for writerfilter RemoveLastParagraph() and MakeFlyAndMove(); can't test file format more specific?
+ return (rStart < rAnchorPos) && (rAnchorPos < rEnd);
+ }
+
+ if (nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+ { // exclude selection start and end node
+ return (rAnchorPos.nNode < rEnd.nNode)
+ && (rStart.nNode < rAnchorPos.nNode);
+ }
+
+ // in general, exclude the start and end position
+ return ((rStart < rAnchorPos)
+ || (rStart == rAnchorPos
+ // special case: fully deleted node
+ && ((rStart.nNode != rEnd.nNode && rStart.nContent == 0
+ // but not if the selection is backspace/delete!
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || (IsAtStartOfSection(rAnchorPos) && IsAtEndOfSection2(rEnd)))))
+ && ((rAnchorPos < rEnd)
+ || (rAnchorPos == rEnd
+ // special case: fully deleted node
+ && ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len()
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || (IsAtEndOfSection(rAnchorPos) && IsAtStartOfSection2(rStart)))));
+}
+
+bool IsSelectFrameAnchoredAtPara(SwPosition const & rAnchorPos,
+ SwPosition const & rStart, SwPosition const & rEnd,
+ DelContentType const nDelContentType)
+{
+ assert(rStart <= rEnd);
+
+ // CheckNoCntnt means DelFullPara which is obvious to handle
+ if (DelContentType::CheckNoCntnt & nDelContentType)
+ { // exclude selection end node because it won't be deleted
+ return (rAnchorPos.nNode < rEnd.nNode)
+ && (rStart.nNode <= rAnchorPos.nNode);
+ }
+
+ if ((nDelContentType & DelContentType::WriterfilterHack)
+ && rAnchorPos.GetDoc()->IsInWriterfilterImport())
+ { // FIXME hack for writerfilter RemoveLastParagraph() and MakeFlyAndMove(); can't test file format more specific?
+ // but it MUST NOT be done during the SetRedlineFlags at the end of ODF
+ // import, where the IsInXMLImport() cannot be checked because the
+ // stupid code temp. overrides it - instead rely on setting the ALLFLYS
+ // flag in MoveFromSection() and converting that to CheckNoCntnt with
+ // adjusted cursor!
+ return (rStart.nNode < rAnchorPos.nNode) && (rAnchorPos.nNode < rEnd.nNode);
+ }
+
+ // in general, exclude the start and end position
+ return ((rStart.nNode < rAnchorPos.nNode)
+ || (rStart.nNode == rAnchorPos.nNode
+ && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+ // special case: fully deleted node
+ && ((rStart.nNode != rEnd.nNode && rStart.nContent == 0
+ // but not if the selection is backspace/delete!
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || (IsAtStartOfSection2(rStart) && IsAtEndOfSection2(rEnd)))))
+ && ((rAnchorPos.nNode < rEnd.nNode)
+ || (rAnchorPos.nNode == rEnd.nNode
+ && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+ // special case: fully deleted node
+ && ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len()
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || (IsAtEndOfSection2(rEnd) && IsAtStartOfSection2(rStart)))));
+}
+
+bool IsFlySelectedByCursor(SwDoc const & rDoc,
+ SwPosition const & rStart, SwPosition const & rEnd)
+{
+ for (SwFrameFormat const*const pFly : *rDoc.GetSpzFrameFormats())
+ {
+ SwFormatAnchor const& rAnchor(pFly->GetAnchor());
+ switch (rAnchor.GetAnchorId())
+ {
+ case RndStdIds::FLY_AT_CHAR:
+ case RndStdIds::FLY_AT_PARA:
+ {
+ SwPosition const*const pAnchorPos(rAnchor.GetContentAnchor());
+ // can this really be null?
+ if (pAnchorPos != nullptr
+ && ((rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ ? IsDestroyFrameAnchoredAtChar(*pAnchorPos, rStart, rEnd)
+ : IsSelectFrameAnchoredAtPara(*pAnchorPos, rStart, rEnd)))
+ {
+ return true;
+ }
+ }
+ break;
+ default: // other types not relevant
+ break;
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undobj1.cxx b/sw/source/core/undo/undobj1.cxx
new file mode 100644
index 000000000..2398ea8da
--- /dev/null
+++ b/sw/source/core/undo/undobj1.cxx
@@ -0,0 +1,714 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svl/itemiter.hxx>
+#include <svx/svdundo.hxx>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <txtflcnt.hxx>
+#include <frmfmt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <rootfrm.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <ndole.hxx>
+#include <frameformats.hxx>
+#include <svx/svdobj.hxx>
+
+SwUndoFlyBase::SwUndoFlyBase( SwFrameFormat* pFormat, SwUndoId nUndoId )
+ : SwUndo(nUndoId, pFormat->GetDoc())
+ , m_pFrameFormat(pFormat)
+ , m_nNodePagePos(0)
+ , m_nContentPos(0)
+ , m_nRndId(RndStdIds::FLY_AT_PARA)
+ , m_bDelFormat(false)
+{
+}
+
+SwUndoFlyBase::~SwUndoFlyBase()
+{
+ if( m_bDelFormat ) // delete during an Undo?
+ {
+ if (m_pFrameFormat->GetOtherTextBoxFormat())
+ { // clear that before delete
+ m_pFrameFormat->SetOtherTextBoxFormat(nullptr);
+ }
+ delete m_pFrameFormat;
+ }
+}
+
+void SwUndoFlyBase::InsFly(::sw::UndoRedoContext & rContext, bool bShowSelFrame)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ // add again into array
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.push_back( m_pFrameFormat );
+
+ // OD 26.06.2003 #108784# - insert 'master' drawing object into drawing page
+ if ( RES_DRAWFRMFMT == m_pFrameFormat->Which() )
+ m_pFrameFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREP_INSERT_FLY));
+
+ SwFormatAnchor aAnchor( m_nRndId );
+
+ if (RndStdIds::FLY_AT_PAGE == m_nRndId)
+ {
+ aAnchor.SetPageNum( static_cast<sal_uInt16>(m_nNodePagePos) );
+ }
+ else
+ {
+ SwPosition aNewPos(pDoc->GetNodes().GetEndOfContent());
+ aNewPos.nNode = m_nNodePagePos;
+ if ((RndStdIds::FLY_AS_CHAR == m_nRndId) || (RndStdIds::FLY_AT_CHAR == m_nRndId))
+ {
+ aNewPos.nContent.Assign( aNewPos.nNode.GetNode().GetContentNode(),
+ m_nContentPos );
+ }
+ aAnchor.SetAnchor( &aNewPos );
+ }
+
+ m_pFrameFormat->SetFormatAttr( aAnchor ); // reset anchor
+
+ if( RES_DRAWFRMFMT != m_pFrameFormat->Which() )
+ {
+ // get Content and reset ContentAttribute
+ SwNodeIndex aIdx( pDoc->GetNodes() );
+ RestoreSection( pDoc, &aIdx, SwFlyStartNode );
+ m_pFrameFormat->SetFormatAttr( SwFormatContent( aIdx.GetNode().GetStartNode() ));
+ }
+
+ // Set InContentAttribute not until there is content!
+ // Otherwise the layout would format the Fly beforehand but would not find
+ // content; this happened with graphics from the internet.
+ if (RndStdIds::FLY_AS_CHAR == m_nRndId)
+ {
+ // there must be at least the attribute in a TextNode
+ SwContentNode* pCNd = aAnchor.GetContentAnchor()->nNode.GetNode().GetContentNode();
+ OSL_ENSURE( pCNd->IsTextNode(), "no Text Node at position." );
+ SwFormatFlyCnt aFormat( m_pFrameFormat );
+ pCNd->GetTextNode()->InsertItem(aFormat, m_nContentPos, m_nContentPos, SetAttrMode::NOHINTEXPAND);
+ }
+
+ if (m_pFrameFormat->GetOtherTextBoxFormat())
+ {
+ // recklessly assume that this thing will live longer than the
+ // SwUndoFlyBase - not sure what could be done if that isn't the case...
+ m_pFrameFormat->GetOtherTextBoxFormat()->SetOtherTextBoxFormat(m_pFrameFormat);
+
+ if (m_pFrameFormat->GetOtherTextBoxFormat()->Which() == RES_DRAWFRMFMT)
+ {
+ SdrObject* pSdrObject = m_pFrameFormat->GetOtherTextBoxFormat()->FindSdrObject();
+ if (pSdrObject)
+ {
+ // Make sure the old UNO wrapper is no longer cached after changing the shape +
+ // textframe pair. Otherwise we would have a wrapper which doesn't know about its
+ // textframe, even if it's there.
+ pSdrObject->setUnoShape(nullptr);
+ }
+ }
+ if (m_pFrameFormat->Which() == RES_DRAWFRMFMT)
+ {
+ // This is a draw format and we just set the fly format's textbox pointer to this draw
+ // format. Sync the draw format's content with the fly format's content.
+ SwFrameFormat* pFlyFormat = m_pFrameFormat->GetOtherTextBoxFormat();
+ m_pFrameFormat->SetFormatAttr(pFlyFormat->GetContent());
+ }
+ }
+
+ m_pFrameFormat->MakeFrames();
+
+ if( bShowSelFrame )
+ {
+ rContext.SetSelections(m_pFrameFormat, nullptr);
+ }
+
+ if( GetHistory() )
+ GetHistory()->Rollback( pDoc );
+
+ switch( m_nRndId )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ m_nNodePagePos = rAnchor.GetContentAnchor()->nNode.GetIndex();
+ m_nContentPos = rAnchor.GetContentAnchor()->nContent.GetIndex();
+ }
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ {
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ m_nNodePagePos = rAnchor.GetContentAnchor()->nNode.GetIndex();
+ }
+ break;
+ case RndStdIds::FLY_AT_PAGE:
+ break;
+ default: break;
+ }
+ m_bDelFormat = false;
+}
+
+void SwUndoFlyBase::DelFly( SwDoc* pDoc )
+{
+ m_bDelFormat = true; // delete Format in DTOR
+ m_pFrameFormat->DelFrames(); // destroy Frames
+
+ if (m_pFrameFormat->GetOtherTextBoxFormat())
+ { // tdf#108867 clear that pointer
+ m_pFrameFormat->GetOtherTextBoxFormat()->SetOtherTextBoxFormat(nullptr);
+ }
+
+ // all Uno objects should now log themselves off
+ {
+ SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, m_pFrameFormat );
+ m_pFrameFormat->ModifyNotification( &aMsgHint, &aMsgHint );
+ }
+
+ if ( RES_DRAWFRMFMT != m_pFrameFormat->Which() )
+ {
+ // if there is content than save it
+ const SwFormatContent& rContent = m_pFrameFormat->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), "Fly without content" );
+
+ SaveSection( *rContent.GetContentIdx() );
+ const_cast<SwFormatContent&>(rContent).SetNewContentIdx( nullptr );
+ }
+ // OD 02.07.2003 #108784# - remove 'master' drawing object from drawing page
+ else
+ m_pFrameFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREP_DELETE_FLY));
+
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ const SwPosition* pPos = rAnchor.GetContentAnchor();
+ // The positions in Nodes array got shifted.
+ m_nRndId = rAnchor.GetAnchorId();
+ if (RndStdIds::FLY_AS_CHAR == m_nRndId)
+ {
+ m_nNodePagePos = pPos->nNode.GetIndex();
+ m_nContentPos = pPos->nContent.GetIndex();
+ SwTextNode *const pTextNd = pPos->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "No Textnode found" );
+ SwTextFlyCnt* const pAttr = static_cast<SwTextFlyCnt*>(
+ pTextNd->GetTextAttrForCharAt( m_nContentPos, RES_TXTATR_FLYCNT ) );
+ // attribute is still in TextNode, delete
+ if( pAttr && pAttr->GetFlyCnt().GetFrameFormat() == m_pFrameFormat )
+ {
+ // Pointer to 0, do not delete
+ const_cast<SwFormatFlyCnt&>(pAttr->GetFlyCnt()).SetFlyFormat();
+ SwIndex aIdx( pPos->nContent );
+ pTextNd->EraseText( aIdx, 1 );
+ }
+ }
+ else if (RndStdIds::FLY_AT_CHAR == m_nRndId)
+ {
+ m_nNodePagePos = pPos->nNode.GetIndex();
+ m_nContentPos = pPos->nContent.GetIndex();
+ }
+ else if ((RndStdIds::FLY_AT_PARA == m_nRndId) || (RndStdIds::FLY_AT_FLY == m_nRndId))
+ {
+ m_nNodePagePos = pPos->nNode.GetIndex();
+ }
+ else
+ {
+ m_nNodePagePos = rAnchor.GetPageNum();
+ }
+
+ m_pFrameFormat->ResetFormatAttr( RES_ANCHOR ); // delete anchor
+
+ // delete from array
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.erase( m_pFrameFormat );
+}
+
+SwUndoInsLayFormat::SwUndoInsLayFormat( SwFrameFormat* pFormat, sal_uLong nNodeIdx, sal_Int32 nCntIdx )
+ : SwUndoFlyBase( pFormat, RES_DRAWFRMFMT == pFormat->Which() ?
+ SwUndoId::INSDRAWFMT : SwUndoId::INSLAYFMT ),
+ mnCursorSaveIndexPara( nNodeIdx ), mnCursorSaveIndexPos( nCntIdx )
+{
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ m_nRndId = rAnchor.GetAnchorId();
+ m_bDelFormat = false;
+ switch( m_nRndId )
+ {
+ case RndStdIds::FLY_AT_PAGE:
+ m_nNodePagePos = rAnchor.GetPageNum();
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ m_nNodePagePos = rAnchor.GetContentAnchor()->nNode.GetIndex();
+ break;
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ const SwPosition* pPos = rAnchor.GetContentAnchor();
+ m_nContentPos = pPos->nContent.GetIndex();
+ m_nNodePagePos = pPos->nNode.GetIndex();
+ }
+ break;
+ default:
+ OSL_FAIL( "Which FlyFrame?" );
+ }
+}
+
+SwUndoInsLayFormat::~SwUndoInsLayFormat()
+{
+}
+
+void SwUndoInsLayFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ const SwFormatContent& rContent = m_pFrameFormat->GetContent();
+ if( rContent.GetContentIdx() ) // no content
+ {
+ assert(&rContent.GetContentIdx()->GetNodes() == &rDoc.GetNodes());
+ bool bRemoveIdx = true;
+ if( mnCursorSaveIndexPara > 0 )
+ {
+ SwTextNode *const pNode =
+ rDoc.GetNodes()[mnCursorSaveIndexPara]->GetTextNode();
+ if( pNode )
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes(),
+ rContent.GetContentIdx()->GetIndex() );
+ SwNodeIndex aEndIdx( rDoc.GetNodes(),
+ aIdx.GetNode().EndOfSectionIndex() );
+ SwIndex aIndex( pNode, mnCursorSaveIndexPos );
+ SwPosition aPos( *pNode, aIndex );
+ SwDoc::CorrAbs( aIdx, aEndIdx, aPos, true );
+ bRemoveIdx = false;
+ }
+ }
+ if( bRemoveIdx )
+ {
+ RemoveIdxFromSection( rDoc, rContent.GetContentIdx()->GetIndex() );
+ }
+ }
+ DelFly(& rDoc);
+}
+
+void SwUndoInsLayFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ InsFly(rContext);
+}
+
+void SwUndoInsLayFormat::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ // get anchor and reset it
+ SwFormatAnchor aAnchor( m_pFrameFormat->GetAnchor() );
+ if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
+ {
+ SwPosition aPos( *rContext.GetRepeatPaM().GetPoint() );
+ if (RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId())
+ {
+ aPos.nContent.Assign( nullptr, 0 );
+ }
+ aAnchor.SetAnchor( &aPos );
+ }
+ else if( RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId() )
+ {
+ SwStartNode const*const pSttNd =
+ rContext.GetRepeatPaM().GetNode().FindFlyStartNode();
+ if( pSttNd )
+ {
+ SwPosition aPos( *pSttNd );
+ aAnchor.SetAnchor( &aPos );
+ }
+ else
+ {
+ return ;
+ }
+ }
+ else if (RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId())
+ {
+ aAnchor.SetPageNum( pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->GetCurrPage( &rContext.GetRepeatPaM() ));
+ }
+ else {
+ OSL_FAIL( "What kind of anchor is this?" );
+ }
+
+ (void) pDoc->getIDocumentLayoutAccess().CopyLayoutFormat( *m_pFrameFormat, aAnchor, true, true );
+}
+
+OUString SwUndoInsLayFormat::GetComment() const
+{
+ OUString aResult;
+
+ // HACK: disable caching:
+ // the SfxUndoManager calls GetComment() too early: the pFrameFormat does not
+ // have a SwDrawContact yet, so it will fall back to SwUndo::GetComment(),
+ // which sets pComment to a wrong value.
+// if (! pComment)
+ if ((true))
+ {
+ /*
+ If frame format is present and has an SdrObject use the undo
+ comment of the SdrObject. Otherwise use the default comment.
+ */
+ bool bDone = false;
+ if (m_pFrameFormat)
+ {
+ const SdrObject * pSdrObj = m_pFrameFormat->FindSdrObject();
+ if ( pSdrObj )
+ {
+ aResult = SdrUndoNewObj::GetComment( *pSdrObj );
+ bDone = true;
+ }
+ }
+
+ if (! bDone)
+ aResult = SwUndo::GetComment();
+ }
+ else
+ aResult = *maComment;
+
+ return aResult;
+}
+
+static SwUndoId
+lcl_GetSwUndoId(SwFrameFormat const *const pFrameFormat)
+{
+ if (RES_DRAWFRMFMT != pFrameFormat->Which())
+ {
+ const SwFormatContent& rContent = pFrameFormat->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), "Fly without content" );
+
+ SwNodeIndex firstNode(*rContent.GetContentIdx(), 1);
+ SwNoTextNode *const pNoTextNode(firstNode.GetNode().GetNoTextNode());
+ if (pNoTextNode && pNoTextNode->IsGrfNode())
+ {
+ return SwUndoId::DELGRF;
+ }
+ else if (pNoTextNode && pNoTextNode->IsOLENode())
+ {
+ // surprisingly not SwUndoId::DELOLE, which does not seem to work
+ return SwUndoId::DELETE;
+ }
+ }
+ return SwUndoId::DELLAYFMT;
+}
+
+SwUndoDelLayFormat::SwUndoDelLayFormat( SwFrameFormat* pFormat )
+ : SwUndoFlyBase( pFormat, lcl_GetSwUndoId(pFormat) )
+ , m_bShowSelFrame( true )
+{
+ SwDoc* pDoc = pFormat->GetDoc();
+ DelFly( pDoc );
+}
+
+SwRewriter SwUndoDelLayFormat::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ SwDoc * pDoc = m_pFrameFormat->GetDoc();
+
+ if (pDoc)
+ {
+ SwNodeIndex* pIdx = GetMvSttIdx();
+ if( 1 == GetMvNodeCnt() && pIdx)
+ {
+ SwNode *const pNd = & pIdx->GetNode();
+
+ if ( pNd->IsNoTextNode() && pNd->IsOLENode())
+ {
+ SwOLENode * pOLENd = pNd->GetOLENode();
+
+ aRewriter.AddRule(UndoArg1, pOLENd->GetDescription());
+ }
+ }
+ }
+
+ return aRewriter;
+}
+
+void SwUndoDelLayFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ InsFly( rContext, m_bShowSelFrame );
+}
+
+void SwUndoDelLayFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ const SwFormatContent& rContent = m_pFrameFormat->GetContent();
+ if( rContent.GetContentIdx() ) // no content
+ {
+ RemoveIdxFromSection(rDoc, rContent.GetContentIdx()->GetIndex());
+ }
+
+ DelFly(& rDoc);
+}
+
+void SwUndoDelLayFormat::RedoForRollback()
+{
+ const SwFormatContent& rContent = m_pFrameFormat->GetContent();
+ if( rContent.GetContentIdx() ) // no content
+ RemoveIdxFromSection( *m_pFrameFormat->GetDoc(),
+ rContent.GetContentIdx()->GetIndex() );
+
+ DelFly( m_pFrameFormat->GetDoc() );
+}
+
+SwUndoSetFlyFormat::SwUndoSetFlyFormat( SwFrameFormat& rFlyFormat, const SwFrameFormat& rNewFrameFormat )
+ : SwUndo( SwUndoId::SETFLYFRMFMT, rFlyFormat.GetDoc() ), SwClient( &rFlyFormat ), m_pFrameFormat( &rFlyFormat ),
+ m_DerivedFromFormatName( rFlyFormat.IsDefault() ? "" : rFlyFormat.DerivedFrom()->GetName() ),
+ m_NewFormatName( rNewFrameFormat.GetName() ),
+ m_pItemSet( new SfxItemSet( *rFlyFormat.GetAttrSet().GetPool(),
+ rFlyFormat.GetAttrSet().GetRanges() )),
+ m_nOldNode( 0 ), m_nNewNode( 0 ),
+ m_nOldContent( 0 ), m_nNewContent( 0 ),
+ m_nOldAnchorType( RndStdIds::FLY_AT_PARA ), m_nNewAnchorType( RndStdIds::FLY_AT_PARA ), m_bAnchorChanged( false )
+{
+}
+
+SwRewriter SwUndoSetFlyFormat::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_NewFormatName);
+
+ return aRewriter;
+}
+
+SwUndoSetFlyFormat::~SwUndoSetFlyFormat()
+{
+}
+
+void SwUndoSetFlyFormat::GetAnchor( SwFormatAnchor& rAnchor,
+ sal_uLong nNode, sal_Int32 nContent )
+{
+ RndStdIds nAnchorTyp = rAnchor.GetAnchorId();
+ if (RndStdIds::FLY_AT_PAGE != nAnchorTyp)
+ {
+ SwNode* pNd = m_pFrameFormat->GetDoc()->GetNodes()[ nNode ];
+
+ if( RndStdIds::FLY_AT_FLY == nAnchorTyp
+ ? ( !pNd->IsStartNode() || SwFlyStartNode !=
+ static_cast<SwStartNode*>(pNd)->GetStartNodeType() )
+ : !pNd->IsTextNode() )
+ {
+ pNd = nullptr; // invalid position
+ }
+ else
+ {
+ SwPosition aPos( *pNd );
+ if ((RndStdIds::FLY_AS_CHAR == nAnchorTyp) ||
+ (RndStdIds::FLY_AT_CHAR == nAnchorTyp))
+ {
+ if (nContent > pNd->GetTextNode()->GetText().getLength())
+ {
+ pNd = nullptr; // invalid position
+ }
+ else
+ {
+ aPos.nContent.Assign(pNd->GetTextNode(), nContent);
+ }
+ }
+ if ( pNd )
+ {
+ rAnchor.SetAnchor( &aPos );
+ }
+ }
+
+ if( !pNd )
+ {
+ // invalid position - assign first page
+ rAnchor.SetType( RndStdIds::FLY_AT_PAGE );
+ rAnchor.SetPageNum( 1 );
+ }
+ }
+ else
+ rAnchor.SetPageNum( nContent );
+}
+
+void SwUndoSetFlyFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ // Is the new Format still existent?
+ SwFrameFormat* pDerivedFromFrameFormat = rDoc.FindFrameFormatByName(m_DerivedFromFormatName);
+ if (pDerivedFromFrameFormat)
+ {
+ if( m_bAnchorChanged )
+ m_pFrameFormat->DelFrames();
+
+ if( m_pFrameFormat->DerivedFrom() != pDerivedFromFrameFormat)
+ m_pFrameFormat->SetDerivedFrom(pDerivedFromFrameFormat);
+
+ SfxItemIter aIter( *m_pItemSet );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if( IsInvalidItem( pItem ))
+ m_pFrameFormat->ResetFormatAttr( m_pItemSet->GetWhichByPos(
+ aIter.GetCurPos() ));
+ else
+ m_pFrameFormat->SetFormatAttr( *pItem );
+ }
+
+ if( m_bAnchorChanged )
+ {
+ const SwFormatAnchor& rOldAnch = m_pFrameFormat->GetAnchor();
+ if (RndStdIds::FLY_AS_CHAR == rOldAnch.GetAnchorId())
+ {
+ // With InContents it's tricky: the text attribute needs to be
+ // deleted. Unfortunately, this not only destroys the Frames but
+ // also the format. To prevent that, first detach the
+ // connection between attribute and format.
+ const SwPosition *pPos = rOldAnch.GetContentAnchor();
+ SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = pPos->nContent.GetIndex();
+ SwTextAttr * pHint = pTextNode->GetTextAttrForCharAt(
+ nIdx, RES_TXTATR_FLYCNT );
+ assert(pHint && "Missing Hint.");
+ OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT,
+ "Missing FlyInCnt-Hint." );
+ OSL_ENSURE( pHint->GetFlyCnt().GetFrameFormat() == m_pFrameFormat,
+ "Wrong TextFlyCnt-Hint." );
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
+
+ // Connection is now detached, therefore the attribute can be
+ // deleted
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
+ }
+
+ // reposition anchor
+ SwFormatAnchor aNewAnchor( m_nOldAnchorType );
+ GetAnchor( aNewAnchor, m_nOldNode, m_nOldContent );
+ m_pFrameFormat->SetFormatAttr( aNewAnchor );
+
+ if (RndStdIds::FLY_AS_CHAR == aNewAnchor.GetAnchorId())
+ {
+ const SwPosition* pPos = aNewAnchor.GetContentAnchor();
+ SwFormatFlyCnt aFormat( m_pFrameFormat );
+ pPos->nNode.GetNode().GetTextNode()->InsertItem( aFormat,
+ m_nOldContent, 0 );
+ }
+
+ m_pFrameFormat->MakeFrames();
+ }
+ rContext.SetSelections(m_pFrameFormat, nullptr);
+ }
+}
+
+void SwUndoSetFlyFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ // Is the new Format still existent?
+ SwFrameFormat* pNewFrameFormat = rDoc.FindFrameFormatByName(m_NewFormatName);
+ if (pNewFrameFormat)
+ {
+ if( m_bAnchorChanged )
+ {
+ SwFormatAnchor aNewAnchor( m_nNewAnchorType );
+ GetAnchor( aNewAnchor, m_nNewNode, m_nNewContent );
+ SfxItemSet aSet( rDoc.GetAttrPool(), aFrameFormatSetRange );
+ aSet.Put( aNewAnchor );
+ rDoc.SetFrameFormatToFly( *m_pFrameFormat, *pNewFrameFormat, &aSet );
+ }
+ else
+ rDoc.SetFrameFormatToFly( *m_pFrameFormat, *pNewFrameFormat);
+
+ rContext.SetSelections(m_pFrameFormat, nullptr);
+ }
+}
+
+void SwUndoSetFlyFormat::PutAttr( sal_uInt16 nWhich, const SfxPoolItem* pItem )
+{
+ if( pItem && pItem != GetDfltAttr( nWhich ) )
+ {
+ // Special treatment for this anchor
+ if( RES_ANCHOR == nWhich )
+ {
+ // only keep the first change
+ OSL_ENSURE( !m_bAnchorChanged, "multiple changes of an anchor are not allowed!" );
+
+ m_bAnchorChanged = true;
+
+ const SwFormatAnchor* pAnchor = static_cast<const SwFormatAnchor*>(pItem);
+ m_nOldAnchorType = pAnchor->GetAnchorId();
+ switch( m_nOldAnchorType )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ m_nOldContent = pAnchor->GetContentAnchor()->nContent.GetIndex();
+ [[fallthrough]];
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ m_nOldNode = pAnchor->GetContentAnchor()->nNode.GetIndex();
+ break;
+
+ default:
+ m_nOldContent = pAnchor->GetPageNum();
+ }
+
+ pAnchor = &m_pFrameFormat->GetAnchor();
+ m_nNewAnchorType = pAnchor->GetAnchorId();
+ switch( m_nNewAnchorType )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ m_nNewContent = pAnchor->GetContentAnchor()->nContent.GetIndex();
+ [[fallthrough]];
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ m_nNewNode = pAnchor->GetContentAnchor()->nNode.GetIndex();
+ break;
+
+ default:
+ m_nNewContent = pAnchor->GetPageNum();
+ }
+ }
+ else
+ m_pItemSet->Put( *pItem );
+ }
+ else
+ m_pItemSet->InvalidateItem( nWhich );
+}
+
+void SwUndoSetFlyFormat::Modify( const SfxPoolItem* pOld, const SfxPoolItem* )
+{
+ if( pOld )
+ {
+ sal_uInt16 nWhich = pOld->Which();
+
+ if( nWhich < POOLATTR_END )
+ PutAttr( nWhich, pOld );
+ else if( RES_ATTRSET_CHG == nWhich )
+ {
+ SfxItemIter aIter( *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ PutAttr( pItem->Which(), pItem );
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undoflystrattr.cxx b/sw/source/core/undo/undoflystrattr.cxx
new file mode 100644
index 000000000..1a811ad21
--- /dev/null
+++ b/sw/source/core/undo/undoflystrattr.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <undoflystrattr.hxx>
+#include <frmfmt.hxx>
+
+
+SwUndoFlyStrAttr::SwUndoFlyStrAttr( SwFlyFrameFormat& rFlyFrameFormat,
+ const SwUndoId eUndoId,
+ const OUString& sOldStr,
+ const OUString& sNewStr )
+ : SwUndo( eUndoId, rFlyFrameFormat.GetDoc() ),
+ mrFlyFrameFormat( rFlyFrameFormat ),
+ msOldStr( sOldStr ),
+ msNewStr( sNewStr )
+{
+ assert(eUndoId == SwUndoId::FLYFRMFMT_TITLE
+ || eUndoId == SwUndoId::FLYFRMFMT_DESCRIPTION);
+}
+
+SwUndoFlyStrAttr::~SwUndoFlyStrAttr()
+{
+}
+
+void SwUndoFlyStrAttr::UndoImpl(::sw::UndoRedoContext &)
+{
+ switch ( GetId() )
+ {
+ case SwUndoId::FLYFRMFMT_TITLE:
+ {
+ mrFlyFrameFormat.SetObjTitle( msOldStr, true );
+ }
+ break;
+ case SwUndoId::FLYFRMFMT_DESCRIPTION:
+ {
+ mrFlyFrameFormat.SetObjDescription( msOldStr, true );
+ }
+ break;
+ default:
+ {
+ }
+ }
+}
+
+void SwUndoFlyStrAttr::RedoImpl(::sw::UndoRedoContext &)
+{
+ switch ( GetId() )
+ {
+ case SwUndoId::FLYFRMFMT_TITLE:
+ {
+ mrFlyFrameFormat.SetObjTitle( msNewStr, true );
+ }
+ break;
+ case SwUndoId::FLYFRMFMT_DESCRIPTION:
+ {
+ mrFlyFrameFormat.SetObjDescription( msNewStr, true );
+ }
+ break;
+ default:
+ {
+ }
+ }
+}
+
+SwRewriter SwUndoFlyStrAttr::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule( UndoArg1, mrFlyFrameFormat.GetName() );
+
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undraw.cxx b/sw/source/core/undo/undraw.cxx
new file mode 100644
index 000000000..b6c8bd521
--- /dev/null
+++ b/sw/source/core/undo/undraw.cxx
@@ -0,0 +1,559 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoDraw.hxx>
+
+#include <svx/svdogrp.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdview.hxx>
+
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <fmtanchr.hxx>
+#include <fmtflcnt.hxx>
+#include <txtflcnt.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <dcontact.hxx>
+#include <viewsh.hxx>
+#include <frameformats.hxx>
+
+struct SwUndoGroupObjImpl
+{
+ SwDrawFrameFormat* pFormat;
+ SdrObject* pObj;
+ sal_uLong nNodeIdx;
+};
+
+// Draw-Objecte
+
+void SwDoc::AddDrawUndo( std::unique_ptr<SdrUndoAction> pUndo )
+{
+ if (GetIDocumentUndoRedo().DoesUndo() &&
+ GetIDocumentUndoRedo().DoesDrawUndo())
+ {
+ const SdrMarkList* pMarkList = nullptr;
+ SwViewShell* pSh = getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pSh && pSh->HasDrawView() )
+ pMarkList = &pSh->GetDrawView()->GetMarkedObjectList();
+
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwSdrUndo>(std::move(pUndo), pMarkList, this) );
+ }
+}
+
+SwSdrUndo::SwSdrUndo( std::unique_ptr<SdrUndoAction> pUndo, const SdrMarkList* pMrkLst, const SwDoc* pDoc )
+ : SwUndo( SwUndoId::DRAWUNDO, pDoc ), m_pSdrUndo( std::move(pUndo) )
+{
+ if( pMrkLst && pMrkLst->GetMarkCount() )
+ m_pMarkList.reset( new SdrMarkList( *pMrkLst ) );
+}
+
+SwSdrUndo::~SwSdrUndo()
+{
+ m_pSdrUndo.reset();
+ m_pMarkList.reset();
+}
+
+void SwSdrUndo::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pSdrUndo->Undo();
+ rContext.SetSelections(nullptr, m_pMarkList.get());
+}
+
+void SwSdrUndo::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pSdrUndo->Redo();
+ rContext.SetSelections(nullptr, m_pMarkList.get());
+}
+
+OUString SwSdrUndo::GetComment() const
+{
+ return m_pSdrUndo->GetComment();
+}
+
+static void lcl_SendRemoveToUno( SwFormat& rFormat )
+{
+ SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, &rFormat );
+ rFormat.ModifyNotification( &aMsgHint, &aMsgHint );
+}
+
+static void lcl_SaveAnchor( SwFrameFormat* pFormat, sal_uLong& rNodePos )
+{
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()))
+ {
+ rNodePos = rAnchor.GetContentAnchor()->nNode.GetIndex();
+ sal_Int32 nContentPos = 0;
+
+ if (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId())
+ {
+ nContentPos = rAnchor.GetContentAnchor()->nContent.GetIndex();
+
+ // destroy TextAttribute
+ SwTextNode *pTextNd = pFormat->GetDoc()->GetNodes()[ rNodePos ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "No text node found!" );
+ SwTextFlyCnt* pAttr = static_cast<SwTextFlyCnt*>(
+ pTextNd->GetTextAttrForCharAt( nContentPos, RES_TXTATR_FLYCNT ));
+ // attribute still in text node, delete
+ if( pAttr && pAttr->GetFlyCnt().GetFrameFormat() == pFormat )
+ {
+ // just set pointer to 0, don't delete
+ const_cast<SwFormatFlyCnt&>(pAttr->GetFlyCnt()).SetFlyFormat();
+ SwIndex aIdx( pTextNd, nContentPos );
+ pTextNd->EraseText( aIdx, 1 );
+ }
+ }
+ else if (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())
+ {
+ nContentPos = rAnchor.GetContentAnchor()->nContent.GetIndex();
+ }
+
+ pFormat->SetFormatAttr( SwFormatAnchor( rAnchor.GetAnchorId(), nContentPos ) );
+ }
+}
+
+static void lcl_RestoreAnchor( SwFrameFormat* pFormat, sal_uLong nNodePos )
+{
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()))
+ {
+ const sal_Int32 nContentPos = rAnchor.GetPageNum();
+ SwNodes& rNds = pFormat->GetDoc()->GetNodes();
+
+ SwNodeIndex aIdx( rNds, nNodePos );
+ SwPosition aPos( aIdx );
+
+ SwFormatAnchor aTmp( rAnchor.GetAnchorId() );
+ if ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()))
+ {
+ aPos.nContent.Assign( aIdx.GetNode().GetContentNode(), nContentPos );
+ }
+ aTmp.SetAnchor( &aPos );
+ RndStdIds nAnchorId = rAnchor.GetAnchorId();
+ pFormat->SetFormatAttr( aTmp );
+
+ if (RndStdIds::FLY_AS_CHAR == nAnchorId)
+ {
+ SwTextNode *pTextNd = aIdx.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "no Text Node" );
+ SwFormatFlyCnt aFormat( pFormat );
+ pTextNd->InsertItem( aFormat, nContentPos, nContentPos );
+ }
+ }
+}
+
+SwUndoDrawGroup::SwUndoDrawGroup( sal_uInt16 nCnt, const SwDoc* pDoc )
+ : SwUndo( SwUndoId::DRAWGROUP, pDoc ), m_nSize( nCnt + 1 ), m_bDeleteFormat( true )
+{
+ m_pObjArray.reset( new SwUndoGroupObjImpl[ m_nSize ] );
+}
+
+SwUndoDrawGroup::~SwUndoDrawGroup()
+{
+ if( m_bDeleteFormat )
+ {
+ SwUndoGroupObjImpl* pTmp = m_pObjArray.get() + 1;
+ for( sal_uInt16 n = 1; n < m_nSize; ++n, ++pTmp )
+ delete pTmp->pFormat;
+ }
+ else
+ delete m_pObjArray[0].pFormat;
+}
+
+void SwUndoDrawGroup::UndoImpl(::sw::UndoRedoContext &)
+{
+ m_bDeleteFormat = false;
+
+ // save group object
+ SwDrawFrameFormat* pFormat = m_pObjArray[0].pFormat;
+
+ pFormat->CallSwClientNotify(sw::ContactChangedHint(&m_pObjArray[0].pObj));
+ auto pObj = m_pObjArray[0].pObj;
+ pObj->SetUserCall(nullptr);
+
+ ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx );
+
+ // notify UNO objects to decouple
+ ::lcl_SendRemoveToUno( *pFormat );
+
+ // remove from array
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+
+ for( sal_uInt16 n = 1; n < m_nSize; ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+
+ ::lcl_RestoreAnchor( rSave.pFormat, rSave.nNodeIdx );
+ rFlyFormats.push_back( rSave.pFormat );
+
+ pObj = rSave.pObj;
+
+ SwDrawContact *pContact = new SwDrawContact( rSave.pFormat, pObj );
+ pContact->ConnectToLayout();
+ // #i45718# - follow-up of #i35635# move object to visible layer
+ pContact->MoveObjToVisibleLayer( pObj );
+
+ SwDrawFrameFormat* pDrawFrameFormat = rSave.pFormat;
+
+ // #i45952# - notify that position attributes are already set
+ OSL_ENSURE(pDrawFrameFormat,
+ "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object");
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+ }
+}
+
+void SwUndoDrawGroup::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_bDeleteFormat = true;
+
+ // remove from array
+ SwDoc* pDoc = m_pObjArray[0].pFormat->GetDoc();
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+
+ for( sal_uInt16 n = 1; n < m_nSize; ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+
+ SdrObject* pObj = rSave.pObj;
+
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+
+ // object will destroy itself
+ pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ ::lcl_SaveAnchor( rSave.pFormat, rSave.nNodeIdx );
+
+ // notify UNO objects to decouple
+ ::lcl_SendRemoveToUno( *rSave.pFormat );
+
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), rSave.pFormat ));
+ }
+
+ // re-insert group object
+ ::lcl_RestoreAnchor( m_pObjArray[0].pFormat, m_pObjArray[0].nNodeIdx );
+ rFlyFormats.push_back( m_pObjArray[0].pFormat );
+
+ SwDrawContact *pContact = new SwDrawContact( m_pObjArray[0].pFormat, m_pObjArray[0].pObj );
+ // #i26791# - correction: connect object to layout
+ pContact->ConnectToLayout();
+ // #i45718# - follow-up of #i35635# move object to visible layer
+ pContact->MoveObjToVisibleLayer( m_pObjArray[0].pObj );
+
+ SwDrawFrameFormat* pDrawFrameFormat = m_pObjArray[0].pFormat;
+
+ // #i45952# - notify that position attributes are already set
+ OSL_ENSURE(pDrawFrameFormat,
+ "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object");
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+}
+
+void SwUndoDrawGroup::AddObj( sal_uInt16 nPos, SwDrawFrameFormat* pFormat, SdrObject* pObj )
+{
+ SwUndoGroupObjImpl& rSave = m_pObjArray[nPos + 1];
+ rSave.pObj = pObj;
+ rSave.pFormat = pFormat;
+ ::lcl_SaveAnchor( pFormat, rSave.nNodeIdx );
+
+ // notify UNO objects to decouple
+ ::lcl_SendRemoveToUno( *pFormat );
+
+ // remove from array
+ SwFrameFormats& rFlyFormats = *pFormat->GetDoc()->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+}
+
+void SwUndoDrawGroup::SetGroupFormat( SwDrawFrameFormat* pFormat )
+{
+ m_pObjArray[0].pObj = nullptr;
+ m_pObjArray[0].pFormat = pFormat;
+}
+
+SwUndoDrawUnGroup::SwUndoDrawUnGroup( SdrObjGroup* pObj, const SwDoc* pDoc )
+ : SwUndo( SwUndoId::DRAWUNGROUP, pDoc ), m_bDeleteFormat( false )
+{
+ m_nSize = static_cast<sal_uInt16>(pObj->GetSubList()->GetObjCount()) + 1;
+ m_pObjArray.reset( new SwUndoGroupObjImpl[ m_nSize ] );
+
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ SwDrawFrameFormat* pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat());
+
+ m_pObjArray[0].pObj = pObj;
+ m_pObjArray[0].pFormat = pFormat;
+
+ // object will destroy itself
+ pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx );
+
+ // notify UNO objects to decouple
+ ::lcl_SendRemoveToUno( *pFormat );
+
+ // remove from array
+ SwFrameFormats& rFlyFormats = *pFormat->GetDoc()->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+}
+
+SwUndoDrawUnGroup::~SwUndoDrawUnGroup()
+{
+ if( m_bDeleteFormat )
+ {
+ SwUndoGroupObjImpl* pTmp = m_pObjArray.get() + 1;
+ for( sal_uInt16 n = 1; n < m_nSize; ++n, ++pTmp )
+ delete pTmp->pFormat;
+ }
+ else
+ delete m_pObjArray[0].pFormat;
+}
+
+void SwUndoDrawUnGroup::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_bDeleteFormat = true;
+
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+
+ // remove from array
+ for( sal_uInt16 n = 1; n < m_nSize; ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+
+ ::lcl_SaveAnchor( rSave.pFormat, rSave.nNodeIdx );
+
+ // notify UNO objects to decouple
+ ::lcl_SendRemoveToUno( *rSave.pFormat );
+
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), rSave.pFormat ));
+ }
+
+ // re-insert group object
+ ::lcl_RestoreAnchor( m_pObjArray[0].pFormat, m_pObjArray[0].nNodeIdx );
+ rFlyFormats.push_back( m_pObjArray[0].pFormat );
+
+ SwDrawContact *pContact = new SwDrawContact( m_pObjArray[0].pFormat, m_pObjArray[0].pObj );
+ pContact->ConnectToLayout();
+ // #i45718# - follow-up of #i35635# move object to visible layer
+ pContact->MoveObjToVisibleLayer( m_pObjArray[0].pObj );
+
+ SwDrawFrameFormat* pDrawFrameFormat = m_pObjArray[0].pFormat;
+
+ // #i45952# - notify that position attributes are already set
+ OSL_ENSURE(pDrawFrameFormat,
+ "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object");
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+}
+
+void SwUndoDrawUnGroup::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_bDeleteFormat = false;
+
+ // save group object
+ SwDrawFrameFormat* pFormat = m_pObjArray[0].pFormat;
+ pFormat->CallSwClientNotify(sw::ContactChangedHint(&(m_pObjArray[0].pObj)));
+ m_pObjArray[0].pObj->SetUserCall( nullptr );
+
+ ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx );
+
+ // notify UNO objects to decouple
+ ::lcl_SendRemoveToUno( *pFormat );
+
+ // remove from array
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+
+ for( sal_uInt16 n = 1; n < m_nSize; ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+
+ ::lcl_RestoreAnchor( rSave.pFormat, rSave.nNodeIdx );
+ rFlyFormats.push_back( rSave.pFormat );
+
+ SwDrawFrameFormat* pDrawFrameFormat = rSave.pFormat;
+
+ // #i45952# - notify that position attributes are already set
+ OSL_ENSURE(pDrawFrameFormat,
+ "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object" );
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+ }
+}
+
+void SwUndoDrawUnGroup::AddObj( sal_uInt16 nPos, SwDrawFrameFormat* pFormat )
+{
+ SwUndoGroupObjImpl& rSave = m_pObjArray[ nPos + 1 ];
+ rSave.pFormat = pFormat;
+ rSave.pObj = nullptr;
+}
+
+SwUndoDrawUnGroupConnectToLayout::SwUndoDrawUnGroupConnectToLayout(const SwDoc* pDoc)
+ : SwUndo( SwUndoId::DRAWUNGROUP, pDoc )
+{
+}
+
+SwUndoDrawUnGroupConnectToLayout::~SwUndoDrawUnGroupConnectToLayout()
+{
+}
+
+void
+SwUndoDrawUnGroupConnectToLayout::UndoImpl(::sw::UndoRedoContext &)
+{
+ for (const std::pair< SwDrawFrameFormat*, SdrObject* > & rPair : m_aDrawFormatsAndObjs)
+ {
+ SdrObject* pObj( rPair.second );
+ SwDrawContact* pDrawContact( dynamic_cast<SwDrawContact*>(pObj->GetUserCall()) );
+ OSL_ENSURE( pDrawContact,
+ "<SwUndoDrawUnGroupConnectToLayout::Undo(..)> -- missing SwDrawContact instance" );
+ if ( pDrawContact )
+ {
+ // deletion of instance <pDrawContact> and thus disconnection from
+ // the Writer layout.
+ pDrawContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+ }
+ }
+}
+
+void
+SwUndoDrawUnGroupConnectToLayout::RedoImpl(::sw::UndoRedoContext &)
+{
+ for (const std::pair< SwDrawFrameFormat*, SdrObject* > & rPair : m_aDrawFormatsAndObjs)
+ {
+ SwDrawFrameFormat* pFormat( rPair.first );
+ SdrObject* pObj( rPair.second );
+ SwDrawContact *pContact = new SwDrawContact( pFormat, pObj );
+ pContact->ConnectToLayout();
+ pContact->MoveObjToVisibleLayer( pObj );
+ }
+}
+
+void SwUndoDrawUnGroupConnectToLayout::AddFormatAndObj( SwDrawFrameFormat* pDrawFrameFormat,
+ SdrObject* pDrawObject )
+{
+ m_aDrawFormatsAndObjs.emplace_back( pDrawFrameFormat, pDrawObject );
+}
+
+SwUndoDrawDelete::SwUndoDrawDelete( sal_uInt16 nCnt, const SwDoc* pDoc )
+ : SwUndo( SwUndoId::DRAWDELETE, pDoc ), m_bDeleteFormat( true )
+{
+ m_pObjArray.reset( new SwUndoGroupObjImpl[ nCnt ] );
+ m_pMarkList.reset( new SdrMarkList() );
+}
+
+SwUndoDrawDelete::~SwUndoDrawDelete()
+{
+ if( m_bDeleteFormat )
+ {
+ SwUndoGroupObjImpl* pTmp = m_pObjArray.get();
+ for( size_t n = 0; n < m_pMarkList->GetMarkCount(); ++n, ++pTmp )
+ delete pTmp->pFormat;
+ }
+}
+
+void SwUndoDrawDelete::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_bDeleteFormat = false;
+ SwFrameFormats & rFlyFormats = *rContext.GetDoc().GetSpzFrameFormats();
+ for( size_t n = 0; n < m_pMarkList->GetMarkCount(); ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+ ::lcl_RestoreAnchor( rSave.pFormat, rSave.nNodeIdx );
+ rFlyFormats.push_back( rSave.pFormat );
+ SdrObject *pObj = rSave.pObj;
+ SwDrawContact *pContact = new SwDrawContact( rSave.pFormat, pObj );
+ pContact->Changed_( *pObj, SdrUserCallType::Inserted, nullptr );
+ // #i45718# - follow-up of #i35635# move object to visible layer
+ pContact->MoveObjToVisibleLayer( pObj );
+
+ SwDrawFrameFormat* pDrawFrameFormat = rSave.pFormat;
+
+ // #i45952# - notify that position attributes are already set
+ OSL_ENSURE(pDrawFrameFormat,
+ "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object");
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+ }
+ rContext.SetSelections(nullptr, m_pMarkList.get());
+}
+
+void SwUndoDrawDelete::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_bDeleteFormat = true;
+ SwFrameFormats & rFlyFormats = *rContext.GetDoc().GetSpzFrameFormats();
+ for( size_t n = 0; n < m_pMarkList->GetMarkCount(); ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+ SdrObject *pObj = rSave.pObj;
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ SwDrawFrameFormat *pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat());
+
+ // object will destroy itself
+ pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ // notify UNO objects to decouple
+ ::lcl_SendRemoveToUno( *pFormat );
+
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+ ::lcl_SaveAnchor( pFormat, rSave.nNodeIdx );
+ }
+}
+
+void SwUndoDrawDelete::AddObj( SwDrawFrameFormat* pFormat,
+ const SdrMark& rMark )
+{
+ SwUndoGroupObjImpl& rSave = m_pObjArray[ m_pMarkList->GetMarkCount() ];
+ rSave.pObj = rMark.GetMarkedSdrObj();
+ rSave.pFormat = pFormat;
+ ::lcl_SaveAnchor( pFormat, rSave.nNodeIdx );
+
+ // notify UNO objects to decouple
+ ::lcl_SendRemoveToUno( *pFormat );
+
+ // remove from array
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+
+ m_pMarkList->InsertEntry( rMark );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unfmco.cxx b/sw/source/core/undo/unfmco.cxx
new file mode 100644
index 000000000..118cf98f3
--- /dev/null
+++ b/sw/source/core/undo/unfmco.cxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+
+SwUndoFormatColl::SwUndoFormatColl( const SwPaM& rRange,
+ const SwFormatColl* pColl,
+ const bool bReset,
+ const bool bResetListAttrs )
+ : SwUndo( SwUndoId::SETFMTCOLL, rRange.GetDoc() ),
+ SwUndRng( rRange ),
+ mpHistory( new SwHistory ),
+ mbReset( bReset ),
+ mbResetListAttrs( bResetListAttrs )
+{
+ // #i31191#
+ if ( pColl )
+ maFormatName = pColl->GetName();
+}
+
+SwUndoFormatColl::~SwUndoFormatColl()
+{
+}
+
+void SwUndoFormatColl::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ // restore old values
+ mpHistory->TmpRollback(& rContext.GetDoc(), 0);
+ mpHistory->SetTmpEnd( mpHistory->Count() );
+
+ // create cursor for undo range
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoFormatColl::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+
+ DoSetFormatColl(rContext.GetDoc(), rPam);
+}
+
+void SwUndoFormatColl::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ DoSetFormatColl(rContext.GetDoc(), rContext.GetRepeatPaM());
+}
+
+void SwUndoFormatColl::DoSetFormatColl(SwDoc & rDoc, SwPaM const & rPaM)
+{
+ // Only one TextFrameColl can be applied to a section, thus request only in
+ // this array.
+ SwTextFormatColl * pFormatColl = rDoc.FindTextFormatCollByName(maFormatName);
+ if (pFormatColl)
+ {
+ rDoc.SetTextFormatColl(rPaM, pFormatColl, mbReset, mbResetListAttrs);
+ }
+}
+
+SwRewriter SwUndoFormatColl::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, maFormatName );
+
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unins.cxx b/sw/source/core/undo/unins.cxx
new file mode 100644
index 000000000..6e08fb502
--- /dev/null
+++ b/sw/source/core/undo/unins.cxx
@@ -0,0 +1,1045 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoInsert.hxx>
+
+#include <hintids.hxx>
+#include <unotools/charclass.hxx>
+#include <sot/storage.hxx>
+#include <editeng/keepitem.hxx>
+#include <svx/svdobj.hxx>
+
+#include <fmtcntnt.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <UndoDelete.hxx>
+#include <UndoAttribute.hxx>
+#include <rolbck.hxx>
+#include <ndgrf.hxx>
+#include <ndole.hxx>
+#include <grfatr.hxx>
+#include <cntfrm.hxx>
+#include <flyfrm.hxx>
+#include <swtable.hxx>
+#include <redline.hxx>
+#include <docary.hxx>
+#include <acorrect.hxx>
+
+#include <strings.hrc>
+
+using namespace ::com::sun::star;
+
+// INSERT
+
+std::optional<OUString> SwUndoInsert::GetTextFromDoc() const
+{
+ std::optional<OUString> aResult;
+
+ SwNodeIndex aNd( m_pDoc->GetNodes(), m_nNode);
+ SwContentNode* pCNd = aNd.GetNode().GetContentNode();
+
+ if( pCNd->IsTextNode() )
+ {
+ OUString sText = pCNd->GetTextNode()->GetText();
+
+ sal_Int32 nStart = m_nContent-m_nLen;
+ sal_Int32 nLength = m_nLen;
+
+ if (nStart < 0)
+ {
+ nLength += nStart;
+ nStart = 0;
+ }
+
+ aResult = sText.copy(nStart, nLength);
+ }
+
+ return aResult;
+}
+
+void SwUndoInsert::Init(const SwNodeIndex & rNd)
+{
+ // consider Redline
+ m_pDoc = rNd.GetNode().GetDoc();
+ if( m_pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlData.reset( new SwRedlineData( RedlineType::Insert,
+ m_pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ maUndoText = GetTextFromDoc();
+
+ m_bCacheComment = false;
+}
+
+SwUndoInsert::SwUndoInsert( const SwNodeIndex& rNd, sal_Int32 nCnt,
+ sal_Int32 nL,
+ const SwInsertFlags nInsertFlags,
+ bool bWDelim )
+ : SwUndo(SwUndoId::TYPING, rNd.GetNode().GetDoc()),
+ m_nNode( rNd.GetIndex() ), m_nContent(nCnt), m_nLen(nL),
+ m_bIsWordDelim( bWDelim ), m_bIsAppend( false )
+ , m_bWithRsid(false)
+ , m_nInsertFlags(nInsertFlags)
+{
+ Init(rNd);
+}
+
+SwUndoInsert::SwUndoInsert( const SwNodeIndex& rNd )
+ : SwUndo(SwUndoId::SPLITNODE, rNd.GetNode().GetDoc()),
+ m_nNode( rNd.GetIndex() ), m_nContent(0), m_nLen(1),
+ m_bIsWordDelim( false ), m_bIsAppend( true )
+ , m_bWithRsid(false)
+ , m_nInsertFlags(SwInsertFlags::EMPTYEXPAND)
+{
+ Init(rNd);
+}
+
+// Check if the next Insert can be combined with the current one. If so
+// change the length and InsPos. As a result, SwDoc::Inser will not add a
+// new object into the Undo list.
+
+bool SwUndoInsert::CanGrouping( sal_Unicode cIns )
+{
+ if( !m_bIsAppend && m_bIsWordDelim ==
+ !GetAppCharClass().isLetterNumeric( OUString( cIns )) )
+ {
+ m_nLen++;
+ m_nContent++;
+
+ if (maUndoText)
+ (*maUndoText) += OUStringChar(cIns);
+
+ return true;
+ }
+ return false;
+}
+
+bool SwUndoInsert::CanGrouping( const SwPosition& rPos )
+{
+ bool bRet = false;
+ if( m_nNode == rPos.nNode.GetIndex() &&
+ m_nContent == rPos.nContent.GetIndex() )
+ {
+ // consider Redline
+ SwDoc& rDoc = *rPos.nNode.GetNode().GetDoc();
+ if( ( ~RedlineFlags::ShowMask & rDoc.getIDocumentRedlineAccess().GetRedlineFlags() ) ==
+ ( ~RedlineFlags::ShowMask & GetRedlineFlags() ) )
+ {
+ bRet = true;
+
+ // then there is or was still an active Redline:
+ // Check if there is another Redline at the InsPosition. If the
+ // same exists only once, it can be combined.
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ if( !rTable.empty() )
+ {
+ SwRedlineData aRData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() );
+ const SwIndexReg* pIReg = rPos.nContent.GetIdxReg();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ SwIndex* pIdx = &pRedl->End()->nContent;
+ if( pIReg == pIdx->GetIdxReg() &&
+ m_nContent == pIdx->GetIndex() )
+ {
+ if( !pRedl->HasMark() || !m_pRedlData ||
+ *pRedl != *m_pRedlData || *pRedl != aRData )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+SwUndoInsert::~SwUndoInsert()
+{
+ if (m_pUndoNodeIndex) // delete the section from UndoNodes array
+ {
+ // Insert saves the content in IconSection
+ SwNodes& rUNds = m_pUndoNodeIndex->GetNodes();
+ rUNds.Delete(*m_pUndoNodeIndex,
+ rUNds.GetEndOfExtras().GetIndex() - m_pUndoNodeIndex->GetIndex());
+ m_pUndoNodeIndex.reset();
+ }
+ else // the inserted text
+ {
+ maText.reset();
+ }
+ m_pRedlData.reset();
+}
+
+void SwUndoInsert::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pTmpDoc = & rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ if( m_bIsAppend )
+ {
+ pPam->GetPoint()->nNode = m_nNode;
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ pPam->GetPoint()->nContent.Assign( pPam->GetContentNode(), 0 );
+ pPam->SetMark();
+ pPam->Move( fnMoveBackward );
+ pPam->Exchange();
+ pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( *pPam, true, RedlineType::Any );
+ }
+ pPam->DeleteMark();
+ pTmpDoc->getIDocumentContentOperations().DelFullPara( *pPam );
+ pPam->GetPoint()->nContent.Assign( pPam->GetContentNode(), 0 );
+ }
+ else
+ {
+ sal_uLong nNd = m_nNode;
+ sal_Int32 nCnt = m_nContent;
+ if( m_nLen )
+ {
+ SwNodeIndex aNd( pTmpDoc->GetNodes(), m_nNode);
+ SwContentNode* pCNd = aNd.GetNode().GetContentNode();
+ SwPaM aPaM( *pCNd, m_nContent );
+
+ aPaM.SetMark();
+
+ SwTextNode * const pTextNode( pCNd->GetTextNode() );
+ if ( pTextNode )
+ {
+ aPaM.GetPoint()->nContent -= m_nLen;
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( aPaM, true, RedlineType::Any );
+ if (m_bWithRsid)
+ {
+ // RSID was added: remove any CHARFMT/AUTOFMT that may be
+ // set on the deleted text; EraseText will leave empty
+ // ones behind otherwise
+ pTextNode->DeleteAttributes(RES_TXTATR_AUTOFMT,
+ aPaM.GetPoint()->nContent.GetIndex(),
+ aPaM.GetMark()->nContent.GetIndex());
+ pTextNode->DeleteAttributes(RES_TXTATR_CHARFMT,
+ aPaM.GetPoint()->nContent.GetIndex(),
+ aPaM.GetMark()->nContent.GetIndex());
+ }
+ RemoveIdxFromRange( aPaM, false );
+ maText = pTextNode->GetText().copy(m_nContent-m_nLen, m_nLen);
+ pTextNode->EraseText( aPaM.GetPoint()->nContent, m_nLen );
+ }
+ else // otherwise Graphics/OLE/Text/...
+ {
+ aPaM.Move(fnMoveBackward);
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( aPaM, true, RedlineType::Any );
+ RemoveIdxFromRange( aPaM, false );
+ }
+
+ nNd = aPaM.GetPoint()->nNode.GetIndex();
+ nCnt = aPaM.GetPoint()->nContent.GetIndex();
+
+ if (!maText)
+ {
+ m_pUndoNodeIndex.reset(
+ new SwNodeIndex(m_pDoc->GetNodes().GetEndOfContent()));
+ MoveToUndoNds(aPaM, m_pUndoNodeIndex.get());
+ }
+ m_nNode = aPaM.GetPoint()->nNode.GetIndex();
+ m_nContent = aPaM.GetPoint()->nContent.GetIndex();
+ }
+
+ // set cursor to Undo range
+ pPam->DeleteMark();
+
+ pPam->GetPoint()->nNode = nNd;
+ pPam->GetPoint()->nContent.Assign(
+ pPam->GetPoint()->nNode.GetNode().GetContentNode(), nCnt );
+ }
+
+ maUndoText.reset();
+}
+
+void SwUndoInsert::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pTmpDoc = & rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ pPam->DeleteMark();
+
+ if( m_bIsAppend )
+ {
+ pPam->GetPoint()->nNode = m_nNode - 1;
+ pTmpDoc->getIDocumentContentOperations().AppendTextNode( *pPam->GetPoint() );
+
+ pPam->SetMark();
+ pPam->Move( fnMoveBackward );
+ pPam->Exchange();
+
+ if( m_pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ RedlineFlags eOld = pTmpDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
+ pTmpDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData, *pPam ), true);
+ pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !pTmpDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ pTmpDoc->getIDocumentRedlineAccess().SplitRedline( *pPam );
+
+ pPam->DeleteMark();
+ }
+ else
+ {
+ pPam->GetPoint()->nNode = m_nNode;
+ SwContentNode *const pCNd =
+ pPam->GetPoint()->nNode.GetNode().GetContentNode();
+ pPam->GetPoint()->nContent.Assign( pCNd, m_nContent );
+
+ if( m_nLen )
+ {
+ const bool bMvBkwrd = MovePtBackward( *pPam );
+
+ if (maText)
+ {
+ SwTextNode *const pTextNode = pCNd->GetTextNode();
+ OSL_ENSURE( pTextNode, "where is my textnode ?" );
+ OUString const ins(
+ pTextNode->InsertText( *maText, pPam->GetMark()->nContent,
+ m_nInsertFlags) );
+ assert(ins.getLength() == maText->getLength()); // must succeed
+ maText.reset();
+ if (m_bWithRsid) // re-insert RSID
+ {
+ SwPaM pam(*pPam->GetMark(), nullptr); // mark -> point
+ pTmpDoc->UpdateRsid(pam, ins.getLength());
+ }
+ }
+ else
+ {
+ // re-insert content again (first detach m_pUndoNodeIndex!)
+ sal_uLong const nMvNd = m_pUndoNodeIndex->GetIndex();
+ m_pUndoNodeIndex.reset();
+ MoveFromUndoNds(*pTmpDoc, nMvNd, *pPam->GetMark());
+ }
+ m_nNode = pPam->GetMark()->nNode.GetIndex();
+ m_nContent = pPam->GetMark()->nContent.GetIndex();
+
+ MovePtForward( *pPam, bMvBkwrd );
+ pPam->Exchange();
+ if( m_pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ RedlineFlags eOld = pTmpDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
+ pTmpDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData,
+ *pPam ), true);
+ pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !pTmpDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ pTmpDoc->getIDocumentRedlineAccess().SplitRedline(*pPam);
+ }
+ }
+
+ maUndoText = GetTextFromDoc();
+}
+
+void SwUndoInsert::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ if( !m_nLen )
+ return;
+
+ SwDoc & rDoc = rContext.GetDoc();
+ SwNodeIndex aNd( rDoc.GetNodes(), m_nNode );
+ SwContentNode* pCNd = aNd.GetNode().GetContentNode();
+
+ if( !m_bIsAppend && 1 == m_nLen ) // >1 than always Text, otherwise Graphics/OLE/Text/...
+ {
+ SwPaM aPaM( *pCNd, m_nContent );
+ aPaM.SetMark();
+ aPaM.Move(fnMoveBackward);
+ pCNd = aPaM.GetContentNode();
+ }
+
+// What happens with the possible selected range ???
+
+ switch( pCNd->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ if( m_bIsAppend )
+ {
+ rDoc.getIDocumentContentOperations().AppendTextNode( *rContext.GetRepeatPaM().GetPoint() );
+ }
+ else
+ {
+ OUString const aText( pCNd->GetTextNode()->GetText() );
+ ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().InsertString( rContext.GetRepeatPaM(),
+ aText.copy(m_nContent - m_nLen, m_nLen) );
+ }
+ break;
+ case SwNodeType::Grf:
+ {
+ SwGrfNode* pGrfNd = static_cast<SwGrfNode*>(pCNd);
+ OUString sFile;
+ OUString sFilter;
+ if( pGrfNd->IsGrfLink() )
+ pGrfNd->GetFileFilterNms( &sFile, &sFilter );
+
+ rDoc.getIDocumentContentOperations().InsertGraphic(
+ rContext.GetRepeatPaM(), sFile, sFilter,
+ &pGrfNd->GetGrf(),
+ nullptr/* Graphics collection*/, nullptr, nullptr );
+ }
+ break;
+
+ case SwNodeType::Ole:
+ {
+ // StarView does not yet provide an option to copy a StarOBJ
+ SwOLEObj& rSwOLE = static_cast<SwOLENode*>(pCNd)->GetOLEObj();
+
+ // temporary storage until object is inserted
+ // TODO/MBA: seems that here a physical copy is done - not as in drawing layer! Testing!
+ // TODO/LATER: Copying through the container would copy the replacement image as well
+ comphelper::EmbeddedObjectContainer aCnt;
+ OUString aName = aCnt.CreateUniqueObjectName();
+ if (aCnt.StoreEmbeddedObject(rSwOLE.GetOleRef(), aName, true, OUString(), OUString()))
+ {
+ uno::Reference < embed::XEmbeddedObject > aNew = aCnt.GetEmbeddedObject( aName );
+ rDoc.getIDocumentContentOperations().InsertEmbObject(
+ rContext.GetRepeatPaM(),
+ svt::EmbeddedObjectRef( aNew,
+ static_cast<SwOLENode*>(pCNd)->GetAspect() ),
+ nullptr );
+ }
+
+ break;
+ }
+
+ default: break;
+ }
+}
+
+SwRewriter SwUndoInsert::GetRewriter() const
+{
+ SwRewriter aResult;
+ std::optional<OUString> aStr;
+ bool bDone = false;
+
+ if (maText)
+ aStr = maText;
+ else if (maUndoText)
+ aStr = maUndoText;
+
+ if (aStr)
+ {
+ OUString aString = ShortenString(DenoteSpecialCharacters(*aStr),
+ nUndoStringLength,
+ SwResId(STR_LDOTS));
+
+ aResult.AddRule(UndoArg1, aString);
+
+ bDone = true;
+ }
+
+ if ( ! bDone )
+ {
+ aResult.AddRule(UndoArg1, "??");
+ }
+
+ return aResult;
+}
+
+class SwUndoReplace::Impl
+ : private SwUndoSaveContent
+{
+ OUString m_sOld;
+ OUString m_sIns;
+ sal_uLong m_nSttNd, m_nEndNd, m_nOffset;
+ sal_Int32 m_nSttCnt, m_nEndCnt, m_nSetPos, m_nSelEnd;
+ bool m_bSplitNext : 1;
+ bool m_bRegExp : 1;
+ // metadata references for paragraph and following para (if m_bSplitNext)
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoStart;
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoEnd;
+
+public:
+ Impl(SwPaM const& rPam, OUString const& rIns, bool const bRegExp);
+ virtual ~Impl()
+ {
+ }
+
+ void UndoImpl( ::sw::UndoRedoContext & );
+ void RedoImpl( ::sw::UndoRedoContext & );
+
+ void SetEnd(SwPaM const& rPam);
+
+ OUString const& GetOld() const { return m_sOld; }
+ OUString const& GetIns() const { return m_sIns; }
+};
+
+SwUndoReplace::SwUndoReplace(SwPaM const& rPam,
+ OUString const& rIns, bool const bRegExp)
+ : SwUndo( SwUndoId::REPLACE, rPam.GetDoc() )
+ , m_pImpl(std::make_unique<Impl>(rPam, rIns, bRegExp))
+{
+}
+
+SwUndoReplace::~SwUndoReplace()
+{
+}
+
+void SwUndoReplace::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pImpl->UndoImpl(rContext);
+}
+
+void SwUndoReplace::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pImpl->RedoImpl(rContext);
+}
+
+SwRewriter
+MakeUndoReplaceRewriter(sal_uLong const occurrences,
+ OUString const& sOld, OUString const& sNew)
+{
+ SwRewriter aResult;
+
+ if (1 < occurrences)
+ {
+ aResult.AddRule(UndoArg1, OUString::number(occurrences));
+ aResult.AddRule(UndoArg2, SwResId(STR_OCCURRENCES_OF));
+
+ OUString aTmpStr = SwResId(STR_START_QUOTE);
+ aTmpStr += ShortenString(sOld, nUndoStringLength,
+ SwResId(STR_LDOTS));
+ aTmpStr += SwResId(STR_END_QUOTE);
+ aResult.AddRule(UndoArg3, aTmpStr);
+ }
+ else if (1 == occurrences)
+ {
+ {
+ OUString aTmpStr = SwResId(STR_START_QUOTE);
+ // #i33488 #
+ aTmpStr += ShortenString(sOld, nUndoStringLength,
+ SwResId(STR_LDOTS));
+ aTmpStr += SwResId(STR_END_QUOTE);
+ aResult.AddRule(UndoArg1, aTmpStr);
+ }
+
+ aResult.AddRule(UndoArg2, SwResId(STR_YIELDS));
+
+ {
+ OUString aTmpStr = SwResId(STR_START_QUOTE);
+ // #i33488 #
+ aTmpStr += ShortenString(sNew, nUndoStringLength,
+ SwResId(STR_LDOTS));
+ aTmpStr += SwResId(STR_END_QUOTE);
+ aResult.AddRule(UndoArg3, aTmpStr);
+ }
+ }
+
+ return aResult;
+}
+
+SwRewriter SwUndoReplace::GetRewriter() const
+{
+ return MakeUndoReplaceRewriter(1, m_pImpl->GetOld(), m_pImpl->GetIns());
+}
+
+void SwUndoReplace::SetEnd(SwPaM const& rPam)
+{
+ m_pImpl->SetEnd(rPam);
+}
+
+SwUndoReplace::Impl::Impl(
+ SwPaM const& rPam, OUString const& rIns, bool const bRegExp)
+ : m_sIns( rIns )
+ , m_nOffset( 0 )
+ , m_bRegExp(bRegExp)
+{
+
+ const SwPosition * pStt( rPam.Start() );
+ const SwPosition * pEnd( rPam.End() );
+
+ m_nSttNd = m_nEndNd = pStt->nNode.GetIndex();
+ m_nSttCnt = pStt->nContent.GetIndex();
+ m_nSelEnd = m_nEndCnt = pEnd->nContent.GetIndex();
+
+ m_bSplitNext = m_nSttNd != pEnd->nNode.GetIndex();
+
+ SwTextNode* pNd = pStt->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pNd, "Dude, where's my TextNode?" );
+
+ m_pHistory.reset( new SwHistory );
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace);
+
+ m_nSetPos = m_pHistory->Count();
+
+ sal_uLong nNewPos = pStt->nNode.GetIndex();
+ m_nOffset = m_nSttNd - nNewPos;
+
+ if ( pNd->GetpSwpHints() )
+ {
+ m_pHistory->CopyAttr( pNd->GetpSwpHints(), nNewPos, 0,
+ pNd->GetText().getLength(), true );
+ }
+
+ if ( m_bSplitNext )
+ {
+ if( pNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pNd->GetpSwAttrSet(), nNewPos );
+ m_pHistory->Add( pNd->GetTextColl(), nNewPos, SwNodeType::Text );
+
+ SwTextNode* pNext = pEnd->nNode.GetNode().GetTextNode();
+ sal_uLong nTmp = pNext->GetIndex();
+ m_pHistory->CopyAttr( pNext->GetpSwpHints(), nTmp, 0,
+ pNext->GetText().getLength(), true );
+ if( pNext->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pNext->GetpSwAttrSet(), nTmp );
+ m_pHistory->Add( pNext->GetTextColl(),nTmp, SwNodeType::Text );
+ // METADATA: store
+ m_pMetadataUndoStart = pNd ->CreateUndo();
+ m_pMetadataUndoEnd = pNext->CreateUndo();
+ }
+
+ if( !m_pHistory->Count() )
+ {
+ m_pHistory.reset();
+ }
+
+ const sal_Int32 nECnt = m_bSplitNext ? pNd->GetText().getLength()
+ : pEnd->nContent.GetIndex();
+ m_sOld = pNd->GetText().copy( m_nSttCnt, nECnt - m_nSttCnt );
+}
+
+void SwUndoReplace::Impl::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM & rPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+ rPam.DeleteMark();
+
+ SwTextNode* pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode();
+ OSL_ENSURE( pNd, "Dude, where's my TextNode?" );
+
+ SwAutoCorrExceptWord* pACEWord = pDoc->GetAutoCorrExceptWord();
+ if( pACEWord )
+ {
+ if ((1 == m_sIns.getLength()) && (1 == m_sOld.getLength()))
+ {
+ SwPosition aPos( *pNd ); aPos.nContent.Assign( pNd, m_nSttCnt );
+ pACEWord->CheckChar( aPos, m_sOld[ 0 ] );
+ }
+ pDoc->SetAutoCorrExceptWord( nullptr );
+ }
+
+ // don't look at m_sIns for deletion, maybe it was not completely inserted
+ {
+ rPam.GetPoint()->nNode = *pNd;
+ rPam.GetPoint()->nContent.Assign( pNd, m_nSttCnt );
+ rPam.SetMark();
+ rPam.GetPoint()->nNode = m_nSttNd - m_nOffset;
+ rPam.GetPoint()->nContent.Assign(rPam.GetContentNode(), m_nSttNd == m_nEndNd ? m_nEndCnt : pNd->Len());
+
+ // replace only in start node, without regex
+ bool const ret = pDoc->getIDocumentContentOperations().ReplaceRange(rPam, m_sOld, false);
+ assert(ret); (void)ret;
+ if (m_nSttNd != m_nEndNd)
+ { // in case of regex inserting paragraph breaks, join nodes...
+ assert(rPam.GetMark()->nContent == rPam.GetMark()->nNode.GetNode().GetTextNode()->Len());
+ rPam.GetPoint()->nNode = m_nEndNd - m_nOffset;
+ rPam.GetPoint()->nContent.Assign(rPam.GetContentNode(true), m_nEndCnt);
+ pDoc->getIDocumentContentOperations().DeleteAndJoin(rPam);
+ }
+ rPam.DeleteMark();
+ pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode();
+ OSL_ENSURE( pNd, "Dude, where's my TextNode?" );
+ }
+
+ if( m_bSplitNext )
+ {
+ SwPosition aPos(*pNd, pNd->Len());
+ pDoc->getIDocumentContentOperations().SplitNode( aPos, false );
+ pNd->RestoreMetadata(m_pMetadataUndoEnd);
+ pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode();
+ // METADATA: restore
+ pNd->RestoreMetadata(m_pMetadataUndoStart);
+ }
+
+ if( m_pHistory )
+ {
+ if( pNd->GetpSwpHints() )
+ pNd->ClearSwpHintsArr( true );
+
+ m_pHistory->TmpRollback( pDoc, m_nSetPos, false );
+ if ( m_nSetPos ) // there were footnotes/FlyFrames
+ {
+ // are there others than these?
+ if( m_nSetPos < m_pHistory->Count() )
+ {
+ // than save those attributes as well
+ SwHistory aHstr;
+ aHstr.Move( 0, m_pHistory.get(), m_nSetPos );
+ m_pHistory->Rollback( pDoc );
+ m_pHistory->Move( 0, &aHstr );
+ }
+ else
+ {
+ m_pHistory->Rollback( pDoc );
+ m_pHistory.reset();
+ }
+ }
+ }
+
+ rPam.GetPoint()->nNode = m_nSttNd;
+ rPam.GetPoint()->nContent = m_nSttCnt;
+}
+
+void SwUndoReplace::Impl::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = m_nSttNd;
+
+ SwTextNode* pNd = rPam.GetPoint()->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pNd, "Dude, where's my TextNode?" );
+ rPam.GetPoint()->nContent.Assign( pNd, m_nSttCnt );
+ rPam.SetMark();
+ if( m_bSplitNext )
+ {
+ rPam.GetPoint()->nNode = m_nSttNd + 1;
+ pNd = rPam.GetPoint()->nNode.GetNode().GetTextNode();
+ }
+ rPam.GetPoint()->nContent.Assign( pNd, m_nSelEnd );
+
+ if( m_pHistory )
+ {
+ auto xSave = std::make_unique<SwHistory>();
+ std::swap(m_pHistory, xSave);
+
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace);
+ m_nSetPos = m_pHistory->Count();
+
+ std::swap(xSave, m_pHistory);
+ m_pHistory->Move(0, xSave.get());
+ }
+ else
+ {
+ m_pHistory.reset( new SwHistory );
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace);
+ m_nSetPos = m_pHistory->Count();
+ if( !m_nSetPos )
+ {
+ m_pHistory.reset();
+ }
+ }
+
+ rDoc.getIDocumentContentOperations().ReplaceRange( rPam, m_sIns, m_bRegExp );
+ rPam.DeleteMark();
+}
+
+void SwUndoReplace::Impl::SetEnd(SwPaM const& rPam)
+{
+ const SwPosition* pEnd = rPam.End();
+ m_nEndNd = m_nOffset + pEnd->nNode.GetIndex();
+ m_nEndCnt = pEnd->nContent.GetIndex();
+}
+
+SwUndoReRead::SwUndoReRead( const SwPaM& rPam, const SwGrfNode& rGrfNd )
+ : SwUndo( SwUndoId::REREAD, rPam.GetDoc() ), mnPosition( rPam.GetPoint()->nNode.GetIndex() )
+{
+ SaveGraphicData( rGrfNd );
+}
+
+SwUndoReRead::~SwUndoReRead()
+{
+}
+
+void SwUndoReRead::SetAndSave(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwGrfNode* pGrfNd = rDoc.GetNodes()[ mnPosition ]->GetGrfNode();
+
+ if( !pGrfNd )
+ return ;
+
+ // cache the old values
+ std::unique_ptr<Graphic> pOldGrf( mpGraphic ? new Graphic(*mpGraphic) : nullptr);
+ std::optional<OUString> aOldNm = maNm;
+ MirrorGraph nOldMirr = mnMirror;
+ // since all of them are cleared/modified by SaveGraphicData:
+ SaveGraphicData( *pGrfNd );
+
+ if( aOldNm )
+ {
+ pGrfNd->ReRead( *aOldNm, maFltr ? *maFltr : OUString() );
+ }
+ else
+ {
+ pGrfNd->ReRead( OUString(), OUString(), pOldGrf.get() );
+ }
+
+ if( MirrorGraph::Dont != nOldMirr )
+ pGrfNd->SetAttr( SwMirrorGrf() );
+
+ rContext.SetSelections(pGrfNd->GetFlyFormat(), nullptr);
+}
+
+void SwUndoReRead::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SetAndSave(rContext);
+}
+
+void SwUndoReRead::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SetAndSave(rContext);
+}
+
+void SwUndoReRead::SaveGraphicData( const SwGrfNode& rGrfNd )
+{
+ if( rGrfNd.IsGrfLink() )
+ {
+ maNm = OUString();
+ maFltr = OUString();
+ rGrfNd.GetFileFilterNms(&*maNm, &*maFltr);
+ mpGraphic.reset();
+ }
+ else
+ {
+ mpGraphic.reset( new Graphic( rGrfNd.GetGrf(true) ) );
+ maNm.reset();
+ maFltr.reset();
+ }
+ mnMirror = rGrfNd.GetSwAttrSet().GetMirrorGrf().GetValue();
+}
+
+SwUndoInsertLabel::SwUndoInsertLabel( const SwLabelType eTyp,
+ const OUString &rText,
+ const OUString& rSeparator,
+ const OUString& rNumberSeparator,
+ const bool bBef,
+ const sal_uInt16 nInitId,
+ const OUString& rCharacterStyle,
+ const bool bCpyBorder,
+ const SwDoc* pDoc )
+ : SwUndo( SwUndoId::INSERTLABEL, pDoc ),
+ m_sText( rText ),
+ m_sSeparator( rSeparator ),
+ m_sNumberSeparator( rNumberSeparator ),//#i61007# order of captions
+ m_sCharacterStyle( rCharacterStyle ),
+ m_nFieldId( nInitId ),
+ m_eType( eTyp ),
+ m_nLayerId( 0 ),
+ m_bBefore( bBef ),
+ m_bCopyBorder( bCpyBorder )
+{
+ m_bUndoKeep = false;
+ OBJECT.pUndoFly = nullptr;
+ OBJECT.pUndoAttr = nullptr;
+}
+
+SwUndoInsertLabel::~SwUndoInsertLabel()
+{
+ if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
+ {
+ delete OBJECT.pUndoFly;
+ delete OBJECT.pUndoAttr;
+ }
+ else
+ delete NODE.pUndoInsNd;
+}
+
+void SwUndoInsertLabel::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
+ {
+ OSL_ENSURE( OBJECT.pUndoAttr && OBJECT.pUndoFly, "Pointer not initialized" );
+ SwFrameFormat* pFormat;
+ SdrObject *pSdrObj = nullptr;
+ if( OBJECT.pUndoAttr &&
+ nullptr != (pFormat = static_cast<SwFrameFormat*>(OBJECT.pUndoAttr->GetFormat( rDoc ))) &&
+ ( SwLabelType::Draw != m_eType ||
+ nullptr != (pSdrObj = pFormat->FindSdrObject()) ) )
+ {
+ OBJECT.pUndoAttr->UndoImpl(rContext);
+ OBJECT.pUndoFly->UndoImpl(rContext);
+ if( SwLabelType::Draw == m_eType )
+ {
+ pSdrObj->SetLayer( m_nLayerId );
+ }
+ }
+ }
+ else if( NODE.nNode )
+ {
+ if ( m_eType == SwLabelType::Table && m_bUndoKeep )
+ {
+ SwTableNode *pNd = rDoc.GetNodes()[
+ rDoc.GetNodes()[NODE.nNode-1]->StartOfSectionIndex()]->GetTableNode();
+ if ( pNd )
+ pNd->GetTable().GetFrameFormat()->ResetFormatAttr( RES_KEEP );
+ }
+ SwPaM aPam( rDoc.GetNodes().GetEndOfContent() );
+ aPam.GetPoint()->nNode = NODE.nNode;
+ aPam.SetMark();
+ aPam.GetPoint()->nNode = NODE.nNode + 1;
+ NODE.pUndoInsNd = new SwUndoDelete( aPam, true );
+ }
+}
+
+void SwUndoInsertLabel::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
+ {
+ OSL_ENSURE( OBJECT.pUndoAttr && OBJECT.pUndoFly, "Pointer not initialized" );
+ SwFrameFormat* pFormat;
+ SdrObject *pSdrObj = nullptr;
+ if( OBJECT.pUndoAttr &&
+ nullptr != (pFormat = static_cast<SwFrameFormat*>(OBJECT.pUndoAttr->GetFormat( rDoc ))) &&
+ ( SwLabelType::Draw != m_eType ||
+ nullptr != (pSdrObj = pFormat->FindSdrObject()) ) )
+ {
+ OBJECT.pUndoFly->RedoImpl(rContext);
+ OBJECT.pUndoAttr->RedoImpl(rContext);
+ if( SwLabelType::Draw == m_eType )
+ {
+ pSdrObj->SetLayer( m_nLayerId );
+ if( pSdrObj->GetLayer() == rDoc.getIDocumentDrawModelAccess().GetHellId() )
+ pSdrObj->SetLayer( rDoc.getIDocumentDrawModelAccess().GetHeavenId() );
+ // OD 02.07.2003 #108784#
+ else if( pSdrObj->GetLayer() == rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId() )
+ pSdrObj->SetLayer( rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId() );
+ }
+ }
+ }
+ else if( NODE.pUndoInsNd )
+ {
+ if ( m_eType == SwLabelType::Table && m_bUndoKeep )
+ {
+ SwTableNode *pNd = rDoc.GetNodes()[
+ rDoc.GetNodes()[NODE.nNode-1]->StartOfSectionIndex()]->GetTableNode();
+ if ( pNd )
+ pNd->GetTable().GetFrameFormat()->SetFormatAttr( SvxFormatKeepItem(true, RES_KEEP) );
+ }
+ NODE.pUndoInsNd->UndoImpl(rContext);
+ delete NODE.pUndoInsNd;
+ NODE.pUndoInsNd = nullptr;
+ }
+}
+
+void SwUndoInsertLabel::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ const SwPosition& rPos = *rContext.GetRepeatPaM().GetPoint();
+
+ sal_uLong nIdx = 0;
+
+ SwContentNode* pCNd = rPos.nNode.GetNode().GetContentNode();
+ if( pCNd )
+ switch( m_eType )
+ {
+ case SwLabelType::Table:
+ {
+ const SwTableNode* pTNd = pCNd->FindTableNode();
+ if( pTNd )
+ nIdx = pTNd->GetIndex();
+ }
+ break;
+
+ case SwLabelType::Fly:
+ case SwLabelType::Object:
+ {
+ SwFlyFrame* pFly;
+ SwContentFrame *pCnt = pCNd->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
+ if( pCnt && nullptr != ( pFly = pCnt->FindFlyFrame() ) )
+ nIdx = pFly->GetFormat()->GetContent().GetContentIdx()->GetIndex();
+ }
+ break;
+ case SwLabelType::Draw:
+ break;
+ }
+
+ if( nIdx )
+ {
+ rDoc.InsertLabel( m_eType, m_sText, m_sSeparator, m_sNumberSeparator, m_bBefore,
+ m_nFieldId, nIdx, m_sCharacterStyle, m_bCopyBorder );
+ }
+}
+
+SwRewriter SwUndoInsertLabel::GetRewriter() const
+{
+ return CreateRewriter(m_sText);
+}
+
+SwRewriter SwUndoInsertLabel::CreateRewriter(const OUString &rStr)
+{
+ SwRewriter aRewriter;
+
+ OUString aTmpStr;
+
+ if (!rStr.isEmpty())
+ {
+ aTmpStr += SwResId(STR_START_QUOTE);
+ aTmpStr += ShortenString(rStr, nUndoStringLength,
+ SwResId(STR_LDOTS));
+ aTmpStr += SwResId(STR_END_QUOTE);
+ }
+
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ return aRewriter;
+}
+
+void SwUndoInsertLabel::SetFlys( SwFrameFormat& rOldFly, SfxItemSet const & rChgSet,
+ SwFrameFormat& rNewFly )
+{
+ if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
+ {
+ SwUndoFormatAttrHelper aTmp( rOldFly, false );
+ rOldFly.SetFormatAttr( rChgSet );
+ if ( aTmp.GetUndo() )
+ {
+ OBJECT.pUndoAttr = aTmp.ReleaseUndo().release();
+ }
+ OBJECT.pUndoFly = new SwUndoInsLayFormat( &rNewFly,0,0 );
+ }
+}
+
+void SwUndoInsertLabel::SetDrawObj( SdrLayerID nLId )
+{
+ if( SwLabelType::Draw == m_eType )
+ {
+ m_nLayerId = nLId;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unmove.cxx b/sw/source/core/undo/unmove.cxx
new file mode 100644
index 000000000..6888d477f
--- /dev/null
+++ b/sw/source/core/undo/unmove.cxx
@@ -0,0 +1,308 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoSplitMove.hxx>
+#include <doc.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+
+// MOVE
+SwUndoMove::SwUndoMove( const SwPaM& rRange, const SwPosition& rMvPos )
+ : SwUndo( SwUndoId::MOVE, rRange.GetDoc() )
+ , SwUndRng( rRange )
+ , m_nDestStartNode(0)
+ , m_nDestEndNode(0)
+ , m_nInsPosNode(0)
+ , m_nMoveDestNode(rMvPos.nNode.GetIndex())
+ , m_nDestStartContent(0)
+ , m_nDestEndContent(0)
+ , m_nInsPosContent(0)
+ , m_nMoveDestContent(rMvPos.nContent.GetIndex())
+ , m_bJoinNext(false)
+ , m_bMoveRange(false)
+ , m_bMoveRedlines(false)
+{
+ // get StartNode from footnotes before delete!
+ SwDoc* pDoc = rRange.GetDoc();
+ SwTextNode* pTextNd = pDoc->GetNodes()[ m_nSttNode ]->GetTextNode();
+ SwTextNode* pEndTextNd = pDoc->GetNodes()[ m_nEndNode ]->GetTextNode();
+
+ m_pHistory.reset( new SwHistory );
+
+ if( pTextNd )
+ {
+ m_pHistory->Add( pTextNd->GetTextColl(), m_nSttNode, SwNodeType::Text );
+ if ( pTextNd->GetpSwpHints() )
+ {
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nSttNode,
+ 0, pTextNd->GetText().getLength(), false );
+ }
+ if( pTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pTextNd->GetpSwAttrSet(), m_nSttNode );
+ }
+ if( pEndTextNd && pEndTextNd != pTextNd )
+ {
+ m_pHistory->Add( pEndTextNd->GetTextColl(), m_nEndNode, SwNodeType::Text );
+ if ( pEndTextNd->GetpSwpHints() )
+ {
+ m_pHistory->CopyAttr( pEndTextNd->GetpSwpHints(), m_nEndNode,
+ 0, pEndTextNd->GetText().getLength(), false );
+ }
+ if( pEndTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pEndTextNd->GetpSwAttrSet(), m_nEndNode );
+ }
+
+ pTextNd = rMvPos.nNode.GetNode().GetTextNode();
+ if (nullptr != pTextNd)
+ {
+ m_pHistory->Add( pTextNd->GetTextColl(), m_nMoveDestNode, SwNodeType::Text );
+ if ( pTextNd->GetpSwpHints() )
+ {
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nMoveDestNode,
+ 0, pTextNd->GetText().getLength(), false );
+ }
+ if( pTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pTextNd->GetpSwAttrSet(), m_nMoveDestNode );
+ }
+
+ m_nFootnoteStart = m_pHistory->Count();
+ DelFootnote( rRange );
+
+ if( m_pHistory && !m_pHistory->Count() )
+ m_pHistory.reset();
+}
+
+SwUndoMove::SwUndoMove( SwDoc* pDoc, const SwNodeRange& rRg,
+ const SwNodeIndex& rMvPos )
+ : SwUndo(SwUndoId::MOVE, pDoc)
+ , m_nDestStartNode(0)
+ , m_nDestEndNode(0)
+ , m_nInsPosNode(0)
+ , m_nMoveDestNode(rMvPos.GetIndex())
+ , m_nDestStartContent(0)
+ , m_nDestEndContent(0)
+ , m_nInsPosContent(0)
+ , m_bMoveRedlines(false)
+{
+ m_bMoveRange = true;
+ m_bJoinNext = false;
+
+ m_nSttContent = m_nEndContent = m_nMoveDestContent = COMPLETE_STRING;
+
+ m_nSttNode = rRg.aStart.GetIndex();
+ m_nEndNode = rRg.aEnd.GetIndex();
+
+// DelFootnote( rRange );
+// FIXME: duplication of the method body of DelFootnote below
+
+ // is the current move from ContentArea into the special section?
+ sal_uLong nContentStt = pDoc->GetNodes().GetEndOfAutotext().GetIndex();
+ if( m_nMoveDestNode < nContentStt && rRg.aStart.GetIndex() > nContentStt )
+ {
+ // delete all footnotes since they are undesired there
+ SwPosition aPtPos( rRg.aEnd );
+ SwContentNode* pCNd = rRg.aEnd.GetNode().GetContentNode();
+ if( pCNd )
+ aPtPos.nContent.Assign( pCNd, pCNd->Len() );
+ SwPosition aMkPos( rRg.aStart );
+ if( nullptr != ( pCNd = aMkPos.nNode.GetNode().GetContentNode() ))
+ aMkPos.nContent.Assign( pCNd, 0 );
+
+ DelContentIndex( aMkPos, aPtPos, DelContentType::Ftn );
+
+ if( m_pHistory && !m_pHistory->Count() )
+ m_pHistory.reset();
+ }
+
+ m_nFootnoteStart = 0;
+}
+
+void SwUndoMove::SetDestRange( const SwNodeIndex& rStt,
+ const SwNodeIndex& rEnd,
+ const SwNodeIndex& rInsPos )
+{
+ m_nDestStartNode = rStt.GetIndex();
+ m_nDestEndNode = rEnd.GetIndex();
+ if( m_nDestStartNode > m_nDestEndNode )
+ {
+ m_nDestStartNode = m_nDestEndNode;
+ m_nDestEndNode = rStt.GetIndex();
+ }
+ m_nInsPosNode = rInsPos.GetIndex();
+
+ m_nDestStartContent = m_nDestEndContent = m_nInsPosContent = COMPLETE_STRING;
+}
+
+void SwUndoMove::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ // Block so that we can jump out of it
+ do {
+ // create index position and section based on the existing values
+ SwNodeIndex aIdx( pDoc->GetNodes(), m_nDestStartNode );
+
+ if( m_bMoveRange )
+ {
+ // only a move with SwRange
+ SwNodeRange aRg( aIdx, aIdx );
+ aRg.aEnd = m_nDestEndNode;
+ aIdx = m_nInsPosNode;
+ bool bSuccess = pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aIdx,
+ SwMoveFlags::DEFAULT );
+ if (!bSuccess)
+ break;
+ }
+ else
+ {
+ SwPaM aPam( aIdx.GetNode(), m_nDestStartContent,
+ *pDoc->GetNodes()[ m_nDestEndNode ], m_nDestEndContent );
+
+ // #i17764# if redlines are to be moved, we may not remove them
+ // before pDoc->Move gets a chance to handle them
+ if( ! m_bMoveRedlines )
+ RemoveIdxFromRange( aPam, false );
+
+ SwPosition aPos( *pDoc->GetNodes()[ m_nInsPosNode] );
+ SwContentNode* pCNd = aPos.nNode.GetNode().GetContentNode();
+ aPos.nContent.Assign( pCNd, m_nInsPosContent );
+
+ if( pCNd->HasSwAttrSet() )
+ pCNd->ResetAllAttr();
+
+ if( pCNd->IsTextNode() && static_cast<SwTextNode*>(pCNd)->GetpSwpHints() )
+ static_cast<SwTextNode*>(pCNd)->ClearSwpHintsArr( false );
+
+ // first delete all attributes at InsertPos
+ const bool bSuccess = pDoc->getIDocumentContentOperations().MoveRange( aPam, aPos, m_bMoveRedlines
+ ? SwMoveFlags::REDLINES
+ : SwMoveFlags::DEFAULT );
+ if (!bSuccess)
+ break;
+
+ aPam.Exchange();
+ aPam.DeleteMark();
+ if( aPam.GetNode().IsContentNode() )
+ aPam.GetNode().GetContentNode()->ResetAllAttr();
+ // the Pam will be dropped now
+ }
+
+ SwTextNode* pTextNd = aIdx.GetNode().GetTextNode();
+ if( m_bJoinNext )
+ {
+ {
+ RemoveIdxRel( aIdx.GetIndex() + 1, SwPosition( aIdx,
+ SwIndex(pTextNd, pTextNd->GetText().getLength())));
+ }
+ // Are there any Pams in the next TextNode?
+ pTextNd->JoinNext();
+ }
+
+ } while( false );
+
+ if( m_pHistory )
+ {
+ if( m_nFootnoteStart != m_pHistory->Count() )
+ m_pHistory->Rollback( pDoc, m_nFootnoteStart );
+ m_pHistory->TmpRollback( pDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+
+ // set the cursor onto Undo area
+ if( !m_bMoveRange )
+ {
+ AddUndoRedoPaM(rContext);
+ }
+}
+
+void SwUndoMove::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM& rPam = AddUndoRedoPaM(rContext);
+ SwDoc& rDoc = rContext.GetDoc();
+
+ SwNodes& rNds = rDoc.GetNodes();
+ SwNodeIndex aIdx( rNds, m_nMoveDestNode );
+
+ if( m_bMoveRange )
+ {
+ // only a move with SwRange
+ SwNodeRange aRg( rNds, m_nSttNode, rNds, m_nEndNode );
+ rDoc.getIDocumentContentOperations().MoveNodeRange( aRg, aIdx, m_bMoveRedlines
+ ? SwMoveFlags::REDLINES
+ : SwMoveFlags::DEFAULT );
+ }
+ else
+ {
+ SwPaM aPam(*rPam.GetPoint());
+ SetPaM( aPam );
+ SwPosition aMvPos( aIdx, SwIndex( aIdx.GetNode().GetContentNode(),
+ m_nMoveDestContent ));
+
+ DelFootnote( aPam );
+ RemoveIdxFromRange( aPam, false );
+
+ aIdx = aPam.Start()->nNode;
+ bool bJoinText = aIdx.GetNode().IsTextNode();
+
+ --aIdx;
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aMvPos,
+ SwMoveFlags::DEFAULT );
+
+ if( m_nSttNode != m_nEndNode && bJoinText )
+ {
+ ++aIdx;
+ SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
+ if( pTextNd && pTextNd->CanJoinNext() )
+ {
+ {
+ RemoveIdxRel( aIdx.GetIndex() + 1, SwPosition( aIdx,
+ SwIndex(pTextNd, pTextNd->GetText().getLength())));
+ }
+ pTextNd->JoinNext();
+ }
+ }
+ *rPam.GetPoint() = *aPam.GetPoint();
+ rPam.SetMark();
+ *rPam.GetMark() = *aPam.GetMark();
+ }
+}
+
+void SwUndoMove::DelFootnote( const SwPaM& rRange )
+{
+ // is the current move from ContentArea into the special section?
+ SwDoc* pDoc = rRange.GetDoc();
+ sal_uLong nContentStt = pDoc->GetNodes().GetEndOfAutotext().GetIndex();
+ if( m_nMoveDestNode < nContentStt &&
+ rRange.GetPoint()->nNode.GetIndex() >= nContentStt )
+ {
+ // delete all footnotes since they are undesired there
+ DelContentIndex( *rRange.GetMark(), *rRange.GetPoint(),
+ DelContentType::Ftn );
+
+ if( m_pHistory && !m_pHistory->Count() )
+ {
+ m_pHistory.reset();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unnum.cxx b/sw/source/core/undo/unnum.cxx
new file mode 100644
index 000000000..a4e4b39c0
--- /dev/null
+++ b/sw/source/core/undo/unnum.cxx
@@ -0,0 +1,395 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoNumbering.hxx>
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <osl/diagnose.h>
+
+SwUndoInsNum::SwUndoInsNum( const SwNumRule& rOldRule,
+ const SwNumRule& rNewRule,
+ const SwDoc* pDoc,
+ SwUndoId nUndoId)
+ : SwUndo( nUndoId, pDoc ),
+ m_aNumRule( rNewRule ),
+ m_pOldNumRule( new SwNumRule( rOldRule )), m_nLRSavePos( 0 )
+{
+}
+
+SwUndoInsNum::SwUndoInsNum( const SwPaM& rPam, const SwNumRule& rRule )
+ : SwUndo( SwUndoId::INSNUM, rPam.GetDoc() ), SwUndRng( rPam ),
+ m_aNumRule( rRule ),
+ m_nLRSavePos( 0 )
+{
+}
+
+SwUndoInsNum::SwUndoInsNum( const SwPosition& rPos, const SwNumRule& rRule,
+ const OUString& rReplaceRule )
+ : SwUndo( SwUndoId::INSNUM, rPos.nNode.GetNode().GetDoc() ),
+ m_aNumRule( rRule ),
+ m_sReplaceRule( rReplaceRule ), m_nLRSavePos( 0 )
+{
+ // No selection!
+ m_nEndNode = 0;
+ m_nEndContent = COMPLETE_STRING;
+ m_nSttNode = rPos.nNode.GetIndex();
+ m_nSttContent = rPos.nContent.GetIndex();
+}
+
+SwUndoInsNum::~SwUndoInsNum()
+{
+ m_pHistory.reset();
+ m_pOldNumRule.reset();
+}
+
+SwRewriter SwUndoInsNum::GetRewriter() const
+{
+ SwRewriter aResult;
+ if( SwUndoId::INSFMTATTR == GetId() )
+ aResult.AddRule(UndoArg1, m_aNumRule.GetName());
+ return aResult;
+}
+
+void SwUndoInsNum::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if( m_pOldNumRule )
+ rDoc.ChgNumRuleFormats( *m_pOldNumRule );
+
+ if( m_pHistory )
+ {
+ if( m_nLRSavePos )
+ {
+ // Update immediately so that potential "old" LRSpaces will be valid again.
+ m_pHistory->TmpRollback( &rDoc, m_nLRSavePos );
+
+ }
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+
+ if (m_nSttNode)
+ {
+ AddUndoRedoPaM(rContext);
+ }
+}
+
+void SwUndoInsNum::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if ( m_pOldNumRule )
+ rDoc.ChgNumRuleFormats( m_aNumRule );
+ else if ( m_pHistory )
+ {
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ if( !m_sReplaceRule.isEmpty() )
+ {
+ rDoc.ReplaceNumRule( *rPam.GetPoint(), m_sReplaceRule, m_aNumRule.GetName() );
+ }
+ else
+ {
+ // #i42921# - adapt to changed signature
+ rDoc.SetNumRule(rPam, m_aNumRule, false);
+ }
+ }
+}
+
+void SwUndoInsNum::SetLRSpaceEndPos()
+{
+ if( m_pHistory )
+ m_nLRSavePos = m_pHistory->Count();
+}
+
+void SwUndoInsNum::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc( rContext.GetDoc() );
+ if ( m_nSttNode )
+ {
+ if( m_sReplaceRule.isEmpty() )
+ {
+ // #i42921# - adapt to changed signature
+ rDoc.SetNumRule(rContext.GetRepeatPaM(), m_aNumRule, false);
+ }
+ }
+ else
+ {
+ rDoc.ChgNumRuleFormats( m_aNumRule );
+ }
+}
+
+SwHistory* SwUndoInsNum::GetHistory()
+{
+ if( !m_pHistory )
+ m_pHistory.reset(new SwHistory);
+ return m_pHistory.get();
+}
+
+void SwUndoInsNum::SaveOldNumRule( const SwNumRule& rOld )
+{
+ if( !m_pOldNumRule )
+ m_pOldNumRule.reset(new SwNumRule( rOld ));
+}
+
+SwUndoDelNum::SwUndoDelNum( const SwPaM& rPam )
+ : SwUndo( SwUndoId::DELNUM, rPam.GetDoc() ), SwUndRng( rPam )
+{
+ m_aNodes.reserve( std::min<sal_uLong>(m_nEndNode - m_nSttNode, 255) );
+ m_pHistory.reset( new SwHistory );
+}
+
+SwUndoDelNum::~SwUndoDelNum()
+{
+}
+
+void SwUndoDelNum::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ for( const auto& rNode : m_aNodes )
+ {
+ SwTextNode* pNd = rDoc.GetNodes()[ rNode.index ]->GetTextNode();
+ OSL_ENSURE( pNd, "Where has the TextNode gone?" );
+ pNd->SetAttrListLevel( rNode.level );
+
+ if( pNd->GetCondFormatColl() )
+ pNd->ChkCondColl();
+ }
+
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoDelNum::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().DelNumRules(rPam);
+}
+
+void SwUndoDelNum::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().DelNumRules(rContext.GetRepeatPaM());
+}
+
+void SwUndoDelNum::AddNode( const SwTextNode& rNd )
+{
+ if( rNd.GetNumRule() )
+ {
+ m_aNodes.emplace_back( rNd.GetIndex(), rNd.GetActualListLevel() );
+ }
+}
+
+SwUndoMoveNum::SwUndoMoveNum( const SwPaM& rPam, long nOff, bool bIsOutlMv )
+ : SwUndo( bIsOutlMv ? SwUndoId::OUTLINE_UD : SwUndoId::MOVENUM, rPam.GetDoc() ),
+ SwUndRng( rPam ),
+ nNewStt( 0 ), nOffset( nOff )
+{
+ // nOffset: Down => 1
+ // Up => -1
+}
+
+void SwUndoMoveNum::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ sal_uLong nTmpStt = m_nSttNode, nTmpEnd = m_nEndNode;
+
+ if (m_nEndNode || m_nEndContent != COMPLETE_STRING) // section?
+ {
+ if( nNewStt < m_nSttNode ) // moved forwards
+ m_nEndNode = m_nEndNode - ( m_nSttNode - nNewStt );
+ else
+ m_nEndNode = m_nEndNode + ( nNewStt - m_nSttNode );
+ }
+ m_nSttNode = nNewStt;
+
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().MoveParagraph( rPam, -nOffset,
+ SwUndoId::OUTLINE_UD == GetId() );
+ m_nSttNode = nTmpStt;
+ m_nEndNode = nTmpEnd;
+}
+
+void SwUndoMoveNum::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().MoveParagraph(rPam, nOffset, SwUndoId::OUTLINE_UD == GetId());
+}
+
+void SwUndoMoveNum::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if( SwUndoId::OUTLINE_UD == GetId() )
+ {
+ rDoc.MoveOutlinePara(rContext.GetRepeatPaM(),
+ 0 < nOffset ? 1 : -1 );
+ }
+ else
+ {
+ rDoc.MoveParagraph(rContext.GetRepeatPaM(), nOffset);
+ }
+}
+
+SwUndoNumUpDown::SwUndoNumUpDown( const SwPaM& rPam, short nOff )
+ : SwUndo( nOff > 0 ? SwUndoId::NUMUP : SwUndoId::NUMDOWN, rPam.GetDoc() ),
+ SwUndRng( rPam ),
+ nOffset( nOff )
+{
+ // nOffset: Down => 1
+ // Up => -1
+}
+
+void SwUndoNumUpDown::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().NumUpDown(rPam, 1 != nOffset );
+}
+
+void SwUndoNumUpDown::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().NumUpDown(rPam, 1 == nOffset);
+}
+
+void SwUndoNumUpDown::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().NumUpDown(rContext.GetRepeatPaM(), 1 == nOffset);
+}
+
+SwUndoNumOrNoNum::SwUndoNumOrNoNum( const SwNodeIndex& rIdx, bool bOldNum,
+ bool bNewNum)
+ : SwUndo( SwUndoId::NUMORNONUM, rIdx.GetNode().GetDoc() ),
+ nIdx( rIdx.GetIndex() ), mbNewNum(bNewNum),
+ mbOldNum(bOldNum)
+{
+}
+
+// #115901#, #i40034#
+void SwUndoNumOrNoNum::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwNodeIndex aIdx( rContext.GetDoc().GetNodes(), nIdx );
+ SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
+
+ if (nullptr != pTextNd)
+ {
+ pTextNd->SetCountedInList(mbOldNum);
+ }
+}
+
+// #115901#, #i40034#
+void SwUndoNumOrNoNum::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwNodeIndex aIdx( rContext.GetDoc().GetNodes(), nIdx );
+ SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
+
+ if (nullptr != pTextNd)
+ {
+ pTextNd->SetCountedInList(mbNewNum);
+ }
+}
+
+void SwUndoNumOrNoNum::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if (mbOldNum && ! mbNewNum)
+ {
+ rDoc.NumOrNoNum(rContext.GetRepeatPaM().GetPoint()->nNode);
+ }
+ else if ( ! mbOldNum && mbNewNum )
+ {
+ rDoc.NumOrNoNum(rContext.GetRepeatPaM().GetPoint()->nNode, true);
+ }
+}
+
+SwUndoNumRuleStart::SwUndoNumRuleStart( const SwPosition& rPos, bool bFlg )
+ : SwUndo( SwUndoId::SETNUMRULESTART, rPos.GetDoc() ),
+ nIdx( rPos.nNode.GetIndex() ), nOldStt( USHRT_MAX ),
+ nNewStt( USHRT_MAX ), bSetSttValue( false ), bFlag( bFlg )
+{
+}
+
+SwUndoNumRuleStart::SwUndoNumRuleStart( const SwPosition& rPos, sal_uInt16 nStt )
+ : SwUndo(SwUndoId::SETNUMRULESTART, rPos.GetDoc())
+ , nIdx(rPos.nNode.GetIndex())
+ , nOldStt(USHRT_MAX)
+ , nNewStt(nStt)
+ , bSetSttValue(true)
+ , bFlag(false)
+{
+ SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
+ if ( pTextNd )
+ {
+ if ( pTextNd->HasAttrListRestartValue() )
+ {
+ nOldStt = static_cast<sal_uInt16>(pTextNd->GetAttrListRestartValue());
+ }
+ else
+ {
+ nOldStt = USHRT_MAX; // indicating, that the list restart value is not set
+ }
+ }
+}
+
+void SwUndoNumRuleStart::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPosition const aPos( *rDoc.GetNodes()[ nIdx ] );
+ if( bSetSttValue )
+ {
+ rDoc.SetNodeNumStart( aPos, nOldStt );
+ }
+ else
+ {
+ rDoc.SetNumRuleStart( aPos, !bFlag );
+ }
+}
+
+void SwUndoNumRuleStart::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPosition const aPos( *rDoc.GetNodes()[ nIdx ] );
+ if( bSetSttValue )
+ {
+ rDoc.SetNodeNumStart( aPos, nNewStt );
+ }
+ else
+ {
+ rDoc.SetNumRuleStart( aPos, bFlag );
+ }
+}
+
+void SwUndoNumRuleStart::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if( bSetSttValue )
+ {
+ rDoc.SetNodeNumStart(*rContext.GetRepeatPaM().GetPoint(), nNewStt);
+ }
+ else
+ {
+ rDoc.SetNumRuleStart(*rContext.GetRepeatPaM().GetPoint(), bFlag);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unoutl.cxx b/sw/source/core/undo/unoutl.cxx
new file mode 100644
index 000000000..04341d61a
--- /dev/null
+++ b/sw/source/core/undo/unoutl.cxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+
+#include <UndoCore.hxx>
+
+SwUndoOutlineLeftRight::SwUndoOutlineLeftRight( const SwPaM& rPam,
+ short nOff )
+ : SwUndo( SwUndoId::OUTLINE_LR, rPam.GetDoc() ), SwUndRng( rPam ), m_nOffset( nOff )
+{
+}
+
+void SwUndoOutlineLeftRight::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPaM( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().OutlineUpDown(rPaM, -m_nOffset);
+}
+
+void SwUndoOutlineLeftRight::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPaM( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().OutlineUpDown(rPaM, m_nOffset);
+}
+
+void SwUndoOutlineLeftRight::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().OutlineUpDown(rContext.GetRepeatPaM(), m_nOffset);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unovwr.cxx b/sw/source/core/undo/unovwr.cxx
new file mode 100644
index 000000000..7361da5a4
--- /dev/null
+++ b/sw/source/core/undo/unovwr.cxx
@@ -0,0 +1,474 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoOverwrite.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <acorrect.hxx>
+#include <docary.hxx>
+#include <strings.hrc>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+
+SwUndoOverwrite::SwUndoOverwrite( SwDoc* pDoc, SwPosition& rPos,
+ sal_Unicode cIns )
+ : SwUndo(SwUndoId::OVERWRITE, pDoc),
+ bGroup( false )
+{
+ SwTextNode *const pTextNd = rPos.nNode.GetNode().GetTextNode();
+ assert(pTextNd);
+ sal_Int32 const nTextNdLen = pTextNd->GetText().getLength();
+
+ nSttNode = rPos.nNode.GetIndex();
+ nSttContent = rPos.nContent.GetIndex();
+
+ if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(),
+ rPos.nNode, rPos.nContent.GetIndex()+1 );
+ pRedlSaveData.reset( new SwRedlineSaveDatas );
+ if( !FillSaveData( aPam, *pRedlSaveData, false ))
+ {
+ pRedlSaveData.reset();
+ }
+ if (nSttContent < nTextNdLen)
+ {
+ pDoc->getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Any);
+ }
+ }
+
+ bInsChar = true;
+ if( nSttContent < nTextNdLen ) // no pure insert?
+ {
+ aDelStr += OUStringChar( pTextNd->GetText()[nSttContent] );
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ SwRegHistory aRHst( *pTextNd, m_pHistory.get() );
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), nSttNode, 0,
+ nTextNdLen, false );
+ ++rPos.nContent;
+ bInsChar = false;
+ }
+
+ bool bOldExpFlg = pTextNd->IsIgnoreDontExpand();
+ pTextNd->SetIgnoreDontExpand( true );
+
+ pTextNd->InsertText( OUString(cIns), rPos.nContent,
+ SwInsertFlags::EMPTYEXPAND );
+ aInsStr += OUStringChar( cIns );
+
+ if( !bInsChar )
+ {
+ const SwIndex aTmpIndex( rPos.nContent, -2 );
+ pTextNd->EraseText( aTmpIndex, 1 );
+ }
+ pTextNd->SetIgnoreDontExpand( bOldExpFlg );
+
+ m_bCacheComment = false;
+}
+
+SwUndoOverwrite::~SwUndoOverwrite()
+{
+}
+
+bool SwUndoOverwrite::CanGrouping( SwDoc* pDoc, SwPosition& rPos,
+ sal_Unicode cIns )
+{
+// What is with only inserted characters?
+
+ // Only deletion of single chars can be combined.
+ if( rPos.nNode != nSttNode || aInsStr.isEmpty() ||
+ ( !bGroup && aInsStr.getLength() != 1 ))
+ return false;
+
+ // Is the node a TextNode at all?
+ SwTextNode * pDelTextNd = rPos.nNode.GetNode().GetTextNode();
+ if( !pDelTextNd ||
+ (pDelTextNd->GetText().getLength() != rPos.nContent.GetIndex() &&
+ rPos.nContent.GetIndex() != ( nSttContent + aInsStr.getLength() )))
+ return false;
+
+ CharClass& rCC = GetAppCharClass();
+
+ // ask the char that should be inserted
+ if (( CH_TXTATR_BREAKWORD == cIns || CH_TXTATR_INWORD == cIns ) ||
+ rCC.isLetterNumeric( OUString( cIns ), 0 ) !=
+ rCC.isLetterNumeric( aInsStr, aInsStr.getLength()-1 ) )
+ return false;
+
+ if (!bInsChar && rPos.nContent.GetIndex() < pDelTextNd->GetText().getLength())
+ {
+ SwRedlineSaveDatas aTmpSav;
+ SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(),
+ rPos.nNode, rPos.nContent.GetIndex()+1 );
+
+ const bool bSaved = FillSaveData( aPam, aTmpSav, false );
+
+ bool bOk = ( !pRedlSaveData && !bSaved ) ||
+ ( pRedlSaveData && bSaved &&
+ SwUndo::CanRedlineGroup( *pRedlSaveData, aTmpSav,
+ nSttContent > rPos.nContent.GetIndex() ));
+ // aTmpSav.DeleteAndDestroyAll();
+ if( !bOk )
+ return false;
+
+ pDoc->getIDocumentRedlineAccess().DeleteRedline( aPam, false, RedlineType::Any );
+ }
+
+ // both 'overwrites' can be combined so 'move' the corresponding character
+ if( !bInsChar )
+ {
+ if (rPos.nContent.GetIndex() < pDelTextNd->GetText().getLength())
+ {
+ aDelStr += OUStringChar( pDelTextNd->GetText()[rPos.nContent.GetIndex()] );
+ ++rPos.nContent;
+ }
+ else
+ bInsChar = true;
+ }
+
+ bool bOldExpFlg = pDelTextNd->IsIgnoreDontExpand();
+ pDelTextNd->SetIgnoreDontExpand( true );
+
+ OUString const ins( pDelTextNd->InsertText(OUString(cIns), rPos.nContent,
+ SwInsertFlags::EMPTYEXPAND) );
+ assert(ins.getLength() == 1); // check in SwDoc::Overwrite => cannot fail
+ (void) ins;
+ aInsStr += OUStringChar( cIns );
+
+ if( !bInsChar )
+ {
+ const SwIndex aTmpIndex( rPos.nContent, -2 );
+ pDelTextNd->EraseText( aTmpIndex, 1 );
+ }
+ pDelTextNd->SetIgnoreDontExpand( bOldExpFlg );
+
+ bGroup = true;
+ return true;
+}
+
+void SwUndoOverwrite::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pCurrentPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pCurrentPam->DeleteMark();
+ pCurrentPam->GetPoint()->nNode = nSttNode;
+ SwTextNode* pTextNd = pCurrentPam->GetNode().GetTextNode();
+ assert(pTextNd);
+ SwIndex& rIdx = pCurrentPam->GetPoint()->nContent;
+ rIdx.Assign( pTextNd, nSttContent );
+
+ SwAutoCorrExceptWord* pACEWord = pDoc->GetAutoCorrExceptWord();
+ if( pACEWord )
+ {
+ if( 1 == aInsStr.getLength() && 1 == aDelStr.getLength() )
+ pACEWord->CheckChar( *pCurrentPam->GetPoint(), aDelStr[0] );
+ pDoc->SetAutoCorrExceptWord( nullptr );
+ }
+
+ // If there was not only an overwrite but also an insert, delete the surplus
+ if( aInsStr.getLength() > aDelStr.getLength() )
+ {
+ rIdx += aDelStr.getLength();
+ pTextNd->EraseText( rIdx, aInsStr.getLength() - aDelStr.getLength() );
+ rIdx = nSttContent;
+ }
+
+ if( !aDelStr.isEmpty() )
+ {
+ bool bOldExpFlg = pTextNd->IsIgnoreDontExpand();
+ pTextNd->SetIgnoreDontExpand( true );
+
+ ++rIdx;
+ for( sal_Int32 n = 0; n < aDelStr.getLength(); n++ )
+ {
+ // do it individually, to keep the attributes!
+ OUString aTmpStr(aDelStr[n]);
+ OUString const ins( pTextNd->InsertText(aTmpStr, rIdx) );
+ assert(ins.getLength() == 1); // cannot fail
+ (void) ins;
+ rIdx -= 2;
+ pTextNd->EraseText( rIdx, 1 );
+ rIdx += 2;
+ }
+ pTextNd->SetIgnoreDontExpand( bOldExpFlg );
+ --rIdx;
+ }
+
+ if( m_pHistory )
+ {
+ if( pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( false );
+ m_pHistory->TmpRollback( pDoc, 0, false );
+ }
+
+ if( pCurrentPam->GetMark()->nContent.GetIndex() != nSttContent )
+ {
+ pCurrentPam->SetMark();
+ pCurrentPam->GetMark()->nContent = nSttContent;
+ }
+
+ if( pRedlSaveData )
+ SetSaveData( *pDoc, *pRedlSaveData );
+}
+
+void SwUndoOverwrite::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM *const pCurrentPam = & rContext.GetRepeatPaM();
+ if( aInsStr.isEmpty() || pCurrentPam->HasMark() )
+ return;
+
+ SwDoc & rDoc = rContext.GetDoc();
+
+ {
+ ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().Overwrite(*pCurrentPam, OUString(aInsStr[0]));
+ }
+ for( sal_Int32 n = 1; n < aInsStr.getLength(); ++n )
+ rDoc.getIDocumentContentOperations().Overwrite( *pCurrentPam, OUString(aInsStr[n]) );
+}
+
+void SwUndoOverwrite::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pCurrentPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pCurrentPam->DeleteMark();
+ pCurrentPam->GetPoint()->nNode = nSttNode;
+ SwTextNode* pTextNd = pCurrentPam->GetNode().GetTextNode();
+ assert(pTextNd);
+ SwIndex& rIdx = pCurrentPam->GetPoint()->nContent;
+
+ if( pRedlSaveData )
+ {
+ rIdx.Assign( pTextNd, nSttContent );
+ pCurrentPam->SetMark();
+ pCurrentPam->GetMark()->nContent += aDelStr.getLength();
+ pDoc->getIDocumentRedlineAccess().DeleteRedline( *pCurrentPam, false, RedlineType::Any );
+ pCurrentPam->DeleteMark();
+ }
+ rIdx.Assign( pTextNd, !aDelStr.isEmpty() ? nSttContent+1 : nSttContent );
+
+ bool bOldExpFlg = pTextNd->IsIgnoreDontExpand();
+ pTextNd->SetIgnoreDontExpand( true );
+
+ for( sal_Int32 n = 0; n < aInsStr.getLength(); n++ )
+ {
+ // do it individually, to keep the attributes!
+ OUString const ins(
+ pTextNd->InsertText( OUString(aInsStr[n]), rIdx,
+ SwInsertFlags::EMPTYEXPAND) );
+ assert(ins.getLength() == 1); // cannot fail
+ (void) ins;
+ if( n < aDelStr.getLength() )
+ {
+ rIdx -= 2;
+ pTextNd->EraseText( rIdx, 1 );
+ rIdx += n+1 < aDelStr.getLength() ? 2 : 1;
+ }
+ }
+ pTextNd->SetIgnoreDontExpand( bOldExpFlg );
+
+ // get back old start position from UndoNodes array
+ if( m_pHistory )
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ if( pCurrentPam->GetMark()->nContent.GetIndex() != nSttContent )
+ {
+ pCurrentPam->SetMark();
+ pCurrentPam->GetMark()->nContent = nSttContent;
+ }
+}
+
+SwRewriter SwUndoOverwrite::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ OUString aString = SwResId(STR_START_QUOTE) +
+ ShortenString(aInsStr, nUndoStringLength, SwResId(STR_LDOTS)) +
+ SwResId(STR_END_QUOTE);
+
+ aResult.AddRule(UndoArg1, aString);
+
+ return aResult;
+}
+
+struct UndoTransliterate_Data
+{
+ OUString sText;
+ std::unique_ptr<SwHistory> pHistory;
+ std::unique_ptr<Sequence< sal_Int32 >> pOffsets;
+ sal_uLong nNdIdx;
+ sal_Int32 nStart, nLen;
+
+ UndoTransliterate_Data( sal_uLong nNd, sal_Int32 nStt, sal_Int32 nStrLen, const OUString& rText )
+ : sText( rText ),
+ nNdIdx( nNd ), nStart( nStt ), nLen( nStrLen )
+ {}
+
+ void SetChangeAtNode( SwDoc& rDoc );
+};
+
+SwUndoTransliterate::SwUndoTransliterate(
+ const SwPaM& rPam,
+ const utl::TransliterationWrapper& rTrans )
+ : SwUndo( SwUndoId::TRANSLITERATE, rPam.GetDoc() ), SwUndRng( rPam ), nType( rTrans.getType() )
+{
+}
+
+SwUndoTransliterate::~SwUndoTransliterate()
+{
+}
+
+void SwUndoTransliterate::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ // since the changes were added to the vector from the end of the string/node towards
+ // the start, we need to revert them from the start towards the end now to keep the
+ // offset information of the undo data in sync with the changing text.
+ // Thus we need to iterate from the end of the vector to the start
+ for (sal_Int32 i = aChanges.size() - 1; i >= 0; --i)
+ aChanges[i]->SetChangeAtNode( rDoc );
+
+ AddUndoRedoPaM(rContext, true);
+}
+
+void SwUndoTransliterate::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ DoTransliterate(rContext.GetDoc(), rPam);
+}
+
+void SwUndoTransliterate::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ DoTransliterate(rContext.GetDoc(), rContext.GetRepeatPaM());
+}
+
+void SwUndoTransliterate::DoTransliterate(SwDoc & rDoc, SwPaM const & rPam)
+{
+ utl::TransliterationWrapper aTrans( ::comphelper::getProcessComponentContext(), nType );
+ rDoc.getIDocumentContentOperations().TransliterateText( rPam, aTrans );
+}
+
+void SwUndoTransliterate::AddChanges( SwTextNode& rTNd,
+ sal_Int32 nStart, sal_Int32 nLen,
+ uno::Sequence <sal_Int32> const & rOffsets )
+{
+ long nOffsLen = rOffsets.getLength();
+ UndoTransliterate_Data* pNew = new UndoTransliterate_Data(
+ rTNd.GetIndex(), nStart, static_cast<sal_Int32>(nOffsLen),
+ rTNd.GetText().copy(nStart, nLen));
+
+ aChanges.push_back( std::unique_ptr<UndoTransliterate_Data>(pNew) );
+
+ const sal_Int32* pOffsets = rOffsets.getConstArray();
+ // where did we need less memory ?
+ const sal_Int32* p = pOffsets;
+ for( long n = 0; n < nOffsLen; ++n, ++p )
+ if( *p != ( nStart + n ))
+ {
+ // create the Offset array
+ pNew->pOffsets.reset( new Sequence <sal_Int32> ( nLen ) );
+ sal_Int32* pIdx = pNew->pOffsets->getArray();
+ p = pOffsets;
+ long nMyOff, nNewVal = nStart;
+ for( n = 0, nMyOff = nStart; n < nOffsLen; ++p, ++n, ++nMyOff )
+ {
+ if( *p < nMyOff )
+ {
+ // something is deleted
+ nMyOff = *p;
+ *(pIdx-1) = nNewVal++;
+ }
+ else if( *p > nMyOff )
+ {
+ for( ; *p > nMyOff; ++nMyOff )
+ *pIdx++ = nNewVal;
+ --nMyOff;
+ --n;
+ --p;
+ }
+ else
+ *pIdx++ = nNewVal++;
+ }
+
+ // and then we need to save the attributes/bookmarks
+ // but this data must moved every time to the last in the chain!
+ for (size_t i = 0; i + 1 < aChanges.size(); ++i) // check all changes but not the current one
+ {
+ UndoTransliterate_Data* pD = aChanges[i].get();
+ if( pD->nNdIdx == pNew->nNdIdx && pD->pHistory )
+ {
+ // same node and have a history?
+ pNew->pHistory = std::move(pD->pHistory);
+ break; // more can't exist
+ }
+ }
+
+ if( !pNew->pHistory )
+ {
+ pNew->pHistory.reset( new SwHistory );
+ SwRegHistory aRHst( rTNd, pNew->pHistory.get() );
+ pNew->pHistory->CopyAttr( rTNd.GetpSwpHints(),
+ pNew->nNdIdx, 0, rTNd.GetText().getLength(), false );
+ }
+ break;
+ }
+}
+
+void UndoTransliterate_Data::SetChangeAtNode( SwDoc& rDoc )
+{
+ SwTextNode* pTNd = rDoc.GetNodes()[ nNdIdx ]->GetTextNode();
+ if( pTNd )
+ {
+ Sequence <sal_Int32> aOffsets( pOffsets ? pOffsets->getLength() : nLen );
+ if( pOffsets )
+ aOffsets = *pOffsets;
+ else
+ {
+ sal_Int32* p = aOffsets.getArray();
+ for( sal_Int32 n = 0; n < nLen; ++n, ++p )
+ *p = n + nStart;
+ }
+ pTNd->ReplaceTextOnly( nStart, nLen, sText, aOffsets );
+
+ if( pHistory )
+ {
+ if( pTNd->GetpSwpHints() )
+ pTNd->ClearSwpHintsArr( false );
+ pHistory->TmpRollback( &rDoc, 0, false );
+ pHistory->SetTmpEnd( pHistory->Count() );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unredln.cxx b/sw/source/core/undo/unredln.cxx
new file mode 100644
index 000000000..4b98043a6
--- /dev/null
+++ b/sw/source/core/undo/unredln.cxx
@@ -0,0 +1,527 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoRedline.hxx>
+#include <hintids.hxx>
+#include <unotools/charclass.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <UndoCore.hxx>
+#include <UndoDelete.hxx>
+#include <strings.hrc>
+#include <redline.hxx>
+#include <docary.hxx>
+#include <sortopt.hxx>
+#include <docedt.hxx>
+
+SwUndoRedline::SwUndoRedline( SwUndoId nUsrId, const SwPaM& rRange )
+ : SwUndo( SwUndoId::REDLINE, rRange.GetDoc() ), SwUndRng( rRange ),
+ mnUserId( nUsrId ),
+ mbHiddenRedlines( false )
+{
+ // consider Redline
+ SwDoc& rDoc = *rRange.GetDoc();
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ switch( mnUserId )
+ {
+ case SwUndoId::DELETE:
+ case SwUndoId::REPLACE:
+ mpRedlData.reset( new SwRedlineData( RedlineType::Delete, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ break;
+ default:
+ ;
+ }
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ sal_uLong nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex();
+
+ mpRedlSaveData.reset( new SwRedlineSaveDatas );
+ if( !FillSaveData( rRange, *mpRedlSaveData, false, SwUndoId::REJECT_REDLINE != mnUserId ))
+ {
+ mpRedlSaveData.reset();
+ }
+ else
+ {
+ mbHiddenRedlines = HasHiddenRedlines( *mpRedlSaveData );
+ if( mbHiddenRedlines ) // then the NodeIndices of SwUndRng need to be corrected
+ {
+ nEndExtra -= rDoc.GetNodes().GetEndOfExtras().GetIndex();
+ m_nSttNode -= nEndExtra;
+ m_nEndNode -= nEndExtra;
+ }
+ }
+}
+
+SwUndoRedline::~SwUndoRedline()
+{
+ mpRedlData.reset();
+ mpRedlSaveData.reset();
+}
+
+sal_uInt16 SwUndoRedline::GetRedlSaveCount() const
+{
+ return mpRedlSaveData ? mpRedlSaveData->size() : 0;
+}
+
+void SwUndoRedline::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwPaM& rPam(AddUndoRedoPaM(rContext));
+
+ UndoRedlineImpl(rDoc, rPam);
+
+ if( mpRedlSaveData )
+ {
+ sal_uLong nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex();
+ SetSaveData(rDoc, *mpRedlSaveData);
+ if( mbHiddenRedlines )
+ {
+ mpRedlSaveData->clear();
+
+ nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex() - nEndExtra;
+ m_nSttNode += nEndExtra;
+ m_nEndNode += nEndExtra;
+ }
+ SetPaM(rPam, true);
+ }
+
+ // update frames after calling SetSaveData
+ if (dynamic_cast<SwUndoRedlineDelete*>(this))
+ {
+ sw::UpdateFramesForRemoveDeleteRedline(rDoc, rPam);
+ }
+ else if (dynamic_cast<SwUndoAcceptRedline*>(this)
+ || dynamic_cast<SwUndoRejectRedline*>(this))
+ { // (can't check here if there's a delete redline being accepted)
+ sw::UpdateFramesForAddDeleteRedline(rDoc, rPam);
+ }
+}
+
+void SwUndoRedline::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ if( mpRedlSaveData && mbHiddenRedlines )
+ {
+ sal_uLong nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex();
+ FillSaveData(rPam, *mpRedlSaveData, false, SwUndoId::REJECT_REDLINE != mnUserId );
+
+ nEndExtra -= rDoc.GetNodes().GetEndOfExtras().GetIndex();
+ m_nSttNode -= nEndExtra;
+ m_nEndNode -= nEndExtra;
+ }
+
+ RedoRedlineImpl(rDoc, rPam);
+
+ SetPaM(rPam, true);
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+void SwUndoRedline::UndoRedlineImpl(SwDoc &, SwPaM &)
+{
+}
+
+// default: remove redlines
+void SwUndoRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
+}
+
+SwUndoRedlineDelete::SwUndoRedlineDelete( const SwPaM& rRange, SwUndoId nUsrId )
+ : SwUndoRedline( nUsrId != SwUndoId::EMPTY ? nUsrId : SwUndoId::DELETE, rRange ),
+ m_bCanGroup( false ), m_bIsDelim( false ), m_bIsBackspace( false )
+{
+ const SwTextNode* pTNd;
+ SetRedlineText(rRange.GetText());
+ if( SwUndoId::DELETE == mnUserId &&
+ m_nSttNode == m_nEndNode && m_nSttContent + 1 == m_nEndContent &&
+ nullptr != (pTNd = rRange.GetNode().GetTextNode()) )
+ {
+ sal_Unicode const cCh = pTNd->GetText()[m_nSttContent];
+ if( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh )
+ {
+ m_bCanGroup = true;
+ m_bIsDelim = !GetAppCharClass().isLetterNumeric( pTNd->GetText(),
+ m_nSttContent );
+ m_bIsBackspace = m_nSttContent == rRange.GetPoint()->nContent.GetIndex();
+ }
+ }
+
+ m_bCacheComment = false;
+}
+
+// bit of a hack, replace everything...
+SwRewriter SwUndoRedlineDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+ OUString aStr = DenoteSpecialCharacters(m_sRedlineText);
+ aStr = ShortenString(aStr, nUndoStringLength, SwResId(STR_LDOTS));
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aStr);
+ OUString ret = aRewriter.Apply(SwResId(STR_UNDO_REDLINE_DELETE));
+ aResult.AddRule(UndoArg1, ret);
+ return aResult;
+}
+
+void SwUndoRedlineDelete::SetRedlineText(const OUString & rText)
+{
+ m_sRedlineText = rText;
+}
+
+void SwUndoRedlineDelete::UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
+}
+
+void SwUndoRedlineDelete::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ if (rPam.GetPoint() != rPam.GetMark())
+ {
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline(*mpRedlData, rPam), false );
+ }
+ sw::UpdateFramesForAddDeleteRedline(rDoc, rPam);
+}
+
+bool SwUndoRedlineDelete::CanGrouping( const SwUndoRedlineDelete& rNext )
+{
+ bool bRet = false;
+ if( SwUndoId::DELETE == mnUserId && mnUserId == rNext.mnUserId &&
+ m_bCanGroup == rNext.m_bCanGroup &&
+ m_bIsDelim == rNext.m_bIsDelim &&
+ m_bIsBackspace == rNext.m_bIsBackspace &&
+ m_nSttNode == m_nEndNode &&
+ rNext.m_nSttNode == m_nSttNode &&
+ rNext.m_nEndNode == m_nEndNode )
+ {
+ int bIsEnd = 0;
+ if( rNext.m_nSttContent == m_nEndContent )
+ bIsEnd = 1;
+ else if( rNext.m_nEndContent == m_nSttContent )
+ bIsEnd = -1;
+
+ if( bIsEnd &&
+ (( !mpRedlSaveData && !rNext.mpRedlSaveData ) ||
+ ( mpRedlSaveData && rNext.mpRedlSaveData &&
+ SwUndo::CanRedlineGroup( *mpRedlSaveData,
+ *rNext.mpRedlSaveData, 1 != bIsEnd )
+ )))
+ {
+ if( 1 == bIsEnd )
+ m_nEndContent = rNext.m_nEndContent;
+ else
+ m_nSttContent = rNext.m_nSttContent;
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+SwUndoRedlineSort::SwUndoRedlineSort( const SwPaM& rRange,
+ const SwSortOptions& rOpt )
+ : SwUndoRedline( SwUndoId::SORT_TXT, rRange ),
+ m_pOpt( new SwSortOptions( rOpt ) ),
+ m_nSaveEndNode( m_nEndNode ), m_nSaveEndContent( m_nEndContent )
+{
+}
+
+SwUndoRedlineSort::~SwUndoRedlineSort()
+{
+}
+
+void SwUndoRedlineSort::UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ // rPam contains the sorted range
+ // aSaveRange contains copied (i.e. original) range
+
+ SwPosition *const pStart = rPam.Start();
+ SwPosition *const pEnd = rPam.End();
+
+ SwNodeIndex aPrevIdx( pStart->nNode, -1 );
+ sal_uLong nOffsetTemp = pEnd->nNode.GetIndex() - pStart->nNode.GetIndex();
+
+ if( !( RedlineFlags::ShowDelete & rDoc.getIDocumentRedlineAccess().GetRedlineFlags()) )
+ {
+ // Search both Redline objects and make them visible to make the nodes
+ // consistent again. The 'delete' one is hidden, thus search for the
+ // 'insert' Redline object. The former is located directly after the latter.
+ SwRedlineTable::size_type nFnd = rDoc.getIDocumentRedlineAccess().GetRedlinePos(
+ *rDoc.GetNodes()[ m_nSttNode + 1 ],
+ RedlineType::Insert );
+ OSL_ENSURE( SwRedlineTable::npos != nFnd && nFnd+1 < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(),
+ "could not find an Insert object" );
+ ++nFnd;
+ rDoc.getIDocumentRedlineAccess().GetRedlineTable()[nFnd]->Show(1, nFnd);
+ }
+
+ {
+ SwPaM aTmp( *rPam.GetMark() );
+ aTmp.GetMark()->nContent = 0;
+ aTmp.SetMark();
+ aTmp.GetPoint()->nNode = m_nSaveEndNode;
+ aTmp.GetPoint()->nContent.Assign( aTmp.GetContentNode(), m_nSaveEndContent );
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( aTmp, true, RedlineType::Any );
+ }
+
+ rDoc.getIDocumentContentOperations().DelFullPara(rPam);
+
+ SwPaM *const pPam = & rPam;
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode.Assign( aPrevIdx.GetNode(), +1 );
+ SwContentNode* pCNd = pPam->GetContentNode();
+ pPam->GetPoint()->nContent.Assign(pCNd, 0 );
+ pPam->SetMark();
+
+ pPam->GetPoint()->nNode += nOffsetTemp;
+ pCNd = pPam->GetContentNode();
+ pPam->GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+
+ SetValues( *pPam );
+
+ SetPaM(rPam);
+}
+
+void SwUndoRedlineSort::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ SwPaM* pPam = &rPam;
+ SwPosition* pStart = pPam->Start();
+ SwPosition* pEnd = pPam->End();
+
+ SwNodeIndex aPrevIdx( pStart->nNode, -1 );
+ sal_uLong nOffsetTemp = pEnd->nNode.GetIndex() - pStart->nNode.GetIndex();
+ const sal_Int32 nCntStt = pStart->nContent.GetIndex();
+
+ rDoc.SortText(rPam, *m_pOpt);
+
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode.Assign( aPrevIdx.GetNode(), +1 );
+ SwContentNode* pCNd = pPam->GetContentNode();
+ sal_Int32 nLen = pCNd->Len();
+ if( nLen > nCntStt )
+ nLen = nCntStt;
+ pPam->GetPoint()->nContent.Assign(pCNd, nLen );
+ pPam->SetMark();
+
+ pPam->GetPoint()->nNode += nOffsetTemp;
+ pCNd = pPam->GetContentNode();
+ pPam->GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+
+ SetValues( rPam );
+
+ SetPaM( rPam );
+ rPam.GetPoint()->nNode = m_nSaveEndNode;
+ rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), m_nSaveEndContent );
+}
+
+void SwUndoRedlineSort::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().SortText( rContext.GetRepeatPaM(), *m_pOpt );
+}
+
+void SwUndoRedlineSort::SetSaveRange( const SwPaM& rRange )
+{
+ const SwPosition& rPos = *rRange.End();
+ m_nSaveEndNode = rPos.nNode.GetIndex();
+ m_nSaveEndContent = rPos.nContent.GetIndex();
+}
+
+SwUndoAcceptRedline::SwUndoAcceptRedline( const SwPaM& rRange )
+ : SwUndoRedline( SwUndoId::ACCEPT_REDLINE, rRange )
+{
+}
+
+void SwUndoAcceptRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ rDoc.getIDocumentRedlineAccess().AcceptRedline(rPam, false);
+}
+
+void SwUndoAcceptRedline::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().getIDocumentRedlineAccess().AcceptRedline(rContext.GetRepeatPaM(), true);
+}
+
+SwUndoRejectRedline::SwUndoRejectRedline( const SwPaM& rRange )
+ : SwUndoRedline( SwUndoId::REJECT_REDLINE, rRange )
+{
+}
+
+void SwUndoRejectRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ rDoc.getIDocumentRedlineAccess().RejectRedline(rPam, false);
+}
+
+void SwUndoRejectRedline::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().getIDocumentRedlineAccess().RejectRedline(rContext.GetRepeatPaM(), true);
+}
+
+SwUndoCompDoc::SwUndoCompDoc( const SwPaM& rRg, bool bIns )
+ : SwUndo( SwUndoId::COMPAREDOC, rRg.GetDoc() ), SwUndRng( rRg ),
+ m_bInsert( bIns )
+{
+ SwDoc* pDoc = rRg.GetDoc();
+ if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ RedlineType eTyp = m_bInsert ? RedlineType::Insert : RedlineType::Delete;
+ m_pRedlineData.reset( new SwRedlineData( eTyp, pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+}
+
+SwUndoCompDoc::SwUndoCompDoc( const SwRangeRedline& rRedl )
+ : SwUndo( SwUndoId::COMPAREDOC, rRedl.GetDoc() ), SwUndRng( rRedl ),
+ // for MergeDoc the corresponding inverse is needed
+ m_bInsert( RedlineType::Delete == rRedl.GetType() )
+{
+ SwDoc* pDoc = rRedl.GetDoc();
+ if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlineData.reset( new SwRedlineData( rRedl.GetRedlineData() ) );
+ SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ m_pRedlineSaveDatas.reset( new SwRedlineSaveDatas );
+ if( !FillSaveData( rRedl, *m_pRedlineSaveDatas, false ))
+ {
+ m_pRedlineSaveDatas.reset();
+ }
+}
+
+SwUndoCompDoc::~SwUndoCompDoc()
+{
+ m_pRedlineData.reset();
+ m_pUndoDelete.reset();
+ m_pUndoDelete2.reset();
+ m_pRedlineSaveDatas.reset();
+}
+
+void SwUndoCompDoc::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwPaM& rPam(AddUndoRedoPaM(rContext));
+
+ if( !m_bInsert )
+ {
+ // delete Redlines
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On);
+
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+
+ // per definition Point is end (in SwUndRng!)
+ SwContentNode* pCSttNd = rPam.GetContentNode(false);
+ SwContentNode* pCEndNd = rPam.GetContentNode();
+
+ // if start- and end-content is zero, then the doc-compare moves
+ // complete nodes into the current doc. And then the selection
+ // must be from end to start, so the delete join into the right
+ // direction.
+ if( !m_nSttContent && !m_nEndContent )
+ rPam.Exchange();
+
+ bool bJoinText, bJoinPrev;
+ sw_GetJoinFlags(rPam, bJoinText, bJoinPrev);
+
+ m_pUndoDelete.reset( new SwUndoDelete(rPam, false) );
+
+ if( bJoinText )
+ sw_JoinText(rPam, bJoinPrev);
+
+ if( pCSttNd && !pCEndNd)
+ {
+ // #112139# Do not step behind the end of content.
+ SwNode & rTmp = rPam.GetNode();
+ SwNode * pEnd = rDoc.GetNodes().DocumentSectionEndNode(&rTmp);
+
+ if (&rTmp != pEnd)
+ {
+ rPam.SetMark();
+ ++rPam.GetPoint()->nNode;
+ rPam.GetBound().nContent.Assign( nullptr, 0 );
+ rPam.GetBound( false ).nContent.Assign( nullptr, 0 );
+ m_pUndoDelete2.reset( new SwUndoDelete(rPam, true) );
+ }
+ }
+ rPam.DeleteMark();
+ }
+ else
+ {
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
+
+ if( m_pRedlineSaveDatas )
+ SetSaveData(rDoc, *m_pRedlineSaveDatas);
+ }
+ SetPaM(rPam, true);
+ }
+}
+
+void SwUndoCompDoc::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+
+ if( m_bInsert )
+ {
+ SwPaM& rPam(AddUndoRedoPaM(rContext));
+ if( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ SwRangeRedline* pTmp = new SwRangeRedline(*m_pRedlineData, rPam);
+ rDoc.getIDocumentRedlineAccess().GetRedlineTable().Insert( pTmp );
+ pTmp->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ rDoc.getIDocumentRedlineAccess().SplitRedline(rPam);
+ }
+ SetPaM(rPam, true);
+ }
+ else
+ {
+ if( m_pUndoDelete2 )
+ {
+ m_pUndoDelete2->UndoImpl(rContext);
+ m_pUndoDelete2.reset();
+ }
+ m_pUndoDelete->UndoImpl(rContext);
+ m_pUndoDelete.reset();
+
+ // note: don't call SetPaM before executing Undo of members
+ SwPaM& rPam(AddUndoRedoPaM(rContext));
+
+ SwRangeRedline* pTmp = new SwRangeRedline(*m_pRedlineData, rPam);
+ rDoc.getIDocumentRedlineAccess().GetRedlineTable().Insert( pTmp );
+ pTmp->InvalidateRange(SwRangeRedline::Invalidation::Add);
+
+ SetPaM(rPam, true);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unsect.cxx b/sw/source/core/undo/unsect.cxx
new file mode 100644
index 000000000..ae2ba60a3
--- /dev/null
+++ b/sw/source/core/undo/unsect.cxx
@@ -0,0 +1,601 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <UndoSection.hxx>
+
+#include <osl/diagnose.h>
+#include <comphelper/scopeguard.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <fmtcntnt.hxx>
+#include <doc.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <poolfmt.hxx>
+#include <docary.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <section.hxx>
+#include <rolbck.hxx>
+#include <redline.hxx>
+#include <doctxm.hxx>
+#include <ftnidx.hxx>
+#include <rootfrm.hxx>
+#include <editsh.hxx>
+/// OD 04.10.2002 #102894#
+/// class Calc needed for calculation of the hidden condition of a section.
+#include <calc.hxx>
+
+static std::unique_ptr<SfxItemSet> lcl_GetAttrSet( const SwSection& rSect )
+{
+ // save attributes of the format (columns, color, ...)
+ // Content and Protect items are not interesting since they are already
+ // stored in Section, thus delete them.
+ std::unique_ptr<SfxItemSet> pAttr;
+ if( rSect.GetFormat() )
+ {
+ sal_uInt16 nCnt = 1;
+ if( rSect.IsProtect() )
+ ++nCnt;
+
+ if( nCnt < rSect.GetFormat()->GetAttrSet().Count() )
+ {
+ pAttr.reset(new SfxItemSet( rSect.GetFormat()->GetAttrSet() ));
+ pAttr->ClearItem( RES_PROTECT );
+ pAttr->ClearItem( RES_CNTNT );
+ if( !pAttr->Count() )
+ {
+ pAttr.reset();
+ }
+ }
+ }
+ return pAttr;
+}
+
+SwUndoInsSection::SwUndoInsSection(
+ SwPaM const& rPam, SwSectionData const& rNewData,
+ SfxItemSet const*const pSet,
+ std::pair<SwTOXBase const*, sw::RedlineMode> const*const pTOXBase)
+ : SwUndo( SwUndoId::INSSECTION, rPam.GetDoc() ), SwUndRng( rPam )
+ , m_pSectionData(new SwSectionData(rNewData))
+ , m_pTOXBase( pTOXBase
+ ? std::make_unique<std::pair<SwTOXBase *, sw::RedlineMode>>(
+ new SwTOXBase(*pTOXBase->first), pTOXBase->second)
+ : nullptr )
+ , m_pAttrSet( (pSet && pSet->Count()) ? new SfxItemSet( *pSet ) : nullptr )
+ , m_nSectionNodePos(0)
+ , m_bSplitAtStart(false)
+ , m_bSplitAtEnd(false)
+ , m_bUpdateFootnote(false)
+{
+ SwDoc& rDoc = *rPam.GetDoc();
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlData.reset(new SwRedlineData( RedlineType::Insert,
+ rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ));
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+ m_pRedlineSaveData.reset( new SwRedlineSaveDatas );
+ if( !FillSaveData( rPam, *m_pRedlineSaveData, false ))
+ m_pRedlineSaveData.reset();
+
+ if( !rPam.HasMark() )
+ {
+ const SwContentNode* pCNd = rPam.GetPoint()->nNode.GetNode().GetContentNode();
+ if( pCNd && pCNd->HasSwAttrSet() && (
+ !rPam.GetPoint()->nContent.GetIndex() ||
+ rPam.GetPoint()->nContent.GetIndex() == pCNd->Len() ))
+ {
+ SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange );
+ aBrkSet.Put( *pCNd->GetpSwAttrSet() );
+ if( aBrkSet.Count() )
+ {
+ m_pHistory.reset( new SwHistory );
+ m_pHistory->CopyFormatAttr( aBrkSet, pCNd->GetIndex() );
+ }
+ }
+ }
+}
+
+SwUndoInsSection::~SwUndoInsSection()
+{
+}
+
+void SwUndoInsSection::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ RemoveIdxFromSection( rDoc, m_nSectionNodePos );
+
+ SwSectionNode *const pNd =
+ rDoc.GetNodes()[ m_nSectionNodePos ]->GetSectionNode();
+ OSL_ENSURE( pNd, "where is my SectionNode?" );
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( *pNd, true, RedlineType::Any );
+
+ // no selection?
+ SwNodeIndex aIdx( *pNd );
+ if( ( !m_nEndNode && COMPLETE_STRING == m_nEndContent ) ||
+ ( m_nSttNode == m_nEndNode && m_nSttContent == m_nEndContent ))
+ // delete simply all nodes
+ rDoc.GetNodes().Delete( aIdx, pNd->EndOfSectionIndex() -
+ aIdx.GetIndex() );
+ else
+ // just delete format, rest happens automatically
+ rDoc.DelSectionFormat( pNd->GetSection().GetFormat() );
+
+ // do we need to consolidate?
+ if (m_bSplitAtStart)
+ {
+ Join( rDoc, m_nSttNode );
+ }
+
+ if (m_bSplitAtEnd)
+ {
+ Join( rDoc, m_nEndNode );
+ }
+
+ if (m_pHistory)
+ {
+ m_pHistory->TmpRollback( &rDoc, 0, false );
+ }
+
+ if (m_bUpdateFootnote)
+ {
+ rDoc.GetFootnoteIdxs().UpdateFootnote( aIdx );
+ }
+
+ AddUndoRedoPaM(rContext);
+
+ if (m_pRedlineSaveData)
+ SetSaveData( rDoc, *m_pRedlineSaveData );
+}
+
+void SwUndoInsSection::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+
+ const SwTOXBaseSection* pUpdateTOX = nullptr;
+ if (m_pTOXBase)
+ {
+ SwRootFrame const* pLayout(nullptr);
+ SwRootFrame * pLayoutToReset(nullptr);
+ comphelper::ScopeGuard g([&]() {
+ if (pLayoutToReset)
+ {
+ pLayoutToReset->SetHideRedlines(m_pTOXBase->second == sw::RedlineMode::Shown);
+ }
+ });
+ o3tl::sorted_vector<SwRootFrame *> layouts(rDoc.GetAllLayouts());
+ for (SwRootFrame const*const p : layouts)
+ {
+ if ((m_pTOXBase->second == sw::RedlineMode::Hidden) == p->IsHideRedlines())
+ {
+ pLayout = p;
+ break;
+ }
+ }
+ if (!pLayout)
+ {
+ assert(!layouts.empty()); // must have one layout
+ pLayoutToReset = *layouts.begin();
+ pLayoutToReset->SetHideRedlines(m_pTOXBase->second == sw::RedlineMode::Hidden);
+ pLayout = pLayoutToReset;
+ }
+ pUpdateTOX = rDoc.InsertTableOf( *rPam.GetPoint(),
+ // don't expand: will be done by SwUndoUpdateIndex::RedoImpl()
+ *m_pTOXBase->first, m_pAttrSet.get(), false, pLayout);
+ }
+ else
+ {
+ rDoc.InsertSwSection(rPam, *m_pSectionData, nullptr, m_pAttrSet.get());
+ }
+
+ if (m_pHistory)
+ {
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+
+ SwSectionNode *const pSectNd =
+ rDoc.GetNodes()[ m_nSectionNodePos ]->GetSectionNode();
+ if (m_pRedlData &&
+ IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags()))
+ {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
+
+ SwPaM aPam( *pSectNd->EndOfSectionNode(), *pSectNd, 1 );
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData, aPam ), true);
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPaM aPam( *pSectNd->EndOfSectionNode(), *pSectNd, 1 );
+ rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+
+ if( pUpdateTOX )
+ {
+ // initiate formatting
+ SwEditShell* pESh = rDoc.GetEditShell();
+ if( pESh )
+ pESh->CalcLayout();
+
+ // insert page numbers
+ const_cast<SwTOXBaseSection*>(pUpdateTOX)->UpdatePageNum();
+ }
+}
+
+void SwUndoInsSection::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if (m_pTOXBase)
+ {
+ rDoc.InsertTableOf(*rContext.GetRepeatPaM().GetPoint(),
+ *m_pTOXBase->first, m_pAttrSet.get(), true,
+ rDoc.getIDocumentLayoutAccess().GetCurrentLayout()); // TODO add shell to RepeatContext?
+ }
+ else
+ {
+ rDoc.InsertSwSection(rContext.GetRepeatPaM(),
+ *m_pSectionData, nullptr, m_pAttrSet.get());
+ }
+}
+
+void SwUndoInsSection::Join( SwDoc& rDoc, sal_uLong nNode )
+{
+ SwNodeIndex aIdx( rDoc.GetNodes(), nNode );
+ SwTextNode* pTextNd = aIdx.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "Where is my TextNode?" );
+
+ {
+ RemoveIdxRel(
+ nNode + 1,
+ SwPosition( aIdx, SwIndex( pTextNd, pTextNd->GetText().getLength() ) ) );
+ }
+ pTextNd->JoinNext();
+
+ if (m_pHistory)
+ {
+ SwIndex aCntIdx( pTextNd, 0 );
+ pTextNd->RstTextAttr( aCntIdx, pTextNd->Len(), 0, nullptr, true );
+ }
+}
+
+void
+SwUndoInsSection::SaveSplitNode(SwTextNode *const pTextNd, bool const bAtStart)
+{
+ if( pTextNd->GetpSwpHints() )
+ {
+ if (!m_pHistory)
+ {
+ m_pHistory.reset( new SwHistory );
+ }
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), pTextNd->GetIndex(), 0,
+ pTextNd->GetText().getLength(), false );
+ }
+
+ if (bAtStart)
+ {
+ m_bSplitAtStart = true;
+ }
+ else
+ {
+ m_bSplitAtEnd = true;
+ }
+}
+
+class SwUndoDelSection
+ : public SwUndo
+{
+private:
+ std::unique_ptr<SwSectionData> const m_pSectionData; /// section not TOX
+ std::unique_ptr<SwTOXBase> const m_pTOXBase; /// set iff section is TOX
+ std::unique_ptr<SfxItemSet> const m_pAttrSet;
+ std::shared_ptr< ::sfx2::MetadatableUndo > const m_pMetadataUndo;
+ sal_uLong const m_nStartNode;
+ sal_uLong const m_nEndNode;
+
+public:
+ SwUndoDelSection(
+ SwSectionFormat const&, SwSection const&, SwNodeIndex const*const);
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+};
+
+std::unique_ptr<SwUndo> MakeUndoDelSection(SwSectionFormat const& rFormat)
+{
+ return std::make_unique<SwUndoDelSection>(rFormat, *rFormat.GetSection(),
+ rFormat.GetContent().GetContentIdx());
+}
+
+SwUndoDelSection::SwUndoDelSection(
+ SwSectionFormat const& rSectionFormat, SwSection const& rSection,
+ SwNodeIndex const*const pIndex)
+ : SwUndo( SwUndoId::DELSECTION, rSectionFormat.GetDoc() )
+ , m_pSectionData( new SwSectionData(rSection) )
+ , m_pTOXBase( dynamic_cast<const SwTOXBaseSection*>( &rSection) != nullptr
+ ? new SwTOXBase(static_cast<SwTOXBaseSection const&>(rSection))
+ : nullptr )
+ , m_pAttrSet( ::lcl_GetAttrSet(rSection) )
+ , m_pMetadataUndo( rSectionFormat.CreateUndo() )
+ , m_nStartNode( pIndex->GetIndex() )
+ , m_nEndNode( pIndex->GetNode().EndOfSectionIndex() )
+{
+}
+
+void SwUndoDelSection::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if (m_pTOXBase)
+ {
+ // sw_redlinehide: this should work as-is; there will be another undo for the update
+ rDoc.InsertTableOf(m_nStartNode, m_nEndNode-2, *m_pTOXBase,
+ m_pAttrSet.get());
+ }
+ else
+ {
+ SwNodeIndex aStt( rDoc.GetNodes(), m_nStartNode );
+ SwNodeIndex aEnd( rDoc.GetNodes(), m_nEndNode-2 );
+ SwSectionFormat* pFormat = rDoc.MakeSectionFormat();
+ if (m_pAttrSet)
+ {
+ pFormat->SetFormatAttr( *m_pAttrSet );
+ }
+
+ /// OD 04.10.2002 #102894#
+ /// remember inserted section node for further calculations
+ SwSectionNode* pInsertedSectNd = rDoc.GetNodes().InsertTextSection(
+ aStt, *pFormat, *m_pSectionData, nullptr, & aEnd);
+
+ if( SfxItemState::SET == pFormat->GetItemState( RES_FTN_AT_TXTEND ) ||
+ SfxItemState::SET == pFormat->GetItemState( RES_END_AT_TXTEND ))
+ {
+ rDoc.GetFootnoteIdxs().UpdateFootnote( aStt );
+ }
+
+ /// OD 04.10.2002 #102894#
+ /// consider that section is hidden by condition.
+ /// If section is hidden by condition,
+ /// recalculate condition and update hidden condition flag.
+ /// Recalculation is necessary, because fields, on which the hide
+ /// condition depends, can be changed - fields changes aren't undoable.
+ /// NOTE: setting hidden condition flag also creates/deletes corresponding
+ /// frames, if the hidden condition flag changes.
+ SwSection& aInsertedSect = pInsertedSectNd->GetSection();
+ if ( aInsertedSect.IsHidden() &&
+ !aInsertedSect.GetCondition().isEmpty() )
+ {
+ SwCalc aCalc( rDoc );
+ rDoc.getIDocumentFieldsAccess().FieldsToCalc(aCalc, pInsertedSectNd->GetIndex(), USHRT_MAX);
+ bool bRecalcCondHidden =
+ aCalc.Calculate( aInsertedSect.GetCondition() ).GetBool();
+ aInsertedSect.SetCondHidden( bRecalcCondHidden );
+ }
+
+ pFormat->RestoreMetadata(m_pMetadataUndo);
+ }
+}
+
+void SwUndoDelSection::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwSectionNode *const pNd =
+ rDoc.GetNodes()[ m_nStartNode ]->GetSectionNode();
+ OSL_ENSURE( pNd, "Where is my SectionNode?" );
+ // just delete format, rest happens automatically
+ rDoc.DelSectionFormat( pNd->GetSection().GetFormat() );
+}
+
+namespace {
+
+class SwUndoUpdateSection
+ : public SwUndo
+{
+private:
+ std::unique_ptr<SwSectionData> m_pSectionData;
+ std::unique_ptr<SfxItemSet> m_pAttrSet;
+ sal_uLong const m_nStartNode;
+ bool const m_bOnlyAttrChanged;
+
+public:
+ SwUndoUpdateSection(
+ SwSection const&, SwNodeIndex const*const, bool const bOnlyAttr);
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+};
+
+}
+
+std::unique_ptr<SwUndo>
+MakeUndoUpdateSection(SwSectionFormat const& rFormat, bool const bOnlyAttr)
+{
+ return std::make_unique<SwUndoUpdateSection>(*rFormat.GetSection(),
+ rFormat.GetContent().GetContentIdx(), bOnlyAttr);
+}
+
+SwUndoUpdateSection::SwUndoUpdateSection(
+ SwSection const& rSection, SwNodeIndex const*const pIndex,
+ bool const bOnlyAttr)
+ : SwUndo( SwUndoId::CHGSECTION, pIndex->GetNode().GetDoc() )
+ , m_pSectionData( new SwSectionData(rSection) )
+ , m_pAttrSet( ::lcl_GetAttrSet(rSection) )
+ , m_nStartNode( pIndex->GetIndex() )
+ , m_bOnlyAttrChanged( bOnlyAttr )
+{
+}
+
+void SwUndoUpdateSection::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwSectionNode *const pSectNd =
+ rDoc.GetNodes()[ m_nStartNode ]->GetSectionNode();
+ OSL_ENSURE( pSectNd, "Where is my SectionNode?" );
+
+ SwSection& rNdSect = pSectNd->GetSection();
+ SwFormat* pFormat = rNdSect.GetFormat();
+
+ std::unique_ptr<SfxItemSet> pCur = ::lcl_GetAttrSet( rNdSect );
+ if (m_pAttrSet)
+ {
+ // The Content and Protect items must persist
+ const SfxPoolItem* pItem;
+ m_pAttrSet->Put( pFormat->GetFormatAttr( RES_CNTNT ));
+ if( SfxItemState::SET == pFormat->GetItemState( RES_PROTECT, true, &pItem ))
+ {
+ m_pAttrSet->Put( *pItem );
+ }
+ pFormat->DelDiffs( *m_pAttrSet );
+ m_pAttrSet->ClearItem( RES_CNTNT );
+ pFormat->SetFormatAttr( *m_pAttrSet );
+ }
+ else
+ {
+ // than the old ones need to be deleted
+ pFormat->ResetFormatAttr( RES_FRMATR_BEGIN, RES_BREAK );
+ pFormat->ResetFormatAttr( RES_HEADER, RES_OPAQUE );
+ pFormat->ResetFormatAttr( RES_SURROUND, RES_FRMATR_END-1 );
+ }
+ m_pAttrSet = std::move(pCur);
+
+ if (!m_bOnlyAttrChanged)
+ {
+ const bool bUpdate =
+ (!rNdSect.IsLinkType() && m_pSectionData->IsLinkType())
+ || ( !m_pSectionData->GetLinkFileName().isEmpty()
+ && (m_pSectionData->GetLinkFileName() !=
+ rNdSect.GetLinkFileName()));
+
+ // swap stored section data with live section data
+ SwSectionData *const pOld( new SwSectionData(rNdSect) );
+ rNdSect.SetSectionData(*m_pSectionData);
+ m_pSectionData.reset(pOld);
+
+ if( bUpdate )
+ rNdSect.CreateLink( LinkCreateType::Update );
+ else if( SectionType::Content == rNdSect.GetType() && rNdSect.IsConnected() )
+ {
+ rNdSect.Disconnect();
+ rDoc.getIDocumentLinksAdministration().GetLinkManager().Remove( &rNdSect.GetBaseLink() );
+ }
+ }
+}
+
+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: */
diff --git a/sw/source/core/undo/unsort.cxx b/sw/source/core/undo/unsort.cxx
new file mode 100644
index 000000000..8f0820fb6
--- /dev/null
+++ b/sw/source/core/undo/unsort.cxx
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <UndoSort.hxx>
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <UndoTable.hxx>
+#include <sortopt.hxx>
+#include <docsort.hxx>
+#include <node2lay.hxx>
+
+// Undo for Sorting
+SwSortUndoElement::~SwSortUndoElement()
+{
+ // are there string pointers saved?
+ if( 0xffffffff != SORT_TXT_TBL.TXT.nID )
+ {
+ delete SORT_TXT_TBL.TBL.pSource;
+ delete SORT_TXT_TBL.TBL.pTarget;
+ }
+}
+
+SwUndoSort::SwUndoSort(const SwPaM& rRg, const SwSortOptions& rOpt)
+ : SwUndo(SwUndoId::SORT_TXT, rRg.GetDoc())
+ , SwUndRng(rRg)
+ , nTableNd(0)
+{
+ pSortOpt.reset( new SwSortOptions(rOpt) );
+}
+
+SwUndoSort::SwUndoSort( sal_uLong nStt, sal_uLong nEnd, const SwTableNode& rTableNd,
+ const SwSortOptions& rOpt, bool bSaveTable )
+ : SwUndo(SwUndoId::SORT_TBL, rTableNd.GetDoc())
+{
+ m_nSttNode = nStt;
+ m_nEndNode = nEnd;
+ nTableNd = rTableNd.GetIndex();
+
+ pSortOpt.reset( new SwSortOptions(rOpt) );
+ if( bSaveTable )
+ pUndoTableAttr.reset( new SwUndoAttrTable( rTableNd ) );
+}
+
+SwUndoSort::~SwUndoSort()
+{
+ pSortOpt.reset();
+ pUndoTableAttr.reset();
+}
+
+void SwUndoSort::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if(pSortOpt->bTable)
+ {
+ // Undo for Table
+ RemoveIdxFromSection( rDoc, m_nSttNode, &m_nEndNode );
+
+ if( pUndoTableAttr )
+ {
+ pUndoTableAttr->UndoImpl(rContext);
+ }
+
+ SwTableNode* pTableNd = rDoc.GetNodes()[ nTableNd ]->GetTableNode();
+
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ SwNode2LayoutSaveUpperFrames aNode2Layout(*pTableNd);
+
+ pTableNd->DelFrames();
+ const SwTable& rTable = pTableNd->GetTable();
+
+ SwMovedBoxes aMovedList;
+ for (const std::unique_ptr<SwSortUndoElement> & i : m_SortList)
+ {
+ const SwTableBox* pSource = rTable.GetTableBox(
+ *i->SORT_TXT_TBL.TBL.pSource );
+ const SwTableBox* pTarget = rTable.GetTableBox(
+ *i->SORT_TXT_TBL.TBL.pTarget );
+
+ // move back
+ MoveCell(&rDoc, pTarget, pSource,
+ USHRT_MAX != aMovedList.GetPos(pSource) );
+
+ // store moved entry in list
+ aMovedList.push_back(pTarget);
+ }
+
+ // Restore table frames:
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ const sal_uLong nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( rDoc.GetNodes(), nIdx, nIdx + 1 );
+ }
+ else
+ {
+ // Undo for Text
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ RemoveIdxFromRange(rPam, true);
+
+ // create index for (sorted) positions
+ // The IndexList must be created based on (asc.) sorted SourcePosition.
+ std::vector<SwNodeIndex> aIdxList;
+ aIdxList.reserve(m_SortList.size());
+
+ for (size_t i = 0; i < m_SortList.size(); ++i)
+ {
+ for (const std::unique_ptr<SwSortUndoElement> & j : m_SortList)
+ {
+ if (j->SORT_TXT_TBL.TXT.nSource == m_nSttNode + i)
+ {
+ aIdxList.push_back( SwNodeIndex( rDoc.GetNodes(),
+ j->SORT_TXT_TBL.TXT.nTarget ) );
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < m_SortList.size(); ++i)
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode + i );
+ SwNodeRange aRg( aIdxList[i], 0, aIdxList[i], 1 );
+ rDoc.getIDocumentContentOperations().MoveNodeRange(aRg, aIdx,
+ SwMoveFlags::DEFAULT);
+ }
+ // delete indices
+ aIdxList.clear();
+ SetPaM(rPam, true);
+ }
+}
+
+void SwUndoSort::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if(pSortOpt->bTable)
+ {
+ // Redo for Table
+ RemoveIdxFromSection( rDoc, m_nSttNode, &m_nEndNode );
+
+ SwTableNode* pTableNd = rDoc.GetNodes()[ nTableNd ]->GetTableNode();
+
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ SwNode2LayoutSaveUpperFrames aNode2Layout(*pTableNd);
+
+ pTableNd->DelFrames();
+ const SwTable& rTable = pTableNd->GetTable();
+
+ SwMovedBoxes aMovedList;
+ for (const std::unique_ptr<SwSortUndoElement> & i : m_SortList)
+ {
+ const SwTableBox* pSource = rTable.GetTableBox(
+ *i->SORT_TXT_TBL.TBL.pSource );
+ const SwTableBox* pTarget = rTable.GetTableBox(
+ *i->SORT_TXT_TBL.TBL.pTarget );
+
+ // move back
+ MoveCell(&rDoc, pSource, pTarget,
+ USHRT_MAX != aMovedList.GetPos( pTarget ) );
+ // store moved entry in list
+ aMovedList.push_back( pSource );
+ }
+
+ if( pUndoTableAttr )
+ {
+ pUndoTableAttr->RedoImpl(rContext);
+ }
+
+ // Restore table frames:
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ const sal_uLong nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( rDoc.GetNodes(), nIdx, nIdx + 1 );
+ }
+ else
+ {
+ // Redo for Text
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ SetPaM(rPam);
+ RemoveIdxFromRange(rPam, true);
+
+ std::vector<SwNodeIndex> aIdxList;
+ aIdxList.reserve(m_SortList.size());
+
+ for (size_t i = 0; i < m_SortList.size(); ++i)
+ { // current position is starting point
+ aIdxList.push_back( SwNodeIndex( rDoc.GetNodes(),
+ m_SortList[i]->SORT_TXT_TBL.TXT.nSource) );
+ }
+
+ for (size_t i = 0; i < m_SortList.size(); ++i)
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode + i);
+ SwNodeRange aRg( aIdxList[i], 0, aIdxList[i], 1 );
+ rDoc.getIDocumentContentOperations().MoveNodeRange(aRg, aIdx,
+ SwMoveFlags::DEFAULT);
+ }
+ // delete indices
+ aIdxList.clear();
+ SetPaM(rPam, true);
+ SwTextNode const*const pTNd = rPam.GetNode().GetTextNode();
+ if( pTNd )
+ {
+ rPam.GetPoint()->nContent = pTNd->GetText().getLength();
+ }
+ }
+}
+
+void SwUndoSort::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // table not repeat capable
+ if(!pSortOpt->bTable)
+ {
+ SwPaM *const pPam = & rContext.GetRepeatPaM();
+ SwDoc& rDoc = *pPam->GetDoc();
+
+ if( !rDoc.IsIdxInTable( pPam->Start()->nNode ) )
+ rDoc.SortText(*pPam, *pSortOpt);
+ }
+}
+
+void SwUndoSort::Insert( const OUString& rOrgPos, const OUString& rNewPos)
+{
+ m_SortList.push_back(std::make_unique< SwSortUndoElement>(rOrgPos, rNewPos));
+}
+
+void SwUndoSort::Insert( sal_uLong nOrgPos, sal_uLong nNewPos)
+{
+ m_SortList.push_back(std::make_unique<SwSortUndoElement>(nOrgPos, nNewPos));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unspnd.cxx b/sw/source/core/undo/unspnd.cxx
new file mode 100644
index 000000000..112db668d
--- /dev/null
+++ b/sw/source/core/undo/unspnd.cxx
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoSplitMove.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <swundo.hxx>
+#include <frmfmt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <redline.hxx>
+#include <docary.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <osl/diagnose.h>
+
+// SPLITNODE
+
+SwUndoSplitNode::SwUndoSplitNode( SwDoc* pDoc, const SwPosition& rPos,
+ bool bChkTable )
+ : SwUndo( SwUndoId::SPLITNODE, pDoc ), nNode( rPos.nNode.GetIndex() ),
+ nContent( rPos.nContent.GetIndex() ),
+ bTableFlag( false ), bChkTableStt( bChkTable )
+{
+ SwTextNode *const pTextNd = rPos.nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "only for TextNode" );
+ if( pTextNd->GetpSwpHints() )
+ {
+ m_pHistory.reset(new SwHistory);
+ m_pHistory->CopyAttr(pTextNd->GetpSwpHints(), nNode, 0,
+ pTextNd->GetText().getLength(), false );
+ if (!m_pHistory->Count())
+ {
+ m_pHistory.reset();
+ }
+ }
+ // consider Redline
+ if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ pRedlData.reset( new SwRedlineData( RedlineType::Insert, pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ nParRsid = pTextNd->GetParRsid();
+}
+
+SwUndoSplitNode::~SwUndoSplitNode()
+{
+ m_pHistory.reset();
+ pRedlData.reset();
+}
+
+void SwUndoSplitNode::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ rPam.DeleteMark();
+ if( bTableFlag )
+ {
+ // than a TextNode was added directly before the current table
+ SwNodeIndex& rIdx = rPam.GetPoint()->nNode;
+ rIdx = nNode;
+ SwTextNode* pTNd;
+ SwNode* pCurrNd = pDoc->GetNodes()[ nNode + 1 ];
+ SwTableNode* pTableNd = pCurrNd->FindTableNode();
+ if( pCurrNd->IsContentNode() && pTableNd &&
+ nullptr != ( pTNd = pDoc->GetNodes()[ pTableNd->GetIndex()-1 ]->GetTextNode() ))
+ {
+ // move break attributes
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+ const SfxItemSet* pNdSet = pTNd->GetpSwAttrSet();
+ if( pNdSet )
+ {
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pNdSet->GetItemState( RES_PAGEDESC, false,
+ &pItem ) )
+ pTableFormat->SetFormatAttr( *pItem );
+
+ if( SfxItemState::SET == pNdSet->GetItemState( RES_BREAK, false,
+ &pItem ) )
+ pTableFormat->SetFormatAttr( *pItem );
+ }
+
+ // than delete it again
+ SwNodeIndex aDelNd( *pTableNd, -1 );
+ rPam.GetPoint()->nContent.Assign( static_cast<SwContentNode*>(pCurrNd), 0 );
+ RemoveIdxRel( aDelNd.GetIndex(), *rPam.GetPoint() );
+ pDoc->GetNodes().Delete( aDelNd );
+ }
+ }
+ else
+ {
+ SwTextNode * pTNd = pDoc->GetNodes()[ nNode ]->GetTextNode();
+ if( pTNd )
+ {
+ rPam.GetPoint()->nNode = *pTNd;
+ rPam.GetPoint()->nContent.Assign(pTNd, pTNd->GetText().getLength());
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ rPam.SetMark();
+ ++rPam.GetMark()->nNode;
+ rPam.GetMark()->nContent.Assign( rPam.GetMark()->
+ nNode.GetNode().GetContentNode(), 0 );
+ pDoc->getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any );
+ rPam.DeleteMark();
+ }
+
+ RemoveIdxRel( nNode+1, *rPam.GetPoint() );
+
+ pTNd->JoinNext();
+ if (m_pHistory)
+ {
+ rPam.GetPoint()->nContent = 0;
+ rPam.SetMark();
+ rPam.GetPoint()->nContent = pTNd->GetText().getLength();
+
+ pDoc->RstTextAttrs( rPam, true );
+ m_pHistory->TmpRollback( pDoc, 0, false );
+ }
+
+ pDoc->UpdateParRsid( pTNd, nParRsid );
+ }
+ }
+
+ // also set the cursor onto undo section
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = nNode;
+ rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), nContent );
+}
+
+void SwUndoSplitNode::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ rPam.GetPoint()->nNode = nNode;
+ SwTextNode * pTNd = rPam.GetNode().GetTextNode();
+ OSL_ENSURE(pTNd, "SwUndoSplitNode::RedoImpl(): SwTextNode expected");
+ if (pTNd)
+ {
+ rPam.GetPoint()->nContent.Assign( pTNd, nContent );
+
+ SwDoc* pDoc = rPam.GetDoc();
+ pDoc->getIDocumentContentOperations().SplitNode( *rPam.GetPoint(), bChkTableStt );
+
+ if (m_pHistory)
+ {
+ m_pHistory->SetTmpEnd(m_pHistory->Count());
+ }
+
+ if( ( pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) ||
+ ( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ rPam.SetMark();
+ if( rPam.Move( fnMoveBackward ))
+ {
+ if( pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
+ pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *pRedlData, rPam ), true);
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else
+ pDoc->getIDocumentRedlineAccess().SplitRedline( rPam );
+ rPam.Exchange();
+ }
+ rPam.DeleteMark();
+ }
+ }
+}
+
+void SwUndoSplitNode::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().getIDocumentContentOperations().SplitNode(
+ *rContext.GetRepeatPaM().GetPoint(), bChkTableStt );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/untbl.cxx b/sw/source/core/undo/untbl.cxx
new file mode 100644
index 000000000..1cbf5c1bd
--- /dev/null
+++ b/sw/source/core/undo/untbl.cxx
@@ -0,0 +1,3155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoTable.hxx>
+#include <UndoRedline.hxx>
+#include <UndoDelete.hxx>
+#include <UndoSplitMove.hxx>
+#include <UndoCore.hxx>
+#include <fesh.hxx>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <doc.hxx>
+#include <docredln.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <editsh.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <pam.hxx>
+#include <tblsel.hxx>
+#include <swundo.hxx>
+#include <rolbck.hxx>
+#include <ddefld.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <swcache.hxx>
+#include <tblafmt.hxx>
+#include <poolfmt.hxx>
+#include <mvsave.hxx>
+#include <cellatr.hxx>
+#include <swtblfmt.hxx>
+#include <swddetbl.hxx>
+#include <redline.hxx>
+#include <node2lay.hxx>
+#include <tblrwcl.hxx>
+#include <fmtanchr.hxx>
+#include <strings.hrc>
+#include <unochart.hxx>
+#include <calbck.hxx>
+#include <frameformats.hxx>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#ifdef DBG_UTIL
+#define CHECK_TABLE(t) (t).CheckConsistency();
+#else
+#define CHECK_TABLE(t)
+#endif
+
+#ifdef DBG_UTIL
+ #define DEBUG_REDLINE( pDoc ) sw_DebugRedline( pDoc );
+#else
+ #define DEBUG_REDLINE( pDoc )
+#endif
+
+typedef std::vector<std::shared_ptr<SfxItemSet> > SfxItemSets;
+
+struct UndoTableCpyTable_Entry
+{
+ sal_uLong nBoxIdx, nOffset;
+ std::unique_ptr<SfxItemSet> pBoxNumAttr;
+ std::unique_ptr<SwUndo> pUndo;
+
+ // Was the last paragraph of the new and the first paragraph of the old content joined?
+ bool bJoin; // For redlining only
+
+ explicit UndoTableCpyTable_Entry( const SwTableBox& rBox );
+};
+
+namespace {
+
+class SaveBox;
+class SaveLine;
+
+}
+
+class SaveTable
+{
+ friend SaveBox;
+ friend SaveLine;
+ SfxItemSet m_aTableSet;
+ std::unique_ptr<SaveLine> m_pLine;
+ const SwTable* m_pSwTable;
+ SfxItemSets m_aSets;
+ SwFrameFormatsV m_aFrameFormats;
+ sal_uInt16 m_nLineCount;
+ bool m_bModifyBox : 1;
+ bool m_bSaveFormula : 1;
+ bool m_bNewModel : 1;
+
+ SaveTable(const SaveTable&) = delete;
+ SaveTable& operator=(const SaveTable&) = delete;
+
+public:
+ SaveTable( const SwTable& rTable, sal_uInt16 nLnCnt = USHRT_MAX,
+ bool bSaveFormula = true );
+
+ sal_uInt16 AddFormat( SwFrameFormat* pFormat, bool bIsLine );
+ void NewFrameFormat( const SwTableLine* , const SwTableBox*, sal_uInt16 nFormatPos,
+ SwFrameFormat* pOldFormat );
+
+ void RestoreAttr( SwTable& rTable, bool bModifyBox = false );
+ void SaveContentAttrs( SwDoc* pDoc );
+ void CreateNew( SwTable& rTable, bool bCreateFrames = true,
+ bool bRestoreChart = true );
+ bool IsNewModel() const { return m_bNewModel; }
+};
+
+namespace {
+
+class SaveLine
+{
+ friend SaveTable;
+ friend class SaveBox;
+
+ SaveLine* pNext;
+ SaveBox* pBox;
+ sal_uInt16 nItemSet;
+
+ SaveLine(const SaveLine&) = delete;
+ SaveLine& operator=(const SaveLine&) = delete;
+
+public:
+ SaveLine( SaveLine* pPrev, const SwTableLine& rLine, SaveTable& rSTable );
+ ~SaveLine();
+
+ void RestoreAttr( SwTableLine& rLine, SaveTable& rSTable );
+ void SaveContentAttrs( SwDoc* pDoc );
+
+ void CreateNew( SwTable& rTable, SwTableBox& rParent, SaveTable& rSTable );
+};
+
+class SaveBox
+{
+ friend class SaveLine;
+
+ SaveBox* pNext;
+ sal_uLong nSttNode;
+ long nRowSpan;
+ sal_uInt16 nItemSet;
+ union
+ {
+ SfxItemSets* pContentAttrs;
+ SaveLine* pLine;
+ } Ptrs;
+
+public:
+ SaveBox( SaveBox* pPrev, const SwTableBox& rBox, SaveTable& rSTable );
+ ~SaveBox();
+
+ void RestoreAttr( SwTableBox& rBox, SaveTable& rSTable );
+ void SaveContentAttrs( SwDoc* pDoc );
+
+ void CreateNew( SwTable& rTable, SwTableLine& rParent, SaveTable& rSTable );
+};
+
+}
+
+#if OSL_DEBUG_LEVEL > 0
+static void CheckTable( const SwTable& );
+#define CHECKTABLE(t) CheckTable( t );
+#else
+#define CHECKTABLE(t)
+#endif
+
+/* #130880: Crash in undo of table to text when the table has (freshly) merged cells
+The order of cell content nodes in the nodes array is not given by the recursive table structure.
+The algorithm must not rely on this even it holds for a fresh loaded table in odt file format.
+So we need to remember not only the start node position but the end node position as well.
+*/
+
+struct SwTableToTextSave
+{
+ sal_uLong m_nSttNd;
+ sal_uLong m_nEndNd;
+ sal_Int32 m_nContent;
+ std::unique_ptr<SwHistory> m_pHstry;
+ // metadata references for first and last paragraph in cell
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoStart;
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoEnd;
+
+ SwTableToTextSave( SwDoc& rDoc, sal_uLong nNd, sal_uLong nEndIdx, sal_Int32 nContent );
+
+private:
+ SwTableToTextSave(const SwTableToTextSave&) = delete;
+ SwTableToTextSave& operator=(const SwTableToTextSave&) = delete;
+
+};
+
+sal_uInt16 const aSave_BoxContentSet[] = {
+ RES_CHRATR_COLOR, RES_CHRATR_CROSSEDOUT,
+ RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
+ RES_CHRATR_POSTURE, RES_CHRATR_POSTURE,
+ RES_CHRATR_SHADOWED, RES_CHRATR_WEIGHT,
+ RES_PARATR_ADJUST, RES_PARATR_ADJUST,
+ 0 };
+
+SwUndoInsTable::SwUndoInsTable( const SwPosition& rPos, sal_uInt16 nCl, sal_uInt16 nRw,
+ sal_uInt16 nAdj, const SwInsertTableOptions& rInsTableOpts,
+ const SwTableAutoFormat* pTAFormat,
+ const std::vector<sal_uInt16> *pColArr,
+ const OUString & rName)
+ : SwUndo( SwUndoId::INSTABLE, rPos.GetDoc() ),
+ m_aInsTableOptions( rInsTableOpts ),
+ m_nStartNode( rPos.nNode.GetIndex() ), m_nRows( nRw ), m_nColumns( nCl ), m_nAdjust( nAdj )
+{
+ if( pColArr )
+ {
+ m_pColumnWidth.reset( new std::vector<sal_uInt16>(*pColArr) );
+ }
+ if( pTAFormat )
+ m_pAutoFormat.reset( new SwTableAutoFormat( *pTAFormat ) );
+
+ // consider redline
+ SwDoc& rDoc = *rPos.nNode.GetNode().GetDoc();
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlineData.reset( new SwRedlineData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ m_sTableName = rName;
+}
+
+SwUndoInsTable::~SwUndoInsTable()
+{
+ m_pDDEFieldType.reset();
+ m_pColumnWidth.reset();
+ m_pRedlineData.reset();
+ m_pAutoFormat.reset();
+}
+
+void SwUndoInsTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nStartNode );
+
+ SwTableNode* pTableNd = aIdx.GetNode().GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+ pTableNd->DelFrames();
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( *pTableNd, true, RedlineType::Any );
+ RemoveIdxFromSection( rDoc, m_nStartNode );
+
+ // move hard page breaks into next node
+ SwContentNode* pNextNd = rDoc.GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+ const SfxPoolItem *pItem;
+
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+ }
+
+ m_sTableName = pTableNd->GetTable().GetFrameFormat()->GetName();
+ if( auto pDDETable = dynamic_cast<const SwDDETable *>(&pTableNd->GetTable()) )
+ m_pDDEFieldType.reset(static_cast<SwDDEFieldType*>(pDDETable->GetDDEFieldType()->Copy().release()));
+
+ rDoc.GetNodes().Delete( aIdx, pTableNd->EndOfSectionIndex() -
+ aIdx.GetIndex() + 1 );
+
+ SwPaM & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = aIdx;
+ rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), 0 );
+}
+
+void SwUndoInsTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwPosition const aPos(SwNodeIndex(rDoc.GetNodes(), m_nStartNode));
+ const SwTable* pTable = rDoc.InsertTable( m_aInsTableOptions, aPos, m_nRows, m_nColumns,
+ m_nAdjust,
+ m_pAutoFormat.get(), m_pColumnWidth.get() );
+ rDoc.GetEditShell()->MoveTable( GotoPrevTable, fnTableStart );
+ static_cast<SwFrameFormat*>(pTable->GetFrameFormat())->SetName( m_sTableName );
+ SwTableNode* pTableNode = rDoc.GetNodes()[m_nStartNode]->GetTableNode();
+
+ if( m_pDDEFieldType )
+ {
+ SwDDEFieldType* pNewType = static_cast<SwDDEFieldType*>(rDoc.getIDocumentFieldsAccess().InsertFieldType(
+ *m_pDDEFieldType));
+ std::unique_ptr<SwDDETable> pDDETable(new SwDDETable( pTableNode->GetTable(), pNewType ));
+ pTableNode->SetNewTable( std::move(pDDETable) );
+ m_pDDEFieldType.reset();
+ }
+
+ if( (m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) ||
+ ( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ SwPaM aPam( *pTableNode->EndOfSectionNode(), *pTableNode, 1 );
+ SwContentNode* pCNd = aPam.GetContentNode( false );
+ if( pCNd )
+ aPam.GetMark()->nContent.Assign( pCNd, 0 );
+
+ if( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) )
+ {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
+
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, aPam ), true);
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else
+ rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+}
+
+void SwUndoInsTable::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().InsertTable(
+ m_aInsTableOptions, *rContext.GetRepeatPaM().GetPoint(),
+ m_nRows, m_nColumns, m_nAdjust, m_pAutoFormat.get(), m_pColumnWidth.get() );
+}
+
+SwRewriter SwUndoInsTable::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, SwResId(STR_START_QUOTE));
+ aRewriter.AddRule(UndoArg2, m_sTableName);
+ aRewriter.AddRule(UndoArg3, SwResId(STR_END_QUOTE));
+
+ return aRewriter;
+}
+
+SwTableToTextSave::SwTableToTextSave( SwDoc& rDoc, sal_uLong nNd, sal_uLong nEndIdx, sal_Int32 nCnt )
+ : m_nSttNd( nNd ), m_nEndNd( nEndIdx), m_nContent( nCnt )
+{
+ // keep attributes of the joined node
+ SwTextNode* pNd = rDoc.GetNodes()[ nNd ]->GetTextNode();
+ if( pNd )
+ {
+ m_pHstry.reset( new SwHistory );
+
+ m_pHstry->Add( pNd->GetTextColl(), nNd, SwNodeType::Text );
+ if ( pNd->GetpSwpHints() )
+ {
+ m_pHstry->CopyAttr( pNd->GetpSwpHints(), nNd, 0,
+ pNd->GetText().getLength(), false );
+ }
+ if( pNd->HasSwAttrSet() )
+ m_pHstry->CopyFormatAttr( *pNd->GetpSwAttrSet(), nNd );
+
+ if( !m_pHstry->Count() )
+ {
+ m_pHstry.reset();
+ }
+
+ // METADATA: store
+ m_pMetadataUndoStart = pNd->CreateUndo();
+ }
+
+ // we also need to store the metadata reference of the _last_ paragraph
+ // we subtract 1 to account for the removed cell start/end node pair
+ // (after SectionUp, the end of the range points to the node after the cell)
+ if ( nEndIdx - 1 > nNd )
+ {
+ SwTextNode* pLastNode( rDoc.GetNodes()[ nEndIdx - 1 ]->GetTextNode() );
+ if( pLastNode )
+ {
+ // METADATA: store
+ m_pMetadataUndoEnd = pLastNode->CreateUndo();
+ }
+ }
+}
+
+SwUndoTableToText::SwUndoTableToText( const SwTable& rTable, sal_Unicode cCh )
+ : SwUndo( SwUndoId::TABLETOTEXT, rTable.GetFrameFormat()->GetDoc() ),
+ m_sTableName( rTable.GetFrameFormat()->GetName() ),
+ m_nStartNode( 0 ), m_nEndNode( 0 ),
+ m_cSeparator( cCh ), m_nHeadlineRepeat( rTable.GetRowsToRepeat() )
+{
+ m_pTableSave.reset( new SaveTable( rTable ) );
+ m_vBoxSaves.reserve(rTable.GetTabSortBoxes().size());
+
+ if( auto pDDETable = dynamic_cast<const SwDDETable *>(&rTable) )
+ m_pDDEFieldType.reset(static_cast<SwDDEFieldType*>(pDDETable->GetDDEFieldType()->Copy().release()));
+
+ m_bCheckNumFormat = rTable.GetFrameFormat()->GetDoc()->IsInsTableFormatNum();
+
+ m_pHistory.reset(new SwHistory);
+ const SwTableNode* pTableNd = rTable.GetTableNode();
+ sal_uLong nTableStt = pTableNd->GetIndex(), nTableEnd = pTableNd->EndOfSectionIndex();
+
+ const SwFrameFormats& rFrameFormatTable = *pTableNd->GetDoc()->GetSpzFrameFormats();
+ for( size_t n = 0; n < rFrameFormatTable.size(); ++n )
+ {
+ SwFrameFormat* pFormat = rFrameFormatTable[ n ];
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) &&
+ nTableStt <= pAPos->nNode.GetIndex() &&
+ pAPos->nNode.GetIndex() < nTableEnd )
+ {
+ m_pHistory->AddChangeFlyAnchor(*pFormat);
+ }
+ }
+
+ if( !m_pHistory->Count() )
+ {
+ m_pHistory.reset();
+ }
+}
+
+SwUndoTableToText::~SwUndoTableToText()
+{
+ m_pDDEFieldType.reset();
+ m_pTableSave.reset();
+ m_vBoxSaves.clear();
+ m_pHistory.reset();
+}
+
+void SwUndoTableToText::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ SwNodeIndex aFrameIdx( rDoc.GetNodes(), m_nStartNode );
+ SwNodeIndex aEndIdx( rDoc.GetNodes(), m_nEndNode );
+
+ pPam->GetPoint()->nNode = aFrameIdx;
+ pPam->SetMark();
+ pPam->GetPoint()->nNode = aEndIdx;
+ rDoc.DelNumRules( *pPam );
+ pPam->DeleteMark();
+
+ // now collect all Uppers
+ SwNode2LayoutSaveUpperFrames aNode2Layout(aFrameIdx.GetNode());
+
+ // create TableNode structure
+ SwTableNode* pTableNd = rDoc.GetNodes().UndoTableToText( m_nStartNode, m_nEndNode, m_vBoxSaves );
+ pTableNd->GetTable().SetTableModel( m_pTableSave->IsNewModel() );
+ SwTableFormat* pTableFormat = rDoc.MakeTableFrameFormat( m_sTableName, rDoc.GetDfltFrameFormat() );
+ pTableNd->GetTable().RegisterToFormat( *pTableFormat );
+ pTableNd->GetTable().SetRowsToRepeat( m_nHeadlineRepeat );
+
+ // create old table structure
+ m_pTableSave->CreateNew( pTableNd->GetTable() );
+
+ if( m_pDDEFieldType )
+ {
+ SwDDEFieldType* pNewType = static_cast<SwDDEFieldType*>(rDoc.getIDocumentFieldsAccess().InsertFieldType(
+ *m_pDDEFieldType));
+ std::unique_ptr<SwDDETable> pDDETable( new SwDDETable( pTableNd->GetTable(), pNewType ) );
+ pTableNd->SetNewTable( std::move(pDDETable), false );
+ m_pDDEFieldType.reset();
+ }
+
+ if( m_bCheckNumFormat )
+ {
+ SwTableSortBoxes& rBxs = pTableNd->GetTable().GetTabSortBoxes();
+ for (size_t nBoxes = rBxs.size(); nBoxes; )
+ {
+ rDoc.ChkBoxNumFormat( *rBxs[ --nBoxes ], false );
+ }
+ }
+
+ if( m_pHistory )
+ {
+ sal_uInt16 nTmpEnd = m_pHistory->GetTmpEnd();
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( nTmpEnd );
+ }
+
+ aNode2Layout.RestoreUpperFrames( rDoc.GetNodes(),
+ pTableNd->GetIndex(), pTableNd->GetIndex()+1 );
+
+ // Is a table selection requested?
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = *pTableNd->EndOfSectionNode();
+ pPam->SetMark();
+ pPam->GetPoint()->nNode = *pPam->GetNode().StartOfSectionNode();
+ pPam->Move( fnMoveForward, GoInContent );
+ pPam->Exchange();
+ pPam->Move( fnMoveBackward, GoInContent );
+
+ ClearFEShellTabCols(rDoc, nullptr);
+}
+
+// located in untbl.cxx and only an Undo object is allowed to call it
+SwTableNode* SwNodes::UndoTableToText( sal_uLong nSttNd, sal_uLong nEndNd,
+ const SwTableToTextSaves& rSavedData )
+{
+ SwNodeIndex aSttIdx( *this, nSttNd );
+ SwNodeIndex aEndIdx( *this, nEndNd+1 );
+
+ SwTableNode * pTableNd = new SwTableNode( aSttIdx );
+ SwEndNode* pEndNd = new SwEndNode( aEndIdx, *pTableNd );
+
+ aEndIdx = *pEndNd;
+
+ /* Set pTableNd as start of section for all nodes in [nSttNd, nEndNd].
+ Delete all Frames attached to the nodes in that range. */
+ SwNode* pNd;
+ {
+ sal_uLong n, nTmpEnd = aEndIdx.GetIndex();
+ for( n = pTableNd->GetIndex() + 1; n < nTmpEnd; ++n )
+ {
+ if( ( pNd = (*this)[ n ] )->IsContentNode() )
+ static_cast<SwContentNode*>(pNd)->DelFrames(nullptr);
+ pNd->m_pStartOfSection = pTableNd;
+ }
+ }
+
+ // than create table structure partially. First a single line that contains
+ // all boxes. The correct structure is then taken from SaveStruct.
+ SwTableBoxFormat* pBoxFormat = GetDoc()->MakeTableBoxFormat();
+ SwTableLineFormat* pLineFormat = GetDoc()->MakeTableLineFormat();
+ SwTableLine* pLine = new SwTableLine( pLineFormat, rSavedData.size(), nullptr );
+ pTableNd->GetTable().GetTabLines().insert( pTableNd->GetTable().GetTabLines().begin(), pLine );
+
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ for( size_t n = rSavedData.size(); n; )
+ {
+ const SwTableToTextSave *const pSave = rSavedData[ --n ].get();
+ // if the start node was merged with last from prev. cell,
+ // subtract 1 from index to get the merged paragraph, and split that
+ aSttIdx = pSave->m_nSttNd - ( ( SAL_MAX_INT32 != pSave->m_nContent ) ? 1 : 0);
+ SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode();
+
+ if( SAL_MAX_INT32 != pSave->m_nContent )
+ {
+ // split at ContentPosition, delete previous char (= separator)
+ OSL_ENSURE( pTextNd, "Where is my TextNode?" );
+ SwIndex aCntPos( pTextNd, pSave->m_nContent - 1 );
+
+ pTextNd->EraseText( aCntPos, 1 );
+
+ std::function<void (SwTextNode *, sw::mark::RestoreMode)> restoreFunc(
+ [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode)
+ {
+ if (!pContentStore->Empty())
+ {
+ pContentStore->Restore(*pNewNode, pSave->m_nContent, pSave->m_nContent + 1, eMode);
+ }
+ });
+ pTextNd->SplitContentNode(
+ SwPosition(aSttIdx, aCntPos), &restoreFunc);
+ }
+ else
+ {
+ pContentStore->Clear();
+ if( pTextNd )
+ pContentStore->Save( GetDoc(), aSttIdx.GetIndex(), pTextNd->GetText().getLength() );
+ }
+
+ if( pTextNd )
+ {
+ // METADATA: restore
+ pTextNd->GetTextNode()->RestoreMetadata(pSave->m_pMetadataUndoStart);
+ if( pTextNd->HasSwAttrSet() )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( false );
+ }
+
+ if( pSave->m_pHstry )
+ {
+ sal_uInt16 nTmpEnd = pSave->m_pHstry->GetTmpEnd();
+ pSave->m_pHstry->TmpRollback( GetDoc(), 0 );
+ pSave->m_pHstry->SetTmpEnd( nTmpEnd );
+ }
+
+ // METADATA: restore
+ // end points to node after cell
+ if ( pSave->m_nEndNd - 1 > pSave->m_nSttNd )
+ {
+ SwTextNode* pLastNode = (*this)[ pSave->m_nEndNd - 1 ]->GetTextNode();
+ if (pLastNode)
+ {
+ pLastNode->RestoreMetadata(pSave->m_pMetadataUndoEnd);
+ }
+ }
+
+ aEndIdx = pSave->m_nEndNd;
+ SwStartNode* pSttNd = new SwStartNode( aSttIdx, SwNodeType::Start,
+ SwTableBoxStartNode );
+ pSttNd->m_pStartOfSection = pTableNd;
+ new SwEndNode( aEndIdx, *pSttNd );
+
+ for( sal_uLong i = aSttIdx.GetIndex(); i < aEndIdx.GetIndex()-1; ++i )
+ {
+ pNd = (*this)[ i ];
+ pNd->m_pStartOfSection = pSttNd;
+ if( pNd->IsStartNode() )
+ i = pNd->EndOfSectionIndex();
+ }
+
+ SwTableBox* pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
+ pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin(), pBox );
+ }
+ return pTableNd;
+}
+
+void SwUndoTableToText::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pPam->GetPoint()->nNode = m_nStartNode;
+ pPam->GetPoint()->nContent.Assign( nullptr, 0 );
+ SwNodeIndex aSaveIdx( pPam->GetPoint()->nNode, -1 );
+
+ pPam->SetMark(); // log off all indices
+ pPam->DeleteMark();
+
+ SwTableNode* pTableNd = pPam->GetNode().GetTableNode();
+ OSL_ENSURE( pTableNd, "Could not find any TableNode" );
+
+ if( auto pDDETable = dynamic_cast<const SwDDETable *>(&pTableNd->GetTable()) )
+ m_pDDEFieldType.reset(static_cast<SwDDEFieldType*>(pDDETable->GetDDEFieldType()->Copy().release()));
+
+ rDoc.TableToText( pTableNd, m_cSeparator );
+
+ ++aSaveIdx;
+ SwContentNode* pCNd = aSaveIdx.GetNode().GetContentNode();
+ if( !pCNd && nullptr == ( pCNd = rDoc.GetNodes().GoNext( &aSaveIdx ) ) &&
+ nullptr == ( pCNd = SwNodes::GoPrevious( &aSaveIdx )) )
+ {
+ OSL_FAIL( "Where is the TextNode now?" );
+ }
+
+ pPam->GetPoint()->nNode = aSaveIdx;
+ pPam->GetPoint()->nContent.Assign( pCNd, 0 );
+
+ pPam->SetMark(); // log off all indices
+ pPam->DeleteMark();
+}
+
+void SwUndoTableToText::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM *const pPam = & rContext.GetRepeatPaM();
+ SwTableNode *const pTableNd = pPam->GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ // move cursor out of table
+ pPam->GetPoint()->nNode = *pTableNd->EndOfSectionNode();
+ pPam->Move( fnMoveForward, GoInContent );
+ pPam->SetMark();
+ pPam->DeleteMark();
+
+ rContext.GetDoc().TableToText( pTableNd, m_cSeparator );
+ }
+}
+
+void SwUndoTableToText::SetRange( const SwNodeRange& rRg )
+{
+ m_nStartNode = rRg.aStart.GetIndex();
+ m_nEndNode = rRg.aEnd.GetIndex();
+}
+
+void SwUndoTableToText::AddBoxPos( SwDoc& rDoc, sal_uLong nNdIdx, sal_uLong nEndIdx, sal_Int32 nContentIdx )
+{
+ m_vBoxSaves.push_back(std::make_unique<SwTableToTextSave>(rDoc, nNdIdx, nEndIdx, nContentIdx));
+}
+
+SwUndoTextToTable::SwUndoTextToTable( const SwPaM& rRg,
+ const SwInsertTableOptions& rInsTableOpts,
+ sal_Unicode cCh, sal_uInt16 nAdj,
+ const SwTableAutoFormat* pAFormat )
+ : SwUndo( SwUndoId::TEXTTOTABLE, rRg.GetDoc() ), SwUndRng( rRg ), m_aInsertTableOpts( rInsTableOpts ),
+ m_pHistory( nullptr ), m_cSeparator( cCh ), m_nAdjust( nAdj )
+{
+ if( pAFormat )
+ m_pAutoFormat.reset( new SwTableAutoFormat( *pAFormat ) );
+
+ const SwPosition* pEnd = rRg.End();
+ SwNodes& rNds = rRg.GetDoc()->GetNodes();
+ m_bSplitEnd = pEnd->nContent.GetIndex() && ( pEnd->nContent.GetIndex()
+ != pEnd->nNode.GetNode().GetContentNode()->Len() ||
+ pEnd->nNode.GetIndex() >= rNds.GetEndOfContent().GetIndex()-1 );
+}
+
+SwUndoTextToTable::~SwUndoTextToTable()
+{
+ m_pAutoFormat.reset();
+}
+
+void SwUndoTextToTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ sal_uLong nTableNd = m_nSttNode;
+ if( m_nSttContent )
+ ++nTableNd; // Node was split previously
+ SwNodeIndex aIdx( rDoc.GetNodes(), nTableNd );
+ SwTableNode *const pTNd = aIdx.GetNode().GetTableNode();
+ OSL_ENSURE( pTNd, "Could not find a TableNode" );
+
+ RemoveIdxFromSection( rDoc, nTableNd );
+
+ m_sTableName = pTNd->GetTable().GetFrameFormat()->GetName();
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+
+ if( !mvDelBoxes.empty() )
+ {
+ pTNd->DelFrames();
+ SwTable& rTable = pTNd->GetTable();
+ for( size_t n = mvDelBoxes.size(); n; )
+ {
+ SwTableBox* pBox = rTable.GetTableBox( mvDelBoxes[ --n ] );
+ if( pBox )
+ ::DeleteBox_( rTable, pBox, nullptr, false, false );
+ else {
+ OSL_ENSURE( false, "Where is my box?" );
+ }
+ }
+ }
+
+ rDoc.TableToText( pTNd, 0x0b == m_cSeparator ? 0x09 : m_cSeparator );
+
+ // join again at start?
+ SwPaM aPam(rDoc.GetNodes().GetEndOfContent());
+ SwPosition *const pPos = aPam.GetPoint();
+ if( m_nSttContent )
+ {
+ pPos->nNode = nTableNd;
+ pPos->nContent.Assign(pPos->nNode.GetNode().GetContentNode(), 0);
+ if (aPam.Move(fnMoveBackward, GoInContent))
+ {
+ SwNodeIndex & rIdx = aPam.GetPoint()->nNode;
+
+ // than move, relatively, the Cursor/etc. again
+ RemoveIdxRel( rIdx.GetIndex()+1, *pPos );
+
+ rIdx.GetNode().GetContentNode()->JoinNext();
+ }
+ }
+
+ // join again at end?
+ if( m_bSplitEnd )
+ {
+ SwNodeIndex& rIdx = pPos->nNode;
+ rIdx = m_nEndNode;
+ SwTextNode* pTextNd = rIdx.GetNode().GetTextNode();
+ if( pTextNd && pTextNd->CanJoinNext() )
+ {
+ aPam.GetMark()->nContent.Assign( nullptr, 0 );
+ aPam.GetPoint()->nContent.Assign( nullptr, 0 );
+
+ // than move, relatively, the Cursor/etc. again
+ pPos->nContent.Assign(pTextNd, pTextNd->GetText().getLength());
+ RemoveIdxRel( m_nEndNode + 1, *pPos );
+
+ pTextNd->JoinNext();
+ }
+ }
+
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoTextToTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ RemoveIdxFromRange(rPam, false);
+ SetPaM(rPam);
+
+ SwTable const*const pTable = rContext.GetDoc().TextToTable(
+ m_aInsertTableOpts, rPam, m_cSeparator, m_nAdjust, m_pAutoFormat.get() );
+ static_cast<SwFrameFormat*>(pTable->GetFrameFormat())->SetName( m_sTableName );
+}
+
+void SwUndoTextToTable::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // no Table In Table
+ if (!rContext.GetRepeatPaM().GetNode().FindTableNode())
+ {
+ rContext.GetDoc().TextToTable( m_aInsertTableOpts, rContext.GetRepeatPaM(),
+ m_cSeparator, m_nAdjust,
+ m_pAutoFormat.get() );
+ }
+}
+
+void SwUndoTextToTable::AddFillBox( const SwTableBox& rBox )
+{
+ mvDelBoxes.push_back( rBox.GetSttIdx() );
+}
+
+SwHistory& SwUndoTextToTable::GetHistory()
+{
+ if( !m_pHistory )
+ m_pHistory = new SwHistory;
+ return *m_pHistory;
+}
+
+SwUndoTableHeadline::SwUndoTableHeadline( const SwTable& rTable, sal_uInt16 nOldHdl,
+ sal_uInt16 nNewHdl )
+ : SwUndo( SwUndoId::TABLEHEADLINE, rTable.GetFrameFormat()->GetDoc() ),
+ m_nOldHeadline( nOldHdl ),
+ m_nNewHeadline( nNewHdl )
+{
+ OSL_ENSURE( !rTable.GetTabSortBoxes().empty(), "Table without content" );
+ const SwStartNode *pSttNd = rTable.GetTabSortBoxes()[ 0 ]->GetSttNd();
+ OSL_ENSURE( pSttNd, "Box without content" );
+
+ m_nTableNode = pSttNd->StartOfSectionIndex();
+}
+
+void SwUndoTableHeadline::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwTableNode* pTNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode();
+ OSL_ENSURE( pTNd, "could not find any TableNode" );
+
+ rDoc.SetRowsToRepeat( pTNd->GetTable(), m_nOldHeadline );
+}
+
+void SwUndoTableHeadline::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwTableNode* pTNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode();
+ OSL_ENSURE( pTNd, "could not find any TableNode" );
+
+ rDoc.SetRowsToRepeat( pTNd->GetTable(), m_nNewHeadline );
+}
+
+void SwUndoTableHeadline::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwTableNode *const pTableNd =
+ rContext.GetRepeatPaM().GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ rContext.GetDoc().SetRowsToRepeat( pTableNd->GetTable(), m_nNewHeadline );
+ }
+}
+
+SaveTable::SaveTable( const SwTable& rTable, sal_uInt16 nLnCnt, bool bSaveFormula )
+ : m_aTableSet(*rTable.GetFrameFormat()->GetAttrSet().GetPool(), aTableSetRange),
+ m_pSwTable(&rTable), m_nLineCount(nLnCnt), m_bSaveFormula(bSaveFormula)
+{
+ m_bModifyBox = false;
+ m_bNewModel = rTable.IsNewModel();
+ m_aTableSet.Put(rTable.GetFrameFormat()->GetAttrSet());
+ m_pLine.reset( new SaveLine( nullptr, *rTable.GetTabLines()[ 0 ], *this ) );
+
+ SaveLine* pLn = m_pLine.get();
+ if( USHRT_MAX == nLnCnt )
+ nLnCnt = rTable.GetTabLines().size();
+ for( sal_uInt16 n = 1; n < nLnCnt; ++n )
+ pLn = new SaveLine( pLn, *rTable.GetTabLines()[ n ], *this );
+
+ m_aFrameFormats.clear();
+ m_pSwTable = nullptr;
+}
+
+sal_uInt16 SaveTable::AddFormat( SwFrameFormat* pFormat, bool bIsLine )
+{
+ size_t nRet = m_aFrameFormats.GetPos(pFormat);
+ if( SIZE_MAX == nRet )
+ {
+ // Create copy of ItemSet
+ auto pSet = std::make_shared<SfxItemSet>( *pFormat->GetAttrSet().GetPool(),
+ bIsLine ? aTableLineSetRange : aTableBoxSetRange );
+ pSet->Put( pFormat->GetAttrSet() );
+ // When a formula is set, never save the value. It possibly must be
+ // recalculated.
+ // Save formulas always in plain text.
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET == pSet->GetItemState( RES_BOXATR_FORMULA, true, &pItem ))
+ {
+ pSet->ClearItem( RES_BOXATR_VALUE );
+ if (m_pSwTable && m_bSaveFormula)
+ {
+ SwTableFormulaUpdate aMsgHint(m_pSwTable);
+ aMsgHint.m_eFlags = TBL_BOXNAME;
+ SwTableBoxFormula* pFormulaItem = const_cast<SwTableBoxFormula*>(static_cast<const SwTableBoxFormula*>(pItem));
+ pFormulaItem->ChgDefinedIn( pFormat );
+ pFormulaItem->ChangeState( &aMsgHint );
+ pFormulaItem->ChgDefinedIn( nullptr );
+ }
+ }
+ nRet = m_aSets.size();
+ m_aSets.push_back(pSet);
+ m_aFrameFormats.insert(m_aFrameFormats.begin() + nRet, pFormat);
+ }
+ return static_cast<sal_uInt16>(nRet);
+}
+
+void SaveTable::RestoreAttr( SwTable& rTable, bool bMdfyBox )
+{
+ m_bModifyBox = bMdfyBox;
+
+ // first, get back attributes of TableFrameFormat
+ SwFrameFormat* pFormat = rTable.GetFrameFormat();
+ SfxItemSet& rFormatSet = const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pFormat->GetAttrSet()));
+ rFormatSet.ClearItem();
+ rFormatSet.Put(m_aTableSet);
+
+ if( pFormat->IsInCache() )
+ {
+ SwFrame::GetCache().Delete( pFormat );
+ pFormat->SetInCache( false );
+ }
+
+ // for safety, invalidate all TableFrames
+ SwIterator<SwTabFrame,SwFormat> aIter( *pFormat );
+ for( SwTabFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ if( pLast->GetTable() == &rTable )
+ {
+ pLast->InvalidateAll();
+ pLast->SetCompletePaint();
+ }
+
+ // fill FrameFormats with defaults (0)
+ pFormat = nullptr;
+ for (size_t n = m_aSets.size(); n; --n)
+ m_aFrameFormats.push_back(pFormat);
+
+ const size_t nLnCnt = (USHRT_MAX == m_nLineCount)
+ ? rTable.GetTabLines().size()
+ : m_nLineCount;
+
+ SaveLine* pLn = m_pLine.get();
+ for( size_t n = 0; n < nLnCnt; ++n, pLn = pLn->pNext )
+ {
+ if( !pLn )
+ {
+ OSL_ENSURE( false, "Number of lines changed" );
+ break;
+ }
+
+ pLn->RestoreAttr( *rTable.GetTabLines()[ n ], *this );
+ }
+
+ m_aFrameFormats.clear();
+ m_bModifyBox = false;
+}
+
+void SaveTable::SaveContentAttrs( SwDoc* pDoc )
+{
+ m_pLine->SaveContentAttrs(pDoc);
+}
+
+void SaveTable::CreateNew( SwTable& rTable, bool bCreateFrames,
+ bool bRestoreChart )
+{
+ FndBox_ aTmpBox( nullptr, nullptr );
+ aTmpBox.DelFrames( rTable );
+
+ // first, get back attributes of TableFrameFormat
+ SwFrameFormat* pFormat = rTable.GetFrameFormat();
+ SfxItemSet& rFormatSet = const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pFormat->GetAttrSet()));
+ rFormatSet.ClearItem();
+ rFormatSet.Put(m_aTableSet);
+
+ if( pFormat->IsInCache() )
+ {
+ SwFrame::GetCache().Delete( pFormat );
+ pFormat->SetInCache( false );
+ }
+
+ // SwTableBox must have a format - the SwTableBox takes ownership of it
+ SwTableBoxFormat *const pNewFormat(pFormat->GetDoc()->MakeTableBoxFormat());
+ SwTableBox aParent(pNewFormat, rTable.GetTabLines().size(), nullptr);
+
+ // fill FrameFormats with defaults (0)
+ pFormat = nullptr;
+ for( size_t n = m_aSets.size(); n; --n )
+ m_aFrameFormats.push_back(pFormat);
+
+ m_pLine->CreateNew(rTable, aParent, *this);
+ m_aFrameFormats.clear();
+
+ // add new lines, delete old ones
+ const size_t nOldLines = (USHRT_MAX == m_nLineCount)
+ ? rTable.GetTabLines().size()
+ : m_nLineCount;
+
+ SwDoc *pDoc = rTable.GetFrameFormat()->GetDoc();
+ SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ size_t n = 0;
+ for( ; n < aParent.GetTabLines().size(); ++n )
+ {
+ SwTableLine* pLn = aParent.GetTabLines()[ n ];
+ pLn->SetUpper( nullptr );
+ if( n < nOldLines )
+ {
+ SwTableLine* pOld = rTable.GetTabLines()[ n ];
+
+ // TL_CHART2: notify chart about boxes to be removed
+ const SwTableBoxes &rBoxes = pOld->GetTabBoxes();
+ const size_t nBoxes = rBoxes.size();
+ for (size_t k = 0; k < nBoxes; ++k)
+ {
+ SwTableBox *pBox = rBoxes[k];
+ if (pPCD)
+ pPCD->DeleteBox( &rTable, *pBox );
+ }
+
+ rTable.GetTabLines()[n] = pLn;
+ delete pOld;
+ }
+ else
+ rTable.GetTabLines().insert( rTable.GetTabLines().begin() + n, pLn );
+ }
+
+ if( n < nOldLines )
+ {
+ // remove remaining lines...
+ for (size_t k1 = 0; k1 < nOldLines - n; ++k1)
+ {
+ const SwTableBoxes &rBoxes = rTable.GetTabLines()[n + k1]->GetTabBoxes();
+ const size_t nBoxes = rBoxes.size();
+ for (size_t k2 = 0; k2 < nBoxes; ++k2)
+ {
+ SwTableBox *pBox = rBoxes[k2];
+ // TL_CHART2: notify chart about boxes to be removed
+ if (pPCD)
+ pPCD->DeleteBox( &rTable, *pBox );
+ }
+ }
+
+ for( SwTableLines::const_iterator it = rTable.GetTabLines().begin() + n;
+ it != rTable.GetTabLines().begin() + nOldLines; ++it )
+ delete *it;
+ rTable.GetTabLines().erase( rTable.GetTabLines().begin() + n, rTable.GetTabLines().begin() + nOldLines );
+ }
+
+ aParent.GetTabLines().erase( aParent.GetTabLines().begin(), aParent.GetTabLines().begin() + n );
+ assert(aParent.GetTabLines().empty());
+
+ if( bCreateFrames )
+ aTmpBox.MakeFrames( rTable );
+ if( bRestoreChart )
+ {
+ // TL_CHART2: need to inform chart of probably changed cell names
+ pDoc->UpdateCharts( rTable.GetFrameFormat()->GetName() );
+ }
+}
+
+void SaveTable::NewFrameFormat( const SwTableLine* pTableLn, const SwTableBox* pTableBx,
+ sal_uInt16 nFormatPos, SwFrameFormat* pOldFormat )
+{
+ SwDoc* pDoc = pOldFormat->GetDoc();
+
+ SwFrameFormat* pFormat = m_aFrameFormats[ nFormatPos ];
+ if( !pFormat )
+ {
+ if( pTableLn )
+ pFormat = pDoc->MakeTableLineFormat();
+ else
+ pFormat = pDoc->MakeTableBoxFormat();
+ pFormat->SetFormatAttr(*m_aSets[nFormatPos]);
+ m_aFrameFormats[nFormatPos] = pFormat;
+ }
+
+ // first re-assign Frames
+ SwIterator<SwLayoutFrame,SwFormat> aIter( *pOldFormat );
+ for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pTableLn ? static_cast<SwRowFrame*>(pLast)->GetTabLine() == pTableLn
+ : static_cast<SwCellFrame*>(pLast)->GetTabBox() == pTableBx )
+ {
+ pLast->RegisterToFormat(*pFormat);
+ pLast->InvalidateAll();
+ pLast->ReinitializeFrameSizeAttrFlags();
+ if ( !pTableLn )
+ {
+ static_cast<SwCellFrame*>(pLast)->SetDerivedVert( false );
+ static_cast<SwCellFrame*>(pLast)->CheckDirChange();
+ }
+ }
+ }
+
+ // than re-assign myself
+ if ( pTableLn )
+ const_cast<SwTableLine*>(pTableLn)->RegisterToFormat( *pFormat );
+ else if ( pTableBx )
+ const_cast<SwTableBox*>(pTableBx)->RegisterToFormat( *pFormat );
+
+ if (m_bModifyBox && !pTableLn)
+ {
+ const SfxPoolItem& rOld = pOldFormat->GetFormatAttr( RES_BOXATR_FORMAT ),
+ & rNew = pFormat->GetFormatAttr( RES_BOXATR_FORMAT );
+ if( rOld != rNew )
+ pFormat->ModifyNotification( &rOld, &rNew );
+ }
+
+ if( !pOldFormat->HasWriterListeners() )
+ delete pOldFormat;
+}
+
+SaveLine::SaveLine( SaveLine* pPrev, const SwTableLine& rLine, SaveTable& rSTable )
+ : pNext( nullptr )
+{
+ if( pPrev )
+ pPrev->pNext = this;
+
+ nItemSet = rSTable.AddFormat( rLine.GetFrameFormat(), true );
+
+ pBox = new SaveBox( nullptr, *rLine.GetTabBoxes()[ 0 ], rSTable );
+ SaveBox* pBx = pBox;
+ for( size_t n = 1; n < rLine.GetTabBoxes().size(); ++n )
+ pBx = new SaveBox( pBx, *rLine.GetTabBoxes()[ n ], rSTable );
+}
+
+SaveLine::~SaveLine()
+{
+ delete pBox;
+ delete pNext;
+}
+
+void SaveLine::RestoreAttr( SwTableLine& rLine, SaveTable& rSTable )
+{
+ rSTable.NewFrameFormat( &rLine, nullptr, nItemSet, rLine.GetFrameFormat() );
+
+ SaveBox* pBx = pBox;
+ for( size_t n = 0; n < rLine.GetTabBoxes().size(); ++n, pBx = pBx->pNext )
+ {
+ if( !pBx )
+ {
+ OSL_ENSURE( false, "Number of boxes changed" );
+ break;
+ }
+ pBx->RestoreAttr( *rLine.GetTabBoxes()[ n ], rSTable );
+ }
+}
+
+void SaveLine::SaveContentAttrs( SwDoc* pDoc )
+{
+ pBox->SaveContentAttrs( pDoc );
+ if( pNext )
+ pNext->SaveContentAttrs( pDoc );
+}
+
+void SaveLine::CreateNew( SwTable& rTable, SwTableBox& rParent, SaveTable& rSTable )
+{
+ SwTableLineFormat* pFormat = static_cast<SwTableLineFormat*>(rSTable.m_aFrameFormats[ nItemSet ]);
+ if( !pFormat )
+ {
+ SwDoc* pDoc = rTable.GetFrameFormat()->GetDoc();
+ pFormat = pDoc->MakeTableLineFormat();
+ pFormat->SetFormatAttr(*rSTable.m_aSets[nItemSet]);
+ rSTable.m_aFrameFormats[nItemSet] = pFormat;
+ }
+ SwTableLine* pNew = new SwTableLine( pFormat, 1, &rParent );
+
+ rParent.GetTabLines().push_back( pNew );
+
+ pBox->CreateNew( rTable, *pNew, rSTable );
+
+ if( pNext )
+ pNext->CreateNew( rTable, rParent, rSTable );
+}
+
+SaveBox::SaveBox( SaveBox* pPrev, const SwTableBox& rBox, SaveTable& rSTable )
+ : pNext( nullptr ), nSttNode( ULONG_MAX ), nRowSpan(0)
+{
+ Ptrs.pLine = nullptr;
+
+ if( pPrev )
+ pPrev->pNext = this;
+
+ nItemSet = rSTable.AddFormat( rBox.GetFrameFormat(), false );
+
+ if( rBox.GetSttNd() )
+ {
+ nSttNode = rBox.GetSttIdx();
+ nRowSpan = rBox.getRowSpan();
+ }
+ else
+ {
+ Ptrs.pLine = new SaveLine( nullptr, *rBox.GetTabLines()[ 0 ], rSTable );
+
+ SaveLine* pLn = Ptrs.pLine;
+ for( size_t n = 1; n < rBox.GetTabLines().size(); ++n )
+ pLn = new SaveLine( pLn, *rBox.GetTabLines()[ n ], rSTable );
+ }
+}
+
+SaveBox::~SaveBox()
+{
+ if( ULONG_MAX == nSttNode ) // no EndBox
+ delete Ptrs.pLine;
+ else
+ delete Ptrs.pContentAttrs;
+ delete pNext;
+}
+
+void SaveBox::RestoreAttr( SwTableBox& rBox, SaveTable& rSTable )
+{
+ rSTable.NewFrameFormat( nullptr, &rBox, nItemSet, rBox.GetFrameFormat() );
+
+ if( ULONG_MAX == nSttNode ) // no EndBox
+ {
+ if( rBox.GetTabLines().empty() )
+ {
+ OSL_ENSURE( false, "Number of lines changed" );
+ }
+ else
+ {
+ SaveLine* pLn = Ptrs.pLine;
+ for( size_t n = 0; n < rBox.GetTabLines().size(); ++n, pLn = pLn->pNext )
+ {
+ if( !pLn )
+ {
+ OSL_ENSURE( false, "Number of lines changed" );
+ break;
+ }
+
+ pLn->RestoreAttr( *rBox.GetTabLines()[ n ], rSTable );
+ }
+ }
+ }
+ else if( rBox.GetSttNd() && rBox.GetSttIdx() == nSttNode )
+ {
+ if( Ptrs.pContentAttrs )
+ {
+ SwNodes& rNds = rBox.GetFrameFormat()->GetDoc()->GetNodes();
+ sal_uInt16 nSet = 0;
+ sal_uLong nEnd = rBox.GetSttNd()->EndOfSectionIndex();
+ for( sal_uLong n = nSttNode + 1; n < nEnd; ++n )
+ {
+ SwContentNode* pCNd = rNds[ n ]->GetContentNode();
+ if( pCNd )
+ {
+ std::shared_ptr<SfxItemSet> pSet( (*Ptrs.pContentAttrs)[ nSet++ ] );
+ if( pSet )
+ {
+ sal_uInt16 const *pRstAttr = aSave_BoxContentSet;
+ while( *pRstAttr )
+ {
+ pCNd->ResetAttr( *pRstAttr, *(pRstAttr+1) );
+ pRstAttr += 2;
+ }
+ pCNd->SetAttr( *pSet );
+ }
+ else
+ pCNd->ResetAllAttr();
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_ENSURE( false, "Box not anymore at the same node" );
+ }
+}
+
+void SaveBox::SaveContentAttrs( SwDoc* pDoc )
+{
+ if( ULONG_MAX == nSttNode ) // no EndBox
+ {
+ // continue in current line
+ Ptrs.pLine->SaveContentAttrs( pDoc );
+ }
+ else
+ {
+ sal_uLong nEnd = pDoc->GetNodes()[ nSttNode ]->EndOfSectionIndex();
+ Ptrs.pContentAttrs = new SfxItemSets;
+ for( sal_uLong n = nSttNode + 1; n < nEnd; ++n )
+ {
+ SwContentNode* pCNd = pDoc->GetNodes()[ n ]->GetContentNode();
+ if( pCNd )
+ {
+ std::shared_ptr<SfxItemSet> pSet;
+ if( pCNd->HasSwAttrSet() )
+ {
+ pSet = std::make_shared<SfxItemSet>( pDoc->GetAttrPool(),
+ aSave_BoxContentSet );
+ pSet->Put( *pCNd->GetpSwAttrSet() );
+ }
+
+ Ptrs.pContentAttrs->push_back( pSet );
+ }
+ }
+ }
+ if( pNext )
+ pNext->SaveContentAttrs( pDoc );
+}
+
+void SaveBox::CreateNew( SwTable& rTable, SwTableLine& rParent, SaveTable& rSTable )
+{
+ SwTableBoxFormat* pFormat = static_cast<SwTableBoxFormat*>(rSTable.m_aFrameFormats[ nItemSet ]);
+ if( !pFormat )
+ {
+ SwDoc* pDoc = rTable.GetFrameFormat()->GetDoc();
+ pFormat = pDoc->MakeTableBoxFormat();
+ pFormat->SetFormatAttr(*rSTable.m_aSets[nItemSet]);
+ rSTable.m_aFrameFormats[nItemSet] = pFormat;
+ }
+
+ if( ULONG_MAX == nSttNode ) // no EndBox
+ {
+ SwTableBox* pNew = new SwTableBox( pFormat, 1, &rParent );
+ rParent.GetTabBoxes().push_back( pNew );
+
+ Ptrs.pLine->CreateNew( rTable, *pNew, rSTable );
+ }
+ else
+ {
+ // search box for StartNode in old table
+ SwTableBox* pBox = rTable.GetTableBox( nSttNode );
+ if (pBox)
+ {
+ SwFrameFormat* pOld = pBox->GetFrameFormat();
+ pBox->RegisterToFormat( *pFormat );
+ if( !pOld->HasWriterListeners() )
+ delete pOld;
+
+ pBox->setRowSpan( nRowSpan );
+
+ SwTableBoxes* pTBoxes = &pBox->GetUpper()->GetTabBoxes();
+ pTBoxes->erase( std::find( pTBoxes->begin(), pTBoxes->end(), pBox ) );
+
+ pBox->SetUpper( &rParent );
+ pTBoxes = &rParent.GetTabBoxes();
+ pTBoxes->push_back( pBox );
+ }
+ }
+
+ if( pNext )
+ pNext->CreateNew( rTable, rParent, rSTable );
+}
+
+// UndoObject for attribute changes on table
+SwUndoAttrTable::SwUndoAttrTable( const SwTableNode& rTableNd, bool bClearTabCols )
+ : SwUndo( SwUndoId::TABLE_ATTR, rTableNd.GetDoc() ),
+ m_nStartNode( rTableNd.GetIndex() )
+{
+ m_bClearTableCol = bClearTabCols;
+ m_pSaveTable.reset( new SaveTable( rTableNd.GetTable() ) );
+}
+
+SwUndoAttrTable::~SwUndoAttrTable()
+{
+}
+
+void SwUndoAttrTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwTableNode* pTableNd = rDoc.GetNodes()[ m_nStartNode ]->GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+
+ if (pTableNd)
+ {
+ SaveTable* pOrig = new SaveTable( pTableNd->GetTable() );
+ m_pSaveTable->RestoreAttr( pTableNd->GetTable() );
+ m_pSaveTable.reset( pOrig );
+ }
+
+ if( m_bClearTableCol )
+ {
+ ClearFEShellTabCols(rDoc, nullptr);
+ }
+}
+
+void SwUndoAttrTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ UndoImpl(rContext);
+}
+
+// UndoObject for AutoFormat on Table
+SwUndoTableAutoFormat::SwUndoTableAutoFormat( const SwTableNode& rTableNd,
+ const SwTableAutoFormat& rAFormat )
+ : SwUndo( SwUndoId::TABLE_AUTOFMT, rTableNd.GetDoc() )
+ , m_TableStyleName(rTableNd.GetTable().GetTableStyleName())
+ , m_nStartNode( rTableNd.GetIndex() )
+ , m_bSaveContentAttr( false )
+ , m_nRepeatHeading(rTableNd.GetTable().GetRowsToRepeat())
+{
+ m_pSaveTable.reset( new SaveTable( rTableNd.GetTable() ) );
+
+ if( rAFormat.IsFont() || rAFormat.IsJustify() )
+ {
+ // then also go over the ContentNodes of the EndBoxes and collect
+ // all paragraph attributes
+ m_pSaveTable->SaveContentAttrs( const_cast<SwDoc*>(rTableNd.GetDoc()) );
+ m_bSaveContentAttr = true;
+ }
+}
+
+SwUndoTableAutoFormat::~SwUndoTableAutoFormat()
+{
+}
+
+void SwUndoTableAutoFormat::SaveBoxContent( const SwTableBox& rBox )
+{
+ m_Undos.push_back(std::make_shared<SwUndoTableNumFormat>(rBox));
+}
+
+void
+SwUndoTableAutoFormat::UndoRedo(bool const bUndo, ::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwTableNode* pTableNd = rDoc.GetNodes()[ m_nStartNode ]->GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+
+ SwTable& table = pTableNd->GetTable();
+ if (table.GetTableStyleName() != m_TableStyleName)
+ {
+ OUString const temp(table.GetTableStyleName());
+ table.SetTableStyleName(m_TableStyleName);
+ m_TableStyleName = temp;
+ }
+ SaveTable* pOrig = new SaveTable( table );
+ // then go also over the ContentNodes of the EndBoxes and collect
+ // all paragraph attributes
+ if( m_bSaveContentAttr )
+ pOrig->SaveContentAttrs( &rDoc );
+
+ if (bUndo)
+ {
+ for (size_t n = m_Undos.size(); 0 < n; --n)
+ {
+ m_Undos.at(n-1)->UndoImpl(rContext);
+ }
+
+ table.SetRowsToRepeat(m_nRepeatHeading);
+ }
+
+ m_pSaveTable->RestoreAttr( pTableNd->GetTable(), !bUndo );
+ m_pSaveTable.reset( pOrig );
+}
+
+void SwUndoTableAutoFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ UndoRedo(true, rContext);
+}
+
+void SwUndoTableAutoFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ UndoRedo(false, rContext);
+}
+
+SwUndoTableNdsChg::SwUndoTableNdsChg( SwUndoId nAction,
+ const SwSelBoxes& rBoxes,
+ const SwTableNode& rTableNd,
+ long nMn, long nMx,
+ sal_uInt16 nCnt, bool bFlg, bool bSmHght )
+ : SwUndo( nAction, rTableNd.GetDoc() ),
+ m_nMin( nMn ), m_nMax( nMx ),
+ m_nSttNode( rTableNd.GetIndex() ),
+ m_nCount( nCnt ),
+ m_bFlag( bFlg ),
+ m_bSameHeight( bSmHght )
+{
+ const SwTable& rTable = rTableNd.GetTable();
+ m_pSaveTable.reset( new SaveTable( rTable ) );
+
+ // and remember selection
+ ReNewBoxes( rBoxes );
+}
+
+void SwUndoTableNdsChg::ReNewBoxes( const SwSelBoxes& rBoxes )
+{
+ if (rBoxes.size() != m_Boxes.size())
+ {
+ m_Boxes.clear();
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ m_Boxes.insert( rBoxes[n]->GetSttIdx() );
+ }
+ }
+}
+
+SwUndoTableNdsChg::~SwUndoTableNdsChg()
+{
+}
+
+void SwUndoTableNdsChg::SaveNewBoxes( const SwTableNode& rTableNd,
+ const SwTableSortBoxes& rOld )
+{
+ const SwTable& rTable = rTableNd.GetTable();
+ const SwTableSortBoxes& rTableBoxes = rTable.GetTabSortBoxes();
+
+ OSL_ENSURE( ! IsDelBox(), "wrong Action" );
+ m_pNewSttNds.reset( new std::set<BoxMove> );
+
+ size_t i = 0;
+ for (size_t n = 0; n < rOld.size(); ++i)
+ {
+ if( rOld[ n ] == rTableBoxes[ i ] )
+ ++n;
+ else
+ // new box: insert sorted
+ m_pNewSttNds->insert( BoxMove(rTableBoxes[ i ]->GetSttIdx()) );
+ }
+
+ for( ; i < rTableBoxes.size(); ++i )
+ // new box: insert sorted
+ m_pNewSttNds->insert( BoxMove(rTableBoxes[ i ]->GetSttIdx()) );
+}
+
+static SwTableLine* lcl_FindTableLine( const SwTable& rTable,
+ const SwTableBox& rBox )
+{
+ SwTableLine* pRet = nullptr;
+ // i63949: For nested cells we have to take nLineNo - 1, too, not 0!
+ const SwTableLines &rTableLines = ( rBox.GetUpper()->GetUpper() != nullptr ) ?
+ rBox.GetUpper()->GetUpper()->GetTabLines()
+ : rTable.GetTabLines();
+ const SwTableLine* pLine = rBox.GetUpper();
+ sal_uInt16 nLineNo = rTableLines.GetPos( pLine );
+ pRet = rTableLines[nLineNo - 1];
+
+ return pRet;
+}
+
+static const SwTableLines& lcl_FindParentLines( const SwTable& rTable,
+ const SwTableBox& rBox )
+{
+ const SwTableLines& rRet =
+ ( rBox.GetUpper()->GetUpper() != nullptr ) ?
+ rBox.GetUpper()->GetUpper()->GetTabLines() :
+ rTable.GetTabLines();
+
+ return rRet;
+}
+
+void SwUndoTableNdsChg::SaveNewBoxes( const SwTableNode& rTableNd,
+ const SwTableSortBoxes& rOld,
+ const SwSelBoxes& rBoxes,
+ const std::vector<sal_uLong> &rNodeCnts )
+{
+ const SwTable& rTable = rTableNd.GetTable();
+ const SwTableSortBoxes& rTableBoxes = rTable.GetTabSortBoxes();
+
+ OSL_ENSURE( ! IsDelBox(), "wrong Action" );
+ m_pNewSttNds.reset( new std::set<BoxMove> );
+
+ OSL_ENSURE( rTable.IsNewModel() || rOld.size() + m_nCount * rBoxes.size() == rTableBoxes.size(),
+ "unexpected boxes" );
+ OSL_ENSURE( rOld.size() <= rTableBoxes.size(), "more unexpected boxes" );
+ for (size_t n = 0, i = 0; i < rTableBoxes.size(); ++i)
+ {
+ if( ( n < rOld.size() ) &&
+ ( rOld[ n ] == rTableBoxes[ i ] ) )
+ {
+ // box already known? Then nothing to be done.
+ ++n;
+ }
+ else
+ {
+ // new box found: insert (obey sort order)
+ const SwTableBox* pBox = rTableBoxes[ i ];
+
+ // find the source box. It must be one in rBoxes.
+ // We found the right one if it's in the same column as pBox.
+ // No, if more than one selected cell in the same column has been split,
+ // we have to look for the nearest one (i65201)!
+ const SwTableBox* pSourceBox = nullptr;
+ const SwTableBox* pCheckBox = nullptr;
+ const SwTableLine* pBoxLine = pBox->GetUpper();
+ sal_uInt16 nLineDiff = lcl_FindParentLines(rTable,*pBox).GetPos(pBoxLine);
+ sal_uInt16 nLineNo = 0;
+ for (size_t j = 0; j < rBoxes.size(); ++j)
+ {
+ pCheckBox = rBoxes[j];
+ if( pCheckBox->GetUpper()->GetUpper() == pBox->GetUpper()->GetUpper() )
+ {
+ const SwTableLine* pCheckLine = pCheckBox->GetUpper();
+ sal_uInt16 nCheckLine = lcl_FindParentLines( rTable, *pCheckBox ).
+ GetPos( pCheckLine );
+ if( ( !pSourceBox || nCheckLine > nLineNo ) && nCheckLine < nLineDiff )
+ {
+ nLineNo = nCheckLine;
+ pSourceBox = pCheckBox;
+ }
+ }
+ }
+
+ // find the line number difference
+ // (to help determine bNodesMoved flag below)
+ nLineDiff = nLineDiff - nLineNo;
+ OSL_ENSURE( pSourceBox, "Split source box not found!" );
+ // find out how many nodes the source box used to have
+ // (to help determine bNodesMoved flag below)
+ size_t nNdsPos = 0;
+ while( rBoxes[ nNdsPos ] != pSourceBox )
+ ++nNdsPos;
+ sal_uLong nNodes = rNodeCnts[ nNdsPos ];
+
+ // When a new table cell is created, it either gets a new
+ // node, or it gets node(s) from elsewhere. The undo must
+ // know, of course, and thus we must determine here just
+ // where pBox's nodes are from:
+ // If 1) the source box has lost nodes, and
+ // 2) we're in the node range that got nodes
+ // then pBox received nodes from elsewhere.
+ // If bNodesMoved is set for pBox the undo must move the
+ // boxes back, otherwise it must delete them.
+ bool bNodesMoved = pSourceBox &&
+ ( nNodes != ( pSourceBox->GetSttNd()->EndOfSectionIndex() -
+ pSourceBox->GetSttIdx() ) )
+ && ( nNodes - 1 > nLineDiff );
+ m_pNewSttNds->insert( BoxMove(pBox->GetSttIdx(), bNodesMoved) );
+ }
+ }
+}
+
+void SwUndoTableNdsChg::SaveSection( SwStartNode* pSttNd )
+{
+ OSL_ENSURE( IsDelBox(), "wrong Action" );
+ if (m_pDelSects == nullptr)
+ m_pDelSects.reset(new SwUndoSaveSections);
+
+ SwTableNode* pTableNd = pSttNd->FindTableNode();
+ std::unique_ptr<SwUndoSaveSection, o3tl::default_delete<SwUndoSaveSection>> pSave(new SwUndoSaveSection);
+ pSave->SaveSection( SwNodeIndex( *pSttNd ));
+
+ m_pDelSects->push_back(std::move(pSave));
+ m_nSttNode = pTableNd->GetIndex();
+}
+
+void SwUndoTableNdsChg::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode );
+
+ SwTableNode *const pTableNd = aIdx.GetNode().GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ CHECK_TABLE( pTableNd->GetTable() )
+
+ FndBox_ aTmpBox( nullptr, nullptr );
+ // ? TL_CHART2: notification or locking of controller required ?
+
+ SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ SwSelBoxes aDelBoxes;
+ std::vector< std::pair<SwTableBox *, sal_uLong> > aDelNodes;
+ if( IsDelBox() )
+ {
+ // Trick: add missing boxes in any line, they will be connected
+ // correctly when calling CreateNew
+ SwTableBox* pCpyBox = pTableNd->GetTable().GetTabSortBoxes()[0];
+ SwTableBoxes& rLnBoxes = pCpyBox->GetUpper()->GetTabBoxes();
+
+ // restore sections
+ for (size_t n = m_pDelSects->size(); n; )
+ {
+ SwUndoSaveSection *const pSave = (*m_pDelSects)[ --n ].get();
+ pSave->RestoreSection( &rDoc, &aIdx, SwTableBoxStartNode );
+ if( pSave->GetHistory() )
+ pSave->GetHistory()->Rollback( &rDoc );
+ SwTableBox* pBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pCpyBox->GetFrameFormat()), aIdx,
+ pCpyBox->GetUpper() );
+ rLnBoxes.push_back( pBox );
+ }
+ m_pDelSects->clear();
+ }
+ else if( !m_pNewSttNds->empty() )
+ {
+ // Then the nodes have be moved and not deleted!
+ // But for that we need a temp array.
+ std::vector<BoxMove> aTmp( m_pNewSttNds->begin(), m_pNewSttNds->end() );
+
+ // backwards
+ for (size_t n = aTmp.size(); n > 0 ; )
+ {
+ --n;
+ // delete box from table structure
+ sal_uLong nIdx = aTmp[n].index;
+ SwTableBox* pBox = pTableNd->GetTable().GetTableBox( nIdx );
+ OSL_ENSURE( pBox, "Where is my TableBox?" );
+
+ // TL_CHART2: notify chart about box to be removed
+ if (pPCD)
+ pPCD->DeleteBox( &pTableNd->GetTable(), *pBox );
+
+ // insert _before_ deleting the section - otherwise the box
+ // has no start node so all boxes sort equal in SwSelBoxes
+ aDelBoxes.insert(pBox);
+
+ if( aTmp[n].hasMoved )
+ {
+ SwNodeRange aRg( *pBox->GetSttNd(), 1,
+ *pBox->GetSttNd()->EndOfSectionNode() );
+
+ SwTableLine* pLine = lcl_FindTableLine( pTableNd->GetTable(), *pBox );
+ SwNodeIndex aInsPos( *(pLine->GetTabBoxes()[0]->GetSttNd()), 2 );
+
+ // adjust all StartNode indices
+ size_t i = n;
+ sal_uLong nSttIdx = aInsPos.GetIndex() - 2,
+ nNdCnt = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
+ while( i && aTmp[ --i ].index > nSttIdx )
+ aTmp[ i ].index += nNdCnt;
+
+ // first delete box
+ delete pBox;
+ // than move nodes
+ rDoc.GetNodes().MoveNodes( aRg, rDoc.GetNodes(), aInsPos, false );
+ }
+ else
+ {
+ aDelNodes.emplace_back(pBox, nIdx);
+ }
+ }
+ }
+ else
+ {
+ // Remove nodes from nodes array (backwards!)
+ std::set<BoxMove>::reverse_iterator it;
+ for( it = m_pNewSttNds->rbegin(); it != m_pNewSttNds->rend(); ++it )
+ {
+ sal_uLong nIdx = (*it).index;
+ SwTableBox* pBox = pTableNd->GetTable().GetTableBox( nIdx );
+ OSL_ENSURE( pBox, "Where's my table box?" );
+ // TL_CHART2: notify chart about box to be removed
+ if (pPCD)
+ pPCD->DeleteBox( &pTableNd->GetTable(), *pBox );
+ aDelBoxes.insert(pBox);
+ aDelNodes.emplace_back(pBox, nIdx);
+ }
+ }
+
+ // fdo#57197: before deleting the SwTableBoxes, delete the SwTabFrames
+ aTmpBox.SetTableLines(aDelBoxes, pTableNd->GetTable());
+ aTmpBox.DelFrames(pTableNd->GetTable());
+
+ // do this _after_ deleting Frames because disposing SwAccessible requires
+ // connection to the nodes, see SwAccessibleChild::IsAccessible()
+ for (const std::pair<SwTableBox *, sal_uLong> & rDelNode : aDelNodes)
+ {
+ // first disconnect box from node, otherwise ~SwTableBox would
+ // access pBox->pSttNd, deleted by DeleteSection
+ rDelNode.first->RemoveFromTable();
+ rDoc.getIDocumentContentOperations().DeleteSection(rDoc.GetNodes()[ rDelNode.second ]);
+ }
+
+ // Remove boxes from table structure
+ for( size_t n = 0; n < aDelBoxes.size(); ++n )
+ {
+ SwTableBox* pCurrBox = aDelBoxes[n];
+ SwTableBoxes* pTBoxes = &pCurrBox->GetUpper()->GetTabBoxes();
+ pTBoxes->erase( std::find( pTBoxes->begin(), pTBoxes->end(), pCurrBox ) );
+ delete pCurrBox;
+ }
+
+ m_pSaveTable->CreateNew( pTableNd->GetTable(), true, false );
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ rDoc.UpdateCharts( pTableNd->GetTable().GetFrameFormat()->GetName() );
+
+ if( IsDelBox() )
+ m_nSttNode = pTableNd->GetIndex();
+ ClearFEShellTabCols(rDoc, nullptr);
+ CHECK_TABLE( pTableNd->GetTable() )
+}
+
+void SwUndoTableNdsChg::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwTableNode* pTableNd = rDoc.GetNodes()[ m_nSttNode ]->GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+ CHECK_TABLE( pTableNd->GetTable() )
+
+ SwSelBoxes aSelBoxes;
+ for (const auto& rBox : m_Boxes)
+ {
+ SwTableBox* pBox = pTableNd->GetTable().GetTableBox( rBox );
+ aSelBoxes.insert( pBox );
+ }
+
+ // create SelBoxes and call InsertCell/-Row/SplitTable
+ switch( GetId() )
+ {
+ case SwUndoId::TABLE_INSCOL:
+ rDoc.InsertCol( aSelBoxes, m_nCount, m_bFlag );
+ break;
+
+ case SwUndoId::TABLE_INSROW:
+ rDoc.InsertRow( aSelBoxes, m_nCount, m_bFlag );
+ break;
+
+ case SwUndoId::TABLE_SPLIT:
+ rDoc.SplitTable( aSelBoxes, m_bFlag, m_nCount, m_bSameHeight );
+ break;
+ case SwUndoId::TABLE_DELBOX:
+ case SwUndoId::ROW_DELETE:
+ case SwUndoId::COL_DELETE:
+ {
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+ SwTable &rTable = pTableNd->GetTable();
+ if( m_nMax > m_nMin && rTable.IsNewModel() )
+ rTable.PrepareDeleteCol( m_nMin, m_nMax );
+ rTable.DeleteSel( &rDoc, aSelBoxes, nullptr, this, true, true );
+ m_nSttNode = pTableNd->GetIndex();
+ }
+ break;
+ default:
+ ;
+ }
+ ClearFEShellTabCols(rDoc, nullptr);
+ CHECK_TABLE( pTableNd->GetTable() )
+}
+
+SwUndoTableMerge::SwUndoTableMerge( const SwPaM& rTableSel )
+ : SwUndo( SwUndoId::TABLE_MERGE, rTableSel.GetDoc() ), SwUndRng( rTableSel )
+{
+ const SwTableNode* pTableNd = rTableSel.GetNode().FindTableNode();
+ OSL_ENSURE( pTableNd, "Where is the TableNode?" );
+ m_pSaveTable.reset( new SaveTable( pTableNd->GetTable() ) );
+ m_nTableNode = pTableNd->GetIndex();
+}
+
+SwUndoTableMerge::~SwUndoTableMerge()
+{
+ m_pSaveTable.reset();
+ m_vMoves.clear();
+ m_pHistory.reset();
+}
+
+void SwUndoTableMerge::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nTableNode );
+
+ SwTableNode *const pTableNd = aIdx.GetNode().GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ // ? TL_CHART2: notification or locking of controller required ?
+
+ // 1. restore deleted boxes:
+ // Trick: add missing boxes in any line, they will be connected
+ // correctly when calling CreateNew
+ SwTableBox *pBox, *pCpyBox = pTableNd->GetTable().GetTabSortBoxes()[0];
+ SwTableBoxes& rLnBoxes = pCpyBox->GetUpper()->GetTabBoxes();
+
+ CHECKTABLE(pTableNd->GetTable())
+
+ SwSelBoxes aSelBoxes;
+ SwTextFormatColl* pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD );
+
+ for (const auto& rBox : m_Boxes)
+ {
+ aIdx = rBox;
+ SwStartNode* pSttNd = rDoc.GetNodes().MakeTextSection( aIdx,
+ SwTableBoxStartNode, pColl );
+ pBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pCpyBox->GetFrameFormat()), *pSttNd,
+ pCpyBox->GetUpper() );
+ rLnBoxes.push_back( pBox );
+
+ aSelBoxes.insert( pBox );
+ }
+
+ CHECKTABLE(pTableNd->GetTable())
+
+ SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ // 2. deleted the inserted boxes
+ // delete nodes (from last to first)
+ for( size_t n = m_aNewStartNodes.size(); n; )
+ {
+ // remove box from table structure
+ sal_uLong nIdx = m_aNewStartNodes[ --n ];
+
+ if( !nIdx && n )
+ {
+ nIdx = m_aNewStartNodes[ --n ];
+ pBox = pTableNd->GetTable().GetTableBox( nIdx );
+ OSL_ENSURE( pBox, "Where is my TableBox?" );
+
+ if( !m_pSaveTable->IsNewModel() )
+ rDoc.GetNodes().MakeTextNode( SwNodeIndex(
+ *pBox->GetSttNd()->EndOfSectionNode() ), pColl );
+
+ // this was the separator -> restore moved ones
+ for (size_t i = m_vMoves.size(); i; )
+ {
+ SwTextNode* pTextNd = nullptr;
+ sal_Int32 nDelPos = 0;
+ SwUndoMove *const pUndo = m_vMoves[ --i ].get();
+ if( !pUndo->IsMoveRange() )
+ {
+ pTextNd = rDoc.GetNodes()[ pUndo->GetDestSttNode() ]->GetTextNode();
+ nDelPos = pUndo->GetDestSttContent() - 1;
+ }
+ pUndo->UndoImpl(rContext);
+ if( pUndo->IsMoveRange() )
+ {
+ // delete the unnecessary node
+ aIdx = pUndo->GetEndNode();
+ SwContentNode *pCNd = aIdx.GetNode().GetContentNode();
+ if( pCNd )
+ {
+ SwNodeIndex aTmp( aIdx, -1 );
+ SwContentNode *pMove = aTmp.GetNode().GetContentNode();
+ if( pMove )
+ pCNd->MoveTo( *pMove );
+ }
+ rDoc.GetNodes().Delete( aIdx );
+ }
+ else if( pTextNd )
+ {
+ // also delete not needed attributes
+ SwIndex aTmpIdx( pTextNd, nDelPos );
+ if( pTextNd->GetpSwpHints() && pTextNd->GetpSwpHints()->Count() )
+ pTextNd->RstTextAttr( aTmpIdx, pTextNd->GetText().getLength() - nDelPos + 1 );
+ // delete separator
+ pTextNd->EraseText( aTmpIdx, 1 );
+ }
+ }
+ nIdx = pBox->GetSttIdx();
+ }
+ else
+ pBox = pTableNd->GetTable().GetTableBox( nIdx );
+
+ if( !m_pSaveTable->IsNewModel() )
+ {
+ // TL_CHART2: notify chart about box to be removed
+ if (pPCD)
+ pPCD->DeleteBox( &pTableNd->GetTable(), *pBox );
+
+ SwTableBoxes* pTBoxes = &pBox->GetUpper()->GetTabBoxes();
+ pTBoxes->erase( std::find(pTBoxes->begin(), pTBoxes->end(), pBox ) );
+
+ // delete indices from section
+ {
+ SwNodeIndex aTmpIdx( *pBox->GetSttNd() );
+ SwDoc::CorrAbs( SwNodeIndex( aTmpIdx, 1 ),
+ SwNodeIndex( *aTmpIdx.GetNode().EndOfSectionNode() ),
+ SwPosition( aTmpIdx, SwIndex( nullptr, 0 )), true );
+ }
+
+ delete pBox;
+ rDoc.getIDocumentContentOperations().DeleteSection( rDoc.GetNodes()[ nIdx ] );
+ }
+ }
+ CHECKTABLE(pTableNd->GetTable())
+
+ m_pSaveTable->CreateNew( pTableNd->GetTable(), true, false );
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ rDoc.UpdateCharts( pTableNd->GetTable().GetFrameFormat()->GetName() );
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = m_nSttNode;
+ pPam->GetPoint()->nContent.Assign( pPam->GetContentNode(), m_nSttContent );
+ pPam->SetMark();
+ pPam->DeleteMark();
+
+ CHECKTABLE(pTableNd->GetTable())
+ ClearFEShellTabCols(rDoc, nullptr);
+}
+
+void SwUndoTableMerge::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rDoc.MergeTable(rPam);
+}
+
+void SwUndoTableMerge::MoveBoxContent( SwDoc* pDoc, SwNodeRange& rRg, SwNodeIndex& rPos )
+{
+ SwNodeIndex aTmp( rRg.aStart, -1 ), aTmp2( rPos, -1 );
+ std::unique_ptr<SwUndoMove> pUndo(new SwUndoMove( pDoc, rRg, rPos ));
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+ pDoc->getIDocumentContentOperations().MoveNodeRange( rRg, rPos, m_pSaveTable->IsNewModel() ?
+ SwMoveFlags::NO_DELFRMS :
+ SwMoveFlags::DEFAULT );
+ ++aTmp;
+ ++aTmp2;
+ pUndo->SetDestRange( aTmp2, rPos, aTmp );
+
+ m_vMoves.push_back(std::move(pUndo));
+}
+
+void SwUndoTableMerge::SetSelBoxes( const SwSelBoxes& rBoxes )
+{
+ // memorize selection
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ m_Boxes.insert(rBoxes[n]->GetSttIdx());
+ }
+
+ // as separator for inserts of new boxes after shifting
+ m_aNewStartNodes.push_back( sal_uLong(0) );
+
+ // The new table model does not delete overlapped cells (by row span),
+ // so the rBoxes array might be empty even some cells have been merged.
+ if( !rBoxes.empty() )
+ m_nTableNode = rBoxes[ 0 ]->GetSttNd()->FindTableNode()->GetIndex();
+}
+
+void SwUndoTableMerge::SaveCollection( const SwTableBox& rBox )
+{
+ if( !m_pHistory )
+ m_pHistory.reset(new SwHistory);
+
+ SwNodeIndex aIdx( *rBox.GetSttNd(), 1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = aIdx.GetNodes().GoNext( &aIdx );
+
+ m_pHistory->Add( pCNd->GetFormatColl(), aIdx.GetIndex(), pCNd->GetNodeType());
+ if( pCNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pCNd->GetpSwAttrSet(), aIdx.GetIndex() );
+}
+
+SwUndoTableNumFormat::SwUndoTableNumFormat( const SwTableBox& rBox,
+ const SfxItemSet* pNewSet )
+ : SwUndo(SwUndoId::TBLNUMFMT, rBox.GetFrameFormat()->GetDoc())
+ , m_nFormatIdx(getSwDefaultTextFormat())
+ , m_nNewFormatIdx(0)
+ , m_fNum(0.0)
+ , m_fNewNum(0.0)
+ , m_bNewFormat(false)
+ , m_bNewFormula(false)
+ , m_bNewValue(false)
+{
+ m_nNode = rBox.GetSttIdx();
+
+ m_nNodePos = rBox.IsValidNumTextNd( nullptr == pNewSet );
+ SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
+
+ if( ULONG_MAX != m_nNodePos )
+ {
+ SwTextNode* pTNd = pDoc->GetNodes()[ m_nNodePos ]->GetTextNode();
+
+ m_pHistory.reset(new SwHistory);
+ SwRegHistory aRHst( *rBox.GetSttNd(), m_pHistory.get() );
+ // always save all text atttibutes because of possibly overlapping
+ // areas of on/off
+ m_pHistory->CopyAttr( pTNd->GetpSwpHints(), m_nNodePos, 0,
+ pTNd->GetText().getLength(), true );
+
+ if( pTNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pTNd->GetpSwAttrSet(), m_nNodePos );
+
+ m_aStr = pTNd->GetText();
+ if( pTNd->GetpSwpHints() )
+ pTNd->GetpSwpHints()->DeRegister();
+ }
+
+ m_pBoxSet.reset( new SfxItemSet( pDoc->GetAttrPool(), aTableBoxSetRange ) );
+ m_pBoxSet->Put( rBox.GetFrameFormat()->GetAttrSet() );
+
+ if( pNewSet )
+ {
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET == pNewSet->GetItemState( RES_BOXATR_FORMAT,
+ false, &pItem ))
+ {
+ m_bNewFormat = true;
+ m_nNewFormatIdx = static_cast<const SwTableBoxNumFormat*>(pItem)->GetValue();
+ }
+ if( SfxItemState::SET == pNewSet->GetItemState( RES_BOXATR_FORMULA,
+ false, &pItem ))
+ {
+ m_bNewFormula = true;
+ m_aNewFormula = static_cast<const SwTableBoxFormula*>(pItem)->GetFormula();
+ }
+ if( SfxItemState::SET == pNewSet->GetItemState( RES_BOXATR_VALUE,
+ false, &pItem ))
+ {
+ m_bNewValue = true;
+ m_fNewNum = static_cast<const SwTableBoxValue*>(pItem)->GetValue();
+ }
+ }
+
+ // is a history needed at all?
+ if (m_pHistory && !m_pHistory->Count())
+ {
+ m_pHistory.reset();
+ }
+}
+
+SwUndoTableNumFormat::~SwUndoTableNumFormat()
+{
+ m_pHistory.reset();
+ m_pBoxSet.reset();
+}
+
+void SwUndoTableNumFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ OSL_ENSURE( m_pBoxSet, "Where's the stored item set?" );
+
+ SwDoc & rDoc = rContext.GetDoc();
+ SwStartNode* pSttNd = rDoc.GetNodes()[ m_nNode ]->
+ FindSttNodeByType( SwTableBoxStartNode );
+ OSL_ENSURE( pSttNd, "without StartNode no TableBox" );
+ SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().GetTableBox(
+ pSttNd->GetIndex() );
+ OSL_ENSURE( pBox, "found no TableBox" );
+
+ SwTableBoxFormat* pFormat = rDoc.MakeTableBoxFormat();
+ pFormat->SetFormatAttr( *m_pBoxSet );
+ pBox->ChgFrameFormat( pFormat );
+
+ if( ULONG_MAX == m_nNodePos )
+ return;
+
+ SwTextNode* pTextNd = rDoc.GetNodes()[ m_nNodePos ]->GetTextNode();
+ // If more than one node was deleted then all "node" attributes were also
+ // saved
+ if( pTextNd->HasSwAttrSet() )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd->GetpSwpHints() && !m_aStr.isEmpty() )
+ pTextNd->ClearSwpHintsArr( true );
+
+ // ChgTextToNum(..) only acts when the strings are different. We need to do
+ // the same here.
+ if( pTextNd->GetText() != m_aStr )
+ {
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( *( pBox->GetSttNd() ), false, RedlineType::Any );
+
+ SwIndex aIdx( pTextNd, 0 );
+ if( !m_aStr.isEmpty() )
+ {
+ pTextNd->EraseText( aIdx );
+ pTextNd->InsertText( m_aStr, aIdx,
+ SwInsertFlags::NOHINTEXPAND );
+ }
+ }
+
+ if( m_pHistory )
+ {
+ sal_uInt16 nTmpEnd = m_pHistory->GetTmpEnd();
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( nTmpEnd );
+ }
+
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = m_nNode + 1;
+ pPam->GetPoint()->nContent.Assign( pTextNd, 0 );
+}
+
+namespace {
+
+/** switch the RedlineFlags on the given document, using
+ * SetRedlineFlags_intern. This class set the mode in the constructor,
+ * and changes it back in the destructor, i.e. it uses the
+ * initialization-is-resource-acquisition idiom.
+ */
+class RedlineFlagsInternGuard
+{
+ SwDoc& mrDoc;
+ RedlineFlags meOldRedlineFlags;
+
+public:
+ RedlineFlagsInternGuard(
+ SwDoc& rDoc, // change mode of this document
+ RedlineFlags eNewRedlineFlags, // new redline mode
+ RedlineFlags eRedlineFlagsMask /*change only bits set in this mask*/);
+
+ ~RedlineFlagsInternGuard();
+};
+
+}
+
+RedlineFlagsInternGuard::RedlineFlagsInternGuard(
+ SwDoc& rDoc,
+ RedlineFlags eNewRedlineFlags,
+ RedlineFlags eRedlineFlagsMask )
+ : mrDoc( rDoc ),
+ meOldRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() )
+{
+ mrDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( meOldRedlineFlags & ~eRedlineFlagsMask ) |
+ ( eNewRedlineFlags & eRedlineFlagsMask ) );
+}
+
+RedlineFlagsInternGuard::~RedlineFlagsInternGuard()
+{
+ mrDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( meOldRedlineFlags );
+}
+
+void SwUndoTableNumFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ // Could the box be changed?
+ if( !m_pBoxSet )
+ return ;
+
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = m_nNode;
+
+ SwNode * pNd = & pPam->GetPoint()->nNode.GetNode();
+ SwStartNode* pSttNd = pNd->FindSttNodeByType( SwTableBoxStartNode );
+ assert(pSttNd && "without StartNode no TableBox");
+ SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().GetTableBox(
+ pSttNd->GetIndex() );
+ OSL_ENSURE( pBox, "found no TableBox" );
+
+ SwFrameFormat* pBoxFormat = pBox->ClaimFrameFormat();
+ if( m_bNewFormat || m_bNewFormula || m_bNewValue )
+ {
+ SfxItemSet aBoxSet( rDoc.GetAttrPool(),
+ svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} );
+
+ // Resetting attributes is not enough. In addition, take care that the
+ // text will be also formatted correctly.
+ pBoxFormat->LockModify();
+
+ if( m_bNewFormula )
+ aBoxSet.Put( SwTableBoxFormula( m_aNewFormula ));
+ else
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
+ if( m_bNewFormat )
+ aBoxSet.Put( SwTableBoxNumFormat( m_nNewFormatIdx ));
+ else
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
+ if( m_bNewValue )
+ aBoxSet.Put( SwTableBoxValue( m_fNewNum ));
+ else
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE );
+ pBoxFormat->UnlockModify();
+
+ // dvo: When redlining is (was) enabled, setting the attribute
+ // will also change the cell content. To allow this, the
+ // RedlineFlags::Ignore flag must be removed during Redo. #108450#
+ RedlineFlagsInternGuard aGuard( rDoc, RedlineFlags::NONE, RedlineFlags::Ignore );
+ pBoxFormat->SetFormatAttr( aBoxSet );
+ }
+ else if( getSwDefaultTextFormat() != m_nFormatIdx )
+ {
+ SfxItemSet aBoxSet( rDoc.GetAttrPool(),
+ svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} );
+
+ aBoxSet.Put( SwTableBoxNumFormat( m_nFormatIdx ));
+ aBoxSet.Put( SwTableBoxValue( m_fNum ));
+
+ // Resetting attributes is not enough. In addition, take care that the
+ // text will be also formatted correctly.
+ pBoxFormat->LockModify();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
+ pBoxFormat->UnlockModify();
+
+ // dvo: When redlining is (was) enabled, setting the attribute
+ // will also change the cell content. To allow this, the
+ // RedlineFlags::Ignore flag must be removed during Redo. #108450#
+ RedlineFlagsInternGuard aGuard( rDoc, RedlineFlags::NONE, RedlineFlags::Ignore );
+ pBoxFormat->SetFormatAttr( aBoxSet );
+ }
+ else
+ {
+ // it's no number
+
+ // Resetting attributes is not enough. In addition, take care that the
+ // text will be also formatted correctly.
+ pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
+
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
+ }
+
+ if( m_bNewFormula )
+ {
+ // No matter what was set, an update of the table is always a good idea
+ SwTableFormulaUpdate aTableUpdate( &pSttNd->FindTableNode()->GetTable() );
+ rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
+ }
+
+ if( !pNd->IsContentNode() )
+ pNd = rDoc.GetNodes().GoNext( &pPam->GetPoint()->nNode );
+ pPam->GetPoint()->nContent.Assign( static_cast<SwContentNode*>(pNd), 0 );
+}
+
+void SwUndoTableNumFormat::SetBox( const SwTableBox& rBox )
+{
+ m_nNode = rBox.GetSttIdx();
+}
+
+UndoTableCpyTable_Entry::UndoTableCpyTable_Entry( const SwTableBox& rBox )
+ : nBoxIdx( rBox.GetSttIdx() ), nOffset( 0 ),
+ bJoin( false )
+{
+}
+
+SwUndoTableCpyTable::SwUndoTableCpyTable(const SwDoc* pDoc)
+ : SwUndo( SwUndoId::TBLCPYTBL, pDoc )
+{
+}
+
+SwUndoTableCpyTable::~SwUndoTableCpyTable()
+{
+ m_vArr.clear();
+ m_pInsRowUndo.reset();
+}
+
+void SwUndoTableCpyTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ DEBUG_REDLINE( &rDoc )
+
+ SwTableNode* pTableNd = nullptr;
+ for (size_t n = m_vArr.size(); n; )
+ {
+ UndoTableCpyTable_Entry *const pEntry = m_vArr[ --n ].get();
+ sal_uLong nSttPos = pEntry->nBoxIdx + pEntry->nOffset;
+ SwStartNode* pSNd = rDoc.GetNodes()[ nSttPos ]->StartOfSectionNode();
+ if( !pTableNd )
+ pTableNd = pSNd->FindTableNode();
+
+ SwTableBox& rBox = *pTableNd->GetTable().GetTableBox( nSttPos );
+
+ SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 );
+ rDoc.GetNodes().MakeTextNode( aInsIdx, rDoc.GetDfltTextFormatColl() );
+
+ // b62341295: Redline for copying tables
+ const SwNode *pEndNode = rBox.GetSttNd()->EndOfSectionNode();
+ SwPaM aPam( aInsIdx.GetNode(), *pEndNode );
+ std::unique_ptr<SwUndoDelete> pUndo;
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) )
+ {
+ bool bDeleteCompleteParagraph = false;
+ bool bShiftPam = false;
+ // There are a couple of different situations to consider during redlining
+ if( pEntry->pUndo )
+ {
+ SwUndoDelete *const pUndoDelete =
+ dynamic_cast<SwUndoDelete*>(pEntry->pUndo.get());
+ SwUndoRedlineDelete *const pUndoRedlineDelete =
+ dynamic_cast<SwUndoRedlineDelete*>(pEntry->pUndo.get());
+ assert(pUndoDelete || pUndoRedlineDelete);
+ if (pUndoRedlineDelete)
+ {
+ // The old content was not empty or he has been merged with the new content
+ bDeleteCompleteParagraph = !pEntry->bJoin; // bJoin is set when merged
+ // Set aTmpIdx to the beginning of the old content
+ SwNodeIndex aTmpIdx( *pEndNode,
+ pUndoRedlineDelete->NodeDiff()-1 );
+ SwTextNode *pText = aTmpIdx.GetNode().GetTextNode();
+ if( pText )
+ {
+ aPam.GetPoint()->nNode = *pText;
+ aPam.GetPoint()->nContent.Assign( pText,
+ pUndoRedlineDelete->ContentStart() );
+ }
+ else
+ *aPam.GetPoint() = SwPosition( aTmpIdx );
+ }
+ else if (pUndoDelete && pUndoDelete->IsDelFullPara())
+ {
+ // When the old content was an empty paragraph, but could not be joined
+ // with the new content (e.g. because of a section or table)
+ // We "save" the aPam.Point, we go one step backwards (because later on the
+ // empty paragraph will be inserted by the undo) and set the "ShiftPam-flag
+ // for step forward later on.
+ bDeleteCompleteParagraph = true;
+ bShiftPam = true;
+ SwNodeIndex aTmpIdx( *pEndNode, -1 );
+ SwTextNode *pText = aTmpIdx.GetNode().GetTextNode();
+ if( pText )
+ {
+ aPam.GetPoint()->nNode = *pText;
+ aPam.GetPoint()->nContent.Assign( pText, 0 );
+ }
+ else
+ *aPam.GetPoint() = SwPosition( aTmpIdx );
+ }
+ }
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, true, RedlineType::Any );
+
+ if( pEntry->pUndo )
+ {
+ pEntry->pUndo->UndoImpl(rContext);
+ pEntry->pUndo.reset();
+ }
+ if( bShiftPam )
+ {
+ // The aPam.Point is at the moment at the last position of the new content and has to be
+ // moved to the first position of the old content for the SwUndoDelete operation
+ SwNodeIndex aTmpIdx( aPam.GetPoint()->nNode, 1 );
+ SwTextNode *pText = aTmpIdx.GetNode().GetTextNode();
+ if( pText )
+ {
+ aPam.GetPoint()->nNode = *pText;
+ aPam.GetPoint()->nContent.Assign( pText, 0 );
+ }
+ else
+ *aPam.GetPoint() = SwPosition( aTmpIdx );
+ }
+ pUndo = std::make_unique<SwUndoDelete>( aPam, bDeleteCompleteParagraph, true );
+ }
+ else
+ {
+ pUndo = std::make_unique<SwUndoDelete>( aPam, true );
+ if( pEntry->pUndo )
+ {
+ pEntry->pUndo->UndoImpl(rContext);
+ pEntry->pUndo.reset();
+ }
+ }
+ pEntry->pUndo = std::move(pUndo);
+
+ aInsIdx = rBox.GetSttIdx() + 1;
+ rDoc.GetNodes().Delete( aInsIdx );
+
+ SfxItemSet aTmpSet(
+ rDoc.GetAttrPool(),
+ svl::Items<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{});
+ aTmpSet.Put( rBox.GetFrameFormat()->GetAttrSet() );
+ if( aTmpSet.Count() )
+ {
+ SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
+ pBoxFormat->ResetFormatAttr( RES_VERT_ORIENT );
+ }
+
+ if( pEntry->pBoxNumAttr )
+ {
+ rBox.ClaimFrameFormat()->SetFormatAttr( *pEntry->pBoxNumAttr );
+ pEntry->pBoxNumAttr.reset();
+ }
+
+ if( aTmpSet.Count() )
+ {
+ pEntry->pBoxNumAttr = std::make_unique<SfxItemSet>(
+ rDoc.GetAttrPool(),
+ svl::Items<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{});
+ pEntry->pBoxNumAttr->Put( aTmpSet );
+ }
+
+ pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx;
+ }
+
+ if( m_pInsRowUndo )
+ {
+ m_pInsRowUndo->UndoImpl(rContext);
+ }
+ DEBUG_REDLINE( &rDoc )
+}
+
+void SwUndoTableCpyTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ DEBUG_REDLINE( &rDoc )
+
+ if( m_pInsRowUndo )
+ {
+ m_pInsRowUndo->RedoImpl(rContext);
+ }
+
+ SwTableNode* pTableNd = nullptr;
+ for (size_t n = 0; n < m_vArr.size(); ++n)
+ {
+ UndoTableCpyTable_Entry *const pEntry = m_vArr[ n ].get();
+ sal_uLong nSttPos = pEntry->nBoxIdx + pEntry->nOffset;
+ SwStartNode* pSNd = rDoc.GetNodes()[ nSttPos ]->StartOfSectionNode();
+ if( !pTableNd )
+ pTableNd = pSNd->FindTableNode();
+
+ SwTableBox& rBox = *pTableNd->GetTable().GetTableBox( nSttPos );
+
+ SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 );
+
+ // b62341295: Redline for copying tables - Start.
+ rDoc.GetNodes().MakeTextNode( aInsIdx, rDoc.GetDfltTextFormatColl() );
+ SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode());
+ std::unique_ptr<SwUndo> pUndo = IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ? nullptr : std::make_unique<SwUndoDelete>( aPam, true );
+ if( pEntry->pUndo )
+ {
+ pEntry->pUndo->UndoImpl(rContext);
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) )
+ {
+ // PrepareRedline has to be called with the beginning of the old content
+ // When new and old content has been joined, the rIter.pAktPam has been set
+ // by the Undo operation to this point.
+ // Otherwise aInsIdx has been moved during the Undo operation
+ if( pEntry->bJoin )
+ {
+ SwPaM const& rLastPam =
+ rContext.GetCursorSupplier().GetCurrentShellCursor();
+ pUndo = PrepareRedline( &rDoc, rBox, *rLastPam.GetPoint(),
+ pEntry->bJoin, true );
+ }
+ else
+ {
+ SwPosition aTmpPos( aInsIdx );
+ pUndo = PrepareRedline( &rDoc, rBox, aTmpPos, pEntry->bJoin, true );
+ }
+ }
+ pEntry->pUndo.reset();
+ }
+ pEntry->pUndo = std::move(pUndo);
+ // b62341295: Redline for copying tables - End.
+
+ aInsIdx = rBox.GetSttIdx() + 1;
+ rDoc.GetNodes().Delete( aInsIdx );
+
+ SfxItemSet aTmpSet(
+ rDoc.GetAttrPool(),
+ svl::Items<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{});
+ aTmpSet.Put( rBox.GetFrameFormat()->GetAttrSet() );
+ if( aTmpSet.Count() )
+ {
+ SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
+ pBoxFormat->ResetFormatAttr( RES_VERT_ORIENT );
+ }
+ if( pEntry->pBoxNumAttr )
+ {
+ rBox.ClaimFrameFormat()->SetFormatAttr( *pEntry->pBoxNumAttr );
+ pEntry->pBoxNumAttr.reset();
+ }
+
+ if( aTmpSet.Count() )
+ {
+ pEntry->pBoxNumAttr = std::make_unique<SfxItemSet>(
+ rDoc.GetAttrPool(),
+ svl::Items<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{});
+ pEntry->pBoxNumAttr->Put( aTmpSet );
+ }
+
+ pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx;
+ }
+ DEBUG_REDLINE( &rDoc )
+}
+
+void SwUndoTableCpyTable::AddBoxBefore( const SwTableBox& rBox, bool bDelContent )
+{
+ if (!m_vArr.empty() && !bDelContent)
+ return;
+
+ UndoTableCpyTable_Entry* pEntry = new UndoTableCpyTable_Entry( rBox );
+ m_vArr.push_back(std::unique_ptr<UndoTableCpyTable_Entry>(pEntry));
+
+ SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
+ DEBUG_REDLINE( pDoc )
+ if( bDelContent )
+ {
+ SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 );
+ pDoc->GetNodes().MakeTextNode( aInsIdx, pDoc->GetDfltTextFormatColl() );
+ SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode() );
+
+ if( !pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ pEntry->pUndo = std::make_unique<SwUndoDelete>( aPam, true );
+ }
+
+ pEntry->pBoxNumAttr = std::make_unique<SfxItemSet>(
+ pDoc->GetAttrPool(),
+ svl::Items<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{});
+ pEntry->pBoxNumAttr->Put( rBox.GetFrameFormat()->GetAttrSet() );
+ if( !pEntry->pBoxNumAttr->Count() )
+ {
+ pEntry->pBoxNumAttr.reset();
+ }
+ DEBUG_REDLINE( pDoc )
+}
+
+void SwUndoTableCpyTable::AddBoxAfter( const SwTableBox& rBox, const SwNodeIndex& rIdx, bool bDelContent )
+{
+ UndoTableCpyTable_Entry *const pEntry = m_vArr.back().get();
+
+ // If the content was deleted then remove also the temporarily created node
+ if( bDelContent )
+ {
+ SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
+ DEBUG_REDLINE( pDoc )
+
+ if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ SwPosition aTmpPos( rIdx );
+ pEntry->pUndo = PrepareRedline( pDoc, rBox, aTmpPos, pEntry->bJoin, false );
+ }
+ SwNodeIndex aDelIdx( *rBox.GetSttNd(), 1 );
+ rBox.GetFrameFormat()->GetDoc()->GetNodes().Delete( aDelIdx );
+ DEBUG_REDLINE( pDoc )
+ }
+
+ pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx;
+}
+
+// PrepareRedline is called from AddBoxAfter() and from Redo() in slightly different situations.
+// bRedo is set by calling from Redo()
+// rJoin is false by calling from AddBoxAfter() and will be set if the old and new content has
+// been merged.
+// rJoin is true if Redo() is calling and the content has already been merged
+
+std::unique_ptr<SwUndo> SwUndoTableCpyTable::PrepareRedline( SwDoc* pDoc, const SwTableBox& rBox,
+ const SwPosition& rPos, bool& rJoin, bool bRedo )
+{
+ std::unique_ptr<SwUndo> pUndo;
+ // b62341295: Redline for copying tables
+ // What's to do?
+ // Mark the cell content before rIdx as insertion,
+ // mark the cell content behind rIdx as deletion
+ // merge text nodes at rIdx if possible
+ RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld | RedlineFlags::DontCombineRedlines ) & ~RedlineFlags::Ignore );
+ SwPosition aInsertEnd( rPos );
+ SwTextNode* pText;
+ if( !rJoin )
+ {
+ // If the content is not merged, the end of the insertion is at the end of the node
+ // _before_ the given position rPos
+ --aInsertEnd.nNode;
+ pText = aInsertEnd.nNode.GetNode().GetTextNode();
+ if( pText )
+ {
+ aInsertEnd.nContent.Assign(pText, pText->GetText().getLength());
+ if( !bRedo && rPos.nNode.GetNode().GetTextNode() )
+ { // Try to merge, if not called by Redo()
+ rJoin = true;
+ pText->JoinNext();
+ }
+ }
+ else
+ aInsertEnd.nContent.Assign(nullptr, 0);
+ }
+ // For joined (merged) contents the start of deletion and end of insertion are identical
+ // otherwise adjacent nodes.
+ SwPosition aDeleteStart( rJoin ? aInsertEnd : rPos );
+ if( !rJoin )
+ {
+ pText = aDeleteStart.nNode.GetNode().GetTextNode();
+ if( pText )
+ aDeleteStart.nContent.Assign( pText, 0 );
+ }
+ SwPosition aCellEnd( SwNodeIndex( *rBox.GetSttNd()->EndOfSectionNode(), -1 ) );
+ pText = aCellEnd.nNode.GetNode().GetTextNode();
+ if( pText )
+ aCellEnd.nContent.Assign(pText, pText->GetText().getLength());
+ if( aDeleteStart != aCellEnd )
+ { // If the old (deleted) part is not empty, here we are...
+ SwPaM aDeletePam( aDeleteStart, aCellEnd );
+ pUndo = std::make_unique<SwUndoRedlineDelete>( aDeletePam, SwUndoId::DELETE );
+ pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete, aDeletePam ), true );
+ }
+ else if( !rJoin ) // If the old part is empty and joined, we are finished
+ { // if it is not joined, we have to delete this empty paragraph
+ aCellEnd = SwPosition(
+ SwNodeIndex( *rBox.GetSttNd()->EndOfSectionNode() ));
+ SwPaM aTmpPam( aDeleteStart, aCellEnd );
+ pUndo = std::make_unique<SwUndoDelete>( aTmpPam, true );
+ }
+ SwPosition aCellStart( SwNodeIndex( *rBox.GetSttNd(), 2 ) );
+ pText = aCellStart.nNode.GetNode().GetTextNode();
+ if( pText )
+ aCellStart.nContent.Assign( pText, 0 );
+ if( aCellStart != aInsertEnd ) // An empty insertion will not been marked
+ {
+ SwPaM aTmpPam( aCellStart, aInsertEnd );
+ pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aTmpPam ), true );
+ }
+
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ return pUndo;
+}
+
+bool SwUndoTableCpyTable::InsertRow( SwTable& rTable, const SwSelBoxes& rBoxes,
+ sal_uInt16 nCnt )
+{
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rTable.GetTabSortBoxes()[0]->
+ GetSttNd()->FindTableNode());
+
+ m_pInsRowUndo.reset( new SwUndoTableNdsChg( SwUndoId::TABLE_INSROW, rBoxes, *pTableNd,
+ 0, 0, nCnt, true, false ) );
+ SwTableSortBoxes aTmpLst( rTable.GetTabSortBoxes() );
+
+ bool bRet = rTable.InsertRow( rTable.GetFrameFormat()->GetDoc(), rBoxes, nCnt, /*bBehind*/true );
+ if( bRet )
+ m_pInsRowUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ else
+ {
+ m_pInsRowUndo.reset();
+ }
+ return bRet;
+}
+
+bool SwUndoTableCpyTable::IsEmpty() const
+{
+ return !m_pInsRowUndo && m_vArr.empty();
+}
+
+SwUndoCpyTable::SwUndoCpyTable(const SwDoc* pDoc)
+ : SwUndo( SwUndoId::CPYTBL, pDoc ), m_nTableNode( 0 )
+{
+}
+
+SwUndoCpyTable::~SwUndoCpyTable()
+{
+}
+
+void SwUndoCpyTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwTableNode* pTNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode();
+
+ // move hard page breaks into next node
+ SwContentNode* pNextNd = rDoc.GetNodes()[ pTNd->EndOfSectionIndex()+1 ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTNd->GetTable().GetFrameFormat();
+ const SfxPoolItem *pItem;
+
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+ }
+
+ SwPaM aPam( *pTNd, *pTNd->EndOfSectionNode(), 0 , 1 );
+ m_pDelete.reset( new SwUndoDelete( aPam, true ) );
+}
+
+void SwUndoCpyTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pDelete->UndoImpl(rContext);
+ m_pDelete.reset();
+}
+
+SwUndoSplitTable::SwUndoSplitTable( const SwTableNode& rTableNd,
+ std::unique_ptr<SwSaveRowSpan> pRowSp, SplitTable_HeadlineOption eMode, bool bNewSize )
+ : SwUndo( SwUndoId::SPLIT_TABLE, rTableNd.GetDoc() ),
+ m_nTableNode( rTableNd.GetIndex() ), m_nOffset( 0 ), mpSaveRowSpan( std::move(pRowSp) ),
+ m_nMode( eMode ), m_nFormulaEnd( 0 ), m_bCalcNewSize( bNewSize )
+{
+ switch( m_nMode )
+ {
+ case SplitTable_HeadlineOption::BoxAttrAllCopy:
+ m_pHistory.reset(new SwHistory);
+ [[fallthrough]];
+ case SplitTable_HeadlineOption::BorderCopy:
+ case SplitTable_HeadlineOption::BoxAttrCopy:
+ m_pSavedTable.reset(new SaveTable( rTableNd.GetTable(), 1, false ));
+ break;
+ default: break;
+ }
+}
+
+SwUndoSplitTable::~SwUndoSplitTable()
+{
+ m_pSavedTable.reset();
+ m_pHistory.reset();
+ mpSaveRowSpan.reset();
+}
+
+void SwUndoSplitTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ SwNodeIndex& rIdx = pPam->GetPoint()->nNode;
+ rIdx = m_nTableNode + m_nOffset;
+ pPam->GetPoint()->nContent.Assign(rIdx.GetNode().GetContentNode(), 0);
+ assert(rIdx.GetNode().GetContentNode()->Len() == 0); // empty para inserted
+
+ {
+ // avoid asserts from ~SwIndexReg
+ SwNodeIndex const idx(pDoc->GetNodes(), m_nTableNode + m_nOffset);
+ {
+ SwPaM pam(idx);
+ pam.Move(fnMoveBackward, GoInContent);
+ ::PaMCorrAbs(*pPam, *pam.GetPoint());
+ }
+
+ // remove implicitly created paragraph again
+ pDoc->GetNodes().Delete( idx );
+ }
+
+ rIdx = m_nTableNode + m_nOffset;
+ SwTableNode* pTableNd = rIdx.GetNode().GetTableNode();
+ SwTable& rTable = pTableNd->GetTable();
+
+ SwTableFormulaUpdate aMsgHint( &rTable );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ switch( m_nMode )
+ {
+ case SplitTable_HeadlineOption::BoxAttrAllCopy:
+ if( m_pHistory )
+ m_pHistory->TmpRollback( pDoc, m_nFormulaEnd );
+ [[fallthrough]];
+ case SplitTable_HeadlineOption::BoxAttrCopy:
+ case SplitTable_HeadlineOption::BorderCopy:
+ {
+ m_pSavedTable->CreateNew( rTable, false );
+ m_pSavedTable->RestoreAttr( rTable );
+ }
+ break;
+
+ case SplitTable_HeadlineOption::ContentCopy:
+ // the created first line has to be removed again
+ {
+ SwSelBoxes aSelBoxes;
+ SwTableBox* pBox = rTable.GetTableBox( m_nTableNode + m_nOffset + 1 );
+ SwTable::SelLineFromBox( pBox, aSelBoxes );
+ FndBox_ aTmpBox( nullptr, nullptr );
+ aTmpBox.SetTableLines( aSelBoxes, rTable );
+ aTmpBox.DelFrames( rTable );
+ rTable.DeleteSel( pDoc, aSelBoxes, nullptr, nullptr, false, false );
+ }
+ break;
+ default: break;
+ }
+
+ pDoc->GetNodes().MergeTable( rIdx );
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback( pDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+ if( mpSaveRowSpan )
+ {
+ pTableNd = rIdx.GetNode().FindTableNode();
+ if( pTableNd )
+ pTableNd->GetTable().RestoreRowSpan( *mpSaveRowSpan );
+ }
+ ClearFEShellTabCols(*pDoc, nullptr);
+}
+
+void SwUndoSplitTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = m_nTableNode;
+ pDoc->SplitTable( *pPam->GetPoint(), m_nMode, m_bCalcNewSize );
+
+ ClearFEShellTabCols(*pDoc, nullptr);
+}
+
+void SwUndoSplitTable::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM *const pPam = & rContext.GetRepeatPaM();
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ pDoc->SplitTable( *pPam->GetPoint(), m_nMode, m_bCalcNewSize );
+ ClearFEShellTabCols(*pDoc, nullptr);
+}
+
+void SwUndoSplitTable::SaveFormula( SwHistory& rHistory )
+{
+ if( !m_pHistory )
+ m_pHistory.reset(new SwHistory);
+
+ m_nFormulaEnd = rHistory.Count();
+ m_pHistory->Move( 0, &rHistory );
+}
+
+SwUndoMergeTable::SwUndoMergeTable( const SwTableNode& rTableNd,
+ const SwTableNode& rDelTableNd,
+ bool bWithPrv, sal_uInt16 nMd )
+ : SwUndo( SwUndoId::MERGE_TABLE, rTableNd.GetDoc() ),
+ m_nMode( nMd ), m_bWithPrev( bWithPrv )
+{
+ // memorize end node of the last table cell that'll stay in position
+ if( m_bWithPrev )
+ m_nTableNode = rDelTableNd.EndOfSectionIndex() - 1;
+ else
+ m_nTableNode = rTableNd.EndOfSectionIndex() - 1;
+
+ m_aName = rDelTableNd.GetTable().GetFrameFormat()->GetName();
+ m_pSaveTable.reset(new SaveTable( rDelTableNd.GetTable() ));
+
+ if (m_bWithPrev)
+ m_pSaveHdl.reset( new SaveTable( rTableNd.GetTable(), 1 ) );
+}
+
+SwUndoMergeTable::~SwUndoMergeTable()
+{
+ m_pSaveTable.reset();
+ m_pSaveHdl.reset();
+ m_pHistory.reset();
+}
+
+void SwUndoMergeTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pPam->DeleteMark();
+ SwNodeIndex& rIdx = pPam->GetPoint()->nNode;
+ rIdx = m_nTableNode;
+
+ SwTableNode* pTableNd = rIdx.GetNode().FindTableNode();
+ SwTable* pTable = &pTableNd->GetTable();
+
+ SwTableFormulaUpdate aMsgHint( pTable );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ // get lines for layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( *pTable );
+ aFndBox.DelFrames( *pTable );
+ // ? TL_CHART2: notification or locking of controller required ?
+
+ SwTableNode* pNew = pDoc->GetNodes().SplitTable( rIdx );
+
+ // update layout
+ aFndBox.MakeFrames( *pTable );
+ // ? TL_CHART2: notification or locking of controller required ?
+
+ if( m_bWithPrev )
+ {
+ // move name
+ pNew->GetTable().GetFrameFormat()->SetName( pTable->GetFrameFormat()->GetName() );
+ m_pSaveHdl->RestoreAttr( pNew->GetTable() );
+ }
+ else
+ pTable = &pNew->GetTable();
+
+ pTable->GetFrameFormat()->SetName( m_aName );
+ m_pSaveTable->RestoreAttr( *pTable );
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback( pDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+
+ // create frames for the new table
+ SwNodeIndex aTmpIdx( *pNew );
+ pNew->MakeOwnFrames(&aTmpIdx);
+
+ // position cursor somewhere in content
+ SwContentNode* pCNd = pDoc->GetNodes().GoNext( &rIdx );
+ pPam->GetPoint()->nContent.Assign( pCNd, 0 );
+
+ ClearFEShellTabCols(*pDoc, nullptr);
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if (pPCD)
+ {
+ pDoc->UpdateCharts( pTable->GetFrameFormat()->GetName() );
+ pDoc->UpdateCharts( pNew->GetTable().GetFrameFormat()->GetName() );
+ }
+}
+
+void SwUndoMergeTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = m_nTableNode;
+ if( m_bWithPrev )
+ pPam->GetPoint()->nNode = m_nTableNode + 3;
+ else
+ pPam->GetPoint()->nNode = m_nTableNode;
+
+ pDoc->MergeTable( *pPam->GetPoint(), m_bWithPrev, m_nMode );
+
+ ClearFEShellTabCols(*pDoc, nullptr);
+}
+
+void SwUndoMergeTable::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pPam = & rContext.GetRepeatPaM();
+
+ pDoc->MergeTable( *pPam->GetPoint(), m_bWithPrev, m_nMode );
+ ClearFEShellTabCols(*pDoc, nullptr);
+}
+
+void SwUndoMergeTable::SaveFormula( SwHistory& rHistory )
+{
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ m_pHistory->Move( 0, &rHistory );
+}
+
+void InsertSort( std::vector<sal_uInt16>& rArr, sal_uInt16 nIdx )
+{
+ size_t nO = rArr.size();
+ size_t nU = 0;
+ if( nO > 0 )
+ {
+ nO--;
+ while( nU <= nO )
+ {
+ const size_t nM = nU + ( nO - nU ) / 2;
+ if ( rArr[nM] == nIdx )
+ {
+ OSL_FAIL( "Index already exists. This should never happen." );
+ return;
+ }
+ if( rArr[nM] < nIdx )
+ nU = nM + 1;
+ else if( nM == 0 )
+ break;
+ else
+ nO = nM - 1;
+ }
+ }
+ rArr.insert( rArr.begin() + nU, nIdx );
+}
+
+#if OSL_DEBUG_LEVEL > 0
+void CheckTable( const SwTable& rTable )
+{
+ const SwNodes& rNds = rTable.GetFrameFormat()->GetDoc()->GetNodes();
+ const SwTableSortBoxes& rSrtArr = rTable.GetTabSortBoxes();
+ for (size_t n = 0; n < rSrtArr.size(); ++n)
+ {
+ const SwTableBox* pBox = rSrtArr[ n ];
+ const SwNode* pNd = pBox->GetSttNd();
+ OSL_ENSURE( rNds[ pBox->GetSttIdx() ] == pNd, "Box with wrong StartNode" );
+ }
+}
+#endif
+
+SwUndoTableStyleMake::SwUndoTableStyleMake(const OUString& rName, const SwDoc* pDoc)
+ : SwUndo(SwUndoId::TBLSTYLE_CREATE, pDoc),
+ m_sName(rName)
+{ }
+
+SwUndoTableStyleMake::~SwUndoTableStyleMake()
+{ }
+
+void SwUndoTableStyleMake::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pAutoFormat = rContext.GetDoc().DelTableStyle(m_sName, true);
+}
+
+void SwUndoTableStyleMake::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ if (m_pAutoFormat)
+ {
+ SwTableAutoFormat* pFormat = rContext.GetDoc().MakeTableStyle(m_sName, true);
+ if (pFormat)
+ {
+ *pFormat = *m_pAutoFormat;
+ m_pAutoFormat.reset();
+ }
+ }
+}
+
+SwRewriter SwUndoTableStyleMake::GetRewriter() const
+{
+ SwRewriter aResult;
+ aResult.AddRule(UndoArg1, m_sName);
+ return aResult;
+}
+
+SwUndoTableStyleDelete::SwUndoTableStyleDelete(std::unique_ptr<SwTableAutoFormat> pAutoFormat, const std::vector<SwTable*>& rAffectedTables, const SwDoc* pDoc)
+ : SwUndo(SwUndoId::TBLSTYLE_DELETE, pDoc),
+ m_pAutoFormat(std::move(pAutoFormat)),
+ m_rAffectedTables(rAffectedTables)
+{ }
+
+SwUndoTableStyleDelete::~SwUndoTableStyleDelete()
+{ }
+
+void SwUndoTableStyleDelete::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwTableAutoFormat* pNewFormat = rContext.GetDoc().MakeTableStyle(m_pAutoFormat->GetName(), true);
+ *pNewFormat = *m_pAutoFormat;
+ for (size_t i=0; i < m_rAffectedTables.size(); i++)
+ m_rAffectedTables[i]->SetTableStyleName(m_pAutoFormat->GetName());
+}
+
+void SwUndoTableStyleDelete::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ // Don't need to remember deleted table style nor affected tables, because they must be the same as these already known.
+ rContext.GetDoc().DelTableStyle(m_pAutoFormat->GetName());
+}
+
+SwRewriter SwUndoTableStyleDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+ aResult.AddRule(UndoArg1, m_pAutoFormat->GetName());
+ return aResult;
+}
+
+SwUndoTableStyleUpdate::SwUndoTableStyleUpdate(const SwTableAutoFormat& rNewFormat, const SwTableAutoFormat& rOldFormat, const SwDoc* pDoc)
+ : SwUndo(SwUndoId::TBLSTYLE_UPDATE, pDoc)
+ , m_pOldFormat(new SwTableAutoFormat(rOldFormat))
+ , m_pNewFormat(new SwTableAutoFormat(rNewFormat))
+{ }
+
+SwUndoTableStyleUpdate::~SwUndoTableStyleUpdate()
+{ }
+
+void SwUndoTableStyleUpdate::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ rContext.GetDoc().ChgTableStyle(m_pNewFormat->GetName(), *m_pOldFormat);
+}
+
+void SwUndoTableStyleUpdate::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ rContext.GetDoc().ChgTableStyle(m_pNewFormat->GetName(), *m_pNewFormat);
+}
+
+SwRewriter SwUndoTableStyleUpdate::GetRewriter() const
+{
+ SwRewriter aResult;
+ aResult.AddRule(UndoArg1, m_pNewFormat->GetName());
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/untblk.cxx b/sw/source/core/undo/untblk.cxx
new file mode 100644
index 000000000..374241404
--- /dev/null
+++ b/sw/source/core/undo/untblk.cxx
@@ -0,0 +1,451 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <docary.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <mvsave.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <redline.hxx>
+#include <frameformats.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 )
+ , m_pTextFormatColl(nullptr)
+ , m_pLastNodeColl(nullptr)
+ , m_nDeleteTextNodes(1)
+ , m_nNodeDiff(0)
+ , m_nSetPos(0)
+{
+ m_pHistory.reset( new SwHistory );
+ SwDoc* pDoc = rPam.GetDoc();
+
+ SwTextNode* pTextNd = rPam.GetPoint()->nNode.GetNode().GetTextNode();
+ if( pTextNd )
+ {
+ m_pTextFormatColl = pTextNd->GetTextColl();
+ assert(m_pTextFormatColl);
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nSttNode,
+ 0, pTextNd->GetText().getLength(), false );
+ if( pTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pTextNd->GetpSwAttrSet(), m_nSttNode );
+
+ // We may have some flys anchored to paragraph where we inserting.
+ // 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.
+ m_pFrameFormats = sw::GetFlysAnchoredAt(*pDoc, m_nSttNode);
+ }
+ // consider Redline
+ if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlineData.reset( new SwRedlineData( RedlineType::Insert, pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+}
+
+// This method does two things:
+// 1. Adjusts SwUndoRng members, required for Undo.
+// Members are:
+// SwUndoRng::nSttNode - all nodes starting from this node will be deleted during Undo (in SwUndoInserts::UndoImpl)
+// SwUndoRng::nSttContent - corresponding content index in SwUndoRng::nSttNode
+// SwUndoRng::nEndNode - end node for deletion
+// SwUndoRng::nEndContent - end content index
+// All these members are filled in during construction of SwUndoInserts instance, and can be adjusted using this method
+//
+// 2. Fills in m_FlyUndos array with flys anchored ONLY to first and last paragraphs (first == rPam.Start(), last == rPam.End())
+// Flys, anchored to any paragraph, but not first and last, are handled by DelContentIndex (see SwUndoInserts::UndoImpl) and are not stored in m_FlyUndos.
+
+void SwUndoInserts::SetInsertRange( const SwPaM& rPam, bool bScanFlys,
+ int const nDeleteTextNodes)
+{
+ const SwPosition* pTmpPos = rPam.End();
+ m_nEndNode = pTmpPos->nNode.GetIndex();
+ m_nEndContent = pTmpPos->nContent.GetIndex();
+ if( rPam.HasMark() )
+ {
+ if( pTmpPos == rPam.GetPoint() )
+ pTmpPos = rPam.GetMark();
+ else
+ pTmpPos = rPam.GetPoint();
+
+ m_nSttNode = pTmpPos->nNode.GetIndex();
+ m_nSttContent = pTmpPos->nContent.GetIndex();
+
+ m_nDeleteTextNodes = nDeleteTextNodes;
+ if (m_nDeleteTextNodes == 0) // if a table selection is added...
+ {
+ ++m_nSttNode; // ... then the CopyPam is not fully correct
+ }
+ }
+
+ // Fill m_FlyUndos with flys anchored to first and last paragraphs
+
+ if( bScanFlys)
+ {
+ // than collect all new Flys
+ SwDoc* pDoc = rPam.GetDoc();
+ 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();
+ if (IsCreateUndoForNewFly(*pAnchor, m_nSttNode, m_nEndNode))
+ {
+ std::vector<SwFrameFormat*>::iterator it;
+ if( !m_pFrameFormats ||
+ m_pFrameFormats->end() == ( it = std::find( m_pFrameFormats->begin(), m_pFrameFormats->end(), pFormat ) ) )
+ {
+ std::shared_ptr<SwUndoInsLayFormat> const pFlyUndo =
+ std::make_shared<SwUndoInsLayFormat>(pFormat, 0, 0);
+ m_FlyUndos.push_back(pFlyUndo);
+ }
+ else
+ m_pFrameFormats->erase( it );
+ }
+ }
+ m_pFrameFormats.reset();
+ }
+}
+
+/** 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:
+ // ExcludeFlyAtStartEnd 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
+ {
+ // Insert saves content in IconSection
+ SwNodes& rUNds = m_pUndoNodeIndex->GetNodes();
+ rUNds.Delete(*m_pUndoNodeIndex,
+ rUNds.GetEndOfExtras().GetIndex() - m_pUndoNodeIndex->GetIndex());
+ m_pUndoNodeIndex.reset();
+ }
+ m_pFrameFormats.reset();
+ m_pRedlineData.reset();
+}
+
+// Undo Insert operation
+// It's important to note that Undo stores absolute node indexes. I.e. if during insertion, you insert nodes 31 to 33,
+// during Undo nodes with indices from 31 to 33 will be deleted. Undo doesn't check that nodes 31 to 33 are the same nodes which were inserted.
+// It just deletes them.
+// This may seem as bad programming practice, but Undo actions are strongly ordered. If you change your document in some way, a new Undo action is added.
+// During Undo most recent actions will be executed first. So during execution of particular Undo action indices will be correct.
+// But storing absolute indices leads to crashes if some action in Undo fails to roll back some modifications.
+
+// Has following main steps:
+// 1. m_FlyUndos removes flys anchored to first and last paragraph in Undo range.
+// This array may be empty.
+// 2. DelContentIndex to delete footnotes, flys, bookmarks (see comment for this function)
+// Deleted flys are stored in pHistory array.
+// First and last paragraphs flys are not deleted by DelContentIndex!
+// For flys anchored to last paragraph, DelContentIndex re-anchors them to
+// the last paragraph that will remain after Undo.
+// 3. MoveToUndoNds moves nodes to Undo nodes array and removes them from document.
+// 4. Lastly (starting from if(pTextNode)), text from last paragraph is joined to last remaining paragraph and FormatColl for last paragraph is restored.
+// Format coll for last paragraph is removed during execution of UndoImpl
+
+void SwUndoInserts::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwPaM& rPam = AddUndoRedoPaM(rContext);
+
+ m_nNodeDiff = 0;
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
+
+ // if Point and Mark are different text nodes so a JoinNext has to be done
+ bool bJoinNext = m_nSttNode != m_nEndNode &&
+ rPam.GetMark()->nNode.GetNode().GetTextNode() &&
+ rPam.GetPoint()->nNode.GetNode().GetTextNode();
+
+ // Is there any content? (loading from template does not have content)
+ if( m_nSttNode != m_nEndNode || m_nSttContent != m_nEndContent )
+ {
+ if( m_nSttNode != m_nEndNode )
+ {
+ SwTextNode* pTextNd = rDoc.GetNodes()[ m_nEndNode ]->GetTextNode();
+ if (pTextNd && pTextNd->GetText().getLength() == m_nEndContent)
+ m_pLastNodeColl = pTextNd->GetTextColl();
+ }
+
+ // tdf#128739 correct cursors but do not delete bookmarks yet
+ ::PaMCorrAbs(rPam, *rPam.End());
+
+ 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);
+ }
+ m_nNodeDiff += nTmp - rPam.GetPoint()->nNode.GetIndex();
+ }
+
+ if (m_nSttNode != m_nEndNode || m_nSttContent != m_nEndContent)
+ {
+ // are there Footnotes or ContentFlyFrames in text?
+ m_nSetPos = m_pHistory->Count();
+ sal_uLong nTmp = rPam.GetMark()->nNode.GetIndex();
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType::AllMask|DelContentType::ExcludeFlyAtStartEnd);
+ m_nNodeDiff += nTmp - rPam.GetMark()->nNode.GetIndex();
+ if( *rPam.GetPoint() != *rPam.GetMark() )
+ {
+ m_pUndoNodeIndex.reset(
+ new SwNodeIndex(rDoc.GetNodes().GetEndOfContent()));
+ MoveToUndoNds(rPam, m_pUndoNodeIndex.get());
+
+ if (m_nDeleteTextNodes == 0)
+ {
+ rPam.Move( fnMoveBackward, GoInContent );
+ }
+ }
+ }
+
+ SwNodeIndex& rIdx = rPam.GetPoint()->nNode;
+ SwTextNode* pTextNode = rIdx.GetNode().GetTextNode();
+ if( pTextNode )
+ {
+ if( !m_pTextFormatColl ) // if 0 than it's no TextNode -> delete
+ {
+ SwNodeIndex aDelIdx( rIdx );
+ assert(0 < m_nDeleteTextNodes && m_nDeleteTextNodes < 3);
+ for (int i = 0; i < m_nDeleteTextNodes; ++i)
+ {
+ rPam.Move(fnMoveForward, GoInNode);
+ }
+ rPam.DeleteMark();
+
+ for (int i = 0; i < m_nDeleteTextNodes; ++i)
+ {
+ RemoveIdxRel(aDelIdx.GetIndex() + i, *rPam.GetPoint());
+ }
+
+ rDoc.GetNodes().Delete( aDelIdx, m_nDeleteTextNodes );
+ }
+ else
+ {
+ if( bJoinNext && pTextNode->CanJoinNext())
+ {
+ {
+ RemoveIdxRel( rIdx.GetIndex()+1, SwPosition( rIdx,
+ SwIndex( pTextNode, pTextNode->GetText().getLength() )));
+ }
+ pTextNode->JoinNext();
+ }
+ // reset all text attributes in the paragraph!
+ pTextNode->RstTextAttr( SwIndex(pTextNode, 0), pTextNode->Len(), 0, nullptr, true );
+
+ pTextNode->ResetAllAttr();
+
+ if (rDoc.GetTextFormatColls()->IsAlive(m_pTextFormatColl))
+ m_pTextFormatColl = static_cast<SwTextFormatColl*>(pTextNode->ChgFormatColl( m_pTextFormatColl )) ;
+
+ m_pHistory->SetTmpEnd( m_nSetPos );
+ m_pHistory->TmpRollback(&rDoc, 0, false);
+ }
+ }
+}
+
+// See SwUndoInserts::UndoImpl comments
+// All actions here should be done in reverse order to what is done in SwUndoInserts::UndoImpl!
+
+void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ // position cursor onto REDO section
+ SwPaM& rPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+ SwDoc* pDoc = rPam.GetDoc();
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = m_nSttNode - m_nNodeDiff;
+ SwContentNode* pCNd = rPam.GetContentNode();
+ rPam.GetPoint()->nContent.Assign( pCNd, m_nSttContent );
+
+ SwTextFormatColl* pSavTextFormatColl = m_pTextFormatColl;
+ if( m_pTextFormatColl && pCNd && pCNd->IsTextNode() )
+ pSavTextFormatColl = static_cast<SwTextNode*>(pCNd)->GetTextColl();
+
+ m_pHistory->SetTmpEnd( m_nSetPos );
+
+ // retrieve start position for rollback
+ if( ( m_nSttNode != m_nEndNode || m_nSttContent != m_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!)
+ sal_uLong const nMvNd = m_pUndoNodeIndex->GetIndex();
+ m_pUndoNodeIndex.reset();
+ MoveFromUndoNds(*pDoc, nMvNd, *rPam.GetMark());
+ if (m_nDeleteTextNodes != 0)
+ {
+ 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(m_pTextFormatColl))
+ {
+ SwTextNode* pTextNd = rPam.GetMark()->nNode.GetNode().GetTextNode();
+ if( pTextNd )
+ pTextNd->ChgFormatColl( m_pTextFormatColl );
+ }
+ m_pTextFormatColl = pSavTextFormatColl;
+
+ if (m_pLastNodeColl && pDoc->GetTextFormatColls()->IsAlive(m_pLastNodeColl)
+ && rPam.GetPoint()->nNode != rPam.GetMark()->nNode)
+ {
+ SwTextNode* pTextNd = rPam.GetPoint()->nNode.GetNode().GetTextNode();
+ if( pTextNd )
+ pTextNd->ChgFormatColl( m_pLastNodeColl );
+ }
+
+ // tdf#108124 the SwHistoryChangeFlyAnchor/SwHistoryFlyCnt must run before
+ // m_FlyUndos as they were created by DelContentIndex()
+ m_pHistory->Rollback( pDoc, m_nSetPos );
+
+ // tdf#108124 (10/25/2017)
+ // During UNDO we call SwUndoInsLayFormat::UndoImpl in reverse order,
+ // firstly for m_FlyUndos[ m_FlyUndos.size()-1 ], etc.
+ // As absolute node index of fly stored in SwUndoFlyBase::nNdPgPos we
+ // should recover from Undo in direct order (last should be recovered first)
+ // During REDO we should recover Flys (Images) in direct order,
+ // firstly m_FlyUndos[0], then with m_FlyUndos[1] index, etc.
+
+ for (size_t n = 0; m_FlyUndos.size() > n; ++n)
+ {
+ m_FlyUndos[n]->RedoImpl(rContext);
+ }
+
+ if( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags::Ignore );
+ pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ), true);
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ pDoc->getIDocumentRedlineAccess().SplitRedline(rPam);
+}
+
+void SwUndoInserts::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM aPam( rContext.GetDoc().GetNodes().GetEndOfContent() );
+ SetPaM( aPam );
+ SwPaM & rRepeatPaM( rContext.GetRepeatPaM() );
+ aPam.GetDoc()->getIDocumentContentOperations().CopyRange( aPam, *rRepeatPaM.GetPoint(), SwCopyFlags::CheckPosInFly);
+}
+
+SwUndoInsDoc::SwUndoInsDoc( const SwPaM& rPam )
+ : SwUndoInserts( SwUndoId::INSDOKUMENT, rPam )
+{
+}
+
+SwUndoCpyDoc::SwUndoCpyDoc( const SwPaM& rPam )
+ : SwUndoInserts( SwUndoId::COPY, rPam )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */