summaryrefslogtreecommitdiffstats
path: root/sw/source/core/undo/docundo.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/undo/docundo.cxx')
-rw-r--r--sw/source/core/undo/docundo.cxx839
1 files changed, 839 insertions, 0 deletions
diff --git a/sw/source/core/undo/docundo.cxx b/sw/source/core/undo/docundo.cxx
new file mode 100644
index 000000000..50db51c04
--- /dev/null
+++ b/sw/source/core/undo/docundo.cxx
@@ -0,0 +1,839 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UndoManager.hxx>
+
+#include <libxml/xmlwriter.h>
+
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <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>
+#include <osl/diagnose.h>
+#include <o3tl/temporary.hxx>
+
+#include <UndoInsert.hxx>
+
+using namespace ::com::sun::star;
+
+// the undo array should never grow beyond this limit:
+#define UNDO_ACTION_LIMIT (USHRT_MAX - 1000)
+
+namespace sw {
+
+UndoManager::UndoManager(std::shared_ptr<SwNodes> 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;
+}
+
+/**
+ * Checks if the topmost undo action owned by pView is independent from the topmost action undo
+ * action.
+ */
+bool UndoManager::IsViewUndoActionIndependent(const SwView* pView, sal_uInt16& rOffset) const
+{
+ if (GetUndoActionCount() <= 1)
+ {
+ // Single or less undo, owned by another view.
+ return false;
+ }
+
+ if (!pView)
+ {
+ return false;
+ }
+
+ // Last undo action that doesn't belong to the view.
+ const SfxUndoAction* pTopAction = GetUndoAction();
+
+ ViewShellId nViewId = pView->GetViewShellId();
+
+ // Earlier undo action that belongs to the view, but is not the top one.
+ const SfxUndoAction* pViewAction = nullptr;
+ size_t nOffset = 0;
+ for (size_t i = 0; i < GetUndoActionCount(); ++i)
+ {
+ const SfxUndoAction* pAction = GetUndoAction(i);
+ if (pAction->GetViewShellId() == nViewId)
+ {
+ pViewAction = pAction;
+ nOffset = i;
+ break;
+ }
+ }
+
+ if (!pViewAction)
+ {
+ // Found no earlier undo action that belongs to the view.
+ return false;
+ }
+
+ auto pTopSwAction = dynamic_cast<const SwUndo*>(pTopAction);
+ if (!pTopSwAction || pTopSwAction->GetId() != SwUndoId::TYPING)
+ {
+ return false;
+ }
+
+ auto pViewSwAction = dynamic_cast<const SwUndo*>(pViewAction);
+ if (!pViewSwAction || pViewSwAction->GetId() != SwUndoId::TYPING)
+ {
+ return false;
+ }
+
+ const auto& rTopInsert = *static_cast<const SwUndoInsert*>(pTopSwAction);
+ const auto& rViewInsert = *static_cast<const SwUndoInsert*>(pViewSwAction);
+
+ for (size_t i = 0; i < GetRedoActionCount(); ++i)
+ {
+ auto pRedoAction = dynamic_cast<const SwUndo*>(GetRedoAction(i));
+ if (!pRedoAction || pViewSwAction->GetId() != SwUndoId::TYPING)
+ {
+ return false;
+ }
+
+ const auto& rRedoInsert = *static_cast<const SwUndoInsert*>(pRedoAction);
+ if (!rViewInsert.IsIndependent(rRedoInsert) && rRedoInsert.GetViewShellId() != nViewId)
+ {
+ // Dependent redo action and owned by another view.
+ return false;
+ }
+ }
+
+ if (!rViewInsert.IsIndependent(rTopInsert))
+ {
+ return false;
+ }
+
+ rOffset = nOffset;
+ return true;
+}
+
+bool
+UndoManager::GetLastUndoInfo(
+ OUString *const o_pStr, SwUndoId *const o_pId, const SwView* pView) const
+{
+ // this is actually expected to work on the current level,
+ // but that was really not obvious from the previous implementation...
+ if (!SdrUndoManager::GetUndoActionCount())
+ {
+ return false;
+ }
+
+ SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction() );
+
+ if (comphelper::LibreOfficeKit::isActive() && !m_bRepair)
+ {
+ // If another view created the undo action, prevent undoing it from this view.
+ ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId();
+ // Unless we know that the other view's undo action is independent from us.
+ if (pAction->GetViewShellId() != nViewShellId
+ && !IsViewUndoActionIndependent(pView, o3tl::temporary(sal_uInt16())))
+ {
+ if (o_pId)
+ {
+ *o_pId = SwUndoId::CONFLICT;
+ }
+ return false;
+ }
+ }
+
+ if (o_pStr)
+ {
+ *o_pStr = pAction->GetComment();
+ }
+ if (o_pId)
+ {
+ if (auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction))
+ *o_pId = static_cast<SwUndoId>(pListAction->GetId());
+ else if (auto pSwAction = dynamic_cast<const SwUndo*>(pAction))
+ *o_pId = pSwAction->GetId();
+ else
+ *o_pId = SwUndoId::EMPTY;
+ }
+
+ return true;
+}
+
+SwUndoComments_t UndoManager::GetUndoComments() const
+{
+ OSL_ENSURE(!SdrUndoManager::IsInListAction(),
+ "GetUndoComments() called while in list action?");
+
+ SwUndoComments_t ret;
+ const size_t nUndoCount(SdrUndoManager::GetUndoActionCount(TopLevel));
+ for (size_t n = 0; n < nUndoCount; ++n)
+ {
+ OUString const comment(
+ SdrUndoManager::GetUndoActionComment(n, TopLevel));
+ ret.push_back(comment);
+ }
+
+ return ret;
+}
+
+bool UndoManager::GetFirstRedoInfo(OUString *const o_pStr,
+ SwUndoId *const o_pId,
+ const SwView* pView) const
+{
+ if (!SdrUndoManager::GetRedoActionCount())
+ {
+ return false;
+ }
+
+ SfxUndoAction *const pAction( SdrUndoManager::GetRedoAction() );
+ if ( pAction == nullptr )
+ {
+ return false;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() && !m_bRepair)
+ {
+ // If another view created the undo action, prevent redoing it from this view.
+ ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId();
+ if (pAction->GetViewShellId() != nViewShellId)
+ {
+ if (o_pId)
+ {
+ *o_pId = SwUndoId::CONFLICT;
+ }
+ return false;
+ }
+ }
+
+ if (o_pStr)
+ {
+ *o_pStr = pAction->GetComment();
+ }
+ if (o_pId)
+ {
+ if (auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction))
+ *o_pId = static_cast<SwUndoId>(pListAction->GetId());
+ else if (auto pSwAction = dynamic_cast<const SwUndo*>(pAction))
+ *o_pId = pSwAction->GetId();
+ else
+ *o_pId = SwUndoId::EMPTY;
+ }
+
+ return true;
+}
+
+SwUndoComments_t UndoManager::GetRedoComments() const
+{
+ OSL_ENSURE(!SdrUndoManager::IsInListAction(),
+ "GetRedoComments() called while in list action?");
+
+ SwUndoComments_t ret;
+ const size_t nRedoCount(SdrUndoManager::GetRedoActionCount(TopLevel));
+ for (size_t n = 0; n < nRedoCount; ++n)
+ {
+ OUString const comment(
+ SdrUndoManager::GetRedoActionComment(n, TopLevel));
+ ret.push_back(comment);
+ }
+
+ return ret;
+}
+
+SwUndoId UndoManager::GetRepeatInfo(OUString *const o_pStr) const
+{
+ SwUndoId nRepeatId(SwUndoId::EMPTY);
+ GetLastUndoInfo(o_pStr, & nRepeatId);
+ if( SwUndoId::REPEAT_START <= nRepeatId && SwUndoId::REPEAT_END > nRepeatId )
+ {
+ return nRepeatId;
+ }
+ if (o_pStr) // not repeatable -> clear comment
+ {
+ o_pStr->clear();
+ }
+ return SwUndoId::EMPTY;
+}
+
+SwUndo * UndoManager::RemoveLastUndo()
+{
+ if (SdrUndoManager::GetRedoActionCount() ||
+ SdrUndoManager::GetRedoActionCount(TopLevel))
+ {
+ OSL_ENSURE(false, "RemoveLastUndoAction(): there are Redo actions?");
+ return nullptr;
+ }
+ if (!SdrUndoManager::GetUndoActionCount())
+ {
+ OSL_ENSURE(false, "RemoveLastUndoAction(): no Undo actions");
+ return nullptr;
+ }
+ SfxUndoAction *const pLastUndo(GetUndoAction());
+ SdrUndoManager::RemoveLastUndoAction();
+ return dynamic_cast<SwUndo *>(pLastUndo);
+}
+
+// SfxUndoManager
+
+void UndoManager::AddUndoAction(std::unique_ptr<SfxUndoAction> pAction, bool bTryMerge)
+{
+ SwUndo *const pUndo( dynamic_cast<SwUndo *>(pAction.get()) );
+ if (pUndo)
+ {
+ if (RedlineFlags::NONE == pUndo->GetRedlineFlags())
+ {
+ pUndo->SetRedlineFlags( m_rRedlineAccess.GetRedlineFlags() );
+ }
+ if (m_isAddWithIgnoreRepeat)
+ {
+ pUndo->IgnoreRepeat();
+ }
+ }
+ SdrUndoManager::AddUndoAction(std::move(pAction), bTryMerge);
+ if (m_pDocShell)
+ {
+ SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst( m_pDocShell );
+ while ( pViewFrame )
+ {
+ pViewFrame->GetBindings().Invalidate( SID_UNDO );
+ pViewFrame->GetBindings().Invalidate( SID_REDO );
+ pViewFrame = SfxViewFrame::GetNext( *pViewFrame, m_pDocShell );
+ }
+ }
+
+ // if the undo nodes array is too large, delete some actions
+ while (UNDO_ACTION_LIMIT < sal_Int32(GetUndoNodes().Count()))
+ {
+ RemoveOldestUndoAction();
+ }
+}
+
+namespace {
+
+class CursorGuard
+{
+public:
+ CursorGuard(SwEditShell & rShell, bool const bSave)
+ : m_rShell(rShell)
+ , m_bSaveCursor(bSave)
+ {
+ if (m_bSaveCursor)
+ {
+ m_rShell.Push(); // prevent modification of current cursor
+ }
+ }
+ ~CursorGuard() COVERITY_NOEXCEPT_FALSE
+ {
+ if (m_bSaveCursor)
+ {
+ m_rShell.Pop(SwCursorShell::PopMode::DeleteCurrent);
+ }
+ }
+private:
+ SwEditShell & m_rShell;
+ bool const m_bSaveCursor;
+};
+
+}
+
+bool UndoManager::impl_DoUndoRedo(UndoOrRedoType undoOrRedo, size_t nUndoOffset)
+{
+ SwDoc & rDoc(GetUndoNodes().GetDoc());
+
+ UnoActionContext c(& rDoc); // exception-safe StartAllAction/EndAllAction
+
+ 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);
+ context.SetUndoOffset(nUndoOffset);
+
+ // N.B. these may throw!
+ if (UndoOrRedoType::Undo == undoOrRedo)
+ {
+ bRet = SdrUndoManager::UndoWithContext(context);
+ }
+ else
+ {
+ bRet = SdrUndoManager::RedoWithContext(context);
+ }
+
+ if (bRet)
+ {
+ // if we are at the "last save" position, the document is not modified
+ if (SdrUndoManager::HasTopUndoActionMark(m_UndoSaveMark))
+ {
+ m_rState.ResetModified();
+ }
+ else
+ {
+ m_rState.SetModified();
+ }
+ }
+
+ pEditShell->HandleUndoRedoContext(context);
+
+ return bRet;
+}
+
+bool UndoManager::Undo() { return UndoWithOffset(0); }
+
+bool UndoManager::UndoWithOffset(size_t nUndoOffset)
+{
+ if(isTextEditActive())
+ {
+ return SdrUndoManager::Undo();
+ }
+ else
+ {
+ return impl_DoUndoRedo(UndoOrRedoType::Undo, nUndoOffset);
+ }
+}
+
+bool UndoManager::Redo()
+{
+ if(isTextEditActive())
+ {
+ return SdrUndoManager::Redo();
+ }
+ else
+ {
+ return impl_DoUndoRedo(UndoOrRedoType::Redo, /*nUndoOffset=*/0);
+ }
+}
+
+void UndoManager::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("swUndoManager"));
+ SdrUndoManager::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_xUndoNodes"));
+ m_xUndoNodes->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void UndoManager::EmptyActionsChanged()
+{
+ if (m_pDocShell)
+ {
+ m_pDocShell->Broadcast(SfxHint(SfxHintId::DocumentRepair));
+ }
+}
+
+/** N.B.: this does _not_ call SdrUndoManager::Repeat because it is not
+ possible to wrap a list action around it:
+ calling EnterListAction here will cause SdrUndoManager::Repeat
+ to repeat the list action!
+ */
+bool UndoManager::Repeat(::sw::RepeatContext & rContext,
+ sal_uInt16 const nRepeatCount)
+{
+ if (SdrUndoManager::IsInListAction())
+ {
+ OSL_ENSURE(false, "repeat in open list action???");
+ return false;
+ }
+ if (!SdrUndoManager::GetUndoActionCount(TopLevel))
+ {
+ return false;
+ }
+ SfxUndoAction *const pRepeatAction(GetUndoAction());
+ assert(pRepeatAction);
+ if (!pRepeatAction->CanRepeat(rContext))
+ {
+ return false;
+ }
+
+ OUString const comment(pRepeatAction->GetComment());
+ OUString const rcomment(pRepeatAction->GetRepeatComment(rContext));
+ SwUndoId nId;
+ if (auto const* const pSwAction = dynamic_cast<SwUndo*>(pRepeatAction))
+ nId = pSwAction->GetId();
+ else if (auto const* const pListAction = dynamic_cast<SfxListUndoAction*>(pRepeatAction))
+ nId = static_cast<SwUndoId>(pListAction->GetId());
+ else
+ return false;
+ if (DoesUndo())
+ {
+ ViewShellId nViewShellId(-1);
+ if (m_pDocShell)
+ {
+ if (const SwView* pView = m_pDocShell->GetView())
+ nViewShellId = pView->GetViewShellId();
+ }
+ EnterListAction(comment, rcomment, static_cast<sal_uInt16>(nId), nViewShellId);
+ }
+
+ SwPaM* pTmp = rContext.m_pCurrentPaM;
+ for(SwPaM& rPaM : rContext.GetRepeatPaM().GetRingContainer())
+ { // iterate over ring
+ rContext.m_pCurrentPaM = &rPaM;
+ if (DoesUndo() && & rPaM != pTmp)
+ {
+ m_isAddWithIgnoreRepeat = true;
+ }
+ for (sal_uInt16 nRptCnt = nRepeatCount; nRptCnt > 0; --nRptCnt)
+ {
+ pRepeatAction->Repeat(rContext);
+ }
+ if (DoesUndo() && & rPaM != pTmp)
+ {
+ m_isAddWithIgnoreRepeat = false;
+ }
+ rContext.m_bDeleteRepeated = false; // reset for next PaM
+ }
+ rContext.m_pCurrentPaM = pTmp;
+
+ if (DoesUndo())
+ {
+ LeaveListAction();
+ }
+ return true;
+}
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */