LibreOffice Module sw (master) 1
thints.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 <sal/config.h>
21#include <sal/log.hxx>
22
24#include <hintids.hxx>
25#include <editeng/rsiditem.hxx>
26#include <osl/diagnose.h>
27#include <svl/whiter.hxx>
28#include <svl/itemiter.hxx>
30#include <editeng/langitem.hxx>
31#include <editeng/lrspitem.hxx>
32#include <txtinet.hxx>
33#include <txtflcnt.hxx>
34#include <fmtfld.hxx>
35#include <fmtrfmrk.hxx>
36#include <fmtanchr.hxx>
37#include <fmtinfmt.hxx>
38#include <txtatr.hxx>
39#include <fchrfmt.hxx>
40#include <fmtautofmt.hxx>
41#include <fmtflcnt.hxx>
42#include <fmtftn.hxx>
43#include <txttxmrk.hxx>
44#include <txtrfmrk.hxx>
45#include <txtftn.hxx>
46#include <textlinebreak.hxx>
47#include <txtfld.hxx>
48#include <txtannotationfld.hxx>
49#include <unotools/fltrcfg.hxx>
50#include <charfmt.hxx>
51#include <frmfmt.hxx>
52#include <ftnidx.hxx>
53#include <fmtruby.hxx>
54#include <fmtmeta.hxx>
57#include <breakit.hxx>
58#include <doc.hxx>
59#include <IDocumentUndoRedo.hxx>
63#include <fldbas.hxx>
64#include <pam.hxx>
65#include <ndtxt.hxx>
66#include <txtfrm.hxx>
67#include <rootfrm.hxx>
68#include <rolbck.hxx>
69#include <ddefld.hxx>
70#include <docufld.hxx>
71#include <expfld.hxx>
72#include <usrfld.hxx>
73#include <poolfmt.hxx>
74#include <istyleaccess.hxx>
75#include <docsh.hxx>
76#include <algorithm>
77#include <map>
78#include <memory>
79
80#include <rdfhelper.hxx>
81#include <hints.hxx>
82
83#ifdef DBG_UTIL
84#define CHECK Check(true);
85#define CHECK_NOTMERGED Check(false);
86#else
87#define CHECK_NOTMERGED
88#endif
89
90using namespace ::com::sun::star::i18n;
91
93 : m_rParent(rParent)
94 , m_pHistory(nullptr)
95 , m_bInSplitNode(false)
96 , m_bCalcHiddenParaField(false)
97 , m_bHiddenByParaField(false)
98 , m_bFootnote(false)
99 , m_bDDEFields(false)
100 , m_bStartMapNeedsSorting(false)
101 , m_bEndMapNeedsSorting(false)
102 , m_bWhichMapNeedsSorting(false)
103{
104}
105
106static void TextAttrDelete( SwDoc & rDoc, SwTextAttr * const pAttr )
107{
108 if (RES_TXTATR_META == pAttr->Which() ||
109 RES_TXTATR_METAFIELD == pAttr->Which())
110 {
111 static_txtattr_cast<SwTextMeta *>(pAttr)->ChgTextNode(nullptr); // prevents ASSERT
112 }
113 else if (pAttr->Which() == RES_TXTATR_CONTENTCONTROL)
114 {
115 static_txtattr_cast<SwTextContentControl*>(pAttr)->ChgTextNode(nullptr);
116 }
117 SwTextAttr::Destroy( pAttr, rDoc.GetAttrPool() );
118}
119
120static bool TextAttrContains(const sal_Int32 nPos, const SwTextAttrEnd * const pAttr)
121{
122 return (pAttr->GetStart() < nPos) && (nPos < *pAttr->End());
123}
124
125// a: |-----|
126// b:
127// |---| => valid: b before a
128// |-----| => valid: start == end; b before a
129// |---------| => invalid: overlap (1)
130// |-----------| => valid: same end; b around a
131// |-----------------| => valid: b around a
132// |---| => valid; same start; b within a
133// |-----| => valid; same start and end; b around or within a?
134// |-----------| => valid: same start: b around a
135// |-| => valid: b within a
136// |---| => valid: same end; b within a
137// |---------| => invalid: overlap (2)
138// |-----| => valid: end == start; b after a
139// |---| => valid: b after a
140// ===> 2 invalid overlap cases
141static
142bool isOverlap(const sal_Int32 nStart1, const sal_Int32 nEnd1,
143 const sal_Int32 nStart2, const sal_Int32 nEnd2)
144{
145 return
146 ((nStart1 > nStart2) && (nStart1 < nEnd2) && (nEnd1 > nEnd2)) // (1)
147 || ((nStart1 < nStart2) && (nStart2 < nEnd1) && (nEnd1 < nEnd2)); // (2)
148}
149
151static
152bool isNestedAny(const sal_Int32 nStart1, const sal_Int32 nEnd1,
153 const sal_Int32 nStart2, const sal_Int32 nEnd2)
154{
155 return ((nStart1 == nStart2) || (nEnd1 == nEnd2))
156 // same start/end: nested except if hint1 empty and hint2 not empty
157 ? (nStart1 != nEnd1) || (nStart2 == nEnd2)
158 : ((nStart1 < nStart2) ? (nEnd1 >= nEnd2) : (nEnd1 <= nEnd2));
159}
160
161static
162bool isSelfNestable(const sal_uInt16 nWhich)
163{
164 if ((RES_TXTATR_INETFMT == nWhich) ||
165 (RES_TXTATR_CJK_RUBY == nWhich) ||
166 (RES_TXTATR_INPUTFIELD == nWhich))
167 return false;
168 assert((RES_TXTATR_META == nWhich) ||
169 (RES_TXTATR_METAFIELD == nWhich) ||
170 (RES_TXTATR_CONTENTCONTROL == nWhich));
171 return true;
172}
173
174static
175bool isSplittable(const sal_uInt16 nWhich)
176{
177 if ((RES_TXTATR_INETFMT == nWhich) ||
178 (RES_TXTATR_CJK_RUBY == nWhich))
179 return true;
180 assert((RES_TXTATR_META == nWhich) ||
181 (RES_TXTATR_METAFIELD == nWhich) ||
182 (RES_TXTATR_INPUTFIELD == nWhich) ||
183 (RES_TXTATR_CONTENTCONTROL == nWhich));
184 return false;
185}
186
187namespace {
188
189enum Split_t { FAIL, SPLIT_NEW, SPLIT_OTHER };
190
191}
192
197static Split_t
198splitPolicy(const sal_uInt16 nWhichNew, const sal_uInt16 nWhichOther)
199{
200 if (!isSplittable(nWhichOther))
201 {
202 if (!isSplittable(nWhichNew))
203 return FAIL;
204 else
205 return SPLIT_NEW;
206 }
207 else
208 {
209 if ( RES_TXTATR_INPUTFIELD == nWhichNew )
210 return FAIL;
211 else if ( (RES_TXTATR_INETFMT == nWhichNew) &&
212 (RES_TXTATR_CJK_RUBY == nWhichOther) )
213 return SPLIT_NEW;
214 else
215 return SPLIT_OTHER;
216 }
217}
218
220{
221 ChgTextNode(&rNode);
222 SwCharFormat * const pFormat(
224 pFormat->Add( this );
225}
226
228{
229 ChgTextNode(&rNode);
230 SwCharFormat * const pFormat(
232 pFormat->Add( this );
233}
234
238static SwTextAttrNesting *
240 const sal_Int32 nStart, const sal_Int32 nEnd)
241{
242 SwTextAttr * const pNew( MakeTextAttr(
243 rNode.GetDoc(), rNesting.GetAttr(), nStart, nEnd ) );
244 switch (pNew->Which())
245 {
247 {
248 static_txtattr_cast<SwTextINetFormat*>(pNew)->InitINetFormat(rNode);
249 break;
250 }
252 {
253 static_txtattr_cast<SwTextRuby*>(pNew)->InitRuby(rNode);
254 break;
255 }
256 default:
257 assert(!"MakeTextAttrNesting: what the hell is that?");
258 break;
259 }
260 return static_txtattr_cast<SwTextAttrNesting*>(pNew);
261}
262
263typedef std::vector<SwTextAttrNesting *> NestList_t;
264
265static NestList_t::iterator
267 NestList_t::iterator const iter, sal_Int32 const nSplitPos,
268 bool const bSplitAtStart, bool const bOtherDummy)
269{
270 const sal_Int32 nStartPos( // skip other's dummy character!
271 (bSplitAtStart && bOtherDummy) ? nSplitPos + 1 : nSplitPos );
273 rNode, **iter, nStartPos, *(*iter)->GetEnd() ) );
274 (*iter)->SetEnd(nSplitPos);
275 return rSplits.insert(iter + 1, pNew);
276}
277
278static void
280 const sal_Int32 nNewStart,
281 const sal_Int32 nOtherStart, const sal_Int32 nOtherEnd, bool bOtherDummy)
282{
283 const bool bSplitAtStart(nNewStart < nOtherStart);
284 const sal_Int32 nSplitPos( bSplitAtStart ? nOtherStart : nOtherEnd );
285 // first find the portion that is split (not necessarily the last one!)
286 NestList_t::iterator const iter(
287 std::find_if( rSplits.begin(), rSplits.end(),
288 [nSplitPos](SwTextAttrEnd * const pAttr) {
289 return TextAttrContains(nSplitPos, pAttr);
290 } ) );
291 if (iter != rSplits.end()) // already split here?
292 {
293 lcl_DoSplitImpl(rSplits, rNode, iter, nSplitPos, bSplitAtStart, bOtherDummy);
294 }
295}
296
302{
303 Insert(& rNewHint);
304 NoteInHistory( & rNewHint, true );
305}
306
377bool
379{
380// INVARIANT: the nestable hints in the array are properly nested
381 const sal_uInt16 nNewWhich( rNewHint.Which() );
382 const sal_Int32 nNewStart( rNewHint.GetStart() );
383 const sal_Int32 nNewEnd ( *rNewHint.GetEnd() );
384 const bool bNewSelfNestable( isSelfNestable(nNewWhich) );
385
386 assert( (RES_TXTATR_INETFMT == nNewWhich) ||
387 (RES_TXTATR_CJK_RUBY == nNewWhich) ||
388 (RES_TXTATR_META == nNewWhich) ||
389 (RES_TXTATR_METAFIELD == nNewWhich) ||
390 (RES_TXTATR_CONTENTCONTROL == nNewWhich) ||
391 (RES_TXTATR_INPUTFIELD == nNewWhich));
392
393 NestList_t OverlappingExisting; // existing hints to be split
394 NestList_t OverwrittenExisting; // existing hints to be replaced
395 NestList_t SplitNew; // new hints to be inserted
396
397 SplitNew.push_back(& rNewHint);
398
399 // pass 1: split the inserted hint into fragments if necessary
400 for ( size_t i = 0; i < Count(); ++i )
401 {
402 SwTextAttr * const pOther = GetSortedByEnd(i);
403
404 if (pOther->IsNesting())
405 {
406 const sal_uInt16 nOtherWhich( pOther->Which() );
407 const sal_Int32 nOtherStart( pOther->GetStart() );
408 const sal_Int32 nOtherEnd ( *pOther->GetEnd() );
409 if (isOverlap(nNewStart, nNewEnd, nOtherStart, nOtherEnd ))
410 {
411 switch (splitPolicy(nNewWhich, nOtherWhich))
412 {
413 case FAIL:
414 SAL_INFO("sw.core", "cannot insert hint: overlap");
415 for (const auto& aSplit : SplitNew)
416 TextAttrDelete(rNode.GetDoc(), aSplit);
417 return false;
418 case SPLIT_NEW:
419 lcl_DoSplitNew(SplitNew, rNode, nNewStart,
420 nOtherStart, nOtherEnd, pOther->HasDummyChar());
421 break;
422 case SPLIT_OTHER:
423 OverlappingExisting.push_back(
424 static_txtattr_cast<SwTextAttrNesting*>(pOther));
425 break;
426 default:
427 assert(!"bad code monkey");
428 break;
429 }
430 }
431 else if (isNestedAny(nNewStart, nNewEnd, nOtherStart, nOtherEnd))
432 {
433 if (!bNewSelfNestable && (nNewWhich == nOtherWhich))
434 {
435 // ruby and hyperlink: if there is nesting, _overwrite_
436 OverwrittenExisting.push_back(
437 static_txtattr_cast<SwTextAttrNesting*>(pOther));
438 }
439 else if ((nNewStart == nOtherStart) && pOther->HasDummyChar())
440 {
441 if (rNewHint.HasDummyChar())
442 {
443 assert(!"ERROR: inserting duplicate CH_TXTATR hint");
444 return false;
445 } else if (nNewEnd < nOtherEnd) {
446 // other has dummy char, new is inside other, but
447 // new contains the other's dummy char?
448 // should be corrected because it may lead to problems
449 // in SwXMeta::createEnumeration
450 // SplitNew is sorted, so this is the first split
451 assert(SplitNew.front()->GetStart() == nNewStart);
452 SplitNew.front()->SetStart(nNewStart + 1);
453 }
454 }
455 }
456 }
457 }
458
459 // pass 1b: tragically need to check for fieldmarks here too
460 for (auto iter = SplitNew.begin(); iter != SplitNew.end(); ++iter)
461 {
462 SwPaM const temp(rNode, (*iter)->GetStart(), rNode, *(*iter)->GetEnd());
463 std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
464 sw::CalcBreaks(Breaks, temp, true);
465 if (!Breaks.empty())
466 {
467 if (!isSplittable(nNewWhich))
468 {
469 SAL_INFO("sw.core", "cannot insert hint: fieldmark overlap");
470 assert(SplitNew.size() == 1);
471 TextAttrDelete(rNode.GetDoc(), &rNewHint);
472 return false;
473 }
474 else
475 {
476 for (auto const& rPos : Breaks)
477 {
478 assert(rPos.first == rNode.GetIndex());
479 iter = lcl_DoSplitImpl(SplitNew, rNode, iter,
480 rPos.second, true, true);
481 }
482 }
483 }
484 }
485
486 assert((isSplittable(nNewWhich) || SplitNew.size() == 1) &&
487 "splitting the unsplittable ???");
488
489 // pass 2: split existing hints that overlap/nest with new hint
490 // do not iterate over hints array, but over remembered set of overlapping
491 // hints, to keep things simple w.r.t. insertion/removal
492 // N.B: if there is a hint that splits the inserted hint, then
493 // that hint would also have already split any hint in OverlappingExisting
494 // so any hint in OverlappingExisting can be split at most by one hint
495 // in SplitNew, or even not at all (this is not true for existing hints
496 // that go _around_ new hint, which is the reason d'^etre for pass 4)
497 for (auto& rpOther : OverlappingExisting)
498 {
499 const sal_Int32 nOtherStart( rpOther->GetStart() );
500 const sal_Int32 nOtherEnd ( *rpOther->GetEnd() );
501
502 for (const auto& rpNew : SplitNew)
503 {
504 const sal_Int32 nSplitNewStart( rpNew->GetStart() );
505 const sal_Int32 nSplitNewEnd ( *rpNew->GetEnd() );
506 // 4 cases: within, around, overlap l, overlap r, (OTHER: no action)
507 const bool bRemoveOverlap(
508 !bNewSelfNestable && (nNewWhich == rpOther->Which()) );
509
510 switch (ComparePosition(nSplitNewStart, nSplitNewEnd,
511 nOtherStart, nOtherEnd))
512 {
514 {
515 assert(!bRemoveOverlap &&
516 "this one should be in OverwrittenExisting?");
517 }
518 break;
521 {
522 assert(!"existing hint inside new hint: why?");
523 }
524 break;
526 {
527 Delete( rpOther ); // this also does NoteInHistory!
528 rpOther->SetStart(nSplitNewEnd);
529 InsertNesting( *rpOther );
530 if (!bRemoveOverlap)
531 {
532 if ( MAX_HINTS <= Count() )
533 {
534 SAL_INFO("sw.core", "hints array full :-(");
535 return false;
536 }
537 SwTextAttrNesting * const pOtherLeft(
538 MakeTextAttrNesting( rNode, *rpOther,
539 nOtherStart, nSplitNewEnd ) );
540 InsertNesting( *pOtherLeft );
541 }
542 }
543 break;
545 {
546 Delete( rpOther ); // this also does NoteInHistory!
547 rpOther->SetEnd(nSplitNewStart);
548 InsertNesting( *rpOther );
549 if (!bRemoveOverlap)
550 {
551 if ( MAX_HINTS <= Count() )
552 {
553 SAL_INFO("sw.core", "hints array full :-(");
554 return false;
555 }
556 SwTextAttrNesting * const pOtherRight(
557 MakeTextAttrNesting( rNode, *rpOther,
558 nSplitNewStart, nOtherEnd ) );
559 InsertNesting( *pOtherRight );
560 }
561 }
562 break;
563 default:
564 break; // overlap resolved by splitting new: nothing to do
565 }
566 }
567 }
568
569 if ( MAX_HINTS <= Count() || MAX_HINTS - Count() <= SplitNew.size() )
570 {
571 SAL_INFO("sw.core", "hints array full :-(");
572 return false;
573 }
574
575 // pass 3: insert new hints
576 for (const auto& rpHint : SplitNew)
577 {
578 InsertNesting(*rpHint);
579 }
580
581 // pass 4: handle overwritten hints
582 // RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY should displace attributes
583 // of the same kind.
584 for (auto& rpOther : OverwrittenExisting)
585 {
586 const sal_Int32 nOtherStart( rpOther->GetStart() );
587 const sal_Int32 nOtherEnd ( *rpOther->GetEnd() );
588
589 // overwritten portion is given by start/end of inserted hint
590 if ((nNewStart <= nOtherStart) && (nOtherEnd <= nNewEnd))
591 {
592 Delete(rpOther);
593 rNode.DestroyAttr( rpOther );
594 }
595 else
596 {
597 assert((nOtherStart < nNewStart) || (nNewEnd < nOtherEnd));
598 // scenario: there is a RUBY, and contained within that a META;
599 // now a RUBY is inserted within the META => the existing RUBY is split:
600 // here it is not possible to simply insert the left/right fragment
601 // of the existing RUBY because they <em>overlap</em> with the META!
602 Delete( rpOther ); // this also does NoteInHistory!
603 if (nNewEnd < nOtherEnd)
604 {
605 SwTextAttrNesting * const pOtherRight(
607 rNode, *rpOther, nNewEnd, nOtherEnd ) );
608 bool const bSuccess( TryInsertNesting(rNode, *pOtherRight) );
609 SAL_WARN_IF(!bSuccess, "sw.core", "recursive call 1 failed?");
610 }
611 if (nOtherStart < nNewStart)
612 {
613 rpOther->SetEnd(nNewStart);
614 bool const bSuccess( TryInsertNesting(rNode, *rpOther) );
615 SAL_WARN_IF(!bSuccess, "sw.core", "recursive call 2 failed?");
616 }
617 else
618 {
619 rNode.DestroyAttr(rpOther);
620 }
621 }
622 }
623
624 return true;
625}
626
627// This function takes care for the following text attribute:
628// RES_TXTATR_CHARFMT, RES_TXTATR_AUTOFMT
629// These attributes have to be handled in a special way (Portion building).
630
631// The new attribute will be split by any existing RES_TXTATR_AUTOFMT or
632// RES_TXTATR_CHARFMT. The new attribute itself will
633// split any existing RES_TXTATR_AUTOFMT or RES_TXTATR_CHARFMT.
634
636 const SetAttrMode nMode )
637{
638 const sal_uInt16 nWhich = rNewHint.Which();
639
640 const sal_Int32 nThisStart = rNewHint.GetStart();
641 const sal_Int32 nThisEnd = *rNewHint.GetEnd();
642 const bool bNoLengthAttribute = nThisStart == nThisEnd;
643
644 std::vector<SwTextAttr*> aInsDelHints;
645
646 assert( RES_TXTATR_CHARFMT == rNewHint.Which() ||
647 RES_TXTATR_AUTOFMT == rNewHint.Which() );
648
649 // 2. Find the hints which cover the start and end position
650 // of the new hint. These hints have to be split into two portions:
651
652 if ( !bNoLengthAttribute ) // nothing to do for no length attributes
653 {
654 for ( size_t i = 0; i < Count(); ++i )
655 {
656 // we're modifying stuff here which affects the sorting, and we
657 // don't want it changing underneath us
659
660 if ( RES_TXTATR_CHARFMT != pOther->Which() &&
661 RES_TXTATR_AUTOFMT != pOther->Which() )
662 continue;
663
664 sal_Int32 nOtherStart = pOther->GetStart();
665 const sal_Int32 nOtherEnd = *pOther->GetEnd();
666
667 // Check if start of new attribute overlaps with pOther:
668 // Split pOther if necessary:
669 if ( nOtherStart < nThisStart && nThisStart < nOtherEnd )
670 {
671 SwTextAttr* pNewAttr = MakeTextAttr( rNode.GetDoc(),
672 pOther->GetAttr(), nOtherStart, nThisStart );
673 if ( RES_TXTATR_CHARFMT == pOther->Which() )
674 {
675 static_txtattr_cast<SwTextCharFormat*>(pNewAttr)->SetSortNumber(
676 static_txtattr_cast<SwTextCharFormat*>(pOther)->GetSortNumber() );
677 }
678 aInsDelHints.push_back( pNewAttr );
679
680 NoteInHistory( pOther );
681 pOther->SetStart(nThisStart);
682 NoteInHistory( pOther, true );
683
684 nOtherStart = nThisStart;
685 }
686
687 // Check if end of new attribute overlaps with pOther:
688 // Split pOther if necessary:
689 if ( nOtherStart < nThisEnd && nThisEnd < nOtherEnd )
690 {
691 SwTextAttr* pNewAttr = MakeTextAttr( rNode.GetDoc(),
692 pOther->GetAttr(), nOtherStart, nThisEnd );
693 if ( RES_TXTATR_CHARFMT == pOther->Which() )
694 {
695 static_txtattr_cast<SwTextCharFormat*>(pNewAttr)->SetSortNumber(
696 static_txtattr_cast<SwTextCharFormat*>(pOther)->GetSortNumber());
697 }
698 aInsDelHints.push_back( pNewAttr );
699
700 NoteInHistory( pOther );
701 pOther->SetStart(nThisEnd);
702 NoteInHistory( pOther, true );
703 }
704 }
705
706 // Insert the newly created attributes:
707 for ( const auto& rpHint : aInsDelHints )
708 {
709 Insert( rpHint );
710 NoteInHistory( rpHint, true );
711 }
712 }
713
714#ifdef DBG_UTIL
715 if( !rNode.GetDoc().IsInReading() )
716 CHECK_NOTMERGED; // ignore flags not set properly yet, don't check them
717#endif
718
719 // 4. Split rNewHint into 1 ... n new hints:
720
722 aBounds.insert( nThisStart );
723 aBounds.insert( nThisEnd );
724
725 if ( !bNoLengthAttribute ) // nothing to do for no length attributes
726 {
727 for ( size_t i = 0; i < Count(); ++i )
728 {
729 const SwTextAttr* pOther = Get(i);
730
731 if ( RES_TXTATR_CHARFMT != pOther->Which() &&
732 RES_TXTATR_AUTOFMT != pOther->Which() )
733 continue;
734
735 const sal_Int32 nOtherStart = pOther->GetStart();
736 const sal_Int32 nOtherEnd = *pOther->End();
737
738 if (nThisStart <= nOtherStart && nOtherStart <= nThisEnd)
739 aBounds.insert( nOtherStart );
740 if (nThisStart <= nOtherEnd && nOtherEnd <= nThisEnd)
741 aBounds.insert( nOtherEnd );
742 }
743 }
744
745 auto aStartIter = aBounds.lower_bound( nThisStart );
746 auto aEndIter = aBounds.upper_bound( nThisEnd );
747 sal_Int32 nPorStart = *aStartIter;
748 ++aStartIter;
749 bool bDestroyHint = true;
750
751 // Insert the 1...n new parts of the new attribute:
752
753 while ( aStartIter != aEndIter || bNoLengthAttribute )
754 {
755 OSL_ENSURE( bNoLengthAttribute || nPorStart < *aStartIter, "AUTOSTYLES: BuildPortion trouble" );
756
757 const sal_Int32 nPorEnd = bNoLengthAttribute ? nPorStart : *aStartIter;
758 aInsDelHints.clear();
759
760 // Get all hints that are in [nPorStart, nPorEnd[:
761 for ( size_t i = 0; i < Count(); ++i )
762 {
763 // we get called from TryInsertHint, which changes ordering
765
766 if ( RES_TXTATR_CHARFMT != pOther->Which() &&
767 RES_TXTATR_AUTOFMT != pOther->Which() )
768 continue;
769
770 const sal_Int32 nOtherStart = pOther->GetStart();
771
772 if ( nOtherStart > nPorStart )
773 break;
774
775 if ( pOther->GetEnd() && *pOther->GetEnd() == nPorEnd && nOtherStart == nPorStart )
776 {
777 OSL_ENSURE( *pOther->GetEnd() == nPorEnd, "AUTOSTYLES: BuildPortion trouble" );
778 aInsDelHints.push_back( pOther );
779 }
780 }
781
782 SwTextAttr* pNewAttr = nullptr;
783 if ( RES_TXTATR_CHARFMT == nWhich )
784 {
785 // pNewHint can be inserted after calculating the sort value.
786 // This should ensure, that pNewHint comes behind the already present
787 // character style
788 sal_uInt16 nCharStyleCount = 0;
789 for ( const auto& rpHint : aInsDelHints )
790 {
791 if ( RES_TXTATR_CHARFMT == rpHint->Which() )
792 {
793 // #i74589#
794 const SwFormatCharFormat& rOtherCharFormat = rpHint->GetCharFormat();
795 const SwFormatCharFormat& rThisCharFormat = rNewHint.GetCharFormat();
796 const bool bSameCharFormat = rOtherCharFormat.GetCharFormat() == rThisCharFormat.GetCharFormat();
797
798 // #i90311#
799 // Do not remove existing character format hint during XML import
800 if ( !rNode.GetDoc().IsInXMLImport() &&
801 ( !( SetAttrMode::DONTREPLACE & nMode ) ||
802 bNoLengthAttribute ||
803 bSameCharFormat ) )
804 {
805 // Remove old hint
806 Delete( rpHint );
807 rNode.DestroyAttr( rpHint );
808 }
809 else
810 ++nCharStyleCount;
811 }
812 else
813 {
814 // remove all attributes from auto styles, which are explicitly set in
815 // the new character format:
816 OSL_ENSURE( RES_TXTATR_AUTOFMT == rpHint->Which(), "AUTOSTYLES - Misc trouble" );
817 SwTextAttr* pOther = rpHint;
818 const std::shared_ptr<SfxItemSet> & pOldStyle = static_cast<const SwFormatAutoFormat&>(pOther->GetAttr()).GetStyleHandle();
819
820 // For each attribute in the automatic style check if it
821 // is also set the new character style:
822 SfxItemSet aNewSet( *pOldStyle->GetPool(),
824 SfxItemIter aItemIter( *pOldStyle );
825 const SfxPoolItem* pItem = aItemIter.GetCurItem();
826 do
827 {
828 if ( !CharFormat::IsItemIncluded( pItem->Which(), &rNewHint ) )
829 {
830 aNewSet.Put( *pItem );
831 }
832
833 pItem = aItemIter.NextItem();
834 } while (pItem);
835
836 // Remove old hint
837 Delete( pOther );
838 rNode.DestroyAttr( pOther );
839
840 // Create new AutoStyle
841 if ( aNewSet.Count() )
842 {
843 pNewAttr = MakeTextAttr( rNode.GetDoc(),
844 aNewSet, nPorStart, nPorEnd );
845 Insert( pNewAttr );
846 NoteInHistory( pNewAttr, true );
847 }
848 }
849 }
850
851 // If there is no current hint and start and end of rNewHint
852 // is ok, we do not need to create a new txtattr.
853 if ( nPorStart == nThisStart &&
854 nPorEnd == nThisEnd &&
855 !nCharStyleCount )
856 {
857 pNewAttr = &rNewHint;
858 bDestroyHint = false;
859 }
860 else
861 {
862 pNewAttr = MakeTextAttr( rNode.GetDoc(), rNewHint.GetAttr(),
863 nPorStart, nPorEnd );
864 static_txtattr_cast<SwTextCharFormat*>(pNewAttr)->SetSortNumber(nCharStyleCount);
865 }
866 }
867 else
868 {
869 // Find the current autostyle. Mix attributes if necessary.
870 SwTextAttr* pCurrentAutoStyle = nullptr;
871 SwTextAttr* pCurrentCharFormat = nullptr;
872 for ( const auto& rpHint : aInsDelHints )
873 {
874 if ( RES_TXTATR_AUTOFMT == rpHint->Which() )
875 pCurrentAutoStyle = rpHint;
876 else if ( RES_TXTATR_CHARFMT == rpHint->Which() )
877 pCurrentCharFormat = rpHint;
878 }
879
880 std::shared_ptr<SfxItemSet> pNewStyle = static_cast<const SwFormatAutoFormat&>(rNewHint.GetAttr()).GetStyleHandle();
881 if ( pCurrentAutoStyle )
882 {
883 const std::shared_ptr<SfxItemSet> & pCurrentStyle = static_cast<const SwFormatAutoFormat&>(pCurrentAutoStyle->GetAttr()).GetStyleHandle();
884
885 // Merge attributes
886 SfxItemSet aNewSet( *pCurrentStyle );
887 aNewSet.Put( *pNewStyle );
888
889 // #i75750# Remove attributes already set at whole paragraph
890 // #i81764# This should not be applied for no length attributes!!! <--
891 if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && aNewSet.Count() )
892 {
893 SfxItemIter aIter2( aNewSet );
894 const SfxPoolItem* pItem = aIter2.GetCurItem();
895 const SfxItemSet& rWholeParaAttrSet = rNode.GetSwAttrSet();
896
897 do
898 {
899 const SfxPoolItem* pTmpItem = nullptr;
900 if ( SfxItemState::SET == rWholeParaAttrSet.GetItemState( pItem->Which(), false, &pTmpItem ) &&
901 pTmpItem == pItem )
902 {
903 // Do not clear item if the attribute is set in a character format:
904 if ( !pCurrentCharFormat || nullptr == CharFormat::GetItem( *pCurrentCharFormat, pItem->Which() ) )
905 aIter2.ClearItem();
906 }
907 }
908 while ((pItem = aIter2.NextItem()));
909 }
910
911 // Remove old hint
912 Delete( pCurrentAutoStyle );
913 rNode.DestroyAttr( pCurrentAutoStyle );
914
915 // Create new AutoStyle
916 if ( aNewSet.Count() )
917 pNewAttr = MakeTextAttr( rNode.GetDoc(), aNewSet,
918 nPorStart, nPorEnd );
919 }
920 else
921 {
922 // Remove any attributes which are already set at the whole paragraph:
923 bool bOptimizeAllowed = true;
924
925 // #i75750# Remove attributes already set at whole paragraph
926 // #i81764# This should not be applied for no length attributes!!! <--
927 if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && pNewStyle->Count() )
928 {
929 std::unique_ptr<SfxItemSet> pNewSet;
930
931 SfxItemIter aIter2( *pNewStyle );
932 const SfxPoolItem* pItem = aIter2.GetCurItem();
933 const SfxItemSet& rWholeParaAttrSet = rNode.GetSwAttrSet();
934
935 do
936 {
937 const SfxPoolItem* pTmpItem = nullptr;
938 if ( SfxItemState::SET == rWholeParaAttrSet.GetItemState( pItem->Which(), false, &pTmpItem ) &&
939 pTmpItem == pItem )
940 {
941 // Do not clear item if the attribute is set in a character format:
942 if ( !pCurrentCharFormat || nullptr == CharFormat::GetItem( *pCurrentCharFormat, pItem->Which() ) )
943 {
944 if ( !pNewSet )
945 pNewSet = pNewStyle->Clone();
946 pNewSet->ClearItem( pItem->Which() );
947 }
948 }
949 }
950 while ((pItem = aIter2.NextItem()));
951
952 if ( pNewSet )
953 {
954 bOptimizeAllowed = false;
955 if ( pNewSet->Count() )
957 else
958 pNewStyle.reset();
959 }
960 }
961
962 // Create new AutoStyle
963 // If there is no current hint and start and end of rNewHint
964 // is ok, we do not need to create a new txtattr.
965 if ( bOptimizeAllowed &&
966 nPorStart == nThisStart &&
967 nPorEnd == nThisEnd )
968 {
969 pNewAttr = &rNewHint;
970 bDestroyHint = false;
971 }
972 else if ( pNewStyle )
973 {
974 pNewAttr = MakeTextAttr( rNode.GetDoc(), *pNewStyle,
975 nPorStart, nPorEnd );
976 }
977 }
978 }
979
980 if ( pNewAttr )
981 {
982 Insert( pNewAttr );
983// if ( bDestroyHint )
984 NoteInHistory( pNewAttr, true );
985 }
986
987 if ( !bNoLengthAttribute )
988 {
989 nPorStart = *aStartIter;
990 ++aStartIter;
991 }
992 else
993 break;
994 }
995
996 if ( bDestroyHint )
997 rNode.DestroyAttr( &rNewHint );
998}
999
1001{
1002 // this is intended _only_ for special-purpose redline attributes!
1003 switch (rAttr.Which())
1004 {
1005 case RES_CHRATR_COLOR:
1006 case RES_CHRATR_WEIGHT:
1009 case RES_CHRATR_POSTURE:
1014 case RES_CHRATR_CASEMAP:
1016 break;
1017 default:
1018 assert(!"unsupported redline attribute");
1019 break;
1020 }
1021
1022 // Put new attribute into pool
1023 // FIXME: this const_cast is evil!
1024 SfxPoolItem& rNew =
1025 const_cast<SfxPoolItem&>( rDoc.GetAttrPool().Put( rAttr ) );
1026 return new SwTextAttrEnd( rNew, 0, 0 );
1027}
1028
1029// create new text attribute
1031 SwDoc & rDoc,
1032 SfxPoolItem& rAttr,
1033 sal_Int32 const nStt,
1034 sal_Int32 const nEnd,
1035 CopyOrNewType const bIsCopy,
1036 SwTextNode *const pTextNode )
1037{
1038 if ( isCHRATR(rAttr.Which()) )
1039 {
1040 // Somebody wants to build a SwTextAttr for a character attribute.
1041 // Sorry, this is not allowed any longer.
1042 // You'll get a brand new autostyle attribute:
1044 aItemSet.Put( rAttr );
1045 return MakeTextAttr( rDoc, aItemSet, nStt, nEnd );
1046 }
1047 else if ( RES_TXTATR_AUTOFMT == rAttr.Which() &&
1048 static_cast<const SwFormatAutoFormat&>(rAttr).GetStyleHandle()->
1049 GetPool() != &rDoc.GetAttrPool() )
1050 {
1051 // If the attribute is an auto-style which refers to a pool that is
1052 // different from rDoc's pool, we have to correct this:
1053 const std::shared_ptr<SfxItemSet> & pAutoStyle = static_cast<const SwFormatAutoFormat&>(rAttr).GetStyleHandle();
1054 std::unique_ptr<const SfxItemSet> pNewSet(
1055 pAutoStyle->SfxItemSet::Clone( true, &rDoc.GetAttrPool() ));
1056 SwTextAttr* pNew = MakeTextAttr( rDoc, *pNewSet, nStt, nEnd );
1057 return pNew;
1058 }
1059
1060 // Put new attribute into pool
1061 // FIXME: this const_cast is evil!
1062 SfxPoolItem& rNew =
1063 const_cast<SfxPoolItem&>( rDoc.GetAttrPool().Put( rAttr ) );
1064
1065 SwTextAttr* pNew = nullptr;
1066 switch( rNew.Which() )
1067 {
1068 case RES_TXTATR_CHARFMT:
1069 {
1070 SwFormatCharFormat &rFormatCharFormat = static_cast<SwFormatCharFormat&>(rNew);
1071 if( !rFormatCharFormat.GetCharFormat() )
1072 {
1073 rFormatCharFormat.SetCharFormat( rDoc.GetDfltCharFormat() );
1074 }
1075
1076 pNew = new SwTextCharFormat( rFormatCharFormat, nStt, nEnd );
1077 }
1078 break;
1079 case RES_TXTATR_INETFMT:
1080 pNew = new SwTextINetFormat( static_cast<SwFormatINetFormat&>(rNew), nStt, nEnd );
1081 break;
1082
1083 case RES_TXTATR_FIELD:
1084 pNew = new SwTextField( static_cast<SwFormatField &>(rNew), nStt,
1085 rDoc.IsClipBoard() );
1086 break;
1087
1089 {
1090 pNew = new SwTextAnnotationField( static_cast<SwFormatField &>(rNew), nStt, rDoc.IsClipBoard() );
1091 if (bIsCopy == CopyOrNewType::Copy)
1092 {
1093 // On copy of the annotation field do not keep the annotated text range by removing
1094 // the relation to its annotation mark (relation established via annotation field's name).
1095 // If the annotation mark is also copied, the relation and thus the annotated text range will be reestablished,
1096 // when the annotation mark is created and inserted into the document.
1097 auto& pField = const_cast<SwPostItField&>(dynamic_cast<const SwPostItField&>(*(pNew->GetFormatField().GetField())));
1098 pField.SetName(OUString());
1099 pField.SetPostItId();
1100 }
1101 }
1102 break;
1103
1105 pNew = new SwTextInputField( static_cast<SwFormatField &>(rNew), nStt, nEnd,
1106 rDoc.IsClipBoard() );
1107 break;
1108
1109 case RES_TXTATR_FLYCNT:
1110 {
1111 // finally, copy the frame format (with content)
1112 pNew = new SwTextFlyCnt( static_cast<SwFormatFlyCnt&>(rNew), nStt );
1113 if ( static_cast<const SwFormatFlyCnt &>(rAttr).GetTextFlyCnt() )
1114 {
1115 // if it has an existing attr then the format must be copied
1116 static_cast<SwTextFlyCnt *>(pNew)->CopyFlyFormat( rDoc );
1117 }
1118 }
1119 break;
1120 case RES_TXTATR_FTN:
1121 pNew = new SwTextFootnote( static_cast<SwFormatFootnote&>(rNew), nStt );
1122 // copy note's SeqNo
1123 if( static_cast<SwFormatFootnote&>(rAttr).GetTextFootnote() )
1124 static_cast<SwTextFootnote*>(pNew)->SetSeqNo( static_cast<SwFormatFootnote&>(rAttr).GetTextFootnote()->GetSeqRefNo() );
1125 break;
1126 case RES_TXTATR_REFMARK:
1127 pNew = nStt == nEnd
1128 ? new SwTextRefMark( static_cast<SwFormatRefMark&>(rNew), nStt )
1129 : new SwTextRefMark( static_cast<SwFormatRefMark&>(rNew), nStt, &nEnd );
1130 break;
1131 case RES_TXTATR_TOXMARK:
1132 {
1133 SwTOXMark& rMark = static_cast<SwTOXMark&>(rNew);
1134
1135 // tdf#98868 if the SwTOXType is from a different document that the
1136 // target, re-register the TOXMark against a matching SwTOXType from
1137 // the target document instead
1138 const SwTOXType* pTOXType = rMark.GetTOXType();
1139 if (pTOXType && &pTOXType->GetDoc() != &rDoc)
1140 {
1141 SwTOXType* pToxType = SwHistorySetTOXMark::GetSwTOXType(rDoc, pTOXType->GetType(),
1142 pTOXType->GetTypeName());
1143 rMark.RegisterToTOXType(*pToxType);
1144 }
1145
1146 pNew = new SwTextTOXMark(rMark, nStt, &nEnd);
1147 break;
1148 }
1150 pNew = new SwTextRuby( static_cast<SwFormatRuby&>(rNew), nStt, nEnd );
1151 break;
1152 case RES_TXTATR_META:
1154 pNew = SwTextMeta::CreateTextMeta( rDoc.GetMetaFieldManager(), pTextNode,
1155 static_cast<SwFormatMeta&>(rNew), nStt, nEnd, bIsCopy == CopyOrNewType::Copy );
1156 break;
1158 pNew = new SwTextLineBreak(static_cast<SwFormatLineBreak&>(rNew), nStt);
1159 break;
1162 rDoc, pTextNode, static_cast<SwFormatContentControl&>(rNew), nStt, nEnd,
1163 bIsCopy == CopyOrNewType::Copy);
1164 break;
1165 default:
1166 assert(RES_TXTATR_AUTOFMT == rNew.Which());
1167 pNew = new SwTextAttrEnd( rNew, nStt, nEnd );
1168 break;
1169 }
1170
1171 return pNew;
1172}
1173
1175 sal_Int32 nStt, sal_Int32 nEnd )
1176{
1177 IStyleAccess& rStyleAccess = rDoc.GetIStyleAccess();
1178 const std::shared_ptr<SfxItemSet> pAutoStyle = rStyleAccess.getAutomaticStyle( rSet, IStyleAccess::AUTO_STYLE_CHAR );
1179 SwFormatAutoFormat aNewAutoFormat;
1180 aNewAutoFormat.SetStyleHandle( pAutoStyle );
1181 SwTextAttr* pNew = MakeTextAttr( rDoc, aNewAutoFormat, nStt, nEnd );
1182 return pNew;
1183}
1184
1185// delete the text attribute and unregister its item at the pool
1187{
1188 if( !pAttr )
1189 return;
1190
1191 // some things need to be done before deleting the formatting attribute
1192 SwDoc& rDoc = GetDoc();
1193 switch( pAttr->Which() )
1194 {
1195 case RES_TXTATR_FLYCNT:
1196 {
1197 SwFrameFormat* pFormat = pAttr->GetFlyCnt().GetFrameFormat();
1198 if( pFormat ) // set to 0 by Undo?
1199 rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
1200 }
1201 break;
1202
1203 case RES_CHRATR_HIDDEN:
1205 break;
1206
1207 case RES_TXTATR_FTN:
1208 static_cast<SwTextFootnote*>(pAttr)->SetStartNode( nullptr );
1209 static_cast<SwFormatFootnote&>(pAttr->GetAttr()).InvalidateFootnote();
1210 break;
1211
1212 case RES_TXTATR_FIELD:
1215 if( !rDoc.IsInDtor() )
1216 {
1217 SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pAttr));
1218 SwFieldType* pFieldType = pAttr->GetFormatField().GetField()->GetTyp();
1219
1220 if (SwFieldIds::Dde != pFieldType->Which()
1221 && !pTextField->GetpTextNode())
1222 {
1223 break; // was not yet inserted
1224 }
1225
1226 //JP 06-08-95: DDE-fields are an exception
1227 assert(SwFieldIds::Dde == pFieldType->Which() ||
1228 this == pTextField->GetpTextNode());
1229
1230 // certain fields must update the SwDoc's calculation flags
1231
1232 // Certain fields (like HiddenParaField) must trigger recalculation of visible flag
1233 if (GetDoc().FieldCanHideParaWeight(pFieldType->Which()))
1235
1236 switch( pFieldType->Which() )
1237 {
1240 case SwFieldIds::GetExp:
1242 case SwFieldIds::SetExp:
1246 if( !rDoc.getIDocumentFieldsAccess().IsNewFieldLst() && GetNodes().IsDocNodes() )
1247 rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(false, *pTextField);
1248 break;
1249 case SwFieldIds::Dde:
1250 if (GetNodes().IsDocNodes() && pTextField->GetpTextNode())
1251 static_cast<SwDDEFieldType*>(pFieldType)->DecRefCnt();
1252 break;
1253 case SwFieldIds::Postit:
1254 {
1255 const_cast<SwFormatField&>(pAttr->GetFormatField()).Broadcast(
1257 break;
1258 }
1259 default: break;
1260 }
1261 }
1262 static_cast<SwFormatField&>(pAttr->GetAttr()).InvalidateField();
1263 break;
1264
1265 case RES_TXTATR_TOXMARK:
1266 static_cast<SwTOXMark&>(pAttr->GetAttr()).InvalidateTOXMark();
1267 break;
1268
1269 case RES_TXTATR_REFMARK:
1270 static_cast<SwFormatRefMark&>(pAttr->GetAttr()).InvalidateRefMark();
1271 break;
1272
1273 case RES_TXTATR_META:
1275 {
1276 auto pTextMeta = static_txtattr_cast<SwTextMeta*>(pAttr);
1277 SwFormatMeta & rFormatMeta( static_cast<SwFormatMeta &>(pTextMeta->GetAttr()) );
1278 if (::sw::Meta* pMeta = rFormatMeta.GetMeta())
1279 {
1280 if (SwDocShell* pDocSh = rDoc.GetDocShell())
1281 {
1282 static constexpr OUStringLiteral metaNS(u"urn:bails");
1283 const css::uno::Reference<css::rdf::XResource> xSubject = pMeta->MakeUnoObject();
1284 uno::Reference<frame::XModel> xModel = pDocSh->GetBaseModel();
1285 SwRDFHelper::clearStatements(xModel, metaNS, xSubject);
1286 }
1287 }
1288
1289 static_txtattr_cast<SwTextMeta*>(pAttr)->ChgTextNode(nullptr);
1290 }
1291 break;
1293 {
1294 static_txtattr_cast<SwTextContentControl*>(pAttr)->ChgTextNode(nullptr);
1295 break;
1296 }
1297
1298 default:
1299 break;
1300 }
1301
1302 SwTextAttr::Destroy( pAttr, rDoc.GetAttrPool() );
1303}
1304
1306 SfxPoolItem& rAttr,
1307 const sal_Int32 nStart,
1308 const sal_Int32 nEnd,
1309 const SetAttrMode nMode )
1310{
1311 // character attributes will be inserted as automatic styles:
1312 assert( !isCHRATR(rAttr.Which()) && "AUTOSTYLES - "
1313 "SwTextNode::InsertItem should not be called with character attributes");
1314
1315 SwTextAttr *const pNew =
1317 GetDoc(),
1318 rAttr,
1319 nStart,
1320 nEnd,
1322 this );
1323
1324 if ( pNew )
1325 {
1326 const bool bSuccess( InsertHint( pNew, nMode ) );
1327 // N.B.: also check that the hint is actually in the hints array,
1328 // because hints of certain types may be merged after successful
1329 // insertion, and thus destroyed!
1330 if (!bSuccess || !m_pSwpHints->Contains( pNew ))
1331 {
1332 return nullptr;
1333 }
1334 }
1335
1336 return pNew;
1337}
1338
1339// take ownership of pAttr; if insertion fails, delete pAttr
1340bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode )
1341{
1342 bool bHiddenPara = false;
1343
1344 assert(pAttr && pAttr->GetStart() <= Len());
1345 assert(!pAttr->GetEnd() || (*pAttr->GetEnd() <= Len()));
1346
1347 // translate from SetAttrMode to InsertMode (for hints with CH_TXTATR)
1348 const SwInsertFlags nInsertFlags =
1354
1355 // need this after TryInsertHint, when pAttr may be deleted
1356 const sal_Int32 nStart( pAttr->GetStart() );
1357 const bool bDummyChar( pAttr->HasDummyChar() );
1358 if (bDummyChar)
1359 {
1360 SetAttrMode nInsMode = nMode;
1361 switch( pAttr->Which() )
1362 {
1363 case RES_TXTATR_FLYCNT:
1364 {
1365 SwTextFlyCnt *pFly = static_cast<SwTextFlyCnt *>(pAttr);
1366 SwFrameFormat* pFormat = pAttr->GetFlyCnt().GetFrameFormat();
1367 if( !(SetAttrMode::NOTXTATRCHR & nInsMode) )
1368 {
1369 // Need to insert char first, because SetAnchor() reads
1370 // GetStart().
1371 //JP 11.05.98: if the anchor is already set correctly,
1372 // fix it after inserting the char, so that clients don't
1373 // have to worry about it.
1374 const SwFormatAnchor* pAnchor = pFormat->GetItemIfSet( RES_ANCHOR, false );
1375
1376 SwContentIndex aIdx( this, pAttr->GetStart() );
1377 const OUString c(GetCharOfTextAttr(*pAttr));
1378 OUString const ins( InsertText(c, aIdx, nInsertFlags) );
1379 if (ins.isEmpty())
1380 {
1381 // do not record deletion of Format!
1382 ::sw::UndoGuard const ug(
1383 pFormat->GetDoc()->GetIDocumentUndoRedo());
1384 DestroyAttr(pAttr);
1385 return false; // text node full :(
1386 }
1387 nInsMode |= SetAttrMode::NOTXTATRCHR;
1388
1389 if (pAnchor &&
1390 (RndStdIds::FLY_AS_CHAR == pAnchor->GetAnchorId()) &&
1391 pAnchor->GetAnchorNode() &&
1392 *pAnchor->GetAnchorNode() == *this &&
1393 pAnchor->GetAnchorContentOffset() == aIdx.GetIndex() )
1394 {
1395 const_cast<SwPosition*>(pAnchor->GetContentAnchor())->AdjustContent(-1);
1396 }
1397 }
1398 pFly->SetAnchor( this );
1399
1400 // format pointer could have changed in SetAnchor,
1401 // when copying to other docs!
1402 pFormat = pAttr->GetFlyCnt().GetFrameFormat();
1403 SwDoc *pDoc = pFormat->GetDoc();
1404
1405 // OD 26.06.2003 - allow drawing objects in header/footer.
1406 // But don't allow control objects in header/footer
1407 if( RES_DRAWFRMFMT == pFormat->Which() &&
1408 pDoc->IsInHeaderFooter( *pFormat->GetAnchor().GetAnchorNode() ) )
1409 {
1410 bool bCheckControlLayer = false;
1411 pFormat->CallSwClientNotify(sw::CheckDrawFrameFormatLayerHint(&bCheckControlLayer));
1412 if( bCheckControlLayer )
1413 {
1414 // This should not be allowed, prevent it here.
1415 // The dtor of the SwTextAttr does not delete the
1416 // char, so delete it explicitly here.
1417 if( SetAttrMode::NOTXTATRCHR & nInsMode )
1418 {
1419 // delete the char from the string
1420 assert(CH_TXTATR_BREAKWORD == m_Text[pAttr->GetStart()]
1421 || CH_TXTATR_INWORD == m_Text[pAttr->GetStart()]);
1422 m_Text = m_Text.replaceAt(pAttr->GetStart(), 1, u"");
1423 // Update SwIndexes
1424 SwContentIndex aTmpIdx( this, pAttr->GetStart() );
1425 Update(aTmpIdx, 1, UpdateMode::Negative);
1426 }
1427 // do not record deletion of Format!
1428 ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
1429 DestroyAttr( pAttr );
1430 return false;
1431 }
1432 }
1433 break;
1434 }
1435
1436 case RES_TXTATR_FTN :
1437 {
1438 // Footnotes: create text node and put it into Inserts-section
1439 SwDoc& rDoc = GetDoc();
1440 SwNodes &rNodes = rDoc.GetNodes();
1441
1442 // check that footnote is inserted into body or redline section
1443 if( StartOfSectionIndex() < rNodes.GetEndOfAutotext().GetIndex() )
1444 {
1445 // This should not be allowed, prevent it here.
1446 // The dtor of the SwTextAttr does not delete the
1447 // char, so delete it explicitly here.
1448 if( SetAttrMode::NOTXTATRCHR & nInsMode )
1449 {
1450 // delete the char from the string
1451 assert(CH_TXTATR_BREAKWORD == m_Text[pAttr->GetStart()]
1452 || CH_TXTATR_INWORD == m_Text[pAttr->GetStart()]);
1453 m_Text = m_Text.replaceAt(pAttr->GetStart(), 1, u"");
1454 // Update SwIndexes
1455 SwContentIndex aTmpIdx( this, pAttr->GetStart() );
1456 Update(aTmpIdx, 1, UpdateMode::Negative);
1457 }
1458 DestroyAttr( pAttr );
1459 return false;
1460 }
1461
1462 // is a new footnote being inserted?
1463 bool bNewFootnote = nullptr == static_cast<SwTextFootnote*>(pAttr)->GetStartNode();
1464 if( bNewFootnote )
1465 {
1466 static_cast<SwTextFootnote*>(pAttr)->MakeNewTextSection( GetNodes() );
1467 SwRegHistory* pHist = GetpSwpHints()
1468 ? GetpSwpHints()->GetHistory() : nullptr;
1469 if( pHist )
1470 pHist->ChangeNodeIndex( GetIndex() );
1471 }
1472 else if ( !GetpSwpHints() || !GetpSwpHints()->IsInSplitNode() )
1473 {
1474 // existing footnote: delete all layout frames of its
1475 // footnote section
1476 SwNodeOffset nSttIdx =
1477 static_cast<SwTextFootnote*>(pAttr)->GetStartNode()->GetIndex();
1478 SwNodeOffset nEndIdx = rNodes[ nSttIdx++ ]->EndOfSectionIndex();
1479 for( ; nSttIdx < nEndIdx; ++nSttIdx )
1480 {
1481 SwContentNode* pCNd = rNodes[ nSttIdx ]->GetContentNode();
1482 if( nullptr != pCNd )
1483 pCNd->DelFrames(nullptr);
1484 else if (SwTableNode *const pTable = rNodes[nSttIdx]->GetTableNode())
1485 {
1486 pTable->DelFrames();
1487 }
1488 }
1489 }
1490
1491 if( !(SetAttrMode::NOTXTATRCHR & nInsMode) )
1492 {
1493 // must insert first, to prevent identical indexes
1494 // that could later prevent insertion into SwDoc's
1495 // footnote array
1496 SwContentIndex aNdIdx( this, pAttr->GetStart() );
1497 const OUString c(GetCharOfTextAttr(*pAttr));
1498 OUString const ins( InsertText(c, aNdIdx, nInsertFlags) );
1499 if (ins.isEmpty())
1500 {
1501 DestroyAttr(pAttr);
1502 return false; // text node full :(
1503 }
1504 nInsMode |= SetAttrMode::NOTXTATRCHR;
1505 }
1506
1507 // insert into SwDoc's footnote index array
1508 SwTextFootnote* pTextFootnote = nullptr;
1509 if( !bNewFootnote )
1510 {
1511 // moving an existing footnote (e.g. SplitNode)
1512 for( size_t n = 0; n < rDoc.GetFootnoteIdxs().size(); ++n )
1513 if( pAttr == rDoc.GetFootnoteIdxs()[n] )
1514 {
1515 // assign new index by removing and re-inserting
1516 pTextFootnote = rDoc.GetFootnoteIdxs()[n];
1517 rDoc.GetFootnoteIdxs().erase( rDoc.GetFootnoteIdxs().begin() + n );
1518 break;
1519 }
1520 // if the Undo set the StartNode, the Index isn't
1521 // in the doc's array yet!
1522 }
1523 if( !pTextFootnote )
1524 pTextFootnote = static_cast<SwTextFootnote*>(pAttr);
1525
1526 // to update the numbers and for sorting, the Node must be set
1527 static_cast<SwTextFootnote*>(pAttr)->ChgTextNode( this );
1528
1529 // do not insert footnote in redline section into footnote array
1530 if( StartOfSectionIndex() > rNodes.GetEndOfRedlines().GetIndex() )
1531 {
1532 const bool bSuccess = rDoc.GetFootnoteIdxs().insert(pTextFootnote).second;
1533 OSL_ENSURE( bSuccess, "FootnoteIdx not inserted." );
1534 }
1535 rDoc.GetFootnoteIdxs().UpdateFootnote( *this );
1536 static_cast<SwTextFootnote*>(pAttr)->SetSeqRefNo();
1537 }
1538 break;
1539
1540 case RES_TXTATR_FIELD:
1541 {
1542 // trigger notification for relevant fields, like HiddenParaFields
1544 pAttr->GetFormatField().GetField()->GetTyp()->Which()))
1545 {
1546 bHiddenPara = true;
1547 }
1548 }
1549 break;
1551 {
1552 static_cast<SwTextLineBreak*>(pAttr)->SetTextNode(this);
1553 }
1554 break;
1555
1556 }
1557 // CH_TXTATR_* are inserted for SwTextHints without EndIndex
1558 // If the caller is SwTextNode::Copy, the char has already been copied,
1559 // and SETATTR_NOTXTATRCHR prevents inserting it again here.
1560 if( !(SetAttrMode::NOTXTATRCHR & nInsMode) )
1561 {
1562 SwContentIndex aIdx( this, pAttr->GetStart() );
1563 OUString const ins( InsertText(OUString(GetCharOfTextAttr(*pAttr)),
1564 aIdx, nInsertFlags) );
1565 if (ins.isEmpty())
1566 {
1567 DestroyAttr(pAttr);
1568 return false; // text node full :(
1569 }
1570
1571 // adjust end of hint to account for inserted CH_TXTATR
1572 const sal_Int32* pEnd(pAttr->GetEnd());
1573 if (pEnd)
1574 {
1575 pAttr->SetEnd(*pEnd + 1);
1576 }
1577
1578 if (pAttr->Which() == RES_TXTATR_CONTENTCONTROL)
1579 {
1580 // Content controls have a dummy character at their end as well.
1581 SwContentIndex aEndIdx(this, *pAttr->GetEnd());
1582 OUString aEnd
1583 = InsertText(OUString(GetCharOfTextAttr(*pAttr)), aEndIdx, nInsertFlags);
1584 if (aEnd.isEmpty())
1585 {
1586 DestroyAttr(pAttr);
1587 return false;
1588 }
1589
1590 pEnd = pAttr->GetEnd();
1591 if (pEnd)
1592 {
1593 pAttr->SetEnd(*pEnd + 1);
1594 }
1595 }
1596 }
1597 }
1598
1599 // handle attributes which provide content
1600 sal_Int32 nEnd = nStart;
1601 bool bInputFieldStartCharInserted = false;
1602 bool bInputFieldEndCharInserted = false;
1603 const bool bHasContent( pAttr->HasContent() );
1604 if ( bHasContent )
1605 {
1606 switch( pAttr->Which() )
1607 {
1609 {
1610 SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pAttr);
1611 if ( pTextInputField )
1612 {
1613 if( !(SetAttrMode::NOTXTATRCHR & nMode) )
1614 {
1615 SwContentIndex aIdx( this, pAttr->GetStart() );
1616 const OUString aContent = OUStringChar(CH_TXT_ATR_INPUTFIELDSTART)
1617 + pTextInputField->GetFieldContent() + OUStringChar(CH_TXT_ATR_INPUTFIELDEND);
1618 InsertText( aContent, aIdx, nInsertFlags );
1619
1620 const sal_Int32* const pEnd(pAttr->GetEnd());
1621 assert(pEnd != nullptr);
1622 pAttr->SetEnd(*pEnd + aContent.getLength());
1623 nEnd = *pAttr->GetEnd();
1624 }
1625 else
1626 {
1627 // assure that CH_TXT_ATR_INPUTFIELDSTART and CH_TXT_ATR_INPUTFIELDEND are inserted.
1628 if ( m_Text[ pAttr->GetStart() ] != CH_TXT_ATR_INPUTFIELDSTART )
1629 {
1630 SwContentIndex aIdx( this, pAttr->GetStart() );
1631 InsertText( OUString(CH_TXT_ATR_INPUTFIELDSTART), aIdx, nInsertFlags );
1632 bInputFieldStartCharInserted = true;
1633 const sal_Int32* const pEnd(pAttr->GetEnd());
1634 assert(pEnd != nullptr);
1635 pAttr->SetEnd(*pEnd + 1);
1636 nEnd = *pAttr->GetEnd();
1637 }
1638
1639 const sal_Int32* const pEnd(pAttr->GetEnd());
1640 assert(pEnd != nullptr);
1641 if (m_Text[ *pEnd - 1 ] != CH_TXT_ATR_INPUTFIELDEND)
1642 {
1643 SwContentIndex aIdx( this, *pEnd );
1644 InsertText( OUString(CH_TXT_ATR_INPUTFIELDEND), aIdx, nInsertFlags );
1645 bInputFieldEndCharInserted = true;
1646 pAttr->SetEnd(*pEnd + 1);
1647 nEnd = *pAttr->GetEnd();
1648 }
1649 }
1650 }
1651 }
1652 break;
1653 default:
1654 break;
1655 }
1656 }
1657
1659
1660 // handle overlap with an existing InputField
1661 bool bInsertHint = true;
1662 {
1663 const SwTextInputField* pTextInputField = GetOverlappingInputField( *pAttr );
1664 if ( pTextInputField != nullptr )
1665 {
1666 if ( pAttr->End() == nullptr )
1667 {
1668 bInsertHint = false;
1669 DestroyAttr(pAttr);
1670 }
1671 else
1672 {
1673 if ( pAttr->GetStart() > pTextInputField->GetStart() )
1674 {
1675 pAttr->SetStart( pTextInputField->GetStart() );
1676 }
1677 if ( *(pAttr->End()) < *(pTextInputField->End()) )
1678 {
1679 pAttr->SetEnd(*(pTextInputField->End()));
1680 }
1681 }
1682 }
1683 }
1684
1685 if (bInsertHint)
1686 {
1687 // Handle the invariant that a plain text content control has the same character formatting
1688 // for all of its content.
1689 auto* pTextContentControl = static_txtattr_cast<SwTextContentControl*>(
1691 if (pTextContentControl)
1692 {
1693 auto& rFormatContentControl
1694 = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
1695 std::shared_ptr<SwContentControl> pContentControl
1696 = rFormatContentControl.GetContentControl();
1697 if (pAttr->End() != nullptr && pContentControl->GetPlainText())
1698 {
1699 if (pAttr->GetStart() > pTextContentControl->GetStart())
1700 {
1701 pAttr->SetStart(pTextContentControl->GetStart());
1702 }
1703 if (*pAttr->End() < *pTextContentControl->End())
1704 {
1705 pAttr->SetEnd(*pTextContentControl->End());
1706 }
1707 }
1708 }
1709 }
1710
1711 const bool bRet = bInsertHint
1712 && m_pSwpHints->TryInsertHint( pAttr, *this, nMode );
1713
1714 if ( !bRet )
1715 {
1716 if ( bDummyChar
1717 && !(SetAttrMode::NOTXTATRCHR & nMode) )
1718 {
1719 // undo insertion of dummy character
1720 // N.B. cannot insert the dummy character after inserting the hint,
1721 // because if the hint has no extent it will be moved in InsertText,
1722 // resulting in infinite recursion
1723 assert((CH_TXTATR_BREAKWORD == m_Text[nStart] ||
1724 CH_TXTATR_INWORD == m_Text[nStart] ));
1725 SwContentIndex aIdx( this, nStart );
1726 EraseText( aIdx, 1 );
1727 }
1728
1729 if ( bHasContent )
1730 {
1731 if ( !(SetAttrMode::NOTXTATRCHR & nMode)
1732 && (nEnd - nStart) > 0 )
1733 {
1734 SwContentIndex aIdx( this, nStart );
1735 EraseText( aIdx, (nEnd - nStart) );
1736 }
1737 else
1738 {
1739 if ( bInputFieldEndCharInserted
1740 && (nEnd - nStart) > 0 )
1741 {
1742 SwContentIndex aIdx( this, nEnd - 1 );
1743 EraseText( aIdx, 1 );
1744 }
1745
1746 if ( bInputFieldStartCharInserted )
1747 {
1748 SwContentIndex aIdx( this, nStart );
1749 EraseText( aIdx, 1 );
1750 }
1751 }
1752 }
1753 }
1754
1755 if ( bHiddenPara )
1756 {
1758 }
1759
1760 return bRet;
1761}
1762
1764{
1765 if ( !HasHints() )
1766 {
1767 OSL_FAIL("DeleteAttribute called, but text node without hints?");
1768 return;
1769 }
1770
1771 if ( pAttr->HasDummyChar() )
1772 {
1773 // copy index!
1774 const SwContentIndex aIdx( this, pAttr->GetStart() );
1775 // erase the CH_TXTATR, which will also delete pAttr
1776 EraseText( aIdx, 1 );
1777 }
1778 else if ( pAttr->HasContent() )
1779 {
1780 const SwContentIndex aIdx( this, pAttr->GetStart() );
1781 assert(pAttr->End() != nullptr);
1782 EraseText( aIdx, *pAttr->End() - pAttr->GetStart() );
1783 }
1784 else
1785 {
1786 // create MsgHint before start/end become invalid
1787 SwUpdateAttr aHint(
1788 pAttr->GetStart(),
1789 *pAttr->GetEnd(),
1790 pAttr->Which());
1791
1792 m_pSwpHints->Delete( pAttr );
1793 SwTextAttr::Destroy( pAttr, GetDoc().GetAttrPool() );
1794 CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
1795
1797 }
1798}
1799
1800//FIXME: this does NOT respect SORT NUMBER (for CHARFMT)!
1802 const sal_uInt16 nWhich,
1803 const sal_Int32 nStart,
1804 const sal_Int32 nEnd )
1805{
1806 if ( !HasHints() )
1807 return;
1808
1809 for ( size_t nPos = 0; m_pSwpHints && nPos < m_pSwpHints->Count(); ++nPos )
1810 {
1811 SwTextAttr * const pTextHt = m_pSwpHints->Get( nPos );
1812 const sal_Int32 nHintStart = pTextHt->GetStart();
1813 if (nStart < nHintStart)
1814 {
1815 break; // sorted by start
1816 }
1817 else if ( (nStart == nHintStart) && (nWhich == pTextHt->Which()) )
1818 {
1819 if ( nWhich == RES_CHRATR_HIDDEN )
1820 {
1821 assert(!"hey, that's a CHRATR! how did that get in?");
1823 }
1824 else if ( nWhich == RES_TXTATR_CHARFMT )
1825 {
1826 // Check if character format contains hidden attribute:
1827 const SwCharFormat* pFormat = pTextHt->GetCharFormat().GetCharFormat();
1828 if ( SfxItemState::SET == pFormat->GetItemState( RES_CHRATR_HIDDEN ) )
1830 }
1831 // #i75430# Recalc hidden flags if necessary
1832 else if ( nWhich == RES_TXTATR_AUTOFMT )
1833 {
1834 // Check if auto style contains hidden attribute:
1835 const SfxPoolItem* pHiddenItem = CharFormat::GetItem( *pTextHt, RES_CHRATR_HIDDEN );
1836 if ( pHiddenItem )
1838 // for auto styles DeleteAttributes is only called from Undo
1839 // so it shouldn't need to care about ignore start/end flags
1840 }
1841
1842 sal_Int32 const * const pEndIdx = pTextHt->GetEnd();
1843
1844 if ( pTextHt->HasDummyChar() )
1845 {
1846 // copy index!
1847 const SwContentIndex aIdx( this, nStart );
1848 // erase the CH_TXTATR, which will also delete pTextHt
1849 EraseText( aIdx, 1 );
1850 }
1851 else if ( pTextHt->HasContent() )
1852 {
1853 const SwContentIndex aIdx( this, nStart );
1854 OSL_ENSURE( pTextHt->End() != nullptr, "<SwTextNode::DeleteAttributes(..)> - missing End() at <SwTextAttr> instance which has content" );
1855 EraseText( aIdx, *pTextHt->End() - nStart );
1856 }
1857 else if( *pEndIdx == nEnd )
1858 {
1859 // Create MsgHint before Start and End are gone.
1860 // For HiddenParaFields it's not necessary to call
1861 // SetCalcHiddenParaField because the dtor does that.
1862 SwUpdateAttr aHint(
1863 nStart,
1864 *pEndIdx,
1865 nWhich);
1866
1867 m_pSwpHints->DeleteAtPos( nPos );
1868 SwTextAttr::Destroy( pTextHt, GetDoc().GetAttrPool() );
1869 CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
1870 }
1871 }
1872 }
1874}
1875
1876void SwTextNode::DelSoftHyph( const sal_Int32 nStt, const sal_Int32 nEnd )
1877{
1878 sal_Int32 nFndPos = nStt;
1879 sal_Int32 nEndPos = nEnd;
1880 for (;;)
1881 {
1882 nFndPos = m_Text.indexOf(CHAR_SOFTHYPHEN, nFndPos);
1883 if (nFndPos<0 || nFndPos>=nEndPos )
1884 {
1885 break;
1886 }
1887 const SwContentIndex aIdx( this, nFndPos );
1888 EraseText( aIdx, 1 );
1889 --nEndPos;
1890 }
1891}
1892
1893bool SwTextNode::IsIgnoredCharFormatForNumbering(const sal_uInt16 nWhich, bool bIsCharStyle)
1894{
1895 // LO can save the char background as either shading or highlight, so check which mode is currently chosen.
1896 // Shading does not affect the numbering. Highlighting does (but isn't allowed in a char style).
1897 if (nWhich == RES_CHRATR_BACKGROUND)
1898 return bIsCharStyle || SvtFilterOptions::Get().IsCharBackground2Shading();
1899
1900 return (nWhich == RES_CHRATR_UNDERLINE
1901 || nWhich == RES_CHRATR_ESCAPEMENT);
1902}
1903
1904// Set these attributes on SwTextNode. If they apply to the entire paragraph
1905// text, set them in the SwTextNode's item set (SwContentNode::SetAttr).
1907 const SfxItemSet& rSet,
1908 const sal_Int32 nStt,
1909 const sal_Int32 nEnd,
1910 const SetAttrMode nMode,
1911 SwTextAttr **ppNewTextAttr )
1912{
1913 if( !rSet.Count() )
1914 return false;
1915
1916 // split sets (for selection in nodes)
1917 const SfxItemSet* pSet = &rSet;
1919
1920 // entire paragraph
1921 if ( !nStt && (nEnd == m_Text.getLength()) &&
1922 !(nMode & SetAttrMode::NOFORMATATTR ) )
1923 {
1924 // if the node already has CharFormat hints, the new attributes must
1925 // be set as hints too to override those.
1926 bool bHasCharFormats = false;
1927 if ( HasHints() )
1928 {
1929 for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
1930 {
1931 if ( m_pSwpHints->Get( n )->IsCharFormatAttr() )
1932 {
1933 bHasCharFormats = true;
1934 break;
1935 }
1936 }
1937 }
1938
1939 if( !bHasCharFormats )
1940 {
1941 aTextSet.Put( rSet );
1942 // If there are any character attributes in rSet,
1943 // we want to set them at the paragraph:
1944 if( aTextSet.Count() != rSet.Count() )
1945 {
1946 const bool bRet = SetAttr( rSet );
1947 if( !aTextSet.Count() )
1948 return bRet;
1949 }
1950
1951 // check for auto style:
1952 if ( const SwFormatAutoFormat* pItem = aTextSet.GetItemIfSet( RES_TXTATR_AUTOFMT, false ) )
1953 {
1954 const bool bRet = SetAttr( *pItem->GetStyleHandle() );
1955 if( 1 == aTextSet.Count() )
1956 return bRet;
1957 }
1958
1959 // Continue with the text attributes:
1960 pSet = &aTextSet;
1961 }
1962 }
1963
1965
1967
1968 size_t nCount = 0;
1969 SfxItemIter aIter( *pSet );
1970 const SfxPoolItem* pItem = aIter.GetCurItem();
1971
1972 do
1973 {
1974 if (!IsInvalidItem(pItem))
1975 {
1976 const sal_uInt16 nWhich = pItem->Which();
1977 OSL_ENSURE( isCHRATR(nWhich) || isTXTATR(nWhich),
1978 "SwTextNode::SetAttr(): unknown attribute" );
1979 if ( isCHRATR(nWhich) || isTXTATR(nWhich) )
1980 {
1981 if ((RES_TXTATR_CHARFMT == nWhich) &&
1982 (GetDoc().GetDfltCharFormat() ==
1983 static_cast<const SwFormatCharFormat*>(pItem)->GetCharFormat()))
1984 {
1985 RstTextAttr( nStt, nEnd - nStt, RES_TXTATR_CHARFMT );
1986 DontExpandFormat( nStt );
1987 }
1988 else
1989 {
1990 if (isCHRATR(nWhich) ||
1991 (RES_TXTATR_UNKNOWN_CONTAINER == nWhich))
1992 {
1993 aCharSet.Put( *pItem );
1994 }
1995 else
1996 {
1997
1998 SwTextAttr *const pNew = MakeTextAttr( GetDoc(),
1999 const_cast<SfxPoolItem&>(*pItem), nStt, nEnd );
2000 if ( pNew )
2001 {
2002 // store the first one we create into the pp
2003 if (ppNewTextAttr && !*ppNewTextAttr)
2004 *ppNewTextAttr = pNew;
2005 if ( nEnd != nStt && !pNew->GetEnd() )
2006 {
2007 OSL_FAIL("Attribute without end, but area marked");
2008 DestroyAttr( pNew ); // do not insert
2009 }
2010 else if ( InsertHint( pNew, nMode ) )
2011 {
2012 ++nCount;
2013 }
2014 }
2015 }
2016 }
2017 }
2018 }
2019 pItem = aIter.NextItem();
2020 } while(pItem);
2021
2022 if ( aCharSet.Count() )
2023 {
2024 SwTextAttr* pTmpNew = MakeTextAttr( GetDoc(), aCharSet, nStt, nEnd );
2025 if ( InsertHint( pTmpNew, nMode ) )
2026 {
2027 ++nCount;
2028 }
2029 }
2030
2032
2033 return nCount != 0;
2034}
2035
2036static void lcl_MergeAttr( SfxItemSet& rSet, const SfxPoolItem& rAttr )
2037{
2038 if ( RES_TXTATR_AUTOFMT == rAttr.Which() )
2039 {
2040 const SfxItemSet* pCFSet = CharFormat::GetItemSet( rAttr );
2041 if ( !pCFSet )
2042 return;
2043 SfxWhichIter aIter( *pCFSet );
2044 sal_uInt16 nWhich = aIter.FirstWhich();
2045 while( nWhich )
2046 {
2047 const SfxPoolItem* pItem = nullptr;
2048 if( ( nWhich < RES_CHRATR_END ||
2049 RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) &&
2050 ( SfxItemState::SET == aIter.GetItemState( true, &pItem ) ) )
2051 rSet.Put( *pItem );
2052 nWhich = aIter.NextWhich();
2053 }
2054 }
2055 else
2056 rSet.Put( rAttr );
2057}
2058
2059static void lcl_MergeAttr_ExpandChrFormat( SfxItemSet& rSet, const SfxPoolItem& rAttr )
2060{
2061 if( RES_TXTATR_CHARFMT == rAttr.Which() ||
2062 RES_TXTATR_INETFMT == rAttr.Which() ||
2063 RES_TXTATR_AUTOFMT == rAttr.Which() )
2064 {
2065 const SfxItemSet* pCFSet = CharFormat::GetItemSet( rAttr );
2066
2067 if ( pCFSet )
2068 {
2069 SfxWhichIter aIter( *pCFSet );
2070 sal_uInt16 nWhich = aIter.FirstWhich();
2071 while( nWhich )
2072 {
2073 const SfxPoolItem* pItem = nullptr;
2074 if( ( nWhich < RES_CHRATR_END ||
2075 ( RES_TXTATR_AUTOFMT == rAttr.Which() && RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) ) &&
2076 ( SfxItemState::SET == aIter.GetItemState( true, &pItem ) ) )
2077 rSet.Put( *pItem );
2078 nWhich = aIter.NextWhich();
2079 }
2080 }
2081 }
2082
2083/* If multiple attributes overlap, the last one wins!
2084 Probably this can only happen between a RES_TXTATR_INETFMT and one of the
2085 other hints, because BuildPortions ensures that CHARFMT/AUTOFMT don't
2086 overlap. But there may be multiple CHARFMT/AUTOFMT with exactly the same
2087 start/end, sorted by BuildPortions, in which case the same logic applies.
2088
2089 1234567890123456789
2090 |------------| Font1
2091 |------| Font2
2092 ^ ^
2093 |--| query range: -> Font2
2094*/
2095 // merge into set
2096 rSet.Put( rAttr );
2097}
2098
2099namespace {
2100
2101struct SwPoolItemEndPair
2102{
2103public:
2104 const SfxPoolItem* mpItem;
2105 sal_Int32 mnEndPos;
2106
2107 SwPoolItemEndPair() : mpItem( nullptr ), mnEndPos( 0 ) {};
2108};
2109
2110}
2111
2113 SfxItemSet& rSet )
2114{
2115 ::sw::ListLevelIndents const indents(rTextNode.AreListLevelIndentsApplicable());
2116 if (indents == ::sw::ListLevelIndents::No)
2117 return;
2118
2119 const SwNumRule* pRule = rTextNode.GetNumRule();
2120 if ( pRule && rTextNode.GetActualListLevel() >= 0 )
2121 {
2122 const SwNumFormat& rFormat = pRule->Get(o3tl::narrowing<sal_uInt16>(rTextNode.GetActualListLevel()));
2124 {
2125 if (indents & ::sw::ListLevelIndents::FirstLine)
2126 {
2127 SvxFirstLineIndentItem const firstLine(static_cast<short>(rFormat.GetFirstLineIndent()), RES_MARGIN_FIRSTLINE);
2128 rSet.Put(firstLine);
2129 }
2131 {
2132 SvxTextLeftMarginItem const leftMargin(rFormat.GetIndentAt(), RES_MARGIN_TEXTLEFT);
2133 rSet.Put(leftMargin);
2134 }
2135 }
2136 }
2137}
2138
2139// request the attributes of the TextNode at the range
2140bool SwTextNode::GetParaAttr(SfxItemSet& rSet, sal_Int32 nStt, sal_Int32 nEnd,
2141 const bool bOnlyTextAttr, const bool bGetFromChrFormat,
2142 const bool bMergeIndentValuesOfNumRule,
2143 SwRootFrame const*const pLayout) const
2144{
2145 assert(!rSet.Count()); // handled inconsistently, typically an error?
2146
2147 if (pLayout && pLayout->HasMergedParas())
2148 {
2150 {
2151 return false; // ignore deleted node
2152 }
2153 }
2154
2155 // get the node's automatic attributes
2156 SfxItemSet aFormatSet( *rSet.GetPool(), rSet.GetRanges() );
2157 if (!bOnlyTextAttr)
2158 {
2159 SwTextNode const& rParaPropsNode(
2160 sw::GetAttrMerged(aFormatSet, *this, pLayout));
2161 if (bMergeIndentValuesOfNumRule)
2162 {
2163 lcl_MergeListLevelIndentAsLRSpaceItem(rParaPropsNode, aFormatSet);
2164 }
2165 }
2166
2167 if( HasHints() )
2168 {
2169 // First, check which text attributes are valid in the range.
2170 // cases:
2171 // Ambiguous, if
2172 // * the attribute is wholly contained in the range
2173 // * the attribute end is in the range
2174 // * the attribute start is in the range
2175 // Unambiguous (merge into set), if
2176 // * the attribute wholly contains the range
2177 // Ignored, if
2178 // * the attribute is wholly outside the range
2179
2180 void (*fnMergeAttr)( SfxItemSet&, const SfxPoolItem& )
2181 = bGetFromChrFormat ? &lcl_MergeAttr_ExpandChrFormat
2182 : &lcl_MergeAttr;
2183
2184 const size_t nSize = m_pSwpHints->Count();
2185
2186 if (nStt == nEnd) // no range:
2187 {
2188 for (size_t n = 0; n < nSize; ++n)
2189 {
2190 const SwTextAttr* pHt = m_pSwpHints->Get(n);
2191 const sal_Int32 nAttrStart = pHt->GetStart();
2192 if (nAttrStart > nEnd) // behind the range
2193 break;
2194
2195 const sal_Int32* pAttrEnd = pHt->End();
2196 if ( ! pAttrEnd ) // no attributes without end
2197 continue;
2198
2199 if( ( nAttrStart < nStt &&
2200 ( pHt->DontExpand() ? nStt < *pAttrEnd
2201 : nStt <= *pAttrEnd )) ||
2202 ( nStt == nAttrStart &&
2203 ( nAttrStart == *pAttrEnd || !nStt )))
2204 (*fnMergeAttr)( rSet, pHt->GetAttr() );
2205 }
2206 }
2207 else // a query range is defined
2208 {
2209 // #i75299#
2210 std::optional< std::vector< SwPoolItemEndPair > > pAttrArr;
2211
2212 const size_t coArrSz = RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN;
2213
2214 for (size_t n = 0; n < nSize; ++n)
2215 {
2216 const SwTextAttr* pHt = m_pSwpHints->Get(n);
2217 const sal_Int32 nAttrStart = pHt->GetStart();
2218 if (nAttrStart > nEnd) // outside, behind
2219 break;
2220
2221 const sal_Int32* pAttrEnd = pHt->End();
2222 if ( ! pAttrEnd ) // no attributes without end
2223 continue;
2224
2225 bool bChkInvalid = false;
2226 if (nAttrStart <= nStt) // before or exactly Start
2227 {
2228 if (*pAttrEnd <= nStt) // outside, before
2229 continue;
2230
2231 if (nEnd <= *pAttrEnd) // behind or exactly End
2232 (*fnMergeAttr)( aFormatSet, pHt->GetAttr() );
2233 else
2234// else if( pHt->GetAttr() != aFormatSet.Get( pHt->Which() ) )
2235 // ambiguous
2236 bChkInvalid = true;
2237 }
2238 else if (nAttrStart < nEnd // starts in the range
2239)// && pHt->GetAttr() != aFormatSet.Get( pHt->Which() ) )
2240 bChkInvalid = true;
2241
2242 if( bChkInvalid )
2243 {
2244 // ambiguous?
2245 std::optional< SfxItemIter > oItemIter;
2246 const SfxPoolItem* pItem = nullptr;
2247
2248 if ( RES_TXTATR_AUTOFMT == pHt->Which() )
2249 {
2250 const SfxItemSet* pAutoSet = CharFormat::GetItemSet( pHt->GetAttr() );
2251 if ( pAutoSet )
2252 {
2253 oItemIter.emplace( *pAutoSet );
2254 pItem = oItemIter->GetCurItem();
2255 }
2256 }
2257 else
2258 pItem = &pHt->GetAttr();
2259
2260 const sal_Int32 nHintEnd = *pAttrEnd;
2261
2262 for (; pItem; pItem = oItemIter ? oItemIter->NextItem() : nullptr)
2263 {
2264 const sal_uInt16 nHintWhich = pItem->Which();
2265 OSL_ENSURE(!isUNKNOWNATR(nHintWhich),
2266 "SwTextNode::GetAttr(): unknown attribute?");
2267
2268 if (!pAttrArr)
2269 {
2270 pAttrArr = std::vector< SwPoolItemEndPair >(coArrSz);
2271 }
2272
2273 std::vector< SwPoolItemEndPair >::iterator pPrev = pAttrArr->begin();
2274 if (isCHRATR(nHintWhich) ||
2275 isTXTATR_WITHEND(nHintWhich))
2276 {
2277 pPrev += nHintWhich - RES_CHRATR_BEGIN;
2278 }
2279 else
2280 {
2281 pPrev = pAttrArr->end();
2282 }
2283
2284 if( pPrev != pAttrArr->end() )
2285 {
2286 if( !pPrev->mpItem )
2287 {
2288 if ( bOnlyTextAttr || *pItem != aFormatSet.Get( nHintWhich ) )
2289 {
2290 if( nAttrStart > nStt )
2291 {
2292 rSet.InvalidateItem( nHintWhich );
2293 pPrev->mpItem = INVALID_POOL_ITEM;
2294 }
2295 else
2296 {
2297 pPrev->mpItem = pItem;
2298 pPrev->mnEndPos = nHintEnd;
2299 }
2300 }
2301 }
2302 else if( !IsInvalidItem(pPrev->mpItem) )
2303 {
2304 if( pPrev->mnEndPos == nAttrStart &&
2305 *pPrev->mpItem == *pItem )
2306 {
2307 pPrev->mpItem = pItem;
2308 pPrev->mnEndPos = nHintEnd;
2309 }
2310 else
2311 {
2312 rSet.InvalidateItem( nHintWhich );
2313 pPrev->mpItem = INVALID_POOL_ITEM;
2314 }
2315 }
2316 }
2317 } // end while
2318 }
2319 }
2320
2321 if (pAttrArr)
2322 {
2323 for (size_t n = 0; n < coArrSz; ++n)
2324 {
2325 const SwPoolItemEndPair& rItemPair = (*pAttrArr)[ n ];
2326 if( rItemPair.mpItem && !IsInvalidItem(rItemPair.mpItem) )
2327 {
2328 const sal_uInt16 nWh =
2329 o3tl::narrowing<sal_uInt16>(n + RES_CHRATR_BEGIN);
2330
2331 if (nEnd <= rItemPair.mnEndPos) // behind or exactly end
2332 {
2333 if( *rItemPair.mpItem != aFormatSet.Get( nWh ) )
2334 (*fnMergeAttr)( rSet, *rItemPair.mpItem );
2335 }
2336 else
2337 // ambiguous
2338 rSet.InvalidateItem( nWh );
2339 }
2340 }
2341 }
2342 }
2343 if( aFormatSet.Count() )
2344 {
2345 // remove all from the format-set that are also set in the text-set
2346 aFormatSet.Differentiate( rSet );
2347 }
2348 }
2349
2350 if (aFormatSet.Count())
2351 {
2352 // now "merge" everything
2353 rSet.Put( aFormatSet );
2354 }
2355
2356 return rSet.Count() != 0;
2357}
2358
2359namespace
2360{
2361
2362typedef std::pair<sal_Int32, sal_Int32> AttrSpan_t;
2363typedef std::multimap<AttrSpan_t, const SwTextAttr*> AttrSpanMap_t;
2364
2365struct IsAutoStyle
2366{
2367 bool
2368 operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
2369 const
2370 {
2371 return i_rAttrSpan.second && i_rAttrSpan.second->Which() == RES_TXTATR_AUTOFMT;
2372 }
2373};
2374
2378struct RemovePresentAttrs
2379{
2380 explicit RemovePresentAttrs(SfxItemSet& io_rAttrSet)
2381 : m_rAttrSet(io_rAttrSet)
2382 {
2383 }
2384
2385 void
2386 operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
2387 const
2388 {
2389 if (!i_rAttrSpan.second)
2390 {
2391 return;
2392 }
2393
2394 const SwTextAttr* const pAutoStyle(i_rAttrSpan.second);
2395 SfxItemIter aIter(m_rAttrSet);
2396 for (const SfxPoolItem* pItem(aIter.GetCurItem()); pItem; pItem = aIter.NextItem())
2397 {
2398 const sal_uInt16 nWhich(pItem->Which());
2399 if (CharFormat::IsItemIncluded(nWhich, pAutoStyle))
2400 {
2401 aIter.ClearItem();
2402 }
2403 }
2404 }
2405
2406private:
2407 SfxItemSet& m_rAttrSet;
2408};
2409
2416void
2417lcl_CollectHintSpans(const SwpHints& i_rHints, const sal_Int32 nLength,
2418 AttrSpanMap_t& o_rSpanMap)
2419{
2420 sal_Int32 nLastEnd(0);
2421
2422 for (size_t i = 0; i < i_rHints.Count(); ++i)
2423 {
2424 const SwTextAttr* pHint = i_rHints.Get(i);
2425 const sal_uInt16 nWhich(pHint->Which());
2426 if (nWhich == RES_TXTATR_CHARFMT || nWhich == RES_TXTATR_AUTOFMT)
2427 {
2428 const AttrSpan_t aSpan(pHint->GetStart(), *pHint->End());
2429 o_rSpanMap.emplace(aSpan, pHint);
2430
2431 // < not != because there may be multiple CHARFMT at same range
2432 if (nLastEnd < aSpan.first)
2433 {
2434 // insert dummy span covering the gap
2435 o_rSpanMap.emplace( AttrSpan_t(nLastEnd, aSpan.first), nullptr );
2436 }
2437
2438 nLastEnd = aSpan.second;
2439 }
2440 }
2441
2442 // no hints at the end (special case: no hints at all in i_rHints)
2443 if (nLastEnd != nLength && nLength != 0)
2444 {
2445 o_rSpanMap.emplace(AttrSpan_t(nLastEnd, nLength), nullptr);
2446 }
2447}
2448
2449void
2450lcl_FillWhichIds(const SfxItemSet& i_rAttrSet, std::vector<sal_uInt16>& o_rClearIds)
2451{
2452 o_rClearIds.reserve(i_rAttrSet.Count());
2453 SfxItemIter aIter(i_rAttrSet);
2454 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
2455 {
2456 o_rClearIds.push_back(pItem->Which());
2457 }
2458}
2459
2460struct SfxItemSetClearer
2461{
2462 SfxItemSet & m_rItemSet;
2463 explicit SfxItemSetClearer(SfxItemSet & rItemSet) : m_rItemSet(rItemSet) { }
2464 void operator()(sal_uInt16 const nWhich) { m_rItemSet.ClearItem(nWhich); }
2465};
2466
2467}
2468
2472void
2474{
2475 typedef AttrSpanMap_t::iterator AttrSpanMap_iterator_t;
2476 AttrSpanMap_t aAttrSpanMap;
2477
2478 if (i_rAttrSet.Count() == 0)
2479 {
2480 return;
2481 }
2482
2483 // 1. Identify all spans in hints' array
2484
2485 lcl_CollectHintSpans(*m_pSwpHints, m_Text.getLength(), aAttrSpanMap);
2486
2487 // 2. Go through all spans and insert new attrs
2488
2489 AttrSpanMap_iterator_t aCurRange(aAttrSpanMap.begin());
2490 const AttrSpanMap_iterator_t aEnd(aAttrSpanMap.end());
2491 while (aCurRange != aEnd)
2492 {
2493 typedef std::pair<AttrSpanMap_iterator_t, AttrSpanMap_iterator_t>
2494 AttrSpanMapRange_t;
2495 AttrSpanMapRange_t aRange(aAttrSpanMap.equal_range(aCurRange->first));
2496
2497 // 2a. Collect attributes to insert
2498
2499 SfxItemSet aCurSet(i_rAttrSet);
2500 std::for_each(aRange.first, aRange.second, RemovePresentAttrs(aCurSet));
2501
2502 // 2b. Insert automatic style containing the collected attributes
2503
2504 if (aCurSet.Count() != 0)
2505 {
2506 AttrSpanMap_iterator_t aAutoStyleIt(
2507 std::find_if(aRange.first, aRange.second, IsAutoStyle()));
2508 if (aAutoStyleIt != aRange.second)
2509 {
2510 // there already is an automatic style on that span:
2511 // create new one and remove the original one
2512 SwTextAttr* const pAutoStyle(const_cast<SwTextAttr*>(aAutoStyleIt->second));
2513 const std::shared_ptr<SfxItemSet> pOldStyle(
2514 static_cast<const SwFormatAutoFormat&>(
2515 pAutoStyle->GetAttr()).GetStyleHandle());
2516 aCurSet.Put(*pOldStyle);
2517
2518 // remove the old hint
2519 m_pSwpHints->Delete(pAutoStyle);
2520 DestroyAttr(pAutoStyle);
2521 }
2522 m_pSwpHints->Insert(
2523 MakeTextAttr(GetDoc(), aCurSet,
2524 aCurRange->first.first, aCurRange->first.second));
2525 }
2526
2527 aCurRange = aRange.second;
2528 }
2529
2530 // hints were directly inserted, so need to fix the Ignore flags now
2531 m_pSwpHints->MergePortions(*this);
2532
2533 // 3. Clear items from the node
2534 std::vector<sal_uInt16> aClearedIds;
2535 lcl_FillWhichIds(i_rAttrSet, aClearedIds);
2536 ClearItemsFromAttrSet(aClearedIds);
2537}
2538
2540{
2541 SfxItemSet aThisSet( GetDoc().GetAttrPool(), aCharFormatSetRange );
2542 if( HasSwAttrSet() && GetpSwAttrSet()->Count() )
2543 aThisSet.Put( *GetpSwAttrSet() );
2544
2546
2547 if( pNd == this )
2548 {
2549 impl_FormatToTextAttr(aThisSet);
2550 }
2551 else
2552 {
2553 // There are five possible combinations of items from this and
2554 // pNd (pNd is the 'main' node):
2555
2556 // case pNd this action
2557
2558 // 1 - - do nothing
2559 // 2 - a convert item to attr of this
2560 // 3 a - convert item to attr of pNd
2561 // 4 a a clear item in this
2562 // 5 a b convert item to attr of this
2563
2565 if( pNd->HasSwAttrSet() && pNd->GetpSwAttrSet()->Count() )
2566 aNdSet.Put( *pNd->GetpSwAttrSet() );
2567
2568 pNd->GetOrCreateSwpHints();
2569
2570 std::vector<sal_uInt16> aProcessedIds;
2571
2572 if( aThisSet.Count() )
2573 {
2574 SfxItemIter aIter( aThisSet );
2575 const SfxPoolItem* pItem = aIter.GetCurItem(), *pNdItem = nullptr;
2576 SfxItemSet aConvertSet( GetDoc().GetAttrPool(), aCharFormatSetRange );
2577 std::vector<sal_uInt16> aClearWhichIds;
2578
2579 do
2580 {
2581 if( SfxItemState::SET == aNdSet.GetItemState( pItem->Which(), false, &pNdItem ) )
2582 {
2583 if (*pItem == *pNdItem) // 4
2584 {
2585 aClearWhichIds.push_back( pItem->Which() );
2586 }
2587 else // 5
2588 {
2589 aConvertSet.Put(*pItem);
2590 }
2591 aProcessedIds.push_back(pItem->Which());
2592 }
2593 else // 2
2594 {
2595 aConvertSet.Put(*pItem);
2596 }
2597
2598 pItem = aIter.NextItem();
2599 } while (pItem);
2600
2601 // 4/ clear items of this that are set with the same value on pNd
2602 ClearItemsFromAttrSet( aClearWhichIds );
2603
2604 // 2, 5/ convert all other items to attrs
2605 impl_FormatToTextAttr(aConvertSet);
2606 }
2607
2608 {
2609 std::for_each(aProcessedIds.begin(), aProcessedIds.end(),
2610 SfxItemSetClearer(aNdSet));
2611
2612 // 3/ convert items to attrs
2613 pNd->impl_FormatToTextAttr(aNdSet);
2614
2615 if( aNdSet.Count() )
2616 {
2617 SwFormatChg aTmp1( pNd->GetFormatColl() );
2618 pNd->CallSwClientNotify(sw::LegacyModifyHint(&aTmp1, &aTmp1));
2619 }
2620 }
2621 }
2622
2624
2625 pNd->TryDeleteSwpHints();
2626}
2627
2629{
2630 m_bDDEFields = m_bFootnote = false;
2631 const size_t nSize = Count();
2632 for( size_t nPos = 0; nPos < nSize; ++nPos )
2633 {
2634 const SwTextAttr* pAttr = Get( nPos );
2635 switch( pAttr->Which() )
2636 {
2637 case RES_TXTATR_FTN:
2638 m_bFootnote = true;
2639 if ( m_bDDEFields )
2640 return;
2641 break;
2642 case RES_TXTATR_FIELD:
2643 {
2644 const SwField* pField = pAttr->GetFormatField().GetField();
2645 if( SwFieldIds::Dde == pField->GetTyp()->Which() )
2646 {
2647 m_bDDEFields = true;
2648 if ( m_bFootnote )
2649 return;
2650 }
2651 }
2652 break;
2653 }
2654 }
2655}
2656
2658{
2659 m_bCalcHiddenParaField = false;
2660 const bool bOldHiddenByParaField = m_bHiddenByParaField;
2661 bool bNewHiddenByParaField = false;
2662 int nNewResultWeight = 0;
2663 const size_t nSize = Count();
2664 const SwTextAttr* pTextHt;
2665
2666 for (size_t nPos = 0; nPos < nSize; ++nPos)
2667 {
2668 pTextHt = Get(nPos);
2669 const sal_uInt16 nWhich = pTextHt->Which();
2670
2671 if (RES_TXTATR_FIELD == nWhich)
2672 {
2673 // see also SwTextFrame::IsHiddenNow()
2674 const SwFormatField& rField = pTextHt->GetFormatField();
2675 int nCurWeight = m_rParent.GetDoc().FieldCanHideParaWeight(rField.GetField()->GetTyp()->Which());
2676 if (nCurWeight > nNewResultWeight)
2677 {
2678 nNewResultWeight = nCurWeight;
2679 bNewHiddenByParaField = m_rParent.GetDoc().FieldHidesPara(*rField.GetField());
2680 }
2681 else if (nCurWeight == nNewResultWeight && bNewHiddenByParaField)
2682 {
2683 // Currently, for both supported hiding types (HiddenPara, Database), "Don't hide"
2684 // takes precedence - i.e., if there's a "Don't hide" field of that weight, we only
2685 // care about fields of higher weight.
2686 bNewHiddenByParaField = m_rParent.GetDoc().FieldHidesPara(*rField.GetField());
2687 }
2688 }
2689 }
2690 SetHiddenByParaField(bNewHiddenByParaField);
2691 return bOldHiddenByParaField != bNewHiddenByParaField;
2692}
2693
2694void SwpHints::NoteInHistory( SwTextAttr *pAttr, const bool bNew )
2695{
2696 if ( m_pHistory ) { m_pHistory->AddHint( pAttr, bNew ); }
2697}
2698
2699namespace {
2700struct Portion {
2701 SwTextAttr* pTextAttr;
2702 sal_Int32 nKey;
2703 bool isRsidOnlyAutoFormat;
2704};
2705typedef std::vector< Portion > PortionMap;
2706enum MergeResult { MATCH, DIFFER_ONLY_RSID, DIFFER };
2707}
2708
2709static MergeResult lcl_Compare_Attributes(
2710 int i, int j,
2711 const std::pair< PortionMap::const_iterator, PortionMap::const_iterator >& aRange1,
2712 const std::pair< PortionMap::const_iterator, PortionMap::const_iterator >& aRange2,
2713 std::vector<bool>& RsidOnlyAutoFormatFlagMap);
2714
2716{
2717 if ( !Count() )
2718 return false;
2719
2720 // sort before merging
2721 Resort();
2722
2723 bool bRet = false;
2724 PortionMap aPortionMap;
2725 aPortionMap.reserve(Count() + 1);
2726 std::vector<bool> RsidOnlyAutoFormatFlagMap;
2727 RsidOnlyAutoFormatFlagMap.resize(Count() + 1);
2728 sal_Int32 nLastPorStart = COMPLETE_STRING;
2729 sal_Int32 nKey = 0;
2730
2731 // get portions by start position:
2732 for ( size_t i = 0; i < Count(); ++i )
2733 {
2734 SwTextAttr *pHt = Get( i );
2735 if ( RES_TXTATR_CHARFMT != pHt->Which() &&
2736 RES_TXTATR_AUTOFMT != pHt->Which() )
2737 //&&
2738 //RES_TXTATR_INETFMT != pHt->Which() )
2739 continue;
2740
2741 bool isRsidOnlyAutoFormat(false);
2742 // check for RSID-only AUTOFMT
2743 if (RES_TXTATR_AUTOFMT == pHt->Which())
2744 {
2745 std::shared_ptr<SfxItemSet> const & pSet(
2746 pHt->GetAutoFormat().GetStyleHandle());
2747 if ((pSet->Count() == 1) && pSet->GetItem(RES_CHRATR_RSID, false))
2748 {
2749 // fdo#70201: eliminate no-extent RSID-only AUTOFMT
2750 // could be produced by ReplaceText or (maybe?) RstAttr
2751 if (pHt->GetStart() == *pHt->GetEnd())
2752 {
2753 DeleteAtPos(i); // kill it without History!
2754 SwTextAttr::Destroy(pHt, rNode.GetDoc().GetAttrPool());
2755 --i;
2756 continue;
2757 }
2758 // fdo#52028: this one has _only_ RSID => ignore it completely
2759 if (!pHt->IsFormatIgnoreStart() || !pHt->IsFormatIgnoreEnd())
2760 {
2761 NoteInHistory(pHt);
2762 pHt->SetFormatIgnoreStart(true);
2763 pHt->SetFormatIgnoreEnd (true);
2764 NoteInHistory(pHt, true);
2765 }
2766 isRsidOnlyAutoFormat = true;
2767 }
2768 }
2769
2770 if (pHt->GetStart() == *pHt->GetEnd())
2771 {
2772 // no-length hints are a disease. ignore them here.
2773 // the SwAttrIter::SeekFwd will not call Rst/Chg for them.
2774 continue;
2775 }
2776
2777 const sal_Int32 nPorStart = pHt->GetStart();
2778 if (nPorStart != nLastPorStart)
2779 ++nKey;
2780 nLastPorStart = nPorStart;
2781 aPortionMap.push_back(Portion {pHt, nKey, isRsidOnlyAutoFormat});
2782 RsidOnlyAutoFormatFlagMap[nKey] = isRsidOnlyAutoFormat;
2783 }
2784
2785 // we add data strictly in-order, so we can forward-search the vector
2786 auto equal_range = [](PortionMap::const_iterator startIt, PortionMap::const_iterator endIt, int i)
2787 {
2788 auto it1 = startIt;
2789 while (it1 != endIt && it1->nKey < i)
2790 ++it1;
2791 auto it2 = it1;
2792 while (it2 != endIt && it2->nKey == i)
2793 ++it2;
2794 return std::pair<PortionMap::const_iterator, PortionMap::const_iterator>{ it1, it2 };
2795 };
2796
2797 // check if portion i can be merged with portion i+1:
2798 // note: need to include i=0 to set IgnoreStart and j=nKey+1 to reset
2799 // IgnoreEnd at first / last portion
2800 int i = 0;
2801 int j = i + 1;
2802 // Store this outside the loop, because we limit the search area on subsequent searches.
2803 std::pair< PortionMap::const_iterator, PortionMap::const_iterator > aRange1 { aPortionMap.begin(), aPortionMap.begin() + aPortionMap.size() };
2804 while ( i <= nKey )
2805 {
2806 aRange1 = equal_range( aRange1.first, aPortionMap.begin() + aPortionMap.size(), i );
2807 // start the search for this one from where the first search ended.
2808 std::pair< PortionMap::const_iterator, PortionMap::const_iterator > aRange2
2809 = equal_range( aRange1.second, aPortionMap.begin() + aPortionMap.size(), j );
2810
2811 MergeResult eMerge = lcl_Compare_Attributes(i, j, aRange1, aRange2, RsidOnlyAutoFormatFlagMap);
2812
2813 if (MATCH == eMerge)
2814 {
2815 // important: delete second range so any IgnoreStart on the first
2816 // range is still valid
2817 // erase all elements with key i + 1
2818 sal_Int32 nNewPortionEnd = 0;
2819 for ( auto aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2 )
2820 {
2821 SwTextAttr *const p2 = aIter2->pTextAttr;
2822 nNewPortionEnd = *p2->GetEnd();
2823
2824 const size_t nCountBeforeDelete = Count();
2825 Delete( p2 );
2826
2827 // robust: check if deletion actually took place before destroying attribute:
2828 if ( Count() < nCountBeforeDelete )
2829 rNode.DestroyAttr( p2 );
2830 }
2831 aPortionMap.erase( aRange2.first, aRange2.second );
2832 ++j;
2833
2834 // change all attributes with key i
2835 aRange1 = equal_range( aRange1.first, aPortionMap.begin() + aPortionMap.size(), i );
2836 for ( auto aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1 )
2837 {
2838 SwTextAttr *const p1 = aIter1->pTextAttr;
2839 NoteInHistory( p1 );
2840 p1->SetEnd(nNewPortionEnd);
2841 NoteInHistory( p1, true );
2842 bRet = true;
2843 }
2844
2845 if (bRet)
2846 {
2847 Resort();
2848 }
2849 }
2850 else
2851 {
2852 // when not merging the ignore flags need to be either set or reset
2853 // (reset too in case one of the autofmts was recently changed)
2854 bool const bSetIgnoreFlag(DIFFER_ONLY_RSID == eMerge);
2855 for (auto aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1)
2856 {
2857 if (!aIter1->isRsidOnlyAutoFormat) // already set above, don't change
2858 {
2859 SwTextAttr *const pCurrent(aIter1->pTextAttr);
2860 if (pCurrent->IsFormatIgnoreEnd() != bSetIgnoreFlag)
2861 {
2862 NoteInHistory(pCurrent);
2863 pCurrent->SetFormatIgnoreEnd(bSetIgnoreFlag);
2864 NoteInHistory(pCurrent, true);
2865 }
2866 }
2867 }
2868 for (auto aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2)
2869 {
2870 if (!aIter2->isRsidOnlyAutoFormat) // already set above, don't change
2871 {
2872 SwTextAttr *const pCurrent(aIter2->pTextAttr);
2873 if (pCurrent->IsFormatIgnoreStart() != bSetIgnoreFlag)
2874 {
2875 NoteInHistory(pCurrent);
2876 pCurrent->SetFormatIgnoreStart(bSetIgnoreFlag);
2877 NoteInHistory(pCurrent, true);
2878 }
2879 }
2880 }
2881 i = j; // ++i not enough: i + 1 may have been deleted (MATCH)!
2882 ++j;
2883 }
2884 }
2885
2886 return bRet;
2887}
2888
2889
2890static MergeResult lcl_Compare_Attributes(
2891 int i, int j,
2892 const std::pair< PortionMap::const_iterator, PortionMap::const_iterator >& aRange1,
2893 const std::pair< PortionMap::const_iterator, PortionMap::const_iterator >& aRange2,
2894 std::vector<bool>& RsidOnlyAutoFormatFlagMap)
2895{
2896 PortionMap::const_iterator aIter1 = aRange1.first;
2897 PortionMap::const_iterator aIter2 = aRange2.first;
2898
2899 size_t const nAttributesInPor1 = std::distance(aRange1.first, aRange1.second);
2900 size_t const nAttributesInPor2 = std::distance(aRange2.first, aRange2.second);
2901 bool const isRsidOnlyAutoFormat1 = i < sal_Int32(RsidOnlyAutoFormatFlagMap.size()) && RsidOnlyAutoFormatFlagMap[i];
2902 bool const isRsidOnlyAutoFormat2 = j < sal_Int32(RsidOnlyAutoFormatFlagMap.size()) && RsidOnlyAutoFormatFlagMap[j];
2903
2904 // if both have one they could be equal, but not if only one has it
2905 bool const bSkipRsidOnlyAutoFormat(nAttributesInPor1 != nAttributesInPor2);
2906
2907 // this loop needs to handle the case where one has a CHARFMT and the
2908 // other CHARFMT + RSID-only AUTOFMT, so...
2909 // want to skip over RSID-only AUTOFMT here, hence the -1
2910 if (!((nAttributesInPor1 - (isRsidOnlyAutoFormat1 ? 1 : 0)) ==
2911 (nAttributesInPor2 - (isRsidOnlyAutoFormat2 ? 1 : 0))
2912 && (nAttributesInPor1 != 0 || nAttributesInPor2 != 0)))
2913 {
2914 return DIFFER;
2915 }
2916
2917 MergeResult eMerge(MATCH);
2918
2919 // _if_ there is one element more either in aRange1 or aRange2
2920 // it _must_ be an RSID-only AUTOFMT, which can be ignored here...
2921 // But if both have RSID-only AUTOFMT they could be equal, no skip!
2922 while (aIter1 != aRange1.second || aIter2 != aRange2.second)
2923 {
2924 // first of all test if there's no gap (before skipping stuff!)
2925 if (aIter1 != aRange1.second && aIter2 != aRange2.second &&
2926 *aIter1->pTextAttr->End() < aIter2->pTextAttr->GetStart())
2927 {
2928 return DIFFER;
2929 }
2930 // skip it - cannot be equal if bSkipRsidOnlyAutoFormat is set
2931 if (bSkipRsidOnlyAutoFormat
2932 && aIter1 != aRange1.second && aIter1->isRsidOnlyAutoFormat)
2933 {
2934 assert(DIFFER != eMerge);
2935 eMerge = DIFFER_ONLY_RSID;
2936 ++aIter1;
2937 continue;
2938 }
2939 if (bSkipRsidOnlyAutoFormat
2940 && aIter2 != aRange2.second && aIter2->isRsidOnlyAutoFormat)
2941 {
2942 assert(DIFFER != eMerge);
2943 eMerge = DIFFER_ONLY_RSID;
2944 ++aIter2;
2945 continue;
2946 }
2947 assert(aIter1 != aRange1.second && aIter2 != aRange2.second);
2948 SwTextAttr const*const p1 = aIter1->pTextAttr;
2949 SwTextAttr const*const p2 = aIter2->pTextAttr;
2950 if (p1->Which() != p2->Which())
2951 {
2952 return DIFFER;
2953 }
2954 if (!(*p1 == *p2))
2955 {
2956 // fdo#52028: for auto styles, check if they differ only
2957 // in the RSID, which should have no effect on text layout
2958 if (RES_TXTATR_AUTOFMT != p1->Which())
2959 return DIFFER;
2960
2961 const SfxItemSet& rSet1 = *p1->GetAutoFormat().GetStyleHandle();
2962 const SfxItemSet& rSet2 = *p2->GetAutoFormat().GetStyleHandle();
2963
2964 // sadly SfxItemSet::operator== does not seem to work?
2965 SfxItemIter iter1(rSet1);
2966 SfxItemIter iter2(rSet2);
2967 for (SfxPoolItem const* pItem1 = iter1.GetCurItem(),
2968 * pItem2 = iter2.GetCurItem();;)
2969 {
2970 if (pItem1 && pItem1->Which() == RES_CHRATR_RSID)
2971 pItem1 = iter1.NextItem();
2972 if (pItem2 && pItem2->Which() == RES_CHRATR_RSID)
2973 pItem2 = iter2.NextItem();
2974 if (!pItem1 && !pItem2)
2975 {
2976 eMerge = DIFFER_ONLY_RSID;
2977 break;
2978 }
2979 if (!pItem1 || !pItem2)
2980 {
2981 return DIFFER;
2982 }
2983 if (pItem1 != pItem2) // all are poolable
2984 {
2985 assert(IsInvalidItem(pItem1) || IsInvalidItem(pItem2) || pItem1->Which() != pItem2->Which() || *pItem1 != *pItem2);
2986 return DIFFER;
2987 }
2988 pItem1 = iter1.NextItem();
2989 pItem2 = iter2.NextItem();
2990 }
2991 }
2992 ++aIter1;
2993 ++aIter2;
2994 }
2995 return eMerge;
2996}
2997
2998
2999// check if there is already a character format and adjust the sort numbers
3000static void lcl_CheckSortNumber( const SwpHints& rHints, SwTextCharFormat& rNewCharFormat )
3001{
3002 const sal_Int32 nHtStart = rNewCharFormat.GetStart();
3003 const sal_Int32 nHtEnd = *rNewCharFormat.GetEnd();
3004 sal_uInt16 nSortNumber = 0;
3005
3006 for ( size_t i = 0; i < rHints.Count(); ++i )
3007 {
3008 const SwTextAttr* pOtherHt = rHints.Get(i);
3009
3010 const sal_Int32 nOtherStart = pOtherHt->GetStart();
3011
3012 if ( nOtherStart > nHtStart )
3013 break;
3014
3015 if ( RES_TXTATR_CHARFMT == pOtherHt->Which() )
3016 {
3017 const sal_Int32 nOtherEnd = *pOtherHt->End();
3018
3019 if ( nOtherStart == nHtStart && nOtherEnd == nHtEnd )
3020 {
3021 nSortNumber = static_txtattr_cast<const SwTextCharFormat*>(pOtherHt)->GetSortNumber() + 1;
3022 }
3023 }
3024 }
3025
3026 if ( nSortNumber > 0 )
3027 rNewCharFormat.SetSortNumber( nSortNumber );
3028}
3029
3030/*
3031 * Try to insert the new hint.
3032 * Depending on the type of the hint, this either always succeeds, or may fail.
3033 * Depending on the type of the hint, other hints may be deleted or
3034 * overwritten.
3035 * The return value indicates successful insertion.
3036 */
3038 SwTextAttr* const pHint,
3039 SwTextNode &rNode,
3040 const SetAttrMode nMode )
3041{
3042 if ( MAX_HINTS <= Count() ) // we're sorry, this flight is overbooked...
3043 {
3044 OSL_FAIL("hints array full :-(");
3045 rNode.DestroyAttr(pHint);
3046 return false;
3047 }
3048
3049 const sal_Int32 *pHtEnd = pHint->GetEnd();
3050 const sal_uInt16 nWhich = pHint->Which();
3051 std::vector<sal_uInt16> aWhichSublist;
3052
3053 switch( nWhich )
3054 {
3055 case RES_TXTATR_CHARFMT:
3056 {
3057 // Check if character format contains hidden attribute:
3058 const SwCharFormat* pFormat = pHint->GetCharFormat().GetCharFormat();
3059 if ( SfxItemState::SET == pFormat->GetItemState( RES_CHRATR_HIDDEN ) )
3060 rNode.SetCalcHiddenCharFlags();
3061
3062 static_txtattr_cast<SwTextCharFormat*>(pHint)->ChgTextNode( &rNode );
3063 break;
3064 }
3065 // #i75430# Recalc hidden flags if necessary
3066 case RES_TXTATR_AUTOFMT:
3067 {
3068 std::shared_ptr<SfxItemSet> const & pSet( pHint->GetAutoFormat().GetStyleHandle() );
3069 if (pHint->GetStart() == *pHint->GetEnd())
3070 {
3071 if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
3072 { // empty range RSID-only hints could cause trouble, there's no
3073 rNode.DestroyAttr(pHint); // need for them so don't insert
3074 return false;
3075 }
3076 }
3077 // Check if auto style contains hidden attribute:
3078 const SfxPoolItem* pHiddenItem = CharFormat::GetItem( *pHint, RES_CHRATR_HIDDEN );
3079 if ( pHiddenItem )
3080 rNode.SetCalcHiddenCharFlags();
3081
3082 // fdo#71556: populate aWhichFormatAttr member of SwMsgPoolItem
3083 const WhichRangesContainer& pRanges = pSet->GetRanges();
3084 for(auto const & rPair : pRanges)
3085 {
3086 const sal_uInt16 nBeg = rPair.first;
3087 const sal_uInt16 nEnd = rPair.second;
3088 for( sal_uInt16 nSubElem = nBeg; nSubElem <= nEnd; ++nSubElem )
3089 if( pSet->HasItem( nSubElem ) )
3090 aWhichSublist.push_back( nSubElem );
3091 }
3092 break;
3093 }
3094 case RES_TXTATR_INETFMT:
3095 static_txtattr_cast<SwTextINetFormat*>(pHint)->InitINetFormat(rNode);
3096 break;
3097
3098 case RES_TXTATR_FIELD:
3101 {
3102 SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
3103 bool bDelFirst = nullptr != pTextField->GetpTextNode();
3104 pTextField->ChgTextNode( &rNode );
3105 SwDoc& rDoc = rNode.GetDoc();
3106 const SwField* pField = pTextField->GetFormatField().GetField();
3107
3109 {
3110 // certain fields must update the SwDoc's calculation flags
3111 switch( pField->GetTyp()->Which() )
3112 {
3114 case SwFieldIds::SetExp:
3119 {
3120 if( bDelFirst )
3121 rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(false, *pTextField);
3122 if( rNode.GetNodes().IsDocNodes() )
3123 rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(true, *pTextField);
3124 }
3125 break;
3126 case SwFieldIds::Dde:
3127 if( rNode.GetNodes().IsDocNodes() )
3128 static_cast<SwDDEFieldType*>(pField->GetTyp())->IncRefCnt();
3129 break;
3130 default: break;
3131 }
3132 }
3133
3134 // insert into real document's nodes-array?
3135 if( rNode.GetNodes().IsDocNodes() )
3136 {
3137 bool bInsFieldType = false;
3138 switch( pField->GetTyp()->Which() )
3139 {
3140 case SwFieldIds::SetExp:
3141 bInsFieldType = static_cast<SwSetExpFieldType*>(pField->GetTyp())->IsDeleted();
3142 if( nsSwGetSetExpType::GSE_SEQ & static_cast<SwSetExpFieldType*>(pField->GetTyp())->GetType() )
3143 {
3144 // register the field at its FieldType before setting
3145 // the sequence reference number!
3146 SwSetExpFieldType* pFieldType = static_cast<SwSetExpFieldType*>(
3147 rDoc.getIDocumentFieldsAccess().InsertFieldType( *pField->GetTyp() ) );
3148 if( pFieldType != pField->GetTyp() )
3149 {
3150 SwFormatField* pFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField());
3151 pFormatField->RegisterToFieldType( *pFieldType );
3152 pFormatField->GetField()->ChgTyp( pFieldType );
3153 }
3154 pFieldType->SetSeqRefNo( *const_cast<SwSetExpField*>(static_cast<const SwSetExpField*>(pField)) );
3155 }
3156 break;
3157 case SwFieldIds::User:
3158 bInsFieldType = static_cast<SwUserFieldType*>(pField->GetTyp())->IsDeleted();
3159 break;
3160
3161 case SwFieldIds::Dde:
3163 static_cast<SwDDEFieldType*>(pField->GetTyp())->IncRefCnt();
3164 bInsFieldType = static_cast<SwDDEFieldType*>(pField->GetTyp())->IsDeleted();
3165 break;
3166
3167 case SwFieldIds::Postit:
3168 if ( rDoc.GetDocShell() )
3169 {
3170 rDoc.GetDocShell()->Broadcast( SwFormatFieldHint(
3172 }
3173 break;
3174 default: break;
3175 }
3176 if( bInsFieldType )
3178 }
3179 }
3180 break;
3181 case RES_TXTATR_FTN :
3182 static_cast<SwTextFootnote*>(pHint)->ChgTextNode( &rNode );
3183 break;
3184 case RES_TXTATR_REFMARK:
3185 static_txtattr_cast<SwTextRefMark*>(pHint)->ChgTextNode( &rNode );
3186 if( rNode.GetNodes().IsDocNodes() )
3187 {
3188 // search for a reference with the same name
3189 SwTextAttr* pTmpHt;
3190 for( size_t n = 0, nEnd = Count(); n < nEnd; ++n )
3191 {
3192 const sal_Int32 *pTmpHtEnd;
3193 const sal_Int32 *pTmpHintEnd;
3194 if (RES_TXTATR_REFMARK == (pTmpHt = Get(n))->Which() &&
3195 pHint->GetAttr() == pTmpHt->GetAttr() &&
3196 nullptr != ( pTmpHtEnd = pTmpHt->GetEnd() ) &&
3197 nullptr != ( pTmpHintEnd = pHint->GetEnd() ) )
3198 {
3200 pTmpHt->GetStart(), *pTmpHtEnd,
3201 pHint->GetStart(), *pTmpHintEnd );
3202 bool bDelOld = true, bChgStart = false, bChgEnd = false;
3203 switch( eCmp )
3204 {
3206 case SwComparePosition::Behind: bDelOld = false; break;
3207
3208 case SwComparePosition::Outside: bChgStart = bChgEnd = true; break;
3209
3211 case SwComparePosition::OverlapBefore: bChgStart = true; break;
3213 case SwComparePosition::OverlapBehind: bChgEnd = true; break;
3214 default: break;
3215 }
3216
3217 if( bChgStart )
3218 {
3219 pHint->SetStart( pTmpHt->GetStart() );
3220 }
3221 if( bChgEnd )
3222 pHint->SetEnd(*pTmpHtEnd);
3223
3224 if( bDelOld )
3225 {
3226 NoteInHistory( pTmpHt );
3227 rNode.DestroyAttr( Cut( n-- ) );
3228 --nEnd;
3229 }
3230 }
3231 }
3232 }
3233 break;
3234 case RES_TXTATR_TOXMARK:
3235 static_txtattr_cast<SwTextTOXMark*>(pHint)->ChgTextNode( &rNode );
3236 break;
3237
3239 static_txtattr_cast<SwTextRuby*>(pHint)->InitRuby(rNode);
3240 break;
3241
3242 case RES_TXTATR_META:
3244 static_txtattr_cast<SwTextMeta *>(pHint)->ChgTextNode( &rNode );
3245 break;
3246
3248 static_txtattr_cast<SwTextContentControl*>(pHint)->ChgTextNode( &rNode );
3249 break;
3250
3251 case RES_CHRATR_HIDDEN:
3252 rNode.SetCalcHiddenCharFlags();
3253 break;
3254 }
3255
3256 if( SetAttrMode::DONTEXPAND & nMode )
3257 pHint->SetDontExpand( true );
3258
3259 // special handling for SwTextAttrs without end:
3260 // 1) they cannot overlap
3261 // 2) if two fields are adjacent, they must not be merged into one
3262 // this is guaranteed by inserting a CH_TXTATR_* into the paragraph text!
3263 sal_Int32 nHtStart = pHint->GetStart();
3264 if( !pHtEnd )
3265 {
3266 Insert( pHint );
3267 NoteInHistory(pHint, true);
3268 CalcFlags();
3269#ifdef DBG_UTIL
3270 if( !rNode.GetDoc().IsInReading() )
3271 CHECK;
3272#endif
3273 // ... and notify listeners
3274 if(rNode.HasWriterListeners())
3275 {
3276 SwUpdateAttr aHint(
3277 nHtStart,
3278 nHtStart,
3279 nWhich);
3280
3281 rNode.TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
3282 }
3283
3284 return true;
3285 }
3286
3287 // from here on, pHint is known to have an end index!
3288
3289 if( *pHtEnd < nHtStart )
3290 {
3291 assert(*pHtEnd >= nHtStart);
3292
3293 // just swap the nonsense:
3294 pHint->SetStart(*pHtEnd);
3295 pHint->SetEnd(nHtStart);
3296 nHtStart = pHint->GetStart();
3297 }
3298
3299 // I need this value later on for notification but the pointer may become invalid
3300 const sal_Int32 nHintEnd = *pHtEnd;
3301 const bool bNoHintAdjustMode = bool(SetAttrMode::NOHINTADJUST & nMode);
3302
3303 // handle nesting attributes: inserting may fail due to overlap!
3304 if (pHint->IsNesting())
3305 {
3306 const bool bRet(
3307 TryInsertNesting(rNode, *static_txtattr_cast<SwTextAttrNesting*>(pHint)));
3308 if (!bRet) return false;
3309 }
3310 // Currently REFMARK and TOXMARK have OverlapAllowed set to true.
3311 // These attributes may be inserted directly.
3312 // Also attributes without length may be inserted directly.
3313 // SETATTR_NOHINTADJUST is set e.g., during undo.
3314 // Portion building in not necessary during XML import.
3315 else if ( !bNoHintAdjustMode &&
3316 !pHint->IsOverlapAllowedAttr() &&
3317 !rNode.GetDoc().IsInXMLImport() &&
3318 ( RES_TXTATR_AUTOFMT == nWhich ||
3319 RES_TXTATR_CHARFMT == nWhich ) )
3320 {
3321 assert( nWhich != RES_TXTATR_AUTOFMT ||
3322 static_cast<const SwFormatAutoFormat&>(pHint->GetAttr()).GetStyleHandle()->GetPool() ==
3323 &rNode.GetDoc().GetAttrPool());
3324
3325 BuildPortions( rNode, *pHint, nMode );
3326
3327 if ( nHtStart < nHintEnd ) // skip merging for 0-length attributes
3328 MergePortions( rNode );
3329 }
3330 else
3331 {
3332 // There may be more than one character style at the current position.
3333 // Take care of the sort number.
3334 // FME 2007-11-08 #i82989# in NOHINTADJUST mode, we want to insert
3335 // character attributes directly
3336 if (!bNoHintAdjustMode
3337 && ( (RES_TXTATR_CHARFMT == nWhich)
3338 // tdf#149978 also for import of automatic styles, could be produced by non-LO application
3339 || (RES_TXTATR_AUTOFMT == nWhich && rNode.GetDoc().IsInXMLImport())))
3340 {
3341 BuildPortions( rNode, *pHint, nMode );
3342 }
3343 else
3344 {
3345 // #i82989# Check sort numbers in NoHintAdjustMode
3346 if ( RES_TXTATR_CHARFMT == nWhich )
3347 lcl_CheckSortNumber(*this, *static_txtattr_cast<SwTextCharFormat*>(pHint));
3348
3349 Insert( pHint );
3350 NoteInHistory( pHint, true );
3351 }
3352 }
3353
3354 // ... and notify listeners
3355 if ( rNode.HasWriterListeners() )
3356 {
3357 const SwUpdateAttr aHint(nHtStart, nHintEnd, nWhich, std::move(aWhichSublist));
3358 rNode.TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
3359 }
3360
3361#ifdef DBG_UTIL
3362 if( !bNoHintAdjustMode && !rNode.GetDoc().IsInReading() )
3363 CHECK;
3364#endif
3365
3366 return true;
3367}
3368
3369void SwpHints::DeleteAtPos( const size_t nPos )
3370{
3371 assert(!m_bStartMapNeedsSorting && "deleting at pos and the list needs sorting?");
3372
3373 SwTextAttr *pHint = Get(nPos);
3374 assert( pHint->m_pHints == this );
3375 // ChainDelete( pHint );
3376 NoteInHistory( pHint );
3377
3378 // optimization: nPos is the position in the Starts array
3379 SwTextAttr *pHt = m_HintsByStart[ nPos ];
3380 m_HintsByStart.erase( m_HintsByStart.begin() + nPos );
3381
3385 ResortEndMap();
3388
3389 auto findIt = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), pHt, CompareSwpHtEnd());
3390 assert(*findIt == pHt);
3391 m_HintsByEnd.erase(findIt);
3392
3393 auto findIt2 = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), pHt, CompareSwpHtWhichStart());
3394 assert(*findIt2 == pHt);
3395 m_HintsByWhichAndStart.erase(findIt2);
3396
3397 pHt->m_pHints = nullptr;
3398
3399 if( pHint->Which() == RES_TXTATR_FIELD )
3400 {
3401 SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
3402 const SwFieldType* pFieldTyp = pTextField->GetFormatField().GetField()->GetTyp();
3403 if( SwFieldIds::Dde == pFieldTyp->Which() )
3404 {
3405 const SwTextNode* pNd = pTextField->GetpTextNode();
3406 if( pNd && pNd->GetNodes().IsDocNodes() )
3407 const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pFieldTyp))->DecRefCnt();
3408 pTextField->ChgTextNode(nullptr);
3409 }
3410 else if (m_bHiddenByParaField
3411 && m_rParent.GetDoc().FieldCanHideParaWeight(pFieldTyp->Which()))
3412 {
3414 }
3415 }
3416 else if ( pHint->Which() == RES_TXTATR_ANNOTATION )
3417 {
3418 SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
3419 const_cast<SwFormatField&>(pTextField->GetFormatField()).Broadcast(
3421 }
3422
3423 CalcFlags();
3424 CHECK_NOTMERGED; // called from BuildPortions
3425}
3426
3429void SwpHints::Delete( SwTextAttr const * pTextHt )
3430{
3431 const size_t nPos = GetIndexOf( pTextHt );
3432 assert(SAL_MAX_SIZE != nPos);
3433 if( SAL_MAX_SIZE != nPos )
3434 DeleteAtPos( nPos );
3435}
3436
3437void SwTextNode::ClearSwpHintsArr( bool bDelFields )
3438{
3439 if ( !HasHints() )
3440 return;
3441
3442 size_t nPos = 0;
3443 while ( nPos < m_pSwpHints->Count() )
3444 {
3445 SwTextAttr* pDel = m_pSwpHints->Get( nPos );
3446 bool bDel = false;
3447
3448 switch( pDel->Which() )
3449 {
3450 case RES_TXTATR_FLYCNT:
3451 case RES_TXTATR_FTN:
3452 break;
3453
3454 case RES_TXTATR_FIELD:
3457 if( bDelFields )
3458 bDel = true;
3459 break;
3460 default:
3461 bDel = true; break;
3462 }
3463
3464 if( bDel )
3465 {
3466 m_pSwpHints->DeleteAtPos( nPos );
3467 DestroyAttr( pDel );
3468 }
3469 else
3470 ++nPos;
3471 }
3472}
3473
3474LanguageType SwTextNode::GetLang( const sal_Int32 nBegin, const sal_Int32 nLen,
3475 sal_uInt16 nScript ) const
3476{
3478
3479 if ( ! nScript )
3480 {
3481 nScript = g_pBreakIt->GetRealScriptOfText( m_Text, nBegin );
3482 }
3483
3484 // #i91465# Consider nScript if pSwpHints == 0
3485 const sal_uInt16 nWhichId = GetWhichOfScript( RES_CHRATR_LANGUAGE, nScript );
3486
3487 if ( HasHints() )
3488 {
3489 const sal_Int32 nEnd = nBegin + nLen;
3490 const size_t nSize = m_pSwpHints->Count();
3491 for ( size_t i = 0; i < nSize; ++i )
3492 {
3493 const SwTextAttr *pHt = m_pSwpHints->Get(i);
3494 const sal_Int32 nAttrStart = pHt->GetStart();
3495 if( nEnd < nAttrStart )
3496 break;
3497
3498 const sal_uInt16 nWhich = pHt->Which();
3499
3500 if( nWhichId == nWhich ||
3501 ( ( pHt->IsCharFormatAttr() || RES_TXTATR_AUTOFMT == nWhich ) && CharFormat::IsItemIncluded( nWhichId, pHt ) ) )
3502 {
3503 const sal_Int32 *pEndIdx = pHt->End();
3504 // do the attribute and the range overlap?
3505 if( !pEndIdx )
3506 continue;
3507 if( nLen )
3508 {
3509 if( nAttrStart >= nEnd || nBegin >= *pEndIdx )
3510 continue;
3511 }
3512 else if( nBegin != nAttrStart || ( nAttrStart != *pEndIdx && nBegin ))
3513 {
3514 if( nAttrStart >= nBegin )
3515 continue;
3516 if( pHt->DontExpand() ? nBegin >= *pEndIdx : nBegin > *pEndIdx)
3517 continue;
3518 }
3519 const SfxPoolItem* pItem = CharFormat::GetItem( *pHt, nWhichId );
3520 const LanguageType nLng = static_cast<const SvxLanguageItem*>(pItem)->GetLanguage();
3521
3522 // does the attribute completely cover the range?
3523 if( nAttrStart <= nBegin && nEnd <= *pEndIdx )
3524 nRet = nLng;
3525 else if( LANGUAGE_DONTKNOW == nRet )
3526 nRet = nLng; // partial overlap, the first one wins
3527 }
3528 }
3529 }
3530 if( LANGUAGE_DONTKNOW == nRet )
3531 {
3532 nRet = static_cast<const SvxLanguageItem&>(GetSwAttrSet().Get( nWhichId )).GetLanguage();
3533 if( LANGUAGE_DONTKNOW == nRet )
3534 nRet = GetAppLanguage();
3535 }
3536 return nRet;
3537}
3538
3540{
3542 switch ( rAttr.Which() )
3543 {
3544 case RES_TXTATR_REFMARK:
3545 case RES_TXTATR_TOXMARK:
3547 cRet = CH_TXTATR_INWORD;
3548 break;
3549
3550 case RES_TXTATR_FIELD:
3551 case RES_TXTATR_FLYCNT:
3552 case RES_TXTATR_FTN:
3553 case RES_TXTATR_META:
3556 {
3557 cRet = CH_TXTATR_BREAKWORD;
3558 }
3559 break;
3561 {
3562 cRet = CH_TXTATR_NEWLINE;
3563 }
3564 break;
3565
3566 default:
3567 assert(!"GetCharOfTextAttr: unknown attr");
3568 break;
3569 }
3570 return cRet;
3571}
3572
3573/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
XSLTFilter & m_rParent
SwBreakIt * g_pBreakIt
Definition: breakit.cxx:34
virtual void InsDeletedFieldType(SwFieldType &)=0
virtual void InsDelFieldInFieldLst(bool bIns, const SwTextField &rField)=0
virtual bool IsNewFieldLst() const =0
virtual SwFieldType * InsertFieldType(const SwFieldType &)=0
virtual void DelLayoutFormat(SwFrameFormat *pFormat)=0
virtual SwCharFormat * GetCharFormatFromPool(sal_uInt16 nId)=0
virtual std::shared_ptr< SfxItemSet > getAutomaticStyle(const SfxItemSet &rSet, SwAutoStyleFamily eFamily, const OUString *pParentName=nullptr)=0
void ClearItem()
const SfxPoolItem * GetCurItem() const
const SfxPoolItem * NextItem()
const T & Put(std::unique_ptr< T > xItem, sal_uInt16 nWhich=0)
const WhichRangesContainer & GetRanges() const
SfxItemPool * GetPool() const
sal_uInt16 Count() const
sal_uInt16 ClearItem(sal_uInt16 nWhich=0)
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
void Differentiate(const SfxItemSet &rSet)
void InvalidateItem(sal_uInt16 nWhich)
sal_uInt16 Which() const
virtual SfxPoolItem * Clone(SfxItemPool *pPool=nullptr) const=0
sal_uInt16 FirstWhich()
SfxItemState GetItemState(bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
sal_uInt16 NextWhich()
bool IsCharBackground2Shading() const
static SvtFilterOptions & Get()
tools::Long GetIndentAt() const
tools::Long GetFirstLineIndent() const
SvxNumPositionAndSpaceMode GetPositionAndSpaceMode() const
sal_uInt16 GetRealScriptOfText(const OUString &rText, sal_Int32 nPos) const
Definition: breakit.cxx:84
Represents the style of a text portion.
Definition: charfmt.hxx:27
Marks a character position inside a document model content node (SwContentNode)
sal_Int32 GetIndex() const
SwFormatColl * GetFormatColl() const
Definition: node.hxx:497
bool HasSwAttrSet() const
Definition: node.hxx:494
const SwAttrSet & GetSwAttrSet() const
Does node has already its own auto-attributes? Access to SwAttrSet.
Definition: node.hxx:727
void DelFrames(SwRootFrame const *pLayout)
Method deletes all views of document for the node.
Definition: node.cxx:1432
sal_uInt16 ClearItemsFromAttrSet(const std::vector< sal_uInt16 > &rWhichIds)
There some functions that like to remove items from the internal SwAttrSet (handle):
Definition: node.cxx:1786
const SwAttrSet * GetpSwAttrSet() const
Definition: node.hxx:493
void DecRefCnt()
Definition: ddefld.hxx:98
Definition: doc.hxx:197
bool IsInReading() const
Definition: doc.hxx:969
bool FieldHidesPara(const SwField &rField) const
Definition: doc.cxx:1385
const SwCharFormat * GetDfltCharFormat() const
Definition: doc.hxx:768
bool IsInDtor() const
Definition: doc.hxx:417
bool IsClipBoard() const
Definition: doc.hxx:978
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:158
SwNodes & GetNodes()
Definition: doc.hxx:422
IStyleAccess & GetIStyleAccess()
Definition: doc.hxx:772
IDocumentFieldsAccess const & getIDocumentFieldsAccess() const
Definition: doc.cxx:371
bool IsInXMLImport() const
Definition: doc.hxx:985
IDocumentLayoutAccess const & getIDocumentLayoutAccess() const
Definition: doc.cxx:419
SwFootnoteIdxs & GetFootnoteIdxs()
Definition: doc.hxx:649
IDocumentStylePoolAccess const & getIDocumentStylePoolAccess() const
Definition: doc.cxx:440
int FieldCanHideParaWeight(SwFieldIds eFieldId) const
Definition: doc.cxx:1370
const SwAttrPool & GetAttrPool() const
Definition: doc.hxx:1337
::sw::MetaFieldManager & GetMetaFieldManager()
Definition: doc.cxx:137
SwDocShell * GetDocShell()
Definition: doc.hxx:1370
bool IsInHeaderFooter(const SwNode &) const
Definition: doclay.cxx:1582
Instances of SwFields and those derived from it occur 0 to n times.
Definition: fldbas.hxx:247
SwFieldIds Which() const
Definition: fldbas.hxx:276
Base class of all fields.
Definition: fldbas.hxx:296
virtual SwFieldType * ChgTyp(SwFieldType *)
Set new type (used for copying among documents).
Definition: fldbas.cxx:415
SwFieldType * GetTyp() const
Definition: fldbas.hxx:402
void UpdateFootnote(const SwNode &rStt)
Definition: ftnidx.cxx:59
FlyAnchors.
Definition: fmtanchr.hxx:37
sal_Int32 GetAnchorContentOffset() const
Definition: atrfrm.cxx:1631
RndStdIds GetAnchorId() const
Definition: fmtanchr.hxx:67
const SwPosition * GetContentAnchor() const
Definition: fmtanchr.hxx:74
SwNode * GetAnchorNode() const
Definition: atrfrm.cxx:1614
void SetStyleHandle(const std::shared_ptr< SfxItemSet > &pHandle)
Definition: fmtautofmt.hxx:48
const std::shared_ptr< SfxItemSet > & GetStyleHandle() const
Definition: fmtautofmt.hxx:49
SwCharFormat * GetCharFormat() const
Definition: fchrfmt.hxx:70
void SetCharFormat(SwFormat *pFormat)
Definition: fchrfmt.hxx:65
SfxPoolItem subclass that wraps an SwContentControl.
const std::shared_ptr< SwContentControl > & GetContentControl() const
const SwField * GetField() const
Definition: fmtfld.hxx:131
void RegisterToFieldType(SwFieldType &)
Definition: atrfld.cxx:170
Format of a fly content.
Definition: fmtflcnt.hxx:33
SwFrameFormat * GetFrameFormat() const
Definition: fmtflcnt.hxx:45
SfxPoolItem subclass for footnotes and endnotes, stored in the anchor text node.
Definition: fmtftn.hxx:47
const SwTextFootnote * GetTextFootnote() const
Definition: fmtftn.hxx:87
SfxPoolItem subclass that wraps an SwLineBreakClear.
SwFormatMeta is a pool item subclass that owns a Meta.
Definition: fmtmeta.hxx:93
::sw::Meta * GetMeta()
Definition: fmtmeta.hxx:123
const SwDoc * GetDoc() const
The document is set in SwAttrPool now, therefore you always can access it.
Definition: format.hxx:139
sal_uInt16 Which() const
for Querying of Writer-functions.
Definition: format.hxx:82
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
Definition: format.cxx:385
const SwFormatAnchor & GetAnchor(bool=true) const
Definition: fmtanchr.hxx:88
const T * GetItemIfSet(TypedWhichId< T > nWhich, bool bSrchInParent=true) const
Templatized version of GetItemState() to directly return the correct type.
Definition: format.hxx:111
Style of a layout element.
Definition: frmfmt.hxx:72
static SwTOXType * GetSwTOXType(SwDoc &rDoc, TOXTypes eTOXTypes, const OUString &rTOXName)
Definition: rolbck.cxx:380
SwStartNode * GetStartNode()
Definition: node.hxx:642
SwNodeOffset GetIndex() const
Definition: node.hxx:312
SwNodes & GetNodes()
Node is in which nodes-array/doc?
Definition: node.hxx:706
SwDoc & GetDoc()
Definition: node.hxx:233
SwNodeOffset StartOfSectionIndex() const
Definition: node.hxx:687
Merge GetRedlineMergeFlag() const
Definition: node.hxx:116
IStyleAccess & getIDocumentStyleAccess()
Provides access to the document automatic styles interface.
Definition: node.cxx:2153
SwTableNode * GetTableNode()
Definition: node.hxx:650
SwNode & GetEndOfAutotext() const
Section for all Flys/Header/Footers.
Definition: ndarr.hxx:158
bool IsDocNodes() const
Is the NodesArray the regular one of Doc? (and not the UndoNds, ...) Implementation in doc....
Definition: nodes.cxx:2555
SwNode & GetEndOfRedlines() const
Section for all Redlines.
Definition: ndarr.hxx:160
const SwNumFormat & Get(sal_uInt16 i) const
Definition: number.cxx:87
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:188
void SetName(const OUString &rStr)
Definition: docufld.cxx:1836
static void clearStatements(const css::uno::Reference< css::frame::XModel > &xModel, const OUString &rType, const css::uno::Reference< css::rdf::XResource > &xSubject)
Remove all statements in the graph of type rType, if any exists.
Definition: rdfhelper.cxx:149
void ChangeNodeIndex(SwNodeOffset nNew)
Definition: rolbck.hxx:438
void AddHint(SwTextAttr *pHt, const bool bNew)
Definition: rolbck.cxx:1425
The root element of a Writer document layout.
Definition: rootfrm.hxx:85
bool HasMergedParas() const
Definition: wsfrm.cxx:4773
void SetSeqRefNo(SwSetExpField &rField)
Definition: expfld.cxx:569
void RegisterToTOXType(SwTOXType &rMark)
Definition: tox.cxx:141
const SwTOXType * GetTOXType() const
Definition: tox.hxx:576
const OUString & GetTypeName() const
Definition: tox.hxx:690
TOXTypes GetType() const
Definition: tox.hxx:693
SwDoc & GetDoc() const
Definition: tox.hxx:183
virtual const sal_Int32 * GetEnd() const override
end position
Definition: txatbase.cxx:77
A wrapper around SfxPoolItem to store the start position of (usually) a text portion,...
Definition: txatbase.hxx:44
static void Destroy(SwTextAttr *pToDestroy, SfxItemPool &rPool)
destroy instance
Definition: txatbase.cxx:58
const SfxPoolItem & GetAttr() const
Definition: txatbase.hxx:167
void SetFormatIgnoreStart(bool bFlag)
Definition: txatbase.hxx:110
bool IsOverlapAllowedAttr() const
Definition: txatbase.hxx:102
virtual const sal_Int32 * GetEnd() const
end position
Definition: txatbase.cxx:48
bool IsFormatIgnoreStart() const
Definition: txatbase.hxx:108
bool IsNesting() const
Definition: txatbase.hxx:106
const sal_Int32 * End() const
Definition: txatbase.hxx:156
const SwFormatFlyCnt & GetFlyCnt() const
Definition: txatbase.hxx:226
sal_Int32 GetStart() const
Definition: txatbase.hxx:88
const SwFormatCharFormat & GetCharFormat() const
Definition: txatbase.hxx:187
virtual void SetEnd(sal_Int32)
Definition: txatbase.cxx:53
void SetFormatIgnoreEnd(bool bFlag)
Definition: txatbase.hxx:111
void SetStart(sal_Int32 n)
start position
Definition: txatbase.hxx:87
bool IsCharFormatAttr() const
Definition: txatbase.hxx:101
bool DontExpand() const
Definition: txatbase.hxx:98
const SwFormatAutoFormat & GetAutoFormat() const
Definition: txatbase.hxx:193
bool HasContent() const
Definition: txatbase.hxx:112
void SetDontExpand(bool bDontExpand)
Definition: txatbase.hxx:179
bool HasDummyChar() const
Definition: txatbase.hxx:107
sal_uInt16 Which() const
Definition: txatbase.hxx:116
SwpHints * m_pHints
Definition: txatbase.hxx:67
bool IsFormatIgnoreEnd() const
Definition: txatbase.hxx:109
const SwFormatField & GetFormatField() const
Definition: txatbase.hxx:199
void SetSortNumber(sal_uInt16 nSortNumber)
Definition: txtatr.hxx:48
static SwTextContentControl * CreateTextContentControl(SwDoc &rDoc, SwTextNode *pTargetTextNode, SwFormatContentControl &rAttr, sal_Int32 nStart, sal_Int32 nEnd, bool bIsCopy)
void ChgTextNode(SwTextNode *pNew)
Definition: txtfld.hxx:58
SwTextNode * GetpTextNode() const
Definition: txtfld.hxx:49
void SetAnchor(const SwTextNode *pNode)
SetAnchor() is called by SwTextNode::InsertHint() and sets the anchor position in the SwFlyFrameForma...
Definition: atrflyin.cxx:138
SwTextAttr subclass for footnotes and endnotes.
Definition: txtftn.hxx:34
sal_uInt16 GetSeqRefNo() const
Definition: txtftn.hxx:66
SwTextAttr subclass that tracks the location of the wrapped SwFormatURL.
Definition: txtinet.hxx:30
SAL_DLLPRIVATE void InitINetFormat(SwTextNode &rNode)
Definition: thints.cxx:219
void ChgTextNode(SwTextNode *pNew)
Definition: txtinet.hxx:49
OUString GetFieldContent() const
Definition: atrfld.cxx:717
SwTextAttr subclass that tracks the location of the wrapped SwFormatLineBreak.
static SwTextMeta * CreateTextMeta(::sw::MetaFieldManager &i_rTargetDocManager, SwTextNode *const i_pTargetTextNode, SwFormatMeta &i_rAttr, sal_Int32 const i_nStart, sal_Int32 const i_nEnd, bool const i_bIsCopy)
Definition: txtatr2.cxx:258
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:112
OUString m_Text
Definition: ndtxt.hxx:129
void DeleteAttributes(const sal_uInt16 nWhich, const sal_Int32 nStart, const sal_Int32 nEnd=0)
delete all attributes of type nWhich at nStart (opt. end nEnd)
Definition: thints.cxx:1801
const SwTextInputField * GetOverlappingInputField(const SwTextAttr &rTextAttr) const
Definition: ndtxt.cxx:1822
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:291
void RstTextAttr(const sal_Int32 nContentStart, const sal_Int32 nLen, const sal_uInt16 nWhich=0, const SfxItemSet *pSet=nullptr, const bool bInclRefToxMark=false, const bool bExactRange=false)
delete all attributes.
Definition: txtedt.cxx:388
SwpHints & GetOrCreateSwpHints()
Definition: ndtxt.hxx:878
bool GetParaAttr(SfxItemSet &rSet, sal_Int32 nStt, sal_Int32 nEnd, const bool bOnlyTextAttr=false, const bool bGetFromChrFormat=true, const bool bMergeIndentValuesOfNumRule=false, SwRootFrame const *pLayout=nullptr) const
Query the attributes of textnode over the range.
Definition: thints.cxx:2140
void DeleteAttribute(SwTextAttr *const pTextAttr)
delete the attribute pTextAttr
Definition: thints.cxx:1763
void DestroyAttr(SwTextAttr *pAttr)
Definition: thints.cxx:1186
SwTextAttr * InsertItem(SfxPoolItem &rAttr, const sal_Int32 nStart, const sal_Int32 nEnd, const SetAttrMode nMode=SetAttrMode::DEFAULT)
create new text attribute from rAttr and insert it
Definition: thints.cxx:1305
void FormatToTextAttr(SwTextNode *pNd)
Convey attributes of an AttrSet (AutoFormat) to SwpHintsArray.
Definition: thints.cxx:2539
::sw::ListLevelIndents AreListLevelIndentsApplicable() const
Determines, if the list level indent attributes can be applied to the paragraph.
Definition: ndtxt.cxx:4614
void DelSoftHyph(const sal_Int32 nStart, const sal_Int32 nEnd)
Definition: thints.cxx:1876
void TryDeleteSwpHints()
Definition: ndtxt.hxx:887
void ClearSwpHintsArr(bool bDelFields)
Definition: thints.cxx:3437
virtual bool SetAttr(const SfxPoolItem &) override
overriding to handle change of certain paragraph attributes
Definition: ndtxt.cxx:5066
std::unique_ptr< SwpHints > m_pSwpHints
May be 0.
Definition: ndtxt.hxx:123
void EraseText(const SwContentIndex &rIdx, const sal_Int32 nCount=SAL_MAX_INT32, const SwInsertFlags nMode=SwInsertFlags::DEFAULT)
delete text content ATTENTION: must not be called with a range that overlaps the start of an attribut...
Definition: ndtxt.cxx:2777
bool HasHints() const
Definition: ndtxt.hxx:254
OUString InsertText(const OUString &rStr, const SwContentIndex &rIdx, const SwInsertFlags nMode=SwInsertFlags::DEFAULT)
insert text content
Definition: ndtxt.cxx:2372
void SetCalcHiddenCharFlags() const
Definition: ndtxt.hxx:781
static bool IsIgnoredCharFormatForNumbering(const sal_uInt16 nWhich, bool bIsCharStyle=false)
In MS Word, the font underline setting of the paragraph end position won't affect the formatting of n...
Definition: thints.cxx:1893
SwNumRule * GetNumRule(bool bInParent=true) const
Returns numbering rule of this text node.
Definition: ndtxt.cxx:2921
int GetActualListLevel(SwListRedlineType eRedline=SwListRedlineType::SHOW) const
Returns the actual list level of this text node, when it is a list item.
Definition: ndtxt.cxx:4248
void SetCalcHiddenParaField()
set CalcVisible flags
Definition: ndtxt.hxx:765
virtual void Update(SwContentIndex const &rPos, const sal_Int32 nChangeLen, UpdateMode eMode) override
override SwContentIndexReg
Definition: ndtxt.cxx:1251
SAL_DLLPRIVATE void impl_FormatToTextAttr(const SfxItemSet &i_rAttrSet)
Does the hard work of SwTextNode::FormatToTextAttr: the real conversion of items to automatic styles.
Definition: thints.cxx:2473
SwTextAttr * GetTextAttrAt(sal_Int32 const nIndex, sal_uInt16 const nWhich, ::sw::GetTextAttrMode const eMode=::sw::GetTextAttrMode::Default) const
get the innermost text attribute covering position nIndex.
Definition: ndtxt.cxx:1804
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:252
void TriggerNodeUpdate(const sw::LegacyModifyHint &)
for hanging TextFormatCollections somewhere else (Outline-Numbering!)
Definition: ndtxt.cxx:5449
bool InsertHint(SwTextAttr *const pAttr, const SetAttrMode nMode=SetAttrMode::DEFAULT)
Insert pAttr into hints array.
Definition: thints.cxx:1340
LanguageType GetLang(const sal_Int32 nBegin, const sal_Int32 nLen=0, sal_uInt16 nScript=0) const
Definition: thints.cxx:3474
bool DontExpandFormat(sal_Int32 nContentIdx, bool bFlag=true, bool bFormatToTextAttributes=true)
When appropriate set DontExpand-flag at INet or character styles respectively.
Definition: ndtxt.cxx:1681
SAL_DLLPRIVATE void InitRuby(SwTextNode &rNode)
Definition: thints.cxx:227
void ChgTextNode(SwTextNode *pNew)
Definition: txtatr.hxx:87
The shared part of a user field.
Definition: usrfld.hxx:35
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:68
SW_DLLPUBLIC void ResortWhichMap() const
Definition: ndhints.cxx:448
bool m_bFootnote
footnotes
Definition: ndhints.hxx:89
std::vector< SwTextAttr * > m_HintsByWhichAndStart
Definition: ndhints.hxx:78
std::vector< SwTextAttr * > m_HintsByEnd
Definition: ndhints.hxx:77
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
bool m_bEndMapNeedsSorting
Definition: ndhints.hxx:93
SwTextAttr * GetWithoutResorting(size_t nPos) const
Definition: ndhints.hxx:152
bool CalcHiddenParaField() const
Definition: thints.cxx:2657
void Delete(SwTextAttr const *pTextHt)
Delete the given Hint. The Hint must actually be in the array!
Definition: thints.cxx:3429
bool m_bDDEFields
the TextNode has DDE fields
Definition: ndhints.hxx:90
size_t GetIndexOf(const SwTextAttr *pHt) const
Definition: ndhints.cxx:476
SwTextAttr * Cut(const size_t nPosInStart)
Definition: ndhints.hxx:185
void SetHiddenByParaField(const bool bNew) const
Definition: ndhints.hxx:111
bool m_bHiddenByParaField
Definition: ndhints.hxx:88
void InsertNesting(SwTextAttrNesting &rNewHint)
Insert nesting hint into the hints array.
Definition: thints.cxx:301
SW_DLLPUBLIC void Resort() const
Definition: ndhints.cxx:406
size_t Count() const
Definition: ndhints.hxx:142
static const size_t MAX_HINTS
Definition: ndhints.hxx:74
SwpHints(const SwTextNode &rParent)
Definition: thints.cxx:92
SwTextAttr * GetSortedByEnd(size_t nPos) const
Definition: ndhints.hxx:158
bool m_bCalcHiddenParaField
Definition: ndhints.hxx:85
bool TryInsertNesting(SwTextNode &rNode, SwTextAttrNesting &rNewHint)
The following hints correspond to well-formed XML elements in ODF: RES_TXTATR_INETFMT,...
Definition: thints.cxx:378
const SwTextNode & m_rParent
Definition: ndhints.hxx:70
void Insert(SwTextAttr *pHt)
Definition: ndhints.cxx:145
bool m_bWhichMapNeedsSorting
Definition: ndhints.hxx:94
std::vector< SwTextAttr * > m_HintsByStart
Definition: ndhints.hxx:76
SwRegHistory * m_pHistory
for Undo
Definition: ndhints.hxx:80
bool MergePortions(SwTextNode &rNode)
Definition: thints.cxx:2715
void NoteInHistory(SwTextAttr *pAttr, const bool bNew=false)
records a new attribute in m_pHistory.
Definition: thints.cxx:2694
void DeleteAtPos(size_t nPos)
Definition: thints.cxx:3369
bool m_bStartMapNeedsSorting
Definition: ndhints.hxx:92
void CalcFlags()
Definition: thints.cxx:2628
bool TryInsertHint(SwTextAttr *const pHint, SwTextNode &rNode, const SetAttrMode nMode=SetAttrMode::DEFAULT)
try to insert the hint
Definition: thints.cxx:3037
SW_DLLPUBLIC void ResortStartMap() const
Definition: ndhints.cxx:428
SW_DLLPUBLIC void ResortEndMap() const
Definition: ndhints.cxx:438
SwRegHistory * GetHistory() const
Definition: ndhints.hxx:198
void BuildPortions(SwTextNode &rNode, SwTextAttr &rNewHint, const SetAttrMode nMode)
Definition: thints.cxx:635
const_iterator upper_bound(const Value &x) const
const_iterator begin() const
size_type erase(const Value &x)
std::pair< const_iterator, bool > insert(Value &&x)
const_iterator lower_bound(const Value &x) const
Meta is an annotation on a range of text.
Definition: fmtmeta.hxx:137
int nCount
float u
@ Database
For old documents the Field-Which IDs must be preserved !!!
#define CH_TXT_ATR_INPUTFIELDSTART
Definition: hintids.hxx:178
constexpr TypedWhichId< SvxCrossedOutItem > RES_CHRATR_CROSSEDOUT(5)
bool isTXTATR_WITHEND(const sal_uInt16 nWhich)
Definition: hintids.hxx:472
bool isTXTATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:480
constexpr TypedWhichId< SwFormatFootnote > RES_TXTATR_FTN(59)
constexpr TypedWhichId< SvxUnderlineItem > RES_CHRATR_UNDERLINE(14)
bool isUNKNOWNATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:508
bool isCHRATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:468
constexpr sal_uInt16 RES_CHRATR_END(46)
constexpr TypedWhichId< SvxLanguageItem > RES_CHRATR_LANGUAGE(10)
constexpr TypedWhichId< SvxFirstLineIndentItem > RES_MARGIN_FIRSTLINE(91)
constexpr TypedWhichId< SvxWeightItem > RES_CHRATR_WEIGHT(15)
constexpr sal_uInt16 RES_TXTATR_WITHEND_END(57)
constexpr TypedWhichId< SwFormatAutoFormat > RES_TXTATR_AUTOFMT(50)
constexpr TypedWhichId< SwFormatINetFormat > RES_TXTATR_INETFMT(51)
constexpr TypedWhichId< SvxWeightItem > RES_CHRATR_CTL_WEIGHT(31)
constexpr TypedWhichId< SvxCharHiddenItem > RES_CHRATR_HIDDEN(37)
constexpr TypedWhichId< SvxEscapementItem > RES_CHRATR_ESCAPEMENT(6)
constexpr sal_uInt16 RES_TXTATR_BEGIN(RES_CHRATR_END)
constexpr TypedWhichId< SvxBrushItem > RES_CHRATR_BACKGROUND(21)
constexpr TypedWhichId< SvxCaseMapItem > RES_CHRATR_CASEMAP(RES_CHRATR_BEGIN)
constexpr TypedWhichId< SvxPostureItem > RES_CHRATR_CTL_POSTURE(30)
constexpr TypedWhichId< SwFormatField > RES_TXTATR_ANNOTATION(60)
constexpr sal_uInt16 RES_CHRATR_BEGIN(HINT_BEGIN)
constexpr TypedWhichId< SvxPostureItem > RES_CHRATR_POSTURE(11)
constexpr TypedWhichId< SwFormatContentControl > RES_TXTATR_CONTENTCONTROL(56)
constexpr TypedWhichId< SwFormatField > RES_TXTATR_FIELD(RES_TXTATR_NOEND_BEGIN)
constexpr TypedWhichId< SwFormatCharFormat > RES_TXTATR_CHARFMT(52)
constexpr TypedWhichId< SwDrawFrameFormat > RES_DRAWFRMFMT(165)
constexpr TypedWhichId< SwFormatFlyCnt > RES_TXTATR_FLYCNT(58)
constexpr TypedWhichId< SwFormatAnchor > RES_ANCHOR(110)
constexpr TypedWhichId< SwFormatLineBreak > RES_TXTATR_LINEBREAK(61)
constexpr TypedWhichId< SvxRsidItem > RES_CHRATR_RSID(39)
#define CH_TXTATR_INWORD
Definition: hintids.hxx:175
constexpr TypedWhichId< SvxTextLeftMarginItem > RES_MARGIN_TEXTLEFT(92)
constexpr TypedWhichId< SwFormatMeta > RES_TXTATR_METAFIELD(49)
constexpr TypedWhichId< SvXMLAttrContainerItem > RES_TXTATR_UNKNOWN_CONTAINER(54)
constexpr TypedWhichId< SvxWeightItem > RES_CHRATR_CJK_WEIGHT(26)
constexpr TypedWhichId< SwFormatRefMark > RES_TXTATR_REFMARK(RES_TXTATR_WITHEND_BEGIN)
constexpr TypedWhichId< SwFormatMeta > RES_TXTATR_META(48)
#define CH_TXT_ATR_INPUTFIELDEND
Definition: hintids.hxx:179
constexpr TypedWhichId< SvxPostureItem > RES_CHRATR_CJK_POSTURE(25)
constexpr TypedWhichId< SwTOXMark > RES_TXTATR_TOXMARK(47)
constexpr TypedWhichId< SwFormatRuby > RES_TXTATR_CJK_RUBY(53)
constexpr TypedWhichId< SwFormatField > RES_TXTATR_INPUTFIELD(55)
constexpr TypedWhichId< SvxColorItem > RES_CHRATR_COLOR(3)
constexpr sal_uInt16 RES_TXTATR_END(RES_TXTATR_NOEND_END)
#define CH_TXTATR_NEWLINE
Definition: hintids.hxx:177
#define CH_TXTATR_BREAKWORD
Definition: hintids.hxx:174
sal_uInt16 GetWhichOfScript(sal_uInt16 nWhich, sal_uInt16 nScript)
Definition: hints.cxx:184
WhichRangesContainer const aCharFormatSetRange(svl::Items< RES_CHRATR_BEGIN, RES_CHRATR_END-1, RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1 >)
LanguageType GetAppLanguage()
Definition: init.cxx:741
WhichRangesContainer const aCharAutoFormatSetRange(svl::Items< RES_CHRATR_BEGIN, RES_CHRATR_END-1, RES_TXTATR_UNKNOWN_CONTAINER, RES_TXTATR_UNKNOWN_CONTAINER, RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1 >)
sal_Int64 n
#define LANGUAGE_DONTKNOW
sal_uInt16 nPos
#define SAL_WARN_IF(condition, area, stream)
#define SAL_INFO(area, stream)
const SfxItemSet * GetItemSet(const SfxPoolItem &rAttr)
Returns the item set associated with a character/inet/auto style.
Definition: atrstck.cxx:133
const SfxPoolItem * GetItem(const SwTextAttr &rAttr, sal_uInt16 nWhich)
Extracts pool item of type nWhich from rAttr.
Definition: atrstck.cxx:157
bool IsItemIncluded(const sal_uInt16 nWhich, const SwTextAttr *pAttr)
Checks if item is included in character/inet/auto style.
Definition: atrstck.cxx:177
LanguageType GetLanguage(SfxItemSet const &aSet, sal_uInt16 nLangWhichId)
Definition: langhelper.cxx:365
size
int i
const SwGetSetExpType GSE_SEQ
Sequence.
Definition: fldbas.hxx:209
SwTextNode const & GetAttrMerged(SfxItemSet &rFormatSet, SwTextNode const &rNode, SwRootFrame const *pLayout)
Definition: txtfrm.cxx:370
void CalcBreaks(std::vector< std::pair< SwNodeOffset, sal_Int32 > > &rBreaks, SwPaM const &rPam, bool const isOnlyFieldmarks)
ListLevelIndents
Definition: paratr.hxx:52
@ Parent
EXPAND : (Start < nIndex <= End)
CopyOrNewType
Definition: ndhints.hxx:33
SwComparePosition ComparePosition(const T &rStt1, const T &rEnd1, const T &rStt2, const T &rEnd2)
Definition: pam.hxx:123
SwComparePosition
Definition: pam.hxx:110
@ OverlapBehind
Pos1 overlaps Pos2 at the end.
@ CollideEnd
Pos1 end touches at Pos2 start.
@ Behind
Pos1 behind Pos2.
@ OverlapBefore
Pos1 overlaps Pos2 at the beginning.
@ Outside
Pos2 completely contained in Pos1.
@ Before
Pos1 before Pos2.
@ Inside
Pos1 completely contained in Pos2.
@ CollideStart
Pos1 start touches at Pos2 end.
@ Equal
Pos1 is as large as Pos2.
@ RES_POOLCHR_INET_NORMAL
Internet normal.
Definition: poolfmt.hxx:120
@ RES_POOLCHR_RUBYTEXT
Rubytext.
Definition: poolfmt.hxx:129
#define INVALID_POOL_ITEM
bool IsInvalidItem(const SfxPoolItem *pItem)
static SfxItemSet & rSet
Marks a position in the document model.
Definition: pam.hxx:38
Reference< XModel > xModel
SetAttrMode
Definition: swtypes.hxx:133
@ NOHINTEXPAND
for Undo, translated to SwInsertFlags::NOHINTEXPAND
@ FORCEHINTEXPAND
Force hint expand (only matters for hints with CH_TXTATR).
@ IS_COPY
The inserted item is a copy – intended for use in ndtxt.cxx.
@ NOHINTADJUST
attention: NOHINTADJUST prevents MergePortions! when using this need to pay attention to ignore start...
#define CHAR_SOFTHYPHEN
Definition: swtypes.hxx:175
constexpr sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:57
static bool isNestedAny(const sal_Int32 nStart1, const sal_Int32 nEnd1, const sal_Int32 nStart2, const sal_Int32 nEnd2)
#i106930#: now asymmetric: empty hint1 is not nested, but empty hint2 is
Definition: thints.cxx:152
static bool TextAttrContains(const sal_Int32 nPos, const SwTextAttrEnd *const pAttr)
Definition: thints.cxx:120
static void lcl_MergeAttr_ExpandChrFormat(SfxItemSet &rSet, const SfxPoolItem &rAttr)
Definition: thints.cxx:2059
static void TextAttrDelete(SwDoc &rDoc, SwTextAttr *const pAttr)
Definition: thints.cxx:106
#define CHECK
Definition: thints.cxx:84
sal_Unicode GetCharOfTextAttr(const SwTextAttr &rAttr)
Definition: thints.cxx:3539
SwTextAttr * MakeTextAttr(SwDoc &rDoc, SfxPoolItem &rAttr, sal_Int32 const nStt, sal_Int32 const nEnd, CopyOrNewType const bIsCopy, SwTextNode *const pTextNode)
if COPY then pTextNode must be given!
Definition: thints.cxx:1030
static bool isSplittable(const sal_uInt16 nWhich)
Definition: thints.cxx:175
static bool isOverlap(const sal_Int32 nStart1, const sal_Int32 nEnd1, const sal_Int32 nStart2, const sal_Int32 nEnd2)
Definition: thints.cxx:142
static void lcl_MergeListLevelIndentAsLRSpaceItem(const SwTextNode &rTextNode, SfxItemSet &rSet)
Definition: thints.cxx:2112
static MergeResult lcl_Compare_Attributes(int i, int j, const std::pair< PortionMap::const_iterator, PortionMap::const_iterator > &aRange1, const std::pair< PortionMap::const_iterator, PortionMap::const_iterator > &aRange2, std::vector< bool > &RsidOnlyAutoFormatFlagMap)
Definition: thints.cxx:2890
static void lcl_MergeAttr(SfxItemSet &rSet, const SfxPoolItem &rAttr)
Definition: thints.cxx:2036
static void lcl_DoSplitNew(NestList_t &rSplits, SwTextNode &rNode, const sal_Int32 nNewStart, const sal_Int32 nOtherStart, const sal_Int32 nOtherEnd, bool bOtherDummy)
Definition: thints.cxx:279
SwTextAttr * MakeRedlineTextAttr(SwDoc &rDoc, SfxPoolItem const &rAttr)
create redline dummy text hint that must not be inserted into hints array
Definition: thints.cxx:1000
std::vector< SwTextAttrNesting * > NestList_t
Definition: thints.cxx:263
#define CHECK_NOTMERGED
Definition: thints.cxx:85
static void lcl_CheckSortNumber(const SwpHints &rHints, SwTextCharFormat &rNewCharFormat)
Definition: thints.cxx:3000
static NestList_t::iterator lcl_DoSplitImpl(NestList_t &rSplits, SwTextNode &rNode, NestList_t::iterator const iter, sal_Int32 const nSplitPos, bool const bSplitAtStart, bool const bOtherDummy)
Definition: thints.cxx:266
static Split_t splitPolicy(const sal_uInt16 nWhichNew, const sal_uInt16 nWhichOther)
Calculate splitting policy for overlapping hints, based on what kind of hint is inserted,...
Definition: thints.cxx:198
static bool isSelfNestable(const sal_uInt16 nWhich)
Definition: thints.cxx:162
static SwTextAttrNesting * MakeTextAttrNesting(SwTextNode &rNode, SwTextAttrNesting &rNesting, const sal_Int32 nStart, const sal_Int32 nEnd)
Create a new nesting text hint.
Definition: thints.cxx:239
sal_uInt16 sal_Unicode
Count