1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <com/sun/star/util/SearchOptions.hpp>
22 #include <com/sun/star/util/SearchFlags.hpp>
24 #include <comphelper/string.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/window.hxx>
29 #include <txatritr.hxx>
36 #include <IDocumentUndoRedo.hxx>
40 #include <UndoInsert.hxx>
41 #include <breakit.hxx>
44 #include <PostItMgr.hxx>
47 using namespace ::com::sun::star
;
50 String
*ReplaceBackReferences( const SearchOptions
& rSearchOpt
, SwPaM
* pPam
);
52 static String
& lcl_CleanStr( const SwTxtNode
& rNd
, xub_StrLen nStart
, xub_StrLen
& rEnd
,
53 std::vector
<sal_uLong
> &rArr
, String
& rRet
,
54 bool bRemoveSoftHyphen
)
59 const SwpHints
*pHts
= rNd
.GetpSwpHints();
62 xub_StrLen nSoftHyphen
= nStart
;
63 xub_StrLen nHintStart
= STRING_LEN
;
65 bool bNewSoftHyphen
= true;
66 const xub_StrLen nEnd
= rEnd
;
67 std::vector
<sal_uInt16
> aReplaced
;
72 nHintStart
= pHts
&& n
< pHts
->Count() ?
73 *(*pHts
)[n
]->GetStart() :
77 nSoftHyphen
= bRemoveSoftHyphen
?
78 rNd
.GetTxt().Search( CHAR_SOFTHYPHEN
, nSoftHyphen
) :
82 bNewSoftHyphen
= false;
86 // Check if next stop is a hint.
87 if ( STRING_LEN
!= nHintStart
&& nHintStart
< nSoftHyphen
&& nHintStart
< nEnd
)
92 // Check if next stop is a soft hyphen.
93 else if ( STRING_LEN
!= nSoftHyphen
&& nSoftHyphen
< nHintStart
&& nSoftHyphen
< nEnd
)
96 bNewSoftHyphen
= true;
98 // If nSoftHyphen == nHintStart, the current hint *must* be a hint with an end.
99 else if ( STRING_LEN
!= nSoftHyphen
&& nSoftHyphen
== nHintStart
)
103 bNewSoftHyphen
= true;
108 const xub_StrLen nAkt
= nStt
- rArr
.size();
112 const SwTxtAttr
* pHt
= (*pHts
)[n
];
113 if ( pHt
->HasDummyChar() && (nStt
>= nStart
) )
115 switch( pHt
->Which() )
117 case RES_TXTATR_FLYCNT
:
119 case RES_TXTATR_FIELD
:
120 case RES_TXTATR_REFMARK
:
121 case RES_TXTATR_TOXMARK
:
122 case RES_TXTATR_META
:
123 case RES_TXTATR_METAFIELD
:
125 // (1998) they are desired as separators and
126 // belong not any longer to a word.
127 // they should also be ignored at a
128 // beginning/end of a sentence if blank. Those are
129 // simply removed if first. If at the end, we keep the
130 // replacement and remove afterwards all at a string's
131 // end (might be normal 0x7f).
132 bool bEmpty
= RES_TXTATR_FIELD
!= pHt
->Which() ||
133 !(static_cast<SwTxtFld
const*>(pHt
)
134 ->GetFld().GetFld()->ExpandField(true).Len());
135 if ( bEmpty
&& nStart
== nAkt
)
137 rArr
.push_back( nAkt
);
139 rRet
.Erase( nAkt
, 1 );
144 aReplaced
.push_back( nAkt
);
145 rRet
.SetChar( nAkt
, '\x7f' );
150 OSL_FAIL( "unknown case in lcl_CleanStr" );
157 if ( bNewSoftHyphen
)
159 rArr
.push_back( nAkt
);
161 rRet
.Erase( nAkt
, 1 );
167 for( sal_uInt16 i
= aReplaced
.size(); i
; )
169 const xub_StrLen nTmp
= aReplaced
[ --i
];
170 if( nTmp
== rRet
.Len() - 1 )
173 rArr
.push_back( nTmp
);
181 // skip all non SwPostIts inside the array
182 xub_StrLen
GetPostIt(xub_StrLen aCount
,const SwpHints
*pHts
)
184 xub_StrLen aIndex
= 0;
187 for (xub_StrLen i
= 0; i
<pHts
->Count();i
++)
190 const SwTxtAttr
* pTxtAttr
= (*pHts
)[i
];
191 if ( (pTxtAttr
->Which()==RES_TXTATR_FIELD
) &&
192 (pTxtAttr
->GetFld().GetFld()->Which()==RES_POSTITFLD
))
200 // throw away all following non postits
201 for (xub_StrLen i
= aIndex
; i
<pHts
->Count();i
++)
203 const SwTxtAttr
* pTxtAttr
= (*pHts
)[i
];
204 if ( (pTxtAttr
->Which()==RES_TXTATR_FIELD
) &&
205 (pTxtAttr
->GetFld().GetFld()->Which()==RES_POSTITFLD
))
213 sal_uInt8
SwPaM::Find( const SearchOptions
& rSearchOpt
, sal_Bool bSearchInNotes
, utl::TextSearch
& rSTxt
,
214 SwMoveFn fnMove
, const SwPaM
* pRegion
,
215 sal_Bool bInReadOnly
)
217 if( rSearchOpt
.searchString
.isEmpty() )
220 SwPaM
* pPam
= MakeRegion( fnMove
, pRegion
);
221 sal_Bool bSrchForward
= fnMove
== fnMoveForward
;
222 SwNodeIndex
& rNdIdx
= pPam
->GetPoint()->nNode
;
223 SwIndex
& rCntntIdx
= pPam
->GetPoint()->nContent
;
225 // If a beginning/end, from out of node; stop if empty node
227 ? ( rCntntIdx
.GetIndex() == pPam
->GetCntntNode()->Len() &&
228 rCntntIdx
.GetIndex() )
229 : !rCntntIdx
.GetIndex() && pPam
->GetCntntNode()->Len() )
231 if( !(*fnMove
->fnNds
)( &rNdIdx
, sal_False
))
236 SwCntntNode
*pNd
= rNdIdx
.GetNode().GetCntntNode();
237 xub_StrLen nTmpPos
= bSrchForward
? 0 : pNd
->Len();
238 rCntntIdx
.Assign( pNd
, nTmpPos
);
241 // If bFound is true then the string was found and is between nStart and nEnd
242 sal_Bool bFound
= sal_False
;
243 // start position in text or initial position
244 sal_Bool bFirst
= sal_True
;
247 xub_StrLen nStart
, nEnd
, nTxtLen
;
249 sal_Bool bRegSearch
= SearchAlgorithms_REGEXP
== rSearchOpt
.algorithmType
;
250 sal_Bool bChkEmptyPara
= bRegSearch
&& 2 == rSearchOpt
.searchString
.getLength() &&
251 ( !rSearchOpt
.searchString
.compareToAscii( "^$" ) ||
252 !rSearchOpt
.searchString
.compareToAscii( "$^" ) );
253 sal_Bool bChkParaEnd
= bRegSearch
&& 1 == rSearchOpt
.searchString
.getLength() &&
254 !rSearchOpt
.searchString
.compareToAscii( "$" );
256 // LanguageType eLastLang = 0;
257 while( 0 != ( pNode
= ::GetNode( *pPam
, bFirst
, fnMove
, bInReadOnly
) ))
259 if( pNode
->IsTxtNode() )
261 nTxtLen
= ((SwTxtNode
*)pNode
)->GetTxt().Len();
262 if( rNdIdx
== pPam
->GetMark()->nNode
)
263 nEnd
= pPam
->GetMark()->nContent
.GetIndex();
265 nEnd
= bSrchForward
? nTxtLen
: 0;
266 nStart
= rCntntIdx
.GetIndex();
269 // if there are SwPostItFields inside our current node text, we
270 // split the text into seperate pieces and search for text inside
271 // the pieces as well as inside the fields
272 const SwpHints
*pHts
= ((SwTxtNode
*)pNode
)->GetpSwpHints();
274 // count PostItFields by looping over all fields
275 xub_StrLen aNumberPostits
= 0;
276 xub_StrLen aIgnore
= 0;
277 if (pHts
&& bSearchInNotes
)
281 xub_StrLen swap
= nEnd
;
286 for (xub_StrLen i
= 0; i
<pHts
->Count();i
++)
288 xub_StrLen aPos
= *(*pHts
)[i
]->GetStart();
289 const SwTxtAttr
* pTxtAttr
= (*pHts
)[i
];
290 if ( (pTxtAttr
->Which()==RES_TXTATR_FIELD
) &&
291 (pTxtAttr
->GetFld().GetFld()->Which()==RES_POSTITFLD
))
293 if ( (aPos
>= nStart
) && (aPos
<= nEnd
) )
305 xub_StrLen swap
= nEnd
;
312 SwDocShell
*const pDocShell
= pNode
->GetDoc()->GetDocShell();
313 ViewShell
*const pWrtShell
= (pDocShell
) ? (ViewShell
*)(pDocShell
->GetWrtShell()) : 0;
314 SwPostItMgr
*const pPostItMgr
= (pWrtShell
) ? pWrtShell
->GetPostItMgr() : 0;
316 xub_StrLen aStart
= 0;
317 // do we need to finish a note?
318 if (pPostItMgr
&& pPostItMgr
->HasActiveSidebarWin())
329 //search inside and finsih and put focus back into the doc
330 if (pPostItMgr
->FinishSearchReplace(rSearchOpt
,bSrchForward
))
338 pPostItMgr
->SetActiveSidebarWin(0);
344 // now we have to split
345 xub_StrLen nStartInside
= 0;
346 xub_StrLen nEndInside
= 0;
347 sal_Int16 aLoop
= bSrchForward
? aStart
: aNumberPostits
;
349 while ( (aLoop
>=0) && (aLoop
<=aNumberPostits
))
353 nStartInside
= aLoop
==0 ? nStart
: *(*pHts
)[GetPostIt(aLoop
+aIgnore
-1,pHts
)]->GetStart()+1;
354 nEndInside
= aLoop
==aNumberPostits
? nEnd
: *(*pHts
)[GetPostIt(aLoop
+aIgnore
,pHts
)]->GetStart();
355 nTxtLen
= nEndInside
- nStartInside
;
359 nStartInside
= aLoop
==aNumberPostits
? nStart
: *(*pHts
)[GetPostIt(aLoop
+aIgnore
,pHts
)]->GetStart();
360 nEndInside
= aLoop
==0 ? nEnd
: *(*pHts
)[GetPostIt(aLoop
+aIgnore
-1,pHts
)]->GetStart()+1;
361 nTxtLen
= nStartInside
- nEndInside
;
363 // search inside the text between a note
364 bFound
= DoSearch( rSearchOpt
, rSTxt
, fnMove
, bSrchForward
,
365 bRegSearch
, bChkEmptyPara
, bChkParaEnd
,
366 nStartInside
, nEndInside
, nTxtLen
, pNode
,
372 // we should now be right in front of a note, search inside
373 if ( (bSrchForward
&& (GetPostIt(aLoop
+ aIgnore
,pHts
) < pHts
->Count()) ) || ( !bSrchForward
&& (aLoop
!=0) ))
375 const SwTxtAttr
* pTxtAttr
= bSrchForward
? (*pHts
)[GetPostIt(aLoop
+aIgnore
,pHts
)] : (*pHts
)[GetPostIt(aLoop
+aIgnore
-1,pHts
)];
376 if ( pPostItMgr
&& pPostItMgr
->SearchReplace(((SwTxtFld
*)pTxtAttr
)->GetFld(),rSearchOpt
,bSrchForward
) )
383 aLoop
= bSrchForward
? aLoop
+1 : aLoop
-1;
388 // if there is no SwPostItField inside or searching inside notes
389 // is disabled, we search the whole length just like before
390 bFound
= DoSearch( rSearchOpt
, rSTxt
, fnMove
, bSrchForward
,
391 bRegSearch
, bChkEmptyPara
, bChkParaEnd
,
392 nStart
, nEnd
, nTxtLen
, pNode
, pPam
);
402 bool SwPaM::DoSearch( const SearchOptions
& rSearchOpt
, utl::TextSearch
& rSTxt
,
403 SwMoveFn fnMove
, sal_Bool bSrchForward
, sal_Bool bRegSearch
,
404 sal_Bool bChkEmptyPara
, sal_Bool bChkParaEnd
,
405 xub_StrLen
&nStart
, xub_StrLen
&nEnd
, xub_StrLen nTxtLen
,
406 SwNode
* pNode
, SwPaM
* pPam
)
409 SwNodeIndex
& rNdIdx
= pPam
->GetPoint()->nNode
;
410 const SwNode
* pSttNd
= &rNdIdx
.GetNode();
412 std::vector
<sal_uLong
> aFltArr
;
413 LanguageType eLastLang
= 0;
414 // if the search string contains a soft hypen,
415 // we don't strip them from the text:
416 bool bRemoveSoftHyphens
= true;
419 const rtl::OUString
a00AD(RTL_CONSTASCII_USTRINGPARAM("\\x00AD"));
420 if ( -1 != rSearchOpt
.searchString
.indexOf( a00AD
) )
421 bRemoveSoftHyphens
= false;
425 if ( 1 == rSearchOpt
.searchString
.getLength() &&
426 CHAR_SOFTHYPHEN
== rSearchOpt
.searchString
.toChar() )
427 bRemoveSoftHyphens
= false;
431 lcl_CleanStr( *(SwTxtNode
*)pNode
, nStart
, nEnd
,
432 aFltArr
, sCleanStr
, bRemoveSoftHyphens
);
434 lcl_CleanStr( *(SwTxtNode
*)pNode
, nEnd
, nStart
,
435 aFltArr
, sCleanStr
, bRemoveSoftHyphens
);
437 SwScriptIterator
* pScriptIter
= 0;
438 sal_uInt16 nSearchScript
= 0;
439 sal_uInt16 nCurrScript
= 0;
441 if ( SearchAlgorithms_APPROXIMATE
== rSearchOpt
.algorithmType
&&
442 pBreakIt
->GetBreakIter().is() )
444 pScriptIter
= new SwScriptIterator( sCleanStr
, nStart
, bSrchForward
);
445 nSearchScript
= pBreakIt
->GetRealScriptOfText( rSearchOpt
.searchString
, 0 );
448 xub_StrLen nStringEnd
= nEnd
;
449 while ( (bSrchForward
&& nStart
< nStringEnd
) ||
450 (! bSrchForward
&& nStart
> nStringEnd
) )
452 // SearchAlgorithms_APPROXIMATE works on a per word base so we have to
453 // provide the text searcher with the correct locale, because it uses
454 // the break-iterator
457 nEnd
= pScriptIter
->GetScriptChgPos();
458 nCurrScript
= pScriptIter
->GetCurrScript();
459 if ( nSearchScript
== nCurrScript
)
461 const LanguageType eCurrLang
=
462 ((SwTxtNode
*)pNode
)->GetLang( bSrchForward
?
466 if ( eCurrLang
!= eLastLang
)
468 const lang::Locale
aLocale(
469 pBreakIt
->GetLocale( eCurrLang
) );
470 rSTxt
.SetLocale( rSearchOpt
, aLocale
);
471 eLastLang
= eCurrLang
;
477 if( nSearchScript
== nCurrScript
&&
478 (rSTxt
.*fnMove
->fnSearch
)( sCleanStr
, &nStart
, &nEnd
, 0 ))
480 // set section correctly
481 *GetPoint() = *pPam
->GetPoint();
484 // adjust start and end
485 if( !aFltArr
.empty() )
488 // if backward search, switch positions temporarily
489 if( !bSrchForward
) { n
= nStart
; nStart
= nEnd
; nEnd
= n
; }
491 for( n
= 0, nNew
= nStart
;
492 n
< aFltArr
.size() && aFltArr
[ n
] <= nStart
;
496 for( n
= 0, nNew
= nEnd
;
497 n
< aFltArr
.size() && aFltArr
[ n
] < nEnd
;
502 // if backward search, switch positions temporarily
503 if( !bSrchForward
) { n
= nStart
; nStart
= nEnd
; nEnd
= n
; }
505 GetMark()->nContent
= nStart
;
506 GetPoint()->nContent
= nEnd
;
508 // if backward search, switch point and mark
521 else if( ( bChkEmptyPara
&& !nStart
&& !nTxtLen
) || bChkParaEnd
)
523 *GetPoint() = *pPam
->GetPoint();
524 GetPoint()->nContent
= bChkParaEnd
? nTxtLen
: 0;
526 if( (bSrchForward
|| pSttNd
!= &rNdIdx
.GetNode()) &&
527 Move( fnMoveForward
, fnGoCntnt
) &&
528 (!bSrchForward
|| pSttNd
!= &GetPoint()->nNode
.GetNode()) &&
529 1 == Abs( (int)( GetPoint()->nNode
.GetIndex() -
530 GetMark()->nNode
.GetIndex()) ) )
532 // if backward search, switch point and mark
541 /// parameters for search and replace in text
542 struct SwFindParaText
: public SwFindParas
544 const SearchOptions
& rSearchOpt
;
546 utl::TextSearch aSTxt
;
548 sal_Bool bSearchInNotes
;
550 SwFindParaText( const SearchOptions
& rOpt
, sal_Bool bSearchNotes
, int bRepl
, SwCursor
& rCrsr
)
551 : rSearchOpt( rOpt
), rCursor( rCrsr
), aSTxt( rOpt
), bReplace( 0 != bRepl
), bSearchInNotes( bSearchNotes
)
553 virtual int Find( SwPaM
* , SwMoveFn
, const SwPaM
*, sal_Bool bInReadOnly
);
554 virtual int IsReplaceMode() const;
555 virtual ~SwFindParaText();
558 SwFindParaText::~SwFindParaText()
562 int SwFindParaText::Find( SwPaM
* pCrsr
, SwMoveFn fnMove
,
563 const SwPaM
* pRegion
, sal_Bool bInReadOnly
)
565 if( bInReadOnly
&& bReplace
)
566 bInReadOnly
= sal_False
;
568 sal_Bool bFnd
= (sal_Bool
)pCrsr
->Find( rSearchOpt
, bSearchInNotes
, aSTxt
, fnMove
, pRegion
, bInReadOnly
);
571 if( bFnd
&& bReplace
) // replace string
573 // use replace method in SwDoc
574 const bool bRegExp(SearchAlgorithms_REGEXP
== rSearchOpt
.algorithmType
);
575 SwIndex
& rSttCntIdx
= pCrsr
->Start()->nContent
;
576 xub_StrLen nSttCnt
= rSttCntIdx
.GetIndex();
577 // add to shell-cursor-ring so that the regions will be moved enventually
581 pPrev
= pRegion
->GetPrev();
582 ((Ring
*)pRegion
)->MoveRingTo( &rCursor
);
585 ::std::auto_ptr
<String
> pRepl( (bRegExp
)
586 ? ReplaceBackReferences( rSearchOpt
, pCrsr
) : 0 );
587 rCursor
.GetDoc()->ReplaceRange( *pCrsr
,
588 (pRepl
.get()) ? *pRepl
: String(rSearchOpt
.replaceString
),
590 rCursor
.SaveTblBoxCntnt( pCrsr
->GetPoint() );
594 // and remove region again
595 Ring
*p
, *pNext
= (Ring
*)pRegion
;
598 pNext
= p
->GetNext();
599 p
->MoveTo( (Ring
*)pRegion
);
600 } while( p
!= pPrev
);
602 pCrsr
->Start()->nContent
= nSttCnt
;
605 return bFnd
? FIND_FOUND
: FIND_NOT_FOUND
;
609 int SwFindParaText::IsReplaceMode() const
615 sal_uLong
SwCursor::Find( const SearchOptions
& rSearchOpt
, sal_Bool bSearchInNotes
,
616 SwDocPositions nStart
, SwDocPositions nEnd
,
617 sal_Bool
& bCancel
, FindRanges eFndRngs
, int bReplace
)
619 // switch off OLE-notifications
620 SwDoc
* pDoc
= GetDoc();
621 Link
aLnk( pDoc
->GetOle2Link() );
622 pDoc
->SetOle2Link( Link() );
624 bool const bStartUndo
= pDoc
->GetIDocumentUndoRedo().DoesUndo() && bReplace
;
627 pDoc
->GetIDocumentUndoRedo().StartUndo( UNDO_REPLACE
, NULL
);
630 bool bSearchSel
= 0 != (rSearchOpt
.searchFlag
& SearchFlags::REG_NOT_BEGINOFLINE
);
632 eFndRngs
= (FindRanges
)(eFndRngs
| FND_IN_SEL
);
633 SwFindParaText
aSwFindParaText( rSearchOpt
, bSearchInNotes
, bReplace
, *this );
634 sal_uLong nRet
= FindAll( aSwFindParaText
, nStart
, nEnd
, eFndRngs
, bCancel
);
635 pDoc
->SetOle2Link( aLnk
);
636 if( nRet
&& bReplace
)
641 SwRewriter
rewriter(MakeUndoReplaceRewriter(
642 nRet
, rSearchOpt
.searchString
, rSearchOpt
.replaceString
));
643 pDoc
->GetIDocumentUndoRedo().EndUndo( UNDO_REPLACE
, & rewriter
);
648 String
*ReplaceBackReferences( const SearchOptions
& rSearchOpt
, SwPaM
* pPam
)
651 if( pPam
&& pPam
->HasMark() &&
652 SearchAlgorithms_REGEXP
== rSearchOpt
.algorithmType
)
654 const SwCntntNode
* pTxtNode
= pPam
->GetCntntNode( sal_True
);
655 if( pTxtNode
&& pTxtNode
->IsTxtNode() && pTxtNode
== pPam
->GetCntntNode( sal_False
) )
657 utl::TextSearch
aSTxt( rSearchOpt
);
658 String
aStr( pPam
->GetTxt() );
659 String
aReplaceStr( rSearchOpt
.replaceString
);
660 aStr
= comphelper::string::remove(aStr
, CH_TXTATR_BREAKWORD
);
661 aStr
= comphelper::string::remove(aStr
, CH_TXTATR_INWORD
);
662 xub_StrLen nStart
= 0;
663 rtl::OUString
sX( 'x' );
664 if( pPam
->Start()->nContent
> 0 )
666 aStr
.Insert( sX
, 0 );
669 xub_StrLen nEnd
= aStr
.Len();
670 bool bDeleteLastX
= false;
671 if( pPam
->End()->nContent
< (static_cast<const SwTxtNode
*>(pTxtNode
))->GetTxt().Len() )
676 SearchResult aResult
;
677 if( aSTxt
.SearchFrwrd( aStr
, &nStart
, &nEnd
, &aResult
) )
680 aStr
.Erase( aStr
.Len() - 1 );
681 aSTxt
.ReplaceBackReferences( aReplaceStr
, aStr
, aResult
);
682 pRet
= new String( aReplaceStr
);
690 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */