1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <DocumentContentOperationsManager.hxx>
24 #include <hintids.hxx>
25 #include <editeng/rsiditem.hxx>
26 #include <editeng/nhypitem.hxx>
27 #include <osl/diagnose.h>
28 #include <svl/whiter.hxx>
29 #include <svl/itemiter.hxx>
30 #include <editeng/charhiddenitem.hxx>
31 #include <editeng/langitem.hxx>
32 #include <editeng/lrspitem.hxx>
33 #include <txtinet.hxx>
34 #include <txtflcnt.hxx>
36 #include <fmtrfmrk.hxx>
37 #include <fmtanchr.hxx>
38 #include <fmtinfmt.hxx>
40 #include <fchrfmt.hxx>
41 #include <fmtautofmt.hxx>
42 #include <fmtflcnt.hxx>
44 #include <txttxmrk.hxx>
45 #include <txtrfmrk.hxx>
47 #include <textlinebreak.hxx>
49 #include <txtannotationfld.hxx>
50 #include <charfmt.hxx>
53 #include <fmtruby.hxx>
54 #include <fmtmeta.hxx>
55 #include <formatcontentcontrol.hxx>
56 #include <formatflysplit.hxx>
57 #include <textcontentcontrol.hxx>
58 #include <breakit.hxx>
60 #include <IDocumentUndoRedo.hxx>
61 #include <IDocumentFieldsAccess.hxx>
62 #include <IDocumentLayoutAccess.hxx>
63 #include <IDocumentStylePoolAccess.hxx>
68 #include <rootfrm.hxx>
71 #include <docufld.hxx>
74 #include <poolfmt.hxx>
75 #include <istyleaccess.hxx>
81 #include <rdfhelper.hxx>
83 #include <unotxdoc.hxx>
85 #include <officecfg/Office/Common.hxx>
88 #define CHECK Check(true);
89 #define CHECK_NOTMERGED Check(false);
91 #define CHECK_NOTMERGED
94 SwpHints::SwpHints(const SwTextNode
& rParent
)
97 , m_bInSplitNode(false)
98 , m_bCalcHiddenParaField(false)
99 , m_bHiddenByParaField(false)
101 , m_bDDEFields(false)
105 static void TextAttrDelete( SwTextAttr
* const pAttr
)
107 if (RES_TXTATR_META
== pAttr
->Which() ||
108 RES_TXTATR_METAFIELD
== pAttr
->Which())
110 static_txtattr_cast
<SwTextMeta
*>(pAttr
)->ChgTextNode(nullptr); // prevents ASSERT
112 else if (pAttr
->Which() == RES_TXTATR_CONTENTCONTROL
)
114 static_txtattr_cast
<SwTextContentControl
*>(pAttr
)->ChgTextNode(nullptr);
116 SwTextAttr::Destroy( pAttr
);
119 static bool TextAttrContains(const sal_Int32 nPos
, const SwTextAttrEnd
* const pAttr
)
121 return (pAttr
->GetStart() < nPos
) && (nPos
< *pAttr
->End());
126 // |---| => valid: b before a
127 // |-----| => valid: start == end; b before a
128 // |---------| => invalid: overlap (1)
129 // |-----------| => valid: same end; b around a
130 // |-----------------| => valid: b around a
131 // |---| => valid; same start; b within a
132 // |-----| => valid; same start and end; b around or within a?
133 // |-----------| => valid: same start: b around a
134 // |-| => valid: b within a
135 // |---| => valid: same end; b within a
136 // |---------| => invalid: overlap (2)
137 // |-----| => valid: end == start; b after a
138 // |---| => valid: b after a
139 // ===> 2 invalid overlap cases
141 bool isOverlap(const sal_Int32 nStart1
, const sal_Int32 nEnd1
,
142 const sal_Int32 nStart2
, const sal_Int32 nEnd2
)
145 ((nStart1
> nStart2
) && (nStart1
< nEnd2
) && (nEnd1
> nEnd2
)) // (1)
146 || ((nStart1
< nStart2
) && (nStart2
< nEnd1
) && (nEnd1
< nEnd2
)); // (2)
149 /// #i106930#: now asymmetric: empty hint1 is _not_ nested, but empty hint2 is
151 bool isNestedAny(const sal_Int32 nStart1
, const sal_Int32 nEnd1
,
152 const sal_Int32 nStart2
, const sal_Int32 nEnd2
)
154 return ((nStart1
== nStart2
) || (nEnd1
== nEnd2
))
155 // same start/end: nested except if hint1 empty and hint2 not empty
156 ? (nStart1
!= nEnd1
) || (nStart2
== nEnd2
)
157 : ((nStart1
< nStart2
) ? (nEnd1
>= nEnd2
) : (nEnd1
<= nEnd2
));
161 bool isSelfNestable(const sal_uInt16 nWhich
)
163 if ((RES_TXTATR_INETFMT
== nWhich
) ||
164 (RES_TXTATR_CJK_RUBY
== nWhich
) ||
165 (RES_TXTATR_INPUTFIELD
== nWhich
))
167 assert((RES_TXTATR_META
== nWhich
) ||
168 (RES_TXTATR_METAFIELD
== nWhich
) ||
169 (RES_TXTATR_CONTENTCONTROL
== nWhich
));
174 bool isSplittable(const sal_uInt16 nWhich
)
176 if ((RES_TXTATR_INETFMT
== nWhich
) ||
177 (RES_TXTATR_CJK_RUBY
== nWhich
))
179 assert((RES_TXTATR_META
== nWhich
) ||
180 (RES_TXTATR_METAFIELD
== nWhich
) ||
181 (RES_TXTATR_INPUTFIELD
== nWhich
) ||
182 (RES_TXTATR_CONTENTCONTROL
== nWhich
));
188 enum Split_t
{ FAIL
, SPLIT_NEW
, SPLIT_OTHER
};
193 Calculate splitting policy for overlapping hints, based on what kind of
194 hint is inserted, and what kind of existing hint overlaps.
197 splitPolicy(const sal_uInt16 nWhichNew
, const sal_uInt16 nWhichOther
)
199 if (!isSplittable(nWhichOther
))
201 if (!isSplittable(nWhichNew
))
208 if ( RES_TXTATR_INPUTFIELD
== nWhichNew
)
210 else if ( (RES_TXTATR_INETFMT
== nWhichNew
) &&
211 (RES_TXTATR_CJK_RUBY
== nWhichOther
) )
218 void SwTextINetFormat::InitINetFormat(SwTextNode
& rNode
)
221 SwCharFormat
* const pFormat(
222 rNode
.GetDoc().getIDocumentStylePoolAccess().GetCharFormatFromPool(RES_POOLCHR_INET_NORMAL
) );
226 void SwTextRuby::InitRuby(SwTextNode
& rNode
)
229 SwCharFormat
* const pFormat(
230 rNode
.GetDoc().getIDocumentStylePoolAccess().GetCharFormatFromPool(RES_POOLCHR_RUBYTEXT
) );
235 Create a new nesting text hint.
237 static SwTextAttrNesting
*
238 MakeTextAttrNesting(SwTextNode
& rNode
, SwTextAttrNesting
& rNesting
,
239 const sal_Int32 nStart
, const sal_Int32 nEnd
)
241 SwTextAttr
* const pNew( MakeTextAttr(
242 rNode
.GetDoc(), rNesting
.GetAttr(), nStart
, nEnd
) );
243 switch (pNew
->Which())
245 case RES_TXTATR_INETFMT
:
247 static_txtattr_cast
<SwTextINetFormat
*>(pNew
)->InitINetFormat(rNode
);
250 case RES_TXTATR_CJK_RUBY
:
252 static_txtattr_cast
<SwTextRuby
*>(pNew
)->InitRuby(rNode
);
256 assert(!"MakeTextAttrNesting: what the hell is that?");
259 return static_txtattr_cast
<SwTextAttrNesting
*>(pNew
);
262 typedef std::vector
<SwTextAttrNesting
*> NestList_t
;
264 static NestList_t::iterator
265 lcl_DoSplitImpl(NestList_t
& rSplits
, SwTextNode
& rNode
,
266 NestList_t::iterator
const iter
, sal_Int32
const nSplitPos
,
267 bool const bSplitAtStart
, bool const bOtherDummy
)
269 const sal_Int32
nStartPos( // skip other's dummy character!
270 (bSplitAtStart
&& bOtherDummy
) ? nSplitPos
+ 1 : nSplitPos
);
271 SwTextAttrNesting
* const pNew( MakeTextAttrNesting(
272 rNode
, **iter
, nStartPos
, *(*iter
)->GetEnd() ) );
273 (*iter
)->SetEnd(nSplitPos
);
274 return rSplits
.insert(iter
+ 1, pNew
);
278 lcl_DoSplitNew(NestList_t
& rSplits
, SwTextNode
& rNode
,
279 const sal_Int32 nNewStart
,
280 const sal_Int32 nOtherStart
, const sal_Int32 nOtherEnd
, bool bOtherDummy
)
282 const bool bSplitAtStart(nNewStart
< nOtherStart
);
283 const sal_Int32
nSplitPos( bSplitAtStart
? nOtherStart
: nOtherEnd
);
284 // first find the portion that is split (not necessarily the last one!)
285 NestList_t::iterator
const iter(
286 std::find_if( rSplits
.begin(), rSplits
.end(),
287 [nSplitPos
](SwTextAttrEnd
* const pAttr
) {
288 return TextAttrContains(nSplitPos
, pAttr
);
290 if (iter
!= rSplits
.end()) // already split here?
292 lcl_DoSplitImpl(rSplits
, rNode
, iter
, nSplitPos
, bSplitAtStart
, bOtherDummy
);
297 Insert nesting hint into the hints array. Also calls NoteInHistory.
298 @param rNewHint the hint to be inserted (must not overlap existing!)
300 void SwpHints::InsertNesting(SwTextAttrNesting
& rNewHint
)
303 NoteInHistory( & rNewHint
, true );
308 The following hints correspond to well-formed XML elements in ODF:
309 RES_TXTATR_INETFMT, RES_TXTATR_CJK_RUBY, RES_TXTATR_META, RES_TXTATR_METAFIELD,
310 RES_TXTATR_CONTENTCONTROL
312 The writer core must ensure that these do not overlap; if they did,
313 the document would not be storable as ODF.
315 Also, a Hyperlink must not be nested within another Hyperlink,
316 and a Ruby must not be nested within another Ruby.
318 The ODF export in xmloff will only put a hyperlink into a ruby, never a ruby
321 Unfortunately the UNO API for Hyperlink and Ruby consists of the properties
322 Hyperlink* and Ruby* of the css.text.CharacterProperties service. In other
323 words, they are treated as formatting attributes, not as content entities.
324 Furthermore, for API users it is not possible to easily test whether a certain
325 range would be overlapping with other nested attributes, and most importantly,
326 <em>which ones</em>, so we can hardly refuse to insert these in cases of
329 It is possible to split Hyperlink and Ruby into multiple portions, such that
330 the result is properly nested.
332 meta and meta-field must not be split, because they have xml:id.
334 content controls should not split, either.
336 These constraints result in the following design:
340 inserts n attributes split at RES_TXTATR_CJK_RUBY, RES_TXTATR_META,
342 may replace existing RES_TXTATR_INETFMT at overlap
345 inserts n attributes split at RES_TXTATR_META, RES_TXTATR_METAFIELD
346 may replace existing RES_TXTATR_CJK_RUBY at overlap
347 may split existing overlapping RES_TXTATR_INETFMT
349 may fail if overlapping existing RES_TXTATR_META/RES_TXTATR_METAFIELD
350 may split existing overlapping RES_TXTATR_INETFMT or RES_TXTATR_CJK_RUBY
352 RES_TXTATR_METAFIELD:
353 may fail if overlapping existing RES_TXTATR_META/RES_TXTATR_METAFIELD
354 may split existing overlapping RES_TXTATR_INETFMT or RES_TXTATR_CJK_RUBY
357 The nesting is expressed by the position of the hints.
358 RES_TXTATR_META and RES_TXTATR_METAFIELD have a CH_TXTATR, and there can
359 only be one such hint starting and ending at a given position.
360 Only RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY lack a CH_TXTATR.
361 The interpretation given is that RES_TXTATR_CJK_RUBY is always around
362 a RES_TXTATR_INETFMT at the same start and end position (which corresponds
364 Both of these are always around a nesting hint with CH_TXTATR at the same
365 start and end position (if they should be inside, then the start should be
366 after the CH_TXTATR).
367 It would probably be a bad idea to add another nesting hint without
368 CH_TXTATR; on the other hand, it would be difficult adding a CH_TXTATR to
369 RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY, due to the overwriting and
370 splitting of existing hints that is necessary for backward compatibility.
372 @param rNode the text node
373 @param rHint the hint to be inserted
374 @returns true iff hint was successfully inserted
377 SwpHints::TryInsertNesting( SwTextNode
& rNode
, SwTextAttrNesting
& rNewHint
)
379 // INVARIANT: the nestable hints in the array are properly nested
380 const sal_uInt16
nNewWhich( rNewHint
.Which() );
381 const sal_Int32
nNewStart( rNewHint
.GetStart() );
382 const sal_Int32
nNewEnd ( *rNewHint
.GetEnd() );
383 const bool bNewSelfNestable( isSelfNestable(nNewWhich
) );
385 assert( (RES_TXTATR_INETFMT
== nNewWhich
) ||
386 (RES_TXTATR_CJK_RUBY
== nNewWhich
) ||
387 (RES_TXTATR_META
== nNewWhich
) ||
388 (RES_TXTATR_METAFIELD
== nNewWhich
) ||
389 (RES_TXTATR_CONTENTCONTROL
== nNewWhich
) ||
390 (RES_TXTATR_INPUTFIELD
== nNewWhich
));
392 NestList_t OverlappingExisting
; // existing hints to be split
393 NestList_t OverwrittenExisting
; // existing hints to be replaced
394 NestList_t SplitNew
; // new hints to be inserted
396 SplitNew
.push_back(& rNewHint
);
398 // pass 1: split the inserted hint into fragments if necessary
399 for ( size_t i
= 0; i
< Count(); ++i
)
401 SwTextAttr
* const pOther
= GetSortedByEnd(i
);
403 if (pOther
->IsNesting())
405 const sal_uInt16
nOtherWhich( pOther
->Which() );
406 const sal_Int32
nOtherStart( pOther
->GetStart() );
407 const sal_Int32
nOtherEnd ( *pOther
->GetEnd() );
408 if (isOverlap(nNewStart
, nNewEnd
, nOtherStart
, nOtherEnd
))
410 switch (splitPolicy(nNewWhich
, nOtherWhich
))
413 SAL_INFO("sw.core", "cannot insert hint: overlap");
414 for (const auto& aSplit
: SplitNew
)
415 TextAttrDelete(aSplit
);
418 lcl_DoSplitNew(SplitNew
, rNode
, nNewStart
,
419 nOtherStart
, nOtherEnd
, pOther
->HasDummyChar());
422 OverlappingExisting
.push_back(
423 static_txtattr_cast
<SwTextAttrNesting
*>(pOther
));
426 assert(!"bad code monkey");
430 else if (isNestedAny(nNewStart
, nNewEnd
, nOtherStart
, nOtherEnd
))
432 if (!bNewSelfNestable
&& (nNewWhich
== nOtherWhich
))
434 // ruby and hyperlink: if there is nesting, _overwrite_
435 OverwrittenExisting
.push_back(
436 static_txtattr_cast
<SwTextAttrNesting
*>(pOther
));
438 else if ((nNewStart
== nOtherStart
) && pOther
->HasDummyChar())
440 if (rNewHint
.HasDummyChar())
442 assert(!"ERROR: inserting duplicate CH_TXTATR hint");
444 } else if (nNewEnd
< nOtherEnd
) {
445 // other has dummy char, new is inside other, but
446 // new contains the other's dummy char?
447 // should be corrected because it may lead to problems
448 // in SwXMeta::createEnumeration
449 // SplitNew is sorted, so this is the first split
450 assert(SplitNew
.front()->GetStart() == nNewStart
);
451 SplitNew
.front()->SetStart(nNewStart
+ 1);
458 // pass 1b: tragically need to check for fieldmarks here too
459 for (auto iter
= SplitNew
.begin(); iter
!= SplitNew
.end(); ++iter
)
461 SwPaM
const temp(rNode
, (*iter
)->GetStart(), rNode
, *(*iter
)->GetEnd());
462 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
463 sw::CalcBreaks(Breaks
, temp
, true);
466 if (!isSplittable(nNewWhich
))
468 SAL_INFO("sw.core", "cannot insert hint: fieldmark overlap");
469 assert(SplitNew
.size() == 1);
470 TextAttrDelete(&rNewHint
);
475 for (auto const& rPos
: Breaks
)
477 assert(rPos
.first
== rNode
.GetIndex());
478 iter
= lcl_DoSplitImpl(SplitNew
, rNode
, iter
,
479 rPos
.second
, true, true);
485 assert((isSplittable(nNewWhich
) || SplitNew
.size() == 1) &&
486 "splitting the unsplittable ???");
488 // pass 2: split existing hints that overlap/nest with new hint
489 // do not iterate over hints array, but over remembered set of overlapping
490 // hints, to keep things simple w.r.t. insertion/removal
491 // N.B: if there is a hint that splits the inserted hint, then
492 // that hint would also have already split any hint in OverlappingExisting
493 // so any hint in OverlappingExisting can be split at most by one hint
494 // in SplitNew, or even not at all (this is not true for existing hints
495 // that go _around_ new hint, which is the reason d'^etre for pass 4)
496 for (auto& rpOther
: OverlappingExisting
)
498 const sal_Int32
nOtherStart( rpOther
->GetStart() );
499 const sal_Int32
nOtherEnd ( *rpOther
->GetEnd() );
501 for (const auto& rpNew
: SplitNew
)
503 const sal_Int32
nSplitNewStart( rpNew
->GetStart() );
504 const sal_Int32
nSplitNewEnd ( *rpNew
->GetEnd() );
505 // 4 cases: within, around, overlap l, overlap r, (OTHER: no action)
506 const bool bRemoveOverlap(
507 !bNewSelfNestable
&& (nNewWhich
== rpOther
->Which()) );
509 switch (ComparePosition(nSplitNewStart
, nSplitNewEnd
,
510 nOtherStart
, nOtherEnd
))
512 case SwComparePosition::Inside
:
514 assert(!bRemoveOverlap
&&
515 "this one should be in OverwrittenExisting?");
518 case SwComparePosition::Outside
:
519 case SwComparePosition::Equal
:
521 assert(!"existing hint inside new hint: why?");
524 case SwComparePosition::OverlapBefore
:
526 Delete( rpOther
); // this also does NoteInHistory!
527 rpOther
->SetStart(nSplitNewEnd
);
528 InsertNesting( *rpOther
);
531 if ( MAX_HINTS
<= Count() )
533 SAL_INFO("sw.core", "hints array full :-(");
536 SwTextAttrNesting
* const pOtherLeft(
537 MakeTextAttrNesting( rNode
, *rpOther
,
538 nOtherStart
, nSplitNewEnd
) );
539 InsertNesting( *pOtherLeft
);
543 case SwComparePosition::OverlapBehind
:
545 Delete( rpOther
); // this also does NoteInHistory!
546 rpOther
->SetEnd(nSplitNewStart
);
547 InsertNesting( *rpOther
);
550 if ( MAX_HINTS
<= Count() )
552 SAL_INFO("sw.core", "hints array full :-(");
555 SwTextAttrNesting
* const pOtherRight(
556 MakeTextAttrNesting( rNode
, *rpOther
,
557 nSplitNewStart
, nOtherEnd
) );
558 InsertNesting( *pOtherRight
);
563 break; // overlap resolved by splitting new: nothing to do
568 if ( MAX_HINTS
<= Count() || MAX_HINTS
- Count() <= SplitNew
.size() )
570 SAL_INFO("sw.core", "hints array full :-(");
574 // pass 3: insert new hints
575 for (const auto& rpHint
: SplitNew
)
577 InsertNesting(*rpHint
);
580 // pass 4: handle overwritten hints
581 // RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY should displace attributes
583 for (auto& rpOther
: OverwrittenExisting
)
585 const sal_Int32
nOtherStart( rpOther
->GetStart() );
586 const sal_Int32
nOtherEnd ( *rpOther
->GetEnd() );
588 // overwritten portion is given by start/end of inserted hint
589 if ((nNewStart
<= nOtherStart
) && (nOtherEnd
<= nNewEnd
))
592 rNode
.DestroyAttr( rpOther
);
596 assert((nOtherStart
< nNewStart
) || (nNewEnd
< nOtherEnd
));
597 // scenario: there is a RUBY, and contained within that a META;
598 // now a RUBY is inserted within the META => the existing RUBY is split:
599 // here it is not possible to simply insert the left/right fragment
600 // of the existing RUBY because they <em>overlap</em> with the META!
601 Delete( rpOther
); // this also does NoteInHistory!
602 if (nNewEnd
< nOtherEnd
)
604 SwTextAttrNesting
* const pOtherRight(
606 rNode
, *rpOther
, nNewEnd
, nOtherEnd
) );
607 bool const bSuccess( TryInsertNesting(rNode
, *pOtherRight
) );
608 SAL_WARN_IF(!bSuccess
, "sw.core", "recursive call 1 failed?");
610 if (nOtherStart
< nNewStart
)
612 rpOther
->SetEnd(nNewStart
);
613 bool const bSuccess( TryInsertNesting(rNode
, *rpOther
) );
614 SAL_WARN_IF(!bSuccess
, "sw.core", "recursive call 2 failed?");
618 rNode
.DestroyAttr(rpOther
);
626 // This function takes care for the following text attribute:
627 // RES_TXTATR_CHARFMT, RES_TXTATR_AUTOFMT
628 // These attributes have to be handled in a special way (Portion building).
630 // The new attribute will be split by any existing RES_TXTATR_AUTOFMT or
631 // RES_TXTATR_CHARFMT. The new attribute itself will
632 // split any existing RES_TXTATR_AUTOFMT or RES_TXTATR_CHARFMT.
634 void SwpHints::BuildPortions( SwTextNode
& rNode
, SwTextAttr
& rNewHint
,
635 const SetAttrMode nMode
)
637 const sal_uInt16 nWhich
= rNewHint
.Which();
639 const sal_Int32 nThisStart
= rNewHint
.GetStart();
640 const sal_Int32 nThisEnd
= *rNewHint
.GetEnd();
641 const bool bNoLengthAttribute
= nThisStart
== nThisEnd
;
643 std::vector
<SwTextAttr
*> aInsDelHints
;
645 assert( RES_TXTATR_CHARFMT
== rNewHint
.Which() ||
646 RES_TXTATR_AUTOFMT
== rNewHint
.Which() );
648 // 2. Find the hints which cover the start and end position
649 // of the new hint. These hints have to be split into two portions:
651 if ( !bNoLengthAttribute
) // nothing to do for no length attributes
653 for ( size_t i
= 0; i
< Count(); ++i
)
655 // we're modifying stuff here which affects the sorting, and we
656 // don't want it changing underneath us
657 SwTextAttr
* pOther
= GetWithoutResorting(i
);
659 if ( RES_TXTATR_CHARFMT
!= pOther
->Which() &&
660 RES_TXTATR_AUTOFMT
!= pOther
->Which() )
663 sal_Int32 nOtherStart
= pOther
->GetStart();
664 const sal_Int32 nOtherEnd
= *pOther
->GetEnd();
666 // Check if start of new attribute overlaps with pOther:
667 // Split pOther if necessary:
668 if ( nOtherStart
< nThisStart
&& nThisStart
< nOtherEnd
)
670 SwTextAttr
* pNewAttr
= MakeTextAttr( rNode
.GetDoc(),
671 pOther
->GetAttr(), nOtherStart
, nThisStart
);
672 if ( RES_TXTATR_CHARFMT
== pOther
->Which() )
674 static_txtattr_cast
<SwTextCharFormat
*>(pNewAttr
)->SetSortNumber(
675 static_txtattr_cast
<SwTextCharFormat
*>(pOther
)->GetSortNumber() );
677 aInsDelHints
.push_back( pNewAttr
);
679 NoteInHistory( pOther
);
680 pOther
->SetStart(nThisStart
);
681 NoteInHistory( pOther
, true );
683 nOtherStart
= nThisStart
;
686 // Check if end of new attribute overlaps with pOther:
687 // Split pOther if necessary:
688 if ( nOtherStart
< nThisEnd
&& nThisEnd
< nOtherEnd
)
690 SwTextAttr
* pNewAttr
= MakeTextAttr( rNode
.GetDoc(),
691 pOther
->GetAttr(), nOtherStart
, nThisEnd
);
692 if ( RES_TXTATR_CHARFMT
== pOther
->Which() )
694 static_txtattr_cast
<SwTextCharFormat
*>(pNewAttr
)->SetSortNumber(
695 static_txtattr_cast
<SwTextCharFormat
*>(pOther
)->GetSortNumber());
697 aInsDelHints
.push_back( pNewAttr
);
699 NoteInHistory( pOther
);
700 pOther
->SetStart(nThisEnd
);
701 NoteInHistory( pOther
, true );
705 // Insert the newly created attributes:
706 for ( const auto& rpHint
: aInsDelHints
)
709 NoteInHistory( rpHint
, true );
714 if( !rNode
.GetDoc().IsInReading() )
715 CHECK_NOTMERGED
; // ignore flags not set properly yet, don't check them
718 // 4. Split rNewHint into 1 ... n new hints:
720 o3tl::sorted_vector
<sal_Int32
> aBounds
;
721 aBounds
.insert( nThisStart
);
722 aBounds
.insert( nThisEnd
);
724 if ( !bNoLengthAttribute
) // nothing to do for no length attributes
726 for ( size_t i
= 0; i
< Count(); ++i
)
728 const SwTextAttr
* pOther
= Get(i
);
730 if ( RES_TXTATR_CHARFMT
!= pOther
->Which() &&
731 RES_TXTATR_AUTOFMT
!= pOther
->Which() )
734 const sal_Int32 nOtherStart
= pOther
->GetStart();
735 const sal_Int32 nOtherEnd
= *pOther
->End();
737 if (nThisStart
<= nOtherStart
&& nOtherStart
<= nThisEnd
)
738 aBounds
.insert( nOtherStart
);
739 if (nThisStart
<= nOtherEnd
&& nOtherEnd
<= nThisEnd
)
740 aBounds
.insert( nOtherEnd
);
744 auto aStartIter
= aBounds
.lower_bound( nThisStart
);
745 auto aEndIter
= aBounds
.upper_bound( nThisEnd
);
746 sal_Int32 nPorStart
= *aStartIter
;
748 bool bDestroyHint
= true;
750 // Insert the 1...n new parts of the new attribute:
752 while ( aStartIter
!= aEndIter
|| bNoLengthAttribute
)
754 OSL_ENSURE( bNoLengthAttribute
|| nPorStart
< *aStartIter
, "AUTOSTYLES: BuildPortion trouble" );
756 const sal_Int32 nPorEnd
= bNoLengthAttribute
? nPorStart
: *aStartIter
;
757 aInsDelHints
.clear();
759 // Get all hints that are in [nPorStart, nPorEnd[:
760 for ( size_t i
= 0; i
< Count(); ++i
)
762 // we get called from TryInsertHint, which changes ordering
763 SwTextAttr
*pOther
= GetWithoutResorting(i
);
765 if ( RES_TXTATR_CHARFMT
!= pOther
->Which() &&
766 RES_TXTATR_AUTOFMT
!= pOther
->Which() )
769 const sal_Int32 nOtherStart
= pOther
->GetStart();
771 if ( nOtherStart
> nPorStart
)
774 if ( pOther
->GetEnd() && *pOther
->GetEnd() == nPorEnd
&& nOtherStart
== nPorStart
)
776 OSL_ENSURE( *pOther
->GetEnd() == nPorEnd
, "AUTOSTYLES: BuildPortion trouble" );
777 aInsDelHints
.push_back( pOther
);
781 SwTextAttr
* pNewAttr
= nullptr;
782 if ( RES_TXTATR_CHARFMT
== nWhich
)
784 // pNewHint can be inserted after calculating the sort value.
785 // This should ensure, that pNewHint comes behind the already present
787 sal_uInt16 nCharStyleCount
= 0;
788 for ( const auto& rpHint
: aInsDelHints
)
790 if ( RES_TXTATR_CHARFMT
== rpHint
->Which() )
793 const SwFormatCharFormat
& rOtherCharFormat
= rpHint
->GetCharFormat();
794 const SwFormatCharFormat
& rThisCharFormat
= rNewHint
.GetCharFormat();
795 const bool bSameCharFormat
= rOtherCharFormat
.GetCharFormat() == rThisCharFormat
.GetCharFormat();
798 // Do not remove existing character format hint during XML import
799 if ( !rNode
.GetDoc().IsInXMLImport() &&
800 ( !( SetAttrMode::DONTREPLACE
& nMode
) ||
801 bNoLengthAttribute
||
806 rNode
.DestroyAttr( rpHint
);
813 // remove all attributes from auto styles, which are explicitly set in
814 // the new character format:
815 OSL_ENSURE( RES_TXTATR_AUTOFMT
== rpHint
->Which(), "AUTOSTYLES - Misc trouble" );
816 SwTextAttr
* pOther
= rpHint
;
817 const std::shared_ptr
<SfxItemSet
> & pOldStyle
= static_cast<const SwFormatAutoFormat
&>(pOther
->GetAttr()).GetStyleHandle();
819 // For each attribute in the automatic style check if it
820 // is also set the new character style:
821 SfxItemSet
aNewSet( *pOldStyle
->GetPool(),
822 aCharAutoFormatSetRange
);
823 SfxItemIter
aItemIter( *pOldStyle
);
824 const SfxPoolItem
* pItem
= aItemIter
.GetCurItem();
827 if ( !CharFormat::IsItemIncluded( pItem
->Which(), &rNewHint
) )
829 aNewSet
.Put( *pItem
);
832 pItem
= aItemIter
.NextItem();
837 rNode
.DestroyAttr( pOther
);
839 // Create new AutoStyle
840 if ( aNewSet
.Count() )
842 pNewAttr
= MakeTextAttr( rNode
.GetDoc(),
843 aNewSet
, nPorStart
, nPorEnd
);
845 NoteInHistory( pNewAttr
, true );
850 // If there is no current hint and start and end of rNewHint
851 // is ok, we do not need to create a new txtattr.
852 if ( nPorStart
== nThisStart
&&
853 nPorEnd
== nThisEnd
&&
856 pNewAttr
= &rNewHint
;
857 bDestroyHint
= false;
861 pNewAttr
= MakeTextAttr( rNode
.GetDoc(), rNewHint
.GetAttr(),
862 nPorStart
, nPorEnd
);
863 static_txtattr_cast
<SwTextCharFormat
*>(pNewAttr
)->SetSortNumber(nCharStyleCount
);
868 // Find the current autostyle. Mix attributes if necessary.
869 SwTextAttr
* pCurrentAutoStyle
= nullptr;
870 SwTextAttr
* pCurrentCharFormat
= nullptr;
871 for ( const auto& rpHint
: aInsDelHints
)
873 if ( RES_TXTATR_AUTOFMT
== rpHint
->Which() )
874 pCurrentAutoStyle
= rpHint
;
875 else if ( RES_TXTATR_CHARFMT
== rpHint
->Which() )
876 pCurrentCharFormat
= rpHint
;
879 std::shared_ptr
<SfxItemSet
> pNewStyle
= static_cast<const SwFormatAutoFormat
&>(rNewHint
.GetAttr()).GetStyleHandle();
880 if ( pCurrentAutoStyle
)
882 const std::shared_ptr
<SfxItemSet
> & pCurrentStyle
= static_cast<const SwFormatAutoFormat
&>(pCurrentAutoStyle
->GetAttr()).GetStyleHandle();
885 SfxItemSet
aNewSet( *pCurrentStyle
);
886 aNewSet
.Put( *pNewStyle
);
888 // #i75750# Remove attributes already set at whole paragraph
889 // #i81764# This should not be applied for no length attributes!!! <--
890 if ( !bNoLengthAttribute
&& rNode
.HasSwAttrSet() && aNewSet
.Count() )
892 const SfxItemSet
& rWholeParaAttrSet(rNode
.GetSwAttrSet());
893 std::vector
<sal_uInt16
> aDeleteWhichIDs
;
895 for (SfxItemIter
aIter(aNewSet
); !aIter
.IsAtEnd(); aIter
.NextItem())
897 const SfxPoolItem
* pGet(nullptr);
898 if (SfxItemState::SET
== rWholeParaAttrSet
.GetItemState(aIter
.GetCurWhich(), false, &pGet
) &&
899 SfxPoolItem::areSame(pGet
, aIter
.GetCurItem()))
901 // Do not clear item if the attribute is set in a character format:
902 if (!pCurrentCharFormat
|| nullptr == CharFormat::GetItem(*pCurrentCharFormat
, aIter
.GetCurWhich()))
903 aDeleteWhichIDs
.push_back(aIter
.GetCurWhich());
907 for (auto nDelWhich
: aDeleteWhichIDs
)
908 aNewSet
.ClearItem(nDelWhich
);
912 Delete( pCurrentAutoStyle
);
913 rNode
.DestroyAttr( pCurrentAutoStyle
);
915 // Create new AutoStyle
916 if ( aNewSet
.Count() )
917 pNewAttr
= MakeTextAttr( rNode
.GetDoc(), aNewSet
,
918 nPorStart
, nPorEnd
);
922 // Remove any attributes which are already set at the whole paragraph:
923 bool bOptimizeAllowed
= true;
925 // #i75750# Remove attributes already set at whole paragraph
926 // #i81764# This should not be applied for no length attributes!!! <--
927 if ( !bNoLengthAttribute
&& rNode
.HasSwAttrSet() && pNewStyle
->Count() )
929 std::unique_ptr
<SfxItemSet
> pNewSet
;
931 SfxItemIter
aIter2( *pNewStyle
);
932 const SfxPoolItem
* pItem
= aIter2
.GetCurItem();
933 const SfxItemSet
& rWholeParaAttrSet
= rNode
.GetSwAttrSet();
937 const SfxPoolItem
* pTmpItem
= nullptr;
938 // here direct SfxPoolItem ptr comp was wrong, found using SfxPoolItem::areSame
939 if ( SfxItemState::SET
== rWholeParaAttrSet
.GetItemState( pItem
->Which(), false, &pTmpItem
) &&
940 SfxPoolItem::areSame(pTmpItem
, pItem
) )
942 // Do not clear item if the attribute is set in a character format:
943 if ( !pCurrentCharFormat
|| nullptr == CharFormat::GetItem( *pCurrentCharFormat
, pItem
->Which() ) )
946 pNewSet
= pNewStyle
->Clone();
947 pNewSet
->ClearItem( pItem
->Which() );
951 while ((pItem
= aIter2
.NextItem()));
955 bOptimizeAllowed
= false;
956 if ( pNewSet
->Count() )
957 pNewStyle
= rNode
.getIDocumentStyleAccess().getAutomaticStyle( *pNewSet
, IStyleAccess::AUTO_STYLE_CHAR
);
963 // Create new AutoStyle
964 // If there is no current hint and start and end of rNewHint
965 // is ok, we do not need to create a new txtattr.
966 if ( bOptimizeAllowed
&&
967 nPorStart
== nThisStart
&&
968 nPorEnd
== nThisEnd
)
970 pNewAttr
= &rNewHint
;
971 bDestroyHint
= false;
973 else if ( pNewStyle
)
975 pNewAttr
= MakeTextAttr( rNode
.GetDoc(), *pNewStyle
,
976 nPorStart
, nPorEnd
);
984 // if ( bDestroyHint )
985 NoteInHistory( pNewAttr
, true );
988 if ( !bNoLengthAttribute
)
990 nPorStart
= *aStartIter
;
998 rNode
.DestroyAttr( &rNewHint
);
1001 SwTextAttr
* MakeRedlineTextAttr( SwDoc
& rDoc
, SfxPoolItem
const & rAttr
)
1003 // this is intended _only_ for special-purpose redline attributes!
1004 switch (rAttr
.Which())
1006 case RES_CHRATR_COLOR
:
1007 case RES_CHRATR_WEIGHT
:
1008 case RES_CHRATR_CJK_WEIGHT
:
1009 case RES_CHRATR_CTL_WEIGHT
:
1010 case RES_CHRATR_POSTURE
:
1011 case RES_CHRATR_CJK_POSTURE
:
1012 case RES_CHRATR_CTL_POSTURE
:
1013 case RES_CHRATR_UNDERLINE
:
1014 case RES_CHRATR_CROSSEDOUT
:
1015 case RES_CHRATR_CASEMAP
:
1016 case RES_CHRATR_BACKGROUND
:
1019 assert(!"unsupported redline attribute");
1023 // create a SfxPoolItemHolder and return it (will move Item to referenced mode)
1024 return new SwTextAttrEnd(SfxPoolItemHolder(rDoc
.GetAttrPool(), &rAttr
), 0, 0);
1027 // create new text attribute
1028 SwTextAttr
* MakeTextAttr(
1031 sal_Int32
const nStt
,
1032 sal_Int32
const nEnd
,
1033 CopyOrNewType
const bIsCopy
,
1034 SwTextNode
*const pTextNode
)
1036 if ( isCHRATR(rAttr
.Which()) )
1038 // Somebody wants to build a SwTextAttr for a character attribute.
1039 // Sorry, this is not allowed any longer.
1040 // You'll get a brand new autostyle attribute:
1041 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_CHRATR_END
> aItemSet( rDoc
.GetAttrPool() );
1042 aItemSet
.Put( rAttr
);
1043 return MakeTextAttr( rDoc
, aItemSet
, nStt
, nEnd
);
1045 else if ( RES_TXTATR_AUTOFMT
== rAttr
.Which() &&
1046 static_cast<const SwFormatAutoFormat
&>(rAttr
).GetStyleHandle()->
1047 GetPool() != &rDoc
.GetAttrPool() )
1049 // If the attribute is an auto-style which refers to a pool that is
1050 // different from rDoc's pool, we have to correct this:
1051 const std::shared_ptr
<SfxItemSet
> & pAutoStyle
= static_cast<const SwFormatAutoFormat
&>(rAttr
).GetStyleHandle();
1052 std::unique_ptr
<const SfxItemSet
> pNewSet(
1053 pAutoStyle
->SfxItemSet::Clone( true, &rDoc
.GetAttrPool() ));
1054 SwTextAttr
* pNew
= MakeTextAttr( rDoc
, *pNewSet
, nStt
, nEnd
);
1058 // create a SfxPoolItemHolder and use it (will move Item to referenced mode)
1059 const SfxPoolItemHolder
aHolder(rDoc
.GetAttrPool(), &rAttr
);
1060 SfxPoolItem
& rNew(const_cast<SfxPoolItem
&>(*aHolder
.getItem()));
1062 SwTextAttr
* pNew
= nullptr;
1063 switch( aHolder
.Which() )
1065 case RES_TXTATR_CHARFMT
:
1067 SwFormatCharFormat
&rFormatCharFormat
= static_cast<SwFormatCharFormat
&>(rNew
);
1068 if( !rFormatCharFormat
.GetCharFormat() )
1070 rFormatCharFormat
.SetCharFormat( rDoc
.GetDfltCharFormat() );
1073 pNew
= new SwTextCharFormat( aHolder
, nStt
, nEnd
);
1076 case RES_TXTATR_INETFMT
:
1077 pNew
= new SwTextINetFormat( aHolder
, nStt
, nEnd
);
1080 case RES_TXTATR_FIELD
:
1081 pNew
= new SwTextField( aHolder
, nStt
,
1082 rDoc
.IsClipBoard() );
1085 case RES_TXTATR_ANNOTATION
:
1087 pNew
= new SwTextAnnotationField( aHolder
, nStt
, rDoc
.IsClipBoard() );
1088 if (bIsCopy
== CopyOrNewType::Copy
)
1090 // On copy of the annotation field do not keep the annotated text range by removing
1091 // the relation to its annotation mark (relation established via annotation field's name).
1092 // If the annotation mark is also copied, the relation and thus the annotated text range will be reestablished,
1093 // when the annotation mark is created and inserted into the document.
1094 auto& pField
= const_cast<SwPostItField
&>(dynamic_cast<const SwPostItField
&>(*(pNew
->GetFormatField().GetField())));
1095 pField
.SetName(OUString());
1096 pField
.SetPostItId();
1101 case RES_TXTATR_INPUTFIELD
:
1102 pNew
= new SwTextInputField( aHolder
, nStt
, nEnd
,
1103 rDoc
.IsClipBoard() );
1106 case RES_TXTATR_FLYCNT
:
1108 // finally, copy the frame format (with content)
1109 pNew
= new SwTextFlyCnt( aHolder
, nStt
);
1110 if ( static_cast<const SwFormatFlyCnt
&>(rAttr
).GetTextFlyCnt() )
1112 // if it has an existing attr then the format must be copied
1113 static_cast<SwTextFlyCnt
*>(pNew
)->CopyFlyFormat( rDoc
);
1117 case RES_TXTATR_FTN
:
1118 pNew
= new SwTextFootnote( aHolder
, nStt
);
1119 // copy note's SeqNo
1120 if( static_cast<SwFormatFootnote
&>(rAttr
).GetTextFootnote() )
1121 static_cast<SwTextFootnote
*>(pNew
)->SetSeqNo( static_cast<SwFormatFootnote
&>(rAttr
).GetTextFootnote()->GetSeqRefNo() );
1123 case RES_TXTATR_REFMARK
:
1125 ? new SwTextRefMark( aHolder
, nStt
)
1126 : new SwTextRefMark( aHolder
, nStt
, &nEnd
);
1128 case RES_TXTATR_TOXMARK
:
1130 SwTOXMark
& rMark
= static_cast<SwTOXMark
&>(rNew
);
1132 // tdf#98868 if the SwTOXType is from a different document that the
1133 // target, re-register the TOXMark against a matching SwTOXType from
1134 // the target document instead
1135 const SwTOXType
* pTOXType
= rMark
.GetTOXType();
1136 if (pTOXType
&& &pTOXType
->GetDoc() != &rDoc
)
1138 SwTOXType
* pToxType
= SwHistorySetTOXMark::GetSwTOXType(rDoc
, pTOXType
->GetType(),
1139 pTOXType
->GetTypeName());
1140 rMark
.RegisterToTOXType(*pToxType
);
1143 pNew
= new SwTextTOXMark(aHolder
, nStt
, &nEnd
);
1146 case RES_TXTATR_CJK_RUBY
:
1147 pNew
= new SwTextRuby( aHolder
, nStt
, nEnd
);
1149 case RES_TXTATR_META
:
1150 case RES_TXTATR_METAFIELD
:
1151 pNew
= SwTextMeta::CreateTextMeta( rDoc
.GetMetaFieldManager(), pTextNode
,
1153 nStt
, nEnd
, bIsCopy
== CopyOrNewType::Copy
);
1155 case RES_TXTATR_LINEBREAK
:
1156 pNew
= new SwTextLineBreak(aHolder
, nStt
);
1158 case RES_TXTATR_CONTENTCONTROL
:
1159 pNew
= SwTextContentControl::CreateTextContentControl(
1163 bIsCopy
== CopyOrNewType::Copy
);
1166 assert(RES_TXTATR_AUTOFMT
== rNew
.Which());
1167 pNew
= new SwTextAttrEnd( aHolder
, nStt
, nEnd
);
1174 SwTextAttr
* MakeTextAttr( SwDoc
& rDoc
, const SfxItemSet
& rSet
,
1175 sal_Int32 nStt
, sal_Int32 nEnd
)
1177 IStyleAccess
& rStyleAccess
= rDoc
.GetIStyleAccess();
1178 const std::shared_ptr
<SfxItemSet
> pAutoStyle
= rStyleAccess
.getAutomaticStyle( rSet
, IStyleAccess::AUTO_STYLE_CHAR
);
1179 SwFormatAutoFormat aNewAutoFormat
;
1180 aNewAutoFormat
.SetStyleHandle( pAutoStyle
);
1181 SwTextAttr
* pNew
= MakeTextAttr( rDoc
, aNewAutoFormat
, nStt
, nEnd
);
1185 // delete the text attribute and unregister its item at the pool
1186 void SwTextNode::DestroyAttr( SwTextAttr
* pAttr
)
1191 // some things need to be done before deleting the formatting attribute
1192 SwDoc
& rDoc
= GetDoc();
1193 switch( pAttr
->Which() )
1195 case RES_TXTATR_FLYCNT
:
1197 SwFrameFormat
* pFormat
= pAttr
->GetFlyCnt().GetFrameFormat();
1198 if( pFormat
) // set to 0 by Undo?
1199 rDoc
.getIDocumentLayoutAccess().DelLayoutFormat( pFormat
);
1203 case RES_CHRATR_HIDDEN
:
1204 SetCalcHiddenCharFlags();
1207 case RES_TXTATR_FTN
:
1208 static_cast<SwTextFootnote
*>(pAttr
)->SetStartNode( nullptr );
1209 static_cast<SwFormatFootnote
&>(pAttr
->GetAttr()).InvalidateFootnote();
1212 case RES_TXTATR_FIELD
:
1213 case RES_TXTATR_ANNOTATION
:
1214 case RES_TXTATR_INPUTFIELD
:
1215 if( !rDoc
.IsInDtor() )
1217 SwTextField
*const pTextField(static_txtattr_cast
<SwTextField
*>(pAttr
));
1218 SwFieldType
* pFieldType
= pAttr
->GetFormatField().GetField()->GetTyp();
1220 if (SwFieldIds::Dde
!= pFieldType
->Which()
1221 && !pTextField
->GetpTextNode())
1223 break; // was not yet inserted
1226 //JP 06-08-95: DDE-fields are an exception
1227 assert(SwFieldIds::Dde
== pFieldType
->Which() ||
1228 this == pTextField
->GetpTextNode());
1230 // certain fields must update the SwDoc's calculation flags
1232 // Certain fields (like HiddenParaField) must trigger recalculation of visible flag
1233 if (GetDoc().FieldCanHideParaWeight(pFieldType
->Which()))
1234 SetCalcHiddenParaField();
1236 switch( pFieldType
->Which() )
1238 case SwFieldIds::HiddenPara
:
1239 case SwFieldIds::DbSetNumber
:
1240 case SwFieldIds::GetExp
:
1241 case SwFieldIds::Database
:
1242 case SwFieldIds::SetExp
:
1243 case SwFieldIds::HiddenText
:
1244 case SwFieldIds::DbNumSet
:
1245 case SwFieldIds::DbNextSet
:
1246 if( !rDoc
.getIDocumentFieldsAccess().IsNewFieldLst() && GetNodes().IsDocNodes() )
1247 rDoc
.getIDocumentFieldsAccess().InsDelFieldInFieldLst(false, *pTextField
);
1249 case SwFieldIds::Dde
:
1250 if (GetNodes().IsDocNodes() && pTextField
->GetpTextNode())
1251 static_cast<SwDDEFieldType
*>(pFieldType
)->DecRefCnt();
1253 case SwFieldIds::Postit
:
1255 const_cast<SwFormatField
&>(pAttr
->GetFormatField()).Broadcast(
1256 SwFormatFieldHint(&pTextField
->GetFormatField(), SwFormatFieldHintWhich::REMOVED
));
1262 static_cast<SwFormatField
&>(pAttr
->GetAttr()).InvalidateField();
1265 case RES_TXTATR_TOXMARK
:
1266 static_cast<SwTOXMark
&>(pAttr
->GetAttr()).InvalidateTOXMark();
1269 case RES_TXTATR_REFMARK
:
1270 static_cast<SwFormatRefMark
&>(pAttr
->GetAttr()).InvalidateRefMark();
1273 case RES_TXTATR_META
:
1274 case RES_TXTATR_METAFIELD
:
1276 auto pTextMeta
= static_txtattr_cast
<SwTextMeta
*>(pAttr
);
1277 SwFormatMeta
& rFormatMeta( static_cast<SwFormatMeta
&>(pTextMeta
->GetAttr()) );
1278 if (::sw::Meta
* pMeta
= rFormatMeta
.GetMeta())
1280 if (SwDocShell
* pDocSh
= rDoc
.GetDocShell())
1282 static constexpr OUStringLiteral
metaNS(u
"urn:bails");
1283 const css::uno::Reference
<css::rdf::XResource
> xSubject
= pMeta
->MakeUnoObject();
1284 rtl::Reference
<SwXTextDocument
> xModel
= pDocSh
->GetBaseModel();
1285 SwRDFHelper::clearStatements(xModel
, metaNS
, xSubject
);
1289 static_txtattr_cast
<SwTextMeta
*>(pAttr
)->ChgTextNode(nullptr);
1292 case RES_TXTATR_CONTENTCONTROL
:
1294 static_txtattr_cast
<SwTextContentControl
*>(pAttr
)->ChgTextNode(nullptr);
1302 SwTextAttr::Destroy( pAttr
);
1305 SwTextAttr
* SwTextNode::InsertItem(
1307 const sal_Int32 nStart
,
1308 const sal_Int32 nEnd
,
1309 const SetAttrMode nMode
)
1311 // character attributes will be inserted as automatic styles:
1312 assert( !isCHRATR(rAttr
.Which()) && "AUTOSTYLES - "
1313 "SwTextNode::InsertItem should not be called with character attributes");
1315 SwTextAttr
*const pNew
=
1321 (nMode
& SetAttrMode::IS_COPY
) ? CopyOrNewType::Copy
: CopyOrNewType::New
,
1326 const bool bSuccess( InsertHint( pNew
, nMode
) );
1327 // N.B.: also check that the hint is actually in the hints array,
1328 // because hints of certain types may be merged after successful
1329 // insertion, and thus destroyed!
1330 if (!bSuccess
|| !m_pSwpHints
->Contains( pNew
))
1339 // take ownership of pAttr; if insertion fails, delete pAttr
1340 bool SwTextNode::InsertHint( SwTextAttr
* const pAttr
, const SetAttrMode nMode
)
1342 bool bHiddenPara
= false;
1344 assert(pAttr
&& pAttr
->GetStart() <= Len());
1345 assert(!pAttr
->GetEnd() || (*pAttr
->GetEnd() <= Len()));
1347 // translate from SetAttrMode to InsertMode (for hints with CH_TXTATR)
1348 const SwInsertFlags nInsertFlags
=
1349 (nMode
& SetAttrMode::NOHINTEXPAND
)
1350 ? SwInsertFlags::NOHINTEXPAND
1351 : (nMode
& SetAttrMode::FORCEHINTEXPAND
)
1352 ? (SwInsertFlags::FORCEHINTEXPAND
| SwInsertFlags::EMPTYEXPAND
)
1353 : SwInsertFlags::EMPTYEXPAND
;
1355 // need this after TryInsertHint, when pAttr may be deleted
1356 const sal_Int32
nStart( pAttr
->GetStart() );
1357 const bool bDummyChar( pAttr
->HasDummyChar() );
1360 SetAttrMode nInsMode
= nMode
;
1361 switch( pAttr
->Which() )
1363 case RES_TXTATR_FLYCNT
:
1365 SwTextFlyCnt
*pFly
= static_cast<SwTextFlyCnt
*>(pAttr
);
1366 SwFrameFormat
* pFormat
= pAttr
->GetFlyCnt().GetFrameFormat();
1367 if( !(SetAttrMode::NOTXTATRCHR
& nInsMode
) )
1369 // Need to insert char first, because SetAnchor() reads
1371 //JP 11.05.98: if the anchor is already set correctly,
1372 // fix it after inserting the char, so that clients don't
1373 // have to worry about it.
1374 const SwFormatAnchor
* pAnchor
= pFormat
->GetItemIfSet( RES_ANCHOR
, false );
1376 SwContentIndex
aIdx( this, pAttr
->GetStart() );
1377 const OUString
c(GetCharOfTextAttr(*pAttr
));
1378 OUString
const ins( InsertText(c
, aIdx
, nInsertFlags
) );
1381 // do not record deletion of Format!
1382 ::sw::UndoGuard
const ug(
1383 pFormat
->GetDoc()->GetIDocumentUndoRedo());
1385 return false; // text node full :(
1387 nInsMode
|= SetAttrMode::NOTXTATRCHR
;
1390 (RndStdIds::FLY_AS_CHAR
== pAnchor
->GetAnchorId()) &&
1391 pAnchor
->GetAnchorNode() &&
1392 *pAnchor
->GetAnchorNode() == *this &&
1393 pAnchor
->GetAnchorContentOffset() == aIdx
.GetIndex() )
1395 const_cast<SwPosition
*>(pAnchor
->GetContentAnchor())->AdjustContent(-1);
1398 pFly
->SetAnchor( this );
1400 // format pointer could have changed in SetAnchor,
1401 // when copying to other docs!
1402 pFormat
= pAttr
->GetFlyCnt().GetFrameFormat();
1403 SwDoc
*pDoc
= pFormat
->GetDoc();
1405 // OD 26.06.2003 - allow drawing objects in header/footer.
1406 // But don't allow control objects in header/footer
1407 if( RES_DRAWFRMFMT
== pFormat
->Which() &&
1408 pDoc
->IsInHeaderFooter( *pFormat
->GetAnchor().GetAnchorNode() ) )
1410 bool bCheckControlLayer
= false;
1411 pFormat
->CallSwClientNotify(sw::CheckDrawFrameFormatLayerHint(&bCheckControlLayer
));
1412 if( bCheckControlLayer
)
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
)
1419 // delete the char from the string
1420 assert(CH_TXTATR_BREAKWORD
== m_Text
[pAttr
->GetStart()]
1421 || CH_TXTATR_INWORD
== m_Text
[pAttr
->GetStart()]);
1422 m_Text
= m_Text
.replaceAt(pAttr
->GetStart(), 1, u
"");
1423 // Update SwContentIndexes
1424 SwContentIndex
aTmpIdx( this, pAttr
->GetStart() );
1425 Update(aTmpIdx
, 1, UpdateMode::Negative
);
1427 // do not record deletion of Format!
1428 ::sw::UndoGuard
const ug(pDoc
->GetIDocumentUndoRedo());
1429 DestroyAttr( pAttr
);
1436 case RES_TXTATR_FTN
:
1438 // Footnotes: create text node and put it into Inserts-section
1439 SwDoc
& rDoc
= GetDoc();
1440 SwNodes
&rNodes
= rDoc
.GetNodes();
1442 // check that footnote is inserted into body or redline section
1443 bool bSplitFly
= false;
1444 if (StartOfSectionIndex() < rNodes
.GetEndOfAutotext().GetIndex()
1445 && StartOfSectionIndex() >= rNodes
.GetEndOfInserts().GetIndex())
1447 // This is a frame, header or footer. Check if it's a split frame.
1448 SwFrameFormat
* pFlyFormat
= StartOfSectionNode()->GetFlyFormat();
1449 bSplitFly
= pFlyFormat
&& pFlyFormat
->GetFlySplit().GetValue();
1452 if (StartOfSectionIndex() < rNodes
.GetEndOfAutotext().GetIndex() && !bSplitFly
)
1454 // This should not be allowed, prevent it here.
1455 // The dtor of the SwTextAttr does not delete the
1456 // char, so delete it explicitly here.
1457 if( SetAttrMode::NOTXTATRCHR
& nInsMode
)
1459 // delete the char from the string
1460 assert(CH_TXTATR_BREAKWORD
== m_Text
[pAttr
->GetStart()]
1461 || CH_TXTATR_INWORD
== m_Text
[pAttr
->GetStart()]);
1462 m_Text
= m_Text
.replaceAt(pAttr
->GetStart(), 1, u
"");
1463 // Update SwContentIndexes
1464 SwContentIndex
aTmpIdx( this, pAttr
->GetStart() );
1465 Update(aTmpIdx
, 1, UpdateMode::Negative
);
1467 DestroyAttr( pAttr
);
1471 // is a new footnote being inserted?
1472 bool bNewFootnote
= nullptr == static_cast<SwTextFootnote
*>(pAttr
)->GetStartNode();
1475 static_cast<SwTextFootnote
*>(pAttr
)->MakeNewTextSection( GetNodes() );
1476 SwRegHistory
* pHist
= GetpSwpHints()
1477 ? GetpSwpHints()->GetHistory() : nullptr;
1479 pHist
->ChangeNodeIndex( GetIndex() );
1481 else if ( !GetpSwpHints() || !GetpSwpHints()->IsInSplitNode() )
1483 // existing footnote: delete all layout frames of its
1485 SwNodeOffset nSttIdx
=
1486 static_cast<SwTextFootnote
*>(pAttr
)->GetStartNode()->GetIndex();
1487 SwNodeOffset nEndIdx
= rNodes
[ nSttIdx
++ ]->EndOfSectionIndex();
1488 for( ; nSttIdx
< nEndIdx
; ++nSttIdx
)
1490 SwContentNode
* pCNd
= rNodes
[ nSttIdx
]->GetContentNode();
1491 if( nullptr != pCNd
)
1492 pCNd
->DelFrames(nullptr);
1493 else if (SwTableNode
*const pTable
= rNodes
[nSttIdx
]->GetTableNode())
1495 pTable
->DelFrames();
1500 if( !(SetAttrMode::NOTXTATRCHR
& nInsMode
) )
1502 // must insert first, to prevent identical indexes
1503 // that could later prevent insertion into SwDoc's
1505 SwContentIndex
aNdIdx( this, pAttr
->GetStart() );
1506 const OUString
c(GetCharOfTextAttr(*pAttr
));
1507 OUString
const ins( InsertText(c
, aNdIdx
, nInsertFlags
) );
1511 return false; // text node full :(
1513 nInsMode
|= SetAttrMode::NOTXTATRCHR
;
1516 // insert into SwDoc's footnote index array
1517 SwTextFootnote
* pTextFootnote
= nullptr;
1520 // moving an existing footnote (e.g. SplitNode)
1521 for( size_t n
= 0; n
< rDoc
.GetFootnoteIdxs().size(); ++n
)
1522 if( pAttr
== rDoc
.GetFootnoteIdxs()[n
] )
1524 // assign new index by removing and re-inserting
1525 pTextFootnote
= rDoc
.GetFootnoteIdxs()[n
];
1526 rDoc
.GetFootnoteIdxs().erase( rDoc
.GetFootnoteIdxs().begin() + n
);
1529 // if the Undo set the StartNode, the Index isn't
1530 // in the doc's array yet!
1532 if( !pTextFootnote
)
1533 pTextFootnote
= static_cast<SwTextFootnote
*>(pAttr
);
1535 // to update the numbers and for sorting, the Node must be set
1536 static_cast<SwTextFootnote
*>(pAttr
)->ChgTextNode( this );
1538 // do not insert footnote in redline section into footnote array
1539 if (StartOfSectionIndex() > rNodes
.GetEndOfRedlines().GetIndex() || bSplitFly
)
1541 const bool bSuccess
= rDoc
.GetFootnoteIdxs().insert(pTextFootnote
).second
;
1542 OSL_ENSURE( bSuccess
, "FootnoteIdx not inserted." );
1544 rDoc
.GetFootnoteIdxs().UpdateFootnote( *this );
1545 static_cast<SwTextFootnote
*>(pAttr
)->SetSeqRefNo();
1549 case RES_TXTATR_FIELD
:
1551 // trigger notification for relevant fields, like HiddenParaFields
1552 if (GetDoc().FieldCanHideParaWeight(
1553 pAttr
->GetFormatField().GetField()->GetTyp()->Which()))
1559 case RES_TXTATR_LINEBREAK
:
1561 static_cast<SwTextLineBreak
*>(pAttr
)->SetTextNode(this);
1566 // CH_TXTATR_* are inserted for SwTextHints without EndIndex
1567 // If the caller is SwTextNode::Copy, the char has already been copied,
1568 // and SETATTR_NOTXTATRCHR prevents inserting it again here.
1569 if( !(SetAttrMode::NOTXTATRCHR
& nInsMode
) )
1571 SwContentIndex
aIdx( this, pAttr
->GetStart() );
1572 OUString
const ins( InsertText(OUString(GetCharOfTextAttr(*pAttr
)),
1573 aIdx
, nInsertFlags
) );
1577 return false; // text node full :(
1580 // adjust end of hint to account for inserted CH_TXTATR
1581 const sal_Int32
* pEnd(pAttr
->GetEnd());
1584 pAttr
->SetEnd(*pEnd
+ 1);
1587 if (pAttr
->Which() == RES_TXTATR_CONTENTCONTROL
)
1589 // Content controls have a dummy character at their end as well.
1590 SwContentIndex
aEndIdx(this, *pAttr
->GetEnd());
1592 = InsertText(OUString(GetCharOfTextAttr(*pAttr
)), aEndIdx
, nInsertFlags
);
1599 pEnd
= pAttr
->GetEnd();
1602 pAttr
->SetEnd(*pEnd
+ 1);
1608 // handle attributes which provide content
1609 sal_Int32 nEnd
= nStart
;
1610 bool bInputFieldStartCharInserted
= false;
1611 bool bInputFieldEndCharInserted
= false;
1612 const bool bHasContent( pAttr
->HasContent() );
1615 switch( pAttr
->Which() )
1617 case RES_TXTATR_INPUTFIELD
:
1619 SwTextInputField
* pTextInputField
= dynamic_cast<SwTextInputField
*>(pAttr
);
1620 if ( pTextInputField
)
1622 if( !(SetAttrMode::NOTXTATRCHR
& nMode
) )
1624 SwContentIndex
aIdx( this, pAttr
->GetStart() );
1625 const OUString aContent
= OUStringChar(CH_TXT_ATR_INPUTFIELDSTART
)
1626 + pTextInputField
->GetFieldContent() + OUStringChar(CH_TXT_ATR_INPUTFIELDEND
);
1627 InsertText( aContent
, aIdx
, nInsertFlags
);
1629 const sal_Int32
* const pEnd(pAttr
->GetEnd());
1630 assert(pEnd
!= nullptr);
1631 pAttr
->SetEnd(*pEnd
+ aContent
.getLength());
1632 nEnd
= *pAttr
->GetEnd();
1636 // assure that CH_TXT_ATR_INPUTFIELDSTART and CH_TXT_ATR_INPUTFIELDEND are inserted.
1637 if ( m_Text
[ pAttr
->GetStart() ] != CH_TXT_ATR_INPUTFIELDSTART
)
1639 SwContentIndex
aIdx( this, pAttr
->GetStart() );
1640 InsertText( OUString(CH_TXT_ATR_INPUTFIELDSTART
), aIdx
, nInsertFlags
);
1641 bInputFieldStartCharInserted
= true;
1642 const sal_Int32
* const pEnd(pAttr
->GetEnd());
1643 assert(pEnd
!= nullptr);
1644 pAttr
->SetEnd(*pEnd
+ 1);
1645 nEnd
= *pAttr
->GetEnd();
1648 const sal_Int32
* const pEnd(pAttr
->GetEnd());
1649 assert(pEnd
!= nullptr);
1650 if (m_Text
[ *pEnd
- 1 ] != CH_TXT_ATR_INPUTFIELDEND
)
1652 SwContentIndex
aIdx( this, *pEnd
);
1653 InsertText( OUString(CH_TXT_ATR_INPUTFIELDEND
), aIdx
, nInsertFlags
);
1654 bInputFieldEndCharInserted
= true;
1655 pAttr
->SetEnd(*pEnd
+ 1);
1656 nEnd
= *pAttr
->GetEnd();
1667 GetOrCreateSwpHints();
1669 // handle overlap with an existing InputField
1670 bool bInsertHint
= true;
1672 const SwTextInputField
* pTextInputField
= GetOverlappingInputField( *pAttr
);
1673 if ( pTextInputField
!= nullptr )
1675 if ( pAttr
->End() == nullptr )
1677 bInsertHint
= false;
1682 if ( pAttr
->GetStart() > pTextInputField
->GetStart() )
1684 pAttr
->SetStart( pTextInputField
->GetStart() );
1686 if ( *(pAttr
->End()) < *(pTextInputField
->End()) )
1688 pAttr
->SetEnd(*(pTextInputField
->End()));
1696 // Handle the invariant that a plain text content control has the same character formatting
1697 // for all of its content.
1698 auto* pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(
1699 GetTextAttrAt(pAttr
->GetStart(), RES_TXTATR_CONTENTCONTROL
, ::sw::GetTextAttrMode::Parent
));
1700 // If the caller is SwTextNode::CopyText, we just copy an existing attribute, no need to
1702 if (pTextContentControl
&& !(nMode
& SetAttrMode::NOTXTATRCHR
))
1704 auto& rFormatContentControl
1705 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
1706 std::shared_ptr
<SwContentControl
> pContentControl
1707 = rFormatContentControl
.GetContentControl();
1708 if (pAttr
->End() != nullptr && pContentControl
->GetPlainText())
1710 if (pAttr
->GetStart() > pTextContentControl
->GetStart())
1712 pAttr
->SetStart(pTextContentControl
->GetStart());
1714 if (*pAttr
->End() < *pTextContentControl
->End())
1716 pAttr
->SetEnd(*pTextContentControl
->End());
1722 const bool bRet
= bInsertHint
1723 && m_pSwpHints
->TryInsertHint( pAttr
, *this, nMode
);
1728 && !(SetAttrMode::NOTXTATRCHR
& nMode
) )
1730 // undo insertion of dummy character
1731 // N.B. cannot insert the dummy character after inserting the hint,
1732 // because if the hint has no extent it will be moved in InsertText,
1733 // resulting in infinite recursion
1734 assert((CH_TXTATR_BREAKWORD
== m_Text
[nStart
] ||
1735 CH_TXTATR_INWORD
== m_Text
[nStart
] ));
1736 SwContentIndex
aIdx( this, nStart
);
1737 EraseText( aIdx
, 1 );
1742 if ( !(SetAttrMode::NOTXTATRCHR
& nMode
)
1743 && (nEnd
- nStart
) > 0 )
1745 SwContentIndex
aIdx( this, nStart
);
1746 EraseText( aIdx
, (nEnd
- nStart
) );
1750 if ( bInputFieldEndCharInserted
1751 && (nEnd
- nStart
) > 0 )
1753 SwContentIndex
aIdx( this, nEnd
- 1 );
1754 EraseText( aIdx
, 1 );
1757 if ( bInputFieldStartCharInserted
)
1759 SwContentIndex
aIdx( this, nStart
);
1760 EraseText( aIdx
, 1 );
1768 SetCalcHiddenParaField();
1774 void SwTextNode::DeleteAttribute( SwTextAttr
* const pAttr
)
1778 OSL_FAIL("DeleteAttribute called, but text node without hints?");
1782 if ( pAttr
->HasDummyChar() )
1785 const SwContentIndex
aIdx( this, pAttr
->GetStart() );
1786 // erase the CH_TXTATR, which will also delete pAttr
1787 EraseText( aIdx
, 1 );
1789 else if ( pAttr
->HasContent() )
1791 const SwContentIndex
aIdx( this, pAttr
->GetStart() );
1792 assert(pAttr
->End() != nullptr);
1793 EraseText( aIdx
, *pAttr
->End() - pAttr
->GetStart() );
1797 // create MsgHint before start/end become invalid
1803 m_pSwpHints
->Delete( pAttr
);
1804 SwTextAttr::Destroy( pAttr
);
1805 CallSwClientNotify(sw::UpdateAttrHint(nullptr, &aHint
));
1807 TryDeleteSwpHints();
1811 //FIXME: this does NOT respect SORT NUMBER (for CHARFMT)!
1812 void SwTextNode::DeleteAttributes(
1813 const sal_uInt16 nWhich
,
1814 const sal_Int32 nStart
,
1815 const sal_Int32 nEnd
)
1820 for ( size_t nPos
= 0; m_pSwpHints
&& nPos
< m_pSwpHints
->Count(); ++nPos
)
1822 SwTextAttr
* const pTextHt
= m_pSwpHints
->Get( nPos
);
1823 const sal_Int32 nHintStart
= pTextHt
->GetStart();
1824 if (nStart
< nHintStart
)
1826 break; // sorted by start
1828 else if ( (nStart
== nHintStart
) && (nWhich
== pTextHt
->Which()) )
1830 if ( nWhich
== RES_CHRATR_HIDDEN
)
1832 assert(!"hey, that's a CHRATR! how did that get in?");
1833 SetCalcHiddenCharFlags();
1835 else if ( nWhich
== RES_TXTATR_CHARFMT
)
1837 // Check if character format contains hidden attribute:
1838 const SwCharFormat
* pFormat
= pTextHt
->GetCharFormat().GetCharFormat();
1839 if ( SfxItemState::SET
== pFormat
->GetItemState( RES_CHRATR_HIDDEN
) )
1840 SetCalcHiddenCharFlags();
1842 // #i75430# Recalc hidden flags if necessary
1843 else if ( nWhich
== RES_TXTATR_AUTOFMT
)
1845 // Check if auto style contains hidden attribute:
1846 const SfxPoolItem
* pHiddenItem
= CharFormat::GetItem( *pTextHt
, RES_CHRATR_HIDDEN
);
1848 SetCalcHiddenCharFlags();
1849 // for auto styles DeleteAttributes is only called from Undo
1850 // so it shouldn't need to care about ignore start/end flags
1853 sal_Int32
const * const pEndIdx
= pTextHt
->GetEnd();
1855 if ( pTextHt
->HasDummyChar() )
1858 const SwContentIndex
aIdx( this, nStart
);
1859 // erase the CH_TXTATR, which will also delete pTextHt
1860 EraseText( aIdx
, 1 );
1862 else if ( pTextHt
->HasContent() )
1864 const SwContentIndex
aIdx( this, nStart
);
1865 OSL_ENSURE( pTextHt
->End() != nullptr, "<SwTextNode::DeleteAttributes(..)> - missing End() at <SwTextAttr> instance which has content" );
1866 EraseText( aIdx
, *pTextHt
->End() - nStart
);
1868 else if( *pEndIdx
== nEnd
)
1870 // Create MsgHint before Start and End are gone.
1871 // For HiddenParaFields it's not necessary to call
1872 // SetCalcHiddenParaField because the dtor does that.
1878 m_pSwpHints
->DeleteAtPos( nPos
);
1879 SwTextAttr::Destroy( pTextHt
);
1880 CallSwClientNotify(sw::UpdateAttrHint(nullptr, &aHint
));
1884 TryDeleteSwpHints();
1887 void SwTextNode::DelSoftHyph( const sal_Int32 nStt
, const sal_Int32 nEnd
)
1889 sal_Int32 nFndPos
= nStt
;
1890 sal_Int32 nEndPos
= nEnd
;
1893 nFndPos
= m_Text
.indexOf(CHAR_SOFTHYPHEN
, nFndPos
);
1894 if (nFndPos
<0 || nFndPos
>=nEndPos
)
1898 const SwContentIndex
aIdx( this, nFndPos
);
1899 EraseText( aIdx
, 1 );
1904 bool SwTextNode::IsIgnoredCharFormatForNumbering(const sal_uInt16 nWhich
, bool bIsCharStyle
)
1906 // LO can save the char background as either shading or highlight, so check which mode is currently chosen.
1907 // Shading does not affect the numbering. Highlighting does (but isn't allowed in a char style).
1908 if (nWhich
== RES_CHRATR_BACKGROUND
)
1909 return bIsCharStyle
|| !officecfg::Office::Common::Filter::Microsoft::Export::CharBackgroundToHighlighting::get();
1911 return (nWhich
== RES_CHRATR_UNDERLINE
1912 || nWhich
== RES_CHRATR_ESCAPEMENT
);
1915 // Set these attributes on SwTextNode. If they apply to the entire paragraph
1916 // text, set them in the SwTextNode's item set (SwContentNode::SetAttr).
1917 bool SwTextNode::SetAttr(
1918 const SfxItemSet
& rSet
,
1919 const sal_Int32 nStt
,
1920 const sal_Int32 nEnd
,
1921 const SetAttrMode nMode
,
1922 SwTextAttr
**ppNewTextAttr
)
1927 // split sets (for selection in nodes)
1928 const SfxItemSet
* pSet
= &rSet
;
1929 SfxItemSetFixed
<RES_TXTATR_BEGIN
, RES_TXTATR_END
-1> aTextSet( *rSet
.GetPool() );
1932 if ( !nStt
&& (nEnd
== m_Text
.getLength()) &&
1933 !(nMode
& SetAttrMode::NOFORMATATTR
) )
1935 // if the node already has CharFormat hints, the new attributes must
1936 // be set as hints too to override those.
1937 bool bHasCharFormats
= false;
1940 for ( size_t n
= 0; n
< m_pSwpHints
->Count(); ++n
)
1942 if ( m_pSwpHints
->Get( n
)->IsCharFormatAttr() )
1944 bHasCharFormats
= true;
1950 if( !bHasCharFormats
)
1952 aTextSet
.Put( rSet
);
1953 // If there are any character attributes in rSet,
1954 // we want to set them at the paragraph:
1955 if( aTextSet
.Count() != rSet
.Count() )
1957 const bool bRet
= SetAttr( rSet
);
1958 if( !aTextSet
.Count() )
1962 // check for auto style:
1963 if ( const SwFormatAutoFormat
* pItem
= aTextSet
.GetItemIfSet( RES_TXTATR_AUTOFMT
, false ) )
1965 const bool bRet
= SetAttr( *pItem
->GetStyleHandle() );
1966 if( 1 == aTextSet
.Count() )
1970 // Continue with the text attributes:
1975 GetOrCreateSwpHints();
1977 SfxItemSet
aCharSet( *rSet
.GetPool(), aCharAutoFormatSetRange
);
1980 SfxItemIter
aIter( *pSet
);
1981 const SfxPoolItem
* pItem
= aIter
.GetCurItem();
1985 if (!IsInvalidItem(pItem
))
1987 const sal_uInt16 nWhich
= pItem
->Which();
1988 OSL_ENSURE( isCHRATR(nWhich
) || isTXTATR(nWhich
),
1989 "SwTextNode::SetAttr(): unknown attribute" );
1990 if ( isCHRATR(nWhich
) || isTXTATR(nWhich
) )
1992 if ((RES_TXTATR_CHARFMT
== nWhich
) &&
1993 (GetDoc().GetDfltCharFormat() ==
1994 static_cast<const SwFormatCharFormat
*>(pItem
)->GetCharFormat()))
1996 RstTextAttr( nStt
, nEnd
- nStt
, RES_TXTATR_CHARFMT
);
1997 DontExpandFormat( nStt
);
2001 if (isCHRATR(nWhich
) ||
2002 (RES_TXTATR_UNKNOWN_CONTAINER
== nWhich
))
2004 aCharSet
.Put( *pItem
);
2009 SwTextAttr
*const pNew
= MakeTextAttr( GetDoc(),
2010 const_cast<SfxPoolItem
&>(*pItem
), nStt
, nEnd
);
2013 // store the first one we create into the pp
2014 if (ppNewTextAttr
&& !*ppNewTextAttr
)
2015 *ppNewTextAttr
= pNew
;
2016 if ( nEnd
!= nStt
&& !pNew
->GetEnd() )
2018 OSL_FAIL("Attribute without end, but area marked");
2019 DestroyAttr( pNew
); // do not insert
2021 else if ( InsertHint( pNew
, nMode
) )
2030 pItem
= aIter
.NextItem();
2033 if ( aCharSet
.Count() )
2035 SwTextAttr
* pTmpNew
= MakeTextAttr( GetDoc(), aCharSet
, nStt
, nEnd
);
2036 if ( InsertHint( pTmpNew
, nMode
) )
2042 TryDeleteSwpHints();
2047 static void lcl_MergeAttr( SfxItemSet
& rSet
, const SfxPoolItem
& rAttr
)
2049 if ( RES_TXTATR_AUTOFMT
== rAttr
.Which() )
2051 const SfxItemSet
* pCFSet
= CharFormat::GetItemSet( rAttr
);
2054 SfxWhichIter
aIter( *pCFSet
);
2055 sal_uInt16 nWhich
= aIter
.FirstWhich();
2058 const SfxPoolItem
* pItem
= nullptr;
2059 if( ( nWhich
< RES_CHRATR_END
||
2060 RES_TXTATR_UNKNOWN_CONTAINER
== nWhich
) &&
2061 ( SfxItemState::SET
== aIter
.GetItemState( true, &pItem
) ) )
2063 nWhich
= aIter
.NextWhich();
2070 static void lcl_MergeAttr_ExpandChrFormat( SfxItemSet
& rSet
, const SfxPoolItem
& rAttr
)
2072 if( RES_TXTATR_CHARFMT
== rAttr
.Which() ||
2073 RES_TXTATR_INETFMT
== rAttr
.Which() ||
2074 RES_TXTATR_AUTOFMT
== rAttr
.Which() )
2076 const SfxItemSet
* pCFSet
= CharFormat::GetItemSet( rAttr
);
2080 SfxWhichIter
aIter( *pCFSet
);
2081 sal_uInt16 nWhich
= aIter
.FirstWhich();
2084 const SfxPoolItem
* pItem
= nullptr;
2085 if( ( nWhich
< RES_CHRATR_END
||
2086 ( RES_TXTATR_AUTOFMT
== rAttr
.Which() && RES_TXTATR_UNKNOWN_CONTAINER
== nWhich
) ) &&
2087 ( SfxItemState::SET
== aIter
.GetItemState( true, &pItem
) ) )
2089 nWhich
= aIter
.NextWhich();
2094 /* If multiple attributes overlap, the last one wins!
2095 Probably this can only happen between a RES_TXTATR_INETFMT and one of the
2096 other hints, because BuildPortions ensures that CHARFMT/AUTOFMT don't
2097 overlap. But there may be multiple CHARFMT/AUTOFMT with exactly the same
2098 start/end, sorted by BuildPortions, in which case the same logic applies.
2101 |------------| Font1
2104 |--| query range: -> Font2
2112 struct SwPoolItemEndPair
2115 const SfxPoolItem
* mpItem
;
2118 SwPoolItemEndPair() : mpItem( nullptr ), mnEndPos( 0 ) {};
2123 static void lcl_MergeListLevelIndentAsLRSpaceItem( const SwTextNode
& rTextNode
,
2126 ::sw::ListLevelIndents
const indents(rTextNode
.AreListLevelIndentsApplicable());
2127 if (indents
== ::sw::ListLevelIndents::No
)
2130 const SwNumRule
* pRule
= rTextNode
.GetNumRule();
2131 if ( pRule
&& rTextNode
.GetActualListLevel() >= 0 )
2133 const SwNumFormat
& rFormat
= pRule
->Get(o3tl::narrowing
<sal_uInt16
>(rTextNode
.GetActualListLevel()));
2134 if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT
)
2136 if (indents
& ::sw::ListLevelIndents::FirstLine
)
2138 SvxFirstLineIndentItem
const firstLine(
2139 SvxIndentValue
{ static_cast<double>(rFormat
.GetFirstLineIndent()),
2140 rFormat
.GetFirstLineIndentUnit() },
2141 RES_MARGIN_FIRSTLINE
);
2142 rSet
.Put(firstLine
);
2144 if (indents
& ::sw::ListLevelIndents::LeftMargin
)
2146 SvxTextLeftMarginItem
const leftMargin(SvxIndentValue::twips(rFormat
.GetIndentAt()),
2147 RES_MARGIN_TEXTLEFT
);
2148 rSet
.Put(leftMargin
);
2154 // request the attributes of the TextNode at the range
2155 bool SwTextNode::GetParaAttr(SfxItemSet
& rSet
, sal_Int32 nStt
, sal_Int32 nEnd
,
2156 const bool bOnlyTextAttr
, const bool bGetFromChrFormat
,
2157 const bool bMergeIndentValuesOfNumRule
,
2158 SwRootFrame
const*const pLayout
) const
2160 assert(!rSet
.Count()); // handled inconsistently, typically an error?
2162 if (pLayout
&& pLayout
->HasMergedParas())
2164 if (GetRedlineMergeFlag() == SwNode::Merge::Hidden
)
2166 return false; // ignore deleted node
2170 // get the node's automatic attributes
2171 SfxItemSet
aFormatSet( *rSet
.GetPool(), rSet
.GetRanges() );
2174 SwTextNode
const& rParaPropsNode(
2175 sw::GetAttrMerged(aFormatSet
, *this, pLayout
));
2176 if (bMergeIndentValuesOfNumRule
)
2178 lcl_MergeListLevelIndentAsLRSpaceItem(rParaPropsNode
, aFormatSet
);
2184 // First, check which text attributes are valid in the range.
2187 // * the attribute is wholly contained in the range
2188 // * the attribute end is in the range
2189 // * the attribute start is in the range
2190 // Unambiguous (merge into set), if
2191 // * the attribute wholly contains the range
2193 // * the attribute is wholly outside the range
2195 void (*fnMergeAttr
)( SfxItemSet
&, const SfxPoolItem
& )
2196 = bGetFromChrFormat
? &lcl_MergeAttr_ExpandChrFormat
2199 const size_t nSize
= m_pSwpHints
->Count();
2201 if (nStt
== nEnd
) // no range:
2203 for (size_t n
= 0; n
< nSize
; ++n
)
2205 const SwTextAttr
* pHt
= m_pSwpHints
->Get(n
);
2206 const sal_Int32 nAttrStart
= pHt
->GetStart();
2207 if (nAttrStart
> nEnd
) // behind the range
2210 const sal_Int32
* pAttrEnd
= pHt
->End();
2211 if ( ! pAttrEnd
) // no attributes without end
2214 if( ( nAttrStart
< nStt
&&
2215 ( pHt
->DontExpand() ? nStt
< *pAttrEnd
2216 : nStt
<= *pAttrEnd
)) ||
2217 ( nStt
== nAttrStart
&&
2218 ( nAttrStart
== *pAttrEnd
|| !nStt
)))
2219 (*fnMergeAttr
)( rSet
, pHt
->GetAttr() );
2222 else // a query range is defined
2225 std::optional
< std::vector
< SwPoolItemEndPair
> > pAttrArr
;
2227 const size_t coArrSz
= RES_TXTATR_WITHEND_END
- RES_CHRATR_BEGIN
;
2229 for (size_t n
= 0; n
< nSize
; ++n
)
2231 const SwTextAttr
* pHt
= m_pSwpHints
->Get(n
);
2232 const sal_Int32 nAttrStart
= pHt
->GetStart();
2233 if (nAttrStart
> nEnd
) // outside, behind
2236 const sal_Int32
* pAttrEnd
= pHt
->End();
2237 if ( ! pAttrEnd
) // no attributes without end
2240 bool bChkInvalid
= false;
2241 if (nAttrStart
<= nStt
) // before or exactly Start
2243 if (*pAttrEnd
<= nStt
) // outside, before
2246 if (nEnd
<= *pAttrEnd
) // behind or exactly End
2247 (*fnMergeAttr
)( aFormatSet
, pHt
->GetAttr() );
2249 // else if( pHt->GetAttr() != aFormatSet.Get( pHt->Which() ) )
2253 else if (nAttrStart
< nEnd
// starts in the range
2254 )// && pHt->GetAttr() != aFormatSet.Get( pHt->Which() ) )
2260 std::optional
< SfxItemIter
> oItemIter
;
2261 const SfxPoolItem
* pItem
= nullptr;
2263 if ( RES_TXTATR_AUTOFMT
== pHt
->Which() )
2265 const SfxItemSet
* pAutoSet
= CharFormat::GetItemSet( pHt
->GetAttr() );
2268 oItemIter
.emplace( *pAutoSet
);
2269 pItem
= oItemIter
->GetCurItem();
2273 pItem
= &pHt
->GetAttr();
2275 const sal_Int32 nHintEnd
= *pAttrEnd
;
2277 for (; pItem
; pItem
= oItemIter
? oItemIter
->NextItem() : nullptr)
2279 const sal_uInt16 nHintWhich
= pItem
->Which();
2280 OSL_ENSURE(!isUNKNOWNATR(nHintWhich
),
2281 "SwTextNode::GetAttr(): unknown attribute?");
2285 pAttrArr
= std::vector
< SwPoolItemEndPair
>(coArrSz
);
2288 std::vector
< SwPoolItemEndPair
>::iterator pPrev
= pAttrArr
->begin();
2289 if (isCHRATR(nHintWhich
) ||
2290 isTXTATR_WITHEND(nHintWhich
))
2292 pPrev
+= nHintWhich
- RES_CHRATR_BEGIN
;
2296 pPrev
= pAttrArr
->end();
2299 if( pPrev
!= pAttrArr
->end() )
2301 if( !pPrev
->mpItem
)
2303 if ( bOnlyTextAttr
|| *pItem
!= aFormatSet
.Get( nHintWhich
) )
2305 if( nAttrStart
> nStt
)
2307 rSet
.InvalidateItem( nHintWhich
);
2308 pPrev
->mpItem
= INVALID_POOL_ITEM
;
2312 pPrev
->mpItem
= pItem
;
2313 pPrev
->mnEndPos
= nHintEnd
;
2317 else if( !IsInvalidItem(pPrev
->mpItem
) )
2319 if( pPrev
->mnEndPos
== nAttrStart
&&
2320 *pPrev
->mpItem
== *pItem
)
2322 pPrev
->mpItem
= pItem
;
2323 pPrev
->mnEndPos
= nHintEnd
;
2327 rSet
.InvalidateItem( nHintWhich
);
2328 pPrev
->mpItem
= INVALID_POOL_ITEM
;
2338 for (size_t n
= 0; n
< coArrSz
; ++n
)
2340 const SwPoolItemEndPair
& rItemPair
= (*pAttrArr
)[ n
];
2341 if( rItemPair
.mpItem
&& !IsInvalidItem(rItemPair
.mpItem
) )
2343 const sal_uInt16 nWh
=
2344 o3tl::narrowing
<sal_uInt16
>(n
+ RES_CHRATR_BEGIN
);
2346 if (nEnd
<= rItemPair
.mnEndPos
) // behind or exactly end
2348 if( *rItemPair
.mpItem
!= aFormatSet
.Get( nWh
) )
2349 (*fnMergeAttr
)( rSet
, *rItemPair
.mpItem
);
2353 rSet
.InvalidateItem( nWh
);
2358 if( aFormatSet
.Count() )
2360 // remove all from the format-set that are also set in the text-set
2361 aFormatSet
.Differentiate( rSet
);
2365 if (aFormatSet
.Count())
2367 // now "merge" everything
2368 rSet
.Put( aFormatSet
);
2371 return rSet
.Count() != 0;
2377 typedef std::pair
<sal_Int32
, sal_Int32
> AttrSpan_t
;
2378 typedef std::multimap
<AttrSpan_t
, const SwTextAttr
*> AttrSpanMap_t
;
2383 operator()(const AttrSpanMap_t::value_type
& i_rAttrSpan
)
2386 return i_rAttrSpan
.second
&& i_rAttrSpan
.second
->Which() == RES_TXTATR_AUTOFMT
;
2390 /** Removes from io_rAttrSet all items that are set by style on the
2393 struct RemovePresentAttrs
2395 explicit RemovePresentAttrs(SfxItemSet
& io_rAttrSet
)
2396 : m_rAttrSet(io_rAttrSet
)
2401 operator()(const AttrSpanMap_t::value_type
& i_rAttrSpan
)
2404 if (!i_rAttrSpan
.second
)
2409 const SwTextAttr
* const pAutoStyle(i_rAttrSpan
.second
);
2411 // ITEM: SfxItemIter and removing SfxPoolItems:
2412 std::vector
<sal_uInt16
> aDeleteWhichIDs
;
2414 for (SfxItemIter
aIter(m_rAttrSet
); !aIter
.IsAtEnd(); aIter
.NextItem())
2416 if (CharFormat::IsItemIncluded(aIter
.GetCurWhich(), pAutoStyle
))
2418 aDeleteWhichIDs
.push_back(aIter
.GetCurWhich());
2422 for (auto nDelWhich
: aDeleteWhichIDs
)
2423 m_rAttrSet
.ClearItem(nDelWhich
);
2427 SfxItemSet
& m_rAttrSet
;
2430 /** Collects all style-covered spans from i_rHints to o_rSpanMap. In
2431 addition inserts dummy spans with pointer to format equal to 0 for
2432 all gaps (i.e. spans not covered by any style). This simplifies
2433 creation of autostyles for all needed spans, but it means all code
2434 that tries to access the pointer has to check if it's non-null!
2437 lcl_CollectHintSpans(const SwpHints
& i_rHints
, const sal_Int32 nLength
,
2438 AttrSpanMap_t
& o_rSpanMap
)
2440 sal_Int32
nLastEnd(0);
2442 for (size_t i
= 0; i
< i_rHints
.Count(); ++i
)
2444 const SwTextAttr
* pHint
= i_rHints
.Get(i
);
2445 const sal_uInt16
nWhich(pHint
->Which());
2446 if (nWhich
== RES_TXTATR_CHARFMT
|| nWhich
== RES_TXTATR_AUTOFMT
)
2448 const AttrSpan_t
aSpan(pHint
->GetStart(), *pHint
->End());
2449 o_rSpanMap
.emplace(aSpan
, pHint
);
2451 // < not != because there may be multiple CHARFMT at same range
2452 if (nLastEnd
< aSpan
.first
)
2454 // insert dummy span covering the gap
2455 o_rSpanMap
.emplace( AttrSpan_t(nLastEnd
, aSpan
.first
), nullptr );
2458 nLastEnd
= aSpan
.second
;
2462 // no hints at the end (special case: no hints at all in i_rHints)
2463 if (nLastEnd
!= nLength
&& nLength
!= 0)
2465 o_rSpanMap
.emplace(AttrSpan_t(nLastEnd
, nLength
), nullptr);
2470 lcl_FillWhichIds(const SfxItemSet
& i_rAttrSet
, std::vector
<sal_uInt16
>& o_rClearIds
)
2472 o_rClearIds
.reserve(i_rAttrSet
.Count());
2473 SfxItemIter
aIter(i_rAttrSet
);
2474 for (const SfxPoolItem
* pItem
= aIter
.GetCurItem(); pItem
; pItem
= aIter
.NextItem())
2476 o_rClearIds
.push_back(pItem
->Which());
2480 struct SfxItemSetClearer
2482 SfxItemSet
& m_rItemSet
;
2483 explicit SfxItemSetClearer(SfxItemSet
& rItemSet
) : m_rItemSet(rItemSet
) { }
2484 void operator()(sal_uInt16
const nWhich
) { m_rItemSet
.ClearItem(nWhich
); }
2489 /** Does the hard work of SwTextNode::FormatToTextAttr: the real conversion
2490 of items to automatic styles.
2493 SwTextNode::impl_FormatToTextAttr(const SfxItemSet
& i_rAttrSet
)
2495 typedef AttrSpanMap_t::iterator AttrSpanMap_iterator_t
;
2496 AttrSpanMap_t aAttrSpanMap
;
2498 if (i_rAttrSet
.Count() == 0)
2503 // 1. Identify all spans in hints' array
2505 lcl_CollectHintSpans(*m_pSwpHints
, m_Text
.getLength(), aAttrSpanMap
);
2507 // 2. Go through all spans and insert new attrs
2509 AttrSpanMap_iterator_t
aCurRange(aAttrSpanMap
.begin());
2510 const AttrSpanMap_iterator_t
aEnd(aAttrSpanMap
.end());
2511 while (aCurRange
!= aEnd
)
2513 typedef std::pair
<AttrSpanMap_iterator_t
, AttrSpanMap_iterator_t
>
2515 AttrSpanMapRange_t
aRange(aAttrSpanMap
.equal_range(aCurRange
->first
));
2517 // 2a. Collect attributes to insert
2519 SfxItemSet
aCurSet(i_rAttrSet
);
2520 std::for_each(aRange
.first
, aRange
.second
, RemovePresentAttrs(aCurSet
));
2522 // 2b. Insert automatic style containing the collected attributes
2524 if (aCurSet
.Count() != 0)
2526 AttrSpanMap_iterator_t
aAutoStyleIt(
2527 std::find_if(aRange
.first
, aRange
.second
, IsAutoStyle()));
2528 if (aAutoStyleIt
!= aRange
.second
)
2530 // there already is an automatic style on that span:
2531 // create new one and remove the original one
2532 SwTextAttr
* const pAutoStyle(const_cast<SwTextAttr
*>(aAutoStyleIt
->second
));
2533 const std::shared_ptr
<SfxItemSet
> pOldStyle(
2534 static_cast<const SwFormatAutoFormat
&>(
2535 pAutoStyle
->GetAttr()).GetStyleHandle());
2536 aCurSet
.Put(*pOldStyle
);
2538 // remove the old hint
2539 m_pSwpHints
->Delete(pAutoStyle
);
2540 DestroyAttr(pAutoStyle
);
2542 m_pSwpHints
->Insert(
2543 MakeTextAttr(GetDoc(), aCurSet
,
2544 aCurRange
->first
.first
, aCurRange
->first
.second
));
2547 aCurRange
= aRange
.second
;
2550 // hints were directly inserted, so need to fix the Ignore flags now
2551 m_pSwpHints
->MergePortions(*this);
2553 // 3. Clear items from the node
2554 std::vector
<sal_uInt16
> aClearedIds
;
2555 lcl_FillWhichIds(i_rAttrSet
, aClearedIds
);
2556 ClearItemsFromAttrSet(aClearedIds
);
2559 void SwTextNode::FormatToTextAttr( SwTextNode
* pNd
)
2561 SfxItemSet
aThisSet( GetDoc().GetAttrPool(), aCharFormatSetRange
);
2562 if( HasSwAttrSet() && GetpSwAttrSet()->Count() )
2563 aThisSet
.Put( *GetpSwAttrSet() );
2565 GetOrCreateSwpHints();
2569 impl_FormatToTextAttr(aThisSet
);
2573 // There are five possible combinations of items from this and
2574 // pNd (pNd is the 'main' node):
2576 // case pNd this action
2579 // 2 - a convert item to attr of this
2580 // 3 a - convert item to attr of pNd
2581 // 4 a a clear item in this
2582 // 5 a b convert item to attr of this
2584 SfxItemSet
aNdSet( pNd
->GetDoc().GetAttrPool(), aCharFormatSetRange
);
2585 if( pNd
->HasSwAttrSet() && pNd
->GetpSwAttrSet()->Count() )
2586 aNdSet
.Put( *pNd
->GetpSwAttrSet() );
2588 pNd
->GetOrCreateSwpHints();
2590 std::vector
<sal_uInt16
> aProcessedIds
;
2592 if( aThisSet
.Count() )
2594 SfxItemIter
aIter( aThisSet
);
2595 const SfxPoolItem
* pItem
= aIter
.GetCurItem(), *pNdItem
= nullptr;
2596 SfxItemSet
aConvertSet( GetDoc().GetAttrPool(), aCharFormatSetRange
);
2597 std::vector
<sal_uInt16
> aClearWhichIds
;
2601 if( SfxItemState::SET
== aNdSet
.GetItemState( pItem
->Which(), false, &pNdItem
) )
2603 if (*pItem
== *pNdItem
) // 4
2605 aClearWhichIds
.push_back( pItem
->Which() );
2609 aConvertSet
.Put(*pItem
);
2611 aProcessedIds
.push_back(pItem
->Which());
2615 aConvertSet
.Put(*pItem
);
2618 pItem
= aIter
.NextItem();
2621 // 4/ clear items of this that are set with the same value on pNd
2622 ClearItemsFromAttrSet( aClearWhichIds
);
2624 // 2, 5/ convert all other items to attrs
2625 impl_FormatToTextAttr(aConvertSet
);
2629 std::for_each(aProcessedIds
.begin(), aProcessedIds
.end(),
2630 SfxItemSetClearer(aNdSet
));
2632 // 3/ convert items to attrs
2633 pNd
->impl_FormatToTextAttr(aNdSet
);
2635 if( aNdSet
.Count() )
2637 pNd
->CallSwClientNotify(SwFormatChangeHint(pNd
->GetFormatColl(), pNd
->GetFormatColl()));
2642 SetCalcHiddenCharFlags();
2644 pNd
->TryDeleteSwpHints();
2647 void SwpHints::CalcFlags()
2649 m_bDDEFields
= m_bFootnote
= false;
2650 const size_t nSize
= Count();
2651 for( size_t nPos
= 0; nPos
< nSize
; ++nPos
)
2653 const SwTextAttr
* pAttr
= Get( nPos
);
2654 switch( pAttr
->Which() )
2656 case RES_TXTATR_FTN
:
2661 case RES_TXTATR_FIELD
:
2663 const SwField
* pField
= pAttr
->GetFormatField().GetField();
2664 if( SwFieldIds::Dde
== pField
->GetTyp()->Which() )
2666 m_bDDEFields
= true;
2676 bool SwpHints::CalcHiddenParaField() const
2678 m_bCalcHiddenParaField
= false;
2679 const bool bOldHiddenByParaField
= m_bHiddenByParaField
;
2680 bool bNewHiddenByParaField
= false;
2681 int nNewResultWeight
= 0;
2682 const size_t nSize
= Count();
2683 const SwTextAttr
* pTextHt
;
2685 for (size_t nPos
= 0; nPos
< nSize
; ++nPos
)
2687 pTextHt
= Get(nPos
);
2688 const sal_uInt16 nWhich
= pTextHt
->Which();
2690 if (RES_TXTATR_FIELD
== nWhich
)
2692 // see also SwTextFrame::IsHiddenNow()
2693 const SwFormatField
& rField
= pTextHt
->GetFormatField();
2694 int nCurWeight
= m_rParent
.GetDoc().FieldCanHideParaWeight(rField
.GetField()->GetTyp()->Which());
2695 if (nCurWeight
> nNewResultWeight
)
2697 nNewResultWeight
= nCurWeight
;
2698 bNewHiddenByParaField
= m_rParent
.GetDoc().FieldHidesPara(*rField
.GetField());
2700 else if (nCurWeight
== nNewResultWeight
&& bNewHiddenByParaField
)
2702 // Currently, for both supported hiding types (HiddenPara, Database), "Don't hide"
2703 // takes precedence - i.e., if there's a "Don't hide" field of that weight, we only
2704 // care about fields of higher weight.
2705 bNewHiddenByParaField
= m_rParent
.GetDoc().FieldHidesPara(*rField
.GetField());
2709 SetHiddenByParaField(bNewHiddenByParaField
);
2710 return bOldHiddenByParaField
!= bNewHiddenByParaField
;
2713 void SwpHints::NoteInHistory( SwTextAttr
*pAttr
, const bool bNew
)
2715 if ( m_pHistory
) { m_pHistory
->AddHint( pAttr
, bNew
); }
2720 SwTextAttr
* pTextAttr
;
2722 bool isRsidOnlyAutoFormat
;
2724 typedef std::vector
< Portion
> PortionMap
;
2725 enum MergeResult
{ MATCH
, DIFFER_ONLY_RSID
, DIFFER
};
2728 static MergeResult
lcl_Compare_Attributes(
2730 const std::pair
< PortionMap::const_iterator
, PortionMap::const_iterator
>& aRange1
,
2731 const std::pair
< PortionMap::const_iterator
, PortionMap::const_iterator
>& aRange2
,
2732 std::vector
<bool>& RsidOnlyAutoFormatFlagMap
);
2734 bool SwpHints::MergePortions( SwTextNode
& rNode
)
2739 // sort before merging
2743 PortionMap aPortionMap
;
2744 aPortionMap
.reserve(Count() + 1);
2745 std::vector
<bool> RsidOnlyAutoFormatFlagMap
;
2746 RsidOnlyAutoFormatFlagMap
.resize(Count() + 1);
2747 sal_Int32 nLastPorStart
= COMPLETE_STRING
;
2750 // get portions by start position:
2751 for ( size_t i
= 0; i
< Count(); ++i
)
2753 SwTextAttr
*pHt
= Get( i
);
2754 if ( RES_TXTATR_CHARFMT
!= pHt
->Which() &&
2755 RES_TXTATR_AUTOFMT
!= pHt
->Which() )
2757 //RES_TXTATR_INETFMT != pHt->Which() )
2760 bool isRsidOnlyAutoFormat(false);
2761 // check for RSID-only AUTOFMT
2762 if (RES_TXTATR_AUTOFMT
== pHt
->Which())
2764 std::shared_ptr
<SfxItemSet
> const & pSet(
2765 pHt
->GetAutoFormat().GetStyleHandle());
2766 if ((pSet
->Count() == 1) && pSet
->GetItem(RES_CHRATR_RSID
, false))
2768 // fdo#70201: eliminate no-extent RSID-only AUTOFMT
2769 // could be produced by ReplaceText or (maybe?) RstAttr
2770 if (pHt
->GetStart() == *pHt
->GetEnd())
2772 DeleteAtPos(i
); // kill it without History!
2773 SwTextAttr::Destroy(pHt
);
2777 // fdo#52028: this one has _only_ RSID => ignore it completely
2778 if (!pHt
->IsFormatIgnoreStart() || !pHt
->IsFormatIgnoreEnd())
2781 pHt
->SetFormatIgnoreStart(true);
2782 pHt
->SetFormatIgnoreEnd (true);
2783 NoteInHistory(pHt
, true);
2785 isRsidOnlyAutoFormat
= true;
2789 if (pHt
->GetStart() == *pHt
->GetEnd())
2791 // no-length hints are a disease. ignore them here.
2792 // the SwAttrIter::SeekFwd will not call Rst/Chg for them.
2796 const sal_Int32 nPorStart
= pHt
->GetStart();
2797 if (nPorStart
!= nLastPorStart
)
2799 nLastPorStart
= nPorStart
;
2800 aPortionMap
.push_back(Portion
{pHt
, nKey
, isRsidOnlyAutoFormat
});
2801 RsidOnlyAutoFormatFlagMap
[nKey
] = isRsidOnlyAutoFormat
;
2804 // we add data strictly in-order, so we can forward-search the vector
2805 auto equal_range
= [](PortionMap::const_iterator startIt
, PortionMap::const_iterator endIt
, int i
)
2808 while (it1
!= endIt
&& it1
->nKey
< i
)
2811 while (it2
!= endIt
&& it2
->nKey
== i
)
2813 return std::pair
<PortionMap::const_iterator
, PortionMap::const_iterator
>{ it1
, it2
};
2816 // check if portion i can be merged with portion i+1:
2817 // note: need to include i=0 to set IgnoreStart and j=nKey+1 to reset
2818 // IgnoreEnd at first / last portion
2821 // Store this outside the loop, because we limit the search area on subsequent searches.
2822 std::pair
< PortionMap::const_iterator
, PortionMap::const_iterator
> aRange1
{ aPortionMap
.begin(), aPortionMap
.begin() + aPortionMap
.size() };
2825 aRange1
= equal_range( aRange1
.first
, aPortionMap
.begin() + aPortionMap
.size(), i
);
2826 // start the search for this one from where the first search ended.
2827 std::pair
< PortionMap::const_iterator
, PortionMap::const_iterator
> aRange2
2828 = equal_range( aRange1
.second
, aPortionMap
.begin() + aPortionMap
.size(), j
);
2830 MergeResult eMerge
= lcl_Compare_Attributes(i
, j
, aRange1
, aRange2
, RsidOnlyAutoFormatFlagMap
);
2832 if (MATCH
== eMerge
)
2834 // important: delete second range so any IgnoreStart on the first
2835 // range is still valid
2836 // erase all elements with key i + 1
2837 sal_Int32 nNewPortionEnd
= 0;
2838 for ( auto aIter2
= aRange2
.first
; aIter2
!= aRange2
.second
; ++aIter2
)
2840 SwTextAttr
*const p2
= aIter2
->pTextAttr
;
2841 nNewPortionEnd
= *p2
->GetEnd();
2843 const size_t nCountBeforeDelete
= Count();
2846 // robust: check if deletion actually took place before destroying attribute:
2847 if ( Count() < nCountBeforeDelete
)
2848 rNode
.DestroyAttr( p2
);
2850 aPortionMap
.erase( aRange2
.first
, aRange2
.second
);
2853 // change all attributes with key i
2854 aRange1
= equal_range( aRange1
.first
, aPortionMap
.begin() + aPortionMap
.size(), i
);
2855 for ( auto aIter1
= aRange1
.first
; aIter1
!= aRange1
.second
; ++aIter1
)
2857 SwTextAttr
*const p1
= aIter1
->pTextAttr
;
2858 NoteInHistory( p1
);
2859 p1
->SetEnd(nNewPortionEnd
);
2860 NoteInHistory( p1
, true );
2871 // when not merging the ignore flags need to be either set or reset
2872 // (reset too in case one of the autofmts was recently changed)
2873 bool const bSetIgnoreFlag(DIFFER_ONLY_RSID
== eMerge
);
2874 for (auto aIter1
= aRange1
.first
; aIter1
!= aRange1
.second
; ++aIter1
)
2876 if (!aIter1
->isRsidOnlyAutoFormat
) // already set above, don't change
2878 SwTextAttr
*const pCurrent(aIter1
->pTextAttr
);
2879 if (pCurrent
->IsFormatIgnoreEnd() != bSetIgnoreFlag
)
2881 NoteInHistory(pCurrent
);
2882 pCurrent
->SetFormatIgnoreEnd(bSetIgnoreFlag
);
2883 NoteInHistory(pCurrent
, true);
2887 for (auto aIter2
= aRange2
.first
; aIter2
!= aRange2
.second
; ++aIter2
)
2889 if (!aIter2
->isRsidOnlyAutoFormat
) // already set above, don't change
2891 SwTextAttr
*const pCurrent(aIter2
->pTextAttr
);
2892 if (pCurrent
->IsFormatIgnoreStart() != bSetIgnoreFlag
)
2894 NoteInHistory(pCurrent
);
2895 pCurrent
->SetFormatIgnoreStart(bSetIgnoreFlag
);
2896 NoteInHistory(pCurrent
, true);
2900 i
= j
; // ++i not enough: i + 1 may have been deleted (MATCH)!
2909 static MergeResult
lcl_Compare_Attributes(
2911 const std::pair
< PortionMap::const_iterator
, PortionMap::const_iterator
>& aRange1
,
2912 const std::pair
< PortionMap::const_iterator
, PortionMap::const_iterator
>& aRange2
,
2913 std::vector
<bool>& RsidOnlyAutoFormatFlagMap
)
2915 PortionMap::const_iterator aIter1
= aRange1
.first
;
2916 PortionMap::const_iterator aIter2
= aRange2
.first
;
2918 size_t const nAttributesInPor1
= std::distance(aRange1
.first
, aRange1
.second
);
2919 size_t const nAttributesInPor2
= std::distance(aRange2
.first
, aRange2
.second
);
2920 bool const isRsidOnlyAutoFormat1
= i
< sal_Int32(RsidOnlyAutoFormatFlagMap
.size()) && RsidOnlyAutoFormatFlagMap
[i
];
2921 bool const isRsidOnlyAutoFormat2
= j
< sal_Int32(RsidOnlyAutoFormatFlagMap
.size()) && RsidOnlyAutoFormatFlagMap
[j
];
2923 // if both have one they could be equal, but not if only one has it
2924 bool const bSkipRsidOnlyAutoFormat(nAttributesInPor1
!= nAttributesInPor2
);
2926 // this loop needs to handle the case where one has a CHARFMT and the
2927 // other CHARFMT + RSID-only AUTOFMT, so...
2928 // want to skip over RSID-only AUTOFMT here, hence the -1
2929 if (!((nAttributesInPor1
- (isRsidOnlyAutoFormat1
? 1 : 0)) ==
2930 (nAttributesInPor2
- (isRsidOnlyAutoFormat2
? 1 : 0))
2931 && (nAttributesInPor1
!= 0 || nAttributesInPor2
!= 0)))
2936 MergeResult
eMerge(MATCH
);
2938 // _if_ there is one element more either in aRange1 or aRange2
2939 // it _must_ be an RSID-only AUTOFMT, which can be ignored here...
2940 // But if both have RSID-only AUTOFMT they could be equal, no skip!
2941 while (aIter1
!= aRange1
.second
|| aIter2
!= aRange2
.second
)
2943 // first of all test if there's no gap (before skipping stuff!)
2944 if (aIter1
!= aRange1
.second
&& aIter2
!= aRange2
.second
&&
2945 *aIter1
->pTextAttr
->End() < aIter2
->pTextAttr
->GetStart())
2949 // skip it - cannot be equal if bSkipRsidOnlyAutoFormat is set
2950 if (bSkipRsidOnlyAutoFormat
2951 && aIter1
!= aRange1
.second
&& aIter1
->isRsidOnlyAutoFormat
)
2953 assert(DIFFER
!= eMerge
);
2954 eMerge
= DIFFER_ONLY_RSID
;
2958 if (bSkipRsidOnlyAutoFormat
2959 && aIter2
!= aRange2
.second
&& aIter2
->isRsidOnlyAutoFormat
)
2961 assert(DIFFER
!= eMerge
);
2962 eMerge
= DIFFER_ONLY_RSID
;
2966 assert(aIter1
!= aRange1
.second
&& aIter2
!= aRange2
.second
);
2967 SwTextAttr
const*const p1
= aIter1
->pTextAttr
;
2968 SwTextAttr
const*const p2
= aIter2
->pTextAttr
;
2969 if (p1
->Which() != p2
->Which())
2975 // fdo#52028: for auto styles, check if they differ only
2976 // in the RSID, which should have no effect on text layout
2977 if (RES_TXTATR_AUTOFMT
!= p1
->Which())
2980 const SfxItemSet
& rSet1
= *p1
->GetAutoFormat().GetStyleHandle();
2981 const SfxItemSet
& rSet2
= *p2
->GetAutoFormat().GetStyleHandle();
2983 // sadly SfxItemSet::operator== does not seem to work?
2984 SfxItemIter
iter1(rSet1
);
2985 SfxItemIter
iter2(rSet2
);
2986 for (SfxPoolItem
const* pItem1
= iter1
.GetCurItem(),
2987 * pItem2
= iter2
.GetCurItem();;)
2989 if (pItem1
&& pItem1
->Which() == RES_CHRATR_RSID
)
2990 pItem1
= iter1
.NextItem();
2991 if (pItem2
&& pItem2
->Which() == RES_CHRATR_RSID
)
2992 pItem2
= iter2
.NextItem();
2994 if (nullptr == pItem1
&& nullptr == pItem2
)
2996 eMerge
= DIFFER_ONLY_RSID
;
3000 if (nullptr == pItem1
|| nullptr == pItem2
)
3002 // one ptr is nullptr, not both, that would
3003 // have triggered above
3007 if (!SfxPoolItem::areSame(*pItem1
, *pItem2
))
3011 pItem1
= iter1
.NextItem();
3012 pItem2
= iter2
.NextItem();
3022 // check if there is already a character format and adjust the sort numbers
3023 static void lcl_CheckSortNumber( const SwpHints
& rHints
, SwTextCharFormat
& rNewCharFormat
)
3025 const sal_Int32 nHtStart
= rNewCharFormat
.GetStart();
3026 const sal_Int32 nHtEnd
= *rNewCharFormat
.GetEnd();
3027 sal_uInt16 nSortNumber
= 0;
3029 for ( size_t i
= 0; i
< rHints
.Count(); ++i
)
3031 const SwTextAttr
* pOtherHt
= rHints
.Get(i
);
3033 const sal_Int32 nOtherStart
= pOtherHt
->GetStart();
3035 if ( nOtherStart
> nHtStart
)
3038 if ( RES_TXTATR_CHARFMT
== pOtherHt
->Which() )
3040 const sal_Int32 nOtherEnd
= *pOtherHt
->End();
3042 if ( nOtherStart
== nHtStart
&& nOtherEnd
== nHtEnd
)
3044 nSortNumber
= static_txtattr_cast
<const SwTextCharFormat
*>(pOtherHt
)->GetSortNumber() + 1;
3049 if ( nSortNumber
> 0 )
3050 rNewCharFormat
.SetSortNumber( nSortNumber
);
3054 * Try to insert the new hint.
3055 * Depending on the type of the hint, this either always succeeds, or may fail.
3056 * Depending on the type of the hint, other hints may be deleted or
3058 * The return value indicates successful insertion.
3060 bool SwpHints::TryInsertHint(
3061 SwTextAttr
* const pHint
,
3063 const SetAttrMode nMode
)
3065 if ( MAX_HINTS
<= Count() ) // we're sorry, this flight is overbooked...
3067 OSL_FAIL("hints array full :-(");
3068 rNode
.DestroyAttr(pHint
);
3072 const sal_Int32
*pHtEnd
= pHint
->GetEnd();
3073 const sal_uInt16 nWhich
= pHint
->Which();
3074 std::vector
<sal_uInt16
> aWhichSublist
;
3078 case RES_TXTATR_CHARFMT
:
3080 // Check if character format contains hidden attribute:
3081 const SwCharFormat
* pFormat
= pHint
->GetCharFormat().GetCharFormat();
3082 if ( SfxItemState::SET
== pFormat
->GetItemState( RES_CHRATR_HIDDEN
) )
3083 rNode
.SetCalcHiddenCharFlags();
3085 static_txtattr_cast
<SwTextCharFormat
*>(pHint
)->ChgTextNode( &rNode
);
3088 // #i75430# Recalc hidden flags if necessary
3089 case RES_TXTATR_AUTOFMT
:
3091 std::shared_ptr
<SfxItemSet
> const & pSet( pHint
->GetAutoFormat().GetStyleHandle() );
3092 if (pHint
->GetStart() == *pHint
->GetEnd())
3094 if (pSet
->Count() == 1 && pSet
->GetItem(RES_CHRATR_RSID
, false))
3095 { // empty range RSID-only hints could cause trouble, there's no
3096 rNode
.DestroyAttr(pHint
); // need for them so don't insert
3100 // Check if auto style contains hidden attribute:
3101 const SfxPoolItem
* pHiddenItem
= CharFormat::GetItem( *pHint
, RES_CHRATR_HIDDEN
);
3103 rNode
.SetCalcHiddenCharFlags();
3105 // fdo#71556: populate aWhichFormatAttr member of SwMsgPoolItem
3106 pSet
->CollectHasItems(aWhichSublist
);
3109 case RES_TXTATR_INETFMT
:
3110 static_txtattr_cast
<SwTextINetFormat
*>(pHint
)->InitINetFormat(rNode
);
3113 case RES_TXTATR_FIELD
:
3114 case RES_TXTATR_ANNOTATION
:
3115 case RES_TXTATR_INPUTFIELD
:
3117 SwTextField
*const pTextField(static_txtattr_cast
<SwTextField
*>(pHint
));
3118 bool bDelFirst
= nullptr != pTextField
->GetpTextNode();
3119 pTextField
->ChgTextNode( &rNode
);
3120 SwDoc
& rDoc
= rNode
.GetDoc();
3121 const SwField
* pField
= pTextField
->GetFormatField().GetField();
3123 if( !rDoc
.getIDocumentFieldsAccess().IsNewFieldLst() )
3125 // certain fields must update the SwDoc's calculation flags
3126 switch( pField
->GetTyp()->Which() )
3128 case SwFieldIds::Database
:
3129 case SwFieldIds::SetExp
:
3130 case SwFieldIds::HiddenPara
:
3131 case SwFieldIds::HiddenText
:
3132 case SwFieldIds::DbNumSet
:
3133 case SwFieldIds::DbNextSet
:
3136 rDoc
.getIDocumentFieldsAccess().InsDelFieldInFieldLst(false, *pTextField
);
3137 if( rNode
.GetNodes().IsDocNodes() )
3138 rDoc
.getIDocumentFieldsAccess().InsDelFieldInFieldLst(true, *pTextField
);
3141 case SwFieldIds::Dde
:
3142 if( rNode
.GetNodes().IsDocNodes() )
3143 static_cast<SwDDEFieldType
*>(pField
->GetTyp())->IncRefCnt();
3149 // insert into real document's nodes-array?
3150 if( rNode
.GetNodes().IsDocNodes() )
3152 bool bInsFieldType
= false;
3153 switch( pField
->GetTyp()->Which() )
3155 case SwFieldIds::SetExp
:
3156 bInsFieldType
= static_cast<SwSetExpFieldType
*>(pField
->GetTyp())->IsDeleted();
3157 if( nsSwGetSetExpType::GSE_SEQ
& static_cast<SwSetExpFieldType
*>(pField
->GetTyp())->GetType() )
3159 // register the field at its FieldType before setting
3160 // the sequence reference number!
3161 SwSetExpFieldType
* pFieldType
= static_cast<SwSetExpFieldType
*>(
3162 rDoc
.getIDocumentFieldsAccess().InsertFieldType( *pField
->GetTyp() ) );
3163 if( pFieldType
!= pField
->GetTyp() )
3165 SwFormatField
* pFormatField
= const_cast<SwFormatField
*>(&pTextField
->GetFormatField());
3166 pFormatField
->RegisterToFieldType( *pFieldType
);
3167 pFormatField
->GetField()->ChgTyp( pFieldType
);
3169 pFieldType
->SetSeqRefNo( *const_cast<SwSetExpField
*>(static_cast<const SwSetExpField
*>(pField
)) );
3172 case SwFieldIds::User
:
3173 bInsFieldType
= static_cast<SwUserFieldType
*>(pField
->GetTyp())->IsDeleted();
3176 case SwFieldIds::Dde
:
3177 if( rDoc
.getIDocumentFieldsAccess().IsNewFieldLst() )
3178 static_cast<SwDDEFieldType
*>(pField
->GetTyp())->IncRefCnt();
3179 bInsFieldType
= static_cast<SwDDEFieldType
*>(pField
->GetTyp())->IsDeleted();
3182 case SwFieldIds::Postit
:
3183 if ( rDoc
.GetDocShell() )
3185 rDoc
.GetDocShell()->Broadcast( SwFormatFieldHint(
3186 &pTextField
->GetFormatField(), SwFormatFieldHintWhich::INSERTED
));
3192 rDoc
.getIDocumentFieldsAccess().InsDeletedFieldType( *pField
->GetTyp() );
3196 case RES_TXTATR_FTN
:
3197 static_cast<SwTextFootnote
*>(pHint
)->ChgTextNode( &rNode
);
3199 case RES_TXTATR_REFMARK
:
3200 static_txtattr_cast
<SwTextRefMark
*>(pHint
)->ChgTextNode( &rNode
);
3201 if( rNode
.GetNodes().IsDocNodes() )
3203 // search for a reference with the same name
3205 size_t n
= 0, nEnd
= Count();
3208 const sal_Int32
*pTmpHtEnd
;
3209 const sal_Int32
*pTmpHintEnd
;
3210 if (RES_TXTATR_REFMARK
== (pTmpHt
= Get(n
))->Which() &&
3211 pHint
->GetAttr() == pTmpHt
->GetAttr() &&
3212 nullptr != ( pTmpHtEnd
= pTmpHt
->GetEnd() ) &&
3213 nullptr != ( pTmpHintEnd
= pHint
->GetEnd() ) )
3215 SwComparePosition eCmp
= ::ComparePosition(
3216 pTmpHt
->GetStart(), *pTmpHtEnd
,
3217 pHint
->GetStart(), *pTmpHintEnd
);
3218 bool bDelOld
= true, bChgStart
= false, bChgEnd
= false;
3221 case SwComparePosition::Before
:
3222 case SwComparePosition::Behind
: bDelOld
= false; break;
3224 case SwComparePosition::Outside
: bChgStart
= bChgEnd
= true; break;
3226 case SwComparePosition::CollideEnd
:
3227 case SwComparePosition::OverlapBefore
: bChgStart
= true; break;
3228 case SwComparePosition::CollideStart
:
3229 case SwComparePosition::OverlapBehind
: bChgEnd
= true; break;
3235 pHint
->SetStart( pTmpHt
->GetStart() );
3238 pHint
->SetEnd(*pTmpHtEnd
);
3242 NoteInHistory( pTmpHt
);
3243 rNode
.DestroyAttr( Cut( n
) );
3245 continue; // n removed, nEnd adjusted
3252 case RES_TXTATR_TOXMARK
:
3253 static_txtattr_cast
<SwTextTOXMark
*>(pHint
)->ChgTextNode( &rNode
);
3256 case RES_TXTATR_CJK_RUBY
:
3257 static_txtattr_cast
<SwTextRuby
*>(pHint
)->InitRuby(rNode
);
3260 case RES_TXTATR_META
:
3261 case RES_TXTATR_METAFIELD
:
3262 static_txtattr_cast
<SwTextMeta
*>(pHint
)->ChgTextNode( &rNode
);
3265 case RES_TXTATR_CONTENTCONTROL
:
3266 static_txtattr_cast
<SwTextContentControl
*>(pHint
)->ChgTextNode( &rNode
);
3269 case RES_CHRATR_HIDDEN
:
3270 rNode
.SetCalcHiddenCharFlags();
3274 if( SetAttrMode::DONTEXPAND
& nMode
)
3275 pHint
->SetDontExpand( true );
3277 // special handling for SwTextAttrs without end:
3278 // 1) they cannot overlap
3279 // 2) if two fields are adjacent, they must not be merged into one
3280 // this is guaranteed by inserting a CH_TXTATR_* into the paragraph text!
3281 sal_Int32 nHtStart
= pHint
->GetStart();
3285 NoteInHistory(pHint
, true);
3288 if( !rNode
.GetDoc().IsInReading() )
3291 // ... and notify listeners
3292 if(rNode
.HasWriterListeners())
3299 rNode
.TriggerNodeUpdate(sw::UpdateAttrHint(&aHint
, &aHint
));
3305 // from here on, pHint is known to have an end index!
3307 if( *pHtEnd
< nHtStart
)
3309 assert(*pHtEnd
>= nHtStart
);
3311 // just swap the nonsense:
3312 pHint
->SetStart(*pHtEnd
);
3313 pHint
->SetEnd(nHtStart
);
3314 nHtStart
= pHint
->GetStart();
3317 // I need this value later on for notification but the pointer may become invalid
3318 const sal_Int32 nHintEnd
= *pHtEnd
;
3319 const bool bNoHintAdjustMode
= bool(SetAttrMode::NOHINTADJUST
& nMode
);
3321 // handle nesting attributes: inserting may fail due to overlap!
3322 if (pHint
->IsNesting())
3325 TryInsertNesting(rNode
, *static_txtattr_cast
<SwTextAttrNesting
*>(pHint
)));
3326 if (!bRet
) return false;
3328 // Currently REFMARK and TOXMARK have OverlapAllowed set to true.
3329 // These attributes may be inserted directly.
3330 // Also attributes without length may be inserted directly.
3331 // SETATTR_NOHINTADJUST is set e.g., during undo.
3332 // Portion building in not necessary during XML import.
3333 else if ( !bNoHintAdjustMode
&&
3334 !pHint
->IsOverlapAllowedAttr() &&
3335 !rNode
.GetDoc().IsInXMLImport() &&
3336 ( RES_TXTATR_AUTOFMT
== nWhich
||
3337 RES_TXTATR_CHARFMT
== nWhich
) )
3339 assert( nWhich
!= RES_TXTATR_AUTOFMT
||
3340 static_cast<const SwFormatAutoFormat
&>(pHint
->GetAttr()).GetStyleHandle()->GetPool() ==
3341 &rNode
.GetDoc().GetAttrPool());
3343 BuildPortions( rNode
, *pHint
, nMode
);
3345 if ( nHtStart
< nHintEnd
) // skip merging for 0-length attributes
3346 MergePortions( rNode
);
3350 // There may be more than one character style at the current position.
3351 // Take care of the sort number.
3352 // FME 2007-11-08 #i82989# in NOHINTADJUST mode, we want to insert
3353 // character attributes directly
3354 if (!bNoHintAdjustMode
3355 && ( (RES_TXTATR_CHARFMT
== nWhich
)
3356 // tdf#149978 also for import of automatic styles, could be produced by non-LO application
3357 || (RES_TXTATR_AUTOFMT
== nWhich
&& rNode
.GetDoc().IsInXMLImport())))
3359 BuildPortions( rNode
, *pHint
, nMode
);
3363 // #i82989# Check sort numbers in NoHintAdjustMode
3364 if ( RES_TXTATR_CHARFMT
== nWhich
)
3365 lcl_CheckSortNumber(*this, *static_txtattr_cast
<SwTextCharFormat
*>(pHint
));
3368 NoteInHistory( pHint
, true );
3372 // ... and notify listeners
3373 if ( rNode
.HasWriterListeners() )
3375 const SwUpdateAttr
aHint(nHtStart
, nHintEnd
, nWhich
, std::move(aWhichSublist
));
3376 rNode
.TriggerNodeUpdate(sw::UpdateAttrHint(&aHint
, &aHint
));
3380 if( !bNoHintAdjustMode
&& !rNode
.GetDoc().IsInReading() )
3387 void SwpHints::DeleteAtPos( const size_t nPos
)
3389 assert(m_StartMapNeedsSortingRange
.first
== SAL_MAX_INT32
&& "deleting at pos and the list needs sorting?");
3391 SwTextAttr
*pHint
= Get(nPos
);
3392 assert( pHint
->m_pHints
== this );
3393 // ChainDelete( pHint );
3394 NoteInHistory( pHint
);
3396 // optimization: nPos is the position in the Starts array
3397 SwTextAttr
*pHt
= m_HintsByStart
[ nPos
];
3398 m_HintsByStart
.erase( m_HintsByStart
.begin() + nPos
);
3400 if (m_StartMapNeedsSortingRange
.first
!= SAL_MAX_INT32
)
3402 if (m_EndMapNeedsSortingRange
.first
!= SAL_MAX_INT32
)
3404 if (m_WhichMapNeedsSortingRange
.first
.first
!= SAL_MAX_INT32
)
3407 auto findIt
= std::lower_bound(m_HintsByEnd
.begin(), m_HintsByEnd
.end(), pHt
, CompareSwpHtEnd());
3408 assert(*findIt
== pHt
);
3409 m_HintsByEnd
.erase(findIt
);
3411 auto findIt2
= std::lower_bound(m_HintsByWhichAndStart
.begin(), m_HintsByWhichAndStart
.end(), pHt
, CompareSwpHtWhichStart());
3412 assert(*findIt2
== pHt
);
3413 m_HintsByWhichAndStart
.erase(findIt2
);
3415 pHt
->m_pHints
= nullptr;
3417 if( pHint
->Which() == RES_TXTATR_FIELD
)
3419 SwTextField
*const pTextField(static_txtattr_cast
<SwTextField
*>(pHint
));
3420 const SwFieldType
* pFieldTyp
= pTextField
->GetFormatField().GetField()->GetTyp();
3421 if( SwFieldIds::Dde
== pFieldTyp
->Which() )
3423 const SwTextNode
* pNd
= pTextField
->GetpTextNode();
3424 if( pNd
&& pNd
->GetNodes().IsDocNodes() )
3425 const_cast<SwDDEFieldType
*>(static_cast<const SwDDEFieldType
*>(pFieldTyp
))->DecRefCnt();
3426 pTextField
->ChgTextNode(nullptr);
3428 else if (m_bHiddenByParaField
3429 && m_rParent
.GetDoc().FieldCanHideParaWeight(pFieldTyp
->Which()))
3431 m_bCalcHiddenParaField
= true;
3434 else if ( pHint
->Which() == RES_TXTATR_ANNOTATION
)
3436 SwTextField
*const pTextField(static_txtattr_cast
<SwTextField
*>(pHint
));
3437 const_cast<SwFormatField
&>(pTextField
->GetFormatField()).Broadcast(
3438 SwFormatFieldHint(&pTextField
->GetFormatField(), SwFormatFieldHintWhich::REMOVED
));
3442 CHECK_NOTMERGED
; // called from BuildPortions
3446 /// precondition: pTextHt must be in this array
3447 void SwpHints::Delete( SwTextAttr
const * pTextHt
)
3449 const size_t nPos
= GetIndexOf( pTextHt
);
3450 assert(SAL_MAX_SIZE
!= nPos
);
3451 if( SAL_MAX_SIZE
!= nPos
)
3452 DeleteAtPos( nPos
);
3455 void SwTextNode::ClearSwpHintsArr( bool bDelFields
)
3461 while ( nPos
< m_pSwpHints
->Count() )
3463 SwTextAttr
* pDel
= m_pSwpHints
->Get( nPos
);
3466 switch( pDel
->Which() )
3468 case RES_TXTATR_FLYCNT
:
3469 case RES_TXTATR_FTN
:
3472 case RES_TXTATR_FIELD
:
3473 case RES_TXTATR_ANNOTATION
:
3474 case RES_TXTATR_INPUTFIELD
:
3484 m_pSwpHints
->DeleteAtPos( nPos
);
3485 DestroyAttr( pDel
);
3492 LanguageType
SwTextNode::GetLang( const sal_Int32 nBegin
, const sal_Int32 nLen
,
3493 sal_uInt16 nScript
, bool const bNoneIfNoHyphenation
) const
3495 LanguageType nRet
= LANGUAGE_DONTKNOW
;
3499 nScript
= g_pBreakIt
->GetRealScriptOfText( m_Text
, nBegin
);
3502 // #i91465# Consider nScript if pSwpHints == 0
3503 const sal_uInt16 nWhichId
= bNoneIfNoHyphenation
3504 ? RES_CHRATR_NOHYPHEN
3505 : GetWhichOfScript( RES_CHRATR_LANGUAGE
, nScript
);
3509 const sal_Int32 nEnd
= nBegin
+ nLen
;
3510 const size_t nSize
= m_pSwpHints
->Count();
3511 for ( size_t i
= 0; i
< nSize
; ++i
)
3513 const SwTextAttr
*pHt
= m_pSwpHints
->Get(i
);
3514 const sal_Int32 nAttrStart
= pHt
->GetStart();
3515 if( nEnd
< nAttrStart
)
3518 const sal_uInt16 nWhich
= pHt
->Which();
3520 if( nWhichId
== nWhich
||
3521 ( ( pHt
->IsCharFormatAttr() || RES_TXTATR_AUTOFMT
== nWhich
) && CharFormat::IsItemIncluded( nWhichId
, pHt
) ) )
3523 const sal_Int32
*pEndIdx
= pHt
->End();
3524 // do the attribute and the range overlap?
3529 if( nAttrStart
>= nEnd
|| nBegin
>= *pEndIdx
)
3532 else if( nBegin
!= nAttrStart
|| ( nAttrStart
!= *pEndIdx
&& nBegin
))
3534 if( nAttrStart
>= nBegin
)
3536 if( pHt
->DontExpand() ? nBegin
>= *pEndIdx
: nBegin
> *pEndIdx
)
3539 const SfxPoolItem
* pItem
= CharFormat::GetItem( *pHt
, nWhichId
);
3541 if ( RES_CHRATR_NOHYPHEN
== nWhichId
)
3543 // bNoneIfNoHyphenation = true: return with LANGUAGE_NONE,
3544 // if the hyphenation is disabled by character formatting
3545 if ( static_cast<const SvxNoHyphenItem
*>(pItem
)->GetValue() )
3546 return LANGUAGE_NONE
;
3550 const LanguageType nLng
= static_cast<const SvxLanguageItem
*>(pItem
)->GetLanguage();
3552 // does the attribute completely cover the range?
3553 if( nAttrStart
<= nBegin
&& nEnd
<= *pEndIdx
)
3555 else if( LANGUAGE_DONTKNOW
== nRet
)
3556 nRet
= nLng
; // partial overlap, the first one wins
3560 if( LANGUAGE_DONTKNOW
== nRet
&& !bNoneIfNoHyphenation
)
3562 nRet
= static_cast<const SvxLanguageItem
&>(GetSwAttrSet().Get( nWhichId
)).GetLanguage();
3563 if( LANGUAGE_DONTKNOW
== nRet
)
3564 nRet
= GetAppLanguage();
3569 sal_Unicode
GetCharOfTextAttr( const SwTextAttr
& rAttr
)
3571 sal_Unicode cRet
= CH_TXTATR_BREAKWORD
;
3572 switch ( rAttr
.Which() )
3574 case RES_TXTATR_REFMARK
:
3575 case RES_TXTATR_TOXMARK
:
3576 case RES_TXTATR_ANNOTATION
:
3577 cRet
= CH_TXTATR_INWORD
;
3580 case RES_TXTATR_FIELD
:
3581 case RES_TXTATR_FLYCNT
:
3582 case RES_TXTATR_FTN
:
3583 case RES_TXTATR_META
:
3584 case RES_TXTATR_METAFIELD
:
3585 case RES_TXTATR_CONTENTCONTROL
:
3587 cRet
= CH_TXTATR_BREAKWORD
;
3590 case RES_TXTATR_LINEBREAK
:
3592 cRet
= CH_TXTATR_NEWLINE
;
3597 assert(!"GetCharOfTextAttr: unknown attr");
3603 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */