[Libreoffice-commits] core.git: Branch 'private/mst/sw_redlinehide' - 104 commits - sw/inc sw/source

Michael Stahl Michael.Stahl at cib.de
Fri Jun 8 11:08:05 UTC 2018


Rebased ref, commits from common ancestor:
commit fea957954c3481239e277f463438d74599573bcb
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Mon May 7 17:44:44 2018 +0200

    hack in Show - not sure if...
    
    Change-Id: I6ca49cfe9c61b3562e87ca994b284e056c2bab54

diff --git a/sw/source/core/doc/docredln.cxx b/sw/source/core/doc/docredln.cxx
index a7a56c6f81aa..ad1a5925d644 100644
--- a/sw/source/core/doc/docredln.cxx
+++ b/sw/source/core/doc/docredln.cxx
@@ -60,6 +60,7 @@
 #include <txtfld.hxx>
 
 #include <flowfrm.hxx>
+#include <txtfrm.hxx>
 
 using namespace com::sun::star;
 
@@ -1672,6 +1673,20 @@ void SwRangeRedline::MoveFromSection(size_t nMyPos)
             *pItem = *Start();
         for( auto& pItem : aBehindArr )
             *pItem = *End();
+
+        // sw_redlinehide: assume that Show will only be called by filters;
+        // when it is called, ensure that no MergedPara instance survives
+        for (SwNodeIndex node = Start()->nNode; node.GetIndex() <= End()->nNode.GetIndex(); ++node)
+        {
+            if (SwTextNode const*const pNode = node.GetNode().GetTextNode())
+            {
+                SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
+                for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+                {
+                    pFrame->SetMergedPara(nullptr);
+                }
+            }
+        }
     }
     else
         InvalidateRange();
commit 458f5e849011bbb7c507607f32612493191c634f
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Thu May 17 13:00:33 2018 +0200

    sw_redlinehide: let the Show menu item toggle new mode
    
    Change-Id: I1201e84b231be17e6a5ec2294acfb1e3111c9ada

diff --git a/sw/source/uibase/uiview/view2.cxx b/sw/source/uibase/uiview/view2.cxx
index 90ca68a0f404..1d57aefdd498 100644
--- a/sw/source/uibase/uiview/view2.cxx
+++ b/sw/source/uibase/uiview/view2.cxx
@@ -140,6 +140,7 @@
 #include <vcl/settings.hxx>
 #include <i18nutil/searchopt.hxx>
 #include <paratr.hxx>
+#include <rootfrm.hxx>
 
 #include <memory>
 
@@ -639,7 +640,15 @@ void SwView::Execute(SfxRequest &rReq)
                 if( static_cast<const SfxBoolItem*>(pItem)->GetValue() )
                     nMode |= RedlineFlags::ShowDelete;
 
-                m_pWrtShell->SetRedlineFlagsAndCheckInsMode( nMode );
+                if (getenv("SW_REDLINEHIDE")) // TODO...
+                {
+                    m_pWrtShell->GetLayout()->SetHideRedlines(
+                        !static_cast<const SfxBoolItem*>(pItem)->GetValue());
+                    if (m_pWrtShell->IsRedlineOn())
+                        m_pWrtShell->SetInsMode();
+                }
+                else
+                    m_pWrtShell->SetRedlineFlagsAndCheckInsMode( nMode );
             }
             break;
         case FN_MAILMERGE_SENDMAIL_CHILDWINDOW:
commit a488f8e48f5e0fa3a9e6506aa95944f2bb122258
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Thu May 17 14:16:27 2018 +0200

    sw_redlinehide: very incomplete impl. of SwRootFrame::SetHideRedlines()
    
    Change-Id: Icff1b1aac20a0a6d3f957e7025f6a08a6d0edbee

diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx
index a25bb8d7bb9f..cdce7870a536 100644
--- a/sw/source/core/layout/wsfrm.cxx
+++ b/sw/source/core/layout/wsfrm.cxx
@@ -4171,6 +4171,53 @@ void SwRootFrame::SetHideRedlines(bool const bHideRedlines)
         return;
     }
     mbHideRedlines = bHideRedlines;
+    SwNodes const& rNodes(GetFormat()->GetDoc()->GetNodes());
+    // Hide->Show: clear MergedPara, create frames
+    // Show->Hide: call CheckParaRedlineMerge, delete frames
+    // TODO how to traverse
+    // * via layout
+    //      - but that won't find nodes that don't have frames in ->Show case
+    // * via nodes
+    //      - what about special sections before content? flys? footnotes?
+    //        is order of these predictable? flys not anchored in content?
+    // * ideally should call something existing that tries to create everything?
+    //      - is that done automatically somewhere already?
+    // * other direction ->Hide - delete frames!
+    // in-order traversal should init flags in nodes *before* the nodes are found
+    for (sal_uLong i = 0; i < rNodes.Count(); ++i)
+    {
+        SwNode *const pNode(rNodes[i]);
+        if (pNode->IsTextNode())
+        {
+            SwTextNode & rTextNode(*pNode->GetTextNode());
+            SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rTextNode);
+            std::vector<SwTextFrame*> frames;
+            for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+            {
+                if (pFrame->getRootFrame() == this)
+                {
+                    frames.push_back(pFrame);
+                }
+            }
+            // this messes with pRegisteredIn so do it outside SwIterator
+            for (SwTextFrame * pFrame : frames)
+            {
+                if (mbHideRedlines && pNode->IsCreateFrameWhenHidingRedlines())
+                {
+                    pFrame->SetMergedPara(CheckParaRedlineMerge(*pFrame, rTextNode));
+                }
+                else
+                {
+                    if (pFrame->GetMergedPara())
+                    {
+                        pFrame->SetMergedPara(nullptr);
+                        rTextNode.DelFrames(); // FIXME only those in this layout?
+                    }
+                }
+            }
+        }
+    }
+    InvalidateAllContent(SwInvalidateFlags::Size); // ??? TODO what to invalidate?
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 6728f4d23b94c2d9bbcf14b1d9e766c8e5c4ce93
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Thu Jun 7 19:36:06 2018 +0200

    sw_redlinehide: trivial conversions in lcl_DrawLineForWrongListData
    
    Change-Id: I7425bac439df7518b7fe1b62cd6f3fd7a9f88fec

diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx
index ed1b86f36218..a0a38aa94364 100644
--- a/sw/source/core/txtnode/fntcache.cxx
+++ b/sw/source/core/txtnode/fntcache.cxx
@@ -639,7 +639,7 @@ static bool lcl_IsFullstopCentered( const vcl::RenderContext& rOut )
 /* This helper structure (SwForbidden) contains the already marked parts of the string
     to avoid double lines (e.g grammar + spell check error) */
 
-typedef std::vector< std::pair< sal_Int32, sal_Int32 > > SwForbidden;
+typedef std::vector<std::pair<TextFrameIndex, TextFrameIndex>> SwForbidden;
 
 static void lcl_DrawLineForWrongListData(
     SwForbidden &rForbidden,
@@ -679,23 +679,20 @@ static void lcl_DrawLineForWrongListData(
     {
         nStart -= rInf.GetIdx();
 
-        const sal_Int32 nEnd = nStart + nWrLen;
-        sal_Int32 nNext = nStart;
+        const TextFrameIndex nEnd = nStart + nWrLen;
+        TextFrameIndex nNext = nStart;
         while( nNext < nEnd )
         {
             while( pIter != rForbidden.end() && pIter->second <= nNext )
                 ++pIter;
 
-            const sal_Int32 nNextStart = nNext;
-            sal_Int32 nNextEnd = nEnd;
+            const TextFrameIndex nNextStart = nNext;
+            TextFrameIndex nNextEnd = nEnd;
 
             if( pIter == rForbidden.end() || nNextEnd <= pIter->first )
             {
                 // No overlapping mark up found
-                std::pair< sal_Int32, sal_Int32 > aNew;
-                aNew.first = nNextStart;
-                aNew.second = nNextEnd;
-                rForbidden.insert( pIter, aNew );
+                rForbidden.insert(pIter, std::make_pair(nNextStart, nNextEnd));
                 pIter = rForbidden.begin();
                 nNext = nEnd;
             }
commit 53fcfe5f97036eae1599ca64bdb55a556c47947b
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Thu Jun 7 17:37:00 2018 +0200

    sw_redlinehide: use WrongListIterator in text formatting code
    
    Change-Id: Ie13119d456fa6fe3919871cc541a9ad927b5982e

diff --git a/sw/source/core/inc/drawfont.hxx b/sw/source/core/inc/drawfont.hxx
index f484e56546c9..8cfa4b720082 100644
--- a/sw/source/core/inc/drawfont.hxx
+++ b/sw/source/core/inc/drawfont.hxx
@@ -30,7 +30,7 @@ class SwTextFrame;
 class SwViewShell;
 class SwScriptInfo;
 class Point;
-class SwWrongList;
+namespace sw { class WrongListIterator; }
 class Size;
 class SwFont;
 namespace vcl {
@@ -50,9 +50,9 @@ class SW_DLLPUBLIC SwDrawTextInfo
     Point m_aPos;
     vcl::TextLayoutCache const* m_pCachedVclData;
     OUString m_aText;
-    const SwWrongList* m_pWrong;
-    const SwWrongList* m_pGrammarCheck;
-    const SwWrongList* m_pSmartTags;
+    sw::WrongListIterator* m_pWrong;
+    sw::WrongListIterator* m_pGrammarCheck;
+    sw::WrongListIterator* m_pSmartTags;
     Size m_aSize;
     SwFont *m_pFnt;
     SwUnderlineFont* m_pUnderFnt;
@@ -220,7 +220,7 @@ public:
         return m_aText;
     }
 
-    const SwWrongList* GetWrong() const
+    sw::WrongListIterator* GetWrong() const
     {
 #ifdef DBG_UTIL
         OSL_ENSURE( m_bWrong, "DrawTextInfo: Undefined WrongList" );
@@ -228,7 +228,7 @@ public:
         return m_pWrong;
     }
 
-    const SwWrongList* GetGrammarCheck() const
+    sw::WrongListIterator* GetGrammarCheck() const
     {
 #ifdef DBG_UTIL
         OSL_ENSURE( m_bGrammarCheck, "DrawTextInfo: Undefined GrammarCheck List" );
@@ -236,7 +236,7 @@ public:
         return m_pGrammarCheck;
     }
 
-    const SwWrongList* GetSmartTags() const
+    sw::WrongListIterator* GetSmartTags() const
     {
         return m_pSmartTags;
     }
@@ -418,7 +418,7 @@ public:
         m_pCachedVclData = nullptr; // would any case benefit from save/restore?
     }
 
-    void SetWrong( const SwWrongList* pNew )
+    void SetWrong(sw::WrongListIterator *const pNew)
     {
         m_pWrong = pNew;
 #ifdef DBG_UTIL
@@ -426,7 +426,7 @@ public:
 #endif
     }
 
-    void SetGrammarCheck( const SwWrongList* pNew )
+    void SetGrammarCheck(sw::WrongListIterator *const pNew)
     {
         m_pGrammarCheck = pNew;
 #ifdef DBG_UTIL
@@ -434,7 +434,7 @@ public:
 #endif
     }
 
-    void SetSmartTags( const SwWrongList* pNew )
+    void SetSmartTags(sw::WrongListIterator *const pNew)
     {
         m_pSmartTags = pNew;
     }
diff --git a/sw/source/core/text/frmpaint.cxx b/sw/source/core/text/frmpaint.cxx
index 217615337e37..5cbc6dbd38c5 100644
--- a/sw/source/core/text/frmpaint.cxx
+++ b/sw/source/core/text/frmpaint.cxx
@@ -45,6 +45,7 @@
 #include <tabfrm.hxx>
 #include <numrule.hxx>
 #include <SwGrammarMarkUp.hxx>
+#include <wrong.hxx>
 
 #include <EnhancedPDFExportHelper.hxx>
 
@@ -651,9 +652,21 @@ void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const&
             SwitchRTLtoLTR( const_cast<SwRect&>(rRect) );
 
         SwTextPaintInfo aInf( const_cast<SwTextFrame*>(this), rRect );
-        aInf.SetWrongList( const_cast<SwTextNode*>(GetTextNode())->GetWrong() );
-        aInf.SetGrammarCheckList( const_cast<SwTextNode*>(GetTextNode())->GetGrammarCheck() );
-        aInf.SetSmartTags( const_cast<SwTextNode*>(GetTextNode())->GetSmartTags() );
+        sw::WrongListIterator iterWrong(*this, &SwTextNode::GetWrong);
+        sw::WrongListIterator iterGrammar(*this, &SwTextNode::GetGrammarCheck);
+        sw::WrongListIterator iterSmartTags(*this, &SwTextNode::GetSmartTags);
+        if (iterWrong.LooksUseful())
+        {
+            aInf.SetWrongList( &iterWrong );
+        }
+        if (iterGrammar.LooksUseful())
+        {
+            aInf.SetGrammarCheckList( &iterGrammar );
+        }
+        if (iterSmartTags.LooksUseful())
+        {
+            aInf.SetSmartTags( &iterSmartTags );
+        }
         aInf.GetTextFly().SetTopRule();
 
         SwTextPainter  aLine( const_cast<SwTextFrame*>(this), &aInf );
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index a0e7c1c06131..9a29d58d0b19 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -487,18 +487,18 @@ void SwTextPaintInfo::CtorInitTextPaintInfo( OutputDevice* pRenderContext, SwTex
     aPaintRect = rPaint;
     nSpaceIdx = 0;
     pSpaceAdd = nullptr;
-    pWrongList = nullptr;
-    pGrammarCheckList = nullptr;
-    pSmartTags = nullptr;
+    m_pWrongList = nullptr;
+    m_pGrammarCheckList = nullptr;
+    m_pSmartTags = nullptr;
     pBrushItem = nullptr;
 }
 
 SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf, const OUString* pText )
-    : SwTextSizeInfo( rInf, pText ),
-      pWrongList( rInf.GetpWrongList() ),
-      pGrammarCheckList( rInf.GetGrammarCheckList() ),
-      pSmartTags( rInf.GetSmartTags() ),
-      pSpaceAdd( rInf.GetpSpaceAdd() ),
+    : SwTextSizeInfo( rInf, pText )
+    , m_pWrongList( rInf.GetpWrongList() )
+    , m_pGrammarCheckList( rInf.GetGrammarCheckList() )
+    , m_pSmartTags( rInf.GetSmartTags() )
+    , pSpaceAdd( rInf.GetpSpaceAdd() ),
       pBrushItem( rInf.GetBrushItem() ),
       aTextFly( rInf.GetTextFly() ),
       aPos( rInf.GetPos() ),
@@ -507,11 +507,11 @@ SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf, const OUString* p
 { }
 
 SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf )
-    : SwTextSizeInfo( rInf ),
-      pWrongList( rInf.GetpWrongList() ),
-      pGrammarCheckList( rInf.GetGrammarCheckList() ),
-      pSmartTags( rInf.GetSmartTags() ),
-      pSpaceAdd( rInf.GetpSpaceAdd() ),
+    : SwTextSizeInfo( rInf )
+    , m_pWrongList( rInf.GetpWrongList() )
+    , m_pGrammarCheckList( rInf.GetGrammarCheckList() )
+    , m_pSmartTags( rInf.GetSmartTags() )
+    , pSpaceAdd( rInf.GetpSpaceAdd() ),
       pBrushItem( rInf.GetBrushItem() ),
       aTextFly( rInf.GetTextFly() ),
       aPos( rInf.GetPos() ),
@@ -693,9 +693,9 @@ void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPo
         aDrawInf.SetSize( aSize );
         aDrawInf.SetAscent( rPor.GetAscent() );
         aDrawInf.SetKern( bKern ? rPor.Width() : 0 );
-        aDrawInf.SetWrong( bTmpWrong ? pWrongList : nullptr );
-        aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? pGrammarCheckList : nullptr );
-        aDrawInf.SetSmartTags( bTmpSmart ? pSmartTags : nullptr );
+        aDrawInf.SetWrong( bTmpWrong ? m_pWrongList : nullptr );
+        aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? m_pGrammarCheckList : nullptr );
+        aDrawInf.SetSmartTags( bTmpSmart ? m_pSmartTags : nullptr );
         GetTextFly().DrawTextOpaque( aDrawInf );
     }
     else
