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