Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / crsr / findattr.cxx
blob29c5c8f7ef71c82bc076ef7d50c80ef4a0c48ada
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 SfxItemIter aIter( m_aComapeSet );
224 m_nArrStart = m_aComapeSet.GetWhichByPos( aIter.GetFirstPos() );
225 m_nArrLen = m_aComapeSet.GetWhichByPos( aIter.GetLastPos() ) - m_nArrStart+1;
227 char* pFndChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
228 char* pStackChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
230 m_pFindArr = reinterpret_cast<SwSrchChrAttr*>(pFndChar);
231 m_pStackArr = reinterpret_cast<SwSrchChrAttr*>(pStackChar);
234 SwAttrCheckArr::~SwAttrCheckArr()
236 delete[] reinterpret_cast<char*>(m_pFindArr);
237 delete[] reinterpret_cast<char*>(m_pStackArr);
240 void SwAttrCheckArr::SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam )
242 std::fill(m_pFindArr, m_pFindArr + m_nArrLen, SwSrchChrAttr());
243 std::fill(m_pStackArr, m_pStackArr + m_nArrLen, SwSrchChrAttr());
244 m_nFound = 0;
245 m_nStackCount = 0;
247 if( m_bForward )
249 m_nNodeStart = rPam.GetPoint()->GetContentIndex();
250 m_nNodeEnd = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode()
251 ? rPam.GetMark()->GetContentIndex()
252 : rTextNd.GetText().getLength();
254 else
256 m_nNodeEnd = rPam.GetPoint()->GetContentIndex();
257 m_nNodeStart = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode()
258 ? rPam.GetMark()->GetContentIndex()
259 : 0;
262 if( m_bNoColls && !rTextNd.HasSwAttrSet() )
263 return ;
265 const SfxItemSet& rSet = rTextNd.GetSwAttrSet();
267 SfxItemIter aIter( m_aComapeSet );
268 const SfxPoolItem* pItem = aIter.GetCurItem();
269 const SfxPoolItem* pFndItem;
270 sal_uInt16 nWhich;
274 if( IsInvalidItem( pItem ) )
276 nWhich = m_aComapeSet.GetWhichByPos( aIter.GetCurPos() );
277 if( RES_TXTATR_END <= nWhich )
278 break; // end of text attributes
280 if( SfxItemState::SET == rSet.GetItemState( nWhich, !m_bNoColls, &pFndItem )
281 && !CmpAttr( *pFndItem, rSet.GetPool()->GetDefaultItem( nWhich ) ))
283 m_pFindArr[ nWhich - m_nArrStart ] =
284 SwSrchChrAttr( *pFndItem, m_nNodeStart, m_nNodeEnd );
285 m_nFound++;
288 else
290 nWhich = pItem->Which();
291 if( RES_TXTATR_END <= nWhich )
292 break; // end of text attributes
294 if( CmpAttr( rSet.Get( nWhich, !m_bNoColls ), *pItem ) )
296 m_pFindArr[ nWhich - m_nArrStart ] =
297 SwSrchChrAttr( *pItem, m_nNodeStart, m_nNodeEnd );
298 m_nFound++;
302 pItem = aIter.NextItem();
303 } while (pItem);
306 static bool
307 lcl_IsAttributeIgnorable(sal_Int32 const nNdStart, sal_Int32 const nNdEnd,
308 SwSrchChrAttr const& rTmp)
310 // #i115528#: if there is a paragraph attribute, it has been added by the
311 // SwAttrCheckArr ctor, and nFound is 1.
312 // if the paragraph is entirely covered by hints that override the paragraph
313 // attribute, then this function must find an attribute to decrement nFound!
314 // so check for an empty search range, let attributes that start/end there
315 // cover it, and hope for the best...
316 return ((nNdEnd == nNdStart)
317 ? ((rTmp.nEnd < nNdStart) || (nNdEnd < rTmp.nStt))
318 : ((rTmp.nEnd <= nNdStart) || (nNdEnd <= rTmp.nStt)));
321 bool SwAttrCheckArr::SetAttrFwd( const SwTextAttr& rAttr )
323 SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
325 // ignore all attributes not in search range
326 if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
328 return Found();
331 const SfxPoolItem* pItem;
332 // here we explicitly also search in character templates
333 sal_uInt16 nWhch = rAttr.Which();
334 std::optional<SfxWhichIter> oIter;
335 const SfxPoolItem* pTmpItem = nullptr;
336 const SfxItemSet* pSet = nullptr;
337 if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
339 if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
340 return Found();
341 pTmpItem = nullptr;
342 pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
343 if ( pSet )
345 oIter.emplace( *pSet );
346 nWhch = oIter->FirstWhich();
347 while( nWhch &&
348 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
349 nWhch = oIter->NextWhich();
350 if( !nWhch )
351 pTmpItem = nullptr;
354 else
355 pTmpItem = &rAttr.GetAttr();
357 while( pTmpItem )
359 SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
360 if( SfxItemState::DONTCARE == eState || SfxItemState::SET == eState )
362 sal_uInt16 n;
363 SwSrchChrAttr* pCmp;
365 // first delete all up to start position that are already invalid
366 SwSrchChrAttr* pArrPtr;
367 if( m_nFound )
368 for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen;
369 ++n, ++pArrPtr )
370 if( pArrPtr->nWhich && pArrPtr->nEnd <= aTmp.nStt )
372 pArrPtr->nWhich = 0; // deleted
373 m_nFound--;
376 // delete all up to start position that are already invalid and
377 // move all "open" ones (= stick out over start position) from stack
378 // into FndSet
379 if( m_nStackCount )
380 for( pArrPtr = m_pStackArr, n=0; n < m_nArrLen; ++n, ++pArrPtr )
382 if( !pArrPtr->nWhich )
383 continue;
385 if( pArrPtr->nEnd <= aTmp.nStt )
387 pArrPtr->nWhich = 0; // deleted
388 if( !--m_nStackCount )
389 break;
391 else if( pArrPtr->nStt <= aTmp.nStt )
393 pCmp = &m_pFindArr[ n ];
394 if( pCmp->nWhich )
396 if( pCmp->nEnd < pArrPtr->nEnd ) // extend
397 pCmp->nEnd = pArrPtr->nEnd;
399 else
401 *pCmp = *pArrPtr;
402 m_nFound++;
404 pArrPtr->nWhich = 0;
405 if( !--m_nStackCount )
406 break;
410 bool bContinue = false;
412 if( SfxItemState::DONTCARE == eState )
414 // Will the attribute become valid?
415 if( !CmpAttr( m_aComapeSet.GetPool()->GetDefaultItem( nWhch ),
416 *pTmpItem ))
418 // search attribute and extend if needed
419 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
420 if( !pCmp->nWhich )
422 *pCmp = aTmp; // not found, insert
423 m_nFound++;
425 else if( pCmp->nEnd < aTmp.nEnd ) // extend?
426 pCmp->nEnd = aTmp.nEnd;
428 bContinue = true;
431 // Will the attribute become valid?
432 else if( CmpAttr( *pItem, *pTmpItem ) )
434 m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
435 ++m_nFound;
436 bContinue = true;
439 // then is has to go on the stack
440 if( !bContinue )
442 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
443 if (pCmp->nWhich )
445 // exists on stack, only if it is even bigger
446 if( pCmp->nEnd > aTmp.nEnd )
448 OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich,
449 "slot on stack is still in use" );
451 if( aTmp.nStt <= pCmp->nStt )
452 pCmp->nStt = aTmp.nEnd;
453 else
454 pCmp->nEnd = aTmp.nStt;
456 m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
457 m_nStackCount++;
459 pCmp->nWhich = 0;
460 m_nFound--;
464 if( oIter )
466 assert(pSet && "otherwise no oIter");
467 nWhch = oIter->NextWhich();
468 while( nWhch &&
469 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
470 nWhch = oIter->NextWhich();
471 if( !nWhch )
472 break;
474 else
475 break;
477 oIter.reset();
478 return Found();
481 bool SwAttrCheckArr::SetAttrBwd( const SwTextAttr& rAttr )
483 SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
485 // ignore all attributes not in search range
486 if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
488 return Found();
491 const SfxPoolItem* pItem;
492 // here we explicitly also search in character templates
493 sal_uInt16 nWhch = rAttr.Which();
494 std::optional<SfxWhichIter> oIter;
495 const SfxPoolItem* pTmpItem = nullptr;
496 const SfxItemSet* pSet = nullptr;
497 if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
499 if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
500 return Found();
502 pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
503 if ( pSet )
505 oIter.emplace( *pSet );
506 nWhch = oIter->FirstWhich();
507 while( nWhch &&
508 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
509 nWhch = oIter->NextWhich();
510 if( !nWhch )
511 pTmpItem = nullptr;
514 else
515 pTmpItem = &rAttr.GetAttr();
517 while( pTmpItem )
519 SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
520 if( SfxItemState::DONTCARE == eState || SfxItemState::SET == eState )
522 sal_uInt16 n;
523 SwSrchChrAttr* pCmp;
525 // first delete all up to start position that are already invalid
526 SwSrchChrAttr* pArrPtr;
527 if( m_nFound )
528 for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
529 if( pArrPtr->nWhich && pArrPtr->nStt >= aTmp.nEnd )
531 pArrPtr->nWhich = 0; // deleted
532 m_nFound--;
535 // delete all up to start position that are already invalid and
536 // move all "open" ones (= stick out over start position) from stack
537 // into FndSet
538 if( m_nStackCount )
539 for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
541 if( !pArrPtr->nWhich )
542 continue;
544 if( pArrPtr->nStt >= aTmp.nEnd )
546 pArrPtr->nWhich = 0; // deleted
547 if( !--m_nStackCount )
548 break;
550 else if( pArrPtr->nEnd >= aTmp.nEnd )
552 pCmp = &m_pFindArr[ n ];
553 if( pCmp->nWhich )
555 if( pCmp->nStt > pArrPtr->nStt ) // extend
556 pCmp->nStt = pArrPtr->nStt;
558 else
560 *pCmp = *pArrPtr;
561 m_nFound++;
563 pArrPtr->nWhich = 0;
564 if( !--m_nStackCount )
565 break;
569 bool bContinue = false;
570 if( SfxItemState::DONTCARE == eState )
572 // Will the attribute become valid?
573 if( !CmpAttr( m_aComapeSet.GetPool()->GetDefaultItem( nWhch ),
574 *pTmpItem ) )
576 // search attribute and extend if needed
577 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
578 if( !pCmp->nWhich )
580 *pCmp = aTmp; // not found, insert
581 m_nFound++;
583 else if( pCmp->nStt > aTmp.nStt ) // extend?
584 pCmp->nStt = aTmp.nStt;
586 bContinue = true;
589 // Will the attribute become valid?
590 else if( CmpAttr( *pItem, *pTmpItem ))
592 m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
593 ++m_nFound;
594 bContinue = true;
597 // then is has to go on the stack
598 if( !bContinue )
600 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
601 if( pCmp->nWhich )
603 // exists on stack, only if it is even bigger
604 if( pCmp->nStt < aTmp.nStt )
606 OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich,
607 "slot on stack is still in use" );
609 if( aTmp.nEnd <= pCmp->nEnd )
610 pCmp->nEnd = aTmp.nStt;
611 else
612 pCmp->nStt = aTmp.nEnd;
614 m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
615 m_nStackCount++;
617 pCmp->nWhich = 0;
618 m_nFound--;
622 if( oIter )
624 assert(pSet && "otherwise no oIter");
625 nWhch = oIter->NextWhich();
626 while( nWhch &&
627 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
628 nWhch = oIter->NextWhich();
629 if( !nWhch )
630 break;
632 else
633 break;
635 oIter.reset();
636 return Found();
639 sal_Int32 SwAttrCheckArr::Start() const
641 sal_Int32 nStart = m_nNodeStart;
642 SwSrchChrAttr* pArrPtr = m_pFindArr;
643 for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
644 if( pArrPtr->nWhich && pArrPtr->nStt > nStart )
645 nStart = pArrPtr->nStt;
647 return nStart;
650 sal_Int32 SwAttrCheckArr::End() const
652 SwSrchChrAttr* pArrPtr = m_pFindArr;
653 sal_Int32 nEnd = m_nNodeEnd;
654 for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
655 if( pArrPtr->nWhich && pArrPtr->nEnd < nEnd )
656 nEnd = pArrPtr->nEnd;
658 return nEnd;
661 bool SwAttrCheckArr::CheckStack()
663 if( !m_nStackCount )
664 return false;
666 sal_uInt16 n;
667 const sal_Int32 nSttPos = Start();
668 const sal_Int32 nEndPos = End();
669 SwSrchChrAttr* pArrPtr;
670 for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
672 if( !pArrPtr->nWhich )
673 continue;
675 if( m_bForward ? pArrPtr->nEnd <= nSttPos : pArrPtr->nStt >= nEndPos )
677 pArrPtr->nWhich = 0; // deleted
678 if( !--m_nStackCount )
679 return m_nFound == m_aComapeSet.Count();
681 else if( m_bForward ? pArrPtr->nStt < nEndPos : pArrPtr->nEnd > nSttPos )
683 // move all "open" ones (= stick out over start position) into FndSet
684 OSL_ENSURE( !m_pFindArr[ n ].nWhich, "slot in array is already in use" );
685 m_pFindArr[ n ] = *pArrPtr;
686 pArrPtr->nWhich = 0;
687 m_nFound++;
688 if( !--m_nStackCount )
689 return m_nFound == m_aComapeSet.Count();
692 return m_nFound == m_aComapeSet.Count();
695 static bool lcl_SearchForward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
696 SwPaM& rPam )
698 sal_Int32 nEndPos;
699 rCmpArr.SetNewSet( rTextNd, rPam );
700 if( !rTextNd.HasHints() )
702 if( !rCmpArr.Found() )
703 return false;
704 nEndPos = rCmpArr.GetNdEnd();
705 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
706 return true;
709 const SwpHints& rHtArr = rTextNd.GetSwpHints();
710 const SwTextAttr* pAttr;
711 size_t nPos = 0;
713 // if everything is already there then check with which it will be ended
714 if( rCmpArr.Found() )
716 for( ; nPos < rHtArr.Count(); ++nPos )
718 pAttr = rHtArr.Get( nPos );
719 if( !rCmpArr.SetAttrFwd( *pAttr ) )
721 if( rCmpArr.GetNdStt() < pAttr->GetStart() )
723 // found end
724 auto nTmpStart = pAttr->GetStart();
725 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(),
726 &nTmpStart, true );
727 return true;
729 // continue search
730 break;
734 if( nPos == rHtArr.Count() && rCmpArr.Found() )
736 // found
737 nEndPos = rCmpArr.GetNdEnd();
738 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
739 return true;
743 sal_Int32 nSttPos;
744 for( ; nPos < rHtArr.Count(); ++nPos )
746 pAttr = rHtArr.Get( nPos );
747 if( rCmpArr.SetAttrFwd( *pAttr ) )
749 // Do multiple start at that position? Do also check those:
750 nSttPos = pAttr->GetStart();
751 while( ++nPos < rHtArr.Count() )
753 pAttr = rHtArr.Get( nPos );
754 if( nSttPos != pAttr->GetStart() || !rCmpArr.SetAttrFwd( *pAttr ) )
755 break;
758 if( !rCmpArr.Found() )
759 continue;
761 // then we have our search area
762 nSttPos = rCmpArr.Start();
763 nEndPos = rCmpArr.End();
764 if( nSttPos > nEndPos )
765 return false;
767 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
768 return true;
772 if( !rCmpArr.CheckStack() )
773 return false;
774 nSttPos = rCmpArr.Start();
775 nEndPos = rCmpArr.End();
776 if( nSttPos > nEndPos )
777 return false;
779 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
780 return true;
783 static bool lcl_SearchBackward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
784 SwPaM& rPam )
786 sal_Int32 nEndPos;
787 rCmpArr.SetNewSet( rTextNd, rPam );
788 if( !rTextNd.HasHints() )
790 if( !rCmpArr.Found() )
791 return false;
792 nEndPos = rCmpArr.GetNdEnd();
793 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
794 return true;
797 const SwpHints& rHtArr = rTextNd.GetSwpHints();
798 const SwTextAttr* pAttr;
799 size_t nPos = rHtArr.Count();
800 sal_Int32 nSttPos;
802 // if everything is already there then check with which it will be ended
803 if( rCmpArr.Found() )
805 while( nPos )
807 pAttr = rHtArr.GetSortedByEnd( --nPos );
808 if( !rCmpArr.SetAttrBwd( *pAttr ) )
810 nSttPos = pAttr->GetAnyEnd();
811 if( nSttPos < rCmpArr.GetNdEnd() )
813 // found end
814 nEndPos = rCmpArr.GetNdEnd();
815 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
816 return true;
819 // continue search
820 break;
824 if( !nPos && rCmpArr.Found() )
826 // found
827 nEndPos = rCmpArr.GetNdEnd();
828 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
829 return true;
833 while( nPos )
835 pAttr = rHtArr.GetSortedByEnd( --nPos );
836 if( rCmpArr.SetAttrBwd( *pAttr ) )
838 // Do multiple start at that position? Do also check those:
839 if( nPos )
841 nEndPos = pAttr->GetAnyEnd();
842 while( --nPos )
844 pAttr = rHtArr.GetSortedByEnd( nPos );
845 if( nEndPos != pAttr->GetAnyEnd() || !rCmpArr.SetAttrBwd( *pAttr ) )
846 break;
849 if( !rCmpArr.Found() )
850 continue;
852 // then we have our search area
853 nSttPos = rCmpArr.Start();
854 nEndPos = rCmpArr.End();
855 if( nSttPos > nEndPos )
856 return false;
858 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
859 return true;
863 if( !rCmpArr.CheckStack() )
864 return false;
865 nSttPos = rCmpArr.Start();
866 nEndPos = rCmpArr.End();
867 if( nSttPos > nEndPos )
868 return false;
870 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
871 return true;
874 static bool lcl_Search( const SwContentNode& rCNd, const SfxItemSet& rCmpSet, bool bNoColls )
876 // search only hard attribution?
877 if( bNoColls && !rCNd.HasSwAttrSet() )
878 return false;
880 const SfxItemSet& rNdSet = rCNd.GetSwAttrSet();
881 SfxItemIter aIter( rCmpSet );
882 const SfxPoolItem* pItem = aIter.GetCurItem();
883 const SfxPoolItem* pNdItem;
884 sal_uInt16 nWhich;
888 if( IsInvalidItem( pItem ))
890 nWhich = rCmpSet.GetWhichByPos( aIter.GetCurPos() );
891 if( SfxItemState::SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
892 || CmpAttr( *pNdItem, rNdSet.GetPool()->GetDefaultItem( nWhich ) ))
893 return false;
895 else
897 nWhich = pItem->Which();
899 if( !CmpAttr( rNdSet.Get( nWhich, !bNoColls ), *pItem ))
900 return false;
903 pItem = aIter.NextItem();
904 } while (pItem);
905 return true; // found
908 namespace sw {
910 bool FindAttrImpl(SwPaM & rSearchPam,
911 const SfxPoolItem& rAttr, SwMoveFnCollection const & fnMove,
912 const SwPaM & rRegion, bool bInReadOnly,
913 SwRootFrame const*const pLayout)
915 // determine which attribute is searched:
916 const sal_uInt16 nWhich = rAttr.Which();
917 bool bCharAttr = isCHRATR(nWhich) || isTXTATR(nWhich);
918 assert(isTXTATR(nWhich)); // sw_redlinehide: only works for non-formatting hints such as needed in UpdateFields; use FindAttrsImpl for others
920 std::optional<SwPaM> oPam;
921 sw::MakeRegion(fnMove, rRegion, oPam);
923 bool bFound = false;
924 bool bFirst = true;
925 const bool bSrchForward = &fnMove == &fnMoveForward;
926 SwContentNode * pNode;
928 // if at beginning/end then move it out of the node
929 if( bSrchForward
930 ? oPam->GetPoint()->GetContentIndex() == oPam->GetPointContentNode()->Len()
931 : !oPam->GetPoint()->GetContentIndex() )
933 if( !(*fnMove.fnPos)( oPam->GetPoint(), false ))
935 return false;
937 SwContentNode *pNd = oPam->GetPointContentNode();
938 oPam->GetPoint()->SetContent( bSrchForward ? 0 : pNd->Len() );
941 while (nullptr != (pNode = ::GetNode(*oPam, bFirst, fnMove, bInReadOnly, pLayout)))
943 if( bCharAttr )
945 if( !pNode->IsTextNode() ) // CharAttr are only in text nodes
946 continue;
948 SwTextFrame const*const pFrame(pLayout
949 ? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
950 : nullptr);
951 if (pFrame)
953 SwTextNode const* pAttrNode(nullptr);
954 SwTextAttr const* pAttr(nullptr);
955 if (bSrchForward)
957 sw::MergedAttrIter iter(*pFrame);
960 pAttr = iter.NextAttr(&pAttrNode);
962 while (pAttr
963 && (pAttrNode->GetIndex() < oPam->GetPoint()->GetNodeIndex()
964 || (pAttrNode->GetIndex() == oPam->GetPoint()->GetNodeIndex()
965 && pAttr->GetStart() < oPam->GetPoint()->GetContentIndex())
966 || pAttr->Which() != nWhich));
968 else
970 sw::MergedAttrIterReverse iter(*pFrame);
973 pAttr = iter.PrevAttr(&pAttrNode);
975 while (pAttr
976 && (oPam->GetPoint()->GetNodeIndex() < pAttrNode->GetIndex()
977 || (oPam->GetPoint()->GetNodeIndex() == pAttrNode->GetIndex()
978 && oPam->GetPoint()->GetContentIndex() <= pAttr->GetStart())
979 || pAttr->Which() != nWhich));
981 if (pAttr)
983 assert(pAttrNode);
984 oPam->GetPoint()->Assign(*pAttrNode);
985 lcl_SetAttrPam(*oPam, pAttr->GetStart(), pAttr->End(), bSrchForward);
986 bFound = true;
987 break;
990 else if (!pLayout && pNode->GetTextNode()->HasHints() &&
991 lcl_SearchAttr(*pNode->GetTextNode(), *oPam, rAttr, fnMove))
993 bFound = true;
995 if (bFound)
997 // set to the values of the attribute
998 rSearchPam.SetMark();
999 *rSearchPam.GetPoint() = *oPam->GetPoint();
1000 *rSearchPam.GetMark() = *oPam->GetMark();
1001 break;
1003 else if (isTXTATR(nWhich))
1004 continue;
1007 #if 0
1008 // no hard attribution, so check if node was asked for this attr before
1009 if( !pNode->HasSwAttrSet() )
1011 SwFormat* pTmpFormat = pNode->GetFormatColl();
1012 if( !aFormatArr.insert( pTmpFormat ).second )
1013 continue; // collection was requested earlier
1016 if( SfxItemState::SET == pNode->GetSwAttrSet().GetItemState( nWhich,
1017 true, &pItem ))
1019 // FORWARD: SPoint at the end, GetMark at the beginning of the node
1020 // BACKWARD: SPoint at the beginning, GetMark at the end of the node
1021 // always: incl. start and incl. end
1022 *rSearchPam.GetPoint() = *pPam->GetPoint();
1023 rSearchPam.SetMark();
1024 rSearchPam.GetPoint()->SetContent(pNode->Len());
1025 bFound = true;
1026 break;
1028 #endif
1031 // if backward search, switch point and mark
1032 if( bFound && !bSrchForward )
1033 rSearchPam.Exchange();
1035 return bFound;
1038 } // namespace sw
1040 typedef bool (*FnSearchAttr)( const SwTextNode&, SwAttrCheckArr&, SwPaM& );
1042 static bool FindAttrsImpl(SwPaM & rSearchPam,
1043 const SfxItemSet& rSet, bool bNoColls, SwMoveFnCollection const & fnMove,
1044 const SwPaM & rRegion, bool bInReadOnly, bool bMoveFirst,
1045 SwRootFrame const*const pLayout)
1047 std::optional<SwPaM> oPam;
1048 sw::MakeRegion(fnMove, rRegion, oPam);
1050 bool bFound = false;
1051 bool bFirst = true;
1052 const bool bSrchForward = &fnMove == &fnMoveForward;
1053 SwContentNode * pNode;
1054 o3tl::sorted_vector<SwFormat*> aFormatArr;
1056 // check which text/char attributes are searched
1057 SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls );
1058 SfxItemSetFixed<RES_PARATR_BEGIN, RES_GRFATR_END-1> aOtherSet( rSearchPam.GetDoc().GetAttrPool() );
1059 aOtherSet.Put( rSet, false ); // got all invalid items
1061 FnSearchAttr fnSearch = bSrchForward
1062 ? (&::lcl_SearchForward)
1063 : (&::lcl_SearchBackward);
1065 // if at beginning/end then move it out of the node
1066 if( bMoveFirst &&
1067 ( bSrchForward
1068 ? oPam->GetPoint()->GetContentIndex() == oPam->GetPointContentNode()->Len()
1069 : !oPam->GetPoint()->GetContentIndex() ) )
1071 if( !(*fnMove.fnPos)( oPam->GetPoint(), false ))
1073 return false;
1075 SwContentNode *pNd = oPam->GetPointContentNode();
1076 oPam->GetPoint()->SetContent( bSrchForward ? 0 : pNd->Len() );
1079 while (nullptr != (pNode = ::GetNode(*oPam, bFirst, fnMove, bInReadOnly, pLayout)))
1081 SwTextFrame const*const pFrame(pLayout && pNode->IsTextNode()
1082 ? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
1083 : nullptr);
1084 assert(!pLayout || !pNode->IsTextNode() || pFrame);
1085 // sw_redlinehide: it's apparently not possible to find break items
1086 // with the UI, so checking one node is enough
1087 SwContentNode const& rPropsNode(*(pFrame
1088 ? pFrame->GetTextNodeForParaProps()
1089 : pNode));
1091 if( aCmpArr.Count() )
1093 if( !pNode->IsTextNode() ) // CharAttr are only in text nodes
1094 continue;
1096 if (aOtherSet.Count() &&
1097 !lcl_Search(rPropsNode, aOtherSet, bNoColls))
1099 continue;
1101 sw::MergedPara const*const pMergedPara(pFrame ? pFrame->GetMergedPara() : nullptr);
1102 if (pMergedPara)
1104 SwPosition const& rStart(*oPam->Start());
1105 SwPosition const& rEnd(*oPam->End());
1106 // no extents? fall back to searching index 0 of propsnode
1107 // to find its node items
1108 if (pMergedPara->extents.empty())
1110 if (rStart.GetNodeIndex() <= rPropsNode.GetIndex()
1111 && rPropsNode.GetIndex() <= rEnd.GetNodeIndex())
1113 SwPaM tmp(rPropsNode, 0, rPropsNode, 0);
1114 bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, tmp);
1115 if (bFound)
1117 *oPam = tmp;
1121 else
1123 // iterate the extents, and intersect with input pPam:
1124 // the found ranges should never include delete redlines
1125 // so that subsequent Replace will not affect them
1126 for (size_t i = 0; i < pMergedPara->extents.size(); ++i)
1128 auto const rExtent(pMergedPara->extents[bSrchForward
1130 : pMergedPara->extents.size() - i - 1]);
1131 if (rExtent.pNode->GetIndex() < rStart.GetNodeIndex()
1132 || rEnd.GetNodeIndex() < rExtent.pNode->GetIndex())
1134 continue;
1136 sal_Int32 const nStart(rExtent.pNode == &rStart.GetNode()
1137 ? rStart.GetContentIndex()
1138 : 0);
1139 if (rExtent.nEnd <= nStart)
1141 continue;
1143 sal_Int32 const nEnd(rExtent.pNode == &rEnd.GetNode()
1144 ? rEnd.GetContentIndex()
1145 : rExtent.pNode->Len());
1146 if (nEnd < rExtent.nStart
1147 || (nStart != nEnd && nEnd == rExtent.nStart))
1149 continue;
1151 SwPaM tmp(*rExtent.pNode, std::max(nStart, rExtent.nStart),
1152 *rExtent.pNode, std::min(nEnd, rExtent.nEnd));
1153 tmp.Normalize(bSrchForward);
1154 bFound = (*fnSearch)(*rExtent.pNode, aCmpArr, tmp);
1155 if (bFound)
1157 *oPam = tmp;
1158 break;
1163 else
1165 bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, *oPam);
1167 if (bFound)
1169 // set to the values of the attribute
1170 rSearchPam.SetMark();
1171 *rSearchPam.GetPoint() = *oPam->GetPoint();
1172 *rSearchPam.GetMark() = *oPam->GetMark();
1173 break;
1175 continue; // text attribute
1178 if( !aOtherSet.Count() )
1179 continue;
1181 // no hard attribution, so check if node was asked for this attr before
1182 // (as an optimisation)
1183 if (!rPropsNode.HasSwAttrSet())
1185 SwFormat* pTmpFormat = rPropsNode.GetFormatColl();
1186 if( !aFormatArr.insert( pTmpFormat ).second )
1187 continue; // collection was requested earlier
1190 if (lcl_Search(rPropsNode, aOtherSet, bNoColls))
1192 // FORWARD: SPoint at the end, GetMark at the beginning of the node
1193 // BACKWARD: SPoint at the beginning, GetMark at the end of the node
1194 if (pFrame)
1196 *rSearchPam.GetPoint() = *oPam->GetPoint();
1197 rSearchPam.SetMark();
1198 *rSearchPam.GetMark() = pFrame->MapViewToModelPos(
1199 TextFrameIndex(bSrchForward ? pFrame->GetText().getLength() : 0));
1201 else
1203 *rSearchPam.GetPoint() = *oPam->GetPoint();
1204 rSearchPam.SetMark();
1205 if (bSrchForward)
1207 rSearchPam.GetPoint()->SetContent(pNode->Len());
1209 else
1211 rSearchPam.GetPoint()->SetContent(0);
1214 bFound = true;
1215 break;
1219 // in search direction, mark precedes point, because the next iteration
1220 // starts at point
1221 if (bFound)
1223 rSearchPam.Normalize(!bSrchForward);
1226 return bFound;
1229 namespace {
1231 /// parameters for search for attributes
1232 struct SwFindParaAttr : public SwFindParas
1234 bool m_bNoCollection;
1235 const SfxItemSet *pSet, *pReplSet;
1236 const i18nutil::SearchOptions2 *pSearchOpt;
1237 SwCursor& m_rCursor;
1238 SwRootFrame const* m_pLayout;
1239 std::unique_ptr<utl::TextSearch> pSText;
1241 SwFindParaAttr( const SfxItemSet& rSet, bool bNoCollection,
1242 const i18nutil::SearchOptions2* pOpt, const SfxItemSet* pRSet,
1243 SwCursor& rCursor, SwRootFrame const*const pLayout)
1244 : m_bNoCollection(bNoCollection)
1245 , pSet( &rSet )
1246 , pReplSet( pRSet )
1247 , pSearchOpt( pOpt )
1248 , m_rCursor(rCursor)
1249 , m_pLayout(pLayout)
1252 virtual ~SwFindParaAttr() {}
1254 virtual int DoFind(SwPaM &, SwMoveFnCollection const &, const SwPaM &, bool bInReadOnly,
1255 std::unique_ptr<SvxSearchItem>& xSearchItem) override;
1256 virtual bool IsReplaceMode() const override;
1261 int SwFindParaAttr::DoFind(SwPaM & rCursor, SwMoveFnCollection const & fnMove,
1262 const SwPaM & rRegion, bool bInReadOnly,
1263 std::unique_ptr<SvxSearchItem>& xSearchItem)
1265 // replace string (only if text given and search is not parameterized)?
1266 bool bReplaceText = pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
1267 !pSet->Count() );
1268 bool bReplaceAttr = pReplSet && pReplSet->Count();
1269 bool bMoveFirst = !bReplaceAttr;
1270 if( bInReadOnly && (bReplaceAttr || bReplaceText ))
1271 bInReadOnly = false;
1273 // We search for attributes, should we search for text as well?
1275 SwPaM aRegion( *rRegion.GetMark(), *rRegion.GetPoint() );
1276 SwPaM* pTextRegion = &aRegion;
1277 SwPaM aSrchPam( *rCursor.GetPoint() );
1279 while( true )
1281 if( pSet->Count() ) // any attributes?
1283 // first attributes
1284 if (!FindAttrsImpl(aSrchPam, *pSet, m_bNoCollection, fnMove, aRegion, bInReadOnly, bMoveFirst, m_pLayout))
1285 return FIND_NOT_FOUND;
1286 bMoveFirst = true;
1288 if( !pSearchOpt )
1289 break; // ok, only attributes, so found
1291 pTextRegion = &aSrchPam;
1293 else if( !pSearchOpt )
1294 return FIND_NOT_FOUND;
1296 // then search in text of it
1297 if( !pSText )
1299 i18nutil::SearchOptions2 aTmp( *pSearchOpt );
1301 // search in selection
1302 aTmp.searchFlag |= (SearchFlags::REG_NOT_BEGINOFLINE |
1303 SearchFlags::REG_NOT_ENDOFLINE);
1305 aTmp.Locale = SvtSysLocale().GetLanguageTag().getLocale();
1307 pSText.reset( new utl::TextSearch( aTmp ) );
1310 // TODO: searching for attributes in Outliner text?!
1312 // continue search in correct section (pTextRegion)
1313 if (sw::FindTextImpl(aSrchPam, *pSearchOpt, false/*bSearchInNotes*/, *pSText, fnMove, *pTextRegion, bInReadOnly, m_pLayout, xSearchItem) &&
1314 *aSrchPam.GetMark() != *aSrchPam.GetPoint() )
1315 break; // found
1316 else if( !pSet->Count() )
1317 return FIND_NOT_FOUND; // only text and nothing found
1319 *aRegion.GetMark() = *aSrchPam.GetPoint();
1322 *rCursor.GetPoint() = *aSrchPam.GetPoint();
1323 rCursor.SetMark();
1324 *rCursor.GetMark() = *aSrchPam.GetMark();
1327 if( bReplaceText )
1329 const bool bRegExp(
1330 SearchAlgorithms2::REGEXP == pSearchOpt->AlgorithmType2);
1331 SwPosition& rSttCntPos = *rCursor.Start();
1332 const sal_Int32 nSttCnt = rSttCntPos.GetContentIndex();
1334 // add to shell-cursor-ring so that the regions will be moved eventually
1335 SwPaM* pPrevRing(nullptr);
1336 if( bRegExp )
1338 pPrevRing = const_cast<SwPaM &>(rRegion).GetPrev();
1339 const_cast<SwPaM &>(rRegion).GetRingContainer().merge( m_rCursor.GetRingContainer() );
1342 std::optional<OUString> xRepl;
1343 if (bRegExp)
1344 xRepl = sw::ReplaceBackReferences(*pSearchOpt, &rCursor, m_pLayout);
1345 sw::ReplaceImpl(rCursor,
1346 xRepl ? *xRepl : pSearchOpt->replaceString, bRegExp,
1347 m_rCursor.GetDoc(), m_pLayout);
1349 m_rCursor.SaveTableBoxContent( rCursor.GetPoint() );
1351 if( bRegExp )
1353 // and remove region again
1354 SwPaM* p;
1355 SwPaM* pNext = const_cast<SwPaM*>(&rRegion);
1356 do {
1357 p = pNext;
1358 pNext = p->GetNext();
1359 p->MoveTo(const_cast<SwPaM*>(&rRegion));
1360 } while( p != pPrevRing );
1362 rSttCntPos.SetContent(nSttCnt);
1365 if( bReplaceAttr )
1367 // is the selection still existent?
1368 // all searched attributes are reset to default if
1369 // they are not in ReplaceSet
1370 if( !pSet->Count() )
1372 rCursor.GetDoc().getIDocumentContentOperations().InsertItemSet(
1373 rCursor, *pReplSet, SetAttrMode::DEFAULT, m_pLayout);
1375 else
1377 SfxItemPool* pPool = pReplSet->GetPool();
1378 SfxItemSet aSet( *pPool, pReplSet->GetRanges() );
1380 SfxItemIter aIter( *pSet );
1381 const SfxPoolItem* pItem = aIter.GetCurItem();
1384 // reset all that are not set with pool defaults
1385 if( !IsInvalidItem( pItem ) && SfxItemState::SET !=
1386 pReplSet->GetItemState( pItem->Which(), false ))
1387 aSet.Put( pPool->GetDefaultItem( pItem->Which() ));
1389 pItem = aIter.NextItem();
1390 } while (pItem);
1391 aSet.Put( *pReplSet );
1392 rCursor.GetDoc().getIDocumentContentOperations().InsertItemSet(
1393 rCursor, aSet, SetAttrMode::DEFAULT, m_pLayout);
1396 return FIND_NO_RING;
1398 else
1399 return FIND_FOUND;
1402 bool SwFindParaAttr::IsReplaceMode() const
1404 return ( pSearchOpt && !pSearchOpt->replaceString.isEmpty() ) ||
1405 ( pReplSet && pReplSet->Count() );
1408 /// search for attributes
1409 sal_Int32 SwCursor::FindAttrs( const SfxItemSet& rSet, bool bNoCollections,
1410 SwDocPositions nStart, SwDocPositions nEnd,
1411 bool& bCancel, FindRanges eFndRngs,
1412 const i18nutil::SearchOptions2* pSearchOpt,
1413 const SfxItemSet* pReplSet,
1414 SwRootFrame const*const pLayout)
1416 // switch off OLE-notifications
1417 SwDoc& rDoc = GetDoc();
1418 Link<bool,void> aLnk( rDoc.GetOle2Link() );
1419 rDoc.SetOle2Link( Link<bool,void>() );
1421 bool bReplace = ( pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
1422 !rSet.Count() ) ) ||
1423 (pReplSet && pReplSet->Count());
1424 bool const bStartUndo = rDoc.GetIDocumentUndoRedo().DoesUndo() && bReplace;
1425 if (bStartUndo)
1427 rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REPLACE, nullptr );
1430 SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt,
1431 pReplSet, *this, pLayout );
1433 sal_Int32 nRet = FindAll( aSwFindParaAttr, nStart, nEnd, eFndRngs, bCancel );
1434 rDoc.SetOle2Link( aLnk );
1435 if( nRet && bReplace )
1436 rDoc.getIDocumentState().SetModified();
1438 if (bStartUndo)
1440 rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REPLACE, nullptr );
1443 return nRet;
1446 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */