/* -*- 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 .
*/
// Special case for SvxFontItem: only compare the name staticbool CmpAttr( const SfxPoolItem& rItem1, const SfxPoolItem& rItem2 )
{ switch( rItem1.Which() )
{ case RES_CHRATR_FONT: return rItem1.StaticWhichCast(RES_CHRATR_FONT).GetFamilyName() == rItem2.StaticWhichCast(RES_CHRATR_FONT).GetFamilyName();
case RES_CHRATR_COLOR: return rItem1.StaticWhichCast(RES_CHRATR_COLOR).GetValue().IsRGBEqual(rItem2.StaticWhichCast(RES_CHRATR_COLOR).GetValue()); case RES_PAGEDESC:
::std::optional<sal_uInt16> const oNumOffset1 = rItem1.StaticWhichCast(RES_PAGEDESC).GetNumOffset();
::std::optional<sal_uInt16> const oNumOffset2 = rItem2.StaticWhichCast(RES_PAGEDESC).GetNumOffset();
rPam.GetPoint()->SetContent( nStart );
rPam.SetMark(); // Point == GetMark
// Point points to end of search area or end of attribute if( pEnd )
{ if( bTstEnd && *pEnd > nContentPos )
rPam.GetPoint()->SetContent(nContentPos); else
rPam.GetPoint()->SetContent(*pEnd);
}
}
// TODO: provide documentation /** search for a text attribute
This function searches in a text node for a given attribute. If that is found then the SwPaM contains the section that surrounds the attribute (w.r.t. the search area).
@param rTextNd Text node to search in. @param rPam ??? @param rCmpItem ??? @param fnMove ??? @return Returns <true> if found, <false> otherwise.
*/ staticbool lcl_SearchAttr( const SwTextNode& rTextNd, SwPaM& rPam, const SfxPoolItem& rCmpItem,
SwMoveFnCollection const & fnMove)
{ if ( !rTextNd.HasHints() ) returnfalse;
staticbool
lcl_IsAttributeIgnorable(sal_Int32 const nNdStart, sal_Int32 const nNdEnd,
SwSrchChrAttr const& rTmp)
{ // #i115528#: if there is a paragraph attribute, it has been added by the // SwAttrCheckArr ctor, and nFound is 1. // if the paragraph is entirely covered by hints that override the paragraph // attribute, then this function must find an attribute to decrement nFound! // so check for an empty search range, let attributes that start/end there // cover it, and hope for the best... return ((nNdEnd == nNdStart)
? ((rTmp.nEnd < nNdStart) || (nNdEnd < rTmp.nStt))
: ((rTmp.nEnd <= nNdStart) || (nNdEnd <= rTmp.nStt)));
}
// first delete all up to start position that are already invalid
SwSrchChrAttr* pArrPtr; if( m_nFound ) for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen;
++n, ++pArrPtr ) if( pArrPtr->nWhich && pArrPtr->nEnd <= aTmp.nStt )
{
pArrPtr->nWhich = 0; // deleted
m_nFound--;
}
// delete all up to start position that are already invalid and // move all "open" ones (= stick out over start position) from stack // into FndSet if( m_nStackCount ) for( pArrPtr = m_pStackArr, n=0; n < m_nArrLen; ++n, ++pArrPtr )
{ if( !pArrPtr->nWhich ) continue;
if( SfxItemState::INVALID == eState )
{ // Will the attribute become valid? if( !CmpAttr( m_aComapeSet.GetPool()->GetUserOrPoolDefaultItem( nWhch ),
*pTmpItem ))
{ // search attribute and extend if needed
pCmp = &m_pFindArr[ nWhch - m_nArrStart ]; if( !pCmp->nWhich )
{
*pCmp = aTmp; // not found, insert
m_nFound++;
} elseif( pCmp->nEnd < aTmp.nEnd ) // extend?
pCmp->nEnd = aTmp.nEnd;
bContinue = true;
}
} // Will the attribute become valid? elseif( CmpAttr( *pItem, *pTmpItem ) )
{
m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
++m_nFound;
bContinue = true;
}
// then is has to go on the stack if( !bContinue )
{
pCmp = &m_pFindArr[ nWhch - m_nArrStart ]; if (pCmp->nWhich )
{ // exists on stack, only if it is even bigger if( pCmp->nEnd > aTmp.nEnd )
{
OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich, "slot on stack is still in use" );
// first delete all up to start position that are already invalid
SwSrchChrAttr* pArrPtr; if( m_nFound ) for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr ) if( pArrPtr->nWhich && pArrPtr->nStt >= aTmp.nEnd )
{
pArrPtr->nWhich = 0; // deleted
m_nFound--;
}
// delete all up to start position that are already invalid and // move all "open" ones (= stick out over start position) from stack // into FndSet if( m_nStackCount ) for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
{ if( !pArrPtr->nWhich ) continue;
bContinue = true;
}
} // Will the attribute become valid? elseif( CmpAttr( *pItem, *pTmpItem ))
{
m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
++m_nFound;
bContinue = true;
}
// then is has to go on the stack if( !bContinue )
{
pCmp = &m_pFindArr[ nWhch - m_nArrStart ]; if( pCmp->nWhich )
{ // exists on stack, only if it is even bigger if( pCmp->nStt < aTmp.nStt )
{
OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich, "slot on stack is still in use" );
// if at beginning/end then move it out of the node if( bSrchForward
? oPam->GetPoint()->GetContentIndex() == oPam->GetPointContentNode()->Len()
: !oPam->GetPoint()->GetContentIndex() )
{ if( !(*fnMove.fnPos)( oPam->GetPoint(), false ))
{ returnfalse;
}
SwContentNode *pNd = oPam->GetPointContentNode();
oPam->GetPoint()->SetContent( bSrchForward ? 0 : pNd->Len() );
}
while (nullptr != (pNode = ::GetNode(*oPam, bFirst, fnMove, bInReadOnly, pLayout)))
{ if( bCharAttr )
{ if( !pNode->IsTextNode() ) // CharAttr are only in text nodes continue;
SwTextFrame const*const pFrame(pLayout
? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
: nullptr); if (pFrame)
{
SwTextNode const* pAttrNode(nullptr);
SwTextAttr const* pAttr(nullptr); if (bSrchForward)
{
sw::MergedAttrIter iter(*pFrame); do
{
pAttr = iter.NextAttr(&pAttrNode);
} while (pAttr
&& (pAttrNode->GetIndex() < oPam->GetPoint()->GetNodeIndex()
|| (pAttrNode->GetIndex() == oPam->GetPoint()->GetNodeIndex()
&& pAttr->GetStart() < oPam->GetPoint()->GetContentIndex())
|| pAttr->Which() != nWhich));
} else
{
sw::MergedAttrIterReverse iter(*pFrame); do
{
pAttr = iter.PrevAttr(&pAttrNode);
} while (pAttr
&& (oPam->GetPoint()->GetNodeIndex() < pAttrNode->GetIndex()
|| (oPam->GetPoint()->GetNodeIndex() == pAttrNode->GetIndex()
&& oPam->GetPoint()->GetContentIndex() <= pAttr->GetStart())
|| pAttr->Which() != nWhich));
} if (pAttr)
{
assert(pAttrNode);
oPam->GetPoint()->Assign(*pAttrNode);
lcl_SetAttrPam(*oPam, pAttr->GetStart(), pAttr->End(), bSrchForward);
bFound = true; break;
}
} elseif (!pLayout && pNode->GetTextNode()->HasHints() &&
lcl_SearchAttr(*pNode->GetTextNode(), *oPam, rAttr, fnMove))
{
bFound = true;
} if (bFound)
{ // set to the values of the attribute
rSearchPam.SetMark();
*rSearchPam.GetPoint() = *oPam->GetPoint();
*rSearchPam.GetMark() = *oPam->GetMark(); break;
} elseif (isTXTATR(nWhich)) continue;
}
#if 0 // no hard attribution, so check if node was asked for this attr before if( !pNode->HasSwAttrSet() )
{
SwFormat* pTmpFormat = pNode->GetFormatColl(); if( !aFormatArr.insert( pTmpFormat ).second ) continue; // collection was requested earlier
}
if( SfxItemState::SET == pNode->GetSwAttrSet().GetItemState( nWhich, true, &pItem ))
{ // FORWARD: SPoint at the end, GetMark at the beginning of the node // BACKWARD: SPoint at the beginning, GetMark at the end of the node // always: incl. start and incl. end
*rSearchPam.GetPoint() = *pPam->GetPoint();
rSearchPam.SetMark();
rSearchPam.GetPoint()->SetContent(pNode->Len());
bFound = true; break;
} #endif
}
// if backward search, switch point and mark if( bFound && !bSrchForward )
rSearchPam.Exchange();
// if at beginning/end then move it out of the node if( bMoveFirst &&
( bSrchForward
? oPam->GetPoint()->GetContentIndex() == oPam->GetPointContentNode()->Len()
: !oPam->GetPoint()->GetContentIndex() ) )
{ if( !(*fnMove.fnPos)( oPam->GetPoint(), false ))
{ returnfalse;
}
SwContentNode *pNd = oPam->GetPointContentNode();
oPam->GetPoint()->SetContent( bSrchForward ? 0 : pNd->Len() );
}
while (nullptr != (pNode = ::GetNode(*oPam, bFirst, fnMove, bInReadOnly, pLayout)))
{
SwTextFrame const*const pFrame(pLayout && pNode->IsTextNode()
? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
: nullptr);
assert(!pLayout || !pNode->IsTextNode() || pFrame); // sw_redlinehide: it's apparently not possible to find break items // with the UI, so checking one node is enough
SwContentNode const& rPropsNode(*(pFrame
? pFrame->GetTextNodeForParaProps()
: pNode));
if( aCmpArr.Count() )
{ if( !pNode->IsTextNode() ) // CharAttr are only in text nodes continue;
if (aOtherSet.Count() &&
!lcl_Search(rPropsNode, aOtherSet, bNoColls))
{ continue;
}
sw::MergedPara const*const pMergedPara(pFrame ? pFrame->GetMergedPara() : nullptr); if (pMergedPara)
{
SwPosition const& rStart(*oPam->Start());
SwPosition const& rEnd(*oPam->End()); // no extents? fall back to searching index 0 of propsnode // to find its node items if (pMergedPara->extents.empty())
{ if (rStart.GetNodeIndex() <= rPropsNode.GetIndex()
&& rPropsNode.GetIndex() <= rEnd.GetNodeIndex())
{
SwPaM tmp(rPropsNode, 0, rPropsNode, 0);
bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, tmp); if (bFound)
{
*oPam = tmp;
}
}
} else
{ // iterate the extents, and intersect with input pPam: // the found ranges should never include delete redlines // so that subsequent Replace will not affect them for (size_t i = 0; i < pMergedPara->extents.size(); ++i)
{ autoconst rExtent(pMergedPara->extents[bSrchForward
? i
: pMergedPara->extents.size() - i - 1]); if (rExtent.pNode->GetIndex() < rStart.GetNodeIndex()
|| rEnd.GetNodeIndex() < rExtent.pNode->GetIndex())
{ continue;
}
sal_Int32 const nStart(rExtent.pNode == &rStart.GetNode()
? rStart.GetContentIndex()
: 0); if (rExtent.nEnd <= nStart)
{ continue;
}
sal_Int32 const nEnd(rExtent.pNode == &rEnd.GetNode()
? rEnd.GetContentIndex()
: rExtent.pNode->Len()); if (nEnd < rExtent.nStart
|| (nStart != nEnd && nEnd == rExtent.nStart))
{ continue;
}
SwPaM tmp(*rExtent.pNode, std::max(nStart, rExtent.nStart),
*rExtent.pNode, std::min(nEnd, rExtent.nEnd));
tmp.Normalize(bSrchForward);
bFound = (*fnSearch)(*rExtent.pNode, aCmpArr, tmp); if (bFound)
{
*oPam = tmp; break;
}
}
}
} else
{
bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, *oPam);
} if (bFound)
{ // set to the values of the attribute
rSearchPam.SetMark();
*rSearchPam.GetPoint() = *oPam->GetPoint();
*rSearchPam.GetMark() = *oPam->GetMark(); break;
} continue; // text attribute
}
if( !aOtherSet.Count() ) continue;
// no hard attribution, so check if node was asked for this attr before // (as an optimisation) if (!rPropsNode.HasSwAttrSet())
{
SwFormat* pTmpFormat = rPropsNode.GetFormatColl(); if( !aFormatArr.insert( pTmpFormat ).second ) continue; // collection was requested earlier
}
if (lcl_Search(rPropsNode, aOtherSet, bNoColls))
{ // FORWARD: SPoint at the end, GetMark at the beginning of the node // BACKWARD: SPoint at the beginning, GetMark at the end of the node if (pFrame)
{
*rSearchPam.GetPoint() = *oPam->GetPoint();
rSearchPam.SetMark();
*rSearchPam.GetMark() = pFrame->MapViewToModelPos(
TextFrameIndex(bSrchForward ? pFrame->GetText().getLength() : 0));
} else
{
*rSearchPam.GetPoint() = *oPam->GetPoint();
rSearchPam.SetMark(); if (bSrchForward)
{
rSearchPam.GetPoint()->SetContent(pNode->Len());
} else
{
rSearchPam.GetPoint()->SetContent(0);
}
}
bFound = true; break;
}
}
// in search direction, mark precedes point, because the next iteration // starts at point if (bFound)
{
rSearchPam.Normalize(!bSrchForward);
}
return bFound;
}
namespace {
/// parameters for search for attributes struct SwFindParaAttr : public SwFindParas
{ bool m_bNoCollection; const SfxItemSet *pSet, *pReplSet; const i18nutil::SearchOptions2 *pSearchOpt;
SwCursor& m_rCursor;
SwRootFrame const* m_pLayout;
std::unique_ptr<utl::TextSearch> pSText;
int SwFindParaAttr::DoFind(SwPaM & rCursor, SwMoveFnCollection const & fnMove, const SwPaM & rRegion, bool bInReadOnly,
std::unique_ptr<SvxSearchItem>& xSearchItem)
{ // replace string (only if text given and search is not parameterized)? bool bReplaceText = pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
!pSet->Count() ); bool bReplaceAttr = pReplSet && pReplSet->Count(); bool bMoveFirst = !bReplaceAttr; if( bInReadOnly && (bReplaceAttr || bReplaceText ))
bInReadOnly = false;
// We search for attributes, should we search for text as well?
{
SwPaM aRegion( *rRegion.GetMark(), *rRegion.GetPoint() );
SwPaM* pTextRegion = &aRegion;
SwPaM aSrchPam( *rCursor.GetPoint() );
while( true )
{ if( pSet->Count() ) // any attributes?
{ // first attributes if (!FindAttrsImpl(aSrchPam, *pSet, m_bNoCollection, fnMove, aRegion, bInReadOnly, bMoveFirst, m_pLayout)) return FIND_NOT_FOUND;
bMoveFirst = true;
if( !pSearchOpt ) break; // ok, only attributes, so found
// add to shell-cursor-ring so that the regions will be moved eventually
SwPaM* pPrevRing(nullptr); if( bRegExp )
{
pPrevRing = const_cast<SwPaM &>(rRegion).GetPrev(); const_cast<SwPaM &>(rRegion).GetRingContainer().merge( m_rCursor.GetRingContainer() );
}
if( bRegExp )
{ // and remove region again
SwPaM* p;
SwPaM* pNext = const_cast<SwPaM*>(&rRegion); do {
p = pNext;
pNext = p->GetNext();
p->MoveTo(const_cast<SwPaM*>(&rRegion));
} while( p != pPrevRing );
}
rSttCntPos.SetContent(nSttCnt);
}
if( bReplaceAttr )
{ // is the selection still existent? // all searched attributes are reset to default if // they are not in ReplaceSet if( !pSet->Count() )
{
rCursor.GetDoc().getIDocumentContentOperations().InsertItemSet(
rCursor, *pReplSet, SetAttrMode::DEFAULT, m_pLayout);
} else
{
SfxItemPool* pPool = pReplSet->GetPool();
SfxItemSet aSet( *pPool, pReplSet->GetRanges() );
SfxItemIter aIter( *pSet ); const SfxPoolItem* pItem = aIter.GetCurItem(); do
{ // reset all that are not set with pool defaults if( !IsInvalidItem( pItem ) && SfxItemState::SET !=
pReplSet->GetItemState( pItem->Which(), false ))
aSet.Put( pPool->GetUserOrPoolDefaultItem( pItem->Which() ));
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.