1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: findtxt.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
33 #include <com/sun/star/util/SearchOptions.hpp>
34 #include <com/sun/star/util/SearchFlags.hpp>
38 #define _SVSTDARR_USHORTS
39 #define _SVSTDARR_ULONGS
40 #include <svtools/svstdarr.hxx>
41 #include <vcl/svapp.hxx>
42 #include <txatritr.hxx>
52 #include <breakit.hxx>
56 #include <PostItMgr.hxx>
58 #include <vcl/window.hxx>
60 #define POSTITMGR ((ViewShell*)pNode->GetDoc()->GetDocShell()->GetWrtShell())->GetPostItMgr()
62 using namespace ::com::sun::star
;
65 String
*ReplaceBackReferences( const SearchOptions
& rSearchOpt
, SwPaM
* pPam
);
67 String
& lcl_CleanStr( const SwTxtNode
& rNd
, xub_StrLen nStart
,
68 xub_StrLen
& rEnde
, SvULongs
& rArr
, String
& rRet
,
69 bool bRemoveSoftHyphen
)
73 rArr
.Remove( 0, rArr
.Count() );
75 const SwpHints
*pHts
= rNd
.GetpSwpHints();
78 xub_StrLen nSoftHyphen
= nStart
;
79 xub_StrLen nHintStart
= STRING_LEN
;
81 bool bNewSoftHyphen
= true;
82 const xub_StrLen nEnd
= rEnde
;
88 nHintStart
= pHts
&& n
< pHts
->Count() ?
89 *(*pHts
)[n
]->GetStart() :
93 nSoftHyphen
= bRemoveSoftHyphen
?
94 rNd
.GetTxt().Search( CHAR_SOFTHYPHEN
, nSoftHyphen
) :
98 bNewSoftHyphen
= false;
102 // Check if next stop is a hint.
103 if ( STRING_LEN
!= nHintStart
&& nHintStart
< nSoftHyphen
&& nHintStart
< nEnd
)
108 // Check if next stop is a soft hyphen.
109 else if ( STRING_LEN
!= nSoftHyphen
&& nSoftHyphen
< nHintStart
&& nSoftHyphen
< nEnd
)
112 bNewSoftHyphen
= true;
114 // If nSoftHyphen == nHintStart, the current hint *must* be a hint with an end.
115 else if ( STRING_LEN
!= nSoftHyphen
&& nSoftHyphen
== nHintStart
)
119 bNewSoftHyphen
= true;
124 const xub_StrLen nAkt
= nStt
- rArr
.Count();
128 const SwTxtAttr
* pHt
= (*pHts
)[n
];
129 if ( pHt
->HasDummyChar() && (nStt
>= nStart
) )
131 //JP 17.05.00: Task 75806 ask for ">=" and not for ">"
132 switch( pHt
->Which() )
134 case RES_TXTATR_FLYCNT
:
136 case RES_TXTATR_FIELD
:
137 case RES_TXTATR_REFMARK
:
138 case RES_TXTATR_TOXMARK
:
139 case RES_TXTATR_META
:
140 case RES_TXTATR_METAFIELD
:
142 // JP 06.05.98: mit Bug 50100 werden sie als Trenner erwuenscht und nicht
143 // mehr zum Wort dazu gehoerend.
144 // MA 23.06.98: mit Bug 51215 sollen sie konsequenterweise auch am
145 // Satzanfang und -ende ignoriert werden wenn sie Leer sind.
146 // Dazu werden sie schlicht entfernt. Fuer den Anfang entfernen
148 // Fuer das Ende merken wir uns die Ersetzungen und entferenen
149 // hinterher alle am Stringende (koenten ja 'normale' 0x7f drinstehen
150 BOOL bEmpty
= RES_TXTATR_FIELD
!= pHt
->Which() ||
151 !((SwTxtFld
*)pHt
)->GetFld().GetFld()->Expand().Len();
152 if ( bEmpty
&& nStart
== nAkt
)
154 rArr
.Insert( nAkt
, rArr
.Count() );
156 rRet
.Erase( nAkt
, 1 );
161 aReplaced
.Insert( nAkt
, aReplaced
.Count() );
162 rRet
.SetChar( nAkt
, '\x7f' );
167 ASSERT( false, "unknown case in lcl_CleanStr" )
174 if ( bNewSoftHyphen
)
176 rArr
.Insert( nAkt
, rArr
.Count() );
178 rRet
.Erase( nAkt
, 1 );
184 for( USHORT i
= aReplaced
.Count(); i
; )
186 const xub_StrLen nTmp
= aReplaced
[ --i
];
187 if( nTmp
== rRet
.Len() - 1 )
190 rArr
.Insert( nTmp
, rArr
.Count() );
198 // skip all non SwPostIts inside the array
199 xub_StrLen
GetPostIt(xub_StrLen aCount
,const SwpHints
*pHts
)
201 xub_StrLen aIndex
= 0;
204 for (xub_StrLen i
= 0; i
<pHts
->Count();i
++)
207 const SwTxtAttr
* pTxtAttr
= (*pHts
)[i
];
208 if ( (pTxtAttr
->Which()==RES_TXTATR_FIELD
) &&
209 (pTxtAttr
->GetFld().GetFld()->Which()==RES_POSTITFLD
))
217 // throw away all following non postits
218 for (xub_StrLen i
= aIndex
; i
<pHts
->Count();i
++)
220 const SwTxtAttr
* pTxtAttr
= (*pHts
)[i
];
221 if ( (pTxtAttr
->Which()==RES_TXTATR_FIELD
) &&
222 (pTxtAttr
->GetFld().GetFld()->Which()==RES_POSTITFLD
))
230 BYTE
SwPaM::Find( const SearchOptions
& rSearchOpt
, BOOL bSearchInNotes
, utl::TextSearch
& rSTxt
,
231 SwMoveFn fnMove
, const SwPaM
* pRegion
,
234 if( !rSearchOpt
.searchString
.getLength() )
237 SwPaM
* pPam
= MakeRegion( fnMove
, pRegion
);
238 BOOL bSrchForward
= fnMove
== fnMoveForward
;
239 SwNodeIndex
& rNdIdx
= pPam
->GetPoint()->nNode
;
240 SwIndex
& rCntntIdx
= pPam
->GetPoint()->nContent
;
242 // Wenn am Anfang/Ende, aus dem Node moven
243 // beim leeren Node nicht weiter
245 ? ( rCntntIdx
.GetIndex() == pPam
->GetCntntNode()->Len() &&
246 rCntntIdx
.GetIndex() )
247 : !rCntntIdx
.GetIndex() && pPam
->GetCntntNode()->Len() )
249 if( !(*fnMove
->fnNds
)( &rNdIdx
, FALSE
))
254 SwCntntNode
*pNd
= rNdIdx
.GetNode().GetCntntNode();
255 xub_StrLen nTmpPos
= bSrchForward
? 0 : pNd
->Len();
256 rCntntIdx
.Assign( pNd
, nTmpPos
);
260 * Ist bFound == TRUE, dann wurde der String gefunden und in
261 * nStart und nEnde steht der gefundenen String
265 * StartPostion im Text oder Anfangsposition
272 //const SwNode* pSttNd = &rNdIdx.GetNode();
274 xub_StrLen nStart
, nEnde
, nTxtLen
;
276 BOOL bRegSearch
= SearchAlgorithms_REGEXP
== rSearchOpt
.algorithmType
;
277 BOOL bChkEmptyPara
= bRegSearch
&& 2 == rSearchOpt
.searchString
.getLength() &&
278 ( !rSearchOpt
.searchString
.compareToAscii( "^$" ) ||
279 !rSearchOpt
.searchString
.compareToAscii( "$^" ) );
280 BOOL bChkParaEnd
= bRegSearch
&& 1 == rSearchOpt
.searchString
.getLength() &&
281 !rSearchOpt
.searchString
.compareToAscii( "$" );
283 // LanguageType eLastLang = 0;
284 while( 0 != ( pNode
= ::GetNode( *pPam
, bFirst
, fnMove
, bInReadOnly
) ))
286 if( pNode
->IsTxtNode() )
288 nTxtLen
= ((SwTxtNode
*)pNode
)->GetTxt().Len();
289 if( rNdIdx
== pPam
->GetMark()->nNode
)
290 nEnde
= pPam
->GetMark()->nContent
.GetIndex();
292 nEnde
= bSrchForward
? nTxtLen
: 0;
293 nStart
= rCntntIdx
.GetIndex();
296 // if there are SwPostItFields inside our current node text, we split the text into seperate pieces
297 // and search for text inside the pieces as well as inside the fields
298 const SwpHints
*pHts
= ((SwTxtNode
*)pNode
)->GetpSwpHints();
300 // count postitfields by looping over all fields
301 xub_StrLen aNumberPostits
= 0;
302 xub_StrLen aIgnore
= 0;
303 if (pHts
&& bSearchInNotes
)
307 xub_StrLen swap
= nEnde
;
312 for (xub_StrLen i
= 0; i
<pHts
->Count();i
++)
314 xub_StrLen aPos
= *(*pHts
)[i
]->GetStart();
315 const SwTxtAttr
* pTxtAttr
= (*pHts
)[i
];
316 if ( (pTxtAttr
->Which()==RES_TXTATR_FIELD
) &&
317 (pTxtAttr
->GetFld().GetFld()->Which()==RES_POSTITFLD
))
319 if ( (aPos
>= nStart
) && (aPos
<= nEnde
) )
331 xub_StrLen swap
= nEnde
;
338 xub_StrLen aStart
= 0;
339 // do we need to finish a note?
340 if (POSTITMGR
->GetActivePostIt())
351 //search inside and finsih and put focus back into the doc
352 if (POSTITMGR
->FinishSearchReplace(rSearchOpt
,bSrchForward
))
360 POSTITMGR
->SetActivePostIt(0);
366 // now we have to split
367 xub_StrLen nStartInside
= 0;
368 xub_StrLen nEndeInside
= 0;
369 sal_Int16 aLoop
= bSrchForward
? aStart
: aNumberPostits
;
371 while ( (aLoop
>=0) && (aLoop
<=aNumberPostits
))
375 nStartInside
= aLoop
==0 ? nStart
: *(*pHts
)[GetPostIt(aLoop
+aIgnore
-1,pHts
)]->GetStart()+1;
376 nEndeInside
= aLoop
==aNumberPostits
? nEnde
: *(*pHts
)[GetPostIt(aLoop
+aIgnore
,pHts
)]->GetStart();
377 nTxtLen
= nEndeInside
-nStartInside
;
381 nStartInside
= aLoop
==aNumberPostits
? nStart
: *(*pHts
)[GetPostIt(aLoop
+aIgnore
,pHts
)]->GetStart();
382 nEndeInside
= aLoop
==0 ? nEnde
: *(*pHts
)[GetPostIt(aLoop
+aIgnore
-1,pHts
)]->GetStart()+1;
383 nTxtLen
= nStartInside
-nEndeInside
;
385 // search inside the text between a note
386 bFound
= DoSearch(rSearchOpt
,rSTxt
,fnMove
,bSrchForward
,bRegSearch
,bChkEmptyPara
,bChkParaEnd
,
387 nStartInside
,nEndeInside
,nTxtLen
, pNode
,pPam
);
392 // we should now be right in front of a note, search inside
393 if ( (bSrchForward
&& (GetPostIt(aLoop
+ aIgnore
,pHts
) < pHts
->Count()) ) || ( !bSrchForward
&& (aLoop
!=0) ))
395 const SwTxtAttr
* pTxtAttr
= bSrchForward
? (*pHts
)[GetPostIt(aLoop
+aIgnore
,pHts
)] : (*pHts
)[GetPostIt(aLoop
+aIgnore
-1,pHts
)];
396 if ( POSTITMGR
->SearchReplace(((SwTxtFld
*)pTxtAttr
)->GetFld(),rSearchOpt
,bSrchForward
) )
403 aLoop
= bSrchForward
? aLoop
+1 : aLoop
-1;
408 // if there is no SwPostItField inside or searching inside notes is disabled, we search the whole length just like before
409 bFound
= DoSearch(rSearchOpt
,rSTxt
,fnMove
,bSrchForward
,bRegSearch
,bChkEmptyPara
,bChkParaEnd
,
410 nStart
,nEnde
,nTxtLen
, pNode
,pPam
);
420 bool SwPaM::DoSearch( const SearchOptions
& rSearchOpt
, utl::TextSearch
& rSTxt
,
422 BOOL bSrchForward
, BOOL bRegSearch
, BOOL bChkEmptyPara
, BOOL bChkParaEnd
,
423 xub_StrLen
&nStart
, xub_StrLen
&nEnde
, xub_StrLen nTxtLen
,SwNode
* pNode
, SwPaM
* pPam
)
426 SwNodeIndex
& rNdIdx
= pPam
->GetPoint()->nNode
;
427 const SwNode
* pSttNd
= &rNdIdx
.GetNode();
430 LanguageType eLastLang
= 0;
431 // if the search string contains a soft hypen, we don't strip them from the text:
432 bool bRemoveSoftHyphens
= true;
435 const rtl::OUString
a00AD( rtl::OUString::createFromAscii( "\\x00AD" ) );
436 if ( -1 != rSearchOpt
.searchString
.indexOf( a00AD
) )
437 bRemoveSoftHyphens
= false;
441 if ( 1 == rSearchOpt
.searchString
.getLength() &&
442 CHAR_SOFTHYPHEN
== rSearchOpt
.searchString
.toChar() )
443 bRemoveSoftHyphens
= false;
447 lcl_CleanStr( *(SwTxtNode
*)pNode
, nStart
, nEnde
,
448 aFltArr
, sCleanStr
, bRemoveSoftHyphens
);
450 lcl_CleanStr( *(SwTxtNode
*)pNode
, nEnde
, nStart
,
451 aFltArr
, sCleanStr
, bRemoveSoftHyphens
);
453 SwScriptIterator
* pScriptIter
= 0;
454 USHORT nSearchScript
= 0;
455 USHORT nCurrScript
= 0;
457 if ( SearchAlgorithms_APPROXIMATE
== rSearchOpt
.algorithmType
&&
458 pBreakIt
->GetBreakIter().is() )
460 pScriptIter
= new SwScriptIterator( sCleanStr
, nStart
, bSrchForward
);
461 nSearchScript
= pBreakIt
->GetRealScriptOfText( rSearchOpt
.searchString
, 0 );
464 xub_StrLen nStringEnd
= nEnde
;
465 while ( (bSrchForward
&& nStart
< nStringEnd
) ||
466 (! bSrchForward
&& nStart
> nStringEnd
) )
468 // SearchAlgorithms_APPROXIMATE works on a per word base
469 // so we have to provide the text searcher with the correct
470 // locale, because it uses the breakiterator
473 nEnde
= pScriptIter
->GetScriptChgPos();
474 nCurrScript
= pScriptIter
->GetCurrScript();
475 if ( nSearchScript
== nCurrScript
)
477 const LanguageType eCurrLang
=
478 ((SwTxtNode
*)pNode
)->GetLang( bSrchForward
?
482 if ( eCurrLang
!= eLastLang
)
484 const lang::Locale
aLocale(
485 pBreakIt
->GetLocale( eCurrLang
) );
486 rSTxt
.SetLocale( rSearchOpt
, aLocale
);
487 eLastLang
= eCurrLang
;
493 if( nSearchScript
== nCurrScript
&&
494 (rSTxt
.*fnMove
->fnSearch
)( sCleanStr
, &nStart
, &nEnde
, 0 ))
496 // setze den Bereich richtig
497 *GetPoint() = *pPam
->GetPoint();
500 // Start und Ende wieder korrigieren !!
501 if( aFltArr
.Count() )
504 // bei Rueckwaertssuche die Positionen temp. vertauschen
505 if( !bSrchForward
) { n
= nStart
; nStart
= nEnde
; nEnde
= n
; }
507 for( n
= 0, nNew
= nStart
;
508 n
< aFltArr
.Count() && aFltArr
[ n
] <= nStart
;
512 for( n
= 0, nNew
= nEnde
;
513 n
< aFltArr
.Count() && aFltArr
[ n
] < nEnde
;
518 // bei Rueckwaertssuche die Positionen temp. vertauschen
519 if( !bSrchForward
) { n
= nStart
; nStart
= nEnde
; nEnde
= n
; }
521 GetMark()->nContent
= nStart
; // Startposition setzen
522 GetPoint()->nContent
= nEnde
;
524 if( !bSrchForward
) // rueckwaerts Suche?
525 Exchange(); // Point und Mark tauschen
531 } // end of script while
537 else if( ( bChkEmptyPara
&& !nStart
&& !nTxtLen
) || bChkParaEnd
)
539 *GetPoint() = *pPam
->GetPoint();
540 GetPoint()->nContent
= bChkParaEnd
? nTxtLen
: 0;
542 if( (bSrchForward
|| pSttNd
!= &rNdIdx
.GetNode()) &&
543 Move( fnMoveForward
, fnGoCntnt
) &&
544 (!bSrchForward
|| pSttNd
!= &GetPoint()->nNode
.GetNode()) &&
545 1 == Abs( (int)( GetPoint()->nNode
.GetIndex() -
546 GetMark()->nNode
.GetIndex()) ) )
548 if( !bSrchForward
) // rueckwaerts Suche?
549 Exchange(); // Point und Mark tauschen
558 // Parameter fuers Suchen und Ersetzen von Text
559 struct SwFindParaText
: public SwFindParas
561 const SearchOptions
& rSearchOpt
;
563 utl::TextSearch aSTxt
;
567 SwFindParaText( const SearchOptions
& rOpt
, BOOL bSearchNotes
, int bRepl
, SwCursor
& rCrsr
)
568 : rSearchOpt( rOpt
), rCursor( rCrsr
), aSTxt( rOpt
), bReplace( 0 != bRepl
), bSearchInNotes( bSearchNotes
)
570 virtual int Find( SwPaM
* , SwMoveFn
, const SwPaM
*, BOOL bInReadOnly
);
571 virtual int IsReplaceMode() const;
572 virtual ~SwFindParaText();
575 SwFindParaText::~SwFindParaText()
579 int SwFindParaText::Find( SwPaM
* pCrsr
, SwMoveFn fnMove
,
580 const SwPaM
* pRegion
, BOOL bInReadOnly
)
582 if( bInReadOnly
&& bReplace
)
585 BOOL bFnd
= (BOOL
)pCrsr
->Find( rSearchOpt
, bSearchInNotes
, aSTxt
, fnMove
, pRegion
, bInReadOnly
);
587 /* #i80135# if we found something in a note, Mark and Point is the same
588 if( bFnd && *pCrsr->GetMark() == *pCrsr->GetPoint() )
589 return FIND_NOT_FOUND;
592 if( bFnd
&& bReplace
) // String ersetzen ??
594 // Replace-Methode vom SwDoc benutzen
595 const bool bRegExp(SearchAlgorithms_REGEXP
== rSearchOpt
.algorithmType
);
596 SwIndex
& rSttCntIdx
= pCrsr
->Start()->nContent
;
597 xub_StrLen nSttCnt
= rSttCntIdx
.GetIndex();
598 // damit die Region auch verschoben wird, in den Shell-Cursr-Ring
603 pPrev
= pRegion
->GetPrev();
604 ((Ring
*)pRegion
)->MoveRingTo( &rCursor
);
607 ::std::auto_ptr
<String
> pRepl( (bRegExp
)
608 ? ReplaceBackReferences( rSearchOpt
, pCrsr
) : 0 );
609 rCursor
.GetDoc()->ReplaceRange( *pCrsr
,
610 (pRepl
.get()) ? *pRepl
: String(rSearchOpt
.replaceString
),
612 rCursor
.SaveTblBoxCntnt( pCrsr
->GetPoint() );
616 // und die Region wieder herausnehmen:
617 Ring
*p
, *pNext
= (Ring
*)pRegion
;
620 pNext
= p
->GetNext();
621 p
->MoveTo( (Ring
*)pRegion
);
622 } while( p
!= pPrev
);
624 pCrsr
->Start()->nContent
= nSttCnt
;
627 return bFnd
? FIND_FOUND
: FIND_NOT_FOUND
;
631 int SwFindParaText::IsReplaceMode() const
637 ULONG
SwCursor::Find( const SearchOptions
& rSearchOpt
, BOOL bSearchInNotes
,
638 SwDocPositions nStart
, SwDocPositions nEnde
,
640 FindRanges eFndRngs
, int bReplace
)
642 // OLE-Benachrichtigung abschalten !!
643 SwDoc
* pDoc
= GetDoc();
644 Link
aLnk( pDoc
->GetOle2Link() );
645 pDoc
->SetOle2Link( Link() );
647 BOOL bSttUndo
= pDoc
->DoesUndo() && bReplace
;
649 pDoc
->StartUndo( UNDO_REPLACE
, NULL
);
651 BOOL bSearchSel
= 0 != (rSearchOpt
.searchFlag
& SearchFlags::REG_NOT_BEGINOFLINE
);
653 eFndRngs
= (FindRanges
)(eFndRngs
| FND_IN_SEL
);
654 SwFindParaText
aSwFindParaText( rSearchOpt
, bSearchInNotes
, bReplace
, *this );
655 ULONG nRet
= FindAll( aSwFindParaText
, nStart
, nEnde
, eFndRngs
, bCancel
);
656 pDoc
->SetOle2Link( aLnk
);
657 if( nRet
&& bReplace
)
661 pDoc
->EndUndo( UNDO_REPLACE
, NULL
);
665 String
*ReplaceBackReferences( const SearchOptions
& rSearchOpt
, SwPaM
* pPam
)
668 if( pPam
&& pPam
->HasMark() &&
669 SearchAlgorithms_REGEXP
== rSearchOpt
.algorithmType
)
671 const SwCntntNode
* pTxtNode
= pPam
->GetCntntNode( TRUE
);
672 if( pTxtNode
&& pTxtNode
->IsTxtNode() && pTxtNode
== pPam
->GetCntntNode( FALSE
) )
674 utl::TextSearch
aSTxt( rSearchOpt
);
675 String
aStr( pPam
->GetTxt() );
676 String
aSearchStr( rSearchOpt
.searchString
);
677 String
aReplaceStr( rSearchOpt
.replaceString
);
678 aStr
.EraseAllChars( CH_TXTATR_BREAKWORD
);
679 aStr
.EraseAllChars( CH_TXTATR_INWORD
);
680 xub_StrLen nStart
= 0;
682 if( pPam
->Start()->nContent
> 0 )
684 aStr
.Insert( sX
, 0 );
687 xub_StrLen nEnd
= aStr
.Len();
688 bool bDeleteLastX
= false;
689 if( pPam
->End()->nContent
< (static_cast<const SwTxtNode
*>(pTxtNode
))->GetTxt().Len() )
694 SearchResult aResult
;
695 if( aSTxt
.SearchFrwrd( aStr
, &nStart
, &nEnd
, &aResult
) )
698 aStr
.Erase( aStr
.Len() - 1 );
699 aSTxt
.ReplaceBackReferences( aReplaceStr
, aStr
, aResult
);
700 pRet
= new String( aReplaceStr
);