@@ -705,9 +705,9 @@ void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPo
             m_pFnt->DrawStretchText_( aDrawInf );
         else
         {
-            aDrawInf.SetWrong( bTmpWrong ? pWrongList : nullptr );
-            aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? pGrammarCheckList : nullptr );
-            aDrawInf.SetSmartTags( bTmpSmart ? pSmartTags : nullptr );
+            aDrawInf.SetWrong( bTmpWrong ? m_pWrongList : nullptr );
+            aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? m_pGrammarCheckList : nullptr );
+            aDrawInf.SetSmartTags( bTmpSmart ? m_pSmartTags : nullptr );
             m_pFnt->DrawText_( aDrawInf );
         }
     }
@@ -1718,9 +1718,8 @@ SwTextSlot::SwTextSlot(
     bool bExgLists,
     OUString const & rCh )
     : pOldText(nullptr)
-    , pOldSmartTagList(nullptr)
-    , pOldGrammarCheckList(nullptr)
-    , pTempList(nullptr)
+    , m_pOldSmartTagList(nullptr)
+    , m_pOldGrammarCheckList(nullptr)
     , nIdx(0)
     , nLen(0)
     , pInf(nullptr)
@@ -1751,37 +1750,63 @@ SwTextSlot::SwTextSlot(
         // ST2
         if ( bExgLists )
         {
-            pOldSmartTagList = static_cast<SwTextPaintInfo*>(pInf)->GetSmartTags();
-            if ( pOldSmartTagList )
+            m_pOldSmartTagList = static_cast<SwTextPaintInfo*>(pInf)->GetSmartTags();
+            if (m_pOldSmartTagList)
             {
-                const sal_uInt16 nPos = pOldSmartTagList->GetWrongPos(nIdx);
-                const sal_Int32 nListPos = pOldSmartTagList->Pos(nPos);
-                if( nListPos == nIdx )
-                    static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags( pOldSmartTagList->SubList( nPos ) );
-                else if( !pTempList && nPos < pOldSmartTagList->Count() && nListPos < nIdx && !aText.isEmpty() )
+                std::pair<SwTextNode const*, sal_Int32> pos(pNew->GetTextFrame()->MapViewToModel(nIdx));
+                SwWrongList const*const pSmartTags(pos.first->GetSmartTags());
+                assert(m_pOldSmartTagList->MergedOrSame(pSmartTags));
+                if (pSmartTags)
                 {
-                    pTempList = new SwWrongList( WRONGLIST_SMARTTAG );
-                    pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
-                    static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags( pTempList );
+                    const sal_uInt16 nPos = pSmartTags->GetWrongPos(pos.second);
+                    const sal_Int32 nListPos = pSmartTags->Pos(nPos);
+                    if (nListPos == pos.second)
+                    {
+                        m_pTempIter.reset(new sw::WrongListIterator(*pSmartTags->SubList(nPos)));
+                        static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pTempIter.get());
+                    }
+                    else if (!m_pTempList && nPos < pSmartTags->Count()
+                                && nListPos < pos.second && !aText.isEmpty())
+                    {
+                        m_pTempList.reset(new SwWrongList( WRONGLIST_SMARTTAG ));
+                        m_pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
+                        m_pTempIter.reset(new sw::WrongListIterator(*m_pTempList));
+                        static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pTempIter.get());
+                    }
+                    else
+                        static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(nullptr);
                 }
                 else
-                    static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags( nullptr);
+                    static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(nullptr);
             }
-            pOldGrammarCheckList = static_cast<SwTextPaintInfo*>(pInf)->GetGrammarCheckList();
-            if ( pOldGrammarCheckList )
+            m_pOldGrammarCheckList = static_cast<SwTextPaintInfo*>(pInf)->GetGrammarCheckList();
+            if (m_pOldGrammarCheckList)
             {
-                const sal_uInt16 nPos = pOldGrammarCheckList->GetWrongPos(nIdx);
-                const sal_Int32 nListPos = pOldGrammarCheckList->Pos(nPos);
-                if( nListPos == nIdx )
-                    static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList( pOldGrammarCheckList->SubList( nPos ) );
-                else if( !pTempList && nPos < pOldGrammarCheckList->Count() && nListPos < nIdx && !aText.isEmpty() )
+                std::pair<SwTextNode const*, sal_Int32> pos(pNew->GetTextFrame()->MapViewToModel(nIdx));
+                SwWrongList const*const pGrammar(pos.first->GetGrammarCheck());
+                assert(m_pOldGrammarCheckList->MergedOrSame(pGrammar));
+                if (pGrammar)
                 {
-                    pTempList = new SwWrongList( WRONGLIST_GRAMMAR );
-                    pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
-                    static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList( pTempList );
+                    const sal_uInt16 nPos = pGrammar->GetWrongPos(pos.second);
+                    const sal_Int32 nListPos = pGrammar->Pos(nPos);
+                    if (nListPos == pos.second)
+                    {
+                        m_pTempIter.reset(new sw::WrongListIterator(*pGrammar->SubList(nPos)));
+                        static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pTempIter.get());
+                    }
+                    else if (!m_pTempList && nPos < pGrammar->Count()
+                                && nListPos < pos.second && !aText.isEmpty())
+                    {
+                        m_pTempList.reset(new SwWrongList( WRONGLIST_GRAMMAR ));
+                        m_pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
+                        m_pTempIter.reset(new sw::WrongListIterator(*m_pTempList));
+                        static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pTempIter.get());
+                    }
+                    else
+                        static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(nullptr);
                 }
                 else
-                    static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList( nullptr);
+                    static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(nullptr);
             }
         }
     }
@@ -1798,11 +1823,10 @@ SwTextSlot::~SwTextSlot()
 
         // ST2
         // Restore old smart tag list
-        if ( pOldSmartTagList )
-            static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags( pOldSmartTagList );
-        if ( pOldGrammarCheckList )
-            static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList( pOldGrammarCheckList );
-        delete pTempList;
+        if (m_pOldSmartTagList)
+            static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pOldSmartTagList);
+        if (m_pOldGrammarCheckList)
+            static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pOldGrammarCheckList);
     }
 }
 
diff --git a/sw/source/core/text/inftxt.hxx b/sw/source/core/text/inftxt.hxx
index 40bf57ebcebb..1156e1197c14 100644
--- a/sw/source/core/text/inftxt.hxx
+++ b/sw/source/core/text/inftxt.hxx
@@ -55,7 +55,7 @@ class SwViewShell;
 class SwAttrIter;
 struct SwMultiCreator;
 class SwMultiPortion;
-class SwWrongList;
+namespace sw { class WrongListIterator; }
 
 #define ARROW_WIDTH 200
 #define DIR_LEFT2RIGHT 0
@@ -351,9 +351,9 @@ public:
 
 class SwTextPaintInfo : public SwTextSizeInfo
 {
-    const SwWrongList *pWrongList;
-    const SwWrongList *pGrammarCheckList;
-    const SwWrongList *pSmartTags;
+    sw::WrongListIterator *m_pWrongList;
+    sw::WrongListIterator *m_pGrammarCheckList;
+    sw::WrongListIterator *m_pSmartTags;
     std::vector<long>* pSpaceAdd;
     const SvxBrushItem *pBrushItem; // For the background
     SwTextFly    aTextFly;    // Calculate the FlyFrame
@@ -371,9 +371,9 @@ class SwTextPaintInfo : public SwTextSizeInfo
 
 protected:
     SwTextPaintInfo()
-        : pWrongList(nullptr)
-        , pGrammarCheckList(nullptr)
-        , pSmartTags(nullptr)
+        : m_pWrongList(nullptr)
+        , m_pGrammarCheckList(nullptr)
+        , m_pSmartTags(nullptr)
         , pSpaceAdd(nullptr)
         , pBrushItem(nullptr)
         , nSpaceIdx(0)
@@ -458,14 +458,14 @@ public:
     void SetpSpaceAdd( std::vector<long>* pNew ){ pSpaceAdd = pNew; }
     std::vector<long>* GetpSpaceAdd() const { return pSpaceAdd; }
 
-    void SetWrongList( const SwWrongList *pNew ){ pWrongList = pNew; }
-    const SwWrongList* GetpWrongList() const { return pWrongList; }
+    void SetWrongList(sw::WrongListIterator *const pNew) { m_pWrongList = pNew; }
+    sw::WrongListIterator* GetpWrongList() const { return m_pWrongList; }
 
-    void SetGrammarCheckList( const SwWrongList *pNew ){ pGrammarCheckList = pNew; }
-    const SwWrongList* GetGrammarCheckList() const { return pGrammarCheckList; }
+    void SetGrammarCheckList(sw::WrongListIterator *const pNew) { m_pGrammarCheckList = pNew; }
+    sw::WrongListIterator* GetGrammarCheckList() const { return m_pGrammarCheckList; }
 
-    void SetSmartTags( const SwWrongList *pNew ){ pSmartTags = pNew; }
-    const SwWrongList* GetSmartTags() const { return pSmartTags; }
+    void SetSmartTags(sw::WrongListIterator *const pNew) { m_pSmartTags = pNew; }
+    sw::WrongListIterator* GetSmartTags() const { return m_pSmartTags; }
 };
 
 class SwTextFormatInfo : public SwTextPaintInfo
@@ -683,9 +683,10 @@ class SwTextSlot final
     OUString aText;
     std::shared_ptr<vcl::TextLayoutCache> m_pOldCachedVclData;
     const OUString *pOldText;
-    const SwWrongList* pOldSmartTagList;
-    const SwWrongList* pOldGrammarCheckList;
-    SwWrongList* pTempList;
+    sw::WrongListIterator * m_pOldSmartTagList;
+    sw::WrongListIterator * m_pOldGrammarCheckList;
+    std::unique_ptr<SwWrongList> m_pTempList;
+    std::unique_ptr<sw::WrongListIterator> m_pTempIter;
     TextFrameIndex nIdx;
     TextFrameIndex nLen;
     bool bOn;
diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx
index 8256d458357e..69593f8d157c 100644
--- a/sw/source/core/text/portxt.cxx
+++ b/sw/source/core/text/portxt.cxx
@@ -550,9 +550,9 @@ void SwTextPortion::Paint( const SwTextPaintInfo &rInf ) const
         if( rInf.OnWin() && pPortion && !pPortion->Width() )
             pPortion->PrePaint( rInf, this );
 
