/************************************************************** * * 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 #include #define _SVSTDARR_USHORTS #define _SVSTDARR_ULONGS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(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 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 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 separate 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 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 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(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; }