diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /sw/source/core/undo | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/undo')
26 files changed, 16510 insertions, 0 deletions
diff --git a/sw/source/core/undo/SwRewriter.cxx b/sw/source/core/undo/SwRewriter.cxx new file mode 100644 index 000000000..05566a1bd --- /dev/null +++ b/sw/source/core/undo/SwRewriter.cxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <algorithm> +#include <SwRewriter.hxx> + +using namespace std; + +SwRewriter::SwRewriter() +{ +} + +void SwRewriter::AddRule(SwUndoArg eWhat, const OUString & rWith) +{ + SwRewriteRule aRule(eWhat, rWith); + + vector<SwRewriteRule>::iterator aIt = find_if( + mRules.begin(), mRules.end(), + [&aRule](SwRewriteRule const & a) { return a.first == aRule.first; }); + + if (aIt != mRules.end()) + *aIt = aRule; + else + mRules.push_back(aRule); +} + +OUString SwRewriter::Apply(const OUString & rStr) const +{ + OUString aResult = rStr; + + for (const auto& rRule : mRules) + { + aResult = aResult.replaceAll(GetPlaceHolder(rRule.first), rRule.second); + } + + return aResult; +} + +OUString SwRewriter::GetPlaceHolder(SwUndoArg eId) +{ + switch (eId) + { + case UndoArg1: + return "$1"; + case UndoArg2: + return "$2"; + case UndoArg3: + return "$3"; + default: + break; + } + return "$1"; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/SwUndoField.cxx b/sw/source/core/undo/SwUndoField.cxx new file mode 100644 index 000000000..1eac1fa41 --- /dev/null +++ b/sw/source/core/undo/SwUndoField.cxx @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <SwUndoField.hxx> +#include <swundo.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <DocumentFieldsManager.hxx> +#include <txtfld.hxx> +#include <fldbas.hxx> +#include <fmtfld.hxx> +#include <docsh.hxx> +#include <pam.hxx> +#include <osl/diagnose.h> + +using namespace ::com::sun::star::uno; + +SwUndoField::SwUndoField(const SwPosition & rPos ) + : SwUndo(SwUndoId::FIELD, rPos.GetDoc()) +{ + nNodeIndex = rPos.nNode.GetIndex(); + nOffset = rPos.nContent.GetIndex(); + pDoc = rPos.GetDoc(); +} + +SwUndoField::~SwUndoField() +{ +} + +SwPosition SwUndoField::GetPosition() +{ + SwNode * pNode = pDoc->GetNodes()[nNodeIndex]; + SwNodeIndex aNodeIndex(*pNode); + SwIndex aIndex(pNode->GetContentNode(), nOffset); + SwPosition aResult(aNodeIndex, aIndex); + + return aResult; +} + +SwUndoFieldFromDoc::SwUndoFieldFromDoc(const SwPosition & rPos, + const SwField & rOldField, + const SwField & rNewField, + SwMsgPoolItem * _pHint, bool _bUpdate) + : SwUndoField(rPos) + , pOldField(rOldField.CopyField()) + , pNewField(rNewField.CopyField()) + , pHint(_pHint) + , bUpdate(_bUpdate) +{ + OSL_ENSURE(pOldField, "No old field!"); + OSL_ENSURE(pNewField, "No new field!"); + OSL_ENSURE(pDoc, "No document!"); +} + +SwUndoFieldFromDoc::~SwUndoFieldFromDoc() +{ +} + +void SwUndoFieldFromDoc::UndoImpl(::sw::UndoRedoContext &) +{ + SwTextField * pTextField = sw::DocumentFieldsManager::GetTextFieldAtPos(GetPosition()); + const SwField * pField = pTextField ? pTextField->GetFormatField().GetField() : nullptr; + + if (pField) + { + pDoc->getIDocumentFieldsAccess().UpdateField(pTextField, *pOldField, pHint, bUpdate); + } +} + +void SwUndoFieldFromDoc::DoImpl() +{ + SwTextField * pTextField = sw::DocumentFieldsManager::GetTextFieldAtPos(GetPosition()); + const SwField * pField = pTextField ? pTextField->GetFormatField().GetField() : nullptr; + + if (pField) + { + pDoc->getIDocumentFieldsAccess().UpdateField(pTextField, *pNewField, pHint, bUpdate); + SwFormatField* pDstFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField()); + + if (pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(), false) == pDstFormatField->GetField()->GetTyp()) + pDoc->GetDocShell()->Broadcast( SwFormatFieldHint( pDstFormatField, SwFormatFieldHintWhich::INSERTED ) ); + } +} + +void SwUndoFieldFromDoc::RedoImpl(::sw::UndoRedoContext &) +{ + DoImpl(); +} + +void SwUndoFieldFromDoc::RepeatImpl(::sw::RepeatContext &) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + DoImpl(); +} + +SwUndoFieldFromAPI::SwUndoFieldFromAPI(const SwPosition & rPos, + const Any & rOldVal, const Any & rNewVal, + sal_uInt16 _nWhich) + : SwUndoField(rPos), aOldVal(rOldVal), aNewVal(rNewVal), nWhich(_nWhich) +{ +} + +SwUndoFieldFromAPI::~SwUndoFieldFromAPI() +{ +} + +void SwUndoFieldFromAPI::UndoImpl(::sw::UndoRedoContext &) +{ + SwField * pField = sw::DocumentFieldsManager::GetFieldAtPos(GetPosition()); + + if (pField) + pField->PutValue(aOldVal, nWhich); +} + +void SwUndoFieldFromAPI::DoImpl() +{ + SwField * pField = sw::DocumentFieldsManager::GetFieldAtPos(GetPosition()); + + if (pField) + pField->PutValue(aNewVal, nWhich); +} + +void SwUndoFieldFromAPI::RedoImpl(::sw::UndoRedoContext &) +{ + DoImpl(); +} + +void SwUndoFieldFromAPI::RepeatImpl(::sw::RepeatContext &) +{ + DoImpl(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/SwUndoFmt.cxx b/sw/source/core/undo/SwUndoFmt.cxx new file mode 100644 index 000000000..d0e3798aa --- /dev/null +++ b/sw/source/core/undo/SwUndoFmt.cxx @@ -0,0 +1,466 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <poolfmt.hxx> +#include <charfmt.hxx> +#include <frmfmt.hxx> +#include <SwUndoFmt.hxx> +#include <SwRewriter.hxx> +#include <swundo.hxx> +#include <undobj.hxx> +#include <fmtcol.hxx> +#include <doc.hxx> +#include <strings.hrc> + +SwUndoFormatCreate::SwUndoFormatCreate +(SwUndoId nUndoId, SwFormat * _pNew, SwFormat const * _pDerivedFrom, SwDoc * _pDoc) + : SwUndo(nUndoId, _pDoc), m_pNew(_pNew), + m_pDoc(_pDoc), m_pNewSet(nullptr), m_nId(0), m_bAuto(false) +{ + if (_pDerivedFrom) + m_sDerivedFrom = _pDerivedFrom->GetName(); +} + +SwUndoFormatCreate::~SwUndoFormatCreate() +{ +} + +void SwUndoFormatCreate::UndoImpl(::sw::UndoRedoContext &) +{ + if (m_pNew) + { + if (m_sNewName.isEmpty()) + m_sNewName = m_pNew->GetName(); + + if (!m_sNewName.isEmpty()) + m_pNew = Find(m_sNewName); + + if (m_pNew) + { + m_pNewSet = new SfxItemSet(m_pNew->GetAttrSet()); + m_nId = m_pNew->GetPoolFormatId() & COLL_GET_RANGE_BITS; + m_bAuto = m_pNew->IsAuto(); + + Delete(); + } + } +} + +void SwUndoFormatCreate::RedoImpl(::sw::UndoRedoContext &) +{ + SwFormat * pDerivedFrom = Find(m_sDerivedFrom); + SwFormat * pFormat = Create(pDerivedFrom); + + if (pFormat && m_pNewSet) + { + pFormat->SetAuto(m_bAuto); + m_pDoc->ChgFormat(*pFormat, *m_pNewSet); + pFormat->SetPoolFormatId((pFormat->GetPoolFormatId() + & ~COLL_GET_RANGE_BITS) + | m_nId); + + m_pNew = pFormat; + } + else + m_pNew = nullptr; +} + +SwRewriter SwUndoFormatCreate::GetRewriter() const +{ + if (m_sNewName.isEmpty() && m_pNew) + m_sNewName = m_pNew->GetName(); + + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, m_sNewName); + + return aRewriter; +} + +SwUndoFormatDelete::SwUndoFormatDelete +(SwUndoId nUndoId, SwFormat const * _pOld, SwDoc * _pDoc) + : SwUndo(nUndoId, _pDoc), + m_pDoc(_pDoc), m_sOldName(_pOld->GetName()), + m_aOldSet(_pOld->GetAttrSet()) +{ + m_sDerivedFrom = _pOld->DerivedFrom()->GetName(); + m_nId = _pOld->GetPoolFormatId() & COLL_GET_RANGE_BITS; + m_bAuto = _pOld->IsAuto(); +} + +SwUndoFormatDelete::~SwUndoFormatDelete() +{ +} + +void SwUndoFormatDelete::UndoImpl(::sw::UndoRedoContext &) +{ + SwFormat * pDerivedFrom = Find(m_sDerivedFrom); + + SwFormat * pFormat = Create(pDerivedFrom); + + if (pFormat) + { + m_pDoc->ChgFormat(*pFormat, m_aOldSet); + pFormat->SetAuto(m_bAuto); + pFormat->SetPoolFormatId((pFormat->GetPoolFormatId() & + ~COLL_GET_RANGE_BITS) + | m_nId); + } +} + +void SwUndoFormatDelete::RedoImpl(::sw::UndoRedoContext &) +{ + SwFormat * pOld = Find(m_sOldName); + + if (pOld) + { + Delete(pOld); + } +} + +SwRewriter SwUndoFormatDelete::GetRewriter() const +{ + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, m_sOldName); + + return aRewriter; +} + +SwUndoRenameFormat::SwUndoRenameFormat(SwUndoId nUndoId, + const OUString & _sOldName, + const OUString & _sNewName, + SwDoc * _pDoc) + : SwUndo(nUndoId, _pDoc), m_sOldName(_sOldName), + m_sNewName(_sNewName), m_pDoc(_pDoc) +{ +} + +SwUndoRenameFormat::~SwUndoRenameFormat() +{ +} + +void SwUndoRenameFormat::UndoImpl(::sw::UndoRedoContext &) +{ + SwFormat * pFormat = Find(m_sNewName); + + if (pFormat) + { + m_pDoc->RenameFormat(*pFormat, m_sOldName, true); + } +} + +void SwUndoRenameFormat::RedoImpl(::sw::UndoRedoContext &) +{ + SwFormat * pFormat = Find(m_sOldName); + + if (pFormat) + { + m_pDoc->RenameFormat(*pFormat, m_sNewName, true); + } +} + +SwRewriter SwUndoRenameFormat::GetRewriter() const +{ + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, m_sOldName); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + aRewriter.AddRule(UndoArg3, m_sNewName); + + return aRewriter; +} + +SwUndoTextFormatCollCreate::SwUndoTextFormatCollCreate +(SwTextFormatColl * _pNew, SwTextFormatColl const * _pDerivedFrom, SwDoc * _pDoc) + : SwUndoFormatCreate(SwUndoId::TXTFMTCOL_CREATE, _pNew, _pDerivedFrom, _pDoc) +{ +} + +SwFormat * SwUndoTextFormatCollCreate::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeTextFormatColl(m_sNewName, static_cast<SwTextFormatColl *>(pDerivedFrom), true); +} + +void SwUndoTextFormatCollCreate::Delete() +{ + m_pDoc->DelTextFormatColl(static_cast<SwTextFormatColl *>(m_pNew), true); +} + +SwFormat * SwUndoTextFormatCollCreate::Find(const OUString & rName) const +{ + return m_pDoc->FindTextFormatCollByName(rName); +} + +SwUndoTextFormatCollDelete::SwUndoTextFormatCollDelete(SwTextFormatColl const * _pOld, + SwDoc * _pDoc) + : SwUndoFormatDelete(SwUndoId::TXTFMTCOL_DELETE, _pOld, _pDoc) +{ +} + +SwFormat * SwUndoTextFormatCollDelete::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeTextFormatColl(m_sOldName, static_cast<SwTextFormatColl *>(pDerivedFrom), true); +} + +void SwUndoTextFormatCollDelete::Delete(SwFormat * pOld) +{ + m_pDoc->DelTextFormatColl(static_cast<SwTextFormatColl *>(pOld), true); +} + +SwFormat * SwUndoTextFormatCollDelete::Find(const OUString & rName) const +{ + return m_pDoc->FindTextFormatCollByName(rName); +} + +SwUndoCondTextFormatCollCreate::SwUndoCondTextFormatCollCreate(SwConditionTextFormatColl *_pNew, + SwTextFormatColl const *_pDerivedFrom, SwDoc *_pDoc) + : SwUndoTextFormatCollCreate(_pNew, _pDerivedFrom, _pDoc) +{ +} + +SwFormat * SwUndoCondTextFormatCollCreate::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeCondTextFormatColl(m_sNewName, static_cast<SwTextFormatColl *>(pDerivedFrom), true); +} + +SwUndoCondTextFormatCollDelete::SwUndoCondTextFormatCollDelete(SwTextFormatColl const * _pOld, + SwDoc * _pDoc) + : SwUndoTextFormatCollDelete(_pOld, _pDoc) +{ +} + +SwFormat * SwUndoCondTextFormatCollDelete::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeCondTextFormatColl(m_sOldName, static_cast<SwTextFormatColl *>(pDerivedFrom), true); +} + +SwUndoRenameFormatColl::SwUndoRenameFormatColl(const OUString & sInitOldName, + const OUString & sInitNewName, + SwDoc * _pDoc) + : SwUndoRenameFormat(SwUndoId::TXTFMTCOL_RENAME, sInitOldName, sInitNewName, _pDoc) +{ +} + +SwFormat * SwUndoRenameFormatColl::Find(const OUString & rName) const +{ + return m_pDoc->FindTextFormatCollByName(rName); +} + +SwUndoCharFormatCreate::SwUndoCharFormatCreate(SwCharFormat * pNewFormat, + SwCharFormat const * pDerivedFrom, + SwDoc * pDocument) + : SwUndoFormatCreate(SwUndoId::CHARFMT_CREATE, pNewFormat, pDerivedFrom, pDocument) +{ +} + +SwFormat * SwUndoCharFormatCreate::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeCharFormat(m_sNewName, static_cast<SwCharFormat *>(pDerivedFrom), true); +} + +void SwUndoCharFormatCreate::Delete() +{ + m_pDoc->DelCharFormat(static_cast<SwCharFormat *>(m_pNew), true); +} + +SwFormat * SwUndoCharFormatCreate::Find(const OUString & rName) const +{ + return m_pDoc->FindCharFormatByName(rName); +} + +SwUndoCharFormatDelete::SwUndoCharFormatDelete(SwCharFormat const * pOld, SwDoc * pDocument) + : SwUndoFormatDelete(SwUndoId::CHARFMT_DELETE, pOld, pDocument) +{ +} + +SwFormat * SwUndoCharFormatDelete::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeCharFormat(m_sOldName, static_cast<SwCharFormat *>(pDerivedFrom), true); +} + +void SwUndoCharFormatDelete::Delete(SwFormat * pFormat) +{ + m_pDoc->DelCharFormat(static_cast<SwCharFormat *>(pFormat), true); +} + +SwFormat * SwUndoCharFormatDelete::Find(const OUString & rName) const +{ + return m_pDoc->FindCharFormatByName(rName); +} + +SwUndoRenameCharFormat::SwUndoRenameCharFormat(const OUString & sInitOldName, + const OUString & sInitNewName, + SwDoc * pDocument) + : SwUndoRenameFormat(SwUndoId::CHARFMT_RENAME, sInitOldName, sInitNewName, pDocument) +{ +} + +SwFormat * SwUndoRenameCharFormat::Find(const OUString & rName) const +{ + return m_pDoc->FindCharFormatByName(rName); +} + +SwUndoFrameFormatCreate::SwUndoFrameFormatCreate(SwFrameFormat * pNewFormat, + SwFrameFormat const * pDerivedFrom, + SwDoc * pDocument) + : SwUndoFormatCreate(SwUndoId::FRMFMT_CREATE, pNewFormat, pDerivedFrom, pDocument) +{ +} + +SwFormat * SwUndoFrameFormatCreate::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeFrameFormat(m_sNewName, static_cast<SwFrameFormat *>(pDerivedFrom), true, m_pNew->IsAuto()); +} + +void SwUndoFrameFormatCreate::Delete() +{ + m_pDoc->DelFrameFormat(static_cast<SwFrameFormat *>(m_pNew), true); +} + +SwFormat * SwUndoFrameFormatCreate::Find(const OUString & rName) const +{ + return m_pDoc->FindFrameFormatByName(rName); +} + +SwUndoFrameFormatDelete::SwUndoFrameFormatDelete(SwFrameFormat const * pOld, SwDoc * pDocument) + : SwUndoFormatDelete(SwUndoId::FRMFMT_DELETE, pOld, pDocument) +{ +} + +SwFormat * SwUndoFrameFormatDelete::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeFrameFormat(m_sOldName, static_cast<SwFrameFormat *>(pDerivedFrom), true); +} + +void SwUndoFrameFormatDelete::Delete(SwFormat * pFormat) +{ + m_pDoc->DelFrameFormat(static_cast<SwFrameFormat *>(pFormat), true); +} + +SwFormat * SwUndoFrameFormatDelete::Find(const OUString & rName) const +{ + return m_pDoc->FindFrameFormatByName(rName); +} + +SwUndoRenameFrameFormat::SwUndoRenameFrameFormat(const OUString & sInitOldName, + const OUString & sInitNewName, + SwDoc * pDocument) + : SwUndoRenameFormat(SwUndoId::FRMFMT_RENAME, sInitOldName, sInitNewName, pDocument) +{ +} + +SwFormat * SwUndoRenameFrameFormat::Find(const OUString & rName) const +{ + return m_pDoc->FindFrameFormatByName(rName); +} + +SwUndoNumruleCreate::SwUndoNumruleCreate(const SwNumRule * _pNew, + SwDoc * _pDoc) + : SwUndo(SwUndoId::NUMRULE_CREATE, _pDoc), m_pNew(_pNew), m_aNew(*_pNew), m_pDoc(_pDoc), + m_bInitialized(false) +{ +} + +void SwUndoNumruleCreate::UndoImpl(::sw::UndoRedoContext &) +{ + if (! m_bInitialized) + { + m_aNew = *m_pNew; + m_bInitialized = true; + } + + m_pDoc->DelNumRule(m_aNew.GetName(), true); +} + +void SwUndoNumruleCreate::RedoImpl(::sw::UndoRedoContext &) +{ + m_pDoc->MakeNumRule(m_aNew.GetName(), &m_aNew, true); +} + +SwRewriter SwUndoNumruleCreate::GetRewriter() const +{ + SwRewriter aResult; + + if (! m_bInitialized) + { + m_aNew = *m_pNew; + m_bInitialized = true; + } + + aResult.AddRule(UndoArg1, m_aNew.GetName()); + + return aResult; +} + +SwUndoNumruleDelete::SwUndoNumruleDelete(const SwNumRule & rRule, + SwDoc * _pDoc) + : SwUndo(SwUndoId::NUMRULE_DELETE, _pDoc), m_aOld(rRule), m_pDoc(_pDoc) +{ +} + +void SwUndoNumruleDelete::UndoImpl(::sw::UndoRedoContext &) +{ + m_pDoc->MakeNumRule(m_aOld.GetName(), &m_aOld, true); +} + +void SwUndoNumruleDelete::RedoImpl(::sw::UndoRedoContext &) +{ + m_pDoc->DelNumRule(m_aOld.GetName(), true); +} + +SwRewriter SwUndoNumruleDelete::GetRewriter() const +{ + SwRewriter aResult; + + aResult.AddRule(UndoArg1, m_aOld.GetName()); + + return aResult; +} + +SwUndoNumruleRename::SwUndoNumruleRename(const OUString & _aOldName, + const OUString & _aNewName, + SwDoc * _pDoc) + : SwUndo(SwUndoId::NUMRULE_RENAME, _pDoc), m_aOldName(_aOldName), m_aNewName(_aNewName), + m_pDoc(_pDoc) +{ +} + +void SwUndoNumruleRename::UndoImpl(::sw::UndoRedoContext &) +{ + m_pDoc->RenameNumRule(m_aNewName, m_aOldName, true); +} + +void SwUndoNumruleRename::RedoImpl(::sw::UndoRedoContext &) +{ + m_pDoc->RenameNumRule(m_aOldName, m_aNewName, true); +} + +SwRewriter SwUndoNumruleRename::GetRewriter() const +{ + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, m_aOldName); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + aRewriter.AddRule(UndoArg3, m_aNewName); + + return aRewriter; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/SwUndoPageDesc.cxx b/sw/source/core/undo/SwUndoPageDesc.cxx new file mode 100644 index 000000000..1ca66f9fb --- /dev/null +++ b/sw/source/core/undo/SwUndoPageDesc.cxx @@ -0,0 +1,341 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <swundo.hxx> +#include <pagedesc.hxx> +#include <SwUndoPageDesc.hxx> +#include <SwRewriter.hxx> +#include <undobj.hxx> +#include <strings.hrc> +#include <fmtcntnt.hxx> +#include <fmthdft.hxx> +#include <osl/diagnose.h> + +SwUndoPageDesc::SwUndoPageDesc(const SwPageDesc & _aOld, + const SwPageDesc & _aNew, + SwDoc * _pDoc) + : SwUndo( _aOld.GetName() != _aNew.GetName() ? + SwUndoId::RENAME_PAGEDESC : + SwUndoId::CHANGE_PAGEDESC, + _pDoc ), + m_aOld(_aOld, _pDoc), m_aNew(_aNew, _pDoc), m_pDoc(_pDoc), m_bExchange( false ) +{ + OSL_ENSURE(nullptr != m_pDoc, "no document?"); + + /* + The page description changes. + If there are no header/footer content changes like header on/off or change from shared content + to unshared etc., there is no reason to duplicate the content nodes (Crash i55547) + But this happens, this Undo Ctor will destroy the unnecessary duplicate and manipulate the + content pointer of the both page descriptions. + */ + SwPageDesc &rOldDesc = m_aOld.m_PageDesc; + SwPageDesc &rNewDesc = m_aNew.m_PageDesc; + const SwFormatHeader& rOldHead = rOldDesc.GetMaster().GetHeader(); + const SwFormatHeader& rNewHead = rNewDesc.GetMaster().GetHeader(); + const SwFormatFooter& rOldFoot = rOldDesc.GetMaster().GetFooter(); + const SwFormatFooter& rNewFoot = rNewDesc.GetMaster().GetFooter(); + /* bExchange must not be set, if the old page descriptor will stay active. + Two known situations: + #i67735#: renaming a page descriptor + #i67334#: changing the follow style + If header/footer will be activated or deactivated, this undo will not work. + */ + m_bExchange = ( m_aOld.GetName() == m_aNew.GetName() ) && + ( _aOld.GetFollow() == _aNew.GetFollow() ) && + ( rOldHead.IsActive() == rNewHead.IsActive() ) && + ( rOldFoot.IsActive() == rNewFoot.IsActive() ); + if( rOldHead.IsActive() && ( rOldDesc.IsHeaderShared() != rNewDesc.IsHeaderShared() ) ) + m_bExchange = false; + if( rOldFoot.IsActive() && ( rOldDesc.IsFooterShared() != rNewDesc.IsFooterShared() ) ) + m_bExchange = false; + if( ( rOldHead.IsActive() || rOldFoot.IsActive() ) && ( rOldDesc.IsFirstShared() != rNewDesc.IsFirstShared() ) ) + m_bExchange = false; + if( m_bExchange ) + { + if( rNewHead.IsActive() ) + { + SwFrameFormat* pFormat = new SwFrameFormat( *rNewHead.GetHeaderFormat() ); + // The Ctor of this object will remove the duplicate! + SwFormatHeader aFormatHeader(pFormat); + (void)aFormatHeader; + if (!rNewDesc.IsHeaderShared()) + { + pFormat = new SwFrameFormat( *rNewDesc.GetLeft().GetHeader().GetHeaderFormat() ); + // The Ctor of this object will remove the duplicate! + SwFormatHeader aLeftHeader(pFormat); + (void)aLeftHeader; + } + if (!rNewDesc.IsFirstShared()) + { + pFormat = new SwFrameFormat( *rNewDesc.GetFirstMaster().GetHeader().GetHeaderFormat() ); + // The Ctor of this object will remove the duplicate! + SwFormatHeader aFirstHeader(pFormat); + (void)aFirstHeader; + } + } + // Same procedure for footers... + if( rNewFoot.IsActive() ) + { + SwFrameFormat* pFormat = new SwFrameFormat( *rNewFoot.GetFooterFormat() ); + // The Ctor of this object will remove the duplicate! + SwFormatFooter aFormatFooter(pFormat); + (void)aFormatFooter; + if (!rNewDesc.IsFooterShared()) + { + pFormat = new SwFrameFormat( *rNewDesc.GetLeft().GetFooter().GetFooterFormat() ); + // The Ctor of this object will remove the duplicate! + SwFormatFooter aLeftFooter(pFormat); + (void)aLeftFooter; + } + if (!rNewDesc.IsFirstShared()) + { + pFormat = new SwFrameFormat( *rNewDesc.GetFirstMaster().GetFooter().GetFooterFormat() ); + // The Ctor of this object will remove the duplicate! + SwFormatFooter aFirstFooter(pFormat); + (void)aFirstFooter; + } + } + + // After this exchange method the old page description will point to zero, + // the new one will point to the node position of the original content nodes. + ExchangeContentNodes( m_aOld.m_PageDesc, m_aNew.m_PageDesc ); + } +} + +SwUndoPageDesc::~SwUndoPageDesc() +{ +} + +void SwUndoPageDesc::ExchangeContentNodes( SwPageDesc& rSource, SwPageDesc &rDest ) +{ + OSL_ENSURE( m_bExchange, "You shouldn't do that." ); + const SwFormatHeader& rDestHead = rDest.GetMaster().GetHeader(); + const SwFormatHeader& rSourceHead = rSource.GetMaster().GetHeader(); + if( rDestHead.IsActive() ) + { + // Let the destination page description point to the source node position, + // from now on this descriptor is responsible for the content nodes! + const SfxPoolItem* pItem; + rDest.GetMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone()); + SwFrameFormat* pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat(); + pNewFormat->SetFormatAttr( rSourceHead.GetHeaderFormat()->GetContent() ); + + // Let the source page description point to zero node position, + // it loses the responsible and can be destroyed without removing the content nodes. + rSource.GetMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); + + if( !rDest.IsHeaderShared() ) + { + // Same procedure for unshared header... + const SwFormatHeader& rSourceLeftHead = rSource.GetLeft().GetHeader(); + rDest.GetLeft().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat(); + pNewFormat->SetFormatAttr( rSourceLeftHead.GetHeaderFormat()->GetContent() ); + rSource.GetLeft().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); + } + if (!rDest.IsFirstShared()) + { + // Same procedure for unshared header... + const SwFormatHeader& rSourceFirstMasterHead = rSource.GetFirstMaster().GetHeader(); + rDest.GetFirstMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat(); + pNewFormat->SetFormatAttr( rSourceFirstMasterHead.GetHeaderFormat()->GetContent() ); + rSource.GetFirstMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); + } + } + // Same procedure for footers... + const SwFormatFooter& rDestFoot = rDest.GetMaster().GetFooter(); + const SwFormatFooter& rSourceFoot = rSource.GetMaster().GetFooter(); + if( !rDestFoot.IsActive() ) + return; + + const SfxPoolItem* pItem; + rDest.GetMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone()); + SwFrameFormat *pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( rSourceFoot.GetFooterFormat()->GetContent() ); + + rSource.GetMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); + + if( !rDest.IsFooterShared() ) + { + const SwFormatFooter& rSourceLeftFoot = rSource.GetLeft().GetFooter(); + rDest.GetLeft().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( rSourceLeftFoot.GetFooterFormat()->GetContent() ); + rSource.GetLeft().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); + } + if (rDest.IsFirstShared()) + return; + + const SwFormatFooter& rSourceFirstMasterFoot = rSource.GetFirstMaster().GetFooter(); + rDest.GetFirstMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( rSourceFirstMasterFoot.GetFooterFormat()->GetContent() ); + rSource.GetFirstMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); +} + +void SwUndoPageDesc::UndoImpl(::sw::UndoRedoContext &) +{ + // Move (header/footer)content node responsibility from new page descriptor to old one again. + if( m_bExchange ) + ExchangeContentNodes( m_aNew.m_PageDesc, m_aOld.m_PageDesc ); + m_pDoc->ChgPageDesc(m_aOld.GetName(), m_aOld); +} + +void SwUndoPageDesc::RedoImpl(::sw::UndoRedoContext &) +{ + // Move (header/footer)content node responsibility from old page descriptor to new one again. + if( m_bExchange ) + ExchangeContentNodes( m_aOld.m_PageDesc, m_aNew.m_PageDesc ); + m_pDoc->ChgPageDesc(m_aNew.GetName(), m_aNew); +} + +SwRewriter SwUndoPageDesc::GetRewriter() const +{ + SwRewriter aResult; + + aResult.AddRule(UndoArg1, m_aOld.GetName()); + aResult.AddRule(UndoArg2, SwResId(STR_YIELDS)); + aResult.AddRule(UndoArg3, m_aNew.GetName()); + + return aResult; +} + +SwUndoPageDescCreate::SwUndoPageDescCreate(const SwPageDesc * pNew, + SwDoc * _pDoc) + : SwUndo(SwUndoId::CREATE_PAGEDESC, _pDoc), m_pDesc(pNew), m_aNew(*pNew, _pDoc), + m_pDoc(_pDoc) +{ + OSL_ENSURE(nullptr != m_pDoc, "no document?"); +} + +SwUndoPageDescCreate::~SwUndoPageDescCreate() +{ +} + +void SwUndoPageDescCreate::UndoImpl(::sw::UndoRedoContext &) +{ + if (m_pDesc) + { + m_aNew = *m_pDesc; + m_pDesc = nullptr; + } + + m_pDoc->DelPageDesc(m_aNew.GetName(), true); +} + +void SwUndoPageDescCreate::DoImpl() +{ + SwPageDesc aPageDesc = m_aNew; + m_pDoc->MakePageDesc(m_aNew.GetName(), &aPageDesc, false, true); +} + +void SwUndoPageDescCreate::RedoImpl(::sw::UndoRedoContext &) +{ + DoImpl(); +} + +void SwUndoPageDescCreate::RepeatImpl(::sw::RepeatContext &) +{ + ::sw::UndoGuard const undoGuard(m_pDoc->GetIDocumentUndoRedo()); + DoImpl(); +} + +SwRewriter SwUndoPageDescCreate::GetRewriter() const +{ + SwRewriter aResult; + + if (m_pDesc) + aResult.AddRule(UndoArg1, m_pDesc->GetName()); + else + aResult.AddRule(UndoArg1, m_aNew.GetName()); + + return aResult; +} + +SwUndoPageDescDelete::SwUndoPageDescDelete(const SwPageDesc & _aOld, + SwDoc * _pDoc) + : SwUndo(SwUndoId::DELETE_PAGEDESC, _pDoc), m_aOld(_aOld, _pDoc), m_pDoc(_pDoc) +{ + OSL_ENSURE(nullptr != m_pDoc, "no document?"); +} + +SwUndoPageDescDelete::~SwUndoPageDescDelete() +{ +} + +void SwUndoPageDescDelete::UndoImpl(::sw::UndoRedoContext &) +{ + SwPageDesc aPageDesc = m_aOld; + m_pDoc->MakePageDesc(m_aOld.GetName(), &aPageDesc, false, true); +} + +void SwUndoPageDescDelete::DoImpl() +{ + m_pDoc->DelPageDesc(m_aOld.GetName(), true); +} + +void SwUndoPageDescDelete::RedoImpl(::sw::UndoRedoContext &) +{ + DoImpl(); +} + +void SwUndoPageDescDelete::RepeatImpl(::sw::RepeatContext &) +{ + ::sw::UndoGuard const undoGuard(m_pDoc->GetIDocumentUndoRedo()); + DoImpl(); +} + +SwRewriter SwUndoPageDescDelete::GetRewriter() const +{ + SwRewriter aResult; + + aResult.AddRule(UndoArg1, m_aOld.GetName()); + + return aResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/SwUndoTOXChange.cxx b/sw/source/core/undo/SwUndoTOXChange.cxx new file mode 100644 index 000000000..c88182b7a --- /dev/null +++ b/sw/source/core/undo/SwUndoTOXChange.cxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <SwUndoTOXChange.hxx> +#include <swundo.hxx> +#include <UndoCore.hxx> +#include <doctxm.hxx> +#include <doc.hxx> +#include <node.hxx> + +namespace +{ + sal_uLong GetSectionNodeIndex(SwTOXBaseSection const& rTOX) + { + const SwSectionNode* pSectNd = rTOX.GetFormat()->GetSectionNode(); + assert(pSectNd); + return pSectNd->GetIndex(); + } +} + +SwUndoTOXChange::SwUndoTOXChange(const SwDoc *pDoc, + SwTOXBaseSection const& rTOX, SwTOXBase const& rNew) + : SwUndo(SwUndoId::TOXCHANGE, pDoc) + , m_Old(rTOX) + , m_New(rNew) + , m_nNodeIndex(GetSectionNodeIndex(rTOX)) +{ +} + +SwUndoTOXChange::~SwUndoTOXChange() +{ +} + +// get the current ToXBase, which is not necessarily the same instance that existed there before +static SwTOXBase & GetTOX(SwDoc & rDoc, sal_uLong const nNodeIndex) +{ + SwSectionNode *const pNode(rDoc.GetNodes()[nNodeIndex]->GetSectionNode()); + assert(pNode); + assert(dynamic_cast<SwTOXBaseSection*>(&pNode->GetSection())); + auto & rTOX(static_cast<SwTOXBaseSection&>(pNode->GetSection())); + return rTOX; +} + +void SwUndoTOXChange::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc(rContext.GetDoc()); + SwTOXBase & rTOX(GetTOX(rDoc, m_nNodeIndex)); + rTOX = m_Old; +} + +void SwUndoTOXChange::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc(rContext.GetDoc()); + SwTOXBase & rTOX(GetTOX(rDoc, m_nNodeIndex)); + rTOX = m_New; +} + +void SwUndoTOXChange::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc(rContext.GetDoc()); + SwTOXBase *const pTOX(SwDoc::GetCurTOX(*rContext.GetRepeatPaM().GetPoint())); + if (pTOX) + { + rDoc.ChangeTOX(*pTOX, m_New); + // intentionally limited to not Update because we'd need layout + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/docundo.cxx b/sw/source/core/undo/docundo.cxx new file mode 100644 index 000000000..61629e087 --- /dev/null +++ b/sw/source/core/undo/docundo.cxx @@ -0,0 +1,735 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoManager.hxx> + +#include <doc.hxx> +#include <docsh.hxx> +#include <view.hxx> +#include <drawdoc.hxx> +#include <ndarr.hxx> +#include <pam.hxx> +#include <swundo.hxx> +#include <UndoCore.hxx> +#include <editsh.hxx> +#include <unobaseclass.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentState.hxx> +#include <comphelper/lok.hxx> +#include <assert.h> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> + +using namespace ::com::sun::star; + +// the undo array should never grow beyond this limit: +#define UNDO_ACTION_LIMIT (USHRT_MAX - 1000) + +namespace sw { + +UndoManager::UndoManager(std::shared_ptr<SwNodes> const & xUndoNodes, + IDocumentDrawModelAccess & rDrawModelAccess, + IDocumentRedlineAccess & rRedlineAccess, + IDocumentState & rState) + : m_rDrawModelAccess(rDrawModelAccess) + , m_rRedlineAccess(rRedlineAccess) + , m_rState(rState) + , m_xUndoNodes(xUndoNodes) + , m_bGroupUndo(true) + , m_bDrawUndo(true) + , m_bRepair(false) + , m_bLockUndoNoModifiedPosition(false) + , m_isAddWithIgnoreRepeat(false) + , m_UndoSaveMark(MARK_INVALID) + , m_pDocShell(nullptr) + , m_pView(nullptr) +{ + assert(bool(m_xUndoNodes)); + // writer expects it to be disabled initially + // Undo is enabled by SwEditShell constructor + SdrUndoManager::EnableUndo(false); +} + +SwNodes const& UndoManager::GetUndoNodes() const +{ + return *m_xUndoNodes; +} + +SwNodes & UndoManager::GetUndoNodes() +{ + return *m_xUndoNodes; +} + +bool UndoManager::IsUndoNodes(SwNodes const& rNodes) const +{ + return & rNodes == m_xUndoNodes.get(); +} + +void UndoManager::SetDocShell(SwDocShell* pDocShell) +{ + m_pDocShell = pDocShell; +} + +void UndoManager::SetView(SwView* pView) +{ + m_pView = pView; +} + +size_t UndoManager::GetUndoActionCount(const bool bCurrentLevel) const +{ + size_t nRet = SdrUndoManager::GetUndoActionCount(bCurrentLevel); + if (!comphelper::LibreOfficeKit::isActive() || !m_pView) + return nRet; + + if (!nRet || !SdrUndoManager::GetUndoActionCount()) + return nRet; + + const SfxUndoAction* pAction = SdrUndoManager::GetUndoAction(); + if (!pAction) + return nRet; + + if (!m_bRepair) + { + // If another view created the last undo action, prevent undoing it from this view. + ViewShellId nViewShellId = m_pView->GetViewShellId(); + if (pAction->GetViewShellId() != nViewShellId) + nRet = 0; + } + + return nRet; +} + +size_t UndoManager::GetRedoActionCount(const bool bCurrentLevel) const +{ + size_t nRet = SdrUndoManager::GetRedoActionCount(bCurrentLevel); + if (!comphelper::LibreOfficeKit::isActive() || !m_pView) + return nRet; + + if (!nRet || !SdrUndoManager::GetRedoActionCount()) + return nRet; + + const SfxUndoAction* pAction = SdrUndoManager::GetRedoAction(); + if (!pAction) + return nRet; + + if (!m_bRepair) + { + // If another view created the first redo action, prevent redoing it from this view. + ViewShellId nViewShellId = m_pView->GetViewShellId(); + if (pAction->GetViewShellId() != nViewShellId) + nRet = 0; + } + + return nRet; +} + +void UndoManager::DoUndo(bool const bDoUndo) +{ + if(!isTextEditActive()) + { + EnableUndo(bDoUndo); + + SwDrawModel*const pSdrModel = m_rDrawModelAccess.GetDrawModel(); + if( pSdrModel ) + { + pSdrModel->EnableUndo(bDoUndo); + } + } +} + +bool UndoManager::DoesUndo() const +{ + if(isTextEditActive()) + { + return false; + } + else + { + return IsUndoEnabled(); + } +} + +void UndoManager::DoGroupUndo(bool const bDoUndo) +{ + m_bGroupUndo = bDoUndo; +} + +bool UndoManager::DoesGroupUndo() const +{ + return m_bGroupUndo; +} + +void UndoManager::DoDrawUndo(bool const bDoUndo) +{ + m_bDrawUndo = bDoUndo; +} + +bool UndoManager::DoesDrawUndo() const +{ + return m_bDrawUndo; +} + +void UndoManager::DoRepair(bool bRepair) +{ + m_bRepair = bRepair; +} + +bool UndoManager::DoesRepair() const +{ + return m_bRepair; +} + +bool UndoManager::IsUndoNoResetModified() const +{ + return MARK_INVALID == m_UndoSaveMark; +} + +void UndoManager::SetUndoNoResetModified() +{ + if (MARK_INVALID != m_UndoSaveMark) + { + RemoveMark(m_UndoSaveMark); + m_UndoSaveMark = MARK_INVALID; + } +} + +void UndoManager::SetUndoNoModifiedPosition() +{ + if (!m_bLockUndoNoModifiedPosition) + { + m_UndoSaveMark = MarkTopUndoAction(); + } +} + +void UndoManager::LockUndoNoModifiedPosition() +{ + m_bLockUndoNoModifiedPosition = true; +} + +void UndoManager::UnLockUndoNoModifiedPosition() +{ + m_bLockUndoNoModifiedPosition = false; +} + +SwUndo* UndoManager::GetLastUndo() +{ + if (!SdrUndoManager::GetUndoActionCount()) + { + return nullptr; + } + SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction() ); + return dynamic_cast<SwUndo*>(pAction); +} + +void UndoManager::AppendUndo(std::unique_ptr<SwUndo> pUndo) +{ + AddUndoAction(std::move(pUndo)); +} + +void UndoManager::ClearRedo() +{ + return SdrUndoManager::ImplClearRedo_NoLock(TopLevel); +} + +void UndoManager::DelAllUndoObj() +{ + ::sw::UndoGuard const undoGuard(*this); + + SdrUndoManager::ClearAllLevels(); + + m_UndoSaveMark = MARK_INVALID; +} + +SwUndoId +UndoManager::StartUndo(SwUndoId const i_eUndoId, + SwRewriter const*const pRewriter) +{ + if (!IsUndoEnabled()) + { + return SwUndoId::EMPTY; + } + + SwUndoId const eUndoId( (i_eUndoId == SwUndoId::EMPTY) ? SwUndoId::START : i_eUndoId ); + + assert(SwUndoId::END != eUndoId); + OUString comment( (SwUndoId::START == eUndoId) + ? OUString("??") + : GetUndoComment(eUndoId) ); + if (pRewriter) + { + assert(SwUndoId::START != eUndoId); + comment = pRewriter->Apply(comment); + } + + ViewShellId nViewShellId(-1); + if (m_pDocShell) + { + if (const SwView* pView = m_pDocShell->GetView()) + nViewShellId = pView->GetViewShellId(); + } + SdrUndoManager::EnterListAction(comment, comment, static_cast<sal_uInt16>(eUndoId), nViewShellId); + + return eUndoId; +} + +SwUndoId +UndoManager::EndUndo(SwUndoId eUndoId, SwRewriter const*const pRewriter) +{ + if (!IsUndoEnabled()) + { + return SwUndoId::EMPTY; + } + + if ((eUndoId == SwUndoId::EMPTY) || (SwUndoId::START == eUndoId)) + eUndoId = SwUndoId::END; + OSL_ENSURE(!((SwUndoId::END == eUndoId) && pRewriter), + "EndUndo(): no Undo ID, but rewriter given?"); + + SfxUndoAction *const pLastUndo( + (0 == SdrUndoManager::GetUndoActionCount()) + ? nullptr : SdrUndoManager::GetUndoAction() ); + + int const nCount = LeaveListAction(); + + if (nCount) // otherwise: empty list action not inserted! + { + assert(pLastUndo); + assert(SwUndoId::START != eUndoId); + auto pListAction = dynamic_cast<SfxListUndoAction*>(SdrUndoManager::GetUndoAction()); + assert(pListAction); + if (SwUndoId::END != eUndoId) + { + OSL_ENSURE(static_cast<SwUndoId>(pListAction->GetId()) == eUndoId, + "EndUndo(): given ID different from StartUndo()"); + // comment set by caller of EndUndo + OUString comment = GetUndoComment(eUndoId); + if (pRewriter) + { + comment = pRewriter->Apply(comment); + } + pListAction->SetComment(comment); + } + else if (SwUndoId::START != static_cast<SwUndoId>(pListAction->GetId())) + { + // comment set by caller of StartUndo: nothing to do here + } + else if (pLastUndo) + { + // comment was not set at StartUndo or EndUndo: + // take comment of last contained action + // (note that this works recursively, i.e. the last contained + // action may be a list action created by StartUndo/EndUndo) + OUString const comment(pLastUndo->GetComment()); + pListAction->SetComment(comment); + } + else + { + OSL_ENSURE(false, "EndUndo(): no comment?"); + } + } + + return eUndoId; +} + +bool +UndoManager::GetLastUndoInfo( + OUString *const o_pStr, SwUndoId *const o_pId, const SwView* pView) const +{ + // this is actually expected to work on the current level, + // but that was really not obvious from the previous implementation... + if (!SdrUndoManager::GetUndoActionCount()) + { + return false; + } + + SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction() ); + + if (comphelper::LibreOfficeKit::isActive() && !m_bRepair) + { + // If another view created the undo action, prevent undoing it from this view. + ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId(); + if (pAction->GetViewShellId() != nViewShellId) + { + if (o_pId) + { + *o_pId = SwUndoId::CONFLICT; + } + return false; + } + } + + if (o_pStr) + { + *o_pStr = pAction->GetComment(); + } + if (o_pId) + { + if (auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction)) + *o_pId = static_cast<SwUndoId>(pListAction->GetId()); + else if (auto pSwAction = dynamic_cast<const SwUndo*>(pAction)) + *o_pId = pSwAction->GetId(); + else + *o_pId = SwUndoId::EMPTY; + } + + return true; +} + +SwUndoComments_t UndoManager::GetUndoComments() const +{ + OSL_ENSURE(!SdrUndoManager::IsInListAction(), + "GetUndoComments() called while in list action?"); + + SwUndoComments_t ret; + const size_t nUndoCount(SdrUndoManager::GetUndoActionCount(TopLevel)); + for (size_t n = 0; n < nUndoCount; ++n) + { + OUString const comment( + SdrUndoManager::GetUndoActionComment(n, TopLevel)); + ret.push_back(comment); + } + + return ret; +} + +bool UndoManager::GetFirstRedoInfo(OUString *const o_pStr, + SwUndoId *const o_pId, + const SwView* pView) const +{ + if (!SdrUndoManager::GetRedoActionCount()) + { + return false; + } + + SfxUndoAction *const pAction( SdrUndoManager::GetRedoAction() ); + if ( pAction == nullptr ) + { + return false; + } + + if (comphelper::LibreOfficeKit::isActive() && !m_bRepair) + { + // If another view created the undo action, prevent redoing it from this view. + ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId(); + if (pAction->GetViewShellId() != nViewShellId) + { + if (o_pId) + { + *o_pId = SwUndoId::CONFLICT; + } + return false; + } + } + + if (o_pStr) + { + *o_pStr = pAction->GetComment(); + } + if (o_pId) + { + if (auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction)) + *o_pId = static_cast<SwUndoId>(pListAction->GetId()); + else if (auto pSwAction = dynamic_cast<const SwUndo*>(pAction)) + *o_pId = pSwAction->GetId(); + else + *o_pId = SwUndoId::EMPTY; + } + + return true; +} + +SwUndoComments_t UndoManager::GetRedoComments() const +{ + OSL_ENSURE(!SdrUndoManager::IsInListAction(), + "GetRedoComments() called while in list action?"); + + SwUndoComments_t ret; + const size_t nRedoCount(SdrUndoManager::GetRedoActionCount(TopLevel)); + for (size_t n = 0; n < nRedoCount; ++n) + { + OUString const comment( + SdrUndoManager::GetRedoActionComment(n, TopLevel)); + ret.push_back(comment); + } + + return ret; +} + +SwUndoId UndoManager::GetRepeatInfo(OUString *const o_pStr) const +{ + SwUndoId nRepeatId(SwUndoId::EMPTY); + GetLastUndoInfo(o_pStr, & nRepeatId); + if( SwUndoId::REPEAT_START <= nRepeatId && SwUndoId::REPEAT_END > nRepeatId ) + { + return nRepeatId; + } + if (o_pStr) // not repeatable -> clear comment + { + o_pStr->clear(); + } + return SwUndoId::EMPTY; +} + +SwUndo * UndoManager::RemoveLastUndo() +{ + if (SdrUndoManager::GetRedoActionCount() || + SdrUndoManager::GetRedoActionCount(TopLevel)) + { + OSL_ENSURE(false, "RemoveLastUndoAction(): there are Redo actions?"); + return nullptr; + } + if (!SdrUndoManager::GetUndoActionCount()) + { + OSL_ENSURE(false, "RemoveLastUndoAction(): no Undo actions"); + return nullptr; + } + SfxUndoAction *const pLastUndo(GetUndoAction()); + SdrUndoManager::RemoveLastUndoAction(); + return dynamic_cast<SwUndo *>(pLastUndo); +} + +// SfxUndoManager + +void UndoManager::AddUndoAction(std::unique_ptr<SfxUndoAction> pAction, bool bTryMerge) +{ + SwUndo *const pUndo( dynamic_cast<SwUndo *>(pAction.get()) ); + if (pUndo) + { + if (RedlineFlags::NONE == pUndo->GetRedlineFlags()) + { + pUndo->SetRedlineFlags( m_rRedlineAccess.GetRedlineFlags() ); + } + if (m_isAddWithIgnoreRepeat) + { + pUndo->IgnoreRepeat(); + } + } + SdrUndoManager::AddUndoAction(std::move(pAction), bTryMerge); + if (m_pDocShell) + { + SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst( m_pDocShell ); + while ( pViewFrame ) + { + pViewFrame->GetBindings().Invalidate( SID_UNDO ); + pViewFrame->GetBindings().Invalidate( SID_REDO ); + pViewFrame = SfxViewFrame::GetNext( *pViewFrame, m_pDocShell ); + } + } + + // if the undo nodes array is too large, delete some actions + while (UNDO_ACTION_LIMIT < GetUndoNodes().Count()) + { + RemoveOldestUndoAction(); + } +} + +namespace { + +class CursorGuard +{ +public: + CursorGuard(SwEditShell & rShell, bool const bSave) + : m_rShell(rShell) + , m_bSaveCursor(bSave) + { + if (m_bSaveCursor) + { + m_rShell.Push(); // prevent modification of current cursor + } + } + ~CursorGuard() COVERITY_NOEXCEPT_FALSE + { + if (m_bSaveCursor) + { + m_rShell.Pop(SwCursorShell::PopMode::DeleteCurrent); + } + } +private: + SwEditShell & m_rShell; + bool const m_bSaveCursor; +}; + +} + +bool UndoManager::impl_DoUndoRedo(UndoOrRedoType undoOrRedo) +{ + SwDoc & rDoc(*GetUndoNodes().GetDoc()); + + UnoActionContext c(& rDoc); // exception-safe StartAllAction/EndAllAction + + SwEditShell *const pEditShell( rDoc.GetEditShell() ); + + OSL_ENSURE(pEditShell, "sw::UndoManager needs a SwEditShell!"); + if (!pEditShell) + { + throw uno::RuntimeException(); + } + + // in case the model has controllers locked, the Undo should not + // change the view cursors! + bool const bSaveCursors(pEditShell->CursorsLocked()); + CursorGuard aCursorGuard(*pEditShell, bSaveCursors); + if (!bSaveCursors) + { + // (in case Undo was called via API) clear the cursors: + pEditShell->KillPams(); + pEditShell->SetMark(); + pEditShell->ClearMark(); + } + + bool bRet(false); + + ::sw::UndoRedoContext context(rDoc, *pEditShell); + + // N.B. these may throw! + if (UndoOrRedoType::Undo == undoOrRedo) + { + bRet = SdrUndoManager::UndoWithContext(context); + } + else + { + bRet = SdrUndoManager::RedoWithContext(context); + } + + if (bRet) + { + // if we are at the "last save" position, the document is not modified + if (SdrUndoManager::HasTopUndoActionMark(m_UndoSaveMark)) + { + m_rState.ResetModified(); + } + else + { + m_rState.SetModified(); + } + } + + pEditShell->HandleUndoRedoContext(context); + + return bRet; +} + +bool UndoManager::Undo() +{ + if(isTextEditActive()) + { + return SdrUndoManager::Undo(); + } + else + { + return impl_DoUndoRedo(UndoOrRedoType::Undo); + } +} + +bool UndoManager::Redo() +{ + if(isTextEditActive()) + { + return SdrUndoManager::Redo(); + } + else + { + return impl_DoUndoRedo(UndoOrRedoType::Redo); + } +} + +void UndoManager::EmptyActionsChanged() +{ + if (m_pDocShell) + { + m_pDocShell->Broadcast(SfxHint(SfxHintId::DocumentRepair)); + } +} + +/** N.B.: this does _not_ call SdrUndoManager::Repeat because it is not + possible to wrap a list action around it: + calling EnterListAction here will cause SdrUndoManager::Repeat + to repeat the list action! + */ +bool UndoManager::Repeat(::sw::RepeatContext & rContext, + sal_uInt16 const nRepeatCount) +{ + if (SdrUndoManager::IsInListAction()) + { + OSL_ENSURE(false, "repeat in open list action???"); + return false; + } + if (!SdrUndoManager::GetUndoActionCount(TopLevel)) + { + return false; + } + SfxUndoAction *const pRepeatAction(GetUndoAction()); + assert(pRepeatAction); + if (!pRepeatAction->CanRepeat(rContext)) + { + return false; + } + + OUString const comment(pRepeatAction->GetComment()); + OUString const rcomment(pRepeatAction->GetRepeatComment(rContext)); + SwUndoId nId; + if (auto const* const pSwAction = dynamic_cast<SwUndo*>(pRepeatAction)) + nId = pSwAction->GetId(); + else if (auto const* const pListAction = dynamic_cast<SfxListUndoAction*>(pRepeatAction)) + nId = static_cast<SwUndoId>(pListAction->GetId()); + else + return false; + if (DoesUndo()) + { + ViewShellId nViewShellId(-1); + if (m_pDocShell) + { + if (const SwView* pView = m_pDocShell->GetView()) + nViewShellId = pView->GetViewShellId(); + } + EnterListAction(comment, rcomment, static_cast<sal_uInt16>(nId), nViewShellId); + } + + SwPaM* pTmp = rContext.m_pCurrentPaM; + for(SwPaM& rPaM : rContext.GetRepeatPaM().GetRingContainer()) + { // iterate over ring + rContext.m_pCurrentPaM = &rPaM; + if (DoesUndo() && & rPaM != pTmp) + { + m_isAddWithIgnoreRepeat = true; + } + for (sal_uInt16 nRptCnt = nRepeatCount; nRptCnt > 0; --nRptCnt) + { + pRepeatAction->Repeat(rContext); + } + if (DoesUndo() && & rPaM != pTmp) + { + m_isAddWithIgnoreRepeat = false; + } + rContext.m_bDeleteRepeated = false; // reset for next PaM + } + rContext.m_pCurrentPaM = pTmp; + + if (DoesUndo()) + { + LeaveListAction(); + } + return true; +} + +} // namespace sw + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/rolbck.cxx b/sw/source/core/undo/rolbck.cxx new file mode 100644 index 000000000..206ad9343 --- /dev/null +++ b/sw/source/core/undo/rolbck.cxx @@ -0,0 +1,1485 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <rolbck.hxx> +#include <svl/itemiter.hxx> +#include <editeng/formatbreakitem.hxx> +#include <hints.hxx> +#include <hintids.hxx> +#include <fmtftn.hxx> +#include <fchrfmt.hxx> +#include <fmtflcnt.hxx> +#include <fmtrfmrk.hxx> +#include <fmtfld.hxx> +#include <fmtpdsc.hxx> +#include <txtfld.hxx> +#include <txtrfmrk.hxx> +#include <txttxmrk.hxx> +#include <txtftn.hxx> +#include <txtflcnt.hxx> +#include <fmtanchr.hxx> +#include <fmtcnct.hxx> +#include <frmfmt.hxx> +#include <ftnidx.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <docary.hxx> +#include <ndtxt.hxx> +#include <paratr.hxx> +#include <cellatr.hxx> +#include <fldbas.hxx> +#include <pam.hxx> +#include <swtable.hxx> +#include <UndoCore.hxx> +#include <IMark.hxx> +#include <charfmt.hxx> +#include <strings.hrc> +#include <bookmrk.hxx> +#include <frameformats.hxx> +#include <memory> + +OUString SwHistoryHint::GetDescription() const +{ + return OUString(); +} + +SwHistorySetFormat::SwHistorySetFormat( const SfxPoolItem* pFormatHt, sal_uLong nNd ) + : SwHistoryHint( HSTRY_SETFMTHNT ) + , m_pAttr( pFormatHt->Clone() ) + , m_nNodeIndex( nNd ) +{ + switch ( m_pAttr->Which() ) + { + case RES_PAGEDESC: + static_cast<SwFormatPageDesc&>(*m_pAttr).ChgDefinedIn( nullptr ); + break; + case RES_PARATR_DROP: + static_cast<SwFormatDrop&>(*m_pAttr).ChgDefinedIn( nullptr ); + break; + case RES_BOXATR_FORMULA: + { + // save formulas always in plain text + SwTableBoxFormula& rNew = static_cast<SwTableBoxFormula&>(*m_pAttr); + if ( rNew.IsIntrnlName() ) + { + const SwTableBoxFormula& rOld = + *static_cast<const SwTableBoxFormula*>(pFormatHt); + const SwNode* pNd = rOld.GetNodeOfFormula(); + if ( pNd ) + { + const SwTableNode* pTableNode = pNd->FindTableNode(); + if (pTableNode) + { + SwTableFormulaUpdate aMsgHint( &pTableNode->GetTable() ); + aMsgHint.m_eFlags = TBL_BOXNAME; + rNew.ChgDefinedIn( rOld.GetDefinedIn() ); + rNew.ChangeState( &aMsgHint ); + } + } + } + rNew.ChgDefinedIn( nullptr ); + } + break; + } +} + +OUString SwHistorySetFormat::GetDescription() const +{ + OUString aResult; + + switch (m_pAttr->Which()) + { + case RES_BREAK: + switch (static_cast<SvxFormatBreakItem &>(*m_pAttr).GetBreak()) + { + case SvxBreak::PageBefore: + case SvxBreak::PageAfter: + case SvxBreak::PageBoth: + aResult = SwResId(STR_UNDO_PAGEBREAKS); + + break; + case SvxBreak::ColumnBefore: + case SvxBreak::ColumnAfter: + case SvxBreak::ColumnBoth: + aResult = SwResId(STR_UNDO_COLBRKS); + + break; + default: + break; + } + break; + default: + break; + } + + return aResult; +} + +void SwHistorySetFormat::SetInDoc( SwDoc* pDoc, bool bTmpSet ) +{ + SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ]; + if ( pNode->IsContentNode() ) + { + static_cast<SwContentNode*>(pNode)->SetAttr( *m_pAttr ); + } + else if ( pNode->IsTableNode() ) + { + static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->SetFormatAttr( + *m_pAttr ); + } + else if ( pNode->IsStartNode() && (SwTableBoxStartNode == + static_cast<SwStartNode*>(pNode)->GetStartNodeType()) ) + { + SwTableNode* pTNd = pNode->FindTableNode(); + if ( pTNd ) + { + SwTableBox* pBox = pTNd->GetTable().GetTableBox( m_nNodeIndex ); + if (pBox) + { + pBox->ClaimFrameFormat()->SetFormatAttr( *m_pAttr ); + } + } + } + + if ( !bTmpSet ) + { + m_pAttr.reset(); + } +} + +SwHistorySetFormat::~SwHistorySetFormat() +{ +} + +SwHistoryResetFormat::SwHistoryResetFormat(const SfxPoolItem* pFormatHt, sal_uLong nNodeIdx) + : SwHistoryHint( HSTRY_RESETFMTHNT ) + , m_nNodeIndex( nNodeIdx ) + , m_nWhich( pFormatHt->Which() ) +{ +} + +void SwHistoryResetFormat::SetInDoc( SwDoc* pDoc, bool ) +{ + SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ]; + if ( pNode->IsContentNode() ) + { + static_cast<SwContentNode*>(pNode)->ResetAttr( m_nWhich ); + } + else if ( pNode->IsTableNode() ) + { + static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()-> + ResetFormatAttr( m_nWhich ); + } +} + +SwHistorySetText::SwHistorySetText( SwTextAttr* pTextHt, sal_uLong nNodePos ) + : SwHistoryHint( HSTRY_SETTXTHNT ) + , m_nNodeIndex( nNodePos ) + , m_nStart( pTextHt->GetStart() ) + , m_nEnd( pTextHt->GetAnyEnd() ) + , m_bFormatIgnoreStart(pTextHt->IsFormatIgnoreStart()) + , m_bFormatIgnoreEnd (pTextHt->IsFormatIgnoreEnd ()) +{ + // Caution: the following attributes generate no format attributes: + // - NoLineBreak, NoHyphen, Inserted, Deleted + // These cases must be handled separately !!! + + // a little bit complicated but works: first assign a copy of the + // default value and afterwards the values from text attribute + if ( RES_TXTATR_CHARFMT == pTextHt->Which() ) + { + m_pAttr.reset( new SwFormatCharFormat( pTextHt->GetCharFormat().GetCharFormat() ) ); + } + else + { + m_pAttr.reset( pTextHt->GetAttr().Clone() ); + } +} + +SwHistorySetText::~SwHistorySetText() +{ +} + +void SwHistorySetText::SetInDoc( SwDoc* pDoc, bool ) +{ + if (!m_pAttr) + return; + + if ( RES_TXTATR_CHARFMT == m_pAttr->Which() ) + { + // ask the Doc if the CharFormat still exists + if (!pDoc->GetCharFormats()->IsAlive(static_cast<SwFormatCharFormat&>(*m_pAttr).GetCharFormat())) + return; // do not set, format does not exist + } + + SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); + OSL_ENSURE( pTextNd, "SwHistorySetText::SetInDoc: not a TextNode" ); + + if ( pTextNd ) + { + SwTextAttr *const pAttr = pTextNd->InsertItem(*m_pAttr, m_nStart, m_nEnd, + SetAttrMode::NOTXTATRCHR | + SetAttrMode::NOHINTADJUST ); + // shouldn't be possible to hit any error/merging path from here + assert(pAttr); + if (m_bFormatIgnoreStart) + { + pAttr->SetFormatIgnoreStart(true); + } + if (m_bFormatIgnoreEnd) + { + pAttr->SetFormatIgnoreEnd(true); + } + } +} + +SwHistorySetTextField::SwHistorySetTextField( const SwTextField* pTextField, sal_uLong nNodePos ) + : SwHistoryHint( HSTRY_SETTXTFLDHNT ) + , m_pField( new SwFormatField( *pTextField->GetFormatField().GetField() ) ) +{ + // only copy if not Sys-FieldType + SwDoc* pDoc = pTextField->GetTextNode().GetDoc(); + + m_nFieldWhich = m_pField->GetField()->GetTyp()->Which(); + if (m_nFieldWhich == SwFieldIds::Database || + m_nFieldWhich == SwFieldIds::User || + m_nFieldWhich == SwFieldIds::SetExp || + m_nFieldWhich == SwFieldIds::Dde || + !pDoc->getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich )) + { + m_pFieldType = m_pField->GetField()->GetTyp()->Copy(); + m_pField->GetField()->ChgTyp( m_pFieldType.get() ); // change field type + } + m_nNodeIndex = nNodePos; + m_nPos = pTextField->GetStart(); +} + +OUString SwHistorySetTextField::GetDescription() const +{ + return m_pField->GetField()->GetDescription(); +} + +SwHistorySetTextField::~SwHistorySetTextField() +{ +} + +void SwHistorySetTextField::SetInDoc( SwDoc* pDoc, bool ) +{ + if (!m_pField) + return; + + SwFieldType* pNewFieldType = m_pFieldType.get(); + if ( !pNewFieldType ) + { + pNewFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich ); + } + else + { + // register type with the document + pNewFieldType = pDoc->getIDocumentFieldsAccess().InsertFieldType( *m_pFieldType ); + } + + m_pField->GetField()->ChgTyp( pNewFieldType ); // change field type + + SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); + OSL_ENSURE( pTextNd, "SwHistorySetTextField: no TextNode" ); + + if ( pTextNd ) + { + pTextNd->InsertItem( *m_pField, m_nPos, m_nPos, + SetAttrMode::NOTXTATRCHR ); + } +} + +SwHistorySetRefMark::SwHistorySetRefMark( const SwTextRefMark* pTextHt, sal_uLong nNodePos ) + : SwHistoryHint( HSTRY_SETREFMARKHNT ) + , m_RefName( pTextHt->GetRefMark().GetRefName() ) + , m_nNodeIndex( nNodePos ) + , m_nStart( pTextHt->GetStart() ) + , m_nEnd( pTextHt->GetAnyEnd() ) +{ +} + +void SwHistorySetRefMark::SetInDoc( SwDoc* pDoc, bool ) +{ + SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); + OSL_ENSURE( pTextNd, "SwHistorySetRefMark: no TextNode" ); + if ( !pTextNd ) + return; + + SwFormatRefMark aRefMark( m_RefName ); + + // if a reference mark without an end already exists here: must not insert! + if ( m_nStart != m_nEnd || + !pTextNd->GetTextAttrForCharAt( m_nStart, RES_TXTATR_REFMARK ) ) + { + pTextNd->InsertItem( aRefMark, m_nStart, m_nEnd, + SetAttrMode::NOTXTATRCHR ); + } +} + +SwHistorySetTOXMark::SwHistorySetTOXMark( const SwTextTOXMark* pTextHt, sal_uLong nNodePos ) + : SwHistoryHint( HSTRY_SETTOXMARKHNT ) + , m_TOXMark( pTextHt->GetTOXMark() ) + , m_TOXName( m_TOXMark.GetTOXType()->GetTypeName() ) + , m_eTOXTypes( m_TOXMark.GetTOXType()->GetType() ) + , m_nNodeIndex( nNodePos ) + , m_nStart( pTextHt->GetStart() ) + , m_nEnd( pTextHt->GetAnyEnd() ) +{ + m_TOXMark.EndListeningAll(); +} + +SwTOXType* SwHistorySetTOXMark::GetSwTOXType(SwDoc& rDoc, TOXTypes eTOXTypes, const OUString& rTOXName) +{ + // search for respective TOX type + const sal_uInt16 nCnt = rDoc.GetTOXTypeCount(eTOXTypes); + SwTOXType* pToxType = nullptr; + for ( sal_uInt16 n = 0; n < nCnt; ++n ) + { + pToxType = const_cast<SwTOXType*>(rDoc.GetTOXType(eTOXTypes, n)); + if (pToxType->GetTypeName() == rTOXName) + break; + pToxType = nullptr; + } + + if ( !pToxType ) // TOX type not found, create new + { + pToxType = const_cast<SwTOXType*>( + rDoc.InsertTOXType(SwTOXType(rDoc, eTOXTypes, rTOXName))); + } + + return pToxType; +} + +void SwHistorySetTOXMark::SetInDoc( SwDoc* pDoc, bool ) +{ + SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); + OSL_ENSURE( pTextNd, "SwHistorySetTOXMark: no TextNode" ); + if ( !pTextNd ) + return; + + SwTOXType* pToxType = GetSwTOXType(*pDoc, m_eTOXTypes, m_TOXName); + + SwTOXMark aNew( m_TOXMark ); + aNew.RegisterToTOXType( *pToxType ); + + pTextNd->InsertItem( aNew, m_nStart, m_nEnd, + SetAttrMode::NOTXTATRCHR ); +} + +bool SwHistorySetTOXMark::IsEqual( const SwTOXMark& rCmp ) const +{ + return m_TOXName == rCmp.GetTOXType()->GetTypeName() && + m_eTOXTypes == rCmp.GetTOXType()->GetType() && + m_TOXMark.GetAlternativeText() == rCmp.GetAlternativeText() && + ( (TOX_INDEX == m_eTOXTypes) + ? ( m_TOXMark.GetPrimaryKey() == rCmp.GetPrimaryKey() && + m_TOXMark.GetSecondaryKey() == rCmp.GetSecondaryKey() ) + : m_TOXMark.GetLevel() == rCmp.GetLevel() + ); +} + +SwHistoryResetText::SwHistoryResetText( sal_uInt16 nWhich, + sal_Int32 nAttrStart, sal_Int32 nAttrEnd, sal_uLong nNodePos ) + : SwHistoryHint( HSTRY_RESETTXTHNT ) + , m_nNodeIndex( nNodePos ), m_nStart( nAttrStart ), m_nEnd( nAttrEnd ) + , m_nAttr( nWhich ) +{ +} + +void SwHistoryResetText::SetInDoc( SwDoc* pDoc, bool ) +{ + SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); + OSL_ENSURE( pTextNd, "SwHistoryResetText: no TextNode" ); + if ( pTextNd ) + { + pTextNd->DeleteAttributes( m_nAttr, m_nStart, m_nEnd ); + } +} + +SwHistorySetFootnote::SwHistorySetFootnote( SwTextFootnote* pTextFootnote, sal_uLong nNodePos ) + : SwHistoryHint( HSTRY_SETFTNHNT ) + , m_pUndo( new SwUndoSaveSection ) + , m_FootnoteNumber( pTextFootnote->GetFootnote().GetNumStr() ) + , m_nNodeIndex( nNodePos ) + , m_nStart( pTextFootnote->GetStart() ) + , m_bEndNote( pTextFootnote->GetFootnote().IsEndNote() ) +{ + OSL_ENSURE( pTextFootnote->GetStartNode(), + "SwHistorySetFootnote: Footnote without Section" ); + + // keep the old NodePos (because who knows what later will be saved/deleted + // in SaveSection) + SwDoc* pDoc = const_cast<SwDoc*>(pTextFootnote->GetTextNode().GetDoc()); + SwNode* pSaveNd = pDoc->GetNodes()[ m_nNodeIndex ]; + + // keep pointer to StartNode of FootnoteSection and reset its attribute for now + // (as a result, its/all Frames will be deleted automatically) + SwNodeIndex aSttIdx( *pTextFootnote->GetStartNode() ); + pTextFootnote->SetStartNode( nullptr, false ); + + m_pUndo->SaveSection( aSttIdx ); + m_nNodeIndex = pSaveNd->GetIndex(); +} + +SwHistorySetFootnote::SwHistorySetFootnote( const SwTextFootnote &rTextFootnote ) + : SwHistoryHint( HSTRY_SETFTNHNT ) + , m_FootnoteNumber( rTextFootnote.GetFootnote().GetNumStr() ) + , m_nNodeIndex( SwTextFootnote_GetIndex( (&rTextFootnote) ) ) + , m_nStart( rTextFootnote.GetStart() ) + , m_bEndNote( rTextFootnote.GetFootnote().IsEndNote() ) +{ + OSL_ENSURE( rTextFootnote.GetStartNode(), + "SwHistorySetFootnote: Footnote without Section" ); +} + +OUString SwHistorySetFootnote::GetDescription() const +{ + return SwResId(STR_FOOTNOTE); +} + +SwHistorySetFootnote::~SwHistorySetFootnote() +{ +} + +void SwHistorySetFootnote::SetInDoc( SwDoc* pDoc, bool ) +{ + SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); + OSL_ENSURE( pTextNd, "SwHistorySetFootnote: no TextNode" ); + if ( !pTextNd ) + return; + + if (m_pUndo) + { + // set the footnote in the TextNode + SwFormatFootnote aTemp( m_bEndNote ); + SwFormatFootnote& rNew = const_cast<SwFormatFootnote&>( + pDoc->GetAttrPool().Put(aTemp) ); + if ( !m_FootnoteNumber.isEmpty() ) + { + rNew.SetNumStr( m_FootnoteNumber ); + } + SwTextFootnote* pTextFootnote = new SwTextFootnote( rNew, m_nStart ); + + // create the section of the Footnote + SwNodeIndex aIdx( *pTextNd ); + m_pUndo->RestoreSection( pDoc, &aIdx, SwFootnoteStartNode ); + pTextFootnote->SetStartNode( &aIdx ); + if ( m_pUndo->GetHistory() ) + { + // create frames only now + m_pUndo->GetHistory()->Rollback( pDoc ); + } + + pTextNd->InsertHint( pTextFootnote ); + } + else + { + SwTextFootnote * const pFootnote = + static_cast<SwTextFootnote*>( + pTextNd->GetTextAttrForCharAt( m_nStart )); + assert(pFootnote); + SwFormatFootnote &rFootnote = const_cast<SwFormatFootnote&>(pFootnote->GetFootnote()); + rFootnote.SetNumStr( m_FootnoteNumber ); + if ( rFootnote.IsEndNote() != m_bEndNote ) + { + rFootnote.SetEndNote( m_bEndNote ); + pFootnote->CheckCondColl(); + } + } +} + +SwHistoryChangeFormatColl::SwHistoryChangeFormatColl( SwFormatColl* pFormatColl, sal_uLong nNd, + SwNodeType nNodeWhich ) + : SwHistoryHint( HSTRY_CHGFMTCOLL ) + , m_pColl( pFormatColl ) + , m_nNodeIndex( nNd ) + , m_nNodeType( nNodeWhich ) +{ +} + +void SwHistoryChangeFormatColl::SetInDoc( SwDoc* pDoc, bool ) +{ + SwContentNode * pContentNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetContentNode(); + OSL_ENSURE( pContentNd, "SwHistoryChangeFormatColl: no ContentNode" ); + + // before setting the format, check if it is still available in the + // document. if it has been deleted, there is no undo! + if ( pContentNd && m_nNodeType == pContentNd->GetNodeType() ) + { + if ( SwNodeType::Text == m_nNodeType ) + { + if (pDoc->GetTextFormatColls()->IsAlive(static_cast<SwTextFormatColl *>(m_pColl))) + { + pContentNd->ChgFormatColl( m_pColl ); + } + } + else if (pDoc->GetGrfFormatColls()->IsAlive(static_cast<SwGrfFormatColl *>(m_pColl))) + { + pContentNd->ChgFormatColl( m_pColl ); + } + } +} + +SwHistoryTextFlyCnt::SwHistoryTextFlyCnt( SwFrameFormat* const pFlyFormat ) + : SwHistoryHint( HSTRY_FLYCNT ) + , m_pUndo( new SwUndoDelLayFormat( pFlyFormat ) ) +{ + OSL_ENSURE( pFlyFormat, "SwHistoryTextFlyCnt: no Format" ); + m_pUndo->ChgShowSel( false ); +} + +SwHistoryTextFlyCnt::~SwHistoryTextFlyCnt() +{ +} + +void SwHistoryTextFlyCnt::SetInDoc( SwDoc* pDoc, bool ) +{ + ::sw::IShellCursorSupplier *const pISCS(pDoc->GetIShellCursorSupplier()); + assert(pISCS); + ::sw::UndoRedoContext context(*pDoc, *pISCS); + m_pUndo->UndoImpl(context); +} + +SwHistoryBookmark::SwHistoryBookmark( + const ::sw::mark::IMark& rBkmk, + bool bSavePos, + bool bSaveOtherPos) + : SwHistoryHint(HSTRY_BOOKMARK) + , m_aName(rBkmk.GetName()) + , m_aShortName() + , m_aKeycode() + , m_nNode(bSavePos ? + rBkmk.GetMarkPos().nNode.GetIndex() : 0) + , m_nOtherNode(bSaveOtherPos ? + rBkmk.GetOtherMarkPos().nNode.GetIndex() : 0) + , m_nContent(bSavePos ? + rBkmk.GetMarkPos().nContent.GetIndex() : 0) + , m_nOtherContent(bSaveOtherPos ? + rBkmk.GetOtherMarkPos().nContent.GetIndex() :0) + , m_bSavePos(bSavePos) + , m_bSaveOtherPos(bSaveOtherPos) + , m_bHadOtherPos(rBkmk.IsExpanded()) + , m_eBkmkType(IDocumentMarkAccess::GetType(rBkmk)) +{ + const ::sw::mark::IBookmark* const pBookmark = dynamic_cast< const ::sw::mark::IBookmark* >(&rBkmk); + if(pBookmark) + { + m_aKeycode = pBookmark->GetKeyCode(); + m_aShortName = pBookmark->GetShortName(); + + ::sfx2::Metadatable const*const pMetadatable( + dynamic_cast< ::sfx2::Metadatable const* >(pBookmark)); + if (pMetadatable) + { + m_pMetadataUndo = pMetadatable->CreateUndo(); + } + } +} + +void SwHistoryBookmark::SetInDoc( SwDoc* pDoc, bool ) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + SwNodes& rNds = pDoc->GetNodes(); + IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); + std::unique_ptr<SwPaM> pPam; + ::sw::mark::IMark* pMark = nullptr; + + if(m_bSavePos) + { + SwContentNode* const pContentNd = rNds[m_nNode]->GetContentNode(); + OSL_ENSURE(pContentNd, + "<SwHistoryBookmark::SetInDoc(..)>" + " - wrong node for a mark"); + + // #111660# don't crash when nNode1 doesn't point to content node. + if(pContentNd) + pPam.reset(new SwPaM(*pContentNd, m_nContent)); + } + else + { + pMark = *pMarkAccess->findMark(m_aName); + pPam.reset(new SwPaM(pMark->GetMarkPos())); + } + + if(m_bSaveOtherPos) + { + SwContentNode* const pContentNd = rNds[m_nOtherNode]->GetContentNode(); + OSL_ENSURE(pContentNd, + "<SwHistoryBookmark::SetInDoc(..)>" + " - wrong node for a mark"); + + if (pPam != nullptr && pContentNd) + { + pPam->SetMark(); + pPam->GetMark()->nNode = m_nOtherNode; + pPam->GetMark()->nContent.Assign(pContentNd, m_nOtherContent); + } + } + else if(m_bHadOtherPos) + { + if(!pMark) + pMark = *pMarkAccess->findMark(m_aName); + OSL_ENSURE(pMark->IsExpanded(), + "<SwHistoryBookmark::SetInDoc(..)>" + " - missing pos on old mark"); + pPam->SetMark(); + *pPam->GetMark() = pMark->GetOtherMarkPos(); + } + + if (pPam) + { + if ( pMark != nullptr ) + { + pMarkAccess->deleteMark( pMark ); + } + ::sw::mark::IBookmark* const pBookmark = + dynamic_cast<::sw::mark::IBookmark*>( + pMarkAccess->makeMark(*pPam, m_aName, m_eBkmkType, sw::mark::InsertMode::New)); + if ( pBookmark != nullptr ) + { + pBookmark->SetKeyCode(m_aKeycode); + pBookmark->SetShortName(m_aShortName); + if (m_pMetadataUndo) + { + ::sfx2::Metadatable * const pMeta( + dynamic_cast< ::sfx2::Metadatable* >(pBookmark)); + OSL_ENSURE(pMeta, "metadata undo, but not metadatable?"); + if (pMeta) + { + pMeta->RestoreMetadata(m_pMetadataUndo); + } + } + } + } +} + +bool SwHistoryBookmark::IsEqualBookmark(const ::sw::mark::IMark& rBkmk) +{ + return m_nNode == rBkmk.GetMarkPos().nNode.GetIndex() + && m_nContent == rBkmk.GetMarkPos().nContent.GetIndex() + && m_aName == rBkmk.GetName(); +} + +SwHistoryNoTextFieldmark::SwHistoryNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark) + : SwHistoryHint(HSTRY_NOTEXTFIELDMARK) + , m_sType(rFieldMark.GetFieldname()) + , m_nNode(rFieldMark.GetMarkPos().nNode.GetIndex()) + , m_nContent(rFieldMark.GetMarkPos().nContent.GetIndex()) +{ +} + +void SwHistoryNoTextFieldmark::SetInDoc(SwDoc* pDoc, bool) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + SwNodes& rNds = pDoc->GetNodes(); + std::unique_ptr<SwPaM> pPam; + + const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode(); + if(pContentNd) + pPam.reset(new SwPaM(*pContentNd, m_nContent)); + + if (pPam) + { + IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); + pMarkAccess->makeNoTextFieldBookmark(*pPam, OUString(), m_sType); + } +} + +void SwHistoryNoTextFieldmark::ResetInDoc(SwDoc* pDoc) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + SwNodes& rNds = pDoc->GetNodes(); + std::unique_ptr<SwPaM> pPam; + + const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode(); + if(pContentNd) + pPam.reset(new SwPaM(*pContentNd, m_nContent-1)); + + if (pPam) + { + IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); + pMarkAccess->deleteFieldmarkAt(*pPam->GetPoint()); + } +} + +SwHistoryTextFieldmark::SwHistoryTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark) + : SwHistoryHint(HSTRY_TEXTFIELDMARK) + , m_sName(rFieldMark.GetName()) + , m_sType(rFieldMark.GetFieldname()) + , m_nStartNode(rFieldMark.GetMarkStart().nNode.GetIndex()) + , m_nStartContent(rFieldMark.GetMarkStart().nContent.GetIndex()) + , m_nEndNode(rFieldMark.GetMarkEnd().nNode.GetIndex()) + , m_nEndContent(rFieldMark.GetMarkEnd().nContent.GetIndex()) +{ + SwPosition const sepPos(sw::mark::FindFieldSep(rFieldMark)); + m_nSepNode = sepPos.nNode.GetIndex(); + m_nSepContent = sepPos.nContent.GetIndex(); +} + +void SwHistoryTextFieldmark::SetInDoc(SwDoc* pDoc, bool) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + SwNodes& rNds = pDoc->GetNodes(); + + assert(rNds[m_nStartNode]->IsContentNode()); + assert(rNds[m_nEndNode]->IsContentNode()); + assert(rNds[m_nSepNode]->IsContentNode()); + + SwPaM const pam(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent, + *rNds[m_nEndNode]->GetContentNode(), + // subtract 1 for the CH_TXT_ATR_FIELDEND itself, + // plus more if same node as other CH_TXT_ATR + m_nStartNode == m_nEndNode + ? (m_nEndContent - 3) + : m_nSepNode == m_nEndNode + ? (m_nEndContent - 2) + : (m_nEndContent - 1)); + SwPosition const sepPos(*rNds[m_nSepNode]->GetContentNode(), + m_nStartNode == m_nSepNode ? (m_nSepContent - 1) : m_nSepContent); + + IDocumentMarkAccess & rMarksAccess(*pDoc->getIDocumentMarkAccess()); + rMarksAccess.makeFieldBookmark(pam, m_sName, m_sType, &sepPos); +} + +void SwHistoryTextFieldmark::ResetInDoc(SwDoc* pDoc) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + SwNodes& rNds = pDoc->GetNodes(); + + assert(rNds[m_nStartNode]->IsContentNode()); + assert(rNds[m_nEndNode]->IsContentNode()); + assert(rNds[m_nSepNode]->IsContentNode()); + + SwPosition const pos(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent); + + IDocumentMarkAccess & rMarksAccess(*pDoc->getIDocumentMarkAccess()); + rMarksAccess.deleteFieldmarkAt(pos); +} + +SwHistorySetAttrSet::SwHistorySetAttrSet( const SfxItemSet& rSet, + sal_uLong nNodePos, const std::set<sal_uInt16> &rSetArr ) + : SwHistoryHint( HSTRY_SETATTRSET ) + , m_OldSet( rSet ) + , m_ResetArray( 0, 4 ) + , m_nNodeIndex( nNodePos ) +{ + SfxItemIter aIter( m_OldSet ), aOrigIter( rSet ); + const SfxPoolItem* pItem = aIter.GetCurItem(), + * pOrigItem = aOrigIter.GetCurItem(); + while (pItem && pOrigItem) + { + if( !rSetArr.count( pOrigItem->Which() )) + { + m_ResetArray.push_back( pOrigItem->Which() ); + m_OldSet.ClearItem( pOrigItem->Which() ); + } + else + { + switch ( pItem->Which() ) + { + case RES_PAGEDESC: + static_cast<SwFormatPageDesc*>( + const_cast<SfxPoolItem*>(pItem))->ChgDefinedIn( nullptr ); + break; + + case RES_PARATR_DROP: + static_cast<SwFormatDrop*>( + const_cast<SfxPoolItem*>(pItem))->ChgDefinedIn( nullptr ); + break; + + case RES_BOXATR_FORMULA: + { + // When a formula is set, never save the value. It + // possibly must be recalculated! + // Save formulas always in plain text + m_OldSet.ClearItem( RES_BOXATR_VALUE ); + + SwTableBoxFormula& rNew = + *static_cast<SwTableBoxFormula*>( + const_cast<SfxPoolItem*>(pItem)); + if ( rNew.IsIntrnlName() ) + { + const SwTableBoxFormula& rOld = + rSet.Get( RES_BOXATR_FORMULA ); + const SwNode* pNd = rOld.GetNodeOfFormula(); + if ( pNd ) + { + const SwTableNode* pTableNode + = pNd->FindTableNode(); + if (pTableNode) + { + SwTableFormulaUpdate aMsgHint( + &pTableNode->GetTable() ); + aMsgHint.m_eFlags = TBL_BOXNAME; + rNew.ChgDefinedIn( rOld.GetDefinedIn() ); + rNew.ChangeState( &aMsgHint ); + } + } + } + rNew.ChgDefinedIn( nullptr ); + } + break; + } + } + + pItem = aIter.NextItem(); + pOrigItem = aOrigIter.NextItem(); + } +} + +void SwHistorySetAttrSet::SetInDoc( SwDoc* pDoc, bool ) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ]; + if ( pNode->IsContentNode() ) + { + static_cast<SwContentNode*>(pNode)->SetAttr( m_OldSet ); + if ( !m_ResetArray.empty() ) + { + static_cast<SwContentNode*>(pNode)->ResetAttr( m_ResetArray ); + } + } + else if ( pNode->IsTableNode() ) + { + SwFormat& rFormat = + *static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat(); + rFormat.SetFormatAttr( m_OldSet ); + if ( !m_ResetArray.empty() ) + { + rFormat.ResetFormatAttr( m_ResetArray.front() ); + } + } +} + +SwHistoryChangeFlyAnchor::SwHistoryChangeFlyAnchor( SwFrameFormat& rFormat ) + : SwHistoryHint( HSTRY_CHGFLYANCHOR ) + , m_rFormat( rFormat ) + , m_nOldNodeIndex( rFormat.GetAnchor().GetContentAnchor()->nNode.GetIndex() ) + , m_nOldContentIndex( (RndStdIds::FLY_AT_CHAR == rFormat.GetAnchor().GetAnchorId()) + ? rFormat.GetAnchor().GetContentAnchor()->nContent.GetIndex() + : COMPLETE_STRING ) +{ +} + +void SwHistoryChangeFlyAnchor::SetInDoc( SwDoc* pDoc, bool ) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + if (pDoc->GetSpzFrameFormats()->IsAlive(&m_rFormat)) // Format does still exist + { + SwFormatAnchor aTmp( m_rFormat.GetAnchor() ); + + SwNode* pNd = pDoc->GetNodes()[ m_nOldNodeIndex ]; + SwContentNode* pCNd = pNd->GetContentNode(); + SwPosition aPos( *pNd ); + if ( COMPLETE_STRING != m_nOldContentIndex ) + { + OSL_ENSURE(pCNd, "SwHistoryChangeFlyAnchor: no ContentNode"); + if (pCNd) + { + aPos.nContent.Assign( pCNd, m_nOldContentIndex ); + } + } + aTmp.SetAnchor( &aPos ); + + // so the Layout does not get confused + if (!pCNd || !pCNd->getLayoutFrame(pDoc->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)) + { + m_rFormat.DelFrames(); + } + + m_rFormat.SetFormatAttr( aTmp ); + } +} + +SwHistoryChangeFlyChain::SwHistoryChangeFlyChain( SwFlyFrameFormat& rFormat, + const SwFormatChain& rAttr ) + : SwHistoryHint( HSTRY_CHGFLYCHAIN ) + , m_pPrevFormat( rAttr.GetPrev() ) + , m_pNextFormat( rAttr.GetNext() ) + , m_pFlyFormat( &rFormat ) +{ +} + +void SwHistoryChangeFlyChain::SetInDoc( SwDoc* pDoc, bool ) +{ + if (pDoc->GetSpzFrameFormats()->IsAlive(m_pFlyFormat)) + { + SwFormatChain aChain; + + if (m_pPrevFormat && + pDoc->GetSpzFrameFormats()->IsAlive(m_pPrevFormat)) + { + aChain.SetPrev( m_pPrevFormat ); + SwFormatChain aTmp( m_pPrevFormat->GetChain() ); + aTmp.SetNext( m_pFlyFormat ); + m_pPrevFormat->SetFormatAttr( aTmp ); + } + + if (m_pNextFormat && + pDoc->GetSpzFrameFormats()->IsAlive(m_pNextFormat)) + { + aChain.SetNext( m_pNextFormat ); + SwFormatChain aTmp( m_pNextFormat->GetChain() ); + aTmp.SetPrev( m_pFlyFormat ); + m_pNextFormat->SetFormatAttr( aTmp ); + } + + if ( aChain.GetNext() || aChain.GetPrev() ) + { + m_pFlyFormat->SetFormatAttr( aChain ); + } + } +} + +// -> #i27615# +SwHistoryChangeCharFormat::SwHistoryChangeCharFormat(const SfxItemSet & rSet, + const OUString & sFormat) + : SwHistoryHint(HSTRY_CHGCHARFMT) + , m_OldSet(rSet), m_Format(sFormat) +{ +} + +void SwHistoryChangeCharFormat::SetInDoc(SwDoc * pDoc, bool ) +{ + SwCharFormat * pCharFormat = pDoc->FindCharFormatByName(m_Format); + + if (pCharFormat) + { + pCharFormat->SetFormatAttr(m_OldSet); + } +} +// <- #i27615# + +SwHistory::SwHistory() + : m_SwpHstry() + , m_nEndDiff( 0 ) +{ +} + +SwHistory::~SwHistory() +{ +} + +void SwHistory::Add( + const SfxPoolItem* pOldValue, + const SfxPoolItem* pNewValue, + sal_uLong nNodeIdx) +{ + OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" ); + const sal_uInt16 nWhich(pNewValue->Which()); + + // excluded values + if(nWhich == RES_TXTATR_FIELD || nWhich == RES_TXTATR_ANNOTATION) + { + return; + } + + // no default Attribute? + std::unique_ptr<SwHistoryHint> pHt; + + // To be able to include the DrawingLayer FillItems something more + // general has to be done to check if an Item is default than to check + // if its pointer equals that in Writer's global PoolDefaults (held in + // aAttrTab and used to fill the pool defaults in Writer - looks as if + // Writer is *older* than the SfxItemPool ?). I checked the possibility to + // get the SfxItemPool here (works), but decided to use the SfxPoolItem's + // global tooling aka IsDefaultItem(const SfxPoolItem*) for now + if(pOldValue && !IsDefaultItem(pOldValue)) + { + pHt.reset( new SwHistorySetFormat( pOldValue, nNodeIdx ) ); + } + else + { + pHt.reset( new SwHistoryResetFormat( pNewValue, nNodeIdx ) ); + } + + m_SwpHstry.push_back( std::move(pHt) ); +} + +// FIXME: refactor the following "Add" methods (DRY)? +void SwHistory::Add( SwTextAttr* pHint, sal_uLong nNodeIdx, bool bNewAttr ) +{ + OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" ); + + std::unique_ptr<SwHistoryHint> pHt; + if( !bNewAttr ) + { + switch ( pHint->Which() ) + { + case RES_TXTATR_FTN: + pHt.reset( new SwHistorySetFootnote( + static_cast<SwTextFootnote*>(pHint), nNodeIdx ) ); + break; + case RES_TXTATR_FLYCNT: + pHt.reset( new SwHistoryTextFlyCnt( static_cast<SwTextFlyCnt*>(pHint) + ->GetFlyCnt().GetFrameFormat() ) ); + break; + case RES_TXTATR_FIELD: + case RES_TXTATR_ANNOTATION: + pHt.reset( new SwHistorySetTextField( + static_txtattr_cast<SwTextField*>(pHint), nNodeIdx) ); + break; + case RES_TXTATR_TOXMARK: + pHt.reset( new SwHistorySetTOXMark( + static_txtattr_cast<SwTextTOXMark*>(pHint), nNodeIdx) ); + break; + case RES_TXTATR_REFMARK: + pHt.reset( new SwHistorySetRefMark( + static_txtattr_cast<SwTextRefMark*>(pHint), nNodeIdx) ); + break; + default: + pHt.reset( new SwHistorySetText( pHint, nNodeIdx ) ); + } + } + else + { + pHt.reset( new SwHistoryResetText( pHint->Which(), pHint->GetStart(), + pHint->GetAnyEnd(), nNodeIdx ) ); + } + m_SwpHstry.push_back( std::move(pHt) ); +} + +void SwHistory::Add( SwFormatColl* pColl, sal_uLong nNodeIdx, SwNodeType nWhichNd ) +{ + OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" ); + + std::unique_ptr<SwHistoryHint> pHt( + new SwHistoryChangeFormatColl( pColl, nNodeIdx, nWhichNd )); + m_SwpHstry.push_back( std::move(pHt) ); +} + +void SwHistory::Add(const ::sw::mark::IMark& rBkmk, bool bSavePos, bool bSaveOtherPos) +{ + OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" ); + + std::unique_ptr<SwHistoryHint> pHt; + + switch (IDocumentMarkAccess::GetType(rBkmk)) + { + case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK: + case IDocumentMarkAccess::MarkType::DATE_FIELDMARK: + assert(bSavePos && bSaveOtherPos); // must be deleted completely! + pHt.reset(new SwHistoryTextFieldmark(dynamic_cast<sw::mark::IFieldmark const&>(rBkmk))); + break; + case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK: + case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK: + assert(bSavePos && bSaveOtherPos); // must be deleted completely! + pHt.reset(new SwHistoryNoTextFieldmark(dynamic_cast<sw::mark::IFieldmark const&>(rBkmk))); + break; + default: + pHt.reset(new SwHistoryBookmark(rBkmk, bSavePos, bSaveOtherPos)); + break; + } + + assert(pHt); + m_SwpHstry.push_back( std::move(pHt) ); +} + +void SwHistory::AddChangeFlyAnchor(SwFrameFormat& rFormat) +{ + std::unique_ptr<SwHistoryHint> pHt(new SwHistoryChangeFlyAnchor( rFormat )); + m_SwpHstry.push_back( std::move(pHt) ); +} + +void SwHistory::AddDeleteFly(SwFrameFormat& rFormat, sal_uInt16& rSetPos) +{ + OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" ); + + const sal_uInt16 nWh = rFormat.Which(); + (void) nWh; + // only Flys! + assert((RES_FLYFRMFMT == nWh && dynamic_cast<SwFlyFrameFormat*>(&rFormat)) + || (RES_DRAWFRMFMT == nWh && dynamic_cast<SwDrawFrameFormat*>(&rFormat))); + { + std::unique_ptr<SwHistoryHint> pHint(new SwHistoryTextFlyCnt( &rFormat )); + m_SwpHstry.push_back( std::move(pHint) ); + + const SwFormatChain* pChainItem; + if( SfxItemState::SET == rFormat.GetItemState( RES_CHAIN, false, + reinterpret_cast<const SfxPoolItem**>(&pChainItem) )) + { + assert(RES_FLYFRMFMT == nWh); // not supported on SdrObjects + if( pChainItem->GetNext() || pChainItem->GetPrev() ) + { + std::unique_ptr<SwHistoryHint> pHt( + new SwHistoryChangeFlyChain(static_cast<SwFlyFrameFormat&>(rFormat), *pChainItem)); + m_SwpHstry.insert( m_SwpHstry.begin() + rSetPos++, std::move(pHt) ); + if ( pChainItem->GetNext() ) + { + SwFormatChain aTmp( pChainItem->GetNext()->GetChain() ); + aTmp.SetPrev( nullptr ); + pChainItem->GetNext()->SetFormatAttr( aTmp ); + } + if ( pChainItem->GetPrev() ) + { + SwFormatChain aTmp( pChainItem->GetPrev()->GetChain() ); + aTmp.SetNext( nullptr ); + pChainItem->GetPrev()->SetFormatAttr( aTmp ); + } + } + rFormat.ResetFormatAttr( RES_CHAIN ); + } + } +} + +void SwHistory::Add( const SwTextFootnote& rFootnote ) +{ + std::unique_ptr<SwHistoryHint> pHt(new SwHistorySetFootnote( rFootnote )); + m_SwpHstry.push_back( std::move(pHt) ); +} + +// #i27615# +void SwHistory::Add(const SfxItemSet & rSet, const SwCharFormat & rFormat) +{ + std::unique_ptr<SwHistoryHint> pHt(new SwHistoryChangeCharFormat(rSet, rFormat.GetName())); + m_SwpHstry.push_back( std::move(pHt) ); +} + +bool SwHistory::Rollback( SwDoc* pDoc, sal_uInt16 nStart ) +{ + if ( !Count() ) + return false; + + for ( sal_uInt16 i = Count(); i > nStart ; ) + { + SwHistoryHint * pHHt = m_SwpHstry[ --i ].get(); + pHHt->SetInDoc( pDoc, false ); + } + m_SwpHstry.erase( m_SwpHstry.begin() + nStart, m_SwpHstry.end() ); + m_nEndDiff = 0; + return true; +} + +bool SwHistory::TmpRollback( SwDoc* pDoc, sal_uInt16 nStart, bool bToFirst ) +{ + sal_uInt16 nEnd = Count() - m_nEndDiff; + if ( !Count() || !nEnd || nStart >= nEnd ) + return false; + + if ( bToFirst ) + { + for ( ; nEnd > nStart; ++m_nEndDiff ) + { + SwHistoryHint* pHHt = m_SwpHstry[ --nEnd ].get(); + pHHt->SetInDoc( pDoc, true ); + } + } + else + { + for ( ; nStart < nEnd; ++m_nEndDiff, ++nStart ) + { + SwHistoryHint* pHHt = m_SwpHstry[ nStart ].get(); + pHHt->SetInDoc( pDoc, true ); + } + } + return true; +} + +sal_uInt16 SwHistory::SetTmpEnd( sal_uInt16 nNewTmpEnd ) +{ + OSL_ENSURE( nNewTmpEnd <= Count(), "SwHistory::SetTmpEnd: out of bounds" ); + + const sal_uInt16 nOld = Count() - m_nEndDiff; + m_nEndDiff = Count() - nNewTmpEnd; + + // for every SwHistoryFlyCnt, call the Redo of its UndoObject. + // this saves the formats of the flys! + for ( sal_uInt16 n = nOld; n < nNewTmpEnd; n++ ) + { + if ( HSTRY_FLYCNT == (*this)[ n ]->Which() ) + { + static_cast<SwHistoryTextFlyCnt*>((*this)[ n ]) + ->GetUDelLFormat()->RedoForRollback(); + } + } + + return nOld; +} + +void SwHistory::CopyFormatAttr( + const SfxItemSet& rSet, + sal_uLong nNodeIdx) +{ + if(rSet.Count()) + { + SfxItemIter aIter(rSet); + const SfxPoolItem* pItem = aIter.GetCurItem(); + do + { + if(!IsInvalidItem(pItem)) + { + Add(pItem, pItem, nNodeIdx); + } + + pItem = aIter.NextItem(); + + } while(pItem); + } +} + +void SwHistory::CopyAttr( + SwpHints const * pHts, + const sal_uLong nNodeIdx, + const sal_Int32 nStart, + const sal_Int32 nEnd, + const bool bCopyFields ) +{ + if( !pHts ) + return; + + // copy all attributes of the TextNode in the area from nStart to nEnd + SwTextAttr* pHt; + for( size_t n = 0; n < pHts->Count(); ++n ) + { + // nAttrStt must even be set when !pEndIdx + pHt = pHts->Get(n); + const sal_Int32 nAttrStt = pHt->GetStart(); + const sal_Int32 * pEndIdx = pHt->GetEnd(); + if( nullptr != pEndIdx && nAttrStt > nEnd ) + break; + + // never copy Flys and Footnote !! + bool bNextAttr = false; + switch( pHt->Which() ) + { + case RES_TXTATR_FIELD: + case RES_TXTATR_ANNOTATION: + case RES_TXTATR_INPUTFIELD: + if( !bCopyFields ) + bNextAttr = true; + break; + case RES_TXTATR_FLYCNT: + case RES_TXTATR_FTN: + bNextAttr = true; + break; + } + + if( bNextAttr ) + continue; + + // save all attributes that are somehow in this area + if ( nStart <= nAttrStt ) + { + if ( nEnd > nAttrStt ) + { + Add( pHt, nNodeIdx, false ); + } + } + else if ( pEndIdx && nStart < *pEndIdx ) + { + Add( pHt, nNodeIdx, false ); + } + } +} + +// Class to register the history at a Node, Format, HintsArray, ... +SwRegHistory::SwRegHistory( SwHistory* pHst ) + : SwClient( nullptr ) + , m_pHistory( pHst ) + , m_nNodeIndex( ULONG_MAX ) +{ + MakeSetWhichIds(); +} + +SwRegHistory::SwRegHistory( SwModify* pRegIn, const SwNode& rNd, + SwHistory* pHst ) + : SwClient( pRegIn ) + , m_pHistory( pHst ) + , m_nNodeIndex( rNd.GetIndex() ) +{ + MakeSetWhichIds(); +} + +SwRegHistory::SwRegHistory( const SwNode& rNd, SwHistory* pHst ) + : SwClient( nullptr ) + , m_pHistory( pHst ) + , m_nNodeIndex( rNd.GetIndex() ) +{ + MakeSetWhichIds(); +} + +void SwRegHistory::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if ( m_pHistory && pNew && pOld != pNew ) + { + if ( pNew->Which() < POOLATTR_END ) + { + if(RES_UPDATE_ATTR == pNew->Which()) + { + // const SfxItemPool& rPool = static_cast< const SwUpdateAttr* >(pNew)->GetSfxItemPool(); + + m_pHistory->Add( + // rPool, + pOld, + pNew, + m_nNodeIndex); + } + else + { + OSL_ENSURE(false, "Unexpected update attribute (!)"); + } + } + else if (pOld && RES_ATTRSET_CHG == pNew->Which()) + { + std::unique_ptr<SwHistoryHint> pNewHstr; + const SfxItemSet& rSet = *static_cast< const SwAttrSetChg* >(pOld)->GetChgSet(); + + if ( 1 < rSet.Count() ) + { + pNewHstr.reset( new SwHistorySetAttrSet( rSet, m_nNodeIndex, m_WhichIdSet ) ); + } + else if (const SfxPoolItem* pItem = SfxItemIter(rSet).GetCurItem()) + { + if ( m_WhichIdSet.count( pItem->Which() ) ) + { + pNewHstr.reset( new SwHistorySetFormat( pItem, m_nNodeIndex ) ); + } + else + { + pNewHstr.reset( new SwHistoryResetFormat( pItem, m_nNodeIndex ) ); + } + } + + if (pNewHstr) + m_pHistory->m_SwpHstry.push_back( std::move(pNewHstr) ); + } + } +} + +void SwRegHistory::AddHint( SwTextAttr* pHt, const bool bNew ) +{ + m_pHistory->Add( pHt, m_nNodeIndex, bNew ); +} + +bool SwRegHistory::InsertItems( const SfxItemSet& rSet, + sal_Int32 const nStart, sal_Int32 const nEnd, SetAttrMode const nFlags, + SwTextAttr **ppNewTextAttr ) +{ + if( !rSet.Count() ) + return false; + + SwTextNode * const pTextNode = + dynamic_cast<SwTextNode *>(GetRegisteredIn()); + + OSL_ENSURE(pTextNode, "SwRegHistory not registered at text node?"); + if (!pTextNode) + return false; + + if (m_pHistory) + { + pTextNode->GetOrCreateSwpHints().Register(this); + } + + const bool bInserted = pTextNode->SetAttr( rSet, nStart, nEnd, nFlags, ppNewTextAttr ); + + // Caution: The array can be deleted when inserting an attribute! + // This can happen when the value that should be added first deletes + // an existing attribute but does not need to be added itself because + // the paragraph attributes are identical + // ( -> bForgetAttr in SwpHints::Insert ) + if ( pTextNode->GetpSwpHints() && m_pHistory ) + { + pTextNode->GetpSwpHints()->DeRegister(); + } + +#ifndef NDEBUG + if ( m_pHistory && bInserted ) + { + SfxItemIter aIter(rSet); + for (SfxPoolItem const* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { // check that the history recorded a hint to reset every item + sal_uInt16 const nWhich(pItem->Which()); + sal_uInt16 const nExpected( + (isCHRATR(nWhich) || RES_TXTATR_UNKNOWN_CONTAINER == nWhich) + ? RES_TXTATR_AUTOFMT + : nWhich); + if (RES_TXTATR_AUTOFMT == nExpected) + continue; // special case, may get set on text node itself + // tdf#105077 even worse, node's set could cause + // nothing at all to be inserted + assert(std::any_of( + m_pHistory->m_SwpHstry.begin(), m_pHistory->m_SwpHstry.end(), + [nExpected](std::unique_ptr<SwHistoryHint> const& pHint) -> bool { + SwHistoryResetText const*const pReset( + dynamic_cast<SwHistoryResetText const*>(pHint.get())); + return pReset && (pReset->GetWhich() == nExpected); + })); + } + } +#endif + + return bInserted; +} + +void SwRegHistory::RegisterInModify( SwModify* pRegIn, const SwNode& rNd ) +{ + if ( m_pHistory && pRegIn ) + { + pRegIn->Add( this ); + m_nNodeIndex = rNd.GetIndex(); + MakeSetWhichIds(); + } + else + { + m_WhichIdSet.clear(); + } +} + +void SwRegHistory::MakeSetWhichIds() +{ + if (!m_pHistory) return; + + m_WhichIdSet.clear(); + + if( GetRegisteredIn() ) + { + const SfxItemSet* pSet = nullptr; + if( dynamic_cast< const SwContentNode *>( GetRegisteredIn() ) != nullptr ) + { + pSet = static_cast<SwContentNode*>( + GetRegisteredIn())->GetpSwAttrSet(); + } + else if ( dynamic_cast< const SwFormat *>( GetRegisteredIn() ) != nullptr ) + { + pSet = &static_cast<SwFormat*>( + GetRegisteredIn())->GetAttrSet(); + } + if( pSet && pSet->Count() ) + { + SfxItemIter aIter( *pSet ); + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + sal_uInt16 nW = pItem->Which(); + m_WhichIdSet.insert( nW ); + } + } + } +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unattr.cxx b/sw/source/core/undo/unattr.cxx new file mode 100644 index 000000000..bccbaba80 --- /dev/null +++ b/sw/source/core/undo/unattr.cxx @@ -0,0 +1,1055 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <utility> + +#include <UndoAttribute.hxx> +#include <svl/itemiter.hxx> +#include <editeng/tstpitem.hxx> +#include <svx/svdobj.hxx> +#include <hintids.hxx> +#include <fmtflcnt.hxx> +#include <txtftn.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <frmfmt.hxx> +#include <fmtcntnt.hxx> +#include <ftnidx.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IShellCursorSupplier.hxx> +#include <docary.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <swtable.hxx> +#include <swtblfmt.hxx> +#include <UndoCore.hxx> +#include <hints.hxx> +#include <rolbck.hxx> +#include <ndnotxt.hxx> +#include <ftninfo.hxx> +#include <redline.hxx> +#include <section.hxx> +#include <charfmt.hxx> +#include <calbck.hxx> +#include <frameformats.hxx> + +SwUndoFormatAttrHelper::SwUndoFormatAttrHelper( SwFormat& rFormat, bool bSvDrwPt ) + : SwClient( &rFormat ) + , m_bSaveDrawPt( bSvDrwPt ) +{ +} + +void SwUndoFormatAttrHelper::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if( pOld ) { + if ( pOld->Which() == RES_OBJECTDYING ) { + CheckRegistration( pOld ); + } else if ( pNew ) { + const SwDoc& rDoc = *static_cast<SwFormat*>(GetRegisteredInNonConst())->GetDoc(); + if( POOLATTR_END >= pOld->Which() ) { + if ( GetUndo() ) { + m_pUndo->PutAttr( *pOld, rDoc ); + } else { + m_pUndo.reset( new SwUndoFormatAttr( *pOld, + *static_cast<SwFormat*>(GetRegisteredInNonConst()), m_bSaveDrawPt ) ); + } + } else if ( RES_ATTRSET_CHG == pOld->Which() ) { + if ( GetUndo() ) { + SfxItemIter aIter( + *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() ); + for (auto pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + m_pUndo->PutAttr( *pItem, rDoc ); + } + } else { + m_pUndo.reset( new SwUndoFormatAttr( + *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet(), + *static_cast<SwFormat*>(GetRegisteredInNonConst()), m_bSaveDrawPt ) ); + } + } + } + } +} + +SwUndoFormatAttr::SwUndoFormatAttr( const SfxItemSet& rOldSet, + SwFormat& rChgFormat, + bool bSaveDrawPt ) + : SwUndo( SwUndoId::INSFMTATTR, rChgFormat.GetDoc() ) + , m_sFormatName ( rChgFormat.GetName() ) + // #i56253# + , m_pOldSet( new SfxItemSet( rOldSet ) ) + , m_nNodeIndex( 0 ) + , m_nFormatWhich( rChgFormat.Which() ) + , m_bSaveDrawPt( bSaveDrawPt ) +{ + Init( rChgFormat ); +} + +SwUndoFormatAttr::SwUndoFormatAttr( const SfxPoolItem& rItem, SwFormat& rChgFormat, + bool bSaveDrawPt ) + : SwUndo( SwUndoId::INSFMTATTR, rChgFormat.GetDoc() ) + , m_sFormatName(rChgFormat.GetName()) + , m_pOldSet( rChgFormat.GetAttrSet().Clone( false ) ) + , m_nNodeIndex( 0 ) + , m_nFormatWhich( rChgFormat.Which() ) + , m_bSaveDrawPt( bSaveDrawPt ) +{ + m_pOldSet->Put( rItem ); + Init( rChgFormat ); +} + +void SwUndoFormatAttr::Init( const SwFormat & rFormat ) +{ + // tdf#126017 never save SwNodeIndex, it will go stale + m_pOldSet->ClearItem(RES_CNTNT); + // treat change of anchor specially + if ( SfxItemState::SET == m_pOldSet->GetItemState( RES_ANCHOR, false )) { + SaveFlyAnchor( &rFormat, m_bSaveDrawPt ); + } else if ( RES_FRMFMT == m_nFormatWhich ) { + const SwDoc* pDoc = rFormat.GetDoc(); + if (pDoc->GetTableFrameFormats()->ContainsFormat(dynamic_cast<const SwFrameFormat&>(rFormat))) + { + // Table Format: save table position, table formats are volatile! + SwTable * pTable = SwIterator<SwTable,SwFormat>( rFormat ).First(); + if ( pTable ) { + m_nNodeIndex = pTable->GetTabSortBoxes()[ 0 ]->GetSttNd() + ->FindTableNode()->GetIndex(); + } + } else if (pDoc->GetSections().ContainsFormat(&rFormat)) { + m_nNodeIndex = rFormat.GetContent().GetContentIdx()->GetIndex(); + } else if ( dynamic_cast< const SwTableBoxFormat* >( &rFormat ) != nullptr ) { + SwTableBox * pTableBox = SwIterator<SwTableBox,SwFormat>( rFormat ).First(); + if ( pTableBox ) { + m_nNodeIndex = pTableBox->GetSttIdx(); + } + } + } +} + +SwUndoFormatAttr::~SwUndoFormatAttr() +{ +} + +void SwUndoFormatAttr::UndoImpl(::sw::UndoRedoContext & rContext) +{ + // OD 2004-10-26 #i35443# + // Important note: <Undo(..)> also called by <ReDo(..)> + + if (!m_pOldSet) + return; + + SwFormat * pFormat = GetFormat(rContext.GetDoc()); + if (!pFormat) + return; + + // #i35443# - If anchor attribute has been successful + // restored, all other attributes are also restored. + // Thus, keep track of its restoration + bool bAnchorAttrRestored( false ); + if ( SfxItemState::SET == m_pOldSet->GetItemState( RES_ANCHOR, false )) { + bAnchorAttrRestored = RestoreFlyAnchor(rContext); + if ( bAnchorAttrRestored ) { + // Anchor attribute successful restored. + // Thus, keep anchor position for redo + SaveFlyAnchor(pFormat); + } else { + // Anchor attribute not restored due to invalid anchor position. + // Thus, delete anchor attribute. + m_pOldSet->ClearItem( RES_ANCHOR ); + } + } + + if ( !bAnchorAttrRestored ) { + SwUndoFormatAttrHelper aTmp( *pFormat, m_bSaveDrawPt ); + pFormat->SetFormatAttr( *m_pOldSet ); + if ( aTmp.GetUndo() ) { + // transfer ownership of helper object's old set + m_pOldSet = std::move(aTmp.GetUndo()->m_pOldSet); + } else { + m_pOldSet->ClearItem(); + } + + if ( RES_FLYFRMFMT == m_nFormatWhich || RES_DRAWFRMFMT == m_nFormatWhich ) { + rContext.SetSelections(static_cast<SwFrameFormat*>(pFormat), nullptr); + } + } +} + +// Check if it is still in Doc +SwFormat* SwUndoFormatAttr::GetFormat( const SwDoc& rDoc ) +{ + switch (m_nFormatWhich) + { + case RES_TXTFMTCOLL: + case RES_CONDTXTFMTCOLL: + return rDoc.FindTextFormatCollByName(m_sFormatName); + + case RES_GRFFMTCOLL: + return SwDoc::FindFormatByName(*rDoc.GetGrfFormatColls(), m_sFormatName); + + case RES_CHRFMT: + return rDoc.FindCharFormatByName(m_sFormatName); + + case RES_FRMFMT: + if (m_nNodeIndex && (m_nNodeIndex < rDoc.GetNodes().Count())) + { + SwNode* pNd = rDoc.GetNodes()[m_nNodeIndex]; + if (pNd->IsTableNode()) + { + return static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat(); + } + else if (pNd->IsSectionNode()) + { + return static_cast<SwSectionNode*>(pNd)->GetSection().GetFormat(); + } + else if (pNd->IsStartNode() && (SwTableBoxStartNode == + static_cast<SwStartNode*>(pNd)->GetStartNodeType())) + { + SwTableNode* pTableNode = pNd->FindTableNode(); + if (pTableNode) + { + SwTableBox* pBox = pTableNode->GetTable().GetTableBox(m_nNodeIndex); + if (pBox) + { + return pBox->GetFrameFormat(); + } + } + } + } + [[fallthrough]]; + case RES_DRAWFRMFMT: + case RES_FLYFRMFMT: + { + SwFormat * pFormat = SwDoc::FindFormatByName(*rDoc.GetSpzFrameFormats(), m_sFormatName); + if (pFormat) + return pFormat; + pFormat = SwDoc::FindFormatByName(*rDoc.GetFrameFormats(), m_sFormatName); + if (pFormat) + return pFormat; + } + break; + } + + return nullptr; +} + +void SwUndoFormatAttr::RedoImpl(::sw::UndoRedoContext & rContext) +{ + // #i35443# - Because the undo stores the attributes for + // redo, the same code as for <Undo(..)> can be applied for <Redo(..)> + UndoImpl(rContext); +} + +void SwUndoFormatAttr::RepeatImpl(::sw::RepeatContext & rContext) +{ + if (!m_pOldSet) + return; + + SwDoc & rDoc(rContext.GetDoc()); + + SwFormat * pFormat = GetFormat(rDoc); + if (!pFormat) + return; + + switch ( m_nFormatWhich ) { + case RES_GRFFMTCOLL: { + SwNoTextNode *const pNd = + rContext.GetRepeatPaM().GetNode().GetNoTextNode(); + if( pNd ) { + rDoc.SetAttr( pFormat->GetAttrSet(), *pNd->GetFormatColl() ); + } + } + break; + + case RES_TXTFMTCOLL: + case RES_CONDTXTFMTCOLL: + { + SwTextNode *const pNd = + rContext.GetRepeatPaM().GetNode().GetTextNode(); + if( pNd ) { + rDoc.SetAttr( pFormat->GetAttrSet(), *pNd->GetFormatColl() ); + } + } + break; + + case RES_FLYFRMFMT: { + // Check if the cursor is in a flying frame + // Steps: search in all FlyFrameFormats for the FlyContent attribute + // and validate if the cursor is in the respective section + SwFrameFormat *const pFly = + rContext.GetRepeatPaM().GetNode().GetFlyFormat(); + if( pFly ) { + // Bug 43672: do not set all attributes! + if (SfxItemState::SET == + pFormat->GetAttrSet().GetItemState( RES_CNTNT )) { + SfxItemSet aTmpSet( pFormat->GetAttrSet() ); + aTmpSet.ClearItem( RES_CNTNT ); + if( aTmpSet.Count() ) { + rDoc.SetAttr( aTmpSet, *pFly ); + } + } else { + rDoc.SetAttr( pFormat->GetAttrSet(), *pFly ); + } + } + break; + } + } +} + +SwRewriter SwUndoFormatAttr::GetRewriter() const +{ + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, m_sFormatName); + + return aRewriter; +} + +void SwUndoFormatAttr::PutAttr( const SfxPoolItem& rItem, const SwDoc& rDoc ) +{ + if (RES_CNTNT == rItem.Which()) + { + return; // tdf#126017 never save SwNodeIndex, it will go stale + } + m_pOldSet->Put( rItem ); + if ( RES_ANCHOR == rItem.Which() ) + { + SwFormat * pFormat = GetFormat( rDoc ); + SaveFlyAnchor( pFormat, m_bSaveDrawPt ); + } +} + +void SwUndoFormatAttr::SaveFlyAnchor( const SwFormat * pFormat, bool bSvDrwPt ) +{ + // Format is valid, otherwise you would not reach this point here + if( bSvDrwPt ) { + if ( RES_DRAWFRMFMT == pFormat->Which() ) { + Point aPt( static_cast<const SwFrameFormat*>(pFormat)->FindSdrObject() + ->GetRelativePos() ); + // store old value as attribute, to keep SwUndoFormatAttr small + m_pOldSet->Put( SwFormatFrameSize( SwFrameSize::Variable, aPt.X(), aPt.Y() ) ); + } + } + + const SwFormatAnchor& rAnchor = + m_pOldSet->Get( RES_ANCHOR, false ); + if( !rAnchor.GetContentAnchor() ) + return; + + sal_Int32 nContent = 0; + switch( rAnchor.GetAnchorId() ) { + case RndStdIds::FLY_AS_CHAR: + case RndStdIds::FLY_AT_CHAR: + nContent = rAnchor.GetContentAnchor()->nContent.GetIndex(); + [[fallthrough]]; + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_FLY: + m_nNodeIndex = rAnchor.GetContentAnchor()->nNode.GetIndex(); + break; + default: + return; + } + + SwFormatAnchor aAnchor( rAnchor.GetAnchorId(), nContent ); + m_pOldSet->Put( aAnchor ); +} + +// #i35443# - Add return value, type <bool>. +// Return value indicates, if anchor attribute is restored. +// Note: If anchor attribute is restored, all other existing attributes +// are also restored. +bool SwUndoFormatAttr::RestoreFlyAnchor(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwFrameFormat* pFrameFormat = static_cast<SwFrameFormat*>( GetFormat( *pDoc ) ); + const SwFormatAnchor& rAnchor = + m_pOldSet->Get( RES_ANCHOR, false ); + + SwFormatAnchor aNewAnchor( rAnchor.GetAnchorId() ); + if (RndStdIds::FLY_AT_PAGE != rAnchor.GetAnchorId()) { + SwNode* pNd = pDoc->GetNodes()[ m_nNodeIndex ]; + + if ( (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) + ? ( !pNd->IsStartNode() || (SwFlyStartNode != + static_cast<SwStartNode*>(pNd)->GetStartNodeType()) ) + : !pNd->IsTextNode() ) { + // #i35443# - invalid position. + // Thus, anchor attribute not restored + return false; + } + + SwPosition aPos( *pNd ); + if ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) { + aPos.nContent.Assign( static_cast<SwTextNode*>(pNd), rAnchor.GetPageNum() ); + if ( aPos.nContent.GetIndex() > pNd->GetTextNode()->GetText().getLength()) { + // #i35443# - invalid position. + // Thus, anchor attribute not restored + return false; + } + } + aNewAnchor.SetAnchor( &aPos ); + } else + aNewAnchor.SetPageNum( rAnchor.GetPageNum() ); + + Point aDrawSavePt, aDrawOldPt; + if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) { + if( RES_DRAWFRMFMT == pFrameFormat->Which() ) { + // get the old cached value + const SwFormatFrameSize& rOldSize = m_pOldSet->Get( RES_FRM_SIZE ); + aDrawSavePt.setX( rOldSize.GetWidth() ); + aDrawSavePt.setY( rOldSize.GetHeight() ); + m_pOldSet->ClearItem( RES_FRM_SIZE ); + + // write the current value into cache + aDrawOldPt = pFrameFormat->FindSdrObject()->GetRelativePos(); + } else { + pFrameFormat->DelFrames(); // delete Frames + } + } + + const SwFormatAnchor &rOldAnch = pFrameFormat->GetAnchor(); + // #i54336# + // Consider case, that as-character anchored object has moved its anchor position. + if (RndStdIds::FLY_AS_CHAR == rOldAnch.GetAnchorId()) { + // With InContents it's tricky: the text attribute needs to be deleted. + // Unfortunately, this not only destroys the Frames but also the format. + // To prevent that, first detach the connection between attribute and + // format. + const SwPosition *pPos = rOldAnch.GetContentAnchor(); + SwTextNode *pTextNode = static_cast<SwTextNode*>(&pPos->nNode.GetNode()); + OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." ); + const sal_Int32 nIdx = pPos->nContent.GetIndex(); + SwTextAttr * const pHint = + pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT ); + assert(pHint && "Missing Hint."); + OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT, + "Missing FlyInCnt-Hint." ); + OSL_ENSURE( pHint->GetFlyCnt().GetFrameFormat() == pFrameFormat, + "Wrong TextFlyCnt-Hint." ); + const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat(); + + // Connection is now detached, therefore the attribute can be deleted + pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx ); + } + + { + m_pOldSet->Put( aNewAnchor ); + SwUndoFormatAttrHelper aTmp( *pFrameFormat, m_bSaveDrawPt ); + pFrameFormat->SetFormatAttr( *m_pOldSet ); + if ( aTmp.GetUndo() ) { + m_nNodeIndex = aTmp.GetUndo()->m_nNodeIndex; + // transfer ownership of helper object's old set + m_pOldSet = std::move(aTmp.GetUndo()->m_pOldSet); + } else { + m_pOldSet->ClearItem(); + } + } + + if ( RES_DRAWFRMFMT == pFrameFormat->Which() ) + { + // The Draw model also prepared an Undo object for its right positioning + // which unfortunately is relative. Therefore block here a position + // change of the Contact object by setting the anchor. + pFrameFormat->CallSwClientNotify(sw::RestoreFlyAnchorHint(aDrawSavePt)); + // cache the old value again + m_pOldSet->Put(SwFormatFrameSize(SwFrameSize::Variable, aDrawOldPt.X(), aDrawOldPt.Y())); + } + + if (RndStdIds::FLY_AS_CHAR == aNewAnchor.GetAnchorId()) { + const SwPosition* pPos = aNewAnchor.GetContentAnchor(); + SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode(); + OSL_ENSURE( pTextNd, "no Text Node at position." ); + SwFormatFlyCnt aFormat( pFrameFormat ); + pTextNd->InsertItem( aFormat, pPos->nContent.GetIndex(), 0 ); + } + + if (RES_DRAWFRMFMT != pFrameFormat->Which()) + pFrameFormat->MakeFrames(); + else + { + pFrameFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::POST_RESTORE_FLY_ANCHOR)); + } + + rContext.SetSelections(pFrameFormat, nullptr); + + // #i35443# - anchor attribute restored. + return true; +} + +SwUndoFormatResetAttr::SwUndoFormatResetAttr( SwFormat& rChangedFormat, + const sal_uInt16 nWhichId ) + : SwUndo( SwUndoId::RESETATTR, rChangedFormat.GetDoc() ) + , m_pChangedFormat( &rChangedFormat ) + , m_nWhichId( nWhichId ) +{ + const SfxPoolItem* pItem = nullptr; + if (rChangedFormat.GetItemState(nWhichId, false, &pItem ) == SfxItemState::SET && pItem) { + m_pOldItem.reset( pItem->Clone() ); + } +} + +SwUndoFormatResetAttr::~SwUndoFormatResetAttr() +{ +} + +void SwUndoFormatResetAttr::UndoImpl(::sw::UndoRedoContext &) +{ + if (m_pOldItem) + { + m_pChangedFormat->SetFormatAttr( *m_pOldItem ); + } +} + +void SwUndoFormatResetAttr::RedoImpl(::sw::UndoRedoContext &) +{ + if (m_pOldItem) + { + m_pChangedFormat->ResetFormatAttr( m_nWhichId ); + } +} + +SwUndoResetAttr::SwUndoResetAttr( const SwPaM& rRange, sal_uInt16 nFormatId ) + : SwUndo( SwUndoId::RESETATTR, rRange.GetDoc() ), SwUndRng( rRange ) + , m_pHistory( new SwHistory ) + , m_nFormatId( nFormatId ) +{ +} + +SwUndoResetAttr::SwUndoResetAttr( const SwPosition& rPos, sal_uInt16 nFormatId ) + : SwUndo( SwUndoId::RESETATTR, rPos.GetDoc() ) + , m_pHistory( new SwHistory ) + , m_nFormatId( nFormatId ) +{ + m_nSttNode = m_nEndNode = rPos.nNode.GetIndex(); + m_nSttContent = m_nEndContent = rPos.nContent.GetIndex(); +} + +SwUndoResetAttr::~SwUndoResetAttr() +{ +} + +void SwUndoResetAttr::UndoImpl(::sw::UndoRedoContext & rContext) +{ + // reset old values + SwDoc & rDoc = rContext.GetDoc(); + m_pHistory->TmpRollback( &rDoc, 0 ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + + if ((RES_CONDTXTFMTCOLL == m_nFormatId) && + (m_nSttNode == m_nEndNode) && (m_nSttContent == m_nEndContent)) { + SwTextNode* pTNd = rDoc.GetNodes()[ m_nSttNode ]->GetTextNode(); + if( pTNd ) { + SwIndex aIdx( pTNd, m_nSttContent ); + pTNd->DontExpandFormat( aIdx, false ); + } + } + + AddUndoRedoPaM(rContext); +} + +void SwUndoResetAttr::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPaM & rPam = AddUndoRedoPaM(rContext); + + switch ( m_nFormatId ) { + case RES_CHRFMT: + rDoc.RstTextAttrs(rPam); + break; + case RES_TXTFMTCOLL: + rDoc.ResetAttrs(rPam, false, m_Ids ); + break; + case RES_CONDTXTFMTCOLL: + rDoc.ResetAttrs(rPam, true, m_Ids ); + + break; + case RES_TXTATR_TOXMARK: + // special treatment for TOXMarks + { + SwTOXMarks aArr; + SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode ); + SwPosition aPos( aIdx, SwIndex( aIdx.GetNode().GetContentNode(), + m_nSttContent )); + + sal_uInt16 nCnt = SwDoc::GetCurTOXMark( aPos, aArr ); + if( nCnt ) { + if( 1 < nCnt ) { + // search for the right one + SwHistoryHint* pHHint = (GetHistory())[ 0 ]; + if( pHHint && HSTRY_SETTOXMARKHNT == pHHint->Which() ) { + while( nCnt ) { + if ( static_cast<SwHistorySetTOXMark*>(pHHint) + ->IsEqual( *aArr[ --nCnt ] ) ) { + ++nCnt; + break; + } + } + } else + nCnt = 0; + } + // found one, thus delete it + if( nCnt-- ) { + rDoc.DeleteTOXMark( aArr[ nCnt ] ); + } + } + } + break; + } +} + +void SwUndoResetAttr::RepeatImpl(::sw::RepeatContext & rContext) +{ + if (m_nFormatId < RES_FMT_BEGIN) { + return; + } + + switch ( m_nFormatId ) { + case RES_CHRFMT: + rContext.GetDoc().RstTextAttrs(rContext.GetRepeatPaM()); + break; + case RES_TXTFMTCOLL: + rContext.GetDoc().ResetAttrs(rContext.GetRepeatPaM(), false, m_Ids); + break; + case RES_CONDTXTFMTCOLL: + rContext.GetDoc().ResetAttrs(rContext.GetRepeatPaM(), true, m_Ids); + break; + } +} + +void SwUndoResetAttr::SetAttrs( const std::set<sal_uInt16> &rAttrs ) +{ + m_Ids = rAttrs; +} + +SwUndoAttr::SwUndoAttr( const SwPaM& rRange, const SfxPoolItem& rAttr, + const SetAttrMode nFlags ) + : SwUndo( SwUndoId::INSATTR, rRange.GetDoc() ), SwUndRng( rRange ) + , m_AttrSet( rRange.GetDoc()->GetAttrPool(), {{rAttr.Which(), rAttr.Which()}} ) + , m_pHistory( new SwHistory ) + , m_nNodeIndex( ULONG_MAX ) + , m_nInsertFlags( nFlags ) +{ + m_AttrSet.Put( rAttr ); + + // Save character style as a style name, not as a reference + const SfxPoolItem* pItem = m_AttrSet.GetItem(RES_TXTATR_CHARFMT); + if (pItem) + { + uno::Any aValue; + pItem->QueryValue(aValue, RES_TXTATR_CHARFMT); + aValue >>= m_aChrFormatName; + } +} + +SwUndoAttr::SwUndoAttr( const SwPaM& rRange, const SfxItemSet& rSet, + const SetAttrMode nFlags ) + : SwUndo( SwUndoId::INSATTR, rRange.GetDoc() ), SwUndRng( rRange ) + , m_AttrSet( rSet ) + , m_pHistory( new SwHistory ) + , m_nNodeIndex( ULONG_MAX ) + , m_nInsertFlags( nFlags ) +{ + // Save character style as a style name, not as a reference + const SfxPoolItem* pItem = m_AttrSet.GetItem(RES_TXTATR_CHARFMT); + if (pItem) + { + uno::Any aValue; + pItem->QueryValue(aValue, RES_TXTATR_CHARFMT); + aValue >>= m_aChrFormatName; + } +} + +SwUndoAttr::~SwUndoAttr() +{ +} + +void SwUndoAttr::SaveRedlineData( const SwPaM& rPam, bool bIsContent ) +{ + SwDoc* pDoc = rPam.GetDoc(); + if ( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) { + m_pRedlineData.reset( new SwRedlineData( bIsContent + ? RedlineType::Insert + : RedlineType::Format, + pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + } + + m_pRedlineSaveData.reset( new SwRedlineSaveDatas ); + if ( !FillSaveDataForFormat( rPam, *m_pRedlineSaveData )) + m_pRedlineSaveData.reset(); + + SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() ); + if ( bIsContent ) { + m_nNodeIndex = rPam.GetPoint()->nNode.GetIndex(); + } +} + +void SwUndoAttr::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + + RemoveIdx( *pDoc ); + + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ) { + SwPaM aPam(pDoc->GetNodes().GetEndOfContent()); + if ( ULONG_MAX != m_nNodeIndex ) { + aPam.DeleteMark(); + aPam.GetPoint()->nNode = m_nNodeIndex; + aPam.GetPoint()->nContent.Assign( aPam.GetContentNode(), m_nSttContent ); + aPam.SetMark(); + ++aPam.GetPoint()->nContent; + pDoc->getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Any); + } else { + // remove all format redlines, will be recreated if needed + SetPaM(aPam); + pDoc->getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Format); + if (m_pRedlineSaveData) + { + SetSaveData( *pDoc, *m_pRedlineSaveData ); + } + } + } + + const bool bToLast = (1 == m_AttrSet.Count()) + && (RES_TXTATR_FIELD <= *m_AttrSet.GetRanges()) + && (*m_AttrSet.GetRanges() <= RES_TXTATR_ANNOTATION); + + // restore old values + m_pHistory->TmpRollback( pDoc, 0, !bToLast ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + + // set cursor onto Undo area + AddUndoRedoPaM(rContext); +} + +void SwUndoAttr::RepeatImpl(::sw::RepeatContext & rContext) +{ + // RefMarks are not repeat capable + if ( SfxItemState::SET != m_AttrSet.GetItemState( RES_TXTATR_REFMARK, false ) ) { + rContext.GetDoc().getIDocumentContentOperations().InsertItemSet( rContext.GetRepeatPaM(), + m_AttrSet, m_nInsertFlags ); + } else if ( 1 < m_AttrSet.Count() ) { + SfxItemSet aTmpSet( m_AttrSet ); + aTmpSet.ClearItem( RES_TXTATR_REFMARK ); + rContext.GetDoc().getIDocumentContentOperations().InsertItemSet( rContext.GetRepeatPaM(), + aTmpSet, m_nInsertFlags ); + } +} + +void SwUndoAttr::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPaM & rPam = AddUndoRedoPaM(rContext); + + // Restore pointer to char format from name + if (!m_aChrFormatName.isEmpty()) + { + SwCharFormat* pCharFormat = rDoc.FindCharFormatByName(m_aChrFormatName); + if (pCharFormat) + { + SwFormatCharFormat aFormat(pCharFormat); + m_AttrSet.Put(aFormat); + } + } + + if ( m_pRedlineData && + IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ) { + RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags::Ignore ); + rDoc.getIDocumentContentOperations().InsertItemSet( rPam, m_AttrSet, m_nInsertFlags ); + + if ( ULONG_MAX != m_nNodeIndex ) { + rPam.SetMark(); + if ( rPam.Move( fnMoveBackward ) ) { + rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ), + true); + } + rPam.DeleteMark(); + } else { + rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ), true); + } + + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } else { + rDoc.getIDocumentContentOperations().InsertItemSet( rPam, m_AttrSet, m_nInsertFlags ); + } +} + +void SwUndoAttr::RemoveIdx( SwDoc& rDoc ) +{ + if ( SfxItemState::SET != m_AttrSet.GetItemState( RES_TXTATR_FTN, false )) + return ; + + SwNodes& rNds = rDoc.GetNodes(); + for ( sal_uInt16 n = 0; n < m_pHistory->Count(); ++n ) { + sal_Int32 nContent = 0; + sal_uLong nNode = 0; + SwHistoryHint* pHstHint = (*m_pHistory)[ n ]; + switch ( pHstHint->Which() ) { + case HSTRY_RESETTXTHNT: { + SwHistoryResetText * pHistoryHint + = static_cast<SwHistoryResetText*>(pHstHint); + if ( RES_TXTATR_FTN == pHistoryHint->GetWhich() ) { + nNode = pHistoryHint->GetNode(); + nContent = pHistoryHint->GetContent(); + } + } + break; + + default: + break; + } + + if( nNode ) { + SwTextNode* pTextNd = rNds[ nNode ]->GetTextNode(); + if( pTextNd ) { + SwTextAttr *const pTextHt = + pTextNd->GetTextAttrForCharAt(nContent, RES_TXTATR_FTN); + if( pTextHt ) { + // ok, so get values + SwTextFootnote* pFootnote = static_cast<SwTextFootnote*>(pTextHt); + RemoveIdxFromSection( rDoc, pFootnote->GetStartNode()->GetIndex() ); + return ; + } + } + } + } +} + +SwUndoDefaultAttr::SwUndoDefaultAttr( const SfxItemSet& rSet, const SwDoc* pDoc ) + : SwUndo( SwUndoId::SETDEFTATTR, pDoc ) +{ + const SfxPoolItem* pItem; + if( SfxItemState::SET == rSet.GetItemState( RES_PARATR_TABSTOP, false, &pItem ) ) { + // store separately, because it may change! + m_pTabStop.reset( static_cast<SvxTabStopItem*>(pItem->Clone()) ); + if ( 1 != rSet.Count() ) { // are there more attributes? + m_pOldSet.reset( new SfxItemSet( rSet ) ); + } + } else { + m_pOldSet.reset( new SfxItemSet( rSet ) ); + } +} + +SwUndoDefaultAttr::~SwUndoDefaultAttr() +{ +} + +void SwUndoDefaultAttr::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if (m_pOldSet) + { + SwUndoFormatAttrHelper aTmp( + *rDoc.GetDfltTextFormatColl() ); + rDoc.SetDefault( *m_pOldSet ); + m_pOldSet.reset(); + if ( aTmp.GetUndo() ) { + // transfer ownership of helper object's old set + m_pOldSet = std::move(aTmp.GetUndo()->m_pOldSet); + } + } + if (m_pTabStop) + { + std::unique_ptr<SvxTabStopItem> pOld(rDoc.GetDefault(RES_PARATR_TABSTOP).Clone()); + rDoc.SetDefault( *m_pTabStop ); + m_pTabStop = std::move( pOld ); + } +} + +void SwUndoDefaultAttr::RedoImpl(::sw::UndoRedoContext & rContext) +{ + UndoImpl(rContext); +} + +SwUndoMoveLeftMargin::SwUndoMoveLeftMargin( + const SwPaM& rPam, bool bFlag, bool bMod ) + : SwUndo( bFlag ? SwUndoId::INC_LEFTMARGIN : SwUndoId::DEC_LEFTMARGIN, rPam.GetDoc() ) + , SwUndRng( rPam ) + , m_pHistory( new SwHistory ) + , m_bModulus( bMod ) +{ +} + +SwUndoMoveLeftMargin::~SwUndoMoveLeftMargin() +{ +} + +void SwUndoMoveLeftMargin::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + // restore old values + m_pHistory->TmpRollback( & rDoc, 0 ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + + AddUndoRedoPaM(rContext); +} + +void SwUndoMoveLeftMargin::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPaM & rPam = AddUndoRedoPaM(rContext); + + rDoc.MoveLeftMargin( rPam, + GetId() == SwUndoId::INC_LEFTMARGIN, m_bModulus, + rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ); +} + +void SwUndoMoveLeftMargin::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + rDoc.MoveLeftMargin(rContext.GetRepeatPaM(), GetId() == SwUndoId::INC_LEFTMARGIN, + m_bModulus, rDoc.getIDocumentLayoutAccess().GetCurrentLayout()); +} + +SwUndoChangeFootNote::SwUndoChangeFootNote( + const SwPaM& rRange, const OUString& rText, + bool const bIsEndNote) + : SwUndo( SwUndoId::CHGFTN, rRange.GetDoc() ), SwUndRng( rRange ) + , m_pHistory( new SwHistory() ) + , m_Text( rText ) + , m_bEndNote( bIsEndNote ) +{ +} + +SwUndoChangeFootNote::~SwUndoChangeFootNote() +{ +} + +void SwUndoChangeFootNote::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + m_pHistory->TmpRollback( &rDoc, 0 ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + + rDoc.GetFootnoteIdxs().UpdateAllFootnote(); + + AddUndoRedoPaM(rContext); +} + +void SwUndoChangeFootNote::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc( rContext.GetDoc() ); + SwPaM & rPaM = AddUndoRedoPaM(rContext); + rDoc.SetCurFootnote(rPaM, m_Text, m_bEndNote); + SetPaM(rPaM); +} + +void SwUndoChangeFootNote::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + rDoc.SetCurFootnote(rContext.GetRepeatPaM(), m_Text, m_bEndNote); +} + +SwUndoFootNoteInfo::SwUndoFootNoteInfo( const SwFootnoteInfo &rInfo, const SwDoc* pDoc ) + : SwUndo( SwUndoId::FTNINFO, pDoc ) + , m_pFootNoteInfo( new SwFootnoteInfo( rInfo ) ) +{ +} + +SwUndoFootNoteInfo::~SwUndoFootNoteInfo() +{ +} + +void SwUndoFootNoteInfo::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwFootnoteInfo *pInf = new SwFootnoteInfo( rDoc.GetFootnoteInfo() ); + rDoc.SetFootnoteInfo( *m_pFootNoteInfo ); + m_pFootNoteInfo.reset( pInf ); +} + +void SwUndoFootNoteInfo::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwFootnoteInfo *pInf = new SwFootnoteInfo( rDoc.GetFootnoteInfo() ); + rDoc.SetFootnoteInfo( *m_pFootNoteInfo ); + m_pFootNoteInfo.reset( pInf ); +} + +SwUndoEndNoteInfo::SwUndoEndNoteInfo( const SwEndNoteInfo &rInfo, const SwDoc* pDoc ) + : SwUndo( SwUndoId::FTNINFO, pDoc ) + , m_pEndNoteInfo( new SwEndNoteInfo( rInfo ) ) +{ +} + +SwUndoEndNoteInfo::~SwUndoEndNoteInfo() +{ +} + +void SwUndoEndNoteInfo::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwEndNoteInfo *pInf = new SwEndNoteInfo( rDoc.GetEndNoteInfo() ); + rDoc.SetEndNoteInfo( *m_pEndNoteInfo ); + m_pEndNoteInfo.reset( pInf ); +} + +void SwUndoEndNoteInfo::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwEndNoteInfo *pInf = new SwEndNoteInfo( rDoc.GetEndNoteInfo() ); + rDoc.SetEndNoteInfo( *m_pEndNoteInfo ); + m_pEndNoteInfo.reset( pInf ); +} + +SwUndoDontExpandFormat::SwUndoDontExpandFormat( const SwPosition& rPos ) + : SwUndo( SwUndoId::DONTEXPAND, rPos.GetDoc() ) + , m_nNodeIndex( rPos.nNode.GetIndex() ) + , m_nContentIndex( rPos.nContent.GetIndex() ) +{ +} + +void SwUndoDontExpandFormat::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + SwDoc *const pDoc = & rContext.GetDoc(); + + SwPosition& rPos = *pPam->GetPoint(); + rPos.nNode = m_nNodeIndex; + rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), m_nContentIndex); + pDoc->DontExpandFormat( rPos, false ); +} + +void SwUndoDontExpandFormat::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + SwDoc *const pDoc = & rContext.GetDoc(); + + SwPosition& rPos = *pPam->GetPoint(); + rPos.nNode = m_nNodeIndex; + rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), m_nContentIndex); + pDoc->DontExpandFormat( rPos ); +} + +void SwUndoDontExpandFormat::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwPaM & rPam = rContext.GetRepeatPaM(); + SwDoc & rDoc = rContext.GetDoc(); + rDoc.DontExpandFormat( *rPam.GetPoint() ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unbkmk.cxx b/sw/source/core/undo/unbkmk.cxx new file mode 100644 index 000000000..16d45a7b7 --- /dev/null +++ b/sw/source/core/undo/unbkmk.cxx @@ -0,0 +1,218 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoBookmark.hxx> + +#include <strings.hrc> +#include <doc.hxx> +#include <swundo.hxx> +#include <pam.hxx> + +#include <UndoCore.hxx> +#include <IMark.hxx> +#include <rolbck.hxx> + +#include <SwRewriter.hxx> + +SwUndoBookmark::SwUndoBookmark( SwUndoId nUndoId, + const ::sw::mark::IMark& rBkmk ) + : SwUndo( nUndoId, rBkmk.GetMarkPos().GetDoc() ) + , m_pHistoryBookmark(new SwHistoryBookmark(rBkmk, true, rBkmk.IsExpanded())) +{ +} + +SwUndoBookmark::~SwUndoBookmark() +{ +} + +void SwUndoBookmark::SetInDoc( SwDoc* pDoc ) +{ + m_pHistoryBookmark->SetInDoc( pDoc, false ); +} + +void SwUndoBookmark::ResetInDoc( SwDoc* pDoc ) +{ + IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess(); + for ( IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getAllMarksBegin(); + ppBkmk != pMarkAccess->getAllMarksEnd(); + ++ppBkmk ) + { + if ( m_pHistoryBookmark->IsEqualBookmark( **ppBkmk ) ) + { + pMarkAccess->deleteMark( ppBkmk ); + break; + } + } +} + +SwRewriter SwUndoBookmark::GetRewriter() const +{ + SwRewriter aResult; + + aResult.AddRule(UndoArg1, m_pHistoryBookmark->GetName()); + + return aResult; +} + +SwUndoInsBookmark::SwUndoInsBookmark( const ::sw::mark::IMark& rBkmk ) + : SwUndoBookmark( SwUndoId::INSBOOKMARK, rBkmk ) +{ +} + +void SwUndoInsBookmark::UndoImpl(::sw::UndoRedoContext & rContext) +{ + ResetInDoc( &rContext.GetDoc() ); +} + +void SwUndoInsBookmark::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SetInDoc( &rContext.GetDoc() ); +} + +SwUndoDeleteBookmark::SwUndoDeleteBookmark( const ::sw::mark::IMark& rBkmk ) + : SwUndoBookmark( SwUndoId::DELBOOKMARK, rBkmk ) +{ +} + +void SwUndoDeleteBookmark::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SetInDoc( &rContext.GetDoc() ); +} + +void SwUndoDeleteBookmark::RedoImpl(::sw::UndoRedoContext & rContext) +{ + ResetInDoc( &rContext.GetDoc() ); +} + +SwUndoRenameBookmark::SwUndoRenameBookmark( const OUString& rOldName, const OUString& rNewName, const SwDoc* pDoc ) + : SwUndo( SwUndoId::BOOKMARK_RENAME, pDoc ) + , m_sOldName( rOldName ) + , m_sNewName( rNewName ) +{ +} + +SwUndoRenameBookmark::~SwUndoRenameBookmark() +{ +} + +static OUString lcl_QuoteName(const OUString& rName) +{ + static const OUString sStart = SwResId(STR_START_QUOTE); + static const OUString sEnd = SwResId(STR_END_QUOTE); + return sStart + rName + sEnd; +} + +SwRewriter SwUndoRenameBookmark::GetRewriter() const +{ + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, lcl_QuoteName(m_sOldName)); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + aRewriter.AddRule(UndoArg3, lcl_QuoteName(m_sNewName)); + return aRewriter; +} + +void SwUndoRenameBookmark::Rename(::sw::UndoRedoContext const & rContext, const OUString& sFrom, const OUString& sTo) +{ + IDocumentMarkAccess* const pMarkAccess = rContext.GetDoc().getIDocumentMarkAccess(); + IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findMark(sFrom); + if (ppBkmk != pMarkAccess->getAllMarksEnd()) + { + pMarkAccess->renameMark( *ppBkmk, sTo ); + } +} + +void SwUndoRenameBookmark::UndoImpl(::sw::UndoRedoContext & rContext) +{ + Rename(rContext, m_sNewName, m_sOldName); +} + +void SwUndoRenameBookmark::RedoImpl(::sw::UndoRedoContext & rContext) +{ + Rename(rContext, m_sOldName, m_sNewName); +} + +SwUndoInsNoTextFieldmark::SwUndoInsNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark) + : SwUndo(SwUndoId::INSERT, rFieldmark.GetMarkPos().GetDoc()) + , m_pHistoryNoTextFieldmark(new SwHistoryNoTextFieldmark(rFieldmark)) +{ +} + +void SwUndoInsNoTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext) +{ + m_pHistoryNoTextFieldmark->ResetInDoc(&rContext.GetDoc()); +} + +void SwUndoInsNoTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext) +{ + m_pHistoryNoTextFieldmark->SetInDoc(&rContext.GetDoc(), false); +} + +SwUndoDelNoTextFieldmark::SwUndoDelNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark) + : SwUndo(SwUndoId::DELETE, rFieldmark.GetMarkPos().GetDoc()) + , m_pHistoryNoTextFieldmark(new SwHistoryNoTextFieldmark(rFieldmark)) +{ +} + +SwUndoDelNoTextFieldmark::~SwUndoDelNoTextFieldmark() = default; + +void SwUndoDelNoTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext) +{ + m_pHistoryNoTextFieldmark->SetInDoc(&rContext.GetDoc(), false); +} + +void SwUndoDelNoTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext) +{ + m_pHistoryNoTextFieldmark->ResetInDoc(&rContext.GetDoc()); +} + +SwUndoInsTextFieldmark::SwUndoInsTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark) + : SwUndo(SwUndoId::INSERT, rFieldmark.GetMarkPos().GetDoc()) + , m_pHistoryTextFieldmark(new SwHistoryTextFieldmark(rFieldmark)) +{ +} + +void SwUndoInsTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext) +{ + m_pHistoryTextFieldmark->ResetInDoc(&rContext.GetDoc()); +} + +void SwUndoInsTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext) +{ + m_pHistoryTextFieldmark->SetInDoc(&rContext.GetDoc(), false); +} + +SwUndoDelTextFieldmark::SwUndoDelTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark) + : SwUndo(SwUndoId::DELETE, rFieldmark.GetMarkPos().GetDoc()) + , m_pHistoryTextFieldmark(new SwHistoryTextFieldmark(rFieldmark)) +{ +} + +SwUndoDelTextFieldmark::~SwUndoDelTextFieldmark() = default; + +void SwUndoDelTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext) +{ + m_pHistoryTextFieldmark->SetInDoc(&rContext.GetDoc(), false); +} + +void SwUndoDelTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext) +{ + m_pHistoryTextFieldmark->ResetInDoc(&rContext.GetDoc()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/undel.cxx b/sw/source/core/undo/undel.cxx new file mode 100644 index 000000000..e1e519fa2 --- /dev/null +++ b/sw/source/core/undo/undel.cxx @@ -0,0 +1,1317 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoDelete.hxx> +#include <hintids.hxx> +#include <rtl/ustrbuf.hxx> +#include <unotools/charclass.hxx> +#include <frmfmt.hxx> +#include <fmtanchr.hxx> +#include <doc.hxx> +#include <UndoManager.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <swtable.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> +#include <poolfmt.hxx> +#include <mvsave.hxx> +#include <docary.hxx> +#include <frmtool.hxx> +#include <txtfrm.hxx> +#include <rootfrm.hxx> +#include <strings.hrc> +#include <frameformats.hxx> +#include <vector> + +// DELETE +/* lcl_MakeAutoFrames has to call MakeFrames for objects bounded "AtChar" + ( == AUTO ), if the anchor frame has be moved via MoveNodes(..) and + DelFrames(..) +*/ +static void lcl_MakeAutoFrames( const SwFrameFormats& rSpzArr, sal_uLong nMovedIndex ) +{ + for( size_t n = 0; n < rSpzArr.size(); ++n ) + { + SwFrameFormat * pFormat = rSpzArr[n]; + const SwFormatAnchor* pAnchor = &pFormat->GetAnchor(); + if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR) + { + const SwPosition* pAPos = pAnchor->GetContentAnchor(); + if( pAPos && nMovedIndex == pAPos->nNode.GetIndex() ) + pFormat->MakeFrames(); + } + } +} + +static SwTextNode * FindFirstAndNextNode(SwDoc & rDoc, SwUndRng const& rRange, + SwRedlineSaveDatas const& rRedlineSaveData, + SwTextNode *& o_rpFirstMergedDeletedTextNode) +{ + // redlines are corrected now to exclude the deleted node + assert(rRange.m_nEndContent == 0); + sal_uLong nEndOfRedline = 0; + for (size_t i = 0; i < rRedlineSaveData.size(); ++i) + { + auto const& rRedline(rRedlineSaveData[i]); + if (rRedline.m_nSttNode <= rRange.m_nSttNode + && rRedline.m_nSttNode < rRange.m_nEndNode + && rRange.m_nEndNode <= rRedline.m_nEndNode + && rRedline.GetType() == RedlineType::Delete) + { + nEndOfRedline = rRedline.m_nEndNode; + o_rpFirstMergedDeletedTextNode = rDoc.GetNodes()[rRedline.m_nSttNode]->GetTextNode(); + assert(rRange.m_nSttNode == rRange.m_nEndNode - 1); // otherwise this needs to iterate more RL to find the first node? + break; + } + } + if (nEndOfRedline) + { + assert(o_rpFirstMergedDeletedTextNode); + SwTextNode * pNextNode(nullptr); + for (sal_uLong i = rRange.m_nEndNode; /* i <= nEndOfRedline */; ++i) + { + SwNode *const pNode(rDoc.GetNodes()[i]); + assert(!pNode->IsEndNode()); // cannot be both leaving section here *and* overlapping redline + if (pNode->IsStartNode()) + { + i = pNode->EndOfSectionIndex(); // will be incremented again + } + else if (pNode->IsTextNode()) + { + pNextNode = pNode->GetTextNode(); + break; + } + } + assert(pNextNode); + return pNextNode; + } + else + { + return nullptr; + } +} + +static void DelFullParaMoveFrames(SwDoc & rDoc, SwUndRng const& rRange, + SwRedlineSaveDatas const& rRedlineSaveData) +{ + SwTextNode * pFirstMergedDeletedTextNode(nullptr); + SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, rRange, + rRedlineSaveData, pFirstMergedDeletedTextNode); + if (pNextNode) + { + std::vector<SwTextFrame*> frames; + SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pFirstMergedDeletedTextNode); + for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + { + if (pFrame->getRootFrame()->IsHideRedlines()) + { + assert(pFrame->GetMergedPara()); + assert(pFrame->GetMergedPara()->pFirstNode == pFirstMergedDeletedTextNode); + assert(pNextNode->GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex()); + frames.push_back(pFrame); + } + } + for (SwTextFrame *const pFrame : frames) + { + // sw_redlinehide: don't need FrameMode::Existing here + // because everything from pNextNode onwards is already + // correctly hidden + pFrame->RegisterToNode(*pNextNode, true); + } + } +} + +// SwUndoDelete has to perform a deletion and to record anything that is needed +// to restore the situation before the deletion. Unfortunately a part of the +// deletion will be done after calling this Ctor, this has to be kept in mind! +// In this Ctor only the complete paragraphs will be deleted, the joining of +// the first and last paragraph of the selection will be handled outside this +// function. +// Here are the main steps of the function: +// 1. Deletion/recording of content indices of the selection: footnotes, fly +// frames and bookmarks +// Step 1 could shift all nodes by deletion of footnotes => nNdDiff will be set. +// 2. If the paragraph where the selection ends, is the last content of a +// section so that this section becomes empty when the paragraphs will be +// joined we have to do some smart actions ;-) The paragraph will be moved +// outside the section and replaced by a dummy text node, the complete +// section will be deleted in step 3. The difference between replacement +// dummy and original is nReplacementDummy. +// 3. Moving complete selected nodes into the UndoArray. Before this happens the +// selection has to be extended if there are sections which would become +// empty otherwise. BTW: sections will be moved into the UndoArray if they +// are complete part of the selection. Sections starting or ending outside +// of the selection will not be removed from the DocNodeArray even they got +// a "dummy"-copy in the UndoArray. +// 4. We have to anticipate the joining of the two paragraphs if the start +// paragraph is inside a section and the end paragraph not. Then we have to +// move the paragraph into this section and to record this in nSectDiff. +SwUndoDelete::SwUndoDelete( + SwPaM& rPam, + bool bFullPara, + bool bCalledByTableCpy ) + : SwUndo(SwUndoId::DELETE, rPam.GetDoc()), + SwUndRng( rPam ), + m_nNode(0), + m_nNdDiff(0), + m_nSectDiff(0), + m_nReplaceDummy(0), + m_nSetPos(0), + m_bGroup( false ), + m_bBackSp( false ), + m_bJoinNext( false ), + m_bTableDelLastNd( false ), + // bFullPara is set e.g. if an empty paragraph before a table is deleted + m_bDelFullPara( bFullPara ), + m_bResetPgDesc( false ), + m_bResetPgBrk( false ), + m_bFromTableCopy( bCalledByTableCpy ) +{ + + m_bCacheComment = false; + + SwDoc * pDoc = rPam.GetDoc(); + + if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + { + m_pRedlSaveData.reset(new SwRedlineSaveDatas); + if( !FillSaveData( rPam, *m_pRedlSaveData )) + { + m_pRedlSaveData.reset(); + } + } + + if( !m_pHistory ) + m_pHistory.reset( new SwHistory ); + + // delete all footnotes for now + const SwPosition *pStt = rPam.Start(), + *pEnd = rPam.GetPoint() == pStt + ? rPam.GetMark() + : rPam.GetPoint(); + + // Step 1. deletion/record of content indices + if( m_bDelFullPara ) + { + OSL_ENSURE( rPam.HasMark(), "PaM without Mark" ); + DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(), + DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) ); + + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + DelBookmarks(pStt->nNode, pEnd->nNode); + } + else + { + DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() ); + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + if (m_nEndNode - m_nSttNode > 1) // check for fully selected nodes + { + SwNodeIndex const start(pStt->nNode, +1); + DelBookmarks(start, pEnd->nNode); + } + } + + m_nSetPos = m_pHistory ? m_pHistory->Count() : 0; + + // Is already anything deleted? + m_nNdDiff = m_nSttNode - pStt->nNode.GetIndex(); + + m_bJoinNext = !bFullPara && pEnd == rPam.GetPoint(); + m_bBackSp = !bFullPara && !m_bJoinNext; + + SwTextNode *pSttTextNd = nullptr, *pEndTextNd = nullptr; + if( !bFullPara ) + { + pSttTextNd = pStt->nNode.GetNode().GetTextNode(); + pEndTextNd = m_nSttNode == m_nEndNode + ? pSttTextNd + : pEnd->nNode.GetNode().GetTextNode(); + } + else if (m_pRedlSaveData) + { + DelFullParaMoveFrames(*pDoc, *this, *m_pRedlSaveData); + } + + bool bMoveNds = *pStt != *pEnd // any area still existent? + && ( SaveContent( pStt, pEnd, pSttTextNd, pEndTextNd ) || m_bFromTableCopy ); + + if( pSttTextNd && pEndTextNd && pSttTextNd != pEndTextNd ) + { + // two different TextNodes, thus save also the TextFormatCollection + m_pHistory->Add( pSttTextNd->GetTextColl(),pStt->nNode.GetIndex(), SwNodeType::Text ); + m_pHistory->Add( pEndTextNd->GetTextColl(),pEnd->nNode.GetIndex(), SwNodeType::Text ); + + if( !m_bJoinNext ) // Selection from bottom to top + { + // When using JoinPrev() all AUTO-PageBreak's will be copied + // correctly. To restore them with UNDO, Auto-PageBreak of the + // EndNode needs to be reset. Same for PageDesc and ColBreak. + if( pEndTextNd->HasSwAttrSet() ) + { + SwRegHistory aRegHist( *pEndTextNd, m_pHistory.get() ); + if( SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState( + RES_BREAK, false ) ) + pEndTextNd->ResetAttr( RES_BREAK ); + if( pEndTextNd->HasSwAttrSet() && + SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState( + RES_PAGEDESC, false ) ) + pEndTextNd->ResetAttr( RES_PAGEDESC ); + } + } + } + + // Move now also the PaM. The SPoint is at the beginning of a SSelection. + if( pEnd == rPam.GetPoint() && ( !bFullPara || pSttTextNd || pEndTextNd ) ) + rPam.Exchange(); + + if( !pSttTextNd && !pEndTextNd ) + --rPam.GetPoint()->nNode; + rPam.DeleteMark(); // the SPoint is in the selection + + if( !pEndTextNd ) + m_nEndContent = 0; + if( !pSttTextNd ) + m_nSttContent = 0; + + if( bMoveNds ) // Do Nodes exist that need to be moved? + { + SwNodes& rNds = pDoc->GetUndoManager().GetUndoNodes(); + SwNodes& rDocNds = pDoc->GetNodes(); + SwNodeRange aRg( rDocNds, m_nSttNode - m_nNdDiff, + rDocNds, m_nEndNode - m_nNdDiff ); + if( !bFullPara && !pEndTextNd && + &aRg.aEnd.GetNode() != &pDoc->GetNodes().GetEndOfContent() ) + { + SwNode* pNode = aRg.aEnd.GetNode().StartOfSectionNode(); + if( pNode->GetIndex() >= m_nSttNode - m_nNdDiff ) + ++aRg.aEnd; // Deletion of a complete table + } + SwNode* pTmpNd; + // Step 2: Expand selection if necessary + if( m_bJoinNext || bFullPara ) + { + // If all content of a section will be moved into Undo, the section + // itself should be moved completely. + while( aRg.aEnd.GetIndex() + 2 < rDocNds.Count() && + ( (pTmpNd = rDocNds[ aRg.aEnd.GetIndex()+1 ])->IsEndNode() && + pTmpNd->StartOfSectionNode()->IsSectionNode() && + pTmpNd->StartOfSectionNode()->GetIndex() >= aRg.aStart.GetIndex() ) ) + ++aRg.aEnd; + m_nReplaceDummy = aRg.aEnd.GetIndex() + m_nNdDiff - m_nEndNode; + if( m_nReplaceDummy ) + { // The selection has been expanded, because + ++aRg.aEnd; + if( pEndTextNd ) + { + // The end text node has to leave the (expanded) selection + // The dummy is needed because MoveNodes deletes empty + // sections + ++m_nReplaceDummy; + SwNodeRange aMvRg( *pEndTextNd, 0, *pEndTextNd, 1 ); + SwPosition aSplitPos( *pEndTextNd ); + ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); + pDoc->getIDocumentContentOperations().SplitNode( aSplitPos, false ); + rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd ); + --aRg.aEnd; + } + else + m_nReplaceDummy = 0; + } + } + if( m_bBackSp || bFullPara ) + { + // See above, the selection has to be expanded if there are "nearly + // empty" sections and a replacement dummy has to be set if needed. + while( 1 < aRg.aStart.GetIndex() && + ( (pTmpNd = rDocNds[ aRg.aStart.GetIndex()-1 ])->IsSectionNode() && + pTmpNd->EndOfSectionIndex() < aRg.aEnd.GetIndex() ) ) + --aRg.aStart; + if( pSttTextNd ) + { + m_nReplaceDummy = m_nSttNode - m_nNdDiff - aRg.aStart.GetIndex(); + if( m_nReplaceDummy ) + { + SwNodeRange aMvRg( *pSttTextNd, 0, *pSttTextNd, 1 ); + SwPosition aSplitPos( *pSttTextNd ); + ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); + pDoc->getIDocumentContentOperations().SplitNode( aSplitPos, false ); + rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart ); + --aRg.aStart; + } + } + } + + if( m_bFromTableCopy ) + { + if( !pEndTextNd ) + { + if( pSttTextNd ) + ++aRg.aStart; + else if( !bFullPara && !aRg.aEnd.GetNode().IsContentNode() ) + --aRg.aEnd; + } + } + else if (pSttTextNd && (pEndTextNd || pSttTextNd->GetText().getLength())) + ++aRg.aStart; + + // Step 3: Moving into UndoArray... + m_nNode = rNds.GetEndOfContent().GetIndex(); + rDocNds.MoveNodes( aRg, rNds, SwNodeIndex( rNds.GetEndOfContent() )); + m_pMvStt.reset( new SwNodeIndex( rNds, m_nNode ) ); + // remember difference! + m_nNode = rNds.GetEndOfContent().GetIndex() - m_nNode; + + if( pSttTextNd && pEndTextNd ) + { + //Step 4: Moving around sections + m_nSectDiff = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex(); + // nSect is the number of sections which starts(ends) between start + // and end node of the selection. The "loser" paragraph has to be + // moved into the section(s) of the "winner" paragraph + if( m_nSectDiff ) + { + if( m_bJoinNext ) + { + SwNodeRange aMvRg( *pEndTextNd, 0, *pEndTextNd, 1 ); + rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart ); + } + else + { + SwNodeRange aMvRg( *pSttTextNd, 0, *pSttTextNd, 1 ); + rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd ); + } + } + } + if( m_nSectDiff || m_nReplaceDummy ) + lcl_MakeAutoFrames( *pDoc->GetSpzFrameFormats(), + m_bJoinNext ? pEndTextNd->GetIndex() : pSttTextNd->GetIndex() ); + } + else + m_nNode = 0; // moved no node -> no difference at the end + + // Are there any Nodes that got deleted before that (FootNotes + // have ContentNodes)? + if( !pSttTextNd && !pEndTextNd ) + { + m_nNdDiff = m_nSttNode - rPam.GetPoint()->nNode.GetIndex() - (bFullPara ? 0 : 1); + rPam.Move( fnMoveForward, GoInNode ); + } + else + { + m_nNdDiff = m_nSttNode; + if( m_nSectDiff && m_bBackSp ) + m_nNdDiff += m_nSectDiff; + m_nNdDiff -= rPam.GetPoint()->nNode.GetIndex(); + } + + if( !rPam.GetNode().IsContentNode() ) + rPam.GetPoint()->nContent.Assign( nullptr, 0 ); + + // is a history necessary here at all? + if( m_pHistory && !m_pHistory->Count() ) + m_pHistory.reset(); +} + +bool SwUndoDelete::SaveContent( const SwPosition* pStt, const SwPosition* pEnd, + SwTextNode* pSttTextNd, SwTextNode* pEndTextNd ) +{ + sal_uLong nNdIdx = pStt->nNode.GetIndex(); + // 1 - copy start in Start-String + if( pSttTextNd ) + { + bool bOneNode = m_nSttNode == m_nEndNode; + SwRegHistory aRHst( *pSttTextNd, m_pHistory.get() ); + // always save all text atttibutes because of possibly overlapping + // areas of on/off + m_pHistory->CopyAttr( pSttTextNd->GetpSwpHints(), nNdIdx, + 0, pSttTextNd->GetText().getLength(), true ); + if( !bOneNode && pSttTextNd->HasSwAttrSet() ) + m_pHistory->CopyFormatAttr( *pSttTextNd->GetpSwAttrSet(), nNdIdx ); + + // the length might have changed (!!Fields!!) + sal_Int32 nLen = (bOneNode + ? pEnd->nContent.GetIndex() + : pSttTextNd->GetText().getLength()) + - pStt->nContent.GetIndex(); + + // delete now also the text (all attribute changes are added to + // UNDO history) + m_aSttStr = pSttTextNd->GetText().copy(m_nSttContent, nLen); + pSttTextNd->EraseText( pStt->nContent, nLen ); + if( pSttTextNd->GetpSwpHints() ) + pSttTextNd->GetpSwpHints()->DeRegister(); + + // METADATA: store + bool emptied( !m_aSttStr->isEmpty() && !pSttTextNd->Len() ); + if (!bOneNode || emptied) // merging may overwrite xmlids... + { + m_pMetadataUndoStart = emptied + ? pSttTextNd->CreateUndoForDelete() + : pSttTextNd->CreateUndo(); + } + + if( bOneNode ) + return false; // stop moving more nodes + } + + // 2 - copy end into End-String + if( pEndTextNd ) + { + SwIndex aEndIdx( pEndTextNd ); + nNdIdx = pEnd->nNode.GetIndex(); + SwRegHistory aRHst( *pEndTextNd, m_pHistory.get() ); + + // always save all text atttibutes because of possibly overlapping + // areas of on/off + m_pHistory->CopyAttr( pEndTextNd->GetpSwpHints(), nNdIdx, 0, + pEndTextNd->GetText().getLength(), true ); + + if( pEndTextNd->HasSwAttrSet() ) + m_pHistory->CopyFormatAttr( *pEndTextNd->GetpSwAttrSet(), nNdIdx ); + + // delete now also the text (all attribute changes are added to + // UNDO history) + m_aEndStr = pEndTextNd->GetText().copy( 0, pEnd->nContent.GetIndex() ); + pEndTextNd->EraseText( aEndIdx, pEnd->nContent.GetIndex() ); + if( pEndTextNd->GetpSwpHints() ) + pEndTextNd->GetpSwpHints()->DeRegister(); + + // METADATA: store + bool emptied = !m_aEndStr->isEmpty() && !pEndTextNd->Len(); + + m_pMetadataUndoEnd = emptied + ? pEndTextNd->CreateUndoForDelete() + : pEndTextNd->CreateUndo(); + } + + // if there are only two Nodes then we're done + if( ( pSttTextNd || pEndTextNd ) && m_nSttNode + 1 == m_nEndNode ) + return false; // do not move any Node + + return true; // move Nodes lying in between +} + +bool SwUndoDelete::CanGrouping( SwDoc* pDoc, const SwPaM& rDelPam ) +{ + // Is Undo greater than one Node (that is Start and EndString)? + if( !m_aSttStr || m_aSttStr->isEmpty() || m_aEndStr ) + return false; + + // only the deletion of single char's can be condensed + if( m_nSttNode != m_nEndNode || ( !m_bGroup && m_nSttContent+1 != m_nEndContent )) + return false; + + const SwPosition *pStt = rDelPam.Start(), + *pEnd = rDelPam.GetPoint() == pStt + ? rDelPam.GetMark() + : rDelPam.GetPoint(); + + if( pStt->nNode != pEnd->nNode || + pStt->nContent.GetIndex()+1 != pEnd->nContent.GetIndex() || + pEnd->nNode != m_nSttNode ) + return false; + + // Distinguish between BackSpace and Delete because the Undo array needs to + // be constructed differently! + if( pEnd->nContent == m_nSttContent ) + { + if( m_bGroup && !m_bBackSp ) return false; + m_bBackSp = true; + } + else if( pStt->nContent == m_nSttContent ) + { + if( m_bGroup && m_bBackSp ) return false; + m_bBackSp = false; + } + else + return false; + + // are both Nodes (Node/Undo array) TextNodes at all? + SwTextNode * pDelTextNd = pStt->nNode.GetNode().GetTextNode(); + if( !pDelTextNd ) return false; + + sal_Int32 nUChrPos = m_bBackSp ? 0 : m_aSttStr->getLength()-1; + sal_Unicode cDelChar = pDelTextNd->GetText()[ pStt->nContent.GetIndex() ]; + CharClass& rCC = GetAppCharClass(); + if( ( CH_TXTATR_BREAKWORD == cDelChar || CH_TXTATR_INWORD == cDelChar ) || + rCC.isLetterNumeric( OUString( cDelChar ), 0 ) != + rCC.isLetterNumeric( *m_aSttStr, nUChrPos ) ) + return false; + + // tdf#132725 - if at-char/at-para flys would be deleted, don't group! + // DelContentIndex() would be called at the wrong time here, the indexes + // in the stored SwHistoryTextFlyCnt would be wrong when Undo is invoked + if (IsFlySelectedByCursor(*pDoc, *pStt, *pEnd)) + { + return false; + } + + { + SwRedlineSaveDatas aTmpSav; + const bool bSaved = FillSaveData( rDelPam, aTmpSav, false ); + + bool bOk = ( !m_pRedlSaveData && !bSaved ) || + ( m_pRedlSaveData && bSaved && + SwUndo::CanRedlineGroup( *m_pRedlSaveData, aTmpSav, m_bBackSp )); + // aTmpSav.DeleteAndDestroyAll(); + if( !bOk ) + return false; + + pDoc->getIDocumentRedlineAccess().DeleteRedline( rDelPam, false, RedlineType::Any ); + } + + // Both 'deletes' can be consolidated, so 'move' the related character + if( m_bBackSp ) + m_nSttContent--; // BackSpace: add char to array! + else + { + m_nEndContent++; // Delete: attach char at the end + nUChrPos++; + } + m_aSttStr = m_aSttStr->replaceAt( nUChrPos, 0, OUString(cDelChar) ); + pDelTextNd->EraseText( pStt->nContent, 1 ); + + m_bGroup = true; + return true; +} + +SwUndoDelete::~SwUndoDelete() +{ + if( m_pMvStt ) // Delete also the selection from UndoNodes array + { + // Insert saves content in IconSection + m_pMvStt->GetNode().GetNodes().Delete( *m_pMvStt, m_nNode ); + m_pMvStt.reset(); + } + m_pRedlSaveData.reset(); +} + +static SwRewriter lcl_RewriterFromHistory(SwHistory & rHistory) +{ + SwRewriter aRewriter; + + bool bDone = false; + + for ( sal_uInt16 n = 0; n < rHistory.Count(); n++) + { + OUString aDescr = rHistory[n]->GetDescription(); + + if (!aDescr.isEmpty()) + { + aRewriter.AddRule(UndoArg2, aDescr); + + bDone = true; + break; + } + } + + if (! bDone) + { + aRewriter.AddRule(UndoArg2, SwResId(STR_FIELD)); + } + + return aRewriter; +} + +static bool lcl_IsSpecialCharacter(sal_Unicode nChar) +{ + switch (nChar) + { + case CH_TXTATR_BREAKWORD: + case CH_TXTATR_INWORD: + case CH_TXTATR_TAB: + case CH_TXTATR_NEWLINE: + case CH_TXT_ATR_INPUTFIELDSTART: + case CH_TXT_ATR_INPUTFIELDEND: + case CH_TXT_ATR_FORMELEMENT: + case CH_TXT_ATR_FIELDSTART: + case CH_TXT_ATR_FIELDSEP: + case CH_TXT_ATR_FIELDEND: + return true; + + default: + break; + } + + return false; +} + +static OUString lcl_DenotedPortion(const OUString& rStr, sal_Int32 nStart, sal_Int32 nEnd) +{ + OUString aResult; + + auto nCount = nEnd - nStart; + if (nCount > 0) + { + sal_Unicode cLast = rStr[nEnd - 1]; + if (lcl_IsSpecialCharacter(cLast)) + { + switch(cLast) + { + case CH_TXTATR_TAB: + aResult = SwResId(STR_UNDO_TABS, nCount); + + break; + case CH_TXTATR_NEWLINE: + aResult = SwResId(STR_UNDO_NLS, nCount); + + break; + + case CH_TXTATR_INWORD: + case CH_TXTATR_BREAKWORD: + aResult = SwRewriter::GetPlaceHolder(UndoArg2); + break; + + case CH_TXT_ATR_INPUTFIELDSTART: + case CH_TXT_ATR_INPUTFIELDEND: + case CH_TXT_ATR_FORMELEMENT: + case CH_TXT_ATR_FIELDSTART: + case CH_TXT_ATR_FIELDSEP: + case CH_TXT_ATR_FIELDEND: + break; // nothing? + + default: + assert(!"unexpected special character"); + break; + } + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, OUString::number(nCount)); + aResult = aRewriter.Apply(aResult); + } + else + { + aResult = SwResId(STR_START_QUOTE) + + rStr.copy(nStart, nCount) + + SwResId(STR_END_QUOTE); + } + } + + return aResult; +} + +OUString DenoteSpecialCharacters(const OUString & rStr) +{ + OUStringBuffer aResult; + + if (!rStr.isEmpty()) + { + bool bStart = false; + sal_Int32 nStart = 0; + sal_Unicode cLast = 0; + + for( sal_Int32 i = 0; i < rStr.getLength(); i++) + { + if (lcl_IsSpecialCharacter(rStr[i])) + { + if (cLast != rStr[i]) + bStart = true; + + } + else + { + if (lcl_IsSpecialCharacter(cLast)) + bStart = true; + } + + if (bStart) + { + aResult.append(lcl_DenotedPortion(rStr, nStart, i)); + + nStart = i; + bStart = false; + } + + cLast = rStr[i]; + } + + aResult.append(lcl_DenotedPortion(rStr, nStart, rStr.getLength())); + } + else + aResult = SwRewriter::GetPlaceHolder(UndoArg2); + + return aResult.makeStringAndClear(); +} + +SwRewriter SwUndoDelete::GetRewriter() const +{ + SwRewriter aResult; + + if (m_nNode != 0) + { + if (!m_sTableName.isEmpty()) + { + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_START_QUOTE)); + aRewriter.AddRule(UndoArg2, m_sTableName); + aRewriter.AddRule(UndoArg3, SwResId(STR_END_QUOTE)); + + OUString sTmp = aRewriter.Apply(SwResId(STR_TABLE_NAME)); + aResult.AddRule(UndoArg1, sTmp); + } + else + aResult.AddRule(UndoArg1, SwResId(STR_PARAGRAPHS)); + } + else + { + OUString aStr; + + if (m_aSttStr && m_aEndStr && m_aSttStr->isEmpty() && + m_aEndStr->isEmpty()) + { + aStr = SwResId(STR_PARAGRAPH_UNDO); + } + else + { + std::optional<OUString> aTmpStr; + if (m_aSttStr) + aTmpStr = m_aSttStr; + else if (m_aEndStr) + aTmpStr = m_aEndStr; + + if (aTmpStr) + { + aStr = DenoteSpecialCharacters(*aTmpStr); + } + else + { + aStr = SwRewriter::GetPlaceHolder(UndoArg2); + } + } + + aStr = ShortenString(aStr, nUndoStringLength, SwResId(STR_LDOTS)); + if (m_pHistory) + { + SwRewriter aRewriter = lcl_RewriterFromHistory(*m_pHistory); + aStr = aRewriter.Apply(aStr); + } + + aResult.AddRule(UndoArg1, aStr); + } + + return aResult; +} + +// Every object, anchored "AtContent" will be reanchored at rPos +static void lcl_ReAnchorAtContentFlyFrames( const SwFrameFormats& rSpzArr, SwPosition &rPos, sal_uLong nOldIdx ) +{ + if( !rSpzArr.empty() ) + { + SwFlyFrameFormat* pFormat; + const SwFormatAnchor* pAnchor; + const SwPosition* pAPos; + for( size_t n = 0; n < rSpzArr.size(); ++n ) + { + pFormat = static_cast<SwFlyFrameFormat*>(rSpzArr[n]); + pAnchor = &pFormat->GetAnchor(); + if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA) + { + pAPos = pAnchor->GetContentAnchor(); + if( pAPos && nOldIdx == pAPos->nNode.GetIndex() ) + { + SwFormatAnchor aAnch( *pAnchor ); + aAnch.SetAnchor( &rPos ); + pFormat->SetFormatAttr( aAnch ); + } + } + } + } +} + +void SwUndoDelete::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc& rDoc = rContext.GetDoc(); + + sal_uLong nCalcStt = m_nSttNode - m_nNdDiff; + + if( m_nSectDiff && m_bBackSp ) + nCalcStt += m_nSectDiff; + + SwNodeIndex aIdx(rDoc.GetNodes(), nCalcStt); + SwNode* pInsNd = &aIdx.GetNode(); + SwNode* pMovedNode = nullptr; + + { // code block so that SwPosition is detached when deleting a Node + SwPosition aPos( aIdx ); + if( !m_bDelFullPara ) + { + assert(!m_bTableDelLastNd || pInsNd->IsTextNode()); + if( pInsNd->IsTableNode() ) + { + pInsNd = rDoc.GetNodes().MakeTextNode( aIdx, + rDoc.GetDfltTextFormatColl() ); + --aIdx; + aPos.nNode = aIdx; + aPos.nContent.Assign( pInsNd->GetContentNode(), m_nSttContent ); + } + else + { + if( pInsNd->IsContentNode() ) + aPos.nContent.Assign( static_cast<SwContentNode*>(pInsNd), m_nSttContent ); + if( !m_bTableDelLastNd ) + pInsNd = nullptr; // do not delete Node! + } + } + else + pInsNd = nullptr; // do not delete Node! + + bool bNodeMove = 0 != m_nNode; + + if( m_aEndStr ) + { + // discard attributes since they all saved! + SwTextNode * pTextNd; + if (!m_bDelFullPara && aPos.nNode.GetNode().IsSectionNode()) + { // tdf#134250 section node wasn't deleted; but aPos must point to it in bNodeMove case below + assert(m_nSttContent == 0); + assert(!m_aSttStr); + pTextNd = rDoc.GetNodes()[aPos.nNode.GetIndex() + 1]->GetTextNode(); + } + else + { + pTextNd = aPos.nNode.GetNode().GetTextNode(); + } + + if( pTextNd && pTextNd->HasSwAttrSet() ) + pTextNd->ResetAllAttr(); + + if( pTextNd && pTextNd->GetpSwpHints() ) + pTextNd->ClearSwpHintsArr( true ); + + if( m_aSttStr && !m_bFromTableCopy ) + { + sal_uLong nOldIdx = aPos.nNode.GetIndex(); + rDoc.getIDocumentContentOperations().SplitNode( aPos, false ); + // After the split all objects are anchored at the first + // paragraph, but the pHistory of the fly frame formats relies + // on anchoring at the start of the selection + // => selection backwards needs a correction. + if( m_bBackSp ) + lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx); + pTextNd = aPos.nNode.GetNode().GetTextNode(); + } + assert(pTextNd); // else where does m_aEndStr come from? + if( pTextNd ) + { + OUString const ins( pTextNd->InsertText(*m_aEndStr, aPos.nContent, + SwInsertFlags::NOHINTEXPAND) ); + assert(ins.getLength() == m_aEndStr->getLength()); // must succeed + (void) ins; + // METADATA: restore + pTextNd->RestoreMetadata(m_pMetadataUndoEnd); + } + } + else if (m_aSttStr && bNodeMove && pInsNd == nullptr) + { + SwTextNode * pNd = aPos.nNode.GetNode().GetTextNode(); + if( pNd ) + { + if (m_nSttContent < pNd->GetText().getLength()) + { + sal_uLong nOldIdx = aPos.nNode.GetIndex(); + rDoc.getIDocumentContentOperations().SplitNode( aPos, false ); + if( m_bBackSp ) + lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx); + } + else + ++aPos.nNode; + } + } + if( m_nSectDiff ) + { + sal_uLong nMoveIndex = aPos.nNode.GetIndex(); + int nDiff = 0; + if( m_bJoinNext ) + { + nMoveIndex += m_nSectDiff + 1; + pMovedNode = &aPos.nNode.GetNode(); + } + else + { + nMoveIndex -= m_nSectDiff + 1; + ++nDiff; + } + SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex); + SwNodeRange aRg( aPos.nNode, 0 - nDiff, aPos.nNode, 1 - nDiff ); + --aPos.nNode; + if( !m_bJoinNext ) + pMovedNode = &aPos.nNode.GetNode(); + rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx); + ++aPos.nNode; + } + + if( bNodeMove ) + { + SwNodeRange aRange( *m_pMvStt, 0, *m_pMvStt, m_nNode ); + SwNodeIndex aCopyIndex( aPos.nNode, -1 ); + rDoc.GetUndoManager().GetUndoNodes().Copy_(aRange, aPos.nNode, + // sw_redlinehide: delay creating frames: the flags on the + // nodes aren't necessarily up-to-date, and the redlines + // from m_pRedlSaveData aren't applied yet... + false); + + if( m_nReplaceDummy ) + { + sal_uLong nMoveIndex; + if( m_bJoinNext ) + { + nMoveIndex = m_nEndNode - m_nNdDiff; + aPos.nNode = nMoveIndex + m_nReplaceDummy; + } + else + { + aPos = SwPosition( aCopyIndex ); + nMoveIndex = aPos.nNode.GetIndex() + m_nReplaceDummy + 1; + } + SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex); + SwNodeRange aRg( aPos.nNode, 0, aPos.nNode, 1 ); + pMovedNode = &aPos.nNode.GetNode(); + // tdf#131684 without deleting frames + rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx, false); + rDoc.GetNodes().Delete( aMvIdx); + } + } + + if( m_aSttStr ) + { + aPos.nNode = m_nSttNode - m_nNdDiff + ( m_bJoinNext ? 0 : m_nReplaceDummy ); + SwTextNode * pTextNd = aPos.nNode.GetNode().GetTextNode(); + // If more than a single Node got deleted, also all "Node" + // attributes were saved + if (pTextNd != nullptr) + { + if( pTextNd->HasSwAttrSet() && bNodeMove && !m_aEndStr ) + pTextNd->ResetAllAttr(); + + if( pTextNd->GetpSwpHints() ) + pTextNd->ClearSwpHintsArr( true ); + + // SectionNode mode and selection from top to bottom: + // -> in StartNode is still the rest of the Join => delete + aPos.nContent.Assign( pTextNd, m_nSttContent ); + OUString const ins( pTextNd->InsertText(*m_aSttStr, aPos.nContent, + SwInsertFlags::NOHINTEXPAND) ); + assert(ins.getLength() == m_aSttStr->getLength()); // must succeed + (void) ins; + // METADATA: restore + pTextNd->RestoreMetadata(m_pMetadataUndoStart); + } + } + + if( m_pHistory ) + { + m_pHistory->TmpRollback(&rDoc, m_nSetPos, false); + if( m_nSetPos ) // there were Footnodes/FlyFrames + { + // are there others than these ones? + if( m_nSetPos < m_pHistory->Count() ) + { + // if so save the attributes of the others + SwHistory aHstr; + aHstr.Move( 0, m_pHistory.get(), m_nSetPos ); + m_pHistory->Rollback(&rDoc); + m_pHistory->Move( 0, &aHstr ); + } + else + { + m_pHistory->Rollback(&rDoc); + m_pHistory.reset(); + } + } + } + + if( m_bResetPgDesc || m_bResetPgBrk ) + { + sal_uInt16 nStt = m_bResetPgDesc ? sal_uInt16(RES_PAGEDESC) : sal_uInt16(RES_BREAK); + sal_uInt16 nEnd = m_bResetPgBrk ? sal_uInt16(RES_BREAK) : sal_uInt16(RES_PAGEDESC); + + SwNode* pNode = rDoc.GetNodes()[ m_nEndNode + 1 ]; + if( pNode->IsContentNode() ) + static_cast<SwContentNode*>(pNode)->ResetAttr( nStt, nEnd ); + else if( pNode->IsTableNode() ) + static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->ResetFormatAttr( nStt, nEnd ); + } + } + // delete the temporarily added Node + if (pInsNd && !m_bTableDelLastNd) + { + assert(&aIdx.GetNode() == pInsNd); + rDoc.GetNodes().Delete( aIdx ); + } + if( m_pRedlSaveData ) + SetSaveData(rDoc, *m_pRedlSaveData); + + sal_uLong delFullParaEndNode(m_nEndNode); + if (m_bDelFullPara && m_pRedlSaveData) + { + SwTextNode * pFirstMergedDeletedTextNode(nullptr); + SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, *this, + *m_pRedlSaveData, pFirstMergedDeletedTextNode); + if (pNextNode) + { + bool bNonMerged(false); + std::vector<SwTextFrame*> frames; + SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNextNode); + for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + { + if (pFrame->getRootFrame()->IsHideRedlines()) + { + frames.push_back(pFrame); + } + else + { + bNonMerged = true; + } + } + for (SwTextFrame *const pFrame : frames) + { + // could either destroy the text frames, or move them... + // destroying them would have the advantage that we don't + // need special code to *exclude* pFirstMergedDeletedTextNode + // from MakeFrames for the layouts in Hide mode but not + // layouts in Show mode ... + // ... except that MakeFrames won't create them then :( + pFrame->RegisterToNode(*pFirstMergedDeletedTextNode); + assert(pFrame->GetMergedPara()); + assert(!bNonMerged); // delFullParaEndNode is such an awful hack + (void) bNonMerged; + delFullParaEndNode = pFirstMergedDeletedTextNode->GetIndex(); + } + } + } + else if (m_aSttStr && (!m_bFromTableCopy || 0 != m_nNode)) + { + // only now do we have redlines in the document again; fix up the split + // frames + SwTextNode *const pStartNode(aIdx.GetNodes()[m_nSttNode]->GetTextNode()); + assert(pStartNode); + sw::RecreateStartTextFrames(*pStartNode); + } + + // create frames after SetSaveData has recreated redlines + if (0 != m_nNode) + { + // tdf#136453 only if section nodes at the start + if (m_bBackSp && m_nReplaceDummy != 0) + { + // tdf#134252 *first* create outer section frames + // note: text node m_nSttNode currently has frame with an upper; + // there's a hack in InsertCnt_() to move it below new section frame + SwNodeIndex const start(rDoc.GetNodes(), m_nSttNode - m_nReplaceDummy); + SwNodeIndex const end(rDoc.GetNodes(), m_nSttNode); // exclude m_nSttNode + ::MakeFrames(&rDoc, start, end); + } + // tdf#121031 if the start node is a text node, it already has a frame; + // if it's a table, it does not + // tdf#109376 exception: end on non-text-node -> start node was inserted + assert(!m_bDelFullPara || (m_nSectDiff == 0)); + SwNodeIndex const start(rDoc.GetNodes(), m_nSttNode + + ((m_bDelFullPara || !rDoc.GetNodes()[m_nSttNode]->IsTextNode() || pInsNd) + ? 0 : 1)); + // don't include end node in the range: it may have been merged already + // by the start node, or it may be merged by one of the moved nodes, + // but if it isn't merged, its current frame(s) should be good... + SwNodeIndex const end(rDoc.GetNodes(), m_bDelFullPara ? delFullParaEndNode : m_nEndNode); + ::MakeFrames(&rDoc, start, end); + } + + if (pMovedNode) + { // probably better do this after creating all frames + lcl_MakeAutoFrames(*rDoc.GetSpzFrameFormats(), pMovedNode->GetIndex()); + } + + // tdf#134021 only after MakeFrames(), because it may be the only node + // that has layout frames + if (pInsNd && m_bTableDelLastNd) + { + assert(&aIdx.GetNode() == pInsNd); + SwPaM tmp(aIdx, aIdx); + rDoc.getIDocumentContentOperations().DelFullPara(tmp); + } + + AddUndoRedoPaM(rContext, true); +} + +void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam = AddUndoRedoPaM(rContext); + SwDoc& rDoc = *rPam.GetDoc(); + + if( m_pRedlSaveData ) + { + const bool bSuccess = FillSaveData(rPam, *m_pRedlSaveData); + OSL_ENSURE(bSuccess, + "SwUndoDelete::Redo: used to have redline data, but now none?"); + if (!bSuccess) + { + m_pRedlSaveData.reset(); + } + } + + if( !m_bDelFullPara ) + { + // tdf#128739 correct cursors but do not delete bookmarks yet + ::PaMCorrAbs(rPam, *rPam.End()); + SetPaM(rPam); + + if( !m_bJoinNext ) // then restore selection from bottom to top + rPam.Exchange(); + } + + if( m_pHistory ) // are the attributes saved? + { + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + SwHistory aHstr; + aHstr.Move( 0, m_pHistory.get() ); + + if( m_bDelFullPara ) + { + OSL_ENSURE( rPam.HasMark(), "PaM without Mark" ); + DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(), + DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) ); + + DelBookmarks(rPam.GetMark()->nNode, rPam.GetPoint()->nNode); + } + else + DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() ); + m_nSetPos = m_pHistory ? m_pHistory->Count() : 0; + + m_pHistory->Move( m_nSetPos, &aHstr ); + } + else + { + if( m_bDelFullPara ) + { + OSL_ENSURE( rPam.HasMark(), "PaM without Mark" ); + DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(), + DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) ); + + DelBookmarks( rPam.GetMark()->nNode, rPam.GetPoint()->nNode ); + } + else + DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() ); + m_nSetPos = m_pHistory ? m_pHistory->Count() : 0; + } + + if( !m_aSttStr && !m_aEndStr ) + { + if (m_bDelFullPara && m_pRedlSaveData) + { + DelFullParaMoveFrames(rDoc, *this, *m_pRedlSaveData); + } + + SwNodeIndex aSttIdx = ( m_bDelFullPara || m_bJoinNext ) + ? rPam.GetMark()->nNode + : rPam.GetPoint()->nNode; + SwTableNode* pTableNd = aSttIdx.GetNode().GetTableNode(); + if( pTableNd ) + { + if( m_bTableDelLastNd ) + { + // than add again a Node at the end + const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 ); + rDoc.GetNodes().MakeTextNode( aTmpIdx, + rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) ); + } + + SwContentNode* pNextNd = rDoc.GetNodes()[ + pTableNd->EndOfSectionIndex()+1 ]->GetContentNode(); + if( pNextNd ) + { + SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat(); + + const SfxPoolItem *pItem; + if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC, + false, &pItem ) ) + pNextNd->SetAttr( *pItem ); + + if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK, + false, &pItem ) ) + pNextNd->SetAttr( *pItem ); + } + pTableNd->DelFrames(); + } + else if (*rPam.GetMark() == *rPam.GetPoint()) + { // paragraph with only footnote or as-char fly, delete that + // => DelContentIndex has already deleted it! nothing to do here + assert(m_nEndNode == m_nSttNode); + return; + } + + // avoid asserts from ~SwIndexReg for deleted nodes + SwPaM aTmp(*rPam.End()); + if (!aTmp.Move(fnMoveForward, GoInNode)) + { + *aTmp.GetPoint() = *rPam.Start(); + aTmp.Move(fnMoveBackward, GoInNode); + } + assert(aTmp.GetPoint()->nNode != rPam.GetPoint()->nNode + && aTmp.GetPoint()->nNode != rPam.GetMark()->nNode); + ::PaMCorrAbs(rPam, *aTmp.GetPoint()); + + rPam.DeleteMark(); + + rDoc.GetNodes().Delete( aSttIdx, m_nEndNode - m_nSttNode ); + } + else if( m_bDelFullPara ) + { + assert(!"dead code"); + // The Pam was incremented by one at Point (== end) to provide space + // for UNDO. This now needs to be reverted! + --rPam.End()->nNode; + if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode ) + *rPam.GetMark() = *rPam.GetPoint(); + rDoc.getIDocumentContentOperations().DelFullPara( rPam ); + } + else + rDoc.getIDocumentContentOperations().DeleteAndJoin( rPam ); +} + +void SwUndoDelete::RepeatImpl(::sw::RepeatContext & rContext) +{ + // this action does not seem idempotent, + // so make sure it is only executed once on repeat + if (rContext.m_bDeleteRepeated) + return; + + SwPaM & rPam = rContext.GetRepeatPaM(); + SwDoc& rDoc = *rPam.GetDoc(); + ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); + if( !rPam.HasMark() ) + { + rPam.SetMark(); + rPam.Move( fnMoveForward, GoInContent ); + } + if( m_bDelFullPara ) + rDoc.getIDocumentContentOperations().DelFullPara( rPam ); + else + rDoc.getIDocumentContentOperations().DeleteAndJoin( rPam ); + rContext.m_bDeleteRepeated = true; +} + +void SwUndoDelete::SetTableName(const OUString & rName) +{ + m_sTableName = rName; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx new file mode 100644 index 000000000..db2fee97b --- /dev/null +++ b/sw/source/core/undo/undobj.cxx @@ -0,0 +1,1682 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <IShellCursorSupplier.hxx> +#include <txtftn.hxx> +#include <fmtanchr.hxx> +#include <ftnidx.hxx> +#include <frmfmt.hxx> +#include <doc.hxx> +#include <UndoManager.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <docary.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> +#include <ndnotxt.hxx> +#include <IMark.hxx> +#include <mvsave.hxx> +#include <redline.hxx> +#include <crossrefbookmark.hxx> +#include <strings.hrc> +#include <docsh.hxx> +#include <view.hxx> +#include <frameformats.hxx> +#include <sal/log.hxx> + +// This class saves the Pam as integers and can recompose those into a PaM +SwUndRng::SwUndRng() + : m_nSttNode( 0 ), m_nEndNode( 0 ), m_nSttContent( 0 ), m_nEndContent( 0 ) +{ +} + +SwUndRng::SwUndRng( const SwPaM& rPam ) +{ + SetValues( rPam ); +} + +void SwUndRng::SetValues( const SwPaM& rPam ) +{ + const SwPosition *pStt = rPam.Start(); + if( rPam.HasMark() ) + { + const SwPosition *pEnd = rPam.GetPoint() == pStt + ? rPam.GetMark() + : rPam.GetPoint(); + m_nEndNode = pEnd->nNode.GetIndex(); + m_nEndContent = pEnd->nContent.GetIndex(); + } + else + { + // no selection !! + m_nEndNode = 0; + m_nEndContent = COMPLETE_STRING; + } + + m_nSttNode = pStt->nNode.GetIndex(); + m_nSttContent = pStt->nContent.GetIndex(); +} + +void SwUndRng::SetPaM( SwPaM & rPam, bool bCorrToContent ) const +{ + rPam.DeleteMark(); + rPam.GetPoint()->nNode = m_nSttNode; + SwNode& rNd = rPam.GetNode(); + if( rNd.IsContentNode() ) + rPam.GetPoint()->nContent.Assign( rNd.GetContentNode(), m_nSttContent ); + else if( bCorrToContent ) + rPam.Move( fnMoveForward, GoInContent ); + else + rPam.GetPoint()->nContent.Assign( nullptr, 0 ); + + if( !m_nEndNode && COMPLETE_STRING == m_nEndContent ) // no selection + return ; + + rPam.SetMark(); + if( m_nSttNode == m_nEndNode && m_nSttContent == m_nEndContent ) + return; // nothing left to do + + rPam.GetPoint()->nNode = m_nEndNode; + if( rPam.GetNode().IsContentNode() ) + rPam.GetPoint()->nContent.Assign( rPam.GetNode().GetContentNode(), m_nEndContent ); + else if( bCorrToContent ) + rPam.Move( fnMoveBackward, GoInContent ); + else + rPam.GetPoint()->nContent.Assign( nullptr, 0 ); +} + +SwPaM & SwUndRng::AddUndoRedoPaM( + ::sw::UndoRedoContext & rContext, bool const bCorrToContent) const +{ + SwPaM & rPaM( rContext.GetCursorSupplier().CreateNewShellCursor() ); + SetPaM( rPaM, bCorrToContent ); + return rPaM; +} + +void SwUndo::RemoveIdxFromSection( SwDoc& rDoc, sal_uLong nSttIdx, + const sal_uLong* pEndIdx ) +{ + SwNodeIndex aIdx( rDoc.GetNodes(), nSttIdx ); + SwNodeIndex aEndIdx( rDoc.GetNodes(), pEndIdx ? *pEndIdx + : aIdx.GetNode().EndOfSectionIndex() ); + SwPosition aPos( rDoc.GetNodes().GetEndOfPostIts() ); + SwDoc::CorrAbs( aIdx, aEndIdx, aPos, true ); +} + +void SwUndo::RemoveIdxFromRange( SwPaM& rPam, bool bMoveNext ) +{ + const SwPosition* pEnd = rPam.End(); + if( bMoveNext ) + { + if( pEnd != rPam.GetPoint() ) + rPam.Exchange(); + + SwNodeIndex aStt( rPam.GetMark()->nNode ); + SwNodeIndex aEnd( rPam.GetPoint()->nNode ); + + if( !rPam.Move( fnMoveForward ) ) + { + rPam.Exchange(); + if( !rPam.Move( fnMoveBackward ) ) + { + rPam.GetPoint()->nNode = rPam.GetDoc()->GetNodes().GetEndOfPostIts(); + rPam.GetPoint()->nContent.Assign( nullptr, 0 ); + } + } + + SwDoc::CorrAbs( aStt, aEnd, *rPam.GetPoint(), true ); + } + else + SwDoc::CorrAbs( rPam, *pEnd, true ); +} + +void SwUndo::RemoveIdxRel( sal_uLong nIdx, const SwPosition& rPos ) +{ + // Move only the Cursor. Bookmarks/TOXMarks/etc. are done by the corresponding + // JoinNext/JoinPrev + SwNodeIndex aIdx( rPos.nNode.GetNode().GetNodes(), nIdx ); + ::PaMCorrRel( aIdx, rPos ); +} + +SwUndo::SwUndo(SwUndoId const nId, const SwDoc* pDoc) + : m_nId(nId), m_nOrigRedlineFlags(RedlineFlags::NONE) + , m_nViewShellId(CreateViewShellId(pDoc)) + , m_isRepeatIgnored(false) + , m_bCacheComment(true) +{ +} + +ViewShellId SwUndo::CreateViewShellId(const SwDoc* pDoc) +{ + ViewShellId nRet(-1); + + if (const SwDocShell* pDocShell = pDoc->GetDocShell()) + { + if (const SwView* pView = pDocShell->GetView()) + nRet = pView->GetViewShellId(); + } + + return nRet; +} + +bool SwUndo::IsDelBox() const +{ + return GetId() == SwUndoId::COL_DELETE || GetId() == SwUndoId::ROW_DELETE || + GetId() == SwUndoId::TABLE_DELBOX; +} + +SwUndo::~SwUndo() +{ +} + +namespace { + +class UndoRedoRedlineGuard +{ +public: + UndoRedoRedlineGuard(::sw::UndoRedoContext const & rContext, SwUndo const & rUndo) + : m_rRedlineAccess(rContext.GetDoc().getIDocumentRedlineAccess()) + , m_eMode(m_rRedlineAccess.GetRedlineFlags()) + { + RedlineFlags const eTmpMode = rUndo.GetRedlineFlags(); + if ((RedlineFlags::ShowMask & eTmpMode) != (RedlineFlags::ShowMask & m_eMode)) + { + m_rRedlineAccess.SetRedlineFlags( eTmpMode ); + } + m_rRedlineAccess.SetRedlineFlags_intern( eTmpMode | RedlineFlags::Ignore ); + } + ~UndoRedoRedlineGuard() + { + m_rRedlineAccess.SetRedlineFlags(m_eMode); + } +private: + IDocumentRedlineAccess & m_rRedlineAccess; + RedlineFlags const m_eMode; +}; + +} + +void SwUndo::Undo() +{ + assert(false); // SwUndo::Undo(): ERROR: must call UndoWithContext instead +} + +void SwUndo::Redo() +{ + assert(false); // SwUndo::Redo(): ERROR: must call RedoWithContext instead +} + +void SwUndo::UndoWithContext(SfxUndoContext & rContext) +{ + ::sw::UndoRedoContext *const pContext( + dynamic_cast< ::sw::UndoRedoContext * >(& rContext)); + assert(pContext); + const UndoRedoRedlineGuard aUndoRedoRedlineGuard(*pContext, *this); + UndoImpl(*pContext); +} + +void SwUndo::RedoWithContext(SfxUndoContext & rContext) +{ + ::sw::UndoRedoContext *const pContext( + dynamic_cast< ::sw::UndoRedoContext * >(& rContext)); + assert(pContext); + const UndoRedoRedlineGuard aUndoRedoRedlineGuard(*pContext, *this); + RedoImpl(*pContext); +} + +void SwUndo::Repeat(SfxRepeatTarget & rContext) +{ + if (m_isRepeatIgnored) + { + return; // ignore Repeat for multi-selections + } + ::sw::RepeatContext *const pRepeatContext( + dynamic_cast< ::sw::RepeatContext * >(& rContext)); + assert(pRepeatContext); + RepeatImpl(*pRepeatContext); +} + +bool SwUndo::CanRepeat(SfxRepeatTarget & rContext) const +{ + assert(dynamic_cast< ::sw::RepeatContext * >(& rContext)); + (void)rContext; + // a MultiSelection action that doesn't do anything must still return true + return (SwUndoId::REPEAT_START <= GetId()) && (GetId() < SwUndoId::REPEAT_END); +} + +void SwUndo::RepeatImpl( ::sw::RepeatContext & ) +{ +} + +OUString GetUndoComment(SwUndoId eId) +{ + const char *pId = nullptr; + switch (eId) + { + case SwUndoId::EMPTY: + pId = STR_CANT_UNDO; + break; + case SwUndoId::START: + case SwUndoId::END: + break; + case SwUndoId::DELETE: + pId = STR_DELETE_UNDO; + break; + case SwUndoId::INSERT: + pId = STR_INSERT_UNDO; + break; + case SwUndoId::OVERWRITE: + pId = STR_OVR_UNDO; + break; + case SwUndoId::SPLITNODE: + pId = STR_SPLITNODE_UNDO; + break; + case SwUndoId::INSATTR: + pId = STR_INSATTR_UNDO; + break; + case SwUndoId::SETFMTCOLL: + pId = STR_SETFMTCOLL_UNDO; + break; + case SwUndoId::RESETATTR: + pId = STR_RESET_ATTR_UNDO; + break; + case SwUndoId::INSFMTATTR: + pId = STR_INSFMT_ATTR_UNDO; + break; + case SwUndoId::INSDOKUMENT: + pId = STR_INSERT_DOC_UNDO; + break; + case SwUndoId::COPY: + pId = STR_COPY_UNDO; + break; + case SwUndoId::INSTABLE: + pId = STR_INSTABLE_UNDO; + break; + case SwUndoId::TABLETOTEXT: + pId = STR_TABLETOTEXT_UNDO; + break; + case SwUndoId::TEXTTOTABLE: + pId = STR_TEXTTOTABLE_UNDO; + break; + case SwUndoId::SORT_TXT: + pId = STR_SORT_TXT; + break; + case SwUndoId::INSLAYFMT: + pId = STR_INSERTFLY; + break; + case SwUndoId::TABLEHEADLINE: + pId = STR_TABLEHEADLINE; + break; + case SwUndoId::INSSECTION: + pId = STR_INSERTSECTION; + break; + case SwUndoId::OUTLINE_LR: + pId = STR_OUTLINE_LR; + break; + case SwUndoId::OUTLINE_UD: + pId = STR_OUTLINE_UD; + break; + case SwUndoId::INSNUM: + pId = STR_INSNUM; + break; + case SwUndoId::NUMUP: + pId = STR_NUMUP; + break; + case SwUndoId::MOVENUM: + pId = STR_MOVENUM; + break; + case SwUndoId::INSDRAWFMT: + pId = STR_INSERTDRAW; + break; + case SwUndoId::NUMORNONUM: + pId = STR_NUMORNONUM; + break; + case SwUndoId::INC_LEFTMARGIN: + pId = STR_INC_LEFTMARGIN; + break; + case SwUndoId::DEC_LEFTMARGIN: + pId = STR_DEC_LEFTMARGIN; + break; + case SwUndoId::INSERTLABEL: + pId = STR_INSERTLABEL; + break; + case SwUndoId::SETNUMRULESTART: + pId = STR_SETNUMRULESTART; + break; + case SwUndoId::CHGFTN: + pId = STR_CHANGEFTN; + break; + case SwUndoId::REDLINE: + SAL_INFO("sw.core", "Should NEVER be used/translated"); + return "$1"; + case SwUndoId::ACCEPT_REDLINE: + pId = STR_ACCEPT_REDLINE; + break; + case SwUndoId::REJECT_REDLINE: + pId = STR_REJECT_REDLINE; + break; + case SwUndoId::SPLIT_TABLE: + pId = STR_SPLIT_TABLE; + break; + case SwUndoId::DONTEXPAND: + pId = STR_DONTEXPAND; + break; + case SwUndoId::AUTOCORRECT: + pId = STR_AUTOCORRECT; + break; + case SwUndoId::MERGE_TABLE: + pId = STR_MERGE_TABLE; + break; + case SwUndoId::TRANSLITERATE: + pId = STR_TRANSLITERATE; + break; + case SwUndoId::PASTE_CLIPBOARD: + pId = STR_PASTE_CLIPBOARD_UNDO; + break; + case SwUndoId::TYPING: + pId = STR_TYPING_UNDO; + break; + case SwUndoId::MOVE: + pId = STR_MOVE_UNDO; + break; + case SwUndoId::INSGLOSSARY: + pId = STR_INSERT_GLOSSARY; + break; + case SwUndoId::DELBOOKMARK: + pId = STR_DELBOOKMARK; + break; + case SwUndoId::INSBOOKMARK: + pId = STR_INSBOOKMARK; + break; + case SwUndoId::SORT_TBL: + pId = STR_SORT_TBL; + break; + case SwUndoId::DELLAYFMT: + pId = STR_DELETEFLY; + break; + case SwUndoId::AUTOFORMAT: + pId = STR_AUTOFORMAT; + break; + case SwUndoId::REPLACE: + pId = STR_REPLACE; + break; + case SwUndoId::DELSECTION: + pId = STR_DELETESECTION; + break; + case SwUndoId::CHGSECTION: + pId = STR_CHANGESECTION; + break; + case SwUndoId::SETDEFTATTR: + pId = STR_CHANGEDEFATTR; + break; + case SwUndoId::DELNUM: + pId = STR_DELNUM; + break; + case SwUndoId::DRAWUNDO: + pId = STR_DRAWUNDO; + break; + case SwUndoId::DRAWGROUP: + pId = STR_DRAWGROUP; + break; + case SwUndoId::DRAWUNGROUP: + pId = STR_DRAWUNGROUP; + break; + case SwUndoId::DRAWDELETE: + pId = STR_DRAWDELETE; + break; + case SwUndoId::REREAD: + pId = STR_REREAD; + break; + case SwUndoId::DELGRF: + pId = STR_DELGRF; + break; + case SwUndoId::TABLE_ATTR: + pId = STR_TABLE_ATTR; + break; + case SwUndoId::TABLE_AUTOFMT: + pId = STR_UNDO_TABLE_AUTOFMT; + break; + case SwUndoId::TABLE_INSCOL: + pId = STR_UNDO_TABLE_INSCOL; + break; + case SwUndoId::TABLE_INSROW: + pId = STR_UNDO_TABLE_INSROW; + break; + case SwUndoId::TABLE_DELBOX: + pId = STR_UNDO_TABLE_DELBOX; + break; + case SwUndoId::TABLE_SPLIT: + pId = STR_UNDO_TABLE_SPLIT; + break; + case SwUndoId::TABLE_MERGE: + pId = STR_UNDO_TABLE_MERGE; + break; + case SwUndoId::TBLNUMFMT: + pId = STR_TABLE_NUMFORMAT; + break; + case SwUndoId::INSTOX: + pId = STR_INSERT_TOX; + break; + case SwUndoId::CLEARTOXRANGE: + pId = STR_CLEAR_TOX_RANGE; + break; + case SwUndoId::TBLCPYTBL: + pId = STR_TABLE_TBLCPYTBL; + break; + case SwUndoId::CPYTBL: + pId = STR_TABLE_CPYTBL; + break; + case SwUndoId::INS_FROM_SHADOWCRSR: + pId = STR_INS_FROM_SHADOWCRSR; + break; + case SwUndoId::CHAINE: + pId = STR_UNDO_CHAIN; + break; + case SwUndoId::UNCHAIN: + pId = STR_UNDO_UNCHAIN; + break; + case SwUndoId::FTNINFO: + pId = STR_UNDO_FTNINFO; + break; + case SwUndoId::COMPAREDOC: + pId = STR_UNDO_COMPAREDOC; + break; + case SwUndoId::SETFLYFRMFMT: + pId = STR_UNDO_SETFLYFRMFMT; + break; + case SwUndoId::SETRUBYATTR: + pId = STR_UNDO_SETRUBYATTR; + break; + case SwUndoId::TOXCHANGE: + pId = STR_TOXCHANGE; + break; + case SwUndoId::CREATE_PAGEDESC: + pId = STR_UNDO_PAGEDESC_CREATE; + break; + case SwUndoId::CHANGE_PAGEDESC: + pId = STR_UNDO_PAGEDESC; + break; + case SwUndoId::DELETE_PAGEDESC: + pId = STR_UNDO_PAGEDESC_DELETE; + break; + case SwUndoId::HEADER_FOOTER: + pId = STR_UNDO_HEADER_FOOTER; + break; + case SwUndoId::FIELD: + pId = STR_UNDO_FIELD; + break; + case SwUndoId::TXTFMTCOL_CREATE: + pId = STR_UNDO_TXTFMTCOL_CREATE; + break; + case SwUndoId::TXTFMTCOL_DELETE: + pId = STR_UNDO_TXTFMTCOL_DELETE; + break; + case SwUndoId::TXTFMTCOL_RENAME: + pId = STR_UNDO_TXTFMTCOL_RENAME; + break; + case SwUndoId::CHARFMT_CREATE: + pId = STR_UNDO_CHARFMT_CREATE; + break; + case SwUndoId::CHARFMT_DELETE: + pId = STR_UNDO_CHARFMT_DELETE; + break; + case SwUndoId::CHARFMT_RENAME: + pId = STR_UNDO_CHARFMT_RENAME; + break; + case SwUndoId::FRMFMT_CREATE: + pId = STR_UNDO_FRMFMT_CREATE; + break; + case SwUndoId::FRMFMT_DELETE: + pId = STR_UNDO_FRMFMT_DELETE; + break; + case SwUndoId::FRMFMT_RENAME: + pId = STR_UNDO_FRMFMT_RENAME; + break; + case SwUndoId::NUMRULE_CREATE: + pId = STR_UNDO_NUMRULE_CREATE; + break; + case SwUndoId::NUMRULE_DELETE: + pId = STR_UNDO_NUMRULE_DELETE; + break; + case SwUndoId::NUMRULE_RENAME: + pId = STR_UNDO_NUMRULE_RENAME; + break; + case SwUndoId::BOOKMARK_RENAME: + pId = STR_UNDO_BOOKMARK_RENAME; + break; + case SwUndoId::INDEX_ENTRY_INSERT: + pId = STR_UNDO_INDEX_ENTRY_INSERT; + break; + case SwUndoId::INDEX_ENTRY_DELETE: + pId = STR_UNDO_INDEX_ENTRY_DELETE; + break; + case SwUndoId::COL_DELETE: + pId = STR_UNDO_COL_DELETE; + break; + case SwUndoId::ROW_DELETE: + pId = STR_UNDO_ROW_DELETE; + break; + case SwUndoId::RENAME_PAGEDESC: + pId = STR_UNDO_PAGEDESC_RENAME; + break; + case SwUndoId::NUMDOWN: + pId = STR_NUMDOWN; + break; + case SwUndoId::FLYFRMFMT_TITLE: + pId = STR_UNDO_FLYFRMFMT_TITLE; + break; + case SwUndoId::FLYFRMFMT_DESCRIPTION: + pId = STR_UNDO_FLYFRMFMT_DESCRIPTION; + break; + case SwUndoId::TBLSTYLE_CREATE: + pId = STR_UNDO_TBLSTYLE_CREATE; + break; + case SwUndoId::TBLSTYLE_DELETE: + pId = STR_UNDO_TBLSTYLE_DELETE; + break; + case SwUndoId::TBLSTYLE_UPDATE: + pId = STR_UNDO_TBLSTYLE_UPDATE; + break; + case SwUndoId::UI_REPLACE: + pId = STR_REPLACE_UNDO; + break; + case SwUndoId::UI_INSERT_PAGE_BREAK: + pId = STR_INSERT_PAGE_BREAK_UNDO; + break; + case SwUndoId::UI_INSERT_COLUMN_BREAK: + pId = STR_INSERT_COLUMN_BREAK_UNDO; + break; + case SwUndoId::UI_INSERT_ENVELOPE: + pId = STR_INSERT_ENV_UNDO; + break; + case SwUndoId::UI_DRAG_AND_COPY: + pId = STR_DRAG_AND_COPY; + break; + case SwUndoId::UI_DRAG_AND_MOVE: + pId = STR_DRAG_AND_MOVE; + break; + case SwUndoId::UI_INSERT_CHART: + pId = STR_INSERT_CHART; + break; + case SwUndoId::UI_INSERT_FOOTNOTE: + pId = STR_INSERT_FOOTNOTE; + break; + case SwUndoId::UI_INSERT_URLBTN: + pId = STR_INSERT_URLBTN; + break; + case SwUndoId::UI_INSERT_URLTXT: + pId = STR_INSERT_URLTXT; + break; + case SwUndoId::UI_DELETE_INVISIBLECNTNT: + pId = STR_DELETE_INVISIBLECNTNT; + break; + case SwUndoId::UI_REPLACE_STYLE: + pId = STR_REPLACE_STYLE; + break; + case SwUndoId::UI_DELETE_PAGE_BREAK: + pId = STR_DELETE_PAGE_BREAK; + break; + case SwUndoId::UI_TEXT_CORRECTION: + pId = STR_TEXT_CORRECTION; + break; + case SwUndoId::UI_TABLE_DELETE: + pId = STR_UNDO_TABLE_DELETE; + break; + case SwUndoId::CONFLICT: + break; + case SwUndoId::PARA_SIGN_ADD: + pId = STR_PARAGRAPH_SIGN_UNDO; + break; + case SwUndoId::INSERT_FORM_FIELD: + pId = STR_UNDO_INSERT_FORM_FIELD; + break; + } + + assert(pId); + return SwResId(pId); +} + +OUString SwUndo::GetComment() const +{ + OUString aResult; + + if (m_bCacheComment) + { + if (! maComment) + { + maComment = GetUndoComment(GetId()); + + SwRewriter aRewriter = GetRewriter(); + + maComment = aRewriter.Apply(*maComment); + } + + aResult = *maComment; + } + else + { + aResult = GetUndoComment(GetId()); + + SwRewriter aRewriter = GetRewriter(); + + aResult = aRewriter.Apply(aResult); + } + + return aResult; +} + +ViewShellId SwUndo::GetViewShellId() const +{ + return m_nViewShellId; +} + +SwRewriter SwUndo::GetRewriter() const +{ + SwRewriter aResult; + + return aResult; +} + +SwUndoSaveContent::SwUndoSaveContent() +{} + +SwUndoSaveContent::~SwUndoSaveContent() COVERITY_NOEXCEPT_FALSE +{ +} + +// This is needed when deleting content. For REDO all contents will be moved +// into the UndoNodesArray. These methods always create a new node to insert +// content. As a result, the attributes will not be expanded. +// - MoveTo moves from NodesArray into UndoNodesArray +// - MoveFrom moves from UndoNodesArray into NodesArray + +// If pEndNdIdx is given, Undo/Redo calls -Ins/DelFly. In that case the whole +// section should be moved. +void SwUndoSaveContent::MoveToUndoNds( SwPaM& rPaM, SwNodeIndex* pNodeIdx, + sal_uLong* pEndNdIdx ) +{ + SwDoc& rDoc = *rPaM.GetDoc(); + ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); + + SwNoTextNode* pCpyNd = rPaM.GetNode().GetNoTextNode(); + + // here comes the actual delete (move) + SwNodes & rNds = rDoc.GetUndoManager().GetUndoNodes(); + SwPosition aPos( pEndNdIdx ? rNds.GetEndOfPostIts() + : rNds.GetEndOfExtras() ); + + const SwPosition* pStt = rPaM.Start(), *pEnd = rPaM.End(); + + sal_uLong nTmpMvNode = aPos.nNode.GetIndex(); + + if( pCpyNd || pEndNdIdx ) + { + SwNodeRange aRg( pStt->nNode, 0, pEnd->nNode, 1 ); + rDoc.GetNodes().MoveNodes( aRg, rNds, aPos.nNode, true ); + aPos.nContent = 0; + --aPos.nNode; + } + else + { + rDoc.GetNodes().MoveRange( rPaM, aPos, rNds ); + } + if( pEndNdIdx ) + *pEndNdIdx = aPos.nNode.GetIndex(); + + // old position + aPos.nNode = nTmpMvNode; + if( pNodeIdx ) + *pNodeIdx = aPos.nNode; +} + +void SwUndoSaveContent::MoveFromUndoNds( SwDoc& rDoc, sal_uLong nNodeIdx, + SwPosition& rInsPos, + const sal_uLong* pEndNdIdx, bool const bForceCreateFrames) +{ + // here comes the recovery + SwNodes & rNds = rDoc.GetUndoManager().GetUndoNodes(); + if( nNodeIdx == rNds.GetEndOfPostIts().GetIndex() ) + return; // nothing saved + + ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); + + SwPaM aPaM( rInsPos ); + if( pEndNdIdx ) // than get the section from it + aPaM.GetPoint()->nNode.Assign( rNds, *pEndNdIdx ); + else + { + aPaM.GetPoint()->nNode = rNds.GetEndOfExtras(); + GoInContent( aPaM, fnMoveBackward ); + } + + SwTextNode* pTextNd = aPaM.GetNode().GetTextNode(); + if (!pEndNdIdx && pTextNd) + { + aPaM.SetMark(); + aPaM.GetPoint()->nNode = nNodeIdx; + aPaM.GetPoint()->nContent.Assign(aPaM.GetContentNode(), 0); + + SaveRedlEndPosForRestore aRedlRest( rInsPos.nNode, rInsPos.nContent.GetIndex() ); + + rNds.MoveRange( aPaM, rInsPos, rDoc.GetNodes() ); + + // delete the last Node as well + if( !aPaM.GetPoint()->nContent.GetIndex() || + ( aPaM.GetPoint()->nNode++ && // still empty Nodes at the end? + &rNds.GetEndOfExtras() != &aPaM.GetPoint()->nNode.GetNode() )) + { + aPaM.GetPoint()->nContent.Assign( nullptr, 0 ); + aPaM.SetMark(); + rNds.Delete( aPaM.GetPoint()->nNode, + rNds.GetEndOfExtras().GetIndex() - + aPaM.GetPoint()->nNode.GetIndex() ); + } + + aRedlRest.Restore(); + } + else + { + SwNodeRange aRg( rNds, nNodeIdx, rNds, (pEndNdIdx + ? ((*pEndNdIdx) + 1) + : rNds.GetEndOfExtras().GetIndex() ) ); + rNds.MoveNodes(aRg, rDoc.GetNodes(), rInsPos.nNode, nullptr == pEndNdIdx || bForceCreateFrames); + + } +} + +// These two methods move the Point of Pam backwards/forwards. With that, one +// can span an area for a Undo/Redo. (The Point is then positioned in front of +// the area to manipulate!) +// The flag indicates if there is still content in front of Point. +bool SwUndoSaveContent::MovePtBackward( SwPaM& rPam ) +{ + rPam.SetMark(); + if( rPam.Move( fnMoveBackward )) + return true; + + // If there is no content onwards, set Point simply to the previous position + // (Node and Content, so that Content will be detached!) + --rPam.GetPoint()->nNode; + rPam.GetPoint()->nContent.Assign( nullptr, 0 ); + return false; +} + +void SwUndoSaveContent::MovePtForward( SwPaM& rPam, bool bMvBkwrd ) +{ + // Was there content before this position? + if( bMvBkwrd ) + rPam.Move( fnMoveForward ); + else + { + ++rPam.GetPoint()->nNode; + SwContentNode* pCNd = rPam.GetContentNode(); + if( pCNd ) + pCNd->MakeStartIndex( &rPam.GetPoint()->nContent ); + else + rPam.Move( fnMoveForward ); + } +} + +// Delete all objects that have ContentIndices to the given area. +// Currently (1994) these exist: +// - Footnotes +// - Flys +// - Bookmarks + +// #i81002# - extending method +// delete certain (not all) cross-reference bookmarks at text node of <rMark> +// and at text node of <rPoint>, if these text nodes aren't the same. +void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, + const SwPosition& rPoint, + DelContentType nDelContentType ) +{ + const SwPosition *pStt = rMark < rPoint ? &rMark : &rPoint, + *pEnd = &rMark == pStt ? &rPoint : &rMark; + + SwDoc* pDoc = rMark.nNode.GetNode().GetDoc(); + + // if it's not in the doc array, probably missing some invalidation somewhere + assert(&rPoint.nNode.GetNodes() == &pDoc->GetNodes()); + assert(&rMark.nNode.GetNodes() == &pDoc->GetNodes()); + + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + // 1. Footnotes + if( DelContentType::Ftn & nDelContentType ) + { + SwFootnoteIdxs& rFootnoteArr = pDoc->GetFootnoteIdxs(); + if( !rFootnoteArr.empty() ) + { + const SwNode* pFootnoteNd; + size_t nPos = 0; + rFootnoteArr.SeekEntry( pStt->nNode, &nPos ); + SwTextFootnote* pSrch; + + // for now delete all that come afterwards + while( nPos < rFootnoteArr.size() && ( pFootnoteNd = + &( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex() + <= pEnd->nNode.GetIndex() ) + { + const sal_Int32 nFootnoteSttIdx = pSrch->GetStart(); + if( (DelContentType::CheckNoCntnt & nDelContentType ) + ? (&pEnd->nNode.GetNode() == pFootnoteNd ) + : (( &pStt->nNode.GetNode() == pFootnoteNd && + pStt->nContent.GetIndex() > nFootnoteSttIdx) || + ( &pEnd->nNode.GetNode() == pFootnoteNd && + nFootnoteSttIdx >= pEnd->nContent.GetIndex() )) ) + { + ++nPos; // continue searching + continue; + } + +// FIXME: duplicated code here and below -> refactor? + // Unfortunately an index needs to be created. Otherwise there + // will be problems with TextNode because the index will be + // deleted in the DTOR of SwFootnote! + SwTextNode* pTextNd = const_cast<SwTextNode*>(static_cast<const SwTextNode*>(pFootnoteNd)); + if( !m_pHistory ) + m_pHistory.reset( new SwHistory ); + SwTextAttr* const pFootnoteHint = + pTextNd->GetTextAttrForCharAt( nFootnoteSttIdx ); + assert(pFootnoteHint); + SwIndex aIdx( pTextNd, nFootnoteSttIdx ); + m_pHistory->Add( pFootnoteHint, pTextNd->GetIndex(), false ); + pTextNd->EraseText( aIdx, 1 ); + } + + while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )-> + GetTextNode())->GetIndex() >= pStt->nNode.GetIndex() ) + { + const sal_Int32 nFootnoteSttIdx = pSrch->GetStart(); + if( !(DelContentType::CheckNoCntnt & nDelContentType) && ( + ( &pStt->nNode.GetNode() == pFootnoteNd && + pStt->nContent.GetIndex() > nFootnoteSttIdx ) || + ( &pEnd->nNode.GetNode() == pFootnoteNd && + nFootnoteSttIdx >= pEnd->nContent.GetIndex() ))) + continue; // continue searching + + // Unfortunately an index needs to be created. Otherwise there + // will be problems with TextNode because the index will be + // deleted in the DTOR of SwFootnote! + SwTextNode* pTextNd = const_cast<SwTextNode*>(static_cast<const SwTextNode*>(pFootnoteNd)); + if( !m_pHistory ) + m_pHistory.reset( new SwHistory ); + SwTextAttr* const pFootnoteHint = + pTextNd->GetTextAttrForCharAt( nFootnoteSttIdx ); + assert(pFootnoteHint); + SwIndex aIdx( pTextNd, nFootnoteSttIdx ); + m_pHistory->Add( pFootnoteHint, pTextNd->GetIndex(), false ); + pTextNd->EraseText( aIdx, 1 ); + } + } + } + + // 2. Flys + if( DelContentType::Fly & nDelContentType ) + { + sal_uInt16 nChainInsPos = m_pHistory ? m_pHistory->Count() : 0; + const SwFrameFormats& rSpzArr = *pDoc->GetSpzFrameFormats(); + if( !rSpzArr.empty() ) + { + SwFrameFormat* pFormat; + const SwFormatAnchor* pAnchor; + size_t n = rSpzArr.size(); + const SwPosition* pAPos; + + while( n && !rSpzArr.empty() ) + { + pFormat = rSpzArr[--n]; + pAnchor = &pFormat->GetAnchor(); + switch( pAnchor->GetAnchorId() ) + { + case RndStdIds::FLY_AS_CHAR: + if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) && + (( DelContentType::CheckNoCntnt & nDelContentType ) + ? ( pStt->nNode <= pAPos->nNode && + pAPos->nNode < pEnd->nNode ) + : ( *pStt <= *pAPos && *pAPos < *pEnd )) ) + { + if( !m_pHistory ) + m_pHistory.reset( new SwHistory ); + SwTextNode *const pTextNd = + pAPos->nNode.GetNode().GetTextNode(); + SwTextAttr* const pFlyHint = pTextNd->GetTextAttrForCharAt( + pAPos->nContent.GetIndex()); + assert(pFlyHint); + m_pHistory->Add( pFlyHint, 0, false ); + // reset n so that no Format is skipped + n = n >= rSpzArr.size() ? rSpzArr.size() : n+1; + } + break; + case RndStdIds::FLY_AT_PARA: + { + pAPos = pAnchor->GetContentAnchor(); + if (pAPos && + pStt->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode) + { + if (!m_pHistory) + m_pHistory.reset( new SwHistory ); + + if (!(DelContentType::Replace & nDelContentType) + && IsSelectFrameAnchoredAtPara(*pAPos, *pStt, *pEnd, nDelContentType)) + { + m_pHistory->AddDeleteFly(*pFormat, nChainInsPos); + // reset n so that no Format is skipped + n = n >= rSpzArr.size() ? rSpzArr.size() : n+1; + } + // Moving the anchor? + else if (!((DelContentType::CheckNoCntnt|DelContentType::ExcludeFlyAtStartEnd) + & nDelContentType) && + // at least for calls from SwUndoDelete, + // this should work - other Undos don't + // remember the order of the cursor + (rPoint.nNode.GetIndex() == pAPos->nNode.GetIndex()) + // Do not try to move the anchor to a table! + && rMark.nNode.GetNode().IsTextNode()) + { + m_pHistory->AddChangeFlyAnchor(*pFormat); + SwFormatAnchor aAnch( *pAnchor ); + SwPosition aPos( rMark.nNode ); + aAnch.SetAnchor( &aPos ); + pFormat->SetFormatAttr( aAnch ); + } + } + } + break; + case RndStdIds::FLY_AT_CHAR: + if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) && + ( pStt->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode ) ) + { + if( !m_pHistory ) + m_pHistory.reset( new SwHistory ); + if (!(DelContentType::Replace & nDelContentType) + && IsDestroyFrameAnchoredAtChar( + *pAPos, *pStt, *pEnd, nDelContentType)) + { + m_pHistory->AddDeleteFly(*pFormat, nChainInsPos); + n = n >= rSpzArr.size() ? rSpzArr.size() : n+1; + } + else if (!((DelContentType::CheckNoCntnt | + DelContentType::ExcludeFlyAtStartEnd) + & nDelContentType)) + { + if( *pStt <= *pAPos && *pAPos < *pEnd ) + { + // These are the objects anchored + // between section start and end position + // Do not try to move the anchor to a table! + if( rMark.nNode.GetNode().GetTextNode() ) + { + m_pHistory->AddChangeFlyAnchor(*pFormat); + SwFormatAnchor aAnch( *pAnchor ); + aAnch.SetAnchor( &rMark ); + pFormat->SetFormatAttr( aAnch ); + } + } + } + } + break; + case RndStdIds::FLY_AT_FLY: + + if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) && + pStt->nNode == pAPos->nNode ) + { + if( !m_pHistory ) + m_pHistory.reset( new SwHistory ); + + m_pHistory->AddDeleteFly(*pFormat, nChainInsPos); + + // reset n so that no Format is skipped + n = n >= rSpzArr.size() ? rSpzArr.size() : n+1; + } + break; + default: break; + } + } + } + } + + // 3. Bookmarks + if( DelContentType::Bkm & nDelContentType ) + { + IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess(); + if( pMarkAccess->getAllMarksCount() ) + { + for( sal_Int32 n = 0; n < pMarkAccess->getAllMarksCount(); ++n ) + { + // #i81002# + bool bSavePos = false; + bool bSaveOtherPos = false; + const ::sw::mark::IMark *const pBkmk = pMarkAccess->getAllMarksBegin()[n]; + auto const type(IDocumentMarkAccess::GetType(*pBkmk)); + + if( DelContentType::CheckNoCntnt & nDelContentType ) + { + if ( pStt->nNode <= pBkmk->GetMarkPos().nNode + && pBkmk->GetMarkPos().nNode < pEnd->nNode ) + { + bSavePos = true; + } + if ( pBkmk->IsExpanded() + && pStt->nNode <= pBkmk->GetOtherMarkPos().nNode + && pBkmk->GetOtherMarkPos().nNode < pEnd->nNode ) + { + bSaveOtherPos = true; + } + } + else + { + // #i92125# + // keep cross-reference bookmarks, if content inside one paragraph is deleted. + if ( rMark.nNode == rPoint.nNode + && ( type == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK + || type == IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK)) + { + continue; + } + + bool bMaybe = false; + if ( *pStt <= pBkmk->GetMarkPos() && pBkmk->GetMarkPos() <= *pEnd ) + { + if ( pBkmk->GetMarkPos() == *pEnd + || ( *pStt == pBkmk->GetMarkPos() && pBkmk->IsExpanded() ) ) + bMaybe = true; + else + bSavePos = true; + } + if( pBkmk->IsExpanded() && + *pStt <= pBkmk->GetOtherMarkPos() && pBkmk->GetOtherMarkPos() <= *pEnd ) + { + assert(!bSaveOtherPos); + if ( bSavePos + || (*pStt < pBkmk->GetOtherMarkPos() && pBkmk->GetOtherMarkPos() < *pEnd) + || (bMaybe + && ( type == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK + || type == IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK + || type == IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK + || type == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))) + { + if( bMaybe ) + bSavePos = true; + bSaveOtherPos = true; + } + } + + if ( !bSavePos && !bSaveOtherPos + && dynamic_cast< const ::sw::mark::CrossRefBookmark* >(pBkmk) ) + { + // certain special handling for cross-reference bookmarks + const bool bDifferentTextNodesAtMarkAndPoint = + rMark.nNode != rPoint.nNode + && rMark.nNode.GetNode().GetTextNode() + && rPoint.nNode.GetNode().GetTextNode(); + if ( bDifferentTextNodesAtMarkAndPoint ) + { + // delete cross-reference bookmark at <pStt>, if only part of + // <pEnd> text node content is deleted. + if( pStt->nNode == pBkmk->GetMarkPos().nNode + && pEnd->nContent.GetIndex() != pEnd->nNode.GetNode().GetTextNode()->Len() ) + { + bSavePos = true; + bSaveOtherPos = false; // cross-reference bookmarks are not expanded + } + // delete cross-reference bookmark at <pEnd>, if only part of + // <pStt> text node content is deleted. + else if( pEnd->nNode == pBkmk->GetMarkPos().nNode && + pStt->nContent.GetIndex() != 0 ) + { + bSavePos = true; + bSaveOtherPos = false; // cross-reference bookmarks are not expanded + } + } + } + else if (type == IDocumentMarkAccess::MarkType::ANNOTATIONMARK) + { + // delete annotation marks, if its end position is covered by the deletion + const SwPosition& rAnnotationEndPos = pBkmk->GetMarkEnd(); + if ( *pStt < rAnnotationEndPos && rAnnotationEndPos <= *pEnd ) + { + bSavePos = true; + bSaveOtherPos = pBkmk->IsExpanded(); //tdf#90138, only save the other pos if there is one + } + } + } + + if ( bSavePos || bSaveOtherPos ) + { + if (type != IDocumentMarkAccess::MarkType::UNO_BOOKMARK) + { + if( !m_pHistory ) + m_pHistory.reset( new SwHistory ); + m_pHistory->Add( *pBkmk, bSavePos, bSaveOtherPos ); + } + if ( bSavePos + && ( bSaveOtherPos + || !pBkmk->IsExpanded() ) ) + { + pMarkAccess->deleteMark(pMarkAccess->getAllMarksBegin()+n); + n--; + } + } + } + } + } +} + +// save a complete section into UndoNodes array +SwUndoSaveSection::SwUndoSaveSection() + : m_nMoveLen( 0 ), m_nStartPos( ULONG_MAX ) +{ +} + +SwUndoSaveSection::~SwUndoSaveSection() +{ + if (m_pMovedStart) // delete also the section from UndoNodes array + { + // SaveSection saves the content in the PostIt section. + SwNodes& rUNds = m_pMovedStart->GetNode().GetNodes(); + rUNds.Delete( *m_pMovedStart, m_nMoveLen ); + + m_pMovedStart.reset(); + } + m_pRedlineSaveData.reset(); +} + +void SwUndoSaveSection::SaveSection( const SwNodeIndex& rSttIdx ) +{ + SwNodeRange aRg( rSttIdx.GetNode(), *rSttIdx.GetNode().EndOfSectionNode() ); + SaveSection( aRg ); +} + +void SwUndoSaveSection::SaveSection( + const SwNodeRange& rRange, bool const bExpandNodes) +{ + SwPaM aPam( rRange.aStart, rRange.aEnd ); + + // delete all footnotes, fly frames, bookmarks + DelContentIndex( *aPam.GetMark(), *aPam.GetPoint() ); + + // redlines *before* CorrAbs, because DelBookmarks will make them 0-length + // but *after* DelContentIndex because that also may use FillSaveData (in + // flys) and that will be restored *after* this one... + m_pRedlineSaveData.reset( new SwRedlineSaveDatas ); + if (!SwUndo::FillSaveData( aPam, *m_pRedlineSaveData )) + { + m_pRedlineSaveData.reset(); + } + + { + // move certain indexes out of deleted range + SwNodeIndex aSttIdx( aPam.Start()->nNode.GetNode() ); + SwNodeIndex aEndIdx( aPam.End()->nNode.GetNode() ); + SwNodeIndex aMvStt( aEndIdx, 1 ); + SwDoc::CorrAbs( aSttIdx, aEndIdx, SwPosition( aMvStt ), true ); + } + + m_nStartPos = rRange.aStart.GetIndex(); + + if (bExpandNodes) + { + --aPam.GetPoint()->nNode; + ++aPam.GetMark()->nNode; + } + + SwContentNode* pCNd = aPam.GetContentNode( false ); + if( pCNd ) + aPam.GetMark()->nContent.Assign( pCNd, 0 ); + if( nullptr != ( pCNd = aPam.GetContentNode()) ) + aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() ); + + // Keep positions as SwIndex so that this section can be deleted in DTOR + sal_uLong nEnd; + m_pMovedStart.reset(new SwNodeIndex(rRange.aStart)); + MoveToUndoNds(aPam, m_pMovedStart.get(), &nEnd); + m_nMoveLen = nEnd - m_pMovedStart->GetIndex() + 1; +} + +void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, SwNodeIndex* pIdx, + sal_uInt16 nSectType ) +{ + if( ULONG_MAX != m_nStartPos ) // was there any content? + { + // check if the content is at the old position + SwNodeIndex aSttIdx( pDoc->GetNodes(), m_nStartPos ); + + // move the content from UndoNodes array into Fly + SwStartNode* pSttNd = SwNodes::MakeEmptySection( aSttIdx, + static_cast<SwStartNodeType>(nSectType) ); + + RestoreSection( pDoc, SwNodeIndex( *pSttNd->EndOfSectionNode() )); + + if( pIdx ) + *pIdx = *pSttNd; + } +} + +void SwUndoSaveSection::RestoreSection( + SwDoc *const pDoc, const SwNodeIndex& rInsPos, bool bForceCreateFrames) +{ + if( ULONG_MAX != m_nStartPos ) // was there any content? + { + SwPosition aInsPos( rInsPos ); + sal_uLong nEnd = m_pMovedStart->GetIndex() + m_nMoveLen - 1; + MoveFromUndoNds(*pDoc, m_pMovedStart->GetIndex(), aInsPos, &nEnd, bForceCreateFrames); + + // destroy indices again, content was deleted from UndoNodes array + m_pMovedStart.reset(); + m_nMoveLen = 0; + + if( m_pRedlineSaveData ) + { + SwUndo::SetSaveData( *pDoc, *m_pRedlineSaveData ); + m_pRedlineSaveData.reset(); + } + } +} + +// save and set the RedlineData +SwRedlineSaveData::SwRedlineSaveData( + SwComparePosition eCmpPos, + const SwPosition& rSttPos, + const SwPosition& rEndPos, + SwRangeRedline& rRedl, + bool bCopyNext ) + : SwUndRng( rRedl ) + , SwRedlineData( rRedl.GetRedlineData(), bCopyNext ) +{ + assert( SwComparePosition::Outside == eCmpPos || + !rRedl.GetContentIdx() ); // "Redline with Content" + + switch (eCmpPos) + { + case SwComparePosition::OverlapBefore: // Pos1 overlaps Pos2 at the beginning + m_nEndNode = rEndPos.nNode.GetIndex(); + m_nEndContent = rEndPos.nContent.GetIndex(); + break; + + case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at the end + m_nSttNode = rSttPos.nNode.GetIndex(); + m_nSttContent = rSttPos.nContent.GetIndex(); + break; + + case SwComparePosition::Inside: // Pos1 lays completely in Pos2 + m_nSttNode = rSttPos.nNode.GetIndex(); + m_nSttContent = rSttPos.nContent.GetIndex(); + m_nEndNode = rEndPos.nNode.GetIndex(); + m_nEndContent = rEndPos.nContent.GetIndex(); + break; + + case SwComparePosition::Outside: // Pos2 lays completely in Pos1 + if ( rRedl.GetContentIdx() ) + { + // than move section into UndoArray and memorize it + SaveSection( *rRedl.GetContentIdx() ); + rRedl.SetContentIdx( nullptr ); + } + break; + + case SwComparePosition::Equal: // Pos1 is exactly as big as Pos2 + break; + + default: + assert(false); + } + +#if OSL_DEBUG_LEVEL > 0 + m_nRedlineCount = rSttPos.nNode.GetNode().GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().size(); +#endif +} + +SwRedlineSaveData::~SwRedlineSaveData() +{ +} + +void SwRedlineSaveData::RedlineToDoc( SwPaM const & rPam ) +{ + SwDoc& rDoc = *rPam.GetDoc(); + SwRangeRedline* pRedl = new SwRangeRedline( *this, rPam ); + + if( GetMvSttIdx() ) + { + SwNodeIndex aIdx( rDoc.GetNodes() ); + RestoreSection( &rDoc, &aIdx, SwNormalStartNode ); + if( GetHistory() ) + GetHistory()->Rollback( &rDoc ); + pRedl->SetContentIdx( &aIdx ); + } + SetPaM( *pRedl ); + // First, delete the "old" so that in an Append no unexpected things will + // happen, e.g. a delete in an insert. In the latter case the just restored + // content will be deleted and not the one you originally wanted. + rDoc.getIDocumentRedlineAccess().DeleteRedline( *pRedl, false, RedlineType::Any ); + + RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::DontCombineRedlines ); + //#i92154# let UI know about a new redline with comment + if (rDoc.GetDocShell() && (!pRedl->GetComment().isEmpty()) ) + rDoc.GetDocShell()->Broadcast(SwRedlineHint()); + + auto const result(rDoc.getIDocumentRedlineAccess().AppendRedline(pRedl, true)); + assert(result != IDocumentRedlineAccess::AppendResult::IGNORED); // SwRedlineSaveData::RedlineToDoc: insert redline failed + (void) result; // unused in non-debug + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); +} + +bool SwUndo::FillSaveData( + const SwPaM& rRange, + SwRedlineSaveDatas& rSData, + bool bDelRange, + bool bCopyNext ) +{ + rSData.clear(); + + const SwPosition* pStt = rRange.Start(); + const SwPosition* pEnd = rRange.End(); + const SwRedlineTable& rTable = rRange.GetDoc()->getIDocumentRedlineAccess().GetRedlineTable(); + SwRedlineTable::size_type n = 0; + rRange.GetDoc()->getIDocumentRedlineAccess().GetRedline( *pStt, &n ); + for ( ; n < rTable.size(); ++n ) + { + SwRangeRedline* pRedl = rTable[n]; + + const SwComparePosition eCmpPos = + ComparePosition( *pStt, *pEnd, *pRedl->Start(), *pRedl->End() ); + if ( eCmpPos != SwComparePosition::Before + && eCmpPos != SwComparePosition::Behind + && eCmpPos != SwComparePosition::CollideEnd + && eCmpPos != SwComparePosition::CollideStart ) + { + + rSData.push_back(std::unique_ptr<SwRedlineSaveData, o3tl::default_delete<SwRedlineSaveData>>(new SwRedlineSaveData(eCmpPos, *pStt, *pEnd, *pRedl, bCopyNext))); + } + } + if( !rSData.empty() && bDelRange ) + { + rRange.GetDoc()->getIDocumentRedlineAccess().DeleteRedline( rRange, false, RedlineType::Any ); + } + return !rSData.empty(); +} + +bool SwUndo::FillSaveDataForFormat( + const SwPaM& rRange, + SwRedlineSaveDatas& rSData ) +{ + rSData.clear(); + + const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End(); + const SwRedlineTable& rTable = rRange.GetDoc()->getIDocumentRedlineAccess().GetRedlineTable(); + SwRedlineTable::size_type n = 0; + rRange.GetDoc()->getIDocumentRedlineAccess().GetRedline( *pStt, &n ); + for ( ; n < rTable.size(); ++n ) + { + SwRangeRedline* pRedl = rTable[n]; + if ( RedlineType::Format == pRedl->GetType() ) + { + const SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRedl->Start(), *pRedl->End() ); + if ( eCmpPos != SwComparePosition::Before + && eCmpPos != SwComparePosition::Behind + && eCmpPos != SwComparePosition::CollideEnd + && eCmpPos != SwComparePosition::CollideStart ) + { + rSData.push_back(std::unique_ptr<SwRedlineSaveData, o3tl::default_delete<SwRedlineSaveData>>(new SwRedlineSaveData(eCmpPos, *pStt, *pEnd, *pRedl, true))); + } + + } + } + return !rSData.empty(); +} + + +void SwUndo::SetSaveData( SwDoc& rDoc, SwRedlineSaveDatas& rSData ) +{ + RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On ); + SwPaM aPam( rDoc.GetNodes().GetEndOfContent() ); + + for( size_t n = rSData.size(); n; ) + rSData[ --n ].RedlineToDoc( aPam ); + +#if OSL_DEBUG_LEVEL > 0 + // check redline count against count saved in RedlineSaveData object + assert(rSData.empty() || + (rSData[0].m_nRedlineCount == rDoc.getIDocumentRedlineAccess().GetRedlineTable().size())); + // "redline count not restored properly" +#endif + + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); +} + +bool SwUndo::HasHiddenRedlines( const SwRedlineSaveDatas& rSData ) +{ + for( size_t n = rSData.size(); n; ) + if( rSData[ --n ].GetMvSttIdx() ) + return true; + return false; +} + +bool SwUndo::CanRedlineGroup( SwRedlineSaveDatas& rCurr, + const SwRedlineSaveDatas& rCheck, bool bCurrIsEnd ) +{ + if( rCurr.size() != rCheck.size() ) + return false; + + for( size_t n = 0; n < rCurr.size(); ++n ) + { + const SwRedlineSaveData& rSet = rCurr[ n ]; + const SwRedlineSaveData& rGet = rCheck[ n ]; + if( rSet.m_nSttNode != rGet.m_nSttNode || + rSet.GetMvSttIdx() || rGet.GetMvSttIdx() || + ( bCurrIsEnd ? rSet.m_nSttContent != rGet.m_nEndContent + : rSet.m_nEndContent != rGet.m_nSttContent ) || + !rGet.CanCombine( rSet ) ) + { + return false; + } + } + + for( size_t n = 0; n < rCurr.size(); ++n ) + { + SwRedlineSaveData& rSet = rCurr[ n ]; + const SwRedlineSaveData& rGet = rCheck[ n ]; + if( bCurrIsEnd ) + rSet.m_nSttContent = rGet.m_nSttContent; + else + rSet.m_nEndContent = rGet.m_nEndContent; + } + return true; +} + +OUString ShortenString(const OUString & rStr, sal_Int32 nLength, const OUString & rFillStr) +{ + assert(nLength - rFillStr.getLength() >= 2); + + if (rStr.getLength() <= nLength) + return rStr; + + nLength -= rFillStr.getLength(); + if ( nLength < 2 ) + nLength = 2; + + const sal_Int32 nFrontLen = nLength - nLength / 2; + const sal_Int32 nBackLen = nLength - nFrontLen; + + return rStr.copy(0, nFrontLen) + + rFillStr + + rStr.copy(rStr.getLength() - nBackLen); +} + +static bool IsAtEndOfSection(SwPosition const& rAnchorPos) +{ + SwNodeIndex node(*rAnchorPos.nNode.GetNode().EndOfSectionNode()); + SwContentNode *const pNode(SwNodes::GoPrevious(&node)); + assert(pNode); + assert(rAnchorPos.nNode <= node); // last valid anchor pos is last content + return node == rAnchorPos.nNode + // at-para fly has no SwIndex! + && (rAnchorPos.nContent == pNode->Len() || rAnchorPos.nContent.GetIdxReg() == nullptr); +} + +static bool IsAtStartOfSection(SwPosition const& rAnchorPos) +{ + SwNodes const& rNodes(rAnchorPos.nNode.GetNodes()); + SwNodeIndex node(*rAnchorPos.nNode.GetNode().StartOfSectionNode()); + SwContentNode *const pNode(rNodes.GoNext(&node)); + assert(pNode); + (void) pNode; + assert(node <= rAnchorPos.nNode); + return node == rAnchorPos.nNode && rAnchorPos.nContent == 0; +} + +/// passed start / end position could be on section start / end node +static bool IsAtEndOfSection2(SwPosition const& rPos) +{ + return rPos.nNode.GetNode().IsEndNode() + || IsAtEndOfSection(rPos); +} + +static bool IsAtStartOfSection2(SwPosition const& rPos) +{ + return rPos.nNode.GetNode().IsStartNode() + || IsAtStartOfSection(rPos); +} + +static bool IsNotBackspaceHeuristic( + SwPosition const& rStart, SwPosition const& rEnd) +{ + // check if the selection is backspace/delete created by DelLeft/DelRight + return rStart.nNode.GetIndex() + 1 != rEnd.nNode.GetIndex() + || rEnd.nContent != 0 + || rStart.nContent != rStart.nNode.GetNode().GetTextNode()->Len(); +} + +bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos, + SwPosition const & rStart, SwPosition const & rEnd, + DelContentType const nDelContentType) +{ + assert(rStart <= rEnd); + + // CheckNoCntnt means DelFullPara which is obvious to handle + if (DelContentType::CheckNoCntnt & nDelContentType) + { // exclude selection end node because it won't be deleted + return (rAnchorPos.nNode < rEnd.nNode) + && (rStart.nNode <= rAnchorPos.nNode); + } + + if ((nDelContentType & DelContentType::WriterfilterHack) + && rAnchorPos.GetDoc()->IsInWriterfilterImport()) + { // FIXME hack for writerfilter RemoveLastParagraph() and MakeFlyAndMove(); can't test file format more specific? + return (rStart < rAnchorPos) && (rAnchorPos < rEnd); + } + + if (nDelContentType & DelContentType::ExcludeFlyAtStartEnd) + { // exclude selection start and end node + return (rAnchorPos.nNode < rEnd.nNode) + && (rStart.nNode < rAnchorPos.nNode); + } + + // in general, exclude the start and end position + return ((rStart < rAnchorPos) + || (rStart == rAnchorPos + // special case: fully deleted node + && ((rStart.nNode != rEnd.nNode && rStart.nContent == 0 + // but not if the selection is backspace/delete! + && IsNotBackspaceHeuristic(rStart, rEnd)) + || (IsAtStartOfSection(rAnchorPos) && IsAtEndOfSection2(rEnd))))) + && ((rAnchorPos < rEnd) + || (rAnchorPos == rEnd + // special case: fully deleted node + && ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len() + && IsNotBackspaceHeuristic(rStart, rEnd)) + || (IsAtEndOfSection(rAnchorPos) && IsAtStartOfSection2(rStart))))); +} + +bool IsSelectFrameAnchoredAtPara(SwPosition const & rAnchorPos, + SwPosition const & rStart, SwPosition const & rEnd, + DelContentType const nDelContentType) +{ + assert(rStart <= rEnd); + + // CheckNoCntnt means DelFullPara which is obvious to handle + if (DelContentType::CheckNoCntnt & nDelContentType) + { // exclude selection end node because it won't be deleted + return (rAnchorPos.nNode < rEnd.nNode) + && (rStart.nNode <= rAnchorPos.nNode); + } + + if ((nDelContentType & DelContentType::WriterfilterHack) + && rAnchorPos.GetDoc()->IsInWriterfilterImport()) + { // FIXME hack for writerfilter RemoveLastParagraph() and MakeFlyAndMove(); can't test file format more specific? + // but it MUST NOT be done during the SetRedlineFlags at the end of ODF + // import, where the IsInXMLImport() cannot be checked because the + // stupid code temp. overrides it - instead rely on setting the ALLFLYS + // flag in MoveFromSection() and converting that to CheckNoCntnt with + // adjusted cursor! + return (rStart.nNode < rAnchorPos.nNode) && (rAnchorPos.nNode < rEnd.nNode); + } + + // in general, exclude the start and end position + return ((rStart.nNode < rAnchorPos.nNode) + || (rStart.nNode == rAnchorPos.nNode + && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd) + // special case: fully deleted node + && ((rStart.nNode != rEnd.nNode && rStart.nContent == 0 + // but not if the selection is backspace/delete! + && IsNotBackspaceHeuristic(rStart, rEnd)) + || (IsAtStartOfSection2(rStart) && IsAtEndOfSection2(rEnd))))) + && ((rAnchorPos.nNode < rEnd.nNode) + || (rAnchorPos.nNode == rEnd.nNode + && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd) + // special case: fully deleted node + && ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len() + && IsNotBackspaceHeuristic(rStart, rEnd)) + || (IsAtEndOfSection2(rEnd) && IsAtStartOfSection2(rStart))))); +} + +bool IsFlySelectedByCursor(SwDoc const & rDoc, + SwPosition const & rStart, SwPosition const & rEnd) +{ + for (SwFrameFormat const*const pFly : *rDoc.GetSpzFrameFormats()) + { + SwFormatAnchor const& rAnchor(pFly->GetAnchor()); + switch (rAnchor.GetAnchorId()) + { + case RndStdIds::FLY_AT_CHAR: + case RndStdIds::FLY_AT_PARA: + { + SwPosition const*const pAnchorPos(rAnchor.GetContentAnchor()); + // can this really be null? + if (pAnchorPos != nullptr + && ((rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) + ? IsDestroyFrameAnchoredAtChar(*pAnchorPos, rStart, rEnd) + : IsSelectFrameAnchoredAtPara(*pAnchorPos, rStart, rEnd))) + { + return true; + } + } + break; + default: // other types not relevant + break; + } + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/undobj1.cxx b/sw/source/core/undo/undobj1.cxx new file mode 100644 index 000000000..2398ea8da --- /dev/null +++ b/sw/source/core/undo/undobj1.cxx @@ -0,0 +1,714 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svl/itemiter.hxx> +#include <svx/svdundo.hxx> +#include <hintids.hxx> +#include <hints.hxx> +#include <fmtflcnt.hxx> +#include <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include <txtflcnt.hxx> +#include <frmfmt.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <docary.hxx> +#include <rootfrm.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <ndole.hxx> +#include <frameformats.hxx> +#include <svx/svdobj.hxx> + +SwUndoFlyBase::SwUndoFlyBase( SwFrameFormat* pFormat, SwUndoId nUndoId ) + : SwUndo(nUndoId, pFormat->GetDoc()) + , m_pFrameFormat(pFormat) + , m_nNodePagePos(0) + , m_nContentPos(0) + , m_nRndId(RndStdIds::FLY_AT_PARA) + , m_bDelFormat(false) +{ +} + +SwUndoFlyBase::~SwUndoFlyBase() +{ + if( m_bDelFormat ) // delete during an Undo? + { + if (m_pFrameFormat->GetOtherTextBoxFormat()) + { // clear that before delete + m_pFrameFormat->SetOtherTextBoxFormat(nullptr); + } + delete m_pFrameFormat; + } +} + +void SwUndoFlyBase::InsFly(::sw::UndoRedoContext & rContext, bool bShowSelFrame) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + + // add again into array + SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats(); + rFlyFormats.push_back( m_pFrameFormat ); + + // OD 26.06.2003 #108784# - insert 'master' drawing object into drawing page + if ( RES_DRAWFRMFMT == m_pFrameFormat->Which() ) + m_pFrameFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREP_INSERT_FLY)); + + SwFormatAnchor aAnchor( m_nRndId ); + + if (RndStdIds::FLY_AT_PAGE == m_nRndId) + { + aAnchor.SetPageNum( static_cast<sal_uInt16>(m_nNodePagePos) ); + } + else + { + SwPosition aNewPos(pDoc->GetNodes().GetEndOfContent()); + aNewPos.nNode = m_nNodePagePos; + if ((RndStdIds::FLY_AS_CHAR == m_nRndId) || (RndStdIds::FLY_AT_CHAR == m_nRndId)) + { + aNewPos.nContent.Assign( aNewPos.nNode.GetNode().GetContentNode(), + m_nContentPos ); + } + aAnchor.SetAnchor( &aNewPos ); + } + + m_pFrameFormat->SetFormatAttr( aAnchor ); // reset anchor + + if( RES_DRAWFRMFMT != m_pFrameFormat->Which() ) + { + // get Content and reset ContentAttribute + SwNodeIndex aIdx( pDoc->GetNodes() ); + RestoreSection( pDoc, &aIdx, SwFlyStartNode ); + m_pFrameFormat->SetFormatAttr( SwFormatContent( aIdx.GetNode().GetStartNode() )); + } + + // Set InContentAttribute not until there is content! + // Otherwise the layout would format the Fly beforehand but would not find + // content; this happened with graphics from the internet. + if (RndStdIds::FLY_AS_CHAR == m_nRndId) + { + // there must be at least the attribute in a TextNode + SwContentNode* pCNd = aAnchor.GetContentAnchor()->nNode.GetNode().GetContentNode(); + OSL_ENSURE( pCNd->IsTextNode(), "no Text Node at position." ); + SwFormatFlyCnt aFormat( m_pFrameFormat ); + pCNd->GetTextNode()->InsertItem(aFormat, m_nContentPos, m_nContentPos, SetAttrMode::NOHINTEXPAND); + } + + if (m_pFrameFormat->GetOtherTextBoxFormat()) + { + // recklessly assume that this thing will live longer than the + // SwUndoFlyBase - not sure what could be done if that isn't the case... + m_pFrameFormat->GetOtherTextBoxFormat()->SetOtherTextBoxFormat(m_pFrameFormat); + + if (m_pFrameFormat->GetOtherTextBoxFormat()->Which() == RES_DRAWFRMFMT) + { + SdrObject* pSdrObject = m_pFrameFormat->GetOtherTextBoxFormat()->FindSdrObject(); + if (pSdrObject) + { + // Make sure the old UNO wrapper is no longer cached after changing the shape + + // textframe pair. Otherwise we would have a wrapper which doesn't know about its + // textframe, even if it's there. + pSdrObject->setUnoShape(nullptr); + } + } + if (m_pFrameFormat->Which() == RES_DRAWFRMFMT) + { + // This is a draw format and we just set the fly format's textbox pointer to this draw + // format. Sync the draw format's content with the fly format's content. + SwFrameFormat* pFlyFormat = m_pFrameFormat->GetOtherTextBoxFormat(); + m_pFrameFormat->SetFormatAttr(pFlyFormat->GetContent()); + } + } + + m_pFrameFormat->MakeFrames(); + + if( bShowSelFrame ) + { + rContext.SetSelections(m_pFrameFormat, nullptr); + } + + if( GetHistory() ) + GetHistory()->Rollback( pDoc ); + + switch( m_nRndId ) + { + case RndStdIds::FLY_AS_CHAR: + case RndStdIds::FLY_AT_CHAR: + { + const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor(); + m_nNodePagePos = rAnchor.GetContentAnchor()->nNode.GetIndex(); + m_nContentPos = rAnchor.GetContentAnchor()->nContent.GetIndex(); + } + break; + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_FLY: + { + const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor(); + m_nNodePagePos = rAnchor.GetContentAnchor()->nNode.GetIndex(); + } + break; + case RndStdIds::FLY_AT_PAGE: + break; + default: break; + } + m_bDelFormat = false; +} + +void SwUndoFlyBase::DelFly( SwDoc* pDoc ) +{ + m_bDelFormat = true; // delete Format in DTOR + m_pFrameFormat->DelFrames(); // destroy Frames + + if (m_pFrameFormat->GetOtherTextBoxFormat()) + { // tdf#108867 clear that pointer + m_pFrameFormat->GetOtherTextBoxFormat()->SetOtherTextBoxFormat(nullptr); + } + + // all Uno objects should now log themselves off + { + SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, m_pFrameFormat ); + m_pFrameFormat->ModifyNotification( &aMsgHint, &aMsgHint ); + } + + if ( RES_DRAWFRMFMT != m_pFrameFormat->Which() ) + { + // if there is content than save it + const SwFormatContent& rContent = m_pFrameFormat->GetContent(); + OSL_ENSURE( rContent.GetContentIdx(), "Fly without content" ); + + SaveSection( *rContent.GetContentIdx() ); + const_cast<SwFormatContent&>(rContent).SetNewContentIdx( nullptr ); + } + // OD 02.07.2003 #108784# - remove 'master' drawing object from drawing page + else + m_pFrameFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREP_DELETE_FLY)); + + const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor(); + const SwPosition* pPos = rAnchor.GetContentAnchor(); + // The positions in Nodes array got shifted. + m_nRndId = rAnchor.GetAnchorId(); + if (RndStdIds::FLY_AS_CHAR == m_nRndId) + { + m_nNodePagePos = pPos->nNode.GetIndex(); + m_nContentPos = pPos->nContent.GetIndex(); + SwTextNode *const pTextNd = pPos->nNode.GetNode().GetTextNode(); + OSL_ENSURE( pTextNd, "No Textnode found" ); + SwTextFlyCnt* const pAttr = static_cast<SwTextFlyCnt*>( + pTextNd->GetTextAttrForCharAt( m_nContentPos, RES_TXTATR_FLYCNT ) ); + // attribute is still in TextNode, delete + if( pAttr && pAttr->GetFlyCnt().GetFrameFormat() == m_pFrameFormat ) + { + // Pointer to 0, do not delete + const_cast<SwFormatFlyCnt&>(pAttr->GetFlyCnt()).SetFlyFormat(); + SwIndex aIdx( pPos->nContent ); + pTextNd->EraseText( aIdx, 1 ); + } + } + else if (RndStdIds::FLY_AT_CHAR == m_nRndId) + { + m_nNodePagePos = pPos->nNode.GetIndex(); + m_nContentPos = pPos->nContent.GetIndex(); + } + else if ((RndStdIds::FLY_AT_PARA == m_nRndId) || (RndStdIds::FLY_AT_FLY == m_nRndId)) + { + m_nNodePagePos = pPos->nNode.GetIndex(); + } + else + { + m_nNodePagePos = rAnchor.GetPageNum(); + } + + m_pFrameFormat->ResetFormatAttr( RES_ANCHOR ); // delete anchor + + // delete from array + SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats(); + rFlyFormats.erase( m_pFrameFormat ); +} + +SwUndoInsLayFormat::SwUndoInsLayFormat( SwFrameFormat* pFormat, sal_uLong nNodeIdx, sal_Int32 nCntIdx ) + : SwUndoFlyBase( pFormat, RES_DRAWFRMFMT == pFormat->Which() ? + SwUndoId::INSDRAWFMT : SwUndoId::INSLAYFMT ), + mnCursorSaveIndexPara( nNodeIdx ), mnCursorSaveIndexPos( nCntIdx ) +{ + const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor(); + m_nRndId = rAnchor.GetAnchorId(); + m_bDelFormat = false; + switch( m_nRndId ) + { + case RndStdIds::FLY_AT_PAGE: + m_nNodePagePos = rAnchor.GetPageNum(); + break; + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_FLY: + m_nNodePagePos = rAnchor.GetContentAnchor()->nNode.GetIndex(); + break; + case RndStdIds::FLY_AS_CHAR: + case RndStdIds::FLY_AT_CHAR: + { + const SwPosition* pPos = rAnchor.GetContentAnchor(); + m_nContentPos = pPos->nContent.GetIndex(); + m_nNodePagePos = pPos->nNode.GetIndex(); + } + break; + default: + OSL_FAIL( "Which FlyFrame?" ); + } +} + +SwUndoInsLayFormat::~SwUndoInsLayFormat() +{ +} + +void SwUndoInsLayFormat::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc(rContext.GetDoc()); + const SwFormatContent& rContent = m_pFrameFormat->GetContent(); + if( rContent.GetContentIdx() ) // no content + { + assert(&rContent.GetContentIdx()->GetNodes() == &rDoc.GetNodes()); + bool bRemoveIdx = true; + if( mnCursorSaveIndexPara > 0 ) + { + SwTextNode *const pNode = + rDoc.GetNodes()[mnCursorSaveIndexPara]->GetTextNode(); + if( pNode ) + { + SwNodeIndex aIdx( rDoc.GetNodes(), + rContent.GetContentIdx()->GetIndex() ); + SwNodeIndex aEndIdx( rDoc.GetNodes(), + aIdx.GetNode().EndOfSectionIndex() ); + SwIndex aIndex( pNode, mnCursorSaveIndexPos ); + SwPosition aPos( *pNode, aIndex ); + SwDoc::CorrAbs( aIdx, aEndIdx, aPos, true ); + bRemoveIdx = false; + } + } + if( bRemoveIdx ) + { + RemoveIdxFromSection( rDoc, rContent.GetContentIdx()->GetIndex() ); + } + } + DelFly(& rDoc); +} + +void SwUndoInsLayFormat::RedoImpl(::sw::UndoRedoContext & rContext) +{ + InsFly(rContext); +} + +void SwUndoInsLayFormat::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + // get anchor and reset it + SwFormatAnchor aAnchor( m_pFrameFormat->GetAnchor() ); + if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId())) + { + SwPosition aPos( *rContext.GetRepeatPaM().GetPoint() ); + if (RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) + { + aPos.nContent.Assign( nullptr, 0 ); + } + aAnchor.SetAnchor( &aPos ); + } + else if( RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId() ) + { + SwStartNode const*const pSttNd = + rContext.GetRepeatPaM().GetNode().FindFlyStartNode(); + if( pSttNd ) + { + SwPosition aPos( *pSttNd ); + aAnchor.SetAnchor( &aPos ); + } + else + { + return ; + } + } + else if (RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId()) + { + aAnchor.SetPageNum( pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->GetCurrPage( &rContext.GetRepeatPaM() )); + } + else { + OSL_FAIL( "What kind of anchor is this?" ); + } + + (void) pDoc->getIDocumentLayoutAccess().CopyLayoutFormat( *m_pFrameFormat, aAnchor, true, true ); +} + +OUString SwUndoInsLayFormat::GetComment() const +{ + OUString aResult; + + // HACK: disable caching: + // the SfxUndoManager calls GetComment() too early: the pFrameFormat does not + // have a SwDrawContact yet, so it will fall back to SwUndo::GetComment(), + // which sets pComment to a wrong value. +// if (! pComment) + if ((true)) + { + /* + If frame format is present and has an SdrObject use the undo + comment of the SdrObject. Otherwise use the default comment. + */ + bool bDone = false; + if (m_pFrameFormat) + { + const SdrObject * pSdrObj = m_pFrameFormat->FindSdrObject(); + if ( pSdrObj ) + { + aResult = SdrUndoNewObj::GetComment( *pSdrObj ); + bDone = true; + } + } + + if (! bDone) + aResult = SwUndo::GetComment(); + } + else + aResult = *maComment; + + return aResult; +} + +static SwUndoId +lcl_GetSwUndoId(SwFrameFormat const *const pFrameFormat) +{ + if (RES_DRAWFRMFMT != pFrameFormat->Which()) + { + const SwFormatContent& rContent = pFrameFormat->GetContent(); + OSL_ENSURE( rContent.GetContentIdx(), "Fly without content" ); + + SwNodeIndex firstNode(*rContent.GetContentIdx(), 1); + SwNoTextNode *const pNoTextNode(firstNode.GetNode().GetNoTextNode()); + if (pNoTextNode && pNoTextNode->IsGrfNode()) + { + return SwUndoId::DELGRF; + } + else if (pNoTextNode && pNoTextNode->IsOLENode()) + { + // surprisingly not SwUndoId::DELOLE, which does not seem to work + return SwUndoId::DELETE; + } + } + return SwUndoId::DELLAYFMT; +} + +SwUndoDelLayFormat::SwUndoDelLayFormat( SwFrameFormat* pFormat ) + : SwUndoFlyBase( pFormat, lcl_GetSwUndoId(pFormat) ) + , m_bShowSelFrame( true ) +{ + SwDoc* pDoc = pFormat->GetDoc(); + DelFly( pDoc ); +} + +SwRewriter SwUndoDelLayFormat::GetRewriter() const +{ + SwRewriter aRewriter; + + SwDoc * pDoc = m_pFrameFormat->GetDoc(); + + if (pDoc) + { + SwNodeIndex* pIdx = GetMvSttIdx(); + if( 1 == GetMvNodeCnt() && pIdx) + { + SwNode *const pNd = & pIdx->GetNode(); + + if ( pNd->IsNoTextNode() && pNd->IsOLENode()) + { + SwOLENode * pOLENd = pNd->GetOLENode(); + + aRewriter.AddRule(UndoArg1, pOLENd->GetDescription()); + } + } + } + + return aRewriter; +} + +void SwUndoDelLayFormat::UndoImpl(::sw::UndoRedoContext & rContext) +{ + InsFly( rContext, m_bShowSelFrame ); +} + +void SwUndoDelLayFormat::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc(rContext.GetDoc()); + const SwFormatContent& rContent = m_pFrameFormat->GetContent(); + if( rContent.GetContentIdx() ) // no content + { + RemoveIdxFromSection(rDoc, rContent.GetContentIdx()->GetIndex()); + } + + DelFly(& rDoc); +} + +void SwUndoDelLayFormat::RedoForRollback() +{ + const SwFormatContent& rContent = m_pFrameFormat->GetContent(); + if( rContent.GetContentIdx() ) // no content + RemoveIdxFromSection( *m_pFrameFormat->GetDoc(), + rContent.GetContentIdx()->GetIndex() ); + + DelFly( m_pFrameFormat->GetDoc() ); +} + +SwUndoSetFlyFormat::SwUndoSetFlyFormat( SwFrameFormat& rFlyFormat, const SwFrameFormat& rNewFrameFormat ) + : SwUndo( SwUndoId::SETFLYFRMFMT, rFlyFormat.GetDoc() ), SwClient( &rFlyFormat ), m_pFrameFormat( &rFlyFormat ), + m_DerivedFromFormatName( rFlyFormat.IsDefault() ? "" : rFlyFormat.DerivedFrom()->GetName() ), + m_NewFormatName( rNewFrameFormat.GetName() ), + m_pItemSet( new SfxItemSet( *rFlyFormat.GetAttrSet().GetPool(), + rFlyFormat.GetAttrSet().GetRanges() )), + m_nOldNode( 0 ), m_nNewNode( 0 ), + m_nOldContent( 0 ), m_nNewContent( 0 ), + m_nOldAnchorType( RndStdIds::FLY_AT_PARA ), m_nNewAnchorType( RndStdIds::FLY_AT_PARA ), m_bAnchorChanged( false ) +{ +} + +SwRewriter SwUndoSetFlyFormat::GetRewriter() const +{ + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, m_NewFormatName); + + return aRewriter; +} + +SwUndoSetFlyFormat::~SwUndoSetFlyFormat() +{ +} + +void SwUndoSetFlyFormat::GetAnchor( SwFormatAnchor& rAnchor, + sal_uLong nNode, sal_Int32 nContent ) +{ + RndStdIds nAnchorTyp = rAnchor.GetAnchorId(); + if (RndStdIds::FLY_AT_PAGE != nAnchorTyp) + { + SwNode* pNd = m_pFrameFormat->GetDoc()->GetNodes()[ nNode ]; + + if( RndStdIds::FLY_AT_FLY == nAnchorTyp + ? ( !pNd->IsStartNode() || SwFlyStartNode != + static_cast<SwStartNode*>(pNd)->GetStartNodeType() ) + : !pNd->IsTextNode() ) + { + pNd = nullptr; // invalid position + } + else + { + SwPosition aPos( *pNd ); + if ((RndStdIds::FLY_AS_CHAR == nAnchorTyp) || + (RndStdIds::FLY_AT_CHAR == nAnchorTyp)) + { + if (nContent > pNd->GetTextNode()->GetText().getLength()) + { + pNd = nullptr; // invalid position + } + else + { + aPos.nContent.Assign(pNd->GetTextNode(), nContent); + } + } + if ( pNd ) + { + rAnchor.SetAnchor( &aPos ); + } + } + + if( !pNd ) + { + // invalid position - assign first page + rAnchor.SetType( RndStdIds::FLY_AT_PAGE ); + rAnchor.SetPageNum( 1 ); + } + } + else + rAnchor.SetPageNum( nContent ); +} + +void SwUndoSetFlyFormat::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + // Is the new Format still existent? + SwFrameFormat* pDerivedFromFrameFormat = rDoc.FindFrameFormatByName(m_DerivedFromFormatName); + if (pDerivedFromFrameFormat) + { + if( m_bAnchorChanged ) + m_pFrameFormat->DelFrames(); + + if( m_pFrameFormat->DerivedFrom() != pDerivedFromFrameFormat) + m_pFrameFormat->SetDerivedFrom(pDerivedFromFrameFormat); + + SfxItemIter aIter( *m_pItemSet ); + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + if( IsInvalidItem( pItem )) + m_pFrameFormat->ResetFormatAttr( m_pItemSet->GetWhichByPos( + aIter.GetCurPos() )); + else + m_pFrameFormat->SetFormatAttr( *pItem ); + } + + if( m_bAnchorChanged ) + { + const SwFormatAnchor& rOldAnch = m_pFrameFormat->GetAnchor(); + if (RndStdIds::FLY_AS_CHAR == rOldAnch.GetAnchorId()) + { + // With InContents it's tricky: the text attribute needs to be + // deleted. Unfortunately, this not only destroys the Frames but + // also the format. To prevent that, first detach the + // connection between attribute and format. + const SwPosition *pPos = rOldAnch.GetContentAnchor(); + SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode(); + OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." ); + const sal_Int32 nIdx = pPos->nContent.GetIndex(); + SwTextAttr * pHint = pTextNode->GetTextAttrForCharAt( + nIdx, RES_TXTATR_FLYCNT ); + assert(pHint && "Missing Hint."); + OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT, + "Missing FlyInCnt-Hint." ); + OSL_ENSURE( pHint->GetFlyCnt().GetFrameFormat() == m_pFrameFormat, + "Wrong TextFlyCnt-Hint." ); + const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat(); + + // Connection is now detached, therefore the attribute can be + // deleted + pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx ); + } + + // reposition anchor + SwFormatAnchor aNewAnchor( m_nOldAnchorType ); + GetAnchor( aNewAnchor, m_nOldNode, m_nOldContent ); + m_pFrameFormat->SetFormatAttr( aNewAnchor ); + + if (RndStdIds::FLY_AS_CHAR == aNewAnchor.GetAnchorId()) + { + const SwPosition* pPos = aNewAnchor.GetContentAnchor(); + SwFormatFlyCnt aFormat( m_pFrameFormat ); + pPos->nNode.GetNode().GetTextNode()->InsertItem( aFormat, + m_nOldContent, 0 ); + } + + m_pFrameFormat->MakeFrames(); + } + rContext.SetSelections(m_pFrameFormat, nullptr); + } +} + +void SwUndoSetFlyFormat::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + // Is the new Format still existent? + SwFrameFormat* pNewFrameFormat = rDoc.FindFrameFormatByName(m_NewFormatName); + if (pNewFrameFormat) + { + if( m_bAnchorChanged ) + { + SwFormatAnchor aNewAnchor( m_nNewAnchorType ); + GetAnchor( aNewAnchor, m_nNewNode, m_nNewContent ); + SfxItemSet aSet( rDoc.GetAttrPool(), aFrameFormatSetRange ); + aSet.Put( aNewAnchor ); + rDoc.SetFrameFormatToFly( *m_pFrameFormat, *pNewFrameFormat, &aSet ); + } + else + rDoc.SetFrameFormatToFly( *m_pFrameFormat, *pNewFrameFormat); + + rContext.SetSelections(m_pFrameFormat, nullptr); + } +} + +void SwUndoSetFlyFormat::PutAttr( sal_uInt16 nWhich, const SfxPoolItem* pItem ) +{ + if( pItem && pItem != GetDfltAttr( nWhich ) ) + { + // Special treatment for this anchor + if( RES_ANCHOR == nWhich ) + { + // only keep the first change + OSL_ENSURE( !m_bAnchorChanged, "multiple changes of an anchor are not allowed!" ); + + m_bAnchorChanged = true; + + const SwFormatAnchor* pAnchor = static_cast<const SwFormatAnchor*>(pItem); + m_nOldAnchorType = pAnchor->GetAnchorId(); + switch( m_nOldAnchorType ) + { + case RndStdIds::FLY_AS_CHAR: + case RndStdIds::FLY_AT_CHAR: + m_nOldContent = pAnchor->GetContentAnchor()->nContent.GetIndex(); + [[fallthrough]]; + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_FLY: + m_nOldNode = pAnchor->GetContentAnchor()->nNode.GetIndex(); + break; + + default: + m_nOldContent = pAnchor->GetPageNum(); + } + + pAnchor = &m_pFrameFormat->GetAnchor(); + m_nNewAnchorType = pAnchor->GetAnchorId(); + switch( m_nNewAnchorType ) + { + case RndStdIds::FLY_AS_CHAR: + case RndStdIds::FLY_AT_CHAR: + m_nNewContent = pAnchor->GetContentAnchor()->nContent.GetIndex(); + [[fallthrough]]; + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_FLY: + m_nNewNode = pAnchor->GetContentAnchor()->nNode.GetIndex(); + break; + + default: + m_nNewContent = pAnchor->GetPageNum(); + } + } + else + m_pItemSet->Put( *pItem ); + } + else + m_pItemSet->InvalidateItem( nWhich ); +} + +void SwUndoSetFlyFormat::Modify( const SfxPoolItem* pOld, const SfxPoolItem* ) +{ + if( pOld ) + { + sal_uInt16 nWhich = pOld->Which(); + + if( nWhich < POOLATTR_END ) + PutAttr( nWhich, pOld ); + else if( RES_ATTRSET_CHG == nWhich ) + { + SfxItemIter aIter( *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() ); + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + PutAttr( pItem->Which(), pItem ); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/undoflystrattr.cxx b/sw/source/core/undo/undoflystrattr.cxx new file mode 100644 index 000000000..1a811ad21 --- /dev/null +++ b/sw/source/core/undo/undoflystrattr.cxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <undoflystrattr.hxx> +#include <frmfmt.hxx> + + +SwUndoFlyStrAttr::SwUndoFlyStrAttr( SwFlyFrameFormat& rFlyFrameFormat, + const SwUndoId eUndoId, + const OUString& sOldStr, + const OUString& sNewStr ) + : SwUndo( eUndoId, rFlyFrameFormat.GetDoc() ), + mrFlyFrameFormat( rFlyFrameFormat ), + msOldStr( sOldStr ), + msNewStr( sNewStr ) +{ + assert(eUndoId == SwUndoId::FLYFRMFMT_TITLE + || eUndoId == SwUndoId::FLYFRMFMT_DESCRIPTION); +} + +SwUndoFlyStrAttr::~SwUndoFlyStrAttr() +{ +} + +void SwUndoFlyStrAttr::UndoImpl(::sw::UndoRedoContext &) +{ + switch ( GetId() ) + { + case SwUndoId::FLYFRMFMT_TITLE: + { + mrFlyFrameFormat.SetObjTitle( msOldStr, true ); + } + break; + case SwUndoId::FLYFRMFMT_DESCRIPTION: + { + mrFlyFrameFormat.SetObjDescription( msOldStr, true ); + } + break; + default: + { + } + } +} + +void SwUndoFlyStrAttr::RedoImpl(::sw::UndoRedoContext &) +{ + switch ( GetId() ) + { + case SwUndoId::FLYFRMFMT_TITLE: + { + mrFlyFrameFormat.SetObjTitle( msNewStr, true ); + } + break; + case SwUndoId::FLYFRMFMT_DESCRIPTION: + { + mrFlyFrameFormat.SetObjDescription( msNewStr, true ); + } + break; + default: + { + } + } +} + +SwRewriter SwUndoFlyStrAttr::GetRewriter() const +{ + SwRewriter aResult; + + aResult.AddRule( UndoArg1, mrFlyFrameFormat.GetName() ); + + return aResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/undraw.cxx b/sw/source/core/undo/undraw.cxx new file mode 100644 index 000000000..b6c8bd521 --- /dev/null +++ b/sw/source/core/undo/undraw.cxx @@ -0,0 +1,559 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoDraw.hxx> + +#include <svx/svdogrp.hxx> +#include <svx/svdundo.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdmark.hxx> +#include <svx/svdview.hxx> + +#include <hintids.hxx> +#include <hints.hxx> +#include <fmtanchr.hxx> +#include <fmtflcnt.hxx> +#include <txtflcnt.hxx> +#include <frmfmt.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <docary.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <dcontact.hxx> +#include <viewsh.hxx> +#include <frameformats.hxx> + +struct SwUndoGroupObjImpl +{ + SwDrawFrameFormat* pFormat; + SdrObject* pObj; + sal_uLong nNodeIdx; +}; + +// Draw-Objecte + +void SwDoc::AddDrawUndo( std::unique_ptr<SdrUndoAction> pUndo ) +{ + if (GetIDocumentUndoRedo().DoesUndo() && + GetIDocumentUndoRedo().DoesDrawUndo()) + { + const SdrMarkList* pMarkList = nullptr; + SwViewShell* pSh = getIDocumentLayoutAccess().GetCurrentViewShell(); + if( pSh && pSh->HasDrawView() ) + pMarkList = &pSh->GetDrawView()->GetMarkedObjectList(); + + GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwSdrUndo>(std::move(pUndo), pMarkList, this) ); + } +} + +SwSdrUndo::SwSdrUndo( std::unique_ptr<SdrUndoAction> pUndo, const SdrMarkList* pMrkLst, const SwDoc* pDoc ) + : SwUndo( SwUndoId::DRAWUNDO, pDoc ), m_pSdrUndo( std::move(pUndo) ) +{ + if( pMrkLst && pMrkLst->GetMarkCount() ) + m_pMarkList.reset( new SdrMarkList( *pMrkLst ) ); +} + +SwSdrUndo::~SwSdrUndo() +{ + m_pSdrUndo.reset(); + m_pMarkList.reset(); +} + +void SwSdrUndo::UndoImpl(::sw::UndoRedoContext & rContext) +{ + m_pSdrUndo->Undo(); + rContext.SetSelections(nullptr, m_pMarkList.get()); +} + +void SwSdrUndo::RedoImpl(::sw::UndoRedoContext & rContext) +{ + m_pSdrUndo->Redo(); + rContext.SetSelections(nullptr, m_pMarkList.get()); +} + +OUString SwSdrUndo::GetComment() const +{ + return m_pSdrUndo->GetComment(); +} + +static void lcl_SendRemoveToUno( SwFormat& rFormat ) +{ + SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, &rFormat ); + rFormat.ModifyNotification( &aMsgHint, &aMsgHint ); +} + +static void lcl_SaveAnchor( SwFrameFormat* pFormat, sal_uLong& rNodePos ) +{ + const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); + if ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId())) + { + rNodePos = rAnchor.GetContentAnchor()->nNode.GetIndex(); + sal_Int32 nContentPos = 0; + + if (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) + { + nContentPos = rAnchor.GetContentAnchor()->nContent.GetIndex(); + + // destroy TextAttribute + SwTextNode *pTextNd = pFormat->GetDoc()->GetNodes()[ rNodePos ]->GetTextNode(); + OSL_ENSURE( pTextNd, "No text node found!" ); + SwTextFlyCnt* pAttr = static_cast<SwTextFlyCnt*>( + pTextNd->GetTextAttrForCharAt( nContentPos, RES_TXTATR_FLYCNT )); + // attribute still in text node, delete + if( pAttr && pAttr->GetFlyCnt().GetFrameFormat() == pFormat ) + { + // just set pointer to 0, don't delete + const_cast<SwFormatFlyCnt&>(pAttr->GetFlyCnt()).SetFlyFormat(); + SwIndex aIdx( pTextNd, nContentPos ); + pTextNd->EraseText( aIdx, 1 ); + } + } + else if (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) + { + nContentPos = rAnchor.GetContentAnchor()->nContent.GetIndex(); + } + + pFormat->SetFormatAttr( SwFormatAnchor( rAnchor.GetAnchorId(), nContentPos ) ); + } +} + +static void lcl_RestoreAnchor( SwFrameFormat* pFormat, sal_uLong nNodePos ) +{ + const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); + if ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId())) + { + const sal_Int32 nContentPos = rAnchor.GetPageNum(); + SwNodes& rNds = pFormat->GetDoc()->GetNodes(); + + SwNodeIndex aIdx( rNds, nNodePos ); + SwPosition aPos( aIdx ); + + SwFormatAnchor aTmp( rAnchor.GetAnchorId() ); + if ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) + { + aPos.nContent.Assign( aIdx.GetNode().GetContentNode(), nContentPos ); + } + aTmp.SetAnchor( &aPos ); + RndStdIds nAnchorId = rAnchor.GetAnchorId(); + pFormat->SetFormatAttr( aTmp ); + + if (RndStdIds::FLY_AS_CHAR == nAnchorId) + { + SwTextNode *pTextNd = aIdx.GetNode().GetTextNode(); + OSL_ENSURE( pTextNd, "no Text Node" ); + SwFormatFlyCnt aFormat( pFormat ); + pTextNd->InsertItem( aFormat, nContentPos, nContentPos ); + } + } +} + +SwUndoDrawGroup::SwUndoDrawGroup( sal_uInt16 nCnt, const SwDoc* pDoc ) + : SwUndo( SwUndoId::DRAWGROUP, pDoc ), m_nSize( nCnt + 1 ), m_bDeleteFormat( true ) +{ + m_pObjArray.reset( new SwUndoGroupObjImpl[ m_nSize ] ); +} + +SwUndoDrawGroup::~SwUndoDrawGroup() +{ + if( m_bDeleteFormat ) + { + SwUndoGroupObjImpl* pTmp = m_pObjArray.get() + 1; + for( sal_uInt16 n = 1; n < m_nSize; ++n, ++pTmp ) + delete pTmp->pFormat; + } + else + delete m_pObjArray[0].pFormat; +} + +void SwUndoDrawGroup::UndoImpl(::sw::UndoRedoContext &) +{ + m_bDeleteFormat = false; + + // save group object + SwDrawFrameFormat* pFormat = m_pObjArray[0].pFormat; + + pFormat->CallSwClientNotify(sw::ContactChangedHint(&m_pObjArray[0].pObj)); + auto pObj = m_pObjArray[0].pObj; + pObj->SetUserCall(nullptr); + + ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + // remove from array + SwDoc* pDoc = pFormat->GetDoc(); + SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats(); + rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat )); + + for( sal_uInt16 n = 1; n < m_nSize; ++n ) + { + SwUndoGroupObjImpl& rSave = m_pObjArray[n]; + + ::lcl_RestoreAnchor( rSave.pFormat, rSave.nNodeIdx ); + rFlyFormats.push_back( rSave.pFormat ); + + pObj = rSave.pObj; + + SwDrawContact *pContact = new SwDrawContact( rSave.pFormat, pObj ); + pContact->ConnectToLayout(); + // #i45718# - follow-up of #i35635# move object to visible layer + pContact->MoveObjToVisibleLayer( pObj ); + + SwDrawFrameFormat* pDrawFrameFormat = rSave.pFormat; + + // #i45952# - notify that position attributes are already set + OSL_ENSURE(pDrawFrameFormat, + "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object"); + if (pDrawFrameFormat) + pDrawFrameFormat->PosAttrSet(); + } +} + +void SwUndoDrawGroup::RedoImpl(::sw::UndoRedoContext &) +{ + m_bDeleteFormat = true; + + // remove from array + SwDoc* pDoc = m_pObjArray[0].pFormat->GetDoc(); + SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats(); + + for( sal_uInt16 n = 1; n < m_nSize; ++n ) + { + SwUndoGroupObjImpl& rSave = m_pObjArray[n]; + + SdrObject* pObj = rSave.pObj; + + SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj)); + + // object will destroy itself + pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() ); + pObj->SetUserCall( nullptr ); + + ::lcl_SaveAnchor( rSave.pFormat, rSave.nNodeIdx ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *rSave.pFormat ); + + rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), rSave.pFormat )); + } + + // re-insert group object + ::lcl_RestoreAnchor( m_pObjArray[0].pFormat, m_pObjArray[0].nNodeIdx ); + rFlyFormats.push_back( m_pObjArray[0].pFormat ); + + SwDrawContact *pContact = new SwDrawContact( m_pObjArray[0].pFormat, m_pObjArray[0].pObj ); + // #i26791# - correction: connect object to layout + pContact->ConnectToLayout(); + // #i45718# - follow-up of #i35635# move object to visible layer + pContact->MoveObjToVisibleLayer( m_pObjArray[0].pObj ); + + SwDrawFrameFormat* pDrawFrameFormat = m_pObjArray[0].pFormat; + + // #i45952# - notify that position attributes are already set + OSL_ENSURE(pDrawFrameFormat, + "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object"); + if (pDrawFrameFormat) + pDrawFrameFormat->PosAttrSet(); +} + +void SwUndoDrawGroup::AddObj( sal_uInt16 nPos, SwDrawFrameFormat* pFormat, SdrObject* pObj ) +{ + SwUndoGroupObjImpl& rSave = m_pObjArray[nPos + 1]; + rSave.pObj = pObj; + rSave.pFormat = pFormat; + ::lcl_SaveAnchor( pFormat, rSave.nNodeIdx ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + // remove from array + SwFrameFormats& rFlyFormats = *pFormat->GetDoc()->GetSpzFrameFormats(); + rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat )); +} + +void SwUndoDrawGroup::SetGroupFormat( SwDrawFrameFormat* pFormat ) +{ + m_pObjArray[0].pObj = nullptr; + m_pObjArray[0].pFormat = pFormat; +} + +SwUndoDrawUnGroup::SwUndoDrawUnGroup( SdrObjGroup* pObj, const SwDoc* pDoc ) + : SwUndo( SwUndoId::DRAWUNGROUP, pDoc ), m_bDeleteFormat( false ) +{ + m_nSize = static_cast<sal_uInt16>(pObj->GetSubList()->GetObjCount()) + 1; + m_pObjArray.reset( new SwUndoGroupObjImpl[ m_nSize ] ); + + SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj)); + SwDrawFrameFormat* pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat()); + + m_pObjArray[0].pObj = pObj; + m_pObjArray[0].pFormat = pFormat; + + // object will destroy itself + pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() ); + pObj->SetUserCall( nullptr ); + + ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + // remove from array + SwFrameFormats& rFlyFormats = *pFormat->GetDoc()->GetSpzFrameFormats(); + rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat )); +} + +SwUndoDrawUnGroup::~SwUndoDrawUnGroup() +{ + if( m_bDeleteFormat ) + { + SwUndoGroupObjImpl* pTmp = m_pObjArray.get() + 1; + for( sal_uInt16 n = 1; n < m_nSize; ++n, ++pTmp ) + delete pTmp->pFormat; + } + else + delete m_pObjArray[0].pFormat; +} + +void SwUndoDrawUnGroup::UndoImpl(::sw::UndoRedoContext & rContext) +{ + m_bDeleteFormat = true; + + SwDoc *const pDoc = & rContext.GetDoc(); + SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats(); + + // remove from array + for( sal_uInt16 n = 1; n < m_nSize; ++n ) + { + SwUndoGroupObjImpl& rSave = m_pObjArray[n]; + + ::lcl_SaveAnchor( rSave.pFormat, rSave.nNodeIdx ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *rSave.pFormat ); + + rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), rSave.pFormat )); + } + + // re-insert group object + ::lcl_RestoreAnchor( m_pObjArray[0].pFormat, m_pObjArray[0].nNodeIdx ); + rFlyFormats.push_back( m_pObjArray[0].pFormat ); + + SwDrawContact *pContact = new SwDrawContact( m_pObjArray[0].pFormat, m_pObjArray[0].pObj ); + pContact->ConnectToLayout(); + // #i45718# - follow-up of #i35635# move object to visible layer + pContact->MoveObjToVisibleLayer( m_pObjArray[0].pObj ); + + SwDrawFrameFormat* pDrawFrameFormat = m_pObjArray[0].pFormat; + + // #i45952# - notify that position attributes are already set + OSL_ENSURE(pDrawFrameFormat, + "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object"); + if (pDrawFrameFormat) + pDrawFrameFormat->PosAttrSet(); +} + +void SwUndoDrawUnGroup::RedoImpl(::sw::UndoRedoContext &) +{ + m_bDeleteFormat = false; + + // save group object + SwDrawFrameFormat* pFormat = m_pObjArray[0].pFormat; + pFormat->CallSwClientNotify(sw::ContactChangedHint(&(m_pObjArray[0].pObj))); + m_pObjArray[0].pObj->SetUserCall( nullptr ); + + ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + // remove from array + SwDoc* pDoc = pFormat->GetDoc(); + SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats(); + rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat )); + + for( sal_uInt16 n = 1; n < m_nSize; ++n ) + { + SwUndoGroupObjImpl& rSave = m_pObjArray[n]; + + ::lcl_RestoreAnchor( rSave.pFormat, rSave.nNodeIdx ); + rFlyFormats.push_back( rSave.pFormat ); + + SwDrawFrameFormat* pDrawFrameFormat = rSave.pFormat; + + // #i45952# - notify that position attributes are already set + OSL_ENSURE(pDrawFrameFormat, + "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object" ); + if (pDrawFrameFormat) + pDrawFrameFormat->PosAttrSet(); + } +} + +void SwUndoDrawUnGroup::AddObj( sal_uInt16 nPos, SwDrawFrameFormat* pFormat ) +{ + SwUndoGroupObjImpl& rSave = m_pObjArray[ nPos + 1 ]; + rSave.pFormat = pFormat; + rSave.pObj = nullptr; +} + +SwUndoDrawUnGroupConnectToLayout::SwUndoDrawUnGroupConnectToLayout(const SwDoc* pDoc) + : SwUndo( SwUndoId::DRAWUNGROUP, pDoc ) +{ +} + +SwUndoDrawUnGroupConnectToLayout::~SwUndoDrawUnGroupConnectToLayout() +{ +} + +void +SwUndoDrawUnGroupConnectToLayout::UndoImpl(::sw::UndoRedoContext &) +{ + for (const std::pair< SwDrawFrameFormat*, SdrObject* > & rPair : m_aDrawFormatsAndObjs) + { + SdrObject* pObj( rPair.second ); + SwDrawContact* pDrawContact( dynamic_cast<SwDrawContact*>(pObj->GetUserCall()) ); + OSL_ENSURE( pDrawContact, + "<SwUndoDrawUnGroupConnectToLayout::Undo(..)> -- missing SwDrawContact instance" ); + if ( pDrawContact ) + { + // deletion of instance <pDrawContact> and thus disconnection from + // the Writer layout. + pDrawContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() ); + pObj->SetUserCall( nullptr ); + } + } +} + +void +SwUndoDrawUnGroupConnectToLayout::RedoImpl(::sw::UndoRedoContext &) +{ + for (const std::pair< SwDrawFrameFormat*, SdrObject* > & rPair : m_aDrawFormatsAndObjs) + { + SwDrawFrameFormat* pFormat( rPair.first ); + SdrObject* pObj( rPair.second ); + SwDrawContact *pContact = new SwDrawContact( pFormat, pObj ); + pContact->ConnectToLayout(); + pContact->MoveObjToVisibleLayer( pObj ); + } +} + +void SwUndoDrawUnGroupConnectToLayout::AddFormatAndObj( SwDrawFrameFormat* pDrawFrameFormat, + SdrObject* pDrawObject ) +{ + m_aDrawFormatsAndObjs.emplace_back( pDrawFrameFormat, pDrawObject ); +} + +SwUndoDrawDelete::SwUndoDrawDelete( sal_uInt16 nCnt, const SwDoc* pDoc ) + : SwUndo( SwUndoId::DRAWDELETE, pDoc ), m_bDeleteFormat( true ) +{ + m_pObjArray.reset( new SwUndoGroupObjImpl[ nCnt ] ); + m_pMarkList.reset( new SdrMarkList() ); +} + +SwUndoDrawDelete::~SwUndoDrawDelete() +{ + if( m_bDeleteFormat ) + { + SwUndoGroupObjImpl* pTmp = m_pObjArray.get(); + for( size_t n = 0; n < m_pMarkList->GetMarkCount(); ++n, ++pTmp ) + delete pTmp->pFormat; + } +} + +void SwUndoDrawDelete::UndoImpl(::sw::UndoRedoContext & rContext) +{ + m_bDeleteFormat = false; + SwFrameFormats & rFlyFormats = *rContext.GetDoc().GetSpzFrameFormats(); + for( size_t n = 0; n < m_pMarkList->GetMarkCount(); ++n ) + { + SwUndoGroupObjImpl& rSave = m_pObjArray[n]; + ::lcl_RestoreAnchor( rSave.pFormat, rSave.nNodeIdx ); + rFlyFormats.push_back( rSave.pFormat ); + SdrObject *pObj = rSave.pObj; + SwDrawContact *pContact = new SwDrawContact( rSave.pFormat, pObj ); + pContact->Changed_( *pObj, SdrUserCallType::Inserted, nullptr ); + // #i45718# - follow-up of #i35635# move object to visible layer + pContact->MoveObjToVisibleLayer( pObj ); + + SwDrawFrameFormat* pDrawFrameFormat = rSave.pFormat; + + // #i45952# - notify that position attributes are already set + OSL_ENSURE(pDrawFrameFormat, + "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object"); + if (pDrawFrameFormat) + pDrawFrameFormat->PosAttrSet(); + } + rContext.SetSelections(nullptr, m_pMarkList.get()); +} + +void SwUndoDrawDelete::RedoImpl(::sw::UndoRedoContext & rContext) +{ + m_bDeleteFormat = true; + SwFrameFormats & rFlyFormats = *rContext.GetDoc().GetSpzFrameFormats(); + for( size_t n = 0; n < m_pMarkList->GetMarkCount(); ++n ) + { + SwUndoGroupObjImpl& rSave = m_pObjArray[n]; + SdrObject *pObj = rSave.pObj; + SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj)); + SwDrawFrameFormat *pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat()); + + // object will destroy itself + pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() ); + pObj->SetUserCall( nullptr ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat )); + ::lcl_SaveAnchor( pFormat, rSave.nNodeIdx ); + } +} + +void SwUndoDrawDelete::AddObj( SwDrawFrameFormat* pFormat, + const SdrMark& rMark ) +{ + SwUndoGroupObjImpl& rSave = m_pObjArray[ m_pMarkList->GetMarkCount() ]; + rSave.pObj = rMark.GetMarkedSdrObj(); + rSave.pFormat = pFormat; + ::lcl_SaveAnchor( pFormat, rSave.nNodeIdx ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + // remove from array + SwDoc* pDoc = pFormat->GetDoc(); + SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats(); + rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat )); + + m_pMarkList->InsertEntry( rMark ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unfmco.cxx b/sw/source/core/undo/unfmco.cxx new file mode 100644 index 000000000..118cf98f3 --- /dev/null +++ b/sw/source/core/undo/unfmco.cxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <doc.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> + +SwUndoFormatColl::SwUndoFormatColl( const SwPaM& rRange, + const SwFormatColl* pColl, + const bool bReset, + const bool bResetListAttrs ) + : SwUndo( SwUndoId::SETFMTCOLL, rRange.GetDoc() ), + SwUndRng( rRange ), + mpHistory( new SwHistory ), + mbReset( bReset ), + mbResetListAttrs( bResetListAttrs ) +{ + // #i31191# + if ( pColl ) + maFormatName = pColl->GetName(); +} + +SwUndoFormatColl::~SwUndoFormatColl() +{ +} + +void SwUndoFormatColl::UndoImpl(::sw::UndoRedoContext & rContext) +{ + // restore old values + mpHistory->TmpRollback(& rContext.GetDoc(), 0); + mpHistory->SetTmpEnd( mpHistory->Count() ); + + // create cursor for undo range + AddUndoRedoPaM(rContext); +} + +void SwUndoFormatColl::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam = AddUndoRedoPaM(rContext); + + DoSetFormatColl(rContext.GetDoc(), rPam); +} + +void SwUndoFormatColl::RepeatImpl(::sw::RepeatContext & rContext) +{ + DoSetFormatColl(rContext.GetDoc(), rContext.GetRepeatPaM()); +} + +void SwUndoFormatColl::DoSetFormatColl(SwDoc & rDoc, SwPaM const & rPaM) +{ + // Only one TextFrameColl can be applied to a section, thus request only in + // this array. + SwTextFormatColl * pFormatColl = rDoc.FindTextFormatCollByName(maFormatName); + if (pFormatColl) + { + rDoc.SetTextFormatColl(rPaM, pFormatColl, mbReset, mbResetListAttrs); + } +} + +SwRewriter SwUndoFormatColl::GetRewriter() const +{ + SwRewriter aResult; + + aResult.AddRule(UndoArg1, maFormatName ); + + return aResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unins.cxx b/sw/source/core/undo/unins.cxx new file mode 100644 index 000000000..6e08fb502 --- /dev/null +++ b/sw/source/core/undo/unins.cxx @@ -0,0 +1,1045 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoInsert.hxx> + +#include <hintids.hxx> +#include <unotools/charclass.hxx> +#include <sot/storage.hxx> +#include <editeng/keepitem.hxx> +#include <svx/svdobj.hxx> + +#include <fmtcntnt.hxx> +#include <frmfmt.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IShellCursorSupplier.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <UndoDelete.hxx> +#include <UndoAttribute.hxx> +#include <rolbck.hxx> +#include <ndgrf.hxx> +#include <ndole.hxx> +#include <grfatr.hxx> +#include <cntfrm.hxx> +#include <flyfrm.hxx> +#include <swtable.hxx> +#include <redline.hxx> +#include <docary.hxx> +#include <acorrect.hxx> + +#include <strings.hrc> + +using namespace ::com::sun::star; + +// INSERT + +std::optional<OUString> SwUndoInsert::GetTextFromDoc() const +{ + std::optional<OUString> aResult; + + SwNodeIndex aNd( m_pDoc->GetNodes(), m_nNode); + SwContentNode* pCNd = aNd.GetNode().GetContentNode(); + + if( pCNd->IsTextNode() ) + { + OUString sText = pCNd->GetTextNode()->GetText(); + + sal_Int32 nStart = m_nContent-m_nLen; + sal_Int32 nLength = m_nLen; + + if (nStart < 0) + { + nLength += nStart; + nStart = 0; + } + + aResult = sText.copy(nStart, nLength); + } + + return aResult; +} + +void SwUndoInsert::Init(const SwNodeIndex & rNd) +{ + // consider Redline + m_pDoc = rNd.GetNode().GetDoc(); + if( m_pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + m_pRedlData.reset( new SwRedlineData( RedlineType::Insert, + m_pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + SetRedlineFlags( m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags() ); + } + + maUndoText = GetTextFromDoc(); + + m_bCacheComment = false; +} + +SwUndoInsert::SwUndoInsert( const SwNodeIndex& rNd, sal_Int32 nCnt, + sal_Int32 nL, + const SwInsertFlags nInsertFlags, + bool bWDelim ) + : SwUndo(SwUndoId::TYPING, rNd.GetNode().GetDoc()), + m_nNode( rNd.GetIndex() ), m_nContent(nCnt), m_nLen(nL), + m_bIsWordDelim( bWDelim ), m_bIsAppend( false ) + , m_bWithRsid(false) + , m_nInsertFlags(nInsertFlags) +{ + Init(rNd); +} + +SwUndoInsert::SwUndoInsert( const SwNodeIndex& rNd ) + : SwUndo(SwUndoId::SPLITNODE, rNd.GetNode().GetDoc()), + m_nNode( rNd.GetIndex() ), m_nContent(0), m_nLen(1), + m_bIsWordDelim( false ), m_bIsAppend( true ) + , m_bWithRsid(false) + , m_nInsertFlags(SwInsertFlags::EMPTYEXPAND) +{ + Init(rNd); +} + +// Check if the next Insert can be combined with the current one. If so +// change the length and InsPos. As a result, SwDoc::Inser will not add a +// new object into the Undo list. + +bool SwUndoInsert::CanGrouping( sal_Unicode cIns ) +{ + if( !m_bIsAppend && m_bIsWordDelim == + !GetAppCharClass().isLetterNumeric( OUString( cIns )) ) + { + m_nLen++; + m_nContent++; + + if (maUndoText) + (*maUndoText) += OUStringChar(cIns); + + return true; + } + return false; +} + +bool SwUndoInsert::CanGrouping( const SwPosition& rPos ) +{ + bool bRet = false; + if( m_nNode == rPos.nNode.GetIndex() && + m_nContent == rPos.nContent.GetIndex() ) + { + // consider Redline + SwDoc& rDoc = *rPos.nNode.GetNode().GetDoc(); + if( ( ~RedlineFlags::ShowMask & rDoc.getIDocumentRedlineAccess().GetRedlineFlags() ) == + ( ~RedlineFlags::ShowMask & GetRedlineFlags() ) ) + { + bRet = true; + + // then there is or was still an active Redline: + // Check if there is another Redline at the InsPosition. If the + // same exists only once, it can be combined. + const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable(); + if( !rTable.empty() ) + { + SwRedlineData aRData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ); + const SwIndexReg* pIReg = rPos.nContent.GetIdxReg(); + for(SwRangeRedline* pRedl : rTable) + { + SwIndex* pIdx = &pRedl->End()->nContent; + if( pIReg == pIdx->GetIdxReg() && + m_nContent == pIdx->GetIndex() ) + { + if( !pRedl->HasMark() || !m_pRedlData || + *pRedl != *m_pRedlData || *pRedl != aRData ) + { + bRet = false; + break; + } + } + } + } + } + } + return bRet; +} + +SwUndoInsert::~SwUndoInsert() +{ + if (m_pUndoNodeIndex) // delete the section from UndoNodes array + { + // Insert saves the content in IconSection + SwNodes& rUNds = m_pUndoNodeIndex->GetNodes(); + rUNds.Delete(*m_pUndoNodeIndex, + rUNds.GetEndOfExtras().GetIndex() - m_pUndoNodeIndex->GetIndex()); + m_pUndoNodeIndex.reset(); + } + else // the inserted text + { + maText.reset(); + } + m_pRedlData.reset(); +} + +void SwUndoInsert::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pTmpDoc = & rContext.GetDoc(); + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + if( m_bIsAppend ) + { + pPam->GetPoint()->nNode = m_nNode; + + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + { + pPam->GetPoint()->nContent.Assign( pPam->GetContentNode(), 0 ); + pPam->SetMark(); + pPam->Move( fnMoveBackward ); + pPam->Exchange(); + pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( *pPam, true, RedlineType::Any ); + } + pPam->DeleteMark(); + pTmpDoc->getIDocumentContentOperations().DelFullPara( *pPam ); + pPam->GetPoint()->nContent.Assign( pPam->GetContentNode(), 0 ); + } + else + { + sal_uLong nNd = m_nNode; + sal_Int32 nCnt = m_nContent; + if( m_nLen ) + { + SwNodeIndex aNd( pTmpDoc->GetNodes(), m_nNode); + SwContentNode* pCNd = aNd.GetNode().GetContentNode(); + SwPaM aPaM( *pCNd, m_nContent ); + + aPaM.SetMark(); + + SwTextNode * const pTextNode( pCNd->GetTextNode() ); + if ( pTextNode ) + { + aPaM.GetPoint()->nContent -= m_nLen; + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( aPaM, true, RedlineType::Any ); + if (m_bWithRsid) + { + // RSID was added: remove any CHARFMT/AUTOFMT that may be + // set on the deleted text; EraseText will leave empty + // ones behind otherwise + pTextNode->DeleteAttributes(RES_TXTATR_AUTOFMT, + aPaM.GetPoint()->nContent.GetIndex(), + aPaM.GetMark()->nContent.GetIndex()); + pTextNode->DeleteAttributes(RES_TXTATR_CHARFMT, + aPaM.GetPoint()->nContent.GetIndex(), + aPaM.GetMark()->nContent.GetIndex()); + } + RemoveIdxFromRange( aPaM, false ); + maText = pTextNode->GetText().copy(m_nContent-m_nLen, m_nLen); + pTextNode->EraseText( aPaM.GetPoint()->nContent, m_nLen ); + } + else // otherwise Graphics/OLE/Text/... + { + aPaM.Move(fnMoveBackward); + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( aPaM, true, RedlineType::Any ); + RemoveIdxFromRange( aPaM, false ); + } + + nNd = aPaM.GetPoint()->nNode.GetIndex(); + nCnt = aPaM.GetPoint()->nContent.GetIndex(); + + if (!maText) + { + m_pUndoNodeIndex.reset( + new SwNodeIndex(m_pDoc->GetNodes().GetEndOfContent())); + MoveToUndoNds(aPaM, m_pUndoNodeIndex.get()); + } + m_nNode = aPaM.GetPoint()->nNode.GetIndex(); + m_nContent = aPaM.GetPoint()->nContent.GetIndex(); + } + + // set cursor to Undo range + pPam->DeleteMark(); + + pPam->GetPoint()->nNode = nNd; + pPam->GetPoint()->nContent.Assign( + pPam->GetPoint()->nNode.GetNode().GetContentNode(), nCnt ); + } + + maUndoText.reset(); +} + +void SwUndoInsert::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pTmpDoc = & rContext.GetDoc(); + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + pPam->DeleteMark(); + + if( m_bIsAppend ) + { + pPam->GetPoint()->nNode = m_nNode - 1; + pTmpDoc->getIDocumentContentOperations().AppendTextNode( *pPam->GetPoint() ); + + pPam->SetMark(); + pPam->Move( fnMoveBackward ); + pPam->Exchange(); + + if( m_pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + { + RedlineFlags eOld = pTmpDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore); + pTmpDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData, *pPam ), true); + pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) && + !pTmpDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + pTmpDoc->getIDocumentRedlineAccess().SplitRedline( *pPam ); + + pPam->DeleteMark(); + } + else + { + pPam->GetPoint()->nNode = m_nNode; + SwContentNode *const pCNd = + pPam->GetPoint()->nNode.GetNode().GetContentNode(); + pPam->GetPoint()->nContent.Assign( pCNd, m_nContent ); + + if( m_nLen ) + { + const bool bMvBkwrd = MovePtBackward( *pPam ); + + if (maText) + { + SwTextNode *const pTextNode = pCNd->GetTextNode(); + OSL_ENSURE( pTextNode, "where is my textnode ?" ); + OUString const ins( + pTextNode->InsertText( *maText, pPam->GetMark()->nContent, + m_nInsertFlags) ); + assert(ins.getLength() == maText->getLength()); // must succeed + maText.reset(); + if (m_bWithRsid) // re-insert RSID + { + SwPaM pam(*pPam->GetMark(), nullptr); // mark -> point + pTmpDoc->UpdateRsid(pam, ins.getLength()); + } + } + else + { + // re-insert content again (first detach m_pUndoNodeIndex!) + sal_uLong const nMvNd = m_pUndoNodeIndex->GetIndex(); + m_pUndoNodeIndex.reset(); + MoveFromUndoNds(*pTmpDoc, nMvNd, *pPam->GetMark()); + } + m_nNode = pPam->GetMark()->nNode.GetIndex(); + m_nContent = pPam->GetMark()->nContent.GetIndex(); + + MovePtForward( *pPam, bMvBkwrd ); + pPam->Exchange(); + if( m_pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + { + RedlineFlags eOld = pTmpDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore); + pTmpDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData, + *pPam ), true); + pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) && + !pTmpDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + pTmpDoc->getIDocumentRedlineAccess().SplitRedline(*pPam); + } + } + + maUndoText = GetTextFromDoc(); +} + +void SwUndoInsert::RepeatImpl(::sw::RepeatContext & rContext) +{ + if( !m_nLen ) + return; + + SwDoc & rDoc = rContext.GetDoc(); + SwNodeIndex aNd( rDoc.GetNodes(), m_nNode ); + SwContentNode* pCNd = aNd.GetNode().GetContentNode(); + + if( !m_bIsAppend && 1 == m_nLen ) // >1 than always Text, otherwise Graphics/OLE/Text/... + { + SwPaM aPaM( *pCNd, m_nContent ); + aPaM.SetMark(); + aPaM.Move(fnMoveBackward); + pCNd = aPaM.GetContentNode(); + } + +// What happens with the possible selected range ??? + + switch( pCNd->GetNodeType() ) + { + case SwNodeType::Text: + if( m_bIsAppend ) + { + rDoc.getIDocumentContentOperations().AppendTextNode( *rContext.GetRepeatPaM().GetPoint() ); + } + else + { + OUString const aText( pCNd->GetTextNode()->GetText() ); + ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); + rDoc.getIDocumentContentOperations().InsertString( rContext.GetRepeatPaM(), + aText.copy(m_nContent - m_nLen, m_nLen) ); + } + break; + case SwNodeType::Grf: + { + SwGrfNode* pGrfNd = static_cast<SwGrfNode*>(pCNd); + OUString sFile; + OUString sFilter; + if( pGrfNd->IsGrfLink() ) + pGrfNd->GetFileFilterNms( &sFile, &sFilter ); + + rDoc.getIDocumentContentOperations().InsertGraphic( + rContext.GetRepeatPaM(), sFile, sFilter, + &pGrfNd->GetGrf(), + nullptr/* Graphics collection*/, nullptr, nullptr ); + } + break; + + case SwNodeType::Ole: + { + // StarView does not yet provide an option to copy a StarOBJ + SwOLEObj& rSwOLE = static_cast<SwOLENode*>(pCNd)->GetOLEObj(); + + // temporary storage until object is inserted + // TODO/MBA: seems that here a physical copy is done - not as in drawing layer! Testing! + // TODO/LATER: Copying through the container would copy the replacement image as well + comphelper::EmbeddedObjectContainer aCnt; + OUString aName = aCnt.CreateUniqueObjectName(); + if (aCnt.StoreEmbeddedObject(rSwOLE.GetOleRef(), aName, true, OUString(), OUString())) + { + uno::Reference < embed::XEmbeddedObject > aNew = aCnt.GetEmbeddedObject( aName ); + rDoc.getIDocumentContentOperations().InsertEmbObject( + rContext.GetRepeatPaM(), + svt::EmbeddedObjectRef( aNew, + static_cast<SwOLENode*>(pCNd)->GetAspect() ), + nullptr ); + } + + break; + } + + default: break; + } +} + +SwRewriter SwUndoInsert::GetRewriter() const +{ + SwRewriter aResult; + std::optional<OUString> aStr; + bool bDone = false; + + if (maText) + aStr = maText; + else if (maUndoText) + aStr = maUndoText; + + if (aStr) + { + OUString aString = ShortenString(DenoteSpecialCharacters(*aStr), + nUndoStringLength, + SwResId(STR_LDOTS)); + + aResult.AddRule(UndoArg1, aString); + + bDone = true; + } + + if ( ! bDone ) + { + aResult.AddRule(UndoArg1, "??"); + } + + return aResult; +} + +class SwUndoReplace::Impl + : private SwUndoSaveContent +{ + OUString m_sOld; + OUString m_sIns; + sal_uLong m_nSttNd, m_nEndNd, m_nOffset; + sal_Int32 m_nSttCnt, m_nEndCnt, m_nSetPos, m_nSelEnd; + bool m_bSplitNext : 1; + bool m_bRegExp : 1; + // metadata references for paragraph and following para (if m_bSplitNext) + std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoStart; + std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoEnd; + +public: + Impl(SwPaM const& rPam, OUString const& rIns, bool const bRegExp); + virtual ~Impl() + { + } + + void UndoImpl( ::sw::UndoRedoContext & ); + void RedoImpl( ::sw::UndoRedoContext & ); + + void SetEnd(SwPaM const& rPam); + + OUString const& GetOld() const { return m_sOld; } + OUString const& GetIns() const { return m_sIns; } +}; + +SwUndoReplace::SwUndoReplace(SwPaM const& rPam, + OUString const& rIns, bool const bRegExp) + : SwUndo( SwUndoId::REPLACE, rPam.GetDoc() ) + , m_pImpl(std::make_unique<Impl>(rPam, rIns, bRegExp)) +{ +} + +SwUndoReplace::~SwUndoReplace() +{ +} + +void SwUndoReplace::UndoImpl(::sw::UndoRedoContext & rContext) +{ + m_pImpl->UndoImpl(rContext); +} + +void SwUndoReplace::RedoImpl(::sw::UndoRedoContext & rContext) +{ + m_pImpl->RedoImpl(rContext); +} + +SwRewriter +MakeUndoReplaceRewriter(sal_uLong const occurrences, + OUString const& sOld, OUString const& sNew) +{ + SwRewriter aResult; + + if (1 < occurrences) + { + aResult.AddRule(UndoArg1, OUString::number(occurrences)); + aResult.AddRule(UndoArg2, SwResId(STR_OCCURRENCES_OF)); + + OUString aTmpStr = SwResId(STR_START_QUOTE); + aTmpStr += ShortenString(sOld, nUndoStringLength, + SwResId(STR_LDOTS)); + aTmpStr += SwResId(STR_END_QUOTE); + aResult.AddRule(UndoArg3, aTmpStr); + } + else if (1 == occurrences) + { + { + OUString aTmpStr = SwResId(STR_START_QUOTE); + // #i33488 # + aTmpStr += ShortenString(sOld, nUndoStringLength, + SwResId(STR_LDOTS)); + aTmpStr += SwResId(STR_END_QUOTE); + aResult.AddRule(UndoArg1, aTmpStr); + } + + aResult.AddRule(UndoArg2, SwResId(STR_YIELDS)); + + { + OUString aTmpStr = SwResId(STR_START_QUOTE); + // #i33488 # + aTmpStr += ShortenString(sNew, nUndoStringLength, + SwResId(STR_LDOTS)); + aTmpStr += SwResId(STR_END_QUOTE); + aResult.AddRule(UndoArg3, aTmpStr); + } + } + + return aResult; +} + +SwRewriter SwUndoReplace::GetRewriter() const +{ + return MakeUndoReplaceRewriter(1, m_pImpl->GetOld(), m_pImpl->GetIns()); +} + +void SwUndoReplace::SetEnd(SwPaM const& rPam) +{ + m_pImpl->SetEnd(rPam); +} + +SwUndoReplace::Impl::Impl( + SwPaM const& rPam, OUString const& rIns, bool const bRegExp) + : m_sIns( rIns ) + , m_nOffset( 0 ) + , m_bRegExp(bRegExp) +{ + + const SwPosition * pStt( rPam.Start() ); + const SwPosition * pEnd( rPam.End() ); + + m_nSttNd = m_nEndNd = pStt->nNode.GetIndex(); + m_nSttCnt = pStt->nContent.GetIndex(); + m_nSelEnd = m_nEndCnt = pEnd->nContent.GetIndex(); + + m_bSplitNext = m_nSttNd != pEnd->nNode.GetIndex(); + + SwTextNode* pNd = pStt->nNode.GetNode().GetTextNode(); + OSL_ENSURE( pNd, "Dude, where's my TextNode?" ); + + m_pHistory.reset( new SwHistory ); + DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace); + + m_nSetPos = m_pHistory->Count(); + + sal_uLong nNewPos = pStt->nNode.GetIndex(); + m_nOffset = m_nSttNd - nNewPos; + + if ( pNd->GetpSwpHints() ) + { + m_pHistory->CopyAttr( pNd->GetpSwpHints(), nNewPos, 0, + pNd->GetText().getLength(), true ); + } + + if ( m_bSplitNext ) + { + if( pNd->HasSwAttrSet() ) + m_pHistory->CopyFormatAttr( *pNd->GetpSwAttrSet(), nNewPos ); + m_pHistory->Add( pNd->GetTextColl(), nNewPos, SwNodeType::Text ); + + SwTextNode* pNext = pEnd->nNode.GetNode().GetTextNode(); + sal_uLong nTmp = pNext->GetIndex(); + m_pHistory->CopyAttr( pNext->GetpSwpHints(), nTmp, 0, + pNext->GetText().getLength(), true ); + if( pNext->HasSwAttrSet() ) + m_pHistory->CopyFormatAttr( *pNext->GetpSwAttrSet(), nTmp ); + m_pHistory->Add( pNext->GetTextColl(),nTmp, SwNodeType::Text ); + // METADATA: store + m_pMetadataUndoStart = pNd ->CreateUndo(); + m_pMetadataUndoEnd = pNext->CreateUndo(); + } + + if( !m_pHistory->Count() ) + { + m_pHistory.reset(); + } + + const sal_Int32 nECnt = m_bSplitNext ? pNd->GetText().getLength() + : pEnd->nContent.GetIndex(); + m_sOld = pNd->GetText().copy( m_nSttCnt, nECnt - m_nSttCnt ); +} + +void SwUndoReplace::Impl::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM & rPam(rContext.GetCursorSupplier().CreateNewShellCursor()); + rPam.DeleteMark(); + + SwTextNode* pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode(); + OSL_ENSURE( pNd, "Dude, where's my TextNode?" ); + + SwAutoCorrExceptWord* pACEWord = pDoc->GetAutoCorrExceptWord(); + if( pACEWord ) + { + if ((1 == m_sIns.getLength()) && (1 == m_sOld.getLength())) + { + SwPosition aPos( *pNd ); aPos.nContent.Assign( pNd, m_nSttCnt ); + pACEWord->CheckChar( aPos, m_sOld[ 0 ] ); + } + pDoc->SetAutoCorrExceptWord( nullptr ); + } + + // don't look at m_sIns for deletion, maybe it was not completely inserted + { + rPam.GetPoint()->nNode = *pNd; + rPam.GetPoint()->nContent.Assign( pNd, m_nSttCnt ); + rPam.SetMark(); + rPam.GetPoint()->nNode = m_nSttNd - m_nOffset; + rPam.GetPoint()->nContent.Assign(rPam.GetContentNode(), m_nSttNd == m_nEndNd ? m_nEndCnt : pNd->Len()); + + // replace only in start node, without regex + bool const ret = pDoc->getIDocumentContentOperations().ReplaceRange(rPam, m_sOld, false); + assert(ret); (void)ret; + if (m_nSttNd != m_nEndNd) + { // in case of regex inserting paragraph breaks, join nodes... + assert(rPam.GetMark()->nContent == rPam.GetMark()->nNode.GetNode().GetTextNode()->Len()); + rPam.GetPoint()->nNode = m_nEndNd - m_nOffset; + rPam.GetPoint()->nContent.Assign(rPam.GetContentNode(true), m_nEndCnt); + pDoc->getIDocumentContentOperations().DeleteAndJoin(rPam); + } + rPam.DeleteMark(); + pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode(); + OSL_ENSURE( pNd, "Dude, where's my TextNode?" ); + } + + if( m_bSplitNext ) + { + SwPosition aPos(*pNd, pNd->Len()); + pDoc->getIDocumentContentOperations().SplitNode( aPos, false ); + pNd->RestoreMetadata(m_pMetadataUndoEnd); + pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode(); + // METADATA: restore + pNd->RestoreMetadata(m_pMetadataUndoStart); + } + + if( m_pHistory ) + { + if( pNd->GetpSwpHints() ) + pNd->ClearSwpHintsArr( true ); + + m_pHistory->TmpRollback( pDoc, m_nSetPos, false ); + if ( m_nSetPos ) // there were footnotes/FlyFrames + { + // are there others than these? + if( m_nSetPos < m_pHistory->Count() ) + { + // than save those attributes as well + SwHistory aHstr; + aHstr.Move( 0, m_pHistory.get(), m_nSetPos ); + m_pHistory->Rollback( pDoc ); + m_pHistory->Move( 0, &aHstr ); + } + else + { + m_pHistory->Rollback( pDoc ); + m_pHistory.reset(); + } + } + } + + rPam.GetPoint()->nNode = m_nSttNd; + rPam.GetPoint()->nContent = m_nSttCnt; +} + +void SwUndoReplace::Impl::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPaM & rPam(rContext.GetCursorSupplier().CreateNewShellCursor()); + rPam.DeleteMark(); + rPam.GetPoint()->nNode = m_nSttNd; + + SwTextNode* pNd = rPam.GetPoint()->nNode.GetNode().GetTextNode(); + OSL_ENSURE( pNd, "Dude, where's my TextNode?" ); + rPam.GetPoint()->nContent.Assign( pNd, m_nSttCnt ); + rPam.SetMark(); + if( m_bSplitNext ) + { + rPam.GetPoint()->nNode = m_nSttNd + 1; + pNd = rPam.GetPoint()->nNode.GetNode().GetTextNode(); + } + rPam.GetPoint()->nContent.Assign( pNd, m_nSelEnd ); + + if( m_pHistory ) + { + auto xSave = std::make_unique<SwHistory>(); + std::swap(m_pHistory, xSave); + + DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace); + m_nSetPos = m_pHistory->Count(); + + std::swap(xSave, m_pHistory); + m_pHistory->Move(0, xSave.get()); + } + else + { + m_pHistory.reset( new SwHistory ); + DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace); + m_nSetPos = m_pHistory->Count(); + if( !m_nSetPos ) + { + m_pHistory.reset(); + } + } + + rDoc.getIDocumentContentOperations().ReplaceRange( rPam, m_sIns, m_bRegExp ); + rPam.DeleteMark(); +} + +void SwUndoReplace::Impl::SetEnd(SwPaM const& rPam) +{ + const SwPosition* pEnd = rPam.End(); + m_nEndNd = m_nOffset + pEnd->nNode.GetIndex(); + m_nEndCnt = pEnd->nContent.GetIndex(); +} + +SwUndoReRead::SwUndoReRead( const SwPaM& rPam, const SwGrfNode& rGrfNd ) + : SwUndo( SwUndoId::REREAD, rPam.GetDoc() ), mnPosition( rPam.GetPoint()->nNode.GetIndex() ) +{ + SaveGraphicData( rGrfNd ); +} + +SwUndoReRead::~SwUndoReRead() +{ +} + +void SwUndoReRead::SetAndSave(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwGrfNode* pGrfNd = rDoc.GetNodes()[ mnPosition ]->GetGrfNode(); + + if( !pGrfNd ) + return ; + + // cache the old values + std::unique_ptr<Graphic> pOldGrf( mpGraphic ? new Graphic(*mpGraphic) : nullptr); + std::optional<OUString> aOldNm = maNm; + MirrorGraph nOldMirr = mnMirror; + // since all of them are cleared/modified by SaveGraphicData: + SaveGraphicData( *pGrfNd ); + + if( aOldNm ) + { + pGrfNd->ReRead( *aOldNm, maFltr ? *maFltr : OUString() ); + } + else + { + pGrfNd->ReRead( OUString(), OUString(), pOldGrf.get() ); + } + + if( MirrorGraph::Dont != nOldMirr ) + pGrfNd->SetAttr( SwMirrorGrf() ); + + rContext.SetSelections(pGrfNd->GetFlyFormat(), nullptr); +} + +void SwUndoReRead::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SetAndSave(rContext); +} + +void SwUndoReRead::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SetAndSave(rContext); +} + +void SwUndoReRead::SaveGraphicData( const SwGrfNode& rGrfNd ) +{ + if( rGrfNd.IsGrfLink() ) + { + maNm = OUString(); + maFltr = OUString(); + rGrfNd.GetFileFilterNms(&*maNm, &*maFltr); + mpGraphic.reset(); + } + else + { + mpGraphic.reset( new Graphic( rGrfNd.GetGrf(true) ) ); + maNm.reset(); + maFltr.reset(); + } + mnMirror = rGrfNd.GetSwAttrSet().GetMirrorGrf().GetValue(); +} + +SwUndoInsertLabel::SwUndoInsertLabel( const SwLabelType eTyp, + const OUString &rText, + const OUString& rSeparator, + const OUString& rNumberSeparator, + const bool bBef, + const sal_uInt16 nInitId, + const OUString& rCharacterStyle, + const bool bCpyBorder, + const SwDoc* pDoc ) + : SwUndo( SwUndoId::INSERTLABEL, pDoc ), + m_sText( rText ), + m_sSeparator( rSeparator ), + m_sNumberSeparator( rNumberSeparator ),//#i61007# order of captions + m_sCharacterStyle( rCharacterStyle ), + m_nFieldId( nInitId ), + m_eType( eTyp ), + m_nLayerId( 0 ), + m_bBefore( bBef ), + m_bCopyBorder( bCpyBorder ) +{ + m_bUndoKeep = false; + OBJECT.pUndoFly = nullptr; + OBJECT.pUndoAttr = nullptr; +} + +SwUndoInsertLabel::~SwUndoInsertLabel() +{ + if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType ) + { + delete OBJECT.pUndoFly; + delete OBJECT.pUndoAttr; + } + else + delete NODE.pUndoInsNd; +} + +void SwUndoInsertLabel::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType ) + { + OSL_ENSURE( OBJECT.pUndoAttr && OBJECT.pUndoFly, "Pointer not initialized" ); + SwFrameFormat* pFormat; + SdrObject *pSdrObj = nullptr; + if( OBJECT.pUndoAttr && + nullptr != (pFormat = static_cast<SwFrameFormat*>(OBJECT.pUndoAttr->GetFormat( rDoc ))) && + ( SwLabelType::Draw != m_eType || + nullptr != (pSdrObj = pFormat->FindSdrObject()) ) ) + { + OBJECT.pUndoAttr->UndoImpl(rContext); + OBJECT.pUndoFly->UndoImpl(rContext); + if( SwLabelType::Draw == m_eType ) + { + pSdrObj->SetLayer( m_nLayerId ); + } + } + } + else if( NODE.nNode ) + { + if ( m_eType == SwLabelType::Table && m_bUndoKeep ) + { + SwTableNode *pNd = rDoc.GetNodes()[ + rDoc.GetNodes()[NODE.nNode-1]->StartOfSectionIndex()]->GetTableNode(); + if ( pNd ) + pNd->GetTable().GetFrameFormat()->ResetFormatAttr( RES_KEEP ); + } + SwPaM aPam( rDoc.GetNodes().GetEndOfContent() ); + aPam.GetPoint()->nNode = NODE.nNode; + aPam.SetMark(); + aPam.GetPoint()->nNode = NODE.nNode + 1; + NODE.pUndoInsNd = new SwUndoDelete( aPam, true ); + } +} + +void SwUndoInsertLabel::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType ) + { + OSL_ENSURE( OBJECT.pUndoAttr && OBJECT.pUndoFly, "Pointer not initialized" ); + SwFrameFormat* pFormat; + SdrObject *pSdrObj = nullptr; + if( OBJECT.pUndoAttr && + nullptr != (pFormat = static_cast<SwFrameFormat*>(OBJECT.pUndoAttr->GetFormat( rDoc ))) && + ( SwLabelType::Draw != m_eType || + nullptr != (pSdrObj = pFormat->FindSdrObject()) ) ) + { + OBJECT.pUndoFly->RedoImpl(rContext); + OBJECT.pUndoAttr->RedoImpl(rContext); + if( SwLabelType::Draw == m_eType ) + { + pSdrObj->SetLayer( m_nLayerId ); + if( pSdrObj->GetLayer() == rDoc.getIDocumentDrawModelAccess().GetHellId() ) + pSdrObj->SetLayer( rDoc.getIDocumentDrawModelAccess().GetHeavenId() ); + // OD 02.07.2003 #108784# + else if( pSdrObj->GetLayer() == rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId() ) + pSdrObj->SetLayer( rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId() ); + } + } + } + else if( NODE.pUndoInsNd ) + { + if ( m_eType == SwLabelType::Table && m_bUndoKeep ) + { + SwTableNode *pNd = rDoc.GetNodes()[ + rDoc.GetNodes()[NODE.nNode-1]->StartOfSectionIndex()]->GetTableNode(); + if ( pNd ) + pNd->GetTable().GetFrameFormat()->SetFormatAttr( SvxFormatKeepItem(true, RES_KEEP) ); + } + NODE.pUndoInsNd->UndoImpl(rContext); + delete NODE.pUndoInsNd; + NODE.pUndoInsNd = nullptr; + } +} + +void SwUndoInsertLabel::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + const SwPosition& rPos = *rContext.GetRepeatPaM().GetPoint(); + + sal_uLong nIdx = 0; + + SwContentNode* pCNd = rPos.nNode.GetNode().GetContentNode(); + if( pCNd ) + switch( m_eType ) + { + case SwLabelType::Table: + { + const SwTableNode* pTNd = pCNd->FindTableNode(); + if( pTNd ) + nIdx = pTNd->GetIndex(); + } + break; + + case SwLabelType::Fly: + case SwLabelType::Object: + { + SwFlyFrame* pFly; + SwContentFrame *pCnt = pCNd->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ); + if( pCnt && nullptr != ( pFly = pCnt->FindFlyFrame() ) ) + nIdx = pFly->GetFormat()->GetContent().GetContentIdx()->GetIndex(); + } + break; + case SwLabelType::Draw: + break; + } + + if( nIdx ) + { + rDoc.InsertLabel( m_eType, m_sText, m_sSeparator, m_sNumberSeparator, m_bBefore, + m_nFieldId, nIdx, m_sCharacterStyle, m_bCopyBorder ); + } +} + +SwRewriter SwUndoInsertLabel::GetRewriter() const +{ + return CreateRewriter(m_sText); +} + +SwRewriter SwUndoInsertLabel::CreateRewriter(const OUString &rStr) +{ + SwRewriter aRewriter; + + OUString aTmpStr; + + if (!rStr.isEmpty()) + { + aTmpStr += SwResId(STR_START_QUOTE); + aTmpStr += ShortenString(rStr, nUndoStringLength, + SwResId(STR_LDOTS)); + aTmpStr += SwResId(STR_END_QUOTE); + } + + aRewriter.AddRule(UndoArg1, aTmpStr); + + return aRewriter; +} + +void SwUndoInsertLabel::SetFlys( SwFrameFormat& rOldFly, SfxItemSet const & rChgSet, + SwFrameFormat& rNewFly ) +{ + if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType ) + { + SwUndoFormatAttrHelper aTmp( rOldFly, false ); + rOldFly.SetFormatAttr( rChgSet ); + if ( aTmp.GetUndo() ) + { + OBJECT.pUndoAttr = aTmp.ReleaseUndo().release(); + } + OBJECT.pUndoFly = new SwUndoInsLayFormat( &rNewFly,0,0 ); + } +} + +void SwUndoInsertLabel::SetDrawObj( SdrLayerID nLId ) +{ + if( SwLabelType::Draw == m_eType ) + { + m_nLayerId = nLId; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unmove.cxx b/sw/source/core/undo/unmove.cxx new file mode 100644 index 000000000..6888d477f --- /dev/null +++ b/sw/source/core/undo/unmove.cxx @@ -0,0 +1,308 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoSplitMove.hxx> +#include <doc.hxx> +#include <pam.hxx> +#include <swundo.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> + +// MOVE +SwUndoMove::SwUndoMove( const SwPaM& rRange, const SwPosition& rMvPos ) + : SwUndo( SwUndoId::MOVE, rRange.GetDoc() ) + , SwUndRng( rRange ) + , m_nDestStartNode(0) + , m_nDestEndNode(0) + , m_nInsPosNode(0) + , m_nMoveDestNode(rMvPos.nNode.GetIndex()) + , m_nDestStartContent(0) + , m_nDestEndContent(0) + , m_nInsPosContent(0) + , m_nMoveDestContent(rMvPos.nContent.GetIndex()) + , m_bJoinNext(false) + , m_bMoveRange(false) + , m_bMoveRedlines(false) +{ + // get StartNode from footnotes before delete! + SwDoc* pDoc = rRange.GetDoc(); + SwTextNode* pTextNd = pDoc->GetNodes()[ m_nSttNode ]->GetTextNode(); + SwTextNode* pEndTextNd = pDoc->GetNodes()[ m_nEndNode ]->GetTextNode(); + + m_pHistory.reset( new SwHistory ); + + if( pTextNd ) + { + m_pHistory->Add( pTextNd->GetTextColl(), m_nSttNode, SwNodeType::Text ); + if ( pTextNd->GetpSwpHints() ) + { + m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nSttNode, + 0, pTextNd->GetText().getLength(), false ); + } + if( pTextNd->HasSwAttrSet() ) + m_pHistory->CopyFormatAttr( *pTextNd->GetpSwAttrSet(), m_nSttNode ); + } + if( pEndTextNd && pEndTextNd != pTextNd ) + { + m_pHistory->Add( pEndTextNd->GetTextColl(), m_nEndNode, SwNodeType::Text ); + if ( pEndTextNd->GetpSwpHints() ) + { + m_pHistory->CopyAttr( pEndTextNd->GetpSwpHints(), m_nEndNode, + 0, pEndTextNd->GetText().getLength(), false ); + } + if( pEndTextNd->HasSwAttrSet() ) + m_pHistory->CopyFormatAttr( *pEndTextNd->GetpSwAttrSet(), m_nEndNode ); + } + + pTextNd = rMvPos.nNode.GetNode().GetTextNode(); + if (nullptr != pTextNd) + { + m_pHistory->Add( pTextNd->GetTextColl(), m_nMoveDestNode, SwNodeType::Text ); + if ( pTextNd->GetpSwpHints() ) + { + m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nMoveDestNode, + 0, pTextNd->GetText().getLength(), false ); + } + if( pTextNd->HasSwAttrSet() ) + m_pHistory->CopyFormatAttr( *pTextNd->GetpSwAttrSet(), m_nMoveDestNode ); + } + + m_nFootnoteStart = m_pHistory->Count(); + DelFootnote( rRange ); + + if( m_pHistory && !m_pHistory->Count() ) + m_pHistory.reset(); +} + +SwUndoMove::SwUndoMove( SwDoc* pDoc, const SwNodeRange& rRg, + const SwNodeIndex& rMvPos ) + : SwUndo(SwUndoId::MOVE, pDoc) + , m_nDestStartNode(0) + , m_nDestEndNode(0) + , m_nInsPosNode(0) + , m_nMoveDestNode(rMvPos.GetIndex()) + , m_nDestStartContent(0) + , m_nDestEndContent(0) + , m_nInsPosContent(0) + , m_bMoveRedlines(false) +{ + m_bMoveRange = true; + m_bJoinNext = false; + + m_nSttContent = m_nEndContent = m_nMoveDestContent = COMPLETE_STRING; + + m_nSttNode = rRg.aStart.GetIndex(); + m_nEndNode = rRg.aEnd.GetIndex(); + +// DelFootnote( rRange ); +// FIXME: duplication of the method body of DelFootnote below + + // is the current move from ContentArea into the special section? + sal_uLong nContentStt = pDoc->GetNodes().GetEndOfAutotext().GetIndex(); + if( m_nMoveDestNode < nContentStt && rRg.aStart.GetIndex() > nContentStt ) + { + // delete all footnotes since they are undesired there + SwPosition aPtPos( rRg.aEnd ); + SwContentNode* pCNd = rRg.aEnd.GetNode().GetContentNode(); + if( pCNd ) + aPtPos.nContent.Assign( pCNd, pCNd->Len() ); + SwPosition aMkPos( rRg.aStart ); + if( nullptr != ( pCNd = aMkPos.nNode.GetNode().GetContentNode() )) + aMkPos.nContent.Assign( pCNd, 0 ); + + DelContentIndex( aMkPos, aPtPos, DelContentType::Ftn ); + + if( m_pHistory && !m_pHistory->Count() ) + m_pHistory.reset(); + } + + m_nFootnoteStart = 0; +} + +void SwUndoMove::SetDestRange( const SwNodeIndex& rStt, + const SwNodeIndex& rEnd, + const SwNodeIndex& rInsPos ) +{ + m_nDestStartNode = rStt.GetIndex(); + m_nDestEndNode = rEnd.GetIndex(); + if( m_nDestStartNode > m_nDestEndNode ) + { + m_nDestStartNode = m_nDestEndNode; + m_nDestEndNode = rStt.GetIndex(); + } + m_nInsPosNode = rInsPos.GetIndex(); + + m_nDestStartContent = m_nDestEndContent = m_nInsPosContent = COMPLETE_STRING; +} + +void SwUndoMove::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + + // Block so that we can jump out of it + do { + // create index position and section based on the existing values + SwNodeIndex aIdx( pDoc->GetNodes(), m_nDestStartNode ); + + if( m_bMoveRange ) + { + // only a move with SwRange + SwNodeRange aRg( aIdx, aIdx ); + aRg.aEnd = m_nDestEndNode; + aIdx = m_nInsPosNode; + bool bSuccess = pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aIdx, + SwMoveFlags::DEFAULT ); + if (!bSuccess) + break; + } + else + { + SwPaM aPam( aIdx.GetNode(), m_nDestStartContent, + *pDoc->GetNodes()[ m_nDestEndNode ], m_nDestEndContent ); + + // #i17764# if redlines are to be moved, we may not remove them + // before pDoc->Move gets a chance to handle them + if( ! m_bMoveRedlines ) + RemoveIdxFromRange( aPam, false ); + + SwPosition aPos( *pDoc->GetNodes()[ m_nInsPosNode] ); + SwContentNode* pCNd = aPos.nNode.GetNode().GetContentNode(); + aPos.nContent.Assign( pCNd, m_nInsPosContent ); + + if( pCNd->HasSwAttrSet() ) + pCNd->ResetAllAttr(); + + if( pCNd->IsTextNode() && static_cast<SwTextNode*>(pCNd)->GetpSwpHints() ) + static_cast<SwTextNode*>(pCNd)->ClearSwpHintsArr( false ); + + // first delete all attributes at InsertPos + const bool bSuccess = pDoc->getIDocumentContentOperations().MoveRange( aPam, aPos, m_bMoveRedlines + ? SwMoveFlags::REDLINES + : SwMoveFlags::DEFAULT ); + if (!bSuccess) + break; + + aPam.Exchange(); + aPam.DeleteMark(); + if( aPam.GetNode().IsContentNode() ) + aPam.GetNode().GetContentNode()->ResetAllAttr(); + // the Pam will be dropped now + } + + SwTextNode* pTextNd = aIdx.GetNode().GetTextNode(); + if( m_bJoinNext ) + { + { + RemoveIdxRel( aIdx.GetIndex() + 1, SwPosition( aIdx, + SwIndex(pTextNd, pTextNd->GetText().getLength()))); + } + // Are there any Pams in the next TextNode? + pTextNd->JoinNext(); + } + + } while( false ); + + if( m_pHistory ) + { + if( m_nFootnoteStart != m_pHistory->Count() ) + m_pHistory->Rollback( pDoc, m_nFootnoteStart ); + m_pHistory->TmpRollback( pDoc, 0 ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + } + + // set the cursor onto Undo area + if( !m_bMoveRange ) + { + AddUndoRedoPaM(rContext); + } +} + +void SwUndoMove::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM& rPam = AddUndoRedoPaM(rContext); + SwDoc& rDoc = rContext.GetDoc(); + + SwNodes& rNds = rDoc.GetNodes(); + SwNodeIndex aIdx( rNds, m_nMoveDestNode ); + + if( m_bMoveRange ) + { + // only a move with SwRange + SwNodeRange aRg( rNds, m_nSttNode, rNds, m_nEndNode ); + rDoc.getIDocumentContentOperations().MoveNodeRange( aRg, aIdx, m_bMoveRedlines + ? SwMoveFlags::REDLINES + : SwMoveFlags::DEFAULT ); + } + else + { + SwPaM aPam(*rPam.GetPoint()); + SetPaM( aPam ); + SwPosition aMvPos( aIdx, SwIndex( aIdx.GetNode().GetContentNode(), + m_nMoveDestContent )); + + DelFootnote( aPam ); + RemoveIdxFromRange( aPam, false ); + + aIdx = aPam.Start()->nNode; + bool bJoinText = aIdx.GetNode().IsTextNode(); + + --aIdx; + rDoc.getIDocumentContentOperations().MoveRange( aPam, aMvPos, + SwMoveFlags::DEFAULT ); + + if( m_nSttNode != m_nEndNode && bJoinText ) + { + ++aIdx; + SwTextNode * pTextNd = aIdx.GetNode().GetTextNode(); + if( pTextNd && pTextNd->CanJoinNext() ) + { + { + RemoveIdxRel( aIdx.GetIndex() + 1, SwPosition( aIdx, + SwIndex(pTextNd, pTextNd->GetText().getLength()))); + } + pTextNd->JoinNext(); + } + } + *rPam.GetPoint() = *aPam.GetPoint(); + rPam.SetMark(); + *rPam.GetMark() = *aPam.GetMark(); + } +} + +void SwUndoMove::DelFootnote( const SwPaM& rRange ) +{ + // is the current move from ContentArea into the special section? + SwDoc* pDoc = rRange.GetDoc(); + sal_uLong nContentStt = pDoc->GetNodes().GetEndOfAutotext().GetIndex(); + if( m_nMoveDestNode < nContentStt && + rRange.GetPoint()->nNode.GetIndex() >= nContentStt ) + { + // delete all footnotes since they are undesired there + DelContentIndex( *rRange.GetMark(), *rRange.GetPoint(), + DelContentType::Ftn ); + + if( m_pHistory && !m_pHistory->Count() ) + { + m_pHistory.reset(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unnum.cxx b/sw/source/core/undo/unnum.cxx new file mode 100644 index 000000000..a4e4b39c0 --- /dev/null +++ b/sw/source/core/undo/unnum.cxx @@ -0,0 +1,395 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoNumbering.hxx> +#include <doc.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> +#include <osl/diagnose.h> + +SwUndoInsNum::SwUndoInsNum( const SwNumRule& rOldRule, + const SwNumRule& rNewRule, + const SwDoc* pDoc, + SwUndoId nUndoId) + : SwUndo( nUndoId, pDoc ), + m_aNumRule( rNewRule ), + m_pOldNumRule( new SwNumRule( rOldRule )), m_nLRSavePos( 0 ) +{ +} + +SwUndoInsNum::SwUndoInsNum( const SwPaM& rPam, const SwNumRule& rRule ) + : SwUndo( SwUndoId::INSNUM, rPam.GetDoc() ), SwUndRng( rPam ), + m_aNumRule( rRule ), + m_nLRSavePos( 0 ) +{ +} + +SwUndoInsNum::SwUndoInsNum( const SwPosition& rPos, const SwNumRule& rRule, + const OUString& rReplaceRule ) + : SwUndo( SwUndoId::INSNUM, rPos.nNode.GetNode().GetDoc() ), + m_aNumRule( rRule ), + m_sReplaceRule( rReplaceRule ), m_nLRSavePos( 0 ) +{ + // No selection! + m_nEndNode = 0; + m_nEndContent = COMPLETE_STRING; + m_nSttNode = rPos.nNode.GetIndex(); + m_nSttContent = rPos.nContent.GetIndex(); +} + +SwUndoInsNum::~SwUndoInsNum() +{ + m_pHistory.reset(); + m_pOldNumRule.reset(); +} + +SwRewriter SwUndoInsNum::GetRewriter() const +{ + SwRewriter aResult; + if( SwUndoId::INSFMTATTR == GetId() ) + aResult.AddRule(UndoArg1, m_aNumRule.GetName()); + return aResult; +} + +void SwUndoInsNum::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + if( m_pOldNumRule ) + rDoc.ChgNumRuleFormats( *m_pOldNumRule ); + + if( m_pHistory ) + { + if( m_nLRSavePos ) + { + // Update immediately so that potential "old" LRSpaces will be valid again. + m_pHistory->TmpRollback( &rDoc, m_nLRSavePos ); + + } + m_pHistory->TmpRollback( &rDoc, 0 ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + } + + if (m_nSttNode) + { + AddUndoRedoPaM(rContext); + } +} + +void SwUndoInsNum::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + if ( m_pOldNumRule ) + rDoc.ChgNumRuleFormats( m_aNumRule ); + else if ( m_pHistory ) + { + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + if( !m_sReplaceRule.isEmpty() ) + { + rDoc.ReplaceNumRule( *rPam.GetPoint(), m_sReplaceRule, m_aNumRule.GetName() ); + } + else + { + // #i42921# - adapt to changed signature + rDoc.SetNumRule(rPam, m_aNumRule, false); + } + } +} + +void SwUndoInsNum::SetLRSpaceEndPos() +{ + if( m_pHistory ) + m_nLRSavePos = m_pHistory->Count(); +} + +void SwUndoInsNum::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc( rContext.GetDoc() ); + if ( m_nSttNode ) + { + if( m_sReplaceRule.isEmpty() ) + { + // #i42921# - adapt to changed signature + rDoc.SetNumRule(rContext.GetRepeatPaM(), m_aNumRule, false); + } + } + else + { + rDoc.ChgNumRuleFormats( m_aNumRule ); + } +} + +SwHistory* SwUndoInsNum::GetHistory() +{ + if( !m_pHistory ) + m_pHistory.reset(new SwHistory); + return m_pHistory.get(); +} + +void SwUndoInsNum::SaveOldNumRule( const SwNumRule& rOld ) +{ + if( !m_pOldNumRule ) + m_pOldNumRule.reset(new SwNumRule( rOld )); +} + +SwUndoDelNum::SwUndoDelNum( const SwPaM& rPam ) + : SwUndo( SwUndoId::DELNUM, rPam.GetDoc() ), SwUndRng( rPam ) +{ + m_aNodes.reserve( std::min<sal_uLong>(m_nEndNode - m_nSttNode, 255) ); + m_pHistory.reset( new SwHistory ); +} + +SwUndoDelNum::~SwUndoDelNum() +{ +} + +void SwUndoDelNum::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + m_pHistory->TmpRollback( &rDoc, 0 ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + + for( const auto& rNode : m_aNodes ) + { + SwTextNode* pNd = rDoc.GetNodes()[ rNode.index ]->GetTextNode(); + OSL_ENSURE( pNd, "Where has the TextNode gone?" ); + pNd->SetAttrListLevel( rNode.level ); + + if( pNd->GetCondFormatColl() ) + pNd->ChkCondColl(); + } + + AddUndoRedoPaM(rContext); +} + +void SwUndoDelNum::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + rContext.GetDoc().DelNumRules(rPam); +} + +void SwUndoDelNum::RepeatImpl(::sw::RepeatContext & rContext) +{ + rContext.GetDoc().DelNumRules(rContext.GetRepeatPaM()); +} + +void SwUndoDelNum::AddNode( const SwTextNode& rNd ) +{ + if( rNd.GetNumRule() ) + { + m_aNodes.emplace_back( rNd.GetIndex(), rNd.GetActualListLevel() ); + } +} + +SwUndoMoveNum::SwUndoMoveNum( const SwPaM& rPam, long nOff, bool bIsOutlMv ) + : SwUndo( bIsOutlMv ? SwUndoId::OUTLINE_UD : SwUndoId::MOVENUM, rPam.GetDoc() ), + SwUndRng( rPam ), + nNewStt( 0 ), nOffset( nOff ) +{ + // nOffset: Down => 1 + // Up => -1 +} + +void SwUndoMoveNum::UndoImpl(::sw::UndoRedoContext & rContext) +{ + sal_uLong nTmpStt = m_nSttNode, nTmpEnd = m_nEndNode; + + if (m_nEndNode || m_nEndContent != COMPLETE_STRING) // section? + { + if( nNewStt < m_nSttNode ) // moved forwards + m_nEndNode = m_nEndNode - ( m_nSttNode - nNewStt ); + else + m_nEndNode = m_nEndNode + ( nNewStt - m_nSttNode ); + } + m_nSttNode = nNewStt; + + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + rContext.GetDoc().MoveParagraph( rPam, -nOffset, + SwUndoId::OUTLINE_UD == GetId() ); + m_nSttNode = nTmpStt; + m_nEndNode = nTmpEnd; +} + +void SwUndoMoveNum::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + rContext.GetDoc().MoveParagraph(rPam, nOffset, SwUndoId::OUTLINE_UD == GetId()); +} + +void SwUndoMoveNum::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if( SwUndoId::OUTLINE_UD == GetId() ) + { + rDoc.MoveOutlinePara(rContext.GetRepeatPaM(), + 0 < nOffset ? 1 : -1 ); + } + else + { + rDoc.MoveParagraph(rContext.GetRepeatPaM(), nOffset); + } +} + +SwUndoNumUpDown::SwUndoNumUpDown( const SwPaM& rPam, short nOff ) + : SwUndo( nOff > 0 ? SwUndoId::NUMUP : SwUndoId::NUMDOWN, rPam.GetDoc() ), + SwUndRng( rPam ), + nOffset( nOff ) +{ + // nOffset: Down => 1 + // Up => -1 +} + +void SwUndoNumUpDown::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + rContext.GetDoc().NumUpDown(rPam, 1 != nOffset ); +} + +void SwUndoNumUpDown::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + rContext.GetDoc().NumUpDown(rPam, 1 == nOffset); +} + +void SwUndoNumUpDown::RepeatImpl(::sw::RepeatContext & rContext) +{ + rContext.GetDoc().NumUpDown(rContext.GetRepeatPaM(), 1 == nOffset); +} + +SwUndoNumOrNoNum::SwUndoNumOrNoNum( const SwNodeIndex& rIdx, bool bOldNum, + bool bNewNum) + : SwUndo( SwUndoId::NUMORNONUM, rIdx.GetNode().GetDoc() ), + nIdx( rIdx.GetIndex() ), mbNewNum(bNewNum), + mbOldNum(bOldNum) +{ +} + +// #115901#, #i40034# +void SwUndoNumOrNoNum::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwNodeIndex aIdx( rContext.GetDoc().GetNodes(), nIdx ); + SwTextNode * pTextNd = aIdx.GetNode().GetTextNode(); + + if (nullptr != pTextNd) + { + pTextNd->SetCountedInList(mbOldNum); + } +} + +// #115901#, #i40034# +void SwUndoNumOrNoNum::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwNodeIndex aIdx( rContext.GetDoc().GetNodes(), nIdx ); + SwTextNode * pTextNd = aIdx.GetNode().GetTextNode(); + + if (nullptr != pTextNd) + { + pTextNd->SetCountedInList(mbNewNum); + } +} + +void SwUndoNumOrNoNum::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if (mbOldNum && ! mbNewNum) + { + rDoc.NumOrNoNum(rContext.GetRepeatPaM().GetPoint()->nNode); + } + else if ( ! mbOldNum && mbNewNum ) + { + rDoc.NumOrNoNum(rContext.GetRepeatPaM().GetPoint()->nNode, true); + } +} + +SwUndoNumRuleStart::SwUndoNumRuleStart( const SwPosition& rPos, bool bFlg ) + : SwUndo( SwUndoId::SETNUMRULESTART, rPos.GetDoc() ), + nIdx( rPos.nNode.GetIndex() ), nOldStt( USHRT_MAX ), + nNewStt( USHRT_MAX ), bSetSttValue( false ), bFlag( bFlg ) +{ +} + +SwUndoNumRuleStart::SwUndoNumRuleStart( const SwPosition& rPos, sal_uInt16 nStt ) + : SwUndo(SwUndoId::SETNUMRULESTART, rPos.GetDoc()) + , nIdx(rPos.nNode.GetIndex()) + , nOldStt(USHRT_MAX) + , nNewStt(nStt) + , bSetSttValue(true) + , bFlag(false) +{ + SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode(); + if ( pTextNd ) + { + if ( pTextNd->HasAttrListRestartValue() ) + { + nOldStt = static_cast<sal_uInt16>(pTextNd->GetAttrListRestartValue()); + } + else + { + nOldStt = USHRT_MAX; // indicating, that the list restart value is not set + } + } +} + +void SwUndoNumRuleStart::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPosition const aPos( *rDoc.GetNodes()[ nIdx ] ); + if( bSetSttValue ) + { + rDoc.SetNodeNumStart( aPos, nOldStt ); + } + else + { + rDoc.SetNumRuleStart( aPos, !bFlag ); + } +} + +void SwUndoNumRuleStart::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPosition const aPos( *rDoc.GetNodes()[ nIdx ] ); + if( bSetSttValue ) + { + rDoc.SetNodeNumStart( aPos, nNewStt ); + } + else + { + rDoc.SetNumRuleStart( aPos, bFlag ); + } +} + +void SwUndoNumRuleStart::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if( bSetSttValue ) + { + rDoc.SetNodeNumStart(*rContext.GetRepeatPaM().GetPoint(), nNewStt); + } + else + { + rDoc.SetNumRuleStart(*rContext.GetRepeatPaM().GetPoint(), bFlag); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unoutl.cxx b/sw/source/core/undo/unoutl.cxx new file mode 100644 index 000000000..04341d61a --- /dev/null +++ b/sw/source/core/undo/unoutl.cxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <doc.hxx> +#include <swundo.hxx> +#include <pam.hxx> + +#include <UndoCore.hxx> + +SwUndoOutlineLeftRight::SwUndoOutlineLeftRight( const SwPaM& rPam, + short nOff ) + : SwUndo( SwUndoId::OUTLINE_LR, rPam.GetDoc() ), SwUndRng( rPam ), m_nOffset( nOff ) +{ +} + +void SwUndoOutlineLeftRight::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPaM( AddUndoRedoPaM(rContext) ); + rContext.GetDoc().OutlineUpDown(rPaM, -m_nOffset); +} + +void SwUndoOutlineLeftRight::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPaM( AddUndoRedoPaM(rContext) ); + rContext.GetDoc().OutlineUpDown(rPaM, m_nOffset); +} + +void SwUndoOutlineLeftRight::RepeatImpl(::sw::RepeatContext & rContext) +{ + rContext.GetDoc().OutlineUpDown(rContext.GetRepeatPaM(), m_nOffset); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unovwr.cxx b/sw/source/core/undo/unovwr.cxx new file mode 100644 index 000000000..7361da5a4 --- /dev/null +++ b/sw/source/core/undo/unovwr.cxx @@ -0,0 +1,474 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoOverwrite.hxx> +#include <unotools/charclass.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <comphelper/processfactory.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IShellCursorSupplier.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> +#include <acorrect.hxx> +#include <docary.hxx> +#include <strings.hrc> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::uno; + +SwUndoOverwrite::SwUndoOverwrite( SwDoc* pDoc, SwPosition& rPos, + sal_Unicode cIns ) + : SwUndo(SwUndoId::OVERWRITE, pDoc), + bGroup( false ) +{ + SwTextNode *const pTextNd = rPos.nNode.GetNode().GetTextNode(); + assert(pTextNd); + sal_Int32 const nTextNdLen = pTextNd->GetText().getLength(); + + nSttNode = rPos.nNode.GetIndex(); + nSttContent = rPos.nContent.GetIndex(); + + if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + { + SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(), + rPos.nNode, rPos.nContent.GetIndex()+1 ); + pRedlSaveData.reset( new SwRedlineSaveDatas ); + if( !FillSaveData( aPam, *pRedlSaveData, false )) + { + pRedlSaveData.reset(); + } + if (nSttContent < nTextNdLen) + { + pDoc->getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Any); + } + } + + bInsChar = true; + if( nSttContent < nTextNdLen ) // no pure insert? + { + aDelStr += OUStringChar( pTextNd->GetText()[nSttContent] ); + if( !m_pHistory ) + m_pHistory.reset( new SwHistory ); + SwRegHistory aRHst( *pTextNd, m_pHistory.get() ); + m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), nSttNode, 0, + nTextNdLen, false ); + ++rPos.nContent; + bInsChar = false; + } + + bool bOldExpFlg = pTextNd->IsIgnoreDontExpand(); + pTextNd->SetIgnoreDontExpand( true ); + + pTextNd->InsertText( OUString(cIns), rPos.nContent, + SwInsertFlags::EMPTYEXPAND ); + aInsStr += OUStringChar( cIns ); + + if( !bInsChar ) + { + const SwIndex aTmpIndex( rPos.nContent, -2 ); + pTextNd->EraseText( aTmpIndex, 1 ); + } + pTextNd->SetIgnoreDontExpand( bOldExpFlg ); + + m_bCacheComment = false; +} + +SwUndoOverwrite::~SwUndoOverwrite() +{ +} + +bool SwUndoOverwrite::CanGrouping( SwDoc* pDoc, SwPosition& rPos, + sal_Unicode cIns ) +{ +// What is with only inserted characters? + + // Only deletion of single chars can be combined. + if( rPos.nNode != nSttNode || aInsStr.isEmpty() || + ( !bGroup && aInsStr.getLength() != 1 )) + return false; + + // Is the node a TextNode at all? + SwTextNode * pDelTextNd = rPos.nNode.GetNode().GetTextNode(); + if( !pDelTextNd || + (pDelTextNd->GetText().getLength() != rPos.nContent.GetIndex() && + rPos.nContent.GetIndex() != ( nSttContent + aInsStr.getLength() ))) + return false; + + CharClass& rCC = GetAppCharClass(); + + // ask the char that should be inserted + if (( CH_TXTATR_BREAKWORD == cIns || CH_TXTATR_INWORD == cIns ) || + rCC.isLetterNumeric( OUString( cIns ), 0 ) != + rCC.isLetterNumeric( aInsStr, aInsStr.getLength()-1 ) ) + return false; + + if (!bInsChar && rPos.nContent.GetIndex() < pDelTextNd->GetText().getLength()) + { + SwRedlineSaveDatas aTmpSav; + SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(), + rPos.nNode, rPos.nContent.GetIndex()+1 ); + + const bool bSaved = FillSaveData( aPam, aTmpSav, false ); + + bool bOk = ( !pRedlSaveData && !bSaved ) || + ( pRedlSaveData && bSaved && + SwUndo::CanRedlineGroup( *pRedlSaveData, aTmpSav, + nSttContent > rPos.nContent.GetIndex() )); + // aTmpSav.DeleteAndDestroyAll(); + if( !bOk ) + return false; + + pDoc->getIDocumentRedlineAccess().DeleteRedline( aPam, false, RedlineType::Any ); + } + + // both 'overwrites' can be combined so 'move' the corresponding character + if( !bInsChar ) + { + if (rPos.nContent.GetIndex() < pDelTextNd->GetText().getLength()) + { + aDelStr += OUStringChar( pDelTextNd->GetText()[rPos.nContent.GetIndex()] ); + ++rPos.nContent; + } + else + bInsChar = true; + } + + bool bOldExpFlg = pDelTextNd->IsIgnoreDontExpand(); + pDelTextNd->SetIgnoreDontExpand( true ); + + OUString const ins( pDelTextNd->InsertText(OUString(cIns), rPos.nContent, + SwInsertFlags::EMPTYEXPAND) ); + assert(ins.getLength() == 1); // check in SwDoc::Overwrite => cannot fail + (void) ins; + aInsStr += OUStringChar( cIns ); + + if( !bInsChar ) + { + const SwIndex aTmpIndex( rPos.nContent, -2 ); + pDelTextNd->EraseText( aTmpIndex, 1 ); + } + pDelTextNd->SetIgnoreDontExpand( bOldExpFlg ); + + bGroup = true; + return true; +} + +void SwUndoOverwrite::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM *const pCurrentPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + pCurrentPam->DeleteMark(); + pCurrentPam->GetPoint()->nNode = nSttNode; + SwTextNode* pTextNd = pCurrentPam->GetNode().GetTextNode(); + assert(pTextNd); + SwIndex& rIdx = pCurrentPam->GetPoint()->nContent; + rIdx.Assign( pTextNd, nSttContent ); + + SwAutoCorrExceptWord* pACEWord = pDoc->GetAutoCorrExceptWord(); + if( pACEWord ) + { + if( 1 == aInsStr.getLength() && 1 == aDelStr.getLength() ) + pACEWord->CheckChar( *pCurrentPam->GetPoint(), aDelStr[0] ); + pDoc->SetAutoCorrExceptWord( nullptr ); + } + + // If there was not only an overwrite but also an insert, delete the surplus + if( aInsStr.getLength() > aDelStr.getLength() ) + { + rIdx += aDelStr.getLength(); + pTextNd->EraseText( rIdx, aInsStr.getLength() - aDelStr.getLength() ); + rIdx = nSttContent; + } + + if( !aDelStr.isEmpty() ) + { + bool bOldExpFlg = pTextNd->IsIgnoreDontExpand(); + pTextNd->SetIgnoreDontExpand( true ); + + ++rIdx; + for( sal_Int32 n = 0; n < aDelStr.getLength(); n++ ) + { + // do it individually, to keep the attributes! + OUString aTmpStr(aDelStr[n]); + OUString const ins( pTextNd->InsertText(aTmpStr, rIdx) ); + assert(ins.getLength() == 1); // cannot fail + (void) ins; + rIdx -= 2; + pTextNd->EraseText( rIdx, 1 ); + rIdx += 2; + } + pTextNd->SetIgnoreDontExpand( bOldExpFlg ); + --rIdx; + } + + if( m_pHistory ) + { + if( pTextNd->GetpSwpHints() ) + pTextNd->ClearSwpHintsArr( false ); + m_pHistory->TmpRollback( pDoc, 0, false ); + } + + if( pCurrentPam->GetMark()->nContent.GetIndex() != nSttContent ) + { + pCurrentPam->SetMark(); + pCurrentPam->GetMark()->nContent = nSttContent; + } + + if( pRedlSaveData ) + SetSaveData( *pDoc, *pRedlSaveData ); +} + +void SwUndoOverwrite::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwPaM *const pCurrentPam = & rContext.GetRepeatPaM(); + if( aInsStr.isEmpty() || pCurrentPam->HasMark() ) + return; + + SwDoc & rDoc = rContext.GetDoc(); + + { + ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); + rDoc.getIDocumentContentOperations().Overwrite(*pCurrentPam, OUString(aInsStr[0])); + } + for( sal_Int32 n = 1; n < aInsStr.getLength(); ++n ) + rDoc.getIDocumentContentOperations().Overwrite( *pCurrentPam, OUString(aInsStr[n]) ); +} + +void SwUndoOverwrite::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM *const pCurrentPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + pCurrentPam->DeleteMark(); + pCurrentPam->GetPoint()->nNode = nSttNode; + SwTextNode* pTextNd = pCurrentPam->GetNode().GetTextNode(); + assert(pTextNd); + SwIndex& rIdx = pCurrentPam->GetPoint()->nContent; + + if( pRedlSaveData ) + { + rIdx.Assign( pTextNd, nSttContent ); + pCurrentPam->SetMark(); + pCurrentPam->GetMark()->nContent += aDelStr.getLength(); + pDoc->getIDocumentRedlineAccess().DeleteRedline( *pCurrentPam, false, RedlineType::Any ); + pCurrentPam->DeleteMark(); + } + rIdx.Assign( pTextNd, !aDelStr.isEmpty() ? nSttContent+1 : nSttContent ); + + bool bOldExpFlg = pTextNd->IsIgnoreDontExpand(); + pTextNd->SetIgnoreDontExpand( true ); + + for( sal_Int32 n = 0; n < aInsStr.getLength(); n++ ) + { + // do it individually, to keep the attributes! + OUString const ins( + pTextNd->InsertText( OUString(aInsStr[n]), rIdx, + SwInsertFlags::EMPTYEXPAND) ); + assert(ins.getLength() == 1); // cannot fail + (void) ins; + if( n < aDelStr.getLength() ) + { + rIdx -= 2; + pTextNd->EraseText( rIdx, 1 ); + rIdx += n+1 < aDelStr.getLength() ? 2 : 1; + } + } + pTextNd->SetIgnoreDontExpand( bOldExpFlg ); + + // get back old start position from UndoNodes array + if( m_pHistory ) + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + if( pCurrentPam->GetMark()->nContent.GetIndex() != nSttContent ) + { + pCurrentPam->SetMark(); + pCurrentPam->GetMark()->nContent = nSttContent; + } +} + +SwRewriter SwUndoOverwrite::GetRewriter() const +{ + SwRewriter aResult; + + OUString aString = SwResId(STR_START_QUOTE) + + ShortenString(aInsStr, nUndoStringLength, SwResId(STR_LDOTS)) + + SwResId(STR_END_QUOTE); + + aResult.AddRule(UndoArg1, aString); + + return aResult; +} + +struct UndoTransliterate_Data +{ + OUString sText; + std::unique_ptr<SwHistory> pHistory; + std::unique_ptr<Sequence< sal_Int32 >> pOffsets; + sal_uLong nNdIdx; + sal_Int32 nStart, nLen; + + UndoTransliterate_Data( sal_uLong nNd, sal_Int32 nStt, sal_Int32 nStrLen, const OUString& rText ) + : sText( rText ), + nNdIdx( nNd ), nStart( nStt ), nLen( nStrLen ) + {} + + void SetChangeAtNode( SwDoc& rDoc ); +}; + +SwUndoTransliterate::SwUndoTransliterate( + const SwPaM& rPam, + const utl::TransliterationWrapper& rTrans ) + : SwUndo( SwUndoId::TRANSLITERATE, rPam.GetDoc() ), SwUndRng( rPam ), nType( rTrans.getType() ) +{ +} + +SwUndoTransliterate::~SwUndoTransliterate() +{ +} + +void SwUndoTransliterate::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + // since the changes were added to the vector from the end of the string/node towards + // the start, we need to revert them from the start towards the end now to keep the + // offset information of the undo data in sync with the changing text. + // Thus we need to iterate from the end of the vector to the start + for (sal_Int32 i = aChanges.size() - 1; i >= 0; --i) + aChanges[i]->SetChangeAtNode( rDoc ); + + AddUndoRedoPaM(rContext, true); +} + +void SwUndoTransliterate::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + DoTransliterate(rContext.GetDoc(), rPam); +} + +void SwUndoTransliterate::RepeatImpl(::sw::RepeatContext & rContext) +{ + DoTransliterate(rContext.GetDoc(), rContext.GetRepeatPaM()); +} + +void SwUndoTransliterate::DoTransliterate(SwDoc & rDoc, SwPaM const & rPam) +{ + utl::TransliterationWrapper aTrans( ::comphelper::getProcessComponentContext(), nType ); + rDoc.getIDocumentContentOperations().TransliterateText( rPam, aTrans ); +} + +void SwUndoTransliterate::AddChanges( SwTextNode& rTNd, + sal_Int32 nStart, sal_Int32 nLen, + uno::Sequence <sal_Int32> const & rOffsets ) +{ + long nOffsLen = rOffsets.getLength(); + UndoTransliterate_Data* pNew = new UndoTransliterate_Data( + rTNd.GetIndex(), nStart, static_cast<sal_Int32>(nOffsLen), + rTNd.GetText().copy(nStart, nLen)); + + aChanges.push_back( std::unique_ptr<UndoTransliterate_Data>(pNew) ); + + const sal_Int32* pOffsets = rOffsets.getConstArray(); + // where did we need less memory ? + const sal_Int32* p = pOffsets; + for( long n = 0; n < nOffsLen; ++n, ++p ) + if( *p != ( nStart + n )) + { + // create the Offset array + pNew->pOffsets.reset( new Sequence <sal_Int32> ( nLen ) ); + sal_Int32* pIdx = pNew->pOffsets->getArray(); + p = pOffsets; + long nMyOff, nNewVal = nStart; + for( n = 0, nMyOff = nStart; n < nOffsLen; ++p, ++n, ++nMyOff ) + { + if( *p < nMyOff ) + { + // something is deleted + nMyOff = *p; + *(pIdx-1) = nNewVal++; + } + else if( *p > nMyOff ) + { + for( ; *p > nMyOff; ++nMyOff ) + *pIdx++ = nNewVal; + --nMyOff; + --n; + --p; + } + else + *pIdx++ = nNewVal++; + } + + // and then we need to save the attributes/bookmarks + // but this data must moved every time to the last in the chain! + for (size_t i = 0; i + 1 < aChanges.size(); ++i) // check all changes but not the current one + { + UndoTransliterate_Data* pD = aChanges[i].get(); + if( pD->nNdIdx == pNew->nNdIdx && pD->pHistory ) + { + // same node and have a history? + pNew->pHistory = std::move(pD->pHistory); + break; // more can't exist + } + } + + if( !pNew->pHistory ) + { + pNew->pHistory.reset( new SwHistory ); + SwRegHistory aRHst( rTNd, pNew->pHistory.get() ); + pNew->pHistory->CopyAttr( rTNd.GetpSwpHints(), + pNew->nNdIdx, 0, rTNd.GetText().getLength(), false ); + } + break; + } +} + +void UndoTransliterate_Data::SetChangeAtNode( SwDoc& rDoc ) +{ + SwTextNode* pTNd = rDoc.GetNodes()[ nNdIdx ]->GetTextNode(); + if( pTNd ) + { + Sequence <sal_Int32> aOffsets( pOffsets ? pOffsets->getLength() : nLen ); + if( pOffsets ) + aOffsets = *pOffsets; + else + { + sal_Int32* p = aOffsets.getArray(); + for( sal_Int32 n = 0; n < nLen; ++n, ++p ) + *p = n + nStart; + } + pTNd->ReplaceTextOnly( nStart, nLen, sText, aOffsets ); + + if( pHistory ) + { + if( pTNd->GetpSwpHints() ) + pTNd->ClearSwpHintsArr( false ); + pHistory->TmpRollback( &rDoc, 0, false ); + pHistory->SetTmpEnd( pHistory->Count() ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unredln.cxx b/sw/source/core/undo/unredln.cxx new file mode 100644 index 000000000..4b98043a6 --- /dev/null +++ b/sw/source/core/undo/unredln.cxx @@ -0,0 +1,527 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoRedline.hxx> +#include <hintids.hxx> +#include <unotools/charclass.hxx> +#include <doc.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <txtfrm.hxx> +#include <UndoCore.hxx> +#include <UndoDelete.hxx> +#include <strings.hrc> +#include <redline.hxx> +#include <docary.hxx> +#include <sortopt.hxx> +#include <docedt.hxx> + +SwUndoRedline::SwUndoRedline( SwUndoId nUsrId, const SwPaM& rRange ) + : SwUndo( SwUndoId::REDLINE, rRange.GetDoc() ), SwUndRng( rRange ), + mnUserId( nUsrId ), + mbHiddenRedlines( false ) +{ + // consider Redline + SwDoc& rDoc = *rRange.GetDoc(); + if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) + { + switch( mnUserId ) + { + case SwUndoId::DELETE: + case SwUndoId::REPLACE: + mpRedlData.reset( new SwRedlineData( RedlineType::Delete, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + break; + default: + ; + } + SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() ); + } + + sal_uLong nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex(); + + mpRedlSaveData.reset( new SwRedlineSaveDatas ); + if( !FillSaveData( rRange, *mpRedlSaveData, false, SwUndoId::REJECT_REDLINE != mnUserId )) + { + mpRedlSaveData.reset(); + } + else + { + mbHiddenRedlines = HasHiddenRedlines( *mpRedlSaveData ); + if( mbHiddenRedlines ) // then the NodeIndices of SwUndRng need to be corrected + { + nEndExtra -= rDoc.GetNodes().GetEndOfExtras().GetIndex(); + m_nSttNode -= nEndExtra; + m_nEndNode -= nEndExtra; + } + } +} + +SwUndoRedline::~SwUndoRedline() +{ + mpRedlData.reset(); + mpRedlSaveData.reset(); +} + +sal_uInt16 SwUndoRedline::GetRedlSaveCount() const +{ + return mpRedlSaveData ? mpRedlSaveData->size() : 0; +} + +void SwUndoRedline::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc& rDoc = rContext.GetDoc(); + SwPaM& rPam(AddUndoRedoPaM(rContext)); + + UndoRedlineImpl(rDoc, rPam); + + if( mpRedlSaveData ) + { + sal_uLong nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex(); + SetSaveData(rDoc, *mpRedlSaveData); + if( mbHiddenRedlines ) + { + mpRedlSaveData->clear(); + + nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex() - nEndExtra; + m_nSttNode += nEndExtra; + m_nEndNode += nEndExtra; + } + SetPaM(rPam, true); + } + + // update frames after calling SetSaveData + if (dynamic_cast<SwUndoRedlineDelete*>(this)) + { + sw::UpdateFramesForRemoveDeleteRedline(rDoc, rPam); + } + else if (dynamic_cast<SwUndoAcceptRedline*>(this) + || dynamic_cast<SwUndoRejectRedline*>(this)) + { // (can't check here if there's a delete redline being accepted) + sw::UpdateFramesForAddDeleteRedline(rDoc, rPam); + } +} + +void SwUndoRedline::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc& rDoc = rContext.GetDoc(); + RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On ); + + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + if( mpRedlSaveData && mbHiddenRedlines ) + { + sal_uLong nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex(); + FillSaveData(rPam, *mpRedlSaveData, false, SwUndoId::REJECT_REDLINE != mnUserId ); + + nEndExtra -= rDoc.GetNodes().GetEndOfExtras().GetIndex(); + m_nSttNode -= nEndExtra; + m_nEndNode -= nEndExtra; + } + + RedoRedlineImpl(rDoc, rPam); + + SetPaM(rPam, true); + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); +} + +void SwUndoRedline::UndoRedlineImpl(SwDoc &, SwPaM &) +{ +} + +// default: remove redlines +void SwUndoRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) +{ + rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any); +} + +SwUndoRedlineDelete::SwUndoRedlineDelete( const SwPaM& rRange, SwUndoId nUsrId ) + : SwUndoRedline( nUsrId != SwUndoId::EMPTY ? nUsrId : SwUndoId::DELETE, rRange ), + m_bCanGroup( false ), m_bIsDelim( false ), m_bIsBackspace( false ) +{ + const SwTextNode* pTNd; + SetRedlineText(rRange.GetText()); + if( SwUndoId::DELETE == mnUserId && + m_nSttNode == m_nEndNode && m_nSttContent + 1 == m_nEndContent && + nullptr != (pTNd = rRange.GetNode().GetTextNode()) ) + { + sal_Unicode const cCh = pTNd->GetText()[m_nSttContent]; + if( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh ) + { + m_bCanGroup = true; + m_bIsDelim = !GetAppCharClass().isLetterNumeric( pTNd->GetText(), + m_nSttContent ); + m_bIsBackspace = m_nSttContent == rRange.GetPoint()->nContent.GetIndex(); + } + } + + m_bCacheComment = false; +} + +// bit of a hack, replace everything... +SwRewriter SwUndoRedlineDelete::GetRewriter() const +{ + SwRewriter aResult; + OUString aStr = DenoteSpecialCharacters(m_sRedlineText); + aStr = ShortenString(aStr, nUndoStringLength, SwResId(STR_LDOTS)); + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, aStr); + OUString ret = aRewriter.Apply(SwResId(STR_UNDO_REDLINE_DELETE)); + aResult.AddRule(UndoArg1, ret); + return aResult; +} + +void SwUndoRedlineDelete::SetRedlineText(const OUString & rText) +{ + m_sRedlineText = rText; +} + +void SwUndoRedlineDelete::UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) +{ + rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any); +} + +void SwUndoRedlineDelete::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) +{ + if (rPam.GetPoint() != rPam.GetMark()) + { + rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline(*mpRedlData, rPam), false ); + } + sw::UpdateFramesForAddDeleteRedline(rDoc, rPam); +} + +bool SwUndoRedlineDelete::CanGrouping( const SwUndoRedlineDelete& rNext ) +{ + bool bRet = false; + if( SwUndoId::DELETE == mnUserId && mnUserId == rNext.mnUserId && + m_bCanGroup == rNext.m_bCanGroup && + m_bIsDelim == rNext.m_bIsDelim && + m_bIsBackspace == rNext.m_bIsBackspace && + m_nSttNode == m_nEndNode && + rNext.m_nSttNode == m_nSttNode && + rNext.m_nEndNode == m_nEndNode ) + { + int bIsEnd = 0; + if( rNext.m_nSttContent == m_nEndContent ) + bIsEnd = 1; + else if( rNext.m_nEndContent == m_nSttContent ) + bIsEnd = -1; + + if( bIsEnd && + (( !mpRedlSaveData && !rNext.mpRedlSaveData ) || + ( mpRedlSaveData && rNext.mpRedlSaveData && + SwUndo::CanRedlineGroup( *mpRedlSaveData, + *rNext.mpRedlSaveData, 1 != bIsEnd ) + ))) + { + if( 1 == bIsEnd ) + m_nEndContent = rNext.m_nEndContent; + else + m_nSttContent = rNext.m_nSttContent; + bRet = true; + } + } + return bRet; +} + +SwUndoRedlineSort::SwUndoRedlineSort( const SwPaM& rRange, + const SwSortOptions& rOpt ) + : SwUndoRedline( SwUndoId::SORT_TXT, rRange ), + m_pOpt( new SwSortOptions( rOpt ) ), + m_nSaveEndNode( m_nEndNode ), m_nSaveEndContent( m_nEndContent ) +{ +} + +SwUndoRedlineSort::~SwUndoRedlineSort() +{ +} + +void SwUndoRedlineSort::UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) +{ + // rPam contains the sorted range + // aSaveRange contains copied (i.e. original) range + + SwPosition *const pStart = rPam.Start(); + SwPosition *const pEnd = rPam.End(); + + SwNodeIndex aPrevIdx( pStart->nNode, -1 ); + sal_uLong nOffsetTemp = pEnd->nNode.GetIndex() - pStart->nNode.GetIndex(); + + if( !( RedlineFlags::ShowDelete & rDoc.getIDocumentRedlineAccess().GetRedlineFlags()) ) + { + // Search both Redline objects and make them visible to make the nodes + // consistent again. The 'delete' one is hidden, thus search for the + // 'insert' Redline object. The former is located directly after the latter. + SwRedlineTable::size_type nFnd = rDoc.getIDocumentRedlineAccess().GetRedlinePos( + *rDoc.GetNodes()[ m_nSttNode + 1 ], + RedlineType::Insert ); + OSL_ENSURE( SwRedlineTable::npos != nFnd && nFnd+1 < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(), + "could not find an Insert object" ); + ++nFnd; + rDoc.getIDocumentRedlineAccess().GetRedlineTable()[nFnd]->Show(1, nFnd); + } + + { + SwPaM aTmp( *rPam.GetMark() ); + aTmp.GetMark()->nContent = 0; + aTmp.SetMark(); + aTmp.GetPoint()->nNode = m_nSaveEndNode; + aTmp.GetPoint()->nContent.Assign( aTmp.GetContentNode(), m_nSaveEndContent ); + rDoc.getIDocumentRedlineAccess().DeleteRedline( aTmp, true, RedlineType::Any ); + } + + rDoc.getIDocumentContentOperations().DelFullPara(rPam); + + SwPaM *const pPam = & rPam; + pPam->DeleteMark(); + pPam->GetPoint()->nNode.Assign( aPrevIdx.GetNode(), +1 ); + SwContentNode* pCNd = pPam->GetContentNode(); + pPam->GetPoint()->nContent.Assign(pCNd, 0 ); + pPam->SetMark(); + + pPam->GetPoint()->nNode += nOffsetTemp; + pCNd = pPam->GetContentNode(); + pPam->GetPoint()->nContent.Assign( pCNd, pCNd->Len() ); + + SetValues( *pPam ); + + SetPaM(rPam); +} + +void SwUndoRedlineSort::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) +{ + SwPaM* pPam = &rPam; + SwPosition* pStart = pPam->Start(); + SwPosition* pEnd = pPam->End(); + + SwNodeIndex aPrevIdx( pStart->nNode, -1 ); + sal_uLong nOffsetTemp = pEnd->nNode.GetIndex() - pStart->nNode.GetIndex(); + const sal_Int32 nCntStt = pStart->nContent.GetIndex(); + + rDoc.SortText(rPam, *m_pOpt); + + pPam->DeleteMark(); + pPam->GetPoint()->nNode.Assign( aPrevIdx.GetNode(), +1 ); + SwContentNode* pCNd = pPam->GetContentNode(); + sal_Int32 nLen = pCNd->Len(); + if( nLen > nCntStt ) + nLen = nCntStt; + pPam->GetPoint()->nContent.Assign(pCNd, nLen ); + pPam->SetMark(); + + pPam->GetPoint()->nNode += nOffsetTemp; + pCNd = pPam->GetContentNode(); + pPam->GetPoint()->nContent.Assign( pCNd, pCNd->Len() ); + + SetValues( rPam ); + + SetPaM( rPam ); + rPam.GetPoint()->nNode = m_nSaveEndNode; + rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), m_nSaveEndContent ); +} + +void SwUndoRedlineSort::RepeatImpl(::sw::RepeatContext & rContext) +{ + rContext.GetDoc().SortText( rContext.GetRepeatPaM(), *m_pOpt ); +} + +void SwUndoRedlineSort::SetSaveRange( const SwPaM& rRange ) +{ + const SwPosition& rPos = *rRange.End(); + m_nSaveEndNode = rPos.nNode.GetIndex(); + m_nSaveEndContent = rPos.nContent.GetIndex(); +} + +SwUndoAcceptRedline::SwUndoAcceptRedline( const SwPaM& rRange ) + : SwUndoRedline( SwUndoId::ACCEPT_REDLINE, rRange ) +{ +} + +void SwUndoAcceptRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) +{ + rDoc.getIDocumentRedlineAccess().AcceptRedline(rPam, false); +} + +void SwUndoAcceptRedline::RepeatImpl(::sw::RepeatContext & rContext) +{ + rContext.GetDoc().getIDocumentRedlineAccess().AcceptRedline(rContext.GetRepeatPaM(), true); +} + +SwUndoRejectRedline::SwUndoRejectRedline( const SwPaM& rRange ) + : SwUndoRedline( SwUndoId::REJECT_REDLINE, rRange ) +{ +} + +void SwUndoRejectRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) +{ + rDoc.getIDocumentRedlineAccess().RejectRedline(rPam, false); +} + +void SwUndoRejectRedline::RepeatImpl(::sw::RepeatContext & rContext) +{ + rContext.GetDoc().getIDocumentRedlineAccess().RejectRedline(rContext.GetRepeatPaM(), true); +} + +SwUndoCompDoc::SwUndoCompDoc( const SwPaM& rRg, bool bIns ) + : SwUndo( SwUndoId::COMPAREDOC, rRg.GetDoc() ), SwUndRng( rRg ), + m_bInsert( bIns ) +{ + SwDoc* pDoc = rRg.GetDoc(); + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + RedlineType eTyp = m_bInsert ? RedlineType::Insert : RedlineType::Delete; + m_pRedlineData.reset( new SwRedlineData( eTyp, pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() ); + } +} + +SwUndoCompDoc::SwUndoCompDoc( const SwRangeRedline& rRedl ) + : SwUndo( SwUndoId::COMPAREDOC, rRedl.GetDoc() ), SwUndRng( rRedl ), + // for MergeDoc the corresponding inverse is needed + m_bInsert( RedlineType::Delete == rRedl.GetType() ) +{ + SwDoc* pDoc = rRedl.GetDoc(); + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + m_pRedlineData.reset( new SwRedlineData( rRedl.GetRedlineData() ) ); + SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() ); + } + + m_pRedlineSaveDatas.reset( new SwRedlineSaveDatas ); + if( !FillSaveData( rRedl, *m_pRedlineSaveDatas, false )) + { + m_pRedlineSaveDatas.reset(); + } +} + +SwUndoCompDoc::~SwUndoCompDoc() +{ + m_pRedlineData.reset(); + m_pUndoDelete.reset(); + m_pUndoDelete2.reset(); + m_pRedlineSaveDatas.reset(); +} + +void SwUndoCompDoc::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc& rDoc = rContext.GetDoc(); + SwPaM& rPam(AddUndoRedoPaM(rContext)); + + if( !m_bInsert ) + { + // delete Redlines + RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On); + + rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any); + + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + + // per definition Point is end (in SwUndRng!) + SwContentNode* pCSttNd = rPam.GetContentNode(false); + SwContentNode* pCEndNd = rPam.GetContentNode(); + + // if start- and end-content is zero, then the doc-compare moves + // complete nodes into the current doc. And then the selection + // must be from end to start, so the delete join into the right + // direction. + if( !m_nSttContent && !m_nEndContent ) + rPam.Exchange(); + + bool bJoinText, bJoinPrev; + sw_GetJoinFlags(rPam, bJoinText, bJoinPrev); + + m_pUndoDelete.reset( new SwUndoDelete(rPam, false) ); + + if( bJoinText ) + sw_JoinText(rPam, bJoinPrev); + + if( pCSttNd && !pCEndNd) + { + // #112139# Do not step behind the end of content. + SwNode & rTmp = rPam.GetNode(); + SwNode * pEnd = rDoc.GetNodes().DocumentSectionEndNode(&rTmp); + + if (&rTmp != pEnd) + { + rPam.SetMark(); + ++rPam.GetPoint()->nNode; + rPam.GetBound().nContent.Assign( nullptr, 0 ); + rPam.GetBound( false ).nContent.Assign( nullptr, 0 ); + m_pUndoDelete2.reset( new SwUndoDelete(rPam, true) ); + } + } + rPam.DeleteMark(); + } + else + { + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + { + rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any); + + if( m_pRedlineSaveDatas ) + SetSaveData(rDoc, *m_pRedlineSaveDatas); + } + SetPaM(rPam, true); + } +} + +void SwUndoCompDoc::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc& rDoc = rContext.GetDoc(); + + if( m_bInsert ) + { + SwPaM& rPam(AddUndoRedoPaM(rContext)); + if( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + { + SwRangeRedline* pTmp = new SwRangeRedline(*m_pRedlineData, rPam); + rDoc.getIDocumentRedlineAccess().GetRedlineTable().Insert( pTmp ); + pTmp->InvalidateRange(SwRangeRedline::Invalidation::Add); + } + else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) && + !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) + { + rDoc.getIDocumentRedlineAccess().SplitRedline(rPam); + } + SetPaM(rPam, true); + } + else + { + if( m_pUndoDelete2 ) + { + m_pUndoDelete2->UndoImpl(rContext); + m_pUndoDelete2.reset(); + } + m_pUndoDelete->UndoImpl(rContext); + m_pUndoDelete.reset(); + + // note: don't call SetPaM before executing Undo of members + SwPaM& rPam(AddUndoRedoPaM(rContext)); + + SwRangeRedline* pTmp = new SwRangeRedline(*m_pRedlineData, rPam); + rDoc.getIDocumentRedlineAccess().GetRedlineTable().Insert( pTmp ); + pTmp->InvalidateRange(SwRangeRedline::Invalidation::Add); + + SetPaM(rPam, true); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unsect.cxx b/sw/source/core/undo/unsect.cxx new file mode 100644 index 000000000..ae2ba60a3 --- /dev/null +++ b/sw/source/core/undo/unsect.cxx @@ -0,0 +1,601 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <UndoSection.hxx> + +#include <osl/diagnose.h> +#include <comphelper/scopeguard.hxx> +#include <sfx2/linkmgr.hxx> +#include <fmtcntnt.hxx> +#include <doc.hxx> +#include <IDocumentLinksAdministration.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <poolfmt.hxx> +#include <docary.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <section.hxx> +#include <rolbck.hxx> +#include <redline.hxx> +#include <doctxm.hxx> +#include <ftnidx.hxx> +#include <rootfrm.hxx> +#include <editsh.hxx> +/// OD 04.10.2002 #102894# +/// class Calc needed for calculation of the hidden condition of a section. +#include <calc.hxx> + +static std::unique_ptr<SfxItemSet> lcl_GetAttrSet( const SwSection& rSect ) +{ + // save attributes of the format (columns, color, ...) + // Content and Protect items are not interesting since they are already + // stored in Section, thus delete them. + std::unique_ptr<SfxItemSet> pAttr; + if( rSect.GetFormat() ) + { + sal_uInt16 nCnt = 1; + if( rSect.IsProtect() ) + ++nCnt; + + if( nCnt < rSect.GetFormat()->GetAttrSet().Count() ) + { + pAttr.reset(new SfxItemSet( rSect.GetFormat()->GetAttrSet() )); + pAttr->ClearItem( RES_PROTECT ); + pAttr->ClearItem( RES_CNTNT ); + if( !pAttr->Count() ) + { + pAttr.reset(); + } + } + } + return pAttr; +} + +SwUndoInsSection::SwUndoInsSection( + SwPaM const& rPam, SwSectionData const& rNewData, + SfxItemSet const*const pSet, + std::pair<SwTOXBase const*, sw::RedlineMode> const*const pTOXBase) + : SwUndo( SwUndoId::INSSECTION, rPam.GetDoc() ), SwUndRng( rPam ) + , m_pSectionData(new SwSectionData(rNewData)) + , m_pTOXBase( pTOXBase + ? std::make_unique<std::pair<SwTOXBase *, sw::RedlineMode>>( + new SwTOXBase(*pTOXBase->first), pTOXBase->second) + : nullptr ) + , m_pAttrSet( (pSet && pSet->Count()) ? new SfxItemSet( *pSet ) : nullptr ) + , m_nSectionNodePos(0) + , m_bSplitAtStart(false) + , m_bSplitAtEnd(false) + , m_bUpdateFootnote(false) +{ + SwDoc& rDoc = *rPam.GetDoc(); + if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) + { + m_pRedlData.reset(new SwRedlineData( RedlineType::Insert, + rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() )); + SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() ); + } + m_pRedlineSaveData.reset( new SwRedlineSaveDatas ); + if( !FillSaveData( rPam, *m_pRedlineSaveData, false )) + m_pRedlineSaveData.reset(); + + if( !rPam.HasMark() ) + { + const SwContentNode* pCNd = rPam.GetPoint()->nNode.GetNode().GetContentNode(); + if( pCNd && pCNd->HasSwAttrSet() && ( + !rPam.GetPoint()->nContent.GetIndex() || + rPam.GetPoint()->nContent.GetIndex() == pCNd->Len() )) + { + SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange ); + aBrkSet.Put( *pCNd->GetpSwAttrSet() ); + if( aBrkSet.Count() ) + { + m_pHistory.reset( new SwHistory ); + m_pHistory->CopyFormatAttr( aBrkSet, pCNd->GetIndex() ); + } + } + } +} + +SwUndoInsSection::~SwUndoInsSection() +{ +} + +void SwUndoInsSection::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + RemoveIdxFromSection( rDoc, m_nSectionNodePos ); + + SwSectionNode *const pNd = + rDoc.GetNodes()[ m_nSectionNodePos ]->GetSectionNode(); + OSL_ENSURE( pNd, "where is my SectionNode?" ); + + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + rDoc.getIDocumentRedlineAccess().DeleteRedline( *pNd, true, RedlineType::Any ); + + // no selection? + SwNodeIndex aIdx( *pNd ); + if( ( !m_nEndNode && COMPLETE_STRING == m_nEndContent ) || + ( m_nSttNode == m_nEndNode && m_nSttContent == m_nEndContent )) + // delete simply all nodes + rDoc.GetNodes().Delete( aIdx, pNd->EndOfSectionIndex() - + aIdx.GetIndex() ); + else + // just delete format, rest happens automatically + rDoc.DelSectionFormat( pNd->GetSection().GetFormat() ); + + // do we need to consolidate? + if (m_bSplitAtStart) + { + Join( rDoc, m_nSttNode ); + } + + if (m_bSplitAtEnd) + { + Join( rDoc, m_nEndNode ); + } + + if (m_pHistory) + { + m_pHistory->TmpRollback( &rDoc, 0, false ); + } + + if (m_bUpdateFootnote) + { + rDoc.GetFootnoteIdxs().UpdateFootnote( aIdx ); + } + + AddUndoRedoPaM(rContext); + + if (m_pRedlineSaveData) + SetSaveData( rDoc, *m_pRedlineSaveData ); +} + +void SwUndoInsSection::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + + const SwTOXBaseSection* pUpdateTOX = nullptr; + if (m_pTOXBase) + { + SwRootFrame const* pLayout(nullptr); + SwRootFrame * pLayoutToReset(nullptr); + comphelper::ScopeGuard g([&]() { + if (pLayoutToReset) + { + pLayoutToReset->SetHideRedlines(m_pTOXBase->second == sw::RedlineMode::Shown); + } + }); + o3tl::sorted_vector<SwRootFrame *> layouts(rDoc.GetAllLayouts()); + for (SwRootFrame const*const p : layouts) + { + if ((m_pTOXBase->second == sw::RedlineMode::Hidden) == p->IsHideRedlines()) + { + pLayout = p; + break; + } + } + if (!pLayout) + { + assert(!layouts.empty()); // must have one layout + pLayoutToReset = *layouts.begin(); + pLayoutToReset->SetHideRedlines(m_pTOXBase->second == sw::RedlineMode::Hidden); + pLayout = pLayoutToReset; + } + pUpdateTOX = rDoc.InsertTableOf( *rPam.GetPoint(), + // don't expand: will be done by SwUndoUpdateIndex::RedoImpl() + *m_pTOXBase->first, m_pAttrSet.get(), false, pLayout); + } + else + { + rDoc.InsertSwSection(rPam, *m_pSectionData, nullptr, m_pAttrSet.get()); + } + + if (m_pHistory) + { + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + } + + SwSectionNode *const pSectNd = + rDoc.GetNodes()[ m_nSectionNodePos ]->GetSectionNode(); + if (m_pRedlData && + IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags())) + { + RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore); + + SwPaM aPam( *pSectNd->EndOfSectionNode(), *pSectNd, 1 ); + rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData, aPam ), true); + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) && + !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) + { + SwPaM aPam( *pSectNd->EndOfSectionNode(), *pSectNd, 1 ); + rDoc.getIDocumentRedlineAccess().SplitRedline( aPam ); + } + + if( pUpdateTOX ) + { + // initiate formatting + SwEditShell* pESh = rDoc.GetEditShell(); + if( pESh ) + pESh->CalcLayout(); + + // insert page numbers + const_cast<SwTOXBaseSection*>(pUpdateTOX)->UpdatePageNum(); + } +} + +void SwUndoInsSection::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if (m_pTOXBase) + { + rDoc.InsertTableOf(*rContext.GetRepeatPaM().GetPoint(), + *m_pTOXBase->first, m_pAttrSet.get(), true, + rDoc.getIDocumentLayoutAccess().GetCurrentLayout()); // TODO add shell to RepeatContext? + } + else + { + rDoc.InsertSwSection(rContext.GetRepeatPaM(), + *m_pSectionData, nullptr, m_pAttrSet.get()); + } +} + +void SwUndoInsSection::Join( SwDoc& rDoc, sal_uLong nNode ) +{ + SwNodeIndex aIdx( rDoc.GetNodes(), nNode ); + SwTextNode* pTextNd = aIdx.GetNode().GetTextNode(); + OSL_ENSURE( pTextNd, "Where is my TextNode?" ); + + { + RemoveIdxRel( + nNode + 1, + SwPosition( aIdx, SwIndex( pTextNd, pTextNd->GetText().getLength() ) ) ); + } + pTextNd->JoinNext(); + + if (m_pHistory) + { + SwIndex aCntIdx( pTextNd, 0 ); + pTextNd->RstTextAttr( aCntIdx, pTextNd->Len(), 0, nullptr, true ); + } +} + +void +SwUndoInsSection::SaveSplitNode(SwTextNode *const pTextNd, bool const bAtStart) +{ + if( pTextNd->GetpSwpHints() ) + { + if (!m_pHistory) + { + m_pHistory.reset( new SwHistory ); + } + m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), pTextNd->GetIndex(), 0, + pTextNd->GetText().getLength(), false ); + } + + if (bAtStart) + { + m_bSplitAtStart = true; + } + else + { + m_bSplitAtEnd = true; + } +} + +class SwUndoDelSection + : public SwUndo +{ +private: + std::unique_ptr<SwSectionData> const m_pSectionData; /// section not TOX + std::unique_ptr<SwTOXBase> const m_pTOXBase; /// set iff section is TOX + std::unique_ptr<SfxItemSet> const m_pAttrSet; + std::shared_ptr< ::sfx2::MetadatableUndo > const m_pMetadataUndo; + sal_uLong const m_nStartNode; + sal_uLong const m_nEndNode; + +public: + SwUndoDelSection( + SwSectionFormat const&, SwSection const&, SwNodeIndex const*const); + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; +}; + +std::unique_ptr<SwUndo> MakeUndoDelSection(SwSectionFormat const& rFormat) +{ + return std::make_unique<SwUndoDelSection>(rFormat, *rFormat.GetSection(), + rFormat.GetContent().GetContentIdx()); +} + +SwUndoDelSection::SwUndoDelSection( + SwSectionFormat const& rSectionFormat, SwSection const& rSection, + SwNodeIndex const*const pIndex) + : SwUndo( SwUndoId::DELSECTION, rSectionFormat.GetDoc() ) + , m_pSectionData( new SwSectionData(rSection) ) + , m_pTOXBase( dynamic_cast<const SwTOXBaseSection*>( &rSection) != nullptr + ? new SwTOXBase(static_cast<SwTOXBaseSection const&>(rSection)) + : nullptr ) + , m_pAttrSet( ::lcl_GetAttrSet(rSection) ) + , m_pMetadataUndo( rSectionFormat.CreateUndo() ) + , m_nStartNode( pIndex->GetIndex() ) + , m_nEndNode( pIndex->GetNode().EndOfSectionIndex() ) +{ +} + +void SwUndoDelSection::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + if (m_pTOXBase) + { + // sw_redlinehide: this should work as-is; there will be another undo for the update + rDoc.InsertTableOf(m_nStartNode, m_nEndNode-2, *m_pTOXBase, + m_pAttrSet.get()); + } + else + { + SwNodeIndex aStt( rDoc.GetNodes(), m_nStartNode ); + SwNodeIndex aEnd( rDoc.GetNodes(), m_nEndNode-2 ); + SwSectionFormat* pFormat = rDoc.MakeSectionFormat(); + if (m_pAttrSet) + { + pFormat->SetFormatAttr( *m_pAttrSet ); + } + + /// OD 04.10.2002 #102894# + /// remember inserted section node for further calculations + SwSectionNode* pInsertedSectNd = rDoc.GetNodes().InsertTextSection( + aStt, *pFormat, *m_pSectionData, nullptr, & aEnd); + + if( SfxItemState::SET == pFormat->GetItemState( RES_FTN_AT_TXTEND ) || + SfxItemState::SET == pFormat->GetItemState( RES_END_AT_TXTEND )) + { + rDoc.GetFootnoteIdxs().UpdateFootnote( aStt ); + } + + /// OD 04.10.2002 #102894# + /// consider that section is hidden by condition. + /// If section is hidden by condition, + /// recalculate condition and update hidden condition flag. + /// Recalculation is necessary, because fields, on which the hide + /// condition depends, can be changed - fields changes aren't undoable. + /// NOTE: setting hidden condition flag also creates/deletes corresponding + /// frames, if the hidden condition flag changes. + SwSection& aInsertedSect = pInsertedSectNd->GetSection(); + if ( aInsertedSect.IsHidden() && + !aInsertedSect.GetCondition().isEmpty() ) + { + SwCalc aCalc( rDoc ); + rDoc.getIDocumentFieldsAccess().FieldsToCalc(aCalc, pInsertedSectNd->GetIndex(), USHRT_MAX); + bool bRecalcCondHidden = + aCalc.Calculate( aInsertedSect.GetCondition() ).GetBool(); + aInsertedSect.SetCondHidden( bRecalcCondHidden ); + } + + pFormat->RestoreMetadata(m_pMetadataUndo); + } +} + +void SwUndoDelSection::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + SwSectionNode *const pNd = + rDoc.GetNodes()[ m_nStartNode ]->GetSectionNode(); + OSL_ENSURE( pNd, "Where is my SectionNode?" ); + // just delete format, rest happens automatically + rDoc.DelSectionFormat( pNd->GetSection().GetFormat() ); +} + +namespace { + +class SwUndoUpdateSection + : public SwUndo +{ +private: + std::unique_ptr<SwSectionData> m_pSectionData; + std::unique_ptr<SfxItemSet> m_pAttrSet; + sal_uLong const m_nStartNode; + bool const m_bOnlyAttrChanged; + +public: + SwUndoUpdateSection( + SwSection const&, SwNodeIndex const*const, bool const bOnlyAttr); + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; +}; + +} + +std::unique_ptr<SwUndo> +MakeUndoUpdateSection(SwSectionFormat const& rFormat, bool const bOnlyAttr) +{ + return std::make_unique<SwUndoUpdateSection>(*rFormat.GetSection(), + rFormat.GetContent().GetContentIdx(), bOnlyAttr); +} + +SwUndoUpdateSection::SwUndoUpdateSection( + SwSection const& rSection, SwNodeIndex const*const pIndex, + bool const bOnlyAttr) + : SwUndo( SwUndoId::CHGSECTION, pIndex->GetNode().GetDoc() ) + , m_pSectionData( new SwSectionData(rSection) ) + , m_pAttrSet( ::lcl_GetAttrSet(rSection) ) + , m_nStartNode( pIndex->GetIndex() ) + , m_bOnlyAttrChanged( bOnlyAttr ) +{ +} + +void SwUndoUpdateSection::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwSectionNode *const pSectNd = + rDoc.GetNodes()[ m_nStartNode ]->GetSectionNode(); + OSL_ENSURE( pSectNd, "Where is my SectionNode?" ); + + SwSection& rNdSect = pSectNd->GetSection(); + SwFormat* pFormat = rNdSect.GetFormat(); + + std::unique_ptr<SfxItemSet> pCur = ::lcl_GetAttrSet( rNdSect ); + if (m_pAttrSet) + { + // The Content and Protect items must persist + const SfxPoolItem* pItem; + m_pAttrSet->Put( pFormat->GetFormatAttr( RES_CNTNT )); + if( SfxItemState::SET == pFormat->GetItemState( RES_PROTECT, true, &pItem )) + { + m_pAttrSet->Put( *pItem ); + } + pFormat->DelDiffs( *m_pAttrSet ); + m_pAttrSet->ClearItem( RES_CNTNT ); + pFormat->SetFormatAttr( *m_pAttrSet ); + } + else + { + // than the old ones need to be deleted + pFormat->ResetFormatAttr( RES_FRMATR_BEGIN, RES_BREAK ); + pFormat->ResetFormatAttr( RES_HEADER, RES_OPAQUE ); + pFormat->ResetFormatAttr( RES_SURROUND, RES_FRMATR_END-1 ); + } + m_pAttrSet = std::move(pCur); + + if (!m_bOnlyAttrChanged) + { + const bool bUpdate = + (!rNdSect.IsLinkType() && m_pSectionData->IsLinkType()) + || ( !m_pSectionData->GetLinkFileName().isEmpty() + && (m_pSectionData->GetLinkFileName() != + rNdSect.GetLinkFileName())); + + // swap stored section data with live section data + SwSectionData *const pOld( new SwSectionData(rNdSect) ); + rNdSect.SetSectionData(*m_pSectionData); + m_pSectionData.reset(pOld); + + if( bUpdate ) + rNdSect.CreateLink( LinkCreateType::Update ); + else if( SectionType::Content == rNdSect.GetType() && rNdSect.IsConnected() ) + { + rNdSect.Disconnect(); + rDoc.getIDocumentLinksAdministration().GetLinkManager().Remove( &rNdSect.GetBaseLink() ); + } + } +} + +void SwUndoUpdateSection::RedoImpl(::sw::UndoRedoContext & rContext) +{ + UndoImpl(rContext); +} + + +SwUndoUpdateIndex::SwUndoUpdateIndex(SwTOXBaseSection & rTOX) + : SwUndo(SwUndoId::INSSECTION, rTOX.GetFormat()->GetDoc()) + , m_pSaveSectionOriginal(new SwUndoSaveSection) + , m_pSaveSectionUpdated(new SwUndoSaveSection) + , m_nStartIndex(rTOX.GetFormat()->GetSectionNode()->GetIndex() + 1) +{ + SwDoc & rDoc(*rTOX.GetFormat()->GetDoc()); + assert(rDoc.GetNodes()[m_nStartIndex-1]->IsSectionNode()); + assert(rDoc.GetNodes()[rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex()-1]->IsTextNode()); // -1 for extra empty node + // note: title is optional + assert(rDoc.GetNodes()[m_nStartIndex]->IsTextNode() + || rDoc.GetNodes()[m_nStartIndex]->IsSectionNode()); + SwNodeIndex const first(rDoc.GetNodes(), m_nStartIndex); + if (first.GetNode().IsSectionNode()) + { + SwSectionFormat & rSectionFormat(*first.GetNode().GetSectionNode()->GetSection().GetFormat()); + // note: DelSectionFormat will create & append SwUndoDelSection! + rDoc.DelSectionFormat(& rSectionFormat); // remove inner section nodes + } + assert(first.GetNode().IsTextNode()); // invariant: ToX section is *never* empty + SwNodeIndex const last(rDoc.GetNodes(), rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex() - 2); // skip empty node + assert(last.GetNode().IsTextNode()); + m_pSaveSectionOriginal->SaveSection(SwNodeRange(first, last), false); +} + +SwUndoUpdateIndex::~SwUndoUpdateIndex() = default; + +void SwUndoUpdateIndex::TitleSectionInserted(SwSectionFormat & rFormat) +{ + SwNodeIndex const tmp(rFormat.GetDoc()->GetNodes(), m_nStartIndex); // title inserted before empty node + assert(tmp.GetNode().IsSectionNode()); + assert(tmp.GetNode().GetSectionNode()->GetSection().GetFormat() == &rFormat); + m_pTitleSectionUpdated.reset(static_cast<SwUndoDelSection*>(MakeUndoDelSection(rFormat).release())); +} + +void SwUndoUpdateIndex::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc(rContext.GetDoc()); + if (m_pTitleSectionUpdated) + { + m_pTitleSectionUpdated->RedoImpl(rContext); + } + SwNodeIndex const first(rDoc.GetNodes(), m_nStartIndex); + assert(first.GetNode().IsTextNode()); // invariant: ToX section is *never* empty + SwNodeIndex const last(rDoc.GetNodes(), rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex() - 1); + assert(last.GetNode().IsTextNode()); + // dummy node so that SaveSection doesn't remove ToX section... + SwTextNode *const pDeletionPrevention = rDoc.GetNodes().MakeTextNode( + SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode()), + rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT)); + m_pSaveSectionUpdated->SaveSection(SwNodeRange(first, last), false); + m_pSaveSectionOriginal->RestoreSection(&rDoc, first, true); + // delete before restoring nested undo, so its node indexes match + SwNodeIndex const del(*pDeletionPrevention); + SwDoc::CorrAbs(del, del, SwPosition(SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode())), true); + rDoc.GetNodes().Delete(del); + // original title section will be restored by next Undo, see ctor! +} + +void SwUndoUpdateIndex::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc(rContext.GetDoc()); + // original title section was deleted by previous Undo, see ctor! + SwNodeIndex const first(rDoc.GetNodes(), m_nStartIndex); + assert(first.GetNode().IsTextNode()); // invariant: ToX section is *never* empty + SwNodeIndex const last(rDoc.GetNodes(), rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex() - 1); + assert(last.GetNode().IsTextNode()); + // dummy node so that SaveSection doesn't remove ToX section... + SwTextNode *const pDeletionPrevention = rDoc.GetNodes().MakeTextNode( + SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode()), + rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT)); + m_pSaveSectionOriginal->SaveSection(SwNodeRange(first, last), false); + m_pSaveSectionUpdated->RestoreSection(&rDoc, first, true); + // delete before restoring nested undo, so its node indexes match + SwNodeIndex const del(*pDeletionPrevention); + SwDoc::CorrAbs(del, del, SwPosition(SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode())), true); + rDoc.GetNodes().Delete(del); + if (m_pTitleSectionUpdated) + { + m_pTitleSectionUpdated->UndoImpl(rContext); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unsort.cxx b/sw/source/core/undo/unsort.cxx new file mode 100644 index 000000000..8f0820fb6 --- /dev/null +++ b/sw/source/core/undo/unsort.cxx @@ -0,0 +1,253 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <UndoSort.hxx> +#include <doc.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <swtable.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <UndoTable.hxx> +#include <sortopt.hxx> +#include <docsort.hxx> +#include <node2lay.hxx> + +// Undo for Sorting +SwSortUndoElement::~SwSortUndoElement() +{ + // are there string pointers saved? + if( 0xffffffff != SORT_TXT_TBL.TXT.nID ) + { + delete SORT_TXT_TBL.TBL.pSource; + delete SORT_TXT_TBL.TBL.pTarget; + } +} + +SwUndoSort::SwUndoSort(const SwPaM& rRg, const SwSortOptions& rOpt) + : SwUndo(SwUndoId::SORT_TXT, rRg.GetDoc()) + , SwUndRng(rRg) + , nTableNd(0) +{ + pSortOpt.reset( new SwSortOptions(rOpt) ); +} + +SwUndoSort::SwUndoSort( sal_uLong nStt, sal_uLong nEnd, const SwTableNode& rTableNd, + const SwSortOptions& rOpt, bool bSaveTable ) + : SwUndo(SwUndoId::SORT_TBL, rTableNd.GetDoc()) +{ + m_nSttNode = nStt; + m_nEndNode = nEnd; + nTableNd = rTableNd.GetIndex(); + + pSortOpt.reset( new SwSortOptions(rOpt) ); + if( bSaveTable ) + pUndoTableAttr.reset( new SwUndoAttrTable( rTableNd ) ); +} + +SwUndoSort::~SwUndoSort() +{ + pSortOpt.reset(); + pUndoTableAttr.reset(); +} + +void SwUndoSort::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if(pSortOpt->bTable) + { + // Undo for Table + RemoveIdxFromSection( rDoc, m_nSttNode, &m_nEndNode ); + + if( pUndoTableAttr ) + { + pUndoTableAttr->UndoImpl(rContext); + } + + SwTableNode* pTableNd = rDoc.GetNodes()[ nTableNd ]->GetTableNode(); + + // #i37739# A simple 'MakeFrames' after the node sorting + // does not work if the table is inside a frame and has no prev/next. + SwNode2LayoutSaveUpperFrames aNode2Layout(*pTableNd); + + pTableNd->DelFrames(); + const SwTable& rTable = pTableNd->GetTable(); + + SwMovedBoxes aMovedList; + for (const std::unique_ptr<SwSortUndoElement> & i : m_SortList) + { + const SwTableBox* pSource = rTable.GetTableBox( + *i->SORT_TXT_TBL.TBL.pSource ); + const SwTableBox* pTarget = rTable.GetTableBox( + *i->SORT_TXT_TBL.TBL.pTarget ); + + // move back + MoveCell(&rDoc, pTarget, pSource, + USHRT_MAX != aMovedList.GetPos(pSource) ); + + // store moved entry in list + aMovedList.push_back(pTarget); + } + + // Restore table frames: + // #i37739# A simple 'MakeFrames' after the node sorting + // does not work if the table is inside a frame and has no prev/next. + const sal_uLong nIdx = pTableNd->GetIndex(); + aNode2Layout.RestoreUpperFrames( rDoc.GetNodes(), nIdx, nIdx + 1 ); + } + else + { + // Undo for Text + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + RemoveIdxFromRange(rPam, true); + + // create index for (sorted) positions + // The IndexList must be created based on (asc.) sorted SourcePosition. + std::vector<SwNodeIndex> aIdxList; + aIdxList.reserve(m_SortList.size()); + + for (size_t i = 0; i < m_SortList.size(); ++i) + { + for (const std::unique_ptr<SwSortUndoElement> & j : m_SortList) + { + if (j->SORT_TXT_TBL.TXT.nSource == m_nSttNode + i) + { + aIdxList.push_back( SwNodeIndex( rDoc.GetNodes(), + j->SORT_TXT_TBL.TXT.nTarget ) ); + break; + } + } + } + + for (size_t i = 0; i < m_SortList.size(); ++i) + { + SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode + i ); + SwNodeRange aRg( aIdxList[i], 0, aIdxList[i], 1 ); + rDoc.getIDocumentContentOperations().MoveNodeRange(aRg, aIdx, + SwMoveFlags::DEFAULT); + } + // delete indices + aIdxList.clear(); + SetPaM(rPam, true); + } +} + +void SwUndoSort::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + if(pSortOpt->bTable) + { + // Redo for Table + RemoveIdxFromSection( rDoc, m_nSttNode, &m_nEndNode ); + + SwTableNode* pTableNd = rDoc.GetNodes()[ nTableNd ]->GetTableNode(); + + // #i37739# A simple 'MakeFrames' after the node sorting + // does not work if the table is inside a frame and has no prev/next. + SwNode2LayoutSaveUpperFrames aNode2Layout(*pTableNd); + + pTableNd->DelFrames(); + const SwTable& rTable = pTableNd->GetTable(); + + SwMovedBoxes aMovedList; + for (const std::unique_ptr<SwSortUndoElement> & i : m_SortList) + { + const SwTableBox* pSource = rTable.GetTableBox( + *i->SORT_TXT_TBL.TBL.pSource ); + const SwTableBox* pTarget = rTable.GetTableBox( + *i->SORT_TXT_TBL.TBL.pTarget ); + + // move back + MoveCell(&rDoc, pSource, pTarget, + USHRT_MAX != aMovedList.GetPos( pTarget ) ); + // store moved entry in list + aMovedList.push_back( pSource ); + } + + if( pUndoTableAttr ) + { + pUndoTableAttr->RedoImpl(rContext); + } + + // Restore table frames: + // #i37739# A simple 'MakeFrames' after the node sorting + // does not work if the table is inside a frame and has no prev/next. + const sal_uLong nIdx = pTableNd->GetIndex(); + aNode2Layout.RestoreUpperFrames( rDoc.GetNodes(), nIdx, nIdx + 1 ); + } + else + { + // Redo for Text + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + SetPaM(rPam); + RemoveIdxFromRange(rPam, true); + + std::vector<SwNodeIndex> aIdxList; + aIdxList.reserve(m_SortList.size()); + + for (size_t i = 0; i < m_SortList.size(); ++i) + { // current position is starting point + aIdxList.push_back( SwNodeIndex( rDoc.GetNodes(), + m_SortList[i]->SORT_TXT_TBL.TXT.nSource) ); + } + + for (size_t i = 0; i < m_SortList.size(); ++i) + { + SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode + i); + SwNodeRange aRg( aIdxList[i], 0, aIdxList[i], 1 ); + rDoc.getIDocumentContentOperations().MoveNodeRange(aRg, aIdx, + SwMoveFlags::DEFAULT); + } + // delete indices + aIdxList.clear(); + SetPaM(rPam, true); + SwTextNode const*const pTNd = rPam.GetNode().GetTextNode(); + if( pTNd ) + { + rPam.GetPoint()->nContent = pTNd->GetText().getLength(); + } + } +} + +void SwUndoSort::RepeatImpl(::sw::RepeatContext & rContext) +{ + // table not repeat capable + if(!pSortOpt->bTable) + { + SwPaM *const pPam = & rContext.GetRepeatPaM(); + SwDoc& rDoc = *pPam->GetDoc(); + + if( !rDoc.IsIdxInTable( pPam->Start()->nNode ) ) + rDoc.SortText(*pPam, *pSortOpt); + } +} + +void SwUndoSort::Insert( const OUString& rOrgPos, const OUString& rNewPos) +{ + m_SortList.push_back(std::make_unique< SwSortUndoElement>(rOrgPos, rNewPos)); +} + +void SwUndoSort::Insert( sal_uLong nOrgPos, sal_uLong nNewPos) +{ + m_SortList.push_back(std::make_unique<SwSortUndoElement>(nOrgPos, nNewPos)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/unspnd.cxx b/sw/source/core/undo/unspnd.cxx new file mode 100644 index 000000000..112db668d --- /dev/null +++ b/sw/source/core/undo/unspnd.cxx @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoSplitMove.hxx> +#include <doc.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <pam.hxx> +#include <swtable.hxx> +#include <ndtxt.hxx> +#include <swundo.hxx> +#include <frmfmt.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> +#include <redline.hxx> +#include <docary.hxx> +#include <IShellCursorSupplier.hxx> +#include <osl/diagnose.h> + +// SPLITNODE + +SwUndoSplitNode::SwUndoSplitNode( SwDoc* pDoc, const SwPosition& rPos, + bool bChkTable ) + : SwUndo( SwUndoId::SPLITNODE, pDoc ), nNode( rPos.nNode.GetIndex() ), + nContent( rPos.nContent.GetIndex() ), + bTableFlag( false ), bChkTableStt( bChkTable ) +{ + SwTextNode *const pTextNd = rPos.nNode.GetNode().GetTextNode(); + OSL_ENSURE( pTextNd, "only for TextNode" ); + if( pTextNd->GetpSwpHints() ) + { + m_pHistory.reset(new SwHistory); + m_pHistory->CopyAttr(pTextNd->GetpSwpHints(), nNode, 0, + pTextNd->GetText().getLength(), false ); + if (!m_pHistory->Count()) + { + m_pHistory.reset(); + } + } + // consider Redline + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + pRedlData.reset( new SwRedlineData( RedlineType::Insert, pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() ); + } + + nParRsid = pTextNd->GetParRsid(); +} + +SwUndoSplitNode::~SwUndoSplitNode() +{ + m_pHistory.reset(); + pRedlData.reset(); +} + +void SwUndoSplitNode::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() ); + rPam.DeleteMark(); + if( bTableFlag ) + { + // than a TextNode was added directly before the current table + SwNodeIndex& rIdx = rPam.GetPoint()->nNode; + rIdx = nNode; + SwTextNode* pTNd; + SwNode* pCurrNd = pDoc->GetNodes()[ nNode + 1 ]; + SwTableNode* pTableNd = pCurrNd->FindTableNode(); + if( pCurrNd->IsContentNode() && pTableNd && + nullptr != ( pTNd = pDoc->GetNodes()[ pTableNd->GetIndex()-1 ]->GetTextNode() )) + { + // move break attributes + SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat(); + const SfxItemSet* pNdSet = pTNd->GetpSwAttrSet(); + if( pNdSet ) + { + const SfxPoolItem *pItem; + if( SfxItemState::SET == pNdSet->GetItemState( RES_PAGEDESC, false, + &pItem ) ) + pTableFormat->SetFormatAttr( *pItem ); + + if( SfxItemState::SET == pNdSet->GetItemState( RES_BREAK, false, + &pItem ) ) + pTableFormat->SetFormatAttr( *pItem ); + } + + // than delete it again + SwNodeIndex aDelNd( *pTableNd, -1 ); + rPam.GetPoint()->nContent.Assign( static_cast<SwContentNode*>(pCurrNd), 0 ); + RemoveIdxRel( aDelNd.GetIndex(), *rPam.GetPoint() ); + pDoc->GetNodes().Delete( aDelNd ); + } + } + else + { + SwTextNode * pTNd = pDoc->GetNodes()[ nNode ]->GetTextNode(); + if( pTNd ) + { + rPam.GetPoint()->nNode = *pTNd; + rPam.GetPoint()->nContent.Assign(pTNd, pTNd->GetText().getLength()); + + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + { + rPam.SetMark(); + ++rPam.GetMark()->nNode; + rPam.GetMark()->nContent.Assign( rPam.GetMark()-> + nNode.GetNode().GetContentNode(), 0 ); + pDoc->getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any ); + rPam.DeleteMark(); + } + + RemoveIdxRel( nNode+1, *rPam.GetPoint() ); + + pTNd->JoinNext(); + if (m_pHistory) + { + rPam.GetPoint()->nContent = 0; + rPam.SetMark(); + rPam.GetPoint()->nContent = pTNd->GetText().getLength(); + + pDoc->RstTextAttrs( rPam, true ); + m_pHistory->TmpRollback( pDoc, 0, false ); + } + + pDoc->UpdateParRsid( pTNd, nParRsid ); + } + } + + // also set the cursor onto undo section + rPam.DeleteMark(); + rPam.GetPoint()->nNode = nNode; + rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), nContent ); +} + +void SwUndoSplitNode::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() ); + rPam.GetPoint()->nNode = nNode; + SwTextNode * pTNd = rPam.GetNode().GetTextNode(); + OSL_ENSURE(pTNd, "SwUndoSplitNode::RedoImpl(): SwTextNode expected"); + if (pTNd) + { + rPam.GetPoint()->nContent.Assign( pTNd, nContent ); + + SwDoc* pDoc = rPam.GetDoc(); + pDoc->getIDocumentContentOperations().SplitNode( *rPam.GetPoint(), bChkTableStt ); + + if (m_pHistory) + { + m_pHistory->SetTmpEnd(m_pHistory->Count()); + } + + if( ( pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) || + ( !( RedlineFlags::Ignore & GetRedlineFlags() ) && + !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )) + { + rPam.SetMark(); + if( rPam.Move( fnMoveBackward )) + { + if( pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + { + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore); + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *pRedlData, rPam ), true); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + else + pDoc->getIDocumentRedlineAccess().SplitRedline( rPam ); + rPam.Exchange(); + } + rPam.DeleteMark(); + } + } +} + +void SwUndoSplitNode::RepeatImpl(::sw::RepeatContext & rContext) +{ + rContext.GetDoc().getIDocumentContentOperations().SplitNode( + *rContext.GetRepeatPaM().GetPoint(), bChkTableStt ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/untbl.cxx b/sw/source/core/undo/untbl.cxx new file mode 100644 index 000000000..1cbf5c1bd --- /dev/null +++ b/sw/source/core/undo/untbl.cxx @@ -0,0 +1,3155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoTable.hxx> +#include <UndoRedline.hxx> +#include <UndoDelete.hxx> +#include <UndoSplitMove.hxx> +#include <UndoCore.hxx> +#include <fesh.hxx> +#include <hintids.hxx> +#include <hints.hxx> +#include <doc.hxx> +#include <docredln.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentChartDataProviderAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <editsh.hxx> +#include <docary.hxx> +#include <ndtxt.hxx> +#include <swtable.hxx> +#include <pam.hxx> +#include <tblsel.hxx> +#include <swundo.hxx> +#include <rolbck.hxx> +#include <ddefld.hxx> +#include <tabfrm.hxx> +#include <rowfrm.hxx> +#include <cellfrm.hxx> +#include <swcache.hxx> +#include <tblafmt.hxx> +#include <poolfmt.hxx> +#include <mvsave.hxx> +#include <cellatr.hxx> +#include <swtblfmt.hxx> +#include <swddetbl.hxx> +#include <redline.hxx> +#include <node2lay.hxx> +#include <tblrwcl.hxx> +#include <fmtanchr.hxx> +#include <strings.hrc> +#include <unochart.hxx> +#include <calbck.hxx> +#include <frameformats.hxx> + +#include <memory> +#include <utility> +#include <vector> + +#ifdef DBG_UTIL +#define CHECK_TABLE(t) (t).CheckConsistency(); +#else +#define CHECK_TABLE(t) +#endif + +#ifdef DBG_UTIL + #define DEBUG_REDLINE( pDoc ) sw_DebugRedline( pDoc ); +#else + #define DEBUG_REDLINE( pDoc ) +#endif + +typedef std::vector<std::shared_ptr<SfxItemSet> > SfxItemSets; + +struct UndoTableCpyTable_Entry +{ + sal_uLong nBoxIdx, nOffset; + std::unique_ptr<SfxItemSet> pBoxNumAttr; + std::unique_ptr<SwUndo> pUndo; + + // Was the last paragraph of the new and the first paragraph of the old content joined? + bool bJoin; // For redlining only + + explicit UndoTableCpyTable_Entry( const SwTableBox& rBox ); +}; + +namespace { + +class SaveBox; +class SaveLine; + +} + +class SaveTable +{ + friend SaveBox; + friend SaveLine; + SfxItemSet m_aTableSet; + std::unique_ptr<SaveLine> m_pLine; + const SwTable* m_pSwTable; + SfxItemSets m_aSets; + SwFrameFormatsV m_aFrameFormats; + sal_uInt16 m_nLineCount; + bool m_bModifyBox : 1; + bool m_bSaveFormula : 1; + bool m_bNewModel : 1; + + SaveTable(const SaveTable&) = delete; + SaveTable& operator=(const SaveTable&) = delete; + +public: + SaveTable( const SwTable& rTable, sal_uInt16 nLnCnt = USHRT_MAX, + bool bSaveFormula = true ); + + sal_uInt16 AddFormat( SwFrameFormat* pFormat, bool bIsLine ); + void NewFrameFormat( const SwTableLine* , const SwTableBox*, sal_uInt16 nFormatPos, + SwFrameFormat* pOldFormat ); + + void RestoreAttr( SwTable& rTable, bool bModifyBox = false ); + void SaveContentAttrs( SwDoc* pDoc ); + void CreateNew( SwTable& rTable, bool bCreateFrames = true, + bool bRestoreChart = true ); + bool IsNewModel() const { return m_bNewModel; } +}; + +namespace { + +class SaveLine +{ + friend SaveTable; + friend class SaveBox; + + SaveLine* pNext; + SaveBox* pBox; + sal_uInt16 nItemSet; + + SaveLine(const SaveLine&) = delete; + SaveLine& operator=(const SaveLine&) = delete; + +public: + SaveLine( SaveLine* pPrev, const SwTableLine& rLine, SaveTable& rSTable ); + ~SaveLine(); + + void RestoreAttr( SwTableLine& rLine, SaveTable& rSTable ); + void SaveContentAttrs( SwDoc* pDoc ); + + void CreateNew( SwTable& rTable, SwTableBox& rParent, SaveTable& rSTable ); +}; + +class SaveBox +{ + friend class SaveLine; + + SaveBox* pNext; + sal_uLong nSttNode; + long nRowSpan; + sal_uInt16 nItemSet; + union + { + SfxItemSets* pContentAttrs; + SaveLine* pLine; + } Ptrs; + +public: + SaveBox( SaveBox* pPrev, const SwTableBox& rBox, SaveTable& rSTable ); + ~SaveBox(); + + void RestoreAttr( SwTableBox& rBox, SaveTable& rSTable ); + void SaveContentAttrs( SwDoc* pDoc ); + + void CreateNew( SwTable& rTable, SwTableLine& rParent, SaveTable& rSTable ); +}; + +} + +#if OSL_DEBUG_LEVEL > 0 +static void CheckTable( const SwTable& ); +#define CHECKTABLE(t) CheckTable( t ); +#else +#define CHECKTABLE(t) +#endif + +/* #130880: Crash in undo of table to text when the table has (freshly) merged cells +The order of cell content nodes in the nodes array is not given by the recursive table structure. +The algorithm must not rely on this even it holds for a fresh loaded table in odt file format. +So we need to remember not only the start node position but the end node position as well. +*/ + +struct SwTableToTextSave +{ + sal_uLong m_nSttNd; + sal_uLong m_nEndNd; + sal_Int32 m_nContent; + std::unique_ptr<SwHistory> m_pHstry; + // metadata references for first and last paragraph in cell + std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoStart; + std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoEnd; + + SwTableToTextSave( SwDoc& rDoc, sal_uLong nNd, sal_uLong nEndIdx, sal_Int32 nContent ); + +private: + SwTableToTextSave(const SwTableToTextSave&) = delete; + SwTableToTextSave& operator=(const SwTableToTextSave&) = delete; + +}; + +sal_uInt16 const aSave_BoxContentSet[] = { + RES_CHRATR_COLOR, RES_CHRATR_CROSSEDOUT, + RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, + RES_CHRATR_POSTURE, RES_CHRATR_POSTURE, + RES_CHRATR_SHADOWED, RES_CHRATR_WEIGHT, + RES_PARATR_ADJUST, RES_PARATR_ADJUST, + 0 }; + +SwUndoInsTable::SwUndoInsTable( const SwPosition& rPos, sal_uInt16 nCl, sal_uInt16 nRw, + sal_uInt16 nAdj, const SwInsertTableOptions& rInsTableOpts, + const SwTableAutoFormat* pTAFormat, + const std::vector<sal_uInt16> *pColArr, + const OUString & rName) + : SwUndo( SwUndoId::INSTABLE, rPos.GetDoc() ), + m_aInsTableOptions( rInsTableOpts ), + m_nStartNode( rPos.nNode.GetIndex() ), m_nRows( nRw ), m_nColumns( nCl ), m_nAdjust( nAdj ) +{ + if( pColArr ) + { + m_pColumnWidth.reset( new std::vector<sal_uInt16>(*pColArr) ); + } + if( pTAFormat ) + m_pAutoFormat.reset( new SwTableAutoFormat( *pTAFormat ) ); + + // consider redline + SwDoc& rDoc = *rPos.nNode.GetNode().GetDoc(); + if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) + { + m_pRedlineData.reset( new SwRedlineData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() ); + } + + m_sTableName = rName; +} + +SwUndoInsTable::~SwUndoInsTable() +{ + m_pDDEFieldType.reset(); + m_pColumnWidth.reset(); + m_pRedlineData.reset(); + m_pAutoFormat.reset(); +} + +void SwUndoInsTable::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwNodeIndex aIdx( rDoc.GetNodes(), m_nStartNode ); + + SwTableNode* pTableNd = aIdx.GetNode().GetTableNode(); + OSL_ENSURE( pTableNd, "no TableNode" ); + pTableNd->DelFrames(); + + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + rDoc.getIDocumentRedlineAccess().DeleteRedline( *pTableNd, true, RedlineType::Any ); + RemoveIdxFromSection( rDoc, m_nStartNode ); + + // move hard page breaks into next node + SwContentNode* pNextNd = rDoc.GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode(); + if( pNextNd ) + { + SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat(); + const SfxPoolItem *pItem; + + if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC, + false, &pItem ) ) + pNextNd->SetAttr( *pItem ); + + if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK, + false, &pItem ) ) + pNextNd->SetAttr( *pItem ); + } + + m_sTableName = pTableNd->GetTable().GetFrameFormat()->GetName(); + if( auto pDDETable = dynamic_cast<const SwDDETable *>(&pTableNd->GetTable()) ) + m_pDDEFieldType.reset(static_cast<SwDDEFieldType*>(pDDETable->GetDDEFieldType()->Copy().release())); + + rDoc.GetNodes().Delete( aIdx, pTableNd->EndOfSectionIndex() - + aIdx.GetIndex() + 1 ); + + SwPaM & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() ); + rPam.DeleteMark(); + rPam.GetPoint()->nNode = aIdx; + rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), 0 ); +} + +void SwUndoInsTable::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + SwPosition const aPos(SwNodeIndex(rDoc.GetNodes(), m_nStartNode)); + const SwTable* pTable = rDoc.InsertTable( m_aInsTableOptions, aPos, m_nRows, m_nColumns, + m_nAdjust, + m_pAutoFormat.get(), m_pColumnWidth.get() ); + rDoc.GetEditShell()->MoveTable( GotoPrevTable, fnTableStart ); + static_cast<SwFrameFormat*>(pTable->GetFrameFormat())->SetName( m_sTableName ); + SwTableNode* pTableNode = rDoc.GetNodes()[m_nStartNode]->GetTableNode(); + + if( m_pDDEFieldType ) + { + SwDDEFieldType* pNewType = static_cast<SwDDEFieldType*>(rDoc.getIDocumentFieldsAccess().InsertFieldType( + *m_pDDEFieldType)); + std::unique_ptr<SwDDETable> pDDETable(new SwDDETable( pTableNode->GetTable(), pNewType )); + pTableNode->SetNewTable( std::move(pDDETable) ); + m_pDDEFieldType.reset(); + } + + if( (m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) || + ( !( RedlineFlags::Ignore & GetRedlineFlags() ) && + !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )) + { + SwPaM aPam( *pTableNode->EndOfSectionNode(), *pTableNode, 1 ); + SwContentNode* pCNd = aPam.GetContentNode( false ); + if( pCNd ) + aPam.GetMark()->nContent.Assign( pCNd, 0 ); + + if( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ) + { + RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore); + + rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, aPam ), true); + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + else + rDoc.getIDocumentRedlineAccess().SplitRedline( aPam ); + } +} + +void SwUndoInsTable::RepeatImpl(::sw::RepeatContext & rContext) +{ + rContext.GetDoc().InsertTable( + m_aInsTableOptions, *rContext.GetRepeatPaM().GetPoint(), + m_nRows, m_nColumns, m_nAdjust, m_pAutoFormat.get(), m_pColumnWidth.get() ); +} + +SwRewriter SwUndoInsTable::GetRewriter() const +{ + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, SwResId(STR_START_QUOTE)); + aRewriter.AddRule(UndoArg2, m_sTableName); + aRewriter.AddRule(UndoArg3, SwResId(STR_END_QUOTE)); + + return aRewriter; +} + +SwTableToTextSave::SwTableToTextSave( SwDoc& rDoc, sal_uLong nNd, sal_uLong nEndIdx, sal_Int32 nCnt ) + : m_nSttNd( nNd ), m_nEndNd( nEndIdx), m_nContent( nCnt ) +{ + // keep attributes of the joined node + SwTextNode* pNd = rDoc.GetNodes()[ nNd ]->GetTextNode(); + if( pNd ) + { + m_pHstry.reset( new SwHistory ); + + m_pHstry->Add( pNd->GetTextColl(), nNd, SwNodeType::Text ); + if ( pNd->GetpSwpHints() ) + { + m_pHstry->CopyAttr( pNd->GetpSwpHints(), nNd, 0, + pNd->GetText().getLength(), false ); + } + if( pNd->HasSwAttrSet() ) + m_pHstry->CopyFormatAttr( *pNd->GetpSwAttrSet(), nNd ); + + if( !m_pHstry->Count() ) + { + m_pHstry.reset(); + } + + // METADATA: store + m_pMetadataUndoStart = pNd->CreateUndo(); + } + + // we also need to store the metadata reference of the _last_ paragraph + // we subtract 1 to account for the removed cell start/end node pair + // (after SectionUp, the end of the range points to the node after the cell) + if ( nEndIdx - 1 > nNd ) + { + SwTextNode* pLastNode( rDoc.GetNodes()[ nEndIdx - 1 ]->GetTextNode() ); + if( pLastNode ) + { + // METADATA: store + m_pMetadataUndoEnd = pLastNode->CreateUndo(); + } + } +} + +SwUndoTableToText::SwUndoTableToText( const SwTable& rTable, sal_Unicode cCh ) + : SwUndo( SwUndoId::TABLETOTEXT, rTable.GetFrameFormat()->GetDoc() ), + m_sTableName( rTable.GetFrameFormat()->GetName() ), + m_nStartNode( 0 ), m_nEndNode( 0 ), + m_cSeparator( cCh ), m_nHeadlineRepeat( rTable.GetRowsToRepeat() ) +{ + m_pTableSave.reset( new SaveTable( rTable ) ); + m_vBoxSaves.reserve(rTable.GetTabSortBoxes().size()); + + if( auto pDDETable = dynamic_cast<const SwDDETable *>(&rTable) ) + m_pDDEFieldType.reset(static_cast<SwDDEFieldType*>(pDDETable->GetDDEFieldType()->Copy().release())); + + m_bCheckNumFormat = rTable.GetFrameFormat()->GetDoc()->IsInsTableFormatNum(); + + m_pHistory.reset(new SwHistory); + const SwTableNode* pTableNd = rTable.GetTableNode(); + sal_uLong nTableStt = pTableNd->GetIndex(), nTableEnd = pTableNd->EndOfSectionIndex(); + + const SwFrameFormats& rFrameFormatTable = *pTableNd->GetDoc()->GetSpzFrameFormats(); + for( size_t n = 0; n < rFrameFormatTable.size(); ++n ) + { + SwFrameFormat* pFormat = rFrameFormatTable[ n ]; + SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor(); + SwPosition const*const pAPos = pAnchor->GetContentAnchor(); + if (pAPos && + ((RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) || + (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) && + nTableStt <= pAPos->nNode.GetIndex() && + pAPos->nNode.GetIndex() < nTableEnd ) + { + m_pHistory->AddChangeFlyAnchor(*pFormat); + } + } + + if( !m_pHistory->Count() ) + { + m_pHistory.reset(); + } +} + +SwUndoTableToText::~SwUndoTableToText() +{ + m_pDDEFieldType.reset(); + m_pTableSave.reset(); + m_vBoxSaves.clear(); + m_pHistory.reset(); +} + +void SwUndoTableToText::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + SwNodeIndex aFrameIdx( rDoc.GetNodes(), m_nStartNode ); + SwNodeIndex aEndIdx( rDoc.GetNodes(), m_nEndNode ); + + pPam->GetPoint()->nNode = aFrameIdx; + pPam->SetMark(); + pPam->GetPoint()->nNode = aEndIdx; + rDoc.DelNumRules( *pPam ); + pPam->DeleteMark(); + + // now collect all Uppers + SwNode2LayoutSaveUpperFrames aNode2Layout(aFrameIdx.GetNode()); + + // create TableNode structure + SwTableNode* pTableNd = rDoc.GetNodes().UndoTableToText( m_nStartNode, m_nEndNode, m_vBoxSaves ); + pTableNd->GetTable().SetTableModel( m_pTableSave->IsNewModel() ); + SwTableFormat* pTableFormat = rDoc.MakeTableFrameFormat( m_sTableName, rDoc.GetDfltFrameFormat() ); + pTableNd->GetTable().RegisterToFormat( *pTableFormat ); + pTableNd->GetTable().SetRowsToRepeat( m_nHeadlineRepeat ); + + // create old table structure + m_pTableSave->CreateNew( pTableNd->GetTable() ); + + if( m_pDDEFieldType ) + { + SwDDEFieldType* pNewType = static_cast<SwDDEFieldType*>(rDoc.getIDocumentFieldsAccess().InsertFieldType( + *m_pDDEFieldType)); + std::unique_ptr<SwDDETable> pDDETable( new SwDDETable( pTableNd->GetTable(), pNewType ) ); + pTableNd->SetNewTable( std::move(pDDETable), false ); + m_pDDEFieldType.reset(); + } + + if( m_bCheckNumFormat ) + { + SwTableSortBoxes& rBxs = pTableNd->GetTable().GetTabSortBoxes(); + for (size_t nBoxes = rBxs.size(); nBoxes; ) + { + rDoc.ChkBoxNumFormat( *rBxs[ --nBoxes ], false ); + } + } + + if( m_pHistory ) + { + sal_uInt16 nTmpEnd = m_pHistory->GetTmpEnd(); + m_pHistory->TmpRollback( &rDoc, 0 ); + m_pHistory->SetTmpEnd( nTmpEnd ); + } + + aNode2Layout.RestoreUpperFrames( rDoc.GetNodes(), + pTableNd->GetIndex(), pTableNd->GetIndex()+1 ); + + // Is a table selection requested? + pPam->DeleteMark(); + pPam->GetPoint()->nNode = *pTableNd->EndOfSectionNode(); + pPam->SetMark(); + pPam->GetPoint()->nNode = *pPam->GetNode().StartOfSectionNode(); + pPam->Move( fnMoveForward, GoInContent ); + pPam->Exchange(); + pPam->Move( fnMoveBackward, GoInContent ); + + ClearFEShellTabCols(rDoc, nullptr); +} + +// located in untbl.cxx and only an Undo object is allowed to call it +SwTableNode* SwNodes::UndoTableToText( sal_uLong nSttNd, sal_uLong nEndNd, + const SwTableToTextSaves& rSavedData ) +{ + SwNodeIndex aSttIdx( *this, nSttNd ); + SwNodeIndex aEndIdx( *this, nEndNd+1 ); + + SwTableNode * pTableNd = new SwTableNode( aSttIdx ); + SwEndNode* pEndNd = new SwEndNode( aEndIdx, *pTableNd ); + + aEndIdx = *pEndNd; + + /* Set pTableNd as start of section for all nodes in [nSttNd, nEndNd]. + Delete all Frames attached to the nodes in that range. */ + SwNode* pNd; + { + sal_uLong n, nTmpEnd = aEndIdx.GetIndex(); + for( n = pTableNd->GetIndex() + 1; n < nTmpEnd; ++n ) + { + if( ( pNd = (*this)[ n ] )->IsContentNode() ) + static_cast<SwContentNode*>(pNd)->DelFrames(nullptr); + pNd->m_pStartOfSection = pTableNd; + } + } + + // than create table structure partially. First a single line that contains + // all boxes. The correct structure is then taken from SaveStruct. + SwTableBoxFormat* pBoxFormat = GetDoc()->MakeTableBoxFormat(); + SwTableLineFormat* pLineFormat = GetDoc()->MakeTableLineFormat(); + SwTableLine* pLine = new SwTableLine( pLineFormat, rSavedData.size(), nullptr ); + pTableNd->GetTable().GetTabLines().insert( pTableNd->GetTable().GetTabLines().begin(), pLine ); + + const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create()); + for( size_t n = rSavedData.size(); n; ) + { + const SwTableToTextSave *const pSave = rSavedData[ --n ].get(); + // if the start node was merged with last from prev. cell, + // subtract 1 from index to get the merged paragraph, and split that + aSttIdx = pSave->m_nSttNd - ( ( SAL_MAX_INT32 != pSave->m_nContent ) ? 1 : 0); + SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode(); + + if( SAL_MAX_INT32 != pSave->m_nContent ) + { + // split at ContentPosition, delete previous char (= separator) + OSL_ENSURE( pTextNd, "Where is my TextNode?" ); + SwIndex aCntPos( pTextNd, pSave->m_nContent - 1 ); + + pTextNd->EraseText( aCntPos, 1 ); + + std::function<void (SwTextNode *, sw::mark::RestoreMode)> restoreFunc( + [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode) + { + if (!pContentStore->Empty()) + { + pContentStore->Restore(*pNewNode, pSave->m_nContent, pSave->m_nContent + 1, eMode); + } + }); + pTextNd->SplitContentNode( + SwPosition(aSttIdx, aCntPos), &restoreFunc); + } + else + { + pContentStore->Clear(); + if( pTextNd ) + pContentStore->Save( GetDoc(), aSttIdx.GetIndex(), pTextNd->GetText().getLength() ); + } + + if( pTextNd ) + { + // METADATA: restore + pTextNd->GetTextNode()->RestoreMetadata(pSave->m_pMetadataUndoStart); + if( pTextNd->HasSwAttrSet() ) + pTextNd->ResetAllAttr(); + + if( pTextNd->GetpSwpHints() ) + pTextNd->ClearSwpHintsArr( false ); + } + + if( pSave->m_pHstry ) + { + sal_uInt16 nTmpEnd = pSave->m_pHstry->GetTmpEnd(); + pSave->m_pHstry->TmpRollback( GetDoc(), 0 ); + pSave->m_pHstry->SetTmpEnd( nTmpEnd ); + } + + // METADATA: restore + // end points to node after cell + if ( pSave->m_nEndNd - 1 > pSave->m_nSttNd ) + { + SwTextNode* pLastNode = (*this)[ pSave->m_nEndNd - 1 ]->GetTextNode(); + if (pLastNode) + { + pLastNode->RestoreMetadata(pSave->m_pMetadataUndoEnd); + } + } + + aEndIdx = pSave->m_nEndNd; + SwStartNode* pSttNd = new SwStartNode( aSttIdx, SwNodeType::Start, + SwTableBoxStartNode ); + pSttNd->m_pStartOfSection = pTableNd; + new SwEndNode( aEndIdx, *pSttNd ); + + for( sal_uLong i = aSttIdx.GetIndex(); i < aEndIdx.GetIndex()-1; ++i ) + { + pNd = (*this)[ i ]; + pNd->m_pStartOfSection = pSttNd; + if( pNd->IsStartNode() ) + i = pNd->EndOfSectionIndex(); + } + + SwTableBox* pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine ); + pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin(), pBox ); + } + return pTableNd; +} + +void SwUndoTableToText::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + pPam->GetPoint()->nNode = m_nStartNode; + pPam->GetPoint()->nContent.Assign( nullptr, 0 ); + SwNodeIndex aSaveIdx( pPam->GetPoint()->nNode, -1 ); + + pPam->SetMark(); // log off all indices + pPam->DeleteMark(); + + SwTableNode* pTableNd = pPam->GetNode().GetTableNode(); + OSL_ENSURE( pTableNd, "Could not find any TableNode" ); + + if( auto pDDETable = dynamic_cast<const SwDDETable *>(&pTableNd->GetTable()) ) + m_pDDEFieldType.reset(static_cast<SwDDEFieldType*>(pDDETable->GetDDEFieldType()->Copy().release())); + + rDoc.TableToText( pTableNd, m_cSeparator ); + + ++aSaveIdx; + SwContentNode* pCNd = aSaveIdx.GetNode().GetContentNode(); + if( !pCNd && nullptr == ( pCNd = rDoc.GetNodes().GoNext( &aSaveIdx ) ) && + nullptr == ( pCNd = SwNodes::GoPrevious( &aSaveIdx )) ) + { + OSL_FAIL( "Where is the TextNode now?" ); + } + + pPam->GetPoint()->nNode = aSaveIdx; + pPam->GetPoint()->nContent.Assign( pCNd, 0 ); + + pPam->SetMark(); // log off all indices + pPam->DeleteMark(); +} + +void SwUndoTableToText::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwPaM *const pPam = & rContext.GetRepeatPaM(); + SwTableNode *const pTableNd = pPam->GetNode().FindTableNode(); + if( pTableNd ) + { + // move cursor out of table + pPam->GetPoint()->nNode = *pTableNd->EndOfSectionNode(); + pPam->Move( fnMoveForward, GoInContent ); + pPam->SetMark(); + pPam->DeleteMark(); + + rContext.GetDoc().TableToText( pTableNd, m_cSeparator ); + } +} + +void SwUndoTableToText::SetRange( const SwNodeRange& rRg ) +{ + m_nStartNode = rRg.aStart.GetIndex(); + m_nEndNode = rRg.aEnd.GetIndex(); +} + +void SwUndoTableToText::AddBoxPos( SwDoc& rDoc, sal_uLong nNdIdx, sal_uLong nEndIdx, sal_Int32 nContentIdx ) +{ + m_vBoxSaves.push_back(std::make_unique<SwTableToTextSave>(rDoc, nNdIdx, nEndIdx, nContentIdx)); +} + +SwUndoTextToTable::SwUndoTextToTable( const SwPaM& rRg, + const SwInsertTableOptions& rInsTableOpts, + sal_Unicode cCh, sal_uInt16 nAdj, + const SwTableAutoFormat* pAFormat ) + : SwUndo( SwUndoId::TEXTTOTABLE, rRg.GetDoc() ), SwUndRng( rRg ), m_aInsertTableOpts( rInsTableOpts ), + m_pHistory( nullptr ), m_cSeparator( cCh ), m_nAdjust( nAdj ) +{ + if( pAFormat ) + m_pAutoFormat.reset( new SwTableAutoFormat( *pAFormat ) ); + + const SwPosition* pEnd = rRg.End(); + SwNodes& rNds = rRg.GetDoc()->GetNodes(); + m_bSplitEnd = pEnd->nContent.GetIndex() && ( pEnd->nContent.GetIndex() + != pEnd->nNode.GetNode().GetContentNode()->Len() || + pEnd->nNode.GetIndex() >= rNds.GetEndOfContent().GetIndex()-1 ); +} + +SwUndoTextToTable::~SwUndoTextToTable() +{ + m_pAutoFormat.reset(); +} + +void SwUndoTextToTable::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + sal_uLong nTableNd = m_nSttNode; + if( m_nSttContent ) + ++nTableNd; // Node was split previously + SwNodeIndex aIdx( rDoc.GetNodes(), nTableNd ); + SwTableNode *const pTNd = aIdx.GetNode().GetTableNode(); + OSL_ENSURE( pTNd, "Could not find a TableNode" ); + + RemoveIdxFromSection( rDoc, nTableNd ); + + m_sTableName = pTNd->GetTable().GetFrameFormat()->GetName(); + + if( m_pHistory ) + { + m_pHistory->TmpRollback( &rDoc, 0 ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + } + + if( !mvDelBoxes.empty() ) + { + pTNd->DelFrames(); + SwTable& rTable = pTNd->GetTable(); + for( size_t n = mvDelBoxes.size(); n; ) + { + SwTableBox* pBox = rTable.GetTableBox( mvDelBoxes[ --n ] ); + if( pBox ) + ::DeleteBox_( rTable, pBox, nullptr, false, false ); + else { + OSL_ENSURE( false, "Where is my box?" ); + } + } + } + + rDoc.TableToText( pTNd, 0x0b == m_cSeparator ? 0x09 : m_cSeparator ); + + // join again at start? + SwPaM aPam(rDoc.GetNodes().GetEndOfContent()); + SwPosition *const pPos = aPam.GetPoint(); + if( m_nSttContent ) + { + pPos->nNode = nTableNd; + pPos->nContent.Assign(pPos->nNode.GetNode().GetContentNode(), 0); + if (aPam.Move(fnMoveBackward, GoInContent)) + { + SwNodeIndex & rIdx = aPam.GetPoint()->nNode; + + // than move, relatively, the Cursor/etc. again + RemoveIdxRel( rIdx.GetIndex()+1, *pPos ); + + rIdx.GetNode().GetContentNode()->JoinNext(); + } + } + + // join again at end? + if( m_bSplitEnd ) + { + SwNodeIndex& rIdx = pPos->nNode; + rIdx = m_nEndNode; + SwTextNode* pTextNd = rIdx.GetNode().GetTextNode(); + if( pTextNd && pTextNd->CanJoinNext() ) + { + aPam.GetMark()->nContent.Assign( nullptr, 0 ); + aPam.GetPoint()->nContent.Assign( nullptr, 0 ); + + // than move, relatively, the Cursor/etc. again + pPos->nContent.Assign(pTextNd, pTextNd->GetText().getLength()); + RemoveIdxRel( m_nEndNode + 1, *pPos ); + + pTextNd->JoinNext(); + } + } + + AddUndoRedoPaM(rContext); +} + +void SwUndoTextToTable::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + RemoveIdxFromRange(rPam, false); + SetPaM(rPam); + + SwTable const*const pTable = rContext.GetDoc().TextToTable( + m_aInsertTableOpts, rPam, m_cSeparator, m_nAdjust, m_pAutoFormat.get() ); + static_cast<SwFrameFormat*>(pTable->GetFrameFormat())->SetName( m_sTableName ); +} + +void SwUndoTextToTable::RepeatImpl(::sw::RepeatContext & rContext) +{ + // no Table In Table + if (!rContext.GetRepeatPaM().GetNode().FindTableNode()) + { + rContext.GetDoc().TextToTable( m_aInsertTableOpts, rContext.GetRepeatPaM(), + m_cSeparator, m_nAdjust, + m_pAutoFormat.get() ); + } +} + +void SwUndoTextToTable::AddFillBox( const SwTableBox& rBox ) +{ + mvDelBoxes.push_back( rBox.GetSttIdx() ); +} + +SwHistory& SwUndoTextToTable::GetHistory() +{ + if( !m_pHistory ) + m_pHistory = new SwHistory; + return *m_pHistory; +} + +SwUndoTableHeadline::SwUndoTableHeadline( const SwTable& rTable, sal_uInt16 nOldHdl, + sal_uInt16 nNewHdl ) + : SwUndo( SwUndoId::TABLEHEADLINE, rTable.GetFrameFormat()->GetDoc() ), + m_nOldHeadline( nOldHdl ), + m_nNewHeadline( nNewHdl ) +{ + OSL_ENSURE( !rTable.GetTabSortBoxes().empty(), "Table without content" ); + const SwStartNode *pSttNd = rTable.GetTabSortBoxes()[ 0 ]->GetSttNd(); + OSL_ENSURE( pSttNd, "Box without content" ); + + m_nTableNode = pSttNd->StartOfSectionIndex(); +} + +void SwUndoTableHeadline::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwTableNode* pTNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode(); + OSL_ENSURE( pTNd, "could not find any TableNode" ); + + rDoc.SetRowsToRepeat( pTNd->GetTable(), m_nOldHeadline ); +} + +void SwUndoTableHeadline::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + SwTableNode* pTNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode(); + OSL_ENSURE( pTNd, "could not find any TableNode" ); + + rDoc.SetRowsToRepeat( pTNd->GetTable(), m_nNewHeadline ); +} + +void SwUndoTableHeadline::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwTableNode *const pTableNd = + rContext.GetRepeatPaM().GetNode().FindTableNode(); + if( pTableNd ) + { + rContext.GetDoc().SetRowsToRepeat( pTableNd->GetTable(), m_nNewHeadline ); + } +} + +SaveTable::SaveTable( const SwTable& rTable, sal_uInt16 nLnCnt, bool bSaveFormula ) + : m_aTableSet(*rTable.GetFrameFormat()->GetAttrSet().GetPool(), aTableSetRange), + m_pSwTable(&rTable), m_nLineCount(nLnCnt), m_bSaveFormula(bSaveFormula) +{ + m_bModifyBox = false; + m_bNewModel = rTable.IsNewModel(); + m_aTableSet.Put(rTable.GetFrameFormat()->GetAttrSet()); + m_pLine.reset( new SaveLine( nullptr, *rTable.GetTabLines()[ 0 ], *this ) ); + + SaveLine* pLn = m_pLine.get(); + if( USHRT_MAX == nLnCnt ) + nLnCnt = rTable.GetTabLines().size(); + for( sal_uInt16 n = 1; n < nLnCnt; ++n ) + pLn = new SaveLine( pLn, *rTable.GetTabLines()[ n ], *this ); + + m_aFrameFormats.clear(); + m_pSwTable = nullptr; +} + +sal_uInt16 SaveTable::AddFormat( SwFrameFormat* pFormat, bool bIsLine ) +{ + size_t nRet = m_aFrameFormats.GetPos(pFormat); + if( SIZE_MAX == nRet ) + { + // Create copy of ItemSet + auto pSet = std::make_shared<SfxItemSet>( *pFormat->GetAttrSet().GetPool(), + bIsLine ? aTableLineSetRange : aTableBoxSetRange ); + pSet->Put( pFormat->GetAttrSet() ); + // When a formula is set, never save the value. It possibly must be + // recalculated. + // Save formulas always in plain text. + const SfxPoolItem* pItem; + if( SfxItemState::SET == pSet->GetItemState( RES_BOXATR_FORMULA, true, &pItem )) + { + pSet->ClearItem( RES_BOXATR_VALUE ); + if (m_pSwTable && m_bSaveFormula) + { + SwTableFormulaUpdate aMsgHint(m_pSwTable); + aMsgHint.m_eFlags = TBL_BOXNAME; + SwTableBoxFormula* pFormulaItem = const_cast<SwTableBoxFormula*>(static_cast<const SwTableBoxFormula*>(pItem)); + pFormulaItem->ChgDefinedIn( pFormat ); + pFormulaItem->ChangeState( &aMsgHint ); + pFormulaItem->ChgDefinedIn( nullptr ); + } + } + nRet = m_aSets.size(); + m_aSets.push_back(pSet); + m_aFrameFormats.insert(m_aFrameFormats.begin() + nRet, pFormat); + } + return static_cast<sal_uInt16>(nRet); +} + +void SaveTable::RestoreAttr( SwTable& rTable, bool bMdfyBox ) +{ + m_bModifyBox = bMdfyBox; + + // first, get back attributes of TableFrameFormat + SwFrameFormat* pFormat = rTable.GetFrameFormat(); + SfxItemSet& rFormatSet = const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pFormat->GetAttrSet())); + rFormatSet.ClearItem(); + rFormatSet.Put(m_aTableSet); + + if( pFormat->IsInCache() ) + { + SwFrame::GetCache().Delete( pFormat ); + pFormat->SetInCache( false ); + } + + // for safety, invalidate all TableFrames + SwIterator<SwTabFrame,SwFormat> aIter( *pFormat ); + for( SwTabFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() ) + if( pLast->GetTable() == &rTable ) + { + pLast->InvalidateAll(); + pLast->SetCompletePaint(); + } + + // fill FrameFormats with defaults (0) + pFormat = nullptr; + for (size_t n = m_aSets.size(); n; --n) + m_aFrameFormats.push_back(pFormat); + + const size_t nLnCnt = (USHRT_MAX == m_nLineCount) + ? rTable.GetTabLines().size() + : m_nLineCount; + + SaveLine* pLn = m_pLine.get(); + for( size_t n = 0; n < nLnCnt; ++n, pLn = pLn->pNext ) + { + if( !pLn ) + { + OSL_ENSURE( false, "Number of lines changed" ); + break; + } + + pLn->RestoreAttr( *rTable.GetTabLines()[ n ], *this ); + } + + m_aFrameFormats.clear(); + m_bModifyBox = false; +} + +void SaveTable::SaveContentAttrs( SwDoc* pDoc ) +{ + m_pLine->SaveContentAttrs(pDoc); +} + +void SaveTable::CreateNew( SwTable& rTable, bool bCreateFrames, + bool bRestoreChart ) +{ + FndBox_ aTmpBox( nullptr, nullptr ); + aTmpBox.DelFrames( rTable ); + + // first, get back attributes of TableFrameFormat + SwFrameFormat* pFormat = rTable.GetFrameFormat(); + SfxItemSet& rFormatSet = const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pFormat->GetAttrSet())); + rFormatSet.ClearItem(); + rFormatSet.Put(m_aTableSet); + + if( pFormat->IsInCache() ) + { + SwFrame::GetCache().Delete( pFormat ); + pFormat->SetInCache( false ); + } + + // SwTableBox must have a format - the SwTableBox takes ownership of it + SwTableBoxFormat *const pNewFormat(pFormat->GetDoc()->MakeTableBoxFormat()); + SwTableBox aParent(pNewFormat, rTable.GetTabLines().size(), nullptr); + + // fill FrameFormats with defaults (0) + pFormat = nullptr; + for( size_t n = m_aSets.size(); n; --n ) + m_aFrameFormats.push_back(pFormat); + + m_pLine->CreateNew(rTable, aParent, *this); + m_aFrameFormats.clear(); + + // add new lines, delete old ones + const size_t nOldLines = (USHRT_MAX == m_nLineCount) + ? rTable.GetTabLines().size() + : m_nLineCount; + + SwDoc *pDoc = rTable.GetFrameFormat()->GetDoc(); + SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider(); + size_t n = 0; + for( ; n < aParent.GetTabLines().size(); ++n ) + { + SwTableLine* pLn = aParent.GetTabLines()[ n ]; + pLn->SetUpper( nullptr ); + if( n < nOldLines ) + { + SwTableLine* pOld = rTable.GetTabLines()[ n ]; + + // TL_CHART2: notify chart about boxes to be removed + const SwTableBoxes &rBoxes = pOld->GetTabBoxes(); + const size_t nBoxes = rBoxes.size(); + for (size_t k = 0; k < nBoxes; ++k) + { + SwTableBox *pBox = rBoxes[k]; + if (pPCD) + pPCD->DeleteBox( &rTable, *pBox ); + } + + rTable.GetTabLines()[n] = pLn; + delete pOld; + } + else + rTable.GetTabLines().insert( rTable.GetTabLines().begin() + n, pLn ); + } + + if( n < nOldLines ) + { + // remove remaining lines... + for (size_t k1 = 0; k1 < nOldLines - n; ++k1) + { + const SwTableBoxes &rBoxes = rTable.GetTabLines()[n + k1]->GetTabBoxes(); + const size_t nBoxes = rBoxes.size(); + for (size_t k2 = 0; k2 < nBoxes; ++k2) + { + SwTableBox *pBox = rBoxes[k2]; + // TL_CHART2: notify chart about boxes to be removed + if (pPCD) + pPCD->DeleteBox( &rTable, *pBox ); + } + } + + for( SwTableLines::const_iterator it = rTable.GetTabLines().begin() + n; + it != rTable.GetTabLines().begin() + nOldLines; ++it ) + delete *it; + rTable.GetTabLines().erase( rTable.GetTabLines().begin() + n, rTable.GetTabLines().begin() + nOldLines ); + } + + aParent.GetTabLines().erase( aParent.GetTabLines().begin(), aParent.GetTabLines().begin() + n ); + assert(aParent.GetTabLines().empty()); + + if( bCreateFrames ) + aTmpBox.MakeFrames( rTable ); + if( bRestoreChart ) + { + // TL_CHART2: need to inform chart of probably changed cell names + pDoc->UpdateCharts( rTable.GetFrameFormat()->GetName() ); + } +} + +void SaveTable::NewFrameFormat( const SwTableLine* pTableLn, const SwTableBox* pTableBx, + sal_uInt16 nFormatPos, SwFrameFormat* pOldFormat ) +{ + SwDoc* pDoc = pOldFormat->GetDoc(); + + SwFrameFormat* pFormat = m_aFrameFormats[ nFormatPos ]; + if( !pFormat ) + { + if( pTableLn ) + pFormat = pDoc->MakeTableLineFormat(); + else + pFormat = pDoc->MakeTableBoxFormat(); + pFormat->SetFormatAttr(*m_aSets[nFormatPos]); + m_aFrameFormats[nFormatPos] = pFormat; + } + + // first re-assign Frames + SwIterator<SwLayoutFrame,SwFormat> aIter( *pOldFormat ); + for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() ) + { + if( pTableLn ? static_cast<SwRowFrame*>(pLast)->GetTabLine() == pTableLn + : static_cast<SwCellFrame*>(pLast)->GetTabBox() == pTableBx ) + { + pLast->RegisterToFormat(*pFormat); + pLast->InvalidateAll(); + pLast->ReinitializeFrameSizeAttrFlags(); + if ( !pTableLn ) + { + static_cast<SwCellFrame*>(pLast)->SetDerivedVert( false ); + static_cast<SwCellFrame*>(pLast)->CheckDirChange(); + } + } + } + + // than re-assign myself + if ( pTableLn ) + const_cast<SwTableLine*>(pTableLn)->RegisterToFormat( *pFormat ); + else if ( pTableBx ) + const_cast<SwTableBox*>(pTableBx)->RegisterToFormat( *pFormat ); + + if (m_bModifyBox && !pTableLn) + { + const SfxPoolItem& rOld = pOldFormat->GetFormatAttr( RES_BOXATR_FORMAT ), + & rNew = pFormat->GetFormatAttr( RES_BOXATR_FORMAT ); + if( rOld != rNew ) + pFormat->ModifyNotification( &rOld, &rNew ); + } + + if( !pOldFormat->HasWriterListeners() ) + delete pOldFormat; +} + +SaveLine::SaveLine( SaveLine* pPrev, const SwTableLine& rLine, SaveTable& rSTable ) + : pNext( nullptr ) +{ + if( pPrev ) + pPrev->pNext = this; + + nItemSet = rSTable.AddFormat( rLine.GetFrameFormat(), true ); + + pBox = new SaveBox( nullptr, *rLine.GetTabBoxes()[ 0 ], rSTable ); + SaveBox* pBx = pBox; + for( size_t n = 1; n < rLine.GetTabBoxes().size(); ++n ) + pBx = new SaveBox( pBx, *rLine.GetTabBoxes()[ n ], rSTable ); +} + +SaveLine::~SaveLine() +{ + delete pBox; + delete pNext; +} + +void SaveLine::RestoreAttr( SwTableLine& rLine, SaveTable& rSTable ) +{ + rSTable.NewFrameFormat( &rLine, nullptr, nItemSet, rLine.GetFrameFormat() ); + + SaveBox* pBx = pBox; + for( size_t n = 0; n < rLine.GetTabBoxes().size(); ++n, pBx = pBx->pNext ) + { + if( !pBx ) + { + OSL_ENSURE( false, "Number of boxes changed" ); + break; + } + pBx->RestoreAttr( *rLine.GetTabBoxes()[ n ], rSTable ); + } +} + +void SaveLine::SaveContentAttrs( SwDoc* pDoc ) +{ + pBox->SaveContentAttrs( pDoc ); + if( pNext ) + pNext->SaveContentAttrs( pDoc ); +} + +void SaveLine::CreateNew( SwTable& rTable, SwTableBox& rParent, SaveTable& rSTable ) +{ + SwTableLineFormat* pFormat = static_cast<SwTableLineFormat*>(rSTable.m_aFrameFormats[ nItemSet ]); + if( !pFormat ) + { + SwDoc* pDoc = rTable.GetFrameFormat()->GetDoc(); + pFormat = pDoc->MakeTableLineFormat(); + pFormat->SetFormatAttr(*rSTable.m_aSets[nItemSet]); + rSTable.m_aFrameFormats[nItemSet] = pFormat; + } + SwTableLine* pNew = new SwTableLine( pFormat, 1, &rParent ); + + rParent.GetTabLines().push_back( pNew ); + + pBox->CreateNew( rTable, *pNew, rSTable ); + + if( pNext ) + pNext->CreateNew( rTable, rParent, rSTable ); +} + +SaveBox::SaveBox( SaveBox* pPrev, const SwTableBox& rBox, SaveTable& rSTable ) + : pNext( nullptr ), nSttNode( ULONG_MAX ), nRowSpan(0) +{ + Ptrs.pLine = nullptr; + + if( pPrev ) + pPrev->pNext = this; + + nItemSet = rSTable.AddFormat( rBox.GetFrameFormat(), false ); + + if( rBox.GetSttNd() ) + { + nSttNode = rBox.GetSttIdx(); + nRowSpan = rBox.getRowSpan(); + } + else + { + Ptrs.pLine = new SaveLine( nullptr, *rBox.GetTabLines()[ 0 ], rSTable ); + + SaveLine* pLn = Ptrs.pLine; + for( size_t n = 1; n < rBox.GetTabLines().size(); ++n ) + pLn = new SaveLine( pLn, *rBox.GetTabLines()[ n ], rSTable ); + } +} + +SaveBox::~SaveBox() +{ + if( ULONG_MAX == nSttNode ) // no EndBox + delete Ptrs.pLine; + else + delete Ptrs.pContentAttrs; + delete pNext; +} + +void SaveBox::RestoreAttr( SwTableBox& rBox, SaveTable& rSTable ) +{ + rSTable.NewFrameFormat( nullptr, &rBox, nItemSet, rBox.GetFrameFormat() ); + + if( ULONG_MAX == nSttNode ) // no EndBox + { + if( rBox.GetTabLines().empty() ) + { + OSL_ENSURE( false, "Number of lines changed" ); + } + else + { + SaveLine* pLn = Ptrs.pLine; + for( size_t n = 0; n < rBox.GetTabLines().size(); ++n, pLn = pLn->pNext ) + { + if( !pLn ) + { + OSL_ENSURE( false, "Number of lines changed" ); + break; + } + + pLn->RestoreAttr( *rBox.GetTabLines()[ n ], rSTable ); + } + } + } + else if( rBox.GetSttNd() && rBox.GetSttIdx() == nSttNode ) + { + if( Ptrs.pContentAttrs ) + { + SwNodes& rNds = rBox.GetFrameFormat()->GetDoc()->GetNodes(); + sal_uInt16 nSet = 0; + sal_uLong nEnd = rBox.GetSttNd()->EndOfSectionIndex(); + for( sal_uLong n = nSttNode + 1; n < nEnd; ++n ) + { + SwContentNode* pCNd = rNds[ n ]->GetContentNode(); + if( pCNd ) + { + std::shared_ptr<SfxItemSet> pSet( (*Ptrs.pContentAttrs)[ nSet++ ] ); + if( pSet ) + { + sal_uInt16 const *pRstAttr = aSave_BoxContentSet; + while( *pRstAttr ) + { + pCNd->ResetAttr( *pRstAttr, *(pRstAttr+1) ); + pRstAttr += 2; + } + pCNd->SetAttr( *pSet ); + } + else + pCNd->ResetAllAttr(); + } + } + } + } + else + { + OSL_ENSURE( false, "Box not anymore at the same node" ); + } +} + +void SaveBox::SaveContentAttrs( SwDoc* pDoc ) +{ + if( ULONG_MAX == nSttNode ) // no EndBox + { + // continue in current line + Ptrs.pLine->SaveContentAttrs( pDoc ); + } + else + { + sal_uLong nEnd = pDoc->GetNodes()[ nSttNode ]->EndOfSectionIndex(); + Ptrs.pContentAttrs = new SfxItemSets; + for( sal_uLong n = nSttNode + 1; n < nEnd; ++n ) + { + SwContentNode* pCNd = pDoc->GetNodes()[ n ]->GetContentNode(); + if( pCNd ) + { + std::shared_ptr<SfxItemSet> pSet; + if( pCNd->HasSwAttrSet() ) + { + pSet = std::make_shared<SfxItemSet>( pDoc->GetAttrPool(), + aSave_BoxContentSet ); + pSet->Put( *pCNd->GetpSwAttrSet() ); + } + + Ptrs.pContentAttrs->push_back( pSet ); + } + } + } + if( pNext ) + pNext->SaveContentAttrs( pDoc ); +} + +void SaveBox::CreateNew( SwTable& rTable, SwTableLine& rParent, SaveTable& rSTable ) +{ + SwTableBoxFormat* pFormat = static_cast<SwTableBoxFormat*>(rSTable.m_aFrameFormats[ nItemSet ]); + if( !pFormat ) + { + SwDoc* pDoc = rTable.GetFrameFormat()->GetDoc(); + pFormat = pDoc->MakeTableBoxFormat(); + pFormat->SetFormatAttr(*rSTable.m_aSets[nItemSet]); + rSTable.m_aFrameFormats[nItemSet] = pFormat; + } + + if( ULONG_MAX == nSttNode ) // no EndBox + { + SwTableBox* pNew = new SwTableBox( pFormat, 1, &rParent ); + rParent.GetTabBoxes().push_back( pNew ); + + Ptrs.pLine->CreateNew( rTable, *pNew, rSTable ); + } + else + { + // search box for StartNode in old table + SwTableBox* pBox = rTable.GetTableBox( nSttNode ); + if (pBox) + { + SwFrameFormat* pOld = pBox->GetFrameFormat(); + pBox->RegisterToFormat( *pFormat ); + if( !pOld->HasWriterListeners() ) + delete pOld; + + pBox->setRowSpan( nRowSpan ); + + SwTableBoxes* pTBoxes = &pBox->GetUpper()->GetTabBoxes(); + pTBoxes->erase( std::find( pTBoxes->begin(), pTBoxes->end(), pBox ) ); + + pBox->SetUpper( &rParent ); + pTBoxes = &rParent.GetTabBoxes(); + pTBoxes->push_back( pBox ); + } + } + + if( pNext ) + pNext->CreateNew( rTable, rParent, rSTable ); +} + +// UndoObject for attribute changes on table +SwUndoAttrTable::SwUndoAttrTable( const SwTableNode& rTableNd, bool bClearTabCols ) + : SwUndo( SwUndoId::TABLE_ATTR, rTableNd.GetDoc() ), + m_nStartNode( rTableNd.GetIndex() ) +{ + m_bClearTableCol = bClearTabCols; + m_pSaveTable.reset( new SaveTable( rTableNd.GetTable() ) ); +} + +SwUndoAttrTable::~SwUndoAttrTable() +{ +} + +void SwUndoAttrTable::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwTableNode* pTableNd = rDoc.GetNodes()[ m_nStartNode ]->GetTableNode(); + OSL_ENSURE( pTableNd, "no TableNode" ); + + if (pTableNd) + { + SaveTable* pOrig = new SaveTable( pTableNd->GetTable() ); + m_pSaveTable->RestoreAttr( pTableNd->GetTable() ); + m_pSaveTable.reset( pOrig ); + } + + if( m_bClearTableCol ) + { + ClearFEShellTabCols(rDoc, nullptr); + } +} + +void SwUndoAttrTable::RedoImpl(::sw::UndoRedoContext & rContext) +{ + UndoImpl(rContext); +} + +// UndoObject for AutoFormat on Table +SwUndoTableAutoFormat::SwUndoTableAutoFormat( const SwTableNode& rTableNd, + const SwTableAutoFormat& rAFormat ) + : SwUndo( SwUndoId::TABLE_AUTOFMT, rTableNd.GetDoc() ) + , m_TableStyleName(rTableNd.GetTable().GetTableStyleName()) + , m_nStartNode( rTableNd.GetIndex() ) + , m_bSaveContentAttr( false ) + , m_nRepeatHeading(rTableNd.GetTable().GetRowsToRepeat()) +{ + m_pSaveTable.reset( new SaveTable( rTableNd.GetTable() ) ); + + if( rAFormat.IsFont() || rAFormat.IsJustify() ) + { + // then also go over the ContentNodes of the EndBoxes and collect + // all paragraph attributes + m_pSaveTable->SaveContentAttrs( const_cast<SwDoc*>(rTableNd.GetDoc()) ); + m_bSaveContentAttr = true; + } +} + +SwUndoTableAutoFormat::~SwUndoTableAutoFormat() +{ +} + +void SwUndoTableAutoFormat::SaveBoxContent( const SwTableBox& rBox ) +{ + m_Undos.push_back(std::make_shared<SwUndoTableNumFormat>(rBox)); +} + +void +SwUndoTableAutoFormat::UndoRedo(bool const bUndo, ::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwTableNode* pTableNd = rDoc.GetNodes()[ m_nStartNode ]->GetTableNode(); + OSL_ENSURE( pTableNd, "no TableNode" ); + + SwTable& table = pTableNd->GetTable(); + if (table.GetTableStyleName() != m_TableStyleName) + { + OUString const temp(table.GetTableStyleName()); + table.SetTableStyleName(m_TableStyleName); + m_TableStyleName = temp; + } + SaveTable* pOrig = new SaveTable( table ); + // then go also over the ContentNodes of the EndBoxes and collect + // all paragraph attributes + if( m_bSaveContentAttr ) + pOrig->SaveContentAttrs( &rDoc ); + + if (bUndo) + { + for (size_t n = m_Undos.size(); 0 < n; --n) + { + m_Undos.at(n-1)->UndoImpl(rContext); + } + + table.SetRowsToRepeat(m_nRepeatHeading); + } + + m_pSaveTable->RestoreAttr( pTableNd->GetTable(), !bUndo ); + m_pSaveTable.reset( pOrig ); +} + +void SwUndoTableAutoFormat::UndoImpl(::sw::UndoRedoContext & rContext) +{ + UndoRedo(true, rContext); +} + +void SwUndoTableAutoFormat::RedoImpl(::sw::UndoRedoContext & rContext) +{ + UndoRedo(false, rContext); +} + +SwUndoTableNdsChg::SwUndoTableNdsChg( SwUndoId nAction, + const SwSelBoxes& rBoxes, + const SwTableNode& rTableNd, + long nMn, long nMx, + sal_uInt16 nCnt, bool bFlg, bool bSmHght ) + : SwUndo( nAction, rTableNd.GetDoc() ), + m_nMin( nMn ), m_nMax( nMx ), + m_nSttNode( rTableNd.GetIndex() ), + m_nCount( nCnt ), + m_bFlag( bFlg ), + m_bSameHeight( bSmHght ) +{ + const SwTable& rTable = rTableNd.GetTable(); + m_pSaveTable.reset( new SaveTable( rTable ) ); + + // and remember selection + ReNewBoxes( rBoxes ); +} + +void SwUndoTableNdsChg::ReNewBoxes( const SwSelBoxes& rBoxes ) +{ + if (rBoxes.size() != m_Boxes.size()) + { + m_Boxes.clear(); + for (size_t n = 0; n < rBoxes.size(); ++n) + { + m_Boxes.insert( rBoxes[n]->GetSttIdx() ); + } + } +} + +SwUndoTableNdsChg::~SwUndoTableNdsChg() +{ +} + +void SwUndoTableNdsChg::SaveNewBoxes( const SwTableNode& rTableNd, + const SwTableSortBoxes& rOld ) +{ + const SwTable& rTable = rTableNd.GetTable(); + const SwTableSortBoxes& rTableBoxes = rTable.GetTabSortBoxes(); + + OSL_ENSURE( ! IsDelBox(), "wrong Action" ); + m_pNewSttNds.reset( new std::set<BoxMove> ); + + size_t i = 0; + for (size_t n = 0; n < rOld.size(); ++i) + { + if( rOld[ n ] == rTableBoxes[ i ] ) + ++n; + else + // new box: insert sorted + m_pNewSttNds->insert( BoxMove(rTableBoxes[ i ]->GetSttIdx()) ); + } + + for( ; i < rTableBoxes.size(); ++i ) + // new box: insert sorted + m_pNewSttNds->insert( BoxMove(rTableBoxes[ i ]->GetSttIdx()) ); +} + +static SwTableLine* lcl_FindTableLine( const SwTable& rTable, + const SwTableBox& rBox ) +{ + SwTableLine* pRet = nullptr; + // i63949: For nested cells we have to take nLineNo - 1, too, not 0! + const SwTableLines &rTableLines = ( rBox.GetUpper()->GetUpper() != nullptr ) ? + rBox.GetUpper()->GetUpper()->GetTabLines() + : rTable.GetTabLines(); + const SwTableLine* pLine = rBox.GetUpper(); + sal_uInt16 nLineNo = rTableLines.GetPos( pLine ); + pRet = rTableLines[nLineNo - 1]; + + return pRet; +} + +static const SwTableLines& lcl_FindParentLines( const SwTable& rTable, + const SwTableBox& rBox ) +{ + const SwTableLines& rRet = + ( rBox.GetUpper()->GetUpper() != nullptr ) ? + rBox.GetUpper()->GetUpper()->GetTabLines() : + rTable.GetTabLines(); + + return rRet; +} + +void SwUndoTableNdsChg::SaveNewBoxes( const SwTableNode& rTableNd, + const SwTableSortBoxes& rOld, + const SwSelBoxes& rBoxes, + const std::vector<sal_uLong> &rNodeCnts ) +{ + const SwTable& rTable = rTableNd.GetTable(); + const SwTableSortBoxes& rTableBoxes = rTable.GetTabSortBoxes(); + + OSL_ENSURE( ! IsDelBox(), "wrong Action" ); + m_pNewSttNds.reset( new std::set<BoxMove> ); + + OSL_ENSURE( rTable.IsNewModel() || rOld.size() + m_nCount * rBoxes.size() == rTableBoxes.size(), + "unexpected boxes" ); + OSL_ENSURE( rOld.size() <= rTableBoxes.size(), "more unexpected boxes" ); + for (size_t n = 0, i = 0; i < rTableBoxes.size(); ++i) + { + if( ( n < rOld.size() ) && + ( rOld[ n ] == rTableBoxes[ i ] ) ) + { + // box already known? Then nothing to be done. + ++n; + } + else + { + // new box found: insert (obey sort order) + const SwTableBox* pBox = rTableBoxes[ i ]; + + // find the source box. It must be one in rBoxes. + // We found the right one if it's in the same column as pBox. + // No, if more than one selected cell in the same column has been split, + // we have to look for the nearest one (i65201)! + const SwTableBox* pSourceBox = nullptr; + const SwTableBox* pCheckBox = nullptr; + const SwTableLine* pBoxLine = pBox->GetUpper(); + sal_uInt16 nLineDiff = lcl_FindParentLines(rTable,*pBox).GetPos(pBoxLine); + sal_uInt16 nLineNo = 0; + for (size_t j = 0; j < rBoxes.size(); ++j) + { + pCheckBox = rBoxes[j]; + if( pCheckBox->GetUpper()->GetUpper() == pBox->GetUpper()->GetUpper() ) + { + const SwTableLine* pCheckLine = pCheckBox->GetUpper(); + sal_uInt16 nCheckLine = lcl_FindParentLines( rTable, *pCheckBox ). + GetPos( pCheckLine ); + if( ( !pSourceBox || nCheckLine > nLineNo ) && nCheckLine < nLineDiff ) + { + nLineNo = nCheckLine; + pSourceBox = pCheckBox; + } + } + } + + // find the line number difference + // (to help determine bNodesMoved flag below) + nLineDiff = nLineDiff - nLineNo; + OSL_ENSURE( pSourceBox, "Split source box not found!" ); + // find out how many nodes the source box used to have + // (to help determine bNodesMoved flag below) + size_t nNdsPos = 0; + while( rBoxes[ nNdsPos ] != pSourceBox ) + ++nNdsPos; + sal_uLong nNodes = rNodeCnts[ nNdsPos ]; + + // When a new table cell is created, it either gets a new + // node, or it gets node(s) from elsewhere. The undo must + // know, of course, and thus we must determine here just + // where pBox's nodes are from: + // If 1) the source box has lost nodes, and + // 2) we're in the node range that got nodes + // then pBox received nodes from elsewhere. + // If bNodesMoved is set for pBox the undo must move the + // boxes back, otherwise it must delete them. + bool bNodesMoved = pSourceBox && + ( nNodes != ( pSourceBox->GetSttNd()->EndOfSectionIndex() - + pSourceBox->GetSttIdx() ) ) + && ( nNodes - 1 > nLineDiff ); + m_pNewSttNds->insert( BoxMove(pBox->GetSttIdx(), bNodesMoved) ); + } + } +} + +void SwUndoTableNdsChg::SaveSection( SwStartNode* pSttNd ) +{ + OSL_ENSURE( IsDelBox(), "wrong Action" ); + if (m_pDelSects == nullptr) + m_pDelSects.reset(new SwUndoSaveSections); + + SwTableNode* pTableNd = pSttNd->FindTableNode(); + std::unique_ptr<SwUndoSaveSection, o3tl::default_delete<SwUndoSaveSection>> pSave(new SwUndoSaveSection); + pSave->SaveSection( SwNodeIndex( *pSttNd )); + + m_pDelSects->push_back(std::move(pSave)); + m_nSttNode = pTableNd->GetIndex(); +} + +void SwUndoTableNdsChg::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode ); + + SwTableNode *const pTableNd = aIdx.GetNode().GetTableNode(); + OSL_ENSURE( pTableNd, "no TableNode" ); + + SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() ); + aMsgHint.m_eFlags = TBL_BOXPTR; + rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint ); + + CHECK_TABLE( pTableNd->GetTable() ) + + FndBox_ aTmpBox( nullptr, nullptr ); + // ? TL_CHART2: notification or locking of controller required ? + + SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider(); + SwSelBoxes aDelBoxes; + std::vector< std::pair<SwTableBox *, sal_uLong> > aDelNodes; + if( IsDelBox() ) + { + // Trick: add missing boxes in any line, they will be connected + // correctly when calling CreateNew + SwTableBox* pCpyBox = pTableNd->GetTable().GetTabSortBoxes()[0]; + SwTableBoxes& rLnBoxes = pCpyBox->GetUpper()->GetTabBoxes(); + + // restore sections + for (size_t n = m_pDelSects->size(); n; ) + { + SwUndoSaveSection *const pSave = (*m_pDelSects)[ --n ].get(); + pSave->RestoreSection( &rDoc, &aIdx, SwTableBoxStartNode ); + if( pSave->GetHistory() ) + pSave->GetHistory()->Rollback( &rDoc ); + SwTableBox* pBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pCpyBox->GetFrameFormat()), aIdx, + pCpyBox->GetUpper() ); + rLnBoxes.push_back( pBox ); + } + m_pDelSects->clear(); + } + else if( !m_pNewSttNds->empty() ) + { + // Then the nodes have be moved and not deleted! + // But for that we need a temp array. + std::vector<BoxMove> aTmp( m_pNewSttNds->begin(), m_pNewSttNds->end() ); + + // backwards + for (size_t n = aTmp.size(); n > 0 ; ) + { + --n; + // delete box from table structure + sal_uLong nIdx = aTmp[n].index; + SwTableBox* pBox = pTableNd->GetTable().GetTableBox( nIdx ); + OSL_ENSURE( pBox, "Where is my TableBox?" ); + + // TL_CHART2: notify chart about box to be removed + if (pPCD) + pPCD->DeleteBox( &pTableNd->GetTable(), *pBox ); + + // insert _before_ deleting the section - otherwise the box + // has no start node so all boxes sort equal in SwSelBoxes + aDelBoxes.insert(pBox); + + if( aTmp[n].hasMoved ) + { + SwNodeRange aRg( *pBox->GetSttNd(), 1, + *pBox->GetSttNd()->EndOfSectionNode() ); + + SwTableLine* pLine = lcl_FindTableLine( pTableNd->GetTable(), *pBox ); + SwNodeIndex aInsPos( *(pLine->GetTabBoxes()[0]->GetSttNd()), 2 ); + + // adjust all StartNode indices + size_t i = n; + sal_uLong nSttIdx = aInsPos.GetIndex() - 2, + nNdCnt = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex(); + while( i && aTmp[ --i ].index > nSttIdx ) + aTmp[ i ].index += nNdCnt; + + // first delete box + delete pBox; + // than move nodes + rDoc.GetNodes().MoveNodes( aRg, rDoc.GetNodes(), aInsPos, false ); + } + else + { + aDelNodes.emplace_back(pBox, nIdx); + } + } + } + else + { + // Remove nodes from nodes array (backwards!) + std::set<BoxMove>::reverse_iterator it; + for( it = m_pNewSttNds->rbegin(); it != m_pNewSttNds->rend(); ++it ) + { + sal_uLong nIdx = (*it).index; + SwTableBox* pBox = pTableNd->GetTable().GetTableBox( nIdx ); + OSL_ENSURE( pBox, "Where's my table box?" ); + // TL_CHART2: notify chart about box to be removed + if (pPCD) + pPCD->DeleteBox( &pTableNd->GetTable(), *pBox ); + aDelBoxes.insert(pBox); + aDelNodes.emplace_back(pBox, nIdx); + } + } + + // fdo#57197: before deleting the SwTableBoxes, delete the SwTabFrames + aTmpBox.SetTableLines(aDelBoxes, pTableNd->GetTable()); + aTmpBox.DelFrames(pTableNd->GetTable()); + + // do this _after_ deleting Frames because disposing SwAccessible requires + // connection to the nodes, see SwAccessibleChild::IsAccessible() + for (const std::pair<SwTableBox *, sal_uLong> & rDelNode : aDelNodes) + { + // first disconnect box from node, otherwise ~SwTableBox would + // access pBox->pSttNd, deleted by DeleteSection + rDelNode.first->RemoveFromTable(); + rDoc.getIDocumentContentOperations().DeleteSection(rDoc.GetNodes()[ rDelNode.second ]); + } + + // Remove boxes from table structure + for( size_t n = 0; n < aDelBoxes.size(); ++n ) + { + SwTableBox* pCurrBox = aDelBoxes[n]; + SwTableBoxes* pTBoxes = &pCurrBox->GetUpper()->GetTabBoxes(); + pTBoxes->erase( std::find( pTBoxes->begin(), pTBoxes->end(), pCurrBox ) ); + delete pCurrBox; + } + + m_pSaveTable->CreateNew( pTableNd->GetTable(), true, false ); + + // TL_CHART2: need to inform chart of probably changed cell names + rDoc.UpdateCharts( pTableNd->GetTable().GetFrameFormat()->GetName() ); + + if( IsDelBox() ) + m_nSttNode = pTableNd->GetIndex(); + ClearFEShellTabCols(rDoc, nullptr); + CHECK_TABLE( pTableNd->GetTable() ) +} + +void SwUndoTableNdsChg::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + + SwTableNode* pTableNd = rDoc.GetNodes()[ m_nSttNode ]->GetTableNode(); + OSL_ENSURE( pTableNd, "no TableNode" ); + CHECK_TABLE( pTableNd->GetTable() ) + + SwSelBoxes aSelBoxes; + for (const auto& rBox : m_Boxes) + { + SwTableBox* pBox = pTableNd->GetTable().GetTableBox( rBox ); + aSelBoxes.insert( pBox ); + } + + // create SelBoxes and call InsertCell/-Row/SplitTable + switch( GetId() ) + { + case SwUndoId::TABLE_INSCOL: + rDoc.InsertCol( aSelBoxes, m_nCount, m_bFlag ); + break; + + case SwUndoId::TABLE_INSROW: + rDoc.InsertRow( aSelBoxes, m_nCount, m_bFlag ); + break; + + case SwUndoId::TABLE_SPLIT: + rDoc.SplitTable( aSelBoxes, m_bFlag, m_nCount, m_bSameHeight ); + break; + case SwUndoId::TABLE_DELBOX: + case SwUndoId::ROW_DELETE: + case SwUndoId::COL_DELETE: + { + SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() ); + aMsgHint.m_eFlags = TBL_BOXPTR; + rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint ); + SwTable &rTable = pTableNd->GetTable(); + if( m_nMax > m_nMin && rTable.IsNewModel() ) + rTable.PrepareDeleteCol( m_nMin, m_nMax ); + rTable.DeleteSel( &rDoc, aSelBoxes, nullptr, this, true, true ); + m_nSttNode = pTableNd->GetIndex(); + } + break; + default: + ; + } + ClearFEShellTabCols(rDoc, nullptr); + CHECK_TABLE( pTableNd->GetTable() ) +} + +SwUndoTableMerge::SwUndoTableMerge( const SwPaM& rTableSel ) + : SwUndo( SwUndoId::TABLE_MERGE, rTableSel.GetDoc() ), SwUndRng( rTableSel ) +{ + const SwTableNode* pTableNd = rTableSel.GetNode().FindTableNode(); + OSL_ENSURE( pTableNd, "Where is the TableNode?" ); + m_pSaveTable.reset( new SaveTable( pTableNd->GetTable() ) ); + m_nTableNode = pTableNd->GetIndex(); +} + +SwUndoTableMerge::~SwUndoTableMerge() +{ + m_pSaveTable.reset(); + m_vMoves.clear(); + m_pHistory.reset(); +} + +void SwUndoTableMerge::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwNodeIndex aIdx( rDoc.GetNodes(), m_nTableNode ); + + SwTableNode *const pTableNd = aIdx.GetNode().GetTableNode(); + OSL_ENSURE( pTableNd, "no TableNode" ); + + SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() ); + aMsgHint.m_eFlags = TBL_BOXPTR; + rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint ); + + // ? TL_CHART2: notification or locking of controller required ? + + // 1. restore deleted boxes: + // Trick: add missing boxes in any line, they will be connected + // correctly when calling CreateNew + SwTableBox *pBox, *pCpyBox = pTableNd->GetTable().GetTabSortBoxes()[0]; + SwTableBoxes& rLnBoxes = pCpyBox->GetUpper()->GetTabBoxes(); + + CHECKTABLE(pTableNd->GetTable()) + + SwSelBoxes aSelBoxes; + SwTextFormatColl* pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ); + + for (const auto& rBox : m_Boxes) + { + aIdx = rBox; + SwStartNode* pSttNd = rDoc.GetNodes().MakeTextSection( aIdx, + SwTableBoxStartNode, pColl ); + pBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pCpyBox->GetFrameFormat()), *pSttNd, + pCpyBox->GetUpper() ); + rLnBoxes.push_back( pBox ); + + aSelBoxes.insert( pBox ); + } + + CHECKTABLE(pTableNd->GetTable()) + + SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider(); + // 2. deleted the inserted boxes + // delete nodes (from last to first) + for( size_t n = m_aNewStartNodes.size(); n; ) + { + // remove box from table structure + sal_uLong nIdx = m_aNewStartNodes[ --n ]; + + if( !nIdx && n ) + { + nIdx = m_aNewStartNodes[ --n ]; + pBox = pTableNd->GetTable().GetTableBox( nIdx ); + OSL_ENSURE( pBox, "Where is my TableBox?" ); + + if( !m_pSaveTable->IsNewModel() ) + rDoc.GetNodes().MakeTextNode( SwNodeIndex( + *pBox->GetSttNd()->EndOfSectionNode() ), pColl ); + + // this was the separator -> restore moved ones + for (size_t i = m_vMoves.size(); i; ) + { + SwTextNode* pTextNd = nullptr; + sal_Int32 nDelPos = 0; + SwUndoMove *const pUndo = m_vMoves[ --i ].get(); + if( !pUndo->IsMoveRange() ) + { + pTextNd = rDoc.GetNodes()[ pUndo->GetDestSttNode() ]->GetTextNode(); + nDelPos = pUndo->GetDestSttContent() - 1; + } + pUndo->UndoImpl(rContext); + if( pUndo->IsMoveRange() ) + { + // delete the unnecessary node + aIdx = pUndo->GetEndNode(); + SwContentNode *pCNd = aIdx.GetNode().GetContentNode(); + if( pCNd ) + { + SwNodeIndex aTmp( aIdx, -1 ); + SwContentNode *pMove = aTmp.GetNode().GetContentNode(); + if( pMove ) + pCNd->MoveTo( *pMove ); + } + rDoc.GetNodes().Delete( aIdx ); + } + else if( pTextNd ) + { + // also delete not needed attributes + SwIndex aTmpIdx( pTextNd, nDelPos ); + if( pTextNd->GetpSwpHints() && pTextNd->GetpSwpHints()->Count() ) + pTextNd->RstTextAttr( aTmpIdx, pTextNd->GetText().getLength() - nDelPos + 1 ); + // delete separator + pTextNd->EraseText( aTmpIdx, 1 ); + } + } + nIdx = pBox->GetSttIdx(); + } + else + pBox = pTableNd->GetTable().GetTableBox( nIdx ); + + if( !m_pSaveTable->IsNewModel() ) + { + // TL_CHART2: notify chart about box to be removed + if (pPCD) + pPCD->DeleteBox( &pTableNd->GetTable(), *pBox ); + + SwTableBoxes* pTBoxes = &pBox->GetUpper()->GetTabBoxes(); + pTBoxes->erase( std::find(pTBoxes->begin(), pTBoxes->end(), pBox ) ); + + // delete indices from section + { + SwNodeIndex aTmpIdx( *pBox->GetSttNd() ); + SwDoc::CorrAbs( SwNodeIndex( aTmpIdx, 1 ), + SwNodeIndex( *aTmpIdx.GetNode().EndOfSectionNode() ), + SwPosition( aTmpIdx, SwIndex( nullptr, 0 )), true ); + } + + delete pBox; + rDoc.getIDocumentContentOperations().DeleteSection( rDoc.GetNodes()[ nIdx ] ); + } + } + CHECKTABLE(pTableNd->GetTable()) + + m_pSaveTable->CreateNew( pTableNd->GetTable(), true, false ); + + // TL_CHART2: need to inform chart of probably changed cell names + rDoc.UpdateCharts( pTableNd->GetTable().GetFrameFormat()->GetName() ); + + if( m_pHistory ) + { + m_pHistory->TmpRollback( &rDoc, 0 ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + } + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + pPam->DeleteMark(); + pPam->GetPoint()->nNode = m_nSttNode; + pPam->GetPoint()->nContent.Assign( pPam->GetContentNode(), m_nSttContent ); + pPam->SetMark(); + pPam->DeleteMark(); + + CHECKTABLE(pTableNd->GetTable()) + ClearFEShellTabCols(rDoc, nullptr); +} + +void SwUndoTableMerge::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + rDoc.MergeTable(rPam); +} + +void SwUndoTableMerge::MoveBoxContent( SwDoc* pDoc, SwNodeRange& rRg, SwNodeIndex& rPos ) +{ + SwNodeIndex aTmp( rRg.aStart, -1 ), aTmp2( rPos, -1 ); + std::unique_ptr<SwUndoMove> pUndo(new SwUndoMove( pDoc, rRg, rPos )); + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + pDoc->getIDocumentContentOperations().MoveNodeRange( rRg, rPos, m_pSaveTable->IsNewModel() ? + SwMoveFlags::NO_DELFRMS : + SwMoveFlags::DEFAULT ); + ++aTmp; + ++aTmp2; + pUndo->SetDestRange( aTmp2, rPos, aTmp ); + + m_vMoves.push_back(std::move(pUndo)); +} + +void SwUndoTableMerge::SetSelBoxes( const SwSelBoxes& rBoxes ) +{ + // memorize selection + for (size_t n = 0; n < rBoxes.size(); ++n) + { + m_Boxes.insert(rBoxes[n]->GetSttIdx()); + } + + // as separator for inserts of new boxes after shifting + m_aNewStartNodes.push_back( sal_uLong(0) ); + + // The new table model does not delete overlapped cells (by row span), + // so the rBoxes array might be empty even some cells have been merged. + if( !rBoxes.empty() ) + m_nTableNode = rBoxes[ 0 ]->GetSttNd()->FindTableNode()->GetIndex(); +} + +void SwUndoTableMerge::SaveCollection( const SwTableBox& rBox ) +{ + if( !m_pHistory ) + m_pHistory.reset(new SwHistory); + + SwNodeIndex aIdx( *rBox.GetSttNd(), 1 ); + SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + pCNd = aIdx.GetNodes().GoNext( &aIdx ); + + m_pHistory->Add( pCNd->GetFormatColl(), aIdx.GetIndex(), pCNd->GetNodeType()); + if( pCNd->HasSwAttrSet() ) + m_pHistory->CopyFormatAttr( *pCNd->GetpSwAttrSet(), aIdx.GetIndex() ); +} + +SwUndoTableNumFormat::SwUndoTableNumFormat( const SwTableBox& rBox, + const SfxItemSet* pNewSet ) + : SwUndo(SwUndoId::TBLNUMFMT, rBox.GetFrameFormat()->GetDoc()) + , m_nFormatIdx(getSwDefaultTextFormat()) + , m_nNewFormatIdx(0) + , m_fNum(0.0) + , m_fNewNum(0.0) + , m_bNewFormat(false) + , m_bNewFormula(false) + , m_bNewValue(false) +{ + m_nNode = rBox.GetSttIdx(); + + m_nNodePos = rBox.IsValidNumTextNd( nullptr == pNewSet ); + SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc(); + + if( ULONG_MAX != m_nNodePos ) + { + SwTextNode* pTNd = pDoc->GetNodes()[ m_nNodePos ]->GetTextNode(); + + m_pHistory.reset(new SwHistory); + SwRegHistory aRHst( *rBox.GetSttNd(), m_pHistory.get() ); + // always save all text atttibutes because of possibly overlapping + // areas of on/off + m_pHistory->CopyAttr( pTNd->GetpSwpHints(), m_nNodePos, 0, + pTNd->GetText().getLength(), true ); + + if( pTNd->HasSwAttrSet() ) + m_pHistory->CopyFormatAttr( *pTNd->GetpSwAttrSet(), m_nNodePos ); + + m_aStr = pTNd->GetText(); + if( pTNd->GetpSwpHints() ) + pTNd->GetpSwpHints()->DeRegister(); + } + + m_pBoxSet.reset( new SfxItemSet( pDoc->GetAttrPool(), aTableBoxSetRange ) ); + m_pBoxSet->Put( rBox.GetFrameFormat()->GetAttrSet() ); + + if( pNewSet ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == pNewSet->GetItemState( RES_BOXATR_FORMAT, + false, &pItem )) + { + m_bNewFormat = true; + m_nNewFormatIdx = static_cast<const SwTableBoxNumFormat*>(pItem)->GetValue(); + } + if( SfxItemState::SET == pNewSet->GetItemState( RES_BOXATR_FORMULA, + false, &pItem )) + { + m_bNewFormula = true; + m_aNewFormula = static_cast<const SwTableBoxFormula*>(pItem)->GetFormula(); + } + if( SfxItemState::SET == pNewSet->GetItemState( RES_BOXATR_VALUE, + false, &pItem )) + { + m_bNewValue = true; + m_fNewNum = static_cast<const SwTableBoxValue*>(pItem)->GetValue(); + } + } + + // is a history needed at all? + if (m_pHistory && !m_pHistory->Count()) + { + m_pHistory.reset(); + } +} + +SwUndoTableNumFormat::~SwUndoTableNumFormat() +{ + m_pHistory.reset(); + m_pBoxSet.reset(); +} + +void SwUndoTableNumFormat::UndoImpl(::sw::UndoRedoContext & rContext) +{ + OSL_ENSURE( m_pBoxSet, "Where's the stored item set?" ); + + SwDoc & rDoc = rContext.GetDoc(); + SwStartNode* pSttNd = rDoc.GetNodes()[ m_nNode ]-> + FindSttNodeByType( SwTableBoxStartNode ); + OSL_ENSURE( pSttNd, "without StartNode no TableBox" ); + SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().GetTableBox( + pSttNd->GetIndex() ); + OSL_ENSURE( pBox, "found no TableBox" ); + + SwTableBoxFormat* pFormat = rDoc.MakeTableBoxFormat(); + pFormat->SetFormatAttr( *m_pBoxSet ); + pBox->ChgFrameFormat( pFormat ); + + if( ULONG_MAX == m_nNodePos ) + return; + + SwTextNode* pTextNd = rDoc.GetNodes()[ m_nNodePos ]->GetTextNode(); + // If more than one node was deleted then all "node" attributes were also + // saved + if( pTextNd->HasSwAttrSet() ) + pTextNd->ResetAllAttr(); + + if( pTextNd->GetpSwpHints() && !m_aStr.isEmpty() ) + pTextNd->ClearSwpHintsArr( true ); + + // ChgTextToNum(..) only acts when the strings are different. We need to do + // the same here. + if( pTextNd->GetText() != m_aStr ) + { + rDoc.getIDocumentRedlineAccess().DeleteRedline( *( pBox->GetSttNd() ), false, RedlineType::Any ); + + SwIndex aIdx( pTextNd, 0 ); + if( !m_aStr.isEmpty() ) + { + pTextNd->EraseText( aIdx ); + pTextNd->InsertText( m_aStr, aIdx, + SwInsertFlags::NOHINTEXPAND ); + } + } + + if( m_pHistory ) + { + sal_uInt16 nTmpEnd = m_pHistory->GetTmpEnd(); + m_pHistory->TmpRollback( &rDoc, 0 ); + m_pHistory->SetTmpEnd( nTmpEnd ); + } + + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + pPam->DeleteMark(); + pPam->GetPoint()->nNode = m_nNode + 1; + pPam->GetPoint()->nContent.Assign( pTextNd, 0 ); +} + +namespace { + +/** switch the RedlineFlags on the given document, using + * SetRedlineFlags_intern. This class set the mode in the constructor, + * and changes it back in the destructor, i.e. it uses the + * initialization-is-resource-acquisition idiom. + */ +class RedlineFlagsInternGuard +{ + SwDoc& mrDoc; + RedlineFlags meOldRedlineFlags; + +public: + RedlineFlagsInternGuard( + SwDoc& rDoc, // change mode of this document + RedlineFlags eNewRedlineFlags, // new redline mode + RedlineFlags eRedlineFlagsMask /*change only bits set in this mask*/); + + ~RedlineFlagsInternGuard(); +}; + +} + +RedlineFlagsInternGuard::RedlineFlagsInternGuard( + SwDoc& rDoc, + RedlineFlags eNewRedlineFlags, + RedlineFlags eRedlineFlagsMask ) + : mrDoc( rDoc ), + meOldRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() ) +{ + mrDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( meOldRedlineFlags & ~eRedlineFlagsMask ) | + ( eNewRedlineFlags & eRedlineFlagsMask ) ); +} + +RedlineFlagsInternGuard::~RedlineFlagsInternGuard() +{ + mrDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( meOldRedlineFlags ); +} + +void SwUndoTableNumFormat::RedoImpl(::sw::UndoRedoContext & rContext) +{ + // Could the box be changed? + if( !m_pBoxSet ) + return ; + + SwDoc & rDoc = rContext.GetDoc(); + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + pPam->DeleteMark(); + pPam->GetPoint()->nNode = m_nNode; + + SwNode * pNd = & pPam->GetPoint()->nNode.GetNode(); + SwStartNode* pSttNd = pNd->FindSttNodeByType( SwTableBoxStartNode ); + assert(pSttNd && "without StartNode no TableBox"); + SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().GetTableBox( + pSttNd->GetIndex() ); + OSL_ENSURE( pBox, "found no TableBox" ); + + SwFrameFormat* pBoxFormat = pBox->ClaimFrameFormat(); + if( m_bNewFormat || m_bNewFormula || m_bNewValue ) + { + SfxItemSet aBoxSet( rDoc.GetAttrPool(), + svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} ); + + // Resetting attributes is not enough. In addition, take care that the + // text will be also formatted correctly. + pBoxFormat->LockModify(); + + if( m_bNewFormula ) + aBoxSet.Put( SwTableBoxFormula( m_aNewFormula )); + else + pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA ); + if( m_bNewFormat ) + aBoxSet.Put( SwTableBoxNumFormat( m_nNewFormatIdx )); + else + pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT ); + if( m_bNewValue ) + aBoxSet.Put( SwTableBoxValue( m_fNewNum )); + else + pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE ); + pBoxFormat->UnlockModify(); + + // dvo: When redlining is (was) enabled, setting the attribute + // will also change the cell content. To allow this, the + // RedlineFlags::Ignore flag must be removed during Redo. #108450# + RedlineFlagsInternGuard aGuard( rDoc, RedlineFlags::NONE, RedlineFlags::Ignore ); + pBoxFormat->SetFormatAttr( aBoxSet ); + } + else if( getSwDefaultTextFormat() != m_nFormatIdx ) + { + SfxItemSet aBoxSet( rDoc.GetAttrPool(), + svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} ); + + aBoxSet.Put( SwTableBoxNumFormat( m_nFormatIdx )); + aBoxSet.Put( SwTableBoxValue( m_fNum )); + + // Resetting attributes is not enough. In addition, take care that the + // text will be also formatted correctly. + pBoxFormat->LockModify(); + pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA ); + pBoxFormat->UnlockModify(); + + // dvo: When redlining is (was) enabled, setting the attribute + // will also change the cell content. To allow this, the + // RedlineFlags::Ignore flag must be removed during Redo. #108450# + RedlineFlagsInternGuard aGuard( rDoc, RedlineFlags::NONE, RedlineFlags::Ignore ); + pBoxFormat->SetFormatAttr( aBoxSet ); + } + else + { + // it's no number + + // Resetting attributes is not enough. In addition, take care that the + // text will be also formatted correctly. + pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT )); + + pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ); + } + + if( m_bNewFormula ) + { + // No matter what was set, an update of the table is always a good idea + SwTableFormulaUpdate aTableUpdate( &pSttNd->FindTableNode()->GetTable() ); + rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate ); + } + + if( !pNd->IsContentNode() ) + pNd = rDoc.GetNodes().GoNext( &pPam->GetPoint()->nNode ); + pPam->GetPoint()->nContent.Assign( static_cast<SwContentNode*>(pNd), 0 ); +} + +void SwUndoTableNumFormat::SetBox( const SwTableBox& rBox ) +{ + m_nNode = rBox.GetSttIdx(); +} + +UndoTableCpyTable_Entry::UndoTableCpyTable_Entry( const SwTableBox& rBox ) + : nBoxIdx( rBox.GetSttIdx() ), nOffset( 0 ), + bJoin( false ) +{ +} + +SwUndoTableCpyTable::SwUndoTableCpyTable(const SwDoc* pDoc) + : SwUndo( SwUndoId::TBLCPYTBL, pDoc ) +{ +} + +SwUndoTableCpyTable::~SwUndoTableCpyTable() +{ + m_vArr.clear(); + m_pInsRowUndo.reset(); +} + +void SwUndoTableCpyTable::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + DEBUG_REDLINE( &rDoc ) + + SwTableNode* pTableNd = nullptr; + for (size_t n = m_vArr.size(); n; ) + { + UndoTableCpyTable_Entry *const pEntry = m_vArr[ --n ].get(); + sal_uLong nSttPos = pEntry->nBoxIdx + pEntry->nOffset; + SwStartNode* pSNd = rDoc.GetNodes()[ nSttPos ]->StartOfSectionNode(); + if( !pTableNd ) + pTableNd = pSNd->FindTableNode(); + + SwTableBox& rBox = *pTableNd->GetTable().GetTableBox( nSttPos ); + + SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 ); + rDoc.GetNodes().MakeTextNode( aInsIdx, rDoc.GetDfltTextFormatColl() ); + + // b62341295: Redline for copying tables + const SwNode *pEndNode = rBox.GetSttNd()->EndOfSectionNode(); + SwPaM aPam( aInsIdx.GetNode(), *pEndNode ); + std::unique_ptr<SwUndoDelete> pUndo; + + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ) + { + bool bDeleteCompleteParagraph = false; + bool bShiftPam = false; + // There are a couple of different situations to consider during redlining + if( pEntry->pUndo ) + { + SwUndoDelete *const pUndoDelete = + dynamic_cast<SwUndoDelete*>(pEntry->pUndo.get()); + SwUndoRedlineDelete *const pUndoRedlineDelete = + dynamic_cast<SwUndoRedlineDelete*>(pEntry->pUndo.get()); + assert(pUndoDelete || pUndoRedlineDelete); + if (pUndoRedlineDelete) + { + // The old content was not empty or he has been merged with the new content + bDeleteCompleteParagraph = !pEntry->bJoin; // bJoin is set when merged + // Set aTmpIdx to the beginning of the old content + SwNodeIndex aTmpIdx( *pEndNode, + pUndoRedlineDelete->NodeDiff()-1 ); + SwTextNode *pText = aTmpIdx.GetNode().GetTextNode(); + if( pText ) + { + aPam.GetPoint()->nNode = *pText; + aPam.GetPoint()->nContent.Assign( pText, + pUndoRedlineDelete->ContentStart() ); + } + else + *aPam.GetPoint() = SwPosition( aTmpIdx ); + } + else if (pUndoDelete && pUndoDelete->IsDelFullPara()) + { + // When the old content was an empty paragraph, but could not be joined + // with the new content (e.g. because of a section or table) + // We "save" the aPam.Point, we go one step backwards (because later on the + // empty paragraph will be inserted by the undo) and set the "ShiftPam-flag + // for step forward later on. + bDeleteCompleteParagraph = true; + bShiftPam = true; + SwNodeIndex aTmpIdx( *pEndNode, -1 ); + SwTextNode *pText = aTmpIdx.GetNode().GetTextNode(); + if( pText ) + { + aPam.GetPoint()->nNode = *pText; + aPam.GetPoint()->nContent.Assign( pText, 0 ); + } + else + *aPam.GetPoint() = SwPosition( aTmpIdx ); + } + } + rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, true, RedlineType::Any ); + + if( pEntry->pUndo ) + { + pEntry->pUndo->UndoImpl(rContext); + pEntry->pUndo.reset(); + } + if( bShiftPam ) + { + // The aPam.Point is at the moment at the last position of the new content and has to be + // moved to the first position of the old content for the SwUndoDelete operation + SwNodeIndex aTmpIdx( aPam.GetPoint()->nNode, 1 ); + SwTextNode *pText = aTmpIdx.GetNode().GetTextNode(); + if( pText ) + { + aPam.GetPoint()->nNode = *pText; + aPam.GetPoint()->nContent.Assign( pText, 0 ); + } + else + *aPam.GetPoint() = SwPosition( aTmpIdx ); + } + pUndo = std::make_unique<SwUndoDelete>( aPam, bDeleteCompleteParagraph, true ); + } + else + { + pUndo = std::make_unique<SwUndoDelete>( aPam, true ); + if( pEntry->pUndo ) + { + pEntry->pUndo->UndoImpl(rContext); + pEntry->pUndo.reset(); + } + } + pEntry->pUndo = std::move(pUndo); + + aInsIdx = rBox.GetSttIdx() + 1; + rDoc.GetNodes().Delete( aInsIdx ); + + SfxItemSet aTmpSet( + rDoc.GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + aTmpSet.Put( rBox.GetFrameFormat()->GetAttrSet() ); + if( aTmpSet.Count() ) + { + SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat(); + pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ); + pBoxFormat->ResetFormatAttr( RES_VERT_ORIENT ); + } + + if( pEntry->pBoxNumAttr ) + { + rBox.ClaimFrameFormat()->SetFormatAttr( *pEntry->pBoxNumAttr ); + pEntry->pBoxNumAttr.reset(); + } + + if( aTmpSet.Count() ) + { + pEntry->pBoxNumAttr = std::make_unique<SfxItemSet>( + rDoc.GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + pEntry->pBoxNumAttr->Put( aTmpSet ); + } + + pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx; + } + + if( m_pInsRowUndo ) + { + m_pInsRowUndo->UndoImpl(rContext); + } + DEBUG_REDLINE( &rDoc ) +} + +void SwUndoTableCpyTable::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + DEBUG_REDLINE( &rDoc ) + + if( m_pInsRowUndo ) + { + m_pInsRowUndo->RedoImpl(rContext); + } + + SwTableNode* pTableNd = nullptr; + for (size_t n = 0; n < m_vArr.size(); ++n) + { + UndoTableCpyTable_Entry *const pEntry = m_vArr[ n ].get(); + sal_uLong nSttPos = pEntry->nBoxIdx + pEntry->nOffset; + SwStartNode* pSNd = rDoc.GetNodes()[ nSttPos ]->StartOfSectionNode(); + if( !pTableNd ) + pTableNd = pSNd->FindTableNode(); + + SwTableBox& rBox = *pTableNd->GetTable().GetTableBox( nSttPos ); + + SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 ); + + // b62341295: Redline for copying tables - Start. + rDoc.GetNodes().MakeTextNode( aInsIdx, rDoc.GetDfltTextFormatColl() ); + SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode()); + std::unique_ptr<SwUndo> pUndo = IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ? nullptr : std::make_unique<SwUndoDelete>( aPam, true ); + if( pEntry->pUndo ) + { + pEntry->pUndo->UndoImpl(rContext); + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ) + { + // PrepareRedline has to be called with the beginning of the old content + // When new and old content has been joined, the rIter.pAktPam has been set + // by the Undo operation to this point. + // Otherwise aInsIdx has been moved during the Undo operation + if( pEntry->bJoin ) + { + SwPaM const& rLastPam = + rContext.GetCursorSupplier().GetCurrentShellCursor(); + pUndo = PrepareRedline( &rDoc, rBox, *rLastPam.GetPoint(), + pEntry->bJoin, true ); + } + else + { + SwPosition aTmpPos( aInsIdx ); + pUndo = PrepareRedline( &rDoc, rBox, aTmpPos, pEntry->bJoin, true ); + } + } + pEntry->pUndo.reset(); + } + pEntry->pUndo = std::move(pUndo); + // b62341295: Redline for copying tables - End. + + aInsIdx = rBox.GetSttIdx() + 1; + rDoc.GetNodes().Delete( aInsIdx ); + + SfxItemSet aTmpSet( + rDoc.GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + aTmpSet.Put( rBox.GetFrameFormat()->GetAttrSet() ); + if( aTmpSet.Count() ) + { + SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat(); + pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ); + pBoxFormat->ResetFormatAttr( RES_VERT_ORIENT ); + } + if( pEntry->pBoxNumAttr ) + { + rBox.ClaimFrameFormat()->SetFormatAttr( *pEntry->pBoxNumAttr ); + pEntry->pBoxNumAttr.reset(); + } + + if( aTmpSet.Count() ) + { + pEntry->pBoxNumAttr = std::make_unique<SfxItemSet>( + rDoc.GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + pEntry->pBoxNumAttr->Put( aTmpSet ); + } + + pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx; + } + DEBUG_REDLINE( &rDoc ) +} + +void SwUndoTableCpyTable::AddBoxBefore( const SwTableBox& rBox, bool bDelContent ) +{ + if (!m_vArr.empty() && !bDelContent) + return; + + UndoTableCpyTable_Entry* pEntry = new UndoTableCpyTable_Entry( rBox ); + m_vArr.push_back(std::unique_ptr<UndoTableCpyTable_Entry>(pEntry)); + + SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc(); + DEBUG_REDLINE( pDoc ) + if( bDelContent ) + { + SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 ); + pDoc->GetNodes().MakeTextNode( aInsIdx, pDoc->GetDfltTextFormatColl() ); + SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode() ); + + if( !pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + pEntry->pUndo = std::make_unique<SwUndoDelete>( aPam, true ); + } + + pEntry->pBoxNumAttr = std::make_unique<SfxItemSet>( + pDoc->GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + pEntry->pBoxNumAttr->Put( rBox.GetFrameFormat()->GetAttrSet() ); + if( !pEntry->pBoxNumAttr->Count() ) + { + pEntry->pBoxNumAttr.reset(); + } + DEBUG_REDLINE( pDoc ) +} + +void SwUndoTableCpyTable::AddBoxAfter( const SwTableBox& rBox, const SwNodeIndex& rIdx, bool bDelContent ) +{ + UndoTableCpyTable_Entry *const pEntry = m_vArr.back().get(); + + // If the content was deleted then remove also the temporarily created node + if( bDelContent ) + { + SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc(); + DEBUG_REDLINE( pDoc ) + + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + SwPosition aTmpPos( rIdx ); + pEntry->pUndo = PrepareRedline( pDoc, rBox, aTmpPos, pEntry->bJoin, false ); + } + SwNodeIndex aDelIdx( *rBox.GetSttNd(), 1 ); + rBox.GetFrameFormat()->GetDoc()->GetNodes().Delete( aDelIdx ); + DEBUG_REDLINE( pDoc ) + } + + pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx; +} + +// PrepareRedline is called from AddBoxAfter() and from Redo() in slightly different situations. +// bRedo is set by calling from Redo() +// rJoin is false by calling from AddBoxAfter() and will be set if the old and new content has +// been merged. +// rJoin is true if Redo() is calling and the content has already been merged + +std::unique_ptr<SwUndo> SwUndoTableCpyTable::PrepareRedline( SwDoc* pDoc, const SwTableBox& rBox, + const SwPosition& rPos, bool& rJoin, bool bRedo ) +{ + std::unique_ptr<SwUndo> pUndo; + // b62341295: Redline for copying tables + // What's to do? + // Mark the cell content before rIdx as insertion, + // mark the cell content behind rIdx as deletion + // merge text nodes at rIdx if possible + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld | RedlineFlags::DontCombineRedlines ) & ~RedlineFlags::Ignore ); + SwPosition aInsertEnd( rPos ); + SwTextNode* pText; + if( !rJoin ) + { + // If the content is not merged, the end of the insertion is at the end of the node + // _before_ the given position rPos + --aInsertEnd.nNode; + pText = aInsertEnd.nNode.GetNode().GetTextNode(); + if( pText ) + { + aInsertEnd.nContent.Assign(pText, pText->GetText().getLength()); + if( !bRedo && rPos.nNode.GetNode().GetTextNode() ) + { // Try to merge, if not called by Redo() + rJoin = true; + pText->JoinNext(); + } + } + else + aInsertEnd.nContent.Assign(nullptr, 0); + } + // For joined (merged) contents the start of deletion and end of insertion are identical + // otherwise adjacent nodes. + SwPosition aDeleteStart( rJoin ? aInsertEnd : rPos ); + if( !rJoin ) + { + pText = aDeleteStart.nNode.GetNode().GetTextNode(); + if( pText ) + aDeleteStart.nContent.Assign( pText, 0 ); + } + SwPosition aCellEnd( SwNodeIndex( *rBox.GetSttNd()->EndOfSectionNode(), -1 ) ); + pText = aCellEnd.nNode.GetNode().GetTextNode(); + if( pText ) + aCellEnd.nContent.Assign(pText, pText->GetText().getLength()); + if( aDeleteStart != aCellEnd ) + { // If the old (deleted) part is not empty, here we are... + SwPaM aDeletePam( aDeleteStart, aCellEnd ); + pUndo = std::make_unique<SwUndoRedlineDelete>( aDeletePam, SwUndoId::DELETE ); + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete, aDeletePam ), true ); + } + else if( !rJoin ) // If the old part is empty and joined, we are finished + { // if it is not joined, we have to delete this empty paragraph + aCellEnd = SwPosition( + SwNodeIndex( *rBox.GetSttNd()->EndOfSectionNode() )); + SwPaM aTmpPam( aDeleteStart, aCellEnd ); + pUndo = std::make_unique<SwUndoDelete>( aTmpPam, true ); + } + SwPosition aCellStart( SwNodeIndex( *rBox.GetSttNd(), 2 ) ); + pText = aCellStart.nNode.GetNode().GetTextNode(); + if( pText ) + aCellStart.nContent.Assign( pText, 0 ); + if( aCellStart != aInsertEnd ) // An empty insertion will not been marked + { + SwPaM aTmpPam( aCellStart, aInsertEnd ); + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aTmpPam ), true ); + } + + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + return pUndo; +} + +bool SwUndoTableCpyTable::InsertRow( SwTable& rTable, const SwSelBoxes& rBoxes, + sal_uInt16 nCnt ) +{ + SwTableNode* pTableNd = const_cast<SwTableNode*>(rTable.GetTabSortBoxes()[0]-> + GetSttNd()->FindTableNode()); + + m_pInsRowUndo.reset( new SwUndoTableNdsChg( SwUndoId::TABLE_INSROW, rBoxes, *pTableNd, + 0, 0, nCnt, true, false ) ); + SwTableSortBoxes aTmpLst( rTable.GetTabSortBoxes() ); + + bool bRet = rTable.InsertRow( rTable.GetFrameFormat()->GetDoc(), rBoxes, nCnt, /*bBehind*/true ); + if( bRet ) + m_pInsRowUndo->SaveNewBoxes( *pTableNd, aTmpLst ); + else + { + m_pInsRowUndo.reset(); + } + return bRet; +} + +bool SwUndoTableCpyTable::IsEmpty() const +{ + return !m_pInsRowUndo && m_vArr.empty(); +} + +SwUndoCpyTable::SwUndoCpyTable(const SwDoc* pDoc) + : SwUndo( SwUndoId::CPYTBL, pDoc ), m_nTableNode( 0 ) +{ +} + +SwUndoCpyTable::~SwUndoCpyTable() +{ +} + +void SwUndoCpyTable::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwTableNode* pTNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode(); + + // move hard page breaks into next node + SwContentNode* pNextNd = rDoc.GetNodes()[ pTNd->EndOfSectionIndex()+1 ]->GetContentNode(); + if( pNextNd ) + { + SwFrameFormat* pTableFormat = pTNd->GetTable().GetFrameFormat(); + const SfxPoolItem *pItem; + + if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC, + false, &pItem ) ) + pNextNd->SetAttr( *pItem ); + + if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK, + false, &pItem ) ) + pNextNd->SetAttr( *pItem ); + } + + SwPaM aPam( *pTNd, *pTNd->EndOfSectionNode(), 0 , 1 ); + m_pDelete.reset( new SwUndoDelete( aPam, true ) ); +} + +void SwUndoCpyTable::RedoImpl(::sw::UndoRedoContext & rContext) +{ + m_pDelete->UndoImpl(rContext); + m_pDelete.reset(); +} + +SwUndoSplitTable::SwUndoSplitTable( const SwTableNode& rTableNd, + std::unique_ptr<SwSaveRowSpan> pRowSp, SplitTable_HeadlineOption eMode, bool bNewSize ) + : SwUndo( SwUndoId::SPLIT_TABLE, rTableNd.GetDoc() ), + m_nTableNode( rTableNd.GetIndex() ), m_nOffset( 0 ), mpSaveRowSpan( std::move(pRowSp) ), + m_nMode( eMode ), m_nFormulaEnd( 0 ), m_bCalcNewSize( bNewSize ) +{ + switch( m_nMode ) + { + case SplitTable_HeadlineOption::BoxAttrAllCopy: + m_pHistory.reset(new SwHistory); + [[fallthrough]]; + case SplitTable_HeadlineOption::BorderCopy: + case SplitTable_HeadlineOption::BoxAttrCopy: + m_pSavedTable.reset(new SaveTable( rTableNd.GetTable(), 1, false )); + break; + default: break; + } +} + +SwUndoSplitTable::~SwUndoSplitTable() +{ + m_pSavedTable.reset(); + m_pHistory.reset(); + mpSaveRowSpan.reset(); +} + +void SwUndoSplitTable::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + SwNodeIndex& rIdx = pPam->GetPoint()->nNode; + rIdx = m_nTableNode + m_nOffset; + pPam->GetPoint()->nContent.Assign(rIdx.GetNode().GetContentNode(), 0); + assert(rIdx.GetNode().GetContentNode()->Len() == 0); // empty para inserted + + { + // avoid asserts from ~SwIndexReg + SwNodeIndex const idx(pDoc->GetNodes(), m_nTableNode + m_nOffset); + { + SwPaM pam(idx); + pam.Move(fnMoveBackward, GoInContent); + ::PaMCorrAbs(*pPam, *pam.GetPoint()); + } + + // remove implicitly created paragraph again + pDoc->GetNodes().Delete( idx ); + } + + rIdx = m_nTableNode + m_nOffset; + SwTableNode* pTableNd = rIdx.GetNode().GetTableNode(); + SwTable& rTable = pTableNd->GetTable(); + + SwTableFormulaUpdate aMsgHint( &rTable ); + aMsgHint.m_eFlags = TBL_BOXPTR; + pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint ); + + switch( m_nMode ) + { + case SplitTable_HeadlineOption::BoxAttrAllCopy: + if( m_pHistory ) + m_pHistory->TmpRollback( pDoc, m_nFormulaEnd ); + [[fallthrough]]; + case SplitTable_HeadlineOption::BoxAttrCopy: + case SplitTable_HeadlineOption::BorderCopy: + { + m_pSavedTable->CreateNew( rTable, false ); + m_pSavedTable->RestoreAttr( rTable ); + } + break; + + case SplitTable_HeadlineOption::ContentCopy: + // the created first line has to be removed again + { + SwSelBoxes aSelBoxes; + SwTableBox* pBox = rTable.GetTableBox( m_nTableNode + m_nOffset + 1 ); + SwTable::SelLineFromBox( pBox, aSelBoxes ); + FndBox_ aTmpBox( nullptr, nullptr ); + aTmpBox.SetTableLines( aSelBoxes, rTable ); + aTmpBox.DelFrames( rTable ); + rTable.DeleteSel( pDoc, aSelBoxes, nullptr, nullptr, false, false ); + } + break; + default: break; + } + + pDoc->GetNodes().MergeTable( rIdx ); + + if( m_pHistory ) + { + m_pHistory->TmpRollback( pDoc, 0 ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + } + if( mpSaveRowSpan ) + { + pTableNd = rIdx.GetNode().FindTableNode(); + if( pTableNd ) + pTableNd->GetTable().RestoreRowSpan( *mpSaveRowSpan ); + } + ClearFEShellTabCols(*pDoc, nullptr); +} + +void SwUndoSplitTable::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + pPam->DeleteMark(); + pPam->GetPoint()->nNode = m_nTableNode; + pDoc->SplitTable( *pPam->GetPoint(), m_nMode, m_bCalcNewSize ); + + ClearFEShellTabCols(*pDoc, nullptr); +} + +void SwUndoSplitTable::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwPaM *const pPam = & rContext.GetRepeatPaM(); + SwDoc *const pDoc = & rContext.GetDoc(); + + pDoc->SplitTable( *pPam->GetPoint(), m_nMode, m_bCalcNewSize ); + ClearFEShellTabCols(*pDoc, nullptr); +} + +void SwUndoSplitTable::SaveFormula( SwHistory& rHistory ) +{ + if( !m_pHistory ) + m_pHistory.reset(new SwHistory); + + m_nFormulaEnd = rHistory.Count(); + m_pHistory->Move( 0, &rHistory ); +} + +SwUndoMergeTable::SwUndoMergeTable( const SwTableNode& rTableNd, + const SwTableNode& rDelTableNd, + bool bWithPrv, sal_uInt16 nMd ) + : SwUndo( SwUndoId::MERGE_TABLE, rTableNd.GetDoc() ), + m_nMode( nMd ), m_bWithPrev( bWithPrv ) +{ + // memorize end node of the last table cell that'll stay in position + if( m_bWithPrev ) + m_nTableNode = rDelTableNd.EndOfSectionIndex() - 1; + else + m_nTableNode = rTableNd.EndOfSectionIndex() - 1; + + m_aName = rDelTableNd.GetTable().GetFrameFormat()->GetName(); + m_pSaveTable.reset(new SaveTable( rDelTableNd.GetTable() )); + + if (m_bWithPrev) + m_pSaveHdl.reset( new SaveTable( rTableNd.GetTable(), 1 ) ); +} + +SwUndoMergeTable::~SwUndoMergeTable() +{ + m_pSaveTable.reset(); + m_pSaveHdl.reset(); + m_pHistory.reset(); +} + +void SwUndoMergeTable::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + pPam->DeleteMark(); + SwNodeIndex& rIdx = pPam->GetPoint()->nNode; + rIdx = m_nTableNode; + + SwTableNode* pTableNd = rIdx.GetNode().FindTableNode(); + SwTable* pTable = &pTableNd->GetTable(); + + SwTableFormulaUpdate aMsgHint( pTable ); + aMsgHint.m_eFlags = TBL_BOXPTR; + pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint ); + + // get lines for layout update + FndBox_ aFndBox( nullptr, nullptr ); + aFndBox.SetTableLines( *pTable ); + aFndBox.DelFrames( *pTable ); + // ? TL_CHART2: notification or locking of controller required ? + + SwTableNode* pNew = pDoc->GetNodes().SplitTable( rIdx ); + + // update layout + aFndBox.MakeFrames( *pTable ); + // ? TL_CHART2: notification or locking of controller required ? + + if( m_bWithPrev ) + { + // move name + pNew->GetTable().GetFrameFormat()->SetName( pTable->GetFrameFormat()->GetName() ); + m_pSaveHdl->RestoreAttr( pNew->GetTable() ); + } + else + pTable = &pNew->GetTable(); + + pTable->GetFrameFormat()->SetName( m_aName ); + m_pSaveTable->RestoreAttr( *pTable ); + + if( m_pHistory ) + { + m_pHistory->TmpRollback( pDoc, 0 ); + m_pHistory->SetTmpEnd( m_pHistory->Count() ); + } + + // create frames for the new table + SwNodeIndex aTmpIdx( *pNew ); + pNew->MakeOwnFrames(&aTmpIdx); + + // position cursor somewhere in content + SwContentNode* pCNd = pDoc->GetNodes().GoNext( &rIdx ); + pPam->GetPoint()->nContent.Assign( pCNd, 0 ); + + ClearFEShellTabCols(*pDoc, nullptr); + + // TL_CHART2: need to inform chart of probably changed cell names + SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider(); + if (pPCD) + { + pDoc->UpdateCharts( pTable->GetFrameFormat()->GetName() ); + pDoc->UpdateCharts( pNew->GetTable().GetFrameFormat()->GetName() ); + } +} + +void SwUndoMergeTable::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + pPam->DeleteMark(); + pPam->GetPoint()->nNode = m_nTableNode; + if( m_bWithPrev ) + pPam->GetPoint()->nNode = m_nTableNode + 3; + else + pPam->GetPoint()->nNode = m_nTableNode; + + pDoc->MergeTable( *pPam->GetPoint(), m_bWithPrev, m_nMode ); + + ClearFEShellTabCols(*pDoc, nullptr); +} + +void SwUndoMergeTable::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM *const pPam = & rContext.GetRepeatPaM(); + + pDoc->MergeTable( *pPam->GetPoint(), m_bWithPrev, m_nMode ); + ClearFEShellTabCols(*pDoc, nullptr); +} + +void SwUndoMergeTable::SaveFormula( SwHistory& rHistory ) +{ + if( !m_pHistory ) + m_pHistory.reset( new SwHistory ); + m_pHistory->Move( 0, &rHistory ); +} + +void InsertSort( std::vector<sal_uInt16>& rArr, sal_uInt16 nIdx ) +{ + size_t nO = rArr.size(); + size_t nU = 0; + if( nO > 0 ) + { + nO--; + while( nU <= nO ) + { + const size_t nM = nU + ( nO - nU ) / 2; + if ( rArr[nM] == nIdx ) + { + OSL_FAIL( "Index already exists. This should never happen." ); + return; + } + if( rArr[nM] < nIdx ) + nU = nM + 1; + else if( nM == 0 ) + break; + else + nO = nM - 1; + } + } + rArr.insert( rArr.begin() + nU, nIdx ); +} + +#if OSL_DEBUG_LEVEL > 0 +void CheckTable( const SwTable& rTable ) +{ + const SwNodes& rNds = rTable.GetFrameFormat()->GetDoc()->GetNodes(); + const SwTableSortBoxes& rSrtArr = rTable.GetTabSortBoxes(); + for (size_t n = 0; n < rSrtArr.size(); ++n) + { + const SwTableBox* pBox = rSrtArr[ n ]; + const SwNode* pNd = pBox->GetSttNd(); + OSL_ENSURE( rNds[ pBox->GetSttIdx() ] == pNd, "Box with wrong StartNode" ); + } +} +#endif + +SwUndoTableStyleMake::SwUndoTableStyleMake(const OUString& rName, const SwDoc* pDoc) + : SwUndo(SwUndoId::TBLSTYLE_CREATE, pDoc), + m_sName(rName) +{ } + +SwUndoTableStyleMake::~SwUndoTableStyleMake() +{ } + +void SwUndoTableStyleMake::UndoImpl(::sw::UndoRedoContext & rContext) +{ + m_pAutoFormat = rContext.GetDoc().DelTableStyle(m_sName, true); +} + +void SwUndoTableStyleMake::RedoImpl(::sw::UndoRedoContext & rContext) +{ + if (m_pAutoFormat) + { + SwTableAutoFormat* pFormat = rContext.GetDoc().MakeTableStyle(m_sName, true); + if (pFormat) + { + *pFormat = *m_pAutoFormat; + m_pAutoFormat.reset(); + } + } +} + +SwRewriter SwUndoTableStyleMake::GetRewriter() const +{ + SwRewriter aResult; + aResult.AddRule(UndoArg1, m_sName); + return aResult; +} + +SwUndoTableStyleDelete::SwUndoTableStyleDelete(std::unique_ptr<SwTableAutoFormat> pAutoFormat, const std::vector<SwTable*>& rAffectedTables, const SwDoc* pDoc) + : SwUndo(SwUndoId::TBLSTYLE_DELETE, pDoc), + m_pAutoFormat(std::move(pAutoFormat)), + m_rAffectedTables(rAffectedTables) +{ } + +SwUndoTableStyleDelete::~SwUndoTableStyleDelete() +{ } + +void SwUndoTableStyleDelete::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwTableAutoFormat* pNewFormat = rContext.GetDoc().MakeTableStyle(m_pAutoFormat->GetName(), true); + *pNewFormat = *m_pAutoFormat; + for (size_t i=0; i < m_rAffectedTables.size(); i++) + m_rAffectedTables[i]->SetTableStyleName(m_pAutoFormat->GetName()); +} + +void SwUndoTableStyleDelete::RedoImpl(::sw::UndoRedoContext & rContext) +{ + // Don't need to remember deleted table style nor affected tables, because they must be the same as these already known. + rContext.GetDoc().DelTableStyle(m_pAutoFormat->GetName()); +} + +SwRewriter SwUndoTableStyleDelete::GetRewriter() const +{ + SwRewriter aResult; + aResult.AddRule(UndoArg1, m_pAutoFormat->GetName()); + return aResult; +} + +SwUndoTableStyleUpdate::SwUndoTableStyleUpdate(const SwTableAutoFormat& rNewFormat, const SwTableAutoFormat& rOldFormat, const SwDoc* pDoc) + : SwUndo(SwUndoId::TBLSTYLE_UPDATE, pDoc) + , m_pOldFormat(new SwTableAutoFormat(rOldFormat)) + , m_pNewFormat(new SwTableAutoFormat(rNewFormat)) +{ } + +SwUndoTableStyleUpdate::~SwUndoTableStyleUpdate() +{ } + +void SwUndoTableStyleUpdate::UndoImpl(::sw::UndoRedoContext & rContext) +{ + rContext.GetDoc().ChgTableStyle(m_pNewFormat->GetName(), *m_pOldFormat); +} + +void SwUndoTableStyleUpdate::RedoImpl(::sw::UndoRedoContext & rContext) +{ + rContext.GetDoc().ChgTableStyle(m_pNewFormat->GetName(), *m_pNewFormat); +} + +SwRewriter SwUndoTableStyleUpdate::GetRewriter() const +{ + SwRewriter aResult; + aResult.AddRule(UndoArg1, m_pNewFormat->GetName()); + return aResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/undo/untblk.cxx b/sw/source/core/undo/untblk.cxx new file mode 100644 index 000000000..374241404 --- /dev/null +++ b/sw/source/core/undo/untblk.cxx @@ -0,0 +1,451 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <fmtanchr.hxx> +#include <frmfmt.hxx> +#include <doc.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IShellCursorSupplier.hxx> +#include <docary.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <mvsave.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> +#include <redline.hxx> +#include <frameformats.hxx> + +namespace sw { + +std::unique_ptr<std::vector<SwFrameFormat*>> +GetFlysAnchoredAt(SwDoc & rDoc, sal_uLong const nSttNode) +{ + std::unique_ptr<std::vector<SwFrameFormat*>> pFrameFormats; + const size_t nArrLen = rDoc.GetSpzFrameFormats()->size(); + for (size_t n = 0; n < nArrLen; ++n) + { + SwFrameFormat *const pFormat = (*rDoc.GetSpzFrameFormats())[n]; + SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor(); + SwPosition const*const pAPos = pAnchor->GetContentAnchor(); + if (pAPos + && nSttNode == pAPos->nNode.GetIndex() + && ((pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA) + || (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR))) + { + if (!pFrameFormats) + pFrameFormats.reset( new std::vector<SwFrameFormat*> ); + pFrameFormats->push_back( pFormat ); + } + } + return pFrameFormats; +} + +} // namespace sw + +//note: parameter is SwPam just so we can init SwUndRng, the End is ignored! +SwUndoInserts::SwUndoInserts( SwUndoId nUndoId, const SwPaM& rPam ) + : SwUndo( nUndoId, rPam.GetDoc() ) + , SwUndRng( rPam ) + , m_pTextFormatColl(nullptr) + , m_pLastNodeColl(nullptr) + , m_nDeleteTextNodes(1) + , m_nNodeDiff(0) + , m_nSetPos(0) +{ + m_pHistory.reset( new SwHistory ); + SwDoc* pDoc = rPam.GetDoc(); + + SwTextNode* pTextNd = rPam.GetPoint()->nNode.GetNode().GetTextNode(); + if( pTextNd ) + { + m_pTextFormatColl = pTextNd->GetTextColl(); + assert(m_pTextFormatColl); + m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nSttNode, + 0, pTextNd->GetText().getLength(), false ); + if( pTextNd->HasSwAttrSet() ) + m_pHistory->CopyFormatAttr( *pTextNd->GetpSwAttrSet(), m_nSttNode ); + + // We may have some flys anchored to paragraph where we inserting. + // These flys will be saved in pFrameFormats array (only flys which exist BEFORE insertion!) + // Then in SwUndoInserts::SetInsertRange the flys saved in pFrameFormats will NOT create Undos. + // m_FlyUndos will only be filled with newly inserted flys. + m_pFrameFormats = sw::GetFlysAnchoredAt(*pDoc, m_nSttNode); + } + // consider Redline + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + m_pRedlineData.reset( new SwRedlineData( RedlineType::Insert, pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() ); + } +} + +// This method does two things: +// 1. Adjusts SwUndoRng members, required for Undo. +// Members are: +// SwUndoRng::nSttNode - all nodes starting from this node will be deleted during Undo (in SwUndoInserts::UndoImpl) +// SwUndoRng::nSttContent - corresponding content index in SwUndoRng::nSttNode +// SwUndoRng::nEndNode - end node for deletion +// SwUndoRng::nEndContent - end content index +// All these members are filled in during construction of SwUndoInserts instance, and can be adjusted using this method +// +// 2. Fills in m_FlyUndos array with flys anchored ONLY to first and last paragraphs (first == rPam.Start(), last == rPam.End()) +// Flys, anchored to any paragraph, but not first and last, are handled by DelContentIndex (see SwUndoInserts::UndoImpl) and are not stored in m_FlyUndos. + +void SwUndoInserts::SetInsertRange( const SwPaM& rPam, bool bScanFlys, + int const nDeleteTextNodes) +{ + const SwPosition* pTmpPos = rPam.End(); + m_nEndNode = pTmpPos->nNode.GetIndex(); + m_nEndContent = pTmpPos->nContent.GetIndex(); + if( rPam.HasMark() ) + { + if( pTmpPos == rPam.GetPoint() ) + pTmpPos = rPam.GetMark(); + else + pTmpPos = rPam.GetPoint(); + + m_nSttNode = pTmpPos->nNode.GetIndex(); + m_nSttContent = pTmpPos->nContent.GetIndex(); + + m_nDeleteTextNodes = nDeleteTextNodes; + if (m_nDeleteTextNodes == 0) // if a table selection is added... + { + ++m_nSttNode; // ... then the CopyPam is not fully correct + } + } + + // Fill m_FlyUndos with flys anchored to first and last paragraphs + + if( bScanFlys) + { + // than collect all new Flys + SwDoc* pDoc = rPam.GetDoc(); + const size_t nArrLen = pDoc->GetSpzFrameFormats()->size(); + for( size_t n = 0; n < nArrLen; ++n ) + { + SwFrameFormat* pFormat = (*pDoc->GetSpzFrameFormats())[n]; + SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor(); + if (IsCreateUndoForNewFly(*pAnchor, m_nSttNode, m_nEndNode)) + { + std::vector<SwFrameFormat*>::iterator it; + if( !m_pFrameFormats || + m_pFrameFormats->end() == ( it = std::find( m_pFrameFormats->begin(), m_pFrameFormats->end(), pFormat ) ) ) + { + std::shared_ptr<SwUndoInsLayFormat> const pFlyUndo = + std::make_shared<SwUndoInsLayFormat>(pFormat, 0, 0); + m_FlyUndos.push_back(pFlyUndo); + } + else + m_pFrameFormats->erase( it ); + } + } + m_pFrameFormats.reset(); + } +} + +/** This is not the same as IsDestroyFrameAnchoredAtChar() + and intentionally so: because the SwUndoInserts::UndoImpl() must remove + the flys at the start/end position that were inserted but not the ones + at the start/insert position that were already there; + handle all at-char flys at start/end node like this, even if they're + not *on* the start/end position, because it makes it easier to ensure + that the Undo/Redo run in inverse order. + */ +bool SwUndoInserts::IsCreateUndoForNewFly(SwFormatAnchor const& rAnchor, + sal_uLong const nStartNode, sal_uLong const nEndNode) +{ + assert(nStartNode <= nEndNode); + + // check all at-char flys at the start/end nodes: + // ExcludeFlyAtStartEnd will exclude them! + SwPosition const*const pAnchorPos = rAnchor.GetContentAnchor(); + return pAnchorPos != nullptr + && ( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA + || rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) + && ( nStartNode == pAnchorPos->nNode.GetIndex() + || nEndNode == pAnchorPos->nNode.GetIndex()); +} + +SwUndoInserts::~SwUndoInserts() +{ + if (m_pUndoNodeIndex) // delete also the section from UndoNodes array + { + // Insert saves content in IconSection + SwNodes& rUNds = m_pUndoNodeIndex->GetNodes(); + rUNds.Delete(*m_pUndoNodeIndex, + rUNds.GetEndOfExtras().GetIndex() - m_pUndoNodeIndex->GetIndex()); + m_pUndoNodeIndex.reset(); + } + m_pFrameFormats.reset(); + m_pRedlineData.reset(); +} + +// Undo Insert operation +// It's important to note that Undo stores absolute node indexes. I.e. if during insertion, you insert nodes 31 to 33, +// during Undo nodes with indices from 31 to 33 will be deleted. Undo doesn't check that nodes 31 to 33 are the same nodes which were inserted. +// It just deletes them. +// This may seem as bad programming practice, but Undo actions are strongly ordered. If you change your document in some way, a new Undo action is added. +// During Undo most recent actions will be executed first. So during execution of particular Undo action indices will be correct. +// But storing absolute indices leads to crashes if some action in Undo fails to roll back some modifications. + +// Has following main steps: +// 1. m_FlyUndos removes flys anchored to first and last paragraph in Undo range. +// This array may be empty. +// 2. DelContentIndex to delete footnotes, flys, bookmarks (see comment for this function) +// Deleted flys are stored in pHistory array. +// First and last paragraphs flys are not deleted by DelContentIndex! +// For flys anchored to last paragraph, DelContentIndex re-anchors them to +// the last paragraph that will remain after Undo. +// 3. MoveToUndoNds moves nodes to Undo nodes array and removes them from document. +// 4. Lastly (starting from if(pTextNode)), text from last paragraph is joined to last remaining paragraph and FormatColl for last paragraph is restored. +// Format coll for last paragraph is removed during execution of UndoImpl + +void SwUndoInserts::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc& rDoc = rContext.GetDoc(); + SwPaM& rPam = AddUndoRedoPaM(rContext); + + m_nNodeDiff = 0; + + if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any); + + // if Point and Mark are different text nodes so a JoinNext has to be done + bool bJoinNext = m_nSttNode != m_nEndNode && + rPam.GetMark()->nNode.GetNode().GetTextNode() && + rPam.GetPoint()->nNode.GetNode().GetTextNode(); + + // Is there any content? (loading from template does not have content) + if( m_nSttNode != m_nEndNode || m_nSttContent != m_nEndContent ) + { + if( m_nSttNode != m_nEndNode ) + { + SwTextNode* pTextNd = rDoc.GetNodes()[ m_nEndNode ]->GetTextNode(); + if (pTextNd && pTextNd->GetText().getLength() == m_nEndContent) + m_pLastNodeColl = pTextNd->GetTextColl(); + } + + // tdf#128739 correct cursors but do not delete bookmarks yet + ::PaMCorrAbs(rPam, *rPam.End()); + + SetPaM(rPam); + } + + // ... for consistency with the Insert File code in shellio.cxx, which + // creates separate SwUndoInsLayFormat for mysterious reasons, do this + // *before* anything else: + // after SetPaM but before MoveToUndoNds and DelContentIndex. + // note: there isn't an order dep wrt. initial Copy action because Undo + // overwrites the indexes but there is wrt. Redo because that uses the + // indexes + if (!m_FlyUndos.empty()) + { + sal_uLong nTmp = rPam.GetPoint()->nNode.GetIndex(); + for (size_t n = m_FlyUndos.size(); 0 < n; --n) + { + m_FlyUndos[ n-1 ]->UndoImpl(rContext); + } + m_nNodeDiff += nTmp - rPam.GetPoint()->nNode.GetIndex(); + } + + if (m_nSttNode != m_nEndNode || m_nSttContent != m_nEndContent) + { + // are there Footnotes or ContentFlyFrames in text? + m_nSetPos = m_pHistory->Count(); + sal_uLong nTmp = rPam.GetMark()->nNode.GetIndex(); + DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), + DelContentType::AllMask|DelContentType::ExcludeFlyAtStartEnd); + m_nNodeDiff += nTmp - rPam.GetMark()->nNode.GetIndex(); + if( *rPam.GetPoint() != *rPam.GetMark() ) + { + m_pUndoNodeIndex.reset( + new SwNodeIndex(rDoc.GetNodes().GetEndOfContent())); + MoveToUndoNds(rPam, m_pUndoNodeIndex.get()); + + if (m_nDeleteTextNodes == 0) + { + rPam.Move( fnMoveBackward, GoInContent ); + } + } + } + + SwNodeIndex& rIdx = rPam.GetPoint()->nNode; + SwTextNode* pTextNode = rIdx.GetNode().GetTextNode(); + if( pTextNode ) + { + if( !m_pTextFormatColl ) // if 0 than it's no TextNode -> delete + { + SwNodeIndex aDelIdx( rIdx ); + assert(0 < m_nDeleteTextNodes && m_nDeleteTextNodes < 3); + for (int i = 0; i < m_nDeleteTextNodes; ++i) + { + rPam.Move(fnMoveForward, GoInNode); + } + rPam.DeleteMark(); + + for (int i = 0; i < m_nDeleteTextNodes; ++i) + { + RemoveIdxRel(aDelIdx.GetIndex() + i, *rPam.GetPoint()); + } + + rDoc.GetNodes().Delete( aDelIdx, m_nDeleteTextNodes ); + } + else + { + if( bJoinNext && pTextNode->CanJoinNext()) + { + { + RemoveIdxRel( rIdx.GetIndex()+1, SwPosition( rIdx, + SwIndex( pTextNode, pTextNode->GetText().getLength() ))); + } + pTextNode->JoinNext(); + } + // reset all text attributes in the paragraph! + pTextNode->RstTextAttr( SwIndex(pTextNode, 0), pTextNode->Len(), 0, nullptr, true ); + + pTextNode->ResetAllAttr(); + + if (rDoc.GetTextFormatColls()->IsAlive(m_pTextFormatColl)) + m_pTextFormatColl = static_cast<SwTextFormatColl*>(pTextNode->ChgFormatColl( m_pTextFormatColl )) ; + + m_pHistory->SetTmpEnd( m_nSetPos ); + m_pHistory->TmpRollback(&rDoc, 0, false); + } + } +} + +// See SwUndoInserts::UndoImpl comments +// All actions here should be done in reverse order to what is done in SwUndoInserts::UndoImpl! + +void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext) +{ + // position cursor onto REDO section + SwPaM& rPam(rContext.GetCursorSupplier().CreateNewShellCursor()); + SwDoc* pDoc = rPam.GetDoc(); + rPam.DeleteMark(); + rPam.GetPoint()->nNode = m_nSttNode - m_nNodeDiff; + SwContentNode* pCNd = rPam.GetContentNode(); + rPam.GetPoint()->nContent.Assign( pCNd, m_nSttContent ); + + SwTextFormatColl* pSavTextFormatColl = m_pTextFormatColl; + if( m_pTextFormatColl && pCNd && pCNd->IsTextNode() ) + pSavTextFormatColl = static_cast<SwTextNode*>(pCNd)->GetTextColl(); + + m_pHistory->SetTmpEnd( m_nSetPos ); + + // retrieve start position for rollback + if( ( m_nSttNode != m_nEndNode || m_nSttContent != m_nEndContent ) && m_pUndoNodeIndex) + { + auto const pFlysAtInsPos(sw::GetFlysAnchoredAt(*pDoc, + rPam.GetPoint()->nNode.GetIndex())); + + const bool bMvBkwrd = MovePtBackward(rPam); + + // re-insert content again (first detach m_pUndoNodeIndex!) + sal_uLong const nMvNd = m_pUndoNodeIndex->GetIndex(); + m_pUndoNodeIndex.reset(); + MoveFromUndoNds(*pDoc, nMvNd, *rPam.GetMark()); + if (m_nDeleteTextNodes != 0) + { + MovePtForward(rPam, bMvBkwrd); + } + rPam.Exchange(); + + // at-char anchors post SplitNode are on index 0 of 2nd node and will + // remain there - move them back to the start (end would also work?) + if (pFlysAtInsPos) + { + for (SwFrameFormat * pFly : *pFlysAtInsPos) + { + SwFormatAnchor const*const pAnchor = &pFly->GetAnchor(); + if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR) + { + SwFormatAnchor anchor(*pAnchor); + anchor.SetAnchor( rPam.GetMark() ); + pFly->SetFormatAttr(anchor); + } + } + } + } + + if (pDoc->GetTextFormatColls()->IsAlive(m_pTextFormatColl)) + { + SwTextNode* pTextNd = rPam.GetMark()->nNode.GetNode().GetTextNode(); + if( pTextNd ) + pTextNd->ChgFormatColl( m_pTextFormatColl ); + } + m_pTextFormatColl = pSavTextFormatColl; + + if (m_pLastNodeColl && pDoc->GetTextFormatColls()->IsAlive(m_pLastNodeColl) + && rPam.GetPoint()->nNode != rPam.GetMark()->nNode) + { + SwTextNode* pTextNd = rPam.GetPoint()->nNode.GetNode().GetTextNode(); + if( pTextNd ) + pTextNd->ChgFormatColl( m_pLastNodeColl ); + } + + // tdf#108124 the SwHistoryChangeFlyAnchor/SwHistoryFlyCnt must run before + // m_FlyUndos as they were created by DelContentIndex() + m_pHistory->Rollback( pDoc, m_nSetPos ); + + // tdf#108124 (10/25/2017) + // During UNDO we call SwUndoInsLayFormat::UndoImpl in reverse order, + // firstly for m_FlyUndos[ m_FlyUndos.size()-1 ], etc. + // As absolute node index of fly stored in SwUndoFlyBase::nNdPgPos we + // should recover from Undo in direct order (last should be recovered first) + // During REDO we should recover Flys (Images) in direct order, + // firstly m_FlyUndos[0], then with m_FlyUndos[1] index, etc. + + for (size_t n = 0; m_FlyUndos.size() > n; ++n) + { + m_FlyUndos[n]->RedoImpl(rContext); + } + + if( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + { + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags::Ignore ); + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ), true); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) && + !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + pDoc->getIDocumentRedlineAccess().SplitRedline(rPam); +} + +void SwUndoInserts::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwPaM aPam( rContext.GetDoc().GetNodes().GetEndOfContent() ); + SetPaM( aPam ); + SwPaM & rRepeatPaM( rContext.GetRepeatPaM() ); + aPam.GetDoc()->getIDocumentContentOperations().CopyRange( aPam, *rRepeatPaM.GetPoint(), SwCopyFlags::CheckPosInFly); +} + +SwUndoInsDoc::SwUndoInsDoc( const SwPaM& rPam ) + : SwUndoInserts( SwUndoId::INSDOKUMENT, rPam ) +{ +} + +SwUndoCpyDoc::SwUndoCpyDoc( const SwPaM& rPam ) + : SwUndoInserts( SwUndoId::COPY, rPam ) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |