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