/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// delete redlines and fieldmarks can't overlap, due to sw::CalcBreaks() // and no combining of adjacent redlines // -> dummy chars are delete-redlined *iff* entire fieldmark is // Note: caller is responsible for checking for immediately adjacent hides bool Next()
{
SwPosition const* pNextRedlineHide(nullptr);
assert(m_pEndPos); if (m_isHideRedlines)
{ // GetRedlinePos() returns npos if there is no redline on the // node but something else could have merged nodes so search again! if (m_RedlineIndex == SwRedlineTable::npos
&& &m_pEndPos->GetNode() != m_pCurrentRedlineNode)
{
m_RedlineIndex = m_rIDRA.GetRedlinePos(m_pEndPos->GetNode(), RedlineType::Any);
m_pCurrentRedlineNode = &m_pEndPos->GetNode();
} // position on current or next redline for (; m_RedlineIndex < m_rIDRA.GetRedlineTable().size(); ++m_RedlineIndex)
{
SwRangeRedline const*const pRed = m_rIDRA.GetRedlineTable()[m_RedlineIndex];
if (m_pEndPos->GetNodeIndex() < pRed->Start()->GetNodeIndex()) break;
if (pRed->GetType() != RedlineType::Delete) continue;
auto [pStart, pEnd] = pRed->StartEnd(); // SwPosition* if (*pStart == *pEnd)
{ // only allowed while moving (either way?) // assert(IDocumentRedlineAccess::IsHideChanges(rIDRA.GetRedlineFlags())); continue;
} if (pStart->GetNode().IsTableNode())
{
assert(pEnd->GetNode() == m_Start.GetNode() && pEnd->GetContentIndex() == 0); continue; // known pathology, ignore it
} if (*m_pEndPos <= *pStart)
{
pNextRedlineHide = pStart; break; // the next one
}
}
}
// position on current or next fieldmark
m_oNextFieldmarkHide.reset(); if (m_eFieldmarkMode != sw::FieldmarkMode::ShowBoth)
{
sal_Unicode const magic(m_eFieldmarkMode == sw::FieldmarkMode::ShowResult
? CH_TXT_ATR_FIELDSTART
: CH_TXT_ATR_FIELDSEP);
SwTextNode* pTextNode = m_pEndPos->GetNode().GetTextNode();
sal_Int32 const nPos = pTextNode ? pTextNode->GetText().indexOf(
magic, m_pEndPos->GetContentIndex()) : -1; if (nPos != -1)
{
m_oNextFieldmarkHide.emplace(*pTextNode, nPos);
sw::mark::Fieldmark const*const pFieldmark(
m_eFieldmarkMode == sw::FieldmarkMode::ShowResult
? m_rIDMA.getFieldmarkAt(*m_oNextFieldmarkHide)
: m_rIDMA.getInnerFieldmarkFor(*m_oNextFieldmarkHide));
assert(pFieldmark);
m_Fieldmark.first = pFieldmark; // for cursor travelling, there should be 2 visible chars; // whichever char is hidden, the cursor travelling needs to // be adapted in any case to skip in some situation or other; // always hide the CH_TXT_ATR_FIELDSEP for now if (m_eFieldmarkMode == sw::FieldmarkMode::ShowResult)
{
m_Fieldmark.second.emplace(
sw::mark::FindFieldSep(*m_Fieldmark.first));
m_Fieldmark.second->AdjustContent(+1);
m_oNextFieldmarkHide->AdjustContent(+1); // skip start
} else
{
m_Fieldmark.second.emplace(pFieldmark->GetMarkEnd());
m_Fieldmark.second->AdjustContent(-1);
}
}
}
// == can happen only if redline starts inside field command, and in // that case redline will end before field separator
assert(!pNextRedlineHide || !m_oNextFieldmarkHide
|| *pNextRedlineHide != *m_oNextFieldmarkHide
|| *m_rIDRA.GetRedlineTable()[m_RedlineIndex]->End() < *m_Fieldmark.second); if (pNextRedlineHide
&& (!m_oNextFieldmarkHide || *pNextRedlineHide < *m_oNextFieldmarkHide))
{
SwRangeRedline const*const pRed(m_rIDRA.GetRedlineTable()[m_RedlineIndex]);
m_pStartPos = pRed->Start();
m_pEndPos = pRed->End();
++m_RedlineIndex; returntrue;
} elseif (m_oNextFieldmarkHide)
{
assert(!pNextRedlineHide || *m_oNextFieldmarkHide <= *pNextRedlineHide);
m_pStartPos = &*m_oNextFieldmarkHide;
m_pEndPos = &*m_Fieldmark.second; returntrue;
} else
{
assert(!pNextRedlineHide && !m_oNextFieldmarkHide); autoconst hasHiddenItem = [](autoconst& rNode) { autoconst& rpSet(rNode.GetAttr(RES_PARATR_LIST_AUTOFMT).GetStyleHandle()); return rpSet ? rpSet->Get(RES_CHRATR_HIDDEN).GetValue() : false;
}; autoconst hasBreakBefore = [](SwTextNode const& rNode) { if (rNode.GetAttr(RES_PAGEDESC).GetPageDesc())
{ returntrue;
} switch (rNode.GetAttr(RES_BREAK).GetBreak())
{ case SvxBreak::ColumnBefore: case SvxBreak::ColumnBoth: case SvxBreak::PageBefore: case SvxBreak::PageBoth: returntrue; default: break;
} returnfalse;
}; autoconst hasBreakAfter = [](SwTextNode const& rNode) { switch (rNode.GetAttr(RES_BREAK).GetBreak())
{ case SvxBreak::ColumnAfter: case SvxBreak::ColumnBoth: case SvxBreak::PageAfter: case SvxBreak::PageBoth: returntrue; default: break;
} returnfalse;
}; if (m_isHideParagraphBreaks
&& m_pEndPos->GetNode().IsTextNode() // ooo27109-1.sxw // only merge if next node is also text node
&& m_pEndPos->GetNodes()[m_pEndPos->GetNodeIndex()+1]->IsTextNode()
&& hasHiddenItem(*m_pEndPos->GetNode().GetTextNode()) // no merge if there's a page break on any node
&& !hasBreakBefore(*m_pEndPos->GetNodes()[m_pEndPos->GetNodeIndex()+1]->GetTextNode()) // first node, see SwTextFrame::GetBreak()
&& !hasBreakAfter(*m_Start.GetNode().GetTextNode()))
{
m_oParagraphBreak.emplace(
SwPosition(*m_pEndPos->GetNode().GetTextNode(), m_pEndPos->GetNode().GetTextNode()->Len()),
SwPosition(*m_pEndPos->GetNodes()[m_pEndPos->GetNodeIndex()+1]->GetTextNode(), 0));
m_pStartPos = &m_oParagraphBreak->first;
m_pEndPos = &m_oParagraphBreak->second; returntrue;
} else// nothing
{
m_pStartPos = nullptr;
m_pEndPos = nullptr; returnfalse;
}
}
}
};
}
namespace sw {
void FindParaPropsNodeIgnoreHidden(sw::MergedPara & rMerged,
sw::ParagraphBreakMode const eMode, SwScriptInfo * pScriptInfo)
{ if (eMode == sw::ParagraphBreakMode::Hidden)
{
::std::optional<SwScriptInfo> oScriptInfo; if (pScriptInfo == nullptr)
{
oScriptInfo.emplace();
pScriptInfo = &*oScriptInfo;
} // always init: when called from SwTextFrame::SwClientNotify() it is stale!
pScriptInfo->InitScriptInfoHidden(*rMerged.pFirstNode, &rMerged);
TextFrameIndex nHiddenStart{COMPLETE_STRING};
TextFrameIndex nHiddenEnd{0};
pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex{0}, nHiddenStart, nHiddenEnd); if (TextFrameIndex{0} == nHiddenStart)
{ if (nHiddenEnd == TextFrameIndex{rMerged.mergedText.getLength()})
{
rMerged.pParaPropsNode = const_cast<SwTextNode*>(rMerged.pLastNode);
} else
{ // this requires MapViewToModel to never return a position at // the end of a node (when all its text is hidden)
rMerged.pParaPropsNode = sw::MapViewToModel(rMerged, nHiddenEnd).first;
} return;
}
} if (!rMerged.extents.empty())
{ // para props from first node that isn't empty (OOo/LO compat)
rMerged.pParaPropsNode = rMerged.extents.begin()->pNode;
} else
{ // if every node is empty, the last one wins (Word compat) // (OOo/LO historically used first one)
rMerged.pParaPropsNode = const_cast<SwTextNode*>(rMerged.pLastNode);
}
}
std::unique_ptr<sw::MergedPara>
CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
FrameMode const eMode)
{ if (!rFrame.getRootFrame()->HasMergedParas())
{ return nullptr;
} bool bHaveRedlines(false);
std::vector<SwTextNode *> nodes{ &rTextNode };
std::vector<SwTableNode *> tables;
std::vector<SwSectionNode *> sections;
std::vector<sw::Extent> extents;
OUStringBuffer mergedText;
SwTextNode * pNode(&rTextNode);
sal_Int32 nLastEnd(0); for (auto iter = HideIterator(rTextNode,
rFrame.getRootFrame()->IsHideRedlines(),
rFrame.getRootFrame()->GetFieldmarkMode(),
rFrame.getRootFrame()->GetParagraphBreakMode());
iter.Next(); )
{
SwPosition const*const pStart(iter.GetStartPos());
SwPosition const*const pEnd(iter.GetEndPos());
bHaveRedlines = true;
assert(pNode != &rTextNode || &pStart->GetNode() == &rTextNode); // detect calls with wrong start node if (pStart->GetContentIndex() != nLastEnd) // not 0 so we eliminate adjacent deletes
{
extents.emplace_back(pNode, nLastEnd, pStart->GetContentIndex());
mergedText.append(pNode->GetText().subView(nLastEnd, pStart->GetContentIndex() - nLastEnd));
} if (&pEnd->GetNode() != pNode)
{ if (pNode == &rTextNode)
{
pNode->SetRedlineMergeFlag(SwNode::Merge::First);
} // else: was already set before int nLevel(0); for (SwNodeOffset j = pNode->GetIndex() + 1; j < pEnd->GetNodeIndex(); ++j)
{
SwNode *const pTmp(pNode->GetNodes()[j]); if (nLevel == 0)
{ if (pTmp->IsTextNode())
{
nodes.push_back(pTmp->GetTextNode());
} elseif (pTmp->IsTableNode())
{
tables.push_back(pTmp->GetTableNode());
} elseif (pTmp->IsSectionNode())
{
sections.push_back(pTmp->GetSectionNode());
}
} if (pTmp->IsStartNode())
{
++nLevel;
} elseif (pTmp->IsEndNode())
{
--nLevel;
}
pTmp->SetRedlineMergeFlag(SwNode::Merge::Hidden);
} // note: in DelLastPara() case, the end node is not actually merged // and is likely a SwTableNode! if (!pEnd->GetNode().IsTextNode())
{
assert(pEnd->GetNode() != pStart->GetNode()); // must set pNode too because it will mark the last node
pNode = nodes.back();
assert(pNode == pNode->GetNodes()[pEnd->GetNodeIndex() - 1]); if (pNode != &rTextNode)
{ // something might depend on last merged one being NonFirst?
pNode->SetRedlineMergeFlag(SwNode::Merge::NonFirst);
}
nLastEnd = pNode->Len();
} else
{
pNode = pEnd->GetNode().GetTextNode();
nodes.push_back(pNode);
pNode->SetRedlineMergeFlag(SwNode::Merge::NonFirst);
nLastEnd = pEnd->GetContentIndex();
}
} else
{
nLastEnd = pEnd->GetContentIndex();
}
} if (pNode == &rTextNode)
{ if (rTextNode.GetRedlineMergeFlag() != SwNode::Merge::None)
{
rTextNode.SetRedlineMergeFlag(SwNode::Merge::None);
}
} // Reset flag of the following text node since we know it's not merged; // also any table/sections in between. // * the following SwTextNode is in same nodes section as pNode (nLevel=0) // * the start nodes that don't have a SwTextNode before them // on their level, and their corresponding end nodes // * the first SwTextNode inside each start node of the previous point // Other (non-first) SwTextNodes in nested sections shouldn't be reset! int nLevel(0); for (SwNodeOffset j = pNode->GetIndex() + 1; j < pNode->GetNodes().Count(); ++j)
{
SwNode *const pTmp(pNode->GetNodes()[j]); if (!pTmp->IsCreateFrameWhenHidingRedlines())
{ // clear stale flag caused by editing with redlines shown
pTmp->SetRedlineMergeFlag(SwNode::Merge::None);
} if (pTmp->IsStartNode())
{
++nLevel;
} elseif (pTmp->IsEndNode())
{ if (nLevel == 0)
{ break; // there is no following text node; avoid leaving section
}
--nLevel;
} elseif (pTmp->IsTextNode())
{ if (nLevel == 0)
{ break; // done
} else
{ // skip everything other than 1st text node in section!
j = pTmp->EndOfSectionIndex() - 1; // will be incremented again
}
}
} if (!bHaveRedlines)
{ if (rTextNode.IsInList() && !rTextNode.GetNum(rFrame.getRootFrame()))
{
rTextNode.AddToListRLHidden(); // try to add it...
} return nullptr;
} if (nLastEnd != pNode->Len())
{
extents.emplace_back(pNode, nLastEnd, pNode->Len());
mergedText.append(pNode->GetText().subView(nLastEnd, pNode->Len() - nLastEnd));
} if (extents.empty()) // there was no text anywhere
{
assert(mergedText.isEmpty());
} else
{
assert(!mergedText.isEmpty());
} auto pRet{std::make_unique<sw::MergedPara>(rFrame, std::move(extents),
mergedText.makeStringAndClear(), &rTextNode, nodes.back())};
FindParaPropsNodeIgnoreHidden(*pRet, rFrame.getRootFrame()->GetParagraphBreakMode(), nullptr);
assert(pRet->pParaPropsNode); // keep lists up to date with visible nodes if (pRet->pParaPropsNode->IsInList() && !pRet->pParaPropsNode->GetNum(rFrame.getRootFrame()))
{
pRet->pParaPropsNode->AddToListRLHidden(); // try to add it...
} for (autoconst pTextNode : nodes)
{ if (pTextNode != pRet->pParaPropsNode)
{
pTextNode->RemoveFromListRLHidden();
}
} if (eMode == FrameMode::Existing)
{ // remove existing footnote frames for first node; // for non-first nodes with own frames, DelFrames will remove all // (could possibly call lcl_ChangeFootnoteRef, not sure if worth it) // note: must be done *before* changing listeners! // for non-first nodes that are already merged with this frame, // need to remove here too, otherwise footnotes can be removed only // by lucky accident, e.g. TruncLines(). auto itExtent(pRet->extents.begin()); for (autoconst pTextNode : nodes)
{
sal_Int32 nLast(0);
std::vector<std::pair<sal_Int32, sal_Int32>> hidden; for ( ; itExtent != pRet->extents.end(); ++itExtent)
{ if (itExtent->pNode != pTextNode)
{ break;
} if (itExtent->nStart != 0)
{
assert(itExtent->nStart != nLast);
hidden.emplace_back(nLast, itExtent->nStart);
}
nLast = itExtent->nEnd;
} if (nLast != pTextNode->Len())
{
hidden.emplace_back(nLast, pTextNode->Len());
}
sw::RemoveFootnotesForNode(*rFrame.getRootFrame(), *pTextNode, &hidden);
} // unfortunately DelFrames() must be done before StartListening too, // otherwise footnotes cannot be deleted by SwTextFootnote::DelFrames! autoconst end(--nodes.rend()); for (auto iter = nodes.rbegin(); iter != end; ++iter)
{
(**iter).DelFrames(rFrame.getRootFrame());
} // also delete tables & sections here; not necessary, but convenient for (autoconst pTableNode : tables)
{
pTableNode->DelFrames(rFrame.getRootFrame());
} for (autoconst pSectionNode : sections)
{
pSectionNode->GetSection().GetFormat()->DelFrames(/*rFrame.getRootFrame()*/);
}
} for (SwTextNode * pTmp : nodes)
{
pRet->listener.StartListening(pTmp);
}
rFrame.EndListeningAll(); return pRet;
}
} // namespace sw
void SwAttrIter::InitFontAndAttrHandler(
SwTextNode const& rPropsNode,
SwTextNode const& rTextNode,
std::u16string_view aText, boolconst*const pbVertLayout, boolconst*const pbVertLayoutLRBT)
{ // Build a font matching the default paragraph style:
SwFontAccess aFontAccess( &rPropsNode.GetAnyFormatColl(), m_pViewShell ); // 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 bool bVertLayoutLRBT = false; if (pbVertLayoutLRBT)
bVertLayoutLRBT = *pbVertLayoutLRBT; if (pbVertLayout ? *pbVertLayout : m_aAttrHandler.IsVertLayout())
{
m_pFont->SetVertical(m_pFont->GetOrientation(), true, bVertLayoutLRBT);
}
// Initialize the default attribute of the attribute handler // based on the attribute array cached together with the font. // If any further attributes for the paragraph are given in pAttrSet // consider them during construction of the default array, and apply // them to the font
m_aAttrHandler.Init(aFontAccess.Get()->GetDefault(), rTextNode.GetpSwAttrSet(),
*rTextNode.getIDocumentSettingAccess(), m_pViewShell, *m_pFont,
pbVertLayout ? *pbVertLayout : m_aAttrHandler.IsVertLayout(),
bVertLayoutLRBT );
void SwAttrIter::CtorInitAttrIter(SwTextNode & rTextNode,
SwScriptInfo & rScriptInfo, SwTextFrame const*const pFrame)
{ // during HTML-Import it can happen, that no layout exists
SwRootFrame* pRootFrame = rTextNode.getIDocumentLayoutAccess().GetCurrentLayout();
m_pViewShell = pRootFrame ? pRootFrame->GetCurrShell() : nullptr;
m_pScriptInfo = &rScriptInfo;
// set font to vertical if frame layout is vertical bool bVertLayout = false; bool bVertLayoutLRBT = false; bool bRTL = false; if ( pFrame )
{ if ( pFrame->IsVertical() )
{
bVertLayout = true;
} if (pFrame->IsVertLRBT())
{
bVertLayoutLRBT = true;
}
bRTL = pFrame->IsRightToLeft();
m_pMergedPara = pFrame->GetMergedPara();
}
// determine script changes if not already done for current paragraph
assert(m_pScriptInfo); if (m_pScriptInfo->GetInvalidityA() != TextFrameIndex(COMPLETE_STRING))
m_pScriptInfo->InitScriptInfo(rTextNode, m_pMergedPara, bRTL);
// sw_redlinehide: this is a Ring - pExtInp is the first PaM that's inside // the node. It's not clear whether there can be more than 1 PaM in the // Ring, and this code doesn't handle that case; neither did the old code. const SwExtTextInput* pExtInp = rDoc.GetExtTextInput( rTextNode ); if (!pExtInp && m_pMergedPara)
{
SwTextNode const* pNode(&rTextNode); for (autoconst& rExtent : m_pMergedPara->extents)
{ if (rExtent.pNode != pNode)
{
pNode = rExtent.pNode;
pExtInp = rDoc.GetExtTextInput(*pNode); if (pExtInp) break;
}
}
} constbool bShow = IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags())
&& pRootFrame && !pRootFrame->IsHideRedlines(); if (!(pExtInp || m_pMergedPara || bShow)) return;
SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, RedlineType::Any ); if (SwRedlineTable::npos == nRedlPos && m_pMergedPara)
{
SwTextNode const* pNode(&rTextNode); for (autoconst& rExtent : m_pMergedPara->extents)
{ // note: have to search because extents based only on Delete if (rExtent.pNode != pNode)
{
pNode = rExtent.pNode;
nRedlPos = rIDRA.GetRedlinePos(*pNode, RedlineType::Any); if (SwRedlineTable::npos != nRedlPos) break;
}
} // TODO this is true initially but after delete ops it may be false... need to delete m_pMerged somewhere? // assert(SwRedlineTable::npos != nRedlPos); // false now with fieldmarks
assert(!pRootFrame
|| pRootFrame->GetFieldmarkMode() != sw::FieldmarkMode::ShowBoth
|| SwRedlineTable::npos != nRedlPos || m_pMergedPara->extents.size() <= 1);
} if (!(pExtInp || m_pMergedPara || SwRedlineTable::npos != nRedlPos)) return;
// The Redline-Iterator // The following information/states exist in RedlineIterator: // // m_nFirst is the first index of RedlineTable, which overlaps with the paragraph. // // m_nAct is the currently active (if m_bOn is set) or the next possible index. // m_nStart and m_nEnd give you the borders of the object within the paragraph. // // If m_bOn is set, the font has been manipulated according to it. // // If m_nAct is set to SwRedlineTable::npos (via Reset()), then currently no // Redline is active, m_nStart and m_nEnd are invalid.
SwRedlineItr::SwRedlineItr( const SwTextNode& rTextNd, SwFont& rFnt,
SwAttrHandler& rAH, sal_Int32 nRed,
Mode const mode, const std::vector<ExtTextInputAttr> *pArr,
SwPosition const*const pExtInputStart)
: m_rDoc( rTextNd.GetDoc() )
, m_rAttrHandler( rAH )
, m_nNdIdx( rTextNd.GetIndex() )
, m_nFirst( nRed )
, m_nAct( SwRedlineTable::npos )
, m_nStart( COMPLETE_STRING )
, m_nEnd( COMPLETE_STRING )
, m_bOn( false )
, m_eMode( mode )
{ if( pArr )
{
assert(pExtInputStart);
m_pExt.reset( new SwExtend(*pArr, pExtInputStart->GetNodeIndex(),
pExtInputStart->GetContentIndex()) );
} else
m_pExt = nullptr;
assert(m_pExt || m_eMode != Mode::Ignore); // only create if necessary
Seek(rFnt, m_nNdIdx, 0, COMPLETE_STRING);
}
/// The return value of SwRedlineItr::Seek tells if the current font /// has been manipulated by leaving (-1) or entering (+1) a range redline short SwRedlineItr::Seek(SwFont& rFnt,
SwNodeOffset const nNode, sal_Int32 const nNew, sal_Int32 const nOld)
{ short nRet = 0; if( ExtOn() ) return 0; // shortcut: if we're within an ExtendTextInputs // there can't be other changes of attributes (not even by redlining) if (m_eMode == Mode::Show)
{ if (m_bOn)
{ if (nNew >= m_nEnd)
{
--nRet;
Clear_( &rFnt ); // We go behind the current range // ++m_nAct; // don't increment, could be in next range too if overlap
} elseif (nNew < m_nStart)
{
--nRet;
Clear_( &rFnt ); // We go before the current range if (m_nAct > m_nFirst)
m_nAct = m_nFirst; // need to start over else return nRet + EnterExtend(rFnt, nNode, nNew); // There's none prior to us
} else return nRet + EnterExtend(rFnt, nNode, nNew); // We stayed in the same range
} if (SwRedlineTable::npos == m_nAct || nOld > nNew)
m_nAct = m_nFirst;
for ( ; m_nAct < rTable.size() ; ++m_nAct)
{
decltype(m_nStart) nStart;
decltype(m_nEnd) nEnd; if (rTable[m_nAct]->CalcStartEnd(nNode, nStart, nEnd))
{ // previous redline intersected nNode but this one precedes it continue;
}
// redline table is sorted, but here it's not the complete redlines
assert(m_nStart == COMPLETE_STRING || m_nStart <= nStart);
assert(m_nStart == COMPLETE_STRING || m_nStart <= nEnd); if (oFirstMatch && nNew < nStart)
{
m_nEnd = std::min(m_nEnd, nStart); break;
} if (nNew < nEnd)
{
m_nStart = nStart;
m_nEnd = std::min(m_nEnd, nEnd); if (nStart <= nNew) // there can be a format and another redline...
{ if (!oFirstMatch)
{
oFirstMatch.emplace(m_nAct);
} const SwRangeRedline *pRed = rTable[ m_nAct ];
if (oFirstMatch)
{
m_bOn = true;
m_nAct = *oFirstMatch; // rewind
++nRet; // increment only once per m_nStart/m_nEnd range
}
} elseif (m_eMode == Mode::Hide)
{ // ... just iterate to update m_nAct for GetNextRedln(); // there is no need to care about formatting in this mode if (m_nAct == SwRedlineTable::npos || nOld == COMPLETE_STRING)
{ // reset, or move backward
m_nAct = m_nFirst;
} for ( ; m_nAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); ++m_nAct)
{ // only Start matters in this mode // Seeks until it finds a RL that starts at or behind the seek pos. // - then update m_nStart/m_nEnd to the intersection of it with the // current node (if any). // The only way to skip to a different node is if there is a Delete // RL, so if there is no intersection we'll never skip again. // Note: here, assume that delete can't nest inside delete!
SwRangeRedline const*const pRedline(
m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[m_nAct]);
SwPosition const*const pStart(pRedline->Start()); if (pRedline->GetType() == RedlineType::Delete
&& (nNode < pStart->GetNodeIndex()
|| (nNode == pStart->GetNodeIndex()
&& nNew <= pStart->GetContentIndex())))
{
pRedline->CalcStartEnd(nNode, m_nStart, m_nEnd); break;
}
m_nStart = COMPLETE_STRING;
m_nEnd = COMPLETE_STRING;
}
} return nRet + EnterExtend(rFnt, nNode, nNew);
}
void SwRedlineItr::FillHints( std::size_t nAuthor, RedlineType eType )
{ switch ( eType )
{ case RedlineType::Insert:
SwModule::get()->GetInsertAuthorAttr(nAuthor, *m_pSet); break; case RedlineType::Delete:
SwModule::get()->GetDeletedAuthorAttr(nAuthor, *m_pSet); break; case RedlineType::Format: case RedlineType::FmtColl: case RedlineType::ParagraphFormat:
SwModule::get()->GetFormatAuthorAttr(nAuthor, *m_pSet); break; default: break;
}
}
/// Ignore mode: does nothing. /// Show mode: returns end of redline if currently in one, or start of next /// Hide mode: returns start of next redline in current node, plus (if it's a /// Delete) its end position and number of consecutive RLs
std::pair<sal_Int32, std::pair<SwRangeRedline const*, size_t>>
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(pNode->GetIndex(), nNext); if (m_eMode == Mode::Ignore || SwRedlineTable::npos == m_nFirst) return std::make_pair(nNext, std::make_pair(nullptr, 0)); if (SwRedlineTable::npos == rAct)
{
rAct = m_nFirst;
} if (rAct != m_nAct)
{ while (rAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size())
{
SwRangeRedline const*const pRedline(
m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[rAct]);
pRedline->CalcStartEnd(pNode->GetIndex(), nStart, nEnd); if (m_eMode != Mode::Hide
|| pRedline->GetType() == RedlineType::Delete)
{ break;
}
++rAct; // Hide mode: search a Delete RL
}
} if (rAct == m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size())
{ return std::make_pair(nNext, std::make_pair(nullptr, 0)); // no Delete here
} if (m_bOn || (m_eMode == Mode::Show && nStart == 0))
{ // in Ignore mode, the end of redlines isn't relevant, except as returned in the second in the pair! if (nEnd < nNext)
nNext = nEnd;
} elseif (nStart <= nNext)
{ if (m_eMode == Mode::Show)
{
nNext = nStart;
} else
{
assert(m_eMode == Mode::Hide);
SwRangeRedline const* pRedline(
m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[rAct]);
assert(pRedline->GetType() == RedlineType::Delete); //? if (pRedline->GetType() == RedlineType::Delete)
{
nNext = nStart;
size_t nSkipped(1); // (consecutive) candidates to be skipped while (rAct + nSkipped <
m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size())
{
SwRangeRedline const*const pNext =
m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[rAct + nSkipped]; if (*pRedline->End() < *pNext->Start())
{ break; // done for now
} elseif (*pNext->Start() == *pRedline->End() &&
pNext->GetType() == RedlineType::Delete)
{ // consecutive delete - continue
pRedline = pNext;
}
++nSkipped;
} return std::make_pair(nNext, std::make_pair(pRedline, nSkipped));
}
}
} return std::make_pair(nNext, std::make_pair(nullptr, 0));
}
bool SwRedlineItr::ChkSpecialUnderline_() const
{ // If the underlining or the escapement is caused by redlining, // we always apply the SpecialUnderlining, i.e. the underlining // below the base line for (SwTextAttr* pHint : m_Hints)
{ const sal_uInt16 nWhich = pHint->Which(); if( RES_CHRATR_UNDERLINE == nWhich ||
RES_CHRATR_ESCAPEMENT == nWhich ) returntrue;
} returnfalse;
}
bool SwRedlineItr::CheckLine(
SwNodeOffset const nStartNode, sal_Int32 const nChkStart,
SwNodeOffset const nEndNode, sal_Int32 nChkEnd, OUString& rRedlineText, bool& bRedlineEnd, RedlineType& eRedlineEnd, size_t* pAuthorAtPos)
{ // note: previously this would return true in the (!m_bShow && m_pExt) // case, but surely that was a bug? if (m_nFirst == SwRedlineTable::npos || m_eMode != Mode::Show) returnfalse; if( nChkEnd == nChkStart && pAuthorAtPos == nullptr ) // empty lines look one char further
++nChkEnd;
sal_Int32 nOldStart = m_nStart;
sal_Int32 nOldEnd = m_nEnd;
SwRedlineTable::size_type const nOldAct = m_nAct; bool bRet = bRedlineEnd = false;
eRedlineEnd = RedlineType::None;
SwPosition const start(*m_rDoc.GetNodes()[nStartNode]->GetContentNode(), nChkStart);
SwPosition const end(*m_rDoc.GetNodes()[nEndNode]->GetContentNode(), nChkEnd);
SwRangeRedline const* pPrevRedline = nullptr; bool isBreak(false); for (m_nAct = m_nFirst; m_nAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); ++m_nAct)
{
SwRangeRedline const*const pRedline(
m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ] ); // collect text of the hidden redlines at the end of the line bool isExtendText(false); switch (ComparePosition(*pRedline->Start(), *pRedline->End(), start, end))
{ case SwComparePosition::Behind:
isBreak = true; break; case SwComparePosition::OverlapBehind: case SwComparePosition::CollideStart: case SwComparePosition::Outside: case SwComparePosition::Equal: // store redlining at line end (for line break formatting)
eRedlineEnd = pRedline->GetType();
bRedlineEnd = true;
isBreak = true; if (pAuthorAtPos)
*pAuthorAtPos = pRedline->GetAuthor();
[[fallthrough]]; case SwComparePosition::OverlapBefore: case SwComparePosition::CollideEnd: case SwComparePosition::Inside:
{
bRet = true; // start to collect text of invisible redlines for ChangesInMargin layout if (rRedlineText.isEmpty() && !pRedline->IsVisible())
{
rRedlineText = pRedline->GetDescr(/*bSimplified=*/true);
pPrevRedline = pRedline;
isExtendText = true;
} // join the text of the next invisible redlines in the same position // i.e. characters deleted by pressing backspace or delete elseif (pPrevRedline && !pRedline->IsVisible() &&
*pRedline->Start() == *pPrevRedline->Start() && *pRedline->End() == *pPrevRedline->End() )
{
OUString sExtendText(pRedline->GetDescr(/*bSimplified=*/true)); if (!sExtendText.isEmpty())
{ if (rRedlineText.getLength() < 12)
{ // TODO: remove extra space from GetDescr(true), // but show deletion of paragraph or line break
rRedlineText = rRedlineText +
pRedline->GetDescr(/*bSimplified=*/true).subView(1);
} else
rRedlineText = OUString::Concat(rRedlineText.subView(0, rRedlineText.getLength() - 3)) + "...";
}
isExtendText = true;
} break;
} case SwComparePosition::Before: break; // -Werror=switch
} if (isBreak && !isExtendText)
{ break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.