1464 lines
49 KiB
C++
1464 lines
49 KiB
C++
/* -*- 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 .
|
|
*/
|
|
|
|
#include <com/sun/star/lang/Locale.hpp>
|
|
#include <com/sun/star/util/SearchAlgorithms2.hpp>
|
|
#include <com/sun/star/util/SearchFlags.hpp>
|
|
#include <i18nlangtag/languagetag.hxx>
|
|
#include <i18nutil/searchopt.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <unotools/syslocale.hxx>
|
|
#include <hintids.hxx>
|
|
#include <svl/itemiter.hxx>
|
|
#include <svl/srchitem.hxx>
|
|
#include <svl/whiter.hxx>
|
|
#include <editeng/colritem.hxx>
|
|
#include <editeng/fontitem.hxx>
|
|
#include <fmtpdsc.hxx>
|
|
#include <txatbase.hxx>
|
|
#include <charfmt.hxx>
|
|
#include <crsrsh.hxx>
|
|
#include <doc.hxx>
|
|
#include <IDocumentUndoRedo.hxx>
|
|
#include <IDocumentState.hxx>
|
|
#include <swcrsr.hxx>
|
|
#include <ndtxt.hxx>
|
|
#include <pamtyp.hxx>
|
|
#include <txtfrm.hxx>
|
|
#include <swundo.hxx>
|
|
#include <optional>
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::lang;
|
|
using namespace ::com::sun::star::util;
|
|
|
|
// Special case for SvxFontItem: only compare the name
|
|
static bool 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();
|
|
|
|
if (oNumOffset1 != oNumOffset2)
|
|
return false;
|
|
|
|
return rItem1.StaticWhichCast(RES_PAGEDESC).GetPageDesc() == rItem2.StaticWhichCast(RES_PAGEDESC).GetPageDesc();
|
|
}
|
|
return rItem1 == rItem2;
|
|
}
|
|
|
|
const SwTextAttr* GetFrwrdTextHint( const SwpHints& rHtsArr, size_t& rPos,
|
|
sal_Int32 nContentPos )
|
|
{
|
|
while( rPos < rHtsArr.Count() )
|
|
{
|
|
const SwTextAttr *pTextHt = rHtsArr.Get( rPos++ );
|
|
// the start of an attribute has to be in the section
|
|
if( pTextHt->GetStart() >= nContentPos )
|
|
return pTextHt; // valid text attribute
|
|
}
|
|
return nullptr; // invalid text attribute
|
|
}
|
|
|
|
const SwTextAttr* GetBkwrdTextHint( const SwpHints& rHtsArr, size_t& rPos,
|
|
sal_Int32 nContentPos )
|
|
{
|
|
while( rPos > 0 )
|
|
{
|
|
const SwTextAttr *pTextHt = rHtsArr.Get( --rPos );
|
|
// the start of an attribute has to be in the section
|
|
if( pTextHt->GetStart() < nContentPos )
|
|
return pTextHt; // valid text attribute
|
|
}
|
|
return nullptr; // invalid text attribute
|
|
}
|
|
|
|
static void lcl_SetAttrPam( SwPaM& rPam, sal_Int32 nStart, const sal_Int32* pEnd,
|
|
const bool bSaveMark )
|
|
{
|
|
sal_Int32 nContentPos;
|
|
if( bSaveMark )
|
|
nContentPos = rPam.GetMark()->GetContentIndex();
|
|
else
|
|
nContentPos = rPam.GetPoint()->GetContentIndex();
|
|
bool bTstEnd = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode();
|
|
|
|
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.
|
|
*/
|
|
static bool lcl_SearchAttr( const SwTextNode& rTextNd, SwPaM& rPam,
|
|
const SfxPoolItem& rCmpItem,
|
|
SwMoveFnCollection const & fnMove)
|
|
{
|
|
if ( !rTextNd.HasHints() )
|
|
return false;
|
|
|
|
const SwTextAttr *pTextHt = nullptr;
|
|
bool bForward = &fnMove == &fnMoveForward;
|
|
size_t nPos = bForward ? 0 : rTextNd.GetSwpHints().Count();
|
|
sal_Int32 nContentPos = rPam.GetPoint()->GetContentIndex();
|
|
|
|
while( nullptr != ( pTextHt=(*fnMove.fnGetHint)(rTextNd.GetSwpHints(),nPos,nContentPos)))
|
|
if (pTextHt->Which() == rCmpItem.Which())
|
|
{
|
|
lcl_SetAttrPam( rPam, pTextHt->GetStart(), pTextHt->End(), bForward );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// search for multiple text attributes
|
|
struct SwSrchChrAttr
|
|
{
|
|
sal_uInt16 nWhich;
|
|
sal_Int32 nStt;
|
|
sal_Int32 nEnd;
|
|
|
|
SwSrchChrAttr(): nWhich(0), nStt(0), nEnd(0) {}
|
|
|
|
SwSrchChrAttr( const SfxPoolItem& rItem,
|
|
sal_Int32 nStart, sal_Int32 nAnyEnd )
|
|
: nWhich( rItem.Which() ), nStt( nStart ), nEnd( nAnyEnd )
|
|
{}
|
|
};
|
|
|
|
class SwAttrCheckArr
|
|
{
|
|
SwSrchChrAttr *m_pFindArr, *m_pStackArr;
|
|
sal_Int32 m_nNodeStart;
|
|
sal_Int32 m_nNodeEnd;
|
|
sal_uInt16 m_nArrStart, m_nArrLen;
|
|
sal_uInt16 m_nFound, m_nStackCount;
|
|
SfxItemSet m_aComapeSet;
|
|
bool m_bNoColls;
|
|
bool m_bForward;
|
|
|
|
public:
|
|
SwAttrCheckArr( const SfxItemSet& rSet, bool bForward, bool bNoCollections );
|
|
~SwAttrCheckArr();
|
|
|
|
void SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam );
|
|
|
|
/// how many attributes are there in total?
|
|
sal_uInt16 Count() const { return m_aComapeSet.Count(); }
|
|
bool Found() const { return m_nFound == m_aComapeSet.Count(); }
|
|
bool CheckStack();
|
|
|
|
sal_Int32 Start() const;
|
|
sal_Int32 End() const;
|
|
|
|
sal_Int32 GetNdStt() const { return m_nNodeStart; }
|
|
sal_Int32 GetNdEnd() const { return m_nNodeEnd; }
|
|
|
|
bool SetAttrFwd( const SwTextAttr& rAttr );
|
|
bool SetAttrBwd( const SwTextAttr& rAttr );
|
|
};
|
|
|
|
}
|
|
|
|
SwAttrCheckArr::SwAttrCheckArr( const SfxItemSet& rSet, bool bFwd,
|
|
bool bNoCollections )
|
|
: m_nNodeStart(0)
|
|
, m_nNodeEnd(0)
|
|
, m_nFound(0)
|
|
, m_nStackCount(0)
|
|
, m_aComapeSet( *rSet.GetPool(), svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END-1> )
|
|
, m_bNoColls(bNoCollections)
|
|
, m_bForward(bFwd)
|
|
{
|
|
m_aComapeSet.Put( rSet, false );
|
|
|
|
// determine area of Fnd/Stack array (Min/Max)
|
|
sal_uInt16 nMinUsedWhichID(0);
|
|
sal_uInt16 nMaxUsedWhichID(0);
|
|
|
|
if (0 != m_aComapeSet.Count())
|
|
{
|
|
nMinUsedWhichID = 5000; // SFX_WHICH_MAX+1;
|
|
for (SfxItemIter aIter(m_aComapeSet); !aIter.IsAtEnd(); aIter.NextItem())
|
|
{
|
|
const sal_uInt16 nCurrentWhich(aIter.GetCurWhich());
|
|
if (SfxItemPool::IsSlot(nCurrentWhich))
|
|
continue;
|
|
nMinUsedWhichID = std::min(nMinUsedWhichID, nCurrentWhich);
|
|
nMaxUsedWhichID = std::max(nMaxUsedWhichID, nCurrentWhich);
|
|
}
|
|
|
|
if (nMinUsedWhichID > nMaxUsedWhichID)
|
|
nMinUsedWhichID = nMaxUsedWhichID = 0;
|
|
}
|
|
|
|
m_nArrStart = nMinUsedWhichID;//m_aComapeSet.GetWhichByOffset( aIter.GetFirstPos() );
|
|
m_nArrLen = nMaxUsedWhichID - nMinUsedWhichID + 1;//m_aComapeSet.GetWhichByOffset( aIter.GetLastPos() ) - m_nArrStart+1;
|
|
|
|
char* pFndChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
|
|
char* pStackChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
|
|
|
|
m_pFindArr = reinterpret_cast<SwSrchChrAttr*>(pFndChar);
|
|
m_pStackArr = reinterpret_cast<SwSrchChrAttr*>(pStackChar);
|
|
}
|
|
|
|
SwAttrCheckArr::~SwAttrCheckArr()
|
|
{
|
|
delete[] reinterpret_cast<char*>(m_pFindArr);
|
|
delete[] reinterpret_cast<char*>(m_pStackArr);
|
|
}
|
|
|
|
void SwAttrCheckArr::SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam )
|
|
{
|
|
std::fill(m_pFindArr, m_pFindArr + m_nArrLen, SwSrchChrAttr());
|
|
std::fill(m_pStackArr, m_pStackArr + m_nArrLen, SwSrchChrAttr());
|
|
m_nFound = 0;
|
|
m_nStackCount = 0;
|
|
|
|
if( m_bForward )
|
|
{
|
|
m_nNodeStart = rPam.GetPoint()->GetContentIndex();
|
|
m_nNodeEnd = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode()
|
|
? rPam.GetMark()->GetContentIndex()
|
|
: rTextNd.GetText().getLength();
|
|
}
|
|
else
|
|
{
|
|
m_nNodeEnd = rPam.GetPoint()->GetContentIndex();
|
|
m_nNodeStart = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode()
|
|
? rPam.GetMark()->GetContentIndex()
|
|
: 0;
|
|
}
|
|
|
|
if( m_bNoColls && !rTextNd.HasSwAttrSet() )
|
|
return ;
|
|
|
|
const SfxItemSet& rSet = rTextNd.GetSwAttrSet();
|
|
|
|
SfxItemIter aIter( m_aComapeSet );
|
|
const SfxPoolItem* pItem = aIter.GetCurItem();
|
|
const SfxPoolItem* pFndItem;
|
|
sal_uInt16 nWhich;
|
|
|
|
do
|
|
{
|
|
if( IsInvalidItem( pItem ) )
|
|
{
|
|
nWhich = aIter.GetCurWhich();
|
|
if( RES_TXTATR_END <= nWhich )
|
|
break; // end of text attributes
|
|
|
|
if( SfxItemState::SET == rSet.GetItemState( nWhich, !m_bNoColls, &pFndItem )
|
|
&& !CmpAttr( *pFndItem, rSet.GetPool()->GetUserOrPoolDefaultItem( nWhich ) ))
|
|
{
|
|
m_pFindArr[ nWhich - m_nArrStart ] =
|
|
SwSrchChrAttr( *pFndItem, m_nNodeStart, m_nNodeEnd );
|
|
m_nFound++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nWhich = pItem->Which();
|
|
if( RES_TXTATR_END <= nWhich )
|
|
break; // end of text attributes
|
|
|
|
if( CmpAttr( rSet.Get( nWhich, !m_bNoColls ), *pItem ) )
|
|
{
|
|
m_pFindArr[ nWhich - m_nArrStart ] =
|
|
SwSrchChrAttr( *pItem, m_nNodeStart, m_nNodeEnd );
|
|
m_nFound++;
|
|
}
|
|
}
|
|
|
|
pItem = aIter.NextItem();
|
|
} while (pItem);
|
|
}
|
|
|
|
static bool
|
|
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)));
|
|
}
|
|
|
|
bool SwAttrCheckArr::SetAttrFwd( const SwTextAttr& rAttr )
|
|
{
|
|
SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
|
|
|
|
// ignore all attributes not in search range
|
|
if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
|
|
{
|
|
return Found();
|
|
}
|
|
|
|
const SfxPoolItem* pItem;
|
|
// here we explicitly also search in character templates
|
|
sal_uInt16 nWhch = rAttr.Which();
|
|
std::optional<SfxWhichIter> oIter;
|
|
const SfxPoolItem* pTmpItem = nullptr;
|
|
const SfxItemSet* pSet = nullptr;
|
|
if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
|
|
{
|
|
if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
|
|
return Found();
|
|
pTmpItem = nullptr;
|
|
pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
|
|
if ( pSet )
|
|
{
|
|
oIter.emplace( *pSet );
|
|
nWhch = oIter->FirstWhich();
|
|
while( nWhch &&
|
|
SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
|
|
nWhch = oIter->NextWhich();
|
|
if( !nWhch )
|
|
pTmpItem = nullptr;
|
|
}
|
|
}
|
|
else
|
|
pTmpItem = &rAttr.GetAttr();
|
|
|
|
while( pTmpItem )
|
|
{
|
|
SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
|
|
if( SfxItemState::INVALID == eState || SfxItemState::SET == eState )
|
|
{
|
|
sal_uInt16 n;
|
|
SwSrchChrAttr* pCmp;
|
|
|
|
// 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( pArrPtr->nEnd <= aTmp.nStt )
|
|
{
|
|
pArrPtr->nWhich = 0; // deleted
|
|
if( !--m_nStackCount )
|
|
break;
|
|
}
|
|
else if( pArrPtr->nStt <= aTmp.nStt )
|
|
{
|
|
pCmp = &m_pFindArr[ n ];
|
|
if( pCmp->nWhich )
|
|
{
|
|
if( pCmp->nEnd < pArrPtr->nEnd ) // extend
|
|
pCmp->nEnd = pArrPtr->nEnd;
|
|
}
|
|
else
|
|
{
|
|
*pCmp = *pArrPtr;
|
|
m_nFound++;
|
|
}
|
|
pArrPtr->nWhich = 0;
|
|
if( !--m_nStackCount )
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool bContinue = false;
|
|
|
|
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++;
|
|
}
|
|
else if( pCmp->nEnd < aTmp.nEnd ) // extend?
|
|
pCmp->nEnd = aTmp.nEnd;
|
|
|
|
bContinue = true;
|
|
}
|
|
}
|
|
// Will the attribute become valid?
|
|
else if( 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" );
|
|
|
|
if( aTmp.nStt <= pCmp->nStt )
|
|
pCmp->nStt = aTmp.nEnd;
|
|
else
|
|
pCmp->nEnd = aTmp.nStt;
|
|
|
|
m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
|
|
m_nStackCount++;
|
|
}
|
|
pCmp->nWhich = 0;
|
|
m_nFound--;
|
|
}
|
|
}
|
|
}
|
|
if( oIter )
|
|
{
|
|
assert(pSet && "otherwise no oIter");
|
|
nWhch = oIter->NextWhich();
|
|
while( nWhch &&
|
|
SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
|
|
nWhch = oIter->NextWhich();
|
|
if( !nWhch )
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
oIter.reset();
|
|
return Found();
|
|
}
|
|
|
|
bool SwAttrCheckArr::SetAttrBwd( const SwTextAttr& rAttr )
|
|
{
|
|
SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
|
|
|
|
// ignore all attributes not in search range
|
|
if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
|
|
{
|
|
return Found();
|
|
}
|
|
|
|
const SfxPoolItem* pItem;
|
|
// here we explicitly also search in character templates
|
|
sal_uInt16 nWhch = rAttr.Which();
|
|
std::optional<SfxWhichIter> oIter;
|
|
const SfxPoolItem* pTmpItem = nullptr;
|
|
const SfxItemSet* pSet = nullptr;
|
|
if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
|
|
{
|
|
if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
|
|
return Found();
|
|
|
|
pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
|
|
if ( pSet )
|
|
{
|
|
oIter.emplace( *pSet );
|
|
nWhch = oIter->FirstWhich();
|
|
while( nWhch &&
|
|
SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
|
|
nWhch = oIter->NextWhich();
|
|
if( !nWhch )
|
|
pTmpItem = nullptr;
|
|
}
|
|
}
|
|
else
|
|
pTmpItem = &rAttr.GetAttr();
|
|
|
|
while( pTmpItem )
|
|
{
|
|
SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
|
|
if( SfxItemState::INVALID == eState || SfxItemState::SET == eState )
|
|
{
|
|
sal_uInt16 n;
|
|
SwSrchChrAttr* pCmp;
|
|
|
|
// 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;
|
|
|
|
if( pArrPtr->nStt >= aTmp.nEnd )
|
|
{
|
|
pArrPtr->nWhich = 0; // deleted
|
|
if( !--m_nStackCount )
|
|
break;
|
|
}
|
|
else if( pArrPtr->nEnd >= aTmp.nEnd )
|
|
{
|
|
pCmp = &m_pFindArr[ n ];
|
|
if( pCmp->nWhich )
|
|
{
|
|
if( pCmp->nStt > pArrPtr->nStt ) // extend
|
|
pCmp->nStt = pArrPtr->nStt;
|
|
}
|
|
else
|
|
{
|
|
*pCmp = *pArrPtr;
|
|
m_nFound++;
|
|
}
|
|
pArrPtr->nWhich = 0;
|
|
if( !--m_nStackCount )
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool bContinue = false;
|
|
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++;
|
|
}
|
|
else if( pCmp->nStt > aTmp.nStt ) // extend?
|
|
pCmp->nStt = aTmp.nStt;
|
|
|
|
bContinue = true;
|
|
}
|
|
}
|
|
// Will the attribute become valid?
|
|
else if( 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( aTmp.nEnd <= pCmp->nEnd )
|
|
pCmp->nEnd = aTmp.nStt;
|
|
else
|
|
pCmp->nStt = aTmp.nEnd;
|
|
|
|
m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
|
|
m_nStackCount++;
|
|
}
|
|
pCmp->nWhich = 0;
|
|
m_nFound--;
|
|
}
|
|
}
|
|
}
|
|
if( oIter )
|
|
{
|
|
assert(pSet && "otherwise no oIter");
|
|
nWhch = oIter->NextWhich();
|
|
while( nWhch &&
|
|
SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
|
|
nWhch = oIter->NextWhich();
|
|
if( !nWhch )
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
oIter.reset();
|
|
return Found();
|
|
}
|
|
|
|
sal_Int32 SwAttrCheckArr::Start() const
|
|
{
|
|
sal_Int32 nStart = m_nNodeStart;
|
|
SwSrchChrAttr* pArrPtr = m_pFindArr;
|
|
for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
|
|
if( pArrPtr->nWhich && pArrPtr->nStt > nStart )
|
|
nStart = pArrPtr->nStt;
|
|
|
|
return nStart;
|
|
}
|
|
|
|
sal_Int32 SwAttrCheckArr::End() const
|
|
{
|
|
SwSrchChrAttr* pArrPtr = m_pFindArr;
|
|
sal_Int32 nEnd = m_nNodeEnd;
|
|
for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
|
|
if( pArrPtr->nWhich && pArrPtr->nEnd < nEnd )
|
|
nEnd = pArrPtr->nEnd;
|
|
|
|
return nEnd;
|
|
}
|
|
|
|
bool SwAttrCheckArr::CheckStack()
|
|
{
|
|
if( !m_nStackCount )
|
|
return false;
|
|
|
|
sal_uInt16 n;
|
|
const sal_Int32 nSttPos = Start();
|
|
const sal_Int32 nEndPos = End();
|
|
SwSrchChrAttr* pArrPtr;
|
|
for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
|
|
{
|
|
if( !pArrPtr->nWhich )
|
|
continue;
|
|
|
|
if( m_bForward ? pArrPtr->nEnd <= nSttPos : pArrPtr->nStt >= nEndPos )
|
|
{
|
|
pArrPtr->nWhich = 0; // deleted
|
|
if( !--m_nStackCount )
|
|
return m_nFound == m_aComapeSet.Count();
|
|
}
|
|
else if( m_bForward ? pArrPtr->nStt < nEndPos : pArrPtr->nEnd > nSttPos )
|
|
{
|
|
// move all "open" ones (= stick out over start position) into FndSet
|
|
OSL_ENSURE( !m_pFindArr[ n ].nWhich, "slot in array is already in use" );
|
|
m_pFindArr[ n ] = *pArrPtr;
|
|
pArrPtr->nWhich = 0;
|
|
m_nFound++;
|
|
if( !--m_nStackCount )
|
|
return m_nFound == m_aComapeSet.Count();
|
|
}
|
|
}
|
|
return m_nFound == m_aComapeSet.Count();
|
|
}
|
|
|
|
static bool lcl_SearchForward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
|
|
SwPaM& rPam )
|
|
{
|
|
sal_Int32 nEndPos;
|
|
rCmpArr.SetNewSet( rTextNd, rPam );
|
|
if( !rTextNd.HasHints() )
|
|
{
|
|
if( !rCmpArr.Found() )
|
|
return false;
|
|
nEndPos = rCmpArr.GetNdEnd();
|
|
lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
|
|
return true;
|
|
}
|
|
|
|
const SwpHints& rHtArr = rTextNd.GetSwpHints();
|
|
const SwTextAttr* pAttr;
|
|
size_t nPos = 0;
|
|
|
|
// if everything is already there then check with which it will be ended
|
|
if( rCmpArr.Found() )
|
|
{
|
|
for( ; nPos < rHtArr.Count(); ++nPos )
|
|
{
|
|
pAttr = rHtArr.Get( nPos );
|
|
if( !rCmpArr.SetAttrFwd( *pAttr ) )
|
|
{
|
|
if( rCmpArr.GetNdStt() < pAttr->GetStart() )
|
|
{
|
|
// found end
|
|
auto nTmpStart = pAttr->GetStart();
|
|
lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(),
|
|
&nTmpStart, true );
|
|
return true;
|
|
}
|
|
// continue search
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( nPos == rHtArr.Count() && rCmpArr.Found() )
|
|
{
|
|
// found
|
|
nEndPos = rCmpArr.GetNdEnd();
|
|
lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
sal_Int32 nSttPos;
|
|
for( ; nPos < rHtArr.Count(); ++nPos )
|
|
{
|
|
pAttr = rHtArr.Get( nPos );
|
|
if( rCmpArr.SetAttrFwd( *pAttr ) )
|
|
{
|
|
// Do multiple start at that position? Do also check those:
|
|
nSttPos = pAttr->GetStart();
|
|
while( ++nPos < rHtArr.Count() )
|
|
{
|
|
pAttr = rHtArr.Get( nPos );
|
|
if( nSttPos != pAttr->GetStart() || !rCmpArr.SetAttrFwd( *pAttr ) )
|
|
break;
|
|
}
|
|
|
|
if( !rCmpArr.Found() )
|
|
continue;
|
|
|
|
// then we have our search area
|
|
nSttPos = rCmpArr.Start();
|
|
nEndPos = rCmpArr.End();
|
|
if( nSttPos > nEndPos )
|
|
return false;
|
|
|
|
lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if( !rCmpArr.CheckStack() )
|
|
return false;
|
|
nSttPos = rCmpArr.Start();
|
|
nEndPos = rCmpArr.End();
|
|
if( nSttPos > nEndPos )
|
|
return false;
|
|
|
|
lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
|
|
return true;
|
|
}
|
|
|
|
static bool lcl_SearchBackward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
|
|
SwPaM& rPam )
|
|
{
|
|
sal_Int32 nEndPos;
|
|
rCmpArr.SetNewSet( rTextNd, rPam );
|
|
if( !rTextNd.HasHints() )
|
|
{
|
|
if( !rCmpArr.Found() )
|
|
return false;
|
|
nEndPos = rCmpArr.GetNdEnd();
|
|
lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
|
|
return true;
|
|
}
|
|
|
|
const SwpHints& rHtArr = rTextNd.GetSwpHints();
|
|
const SwTextAttr* pAttr;
|
|
size_t nPos = rHtArr.Count();
|
|
sal_Int32 nSttPos;
|
|
|
|
// if everything is already there then check with which it will be ended
|
|
if( rCmpArr.Found() )
|
|
{
|
|
while( nPos )
|
|
{
|
|
pAttr = rHtArr.GetSortedByEnd( --nPos );
|
|
if( !rCmpArr.SetAttrBwd( *pAttr ) )
|
|
{
|
|
nSttPos = pAttr->GetAnyEnd();
|
|
if( nSttPos < rCmpArr.GetNdEnd() )
|
|
{
|
|
// found end
|
|
nEndPos = rCmpArr.GetNdEnd();
|
|
lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
|
|
return true;
|
|
}
|
|
|
|
// continue search
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !nPos && rCmpArr.Found() )
|
|
{
|
|
// found
|
|
nEndPos = rCmpArr.GetNdEnd();
|
|
lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
while( nPos )
|
|
{
|
|
pAttr = rHtArr.GetSortedByEnd( --nPos );
|
|
if( rCmpArr.SetAttrBwd( *pAttr ) )
|
|
{
|
|
// Do multiple start at that position? Do also check those:
|
|
if( nPos )
|
|
{
|
|
nEndPos = pAttr->GetAnyEnd();
|
|
while( --nPos )
|
|
{
|
|
pAttr = rHtArr.GetSortedByEnd( nPos );
|
|
if( nEndPos != pAttr->GetAnyEnd() || !rCmpArr.SetAttrBwd( *pAttr ) )
|
|
break;
|
|
}
|
|
}
|
|
if( !rCmpArr.Found() )
|
|
continue;
|
|
|
|
// then we have our search area
|
|
nSttPos = rCmpArr.Start();
|
|
nEndPos = rCmpArr.End();
|
|
if( nSttPos > nEndPos )
|
|
return false;
|
|
|
|
lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if( !rCmpArr.CheckStack() )
|
|
return false;
|
|
nSttPos = rCmpArr.Start();
|
|
nEndPos = rCmpArr.End();
|
|
if( nSttPos > nEndPos )
|
|
return false;
|
|
|
|
lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
|
|
return true;
|
|
}
|
|
|
|
static bool lcl_Search( const SwContentNode& rCNd, const SfxItemSet& rCmpSet, bool bNoColls )
|
|
{
|
|
// search only hard attribution?
|
|
if( bNoColls && !rCNd.HasSwAttrSet() )
|
|
return false;
|
|
|
|
const SfxItemSet& rNdSet = rCNd.GetSwAttrSet();
|
|
SfxItemIter aIter( rCmpSet );
|
|
const SfxPoolItem* pItem = aIter.GetCurItem();
|
|
const SfxPoolItem* pNdItem;
|
|
sal_uInt16 nWhich;
|
|
|
|
do
|
|
{
|
|
if( IsInvalidItem( pItem ))
|
|
{
|
|
nWhich = aIter.GetCurWhich();
|
|
if( SfxItemState::SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
|
|
|| CmpAttr( *pNdItem, rNdSet.GetPool()->GetUserOrPoolDefaultItem( nWhich ) ))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
nWhich = pItem->Which();
|
|
|
|
if( !CmpAttr( rNdSet.Get( nWhich, !bNoColls ), *pItem ))
|
|
return false;
|
|
}
|
|
|
|
pItem = aIter.NextItem();
|
|
} while (pItem);
|
|
return true; // found
|
|
}
|
|
|
|
namespace sw {
|
|
|
|
bool FindAttrImpl(SwPaM & rSearchPam,
|
|
const SfxPoolItem& rAttr, SwMoveFnCollection const & fnMove,
|
|
const SwPaM & rRegion, bool bInReadOnly,
|
|
SwRootFrame const*const pLayout)
|
|
{
|
|
// determine which attribute is searched:
|
|
const sal_uInt16 nWhich = rAttr.Which();
|
|
bool bCharAttr = isCHRATR(nWhich) || isTXTATR(nWhich);
|
|
assert(isTXTATR(nWhich)); // sw_redlinehide: only works for non-formatting hints such as needed in UpdateFields; use FindAttrsImpl for others
|
|
|
|
std::optional<SwPaM> oPam;
|
|
sw::MakeRegion(fnMove, rRegion, oPam);
|
|
|
|
bool bFound = false;
|
|
bool bFirst = true;
|
|
const bool bSrchForward = &fnMove == &fnMoveForward;
|
|
SwContentNode * pNode;
|
|
|
|
// 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 ))
|
|
{
|
|
return false;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
else if (!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;
|
|
}
|
|
else if (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();
|
|
|
|
return bFound;
|
|
}
|
|
|
|
} // namespace sw
|
|
|
|
typedef bool (*FnSearchAttr)( const SwTextNode&, SwAttrCheckArr&, SwPaM& );
|
|
|
|
static bool FindAttrsImpl(SwPaM & rSearchPam,
|
|
const SfxItemSet& rSet, bool bNoColls, SwMoveFnCollection const & fnMove,
|
|
const SwPaM & rRegion, bool bInReadOnly, bool bMoveFirst,
|
|
SwRootFrame const*const pLayout)
|
|
{
|
|
std::optional<SwPaM> oPam;
|
|
sw::MakeRegion(fnMove, rRegion, oPam);
|
|
|
|
bool bFound = false;
|
|
bool bFirst = true;
|
|
const bool bSrchForward = &fnMove == &fnMoveForward;
|
|
SwContentNode * pNode;
|
|
o3tl::sorted_vector<SwFormat*> aFormatArr;
|
|
|
|
// check which text/char attributes are searched
|
|
SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls );
|
|
SfxItemSetFixed<RES_PARATR_BEGIN, RES_GRFATR_END-1> aOtherSet( rSearchPam.GetDoc().GetAttrPool() );
|
|
aOtherSet.Put( rSet, false ); // got all invalid items
|
|
|
|
FnSearchAttr fnSearch = bSrchForward
|
|
? (&::lcl_SearchForward)
|
|
: (&::lcl_SearchBackward);
|
|
|
|
// 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 ))
|
|
{
|
|
return false;
|
|
}
|
|
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)
|
|
{
|
|
auto const 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;
|
|
|
|
SwFindParaAttr( const SfxItemSet& rSet, bool bNoCollection,
|
|
const i18nutil::SearchOptions2* pOpt, const SfxItemSet* pRSet,
|
|
SwCursor& rCursor, SwRootFrame const*const pLayout)
|
|
: m_bNoCollection(bNoCollection)
|
|
, pSet( &rSet )
|
|
, pReplSet( pRSet )
|
|
, pSearchOpt( pOpt )
|
|
, m_rCursor(rCursor)
|
|
, m_pLayout(pLayout)
|
|
{}
|
|
|
|
virtual ~SwFindParaAttr() {}
|
|
|
|
virtual int DoFind(SwPaM &, SwMoveFnCollection const &, const SwPaM &, bool bInReadOnly,
|
|
std::unique_ptr<SvxSearchItem>& xSearchItem) override;
|
|
virtual bool IsReplaceMode() const override;
|
|
};
|
|
|
|
}
|
|
|
|
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
|
|
|
|
pTextRegion = &aSrchPam;
|
|
}
|
|
else if( !pSearchOpt )
|
|
return FIND_NOT_FOUND;
|
|
|
|
// then search in text of it
|
|
if( !pSText )
|
|
{
|
|
i18nutil::SearchOptions2 aTmp( *pSearchOpt );
|
|
|
|
// search in selection
|
|
aTmp.searchFlag |= (SearchFlags::REG_NOT_BEGINOFLINE |
|
|
SearchFlags::REG_NOT_ENDOFLINE);
|
|
|
|
aTmp.Locale = SvtSysLocale().GetLanguageTag().getLocale();
|
|
|
|
pSText.reset( new utl::TextSearch( aTmp ) );
|
|
}
|
|
|
|
// TODO: searching for attributes in Outliner text?!
|
|
|
|
// continue search in correct section (pTextRegion)
|
|
if (sw::FindTextImpl(aSrchPam, *pSearchOpt, false/*bSearchInNotes*/, *pSText, fnMove, *pTextRegion, bInReadOnly, m_pLayout, xSearchItem) &&
|
|
*aSrchPam.GetMark() != *aSrchPam.GetPoint() )
|
|
break; // found
|
|
else if( !pSet->Count() )
|
|
return FIND_NOT_FOUND; // only text and nothing found
|
|
|
|
*aRegion.GetMark() = *aSrchPam.GetPoint();
|
|
}
|
|
|
|
*rCursor.GetPoint() = *aSrchPam.GetPoint();
|
|
rCursor.SetMark();
|
|
*rCursor.GetMark() = *aSrchPam.GetMark();
|
|
}
|
|
|
|
if( bReplaceText )
|
|
{
|
|
const bool bRegExp(
|
|
SearchAlgorithms2::REGEXP == pSearchOpt->AlgorithmType2);
|
|
SwPosition& rSttCntPos = *rCursor.Start();
|
|
const sal_Int32 nSttCnt = rSttCntPos.GetContentIndex();
|
|
|
|
// 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() );
|
|
}
|
|
|
|
std::optional<OUString> xRepl;
|
|
if (bRegExp)
|
|
xRepl = sw::ReplaceBackReferences(*pSearchOpt, &rCursor, m_pLayout);
|
|
sw::ReplaceImpl(rCursor,
|
|
xRepl ? *xRepl : pSearchOpt->replaceString, bRegExp,
|
|
m_rCursor.GetDoc(), m_pLayout);
|
|
|
|
m_rCursor.SaveTableBoxContent( rCursor.GetPoint() );
|
|
|
|
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() ));
|
|
|
|
pItem = aIter.NextItem();
|
|
} while (pItem);
|
|
aSet.Put( *pReplSet );
|
|
rCursor.GetDoc().getIDocumentContentOperations().InsertItemSet(
|
|
rCursor, aSet, SetAttrMode::DEFAULT, m_pLayout);
|
|
}
|
|
|
|
return FIND_NO_RING;
|
|
}
|
|
else
|
|
return FIND_FOUND;
|
|
}
|
|
|
|
bool SwFindParaAttr::IsReplaceMode() const
|
|
{
|
|
return ( pSearchOpt && !pSearchOpt->replaceString.isEmpty() ) ||
|
|
( pReplSet && pReplSet->Count() );
|
|
}
|
|
|
|
/// search for attributes
|
|
sal_Int32 SwCursor::FindAttrs( const SfxItemSet& rSet, bool bNoCollections,
|
|
SwDocPositions nStart, SwDocPositions nEnd,
|
|
bool& bCancel, FindRanges eFndRngs,
|
|
const i18nutil::SearchOptions2* pSearchOpt,
|
|
const SfxItemSet* pReplSet,
|
|
SwRootFrame const*const pLayout)
|
|
{
|
|
// switch off OLE-notifications
|
|
SwDoc& rDoc = GetDoc();
|
|
Link<bool,void> aLnk( rDoc.GetOle2Link() );
|
|
rDoc.SetOle2Link( Link<bool,void>() );
|
|
|
|
bool bReplace = ( pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
|
|
!rSet.Count() ) ) ||
|
|
(pReplSet && pReplSet->Count());
|
|
bool const bStartUndo = rDoc.GetIDocumentUndoRedo().DoesUndo() && bReplace;
|
|
if (bStartUndo)
|
|
{
|
|
rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REPLACE, nullptr );
|
|
}
|
|
|
|
SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt,
|
|
pReplSet, *this, pLayout );
|
|
|
|
sal_Int32 nRet = FindAll( aSwFindParaAttr, nStart, nEnd, eFndRngs, bCancel );
|
|
rDoc.SetOle2Link( aLnk );
|
|
if( nRet && bReplace )
|
|
rDoc.getIDocumentState().SetModified();
|
|
|
|
if (bStartUndo)
|
|
{
|
|
rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REPLACE, nullptr );
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|