xref: /trunk/main/sw/source/core/crsr/findattr.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sw.hxx"
30 
31 
32 #include <com/sun/star/lang/Locale.hpp>
33 #include <com/sun/star/util/SearchOptions.hpp>
34 #include <com/sun/star/util/SearchFlags.hpp>
35 #include <i18npool/mslangid.hxx>
36 #include <hintids.hxx>
37 #include <vcl/svapp.hxx>
38 #include <svl/itemiter.hxx>
39 #include <svl/whiter.hxx>
40 #include <editeng/brkitem.hxx>
41 #include <editeng/colritem.hxx>
42 #include <editeng/fontitem.hxx>
43 #include <fmtpdsc.hxx>
44 #include <txatbase.hxx>
45 #include <fchrfmt.hxx>
46 #include <charfmt.hxx>
47 #include <doc.hxx>
48 #include <IDocumentUndoRedo.hxx>
49 #include <swcrsr.hxx>
50 #include <editsh.hxx>
51 #include <ndtxt.hxx>
52 #include <pamtyp.hxx>
53 #include <swundo.hxx>
54 #include <crsskip.hxx>
55 
56 
57 using namespace ::com::sun::star;
58 using namespace ::com::sun::star::lang;
59 using namespace ::com::sun::star::util;
60 
61 SV_DECL_PTRARR_SORT( SwpFmts, SwFmt*, 0, 4 )
62 SV_IMPL_PTRARR_SORT( SwpFmts, SwFmt* )
63 
64 	// Sonderbehandlung fuer SvxFontItem, nur den Namen vergleichen:
65 int CmpAttr( const SfxPoolItem& rItem1, const SfxPoolItem& rItem2 )
66 {
67 	switch( rItem1.Which() )
68 	{
69 	case RES_CHRATR_FONT:
70 		return ((SvxFontItem&)rItem1).GetFamilyName() ==
71 				((SvxFontItem&)rItem2).GetFamilyName();
72 
73 	case RES_CHRATR_COLOR:
74 		return ((SvxColorItem&)rItem1).GetValue().IsRGBEqual(
75 								((SvxColorItem&)rItem2).GetValue() );
76 	case RES_PAGEDESC:
77 		return ((SwFmtPageDesc&)rItem1).GetNumOffset() ==
78 						((SwFmtPageDesc&)rItem2).GetNumOffset() &&
79 				((SwFmtPageDesc&)rItem1).GetPageDesc() ==
80 						((SwFmtPageDesc&)rItem2).GetPageDesc();
81 	}
82 	return rItem1 == rItem2;
83 }
84 
85 
86 const SwTxtAttr* GetFrwrdTxtHint( const SwpHints& rHtsArr, sal_uInt16& rPos,
87 									xub_StrLen nCntntPos )
88 {
89 	while( rPos < rHtsArr.Count() )
90 	{
91 		const SwTxtAttr *pTxtHt = rHtsArr.GetStart( rPos++ );
92 		// der Start vom Attribut muss innerhalb des Bereiches liegen !!
93 		if( *pTxtHt->GetStart() >= nCntntPos )
94 			return pTxtHt; 		// gueltiges TextAttribut
95 	}
96 	return 0;			 		// kein gueltiges TextAttribut
97 }
98 
99 
100 const SwTxtAttr* GetBkwrdTxtHint( const SwpHints& rHtsArr, sal_uInt16& rPos,
101 								  xub_StrLen nCntntPos )
102 {
103 	while( rPos > 0 )
104 	{
105 		//Hack mit cast fuer das Update
106 		const SwTxtAttr *pTxtHt = rHtsArr.GetStart( --rPos );
107 		// der Start vom Attribut muss innerhalb des Bereiches liegen !!
108 		if( *pTxtHt->GetStart() < nCntntPos )
109 			return pTxtHt; 		// gueltiges TextAttribut
110 	}
111 	return 0; 					// kein gueltiges TextAttribut
112 }
113 
114 
115 void lcl_SetAttrPam( SwPaM & rPam, xub_StrLen nStart, const xub_StrLen* pEnde,
116 						const sal_Bool bSaveMark )
117 {
118 	xub_StrLen nCntntPos;
119 	if( bSaveMark )
120 		nCntntPos = rPam.GetMark()->nContent.GetIndex();
121 	else
122 		nCntntPos = rPam.GetPoint()->nContent.GetIndex();
123 	sal_Bool bTstEnde = rPam.GetPoint()->nNode == rPam.GetMark()->nNode;
124 
125 	SwCntntNode* pCNd = rPam.GetCntntNode();
126 	rPam.GetPoint()->nContent.Assign( pCNd, nStart );
127 	rPam.SetMark(); 	// Point == GetMark
128 
129 	// Point zeigt auf das Ende vom SuchBereich oder Ende vom Attribut
130 	if( pEnde )
131 	{
132 		if( bTstEnde && *pEnde > nCntntPos )
133 			rPam.GetPoint()->nContent = nCntntPos;
134 		else
135 			rPam.GetPoint()->nContent = *pEnde;
136 	}
137 }
138 
139 //------------------ Suche nach einem Text Attribut -----------------------
140 
141 // diese Funktion sucht in einem TextNode nach dem vorgegebenen Attribut.
142 // Wird es gefunden, dann hat der SwPaM den Bereich der das Attribut
143 // umspannt, unter Beachtung des Suchbereiches
144 
145 
146 sal_Bool lcl_Search( const SwTxtNode& rTxtNd, SwPaM& rPam,
147 					const SfxPoolItem& rCmpItem,
148 					SwMoveFn fnMove, sal_Bool bValue )
149 {
150 	if ( !rTxtNd.HasHints() )
151 		return sal_False;
152 	const SwTxtAttr *pTxtHt = 0;
153 	sal_Bool bForward = fnMove == fnMoveForward;
154 	sal_uInt16 nPos = bForward ? 0 : rTxtNd.GetSwpHints().Count();
155 	xub_StrLen nCntntPos = rPam.GetPoint()->nContent.GetIndex();
156 
157 	while( 0 != ( pTxtHt=(*fnMove->fnGetHint)(rTxtNd.GetSwpHints(),nPos,nCntntPos)))
158 		if( pTxtHt->Which() == rCmpItem.Which() &&
159 			( !bValue || CmpAttr( pTxtHt->GetAttr(), rCmpItem )))
160 		{
161 			lcl_SetAttrPam( rPam, *pTxtHt->GetStart(), pTxtHt->GetEnd(), bForward );
162 			return sal_True;
163 		}
164 	return sal_False;
165 }
166 
167 
168 //------------------ Suche nach mehren Text Attributen -------------------
169 
170 struct _SwSrchChrAttr
171 {
172 	sal_uInt16 nWhich;
173 	xub_StrLen nStt, nEnd;
174 
175 	_SwSrchChrAttr( const SfxPoolItem& rItem,
176 					xub_StrLen nStart, xub_StrLen nAnyEnd )
177 		: nWhich( rItem.Which() ), nStt( nStart ), nEnd( nAnyEnd )
178 	{}
179 };
180 
181 class SwAttrCheckArr
182 {
183 	_SwSrchChrAttr *pFndArr, *pStackArr;
184 	xub_StrLen nNdStt, nNdEnd;
185 	sal_uInt16 nArrStart, nArrLen;
186 	sal_uInt16 nFound, nStackCnt;
187 	SfxItemSet aCmpSet;
188 	sal_Bool bNoColls;
189 	sal_Bool bForward;
190 
191 public:
192 	SwAttrCheckArr( const SfxItemSet& rSet, int bForward, int bNoCollections );
193 	~SwAttrCheckArr();
194 
195 	void SetNewSet( const SwTxtNode& rTxtNd, const SwPaM& rPam );
196 
197 	// wieviele Attribute ueberhaupt ??
198 	sal_uInt16 Count() const 	{ return aCmpSet.Count(); }
199 	int Found() const 		{ return nFound == aCmpSet.Count(); }
200 	int CheckStack();
201 
202 	xub_StrLen Start() const;
203 	xub_StrLen End() const;
204 
205 	xub_StrLen GetNdStt() const { return nNdStt; }
206 	xub_StrLen GetNdEnd() const { return nNdEnd; }
207 
208 	int SetAttrFwd( const SwTxtAttr& rAttr );
209 	int SetAttrBwd( const SwTxtAttr& rAttr );
210 };
211 
212 
213 
214 SwAttrCheckArr::SwAttrCheckArr( const SfxItemSet& rSet, int bFwd,
215 								int bNoCollections )
216 	: aCmpSet( *rSet.GetPool(), RES_CHRATR_BEGIN, RES_TXTATR_END-1 )
217 {
218 	aCmpSet.Put( rSet, sal_False );
219 	bNoColls = 0 != bNoCollections;
220 
221 	bForward = 0 != bFwd;
222 
223 	// Bestimmen den Bereich des Fnd/Stack-Arrays (Min/Max)
224 	SfxItemIter aIter( aCmpSet );
225 	nArrStart = aCmpSet.GetWhichByPos( aIter.GetFirstPos() );
226 	nArrLen = aCmpSet.GetWhichByPos( aIter.GetLastPos() ) - nArrStart+1;
227 
228     char* pFndChar  = new char[ nArrLen * sizeof(_SwSrchChrAttr) ];
229     char* pStackChar = new char[ nArrLen * sizeof(_SwSrchChrAttr) ];
230 
231     pFndArr = (_SwSrchChrAttr*)pFndChar;
232     pStackArr = (_SwSrchChrAttr*)pStackChar;
233 }
234 
235 SwAttrCheckArr::~SwAttrCheckArr()
236 {
237     delete[] (char*)pFndArr;
238     delete[] (char*)pStackArr;
239 }
240 
241 void SwAttrCheckArr::SetNewSet( const SwTxtNode& rTxtNd, const SwPaM& rPam )
242 {
243 	memset( pFndArr, 0, nArrLen * sizeof(_SwSrchChrAttr) );
244 	memset( pStackArr, 0, nArrLen * sizeof(_SwSrchChrAttr) );
245 	nFound = 0;
246 	nStackCnt = 0;
247 
248 	if( bForward )
249 	{
250 		nNdStt = rPam.GetPoint()->nContent.GetIndex();
251 		nNdEnd = rPam.GetPoint()->nNode == rPam.GetMark()->nNode
252 				? rPam.GetMark()->nContent.GetIndex()
253 				: rTxtNd.GetTxt().Len();
254 	}
255 	else
256 	{
257 		nNdEnd = rPam.GetPoint()->nContent.GetIndex();
258 		nNdStt = rPam.GetPoint()->nNode == rPam.GetMark()->nNode
259 				? rPam.GetMark()->nContent.GetIndex()
260 				: 0;
261 	}
262 
263     if( bNoColls && !rTxtNd.HasSwAttrSet() )
264 		return ;
265 
266 	const SfxItemSet& rSet = rTxtNd.GetSwAttrSet();
267 //	if( !rSet.Count() )
268 //		return;
269 
270 	SfxItemIter aIter( aCmpSet );
271 	const SfxPoolItem* pItem = aIter.GetCurItem();
272 	const SfxPoolItem* pFndItem;
273 	sal_uInt16 nWhich;
274 
275 	while( sal_True )
276 	{
277 		// nur testen, ob vorhanden ist ?
278 		if( IsInvalidItem( pItem ) )
279 		{
280 			nWhich = aCmpSet.GetWhichByPos( aIter.GetCurPos() );
281 			if( RES_TXTATR_END <= nWhich )
282 				break;				// Ende der TextAttribute
283 
284 			if( SFX_ITEM_SET == rSet.GetItemState( nWhich, !bNoColls, &pFndItem )
285 				&& !CmpAttr( *pFndItem, rSet.GetPool()->GetDefaultItem( nWhich ) ))
286 			{
287 				pFndArr[ nWhich - nArrStart ] =
288 					_SwSrchChrAttr( *pFndItem, nNdStt, nNdEnd );
289 				nFound++;
290 			}
291 		}
292 		else
293 		{
294 			if( RES_TXTATR_END <= (nWhich = pItem->Which() ))
295 				break;				// Ende der TextAttribute
296 
297 //JP 27.02.95: wenn nach defaults gesucht wird, dann muss man bis zum Pool
298 //				runter
299 //			if( SFX_ITEM_SET == rSet.GetItemState( nWhich, !bNoColls, &pFndItem )
300 //                && *pFndItem == *pItem )
301 			if( CmpAttr( rSet.Get( nWhich, !bNoColls ), *pItem ) )
302 			{
303 				pFndArr[ nWhich - nArrStart ] =
304 					_SwSrchChrAttr( *pItem, nNdStt, nNdEnd );
305 				nFound++;
306 			}
307 		}
308 
309 		if( aIter.IsAtEnd() )
310 			break;
311 		pItem = aIter.NextItem();
312 	}
313 }
314 
315 static bool
316 lcl_IsAttributeIgnorable(xub_StrLen const nNdStart, xub_StrLen const nNdEnd,
317         _SwSrchChrAttr const& rTmp)
318 {
319     // #i115528#: if there is a paragraph attribute, it has been added by the
320     // SwAttrCheckArr ctor, and nFound is 1.
321     // if the paragraph is entirely covered by hints that override the paragraph
322     // attribute, then this function must find an attribute to decrement nFound!
323     // so check for an empty search range, let attributes that start/end there
324     // cover it, and hope for the best...
325     return ((nNdEnd == nNdStart)
326             ? ((rTmp.nEnd <  nNdStart) || (nNdEnd <  rTmp.nStt))
327             : ((rTmp.nEnd <= nNdStart) || (nNdEnd <= rTmp.nStt)));
328 }
329 
330 int SwAttrCheckArr::SetAttrFwd( const SwTxtAttr& rAttr )
331 {
332 	_SwSrchChrAttr aTmp( rAttr.GetAttr(), *rAttr.GetStart(), *rAttr.GetAnyEnd() );
333 
334     // ignore all attributes not in search range
335     if (lcl_IsAttributeIgnorable(nNdStt, nNdEnd, aTmp))
336     {
337         return Found();
338     }
339 
340 	const SfxPoolItem* pItem;
341 // --------------------------------------------------------------
342 // Hier wird jetzt ausdruecklich auch in Zeichenvorlagen gesucht
343 // --------------------------------------------------------------
344 	sal_uInt16 nWhch = rAttr.Which();
345 	SfxWhichIter* pIter = NULL;
346 	const SfxPoolItem* pTmpItem = NULL;
347     const SfxItemSet* pSet = NULL;
348 	if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
349 	{
350 		if( bNoColls && RES_TXTATR_CHARFMT == nWhch )
351 			return Found();
352         pTmpItem = NULL;
353         pSet = CharFmt::GetItemSet( rAttr.GetAttr() );
354         if ( pSet )
355         {
356 			pIter = new SfxWhichIter( *pSet );
357 			nWhch = pIter->FirstWhich();
358 			while( nWhch &&
359 				SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
360 				nWhch = pIter->NextWhich();
361 			if( !nWhch )
362 				pTmpItem = NULL;
363         }
364 	}
365 	else
366 		pTmpItem = &rAttr.GetAttr();
367 	while( pTmpItem )
368 	{
369 		SfxItemState eState = aCmpSet.GetItemState( nWhch, sal_False, &pItem );
370 		if( SFX_ITEM_DONTCARE == eState || SFX_ITEM_SET == eState )
371 		{
372 			sal_uInt16 n;
373 			_SwSrchChrAttr* pCmp;
374 
375 			// loesche erstmal alle, die bis zu der Start Position schon wieder
376 			// ungueltig sind:
377 
378 			_SwSrchChrAttr* pArrPtr;
379 			if( nFound )
380 				for( pArrPtr = pFndArr, n = 0; n < nArrLen;
381 					++n, ++pArrPtr )
382 					if( pArrPtr->nWhich && pArrPtr->nEnd <= aTmp.nStt )
383 					{
384 						pArrPtr->nWhich = 0;		// geloescht
385 						nFound--;
386 					}
387 
388 			// loesche erstmal alle, die bis zu der Start Position schon wieder
389 			// ungueltig sind. Und verschiebe alle die "offen" sind, heisst ueber
390 			// die Start Position ragen, vom Stack in den FndSet
391 
392 			if( nStackCnt )
393 				for( pArrPtr = pStackArr, n=0; n < nArrLen; ++n, ++pArrPtr )
394 				{
395 					if( !pArrPtr->nWhich )
396 						continue;
397 
398 					if( pArrPtr->nEnd <= aTmp.nStt )
399 					{
400 						pArrPtr->nWhich = 0;		// geloescht
401 						if( !--nStackCnt )
402 							break;
403 					}
404 					else if( pArrPtr->nStt <= aTmp.nStt )
405 					{
406 						if( ( pCmp = &pFndArr[ n ])->nWhich )
407 						{
408 							if( pCmp->nEnd < pArrPtr->nEnd )		// erweitern
409 								pCmp->nEnd = pArrPtr->nEnd;
410 						}
411 						else
412 						{
413 							*pCmp = *pArrPtr;
414 							nFound++;
415 						}
416 						pArrPtr->nWhich = 0;
417 						if( !--nStackCnt )
418 							break;
419 					}
420 				}
421 
422 			sal_Bool bContinue = sal_False;
423 
424 			if( SFX_ITEM_DONTCARE == eState  )
425 			{
426 				// wird Attribut gueltig ?
427 				if( !CmpAttr( aCmpSet.GetPool()->GetDefaultItem( nWhch ),
428 					*pTmpItem ))
429 				{
430 					// suche das Attribut und erweiter es gegebenenfalls
431 					if( !( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
432 					{
433 						*pCmp = aTmp;				// nicht gefunden, eintragen
434 						nFound++;
435 					}
436 					else if( pCmp->nEnd < aTmp.nEnd )		// erweitern ?
437 						pCmp->nEnd = aTmp.nEnd;
438 
439 					bContinue = sal_True;
440 				}
441 			}
442 			// wird Attribut gueltig ?
443 			else if(  CmpAttr( *pItem, *pTmpItem ) )
444 			{
445 				pFndArr[ nWhch - nArrStart ] = aTmp;
446 				++nFound;
447 				bContinue = sal_True;
448 			}
449 
450 			// tja, dann muss es auf den Stack
451 			if( !bContinue && ( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
452 			{
453 				// vorhanden, auf den Stack. Aber nur wenn es noch grosser ist
454 				if( pCmp->nEnd > aTmp.nEnd )
455 				{
456 					ASSERT( !pStackArr[ nWhch - nArrStart ].nWhich,
457 									"Stack-Platz ist noch belegt" );
458 
459 		// ---------
460 		// JP 22.08.96: nur Ende manipulieren reicht nicht. Bug 30547
461 		//			pCmp->nStt = aTmp.nEnd;
462 					if( aTmp.nStt <= pCmp->nStt )
463 						pCmp->nStt = aTmp.nEnd;
464 					else
465 						pCmp->nEnd = aTmp.nStt;
466 		// ---------
467 
468 					pStackArr[ nWhch - nArrStart ] = *pCmp;
469 					nStackCnt++;
470 				}
471 				pCmp->nWhich = 0;
472 				nFound--;
473 			}
474 		}
475 		if( pIter )
476 		{
477 			nWhch = pIter->NextWhich();
478 			while( nWhch &&
479 				SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
480 				nWhch = pIter->NextWhich();
481 			if( !nWhch )
482 				break;
483 		}
484 		else
485 			break;
486 	}
487 	return Found();
488 }
489 
490 
491 int SwAttrCheckArr::SetAttrBwd( const SwTxtAttr& rAttr )
492 {
493 	_SwSrchChrAttr aTmp( rAttr.GetAttr(), *rAttr.GetStart(), *rAttr.GetAnyEnd() );
494 
495     // ignore all attributes not in search range
496     if (lcl_IsAttributeIgnorable(nNdStt, nNdEnd, aTmp))
497     {
498         return Found();
499     }
500 
501 	const SfxPoolItem* pItem;
502 // --------------------------------------------------------------
503 // Hier wird jetzt ausdruecklich auch in Zeichenvorlagen gesucht
504 // --------------------------------------------------------------
505 	sal_uInt16 nWhch = rAttr.Which();
506 	SfxWhichIter* pIter = NULL;
507     const SfxPoolItem* pTmpItem = NULL;
508     const SfxItemSet* pSet = NULL;
509     if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
510 	{
511 		if( bNoColls && RES_TXTATR_CHARFMT == nWhch )
512 			return Found();
513 
514         pSet = CharFmt::GetItemSet( rAttr.GetAttr() );
515         if ( pSet )
516         {
517 			pIter = new SfxWhichIter( *pSet );
518 			nWhch = pIter->FirstWhich();
519 			while( nWhch &&
520 				SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
521 				nWhch = pIter->NextWhich();
522 			if( !nWhch )
523 				pTmpItem = NULL;
524         }
525 	}
526 	else
527 		pTmpItem = &rAttr.GetAttr();
528 	while( pTmpItem )
529 	{
530 		SfxItemState eState = aCmpSet.GetItemState( nWhch, sal_False, &pItem );
531 		if( SFX_ITEM_DONTCARE == eState || SFX_ITEM_SET == eState )
532 		{
533 			sal_uInt16 n;
534 			_SwSrchChrAttr* pCmp;
535 
536 			// loesche erstmal alle, die bis zu der Start Position schon wieder
537 			// ungueltig sind:
538 
539 			_SwSrchChrAttr* pArrPtr;
540 			if( nFound )
541 				for( pArrPtr = pFndArr, n = 0; n < nArrLen; ++n, ++pArrPtr )
542 					if( pArrPtr->nWhich && pArrPtr->nStt >= aTmp.nEnd )
543 					{
544 						pArrPtr->nWhich = 0;		// geloescht
545 						nFound--;
546 					}
547 
548 			// loesche erstmal alle, die bis zu der Start Position schon wieder
549 			// ungueltig sind. Und verschiebe alle die "offen" sind, heisst ueber
550 			// die Start Position ragen, vom Stack in den FndSet
551 
552 			if( nStackCnt )
553 				for( pArrPtr = pStackArr, n = 0; n < nArrLen; ++n, ++pArrPtr )
554 				{
555 					if( !pArrPtr->nWhich )
556 						continue;
557 
558 					if( pArrPtr->nStt >= aTmp.nEnd )
559 					{
560 						pArrPtr->nWhich = 0;		// geloescht
561 						if( !--nStackCnt )
562 							break;
563 					}
564 					else if( pArrPtr->nEnd >= aTmp.nEnd )
565 					{
566 						if( ( pCmp = &pFndArr[ n ])->nWhich )
567 						{
568 							if( pCmp->nStt > pArrPtr->nStt )		// erweitern
569 								pCmp->nStt = pArrPtr->nStt;
570 						}
571 						else
572 						{
573 							*pCmp = *pArrPtr;
574 							nFound++;
575 					}
576 					pArrPtr->nWhich = 0;
577 					if( !--nStackCnt )
578 						break;
579 				}
580 			}
581 
582 			sal_Bool bContinue = sal_False;
583 			if( SFX_ITEM_DONTCARE == eState  )
584 			{
585 				// wird Attribut gueltig ?
586 				if( !CmpAttr( aCmpSet.GetPool()->GetDefaultItem( nWhch ),
587 					*pTmpItem ) )
588 				{
589 					// suche das Attribut und erweiter es gegebenenfalls
590 					if( !( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
591 					{
592 						*pCmp = aTmp;				// nicht gefunden, eintragen
593 						nFound++;
594 					}
595 					else if( pCmp->nStt > aTmp.nStt )		// erweitern ?
596 						pCmp->nStt = aTmp.nStt;
597 
598 					bContinue = sal_True;
599 				}
600 			}
601 			// wird Attribut gueltig ?
602 			else if( CmpAttr( *pItem, *pTmpItem ))
603 			{
604 				pFndArr[ nWhch - nArrStart ] = aTmp;
605 				++nFound;
606 				bContinue = sal_True;
607 			}
608 
609 			// tja, dann muss es auf den Stack
610 			if( !bContinue && ( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
611 			{
612 				// vorhanden, auf den Stack. Aber nur wenn es noch grosser ist
613 				if( pCmp->nStt < aTmp.nStt )
614 				{
615 					ASSERT( !pStackArr[ nWhch - nArrStart ].nWhich,
616 							"Stack-Platz ist noch belegt" );
617 
618 // ---------
619 // JP 22.08.96: nur Ende manipulieren reicht nicht. Bug 30547
620 //			pCmp->nEnd = aTmp.nStt;
621 					if( aTmp.nEnd <= pCmp->nEnd )
622 						pCmp->nEnd = aTmp.nStt;
623 					else
624 						pCmp->nStt = aTmp.nEnd;
625 // ---------
626 
627 					pStackArr[ nWhch - nArrStart ] = *pCmp;
628 					nStackCnt++;
629 				}
630 				pCmp->nWhich = 0;
631 				nFound--;
632 			}
633 		}
634 		if( pIter )
635 		{
636 			nWhch = pIter->NextWhich();
637 			while( nWhch &&
638 				SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
639 				nWhch = pIter->NextWhich();
640 			if( !nWhch )
641 				break;
642 		}
643 		else
644 			break;
645 	}
646 	return Found();
647 }
648 
649 
650 xub_StrLen SwAttrCheckArr::Start() const
651 {
652 	xub_StrLen nStart = nNdStt;
653 	_SwSrchChrAttr* pArrPtr = pFndArr;
654 	for( sal_uInt16 n = 0; n < nArrLen; ++n, ++pArrPtr )
655 		if( pArrPtr->nWhich && pArrPtr->nStt > nStart )
656 			nStart = pArrPtr->nStt;
657 
658 	return nStart;
659 }
660 
661 
662 xub_StrLen SwAttrCheckArr::End() const
663 {
664 	_SwSrchChrAttr* pArrPtr = pFndArr;
665 	xub_StrLen nEnd = nNdEnd;
666 	for( sal_uInt16 n = 0; n < nArrLen; ++n, ++pArrPtr )
667 		if( pArrPtr->nWhich && pArrPtr->nEnd < nEnd )
668 			nEnd = pArrPtr->nEnd;
669 
670 	return nEnd;
671 }
672 
673 
674 int SwAttrCheckArr::CheckStack()
675 {
676 	if( !nStackCnt )
677 		return sal_False;
678 
679 	sal_uInt16 n;
680 	xub_StrLen nSttPos = Start(), nEndPos = End();
681 	_SwSrchChrAttr* pArrPtr;
682 	for( pArrPtr = pStackArr, n = 0; n < nArrLen; ++n, ++pArrPtr )
683 	{
684 		if( !pArrPtr->nWhich )
685 			continue;
686 
687 		if( bForward ? pArrPtr->nEnd <= nSttPos : pArrPtr->nStt >= nEndPos )
688 		{
689 			pArrPtr->nWhich = 0;		// geloescht
690 			if( !--nStackCnt )
691 				return nFound == aCmpSet.Count();
692 		}
693 		else if( bForward ? pArrPtr->nStt < nEndPos : pArrPtr->nEnd > nSttPos )
694 		{
695 			// alle die "offen" sind, heisst ueber die Start Position ragen,
696 			// im FndSet setzen
697 			ASSERT( !pFndArr[ n ].nWhich, "Array-Platz ist noch belegt" );
698 			pFndArr[ n ] = *pArrPtr;
699 			pArrPtr->nWhich = 0;
700 			nFound++;
701 			if( !--nStackCnt )
702 				return nFound == aCmpSet.Count();
703 		}
704 	}
705 	return nFound == aCmpSet.Count();
706 }
707 
708 
709 
710 int lcl_SearchForward( const SwTxtNode& rTxtNd, SwAttrCheckArr& rCmpArr,
711 							SwPaM& rPam )
712 {
713 	xub_StrLen nEndPos, nSttPos;
714 	rCmpArr.SetNewSet( rTxtNd, rPam );
715 	if( !rTxtNd.HasHints() )
716 	{
717 		if( !rCmpArr.Found() )
718 			return sal_False;
719 		nEndPos = rCmpArr.GetNdEnd();
720 		lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_True );
721 		return sal_True;
722 	}
723 
724 	// dann gehe mal durch das nach "Start" sortierte Array
725 	const SwpHints& rHtArr = rTxtNd.GetSwpHints();
726 	const SwTxtAttr* pAttr;
727 	sal_uInt16 nPos = 0;
728 
729 	// sollte jetzt schon alles vorhanden sein, dann teste, mit welchem
730 	// das wieder beendet wird.
731 	if( rCmpArr.Found() )
732 	{
733 		for( ; nPos < rHtArr.Count(); ++nPos )
734 			if( !rCmpArr.SetAttrFwd( *( pAttr = rHtArr.GetStart( nPos )) ) )
735 			{
736 				if( rCmpArr.GetNdStt() < *pAttr->GetStart() )
737 				{
738 					// dann haben wir unser Ende:
739 					lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(),
740 								pAttr->GetStart(), sal_True );
741 					return sal_True;
742 				}
743 				// ansonsten muessen wir weiter suchen
744 				break;
745 			}
746 
747 		if( nPos == rHtArr.Count() && rCmpArr.Found() )
748 		{
749 			// dann haben wir unseren Bereich
750 			nEndPos = rCmpArr.GetNdEnd();
751 			lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_True );
752 			return sal_True;
753 		}
754 	}
755 
756 	for( ; nPos < rHtArr.Count(); ++nPos )
757 		if( rCmpArr.SetAttrFwd( *( pAttr = rHtArr.GetStart( nPos )) ) )
758 		{
759 			// sollten noch mehr auf der gleichen Position anfangen ??
760 			// auch die noch mit testen !!
761 			nSttPos = *pAttr->GetStart();
762 			while( ++nPos < rHtArr.Count() && nSttPos ==
763 					*( pAttr = rHtArr.GetStart( nPos ))->GetStart() &&
764 					rCmpArr.SetAttrFwd( *pAttr ) )
765 				;
766 			if( !rCmpArr.Found() )
767 				continue;
768 
769 			// dann haben wir den Bereich zusammen
770 			if( (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
771 				return sal_False;
772 			lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_True );
773 			return sal_True;
774 		}
775 
776 	if( !rCmpArr.CheckStack() ||
777 		(nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
778 		return sal_False;
779 	lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_True );
780 	return sal_True;
781 }
782 
783 
784 int lcl_SearchBackward( const SwTxtNode& rTxtNd, SwAttrCheckArr& rCmpArr,
785 							SwPaM& rPam )
786 {
787 	xub_StrLen nEndPos, nSttPos;
788 	rCmpArr.SetNewSet( rTxtNd, rPam );
789 	if( !rTxtNd.HasHints() )
790 	{
791 		if( !rCmpArr.Found() )
792 			return sal_False;
793 		nEndPos = rCmpArr.GetNdEnd();
794 		lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_False );
795 		return sal_True;
796 	}
797 
798 	// dann gehe mal durch das nach "Start" sortierte Array
799 	const SwpHints& rHtArr = rTxtNd.GetSwpHints();
800 	const SwTxtAttr* pAttr;
801 	sal_uInt16 nPos = rHtArr.Count();
802 
803 	// sollte jetzt schon alles vorhanden sein, dann teste, mit welchem
804 	// das wieder beendet wird.
805 	if( rCmpArr.Found() )
806 	{
807 		while( nPos )
808 			if( !rCmpArr.SetAttrBwd( *( pAttr = rHtArr.GetEnd( --nPos )) ) )
809 			{
810 				nSttPos = *pAttr->GetAnyEnd();
811 				if( nSttPos < rCmpArr.GetNdEnd() )
812 				{
813 					// dann haben wir unser Ende:
814 					nEndPos = rCmpArr.GetNdEnd();
815 					lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False );
816 					return sal_True;
817 				}
818 
819 				// ansonsten muessen wir weiter suchen
820 				break;
821 			}
822 
823 		if( !nPos && rCmpArr.Found() )
824 		{
825 			// dann haben wir unseren Bereich
826 			nEndPos = rCmpArr.GetNdEnd();
827 			lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_False );
828 			return sal_True;
829 		}
830 	}
831 
832 	while( nPos )
833 		if( rCmpArr.SetAttrBwd( *( pAttr = rHtArr.GetEnd( --nPos )) ) )
834 		{
835 			// sollten noch mehr auf der gleichen Position anfangen ??
836 			// auch die noch mit testen !!
837 			if( nPos )
838 			{
839 				nEndPos = *pAttr->GetAnyEnd();
840 				while( --nPos && nEndPos ==
841 						*( pAttr = rHtArr.GetEnd( nPos ))->GetAnyEnd() &&
842 						rCmpArr.SetAttrBwd( *pAttr ) )
843 					;
844 			}
845 			if( !rCmpArr.Found() )
846 				continue;
847 
848 
849 			// dann haben wir den Bereich zusammen
850 			if( (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
851 				return sal_False;
852 			lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False );
853 			return sal_True;
854 		}
855 
856 	if( !rCmpArr.CheckStack() ||
857 		(nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
858 		return sal_False;
859 	lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False );
860 	return sal_True;
861 }
862 
863 
864 int lcl_Search( const SwCntntNode& rCNd, const SfxItemSet& rCmpSet, sal_Bool bNoColls )
865 {
866 	// nur die harte Attributierung suchen ?
867     if( bNoColls && !rCNd.HasSwAttrSet() )
868 		return sal_False;
869 
870 	const SfxItemSet& rNdSet = rCNd.GetSwAttrSet();
871 	SfxItemIter aIter( rCmpSet );
872 	const SfxPoolItem* pItem = aIter.GetCurItem();
873 	const SfxPoolItem* pNdItem;
874 	sal_uInt16 nWhich;
875 
876 	while( sal_True )
877 	{
878 		// nur testen, ob vorhanden ist ?
879 		if( IsInvalidItem( pItem ))
880 		{
881 			nWhich = rCmpSet.GetWhichByPos( aIter.GetCurPos() );
882 			if( SFX_ITEM_SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
883 				|| CmpAttr( *pNdItem, rNdSet.GetPool()->GetDefaultItem( nWhich ) ))
884 				return sal_False;
885 		}
886 		else
887 		{
888 			nWhich = pItem->Which();
889 //JP 27.02.95: wenn nach defaults gesucht wird, dann muss man bis zum Pool
890 //				runter
891 //			if( SFX_ITEM_SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
892 //				|| *pNdItem != *pItem )
893 			if( !CmpAttr( rNdSet.Get( nWhich, !bNoColls ), *pItem ))
894 				return sal_False;
895 		}
896 
897 		if( aIter.IsAtEnd() )
898 			break;
899 		pItem = aIter.NextItem();
900 	}
901 	return sal_True;			// wurde gefunden
902 }
903 
904 
905 sal_Bool SwPaM::Find( const SfxPoolItem& rAttr, sal_Bool bValue, SwMoveFn fnMove,
906 					const SwPaM *pRegion, sal_Bool bInReadOnly )
907 {
908 	// stelle fest welches Attribut gesucht wird:
909 	sal_uInt16 nWhich = rAttr.Which();
910     int bCharAttr = isCHRATR(nWhich) || isTXTATR(nWhich);
911 
912 	SwPaM* pPam = MakeRegion( fnMove, pRegion );
913 
914 	sal_Bool bFound = sal_False;
915 	sal_Bool bFirst = sal_True;
916 	sal_Bool bSrchForward = fnMove == fnMoveForward;
917 	SwCntntNode * pNode;
918 	const SfxPoolItem* pItem;
919 	SwpFmts aFmtArr;
920 
921 	// Wenn am Anfang/Ende, aus dem Node moven
922 	if( bSrchForward
923 		? pPam->GetPoint()->nContent.GetIndex() == pPam->GetCntntNode()->Len()
924 		: !pPam->GetPoint()->nContent.GetIndex() )
925 	{
926 		if( !(*fnMove->fnNds)( &pPam->GetPoint()->nNode, sal_False ))
927 		{
928 			delete pPam;
929 			return sal_False;
930 		}
931 		SwCntntNode *pNd = pPam->GetCntntNode();
932 		xub_StrLen nTmpPos = bSrchForward ? 0 : pNd->Len();
933 		pPam->GetPoint()->nContent.Assign( pNd, nTmpPos );
934 	}
935 
936 	while( 0 != ( pNode = ::GetNode( *pPam, bFirst, fnMove, bInReadOnly ) ) )
937 	{
938 		if( bCharAttr )
939 		{
940 			if( !pNode->IsTxtNode() )       // CharAttr sind nur in TextNodes
941 				continue;
942 
943 			if( ((SwTxtNode*)pNode)->HasHints() &&
944 				lcl_Search( *(SwTxtNode*)pNode, *pPam, rAttr, fnMove,  bValue ))
945 			{
946 				// setze auf die Werte vom Attribut
947 				SetMark();
948 				*GetPoint() = *pPam->GetPoint();
949 				*GetMark() = *pPam->GetMark();
950 				bFound = sal_True;
951 				break;
952 			}
953             else if (isTXTATR(nWhich))
954 				continue;               // --> also weiter
955 		}
956 
957 		// keine harte Attributierung, dann pruefe, ob die Vorlage schon
958 		// mal nach dem Attribut befragt wurde
959         if( !pNode->HasSwAttrSet() )
960 		{
961 			const SwFmt* pTmpFmt = pNode->GetFmtColl();
962 			if( aFmtArr.Count() && aFmtArr.Seek_Entry( pTmpFmt ))
963 				continue; 	// die Collection wurde schon mal befragt
964 			aFmtArr.Insert( pTmpFmt );
965 		}
966 
967 		if( SFX_ITEM_SET == pNode->GetSwAttrSet().GetItemState( nWhich,
968 			sal_True, &pItem ) && ( !bValue || *pItem == rAttr ) )
969 		{
970 			// FORWARD:  Point an das Ende, GetMark zum Anfanf vom Node
971 			// BACKWARD: Point zum Anfang,	GetMark an das Ende vom Node
972 			// und immer nach der Logik: inkl. Start, exkl. End !!!
973 			*GetPoint() = *pPam->GetPoint();
974 			SetMark();
975 			pNode->MakeEndIndex( &GetPoint()->nContent );
976 			bFound = sal_True;
977 			break;
978 		}
979 	}
980 
981 	// beim rueckwaerts Suchen noch Point und Mark vertauschen
982 	if( bFound && !bSrchForward )
983 		Exchange();
984 
985 	delete pPam;
986 	return bFound;
987 }
988 
989 
990 typedef int (*FnSearchAttr)( const SwTxtNode&, SwAttrCheckArr&, SwPaM& );
991 
992 sal_Bool SwPaM::Find( const SfxItemSet& rSet, sal_Bool bNoColls, SwMoveFn fnMove,
993 					const SwPaM *pRegion, sal_Bool bInReadOnly, sal_Bool bMoveFirst )
994 {
995 	SwPaM* pPam = MakeRegion( fnMove, pRegion );
996 
997 	sal_Bool bFound = sal_False;
998 	sal_Bool bFirst = sal_True;
999 	sal_Bool bSrchForward = fnMove == fnMoveForward;
1000 	SwCntntNode * pNode;
1001 	SwpFmts aFmtArr;
1002 
1003 	// teste doch mal welche Text/Char-Attribute gesucht werden
1004 	SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls );
1005 	SfxItemSet aOtherSet( GetDoc()->GetAttrPool(),
1006 							RES_PARATR_BEGIN, RES_GRFATR_END-1 );
1007 	aOtherSet.Put( rSet, sal_False );	// alle Invalid-Items erhalten!
1008 
1009 	FnSearchAttr fnSearch = bSrchForward
1010 								? (&::lcl_SearchForward)
1011 								: (&::lcl_SearchBackward);
1012 
1013 	// Wenn am Anfang/Ende, aus dem Node moven
1014 	// Wenn am Anfang/Ende, aus dem Node moven
1015 	if( bMoveFirst &&
1016         ( bSrchForward
1017 		? pPam->GetPoint()->nContent.GetIndex() == pPam->GetCntntNode()->Len()
1018 		: !pPam->GetPoint()->nContent.GetIndex() ) )
1019 	{
1020 		if( !(*fnMove->fnNds)( &pPam->GetPoint()->nNode, sal_False ))
1021 		{
1022 			delete pPam;
1023 			return sal_False;
1024 		}
1025 		SwCntntNode *pNd = pPam->GetCntntNode();
1026 		xub_StrLen nTmpPos = bSrchForward ? 0 : pNd->Len();
1027 		pPam->GetPoint()->nContent.Assign( pNd, nTmpPos );
1028 	}
1029 
1030 
1031 	while( 0 != ( pNode = ::GetNode( *pPam, bFirst, fnMove, bInReadOnly ) ) )
1032 	{
1033 		if( aCmpArr.Count() )
1034 		{
1035 			if( !pNode->IsTxtNode() )       // CharAttr sind nur in TextNodes
1036 				continue;
1037 
1038 			if( (!aOtherSet.Count() ||
1039                 lcl_Search( *pNode, aOtherSet, bNoColls )) &&
1040 				(*fnSearch)( *(SwTxtNode*)pNode, aCmpArr, *pPam ))
1041 			{
1042 				// setze auf die Werte vom Attribut
1043 				SetMark();
1044 				*GetPoint() = *pPam->GetPoint();
1045 				*GetMark() = *pPam->GetMark();
1046 				bFound = sal_True;
1047 				break;
1048 			}
1049 			continue;		// TextAttribute
1050 		}
1051 
1052 		if( !aOtherSet.Count() )
1053 			continue;
1054 
1055 		// keine harte Attributierung, dann pruefe, ob die Vorlage schon
1056 		// mal nach dem Attribut befragt wurde
1057         if( !pNode->HasSwAttrSet() )
1058 		{
1059 			const SwFmt* pTmpFmt = pNode->GetFmtColl();
1060 			if( aFmtArr.Count() && aFmtArr.Seek_Entry( pTmpFmt ))
1061 				continue; 	// die Collection wurde schon mal befragt
1062 			aFmtArr.Insert( pTmpFmt );
1063 		}
1064 
1065         if( lcl_Search( *pNode, aOtherSet, bNoColls ))
1066 		{
1067 			// FORWARD:  Point an das Ende, GetMark zum Anfanf vom Node
1068 			// BACKWARD: Point zum Anfang,	GetMark an das Ende vom Node
1069 			// und immer nach der Logik: inkl. Start, exkl. End !!!
1070 			*GetPoint() = *pPam->GetPoint();
1071 			SetMark();
1072 			pNode->MakeEndIndex( &GetPoint()->nContent );
1073 			bFound = sal_True;
1074 			break;
1075 		}
1076 	}
1077 
1078 	// beim rueckwaerts Suchen noch Point und Mark vertauschen
1079 	if( bFound && !bSrchForward )
1080 		Exchange();
1081 
1082 	delete pPam;
1083 	return bFound;
1084 }
1085 
1086 //------------------ Methoden vom SwCursor ---------------------------
1087 
1088 // Parameter fuer das Suchen vom Attributen
1089 struct SwFindParaAttr : public SwFindParas
1090 {
1091 	sal_Bool bValue;
1092 	const SfxItemSet *pSet, *pReplSet;
1093 	const SearchOptions *pSearchOpt;
1094 	SwCursor& rCursor;
1095 	utl::TextSearch* pSTxt;
1096 
1097 	SwFindParaAttr( const SfxItemSet& rSet, sal_Bool bNoCollection,
1098 					const SearchOptions* pOpt, const SfxItemSet* pRSet,
1099 					SwCursor& rCrsr )
1100         : bValue( bNoCollection ), pSet( &rSet ), pReplSet( pRSet ),
1101           pSearchOpt( pOpt ), rCursor( rCrsr ),pSTxt( 0 ) {}
1102 
1103     virtual ~SwFindParaAttr()   { delete pSTxt; }
1104 
1105 	virtual int Find( SwPaM* , SwMoveFn , const SwPaM*, sal_Bool bInReadOnly );
1106 	virtual int IsReplaceMode() const;
1107 };
1108 
1109 
1110 int SwFindParaAttr::Find( SwPaM* pCrsr, SwMoveFn fnMove, const SwPaM* pRegion,
1111 							sal_Bool bInReadOnly )
1112 {
1113 	// String ersetzen ?? (nur wenn Text angegeben oder nicht attributiert
1114 	// 						gesucht wird)
1115 	sal_Bool bReplaceTxt = pSearchOpt && ( pSearchOpt->replaceString.getLength() ||
1116 									!pSet->Count() );
1117 	sal_Bool bReplaceAttr = pReplSet && pReplSet->Count();
1118     sal_Bool bMoveFirst = !bReplaceAttr;
1119 	if( bInReadOnly && (bReplaceAttr || bReplaceTxt ))
1120 		bInReadOnly = sal_False;
1121 
1122 	// wir suchen nach Attributen, soll zusaetzlich Text gesucht werden ?
1123 	{
1124 		SwPaM aRegion( *pRegion->GetMark(), *pRegion->GetPoint() );
1125 		SwPaM* pTextRegion = &aRegion;
1126 		SwPaM aSrchPam( *pCrsr->GetPoint() );
1127 
1128 		while( sal_True )
1129 		{
1130 			if( pSet->Count() )			// gibts ueberhaupt Attributierung?
1131 			{
1132 				// zuerst die Attributierung
1133 				if( !aSrchPam.Find( *pSet, bValue, fnMove, &aRegion, bInReadOnly, bMoveFirst ) )
1134 //JP 17.11.95: was ist mit Attributen in leeren Absaetzen !!
1135 //					|| *pCrsr->GetMark() == *pCrsr->GetPoint() )	// kein Bereich ??
1136 					return FIND_NOT_FOUND;
1137                 bMoveFirst = sal_True;
1138 
1139 				if( !pSearchOpt )
1140 					break; 		// ok, nur Attribute, also gefunden
1141 
1142 				pTextRegion = &aSrchPam;
1143 			}
1144 			else if( !pSearchOpt )
1145 				return FIND_NOT_FOUND;
1146 
1147 			// dann darin den Text
1148 			if( !pSTxt )
1149 			{
1150 				SearchOptions aTmp( *pSearchOpt );
1151 
1152 				// search in selection
1153 				aTmp.searchFlag |= (SearchFlags::REG_NOT_BEGINOFLINE |
1154 								    SearchFlags::REG_NOT_ENDOFLINE);
1155 
1156                 MsLangId::convertLanguageToLocale( LANGUAGE_SYSTEM, aTmp.Locale );
1157 
1158 				pSTxt = new utl::TextSearch( aTmp );
1159 			}
1160 
1161 			// todo/mba: searching for attributes in Outliner text?!
1162 			sal_Bool bSearchInNotes = sal_False;
1163 
1164 			// Bug 24665: suche im richtigen Bereich weiter (pTextRegion!)
1165 			if( aSrchPam.Find( *pSearchOpt, bSearchInNotes, *pSTxt, fnMove, pTextRegion, bInReadOnly ) &&
1166 				*aSrchPam.GetMark() != *aSrchPam.GetPoint() )   // gefunden ?
1167 				break;										// also raus
1168 			else if( !pSet->Count() )
1169 				return FIND_NOT_FOUND;		// nur Text und nicht gefunden
1170 
1171 /*          // --> FME 2007-4-12 #i74765 # Why should we move the position?
1172             Moving the position results in bugs when there are two adjacent
1173             portions which both have the requested attributes set. I suspect this
1174             should be only be an optimization. Therefore I boldly remove it now!
1175 
1176             // JP: und wieder neu aufsetzen, aber eine Position weiter
1177 			//JP 04.11.97: Bug 44897 - aber den Mark wieder aufheben, damit
1178 			//				weiterbewegt werden kann!
1179 			{
1180 				sal_Bool bCheckRegion = sal_True;
1181 				SwPosition* pPos = aSrchPam.GetPoint();
1182 				if( !(*fnMove->fnNd)( &pPos->nNode.GetNode(),
1183 										&pPos->nContent, CRSR_SKIP_CHARS ))
1184 				{
1185 					if( (*fnMove->fnNds)( &pPos->nNode, sal_False ))
1186 					{
1187 						SwCntntNode *pNd = pPos->nNode.GetNode().GetCntntNode();
1188 						xub_StrLen nCPos;
1189 						if( fnMove == fnMoveForward )
1190 							nCPos = 0;
1191 						else
1192 							nCPos = pNd->Len();
1193 						pPos->nContent.Assign( pNd, nCPos );
1194 					}
1195 					else
1196 						bCheckRegion = sal_False;
1197 				}
1198 				if( !bCheckRegion || *aRegion.GetPoint() <= *pPos )
1199 					return FIND_NOT_FOUND;		// nicht gefunden
1200 			}*/
1201 			*aRegion.GetMark() = *aSrchPam.GetPoint();
1202 		}
1203 
1204 		*pCrsr->GetPoint() = *aSrchPam.GetPoint();
1205 		pCrsr->SetMark();
1206 		*pCrsr->GetMark() = *aSrchPam.GetMark();
1207 	}
1208 
1209 	if( bReplaceTxt )
1210 	{
1211         const bool bRegExp(
1212                 SearchAlgorithms_REGEXP == pSearchOpt->algorithmType);
1213 		SwIndex& rSttCntIdx = pCrsr->Start()->nContent;
1214 		xub_StrLen nSttCnt = rSttCntIdx.GetIndex();
1215 
1216 		// damit die Region auch verschoben wird, in den Shell-Cursr-Ring
1217 		// mit aufnehmen !!
1218         Ring *pPrevRing = 0;
1219 		if( bRegExp )
1220 		{
1221             pPrevRing = pRegion->GetPrev();
1222 			((Ring*)pRegion)->MoveRingTo( &rCursor );
1223 		}
1224 
1225         ::std::auto_ptr<String> pRepl( (bRegExp) ?
1226                 ReplaceBackReferences( *pSearchOpt, pCrsr ) : 0 );
1227         rCursor.GetDoc()->ReplaceRange( *pCrsr,
1228             (pRepl.get()) ? *pRepl : String(pSearchOpt->replaceString),
1229             bRegExp );
1230 		rCursor.SaveTblBoxCntnt( pCrsr->GetPoint() );
1231 
1232 		if( bRegExp )
1233 		{
1234 			// und die Region wieder herausnehmen:
1235 			Ring *p, *pNext = (Ring*)pRegion;
1236 			do {
1237 				p = pNext;
1238 				pNext = p->GetNext();
1239 				p->MoveTo( (Ring*)pRegion );
1240             } while( p != pPrevRing );
1241 		}
1242 		rSttCntIdx = nSttCnt;
1243 	}
1244 
1245 	if( bReplaceAttr )
1246 	{
1247 		// --- Ist die Selection noch da ??????
1248 
1249 		// und noch die Attribute setzen
1250 #ifdef OLD
1251 		pCrsr->GetDoc()->Insert( *pCrsr, *pReplSet, 0 );
1252 #else
1253 		//JP 13.07.95: alle gesuchten Attribute werden, wenn nicht im
1254 		//				ReplaceSet angegeben, auf Default zurueck gesetzt
1255 
1256 		if( !pSet->Count() )
1257         {
1258             pCrsr->GetDoc()->InsertItemSet( *pCrsr, *pReplSet, 0 );
1259         }
1260 		else
1261 		{
1262 			SfxItemPool* pPool = pReplSet->GetPool();
1263 			SfxItemSet aSet( *pPool, pReplSet->GetRanges() );
1264 
1265 			SfxItemIter aIter( *pSet );
1266 			const SfxPoolItem* pItem = aIter.GetCurItem();
1267 			while( sal_True )
1268 			{
1269 				// alle die nicht gesetzt sind mit Pool-Defaults aufuellen
1270 				if( !IsInvalidItem( pItem ) && SFX_ITEM_SET !=
1271 					pReplSet->GetItemState( pItem->Which(), sal_False ))
1272 					aSet.Put( pPool->GetDefaultItem( pItem->Which() ));
1273 
1274 				if( aIter.IsAtEnd() )
1275 					break;
1276 				pItem = aIter.NextItem();
1277 			}
1278 			aSet.Put( *pReplSet );
1279             pCrsr->GetDoc()->InsertItemSet( *pCrsr, aSet, 0 );
1280         }
1281 #endif
1282 		return FIND_NO_RING;
1283 	}
1284 
1285 	else
1286 		return FIND_FOUND;
1287 }
1288 
1289 
1290 int SwFindParaAttr::IsReplaceMode() const
1291 {
1292 	return ( pSearchOpt && pSearchOpt->replaceString.getLength() ) ||
1293 		   ( pReplSet && pReplSet->Count() );
1294 }
1295 
1296 // Suchen nach Attributen
1297 
1298 
1299 sal_uLong SwCursor::Find( const SfxItemSet& rSet, sal_Bool bNoCollections,
1300 					SwDocPositions nStart, SwDocPositions nEnde, sal_Bool& bCancel,
1301 					FindRanges eFndRngs,
1302 					const SearchOptions* pSearchOpt, const SfxItemSet* pReplSet )
1303 {
1304 	// OLE-Benachrichtigung abschalten !!
1305 	SwDoc* pDoc = GetDoc();
1306 	Link aLnk( pDoc->GetOle2Link() );
1307 	pDoc->SetOle2Link( Link() );
1308 
1309 	sal_Bool bReplace = ( pSearchOpt && ( pSearchOpt->replaceString.getLength() ||
1310 									!rSet.Count() ) ) ||
1311 					(pReplSet && pReplSet->Count());
1312     bool const bStartUndo = pDoc->GetIDocumentUndoRedo().DoesUndo() && bReplace;
1313     if (bStartUndo)
1314     {
1315         pDoc->GetIDocumentUndoRedo().StartUndo( UNDO_REPLACE, NULL );
1316     }
1317 
1318 	SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt,
1319 									pReplSet, *this );
1320 
1321     sal_uLong nRet = FindAll(aSwFindParaAttr, nStart, nEnde, eFndRngs, bCancel );
1322 	pDoc->SetOle2Link( aLnk );
1323 	if( nRet && bReplace )
1324 		pDoc->SetModified();
1325 
1326     if (bStartUndo)
1327     {
1328         pDoc->GetIDocumentUndoRedo().EndUndo( UNDO_REPLACE, NULL );
1329     }
1330 
1331 	return nRet;
1332 }
1333 
1334 
1335 
1336