LibreOffice Module sw (master)  1
findattr.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <com/sun/star/lang/Locale.hpp>
21 #include <com/sun/star/util/SearchAlgorithms2.hpp>
22 #include <com/sun/star/util/SearchFlags.hpp>
24 #include <i18nutil/searchopt.hxx>
25 #include <unotools/syslocale.hxx>
26 #include <hintids.hxx>
27 #include <svl/itemiter.hxx>
28 #include <svl/srchitem.hxx>
29 #include <svl/whiter.hxx>
30 #include <editeng/colritem.hxx>
31 #include <editeng/fontitem.hxx>
32 #include <fmtpdsc.hxx>
33 #include <txatbase.hxx>
34 #include <charfmt.hxx>
35 #include <crsrsh.hxx>
36 #include <doc.hxx>
37 #include <IDocumentUndoRedo.hxx>
38 #include <IDocumentState.hxx>
39 #include <swcrsr.hxx>
40 #include <ndtxt.hxx>
41 #include <pamtyp.hxx>
42 #include <txtfrm.hxx>
43 #include <swundo.hxx>
44 #include <optional>
45 
46 #include <algorithm>
47 #include <memory>
48 
49 using namespace ::com::sun::star;
50 using namespace ::com::sun::star::lang;
51 using namespace ::com::sun::star::util;
52 
53 // Special case for SvxFontItem: only compare the name
54 static bool CmpAttr( const SfxPoolItem& rItem1, const SfxPoolItem& rItem2 )
55 {
56  switch( rItem1.Which() )
57  {
58  case RES_CHRATR_FONT:
59  return static_cast<const SvxFontItem&>(rItem1).GetFamilyName() ==
60  static_cast<const SvxFontItem&>(rItem2).GetFamilyName();
61 
62  case RES_CHRATR_COLOR:
63  return static_cast<const SvxColorItem&>(rItem1).GetValue().IsRGBEqual(
64  static_cast<const SvxColorItem&>(rItem2).GetValue() );
65  case RES_PAGEDESC:
66  ::std::optional<sal_uInt16> const oNumOffset1 =
67  static_cast<const SwFormatPageDesc&>(rItem1).GetNumOffset();
68  ::std::optional<sal_uInt16> const oNumOffset2 =
69  static_cast<const SwFormatPageDesc&>(rItem2).GetNumOffset();
70 
71  if (oNumOffset1 != oNumOffset2)
72  return false;
73 
74  return static_cast<const SwFormatPageDesc&>(rItem1).GetPageDesc() == static_cast<const SwFormatPageDesc&>(rItem2).GetPageDesc();
75  }
76  return rItem1 == rItem2;
77 }
78 
79 const SwTextAttr* GetFrwrdTextHint( const SwpHints& rHtsArr, size_t& rPos,
80  sal_Int32 nContentPos )
81 {
82  while( rPos < rHtsArr.Count() )
83  {
84  const SwTextAttr *pTextHt = rHtsArr.Get( rPos++ );
85  // the start of an attribute has to be in the section
86  if( pTextHt->GetStart() >= nContentPos )
87  return pTextHt; // valid text attribute
88  }
89  return nullptr; // invalid text attribute
90 }
91 
92 const SwTextAttr* GetBkwrdTextHint( const SwpHints& rHtsArr, size_t& rPos,
93  sal_Int32 nContentPos )
94 {
95  while( rPos > 0 )
96  {
97  const SwTextAttr *pTextHt = rHtsArr.Get( --rPos );
98  // the start of an attribute has to be in the section
99  if( pTextHt->GetStart() < nContentPos )
100  return pTextHt; // valid text attribute
101  }
102  return nullptr; // invalid text attribute
103 }
104 
105 static void lcl_SetAttrPam( SwPaM& rPam, sal_Int32 nStart, const sal_Int32* pEnd,
106  const bool bSaveMark )
107 {
108  sal_Int32 nContentPos;
109  if( bSaveMark )
110  nContentPos = rPam.GetMark()->nContent.GetIndex();
111  else
112  nContentPos = rPam.GetPoint()->nContent.GetIndex();
113  bool bTstEnd = rPam.GetPoint()->nNode == rPam.GetMark()->nNode;
114 
115  SwContentNode* pCNd = rPam.GetContentNode();
116  rPam.GetPoint()->nContent.Assign( pCNd, nStart );
117  rPam.SetMark(); // Point == GetMark
118 
119  // Point points to end of search area or end of attribute
120  if( pEnd )
121  {
122  if( bTstEnd && *pEnd > nContentPos )
123  rPam.GetPoint()->nContent = nContentPos;
124  else
125  rPam.GetPoint()->nContent = *pEnd;
126  }
127 }
128 
129 // TODO: provide documentation
142 static bool lcl_SearchAttr( const SwTextNode& rTextNd, SwPaM& rPam,
143  const SfxPoolItem& rCmpItem,
144  SwMoveFnCollection const & fnMove)
145 {
146  if ( !rTextNd.HasHints() )
147  return false;
148 
149  const SwTextAttr *pTextHt = nullptr;
150  bool bForward = &fnMove == &fnMoveForward;
151  size_t nPos = bForward ? 0 : rTextNd.GetSwpHints().Count();
152  sal_Int32 nContentPos = rPam.GetPoint()->nContent.GetIndex();
153 
154  while( nullptr != ( pTextHt=(*fnMove.fnGetHint)(rTextNd.GetSwpHints(),nPos,nContentPos)))
155  if (pTextHt->Which() == rCmpItem.Which())
156  {
157  lcl_SetAttrPam( rPam, pTextHt->GetStart(), pTextHt->End(), bForward );
158  return true;
159  }
160  return false;
161 }
162 
163 namespace {
164 
166 struct SwSrchChrAttr
167 {
168  sal_uInt16 nWhich;
169  sal_Int32 nStt;
170  sal_Int32 nEnd;
171 
172  SwSrchChrAttr(): nWhich(0), nStt(0), nEnd(0) {}
173 
174  SwSrchChrAttr( const SfxPoolItem& rItem,
175  sal_Int32 nStart, sal_Int32 nAnyEnd )
176  : nWhich( rItem.Which() ), nStt( nStart ), nEnd( nAnyEnd )
177  {}
178 };
179 
180 class SwAttrCheckArr
181 {
182  SwSrchChrAttr *m_pFindArr, *m_pStackArr;
183  sal_Int32 m_nNodeStart;
184  sal_Int32 m_nNodeEnd;
185  sal_uInt16 m_nArrStart, m_nArrLen;
186  sal_uInt16 m_nFound, m_nStackCount;
187  SfxItemSet m_aComapeSet;
188  bool m_bNoColls;
189  bool m_bForward;
190 
191 public:
192  SwAttrCheckArr( const SfxItemSet& rSet, bool bForward, bool bNoCollections );
193  ~SwAttrCheckArr();
194 
195  void SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam );
196 
198  sal_uInt16 Count() const { return m_aComapeSet.Count(); }
199  bool Found() const { return m_nFound == m_aComapeSet.Count(); }
200  bool CheckStack();
201 
202  sal_Int32 Start() const;
203  sal_Int32 End() const;
204 
205  sal_Int32 GetNdStt() const { return m_nNodeStart; }
206  sal_Int32 GetNdEnd() const { return m_nNodeEnd; }
207 
208  bool SetAttrFwd( const SwTextAttr& rAttr );
209  bool SetAttrBwd( const SwTextAttr& rAttr );
210 };
211 
212 }
213 
214 SwAttrCheckArr::SwAttrCheckArr( const SfxItemSet& rSet, bool bFwd,
215  bool bNoCollections )
216  : m_nNodeStart(0)
217  , m_nNodeEnd(0)
218  , m_nFound(0)
219  , m_nStackCount(0)
220  , m_aComapeSet( *rSet.GetPool(), svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END-1>{} )
221  , m_bNoColls(bNoCollections)
222  , m_bForward(bFwd)
223 {
224  m_aComapeSet.Put( rSet, false );
225 
226  // determine area of Fnd/Stack array (Min/Max)
227  SfxItemIter aIter( m_aComapeSet );
228  m_nArrStart = m_aComapeSet.GetWhichByPos( aIter.GetFirstPos() );
229  m_nArrLen = m_aComapeSet.GetWhichByPos( aIter.GetLastPos() ) - m_nArrStart+1;
230 
231  char* pFndChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
232  char* pStackChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
233 
234  m_pFindArr = reinterpret_cast<SwSrchChrAttr*>(pFndChar);
235  m_pStackArr = reinterpret_cast<SwSrchChrAttr*>(pStackChar);
236 }
237 
238 SwAttrCheckArr::~SwAttrCheckArr()
239 {
240  delete[] reinterpret_cast<char*>(m_pFindArr);
241  delete[] reinterpret_cast<char*>(m_pStackArr);
242 }
243 
244 void SwAttrCheckArr::SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam )
245 {
246  std::fill(m_pFindArr, m_pFindArr + m_nArrLen, SwSrchChrAttr());
247  std::fill(m_pStackArr, m_pStackArr + m_nArrLen, SwSrchChrAttr());
248  m_nFound = 0;
249  m_nStackCount = 0;
250 
251  if( m_bForward )
252  {
253  m_nNodeStart = rPam.GetPoint()->nContent.GetIndex();
254  m_nNodeEnd = rPam.GetPoint()->nNode == rPam.GetMark()->nNode
255  ? rPam.GetMark()->nContent.GetIndex()
256  : rTextNd.GetText().getLength();
257  }
258  else
259  {
260  m_nNodeEnd = rPam.GetPoint()->nContent.GetIndex();
261  m_nNodeStart = rPam.GetPoint()->nNode == rPam.GetMark()->nNode
262  ? rPam.GetMark()->nContent.GetIndex()
263  : 0;
264  }
265 
266  if( m_bNoColls && !rTextNd.HasSwAttrSet() )
267  return ;
268 
269  const SfxItemSet& rSet = rTextNd.GetSwAttrSet();
270 
271  SfxItemIter aIter( m_aComapeSet );
272  const SfxPoolItem* pItem = aIter.GetCurItem();
273  const SfxPoolItem* pFndItem;
274  sal_uInt16 nWhich;
275 
276  do
277  {
278  if( IsInvalidItem( pItem ) )
279  {
280  nWhich = m_aComapeSet.GetWhichByPos( aIter.GetCurPos() );
281  if( RES_TXTATR_END <= nWhich )
282  break; // end of text attributes
283 
284  if( SfxItemState::SET == rSet.GetItemState( nWhich, !m_bNoColls, &pFndItem )
285  && !CmpAttr( *pFndItem, rSet.GetPool()->GetDefaultItem( nWhich ) ))
286  {
287  m_pFindArr[ nWhich - m_nArrStart ] =
288  SwSrchChrAttr( *pFndItem, m_nNodeStart, m_nNodeEnd );
289  m_nFound++;
290  }
291  }
292  else
293  {
294  nWhich = pItem->Which();
295  if( RES_TXTATR_END <= nWhich )
296  break; // end of text attributes
297 
298  if( CmpAttr( rSet.Get( nWhich, !m_bNoColls ), *pItem ) )
299  {
300  m_pFindArr[ nWhich - m_nArrStart ] =
301  SwSrchChrAttr( *pItem, m_nNodeStart, m_nNodeEnd );
302  m_nFound++;
303  }
304  }
305 
306  pItem = aIter.NextItem();
307  } while (pItem);
308 }
309 
310 static bool
311 lcl_IsAttributeIgnorable(sal_Int32 const nNdStart, sal_Int32 const nNdEnd,
312  SwSrchChrAttr const& rTmp)
313 {
314  // #i115528#: if there is a paragraph attribute, it has been added by the
315  // SwAttrCheckArr ctor, and nFound is 1.
316  // if the paragraph is entirely covered by hints that override the paragraph
317  // attribute, then this function must find an attribute to decrement nFound!
318  // so check for an empty search range, let attributes that start/end there
319  // cover it, and hope for the best...
320  return ((nNdEnd == nNdStart)
321  ? ((rTmp.nEnd < nNdStart) || (nNdEnd < rTmp.nStt))
322  : ((rTmp.nEnd <= nNdStart) || (nNdEnd <= rTmp.nStt)));
323 }
324 
325 bool SwAttrCheckArr::SetAttrFwd( const SwTextAttr& rAttr )
326 {
327  SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
328 
329  // ignore all attributes not in search range
330  if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
331  {
332  return Found();
333  }
334 
335  const SfxPoolItem* pItem;
336  // here we explicitly also search in character templates
337  sal_uInt16 nWhch = rAttr.Which();
338  std::unique_ptr<SfxWhichIter> pIter;
339  const SfxPoolItem* pTmpItem = nullptr;
340  const SfxItemSet* pSet = nullptr;
341  if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
342  {
343  if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
344  return Found();
345  pTmpItem = nullptr;
346  pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
347  if ( pSet )
348  {
349  pIter.reset(new SfxWhichIter( *pSet ));
350  nWhch = pIter->FirstWhich();
351  while( nWhch &&
352  SfxItemState::SET != pSet->GetItemState( nWhch, true, &pTmpItem ) )
353  nWhch = pIter->NextWhich();
354  if( !nWhch )
355  pTmpItem = nullptr;
356  }
357  }
358  else
359  pTmpItem = &rAttr.GetAttr();
360 
361  while( pTmpItem )
362  {
363  SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
364  if( SfxItemState::DONTCARE == eState || SfxItemState::SET == eState )
365  {
366  sal_uInt16 n;
367  SwSrchChrAttr* pCmp;
368 
369  // first delete all up to start position that are already invalid
370  SwSrchChrAttr* pArrPtr;
371  if( m_nFound )
372  for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen;
373  ++n, ++pArrPtr )
374  if( pArrPtr->nWhich && pArrPtr->nEnd <= aTmp.nStt )
375  {
376  pArrPtr->nWhich = 0; // deleted
377  m_nFound--;
378  }
379 
380  // delete all up to start position that are already invalid and
381  // move all "open" ones (= stick out over start position) from stack
382  // into FndSet
383  if( m_nStackCount )
384  for( pArrPtr = m_pStackArr, n=0; n < m_nArrLen; ++n, ++pArrPtr )
385  {
386  if( !pArrPtr->nWhich )
387  continue;
388 
389  if( pArrPtr->nEnd <= aTmp.nStt )
390  {
391  pArrPtr->nWhich = 0; // deleted
392  if( !--m_nStackCount )
393  break;
394  }
395  else if( pArrPtr->nStt <= aTmp.nStt )
396  {
397  pCmp = &m_pFindArr[ n ];
398  if( pCmp->nWhich )
399  {
400  if( pCmp->nEnd < pArrPtr->nEnd ) // extend
401  pCmp->nEnd = pArrPtr->nEnd;
402  }
403  else
404  {
405  *pCmp = *pArrPtr;
406  m_nFound++;
407  }
408  pArrPtr->nWhich = 0;
409  if( !--m_nStackCount )
410  break;
411  }
412  }
413 
414  bool bContinue = false;
415 
416  if( SfxItemState::DONTCARE == eState )
417  {
418  // Will the attribute become valid?
419  if( !CmpAttr( m_aComapeSet.GetPool()->GetDefaultItem( nWhch ),
420  *pTmpItem ))
421  {
422  // search attribute and extend if needed
423  pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
424  if( !pCmp->nWhich )
425  {
426  *pCmp = aTmp; // not found, insert
427  m_nFound++;
428  }
429  else if( pCmp->nEnd < aTmp.nEnd ) // extend?
430  pCmp->nEnd = aTmp.nEnd;
431 
432  bContinue = true;
433  }
434  }
435  // Will the attribute become valid?
436  else if( CmpAttr( *pItem, *pTmpItem ) )
437  {
438  m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
439  ++m_nFound;
440  bContinue = true;
441  }
442 
443  // then is has to go on the stack
444  if( !bContinue )
445  {
446  pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
447  if (pCmp->nWhich )
448  {
449  // exists on stack, only if it is even bigger
450  if( pCmp->nEnd > aTmp.nEnd )
451  {
452  OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich,
453  "slot on stack is still in use" );
454 
455  if( aTmp.nStt <= pCmp->nStt )
456  pCmp->nStt = aTmp.nEnd;
457  else
458  pCmp->nEnd = aTmp.nStt;
459 
460  m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
461  m_nStackCount++;
462  }
463  pCmp->nWhich = 0;
464  m_nFound--;
465  }
466  }
467  }
468  if( pIter )
469  {
470  assert(pSet && "otherwise no pIter");
471  nWhch = pIter->NextWhich();
472  while( nWhch &&
473  SfxItemState::SET != pSet->GetItemState( nWhch, true, &pTmpItem ) )
474  nWhch = pIter->NextWhich();
475  if( !nWhch )
476  break;
477  }
478  else
479  break;
480  }
481  pIter.reset();
482  return Found();
483 }
484 
485 bool SwAttrCheckArr::SetAttrBwd( const SwTextAttr& rAttr )
486 {
487  SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
488 
489  // ignore all attributes not in search range
490  if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
491  {
492  return Found();
493  }
494 
495  const SfxPoolItem* pItem;
496  // here we explicitly also search in character templates
497  sal_uInt16 nWhch = rAttr.Which();
498  std::unique_ptr<SfxWhichIter> pIter;
499  const SfxPoolItem* pTmpItem = nullptr;
500  const SfxItemSet* pSet = nullptr;
501  if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
502  {
503  if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
504  return Found();
505 
506  pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
507  if ( pSet )
508  {
509  pIter.reset( new SfxWhichIter( *pSet ) );
510  nWhch = pIter->FirstWhich();
511  while( nWhch &&
512  SfxItemState::SET != pSet->GetItemState( nWhch, true, &pTmpItem ) )
513  nWhch = pIter->NextWhich();
514  if( !nWhch )
515  pTmpItem = nullptr;
516  }
517  }
518  else
519  pTmpItem = &rAttr.GetAttr();
520 
521  while( pTmpItem )
522  {
523  SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
524  if( SfxItemState::DONTCARE == eState || SfxItemState::SET == eState )
525  {
526  sal_uInt16 n;
527  SwSrchChrAttr* pCmp;
528 
529  // first delete all up to start position that are already invalid
530  SwSrchChrAttr* pArrPtr;
531  if( m_nFound )
532  for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
533  if( pArrPtr->nWhich && pArrPtr->nStt >= aTmp.nEnd )
534  {
535  pArrPtr->nWhich = 0; // deleted
536  m_nFound--;
537  }
538 
539  // delete all up to start position that are already invalid and
540  // move all "open" ones (= stick out over start position) from stack
541  // into FndSet
542  if( m_nStackCount )
543  for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
544  {
545  if( !pArrPtr->nWhich )
546  continue;
547 
548  if( pArrPtr->nStt >= aTmp.nEnd )
549  {
550  pArrPtr->nWhich = 0; // deleted
551  if( !--m_nStackCount )
552  break;
553  }
554  else if( pArrPtr->nEnd >= aTmp.nEnd )
555  {
556  pCmp = &m_pFindArr[ n ];
557  if( pCmp->nWhich )
558  {
559  if( pCmp->nStt > pArrPtr->nStt ) // extend
560  pCmp->nStt = pArrPtr->nStt;
561  }
562  else
563  {
564  *pCmp = *pArrPtr;
565  m_nFound++;
566  }
567  pArrPtr->nWhich = 0;
568  if( !--m_nStackCount )
569  break;
570  }
571  }
572 
573  bool bContinue = false;
574  if( SfxItemState::DONTCARE == eState )
575  {
576  // Will the attribute become valid?
577  if( !CmpAttr( m_aComapeSet.GetPool()->GetDefaultItem( nWhch ),
578  *pTmpItem ) )
579  {
580  // search attribute and extend if needed
581  pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
582  if( !pCmp->nWhich )
583  {
584  *pCmp = aTmp; // not found, insert
585  m_nFound++;
586  }
587  else if( pCmp->nStt > aTmp.nStt ) // extend?
588  pCmp->nStt = aTmp.nStt;
589 
590  bContinue = true;
591  }
592  }
593  // Will the attribute become valid?
594  else if( CmpAttr( *pItem, *pTmpItem ))
595  {
596  m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
597  ++m_nFound;
598  bContinue = true;
599  }
600 
601  // then is has to go on the stack
602  if( !bContinue )
603  {
604  pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
605  if( pCmp->nWhich )
606  {
607  // exists on stack, only if it is even bigger
608  if( pCmp->nStt < aTmp.nStt )
609  {
610  OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich,
611  "slot on stack is still in use" );
612 
613  if( aTmp.nEnd <= pCmp->nEnd )
614  pCmp->nEnd = aTmp.nStt;
615  else
616  pCmp->nStt = aTmp.nEnd;
617 
618  m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
619  m_nStackCount++;
620  }
621  pCmp->nWhich = 0;
622  m_nFound--;
623  }
624  }
625  }
626  if( pIter )
627  {
628  assert(pSet && "otherwise no pIter");
629  nWhch = pIter->NextWhich();
630  while( nWhch &&
631  SfxItemState::SET != pSet->GetItemState( nWhch, true, &pTmpItem ) )
632  nWhch = pIter->NextWhich();
633  if( !nWhch )
634  break;
635  }
636  else
637  break;
638  }
639  pIter.reset();
640  return Found();
641 }
642 
643 sal_Int32 SwAttrCheckArr::Start() const
644 {
645  sal_Int32 nStart = m_nNodeStart;
646  SwSrchChrAttr* pArrPtr = m_pFindArr;
647  for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
648  if( pArrPtr->nWhich && pArrPtr->nStt > nStart )
649  nStart = pArrPtr->nStt;
650 
651  return nStart;
652 }
653 
654 sal_Int32 SwAttrCheckArr::End() const
655 {
656  SwSrchChrAttr* pArrPtr = m_pFindArr;
657  sal_Int32 nEnd = m_nNodeEnd;
658  for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
659  if( pArrPtr->nWhich && pArrPtr->nEnd < nEnd )
660  nEnd = pArrPtr->nEnd;
661 
662  return nEnd;
663 }
664 
665 bool SwAttrCheckArr::CheckStack()
666 {
667  if( !m_nStackCount )
668  return false;
669 
670  sal_uInt16 n;
671  const sal_Int32 nSttPos = Start();
672  const sal_Int32 nEndPos = End();
673  SwSrchChrAttr* pArrPtr;
674  for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
675  {
676  if( !pArrPtr->nWhich )
677  continue;
678 
679  if( m_bForward ? pArrPtr->nEnd <= nSttPos : pArrPtr->nStt >= nEndPos )
680  {
681  pArrPtr->nWhich = 0; // deleted
682  if( !--m_nStackCount )
683  return m_nFound == m_aComapeSet.Count();
684  }
685  else if( m_bForward ? pArrPtr->nStt < nEndPos : pArrPtr->nEnd > nSttPos )
686  {
687  // move all "open" ones (= stick out over start position) into FndSet
688  OSL_ENSURE( !m_pFindArr[ n ].nWhich, "slot in array is already in use" );
689  m_pFindArr[ n ] = *pArrPtr;
690  pArrPtr->nWhich = 0;
691  m_nFound++;
692  if( !--m_nStackCount )
693  return m_nFound == m_aComapeSet.Count();
694  }
695  }
696  return m_nFound == m_aComapeSet.Count();
697 }
698 
699 static bool lcl_SearchForward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
700  SwPaM& rPam )
701 {
702  sal_Int32 nEndPos;
703  rCmpArr.SetNewSet( rTextNd, rPam );
704  if( !rTextNd.HasHints() )
705  {
706  if( !rCmpArr.Found() )
707  return false;
708  nEndPos = rCmpArr.GetNdEnd();
709  lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
710  return true;
711  }
712 
713  const SwpHints& rHtArr = rTextNd.GetSwpHints();
714  const SwTextAttr* pAttr;
715  size_t nPos = 0;
716 
717  // if everything is already there then check with which it will be ended
718  if( rCmpArr.Found() )
719  {
720  for( ; nPos < rHtArr.Count(); ++nPos )
721  {
722  pAttr = rHtArr.Get( nPos );
723  if( !rCmpArr.SetAttrFwd( *pAttr ) )
724  {
725  if( rCmpArr.GetNdStt() < pAttr->GetStart() )
726  {
727  // found end
728  auto nTmpStart = pAttr->GetStart();
729  lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(),
730  &nTmpStart, true );
731  return true;
732  }
733  // continue search
734  break;
735  }
736  }
737 
738  if( nPos == rHtArr.Count() && rCmpArr.Found() )
739  {
740  // found
741  nEndPos = rCmpArr.GetNdEnd();
742  lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
743  return true;
744  }
745  }
746 
747  sal_Int32 nSttPos;
748  for( ; nPos < rHtArr.Count(); ++nPos )
749  {
750  pAttr = rHtArr.Get( nPos );
751  if( rCmpArr.SetAttrFwd( *pAttr ) )
752  {
753  // Do multiple start at that position? Do also check those:
754  nSttPos = pAttr->GetStart();
755  while( ++nPos < rHtArr.Count() )
756  {
757  pAttr = rHtArr.Get( nPos );
758  if( nSttPos != pAttr->GetStart() || !rCmpArr.SetAttrFwd( *pAttr ) )
759  break;
760  }
761 
762  if( !rCmpArr.Found() )
763  continue;
764 
765  // then we have our search area
766  nSttPos = rCmpArr.Start();
767  nEndPos = rCmpArr.End();
768  if( nSttPos > nEndPos )
769  return false;
770 
771  lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
772  return true;
773  }
774  }
775 
776  if( !rCmpArr.CheckStack() )
777  return false;
778  nSttPos = rCmpArr.Start();
779  nEndPos = rCmpArr.End();
780  if( nSttPos > nEndPos )
781  return false;
782 
783  lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
784  return true;
785 }
786 
787 static bool lcl_SearchBackward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
788  SwPaM& rPam )
789 {
790  sal_Int32 nEndPos;
791  rCmpArr.SetNewSet( rTextNd, rPam );
792  if( !rTextNd.HasHints() )
793  {
794  if( !rCmpArr.Found() )
795  return false;
796  nEndPos = rCmpArr.GetNdEnd();
797  lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
798  return true;
799  }
800 
801  const SwpHints& rHtArr = rTextNd.GetSwpHints();
802  const SwTextAttr* pAttr;
803  size_t nPos = rHtArr.Count();
804  sal_Int32 nSttPos;
805 
806  // if everything is already there then check with which it will be ended
807  if( rCmpArr.Found() )
808  {
809  while( nPos )
810  {
811  pAttr = rHtArr.GetSortedByEnd( --nPos );
812  if( !rCmpArr.SetAttrBwd( *pAttr ) )
813  {
814  nSttPos = pAttr->GetAnyEnd();
815  if( nSttPos < rCmpArr.GetNdEnd() )
816  {
817  // found end
818  nEndPos = rCmpArr.GetNdEnd();
819  lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
820  return true;
821  }
822 
823  // continue search
824  break;
825  }
826  }
827 
828  if( !nPos && rCmpArr.Found() )
829  {
830  // found
831  nEndPos = rCmpArr.GetNdEnd();
832  lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
833  return true;
834  }
835  }
836 
837  while( nPos )
838  {
839  pAttr = rHtArr.GetSortedByEnd( --nPos );
840  if( rCmpArr.SetAttrBwd( *pAttr ) )
841  {
842  // Do multiple start at that position? Do also check those:
843  if( nPos )
844  {
845  nEndPos = pAttr->GetAnyEnd();
846  while( --nPos )
847  {
848  pAttr = rHtArr.GetSortedByEnd( nPos );
849  if( nEndPos != pAttr->GetAnyEnd() || !rCmpArr.SetAttrBwd( *pAttr ) )
850  break;
851  }
852  }
853  if( !rCmpArr.Found() )
854  continue;
855 
856  // then we have our search area
857  nSttPos = rCmpArr.Start();
858  nEndPos = rCmpArr.End();
859  if( nSttPos > nEndPos )
860  return false;
861 
862  lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
863  return true;
864  }
865  }
866 
867  if( !rCmpArr.CheckStack() )
868  return false;
869  nSttPos = rCmpArr.Start();
870  nEndPos = rCmpArr.End();
871  if( nSttPos > nEndPos )
872  return false;
873 
874  lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
875  return true;
876 }
877 
878 static bool lcl_Search( const SwContentNode& rCNd, const SfxItemSet& rCmpSet, bool bNoColls )
879 {
880  // search only hard attribution?
881  if( bNoColls && !rCNd.HasSwAttrSet() )
882  return false;
883 
884  const SfxItemSet& rNdSet = rCNd.GetSwAttrSet();
885  SfxItemIter aIter( rCmpSet );
886  const SfxPoolItem* pItem = aIter.GetCurItem();
887  const SfxPoolItem* pNdItem;
888  sal_uInt16 nWhich;
889 
890  do
891  {
892  if( IsInvalidItem( pItem ))
893  {
894  nWhich = rCmpSet.GetWhichByPos( aIter.GetCurPos() );
895  if( SfxItemState::SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
896  || CmpAttr( *pNdItem, rNdSet.GetPool()->GetDefaultItem( nWhich ) ))
897  return false;
898  }
899  else
900  {
901  nWhich = pItem->Which();
902 
903  if( !CmpAttr( rNdSet.Get( nWhich, !bNoColls ), *pItem ))
904  return false;
905  }
906 
907  pItem = aIter.NextItem();
908  } while (pItem);
909  return true; // found
910 }
911 
912 namespace sw {
913 
914 bool FindAttrImpl(SwPaM & rSearchPam,
915  const SfxPoolItem& rAttr, SwMoveFnCollection const & fnMove,
916  const SwPaM & rRegion, bool bInReadOnly,
917  SwRootFrame const*const pLayout)
918 {
919  // determine which attribute is searched:
920  const sal_uInt16 nWhich = rAttr.Which();
921  bool bCharAttr = isCHRATR(nWhich) || isTXTATR(nWhich);
922  assert(isTXTATR(nWhich)); // sw_redlinehide: only works for non-formatting hints such as needed in UpdateFields; use FindAttrsImpl for others
923 
924  std::unique_ptr<SwPaM> pPam(sw::MakeRegion(fnMove, rRegion));
925 
926  bool bFound = false;
927  bool bFirst = true;
928  const bool bSrchForward = &fnMove == &fnMoveForward;
929  SwContentNode * pNode;
930 
931  // if at beginning/end then move it out of the node
932  if( bSrchForward
933  ? pPam->GetPoint()->nContent.GetIndex() == pPam->GetContentNode()->Len()
934  : !pPam->GetPoint()->nContent.GetIndex() )
935  {
936  if( !(*fnMove.fnNds)( &pPam->GetPoint()->nNode, false ))
937  {
938  return false;
939  }
940  SwContentNode *pNd = pPam->GetContentNode();
941  pPam->GetPoint()->nContent.Assign( pNd, bSrchForward ? 0 : pNd->Len() );
942  }
943 
944  while (nullptr != (pNode = ::GetNode(*pPam, bFirst, fnMove, bInReadOnly, pLayout)))
945  {
946  if( bCharAttr )
947  {
948  if( !pNode->IsTextNode() ) // CharAttr are only in text nodes
949  continue;
950 
951  SwTextFrame const*const pFrame(pLayout
952  ? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
953  : nullptr);
954  if (pFrame)
955  {
956  SwTextNode const* pAttrNode(nullptr);
957  SwTextAttr const* pAttr(nullptr);
958  if (bSrchForward)
959  {
960  sw::MergedAttrIter iter(*pFrame);
961  do
962  {
963  pAttr = iter.NextAttr(&pAttrNode);
964  }
965  while (pAttr
966  && (pAttrNode->GetIndex() < pPam->GetPoint()->nNode.GetIndex()
967  || (pAttrNode->GetIndex() == pPam->GetPoint()->nNode.GetIndex()
968  && pAttr->GetStart() < pPam->GetPoint()->nContent.GetIndex())
969  || pAttr->Which() != nWhich));
970  }
971  else
972  {
973  sw::MergedAttrIterReverse iter(*pFrame);
974  do
975  {
976  pAttr = iter.PrevAttr(&pAttrNode);
977  }
978  while (pAttr
979  && (pPam->GetPoint()->nNode.GetIndex() < pAttrNode->GetIndex()
980  || (pPam->GetPoint()->nNode.GetIndex() == pAttrNode->GetIndex()
981  && pPam->GetPoint()->nContent.GetIndex() <= pAttr->GetStart())
982  || pAttr->Which() != nWhich));
983  }
984  if (pAttr)
985  {
986  assert(pAttrNode);
987  pPam->GetPoint()->nNode = *pAttrNode;
988  lcl_SetAttrPam(*pPam, pAttr->GetStart(), pAttr->End(), bSrchForward);
989  bFound = true;
990  break;
991  }
992  }
993  else if (!pLayout && pNode->GetTextNode()->HasHints() &&
994  lcl_SearchAttr(*pNode->GetTextNode(), *pPam, rAttr, fnMove))
995  {
996  bFound = true;
997  }
998  if (bFound)
999  {
1000  // set to the values of the attribute
1001  rSearchPam.SetMark();
1002  *rSearchPam.GetPoint() = *pPam->GetPoint();
1003  *rSearchPam.GetMark() = *pPam->GetMark();
1004  break;
1005  }
1006  else if (isTXTATR(nWhich))
1007  continue;
1008  }
1009 
1010 #if 0
1011  // no hard attribution, so check if node was asked for this attr before
1012  if( !pNode->HasSwAttrSet() )
1013  {
1014  SwFormat* pTmpFormat = pNode->GetFormatColl();
1015  if( !aFormatArr.insert( pTmpFormat ).second )
1016  continue; // collection was requested earlier
1017  }
1018 
1019  if( SfxItemState::SET == pNode->GetSwAttrSet().GetItemState( nWhich,
1020  true, &pItem ))
1021  {
1022  // FORWARD: SPoint at the end, GetMark at the beginning of the node
1023  // BACKWARD: SPoint at the beginning, GetMark at the end of the node
1024  // always: incl. start and incl. end
1025  *rSearchPam.GetPoint() = *pPam->GetPoint();
1026  rSearchPam.SetMark();
1027  pNode->MakeEndIndex( &rSearchPam.GetPoint()->nContent );
1028  bFound = true;
1029  break;
1030  }
1031 #endif
1032  }
1033 
1034  // if backward search, switch point and mark
1035  if( bFound && !bSrchForward )
1036  rSearchPam.Exchange();
1037 
1038  return bFound;
1039 }
1040 
1041 } // namespace sw
1042 
1043 typedef bool (*FnSearchAttr)( const SwTextNode&, SwAttrCheckArr&, SwPaM& );
1044 
1045 static bool FindAttrsImpl(SwPaM & rSearchPam,
1046  const SfxItemSet& rSet, bool bNoColls, SwMoveFnCollection const & fnMove,
1047  const SwPaM & rRegion, bool bInReadOnly, bool bMoveFirst,
1048  SwRootFrame const*const pLayout)
1049 {
1050  std::unique_ptr<SwPaM> pPam(sw::MakeRegion(fnMove, rRegion));
1051 
1052  bool bFound = false;
1053  bool bFirst = true;
1054  const bool bSrchForward = &fnMove == &fnMoveForward;
1055  SwContentNode * pNode;
1056  o3tl::sorted_vector<SwFormat*> aFormatArr;
1057 
1058  // check which text/char attributes are searched
1059  SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls );
1060  SfxItemSet aOtherSet( rSearchPam.GetDoc().GetAttrPool(),
1062  aOtherSet.Put( rSet, false ); // got all invalid items
1063 
1064  FnSearchAttr fnSearch = bSrchForward
1065  ? (&::lcl_SearchForward)
1066  : (&::lcl_SearchBackward);
1067 
1068  // if at beginning/end then move it out of the node
1069  if( bMoveFirst &&
1070  ( bSrchForward
1071  ? pPam->GetPoint()->nContent.GetIndex() == pPam->GetContentNode()->Len()
1072  : !pPam->GetPoint()->nContent.GetIndex() ) )
1073  {
1074  if( !(*fnMove.fnNds)( &pPam->GetPoint()->nNode, false ))
1075  {
1076  return false;
1077  }
1078  SwContentNode *pNd = pPam->GetContentNode();
1079  pPam->GetPoint()->nContent.Assign( pNd, bSrchForward ? 0 : pNd->Len() );
1080  }
1081 
1082  while (nullptr != (pNode = ::GetNode(*pPam, bFirst, fnMove, bInReadOnly, pLayout)))
1083  {
1084  SwTextFrame const*const pFrame(pLayout && pNode->IsTextNode()
1085  ? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
1086  : nullptr);
1087  assert(!pLayout || !pNode->IsTextNode() || pFrame);
1088  // sw_redlinehide: it's apparently not possible to find break items
1089  // with the UI, so checking one node is enough
1090  SwContentNode const& rPropsNode(*(pFrame
1091  ? pFrame->GetTextNodeForParaProps()
1092  : pNode));
1093 
1094  if( aCmpArr.Count() )
1095  {
1096  if( !pNode->IsTextNode() ) // CharAttr are only in text nodes
1097  continue;
1098 
1099  if (aOtherSet.Count() &&
1100  !lcl_Search(rPropsNode, aOtherSet, bNoColls))
1101  {
1102  continue;
1103  }
1104  sw::MergedPara const*const pMergedPara(pFrame ? pFrame->GetMergedPara() : nullptr);
1105  if (pMergedPara)
1106  {
1107  SwPosition const& rStart(*pPam->Start());
1108  SwPosition const& rEnd(*pPam->End());
1109  // no extents? fall back to searching index 0 of propsnode
1110  // to find its node items
1111  if (pMergedPara->extents.empty())
1112  {
1113  if (rStart.nNode.GetIndex() <= rPropsNode.GetIndex()
1114  && rPropsNode.GetIndex() <= rEnd.nNode.GetIndex())
1115  {
1116  SwPaM tmp(rPropsNode, 0, rPropsNode, 0);
1117  bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, tmp);
1118  if (bFound)
1119  {
1120  *pPam = tmp;
1121  }
1122  }
1123  }
1124  else
1125  {
1126  // iterate the extents, and intersect with input pPam:
1127  // the found ranges should never include delete redlines
1128  // so that subsequent Replace will not affect them
1129  for (size_t i = 0; i < pMergedPara->extents.size(); ++i)
1130  {
1131  auto const rExtent(pMergedPara->extents[bSrchForward
1132  ? i
1133  : pMergedPara->extents.size() - i - 1]);
1134  if (rExtent.pNode->GetIndex() < rStart.nNode.GetIndex()
1135  || rEnd.nNode.GetIndex() < rExtent.pNode->GetIndex())
1136  {
1137  continue;
1138  }
1139  sal_Int32 const nStart(rExtent.pNode == &rStart.nNode.GetNode()
1140  ? rStart.nContent.GetIndex()
1141  : 0);
1142  if (rExtent.nEnd <= nStart)
1143  {
1144  continue;
1145  }
1146  sal_Int32 const nEnd(rExtent.pNode == &rEnd.nNode.GetNode()
1147  ? rEnd.nContent.GetIndex()
1148  : rExtent.pNode->Len());
1149  if (nEnd < rExtent.nStart
1150  || (nStart != nEnd && nEnd == rExtent.nStart))
1151  {
1152  continue;
1153  }
1154  SwPaM tmp(*rExtent.pNode, std::max(nStart, rExtent.nStart),
1155  *rExtent.pNode, std::min(nEnd, rExtent.nEnd));
1156  tmp.Normalize(bSrchForward);
1157  bFound = (*fnSearch)(*rExtent.pNode, aCmpArr, tmp);
1158  if (bFound)
1159  {
1160  *pPam = tmp;
1161  break;
1162  }
1163  }
1164  }
1165  }
1166  else
1167  {
1168  bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, *pPam);
1169  }
1170  if (bFound)
1171  {
1172  // set to the values of the attribute
1173  rSearchPam.SetMark();
1174  *rSearchPam.GetPoint() = *pPam->GetPoint();
1175  *rSearchPam.GetMark() = *pPam->GetMark();
1176  break;
1177  }
1178  continue; // text attribute
1179  }
1180 
1181  if( !aOtherSet.Count() )
1182  continue;
1183 
1184  // no hard attribution, so check if node was asked for this attr before
1185  // (as an optimisation)
1186  if (!rPropsNode.HasSwAttrSet())
1187  {
1188  SwFormat* pTmpFormat = rPropsNode.GetFormatColl();
1189  if( !aFormatArr.insert( pTmpFormat ).second )
1190  continue; // collection was requested earlier
1191  }
1192 
1193  if (lcl_Search(rPropsNode, aOtherSet, bNoColls))
1194  {
1195  // FORWARD: SPoint at the end, GetMark at the beginning of the node
1196  // BACKWARD: SPoint at the beginning, GetMark at the end of the node
1197  if (pFrame)
1198  {
1199  *rSearchPam.GetPoint() = *pPam->GetPoint();
1200  rSearchPam.SetMark();
1201  *rSearchPam.GetMark() = pFrame->MapViewToModelPos(
1202  TextFrameIndex(bSrchForward ? pFrame->GetText().getLength() : 0));
1203  }
1204  else
1205  {
1206  *rSearchPam.GetPoint() = *pPam->GetPoint();
1207  rSearchPam.SetMark();
1208  if (bSrchForward)
1209  {
1210  pNode->MakeEndIndex( &rSearchPam.GetPoint()->nContent );
1211  }
1212  else
1213  {
1214  pNode->MakeStartIndex( &rSearchPam.GetPoint()->nContent );
1215  }
1216  }
1217  bFound = true;
1218  break;
1219  }
1220  }
1221 
1222  // in search direction, mark precedes point, because the next iteration
1223  // starts at point
1224  if (bFound)
1225  {
1226  rSearchPam.Normalize(!bSrchForward);
1227  }
1228 
1229  return bFound;
1230 }
1231 
1232 namespace {
1233 
1235 struct SwFindParaAttr : public SwFindParas
1236 {
1237  bool m_bNoCollection;
1238  const SfxItemSet *pSet, *pReplSet;
1239  const i18nutil::SearchOptions2 *pSearchOpt;
1240  SwCursor& m_rCursor;
1241  SwRootFrame const* m_pLayout;
1242  std::unique_ptr<utl::TextSearch> pSText;
1243 
1244  SwFindParaAttr( const SfxItemSet& rSet, bool bNoCollection,
1245  const i18nutil::SearchOptions2* pOpt, const SfxItemSet* pRSet,
1246  SwCursor& rCursor, SwRootFrame const*const pLayout)
1247  : m_bNoCollection(bNoCollection)
1248  , pSet( &rSet )
1249  , pReplSet( pRSet )
1250  , pSearchOpt( pOpt )
1251  , m_rCursor(rCursor)
1252  , m_pLayout(pLayout)
1253  {}
1254 
1255  virtual ~SwFindParaAttr() {}
1256 
1257  virtual int DoFind(SwPaM &, SwMoveFnCollection const &, const SwPaM &, bool bInReadOnly,
1258  std::unique_ptr<SvxSearchItem>& xSearchItem) override;
1259  virtual bool IsReplaceMode() const override;
1260 };
1261 
1262 }
1263 
1264 int SwFindParaAttr::DoFind(SwPaM & rCursor, SwMoveFnCollection const & fnMove,
1265  const SwPaM & rRegion, bool bInReadOnly,
1266  std::unique_ptr<SvxSearchItem>& xSearchItem)
1267 {
1268  // replace string (only if text given and search is not parameterized)?
1269  bool bReplaceText = pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
1270  !pSet->Count() );
1271  bool bReplaceAttr = pReplSet && pReplSet->Count();
1272  bool bMoveFirst = !bReplaceAttr;
1273  if( bInReadOnly && (bReplaceAttr || bReplaceText ))
1274  bInReadOnly = false;
1275 
1276  // We search for attributes, should we search for text as well?
1277  {
1278  SwPaM aRegion( *rRegion.GetMark(), *rRegion.GetPoint() );
1279  SwPaM* pTextRegion = &aRegion;
1280  SwPaM aSrchPam( *rCursor.GetPoint() );
1281 
1282  while( true )
1283  {
1284  if( pSet->Count() ) // any attributes?
1285  {
1286  // first attributes
1287  if (!FindAttrsImpl(aSrchPam, *pSet, m_bNoCollection, fnMove, aRegion, bInReadOnly, bMoveFirst, m_pLayout))
1288  return FIND_NOT_FOUND;
1289  bMoveFirst = true;
1290 
1291  if( !pSearchOpt )
1292  break; // ok, only attributes, so found
1293 
1294  pTextRegion = &aSrchPam;
1295  }
1296  else if( !pSearchOpt )
1297  return FIND_NOT_FOUND;
1298 
1299  // then search in text of it
1300  if( !pSText )
1301  {
1302  i18nutil::SearchOptions2 aTmp( *pSearchOpt );
1303 
1304  // search in selection
1305  aTmp.searchFlag |= (SearchFlags::REG_NOT_BEGINOFLINE |
1306  SearchFlags::REG_NOT_ENDOFLINE);
1307 
1308  aTmp.Locale = SvtSysLocale().GetLanguageTag().getLocale();
1309 
1310  pSText.reset( new utl::TextSearch( aTmp ) );
1311  }
1312 
1313  // TODO: searching for attributes in Outliner text?!
1314 
1315  // continue search in correct section (pTextRegion)
1316  if (sw::FindTextImpl(aSrchPam, *pSearchOpt, false/*bSearchInNotes*/, *pSText, fnMove, *pTextRegion, bInReadOnly, m_pLayout, xSearchItem) &&
1317  *aSrchPam.GetMark() != *aSrchPam.GetPoint() )
1318  break; // found
1319  else if( !pSet->Count() )
1320  return FIND_NOT_FOUND; // only text and nothing found
1321 
1322  *aRegion.GetMark() = *aSrchPam.GetPoint();
1323  }
1324 
1325  *rCursor.GetPoint() = *aSrchPam.GetPoint();
1326  rCursor.SetMark();
1327  *rCursor.GetMark() = *aSrchPam.GetMark();
1328  }
1329 
1330  if( bReplaceText )
1331  {
1332  const bool bRegExp(
1333  SearchAlgorithms2::REGEXP == pSearchOpt->AlgorithmType2);
1334  SwIndex& rSttCntIdx = rCursor.Start()->nContent;
1335  const sal_Int32 nSttCnt = rSttCntIdx.GetIndex();
1336 
1337  // add to shell-cursor-ring so that the regions will be moved eventually
1338  SwPaM* pPrevRing(nullptr);
1339  if( bRegExp )
1340  {
1341  pPrevRing = const_cast<SwPaM &>(rRegion).GetPrev();
1342  const_cast<SwPaM &>(rRegion).GetRingContainer().merge( m_rCursor.GetRingContainer() );
1343  }
1344 
1345  std::optional<OUString> xRepl;
1346  if (bRegExp)
1347  xRepl = sw::ReplaceBackReferences(*pSearchOpt, &rCursor, m_pLayout);
1348  sw::ReplaceImpl(rCursor,
1349  xRepl ? *xRepl : pSearchOpt->replaceString, bRegExp,
1350  m_rCursor.GetDoc(), m_pLayout);
1351 
1352  m_rCursor.SaveTableBoxContent( rCursor.GetPoint() );
1353 
1354  if( bRegExp )
1355  {
1356  // and remove region again
1357  SwPaM* p;
1358  SwPaM* pNext = const_cast<SwPaM*>(&rRegion);
1359  do {
1360  p = pNext;
1361  pNext = p->GetNext();
1362  p->MoveTo(const_cast<SwPaM*>(&rRegion));
1363  } while( p != pPrevRing );
1364  }
1365  rSttCntIdx = nSttCnt;
1366  }
1367 
1368  if( bReplaceAttr )
1369  {
1370  // is the selection still existent?
1371  // all searched attributes are reset to default if
1372  // they are not in ReplaceSet
1373  if( !pSet->Count() )
1374  {
1376  rCursor, *pReplSet, SetAttrMode::DEFAULT, m_pLayout);
1377  }
1378  else
1379  {
1380  SfxItemPool* pPool = pReplSet->GetPool();
1381  SfxItemSet aSet( *pPool, pReplSet->GetRanges() );
1382 
1383  SfxItemIter aIter( *pSet );
1384  const SfxPoolItem* pItem = aIter.GetCurItem();
1385  do
1386  {
1387  // reset all that are not set with pool defaults
1388  if( !IsInvalidItem( pItem ) && SfxItemState::SET !=
1389  pReplSet->GetItemState( pItem->Which(), false ))
1390  aSet.Put( pPool->GetDefaultItem( pItem->Which() ));
1391 
1392  pItem = aIter.NextItem();
1393  } while (pItem);
1394  aSet.Put( *pReplSet );
1396  rCursor, aSet, SetAttrMode::DEFAULT, m_pLayout);
1397  }
1398 
1399  return FIND_NO_RING;
1400  }
1401  else
1402  return FIND_FOUND;
1403 }
1404 
1405 bool SwFindParaAttr::IsReplaceMode() const
1406 {
1407  return ( pSearchOpt && !pSearchOpt->replaceString.isEmpty() ) ||
1408  ( pReplSet && pReplSet->Count() );
1409 }
1410 
1412 sal_uLong SwCursor::FindAttrs( const SfxItemSet& rSet, bool bNoCollections,
1413  SwDocPositions nStart, SwDocPositions nEnd,
1414  bool& bCancel, FindRanges eFndRngs,
1415  const i18nutil::SearchOptions2* pSearchOpt,
1416  const SfxItemSet* pReplSet,
1417  SwRootFrame const*const pLayout)
1418 {
1419  // switch off OLE-notifications
1420  SwDoc& rDoc = GetDoc();
1421  Link<bool,void> aLnk( rDoc.GetOle2Link() );
1422  rDoc.SetOle2Link( Link<bool,void>() );
1423 
1424  bool bReplace = ( pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
1425  !rSet.Count() ) ) ||
1426  (pReplSet && pReplSet->Count());
1427  bool const bStartUndo = rDoc.GetIDocumentUndoRedo().DoesUndo() && bReplace;
1428  if (bStartUndo)
1429  {
1431  }
1432 
1433  SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt,
1434  pReplSet, *this, pLayout );
1435 
1436  sal_uLong nRet = FindAll( aSwFindParaAttr, nStart, nEnd, eFndRngs, bCancel );
1437  rDoc.SetOle2Link( aLnk );
1438  if( nRet && bReplace )
1439  rDoc.getIDocumentState().SetModified();
1440 
1441  if (bStartUndo)
1442  {
1443  rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REPLACE, nullptr );
1444  }
1445 
1446  return nRet;
1447 }
1448 
1449 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::optional< OUString > ReplaceBackReferences(const i18nutil::SearchOptions2 &rSearchOpt, SwPaM *const pPam, SwRootFrame const *const pLayout)
Helperfunction to resolve backward references in regular expressions.
Definition: findtxt.cxx:1117
Represents the visualization of a paragraph.
Definition: txtfrm.hxx:151
virtual sal_Int32 Len() const
Definition: node.cxx:1225
constexpr TypedWhichId< SwFormatPageDesc > RES_PAGEDESC(93)
virtual int DoFind(SwPaM &, SwMoveFnCollection const &, const SwPaM &, bool, std::unique_ptr< SvxSearchItem > &xSearchItem)=0
sal_uLong GetIndex() const
Definition: node.hxx:290
Marks a position in the document model.
Definition: pam.hxx:35
SwContentNode * GetNode(SwPaM &rPam, bool &rbFirst, SwMoveFnCollection const &fnMove, bool const bInReadOnly, SwRootFrame const *const i_pLayout)
This function returns the next node in direction of search.
Definition: pam.cxx:816
Pagedescriptor Client of SwPageDesc that is "described" by the attribute.
Definition: fmtpdsc.hxx:35
bool isCHRATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:471
const OUString & GetText() const
Definition: ndtxt.hxx:211
std::unique_ptr< SwPaM > MakeRegion(SwMoveFnCollection const &fnMove, const SwPaM &rOrigRg)
make a new region
Definition: pam.cxx:521
std::string GetValue
bool isTXTATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:485
const SwTextAttr * GetBkwrdTextHint(const SwpHints &rHtsArr, size_t &rPos, sal_Int32 nContentPos)
Definition: findattr.cxx:92
SwNodeIndex nNode
Definition: pam.hxx:37
virtual void InsertItemSet(const SwPaM &rRg, const SfxItemSet &, const SetAttrMode nFlags=SetAttrMode::DEFAULT, SwRootFrame const *pLayout=nullptr)=0
virtual void SetModified()=0
Must be called manually at changes of format.
sal_uIntPtr sal_uLong
const SwPosition * GetMark() const
Definition: pam.hxx:209
sw::MergedPara * GetMergedPara()
Definition: txtfrm.hxx:441
SwContentFrame * getLayoutFrame(const SwRootFrame *, const SwPosition *pPos=nullptr, std::pair< Point, bool > const *pViewPosAndCalcFrame=nullptr) const
Definition: node.cxx:1192
sal_Int64 n
virtual SwUndoId EndUndo(SwUndoId const eUndoId, SwRewriter const *const pRewriter)=0
Closes undo block.
Definition: doc.hxx:184
void SetOle2Link(const Link< bool, void > &rLink)
Definition: doc.hxx:1324
SwTextAttr const * PrevAttr(SwTextNode const **ppNode=nullptr)
Definition: txtfrm.cxx:222
static bool lcl_IsAttributeIgnorable(sal_Int32 const nNdStart, sal_Int32 const nNdEnd, SwSrchChrAttr const &rTmp)
Definition: findattr.cxx:311
SwTextAttr * GetSortedByEnd(size_t nPos) const
Definition: ndhints.hxx:158
Dialog to specify the properties of date form field.
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:144
sal_uInt16 Which() const
Definition: txatbase.hxx:110
const OUString & GetText() const
Returns the text portion we want to edit (for inline see underneath)
Definition: txtfrm.cxx:1283
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:153
static bool CmpAttr(const SfxPoolItem &rItem1, const SfxPoolItem &rItem2)
Definition: findattr.cxx:54
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:315
The root element of a Writer document layout.
Definition: rootfrm.hxx:80
constexpr TypedWhichId< SvxFontItem > RES_CHRATR_FONT(7)
void MoveTo(value_type *pDestRing)
Removes this item from its current ring container and adds it to another ring container.
Definition: ring.hxx:135
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
SwTextAttr const * NextAttr(SwTextNode const **ppNode=nullptr)
Definition: txtfrm.cxx:85
SwDocPositions
Definition: cshtyp.hxx:103
SwContentNode * GetContentNode(bool bPoint=true) const
Definition: pam.hxx:229
const SfxPoolItem * NextItem()
constexpr sal_uInt16 RES_PARATR_BEGIN(RES_TXTATR_END)
SwIndex nContent
Definition: pam.hxx:38
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
const Link< bool, void > & GetOle2Link() const
Definition: doc.hxx:1325
GetHint fnGetHint
Definition: pamtyp.hxx:77
FindRanges
Definition: cshtyp.hxx:90
sal_Int32 GetStart() const
Definition: txatbase.hxx:82
constexpr TypedWhichId< SwFormatCharFormat > RES_TXTATR_CHARFMT(52)
Describes parts of multiple text nodes, which will form a text frame, even when redlines are hidden a...
Definition: txtfrm.hxx:947
constexpr TypedWhichId< SwFormatAutoFormat > RES_TXTATR_AUTOFMT(50)
virtual bool DoesUndo() const =0
Is Undo enabled?
SwPaM * GetNext()
Definition: pam.hxx:264
void Normalize(bool bPointFirst=true)
Normalizes PaM, i.e.
Definition: pam.cxx:538
bool FindAttrImpl(SwPaM &rSearchPam, const SfxPoolItem &rAttr, SwMoveFnCollection const &fnMove, const SwPaM &rRegion, bool bInReadOnly, SwRootFrame const *const pLayout)
Definition: findattr.cxx:914
Base class for various Writer styles.
Definition: format.hxx:43
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:136
SwTextNode const * GetTextNodeForParaProps() const
Definition: txtfrm.cxx:1293
virtual bool IsReplaceMode() const =0
size_t Count() const
Definition: ndhints.hxx:142
virtual SwUndoId StartUndo(SwUndoId const eUndoId, SwRewriter const *const pRewriter)=0
Opens undo block.
sal_uLong FindAll(SwFindParas &, SwDocPositions, SwDocPositions, FindRanges, bool &bCancel)
Definition: swcrsr.cxx:952
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
int i
const SwPosition * GetPoint() const
Definition: pam.hxx:207
const SwTextAttr * GetFrwrdTextHint(const SwpHints &rHtsArr, size_t &rPos, sal_Int32 nContentPos)
Definition: findattr.cxx:79
SwIndex & Assign(SwIndexReg *, sal_Int32)
Definition: index.cxx:206
static bool lcl_Search(const SwContentNode &rCNd, const SfxItemSet &rCmpSet, bool bNoColls)
Definition: findattr.cxx:878
Count
void Exchange()
Definition: pam.cxx:489
SwContentNode * GetContentNode()
Definition: node.hxx:623
sal_uInt16 Count() const
void MakeStartIndex(SwIndex *pIdx)
Definition: node.hxx:399
Marks a character position inside a document model node.
Definition: index.hxx:33
const SfxItemSet * GetItemSet(const SfxPoolItem &rAttr)
Returns the item set associated with a character/inet/auto style.
Definition: atrstck.cxx:133
IDocumentState const & getIDocumentState() const
Definition: doc.cxx:394
constexpr sal_uInt16 RES_GRFATR_END(148)
SwpHints & GetSwpHints()
getters for SwpHints
Definition: ndtxt.hxx:810
bool HasSwAttrSet() const
Definition: node.hxx:452
sal_uInt16 GetWhichByPos(sal_uInt16 nPos) const
bool ReplaceImpl(SwPaM &rCursor, OUString const &rReplacement, bool const bRegExp, SwDoc &rDoc, SwRootFrame const *const pLayout)
Definition: findtxt.cxx:1049
const SfxPoolItem & GetDefaultItem(sal_uInt16 nWhich) const
SfxItemPool * GetPool() const
const int FIND_NO_RING
Definition: swcrsr.hxx:37
const SwPosition * Start() const
Definition: pam.hxx:212
sal_uInt16 GetCurPos() const
constexpr TypedWhichId< SvxColorItem > RES_CHRATR_COLOR(3)
const SfxPoolItem * Put(const SfxPoolItem &rItem, sal_uInt16 nWhich)
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:80
constexpr sal_uInt16 RES_TXTATR_END(RES_TXTATR_NOEND_END)
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:67
SfxItemState
constexpr sal_uInt16 RES_CHRATR_BEGIN(HINT_BEGIN)
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
sal_uLong FindAttrs(const SfxItemSet &rSet, bool bNoCollections, SwDocPositions nStart, SwDocPositions nEnd, bool &bCancel, FindRanges, const i18nutil::SearchOptions2 *pSearchOpt, const SfxItemSet *rReplSet=nullptr, SwRootFrame const *const pLayout=nullptr)
search for attributes
Definition: findattr.cxx:1412
SwMoveFnCollection const & fnMoveForward
SwPam::Move()/Find() default argument.
Definition: paminit.cxx:59
const int FIND_FOUND
Definition: swcrsr.hxx:36
sal_Int32 GetIndex() const
Definition: index.hxx:91
const sal_Int32 * End() const
Definition: txatbase.hxx:148
void * p
static bool FindAttrsImpl(SwPaM &rSearchPam, const SfxItemSet &rSet, bool bNoColls, SwMoveFnCollection const &fnMove, const SwPaM &rRegion, bool bInReadOnly, bool bMoveFirst, SwRootFrame const *const pLayout)
Definition: findattr.cxx:1045
const SwAttrSet & GetSwAttrSet() const
Does node has already its own auto-attributes? Access to SwAttrSet.
Definition: node.hxx:722
const SfxPoolItem & GetAttr() const
Definition: txatbase.hxx:159
const LanguageTag & GetLanguageTag() const
SwFormatColl * GetFormatColl() const
Definition: node.hxx:455
const int FIND_NOT_FOUND
Definition: swcrsr.hxx:35
SwDoc & GetDoc() const
Definition: pam.hxx:243
bool FindTextImpl(SwPaM &rSearchPam, const i18nutil::SearchOptions2 &rSearchOpt, bool bSearchInNotes, utl::TextSearch &rSText, SwMoveFnCollection const &fnMove, const SwPaM &rRegion, bool bInReadOnly, SwRootFrame const *const pLayout, std::unique_ptr< SvxSearchItem > &xSearchItem)
Search.
Definition: findtxt.cxx:373
virtual void SetMark()
Unless this is called, the getter method of Mark will return Point.
Definition: pam.cxx:475
std::pair< const_iterator, bool > insert(Value &&x)
bool IsInvalidItem(const SfxPoolItem *pItem)
o3tl::strong_int< sal_Int32, struct Tag_TextFrameIndex > TextFrameIndex
Denotes a character index in a text frame at a layout level, after extent mapping from a text node at...
bool IsTextNode() const
Definition: node.hxx:644
SwPosition MapViewToModelPos(TextFrameIndex nIndex) const
Definition: txtfrm.cxx:1238
Found
bool(* FnSearchAttr)(const SwTextNode &, SwAttrCheckArr &, SwPaM &)
Definition: findattr.cxx:1043
static void lcl_SetAttrPam(SwPaM &rPam, sal_Int32 nStart, const sal_Int32 *pEnd, const bool bSaveMark)
Definition: findattr.cxx:105
void MakeEndIndex(SwIndex *pIdx)
Definition: node.hxx:400
sal_uInt16 Which() const
static bool lcl_SearchForward(const SwTextNode &rTextNd, SwAttrCheckArr &rCmpArr, SwPaM &rPam)
Definition: findattr.cxx:699
sal_uInt16 nPos
const SfxPoolItem * GetCurItem() const
const SwAttrPool & GetAttrPool() const
Definition: doc.hxx:1315
static bool lcl_SearchAttr(const SwTextNode &rTextNd, SwPaM &rPam, const SfxPoolItem &rCmpItem, SwMoveFnCollection const &fnMove)
search for a text attribute
Definition: findattr.cxx:142
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:844
bool HasHints() const
Definition: ndtxt.hxx:221
static bool lcl_SearchBackward(const SwTextNode &rTextNd, SwAttrCheckArr &rCmpArr, SwPaM &rPam)
Definition: findattr.cxx:787