Impress Remote 1.0.5, tag sdremote-1.0.5
[LibreOffice.git] / sw / source / core / crsr / findattr.cxx
blob8df43bef7d718e1ea7c72d3db48f301cd6467c72
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <com/sun/star/lang/Locale.hpp>
21 #include <com/sun/star/util/SearchOptions.hpp>
22 #include <com/sun/star/util/SearchFlags.hpp>
23 #include <i18npool/languagetag.hxx>
24 #include <hintids.hxx>
25 #include <vcl/svapp.hxx>
26 #include <svl/itemiter.hxx>
27 #include <svl/whiter.hxx>
28 #include <editeng/brkitem.hxx>
29 #include <editeng/colritem.hxx>
30 #include <editeng/fontitem.hxx>
31 #include <fmtpdsc.hxx>
32 #include <txatbase.hxx>
33 #include <fchrfmt.hxx>
34 #include <charfmt.hxx>
35 #include <doc.hxx>
36 #include <IDocumentUndoRedo.hxx>
37 #include <swcrsr.hxx>
38 #include <editsh.hxx>
39 #include <ndtxt.hxx>
40 #include <pamtyp.hxx>
41 #include <swundo.hxx>
42 #include <crsskip.hxx>
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::lang;
47 using namespace ::com::sun::star::util;
49 typedef std::set<SwFmt*> SwpFmts;
51 // Special case for SvxFontItem: only compare the name
52 int CmpAttr( const SfxPoolItem& rItem1, const SfxPoolItem& rItem2 )
54 switch( rItem1.Which() )
56 case RES_CHRATR_FONT:
57 return ((SvxFontItem&)rItem1).GetFamilyName() ==
58 ((SvxFontItem&)rItem2).GetFamilyName();
60 case RES_CHRATR_COLOR:
61 return ((SvxColorItem&)rItem1).GetValue().IsRGBEqual(
62 ((SvxColorItem&)rItem2).GetValue() );
63 case RES_PAGEDESC:
64 return ((SwFmtPageDesc&)rItem1).GetNumOffset() ==
65 ((SwFmtPageDesc&)rItem2).GetNumOffset() &&
66 ((SwFmtPageDesc&)rItem1).GetPageDesc() ==
67 ((SwFmtPageDesc&)rItem2).GetPageDesc();
69 return rItem1 == rItem2;
73 const SwTxtAttr* GetFrwrdTxtHint( const SwpHints& rHtsArr, sal_uInt16& rPos,
74 xub_StrLen nCntntPos )
76 while( rPos < rHtsArr.Count() )
78 const SwTxtAttr *pTxtHt = rHtsArr.GetStart( rPos++ );
79 // the start of an attribute has to be in the section
80 if( *pTxtHt->GetStart() >= nCntntPos )
81 return pTxtHt; // valid text attribute
83 return 0; // invalid text attribute
87 const SwTxtAttr* GetBkwrdTxtHint( const SwpHints& rHtsArr, sal_uInt16& rPos,
88 xub_StrLen nCntntPos )
90 while( rPos > 0 )
92 const SwTxtAttr *pTxtHt = rHtsArr.GetStart( --rPos );
93 // the start of an attribute has to be in the section
94 if( *pTxtHt->GetStart() < nCntntPos )
95 return pTxtHt; // valid text attribute
97 return 0; // invalid text attribute
100 static void lcl_SetAttrPam( SwPaM& rPam, xub_StrLen nStart, const xub_StrLen* pEnd,
101 const sal_Bool bSaveMark )
103 xub_StrLen nCntntPos;
104 if( bSaveMark )
105 nCntntPos = rPam.GetMark()->nContent.GetIndex();
106 else
107 nCntntPos = rPam.GetPoint()->nContent.GetIndex();
108 bool bTstEnd = rPam.GetPoint()->nNode == rPam.GetMark()->nNode;
110 SwCntntNode* pCNd = rPam.GetCntntNode();
111 rPam.GetPoint()->nContent.Assign( pCNd, nStart );
112 rPam.SetMark(); // Point == GetMark
114 // Point points to end of search area or end of attribute
115 if( pEnd )
117 if( bTstEnd && *pEnd > nCntntPos )
118 rPam.GetPoint()->nContent = nCntntPos;
119 else
120 rPam.GetPoint()->nContent = *pEnd;
124 // TODO: provide documentation
125 /** search for a text attribute
127 This function searches in a text node for a given attribute.
128 If that is found then the SwPaM contains the section that surrounds the
129 attribute (w.r.t. the search area).
131 @param rTxtNd Text node to search in.
132 @param rPam ???
133 @param rCmpItem ???
134 @param fnMove ???
135 @param bValue ???
136 @return Returns <true> if found, <false> otherwise.
138 static sal_Bool lcl_Search( const SwTxtNode& rTxtNd, SwPaM& rPam,
139 const SfxPoolItem& rCmpItem,
140 SwMoveFn fnMove, sal_Bool bValue )
142 if ( !rTxtNd.HasHints() )
143 return sal_False;
144 const SwTxtAttr *pTxtHt = 0;
145 sal_Bool bForward = fnMove == fnMoveForward;
146 sal_uInt16 nPos = bForward ? 0 : rTxtNd.GetSwpHints().Count();
147 xub_StrLen nCntntPos = rPam.GetPoint()->nContent.GetIndex();
149 while( 0 != ( pTxtHt=(*fnMove->fnGetHint)(rTxtNd.GetSwpHints(),nPos,nCntntPos)))
150 if( pTxtHt->Which() == rCmpItem.Which() &&
151 ( !bValue || CmpAttr( pTxtHt->GetAttr(), rCmpItem )))
153 lcl_SetAttrPam( rPam, *pTxtHt->GetStart(), pTxtHt->GetEnd(), bForward );
154 return sal_True;
156 return sal_False;
159 /// search for multiple text attributes
160 struct _SwSrchChrAttr
162 sal_uInt16 nWhich;
163 xub_StrLen nStt, nEnd;
165 _SwSrchChrAttr( const SfxPoolItem& rItem,
166 xub_StrLen nStart, xub_StrLen nAnyEnd )
167 : nWhich( rItem.Which() ), nStt( nStart ), nEnd( nAnyEnd )
171 class SwAttrCheckArr
173 _SwSrchChrAttr *pFndArr, *pStackArr;
174 xub_StrLen nNdStt, nNdEnd;
175 sal_uInt16 nArrStart, nArrLen;
176 sal_uInt16 nFound, nStackCnt;
177 SfxItemSet aCmpSet;
178 sal_Bool bNoColls;
179 sal_Bool bForward;
181 public:
182 SwAttrCheckArr( const SfxItemSet& rSet, int bForward, int bNoCollections );
183 ~SwAttrCheckArr();
185 void SetNewSet( const SwTxtNode& rTxtNd, const SwPaM& rPam );
187 /// how many attributes are there in total?
188 sal_uInt16 Count() const { return aCmpSet.Count(); }
189 int Found() const { return nFound == aCmpSet.Count(); }
190 int CheckStack();
192 xub_StrLen Start() const;
193 xub_StrLen End() const;
195 xub_StrLen GetNdStt() const { return nNdStt; }
196 xub_StrLen GetNdEnd() const { return nNdEnd; }
198 int SetAttrFwd( const SwTxtAttr& rAttr );
199 int SetAttrBwd( const SwTxtAttr& rAttr );
204 SwAttrCheckArr::SwAttrCheckArr( const SfxItemSet& rSet, int bFwd,
205 int bNoCollections )
206 : aCmpSet( *rSet.GetPool(), RES_CHRATR_BEGIN, RES_TXTATR_END-1 )
208 aCmpSet.Put( rSet, sal_False );
209 bNoColls = 0 != bNoCollections;
211 bForward = 0 != bFwd;
213 // determine area of Fnd/Stack array (Min/Max)
214 SfxItemIter aIter( aCmpSet );
215 nArrStart = aCmpSet.GetWhichByPos( aIter.GetFirstPos() );
216 nArrLen = aCmpSet.GetWhichByPos( aIter.GetLastPos() ) - nArrStart+1;
218 char* pFndChar = new char[ nArrLen * sizeof(_SwSrchChrAttr) ];
219 char* pStackChar = new char[ nArrLen * sizeof(_SwSrchChrAttr) ];
221 pFndArr = (_SwSrchChrAttr*)pFndChar;
222 pStackArr = (_SwSrchChrAttr*)pStackChar;
225 SwAttrCheckArr::~SwAttrCheckArr()
227 delete[] (char*)pFndArr;
228 delete[] (char*)pStackArr;
231 void SwAttrCheckArr::SetNewSet( const SwTxtNode& rTxtNd, const SwPaM& rPam )
233 memset( pFndArr, 0, nArrLen * sizeof(_SwSrchChrAttr) );
234 memset( pStackArr, 0, nArrLen * sizeof(_SwSrchChrAttr) );
235 nFound = 0;
236 nStackCnt = 0;
238 if( bForward )
240 nNdStt = rPam.GetPoint()->nContent.GetIndex();
241 nNdEnd = rPam.GetPoint()->nNode == rPam.GetMark()->nNode
242 ? rPam.GetMark()->nContent.GetIndex()
243 : rTxtNd.GetTxt().Len();
245 else
247 nNdEnd = rPam.GetPoint()->nContent.GetIndex();
248 nNdStt = rPam.GetPoint()->nNode == rPam.GetMark()->nNode
249 ? rPam.GetMark()->nContent.GetIndex()
250 : 0;
253 if( bNoColls && !rTxtNd.HasSwAttrSet() )
254 return ;
256 const SfxItemSet& rSet = rTxtNd.GetSwAttrSet();
258 SfxItemIter aIter( aCmpSet );
259 const SfxPoolItem* pItem = aIter.GetCurItem();
260 const SfxPoolItem* pFndItem;
261 sal_uInt16 nWhich;
263 while( sal_True )
265 if( IsInvalidItem( pItem ) )
267 nWhich = aCmpSet.GetWhichByPos( aIter.GetCurPos() );
268 if( RES_TXTATR_END <= nWhich )
269 break; // end of text attributes
271 if( SFX_ITEM_SET == rSet.GetItemState( nWhich, !bNoColls, &pFndItem )
272 && !CmpAttr( *pFndItem, rSet.GetPool()->GetDefaultItem( nWhich ) ))
274 pFndArr[ nWhich - nArrStart ] =
275 _SwSrchChrAttr( *pFndItem, nNdStt, nNdEnd );
276 nFound++;
279 else
281 if( RES_TXTATR_END <= (nWhich = pItem->Which() ))
282 break; // end of text attributes
284 if( CmpAttr( rSet.Get( nWhich, !bNoColls ), *pItem ) )
286 pFndArr[ nWhich - nArrStart ] =
287 _SwSrchChrAttr( *pItem, nNdStt, nNdEnd );
288 nFound++;
292 if( aIter.IsAtEnd() )
293 break;
294 pItem = aIter.NextItem();
298 static bool
299 lcl_IsAttributeIgnorable(xub_StrLen const nNdStart, xub_StrLen const nNdEnd,
300 _SwSrchChrAttr const& rTmp)
302 // #i115528#: if there is a paragraph attribute, it has been added by the
303 // SwAttrCheckArr ctor, and nFound is 1.
304 // if the paragraph is entirely covered by hints that override the paragraph
305 // attribute, then this function must find an attribute to decrement nFound!
306 // so check for an empty search range, let attributes that start/end there
307 // cover it, and hope for the best...
308 return ((nNdEnd == nNdStart)
309 ? ((rTmp.nEnd < nNdStart) || (nNdEnd < rTmp.nStt))
310 : ((rTmp.nEnd <= nNdStart) || (nNdEnd <= rTmp.nStt)));
313 int SwAttrCheckArr::SetAttrFwd( const SwTxtAttr& rAttr )
315 _SwSrchChrAttr aTmp( rAttr.GetAttr(), *rAttr.GetStart(), *rAttr.GetAnyEnd() );
317 // ignore all attributes not in search range
318 if (lcl_IsAttributeIgnorable(nNdStt, nNdEnd, aTmp))
320 return Found();
323 const SfxPoolItem* pItem;
324 // here we explicitly also search in character templates
325 sal_uInt16 nWhch = rAttr.Which();
326 SfxWhichIter* pIter = NULL;
327 const SfxPoolItem* pTmpItem = NULL;
328 const SfxItemSet* pSet = NULL;
329 if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
331 if( bNoColls && RES_TXTATR_CHARFMT == nWhch )
332 return Found();
333 pTmpItem = NULL;
334 pSet = CharFmt::GetItemSet( rAttr.GetAttr() );
335 if ( pSet )
337 pIter = new SfxWhichIter( *pSet );
338 nWhch = pIter->FirstWhich();
339 while( nWhch &&
340 SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
341 nWhch = pIter->NextWhich();
342 if( !nWhch )
343 pTmpItem = NULL;
346 else
347 pTmpItem = &rAttr.GetAttr();
348 while( pTmpItem )
350 SfxItemState eState = aCmpSet.GetItemState( nWhch, sal_False, &pItem );
351 if( SFX_ITEM_DONTCARE == eState || SFX_ITEM_SET == eState )
353 sal_uInt16 n;
354 _SwSrchChrAttr* pCmp;
356 // first delete all up to start position that are already invalid
357 _SwSrchChrAttr* pArrPtr;
358 if( nFound )
359 for( pArrPtr = pFndArr, n = 0; n < nArrLen;
360 ++n, ++pArrPtr )
361 if( pArrPtr->nWhich && pArrPtr->nEnd <= aTmp.nStt )
363 pArrPtr->nWhich = 0; // deleted
364 nFound--;
367 // delete all up to start position that are already invalid and
368 // move all "open" ones (= stick out over start position) from stack
369 // into FndSet
370 if( nStackCnt )
371 for( pArrPtr = pStackArr, n=0; n < nArrLen; ++n, ++pArrPtr )
373 if( !pArrPtr->nWhich )
374 continue;
376 if( pArrPtr->nEnd <= aTmp.nStt )
378 pArrPtr->nWhich = 0; // deleted
379 if( !--nStackCnt )
380 break;
382 else if( pArrPtr->nStt <= aTmp.nStt )
384 if( ( pCmp = &pFndArr[ n ])->nWhich )
386 if( pCmp->nEnd < pArrPtr->nEnd ) // extend
387 pCmp->nEnd = pArrPtr->nEnd;
389 else
391 *pCmp = *pArrPtr;
392 nFound++;
394 pArrPtr->nWhich = 0;
395 if( !--nStackCnt )
396 break;
400 bool bContinue = false;
402 if( SFX_ITEM_DONTCARE == eState )
404 // Will the attribute become valid?
405 if( !CmpAttr( aCmpSet.GetPool()->GetDefaultItem( nWhch ),
406 *pTmpItem ))
408 // search attribute and extend if needed
409 if( !( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
411 *pCmp = aTmp; // not found, insert
412 nFound++;
414 else if( pCmp->nEnd < aTmp.nEnd ) // extend?
415 pCmp->nEnd = aTmp.nEnd;
417 bContinue = true;
420 // Will the attribute become valid?
421 else if( CmpAttr( *pItem, *pTmpItem ) )
423 pFndArr[ nWhch - nArrStart ] = aTmp;
424 ++nFound;
425 bContinue = true;
428 // then is has to go on the stack
429 if( !bContinue && ( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
431 // exists on stack, only if it is even bigger
432 if( pCmp->nEnd > aTmp.nEnd )
434 OSL_ENSURE( !pStackArr[ nWhch - nArrStart ].nWhich,
435 "slot on stack is still in use" );
437 if( aTmp.nStt <= pCmp->nStt )
438 pCmp->nStt = aTmp.nEnd;
439 else
440 pCmp->nEnd = aTmp.nStt;
442 pStackArr[ nWhch - nArrStart ] = *pCmp;
443 nStackCnt++;
445 pCmp->nWhich = 0;
446 nFound--;
449 if( pIter )
451 nWhch = pIter->NextWhich();
452 while( nWhch &&
453 SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
454 nWhch = pIter->NextWhich();
455 if( !nWhch )
456 break;
458 else
459 break;
461 return Found();
465 int SwAttrCheckArr::SetAttrBwd( const SwTxtAttr& rAttr )
467 _SwSrchChrAttr aTmp( rAttr.GetAttr(), *rAttr.GetStart(), *rAttr.GetAnyEnd() );
469 // ignore all attributes not in search range
470 if (lcl_IsAttributeIgnorable(nNdStt, nNdEnd, aTmp))
472 return Found();
475 const SfxPoolItem* pItem;
476 // here we explicitly also search in character templates
477 sal_uInt16 nWhch = rAttr.Which();
478 SfxWhichIter* pIter = NULL;
479 const SfxPoolItem* pTmpItem = NULL;
480 const SfxItemSet* pSet = NULL;
481 if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
483 if( bNoColls && RES_TXTATR_CHARFMT == nWhch )
484 return Found();
486 pSet = CharFmt::GetItemSet( rAttr.GetAttr() );
487 if ( pSet )
489 pIter = new SfxWhichIter( *pSet );
490 nWhch = pIter->FirstWhich();
491 while( nWhch &&
492 SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
493 nWhch = pIter->NextWhich();
494 if( !nWhch )
495 pTmpItem = NULL;
498 else
499 pTmpItem = &rAttr.GetAttr();
500 while( pTmpItem )
502 SfxItemState eState = aCmpSet.GetItemState( nWhch, sal_False, &pItem );
503 if( SFX_ITEM_DONTCARE == eState || SFX_ITEM_SET == eState )
505 sal_uInt16 n;
506 _SwSrchChrAttr* pCmp;
508 // first delete all up to start position that are already invalid
509 _SwSrchChrAttr* pArrPtr;
510 if( nFound )
511 for( pArrPtr = pFndArr, n = 0; n < nArrLen; ++n, ++pArrPtr )
512 if( pArrPtr->nWhich && pArrPtr->nStt >= aTmp.nEnd )
514 pArrPtr->nWhich = 0; // deleted
515 nFound--;
518 // delete all up to start position that are already invalid and
519 // move all "open" ones (= stick out over start position) from stack
520 // into FndSet
521 if( nStackCnt )
522 for( pArrPtr = pStackArr, n = 0; n < nArrLen; ++n, ++pArrPtr )
524 if( !pArrPtr->nWhich )
525 continue;
527 if( pArrPtr->nStt >= aTmp.nEnd )
529 pArrPtr->nWhich = 0; // deleted
530 if( !--nStackCnt )
531 break;
533 else if( pArrPtr->nEnd >= aTmp.nEnd )
535 if( ( pCmp = &pFndArr[ n ])->nWhich )
537 if( pCmp->nStt > pArrPtr->nStt ) // extend
538 pCmp->nStt = pArrPtr->nStt;
540 else
542 *pCmp = *pArrPtr;
543 nFound++;
545 pArrPtr->nWhich = 0;
546 if( !--nStackCnt )
547 break;
551 bool bContinue = false;
552 if( SFX_ITEM_DONTCARE == eState )
554 // Will the attribute become valid?
555 if( !CmpAttr( aCmpSet.GetPool()->GetDefaultItem( nWhch ),
556 *pTmpItem ) )
558 // search attribute and extend if needed
559 if( !( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
561 *pCmp = aTmp; // not found, insert
562 nFound++;
564 else if( pCmp->nStt > aTmp.nStt ) // extend?
565 pCmp->nStt = aTmp.nStt;
567 bContinue = true;
570 // Will the attribute become valid?
571 else if( CmpAttr( *pItem, *pTmpItem ))
573 pFndArr[ nWhch - nArrStart ] = aTmp;
574 ++nFound;
575 bContinue = true;
578 // then is has to go on the stack
579 if( !bContinue && ( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich )
581 // exists on stack, only if it is even bigger
582 if( pCmp->nStt < aTmp.nStt )
584 OSL_ENSURE( !pStackArr[ nWhch - nArrStart ].nWhich,
585 "slot on stack is still in use" );
587 if( aTmp.nEnd <= pCmp->nEnd )
588 pCmp->nEnd = aTmp.nStt;
589 else
590 pCmp->nStt = aTmp.nEnd;
592 pStackArr[ nWhch - nArrStart ] = *pCmp;
593 nStackCnt++;
595 pCmp->nWhich = 0;
596 nFound--;
599 if( pIter )
601 nWhch = pIter->NextWhich();
602 while( nWhch &&
603 SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) )
604 nWhch = pIter->NextWhich();
605 if( !nWhch )
606 break;
608 else
609 break;
611 return Found();
615 xub_StrLen SwAttrCheckArr::Start() const
617 xub_StrLen nStart = nNdStt;
618 _SwSrchChrAttr* pArrPtr = pFndArr;
619 for( sal_uInt16 n = 0; n < nArrLen; ++n, ++pArrPtr )
620 if( pArrPtr->nWhich && pArrPtr->nStt > nStart )
621 nStart = pArrPtr->nStt;
623 return nStart;
627 xub_StrLen SwAttrCheckArr::End() const
629 _SwSrchChrAttr* pArrPtr = pFndArr;
630 xub_StrLen nEnd = nNdEnd;
631 for( sal_uInt16 n = 0; n < nArrLen; ++n, ++pArrPtr )
632 if( pArrPtr->nWhich && pArrPtr->nEnd < nEnd )
633 nEnd = pArrPtr->nEnd;
635 return nEnd;
639 int SwAttrCheckArr::CheckStack()
641 if( !nStackCnt )
642 return sal_False;
644 sal_uInt16 n;
645 xub_StrLen nSttPos = Start(), nEndPos = End();
646 _SwSrchChrAttr* pArrPtr;
647 for( pArrPtr = pStackArr, n = 0; n < nArrLen; ++n, ++pArrPtr )
649 if( !pArrPtr->nWhich )
650 continue;
652 if( bForward ? pArrPtr->nEnd <= nSttPos : pArrPtr->nStt >= nEndPos )
654 pArrPtr->nWhich = 0; // deleted
655 if( !--nStackCnt )
656 return nFound == aCmpSet.Count();
658 else if( bForward ? pArrPtr->nStt < nEndPos : pArrPtr->nEnd > nSttPos )
660 // move all "open" ones (= stick out over start position) into FndSet
661 OSL_ENSURE( !pFndArr[ n ].nWhich, "slot in array is already in use" );
662 pFndArr[ n ] = *pArrPtr;
663 pArrPtr->nWhich = 0;
664 nFound++;
665 if( !--nStackCnt )
666 return nFound == aCmpSet.Count();
669 return nFound == aCmpSet.Count();
674 static int lcl_SearchForward( const SwTxtNode& rTxtNd, SwAttrCheckArr& rCmpArr,
675 SwPaM& rPam )
677 xub_StrLen nEndPos, nSttPos;
678 rCmpArr.SetNewSet( rTxtNd, rPam );
679 if( !rTxtNd.HasHints() )
681 if( !rCmpArr.Found() )
682 return sal_False;
683 nEndPos = rCmpArr.GetNdEnd();
684 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_True );
685 return sal_True;
688 const SwpHints& rHtArr = rTxtNd.GetSwpHints();
689 const SwTxtAttr* pAttr;
690 sal_uInt16 nPos = 0;
692 // if everything is already there then check with which it will be ended
693 if( rCmpArr.Found() )
695 for( ; nPos < rHtArr.Count(); ++nPos )
696 if( !rCmpArr.SetAttrFwd( *( pAttr = rHtArr.GetStart( nPos )) ) )
698 if( rCmpArr.GetNdStt() < *pAttr->GetStart() )
700 // found end
701 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(),
702 pAttr->GetStart(), sal_True );
703 return sal_True;
705 // continue search
706 break;
709 if( nPos == rHtArr.Count() && rCmpArr.Found() )
711 // found
712 nEndPos = rCmpArr.GetNdEnd();
713 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_True );
714 return sal_True;
718 for( ; nPos < rHtArr.Count(); ++nPos )
719 if( rCmpArr.SetAttrFwd( *( pAttr = rHtArr.GetStart( nPos )) ) )
721 // Do multiple start at that position? Do also check those:
722 nSttPos = *pAttr->GetStart();
723 while( ++nPos < rHtArr.Count() && nSttPos ==
724 *( pAttr = rHtArr.GetStart( nPos ))->GetStart() &&
725 rCmpArr.SetAttrFwd( *pAttr ) )
727 if( !rCmpArr.Found() )
728 continue;
730 // then we have our search area
731 if( (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
732 return sal_False;
733 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_True );
734 return sal_True;
737 if( !rCmpArr.CheckStack() ||
738 (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
739 return sal_False;
740 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_True );
741 return sal_True;
745 static int lcl_SearchBackward( const SwTxtNode& rTxtNd, SwAttrCheckArr& rCmpArr,
746 SwPaM& rPam )
748 xub_StrLen nEndPos, nSttPos;
749 rCmpArr.SetNewSet( rTxtNd, rPam );
750 if( !rTxtNd.HasHints() )
752 if( !rCmpArr.Found() )
753 return sal_False;
754 nEndPos = rCmpArr.GetNdEnd();
755 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_False );
756 return sal_True;
759 const SwpHints& rHtArr = rTxtNd.GetSwpHints();
760 const SwTxtAttr* pAttr;
761 sal_uInt16 nPos = rHtArr.Count();
763 // if everything is already there then check with which it will be ended
764 if( rCmpArr.Found() )
766 while( nPos )
767 if( !rCmpArr.SetAttrBwd( *( pAttr = rHtArr.GetEnd( --nPos )) ) )
769 nSttPos = *pAttr->GetAnyEnd();
770 if( nSttPos < rCmpArr.GetNdEnd() )
772 // found end
773 nEndPos = rCmpArr.GetNdEnd();
774 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False );
775 return sal_True;
778 // continue search
779 break;
782 if( !nPos && rCmpArr.Found() )
784 // found
785 nEndPos = rCmpArr.GetNdEnd();
786 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_False );
787 return sal_True;
791 while( nPos )
792 if( rCmpArr.SetAttrBwd( *( pAttr = rHtArr.GetEnd( --nPos )) ) )
794 // Do multiple start at that position? Do also check those:
795 if( nPos )
797 nEndPos = *pAttr->GetAnyEnd();
798 while( --nPos && nEndPos ==
799 *( pAttr = rHtArr.GetEnd( nPos ))->GetAnyEnd() &&
800 rCmpArr.SetAttrBwd( *pAttr ) )
803 if( !rCmpArr.Found() )
804 continue;
806 // then we have our search area
807 if( (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
808 return sal_False;
809 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False );
810 return sal_True;
813 if( !rCmpArr.CheckStack() ||
814 (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) )
815 return sal_False;
816 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False );
817 return sal_True;
821 static int lcl_Search( const SwCntntNode& rCNd, const SfxItemSet& rCmpSet, sal_Bool bNoColls )
823 // search only hard attribution?
824 if( bNoColls && !rCNd.HasSwAttrSet() )
825 return sal_False;
827 const SfxItemSet& rNdSet = rCNd.GetSwAttrSet();
828 SfxItemIter aIter( rCmpSet );
829 const SfxPoolItem* pItem = aIter.GetCurItem();
830 const SfxPoolItem* pNdItem;
831 sal_uInt16 nWhich;
833 while( sal_True )
835 if( IsInvalidItem( pItem ))
837 nWhich = rCmpSet.GetWhichByPos( aIter.GetCurPos() );
838 if( SFX_ITEM_SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
839 || CmpAttr( *pNdItem, rNdSet.GetPool()->GetDefaultItem( nWhich ) ))
840 return sal_False;
842 else
844 nWhich = pItem->Which();
846 if( !CmpAttr( rNdSet.Get( nWhich, !bNoColls ), *pItem ))
847 return sal_False;
850 if( aIter.IsAtEnd() )
851 break;
852 pItem = aIter.NextItem();
854 return sal_True; // found
858 sal_Bool SwPaM::Find( const SfxPoolItem& rAttr, sal_Bool bValue, SwMoveFn fnMove,
859 const SwPaM *pRegion, sal_Bool bInReadOnly )
861 // determine which attribute is searched:
862 sal_uInt16 nWhich = rAttr.Which();
863 int bCharAttr = isCHRATR(nWhich) || isTXTATR(nWhich);
865 SwPaM* pPam = MakeRegion( fnMove, pRegion );
867 sal_Bool bFound = sal_False;
868 sal_Bool bFirst = sal_True;
869 sal_Bool bSrchForward = fnMove == fnMoveForward;
870 SwCntntNode * pNode;
871 const SfxPoolItem* pItem;
872 SwpFmts aFmtArr;
874 // if at beginning/end then move it out of the node
875 if( bSrchForward
876 ? pPam->GetPoint()->nContent.GetIndex() == pPam->GetCntntNode()->Len()
877 : !pPam->GetPoint()->nContent.GetIndex() )
879 if( !(*fnMove->fnNds)( &pPam->GetPoint()->nNode, sal_False ))
881 delete pPam;
882 return sal_False;
884 SwCntntNode *pNd = pPam->GetCntntNode();
885 xub_StrLen nTmpPos = bSrchForward ? 0 : pNd->Len();
886 pPam->GetPoint()->nContent.Assign( pNd, nTmpPos );
889 while( 0 != ( pNode = ::GetNode( *pPam, bFirst, fnMove, bInReadOnly ) ) )
891 if( bCharAttr )
893 if( !pNode->IsTxtNode() ) // CharAttr are only in text nodes
894 continue;
896 if( ((SwTxtNode*)pNode)->HasHints() &&
897 lcl_Search( *(SwTxtNode*)pNode, *pPam, rAttr, fnMove, bValue ))
899 // set to the values of the attribute
900 SetMark();
901 *GetPoint() = *pPam->GetPoint();
902 *GetMark() = *pPam->GetMark();
903 bFound = sal_True;
904 break;
906 else if (isTXTATR(nWhich))
907 continue;
910 // no hard attribution, so check if node was asked for this attr before
911 if( !pNode->HasSwAttrSet() )
913 SwFmt* pTmpFmt = pNode->GetFmtColl();
914 if( aFmtArr.find( pTmpFmt ) != aFmtArr.end() )
915 continue; // collection was requested earlier
916 aFmtArr.insert( pTmpFmt );
919 if( SFX_ITEM_SET == pNode->GetSwAttrSet().GetItemState( nWhich,
920 sal_True, &pItem ) && ( !bValue || *pItem == rAttr ) )
922 // FORWARD: SPoint at the end, GetMark at the beginning of the node
923 // BACKWARD: SPoint at the beginning, GetMark at the end of the node
924 // always: incl. start and incl. end
925 *GetPoint() = *pPam->GetPoint();
926 SetMark();
927 pNode->MakeEndIndex( &GetPoint()->nContent );
928 bFound = sal_True;
929 break;
933 // if backward search, switch point and mark
934 if( bFound && !bSrchForward )
935 Exchange();
937 delete pPam;
938 return bFound;
942 typedef int (*FnSearchAttr)( const SwTxtNode&, SwAttrCheckArr&, SwPaM& );
944 sal_Bool SwPaM::Find( const SfxItemSet& rSet, sal_Bool bNoColls, SwMoveFn fnMove,
945 const SwPaM *pRegion, sal_Bool bInReadOnly, sal_Bool bMoveFirst )
947 SwPaM* pPam = MakeRegion( fnMove, pRegion );
949 sal_Bool bFound = sal_False;
950 sal_Bool bFirst = sal_True;
951 sal_Bool bSrchForward = fnMove == fnMoveForward;
952 SwCntntNode * pNode;
953 SwpFmts aFmtArr;
955 // check which text/char attributes are searched
956 SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls );
957 SfxItemSet aOtherSet( GetDoc()->GetAttrPool(),
958 RES_PARATR_BEGIN, RES_GRFATR_END-1 );
959 aOtherSet.Put( rSet, sal_False ); // got all invalid items
961 FnSearchAttr fnSearch = bSrchForward
962 ? (&::lcl_SearchForward)
963 : (&::lcl_SearchBackward);
965 // if at beginning/end then move it out of the node
966 if( bMoveFirst &&
967 ( bSrchForward
968 ? pPam->GetPoint()->nContent.GetIndex() == pPam->GetCntntNode()->Len()
969 : !pPam->GetPoint()->nContent.GetIndex() ) )
971 if( !(*fnMove->fnNds)( &pPam->GetPoint()->nNode, sal_False ))
973 delete pPam;
974 return sal_False;
976 SwCntntNode *pNd = pPam->GetCntntNode();
977 xub_StrLen nTmpPos = bSrchForward ? 0 : pNd->Len();
978 pPam->GetPoint()->nContent.Assign( pNd, nTmpPos );
982 while( 0 != ( pNode = ::GetNode( *pPam, bFirst, fnMove, bInReadOnly ) ) )
984 if( aCmpArr.Count() )
986 if( !pNode->IsTxtNode() ) // CharAttr are only in text nodes
987 continue;
989 if( (!aOtherSet.Count() ||
990 lcl_Search( *pNode, aOtherSet, bNoColls )) &&
991 (*fnSearch)( *(SwTxtNode*)pNode, aCmpArr, *pPam ))
993 // set to the values of the attribute
994 SetMark();
995 *GetPoint() = *pPam->GetPoint();
996 *GetMark() = *pPam->GetMark();
997 bFound = sal_True;
998 break;
1000 continue; // text attribute
1003 if( !aOtherSet.Count() )
1004 continue;
1006 // no hard attribution, so check if node was asked for this attr before
1007 if( !pNode->HasSwAttrSet() )
1009 SwFmt* pTmpFmt = pNode->GetFmtColl();
1010 if( aFmtArr.find( pTmpFmt ) != aFmtArr.end() )
1011 continue; // collection was requested earlier
1012 aFmtArr.insert( pTmpFmt );
1015 if( lcl_Search( *pNode, aOtherSet, bNoColls ))
1017 // FORWARD: SPoint at the end, GetMark at the beginning of the node
1018 // BACKWARD: SPoint at the beginning, GetMark at the end of the node
1019 // always: incl. start and incl. end
1020 *GetPoint() = *pPam->GetPoint();
1021 SetMark();
1022 pNode->MakeEndIndex( &GetPoint()->nContent );
1023 bFound = sal_True;
1024 break;
1028 // if backward search, switch point and mark
1029 if( bFound && !bSrchForward )
1030 Exchange();
1032 delete pPam;
1033 return bFound;
1036 /// parameters for search for attributes
1037 struct SwFindParaAttr : public SwFindParas
1039 sal_Bool bValue;
1040 const SfxItemSet *pSet, *pReplSet;
1041 const SearchOptions *pSearchOpt;
1042 SwCursor& rCursor;
1043 utl::TextSearch* pSTxt;
1045 SwFindParaAttr( const SfxItemSet& rSet, sal_Bool bNoCollection,
1046 const SearchOptions* pOpt, const SfxItemSet* pRSet,
1047 SwCursor& rCrsr )
1048 : bValue( bNoCollection ), pSet( &rSet ), pReplSet( pRSet ),
1049 pSearchOpt( pOpt ), rCursor( rCrsr ),pSTxt( 0 ) {}
1051 virtual ~SwFindParaAttr() { delete pSTxt; }
1053 virtual int Find( SwPaM* , SwMoveFn , const SwPaM*, sal_Bool bInReadOnly );
1054 virtual int IsReplaceMode() const;
1058 int SwFindParaAttr::Find( SwPaM* pCrsr, SwMoveFn fnMove, const SwPaM* pRegion,
1059 sal_Bool bInReadOnly )
1061 // replace string (only if text given and search is not parameterized)?
1062 sal_Bool bReplaceTxt = pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
1063 !pSet->Count() );
1064 sal_Bool bReplaceAttr = pReplSet && pReplSet->Count();
1065 sal_Bool bMoveFirst = !bReplaceAttr;
1066 if( bInReadOnly && (bReplaceAttr || bReplaceTxt ))
1067 bInReadOnly = sal_False;
1069 // We search for attributes, should we search for text as well?
1071 SwPaM aRegion( *pRegion->GetMark(), *pRegion->GetPoint() );
1072 SwPaM* pTextRegion = &aRegion;
1073 SwPaM aSrchPam( *pCrsr->GetPoint() );
1075 while( sal_True )
1077 if( pSet->Count() ) // any attributes?
1079 // first attributes
1080 if( !aSrchPam.Find( *pSet, bValue, fnMove, &aRegion, bInReadOnly, bMoveFirst ) )
1081 return FIND_NOT_FOUND;
1082 bMoveFirst = sal_True;
1084 if( !pSearchOpt )
1085 break; // ok, only attributes, so found
1087 pTextRegion = &aSrchPam;
1089 else if( !pSearchOpt )
1090 return FIND_NOT_FOUND;
1092 // then search in text of it
1093 if( !pSTxt )
1095 SearchOptions aTmp( *pSearchOpt );
1097 // search in selection
1098 aTmp.searchFlag |= (SearchFlags::REG_NOT_BEGINOFLINE |
1099 SearchFlags::REG_NOT_ENDOFLINE);
1101 aTmp.Locale = SvtSysLocale().GetLanguageTag().getLocale();
1103 pSTxt = new utl::TextSearch( aTmp );
1106 // TODO: searching for attributes in Outliner text?!
1107 sal_Bool bSearchInNotes = sal_False;
1109 // continue search in correct section (pTextRegion)
1110 if( aSrchPam.Find( *pSearchOpt, bSearchInNotes, *pSTxt, fnMove, pTextRegion, bInReadOnly ) &&
1111 *aSrchPam.GetMark() != *aSrchPam.GetPoint() )
1112 break; // found
1113 else if( !pSet->Count() )
1114 return FIND_NOT_FOUND; // only text and nothing found
1116 *aRegion.GetMark() = *aSrchPam.GetPoint();
1119 *pCrsr->GetPoint() = *aSrchPam.GetPoint();
1120 pCrsr->SetMark();
1121 *pCrsr->GetMark() = *aSrchPam.GetMark();
1124 if( bReplaceTxt )
1126 const bool bRegExp(
1127 SearchAlgorithms_REGEXP == pSearchOpt->algorithmType);
1128 SwIndex& rSttCntIdx = pCrsr->Start()->nContent;
1129 xub_StrLen nSttCnt = rSttCntIdx.GetIndex();
1131 // add to shell-cursor-ring so that the regions will be moved enventually
1132 Ring *pPrevRing = 0;
1133 if( bRegExp )
1135 pPrevRing = pRegion->GetPrev();
1136 ((Ring*)pRegion)->MoveRingTo( &rCursor );
1139 ::std::auto_ptr<String> pRepl( (bRegExp) ?
1140 ReplaceBackReferences( *pSearchOpt, pCrsr ) : 0 );
1141 rCursor.GetDoc()->ReplaceRange( *pCrsr,
1142 (pRepl.get()) ? *pRepl : String(pSearchOpt->replaceString),
1143 bRegExp );
1144 rCursor.SaveTblBoxCntnt( pCrsr->GetPoint() );
1146 if( bRegExp )
1148 // and remove region again
1149 Ring *p, *pNext = (Ring*)pRegion;
1150 do {
1151 p = pNext;
1152 pNext = p->GetNext();
1153 p->MoveTo( (Ring*)pRegion );
1154 } while( p != pPrevRing );
1156 rSttCntIdx = nSttCnt;
1159 if( bReplaceAttr )
1161 // is the selection still existent?
1162 // all searched attributes are reset to default if
1163 // they are not in ReplaceSet
1164 if( !pSet->Count() )
1166 pCrsr->GetDoc()->InsertItemSet( *pCrsr, *pReplSet, 0 );
1168 else
1170 SfxItemPool* pPool = pReplSet->GetPool();
1171 SfxItemSet aSet( *pPool, pReplSet->GetRanges() );
1173 SfxItemIter aIter( *pSet );
1174 const SfxPoolItem* pItem = aIter.GetCurItem();
1175 while( sal_True )
1177 // reset all that are not set with pool defaults
1178 if( !IsInvalidItem( pItem ) && SFX_ITEM_SET !=
1179 pReplSet->GetItemState( pItem->Which(), sal_False ))
1180 aSet.Put( pPool->GetDefaultItem( pItem->Which() ));
1182 if( aIter.IsAtEnd() )
1183 break;
1184 pItem = aIter.NextItem();
1186 aSet.Put( *pReplSet );
1187 pCrsr->GetDoc()->InsertItemSet( *pCrsr, aSet, 0 );
1190 return FIND_NO_RING;
1193 else
1194 return FIND_FOUND;
1198 int SwFindParaAttr::IsReplaceMode() const
1200 return ( pSearchOpt && !pSearchOpt->replaceString.isEmpty() ) ||
1201 ( pReplSet && pReplSet->Count() );
1204 /// search for attributes
1205 sal_uLong SwCursor::Find( const SfxItemSet& rSet, sal_Bool bNoCollections,
1206 SwDocPositions nStart, SwDocPositions nEnd,
1207 sal_Bool& bCancel, FindRanges eFndRngs,
1208 const SearchOptions* pSearchOpt,
1209 const SfxItemSet* pReplSet )
1211 // switch off OLE-notifications
1212 SwDoc* pDoc = GetDoc();
1213 Link aLnk( pDoc->GetOle2Link() );
1214 pDoc->SetOle2Link( Link() );
1216 sal_Bool bReplace = ( pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
1217 !rSet.Count() ) ) ||
1218 (pReplSet && pReplSet->Count());
1219 bool const bStartUndo = pDoc->GetIDocumentUndoRedo().DoesUndo() && bReplace;
1220 if (bStartUndo)
1222 pDoc->GetIDocumentUndoRedo().StartUndo( UNDO_REPLACE, NULL );
1225 SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt,
1226 pReplSet, *this );
1228 sal_uLong nRet = FindAll( aSwFindParaAttr, nStart, nEnd, eFndRngs, bCancel );
1229 pDoc->SetOle2Link( aLnk );
1230 if( nRet && bReplace )
1231 pDoc->SetModified();
1233 if (bStartUndo)
1235 pDoc->GetIDocumentUndoRedo().EndUndo( UNDO_REPLACE, NULL );
1238 return nRet;
1243 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */