Add a comment to clarify what kind of inputs the class handles
[LibreOffice.git] / sw / source / core / crsr / findattr.cxx
blobf4ff66f81e1b21cbc7d2b3daf3083fc87addb593
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/SearchAlgorithms2.hpp>
22 #include <com/sun/star/util/SearchFlags.hpp>
23 #include <i18nlangtag/languagetag.hxx>
24 #include <i18nutil/searchopt.hxx>
25 #include <osl/diagnose.h>
26 #include <unotools/syslocale.hxx>
27 #include <hintids.hxx>
28 #include <svl/itemiter.hxx>
29 #include <svl/srchitem.hxx>
30 #include <svl/whiter.hxx>
31 #include <editeng/colritem.hxx>
32 #include <editeng/fontitem.hxx>
33 #include <fmtpdsc.hxx>
34 #include <txatbase.hxx>
35 #include <charfmt.hxx>
36 #include <crsrsh.hxx>
37 #include <doc.hxx>
38 #include <IDocumentUndoRedo.hxx>
39 #include <IDocumentState.hxx>
40 #include <swcrsr.hxx>
41 #include <ndtxt.hxx>
42 #include <pamtyp.hxx>
43 #include <txtfrm.hxx>
44 #include <swundo.hxx>
45 #include <optional>
47 #include <algorithm>
48 #include <memory>
50 using namespace ::com::sun::star;
51 using namespace ::com::sun::star::lang;
52 using namespace ::com::sun::star::util;
54 // Special case for SvxFontItem: only compare the name
55 static bool CmpAttr( const SfxPoolItem& rItem1, const SfxPoolItem& rItem2 )
57 switch( rItem1.Which() )
59 case RES_CHRATR_FONT:
60 return rItem1.StaticWhichCast(RES_CHRATR_FONT).GetFamilyName() == rItem2.StaticWhichCast(RES_CHRATR_FONT).GetFamilyName();
62 case RES_CHRATR_COLOR:
63 return rItem1.StaticWhichCast(RES_CHRATR_COLOR).GetValue().IsRGBEqual(rItem2.StaticWhichCast(RES_CHRATR_COLOR).GetValue());
64 case RES_PAGEDESC:
65 ::std::optional<sal_uInt16> const oNumOffset1 = rItem1.StaticWhichCast(RES_PAGEDESC).GetNumOffset();
66 ::std::optional<sal_uInt16> const oNumOffset2 = rItem2.StaticWhichCast(RES_PAGEDESC).GetNumOffset();
68 if (oNumOffset1 != oNumOffset2)
69 return false;
71 return rItem1.StaticWhichCast(RES_PAGEDESC).GetPageDesc() == rItem2.StaticWhichCast(RES_PAGEDESC).GetPageDesc();
73 return rItem1 == rItem2;
76 const SwTextAttr* GetFrwrdTextHint( const SwpHints& rHtsArr, size_t& rPos,
77 sal_Int32 nContentPos )
79 while( rPos < rHtsArr.Count() )
81 const SwTextAttr *pTextHt = rHtsArr.Get( rPos++ );
82 // the start of an attribute has to be in the section
83 if( pTextHt->GetStart() >= nContentPos )
84 return pTextHt; // valid text attribute
86 return nullptr; // invalid text attribute
89 const SwTextAttr* GetBkwrdTextHint( const SwpHints& rHtsArr, size_t& rPos,
90 sal_Int32 nContentPos )
92 while( rPos > 0 )
94 const SwTextAttr *pTextHt = rHtsArr.Get( --rPos );
95 // the start of an attribute has to be in the section
96 if( pTextHt->GetStart() < nContentPos )
97 return pTextHt; // valid text attribute
99 return nullptr; // invalid text attribute
102 static void lcl_SetAttrPam( SwPaM& rPam, sal_Int32 nStart, const sal_Int32* pEnd,
103 const bool bSaveMark )
105 sal_Int32 nContentPos;
106 if( bSaveMark )
107 nContentPos = rPam.GetMark()->GetContentIndex();
108 else
109 nContentPos = rPam.GetPoint()->GetContentIndex();
110 bool bTstEnd = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode();
112 rPam.GetPoint()->SetContent( nStart );
113 rPam.SetMark(); // Point == GetMark
115 // Point points to end of search area or end of attribute
116 if( pEnd )
118 if( bTstEnd && *pEnd > nContentPos )
119 rPam.GetPoint()->SetContent(nContentPos);
120 else
121 rPam.GetPoint()->SetContent(*pEnd);
125 // TODO: provide documentation
126 /** search for a text attribute
128 This function searches in a text node for a given attribute.
129 If that is found then the SwPaM contains the section that surrounds the
130 attribute (w.r.t. the search area).
132 @param rTextNd Text node to search in.
133 @param rPam ???
134 @param rCmpItem ???
135 @param fnMove ???
136 @return Returns <true> if found, <false> otherwise.
138 static bool lcl_SearchAttr( const SwTextNode& rTextNd, SwPaM& rPam,
139 const SfxPoolItem& rCmpItem,
140 SwMoveFnCollection const & fnMove)
142 if ( !rTextNd.HasHints() )
143 return false;
145 const SwTextAttr *pTextHt = nullptr;
146 bool bForward = &fnMove == &fnMoveForward;
147 size_t nPos = bForward ? 0 : rTextNd.GetSwpHints().Count();
148 sal_Int32 nContentPos = rPam.GetPoint()->GetContentIndex();
150 while( nullptr != ( pTextHt=(*fnMove.fnGetHint)(rTextNd.GetSwpHints(),nPos,nContentPos)))
151 if (pTextHt->Which() == rCmpItem.Which())
153 lcl_SetAttrPam( rPam, pTextHt->GetStart(), pTextHt->End(), bForward );
154 return true;
156 return false;
159 namespace {
161 /// search for multiple text attributes
162 struct SwSrchChrAttr
164 sal_uInt16 nWhich;
165 sal_Int32 nStt;
166 sal_Int32 nEnd;
168 SwSrchChrAttr(): nWhich(0), nStt(0), nEnd(0) {}
170 SwSrchChrAttr( const SfxPoolItem& rItem,
171 sal_Int32 nStart, sal_Int32 nAnyEnd )
172 : nWhich( rItem.Which() ), nStt( nStart ), nEnd( nAnyEnd )
176 class SwAttrCheckArr
178 SwSrchChrAttr *m_pFindArr, *m_pStackArr;
179 sal_Int32 m_nNodeStart;
180 sal_Int32 m_nNodeEnd;
181 sal_uInt16 m_nArrStart, m_nArrLen;
182 sal_uInt16 m_nFound, m_nStackCount;
183 SfxItemSet m_aComapeSet;
184 bool m_bNoColls;
185 bool m_bForward;
187 public:
188 SwAttrCheckArr( const SfxItemSet& rSet, bool bForward, bool bNoCollections );
189 ~SwAttrCheckArr();
191 void SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam );
193 /// how many attributes are there in total?
194 sal_uInt16 Count() const { return m_aComapeSet.Count(); }
195 bool Found() const { return m_nFound == m_aComapeSet.Count(); }
196 bool CheckStack();
198 sal_Int32 Start() const;
199 sal_Int32 End() const;
201 sal_Int32 GetNdStt() const { return m_nNodeStart; }
202 sal_Int32 GetNdEnd() const { return m_nNodeEnd; }
204 bool SetAttrFwd( const SwTextAttr& rAttr );
205 bool SetAttrBwd( const SwTextAttr& rAttr );
210 SwAttrCheckArr::SwAttrCheckArr( const SfxItemSet& rSet, bool bFwd,
211 bool bNoCollections )
212 : m_nNodeStart(0)
213 , m_nNodeEnd(0)
214 , m_nFound(0)
215 , m_nStackCount(0)
216 , m_aComapeSet( *rSet.GetPool(), svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END-1> )
217 , m_bNoColls(bNoCollections)
218 , m_bForward(bFwd)
220 m_aComapeSet.Put( rSet, false );
222 // determine area of Fnd/Stack array (Min/Max)
223 sal_uInt16 nMinUsedWhichID(0);
224 sal_uInt16 nMaxUsedWhichID(0);
226 if (0 != m_aComapeSet.Count())
228 nMinUsedWhichID = 5000; // SFX_WHICH_MAX+1;
229 for (SfxItemIter aIter(m_aComapeSet); !aIter.IsAtEnd(); aIter.NextItem())
231 const sal_uInt16 nCurrentWhich(aIter.GetCurWhich());
232 if (SfxItemPool::IsSlot(nCurrentWhich))
233 continue;
234 nMinUsedWhichID = std::min(nMinUsedWhichID, nCurrentWhich);
235 nMaxUsedWhichID = std::max(nMaxUsedWhichID, nCurrentWhich);
238 if (nMinUsedWhichID > nMaxUsedWhichID)
239 nMinUsedWhichID = nMaxUsedWhichID = 0;
242 m_nArrStart = nMinUsedWhichID;//m_aComapeSet.GetWhichByOffset( aIter.GetFirstPos() );
243 m_nArrLen = nMaxUsedWhichID - nMinUsedWhichID + 1;//m_aComapeSet.GetWhichByOffset( aIter.GetLastPos() ) - m_nArrStart+1;
245 char* pFndChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
246 char* pStackChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
248 m_pFindArr = reinterpret_cast<SwSrchChrAttr*>(pFndChar);
249 m_pStackArr = reinterpret_cast<SwSrchChrAttr*>(pStackChar);
252 SwAttrCheckArr::~SwAttrCheckArr()
254 delete[] reinterpret_cast<char*>(m_pFindArr);
255 delete[] reinterpret_cast<char*>(m_pStackArr);
258 void SwAttrCheckArr::SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam )
260 std::fill(m_pFindArr, m_pFindArr + m_nArrLen, SwSrchChrAttr());
261 std::fill(m_pStackArr, m_pStackArr + m_nArrLen, SwSrchChrAttr());
262 m_nFound = 0;
263 m_nStackCount = 0;
265 if( m_bForward )
267 m_nNodeStart = rPam.GetPoint()->GetContentIndex();
268 m_nNodeEnd = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode()
269 ? rPam.GetMark()->GetContentIndex()
270 : rTextNd.GetText().getLength();
272 else
274 m_nNodeEnd = rPam.GetPoint()->GetContentIndex();
275 m_nNodeStart = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode()
276 ? rPam.GetMark()->GetContentIndex()
277 : 0;
280 if( m_bNoColls && !rTextNd.HasSwAttrSet() )
281 return ;
283 const SfxItemSet& rSet = rTextNd.GetSwAttrSet();
285 SfxItemIter aIter( m_aComapeSet );
286 const SfxPoolItem* pItem = aIter.GetCurItem();
287 const SfxPoolItem* pFndItem;
288 sal_uInt16 nWhich;
292 if( IsInvalidItem( pItem ) )
294 nWhich = aIter.GetCurWhich();
295 if( RES_TXTATR_END <= nWhich )
296 break; // end of text attributes
298 if( SfxItemState::SET == rSet.GetItemState( nWhich, !m_bNoColls, &pFndItem )
299 && !CmpAttr( *pFndItem, rSet.GetPool()->GetUserOrPoolDefaultItem( nWhich ) ))
301 m_pFindArr[ nWhich - m_nArrStart ] =
302 SwSrchChrAttr( *pFndItem, m_nNodeStart, m_nNodeEnd );
303 m_nFound++;
306 else
308 nWhich = pItem->Which();
309 if( RES_TXTATR_END <= nWhich )
310 break; // end of text attributes
312 if( CmpAttr( rSet.Get( nWhich, !m_bNoColls ), *pItem ) )
314 m_pFindArr[ nWhich - m_nArrStart ] =
315 SwSrchChrAttr( *pItem, m_nNodeStart, m_nNodeEnd );
316 m_nFound++;
320 pItem = aIter.NextItem();
321 } while (pItem);
324 static bool
325 lcl_IsAttributeIgnorable(sal_Int32 const nNdStart, sal_Int32 const nNdEnd,
326 SwSrchChrAttr const& rTmp)
328 // #i115528#: if there is a paragraph attribute, it has been added by the
329 // SwAttrCheckArr ctor, and nFound is 1.
330 // if the paragraph is entirely covered by hints that override the paragraph
331 // attribute, then this function must find an attribute to decrement nFound!
332 // so check for an empty search range, let attributes that start/end there
333 // cover it, and hope for the best...
334 return ((nNdEnd == nNdStart)
335 ? ((rTmp.nEnd < nNdStart) || (nNdEnd < rTmp.nStt))
336 : ((rTmp.nEnd <= nNdStart) || (nNdEnd <= rTmp.nStt)));
339 bool SwAttrCheckArr::SetAttrFwd( const SwTextAttr& rAttr )
341 SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
343 // ignore all attributes not in search range
344 if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
346 return Found();
349 const SfxPoolItem* pItem;
350 // here we explicitly also search in character templates
351 sal_uInt16 nWhch = rAttr.Which();
352 std::optional<SfxWhichIter> oIter;
353 const SfxPoolItem* pTmpItem = nullptr;
354 const SfxItemSet* pSet = nullptr;
355 if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
357 if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
358 return Found();
359 pTmpItem = nullptr;
360 pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
361 if ( pSet )
363 oIter.emplace( *pSet );
364 nWhch = oIter->FirstWhich();
365 while( nWhch &&
366 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
367 nWhch = oIter->NextWhich();
368 if( !nWhch )
369 pTmpItem = nullptr;
372 else
373 pTmpItem = &rAttr.GetAttr();
375 while( pTmpItem )
377 SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
378 if( SfxItemState::INVALID == eState || SfxItemState::SET == eState )
380 sal_uInt16 n;
381 SwSrchChrAttr* pCmp;
383 // first delete all up to start position that are already invalid
384 SwSrchChrAttr* pArrPtr;
385 if( m_nFound )
386 for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen;
387 ++n, ++pArrPtr )
388 if( pArrPtr->nWhich && pArrPtr->nEnd <= aTmp.nStt )
390 pArrPtr->nWhich = 0; // deleted
391 m_nFound--;
394 // delete all up to start position that are already invalid and
395 // move all "open" ones (= stick out over start position) from stack
396 // into FndSet
397 if( m_nStackCount )
398 for( pArrPtr = m_pStackArr, n=0; n < m_nArrLen; ++n, ++pArrPtr )
400 if( !pArrPtr->nWhich )
401 continue;
403 if( pArrPtr->nEnd <= aTmp.nStt )
405 pArrPtr->nWhich = 0; // deleted
406 if( !--m_nStackCount )
407 break;
409 else if( pArrPtr->nStt <= aTmp.nStt )
411 pCmp = &m_pFindArr[ n ];
412 if( pCmp->nWhich )
414 if( pCmp->nEnd < pArrPtr->nEnd ) // extend
415 pCmp->nEnd = pArrPtr->nEnd;
417 else
419 *pCmp = *pArrPtr;
420 m_nFound++;
422 pArrPtr->nWhich = 0;
423 if( !--m_nStackCount )
424 break;
428 bool bContinue = false;
430 if( SfxItemState::INVALID == eState )
432 // Will the attribute become valid?
433 if( !CmpAttr( m_aComapeSet.GetPool()->GetUserOrPoolDefaultItem( nWhch ),
434 *pTmpItem ))
436 // search attribute and extend if needed
437 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
438 if( !pCmp->nWhich )
440 *pCmp = aTmp; // not found, insert
441 m_nFound++;
443 else if( pCmp->nEnd < aTmp.nEnd ) // extend?
444 pCmp->nEnd = aTmp.nEnd;
446 bContinue = true;
449 // Will the attribute become valid?
450 else if( CmpAttr( *pItem, *pTmpItem ) )
452 m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
453 ++m_nFound;
454 bContinue = true;
457 // then is has to go on the stack
458 if( !bContinue )
460 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
461 if (pCmp->nWhich )
463 // exists on stack, only if it is even bigger
464 if( pCmp->nEnd > aTmp.nEnd )
466 OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich,
467 "slot on stack is still in use" );
469 if( aTmp.nStt <= pCmp->nStt )
470 pCmp->nStt = aTmp.nEnd;
471 else
472 pCmp->nEnd = aTmp.nStt;
474 m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
475 m_nStackCount++;
477 pCmp->nWhich = 0;
478 m_nFound--;
482 if( oIter )
484 assert(pSet && "otherwise no oIter");
485 nWhch = oIter->NextWhich();
486 while( nWhch &&
487 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
488 nWhch = oIter->NextWhich();
489 if( !nWhch )
490 break;
492 else
493 break;
495 oIter.reset();
496 return Found();
499 bool SwAttrCheckArr::SetAttrBwd( const SwTextAttr& rAttr )
501 SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
503 // ignore all attributes not in search range
504 if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
506 return Found();
509 const SfxPoolItem* pItem;
510 // here we explicitly also search in character templates
511 sal_uInt16 nWhch = rAttr.Which();
512 std::optional<SfxWhichIter> oIter;
513 const SfxPoolItem* pTmpItem = nullptr;
514 const SfxItemSet* pSet = nullptr;
515 if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
517 if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
518 return Found();
520 pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
521 if ( pSet )
523 oIter.emplace( *pSet );
524 nWhch = oIter->FirstWhich();
525 while( nWhch &&
526 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
527 nWhch = oIter->NextWhich();
528 if( !nWhch )
529 pTmpItem = nullptr;
532 else
533 pTmpItem = &rAttr.GetAttr();
535 while( pTmpItem )
537 SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
538 if( SfxItemState::INVALID == eState || SfxItemState::SET == eState )
540 sal_uInt16 n;
541 SwSrchChrAttr* pCmp;
543 // first delete all up to start position that are already invalid
544 SwSrchChrAttr* pArrPtr;
545 if( m_nFound )
546 for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
547 if( pArrPtr->nWhich && pArrPtr->nStt >= aTmp.nEnd )
549 pArrPtr->nWhich = 0; // deleted
550 m_nFound--;
553 // delete all up to start position that are already invalid and
554 // move all "open" ones (= stick out over start position) from stack
555 // into FndSet
556 if( m_nStackCount )
557 for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
559 if( !pArrPtr->nWhich )
560 continue;
562 if( pArrPtr->nStt >= aTmp.nEnd )
564 pArrPtr->nWhich = 0; // deleted
565 if( !--m_nStackCount )
566 break;
568 else if( pArrPtr->nEnd >= aTmp.nEnd )
570 pCmp = &m_pFindArr[ n ];
571 if( pCmp->nWhich )
573 if( pCmp->nStt > pArrPtr->nStt ) // extend
574 pCmp->nStt = pArrPtr->nStt;
576 else
578 *pCmp = *pArrPtr;
579 m_nFound++;
581 pArrPtr->nWhich = 0;
582 if( !--m_nStackCount )
583 break;
587 bool bContinue = false;
588 if( SfxItemState::INVALID == eState )
590 // Will the attribute become valid?
591 if( !CmpAttr( m_aComapeSet.GetPool()->GetUserOrPoolDefaultItem( nWhch ),
592 *pTmpItem ) )
594 // search attribute and extend if needed
595 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
596 if( !pCmp->nWhich )
598 *pCmp = aTmp; // not found, insert
599 m_nFound++;
601 else if( pCmp->nStt > aTmp.nStt ) // extend?
602 pCmp->nStt = aTmp.nStt;
604 bContinue = true;
607 // Will the attribute become valid?
608 else if( CmpAttr( *pItem, *pTmpItem ))
610 m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
611 ++m_nFound;
612 bContinue = true;
615 // then is has to go on the stack
616 if( !bContinue )
618 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
619 if( pCmp->nWhich )
621 // exists on stack, only if it is even bigger
622 if( pCmp->nStt < aTmp.nStt )
624 OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich,
625 "slot on stack is still in use" );
627 if( aTmp.nEnd <= pCmp->nEnd )
628 pCmp->nEnd = aTmp.nStt;
629 else
630 pCmp->nStt = aTmp.nEnd;
632 m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
633 m_nStackCount++;
635 pCmp->nWhich = 0;
636 m_nFound--;
640 if( oIter )
642 assert(pSet && "otherwise no oIter");
643 nWhch = oIter->NextWhich();
644 while( nWhch &&
645 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
646 nWhch = oIter->NextWhich();
647 if( !nWhch )
648 break;
650 else
651 break;
653 oIter.reset();
654 return Found();
657 sal_Int32 SwAttrCheckArr::Start() const
659 sal_Int32 nStart = m_nNodeStart;
660 SwSrchChrAttr* pArrPtr = m_pFindArr;
661 for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
662 if( pArrPtr->nWhich && pArrPtr->nStt > nStart )
663 nStart = pArrPtr->nStt;
665 return nStart;
668 sal_Int32 SwAttrCheckArr::End() const
670 SwSrchChrAttr* pArrPtr = m_pFindArr;
671 sal_Int32 nEnd = m_nNodeEnd;
672 for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
673 if( pArrPtr->nWhich && pArrPtr->nEnd < nEnd )
674 nEnd = pArrPtr->nEnd;
676 return nEnd;
679 bool SwAttrCheckArr::CheckStack()
681 if( !m_nStackCount )
682 return false;
684 sal_uInt16 n;
685 const sal_Int32 nSttPos = Start();
686 const sal_Int32 nEndPos = End();
687 SwSrchChrAttr* pArrPtr;
688 for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
690 if( !pArrPtr->nWhich )
691 continue;
693 if( m_bForward ? pArrPtr->nEnd <= nSttPos : pArrPtr->nStt >= nEndPos )
695 pArrPtr->nWhich = 0; // deleted
696 if( !--m_nStackCount )
697 return m_nFound == m_aComapeSet.Count();
699 else if( m_bForward ? pArrPtr->nStt < nEndPos : pArrPtr->nEnd > nSttPos )
701 // move all "open" ones (= stick out over start position) into FndSet
702 OSL_ENSURE( !m_pFindArr[ n ].nWhich, "slot in array is already in use" );
703 m_pFindArr[ n ] = *pArrPtr;
704 pArrPtr->nWhich = 0;
705 m_nFound++;
706 if( !--m_nStackCount )
707 return m_nFound == m_aComapeSet.Count();
710 return m_nFound == m_aComapeSet.Count();
713 static bool lcl_SearchForward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
714 SwPaM& rPam )
716 sal_Int32 nEndPos;
717 rCmpArr.SetNewSet( rTextNd, rPam );
718 if( !rTextNd.HasHints() )
720 if( !rCmpArr.Found() )
721 return false;
722 nEndPos = rCmpArr.GetNdEnd();
723 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
724 return true;
727 const SwpHints& rHtArr = rTextNd.GetSwpHints();
728 const SwTextAttr* pAttr;
729 size_t nPos = 0;
731 // if everything is already there then check with which it will be ended
732 if( rCmpArr.Found() )
734 for( ; nPos < rHtArr.Count(); ++nPos )
736 pAttr = rHtArr.Get( nPos );
737 if( !rCmpArr.SetAttrFwd( *pAttr ) )
739 if( rCmpArr.GetNdStt() < pAttr->GetStart() )
741 // found end
742 auto nTmpStart = pAttr->GetStart();
743 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(),
744 &nTmpStart, true );
745 return true;
747 // continue search
748 break;
752 if( nPos == rHtArr.Count() && rCmpArr.Found() )
754 // found
755 nEndPos = rCmpArr.GetNdEnd();
756 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
757 return true;
761 sal_Int32 nSttPos;
762 for( ; nPos < rHtArr.Count(); ++nPos )
764 pAttr = rHtArr.Get( nPos );
765 if( rCmpArr.SetAttrFwd( *pAttr ) )
767 // Do multiple start at that position? Do also check those:
768 nSttPos = pAttr->GetStart();
769 while( ++nPos < rHtArr.Count() )
771 pAttr = rHtArr.Get( nPos );
772 if( nSttPos != pAttr->GetStart() || !rCmpArr.SetAttrFwd( *pAttr ) )
773 break;
776 if( !rCmpArr.Found() )
777 continue;
779 // then we have our search area
780 nSttPos = rCmpArr.Start();
781 nEndPos = rCmpArr.End();
782 if( nSttPos > nEndPos )
783 return false;
785 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
786 return true;
790 if( !rCmpArr.CheckStack() )
791 return false;
792 nSttPos = rCmpArr.Start();
793 nEndPos = rCmpArr.End();
794 if( nSttPos > nEndPos )
795 return false;
797 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
798 return true;
801 static bool lcl_SearchBackward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
802 SwPaM& rPam )
804 sal_Int32 nEndPos;
805 rCmpArr.SetNewSet( rTextNd, rPam );
806 if( !rTextNd.HasHints() )
808 if( !rCmpArr.Found() )
809 return false;
810 nEndPos = rCmpArr.GetNdEnd();
811 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
812 return true;
815 const SwpHints& rHtArr = rTextNd.GetSwpHints();
816 const SwTextAttr* pAttr;
817 size_t nPos = rHtArr.Count();
818 sal_Int32 nSttPos;
820 // if everything is already there then check with which it will be ended
821 if( rCmpArr.Found() )
823 while( nPos )
825 pAttr = rHtArr.GetSortedByEnd( --nPos );
826 if( !rCmpArr.SetAttrBwd( *pAttr ) )
828 nSttPos = pAttr->GetAnyEnd();
829 if( nSttPos < rCmpArr.GetNdEnd() )
831 // found end
832 nEndPos = rCmpArr.GetNdEnd();
833 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
834 return true;
837 // continue search
838 break;
842 if( !nPos && rCmpArr.Found() )
844 // found
845 nEndPos = rCmpArr.GetNdEnd();
846 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
847 return true;
851 while( nPos )
853 pAttr = rHtArr.GetSortedByEnd( --nPos );
854 if( rCmpArr.SetAttrBwd( *pAttr ) )
856 // Do multiple start at that position? Do also check those:
857 if( nPos )
859 nEndPos = pAttr->GetAnyEnd();
860 while( --nPos )
862 pAttr = rHtArr.GetSortedByEnd( nPos );
863 if( nEndPos != pAttr->GetAnyEnd() || !rCmpArr.SetAttrBwd( *pAttr ) )
864 break;
867 if( !rCmpArr.Found() )
868 continue;
870 // then we have our search area
871 nSttPos = rCmpArr.Start();
872 nEndPos = rCmpArr.End();
873 if( nSttPos > nEndPos )
874 return false;
876 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
877 return true;
881 if( !rCmpArr.CheckStack() )
882 return false;
883 nSttPos = rCmpArr.Start();
884 nEndPos = rCmpArr.End();
885 if( nSttPos > nEndPos )
886 return false;
888 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
889 return true;
892 static bool lcl_Search( const SwContentNode& rCNd, const SfxItemSet& rCmpSet, bool bNoColls )
894 // search only hard attribution?
895 if( bNoColls && !rCNd.HasSwAttrSet() )
896 return false;
898 const SfxItemSet& rNdSet = rCNd.GetSwAttrSet();
899 SfxItemIter aIter( rCmpSet );
900 const SfxPoolItem* pItem = aIter.GetCurItem();
901 const SfxPoolItem* pNdItem;
902 sal_uInt16 nWhich;
906 if( IsInvalidItem( pItem ))
908 nWhich = aIter.GetCurWhich();
909 if( SfxItemState::SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
910 || CmpAttr( *pNdItem, rNdSet.GetPool()->GetUserOrPoolDefaultItem( nWhich ) ))
911 return false;
913 else
915 nWhich = pItem->Which();
917 if( !CmpAttr( rNdSet.Get( nWhich, !bNoColls ), *pItem ))
918 return false;
921 pItem = aIter.NextItem();
922 } while (pItem);
923 return true; // found
926 namespace sw {
928 bool FindAttrImpl(SwPaM & rSearchPam,
929 const SfxPoolItem& rAttr, SwMoveFnCollection const & fnMove,
930 const SwPaM & rRegion, bool bInReadOnly,
931 SwRootFrame const*const pLayout)
933 // determine which attribute is searched:
934 const sal_uInt16 nWhich = rAttr.Which();
935 bool bCharAttr = isCHRATR(nWhich) || isTXTATR(nWhich);
936 assert(isTXTATR(nWhich)); // sw_redlinehide: only works for non-formatting hints such as needed in UpdateFields; use FindAttrsImpl for others
938 std::optional<SwPaM> oPam;
939 sw::MakeRegion(fnMove, rRegion, oPam);
941 bool bFound = false;
942 bool bFirst = true;
943 const bool bSrchForward = &fnMove == &fnMoveForward;
944 SwContentNode * pNode;
946 // if at beginning/end then move it out of the node
947 if( bSrchForward
948 ? oPam->GetPoint()->GetContentIndex() == oPam->GetPointContentNode()->Len()
949 : !oPam->GetPoint()->GetContentIndex() )
951 if( !(*fnMove.fnPos)( oPam->GetPoint(), false ))
953 return false;
955 SwContentNode *pNd = oPam->GetPointContentNode();
956 oPam->GetPoint()->SetContent( bSrchForward ? 0 : pNd->Len() );
959 while (nullptr != (pNode = ::GetNode(*oPam, bFirst, fnMove, bInReadOnly, pLayout)))
961 if( bCharAttr )
963 if( !pNode->IsTextNode() ) // CharAttr are only in text nodes
964 continue;
966 SwTextFrame const*const pFrame(pLayout
967 ? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
968 : nullptr);
969 if (pFrame)
971 SwTextNode const* pAttrNode(nullptr);
972 SwTextAttr const* pAttr(nullptr);
973 if (bSrchForward)
975 sw::MergedAttrIter iter(*pFrame);
978 pAttr = iter.NextAttr(&pAttrNode);
980 while (pAttr
981 && (pAttrNode->GetIndex() < oPam->GetPoint()->GetNodeIndex()
982 || (pAttrNode->GetIndex() == oPam->GetPoint()->GetNodeIndex()
983 && pAttr->GetStart() < oPam->GetPoint()->GetContentIndex())
984 || pAttr->Which() != nWhich));
986 else
988 sw::MergedAttrIterReverse iter(*pFrame);
991 pAttr = iter.PrevAttr(&pAttrNode);
993 while (pAttr
994 && (oPam->GetPoint()->GetNodeIndex() < pAttrNode->GetIndex()
995 || (oPam->GetPoint()->GetNodeIndex() == pAttrNode->GetIndex()
996 && oPam->GetPoint()->GetContentIndex() <= pAttr->GetStart())
997 || pAttr->Which() != nWhich));
999 if (pAttr)
1001 assert(pAttrNode);
1002 oPam->GetPoint()->Assign(*pAttrNode);
1003 lcl_SetAttrPam(*oPam, pAttr->GetStart(), pAttr->End(), bSrchForward);
1004 bFound = true;
1005 break;
1008 else if (!pLayout && pNode->GetTextNode()->HasHints() &&
1009 lcl_SearchAttr(*pNode->GetTextNode(), *oPam, rAttr, fnMove))
1011 bFound = true;
1013 if (bFound)
1015 // set to the values of the attribute
1016 rSearchPam.SetMark();
1017 *rSearchPam.GetPoint() = *oPam->GetPoint();
1018 *rSearchPam.GetMark() = *oPam->GetMark();
1019 break;
1021 else if (isTXTATR(nWhich))
1022 continue;
1025 #if 0
1026 // no hard attribution, so check if node was asked for this attr before
1027 if( !pNode->HasSwAttrSet() )
1029 SwFormat* pTmpFormat = pNode->GetFormatColl();
1030 if( !aFormatArr.insert( pTmpFormat ).second )
1031 continue; // collection was requested earlier
1034 if( SfxItemState::SET == pNode->GetSwAttrSet().GetItemState( nWhich,
1035 true, &pItem ))
1037 // FORWARD: SPoint at the end, GetMark at the beginning of the node
1038 // BACKWARD: SPoint at the beginning, GetMark at the end of the node
1039 // always: incl. start and incl. end
1040 *rSearchPam.GetPoint() = *pPam->GetPoint();
1041 rSearchPam.SetMark();
1042 rSearchPam.GetPoint()->SetContent(pNode->Len());
1043 bFound = true;
1044 break;
1046 #endif
1049 // if backward search, switch point and mark
1050 if( bFound && !bSrchForward )
1051 rSearchPam.Exchange();
1053 return bFound;
1056 } // namespace sw
1058 typedef bool (*FnSearchAttr)( const SwTextNode&, SwAttrCheckArr&, SwPaM& );
1060 static bool FindAttrsImpl(SwPaM & rSearchPam,
1061 const SfxItemSet& rSet, bool bNoColls, SwMoveFnCollection const & fnMove,
1062 const SwPaM & rRegion, bool bInReadOnly, bool bMoveFirst,
1063 SwRootFrame const*const pLayout)
1065 std::optional<SwPaM> oPam;
1066 sw::MakeRegion(fnMove, rRegion, oPam);
1068 bool bFound = false;
1069 bool bFirst = true;
1070 const bool bSrchForward = &fnMove == &fnMoveForward;
1071 SwContentNode * pNode;
1072 o3tl::sorted_vector<SwFormat*> aFormatArr;
1074 // check which text/char attributes are searched
1075 SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls );
1076 SfxItemSetFixed<RES_PARATR_BEGIN, RES_GRFATR_END-1> aOtherSet( rSearchPam.GetDoc().GetAttrPool() );
1077 aOtherSet.Put( rSet, false ); // got all invalid items
1079 FnSearchAttr fnSearch = bSrchForward
1080 ? (&::lcl_SearchForward)
1081 : (&::lcl_SearchBackward);
1083 // if at beginning/end then move it out of the node
1084 if( bMoveFirst &&
1085 ( bSrchForward
1086 ? oPam->GetPoint()->GetContentIndex() == oPam->GetPointContentNode()->Len()
1087 : !oPam->GetPoint()->GetContentIndex() ) )
1089 if( !(*fnMove.fnPos)( oPam->GetPoint(), false ))
1091 return false;
1093 SwContentNode *pNd = oPam->GetPointContentNode();
1094 oPam->GetPoint()->SetContent( bSrchForward ? 0 : pNd->Len() );
1097 while (nullptr != (pNode = ::GetNode(*oPam, bFirst, fnMove, bInReadOnly, pLayout)))
1099 SwTextFrame const*const pFrame(pLayout && pNode->IsTextNode()
1100 ? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
1101 : nullptr);
1102 assert(!pLayout || !pNode->IsTextNode() || pFrame);
1103 // sw_redlinehide: it's apparently not possible to find break items
1104 // with the UI, so checking one node is enough
1105 SwContentNode const& rPropsNode(*(pFrame
1106 ? pFrame->GetTextNodeForParaProps()
1107 : pNode));
1109 if( aCmpArr.Count() )
1111 if( !pNode->IsTextNode() ) // CharAttr are only in text nodes
1112 continue;
1114 if (aOtherSet.Count() &&
1115 !lcl_Search(rPropsNode, aOtherSet, bNoColls))
1117 continue;
1119 sw::MergedPara const*const pMergedPara(pFrame ? pFrame->GetMergedPara() : nullptr);
1120 if (pMergedPara)
1122 SwPosition const& rStart(*oPam->Start());
1123 SwPosition const& rEnd(*oPam->End());
1124 // no extents? fall back to searching index 0 of propsnode
1125 // to find its node items
1126 if (pMergedPara->extents.empty())
1128 if (rStart.GetNodeIndex() <= rPropsNode.GetIndex()
1129 && rPropsNode.GetIndex() <= rEnd.GetNodeIndex())
1131 SwPaM tmp(rPropsNode, 0, rPropsNode, 0);
1132 bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, tmp);
1133 if (bFound)
1135 *oPam = tmp;
1139 else
1141 // iterate the extents, and intersect with input pPam:
1142 // the found ranges should never include delete redlines
1143 // so that subsequent Replace will not affect them
1144 for (size_t i = 0; i < pMergedPara->extents.size(); ++i)
1146 auto const rExtent(pMergedPara->extents[bSrchForward
1148 : pMergedPara->extents.size() - i - 1]);
1149 if (rExtent.pNode->GetIndex() < rStart.GetNodeIndex()
1150 || rEnd.GetNodeIndex() < rExtent.pNode->GetIndex())
1152 continue;
1154 sal_Int32 const nStart(rExtent.pNode == &rStart.GetNode()
1155 ? rStart.GetContentIndex()
1156 : 0);
1157 if (rExtent.nEnd <= nStart)
1159 continue;
1161 sal_Int32 const nEnd(rExtent.pNode == &rEnd.GetNode()
1162 ? rEnd.GetContentIndex()
1163 : rExtent.pNode->Len());
1164 if (nEnd < rExtent.nStart
1165 || (nStart != nEnd && nEnd == rExtent.nStart))
1167 continue;
1169 SwPaM tmp(*rExtent.pNode, std::max(nStart, rExtent.nStart),
1170 *rExtent.pNode, std::min(nEnd, rExtent.nEnd));
1171 tmp.Normalize(bSrchForward);
1172 bFound = (*fnSearch)(*rExtent.pNode, aCmpArr, tmp);
1173 if (bFound)
1175 *oPam = tmp;
1176 break;
1181 else
1183 bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, *oPam);
1185 if (bFound)
1187 // set to the values of the attribute
1188 rSearchPam.SetMark();
1189 *rSearchPam.GetPoint() = *oPam->GetPoint();
1190 *rSearchPam.GetMark() = *oPam->GetMark();
1191 break;
1193 continue; // text attribute
1196 if( !aOtherSet.Count() )
1197 continue;
1199 // no hard attribution, so check if node was asked for this attr before
1200 // (as an optimisation)
1201 if (!rPropsNode.HasSwAttrSet())
1203 SwFormat* pTmpFormat = rPropsNode.GetFormatColl();
1204 if( !aFormatArr.insert( pTmpFormat ).second )
1205 continue; // collection was requested earlier
1208 if (lcl_Search(rPropsNode, aOtherSet, bNoColls))
1210 // FORWARD: SPoint at the end, GetMark at the beginning of the node
1211 // BACKWARD: SPoint at the beginning, GetMark at the end of the node
1212 if (pFrame)
1214 *rSearchPam.GetPoint() = *oPam->GetPoint();
1215 rSearchPam.SetMark();
1216 *rSearchPam.GetMark() = pFrame->MapViewToModelPos(
1217 TextFrameIndex(bSrchForward ? pFrame->GetText().getLength() : 0));
1219 else
1221 *rSearchPam.GetPoint() = *oPam->GetPoint();
1222 rSearchPam.SetMark();
1223 if (bSrchForward)
1225 rSearchPam.GetPoint()->SetContent(pNode->Len());
1227 else
1229 rSearchPam.GetPoint()->SetContent(0);
1232 bFound = true;
1233 break;
1237 // in search direction, mark precedes point, because the next iteration
1238 // starts at point
1239 if (bFound)
1241 rSearchPam.Normalize(!bSrchForward);
1244 return bFound;
1247 namespace {
1249 /// parameters for search for attributes
1250 struct SwFindParaAttr : public SwFindParas
1252 bool m_bNoCollection;
1253 const SfxItemSet *pSet, *pReplSet;
1254 const i18nutil::SearchOptions2 *pSearchOpt;
1255 SwCursor& m_rCursor;
1256 SwRootFrame const* m_pLayout;
1257 std::unique_ptr<utl::TextSearch> pSText;
1259 SwFindParaAttr( const SfxItemSet& rSet, bool bNoCollection,
1260 const i18nutil::SearchOptions2* pOpt, const SfxItemSet* pRSet,
1261 SwCursor& rCursor, SwRootFrame const*const pLayout)
1262 : m_bNoCollection(bNoCollection)
1263 , pSet( &rSet )
1264 , pReplSet( pRSet )
1265 , pSearchOpt( pOpt )
1266 , m_rCursor(rCursor)
1267 , m_pLayout(pLayout)
1270 virtual ~SwFindParaAttr() {}
1272 virtual int DoFind(SwPaM &, SwMoveFnCollection const &, const SwPaM &, bool bInReadOnly,
1273 std::unique_ptr<SvxSearchItem>& xSearchItem) override;
1274 virtual bool IsReplaceMode() const override;
1279 int SwFindParaAttr::DoFind(SwPaM & rCursor, SwMoveFnCollection const & fnMove,
1280 const SwPaM & rRegion, bool bInReadOnly,
1281 std::unique_ptr<SvxSearchItem>& xSearchItem)
1283 // replace string (only if text given and search is not parameterized)?
1284 bool bReplaceText = pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
1285 !pSet->Count() );
1286 bool bReplaceAttr = pReplSet && pReplSet->Count();
1287 bool bMoveFirst = !bReplaceAttr;
1288 if( bInReadOnly && (bReplaceAttr || bReplaceText ))
1289 bInReadOnly = false;
1291 // We search for attributes, should we search for text as well?
1293 SwPaM aRegion( *rRegion.GetMark(), *rRegion.GetPoint() );
1294 SwPaM* pTextRegion = &aRegion;
1295 SwPaM aSrchPam( *rCursor.GetPoint() );
1297 while( true )
1299 if( pSet->Count() ) // any attributes?
1301 // first attributes
1302 if (!FindAttrsImpl(aSrchPam, *pSet, m_bNoCollection, fnMove, aRegion, bInReadOnly, bMoveFirst, m_pLayout))
1303 return FIND_NOT_FOUND;
1304 bMoveFirst = true;
1306 if( !pSearchOpt )
1307 break; // ok, only attributes, so found
1309 pTextRegion = &aSrchPam;
1311 else if( !pSearchOpt )
1312 return FIND_NOT_FOUND;
1314 // then search in text of it
1315 if( !pSText )
1317 i18nutil::SearchOptions2 aTmp( *pSearchOpt );
1319 // search in selection
1320 aTmp.searchFlag |= (SearchFlags::REG_NOT_BEGINOFLINE |
1321 SearchFlags::REG_NOT_ENDOFLINE);
1323 aTmp.Locale = SvtSysLocale().GetLanguageTag().getLocale();
1325 pSText.reset( new utl::TextSearch( aTmp ) );
1328 // TODO: searching for attributes in Outliner text?!
1330 // continue search in correct section (pTextRegion)
1331 if (sw::FindTextImpl(aSrchPam, *pSearchOpt, false/*bSearchInNotes*/, *pSText, fnMove, *pTextRegion, bInReadOnly, m_pLayout, xSearchItem) &&
1332 *aSrchPam.GetMark() != *aSrchPam.GetPoint() )
1333 break; // found
1334 else if( !pSet->Count() )
1335 return FIND_NOT_FOUND; // only text and nothing found
1337 *aRegion.GetMark() = *aSrchPam.GetPoint();
1340 *rCursor.GetPoint() = *aSrchPam.GetPoint();
1341 rCursor.SetMark();
1342 *rCursor.GetMark() = *aSrchPam.GetMark();
1345 if( bReplaceText )
1347 const bool bRegExp(
1348 SearchAlgorithms2::REGEXP == pSearchOpt->AlgorithmType2);
1349 SwPosition& rSttCntPos = *rCursor.Start();
1350 const sal_Int32 nSttCnt = rSttCntPos.GetContentIndex();
1352 // add to shell-cursor-ring so that the regions will be moved eventually
1353 SwPaM* pPrevRing(nullptr);
1354 if( bRegExp )
1356 pPrevRing = const_cast<SwPaM &>(rRegion).GetPrev();
1357 const_cast<SwPaM &>(rRegion).GetRingContainer().merge( m_rCursor.GetRingContainer() );
1360 std::optional<OUString> xRepl;
1361 if (bRegExp)
1362 xRepl = sw::ReplaceBackReferences(*pSearchOpt, &rCursor, m_pLayout);
1363 sw::ReplaceImpl(rCursor,
1364 xRepl ? *xRepl : pSearchOpt->replaceString, bRegExp,
1365 m_rCursor.GetDoc(), m_pLayout);
1367 m_rCursor.SaveTableBoxContent( rCursor.GetPoint() );
1369 if( bRegExp )
1371 // and remove region again
1372 SwPaM* p;
1373 SwPaM* pNext = const_cast<SwPaM*>(&rRegion);
1374 do {
1375 p = pNext;
1376 pNext = p->GetNext();
1377 p->MoveTo(const_cast<SwPaM*>(&rRegion));
1378 } while( p != pPrevRing );
1380 rSttCntPos.SetContent(nSttCnt);
1383 if( bReplaceAttr )
1385 // is the selection still existent?
1386 // all searched attributes are reset to default if
1387 // they are not in ReplaceSet
1388 if( !pSet->Count() )
1390 rCursor.GetDoc().getIDocumentContentOperations().InsertItemSet(
1391 rCursor, *pReplSet, SetAttrMode::DEFAULT, m_pLayout);
1393 else
1395 SfxItemPool* pPool = pReplSet->GetPool();
1396 SfxItemSet aSet( *pPool, pReplSet->GetRanges() );
1398 SfxItemIter aIter( *pSet );
1399 const SfxPoolItem* pItem = aIter.GetCurItem();
1402 // reset all that are not set with pool defaults
1403 if( !IsInvalidItem( pItem ) && SfxItemState::SET !=
1404 pReplSet->GetItemState( pItem->Which(), false ))
1405 aSet.Put( pPool->GetUserOrPoolDefaultItem( pItem->Which() ));
1407 pItem = aIter.NextItem();
1408 } while (pItem);
1409 aSet.Put( *pReplSet );
1410 rCursor.GetDoc().getIDocumentContentOperations().InsertItemSet(
1411 rCursor, aSet, SetAttrMode::DEFAULT, m_pLayout);
1414 return FIND_NO_RING;
1416 else
1417 return FIND_FOUND;
1420 bool SwFindParaAttr::IsReplaceMode() const
1422 return ( pSearchOpt && !pSearchOpt->replaceString.isEmpty() ) ||
1423 ( pReplSet && pReplSet->Count() );
1426 /// search for attributes
1427 sal_Int32 SwCursor::FindAttrs( const SfxItemSet& rSet, bool bNoCollections,
1428 SwDocPositions nStart, SwDocPositions nEnd,
1429 bool& bCancel, FindRanges eFndRngs,
1430 const i18nutil::SearchOptions2* pSearchOpt,
1431 const SfxItemSet* pReplSet,
1432 SwRootFrame const*const pLayout)
1434 // switch off OLE-notifications
1435 SwDoc& rDoc = GetDoc();
1436 Link<bool,void> aLnk( rDoc.GetOle2Link() );
1437 rDoc.SetOle2Link( Link<bool,void>() );
1439 bool bReplace = ( pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
1440 !rSet.Count() ) ) ||
1441 (pReplSet && pReplSet->Count());
1442 bool const bStartUndo = rDoc.GetIDocumentUndoRedo().DoesUndo() && bReplace;
1443 if (bStartUndo)
1445 rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REPLACE, nullptr );
1448 SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt,
1449 pReplSet, *this, pLayout );
1451 sal_Int32 nRet = FindAll( aSwFindParaAttr, nStart, nEnd, eFndRngs, bCancel );
1452 rDoc.SetOle2Link( aLnk );
1453 if( nRet && bReplace )
1454 rDoc.getIDocumentState().SetModified();
1456 if (bStartUndo)
1458 rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REPLACE, nullptr );
1461 return nRet;
1464 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */