blob: 23abc9a5499a3244462069f2a076f57effa0c96e [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/util/SearchOptions.hpp>
#include <com/sun/star/util/SearchFlags.hpp>
#include <i18npool/mslangid.hxx>
#include <hintids.hxx>
#include <vcl/svapp.hxx>
#include <svl/itemiter.hxx>
#include <svl/whiter.hxx>
#include <editeng/brkitem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/fontitem.hxx>
#include <fmtpdsc.hxx>
#include <txatbase.hxx>
#include <fchrfmt.hxx>
#include <charfmt.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <swcrsr.hxx>
#include <editsh.hxx>
#include <ndtxt.hxx>
#include <pamtyp.hxx>
#include <swundo.hxx>
#include <crsskip.hxx>
using namespace ::com::sun::star;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::util;
SV_DECL_PTRARR_SORT( SwpFmts, SwFmt*, 0, 4 )
SV_IMPL_PTRARR_SORT( SwpFmts, SwFmt* )
// Sonderbehandlung fuer SvxFontItem, nur den Namen vergleichen:
int CmpAttr( const SfxPoolItem& rItem1, const SfxPoolItem& rItem2 )
{
switch( rItem1.Which() )
{
case RES_CHRATR_FONT:
return ((SvxFontItem&)rItem1).GetFamilyName() ==
((SvxFontItem&)rItem2).GetFamilyName();
case RES_CHRATR_COLOR:
return ((SvxColorItem&)rItem1).GetValue().IsRGBEqual(
((SvxColorItem&)rItem2).GetValue() );
case RES_PAGEDESC:
return ((SwFmtPageDesc&)rItem1).GetNumOffset() ==
((SwFmtPageDesc&)rItem2).GetNumOffset() &&
((SwFmtPageDesc&)rItem1).GetPageDesc() ==
((SwFmtPageDesc&)rItem2).GetPageDesc();
}
return rItem1 == rItem2;
}
const SwTxtAttr* GetFrwrdTxtHint( const SwpHints& rHtsArr, sal_uInt16& rPos,
xub_StrLen nCntntPos )
{
while( rPos < rHtsArr.Count() )
{
const SwTxtAttr *pTxtHt = rHtsArr.GetStart( rPos++ );
// der Start vom Attribut muss innerhalb des Bereiches liegen !!
if( *pTxtHt->GetStart() >= nCntntPos )
return pTxtHt; // gueltiges TextAttribut
}
return 0; // kein gueltiges TextAttribut
}
const SwTxtAttr* GetBkwrdTxtHint( const SwpHints& rHtsArr, sal_uInt16& rPos,
xub_StrLen nCntntPos )
{
while( rPos > 0 )
{
//Hack mit cast fuer das Update
const SwTxtAttr *pTxtHt = rHtsArr.GetStart( --rPos );
// der Start vom Attribut muss innerhalb des Bereiches liegen !!
if( *pTxtHt->GetStart() < nCntntPos )
return pTxtHt; // gueltiges TextAttribut
}
return 0; // kein gueltiges TextAttribut
}
void lcl_SetAttrPam( SwPaM & rPam, xub_StrLen nStart, const xub_StrLen* pEnde,
const sal_Bool bSaveMark )
{
xub_StrLen nCntntPos;
if( bSaveMark )
nCntntPos = rPam.GetMark()->nContent.GetIndex();
else
nCntntPos = rPam.GetPoint()->nContent.GetIndex();
sal_Bool bTstEnde = rPam.GetPoint()->nNode == rPam.GetMark()->nNode;
SwCntntNode* pCNd = rPam.GetCntntNode();
rPam.GetPoint()->nContent.Assign( pCNd, nStart );
rPam.SetMark(); // Point == GetMark
// Point zeigt auf das Ende vom SuchBereich oder Ende vom Attribut
if( pEnde )
{
if( bTstEnde && *pEnde > nCntntPos )
rPam.GetPoint()->nContent = nCntntPos;
else
rPam.GetPoint()->nContent = *pEnde;
}
}
//------------------ Suche nach einem Text Attribut -----------------------
// diese Funktion sucht in einem TextNode nach dem vorgegebenen Attribut.
// Wird es gefunden, dann hat der SwPaM den Bereich der das Attribut
// umspannt, unter Beachtung des Suchbereiches
sal_Bool lcl_Search( const SwTxtNode& rTxtNd, SwPaM& rPam,
const SfxPoolItem& rCmpItem,
SwMoveFn fnMove, sal_Bool bValue )
{
if ( !rTxtNd.HasHints() )
return sal_False;
const SwTxtAttr *pTxtHt = 0;
sal_Bool bForward = fnMove == fnMoveForward;
sal_uInt16 nPos = bForward ? 0 : rTxtNd.GetSwpHints().Count();
xub_StrLen nCntntPos = rPam.GetPoint()->nContent.GetIndex();
while( 0 != ( pTxtHt=(*fnMove->fnGetHint)(rTxtNd.GetSwpHints(),nPos,nCntntPos)))
if( pTxtHt->Which() == rCmpItem.Which() &&
( !bValue || CmpAttr( pTxtHt->GetAttr(), rCmpItem )))
{
lcl_SetAttrPam( rPam, *pTxtHt->GetStart(), pTxtHt->End(), bForward );
return sal_True;
}
return sal_False;
}
//------------------ Suche nach mehren Text Attributen -------------------
struct _SwSrchChrAttr
{
sal_uInt16 nWhich;
xub_StrLen nStt, nEnd;
_SwSrchChrAttr( const SfxPoolItem& rItem,
xub_StrLen nStart, xub_StrLen nAnyEnd )
: nWhich( rItem.Which() ), nStt( nStart ), nEnd( nAnyEnd )
{}
};
class SwAttrCheckArr
{
_SwSrchChrAttr *pFndArr, *pStackArr;
xub_StrLen nNdStt, nNdEnd;
sal_uInt16 nArrStart, nArrLen;
sal_uInt16 nFound, nStackCnt;
SfxItemSet aCmpSet;
sal_Bool bNoColls;
sal_Bool bForward;
public:
SwAttrCheckArr( const SfxItemSet& rSet, int bForward, int bNoCollections );
~SwAttrCheckArr();
void SetNewSet( const SwTxtNode& rTxtNd, const SwPaM& rPam );
// wieviele Attribute ueberhaupt ??
sal_uInt16 Count() const { return aCmpSet.Count(); }
int Found() const { return nFound == aCmpSet.Count(); }
int CheckStack();
xub_StrLen Start() const;
xub_StrLen End() const;
xub_StrLen GetNdStt() const { return nNdStt; }
xub_StrLen GetNdEnd() const { return nNdEnd; }
int SetAttrFwd( const SwTxtAttr& rAttr );
int SetAttrBwd( const SwTxtAttr& rAttr );
};
SwAttrCheckArr::SwAttrCheckArr( const SfxItemSet& rSet, int bFwd,
int bNoCollections )
: aCmpSet( *rSet.GetPool(), RES_CHRATR_BEGIN, RES_TXTATR_END-1 )
{
aCmpSet.Put( rSet, sal_False );
bNoColls = 0 != bNoCollections;
bForward = 0 != bFwd;
// Bestimmen den Bereich des Fnd/Stack-Arrays (Min/Max)
SfxItemIter aIter( aCmpSet );
nArrStart = aCmpSet.GetWhichByPos( aIter.GetFirstPos() );
nArrLen = aCmpSet.GetWhichByPos( aIter.GetLastPos() ) - nArrStart+1;
char* pFndChar = new char[ nArrLen * sizeof(_SwSrchChrAttr) ];
char* pStackChar = new char[ nArrLen * sizeof(_SwSrchChrAttr) ];
pFndArr = (_SwSrchChrAttr*)pFndChar;
pStackArr = (_SwSrchChrAttr*)pStackChar;
}
SwAttrCheckArr::~SwAttrCheckArr()
{
delete[] (char*)pFndArr;
delete[] (char*)pStackArr;
}
void SwAttrCheckArr::SetNewSet( const SwTxtNode& rTxtNd, const SwPaM& rPam )
{
memset( pFndArr, 0, nArrLen * sizeof(_SwSrchChrAttr) );
memset( pStackArr, 0, nArrLen * sizeof(_SwSrchChrAttr) );
nFound = 0;
nStackCnt = 0;
if( bForward )
{
nNdStt = rPam.GetPoint()->nContent.GetIndex();
nNdEnd = rPam.GetPoint()->nNode == rPam.GetMark()->nNode
? rPam.GetMark()->nContent.GetIndex()
: rTxtNd.GetTxt().Len();
}
else
{
nNdEnd = rPam.GetPoint()->nContent.GetIndex();
nNdStt = rPam.GetPoint()->nNode == rPam.GetMark()->nNode
? rPam.GetMark()->nContent.GetIndex()
: 0;
}
if( bNoColls && !rTxtNd.HasSwAttrSet() )
return ;
const SfxItemSet& rSet = rTxtNd.GetSwAttrSet();
// if( !rSet.Count() )
// return;
SfxItemIter aIter( aCmpSet );
const SfxPoolItem* pItem = aIter.GetCurItem();
const SfxPoolItem* pFndItem;
sal_uInt16 nWhich;
while( sal_True )
{
// nur testen, ob vorhanden ist ?
if( IsInvalidItem( pItem ) )
{
nWhich = aCmpSet.GetWhichByPos( aIter.GetCurPos() );
if( RES_TXTATR_END <= nWhich )
break; // Ende der TextAttribute
if( SFX_ITEM_SET == rSet.GetItemState( nWhich, !bNoColls, &pFndItem )
&& !CmpAttr( *pFndItem, rSet.GetPool()->GetDefaultItem( nWhich ) ))
{
pFndArr[ nWhich - nArrStart ] =
_SwSrchChrAttr( *pFndItem, nNdStt, nNdEnd );
nFound++;
}
}
else
{
if( RES_TXTATR_END <= (nWhich = pItem->Which() ))
break; // Ende der TextAttribute
//JP 27.02.95: wenn nach defaults gesucht wird, dann muss man bis zum Pool
// runter
// if( SFX_ITEM_SET == rSet.GetItemState( nWhich, !bNoColls, &pFndItem )
// && *pFndItem == *pItem )
if( CmpAttr( rSet.Get( nWhich, !bNoColls ), *pItem ) )
{
pFndArr[ nWhich - nArrStart ] =
_SwSrchChrAttr( *pItem, nNdStt, nNdEnd );
nFound++;
}
}
if( aIter.IsAtEnd() )
break;
pItem = aIter.NextItem();
}
}
static bool
lcl_IsAttributeIgnorable(xub_StrLen const nNdStart, xub_StrLen 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)));
}
int SwAttrCheckArr::SetAttrFwd( const SwTxtAttr& rAttr )
{
_SwSrchChrAttr aTmp( rAttr.GetAttr(), *rAttr.GetStart(), *rAttr.GetAnyEnd() );
// ignore all attributes not in search range
if (lcl_IsAttributeIgnorable(nNdStt, nNdEnd, aTmp))
{
return Found();
}
const SfxPoolItem* pItem;
// --------------------------------------------------------------
// Hier wird jetzt ausdruecklich auch in Zeichenvorlagen gesucht
// --------------------------------------------------------------
sal_uInt16 nWhch = rAttr.Which();
SfxWhichIter* pIter = NULL;
const SfxPoolItem* pTmpItem = NULL;
const SfxItemSet* pSet = NULL;
if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
{
if( bNoColls && RES_TXTATR_CHARFMT == nWhch )
return Found();
pTmpItem = NULL;
pSet = CharFmt::GetItemSet( rAttr.GetAttr() );
if ( pSet )
{
pIter = new SfxWhichIter( *pSet );
nWhch = pIter->FirstWhich();
while( nWhch &&
SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
nWhch = pIter->NextWhich();
if( !nWhch )
pTmpItem = NULL;
}
}
else
pTmpItem = &rAttr.GetAttr();
while( pTmpItem )
{
SfxItemState eState = aCmpSet.GetItemState( nWhch, sal_False, &pItem );
if( SFX_ITEM_DONTCARE == eState || SFX_ITEM_SET == eState )
{
sal_uInt16 n;
_SwSrchChrAttr* pCmp;
// loesche erstmal alle, die bis zu der Start Position schon wieder
// ungueltig sind:
_SwSrchChrAttr* pArrPtr;
if( nFound )
for( pArrPtr = pFndArr, n = 0; n < nArrLen;
++n, ++pArrPtr )
if( pArrPtr->nWhich && pArrPtr->nEnd <= aTmp.nStt )
{
pArrPtr->nWhich = 0; // geloescht
nFound--;
}
// loesche erstmal alle, die bis zu der Start Position schon wieder
// ungueltig sind. Und verschiebe alle die "offen" sind, heisst ueber
// die Start Position ragen, vom Stack in den FndSet
if( nStackCnt )
for( pArrPtr = pStackArr, n=0; n < nArrLen; ++n, ++pArrPtr )
{
if( !pArrPtr->nWhich )
continue;
if( pArrPtr->nEnd <= aTmp.nStt )
{
pArrPtr->nWhich = 0; // geloescht
if( !--nStackCnt )
break;
}
else if( pArrPtr->nStt <= aTmp.nStt )
{
if( ( pCmp = &pFndArr[ n ])->nWhich )
{
if( pCmp->nEnd < pArrPtr->nEnd ) // erweitern
pCmp->nEnd = pArrPtr->nEnd;
}
else
{
*pCmp = *pArrPtr;
nFound++;
}
pArrPtr->nWhich = 0;
if( !--nStackCnt )
break;
}
}
sal_Bool bContinue = sal_False;
if( SFX_ITEM_DONTCARE == eState )
{
// wird Attribut gueltig ?
if( !CmpAttr( aCmpSet.GetPool()->GetDefaultItem( nWhch ),
*pTmpItem ))
{
// suche das Attribut und erweiter es gegebenenfalls
if( !( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
{
*pCmp = aTmp; // nicht gefunden, eintragen
nFound++;
}
else if( pCmp->nEnd < aTmp.nEnd ) // erweitern ?
pCmp->nEnd = aTmp.nEnd;
bContinue = sal_True;
}
}
// wird Attribut gueltig ?
else if( CmpAttr( *pItem, *pTmpItem ) )
{
pFndArr[ nWhch - nArrStart ] = aTmp;
++nFound;
bContinue = sal_True;
}
// tja, dann muss es auf den Stack
if( !bContinue && ( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
{
// vorhanden, auf den Stack. Aber nur wenn es noch grosser ist
if( pCmp->nEnd > aTmp.nEnd )
{
ASSERT( !pStackArr[ nWhch - nArrStart ].nWhich,
"Stack-Platz ist noch belegt" );
// ---------
// JP 22.08.96: nur Ende manipulieren reicht nicht. Bug 30547
// pCmp->nStt = aTmp.nEnd;
if( aTmp.nStt <= pCmp->nStt )
pCmp->nStt = aTmp.nEnd;
else
pCmp->nEnd = aTmp.nStt;
// ---------
pStackArr[ nWhch - nArrStart ] = *pCmp;
nStackCnt++;
}
pCmp->nWhich = 0;
nFound--;
}
}
if( pIter )
{
nWhch = pIter->NextWhich();
while( nWhch &&
SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
nWhch = pIter->NextWhich();
if( !nWhch )
break;
}
else
break;
}
return Found();
}
int SwAttrCheckArr::SetAttrBwd( const SwTxtAttr& rAttr )
{
_SwSrchChrAttr aTmp( rAttr.GetAttr(), *rAttr.GetStart(), *rAttr.GetAnyEnd() );
// ignore all attributes not in search range
if (lcl_IsAttributeIgnorable(nNdStt, nNdEnd, aTmp))
{
return Found();
}
const SfxPoolItem* pItem;
// --------------------------------------------------------------
// Hier wird jetzt ausdruecklich auch in Zeichenvorlagen gesucht
// --------------------------------------------------------------
sal_uInt16 nWhch = rAttr.Which();
SfxWhichIter* pIter = NULL;
const SfxPoolItem* pTmpItem = NULL;
const SfxItemSet* pSet = NULL;
if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
{
if( bNoColls && RES_TXTATR_CHARFMT == nWhch )
return Found();
pSet = CharFmt::GetItemSet( rAttr.GetAttr() );
if ( pSet )
{
pIter = new SfxWhichIter( *pSet );
nWhch = pIter->FirstWhich();
while( nWhch &&
SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
nWhch = pIter->NextWhich();
if( !nWhch )
pTmpItem = NULL;
}
}
else
pTmpItem = &rAttr.GetAttr();
while( pTmpItem )
{
SfxItemState eState = aCmpSet.GetItemState( nWhch, sal_False, &pItem );
if( SFX_ITEM_DONTCARE == eState || SFX_ITEM_SET == eState )
{
sal_uInt16 n;
_SwSrchChrAttr* pCmp;
// loesche erstmal alle, die bis zu der Start Position schon wieder
// ungueltig sind:
_SwSrchChrAttr* pArrPtr;
if( nFound )
for( pArrPtr = pFndArr, n = 0; n < nArrLen; ++n, ++pArrPtr )
if( pArrPtr->nWhich && pArrPtr->nStt >= aTmp.nEnd )
{
pArrPtr->nWhich = 0; // geloescht
nFound--;
}
// loesche erstmal alle, die bis zu der Start Position schon wieder
// ungueltig sind. Und verschiebe alle die "offen" sind, heisst ueber
// die Start Position ragen, vom Stack in den FndSet
if( nStackCnt )
for( pArrPtr = pStackArr, n = 0; n < nArrLen; ++n, ++pArrPtr )
{
if( !pArrPtr->nWhich )
continue;
if( pArrPtr->nStt >= aTmp.nEnd )
{
pArrPtr->nWhich = 0; // geloescht
if( !--nStackCnt )
break;
}
else if( pArrPtr->nEnd >= aTmp.nEnd )
{
if( ( pCmp = &pFndArr[ n ])->nWhich )
{
if( pCmp->nStt > pArrPtr->nStt ) // erweitern
pCmp->nStt = pArrPtr->nStt;
}
else
{
*pCmp = *pArrPtr;
nFound++;
}
pArrPtr->nWhich = 0;
if( !--nStackCnt )
break;
}
}
sal_Bool bContinue = sal_False;
if( SFX_ITEM_DONTCARE == eState )
{
// wird Attribut gueltig ?
if( !CmpAttr( aCmpSet.GetPool()->GetDefaultItem( nWhch ),
*pTmpItem ) )
{
// suche das Attribut und erweiter es gegebenenfalls
if( !( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
{
*pCmp = aTmp; // nicht gefunden, eintragen
nFound++;
}
else if( pCmp->nStt > aTmp.nStt ) // erweitern ?
pCmp->nStt = aTmp.nStt;
bContinue = sal_True;
}
}
// wird Attribut gueltig ?
else if( CmpAttr( *pItem, *pTmpItem ))
{
pFndArr[ nWhch - nArrStart ] = aTmp;
++nFound;
bContinue = sal_True;
}
// tja, dann muss es auf den Stack
if( !bContinue && ( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
{
// vorhanden, auf den Stack. Aber nur wenn es noch grosser ist
if( pCmp->nStt < aTmp.nStt )
{
ASSERT( !pStackArr[ nWhch - nArrStart ].nWhich,
"Stack-Platz ist noch belegt" );
// ---------
// JP 22.08.96: nur Ende manipulieren reicht nicht. Bug 30547
// pCmp->nEnd = aTmp.nStt;
if( aTmp.nEnd <= pCmp->nEnd )
pCmp->nEnd = aTmp.nStt;
else
pCmp->nStt = aTmp.nEnd;
// ---------
pStackArr[ nWhch - nArrStart ] = *pCmp;
nStackCnt++;
}
pCmp->nWhich = 0;
nFound--;
}
}
if( pIter )
{
nWhch = pIter->NextWhich();
while( nWhch &&
SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
nWhch = pIter->NextWhich();
if( !nWhch )
break;
}
else
break;
}
return Found();
}
xub_StrLen SwAttrCheckArr::Start() const
{
xub_StrLen nStart = nNdStt;
_SwSrchChrAttr* pArrPtr = pFndArr;
for( sal_uInt16 n = 0; n < nArrLen; ++n, ++pArrPtr )
if( pArrPtr->nWhich && pArrPtr->nStt > nStart )
nStart = pArrPtr->nStt;
return nStart;
}
xub_StrLen SwAttrCheckArr::End() const
{
_SwSrchChrAttr* pArrPtr = pFndArr;
xub_StrLen nEnd = nNdEnd;
for( sal_uInt16 n = 0; n < nArrLen; ++n, ++pArrPtr )
if( pArrPtr->nWhich && pArrPtr->nEnd < nEnd )
nEnd = pArrPtr->nEnd;
return nEnd;
}
int SwAttrCheckArr::CheckStack()
{
if( !nStackCnt )
return sal_False;
sal_uInt16 n;
xub_StrLen nSttPos = Start(), nEndPos = End();
_SwSrchChrAttr* pArrPtr;
for( pArrPtr = pStackArr, n = 0; n < nArrLen; ++n, ++pArrPtr )
{
if( !pArrPtr->nWhich )
continue;
if( bForward ? pArrPtr->nEnd <= nSttPos : pArrPtr->nStt >= nEndPos )
{
pArrPtr->nWhich = 0; // geloescht
if( !--nStackCnt )
return nFound == aCmpSet.Count();
}
else if( bForward ? pArrPtr->nStt < nEndPos : pArrPtr->nEnd > nSttPos )
{
// alle die "offen" sind, heisst ueber die Start Position ragen,
// im FndSet setzen
ASSERT( !pFndArr[ n ].nWhich, "Array-Platz ist noch belegt" );
pFndArr[ n ] = *pArrPtr;
pArrPtr->nWhich = 0;
nFound++;
if( !--nStackCnt )
return nFound == aCmpSet.Count();
}
}
return nFound == aCmpSet.Count();
}
int lcl_SearchForward( const SwTxtNode& rTxtNd, SwAttrCheckArr& rCmpArr,
SwPaM& rPam )
{
xub_StrLen nEndPos, nSttPos;
rCmpArr.SetNewSet( rTxtNd, rPam );
if( !rTxtNd.HasHints() )
{
if( !rCmpArr.Found() )
return sal_False;
nEndPos = rCmpArr.GetNdEnd();
lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_True );
return sal_True;
}
// dann gehe mal durch das nach "Start" sortierte Array
const SwpHints& rHtArr = rTxtNd.GetSwpHints();
const SwTxtAttr* pAttr;
sal_uInt16 nPos = 0;
// sollte jetzt schon alles vorhanden sein, dann teste, mit welchem
// das wieder beendet wird.
if( rCmpArr.Found() )
{
for( ; nPos < rHtArr.Count(); ++nPos )
if( !rCmpArr.SetAttrFwd( *( pAttr = rHtArr.GetStart( nPos )) ) )
{
if( rCmpArr.GetNdStt() < *pAttr->GetStart() )
{
// dann haben wir unser Ende:
lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(),
pAttr->GetStart(), sal_True );
return sal_True;
}
// ansonsten muessen wir weiter suchen
break;
}
if( nPos == rHtArr.Count() && rCmpArr.Found() )
{
// dann haben wir unseren Bereich
nEndPos = rCmpArr.GetNdEnd();
lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_True );
return sal_True;
}
}
for( ; nPos < rHtArr.Count(); ++nPos )
if( rCmpArr.SetAttrFwd( *( pAttr = rHtArr.GetStart( nPos )) ) )
{
// sollten noch mehr auf der gleichen Position anfangen ??
// auch die noch mit testen !!
nSttPos = *pAttr->GetStart();
while( ++nPos < rHtArr.Count() && nSttPos ==
*( pAttr = rHtArr.GetStart( nPos ))->GetStart() &&
rCmpArr.SetAttrFwd( *pAttr ) )
;
if( !rCmpArr.Found() )
continue;
// dann haben wir den Bereich zusammen
if( (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
return sal_False;
lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_True );
return sal_True;
}
if( !rCmpArr.CheckStack() ||
(nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
return sal_False;
lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_True );
return sal_True;
}
int lcl_SearchBackward( const SwTxtNode& rTxtNd, SwAttrCheckArr& rCmpArr,
SwPaM& rPam )
{
xub_StrLen nEndPos, nSttPos;
rCmpArr.SetNewSet( rTxtNd, rPam );
if( !rTxtNd.HasHints() )
{
if( !rCmpArr.Found() )
return sal_False;
nEndPos = rCmpArr.GetNdEnd();
lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_False );
return sal_True;
}
// dann gehe mal durch das nach "Start" sortierte Array
const SwpHints& rHtArr = rTxtNd.GetSwpHints();
const SwTxtAttr* pAttr;
sal_uInt16 nPos = rHtArr.Count();
// sollte jetzt schon alles vorhanden sein, dann teste, mit welchem
// das wieder beendet wird.
if( rCmpArr.Found() )
{
while( nPos )
if( !rCmpArr.SetAttrBwd( *( pAttr = rHtArr.GetEnd( --nPos )) ) )
{
nSttPos = *pAttr->GetAnyEnd();
if( nSttPos < rCmpArr.GetNdEnd() )
{
// dann haben wir unser Ende:
nEndPos = rCmpArr.GetNdEnd();
lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False );
return sal_True;
}
// ansonsten muessen wir weiter suchen
break;
}
if( !nPos && rCmpArr.Found() )
{
// dann haben wir unseren Bereich
nEndPos = rCmpArr.GetNdEnd();
lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_False );
return sal_True;
}
}
while( nPos )
if( rCmpArr.SetAttrBwd( *( pAttr = rHtArr.GetEnd( --nPos )) ) )
{
// sollten noch mehr auf der gleichen Position anfangen ??
// auch die noch mit testen !!
if( nPos )
{
nEndPos = *pAttr->GetAnyEnd();
while( --nPos && nEndPos ==
*( pAttr = rHtArr.GetEnd( nPos ))->GetAnyEnd() &&
rCmpArr.SetAttrBwd( *pAttr ) )
;
}
if( !rCmpArr.Found() )
continue;
// dann haben wir den Bereich zusammen
if( (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
return sal_False;
lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False );
return sal_True;
}
if( !rCmpArr.CheckStack() ||
(nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
return sal_False;
lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False );
return sal_True;
}
int lcl_Search( const SwCntntNode& rCNd, const SfxItemSet& rCmpSet, sal_Bool bNoColls )
{
// nur die harte Attributierung suchen ?
if( bNoColls && !rCNd.HasSwAttrSet() )
return sal_False;
const SfxItemSet& rNdSet = rCNd.GetSwAttrSet();
SfxItemIter aIter( rCmpSet );
const SfxPoolItem* pItem = aIter.GetCurItem();
const SfxPoolItem* pNdItem;
sal_uInt16 nWhich;
while( sal_True )
{
// nur testen, ob vorhanden ist ?
if( IsInvalidItem( pItem ))
{
nWhich = rCmpSet.GetWhichByPos( aIter.GetCurPos() );
if( SFX_ITEM_SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
|| CmpAttr( *pNdItem, rNdSet.GetPool()->GetDefaultItem( nWhich ) ))
return sal_False;
}
else
{
nWhich = pItem->Which();
//JP 27.02.95: wenn nach defaults gesucht wird, dann muss man bis zum Pool
// runter
// if( SFX_ITEM_SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
// || *pNdItem != *pItem )
if( !CmpAttr( rNdSet.Get( nWhich, !bNoColls ), *pItem ))
return sal_False;
}
if( aIter.IsAtEnd() )
break;
pItem = aIter.NextItem();
}
return sal_True; // wurde gefunden
}
sal_Bool SwPaM::Find( const SfxPoolItem& rAttr, sal_Bool bValue, SwMoveFn fnMove,
const SwPaM *pRegion, sal_Bool bInReadOnly )
{
// stelle fest welches Attribut gesucht wird:
const sal_uInt16 nWhich = rAttr.Which();
int bCharAttr = isCHRATR(nWhich) || isTXTATR(nWhich);
SwPaM* pPam = MakeRegion( fnMove, pRegion );
sal_Bool bFound = sal_False;
sal_Bool bFirst = sal_True;
sal_Bool bSrchForward = fnMove == fnMoveForward;
SwCntntNode * pNode;
const SfxPoolItem* pItem;
SwpFmts aFmtArr;
// Wenn am Anfang/Ende, aus dem Node moven
if( bSrchForward
? pPam->GetPoint()->nContent.GetIndex() == pPam->GetCntntNode()->Len()
: !pPam->GetPoint()->nContent.GetIndex() )
{
if( !(*fnMove->fnNds)( &pPam->GetPoint()->nNode, sal_False ))
{
delete pPam;
return sal_False;
}
SwCntntNode *pNd = pPam->GetCntntNode();
xub_StrLen nTmpPos = bSrchForward ? 0 : pNd->Len();
pPam->GetPoint()->nContent.Assign( pNd, nTmpPos );
}
while( 0 != ( pNode = ::GetNode( *pPam, bFirst, fnMove, bInReadOnly ) ) )
{
if( bCharAttr )
{
if( !pNode->IsTxtNode() ) // CharAttr sind nur in TextNodes
continue;
if( ((SwTxtNode*)pNode)->HasHints() &&
lcl_Search( *(SwTxtNode*)pNode, *pPam, rAttr, fnMove, bValue ))
{
// setze auf die Werte vom Attribut
SetMark();
*GetPoint() = *pPam->GetPoint();
*GetMark() = *pPam->GetMark();
bFound = sal_True;
break;
}
else if (isTXTATR(nWhich))
continue; // --> also weiter
}
// keine harte Attributierung, dann pruefe, ob die Vorlage schon
// mal nach dem Attribut befragt wurde
if( !pNode->HasSwAttrSet() )
{
const SwFmt* pTmpFmt = pNode->GetFmtColl();
if( aFmtArr.Count() && aFmtArr.Seek_Entry( pTmpFmt ))
continue; // die Collection wurde schon mal befragt
aFmtArr.Insert( pTmpFmt );
}
if( SFX_ITEM_SET == pNode->GetSwAttrSet().GetItemState( nWhich,
sal_True, &pItem ) && ( !bValue || *pItem == rAttr ) )
{
// FORWARD: Point an das Ende, GetMark zum Anfanf vom Node
// BACKWARD: Point zum Anfang, GetMark an das Ende vom Node
// und immer nach der Logik: inkl. Start, exkl. End !!!
*GetPoint() = *pPam->GetPoint();
SetMark();
pNode->MakeEndIndex( &GetPoint()->nContent );
bFound = sal_True;
break;
}
}
// beim rueckwaerts Suchen noch Point und Mark vertauschen
if( bFound && !bSrchForward )
Exchange();
delete pPam;
return bFound;
}
typedef int (*FnSearchAttr)( const SwTxtNode&, SwAttrCheckArr&, SwPaM& );
sal_Bool SwPaM::Find( const SfxItemSet& rSet, sal_Bool bNoColls, SwMoveFn fnMove,
const SwPaM *pRegion, sal_Bool bInReadOnly, sal_Bool bMoveFirst )
{
SwPaM* pPam = MakeRegion( fnMove, pRegion );
sal_Bool bFound = sal_False;
sal_Bool bFirst = sal_True;
sal_Bool bSrchForward = fnMove == fnMoveForward;
SwCntntNode * pNode;
SwpFmts aFmtArr;
// teste doch mal welche Text/Char-Attribute gesucht werden
SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls );
SfxItemSet aOtherSet( GetDoc()->GetAttrPool(),
RES_PARATR_BEGIN, RES_GRFATR_END-1 );
aOtherSet.Put( rSet, sal_False ); // alle Invalid-Items erhalten!
FnSearchAttr fnSearch = bSrchForward
? (&::lcl_SearchForward)
: (&::lcl_SearchBackward);
// Wenn am Anfang/Ende, aus dem Node moven
// Wenn am Anfang/Ende, aus dem Node moven
if( bMoveFirst &&
( bSrchForward
? pPam->GetPoint()->nContent.GetIndex() == pPam->GetCntntNode()->Len()
: !pPam->GetPoint()->nContent.GetIndex() ) )
{
if( !(*fnMove->fnNds)( &pPam->GetPoint()->nNode, sal_False ))
{
delete pPam;
return sal_False;
}
SwCntntNode *pNd = pPam->GetCntntNode();
xub_StrLen nTmpPos = bSrchForward ? 0 : pNd->Len();
pPam->GetPoint()->nContent.Assign( pNd, nTmpPos );
}
while( 0 != ( pNode = ::GetNode( *pPam, bFirst, fnMove, bInReadOnly ) ) )
{
if( aCmpArr.Count() )
{
if( !pNode->IsTxtNode() ) // CharAttr sind nur in TextNodes
continue;
if( (!aOtherSet.Count() ||
lcl_Search( *pNode, aOtherSet, bNoColls )) &&
(*fnSearch)( *(SwTxtNode*)pNode, aCmpArr, *pPam ))
{
// setze auf die Werte vom Attribut
SetMark();
*GetPoint() = *pPam->GetPoint();
*GetMark() = *pPam->GetMark();
bFound = sal_True;
break;
}
continue; // TextAttribute
}
if( !aOtherSet.Count() )
continue;
// keine harte Attributierung, dann pruefe, ob die Vorlage schon
// mal nach dem Attribut befragt wurde
if( !pNode->HasSwAttrSet() )
{
const SwFmt* pTmpFmt = pNode->GetFmtColl();
if( aFmtArr.Count() && aFmtArr.Seek_Entry( pTmpFmt ))
continue; // die Collection wurde schon mal befragt
aFmtArr.Insert( pTmpFmt );
}
if( lcl_Search( *pNode, aOtherSet, bNoColls ))
{
// FORWARD: Point an das Ende, GetMark zum Anfanf vom Node
// BACKWARD: Point zum Anfang, GetMark an das Ende vom Node
// und immer nach der Logik: inkl. Start, exkl. End !!!
*GetPoint() = *pPam->GetPoint();
SetMark();
pNode->MakeEndIndex( &GetPoint()->nContent );
bFound = sal_True;
break;
}
}
// beim rueckwaerts Suchen noch Point und Mark vertauschen
if( bFound && !bSrchForward )
Exchange();
delete pPam;
return bFound;
}
//------------------ Methoden vom SwCursor ---------------------------
// Parameter fuer das Suchen vom Attributen
struct SwFindParaAttr : public SwFindParas
{
sal_Bool bValue;
const SfxItemSet *pSet, *pReplSet;
const SearchOptions *pSearchOpt;
SwCursor& rCursor;
utl::TextSearch* pSTxt;
SwFindParaAttr( const SfxItemSet& rSet, sal_Bool bNoCollection,
const SearchOptions* pOpt, const SfxItemSet* pRSet,
SwCursor& rCrsr )
: bValue( bNoCollection ), pSet( &rSet ), pReplSet( pRSet ),
pSearchOpt( pOpt ), rCursor( rCrsr ),pSTxt( 0 ) {}
virtual ~SwFindParaAttr() { delete pSTxt; }
virtual int Find( SwPaM* , SwMoveFn , const SwPaM*, sal_Bool bInReadOnly );
virtual int IsReplaceMode() const;
};
int SwFindParaAttr::Find( SwPaM* pCrsr, SwMoveFn fnMove, const SwPaM* pRegion,
sal_Bool bInReadOnly )
{
// String ersetzen ?? (nur wenn Text angegeben oder nicht attributiert
// gesucht wird)
sal_Bool bReplaceTxt = pSearchOpt && ( pSearchOpt->replaceString.getLength() ||
!pSet->Count() );
sal_Bool bReplaceAttr = pReplSet && pReplSet->Count();
sal_Bool bMoveFirst = !bReplaceAttr;
if( bInReadOnly && (bReplaceAttr || bReplaceTxt ))
bInReadOnly = sal_False;
// wir suchen nach Attributen, soll zusaetzlich Text gesucht werden ?
{
SwPaM aRegion( *pRegion->GetMark(), *pRegion->GetPoint() );
SwPaM* pTextRegion = &aRegion;
SwPaM aSrchPam( *pCrsr->GetPoint() );
while( sal_True )
{
if( pSet->Count() ) // gibts ueberhaupt Attributierung?
{
// zuerst die Attributierung
if( !aSrchPam.Find( *pSet, bValue, fnMove, &aRegion, bInReadOnly, bMoveFirst ) )
//JP 17.11.95: was ist mit Attributen in leeren Absaetzen !!
// || *pCrsr->GetMark() == *pCrsr->GetPoint() ) // kein Bereich ??
return FIND_NOT_FOUND;
bMoveFirst = sal_True;
if( !pSearchOpt )
break; // ok, nur Attribute, also gefunden
pTextRegion = &aSrchPam;
}
else if( !pSearchOpt )
return FIND_NOT_FOUND;
// dann darin den Text
if( !pSTxt )
{
SearchOptions aTmp( *pSearchOpt );
// search in selection
aTmp.searchFlag |= (SearchFlags::REG_NOT_BEGINOFLINE |
SearchFlags::REG_NOT_ENDOFLINE);
MsLangId::convertLanguageToLocale( LANGUAGE_SYSTEM, aTmp.Locale );
pSTxt = new utl::TextSearch( aTmp );
}
// todo/mba: searching for attributes in Outliner text?!
sal_Bool bSearchInNotes = sal_False;
// Bug 24665: suche im richtigen Bereich weiter (pTextRegion!)
if( aSrchPam.Find( *pSearchOpt, bSearchInNotes, *pSTxt, fnMove, pTextRegion, bInReadOnly ) &&
*aSrchPam.GetMark() != *aSrchPam.GetPoint() ) // gefunden ?
break; // also raus
else if( !pSet->Count() )
return FIND_NOT_FOUND; // nur Text und nicht gefunden
/* // --> FME 2007-4-12 #i74765 # Why should we move the position?
Moving the position results in bugs when there are two adjacent
portions which both have the requested attributes set. I suspect this
should be only be an optimization. Therefore I boldly remove it now!
// JP: und wieder neu aufsetzen, aber eine Position weiter
//JP 04.11.97: Bug 44897 - aber den Mark wieder aufheben, damit
// weiterbewegt werden kann!
{
sal_Bool bCheckRegion = sal_True;
SwPosition* pPos = aSrchPam.GetPoint();
if( !(*fnMove->fnNd)( &pPos->nNode.GetNode(),
&pPos->nContent, CRSR_SKIP_CHARS ))
{
if( (*fnMove->fnNds)( &pPos->nNode, sal_False ))
{
SwCntntNode *pNd = pPos->nNode.GetNode().GetCntntNode();
xub_StrLen nCPos;
if( fnMove == fnMoveForward )
nCPos = 0;
else
nCPos = pNd->Len();
pPos->nContent.Assign( pNd, nCPos );
}
else
bCheckRegion = sal_False;
}
if( !bCheckRegion || *aRegion.GetPoint() <= *pPos )
return FIND_NOT_FOUND; // nicht gefunden
}*/
*aRegion.GetMark() = *aSrchPam.GetPoint();
}
*pCrsr->GetPoint() = *aSrchPam.GetPoint();
pCrsr->SetMark();
*pCrsr->GetMark() = *aSrchPam.GetMark();
}
if( bReplaceTxt )
{
const bool bRegExp(
SearchAlgorithms_REGEXP == pSearchOpt->algorithmType);
SwIndex& rSttCntIdx = pCrsr->Start()->nContent;
xub_StrLen nSttCnt = rSttCntIdx.GetIndex();
// damit die Region auch verschoben wird, in den Shell-Cursr-Ring
// mit aufnehmen !!
Ring *pPrevRing = 0;
if( bRegExp )
{
pPrevRing = pRegion->GetPrev();
((Ring*)pRegion)->MoveRingTo( &rCursor );
}
::std::auto_ptr<String> pRepl( (bRegExp) ?
ReplaceBackReferences( *pSearchOpt, pCrsr ) : 0 );
rCursor.GetDoc()->ReplaceRange( *pCrsr,
(pRepl.get()) ? *pRepl : String(pSearchOpt->replaceString),
bRegExp );
rCursor.SaveTblBoxCntnt( pCrsr->GetPoint() );
if( bRegExp )
{
// und die Region wieder herausnehmen:
Ring *p, *pNext = (Ring*)pRegion;
do {
p = pNext;
pNext = p->GetNext();
p->MoveTo( (Ring*)pRegion );
} while( p != pPrevRing );
}
rSttCntIdx = nSttCnt;
}
if( bReplaceAttr )
{
// --- Ist die Selection noch da ??????
// und noch die Attribute setzen
#ifdef OLD
pCrsr->GetDoc()->Insert( *pCrsr, *pReplSet, 0 );
#else
//JP 13.07.95: alle gesuchten Attribute werden, wenn nicht im
// ReplaceSet angegeben, auf Default zurueck gesetzt
if( !pSet->Count() )
{
pCrsr->GetDoc()->InsertItemSet( *pCrsr, *pReplSet, 0 );
}
else
{
SfxItemPool* pPool = pReplSet->GetPool();
SfxItemSet aSet( *pPool, pReplSet->GetRanges() );
SfxItemIter aIter( *pSet );
const SfxPoolItem* pItem = aIter.GetCurItem();
while( sal_True )
{
// alle die nicht gesetzt sind mit Pool-Defaults aufuellen
if( !IsInvalidItem( pItem ) && SFX_ITEM_SET !=
pReplSet->GetItemState( pItem->Which(), sal_False ))
aSet.Put( pPool->GetDefaultItem( pItem->Which() ));
if( aIter.IsAtEnd() )
break;
pItem = aIter.NextItem();
}
aSet.Put( *pReplSet );
pCrsr->GetDoc()->InsertItemSet( *pCrsr, aSet, 0 );
}
#endif
return FIND_NO_RING;
}
else
return FIND_FOUND;
}
int SwFindParaAttr::IsReplaceMode() const
{
return ( pSearchOpt && pSearchOpt->replaceString.getLength() ) ||
( pReplSet && pReplSet->Count() );
}
// Suchen nach Attributen
sal_uLong SwCursor::Find( const SfxItemSet& rSet, sal_Bool bNoCollections,
SwDocPositions nStart, SwDocPositions nEnde, sal_Bool& bCancel,
FindRanges eFndRngs,
const SearchOptions* pSearchOpt, const SfxItemSet* pReplSet )
{
// OLE-Benachrichtigung abschalten !!
SwDoc* pDoc = GetDoc();
Link aLnk( pDoc->GetOle2Link() );
pDoc->SetOle2Link( Link() );
sal_Bool bReplace = ( pSearchOpt && ( pSearchOpt->replaceString.getLength() ||
!rSet.Count() ) ) ||
(pReplSet && pReplSet->Count());
bool const bStartUndo = pDoc->GetIDocumentUndoRedo().DoesUndo() && bReplace;
if (bStartUndo)
{
pDoc->GetIDocumentUndoRedo().StartUndo( UNDO_REPLACE, NULL );
}
SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt,
pReplSet, *this );
sal_uLong nRet = FindAll(aSwFindParaAttr, nStart, nEnde, eFndRngs, bCancel );
pDoc->SetOle2Link( aLnk );
if( nRet && bReplace )
pDoc->SetModified();
if (bStartUndo)
{
pDoc->GetIDocumentUndoRedo().EndUndo( UNDO_REPLACE, NULL );
}
return nRet;
}