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(),
1030  svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END> );
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 // Set these attributes on SwTextNode. If they apply to the entire paragraph
1825 // text, set them in the SwTextNode's item set (SwContentNode::SetAttr).
1827  const SfxItemSet& rSet,
1828  const sal_Int32 nStt,
1829  const sal_Int32 nEnd,
1830  const SetAttrMode nMode,
1831  SwTextAttr **ppNewTextAttr )
1832 {
1833  if( !rSet.Count() )
1834  return false;
1835 
1836  // split sets (for selection in nodes)
1837  const SfxItemSet* pSet = &rSet;
1838  SfxItemSet aTextSet( *rSet.GetPool(), svl::Items<RES_TXTATR_BEGIN, RES_TXTATR_END-1> );
1839 
1840  // entire paragraph
1841  if ( !nStt && (nEnd == m_Text.getLength()) &&
1842  !(nMode & SetAttrMode::NOFORMATATTR ) )
1843  {
1844  // if the node already has CharFormat hints, the new attributes must
1845  // be set as hints too to override those.
1846  bool bHasCharFormats = false;
1847  if ( HasHints() )
1848  {
1849  for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
1850  {
1851  if ( m_pSwpHints->Get( n )->IsCharFormatAttr() )
1852  {
1853  bHasCharFormats = true;
1854  break;
1855  }
1856  }
1857  }
1858 
1859  if( !bHasCharFormats )
1860  {
1861  aTextSet.Put( rSet );
1862  // If there are any character attributes in rSet,
1863  // we want to set them at the paragraph:
1864  if( aTextSet.Count() != rSet.Count() )
1865  {
1866  const bool bRet = SetAttr( rSet );
1867  if( !aTextSet.Count() )
1868  return bRet;
1869  }
1870 
1871  // check for auto style:
1872  const SfxPoolItem* pItem;
1873  const bool bAutoStyle = SfxItemState::SET == aTextSet.GetItemState( RES_TXTATR_AUTOFMT, false, &pItem );
1874  if ( bAutoStyle )
1875  {
1876  std::shared_ptr<SfxItemSet> pAutoStyleSet = static_cast<const SwFormatAutoFormat*>(pItem)->GetStyleHandle();
1877  const bool bRet = SetAttr( *pAutoStyleSet );
1878  if( 1 == aTextSet.Count() )
1879  return bRet;
1880  }
1881 
1882  // Continue with the text attributes:
1883  pSet = &aTextSet;
1884  }
1885  }
1886 
1888 
1889  SfxItemSet aCharSet( *rSet.GetPool(), aCharAutoFormatSetRange );
1890 
1891  size_t nCount = 0;
1892  SfxItemIter aIter( *pSet );
1893  const SfxPoolItem* pItem = aIter.GetCurItem();
1894 
1895  do
1896  {
1897  if (!IsInvalidItem(pItem))
1898  {
1899  const sal_uInt16 nWhich = pItem->Which();
1900  OSL_ENSURE( isCHRATR(nWhich) || isTXTATR(nWhich),
1901  "SwTextNode::SetAttr(): unknown attribute" );
1902  if ( isCHRATR(nWhich) || isTXTATR(nWhich) )
1903  {
1904  if ((RES_TXTATR_CHARFMT == nWhich) &&
1905  (GetDoc().GetDfltCharFormat() ==
1906  static_cast<const SwFormatCharFormat*>(pItem)->GetCharFormat()))
1907  {
1908  SwIndex aIndex( this, nStt );
1909  RstTextAttr( aIndex, nEnd - nStt, RES_TXTATR_CHARFMT );
1910  DontExpandFormat( aIndex );
1911  }
1912  else
1913  {
1914  if (isCHRATR(nWhich) ||
1915  (RES_TXTATR_UNKNOWN_CONTAINER == nWhich))
1916  {
1917  aCharSet.Put( *pItem );
1918  }
1919  else
1920  {
1921 
1922  SwTextAttr *const pNew = MakeTextAttr( GetDoc(),
1923  const_cast<SfxPoolItem&>(*pItem), nStt, nEnd );
1924  if ( pNew )
1925  {
1926  // store the first one we create into the pp
1927  if (ppNewTextAttr && !*ppNewTextAttr)
1928  *ppNewTextAttr = pNew;
1929  if ( nEnd != nStt && !pNew->GetEnd() )
1930  {
1931  OSL_FAIL("Attribute without end, but area marked");
1932  DestroyAttr( pNew ); // do not insert
1933  }
1934  else if ( InsertHint( pNew, nMode ) )
1935  {
1936  ++nCount;
1937  }
1938  }
1939  }
1940  }
1941  }
1942  }
1943  pItem = aIter.NextItem();
1944  } while(pItem);
1945 
1946  if ( aCharSet.Count() )
1947  {
1948  SwTextAttr* pTmpNew = MakeTextAttr( GetDoc(), aCharSet, nStt, nEnd );
1949  if ( InsertHint( pTmpNew, nMode ) )
1950  {
1951  ++nCount;
1952  }
1953  }
1954 
1956 
1957  return nCount != 0;
1958 }
1959 
1960 static void lcl_MergeAttr( SfxItemSet& rSet, const SfxPoolItem& rAttr )
1961 {
1962  if ( RES_TXTATR_AUTOFMT == rAttr.Which() )
1963  {
1964  const SfxItemSet* pCFSet = CharFormat::GetItemSet( rAttr );
1965  if ( !pCFSet )
1966  return;
1967  SfxWhichIter aIter( *pCFSet );
1968  sal_uInt16 nWhich = aIter.FirstWhich();
1969  while( nWhich )
1970  {
1971  if( ( nWhich < RES_CHRATR_END ||
1972  RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) &&
1973  ( SfxItemState::SET == pCFSet->GetItemState( nWhich ) ) )
1974  rSet.Put( pCFSet->Get( nWhich ) );
1975  nWhich = aIter.NextWhich();
1976  }
1977  }
1978  else
1979  rSet.Put( rAttr );
1980 }
1981 
1982 static void lcl_MergeAttr_ExpandChrFormat( SfxItemSet& rSet, const SfxPoolItem& rAttr )
1983 {
1984  if( RES_TXTATR_CHARFMT == rAttr.Which() ||
1985  RES_TXTATR_INETFMT == rAttr.Which() ||
1986  RES_TXTATR_AUTOFMT == rAttr.Which() )
1987  {
1988  const SfxItemSet* pCFSet = CharFormat::GetItemSet( rAttr );
1989 
1990  if ( pCFSet )
1991  {
1992  SfxWhichIter aIter( *pCFSet );
1993  sal_uInt16 nWhich = aIter.FirstWhich();
1994  while( nWhich )
1995  {
1996  if( ( nWhich < RES_CHRATR_END ||
1997  ( RES_TXTATR_AUTOFMT == rAttr.Which() && RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) ) &&
1998  ( SfxItemState::SET == pCFSet->GetItemState( nWhich ) ) )
1999  rSet.Put( pCFSet->Get( nWhich ) );
2000  nWhich = aIter.NextWhich();
2001  }
2002  }
2003  }
2004 
2005 /* If multiple attributes overlap, the last one wins!
2006  Probably this can only happen between a RES_TXTATR_INETFMT and one of the
2007  other hints, because BuildPortions ensures that CHARFMT/AUTOFMT don't
2008  overlap. But there may be multiple CHARFMT/AUTOFMT with exactly the same
2009  start/end, sorted by BuildPortions, in which case the same logic applies.
2010 
2011  1234567890123456789
2012  |------------| Font1
2013  |------| Font2
2014  ^ ^
2015  |--| query range: -> Font2
2016 */
2017  // merge into set
2018  rSet.Put( rAttr );
2019 }
2020 
2021 namespace {
2022 
2023 struct SwPoolItemEndPair
2024 {
2025 public:
2026  const SfxPoolItem* mpItem;
2027  sal_Int32 mnEndPos;
2028 
2029  SwPoolItemEndPair() : mpItem( nullptr ), mnEndPos( 0 ) {};
2030 };
2031 
2032 }
2033 
2035  SfxItemSet& rSet )
2036 {
2037  if ( !rTextNode.AreListLevelIndentsApplicable() )
2038  return;
2039 
2040  const SwNumRule* pRule = rTextNode.GetNumRule();
2041  if ( pRule && rTextNode.GetActualListLevel() >= 0 )
2042  {
2043  const SwNumFormat& rFormat = pRule->Get(o3tl::narrowing<sal_uInt16>(rTextNode.GetActualListLevel()));
2045  {
2047  aLR.SetTextLeft( rFormat.GetIndentAt() );
2048  aLR.SetTextFirstLineOffset( static_cast<short>(rFormat.GetFirstLineIndent()) );
2049  rSet.Put( aLR );
2050  }
2051  }
2052 }
2053 
2054 // request the attributes of the TextNode at the range
2055 bool SwTextNode::GetParaAttr(SfxItemSet& rSet, sal_Int32 nStt, sal_Int32 nEnd,
2056  const bool bOnlyTextAttr, const bool bGetFromChrFormat,
2057  const bool bMergeIndentValuesOfNumRule,
2058  SwRootFrame const*const pLayout) const
2059 {
2060  assert(!rSet.Count()); // handled inconsistently, typically an error?
2061 
2062  if (pLayout && pLayout->HasMergedParas())
2063  {
2065  {
2066  return false; // ignore deleted node
2067  }
2068  }
2069 
2070  // get the node's automatic attributes
2071  SfxItemSet aFormatSet( *rSet.GetPool(), rSet.GetRanges() );
2072  if (!bOnlyTextAttr)
2073  {
2074  SwTextNode const& rParaPropsNode(
2075  sw::GetAttrMerged(aFormatSet, *this, pLayout));
2076  if (bMergeIndentValuesOfNumRule)
2077  {
2078  lcl_MergeListLevelIndentAsLRSpaceItem(rParaPropsNode, aFormatSet);
2079  }
2080  }
2081 
2082  if( HasHints() )
2083  {
2084  // First, check which text attributes are valid in the range.
2085  // cases:
2086  // Ambiguous, if
2087  // * the attribute is wholly contained in the range
2088  // * the attribute end is in the range
2089  // * the attribute start is in the range
2090  // Unambiguous (merge into set), if
2091  // * the attribute wholly contains the range
2092  // Ignored, if
2093  // * the attribute is wholly outside the range
2094 
2095  void (*fnMergeAttr)( SfxItemSet&, const SfxPoolItem& )
2096  = bGetFromChrFormat ? &lcl_MergeAttr_ExpandChrFormat
2097  : &lcl_MergeAttr;
2098 
2099  const size_t nSize = m_pSwpHints->Count();
2100 
2101  if (nStt == nEnd) // no range:
2102  {
2103  for (size_t n = 0; n < nSize; ++n)
2104  {
2105  const SwTextAttr* pHt = m_pSwpHints->Get(n);
2106  const sal_Int32 nAttrStart = pHt->GetStart();
2107  if (nAttrStart > nEnd) // behind the range
2108  break;
2109 
2110  const sal_Int32* pAttrEnd = pHt->End();
2111  if ( ! pAttrEnd ) // no attributes without end
2112  continue;
2113 
2114  if( ( nAttrStart < nStt &&
2115  ( pHt->DontExpand() ? nStt < *pAttrEnd
2116  : nStt <= *pAttrEnd )) ||
2117  ( nStt == nAttrStart &&
2118  ( nAttrStart == *pAttrEnd || !nStt )))
2119  (*fnMergeAttr)( rSet, pHt->GetAttr() );
2120  }
2121  }
2122  else // a query range is defined
2123  {
2124  // #i75299#
2125  std::optional< std::vector< SwPoolItemEndPair > > pAttrArr;
2126 
2127  const size_t coArrSz = RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN;
2128 
2129  for (size_t n = 0; n < nSize; ++n)
2130  {
2131  const SwTextAttr* pHt = m_pSwpHints->Get(n);
2132  const sal_Int32 nAttrStart = pHt->GetStart();
2133  if (nAttrStart > nEnd) // outside, behind
2134  break;
2135 
2136  const sal_Int32* pAttrEnd = pHt->End();
2137  if ( ! pAttrEnd ) // no attributes without end
2138  continue;
2139 
2140  bool bChkInvalid = false;
2141  if (nAttrStart <= nStt) // before or exactly Start
2142  {
2143  if (*pAttrEnd <= nStt) // outside, before
2144  continue;
2145 
2146  if (nEnd <= *pAttrEnd) // behind or exactly End
2147  (*fnMergeAttr)( aFormatSet, pHt->GetAttr() );
2148  else
2149 // else if( pHt->GetAttr() != aFormatSet.Get( pHt->Which() ) )
2150  // ambiguous
2151  bChkInvalid = true;
2152  }
2153  else if (nAttrStart < nEnd // starts in the range
2154 )// && pHt->GetAttr() != aFormatSet.Get( pHt->Which() ) )
2155  bChkInvalid = true;
2156 
2157  if( bChkInvalid )
2158  {
2159  // ambiguous?
2160  std::unique_ptr< SfxItemIter > pItemIter;
2161  const SfxPoolItem* pItem = nullptr;
2162 
2163  if ( RES_TXTATR_AUTOFMT == pHt->Which() )
2164  {
2165  const SfxItemSet* pAutoSet = CharFormat::GetItemSet( pHt->GetAttr() );
2166  if ( pAutoSet )
2167  {
2168  pItemIter.reset( new SfxItemIter( *pAutoSet ) );
2169  pItem = pItemIter->GetCurItem();
2170  }
2171  }
2172  else
2173  pItem = &pHt->GetAttr();
2174 
2175  const sal_Int32 nHintEnd = *pAttrEnd;
2176 
2177  for (; pItem; pItem = pItemIter ? pItemIter->NextItem() : nullptr)
2178  {
2179  const sal_uInt16 nHintWhich = pItem->Which();
2180  OSL_ENSURE(!isUNKNOWNATR(nHintWhich),
2181  "SwTextNode::GetAttr(): unknown attribute?");
2182 
2183  if (!pAttrArr)
2184  {
2185  pAttrArr = std::vector< SwPoolItemEndPair >(coArrSz);
2186  }
2187 
2188  std::vector< SwPoolItemEndPair >::iterator pPrev = pAttrArr->begin();
2189  if (isCHRATR(nHintWhich) ||
2190  isTXTATR_WITHEND(nHintWhich))
2191  {
2192  pPrev += nHintWhich - RES_CHRATR_BEGIN;
2193  }
2194  else
2195  {
2196  pPrev = pAttrArr->end();
2197  }
2198 
2199  if( pPrev != pAttrArr->end() )
2200  {
2201  if( !pPrev->mpItem )
2202  {
2203  if ( bOnlyTextAttr || *pItem != aFormatSet.Get( nHintWhich ) )
2204  {
2205  if( nAttrStart > nStt )
2206  {
2207  rSet.InvalidateItem( nHintWhich );
2208  pPrev->mpItem = INVALID_POOL_ITEM;
2209  }
2210  else
2211  {
2212  pPrev->mpItem = pItem;
2213  pPrev->mnEndPos = nHintEnd;
2214  }
2215  }
2216  }
2217  else if( !IsInvalidItem(pPrev->mpItem) )
2218  {
2219  if( pPrev->mnEndPos == nAttrStart &&
2220  *pPrev->mpItem == *pItem )
2221  {
2222  pPrev->mpItem = pItem;
2223  pPrev->mnEndPos = nHintEnd;
2224  }
2225  else
2226  {
2227  rSet.InvalidateItem( nHintWhich );
2228  pPrev->mpItem = INVALID_POOL_ITEM;
2229  }
2230  }
2231  }
2232  } // end while
2233  }
2234  }
2235 
2236  if (pAttrArr)
2237  {
2238  for (size_t n = 0; n < coArrSz; ++n)
2239  {
2240  const SwPoolItemEndPair& rItemPair = (*pAttrArr)[ n ];
2241  if( rItemPair.mpItem && !IsInvalidItem(rItemPair.mpItem) )
2242  {
2243  const sal_uInt16 nWh =
2244  o3tl::narrowing<sal_uInt16>(n + RES_CHRATR_BEGIN);
2245 
2246  if (nEnd <= rItemPair.mnEndPos) // behind or exactly end
2247  {
2248  if( *rItemPair.mpItem != aFormatSet.Get( nWh ) )
2249  (*fnMergeAttr)( rSet, *rItemPair.mpItem );
2250  }
2251  else
2252  // ambiguous
2253  rSet.InvalidateItem( nWh );
2254  }
2255  }
2256  }
2257  }
2258  if( aFormatSet.Count() )
2259  {
2260  // remove all from the format-set that are also set in the text-set
2261  aFormatSet.Differentiate( rSet );
2262  }
2263  }
2264 
2265  if (aFormatSet.Count())
2266  {
2267  // now "merge" everything
2268  rSet.Put( aFormatSet );
2269  }
2270 
2271  return rSet.Count() != 0;
2272 }
2273 
2274 namespace
2275 {
2276 
2277 typedef std::pair<sal_Int32, sal_Int32> AttrSpan_t;
2278 typedef std::multimap<AttrSpan_t, const SwTextAttr*> AttrSpanMap_t;
2279 
2280 struct IsAutoStyle
2281 {
2282  bool
2283  operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
2284  const
2285  {
2286  return i_rAttrSpan.second && i_rAttrSpan.second->Which() == RES_TXTATR_AUTOFMT;
2287  }
2288 };
2289 
2293 struct RemovePresentAttrs
2294 {
2295  explicit RemovePresentAttrs(SfxItemSet& io_rAttrSet)
2296  : m_rAttrSet(io_rAttrSet)
2297  {
2298  }
2299 
2300  void
2301  operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
2302  const
2303  {
2304  if (!i_rAttrSpan.second)
2305  {
2306  return;
2307  }
2308 
2309  const SwTextAttr* const pAutoStyle(i_rAttrSpan.second);
2310  SfxItemIter aIter(m_rAttrSet);
2311  for (const SfxPoolItem* pItem(aIter.GetCurItem()); pItem; pItem = aIter.NextItem())
2312  {
2313  const sal_uInt16 nWhich(pItem->Which());
2314  if (CharFormat::IsItemIncluded(nWhich, pAutoStyle))
2315  {
2316  m_rAttrSet.ClearItem(nWhich);
2317  }
2318  }
2319  }
2320 
2321 private:
2322  SfxItemSet& m_rAttrSet;
2323 };
2324 
2331 void
2332 lcl_CollectHintSpans(const SwpHints& i_rHints, const sal_Int32 nLength,
2333  AttrSpanMap_t& o_rSpanMap)
2334 {
2335  sal_Int32 nLastEnd(0);
2336 
2337  for (size_t i = 0; i < i_rHints.Count(); ++i)
2338  {
2339  const SwTextAttr* pHint = i_rHints.Get(i);
2340  const sal_uInt16 nWhich(pHint->Which());
2341  if (nWhich == RES_TXTATR_CHARFMT || nWhich == RES_TXTATR_AUTOFMT)
2342  {
2343  const AttrSpan_t aSpan(pHint->GetStart(), *pHint->End());
2344  o_rSpanMap.emplace(aSpan, pHint);
2345 
2346  // < not != because there may be multiple CHARFMT at same range
2347  if (nLastEnd < aSpan.first)
2348  {
2349  // insert dummy span covering the gap
2350  o_rSpanMap.emplace( AttrSpan_t(nLastEnd, aSpan.first), nullptr );
2351  }
2352 
2353  nLastEnd = aSpan.second;
2354  }
2355  }
2356 
2357  // no hints at the end (special case: no hints at all in i_rHints)
2358  if (nLastEnd != nLength && nLength != 0)
2359  {
2360  o_rSpanMap.emplace(AttrSpan_t(nLastEnd, nLength), nullptr);
2361  }
2362 }
2363 
2364 void
2365 lcl_FillWhichIds(const SfxItemSet& i_rAttrSet, std::vector<sal_uInt16>& o_rClearIds)
2366 {
2367  o_rClearIds.reserve(i_rAttrSet.Count());
2368  SfxItemIter aIter(i_rAttrSet);
2369  for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
2370  {
2371  o_rClearIds.push_back(pItem->Which());
2372  }
2373 }
2374 
2375 struct SfxItemSetClearer
2376 {
2377  SfxItemSet & m_rItemSet;
2378  explicit SfxItemSetClearer(SfxItemSet & rItemSet) : m_rItemSet(rItemSet) { }
2379  void operator()(sal_uInt16 const nWhich) { m_rItemSet.ClearItem(nWhich); }
2380 };
2381 
2382 }
2383 
2387 void
2389 {
2390  typedef AttrSpanMap_t::iterator AttrSpanMap_iterator_t;
2391  AttrSpanMap_t aAttrSpanMap;
2392 
2393  if (i_rAttrSet.Count() == 0)
2394  {
2395  return;
2396  }
2397 
2398  // 1. Identify all spans in hints' array
2399 
2400  lcl_CollectHintSpans(*m_pSwpHints, m_Text.getLength(), aAttrSpanMap);
2401 
2402  // 2. Go through all spans and insert new attrs
2403 
2404  AttrSpanMap_iterator_t aCurRange(aAttrSpanMap.begin());
2405  const AttrSpanMap_iterator_t aEnd(aAttrSpanMap.end());
2406  while (aCurRange != aEnd)
2407  {
2408  typedef std::pair<AttrSpanMap_iterator_t, AttrSpanMap_iterator_t>
2409  AttrSpanMapRange_t;
2410  AttrSpanMapRange_t aRange(aAttrSpanMap.equal_range(aCurRange->first));
2411 
2412  // 2a. Collect attributes to insert
2413 
2414  SfxItemSet aCurSet(i_rAttrSet);
2415  std::for_each(aRange.first, aRange.second, RemovePresentAttrs(aCurSet));
2416 
2417  // 2b. Insert automatic style containing the collected attributes
2418 
2419  if (aCurSet.Count() != 0)
2420  {
2421  AttrSpanMap_iterator_t aAutoStyleIt(
2422  std::find_if(aRange.first, aRange.second, IsAutoStyle()));
2423  if (aAutoStyleIt != aRange.second)
2424  {
2425  // there already is an automatic style on that span:
2426  // create new one and remove the original one
2427  SwTextAttr* const pAutoStyle(const_cast<SwTextAttr*>(aAutoStyleIt->second));
2428  const std::shared_ptr<SfxItemSet> pOldStyle(
2429  static_cast<const SwFormatAutoFormat&>(
2430  pAutoStyle->GetAttr()).GetStyleHandle());
2431  aCurSet.Put(*pOldStyle);
2432 
2433  // remove the old hint
2434  m_pSwpHints->Delete(pAutoStyle);
2435  DestroyAttr(pAutoStyle);
2436  }
2437  m_pSwpHints->Insert(
2438  MakeTextAttr(GetDoc(), aCurSet,
2439  aCurRange->first.first, aCurRange->first.second));
2440  }
2441 
2442  aCurRange = aRange.second;
2443  }
2444 
2445  // hints were directly inserted, so need to fix the Ignore flags now
2446  m_pSwpHints->MergePortions(*this);
2447 
2448  // 3. Clear items from the node
2449  std::vector<sal_uInt16> aClearedIds;
2450  lcl_FillWhichIds(i_rAttrSet, aClearedIds);
2451  ClearItemsFromAttrSet(aClearedIds);
2452 }
2453 
2455 {
2456  SfxItemSet aThisSet( GetDoc().GetAttrPool(), aCharFormatSetRange );
2457  if( HasSwAttrSet() && GetpSwAttrSet()->Count() )
2458  aThisSet.Put( *GetpSwAttrSet() );
2459 
2461 
2462  if( pNd == this )
2463  {
2464  impl_FormatToTextAttr(aThisSet);
2465  }
2466  else
2467  {
2468  // There are five possible combinations of items from this and
2469  // pNd (pNd is the 'main' node):
2470 
2471  // case pNd this action
2472 
2473  // 1 - - do nothing
2474  // 2 - a convert item to attr of this
2475  // 3 a - convert item to attr of pNd
2476  // 4 a a clear item in this
2477  // 5 a b convert item to attr of this
2478 
2479  SfxItemSet aNdSet( pNd->GetDoc().GetAttrPool(), aCharFormatSetRange );
2480  if( pNd->HasSwAttrSet() && pNd->GetpSwAttrSet()->Count() )
2481  aNdSet.Put( *pNd->GetpSwAttrSet() );
2482 
2483  pNd->GetOrCreateSwpHints();
2484 
2485  std::vector<sal_uInt16> aProcessedIds;
2486 
2487  if( aThisSet.Count() )
2488  {
2489  SfxItemIter aIter( aThisSet );
2490  const SfxPoolItem* pItem = aIter.GetCurItem(), *pNdItem = nullptr;
2491  SfxItemSet aConvertSet( GetDoc().GetAttrPool(), aCharFormatSetRange );
2492  std::vector<sal_uInt16> aClearWhichIds;
2493 
2494  do
2495  {
2496  if( SfxItemState::SET == aNdSet.GetItemState( pItem->Which(), false, &pNdItem ) )
2497  {
2498  if (*pItem == *pNdItem) // 4
2499  {
2500  aClearWhichIds.push_back( pItem->Which() );
2501  }
2502  else // 5
2503  {
2504  aConvertSet.Put(*pItem);
2505  }
2506  aProcessedIds.push_back(pItem->Which());
2507  }
2508  else // 2
2509  {
2510  aConvertSet.Put(*pItem);
2511  }
2512 
2513  pItem = aIter.NextItem();
2514  } while (pItem);
2515 
2516  // 4/ clear items of this that are set with the same value on pNd
2517  ClearItemsFromAttrSet( aClearWhichIds );
2518 
2519  // 2, 5/ convert all other items to attrs
2520  impl_FormatToTextAttr(aConvertSet);
2521  }
2522 
2523  {
2524  std::for_each(aProcessedIds.begin(), aProcessedIds.end(),
2525  SfxItemSetClearer(aNdSet));
2526 
2527  // 3/ convert items to attrs
2528  pNd->impl_FormatToTextAttr(aNdSet);
2529 
2530  if( aNdSet.Count() )
2531  {
2532  SwFormatChg aTmp1( pNd->GetFormatColl() );
2533  pNd->CallSwClientNotify(sw::LegacyModifyHint(&aTmp1, &aTmp1));
2534  }
2535  }
2536  }
2537 
2539 
2540  pNd->TryDeleteSwpHints();
2541 }
2542 
2544 {
2545  m_bDDEFields = m_bFootnote = false;
2546  const size_t nSize = Count();
2547  for( size_t nPos = 0; nPos < nSize; ++nPos )
2548  {
2549  const SwTextAttr* pAttr = Get( nPos );
2550  switch( pAttr->Which() )
2551  {
2552  case RES_TXTATR_FTN:
2553  m_bFootnote = true;
2554  if ( m_bDDEFields )
2555  return;
2556  break;
2557  case RES_TXTATR_FIELD:
2558  {
2559  const SwField* pField = pAttr->GetFormatField().GetField();
2560  if( SwFieldIds::Dde == pField->GetTyp()->Which() )
2561  {
2562  m_bDDEFields = true;
2563  if ( m_bFootnote )
2564  return;
2565  }
2566  }
2567  break;
2568  }
2569  }
2570 }
2571 
2573 {
2574  m_bCalcHiddenParaField = false;
2575  const bool bOldHiddenByParaField = m_bHiddenByParaField;
2576  bool bNewHiddenByParaField = false;
2577  int nNewResultWeight = 0;
2578  const size_t nSize = Count();
2579  const SwTextAttr* pTextHt;
2580 
2581  for (size_t nPos = 0; nPos < nSize; ++nPos)
2582  {
2583  pTextHt = Get(nPos);
2584  const sal_uInt16 nWhich = pTextHt->Which();
2585 
2586  if (RES_TXTATR_FIELD == nWhich)
2587  {
2588  // see also SwTextFrame::IsHiddenNow()
2589  const SwFormatField& rField = pTextHt->GetFormatField();
2590  int nCurWeight = m_rParent.GetDoc().FieldCanHideParaWeight(rField.GetField()->GetTyp()->Which());
2591  if (nCurWeight > nNewResultWeight)
2592  {
2593  nNewResultWeight = nCurWeight;
2594  bNewHiddenByParaField = m_rParent.GetDoc().FieldHidesPara(*rField.GetField());
2595  }
2596  else if (nCurWeight == nNewResultWeight && bNewHiddenByParaField)
2597  {
2598  // Currently, for both supported hiding types (HiddenPara, Database), "Don't hide"
2599  // takes precedence - i.e., if there's a "Don't hide" field of that weight, we only
2600  // care about fields of higher weight.
2601  bNewHiddenByParaField = m_rParent.GetDoc().FieldHidesPara(*rField.GetField());
2602  }
2603  }
2604  }
2605  SetHiddenByParaField(bNewHiddenByParaField);
2606  return bOldHiddenByParaField != bNewHiddenByParaField;
2607 }
2608 
2609 void SwpHints::NoteInHistory( SwTextAttr *pAttr, const bool bNew )
2610 {
2611  if ( m_pHistory ) { m_pHistory->AddHint( pAttr, bNew ); }
2612 }
2613 
2614 namespace {
2615 typedef std::multimap< int, std::pair<SwTextAttr*, bool> > PortionMap;
2616 enum MergeResult { MATCH, DIFFER_ONLY_RSID, DIFFER };
2617 }
2618 
2620  int i, int j,
2621  const std::pair< PortionMap::iterator, PortionMap::iterator >& aRange1,
2622  const std::pair< PortionMap::iterator, PortionMap::iterator >& aRange2,
2623  std::unordered_map<int, bool>& RsidOnlyAutoFormatFlagMap);
2624 
2626 {
2627  if ( !Count() )
2628  return false;
2629 
2630  // sort before merging
2631  Resort();
2632 
2633  bool bRet = false;
2634  PortionMap aPortionMap;
2635  std::unordered_map<int, bool> RsidOnlyAutoFormatFlagMap;
2636  sal_Int32 nLastPorStart = COMPLETE_STRING;
2637  int nKey = 0;
2638 
2639  // get portions by start position:
2640  for ( size_t i = 0; i < Count(); ++i )
2641  {
2642  SwTextAttr *pHt = Get( i );
2643  if ( RES_TXTATR_CHARFMT != pHt->Which() &&
2644  RES_TXTATR_AUTOFMT != pHt->Which() )
2645  //&&
2646  //RES_TXTATR_INETFMT != pHt->Which() )
2647  continue;
2648 
2649  bool isRsidOnlyAutoFormat(false);
2650  // check for RSID-only AUTOFMT
2651  if (RES_TXTATR_AUTOFMT == pHt->Which())
2652  {
2653  std::shared_ptr<SfxItemSet> const pSet(
2654  pHt->GetAutoFormat().GetStyleHandle());
2655  if ((pSet->Count() == 1) && pSet->GetItem(RES_CHRATR_RSID, false))
2656  {
2657  // fdo#70201: eliminate no-extent RSID-only AUTOFMT
2658  // could be produced by ReplaceText or (maybe?) RstAttr
2659  if (pHt->GetStart() == *pHt->GetEnd())
2660  {
2661  DeleteAtPos(i); // kill it without History!
2662  SwTextAttr::Destroy(pHt, rNode.GetDoc().GetAttrPool());
2663  --i;
2664  continue;
2665  }
2666  // fdo#52028: this one has _only_ RSID => ignore it completely
2667  if (!pHt->IsFormatIgnoreStart() || !pHt->IsFormatIgnoreEnd())
2668  {
2669  NoteInHistory(pHt);
2670  pHt->SetFormatIgnoreStart(true);
2671  pHt->SetFormatIgnoreEnd (true);
2672  NoteInHistory(pHt, true);
2673  }
2674  isRsidOnlyAutoFormat = true;
2675  }
2676  }
2677 
2678  if (pHt->GetStart() == *pHt->GetEnd())
2679  {
2680  // no-length hints are a disease. ignore them here.
2681  // the SwAttrIter::SeekFwd will not call Rst/Chg for them.
2682  continue;
2683  }
2684 
2685  const sal_Int32 nPorStart = pHt->GetStart();
2686  if (nPorStart != nLastPorStart)
2687  ++nKey;
2688  nLastPorStart = nPorStart;
2689  aPortionMap.insert(std::make_pair(nKey,
2690  std::make_pair(pHt, isRsidOnlyAutoFormat)));
2691  RsidOnlyAutoFormatFlagMap[nKey] = isRsidOnlyAutoFormat;
2692  }
2693 
2694  // check if portion i can be merged with portion i+1:
2695  // note: need to include i=0 to set IgnoreStart and j=nKey+1 to reset
2696  // IgnoreEnd at first / last portion
2697  int i = 0;
2698  int j = i + 1;
2699  while ( i <= nKey )
2700  {
2701  std::pair< PortionMap::iterator, PortionMap::iterator > aRange1 = aPortionMap.equal_range( i );
2702  std::pair< PortionMap::iterator, PortionMap::iterator > aRange2 = aPortionMap.equal_range( j );
2703 
2704  MergeResult eMerge = lcl_Compare_Attributes(i, j, aRange1, aRange2, RsidOnlyAutoFormatFlagMap);
2705 
2706  if (MATCH == eMerge)
2707  {
2708  // important: delete second range so any IgnoreStart on the first
2709  // range is still valid
2710  // erase all elements with key i + 1
2711  sal_Int32 nNewPortionEnd = 0;
2712  for ( auto aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2 )
2713  {
2714  SwTextAttr *const p2 = aIter2->second.first;
2715  nNewPortionEnd = *p2->GetEnd();
2716 
2717  const size_t nCountBeforeDelete = Count();
2718  Delete( p2 );
2719 
2720  // robust: check if deletion actually took place before destroying attribute:
2721  if ( Count() < nCountBeforeDelete )
2722  rNode.DestroyAttr( p2 );
2723  }
2724  aPortionMap.erase( aRange2.first, aRange2.second );
2725  ++j;
2726 
2727  // change all attributes with key i
2728  aRange1 = aPortionMap.equal_range( i );
2729  for ( auto aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1 )
2730  {
2731  SwTextAttr *const p1 = aIter1->second.first;
2732  NoteInHistory( p1 );
2733  p1->SetEnd(nNewPortionEnd);
2734  NoteInHistory( p1, true );
2735  bRet = true;
2736  }
2737 
2738  if (bRet)
2739  {
2740  Resort();
2741  }
2742  }
2743  else
2744  {
2745  // when not merging the ignore flags need to be either set or reset
2746  // (reset too in case one of the autofmts was recently changed)
2747  bool const bSetIgnoreFlag(DIFFER_ONLY_RSID == eMerge);
2748  for (auto aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1)
2749  {
2750  if (!aIter1->second.second) // already set above, don't change
2751  {
2752  SwTextAttr *const pCurrent(aIter1->second.first);
2753  if (pCurrent->IsFormatIgnoreEnd() != bSetIgnoreFlag)
2754  {
2755  NoteInHistory(pCurrent);
2756  pCurrent->SetFormatIgnoreEnd(bSetIgnoreFlag);
2757  NoteInHistory(pCurrent, true);
2758  }
2759  }
2760  }
2761  for (auto aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2)
2762  {
2763  if (!aIter2->second.second) // already set above, don't change
2764  {
2765  SwTextAttr *const pCurrent(aIter2->second.first);
2766  if (pCurrent->IsFormatIgnoreStart() != bSetIgnoreFlag)
2767  {
2768  NoteInHistory(pCurrent);
2769  pCurrent->SetFormatIgnoreStart(bSetIgnoreFlag);
2770  NoteInHistory(pCurrent, true);
2771  }
2772  }
2773  }
2774  i = j; // ++i not enough: i + 1 may have been deleted (MATCH)!
2775  ++j;
2776  }
2777  }
2778 
2779  return bRet;
2780 }
2781 
2782 
2784  int i, int j,
2785  const std::pair< PortionMap::iterator, PortionMap::iterator >& aRange1,
2786  const std::pair< PortionMap::iterator, PortionMap::iterator >& aRange2,
2787  std::unordered_map<int, bool>& RsidOnlyAutoFormatFlagMap)
2788 {
2789  PortionMap::iterator aIter1 = aRange1.first;
2790  PortionMap::iterator aIter2 = aRange2.first;
2791 
2792  size_t const nAttributesInPor1 = std::distance(aRange1.first, aRange1.second);
2793  size_t const nAttributesInPor2 = std::distance(aRange2.first, aRange2.second);
2794  bool const isRsidOnlyAutoFormat1(RsidOnlyAutoFormatFlagMap[i]);
2795  bool const isRsidOnlyAutoFormat2(RsidOnlyAutoFormatFlagMap[j]);
2796 
2797  // if both have one they could be equal, but not if only one has it
2798  bool const bSkipRsidOnlyAutoFormat(nAttributesInPor1 != nAttributesInPor2);
2799 
2800  // this loop needs to handle the case where one has a CHARFMT and the
2801  // other CHARFMT + RSID-only AUTOFMT, so...
2802  // want to skip over RSID-only AUTOFMT here, hence the -1
2803  if (!((nAttributesInPor1 - (isRsidOnlyAutoFormat1 ? 1 : 0)) ==
2804  (nAttributesInPor2 - (isRsidOnlyAutoFormat2 ? 1 : 0))
2805  && (nAttributesInPor1 != 0 || nAttributesInPor2 != 0)))
2806  {
2807  return DIFFER;
2808  }
2809 
2810  MergeResult eMerge(MATCH);
2811 
2812  // _if_ there is one element more either in aRange1 or aRange2
2813  // it _must_ be an RSID-only AUTOFMT, which can be ignored here...
2814  // But if both have RSID-only AUTOFMT they could be equal, no skip!
2815  while (aIter1 != aRange1.second || aIter2 != aRange2.second)
2816  {
2817  // first of all test if there's no gap (before skipping stuff!)
2818  if (aIter1 != aRange1.second && aIter2 != aRange2.second &&
2819  *aIter1->second.first->End() < aIter2->second.first->GetStart())
2820  {
2821  return DIFFER;
2822  }
2823  // skip it - cannot be equal if bSkipRsidOnlyAutoFormat is set
2824  if (bSkipRsidOnlyAutoFormat
2825  && aIter1 != aRange1.second && aIter1->second.second)
2826  {
2827  assert(DIFFER != eMerge);
2828  eMerge = DIFFER_ONLY_RSID;
2829  ++aIter1;
2830  continue;
2831  }
2832  if (bSkipRsidOnlyAutoFormat
2833  && aIter2 != aRange2.second && aIter2->second.second)
2834  {
2835  assert(DIFFER != eMerge);
2836  eMerge = DIFFER_ONLY_RSID;
2837  ++aIter2;
2838  continue;
2839  }
2840  assert(aIter1 != aRange1.second && aIter2 != aRange2.second);
2841  SwTextAttr const*const p1 = aIter1->second.first;
2842  SwTextAttr const*const p2 = aIter2->second.first;
2843  if (p1->Which() != p2->Which())
2844  {
2845  return DIFFER;
2846  }
2847  if (!(*p1 == *p2))
2848  {
2849  // fdo#52028: for auto styles, check if they differ only
2850  // in the RSID, which should have no effect on text layout
2851  if (RES_TXTATR_AUTOFMT != p1->Which())
2852  return DIFFER;
2853 
2854  const SfxItemSet& rSet1 = *p1->GetAutoFormat().GetStyleHandle();
2855  const SfxItemSet& rSet2 = *p2->GetAutoFormat().GetStyleHandle();
2856 
2857  // sadly SfxItemSet::operator== does not seem to work?
2858  SfxItemIter iter1(rSet1);
2859  SfxItemIter iter2(rSet2);
2860  for (SfxPoolItem const* pItem1 = iter1.GetCurItem(),
2861  * pItem2 = iter2.GetCurItem();;)
2862  {
2863  if (pItem1 && pItem1->Which() == RES_CHRATR_RSID)
2864  pItem1 = iter1.NextItem();
2865  if (pItem2 && pItem2->Which() == RES_CHRATR_RSID)
2866  pItem2 = iter2.NextItem();
2867  if (!pItem1 && !pItem2)
2868  {
2869  eMerge = DIFFER_ONLY_RSID;
2870  break;
2871  }
2872  if (!pItem1 || !pItem2)
2873  {
2874  return DIFFER;
2875  }
2876  if (pItem1 != pItem2) // all are poolable
2877  {
2878  assert(IsInvalidItem(pItem1) || IsInvalidItem(pItem2) || pItem1->Which() != pItem2->Which() || *pItem1 != *pItem2);
2879  return DIFFER;
2880  }
2881  pItem1 = iter1.NextItem();
2882  pItem2 = iter2.NextItem();
2883  }
2884  }
2885  ++aIter1;
2886  ++aIter2;
2887  }
2888  return eMerge;
2889 }
2890 
2891 
2892 // check if there is already a character format and adjust the sort numbers
2893 static void lcl_CheckSortNumber( const SwpHints& rHints, SwTextCharFormat& rNewCharFormat )
2894 {
2895  const sal_Int32 nHtStart = rNewCharFormat.GetStart();
2896  const sal_Int32 nHtEnd = *rNewCharFormat.GetEnd();
2897  sal_uInt16 nSortNumber = 0;
2898 
2899  for ( size_t i = 0; i < rHints.Count(); ++i )
2900  {
2901  const SwTextAttr* pOtherHt = rHints.Get(i);
2902 
2903  const sal_Int32 nOtherStart = pOtherHt->GetStart();
2904 
2905  if ( nOtherStart > nHtStart )
2906  break;
2907 
2908  if ( RES_TXTATR_CHARFMT == pOtherHt->Which() )
2909  {
2910  const sal_Int32 nOtherEnd = *pOtherHt->End();
2911 
2912  if ( nOtherStart == nHtStart && nOtherEnd == nHtEnd )
2913  {
2914  nSortNumber = static_txtattr_cast<const SwTextCharFormat*>(pOtherHt)->GetSortNumber() + 1;
2915  }
2916  }
2917  }
2918 
2919  if ( nSortNumber > 0 )
2920  rNewCharFormat.SetSortNumber( nSortNumber );
2921 }
2922 
2923 /*
2924  * Try to insert the new hint.
2925  * Depending on the type of the hint, this either always succeeds, or may fail.
2926  * Depending on the type of the hint, other hints may be deleted or
2927  * overwritten.
2928  * The return value indicates successful insertion.
2929  */
2931  SwTextAttr* const pHint,
2932  SwTextNode &rNode,
2933  const SetAttrMode nMode )
2934 {
2935  if ( MAX_HINTS <= Count() ) // we're sorry, this flight is overbooked...
2936  {
2937  OSL_FAIL("hints array full :-(");
2938  return false;
2939  }
2940 
2941  const sal_Int32 *pHtEnd = pHint->GetEnd();
2942  const sal_uInt16 nWhich = pHint->Which();
2943  std::vector<sal_uInt16> aWhichSublist;
2944 
2945  switch( nWhich )
2946  {
2947  case RES_TXTATR_CHARFMT:
2948  {
2949  // Check if character format contains hidden attribute:
2950  const SwCharFormat* pFormat = pHint->GetCharFormat().GetCharFormat();
2951  const SfxPoolItem* pItem;
2952  if ( SfxItemState::SET == pFormat->GetItemState( RES_CHRATR_HIDDEN, true, &pItem ) )
2953  rNode.SetCalcHiddenCharFlags();
2954 
2955  static_txtattr_cast<SwTextCharFormat*>(pHint)->ChgTextNode( &rNode );
2956  break;
2957  }
2958  // #i75430# Recalc hidden flags if necessary
2959  case RES_TXTATR_AUTOFMT:
2960  {
2961  std::shared_ptr<SfxItemSet> const pSet( pHint->GetAutoFormat().GetStyleHandle() );
2962  if (pHint->GetStart() == *pHint->GetEnd())
2963  {
2964  if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
2965  { // empty range RSID-only hints could cause trouble, there's no
2966  rNode.DestroyAttr(pHint); // need for them so don't insert
2967  return false;
2968  }
2969  }
2970  // Check if auto style contains hidden attribute:
2971  const SfxPoolItem* pHiddenItem = CharFormat::GetItem( *pHint, RES_CHRATR_HIDDEN );
2972  if ( pHiddenItem )
2973  rNode.SetCalcHiddenCharFlags();
2974 
2975  // fdo#71556: populate aWhichFormatAttr member of SwMsgPoolItem
2976  const WhichRangesContainer& pRanges = pSet->GetRanges();
2977  for(auto const & rPair : pRanges)
2978  {
2979  const sal_uInt16 nBeg = rPair.first;
2980  const sal_uInt16 nEnd = rPair.second;
2981  for( sal_uInt16 nSubElem = nBeg; nSubElem <= nEnd; ++nSubElem )
2982  if( pSet->HasItem( nSubElem ) )
2983  aWhichSublist.push_back( nSubElem );
2984  }
2985  break;
2986  }
2987  case RES_TXTATR_INETFMT:
2988  static_txtattr_cast<SwTextINetFormat*>(pHint)->InitINetFormat(rNode);
2989  break;
2990 
2991  case RES_TXTATR_FIELD:
2992  case RES_TXTATR_ANNOTATION:
2993  case RES_TXTATR_INPUTFIELD:
2994  {
2995  SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
2996  bool bDelFirst = nullptr != pTextField->GetpTextNode();
2997  pTextField->ChgTextNode( &rNode );
2998  SwDoc& rDoc = rNode.GetDoc();
2999  const SwField* pField = pTextField->GetFormatField().GetField();
3000 
3001  if( !rDoc.getIDocumentFieldsAccess().IsNewFieldLst() )
3002  {
3003  // certain fields must update the SwDoc's calculation flags
3004  switch( pField->GetTyp()->Which() )
3005  {
3006  case SwFieldIds::Database:
3007  case SwFieldIds::SetExp:
3010  case SwFieldIds::DbNumSet:
3011  case SwFieldIds::DbNextSet:
3012  {
3013  if( bDelFirst )
3014  rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(false, *pTextField);
3015  if( rNode.GetNodes().IsDocNodes() )
3016  rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(true, *pTextField);
3017  }
3018  break;
3019  case SwFieldIds::Dde:
3020  if( rNode.GetNodes().IsDocNodes() )
3021  static_cast<SwDDEFieldType*>(pField->GetTyp())->IncRefCnt();
3022  break;
3023  default: break;
3024  }
3025  }
3026 
3027  // insert into real document's nodes-array?
3028  if( rNode.GetNodes().IsDocNodes() )
3029  {
3030  bool bInsFieldType = false;
3031  switch( pField->GetTyp()->Which() )
3032  {
3033  case SwFieldIds::SetExp:
3034  bInsFieldType = static_cast<SwSetExpFieldType*>(pField->GetTyp())->IsDeleted();
3035  if( nsSwGetSetExpType::GSE_SEQ & static_cast<SwSetExpFieldType*>(pField->GetTyp())->GetType() )
3036  {
3037  // register the field at its FieldType before setting
3038  // the sequence reference number!
3039  SwSetExpFieldType* pFieldType = static_cast<SwSetExpFieldType*>(
3040  rDoc.getIDocumentFieldsAccess().InsertFieldType( *pField->GetTyp() ) );
3041  if( pFieldType != pField->GetTyp() )
3042  {
3043  SwFormatField* pFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField());
3044  pFormatField->RegisterToFieldType( *pFieldType );
3045  pFormatField->GetField()->ChgTyp( pFieldType );
3046  }
3047  pFieldType->SetSeqRefNo( *const_cast<SwSetExpField*>(static_cast<const SwSetExpField*>(pField)) );
3048  }
3049  break;
3050  case SwFieldIds::User:
3051  bInsFieldType = static_cast<SwUserFieldType*>(pField->GetTyp())->IsDeleted();
3052  break;
3053 
3054  case SwFieldIds::Dde:
3056  static_cast<SwDDEFieldType*>(pField->GetTyp())->IncRefCnt();
3057  bInsFieldType = static_cast<SwDDEFieldType*>(pField->GetTyp())->IsDeleted();
3058  break;
3059 
3060  case SwFieldIds::Postit:
3061  if ( rDoc.GetDocShell() )
3062  {
3063  rDoc.GetDocShell()->Broadcast( SwFormatFieldHint(
3065  }
3066  break;
3067  default: break;
3068  }
3069  if( bInsFieldType )
3071  }
3072  }
3073  break;
3074  case RES_TXTATR_FTN :
3075  static_cast<SwTextFootnote*>(pHint)->ChgTextNode( &rNode );
3076  break;
3077  case RES_TXTATR_REFMARK:
3078  static_txtattr_cast<SwTextRefMark*>(pHint)->ChgTextNode( &rNode );
3079  if( rNode.GetNodes().IsDocNodes() )
3080  {
3081  // search for a reference with the same name
3082  SwTextAttr* pTmpHt;
3083  for( size_t n = 0, nEnd = Count(); n < nEnd; ++n )
3084  {
3085  const sal_Int32 *pTmpHtEnd;
3086  const sal_Int32 *pTmpHintEnd;
3087  if (RES_TXTATR_REFMARK == (pTmpHt = Get(n))->Which() &&
3088  pHint->GetAttr() == pTmpHt->GetAttr() &&
3089  nullptr != ( pTmpHtEnd = pTmpHt->GetEnd() ) &&
3090  nullptr != ( pTmpHintEnd = pHint->GetEnd() ) )
3091  {
3093  pTmpHt->GetStart(), *pTmpHtEnd,
3094  pHint->GetStart(), *pTmpHintEnd );
3095  bool bDelOld = true, bChgStart = false, bChgEnd = false;
3096  switch( eCmp )
3097  {
3099  case SwComparePosition::Behind: bDelOld = false; break;
3100 
3101  case SwComparePosition::Outside: bChgStart = bChgEnd = true; break;
3102 
3104  case SwComparePosition::OverlapBefore: bChgStart = true; break;
3106  case SwComparePosition::OverlapBehind: bChgEnd = true; break;
3107  default: break;
3108  }
3109 
3110  if( bChgStart )
3111  {
3112  pHint->SetStart( pTmpHt->GetStart() );
3113  }
3114  if( bChgEnd )
3115  pHint->SetEnd(*pTmpHtEnd);
3116 
3117  if( bDelOld )
3118  {
3119  NoteInHistory( pTmpHt );
3120  rNode.DestroyAttr( Cut( n-- ) );
3121  --nEnd;
3122  }
3123  }
3124  }
3125  }
3126  break;
3127  case RES_TXTATR_TOXMARK:
3128  static_txtattr_cast<SwTextTOXMark*>(pHint)->ChgTextNode( &rNode );
3129  break;
3130 
3131  case RES_TXTATR_CJK_RUBY:
3132  static_txtattr_cast<SwTextRuby*>(pHint)->InitRuby(rNode);
3133  break;
3134 
3135  case RES_TXTATR_META:
3136  case RES_TXTATR_METAFIELD:
3137  static_txtattr_cast<SwTextMeta *>(pHint)->ChgTextNode( &rNode );
3138  break;
3139 
3140  case RES_CHRATR_HIDDEN:
3141  rNode.SetCalcHiddenCharFlags();
3142  break;
3143  }
3144 
3145  if( SetAttrMode::DONTEXPAND & nMode )
3146  pHint->SetDontExpand( true );
3147 
3148  // special handling for SwTextAttrs without end:
3149  // 1) they cannot overlap
3150  // 2) if two fields are adjacent, they must not be merged into one
3151  // this is guaranteed by inserting a CH_TXTATR_* into the paragraph text!
3152  sal_Int32 nHtStart = pHint->GetStart();
3153  if( !pHtEnd )
3154  {
3155  Insert( pHint );
3156  NoteInHistory(pHint, true);
3157  CalcFlags();
3158 #ifdef DBG_UTIL
3159  if( !rNode.GetDoc().IsInReading() )
3160  CHECK;
3161 #endif
3162  // ... and notify listeners
3163  if(rNode.HasWriterListeners())
3164  {
3165  SwUpdateAttr aHint(
3166  nHtStart,
3167  nHtStart,
3168  nWhich);
3169 
3170  rNode.TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
3171  }
3172 
3173  return true;
3174  }
3175 
3176  // from here on, pHint is known to have an end index!
3177 
3178  if( *pHtEnd < nHtStart )
3179  {
3180  assert(*pHtEnd >= nHtStart);
3181 
3182  // just swap the nonsense:
3183  pHint->SetStart(*pHtEnd);
3184  pHint->SetEnd(nHtStart);
3185  nHtStart = pHint->GetStart();
3186  }
3187 
3188  // I need this value later on for notification but the pointer may become invalid
3189  const sal_Int32 nHintEnd = *pHtEnd;
3190  const bool bNoHintAdjustMode = bool(SetAttrMode::NOHINTADJUST & nMode);
3191 
3192  // handle nesting attributes: inserting may fail due to overlap!
3193  if (pHint->IsNesting())
3194  {
3195  const bool bRet(
3196  TryInsertNesting(rNode, *static_txtattr_cast<SwTextAttrNesting*>(pHint)));
3197  if (!bRet) return false;
3198  }
3199  // Currently REFMARK and TOXMARK have OverlapAllowed set to true.
3200  // These attributes may be inserted directly.
3201  // Also attributes without length may be inserted directly.
3202  // SETATTR_NOHINTADJUST is set e.g., during undo.
3203  // Portion building in not necessary during XML import.
3204  else if ( !bNoHintAdjustMode &&
3205  !pHint->IsOverlapAllowedAttr() &&
3206  !rNode.GetDoc().IsInXMLImport() &&
3207  ( RES_TXTATR_AUTOFMT == nWhich ||
3208  RES_TXTATR_CHARFMT == nWhich ) )
3209  {
3210  assert( nWhich != RES_TXTATR_AUTOFMT ||
3211  static_cast<const SwFormatAutoFormat&>(pHint->GetAttr()).GetStyleHandle()->GetPool() ==
3212  &rNode.GetDoc().GetAttrPool());
3213 
3214  BuildPortions( rNode, *pHint, nMode );
3215 
3216  if ( nHtStart < nHintEnd ) // skip merging for 0-length attributes
3217  MergePortions( rNode );
3218  }
3219  else
3220  {
3221  // There may be more than one character style at the current position.
3222  // Take care of the sort number.
3223  // Special case ruby portion: During import, the ruby attribute is set
3224  // multiple times
3225  // Special case hyperlink: During import, the ruby attribute is set
3226  // multiple times
3227  // FME 2007-11-08 #i82989# in NOHINTADJUST mode, we want to insert
3228  // character attributes directly
3229  if ( RES_TXTATR_CHARFMT == nWhich && !bNoHintAdjustMode )
3230  {
3231  BuildPortions( rNode, *pHint, nMode );
3232  }
3233  else
3234  {
3235  // #i82989# Check sort numbers in NoHintAdjustMode
3236  if ( RES_TXTATR_CHARFMT == nWhich )
3237  lcl_CheckSortNumber(*this, *static_txtattr_cast<SwTextCharFormat*>(pHint));
3238 
3239  Insert( pHint );
3240  NoteInHistory( pHint, true );
3241  }
3242  }
3243 
3244  // ... and notify listeners
3245  if ( rNode.HasWriterListeners() )
3246  {
3247  const SwUpdateAttr aHint(nHtStart, nHintEnd, nWhich, aWhichSublist);
3248  rNode.TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
3249  }
3250 
3251 #ifdef DBG_UTIL
3252  if( !bNoHintAdjustMode && !rNode.GetDoc().IsInReading() )
3253  CHECK;
3254 #endif
3255 
3256  return true;
3257 }
3258 
3259 void SwpHints::DeleteAtPos( const size_t nPos )
3260 {
3261  assert(!m_bStartMapNeedsSorting && "deleting at pos and the list needs sorting?");
3262 
3263  SwTextAttr *pHint = Get(nPos);
3264  assert( pHint->m_pHints == this );
3265  // ChainDelete( pHint );
3266  NoteInHistory( pHint );
3267 
3268  // optimization: nPos is the position in the Starts array
3269  SwTextAttr *pHt = m_HintsByStart[ nPos ];
3270  m_HintsByStart.erase( m_HintsByStart.begin() + nPos );
3271 
3273  ResortStartMap();
3275  ResortEndMap();
3277  ResortWhichMap();
3278 
3279  auto findIt = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), pHt, CompareSwpHtEnd());
3280  assert(*findIt == pHt);
3281  m_HintsByEnd.erase(findIt);
3282 
3283  auto findIt2 = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), pHt, CompareSwpHtWhichStart());
3284  assert(*findIt2 == pHt);
3285  m_HintsByWhichAndStart.erase(findIt2);
3286 
3287  pHt->m_pHints = nullptr;
3288 
3289  if( pHint->Which() == RES_TXTATR_FIELD )
3290  {
3291  SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
3292  const SwFieldType* pFieldTyp = pTextField->GetFormatField().GetField()->GetTyp();
3293  if( SwFieldIds::Dde == pFieldTyp->Which() )
3294  {
3295  const SwTextNode* pNd = pTextField->GetpTextNode();
3296  if( pNd && pNd->GetNodes().IsDocNodes() )
3297  const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pFieldTyp))->DecRefCnt();
3298  pTextField->ChgTextNode(nullptr);
3299  }
3300  else if (m_bHiddenByParaField
3301  && m_rParent.GetDoc().FieldCanHideParaWeight(pFieldTyp->Which()))
3302  {
3303  m_bCalcHiddenParaField = true;
3304  }
3305  }
3306  else if ( pHint->Which() == RES_TXTATR_ANNOTATION )
3307  {
3308  SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
3309  const_cast<SwFormatField&>(pTextField->GetFormatField()).Broadcast(
3311  }
3312 
3313  CalcFlags();
3314  CHECK_NOTMERGED; // called from BuildPortions
3315 }
3316 
3319 void SwpHints::Delete( SwTextAttr const * pTextHt )
3320 {
3321  const size_t nPos = GetIndexOf( pTextHt );
3322  assert(SAL_MAX_SIZE != nPos);
3323  if( SAL_MAX_SIZE != nPos )
3324  DeleteAtPos( nPos );
3325 }
3326 
3327 void SwTextNode::ClearSwpHintsArr( bool bDelFields )
3328 {
3329  if ( !HasHints() )
3330  return;
3331 
3332  size_t nPos = 0;
3333  while ( nPos < m_pSwpHints->Count() )
3334  {
3335  SwTextAttr* pDel = m_pSwpHints->Get( nPos );
3336  bool bDel = false;
3337 
3338  switch( pDel->Which() )
3339  {
3340  case RES_TXTATR_FLYCNT:
3341  case RES_TXTATR_FTN:
3342  break;
3343 
3344  case RES_TXTATR_FIELD:
3345  case RES_TXTATR_ANNOTATION:
3346  case RES_TXTATR_INPUTFIELD:
3347  if( bDelFields )
3348  bDel = true;
3349  break;
3350  default:
3351  bDel = true; break;
3352  }
3353 
3354  if( bDel )
3355  {
3356  m_pSwpHints->DeleteAtPos( nPos );
3357  DestroyAttr( pDel );
3358  }
3359  else
3360  ++nPos;
3361  }
3362 }
3363 
3364 LanguageType SwTextNode::GetLang( const sal_Int32 nBegin, const sal_Int32 nLen,
3365  sal_uInt16 nScript ) const
3366 {
3368 
3369  if ( ! nScript )
3370  {
3371  nScript = g_pBreakIt->GetRealScriptOfText( m_Text, nBegin );
3372  }
3373 
3374  // #i91465# Consider nScript if pSwpHints == 0
3375  const sal_uInt16 nWhichId = GetWhichOfScript( RES_CHRATR_LANGUAGE, nScript );
3376 
3377  if ( HasHints() )
3378  {
3379  const sal_Int32 nEnd = nBegin + nLen;
3380  const size_t nSize = m_pSwpHints->Count();
3381  for ( size_t i = 0; i < nSize; ++i )
3382  {
3383  const SwTextAttr *pHt = m_pSwpHints->Get(i);
3384  const sal_Int32 nAttrStart = pHt->GetStart();
3385  if( nEnd < nAttrStart )
3386  break;
3387 
3388  const sal_uInt16 nWhich = pHt->Which();
3389 
3390  if( nWhichId == nWhich ||
3391  ( ( pHt->IsCharFormatAttr() || RES_TXTATR_AUTOFMT == nWhich ) && CharFormat::IsItemIncluded( nWhichId, pHt ) ) )
3392  {
3393  const sal_Int32 *pEndIdx = pHt->End();
3394  // do the attribute and the range overlap?
3395  if( !pEndIdx )
3396  continue;
3397  if( nLen )
3398  {
3399  if( nAttrStart >= nEnd || nBegin >= *pEndIdx )
3400  continue;
3401  }
3402  else if( nBegin != nAttrStart || ( nAttrStart != *pEndIdx && nBegin ))
3403  {
3404  if( nAttrStart >= nBegin )
3405  continue;
3406  if( pHt->DontExpand() ? nBegin >= *pEndIdx : nBegin > *pEndIdx)
3407  continue;
3408  }
3409  const SfxPoolItem* pItem = CharFormat::GetItem( *pHt, nWhichId );
3410  const LanguageType nLng = static_cast<const SvxLanguageItem*>(pItem)->GetLanguage();
3411 
3412  // does the attribute completely cover the range?
3413  if( nAttrStart <= nBegin && nEnd <= *pEndIdx )
3414  nRet = nLng;
3415  else if( LANGUAGE_DONTKNOW == nRet )
3416  nRet = nLng; // partial overlap, the first one wins
3417  }
3418  }
3419  }
3420  if( LANGUAGE_DONTKNOW == nRet )
3421  {
3422  nRet = static_cast<const SvxLanguageItem&>(GetSwAttrSet().Get( nWhichId )).GetLanguage();
3423  if( LANGUAGE_DONTKNOW == nRet )
3424  nRet = GetAppLanguage();
3425  }
3426  return nRet;
3427 }
3428 
3430 {
3432  switch ( rAttr.Which() )
3433  {
3434  case RES_TXTATR_REFMARK:
3435  case RES_TXTATR_TOXMARK:
3436  case RES_TXTATR_ANNOTATION:
3437  cRet = CH_TXTATR_INWORD;
3438  break;
3439 
3440  case RES_TXTATR_FIELD:
3441  case RES_TXTATR_FLYCNT:
3442  case RES_TXTATR_FTN:
3443  case RES_TXTATR_META:
3444  case RES_TXTATR_METAFIELD:
3445  {
3446  cRet = CH_TXTATR_BREAKWORD;
3447  }
3448  break;
3449 
3450  default:
3451  assert(!"GetCharOfTextAttr: unknown attr");
3452  break;
3453  }
3454  return cRet;
3455 }
3456 
3457 /* 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:512
WhichRangesContainer const aCharAutoFormatSetRange(svl::Items< RES_CHRATR_BEGIN, RES_CHRATR_END-1, RES_TXTATR_UNKNOWN_CONTAINER, RES_TXTATR_UNKNOWN_CONTAINER, RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1 >)
void BuildPortions(SwTextNode &rNode, SwTextAttr &rNewHint, const SetAttrMode nMode)
Definition: thints.cxx:623
static SwTextMeta * CreateTextMeta(::sw::MetaFieldManager &i_rTargetDocManager, SwTextNode *const i_pTargetTextNode, SwFormatMeta &i_rAttr, sal_Int32 const i_nStart, sal_Int32 const i_nEnd, bool const i_bIsCopy)
Definition: txtatr2.cxx:267
void DeleteAtPos(size_t nPos)
Definition: thints.cxx:3259
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:173
void SetDontExpand(bool bDontExpand)
Definition: txatbase.hxx:175
The shared part of a user field.
Definition: usrfld.hxx:34
void DelSoftHyph(const sal_Int32 nStart, const sal_Int32 nEnd)
Definition: thints.cxx:1796
SwTextNode const & GetAttrMerged(SfxItemSet &rFormatSet, SwTextNode const &rNode, SwRootFrame const *pLayout)
Definition: txtfrm.cxx:371
static bool isOverlap(const sal_Int32 nStart1, const sal_Int32 nEnd1, const sal_Int32 nStart2, const sal_Int32 nEnd2)
Definition: thints.cxx:136
bool TryInsertNesting(SwTextNode &rNode, SwTextAttrNesting &rNewHint)
The following hints correspond to well-formed XML elements in ODF: RES_TXTATR_INETFMT, RES_TXTATR_CJK_RUBY, RES_TXTATR_META, RES_TXTATR_METAFIELD.
Definition: thints.cxx:367
const SwField * GetField() const
Definition: fmtfld.hxx: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:674
The inserted item is a copy – intended for use in ndtxt.cxx.
bool isCHRATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:472
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
static constexpr auto Items
SwDocShell * GetDocShell()
Definition: doc.hxx:1352
virtual bool SetAttr(const SfxPoolItem &) override
overriding to handle change of certain paragraph attributes
Definition: ndtxt.cxx:4882
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:223
bool isTXTATR_WITHEND(const sal_uInt16 nWhich)
Definition: hintids.hxx:476
bool isTXTATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:484
constexpr TypedWhichId< SwFormatMeta > RES_TXTATR_META(48)
void TriggerNodeUpdate(const sw::LegacyModifyHint &)
for hanging TextFormatCollections somewhere else (Outline-Numbering!)
Definition: ndtxt.cxx:5254
virtual SfxPoolItem * Clone(SfxItemPool *pPool=nullptr) const =0
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 MergeResult lcl_Compare_Attributes(int i, int j, const std::pair< PortionMap::iterator, PortionMap::iterator > &aRange1, const std::pair< PortionMap::iterator, PortionMap::iterator > &aRange2, std::unordered_map< int, bool > &RsidOnlyAutoFormatFlagMap)
Definition: thints.cxx:2783
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:1595
Pos1 completely contained in Pos2.
sal_Int64 n
const WhichRangesContainer & GetRanges() const
Definition: doc.hxx:188
sal_uInt16 ClearItemsFromAttrSet(const std::vector< sal_uInt16 > &rWhichIds)
There some functions that like to remove items from the internal SwAttrSet (handle): ...
Definition: node.cxx:1765
void InvalidateItem(sal_uInt16 nWhich)
void SetFormatIgnoreStart(bool bFlag)
Definition: txatbase.hxx:108
constexpr TypedWhichId< SwFormatFlyCnt > RES_TXTATR_FLYCNT(57)
sal_uInt16 FirstWhich()
constexpr TypedWhichId< SvxUnderlineItem > RES_CHRATR_UNDERLINE(14)
bool HasDummyChar() const
Definition: txatbase.hxx:105
void TryDeleteSwpHints()
Definition: ndtxt.hxx:836
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:4397
std::vector< SwTextAttrNesting * > NestList_t
Definition: thints.cxx:255
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:144
const SwTOXType * GetTOXType() const
Definition: tox.hxx:588
void SetFormatIgnoreEnd(bool bFlag)
Definition: txatbase.hxx:109
sal_uInt16 Which() const
Definition: txatbase.hxx:114
virtual const sal_Int32 * GetEnd() const override
end position
Definition: txatbase.cxx:77
static bool isNestedAny(const sal_Int32 nStart1, const sal_Int32 nEnd1, const sal_Int32 nStart2, const sal_Int32 nEnd2)
#i106930#: now asymmetric: empty hint1 is not nested, but empty hint2 is
Definition: thints.cxx:146
void SetSeqRefNo(SwSetExpField &rField)
Definition: expfld.cxx:576
bool HasWriterListeners() const
Definition: calbck.hxx: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:4087
sal_uInt16 GetRealScriptOfText(const OUString &rText, sal_Int32 nPos) const
Definition: breakit.cxx:83
bool IsInHeaderFooter(const SwNodeIndex &rIdx) const
Definition: doclay.cxx:1552
#define CHECK
Definition: thints.cxx:82
sal_uInt16 NextWhich()
constexpr sal_uInt16 RES_TXTATR_BEGIN(RES_CHRATR_END)
#define CHAR_SOFTHYPHEN
Definition: swtypes.hxx:164
bool TryInsertHint(SwTextAttr *const pHint, SwTextNode &rNode, const SetAttrMode nMode=SetAttrMode::DEFAULT)
try to insert the hint
Definition: thints.cxx:2930
constexpr TypedWhichId< SvxCaseMapItem > RES_CHRATR_CASEMAP(RES_CHRATR_BEGIN)
Pos1 end touches at Pos2 start.
IDocumentFieldsAccess const & getIDocumentFieldsAccess() const
Definition: doc.cxx:357
#define CH_TXTATR_BREAKWORD
Definition: hintids.hxx:170
static bool TextAttrContains(const sal_Int32 nPos, const SwTextAttrEnd *const pAttr)
Definition: thints.cxx:114
sal_uInt16 sal_Unicode
const SfxPoolItem * NextItem()
void SetTextLeft(const tools::Long nL, const sal_uInt16 nProp=100)
const SwCharFormat * GetDfltCharFormat() const
Definition: doc.hxx:753
#define CH_TXTATR_INWORD
Definition: hintids.hxx:171
SwTableNode * GetTableNode()
Definition: node.hxx:600
void NoteInHistory(SwTextAttr *pAttr, const bool bNew=false)
records a new attribute in m_pHistory.
Definition: thints.cxx:2609
constexpr TypedWhichId< SwDrawFrameFormat > RES_DRAWFRMFMT(159)
static void lcl_MergeAttr_ExpandChrFormat(SfxItemSet &rSet, const SfxPoolItem &rAttr)
Definition: thints.cxx:1982
SwIndex nContent
Definition: pam.hxx:38
SwDoc & GetDoc() const
Definition: tox.hxx:186
SwpHints(const SwTextNode &rParent)
Definition: thints.cxx:90
void SetSortNumber(sal_uInt16 nSortNumber)
Definition: txtatr.hxx:48
A wrapper around SfxPoolItem to store the start position of (usually) a text portion, with an optional end.
Definition: txatbase.hxx:41
virtual void InsDelFieldInFieldLst(bool bIns, const SwTextField &rField)=0
void DestroyAttr(SwTextAttr *pAttr)
Definition: thints.cxx:1163
IDocumentStylePoolAccess const & getIDocumentStylePoolAccess() const
Definition: doc.cxx:426
#define CH_TXT_ATR_INPUTFIELDSTART
Definition: hintids.hxx:174
for Undo, translated to SwInsertFlags::NOHINTEXPAND
SwBreakIt * g_pBreakIt
Definition: breakit.cxx:33
int nCount
const SwTextNode & m_rParent
Definition: ndhints.hxx:70
virtual void DelLayoutFormat(SwFrameFormat *pFormat)=0
static void TextAttrDelete(SwDoc &rDoc, SwTextAttr *const pAttr)
Definition: thints.cxx:104
sal_Int32 GetStart() const
Definition: txatbase.hxx:86
bool IsOverlapAllowedAttr() const
Definition: txatbase.hxx:100
void SetCalcHiddenParaField()
set CalcVisible flags
Definition: ndtxt.hxx:717
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:733
CopyOrNewType
Definition: ndhints.hxx:33
constexpr TypedWhichId< SwFormatINetFormat > RES_TXTATR_INETFMT(51)
constexpr TypedWhichId< SvxWeightItem > RES_CHRATR_WEIGHT(15)
sal_Unicode GetCharOfTextAttr(const SwTextAttr &rAttr)
Definition: thints.cxx:3429
const_iterator upper_bound(const Value &x) const
virtual SwFieldType * ChgTyp(SwFieldType *)
Set new type (used for copying among documents).
Definition: fldbas.cxx:383
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:136
const SwAttrSet * GetpSwAttrSet() const
Definition: node.hxx:450
OUString GetFieldContent() const
Definition: atrfld.cxx:675
constexpr TypedWhichId< SwFormatField > RES_TXTATR_FIELD(RES_TXTATR_NOEND_BEGIN)
bool IsCharFormatAttr() const
Definition: txatbase.hxx:99
sal_uInt16 ClearItem(sal_uInt16 nWhich=0)
void RstTextAttr(const SwIndex &rIdx, const sal_Int32 nLen, const sal_uInt16 nWhich=0, const SfxItemSet *pSet=nullptr, const bool bInclRefToxMark=false, const bool bExactRange=false)
delete all attributes.
Definition: txtedt.cxx:352
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:2625
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:2055
WhichRangesContainer const aCharFormatSetRange(svl::Items< RES_CHRATR_BEGIN, RES_CHRATR_END-1, RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1 >)
void Delete(SwTextAttr const *pTextHt)
Delete the given Hint. The Hint must actually be in the array!
Definition: thints.cxx:3319
Count
SetAttrMode
Definition: swtypes.hxx:133
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:1733
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:1407
void RegisterToTOXType(SwTOXType &rMark)
Definition: tox.cxx:137
void SetTextFirstLineOffset(const short nF, const sal_uInt16 nProp=100)
Force hint expand (only matters for hints with CH_TXTATR).
Internet normal.
Definition: poolfmt.hxx:120
Marks a character position inside a document model node.
Definition: index.hxx:33
const SfxItemSet * GetItemSet(const SfxPoolItem &rAttr)
Returns the item set associated with a character/inet/auto style.
Definition: atrstck.cxx:132
float u
SW_DLLPUBLIC void ResortStartMap() const
Definition: ndhints.cxx: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:2388
SwNumRule * GetNumRule(bool bInParent=true) const
Returns numbering rule of this text node.
Definition: ndtxt.cxx:2798
const OUString & GetTypeName() const
Definition: tox.hxx:706
#define LANGUAGE_DONTKNOW
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
Definition: format.cxx:388
Marks a node in the document model.
Definition: ndindex.hxx:31
SwNodes & GetNodes()
Node is in which nodes-array/doc?
Definition: node.hxx:694
T static_txtattr_cast(S *s)
Definition: txatbase.hxx:241
bool HasSwAttrSet() const
Definition: node.hxx:451
std::vector< SwTextAttr * > m_HintsByStart
Definition: ndhints.hxx:76
const SwDoc * GetDoc() const
The document is set in SwAttrPool now, therefore you always can access it.
Definition: format.hxx:123
int FieldCanHideParaWeight(SwFieldIds eFieldId) const
Definition: doc.cxx:1333
SwStartNode * GetStartNode()
Definition: node.hxx:592
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:954
void RegisterToFieldType(SwFieldType &)
Definition: atrfld.cxx:170
IDocumentLayoutAccess const & getIDocumentLayoutAccess() const
Definition: doc.cxx:405
void ChgTextNode(SwTextNode *pNew)
Definition: txtfld.hxx:58
SwFieldType * GetTyp() const
Definition: fldbas.hxx: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 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:2653
OUString InsertText(const OUString &rStr, const SwIndex &rIdx, const SwInsertFlags nMode=SwInsertFlags::DEFAULT)
insert text content
Definition: ndtxt.cxx:2268
void ChangeNodeIndex(sal_uLong nNew)
Definition: rolbck.hxx:436
void ClearSwpHintsArr(bool bDelFields)
Definition: thints.cxx:3327
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:1433
#define CH_TXT_ATR_INPUTFIELDEND
Definition: hintids.hxx:175
void SetHiddenByParaField(const bool bNew) const
Definition: ndhints.hxx:111
const SwGetSetExpType GSE_SEQ
Sequence.
Definition: fldbas.hxx:204
SwCharFormat * GetCharFormat() const
Definition: fchrfmt.hxx:70
constexpr sal_uInt16 RES_TXTATR_END(RES_TXTATR_NOEND_END)
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:67
constexpr sal_uInt16 RES_CHRATR_BEGIN(HINT_BEGIN)
SAL_DLLPRIVATE void InitINetFormat(SwTextNode &rNode)
Definition: thints.cxx:211
std::deque< AttacherIndex_Impl > aIndex
constexpr TypedWhichId< SvxWeightItem > RES_CHRATR_CJK_WEIGHT(26)
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
void InsertNesting(SwTextAttrNesting &rNewHint)
Insert nesting hint into the hints array.
Definition: thints.cxx:293
#define SAL_WARN_IF(condition, area, stream)
Pos1 overlaps Pos2 at the end.
static bool IsIgnoredCharFormatForNumbering(const sal_uInt16 nWhich, bool bIsCharStyle=false)
In MS Word, the font underline setting of the paragraph end position won't affect the formatting of n...
Definition: thints.cxx: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:963
LanguageType GetAppLanguage()
Definition: init.cxx:722
SwNodes & GetNodes()
Definition: doc.hxx:409
constexpr TypedWhichId< SvxPostureItem > RES_CHRATR_CTL_POSTURE(30)
const T & Put(std::unique_ptr< T > xItem, sal_uInt16 nWhich=0)
Split_t
Definition: thints.cxx:181
static void lcl_MergeListLevelIndentAsLRSpaceItem(const SwTextNode &rTextNode, SfxItemSet &rSet)
Definition: thints.cxx:2034
LanguageType GetLang(const sal_Int32 nBegin, const sal_Int32 nLen=0, sal_uInt16 nScript=0) const
Definition: thints.cxx:3364
static void Destroy(SwTextAttr *pToDestroy, SfxItemPool &rPool)
destroy instance
Definition: txatbase.cxx:58
bool FieldHidesPara(const SwField &rField) const
Definition: doc.cxx:1348
const sal_Int32 * End() const
Definition: txatbase.hxx:152
::sw::MetaFieldManager & GetMetaFieldManager()
Definition: doc.cxx:128
static const size_t MAX_HINTS
Definition: ndhints.hxx:74
static void lcl_MergeAttr(SfxItemSet &rSet, const SfxPoolItem &rAttr)
Definition: thints.cxx:1960
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:2572
constexpr TypedWhichId< SwFormatField > RES_TXTATR_INPUTFIELD(55)
constexpr sal_uInt16 RES_CHRATR_END(46)
TOXTypes GetType() const
Definition: tox.hxx:709
const SwAttrSet & GetSwAttrSet() const
Does node has already its own auto-attributes? Access to SwAttrSet.
Definition: node.hxx:715
virtual void CallSwClientNotify(const SfxHint &rHint) const override
Definition: calbck.cxx:326
bool InsertHint(SwTextAttr *const pAttr, const SetAttrMode nMode=SetAttrMode::DEFAULT)
Insert pAttr into hints array.
Definition: thints.cxx: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
bool IsItemIncluded(const sal_uInt16 nWhich, const SwTextAttr *pAttr)
Checks if item is included in character/inet/auto style.
Definition: atrstck.cxx:176
bool IsInDtor() const
Definition: doc.hxx:404
bool DontExpand() const
Definition: txatbase.hxx:96
SwFootnoteIdxs & GetFootnoteIdxs()
Definition: doc.hxx:634
SwFrameFormat * GetFrameFormat() const
Definition: fmtflcnt.hxx:45
Reference< XModel > xModel
bool IsDocNodes() const
Is the NodesArray the regular one of Doc? (and not the UndoNds, ...) Implementation in doc...
Definition: nodes.cxx:2329
#define INVALID_POOL_ITEM
void CalcFlags()
Definition: thints.cxx:2543
SwFormatColl * GetFormatColl() const
Definition: node.hxx:454
const SwFormatAutoFormat & GetAutoFormat() const
Definition: txatbase.hxx:189
std::unique_ptr< SwpHints > m_pSwpHints
May be 0.
Definition: ndtxt.hxx:93
bool HasMergedParas() const
Definition: rootfrm.hxx:426
Pos1 behind Pos2.
constexpr TypedWhichId< SwFormatRefMark > RES_TXTATR_REFMARK(RES_TXTATR_WITHEND_BEGIN)
bool m_bDDEFields
the TextNode has DDE fields
Definition: ndhints.hxx:90
void FormatToTextAttr(SwTextNode *pNd)
Convey attributes of an AttrSet (AutoFormat) to SwpHintsArray.
Definition: thints.cxx:2454
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)
static void lcl_CheckSortNumber(const SwpHints &rHints, SwTextCharFormat &rNewCharFormat)
Definition: thints.cxx:2893
XSLTFilter & m_rParent
bool IsCharBackground2Shading() const
IStyleAccess & GetIStyleAccess()
Definition: doc.hxx:757
tools::Long GetIndentAt() const
virtual bool IsNewFieldLst() const =0
SwFieldIds Which() const
Definition: fldbas.hxx: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:2108
const sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:57
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:1161
Pos1 overlaps Pos2 at the beginning.
SwTextAttr * Cut(const size_t nPosInStart)
Definition: ndhints.hxx:185
static bool isSplittable(const sal_uInt16 nWhich)
Definition: thints.cxx:168
constexpr TypedWhichId< SvXMLAttrContainerItem > RES_TXTATR_UNKNOWN_CONTAINER(54)
sal_uInt16 nPos
bool IsInXMLImport() const
Definition: doc.hxx:970
const SfxPoolItem * GetCurItem() const
constexpr TypedWhichId< SwFormatAnchor > RES_ANCHOR(104)
const SwAttrPool & GetAttrPool() const
Definition: doc.hxx:1319
SwComparePosition
Definition: pam.hxx:64
bool HasHints() const
Definition: ndtxt.hxx:225
size_type erase(const Value &x)
SwpHints & GetOrCreateSwpHints()
Definition: ndtxt.hxx:827
MergeResult
Definition: thints.cxx:2616
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