1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <UndoManager.hxx>
21 
22 #include <svx/svdmodel.hxx>
23 #include <swmodule.hxx>
24 #include <doc.hxx>
25 #include <docsh.hxx>
26 #include <view.hxx>
27 #include <drawdoc.hxx>
28 #include <ndarr.hxx>
29 #include <pam.hxx>
30 #include <ndtxt.hxx>
31 #include <swundo.hxx>
32 #include <UndoCore.hxx>
33 #include <rolbck.hxx>
34 #include <editsh.hxx>
35 #include <unobaseclass.hxx>
36 #include <limits>
37 #include <IDocumentDrawModelAccess.hxx>
38 #include <IDocumentRedlineAccess.hxx>
39 #include <IDocumentState.hxx>
40 #include <comphelper/lok.hxx>
41 #include <assert.h>
42 
43 #include <sfx2/viewfrm.hxx>
44 #include <sfx2/bindings.hxx>
45 
46 using namespace ::com::sun::star;
47 
48 // the undo array should never grow beyond this limit:
49 #define UNDO_ACTION_LIMIT (USHRT_MAX - 1000)
50 
51 namespace sw {
52 
UndoManager(std::shared_ptr<SwNodes> const & xUndoNodes,IDocumentDrawModelAccess & rDrawModelAccess,IDocumentRedlineAccess & rRedlineAccess,IDocumentState & rState)53 UndoManager::UndoManager(std::shared_ptr<SwNodes> const & xUndoNodes,
54             IDocumentDrawModelAccess & rDrawModelAccess,
55             IDocumentRedlineAccess & rRedlineAccess,
56             IDocumentState & rState)
57     :   m_rDrawModelAccess(rDrawModelAccess)
58     ,   m_rRedlineAccess(rRedlineAccess)
59     ,   m_rState(rState)
60     ,   m_xUndoNodes(xUndoNodes)
61     ,   m_bGroupUndo(true)
62     ,   m_bDrawUndo(true)
63     ,   m_bRepair(false)
64     ,   m_bLockUndoNoModifiedPosition(false)
65     ,   m_isAddWithIgnoreRepeat(false)
66     ,   m_UndoSaveMark(MARK_INVALID)
67     ,   m_pDocShell(nullptr)
68     ,   m_pView(nullptr)
69 {
70     assert(m_xUndoNodes.get());
71     // writer expects it to be disabled initially
72     // Undo is enabled by SwEditShell constructor
73     SdrUndoManager::EnableUndo(false);
74 }
75 
GetUndoNodes() const76 SwNodes const& UndoManager::GetUndoNodes() const
77 {
78     return *m_xUndoNodes;
79 }
80 
GetUndoNodes()81 SwNodes      & UndoManager::GetUndoNodes()
82 {
83     return *m_xUndoNodes;
84 }
85 
IsUndoNodes(SwNodes const & rNodes) const86 bool UndoManager::IsUndoNodes(SwNodes const& rNodes) const
87 {
88     return & rNodes == m_xUndoNodes.get();
89 }
90 
SetDocShell(SwDocShell * pDocShell)91 void UndoManager::SetDocShell(SwDocShell* pDocShell)
92 {
93     m_pDocShell = pDocShell;
94 }
95 
SetView(SwView * pView)96 void UndoManager::SetView(SwView* pView)
97 {
98     m_pView = pView;
99 }
100 
GetUndoActionCount(const bool bCurrentLevel) const101 size_t UndoManager::GetUndoActionCount(const bool bCurrentLevel) const
102 {
103     size_t nRet = SdrUndoManager::GetUndoActionCount(bCurrentLevel);
104     if (!comphelper::LibreOfficeKit::isActive() || !m_pView)
105         return nRet;
106 
107     if (!nRet || !SdrUndoManager::GetUndoActionCount())
108         return nRet;
109 
110     const SfxUndoAction* pAction = SdrUndoManager::GetUndoAction();
111     if (!pAction)
112         return nRet;
113 
114     if (!m_bRepair)
115     {
116         // If another view created the last undo action, prevent undoing it from this view.
117         ViewShellId nViewShellId = m_pView->GetViewShellId();
118         if (pAction->GetViewShellId() != nViewShellId)
119             nRet = 0;
120     }
121 
122     return nRet;
123 }
124 
GetRedoActionCount(const bool bCurrentLevel) const125 size_t UndoManager::GetRedoActionCount(const bool bCurrentLevel) const
126 {
127     size_t nRet = SdrUndoManager::GetRedoActionCount(bCurrentLevel);
128     if (!comphelper::LibreOfficeKit::isActive() || !m_pView)
129         return nRet;
130 
131     if (!nRet || !SdrUndoManager::GetRedoActionCount())
132         return nRet;
133 
134     const SfxUndoAction* pAction = SdrUndoManager::GetRedoAction();
135     if (!pAction)
136         return nRet;
137 
138     if (!m_bRepair)
139     {
140         // If another view created the first redo action, prevent redoing it from this view.
141         ViewShellId nViewShellId = m_pView->GetViewShellId();
142         if (pAction->GetViewShellId() != nViewShellId)
143             nRet = 0;
144     }
145 
146     return nRet;
147 }
148 
DoUndo(bool const bDoUndo)149 void UndoManager::DoUndo(bool const bDoUndo)
150 {
151     if(!isTextEditActive())
152     {
153         EnableUndo(bDoUndo);
154 
155         SwDrawModel*const pSdrModel = m_rDrawModelAccess.GetDrawModel();
156         if( pSdrModel )
157         {
158             pSdrModel->EnableUndo(bDoUndo);
159         }
160     }
161 }
162 
DoesUndo() const163 bool UndoManager::DoesUndo() const
164 {
165     if(isTextEditActive())
166     {
167         return false;
168     }
169     else
170     {
171         return IsUndoEnabled();
172     }
173 }
174 
DoGroupUndo(bool const bDoUndo)175 void UndoManager::DoGroupUndo(bool const bDoUndo)
176 {
177     m_bGroupUndo = bDoUndo;
178 }
179 
DoesGroupUndo() const180 bool UndoManager::DoesGroupUndo() const
181 {
182     return m_bGroupUndo;
183 }
184 
DoDrawUndo(bool const bDoUndo)185 void UndoManager::DoDrawUndo(bool const bDoUndo)
186 {
187     m_bDrawUndo = bDoUndo;
188 }
189 
DoesDrawUndo() const190 bool UndoManager::DoesDrawUndo() const
191 {
192     return m_bDrawUndo;
193 }
194 
DoRepair(bool bRepair)195 void UndoManager::DoRepair(bool bRepair)
196 {
197     m_bRepair = bRepair;
198 }
199 
DoesRepair() const200 bool UndoManager::DoesRepair() const
201 {
202     return m_bRepair;
203 }
204 
IsUndoNoResetModified() const205 bool UndoManager::IsUndoNoResetModified() const
206 {
207     return MARK_INVALID == m_UndoSaveMark;
208 }
209 
SetUndoNoResetModified()210 void UndoManager::SetUndoNoResetModified()
211 {
212     if (MARK_INVALID != m_UndoSaveMark)
213     {
214         RemoveMark(m_UndoSaveMark);
215         m_UndoSaveMark = MARK_INVALID;
216     }
217 }
218 
SetUndoNoModifiedPosition()219 void UndoManager::SetUndoNoModifiedPosition()
220 {
221     if (!m_bLockUndoNoModifiedPosition)
222     {
223         m_UndoSaveMark = MarkTopUndoAction();
224     }
225 }
226 
LockUndoNoModifiedPosition()227 void UndoManager::LockUndoNoModifiedPosition()
228 {
229     m_bLockUndoNoModifiedPosition = true;
230 }
231 
UnLockUndoNoModifiedPosition()232 void UndoManager::UnLockUndoNoModifiedPosition()
233 {
234     m_bLockUndoNoModifiedPosition = false;
235 }
236 
GetLastUndo()237 SwUndo* UndoManager::GetLastUndo()
238 {
239     if (!SdrUndoManager::GetUndoActionCount())
240     {
241         return nullptr;
242     }
243     SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction() );
244     return dynamic_cast<SwUndo*>(pAction);
245 }
246 
AppendUndo(std::unique_ptr<SwUndo> pUndo)247 void UndoManager::AppendUndo(std::unique_ptr<SwUndo> pUndo)
248 {
249     AddUndoAction(std::move(pUndo));
250 }
251 
ClearRedo()252 void UndoManager::ClearRedo()
253 {
254     return SdrUndoManager::ImplClearRedo_NoLock(TopLevel);
255 }
256 
DelAllUndoObj()257 void UndoManager::DelAllUndoObj()
258 {
259     ::sw::UndoGuard const undoGuard(*this);
260 
261     SdrUndoManager::ClearAllLevels();
262 
263     m_UndoSaveMark = MARK_INVALID;
264 }
265 
266 SwUndoId
StartUndo(SwUndoId const i_eUndoId,SwRewriter const * const pRewriter)267 UndoManager::StartUndo(SwUndoId const i_eUndoId,
268         SwRewriter const*const pRewriter)
269 {
270     if (!IsUndoEnabled())
271     {
272         return SwUndoId::EMPTY;
273     }
274 
275     SwUndoId const eUndoId( (i_eUndoId == SwUndoId::EMPTY) ? SwUndoId::START : i_eUndoId );
276 
277     assert(SwUndoId::END != eUndoId);
278     OUString comment( (SwUndoId::START == eUndoId)
279         ?   OUString("??")
280         :   GetUndoComment(eUndoId) );
281     if (pRewriter)
282     {
283         assert(SwUndoId::START != eUndoId);
284         comment = pRewriter->Apply(comment);
285     }
286 
287     ViewShellId nViewShellId(-1);
288     if (m_pDocShell)
289     {
290         if (const SwView* pView = m_pDocShell->GetView())
291             nViewShellId = pView->GetViewShellId();
292     }
293     SdrUndoManager::EnterListAction(comment, comment, static_cast<sal_uInt16>(eUndoId), nViewShellId);
294 
295     return eUndoId;
296 }
297 
298 SwUndoId
EndUndo(SwUndoId eUndoId,SwRewriter const * const pRewriter)299 UndoManager::EndUndo(SwUndoId eUndoId, SwRewriter const*const pRewriter)
300 {
301     if (!IsUndoEnabled())
302     {
303         return SwUndoId::EMPTY;
304     }
305 
306     if ((eUndoId == SwUndoId::EMPTY) || (SwUndoId::START == eUndoId))
307            eUndoId = SwUndoId::END;
308     OSL_ENSURE(!((SwUndoId::END == eUndoId) && pRewriter),
309                 "EndUndo(): no Undo ID, but rewriter given?");
310 
311     SfxUndoAction *const pLastUndo(
312         (0 == SdrUndoManager::GetUndoActionCount())
313             ? nullptr : SdrUndoManager::GetUndoAction() );
314 
315     int const nCount = LeaveListAction();
316 
317     if (nCount) // otherwise: empty list action not inserted!
318     {
319         assert(pLastUndo);
320         assert(SwUndoId::START != eUndoId);
321         auto pListAction = dynamic_cast<SfxListUndoAction*>(SdrUndoManager::GetUndoAction());
322         assert(pListAction);
323         if (SwUndoId::END != eUndoId)
324         {
325             OSL_ENSURE(static_cast<SwUndoId>(pListAction->GetId()) == eUndoId,
326                     "EndUndo(): given ID different from StartUndo()");
327             // comment set by caller of EndUndo
328             OUString comment = GetUndoComment(eUndoId);
329             if (pRewriter)
330             {
331                 comment = pRewriter->Apply(comment);
332             }
333             pListAction->SetComment(comment);
334         }
335         else if (SwUndoId::START != static_cast<SwUndoId>(pListAction->GetId()))
336         {
337             // comment set by caller of StartUndo: nothing to do here
338         }
339         else if (pLastUndo)
340         {
341             // comment was not set at StartUndo or EndUndo:
342             // take comment of last contained action
343             // (note that this works recursively, i.e. the last contained
344             // action may be a list action created by StartUndo/EndUndo)
345             OUString const comment(pLastUndo->GetComment());
346             pListAction->SetComment(comment);
347         }
348         else
349         {
350             OSL_ENSURE(false, "EndUndo(): no comment?");
351         }
352     }
353 
354     return eUndoId;
355 }
356 
357 bool
GetLastUndoInfo(OUString * const o_pStr,SwUndoId * const o_pId,const SwView * pView) const358 UndoManager::GetLastUndoInfo(
359         OUString *const o_pStr, SwUndoId *const o_pId, const SwView* pView) const
360 {
361     // this is actually expected to work on the current level,
362     // but that was really not obvious from the previous implementation...
363     if (!SdrUndoManager::GetUndoActionCount())
364     {
365         return false;
366     }
367 
368     SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction() );
369 
370     if (comphelper::LibreOfficeKit::isActive() && !m_bRepair)
371     {
372         // If another view created the undo action, prevent undoing it from this view.
373         ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId();
374         if (pAction->GetViewShellId() != nViewShellId)
375         {
376             if (o_pId)
377             {
378                  *o_pId = SwUndoId::CONFLICT;
379             }
380             return false;
381         }
382     }
383 
384     if (o_pStr)
385     {
386         *o_pStr = pAction->GetComment();
387     }
388     if (o_pId)
389     {
390         if (auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction))
391             *o_pId = static_cast<SwUndoId>(pListAction->GetId());
392         else if (auto pSwAction = dynamic_cast<const SwUndo*>(pAction))
393             *o_pId = pSwAction->GetId();
394         else
395             *o_pId = SwUndoId::EMPTY;
396     }
397 
398     return true;
399 }
400 
GetUndoComments() const401 SwUndoComments_t UndoManager::GetUndoComments() const
402 {
403     OSL_ENSURE(!SdrUndoManager::IsInListAction(),
404             "GetUndoComments() called while in list action?");
405 
406     SwUndoComments_t ret;
407     const size_t nUndoCount(SdrUndoManager::GetUndoActionCount(TopLevel));
408     for (size_t n = 0; n < nUndoCount; ++n)
409     {
410         OUString const comment(
411                 SdrUndoManager::GetUndoActionComment(n, TopLevel));
412         ret.push_back(comment);
413     }
414 
415     return ret;
416 }
417 
GetFirstRedoInfo(OUString * const o_pStr,SwUndoId * const o_pId,const SwView * pView) const418 bool UndoManager::GetFirstRedoInfo(OUString *const o_pStr,
419                                    SwUndoId *const o_pId,
420                                    const SwView* pView) const
421 {
422     if (!SdrUndoManager::GetRedoActionCount())
423     {
424         return false;
425     }
426 
427     SfxUndoAction *const pAction( SdrUndoManager::GetRedoAction() );
428     if ( pAction == nullptr )
429     {
430         return false;
431     }
432 
433     if (comphelper::LibreOfficeKit::isActive() && !m_bRepair)
434     {
435         // If another view created the undo action, prevent redoing it from this view.
436         ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId();
437         if (pAction->GetViewShellId() != nViewShellId)
438         {
439             if (o_pId)
440             {
441                  *o_pId = SwUndoId::CONFLICT;
442             }
443             return false;
444         }
445     }
446 
447     if (o_pStr)
448     {
449         *o_pStr = pAction->GetComment();
450     }
451     if (o_pId)
452     {
453         if (auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction))
454             *o_pId = static_cast<SwUndoId>(pListAction->GetId());
455         else if (auto pSwAction = dynamic_cast<const SwUndo*>(pAction))
456             *o_pId = pSwAction->GetId();
457         else
458             *o_pId = SwUndoId::EMPTY;
459     }
460 
461     return true;
462 }
463 
GetRedoComments() const464 SwUndoComments_t UndoManager::GetRedoComments() const
465 {
466     OSL_ENSURE(!SdrUndoManager::IsInListAction(),
467             "GetRedoComments() called while in list action?");
468 
469     SwUndoComments_t ret;
470     const size_t nRedoCount(SdrUndoManager::GetRedoActionCount(TopLevel));
471     for (size_t n = 0; n < nRedoCount; ++n)
472     {
473         OUString const comment(
474                 SdrUndoManager::GetRedoActionComment(n, TopLevel));
475         ret.push_back(comment);
476     }
477 
478     return ret;
479 }
480 
GetRepeatInfo(OUString * const o_pStr) const481 SwUndoId UndoManager::GetRepeatInfo(OUString *const o_pStr) const
482 {
483     SwUndoId nRepeatId(SwUndoId::EMPTY);
484     GetLastUndoInfo(o_pStr, & nRepeatId);
485     if( SwUndoId::REPEAT_START <= nRepeatId && SwUndoId::REPEAT_END > nRepeatId )
486     {
487         return nRepeatId;
488     }
489     if (o_pStr) // not repeatable -> clear comment
490     {
491         o_pStr->clear();
492     }
493     return SwUndoId::EMPTY;
494 }
495 
RemoveLastUndo()496 SwUndo * UndoManager::RemoveLastUndo()
497 {
498     if (SdrUndoManager::GetRedoActionCount() ||
499         SdrUndoManager::GetRedoActionCount(TopLevel))
500     {
501         OSL_ENSURE(false, "RemoveLastUndoAction(): there are Redo actions?");
502         return nullptr;
503     }
504     if (!SdrUndoManager::GetUndoActionCount())
505     {
506         OSL_ENSURE(false, "RemoveLastUndoAction(): no Undo actions");
507         return nullptr;
508     }
509     SfxUndoAction *const pLastUndo(GetUndoAction());
510     SdrUndoManager::RemoveLastUndoAction();
511     return dynamic_cast<SwUndo *>(pLastUndo);
512 }
513 
514 // SfxUndoManager
515 
AddUndoAction(std::unique_ptr<SfxUndoAction> pAction,bool bTryMerge)516 void UndoManager::AddUndoAction(std::unique_ptr<SfxUndoAction> pAction, bool bTryMerge)
517 {
518     SwUndo *const pUndo( dynamic_cast<SwUndo *>(pAction.get()) );
519     if (pUndo)
520     {
521         if (RedlineFlags::NONE == pUndo->GetRedlineFlags())
522         {
523             pUndo->SetRedlineFlags( m_rRedlineAccess.GetRedlineFlags() );
524         }
525         if (m_isAddWithIgnoreRepeat)
526         {
527             pUndo->IgnoreRepeat();
528         }
529     }
530     SdrUndoManager::AddUndoAction(std::move(pAction), bTryMerge);
531     if (m_pDocShell)
532     {
533         SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst( m_pDocShell );
534         while ( pViewFrame )
535         {
536             pViewFrame->GetBindings().Invalidate( SID_UNDO );
537             pViewFrame->GetBindings().Invalidate( SID_REDO );
538             pViewFrame = SfxViewFrame::GetNext( *pViewFrame, m_pDocShell );
539         }
540     }
541 
542     // if the undo nodes array is too large, delete some actions
543     while (UNDO_ACTION_LIMIT < GetUndoNodes().Count())
544     {
545         RemoveOldestUndoAction();
546     }
547 }
548 
549 class CursorGuard
550 {
551 public:
CursorGuard(SwEditShell & rShell,bool const bSave)552     CursorGuard(SwEditShell & rShell, bool const bSave)
553         : m_rShell(rShell)
554         , m_bSaveCursor(bSave)
555     {
556         if (m_bSaveCursor)
557         {
558             m_rShell.Push(); // prevent modification of current cursor
559         }
560     }
~CursorGuard()561     ~CursorGuard() COVERITY_NOEXCEPT_FALSE
562     {
563         if (m_bSaveCursor)
564         {
565             m_rShell.Pop(SwCursorShell::PopMode::DeleteCurrent);
566         }
567     }
568 private:
569     SwEditShell & m_rShell;
570     bool const m_bSaveCursor;
571 };
572 
impl_DoUndoRedo(UndoOrRedoType undoOrRedo)573 bool UndoManager::impl_DoUndoRedo(UndoOrRedoType undoOrRedo)
574 {
575     SwDoc & rDoc(*GetUndoNodes().GetDoc());
576 
577     UnoActionContext c(& rDoc); // exception-safe StartAllAction/EndAllAction
578 
579     SwEditShell *const pEditShell( rDoc.GetEditShell() );
580 
581     OSL_ENSURE(pEditShell, "sw::UndoManager needs a SwEditShell!");
582     if (!pEditShell)
583     {
584         throw uno::RuntimeException();
585     }
586 
587     // in case the model has controllers locked, the Undo should not
588     // change the view cursors!
589     bool const bSaveCursors(pEditShell->CursorsLocked());
590     CursorGuard aCursorGuard(*pEditShell, bSaveCursors);
591     if (!bSaveCursors)
592     {
593         // (in case Undo was called via API) clear the cursors:
594         pEditShell->KillPams();
595         pEditShell->SetMark();
596         pEditShell->ClearMark();
597     }
598 
599     bool bRet(false);
600 
601     ::sw::UndoRedoContext context(rDoc, *pEditShell);
602 
603     // N.B. these may throw!
604     if (UndoOrRedoType::Undo == undoOrRedo)
605     {
606         bRet = SdrUndoManager::UndoWithContext(context);
607     }
608     else
609     {
610         bRet = SdrUndoManager::RedoWithContext(context);
611     }
612 
613     if (bRet)
614     {
615         // if we are at the "last save" position, the document is not modified
616         if (SdrUndoManager::HasTopUndoActionMark(m_UndoSaveMark))
617         {
618             m_rState.ResetModified();
619         }
620         else
621         {
622             m_rState.SetModified();
623         }
624     }
625 
626     pEditShell->HandleUndoRedoContext(context);
627 
628     return bRet;
629 }
630 
Undo()631 bool UndoManager::Undo()
632 {
633     if(isTextEditActive())
634     {
635         return SdrUndoManager::Undo();
636     }
637     else
638     {
639         return impl_DoUndoRedo(UndoOrRedoType::Undo);
640     }
641 }
642 
Redo()643 bool UndoManager::Redo()
644 {
645     if(isTextEditActive())
646     {
647         return SdrUndoManager::Redo();
648     }
649     else
650     {
651         return impl_DoUndoRedo(UndoOrRedoType::Redo);
652     }
653 }
654 
EmptyActionsChanged()655 void UndoManager::EmptyActionsChanged()
656 {
657     if (m_pDocShell)
658     {
659         m_pDocShell->Broadcast(SfxHint(SfxHintId::DocumentRepair));
660     }
661 }
662 
663 /** N.B.: this does _not_ call SdrUndoManager::Repeat because it is not
664           possible to wrap a list action around it:
665           calling EnterListAction here will cause SdrUndoManager::Repeat
666           to repeat the list action!
667  */
Repeat(::sw::RepeatContext & rContext,sal_uInt16 const nRepeatCount)668 bool UndoManager::Repeat(::sw::RepeatContext & rContext,
669                          sal_uInt16 const nRepeatCount)
670 {
671     if (SdrUndoManager::IsInListAction())
672     {
673         OSL_ENSURE(false, "repeat in open list action???");
674         return false;
675     }
676     if (!SdrUndoManager::GetUndoActionCount(TopLevel))
677     {
678         return false;
679     }
680     SfxUndoAction *const pRepeatAction(GetUndoAction());
681     assert(pRepeatAction);
682     if (!pRepeatAction->CanRepeat(rContext))
683     {
684         return false;
685     }
686 
687     OUString const comment(pRepeatAction->GetComment());
688     OUString const rcomment(pRepeatAction->GetRepeatComment(rContext));
689     SwUndoId nId;
690     if (auto const* const pSwAction = dynamic_cast<SwUndo*>(pRepeatAction))
691         nId = pSwAction->GetId();
692     else if (auto const* const pListAction = dynamic_cast<SfxListUndoAction*>(pRepeatAction))
693         nId = static_cast<SwUndoId>(pListAction->GetId());
694     else
695         return false;
696     if (DoesUndo())
697     {
698         ViewShellId nViewShellId(-1);
699         if (m_pDocShell)
700         {
701             if (const SwView* pView = m_pDocShell->GetView())
702                 nViewShellId = pView->GetViewShellId();
703         }
704         EnterListAction(comment, rcomment, static_cast<sal_uInt16>(nId), nViewShellId);
705     }
706 
707     SwPaM* pTmp = rContext.m_pCurrentPaM;
708     for(SwPaM& rPaM : rContext.GetRepeatPaM().GetRingContainer())
709     {    // iterate over ring
710         rContext.m_pCurrentPaM = &rPaM;
711         if (DoesUndo() && & rPaM != pTmp)
712         {
713             m_isAddWithIgnoreRepeat = true;
714         }
715         for (sal_uInt16 nRptCnt = nRepeatCount; nRptCnt > 0; --nRptCnt)
716         {
717             pRepeatAction->Repeat(rContext);
718         }
719         if (DoesUndo() && & rPaM != pTmp)
720         {
721             m_isAddWithIgnoreRepeat = false;
722         }
723         rContext.m_bDeleteRepeated = false; // reset for next PaM
724     }
725     rContext.m_pCurrentPaM = pTmp;
726 
727     if (DoesUndo())
728     {
729         LeaveListAction();
730     }
731     return true;
732 }
733 
734 } // namespace sw
735 
736 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
737