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>
25#include <osl/diagnose.h>
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
50using namespace ::com::sun::star;
51using namespace ::com::sun::star::lang;
52using namespace ::com::sun::star::util;
53
54// Special case for SvxFontItem: only compare the name
55static 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
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
76const 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
89const 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
102static 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()->GetContentIndex();
108 else
109 nContentPos = rPam.GetPoint()->GetContentIndex();
110 bool bTstEnd = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode();
111
112 rPam.GetPoint()->SetContent( nStart );
113 rPam.SetMark(); // Point == GetMark
114
115 // Point points to end of search area or end of attribute
116 if( pEnd )
117 {
118 if( bTstEnd && *pEnd > nContentPos )
119 rPam.GetPoint()->SetContent(nContentPos);
120 else
121 rPam.GetPoint()->SetContent(*pEnd);
122 }
123}
124
125// TODO: provide documentation
138static bool lcl_SearchAttr( const SwTextNode& rTextNd, SwPaM& rPam,
139 const SfxPoolItem& rCmpItem,
140 SwMoveFnCollection const & fnMove)
141{
142 if ( !rTextNd.HasHints() )
143 return false;
144
145 const SwTextAttr *pTextHt = nullptr;
146 bool bForward = &fnMove == &fnMoveForward;
147 size_t nPos = bForward ? 0 : rTextNd.GetSwpHints().Count();
148 sal_Int32 nContentPos = rPam.GetPoint()->GetContentIndex();
149
150 while( nullptr != ( pTextHt=(*fnMove.fnGetHint)(rTextNd.GetSwpHints(),nPos,nContentPos)))
151 if (pTextHt->Which() == rCmpItem.Which())
152 {
153 lcl_SetAttrPam( rPam, pTextHt->GetStart(), pTextHt->End(), bForward );
154 return true;
155 }
156 return false;
157}
158
159namespace {
160
162struct SwSrchChrAttr
163{
164 sal_uInt16 nWhich;
165 sal_Int32 nStt;
166 sal_Int32 nEnd;
167
168 SwSrchChrAttr(): nWhich(0), nStt(0), nEnd(0) {}
169
170 SwSrchChrAttr( const SfxPoolItem& rItem,
171 sal_Int32 nStart, sal_Int32 nAnyEnd )
172 : nWhich( rItem.Which() ), nStt( nStart ), nEnd( nAnyEnd )
173 {}
174};
175
176class SwAttrCheckArr
177{
178 SwSrchChrAttr *m_pFindArr, *m_pStackArr;
179 sal_Int32 m_nNodeStart;
180 sal_Int32 m_nNodeEnd;
181 sal_uInt16 m_nArrStart, m_nArrLen;
182 sal_uInt16 m_nFound, m_nStackCount;
183 SfxItemSet m_aComapeSet;
184 bool m_bNoColls;
185 bool m_bForward;
186
187public:
188 SwAttrCheckArr( const SfxItemSet& rSet, bool bForward, bool bNoCollections );
189 ~SwAttrCheckArr();
190
191 void SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam );
192
194 sal_uInt16 Count() const { return m_aComapeSet.Count(); }
195 bool Found() const { return m_nFound == m_aComapeSet.Count(); }
196 bool CheckStack();
197
198 sal_Int32 Start() const;
199 sal_Int32 End() const;
200
201 sal_Int32 GetNdStt() const { return m_nNodeStart; }
202 sal_Int32 GetNdEnd() const { return m_nNodeEnd; }
203
204 bool SetAttrFwd( const SwTextAttr& rAttr );
205 bool SetAttrBwd( const SwTextAttr& rAttr );
206};
207
208}
209
210SwAttrCheckArr::SwAttrCheckArr( const SfxItemSet& rSet, bool bFwd,
211 bool bNoCollections )
212 : m_nNodeStart(0)
213 , m_nNodeEnd(0)
214 , m_nFound(0)
215 , m_nStackCount(0)
216 , m_aComapeSet( *rSet.GetPool(), svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END-1> )
217 , m_bNoColls(bNoCollections)
218 , m_bForward(bFwd)
219{
220 m_aComapeSet.Put( rSet, false );
221
222 // determine area of Fnd/Stack array (Min/Max)
223 SfxItemIter aIter( m_aComapeSet );
224 m_nArrStart = m_aComapeSet.GetWhichByPos( aIter.GetFirstPos() );
225 m_nArrLen = m_aComapeSet.GetWhichByPos( aIter.GetLastPos() ) - m_nArrStart+1;
226
227 char* pFndChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
228 char* pStackChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
229
230 m_pFindArr = reinterpret_cast<SwSrchChrAttr*>(pFndChar);
231 m_pStackArr = reinterpret_cast<SwSrchChrAttr*>(pStackChar);
232}
233
234SwAttrCheckArr::~SwAttrCheckArr()
235{
236 delete[] reinterpret_cast<char*>(m_pFindArr);
237 delete[] reinterpret_cast<char*>(m_pStackArr);
238}
239
240void SwAttrCheckArr::SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam )
241{
242 std::fill(m_pFindArr, m_pFindArr + m_nArrLen, SwSrchChrAttr());
243 std::fill(m_pStackArr, m_pStackArr + m_nArrLen, SwSrchChrAttr());
244 m_nFound = 0;
245 m_nStackCount = 0;
246
247 if( m_bForward )
248 {
249 m_nNodeStart = rPam.GetPoint()->GetContentIndex();
250 m_nNodeEnd = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode()
251 ? rPam.GetMark()->GetContentIndex()
252 : rTextNd.GetText().getLength();
253 }
254 else
255 {
256 m_nNodeEnd = rPam.GetPoint()->GetContentIndex();
257 m_nNodeStart = rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode()
258 ? rPam.GetMark()->GetContentIndex()
259 : 0;
260 }
261
262 if( m_bNoColls && !rTextNd.HasSwAttrSet() )
263 return ;
264
265 const SfxItemSet& rSet = rTextNd.GetSwAttrSet();
266
267 SfxItemIter aIter( m_aComapeSet );
268 const SfxPoolItem* pItem = aIter.GetCurItem();
269 const SfxPoolItem* pFndItem;
270 sal_uInt16 nWhich;
271
272 do
273 {
274 if( IsInvalidItem( pItem ) )
275 {
276 nWhich = m_aComapeSet.GetWhichByPos( aIter.GetCurPos() );
277 if( RES_TXTATR_END <= nWhich )
278 break; // end of text attributes
279
280 if( SfxItemState::SET == rSet.GetItemState( nWhich, !m_bNoColls, &pFndItem )
281 && !CmpAttr( *pFndItem, rSet.GetPool()->GetDefaultItem( nWhich ) ))
282 {
283 m_pFindArr[ nWhich - m_nArrStart ] =
284 SwSrchChrAttr( *pFndItem, m_nNodeStart, m_nNodeEnd );
285 m_nFound++;
286 }
287 }
288 else
289 {
290 nWhich = pItem->Which();
291 if( RES_TXTATR_END <= nWhich )
292 break; // end of text attributes
293
294 if( CmpAttr( rSet.Get( nWhich, !m_bNoColls ), *pItem ) )
295 {
296 m_pFindArr[ nWhich - m_nArrStart ] =
297 SwSrchChrAttr( *pItem, m_nNodeStart, m_nNodeEnd );
298 m_nFound++;
299 }
300 }
301
302 pItem = aIter.NextItem();
303 } while (pItem);
304}
305
306static bool
307lcl_IsAttributeIgnorable(sal_Int32 const nNdStart, sal_Int32 const nNdEnd,
308 SwSrchChrAttr const& rTmp)
309{
310 // #i115528#: if there is a paragraph attribute, it has been added by the
311 // SwAttrCheckArr ctor, and nFound is 1.
312 // if the paragraph is entirely covered by hints that override the paragraph
313 // attribute, then this function must find an attribute to decrement nFound!
314 // so check for an empty search range, let attributes that start/end there
315 // cover it, and hope for the best...
316 return ((nNdEnd == nNdStart)
317 ? ((rTmp.nEnd < nNdStart) || (nNdEnd < rTmp.nStt))
318 : ((rTmp.nEnd <= nNdStart) || (nNdEnd <= rTmp.nStt)));
319}
320
321bool SwAttrCheckArr::SetAttrFwd( const SwTextAttr& rAttr )
322{
323 SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
324
325 // ignore all attributes not in search range
326 if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
327 {
328 return Found();
329 }
330
331 const SfxPoolItem* pItem;
332 // here we explicitly also search in character templates
333 sal_uInt16 nWhch = rAttr.Which();
334 std::optional<SfxWhichIter> oIter;
335 const SfxPoolItem* pTmpItem = nullptr;
336 const SfxItemSet* pSet = nullptr;
337 if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
338 {
339 if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
340 return Found();
341 pTmpItem = nullptr;
342 pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
343 if ( pSet )
344 {
345 oIter.emplace( *pSet );
346 nWhch = oIter->FirstWhich();
347 while( nWhch &&
348 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
349 nWhch = oIter->NextWhich();
350 if( !nWhch )
351 pTmpItem = nullptr;
352 }
353 }
354 else
355 pTmpItem = &rAttr.GetAttr();
356
357 while( pTmpItem )
358 {
359 SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
360 if( SfxItemState::DONTCARE == eState || SfxItemState::SET == eState )
361 {
362 sal_uInt16 n;
363 SwSrchChrAttr* pCmp;
364
365 // first delete all up to start position that are already invalid
366 SwSrchChrAttr* pArrPtr;
367 if( m_nFound )
368 for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen;
369 ++n, ++pArrPtr )
370 if( pArrPtr->nWhich && pArrPtr->nEnd <= aTmp.nStt )
371 {
372 pArrPtr->nWhich = 0; // deleted
373 m_nFound--;
374 }
375
376 // delete all up to start position that are already invalid and
377 // move all "open" ones (= stick out over start position) from stack
378 // into FndSet
379 if( m_nStackCount )
380 for( pArrPtr = m_pStackArr, n=0; n < m_nArrLen; ++n, ++pArrPtr )
381 {
382 if( !pArrPtr->nWhich )
383 continue;
384
385 if( pArrPtr->nEnd <= aTmp.nStt )
386 {
387 pArrPtr->nWhich = 0; // deleted
388 if( !--m_nStackCount )
389 break;
390 }
391 else if( pArrPtr->nStt <= aTmp.nStt )
392 {
393 pCmp = &m_pFindArr[ n ];
394 if( pCmp->nWhich )
395 {
396 if( pCmp->nEnd < pArrPtr->nEnd ) // extend
397 pCmp->nEnd = pArrPtr->nEnd;
398 }
399 else
400 {
401 *pCmp = *pArrPtr;
402 m_nFound++;
403 }
404 pArrPtr->nWhich = 0;
405 if( !--m_nStackCount )
406 break;
407 }
408 }
409
410 bool bContinue = false;
411
412 if( SfxItemState::DONTCARE == eState )
413 {
414 // Will the attribute become valid?
415 if( !CmpAttr( m_aComapeSet.GetPool()->GetDefaultItem( nWhch ),
416 *pTmpItem ))
417 {
418 // search attribute and extend if needed
419 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
420 if( !pCmp->nWhich )
421 {
422 *pCmp = aTmp; // not found, insert
423 m_nFound++;
424 }
425 else if( pCmp->nEnd < aTmp.nEnd ) // extend?
426 pCmp->nEnd = aTmp.nEnd;
427
428 bContinue = true;
429 }
430 }
431 // Will the attribute become valid?
432 else if( CmpAttr( *pItem, *pTmpItem ) )
433 {
434 m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
435 ++m_nFound;
436 bContinue = true;
437 }
438
439 // then is has to go on the stack
440 if( !bContinue )
441 {
442 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
443 if (pCmp->nWhich )
444 {
445 // exists on stack, only if it is even bigger
446 if( pCmp->nEnd > aTmp.nEnd )
447 {
448 OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich,
449 "slot on stack is still in use" );
450
451 if( aTmp.nStt <= pCmp->nStt )
452 pCmp->nStt = aTmp.nEnd;
453 else
454 pCmp->nEnd = aTmp.nStt;
455
456 m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
457 m_nStackCount++;
458 }
459 pCmp->nWhich = 0;
460 m_nFound--;
461 }
462 }
463 }
464 if( oIter )
465 {
466 assert(pSet && "otherwise no oIter");
467 nWhch = oIter->NextWhich();
468 while( nWhch &&
469 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
470 nWhch = oIter->NextWhich();
471 if( !nWhch )
472 break;
473 }
474 else
475 break;
476 }
477 oIter.reset();
478 return Found();
479}
480
481bool SwAttrCheckArr::SetAttrBwd( const SwTextAttr& rAttr )
482{
483 SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
484
485 // ignore all attributes not in search range
486 if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
487 {
488 return Found();
489 }
490
491 const SfxPoolItem* pItem;
492 // here we explicitly also search in character templates
493 sal_uInt16 nWhch = rAttr.Which();
494 std::optional<SfxWhichIter> oIter;
495 const SfxPoolItem* pTmpItem = nullptr;
496 const SfxItemSet* pSet = nullptr;
497 if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
498 {
499 if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
500 return Found();
501
502 pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
503 if ( pSet )
504 {
505 oIter.emplace( *pSet );
506 nWhch = oIter->FirstWhich();
507 while( nWhch &&
508 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
509 nWhch = oIter->NextWhich();
510 if( !nWhch )
511 pTmpItem = nullptr;
512 }
513 }
514 else
515 pTmpItem = &rAttr.GetAttr();
516
517 while( pTmpItem )
518 {
519 SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
520 if( SfxItemState::DONTCARE == eState || SfxItemState::SET == eState )
521 {
522 sal_uInt16 n;
523 SwSrchChrAttr* pCmp;
524
525 // first delete all up to start position that are already invalid
526 SwSrchChrAttr* pArrPtr;
527 if( m_nFound )
528 for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
529 if( pArrPtr->nWhich && pArrPtr->nStt >= aTmp.nEnd )
530 {
531 pArrPtr->nWhich = 0; // deleted
532 m_nFound--;
533 }
534
535 // delete all up to start position that are already invalid and
536 // move all "open" ones (= stick out over start position) from stack
537 // into FndSet
538 if( m_nStackCount )
539 for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
540 {
541 if( !pArrPtr->nWhich )
542 continue;
543
544 if( pArrPtr->nStt >= aTmp.nEnd )
545 {
546 pArrPtr->nWhich = 0; // deleted
547 if( !--m_nStackCount )
548 break;
549 }
550 else if( pArrPtr->nEnd >= aTmp.nEnd )
551 {
552 pCmp = &m_pFindArr[ n ];
553 if( pCmp->nWhich )
554 {
555 if( pCmp->nStt > pArrPtr->nStt ) // extend
556 pCmp->nStt = pArrPtr->nStt;
557 }
558 else
559 {
560 *pCmp = *pArrPtr;
561 m_nFound++;
562 }
563 pArrPtr->nWhich = 0;
564 if( !--m_nStackCount )
565 break;
566 }
567 }
568
569 bool bContinue = false;
570 if( SfxItemState::DONTCARE == eState )
571 {
572 // Will the attribute become valid?
573 if( !CmpAttr( m_aComapeSet.GetPool()->GetDefaultItem( nWhch ),
574 *pTmpItem ) )
575 {
576 // search attribute and extend if needed
577 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
578 if( !pCmp->nWhich )
579 {
580 *pCmp = aTmp; // not found, insert
581 m_nFound++;
582 }
583 else if( pCmp->nStt > aTmp.nStt ) // extend?
584 pCmp->nStt = aTmp.nStt;
585
586 bContinue = true;
587 }
588 }
589 // Will the attribute become valid?
590 else if( CmpAttr( *pItem, *pTmpItem ))
591 {
592 m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
593 ++m_nFound;
594 bContinue = true;
595 }
596
597 // then is has to go on the stack
598 if( !bContinue )
599 {
600 pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
601 if( pCmp->nWhich )
602 {
603 // exists on stack, only if it is even bigger
604 if( pCmp->nStt < aTmp.nStt )
605 {
606 OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich,
607 "slot on stack is still in use" );
608
609 if( aTmp.nEnd <= pCmp->nEnd )
610 pCmp->nEnd = aTmp.nStt;
611 else
612 pCmp->nStt = aTmp.nEnd;
613
614 m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
615 m_nStackCount++;
616 }
617 pCmp->nWhich = 0;
618 m_nFound--;
619 }
620 }
621 }
622 if( oIter )
623 {
624 assert(pSet && "otherwise no oIter");
625 nWhch = oIter->NextWhich();
626 while( nWhch &&
627 SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
628 nWhch = oIter->NextWhich();
629 if( !nWhch )
630 break;
631 }
632 else
633 break;
634 }
635 oIter.reset();
636 return Found();
637}
638
639sal_Int32 SwAttrCheckArr::Start() const
640{
641 sal_Int32 nStart = m_nNodeStart;
642 SwSrchChrAttr* pArrPtr = m_pFindArr;
643 for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
644 if( pArrPtr->nWhich && pArrPtr->nStt > nStart )
645 nStart = pArrPtr->nStt;
646
647 return nStart;
648}
649
650sal_Int32 SwAttrCheckArr::End() const
651{
652 SwSrchChrAttr* pArrPtr = m_pFindArr;
653 sal_Int32 nEnd = m_nNodeEnd;
654 for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
655 if( pArrPtr->nWhich && pArrPtr->nEnd < nEnd )
656 nEnd = pArrPtr->nEnd;
657
658 return nEnd;
659}
660
661bool SwAttrCheckArr::CheckStack()
662{
663 if( !m_nStackCount )
664 return false;
665
666 sal_uInt16 n;
667 const sal_Int32 nSttPos = Start();
668 const sal_Int32 nEndPos = End();
669 SwSrchChrAttr* pArrPtr;
670 for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
671 {
672 if( !pArrPtr->nWhich )
673 continue;
674
675 if( m_bForward ? pArrPtr->nEnd <= nSttPos : pArrPtr->nStt >= nEndPos )
676 {
677 pArrPtr->nWhich = 0; // deleted
678 if( !--m_nStackCount )
679 return m_nFound == m_aComapeSet.Count();
680 }
681 else if( m_bForward ? pArrPtr->nStt < nEndPos : pArrPtr->nEnd > nSttPos )
682 {
683 // move all "open" ones (= stick out over start position) into FndSet
684 OSL_ENSURE( !m_pFindArr[ n ].nWhich, "slot in array is already in use" );
685 m_pFindArr[ n ] = *pArrPtr;
686 pArrPtr->nWhich = 0;
687 m_nFound++;
688 if( !--m_nStackCount )
689 return m_nFound == m_aComapeSet.Count();
690 }
691 }
692 return m_nFound == m_aComapeSet.Count();
693}
694
695static bool lcl_SearchForward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
696 SwPaM& rPam )
697{
698 sal_Int32 nEndPos;
699 rCmpArr.SetNewSet( rTextNd, rPam );
700 if( !rTextNd.HasHints() )
701 {
702 if( !rCmpArr.Found() )
703 return false;
704 nEndPos = rCmpArr.GetNdEnd();
705 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
706 return true;
707 }
708
709 const SwpHints& rHtArr = rTextNd.GetSwpHints();
710 const SwTextAttr* pAttr;
711 size_t nPos = 0;
712
713 // if everything is already there then check with which it will be ended
714 if( rCmpArr.Found() )
715 {
716 for( ; nPos < rHtArr.Count(); ++nPos )
717 {
718 pAttr = rHtArr.Get( nPos );
719 if( !rCmpArr.SetAttrFwd( *pAttr ) )
720 {
721 if( rCmpArr.GetNdStt() < pAttr->GetStart() )
722 {
723 // found end
724 auto nTmpStart = pAttr->GetStart();
725 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(),
726 &nTmpStart, true );
727 return true;
728 }
729 // continue search
730 break;
731 }
732 }
733
734 if( nPos == rHtArr.Count() && rCmpArr.Found() )
735 {
736 // found
737 nEndPos = rCmpArr.GetNdEnd();
738 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
739 return true;
740 }
741 }
742
743 sal_Int32 nSttPos;
744 for( ; nPos < rHtArr.Count(); ++nPos )
745 {
746 pAttr = rHtArr.Get( nPos );
747 if( rCmpArr.SetAttrFwd( *pAttr ) )
748 {
749 // Do multiple start at that position? Do also check those:
750 nSttPos = pAttr->GetStart();
751 while( ++nPos < rHtArr.Count() )
752 {
753 pAttr = rHtArr.Get( nPos );
754 if( nSttPos != pAttr->GetStart() || !rCmpArr.SetAttrFwd( *pAttr ) )
755 break;
756 }
757
758 if( !rCmpArr.Found() )
759 continue;
760
761 // then we have our search area
762 nSttPos = rCmpArr.Start();
763 nEndPos = rCmpArr.End();
764 if( nSttPos > nEndPos )
765 return false;
766
767 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
768 return true;
769 }
770 }
771
772 if( !rCmpArr.CheckStack() )
773 return false;
774 nSttPos = rCmpArr.Start();
775 nEndPos = rCmpArr.End();
776 if( nSttPos > nEndPos )
777 return false;
778
779 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
780 return true;
781}
782
783static bool lcl_SearchBackward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
784 SwPaM& rPam )
785{
786 sal_Int32 nEndPos;
787 rCmpArr.SetNewSet( rTextNd, rPam );
788 if( !rTextNd.HasHints() )
789 {
790 if( !rCmpArr.Found() )
791 return false;
792 nEndPos = rCmpArr.GetNdEnd();
793 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
794 return true;
795 }
796
797 const SwpHints& rHtArr = rTextNd.GetSwpHints();
798 const SwTextAttr* pAttr;
799 size_t nPos = rHtArr.Count();
800 sal_Int32 nSttPos;
801
802 // if everything is already there then check with which it will be ended
803 if( rCmpArr.Found() )
804 {
805 while( nPos )
806 {
807 pAttr = rHtArr.GetSortedByEnd( --nPos );
808 if( !rCmpArr.SetAttrBwd( *pAttr ) )
809 {
810 nSttPos = pAttr->GetAnyEnd();
811 if( nSttPos < rCmpArr.GetNdEnd() )
812 {
813 // found end
814 nEndPos = rCmpArr.GetNdEnd();
815 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
816 return true;
817 }
818
819 // continue search
820 break;
821 }
822 }
823
824 if( !nPos && rCmpArr.Found() )
825 {
826 // found
827 nEndPos = rCmpArr.GetNdEnd();
828 lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
829 return true;
830 }
831 }
832
833 while( nPos )
834 {
835 pAttr = rHtArr.GetSortedByEnd( --nPos );
836 if( rCmpArr.SetAttrBwd( *pAttr ) )
837 {
838 // Do multiple start at that position? Do also check those:
839 if( nPos )
840 {
841 nEndPos = pAttr->GetAnyEnd();
842 while( --nPos )
843 {
844 pAttr = rHtArr.GetSortedByEnd( nPos );
845 if( nEndPos != pAttr->GetAnyEnd() || !rCmpArr.SetAttrBwd( *pAttr ) )
846 break;
847 }
848 }
849 if( !rCmpArr.Found() )
850 continue;
851
852 // then we have our search area
853 nSttPos = rCmpArr.Start();
854 nEndPos = rCmpArr.End();
855 if( nSttPos > nEndPos )
856 return false;
857
858 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
859 return true;
860 }
861 }
862
863 if( !rCmpArr.CheckStack() )
864 return false;
865 nSttPos = rCmpArr.Start();
866 nEndPos = rCmpArr.End();
867 if( nSttPos > nEndPos )
868 return false;
869
870 lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
871 return true;
872}
873
874static bool lcl_Search( const SwContentNode& rCNd, const SfxItemSet& rCmpSet, bool bNoColls )
875{
876 // search only hard attribution?
877 if( bNoColls && !rCNd.HasSwAttrSet() )
878 return false;
879
880 const SfxItemSet& rNdSet = rCNd.GetSwAttrSet();
881 SfxItemIter aIter( rCmpSet );
882 const SfxPoolItem* pItem = aIter.GetCurItem();
883 const SfxPoolItem* pNdItem;
884 sal_uInt16 nWhich;
885
886 do
887 {
888 if( IsInvalidItem( pItem ))
889 {
890 nWhich = rCmpSet.GetWhichByPos( aIter.GetCurPos() );
891 if( SfxItemState::SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
892 || CmpAttr( *pNdItem, rNdSet.GetPool()->GetDefaultItem( nWhich ) ))
893 return false;
894 }
895 else
896 {
897 nWhich = pItem->Which();
898
899 if( !CmpAttr( rNdSet.Get( nWhich, !bNoColls ), *pItem ))
900 return false;
901 }
902
903 pItem = aIter.NextItem();
904 } while (pItem);
905 return true; // found
906}
907
908namespace sw {
909
910bool FindAttrImpl(SwPaM & rSearchPam,
911 const SfxPoolItem& rAttr, SwMoveFnCollection const & fnMove,
912 const SwPaM & rRegion, bool bInReadOnly,
913 SwRootFrame const*const pLayout)
914{
915 // determine which attribute is searched:
916 const sal_uInt16 nWhich = rAttr.Which();
917 bool bCharAttr = isCHRATR(nWhich) || isTXTATR(nWhich);
918 assert(isTXTATR(nWhich)); // sw_redlinehide: only works for non-formatting hints such as needed in UpdateFields; use FindAttrsImpl for others
919
920 std::optional<SwPaM> oPam;
921 sw::MakeRegion(fnMove, rRegion, oPam);
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 ? oPam->GetPoint()->GetContentIndex() == oPam->GetPointContentNode()->Len()
931 : !oPam->GetPoint()->GetContentIndex() )
932 {
933 if( !(*fnMove.fnPos)( oPam->GetPoint(), false ))
934 {
935 return false;
936 }
937 SwContentNode *pNd = oPam->GetPointContentNode();
938 oPam->GetPoint()->SetContent( bSrchForward ? 0 : pNd->Len() );
939 }
940
941 while (nullptr != (pNode = ::GetNode(*oPam, 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() < oPam->GetPoint()->GetNodeIndex()
964 || (pAttrNode->GetIndex() == oPam->GetPoint()->GetNodeIndex()
965 && pAttr->GetStart() < oPam->GetPoint()->GetContentIndex())
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 && (oPam->GetPoint()->GetNodeIndex() < pAttrNode->GetIndex()
977 || (oPam->GetPoint()->GetNodeIndex() == pAttrNode->GetIndex()
978 && oPam->GetPoint()->GetContentIndex() <= pAttr->GetStart())
979 || pAttr->Which() != nWhich));
980 }
981 if (pAttr)
982 {
983 assert(pAttrNode);
984 oPam->GetPoint()->Assign(*pAttrNode);
985 lcl_SetAttrPam(*oPam, pAttr->GetStart(), pAttr->End(), bSrchForward);
986 bFound = true;
987 break;
988 }
989 }
990 else if (!pLayout && pNode->GetTextNode()->HasHints() &&
991 lcl_SearchAttr(*pNode->GetTextNode(), *oPam, 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() = *oPam->GetPoint();
1000 *rSearchPam.GetMark() = *oPam->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 rSearchPam.GetPoint()->SetContent(pNode->Len());
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
1040typedef bool (*FnSearchAttr)( const SwTextNode&, SwAttrCheckArr&, SwPaM& );
1041
1042static 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::optional<SwPaM> oPam;
1048 sw::MakeRegion(fnMove, rRegion, oPam);
1049
1050 bool bFound = false;
1051 bool bFirst = true;
1052 const bool bSrchForward = &fnMove == &fnMoveForward;
1053 SwContentNode * pNode;
1055
1056 // check which text/char attributes are searched
1057 SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls );
1058 SfxItemSetFixed<RES_PARATR_BEGIN, RES_GRFATR_END-1> aOtherSet( rSearchPam.GetDoc().GetAttrPool() );
1059 aOtherSet.Put( rSet, false ); // got all invalid items
1060
1061 FnSearchAttr fnSearch = bSrchForward
1064
1065 // if at beginning/end then move it out of the node
1066 if( bMoveFirst &&
1067 ( bSrchForward
1068 ? oPam->GetPoint()->GetContentIndex() == oPam->GetPointContentNode()->Len()
1069 : !oPam->GetPoint()->GetContentIndex() ) )
1070 {
1071 if( !(*fnMove.fnPos)( oPam->GetPoint(), false ))
1072 {
1073 return false;
1074 }
1075 SwContentNode *pNd = oPam->GetPointContentNode();
1076 oPam->GetPoint()->SetContent( bSrchForward ? 0 : pNd->Len() );
1077 }
1078
1079 while (nullptr != (pNode = ::GetNode(*oPam, 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(*oPam->Start());
1105 SwPosition const& rEnd(*oPam->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.GetNodeIndex() <= rPropsNode.GetIndex()
1111 && rPropsNode.GetIndex() <= rEnd.GetNodeIndex())
1112 {
1113 SwPaM tmp(rPropsNode, 0, rPropsNode, 0);
1114 bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, tmp);
1115 if (bFound)
1116 {
1117 *oPam = 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.GetNodeIndex()
1132 || rEnd.GetNodeIndex() < rExtent.pNode->GetIndex())
1133 {
1134 continue;
1135 }
1136 sal_Int32 const nStart(rExtent.pNode == &rStart.GetNode()
1137 ? rStart.GetContentIndex()
1138 : 0);
1139 if (rExtent.nEnd <= nStart)
1140 {
1141 continue;
1142 }
1143 sal_Int32 const nEnd(rExtent.pNode == &rEnd.GetNode()
1144 ? rEnd.GetContentIndex()
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 *oPam = tmp;
1158 break;
1159 }
1160 }
1161 }
1162 }
1163 else
1164 {
1165 bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, *oPam);
1166 }
1167 if (bFound)
1168 {
1169 // set to the values of the attribute
1170 rSearchPam.SetMark();
1171 *rSearchPam.GetPoint() = *oPam->GetPoint();
1172 *rSearchPam.GetMark() = *oPam->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() = *oPam->GetPoint();
1197 rSearchPam.SetMark();
1198 *rSearchPam.GetMark() = pFrame->MapViewToModelPos(
1199 TextFrameIndex(bSrchForward ? pFrame->GetText().getLength() : 0));
1200 }
1201 else
1202 {
1203 *rSearchPam.GetPoint() = *oPam->GetPoint();
1204 rSearchPam.SetMark();
1205 if (bSrchForward)
1206 {
1207 rSearchPam.GetPoint()->SetContent(pNode->Len());
1208 }
1209 else
1210 {
1211 rSearchPam.GetPoint()->SetContent(0);
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
1229namespace {
1230
1232struct 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
1261int 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 SwPosition& rSttCntPos = *rCursor.Start();
1332 const sal_Int32 nSttCnt = rSttCntPos.GetContentIndex();
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 rSttCntPos.SetContent(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
1402bool SwFindParaAttr::IsReplaceMode() const
1403{
1404 return ( pSearchOpt && !pSearchOpt->replaceString.isEmpty() ) ||
1405 ( pReplSet && pReplSet->Count() );
1406}
1407
1409sal_Int32 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 {
1427 rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REPLACE, nullptr );
1428 }
1429
1430 SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt,
1431 pReplSet, *this, pLayout );
1432
1433 sal_Int32 nRet = FindAll( aSwFindParaAttr, nStart, nEnd, eFndRngs, bCancel );
1434 rDoc.SetOle2Link( aLnk );
1435 if( nRet && bReplace )
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: */
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...
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.
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
const SfxPoolItem * GetCurItem() const
sal_uInt16 GetCurPos() const
const SfxPoolItem * NextItem()
const SfxPoolItem & GetDefaultItem(sal_uInt16 nWhich) const
SfxItemPool * GetPool() const
sal_uInt16 Count() const
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
const SfxPoolItem * Put(const SfxPoolItem &rItem, sal_uInt16 nWhich)
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
sal_uInt16 GetWhichByPos(sal_uInt16 nPos) const
T & StaticWhichCast(TypedWhichId< T > nId)
sal_uInt16 Which() const
const LanguageTag & GetLanguageTag() const
SwFormatColl * GetFormatColl() const
Definition: node.hxx:497
bool HasSwAttrSet() const
Definition: node.hxx:494
SwContentFrame * getLayoutFrame(const SwRootFrame *, const SwPosition *pPos=nullptr, std::pair< Point, bool > const *pViewPosAndCalcFrame=nullptr) const
Definition: node.cxx:1223
const SwAttrSet & GetSwAttrSet() const
Does node has already its own auto-attributes? Access to SwAttrSet.
Definition: node.hxx:727
virtual sal_Int32 Len() const
Definition: node.cxx:1256
sal_Int32 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
sal_Int32 FindAll(SwFindParas &, SwDocPositions, SwDocPositions, FindRanges, bool &bCancel)
Definition: swcrsr.cxx:949
Definition: doc.hxx:197
IDocumentState const & getIDocumentState() const
Definition: doc.cxx:408
void SetOle2Link(const Link< bool, void > &rLink)
Definition: doc.hxx:1346
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:329
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:158
const Link< bool, void > & GetOle2Link() const
Definition: doc.hxx:1347
const SwAttrPool & GetAttrPool() const
Definition: doc.hxx:1337
Base class for various Writer styles.
Definition: format.hxx:47
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:901
SwNodeOffset GetIndex() const
Definition: node.hxx:312
bool IsTextNode() const
Definition: node.hxx:190
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:188
const SwPosition * GetMark() const
Definition: pam.hxx:255
virtual void SetMark()
Unless this is called, the getter method of Mark will return Point.
Definition: pam.cxx:643
void Exchange()
Definition: pam.hxx:242
void Normalize(bool bPointFirst=true)
Normalizes PaM, i.e.
Definition: pam.cxx:689
SwDoc & GetDoc() const
Definition: pam.hxx:291
const SwPosition * GetPoint() const
Definition: pam.hxx:253
const SwPosition * Start() const
Definition: pam.hxx:258
The root element of a Writer document layout.
Definition: rootfrm.hxx:85
A wrapper around SfxPoolItem to store the start position of (usually) a text portion,...
Definition: txatbase.hxx:44
const SfxPoolItem & GetAttr() const
Definition: txatbase.hxx:167
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:161
const sal_Int32 * End() const
Definition: txatbase.hxx:156
sal_Int32 GetStart() const
Definition: txatbase.hxx:88
sal_uInt16 Which() const
Definition: txatbase.hxx:116
Represents the visualization of a paragraph.
Definition: txtfrm.hxx:168
SwPosition MapViewToModelPos(TextFrameIndex nIndex) const
Definition: txtfrm.cxx:1333
sw::MergedPara * GetMergedPara()
Definition: txtfrm.hxx:465
const OUString & GetText() const
Returns the text portion we want to edit (for inline see underneath)
Definition: txtfrm.cxx:1380
SwTextNode const * GetTextNodeForParaProps() const
Definition: txtfrm.cxx:1390
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:112
bool HasHints() const
Definition: ndtxt.hxx:254
SwpHints & GetSwpHints()
getters for SwpHints
Definition: ndtxt.hxx:867
const OUString & GetText() const
Definition: ndtxt.hxx:244
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:68
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
size_t Count() const
Definition: ndhints.hxx:142
SwTextAttr * GetSortedByEnd(size_t nPos) const
Definition: ndhints.hxx:158
std::pair< const_iterator, bool > insert(Value &&x)
SwTextAttr const * PrevAttr(SwTextNode const **ppNode=nullptr)
Definition: txtfrm.cxx:228
SwTextAttr const * NextAttr(SwTextNode const **ppNode=nullptr)
Definition: txtfrm.cxx:91
SwDocPositions
Definition: cshtyp.hxx:104
FindRanges
Definition: cshtyp.hxx:91
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
const SwTextAttr * GetBkwrdTextHint(const SwpHints &rHtsArr, size_t &rPos, sal_Int32 nContentPos)
Definition: findattr.cxx:89
static bool CmpAttr(const SfxPoolItem &rItem1, const SfxPoolItem &rItem2)
Definition: findattr.cxx:55
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
static bool lcl_SearchBackward(const SwTextNode &rTextNd, SwAttrCheckArr &rCmpArr, SwPaM &rPam)
Definition: findattr.cxx:783
static bool lcl_Search(const SwContentNode &rCNd, const SfxItemSet &rCmpSet, bool bNoColls)
Definition: findattr.cxx:874
static bool lcl_SearchAttr(const SwTextNode &rTextNd, SwPaM &rPam, const SfxPoolItem &rCmpItem, SwMoveFnCollection const &fnMove)
search for a text attribute
Definition: findattr.cxx:138
static bool lcl_IsAttributeIgnorable(sal_Int32 const nNdStart, sal_Int32 const nNdEnd, SwSrchChrAttr const &rTmp)
Definition: findattr.cxx:307
const SwTextAttr * GetFrwrdTextHint(const SwpHints &rHtsArr, size_t &rPos, sal_Int32 nContentPos)
Definition: findattr.cxx:76
static bool lcl_SearchForward(const SwTextNode &rTextNd, SwAttrCheckArr &rCmpArr, SwPaM &rPam)
Definition: findattr.cxx:695
bool isTXTATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:480
bool isCHRATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:468
constexpr sal_uInt16 RES_PARATR_BEGIN(RES_TXTATR_END)
constexpr TypedWhichId< SwFormatAutoFormat > RES_TXTATR_AUTOFMT(50)
constexpr TypedWhichId< SwFormatPageDesc > RES_PAGEDESC(99)
constexpr sal_uInt16 RES_CHRATR_BEGIN(HINT_BEGIN)
constexpr TypedWhichId< SwFormatCharFormat > RES_TXTATR_CHARFMT(52)
constexpr TypedWhichId< SvxFontItem > RES_CHRATR_FONT(7)
constexpr sal_uInt16 RES_GRFATR_END(156)
constexpr TypedWhichId< SvxColorItem > RES_CHRATR_COLOR(3)
constexpr sal_uInt16 RES_TXTATR_END(RES_TXTATR_NOEND_END)
void * p
sal_Int64 n
sal_uInt16 nPos
const SfxItemSet * GetItemSet(const SfxPoolItem &rAttr)
Returns the item set associated with a character/inet/auto style.
Definition: atrstck.cxx:133
int i
Dialog to specify the properties of date form field.
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:369
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:1108
void MakeRegion(SwMoveFnCollection const &fnMove, const SwPaM &rOrigRg, std::optional< SwPaM > &rPam)
make a new region
Definition: pam.cxx:676
bool FindAttrImpl(SwPaM &rSearchPam, const SfxPoolItem &rAttr, SwMoveFnCollection const &fnMove, const SwPaM &rRegion, bool bInReadOnly, SwRootFrame const *const pLayout)
Definition: findattr.cxx:910
bool ReplaceImpl(SwPaM &rCursor, OUString const &rReplacement, bool const bRegExp, SwDoc &rDoc, SwRootFrame const *const pLayout)
Definition: findtxt.cxx:1040
SwNodeOffset min(const SwNodeOffset &a, const SwNodeOffset &b)
Definition: nodeoffset.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:1043
SwMoveFnCollection const & fnMoveForward
SwPam::Move()/Find() default argument.
Definition: paminit.cxx:61
SfxItemState
bool IsInvalidItem(const SfxPoolItem *pItem)
static SfxItemSet & rSet
virtual bool IsReplaceMode() const =0
virtual int DoFind(SwPaM &, SwMoveFnCollection const &, const SwPaM &, bool, std::unique_ptr< SvxSearchItem > &xSearchItem)=0
GetHint fnGetHint
Definition: pamtyp.hxx:84
Marks a position in the document model.
Definition: pam.hxx:38
SwNode & GetNode() const
Definition: pam.hxx:81
void SetContent(sal_Int32 nContentIndex)
Set content index, only valid to call this if the position points to a SwContentNode subclass.
Definition: pam.cxx:267
SwNodeOffset GetNodeIndex() const
Definition: pam.hxx:78
sal_Int32 GetContentIndex() const
Definition: pam.hxx:85
Describes parts of multiple text nodes, which will form a text frame, even when redlines are hidden a...
Definition: txtfrm.hxx:991
std::vector< Extent > extents
Definition: txtfrm.hxx:993
const int FIND_NOT_FOUND
Definition: swcrsr.hxx:35
const int FIND_FOUND
Definition: swcrsr.hxx:36
const int FIND_NO_RING
Definition: swcrsr.hxx:37
Count