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