-        const SwWrongList *pWrongList = rInf.GetpWrongList();
-        const SwWrongList *pGrammarCheckList = rInf.GetGrammarCheckList();
-        const SwWrongList *pSmarttags = rInf.GetSmartTags();
+        auto const* pWrongList = rInf.GetpWrongList();
+        auto const* pGrammarCheckList = rInf.GetGrammarCheckList();
+        auto const* pSmarttags = rInf.GetSmartTags();
 
         const bool bWrong = nullptr != pWrongList;
         const bool bGrammarCheck = nullptr != pGrammarCheckList;
diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx
index c45b14086a2a..ed1b86f36218 100644
--- a/sw/source/core/txtnode/fntcache.cxx
+++ b/sw/source/core/txtnode/fntcache.cxx
@@ -644,7 +644,7 @@ typedef std::vector< std::pair< sal_Int32, sal_Int32 > > SwForbidden;
 static void lcl_DrawLineForWrongListData(
     SwForbidden &rForbidden,
     const SwDrawTextInfo    &rInf,
-    const SwWrongList       *pWList,
+    sw::WrongListIterator *pWList,
     const CalcLinePosData   &rCalcLinePosData,
     const Size              &rPrtFontSize )
 {
@@ -715,10 +715,7 @@ static void lcl_DrawLineForWrongListData(
             Point aEnd;
             lcl_calcLinePos( rCalcLinePosData, aStart, aEnd, nNextStart, nNextEnd - nNextStart );
 
-            const sal_uInt16 wrongPos = pWList->GetWrongPos(nNextStart + rInf.GetIdx());
-
-            const SwWrongArea* wrongArea = pWList->GetElement(wrongPos);
-
+            SwWrongArea const*const wrongArea = pWList->GetWrongElement(nNextStart + rInf.GetIdx());
             if (wrongArea != nullptr)
             {
                 if (WRONGAREA_DASHED == wrongArea->mLineType)
commit 3208c1ec69326f89748afd071ab606b76ff0be33
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Thu Jun 7 17:37:52 2018 +0200

    sw_redlinehide: Add sw::WrongListIterator
    
    The simplest way to deal with the problem that wrong-lists are on the
    SwTextNode is to just take them as-is and iterate over them in a merged
    SwTextFrame.  This may give results that users may disapprove of, but
    i'm not sure how likely it is in practice to e.g. start a delete redline
    in the middle of a word and end it in another paragraph.
    
    Perhaps it would be better to move the SwWrongLists to the SwTextFrame,
    but that is probably more work.
    
    Also add missing SwTextNode function overloads for "const", so we don't
    need mutable SwTextNodes.
    
    Change-Id: I9742e3793abe090cf58ad3f27b51e89be86a1964

diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx
index 894f8c078635..d1a8801eb9c7 100644
--- a/sw/inc/ndtxt.hxx
+++ b/sw/inc/ndtxt.hxx
@@ -194,8 +194,11 @@ public:
     const SwWrongList* GetWrong() const;
     void SetGrammarCheck( SwGrammarMarkUp* pNew, bool bDelete = true );
     SwGrammarMarkUp* GetGrammarCheck();
+    // return SwWrongList because *function pointer* return values aren't covariant
+    SwWrongList const* GetGrammarCheck() const;
     void SetSmartTags( SwWrongList* pNew, bool bDelete = true );
     SwWrongList* GetSmartTags();
+    SwWrongList const* GetSmartTags() const;
     void TryCharSetExpandToNum(const SfxItemSet& pCharSet);
 
     /// End: Data collected during idle time
diff --git a/sw/source/core/inc/wrong.hxx b/sw/source/core/inc/wrong.hxx
index 0aa575434483..831c5bb7f99a 100644
--- a/sw/source/core/inc/wrong.hxx
+++ b/sw/source/core/inc/wrong.hxx
@@ -32,6 +32,7 @@
 #include <tools/color.hxx>
 #include <swtypes.hxx>
 #include <viewopt.hxx>
+#include "TextFrameIndex.hxx"
 
 class SwWrongList;
 
@@ -331,6 +332,41 @@ public:
     bool LookForEntry( sal_Int32 nBegin, sal_Int32 nEnd );
 };
 
+class SwTextNode;
+class SwTextFrame;
+
+namespace sw {
+
+struct MergedPara;
+
+class WrongListIterator
+{
+private:
+    SwWrongList const* (SwTextNode::*const m_pGetWrongList)() const;
+    sw::MergedPara const*const m_pMergedPara;
+    size_t m_CurrentExtent;
+    TextFrameIndex m_CurrentIndex;
+    TextFrameIndex m_CurrentNodeIndex;
+    SwWrongList const*const m_pWrongList;
+
+public:
+    /// for the text frame
+    WrongListIterator(SwTextFrame const& rFrame,
+        SwWrongList const* (SwTextNode::*pGetWrongList)() const);
+    /// for SwTextSlot
+    WrongListIterator(SwWrongList const& rWrongList);
+
+    bool Check(TextFrameIndex &rStart, TextFrameIndex &rLen);
+    const SwWrongArea* GetWrongElement(TextFrameIndex nStart);
+
+    bool LooksUseful() { return m_pMergedPara || m_pWrongList; }
+    bool MergedOrSame(SwWrongList const*const pList) const {
+        return m_pMergedPara || m_pWrongList == pList;
+    }
+};
+
+} // namespace sw
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/wrong.cxx b/sw/source/core/text/wrong.cxx
index 20fdac821985..ceb2c8859029 100644
--- a/sw/source/core/text/wrong.cxx
+++ b/sw/source/core/text/wrong.cxx
@@ -20,6 +20,9 @@
 #include <swtypes.hxx>
 
 #include <SwGrammarMarkUp.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+
 #include <osl/diagnose.h>
 
 SwWrongArea::SwWrongArea( const OUString& rType, WrongListType listType,
@@ -659,4 +662,164 @@ void SwWrongList::Insert( const OUString& rType,
     maList.insert(aIter, SwWrongArea( rType, meType, xPropertyBag, nNewPos, nNewLen) );
 }
 
+namespace sw {
+
+WrongListIterator::WrongListIterator(SwTextFrame const& rFrame,
+        SwWrongList const* (SwTextNode::*pGetWrongList)() const)
+    : m_pGetWrongList(pGetWrongList)
+    , m_pMergedPara(rFrame.GetMergedPara())
+    , m_CurrentExtent(0)
+    , m_CurrentIndex(0)
+    , m_CurrentNodeIndex(0)
+    , m_pWrongList(m_pMergedPara
+                    ? nullptr
+                    : (rFrame.GetTextNodeFirst()->*pGetWrongList)())
+{
+}
+
+WrongListIterator::WrongListIterator(SwWrongList const& rWrongList)
+    : m_pGetWrongList(nullptr)
+    , m_pMergedPara(nullptr)
+    , m_CurrentExtent(0)
+    , m_CurrentIndex(0)
+    , m_CurrentNodeIndex(0)
+    , m_pWrongList(&rWrongList)
+{
+}
+
+bool WrongListIterator::Check(TextFrameIndex & rStart, TextFrameIndex & rLen)
+{
+    if (m_pMergedPara)
+    {
+        if (rStart < m_CurrentIndex)
+        {   // rewind
+            m_CurrentExtent = 0;
+            m_CurrentIndex = TextFrameIndex(0);
+            m_CurrentNodeIndex = TextFrameIndex(0);
+        }
+        while (m_CurrentExtent < m_pMergedPara->extents.size())
+        {
+            sw::Extent const& rExtent(m_pMergedPara->extents[m_CurrentExtent]);
+            if (rStart + rLen <= m_CurrentIndex)
+            {
+                return false;
+            }
+            else if (rStart < m_CurrentIndex)
+            {
+                rLen -= (m_CurrentIndex - rStart);
+                assert(0 < sal_Int32(rLen));
+                rStart = m_CurrentIndex;
+            }
+            if (m_CurrentIndex <= rStart &&
+                rStart < m_CurrentIndex + TextFrameIndex(rExtent.nEnd - rExtent.nStart))
+            {
+                SwWrongList const*const pWrongList((rExtent.pNode->*m_pGetWrongList)());
+                // found the extent containing start - first, call Check
+                sal_Int32 nStart(rStart - (m_CurrentIndex - m_CurrentNodeIndex));
+                sal_Int32 nLen(rLen); // upper bound; check it properly later
+                if (pWrongList && pWrongList->Check(nStart, nLen))
+                {
+                    // check if there's overlap with this extent
+                    if (rExtent.nStart <= nStart && nStart < rExtent.nEnd)
+                    {
+                        // yes - now compute end position / length
+                        sal_Int32 const nEnd(nStart + nLen);
+                        rStart = m_CurrentIndex + TextFrameIndex(nStart - rExtent.nStart);
+                        if (nEnd < rExtent.nEnd)
+                        {
+                            rLen = TextFrameIndex(nEnd - nStart);
+                        }
+                        else // have to search other extents for the end...
+                        {
+                            rLen = TextFrameIndex(rExtent.nEnd - nStart);
+                            for (size_t i = m_CurrentExtent + 1;
+                                 i < m_pMergedPara->extents.size(); ++i)
+                            {
+                                sw::Extent const& rExtentEnd(m_pMergedPara->extents[i]);
+                                if (rExtentEnd.pNode != rExtent.pNode
+                                    || nEnd <= rExtentEnd.nStart)
+                                {
+                                    break;
+                                }
+                                if (nEnd <= rExtentEnd.nEnd)
+                                {
+                                    rLen += TextFrameIndex(nEnd - rExtentEnd.nStart);
+                                    break;
+                                }
+                                rLen += TextFrameIndex(rExtentEnd.nEnd - rExtentEnd.nStart);
+                            }
+                        }
+                        return true;
+                    }
+                }
+            }
+            m_CurrentIndex += TextFrameIndex(rExtent.nEnd - rExtent.nStart);
+            ++m_CurrentExtent;
+            if (m_CurrentExtent < m_pMergedPara->extents.size() &&
+                rExtent.pNode != m_pMergedPara->extents[m_CurrentExtent].pNode)
+            {
+                m_CurrentNodeIndex = m_CurrentIndex; // reset
+            }
+        }
+        return false;
+    }
+    else if (m_pWrongList)
+    {
+        sal_Int32 nStart(rStart);
+        sal_Int32 nLen(rLen);
+        bool const bRet(m_pWrongList->Check(nStart, nLen));
+        rStart = TextFrameIndex(nStart);
+        rLen = TextFrameIndex(nLen);
+        return bRet;
+    }
+    return false;
+}
+
+const SwWrongArea*
+WrongListIterator::GetWrongElement(TextFrameIndex const nStart)
+{
+    if (m_pMergedPara)
+    {
+        if (nStart < m_CurrentIndex)
+        {   // rewind
+            m_CurrentExtent = 0;
+            m_CurrentIndex = TextFrameIndex(0);
+            m_CurrentNodeIndex = TextFrameIndex(0);
+        }
+        while (m_CurrentExtent < m_pMergedPara->extents.size())
+        {
+            sw::Extent const& rExtent(m_pMergedPara->extents[m_CurrentExtent]);
+            if (m_CurrentIndex <= nStart &&
+                nStart <= m_CurrentIndex + TextFrameIndex(rExtent.nEnd - rExtent.nStart))
+            {
+                // note: the returned object isn't wrapped because fntcache.cxx
+                // does not look at its positions, only its formatting props
+                SwWrongList const*const pWrongList((rExtent.pNode->*m_pGetWrongList)());
+                if (pWrongList)
+                {
+                    sal_Int16 const nPos(pWrongList->GetWrongPos(
+                        sal_Int32(nStart - (m_CurrentIndex - m_CurrentNodeIndex))));
+                    return pWrongList->GetElement(nPos);
+                }
+            }
+            m_CurrentIndex += TextFrameIndex(rExtent.nEnd - rExtent.nStart);
+            ++m_CurrentExtent;
+            if (m_CurrentExtent < m_pMergedPara->extents.size() &&
+                rExtent.pNode != m_pMergedPara->extents[m_CurrentExtent].pNode)
+            {
+                m_CurrentNodeIndex = m_CurrentIndex; // reset
+            }
+        }
+        return nullptr;
+    }
+    else if (m_pWrongList)
+    {
+        sal_Int16 const nPos(m_pWrongList->GetWrongPos(sal_Int32(nStart)));
+        return m_pWrongList->GetElement(nPos);
+    }
+    return nullptr;
+}
+
+} // namespace sw
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx
index 1f1468b1429d..7a646fc2b8ed 100644
--- a/sw/source/core/txtnode/txtedt.cxx
+++ b/sw/source/core/txtnode/txtedt.cxx
@@ -2216,6 +2216,11 @@ SwGrammarMarkUp* SwTextNode::GetGrammarCheck()
     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck : nullptr;
 }
 
+SwWrongList const* SwTextNode::GetGrammarCheck() const
+{
+    return static_cast<SwWrongList const*>(const_cast<SwTextNode*>(this)->GetGrammarCheck());
+}
+
 void SwTextNode::SetSmartTags( SwWrongList* pNew, bool bDelete )
 {
     OSL_ENSURE( !pNew || SwSmartTagMgr::Get().IsSmartTagsEnabled(),
@@ -2236,6 +2241,11 @@ SwWrongList* SwTextNode::GetSmartTags()
     return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags : nullptr;
 }
 
+SwWrongList const* SwTextNode::GetSmartTags() const
+{
+    return const_cast<SwWrongList const*>(const_cast<SwTextNode*>(this)->GetSmartTags());
+}
+
 void SwTextNode::SetWordCountDirty( bool bNew ) const
 {
     if ( m_pParaIdleData_Impl )
commit bab5f7a7ffed6512c75ec6a0184ffc6138b15740
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Tue Jun 5 13:41:58 2018 +0200

    sw_redlinehide: send a hint to the SwTextFrame when Delete redline
    
    ... is created or updated; there is already a SwUpdateAttr
    hint being sent from SwRangeRedline::InvalidateRange(),
    but it doesn't contain any info about the redline other than the
    position; add a new hint sw::RedlineDelText and send it from there in
    addition to the existing one.
    
    Change-Id: I37150b2a5ca6a0e70ea4a5788f720f8522227631

diff --git a/sw/inc/hints.hxx b/sw/inc/hints.hxx
index 4da6408dfe99..dc31d7424756 100644
--- a/sw/inc/hints.hxx
+++ b/sw/inc/hints.hxx
@@ -93,6 +93,19 @@ public:
     SwDelText( sal_Int32 nS, sal_Int32 nL );
 };
 
+namespace sw {
+
+class RedlineDelText : public SfxHint
+{
+public:
+    sal_Int32 nStart;
+    sal_Int32 nLen;
+
+    RedlineDelText(sal_Int32 nS, sal_Int32 nL);
+};
+
+}
+
 class SwUpdateAttr : public SwMsgPoolItem
 {
 private:
diff --git a/sw/source/core/attr/hints.cxx b/sw/source/core/attr/hints.cxx
index 678db2590528..44c60ff0fd17 100644
--- a/sw/source/core/attr/hints.cxx
+++ b/sw/source/core/attr/hints.cxx
@@ -46,6 +46,15 @@ SwDelText::SwDelText( sal_Int32 nS, sal_Int32 nL )
 {
 }
 
+namespace sw {
+
+RedlineDelText::RedlineDelText(sal_Int32 const nS, sal_Int32 const nL)
+    : nStart(nS), nLen(nL)
+{
+}
+
+} // namespace sw
+
 SwUpdateAttr::SwUpdateAttr( sal_Int32 nS, sal_Int32 nE, sal_uInt16 nW )
     : SwMsgPoolItem( RES_UPDATE_ATTR ), m_nStart( nS ), m_nEnd( nE ), m_nWhichAttr( nW )
 {
diff --git a/sw/source/core/doc/docredln.cxx b/sw/source/core/doc/docredln.cxx
index c5731c36c457..a7a56c6f81aa 100644
--- a/sw/source/core/doc/docredln.cxx
+++ b/sw/source/core/doc/docredln.cxx
@@ -1269,6 +1269,15 @@ void SwRangeRedline::InvalidateRange()       // trigger the Layout
         if (pNode && pNode->IsTextNode())
         {
             SwTextNode* pNd = pNode->GetTextNode();
+
+            if (GetType() == nsRedlineType_t::REDLINE_DELETE)
+            {
+                sw::RedlineDelText const hint(
+                    n == nSttNd ? nSttCnt : 0,
+                    n == nEndNd ? nEndCnt : pNd->GetText().getLength());
+                pNd->CallSwClientNotify(hint);
+            }
+
             SwUpdateAttr aHt(
                 n == nSttNd ? nSttCnt : 0,
                 n == nEndNd ? nEndCnt : pNd->GetText().getLength(),
diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx
index fae1cb86bf09..22502f48d6df 100644
--- a/sw/source/core/text/txtfrm.cxx
+++ b/sw/source/core/text/txtfrm.cxx
@@ -1510,18 +1510,30 @@ static bool isA11yRelevantAttribute(sal_uInt16 nWhich)
 // SwContentFrame::Modify() when appropriate.
 void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
 {
-    auto const pHint(dynamic_cast<sw::LegacyModifyHint const*>(&rHint));
-    assert(pHint); // TODO this is the only type expected here for now
+    SfxPoolItem const* pOld(nullptr);
+    SfxPoolItem const* pNew(nullptr);
+    sw::RedlineDelText const* pRedlineDelText(nullptr);
 
-    SfxPoolItem const*const pOld(pHint->m_pOld);
-    SfxPoolItem const*const pNew(pHint->m_pNew);
-    SwTextNode const& rNode(static_cast<SwTextNode const&>(rModify));
+    if (auto const pHint = dynamic_cast<sw::LegacyModifyHint const*>(&rHint))
+    {
+        pOld = pHint->m_pOld;
+        pNew = pHint->m_pNew;
+    }
+    else if (auto const pHynt = dynamic_cast<sw::RedlineDelText const*>(&rHint))
+    {
+        pRedlineDelText = pHynt;
+    }
+    else
+    {
+        assert(!"unexpected hint");
+    }
 
     if (m_pMergedPara)
     {
         assert(m_pMergedPara->listener.IsListeningTo(&rModify));
     }
 
+    SwTextNode const& rNode(static_cast<SwTextNode const&>(rModify));
     const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
 
     // modifications concerning frame attributes are processed by the base class
@@ -1573,7 +1585,34 @@ void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
     bool bSetFieldsDirty = false;
     bool bRecalcFootnoteFlag = false;
 
-    switch( nWhich )
+    if (pRedlineDelText)
+    {
+        if (m_pMergedPara)
+        {
+            sal_Int32 const nNPos = pRedlineDelText->nStart;
+            sal_Int32 const nNLen = pRedlineDelText->nLen;
+            nPos = MapModelToView(&rNode, nNPos);
+            // update merged before doing anything else
+            nLen = UpdateMergedParaForDelete(*m_pMergedPara, rNode, nNPos, nNLen);
+            const sal_Int32 m = -nNLen;
+            if (nLen && IsIdxInside(nPos, nLen))
+            {
+                if (!nLen)
+                    InvalidateSize();
+                else
+                    InvalidateRange( SwCharRange(nPos, TextFrameIndex(1)), m );
+            }
+            lcl_SetWrong( *this, rNode, nNPos, m, true );
+            if (nLen)
+            {
+                lcl_SetScriptInval( *this, nPos );
+                bSetFieldsDirty = bRecalcFootnoteFlag = true;
+                if (HasFollow())
+                    lcl_ModifyOfst( this, nPos, nLen );
+            }
+        }
+    }
+    else switch (nWhich)
     {
         case RES_LINENUMBER:
         {
commit 4531b0b96db6ea5a6ccd1e1ae0a1b5742a621ded
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Fri Jun 1 22:45:55 2018 +0200

    sw_redlinehide: SwTextNode::Insert(): send SwInsText hint before
    
    ... adjusting hints, as the latter sends RES_UPDATE_ATTR hints
    and the ModelToViewPos() asserts because the mergedText is too short.
    
    Change-Id: I48bf6388ce69f3294decf96dc6dd11ba3bf4bc7b

diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index b044a7439429..a241caf00d4e 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -1993,6 +1993,12 @@ OUString SwTextNode::InsertText( const OUString & rStr, const SwIndex & rIdx,
         SetIgnoreDontExpand( bOldExpFlg );
     }
 
+    if ( HasWriterListeners() )
+    {   // send this before messing with hints, which will send RES_UPDATE_ATTR
+        SwInsText aHint( aPos, nLen );
+        NotifyClients( nullptr, &aHint );
+    }
+
     if ( HasHints() )
     {
         bool const bHadHints(!m_pSwpHints->CanBeDeleted());
@@ -2064,12 +2070,6 @@ OUString SwTextNode::InsertText( const OUString & rStr, const SwIndex & rIdx,
                 "SwTextNode::InsertText: unexpected loss of hints");
     }
 
-    if ( HasWriterListeners() )
-    {
-        SwInsText aHint( aPos, nLen );
-        NotifyClients( nullptr, &aHint );
-    }
-
     // By inserting a character, the hidden flags
     // at the TextNode can become invalid:
     SetCalcHiddenCharFlags();
commit 176962f92f99b23f46922ebaf0dfcf7162e2f1f2
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Thu Jun 7 10:27:52 2018 +0200

    sw_redlinehide: convert spelling SwTextFrame funcs called by DoIdleJob_
    
    These currently get passed the current shell cursor position, which
    seems quite pointless as the caller DoIdleJob_ can handle that case
    in one place.
    
    Instead, pass in the actual node that should be checked, since the
    SwTextFrame can have multiple nodes now.
    
    Change-Id: Id7a8a7a2a5a3eaeb700963eff5147d37b194a953

diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx
index f6fbb686903e..98a18f7ed8ad 100644
--- a/sw/source/core/inc/txtfrm.hxx
+++ b/sw/source/core/inc/txtfrm.hxx
@@ -280,14 +280,14 @@ public:
      */
     void Init();
 
-    /// Is called by FormatSpelling()
-    SwRect AutoSpell_( const SwContentNode*, sal_Int32 );
+    /// Is called by DoIdleJob_() and ExecSpellPopup()
+    SwRect AutoSpell_(SwTextNode &, sal_Int32);
 
-    /// Is called by FormatSpelling()
-    SwRect SmartTagScan();
+    /// Is called by DoIdleJob_()
+    SwRect SmartTagScan(SwTextNode &);
 
-    /// Is called by CollectAutoCmplWords()
-    void CollectAutoCmplWrds( SwContentNode const * , sal_Int32 );
+    /// Is called by DoIdleJob_()
+    void CollectAutoCmplWrds(SwTextNode &, sal_Int32);
 
     /**
      * Returns the screen position of rPos. The values are relative to the upper
diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx
index e98f19ccb771..761a85c51741 100644
--- a/sw/source/core/layout/layact.cxx
+++ b/sw/source/core/layout/layact.cxx
@@ -1849,7 +1849,7 @@ bool SwLayIdle::DoIdleJob_( const SwContentFrame *pCnt, IdleJobType eJob )
 
     SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pCnt));
     // sw_redlinehide: spell check only the nodes with visible content?
-    const SwTextNode* pTextNode = pTextFrame->GetTextNodeForParaProps();
+    SwTextNode* pTextNode = const_cast<SwTextNode*>(pTextFrame->GetTextNodeForParaProps());
 
     bool bProcess = false;
     for (size_t i = 0; pTextNode; )
@@ -1895,6 +1895,7 @@ bool SwLayIdle::DoIdleJob_( const SwContentFrame *pCnt, IdleJobType eJob )
 
     if( bProcess )
     {
+        assert(pTextNode);
         SwViewShell *pSh = pImp->GetShell();
         if( COMPLETE_STRING == nTextPos )
         {
@@ -1909,12 +1910,15 @@ bool SwLayIdle::DoIdleJob_( const SwContentFrame *pCnt, IdleJobType eJob )
                 }
             }
         }
+        sal_Int32 const nPos((pContentNode && pTextNode == pContentNode)
+                ? nTextPos
+                : COMPLETE_STRING);
 
         switch ( eJob )
         {
             case ONLINE_SPELLING :
             {
-                SwRect aRepaint( const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCnt))->AutoSpell_( pContentNode, nTextPos ) );
+                SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->AutoSpell_(*pTextNode, nPos) );
                 // PENDING should stop idle spell checking
                 bPageValid = bPageValid && (SwTextNode::WrongState::TODO != pTextNode->GetWrongDirty());
                 if ( aRepaint.HasArea() )
@@ -1924,7 +1928,7 @@ bool SwLayIdle::DoIdleJob_( const SwContentFrame *pCnt, IdleJobType eJob )
                 break;
             }
             case AUTOCOMPLETE_WORDS :
-                const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCnt))->CollectAutoCmplWrds( pContentNode, nTextPos );
+                const_cast<SwTextFrame*>(pTextFrame)->CollectAutoCmplWrds(*pTextNode, nPos);
                 // note: bPageValid remains true here even if the cursor
                 // position is skipped, so no PENDING state needed currently
                 if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
@@ -1942,7 +1946,7 @@ bool SwLayIdle::DoIdleJob_( const SwContentFrame *pCnt, IdleJobType eJob )
             case SMART_TAGS :
             {
                 try {
-                    const SwRect aRepaint( const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCnt))->SmartTagScan() );
+                    const SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->SmartTagScan(*pTextNode) );
                     bPageValid = bPageValid && !pTextNode->IsSmartTagDirty();
                     if ( aRepaint.HasArea() )
                         pImp->GetShell()->InvalidateWindows( aRepaint );
diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx
index 7523293b45ea..1f1468b1429d 100644
--- a/sw/source/core/txtnode/txtedt.cxx
+++ b/sw/source/core/txtnode/txtedt.cxx
@@ -200,14 +200,16 @@ lcl_MaskRedlinesAndHiddenText( const SwTextNode& rNode, OUStringBuffer& rText,
 /**
  * Used for spell checking. Calculates a rectangle for repaint.
  */
-static SwRect lcl_CalculateRepaintRect( SwTextFrame& rTextFrame, sal_Int32 nChgStart, sal_Int32 nChgEnd )
+static SwRect lcl_CalculateRepaintRect(
+        SwTextFrame & rTextFrame, SwTextNode & rNode,
+        sal_Int32 const nChgStart, sal_Int32 const nChgEnd)
 {
     SwRect aRect;
 
-    SwTextNode *pNode = rTextFrame.GetTextNode();
+    TextFrameIndex const iChgStart(rTextFrame.MapModelToView(&rNode, nChgStart));
+    TextFrameIndex const iChgEnd(rTextFrame.MapModelToView(&rNode, nChgEnd));
 
-    SwNodeIndex aNdIdx( *pNode );
-    SwPosition aPos( aNdIdx, SwIndex( pNode, nChgEnd ) );
+    SwPosition aPos( rNode, nChgEnd );
     SwCursorMoveState aTmpState( MV_NONE );
     aTmpState.m_b2Lines = true;
     rTextFrame.GetCharRect( aRect, aPos, &aTmpState );
@@ -217,7 +219,7 @@ static SwRect lcl_CalculateRepaintRect( SwTextFrame& rTextFrame, sal_Int32 nChgS
     const SwTextFrame *pEndFrame = &rTextFrame;
 
     while( pEndFrame->HasFollow() &&
-           nChgEnd >= pEndFrame->GetFollow()->GetOfst() )
+           iChgEnd >= pEndFrame->GetFollow()->GetOfst())
         pEndFrame = pEndFrame->GetFollow();
 
     if ( pEnd2Pos )
@@ -236,7 +238,7 @@ static SwRect lcl_CalculateRepaintRect( SwTextFrame& rTextFrame, sal_Int32 nChgS
 
     aTmpState.m_p2Lines = nullptr;
     SwRect aTmp;
-    aPos = SwPosition( aNdIdx, SwIndex( pNode, nChgStart ) );
+    aPos = SwPosition( rNode, nChgStart );
     rTextFrame.GetCharRect( aTmp, aPos, &aTmpState );
 
     // i63141: GetCharRect(..) could cause a formatting,
@@ -244,11 +246,11 @@ static SwRect lcl_CalculateRepaintRect( SwTextFrame& rTextFrame, sal_Int32 nChgS
     // => we have to reinit pStartFrame and pEndFrame after the formatting
     const SwTextFrame* pStartFrame = &rTextFrame;
     while( pStartFrame->HasFollow() &&
-           nChgStart >= pStartFrame->GetFollow()->GetOfst() )
+           iChgStart >= pStartFrame->GetFollow()->GetOfst())
         pStartFrame = pStartFrame->GetFollow();
     pEndFrame = pStartFrame;
     while( pEndFrame->HasFollow() &&
-           nChgEnd >= pEndFrame->GetFollow()->GetOfst() )
+           iChgEnd >= pEndFrame->GetFollow()->GetOfst())
         pEndFrame = pEndFrame->GetFollow();
 
     // information about start of repaint area
@@ -1266,11 +1268,12 @@ bool SwTextNode::Convert( SwConversionArgs &rArgs )
 }
 
 // Note: this is a clone of SwTextNode::Spell, so keep them in sync when fixing things!
-SwRect SwTextFrame::AutoSpell_( const SwContentNode* pActNode, sal_Int32 nActPos )
+SwRect SwTextFrame::AutoSpell_(SwTextNode & rNode, sal_Int32 nActPos)
 {
     SwRect aRect;
-    SwTextNode *pNode = GetTextNode();
-    if( pNode != pActNode || !nActPos )
+    assert(sw::FrameContainsNode(*this, rNode.GetIndex()));
+    SwTextNode *const pNode(&rNode);
+    if (!nActPos)
         nActPos = COMPLETE_STRING;
 
     SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords();
@@ -1281,7 +1284,7 @@ SwRect SwTextFrame::AutoSpell_( const SwContentNode* pActNode, sal_Int32 nActPos
     const bool bRestoreString =
         lcl_MaskRedlinesAndHiddenText(*pNode, buf, 0, pNode->GetText().getLength());
     if (bRestoreString)
-    {   // ??? UGLY: is it really necessary to modify m_Text here?
+    {   // ??? UGLY: is it really necessary to modify m_Text here? just for GetLang()?
         pNode->m_Text = buf.makeStringAndClear();
     }
 
@@ -1415,7 +1418,7 @@ SwRect SwTextFrame::AutoSpell_( const SwContentNode* pActNode, sal_Int32 nActPos
 
         if( nChgStart < nChgEnd )
         {
-            aRect = lcl_CalculateRepaintRect( *this, nChgStart, nChgEnd );
+            aRect = lcl_CalculateRepaintRect(*this, rNode, nChgStart, nChgEnd);
 
             // fdo#71558 notify misspelled word to accessibility
             SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
@@ -1451,10 +1454,12 @@ SwRect SwTextFrame::AutoSpell_( const SwContentNode* pActNode, sal_Int32 nActPos
 
     @return SwRect Repaint area
 */
-SwRect SwTextFrame::SmartTagScan()
+SwRect SwTextFrame::SmartTagScan(SwTextNode & rNode)
 {
     SwRect aRet;
-    SwTextNode *pNode = GetTextNode();
+
+    assert(sw::FrameContainsNode(*this, rNode.GetIndex()));
+    SwTextNode *const pNode = &rNode;
     const OUString& rText = pNode->GetText();
 
     // Iterate over language portions
@@ -1556,7 +1561,7 @@ SwRect SwTextFrame::SmartTagScan()
         if ( nBegin < nEnd && ( 0 != nNumberOfRemovedEntries ||
                                 0 != nNumberOfInsertedEntries ) )
         {
-            aRet = lcl_CalculateRepaintRect( *this, nBegin, nEnd );
+            aRet = lcl_CalculateRepaintRect(*this, rNode, nBegin, nEnd);
         }
     }
     else
@@ -1565,10 +1570,11 @@ SwRect SwTextFrame::SmartTagScan()
     return aRet;
 }
 
-void SwTextFrame::CollectAutoCmplWrds( SwContentNode const * pActNode, sal_Int32 nActPos )
+void SwTextFrame::CollectAutoCmplWrds(SwTextNode & rNode, sal_Int32 nActPos)
 {
-    SwTextNode *pNode = GetTextNode();
-    if( pNode != pActNode || !nActPos )
+    assert(sw::FrameContainsNode(*this, rNode.GetIndex()));
+    SwTextNode *const pNode(&rNode);
+    if (!nActPos)
         nActPos = COMPLETE_STRING;
 
     SwDoc* pDoc = pNode->GetDoc();
diff --git a/sw/source/uibase/uiview/viewling.cxx b/sw/source/uibase/uiview/viewling.cxx
index 0f646e637c5e..1e0b389aefb0 100644
--- a/sw/source/uibase/uiview/viewling.cxx
+++ b/sw/source/uibase/uiview/viewling.cxx
@@ -656,7 +656,8 @@ bool SwView::ExecSpellPopup(const Point& rPt)
                                         &rPt, &aPoint, false);
                 if (pContentFrame)
                 {
-                    SwRect aRepaint(static_cast<SwTextFrame*>(pContentFrame)->AutoSpell_(nullptr, 0));
+                    SwRect aRepaint(static_cast<SwTextFrame*>(pContentFrame)->AutoSpell_(
+                        *pCursor->GetContentNode()->GetTextNode(), 0));
                     if (aRepaint.HasArea())
                         m_pWrtShell->InvalidateWindows(aRepaint);
                 }
commit 7df664276df4c7285032dbef21358b6e82ebf58b
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Wed Jun 6 17:23:49 2018 +0200

    sw_redlinehide: SwAttrIter direct ctor gets a SwTextFrame arg
    
    ... and use it in SwTextPainter::CheckSpecialUnderline()
    
    Change-Id: I904cb955f0bc8dc1f92612b47dc129922865c198

diff --git a/sw/source/core/text/itratr.hxx b/sw/source/core/text/itratr.hxx
index 016f97c333bc..c47be02230d8 100644
--- a/sw/source/core/text/itratr.hxx
+++ b/sw/source/core/text/itratr.hxx
@@ -71,7 +71,7 @@ private:
 protected:
     void Chg( SwTextAttr const *pHt );
     void Rst( SwTextAttr const *pHt );
-    void CtorInitAttrIter(SwTextNode& rTextNode, SwScriptInfo& rScrInf, SwTextFrame * pFrame = nullptr);
+    void CtorInitAttrIter(SwTextNode& rTextNode, SwScriptInfo& rScrInf, SwTextFrame const* pFrame = nullptr);
     explicit SwAttrIter(SwTextNode const * pTextNode)
         : m_pViewShell(nullptr)
         , m_pFont(nullptr)
@@ -93,7 +93,7 @@ public:
     /// All subclasses of this always have a SwTextFrame passed to the
     /// constructor, but SwAttrIter itself may be created without a
     /// SwTextFrame in certain special cases via this ctor here
-    SwAttrIter( SwTextNode& rTextNode, SwScriptInfo& rScrInf )
+    SwAttrIter(SwTextNode& rTextNode, SwScriptInfo& rScrInf, SwTextFrame const*const pFrame = nullptr)
         : m_pViewShell(nullptr)
         , m_pFont(nullptr)
         , m_pScriptInfo(nullptr)
@@ -103,7 +103,9 @@ public:
         , m_nPropFont(0)
         , m_pTextNode(&rTextNode)
         , m_pMergedPara(nullptr)
-        { CtorInitAttrIter( rTextNode, rScrInf ); }
+    {
+        CtorInitAttrIter(rTextNode, rScrInf, pFrame);
+    }
 
     virtual ~SwAttrIter();
 
diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx
index 295765d7f809..7e2458fcb2b9 100644
--- a/sw/source/core/text/itrpaint.cxx
+++ b/sw/source/core/text/itrpaint.cxx
@@ -596,8 +596,8 @@ void SwTextPainter::CheckSpecialUnderline( const SwLinePortion* pPor,
     {
         // here starts the algorithm for calculating the underline font
         SwScriptInfo& rScriptInfo = GetInfo().GetParaPortion()->GetScriptInfo();
-        SwAttrIter aIter( *GetInfo().GetTextFrame()->GetTextNode(),
-                          rScriptInfo );
+        SwAttrIter aIter(*GetInfo().GetTextFrame()->GetTextNodeFirst(),
+                         rScriptInfo, GetTextFrame());
 
         TextFrameIndex nTmpIdx = nIndx;
         sal_uLong nSumWidth = 0;
diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx
index 9ae23cb69772..afebb9418789 100644
--- a/sw/source/core/text/redlnitr.cxx
+++ b/sw/source/core/text/redlnitr.cxx
@@ -189,7 +189,7 @@ void SwAttrIter::InitFontAndAttrHandler(SwTextNode const& rTextNode,
 }
 
 void SwAttrIter::CtorInitAttrIter(SwTextNode & rTextNode,
-        SwScriptInfo & rScriptInfo, SwTextFrame *const pFrame)
+        SwScriptInfo & rScriptInfo, SwTextFrame const*const pFrame)
 {
     // during HTML-Import it can happen, that no layout exists
     SwRootFrame* pRootFrame = rTextNode.getIDocumentLayoutAccess().GetCurrentLayout();
commit c494a886cd5bb3f87f09a1a5170b8fe2a2986c45
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Wed Jun 6 13:29:04 2018 +0200

    sw_redlinehide: convert SwTextSizeInfo::GetMultiCreator()
    
    Very tricky ... i'd be surprised if this actually works :-/
    
    Change-Id: I309db66e64838aaf92609313d04c4fe3d096c6ee

diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx
index 838f90cb296d..13b7207b71d9 100644
--- a/sw/source/core/text/pormulti.cxx
+++ b/sw/source/core/text/pormulti.cxx
@@ -756,10 +756,9 @@ void SwRubyPortion::CalcRubyOffset()
 // no 2-line-format reference is passed. If there is a 2-line-format reference,
 // then the rValue is set only, if the 2-line-attribute's value is set _and_
 // the 2-line-formats has the same brackets.
-static bool lcl_Has2Lines( const SwTextAttr& rAttr, const SvxTwoLinesItem* &rpRef,
-    bool &rValue )
+static bool lcl_Check2Lines(const SfxPoolItem *const pItem,
+        const SvxTwoLinesItem* &rpRef, bool &rValue)
 {
-    const SfxPoolItem* pItem = CharFormat::GetItem( rAttr, RES_CHRATR_TWO_LINES );
     if( pItem )
     {
         rValue = static_cast<const SvxTwoLinesItem*>(pItem)->GetValue();
@@ -775,6 +774,13 @@ static bool lcl_Has2Lines( const SwTextAttr& rAttr, const SvxTwoLinesItem* &rpRe
     return false;
 }
 
+static bool lcl_Has2Lines(const SwTextAttr& rAttr,
+        const SvxTwoLinesItem* &rpRef, bool &rValue)
+{
+    const SfxPoolItem* pItem = CharFormat::GetItem(rAttr, RES_CHRATR_TWO_LINES);
+    return lcl_Check2Lines(pItem, rpRef, rValue);
+}
+
 // is a little help function for GetMultiCreator(..)
 // It extracts the charrotation from a charrotate-attribute or a character style.
 // The rValue is set to true, if the charrotate-attribute's value is set and
@@ -782,10 +788,9 @@ static bool lcl_Has2Lines( const SwTextAttr& rAttr, const SvxTwoLinesItem* &rpRe
 // If there is a charrotate-format reference, then the rValue is set only,
 // if the charrotate-attribute's value is set _and_ identical
 // to the charrotate-format's value.
-static bool lcl_HasRotation( const SwTextAttr& rAttr,
-    const SvxCharRotateItem* &rpRef, bool &rValue )
+static bool lcl_CheckRotation(const SfxPoolItem *const pItem,
+        const SvxCharRotateItem* &rpRef, bool &rValue)
 {
-    const SfxPoolItem* pItem = CharFormat::GetItem( rAttr, RES_CHRATR_ROTATE );
     if ( pItem )
     {
         rValue = static_cast<const SvxCharRotateItem*>(pItem)->GetValue();
@@ -800,6 +805,100 @@ static bool lcl_HasRotation( const SwTextAttr& rAttr,
     return false;
 }
 
+static bool lcl_HasRotation(const SwTextAttr& rAttr,
+        const SvxCharRotateItem* &rpRef, bool &rValue)
+{
+    const SfxPoolItem* pItem = CharFormat::GetItem( rAttr, RES_CHRATR_ROTATE );
+    return lcl_CheckRotation(pItem, rpRef, rValue);
+}
+
+namespace sw {
+
+    // need to use a very special attribute iterator here that returns
+    // both the hints and the nodes, so that GetMultiCreator() can handle
+    // items in the nodes' set properly
+    class MergedAttrIterMulti
+        : public MergedAttrIterBase
+    {
+    private:
+        bool m_First = true;
+    public:
+        MergedAttrIterMulti(SwTextFrame const& rFrame) : MergedAttrIterBase(rFrame) {}
+        SwTextAttr const* NextAttr(SwTextNode const*& rpNode);
+        // can't have operator= because m_pMerged/m_pNode const
+        void Assign(MergedAttrIterMulti const& rOther)
+        {
+            assert(m_pMerged == rOther.m_pMerged);
+            assert(m_pNode == rOther.m_pNode);
+            m_CurrentExtent = rOther.m_CurrentExtent;
+            m_CurrentHint = rOther.m_CurrentHint;
+            m_First = rOther.m_First;
+        }
+    };
+
+    SwTextAttr const* MergedAttrIterMulti::NextAttr(SwTextNode const*& rpNode)
+    {
+        if (m_First)
+        {
+            m_First = false;
+            rpNode = m_pMerged
+                ? m_pMerged->extents.size()
+                    ? m_pMerged->extents[0].pNode
+                    : m_pMerged->pFirstNode
+                : m_pNode;
+            return nullptr;
+        }
+        if (m_pMerged)
+        {
+            while (m_CurrentExtent < m_pMerged->extents.size())
+            {
+                sw::Extent const& rExtent(m_pMerged->extents[m_CurrentExtent]);
+                if (SwpHints const*const pHints = rExtent.pNode->GetpSwpHints())
+                {
+                    while (m_CurrentHint < pHints->Count())
+                    {
+                        SwTextAttr const*const pHint(pHints->Get(m_CurrentHint));
+                        if (rExtent.nEnd < pHint->GetStart())
+                        {
+                            break;
+                        }
+                        ++m_CurrentHint;
+                        if (rExtent.nStart <= pHint->GetStart())
+                        {
+                            rpNode = rExtent.pNode;
+                            return pHint;
+                        }
+                    }
+                }
+                ++m_CurrentExtent;
+                if (m_CurrentExtent < m_pMerged->extents.size() &&
+                    rExtent.pNode != m_pMerged->extents[m_CurrentExtent].pNode)
+                {
+                    m_CurrentHint = 0; // reset
+                    rpNode = rExtent.pNode;
+                    return nullptr;
+                }
+            }
+            return nullptr;
+        }
+        else
+        {
+            SwpHints const*const pHints(m_pNode->GetpSwpHints());
+            if (pHints)
+            {
+                while (m_CurrentHint < pHints->Count())
+                {
+                    SwTextAttr const*const pHint(pHints->Get(m_CurrentHint));
+                    ++m_CurrentHint;
+                    rpNode = m_pNode;
+                    return pHint;
+                }
+            }
+            return nullptr;
+        }
+    }
+}
+
 // If we (e.g. the position rPos) are inside a two-line-attribute or
 // a ruby-attribute, the attribute will be returned in a SwMultiCreator-struct,
 // otherwise the function returns zero.
@@ -854,66 +953,99 @@ SwMultiCreator* SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
     if ( pMulti )
         return nullptr;
 
-    const SvxCharRotateItem* pRotate = nullptr;
-    const SfxPoolItem* pRotItem;
-    if( SfxItemState::SET == m_pFrame->GetTextNode()->GetSwAttrSet().
-        GetItemState( RES_CHRATR_ROTATE, true, &pRotItem ) &&
-        static_cast<const SvxCharRotateItem*>(pRotItem)->GetValue() )
-        pRotate = static_cast<const SvxCharRotateItem*>(pRotItem);
-    else
-        pRotItem = nullptr;
-    const SvxTwoLinesItem* p2Lines = nullptr;
-    const SwTextNode *pLclTextNode = m_pFrame->GetTextNode();
-    if( !pLclTextNode )
-        return nullptr;
-    const SfxPoolItem* pItem;
-    if( SfxItemState::SET == pLclTextNode->GetSwAttrSet().
-        GetItemState( RES_CHRATR_TWO_LINES, true, &pItem ) &&
-        static_cast<const SvxTwoLinesItem*>(pItem)->GetValue() )
-        p2Lines = static_cast<const SvxTwoLinesItem*>(pItem);
-    else
-        pItem = nullptr;
-
-    const SwpHints *pHints = pLclTextNode->GetpSwpHints();
-    if( !pHints && !p2Lines && !pRotate )
-        return nullptr;
+    // need the node that contains input rPos
+    std::pair<SwTextNode const*, sal_Int32> startPos(m_pFrame->MapViewToModel(rPos));
+    const SvxCharRotateItem* pActiveRotateItem(nullptr);
+    const SfxPoolItem* pNodeRotateItem(nullptr);
+    const SvxTwoLinesItem* pActiveTwoLinesItem(nullptr);
+    const SfxPoolItem* pNodeTwoLinesItem(nullptr);
+    SwTextAttr const* pActiveTwoLinesHint(nullptr);
+    SwTextAttr const* pActiveRotateHint(nullptr);
     const SwTextAttr *pRuby = nullptr;
+    sw::MergedAttrIterMulti iterAtStartOfNode(*m_pFrame);
     bool bTwo = false;
     bool bRot = false;
-    size_t n2Lines = SAL_MAX_SIZE;
-    size_t nRotate = SAL_MAX_SIZE;
-    const size_t nCount = pHints ? pHints->Count() : 0;
-    for( size_t i = 0; i < nCount; ++i )
-    {
-        const SwTextAttr *pTmp = pHints->Get(i);
-        sal_Int32 nStart = pTmp->GetStart();
-        if( rPos < nStart )
+
+    for (sw::MergedAttrIterMulti iter = *m_pFrame; ; )
+    {
+        SwTextNode const* pNode(nullptr);
+        SwTextAttr const*const pAttr = iter.NextAttr(pNode);
+        if (!pNode)
+        {
             break;
-        if( *pTmp->GetAnyEnd() > rPos )
+        }
+        if (pAttr)
         {
-            if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
-                pRuby = pTmp;
-            else
+            assert(pNode->GetIndex() <= startPos.first->GetIndex()); // should break earlier
+            if (startPos.first->GetIndex() <= pNode->GetIndex())
             {
-                const SvxCharRotateItem* pRoTmp = nullptr;
-                if( lcl_HasRotation( *pTmp, pRoTmp, bRot ) )
+                if (startPos.first->GetIndex() != pNode->GetIndex()
+                    || startPos.second < pAttr->GetStart())
                 {
-                    nRotate = bRot ? i : nCount;
-                    pRotate = pRoTmp;
+                    break;
                 }
-                const SvxTwoLinesItem* p2Tmp = nullptr;
-                if( lcl_Has2Lines( *pTmp, p2Tmp, bTwo ) )
+                if (startPos.second < *pAttr->GetAnyEnd())
                 {
-                    n2Lines = bTwo ? i : nCount;
-                    p2Lines = p2Tmp;
+                    // sw_redlinehide: ruby *always* splits
+                    if (RES_TXTATR_CJK_RUBY == pAttr->Which())
+                        pRuby = pAttr;
+                    else
+                    {
+                        const SvxCharRotateItem* pRoTmp = nullptr;
+                        if (lcl_HasRotation( *pAttr, pRoTmp, bRot ))
+                        {
+                            pActiveRotateHint = bRot ? pAttr : nullptr;
+                            pActiveRotateItem = pRoTmp;
+                        }
+                        const SvxTwoLinesItem* p2Tmp = nullptr;
+                        if (lcl_Has2Lines( *pAttr, p2Tmp, bTwo ))
+                        {
+                            pActiveTwoLinesHint = bTwo ? pAttr : nullptr;
+                            pActiveTwoLinesItem = p2Tmp;
+                        }
+                    }
+                }
+            }
+        }
+        else if (pNode) // !pAttr && pNode means the node changed
+        {
+            if (startPos.first->GetIndex() < pNode->GetIndex())
+            {
+                break; // only one node initially
+            }
+            if (startPos.first->GetIndex() == pNode->GetIndex())
+            {
+                iterAtStartOfNode.Assign(iter);
+                if (SfxItemState::SET == pNode->GetSwAttrSet().GetItemState(
+                            RES_CHRATR_ROTATE, true, &pNodeRotateItem) &&
+                    static_cast<const SvxCharRotateItem*>(pNodeRotateItem)->GetValue())
+                {
+                    pActiveRotateItem = static_cast<const SvxCharRotateItem*>(pNodeRotateItem);
+                }
+                else
+                {
+                    pNodeRotateItem = nullptr;
+                }
+                if (SfxItemState::SET == startPos.first->GetSwAttrSet().GetItemState(
+                            RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem) &&
+                    static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetValue())
+                {
+                    pActiveTwoLinesItem = static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem);
+                }
+                else
+                {
+                    pNodeTwoLinesItem = nullptr;
                 }
             }
         }
     }
+    if (!pRuby && !pActiveTwoLinesItem && !pActiveRotateItem)
+        return nullptr;
+
     if( pRuby )
     {   // The winner is ... a ruby attribute and so
         // the end of the multiportion is the end of the ruby attribute.
-        rPos = *pRuby->End();
+        rPos = m_pFrame->MapModelToView(startPos.first, *pRuby->End());
         SwMultiCreator *pRet = new SwMultiCreator;
         pRet->pItem = nullptr;
         pRet->pAttr = pRuby;
@@ -921,44 +1053,48 @@ SwMultiCreator* SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
         pRet->nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
         return pRet;
     }
-    if( n2Lines < nCount || ( pItem && pItem == p2Lines &&
-        rPos < TextFrameIndex(GetText().getLength())))
+    if (pActiveTwoLinesHint ||
+        (pNodeTwoLinesItem && pNodeTwoLinesItem == pActiveTwoLinesItem &&
+         rPos < TextFrameIndex(GetText().getLength())))
     {   // The winner is a 2-line-attribute,
         // the end of the multiportion depends on the following attributes...
         SwMultiCreator *pRet = new SwMultiCreator;
 
         // We note the endpositions of the 2-line attributes in aEnd as stack
-        std::deque< sal_Int32 > aEnd;
+        std::deque<TextFrameIndex> aEnd;
 
         // The bOn flag signs the state of the last 2-line attribute in the
         // aEnd-stack, it is compatible with the winner-attribute or
         // it interrupts the other attribute.
         bool bOn = true;
 
-        if( n2Lines < nCount )
+        if (pActiveTwoLinesHint)
         {
             pRet->pItem = nullptr;
-            pRet->pAttr = pHints->Get(n2Lines);
-            aEnd.push_front( *pRet->pAttr->End() );
-            if( pItem )
+            pRet->pAttr = pActiveTwoLinesHint;
+            if (pNodeTwoLinesItem)
+            {
+                aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
+                bOn = static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetEndBracket() ==
+                        pActiveTwoLinesItem->GetEndBracket() &&
+                      static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetStartBracket() ==
+                        pActiveTwoLinesItem->GetStartBracket();
+            }
+            else
             {
-                aEnd.front() = GetText().getLength();
-                bOn = static_cast<const SvxTwoLinesItem*>(pItem)->GetEndBracket() ==
-                        p2Lines->GetEndBracket() &&
-                      static_cast<const SvxTwoLinesItem*>(pItem)->GetStartBracket() ==
-                        p2Lines->GetStartBracket();
+                aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *pRet->pAttr->End()));
             }
         }
         else
         {
-            pRet->pItem = pItem;
+            pRet->pItem = pNodeTwoLinesItem;
             pRet->pAttr = nullptr;
-            aEnd.push_front( GetText().getLength() );
+            aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
         }
         pRet->nId = SwMultiCreatorId::Double;
         pRet->nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
 
-        // n2Lines is the index of the last 2-line-attribute, which contains
+        // pActiveTwoLinesHint is the last 2-line-attribute, which contains
         // the actual position.
 
         // At this moment we know that at position rPos the "winner"-attribute
@@ -975,23 +1111,49 @@ SwMultiCreator* SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
         // In the following loop rPos is the critical position and it will be
         // evaluated, if at rPos starts a interrupting or a maintaining
         // continuity attribute.
-        for( size_t i = 0; i < nCount; ++i )
+
+        // iterAtStartOfNode is positioned to the first hint of the node
+        // (if any); the node item itself has already been handled above
+        for (sw::MergedAttrIterMulti iter = iterAtStartOfNode; ; )
         {
-            const SwTextAttr *pTmp = pHints->Get(i);
-            if( *pTmp->GetAnyEnd() <= rPos )
-                continue;
-            if( rPos < pTmp->GetStart() )
+            SwTextNode const* pNode(nullptr);
+            SwTextAttr const*const pTmp = iter.NextAttr(pNode);
+            if (!pNode)
+            {
+                break;
+            }
+            assert(startPos.first->GetIndex() <= pNode->GetIndex());
+            TextFrameIndex nTmpStart;
+            TextFrameIndex nTmpEnd;
+            if (pTmp)
+            {
+                nTmpEnd = m_pFrame->MapModelToView(pNode, *pTmp->GetAnyEnd());
+                if (nTmpEnd <= rPos)
+                    continue;
+                nTmpStart = m_pFrame->MapModelToView(pNode, pTmp->GetStart());
+            }
+            else
+            {
+                pNodeTwoLinesItem = nullptr;
+                pNode->GetSwAttrSet().GetItemState(
+                            RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem);
+                nTmpStart = m_pFrame->MapModelToView(pNode, 0);
+                nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
+                assert(rPos <= nTmpEnd); // next node must not have smaller index
+            }
+
+            if (rPos < nTmpStart)
             {
                 // If bOn is false and the next attribute starts later than rPos
                 // the winner attribute is interrupted at rPos.
                 // If the start of the next attribute is behind the end of
                 // the last attribute on the aEnd-stack, this is the endposition
                 // on the stack is the end of the 2-line portion.
-                if( !bOn || aEnd.back() < pTmp->GetStart() )
+                if (!bOn || aEnd.back() < nTmpStart)
                     break;
                 // At this moment, bOn is true and the next attribute starts
                 // behind rPos, so we could move rPos to the next startpoint
-                rPos = pTmp->GetStart();
+                rPos = nTmpStart;
                 // We clean up the aEnd-stack, endpositions equal to rPos are
                 // superfluous.
                 while( !aEnd.empty() && aEnd.back() <= rPos )
@@ -1008,15 +1170,16 @@ SwMultiCreator* SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
                 }
             }
             // A ruby attribute stops the 2-line immediately
-            if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
+            if (pTmp && RES_TXTATR_CJK_RUBY == pTmp->Which())
                 return pRet;
-            if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) )
+            if (pTmp ? lcl_Has2Lines(*pTmp, pActiveTwoLinesItem, bTwo)
+                     : lcl_Check2Lines(pNodeTwoLinesItem, pActiveTwoLinesItem, bTwo))
             {   // We have an interesting attribute..
                 if( bTwo == bOn )
                 {   // .. with the same state, so the last attribute could
                     // be continued.
-                    if( aEnd.back() < *pTmp->End() )
-                        aEnd.back() = *pTmp->End();
+                    if (aEnd.back() < nTmpEnd)
+                        aEnd.back() = nTmpEnd;
                 }
                 else
                 {   // .. with a different state.
@@ -1024,12 +1187,12 @@ SwMultiCreator* SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
                     // If this is smaller than the last on the stack, we put
                     // it on the stack. If it has the same endposition, the last
                     // could be removed.
-                    if( aEnd.back() > *pTmp->End() )
-                        aEnd.push_back( *pTmp->End() );
+                    if (nTmpEnd < aEnd.back())
+                        aEnd.push_back( nTmpEnd );
                     else if( aEnd.size() > 1 )
                         aEnd.pop_back();
                     else
-                        aEnd.back() = *pTmp->End();
+                        aEnd.back() = nTmpEnd;
                 }
             }
         }
@@ -1037,32 +1200,58 @@ SwMultiCreator* SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
             rPos = aEnd.back();
         return pRet;
     }
-    if (nRotate < nCount || ( pRotItem && pRotItem == pRotate &&
-        rPos < TextFrameIndex(GetText().getLength())))
+    if (pActiveRotateHint ||
+        (pNodeRotateItem && pNodeRotateItem == pActiveRotateItem &&
+         rPos < TextFrameIndex(GetText().getLength())))
     {   // The winner is a rotate-attribute,
         // the end of the multiportion depends on the following attributes...
         SwMultiCreator *pRet = new SwMultiCreator;
         pRet->nId = SwMultiCreatorId::Rotate;
 
         // We note the endpositions of the 2-line attributes in aEnd as stack
-        std::deque< sal_Int32 > aEnd;
+        std::deque<TextFrameIndex> aEnd;
 
         // The bOn flag signs the state of the last 2-line attribute in the
         // aEnd-stack, which could interrupts the winning rotation attribute.
-        bool bOn = pItem;
-        aEnd.push_front( GetText().getLength() );
+        bool bOn = pNodeTwoLinesItem != nullptr;
+        aEnd.push_front(TextFrameIndex(GetText().getLength()));
 
-        sal_Int32 n2Start = rPos;
-        for( size_t i = 0; i < nCount; ++i )
+        // first, search for the start position of the next TWOLINE portion
+        // because the ROTATE portion must end there at the latest
+        TextFrameIndex n2Start = rPos;
+        for (sw::MergedAttrIterMulti iter = iterAtStartOfNode; ; )
         {
-            const SwTextAttr *pTmp = pHints->Get(i);
-            if( *pTmp->GetAnyEnd() <= n2Start )
-                continue;
-            if( n2Start < pTmp->GetStart() )
+            SwTextNode const* pNode(nullptr);
+            SwTextAttr const*const pTmp = iter.NextAttr(pNode);
+            if (!pNode)
+            {
+                break;
+            }
+            assert(startPos.first->GetIndex() <= pNode->GetIndex());
+            TextFrameIndex nTmpStart;
+            TextFrameIndex nTmpEnd;
+            if (pTmp)
+            {
+                nTmpEnd = m_pFrame->MapModelToView(pNode, *pTmp->GetAnyEnd());
+                if (nTmpEnd <= n2Start)
+                    continue;
+                nTmpStart = m_pFrame->MapModelToView(pNode, pTmp->GetStart());
+            }
+            else
+            {
+                pNodeTwoLinesItem = nullptr;
+                pNode->GetSwAttrSet().GetItemState(
+                            RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem);
+                nTmpStart = m_pFrame->MapModelToView(pNode, 0);
+                nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
+                assert(n2Start <= nTmpEnd); // next node must not have smaller index
+            }
+
+            if (n2Start < nTmpStart)
             {
-                if( bOn || aEnd.back() < pTmp->GetStart() )
+                if (bOn || aEnd.back() < nTmpStart)
                     break;
-                n2Start = pTmp->GetStart();
+                n2Start = nTmpStart;
                 while( !aEnd.empty() && aEnd.back() <= n2Start )
                 {
                     bOn = !bOn;
@@ -1075,28 +1264,29 @@ SwMultiCreator* SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
                 }
             }
             // A ruby attribute stops immediately
-            if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
+            if (pTmp && RES_TXTATR_CJK_RUBY == pTmp->Which())
             {
                 bOn = true;
                 break;
             }
-            p2Lines = nullptr;
-            if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) )
+            const SvxTwoLinesItem* p2Lines = nullptr;
+            if (pTmp ? lcl_Has2Lines(*pTmp, p2Lines, bTwo)
+                     : lcl_Check2Lines(pNodeTwoLinesItem, p2Lines, bTwo))
             {
                 if( bTwo == bOn )
                 {
-                    if( aEnd.back() < *pTmp->End() )
-                        aEnd.back() = *pTmp->End();
+                    if (aEnd.back() < nTmpEnd)
+                        aEnd.back() = nTmpEnd;
                 }
                 else
                 {
                     bOn = bTwo;
-                    if( aEnd.back() > *pTmp->End() )
-                        aEnd.push_back( *pTmp->End() );
+                    if (nTmpEnd < aEnd.back())
+                        aEnd.push_back( nTmpEnd );
                     else if( aEnd.size() > 1 )
                         aEnd.pop_back();
                     else
-                        aEnd.back() = *pTmp->End();
+                        aEnd.back() = nTmpEnd;
                 }
             }
         }
@@ -1106,35 +1296,62 @@ SwMultiCreator* SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
         if( !aEnd.empty() )
             aEnd.clear();
 
+        // now, search for the end of the ROTATE portion, similar to above
         bOn = true;
-        if( nRotate < nCount )
+        if (pActiveRotateHint)
         {
             pRet->pItem = nullptr;
-            pRet->pAttr = pHints->Get(nRotate);
-            aEnd.push_front( *pRet->pAttr->End() );
-            if( pRotItem )
+            pRet->pAttr = pActiveRotateHint;
+            if (pNodeRotateItem)
             {
-                aEnd.front() = GetText().getLength();
-                bOn = static_cast<const SvxCharRotateItem*>(pRotItem)->GetValue() ==
-                        pRotate->GetValue();
+                aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
+                bOn = static_cast<const SvxCharRotateItem*>(pNodeRotateItem)->GetValue() ==
+                        pActiveRotateItem->GetValue();
+            }
+            else
+            {
+                aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *pRet->pAttr->End()));
             }
         }
         else
         {
-            pRet->pItem = pRotItem;
+            pRet->pItem = pNodeRotateItem;
             pRet->pAttr = nullptr;
-            aEnd.push_front( GetText().getLength() );
+            aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
         }
-        for( size_t i = 0; i < nCount; ++i )
+        for (sw::MergedAttrIterMulti iter = iterAtStartOfNode; ; )
         {
-            const SwTextAttr *pTmp = pHints->Get(i);
-            if( *pTmp->GetAnyEnd() <= rPos )
-                continue;
-            if( rPos < pTmp->GetStart() )
+            SwTextNode const* pNode(nullptr);
+            SwTextAttr const*const pTmp = iter.NextAttr(pNode);
+            if (!pNode)
+            {
+                break;
+            }
+            assert(startPos.first->GetIndex() <= pNode->GetIndex());
+            TextFrameIndex nTmpStart;
+            TextFrameIndex nTmpEnd;
+            if (pTmp)
+            {
+                nTmpEnd = m_pFrame->MapModelToView(pNode, *pTmp->GetAnyEnd());
+                if (nTmpEnd <= rPos)
+                    continue;
+                nTmpStart = m_pFrame->MapModelToView(pNode, pTmp->GetStart());
+            }
+            else
+            {
+                pNodeRotateItem = nullptr;
+                pNode->GetSwAttrSet().GetItemState(
+                            RES_CHRATR_ROTATE, true, &pNodeRotateItem);
+                nTmpStart = m_pFrame->MapModelToView(pNode, 0);
+                nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
+                assert(rPos <= nTmpEnd); // next node must not have smaller index
+            }
+
+            if (rPos < nTmpStart)
             {
-                if( !bOn || aEnd.back() < pTmp->GetStart() )
+                if (!bOn || aEnd.back() < nTmpStart)
                     break;
-                rPos = pTmp->GetStart();
+                rPos = nTmpStart;
                 while( !aEnd.empty() && aEnd.back() <= rPos )
                 {
                     bOn = !bOn;
@@ -1146,27 +1363,29 @@ SwMultiCreator* SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
                     bOn = true;
                 }
             }
-            if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
+            if (pTmp && RES_TXTATR_CJK_RUBY == pTmp->Which())
             {
                 bOn = false;
                 break;
             }
-            if( lcl_HasRotation( *pTmp, pRotate, bTwo ) )
+            // TODO why does this use bTwo, not bRot ???
+            if (pTmp ? lcl_HasRotation(*pTmp, pActiveRotateItem, bTwo)
+                     : lcl_CheckRotation(pNodeRotateItem, pActiveRotateItem, bTwo))
             {
                 if( bTwo == bOn )
                 {
-                    if( aEnd.back() < *pTmp->End() )
-                        aEnd.back() = *pTmp->End();
+                    if (aEnd.back() < nTmpEnd)
+                        aEnd.back() = nTmpEnd;
                 }
                 else
                 {
                     bOn = bTwo;
-                    if( aEnd.back() > *pTmp->End() )
-                        aEnd.push_back( *pTmp->End() );
+                    if (nTmpEnd < aEnd.back())
+                        aEnd.push_back( nTmpEnd );
                     else if( aEnd.size() > 1 )
                         aEnd.pop_back();
                     else
-                        aEnd.back() = *pTmp->End();
+                        aEnd.back() = nTmpEnd;
                 }
             }
         }
commit 260f04ab571b752aba4b94c73d8cccd0fec0fa4e
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Fri Jun 1 22:41:06 2018 +0200

    sw_redlinehide: convert GetAttrSet() in txtfrm.cxx, frmtool.cxx
    
    Change-Id: I3541fa79ff48f6829fc459ab5bd8224c29c51cbb

diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx
index cfafce5b3d51..3713439f3f53 100644
--- a/sw/source/core/layout/frmtool.cxx
+++ b/sw/source/core/layout/frmtool.cxx
@@ -738,8 +738,7 @@ SwContentNotify::SwContentNotify( SwContentFrame *pContentFrame ) :
         SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pContentFrame);
         if (!pTextFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::OLD_LINE_SPACING))
         {
-            const SwAttrSet* pSet = pTextFrame->GetAttrSet();
-            const SvxLineSpacingItem &rSpace = pSet->GetLineSpacing();
+            const SvxLineSpacingItem &rSpace = pTextFrame->GetAttrSet()->GetLineSpacing();
             if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
             {
                 mbChkHeightOfLastLine = true;
diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx
index 339c23e2a430..fae1cb86bf09 100644
--- a/sw/source/core/text/txtfrm.cxx
+++ b/sw/source/core/text/txtfrm.cxx
@@ -2998,7 +2998,8 @@ void SwTextFrame::CalcHeightOfLastLine( const bool _bUseFont )
     {
         // former determination of last line height for proprotional line
         // spacing - take height of font set at the paragraph
-        SwFont aFont( GetAttrSet(), pIDSA );
+        // FIXME actually ... must the font match across all nodes?
+        SwFont aFont( &GetTextNodeForParaProps()->GetSwAttrSet(), pIDSA );
 
         // we must ensure that the font is restored correctly on the OutputDevice
         // otherwise Last!=Owner could occur
@@ -3104,8 +3105,7 @@ long SwTextFrame::GetLineSpace( const bool _bNoPropLineSpace ) const
 {
     long nRet = 0;
 
-    const SwAttrSet* pSet = GetAttrSet();
-    const SvxLineSpacingItem &rSpace = pSet->GetLineSpacing();
+    const SvxLineSpacingItem &rSpace = GetTextNodeForParaProps()->GetSwAttrSet().GetLineSpacing();
 
     switch( rSpace.GetInterLineSpaceRule() )
     {
@@ -3206,7 +3206,7 @@ void SwTextFrame::ChgThisLines()
 
     if ( nNew != mnThisLines )
     {
-        if ( !IsInTab() && GetAttrSet()->GetLineNumber().IsCount() )
+        if (!IsInTab() && GetTextNodeForParaProps()->GetSwAttrSet().GetLineNumber().IsCount())
         {
             mnAllLines -= mnThisLines;
             mnThisLines = nNew;
@@ -3237,12 +3237,10 @@ void SwTextFrame::RecalcAllLines()
 {
     ValidateLineNum();
 
-    const SwAttrSet *pAttrSet = GetAttrSet();
-
     if ( !IsInTab() )
     {
         const sal_uLong nOld = GetAllLines();
-        const SwFormatLineNumber &rLineNum = pAttrSet->GetLineNumber();
+        const SwFormatLineNumber &rLineNum = GetTextNodeForParaProps()->GetSwAttrSet().GetLineNumber();
         sal_uLong nNewNum;
         const bool bRestart = GetDoc().GetLineNumberInfo().IsRestartEachPage();
 
commit 67335382dc84ab4a267b0f0f776d76e24dccc747
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Fri Jun 1 22:37:02 2018 +0200

    sw_redlinehide: move SwContentNode::GetNode() to SwNoTextNode
    
    SwTextNode's GetDep() may be a WriterMultiListener, which isn't even a
    node.
    
    SwTextNode has GetTextNodeForParaProps()/GetTextNodeFirst().
    
    Change-Id: Ica177a6a3cf7c886c9a8d2733fb9cb452a475450

diff --git a/sw/source/core/inc/cntfrm.hxx b/sw/source/core/inc/cntfrm.hxx
index 62413e38de8c..aa28f580f3ad 100644
--- a/sw/source/core/inc/cntfrm.hxx
+++ b/sw/source/core/inc/cntfrm.hxx
@@ -71,9 +71,6 @@ public:
     virtual void Cut() override;
     virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override;
 
-    inline const SwContentNode *GetNode() const;
-    inline       SwContentNode *GetNode();
-
     inline const SwContentFrame *GetFollow() const;
     inline       SwContentFrame *GetFollow();
     SwTextFrame* FindMaster() const;
@@ -114,15 +111,6 @@ inline SwContentFrame* SwContentFrame::GetPrevContentFrame() const
         return const_cast<SwContentFrame*>(ImplGetNextContentFrame( false ));
 }
 
-inline SwContentNode *SwContentFrame::GetNode()
-{
-    return static_cast< SwContentNode* >( GetDep() );
-}
-inline const SwContentNode *SwContentFrame::GetNode() const
-{
-    return static_cast< const SwContentNode* >( GetDep() );
-}
-
 inline const SwContentFrame *SwContentFrame::GetFollow() const
 {
     return static_cast<const SwContentFrame*>(SwFlowFrame::GetFollow());
diff --git a/sw/source/core/inc/notxtfrm.hxx b/sw/source/core/inc/notxtfrm.hxx
index 9de5fd31abfe..eaa10653964b 100644
--- a/sw/source/core/inc/notxtfrm.hxx
+++ b/sw/source/core/inc/notxtfrm.hxx
@@ -54,6 +54,11 @@ protected:
 public:
     SwNoTextFrame( SwNoTextNode * const, SwFrame* );
 
+    const SwContentNode *GetNode() const
+        { return static_cast<SwContentNode const*>(GetDep()); }
+          SwContentNode *GetNode()
+        { return static_cast<SwContentNode      *>(GetDep()); }
+
     virtual bool LeftMargin(SwPaM *) const override;
     virtual bool RightMargin(SwPaM *, bool bAPI = false) const override;
 
diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx
index 9c0f787060f7..f6fbb686903e 100644
--- a/sw/source/core/inc/txtfrm.hxx
+++ b/sw/source/core/inc/txtfrm.hxx
@@ -398,9 +398,9 @@ public:
     const OUString& GetText() const;
     // TODO: remove GetTextNode
     SwTextNode *GetTextNode()
-        { return static_cast< SwTextNode* >( SwContentFrame::GetNode()); }
+        { return static_cast<SwTextNode*>(SwFrame::GetDep()); }
     const SwTextNode *GetTextNode() const
-        { return static_cast< const SwTextNode* >( SwContentFrame::GetNode()); }
+        { return static_cast<const SwTextNode*>(SwFrame::GetDep()); }
     SwTextNode const* GetTextNodeForParaProps() const;
     SwTextNode      * GetTextNodeFirst()
         { return const_cast<SwTextNode*>(const_cast<SwTextFrame const*>(this)->GetTextNodeFirst()); };
commit ede0b3613f3e5d66d8afb716aab75029120deef2
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Fri May 4 13:45:09 2018 +0200

    sw_redlinehide: use FrameContainsNode in anchoredobjectposition.cxx
    
    Change-Id: I648810c1d5549d700a64f120a52416a951d56dca

diff --git a/sw/source/core/objectpositioning/anchoredobjectposition.cxx b/sw/source/core/objectpositioning/anchoredobjectposition.cxx
index 0bae1d1c8ade..f9fa2c8d11cd 100644
--- a/sw/source/core/objectpositioning/anchoredobjectposition.cxx
+++ b/sw/source/core/objectpositioning/anchoredobjectposition.cxx
@@ -947,7 +947,7 @@ SwTwips SwAnchoredObjectPosition::AdjustHoriRelPosForDrawAside(
     const sal_uInt32 nObjOrdNum = GetObject().GetOrdNum();
     const SwPageFrame* pObjPage = rFlyAtContentFrame.FindPageFrame();
     const SwFrame* pObjContext = ::FindContext( &rAnchorTextFrame, SwFrameType::Column );
-    sal_uLong nObjIndex = rAnchorTextFrame.GetTextNode()->GetIndex();
+    sal_uLong nObjIndex = rAnchorTextFrame.GetTextNodeFirst()->GetIndex();
     SwOrderIter aIter( pObjPage );
     const SwFlyFrame* pFly = static_cast<const SwVirtFlyDrawObj*>(aIter.Bottom())->GetFlyFrame();
     while ( pFly && nObjOrdNum > pFly->GetVirtDrawObj()->GetOrdNumDirect() )
@@ -1058,8 +1058,9 @@ bool SwAnchoredObjectPosition::DrawAsideFly( const SwFlyFrame* _pFly,
          ::FindContext( _pFly->GetAnchorFrame(), SwFrameType::Column ) == _pObjContext )
     {
         sal_uLong nOtherIndex =
-            static_cast<const SwTextFrame*>(_pFly->GetAnchorFrame())->GetTextNode()->GetIndex();
-        if( _nObjIndex >= nOtherIndex )
+            static_cast<const SwTextFrame*>(_pFly->GetAnchorFrame())->GetTextNodeFirst()->GetIndex();
+        if (sw::FrameContainsNode(static_cast<SwTextFrame const&>(*_pFly->GetAnchorFrame()), _nObjIndex)
+            || nOtherIndex < _nObjIndex)
         {
             const SwFormatHoriOrient& rHori = _pFly->GetFormat()->GetHoriOrient();
             sal_Int16 eOtherRelOrient = rHori.GetRelationOrient();
commit ef64c5cad0d22acf149a911d07aac1c1f67be638
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Fri Jun 1 22:32:26 2018 +0200

    sw_redlinehide: convert GetNode(), use FrameContainsNode(), txtfly.cxx
    
    Change-Id: I0e4961acc191a764db74ede75e140e63ad1318a0

diff --git a/sw/source/core/layout/trvlfrm.cxx b/sw/source/core/layout/trvlfrm.cxx
index 39843fd25efb..fee388a696c1 100644
--- a/sw/source/core/layout/trvlfrm.cxx
+++ b/sw/source/core/layout/trvlfrm.cxx
@@ -692,7 +692,7 @@ static const SwContentFrame * lcl_MissProtectedFrames( const SwContentFrame *pCn
 static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart,
                     GetNxtPrvCnt fnNxtPrv, bool bInReadOnly )
 {
-    OSL_ENSURE( &pPam->GetNode() == pStart->GetNode(),
+    OSL_ENSURE( FrameContainsNode(*pStart, pPam->GetNode().GetIndex()),
             "lcl_UpDown doesn't work for others." );
 
     const SwContentFrame *pCnt = nullptr;
diff --git a/sw/source/core/text/txtfly.cxx b/sw/source/core/text/txtfly.cxx
index c7d3f891fa0d..45950b93ec6b 100644
--- a/sw/source/core/text/txtfly.cxx
+++ b/sw/source/core/text/txtfly.cxx
@@ -834,9 +834,9 @@ bool SwTextFly::GetTop( const SwAnchoredObject* _pAnchoredObj,
                 // If possible determine Index via SwFormatAnchor because
                 // otherwise it's quite expensive.
                 if (ULONG_MAX == m_nCurrFrameNodeIndex)
-                    m_nCurrFrameNodeIndex = m_pCurrFrame->GetNode()->GetIndex();
+                    m_nCurrFrameNodeIndex = m_pCurrFrame->GetTextNodeFirst()->GetIndex();
 
-                if (nTmpIndex < m_nCurrFrameNodeIndex)
+                if (FrameContainsNode(*m_pCurrFrame, nTmpIndex) || nTmpIndex < m_nCurrFrameNodeIndex)
                     return true;
             }
         }
commit 6cd9d89d36bd81f75d930b69bd4f53577fb04c3b
Author: Michael Stahl <Michael.Stahl at cib.de>
Date:   Fri Jun 1 22:30:58 2018 +0200

    sw_redlinehide: iterate merged nodes in SwLayIdle::DoIdleJob_()
    
    Change-Id: I42924448749385bb0e5bc2c187e37053e104c0da

diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx
index a4df2477ea01..e98f19ccb771 100644
--- a/sw/source/core/layout/layact.cxx
+++ b/sw/source/core/layout/layact.cxx
@@ -1847,19 +1847,50 @@ bool SwLayIdle::DoIdleJob_( const SwContentFrame *pCnt, IdleJobType eJob )
     if( !pCnt->IsTextFrame() )
         return false;
 
-    const SwTextNode* pTextNode = pCnt->GetNode()->GetTextNode();
+    SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pCnt));
+    // sw_redlinehide: spell check only the nodes with visible content?
+    const SwTextNode* pTextNode = pTextFrame->GetTextNodeForParaProps();
 
     bool bProcess = false;
-    switch ( eJob )
+    for (size_t i = 0; pTextNode; )
     {
-        case ONLINE_SPELLING :
-            bProcess = pTextNode->IsWrongDirty(); break;
-        case AUTOCOMPLETE_WORDS :
-            bProcess = pTextNode->IsAutoCompleteWordDirty(); break;
-        case WORD_COUNT :
-            bProcess = pTextNode->IsWordCountDirty(); break;
-        case SMART_TAGS :
-            bProcess = pTextNode->IsSmartTagDirty(); break;
+        switch ( eJob )
+        {
+            case ONLINE_SPELLING :
+                bProcess = pTextNode->IsWrongDirty(); break;
+            case AUTOCOMPLETE_WORDS :
+                bProcess = pTextNode->IsAutoCompleteWordDirty(); break;
+            case WORD_COUNT :
+                bProcess = pTextNode->IsWordCountDirty(); break;
+            case SMART_TAGS :
+                bProcess = pTextNode->IsSmartTagDirty(); break;
+        }
+        if (bProcess)
+        {
+            break;
+        }
+        if (sw::MergedPara const* pMerged = pTextFrame->GetMergedPara())
+        {
+            while (true)
+            {
+                ++i;
+                if (i < pMerged->extents.size())
+                {
+                    if (pMerged->extents[i].pNode != pTextNode)
+                    {
+                        pTextNode = pMerged->extents[i].pNode;
+                        break;
+                    }
+                }
+                else
+                {
+                    pTextNode = nullptr;
+                    break;
+                }
+            }
+        }
+        else
+            pTextNode = nullptr;
     }
 
     if( bProcess )
commit fa42e0d166531101489e0b99e20c72e3606fa366
Author: Michael Stahl <Michael.Stahl at cib.de>

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list