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