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