diff options
author | Michael Stahl <Michael.Stahl@cib.de> | 2018-04-27 18:42:06 +0200 |
---|---|---|
committer | Michael Stahl <Michael.Stahl@cib.de> | 2018-06-08 21:51:20 +0200 |
commit | d5fc4eb1eb77cb7e37741ce65fb5053b126b05a0 (patch) | |
tree | 0cc5a5855212799fdf642b8b6435382dc8aaf938 | |
parent | 2136dc24a16c14138a0cf00b701cc468f4c76d2d (diff) |
sw_redlinehide: SwAttrIter::GetNextAttr() skips over delete redlines
That's a bunch of new code.
Change-Id: Ibb0170bf398c5e09bce75797206710de9b9ee893
-rw-r--r-- | sw/source/core/text/itratr.cxx | 314 | ||||
-rw-r--r-- | sw/source/core/text/itratr.hxx | 9 | ||||
-rw-r--r-- | sw/source/core/text/redlnitr.cxx | 55 | ||||
-rw-r--r-- | sw/source/core/text/redlnitr.hxx | 7 |
4 files changed, 356 insertions, 29 deletions
diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx index d9a9f729c9cc..a558c0f3e70f 100644 --- a/sw/source/core/text/itratr.cxx +++ b/sw/source/core/text/itratr.cxx @@ -24,6 +24,7 @@ #include <hintids.hxx> #include <editeng/charscaleitem.hxx> #include <txtatr.hxx> +#include <svl/itemiter.hxx> #include <sfx2/printer.hxx> #include <svx/svdobj.hxx> #include <vcl/window.hxx> @@ -52,12 +53,14 @@ #include <htmltbl.hxx> #include <swtable.hxx> #include "redlnitr.hxx" +#include <redline.hxx> #include <fmtsrnd.hxx> #include "itrtxt.hxx" #include <breakit.hxx> #include <com/sun/star/i18n/WordType.hpp> #include <com/sun/star/i18n/XBreakIterator.hpp> #include <editeng/lrspitem.hxx> +#include <editeng/rsiditem.hxx> #include <calbck.hxx> using namespace ::com::sun::star::i18n; @@ -284,15 +287,255 @@ bool SwAttrIter::Seek( const sal_Int32 nNewPos ) return m_pFont->IsFntChg(); } -sal_Int32 SwAttrIter::GetNextAttr( ) const +static void InsertCharAttrs(SfxPoolItem const** pAttrs, SfxItemSet const& rItems) +{ + SfxItemIter iter(rItems); + for (SfxPoolItem const* pItem = iter.FirstItem(); pItem; pItem = iter.NextItem()) + { + auto const nWhich(pItem->Which()); + if (isCHRATR(nWhich) && RES_CHRATR_RSID != nWhich) + { + pAttrs[nWhich - RES_CHRATR_BEGIN] = pItem; + } + else if (nWhich == RES_TXTATR_UNKNOWN_CONTAINER) + { + pAttrs[RES_CHRATR_END] = pItem; + } + } +} + +// if return false: portion ends at start of redline, indexes unchanged +// if return true: portion end not known (past end of redline), indexes point to first hint past end of redline +bool CanSkipOverRedline(SwRangeRedline const& rRedline, + size_t & rStartIndex, size_t & rEndIndex) +{ + size_t nStartIndex(rStartIndex); + size_t nEndIndex(rEndIndex); + SwPosition const*const pRLStart(rRedline.Start()); + SwPosition const*const pRLEnd(rRedline.End()); + if (pRLEnd->nContent == pRLEnd->nNode.GetNode().GetTextNode()->Len()) + { + // shortcut: nothing follows redline + // current state is end state + return false; + } + std::vector<SwTextAttr*> activeCharFmts; + // can't compare the SwFont that's stored somewhere, it doesn't have compare + // operator, so try to recreate the situation with some temp arrays here + SfxPoolItem const* activeCharAttrsStart[RES_CHRATR_END - RES_CHRATR_BEGIN + 1] = { nullptr, }; + if (pRLStart->nNode != pRLEnd->nNode) + { // nodes' attributes are only needed if there are different nodes + InsertCharAttrs(activeCharAttrsStart, + pRLStart->nNode.GetNode().GetTextNode()->GetSwAttrSet()); + } + if (SwpHints *const pStartHints = pRLStart->nNode.GetNode().GetTextNode()->GetpSwpHints()) + { + // check hint ends of hints that start before and end within + sal_Int32 const nRedlineEnd(pRLStart->nNode == pRLEnd->nNode + ? pRLEnd->nContent.GetIndex() + : pRLStart->nNode.GetNode().GetTextNode()->Len()); + for ( ; nEndIndex < pStartHints->Count(); ++nEndIndex) + { + SwTextAttr *const pAttr(pStartHints->GetSortedByEnd(nEndIndex)); + if (nRedlineEnd < *pAttr->End()) + { + break; + } + if (!pAttr->End()) + continue; + if (pRLStart->nContent.GetIndex() <= pAttr->GetStart()) + { + continue; + } + if (pAttr->IsFormatIgnoreEnd()) + { + continue; + } + switch (pAttr->Which()) + { + // if any of these ends inside RL then we need a new portion + case RES_TXTATR_REFMARK: + case RES_TXTATR_TOXMARK: + case RES_TXTATR_META: // actually these 2 aren't allowed to overlap ??? + case RES_TXTATR_METAFIELD: + case RES_TXTATR_INETFMT: + case RES_TXTATR_CJK_RUBY: + case RES_TXTATR_INPUTFIELD: + { + return false; // always break + } + break; + // these are guaranteed not to overlap + // and come in order of application + case RES_TXTATR_AUTOFMT: + case RES_TXTATR_CHARFMT: + { + if (pAttr->Which() == RES_TXTATR_CHARFMT) + { + activeCharFmts.push_back(pAttr); + } + // pure formatting hints may end inside the redline & + // start again inside the redline, which must not cause + // a new text portion if they have the same items - so + // store the effective items & compare all at the end + SfxItemSet const& rSet((pAttr->Which() == RES_TXTATR_CHARFMT) + ? static_cast<SfxItemSet const&>(pAttr->GetCharFormat().GetCharFormat()->GetAttrSet()) + : *pAttr->GetAutoFormat().GetStyleHandle().get()); + InsertCharAttrs(activeCharAttrsStart, rSet); + } + break; + // SwTextNode::SetAttr puts it into AUTOFMT which is quite + // sensible so it doesn't actually exist as a hint + case RES_TXTATR_UNKNOWN_CONTAINER: + default: assert(false); + } + } + assert(nEndIndex == pStartHints->Count() || + pRLEnd->nContent.GetIndex() < *pStartHints->GetSortedByEnd(nEndIndex)->GetAnyEnd()); + } + + if (pRLStart->nNode != pRLEnd->nNode) + { + nStartIndex = 0; + nEndIndex = 0; + } + + // treat para properties as text properties + // ... with the FormatToTextAttr we get autofmts that correspond to the *effective* attr set difference + // effective attr set: para props + charfmts + autofmt *in that order* + // ... and the charfmt must be *nominally* the same + + SfxPoolItem const* activeCharAttrsEnd[RES_CHRATR_END - RES_CHRATR_BEGIN + 1] = { nullptr, }; + if (pRLStart->nNode != pRLEnd->nNode) + { // nodes' attributes are only needed if there are different nodes + InsertCharAttrs(activeCharAttrsEnd, + pRLEnd->nNode.GetNode().GetTextNode()->GetSwAttrSet()); + } + + if (SwpHints *const pEndHints = pRLEnd->nNode.GetNode().GetTextNode()->GetpSwpHints()) + { + // check hint starts of hints that start within and end after +#ifndef NDEBUG + sal_Int32 const nRedlineStart(pRLStart->nNode == pRLEnd->nNode + ? pRLStart->nContent.GetIndex() + : 0); +#endif + for ( ; nStartIndex < pEndHints->Count(); ++nStartIndex) + { + SwTextAttr *const pAttr(pEndHints->Get(nStartIndex)); + // compare with < here, not <=, to get the effective formatting + // of the 1st char after the redline; should not cause problems + // with consecutive delete redlines because those are handed by + // GetNextRedln() and here we have the last end pos. + if (pRLEnd->nContent.GetIndex() < pAttr->GetStart()) + { + break; + } + if (!pAttr->End()) + continue; + if (pAttr->IsFormatIgnoreStart()) + { + continue; + } + assert(nRedlineStart <= pAttr->GetStart()); // we wouldn't be here otherwise? + if (*pAttr->End() <= pRLEnd->nContent.GetIndex()) + { + continue; + } + switch (pAttr->Which()) + { + case RES_TXTATR_REFMARK: + case RES_TXTATR_TOXMARK: + case RES_TXTATR_META: // actually these 2 aren't allowed to overlap ??? + case RES_TXTATR_METAFIELD: + case RES_TXTATR_INETFMT: + case RES_TXTATR_CJK_RUBY: + case RES_TXTATR_INPUTFIELD: + { + return false; + } + break; + case RES_TXTATR_AUTOFMT: + case RES_TXTATR_CHARFMT: + { + // char formats must be *nominally* the same + if (pAttr->Which() == RES_TXTATR_CHARFMT) + { + bool isFound(false); + for (auto iter = activeCharFmts.begin(); iter != activeCharFmts.end(); ++iter) + { + if (**iter == *pAttr) + { + activeCharFmts.erase(iter); + isFound = true; + break; + } + } + if (!isFound) + { + return false; + } + } + SfxItemSet const& rSet((pAttr->Which() == RES_TXTATR_CHARFMT) + ? static_cast<SfxItemSet const&>(pAttr->GetCharFormat().GetCharFormat()->GetAttrSet()) + : *pAttr->GetAutoFormat().GetStyleHandle().get()); + InsertCharAttrs(activeCharAttrsEnd, rSet); + + } + break; + // SwTextNode::SetAttr puts it into AUTOFMT which is quite + // sensible so it doesn't actually exist as a hint + case RES_TXTATR_UNKNOWN_CONTAINER: + default: assert(false); + } + } + if (pRLStart->nNode != pRLEnd->nNode) + { + // need to iterate the nEndIndex forward too so the loop in the + // caller can look for the right ends in the next iteration + for (nEndIndex = 0; nEndIndex < pEndHints->Count(); ++nEndIndex) + { + SwTextAttr *const pAttr(pEndHints->GetSortedByEnd(nEndIndex)); + if (!pAttr->End()) + continue; + if (pRLEnd->nContent.GetIndex() < *pAttr->End()) + { + break; + } + } + } + } + + // if we didn't find a matching start for any end, then it really ends inside + if (!activeCharFmts.empty()) + { + return false; + } + for (size_t i = 0; i < SAL_N_ELEMENTS(activeCharAttrsStart); ++i) + { + // all of these are poolable +// assert(!activeCharAttrsStart[i] || activeCharAttrsStart[i]->GetItemPool()->IsItemPoolable(*activeCharAttrsStart[i])); + if (activeCharAttrsStart[i] != activeCharAttrsEnd[i]) + { + return false; + } + } + rStartIndex = nStartIndex; + rEndIndex = nEndIndex; + return true; +} + +static sal_Int32 GetNextAttrImpl(SwTextNode const*const pTextNode, + size_t const nStartIndex, size_t const nEndIndex, + sal_Int32 const nPosition) { sal_Int32 nNext = COMPLETE_STRING; - if( m_pHints ) + if (SwpHints const*const pHints = pTextNode->GetpSwpHints()) { // are there attribute starts left? - for (size_t i = m_nStartIndex; i < m_pHints->Count(); ++i) + for (size_t i = nStartIndex; i < pHints->Count(); ++i) { - SwTextAttr *const pAttr(m_pHints->Get(i)); + SwTextAttr *const pAttr(pHints->Get(i)); if (!pAttr->IsFormatIgnoreStart()) { nNext = pAttr->GetStart(); @@ -300,9 +543,9 @@ sal_Int32 SwAttrIter::GetNextAttr( ) const } } // are there attribute ends left? - for (size_t i = m_nEndIndex; i < m_pHints->Count(); ++i) + for (size_t i = nEndIndex; i < pHints->Count(); ++i) { - SwTextAttr *const pAttr(m_pHints->GetSortedByEnd(i)); + SwTextAttr *const pAttr(pHints->GetSortedByEnd(i)); if (!pAttr->IsFormatIgnoreEnd()) { sal_Int32 const nNextEnd = *pAttr->GetAnyEnd(); @@ -311,11 +554,12 @@ sal_Int32 SwAttrIter::GetNextAttr( ) const } } } - if (m_pTextNode!=nullptr) { + if (pTextNode != nullptr) + { // TODO: maybe use hints like FieldHints for this instead of looking at the text... - const sal_Int32 l = std::min(nNext, m_pTextNode->Len()); - sal_Int32 p=m_nPosition; - const sal_Unicode* aStr = m_pTextNode->GetText().getStr(); + const sal_Int32 l = std::min(nNext, pTextNode->Len()); + sal_Int32 p = nPosition; + const sal_Unicode* aStr = pTextNode->GetText().getStr(); while (p<l) { sal_Unicode aChar = aStr[p]; @@ -329,16 +573,60 @@ sal_Int32 SwAttrIter::GetNextAttr( ) const break; } } - if ((p<l && p>m_nPosition) || nNext<=p) + if ((p < l && nPosition < p) || nNext <= p) nNext=p; else nNext=p+1; } - if( m_pRedline ) - return m_pRedline->GetNextRedln( nNext ); return nNext; } +sal_Int32 SwAttrIter::GetNextAttr() const +{ + size_t nStartIndex(m_nStartIndex); + size_t nEndIndex(m_nEndIndex); + size_t nPosition(m_nPosition); + SwTextNode const* pTextNode(m_pTextNode); + SwRedlineTable::size_type nActRedline(m_pRedline ? m_pRedline->GetAct() : SwRedlineTable::npos); + + while (true) + { + sal_Int32 nNext = GetNextAttrImpl(pTextNode, nStartIndex, nEndIndex, nPosition); + if( m_pRedline ) + { + // TODO refactor this so it can iterate + std::pair<sal_Int32, SwRangeRedline const*> const redline(m_pRedline->GetNextRedln(nNext, pTextNode, nActRedline)); + if (redline.second) + { + if (CanSkipOverRedline(*redline.second, nStartIndex, nEndIndex)) + { + if (&redline.second->End()->nNode.GetNode() != pTextNode) + { + // FIXME when to update the offset? now or when seeking? + const_cast<SwAttrIter*>(this)->m_nCurrentIndexOffset += pTextNode->Len() - redline.first; + // FIXME this needs to sum up *all* prev. nodes? + const_cast<SwAttrIter*>(this)->m_nCurrentIndexOffset = redline.second->End()->nContent.GetIndex() - pTextNode->Len(); + pTextNode = redline.second->End()->nNode.GetNode().GetTextNode(); + nPosition = redline.second->End()->nContent.GetIndex(); + // TODO: reset m_pRedline ... its m_pExt ... + } + else + { + nPosition = redline.second->End()->nContent.GetIndex(); + const_cast<SwAttrIter*>(this)->m_nCurrentIndexOffset += (redline.second->End()->nContent.GetIndex() - redline.first); + } + } + else + return redline.first - m_nCurrentIndexOffset; + } + else + return redline.first - m_nCurrentIndexOffset; + } + else + return nNext - m_nCurrentIndexOffset; + } +} + class SwMinMaxArgs { public: diff --git a/sw/source/core/text/itratr.hxx b/sw/source/core/text/itratr.hxx index fa35a26d7119..abbddcf9f2e3 100644 --- a/sw/source/core/text/itratr.hxx +++ b/sw/source/core/text/itratr.hxx @@ -59,7 +59,12 @@ private: sal_uInt8 m_nPropFont; o3tl::enumarray<SwFontScript, const void*> m_aMagicNo; o3tl::enumarray<SwFontScript, sal_uInt16> m_aFontIdx; + /// input: the current text node const SwTextNode* m_pTextNode; + /// the 1st one, for reset + const SwTextNode* m_pFirstTextNode; + /// from view (text frame) index to current node index + sal_Int32 m_nCurrentIndexOffset; void SeekFwd( const sal_Int32 nPos ); void SetFnt( SwFont* pNew ) { m_pFont = pNew; } @@ -81,6 +86,8 @@ protected: , m_nPosition(0) , m_nPropFont(0) , m_pTextNode(pTextNode) + , m_pFirstTextNode(pTextNode) + , m_nCurrentIndexOffset(0) { m_aMagicNo[SwFontScript::Latin] = m_aMagicNo[SwFontScript::CJK] = m_aMagicNo[SwFontScript::CTL] = nullptr; } @@ -91,6 +98,8 @@ public: /// SwTextFrame in certain special cases via this ctor here SwAttrIter( SwTextNode& rTextNode, SwScriptInfo& rScrInf ) : m_pViewShell(nullptr), m_pFont(nullptr), m_pHints(nullptr), m_pScriptInfo(nullptr), m_pLastOut(nullptr), m_nChgCnt(0), m_pRedline(nullptr),m_nPropFont(0), m_pTextNode(&rTextNode) + , m_pFirstTextNode(&rTextNode) + , m_nCurrentIndexOffset(0) { CtorInitAttrIter( rTextNode, rScrInf ); } virtual ~SwAttrIter(); diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx index 6555a54af18a..2bffb70e6d89 100644 --- a/sw/source/core/text/redlnitr.cxx +++ b/sw/source/core/text/redlnitr.cxx @@ -400,24 +400,57 @@ void SwRedlineItr::Clear_( SwFont* pFnt ) m_Hints.clear(); } -sal_Int32 SwRedlineItr::GetNextRedln_( sal_Int32 nNext ) +// TODO this must be ITERABLE pass in members as parameter +std::pair<sal_Int32, SwRangeRedline const*> +SwRedlineItr::GetNextRedln(sal_Int32 nNext, SwTextNode const*const pNode, SwRedlineTable::size_type & rAct) { + sal_Int32 nStart(m_nStart); + sal_Int32 nEnd(m_nEnd); nNext = NextExtend( nNext ); if (!m_bShow || SwRedlineTable::npos == m_nFirst) - return nNext; - if (SwRedlineTable::npos == m_nAct) + return std::make_pair(nNext, nullptr); + if (SwRedlineTable::npos == rAct) { - m_nAct = m_nFirst; - m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ]->CalcStartEnd(m_nNdIdx, m_nStart, m_nEnd); + rAct = m_nFirst; // TODO??? + m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[rAct]->CalcStartEnd(pNode->GetIndex(), nStart, nEnd); } - if (m_bOn || !m_nStart) + if (rAct != m_nAct) { - if (m_nEnd < nNext) - nNext = m_nEnd; + m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[rAct]->CalcStartEnd(pNode->GetIndex(), nStart, nEnd); } - else if (m_nStart < nNext) - nNext = m_nStart; - return nNext; + if (m_bOn || !nStart) + { + if (nEnd < nNext) + nNext = nEnd; + } + else if (nStart <= nNext) + { + nNext = nStart; + if (!m_bShow) + { + SwRangeRedline const* pRedline = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[rAct]; + if (pRedline->GetType() == nsRedlineType_t::REDLINE_DELETE) + { + ++rAct; + while (rAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size()) + { + SwRangeRedline *const pNext = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[rAct]; + if (pRedline->End() < pNext->Start()) + { + break; // done for now + } + else if (pNext->Start() == pRedline->End() && pNext->GetType() == nsRedlineType_t::REDLINE_DELETE) + { + // consecutive delete - continue + pRedline = pNext; + } + ++rAct; + } + return std::make_pair(nNext, pRedline); + } + } + } + return std::make_pair(nNext, nullptr); } bool SwRedlineItr::ChkSpecialUnderline_() const diff --git a/sw/source/core/text/redlnitr.hxx b/sw/source/core/text/redlnitr.hxx index 8961e4eb9af3..e254abb17ecd 100644 --- a/sw/source/core/text/redlnitr.hxx +++ b/sw/source/core/text/redlnitr.hxx @@ -80,7 +80,6 @@ class SwRedlineItr bool ChkSpecialUnderline_() const; void FillHints( std::size_t nAuthor, RedlineType_t eType ); short Seek_( SwFont& rFnt, sal_Int32 nNew, sal_Int32 nOld ); - sal_Int32 GetNextRedln_( sal_Int32 nNext ); short EnterExtend( SwFont& rFnt, sal_Int32 nNew ) { if (m_pExt) return m_pExt->Enter( rFnt, nNew ); return 0; @@ -94,6 +93,7 @@ public: sal_Int32 nRedlPos, bool bShw, const std::vector<ExtTextInputAttr> *pArr = nullptr, sal_Int32 nExtStart = COMPLETE_STRING ); ~SwRedlineItr() COVERITY_NOEXCEPT_FALSE; + SwRedlineTable::size_type GetAct() const { return m_nAct; } bool IsOn() const { return m_bOn || (m_pExt && m_pExt->IsOn()); } void Clear( SwFont* pFnt ) { if (m_bOn) Clear_( pFnt ); } void ChangeTextAttr( SwFont* pFnt, SwTextAttr const &rHt, bool bChg ); @@ -105,10 +105,7 @@ public: if (m_nAct != m_nFirst) m_nAct = SwRedlineTable::npos; if (m_pExt) m_pExt->Reset(); } - sal_Int32 GetNextRedln( sal_Int32 nNext ) { - if (m_bShow || m_pExt) return GetNextRedln_( nNext ); - return nNext; - } + std::pair<sal_Int32, SwRangeRedline const*> GetNextRedln(sal_Int32 nNext, SwTextNode const* pNode, SwRedlineTable::size_type & rAct); bool ChkSpecialUnderline() const { return IsOn() && ChkSpecialUnderline_(); } bool CheckLine( sal_Int32 nChkStart, sal_Int32 nChkEnd ); |