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