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