Rebased ref, commits from common ancestor: commit faf79cec2ec4097f03b1f1c78c23e681dec6a40c Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Thu Aug 9 17:26:50 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200
sw_redlinehide_2: also reset Merge flag in CheckParaRedlineMerge If it sets the Merge flag, it needs to reset it too. Change-Id: I0b07ca87ff9911db37166312ca07edd15e8b496c diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx index f023d069b740..21de262420bf 100644 --- a/sw/source/core/text/redlnitr.cxx +++ b/sw/source/core/text/redlnitr.cxx @@ -102,6 +102,10 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode, } if (!bHaveRedlines) { + if (pNode->GetRedlineMergeFlag() != SwNode::Merge::None) + { + pNode->SetRedlineMergeFlag(SwNode::Merge::None); + } return nullptr; } if (nLastEnd != pNode->Len()) commit 7d10c2c6239c5a8912d6d99f668f801de4eddc86 Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Thu Aug 9 17:25:15 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: when switching show/hide, invalidate Insert redlines ... so that the frames are repainted with/without font color. Change-Id: I68f105868d262c9d0a88f124c98243a64159aa38 diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index 513e8c339f8a..b7cde56120e1 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -31,6 +31,7 @@ #include <IDocumentSettingAccess.hxx> #include <IDocumentFieldsAccess.hxx> #include <IDocumentRedlineAccess.hxx> +#include <redline.hxx> #include <fesh.hxx> #include <docsh.hxx> #include <ftninfo.hxx> @@ -4461,6 +4462,15 @@ void SwRootFrame::SetHideRedlines(bool const bHideRedlines) AppendAllObjs(rDoc.GetSpzFrameFormats(), this); } + for (auto const pRedline : rDoc.getIDocumentRedlineAccess().GetRedlineTable()) + { // DELETE are handled by the code above; for other types, need to + // trigger repaint of text frames to add/remove the redline color font + if (pRedline->GetType() != nsRedlineType_t::REDLINE_DELETE) + { + pRedline->InvalidateRange(); + } + } + // InvalidateAllContent(SwInvalidateFlags::Size); // ??? TODO what to invalidate? this is the big hammer } commit 615a913ddb7019d13901ea24be08071d93f59aed Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Thu Aug 9 17:24:05 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: fix painting of "bars" in the margin If the layout wants to hide redlines, these should not be painted. Change-Id: I3cc725b466ca3874a00c6b96eb0e02ff70dcc42b diff --git a/sw/source/core/text/frmpaint.cxx b/sw/source/core/text/frmpaint.cxx index 4d2adf502528..4b3007087c6d 100644 --- a/sw/source/core/text/frmpaint.cxx +++ b/sw/source/core/text/frmpaint.cxx @@ -288,8 +288,12 @@ void SwTextFrame::PaintExtraData( const SwRect &rRect ) const bool bLineNum = !IsInTab() && rLineInf.IsPaintLineNumbers() && ( !IsInFly() || rLineInf.IsCountInFlys() ) && rLineNum.IsCount(); sal_Int16 eHor = static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos()); - if( eHor != text::HoriOrientation::NONE && !IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) ) + if (eHor != text::HoriOrientation::NONE + && (!IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags()) + || getRootFrame()->IsHideRedlines())) + { eHor = text::HoriOrientation::NONE; + } bool bRedLine = eHor != text::HoriOrientation::NONE; if ( !bLineNum && !bRedLine ) return; @@ -475,7 +479,8 @@ bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const } const IDocumentRedlineAccess& rIDRA = rTextNode.getIDocumentRedlineAccess(); - if( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) ) + if (IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags()) + && !getRootFrame()->IsHideRedlines()) { const SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, USHRT_MAX ); if( SwRedlineTable::npos != nRedlPos ) diff --git a/sw/source/core/text/porrst.cxx b/sw/source/core/text/porrst.cxx index 24778b6ec9d6..727e0319a730 100644 --- a/sw/source/core/text/porrst.cxx +++ b/sw/source/core/text/porrst.cxx @@ -250,7 +250,8 @@ SwTwips SwTextFrame::EmptyHeight() const } const IDocumentRedlineAccess& rIDRA = rTextNode.getIDocumentRedlineAccess(); - if( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) ) + if (IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags()) + && !getRootFrame()->IsHideRedlines()) { const SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, USHRT_MAX ); if( SwRedlineTable::npos != nRedlPos ) commit f9138d52ff6689bf757832a78bc9302070527345 Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Thu Aug 9 17:22:36 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: fix init of SwRedlineIter::Mode If there are no delete redlines in a frame, the mode must be Ignore, otherwise the insert redlines will be colorful... Change-Id: Ibd57f2827b0805ac2fdb29ed347d9b2ed0b00737 diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx index 5bcb4d1b4350..f023d069b740 100644 --- a/sw/source/core/text/redlnitr.cxx +++ b/sw/source/core/text/redlnitr.cxx @@ -287,7 +287,8 @@ void SwAttrIter::CtorInitAttrIter(SwTextNode & rTextNode, } } } - const bool bShow = IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ); + const bool bShow = IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags()) + && pRootFrame && !pRootFrame->IsHideRedlines(); if (pExtInp || m_pMergedPara || bShow) { SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, USHRT_MAX ); commit e1b24b66b5fecf70e649d901e70f33bbaa2d76f3 Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Thu Aug 9 15:10:13 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: fix ordering of SplitNode usage of ContentIdxStore 2 The flys have their anchor positions updated, and that causes lookups from the layout frame to the anchor SwTextFrame, but that isn't updated yet; it looks like the fly restore must be done after adapting the SwTextFrames, while the redline restore must be done before. Also RegisterToNode must call Check only for the 1st node. Change-Id: If87a62108f1bcaf794e5be1cc38dc936f08cd69e diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx index a32ea79c310a..82044796a5eb 100644 --- a/sw/inc/ndtxt.hxx +++ b/sw/inc/ndtxt.hxx @@ -62,6 +62,8 @@ class SwGrammarMarkUp; struct SwDocStat; struct SwParaIdleData_Impl; +namespace sw { namespace mark { enum class RestoreMode; } } + namespace com { namespace sun { namespace star { namespace uno { template < class > class Sequence; @@ -349,7 +351,7 @@ public: /// Virtual methods from ContentNode. virtual SwContentFrame *MakeFrame( SwFrame* ) override; SwTextNode * SplitContentNode(const SwPosition &, - std::function<void (SwTextNode *)> const* pContentIndexRestore); + std::function<void (SwTextNode *, sw::mark::RestoreMode)> const* pContentIndexRestore); virtual SwContentNode *JoinNext() override; void JoinPrev(); diff --git a/sw/source/core/doc/CntntIdxStore.cxx b/sw/source/core/doc/CntntIdxStore.cxx index ffdf2ff15764..0bdd11bae239 100644 --- a/sw/source/core/doc/CntntIdxStore.cxx +++ b/sw/source/core/doc/CntntIdxStore.cxx @@ -151,26 +151,38 @@ namespace SaveUnoCursors(pDoc, nNode, nContent); SaveShellCursors(pDoc, nNode, nContent); } - virtual void Restore(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nOffset=0, bool bAuto = false) override + virtual void Restore(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nOffset=0, bool bAuto = false, RestoreMode eMode = RestoreMode::All) override { SwContentNode* pCNd = pDoc->GetNodes()[ nNode ]->GetContentNode(); updater_t aUpdater = OffsetUpdater(pCNd, nOffset); - RestoreBkmks(pDoc, aUpdater); - RestoreRedlines(pDoc, aUpdater); - RestoreFlys(pDoc, aUpdater, bAuto); - RestoreUnoCursors(aUpdater); - RestoreShellCursors(aUpdater); + if (eMode & RestoreMode::NonFlys) + { + RestoreBkmks(pDoc, aUpdater); + RestoreRedlines(pDoc, aUpdater); + RestoreUnoCursors(aUpdater); + RestoreShellCursors(aUpdater); + } + if (eMode & RestoreMode::Flys) + { + RestoreFlys(pDoc, aUpdater, bAuto); + } } - virtual void Restore(SwNode& rNd, sal_Int32 nLen, sal_Int32 nCorrLen) override + virtual void Restore(SwNode& rNd, sal_Int32 nLen, sal_Int32 nCorrLen, RestoreMode eMode = RestoreMode::All) override { SwContentNode* pCNd = rNd.GetContentNode(); SwDoc* pDoc = rNd.GetDoc(); updater_t aUpdater = LimitUpdater(pCNd, nLen, nCorrLen); - RestoreBkmks(pDoc, aUpdater); - RestoreRedlines(pDoc, aUpdater); - RestoreFlys(pDoc, aUpdater, false); - RestoreUnoCursors(aUpdater); - RestoreShellCursors(aUpdater); + if (eMode & RestoreMode::NonFlys) + { + RestoreBkmks(pDoc, aUpdater); + RestoreRedlines(pDoc, aUpdater); + RestoreUnoCursors(aUpdater); + RestoreShellCursors(aUpdater); + } + if (eMode & RestoreMode::Flys) + { + RestoreFlys(pDoc, aUpdater, false); + } } private: diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx index acca6b19c0e2..20f237eafc1e 100644 --- a/sw/source/core/doc/DocumentContentOperationsManager.cxx +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -1991,12 +1991,12 @@ bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos, assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex()); assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex()); - std::function<void (SwTextNode *)> restoreFunc( - [&](SwTextNode *const) + std::function<void (SwTextNode *, sw::mark::RestoreMode)> restoreFunc( + [&](SwTextNode *const, sw::mark::RestoreMode const eMode) { if (!pContentStore->Empty()) { - pContentStore->Restore(&m_rDoc, pOrigNode->GetIndex()-1, 0, true); + pContentStore->Restore(&m_rDoc, pOrigNode->GetIndex()-1, 0, true, eMode); } }); pTNd = pTNd->SplitContentNode(rPos, &restoreFunc)->GetTextNode(); @@ -2937,12 +2937,12 @@ bool DocumentContentOperationsManager::SplitNode( const SwPosition &rPos, bool b const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create()); pContentStore->Save( &m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true ); assert(pNode->IsTextNode()); - std::function<void (SwTextNode *)> restoreFunc( - [&](SwTextNode *const) + std::function<void (SwTextNode *, sw::mark::RestoreMode)> restoreFunc( + [&](SwTextNode *const, sw::mark::RestoreMode const eMode) { if (!pContentStore->Empty()) { // move all bookmarks, TOXMarks, FlyAtCnt - pContentStore->Restore(&m_rDoc, rPos.nNode.GetIndex()-1, 0, true); + pContentStore->Restore(&m_rDoc, rPos.nNode.GetIndex()-1, 0, true, eMode); } }); pNode = pNode->GetTextNode()->SplitContentNode(rPos, &restoreFunc); diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx index ed96fae59ee3..6041221efbe6 100644 --- a/sw/source/core/docnode/ndtbl.cxx +++ b/sw/source/core/docnode/ndtbl.cxx @@ -1072,12 +1072,12 @@ SwTableNode* SwNodes::TextToTable( const SwNodeRange& rRange, sal_Unicode cCh, if (pTextNd->GetText()[nChPos] == cCh) { aCntPos.nContent = nChPos; - std::function<void (SwTextNode *)> restoreFunc( - [&](SwTextNode *const pNewNode) + std::function<void (SwTextNode *, sw::mark::RestoreMode)> restoreFunc( + [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode) { if (!pContentStore->Empty()) { - pContentStore->Restore(*pNewNode, nChPos, nChPos + 1); + pContentStore->Restore(*pNewNode, nChPos, nChPos + 1, eMode); } }); SwContentNode *const pNewNd = diff --git a/sw/source/core/inc/mvsave.hxx b/sw/source/core/inc/mvsave.hxx index 1279617ef313..ec0a1e412787 100644 --- a/sw/source/core/inc/mvsave.hxx +++ b/sw/source/core/inc/mvsave.hxx @@ -23,6 +23,7 @@ #include <IDocumentMarkAccess.hxx> #include <vector> #include <deque> +#include <o3tl/typed_flags_set.hxx> namespace sfx2 { class MetadatableUndo; @@ -66,20 +67,27 @@ namespace sw { namespace mark std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndo; }; + enum class RestoreMode { Flys = 1, NonFlys = 2, All = 3 }; + /// Takes care of storing relevant attributes of an SwTextNode before split, then restore them on the new node. class ContentIdxStore { public: + virtual void Clear() =0; virtual bool Empty() =0; virtual void Save(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nContent, bool bSaveFlySplit=false) =0; - virtual void Restore(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nOffset=0, bool bAuto = false) =0; - virtual void Restore(SwNode& rNd, sal_Int32 nLen, sal_Int32 nCorrLen) =0; + virtual void Restore(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nOffset=0, bool bAuto = false, RestoreMode = RestoreMode::All) =0; + virtual void Restore(SwNode& rNd, sal_Int32 nLen, sal_Int32 nCorrLen, RestoreMode = RestoreMode::All) =0; virtual ~ContentIdxStore() {}; static std::shared_ptr<ContentIdxStore> Create(); }; }} +namespace o3tl { + template<> struct typed_flags<sw::mark::RestoreMode> : is_typed_flags<sw::mark::RestoreMode, 3> {}; +} + void DelBookmarks(const SwNodeIndex& rStt, const SwNodeIndex& rEnd, std::vector< ::sw::mark::SaveBookmark> * SaveBkmk =nullptr, diff --git a/sw/source/core/layout/ssfrm.cxx b/sw/source/core/layout/ssfrm.cxx index d847bf6f21bd..a6401fd07d7a 100644 --- a/sw/source/core/layout/ssfrm.cxx +++ b/sw/source/core/layout/ssfrm.cxx @@ -444,7 +444,8 @@ void SwTextFrame::RegisterToNode(SwTextNode & rNode) { assert(&rNode != GetDep()); // sw_redlinehide: use New here, because the only caller also calls lcl_ChangeFootnoteRef - m_pMergedPara = sw::CheckParaRedlineMerge(*this, rNode, sw::FrameMode::New); + SwTextNode & rFirstNode(m_pMergedPara ? *m_pMergedPara->pFirstNode : rNode); + m_pMergedPara = sw::CheckParaRedlineMerge(*this, rFirstNode, sw::FrameMode::New); if (!m_pMergedPara) { rNode.Add(this); diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx index a1e14f7a732f..6bd1913e4272 100644 --- a/sw/source/core/txtnode/ndtxt.cxx +++ b/sw/source/core/txtnode/ndtxt.cxx @@ -365,7 +365,7 @@ static void lcl_ChangeFootnoteRef( SwTextNode &rNode ) } SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos, - std::function<void (SwTextNode *)> const*const pContentIndexRestore) + std::function<void (SwTextNode *, sw::mark::RestoreMode)> const*const pContentIndexRestore) { bool parentIsOutline = IsOutline(); @@ -478,7 +478,7 @@ SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos, if (pContentIndexRestore) { // call before making frames and before RegisterToNode - (*pContentIndexRestore)(pNode); + (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys); } SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this); @@ -525,6 +525,10 @@ SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos, } pNode->MakeFramesForAdjacentContentNode(*this); lcl_ChangeFootnoteRef( *this ); + if (pContentIndexRestore) + { // call after making frames + (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys); + } } else { @@ -582,7 +586,7 @@ SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos, if (pContentIndexRestore) { // call before making frames - (*pContentIndexRestore)(pNode); + (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys); } if ( HasWriterListeners() ) @@ -590,6 +594,10 @@ SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos, MakeFramesForAdjacentContentNode(*pNode); } lcl_ChangeFootnoteRef( *pNode ); + if (pContentIndexRestore) + { // call after making frames + (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys); + } } { diff --git a/sw/source/core/undo/untbl.cxx b/sw/source/core/undo/untbl.cxx index e2912ddb2587..d8e8c75f90ae 100644 --- a/sw/source/core/undo/untbl.cxx +++ b/sw/source/core/undo/untbl.cxx @@ -557,12 +557,12 @@ SwTableNode* SwNodes::UndoTableToText( sal_uLong nSttNd, sal_uLong nEndNd, pTextNd->EraseText( aCntPos, 1 ); - std::function<void (SwTextNode *)> restoreFunc( - [&](SwTextNode *const pNewNode) + std::function<void (SwTextNode *, sw::mark::RestoreMode)> restoreFunc( + [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode) { if (!pContentStore->Empty()) { - pContentStore->Restore(*pNewNode, pSave->m_nContent, pSave->m_nContent + 1); + pContentStore->Restore(*pNewNode, pSave->m_nContent, pSave->m_nContent + 1, eMode); } }); pTextNd->SplitContentNode( commit bbf4159638c303059d72b4d1b747de7693b33cca Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Wed Aug 8 16:13:58 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: fix ordering of SplitNode usage of ContentIdxStore The problem is that now the ctor of SwTextFrame will check the redline positions, but the call to MakeFramesForAdjacentContentNode() happens before the call to ContentIdxStore::Restore() that updates the SwPositions of the redlines, hence they point to the wrong node. Try to fix this by not calling Restore directly but pass in a closure to SwTextNode::SplitContentNode() so that it can call ContentIdxStore::Restore() before frames are created and redline positions are checked. Also remove the useless SwContentNode::SplitContentNode() - only the SwTextNode override actually did anything. Change-Id: I2088fd124d04cf354f4f0f691a50ff5217d778d7 diff --git a/sw/inc/ndgrf.hxx b/sw/inc/ndgrf.hxx index a5e4e0cc2969..74c46f20b238 100644 --- a/sw/inc/ndgrf.hxx +++ b/sw/inc/ndgrf.hxx @@ -80,7 +80,6 @@ public: const Graphic& GetGrf(bool bWait = false) const; const GraphicObject& GetGrfObj(bool bWait = false) const; const GraphicObject* GetReplacementGrfObj() const; - virtual SwContentNode *SplitContentNode( const SwPosition & ) override; /// isolated only way to set GraphicObject to allow more actions when doing so void SetGraphic(const Graphic& rGraphic); diff --git a/sw/inc/ndole.hxx b/sw/inc/ndole.hxx index 4f6f4596736d..93139c986526 100644 --- a/sw/inc/ndole.hxx +++ b/sw/inc/ndole.hxx @@ -111,8 +111,6 @@ public: SwOLEObj& GetOLEObj() { return maOLEObj; } virtual ~SwOLENode() override; - virtual SwContentNode *SplitContentNode( const SwPosition & ) override; - /// Is in ndcopy.cxx. virtual SwContentNode* MakeCopy( SwDoc*, const SwNodeIndex& ) const override; diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx index d1a8801eb9c7..a32ea79c310a 100644 --- a/sw/inc/ndtxt.hxx +++ b/sw/inc/ndtxt.hxx @@ -34,6 +34,7 @@ #include <memory> #include <vector> #include <set> +#include <functional> class SfxHint; class SwNumRule; @@ -347,7 +348,8 @@ public: /// Virtual methods from ContentNode. virtual SwContentFrame *MakeFrame( SwFrame* ) override; - virtual SwContentNode *SplitContentNode( const SwPosition & ) override; + SwTextNode * SplitContentNode(const SwPosition &, + std::function<void (SwTextNode *)> const* pContentIndexRestore); virtual SwContentNode *JoinNext() override; void JoinPrev(); diff --git a/sw/inc/node.hxx b/sw/inc/node.hxx index 0c5ee9a9f309..2257446b2f9e 100644 --- a/sw/inc/node.hxx +++ b/sw/inc/node.hxx @@ -387,8 +387,6 @@ public: pSib is another SwFrame of the same layout (e.g. the SwRootFrame itself, a sibling, the parent) */ virtual SwContentFrame *MakeFrame( SwFrame* pSib ) = 0; - virtual SwContentNode *SplitContentNode(const SwPosition & ) = 0; - virtual SwContentNode *JoinNext(); /** Is it possible to join two nodes? In pIdx the second position can be returned. */ diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx index 256fce7bbe4d..acca6b19c0e2 100644 --- a/sw/source/core/doc/DocumentContentOperationsManager.cxx +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -1991,7 +1991,15 @@ bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos, assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex()); assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex()); - pTNd = pTNd->SplitContentNode( rPos )->GetTextNode(); + std::function<void (SwTextNode *)> restoreFunc( + [&](SwTextNode *const) + { + if (!pContentStore->Empty()) + { + pContentStore->Restore(&m_rDoc, pOrigNode->GetIndex()-1, 0, true); + } + }); + pTNd = pTNd->SplitContentNode(rPos, &restoreFunc)->GetTextNode(); //A new node was inserted before the orig pTNd and the content up to //rPos moved into it. The old node is returned with the remainder @@ -2011,9 +2019,6 @@ bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos, aSavePam.GetPoint()->nContent.Assign(pOrigNode, 0); rPos = *aSavePam.GetMark() = *aSavePam.GetPoint(); - if( !pContentStore->Empty() ) - pContentStore->Restore( &m_rDoc, rPos.nNode.GetIndex()-1, 0, true ); - // correct the PaM! if( rPos.nNode == rPaM.GetMark()->nNode ) { @@ -2931,15 +2936,18 @@ bool DocumentContentOperationsManager::SplitNode( const SwPosition &rPos, bool b const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create()); pContentStore->Save( &m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true ); - // FIXME: only SwTextNode has a valid implementation of SplitContentNode! - OSL_ENSURE(pNode->IsTextNode(), "splitting non-text node?"); - pNode = pNode->SplitContentNode( rPos ); + assert(pNode->IsTextNode()); + std::function<void (SwTextNode *)> restoreFunc( + [&](SwTextNode *const) + { + if (!pContentStore->Empty()) + { // move all bookmarks, TOXMarks, FlyAtCnt + pContentStore->Restore(&m_rDoc, rPos.nNode.GetIndex()-1, 0, true); + } + }); + pNode = pNode->GetTextNode()->SplitContentNode(rPos, &restoreFunc); if (pNode) { - // move all bookmarks, TOXMarks, FlyAtCnt - if( !pContentStore->Empty() ) - pContentStore->Restore( &m_rDoc, rPos.nNode.GetIndex()-1, 0, true ); - // To-Do - add 'SwExtraRedlineTable' also ? if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )) { diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx index d5b62a198d0a..ed96fae59ee3 100644 --- a/sw/source/core/docnode/ndtbl.cxx +++ b/sw/source/core/docnode/ndtbl.cxx @@ -1072,10 +1072,16 @@ SwTableNode* SwNodes::TextToTable( const SwNodeRange& rRange, sal_Unicode cCh, if (pTextNd->GetText()[nChPos] == cCh) { aCntPos.nContent = nChPos; - SwContentNode* pNewNd = pTextNd->SplitContentNode( aCntPos ); - - if( !pContentStore->Empty() ) - pContentStore->Restore( *pNewNd, nChPos, nChPos + 1 ); + std::function<void (SwTextNode *)> restoreFunc( + [&](SwTextNode *const pNewNode) + { + if (!pContentStore->Empty()) + { + pContentStore->Restore(*pNewNode, nChPos, nChPos + 1); + } + }); + SwContentNode *const pNewNd = + pTextNd->SplitContentNode(aCntPos, &restoreFunc); // Delete separator and correct search string pTextNd->EraseText( aCntPos.nContent, 1 ); diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx index 0747395460d8..b43e9e67d583 100644 --- a/sw/source/core/docnode/nodes.cxx +++ b/sw/source/core/docnode/nodes.cxx @@ -1512,7 +1512,7 @@ void SwNodes::MoveRange( SwPaM & rPam, SwPosition & rPos, SwNodes& rNodes ) } else { - pDestNd->SplitContentNode( rPos ); + pDestNd->SplitContentNode(rPos, nullptr); } if( rPos.nNode == aEndIdx ) @@ -1577,7 +1577,7 @@ void SwNodes::MoveRange( SwPaM & rPam, SwPosition & rPos, SwNodes& rNodes ) } else { - pDestNd->SplitContentNode( rPos ); + pDestNd->SplitContentNode(rPos, nullptr); } if ( bCorrEnd ) diff --git a/sw/source/core/graphic/ndgrf.cxx b/sw/source/core/graphic/ndgrf.cxx index 63a0c43d946c..d0f5173645c0 100644 --- a/sw/source/core/graphic/ndgrf.cxx +++ b/sw/source/core/graphic/ndgrf.cxx @@ -401,11 +401,6 @@ const GraphicObject* SwGrfNode::GetReplacementGrfObj() const return mpReplacementGraphic.get(); } -SwContentNode *SwGrfNode::SplitContentNode( const SwPosition & ) -{ - return this; -} - SwGrfNode * SwNodes::MakeGrfNode( const SwNodeIndex & rWhere, const OUString& rGrfName, const OUString& rFltName, diff --git a/sw/source/core/ole/ndole.cxx b/sw/source/core/ole/ndole.cxx index e4b6115fd0ec..8d59c502817c 100644 --- a/sw/source/core/ole/ndole.cxx +++ b/sw/source/core/ole/ndole.cxx @@ -242,13 +242,6 @@ const Graphic* SwOLENode::GetGraphic() return nullptr; } -SwContentNode *SwOLENode::SplitContentNode( const SwPosition & ) -{ - // Multiply OLE objects? - OSL_FAIL( "OleNode: can't split." ); - return this; -} - /** * Loading a OLE object that has been moved to the Undo Area */ diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx index 880ce8ea14a3..a1e14f7a732f 100644 --- a/sw/source/core/txtnode/ndtxt.cxx +++ b/sw/source/core/txtnode/ndtxt.cxx @@ -364,7 +364,8 @@ static void lcl_ChangeFootnoteRef( SwTextNode &rNode ) } } -SwContentNode *SwTextNode::SplitContentNode( const SwPosition &rPos ) +SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos, + std::function<void (SwTextNode *)> const*const pContentIndexRestore) { bool parentIsOutline = IsOutline(); @@ -475,6 +476,11 @@ SwContentNode *SwTextNode::SplitContentNode( const SwPosition &rPos ) } + if (pContentIndexRestore) + { // call before making frames and before RegisterToNode + (*pContentIndexRestore)(pNode); + } + SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this); for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) { @@ -574,6 +580,11 @@ SwContentNode *SwTextNode::SplitContentNode( const SwPosition &rPos ) SetSmartTags( pList2, false ); } + if (pContentIndexRestore) + { // call before making frames + (*pContentIndexRestore)(pNode); + } + if ( HasWriterListeners() ) { MakeFramesForAdjacentContentNode(*pNode); diff --git a/sw/source/core/undo/untbl.cxx b/sw/source/core/undo/untbl.cxx index f84dca847425..e2912ddb2587 100644 --- a/sw/source/core/undo/untbl.cxx +++ b/sw/source/core/undo/untbl.cxx @@ -556,10 +556,17 @@ SwTableNode* SwNodes::UndoTableToText( sal_uLong nSttNd, sal_uLong nEndNd, SwIndex aCntPos( pTextNd, pSave->m_nContent - 1 ); pTextNd->EraseText( aCntPos, 1 ); - SwContentNode* pNewNd = pTextNd->SplitContentNode( - SwPosition( aSttIdx, aCntPos )); - if( !pContentStore->Empty() ) - pContentStore->Restore( *pNewNd, pSave->m_nContent, pSave->m_nContent + 1 ); + + std::function<void (SwTextNode *)> restoreFunc( + [&](SwTextNode *const pNewNode) + { + if (!pContentStore->Empty()) + { + pContentStore->Restore(*pNewNode, pSave->m_nContent, pSave->m_nContent + 1); + } + }); + pTextNd->SplitContentNode( + SwPosition(aSttIdx, aCntPos), &restoreFunc); } else { commit 72bdec50dd0ccf536206c4d6940d7b2576ddc169 Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Wed Aug 8 13:29:44 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: adapt SwFlyFrameFormat::MakeFrames() ... ... and SwDrawFrameFormat::MakeFrames() to do nothing if anchored in hidden redline. Change-Id: Idb0668db81b20ee52cd9c0237c22f8fa72beb7b3 diff --git a/sw/source/core/draw/dcontact.cxx b/sw/source/core/draw/dcontact.cxx index 96c0787ae835..100fa538d120 100644 --- a/sw/source/core/draw/dcontact.cxx +++ b/sw/source/core/draw/dcontact.cxx @@ -1837,11 +1837,17 @@ void SwDrawContact::ConnectToLayout( const SwFormatAnchor* pAnch ) // (1) proposed anchor frame isn't a follow and // (2) drawing object isn't a control object to be anchored // in header/footer. - const bool bAdd = ( !pFrame->IsContentFrame() || + bool bAdd = ( !pFrame->IsContentFrame() || !static_cast<SwContentFrame*>(pFrame)->IsFollow() ) && ( !::CheckControlLayer( GetMaster() ) || !pFrame->FindFooterOrHeader() ); + if (bAdd && RndStdIds::FLY_AT_FLY != pAnch->GetAnchorId()) + { + assert(pFrame->IsTextFrame()); + bAdd = IsAnchoredObjShown(*static_cast<SwTextFrame*>(pFrame), *pAnch); + } + if( bAdd ) { if ( RndStdIds::FLY_AT_FLY == pAnch->GetAnchorId() && !pFrame->IsFlyFrame() ) diff --git a/sw/source/core/inc/frmtool.hxx b/sw/source/core/inc/frmtool.hxx index 05b2dc237253..fdc014d8a101 100644 --- a/sw/source/core/inc/frmtool.hxx +++ b/sw/source/core/inc/frmtool.hxx @@ -66,6 +66,8 @@ void RemoveHiddenObjsOfNode(SwTextNode const& rNode, std::vector<sw::Extent>::const_iterator * pIter, std::vector<sw::Extent>::const_iterator const* pEnd); +bool IsAnchoredObjShown(SwTextFrame const& rFrame, SwFormatAnchor const& rAnchor); + void AppendAllObjs(const SwFrameFormats* pTable, const SwFrame* pSib); // draw background with brush or graphics diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx index 98e6538d3c61..3e5abda010f0 100644 --- a/sw/source/core/layout/atrfrm.cxx +++ b/sw/source/core/layout/atrfrm.cxx @@ -56,6 +56,7 @@ #include <rootfrm.hxx> #include <cntfrm.hxx> #include <notxtfrm.hxx> +#include <txtfrm.hxx> #include <crsrsh.hxx> #include <dflyobj.hxx> #include <dcontact.hxx> @@ -3027,7 +3028,24 @@ void SwFlyFrameFormat::MakeFrames() } } - if( pFrame->GetDrawObjs() ) + if (bAdd) + { + switch (aAnchorAttr.GetAnchorId()) + { + case RndStdIds::FLY_AS_CHAR: + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_CHAR: + { + assert(pFrame->IsTextFrame()); + bAdd = IsAnchoredObjShown(*static_cast<SwTextFrame*>(pFrame), aAnchorAttr); + } + break; + default: + break; + } + } + + if (bAdd && pFrame->GetDrawObjs()) { // #i28701# - new type <SwSortedObjs> SwSortedObjs &rObjs = *pFrame->GetDrawObjs(); diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx index 11cb79523ba7..b7fec8eac2aa 100644 --- a/sw/source/core/layout/frmtool.cxx +++ b/sw/source/core/layout/frmtool.cxx @@ -1192,6 +1192,46 @@ void AppendObjs(const SwFrameFormats *const pTable, sal_uLong const nIndex, } } +bool IsAnchoredObjShown(SwTextFrame const& rFrame, SwFormatAnchor const& rAnchor) +{ + assert(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA || + rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR || + rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR); + bool ret(true); + if (auto const pMergedPara = rFrame.GetMergedPara()) + { + ret = false; + auto const pAnchor(rAnchor.GetContentAnchor()); + auto iterFirst(pMergedPara->extents.cbegin()); + auto iter(iterFirst); + SwTextNode const* pNode(pMergedPara->pFirstNode); + for ( ; ; ++iter) + { + if (iter == pMergedPara->extents.end() + || iter->pNode != pNode) + { + assert(pNode->GetRedlineMergeFlag() != SwNode::Merge::Hidden); + if (pNode == &pAnchor->nNode.GetNode()) + { + ret = IsShown(pNode->GetIndex(), rAnchor, &iterFirst, &iter); + break; + } + if (iter == pMergedPara->extents.end()) + { + break; + } + pNode = iter->pNode; + if (pAnchor->nNode.GetIndex() < pNode->GetIndex()) + { + break; + } + iterFirst = iter; + } + } + } + return ret; +} + void AppendAllObjs(const SwFrameFormats* pTable, const SwFrame* pSib) { //Connecting of all Objects, which are described in the SpzTable with the commit 49f9a42436592aaca26899f4884e5c2690d8d1fd Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Tue Aug 7 16:40:02 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: handle flys anchored in flys There is no way to iterate over the nodes-array such that flys are ordered wrt. the flys in whose content they are anchored; this makes it hard to ensure that flys anchored in flys are handled only once. For the Hide implementation, when the flys anchored to a non-first merged SwTextNode in a fly are inserted, ensure that the content of the same fly is skipped if it happens to come later in the nodes-array. For the Show implementation, the ::MakeFrames() would call AppendAllObj() anyway; suppress that and manually call it at the end, which should avoid the problem. Change-Id: I7fb31cf14ef26c095fa7e09edd4ab530add9f253 diff --git a/sw/source/core/inc/frmtool.hxx b/sw/source/core/inc/frmtool.hxx index e692bb900ef6..05b2dc237253 100644 --- a/sw/source/core/inc/frmtool.hxx +++ b/sw/source/core/inc/frmtool.hxx @@ -66,6 +66,8 @@ void RemoveHiddenObjsOfNode(SwTextNode const& rNode, std::vector<sw::Extent>::const_iterator * pIter, std::vector<sw::Extent>::const_iterator const* pEnd); +void AppendAllObjs(const SwFrameFormats* pTable, const SwFrame* pSib); + // draw background with brush or graphics // The 6th parameter indicates that the method should consider background // transparency, saved in the color of the brush item. diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index 617c15d94731..513e8c339f8a 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -40,6 +40,7 @@ #include <txtftn.hxx> #include <fmtftn.hxx> #include <fmtsrnd.hxx> +#include <fmtcntnt.hxx> #include <ftnfrm.hxx> #include <tabfrm.hxx> #include <flyfrms.hxx> @@ -4166,7 +4167,8 @@ void SwRootFrame::InvalidateAllObjPos() } static void UnHideRedlines(SwRootFrame & rLayout, - SwNodes & rNodes, SwNode const& rEndOfSectionNode) + SwNodes & rNodes, SwNode const& rEndOfSectionNode, + std::set<sal_uLong> *const pSkipped) { assert(rEndOfSectionNode.IsEndNode()); for (sal_uLong i = rEndOfSectionNode.StartOfSectionNode()->GetIndex() + 1; @@ -4251,6 +4253,20 @@ static void UnHideRedlines(SwRootFrame & rLayout, pNode->GetIndex(), pFrame, pPage, rTextNode.GetDoc(), &iterFirst, &iter); + if (pSkipped) + { + // if a fly has been added by AppendObjsOfNode, it must be skipped; if not, then it doesn't matter if it's skipped or not because it has no frames and because of that it would be skipped anyway + if (auto const pFlys = pNode->GetAnchoredFlys()) + { + for (auto const pFly : *pFlys) + { + if (pFly->Which() != RES_DRAWFRMFMT) + { + pSkipped->insert(pFly->GetContent().GetContentIdx()->GetIndex()); + } + } + } + } } if (iter == pMerged->extents.end()) { @@ -4302,33 +4318,8 @@ static void UnHideRedlines(SwRootFrame & rLayout, pNode = rExtent.pNode; } } - // add all flys in first node that are hidden - std::vector<sw::Extent> hidden; - sal_Int32 nLast(0); - for (auto const& rExtent : pMergedPara->extents) - { - if (rExtent.pNode != &rTextNode) - { - break; - } - if (rExtent.nStart != 0) - { - assert(rExtent.nStart != nLast); - - hidden.emplace_back(&rTextNode, nLast, rExtent.nStart); - } - nLast = rExtent.nEnd; - } - if (nLast != rTextNode.Len()) - { - hidden.emplace_back(&rTextNode, nLast, rTextNode.Len()); - } - SwFrameFormats& rTable(*rTextNode.GetDoc()->GetSpzFrameFormats()); - auto iterBegin(hidden.cbegin()); - auto const iterEnd(hidden.cend()); - AppendObjsOfNode(&rTable, rTextNode.GetIndex(), pFrame, - pFrame->FindPageFrame(), rTextNode.GetDoc(), - &iterBegin, &iterEnd); + // rely on AppendAllObjs call at the end to add + // all flys in first node that are hidden } pFrame->SetMergedPara(nullptr); // ??? TODO recreate? or is invalidate enough? @@ -4356,6 +4347,7 @@ static void UnHideRedlines(SwRootFrame & rLayout, } else { + assert(!rNode.IsContentNode() || !rNode.GetContentNode()->getLayoutFrame(&rLayout)); sal_uLong j = i + 1; for ( ; j < rEndOfSectionNode.GetIndex(); ++j) { @@ -4368,7 +4360,10 @@ static void UnHideRedlines(SwRootFrame & rLayout, // InsertCnt_ also checks for hidden sections SwNodeIndex const start(rNodes, i); SwNodeIndex const end(rNodes, j); + assert(!bDontCreateObjects); + bDontCreateObjects = true; // suppress here, to be called once ::MakeFrames(rLayout.GetFormat()->GetDoc(), start, end); + bDontCreateObjects = false; i = j - 1; // will be incremented again } } @@ -4376,7 +4371,8 @@ static void UnHideRedlines(SwRootFrame & rLayout, } static void UnHideRedlinesExtras(SwRootFrame & rLayout, - SwNodes & rNodes, SwNode const& rEndOfExtraSectionNode) + SwNodes & rNodes, SwNode const& rEndOfExtraSectionNode, + std::set<sal_uLong> *const pSkipped) { assert(rEndOfExtraSectionNode.IsEndNode()); for (sal_uLong i = rEndOfExtraSectionNode.StartOfSectionNode()->GetIndex() @@ -4386,8 +4382,8 @@ static void UnHideRedlinesExtras(SwRootFrame & rLayout, assert(rStartNode.IsStartNode()); assert(rStartNode.GetRedlineMergeFlag() == SwNode::Merge::None); SwNode const& rEndNode(*rStartNode.EndOfSectionNode()); + bool bSkip(pSkipped ? pSkipped->find(i) != pSkipped->end() : false); i = rEndNode.GetIndex(); - bool bSkip(false); for (sal_uLong j = rStartNode.GetIndex() + 1; j < i; ++j) { // note: SwStartNode has no way to access the frames, so check @@ -4411,7 +4407,7 @@ static void UnHideRedlinesExtras(SwRootFrame & rLayout, } if (!bSkip) { - UnHideRedlines(rLayout, rNodes, rEndNode); + UnHideRedlines(rLayout, rNodes, rEndNode, pSkipped); } } } @@ -4445,12 +4441,25 @@ void SwRootFrame::SetHideRedlines(bool const bHideRedlines) // Flys before footnotes: because footnotes may contain flys but not // vice-versa; alas flys may contain flys, so we skip some of them // if they have already been created from scratch via their anchor flys. - UnHideRedlinesExtras(*this, rNodes, rNodes.GetEndOfAutotext()); + std::set<sal_uLong> skippedFlys; + UnHideRedlinesExtras(*this, rNodes, rNodes.GetEndOfAutotext(), + // when un-hiding, delay all fly frame creation to AppendAllObjs below + IsHideRedlines() ? &skippedFlys : nullptr); // Footnotes are created automatically (after invalidation etc.) by // ConnectFootnote(), but need to be deleted manually. Footnotes do not // occur in flys or headers/footers. - UnHideRedlinesExtras(*this, rNodes, rNodes.GetEndOfInserts()); - UnHideRedlines(*this, rNodes, rNodes.GetEndOfContent()); + UnHideRedlinesExtras(*this, rNodes, rNodes.GetEndOfInserts(), nullptr); + UnHideRedlines(*this, rNodes, rNodes.GetEndOfContent(), nullptr); + + if (!IsHideRedlines()) + { // create all previously hidden flys at once: + // * Flys on first node of pre-existing merged frames that are hidden + // (in delete redline), to be added to the existing frame + // * Flys on non-first (hidden/merged) nodes of pre-existing merged + // frames, to be added to the new frame of their node + // * Flys anchored in other flys that are hidden + AppendAllObjs(rDoc.GetSpzFrameFormats(), this); + } // InvalidateAllContent(SwInvalidateFlags::Size); // ??? TODO what to invalidate? this is the big hammer } commit 1d44635b26060f2495bf82c08906ee3eba25b7c2 Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Mon Aug 6 13:31:37 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: disable hiding at-char flys again Needs changes in Delete for consistency. Change-Id: If9382ebca9b6335ffef8c738840813837316f841 diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx index d0a847e849ef..11cb79523ba7 100644 --- a/sw/source/core/layout/frmtool.cxx +++ b/sw/source/core/layout/frmtool.cxx @@ -1043,7 +1043,10 @@ static bool IsShown(sal_uLong const nIndex, { return false; } - if (pIter && rAnch.GetAnchorId() != RndStdIds::FLY_AT_PARA) + if (pIter && rAnch.GetAnchorId() != RndStdIds::FLY_AT_PARA + // sw_redlinehide: we want to hide AT_CHAR, but currently can't + // because Delete and Accept Redline don't delete them! + && rAnch.GetAnchorId() != RndStdIds::FLY_AT_CHAR) { // note: frames are not sorted by anchor position. assert(pEnd); commit 59ed3c75e960fefeb15d10cf3c791f39150105bd Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Mon Aug 6 12:42:27 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: fix use-after-free in SwFootnoteFrame On hide, the SwTextFootnote::DelFrames() for a note in a deleted frame must actually delete the frames; if there's a follow SwTextFrame, then it might erroneously return without deleting anything. Change-Id: I7e9473b94feaf939be72e285553a8990c2ce1a06 diff --git a/sw/source/core/inc/ftnboss.hxx b/sw/source/core/inc/ftnboss.hxx index a04c8ae172eb..ba363e53a2f7 100644 --- a/sw/source/core/inc/ftnboss.hxx +++ b/sw/source/core/inc/ftnboss.hxx @@ -75,7 +75,7 @@ public: // footnote interface void AppendFootnote( SwContentFrame *, SwTextFootnote * ); - void RemoveFootnote( const SwContentFrame *, const SwTextFootnote *, bool bPrep = true ); + bool RemoveFootnote(const SwContentFrame *, const SwTextFootnote *, bool bPrep = true); static SwFootnoteFrame *FindFootnote( const SwContentFrame *, const SwTextFootnote * ); SwFootnoteContFrame *FindFootnoteCont(); inline const SwFootnoteContFrame *FindFootnoteCont() const; diff --git a/sw/source/core/layout/ftnfrm.cxx b/sw/source/core/layout/ftnfrm.cxx index e34c2603d7bd..591b50dc4af8 100644 --- a/sw/source/core/layout/ftnfrm.cxx +++ b/sw/source/core/layout/ftnfrm.cxx @@ -1641,12 +1641,15 @@ SwFootnoteFrame *SwFootnoteBossFrame::FindFootnote( const SwContentFrame *pRef, return nullptr; } -void SwFootnoteBossFrame::RemoveFootnote( const SwContentFrame *pRef, const SwTextFootnote *pAttr, +bool SwFootnoteBossFrame::RemoveFootnote( + const SwContentFrame *const pRef, const SwTextFootnote *const pAttr, bool bPrep ) { + bool ret(false); SwFootnoteFrame *pFootnote = FindFootnote( pRef, pAttr ); if( pFootnote ) { + ret = true; do { SwFootnoteFrame *pFoll = pFootnote->GetFollow(); @@ -1663,6 +1666,7 @@ void SwFootnoteBossFrame::RemoveFootnote( const SwContentFrame *pRef, const SwTe } } FindPageFrame()->UpdateFootnoteNum(); + return ret; } void SwFootnoteBossFrame::ChangeFootnoteRef( const SwContentFrame *pOld, const SwTextFootnote *pAttr, diff --git a/sw/source/core/txtnode/atrftn.cxx b/sw/source/core/txtnode/atrftn.cxx index 26f41e0333eb..cc0e6f0ea12a 100644 --- a/sw/source/core/txtnode/atrftn.cxx +++ b/sw/source/core/txtnode/atrftn.cxx @@ -444,8 +444,12 @@ void SwTextFootnote::DelFrames( const SwFrame* pSib ) SwPageFrame* pPage = pFnd->FindPageFrame(); if( pPage ) { - pPage->RemoveFootnote( pFnd, this ); - bFrameFnd = true; + // note: we have found the correct frame only if the footnote + // was actually removed; in case this is called from + // SwTextFrame::DestroyImpl(), then that frame isn't connected + // to SwPageFrame any more, and RemoveFootnote on any follow + // must not prevent the fall-back to the !bFrameFnd code. + bFrameFnd = pPage->RemoveFootnote(pFnd, this); } } } commit 330684a4e739a3d21450f1beb3cdb3c833319247 Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Fri Aug 3 18:48:10 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: try to minimise invalidation on show/hide InvalidateAllContent(Size) will basically format every paragraph in the document from scratch; let's try to optimise this a bit by invalidating only the SwTextFrames that actually contain redlines and the SwPageFrames they're on, and also invalidate the position of all flys anchored at these frames as a precautionary measure. Change-Id: I22ed683dc2225992ee48faa6084f277ef8733e8b diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index bc5aadf13375..617c15d94731 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -4208,6 +4208,20 @@ static void UnHideRedlines(SwRootFrame & rLayout, pFrame->SetMergedPara(std::move(pMerged)); } auto const pMerged(pFrame->GetMergedPara()); + if (pMerged) + { + // invalidate SwInvalidateFlags::Size + pFrame->Prepare(PREP_CLEAR, nullptr, false); + pFrame->InvalidatePage(); + if (auto const pObjs = pFrame->GetDrawObjs()) + { // also invalidate position of existing flys + // because they may need to be moved + for (auto const pObject : *pObjs) + { + pObject->InvalidateObjPos(); + } + } + } if (pMerged // do this only *once*, for the *last* frame // otherwise AppendObj would create multiple frames for fly-frames! @@ -4253,6 +4267,16 @@ static void UnHideRedlines(SwRootFrame & rLayout, { if (auto const& pMergedPara = pFrame->GetMergedPara()) { + // invalidate SwInvalidateFlags::Size + pFrame->Prepare(PREP_CLEAR, nullptr, false); + pFrame->InvalidatePage(); + if (auto const pObjs = pFrame->GetDrawObjs()) + { // also invalidate position of existing flys + for (auto const pObject : *pObjs) + { + pObject->InvalidateObjPos(); + } + } // SwFlyAtContentFrame::Modify() always appends to // the master frame, so do the same here. // (RemoveFootnotesForNode must be called at least once) @@ -4428,7 +4452,7 @@ void SwRootFrame::SetHideRedlines(bool const bHideRedlines) UnHideRedlinesExtras(*this, rNodes, rNodes.GetEndOfInserts()); UnHideRedlines(*this, rNodes, rNodes.GetEndOfContent()); - InvalidateAllContent(SwInvalidateFlags::Size); // ??? TODO what to invalidate? +// InvalidateAllContent(SwInvalidateFlags::Size); // ??? TODO what to invalidate? this is the big hammer } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit e0c375bb2622965753ebab0b950a20a0cf383b1c Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Thu Aug 2 17:09:53 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: show/hide flys in redlines The old implementation would not actually hide at-char flys but move the anchor, which is clearly not ideal; better to hide them. Change-Id: If21d0360e04857752a2c84f5329eadfad7818ac8 diff --git a/sw/source/core/inc/frmtool.hxx b/sw/source/core/inc/frmtool.hxx index 0ffe4a75c0f6..e692bb900ef6 100644 --- a/sw/source/core/inc/frmtool.hxx +++ b/sw/source/core/inc/frmtool.hxx @@ -45,6 +45,8 @@ class GraphicAttr; class SwPageDesc; class SwFrameFormats; class SwRegionRects; +class SwTextNode; +namespace sw { struct Extent; } #define FAR_AWAY (SAL_MAX_INT32 - 20000) // initial position of a Fly #define BROWSE_HEIGHT (56700L * 10L) // 10 Meters @@ -55,6 +57,15 @@ class SwRegionRects; void AppendObjs( const SwFrameFormats *pTable, sal_uLong nIndex, SwFrame *pFrame, SwPageFrame *pPage, SwDoc* doc ); +void AppendObjsOfNode(SwFrameFormats const* pTable, sal_uLong nIndex, + SwFrame * pFrame, SwPageFrame * pPage, SwDoc * pDoc, + std::vector<sw::Extent>::const_iterator * pIter, + std::vector<sw::Extent>::const_iterator const* pEnd); + +void RemoveHiddenObjsOfNode(SwTextNode const& rNode, + std::vector<sw::Extent>::const_iterator * pIter, + std::vector<sw::Extent>::const_iterator const* pEnd); + // draw background with brush or graphics // The 6th parameter indicates that the method should consider background // transparency, saved in the color of the brush item. diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx index 9b1ab7417028..d0a847e849ef 100644 --- a/sw/source/core/layout/frmtool.cxx +++ b/sw/source/core/layout/frmtool.cxx @@ -1039,32 +1039,63 @@ static bool IsShown(sal_uLong const nIndex, std::vector<sw::Extent>::const_iterator const*const pEnd) { SwPosition const& rAnchor(*rAnch.GetContentAnchor()); + if (rAnchor.nNode.GetIndex() != nIndex) + { + return false; + } if (pIter && rAnch.GetAnchorId() != RndStdIds::FLY_AT_PARA) { - // TODO are frames sorted by anchor positions perhaps? + // note: frames are not sorted by anchor position. assert(pEnd); assert(rAnch.GetAnchorId() != RndStdIds::FLY_AT_FLY); - for ( ; *pIter != *pEnd; ++*pIter) + for (auto iter = *pIter; iter != *pEnd; ++iter) { - assert((**pIter).pNode->GetIndex() == nIndex); - if ((**pIter).nStart <= rAnchor.nContent.GetIndex()) + assert(iter->pNode->GetIndex() == nIndex); + if (rAnchor.nContent.GetIndex() < iter->nStart) { - // TODO off by one? need < for AS_CHAR but what for AT_CHAR? - if (rAnchor.nContent.GetIndex() < (**pIter).nEnd) - { - return true; - } - else - { - return false; - } + return false; + } + // for AS_CHAR obviously must be < + // for AT_CHAR it is questionable whether < or <= should be used + // and there is the additional corner case of Len() to consider + // prefer < for now for symmetry (and inverted usage with + // "hidden") and handle special case explicitly + if (rAnchor.nContent.GetIndex() < iter->nEnd + || iter->nEnd == iter->pNode->Len()) + { + return true; } } return false; } else { - return rAnch.GetContentAnchor()->nNode.GetIndex() == nIndex; + return true; + } +} + +void RemoveHiddenObjsOfNode(SwTextNode const& rNode, + std::vector<sw::Extent>::const_iterator *const pIter, + std::vector<sw::Extent>::const_iterator const*const pEnd) +{ + std::vector<SwFrameFormat*> const*const pFlys(rNode.GetAnchoredFlys()); + if (!pFlys) + { + return; + } + for (SwFrameFormat * pFrameFormat : *pFlys) + { + SwFormatAnchor const& rAnchor = pFrameFormat->GetAnchor(); + if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR + || (rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR + && RES_DRAWFRMFMT == pFrameFormat->Which())) + { + assert(rAnchor.GetContentAnchor()->nNode.GetIndex() == rNode.GetIndex()); + if (!IsShown(rNode.GetIndex(), rAnchor, pIter, pEnd)) + { + pFrameFormat->DelFrames(); + } + } } } diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index cfe0d11b56c8..bc5aadf13375 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -4182,7 +4182,15 @@ static void UnHideRedlines(SwRootFrame & rLayout, { if (pFrame->getRootFrame() == &rLayout) { - frames.push_back(pFrame); + if (pFrame->IsFollow()) + { + frames.push_back(pFrame); + } // when hiding, the loop must remove the anchored flys + else // *before* resetting SetMergedPara anywhere - else + { // the fly deletion code will access multiple of the + // frames with inconsistent MergedPara and assert + frames.insert(frames.begin(), pFrame); + } } } // this messes with pRegisteredIn so do it outside SwIterator @@ -4194,29 +4202,111 @@ static void UnHideRedlines(SwRootFrame & rLayout, rNode.GetRedlineMergeFlag() == SwNode::Merge::NonFirst); if (rNode.IsCreateFrameWhenHidingRedlines()) { - pFrame->SetMergedPara(CheckParaRedlineMerge(*pFrame, - rTextNode, sw::FrameMode::Existing)); - // ??? TODO flys etc. + { + auto pMerged(CheckParaRedlineMerge(*pFrame, + rTextNode, sw::FrameMode::Existing)); + pFrame->SetMergedPara(std::move(pMerged)); + } + auto const pMerged(pFrame->GetMergedPara()); + if (pMerged + // do this only *once*, for the *last* frame + // otherwise AppendObj would create multiple frames for fly-frames! + && !pFrame->GetFollow()) + { + // add visible flys in non-first node to merged frame + // (hidden flys remain and are deleted via DelFrames()) + SwFrameFormats& rTable(*rTextNode.GetDoc()->GetSpzFrameFormats()); + SwPageFrame *const pPage(pFrame->FindPageFrame()); + std::vector<sw::Extent>::const_iterator iterFirst(pMerged->extents.begin()); + std::vector<sw::Extent>::const_iterator iter(iterFirst); + SwTextNode const* pNode(&rTextNode); + for ( ; ; ++iter) + { + if (iter == pMerged->extents.end() + || iter->pNode != pNode) + { + if (pNode == &rTextNode) + { // remove existing hidden at-char anchored flys + RemoveHiddenObjsOfNode( + rTextNode, &iterFirst, &iter); + } + else + { + // pNode's frame has been deleted by CheckParaRedlineMerge() + AppendObjsOfNode(&rTable, + pNode->GetIndex(), pFrame, pPage, + rTextNode.GetDoc(), + &iterFirst, &iter); + } + if (iter == pMerged->extents.end()) + { + break; + } + pNode = iter->pNode; + iterFirst = iter; + } + } + } } } else { if (auto const& pMergedPara = pFrame->GetMergedPara()) { - // the new text frames don't exist yet, so at this point - // we can only delete the footnote frames so they don't - // point to the merged SwTextFrame any more... - SwTextNode const* pNode(&rTextNode); - for (auto const& rExtent : pMergedPara->extents) + // SwFlyAtContentFrame::Modify() always appends to + // the master frame, so do the same here. + // (RemoveFootnotesForNode must be called at least once) + if (!pFrame->IsFollow()) { - if (rExtent.pNode != pNode) + // the new text frames don't exist yet, so at this point + // we can only delete the footnote frames so they don't + // point to the merged SwTextFrame any more... + SwTextNode const* pNode(&rTextNode); + for (auto const& rExtent : pMergedPara->extents) + { + if (rExtent.pNode != pNode) + { + sw::RemoveFootnotesForNode(*pFrame, *rExtent.pNode, nullptr); + // similarly, remove the anchored flys + if (auto const pFlys = rExtent.pNode->GetAnchoredFlys()) + { + for (SwFrameFormat * pFormat : *pFlys) + { + pFormat->DelFrames(/*&rLayout*/); + } + } + pNode = rExtent.pNode; + } + } + // add all flys in first node that are hidden + std::vector<sw::Extent> hidden; + sal_Int32 nLast(0); + for (auto const& rExtent : pMergedPara->extents) + { + if (rExtent.pNode != &rTextNode) + { + break; + } + if (rExtent.nStart != 0) + { + assert(rExtent.nStart != nLast); + + hidden.emplace_back(&rTextNode, nLast, rExtent.nStart); + } + nLast = rExtent.nEnd; + } + if (nLast != rTextNode.Len()) { - sw::RemoveFootnotesForNode(*pFrame, *rExtent.pNode, nullptr); - pNode = rExtent.pNode; + hidden.emplace_back(&rTextNode, nLast, rTextNode.Len()); } + SwFrameFormats& rTable(*rTextNode.GetDoc()->GetSpzFrameFormats()); + auto iterBegin(hidden.cbegin()); + auto const iterEnd(hidden.cend()); + AppendObjsOfNode(&rTable, rTextNode.GetIndex(), pFrame, + pFrame->FindPageFrame(), rTextNode.GetDoc(), + &iterBegin, &iterEnd); } pFrame->SetMergedPara(nullptr); - // ??? TODO flys etc. // ??? TODO recreate? or is invalidate enough? } } commit 0ea52c21d94830cbdbccb7ce07253622aaf05dae Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Tue Jul 31 19:11:16 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: add layout parameter to *Node::DelFrames A trivial patch to remove some FIXMEs; unfortunately one new FIXME because the SwSectionNode case isn't entirely trivial. Change-Id: I94f11ffd19b189b165ad1fb05488ba8617e81357 diff --git a/sw/inc/node.hxx b/sw/inc/node.hxx index ecc7ab109d0a..0c5ee9a9f309 100644 --- a/sw/inc/node.hxx +++ b/sw/inc/node.hxx @@ -418,10 +418,8 @@ public: /** Method deletes all views of document for the node. The content- frames are removed from the respective layout. - - Add an input param to identify if acc table should be disposed */ - void DelFrames( bool bIsAccTableDispose = true ); + void DelFrames(SwRootFrame const* pLayout = nullptr); /** @return count of elements of node content. Default is 1. There are differences between text node and formula node. */ @@ -508,7 +506,7 @@ public: /** Method deletes all views of document for the node. The content frames are removed from the respective layout. */ - void DelFrames(); + void DelFrames(SwRootFrame const* pLayout = nullptr); /** Method creates all views of the document for the previous node. The content frames that are created are put into the respective layout. */ @@ -556,7 +554,7 @@ public: /** Method deletes all views of document for the node. The content frames are removed from the respective layout. */ - void DelFrames(); + void DelFrames(SwRootFrame const* pLayout = nullptr); /** Method creates all views of document for the previous node. The content frames created are put into the respective layout. */ diff --git a/sw/source/core/docnode/ndsect.cxx b/sw/source/core/docnode/ndsect.cxx index 360722fd6f04..ec6ac343b797 100644 --- a/sw/source/core/docnode/ndsect.cxx +++ b/sw/source/core/docnode/ndsect.cxx @@ -1150,7 +1150,7 @@ void SwSectionNode::MakeOwnFrames(SwNodeIndex* pIdxBehind, SwNodeIndex* pEndIdx) } } -void SwSectionNode::DelFrames() +void SwSectionNode::DelFrames(SwRootFrame const*const /*FIXME TODO*/) { sal_uLong nStt = GetIndex()+1, nEnd = EndOfSectionIndex(); if( nStt >= nEnd ) diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx index 2f2a135d0d39..d5b62a198d0a 100644 --- a/sw/source/core/docnode/ndtbl.cxx +++ b/sw/source/core/docnode/ndtbl.cxx @@ -2425,7 +2425,7 @@ void SwTableNode::MakeOwnFrames(SwNodeIndex* pIdxBehind) } } -void SwTableNode::DelFrames() +void SwTableNode::DelFrames(SwRootFrame const*const pLayout) { /* For a start, cut out and delete the TabFrames (which will also delete the Columns and Rows) The TabFrames are attached to the FrameFormat of the SwTable. @@ -2437,7 +2437,7 @@ void SwTableNode::DelFrames() { bool bAgain = false; { - if ( !pFrame->IsFollow() ) + if (!pFrame->IsFollow() && (!pLayout || pLayout == pFrame->getRootFrame())) { while ( pFrame->HasFollow() ) pFrame->JoinAndDelFollows(); diff --git a/sw/source/core/docnode/node.cxx b/sw/source/core/docnode/node.cxx index b3a5ecc27438..7812c94fdc43 100644 --- a/sw/source/core/docnode/node.cxx +++ b/sw/source/core/docnode/node.cxx @@ -1016,7 +1016,7 @@ SwContentNode::~SwContentNode() // Thus, we need to delete all Frames in the dependency list. if (!IsTextNode()) // see ~SwTextNode { - DelFrames(false); + DelFrames(nullptr); } m_aCondCollListener.EndListeningAll(); @@ -1310,7 +1310,7 @@ void SwContentNode::MakeFramesForAdjacentContentNode(SwContentNode& rNode) * * An input param to identify if the acc table should be disposed. */ -void SwContentNode::DelFrames(bool /*removeme*/) +void SwContentNode::DelFrames(SwRootFrame const*const pLayout) { if( !HasWriterListeners() ) return; @@ -1318,6 +1318,10 @@ void SwContentNode::DelFrames(bool /*removeme*/) SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*this); for( SwContentFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() ) { + if (pLayout && pLayout != pFrame->getRootFrame()) + { + continue; // skip it + } if (pFrame->IsTextFrame()) { if (sw::MergedPara const* pMerged = diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index e5580ac8b2da..cfe0d11b56c8 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -4229,15 +4229,15 @@ static void UnHideRedlines(SwRootFrame & rLayout, if (rNode.IsContentNode()) { // note: no-op for NonFirst nodes, only Hidden will delete - static_cast<SwContentNode&>(rNode).DelFrames(); // FIXME only those in this layout? + static_cast<SwContentNode&>(rNode).DelFrames(&rLayout); } else if (rNode.IsTableNode()) { - static_cast<SwTableNode&>(rNode).DelFrames(); // FIXME only those in this layout? + static_cast<SwTableNode&>(rNode).DelFrames(&rLayout); } else if (rNode.IsSectionNode()) { - static_cast<SwSectionNode&>(rNode).DelFrames(); // FIXME only those in this layout? + static_cast<SwSectionNode&>(rNode).DelFrames(&rLayout); } } else diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx index b9d7afb7da55..5bcb4d1b4350 100644 --- a/sw/source/core/text/redlnitr.cxx +++ b/sw/source/core/text/redlnitr.cxx @@ -149,7 +149,7 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode, // otherwise footnotes cannot be deleted by SwTextFootnote::DelFrames! for (auto iter = ++nodes.begin(); iter != nodes.end(); ++iter) { - (**iter).DelFrames(); // FIXME only those in this layout? + (**iter).DelFrames(rFrame.getRootFrame()); } } auto pRet(o3tl::make_unique<sw::MergedPara>(rFrame, std::move(extents), diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx index d2901a640e11..880ce8ea14a3 100644 --- a/sw/source/core/txtnode/ndtxt.cxx +++ b/sw/source/core/txtnode/ndtxt.cxx @@ -263,7 +263,7 @@ SwTextNode::~SwTextNode() InitSwParaStatistics( false ); - DelFrames(false); // must be called here while it's still a SwTextNode + DelFrames(nullptr); // must be called here while it's still a SwTextNode DelFrames_TextNodePart(); } commit 6c5c3fc5db5ebc9ca2e98b6ab11ff6ad63529b80 Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Tue Jul 31 18:39:36 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: show/hide footnotes in redlines This requires some manual work to delete the footnote frames. Change-Id: I2c5efccdd1e97f26e18402b809ca4f893147cba1 diff --git a/sw/source/core/inc/ftnfrm.hxx b/sw/source/core/inc/ftnfrm.hxx index 7f964d654d4d..c0848948848f 100644 --- a/sw/source/core/inc/ftnfrm.hxx +++ b/sw/source/core/inc/ftnfrm.hxx @@ -23,12 +23,22 @@ #include "layfrm.hxx" class SwContentFrame; +class SwTextFrame; +class SwTextNode; class SwTextFootnote; class SwBorderAttrs; class SwFootnoteFrame; void sw_RemoveFootnotes( SwFootnoteBossFrame* pBoss, bool bPageOnly, bool bEndNotes ); +namespace sw { + +void RemoveFootnotesForNode( + SwTextFrame const& rTextFrame, SwTextNode const& rTextNode, + std::vector<std::pair<sal_Int32, sal_Int32>> const*const pExtents); + +} + // There exists a special section on a page for footnotes. It's called // SwFootnoteContFrame. Each footnote is separated by a SwFootnoteFrame which contains // the paragraphs of a footnote. SwFootnoteFrame can be splitted and will then diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx index d5a38e17a455..3b6860cb4691 100644 --- a/sw/source/core/inc/txtfrm.hxx +++ b/sw/source/core/inc/txtfrm.hxx @@ -93,7 +93,8 @@ struct MergedPara; std::pair<SwTextNode*, sal_Int32> MapViewToModel(MergedPara const&, TextFrameIndex nIndex); TextFrameIndex MapModelToView(MergedPara const&, SwTextNode const* pNode, sal_Int32 nIndex); -std::unique_ptr<sw::MergedPara> CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode); +enum class FrameMode { New, Existing }; +std::unique_ptr<sw::MergedPara> CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode, FrameMode eMode); bool FrameContainsNode(SwContentFrame const& rFrame, sal_uLong nNodeIndex); diff --git a/sw/source/core/layout/ssfrm.cxx b/sw/source/core/layout/ssfrm.cxx index b16643c3e767..d847bf6f21bd 100644 --- a/sw/source/core/layout/ssfrm.cxx +++ b/sw/source/core/layout/ssfrm.cxx @@ -443,7 +443,8 @@ SwContentFrame::~SwContentFrame() void SwTextFrame::RegisterToNode(SwTextNode & rNode) { assert(&rNode != GetDep()); - m_pMergedPara = sw::CheckParaRedlineMerge(*this, rNode); + // sw_redlinehide: use New here, because the only caller also calls lcl_ChangeFootnoteRef + m_pMergedPara = sw::CheckParaRedlineMerge(*this, rNode, sw::FrameMode::New); if (!m_pMergedPara) { rNode.Add(this); diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index 365b8a099ac3..e5580ac8b2da 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -4194,14 +4194,27 @@ static void UnHideRedlines(SwRootFrame & rLayout, rNode.GetRedlineMergeFlag() == SwNode::Merge::NonFirst); if (rNode.IsCreateFrameWhenHidingRedlines()) { - pFrame->SetMergedPara(CheckParaRedlineMerge(*pFrame, rTextNode)); + pFrame->SetMergedPara(CheckParaRedlineMerge(*pFrame, + rTextNode, sw::FrameMode::Existing)); // ??? TODO flys etc. } } else { - if (pFrame->GetMergedPara()) + if (auto const& pMergedPara = pFrame->GetMergedPara()) { + // the new text frames don't exist yet, so at this point + // we can only delete the footnote frames so they don't + // point to the merged SwTextFrame any more... + SwTextNode const* pNode(&rTextNode); + for (auto const& rExtent : pMergedPara->extents) + { + if (rExtent.pNode != pNode) + { + sw::RemoveFootnotesForNode(*pFrame, *rExtent.pNode, nullptr); + pNode = rExtent.pNode; + } + } pFrame->SetMergedPara(nullptr); // ??? TODO flys etc. // ??? TODO recreate? or is invalidate enough? @@ -4215,6 +4228,7 @@ static void UnHideRedlines(SwRootFrame & rLayout, { if (rNode.IsContentNode()) { + // note: no-op for NonFirst nodes, only Hidden will delete static_cast<SwContentNode&>(rNode).DelFrames(); // FIXME only those in this layout? } else if (rNode.IsTableNode()) diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx index aba968a8740f..b9d7afb7da55 100644 --- a/sw/source/core/text/redlnitr.cxx +++ b/sw/source/core/text/redlnitr.cxx @@ -36,6 +36,7 @@ #include <vcl/commandevent.hxx> #include <vcl/settings.hxx> #include <txtfrm.hxx> +#include <ftnfrm.hxx> #include <vcl/svapp.hxx> #include "redlnitr.hxx" #include <extinput.hxx> @@ -47,7 +48,8 @@ using namespace ::com::sun::star; namespace sw { std::unique_ptr<sw::MergedPara> -CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode) +CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode, + FrameMode const eMode) { IDocumentRedlineAccess const& rIDRA = rTextNode.getIDocumentRedlineAccess(); if (!rFrame.getRootFrame()->IsHideRedlines()) @@ -117,6 +119,39 @@ CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode) assert(!mergedText.isEmpty()); pParaPropsNode = extents.begin()->pNode; // para props from first node that isn't empty } + if (eMode == FrameMode::Existing) + { + // remove existing footnote frames for first node; + // for non-first notes, DelFrames will remove all + // (could possibly call lcl_ChangeFootnoteRef, not sure if worth it) + // note: must be done *before* changing listeners! + sal_Int32 nLast(0); + std::vector<std::pair<sal_Int32, sal_Int32>> hidden; + for (auto const& rExtent : extents) + { + if (rExtent.pNode != &rTextNode) + { + break; + } + if (rExtent.nStart != 0) + { + assert(rExtent.nStart != nLast); + hidden.emplace_back(nLast, rExtent.nStart); + } + nLast = rExtent.nEnd; + } + if (nLast != rTextNode.Len()) + { + hidden.emplace_back(nLast, rTextNode.Len()); + } + sw::RemoveFootnotesForNode(rFrame, rTextNode, &hidden); + // unfortunately DelFrames() must be done before StartListening too, + // otherwise footnotes cannot be deleted by SwTextFootnote::DelFrames! + for (auto iter = ++nodes.begin(); iter != nodes.end(); ++iter) + { + (**iter).DelFrames(); // FIXME only those in this layout? + } + } auto pRet(o3tl::make_unique<sw::MergedPara>(rFrame, std::move(extents), mergedText.makeStringAndClear(), pParaPropsNode, &rTextNode)); for (SwTextNode * pTmp : nodes) diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx index d5913ba9b3b4..a548c55ed2cc 100644 --- a/sw/source/core/text/txtfrm.cxx +++ b/sw/source/core/text/txtfrm.cxx @@ -612,7 +612,7 @@ SwTextFrame::SwTextFrame(SwTextNode * const pNode, SwFrame* pSib ) , mnHeightOfLastLine( 0 ) , mnAdditionalFirstLineOffset( 0 ) // note: this may change this->pRegisteredIn to m_pMergedPara->listeners - , m_pMergedPara(CheckParaRedlineMerge(*this, *pNode)) // ensure it is inited + , m_pMergedPara(CheckParaRedlineMerge(*this, *pNode, sw::FrameMode::New)) // ensure it is inited , mnOffset( 0 ) , mnCacheIndex( USHRT_MAX ) , mbLocked( false ) @@ -631,9 +631,16 @@ SwTextFrame::SwTextFrame(SwTextNode * const pNode, SwFrame* pSib ) mnFrameType = SwFrameType::Txt; } -static void RemoveFootnotesForNode( - SwTextFrame const& rTextFrame, SwTextNode const& rTextNode) +namespace sw { + +void RemoveFootnotesForNode( + SwTextFrame const& rTextFrame, SwTextNode const& rTextNode, + std::vector<std::pair<sal_Int32, sal_Int32>> const*const pExtents) { + if (pExtents && pExtents->empty()) + { + return; // nothing to do + } const SwFootnoteIdxs &rFootnoteIdxs = rTextNode.GetDoc()->GetFootnoteIdxs(); size_t nPos = 0; sal_uLong const nIndex = rTextNode.GetIndex(); @@ -645,16 +652,33 @@ static void RemoveFootnotesForNode( if (nPos || &rTextNode != &(rFootnoteIdxs[ nPos ]->GetTextNode())) ++nPos; } - while (nPos < rFootnoteIdxs.size()) + size_t iter(0); + for ( ; nPos < rFootnoteIdxs.size(); ++nPos) { SwTextFootnote* pTextFootnote = rFootnoteIdxs[ nPos ]; if (pTextFootnote->GetTextNode().GetIndex() > nIndex) break; + if (pExtents) + { + while ((*pExtents)[iter].second <= pTextFootnote->GetStart()) + { + ++iter; + if (iter == pExtents->size()) + { + return; + } + } + if (pTextFootnote->GetStart() < (*pExtents)[iter].first) + { + continue; + } + } pTextFootnote->DelFrames( &rTextFrame ); - ++nPos; } } +} // namespace sw + void SwTextFrame::DestroyImpl() { // Remove associated SwParaPortion from s_pTextCache @@ -674,7 +698,7 @@ void SwTextFrame::DestroyImpl() // sw_redlinehide: not sure if it's necessary to check // if the nodes are still alive here, which would require // accessing WriterMultiListener::m_vDepends - RemoveFootnotesForNode(*this, *pNode); + sw::RemoveFootnotesForNode(*this, *pNode, nullptr); } } } @@ -683,7 +707,7 @@ void SwTextFrame::DestroyImpl() SwTextNode *const pNode(static_cast<SwTextNode*>(GetDep())); if (pNode) { - RemoveFootnotesForNode(*this, *pNode); + sw::RemoveFootnotesForNode(*this, *pNode, nullptr); } } } commit ac275cb0ec4108b4a3f043ae0166c960e49fee2b Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Tue Jul 31 17:51:54 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: invalid position from SwTextCursor::GetCursorOfst() The problem here is that nCurrStart is incremented, but then the early return isn't taken, so a position of nCurrStart + nLength is returned, and if the portion is the last in the line it will be beyond the end of the paragraph. (regression from CWS smarttags3) Change-Id: I58a0591202bd664a89c395ea06098eb939a7ed93 diff --git a/sw/source/core/text/itrcrsr.cxx b/sw/source/core/text/itrcrsr.cxx index 4103b8505b77..ea544492a358 100644 --- a/sw/source/core/text/itrcrsr.cxx +++ b/sw/source/core/text/itrcrsr.cxx @@ -1477,8 +1477,10 @@ TextFrameIndex SwTextCursor::GetCursorOfst( SwPosition *pPos, const Point &rPoin { if ( nWidth ) { - // Else we may not enter the character-supplying frame... - if( !( bChgNode && pPos && pPor->IsFlyCntPortion() ) ) + // no quick return for as-character frames, we want to peek inside + if (!(bChgNode && pPos && pPor->IsFlyCntPortion()) + // if we want to get the position inside the field, we should not return + && (!pCMS || !pCMS->m_pSpecialPos)) { if ( pPor->InFieldGrp() || ( pPor->IsMultiPortion() && @@ -1507,9 +1509,7 @@ TextFrameIndex SwTextCursor::GetCursorOfst( SwPosition *pPos, const Point &rPoin && ( bRightAllowed || !bLastHyph )) ++nCurrStart; - // if we want to get the position inside the field, we should not return - if ( !pCMS || !pCMS->m_pSpecialPos ) - return nCurrStart; + return nCurrStart; } } else commit d96ab97a1b2eb4d558f7d240e77edec162b25eb0 Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Tue Jul 31 15:20:00 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: fix use-after-free of SwFont SwTextSizeInfo::m_pFnt may be an alias of either SwAttrIter or SwAttrHandler's SwFont members; keep these alive if they exist when re-initialising from SwAttrIter::Seek(). Change-Id: I8fcbcf3aa339dfc6fa33b5439facadc6034c8cf5 diff --git a/sw/source/core/text/atrstck.cxx b/sw/source/core/text/atrstck.cxx index 345400cede4f..ecae4e4a8385 100644 --- a/sw/source/core/text/atrstck.cxx +++ b/sw/source/core/text/atrstck.cxx @@ -401,8 +401,17 @@ void SwAttrHandler::Init( const SfxPoolItem** pPoolItem, const SwAttrSet* pAS, } // It is possible, that Init is called more than once, e.g., in a - // SwTextFrame::FormatOnceMore situation. - m_pFnt.reset( new SwFont(rFnt) ); + // SwTextFrame::FormatOnceMore situation or (since sw_redlinehide) + // from SwAttrIter::Seek(); in the latter case SwTextSizeInfo::m_pFnt + // is an alias of m_pFnt so it must not be deleted! + if (m_pFnt) + { + *m_pFnt = rFnt; + } + else + { + m_pFnt.reset(new SwFont(rFnt)); + } } void SwAttrHandler::Reset( ) diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx index 0d2a39f5fdad..aba968a8740f 100644 --- a/sw/source/core/text/redlnitr.cxx +++ b/sw/source/core/text/redlnitr.cxx @@ -135,8 +135,18 @@ void SwAttrIter::InitFontAndAttrHandler(SwTextNode const& rTextNode, { // Build a font matching the default paragraph style: SwFontAccess aFontAccess( &rTextNode.GetAnyFormatColl(), m_pViewShell ); - delete m_pFont; - m_pFont = new SwFont( aFontAccess.Get()->GetFont() ); + // It is possible that Init is called more than once, e.g., in a + // SwTextFrame::FormatOnceMore situation or (since sw_redlinehide) + // from SwAttrIter::Seek(); in the latter case SwTextSizeInfo::m_pFnt + // is an alias of m_pFont so it must not be deleted! + if (m_pFont) + { + *m_pFont = aFontAccess.Get()->GetFont(); + } + else + { + m_pFont = new SwFont( aFontAccess.Get()->GetFont() ); + } // set font to vertical if frame layout is vertical // if it's a re-init, the vert flag never changes commit a5838a428d240965e348232b87e94a4fe064a25b Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Tue Jul 31 15:17:14 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: fix crash in SwAttrIter::Seek() There aren't necessarily hints in every merged node. Change-Id: Id83319d8846602b65d9d25b850a8254daf8c54ff diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx index bdc4d88b9f13..0eeeff080669 100644 --- a/sw/source/core/text/itratr.cxx +++ b/sw/source/core/text/itratr.cxx @@ -308,20 +308,23 @@ bool SwAttrIter::Seek(TextFrameIndex const nNewPos) { // Skipping to a different node - first seek until the end of this node // to get rid of all hint items - sal_Int32 nPos(m_nPosition); - do + if (m_pTextNode->GetpSwpHints()) { - nPos = GetNextAttrImpl(m_pTextNode, m_nStartIndex, m_nEndIndex, nPos); - if (nPos <= m_pTextNode->Len()) - { - SeekFwd(nPos); - } - else + sal_Int32 nPos(m_nPosition); + do { - SeekFwd(m_pTextNode->Len()); + nPos = GetNextAttrImpl(m_pTextNode, m_nStartIndex, m_nEndIndex, nPos); + if (nPos <= m_pTextNode->Len()) + { + SeekFwd(nPos); + } + else + { + SeekFwd(m_pTextNode->Len()); + } } + while (nPos < m_pTextNode->Len()); } - while (nPos < m_pTextNode->Len()); assert(m_nChgCnt == 0); // should have reset it all? there cannot be ExtOn() inside of a Delete redline, surely? // Unapply current para items: // the SwAttrHandler doesn't appear to be capable of *unapplying* commit c907f741fbc1a235badd59e8009032dba7d6cc8c Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Tue Jul 31 15:14:23 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: incorrect node returned by MergedAttrIterMulti Change-Id: I0aa83b5902b2e0e4d0c5371cdbf6ce6dccbf6e74 diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx index 13b7207b71d9..80982dd5dcc6 100644 --- a/sw/source/core/text/pormulti.cxx +++ b/sw/source/core/text/pormulti.cxx @@ -875,7 +875,7 @@ namespace sw { rExtent.pNode != m_pMerged->extents[m_CurrentExtent].pNode) { m_CurrentHint = 0; // reset - rpNode = rExtent.pNode; + rpNode = m_pMerged->extents[m_CurrentExtent].pNode; return nullptr; } } commit 0a9017b4acf800559a61c0476c7f527f2e7221c3 Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Fri Jul 27 18:25:59 2018 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Aug 10 18:22:36 2018 +0200 sw_redlinehide_2: improve SwRootFrame::SetHideRedlines() Change-Id: If54585d20bbe0fbf5c071e3a96737015d7d62c05 diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index cdce7870a536..365b8a099ac3 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -30,6 +30,7 @@ #include <viewopt.hxx> #include <IDocumentSettingAccess.hxx> #include <IDocumentFieldsAccess.hxx> +#include <IDocumentRedlineAccess.hxx> #include <fesh.hxx> #include <docsh.hxx> #include <ftninfo.hxx> @@ -4164,37 +4165,22 @@ void SwRootFrame::InvalidateAllObjPos() } } -void SwRootFrame::SetHideRedlines(bool const bHideRedlines) +static void UnHideRedlines(SwRootFrame & rLayout, + SwNodes & rNodes, SwNode const& rEndOfSectionNode) { - if (bHideRedlines == mbHideRedlines) - { - 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) + assert(rEndOfSectionNode.IsEndNode()); + for (sal_uLong i = rEndOfSectionNode.StartOfSectionNode()->GetIndex() + 1; + i < rEndOfSectionNode.GetIndex(); ++i) { - SwNode *const pNode(rNodes[i]); - if (pNode->IsTextNode()) + SwNode & rNode(*rNodes[i]); + if (rNode.IsTextNode()) // only text nodes are 1st node of a merge { - SwTextNode & rTextNode(*pNode->GetTextNode()); + SwTextNode & rTextNode(*rNode.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) + if (pFrame->getRootFrame() == &rLayout) { frames.push_back(pFrame); } @@ -4202,21 +4188,142 @@ void SwRootFrame::SetHideRedlines(bool const bHideRedlines) // this messes with pRegisteredIn so do it outside SwIterator for (SwTextFrame * pFrame : frames) { - if (mbHideRedlines && pNode->IsCreateFrameWhenHidingRedlines()) + if (rLayout.IsHideRedlines()) { - pFrame->SetMergedPara(CheckParaRedlineMerge(*pFrame, rTextNode)); + assert(!pFrame->GetMergedPara() || + rNode.GetRedlineMergeFlag() == SwNode::Merge::NonFirst); + if (rNode.IsCreateFrameWhenHidingRedlines()) + { + pFrame->SetMergedPara(CheckParaRedlineMerge(*pFrame, rTextNode)); + // ??? TODO flys etc. + } } else { if (pFrame->GetMergedPara()) { pFrame->SetMergedPara(nullptr); - rTextNode.DelFrames(); // FIXME only those in this layout? + // ??? TODO flys etc. + // ??? TODO recreate? or is invalidate enough? + } + } + } + } + if (!rNode.IsCreateFrameWhenHidingRedlines()) + { + if (rLayout.IsHideRedlines()) + { + if (rNode.IsContentNode()) + { + static_cast<SwContentNode&>(rNode).DelFrames(); // FIXME only those in this layout? + } + else if (rNode.IsTableNode()) + { + static_cast<SwTableNode&>(rNode).DelFrames(); // FIXME only those in this layout? + } + else if (rNode.IsSectionNode()) + { + static_cast<SwSectionNode&>(rNode).DelFrames(); // FIXME only those in this layout? + } + } + else + { + sal_uLong j = i + 1; + for ( ; j < rEndOfSectionNode.GetIndex(); ++j) + { + if (rNodes[j]->IsCreateFrameWhenHidingRedlines()) + { + break; } } + // call MakeFrames once, because sections/tables + // InsertCnt_ also checks for hidden sections + SwNodeIndex const start(rNodes, i); + SwNodeIndex const end(rNodes, j); + ::MakeFrames(rLayout.GetFormat()->GetDoc(), start, end); + i = j - 1; // will be incremented again + } + } + } +} + +static void UnHideRedlinesExtras(SwRootFrame & rLayout, + SwNodes & rNodes, SwNode const& rEndOfExtraSectionNode) +{ + assert(rEndOfExtraSectionNode.IsEndNode()); + for (sal_uLong i = rEndOfExtraSectionNode.StartOfSectionNode()->GetIndex() + + 1; i < rEndOfExtraSectionNode.GetIndex(); ++i) + { + SwNode const& rStartNode(*rNodes[i]); + assert(rStartNode.IsStartNode()); + assert(rStartNode.GetRedlineMergeFlag() == SwNode::Merge::None); + SwNode const& rEndNode(*rStartNode.EndOfSectionNode()); + i = rEndNode.GetIndex(); + bool bSkip(false); + for (sal_uLong j = rStartNode.GetIndex() + 1; j < i; ++j) + { + // note: SwStartNode has no way to access the frames, so check + // whether the first content-node inside the section has frames + SwNode const& rNode(*rNodes[j]); + if (rNode.IsSectionNode() && + static_cast<SwSectionNode const&>(rNode).GetSection().IsHiddenFlag()) + { // skip hidden sections - they can be inserted in fly-frames :( + j = rNode.EndOfSectionNode()->GetIndex(); + continue; + } + if (rNode.IsContentNode()) + { + SwContentNode const& rCNode(static_cast<SwContentNode const&>(rNode)); + if (!rCNode.getLayoutFrame(&rLayout)) + { // ignore footnote/fly/header/footer with no layout frame + bSkip = true; // they will be created from scratch later if needed + } + break; } } + if (!bSkip) + { + UnHideRedlines(rLayout, rNodes, rEndNode); + } + } +} + +void SwRootFrame::SetHideRedlines(bool const bHideRedlines) +{ + if (bHideRedlines == mbHideRedlines) + { + return; } + mbHideRedlines = bHideRedlines; + SwDoc & rDoc(*GetFormat()->GetDoc()); + if (rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()) + { + return; + } + // Hide->Show: clear MergedPara, create frames ... etc. - the rest is truncated _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits