blob: 9cdac9bd99d47ec0d46fbd91fb522f431756b61a [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/util/SearchOptions.hpp>
#include <com/sun/star/util/SearchFlags.hpp>
#define _SVSTDARR_USHORTS
#define _SVSTDARR_ULONGS
#include <svl/svstdarr.hxx>
#include <vcl/svapp.hxx>
#include <vcl/window.hxx>
#include <txatritr.hxx>
#include <fldbas.hxx>
#include <fmtfld.hxx>
#include <txtatr.hxx>
#include <txtfld.hxx>
#include <swcrsr.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <pamtyp.hxx>
#include <ndtxt.hxx>
#include <swundo.hxx>
#include <UndoInsert.hxx>
#include <breakit.hxx>
#include <docsh.hxx>
#include <PostItMgr.hxx>
#include <viewsh.hxx>
using namespace ::com::sun::star;
using namespace util;
String *ReplaceBackReferences( const SearchOptions& rSearchOpt, SwPaM* pPam );
String& lcl_CleanStr(
const SwTxtNode& rNd,
const xub_StrLen nStart,
xub_StrLen& rEnde,
SvULongs& rArr,
String& rRet,
const bool bRemoveSoftHyphen )
{
rRet = rNd.GetTxt();
if( rArr.Count() )
rArr.Remove( 0, rArr.Count() );
const SwpHints *pHts = rNd.GetpSwpHints();
sal_uInt16 n = 0;
xub_StrLen nSoftHyphen = nStart;
xub_StrLen nHintStart = STRING_LEN;
bool bNewHint = true;
bool bNewSoftHyphen = true;
const xub_StrLen nEnd = rEnde;
SvUShorts aReplaced;
do
{
if ( bNewHint )
nHintStart = pHts && n < pHts->Count() ?
*(*pHts)[n]->GetStart() :
STRING_LEN;
if ( bNewSoftHyphen )
nSoftHyphen = bRemoveSoftHyphen ?
rNd.GetTxt().Search( CHAR_SOFTHYPHEN, nSoftHyphen ) :
STRING_LEN;
bNewHint = false;
bNewSoftHyphen = false;
xub_StrLen nStt = 0;
// Check if next stop is a hint.
if ( STRING_LEN != nHintStart && nHintStart < nSoftHyphen && nHintStart < nEnd )
{
nStt = nHintStart;
bNewHint = true;
}
// Check if next stop is a soft hyphen.
else if ( STRING_LEN != nSoftHyphen && nSoftHyphen < nHintStart && nSoftHyphen < nEnd )
{
nStt = nSoftHyphen;
bNewSoftHyphen = true;
}
// If nSoftHyphen == nHintStart, the current hint *must* be a hint with an end.
else if ( STRING_LEN != nSoftHyphen && nSoftHyphen == nHintStart )
{
nStt = nSoftHyphen;
bNewHint = true;
bNewSoftHyphen = true;
}
else
break;
const xub_StrLen nAkt = nStt - rArr.Count();
if ( bNewHint )
{
const SwTxtAttr* pHt = (*pHts)[n];
if ( pHt->HasDummyChar() && (nStt >= nStart) )
{
switch( pHt->Which() )
{
case RES_TXTATR_FLYCNT:
case RES_TXTATR_FTN:
case RES_TXTATR_FIELD:
case RES_TXTATR_ANNOTATION:
case RES_TXTATR_REFMARK:
case RES_TXTATR_TOXMARK:
case RES_TXTATR_META:
case RES_TXTATR_METAFIELD:
{
const bool bEmpty =
( pHt->Which() != RES_TXTATR_FIELD
&& pHt->Which() != RES_TXTATR_ANNOTATION )
|| !(static_cast<SwTxtFld const*>(pHt)->GetFmtFld().GetField()->ExpandField(true).Len());
if ( bEmpty && nStart == nAkt )
{
rArr.Insert( nAkt, rArr.Count() );
--rEnde;
rRet.Erase( nAkt, 1 );
}
else
{
if ( bEmpty )
aReplaced.Insert( nAkt, aReplaced.Count() );
rRet.SetChar( nAkt, '\x7f' );
}
}
break;
default:
ASSERT( false, "unknown case in lcl_CleanStr" )
break;
}
}
++n;
}
if ( bNewSoftHyphen )
{
rArr.Insert( nAkt, rArr.Count() );
--rEnde;
rRet.Erase( nAkt, 1 );
++nSoftHyphen;
}
}
while ( true );
for( sal_uInt16 i = aReplaced.Count(); i; )
{
const xub_StrLen nTmp = aReplaced[ --i ];
if( nTmp == rRet.Len() - 1 )
{
rRet.Erase( nTmp );
rArr.Insert( nTmp, rArr.Count() );
--rEnde;
}
}
return rRet;
}
// skip all non SwPostIts inside the array
xub_StrLen GetPostIt(xub_StrLen aCount,const SwpHints *pHts)
{
xub_StrLen aIndex = 0;
while (aCount)
{
for (xub_StrLen i = 0; i <pHts->Count();i++)
{
aIndex++;
const SwTxtAttr* pTxtAttr = (*pHts)[i];
if ( pTxtAttr->Which() == RES_TXTATR_ANNOTATION )
{
aCount--;
if (!aCount)
break;
}
}
}
// throw away all following non postits
for (xub_StrLen i = aIndex; i <pHts->Count();i++)
{
const SwTxtAttr* pTxtAttr = (*pHts)[i];
if ( pTxtAttr->Which() == RES_TXTATR_ANNOTATION )
break;
else
aIndex++;
}
return aIndex;
}
sal_uInt8 SwPaM::Find( const SearchOptions& rSearchOpt, sal_Bool bSearchInNotes , utl::TextSearch& rSTxt,
SwMoveFn fnMove, const SwPaM * pRegion,
sal_Bool bInReadOnly )
{
if( !rSearchOpt.searchString.getLength() )
return sal_False;
SwPaM* pPam = MakeRegion( fnMove, pRegion );
sal_Bool bSrchForward = fnMove == fnMoveForward;
SwNodeIndex& rNdIdx = pPam->GetPoint()->nNode;
SwIndex& rCntntIdx = pPam->GetPoint()->nContent;
// Wenn am Anfang/Ende, aus dem Node moven
// beim leeren Node nicht weiter
if( bSrchForward
? ( rCntntIdx.GetIndex() == pPam->GetCntntNode()->Len() &&
rCntntIdx.GetIndex() )
: !rCntntIdx.GetIndex() && pPam->GetCntntNode()->Len() )
{
if( !(*fnMove->fnNds)( &rNdIdx, sal_False ))
{
delete pPam;
return sal_False;
}
SwCntntNode *pNd = rNdIdx.GetNode().GetCntntNode();
xub_StrLen nTmpPos = bSrchForward ? 0 : pNd->Len();
rCntntIdx.Assign( pNd, nTmpPos );
}
/*
* Ist bFound == sal_True, dann wurde der String gefunden und in
* nStart und nEnde steht der gefundenen String
*/
sal_Bool bFound = sal_False;
/*
* StartPostion im Text oder Anfangsposition
*/
sal_Bool bFirst = sal_True;
SwCntntNode * pNode;
//testarea
//String sCleanStr;
//SvULongs aFltArr;
//const SwNode* pSttNd = &rNdIdx.GetNode();
xub_StrLen nStart, nEnde, nTxtLen;
sal_Bool bRegSearch = SearchAlgorithms_REGEXP == rSearchOpt.algorithmType;
sal_Bool bChkEmptyPara = bRegSearch && 2 == rSearchOpt.searchString.getLength() &&
( !rSearchOpt.searchString.compareToAscii( "^$" ) ||
!rSearchOpt.searchString.compareToAscii( "$^" ) );
sal_Bool bChkParaEnd = bRegSearch && 1 == rSearchOpt.searchString.getLength() &&
!rSearchOpt.searchString.compareToAscii( "$" );
// LanguageType eLastLang = 0;
while( 0 != ( pNode = ::GetNode( *pPam, bFirst, fnMove, bInReadOnly ) ))
{
if( pNode->IsTxtNode() )
{
nTxtLen = ((SwTxtNode*)pNode)->GetTxt().Len();
if( rNdIdx == pPam->GetMark()->nNode )
nEnde = pPam->GetMark()->nContent.GetIndex();
else
nEnde = bSrchForward ? nTxtLen : 0;
nStart = rCntntIdx.GetIndex();
/* #i80135# */
// if there are SwPostItFields inside our current node text, we split the text into seperate pieces
// and search for text inside the pieces as well as inside the fields
const SwpHints *pHts = ((SwTxtNode*)pNode)->GetpSwpHints();
// count postitfields by looping over all fields
xub_StrLen aNumberPostits = 0;
xub_StrLen aIgnore = 0;
if (pHts && bSearchInNotes)
{
if (!bSrchForward)
{
xub_StrLen swap = nEnde;
nEnde = nStart;
nStart = swap;
}
for (xub_StrLen i = 0; i <pHts->Count();i++)
{
const xub_StrLen aPos = *(*pHts)[i]->GetStart();
const SwTxtAttr* pTxtAttr = (*pHts)[i];
if ( pTxtAttr->Which()==RES_TXTATR_ANNOTATION )
{
if ( (aPos >= nStart) && (aPos <= nEnde) )
aNumberPostits++;
else
{
if (bSrchForward)
aIgnore++;
}
}
}
if (!bSrchForward)
{
xub_StrLen swap = nEnde;
nEnde = nStart;
nStart = swap;
}
}
SwDocShell *const pDocShell = pNode->GetDoc()->GetDocShell();
ViewShell *const pWrtShell = (pDocShell) ? (ViewShell*)(pDocShell->GetWrtShell()) : 0;
SwPostItMgr *const pPostItMgr = (pWrtShell) ? pWrtShell->GetPostItMgr() : 0;
xub_StrLen aStart = 0;
// do we need to finish a note?
if (pPostItMgr && pPostItMgr->HasActiveSidebarWin())
{
if (bSearchInNotes)
{
if (bSrchForward)
aStart++;
else
{
if (aNumberPostits)
--aNumberPostits;
}
//search inside and finsih and put focus back into the doc
if (pPostItMgr->FinishSearchReplace(rSearchOpt,bSrchForward))
{
bFound = true ;
break;
}
}
else
{
pPostItMgr->SetActiveSidebarWin(0);
}
}
if (aNumberPostits)
{
// now we have to split
xub_StrLen nStartInside = 0;
xub_StrLen nEndeInside = 0;
sal_Int16 aLoop= bSrchForward ? aStart : aNumberPostits;
while ( (aLoop>=0) && (aLoop<=aNumberPostits))
{
if (bSrchForward)
{
nStartInside = aLoop==0 ? nStart : *(*pHts)[GetPostIt(aLoop+aIgnore-1,pHts)]->GetStart()+1;
nEndeInside = aLoop==aNumberPostits? nEnde : *(*pHts)[GetPostIt(aLoop+aIgnore,pHts)]->GetStart();
nTxtLen = nEndeInside-nStartInside;
}
else
{
nStartInside = aLoop==aNumberPostits ? nStart : *(*pHts)[GetPostIt(aLoop+aIgnore,pHts)]->GetStart();
nEndeInside = aLoop==0 ? nEnde : *(*pHts)[GetPostIt(aLoop+aIgnore-1,pHts)]->GetStart()+1;
nTxtLen = nStartInside-nEndeInside;
}
// search inside the text between a note
bFound = DoSearch(rSearchOpt,rSTxt,fnMove,bSrchForward,bRegSearch,bChkEmptyPara,bChkParaEnd,
nStartInside,nEndeInside,nTxtLen, pNode,pPam);
if (bFound)
break;
else
{
// we should now be right in front of a note, search inside
if ( (bSrchForward && (GetPostIt(aLoop + aIgnore,pHts) < pHts->Count()) ) || ( !bSrchForward && (aLoop!=0) ))
{
const SwTxtAttr* pTxtAttr = bSrchForward ? (*pHts)[GetPostIt(aLoop+aIgnore,pHts)] : (*pHts)[GetPostIt(aLoop+aIgnore-1,pHts)];
if ( pPostItMgr && pPostItMgr->SearchReplace(((SwTxtFld*)pTxtAttr)->GetFmtFld(),rSearchOpt,bSrchForward) )
{
bFound = true ;
break;
}
}
}
aLoop = bSrchForward ? aLoop+1 : aLoop-1;
}
}
else
{
// if there is no SwPostItField inside or searching inside notes is disabled, we search the whole length just like before
bFound = DoSearch(rSearchOpt,rSTxt,fnMove,bSrchForward,bRegSearch,bChkEmptyPara,bChkParaEnd,
nStart,nEnde,nTxtLen, pNode,pPam);
}
if (bFound)
break;
}
}
delete pPam;
return bFound;
}
bool SwPaM::DoSearch( const SearchOptions& rSearchOpt, utl::TextSearch& rSTxt,
SwMoveFn fnMove,
sal_Bool bSrchForward, sal_Bool bRegSearch, sal_Bool bChkEmptyPara, sal_Bool bChkParaEnd,
xub_StrLen &nStart, xub_StrLen &nEnde, xub_StrLen nTxtLen,SwNode* pNode, SwPaM* pPam)
{
bool bFound = false;
SwNodeIndex& rNdIdx = pPam->GetPoint()->nNode;
const SwNode* pSttNd = &rNdIdx.GetNode();
String sCleanStr;
SvULongs aFltArr;
LanguageType eLastLang = 0;
// if the search string contains a soft hypen, we don't strip them from the text:
bool bRemoveSoftHyphens = true;
if ( bRegSearch )
{
const rtl::OUString a00AD( rtl::OUString::createFromAscii( "\\x00AD" ) );
if ( -1 != rSearchOpt.searchString.indexOf( a00AD ) )
bRemoveSoftHyphens = false;
}
else
{
if ( 1 == rSearchOpt.searchString.getLength() &&
CHAR_SOFTHYPHEN == rSearchOpt.searchString.toChar() )
bRemoveSoftHyphens = false;
}
if( bSrchForward )
lcl_CleanStr( *(SwTxtNode*)pNode, nStart, nEnde,
aFltArr, sCleanStr, bRemoveSoftHyphens );
else
lcl_CleanStr( *(SwTxtNode*)pNode, nEnde, nStart,
aFltArr, sCleanStr, bRemoveSoftHyphens );
SwScriptIterator* pScriptIter = 0;
sal_uInt16 nSearchScript = 0;
sal_uInt16 nCurrScript = 0;
if ( SearchAlgorithms_APPROXIMATE == rSearchOpt.algorithmType &&
pBreakIt->GetBreakIter().is() )
{
pScriptIter = new SwScriptIterator( sCleanStr, nStart, bSrchForward );
nSearchScript = pBreakIt->GetRealScriptOfText( rSearchOpt.searchString, 0 );
}
xub_StrLen nStringEnd = nEnde;
while ( (bSrchForward && nStart < nStringEnd) ||
(! bSrchForward && nStart > nStringEnd) )
{
// SearchAlgorithms_APPROXIMATE works on a per word base
// so we have to provide the text searcher with the correct
// locale, because it uses the breakiterator
if ( pScriptIter )
{
nEnde = pScriptIter->GetScriptChgPos();
nCurrScript = pScriptIter->GetCurrScript();
if ( nSearchScript == nCurrScript )
{
const LanguageType eCurrLang =
((SwTxtNode*)pNode)->GetLang( bSrchForward ?
nStart :
nEnde );
if ( eCurrLang != eLastLang )
{
const lang::Locale aLocale(
pBreakIt->GetLocale( eCurrLang ) );
rSTxt.SetLocale( rSearchOpt, aLocale );
eLastLang = eCurrLang;
}
}
pScriptIter->Next();
}
if( nSearchScript == nCurrScript &&
(rSTxt.*fnMove->fnSearch)( sCleanStr, &nStart, &nEnde, 0 ))
{
// setze den Bereich richtig
*GetPoint() = *pPam->GetPoint();
SetMark();
// Start und Ende wieder korrigieren !!
if( aFltArr.Count() )
{
xub_StrLen n, nNew;
// bei Rueckwaertssuche die Positionen temp. vertauschen
if( !bSrchForward ) { n = nStart; nStart = nEnde; nEnde = n; }
for( n = 0, nNew = nStart;
n < aFltArr.Count() && aFltArr[ n ] <= nStart;
++n, ++nNew )
;
nStart = nNew;
for( n = 0, nNew = nEnde;
n < aFltArr.Count() && aFltArr[ n ] < nEnde;
++n, ++nNew )
;
nEnde = nNew;
// bei Rueckwaertssuche die Positionen temp. vertauschen
if( !bSrchForward ) { n = nStart; nStart = nEnde; nEnde = n; }
}
GetMark()->nContent = nStart; // Startposition setzen
GetPoint()->nContent = nEnde;
if( !bSrchForward ) // rueckwaerts Suche?
Exchange(); // Point und Mark tauschen
bFound = sal_True;
break;
}
nStart = nEnde;
} // end of script while
delete pScriptIter;
if ( bFound )
return true;
else if( ( bChkEmptyPara && !nStart && !nTxtLen ) || bChkParaEnd )
{
*GetPoint() = *pPam->GetPoint();
GetPoint()->nContent = bChkParaEnd ? nTxtLen : 0;
SetMark();
if( (bSrchForward || pSttNd != &rNdIdx.GetNode()) &&
Move( fnMoveForward, fnGoCntnt ) &&
(!bSrchForward || pSttNd != &GetPoint()->nNode.GetNode()) &&
1 == Abs( (int)( GetPoint()->nNode.GetIndex() -
GetMark()->nNode.GetIndex()) ) )
{
if( !bSrchForward ) // rueckwaerts Suche?
Exchange(); // Point und Mark tauschen
//bFound = sal_True;
//break;
return true;
}
}
return bFound;
}
// Parameter fuers Suchen und Ersetzen von Text
struct SwFindParaText : public SwFindParas
{
const SearchOptions& rSearchOpt;
SwCursor& rCursor;
utl::TextSearch aSTxt;
sal_Bool bReplace;
sal_Bool bSearchInNotes;
SwFindParaText( const SearchOptions& rOpt, sal_Bool bSearchNotes, int bRepl, SwCursor& rCrsr )
: rSearchOpt( rOpt ), rCursor( rCrsr ), aSTxt( rOpt ), bReplace( 0 != bRepl ), bSearchInNotes( bSearchNotes )
{}
virtual int Find( SwPaM* , SwMoveFn , const SwPaM*, sal_Bool bInReadOnly );
virtual int IsReplaceMode() const;
virtual ~SwFindParaText();
};
SwFindParaText::~SwFindParaText()
{
}
int SwFindParaText::Find( SwPaM* pCrsr, SwMoveFn fnMove,
const SwPaM* pRegion, sal_Bool bInReadOnly )
{
if( bInReadOnly && bReplace )
bInReadOnly = sal_False;
sal_Bool bFnd = (sal_Bool)pCrsr->Find( rSearchOpt, bSearchInNotes, aSTxt, fnMove, pRegion, bInReadOnly );
/* #i80135# if we found something in a note, Mark and Point is the same
if( bFnd && *pCrsr->GetMark() == *pCrsr->GetPoint() )
return FIND_NOT_FOUND;
*/
if( bFnd && bReplace ) // String ersetzen ??
{
// Replace-Methode vom SwDoc benutzen
const bool bRegExp(SearchAlgorithms_REGEXP == rSearchOpt.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 *pPrev(0);
if( bRegExp )
{
pPrev = pRegion->GetPrev();
((Ring*)pRegion)->MoveRingTo( &rCursor );
}
::std::auto_ptr<String> pRepl( (bRegExp)
? ReplaceBackReferences( rSearchOpt, pCrsr ) : 0 );
rCursor.GetDoc()->ReplaceRange( *pCrsr,
(pRepl.get()) ? *pRepl : String(rSearchOpt.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 != pPrev );
}
pCrsr->Start()->nContent = nSttCnt;
return FIND_NO_RING;
}
return bFnd ? FIND_FOUND : FIND_NOT_FOUND;
}
int SwFindParaText::IsReplaceMode() const
{
return bReplace;
}
sal_uLong SwCursor::Find( const SearchOptions& rSearchOpt, sal_Bool bSearchInNotes,
SwDocPositions nStart, SwDocPositions nEnde,
sal_Bool& bCancel,
FindRanges eFndRngs, int bReplace )
{
// OLE-Benachrichtigung abschalten !!
SwDoc* pDoc = GetDoc();
Link aLnk( pDoc->GetOle2Link() );
pDoc->SetOle2Link( Link() );
bool const bStartUndo = pDoc->GetIDocumentUndoRedo().DoesUndo() && bReplace;
if (bStartUndo)
{
pDoc->GetIDocumentUndoRedo().StartUndo( UNDO_REPLACE, NULL );
}
sal_Bool bSearchSel = 0 != (rSearchOpt.searchFlag & SearchFlags::REG_NOT_BEGINOFLINE);
if( bSearchSel )
eFndRngs = (FindRanges)(eFndRngs | FND_IN_SEL);
SwFindParaText aSwFindParaText( rSearchOpt, bSearchInNotes, bReplace, *this );
sal_uLong nRet = FindAll( aSwFindParaText, nStart, nEnde, eFndRngs, bCancel );
pDoc->SetOle2Link( aLnk );
if( nRet && bReplace )
pDoc->SetModified();
if (bStartUndo)
{
SwRewriter rewriter(MakeUndoReplaceRewriter(
nRet, rSearchOpt.searchString, rSearchOpt.replaceString));
pDoc->GetIDocumentUndoRedo().EndUndo( UNDO_REPLACE, & rewriter );
}
return nRet;
}
String *ReplaceBackReferences( const SearchOptions& rSearchOpt, SwPaM* pPam )
{
String *pRet = 0;
if( pPam && pPam->HasMark() &&
SearchAlgorithms_REGEXP == rSearchOpt.algorithmType )
{
const SwCntntNode* pTxtNode = pPam->GetCntntNode( sal_True );
if( pTxtNode && pTxtNode->IsTxtNode() && pTxtNode == pPam->GetCntntNode( sal_False ) )
{
utl::TextSearch aSTxt( rSearchOpt );
const String& rStr = static_cast<const SwTxtNode*>(pTxtNode)->GetTxt();
xub_StrLen nStart = pPam->Start()->nContent.GetIndex();
xub_StrLen nEnd = pPam->End()->nContent.GetIndex();
SearchResult aResult;
if( aSTxt.SearchFrwrd( rStr, &nStart, &nEnd, &aResult ) )
{
String aReplaceStr( rSearchOpt.replaceString );
aSTxt.ReplaceBackReferences( aReplaceStr, rStr, aResult );
pRet = new String( aReplaceStr );
}
}
}
return pRet;
}