summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Stahl <Michael.Stahl@cib.de>2018-04-27 18:42:06 +0200
committerMichael Stahl <Michael.Stahl@cib.de>2018-06-08 21:51:20 +0200
commitd5fc4eb1eb77cb7e37741ce65fb5053b126b05a0 (patch)
tree0cc5a5855212799fdf642b8b6435382dc8aaf938
parent2136dc24a16c14138a0cf00b701cc468f4c76d2d (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.cxx314
-rw-r--r--sw/source/core/text/itratr.hxx9
-rw-r--r--sw/source/core/text/redlnitr.cxx55
-rw-r--r--sw/source/core/text/redlnitr.hxx7
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 );