summaryrefslogtreecommitdiffstats
path: root/sw/source/core/undo
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/core/undo
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
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.cxx67
-rw-r--r--sw/source/core/undo/SwUndoField.cxx149
-rw-r--r--sw/source/core/undo/SwUndoFmt.cxx467
-rw-r--r--sw/source/core/undo/SwUndoPageDesc.cxx352
-rw-r--r--sw/source/core/undo/SwUndoTOXChange.cxx85
-rw-r--r--sw/source/core/undo/UndoThemeChange.cxx48
-rw-r--r--sw/source/core/undo/docundo.cxx844
-rw-r--r--sw/source/core/undo/rolbck.cxx1525
-rw-r--r--sw/source/core/undo/unattr.cxx1155
-rw-r--r--sw/source/core/undo/unbkmk.cxx223
-rw-r--r--sw/source/core/undo/undel.cxx1361
-rw-r--r--sw/source/core/undo/undobj.cxx1768
-rw-r--r--sw/source/core/undo/undobj1.cxx720
-rw-r--r--sw/source/core/undo/undoflystrattr.cxx122
-rw-r--r--sw/source/core/undo/undraw.cxx679
-rw-r--r--sw/source/core/undo/unfmco.cxx87
-rw-r--r--sw/source/core/undo/unins.cxx1035
-rw-r--r--sw/source/core/undo/unmove.cxx305
-rw-r--r--sw/source/core/undo/unnum.cxx397
-rw-r--r--sw/source/core/undo/unoutl.cxx73
-rw-r--r--sw/source/core/undo/unovwr.cxx475
-rw-r--r--sw/source/core/undo/unredln.cxx602
-rw-r--r--sw/source/core/undo/unsect.cxx613
-rw-r--r--sw/source/core/undo/unsort.cxx236
-rw-r--r--sw/source/core/undo/unspnd.cxx192
-rw-r--r--sw/source/core/undo/untbl.cxx3179
-rw-r--r--sw/source/core/undo/untblk.cxx484
27 files changed, 17243 insertions, 0 deletions
diff --git a/sw/source/core/undo/SwRewriter.cxx b/sw/source/core/undo/SwRewriter.cxx
new file mode 100644
index 0000000000..58e6d06e9e
--- /dev/null
+++ b/sw/source/core/undo/SwRewriter.cxx
@@ -0,0 +1,67 @@
+/* -*- 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>
+
+SwRewriter::SwRewriter() {}
+
+void SwRewriter::AddRule(SwUndoArg eWhat, const OUString& rWith)
+{
+ SwRewriteRule aRule(eWhat, rWith);
+
+ std::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 0000000000..57d615e04f
--- /dev/null
+++ b/sw/source/core/undo/SwUndoField.cxx
@@ -0,0 +1,149 @@
+/* -*- 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 <utility>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star::uno;
+
+SwUndoField::SwUndoField(const SwPosition & rPos )
+ : SwUndo(SwUndoId::FIELD, &rPos.GetDoc())
+{
+ m_nNodeIndex = rPos.GetNodeIndex();
+ m_nOffset = rPos.GetContentIndex();
+ m_pDoc = &rPos.GetDoc();
+}
+
+SwUndoField::~SwUndoField()
+{
+}
+
+SwPosition SwUndoField::GetPosition()
+{
+ SwNode * pNode = m_pDoc->GetNodes()[m_nNodeIndex];
+ SwContentIndex aIndex(pNode->GetContentNode(), m_nOffset);
+ SwPosition aResult(*pNode, aIndex);
+
+ return aResult;
+}
+
+SwUndoFieldFromDoc::SwUndoFieldFromDoc(const SwPosition & rPos,
+ const SwField & rOldField,
+ const SwField & rNewField,
+ bool _bUpdate)
+ : SwUndoField(rPos)
+ , m_pOldField(rOldField.CopyField())
+ , m_pNewField(rNewField.CopyField())
+ , m_bUpdate(_bUpdate)
+{
+ OSL_ENSURE(m_pOldField, "No old field!");
+ OSL_ENSURE(m_pNewField, "No new field!");
+ OSL_ENSURE(m_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)
+ {
+ m_pDoc->getIDocumentFieldsAccess().UpdateField(pTextField, *m_pOldField, m_bUpdate);
+ }
+}
+
+void SwUndoFieldFromDoc::DoImpl()
+{
+ SwTextField * pTextField = sw::DocumentFieldsManager::GetTextFieldAtPos(GetPosition());
+ const SwField * pField = pTextField ? pTextField->GetFormatField().GetField() : nullptr;
+
+ if (pField)
+ {
+ m_pDoc->getIDocumentFieldsAccess().UpdateField(pTextField, *m_pNewField, m_bUpdate);
+ SwFormatField* pDstFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField());
+
+ if (m_pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(), false) == pDstFormatField->GetField()->GetTyp())
+ m_pDoc->GetDocShell()->Broadcast( SwFormatFieldHint( pDstFormatField, SwFormatFieldHintWhich::INSERTED ) );
+ }
+}
+
+void SwUndoFieldFromDoc::RedoImpl(::sw::UndoRedoContext &)
+{
+ DoImpl();
+}
+
+void SwUndoFieldFromDoc::RepeatImpl(::sw::RepeatContext &)
+{
+ ::sw::UndoGuard const undoGuard(m_pDoc->GetIDocumentUndoRedo());
+ DoImpl();
+}
+
+SwUndoFieldFromAPI::SwUndoFieldFromAPI(const SwPosition & rPos,
+ Any aOldVal, Any aNewVal,
+ sal_uInt16 _nWhich)
+ : SwUndoField(rPos), m_aOldVal(std::move(aOldVal)), m_aNewVal(std::move(aNewVal)), m_nWhich(_nWhich)
+{
+}
+
+SwUndoFieldFromAPI::~SwUndoFieldFromAPI()
+{
+}
+
+void SwUndoFieldFromAPI::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwField * pField = sw::DocumentFieldsManager::GetFieldAtPos(GetPosition());
+
+ if (pField)
+ pField->PutValue(m_aOldVal, m_nWhich);
+}
+
+void SwUndoFieldFromAPI::DoImpl()
+{
+ SwField * pField = sw::DocumentFieldsManager::GetFieldAtPos(GetPosition());
+
+ if (pField)
+ pField->PutValue(m_aNewVal, m_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 0000000000..3493ad56f3
--- /dev/null
+++ b/sw/source/core/undo/SwUndoFmt.cxx
@@ -0,0 +1,467 @@
+/* -*- 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>
+#include <utility>
+
+SwUndoFormatCreate::SwUndoFormatCreate
+(SwUndoId nUndoId, SwFormat * _pNew, SwFormat const * _pDerivedFrom, SwDoc& rDoc)
+ : SwUndo(nUndoId, &rDoc), m_pNew(_pNew),
+ m_rDoc(rDoc), m_nId(0), m_bAuto(false)
+{
+ if (_pDerivedFrom)
+ m_sDerivedFrom = _pDerivedFrom->GetName();
+}
+
+SwUndoFormatCreate::~SwUndoFormatCreate()
+{
+}
+
+void SwUndoFormatCreate::UndoImpl(::sw::UndoRedoContext &)
+{
+ if (!m_pNew)
+ return;
+
+ if (m_sNewName.isEmpty())
+ m_sNewName = m_pNew->GetName();
+
+ if (!m_sNewName.isEmpty())
+ m_pNew = Find(m_sNewName);
+
+ if (m_pNew)
+ {
+ m_pNewSet.reset(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_rDoc.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& rDoc)
+ : SwUndo(nUndoId, &rDoc),
+ m_rDoc(rDoc), 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_rDoc.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,
+ OUString _sOldName,
+ OUString _sNewName,
+ SwDoc& rDoc)
+ : SwUndo(nUndoId, &rDoc), m_sOldName(std::move(_sOldName)),
+ m_sNewName(std::move(_sNewName)), m_rDoc(rDoc)
+{
+}
+
+SwUndoRenameFormat::~SwUndoRenameFormat()
+{
+}
+
+void SwUndoRenameFormat::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pFormat = Find(m_sNewName);
+
+ if (pFormat)
+ {
+ m_rDoc.RenameFormat(*pFormat, m_sOldName, true);
+ }
+}
+
+void SwUndoRenameFormat::RedoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pFormat = Find(m_sOldName);
+
+ if (pFormat)
+ {
+ m_rDoc.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& rDoc)
+ : SwUndoFormatCreate(SwUndoId::TXTFMTCOL_CREATE, _pNew, _pDerivedFrom, rDoc)
+{
+}
+
+SwFormat * SwUndoTextFormatCollCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeTextFormatColl(m_sNewName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+void SwUndoTextFormatCollCreate::Delete()
+{
+ m_rDoc.DelTextFormatColl(static_cast<SwTextFormatColl *>(m_pNew), true);
+}
+
+SwFormat * SwUndoTextFormatCollCreate::Find(const OUString & rName) const
+{
+ return m_rDoc.FindTextFormatCollByName(rName);
+}
+
+SwUndoTextFormatCollDelete::SwUndoTextFormatCollDelete(SwTextFormatColl const * _pOld,
+ SwDoc& rDoc)
+ : SwUndoFormatDelete(SwUndoId::TXTFMTCOL_DELETE, _pOld, rDoc)
+{
+}
+
+SwFormat * SwUndoTextFormatCollDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeTextFormatColl(m_sOldName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+void SwUndoTextFormatCollDelete::Delete(SwFormat * pOld)
+{
+ m_rDoc.DelTextFormatColl(static_cast<SwTextFormatColl *>(pOld), true);
+}
+
+SwFormat * SwUndoTextFormatCollDelete::Find(const OUString & rName) const
+{
+ return m_rDoc.FindTextFormatCollByName(rName);
+}
+
+SwUndoCondTextFormatCollCreate::SwUndoCondTextFormatCollCreate(SwConditionTextFormatColl *_pNew,
+ SwTextFormatColl const *_pDerivedFrom, SwDoc& rDoc)
+ : SwUndoTextFormatCollCreate(_pNew, _pDerivedFrom, rDoc)
+{
+}
+
+SwFormat * SwUndoCondTextFormatCollCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeCondTextFormatColl(m_sNewName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+SwUndoCondTextFormatCollDelete::SwUndoCondTextFormatCollDelete(SwTextFormatColl const * _pOld,
+ SwDoc& rDoc)
+ : SwUndoTextFormatCollDelete(_pOld, rDoc)
+{
+}
+
+SwFormat * SwUndoCondTextFormatCollDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeCondTextFormatColl(m_sOldName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+SwUndoRenameFormatColl::SwUndoRenameFormatColl(const OUString & sInitOldName,
+ const OUString & sInitNewName,
+ SwDoc& rDoc)
+ : SwUndoRenameFormat(SwUndoId::TXTFMTCOL_RENAME, sInitOldName, sInitNewName, rDoc)
+{
+}
+
+SwFormat * SwUndoRenameFormatColl::Find(const OUString & rName) const
+{
+ return m_rDoc.FindTextFormatCollByName(rName);
+}
+
+SwUndoCharFormatCreate::SwUndoCharFormatCreate(SwCharFormat * pNewFormat,
+ SwCharFormat const * pDerivedFrom,
+ SwDoc& rDocument)
+ : SwUndoFormatCreate(SwUndoId::CHARFMT_CREATE, pNewFormat, pDerivedFrom, rDocument)
+{
+}
+
+SwFormat * SwUndoCharFormatCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeCharFormat(m_sNewName, static_cast<SwCharFormat *>(pDerivedFrom), true);
+}
+
+void SwUndoCharFormatCreate::Delete()
+{
+ m_rDoc.DelCharFormat(static_cast<SwCharFormat *>(m_pNew), true);
+}
+
+SwFormat * SwUndoCharFormatCreate::Find(const OUString & rName) const
+{
+ return m_rDoc.FindCharFormatByName(rName);
+}
+
+SwUndoCharFormatDelete::SwUndoCharFormatDelete(SwCharFormat const * pOld, SwDoc& rDocument)
+ : SwUndoFormatDelete(SwUndoId::CHARFMT_DELETE, pOld, rDocument)
+{
+}
+
+SwFormat * SwUndoCharFormatDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeCharFormat(m_sOldName, static_cast<SwCharFormat *>(pDerivedFrom), true);
+}
+
+void SwUndoCharFormatDelete::Delete(SwFormat * pFormat)
+{
+ m_rDoc.DelCharFormat(static_cast<SwCharFormat *>(pFormat), true);
+}
+
+SwFormat * SwUndoCharFormatDelete::Find(const OUString & rName) const
+{
+ return m_rDoc.FindCharFormatByName(rName);
+}
+
+SwUndoRenameCharFormat::SwUndoRenameCharFormat(const OUString & sInitOldName,
+ const OUString & sInitNewName,
+ SwDoc& rDocument)
+ : SwUndoRenameFormat(SwUndoId::CHARFMT_RENAME, sInitOldName, sInitNewName, rDocument)
+{
+}
+
+SwFormat * SwUndoRenameCharFormat::Find(const OUString & rName) const
+{
+ return m_rDoc.FindCharFormatByName(rName);
+}
+
+SwUndoFrameFormatCreate::SwUndoFrameFormatCreate(SwFrameFormat * pNewFormat,
+ SwFrameFormat const * pDerivedFrom,
+ SwDoc& rDocument)
+ : SwUndoFormatCreate(SwUndoId::FRMFMT_CREATE, pNewFormat, pDerivedFrom, rDocument)
+{
+}
+
+SwFormat * SwUndoFrameFormatCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeFrameFormat(m_sNewName, static_cast<SwFrameFormat *>(pDerivedFrom), true, m_pNew->IsAuto());
+}
+
+void SwUndoFrameFormatCreate::Delete()
+{
+ m_rDoc.DelFrameFormat(static_cast<SwFrameFormat *>(m_pNew), true);
+}
+
+SwFormat * SwUndoFrameFormatCreate::Find(const OUString & rName) const
+{
+ return m_rDoc.FindFrameFormatByName(rName);
+}
+
+SwUndoFrameFormatDelete::SwUndoFrameFormatDelete(SwFrameFormat const * pOld, SwDoc& rDocument)
+ : SwUndoFormatDelete(SwUndoId::FRMFMT_DELETE, pOld, rDocument)
+{
+}
+
+SwFormat * SwUndoFrameFormatDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeFrameFormat(m_sOldName, static_cast<SwFrameFormat *>(pDerivedFrom), true);
+}
+
+void SwUndoFrameFormatDelete::Delete(SwFormat * pFormat)
+{
+ m_rDoc.DelFrameFormat(static_cast<SwFrameFormat *>(pFormat), true);
+}
+
+SwFormat * SwUndoFrameFormatDelete::Find(const OUString & rName) const
+{
+ return m_rDoc.FindFrameFormatByName(rName);
+}
+
+SwUndoRenameFrameFormat::SwUndoRenameFrameFormat(const OUString & sInitOldName,
+ const OUString & sInitNewName,
+ SwDoc& rDocument)
+ : SwUndoRenameFormat(SwUndoId::FRMFMT_RENAME, sInitOldName, sInitNewName, rDocument)
+{
+}
+
+SwFormat * SwUndoRenameFrameFormat::Find(const OUString & rName) const
+{
+ return m_rDoc.FindFrameFormatByName(rName);
+}
+
+SwUndoNumruleCreate::SwUndoNumruleCreate(const SwNumRule * _pNew,
+ SwDoc& rDoc)
+ : SwUndo(SwUndoId::NUMRULE_CREATE, &rDoc), m_pNew(_pNew), m_aNew(*_pNew), m_rDoc(rDoc),
+ m_bInitialized(false)
+{
+}
+
+void SwUndoNumruleCreate::UndoImpl(::sw::UndoRedoContext &)
+{
+ if (! m_bInitialized)
+ {
+ m_aNew = *m_pNew;
+ m_bInitialized = true;
+ }
+
+ m_rDoc.DelNumRule(m_aNew.GetName(), true);
+}
+
+void SwUndoNumruleCreate::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_rDoc.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& rDoc)
+ : SwUndo(SwUndoId::NUMRULE_DELETE, &rDoc), m_aOld(rRule), m_rDoc(rDoc)
+{
+}
+
+void SwUndoNumruleDelete::UndoImpl(::sw::UndoRedoContext &)
+{
+ m_rDoc.MakeNumRule(m_aOld.GetName(), &m_aOld, true);
+}
+
+void SwUndoNumruleDelete::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_rDoc.DelNumRule(m_aOld.GetName(), true);
+}
+
+SwRewriter SwUndoNumruleDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, m_aOld.GetName());
+
+ return aResult;
+}
+
+SwUndoNumruleRename::SwUndoNumruleRename(OUString _aOldName,
+ OUString _aNewName,
+ SwDoc& rDoc)
+ : SwUndo(SwUndoId::NUMRULE_RENAME, &rDoc), m_aOldName(std::move(_aOldName)), m_aNewName(std::move(_aNewName)),
+ m_rDoc(rDoc)
+{
+}
+
+void SwUndoNumruleRename::UndoImpl(::sw::UndoRedoContext &)
+{
+ m_rDoc.RenameNumRule(m_aNewName, m_aOldName, true);
+}
+
+void SwUndoNumruleRename::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_rDoc.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 0000000000..eec1300d5e
--- /dev/null
+++ b/sw/source/core/undo/SwUndoPageDesc.cxx
@@ -0,0 +1,352 @@
+/* -*- 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 <editsh.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 )
+ return;
+
+ 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 SwFormatHeader* pItem = rDest.GetMaster().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ std::unique_ptr<SwFormatHeader> pNewItem(pItem->Clone());
+ SwFrameFormat* pNewFormat = pNewItem->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.
+ pItem = rSource.GetMaster().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+
+ if( !rDest.IsHeaderShared() )
+ {
+ // Same procedure for unshared header...
+ const SwFormatHeader& rSourceLeftHead = rSource.GetLeft().GetHeader();
+ pItem = rDest.GetLeft().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( rSourceLeftHead.GetHeaderFormat()->GetContent() );
+ pItem = rSource.GetLeft().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+ }
+ if (!rDest.IsFirstShared())
+ {
+ // Same procedure for unshared header...
+ const SwFormatHeader& rSourceFirstMasterHead = rSource.GetFirstMaster().GetHeader();
+ pItem = rDest.GetFirstMaster().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( rSourceFirstMasterHead.GetHeaderFormat()->GetContent() );
+ pItem = rSource.GetFirstMaster().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->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 SwFormatFooter* pItem;
+ pItem = rDest.GetMaster().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ std::unique_ptr<SwFormatFooter> pNewItem(pItem->Clone());
+ SwFrameFormat *pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( rSourceFoot.GetFooterFormat()->GetContent() );
+
+ pItem = rSource.GetMaster().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+
+ if( !rDest.IsFooterShared() )
+ {
+ const SwFormatFooter& rSourceLeftFoot = rSource.GetLeft().GetFooter();
+ pItem = rDest.GetLeft().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( rSourceLeftFoot.GetFooterFormat()->GetContent() );
+ pItem = rSource.GetLeft().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+ }
+ if (rDest.IsFirstShared())
+ return;
+
+ const SwFormatFooter& rSourceFirstMasterFoot = rSource.GetFirstMaster().GetFooter();
+ pItem = rDest.GetFirstMaster().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( rSourceFirstMasterFoot.GetFooterFormat()->GetContent() );
+ pItem = rSource.GetFirstMaster().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+}
+
+void SwUndoPageDesc::ExitHeaderFooterEdit()
+{
+ SwEditShell* pESh = m_pDoc->GetEditShell();
+ if (!pESh)
+ return;
+ if (pESh->IsHeaderFooterEdit())
+ pESh->ToggleHeaderFooterEdit();
+}
+
+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);
+ ExitHeaderFooterEdit();
+}
+
+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);
+ ExitHeaderFooterEdit();
+}
+
+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 0000000000..5223e9993e
--- /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
+{
+ SwNodeOffset GetSectionNodeIndex(SwTOXBaseSection const& rTOX)
+ {
+ const SwSectionNode* pSectNd = rTOX.GetFormat()->GetSectionNode();
+ assert(pSectNd);
+ return pSectNd->GetIndex();
+ }
+}
+
+SwUndoTOXChange::SwUndoTOXChange(const SwDoc& rDoc,
+ SwTOXBaseSection const& rTOX, SwTOXBase const& rNew)
+ : SwUndo(SwUndoId::TOXCHANGE, &rDoc)
+ , 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, SwNodeOffset 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/UndoThemeChange.cxx b/sw/source/core/undo/UndoThemeChange.cxx
new file mode 100644
index 0000000000..8e26106873
--- /dev/null
+++ b/sw/source/core/undo/UndoThemeChange.cxx
@@ -0,0 +1,48 @@
+/* -*- 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/.
+ */
+
+#include <UndoThemeChange.hxx>
+#include <svx/svdpage.hxx>
+#include <docmodel/theme/Theme.hxx>
+#include <doc.hxx>
+#include <drawdoc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+
+#include <memory>
+
+namespace sw
+{
+UndoThemeChange::UndoThemeChange(SwDoc& rDocument,
+ std::shared_ptr<model::ColorSet> const& pOldColorSet,
+ std::shared_ptr<model::ColorSet> const& pNewColorSet)
+ : SwUndo(SwUndoId::CHANGE_THEME, &rDocument)
+ , mrDocument(rDocument)
+ , mpOldColorSet(pOldColorSet)
+ , mpNewColorSet(pNewColorSet)
+{
+}
+
+UndoThemeChange::~UndoThemeChange() {}
+
+void UndoThemeChange::UndoImpl(UndoRedoContext& /*rUndoRedoContext*/)
+{
+ SdrModel* pModel = mrDocument.getIDocumentDrawModelAccess().GetDrawModel();
+ auto pTheme = pModel->getTheme();
+ pTheme->setColorSet(mpOldColorSet);
+}
+
+void UndoThemeChange::RedoImpl(UndoRedoContext& /*rUndoRedoContext*/)
+{
+ SdrModel* pModel = mrDocument.getIDocumentDrawModelAccess().GetDrawModel();
+ auto pTheme = pModel->getTheme();
+ pTheme->setColorSet(mpNewColorSet);
+}
+}
+
+/* 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 0000000000..57202fe363
--- /dev/null
+++ b/sw/source/core/undo/docundo.cxx
@@ -0,0 +1,844 @@
+/* -*- 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 <libxml/xmlwriter.h>
+
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <utility>
+#include <view.hxx>
+#include <drawdoc.hxx>
+#include <ndarr.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <UndoCore.hxx>
+#include <wrtsh.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>
+#include <osl/diagnose.h>
+#include <o3tl/temporary.hxx>
+
+#include <UndoInsert.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> xUndoNodes,
+ IDocumentDrawModelAccess & rDrawModelAccess,
+ IDocumentRedlineAccess & rRedlineAccess,
+ IDocumentState & rState)
+ : m_rDrawModelAccess(rDrawModelAccess)
+ , m_rRedlineAccess(rRedlineAccess)
+ , m_rState(rState)
+ , m_xUndoNodes(std::move(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;
+}
+
+/**
+ * Checks if the topmost undo action owned by pView is independent from the topmost action undo
+ * action.
+ */
+bool UndoManager::IsViewUndoActionIndependent(const SwView* pView, sal_uInt16& rOffset) const
+{
+ if (GetUndoActionCount() <= 1)
+ {
+ // Single or less undo, owned by another view.
+ return false;
+ }
+
+ if (!pView)
+ {
+ return false;
+ }
+
+ // Last undo action that doesn't belong to the view.
+ const SfxUndoAction* pTopAction = GetUndoAction();
+
+ ViewShellId nViewId = pView->GetViewShellId();
+
+ // Earlier undo action that belongs to the view, but is not the top one.
+ const SfxUndoAction* pViewAction = nullptr;
+ size_t nOffset = 0;
+ for (size_t i = 0; i < GetUndoActionCount(); ++i)
+ {
+ const SfxUndoAction* pAction = GetUndoAction(i);
+ if (pAction->GetViewShellId() == nViewId)
+ {
+ pViewAction = pAction;
+ nOffset = i;
+ break;
+ }
+ }
+
+ if (!pViewAction)
+ {
+ // Found no earlier undo action that belongs to the view.
+ return false;
+ }
+
+ auto pTopSwAction = dynamic_cast<const SwUndo*>(pTopAction);
+ if (!pTopSwAction || pTopSwAction->GetId() != SwUndoId::TYPING)
+ {
+ return false;
+ }
+
+ auto pViewSwAction = dynamic_cast<const SwUndo*>(pViewAction);
+ if (!pViewSwAction || pViewSwAction->GetId() != SwUndoId::TYPING)
+ {
+ return false;
+ }
+
+ const auto& rTopInsert = *static_cast<const SwUndoInsert*>(pTopSwAction);
+ const auto& rViewInsert = *static_cast<const SwUndoInsert*>(pViewSwAction);
+
+ for (size_t i = 0; i < GetRedoActionCount(); ++i)
+ {
+ auto pRedoAction = dynamic_cast<const SwUndo*>(GetRedoAction(i));
+ if (!pRedoAction || pRedoAction->GetId() != SwUndoId::TYPING)
+ {
+ return false;
+ }
+
+ const auto& rRedoInsert = *static_cast<const SwUndoInsert*>(pRedoAction);
+ if (!rViewInsert.IsIndependent(rRedoInsert) && rRedoInsert.GetViewShellId() != nViewId)
+ {
+ // Dependent redo action and owned by another view.
+ return false;
+ }
+ }
+
+ if (!rViewInsert.IsIndependent(rTopInsert))
+ {
+ return false;
+ }
+
+ rOffset = nOffset;
+ return true;
+}
+
+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();
+ // Unless we know that the other view's undo action is independent from us.
+ if (pAction->GetViewShellId() != nViewShellId
+ && !IsViewUndoActionIndependent(pView, o3tl::temporary(sal_uInt16())))
+ {
+ 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 < sal_Int32(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, size_t nUndoOffset)
+{
+ SwDoc & rDoc(GetUndoNodes().GetDoc());
+
+ UnoActionContext c(& rDoc); // exception-safe StartAllAction/EndAllAction
+
+ SwView* pViewShell = dynamic_cast<SwView*>(SfxViewShell::Current());
+ SwEditShell *const pEditShell(
+ comphelper::LibreOfficeKit::isActive() && pViewShell ? pViewShell->GetWrtShellPtr()
+ : 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);
+ context.SetUndoOffset(nUndoOffset);
+
+ // 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() { return UndoWithOffset(0); }
+
+bool UndoManager::UndoWithOffset(size_t nUndoOffset)
+{
+ if(isTextEditActive())
+ {
+ return SdrUndoManager::Undo();
+ }
+ else
+ {
+ return impl_DoUndoRedo(UndoOrRedoType::Undo, nUndoOffset);
+ }
+}
+
+bool UndoManager::Redo()
+{
+ if(isTextEditActive())
+ {
+ return SdrUndoManager::Redo();
+ }
+ else
+ {
+ return impl_DoUndoRedo(UndoOrRedoType::Redo, /*nUndoOffset=*/0);
+ }
+}
+
+void UndoManager::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("swUndoManager"));
+ SdrUndoManager::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_xUndoNodes"));
+ m_xUndoNodes->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+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 0000000000..426be22dcc
--- /dev/null
+++ b/sw/source/core/undo/rolbck.cxx
@@ -0,0 +1,1525 @@
+/* -*- 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 <libxml/xmlwriter.h>
+
+#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 <bookmark.hxx>
+#include <frameformats.hxx>
+#include <memory>
+#include <utility>
+
+OUString SwHistoryHint::GetDescription() const
+{
+ return OUString();
+}
+
+void SwHistoryHint::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistoryHint"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), BAD_CAST(typeid(*this).name()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_eWhichId"),
+ BAD_CAST(OString::number(m_eWhichId).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwHistorySetFormat::SwHistorySetFormat( const SfxPoolItem* pFormatHt, SwNodeOffset 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)
+ {
+ auto pCpyTable = const_cast<SwTable*>(&pTableNode->GetTable());
+ pCpyTable->SwitchFormulasToExternalRepresentation();
+ rNew.ChgDefinedIn(rOld.GetDefinedIn());
+ rNew.ToRelBoxNm(pCpyTable);
+ }
+ }
+ }
+ 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::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistorySetFormat"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nNodeIndex"),
+ BAD_CAST(OString::number(sal_Int32(m_nNodeIndex)).getStr()));
+ SwHistoryHint::dumpAsXml(pWriter);
+
+ if (m_pAttr)
+ {
+ m_pAttr->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+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, SwNodeOffset 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, SwNodeOffset 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()->ContainsFormat(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 )
+ return;
+
+ 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, SwNodeOffset nNodePos )
+ : SwHistoryHint( HSTRY_SETTXTFLDHNT )
+ , m_pField( new SwFormatField( *pTextField->GetFormatField().GetField() ) )
+{
+ // only copy if not Sys-FieldType
+ SwDoc& rDoc = 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 ||
+ !rDoc.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, SwNodeOffset 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, SwNodeOffset 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() )
+{
+ static_cast<SvtListener*>(&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, SwNodeOffset 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, SwNodeOffset 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& rDoc = const_cast<SwDoc&>(pTextFootnote->GetTextNode().GetDoc());
+ SwNode* pSaveNd = rDoc.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().DirectPutItemInPool(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, SwNodeOffset 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()) )
+ return;
+
+ 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);
+}
+
+void SwHistoryTextFlyCnt::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistoryTextFlyCnt"));
+ SwHistoryHint::dumpAsXml(pWriter);
+
+ if (m_pUndo)
+ {
+ m_pUndo->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwHistoryBookmark::SwHistoryBookmark(
+ const ::sw::mark::IMark& rBkmk,
+ bool bSavePos,
+ bool bSaveOtherPos)
+ : SwHistoryHint(HSTRY_BOOKMARK)
+ , m_aName(rBkmk.GetName())
+ , m_bHidden(false)
+ , m_nNode(bSavePos ?
+ rBkmk.GetMarkPos().GetNodeIndex() : SwNodeOffset(0))
+ , m_nOtherNode(bSaveOtherPos ?
+ rBkmk.GetOtherMarkPos().GetNodeIndex() : SwNodeOffset(0))
+ , m_nContent(bSavePos ?
+ rBkmk.GetMarkPos().GetContentIndex() : 0)
+ , m_nOtherContent(bSaveOtherPos ?
+ rBkmk.GetOtherMarkPos().GetContentIndex() :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)
+ return;
+
+ m_aKeycode = pBookmark->GetKeyCode();
+ m_aShortName = pBookmark->GetShortName();
+ m_bHidden = pBookmark->IsHidden();
+ m_aHideCondition = pBookmark->GetHideCondition();
+
+ ::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::optional<SwPaM> oPam;
+ ::sw::mark::IMark* pMark = nullptr;
+
+ // now the situation is that m_bSavePos and m_bSaveOtherPos don't determine
+ // whether the mark was deleted
+ if (auto const iter = pMarkAccess->findMark(m_aName); iter != pMarkAccess->getAllMarksEnd())
+ {
+ pMark = *iter;
+ }
+ if(m_bSavePos)
+ {
+ SwContentNode* const pContentNd = rNds[m_nNode]->GetContentNode();
+ assert(pContentNd);
+ oPam.emplace(*pContentNd, m_nContent);
+ }
+ else
+ {
+ assert(pMark);
+ oPam.emplace(pMark->GetMarkPos());
+ }
+ assert(oPam);
+
+ if(m_bSaveOtherPos)
+ {
+ SwContentNode* const pContentNd = rNds[m_nOtherNode]->GetContentNode();
+ assert(pContentNd);
+ oPam->SetMark();
+ oPam->GetMark()->Assign(*pContentNd, m_nOtherContent);
+ }
+ else if(m_bHadOtherPos)
+ {
+ assert(pMark);
+ assert(pMark->IsExpanded());
+ oPam->SetMark();
+ *oPam->GetMark() = pMark->GetOtherMarkPos();
+ }
+
+ if ( pMark != nullptr )
+ {
+ pMarkAccess->deleteMark( pMark );
+ }
+ ::sw::mark::IBookmark* const pBookmark =
+ dynamic_cast<::sw::mark::IBookmark*>(
+ pMarkAccess->makeMark(*oPam, m_aName, m_eBkmkType, sw::mark::InsertMode::New));
+ if ( pBookmark == nullptr )
+ return;
+
+ pBookmark->SetKeyCode(m_aKeycode);
+ pBookmark->SetShortName(m_aShortName);
+ pBookmark->Hide(m_bHidden);
+ pBookmark->SetHideCondition(m_aHideCondition);
+
+ 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().GetNodeIndex()
+ && m_nContent == rBkmk.GetMarkPos().GetContentIndex()
+ && m_aName == rBkmk.GetName();
+}
+
+SwHistoryNoTextFieldmark::SwHistoryNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark)
+ : SwHistoryHint(HSTRY_NOTEXTFIELDMARK)
+ , m_sType(rFieldMark.GetFieldname())
+ , m_nNode(rFieldMark.GetMarkStart().GetNodeIndex())
+ , m_nContent(rFieldMark.GetMarkStart().GetContentIndex())
+{
+}
+
+void SwHistoryNoTextFieldmark::SetInDoc(SwDoc* pDoc, bool)
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodes& rNds = pDoc->GetNodes();
+ std::optional<SwPaM> pPam;
+
+ const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode();
+ if(pContentNd)
+ pPam.emplace(*pContentNd, m_nContent);
+
+ if (pPam)
+ {
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+ pMarkAccess->makeNoTextFieldBookmark(*pPam, OUString(), m_sType);
+ }
+}
+
+void SwHistoryNoTextFieldmark::ResetInDoc(SwDoc& rDoc)
+{
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwNodes& rNds = rDoc.GetNodes();
+ std::optional<SwPaM> pPam;
+
+ const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode();
+ assert(pContentNd);
+ pPam.emplace(*pContentNd, m_nContent);
+
+ if (pPam)
+ {
+ IDocumentMarkAccess* pMarkAccess = rDoc.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().GetNodeIndex())
+ , m_nStartContent(rFieldMark.GetMarkStart().GetContentIndex())
+ , m_nEndNode(rFieldMark.GetMarkEnd().GetNodeIndex())
+ , m_nEndContent(rFieldMark.GetMarkEnd().GetContentIndex())
+{
+ SwPosition const sepPos(sw::mark::FindFieldSep(rFieldMark));
+ m_nSepNode = sepPos.GetNodeIndex();
+ m_nSepContent = sepPos.GetContentIndex();
+}
+
+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& rDoc)
+{
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwNodes& rNds = rDoc.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(*rDoc.getIDocumentMarkAccess());
+ rMarksAccess.deleteFieldmarkAt(pos);
+}
+
+SwHistorySetAttrSet::SwHistorySetAttrSet( const SfxItemSet& rSet,
+ SwNodeOffset nNodePos, const o3tl::sorted_vector<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)
+ {
+ auto pCpyTable = const_cast<SwTable*>(&pTableNode->GetTable());
+ pCpyTable->SwitchFormulasToExternalRepresentation();
+ rNew.ChgDefinedIn(rOld.GetDefinedIn());
+ rNew.PtrToBoxNm(pCpyTable);
+ }
+ }
+ }
+ 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(sw::SpzFrameFormat& rFormat)
+ : SwHistoryHint( HSTRY_CHGFLYANCHOR )
+ , m_rFormat(rFormat)
+ , m_nOldNodeIndex( rFormat.GetAnchor().GetAnchorNode()->GetIndex() )
+ , m_nOldContentIndex( (RndStdIds::FLY_AT_CHAR == rFormat.GetAnchor().GetAnchorId())
+ ? rFormat.GetAnchor().GetAnchorContentOffset()
+ : COMPLETE_STRING )
+{
+}
+
+void SwHistoryChangeFlyAnchor::SetInDoc( SwDoc* pDoc, bool )
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ // One would expect m_rFormat to only contain FlyFormats, given the name of
+ // this class, but apparently it is also used for DrawFormats.
+ if (!pDoc->GetSpzFrameFormats()->IsAlive(&m_rFormat)) // Format does still exist
+ return;
+
+ SwFormatAnchor aTmp( m_rFormat.GetAnchor() );
+
+ SwNode* pNd = pDoc->GetNodes()[ m_nOldNodeIndex ];
+ SwContentNode* pCNd = pNd->GetContentNode();
+ SwPosition aPos( *pNd );
+ if ( COMPLETE_STRING != m_nOldContentIndex )
+ aPos.SetContent( m_nOldContentIndex );
+ aTmp.SetAnchor( &aPos );
+
+ // so the Layout does not get confused
+ if (!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))
+ return;
+
+ 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(SfxItemSet aSet,
+ OUString sFormat)
+ : SwHistoryHint(HSTRY_CHGCHARFMT)
+ , m_OldSet(std::move(aSet)), m_Format(std::move(sFormat))
+{
+}
+
+void SwHistoryChangeCharFormat::SetInDoc(SwDoc * pDoc, bool )
+{
+ SwCharFormat * pCharFormat = pDoc->FindCharFormatByName(m_Format);
+
+ if (pCharFormat)
+ {
+ pCharFormat->SetFormatAttr(m_OldSet);
+ }
+}
+// <- #i27615#
+
+SwHistory::SwHistory()
+ : m_nEndDiff( 0 )
+{
+}
+
+SwHistory::~SwHistory()
+{
+}
+
+void SwHistory::AddPoolItem(
+ const SfxPoolItem* pOldValue,
+ const SfxPoolItem* pNewValue,
+ SwNodeOffset 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::AddTextAttr(SwTextAttr *const pHint,
+ SwNodeOffset const nNodeIdx, bool const 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::AddColl(SwFormatColl *const pColl, SwNodeOffset const nNodeIdx,
+ SwNodeType const 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::AddIMark(const ::sw::mark::IMark& rBkmk,
+ bool const bSavePos, bool const 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(sw::SpzFrameFormat& 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) );
+
+ if( const SwFormatChain* pChainItem = rFormat.GetItemIfSet( RES_CHAIN, false ) )
+ {
+ 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::AddFootnote(const SwTextFootnote& rFootnote)
+{
+ std::unique_ptr<SwHistoryHint> pHt(new SwHistorySetFootnote( rFootnote ));
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+// #i27615#
+void SwHistory::AddCharFormat(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,
+ SwNodeOffset nNodeIdx)
+{
+ if(!rSet.Count())
+ return;
+
+ SfxItemIter aIter(rSet);
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ if(!IsInvalidItem(pItem))
+ {
+ AddPoolItem(pItem, pItem, nNodeIdx);
+ }
+
+ pItem = aIter.NextItem();
+
+ } while(pItem);
+}
+
+void SwHistory::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistory"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_SwpHstry"));
+ for (const auto& pHistory : m_SwpHstry)
+ {
+ pHistory->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwHistory::CopyAttr(
+ SwpHints const * pHts,
+ const SwNodeOffset 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 )
+ {
+ AddTextAttr(pHt, nNodeIdx, false);
+ }
+ }
+ else if ( pEndIdx && nStart < *pEndIdx )
+ {
+ AddTextAttr(pHt, nNodeIdx, false);
+ }
+ }
+}
+
+// Class to register the history at a Node, Format, HintsArray, ...
+SwRegHistory::SwRegHistory( SwHistory* pHst )
+ : SwClient( nullptr )
+ , m_pHistory( pHst )
+ , m_nNodeIndex( NODE_OFFSET_MAX )
+{
+ MakeSetWhichIds();
+}
+
+SwRegHistory::SwRegHistory( sw::BroadcastingModify* 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::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if ( !(m_pHistory && pLegacyHint->m_pNew && !areSfxPoolItemPtrsEqual(pLegacyHint->m_pOld, pLegacyHint->m_pNew) ) )
+ return;
+
+ if ( pLegacyHint->m_pNew->Which() < POOLATTR_END )
+ {
+ if(RES_UPDATE_ATTR == pLegacyHint->m_pNew->Which())
+ {
+ m_pHistory->AddPoolItem(pLegacyHint->m_pOld, pLegacyHint->m_pNew, m_nNodeIndex);
+ }
+ else
+ {
+ OSL_ENSURE(false, "Unexpected update attribute (!)");
+ }
+ }
+ else if (pLegacyHint->m_pOld && RES_ATTRSET_CHG == pLegacyHint->m_pNew->Which())
+ {
+ std::unique_ptr<SwHistoryHint> pNewHstr;
+ const SfxItemSet& rSet = *static_cast< const SwAttrSetChg* >(pLegacyHint->m_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->AddTextAttr(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( sw::BroadcastingModify* 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() )
+ return;
+
+ const SfxItemSet* pSet = nullptr;
+ if( auto pContentNode = dynamic_cast< const SwContentNode *>( GetRegisteredIn() ) )
+ {
+ pSet = pContentNode->GetpSwAttrSet();
+ }
+ else if ( auto pSwFormat = dynamic_cast< const SwFormat *>( GetRegisteredIn() ) )
+ {
+ pSet = &pSwFormat->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 0000000000..7e694e21b7
--- /dev/null
+++ b/sw/source/core/undo/unattr.cxx
@@ -0,0 +1,1155 @@
+/* -*- 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 <osl/diagnose.h>
+#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 <IDocumentState.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <docary.hxx>
+#include <swcrsr.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>
+#include <editsh.hxx>
+
+SwUndoFormatAttrHelper::SwUndoFormatAttrHelper(SwFormat& rFormat, bool bSvDrwPt)
+ : SwClient(&rFormat)
+ , m_rFormat(rFormat)
+ , m_bSaveDrawPt(bSvDrwPt)
+{
+}
+
+void SwUndoFormatAttrHelper::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(!pLegacy->m_pOld)
+ return;
+ assert(pLegacy->m_pOld->Which() != RES_OBJECTDYING);
+ if(!pLegacy->m_pNew)
+ return;
+ const SwDoc& rDoc = *m_rFormat.GetDoc();
+ auto pOld = pLegacy->m_pOld;
+ if(POOLATTR_END >= pLegacy->m_pOld->Which()) {
+ if(!GetUndo())
+ m_pUndo.reset(new SwUndoFormatAttr(*pOld, m_rFormat, m_bSaveDrawPt));
+ else
+ m_pUndo->PutAttr(*pOld, rDoc);
+ } else if(RES_ATTRSET_CHG == pOld->Which()) {
+ auto& rChgSet = *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet();
+ if(!GetUndo())
+ m_pUndo.reset(new SwUndoFormatAttr(SfxItemSet(rChgSet), m_rFormat, m_bSaveDrawPt));
+ else {
+ SfxItemIter aIter(rChgSet);
+ for(auto pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ m_pUndo->PutAttr(*pItem, rDoc);
+ }
+ }
+}
+
+SwDocModifyAndUndoGuard::SwDocModifyAndUndoGuard(SwFormat& format)
+ : doc(format.GetName().isEmpty() ? nullptr : format.GetDoc())
+ , helper(doc ? new SwUndoFormatAttrHelper(format) : nullptr)
+{
+}
+
+SwDocModifyAndUndoGuard::~SwDocModifyAndUndoGuard()
+{
+ if (helper && helper->GetUndo())
+ {
+ // helper tracks changes, even when DoesUndo is false, to detect modified state
+ if (doc->GetIDocumentUndoRedo().DoesUndo())
+ doc->GetIDocumentUndoRedo().AppendUndo(helper->ReleaseUndo());
+
+ doc->getIDocumentState().SetModified();
+ }
+}
+
+SwUndoFormatAttr::SwUndoFormatAttr( SfxItemSet&& rOldSet,
+ SwFormat& rChgFormat,
+ bool bSaveDrawPt )
+ : SwUndo( SwUndoId::INSFMTATTR, rChgFormat.GetDoc() )
+ , m_sFormatName ( rChgFormat.GetName() )
+ // #i56253#
+ , m_oOldSet( std::move( rOldSet ) )
+ , m_nAnchorContentOffset( 0 )
+ , m_nNodeIndex( 0 )
+ , m_nFormatWhich( rChgFormat.Which() )
+ , m_bSaveDrawPt( bSaveDrawPt )
+{
+ assert(m_sFormatName.getLength());
+
+ Init( rChgFormat );
+}
+
+SwUndoFormatAttr::SwUndoFormatAttr( const SfxPoolItem& rItem, SwFormat& rChgFormat,
+ bool bSaveDrawPt )
+ : SwUndo( SwUndoId::INSFMTATTR, rChgFormat.GetDoc() )
+ , m_sFormatName(rChgFormat.GetName())
+ , m_oOldSet( rChgFormat.GetAttrSet().CloneAsValue( false ) )
+ , m_nAnchorContentOffset( 0 )
+ , m_nNodeIndex( 0 )
+ , m_nFormatWhich( rChgFormat.Which() )
+ , m_bSaveDrawPt( bSaveDrawPt )
+{
+ assert(m_sFormatName.getLength());
+
+ m_oOldSet->Put( rItem );
+ Init( rChgFormat );
+}
+
+void SwUndoFormatAttr::Init( const SwFormat & rFormat )
+{
+ // tdf#126017 never save SwNodeIndex, it will go stale
+ m_oOldSet->ClearItem(RES_CNTNT);
+ // treat change of anchor specially
+ if ( SfxItemState::SET == m_oOldSet->GetItemState( RES_ANCHOR, false )) {
+ SaveFlyAnchor( &rFormat, m_bSaveDrawPt );
+ } else if ( RES_FRMFMT == m_nFormatWhich ) {
+ const SwDoc* pDoc = rFormat.GetDoc();
+ auto pTableFormat = dynamic_cast<const SwTableFormat*>(&rFormat);
+ if (pTableFormat && pDoc->GetTableFrameFormats()->ContainsFormat(const_cast<SwTableFormat*>(pTableFormat)))
+ {
+ // 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 (dynamic_cast<const SwSectionFormat*>(&rFormat)) {
+ if (auto pContentIndex = rFormat.GetContent().GetContentIdx())
+ m_nNodeIndex = pContentIndex->GetIndex();
+ } else if(auto pBoxFormat = dynamic_cast<const SwTableBoxFormat*>(&rFormat))
+ {
+ auto pTableBox = pBoxFormat->GetTableBox();
+ 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_oOldSet)
+ 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_oOldSet->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_oOldSet->ClearItem( RES_ANCHOR );
+ }
+ }
+
+ if ( bAnchorAttrRestored ) return;
+
+ SwUndoFormatAttrHelper aTmp( *pFormat, m_bSaveDrawPt );
+ pFormat->SetFormatAttr( *m_oOldSet );
+ if ( aTmp.GetUndo() ) {
+ // transfer ownership of helper object's old set
+ if (aTmp.GetUndo()->m_oOldSet)
+ m_oOldSet.emplace(std::move(*aTmp.GetUndo()->m_oOldSet));
+ else
+ m_oOldSet.reset();
+ } else {
+ m_oOldSet->ClearItem();
+ }
+
+ if ( RES_FLYFRMFMT == m_nFormatWhich || RES_DRAWFRMFMT == m_nFormatWhich ) {
+ rContext.SetSelections(static_cast<SwFrameFormat*>(pFormat), nullptr);
+ }
+
+ SfxStyleFamily nFamily = SfxStyleFamily::None;
+ if (RES_TXTFMTCOLL == m_nFormatWhich || RES_CONDTXTFMTCOLL == m_nFormatWhich)
+ nFamily = SfxStyleFamily::Para;
+ else if (RES_CHRFMT == m_nFormatWhich)
+ nFamily = SfxStyleFamily::Char;
+
+ if (m_oOldSet && m_oOldSet->Count() > 0 && nFamily != SfxStyleFamily::None)
+ rContext.GetDoc().BroadcastStyleOperation(pFormat->GetName(), nFamily, SfxHintId::StyleSheetModified);
+}
+
+// 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 rDoc.GetGrfFormatColls()->FindFormatByName(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:
+ {
+ auto it = rDoc.GetSpzFrameFormats()->findByTypeAndName( m_nFormatWhich, m_sFormatName );
+ if( it != rDoc.GetSpzFrameFormats()->typeAndNameEnd() )
+ return *it;
+ SwFormat* pFormat = rDoc.GetFrameFormats()->FindFormatByName(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_oOldSet)
+ return;
+
+ SwDoc & rDoc(rContext.GetDoc());
+
+ SwFormat * pFormat = GetFormat(rDoc);
+ if (!pFormat)
+ return;
+
+ switch ( m_nFormatWhich ) {
+ case RES_GRFFMTCOLL: {
+ SwNoTextNode *const pNd =
+ rContext.GetRepeatPaM().GetPointNode().GetNoTextNode();
+ if( pNd ) {
+ rDoc.SetAttr( pFormat->GetAttrSet(), *pNd->GetFormatColl() );
+ }
+ }
+ break;
+
+ case RES_TXTFMTCOLL:
+ case RES_CONDTXTFMTCOLL:
+ {
+ SwTextNode *const pNd =
+ rContext.GetRepeatPaM().GetPointNode().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().GetPointNode().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_oOldSet->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_oOldSet->Put( SwFormatFrameSize( SwFrameSize::Variable, aPt.X(), aPt.Y() ) );
+ }
+ }
+
+ const SwFormatAnchor& rAnchor =
+ m_oOldSet->Get( RES_ANCHOR, false );
+ if( !rAnchor.GetAnchorNode() || rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ return;
+
+ switch( rAnchor.GetAnchorId() ) {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ m_nAnchorContentOffset = rAnchor.GetAnchorContentOffset();
+ [[fallthrough]];
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ m_nNodeIndex = rAnchor.GetAnchorNode()->GetIndex();
+ break;
+ default:
+ assert(false);
+ }
+
+ SwFormatAnchor aAnchor( rAnchor.GetAnchorId(), 0 );
+ m_oOldSet->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_oOldSet->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.SetContent( m_nAnchorContentOffset );
+ if ( aPos.GetContentIndex() > 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_oOldSet->Get( RES_FRM_SIZE );
+ aDrawSavePt.setX( rOldSize.GetWidth() );
+ aDrawSavePt.setY( rOldSize.GetHeight() );
+ m_oOldSet->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.
+ SwTextNode *pTextNode = static_cast<SwTextNode*>(rOldAnch.GetAnchorNode());
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = rOldAnch.GetAnchorContentOffset();
+ 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_oOldSet->Put( aNewAnchor );
+ SwUndoFormatAttrHelper aTmp( *pFrameFormat, m_bSaveDrawPt );
+ pFrameFormat->SetFormatAttr( *m_oOldSet );
+ if ( aTmp.GetUndo() ) {
+ m_nNodeIndex = aTmp.GetUndo()->m_nNodeIndex;
+ // transfer ownership of helper object's old set
+ if (aTmp.GetUndo()->m_oOldSet)
+ m_oOldSet.emplace(std::move(*aTmp.GetUndo()->m_oOldSet));
+ else
+ m_oOldSet.reset();
+ } else {
+ m_oOldSet->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.
+ const SwFormatVertOrient& rVertOrient = pFrameFormat->GetVertOrient();
+ const SwFormatHoriOrient& rHoriOrient = pFrameFormat->GetHoriOrient();
+ Point aFormatPos(rHoriOrient.GetPos(), rVertOrient.GetPos());
+ if (aDrawSavePt != aFormatPos)
+ {
+ // If the position would be the same, then skip the call: either it would do nothing or
+ // it would just go wrong.
+ pFrameFormat->CallSwClientNotify(sw::RestoreFlyAnchorHint(aDrawSavePt));
+ }
+
+ // cache the old value again
+ m_oOldSet->Put(SwFormatFrameSize(SwFrameSize::Variable, aDrawOldPt.X(), aDrawOldPt.Y()));
+ }
+
+ if (RndStdIds::FLY_AS_CHAR == aNewAnchor.GetAnchorId()) {
+ SwTextNode* pTextNd = aNewAnchor.GetAnchorNode()->GetTextNode();
+ OSL_ENSURE( pTextNd, "no Text Node at position." );
+ SwFormatFlyCnt aFormat( pFrameFormat );
+ pTextNd->InsertItem( aFormat, aNewAnchor.GetAnchorContentOffset(), 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 std::vector<sal_uInt16>& rIds )
+ : SwUndo( SwUndoId::RESETATTR, rChangedFormat.GetDoc() )
+ , m_pChangedFormat( &rChangedFormat )
+ , m_aSet(*rChangedFormat.GetAttrSet().GetPool())
+{
+ for (const auto& nWhichId : rIds)
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if (rChangedFormat.GetItemState(nWhichId, false, &pItem ) == SfxItemState::SET && pItem)
+ m_aSet.Put(*pItem);
+ }
+}
+
+SwUndoFormatResetAttr::~SwUndoFormatResetAttr()
+{
+}
+
+void SwUndoFormatResetAttr::UndoImpl(::sw::UndoRedoContext &)
+{
+ m_pChangedFormat->SetFormatAttr(m_aSet);
+ BroadcastStyleChange();
+}
+
+void SwUndoFormatResetAttr::RedoImpl(::sw::UndoRedoContext &)
+{
+ SfxItemIter aIter(m_aSet);
+ for (auto pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ m_pChangedFormat->ResetFormatAttr(pItem->Which());
+ BroadcastStyleChange();
+}
+
+void SwUndoFormatResetAttr::BroadcastStyleChange()
+{
+ auto nWhich = m_pChangedFormat->Which();
+ SfxStyleFamily nFamily = SfxStyleFamily::None;
+
+ if (RES_TXTFMTCOLL == nWhich || RES_CONDTXTFMTCOLL == nWhich)
+ nFamily = SfxStyleFamily::Para;
+ else if (RES_CHRFMT == nWhich)
+ nFamily = SfxStyleFamily::Char;
+
+ if (nFamily != SfxStyleFamily::None)
+ m_pChangedFormat->GetDoc()->BroadcastStyleOperation(m_pChangedFormat->GetName(), nFamily, SfxHintId::StyleSheetModified);
+}
+
+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.GetNodeIndex();
+ m_nSttContent = m_nEndContent = rPos.GetContentIndex();
+}
+
+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 )
+ pTNd->DontExpandFormat( m_nSttContent, false );
+ }
+ else if (m_nFormatId == RES_TXTATR_REFMARK)
+ {
+ rDoc.GetEditShell()->SwViewShell::UpdateFields();
+ }
+
+ 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, 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;
+ case RES_TXTATR_REFMARK:
+ {
+ const registeredSfxPoolItems& aRange(rDoc.GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK));
+ SwHistoryHint* pHistoryHint = GetHistory()[0];
+ if (pHistoryHint && HSTRY_SETREFMARKHNT == pHistoryHint->Which())
+ {
+ for (const SfxPoolItem* pItem : aRange)
+ {
+ assert(dynamic_cast<const SwFormatRefMark*>(pItem));
+ const auto pFormatRefMark = static_cast<const SwFormatRefMark*>(pItem);
+ if (static_cast<SwHistorySetRefMark*>(pHistoryHint)->GetRefName() ==
+ pFormatRefMark->GetRefName())
+ {
+ rDoc.DeleteFormatRefMark(pFormatRefMark);
+ rDoc.GetEditShell()->SwViewShell::UpdateFields();
+ break;
+ }
+ }
+ }
+ }
+ 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( o3tl::sorted_vector<sal_uInt16> && rAttrs )
+{
+ m_Ids = std::move(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( NODE_OFFSET_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, SfxItemSet aSet,
+ const SetAttrMode nFlags )
+ : SwUndo( SwUndoId::INSATTR, &rRange.GetDoc() ), SwUndRng( rRange )
+ , m_AttrSet(std::move( aSet ))
+ , m_pHistory( new SwHistory )
+ , m_nNodeIndex( NODE_OFFSET_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& rDoc = rPam.GetDoc();
+ if ( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) {
+ m_pRedlineData.reset( new SwRedlineData( bIsContent
+ ? RedlineType::Insert
+ : RedlineType::Format,
+ rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ }
+
+ m_pRedlineSaveData.reset( new SwRedlineSaveDatas );
+ if ( !FillSaveDataForFormat( rPam, *m_pRedlineSaveData ))
+ m_pRedlineSaveData.reset();
+
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ if ( bIsContent ) {
+ m_nNodeIndex = rPam.GetPoint()->GetNodeIndex();
+ }
+}
+
+void SwUndoAttr::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ RemoveIdx( *pDoc );
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ) {
+ SwPaM aPam(pDoc->GetNodes().GetEndOfContent());
+ if ( NODE_OFFSET_MAX != m_nNodeIndex ) {
+ aPam.DeleteMark();
+ aPam.GetPoint()->Assign( m_nNodeIndex, m_nSttContent );
+ aPam.SetMark();
+ aPam.GetPoint()->AdjustContent(+1);
+ 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()[0].first)
+ && (m_AttrSet.GetRanges()[0].first <= RES_TXTATR_ANNOTATION);
+
+ // restore old values
+ m_pHistory->TmpRollback( pDoc, 0, !bToLast );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ // set cursor onto Undo area
+ if (!(m_nInsertFlags & SetAttrMode::NO_CURSOR_CHANGE))
+ 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::redoAttribute(SwPaM& rPam, sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ // 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 ( NODE_OFFSET_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::RedoImpl(sw::UndoRedoContext & rContext)
+{
+ if (m_nInsertFlags & SetAttrMode::NO_CURSOR_CHANGE)
+ {
+ SwPaM aPam(rContext.GetDoc().GetNodes().GetEndOfContent());
+ SetPaM(aPam, false);
+ redoAttribute(aPam, rContext);
+ }
+ else
+ {
+ SwPaM& rPam = AddUndoRedoPaM(rContext);
+ redoAttribute(rPam, rContext);
+ }
+}
+
+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;
+ SwNodeOffset 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& rDoc )
+ : SwUndo( SwUndoId::SETDEFTATTR, &rDoc )
+{
+ const SvxTabStopItem* pItem = rSet.GetItemIfSet( RES_PARATR_TABSTOP, false );
+ if( pItem )
+ {
+ // store separately, because it may change!
+ m_pTabStop.reset(pItem->Clone());
+ if ( 1 != rSet.Count() ) { // are there more attributes?
+ m_oOldSet.emplace( rSet );
+ }
+ } else {
+ m_oOldSet.emplace( rSet );
+ }
+}
+
+SwUndoDefaultAttr::~SwUndoDefaultAttr()
+{
+}
+
+void SwUndoDefaultAttr::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if (m_oOldSet)
+ {
+ SwUndoFormatAttrHelper aTmp(
+ *rDoc.GetDfltTextFormatColl() );
+ rDoc.SetDefault( *m_oOldSet );
+ m_oOldSet.reset();
+ if ( aTmp.GetUndo() ) {
+ // transfer ownership of helper object's old set
+ if (aTmp.GetUndo()->m_oOldSet)
+ m_oOldSet.emplace(std::move(*aTmp.GetUndo()->m_oOldSet));
+ }
+ }
+ 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, OUString aText,
+ bool const bIsEndNote)
+ : SwUndo( SwUndoId::CHGFTN, &rRange.GetDoc() ), SwUndRng( rRange )
+ , m_pHistory( new SwHistory() )
+ , m_Text(std::move( aText ))
+ , 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& rDoc )
+ : SwUndo( SwUndoId::FTNINFO, &rDoc )
+ , 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& rDoc )
+ : SwUndo( SwUndoId::FTNINFO, &rDoc )
+ , 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.GetNodeIndex() )
+ , m_nContentIndex( rPos.GetContentIndex() )
+{
+}
+
+void SwUndoDontExpandFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwCursor *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ SwPosition& rPos = *pPam->GetPoint();
+ rPos.Assign( m_nNodeIndex, 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.Assign( m_nNodeIndex, 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 0000000000..8288453307
--- /dev/null
+++ b/sw/source/core/undo/unbkmk.cxx
@@ -0,0 +1,223 @@
+/* -*- 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 <string_view>
+
+#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>
+#include <utility>
+
+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& rDoc )
+{
+ IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
+ for ( IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getAllMarksBegin();
+ ppBkmk != pMarkAccess->getAllMarksEnd();
+ ++ppBkmk )
+ {
+ if ( m_pHistoryBookmark->IsEqualBookmark( **ppBkmk ) )
+ {
+ pMarkAccess->deleteMark(ppBkmk, false);
+ 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( OUString aOldName, OUString aNewName, const SwDoc& rDoc )
+ : SwUndo( SwUndoId::BOOKMARK_RENAME, &rDoc )
+ , m_sOldName(std::move( aOldName ))
+ , m_sNewName(std::move( aNewName ))
+{
+}
+
+SwUndoRenameBookmark::~SwUndoRenameBookmark()
+{
+}
+
+static OUString lcl_QuoteName(std::u16string_view 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 0000000000..4c199c8ac3
--- /dev/null
+++ b/sw/source/core/undo/undel.cxx
@@ -0,0 +1,1361 @@
+/* -*- 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 <libxml/xmlwriter.h>
+#include <editeng/formatbreakitem.hxx>
+
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#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 <fmtpdsc.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 sw::FrameFormats<sw::SpzFrameFormat*>& rSpzs, SwNodeOffset nMovedIndex )
+{
+ for(auto pSpz: rSpzs)
+ {
+ const SwFormatAnchor* pAnchor = &pSpz->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ const SwNode* pAnchorNode = pAnchor->GetAnchorNode();
+ if( pAnchorNode && nMovedIndex == pAnchorNode->GetIndex() )
+ pSpz->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);
+ SwNodeOffset nEndOfRedline(0);
+ for (size_t i = 0; i < rRedlineSaveData.size(); ++i)
+ {
+ auto const& rRedline(rRedlineSaveData[i]);
+ if (rRedline.m_nSttNode <= rRange.m_nSttNode
+ // coverity[copy_paste_error : FALSE] : m_nEndNode is intentional here
+ && 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 (SwNodeOffset 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)
+ return;
+
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pFirstMergedDeletedTextNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ 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,
+ SwDeleteFlags const flags,
+ 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_DeleteFlags(flags)
+{
+ assert(!m_bDelFullPara || !(m_DeleteFlags & SwDeleteFlags::ArtificialSelection));
+
+ m_bCacheComment = false;
+
+ SwDoc& rDoc = rPam.GetDoc();
+
+ if( !rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc.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
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+
+ // 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(rDoc.GetIDocumentUndoRedo());
+ DelBookmarks(pStt->GetNode(), pEnd->GetNode());
+ }
+ else
+ {
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType::AllMask
+ | ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) ? DelContentType::Replace : DelContentType(0)));
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ if (m_nEndNode - m_nSttNode > SwNodeOffset(1)) // check for fully selected nodes
+ {
+ SwNodeIndex const start(pStt->GetNode(), +1);
+ DelBookmarks(start.GetNode(), pEnd->GetNode());
+ }
+ }
+
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+
+ // Is already anything deleted?
+ m_nNdDiff = m_nSttNode - pStt->GetNodeIndex();
+
+ m_bJoinNext = !bFullPara && pEnd == rPam.GetPoint();
+ m_bBackSp = !bFullPara && !m_bJoinNext;
+
+ SwTextNode *pSttTextNd = nullptr, *pEndTextNd = nullptr;
+ if( !bFullPara )
+ {
+ pSttTextNd = pStt->GetNode().GetTextNode();
+ pEndTextNd = m_nSttNode == m_nEndNode
+ ? pSttTextNd
+ : pEnd->GetNode().GetTextNode();
+ }
+ else if (m_pRedlSaveData)
+ {
+ DelFullParaMoveFrames(rDoc, *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->AddColl(pSttTextNd->GetTextColl(), pStt->GetNodeIndex(), SwNodeType::Text);
+ m_pHistory->AddColl(pEndTextNd->GetTextColl(), pEnd->GetNodeIndex(), 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()->Adjust(SwNodeOffset(-1));
+ 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 = rDoc.GetUndoManager().GetUndoNodes();
+ SwNodes& rDocNds = rDoc.GetNodes();
+ SwNodeRange aRg( rDocNds, m_nSttNode - m_nNdDiff, m_nEndNode - m_nNdDiff );
+ if( !bFullPara && !pEndTextNd &&
+ aRg.aEnd.GetNode() != rDoc.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, SwNodeOffset(0), *pEndTextNd, SwNodeOffset(1) );
+ SwPosition aSplitPos( *pEndTextNd );
+ ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().SplitNode( aSplitPos, false );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd.GetNode() );
+ --aRg.aEnd;
+ }
+ else
+ m_nReplaceDummy = SwNodeOffset(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( SwNodeOffset(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, SwNodeOffset(0), *pSttTextNd, SwNodeOffset(1) );
+ SwPosition aSplitPos( *pSttTextNd );
+ ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().SplitNode( aSplitPos, false );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart.GetNode() );
+ --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, rNds.GetEndOfContent() );
+ m_oMvStt.emplace( 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, SwNodeOffset(0), *pEndTextNd, SwNodeOffset(1) );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart.GetNode() );
+ }
+ else
+ {
+ SwNodeRange aMvRg( *pSttTextNd, SwNodeOffset(0), *pSttTextNd, SwNodeOffset(1) );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd.GetNode() );
+ }
+ }
+ }
+ if( m_nSectDiff || m_nReplaceDummy )
+ lcl_MakeAutoFrames( *rDoc.GetSpzFrameFormats(),
+ m_bJoinNext ? pEndTextNd->GetIndex() : pSttTextNd->GetIndex() );
+ }
+ else
+ m_nNode = SwNodeOffset(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()->GetNodeIndex() - (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()->GetNodeIndex();
+ }
+
+ // 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 )
+{
+ SwNodeOffset nNdIdx = pStt->GetNodeIndex();
+ // 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->GetContentIndex()
+ : pSttTextNd->GetText().getLength())
+ - pStt->GetContentIndex();
+
+ // delete now also the text (all attribute changes are added to
+ // UNDO history)
+ m_aSttStr = pSttTextNd->GetText().copy(m_nSttContent, nLen);
+ pSttTextNd->EraseText( *pStt, 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 )
+ {
+ SwContentIndex aEndIdx( pEndTextNd );
+ nNdIdx = pEnd->GetNodeIndex();
+ 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->GetContentIndex() );
+ pEndTextNd->EraseText( aEndIdx, pEnd->GetContentIndex() );
+ 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& rDoc, 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;
+
+ auto [pStt, pEnd] = rDelPam.StartEnd(); // SwPosition*
+
+ if( pStt->GetNode() != pEnd->GetNode() ||
+ pStt->GetContentIndex()+1 != pEnd->GetContentIndex() ||
+ pEnd->GetNodeIndex() != m_nSttNode )
+ return false;
+
+ // Distinguish between BackSpace and Delete because the Undo array needs to
+ // be constructed differently!
+ if( pEnd->GetContentIndex() == m_nSttContent )
+ {
+ if( m_bGroup && !m_bBackSp ) return false;
+ m_bBackSp = true;
+ }
+ // note: compare m_nSttContent here because the text isn't there any more!
+ else if( pStt->GetContentIndex() == 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->GetNode().GetTextNode();
+ if( !pDelTextNd ) return false;
+
+ sal_Int32 nUChrPos = m_bBackSp ? 0 : m_aSttStr->getLength()-1;
+ sal_Unicode cDelChar = pDelTextNd->GetText()[ pStt->GetContentIndex() ];
+ 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(rDoc, *pStt, *pEnd))
+ {
+ return false;
+ }
+
+ if ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) && m_pHistory)
+ {
+ IDocumentMarkAccess const& rIDMA(*rDoc.getIDocumentMarkAccess());
+ for (auto i = m_pHistory->Count(); 0 < i; )
+ {
+ --i;
+ SwHistoryHint const*const pHistory((*m_pHistory)[i]);
+ if (pHistory->Which() == HSTRY_BOOKMARK)
+ {
+ SwHistoryBookmark const*const pHistoryBM(
+ static_cast<SwHistoryBookmark const*>(pHistory));
+ auto const ppMark(rIDMA.findMark(pHistoryBM->GetName()));
+ if (ppMark != rIDMA.getAllMarksEnd()
+ && (m_bBackSp
+ ? ((**ppMark).GetMarkPos() == *pStt)
+ : ((**ppMark).IsExpanded()
+ && (**ppMark).GetOtherMarkPos() == *pEnd)))
+ { // prevent grouping that would delete this mark on Redo()
+ 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;
+
+ rDoc.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, rtl::OUStringChar(cDelChar) );
+ pDelTextNd->EraseText( *pStt, 1 );
+
+ m_bGroup = true;
+ return true;
+}
+
+SwUndoDelete::~SwUndoDelete()
+{
+ if( m_oMvStt ) // Delete also the selection from UndoNodes array
+ {
+ // Insert saves content in IconSection
+ m_oMvStt->GetNode().GetNodes().Delete( *m_oMvStt, m_nNode );
+ m_oMvStt.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(std::u16string_view rStr, sal_Int32 nStart, sal_Int32 nEnd, bool bQuoted)
+{
+ 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 if (bQuoted)
+ {
+ aResult = SwResId(STR_START_QUOTE) +
+ rStr.substr(nStart, nCount) +
+ SwResId(STR_END_QUOTE);
+ }
+ else
+ aResult = rStr.substr(nStart, nCount);
+ }
+
+ return aResult;
+}
+
+OUString DenoteSpecialCharacters(std::u16string_view aStr, bool bQuoted)
+{
+ OUStringBuffer aResult;
+
+ if (!aStr.empty())
+ {
+ bool bStart = false;
+ sal_Int32 nStart = 0;
+ sal_Unicode cLast = 0;
+
+ for( size_t i = 0; i < aStr.size(); i++)
+ {
+ if (lcl_IsSpecialCharacter(aStr[i]))
+ {
+ if (cLast != aStr[i])
+ bStart = true;
+
+ }
+ else
+ {
+ if (lcl_IsSpecialCharacter(cLast))
+ bStart = true;
+ }
+
+ if (bStart)
+ {
+ aResult.append(lcl_DenotedPortion(aStr, nStart, i, bQuoted));
+
+ nStart = i;
+ bStart = false;
+ }
+
+ cLast = aStr[i];
+ }
+
+ aResult.append(lcl_DenotedPortion(aStr, nStart, aStr.size(), bQuoted));
+ }
+ else
+ aResult = SwRewriter::GetPlaceHolder(UndoArg2);
+
+ return aResult.makeStringAndClear();
+}
+
+SwRewriter SwUndoDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ if (m_nNode != SwNodeOffset(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 sw::FrameFormats<sw::SpzFrameFormat*>& rSpzs, const SwPosition &rPos, SwNodeOffset nOldIdx )
+{
+ const SwFormatAnchor* pAnchor;
+ for(auto pSpz: rSpzs)
+ {
+ pAnchor = &pSpz->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ SwNode* pAnchorNode = pAnchor->GetAnchorNode();
+ if( pAnchorNode && nOldIdx == pAnchorNode->GetIndex() )
+ {
+ SwFormatAnchor aAnch( *pAnchor );
+ aAnch.SetAnchor( &rPos );
+ pSpz->SetFormatAttr( aAnch );
+ }
+ }
+ }
+}
+
+void SwUndoDelete::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+
+ SwNodeOffset 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.GetNode(),
+ rDoc.GetDfltTextFormatColl() );
+ --aIdx;
+ aPos.Assign( *pInsNd->GetContentNode(), m_nSttContent );
+ }
+ else
+ {
+ if( pInsNd->IsContentNode() )
+ aPos.SetContent( m_nSttContent );
+ if( !m_bTableDelLastNd )
+ pInsNd = nullptr; // do not delete Node!
+ }
+ }
+ else
+ pInsNd = nullptr; // do not delete Node!
+
+ bool bNodeMove = SwNodeOffset(0) != m_nNode;
+
+ if( m_aEndStr )
+ {
+ // discard attributes since they all saved!
+ SwTextNode * pTextNd;
+ if (!m_bDelFullPara && aPos.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.GetNodeIndex() + 1]->GetTextNode();
+ }
+ else
+ {
+ pTextNd = aPos.GetNode().GetTextNode();
+ }
+
+ if( pTextNd && pTextNd->HasSwAttrSet() )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd && pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( true );
+
+ if( m_aSttStr && !m_bFromTableCopy )
+ {
+ SwNodeOffset nOldIdx = aPos.GetNodeIndex();
+ 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.GetNode().GetTextNode();
+ }
+ assert(pTextNd); // else where does m_aEndStr come from?
+ if( pTextNd )
+ {
+ SwContentIndex aTmpIdx(pTextNd, aPos.GetContentIndex());
+ OUString const ins( pTextNd->InsertText(*m_aEndStr, aTmpIdx,
+ 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.GetNode().GetTextNode();
+ if( pNd )
+ {
+ if (m_nSttContent < pNd->GetText().getLength())
+ {
+ SwNodeOffset nOldIdx = aPos.GetNodeIndex();
+ rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
+ if( m_bBackSp )
+ lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx);
+ }
+ else
+ aPos.Adjust(SwNodeOffset(+1));
+ }
+ }
+ if( m_nSectDiff )
+ {
+ SwNodeOffset nMoveIndex = aPos.GetNodeIndex();
+ SwNodeOffset nDiff(0);
+ if( m_bJoinNext )
+ {
+ nMoveIndex += m_nSectDiff + 1;
+ pMovedNode = &aPos.GetNode();
+ }
+ else
+ {
+ nMoveIndex -= m_nSectDiff + 1;
+ ++nDiff;
+ }
+ SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
+ SwNodeRange aRg( aPos.GetNode(), SwNodeOffset(0) - nDiff, aPos.GetNode(), SwNodeOffset(1) - nDiff );
+ aPos.Adjust(SwNodeOffset(-1));
+ if( !m_bJoinNext )
+ pMovedNode = &aPos.GetNode();
+ rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx.GetNode());
+ aPos.Adjust(SwNodeOffset(+1));
+ }
+
+ if( bNodeMove )
+ {
+ SwNodeRange aRange( *m_oMvStt, SwNodeOffset(0), *m_oMvStt, m_nNode );
+ SwNodeIndex aCopyIndex( aPos.GetNode(), -1 );
+ rDoc.GetUndoManager().GetUndoNodes().Copy_(aRange, aPos.GetNode(),
+ // 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 )
+ {
+ SwNodeOffset nMoveIndex;
+ if( m_bJoinNext )
+ {
+ nMoveIndex = m_nEndNode - m_nNdDiff;
+ aPos.Assign( nMoveIndex + m_nReplaceDummy );
+ }
+ else
+ {
+ aPos.Assign( aCopyIndex );
+ nMoveIndex = aPos.GetNodeIndex() + m_nReplaceDummy + 1;
+ }
+ SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
+ SwNodeRange aRg( aPos.GetNode(), SwNodeOffset(0), aPos.GetNode(), SwNodeOffset(1) );
+ pMovedNode = &aPos.GetNode();
+ // tdf#131684 without deleting frames
+ rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx.GetNode(), false);
+ rDoc.GetNodes().Delete( aMvIdx);
+ }
+ }
+
+ if( m_aSttStr )
+ {
+ aPos.Assign( m_nSttNode - m_nNdDiff + ( m_bJoinNext ? SwNodeOffset(0) : m_nReplaceDummy ) );
+ SwTextNode * pTextNd = aPos.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.SetContent( m_nSttContent );
+ pTextNd->SetInSwUndo(true);
+ OUString const ins( pTextNd->InsertText(*m_aSttStr, aPos,
+ SwInsertFlags::NOHINTEXPAND) );
+ pTextNd->SetInSwUndo(false);
+ 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);
+
+ SwNodeOffset 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()->HasMergedParas())
+ {
+ 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 || SwNodeOffset(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 (SwNodeOffset(0) != m_nNode)
+ {
+ // tdf#136453 only if section nodes at the start
+ if (m_bBackSp && m_nReplaceDummy != SwNodeOffset(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
+ SwNode& start(*rDoc.GetNodes()[m_nSttNode - m_nReplaceDummy]);
+ SwNode& 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 == SwNodeOffset(0)));
+ SwNode& 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...
+ SwNode& end(*rDoc.GetNodes()[ m_bDelFullPara
+ ? delFullParaEndNode
+ // tdf#147310 SwDoc::DeleteRowCol() may delete whole table - end must be node following table!
+ : (m_nEndNode + (rDoc.GetNodes()[m_nSttNode]->IsTableNode() && rDoc.GetNodes()[m_nEndNode]->IsEndNode() ? 1 : 0))]);
+ ::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()->GetNode(), rPam.GetPoint()->GetNode());
+ }
+ else
+ {
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType::AllMask
+ | ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) ? DelContentType::Replace : DelContentType(0)));
+ }
+ 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()->GetNode(), rPam.GetPoint()->GetNode() );
+ }
+ else
+ {
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType::AllMask
+ | ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) ? DelContentType::Replace : DelContentType(0)));
+ }
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+ }
+
+ if( !m_aSttStr && !m_aEndStr )
+ {
+ if (m_bDelFullPara && m_pRedlSaveData)
+ {
+ DelFullParaMoveFrames(rDoc, *this, *m_pRedlSaveData);
+ }
+
+ SwNode& rSttNd = ( m_bDelFullPara || m_bJoinNext )
+ ? rPam.GetMark()->GetNode()
+ : rPam.GetPoint()->GetNode();
+ SwTableNode* pTableNd = rSttNd.GetTableNode();
+ if( pTableNd )
+ {
+ if( m_bTableDelLastNd )
+ {
+ // than add again a Node at the end
+ const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
+ rDoc.GetNodes().MakeTextNode( aTmpIdx.GetNode(),
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
+ }
+
+ SwContentNode* pNextNd = rDoc.GetNodes()[
+ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+
+ if( const SwFormatPageDesc* pItem = pTableFormat->GetItemIfSet( RES_PAGEDESC,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( const SvxFormatBreakItem* pItem = pTableFormat->GetItemIfSet( RES_BREAK,
+ false ) )
+ 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 ~SwContentIndexReg for deleted nodes
+ SwPaM aTmp(*rPam.End());
+ if (!aTmp.Move(fnMoveForward, GoInNode))
+ {
+ *aTmp.GetPoint() = *rPam.Start();
+ aTmp.Move(fnMoveBackward, GoInNode);
+ }
+ // coverity[copy_paste_error : FALSE] : GetNode() is intentional on both branches
+ assert(aTmp.GetPoint()->GetNode() != rPam.GetPoint()->GetNode()
+ && aTmp.GetPoint()->GetNode() != rPam.GetMark()->GetNode());
+ ::PaMCorrAbs(rPam, *aTmp.GetPoint());
+
+ rPam.DeleteMark();
+
+ rDoc.GetNodes().Delete( rSttNd, 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()->Adjust(SwNodeOffset(-1));
+ if( rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode() )
+ *rPam.GetMark() = *rPam.GetPoint();
+ rDoc.getIDocumentContentOperations().DelFullPara( rPam );
+ }
+ else
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(rPam, m_DeleteFlags);
+}
+
+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;
+}
+
+void SwUndoDelete::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoDelete"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ SwUndo::dumpAsXml(pWriter);
+ SwUndoSaveContent::dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* 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 0000000000..ce27962aa3
--- /dev/null
+++ b/sw/source/core/undo/undobj.cxx
@@ -0,0 +1,1768 @@
+/* -*- 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 <libxml/xmlwriter.h>
+
+#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 <swcrsr.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 <o3tl/deleter.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.End();
+ m_nEndNode = pEnd->GetNodeIndex();
+ m_nEndContent = pEnd->GetContentIndex();
+ }
+ else
+ {
+ // no selection !!
+ m_nEndNode = SwNodeOffset(0);
+ m_nEndContent = COMPLETE_STRING;
+ }
+
+ m_nSttNode = pStt->GetNodeIndex();
+ m_nSttContent = pStt->GetContentIndex();
+}
+
+void SwUndRng::SetPaM( SwPaM & rPam, bool bCorrToContent ) const
+{
+ rPam.DeleteMark();
+ rPam.GetPoint()->Assign( m_nSttNode, m_nSttContent );
+ SwNode& rNd = rPam.GetPointNode();
+ if( !rNd.IsContentNode() && bCorrToContent )
+ rPam.Move( fnMoveForward, GoInContent );
+
+ 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()->Assign( m_nEndNode, m_nEndContent );
+ if( !rPam.GetPointNode().IsContentNode() && bCorrToContent )
+ rPam.Move( fnMoveBackward, GoInContent );
+}
+
+SwPaM & SwUndRng::AddUndoRedoPaM(
+ ::sw::UndoRedoContext & rContext, bool const bCorrToContent) const
+{
+ SwCursor & rPaM( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ SetPaM( rPaM, bCorrToContent );
+ return rPaM;
+}
+
+void SwUndo::RemoveIdxFromSection( SwDoc& rDoc, SwNodeOffset nSttIdx,
+ const SwNodeOffset* 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()->GetNode() );
+ SwNodeIndex aEnd( rPam.GetPoint()->GetNode() );
+
+ if( !rPam.Move( fnMoveForward ) )
+ {
+ rPam.Exchange();
+ if( !rPam.Move( fnMoveBackward ) )
+ {
+ rPam.GetPoint()->Assign( rPam.GetDoc().GetNodes().GetEndOfPostIts() );
+ }
+ }
+
+ SwDoc::CorrAbs( aStt, aEnd, *rPam.GetPoint(), true );
+ }
+ else
+ SwDoc::CorrAbs( rPam, *pEnd, true );
+}
+
+void SwUndo::RemoveIdxRel( SwNodeOffset nIdx, const SwPosition& rPos )
+{
+ // Move only the Cursor. Bookmarks/TOXMarks/etc. are done by the corresponding
+ // JoinNext/JoinPrev
+ ::PaMCorrRel( *rPos.GetNode().GetNodes()[nIdx], 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)
+{
+ TranslateId pId;
+ 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::OUTLINE_EDIT:
+ pId = STR_OUTLINE_EDIT;
+ 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;
+ case SwUndoId::INSERT_PAGE_NUMBER:
+ pId = STR_UNDO_INSERT_PAGE_NUMBER;
+ break;
+ case SwUndoId::UPDATE_FORM_FIELD:
+ pId = STR_UNDO_UPDATE_FORM_FIELD;
+ break;
+ case SwUndoId::UPDATE_FORM_FIELDS:
+ pId = STR_UNDO_UPDATE_FORM_FIELDS;
+ break;
+ case SwUndoId::DELETE_FORM_FIELDS:
+ pId = STR_UNDO_DELETE_FORM_FIELDS;
+ break;
+ case SwUndoId::UPDATE_BOOKMARK:
+ pId = STR_UPDATE_BOOKMARK;
+ break;
+ case SwUndoId::UPDATE_BOOKMARKS:
+ pId = STR_UPDATE_BOOKMARKS;
+ break;
+ case SwUndoId::DELETE_BOOKMARKS:
+ pId = STR_DELETE_BOOKMARKS;
+ break;
+ case SwUndoId::UPDATE_FIELD:
+ pId = STR_UPDATE_FIELD;
+ break;
+ case SwUndoId::UPDATE_FIELDS:
+ pId = STR_UPDATE_FIELDS;
+ break;
+ case SwUndoId::DELETE_FIELDS:
+ pId = STR_DELETE_FIELDS;
+ break;
+ case SwUndoId::UPDATE_SECTIONS:
+ pId = STR_UPDATE_SECTIONS;
+ break;
+ case SwUndoId::CHANGE_THEME:
+ pId = STR_UNDO_CHANGE_THEME_COLORS;
+ break;
+ case SwUndoId::DELETE_SECTIONS:
+ pId = STR_DELETE_SECTIONS;
+ break;
+ case SwUndoId::FLYFRMFMT_DECORATIVE:
+ pId = STR_UNDO_FLYFRMFMT_DECORATIVE;
+ 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
+{
+}
+
+void SwUndoSaveContent::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoSaveContent"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ if (m_pHistory)
+ {
+ m_pHistory->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// 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,
+ SwNodeOffset* pEndNdIdx )
+{
+ SwDoc& rDoc = rPaM.GetDoc();
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwNoTextNode* pCpyNd = rPaM.GetPointNode().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();
+
+ SwNodeOffset nTmpMvNode = aPos.GetNodeIndex();
+
+ if( pCpyNd || pEndNdIdx )
+ {
+ SwNodeRange aRg( pStt->GetNode(), SwNodeOffset(0), pEnd->GetNode(), SwNodeOffset(1) );
+ rDoc.GetNodes().MoveNodes( aRg, rNds, aPos.GetNode(), true );
+ aPos.Adjust(SwNodeOffset(-1));
+ }
+ else
+ {
+ rDoc.GetNodes().MoveRange( rPaM, aPos, rNds );
+ }
+ if( pEndNdIdx )
+ *pEndNdIdx = aPos.GetNodeIndex();
+
+ // old position
+ aPos.Assign(nTmpMvNode);
+ if( pNodeIdx )
+ *pNodeIdx = aPos.GetNode();
+}
+
+void SwUndoSaveContent::MoveFromUndoNds( SwDoc& rDoc, SwNodeOffset nNodeIdx,
+ SwPosition& rInsPos,
+ const SwNodeOffset* 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()->Assign( *rNds[SwNodeOffset(0)], *pEndNdIdx );
+ else
+ {
+ aPaM.GetPoint()->Assign( rNds.GetEndOfExtras() );
+ GoInContent( aPaM, fnMoveBackward );
+ }
+
+ SwTextNode* pTextNd = aPaM.GetPointNode().GetTextNode();
+ if (!pEndNdIdx && pTextNd)
+ {
+ aPaM.SetMark();
+ aPaM.GetPoint()->Assign(nNodeIdx, 0);
+
+ SaveRedlEndPosForRestore aRedlRest( rInsPos.GetNode(), rInsPos.GetContentIndex() );
+
+ rNds.MoveRange( aPaM, rInsPos, rDoc.GetNodes() );
+
+ // delete the last Node as well
+ bool bDeleteLastNode = false;
+ if( !aPaM.GetPoint()->GetContentIndex() )
+ bDeleteLastNode = true;
+ else
+ {
+ // still empty Nodes at the end?
+ aPaM.GetPoint()->Adjust(SwNodeOffset(1));
+ if ( &rNds.GetEndOfExtras() != &aPaM.GetPoint()->GetNode() )
+ bDeleteLastNode = true;
+ }
+ if( bDeleteLastNode )
+ {
+ SwNode& rDelNode = aPaM.GetPoint()->GetNode();
+ SwNodeOffset nDelOffset = rNds.GetEndOfExtras().GetIndex() -
+ aPaM.GetPoint()->GetNodeIndex();
+ //move it so we don't have SwContentIndex pointing at a node when it is deleted.
+ aPaM.GetPoint()->Adjust(SwNodeOffset(-1));
+ aPaM.SetMark();
+ rNds.Delete( rDelNode, nDelOffset );
+ }
+
+ aRedlRest.Restore();
+ }
+ else
+ {
+ SwNodeRange aRg( rNds, nNodeIdx, (pEndNdIdx
+ ? ((*pEndNdIdx) + 1)
+ : rNds.GetEndOfExtras().GetIndex() ) );
+ rNds.MoveNodes(aRg, rDoc.GetNodes(), rInsPos.GetNode(), nullptr == pEndNdIdx || bForceCreateFrames);
+
+ }
+}
+
+// These two methods save and restore the Point of PaM.
+// If the point cannot be moved, a "backup" is created on the previous node.
+// Either way, returned, inserting at its original position will not move it.
+::std::optional<SwNodeIndex> SwUndoSaveContent::MovePtBackward(SwPaM & rPam)
+{
+ rPam.SetMark();
+ if( rPam.Move( fnMoveBackward ))
+ return {};
+
+ return { SwNodeIndex(rPam.GetPoint()->GetNode(), -1) };
+}
+
+void SwUndoSaveContent::MovePtForward(SwPaM& rPam, ::std::optional<SwNodeIndex> && oMvBkwrd)
+{
+ // Was there content before this position?
+ if (!oMvBkwrd)
+ rPam.Move( fnMoveForward );
+ else
+ {
+ *rPam.GetPoint() = SwPosition(*oMvBkwrd);
+ rPam.GetPoint()->Adjust(SwNodeOffset(1));
+ SwContentNode* pCNd = rPam.GetPointContentNode();
+ if( !pCNd )
+ 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& rDoc = rMark.GetNode().GetDoc();
+
+ // if it's not in the doc array, probably missing some invalidation somewhere
+ assert(&rPoint.GetNodes() == &rDoc.GetNodes());
+ assert(&rMark.GetNodes() == &rDoc.GetNodes());
+
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ // 1. Footnotes
+ if( DelContentType::Ftn & nDelContentType )
+ {
+ SwFootnoteIdxs& rFootnoteArr = rDoc.GetFootnoteIdxs();
+ if( !rFootnoteArr.empty() )
+ {
+ const SwNode* pFootnoteNd;
+ size_t nPos = 0;
+ rFootnoteArr.SeekEntry( pStt->GetNode(), &nPos );
+ SwTextFootnote* pSrch;
+
+ // for now delete all that come afterwards
+ while( nPos < rFootnoteArr.size() && ( pFootnoteNd =
+ &( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex()
+ <= pEnd->GetNodeIndex() )
+ {
+ const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
+ if( (DelContentType::CheckNoCntnt & nDelContentType )
+ ? (&pEnd->GetNode() == pFootnoteNd )
+ : (( &pStt->GetNode() == pFootnoteNd &&
+ pStt->GetContentIndex() > nFootnoteSttIdx) ||
+ ( &pEnd->GetNode() == pFootnoteNd &&
+ nFootnoteSttIdx >= pEnd->GetContentIndex() )) )
+ {
+ ++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);
+ SwContentIndex aIdx( pTextNd, nFootnoteSttIdx );
+ m_pHistory->AddTextAttr(pFootnoteHint, pTextNd->GetIndex(), false);
+ pTextNd->EraseText( aIdx, 1 );
+ }
+
+ while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )->
+ GetTextNode())->GetIndex() >= pStt->GetNodeIndex() )
+ {
+ const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
+ if( !(DelContentType::CheckNoCntnt & nDelContentType) && (
+ ( &pStt->GetNode() == pFootnoteNd &&
+ pStt->GetContentIndex() > nFootnoteSttIdx ) ||
+ ( &pEnd->GetNode() == pFootnoteNd &&
+ nFootnoteSttIdx >= pEnd->GetContentIndex() )))
+ 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);
+ SwContentIndex aIdx( pTextNd, nFootnoteSttIdx );
+ m_pHistory->AddTextAttr(pFootnoteHint, pTextNd->GetIndex(), false);
+ pTextNd->EraseText( aIdx, 1 );
+ }
+ }
+ }
+
+ // 2. Flys
+ if( DelContentType::Fly & nDelContentType )
+ {
+ sal_uInt16 nChainInsPos = m_pHistory ? m_pHistory->Count() : 0;
+ const sw::SpzFrameFormats& rSpzArr = *rDoc.GetSpzFrameFormats();
+ if( !rSpzArr.empty() )
+ {
+ sw::SpzFrameFormat* 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->GetNode() <= pAPos->GetNode() &&
+ pAPos->GetNode() < pEnd->GetNode() )
+ : ( *pStt <= *pAPos && *pAPos < *pEnd )) )
+ {
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ SwTextNode *const pTextNd =
+ pAPos->GetNode().GetTextNode();
+ SwTextAttr* const pFlyHint = pTextNd->GetTextAttrForCharAt(
+ pAPos->GetContentIndex());
+ assert(pFlyHint);
+ m_pHistory->AddTextAttr(pFlyHint, SwNodeOffset(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->GetNode() <= pAPos->GetNode() && pAPos->GetNode() <= pEnd->GetNode())
+ {
+ 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) &&
+ // for SwUndoDelete: rPoint is the node that
+ // will be Joined - so anchor should be moved
+ // off it - but UndoImpl() split will insert
+ // new node *before* existing one so a no-op
+ // may need to be done here to add it to
+ // history for Undo.
+ (rPoint.GetNodeIndex() == pAPos->GetNodeIndex()
+ || pStt->GetNodeIndex() == pAPos->GetNodeIndex())
+ // Do not try to move the anchor to a table!
+ && rMark.GetNode().IsTextNode())
+ {
+ m_pHistory->AddChangeFlyAnchor(*pFormat);
+ SwFormatAnchor aAnch( *pAnchor );
+ SwPosition aPos( rMark.GetNode() );
+ aAnch.SetAnchor( &aPos );
+ pFormat->SetFormatAttr( aAnch );
+ }
+ }
+ }
+ break;
+ case RndStdIds::FLY_AT_CHAR:
+ if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) &&
+ ( pStt->GetNode() <= pAPos->GetNode() && pAPos->GetNode() <= pEnd->GetNode() ) )
+ {
+ 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.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->GetNode() == pAPos->GetNode() )
+ {
+ 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) )
+ return;
+
+ IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
+ if( !pMarkAccess->getAllMarksCount() )
+ return;
+
+ for( sal_Int32 n = 0; n < pMarkAccess->getAllMarksCount(); ++n )
+ {
+ // #i81002#
+ bool bSavePos = false;
+ bool bSaveOtherPos = false;
+ bool bDelete = false;
+ const ::sw::mark::IMark *const pBkmk = pMarkAccess->getAllMarksBegin()[n];
+ auto const type(IDocumentMarkAccess::GetType(*pBkmk));
+
+ if( DelContentType::CheckNoCntnt & nDelContentType )
+ {
+ if ( pStt->GetNode() <= pBkmk->GetMarkPos().GetNode()
+ && pBkmk->GetMarkPos().GetNode() < pEnd->GetNode() )
+ {
+ bSavePos = true;
+ }
+ if ( pBkmk->IsExpanded()
+ && pStt->GetNode() <= pBkmk->GetOtherMarkPos().GetNode()
+ && pBkmk->GetOtherMarkPos().GetNode() < pEnd->GetNode() )
+ {
+ bSaveOtherPos = true;
+ }
+ bDelete = bSavePos && bSaveOtherPos;
+ }
+ else
+ {
+ // #i92125#
+ // keep cross-reference bookmarks, if content inside one paragraph is deleted.
+ if ( rMark.GetNode() == rPoint.GetNode()
+ && ( 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))
+ || (bMaybe
+ && !(nDelContentType & DelContentType::Replace)
+ && type == IDocumentMarkAccess::MarkType::BOOKMARK
+ && pStt->GetContentIndex() == 0 // entire paragraph deleted?
+ && pEnd->GetContentIndex() == pEnd->GetNode().GetTextNode()->Len()))
+ {
+ if( bMaybe )
+ bSavePos = true;
+ bDelete = true;
+ }
+ if (bDelete || pBkmk->GetOtherMarkPos() == *pEnd)
+ {
+ bSaveOtherPos = true; // tdf#148389 always undo if at end
+ }
+ }
+ if (!bSavePos && bMaybe && pBkmk->IsExpanded() && *pStt == pBkmk->GetMarkPos())
+ {
+ bSavePos = true; // tdf#148389 always undo if at start
+ }
+
+ if ( !bSavePos && !bSaveOtherPos
+ && dynamic_cast< const ::sw::mark::CrossRefBookmark* >(pBkmk) )
+ {
+ // certain special handling for cross-reference bookmarks
+ const bool bDifferentTextNodesAtMarkAndPoint =
+ rMark.GetNode() != rPoint.GetNode()
+ && rMark.GetNode().GetTextNode()
+ && rPoint.GetNode().GetTextNode();
+ if ( bDifferentTextNodesAtMarkAndPoint )
+ {
+ // delete cross-reference bookmark at <pStt>, if only part of
+ // <pEnd> text node content is deleted.
+ if( pStt->GetNode() == pBkmk->GetMarkPos().GetNode()
+ && pEnd->GetContentIndex() != pEnd->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->GetNode() == pBkmk->GetMarkPos().GetNode() &&
+ pStt->GetContentIndex() != 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
+ bDelete = true;
+ }
+ }
+ }
+
+ if ( bSavePos || bSaveOtherPos )
+ {
+ if (type != IDocumentMarkAccess::MarkType::UNO_BOOKMARK)
+ {
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ m_pHistory->AddIMark(*pBkmk, bSavePos, bSaveOtherPos);
+ }
+ if ( bSavePos
+ && (bDelete || !pBkmk->IsExpanded()))
+ {
+ pMarkAccess->deleteMark(pMarkAccess->getAllMarksBegin()+n, false);
+ n--;
+ }
+ }
+ }
+}
+
+// save a complete section into UndoNodes array
+SwUndoSaveSection::SwUndoSaveSection()
+ : m_nMoveLen( 0 ), m_nStartPos( NODE_OFFSET_MAX )
+{
+}
+
+SwUndoSaveSection::~SwUndoSaveSection()
+{
+ if (m_oMovedStart) // delete also the section from UndoNodes array
+ {
+ // SaveSection saves the content in the PostIt section.
+ SwNodes& rUNds = m_oMovedStart->GetNode().GetNodes();
+ // cid#1486004 Uncaught exception
+ suppress_fun_call_w_exception(rUNds.Delete(*m_oMovedStart, m_nMoveLen));
+
+ m_oMovedStart.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()->GetNode() );
+ SwNodeIndex aEndIdx( aPam.End()->GetNode() );
+ SwNodeIndex aMvStt( aEndIdx, 1 );
+ SwDoc::CorrAbs( aSttIdx, aEndIdx, SwPosition( aMvStt ), true );
+ }
+
+ m_nStartPos = rRange.aStart.GetIndex();
+
+ if (bExpandNodes)
+ {
+ aPam.GetPoint()->Adjust(SwNodeOffset(-1));
+ aPam.GetMark()->Adjust(SwNodeOffset(+1));
+ }
+
+ SwContentNode* pCNd = aPam.GetMarkContentNode();
+ if( pCNd )
+ aPam.GetMark()->SetContent( 0 );
+ pCNd = aPam.GetPointContentNode();
+ if( nullptr != pCNd )
+ aPam.GetPoint()->SetContent( pCNd->Len() );
+
+ // Keep positions as SwContentIndex so that this section can be deleted in DTOR
+ SwNodeOffset nEnd;
+ m_oMovedStart = rRange.aStart;
+ MoveToUndoNds(aPam, &*m_oMovedStart, &nEnd);
+ m_nMoveLen = nEnd - m_oMovedStart->GetIndex() + 1;
+}
+
+void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, SwNodeIndex* pIdx,
+ sal_uInt16 nSectType )
+{
+ if( NODE_OFFSET_MAX == m_nStartPos ) // was there any content?
+ return;
+
+ // 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.GetNode(),
+ static_cast<SwStartNodeType>(nSectType) );
+
+ RestoreSection( pDoc, *pSttNd->EndOfSectionNode() );
+
+ if( pIdx )
+ *pIdx = *pSttNd;
+}
+
+void SwUndoSaveSection::RestoreSection(
+ SwDoc *const pDoc, const SwNode& rInsPos, bool bForceCreateFrames)
+{
+ if( NODE_OFFSET_MAX == m_nStartPos ) // was there any content?
+ return;
+
+ SwPosition aInsPos( rInsPos );
+ SwNodeOffset nEnd = m_oMovedStart->GetIndex() + m_nMoveLen - 1;
+ MoveFromUndoNds(*pDoc, m_oMovedStart->GetIndex(), aInsPos, &nEnd, bForceCreateFrames);
+
+ // destroy indices again, content was deleted from UndoNodes array
+ m_oMovedStart.reset();
+ m_nMoveLen = SwNodeOffset(0);
+
+ if( m_pRedlineSaveData )
+ {
+ SwUndo::SetSaveData( *pDoc, *m_pRedlineSaveData );
+ m_pRedlineSaveData.reset();
+ }
+}
+
+void SwUndoSaveSection::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ SwUndoSaveContent::dumpAsXml(pWriter);
+}
+
+// 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.GetNodeIndex();
+ m_nEndContent = rEndPos.GetContentIndex();
+ break;
+
+ case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at the end
+ m_nSttNode = rSttPos.GetNodeIndex();
+ m_nSttContent = rSttPos.GetContentIndex();
+ break;
+
+ case SwComparePosition::Inside: // Pos1 lays completely in Pos2
+ m_nSttNode = rSttPos.GetNodeIndex();
+ m_nSttContent = rSttPos.GetContentIndex();
+ m_nEndNode = rEndPos.GetNodeIndex();
+ m_nEndContent = rEndPos.GetContentIndex();
+ break;
+
+ case SwComparePosition::Outside: // Pos2 lays completely in Pos1
+ if ( rRedl.GetContentIdx() )
+ {
+ // than move section into UndoArray and memorize it
+ SaveSection( *rRedl.GetContentIdx() );
+ rRedl.ClearContentIdx();
+ }
+ break;
+
+ case SwComparePosition::Equal: // Pos1 is exactly as big as Pos2
+ break;
+
+ default:
+ assert(false);
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ m_nRedlineCount = rSttPos.GetNode().GetDoc().getIDocumentRedlineAccess().GetRedlineTable().size();
+ m_bRedlineCountDontCheck = false;
+ m_bRedlineMoved = rRedl.IsMoved();
+#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();
+
+ auto [pStt, pEnd] = rRange.StartEnd(); // SwPosition*
+ 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>(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>(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
+ // except in the case of moved redlines
+ assert(
+ rSData.empty() || rSData[0].m_bRedlineMoved || rSData[0].m_bRedlineCountDontCheck ||
+ (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, std::u16string_view aFillStr)
+{
+ assert(nLength - aFillStr.size() >= 2);
+
+ if (rStr.getLength() <= nLength)
+ return rStr;
+
+ nLength -= aFillStr.size();
+ if ( nLength < 2 )
+ nLength = 2;
+
+ const sal_Int32 nFrontLen = nLength - nLength / 2;
+ const sal_Int32 nBackLen = nLength - nFrontLen;
+
+ return OUString::Concat(rStr.subView(0, nFrontLen))
+ + aFillStr
+ + rStr.subView(rStr.getLength() - nBackLen);
+}
+
+static bool IsAtEndOfSection(SwPosition const& rAnchorPos)
+{
+ SwNodeIndex node(*rAnchorPos.GetNode().EndOfSectionNode());
+ SwContentNode *const pNode(SwNodes::GoPrevious(&node));
+ assert(pNode);
+ assert(rAnchorPos.GetNode() <= node.GetNode()); // last valid anchor pos is last content
+ return node == rAnchorPos.GetNode()
+ // at-para fly has no SwContentIndex!
+ && (rAnchorPos.GetContentIndex() == pNode->Len() || rAnchorPos.GetContentNode() == nullptr);
+}
+
+static bool IsAtStartOfSection(SwPosition const& rAnchorPos)
+{
+ SwNodes const& rNodes(rAnchorPos.GetNodes());
+ SwNodeIndex node(*rAnchorPos.GetNode().StartOfSectionNode());
+ SwContentNode *const pNode(rNodes.GoNext(&node));
+ assert(pNode);
+ (void) pNode;
+ assert(node <= rAnchorPos.GetNode());
+ return node == rAnchorPos.GetNode() && rAnchorPos.GetContentIndex() == 0;
+}
+
+/// passed start / end position could be on section start / end node
+static bool IsAtEndOfSection2(SwPosition const& rPos)
+{
+ return rPos.GetNode().IsEndNode()
+ || IsAtEndOfSection(rPos);
+}
+
+static bool IsAtStartOfSection2(SwPosition const& rPos)
+{
+ return rPos.GetNode().IsStartNode()
+ || IsAtStartOfSection(rPos);
+}
+
+static bool IsNotBackspaceHeuristic(
+ SwPosition const& rStart, SwPosition const& rEnd)
+{
+ // check if the selection is backspace/delete created by DelLeft/DelRight
+ if (rStart.GetNodeIndex() + 1 != rEnd.GetNodeIndex())
+ return true;
+ if (rEnd.GetContentIndex() != 0)
+ return true;
+ const SwTextNode* pTextNode = rStart.GetNode().GetTextNode();
+ if (!pTextNode || rStart.GetContentIndex() != pTextNode->Len())
+ return true;
+ return false;
+}
+
+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.GetNode() < rEnd.GetNode())
+ && (rStart.GetNode() <= rAnchorPos.GetNode());
+ }
+
+ 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.GetNode() < rEnd.GetNode())
+ && (rStart.GetNode() < rAnchorPos.GetNode());
+ }
+
+ // in general, exclude the start and end position
+ return ((rStart < rAnchorPos)
+ || (rStart == rAnchorPos
+ // special case: fully deleted node
+ && ((rStart.GetNode() != rEnd.GetNode() && rStart.GetContentIndex() == 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.GetNode() != rStart.GetNode() && rEnd.GetContentIndex() == rEnd.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.GetNode() < rEnd.GetNode())
+ && (rStart.GetNode() <= rAnchorPos.GetNode());
+ }
+
+ 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.GetNode() < rAnchorPos.GetNode()) && (rAnchorPos.GetNode() < rEnd.GetNode());
+ }
+
+ // in general, exclude the start and end position
+ return ((rStart.GetNode() < rAnchorPos.GetNode())
+ || (rStart.GetNode() == rAnchorPos.GetNode()
+ && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+ // special case: fully deleted node
+ && ((rStart.GetNode() != rEnd.GetNode() && rStart.GetContentIndex() == 0
+ // but not if the selection is backspace/delete!
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || (IsAtStartOfSection2(rStart) && IsAtEndOfSection2(rEnd)))))
+ && ((rAnchorPos.GetNode() < rEnd.GetNode())
+ || (rAnchorPos.GetNode() == rEnd.GetNode()
+ && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+ // special case: fully deleted node
+ && ((rEnd.GetNode() != rStart.GetNode() && rEnd.GetContentIndex() == rEnd.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 0000000000..f0430b965b
--- /dev/null
+++ b/sw/source/core/undo/undobj1.cxx
@@ -0,0 +1,720 @@
+/* -*- 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 <libxml/xmlwriter.h>
+
+#include <svl/itemiter.hxx>
+#include <svx/svdundo.hxx>
+#include <osl/diagnose.h>
+#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 <rootfrm.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <mvsave.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 (const auto& pTextBoxes = m_pFrameFormat->GetOtherTextBoxFormats())
+ {
+ // Clear and unregister before release.
+ if (m_pFrameFormat->Which() == RES_FLYFRMFMT)
+ pTextBoxes->DelTextBox(m_pFrameFormat);
+
+ if (m_pFrameFormat->Which() == RES_DRAWFRMFMT)
+ pTextBoxes->ClearAll();
+
+ // clear that before delete
+ m_pFrameFormat->SetOtherTextBoxFormats(nullptr);
+ }
+ delete m_pFrameFormat;
+ }
+}
+
+void SwUndoFlyBase::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoFlyBase"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nNodePagePos"),
+ BAD_CAST(OString::number(sal_Int32(m_nNodePagePos)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nContentPos"),
+ BAD_CAST(OString::number(m_nContentPos).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nRndId"),
+ BAD_CAST(OString::number(static_cast<int>(m_nRndId)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_bDelFormat"),
+ BAD_CAST(OString::boolean(m_bDelFormat).getStr()));
+
+ SwUndo::dumpAsXml(pWriter);
+ SwUndoSaveSection::dumpAsXml(pWriter);
+
+ if (m_pFrameFormat)
+ {
+ m_pFrameFormat->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwUndoFlyBase::InsFly(::sw::UndoRedoContext & rContext, bool bShowSelFrame)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ // add again into array
+ sw::SpzFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.push_back( static_cast<sw::SpzFrameFormat*>(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( o3tl::narrowing<sal_uInt16>(sal_Int32(m_nNodePagePos)) );
+ }
+ else
+ {
+ SwPosition aNewPos(*pDoc->GetNodes()[m_nNodePagePos]);
+ if ((RndStdIds::FLY_AS_CHAR == m_nRndId) || (RndStdIds::FLY_AT_CHAR == m_nRndId))
+ {
+ aNewPos.SetContent( 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.GetAnchorNode()->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->GetOtherTextBoxFormats())
+ {
+ // 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->GetOtherTextBoxFormats()->GetOwnerShape()->SetOtherTextBoxFormats(
+ m_pFrameFormat->GetOtherTextBoxFormats());
+
+ SdrObject* pSdrObject
+ = m_pFrameFormat->GetOtherTextBoxFormats()->GetOwnerShape()->FindSdrObject();
+ if (pSdrObject && m_pFrameFormat->Which() == RES_FLYFRMFMT)
+ m_pFrameFormat->GetOtherTextBoxFormats()->AddTextBox(pSdrObject, m_pFrameFormat);
+
+ if (m_pFrameFormat->GetOtherTextBoxFormats()->GetOwnerShape()->Which() == RES_DRAWFRMFMT)
+ {
+
+ 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_FLYFRMFMT)
+ {
+ SwFrameFormat* pShapeFormat = m_pFrameFormat->GetOtherTextBoxFormats()->GetOwnerShape();
+ pShapeFormat->SetFormatAttr(m_pFrameFormat->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.GetAnchorNode()->GetIndex();
+ m_nContentPos = rAnchor.GetAnchorContentOffset();
+ }
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ {
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ m_nNodePagePos = rAnchor.GetAnchorNode()->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->GetOtherTextBoxFormats())
+ { // tdf#108867 clear that pointer
+ m_pFrameFormat->GetOtherTextBoxFormats()->GetOwnerShape()->SetOtherTextBoxFormats(nullptr);
+ }
+
+ // all Uno objects should now log themselves off
+ m_pFrameFormat->RemoveAllUnos();
+
+ 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();
+ SwNode* pAnchorNode = rAnchor.GetAnchorNode();
+ // The positions in Nodes array got shifted.
+ m_nRndId = rAnchor.GetAnchorId();
+ if (RndStdIds::FLY_AS_CHAR == m_nRndId)
+ {
+ m_nNodePagePos = pAnchorNode->GetIndex();
+ m_nContentPos = rAnchor.GetAnchorContentOffset();
+ SwTextNode *const pTextNd = pAnchorNode->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();
+ pTextNd->EraseText( *rAnchor.GetContentAnchor(), 1 );
+ }
+ }
+ else if (RndStdIds::FLY_AT_CHAR == m_nRndId)
+ {
+ m_nNodePagePos = pAnchorNode->GetIndex();
+ m_nContentPos = rAnchor.GetAnchorContentOffset();
+ }
+ else if ((RndStdIds::FLY_AT_PARA == m_nRndId) || (RndStdIds::FLY_AT_FLY == m_nRndId))
+ {
+ m_nNodePagePos = pAnchorNode->GetIndex();
+ }
+ else
+ {
+ m_nNodePagePos = SwNodeOffset(rAnchor.GetPageNum());
+ }
+
+ m_pFrameFormat->ResetFormatAttr( RES_ANCHOR ); // delete anchor
+
+ // delete from array
+ sw::SpzFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.erase(static_cast<sw::SpzFrameFormat*>(m_pFrameFormat));
+}
+
+SwUndoInsLayFormat::SwUndoInsLayFormat( SwFrameFormat* pFormat, SwNodeOffset 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;
+ // note: SwUndoInsLayFormat is called with the content being fully inserted
+ // from most places but with only an empty content section from
+ // CopyLayoutFormat(); it's not necessary here to init m_nNodePagePos
+ // because Undo will do it.
+}
+
+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());
+ if( mnCursorSaveIndexPara > SwNodeOffset(0) )
+ {
+ SwTextNode *const pNode =
+ rDoc.GetNodes()[mnCursorSaveIndexPara]->GetTextNode();
+ if( pNode )
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes(),
+ rContent.GetContentIdx()->GetIndex() );
+ SwNodeIndex aEndIdx( rDoc.GetNodes(),
+ aIdx.GetNode().EndOfSectionIndex() );
+ SwPosition aPos( *pNode, mnCursorSaveIndexPos );
+ // don't delete bookmarks here, DelFly() will save them in history
+ ::PaMCorrAbs(SwPaM(aIdx, aEndIdx), aPos);
+ // TODO: is aPos actually a sensible pos for e.g. SwXText* ?
+ }
+ }
+ }
+ 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() );
+ aAnchor.SetAnchor( &aPos );
+ }
+ else if( RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId() )
+ {
+ SwStartNode const*const pSttNd =
+ rContext.GetRepeatPaM().GetPointNode().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)
+ {
+ const SwNodeIndex* pIdx = GetMvSttIdx();
+ if( SwNodeOffset(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_oItemSet( std::in_place, *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,
+ SwNodeOffset 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.SetContent(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)
+ return;
+
+ if( m_bAnchorChanged )
+ m_pFrameFormat->DelFrames();
+
+ if( m_pFrameFormat->DerivedFrom() != pDerivedFromFrameFormat)
+ m_pFrameFormat->SetDerivedFrom(pDerivedFromFrameFormat);
+
+ SfxItemIter aIter( *m_oItemSet );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if( IsInvalidItem( pItem ))
+ m_pFrameFormat->ResetFormatAttr( m_oItemSet->GetWhichByOffset(
+ 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.
+ SwNode *pAnchorNode = rOldAnch.GetAnchorNode();
+ SwTextNode *pTextNode = pAnchorNode->GetTextNode();
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = rOldAnch.GetAnchorContentOffset();
+ 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())
+ {
+ SwNode* pAnchorNode = aNewAnchor.GetAnchorNode();
+ SwFormatFlyCnt aFormat( m_pFrameFormat );
+ pAnchorNode->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)
+ return;
+
+ 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 && !SfxPoolItem::areSame(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->GetAnchorContentOffset();
+ [[fallthrough]];
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ m_nOldNode = pAnchor->GetAnchorNode()->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->GetAnchorContentOffset();
+ [[fallthrough]];
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ m_nNewNode = pAnchor->GetAnchorNode()->GetIndex();
+ break;
+
+ default:
+ m_nNewContent = pAnchor->GetPageNum();
+ }
+ }
+ else
+ m_oItemSet->Put( *pItem );
+ }
+ else
+ m_oItemSet->InvalidateItem( nWhich );
+}
+
+void SwUndoSetFlyFormat::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(!pLegacy->m_pOld)
+ return;
+ const sal_uInt16 nWhich = pLegacy->m_pOld->Which();
+ if(nWhich < POOLATTR_END)
+ PutAttr(nWhich, pLegacy->m_pOld);
+ else if(RES_ATTRSET_CHG == nWhich)
+ {
+ SfxItemIter aIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_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 0000000000..60d8da4e95
--- /dev/null
+++ b/sw/source/core/undo/undoflystrattr.cxx
@@ -0,0 +1,122 @@
+/* -*- 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>
+#include <utility>
+
+
+SwUndoFlyStrAttr::SwUndoFlyStrAttr( SwFlyFrameFormat& rFlyFrameFormat,
+ const SwUndoId eUndoId,
+ OUString sOldStr,
+ OUString sNewStr )
+ : SwUndo( eUndoId, rFlyFrameFormat.GetDoc() ),
+ mrFlyFrameFormat( rFlyFrameFormat ),
+ msOldStr(std::move( sOldStr )),
+ msNewStr(std::move( 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;
+}
+
+SwUndoFlyDecorative::SwUndoFlyDecorative(SwFlyFrameFormat& rFlyFrameFormat,
+ bool const isDecorative)
+ : SwUndo(SwUndoId::FLYFRMFMT_DECORATIVE, rFlyFrameFormat.GetDoc())
+ , m_rFlyFrameFormat(rFlyFrameFormat)
+ , m_IsDecorative(isDecorative)
+{
+}
+
+SwUndoFlyDecorative::~SwUndoFlyDecorative()
+{
+}
+
+void SwUndoFlyDecorative::UndoImpl(::sw::UndoRedoContext &)
+{
+ m_rFlyFrameFormat.SetObjDecorative(!m_IsDecorative);
+}
+
+void SwUndoFlyDecorative::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_rFlyFrameFormat.SetObjDecorative(m_IsDecorative);
+}
+
+SwRewriter SwUndoFlyDecorative::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, m_rFlyFrameFormat.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 0000000000..134c686297
--- /dev/null
+++ b/sw/source/core/undo/undraw.cxx
@@ -0,0 +1,679 @@
+/* -*- 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 <osl/diagnose.h>
+
+#include <hintids.hxx>
+#include <fmtanchr.hxx>
+#include <fmtflcnt.hxx>
+#include <txtflcnt.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <dcontact.hxx>
+#include <viewsh.hxx>
+#include <frameformats.hxx>
+#include <textboxhelper.hxx>
+
+struct SwUndoGroupObjImpl
+{
+ SwDrawFrameFormat* pFormat;
+ SdrObject* pObj;
+ SwNodeOffset 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& rDoc )
+ : SwUndo( SwUndoId::DRAWUNDO, &rDoc ), 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_SaveAnchor( SwFrameFormat* pFormat, SwNodeOffset& 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())))
+ return;
+
+ rNodePos = rAnchor.GetAnchorNode()->GetIndex();
+ sal_Int32 nContentPos = 0;
+
+ if (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId())
+ {
+ nContentPos = rAnchor.GetAnchorContentOffset();
+
+ // 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();
+ SwContentIndex aIdx( pTextNd, nContentPos );
+ pTextNd->EraseText( aIdx, 1 );
+ }
+ }
+ else if (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())
+ {
+ nContentPos = rAnchor.GetAnchorContentOffset();
+ }
+
+ pFormat->SetFormatAttr( SwFormatAnchor( rAnchor.GetAnchorId(), nContentPos ) );
+}
+
+static void lcl_RestoreAnchor( SwFrameFormat* pFormat, SwNodeOffset 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())))
+ return;
+
+ 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.SetContent( 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& rDoc )
+ : SwUndo( SwUndoId::DRAWGROUP, &rDoc ), 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);
+
+ // This will store the textboxes what were owned by this group
+ std::vector<std::pair<SdrObject*, SwFrameFormat*>> vTextBoxes;
+ if (auto pOldTextBoxNode = pFormat->GetOtherTextBoxFormats())
+ {
+ if (auto pChildren = pObj->getChildrenOfSdrObject())
+ {
+ for (const rtl::Reference<SdrObject>& pChild : *pChildren)
+ {
+ if (auto pTextBox = pOldTextBoxNode->GetTextBox(pChild.get()))
+ vTextBoxes.push_back(std::pair(pChild.get(), pTextBox));
+ }
+ }
+ }
+
+ ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx );
+
+ pFormat->RemoveAllUnos();
+
+ // remove from array
+ SwDoc* pDoc = pFormat->GetDoc();
+ sw::SpzFrameFormats& 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(static_cast<sw::SpzFrameFormat*>(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 );
+
+ for (auto& rElem : vTextBoxes)
+ {
+ if (rElem.first == pObj)
+ {
+ auto pNewTextBoxNode = std::make_shared<SwTextBoxNode>(SwTextBoxNode(rSave.pFormat));
+ rSave.pFormat->SetOtherTextBoxFormats(pNewTextBoxNode);
+ pNewTextBoxNode->AddTextBox(rElem.first, rElem.second);
+ rElem.second->SetOtherTextBoxFormats(pNewTextBoxNode);
+ break;
+ }
+ }
+
+ 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();
+ sw::SpzFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+
+ // This will store the textboxes from the ex-group-shapes
+ std::vector<std::pair<SdrObject*, SwFrameFormat*>> vTextBoxes;
+
+ 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));
+
+ // Save the textboxes
+ if (auto pOldTextBoxNode = rSave.pFormat->GetOtherTextBoxFormats())
+ {
+ if (auto pTextBox = pOldTextBoxNode->GetTextBox(pObj))
+ vTextBoxes.push_back(std::pair(pObj, pTextBox));
+ }
+
+ // object will destroy itself
+ pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ ::lcl_SaveAnchor( rSave.pFormat, rSave.nNodeIdx );
+
+ rSave.pFormat->RemoveAllUnos();
+
+ 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(static_cast<sw::SpzFrameFormat*>(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;
+
+ // Restore the textboxes
+ if (vTextBoxes.size())
+ {
+ auto pNewTextBoxNode = std::make_shared<SwTextBoxNode>(SwTextBoxNode(m_pObjArray[0].pFormat));
+ for (auto& rElem : vTextBoxes)
+ {
+ pNewTextBoxNode->AddTextBox(rElem.first, rElem.second);
+ rElem.second->SetOtherTextBoxFormats(pNewTextBoxNode);
+ }
+ m_pObjArray[0].pFormat->SetOtherTextBoxFormats(pNewTextBoxNode);
+ }
+
+ // #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 );
+
+ pFormat->RemoveAllUnos();
+
+ // remove from array
+ sw::SpzFrameFormats& 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& rDoc )
+ : SwUndo( SwUndoId::DRAWUNGROUP, &rDoc ), m_bDeleteFormat( false )
+{
+ m_nSize = o3tl::narrowing<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 );
+
+ pFormat->RemoveAllUnos();
+
+ // remove from array
+ sw::SpzFrameFormats& 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();
+ sw::SpzFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+
+ // This will store the textboxes what were owned by this group
+ std::vector<std::pair<SdrObject*, SwFrameFormat*>> vTextBoxes;
+
+ // remove from array
+ for( sal_uInt16 n = 1; n < m_nSize; ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+
+ ::lcl_SaveAnchor( rSave.pFormat, rSave.nNodeIdx );
+
+ // copy the textboxes for later use to this vector
+ if (auto pTxBxNd = rSave.pFormat->GetOtherTextBoxFormats())
+ {
+ if (auto pGroupObj = m_pObjArray[0].pObj)
+ {
+ if (auto pChildren = pGroupObj->getChildrenOfSdrObject())
+ {
+ for (const rtl::Reference<SdrObject>& pChild : *pChildren)
+ {
+ if (auto pTextBox = pTxBxNd->GetTextBox(pChild.get()))
+ vTextBoxes.push_back(std::pair(pChild.get(), pTextBox));
+ }
+ }
+ }
+ }
+
+ rSave.pFormat->RemoveAllUnos();
+
+ 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(static_cast<sw::SpzFrameFormat*>(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;
+
+ // Restore the vector content for the new formats
+ if (vTextBoxes.size())
+ {
+ auto pNewTxBxNd = std::make_shared<SwTextBoxNode>( SwTextBoxNode(m_pObjArray[0].pFormat));
+ for (auto& rElem : vTextBoxes)
+ {
+ pNewTxBxNd->AddTextBox(rElem.first, rElem.second);
+ rElem.second->SetOtherTextBoxFormats(pNewTxBxNd);
+ }
+ m_pObjArray[0].pFormat->SetOtherTextBoxFormats(pNewTxBxNd);
+ }
+
+
+ // #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 );
+
+ // Store the textboxes in this vector for later use.
+ std::vector<std::pair<SdrObject*, SwFrameFormat*>> vTextBoxes;
+ if (auto pTextBoxNode = pFormat->GetOtherTextBoxFormats())
+ {
+ auto pMasterObj = m_pObjArray[0].pObj;
+
+ if (auto pObjList = pMasterObj->getChildrenOfSdrObject())
+ for (const rtl::Reference<SdrObject>& pObj : *pObjList)
+ {
+ vTextBoxes.push_back(std::pair(pObj.get(), pTextBoxNode->GetTextBox(pObj.get())));
+ }
+ }
+
+ pFormat->RemoveAllUnos();
+
+ // remove from array
+ SwDoc* pDoc = pFormat->GetDoc();
+ sw::SpzFrameFormats& 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(static_cast<sw::SpzFrameFormat*>(rSave.pFormat));
+
+ SwDrawFrameFormat* pDrawFrameFormat = rSave.pFormat;
+
+ // Restore the textboxes for the restored group shape.
+ for (auto& pElem : vTextBoxes)
+ {
+ if (pElem.first == rSave.pObj)
+ {
+ auto pTmpTxBxNd = std::make_shared<SwTextBoxNode>(SwTextBoxNode(rSave.pFormat));
+ pTmpTxBxNd->AddTextBox(rSave.pObj, pElem.second);
+ pFormat->SetOtherTextBoxFormats(pTmpTxBxNd);
+ pElem.second->SetOtherTextBoxFormats(pTmpTxBxNd);
+ break;
+ }
+ }
+
+ // #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& rDoc)
+ : SwUndo( SwUndoId::DRAWUNGROUP, &rDoc )
+{
+}
+
+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& rDoc )
+ : SwUndo( SwUndoId::DRAWDELETE, &rDoc ), 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;
+ sw::SpzFrameFormats& 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(static_cast<sw::SpzFrameFormat*>(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;
+ if (pDrawFrameFormat->GetOtherTextBoxFormats())
+ {
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(
+ SwTextBoxHelper::changeAnchor, pDrawFrameFormat, pObj);
+ }
+
+ // #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;
+ sw::SpzFrameFormats& 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 );
+
+ pFormat->RemoveAllUnos();
+
+ 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 );
+
+ pFormat->RemoveAllUnos();
+
+ // remove from array
+ SwDoc* pDoc = pFormat->GetDoc();
+ sw::SpzFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+
+ m_pMarkList->InsertEntry( rMark );
+}
+
+void SwUndoDrawDelete::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoDrawDelete"));
+
+ for (size_t i = 0; i < m_pMarkList->GetMarkCount(); ++i)
+ {
+ SwUndoGroupObjImpl& rObj = m_pObjArray[i];
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoGroupObjImpl"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(i).getStr()));
+
+ if (rObj.pFormat)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pFormat"));
+ rObj.pFormat->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* 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 0000000000..7ea43d0d5f
--- /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 0000000000..bf6c2877b2
--- /dev/null
+++ b/sw/source/core/undo/unins.cxx
@@ -0,0 +1,1035 @@
+/* -*- 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 <editeng/keepitem.hxx>
+#include <svx/svdobj.hxx>
+#include <osl/diagnose.h>
+
+#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 <swcrsr.hxx>
+#include <swtable.hxx>
+#include <redline.hxx>
+#include <docary.hxx>
+#include <acorrect.hxx>
+
+#include <strings.hrc>
+#include <utility>
+
+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 SwNode & rNd)
+{
+ // consider Redline
+ m_pDoc = const_cast<SwDoc*>(&rNd.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 SwNode& rNd, sal_Int32 nCnt,
+ sal_Int32 nL,
+ const SwInsertFlags nInsertFlags,
+ bool bWDelim )
+ : SwUndo(SwUndoId::TYPING, &rNd.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 SwNode& rNd )
+ : SwUndo(SwUndoId::SPLITNODE, &rNd.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::Insert 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.GetNodeIndex() &&
+ m_nContent == rPos.GetContentIndex() )
+ {
+ // consider Redline
+ SwDoc& rDoc = rPos.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 SwContentNode* pIReg = rPos.GetContentNode();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ const SwPosition& rIdx = *pRedl->End();
+ if( pIReg == rIdx.GetContentNode() &&
+ m_nContent == rIdx.GetContentIndex() )
+ {
+ if( !pRedl->HasMark() || !m_pRedlData ||
+ *pRedl != *m_pRedlData || *pRedl != aRData )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+SwUndoInsert::~SwUndoInsert()
+{
+ if (m_oUndoNodeIndex) // delete the section from UndoNodes array
+ {
+ // Insert saves the content in IconSection
+ SwNodes& rUNds = m_oUndoNodeIndex->GetNodes();
+ rUNds.Delete(*m_oUndoNodeIndex,
+ rUNds.GetEndOfExtras().GetIndex() - m_oUndoNodeIndex->GetIndex());
+ m_oUndoNodeIndex.reset();
+ }
+ else // the inserted text
+ {
+ maText.reset();
+ }
+ m_pRedlData.reset();
+}
+
+void SwUndoInsert::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pTmpDoc = & rContext.GetDoc();
+ SwCursor *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ if( m_bIsAppend )
+ {
+ pPam->GetPoint()->Assign(m_nNode);
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ pPam->SetMark();
+ pPam->Move( fnMoveBackward );
+ pPam->Exchange();
+ pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( *pPam, true, RedlineType::Any );
+ }
+ pPam->DeleteMark();
+ pTmpDoc->getIDocumentContentOperations().DelFullPara( *pPam );
+ pPam->GetPoint()->SetContent( 0 );
+ }
+ else
+ {
+ SwNodeOffset 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()->AdjustContent( - 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()->GetContentIndex(),
+ aPaM.GetMark()->GetContentIndex());
+ pTextNode->DeleteAttributes(RES_TXTATR_CHARFMT,
+ aPaM.GetPoint()->GetContentIndex(),
+ aPaM.GetMark()->GetContentIndex());
+ }
+ RemoveIdxFromRange( aPaM, false );
+ maText = pTextNode->GetText().copy(m_nContent-m_nLen, m_nLen);
+ pTextNode->EraseText( *aPaM.GetPoint(), 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()->GetNodeIndex();
+ nCnt = aPaM.GetPoint()->GetContentIndex();
+
+ if (!maText)
+ {
+ m_oUndoNodeIndex.emplace(m_pDoc->GetNodes().GetEndOfContent());
+ MoveToUndoNds(aPaM, &*m_oUndoNodeIndex);
+ }
+ m_nNode = aPaM.GetPoint()->GetNodeIndex();
+ m_nContent = aPaM.GetPoint()->GetContentIndex();
+ }
+
+ // set cursor to Undo range
+ pPam->DeleteMark();
+
+ pPam->GetPoint()->Assign( nNd, nCnt );
+ }
+
+ maUndoText.reset();
+}
+
+void SwUndoInsert::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pTmpDoc = & rContext.GetDoc();
+ SwCursor *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ pPam->DeleteMark();
+
+ if( m_bIsAppend )
+ {
+ pPam->GetPoint()->Assign( 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()->Assign( m_nNode );
+ SwContentNode *const pCNd =
+ pPam->GetPoint()->GetNode().GetContentNode();
+ pPam->GetPoint()->SetContent( m_nContent );
+
+ if( m_nLen )
+ {
+ ::std::optional<SwNodeIndex> oMvBkwrd = MovePtBackward(*pPam);
+
+ if (maText)
+ {
+ SwTextNode *const pTextNode = pCNd->GetTextNode();
+ OSL_ENSURE( pTextNode, "where is my textnode ?" );
+ OUString const ins(
+ pTextNode->InsertText( *maText, *pPam->GetMark(),
+ 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_oUndoNodeIndex!)
+ SwNodeOffset const nMvNd = m_oUndoNodeIndex->GetIndex();
+ m_oUndoNodeIndex.reset();
+ MoveFromUndoNds(*pTmpDoc, nMvNd, *pPam->GetMark());
+ }
+ m_nNode = pPam->GetMark()->GetNodeIndex();
+ m_nContent = pPam->GetMark()->GetContentIndex();
+
+ MovePtForward(*pPam, ::std::move(oMvBkwrd));
+ 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.GetPointContentNode();
+ }
+
+// 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;
+}
+
+bool SwUndoInsert::IsIndependent(const SwUndoInsert& rOther) const
+{
+ return m_nNode != rOther.m_nNode;
+}
+
+class SwUndoReplace::Impl
+ : private SwUndoSaveContent
+{
+ OUString m_sOld;
+ OUString m_sIns;
+ SwNodeOffset 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 aIns, bool const bRegExp);
+
+ 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)
+ + ShortenString(sOld, nUndoStringLength, SwResId(STR_LDOTS))
+ + SwResId(STR_END_QUOTE);
+ aResult.AddRule(UndoArg3, aTmpStr);
+ }
+ else if (1 == occurrences)
+ {
+ {
+ // #i33488 #
+ OUString aTmpStr =
+ SwResId(STR_START_QUOTE)
+ + ShortenString(sOld, nUndoStringLength, SwResId(STR_LDOTS))
+ + SwResId(STR_END_QUOTE);
+ aResult.AddRule(UndoArg1, aTmpStr);
+ }
+
+ aResult.AddRule(UndoArg2, SwResId(STR_YIELDS));
+
+ {
+ // #i33488 #
+ OUString aTmpStr =
+ SwResId(STR_START_QUOTE)
+ + ShortenString(sNew, nUndoStringLength, SwResId(STR_LDOTS))
+ + 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 aIns, bool const bRegExp)
+ : m_sIns(std::move( aIns ))
+ , m_nOffset( 0 )
+ , m_bRegExp(bRegExp)
+{
+
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+
+ m_nSttNd = m_nEndNd = pStt->GetNodeIndex();
+ m_nSttCnt = pStt->GetContentIndex();
+ m_nSelEnd = m_nEndCnt = pEnd->GetContentIndex();
+
+ m_bSplitNext = m_nSttNd != pEnd->GetNodeIndex();
+
+ SwTextNode* pNd = pStt->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();
+
+ SwNodeOffset nNewPos = pStt->GetNodeIndex();
+ 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->AddColl(pNd->GetTextColl(), nNewPos, SwNodeType::Text);
+
+ SwTextNode* pNext = pEnd->GetNode().GetTextNode();
+ SwNodeOffset 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->AddColl(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->GetContentIndex();
+ m_sOld = pNd->GetText().copy( m_nSttCnt, nECnt - m_nSttCnt );
+}
+
+void SwUndoReplace::Impl::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwCursor & 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, 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()->Assign(*pNd, m_nSttCnt );
+ rPam.SetMark();
+ rPam.GetPoint()->Assign( m_nSttNd - m_nOffset, 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()->GetContentIndex() == rPam.GetMark()->GetNode().GetTextNode()->Len());
+ rPam.GetPoint()->Assign( m_nEndNd - m_nOffset, 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 )
+ {
+ assert(m_nSttCnt + m_sOld.getLength() <= pNd->Len());
+ SwPosition aPos(*pNd, m_nSttCnt + m_sOld.getLength());
+ 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()->Assign( m_nSttNd, m_nSttCnt );
+}
+
+void SwUndoReplace::Impl::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwCursor & rPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+ rPam.DeleteMark();
+ rPam.GetPoint()->Assign( m_nSttNd, m_nSttCnt );
+
+ rPam.SetMark();
+ if( m_bSplitNext )
+ rPam.GetPoint()->Assign( m_nSttNd + 1 );
+ rPam.GetPoint()->SetContent( 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->GetNodeIndex();
+ m_nEndCnt = pEnd->GetContentIndex();
+}
+
+SwUndoReRead::SwUndoReRead( const SwPaM& rPam, const SwGrfNode& rGrfNd )
+ : SwUndo( SwUndoId::REREAD, &rPam.GetDoc() ), mnPosition( rPam.GetPoint()->GetNodeIndex() )
+{
+ 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::optional<Graphic> oOldGrf(moGraphic);
+ 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(), oOldGrf ? &*oOldGrf : nullptr );
+ }
+
+ 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);
+ moGraphic.reset();
+ }
+ else
+ {
+ moGraphic.emplace( rGrfNd.GetGrf(true) );
+ maNm.reset();
+ maFltr.reset();
+ }
+ mnMirror = rGrfNd.GetSwAttrSet().GetMirrorGrf().GetValue();
+}
+
+SwUndoInsertLabel::SwUndoInsertLabel( const SwLabelType eTyp,
+ OUString aText,
+ OUString aSeparator,
+ OUString aNumberSeparator,
+ const bool bBef,
+ const sal_uInt16 nInitId,
+ OUString aCharacterStyle,
+ const bool bCpyBorder,
+ const SwDoc* pDoc )
+ : SwUndo( SwUndoId::INSERTLABEL, pDoc ),
+ m_sText(std::move( aText )),
+ m_sSeparator(std::move( aSeparator )),
+ m_sNumberSeparator(std::move( aNumberSeparator )),//#i61007# order of captions
+ m_sCharacterStyle(std::move( aCharacterStyle )),
+ 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()->Assign( NODE.nNode );
+ aPam.SetMark();
+ aPam.GetPoint()->Assign( NODE.nNode + 1 );
+ NODE.pUndoInsNd = new SwUndoDelete(aPam, SwDeleteFlags::Default, 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();
+
+ SwNodeOffset nIdx(0);
+
+ SwContentNode* pCNd = rPos.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)
+ + ShortenString(rStr, nUndoStringLength, SwResId(STR_LDOTS))
+ + 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, SwNodeOffset(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 0000000000..0ea6443f61
--- /dev/null
+++ b/sw/source/core/undo/unmove.cxx
@@ -0,0 +1,305 @@
+/* -*- 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.GetNodeIndex())
+ , m_nDestStartContent(0)
+ , m_nDestEndContent(0)
+ , m_nInsPosContent(0)
+ , m_nMoveDestContent(rMvPos.GetContentIndex())
+ , m_bJoinNext(false)
+ , m_bMoveRange(false)
+ , m_bMoveRedlines(false)
+{
+ // get StartNode from footnotes before delete!
+ SwDoc& rDoc = rRange.GetDoc();
+ SwTextNode* pTextNd = rDoc.GetNodes()[ m_nSttNode ]->GetTextNode();
+ SwTextNode* pEndTextNd = rDoc.GetNodes()[ m_nEndNode ]->GetTextNode();
+
+ m_pHistory.reset( new SwHistory );
+
+ if( pTextNd )
+ {
+ m_pHistory->AddColl(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->AddColl(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.GetNode().GetTextNode();
+ if (nullptr != pTextNd)
+ {
+ m_pHistory->AddColl(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& rDoc, const SwNodeRange& rRg,
+ const SwNode& rMvPos )
+ : SwUndo(SwUndoId::MOVE, &rDoc)
+ , 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?
+ SwNodeOffset nContentStt = rDoc.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.SetContent( pCNd->Len() );
+ SwPosition aMkPos( rRg.aStart );
+
+ DelContentIndex( aMkPos, aPtPos, DelContentType::Ftn );
+
+ if( m_pHistory && !m_pHistory->Count() )
+ m_pHistory.reset();
+ }
+
+ m_nFootnoteStart = 0;
+}
+
+void SwUndoMove::SetDestRange( const SwNode& rStt,
+ const SwNode& 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.GetNode(),
+ 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.GetNode().GetContentNode();
+ aPos.SetContent( 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.GetPointNode().IsContentNode() )
+ aPam.GetPointNode().GetContentNode()->ResetAllAttr();
+ // the Pam will be dropped now
+ }
+
+ SwTextNode* pTextNd = aIdx.GetNode().GetTextNode();
+ if( m_bJoinNext )
+ {
+ {
+ RemoveIdxRel( aIdx.GetIndex() + 1,
+ SwPosition( aIdx, 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, m_nEndNode );
+ rDoc.getIDocumentContentOperations().MoveNodeRange( aRg, aIdx.GetNode(), m_bMoveRedlines
+ ? SwMoveFlags::REDLINES
+ : SwMoveFlags::DEFAULT );
+ }
+ else
+ {
+ SwPaM aPam(*rPam.GetPoint());
+ SetPaM( aPam );
+ SwPosition aMvPos( aIdx, aIdx.GetNode().GetContentNode(), m_nMoveDestContent );
+
+ DelFootnote( aPam );
+ RemoveIdxFromRange( aPam, false );
+
+ aIdx = aPam.Start()->GetNode();
+ 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( *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& rDoc = rRange.GetDoc();
+ SwNodeOffset nContentStt = rDoc.GetNodes().GetEndOfAutotext().GetIndex();
+ if( m_nMoveDestNode < nContentStt &&
+ rRange.GetPoint()->GetNodeIndex() >= 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 0000000000..1251635a31
--- /dev/null
+++ b/sw/source/core/undo/unnum.cxx
@@ -0,0 +1,397 @@
+/* -*- 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 <utility>
+#include <osl/diagnose.h>
+
+SwUndoInsNum::SwUndoInsNum( const SwNumRule& rOldRule,
+ const SwNumRule& rNewRule,
+ const SwDoc& rDoc,
+ SwUndoId nUndoId)
+ : SwUndo( nUndoId, &rDoc ),
+ 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,
+ OUString aReplaceRule )
+ : SwUndo( SwUndoId::INSNUM, &rPos.GetNode().GetDoc() ),
+ m_aNumRule( rRule ),
+ m_sReplaceRule(std::move( aReplaceRule )), m_nLRSavePos( 0 )
+{
+ // No selection!
+ m_nEndNode = SwNodeOffset(0);
+ m_nEndContent = COMPLETE_STRING;
+ m_nSttNode = rPos.GetNodeIndex();
+ m_nSttContent = rPos.GetContentIndex();
+}
+
+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 )
+{
+ if (m_nEndNode > m_nSttNode)
+ m_aNodes.reserve( std::min<sal_Int32>(sal_Int32(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, SwNodeOffset nOff, bool bIsOutlMv )
+ : SwUndo( bIsOutlMv ? SwUndoId::OUTLINE_UD : SwUndoId::MOVENUM, &rPam.GetDoc() ),
+ SwUndRng( rPam ),
+ m_nNewStart( 0 ), m_nOffset( nOff )
+{
+ // nOffset: Down => 1
+ // Up => -1
+}
+
+void SwUndoMoveNum::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwNodeOffset nTmpStt = m_nSttNode, nTmpEnd = m_nEndNode;
+
+ if (m_nEndNode || m_nEndContent != COMPLETE_STRING) // section?
+ {
+ if( m_nNewStart < m_nSttNode ) // moved forwards
+ m_nEndNode = m_nEndNode - ( m_nSttNode - m_nNewStart );
+ else
+ m_nEndNode = m_nEndNode + ( m_nNewStart - m_nSttNode );
+ }
+ m_nSttNode = m_nNewStart;
+
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().MoveParagraph( rPam, -m_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, m_nOffset, SwUndoId::OUTLINE_UD == GetId());
+}
+
+void SwUndoMoveNum::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if( SwUndoId::OUTLINE_UD == GetId() )
+ {
+ rDoc.MoveOutlinePara(rContext.GetRepeatPaM(),
+ SwNodeOffset(0) < m_nOffset ? 1 : -1 );
+ }
+ else
+ {
+ rDoc.MoveParagraph(rContext.GetRepeatPaM(), m_nOffset);
+ }
+}
+
+SwUndoNumUpDown::SwUndoNumUpDown( const SwPaM& rPam, short nOff )
+ : SwUndo( nOff > 0 ? SwUndoId::NUMUP : SwUndoId::NUMDOWN, &rPam.GetDoc() ),
+ SwUndRng( rPam ),
+ m_nOffset( nOff )
+{
+ // nOffset: Down => 1
+ // Up => -1
+}
+
+void SwUndoNumUpDown::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().NumUpDown(rPam, 1 != m_nOffset );
+}
+
+void SwUndoNumUpDown::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().NumUpDown(rPam, 1 == m_nOffset);
+}
+
+void SwUndoNumUpDown::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().NumUpDown(rContext.GetRepeatPaM(), 1 == m_nOffset);
+}
+
+SwUndoNumOrNoNum::SwUndoNumOrNoNum( const SwNode& rIdx, bool bOldNum,
+ bool bNewNum)
+ : SwUndo( SwUndoId::NUMORNONUM, &rIdx.GetDoc() ),
+ m_nIndex( rIdx.GetIndex() ), mbNewNum(bNewNum),
+ mbOldNum(bOldNum)
+{
+}
+
+// #115901#, #i40034#
+void SwUndoNumOrNoNum::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwNodeIndex aIdx( rContext.GetDoc().GetNodes(), m_nIndex );
+ SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
+
+ if (nullptr != pTextNd)
+ {
+ pTextNd->SetCountedInList(mbOldNum);
+ }
+}
+
+// #115901#, #i40034#
+void SwUndoNumOrNoNum::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwNodeIndex aIdx( rContext.GetDoc().GetNodes(), m_nIndex );
+ 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()->GetNode());
+ }
+ else if ( ! mbOldNum && mbNewNum )
+ {
+ rDoc.NumOrNoNum(rContext.GetRepeatPaM().GetPoint()->GetNode(), true);
+ }
+}
+
+SwUndoNumRuleStart::SwUndoNumRuleStart( const SwPosition& rPos, bool bFlg )
+ : SwUndo( SwUndoId::SETNUMRULESTART, &rPos.GetDoc() ),
+ m_nIndex( rPos.GetNodeIndex() ), m_nOldStart( USHRT_MAX ),
+ m_nNewStart( USHRT_MAX ), m_bSetStartValue( false ), m_bFlag( bFlg )
+{
+}
+
+SwUndoNumRuleStart::SwUndoNumRuleStart( const SwPosition& rPos, sal_uInt16 nStt )
+ : SwUndo(SwUndoId::SETNUMRULESTART, &rPos.GetDoc())
+ , m_nIndex(rPos.GetNodeIndex())
+ , m_nOldStart(USHRT_MAX)
+ , m_nNewStart(nStt)
+ , m_bSetStartValue(true)
+ , m_bFlag(false)
+{
+ SwTextNode* pTextNd = rPos.GetNode().GetTextNode();
+ if ( pTextNd )
+ {
+ if ( pTextNd->HasAttrListRestartValue() )
+ {
+ m_nOldStart = o3tl::narrowing<sal_uInt16>(pTextNd->GetAttrListRestartValue());
+ }
+ else
+ {
+ m_nOldStart = 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()[ m_nIndex ] );
+ if( m_bSetStartValue )
+ {
+ rDoc.SetNodeNumStart( aPos, m_nOldStart );
+ }
+ else
+ {
+ rDoc.SetNumRuleStart( aPos, !m_bFlag );
+ }
+}
+
+void SwUndoNumRuleStart::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPosition const aPos( *rDoc.GetNodes()[ m_nIndex ] );
+ if( m_bSetStartValue )
+ {
+ rDoc.SetNodeNumStart( aPos, m_nNewStart );
+ }
+ else
+ {
+ rDoc.SetNumRuleStart( aPos, m_bFlag );
+ }
+}
+
+void SwUndoNumRuleStart::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if( m_bSetStartValue )
+ {
+ rDoc.SetNodeNumStart(*rContext.GetRepeatPaM().GetPoint(), m_nNewStart);
+ }
+ else
+ {
+ rDoc.SetNumRuleStart(*rContext.GetRepeatPaM().GetPoint(), m_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 0000000000..2144c7dd78
--- /dev/null
+++ b/sw/source/core/undo/unoutl.cxx
@@ -0,0 +1,73 @@
+/* -*- 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);
+}
+
+
+SwUndoOutlineEdit::SwUndoOutlineEdit(const SwNumRule& rOldRule, const SwNumRule& rNewRule,
+ const SwDoc& rDoc)
+ : SwUndo(SwUndoId::OUTLINE_EDIT, &rDoc)
+ , m_aNewNumRule(rNewRule)
+ , m_aOldNumRule(rOldRule)
+{
+}
+
+void SwUndoOutlineEdit::UndoImpl(::sw::UndoRedoContext& rContext)
+{
+ rContext.GetDoc().SetOutlineNumRule(m_aOldNumRule);
+}
+
+void SwUndoOutlineEdit::RedoImpl(::sw::UndoRedoContext& rContext)
+{
+ rContext.GetDoc().SetOutlineNumRule(m_aNewNumRule);
+}
+
+void SwUndoOutlineEdit::RepeatImpl(::sw::RepeatContext& rContext)
+{
+ rContext.GetDoc().SetOutlineNumRule(m_aNewNumRule);
+}
+
+/* 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 0000000000..7d14c89779
--- /dev/null
+++ b/sw/source/core/undo/unovwr.cxx
@@ -0,0 +1,475 @@
+/* -*- 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 <swcrsr.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>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+
+SwUndoOverwrite::SwUndoOverwrite( SwDoc& rDoc, SwPosition& rPos,
+ sal_Unicode cIns )
+ : SwUndo(SwUndoId::OVERWRITE, &rDoc),
+ m_bGroup( false )
+{
+ SwTextNode *const pTextNd = rPos.GetNode().GetTextNode();
+ assert(pTextNd);
+ sal_Int32 const nTextNdLen = pTextNd->GetText().getLength();
+
+ m_nStartNode = rPos.GetNodeIndex();
+ m_nStartContent = rPos.GetContentIndex();
+
+ if( !rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPaM aPam( rPos.GetNode(), rPos.GetContentIndex(),
+ rPos.GetNode(), rPos.GetContentIndex()+1 );
+ m_pRedlSaveData.reset( new SwRedlineSaveDatas );
+ if( !FillSaveData( aPam, *m_pRedlSaveData, false ))
+ {
+ m_pRedlSaveData.reset();
+ }
+ if (m_nStartContent < nTextNdLen)
+ {
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Any);
+ }
+ }
+
+ m_bInsChar = true;
+ if( m_nStartContent < nTextNdLen ) // no pure insert?
+ {
+ m_aDelStr += OUStringChar( pTextNd->GetText()[m_nStartContent] );
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ SwRegHistory aRHst( *pTextNd, m_pHistory.get() );
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nStartNode, 0,
+ nTextNdLen, false );
+ rPos.AdjustContent(+1);
+ m_bInsChar = false;
+ }
+
+ bool bOldExpFlg = pTextNd->IsIgnoreDontExpand();
+ pTextNd->SetIgnoreDontExpand( true );
+
+ pTextNd->InsertText( OUString(cIns), rPos, SwInsertFlags::EMPTYEXPAND );
+ m_aInsStr += OUStringChar( cIns );
+
+ if( !m_bInsChar )
+ {
+ const SwContentIndex aTmpIndex( rPos.GetContentNode(), rPos.GetContentIndex() - 2 );
+ pTextNd->EraseText( aTmpIndex, 1 );
+ }
+ pTextNd->SetIgnoreDontExpand( bOldExpFlg );
+
+ m_bCacheComment = false;
+}
+
+SwUndoOverwrite::~SwUndoOverwrite()
+{
+}
+
+bool SwUndoOverwrite::CanGrouping( SwDoc& rDoc, SwPosition& rPos,
+ sal_Unicode cIns )
+{
+// What is with only inserted characters?
+
+ // Only deletion of single chars can be combined.
+ if( rPos.GetNodeIndex() != m_nStartNode || m_aInsStr.isEmpty() ||
+ ( !m_bGroup && m_aInsStr.getLength() != 1 ))
+ return false;
+
+ // Is the node a TextNode at all?
+ SwTextNode * pDelTextNd = rPos.GetNode().GetTextNode();
+ if( !pDelTextNd ||
+ (pDelTextNd->GetText().getLength() != rPos.GetContentIndex() &&
+ rPos.GetContentIndex() != ( m_nStartContent + m_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( m_aInsStr, m_aInsStr.getLength()-1 ) )
+ return false;
+
+ if (!m_bInsChar && rPos.GetContentIndex() < pDelTextNd->GetText().getLength())
+ {
+ SwRedlineSaveDatas aTmpSav;
+ SwPaM aPam( rPos.GetNode(), rPos.GetContentIndex(),
+ rPos.GetNode(), rPos.GetContentIndex()+1 );
+
+ const bool bSaved = FillSaveData( aPam, aTmpSav, false );
+
+ bool bOk = ( !m_pRedlSaveData && !bSaved ) ||
+ ( m_pRedlSaveData && bSaved &&
+ SwUndo::CanRedlineGroup( *m_pRedlSaveData, aTmpSav,
+ m_nStartContent > rPos.GetContentIndex() ));
+ // aTmpSav.DeleteAndDestroyAll();
+ if( !bOk )
+ return false;
+
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, false, RedlineType::Any );
+ }
+
+ // both 'overwrites' can be combined so 'move' the corresponding character
+ if( !m_bInsChar )
+ {
+ if (rPos.GetContentIndex() < pDelTextNd->GetText().getLength())
+ {
+ m_aDelStr += OUStringChar( pDelTextNd->GetText()[rPos.GetContentIndex()] );
+ rPos.AdjustContent(+1);
+ }
+ else
+ m_bInsChar = true;
+ }
+
+ bool bOldExpFlg = pDelTextNd->IsIgnoreDontExpand();
+ pDelTextNd->SetIgnoreDontExpand( true );
+
+ OUString const ins( pDelTextNd->InsertText(OUString(cIns), rPos,
+ SwInsertFlags::EMPTYEXPAND) );
+ assert(ins.getLength() == 1); // check in SwDoc::Overwrite => cannot fail
+ (void) ins;
+ m_aInsStr += OUStringChar( cIns );
+
+ if( !m_bInsChar )
+ {
+ const SwContentIndex aTmpIndex( rPos.GetContentNode(), rPos.GetContentIndex() - 2 );
+ pDelTextNd->EraseText( aTmpIndex, 1 );
+ }
+ pDelTextNd->SetIgnoreDontExpand( bOldExpFlg );
+
+ m_bGroup = true;
+ return true;
+}
+
+void SwUndoOverwrite::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwCursor& rCurrentPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ rCurrentPam.DeleteMark();
+ rCurrentPam.GetPoint()->Assign( m_nStartNode );
+ SwTextNode* pTextNd = rCurrentPam.GetPointNode().GetTextNode();
+ assert(pTextNd);
+ SwPosition& rPtPos = *rCurrentPam.GetPoint();
+ rPtPos.SetContent( m_nStartContent );
+
+ SwAutoCorrExceptWord* pACEWord = rDoc.GetAutoCorrExceptWord();
+ if( pACEWord )
+ {
+ if( 1 == m_aInsStr.getLength() && 1 == m_aDelStr.getLength() )
+ pACEWord->CheckChar( *rCurrentPam.GetPoint(), m_aDelStr[0] );
+ rDoc.SetAutoCorrExceptWord( nullptr );
+ }
+
+ // If there was not only an overwrite but also an insert, delete the surplus
+ if( m_aInsStr.getLength() > m_aDelStr.getLength() )
+ {
+ rPtPos.AdjustContent( m_aDelStr.getLength() );
+ pTextNd->EraseText( rPtPos, m_aInsStr.getLength() - m_aDelStr.getLength() );
+ rPtPos.SetContent( m_nStartContent );
+ }
+
+ if( !m_aDelStr.isEmpty() )
+ {
+ bool bOldExpFlg = pTextNd->IsIgnoreDontExpand();
+ pTextNd->SetIgnoreDontExpand( true );
+
+ rPtPos.AdjustContent(+1);
+ for( sal_Int32 n = 0; n < m_aDelStr.getLength(); n++ )
+ {
+ // do it individually, to keep the attributes!
+ OUString aTmpStr(m_aDelStr[n]);
+ OUString const ins( pTextNd->InsertText(aTmpStr, rPtPos) );
+ assert(ins.getLength() == 1); // cannot fail
+ (void) ins;
+ rPtPos.AdjustContent(-2);
+ pTextNd->EraseText( rPtPos, 1 );
+ rPtPos.AdjustContent(+2);
+ }
+ pTextNd->SetIgnoreDontExpand( bOldExpFlg );
+ rPtPos.AdjustContent(-1);
+ }
+
+ if( m_pHistory )
+ {
+ if( pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( false );
+ m_pHistory->TmpRollback( &rDoc, 0, false );
+ }
+
+ if( rCurrentPam.GetMark()->GetContentIndex() != m_nStartContent )
+ {
+ rCurrentPam.SetMark();
+ rCurrentPam.GetMark()->SetContent( m_nStartContent );
+ }
+
+ if( m_pRedlSaveData )
+ SetSaveData( rDoc, *m_pRedlSaveData );
+}
+
+void SwUndoOverwrite::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM& rCurrentPam = rContext.GetRepeatPaM();
+ if( m_aInsStr.isEmpty() || rCurrentPam.HasMark() )
+ return;
+
+ SwDoc & rDoc = rContext.GetDoc();
+
+ {
+ ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().Overwrite(rCurrentPam, OUString(m_aInsStr[0]));
+ }
+ for( sal_Int32 n = 1; n < m_aInsStr.getLength(); ++n )
+ rDoc.getIDocumentContentOperations().Overwrite(rCurrentPam, OUString(m_aInsStr[n]));
+}
+
+void SwUndoOverwrite::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwCursor& rCurrentPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ rCurrentPam.DeleteMark();
+ rCurrentPam.GetPoint()->Assign(m_nStartNode);
+ SwTextNode* pTextNd = rCurrentPam.GetPointNode().GetTextNode();
+ assert(pTextNd);
+ SwPosition& rPtPos = *rCurrentPam.GetPoint();
+
+ if( m_pRedlSaveData )
+ {
+ rPtPos.SetContent( m_nStartContent );
+ rCurrentPam.SetMark();
+ rCurrentPam.GetMark()->AdjustContent( m_aDelStr.getLength() );
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( rCurrentPam, false, RedlineType::Any );
+ rCurrentPam.DeleteMark();
+ }
+ rPtPos.SetContent( !m_aDelStr.isEmpty() ? m_nStartContent+1 : m_nStartContent );
+
+ bool bOldExpFlg = pTextNd->IsIgnoreDontExpand();
+ pTextNd->SetIgnoreDontExpand( true );
+
+ for( sal_Int32 n = 0; n < m_aInsStr.getLength(); n++ )
+ {
+ // do it individually, to keep the attributes!
+ OUString const ins(
+ pTextNd->InsertText( OUString(m_aInsStr[n]), rPtPos,
+ SwInsertFlags::EMPTYEXPAND) );
+ assert(ins.getLength() == 1); // cannot fail
+ (void) ins;
+ if( n < m_aDelStr.getLength() )
+ {
+ rPtPos.AdjustContent(-2);
+ pTextNd->EraseText( rPtPos, 1 );
+ rPtPos.AdjustContent( n+1 < m_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( rCurrentPam.GetMark()->GetContentIndex() != m_nStartContent )
+ {
+ rCurrentPam.SetMark();
+ rCurrentPam.GetMark()->SetContent( m_nStartContent );
+ }
+}
+
+SwRewriter SwUndoOverwrite::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ OUString aString = SwResId(STR_START_QUOTE) +
+ ShortenString(m_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::optional<Sequence< sal_Int32 >> oOffsets;
+ SwNodeOffset nNdIdx;
+ sal_Int32 nStart, nLen;
+
+ UndoTransliterate_Data( SwNodeOffset nNd, sal_Int32 nStt, sal_Int32 nStrLen, OUString aText )
+ : sText(std::move( aText )),
+ 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 ), m_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 = m_aChanges.size() - 1; i >= 0; --i)
+ m_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(), m_nType );
+ rDoc.getIDocumentContentOperations().TransliterateText( rPam, aTrans );
+}
+
+void SwUndoTransliterate::AddChanges( SwTextNode& rTNd,
+ sal_Int32 nStart, sal_Int32 nLen,
+ uno::Sequence <sal_Int32> const & rOffsets )
+{
+ tools::Long nOffsLen = rOffsets.getLength();
+ UndoTransliterate_Data* pNew = new UndoTransliterate_Data(
+ rTNd.GetIndex(), nStart, static_cast<sal_Int32>(nOffsLen),
+ rTNd.GetText().copy(nStart, nLen));
+
+ m_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( tools::Long n = 0; n < nOffsLen; ++n, ++p )
+ if( *p != ( nStart + n ))
+ {
+ // create the Offset array
+ pNew->oOffsets.emplace( nLen );
+ sal_Int32* pIdx = pNew->oOffsets->getArray();
+ p = pOffsets;
+ tools::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 < m_aChanges.size(); ++i) // check all changes but not the current one
+ {
+ UndoTransliterate_Data* pD = m_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 )
+ return;
+
+ Sequence <sal_Int32> aOffsets( oOffsets ? oOffsets->getLength() : nLen );
+ if( oOffsets )
+ aOffsets = *oOffsets;
+ 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 0000000000..f77ceb3d26
--- /dev/null
+++ b/sw/source/core/undo/unredln.cxx
@@ -0,0 +1,602 @@
+/* -*- 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 <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <mvsave.hxx>
+#include <rolbck.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, sal_Int8 nDepth )
+ : SwUndo( SwUndoId::REDLINE, &rRange.GetDoc() ), SwUndRng( rRange ),
+ mnUserId( nUsrId ),
+ mbHiddenRedlines( false ),
+ mnDepth( nDepth )
+{
+ // 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() );
+ }
+
+ SwNodeOffset 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;
+}
+
+#if OSL_DEBUG_LEVEL > 0
+void SwUndoRedline::SetRedlineCountDontCheck(bool bCheck)
+{
+ if (mpRedlSaveData)
+ mpRedlSaveData->SetRedlineCountDontCheck(bCheck);
+}
+#endif
+
+void SwUndoRedline::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwPaM& rPam(AddUndoRedoPaM(rContext));
+
+ // fix PaM for deletions shown in margin
+ bool bIsDeletion = dynamic_cast<SwUndoRedlineDelete*>(this);
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ sal_uInt32 nMaxId = SAL_MAX_UINT32;
+ if ( bIsDeletion && rTable.size() > 0 )
+ {
+ // Nodes of the deletion range are in the newest invisible redlines.
+ // Set all redlines visible and recover the original deletion range.
+ for (SwNodeOffset nNodes(0); nNodes < m_nEndNode - m_nSttNode + 1; ++nNodes)
+ {
+ SwRedlineTable::size_type nCurRedlinePos = 0;
+ SwRangeRedline * pRedline(rTable[nCurRedlinePos]);
+
+ // search last but nNodes redline by its nth biggest id
+ for( SwRedlineTable::size_type n = 1; n < rTable.size(); ++n )
+ {
+ SwRangeRedline *pRed(rTable[n]);
+ if ( !pRed->HasMark() && pRedline->GetId() < pRed->GetId() && pRed->GetId() < nMaxId )
+ {
+ nCurRedlinePos = n;
+ pRedline = pRed;
+ }
+ }
+
+ nMaxId = pRedline->GetId();
+
+ if ( !pRedline->IsVisible() && !pRedline->HasMark() )
+ {
+ // set it visible
+ pRedline->Show(0, rTable.GetPos(pRedline), /*bForced=*/true);
+ pRedline->Show(1, rTable.GetPos(pRedline), /*bForced=*/true);
+
+ // extend the range
+ if ( nNodes == SwNodeOffset(0) )
+ rPam = *pRedline;
+ else
+ {
+ rPam.SetMark();
+ *rPam.GetMark() = *pRedline->GetMark();
+ }
+ }
+ }
+ }
+
+ UndoRedlineImpl(rDoc, rPam);
+
+ if( mpRedlSaveData )
+ {
+ SwNodeOffset 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 ( bIsDeletion )
+ {
+ 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 )
+ {
+ SwNodeOffset 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 const nUsrId, SwDeleteFlags const flags)
+ : 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.GetPointNode().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()->GetContentIndex();
+ }
+ }
+
+ m_bCacheComment = false;
+ if (flags & SwDeleteFlags::ArtificialSelection)
+ {
+ InitHistory(rRange);
+ }
+}
+
+void SwUndoRedlineDelete::InitHistory(SwPaM const& rRedline)
+{
+ m_pHistory.reset(new SwHistory);
+ // try to rely on direction of rPam here so it works for
+ // backspacing/deleting consecutive characters
+ SaveFlyArr flys;
+ SaveFlyInRange(rRedline, *rRedline.GetMark(), flys, false, m_pHistory.get());
+ RestFlyInRange(flys, *rRedline.GetPoint(), &rRedline.GetPoint()->GetNode(), true);
+ if (m_pHistory->Count())
+ {
+ m_bCanGroup = false; // how to group history?
+ }
+}
+
+// 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);
+ if (m_pHistory)
+ {
+ m_pHistory->TmpRollback(&rDoc, 0);
+ }
+}
+
+void SwUndoRedlineDelete::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ if (rPam.GetPoint() != rPam.GetMark())
+ {
+ if (m_pHistory) // if it was created before, it must be recreated now
+ {
+ rPam.Normalize(m_bIsBackspace); // to check the correct edge
+ InitHistory(rPam);
+ }
+ 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
+
+ auto [pStart, pEnd] = rPam.StartEnd(); // SwPosition*
+
+ SwNodeIndex aPrevIdx( pStart->GetNode(), -1 );
+ SwNodeOffset nOffsetTemp = pEnd->GetNodeIndex() - pStart->GetNodeIndex();
+
+ 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()->GetNode() );
+ aTmp.SetMark();
+ aTmp.GetPoint()->Assign( m_nSaveEndNode, m_nSaveEndContent );
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( aTmp, true, RedlineType::Any );
+ }
+
+ rDoc.getIDocumentContentOperations().DelFullPara(rPam);
+
+ SwPaM *const pPam = & rPam;
+ pPam->DeleteMark();
+ pPam->GetPoint()->Assign( aPrevIdx.GetNode(), SwNodeOffset(+1) );
+ pPam->SetMark();
+
+ pPam->GetPoint()->Adjust(nOffsetTemp);
+ SwContentNode* pCNd = pPam->GetPointContentNode();
+ pPam->GetPoint()->SetContent( pCNd->Len() );
+
+ SetValues( *pPam );
+
+ SetPaM(rPam);
+}
+
+void SwUndoRedlineSort::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ SwPaM* pPam = &rPam;
+ auto [pStart, pEnd] = pPam->StartEnd(); // SwPosition*
+
+ SwNodeIndex aPrevIdx( pStart->GetNode(), -1 );
+ SwNodeOffset nOffsetTemp = pEnd->GetNodeIndex() - pStart->GetNodeIndex();
+ const sal_Int32 nCntStt = pStart->GetContentIndex();
+
+ rDoc.SortText(rPam, *m_pOpt);
+
+ pPam->DeleteMark();
+ pPam->GetPoint()->Assign( aPrevIdx.GetNode(), SwNodeOffset(+1) );
+ SwContentNode* pCNd = pPam->GetPointContentNode();
+ sal_Int32 nLen = pCNd->Len();
+ if( nLen > nCntStt )
+ nLen = nCntStt;
+ pPam->GetPoint()->SetContent( nLen );
+ pPam->SetMark();
+
+ pPam->GetPoint()->Adjust(nOffsetTemp);
+ pCNd = pPam->GetPointContentNode();
+ pPam->GetPoint()->SetContent( pCNd->Len() );
+
+ SetValues( rPam );
+
+ SetPaM( rPam );
+ rPam.GetPoint()->Assign( m_nSaveEndNode, 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.GetNodeIndex();
+ m_nSaveEndContent = rPos.GetContentIndex();
+}
+
+SwUndoAcceptRedline::SwUndoAcceptRedline( const SwPaM& rRange, sal_Int8 nDepth /* = 0 */ )
+ : SwUndoRedline( SwUndoId::ACCEPT_REDLINE, rRange, nDepth )
+{
+}
+
+void SwUndoAcceptRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ rDoc.getIDocumentRedlineAccess().AcceptRedline(rPam, false, mnDepth);
+}
+
+void SwUndoAcceptRedline::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().getIDocumentRedlineAccess().AcceptRedline(rContext.GetRepeatPaM(), true);
+}
+
+SwUndoRejectRedline::SwUndoRejectRedline( const SwPaM& rRange, sal_Int8 nDepth /* = 0 */ )
+ : SwUndoRedline( SwUndoId::REJECT_REDLINE, rRange, nDepth )
+{
+}
+
+void SwUndoRejectRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ rDoc.getIDocumentRedlineAccess().RejectRedline(rPam, false, mnDepth);
+}
+
+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& rDoc = rRg.GetDoc();
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ RedlineType eTyp = m_bInsert ? RedlineType::Insert : RedlineType::Delete;
+ m_pRedlineData.reset( new SwRedlineData( eTyp, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( rDoc.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& rDoc = rRedl.GetDoc();
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlineData.reset( new SwRedlineData( rRedl.GetRedlineData() ) );
+ SetRedlineFlags( rDoc.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.GetMarkContentNode();
+ SwContentNode* pCEndNd = rPam.GetPointContentNode();
+
+ // 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, SwDeleteFlags::Default, false));
+
+ if( bJoinText )
+ sw_JoinText(rPam, bJoinPrev);
+
+ if( pCSttNd && !pCEndNd)
+ {
+ // #112139# Do not step behind the end of content.
+ SwNode & rTmp = rPam.GetPointNode();
+ SwNode * pEnd = rDoc.GetNodes().DocumentSectionEndNode(&rTmp);
+
+ if (&rTmp != pEnd)
+ {
+ rPam.SetMark();
+ rPam.GetPoint()->Adjust(SwNodeOffset(1));
+ m_pUndoDelete2.reset(new SwUndoDelete(rPam, SwDeleteFlags::Default, 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 0000000000..4e75aa9adb
--- /dev/null
+++ b/sw/source/core/undo/unsect.cxx
@@ -0,0 +1,613 @@
+/* -*- 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 <editeng/protitem.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::optional<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::optional<SfxItemSet> oAttr;
+ if( rSect.GetFormat() )
+ {
+ sal_uInt16 nCnt = 1;
+ if( rSect.IsProtect() )
+ ++nCnt;
+
+ if( nCnt < rSect.GetFormat()->GetAttrSet().Count() )
+ {
+ oAttr.emplace( rSect.GetFormat()->GetAttrSet() );
+ oAttr->ClearItem( RES_PROTECT );
+ oAttr->ClearItem( RES_CNTNT );
+ if( !oAttr->Count() )
+ {
+ oAttr.reset();
+ }
+ }
+ }
+ return oAttr;
+}
+
+SwUndoInsSection::SwUndoInsSection(
+ SwPaM const& rPam, SwSectionData const& rNewData,
+ SfxItemSet const*const pSet,
+ std::tuple<SwTOXBase const*, sw::RedlineMode, sw::FieldmarkMode, sw::ParagraphBreakMode> const*const pTOXBase)
+ : SwUndo( SwUndoId::INSSECTION, &rPam.GetDoc() ), SwUndRng( rPam )
+ , m_pSectionData(new SwSectionData(rNewData))
+ , m_pAttrSet( (pSet && pSet->Count()) ? new SfxItemSet( *pSet ) : nullptr )
+ , m_nSectionNodePos(0)
+ , m_bSplitAtStart(false)
+ , m_bSplitAtEnd(false)
+ , m_bUpdateFootnote(false)
+{
+ if (pTOXBase)
+ m_xTOXBase.emplace(
+ std::make_unique<SwTOXBase>(*std::get<0>(*pTOXBase)),
+ std::get<1>(*pTOXBase),
+ std::get<2>(*pTOXBase),
+ std::get<3>(*pTOXBase));
+
+ 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() )
+ return;
+
+ const SwContentNode* pCNd = rPam.GetPoint()->GetNode().GetContentNode();
+ if( pCNd && pCNd->HasSwAttrSet() && (
+ !rPam.GetPoint()->GetContentIndex() ||
+ rPam.GetPoint()->GetContentIndex() == 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.GetNode() );
+ }
+
+ 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_xTOXBase)
+ {
+ SwRootFrame const* pLayout(nullptr);
+ SwRootFrame * pLayoutToReset(nullptr);
+ sw::FieldmarkMode eFieldmarkMode{};
+ sw::ParagraphBreakMode eParagraphBreakMode{};
+ comphelper::ScopeGuard g([&]() {
+ if (pLayoutToReset)
+ {
+ pLayoutToReset->SetHideRedlines(std::get<1>(*m_xTOXBase) == sw::RedlineMode::Shown);
+ pLayoutToReset->SetFieldmarkMode(eFieldmarkMode, eParagraphBreakMode);
+ }
+ });
+ o3tl::sorted_vector<SwRootFrame *> layouts(rDoc.GetAllLayouts());
+ for (SwRootFrame const*const p : layouts)
+ {
+ if ((std::get<1>(*m_xTOXBase) == sw::RedlineMode::Hidden) == p->IsHideRedlines()
+ && std::get<2>(*m_xTOXBase) == p->GetFieldmarkMode()
+ && std::get<3>(*m_xTOXBase) == p->GetParagraphBreakMode())
+ {
+ pLayout = p;
+ break;
+ }
+ }
+ if (!pLayout)
+ {
+ assert(!layouts.empty()); // must have one layout
+ pLayoutToReset = *layouts.begin();
+ eFieldmarkMode = pLayoutToReset->GetFieldmarkMode();
+ eParagraphBreakMode = pLayoutToReset->GetParagraphBreakMode();
+ pLayoutToReset->SetHideRedlines(std::get<1>(*m_xTOXBase) == sw::RedlineMode::Hidden);
+ pLayoutToReset->SetFieldmarkMode(std::get<2>(*m_xTOXBase), std::get<3>(*m_xTOXBase));
+ pLayout = pLayoutToReset;
+ }
+ pUpdateTOX = rDoc.InsertTableOf( *rPam.GetPoint(),
+ // don't expand: will be done by SwUndoUpdateIndex::RedoImpl()
+ *std::get<0>(*m_xTOXBase), 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, SwNodeOffset(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, SwNodeOffset(1) );
+ rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+
+ if( pUpdateTOX )
+ {
+ // initiate formatting
+ if (SwEditShell* pESh = rDoc.GetEditShell())
+ pESh->CalcLayout();
+
+ // insert page numbers
+ const_cast<SwTOXBaseSection*>(pUpdateTOX)->UpdatePageNum();
+ }
+}
+
+void SwUndoInsSection::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if (m_xTOXBase)
+ {
+ rDoc.InsertTableOf(*rContext.GetRepeatPaM().GetPoint(),
+ *std::get<0>(*m_xTOXBase), 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, SwNodeOffset nNode )
+{
+ SwNodeIndex aIdx( rDoc.GetNodes(), nNode );
+ SwTextNode* pTextNd = aIdx.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "Where is my TextNode?" );
+
+ {
+ RemoveIdxRel(
+ nNode + 1,
+ SwPosition( aIdx, pTextNd, pTextNd->GetText().getLength() ) );
+ }
+ pTextNd->JoinNext();
+
+ if (m_pHistory)
+ pTextNd->RstTextAttr( 0, 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::optional<SfxItemSet> const m_oAttrSet;
+ std::shared_ptr< ::sfx2::MetadatableUndo > const m_pMetadataUndo;
+ SwNodeOffset const m_nStartNode;
+ SwNodeOffset 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_oAttrSet( ::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_oAttrSet ? &*m_oAttrSet : nullptr);
+ }
+ else
+ {
+ SwNodeIndex aStt( rDoc.GetNodes(), m_nStartNode );
+ SwNodeIndex aEnd( rDoc.GetNodes(), m_nEndNode-2 );
+ SwSectionFormat* pFormat = rDoc.MakeSectionFormat();
+ if (m_oAttrSet)
+ {
+ pFormat->SetFormatAttr( *m_oAttrSet );
+ }
+
+ /// OD 04.10.2002 #102894#
+ /// remember inserted section node for further calculations
+ SwSectionNode* pInsertedSectNd = rDoc.GetNodes().InsertTextSection(
+ aStt.GetNode(), *pFormat, *m_pSectionData, nullptr, & aEnd.GetNode() );
+
+ if( SfxItemState::SET == pFormat->GetItemState( RES_FTN_AT_TXTEND ) ||
+ SfxItemState::SET == pFormat->GetItemState( RES_END_AT_TXTEND ))
+ {
+ rDoc.GetFootnoteIdxs().UpdateFootnote( aStt.GetNode() );
+ }
+
+ /// 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(), SAL_MAX_INT32);
+ 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::optional<SfxItemSet> m_oAttrSet;
+ SwNodeOffset 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_oAttrSet( ::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::optional<SfxItemSet> oCur = ::lcl_GetAttrSet( rNdSect );
+ if (m_oAttrSet)
+ {
+ // The Content and Protect items must persist
+ m_oAttrSet->Put( pFormat->GetFormatAttr( RES_CNTNT ));
+ if( const SvxProtectItem* pItem = pFormat->GetItemIfSet( RES_PROTECT ))
+ {
+ m_oAttrSet->Put( *pItem );
+ }
+ pFormat->DelDiffs( *m_oAttrSet );
+ m_oAttrSet->ClearItem( RES_CNTNT );
+ pFormat->SetFormatAttr( *m_oAttrSet );
+ }
+ 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 );
+ }
+ if (oCur)
+ m_oAttrSet.emplace(std::move(*oCur));
+ else
+ m_oAttrSet.reset();
+
+ if (m_bOnlyAttrChanged)
+ return;
+
+ 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)
+{
+#ifndef NDEBUG
+ SwNodeIndex const tmp(rFormat.GetDoc()->GetNodes(), m_nStartIndex); // title inserted before empty node
+ assert(tmp.GetNode().IsSectionNode());
+ assert(tmp.GetNode().GetSectionNode()->GetSection().GetFormat() == &rFormat);
+#endif
+ 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(
+ *rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode(),
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT));
+ m_pSaveSectionUpdated->SaveSection(SwNodeRange(first, last), false);
+ m_pSaveSectionOriginal->RestoreSection(&rDoc, first.GetNode(), true);
+ // delete before restoring nested undo, so its node indexes match
+ SwNodeIndex const del(*pDeletionPrevention);
+ SwDoc::CorrAbs(del, del, SwPosition(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode(), SwNodeOffset(0)), 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(
+ *rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode(),
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT));
+ m_pSaveSectionOriginal->SaveSection(SwNodeRange(first, last), false);
+ m_pSaveSectionUpdated->RestoreSection(&rDoc, first.GetNode(), true);
+ // delete before restoring nested undo, so its node indexes match
+ SwNodeIndex const del(*pDeletionPrevention);
+ SwDoc::CorrAbs(del, del, SwPosition(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode(), SwNodeOffset(0)), 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 0000000000..bf188918a4
--- /dev/null
+++ b/sw/source/core/undo/unsort.cxx
@@ -0,0 +1,236 @@
+/* -*- 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>
+
+SwUndoSort::SwUndoSort(const SwPaM& rRg, const SwSortOptions& rOpt)
+ : SwUndo(SwUndoId::SORT_TXT, &rRg.GetDoc())
+ , SwUndRng(rRg)
+ , m_nTableNode(0)
+{
+ m_pSortOptions.reset( new SwSortOptions(rOpt) );
+}
+
+SwUndoSort::SwUndoSort( SwNodeOffset nStt, SwNodeOffset nEnd, const SwTableNode& rTableNd,
+ const SwSortOptions& rOpt, bool bSaveTable )
+ : SwUndo(SwUndoId::SORT_TBL, &rTableNd.GetDoc())
+{
+ m_nSttNode = nStt;
+ m_nEndNode = nEnd;
+ m_nTableNode = rTableNd.GetIndex();
+
+ m_pSortOptions.reset( new SwSortOptions(rOpt) );
+ if( bSaveTable )
+ m_pUndoAttrTable.reset( new SwUndoAttrTable( rTableNd ) );
+}
+
+SwUndoSort::~SwUndoSort()
+{
+ m_pSortOptions.reset();
+ m_pUndoAttrTable.reset();
+}
+
+void SwUndoSort::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if(m_pSortOptions->bTable)
+ {
+ // Undo for Table
+ RemoveIdxFromSection( rDoc, m_nSttNode, &m_nEndNode );
+
+ if( m_pUndoAttrTable )
+ {
+ m_pUndoAttrTable->UndoImpl(rContext);
+ }
+
+ SwTableNode* pTableNd = rDoc.GetNodes()[ m_nTableNode ]->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 (std::unique_ptr<SwSortUndoElement> const& pElement : m_SortList)
+ {
+ const SwTableBox* pSource = rTable.GetTableBox(pElement->maSourceString);
+ const SwTableBox* pTarget = rTable.GetTableBox(pElement->maTargetString);
+
+ // 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 SwNodeOffset 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 (std::unique_ptr<SwSortUndoElement> const& pElement : m_SortList)
+ {
+ if (pElement->mnSourceNodeOffset == (m_nSttNode + SwNodeOffset(i)))
+ {
+ aIdxList.push_back( SwNodeIndex(rDoc.GetNodes(), pElement->mnTargetNodeOffset));
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < m_SortList.size(); ++i)
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode + SwNodeOffset(i) );
+ SwNodeRange aRg( aIdxList[i], SwNodeOffset(0), aIdxList[i], SwNodeOffset(1) );
+ rDoc.getIDocumentContentOperations().MoveNodeRange(aRg, aIdx.GetNode(),
+ SwMoveFlags::DEFAULT);
+ }
+ // delete indices
+ aIdxList.clear();
+ SetPaM(rPam, true);
+ }
+}
+
+void SwUndoSort::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if(m_pSortOptions->bTable)
+ {
+ // Redo for Table
+ RemoveIdxFromSection( rDoc, m_nSttNode, &m_nEndNode );
+
+ SwTableNode* pTableNd = rDoc.GetNodes()[ m_nTableNode ]->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 (std::unique_ptr<SwSortUndoElement> const& pElement: m_SortList)
+ {
+ const SwTableBox* pSource = rTable.GetTableBox(pElement->maSourceString);
+ const SwTableBox* pTarget = rTable.GetTableBox(pElement->maTargetString);
+
+ // move back
+ MoveCell(&rDoc, pSource, pTarget,
+ USHRT_MAX != aMovedList.GetPos( pTarget ) );
+ // store moved entry in list
+ aMovedList.push_back( pSource );
+ }
+
+ if( m_pUndoAttrTable )
+ {
+ m_pUndoAttrTable->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 SwNodeOffset 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]->mnSourceNodeOffset) );
+ }
+
+ for (size_t i = 0; i < m_SortList.size(); ++i)
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode + SwNodeOffset(i));
+ SwNodeRange aRg( aIdxList[i], SwNodeOffset(0), aIdxList[i], SwNodeOffset(1) );
+ rDoc.getIDocumentContentOperations().MoveNodeRange(aRg, aIdx.GetNode(),
+ SwMoveFlags::DEFAULT);
+ }
+ // delete indices
+ aIdxList.clear();
+ SetPaM(rPam, true);
+ SwTextNode const*const pTNd = rPam.GetPointNode().GetTextNode();
+ if( pTNd )
+ {
+ rPam.GetPoint()->SetContent( pTNd->GetText().getLength() );
+ }
+ }
+}
+
+void SwUndoSort::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // table not repeat capable
+ if(!m_pSortOptions->bTable)
+ {
+ SwPaM *const pPam = & rContext.GetRepeatPaM();
+ SwDoc& rDoc = pPam->GetDoc();
+
+ if( !SwDoc::IsInTable( pPam->Start()->GetNode() ) )
+ rDoc.SortText(*pPam, *m_pSortOptions);
+ }
+}
+
+void SwUndoSort::Insert( const OUString& rOrgPos, const OUString& rNewPos)
+{
+ m_SortList.push_back(std::make_unique<SwSortUndoElement>(rOrgPos, rNewPos));
+}
+
+void SwUndoSort::Insert( SwNodeOffset nOrgPos, SwNodeOffset 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 0000000000..8dc6533032
--- /dev/null
+++ b/sw/source/core/undo/unspnd.cxx
@@ -0,0 +1,192 @@
+/* -*- 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 <swcrsr.hxx>
+#include <swundo.hxx>
+#include <frmfmt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <redline.hxx>
+#include <docary.hxx>
+#include <fmtpdsc.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <osl/diagnose.h>
+#include <editeng/formatbreakitem.hxx>
+
+// SPLITNODE
+
+SwUndoSplitNode::SwUndoSplitNode( SwDoc& rDoc, const SwPosition& rPos,
+ bool bChkTable )
+ : SwUndo( SwUndoId::SPLITNODE, &rDoc ), m_nNode( rPos.GetNodeIndex() ),
+ m_nContent( rPos.GetContentIndex() ),
+ m_bTableFlag( false ), m_bCheckTableStart( bChkTable )
+{
+ SwTextNode *const pTextNd = rPos.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "only for TextNode" );
+ if( pTextNd->GetpSwpHints() )
+ {
+ m_pHistory.reset(new SwHistory);
+ m_pHistory->CopyAttr(pTextNd->GetpSwpHints(), m_nNode, 0,
+ pTextNd->GetText().getLength(), false );
+ if (!m_pHistory->Count())
+ {
+ m_pHistory.reset();
+ }
+ }
+ // consider Redline
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlineData.reset( new SwRedlineData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ m_nParRsid = pTextNd->GetParRsid();
+}
+
+SwUndoSplitNode::~SwUndoSplitNode()
+{
+ m_pHistory.reset();
+ m_pRedlineData.reset();
+}
+
+void SwUndoSplitNode::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwCursor & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ rPam.DeleteMark();
+ if( m_bTableFlag )
+ {
+ // than a TextNode was added directly before the current table
+ SwPosition& rPos = *rPam.GetPoint();
+ rPos.Assign(m_nNode);
+ SwTextNode* pTNd;
+ SwNode* pCurrNd = pDoc->GetNodes()[ m_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 )
+ {
+ if( const SwFormatPageDesc* pItem = pNdSet->GetItemIfSet( RES_PAGEDESC, false ) )
+ pTableFormat->SetFormatAttr( *pItem );
+
+ if( const SvxFormatBreakItem* pItem = pNdSet->GetItemIfSet( RES_BREAK, false ) )
+ pTableFormat->SetFormatAttr( *pItem );
+ }
+
+ // than delete it again
+ SwNodeIndex aDelNd( *pTableNd, -1 );
+ RemoveIdxRel( aDelNd.GetIndex(), *rPam.GetPoint() );
+ pDoc->GetNodes().Delete( aDelNd );
+ }
+ }
+ else
+ {
+ SwTextNode * pTNd = pDoc->GetNodes()[ m_nNode ]->GetTextNode();
+ if( pTNd )
+ {
+ rPam.GetPoint()->Assign(*pTNd, pTNd->GetText().getLength());
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ rPam.SetMark();
+ rPam.GetMark()->Adjust(SwNodeOffset(1));
+ pDoc->getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any );
+ rPam.DeleteMark();
+ }
+
+ RemoveIdxRel( m_nNode+1, *rPam.GetPoint() );
+
+ pTNd->JoinNext();
+ if (m_pHistory)
+ {
+ rPam.GetPoint()->SetContent(0);
+ rPam.SetMark();
+ rPam.GetPoint()->SetContent(pTNd->GetText().getLength());
+
+ pDoc->RstTextAttrs( rPam, true );
+ m_pHistory->TmpRollback( pDoc, 0, false );
+ }
+
+ pDoc->UpdateParRsid( pTNd, m_nParRsid );
+ }
+ }
+
+ // also set the cursor onto undo section
+ rPam.DeleteMark();
+ rPam.GetPoint()->Assign( m_nNode, m_nContent );
+}
+
+void SwUndoSplitNode::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwCursor & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ rPam.GetPoint()->Assign(m_nNode);
+ SwTextNode * pTNd = rPam.GetPointNode().GetTextNode();
+ OSL_ENSURE(pTNd, "SwUndoSplitNode::RedoImpl(): SwTextNode expected");
+ if (!pTNd)
+ return;
+
+ rPam.GetPoint()->SetContent( m_nContent );
+
+ SwDoc& rDoc = rPam.GetDoc();
+ rDoc.getIDocumentContentOperations().SplitNode( *rPam.GetPoint(), m_bCheckTableStart );
+
+ if (m_pHistory)
+ {
+ m_pHistory->SetTmpEnd(m_pHistory->Count());
+ }
+
+ if( !(( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) ||
+ ( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )))
+ return;
+
+ rPam.SetMark();
+ if( rPam.Move( fnMoveBackward ))
+ {
+ 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, rPam ), true);
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else
+ rDoc.getIDocumentRedlineAccess().SplitRedline( rPam );
+ rPam.Exchange();
+ }
+ rPam.DeleteMark();
+}
+
+void SwUndoSplitNode::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().getIDocumentContentOperations().SplitNode(
+ *rContext.GetRepeatPaM().GetPoint(), m_bCheckTableStart );
+}
+
+/* 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 0000000000..72f1c809e2
--- /dev/null
+++ b/sw/source/core/undo/untbl.cxx
@@ -0,0 +1,3179 @@
+/* -*- 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 <libxml/xmlwriter.h>
+
+#include <UndoTable.hxx>
+#include <UndoRedline.hxx>
+#include <UndoDelete.hxx>
+#include <UndoSplitMove.hxx>
+#include <UndoCore.hxx>
+#include <fesh.hxx>
+#include <fmtpdsc.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 <IDocumentLayoutAccess.hxx>
+#include <rootfrm.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 <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 <editeng/formatbreakitem.hxx>
+#include <osl/diagnose.h>
+#include <docsh.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
+{
+ SwNodeOffset 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 );
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+};
+
+namespace {
+
+class SaveBox;
+class SaveLine;
+
+void KillEmptyFrameFormat(SwFrameFormat& rFormat)
+{
+ if(!rFormat.HasWriterListeners())
+ delete &rFormat;
+};
+
+}
+
+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;
+ SwFrameFormat& CreateNewFormat(SwFrameFormat& rFormat, sal_uInt16 nFormatPos);
+
+public:
+ SaveTable( const SwTable& rTable, sal_uInt16 nLnCnt = USHRT_MAX,
+ bool bSaveFormula = true );
+
+ sal_uInt16 AddFormat( SwFrameFormat* pFormat, bool bIsLine );
+ void NewFrameFormatForLine(const SwTableLine&, sal_uInt16 nFormatPos, SwFrameFormat* pOldFormat);
+ void NewFrameFormatForBox(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* m_pNext;
+ SaveBox* m_pBox;
+ sal_uInt16 m_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* m_pNext;
+ SwNodeOffset m_nStartNode;
+ sal_Int32 m_nRowSpan;
+ sal_uInt16 m_nItemSet;
+ union
+ {
+ SfxItemSets* pContentAttrs;
+ SaveLine* pLine;
+ } m_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
+{
+ SwNodeOffset m_nSttNd;
+ SwNodeOffset 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, SwNodeOffset nNd, SwNodeOffset nEndIdx, sal_Int32 nContent );
+
+private:
+ SwTableToTextSave(const SwTableToTextSave&) = delete;
+ SwTableToTextSave& operator=(const SwTableToTextSave&) = delete;
+
+};
+
+WhichRangesContainer const aSave_BoxContentSet(svl::Items<
+ 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>);
+
+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.GetNodeIndex() ), m_nRows( nRw ), m_nColumns( nCl ), m_nAdjust( nAdj )
+{
+ if( pColArr )
+ {
+ m_oColumnWidth.emplace( *pColArr );
+ }
+ if( pTAFormat )
+ m_pAutoFormat.reset( new SwTableAutoFormat( *pTAFormat ) );
+
+ // consider redline
+ SwDoc& rDoc = rPos.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_oColumnWidth.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();
+ // tdf#159025 skip undo if SwTableNode is a nullptr
+ // I don't know what causes the SwTableNode to be a nullptr in the
+ // case of tdf#159025, but at least stop the crashing by skipping
+ // this undo request.
+ SAL_WARN_IF( !pTableNd, "sw.core", "no TableNode" );
+ if( !pTableNd )
+ return;
+
+ 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();
+
+ if( const SwFormatPageDesc* pItem = pTableFormat->GetItemIfSet( RES_PAGEDESC,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( const SvxFormatBreakItem* pItem = pTableFormat->GetItemIfSet( RES_BREAK,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+
+ ::sw::NotifyTableCollapsedParagraph(pNextNd, nullptr);
+ }
+
+ 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()->Assign(aIdx);
+}
+
+void SwUndoInsTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwEditShell *const pEditShell(rDoc.GetEditShell());
+ OSL_ENSURE(pEditShell, "SwUndoInsTable::RedoImpl needs a SwEditShell!");
+ if (!pEditShell)
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwPosition const aPos(rDoc.GetNodes(), m_nStartNode);
+ const SwTable* pTable = rDoc.InsertTable( m_aInsTableOptions, aPos, m_nRows, m_nColumns,
+ m_nAdjust,
+ m_pAutoFormat.get(),
+ m_oColumnWidth ? &*m_oColumnWidth : nullptr );
+ pEditShell->MoveTable( GotoPrevTable, fnTableStart );
+ static_cast<SwFrameFormat*>(pTable->GetFrameFormat())->SetFormatName( 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() )))
+ return;
+
+ SwPaM aPam( *pTableNode->EndOfSectionNode(), *pTableNode, SwNodeOffset(1) );
+
+ 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_oColumnWidth ? &*m_oColumnWidth : nullptr );
+}
+
+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, SwNodeOffset nNd, SwNodeOffset 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->AddColl(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();
+ SwNodeOffset nTableStt = pTableNd->GetIndex(), nTableEnd = pTableNd->EndOfSectionIndex();
+
+ for(sw::SpzFrameFormat* pFormat: *pTableNd->GetDoc().GetSpzFrameFormats())
+ {
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) &&
+ nTableStt <= pAnchorNode->GetIndex() &&
+ pAnchorNode->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()->Assign( aFrameIdx );
+ pPam->SetMark();
+ pPam->GetPoint()->Assign( 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()->Assign( *pTableNd->EndOfSectionNode() );
+ pPam->SetMark();
+ pPam->GetPoint()->Assign( *pPam->GetPointNode().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( SwNodeOffset nSttNd, SwNodeOffset nEndNd,
+ const SwTableToTextSaves& rSavedData )
+{
+ SwNodeIndex aSttIdx( *this, nSttNd );
+ SwNodeIndex aEndIdx( *this, nEndNd+1 );
+
+ SwTableNode * pTableNd = new SwTableNode( aSttIdx.GetNode() );
+ SwEndNode* pEndNd = new SwEndNode( aEndIdx.GetNode(), *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;
+ {
+ SwNodeOffset n, nTmpEnd = aEndIdx.GetIndex();
+ for( n = pTableNd->GetIndex() + 1; n < nTmpEnd; ++n )
+ {
+ pNd = (*this)[n];
+ if (pNd->IsContentNode())
+ {
+ static_cast<SwContentNode*>(pNd)->DelFrames(nullptr);
+ }
+ // tdf#147938 reset merge flag in nodes
+ pNd->SetRedlineMergeFlag(SwNode::Merge::None);
+ 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 );
+
+ 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?" );
+ SwContentIndex aCntPos( pTextNd, pSave->m_nContent - 1 );
+
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save(GetDoc(), aSttIdx.GetIndex(), aCntPos.GetIndex());
+
+ pTextNd->EraseText( aCntPos, 1 );
+
+ std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
+ [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode, bool)
+ {
+ if (!pContentStore->Empty())
+ {
+ pContentStore->Restore(*pNewNode, pSave->m_nContent, pSave->m_nContent + 1, eMode);
+ }
+ });
+ pTextNd->SplitContentNode(
+ SwPosition(aSttIdx, aCntPos), &restoreFunc);
+ }
+
+ 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.GetNode(), SwNodeType::Start,
+ SwTableBoxStartNode );
+ pSttNd->m_pStartOfSection = pTableNd;
+ new SwEndNode( aEndIdx.GetNode(), *pSttNd );
+
+ for( SwNodeOffset 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()->Assign( m_nStartNode );
+ SwNodeIndex aSaveIdx( pPam->GetPoint()->GetNode(), -1 );
+
+ pPam->SetMark(); // log off all indices
+ pPam->DeleteMark();
+
+ SwTableNode* pTableNd = pPam->GetPointNode().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()->Assign( aSaveIdx );
+
+ pPam->SetMark(); // log off all indices
+ pPam->DeleteMark();
+}
+
+void SwUndoTableToText::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM *const pPam = & rContext.GetRepeatPaM();
+ SwTableNode *const pTableNd = pPam->GetPointNode().FindTableNode();
+ if( pTableNd )
+ {
+ // move cursor out of table
+ pPam->GetPoint()->Assign( *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, SwNodeOffset nNdIdx, SwNodeOffset 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->GetContentIndex() && ( pEnd->GetContentIndex()
+ != pEnd->GetNode().GetContentNode()->Len() ||
+ pEnd->GetNodeIndex() >= rNds.GetEndOfContent().GetIndex()-1 );
+}
+
+SwUndoTextToTable::~SwUndoTextToTable()
+{
+ m_pAutoFormat.reset();
+}
+
+void SwUndoTextToTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwNodeOffset 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?
+ if( m_nSttContent )
+ {
+ SwPaM aPam(rDoc.GetNodes(), nTableNd);
+ if (aPam.Move(fnMoveBackward, GoInContent))
+ {
+ SwNode & rIdx = aPam.GetPoint()->GetNode();
+
+ // than move, relatively, the Cursor/etc. again
+ RemoveIdxRel( rIdx.GetIndex()+1, *aPam.GetPoint() );
+
+ rIdx.GetContentNode()->JoinNext();
+ }
+ }
+
+ // join again at end?
+ if( m_bSplitEnd )
+ {
+ SwPosition aEndPos( rDoc.GetNodes(), m_nEndNode );
+ SwTextNode* pTextNd = aEndPos.GetNode().GetTextNode();
+ if( pTextNd && pTextNd->CanJoinNext() )
+ {
+ aEndPos.nContent.Assign( nullptr, 0 );
+
+ // than move, relatively, the Cursor/etc. again
+ aEndPos.SetContent(pTextNd->GetText().getLength());
+ RemoveIdxRel( m_nEndNode + 1, aEndPos );
+
+ 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())->SetFormatName( m_sTableName );
+}
+
+void SwUndoTextToTable::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // no Table In Table
+ if (!rContext.GetRepeatPaM().GetPointNode().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().GetPointNode().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.
+ if( const SwTableBoxFormula* pItem = pSet->GetItemIfSet( RES_BOXATR_FORMULA ))
+ {
+ pSet->ClearItem( RES_BOXATR_VALUE );
+ if (m_pSwTable && m_bSaveFormula)
+ {
+ const_cast<SwTable*>(m_pSwTable)->SwitchFormulasToExternalRepresentation();
+ SwTableBoxFormula* pFormulaItem = const_cast<SwTableBoxFormula*>(pItem);
+ pFormulaItem->ChgDefinedIn(pFormat);
+ pFormulaItem->ToRelBoxNm(m_pSwTable);
+ pFormulaItem->ChgDefinedIn(nullptr);
+ }
+ }
+ nRet = m_aSets.size();
+ m_aSets.push_back(pSet);
+ m_aFrameFormats.insert(m_aFrameFormats.begin() + nRet, pFormat);
+ }
+ return o3tl::narrowing<sal_uInt16>(nRet);
+}
+
+void SaveTable::RestoreAttr( SwTable& rTable, bool bMdfyBox )
+{
+ m_bModifyBox = bMdfyBox;
+
+ FndBox_ aTmpBox( nullptr, nullptr );
+ bool bHideChanges = rTable.GetFrameFormat()->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines();
+ // TODO delete/make frames only at changing line attribute TextChangesOnly (RES_PRINT) to true again
+ if ( bHideChanges )
+ 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);
+
+ pFormat->InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ // table without table frame
+ bool bHiddenTable = true;
+
+ // 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();
+ bHiddenTable = false;
+ }
+ }
+
+ // 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->m_pNext)
+ {
+ if( !pLn )
+ {
+ OSL_ENSURE( false, "Number of lines changed" );
+ break;
+ }
+
+ pLn->RestoreAttr( *rTable.GetTabLines()[ n ], *this );
+ }
+
+ m_aFrameFormats.clear();
+ m_bModifyBox = false;
+
+ if ( bHideChanges )
+ {
+ if ( bHiddenTable )
+ {
+ SwTableNode* pTableNode = rTable.GetTableNode();
+ pTableNode->DelFrames();
+ pTableNode->MakeOwnFrames();
+ }
+ else
+ {
+ aTmpBox.MakeFrames( rTable );
+ }
+ }
+}
+
+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);
+
+ pFormat->InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ // 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() );
+ }
+}
+
+SwFrameFormat& SaveTable::CreateNewFormat(SwFrameFormat& rFormat, sal_uInt16 nFormatPos)
+{
+ rFormat.SetFormatAttr(*m_aSets[nFormatPos]);
+ m_aFrameFormats[nFormatPos] = &rFormat;
+ return rFormat;
+}
+
+void SaveTable::NewFrameFormatForLine(const SwTableLine& rTableLn, sal_uInt16 nFormatPos, SwFrameFormat* pOldFormat)
+{
+ SwFrameFormat* pFormat = m_aFrameFormats[nFormatPos];
+ if(!pFormat)
+ pFormat = &CreateNewFormat(*pOldFormat->GetDoc()->MakeTableLineFormat(), nFormatPos);
+ pOldFormat->CallSwClientNotify(sw::MoveTableLineHint(*pFormat, rTableLn));
+ pFormat->Add(const_cast<SwTableLine*>(&rTableLn));
+ KillEmptyFrameFormat(*pOldFormat);
+}
+
+void SaveTable::NewFrameFormatForBox(const SwTableBox& rTableBx, sal_uInt16 nFormatPos, SwFrameFormat* pOldFormat)
+{
+ SwFrameFormat* pFormat = m_aFrameFormats[nFormatPos];
+ if(!pFormat)
+ pFormat = &CreateNewFormat(*pOldFormat->GetDoc()->MakeTableBoxFormat(), nFormatPos);
+ pOldFormat->CallSwClientNotify(sw::MoveTableBoxHint(*pFormat, rTableBx));
+ pFormat->MoveTableBox(*const_cast<SwTableBox*>(&rTableBx), m_bModifyBox ? pOldFormat : nullptr);
+ KillEmptyFrameFormat(*pOldFormat);
+}
+
+SaveLine::SaveLine(SaveLine* pPrev, const SwTableLine& rLine, SaveTable& rSTable)
+ : m_pNext(nullptr)
+{
+ if( pPrev )
+ pPrev->m_pNext = this;
+
+ m_nItemSet = rSTable.AddFormat(rLine.GetFrameFormat(), true);
+
+ m_pBox = new SaveBox(nullptr, *rLine.GetTabBoxes()[0], rSTable);
+ SaveBox* pBx = m_pBox;
+ for( size_t n = 1; n < rLine.GetTabBoxes().size(); ++n )
+ pBx = new SaveBox( pBx, *rLine.GetTabBoxes()[ n ], rSTable );
+}
+
+SaveLine::~SaveLine()
+{
+ delete m_pBox;
+ delete m_pNext;
+}
+
+void SaveLine::RestoreAttr( SwTableLine& rLine, SaveTable& rSTable )
+{
+ rSTable.NewFrameFormatForLine(rLine, m_nItemSet, rLine.GetFrameFormat());
+
+ SaveBox* pBx = m_pBox;
+ for (size_t n = 0; n < rLine.GetTabBoxes().size(); ++n, pBx = pBx->m_pNext)
+ {
+ if( !pBx )
+ {
+ OSL_ENSURE( false, "Number of boxes changed" );
+ break;
+ }
+ pBx->RestoreAttr( *rLine.GetTabBoxes()[ n ], rSTable );
+ }
+}
+
+void SaveLine::SaveContentAttrs( SwDoc* pDoc )
+{
+ m_pBox->SaveContentAttrs(pDoc);
+ if (m_pNext)
+ m_pNext->SaveContentAttrs(pDoc);
+}
+
+void SaveLine::CreateNew( SwTable& rTable, SwTableBox& rParent, SaveTable& rSTable )
+{
+ SwTableLineFormat* pFormat
+ = static_cast<SwTableLineFormat*>(rSTable.m_aFrameFormats[m_nItemSet]);
+ if( !pFormat )
+ {
+ SwDoc* pDoc = rTable.GetFrameFormat()->GetDoc();
+ pFormat = pDoc->MakeTableLineFormat();
+ pFormat->SetFormatAttr(*rSTable.m_aSets[m_nItemSet]);
+ rSTable.m_aFrameFormats[m_nItemSet] = pFormat;
+ }
+ SwTableLine* pNew = new SwTableLine( pFormat, 1, &rParent );
+
+ rParent.GetTabLines().push_back( pNew );
+
+ m_pBox->CreateNew(rTable, *pNew, rSTable);
+
+ if (m_pNext)
+ m_pNext->CreateNew(rTable, rParent, rSTable);
+}
+
+SaveBox::SaveBox(SaveBox* pPrev, const SwTableBox& rBox, SaveTable& rSTable)
+ : m_pNext(nullptr)
+ , m_nStartNode(NODE_OFFSET_MAX)
+ , m_nRowSpan(0)
+{
+ m_Ptrs.pLine = nullptr;
+
+ if( pPrev )
+ pPrev->m_pNext = this;
+
+ m_nItemSet = rSTable.AddFormat(rBox.GetFrameFormat(), false);
+
+ if( rBox.GetSttNd() )
+ {
+ m_nStartNode = rBox.GetSttIdx();
+ m_nRowSpan = rBox.getRowSpan();
+ }
+ else
+ {
+ m_Ptrs.pLine = new SaveLine(nullptr, *rBox.GetTabLines()[0], rSTable);
+
+ SaveLine* pLn = m_Ptrs.pLine;
+ for( size_t n = 1; n < rBox.GetTabLines().size(); ++n )
+ pLn = new SaveLine( pLn, *rBox.GetTabLines()[ n ], rSTable );
+ }
+}
+
+SaveBox::~SaveBox()
+{
+ if (NODE_OFFSET_MAX == m_nStartNode) // no EndBox
+ delete m_Ptrs.pLine;
+ else
+ delete m_Ptrs.pContentAttrs;
+ delete m_pNext;
+}
+
+void SaveBox::RestoreAttr( SwTableBox& rBox, SaveTable& rSTable )
+{
+ rSTable.NewFrameFormatForBox(rBox, m_nItemSet, rBox.GetFrameFormat());
+
+ if (NODE_OFFSET_MAX == m_nStartNode) // no EndBox
+ {
+ if( rBox.GetTabLines().empty() )
+ {
+ OSL_ENSURE( false, "Number of lines changed" );
+ }
+ else
+ {
+ SaveLine* pLn = m_Ptrs.pLine;
+ for (size_t n = 0; n < rBox.GetTabLines().size(); ++n, pLn = pLn->m_pNext)
+ {
+ if( !pLn )
+ {
+ OSL_ENSURE( false, "Number of lines changed" );
+ break;
+ }
+
+ pLn->RestoreAttr( *rBox.GetTabLines()[ n ], rSTable );
+ }
+ }
+ }
+ else if (rBox.GetSttNd() && rBox.GetSttIdx() == m_nStartNode)
+ {
+ if (m_Ptrs.pContentAttrs)
+ {
+ SwNodes& rNds = rBox.GetFrameFormat()->GetDoc()->GetNodes();
+ sal_uInt16 nSet = 0;
+ SwNodeOffset nEnd = rBox.GetSttNd()->EndOfSectionIndex();
+ for (SwNodeOffset n = m_nStartNode + 1; n < nEnd; ++n)
+ {
+ SwContentNode* pCNd = rNds[ n ]->GetContentNode();
+ if( pCNd )
+ {
+ std::shared_ptr<SfxItemSet> pSet((*m_Ptrs.pContentAttrs)[nSet++]);
+ if( pSet )
+ {
+ for( const WhichPair& rPair : aSave_BoxContentSet )
+ pCNd->ResetAttr( rPair.first, rPair.second );
+ pCNd->SetAttr( *pSet );
+ }
+ else
+ pCNd->ResetAllAttr();
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_ENSURE( false, "Box not anymore at the same node" );
+ }
+}
+
+void SaveBox::SaveContentAttrs( SwDoc* pDoc )
+{
+ if (NODE_OFFSET_MAX == m_nStartNode) // no EndBox
+ {
+ // continue in current line
+ m_Ptrs.pLine->SaveContentAttrs(pDoc);
+ }
+ else
+ {
+ SwNodeOffset nEnd = pDoc->GetNodes()[m_nStartNode]->EndOfSectionIndex();
+ m_Ptrs.pContentAttrs = new SfxItemSets;
+ for (SwNodeOffset n = m_nStartNode + 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() );
+ }
+
+ m_Ptrs.pContentAttrs->push_back(pSet);
+ }
+ }
+ }
+ if (m_pNext)
+ m_pNext->SaveContentAttrs(pDoc);
+}
+
+void SaveBox::CreateNew( SwTable& rTable, SwTableLine& rParent, SaveTable& rSTable )
+{
+ SwTableBoxFormat* pFormat = static_cast<SwTableBoxFormat*>(rSTable.m_aFrameFormats[m_nItemSet]);
+ if( !pFormat )
+ {
+ SwDoc* pDoc = rTable.GetFrameFormat()->GetDoc();
+ pFormat = pDoc->MakeTableBoxFormat();
+ pFormat->SetFormatAttr(*rSTable.m_aSets[m_nItemSet]);
+ rSTable.m_aFrameFormats[m_nItemSet] = pFormat;
+ }
+
+ if (NODE_OFFSET_MAX == m_nStartNode) // no EndBox
+ {
+ SwTableBox* pNew = new SwTableBox( pFormat, 1, &rParent );
+ rParent.GetTabBoxes().push_back( pNew );
+
+ m_Ptrs.pLine->CreateNew(rTable, *pNew, rSTable);
+ }
+ else
+ {
+ // search box for StartNode in old table
+ SwTableBox* pBox = rTable.GetTableBox(m_nStartNode);
+ if (pBox)
+ {
+ SwFrameFormat* pOld = pBox->GetFrameFormat();
+ pBox->RegisterToFormat( *pFormat );
+ if( !pOld->HasWriterListeners() )
+ delete pOld;
+
+ pBox->setRowSpan(m_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 (m_pNext)
+ m_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,
+ tools::Long nMn, tools::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_xNewSttNds.emplace();
+
+ 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_xNewSttNds->insert( BoxMove(rTableBoxes[ i ]->GetSttIdx()) );
+ }
+
+ for( ; i < rTableBoxes.size(); ++i )
+ // new box: insert sorted
+ m_xNewSttNds->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<SwNodeOffset> &rNodeCnts )
+{
+ const SwTable& rTable = rTableNd.GetTable();
+ const SwTableSortBoxes& rTableBoxes = rTable.GetTabSortBoxes();
+
+ OSL_ENSURE( ! IsDelBox(), "wrong Action" );
+ m_xNewSttNds.emplace();
+
+ 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;
+ SwNodeOffset 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 > SwNodeOffset(nLineDiff) );
+ m_xNewSttNds->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" );
+ pTableNd->GetTable().SwitchFormulasToInternalRepresentation();
+
+ 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 *, SwNodeOffset> > 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_xNewSttNds->empty() )
+ {
+ // Then the nodes have be moved and not deleted!
+ // But for that we need a temp array.
+ std::vector<BoxMove> aTmp( m_xNewSttNds->begin(), m_xNewSttNds->end() );
+
+ // backwards
+ for (size_t n = aTmp.size(); n > 0 ; )
+ {
+ --n;
+ // delete box from table structure
+ SwNodeOffset 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(), SwNodeOffset(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;
+ SwNodeOffset 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.GetNode(), false );
+ }
+ else
+ {
+ aDelNodes.emplace_back(pBox, nIdx);
+ }
+ }
+ }
+ else
+ {
+ // Remove nodes from nodes array (backwards!)
+ std::set<BoxMove>::reverse_iterator it;
+ for( it = m_xNewSttNds->rbegin(); it != m_xNewSttNds->rend(); ++it )
+ {
+ SwNodeOffset 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 *, SwNodeOffset> & 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 (SwFEShell* pFEShell = rDoc.GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting(pTableNd);
+ 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:
+ {
+ SwTable &rTable = pTableNd->GetTable();
+ rTable.SwitchFormulasToInternalRepresentation();
+ 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.GetPointNode().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" );
+
+ pTableNd->GetTable().SwitchFormulasToInternalRepresentation();
+
+ // ? 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.GetNode(),
+ 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
+ SwNodeOffset 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(
+ const_cast<SwEndNode&>(*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
+ SwContentIndex aTmpIdx( pTextNd, nDelPos );
+ if( pTextNd->GetpSwpHints() && pTextNd->GetpSwpHints()->Count() )
+ pTextNd->RstTextAttr( nDelPos, 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, 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()->Assign(m_nSttNode, 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& rDoc, SwNodeRange& rRg, SwNode& rPos )
+{
+ SwNodeIndex aTmp( rRg.aStart, -1 ), aTmp2( rPos, -1 );
+ std::unique_ptr<SwUndoMove> pUndo(new SwUndoMove( rDoc, rRg, rPos ));
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().MoveNodeRange( rRg, rPos, m_pSaveTable->IsNewModel() ?
+ SwMoveFlags::NO_DELFRMS :
+ SwMoveFlags::DEFAULT );
+ ++aTmp;
+ ++aTmp2;
+ pUndo->SetDestRange( aTmp2.GetNode(), 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( SwNodeOffset(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->AddColl(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( NODE_OFFSET_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 attributes 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 )
+ {
+ if( const SwTableBoxNumFormat* pItem = pNewSet->GetItemIfSet( RES_BOXATR_FORMAT,
+ false ))
+ {
+ m_bNewFormat = true;
+ m_nNewFormatIdx = pItem->GetValue();
+ }
+ if( const SwTableBoxFormula* pItem = pNewSet->GetItemIfSet( RES_BOXATR_FORMULA,
+ false ))
+ {
+ m_bNewFormula = true;
+ m_aNewFormula = pItem->GetFormula();
+ }
+ if( const SwTableBoxValue* pItem = pNewSet->GetItemIfSet( RES_BOXATR_VALUE,
+ false ))
+ {
+ m_bNewValue = true;
+ m_fNewNum = 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( NODE_OFFSET_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 );
+
+ SwContentIndex 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()->Assign( m_nNode + 1 );
+}
+
+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()->Assign( m_nNode );
+
+ SwNode * pNd = & pPam->GetPoint()->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 )
+ {
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxSet( rDoc.GetAttrPool() );
+
+ // 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 )
+ {
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxSet( rDoc.GetAttrPool() );
+
+ 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
+ rDoc.getIDocumentFieldsAccess().UpdateTableFields(&pSttNd->FindTableNode()->GetTable());
+ }
+
+ if( !pNd->IsContentNode() )
+ pNd = rDoc.GetNodes().GoNext( pPam->GetPoint() );
+}
+
+void SwUndoTableNumFormat::SetBox( const SwTableBox& rBox )
+{
+ m_nNode = rBox.GetSttIdx();
+}
+
+UndoTableCpyTable_Entry::UndoTableCpyTable_Entry( const SwTableBox& rBox )
+ : nBoxIdx( rBox.GetSttIdx() ), nOffset( 0 ),
+ bJoin( false )
+{
+}
+
+void UndoTableCpyTable_Entry::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("UndoTableCpyTable_Entry"));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("nBoxIdx"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(sal_Int32(nBoxIdx)).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("nOffset"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(sal_Int32(nOffset)).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (pBoxNumAttr)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pBoxNumAttr"));
+ pBoxNumAttr->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ if (pUndo)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pUndo"));
+ pUndo->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("bJoin"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(bJoin).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwUndoTableCpyTable::SwUndoTableCpyTable(const SwDoc& rDoc)
+ : SwUndo( SwUndoId::TBLCPYTBL, &rDoc )
+{
+}
+
+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();
+ SwNodeOffset nSttPos = pEntry->nBoxIdx + pEntry->nOffset;
+ SwStartNode* pSNd = rDoc.GetNodes()[ nSttPos ]->StartOfSectionNode();
+ if( !pTableNd )
+ pTableNd = pSNd->FindTableNode();
+
+ SwTableBox* pBox = pTableNd->GetTable().GetTableBox( nSttPos );
+ if (!pBox)
+ {
+ SAL_WARN("sw.core",
+ "SwUndoTableCpyTable::UndoImpl: invalid start node index for table box");
+ continue;
+ }
+
+ SwTableBox& rBox = *pBox;
+
+ SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 );
+ rDoc.GetNodes().MakeTextNode( aInsIdx.GetNode(), 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()->Assign(*pText,
+ pUndoRedlineDelete->ContentStart() );
+ }
+ else
+ aPam.GetPoint()->Assign( 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;
+ aPam.GetPoint()->Assign(*pEndNode, SwNodeOffset(-1));
+ }
+ }
+ 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
+ aPam.GetPoint()->Assign(aPam.GetPoint()->GetNode(), SwNodeOffset(1));
+ }
+ pUndo = std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, bDeleteCompleteParagraph, true);
+ }
+ else
+ {
+ pUndo = std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, true);
+ if( pEntry->pUndo )
+ {
+ pEntry->pUndo->UndoImpl(rContext);
+ pEntry->pUndo.reset();
+ }
+ }
+ pEntry->pUndo = std::move(pUndo);
+
+ aInsIdx = rBox.GetSttIdx() + 1;
+ rDoc.GetNodes().Delete( aInsIdx );
+
+ SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>
+ aTmpSet(rDoc.GetAttrPool());
+ 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<SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>>(rDoc.GetAttrPool());
+ 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();
+ SwNodeOffset 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.GetNode(), rDoc.GetDfltTextFormatColl() );
+ SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode());
+ std::unique_ptr<SwUndo> pUndo(IDocumentRedlineAccess::IsRedlineOn(GetRedlineFlags())
+ ? nullptr
+ : std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, 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& 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 );
+
+ SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aTmpSet(rDoc.GetAttrPool());
+ 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<SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>>(rDoc.GetAttrPool());
+ 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.GetNode(), pDoc->GetDfltTextFormatColl() );
+ SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode() );
+
+ if( !pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ pEntry->pUndo = std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, true);
+ }
+
+ pEntry->pBoxNumAttr = std::make_unique<SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>>(pDoc->GetAttrPool());
+ 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,
+ 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.Adjust(SwNodeOffset(-1));
+ pText = aInsertEnd.GetNode().GetTextNode();
+ if( pText )
+ {
+ aInsertEnd.SetContent(pText->GetText().getLength());
+ if( !bRedo && rPos.GetNode().GetTextNode() )
+ { // Try to merge, if not called by Redo()
+ rJoin = true;
+
+ // Park this somewhere else so nothing points to the to-be-deleted node.
+ rPos.nContent.Assign(pText, 0);
+
+ 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.GetNode().GetTextNode();
+ if( pText )
+ aDeleteStart.SetContent( 0 );
+ }
+ SwPosition aCellEnd( *rBox.GetSttNd()->EndOfSectionNode(), SwNodeOffset(-1) );
+ pText = aCellEnd.GetNode().GetTextNode();
+ if( pText )
+ aCellEnd.SetContent(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.Assign(*rBox.GetSttNd()->EndOfSectionNode());
+ SwPaM aTmpPam( aDeleteStart, aCellEnd );
+ pUndo = std::make_unique<SwUndoDelete>(aTmpPam, SwDeleteFlags::Default, true);
+ }
+ SwPosition aCellStart( *rBox.GetSttNd(), SwNodeOffset(2) );
+ pText = aCellStart.GetNode().GetTextNode();
+ if( pText )
+ aCellStart.SetContent( 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;
+}
+
+void SwUndoTableCpyTable::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoTableCpyTable"));
+
+ for (const auto& pEntry : m_vArr)
+ {
+ pEntry->dumpAsXml(pWriter);
+ }
+
+ if (m_pInsRowUndo)
+ {
+ m_pInsRowUndo->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+bool SwUndoTableCpyTable::IsEmpty() const
+{
+ return !m_pInsRowUndo && m_vArr.empty();
+}
+
+SwUndoCpyTable::SwUndoCpyTable(const SwDoc& rDoc)
+ : SwUndo( SwUndoId::CPYTBL, &rDoc ), 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();
+
+ if( const SwFormatPageDesc* pItem = pTableFormat->GetItemIfSet( RES_PAGEDESC,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( const SvxFormatBreakItem* pItem = pTableFormat->GetItemIfSet( RES_BREAK,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+ }
+
+ SwPaM aPam( *pTNd, *pTNd->EndOfSectionNode(), SwNodeOffset(0) , SwNodeOffset(1) );
+ m_pDelete.reset(new SwUndoDelete(aPam, SwDeleteFlags::Default, 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());
+
+ SwPosition& rPtPos = *pPam->GetPoint();
+ rPtPos.Assign( m_nTableNode + m_nOffset );
+ assert(rPtPos.GetNode().GetContentNode()->Len() == 0); // empty para inserted
+
+ {
+ // avoid asserts from ~SwContentIndexReg
+ 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 );
+ }
+
+ rPtPos.Assign( m_nTableNode + m_nOffset );
+ SwTableNode* pTableNd = rPtPos.GetNode().GetTableNode();
+ SwTable& rTable = pTableNd->GetTable();
+ rTable.SwitchFormulasToInternalRepresentation();
+
+ 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( rPtPos.GetNode() );
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback( pDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+ if( mpSaveRowSpan )
+ {
+ pTableNd = rPtPos.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()->Assign( 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 )
+ : SwUndo( SwUndoId::MERGE_TABLE, &rTableNd.GetDoc() ),
+ 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();
+ SwPosition& rPtPos = *pPam->GetPoint();
+ rPtPos.Assign( m_nTableNode);
+
+ SwTableNode* pTableNd = rPtPos.GetNode().FindTableNode();
+ SwTable* pTable = &pTableNd->GetTable();
+ pTable->SwitchFormulasToInternalRepresentation();
+
+ // 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( rPtPos.GetNode() );
+
+ // update layout
+ aFndBox.MakeFrames( *pTable );
+ // ? TL_CHART2: notification or locking of controller required ?
+
+ if( m_bWithPrev )
+ {
+ // move name
+ pNew->GetTable().GetFrameFormat()->SetFormatName( pTable->GetFrameFormat()->GetName() );
+ m_pSaveHdl->RestoreAttr( pNew->GetTable() );
+ }
+ else
+ pTable = &pNew->GetTable();
+
+ pTable->GetFrameFormat()->SetFormatName( 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
+ pNew->MakeOwnFrames();
+
+ // position cursor somewhere in content
+ pDoc->GetNodes().GoNext( &rPtPos );
+
+ 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();
+ if( m_bWithPrev )
+ pPam->GetPoint()->Assign( m_nTableNode + 3 );
+ else
+ pPam->GetPoint()->Assign( m_nTableNode );
+
+ pDoc->MergeTable( *pPam->GetPoint(), m_bWithPrev );
+
+ 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 );
+ 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(OUString aName, const SwDoc& rDoc)
+ : SwUndo(SwUndoId::TBLSTYLE_CREATE, &rDoc),
+ m_sName(std::move(aName))
+{ }
+
+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, std::vector<SwTable*>&& rAffectedTables, const SwDoc& rDoc)
+ : SwUndo(SwUndoId::TBLSTYLE_DELETE, &rDoc),
+ m_pAutoFormat(std::move(pAutoFormat)),
+ m_rAffectedTables(std::move(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& rDoc)
+ : SwUndo(SwUndoId::TBLSTYLE_UPDATE, &rDoc)
+ , 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 0000000000..fd5c3239c0
--- /dev/null
+++ b/sw/source/core/undo/untblk.cxx
@@ -0,0 +1,484 @@
+/* -*- 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 <libxml/xmlwriter.h>
+
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <docary.hxx>
+#include <swcrsr.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::optional<std::vector<SwFrameFormat*>>
+GetFlysAnchoredAt(SwDoc & rDoc, SwNodeOffset const nSttNode)
+{
+ std::optional<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();
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+ if (pAnchorNode
+ && nSttNode == pAnchorNode->GetIndex()
+ && ((pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ || (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)))
+ {
+ if (!pFrameFormats)
+ pFrameFormats.emplace();
+ 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& rDoc = rPam.GetDoc();
+
+ SwTextNode* pTextNd = rPam.GetPoint()->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(rDoc, m_nSttNode);
+ }
+ // consider Redline
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlineData.reset( new SwRedlineData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( rDoc.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,
+ SwNodeOffset const nDeleteTextNodes)
+{
+ const SwPosition* pTmpPos = rPam.End();
+ m_nEndNode = pTmpPos->GetNodeIndex();
+ m_nEndContent = pTmpPos->GetContentIndex();
+ if( rPam.HasMark() )
+ {
+ if( pTmpPos == rPam.GetPoint() )
+ pTmpPos = rPam.GetMark();
+ else
+ pTmpPos = rPam.GetPoint();
+
+ m_nSttNode = pTmpPos->GetNodeIndex();
+ m_nSttContent = pTmpPos->GetContentIndex();
+
+ m_nDeleteTextNodes = nDeleteTextNodes;
+ if (m_nDeleteTextNodes == SwNodeOffset(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)
+ return;
+
+ // than collect all new Flys
+ SwDoc& rDoc = rPam.GetDoc();
+ const size_t nArrLen = rDoc.GetSpzFrameFormats()->size();
+ for( size_t n = 0; n < nArrLen; ++n )
+ {
+ SwFrameFormat* pFormat = (*rDoc.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, SwNodeOffset(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,
+ SwNodeOffset const nStartNode, SwNodeOffset const nEndNode)
+{
+ assert(nStartNode <= nEndNode);
+
+ // check all at-char flys at the start/end nodes:
+ // ExcludeFlyAtStartEnd will exclude them!
+ SwNode const*const pAnchorNode = rAnchor.GetAnchorNode();
+ return pAnchorNode != nullptr
+ && ( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
+ || rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ && ( nStartNode == pAnchorNode->GetIndex()
+ || nEndNode == pAnchorNode->GetIndex());
+}
+
+void SwUndoInserts::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoInserts"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s",
+ BAD_CAST(typeid(*this).name()));
+
+ SwUndo::dumpAsXml(pWriter);
+ SwUndoSaveContent::dumpAsXml(pWriter);
+
+ if (m_pFrameFormats)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_pFrameFormats"));
+ for (const auto& pFormat : *m_pFrameFormats)
+ {
+ pFormat->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ if (!m_FlyUndos.empty())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_FlyUndos"));
+ for (const auto& pFly : m_FlyUndos)
+ {
+ pFly->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwUndoInserts::~SwUndoInserts()
+{
+ if (m_oUndoNodeIndex) // delete also the section from UndoNodes array
+ {
+ // Insert saves content in IconSection
+ SwNodes& rUNds = m_oUndoNodeIndex->GetNodes();
+ rUNds.Delete(*m_oUndoNodeIndex,
+ rUNds.GetEndOfExtras().GetIndex() - m_oUndoNodeIndex->GetIndex());
+ m_oUndoNodeIndex.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 = SwNodeOffset(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()->GetNode().GetTextNode() &&
+ rPam.GetPoint()->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())
+ {
+ SwNodeOffset nTmp = rPam.GetPoint()->GetNodeIndex();
+ for (size_t n = m_FlyUndos.size(); 0 < n; --n)
+ {
+ m_FlyUndos[ n-1 ]->UndoImpl(rContext);
+ }
+ m_nNodeDiff += nTmp - rPam.GetPoint()->GetNodeIndex();
+ }
+
+ if (m_nSttNode != m_nEndNode || m_nSttContent != m_nEndContent)
+ {
+ // are there Footnotes or ContentFlyFrames in text?
+ m_nSetPos = m_pHistory->Count();
+ SwNodeOffset nTmp = rPam.GetMark()->GetNodeIndex();
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType::AllMask|DelContentType::ExcludeFlyAtStartEnd);
+ m_nNodeDiff += nTmp - rPam.GetMark()->GetNodeIndex();
+ if( *rPam.GetPoint() != *rPam.GetMark() )
+ {
+ m_oUndoNodeIndex.emplace(rDoc.GetNodes().GetEndOfContent());
+ MoveToUndoNds(rPam, &*m_oUndoNodeIndex);
+
+ if (m_nDeleteTextNodes == SwNodeOffset(0))
+ {
+ rPam.Move( fnMoveBackward, GoInContent );
+ }
+ }
+ }
+
+ SwTextNode* pTextNode = rPam.GetPoint()->GetNode().GetTextNode();
+ if( !pTextNode )
+ return;
+
+ if( !m_pTextFormatColl ) // if 0 than it's no TextNode -> delete
+ {
+ SwNodeIndex aDelIdx( *pTextNode );
+ assert(SwNodeOffset(0) < m_nDeleteTextNodes && m_nDeleteTextNodes < SwNodeOffset(3));
+ for (SwNodeOffset i(0); i < m_nDeleteTextNodes; ++i)
+ {
+ rPam.Move(fnMoveForward, GoInNode);
+ }
+ rPam.DeleteMark();
+
+ for (SwNodeOffset i(0); i < m_nDeleteTextNodes; ++i)
+ {
+ RemoveIdxRel(aDelIdx.GetIndex() + i, *rPam.GetPoint());
+ }
+
+ rDoc.GetNodes().Delete( aDelIdx, m_nDeleteTextNodes );
+ }
+ else
+ {
+ if( bJoinNext && pTextNode->CanJoinNext())
+ {
+ {
+ RemoveIdxRel( pTextNode->GetIndex()+1,
+ SwPosition( *pTextNode, pTextNode, pTextNode->GetText().getLength() ) );
+ }
+ pTextNode->JoinNext();
+ }
+ // reset all text attributes in the paragraph!
+ pTextNode->RstTextAttr( 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
+ SwCursor& rPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+ SwDoc& rDoc = rPam.GetDoc();
+ rPam.DeleteMark();
+ rPam.GetPoint()->Assign( m_nSttNode - m_nNodeDiff, m_nSttContent );
+ SwContentNode* pCNd = rPam.GetPointContentNode();
+
+ 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_oUndoNodeIndex)
+ {
+ auto const pFlysAtInsPos(sw::GetFlysAnchoredAt(rDoc,
+ rPam.GetPoint()->GetNodeIndex()));
+
+ ::std::optional<SwNodeIndex> oMvBkwrd = MovePtBackward(rPam);
+
+ // re-insert content again (first detach m_oUndoNodeIndex!)
+ SwNodeOffset const nMvNd = m_oUndoNodeIndex->GetIndex();
+ m_oUndoNodeIndex.reset();
+ MoveFromUndoNds(rDoc, nMvNd, *rPam.GetMark());
+ if (m_nDeleteTextNodes != SwNodeOffset(0) || oMvBkwrd)
+ {
+ MovePtForward(rPam, ::std::move(oMvBkwrd));
+ }
+ 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 (m_pTextFormatColl && rDoc.GetTextFormatColls()->IsAlive(m_pTextFormatColl))
+ {
+ SwTextNode* pTextNd = rPam.GetMark()->GetNode().GetTextNode();
+ if( pTextNd )
+ pTextNd->ChgFormatColl( m_pTextFormatColl );
+ }
+ m_pTextFormatColl = pSavTextFormatColl;
+
+ if (m_pLastNodeColl && rDoc.GetTextFormatColls()->IsAlive(m_pLastNodeColl)
+ && rPam.GetPoint()->GetNode() != rPam.GetMark()->GetNode())
+ {
+ SwTextNode* pTextNd = rPam.GetPoint()->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( &rDoc, 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 = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags::Ignore );
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ), true);
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ rDoc.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: */