[Libreoffice-commits] core.git: sw/inc sw/source

Michael Stahl mstahl at redhat.com
Mon May 8 15:23:59 UTC 2017


 sw/inc/undobj.hxx                  |    2 ++
 sw/source/core/inc/UndoManager.hxx |    2 ++
 sw/source/core/undo/docundo.cxx    |   13 +++++++++++++
 sw/source/core/undo/undobj.cxx     |   12 +++++++++---
 4 files changed, 26 insertions(+), 3 deletions(-)

New commits:
commit 13d7778fc10638a145af7fb077688e032bd16f81
Author: Michael Stahl <mstahl at redhat.com>
Date:   Mon May 8 16:56:34 2017 +0200

    tdf#70960 sw::UndoManager: fix exponential Repeat proliferation
    
    In case of a multi-selection, the usual pattern is that the EditShell
    contains a loop around GetCursor()->GetRingContainer() that creates N
    "groups" of undo-actions, all of which are inside a SfxListUndoAction.
    
    If that is then repeated again with a multi-selection, the result will
    be N*N actions, etc.  This is usually not an issue because most
    repeatable actions are idempotent - except for the resource usage.
    
    Prevent the 2nd step of the proliferation by introducing a flag in
    SwUndo to do nothing in SwUndo::Repeat() that is set for all but the
    1st cursor of the multi-selection in sw::UndoManager::Repeat().
    
    (presumably regression from CWS undoapi)
    
    Change-Id: I095258f6d57af6bcaa8a39747632422e8311e694

diff --git a/sw/inc/undobj.hxx b/sw/inc/undobj.hxx
index 91185bbcded0..bfd7cf804051 100644
--- a/sw/inc/undobj.hxx
+++ b/sw/inc/undobj.hxx
@@ -53,6 +53,7 @@ class SwUndo
     SwUndoId const m_nId;
     RedlineFlags   nOrigRedlineFlags;
     ViewShellId    m_nViewShellId;
+    bool m_isRepeatIgnored; /// for multi-selection, only repeat 1st selection
 
 protected:
     bool bCacheComment;
@@ -124,6 +125,7 @@ public:
     static bool FillSaveDataForFormat( const SwPaM& , SwRedlineSaveDatas& );
     static void SetSaveData( SwDoc& rDoc, SwRedlineSaveDatas& rSData );
     static bool HasHiddenRedlines( const SwRedlineSaveDatas& rSData );
+    void IgnoreRepeat() { m_isRepeatIgnored = true; }
 };
 
 enum class DelContentType : sal_uInt16
diff --git a/sw/source/core/inc/UndoManager.hxx b/sw/source/core/inc/UndoManager.hxx
index ede9604059ad..6064757c4bc0 100644
--- a/sw/source/core/inc/UndoManager.hxx
+++ b/sw/source/core/inc/UndoManager.hxx
@@ -107,6 +107,8 @@ private:
     /// If true, then repair mode is enabled.
     bool m_bRepair;
     bool m_bLockUndoNoModifiedPosition : 1;
+    /// set the IgnoreRepeat flag on every added action
+    bool m_isAddWithIgnoreRepeat;
     /// position in Undo-Array at which Doc was saved (and is not modified)
     UndoStackMark m_UndoSaveMark;
     SwDocShell* m_pDocShell;
diff --git a/sw/source/core/undo/docundo.cxx b/sw/source/core/undo/docundo.cxx
index 833044fb3f49..42417c94af38 100644
--- a/sw/source/core/undo/docundo.cxx
+++ b/sw/source/core/undo/docundo.cxx
@@ -61,6 +61,7 @@ UndoManager::UndoManager(std::shared_ptr<SwNodes> const & xUndoNodes,
     ,   m_bDrawUndo(true)
     ,   m_bRepair(false)
     ,   m_bLockUndoNoModifiedPosition(false)
+    ,   m_isAddWithIgnoreRepeat(false)
     ,   m_UndoSaveMark(MARK_INVALID)
     ,   m_pDocShell(nullptr)
     ,   m_pView(nullptr)
@@ -520,6 +521,10 @@ void UndoManager::AddUndoAction(SfxUndoAction *pAction, bool bTryMerge)
         {
             pUndo->SetRedlineFlags( m_rRedlineAccess.GetRedlineFlags() );
         }
+        if (m_isAddWithIgnoreRepeat)
+        {
+            pUndo->IgnoreRepeat();
+        }
     }
     SdrUndoManager::AddUndoAction(pAction, bTryMerge);
     // if the undo nodes array is too large, delete some actions
@@ -685,10 +690,18 @@ bool UndoManager::Repeat(::sw::RepeatContext & rContext,
     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;
diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx
index 96c8969dee6d..2ad6df159422 100644
--- a/sw/source/core/undo/undobj.cxx
+++ b/sw/source/core/undo/undobj.cxx
@@ -158,9 +158,10 @@ void SwUndo::RemoveIdxRel( sal_uLong nIdx, const SwPosition& rPos )
 }
 
 SwUndo::SwUndo(SwUndoId const nId, const SwDoc* pDoc)
-    : m_nId(nId), nOrigRedlineFlags(RedlineFlags::NONE),
-      m_nViewShellId(CreateViewShellId(pDoc)),
-      bCacheComment(true), pComment(nullptr)
+    : m_nId(nId), nOrigRedlineFlags(RedlineFlags::NONE)
+    , m_nViewShellId(CreateViewShellId(pDoc))
+    , m_isRepeatIgnored(false)
+    , bCacheComment(true), pComment(nullptr)
 {
 }
 
@@ -240,6 +241,10 @@ void SwUndo::RedoWithContext(SfxUndoContext & rContext)
 
 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);
@@ -250,6 +255,7 @@ 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);
 }
 


More information about the Libreoffice-commits mailing list