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 <hintids.hxx>
23 #include <comphelper/lok.hxx>
24 #include <comphelper/string.hxx>
25 #include <editeng/fontitem.hxx>
26 #include <editeng/escapementitem.hxx>
27 #include <editeng/lrspitem.hxx>
28 #include <editeng/rsiditem.hxx>
29 #include <sal/log.hxx>
30 #include <osl/diagnose.h>
31 #include <anchoredobject.hxx>
33 #include <txtinet.hxx>
34 #include <fmtanchr.hxx>
35 #include <fmtinfmt.hxx>
36 #include <fmtrfmrk.hxx>
37 #include <txttxmrk.hxx>
38 #include <fchrfmt.hxx>
40 #include <fmtflcnt.hxx>
44 #include <ftninfo.hxx>
46 #include <charfmt.hxx>
49 #include <IDocumentUndoRedo.hxx>
50 #include <IDocumentSettingAccess.hxx>
51 #include <IDocumentListsAccess.hxx>
52 #include <IDocumentRedlineAccess.hxx>
53 #include <IDocumentLayoutAccess.hxx>
60 #include <pagefrm.hxx>
61 #include <rootfrm.hxx>
63 #include <section.hxx>
65 #include <SwGrammarMarkUp.hxx>
66 #include <redline.hxx>
68 #include <scriptinfo.hxx>
69 #include <istyleaccess.hxx>
70 #include <SwStyleNameMapper.hxx>
71 #include <numrule.hxx>
73 #include <SwNodeNum.hxx>
74 #include <svl/grabbagitem.hxx>
75 #include <svl/intitem.hxx>
76 #include <sortedobjs.hxx>
78 #include <attrhint.hxx>
80 #include <unoparagraph.hxx>
82 #include <fmtpdsc.hxx>
83 #include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
84 #include <svl/itemiter.hxx>
86 using namespace ::com::sun::star
;
88 typedef std::vector
<SwTextAttr
*> SwpHts
;
91 class TextNodeNotificationSuppressor
{
93 bool m_bWasNotifiable
;
95 TextNodeNotificationSuppressor(SwTextNode
& rNode
)
97 , m_bWasNotifiable(rNode
.m_bNotifiable
)
99 m_rNode
.m_bNotifiable
= false;
101 ~TextNodeNotificationSuppressor()
103 m_rNode
.m_bNotifiable
= m_bWasNotifiable
;
108 // unfortunately everyone can change Hints without ensuring order or the linking between them
110 #define CHECK_SWPHINTS(pNd) { if( pNd->GetpSwpHints() && \
111 !pNd->GetDoc().IsInReading() ) \
112 pNd->GetpSwpHints()->Check(true); }
113 #define CHECK_SWPHINTS_IF_FRM(pNd) { if( pNd->GetpSwpHints() && \
114 !pNd->GetDoc().IsInReading() ) \
115 pNd->GetpSwpHints()->Check(getLayoutFrame(nullptr, nullptr, nullptr) != nullptr); }
117 #define CHECK_SWPHINTS(pNd)
118 #define CHECK_SWPHINTS_IF_FRM(pNd)
121 SwTextNode
*SwNodes::MakeTextNode( SwNode
& rWhere
,
122 SwTextFormatColl
*pColl
, bool const bNewFrames
)
124 OSL_ENSURE( pColl
, "Collection pointer is 0." );
126 SwTextNode
*pNode
= new SwTextNode( rWhere
, pColl
, nullptr );
128 SwNodeIndex
aIdx( *pNode
);
130 // if there is no layout or it is in a hidden section, MakeFrames is not needed
131 const SwSectionNode
* pSectNd
;
133 !GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() ||
134 ( nullptr != (pSectNd
= pNode
->FindSectionNode()) &&
135 pSectNd
->GetSection().IsHiddenFlag() ))
138 SwNodeIndex
aTmp( rWhere
);
141 // 1. take the successor
142 // 2. take the predecessor
144 SwNode
* pNd
= & aTmp
.GetNode();
145 switch (pNd
->GetNodeType())
147 case SwNodeType::Table
:
148 static_cast<SwTableNode
*>(pNd
)->MakeFramesForAdjacentContentNode(aIdx
);
151 case SwNodeType::Section
:
152 if( static_cast<SwSectionNode
*>(pNd
)->GetSection().IsHidden() ||
153 static_cast<SwSectionNode
*>(pNd
)->IsContentHidden() )
155 pNd
= FindPrvNxtFrameNode( *pNode
, pNode
);
161 static_cast<SwSectionNode
*>(pNd
)->MakeFramesForAdjacentContentNode(aIdx
);
164 case SwNodeType::Text
:
165 case SwNodeType::Grf
:
166 case SwNodeType::Ole
:
167 static_cast<SwContentNode
*>(pNd
)->MakeFramesForAdjacentContentNode(*pNode
);
170 case SwNodeType::End
:
171 if( pNd
->StartOfSectionNode()->IsSectionNode() &&
172 aTmp
.GetIndex() < rWhere
.GetIndex() )
174 if( pNd
->StartOfSectionNode()->GetSectionNode()->GetSection().IsHiddenFlag())
176 if( !GoPrevSection( &aTmp
, true, false ) ||
177 aTmp
.GetNode().FindTableNode() !=
178 pNode
->FindTableNode() )
182 aTmp
= *pNd
->StartOfSectionNode();
185 else if( pNd
->StartOfSectionNode()->IsTableNode() &&
186 aTmp
.GetIndex() < rWhere
.GetIndex() )
188 // after a table node
189 aTmp
= *pNd
->StartOfSectionNode();
194 if( &rWhere
== &aTmp
.GetNode() )
195 aTmp
-= SwNodeOffset(2);
203 SwTextNode::SwTextNode( SwNode
& rWhere
, SwTextFormatColl
*pTextColl
, const SfxItemSet
* pAutoAttr
)
204 : SwContentNode( rWhere
, SwNodeType::Text
, pTextColl
),
205 m_bContainsHiddenChars(false),
206 m_bHiddenCharsHidePara(false),
207 m_bRecalcHiddenCharFlags(false),
208 m_bLastOutlineState( false ),
209 m_bNotifiable( true ),
210 mbEmptyListStyleSetDueToSetOutlineLevelAttr( false ),
211 mbInSetOrResetAttr( false ),
215 sw::TextNodeNotificationSuppressor(*this);
218 SetAttr( *pAutoAttr
);
220 if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
223 // apply paragraph style's assigned outline style list level as
224 // list level of the paragraph, if it has none set already.
225 if ( !HasAttrListLevel() &&
226 pTextColl
&& pTextColl
->IsAssignedToListLevelOfOutlineStyle() )
228 SetAttrListLevel( pTextColl
->GetAssignedOutlineStyleLevel() );
233 // call method <UpdateOutlineNode(..)> only for the document nodes array
234 if (GetNodes().IsDocNodes())
235 GetNodes().UpdateOutlineNode(*this);
238 m_bContainsHiddenChars
= m_bHiddenCharsHidePara
= false;
239 m_bRecalcHiddenCharFlags
= true;
242 SwTextNode::~SwTextNode()
244 // delete only removes the pointer not the array elements!
247 // do not delete attributes twice when those delete their content
248 std::unique_ptr
<SwpHints
> pTmpHints(std::move(m_pSwpHints
));
250 for( size_t j
= pTmpHints
->Count(); j
; )
252 // first remove the attribute from the array otherwise
253 // if would delete itself
254 DestroyAttr( pTmpHints
->Get( --j
) );
258 // must be removed from outline nodes by now
259 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
260 SwOutlineNodes::size_type foo
;
261 assert(!GetNodes().GetOutLineNds().Seek_Entry(this, &foo
));
266 DelFrames(nullptr); // must be called here while it's still a SwTextNode
267 DelFrames_TextNodePart();
268 #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
269 if (!GetDoc().IsInDtor())
270 ResetAttr(RES_PAGEDESC
);
272 ResetAttr(RES_PAGEDESC
);
274 InvalidateInSwCache(RES_OBJECTDYING
);
277 void SwTextNode::FileLoadedInitHints()
281 m_pSwpHints
->MergePortions(*this);
285 SwContentFrame
*SwTextNode::MakeFrame( SwFrame
* pSib
)
287 SwContentFrame
*pFrame
= sw::MakeTextFrame(*this, pSib
, sw::FrameMode::New
);
291 sal_Int32
SwTextNode::Len() const
293 return m_Text
.getLength();
296 // After a split node, it's necessary to actualize the ref-pointer of the ftnfrms.
297 static void lcl_ChangeFootnoteRef( SwTextNode
&rNode
)
299 SwpHints
*pSwpHints
= rNode
.GetpSwpHints();
300 if( !(pSwpHints
&& rNode
.GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()) )
303 SwContentFrame
* pFrame
= nullptr;
304 // OD 07.11.2002 #104840# - local variable to remember first footnote
305 // of node <rNode> in order to invalidate position of its first content.
306 // Thus, in its <MakeAll()> it will checked its position relative to its reference.
307 SwFootnoteFrame
* pFirstFootnoteOfNode
= nullptr;
308 for( size_t j
= pSwpHints
->Count(); j
; )
310 SwTextAttr
* pHt
= pSwpHints
->Get(--j
);
311 if (RES_TXTATR_FTN
== pHt
->Which())
315 pFrame
= SwIterator
<SwContentFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
>(rNode
).First();
319 SwTextFootnote
*pAttr
= static_cast<SwTextFootnote
*>(pHt
);
320 OSL_ENSURE( pAttr
->GetStartNode(), "FootnoteAtr without StartNode." );
321 SwNodeIndex
aIdx( *pAttr
->GetStartNode(), 1 );
322 SwContentNode
*pNd
= aIdx
.GetNode().GetContentNode();
324 pNd
= pFrame
->GetAttrSet()->GetDoc()->
325 GetNodes().GoNextSection( &aIdx
, true, false );
329 SwIterator
<SwContentFrame
, SwContentNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pNd
);
330 SwContentFrame
* pContent
= aIter
.First();
333 OSL_ENSURE( pContent
->getRootFrame() == pFrame
->getRootFrame(),
334 "lcl_ChangeFootnoteRef: Layout double?" );
335 SwFootnoteFrame
*pFootnote
= pContent
->FindFootnoteFrame();
336 if( pFootnote
&& pFootnote
->GetAttr() == pAttr
)
338 while( pFootnote
->GetMaster() )
339 pFootnote
= pFootnote
->GetMaster();
340 // #104840# - remember footnote frame
341 pFirstFootnoteOfNode
= pFootnote
;
344 pFootnote
->SetRef( pFrame
);
345 pFootnote
= pFootnote
->GetFollow();
346 static_cast<SwTextFrame
*>(pFrame
)->SetFootnote( true );
349 #if OSL_DEBUG_LEVEL > 0
350 while( nullptr != (pContent
= aIter
.Next()) )
352 SwFootnoteFrame
*pDbgFootnote
= pContent
->FindFootnoteFrame();
353 OSL_ENSURE( !pDbgFootnote
|| pDbgFootnote
->GetRef() == pFrame
,
354 "lcl_ChangeFootnoteRef: Who's that guy?" );
359 } // end of for-loop on <SwpHints>
360 // #104840# - invalidate
361 if ( pFirstFootnoteOfNode
)
363 SwContentFrame
* pContent
= pFirstFootnoteOfNode
->ContainsContent();
366 pContent
->InvalidatePos_();
373 // check if there are flys on the existing frames (now on "pNode")
374 // that need to be moved to the new frames of "this"
375 void MoveMergedFlysAndFootnotes(std::vector
<SwTextFrame
*> const& rFrames
,
376 SwTextNode
const& rFirstNode
, SwTextNode
& rSecondNode
,
381 lcl_ChangeFootnoteRef(rSecondNode
);
383 for (SwNodeOffset nIndex
= rSecondNode
.GetIndex() + 1; ; ++nIndex
)
385 SwNode
*const pTmp(rSecondNode
.GetNodes()[nIndex
]);
386 if (pTmp
->IsCreateFrameWhenHidingRedlines() || pTmp
->IsEndNode())
390 else if (pTmp
->IsStartNode())
392 nIndex
= pTmp
->EndOfSectionIndex();
394 else if (pTmp
->GetRedlineMergeFlag() == SwNode::Merge::NonFirst
395 && pTmp
->IsTextNode())
397 lcl_ChangeFootnoteRef(*pTmp
->GetTextNode());
400 for (SwTextFrame
*const pFrame
: rFrames
)
402 if (SwSortedObjs
*const pObjs
= pFrame
->GetDrawObjs())
404 std::vector
<SwAnchoredObject
*> objs
;
405 objs
.reserve(pObjs
->size());
406 for (SwAnchoredObject
*const pObj
: *pObjs
)
408 objs
.push_back(pObj
);
410 for (SwAnchoredObject
*const pObj
: objs
)
412 SwFrameFormat
& rFormat(pObj
->GetFrameFormat());
413 SwFormatAnchor
const& rAnchor(rFormat
.GetAnchor());
414 if (rFirstNode
.GetIndex() < rAnchor
.GetAnchorNode()->GetIndex())
416 // move it to the new frame of "this"
417 rFormat
.CallSwClientNotify(sw::LegacyModifyHint(&rAnchor
, &rAnchor
));
418 // note pObjs will be deleted if it becomes empty
419 assert(!pFrame
->GetDrawObjs() || !pObjs
->Contains(*pObj
));
428 SwTextNode
*SwTextNode::SplitContentNode(const SwPosition
& rPos
,
429 std::function
<void (SwTextNode
*, sw::mark::RestoreMode
, bool AtStart
)> const*const pContentIndexRestore
)
432 SwNode::Merge
const eOldMergeFlag(GetRedlineMergeFlag());
433 bool parentIsOutline
= IsOutline();
435 // create a node "in front" of me
436 const sal_Int32 nSplitPos
= rPos
.GetContentIndex();
437 const sal_Int32 nTextLen
= m_Text
.getLength();
438 SwTextNode
* const pNode
=
439 MakeNewTextNode( rPos
.GetNode(), false, nSplitPos
==nTextLen
);
441 // the first paragraph gets the XmlId,
442 // _except_ if it is empty and the second is not empty
443 if (nSplitPos
!= 0) {
444 pNode
->RegisterAsCopyOf(*this, true);
445 if (nSplitPos
== nTextLen
)
447 RemoveMetadataReference();
448 // NB: SwUndoSplitNode will call pNode->JoinNext,
449 // which is sufficient even in this case!
453 ResetAttr( RES_PARATR_LIST_ISRESTART
);
454 ResetAttr( RES_PARATR_LIST_RESTARTVALUE
);
455 ResetAttr( RES_PARATR_LIST_ISCOUNTED
);
456 if ( GetNumRule() == nullptr || (parentIsOutline
&& !IsOutline()) )
458 ResetAttr( RES_PARATR_LIST_ID
);
459 ResetAttr( RES_PARATR_LIST_LEVEL
);
462 if ( HasWriterListeners() && !m_Text
.isEmpty() && (nTextLen
/ 2) < nSplitPos
)
464 // optimization for SplitNode: If a split is at the end of a node then
465 // move the frames from the current to the new one and create new ones
466 // for the current one.
468 // If fly frames are moved, they don't need to destroy their layout
469 // frames. Set a flag that is checked in SwTextFlyCnt::SetAnchor.
472 pNode
->GetOrCreateSwpHints().SetInSplitNode(true);
475 // Move the first part of the content to the new node and delete
476 // it in the old node.
477 SwContentIndex
aIdx( this );
478 CutText( pNode
, aIdx
, nSplitPos
);
482 pNode
->SetWrong( GetWrong()->SplitList( nSplitPos
) );
484 SetWrongDirty(sw::WrongState::TODO
);
486 if( GetGrammarCheck() )
488 pNode
->SetGrammarCheck( GetGrammarCheck()->SplitGrammarList( nSplitPos
) );
490 SetGrammarCheckDirty( true );
492 SetWordCountDirty( true );
496 pNode
->SetSmartTags( GetSmartTags()->SplitList( nSplitPos
) );
498 SetSmartTagDirty( true );
500 resetAndQueueAccessibilityCheck();
501 pNode
->resetAndQueueAccessibilityCheck();
503 if ( pNode
->HasHints() )
505 if ( pNode
->m_pSwpHints
->CanBeDeleted() )
507 pNode
->m_pSwpHints
.reset();
511 pNode
->m_pSwpHints
->SetInSplitNode(false);
514 // All fly frames anchored as char that are moved to the new
515 // node must have their layout frames deleted.
516 // This comment is sort of silly because we actually delete the
517 // layout frames of those which were not moved?
518 // JP 01.10.96: delete all empty and not-to-be-expanded attributes
521 for ( size_t j
= m_pSwpHints
->Count(); j
; )
523 SwTextAttr
* const pHt
= m_pSwpHints
->Get( --j
);
524 if ( RES_TXTATR_FLYCNT
== pHt
->Which() )
526 pHt
->GetFlyCnt().GetFrameFormat()->DelFrames();
528 else if ( pHt
->DontExpand() )
530 const sal_Int32
* const pEnd
= pHt
->GetEnd();
531 if (pEnd
&& pHt
->GetStart() == *pEnd
)
534 m_pSwpHints
->DeleteAtPos( j
);
543 if (pContentIndexRestore
)
544 { // call before making frames and before RegisterToNode
545 (*pContentIndexRestore
)(pNode
, sw::mark::RestoreMode::NonFlys
, false);
547 if (eOldMergeFlag
!= SwNode::Merge::None
)
548 { // clear before making frames and before RegisterToNode
549 SetRedlineMergeFlag(SwNode::Merge::None
);
550 } // now RegisterToNode will set merge flags in both nodes properly!
552 std::vector
<SwTextFrame
*> frames
;
553 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*this);
554 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
556 if (pFrame
->getRootFrame()->HasMergedParas())
560 frames
.push_back(pFrame
);
562 for (SwTextFrame
* pFrame
: frames
)
564 pFrame
->RegisterToNode( *pNode
);
565 if (!pFrame
->IsFollow() && pFrame
->GetOffset())
567 pFrame
->SetOffset( TextFrameIndex(0) );
571 InvalidateInSwCache(RES_ATTRSET_CHG
);
575 MoveTextAttr_To_AttrSet();
577 // in case there are frames, the RegisterToNode has set the merge flag
578 pNode
->MakeFramesForAdjacentContentNode(*this);
579 lcl_ChangeFootnoteRef( *this );
580 if (pContentIndexRestore
)
581 { // call after making frames; listeners will take care of adding to the right frame
582 (*pContentIndexRestore
)(pNode
, sw::mark::RestoreMode::Flys
, false);
584 if (eOldMergeFlag
!= SwNode::Merge::None
)
586 MoveMergedFlysAndFootnotes(frames
, *pNode
, *this, true);
591 std::unique_ptr
<SwWrongList
> pList
= ReleaseWrong();
592 SetWrongDirty(sw::WrongState::TODO
);
594 std::unique_ptr
<SwGrammarMarkUp
> pList3
= ReleaseGrammarCheck();
595 SetGrammarCheckDirty( true );
597 SetWordCountDirty( true );
599 std::unique_ptr
<SwWrongList
> pList2
= ReleaseSmartTags();
600 SetSmartTagDirty( true );
602 SwContentIndex
aIdx( this );
603 CutText( pNode
, aIdx
, nSplitPos
);
605 // JP 01.10.96: delete all empty and not-to-be-expanded attributes
608 for ( size_t j
= m_pSwpHints
->Count(); j
; )
610 SwTextAttr
* const pHt
= m_pSwpHints
->Get( --j
);
611 const sal_Int32
* const pEnd
= pHt
->GetEnd();
612 if ( pHt
->DontExpand() && pEnd
&& (pHt
->GetStart() == *pEnd
) )
615 m_pSwpHints
->DeleteAtPos( j
);
619 MoveTextAttr_To_AttrSet();
624 pNode
->SetWrong( pList
->SplitList( nSplitPos
) );
625 SetWrong( std::move(pList
) );
630 pNode
->SetGrammarCheck( pList3
->SplitGrammarList( nSplitPos
) );
631 SetGrammarCheck( std::move(pList3
) );
636 pNode
->SetSmartTags( pList2
->SplitList( nSplitPos
) );
637 SetSmartTags( std::move(pList2
) );
640 resetAndQueueAccessibilityCheck();
641 pNode
->resetAndQueueAccessibilityCheck();
643 if (pContentIndexRestore
)
644 { // call before making frames and before RegisterToNode
645 (*pContentIndexRestore
)(pNode
, sw::mark::RestoreMode::NonFlys
, false);
648 std::vector
<SwTextFrame
*> frames
;
649 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*this);
650 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
652 frames
.push_back(pFrame
);
653 if (pFrame
->getRootFrame()->HasMergedParas())
658 bool bNonMerged(false);
659 bool bRecreateThis(false);
660 for (SwTextFrame
* pFrame
: frames
)
662 // sw_redlinehide: for this to work properly with hidden nodes,
663 // the frame needs to listen on them too.
664 // also: have to check the frame; this->GetRedlineMergeFlag()
665 // is None in case there's a delete redline inside the paragraph,
666 // but that could still result in a merged frame after split...
667 if (pFrame
->GetMergedPara())
669 // Can't special case this == First here - that could (if
670 // both nodes are still merged by redline) lead to
671 // duplicate frames on "this".
672 // Update the extents with new node; also inits merge flag,
673 // so the MakeFramesForAdjacentContentNode below respects it
674 pFrame
->RegisterToNode(*pNode
);
677 // in this case, it was not
678 // invalidated because Cut didn't sent it any hints,
679 // so we have to invalidate it here!
680 pFrame
->Prepare(PrepareHint::Clear
, nullptr, false);
682 if (!pFrame
->GetMergedPara() ||
683 !pFrame
->GetMergedPara()->listener
.IsListeningTo(this))
685 // it's no longer listening - need to recreate frame
686 // (note this is idempotent, can be done once per frame)
687 SetRedlineMergeFlag(SwNode::Merge::None
);
688 bRecreateThis
= true;
696 assert(!(bNonMerged
&& bRecreateThis
)); // 2 layouts not handled yet - maybe best to simply use the other branch then?
697 if (!frames
.empty() && bNonMerged
)
699 // the existing frame on "this" should have been updated by Cut
700 MakeFramesForAdjacentContentNode(*pNode
);
701 lcl_ChangeFootnoteRef(*pNode
);
703 else if (bRecreateThis
)
705 assert(pNode
->HasWriterListeners()); // was just moved there
706 pNode
->MakeFramesForAdjacentContentNode(*this);
707 lcl_ChangeFootnoteRef(*this);
710 if (pContentIndexRestore
)
711 { // call after making frames; listeners will take care of adding to the right frame
712 (*pContentIndexRestore
)(pNode
, sw::mark::RestoreMode::Flys
, nSplitPos
== 0);
717 MoveMergedFlysAndFootnotes(frames
, *pNode
, *this, true);
721 // pNode is the previous node, 'this' is the next node from the split.
722 if (nSplitPos
== nTextLen
&& m_pSwpHints
)
724 // We just created an empty next node: avoid unwanted superscript in the new node if it's
726 for (size_t i
= 0; i
< m_pSwpHints
->Count(); ++i
)
728 SwTextAttr
* pHt
= m_pSwpHints
->Get(i
);
729 if (pHt
->Which() != RES_TXTATR_AUTOFMT
)
734 const sal_Int32
* pEnd
= pHt
->GetEnd();
735 if (!pEnd
|| pHt
->GetStart() != *pEnd
)
740 const std::shared_ptr
<SfxItemSet
>& pSet
= pHt
->GetAutoFormat().GetStyleHandle();
741 if (!pSet
|| pSet
->Count() != 1 || !pSet
->HasItem(RES_CHRATR_ESCAPEMENT
))
746 m_pSwpHints
->DeleteAtPos(i
);
747 SwTextAttr::Destroy(pHt
, GetDoc().GetAttrPool());
753 if (isHide
) // otherwise flags won't be set anyway
760 // -> Hidden,Hidden (if still inside merge rl)
761 // -> NonFirst,First (if redline was split)
763 // -> NonFirst,First (if split after end of "incoming" redline &
764 // before start of "outgoing" redline)
765 // -> NonFirst,None (if split after end of "incoming" redline)
766 // -> NonFirst,Hidden (if split after start of "outgoing" redline)
767 // -> Hidden, NonFirst (if split before end of "incoming" redline)
770 // -> First,NonFirst (if splitting inside a delete redline)
771 SwNode::Merge
const eFirst(pNode
->GetRedlineMergeFlag());
772 SwNode::Merge
const eSecond(GetRedlineMergeFlag());
773 switch (eOldMergeFlag
)
776 assert((eFirst
== Merge::First
&& eSecond
== Merge::NonFirst
)
777 || (eFirst
== Merge::First
&& eSecond
== Merge::Hidden
)
778 || (eFirst
== Merge::None
&& eSecond
== Merge::First
));
781 assert((eFirst
== Merge::Hidden
&& eSecond
== Merge::Hidden
)
782 || (eFirst
== Merge::NonFirst
&& eSecond
== Merge::First
)
783 // next ones can happen temp. in UndoDelete :(
784 || (eFirst
== Merge::Hidden
&& eSecond
== Merge::NonFirst
)
785 || (eFirst
== Merge::NonFirst
&& eSecond
== Merge::None
));
787 case Merge::NonFirst
:
788 assert((eFirst
== Merge::NonFirst
&& eSecond
== Merge::First
)
789 || (eFirst
== Merge::NonFirst
&& eSecond
== Merge::None
)
790 || (eFirst
== Merge::NonFirst
&& eSecond
== Merge::Hidden
)
791 || (eFirst
== Merge::Hidden
&& eSecond
== Merge::NonFirst
));
794 assert((eFirst
== Merge::None
&& eSecond
== Merge::None
)
795 || (eFirst
== Merge::First
&& eSecond
== Merge::NonFirst
));
804 // Send Hint for PageDesc. This should be done in the Layout when
805 // pasting the frames, but that causes other problems that look
806 // expensive to solve.
807 const SwFormatPageDesc
*pItem
;
808 if(HasWriterListeners() && (pItem
= pNode
->GetSwAttrSet().GetItemIfSet(RES_PAGEDESC
)))
809 pNode
->TriggerNodeUpdate(sw::LegacyModifyHint(pItem
, pItem
));
814 void SwTextNode::MoveTextAttr_To_AttrSet()
816 OSL_ENSURE( m_pSwpHints
, "MoveTextAttr_To_AttrSet without SwpHints?" );
817 for ( size_t i
= 0; m_pSwpHints
&& i
< m_pSwpHints
->Count(); ++i
)
819 SwTextAttr
*pHt
= m_pSwpHints
->Get(i
);
821 if( pHt
->GetStart() )
824 const sal_Int32
* pHtEndIdx
= pHt
->GetEnd();
829 if (*pHtEndIdx
< m_Text
.getLength() || pHt
->IsCharFormatAttr())
832 if( !pHt
->IsDontMoveAttr() &&
833 SetAttr( pHt
->GetAttr() ) )
835 m_pSwpHints
->DeleteAtPos(i
);
845 /// if first node is deleted & second survives, then the first node's frame
846 /// will be deleted too; prevent this by moving the frame to the second node
848 void MoveDeletedPrevFrames(const SwTextNode
& rDeletedPrev
, SwTextNode
& rNode
)
850 std::vector
<SwTextFrame
*> frames
;
851 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(rDeletedPrev
);
852 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
854 if (pFrame
->getRootFrame()->HasMergedParas())
856 frames
.push_back(pFrame
);
860 auto frames2(frames
);
861 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIt(rNode
);
862 for (SwTextFrame
* pFrame
= aIt
.First(); pFrame
; pFrame
= aIt
.Next())
864 if (pFrame
->getRootFrame()->HasMergedParas())
866 auto const it(std::find(frames2
.begin(), frames2
.end(), pFrame
));
867 assert(it
!= frames2
.end());
871 assert(frames2
.empty());
873 for (SwTextFrame
*const pFrame
: frames
)
875 pFrame
->RegisterToNode(rNode
, true);
882 // First,NonFirst->First
883 // NonFirst,First->NonFirst
884 // NonFirst,None->NonFirst
886 /// if first node is First, its frames may need to be moved, never deleted.
887 /// if first node is NonFirst, second node's own frames (First/None) must be deleted
888 void CheckResetRedlineMergeFlag(SwTextNode
& rNode
, Recreate
const eRecreateMerged
)
890 if (eRecreateMerged
!= sw::Recreate::No
)
892 SwTextNode
* pMergeNode(&rNode
);
893 if (eRecreateMerged
== sw::Recreate::Predecessor
894 // tdf#135018 check that there is a predecessor node, i.e. rNode
895 // isn't the first node after the body start node
896 && rNode
.GetNodes()[rNode
.GetIndex() - 1]->StartOfSectionIndex() != SwNodeOffset(0))
898 for (SwNodeOffset i
= rNode
.GetIndex() - 1; ; --i
)
900 SwNode
*const pNode(rNode
.GetNodes()[i
]);
901 assert(!pNode
->IsStartNode());
902 if (pNode
->IsEndNode())
904 i
= pNode
->StartOfSectionIndex();
906 else if (pNode
->IsTextNode())
908 pMergeNode
= pNode
->GetTextNode(); // use predecessor to merge
913 std::vector
<SwTextFrame
*> frames
;
914 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pMergeNode
);
915 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
917 if (pFrame
->getRootFrame()->HasMergedParas())
919 frames
.push_back(pFrame
);
922 auto eMode(sw::FrameMode::Existing
);
923 for (SwTextFrame
* pFrame
: frames
)
925 SwTextNode
& rFirstNode(pFrame
->GetMergedPara()
926 ? *pFrame
->GetMergedPara()->pFirstNode
928 assert(rFirstNode
.GetIndex() <= rNode
.GetIndex());
929 pFrame
->SetMergedPara(sw::CheckParaRedlineMerge(
930 *pFrame
, rFirstNode
, eMode
));
931 // there is no merged para in case the deleted node had one but
932 // nothing was actually hidden
933 if (pFrame
->GetMergedPara())
935 assert(pFrame
->GetMergedPara()->listener
.IsListeningTo(&rNode
));
936 assert(rNode
.GetIndex() <= pFrame
->GetMergedPara()->pLastNode
->GetIndex());
937 // tdf#135978 Join: recreate fly frames anchored to subsequent nodes
938 if (eRecreateMerged
== sw::Recreate::ThisNode
)
940 AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame
, rNode
, nullptr);
943 eMode
= sw::FrameMode::New
; // Existing is not idempotent!
946 else if (rNode
.GetRedlineMergeFlag() != SwNode::Merge::None
)
948 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(rNode
);
949 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
951 if (auto const pMergedPara
= pFrame
->GetMergedPara())
953 if (pMergedPara
->pFirstNode
== pMergedPara
->pLastNode
)
955 assert(pMergedPara
->pFirstNode
== &rNode
);
956 rNode
.SetRedlineMergeFlag(SwNode::Merge::None
);
958 break; // checking once is enough
960 else if (pFrame
->getRootFrame()->HasMergedParas())
962 rNode
.SetRedlineMergeFlag(SwNode::Merge::None
);
963 break; // checking once is enough
969 bool HasNumberingWhichNeedsLayoutUpdate(const SwTextNode
& rTextNode
)
971 const SwNodeNum
* pNodeNum
= rTextNode
.GetNum();
977 const SwNumRule
* pNumRule
= pNodeNum
->GetNumRule();
983 const SwNumFormat
* pFormat
984 = pNumRule
->GetNumFormat(o3tl::narrowing
<sal_uInt16
>(rTextNode
.GetAttrListLevel()));
990 switch (pFormat
->GetNumberingType())
992 case SVX_NUM_NUMBER_NONE
:
993 case SVX_NUM_CHAR_SPECIAL
:
1002 SwContentNode
*SwTextNode::JoinNext()
1004 SwNodes
& rNds
= GetNodes();
1005 SwNodeIndex
aIdx( *this );
1006 if( SwContentNode::CanJoinNext( &aIdx
) )
1008 SwDoc
& rDoc
= rNds
.GetDoc();
1009 const std::shared_ptr
<sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
1010 pContentStore
->Save(rDoc
, aIdx
.GetIndex(), SAL_MAX_INT32
);
1011 SwTextNode
*pTextNode
= aIdx
.GetNode().GetTextNode();
1012 sal_Int32 nOldLen
= m_Text
.getLength();
1015 JoinMetadatable(*pTextNode
, !Len(), !pTextNode
->Len());
1017 std::unique_ptr
<SwWrongList
> pList
= ReleaseWrong();
1020 pList
->JoinList( pTextNode
->GetWrong(), nOldLen
);
1021 SetWrongDirty(sw::WrongState::TODO
);
1025 pList
= pTextNode
->ReleaseWrong();
1028 pList
->Move( 0, nOldLen
);
1029 SetWrongDirty(sw::WrongState::TODO
);
1033 std::unique_ptr
<SwGrammarMarkUp
> pList3
= ReleaseGrammarCheck();
1036 pList3
->JoinGrammarList( pTextNode
->GetGrammarCheck(), nOldLen
);
1037 SetGrammarCheckDirty( true );
1041 pList3
= pTextNode
->ReleaseGrammarCheck();
1044 pList3
->MoveGrammar( 0, nOldLen
);
1045 SetGrammarCheckDirty( true );
1049 std::unique_ptr
<SwWrongList
> pList2
= ReleaseSmartTags();
1052 pList2
->JoinList( pTextNode
->GetSmartTags(), nOldLen
);
1053 SetSmartTagDirty( true );
1057 pList2
= pTextNode
->ReleaseSmartTags();
1060 pList2
->Move( 0, nOldLen
);
1061 SetSmartTagDirty( true );
1065 { // scope for SwContentIndex
1066 pTextNode
->CutText( this, SwContentIndex(pTextNode
), pTextNode
->Len() );
1068 // move all Bookmarks/TOXMarks
1069 if( !pContentStore
->Empty())
1070 pContentStore
->Restore( rDoc
, GetIndex(), nOldLen
);
1072 if( pTextNode
->HasAnyIndex() )
1074 // move all ShellCursor/StackCursor/UnoCursor out of delete range
1075 rDoc
.CorrAbs( aIdx
.GetNode(), SwPosition( *this ), nOldLen
, true );
1077 SwNode::Merge
const eOldMergeFlag(pTextNode
->GetRedlineMergeFlag());
1078 auto eRecreateMerged(eOldMergeFlag
== SwNode::Merge::First
1079 ? sw::Recreate::ThisNode
1080 : sw::Recreate::No
);
1081 if (eRecreateMerged
== sw::Recreate::No
)
1083 // tdf#137318 if a delete is inside one node, flag is still None!
1084 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pTextNode
);
1085 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
1087 if (pFrame
->GetMergedPara())
1089 eRecreateMerged
= sw::Recreate::ThisNode
;
1094 bool bOldHasNumberingWhichNeedsLayoutUpdate
= HasNumberingWhichNeedsLayoutUpdate(*pTextNode
);
1097 SetWrong( std::move(pList
) );
1098 SetGrammarCheck( std::move(pList3
) );
1099 SetSmartTags( std::move(pList2
) );
1101 resetAndQueueAccessibilityCheck();
1103 if (bOldHasNumberingWhichNeedsLayoutUpdate
|| HasNumberingWhichNeedsLayoutUpdate(*this))
1105 // Repaint all text frames that belong to this numbering to avoid outdated generated
1107 InvalidateNumRule();
1110 CheckResetRedlineMergeFlag(*this, eRecreateMerged
);
1113 OSL_FAIL( "No TextNode." );
1119 void SwTextNode::JoinPrev()
1121 SwNodes
& rNds
= GetNodes();
1122 SwNodeIndex
aIdx( *this );
1123 if( SwContentNode::CanJoinPrev( &aIdx
) )
1125 SwDoc
& rDoc
= rNds
.GetDoc();
1126 const std::shared_ptr
<sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
1127 pContentStore
->Save( rDoc
, aIdx
.GetIndex(), SAL_MAX_INT32
);
1128 SwTextNode
*pTextNode
= aIdx
.GetNode().GetTextNode();
1129 const sal_Int32 nLen
= pTextNode
->Len();
1131 std::unique_ptr
<SwWrongList
> pList
= pTextNode
->ReleaseWrong();
1134 pList
->JoinList( GetWrong(), Len() );
1135 SetWrongDirty(sw::WrongState::TODO
);
1140 pList
= ReleaseWrong();
1143 pList
->Move( 0, nLen
);
1144 SetWrongDirty(sw::WrongState::TODO
);
1148 std::unique_ptr
<SwGrammarMarkUp
> pList3
= pTextNode
->ReleaseGrammarCheck();
1151 pList3
->JoinGrammarList( GetGrammarCheck(), Len() );
1152 SetGrammarCheckDirty( true );
1153 ClearGrammarCheck();
1157 pList3
= ReleaseGrammarCheck();
1160 pList3
->MoveGrammar( 0, nLen
);
1161 SetGrammarCheckDirty( true );
1165 std::unique_ptr
<SwWrongList
> pList2
= pTextNode
->ReleaseSmartTags();
1168 pList2
->JoinList( GetSmartTags(), Len() );
1169 SetSmartTagDirty( true );
1174 pList2
= ReleaseSmartTags();
1177 pList2
->Move( 0, nLen
);
1178 SetSmartTagDirty( true );
1182 { // scope for SwContentIndex
1183 pTextNode
->CutText( this, SwContentIndex(this), SwContentIndex(pTextNode
), nLen
);
1185 // move all Bookmarks/TOXMarks
1186 if( !pContentStore
->Empty() )
1187 pContentStore
->Restore( rDoc
, GetIndex() );
1189 if( pTextNode
->HasAnyIndex() )
1191 // move all ShellCursor/StackCursor/UnoCursor out of delete range
1192 rDoc
.CorrAbs( aIdx
.GetNode(), SwPosition( *this ), nLen
, true );
1194 SwNode::Merge
const eOldMergeFlag(pTextNode
->GetRedlineMergeFlag());
1195 if (eOldMergeFlag
== SwNode::Merge::First
1196 && !IsCreateFrameWhenHidingRedlines())
1198 sw::MoveDeletedPrevFrames(*pTextNode
, *this);
1201 SetWrong( std::move(pList
) );
1202 SetGrammarCheck( std::move(pList3
) );
1203 SetSmartTags( std::move(pList2
) );
1204 resetAndQueueAccessibilityCheck();
1205 InvalidateNumRule();
1206 sw::CheckResetRedlineMergeFlag(*this,
1207 eOldMergeFlag
== SwNode::Merge::NonFirst
1208 ? sw::Recreate::Predecessor
1209 : sw::Recreate::No
);
1212 OSL_FAIL( "No TextNode." );
1216 // create an AttrSet with ranges for Frame-/Para/Char-attributes
1217 void SwTextNode::NewAttrSet( SwAttrPool
& rPool
)
1219 OSL_ENSURE( !mpAttrSet
, "AttrSet is set after all" );
1220 SwAttrSet
aNewAttrSet( rPool
, aTextNodeSetRange
);
1222 // put names of parent style and conditional style:
1223 const SwFormatColl
* pAnyFormatColl
= &GetAnyFormatColl();
1224 const SwFormatColl
* pFormatColl
= GetFormatColl();
1226 SwStyleNameMapper::FillProgName( pAnyFormatColl
->GetName(), sVal
, SwGetPoolIdFromName::TxtColl
);
1227 SfxStringItem
aAnyFormatColl( RES_FRMATR_STYLE_NAME
, sVal
);
1228 if ( pFormatColl
!= pAnyFormatColl
)
1229 SwStyleNameMapper::FillProgName( pFormatColl
->GetName(), sVal
, SwGetPoolIdFromName::TxtColl
);
1230 SfxStringItem
aFormatColl( RES_FRMATR_CONDITIONAL_STYLE_NAME
, sVal
);
1231 aNewAttrSet
.Put( aAnyFormatColl
);
1232 aNewAttrSet
.Put( aFormatColl
);
1234 aNewAttrSet
.SetParent( &pAnyFormatColl
->GetAttrSet() );
1235 mpAttrSet
= GetDoc().GetIStyleAccess().getAutomaticStyle( aNewAttrSet
, IStyleAccess::AUTO_STYLE_PARA
, &sVal
);
1240 class SwContentNodeTmp
: public SwContentNode
1243 SwContentNodeTmp() : SwContentNode() {}
1244 virtual void NewAttrSet(SwAttrPool
&) override
{}
1245 virtual SwContentFrame
*MakeFrame(SwFrame
*) override
{ return nullptr; }
1246 virtual SwContentNode
* MakeCopy(SwDoc
&, SwNode
&, bool /*bNewFrames*/) const override
{ return nullptr; };
1250 // override SwContentIndexReg::Update => text hints do not need SwContentIndex for start/end!
1251 void SwTextNode::Update(
1252 SwContentIndex
const & rPos
,
1253 const sal_Int32 nChangeLen
,
1254 UpdateMode
const eMode
)
1256 assert(rPos
.GetContentNode() == this);
1257 SetAutoCompleteWordDirty( true );
1259 std::unique_ptr
<SwpHts
> pCollector
;
1260 const sal_Int32 nChangePos
= rPos
.GetIndex();
1264 if (eMode
& UpdateMode::Negative
)
1266 std::vector
<SwTextInputField
*> aTextInputFields
;
1268 const sal_Int32 nChangeEnd
= nChangePos
+ nChangeLen
;
1269 for ( size_t n
= 0; n
< m_pSwpHints
->Count(); ++n
)
1271 bool bTextAttrChanged
= false;
1272 bool bStartOfTextAttrChanged
= false;
1273 SwTextAttr
* const pHint
= m_pSwpHints
->GetWithoutResorting(n
);
1274 if ( pHint
->GetStart() > nChangePos
)
1276 if ( pHint
->GetStart() > nChangeEnd
)
1278 pHint
->SetStart( pHint
->GetStart() - nChangeLen
);
1282 pHint
->SetStart( nChangePos
);
1284 bStartOfTextAttrChanged
= true;
1287 const sal_Int32
* const pEnd
= pHint
->GetEnd();
1288 if (pEnd
&& *pEnd
> nChangePos
)
1290 if( *pEnd
> nChangeEnd
)
1292 pHint
->SetEnd(*pEnd
- nChangeLen
);
1296 pHint
->SetEnd(nChangePos
);
1298 bTextAttrChanged
= !bStartOfTextAttrChanged
;
1301 if ( bTextAttrChanged
1302 && pHint
->Which() == RES_TXTATR_INPUTFIELD
)
1304 SwTextInputField
* pTextInputField
= dynamic_cast<SwTextInputField
*>(pHint
);
1305 if ( pTextInputField
)
1306 aTextInputFields
.push_back(pTextInputField
);
1310 //wait until all the attribute positions are correct
1311 //before updating the field contents
1312 for (SwTextInputField
* pTextInputField
: aTextInputFields
)
1314 pTextInputField
->UpdateFieldContent();
1317 m_pSwpHints
->MergePortions( *this );
1321 bool bNoExp
= false;
1322 bool bResort
= false;
1323 bool bMergePortionsNeeded
= false;
1324 const int coArrSz
= RES_TXTATR_WITHEND_END
- RES_CHRATR_BEGIN
;
1325 std::vector
<SwTextInputField
*> aTextInputFields
;
1327 bool aDontExp
[ coArrSz
] = {};
1329 for ( size_t n
= 0; n
< m_pSwpHints
->Count(); ++n
)
1331 bool bTextAttrChanged
= false;
1332 SwTextAttr
* const pHint
= m_pSwpHints
->GetWithoutResorting(n
);
1333 const sal_Int32
* const pEnd
= pHint
->GetEnd();
1334 if ( pHint
->GetStart() >= nChangePos
)
1336 pHint
->SetStart( pHint
->GetStart() + nChangeLen
);
1339 pHint
->SetEnd(*pEnd
+ nChangeLen
);
1342 else if ( pEnd
&& (*pEnd
>= nChangePos
) )
1344 if ( (*pEnd
> nChangePos
) || IsIgnoreDontExpand() )
1346 pHint
->SetEnd(*pEnd
+ nChangeLen
);
1347 bTextAttrChanged
= true;
1349 else // *pEnd == nChangePos
1351 const sal_uInt16 nWhich
= pHint
->Which();
1353 OSL_ENSURE(!isCHRATR(nWhich
), "Update: char attr hint?");
1354 if (!(isCHRATR(nWhich
) || isTXTATR_WITHEND(nWhich
)))
1357 const sal_uInt16 nWhPos
= nWhich
- RES_CHRATR_BEGIN
;
1359 if( aDontExp
[ nWhPos
] )
1362 if ( pHint
->DontExpand() )
1364 pHint
->SetDontExpand( false );
1366 // could have a continuation with IgnoreStart()...
1367 if (pHint
->IsFormatIgnoreEnd())
1369 bMergePortionsNeeded
= true;
1371 if ( pHint
->IsCharFormatAttr() )
1374 aDontExp
[ RES_TXTATR_CHARFMT
- RES_CHRATR_BEGIN
] = true;
1375 aDontExp
[ RES_TXTATR_INETFMT
- RES_CHRATR_BEGIN
] = true;
1378 aDontExp
[ nWhPos
] = true;
1384 pCollector
.reset( new SwpHts
);
1386 auto it
= std::find_if(pCollector
->begin(), pCollector
->end(),
1387 [nWhich
](const SwTextAttr
*pTmp
) { return nWhich
== pTmp
->Which(); });
1388 if (it
!= pCollector
->end())
1390 SwTextAttr
*pTmp
= *it
;
1391 pCollector
->erase( it
);
1392 SwTextAttr::Destroy( pTmp
, GetDoc().GetAttrPool() );
1394 SwTextAttr
* const pTmp
=
1395 MakeTextAttr( GetDoc(),
1396 pHint
->GetAttr(), nChangePos
, nChangePos
+ nChangeLen
);
1397 pCollector
->push_back( pTmp
);
1401 pHint
->SetEnd(*pEnd
+ nChangeLen
);
1402 bTextAttrChanged
= true;
1407 if ( bTextAttrChanged
1408 && pHint
->Which() == RES_TXTATR_INPUTFIELD
)
1410 SwTextInputField
* pTextInputField
= dynamic_cast<SwTextInputField
*>(pHint
);
1411 if ( pTextInputField
)
1412 aTextInputFields
.push_back(pTextInputField
);
1416 //wait until all the attribute positions are correct
1417 //before updating the field contents
1418 for (SwTextInputField
* pTextInputField
: aTextInputFields
)
1420 pTextInputField
->UpdateFieldContent();
1423 if (bMergePortionsNeeded
)
1425 m_pSwpHints
->MergePortions(*this); // does Resort too
1429 m_pSwpHints
->Resort();
1434 bool bSortMarks
= false;
1435 SwContentNodeTmp aTmpIdxReg
;
1436 if (!(eMode
& UpdateMode::Negative
) && !(eMode
& UpdateMode::Delete
))
1438 std::vector
<SwRangeRedline
*> vMyRedlines
;
1439 // walk the list of SwIndex attached to me and see if any of them are redlines
1440 const SwContentIndex
* pContentNodeIndex
= GetFirstIndex();
1441 while (pContentNodeIndex
)
1443 SwRangeRedline
* pRedl
= pContentNodeIndex
->GetRedline();
1445 vMyRedlines
.push_back(pRedl
);
1446 pContentNodeIndex
= pContentNodeIndex
->GetNext();
1448 std::sort(vMyRedlines
.begin(), vMyRedlines
.end());
1449 vMyRedlines
.erase( std::unique( vMyRedlines
.begin(), vMyRedlines
.end() ), vMyRedlines
.end() );
1450 for (SwRangeRedline
* pRedl
: vMyRedlines
)
1452 if ( pRedl
->HasMark() )
1454 SwPosition
* const pEnd
= pRedl
->End();
1455 if ( *this == pEnd
->GetNode() &&
1456 *pRedl
->GetPoint() != *pRedl
->GetMark() )
1458 SwContentIndex
& rIdx
= pEnd
->nContent
;
1459 if (nChangePos
== rIdx
.GetIndex())
1461 rIdx
.Assign( &aTmpIdxReg
, rIdx
.GetIndex() );
1465 else if ( this == &pRedl
->GetPoint()->GetNode() )
1467 SwContentIndex
& rIdx
= pRedl
->GetPoint()->nContent
;
1468 if (nChangePos
== rIdx
.GetIndex())
1470 rIdx
.Assign( &aTmpIdxReg
, rIdx
.GetIndex() );
1472 // the unused position must not be on a SwTextNode
1473 bool const isOneUsed(&pRedl
->GetBound() == pRedl
->GetPoint());
1474 assert(!pRedl
->GetBound(!isOneUsed
).GetNode().IsTextNode());
1475 assert(!pRedl
->GetBound(!isOneUsed
).GetContentNode()); (void)isOneUsed
;
1479 // Bookmarks must never grow to either side, when editing (directly)
1480 // to the left or right (i#29942)! Exception: if the bookmark has
1481 // 2 positions and start == end, then expand it (tdf#96479)
1482 if (!(eMode
& UpdateMode::Replace
)) // Exception: Replace
1484 bool bAtLeastOneBookmarkMoved
= false;
1485 bool bAtLeastOneExpandedBookmarkAtInsertionPosition
= false;
1486 // A text node already knows its marks via its SwContentIndexes.
1487 o3tl::sorted_vector
<const sw::mark::IMark
*> aSeenMarks
;
1488 const SwContentIndex
* next
;
1489 for (const SwContentIndex
* pIndex
= GetFirstIndex(); pIndex
; pIndex
= next
)
1491 next
= pIndex
->GetNext();
1492 const sw::mark::IMark
* pMark
= pIndex
->GetMark();
1495 // Only handle bookmarks once, if they start and end at this node as well.
1496 if (!aSeenMarks
.insert(pMark
).second
)
1498 const SwPosition
* pEnd
= &pMark
->GetMarkEnd();
1499 SwContentIndex
& rEndIdx
= const_cast<SwContentIndex
&>(pEnd
->nContent
);
1500 if( *this == pEnd
->GetNode() &&
1501 rPos
.GetIndex() == rEndIdx
.GetIndex() )
1503 if (&rEndIdx
== next
) // nasty corner case:
1504 { // don't switch to iterating aTmpIdxReg!
1505 next
= rEndIdx
.GetNext();
1507 // tdf#96479: if start == end, ignore the other position
1509 rEndIdx
.Assign( &aTmpIdxReg
, rEndIdx
.GetIndex() );
1510 bAtLeastOneBookmarkMoved
= true;
1512 else if ( !bAtLeastOneExpandedBookmarkAtInsertionPosition
)
1514 if ( pMark
->IsExpanded() )
1516 const SwPosition
* pStart
= &pMark
->GetMarkStart();
1517 if ( this == &pStart
->GetNode()
1518 && rPos
.GetIndex() == pStart
->GetContentIndex() )
1520 bAtLeastOneExpandedBookmarkAtInsertionPosition
= true;
1526 bSortMarks
= bAtLeastOneBookmarkMoved
&& bAtLeastOneExpandedBookmarkAtInsertionPosition
;
1529 // at-char anchored flys shouldn't be moved, either.
1532 std::vector
<SwFrameFormat
*> const& rFlys(GetAnchoredFlys());
1533 for (size_t i
= 0; i
!= rFlys
.size(); ++i
)
1535 SwFrameFormat
const*const pFormat
= rFlys
[i
];
1536 const SwFormatAnchor
& rAnchor
= pFormat
->GetAnchor();
1537 const SwNode
* pAnchorNode
= rAnchor
.GetAnchorNode();
1538 if (rAnchor
.GetAnchorId() == RndStdIds::FLY_AT_CHAR
&& pAnchorNode
)
1540 // The fly is at-char anchored and has an anchor position.
1541 SwContentIndex
& rEndIdx
= const_cast<SwContentIndex
&>(rAnchor
.GetContentAnchor()->nContent
);
1542 if (*pAnchorNode
== *this && rEndIdx
.GetIndex() == rPos
.GetIndex())
1544 // The anchor position is exactly our insert position.
1545 rEndIdx
.Assign(&aTmpIdxReg
, rEndIdx
.GetIndex());
1551 // The cursors of other shells shouldn't be moved, either.
1552 if (SwDocShell
* pDocShell
= GetDoc().GetDocShell())
1554 if (pDocShell
->GetWrtShell())
1556 for (SwViewShell
& rShell
: pDocShell
->GetWrtShell()->GetRingContainer())
1558 auto pWrtShell
= dynamic_cast<SwWrtShell
*>(&rShell
);
1559 if (!pWrtShell
|| pWrtShell
== pDocShell
->GetWrtShell())
1562 SwShellCursor
* pCursor
= pWrtShell
->GetCursor_();
1566 SwContentIndex
& rIndex
= pCursor
->Start()->nContent
;
1567 if (pCursor
->Start()->GetNode() == *this && rIndex
.GetIndex() == rPos
.GetIndex())
1569 // The cursor position of this other shell is exactly our insert position.
1570 rIndex
.Assign(&aTmpIdxReg
, rIndex
.GetIndex());
1578 SwContentIndexReg::Update(rPos
, nChangeLen
, eMode
);
1582 const size_t nCount
= pCollector
->size();
1583 for ( size_t i
= 0; i
< nCount
; ++i
)
1585 m_pSwpHints
->TryInsertHint( (*pCollector
)[ i
], *this );
1589 aTmpIdxReg
.MoveTo( *this );
1592 getIDocumentMarkAccess()->assureSortedMarkContainers();
1595 //Any drawing objects anchored into this text node may be sorted by their
1596 //anchor position which may have changed here, so resort them
1597 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> iter(*this);
1598 for (SwTextFrame
* pFrame
= iter
.First(); pFrame
; pFrame
= iter
.Next())
1600 SwSortedObjs
* pSortedObjs(pFrame
->GetDrawObjs());
1603 pSortedObjs
->UpdateAll();
1605 // also sort the objs on the page frame
1606 if (SwPageFrame
*pPage
= pFrame
->FindPageFrame())
1607 pSortedObjs
= pPage
->GetSortedObjs();
1609 if (pSortedObjs
) // doesn't exist yet if called for inserting as-char fly
1611 pSortedObjs
->UpdateAll();
1615 // Update the paragraph signatures.
1616 if (SwEditShell
* pEditShell
= GetDoc().GetEditShell())
1618 pEditShell
->ValidateParagraphSignatures(this, true);
1621 // Inform LOK clients about change in position of redlines (if any)
1622 // Don't emit notifications during save: redline flags are temporarily changed during save, but
1623 // it's not useful to let clients know about such changes.
1624 if (!comphelper::LibreOfficeKit::isActive() || GetDoc().IsInWriting())
1627 const SwRedlineTable
& rTable
= GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
1628 for (SwRedlineTable::size_type nRedlnPos
= 0; nRedlnPos
< rTable
.size(); ++nRedlnPos
)
1630 SwRangeRedline
* pRedln
= rTable
[nRedlnPos
];
1631 if (pRedln
->HasMark())
1633 if (*this == pRedln
->End()->GetNode() && *pRedln
->GetPoint() != *pRedln
->GetMark())
1635 // Redline is changed only when some change occurs before it
1636 if (nChangePos
<= pRedln
->Start()->GetContentIndex())
1638 SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify
, pRedln
);
1642 else if (this == &pRedln
->GetPoint()->GetNode())
1643 SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify
, pRedln
);
1647 void SwTextNode::ChgTextCollUpdateNum( const SwTextFormatColl
*pOldColl
,
1648 const SwTextFormatColl
*pNewColl
)
1650 SwDoc
& rDoc
= GetDoc();
1651 // query the OutlineLevel and if it changed, notify the Nodes-Array!
1652 const int nOldLevel
= pOldColl
&& pOldColl
->IsAssignedToListLevelOfOutlineStyle() ?
1653 pOldColl
->GetAssignedOutlineStyleLevel() : MAXLEVEL
;
1654 const int nNewLevel
= pNewColl
&& pNewColl
->IsAssignedToListLevelOfOutlineStyle() ?
1655 pNewColl
->GetAssignedOutlineStyleLevel() : MAXLEVEL
;
1657 if ( MAXLEVEL
!= nNewLevel
&& -1 != nNewLevel
)
1659 SetAttrListLevel(nNewLevel
);
1661 rDoc
.GetNodes().UpdateOutlineNode(*this);
1663 SwNodes
& rNds
= GetNodes();
1664 // If Level 0 (Chapter), update the footnotes!
1665 if( ( !nNewLevel
|| !nOldLevel
) && !rDoc
.GetFootnoteIdxs().empty() &&
1666 FTNNUM_CHAPTER
== rDoc
.GetFootnoteInfo().m_eNum
&&
1669 rDoc
.GetFootnoteIdxs().UpdateFootnote( *rNds
[GetIndex()] );
1672 if( pNewColl
&& RES_CONDTXTFMTCOLL
== pNewColl
->Which() )
1674 // check the condition of the text node again
1679 // If positioned exactly at the end of a CharStyle or Hyperlink,
1680 // set its DontExpand flag.
1681 bool SwTextNode::DontExpandFormat( sal_Int32 nIdx
, bool bFlag
,
1682 bool bFormatToTextAttributes
)
1684 if (bFormatToTextAttributes
&& nIdx
== m_Text
.getLength())
1686 FormatToTextAttr( this );
1692 m_pSwpHints
->SortIfNeedBe();
1693 int nPos
= m_pSwpHints
->GetLastPosSortedByEnd(nIdx
);
1694 for ( ; nPos
>= 0; --nPos
)
1696 SwTextAttr
*pTmp
= m_pSwpHints
->GetSortedByEnd( nPos
);
1697 const sal_Int32
*pEnd
= pTmp
->GetEnd();
1700 assert( *pEnd
<= nIdx
);
1703 if( bFlag
!= pTmp
->DontExpand() && !pTmp
->IsLockExpandFlag()
1704 && *pEnd
> pTmp
->GetStart())
1707 m_pSwpHints
->NoteInHistory( pTmp
);
1708 pTmp
->SetDontExpand( bFlag
);
1715 static bool lcl_GetTextAttrDefault(sal_Int32 nIndex
, sal_Int32 nHintStart
, sal_Int32 nHintEnd
)
1717 return ((nHintStart
<= nIndex
) && (nIndex
< nHintEnd
));
1719 static bool lcl_GetTextAttrExpand(sal_Int32 nIndex
, sal_Int32 nHintStart
, sal_Int32 nHintEnd
)
1721 return ((nHintStart
< nIndex
) && (nIndex
<= nHintEnd
));
1723 static bool lcl_GetTextAttrParent(sal_Int32 nIndex
, sal_Int32 nHintStart
, sal_Int32 nHintEnd
)
1725 return ((nHintStart
< nIndex
) && (nIndex
< nHintEnd
));
1730 std::vector
<SwTextAttr
*> *const pVector
,
1731 SwTextAttr
**const ppTextAttr
,
1732 SwpHints
const *const pSwpHints
,
1733 sal_Int32
const nIndex
, sal_uInt16
const nWhich
,
1734 ::sw::GetTextAttrMode
const eMode
)
1736 assert(nWhich
>= RES_TXTATR_BEGIN
&& nWhich
< RES_TXTATR_END
);
1739 size_t const nSize
= pSwpHints
->Count();
1740 sal_Int32
nPreviousIndex(0); // index of last hint with nWhich
1741 bool (*pMatchFunc
)(sal_Int32
, sal_Int32
, sal_Int32
)=nullptr;
1744 case ::sw::GetTextAttrMode::Default
: pMatchFunc
= &lcl_GetTextAttrDefault
;
1746 case ::sw::GetTextAttrMode::Expand
: pMatchFunc
= &lcl_GetTextAttrExpand
;
1748 case ::sw::GetTextAttrMode::Parent
: pMatchFunc
= &lcl_GetTextAttrParent
;
1750 default: assert(false);
1753 for( size_t i
= pSwpHints
->GetFirstPosSortedByWhichAndStart(nWhich
); i
< nSize
; ++i
)
1755 SwTextAttr
*const pHint
= pSwpHints
->GetSortedByWhichAndStart(i
);
1756 if (pHint
->Which() != nWhich
)
1757 break; // hints are sorted by which&start, so we are done...
1759 sal_Int32
const nHintStart
= pHint
->GetStart();
1760 if (nIndex
< nHintStart
)
1761 break; // hints are sorted by which&start, so we are done...
1763 sal_Int32
const*const pEndIdx
= pHint
->GetEnd();
1764 // cannot have hint with no end and no dummy char
1765 assert(pEndIdx
|| pHint
->HasDummyChar());
1766 // If EXPAND is set, simulate the text input behavior, i.e.
1767 // move the start, and expand the end.
1768 bool const bContained( pEndIdx
1769 ? (*pMatchFunc
)(nIndex
, nHintStart
, *pEndIdx
)
1770 : (nHintStart
== nIndex
) );
1775 if (nPreviousIndex
< nHintStart
)
1777 pVector
->clear(); // clear hints that are outside pHint
1778 nPreviousIndex
= nHintStart
;
1780 pVector
->push_back(pHint
);
1784 *ppTextAttr
= pHint
; // and possibly overwrite outer hint
1794 std::vector
<SwTextAttr
*>
1795 SwTextNode::GetTextAttrsAt(sal_Int32
const nIndex
, sal_uInt16
const nWhich
) const
1797 assert(nWhich
>= RES_TXTATR_BEGIN
&& nWhich
< RES_TXTATR_END
);
1798 std::vector
<SwTextAttr
*> ret
;
1799 lcl_GetTextAttrs(&ret
, nullptr, m_pSwpHints
.get(), nIndex
, nWhich
, ::sw::GetTextAttrMode::Default
);
1804 SwTextNode::GetTextAttrAt(sal_Int32
const nIndex
, sal_uInt16
const nWhich
,
1805 ::sw::GetTextAttrMode
const eMode
) const
1807 assert( (nWhich
== RES_TXTATR_META
)
1808 || (nWhich
== RES_TXTATR_METAFIELD
)
1809 || (nWhich
== RES_TXTATR_AUTOFMT
)
1810 || (nWhich
== RES_TXTATR_INETFMT
)
1811 || (nWhich
== RES_TXTATR_CJK_RUBY
)
1812 || (nWhich
== RES_TXTATR_UNKNOWN_CONTAINER
)
1813 || (nWhich
== RES_TXTATR_CONTENTCONTROL
)
1814 || (nWhich
== RES_TXTATR_INPUTFIELD
) );
1815 // "GetTextAttrAt() will give wrong result for this hint!")
1817 SwTextAttr
* pRet(nullptr);
1818 lcl_GetTextAttrs(nullptr, & pRet
, m_pSwpHints
.get(), nIndex
, nWhich
, eMode
);
1822 const SwTextInputField
* SwTextNode::GetOverlappingInputField( const SwTextAttr
& rTextAttr
) const
1824 const SwTextInputField
* pTextInputField
= dynamic_cast<const SwTextInputField
*>(GetTextAttrAt(rTextAttr
.GetStart(), RES_TXTATR_INPUTFIELD
, ::sw::GetTextAttrMode::Parent
));
1826 if ( pTextInputField
== nullptr && rTextAttr
.End() != nullptr )
1828 pTextInputField
= dynamic_cast<const SwTextInputField
*>(GetTextAttrAt(*(rTextAttr
.End()), RES_TXTATR_INPUTFIELD
, ::sw::GetTextAttrMode::Parent
));
1831 return pTextInputField
;
1834 void SwTextNode::DelFrames_TextNodePart()
1836 SetWrong( nullptr );
1837 SetWrongDirty(sw::WrongState::TODO
);
1839 SetGrammarCheck( nullptr );
1840 SetGrammarCheckDirty( true );
1842 SetSmartTags( nullptr );
1843 SetSmartTagDirty( true );
1845 SetWordCountDirty( true );
1846 SetAutoCompleteWordDirty( true );
1849 SwTextField
* SwTextNode::GetFieldTextAttrAt(
1850 const sal_Int32 nIndex
,
1851 ::sw::GetTextAttrMode
const eMode
) const
1853 SwTextField
* pTextField
= dynamic_cast<SwTextField
*>(GetTextAttrForCharAt( nIndex
, RES_TXTATR_FIELD
));
1854 if ( pTextField
== nullptr )
1856 pTextField
= dynamic_cast<SwTextField
*>(GetTextAttrForCharAt( nIndex
, RES_TXTATR_ANNOTATION
));
1858 if ( pTextField
== nullptr )
1861 dynamic_cast<SwTextField
*>( GetTextAttrAt(
1863 RES_TXTATR_INPUTFIELD
,
1870 static SwCharFormat
* lcl_FindCharFormat( const SwCharFormats
* pCharFormats
, std::u16string_view rName
)
1872 if( !rName
.empty() )
1874 const size_t nArrLen
= pCharFormats
->size();
1875 for( size_t i
= 1; i
< nArrLen
; i
++ )
1877 SwCharFormat
* pFormat
= (*pCharFormats
)[ i
];
1878 if( pFormat
->GetName()==rName
)
1885 static void lcl_CopyHint(
1886 const sal_uInt16 nWhich
,
1887 const SwTextAttr
* const pHt
,
1888 SwTextAttr
*const pNewHt
,
1889 SwDoc
*const pOtherDoc
,
1890 SwTextNode
*const pDest
)
1892 assert(nWhich
== pHt
->Which()); // wrong hint-id
1895 // copy nodesarray section with footnote content
1896 case RES_TXTATR_FTN
:
1897 assert(pDest
); // "lcl_CopyHint: no destination text node?"
1898 static_cast<const SwTextFootnote
*>(pHt
)->CopyFootnote( *static_cast<SwTextFootnote
*>(pNewHt
), *pDest
);
1901 // Fields that are copied into different SwDocs must be registered
1902 // at their new FieldTypes.
1904 case RES_TXTATR_FIELD
:
1906 if( pOtherDoc
!= nullptr )
1908 static_txtattr_cast
<const SwTextField
*>(pHt
)->CopyTextField(
1909 static_txtattr_cast
<SwTextField
*>(pNewHt
));
1912 // Table Formula must be copied relative.
1913 const SwFormatField
& rField
= pHt
->GetFormatField();
1914 if( SwFieldIds::Table
== rField
.GetField()->GetTyp()->Which()
1915 && static_cast<const SwTableField
*>(rField
.GetField())->IsIntrnlName())
1917 // convert internal formula to external
1918 const SwTableNode
* const pDstTableNd
=
1919 static_txtattr_cast
<const SwTextField
*>(pHt
)->GetTextNode().FindTableNode();
1922 SwTableField
* const pTableField
=
1923 const_cast<SwTableField
*>(static_cast<const SwTableField
*>(
1924 pNewHt
->GetFormatField().GetField()));
1925 pTableField
->PtrToBoxNm( &pDstTableNd
->GetTable() );
1931 case RES_TXTATR_INPUTFIELD
:
1932 case RES_TXTATR_ANNOTATION
:
1933 if( pOtherDoc
!= nullptr )
1935 static_txtattr_cast
<const SwTextField
*>(pHt
)->CopyTextField(
1936 static_txtattr_cast
<SwTextField
*>(pNewHt
));
1940 case RES_TXTATR_TOXMARK
:
1941 if( pOtherDoc
&& pDest
&& pDest
->GetpSwpHints()
1942 && pDest
->GetpSwpHints()->Contains( pNewHt
) )
1944 // ToXMarks that are copied to different SwDocs must register
1945 // at their new ToX (sw::BroadcastingModify).
1946 static_txtattr_cast
<SwTextTOXMark
*>(pNewHt
)->CopyTOXMark(*pOtherDoc
);
1950 case RES_TXTATR_CHARFMT
:
1951 // For CharacterStyles, the format must be copied too.
1952 if( pDest
&& pDest
->GetpSwpHints()
1953 && pDest
->GetpSwpHints()->Contains( pNewHt
) )
1955 SwCharFormat
* pFormat
= pHt
->GetCharFormat().GetCharFormat();
1959 pFormat
= pOtherDoc
->CopyCharFormat( *pFormat
);
1961 const_cast<SwFormatCharFormat
&>(
1962 pNewHt
->GetCharFormat() ).SetCharFormat( pFormat
);
1965 case RES_TXTATR_INETFMT
:
1967 // For Hyperlinks, the format must be copied too.
1968 if( pOtherDoc
&& pDest
&& pDest
->GetpSwpHints()
1969 && pDest
->GetpSwpHints()->Contains( pNewHt
) )
1971 const SwDoc
& rDoc
= static_txtattr_cast
<
1972 const SwTextINetFormat
*>(pHt
)->GetTextNode().GetDoc();
1973 const SwCharFormats
* pCharFormats
= rDoc
.GetCharFormats();
1974 const SwFormatINetFormat
& rFormat
= pHt
->GetINetFormat();
1975 SwCharFormat
* pFormat
;
1976 pFormat
= lcl_FindCharFormat( pCharFormats
, rFormat
.GetINetFormat() );
1978 pOtherDoc
->CopyCharFormat( *pFormat
);
1979 pFormat
= lcl_FindCharFormat( pCharFormats
, rFormat
.GetVisitedFormat() );
1981 pOtherDoc
->CopyCharFormat( *pFormat
);
1983 //JP 24.04.98: The attribute must point to a text node, so that
1984 // the styles can be created.
1985 SwTextINetFormat
*const pINetHt
= static_txtattr_cast
<SwTextINetFormat
*>(pNewHt
);
1986 if ( !pINetHt
->GetpTextNode() )
1988 pINetHt
->ChgTextNode( pDest
);
1991 //JP 22.10.97: set up link to char style
1992 pINetHt
->GetCharFormat();
1995 case RES_TXTATR_META
:
1996 case RES_TXTATR_METAFIELD
:
1997 OSL_ENSURE( pNewHt
, "copying Meta should not fail!" );
1999 && (CH_TXTATR_INWORD
== pDest
->GetText()[pNewHt
->GetStart()]),
2000 "missing CH_TXTATR?");
2005 /// copy attributes at position nTextStartIdx to node pDest
2006 // BP 7.6.93: Intentionally copy only attributes _with_ EndIdx!
2007 // CopyAttr is usually called when attributes are set on a
2008 // node with no text.
2009 void SwTextNode::CopyAttr( SwTextNode
*pDest
, const sal_Int32 nTextStartIdx
,
2010 const sal_Int32 nOldPos
)
2014 SwDoc
* const pOtherDoc
= (&pDest
->GetDoc() != &GetDoc()) ?
2015 &pDest
->GetDoc() : nullptr;
2017 for ( size_t i
= 0; i
< m_pSwpHints
->Count(); ++i
)
2019 SwTextAttr
*const pHt
= m_pSwpHints
->Get(i
);
2020 sal_Int32
const nAttrStartIdx
= pHt
->GetStart();
2021 if ( nTextStartIdx
< nAttrStartIdx
)
2022 break; // beyond end of text, because nLen == 0
2024 const sal_Int32
*const pEndIdx
= pHt
->GetEnd();
2025 if ( pEndIdx
&& !pHt
->HasDummyChar() )
2027 sal_uInt16
const nWhich
= pHt
->Which();
2028 if (RES_TXTATR_INPUTFIELD
!= nWhich
// fdo#74981 skip fields
2029 && ( *pEndIdx
> nTextStartIdx
2030 || (*pEndIdx
== nTextStartIdx
2031 && nAttrStartIdx
== nTextStartIdx
)))
2033 if ( RES_TXTATR_REFMARK
!= nWhich
)
2035 // attribute in the area => copy
2036 SwTextAttr
*const pNewHt
=
2037 pDest
->InsertItem( pHt
->GetAttr(), nOldPos
, nOldPos
, SetAttrMode::IS_COPY
);
2040 lcl_CopyHint( nWhich
, pHt
, pNewHt
,
2045 ? GetDoc().IsCopyIsMove()
2046 : nullptr == pOtherDoc
->GetRefMark( pHt
->GetRefMark().GetRefName() ) )
2049 pHt
->GetAttr(), nOldPos
, nOldPos
, SetAttrMode::IS_COPY
);
2058 // notify layout frames, to prevent disappearance of footnote numbers
2064 pDest
->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint
, &aHint
));
2068 /// copy text and attributes to node pDest
2069 void SwTextNode::CopyText( SwTextNode
*const pDest
,
2070 const SwContentIndex
&rStart
,
2071 const sal_Int32 nLen
,
2072 const bool bForceCopyOfAllAttrs
)
2074 SwContentIndex
const aIdx( pDest
, pDest
->m_Text
.getLength() );
2075 CopyText( pDest
, aIdx
, rStart
, nLen
, bForceCopyOfAllAttrs
);
2078 void SwTextNode::CopyText( SwTextNode
*const pDest
,
2079 const SwContentIndex
&rDestStart
,
2080 const SwPosition
&rStart
,
2082 const bool bForceCopyOfAllAttrs
)
2084 CopyText( pDest
, rDestStart
, rStart
.nContent
, nLen
, bForceCopyOfAllAttrs
);
2087 void SwTextNode::CopyText( SwTextNode
*const pDest
,
2088 const SwContentIndex
&rDestStart
,
2089 const SwContentIndex
&rStart
,
2091 const bool bForceCopyOfAllAttrs
)
2093 CHECK_SWPHINTS_IF_FRM(this);
2094 CHECK_SWPHINTS(pDest
);
2095 assert(rDestStart
.GetContentNode() == pDest
);
2096 assert(rStart
.GetContentNode() == this);
2097 sal_Int32 nTextStartIdx
= rStart
.GetIndex();
2098 sal_Int32 nDestStart
= rDestStart
.GetIndex(); // remember old Pos
2100 if (pDest
->GetDoc().IsClipBoard() && GetNum())
2102 // #i111677# cache expansion of source (for clipboard)
2103 pDest
->m_oNumStringCache
= (nTextStartIdx
!= 0)
2104 ? OUString() // fdo#49076: numbering only if copy from para start
2110 // if no length is given, copy attributes at position rStart
2111 CopyAttr( pDest
, nTextStartIdx
, nDestStart
);
2113 // copy hard attributes on whole paragraph
2114 if( HasSwAttrSet() )
2116 // i#96213 all or just the Char attributes?
2117 if ( !bForceCopyOfAllAttrs
&&
2119 pDest
->HasSwAttrSet() ||
2120 nLen
!= pDest
->GetText().getLength()))
2123 RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1,
2124 RES_TXTATR_INETFMT
, RES_TXTATR_CHARFMT
,
2125 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
- 1>
2126 aCharSet( pDest
->GetDoc().GetAttrPool() );
2127 aCharSet
.Put( *GetpSwAttrSet() );
2128 if( aCharSet
.Count() )
2130 pDest
->SetAttr( aCharSet
, nDestStart
, nDestStart
);
2135 GetpSwAttrSet()->CopyToModify( *pDest
);
2142 const sal_Int32 oldLen
= pDest
->m_Text
.getLength();
2143 // JP 15.02.96: missing attribute handling at the end!
2144 // hence call InsertText and don't modify m_Text directly
2145 pDest
->InsertText( m_Text
.copy(nTextStartIdx
, nLen
), rDestStart
,
2146 SwInsertFlags::EMPTYEXPAND
);
2148 // update with actual new size
2149 nLen
= pDest
->m_Text
.getLength() - oldLen
;
2150 if ( !nLen
) // string not longer?
2153 SwDoc
* const pOtherDoc
= (&pDest
->GetDoc() != &GetDoc()) ? &pDest
->GetDoc() : nullptr;
2155 // copy hard attributes on whole paragraph
2156 if( HasSwAttrSet() )
2158 // i#96213 all or just the Char attributes?
2159 if ( !bForceCopyOfAllAttrs
&&
2161 pDest
->HasSwAttrSet() ||
2162 nLen
!= pDest
->GetText().getLength()))
2165 RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1,
2166 RES_TXTATR_INETFMT
, RES_TXTATR_CHARFMT
,
2167 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
- 1>
2168 aCharSet( pDest
->GetDoc().GetAttrPool() );
2169 aCharSet
.Put( *GetpSwAttrSet() );
2170 if( aCharSet
.Count() )
2172 pDest
->SetAttr( aCharSet
, nDestStart
, nDestStart
+ nLen
);
2177 GetpSwAttrSet()->CopyToModify( *pDest
);
2181 bool const bUndoNodes
= !pOtherDoc
2182 && GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
2184 // Fetch end only now, because copying into self updates the start index
2185 // and all attributes
2186 nTextStartIdx
= rStart
.GetIndex();
2187 const sal_Int32 nEnd
= nTextStartIdx
+ nLen
;
2189 // 2. copy attributes
2190 // Iterate over attribute array until the start of the attribute
2191 // is behind the copied range
2192 const size_t nSize
= m_pSwpHints
? m_pSwpHints
->Count() : 0;
2194 // If copying into self, inserting can delete attributes!
2195 // Hence first copy into temp-array, and then move that into hints array.
2198 // Del-Array for all RefMarks without extent
2201 std::vector
<std::pair
<sal_Int32
, sal_Int32
>> metaFieldRanges
;
2202 sal_Int32
nDeletedDummyChars(0);
2203 for (size_t n
= 0; n
< nSize
; ++n
)
2205 SwTextAttr
* const pHt
= m_pSwpHints
->Get(n
);
2207 const sal_Int32 nAttrStartIdx
= pHt
->GetStart();
2208 if ( nAttrStartIdx
>= nEnd
)
2211 const sal_Int32
* const pEndIdx
= pHt
->GetEnd();
2212 const sal_uInt16 nWhich
= pHt
->Which();
2214 // JP 26.04.94: RefMarks are never copied. If the refmark doesn't have
2215 // an extent, there is a dummy char in the text, which
2216 // must be removed. So we first copy the attribute,
2217 // but remember it, and when we're done delete it,
2218 // which also deletes the dummy character!
2219 // JP 14.08.95: May RefMarks be moved?
2220 const bool bCopyRefMark
= RES_TXTATR_REFMARK
== nWhich
2223 ? GetDoc().IsCopyIsMove()
2224 : nullptr == pOtherDoc
->GetRefMark( pHt
->GetRefMark().GetRefName() ) ) );
2227 && RES_TXTATR_REFMARK
== nWhich
2233 // Input Fields are only copied, if completely covered by copied text
2234 if ( nWhich
== RES_TXTATR_INPUTFIELD
)
2236 assert(pEndIdx
!= nullptr &&
2237 "<SwTextNode::CopyText(..)> - RES_TXTATR_INPUTFIELD without EndIndex!" );
2238 if ( nAttrStartIdx
< nTextStartIdx
2239 || ( pEndIdx
!= nullptr
2240 && *pEndIdx
> nEnd
) )
2246 if (nWhich
== RES_TXTATR_METAFIELD
)
2248 // Skip metadata fields. Also remember the range to strip the text later.
2249 metaFieldRanges
.emplace_back(nAttrStartIdx
, pEndIdx
? *pEndIdx
: nEnd
);
2253 sal_Int32 nAttrStt
= 0;
2254 sal_Int32 nAttrEnd
= 0;
2256 if( nAttrStartIdx
< nTextStartIdx
)
2258 // start is before selection
2259 // copy hints with end and CH_TXTATR only if dummy char is copied
2260 if ( pEndIdx
&& (*pEndIdx
> nTextStartIdx
) && !pHt
->HasDummyChar() )
2262 // attribute with extent and the end is in the selection
2263 nAttrStt
= nDestStart
;
2264 nAttrEnd
= (*pEndIdx
> nEnd
)
2265 ? rDestStart
.GetIndex()
2266 : nDestStart
+ (*pEndIdx
) - nTextStartIdx
;
2275 // start is in the selection
2276 nAttrStt
= nDestStart
+ ( nAttrStartIdx
- nTextStartIdx
);
2279 nAttrEnd
= *pEndIdx
> nEnd
2280 ? rDestStart
.GetIndex()
2281 : nDestStart
+ ( *pEndIdx
- nTextStartIdx
);
2285 nAttrEnd
= nAttrStt
;
2289 SwTextAttr
* pNewHt
= nullptr;
2293 // copy the hint here, but insert it later
2294 pNewHt
= MakeTextAttr( GetDoc(), pHt
->GetAttr(),
2295 nAttrStt
, nAttrEnd
, CopyOrNewType::Copy
, pDest
);
2297 lcl_CopyHint(nWhich
, pHt
, pNewHt
, nullptr, pDest
);
2298 aArr
.push_back( pNewHt
);
2302 pNewHt
= pDest
->InsertItem(
2304 nAttrStt
- nDeletedDummyChars
,
2305 nAttrEnd
- nDeletedDummyChars
,
2306 SetAttrMode::NOTXTATRCHR
| SetAttrMode::IS_COPY
);
2309 lcl_CopyHint( nWhich
, pHt
, pNewHt
, pOtherDoc
, pDest
);
2311 else if (pHt
->HasDummyChar())
2313 // The attribute that has failed to be copied would insert
2314 // dummy char, so positions of the following attributes have
2315 // to be shifted by one to compensate for that missing char.
2316 ++nDeletedDummyChars
;
2320 if( RES_TXTATR_REFMARK
== nWhich
&& !pEndIdx
&& !bCopyRefMark
)
2322 aRefMrkArr
.push_back( pNewHt
);
2326 // Strip the metadata fields, since we don't copy the RDF entries
2327 // yet and so they are inconsistent upon copy/pasting.
2328 if (!metaFieldRanges
.empty())
2330 // Reverse to remove without messing the offsets.
2331 std::reverse(metaFieldRanges
.begin(), metaFieldRanges
.end());
2332 for (const auto& pair
: metaFieldRanges
)
2334 const SwContentIndex
aIdx(pDest
, pair
.first
);
2335 pDest
->EraseText(aIdx
, pair
.second
- pair
.first
);
2339 // this can only happen when copying into self
2340 for (SwTextAttr
* i
: aArr
)
2342 InsertHint( i
, SetAttrMode::NOTXTATRCHR
);
2345 if( pDest
->GetpSwpHints() )
2347 for (SwTextAttr
* pNewHt
: aRefMrkArr
)
2349 if( pNewHt
->GetEnd() )
2351 pDest
->GetpSwpHints()->Delete( pNewHt
);
2352 pDest
->DestroyAttr( pNewHt
);
2356 const SwContentIndex
aIdx( pDest
, pNewHt
->GetStart() );
2357 pDest
->EraseText( aIdx
, 1 );
2362 CHECK_SWPHINTS_IF_FRM(this);
2363 CHECK_SWPHINTS(pDest
);
2366 OUString
SwTextNode::InsertText( const OUString
& rStr
, const SwPosition
& rIdx
,
2367 const SwInsertFlags nMode
)
2369 return InsertText(rStr
, rIdx
.nContent
, nMode
);
2372 OUString
SwTextNode::InsertText( const OUString
& rStr
, const SwContentIndex
& rIdx
,
2373 const SwInsertFlags nMode
)
2375 assert(rIdx
.GetContentNode() == this);
2376 assert(rIdx
<= m_Text
.getLength()); // invalid index
2378 const sal_Int32 aPos
= rIdx
.GetIndex();
2379 sal_Int32 nLen
= m_Text
.getLength() - aPos
;
2380 sal_Int32
const nOverflow(rStr
.getLength() - GetSpaceLeft());
2381 SAL_WARN_IF(nOverflow
> 0, "sw.core",
2382 "SwTextNode::InsertText: node text with insertion > capacity.");
2383 OUString
const sInserted(
2384 (nOverflow
> 0) ? rStr
.copy(0, rStr
.getLength() - nOverflow
) : rStr
);
2385 if (sInserted
.isEmpty())
2389 if (aPos
== 0 && m_Text
.isEmpty())
2392 m_Text
= m_Text
.replaceAt(aPos
, 0, sInserted
);
2393 assert(GetSpaceLeft()>=0);
2394 nLen
= m_Text
.getLength() - aPos
- nLen
;
2397 bool bOldExpFlg
= IsIgnoreDontExpand();
2398 if (nMode
& SwInsertFlags::FORCEHINTEXPAND
)
2400 SetIgnoreDontExpand( true );
2403 Update(rIdx
, nLen
, UpdateMode::Default
); // text content changed!
2405 if (nMode
& SwInsertFlags::FORCEHINTEXPAND
)
2407 SetIgnoreDontExpand( bOldExpFlg
);
2410 if ( HasWriterListeners() )
2411 { // send this before messing with hints, which will send RES_UPDATE_ATTR
2412 auto aInsHint
= sw::MakeInsertText(*this, aPos
, nLen
);
2413 CallSwClientNotify(aInsHint
);
2418 m_pSwpHints
->SortIfNeedBe();
2419 bool const bHadHints(!m_pSwpHints
->CanBeDeleted());
2420 bool bMergePortionsNeeded(false);
2421 for ( size_t i
= 0; i
< m_pSwpHints
->Count() &&
2422 rIdx
>= m_pSwpHints
->GetWithoutResorting(i
)->GetStart(); ++i
)
2424 SwTextAttr
* const pHt
= m_pSwpHints
->GetWithoutResorting( i
);
2425 const sal_Int32
* const pEndIdx
= pHt
->GetEnd();
2429 if( rIdx
== *pEndIdx
)
2431 if ( (nMode
& SwInsertFlags::NOHINTEXPAND
) ||
2432 (!(nMode
& SwInsertFlags::FORCEHINTEXPAND
)
2433 && pHt
->DontExpand()) )
2435 m_pSwpHints
->DeleteAtPos(i
);
2436 // on empty attributes also adjust Start
2437 if( rIdx
== pHt
->GetStart() )
2438 pHt
->SetStart( pHt
->GetStart() - nLen
);
2439 pHt
->SetEnd(*pEndIdx
- nLen
);
2440 // could be that pHt has IsFormatIgnoreEnd set, and it's
2441 // not a RSID-only hint - now we have the inserted text
2442 // between pHt and its continuation... which we don't know.
2443 // punt the job to MergePortions below.
2444 if (pHt
->IsFormatIgnoreEnd())
2446 bMergePortionsNeeded
= true;
2448 InsertHint( pHt
, SetAttrMode::NOHINTADJUST
);
2450 // empty hints at insert position?
2451 else if ( (nMode
& SwInsertFlags::EMPTYEXPAND
)
2452 && (*pEndIdx
== pHt
->GetStart()) )
2454 m_pSwpHints
->DeleteAtPos(i
);
2455 pHt
->SetStart( pHt
->GetStart() - nLen
);
2456 const size_t nCurrentLen
= m_pSwpHints
->Count();
2457 InsertHint( pHt
/* AUTOSTYLES:, SetAttrMode::NOHINTADJUST*/ );
2458 if ( nCurrentLen
> m_pSwpHints
->Count() && i
)
2469 if ( !(nMode
& SwInsertFlags::NOHINTEXPAND
) &&
2470 rIdx
== nLen
&& pHt
->GetStart() == rIdx
.GetIndex() &&
2471 !pHt
->IsDontExpandStartAttr() )
2473 // no field, at paragraph start, HintExpand
2474 m_pSwpHints
->DeleteAtPos(i
);
2475 pHt
->SetStart( pHt
->GetStart() - nLen
);
2476 // no effect on format ignore flags here (para start)
2477 InsertHint( pHt
, SetAttrMode::NOHINTADJUST
);
2480 if (bMergePortionsNeeded
)
2482 m_pSwpHints
->MergePortions(*this);
2484 SAL_WARN_IF(bHadHints
&& m_pSwpHints
->CanBeDeleted(), "sw.core",
2485 "SwTextNode::InsertText: unexpected loss of hints");
2488 // By inserting a character, the hidden flags
2489 // at the TextNode can become invalid:
2490 SetCalcHiddenCharFlags();
2492 CHECK_SWPHINTS(this);
2496 void SwTextNode::CutText( SwTextNode
* const pDest
,
2497 const SwContentIndex
& rStart
, const sal_Int32 nLen
)
2499 assert(pDest
); // Cut requires a destination
2500 SwContentIndex
aDestStt(pDest
, pDest
->GetText().getLength());
2501 CutImpl( pDest
, aDestStt
, rStart
, nLen
, false );
2504 void SwTextNode::CutImpl( SwTextNode
* const pDest
, const SwContentIndex
& rDestStart
,
2505 const SwContentIndex
& rStart
, sal_Int32 nLen
, const bool bUpdate
)
2507 assert(pDest
); // Cut requires a destination
2509 assert(&GetDoc() == &pDest
->GetDoc()); // must be same document
2511 assert(pDest
!= this); // destination must be different node
2513 assert(rDestStart
.GetContentNode() == pDest
);
2514 assert(rStart
.GetContentNode() == this);
2518 // if no length is given, copy attributes at position rStart
2519 CopyAttr( pDest
, rStart
.GetIndex(), rDestStart
.GetIndex() );
2523 sal_Int32 nTextStartIdx
= rStart
.GetIndex();
2524 sal_Int32 nDestStart
= rDestStart
.GetIndex(); // remember old Pos
2525 const sal_Int32 nInitSize
= pDest
->m_Text
.getLength();
2527 if (pDest
->GetSpaceLeft() < nLen
)
2528 { // FIXME: could only happen when called from SwRangeRedline::Show.
2529 // unfortunately can't really do anything here to handle that...
2532 pDest
->m_Text
= pDest
->m_Text
.replaceAt(nDestStart
, 0,
2533 m_Text
.subView(nTextStartIdx
, nLen
));
2534 OUString
const newText
= m_Text
.replaceAt(nTextStartIdx
, nLen
, u
"");
2535 nLen
= pDest
->m_Text
.getLength() - nInitSize
; // update w/ current size!
2536 if (!nLen
) // String didn't grow?
2541 // Update all SwContentIndex
2542 pDest
->Update(rDestStart
, nLen
, UpdateMode::Default
);
2545 CHECK_SWPHINTS(pDest
);
2547 const sal_Int32 nEnd
= rStart
.GetIndex() + nLen
;
2548 bool const bUndoNodes
=
2549 GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
2551 // copy hard attributes on whole paragraph
2554 bool hasSwAttrSet
= pDest
->HasSwAttrSet();
2557 // if we have our own property set it doesn't mean
2558 // that this set defines any style different to Standard one.
2559 hasSwAttrSet
= false;
2561 // so, let's check deeper if property set has defined any property
2562 if (pDest
->GetpSwAttrSet())
2564 // check all items in the property set
2565 SfxItemIter
aIter( *pDest
->GetpSwAttrSet() );
2566 const SfxPoolItem
* pItem
= aIter
.GetCurItem();
2569 // check current item
2570 const sal_uInt16 nWhich
= IsInvalidItem( pItem
)
2571 ? pDest
->GetpSwAttrSet()->GetWhichByPos( aIter
.GetCurPos() )
2573 if( RES_FRMATR_STYLE_NAME
!= nWhich
&&
2574 RES_FRMATR_CONDITIONAL_STYLE_NAME
!= nWhich
&&
2575 RES_PAGEDESC
!= nWhich
&&
2576 RES_BREAK
!= nWhich
&&
2577 SfxItemState::SET
== pDest
->GetpSwAttrSet()->GetItemState( nWhich
, false ) )
2579 // check if parent value (original value in style) has the same value as in [pItem]
2580 const SfxPoolItem
& rParentItem
= pDest
->GetpSwAttrSet()->GetParent()->Get( nWhich
, true );
2582 hasSwAttrSet
= (rParentItem
!= *pItem
);
2584 // property set is not empty => no need to make anymore checks
2589 // let's check next item
2590 pItem
= aIter
.NextItem();
2595 // all or just the Char attributes?
2596 if( nInitSize
|| hasSwAttrSet
||
2597 nLen
!= pDest
->GetText().getLength())
2600 RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1,
2601 RES_TXTATR_INETFMT
, RES_TXTATR_CHARFMT
,
2602 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
- 1>
2603 aCharSet( pDest
->GetDoc().GetAttrPool() );
2604 aCharSet
.Put( *GetpSwAttrSet() );
2605 if( aCharSet
.Count() )
2606 pDest
->SetAttr( aCharSet
, nDestStart
, nDestStart
+ nLen
);
2610 // Copy all attrs except RES_PARATR_LIST_LEVEL: it was initialized before
2611 // and current SwTextNode can contain not suitable for pDest value
2612 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_PARATR_LIST_LEVEL
- 1,
2613 RES_PARATR_LIST_LEVEL
+ 1, HINT_END
>
2614 aCharSet(pDest
->GetDoc().GetAttrPool());
2615 aCharSet
.Put(*GetpSwAttrSet());
2616 if (aCharSet
.Count())
2617 pDest
->SetAttr(aCharSet
, nDestStart
, nDestStart
+ nLen
);
2621 // notify frames - before moving hints, because footnotes
2622 // want to find their anchor text frame in the follow chain
2623 // (also ignore fieldmarks, the caller will recreate frames)
2624 const sw::InsertText
aInsHint(nDestStart
, nLen
, false, false);
2625 pDest
->HandleNonLegacyHint(aInsHint
);
2626 const sw::MoveText
aMoveHint(pDest
, nDestStart
, nTextStartIdx
, nLen
);
2627 CallSwClientNotify(aMoveHint
);
2628 const sw::DeleteText
aDelText(nTextStartIdx
, nLen
);
2629 HandleNonLegacyHint(aDelText
);
2631 // 2. move attributes
2632 // Iterate over attribute array until the start of the attribute
2633 // is behind the moved range
2634 bool bMergePortionsNeeded(false);
2635 size_t nAttrCnt
= 0;
2636 while (m_pSwpHints
&& (nAttrCnt
< m_pSwpHints
->Count()))
2638 SwTextAttr
* const pHt
= m_pSwpHints
->Get(nAttrCnt
);
2639 const sal_Int32 nAttrStartIdx
= pHt
->GetStart();
2640 if ( nAttrStartIdx
>= nEnd
)
2642 const sal_Int32
* const pEndIdx
= pHt
->GetEnd();
2643 const sal_uInt16 nWhich
= pHt
->Which();
2644 SwTextAttr
*pNewHt
= nullptr;
2646 // if the hint has a dummy character, then it must not be split!
2647 if(nAttrStartIdx
< nTextStartIdx
)
2649 // start is before the range
2650 if (!pHt
->HasDummyChar() && ( RES_TXTATR_REFMARK
!= nWhich
2651 || bUndoNodes
) && pEndIdx
&& *pEndIdx
> nTextStartIdx
)
2653 // attribute with extent and end of attribute is in the range
2654 pNewHt
= MakeTextAttr( pDest
->GetDoc(), pHt
->GetAttr(),
2659 : *pEndIdx
- nTextStartIdx
) );
2664 // start is inside the range
2665 if (!pEndIdx
|| *pEndIdx
< nEnd
||
2666 (!bUndoNodes
&& RES_TXTATR_REFMARK
== nWhich
)
2667 || pHt
->HasDummyChar() )
2669 // do not delete note and later add it -> sidebar flickering
2670 if (GetDoc().GetDocShell())
2672 GetDoc().GetDocShell()->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation
));
2675 m_pSwpHints
->Delete( pHt
);
2676 // reset start/end indexes
2677 if (pHt
->IsFormatIgnoreStart() || pHt
->IsFormatIgnoreEnd())
2679 bMergePortionsNeeded
= true;
2681 pHt
->SetStart(nDestStart
+ (nAttrStartIdx
- nTextStartIdx
));
2684 pHt
->SetEnd( nDestStart
+ (
2687 : *pEndIdx
- nTextStartIdx
) );
2689 pDest
->InsertHint( pHt
,
2690 SetAttrMode::NOTXTATRCHR
2691 | SetAttrMode::DONTREPLACE
);
2692 if (GetDoc().GetDocShell())
2694 GetDoc().GetDocShell()->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation
));
2696 continue; // iterate while loop, no ++ !
2698 // the end is behind the range
2699 else if (RES_TXTATR_REFMARK
!= nWhich
|| bUndoNodes
)
2701 pNewHt
= MakeTextAttr( GetDoc(), pHt
->GetAttr(),
2702 nDestStart
+ (nAttrStartIdx
- nTextStartIdx
),
2703 nDestStart
+ (*pEndIdx
> nEnd
2705 : *pEndIdx
- nTextStartIdx
));
2710 const bool bSuccess( pDest
->InsertHint( pNewHt
,
2711 SetAttrMode::NOTXTATRCHR
2712 | SetAttrMode::DONTREPLACE
2713 | SetAttrMode::IS_COPY
) );
2716 lcl_CopyHint( nWhich
, pHt
, pNewHt
, nullptr, pDest
);
2721 // If there are still empty attributes around, they have a higher priority
2722 // than any attributes that become empty due to the move.
2723 // So temporarily remove them and Update the array, then re-insert the
2724 // removed ones so they overwrite the others.
2725 if (m_pSwpHints
&& nAttrCnt
< m_pSwpHints
->Count())
2728 while (nAttrCnt
< m_pSwpHints
->Count())
2730 SwTextAttr
* const pHt
= m_pSwpHints
->Get(nAttrCnt
);
2731 if (nEnd
!= pHt
->GetStart())
2733 const sal_Int32
* const pEndIdx
= pHt
->GetEnd();
2734 if (pEndIdx
&& *pEndIdx
== nEnd
)
2736 aArr
.push_back( pHt
);
2737 m_pSwpHints
->Delete( pHt
);
2744 Update(rStart
, nLen
, UpdateMode::Negative
|UpdateMode::Delete
);
2746 for (SwTextAttr
* pHt
: aArr
)
2748 pHt
->SetStart( rStart
.GetIndex() );
2749 pHt
->SetEnd( rStart
.GetIndex() );
2755 Update(rStart
, nLen
, UpdateMode::Negative
|UpdateMode::Delete
);
2758 // set after moving hints
2761 if (bMergePortionsNeeded
)
2763 m_pSwpHints
->MergePortions(*this);
2766 CHECK_SWPHINTS(this);
2768 TryDeleteSwpHints();
2771 void SwTextNode::EraseText(const SwPosition
&rIdx
, const sal_Int32 nCount
,
2772 const SwInsertFlags nMode
)
2774 EraseText(rIdx
.nContent
, nCount
, nMode
);
2777 void SwTextNode::EraseText(const SwContentIndex
&rIdx
, const sal_Int32 nCount
,
2778 const SwInsertFlags nMode
)
2780 assert(rIdx
.GetContentNode() == this);
2781 assert(rIdx
<= m_Text
.getLength()); // invalid index
2783 const sal_Int32 nStartIdx
= rIdx
.GetIndex();
2784 const sal_Int32 nCnt
= (nCount
==SAL_MAX_INT32
)
2785 ? m_Text
.getLength() - nStartIdx
: nCount
;
2786 const sal_Int32 nEndIdx
= nStartIdx
+ nCnt
;
2787 if (nEndIdx
<= m_Text
.getLength())
2788 m_Text
= m_Text
.replaceAt(nStartIdx
, nCnt
, u
"");
2790 // GCAttr(); don't remove all empty ones, just the ones that are in the
2791 // range but not at the end of the range.
2793 for ( size_t i
= 0; m_pSwpHints
&& i
< m_pSwpHints
->Count(); ++i
)
2795 SwTextAttr
*pHt
= m_pSwpHints
->Get(i
);
2797 const sal_Int32 nHintStart
= pHt
->GetStart();
2799 if ( nHintStart
< nStartIdx
)
2802 if ( nHintStart
> nEndIdx
)
2803 break; // hints are sorted by end, so break here
2805 const sal_Int32
* pHtEndIdx
= pHt
->GetEnd();
2806 const sal_uInt16 nWhich
= pHt
->Which();
2810 // attribute with neither end nor CH_TXTATR?
2811 assert(pHt
->HasDummyChar());
2812 if (isTXTATR(nWhich
) && (nHintStart
< nEndIdx
))
2814 m_pSwpHints
->DeleteAtPos(i
);
2821 assert(!( (nHintStart
< nEndIdx
) && (*pHtEndIdx
> nEndIdx
)
2822 && pHt
->HasDummyChar() )
2823 // next line: deleting exactly dummy char: DeleteAttributes
2824 || ((nHintStart
== nStartIdx
) && (nHintStart
+ 1 == nEndIdx
)));
2825 // "ERROR: deleting left-overlapped attribute with CH_TXTATR");
2827 // Delete the hint if:
2828 // 1. The hint ends before the deletion end position or
2829 // 2. The hint ends at the deletion end position and
2830 // we are not in empty expand mode and
2831 // the hint is a [toxmark|refmark|ruby|inputfield] text attribute
2832 // 3. deleting exactly the dummy char of an hint with end and dummy
2833 // char deletes the hint
2834 if ( (*pHtEndIdx
< nEndIdx
)
2835 || ( (*pHtEndIdx
== nEndIdx
) &&
2836 !(SwInsertFlags::EMPTYEXPAND
& nMode
) &&
2837 ( (RES_TXTATR_TOXMARK
== nWhich
) ||
2838 (RES_TXTATR_REFMARK
== nWhich
) ||
2839 (RES_TXTATR_CJK_RUBY
== nWhich
) ||
2840 (RES_TXTATR_INPUTFIELD
== nWhich
) ) )
2841 || ( (nHintStart
< nEndIdx
) &&
2842 pHt
->HasDummyChar() )
2845 m_pSwpHints
->DeleteAtPos(i
);
2851 OSL_ENSURE(rIdx
.GetIndex() == nStartIdx
, "huh? start index has changed?");
2853 TryDeleteSwpHints();
2855 Update(rIdx
, nCnt
, UpdateMode::Negative
);
2859 const auto aHint
= sw::DeleteChar(nStartIdx
);
2860 CallSwClientNotify(aHint
);
2862 const auto aHint
= sw::DeleteText(nStartIdx
, nCnt
);
2863 CallSwClientNotify(aHint
);
2866 OSL_ENSURE(rIdx
.GetIndex() == nStartIdx
, "huh? start index has changed?");
2868 // By deleting a character, the hidden flags
2869 // at the TextNode can become invalid:
2870 SetCalcHiddenCharFlags();
2872 CHECK_SWPHINTS(this);
2875 void SwTextNode::GCAttr()
2880 bool bChanged
= false;
2881 sal_Int32 nMin
= m_Text
.getLength();
2883 const bool bAll
= nMin
!= 0; // on empty paragraphs only remove INetFormats
2885 for ( size_t i
= 0; m_pSwpHints
&& i
< m_pSwpHints
->Count(); ++i
)
2887 SwTextAttr
* const pHt
= m_pSwpHints
->Get(i
);
2889 // if end and start are equal, delete it
2890 const sal_Int32
* const pEndIdx
= pHt
->GetEnd();
2891 if (pEndIdx
&& !pHt
->HasDummyChar() && (*pEndIdx
== pHt
->GetStart())
2892 && ( bAll
|| pHt
->Which() == RES_TXTATR_INETFMT
) )
2895 nMin
= std::min( nMin
, pHt
->GetStart() );
2896 nMax
= std::max( nMax
, *pHt
->GetEnd() );
2897 DestroyAttr( m_pSwpHints
->Cut(i
) );
2902 pHt
->SetDontExpand( false );
2905 TryDeleteSwpHints();
2909 // textframes react to aHint, others to aNew
2915 CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint
));
2916 SwFormatChg
aNew( GetTextColl() );
2917 CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aNew
));
2921 SwNumRule
* SwTextNode::GetNumRule(bool bInParent
) const
2923 SwNumRule
* pRet
= nullptr;
2925 const SfxPoolItem
* pItem
= GetNoCondAttr( RES_PARATR_NUMRULE
, bInParent
);
2926 bool bNoNumRule
= false;
2929 OUString sNumRuleName
=
2930 static_cast<const SwNumRuleItem
*>(pItem
)->GetValue();
2931 if (!sNumRuleName
.isEmpty())
2933 pRet
= GetDoc().FindNumRulePtr( sNumRuleName
);
2935 else // numbering is turned off
2941 if ( pRet
&& pRet
== GetDoc().GetOutlineNumRule() &&
2942 ( !HasSwAttrSet() ||
2943 SfxItemState::SET
!=
2944 GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE
, false ) ) )
2946 SwTextFormatColl
* pColl
= GetTextColl();
2949 const SwNumRuleItem
& rDirectItem
= pColl
->GetNumRule( false );
2950 if ( rDirectItem
.GetValue().isEmpty() )
2961 void SwTextNode::NumRuleChgd()
2965 SwNumRule
* pNumRule
= GetNumRule();
2966 if ( pNumRule
&& pNumRule
!= GetNum()->GetNumRule() )
2968 mpNodeNum
->ChangeNumRule( *pNumRule
);
2969 if (mpNodeNumRLHidden
)
2971 mpNodeNumRLHidden
->ChangeNumRule(*pNumRule
);
2976 // Sending "noop" modify in order to cause invalidations of registered
2977 // <SwTextFrame> instances to get the list style change respectively the change
2978 // in the list tree reflected in the layout.
2981 SvxTextLeftMarginItem
& rLR
= const_cast<SvxTextLeftMarginItem
&>(GetSwAttrSet().GetTextLeftMargin());
2982 CallSwClientNotify(sw::LegacyModifyHint(&rLR
, &rLR
));
2985 SetWordCountDirty( true );
2989 bool SwTextNode::IsNumbered(SwRootFrame
const*const pLayout
) const
2991 SwNumRule
* pRule
= GetNum(pLayout
) ? GetNum(pLayout
)->GetNumRule() : nullptr;
2992 return pRule
&& IsCountedInList();
2995 bool SwTextNode::HasMarkedLabel() const
2997 bool bResult
= false;
3002 GetDoc().getIDocumentListsAccess().getListByName( GetListId() )->IsListLevelMarked( GetActualListLevel() );
3009 SwTextNode
* SwTextNode::MakeNewTextNode( SwNode
& rPosNd
, bool bNext
,
3012 // ignore hard PageBreak/PageDesc/ColumnBreak from Auto-Set
3013 std::optional
<SwAttrSet
> oNewAttrSet
;
3015 bool bClearHardSetNumRuleWhenFormatCollChanges( false );
3016 if( HasSwAttrSet() )
3018 oNewAttrSet
.emplace( *GetpSwAttrSet() );
3019 const SfxItemSet
* pTmpSet
= GetpSwAttrSet();
3021 if (bNext
) // successor doesn't inherit breaks!
3022 pTmpSet
= &*oNewAttrSet
;
3024 // !bNext: remove PageBreaks/PageDesc/ColBreak from this
3025 bool bRemoveFromCache
= false;
3026 std::vector
<sal_uInt16
> aClearWhichIds
;
3028 bRemoveFromCache
= ( 0 != oNewAttrSet
->ClearItem( RES_PAGEDESC
) );
3030 aClearWhichIds
.push_back( RES_PAGEDESC
);
3032 if( SfxItemState::SET
== pTmpSet
->GetItemState( RES_BREAK
, false ) )
3035 oNewAttrSet
->ClearItem( RES_BREAK
);
3037 aClearWhichIds
.push_back( RES_BREAK
);
3038 bRemoveFromCache
= true;
3040 if( SfxItemState::SET
== pTmpSet
->GetItemState( RES_KEEP
, false ) )
3043 oNewAttrSet
->ClearItem( RES_KEEP
);
3045 aClearWhichIds
.push_back( RES_KEEP
);
3046 bRemoveFromCache
= true;
3048 if( SfxItemState::SET
== pTmpSet
->GetItemState( RES_PARATR_SPLIT
, false ) )
3051 oNewAttrSet
->ClearItem( RES_PARATR_SPLIT
);
3053 aClearWhichIds
.push_back( RES_PARATR_SPLIT
);
3054 bRemoveFromCache
= true;
3056 if(SfxItemState::SET
== pTmpSet
->GetItemState(RES_PARATR_NUMRULE
, false))
3058 SwNumRule
* pRule
= GetNumRule();
3060 if (pRule
&& IsOutline())
3063 oNewAttrSet
->ClearItem(RES_PARATR_NUMRULE
);
3067 // No clear of hard set numbering rule at an outline paragraph at this point.
3068 // Only if the paragraph style changes - see below.
3069 bClearHardSetNumRuleWhenFormatCollChanges
= true;
3071 bRemoveFromCache
= true;
3075 if ( !aClearWhichIds
.empty() )
3076 bRemoveFromCache
= 0 != ClearItemsFromAttrSet( aClearWhichIds
);
3078 if( !bNext
&& bRemoveFromCache
)
3080 InvalidateInSwCache(RES_OBJECTDYING
);
3083 SwNodes
& rNds
= GetNodes();
3085 SwTextFormatColl
* pColl
= GetTextColl();
3087 SwTextNode
*pNode
= new SwTextNode( rPosNd
, pColl
, oNewAttrSet
? &*oNewAttrSet
: nullptr );
3089 oNewAttrSet
.reset();
3091 const SwNumRule
* pRule
= GetNumRule();
3092 if( pRule
&& pRule
== pNode
->GetNumRule() && rNds
.IsDocNodes() )
3095 // - correction: parameter <bNext> has to be checked, as it was in the
3096 // previous implementation.
3097 if ( !bNext
&& !IsCountedInList() )
3098 SetCountedInList(true);
3101 // In case the numbering caused a style from the pool to be assigned to
3102 // the new node, don't overwrite that here!
3103 if( pColl
!= pNode
->GetTextColl() ||
3104 ( bChgFollow
&& pColl
!= GetTextColl() ))
3105 return pNode
; // that ought to be enough?
3107 pNode
->ChgTextCollUpdateNum( nullptr, pColl
); // for numbering/outline
3108 if( bNext
|| !bChgFollow
)
3111 SwTextFormatColl
*pNextColl
= &pColl
->GetNextTextFormatColl();
3112 // i#101870 perform action on different paragraph styles before applying
3113 // the new paragraph style
3114 if (pNextColl
!= pColl
)
3117 if ( bClearHardSetNumRuleWhenFormatCollChanges
)
3119 if ( ClearItemsFromAttrSet( { RES_PARATR_NUMRULE
} ) != 0 )
3121 InvalidateInSwCache(RES_ATTRSET_CHG
);
3125 ChgFormatColl( pNextColl
);
3130 SwContentNode
* SwTextNode::AppendNode( const SwPosition
& rPos
)
3132 // position behind which it will be inserted
3133 SwTextNode
* pNew
= MakeNewTextNode( *rPos
.GetNodes()[rPos
.GetNodeIndex() + 1] );
3135 // reset list attributes at appended text node
3136 pNew
->ResetAttr( RES_PARATR_LIST_ISRESTART
);
3137 pNew
->ResetAttr( RES_PARATR_LIST_RESTARTVALUE
);
3138 pNew
->ResetAttr( RES_PARATR_LIST_ISCOUNTED
);
3139 if ( pNew
->GetNumRule() == nullptr )
3141 pNew
->ResetAttr( RES_PARATR_LIST_ID
);
3142 pNew
->ResetAttr( RES_PARATR_LIST_LEVEL
);
3145 if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
3150 if( HasWriterListeners() )
3151 MakeFramesForAdjacentContentNode(*pNew
);
3155 SwTextAttr
* SwTextNode::GetTextAttrForCharAt(
3156 const sal_Int32 nIndex
,
3157 const sal_uInt16 nWhich
) const
3159 assert(nWhich
>= RES_TXTATR_BEGIN
&& nWhich
<= RES_TXTATR_END
);
3162 for ( size_t i
= 0; i
< m_pSwpHints
->Count(); ++i
)
3164 SwTextAttr
* const pHint
= m_pSwpHints
->Get(i
);
3165 const sal_Int32 nStartPos
= pHint
->GetStart();
3166 if ( nIndex
< nStartPos
)
3170 if ( (nIndex
== nStartPos
) && pHint
->HasDummyChar() )
3172 return ( RES_TXTATR_END
== nWhich
|| nWhich
== pHint
->Which() )
3180 SwTextAttr
* SwTextNode::GetTextAttrForEndCharAt(sal_Int32 nIndex
, sal_uInt16 nWhich
) const
3182 SwTextAttr
* pAttr
= GetTextAttrAt(nIndex
, nWhich
, ::sw::GetTextAttrMode::Expand
);
3193 // The start-end range covers the end dummy character.
3194 if (*pAttr
->End() - 1 != nIndex
)
3205 sal_uInt16
lcl_BoundListLevel(const int nActualLevel
)
3207 return o3tl::narrowing
<sal_uInt16
>( std::clamp( nActualLevel
, 0, MAXLEVEL
-1 ) );
3213 bool SwTextNode::HasNumber(SwRootFrame
const*const pLayout
) const
3215 bool bResult
= false;
3217 const SwNumRule
*const pRule
= GetNum(pLayout
) ? GetNum(pLayout
)->GetNumRule() : nullptr;
3220 const SwNumFormat
& aFormat(pRule
->Get(lcl_BoundListLevel(GetActualListLevel())));
3223 bResult
= aFormat
.IsEnumeration();
3229 bool SwTextNode::HasBullet() const
3231 bool bResult
= false;
3233 const SwNumRule
* pRule
= GetNum() ? GetNum()->GetNumRule() : nullptr;
3236 const SwNumFormat
& aFormat(pRule
->Get(lcl_BoundListLevel(GetActualListLevel())));
3238 bResult
= aFormat
.IsItemize();
3245 // #128041# - introduce parameter <_bInclPrefixAndSuffixStrings>
3246 //i53420 added max outline parameter
3247 OUString
SwTextNode::GetNumString( const bool _bInclPrefixAndSuffixStrings
,
3248 const unsigned int _nRestrictToThisLevel
,
3249 SwRootFrame
const*const pLayout
, SwListRedlineType eRedline
) const
3251 if (GetDoc().IsClipBoard() && m_oNumStringCache
)
3253 // #i111677# do not expand number strings in clipboard documents
3254 return *m_oNumStringCache
;
3256 const SwNumRule
* pRule
= GetNum(pLayout
, eRedline
) ? GetNum(pLayout
, eRedline
)->GetNumRule() : nullptr;
3260 SvxNumberType
const& rNumberType(
3261 pRule
->Get( lcl_BoundListLevel(GetActualListLevel(eRedline
)) ) );
3262 if (rNumberType
.IsTextFormat() ||
3264 (style::NumberingType::NUMBER_NONE
== rNumberType
.GetNumberingType()))
3266 return pRule
->MakeNumString( GetNum(pLayout
, eRedline
)->GetNumberVector(),
3267 _bInclPrefixAndSuffixStrings
,
3268 _nRestrictToThisLevel
,
3277 tools::Long
SwTextNode::GetLeftMarginWithNum( bool bTextLeft
) const
3279 tools::Long nRet
= 0;
3280 const SwNumRule
* pRule
= GetNum() ? GetNum()->GetNumRule() : nullptr;
3283 const SwNumFormat
& rFormat
= pRule
->Get(lcl_BoundListLevel(GetActualListLevel()));
3285 if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION
)
3287 nRet
= rFormat
.GetAbsLSpace();
3291 if( 0 > rFormat
.GetFirstLineOffset() &&
3292 nRet
> -rFormat
.GetFirstLineOffset() )
3293 nRet
= nRet
+ rFormat
.GetFirstLineOffset();
3298 if( pRule
->IsAbsSpaces() )
3300 SvxFirstLineIndentItem
const& rFirst(GetSwAttrSet().GetFirstLineIndent());
3301 nRet
= nRet
- GetSwAttrSet().GetTextLeftMargin().GetLeft(rFirst
);
3304 else if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT
)
3306 ::sw::ListLevelIndents
const indents(AreListLevelIndentsApplicable());
3307 // note: the result is *always* added to either the left-margin
3308 // or the text-left-margin of the node itself by the caller.
3309 // so first, subtract what the caller has computed anyway,
3310 // and then add the value according to combination of
3311 // list/paragraph items. (this is rather inelegant)
3312 SvxFirstLineIndentItem
firstLine(GetSwAttrSet().GetFirstLineIndent());
3313 SvxTextLeftMarginItem
leftMargin(GetSwAttrSet().GetTextLeftMargin());
3315 ? - leftMargin
.GetTextLeft()
3316 : - leftMargin
.GetLeft(firstLine
);
3317 if (indents
& ::sw::ListLevelIndents::LeftMargin
)
3319 leftMargin
.SetTextLeft(rFormat
.GetIndentAt());
3321 if (indents
& ::sw::ListLevelIndents::FirstLine
)
3323 firstLine
.SetTextFirstLineOffset(rFormat
.GetFirstLineIndent());
3326 ? leftMargin
.GetTextLeft()
3327 : leftMargin
.GetLeft(firstLine
);
3334 bool SwTextNode::GetFirstLineOfsWithNum( short& rFLOffset
) const
3340 const SwNumRule
* pRule
= GetNum() ? GetNum()->GetNumRule() : nullptr;
3343 if ( IsCountedInList() )
3345 const SwNumFormat
& rFormat
= pRule
->Get(lcl_BoundListLevel(GetActualListLevel()));
3346 if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION
)
3348 rFLOffset
= rFormat
.GetFirstLineOffset(); //TODO: overflow
3350 if (!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING
))
3352 SvxFirstLineIndentItem
const aItem(GetSwAttrSet().GetFirstLineIndent());
3353 rFLOffset
= rFLOffset
+ aItem
.GetTextFirstLineOffset();
3356 else if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT
)
3358 if (AreListLevelIndentsApplicable() & ::sw::ListLevelIndents::FirstLine
)
3360 rFLOffset
= rFormat
.GetFirstLineIndent();
3362 else if (!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING
))
3364 SvxFirstLineIndentItem
const aItem(GetSwAttrSet().GetFirstLineIndent());
3365 rFLOffset
= aItem
.GetTextFirstLineOffset();
3373 rFLOffset
= GetSwAttrSet().GetFirstLineIndent().GetTextFirstLineOffset();
3377 SwTwips
SwTextNode::GetAdditionalIndentForStartingNewList() const
3379 SwTwips nAdditionalIndent
= 0;
3381 const SwNumRule
* pRule
= GetNum() ? GetNum()->GetNumRule() : nullptr;
3384 const SwNumFormat
& rFormat
= pRule
->Get(lcl_BoundListLevel(GetActualListLevel()));
3385 if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION
)
3387 SvxFirstLineIndentItem
const& rFirst(GetSwAttrSet().GetFirstLineIndent());
3388 nAdditionalIndent
= GetSwAttrSet().GetTextLeftMargin().GetLeft(rFirst
);
3390 if (getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING
))
3392 nAdditionalIndent
= nAdditionalIndent
-
3393 GetSwAttrSet().GetFirstLineIndent().GetTextFirstLineOffset();
3396 else if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT
)
3398 // note: there was an apparent bug here, list GetIndentAt()
3399 // was interpreted as left-margin not text-left-margin unlike every
3401 ::sw::ListLevelIndents
const indents(AreListLevelIndentsApplicable());
3402 SvxFirstLineIndentItem
const& rFirst(
3403 indents
& ::sw::ListLevelIndents::FirstLine
3404 ? SvxFirstLineIndentItem(rFormat
.GetFirstLineIndent(), RES_MARGIN_FIRSTLINE
)
3405 : GetSwAttrSet().GetFirstLineIndent());
3406 SvxTextLeftMarginItem
const& rLeft(
3407 indents
& ::sw::ListLevelIndents::LeftMargin
3408 ? SvxTextLeftMarginItem(rFormat
.GetIndentAt(), RES_MARGIN_TEXTLEFT
)
3409 : GetSwAttrSet().GetTextLeftMargin());
3410 nAdditionalIndent
= rLeft
.GetLeft(rFirst
);
3411 if (!(indents
& ::sw::ListLevelIndents::FirstLine
))
3413 if (getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING
))
3415 nAdditionalIndent
= nAdditionalIndent
- rFirst
.GetTextFirstLineOffset();
3422 SvxFirstLineIndentItem
const& rFirst(GetSwAttrSet().GetFirstLineIndent());
3423 nAdditionalIndent
= GetSwAttrSet().GetTextLeftMargin().GetLeft(rFirst
);
3426 return nAdditionalIndent
;
3430 void SwTextNode::ClearLRSpaceItemDueToListLevelIndents(
3431 std::unique_ptr
<SvxFirstLineIndentItem
>& o_rFirstLineItem
,
3432 std::unique_ptr
<SvxTextLeftMarginItem
>& o_rTextLeftMarginItem
) const
3434 ::sw::ListLevelIndents
const result(AreListLevelIndentsApplicable());
3435 if (result
!= ::sw::ListLevelIndents::No
)
3437 const SwNumRule
* pRule
= GetNumRule();
3438 if ( pRule
&& GetActualListLevel() >= 0 )
3440 const SwNumFormat
& rFormat
= pRule
->Get(lcl_BoundListLevel(GetActualListLevel()));
3441 if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT
)
3443 if (result
& ::sw::ListLevelIndents::FirstLine
)
3445 o_rFirstLineItem
= std::make_unique
<SvxFirstLineIndentItem
>(RES_MARGIN_FIRSTLINE
);
3447 if (result
& ::sw::ListLevelIndents::LeftMargin
)
3449 o_rTextLeftMarginItem
= std::make_unique
<SvxTextLeftMarginItem
>(RES_MARGIN_TEXTLEFT
);
3457 tools::Long
SwTextNode::GetLeftMarginForTabCalculation() const
3459 tools::Long nLeftMarginForTabCalc
= 0;
3461 bool bLeftMarginForTabCalcSetToListLevelIndent( false );
3462 const SwNumRule
* pRule
= GetNum() ? GetNum()->GetNumRule() : nullptr;
3465 const SwNumFormat
& rFormat
= pRule
->Get(lcl_BoundListLevel(GetActualListLevel()));
3466 if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT
)
3468 if (AreListLevelIndentsApplicable() & ::sw::ListLevelIndents::LeftMargin
)
3470 nLeftMarginForTabCalc
= rFormat
.GetIndentAt();
3471 bLeftMarginForTabCalcSetToListLevelIndent
= true;
3475 if ( !bLeftMarginForTabCalcSetToListLevelIndent
)
3477 nLeftMarginForTabCalc
= GetSwAttrSet().GetTextLeftMargin().GetTextLeft();
3480 return nLeftMarginForTabCalc
;
3483 static void Replace0xFF(
3484 SwTextNode
const& rNode
,
3485 OUStringBuffer
& rText
,
3486 sal_Int32
& rTextStt
,
3489 if (!rNode
.GetpSwpHints())
3492 sal_Unicode cSrchChr
= CH_TXTATR_BREAKWORD
;
3493 for( int nSrchIter
= 0; 2 > nSrchIter
; ++nSrchIter
, cSrchChr
= CH_TXTATR_INWORD
)
3495 sal_Int32 nPos
= rText
.indexOf(cSrchChr
);
3496 while (-1 != nPos
&& nPos
< nEndPos
)
3498 const SwTextAttr
* const pAttr
=
3499 rNode
.GetTextAttrForCharAt(rTextStt
+ nPos
);
3502 switch( pAttr
->Which() )
3504 case RES_TXTATR_FIELD
:
3505 case RES_TXTATR_ANNOTATION
:
3506 rText
.remove(nPos
, 1);
3510 case RES_TXTATR_FTN
:
3511 rText
.remove(nPos
, 1);
3516 rText
.remove(nPos
, 1);
3525 nPos
= rText
.indexOf(cSrchChr
, nPos
);
3531 // #i83479# - handling of new parameters
3532 OUString
SwTextNode::GetExpandText(SwRootFrame
const*const pLayout
,
3533 const sal_Int32 nIdx
,
3534 const sal_Int32 nLen
,
3535 const bool bWithNum
,
3536 const bool bAddSpaceAfterListLabelStr
,
3537 const bool bWithSpacesForLevel
,
3538 const ExpandMode eAdditionalMode
) const
3541 ExpandMode eMode
= ExpandMode::ExpandFields
| eAdditionalMode
;
3542 if (pLayout
&& pLayout
->IsHideRedlines())
3544 eMode
|= ExpandMode::HideDeletions
;
3547 ModelToViewHelper
aConversionMap(*this, pLayout
, eMode
);
3548 const OUString aExpandText
= aConversionMap
.getViewText();
3549 const sal_Int32 nExpandBegin
= aConversionMap
.ConvertToViewPosition( nIdx
);
3550 sal_Int32 nEnd
= nLen
== -1 ? GetText().getLength() : nIdx
+ nLen
;
3551 const sal_Int32 nExpandEnd
= aConversionMap
.ConvertToViewPosition( nEnd
);
3552 OUStringBuffer
aText(aExpandText
.subView(nExpandBegin
, nExpandEnd
-nExpandBegin
));
3554 // remove dummy characters of Input Fields
3555 comphelper::string::remove(aText
, CH_TXT_ATR_INPUTFIELDSTART
);
3556 comphelper::string::remove(aText
, CH_TXT_ATR_INPUTFIELDEND
);
3557 comphelper::string::remove(aText
, CH_TXTATR_BREAKWORD
);
3561 if (!GetNumString(true, MAXLEVEL
, pLayout
).isEmpty())
3563 if ( bAddSpaceAfterListLabelStr
)
3565 const sal_Unicode aSpace
= ' ';
3566 aText
.insert(0, aSpace
);
3568 aText
.insert(0, GetNumString(true, MAXLEVEL
, pLayout
));
3572 if (bWithSpacesForLevel
)
3574 const sal_Unicode aSpace
= ' ';
3575 for (int nLevel
= GetActualListLevel(); nLevel
> 0; --nLevel
)
3577 aText
.insert(0, aSpace
);
3578 aText
.insert(0, aSpace
);
3582 return aText
.makeStringAndClear();
3585 bool SwTextNode::CopyExpandText(SwTextNode
& rDestNd
, const SwContentIndex
* pDestIdx
,
3586 sal_Int32 nIdx
, sal_Int32 nLen
,
3587 SwRootFrame
const*const pLayout
, bool bWithNum
,
3588 bool bWithFootnote
, bool bReplaceTabsWithSpaces
) const
3590 if( &rDestNd
== this )
3592 assert(!pDestIdx
|| pDestIdx
->GetContentNode() == &rDestNd
);
3594 SwContentIndex
aDestIdx(&rDestNd
, rDestNd
.GetText().getLength());
3596 aDestIdx
= *pDestIdx
;
3597 const sal_Int32 nDestStt
= aDestIdx
.GetIndex();
3599 // first, start with the text
3600 OUStringBuffer
buf(GetText());
3601 if( bReplaceTabsWithSpaces
)
3602 buf
.replace('\t', ' ');
3604 // mask hidden characters
3605 const sal_Unicode cChar
= CH_TXTATR_BREAKWORD
;
3606 SwScriptInfo::MaskHiddenRanges(*this, buf
, 0, buf
.getLength(), cChar
);
3608 buf
.remove(0, nIdx
);
3613 // remove dummy characters of Input Fields
3615 comphelper::string::remove(buf
, CH_TXT_ATR_INPUTFIELDSTART
);
3616 comphelper::string::remove(buf
, CH_TXT_ATR_INPUTFIELDEND
);
3618 rDestNd
.InsertText(buf
.makeStringAndClear(), aDestIdx
);
3619 nLen
= aDestIdx
.GetIndex() - nDestStt
;
3621 // set all char attributes with Symbol font
3624 sal_Int32 nInsPos
= nDestStt
- nIdx
;
3625 for ( size_t i
= 0; i
< m_pSwpHints
->Count(); ++i
)
3627 const SwTextAttr
* pHt
= m_pSwpHints
->Get(i
);
3628 const sal_Int32 nAttrStartIdx
= pHt
->GetStart();
3629 const sal_uInt16 nWhich
= pHt
->Which();
3630 if (nIdx
+ nLen
<= nAttrStartIdx
)
3631 break; // behind end of text
3633 const sal_Int32
*pEndIdx
= pHt
->End();
3634 if( pEndIdx
&& *pEndIdx
> nIdx
&&
3635 ( RES_CHRATR_FONT
== nWhich
||
3636 RES_TXTATR_CHARFMT
== nWhich
||
3637 RES_TXTATR_AUTOFMT
== nWhich
))
3639 const SvxFontItem
* const pFont
=
3640 CharFormat::GetItem( *pHt
, RES_CHRATR_FONT
);
3641 if ( pFont
&& RTL_TEXTENCODING_SYMBOL
== pFont
->GetCharSet() )
3643 // attribute in area => copy
3644 rDestNd
.InsertItem( *const_cast<SvxFontItem
*>(pFont
),
3645 nInsPos
+ nAttrStartIdx
, nInsPos
+ *pEndIdx
);
3648 else if ( pHt
->HasDummyChar() && (nAttrStartIdx
>= nIdx
) )
3650 aDestIdx
= nInsPos
+ nAttrStartIdx
;
3653 case RES_TXTATR_FIELD
:
3654 case RES_TXTATR_ANNOTATION
:
3656 OUString
const aExpand(
3657 static_txtattr_cast
<SwTextField
const*>(pHt
)->GetFormatField().GetField()->ExpandField(true, pLayout
));
3658 if (!aExpand
.isEmpty())
3660 ++aDestIdx
; // insert behind
3662 rDestNd
.InsertText( aExpand
, aDestIdx
));
3663 SAL_INFO_IF(ins
.getLength() != aExpand
.getLength(),
3664 "sw.core", "GetExpandText lossage");
3665 aDestIdx
= nInsPos
+ nAttrStartIdx
;
3666 nInsPos
+= ins
.getLength();
3668 rDestNd
.EraseText( aDestIdx
, 1 );
3673 case RES_TXTATR_FTN
:
3675 if ( bWithFootnote
)
3677 const SwFormatFootnote
& rFootnote
= pHt
->GetFootnote();
3679 auto const number(pLayout
&& pLayout
->IsHideRedlines()
3680 ? rFootnote
.GetNumberRLHidden()
3681 : rFootnote
.GetNumber());
3682 if( !rFootnote
.GetNumStr().isEmpty() )
3683 sExpand
= rFootnote
.GetNumStr();
3684 else if( rFootnote
.IsEndNote() )
3685 sExpand
= GetDoc().GetEndNoteInfo().m_aFormat
.
3688 sExpand
= GetDoc().GetFootnoteInfo().m_aFormat
.
3690 if( !sExpand
.isEmpty() )
3692 ++aDestIdx
; // insert behind
3693 SvxEscapementItem
aItem( SvxEscapement::Superscript
, RES_CHRATR_ESCAPEMENT
);
3696 aDestIdx
.GetIndex(),
3697 aDestIdx
.GetIndex() );
3698 OUString
const ins( rDestNd
.InsertText(sExpand
, aDestIdx
, SwInsertFlags::EMPTYEXPAND
));
3699 SAL_INFO_IF(ins
.getLength() != sExpand
.getLength(),
3700 "sw.core", "GetExpandText lossage");
3701 aDestIdx
= nInsPos
+ nAttrStartIdx
;
3702 nInsPos
+= ins
.getLength();
3705 rDestNd
.EraseText( aDestIdx
, 1 );
3711 rDestNd
.EraseText( aDestIdx
, 1 );
3720 aDestIdx
= nDestStt
;
3721 rDestNd
.InsertText( GetNumString(true, MAXLEVEL
, pLayout
), aDestIdx
);
3725 sal_Int32
nStartDelete(-1);
3726 while (aDestIdx
< rDestNd
.GetText().getLength())
3728 sal_Unicode
const cur(rDestNd
.GetText()[aDestIdx
.GetIndex()]);
3729 if ( (cChar
== cur
) // filter substituted hidden text
3730 || (CH_TXT_ATR_FIELDSTART
== cur
) // filter all fieldmarks
3731 || (CH_TXT_ATR_FIELDSEP
== cur
)
3732 || (CH_TXT_ATR_FIELDEND
== cur
)
3733 || (CH_TXT_ATR_FORMELEMENT
== cur
))
3735 if (-1 == nStartDelete
)
3737 nStartDelete
= aDestIdx
.GetIndex(); // start deletion range
3740 if (aDestIdx
< rDestNd
.GetText().getLength())
3743 } // else: end of paragraph => delete, see below
3747 if (-1 == nStartDelete
)
3751 } // else: delete, see below
3753 assert(-1 != nStartDelete
); // without delete range, would have continued
3755 SwContentIndex(&rDestNd
, nStartDelete
),
3756 aDestIdx
.GetIndex() - nStartDelete
);
3757 assert(aDestIdx
.GetIndex() == nStartDelete
);
3758 nStartDelete
= -1; // reset
3764 OUString
SwTextNode::GetRedlineText() const
3766 std::vector
<sal_Int32
> aRedlArr
;
3767 const SwDoc
& rDoc
= GetDoc();
3768 SwRedlineTable::size_type nRedlPos
= rDoc
.getIDocumentRedlineAccess().GetRedlinePos( *this, RedlineType::Delete
);
3769 if( SwRedlineTable::npos
!= nRedlPos
)
3771 // some redline-delete object exists for the node
3772 const SwNodeOffset nNdIdx
= GetIndex();
3773 for( ; nRedlPos
< rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size() ; ++nRedlPos
)
3775 const SwRangeRedline
* pTmp
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos
];
3776 if( RedlineType::Delete
== pTmp
->GetType() )
3778 const SwPosition
*pRStt
= pTmp
->Start(), *pREnd
= pTmp
->End();
3779 if( pRStt
->GetNodeIndex() < nNdIdx
)
3781 if( pREnd
->GetNodeIndex() > nNdIdx
)
3782 // paragraph is fully deleted
3784 else if( pREnd
->GetNodeIndex() == nNdIdx
)
3786 // deleted from 0 to nContent
3787 aRedlArr
.push_back( 0 );
3788 aRedlArr
.push_back( pREnd
->GetContentIndex() );
3791 else if( pRStt
->GetNodeIndex() == nNdIdx
)
3793 //aRedlArr.Insert( pRStt->GetContentIndex(), aRedlArr.Count() );
3794 aRedlArr
.push_back( pRStt
->GetContentIndex() );
3795 if( pREnd
->GetNodeIndex() == nNdIdx
)
3796 aRedlArr
.push_back( pREnd
->GetContentIndex() );
3799 aRedlArr
.push_back(GetText().getLength());
3800 break; // that was all
3804 break; // that was all
3809 OUStringBuffer
aText(GetText());
3811 sal_Int32 nTextStt
= 0;
3812 sal_Int32 nIdxEnd
= aText
.getLength();
3813 for( size_t n
= 0; n
< aRedlArr
.size(); n
+= 2 )
3815 sal_Int32 nStt
= aRedlArr
[ n
];
3816 sal_Int32 nEnd
= aRedlArr
[ n
+1 ];
3817 if( ( 0 <= nStt
&& nStt
<= nIdxEnd
) ||
3818 ( 0 <= nEnd
&& nEnd
<= nIdxEnd
))
3820 if( nStt
< 0 ) nStt
= 0;
3821 if( nIdxEnd
< nEnd
) nEnd
= nIdxEnd
;
3822 const sal_Int32 nDelCnt
= nEnd
- nStt
;
3823 aText
.remove(nStt
- nTextStt
, nDelCnt
);
3824 Replace0xFF(*this, aText
, nTextStt
, nStt
- nTextStt
);
3825 nTextStt
+= nDelCnt
;
3827 else if( nStt
>= nIdxEnd
)
3830 Replace0xFF(*this, aText
, nTextStt
, aText
.getLength());
3832 return aText
.makeStringAndClear();
3835 void SwTextNode::ReplaceText( const SwContentIndex
& rStart
, const sal_Int32 nDelLen
,
3836 const OUString
& rStr
)
3838 assert(rStart
.GetContentNode() == this);
3839 assert( rStart
.GetIndex() < m_Text
.getLength() // index out of bounds
3840 && rStart
.GetIndex() + nDelLen
<= m_Text
.getLength());
3842 sal_Int32
const nOverflow(rStr
.getLength() - nDelLen
- GetSpaceLeft());
3843 SAL_WARN_IF(nOverflow
> 0, "sw.core",
3844 "SwTextNode::ReplaceText: node text with insertion > node capacity.");
3845 OUString
const sInserted(
3846 (nOverflow
> 0) ? rStr
.copy(0, rStr
.getLength() - nOverflow
) : rStr
);
3847 if (sInserted
.isEmpty() && 0 == nDelLen
)
3849 return; // nothing to do
3852 const sal_Int32 nStartPos
= rStart
.GetIndex();
3853 sal_Int32 nEndPos
= nStartPos
+ nDelLen
;
3854 sal_Int32 nLen
= nDelLen
;
3855 for( sal_Int32 nPos
= nStartPos
; nPos
< nEndPos
; ++nPos
)
3857 if ((CH_TXTATR_BREAKWORD
== m_Text
[nPos
]) ||
3858 (CH_TXTATR_INWORD
== m_Text
[nPos
]))
3860 SwTextAttr
*const pHint
= GetTextAttrForCharAt( nPos
);
3863 assert(!( pHint
->GetEnd() && pHint
->HasDummyChar()
3864 && (pHint
->GetStart() < nEndPos
)
3865 && (*pHint
->GetEnd() > nEndPos
) ));
3866 // "deleting left-overlapped attribute with CH_TXTATR"
3867 DeleteAttribute( pHint
);
3874 bool bOldExpFlg
= IsIgnoreDontExpand();
3875 SetIgnoreDontExpand( true );
3877 if (nLen
&& sInserted
.getLength())
3879 // Replace the 1st char, then delete the rest and insert.
3880 // This way the attributes of the 1st char are expanded!
3881 m_Text
= m_Text
.replaceAt(nStartPos
, 1, sInserted
.subView(0, 1));
3883 ++const_cast<SwContentIndex
&>(rStart
);
3884 m_Text
= m_Text
.replaceAt(rStart
.GetIndex(), nLen
- 1, u
"");
3885 Update(rStart
, nLen
- 1, UpdateMode::Negative
);
3887 std::u16string_view
aTmpText( sInserted
.subView(1) );
3888 m_Text
= m_Text
.replaceAt(rStart
.GetIndex(), 0, aTmpText
);
3889 Update(rStart
, aTmpText
.size(), UpdateMode::Replace
);
3893 m_Text
= m_Text
.replaceAt(nStartPos
, nLen
, u
"");
3894 Update(rStart
, nLen
, UpdateMode::Negative
);
3896 m_Text
= m_Text
.replaceAt(nStartPos
, 0, sInserted
);
3897 Update(rStart
, sInserted
.getLength(), UpdateMode::Replace
);
3900 SetIgnoreDontExpand( bOldExpFlg
);
3901 auto aDelHint
= sw::DeleteText(nStartPos
, nDelLen
);
3902 CallSwClientNotify(aDelHint
);
3904 if (sInserted
.getLength())
3906 auto aInsHint
= sw::MakeInsertText(*this, nStartPos
, sInserted
.getLength());
3907 CallSwClientNotify(aInsHint
);
3911 void SwTextNode::ReplaceText( SwPosition
& rStart
, const sal_Int32 nDelLen
,
3912 const OUString
& rStr
)
3914 ReplaceText(rStart
.nContent
, nDelLen
, rStr
);
3918 void lcl_ResetParAttrs( SwTextNode
&rTextNode
)
3920 const o3tl::sorted_vector
<sal_uInt16
> aAttrs
{ RES_PARATR_LIST_ID
, RES_PARATR_LIST_LEVEL
,
3921 RES_PARATR_LIST_ISRESTART
,
3922 RES_PARATR_LIST_RESTARTVALUE
,
3923 RES_PARATR_LIST_ISCOUNTED
};
3924 SwPaM
aPam( rTextNode
);
3926 // suppress side effect "send data changed events"
3927 rTextNode
.GetDoc().ResetAttrs( aPam
, false, aAttrs
, false );
3930 // Helper method for special handling of modified attributes at text node.
3931 // The following is handled:
3932 // (1) on changing the paragraph style - RES_FMT_CHG:
3933 // Check, if list style of the text node is changed. If yes, add respectively
3934 // remove the text node to the corresponding list.
3935 // (2) on changing the attributes - RES_ATTRSET_CHG:
3937 // (3) on changing the list style - RES_PARATR_NUMRULE:
3939 void HandleModifyAtTextNode( SwTextNode
& rTextNode
,
3940 const SfxPoolItem
* pOldValue
,
3941 const SfxPoolItem
* pNewValue
)
3943 const sal_uInt16 nWhich
= pOldValue
? pOldValue
->Which() :
3944 pNewValue
? pNewValue
->Which() : 0 ;
3945 bool bNumRuleSet
= false;
3946 bool bParagraphStyleChanged
= false;
3948 OUString sOldNumRule
;
3953 bParagraphStyleChanged
= true;
3954 if( rTextNode
.GetNodes().IsDocNodes() )
3956 const SwNumRule
* pFormerNumRuleAtTextNode
=
3957 rTextNode
.GetNum() ? rTextNode
.GetNum()->GetNumRule() : nullptr;
3958 if ( pFormerNumRuleAtTextNode
)
3960 sOldNumRule
= pFormerNumRuleAtTextNode
->GetName();
3962 if ( rTextNode
.IsEmptyListStyleDueToSetOutlineLevelAttr() )
3964 const SwNumRuleItem
& rNumRuleItem
= rTextNode
.GetTextColl()->GetNumRule();
3965 if ( !rNumRuleItem
.GetValue().isEmpty() )
3967 rTextNode
.ResetEmptyListStyleDueToResetOutlineLevelAttr();
3970 const SwNumRule
* pNumRuleAtTextNode
= rTextNode
.GetNumRule();
3971 if ( pNumRuleAtTextNode
)
3974 sNumRule
= pNumRuleAtTextNode
->GetName();
3979 case RES_ATTRSET_CHG
:
3981 const SwNumRule
* pFormerNumRuleAtTextNode
=
3982 rTextNode
.GetNum() ? rTextNode
.GetNum()->GetNumRule() : nullptr;
3983 if ( pFormerNumRuleAtTextNode
)
3985 sOldNumRule
= pFormerNumRuleAtTextNode
->GetName();
3988 const SwAttrSetChg
* pSet
= dynamic_cast<const SwAttrSetChg
*>(pNewValue
);
3989 if ( pSet
&& pSet
->GetChgSet()->GetItemState( RES_PARATR_NUMRULE
, false ) ==
3993 rTextNode
.ResetEmptyListStyleDueToResetOutlineLevelAttr();
3997 // The new list style set at the paragraph.
3998 const SwNumRule
* pNumRuleAtTextNode
= rTextNode
.GetNumRule();
3999 if ( pNumRuleAtTextNode
)
4001 sNumRule
= pNumRuleAtTextNode
->GetName();
4005 case RES_PARATR_NUMRULE
:
4007 if ( rTextNode
.GetNodes().IsDocNodes() )
4009 const SwNumRule
* pFormerNumRuleAtTextNode
=
4010 rTextNode
.GetNum() ? rTextNode
.GetNum()->GetNumRule() : nullptr;
4011 if ( pFormerNumRuleAtTextNode
)
4013 sOldNumRule
= pFormerNumRuleAtTextNode
->GetName();
4019 rTextNode
.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4023 // The new list style set at the paragraph.
4024 const SwNumRule
* pNumRuleAtTextNode
= rTextNode
.GetNumRule();
4025 if ( pNumRuleAtTextNode
)
4027 sNumRule
= pNumRuleAtTextNode
->GetName();
4033 if ( sNumRule
!= sOldNumRule
)
4037 if (sNumRule
.isEmpty())
4039 rTextNode
.RemoveFromList();
4040 if ( bParagraphStyleChanged
)
4042 lcl_ResetParAttrs(rTextNode
);
4047 rTextNode
.RemoveFromList();
4048 // If new list style is the outline style, apply outline
4049 // level as the list level.
4050 if (sNumRule
==SwNumRule::GetOutlineRuleName())
4053 OSL_ENSURE( rTextNode
.GetTextColl()->IsAssignedToListLevelOfOutlineStyle(),
4054 "<HandleModifyAtTextNode()> - text node with outline style, but its paragraph style is not assigned to outline style." );
4055 const int nNewListLevel
=
4056 rTextNode
.GetTextColl()->GetAssignedOutlineStyleLevel();
4057 if ( 0 <= nNewListLevel
&& nNewListLevel
< MAXLEVEL
)
4059 rTextNode
.SetAttrListLevel( nNewListLevel
);
4062 rTextNode
.AddToList();
4065 else // <sNumRule.Len() == 0 && sOldNumRule.Len() != 0>
4067 rTextNode
.RemoveFromList();
4068 if ( bParagraphStyleChanged
)
4070 lcl_ResetParAttrs(rTextNode
);
4072 if ( rTextNode
.GetAttr( RES_PARATR_OUTLINELEVEL
, false ).GetValue() > 0 )
4074 rTextNode
.SetEmptyListStyleDueToSetOutlineLevelAttr();
4079 else if (!sNumRule
.isEmpty() && !rTextNode
.IsInList())
4081 rTextNode
.AddToList();
4084 // End of method <HandleModifyAtTextNode>
4087 SwFormatColl
* SwTextNode::ChgFormatColl( SwFormatColl
*pNewColl
)
4089 OSL_ENSURE( pNewColl
,"ChgFormatColl: Collectionpointer has value 0." );
4090 assert( dynamic_cast<const SwTextFormatColl
*>(pNewColl
) && "ChgFormatColl: is not a Text Collection pointer." );
4092 SwTextFormatColl
*pOldColl
= GetTextColl();
4093 if( pNewColl
!= pOldColl
)
4095 SetCalcHiddenCharFlags();
4096 SwContentNode::ChgFormatColl( pNewColl
);
4097 OSL_ENSURE( !mbInSetOrResetAttr
,
4098 "DEBUG OSL_ENSURE(ON - <SwTextNode::ChgFormatColl(..)> called during <Set/ResetAttr(..)>" );
4099 if ( !mbInSetOrResetAttr
)
4101 SwFormatChg
aTmp1( pOldColl
);
4102 SwFormatChg
aTmp2( pNewColl
);
4103 HandleModifyAtTextNode( *this, &aTmp1
, &aTmp2
);
4106 // reset fill information on parent style change
4107 if(maFillAttributes
)
4109 maFillAttributes
.reset();
4113 // only for real nodes-array
4114 if( GetNodes().IsDocNodes() )
4116 ChgTextCollUpdateNum( pOldColl
, static_cast<SwTextFormatColl
*>(pNewColl
) );
4122 const SwNodeNum
* SwTextNode::GetNum(SwRootFrame
const*const pLayout
, SwListRedlineType eRedline
) const
4124 // invariant: it's only in list in Hide mode if it's in list in normal mode
4125 assert(mpNodeNum
|| !mpNodeNumRLHidden
);
4126 return (pLayout
&& pLayout
->IsHideRedlines()) || SwListRedlineType::HIDDEN
== eRedline
4127 ? mpNodeNumRLHidden
.get()
4128 : ( SwListRedlineType::ORIGTEXT
== eRedline
? mpNodeNumOrig
.get() : mpNodeNum
.get() );
4131 void SwTextNode::DoNum(std::function
<void (SwNodeNum
&)> const& rFunc
)
4133 // temp. clear because GetActualListLevel() may be called and the assert
4134 // there triggered during update, which is unhelpful
4135 std::unique_ptr
<SwNodeNum
> pBackup
= std::move(mpNodeNumRLHidden
);
4136 std::unique_ptr
<SwNodeNum
> pBackup2
= std::move(mpNodeNumOrig
);
4141 mpNodeNumRLHidden
= std::move(pBackup
);
4142 rFunc(*mpNodeNumRLHidden
);
4146 mpNodeNumOrig
= std::move(pBackup2
);
4147 rFunc(*mpNodeNumOrig
);
4151 SwNumberTree::tNumberVector
4152 SwTextNode::GetNumberVector(SwRootFrame
const*const pLayout
, SwListRedlineType eRedline
) const
4154 if (SwNodeNum
const*const pNum
= GetNum(pLayout
, eRedline
))
4156 return pNum
->GetNumberVector();
4160 SwNumberTree::tNumberVector aResult
;
4165 bool SwTextNode::IsOutline() const
4167 bool bResult
= false;
4169 if ( GetAttrOutlineLevel() > 0 )
4171 bResult
= !IsInRedlines();
4175 const SwNumRule
* pRule( GetNum() ? GetNum()->GetNumRule() : nullptr );
4176 if ( pRule
&& pRule
->IsOutlineRule() )
4178 bResult
= !IsInRedlines();
4185 bool SwTextNode::IsOutlineStateChanged() const
4187 return IsOutline() != m_bLastOutlineState
;
4190 void SwTextNode::UpdateOutlineState()
4192 m_bLastOutlineState
= IsOutline();
4195 int SwTextNode::GetAttrOutlineLevel() const
4197 return GetAttr(RES_PARATR_OUTLINELEVEL
).GetValue();
4200 void SwTextNode::SetAttrOutlineLevel(int nLevel
)
4202 assert(0 <= nLevel
&& nLevel
<= MAXLEVEL
); // Level Out Of Range
4203 if ( 0 <= nLevel
&& nLevel
<= MAXLEVEL
)
4205 SetAttr( SfxUInt16Item( RES_PARATR_OUTLINELEVEL
,
4206 o3tl::narrowing
<sal_uInt16
>(nLevel
) ) );
4210 void SwTextNode::GetAttrOutlineContentVisible(bool& bOutlineContentVisibleAttr
)
4212 const SfxGrabBagItem
& rGrabBagItem
= GetAttr(RES_PARATR_GRABBAG
);
4213 auto it
= rGrabBagItem
.GetGrabBag().find("OutlineContentVisibleAttr");
4214 if (it
!= rGrabBagItem
.GetGrabBag().end())
4215 it
->second
>>= bOutlineContentVisibleAttr
;
4218 void SwTextNode::SetAttrOutlineContentVisible(bool bVisible
)
4220 SfxGrabBagItem
aGrabBagItem(RES_PARATR_GRABBAG
);
4221 aGrabBagItem
.GetGrabBag()["OutlineContentVisibleAttr"] <<= bVisible
;
4222 SetAttr(aGrabBagItem
);
4227 void SwTextNode::SetEmptyListStyleDueToSetOutlineLevelAttr()
4229 if ( !mbEmptyListStyleSetDueToSetOutlineLevelAttr
)
4231 SetAttr( SwNumRuleItem() );
4232 mbEmptyListStyleSetDueToSetOutlineLevelAttr
= true;
4236 void SwTextNode::ResetEmptyListStyleDueToResetOutlineLevelAttr()
4238 if ( mbEmptyListStyleSetDueToSetOutlineLevelAttr
)
4240 ResetAttr( RES_PARATR_NUMRULE
);
4241 mbEmptyListStyleSetDueToSetOutlineLevelAttr
= false;
4245 void SwTextNode::SetAttrListLevel( int nLevel
)
4247 if ( nLevel
< 0 || nLevel
>= MAXLEVEL
)
4249 assert(false); // invalid level
4253 SfxInt16Item
aNewListLevelItem( RES_PARATR_LIST_LEVEL
,
4254 static_cast<sal_Int16
>(nLevel
) );
4255 SetAttr( aNewListLevelItem
);
4258 bool SwTextNode::HasAttrListLevel() const
4260 return GetpSwAttrSet() &&
4261 GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_LEVEL
, false ) == SfxItemState::SET
;
4264 int SwTextNode::GetAttrListLevel() const
4266 int nAttrListLevel
= 0;
4268 const SfxInt16Item
& aListLevelItem
=
4269 GetAttr( RES_PARATR_LIST_LEVEL
);
4270 nAttrListLevel
= static_cast<int>(aListLevelItem
.GetValue());
4272 return nAttrListLevel
;
4275 int SwTextNode::GetActualListLevel(SwListRedlineType eRedline
) const
4277 assert(SwListRedlineType::SHOW
!= eRedline
||
4278 !GetNum(nullptr, SwListRedlineType::SHOW
) || !mpNodeNumRLHidden
|| // must be in sync
4279 GetNum(nullptr, SwListRedlineType::SHOW
)->GetLevelInListTree() ==
4280 mpNodeNumRLHidden
->GetLevelInListTree());
4281 return GetNum(nullptr, eRedline
) ? GetNum(nullptr, eRedline
)->GetLevelInListTree() : -1;
4284 void SwTextNode::SetListRestart( bool bRestart
)
4288 // attribute not contained in paragraph style's attribute set. Thus,
4289 // it can be reset to the attribute pool default by resetting the attribute.
4290 ResetAttr( RES_PARATR_LIST_ISRESTART
);
4294 SfxBoolItem
aNewIsRestartItem( RES_PARATR_LIST_ISRESTART
,
4296 SetAttr( aNewIsRestartItem
);
4300 bool SwTextNode::IsListRestart() const
4302 const SfxBoolItem
& aIsRestartItem
= GetAttr( RES_PARATR_LIST_ISRESTART
);
4304 return aIsRestartItem
.GetValue();
4307 /** Returns if the paragraph has a visible numbering or bullet.
4308 This includes all kinds of numbering/bullet/outlines.
4309 The concrete list label string has to be checked, too.
4311 bool SwTextNode::HasVisibleNumberingOrBullet() const
4313 const SwNumRule
* pRule
= GetNum() ? GetNum()->GetNumRule() : nullptr;
4314 if ( pRule
&& IsCountedInList())
4316 const SwNumFormat
& rFormat
= pRule
->Get(lcl_BoundListLevel(GetActualListLevel()));
4317 if (getIDocumentSettingAccess()->get(DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY
))
4318 // True if we have something in label text or there is a non-empty
4319 // FollowedBy separator (space, tab or whatsoever)
4320 return rFormat
.GetLabelFollowedBy() != SvxNumberFormat::LabelFollowedBy::NOTHING
||
4321 !pRule
->MakeNumString(*GetNum()).isEmpty();
4324 // Correction of #newlistlevelattrs#:
4325 // The numbering type has to be checked for bullet lists.
4326 return SVX_NUM_NUMBER_NONE
!= rFormat
.GetNumberingType() ||
4327 !pRule
->MakeNumString(*(GetNum())).isEmpty();
4333 void SwTextNode::SetAttrListRestartValue( SwNumberTree::tSwNumTreeNumber nNumber
)
4335 const bool bChanged( HasAttrListRestartValue()
4336 ? GetAttrListRestartValue() != nNumber
4337 : nNumber
!= USHRT_MAX
);
4339 if ( !bChanged
&& HasAttrListRestartValue() )
4342 if ( nNumber
== USHRT_MAX
)
4344 ResetAttr( RES_PARATR_LIST_RESTARTVALUE
);
4348 SfxInt16Item
aNewListRestartValueItem( RES_PARATR_LIST_RESTARTVALUE
,
4349 static_cast<sal_Int16
>(nNumber
) );
4350 SetAttr( aNewListRestartValueItem
);
4354 bool SwTextNode::HasAttrListRestartValue() const
4356 return GetpSwAttrSet() &&
4357 GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_RESTARTVALUE
, false ) == SfxItemState::SET
;
4359 SwNumberTree::tSwNumTreeNumber
SwTextNode::GetAttrListRestartValue() const
4361 OSL_ENSURE( HasAttrListRestartValue(),
4362 "<SwTextNode::GetAttrListRestartValue()> - only ask for list restart value, if attribute is set at text node." );
4364 const SfxInt16Item
& aListRestartValueItem
=
4365 GetAttr( RES_PARATR_LIST_RESTARTVALUE
);
4366 return static_cast<SwNumberTree::tSwNumTreeNumber
>(aListRestartValueItem
.GetValue());
4369 SwNumberTree::tSwNumTreeNumber
SwTextNode::GetActualListStartValue() const
4371 SwNumberTree::tSwNumTreeNumber nListRestartValue
= 1;
4373 if ( IsListRestart() && HasAttrListRestartValue() )
4375 nListRestartValue
= GetAttrListRestartValue();
4379 SwNumRule
* pRule
= GetNumRule();
4382 const SwNumFormat
* pFormat
=
4383 pRule
->GetNumFormat( o3tl::narrowing
<sal_uInt16
>(GetAttrListLevel()) );
4386 nListRestartValue
= pFormat
->GetStart();
4391 return nListRestartValue
;
4394 bool SwTextNode::IsNotifiable() const
4396 return m_bNotifiable
&& IsNotificationEnabled();
4399 bool SwTextNode::IsNotificationEnabled() const
4401 const SwDoc
& rDoc
= GetDoc();
4402 return !rDoc
.IsInReading() && !rDoc
.IsInDtor();
4405 void SwTextNode::SetCountedInList( bool bCounted
)
4409 // attribute not contained in paragraph style's attribute set. Thus,
4410 // it can be reset to the attribute pool default by resetting the attribute.
4411 ResetAttr( RES_PARATR_LIST_ISCOUNTED
);
4415 SfxBoolItem
aIsCountedInListItem( RES_PARATR_LIST_ISCOUNTED
, false );
4416 SetAttr( aIsCountedInListItem
);
4420 bool SwTextNode::IsCountedInList() const
4422 const SfxBoolItem
& aIsCountedInListItem
= GetAttr( RES_PARATR_LIST_ISCOUNTED
);
4424 return aIsCountedInListItem
.GetValue();
4427 static SwList
* FindList(SwTextNode
*const pNode
)
4429 const OUString sListId
= pNode
->GetListId();
4430 if (!sListId
.isEmpty())
4432 auto & rIDLA(pNode
->GetDoc().getIDocumentListsAccess());
4433 SwList
* pList
= rIDLA
.getListByName( sListId
);
4434 if ( pList
== nullptr )
4436 // Create corresponding list.
4437 SwNumRule
* pNumRule
= pNode
->GetNumRule();
4440 pList
= rIDLA
.createList(sListId
, pNode
->GetNumRule()->GetName());
4443 OSL_ENSURE( pList
!= nullptr,
4444 "<SwTextNode::AddToList()> - no list for given list id. Serious defect" );
4450 void SwTextNode::AddToList()
4454 OSL_FAIL( "<SwTextNode::AddToList()> - the text node is already added to a list. Serious defect" );
4458 SwList
*const pList(FindList(this));
4459 if (!(pList
&& GetNodes().IsDocNodes())) // not for undo nodes
4463 mpNodeNum
.reset(new SwNodeNum(this, false));
4464 pList
->InsertListItem(*mpNodeNum
, SwListRedlineType::SHOW
, GetAttrListLevel(), GetDoc());
4466 // set redline lists
4467 // "default" list: visible items in Show Changes mode (tracked insertions and deletions)
4468 // "hidden" list: visible items in Hide Changes mode (tracked insertions, but not deletions)
4469 // "orig" list: visible items rejecting all changes (no tracked insertions and deletions)
4470 bool bRecordChanges
= GetDoc().GetDocShell() && GetDoc().GetDocShell()->IsChangeRecording();
4471 if (!bRecordChanges
|| GetDoc().IsInXMLImport() || GetDoc().IsInWriterfilterImport() )
4473 const SwRedlineTable
& rRedTable
= GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
4474 SwRedlineTable::size_type nRedlPos
= GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Insert
);
4475 // paragraph start is not in a tracked insertion
4476 if ( SwRedlineTable::npos
== nRedlPos
|| GetIndex() <= rRedTable
[nRedlPos
]->Start()->GetNode().GetIndex() )
4480 // if the paragraph is not deleted, add to the "hidden" list, too
4481 SwRedlineTable::size_type nRedlPosDel
= GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Delete
);
4482 if ( SwRedlineTable::npos
== nRedlPosDel
)
4483 AddToListRLHidden();
4485 // inserted paragraph, e.g. during file load, add to the "hidden" list
4486 else if ( SwRedlineTable::npos
!= nRedlPos
)
4487 AddToListRLHidden();
4489 else if ( bRecordChanges
)
4490 AddToListRLHidden();
4492 // iterate all frames & if there's one with hidden layout...
4493 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> iter(*this);
4494 for (SwTextFrame
* pFrame
= iter
.First(); pFrame
&& !mpNodeNumRLHidden
; pFrame
= iter
.Next())
4496 if (pFrame
->getRootFrame()->IsHideRedlines())
4498 if (pFrame
->GetTextNodeForParaProps() == this)
4500 AddToListRLHidden();
4502 break; // assume it's consistent, need to check only once
4507 void SwTextNode::AddToListRLHidden()
4509 if (mpNodeNumRLHidden
)
4512 SwList
*const pList(FindList(this));
4515 assert(!mpNodeNumRLHidden
);
4516 mpNodeNumRLHidden
.reset(new SwNodeNum(this, true));
4517 pList
->InsertListItem(*mpNodeNumRLHidden
, SwListRedlineType::HIDDEN
, GetAttrListLevel(), GetDoc());
4521 void SwTextNode::AddToListOrig()
4526 SwList
*const pList(FindList(this));
4529 assert(!mpNodeNumOrig
);
4530 mpNodeNumOrig
.reset(new SwNodeNum(this, true));
4531 pList
->InsertListItem(*mpNodeNumOrig
, SwListRedlineType::ORIGTEXT
, GetAttrListLevel(), GetDoc());
4535 void SwTextNode::RemoveFromList()
4537 // sw_redlinehide: ensure it's removed from the other half too!
4538 RemoveFromListRLHidden();
4539 RemoveFromListOrig();
4542 SwList::RemoveListItem(*mpNodeNum
, GetDoc());
4545 SetWordCountDirty( true );
4549 void SwTextNode::RemoveFromListRLHidden()
4551 if (mpNodeNumRLHidden
) // direct access because RemoveFromList doesn't have layout
4553 assert(mpNodeNumRLHidden
->GetParent() || !GetNodes().IsDocNodes());
4554 SwList::RemoveListItem(*mpNodeNumRLHidden
, GetDoc());
4555 mpNodeNumRLHidden
.reset();
4557 SetWordCountDirty( true );
4561 void SwTextNode::RemoveFromListOrig()
4563 if (mpNodeNumOrig
) // direct access because RemoveFromList doesn't have layout
4565 assert(mpNodeNumOrig
->GetParent() || !GetNodes().IsDocNodes());
4566 SwList::RemoveListItem(*mpNodeNumOrig
, GetDoc());
4567 mpNodeNumOrig
.reset();
4569 SetWordCountDirty( true );
4573 bool SwTextNode::IsInList() const
4575 return GetNum() != nullptr && GetNum()->GetParent() != nullptr;
4578 bool SwTextNode::IsFirstOfNumRule(SwRootFrame
const& rLayout
) const
4580 bool bResult
= false;
4582 SwNodeNum
const*const pNum(GetNum(&rLayout
));
4583 if (pNum
&& pNum
->GetNumRule())
4584 bResult
= pNum
->IsFirst();
4589 void SwTextNode::SetListId(OUString
const& rListId
)
4591 const SfxStringItem
& rListIdItem
=
4592 GetAttr( RES_PARATR_LIST_ID
);
4593 if (rListIdItem
.GetValue() != rListId
)
4595 if (rListId
.isEmpty())
4597 ResetAttr( RES_PARATR_LIST_ID
);
4601 SfxStringItem
aNewListIdItem(RES_PARATR_LIST_ID
, rListId
);
4602 SetAttr( aNewListIdItem
);
4607 OUString
SwTextNode::GetListId() const
4609 const SfxStringItem
& rListIdItem
=
4610 GetAttr( RES_PARATR_LIST_ID
);
4611 const OUString
& sListId
{rListIdItem
.GetValue()};
4613 // As long as no explicit list id attribute is set, use the list id of
4614 // the list, which has been created for the applied list style.
4615 if (sListId
.isEmpty())
4617 SwNumRule
* pRule
= GetNumRule();
4620 return pRule
->GetDefaultListId();
4627 /** Determines, if the list level indent attributes can be applied to the
4630 The list level indents can be applied to the paragraph under the one
4631 of following conditions:
4632 - the list style is directly applied to the paragraph and the paragraph
4633 has no own indent attributes.
4634 - the list style is applied to the paragraph through one of its paragraph
4635 styles, the paragraph has no own indent attributes and on the paragraph
4636 style hierarchy from the paragraph to the paragraph style with the
4637 list style no indent attributes are found.
4641 ::sw::ListLevelIndents
SwTextNode::AreListLevelIndentsApplicable() const
4643 ::sw::ListLevelIndents
ret(::sw::ListLevelIndents::No
);
4644 if (AreListLevelIndentsApplicableImpl(RES_MARGIN_FIRSTLINE
))
4646 ret
|= ::sw::ListLevelIndents::FirstLine
;
4648 if (AreListLevelIndentsApplicableImpl(RES_MARGIN_TEXTLEFT
))
4650 ret
|= ::sw::ListLevelIndents::LeftMargin
;
4655 bool SwTextNode::AreListLevelIndentsApplicableImpl(sal_uInt16
const nWhich
) const
4657 bool bAreListLevelIndentsApplicable( true );
4659 if ( !GetNum() || !GetNum()->GetNumRule() )
4661 // no list style applied to paragraph
4662 bAreListLevelIndentsApplicable
= false;
4664 else if ( HasSwAttrSet() &&
4665 GetpSwAttrSet()->GetItemState(nWhich
, false) == SfxItemState::SET
)
4667 // paragraph has hard-set indent attributes
4668 bAreListLevelIndentsApplicable
= false;
4670 else if ( HasSwAttrSet() &&
4671 GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE
, false ) == SfxItemState::SET
)
4673 // list style is directly applied to paragraph and paragraph has no
4674 // hard-set indent attributes
4675 bAreListLevelIndentsApplicable
= true;
4679 // list style is applied through one of the paragraph styles and
4680 // paragraph has no hard-set indent attributes
4682 // check, paragraph's
4683 const SwTextFormatColl
* pColl
= GetTextColl();
4686 if (pColl
->GetAttrSet().GetItemState(nWhich
, false) == SfxItemState::SET
)
4688 // indent attributes found in the paragraph style hierarchy.
4689 bAreListLevelIndentsApplicable
= false;
4693 if ( pColl
->GetAttrSet().GetItemState( RES_PARATR_NUMRULE
, false ) == SfxItemState::SET
)
4695 // paragraph style with the list style found and until now no
4696 // indent attributes are found in the paragraph style hierarchy.
4697 bAreListLevelIndentsApplicable
= true;
4701 pColl
= dynamic_cast<const SwTextFormatColl
*>(pColl
->DerivedFrom());
4703 "<SwTextNode::AreListLevelIndentsApplicable()> - something wrong in paragraph's style hierarchy. The applied list style is not found." );
4707 return bAreListLevelIndentsApplicable
;
4710 /** Retrieves the list tab stop position, if the paragraph's list level defines
4711 one and this list tab stop has to merged into the tap stops of the paragraph
4713 @param nListTabStopPosition
4714 output parameter - containing the list tab stop position
4716 @return boolean - indicating, if a list tab stop position is provided
4718 bool SwTextNode::GetListTabStopPosition( tools::Long
& nListTabStopPosition
) const
4720 bool bListTabStopPositionProvided(false);
4722 const SwNumRule
* pNumRule
= GetNum() ? GetNum()->GetNumRule() : nullptr;
4723 if ( pNumRule
&& HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 )
4725 const SwNumFormat
& rFormat
= pNumRule
->Get( o3tl::narrowing
<sal_uInt16
>(GetActualListLevel()) );
4726 if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT
&&
4727 rFormat
.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB
)
4729 bListTabStopPositionProvided
= true;
4730 nListTabStopPosition
= rFormat
.GetListtabPos();
4732 if ( getIDocumentSettingAccess()->get(DocumentSettingId::TABS_RELATIVE_TO_INDENT
) )
4734 // tab stop position are treated to be relative to the "before text"
4735 // indent value of the paragraph. Thus, adjust <nListTabStopPos>.
4736 if (AreListLevelIndentsApplicable() & ::sw::ListLevelIndents::LeftMargin
)
4738 nListTabStopPosition
-= rFormat
.GetIndentAt();
4740 else if (!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING
))
4742 SvxTextLeftMarginItem
const aItem(GetSwAttrSet().GetTextLeftMargin());
4743 nListTabStopPosition
-= aItem
.GetTextLeft();
4749 return bListTabStopPositionProvided
;
4752 OUString
SwTextNode::GetLabelFollowedBy() const
4754 const SwNumRule
* pNumRule
= GetNum() ? GetNum()->GetNumRule() : nullptr;
4755 if ( pNumRule
&& HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 )
4757 const SwNumFormat
& rFormat
= pNumRule
->Get( o3tl::narrowing
<sal_uInt16
>(GetActualListLevel()) );
4758 if ( rFormat
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT
)
4760 return rFormat
.GetLabelFollowedByAsString();
4767 void SwTextNode::CalcHiddenCharFlags() const
4769 sal_Int32 nStartPos
;
4771 // Update of the flags is done inside GetBoundsOfHiddenRange()
4772 SwScriptInfo::GetBoundsOfHiddenRange( *this, 0, nStartPos
, nEndPos
);
4775 // #i12836# enhanced pdf export
4776 bool SwTextNode::IsHidden() const
4778 if ( IsHiddenByParaField() || HasHiddenCharAttribute( true ) )
4781 const SwSectionNode
* pSectNd
= FindSectionNode();
4782 return pSectNd
&& pSectNd
->GetSection().IsHiddenFlag();
4786 // Helper class for special handling of setting attributes at text node:
4787 // In constructor an instance of the helper class recognize whose attributes
4788 // are set and perform corresponding actions before the intrinsic set of
4789 // attributes has been taken place.
4790 // In the destructor - after the attributes have been set at the text
4791 // node - corresponding actions are performed.
4792 // The following is handled:
4793 // (1) When the list style attribute - RES_PARATR_NUMRULE - is set,
4794 // (A) list style attribute is empty -> the text node is removed from
4796 // (B) list style attribute is not empty
4797 // (a) text node has no list style -> add text node to its list after
4798 // the attributes have been set.
4799 // (b) text node has list style -> change of list style is notified
4800 // after the attributes have been set.
4801 // (2) When the list id attribute - RES_PARATR_LIST_ID - is set and changed,
4802 // the text node is removed from its current list before the attributes
4803 // are set and added to its new list after the attributes have been set.
4804 // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is set
4805 // and changed after the attributes have been set
4806 // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is set
4807 // and changed after the attributes have been set
4808 // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE -
4809 // is set and changed after the attributes have been set
4810 // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is set
4811 // and changed after the attributes have been set
4812 // (7) Set or Reset empty list style due to changed outline level - RES_PARATR_OUTLINELEVEL.
4813 class HandleSetAttrAtTextNode
4816 HandleSetAttrAtTextNode( SwTextNode
& rTextNode
,
4817 const SfxPoolItem
& pItem
);
4818 HandleSetAttrAtTextNode( SwTextNode
& rTextNode
,
4819 const SfxItemSet
& rItemSet
);
4820 ~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
;
4823 SwTextNode
& mrTextNode
;
4824 bool mbAddTextNodeToList
;
4825 bool mbUpdateListLevel
;
4826 bool mbUpdateListRestart
;
4827 bool mbUpdateListCount
;
4829 bool mbOutlineLevelSet
;
4832 HandleSetAttrAtTextNode::HandleSetAttrAtTextNode( SwTextNode
& rTextNode
,
4833 const SfxPoolItem
& pItem
)
4834 : mrTextNode( rTextNode
),
4835 mbAddTextNodeToList( false ),
4836 mbUpdateListLevel( false ),
4837 mbUpdateListRestart( false ),
4838 mbUpdateListCount( false ),
4840 mbOutlineLevelSet( false )
4842 switch ( pItem
.Which() )
4844 // handle RES_PARATR_NUMRULE
4845 case RES_PARATR_NUMRULE
:
4847 mrTextNode
.RemoveFromList();
4849 const SwNumRuleItem
& rNumRuleItem
=
4850 dynamic_cast<const SwNumRuleItem
&>(pItem
);
4851 if ( !rNumRuleItem
.GetValue().isEmpty() )
4853 mbAddTextNodeToList
= true;
4856 mrTextNode
.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4861 // handle RES_PARATR_LIST_ID
4862 case RES_PARATR_LIST_ID
:
4864 const SfxStringItem
& rListIdItem
=
4865 dynamic_cast<const SfxStringItem
&>(pItem
);
4866 OSL_ENSURE( rListIdItem
.GetValue().getLength() > 0,
4867 "<HandleSetAttrAtTextNode(..)> - empty list id attribute not expected. Serious defect." );
4868 const OUString sListIdOfTextNode
= rTextNode
.GetListId();
4869 if ( rListIdItem
.GetValue() != sListIdOfTextNode
)
4871 mbAddTextNodeToList
= true;
4872 if ( mrTextNode
.IsInList() )
4874 mrTextNode
.RemoveFromList();
4880 // handle RES_PARATR_LIST_LEVEL
4881 case RES_PARATR_LIST_LEVEL
:
4883 const SfxInt16Item
& aListLevelItem
=
4884 dynamic_cast<const SfxInt16Item
&>(pItem
);
4885 if ( aListLevelItem
.GetValue() != mrTextNode
.GetAttrListLevel() )
4887 mbUpdateListLevel
= true;
4892 // handle RES_PARATR_LIST_ISRESTART
4893 case RES_PARATR_LIST_ISRESTART
:
4895 const SfxBoolItem
& aListIsRestartItem
=
4896 dynamic_cast<const SfxBoolItem
&>(pItem
);
4897 if ( aListIsRestartItem
.GetValue() !=
4898 mrTextNode
.IsListRestart() )
4900 mbUpdateListRestart
= true;
4905 // handle RES_PARATR_LIST_RESTARTVALUE
4906 case RES_PARATR_LIST_RESTARTVALUE
:
4908 const SfxInt16Item
& aListRestartValueItem
=
4909 dynamic_cast<const SfxInt16Item
&>(pItem
);
4910 if ( !mrTextNode
.HasAttrListRestartValue() ||
4911 aListRestartValueItem
.GetValue() != mrTextNode
.GetAttrListRestartValue() )
4913 mbUpdateListRestart
= true;
4918 // handle RES_PARATR_LIST_ISCOUNTED
4919 case RES_PARATR_LIST_ISCOUNTED
:
4921 const SfxBoolItem
& aIsCountedInListItem
=
4922 dynamic_cast<const SfxBoolItem
&>(pItem
);
4923 if ( aIsCountedInListItem
.GetValue() !=
4924 mrTextNode
.IsCountedInList() )
4926 mbUpdateListCount
= true;
4932 // handle RES_PARATR_OUTLINELEVEL
4933 case RES_PARATR_OUTLINELEVEL
:
4935 const SfxUInt16Item
& aOutlineLevelItem
=
4936 dynamic_cast<const SfxUInt16Item
&>(pItem
);
4937 if ( aOutlineLevelItem
.GetValue() != mrTextNode
.GetAttrOutlineLevel() )
4939 mbOutlineLevelSet
= true;
4947 HandleSetAttrAtTextNode::HandleSetAttrAtTextNode( SwTextNode
& rTextNode
,
4948 const SfxItemSet
& rItemSet
)
4949 : mrTextNode( rTextNode
),
4950 mbAddTextNodeToList( false ),
4951 mbUpdateListLevel( false ),
4952 mbUpdateListRestart( false ),
4953 mbUpdateListCount( false ),
4955 mbOutlineLevelSet( false )
4957 // handle RES_PARATR_NUMRULE
4958 if ( const SwNumRuleItem
* pNumRuleItem
= rItemSet
.GetItemIfSet( RES_PARATR_NUMRULE
, false ) )
4960 mrTextNode
.RemoveFromList();
4962 if ( !pNumRuleItem
->GetValue().isEmpty() )
4964 mbAddTextNodeToList
= true;
4966 mrTextNode
.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4970 // handle RES_PARATR_LIST_ID
4971 if ( const SfxStringItem
* pListIdItem
= rItemSet
.GetItemIfSet( RES_PARATR_LIST_ID
, false ) )
4973 const OUString sListIdOfTextNode
= mrTextNode
.GetListId();
4974 if ( pListIdItem
->GetValue() != sListIdOfTextNode
)
4976 mbAddTextNodeToList
= true;
4977 if ( mrTextNode
.IsInList() )
4979 mrTextNode
.RemoveFromList();
4984 // handle RES_PARATR_LIST_LEVEL
4985 if ( const SfxInt16Item
* pListLevelItem
= rItemSet
.GetItemIfSet( RES_PARATR_LIST_LEVEL
, false ) )
4987 if (pListLevelItem
->GetValue() != mrTextNode
.GetAttrListLevel())
4989 mbUpdateListLevel
= true;
4993 // handle RES_PARATR_LIST_ISRESTART
4994 if ( const SfxBoolItem
* pListIsRestartItem
= rItemSet
.GetItemIfSet( RES_PARATR_LIST_ISRESTART
, false ) )
4996 if (pListIsRestartItem
->GetValue() != mrTextNode
.IsListRestart())
4998 mbUpdateListRestart
= true;
5002 // handle RES_PARATR_LIST_RESTARTVALUE
5003 if ( const SfxInt16Item
* pListRestartValueItem
= rItemSet
.GetItemIfSet( RES_PARATR_LIST_RESTARTVALUE
, false ) )
5005 if ( !mrTextNode
.HasAttrListRestartValue() ||
5006 pListRestartValueItem
->GetValue() != mrTextNode
.GetAttrListRestartValue() )
5008 mbUpdateListRestart
= true;
5012 // handle RES_PARATR_LIST_ISCOUNTED
5013 if ( const SfxBoolItem
* pIsCountedInListItem
= rItemSet
.GetItemIfSet( RES_PARATR_LIST_ISCOUNTED
, false ) )
5015 if (pIsCountedInListItem
->GetValue() != mrTextNode
.IsCountedInList())
5017 mbUpdateListCount
= true;
5022 // handle RES_PARATR_OUTLINELEVEL
5023 if ( const SfxUInt16Item
* pOutlineLevelItem
= rItemSet
.GetItemIfSet( RES_PARATR_OUTLINELEVEL
, false ) )
5025 if (pOutlineLevelItem
->GetValue() != mrTextNode
.GetAttrOutlineLevel())
5027 mbOutlineLevelSet
= true;
5032 HandleSetAttrAtTextNode::~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
5034 if ( mbAddTextNodeToList
)
5036 SwNumRule
* pNumRuleAtTextNode
= mrTextNode
.GetNumRule();
5037 if ( pNumRuleAtTextNode
)
5039 mrTextNode
.AddToList();
5044 if ( mbUpdateListLevel
&& mrTextNode
.IsInList() )
5046 auto const nLevel(mrTextNode
.GetAttrListLevel());
5047 const SwDoc
& rDoc(mrTextNode
.GetDoc());
5049 [nLevel
, &rDoc
](SwNodeNum
& rNum
) { rNum
.SetLevelInListTree(nLevel
, rDoc
); });
5052 if ( mbUpdateListRestart
&& mrTextNode
.IsInList() )
5054 const SwDoc
& rDoc(mrTextNode
.GetDoc());
5056 [&rDoc
](SwNodeNum
& rNum
) {
5057 rNum
.InvalidateMe();
5058 rNum
.NotifyInvalidSiblings(rDoc
);
5062 if (mbUpdateListCount
&& mrTextNode
.IsInList() && HasNumberingWhichNeedsLayoutUpdate(mrTextNode
))
5064 // Repaint all text frames that belong to this numbering to avoid outdated generated
5066 const SwDoc
& rDoc(mrTextNode
.GetDoc());
5068 [&rDoc
](SwNodeNum
& rNum
) { rNum
.InvalidateAndNotifyTree(rDoc
); });
5073 if (!mbOutlineLevelSet
)
5076 mrTextNode
.GetNodes().UpdateOutlineNode(mrTextNode
);
5077 if (mrTextNode
.GetAttrOutlineLevel() == 0)
5079 mrTextNode
.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5083 if ( mrTextNode
.GetSwAttrSet().GetItemState( RES_PARATR_NUMRULE
)
5084 != SfxItemState::SET
)
5086 mrTextNode
.SetEmptyListStyleDueToSetOutlineLevelAttr();
5090 // End of class <HandleSetAttrAtTextNode>
5093 bool SwTextNode::SetAttr( const SfxPoolItem
& pItem
)
5095 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr
);
5096 mbInSetOrResetAttr
= true;
5098 HandleSetAttrAtTextNode
aHandleSetAttr( *this, pItem
);
5100 bool bRet
= SwContentNode::SetAttr( pItem
);
5102 mbInSetOrResetAttr
= bOldIsSetOrResetAttr
;
5107 bool SwTextNode::SetAttr( const SfxItemSet
& rSet
)
5109 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr
);
5110 mbInSetOrResetAttr
= true;
5112 HandleSetAttrAtTextNode
aHandleSetAttr( *this, rSet
);
5114 bool bRet
= SwContentNode::SetAttr( rSet
);
5116 mbInSetOrResetAttr
= bOldIsSetOrResetAttr
;
5121 void SwTextNode::SetInSwUndo(bool bInUndo
)
5123 m_bInUndo
= bInUndo
;
5127 // Helper class for special handling of resetting attributes at text node:
5128 // In constructor an instance of the helper class recognize whose attributes
5129 // are reset and perform corresponding actions before the intrinsic reset of
5130 // attributes has been taken place.
5131 // In the destructor - after the attributes have been reset at the text
5132 // node - corresponding actions are performed.
5133 // The following is handled:
5134 // (1) When the list style attribute - RES_PARATR_NUMRULE - is reset,
5135 // the text is removed from its list before the attributes have been reset.
5136 // (2) When the list id attribute - RES_PARATR_LIST_ID - is reset,
5137 // the text is removed from its list before the attributes have been reset.
5138 // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is reset.
5139 // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is reset.
5140 // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE - is reset.
5141 // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is reset.
5142 // (7) Reset empty list style, if outline level attribute - RES_PARATR_OUTLINELEVEL - is reset.
5143 class HandleResetAttrAtTextNode
5146 HandleResetAttrAtTextNode( SwTextNode
& rTextNode
,
5147 const sal_uInt16 nWhich1
,
5148 sal_uInt16 nWhich2
);
5149 HandleResetAttrAtTextNode( SwTextNode
& rTextNode
,
5150 const std::vector
<sal_uInt16
>& rWhichArr
);
5151 explicit HandleResetAttrAtTextNode( SwTextNode
& rTextNode
);
5153 ~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
;
5156 SwTextNode
& mrTextNode
;
5157 bool mbListStyleOrIdReset
;
5158 bool mbUpdateListLevel
;
5159 bool mbUpdateListRestart
;
5160 bool mbUpdateListCount
;
5162 void init( sal_uInt16 nWhich
, bool& rbRemoveFromList
);
5165 HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode
& rTextNode
,
5166 const sal_uInt16 nWhich1
,
5167 sal_uInt16 nWhich2
)
5168 : mrTextNode( rTextNode
),
5169 mbListStyleOrIdReset( false ),
5170 mbUpdateListLevel( false ),
5171 mbUpdateListRestart( false ),
5172 mbUpdateListCount( false )
5174 if ( nWhich2
< nWhich1
)
5176 bool bRemoveFromList( false );
5177 for ( sal_uInt16 nWhich
= nWhich1
; nWhich
<= nWhich2
; ++nWhich
)
5178 init( nWhich
, bRemoveFromList
);
5179 if ( bRemoveFromList
&& mrTextNode
.IsInList() )
5180 mrTextNode
.RemoveFromList();
5183 HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode
& rTextNode
,
5184 const std::vector
<sal_uInt16
>& rWhichArr
)
5185 : mrTextNode( rTextNode
),
5186 mbListStyleOrIdReset( false ),
5187 mbUpdateListLevel( false ),
5188 mbUpdateListRestart( false ),
5189 mbUpdateListCount( false )
5191 bool bRemoveFromList( false );
5192 for ( sal_uInt16 nWhich
: rWhichArr
)
5193 init( nWhich
, bRemoveFromList
);
5194 if ( bRemoveFromList
&& mrTextNode
.IsInList() )
5195 mrTextNode
.RemoveFromList();
5198 HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode
& rTextNode
)
5199 : mrTextNode( rTextNode
),
5200 mbListStyleOrIdReset( true ),
5201 mbUpdateListLevel( false ),
5202 mbUpdateListRestart( false ),
5203 mbUpdateListCount( false )
5205 if ( rTextNode
.IsInList() )
5207 rTextNode
.RemoveFromList();
5210 mrTextNode
.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5213 void HandleResetAttrAtTextNode::init( sal_uInt16 rWhich
, bool& rbRemoveFromList
)
5215 if ( rWhich
== RES_PARATR_NUMRULE
)
5217 rbRemoveFromList
= rbRemoveFromList
||
5218 mrTextNode
.GetNumRule() != nullptr;
5219 mbListStyleOrIdReset
= true;
5221 else if ( rWhich
== RES_PARATR_LIST_ID
)
5223 rbRemoveFromList
= rbRemoveFromList
||
5224 ( mrTextNode
.GetpSwAttrSet() &&
5225 mrTextNode
.GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_ID
, false ) == SfxItemState::SET
);
5226 mbListStyleOrIdReset
= true;
5228 else if ( rWhich
== RES_PARATR_OUTLINELEVEL
)
5229 mrTextNode
.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5230 else if ( rWhich
== RES_BACKGROUND
)
5231 mrTextNode
.ResetAttr( XATTR_FILL_FIRST
, XATTR_FILL_LAST
);
5233 if ( !rbRemoveFromList
)
5235 // RES_PARATR_LIST_LEVEL
5236 mbUpdateListLevel
= mbUpdateListLevel
||
5237 ( rWhich
== RES_PARATR_LIST_LEVEL
&&
5238 mrTextNode
.HasAttrListLevel() );
5240 // RES_PARATR_LIST_ISRESTART and RES_PARATR_LIST_RESTARTVALUE
5241 mbUpdateListRestart
= mbUpdateListRestart
||
5242 ( rWhich
== RES_PARATR_LIST_ISRESTART
&&
5243 mrTextNode
.IsListRestart() ) ||
5244 ( rWhich
== RES_PARATR_LIST_RESTARTVALUE
&&
5245 mrTextNode
.HasAttrListRestartValue() );
5247 // RES_PARATR_LIST_ISCOUNTED
5248 mbUpdateListCount
= mbUpdateListCount
||
5249 ( rWhich
== RES_PARATR_LIST_ISCOUNTED
&&
5250 !mrTextNode
.IsCountedInList() );
5254 HandleResetAttrAtTextNode::~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
5256 if ( mbListStyleOrIdReset
&& !mrTextNode
.IsInList() )
5258 // check, if in spite of the reset of the list style or the list id
5259 // the paragraph still has to be added to a list.
5260 if (mrTextNode
.GetNumRule() && !mrTextNode
.GetListId().isEmpty())
5263 // If paragraph has no list level attribute set and list style
5264 // is the outline style, apply outline level as the list level.
5265 if ( !mrTextNode
.HasAttrListLevel() &&
5266 mrTextNode
.GetNumRule()->GetName()==SwNumRule::GetOutlineRuleName() &&
5267 mrTextNode
.GetTextColl()->IsAssignedToListLevelOfOutlineStyle() )
5269 int nNewListLevel
= mrTextNode
.GetTextColl()->GetAssignedOutlineStyleLevel();
5270 if ( 0 <= nNewListLevel
&& nNewListLevel
< MAXLEVEL
)
5272 mrTextNode
.SetAttrListLevel( nNewListLevel
);
5275 mrTextNode
.AddToList();
5281 if (mrTextNode
.GetpSwAttrSet()
5282 && mrTextNode
.GetAttr(RES_PARATR_OUTLINELEVEL
, false).GetValue() > 0)
5284 mrTextNode
.SetEmptyListStyleDueToSetOutlineLevelAttr();
5289 if ( !mrTextNode
.IsInList() )
5292 if ( mbUpdateListLevel
)
5294 auto const nLevel(mrTextNode
.GetAttrListLevel());
5295 const SwDoc
& rDoc(mrTextNode
.GetDoc());
5297 [nLevel
, &rDoc
](SwNodeNum
& rNum
) { rNum
.SetLevelInListTree(nLevel
, rDoc
); });
5300 if ( mbUpdateListRestart
)
5302 const SwDoc
& rDoc(mrTextNode
.GetDoc());
5304 [&rDoc
](SwNodeNum
& rNum
) {
5305 rNum
.InvalidateMe();
5306 rNum
.NotifyInvalidSiblings(rDoc
);
5310 if ( mbUpdateListCount
)
5312 const SwDoc
& rDoc(mrTextNode
.GetDoc());
5314 [&rDoc
](SwNodeNum
& rNum
) { rNum
.InvalidateAndNotifyTree(rDoc
); });
5317 // End of class <HandleResetAttrAtTextNode>
5320 bool SwTextNode::ResetAttr( sal_uInt16 nWhich1
, sal_uInt16 nWhich2
)
5322 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr
);
5323 mbInSetOrResetAttr
= true;
5325 HandleResetAttrAtTextNode
aHandleResetAttr( *this, nWhich1
, nWhich2
);
5327 bool bRet
= SwContentNode::ResetAttr( nWhich1
, nWhich2
);
5329 mbInSetOrResetAttr
= bOldIsSetOrResetAttr
;
5334 bool SwTextNode::ResetAttr( const std::vector
<sal_uInt16
>& rWhichArr
)
5336 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr
);
5337 mbInSetOrResetAttr
= true;
5339 HandleResetAttrAtTextNode
aHandleResetAttr( *this, rWhichArr
);
5341 bool bRet
= SwContentNode::ResetAttr( rWhichArr
);
5343 mbInSetOrResetAttr
= bOldIsSetOrResetAttr
;
5348 sal_uInt16
SwTextNode::ResetAllAttr()
5350 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr
);
5351 mbInSetOrResetAttr
= true;
5353 HandleResetAttrAtTextNode
aHandleResetAttr( *this );
5355 const sal_uInt16 nRet
= SwContentNode::ResetAllAttr();
5357 mbInSetOrResetAttr
= bOldIsSetOrResetAttr
;
5362 void SwTextNode::dumpAsXml(xmlTextWriterPtr pWriter
) const
5364 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwTextNode"));
5365 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
5366 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
5368 OUString sText
= GetText();
5369 for (int i
= 0; i
< 32; ++i
)
5370 sText
= sText
.replace(i
, '*');
5371 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("m_Text"));
5372 (void)xmlTextWriterWriteString(pWriter
, BAD_CAST(sText
.toUtf8().getStr()));
5373 (void)xmlTextWriterEndElement(pWriter
);
5375 if (GetFormatColl())
5377 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwTextFormatColl"));
5378 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("name"), BAD_CAST(GetFormatColl()->GetName().toUtf8().getStr()));
5379 (void)xmlTextWriterEndElement(pWriter
);
5384 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwAttrSet"));
5385 GetSwAttrSet().dumpAsXml(pWriter
);
5386 (void)xmlTextWriterEndElement(pWriter
);
5391 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwpHints"));
5392 const SwpHints
& rHints
= GetSwpHints();
5393 for (size_t i
= 0; i
< rHints
.Count(); ++i
)
5394 rHints
.Get(i
)->dumpAsXml(pWriter
);
5395 (void)xmlTextWriterEndElement(pWriter
);
5398 (void)xmlTextWriterEndElement(pWriter
);
5401 sal_uInt32
SwTextNode::GetRsid( sal_Int32 nStt
, sal_Int32 nEnd
) const
5403 SfxItemSetFixed
<RES_CHRATR_RSID
, RES_CHRATR_RSID
> aSet( const_cast<SwAttrPool
&>((GetDoc().GetAttrPool())) );
5404 if (GetParaAttr(aSet
, nStt
, nEnd
))
5406 const SvxRsidItem
* pRsid
= aSet
.GetItem
<SvxRsidItem
>(RES_CHRATR_RSID
);
5408 return pRsid
->GetValue();
5414 sal_uInt32
SwTextNode::GetParRsid() const
5416 return reinterpret_cast<const SvxRsidItem
&>(GetAttr( RES_PARATR_RSID
)).GetValue();
5419 bool SwTextNode::CompareParRsid( const SwTextNode
&rTextNode
) const
5421 sal_uInt32 nThisRsid
= GetParRsid();
5422 sal_uInt32 nRsid
= rTextNode
.GetParRsid();
5424 return nThisRsid
== nRsid
;
5427 bool SwTextNode::CompareRsid( const SwTextNode
&rTextNode
, sal_Int32 nStt1
, sal_Int32 nStt2
) const
5429 sal_uInt32 nThisRsid
= GetRsid( nStt1
, nStt1
);
5430 sal_uInt32 nRsid
= rTextNode
.GetRsid( nStt2
, nStt2
);
5432 return nThisRsid
== nRsid
;
5436 ::sfx2::IXmlIdRegistry
& SwTextNode::GetRegistry()
5438 return GetDoc().GetXmlIdRegistry();
5441 bool SwTextNode::IsInClipboard() const
5443 return GetDoc().IsClipBoard();
5446 bool SwTextNode::IsInUndo() const
5448 return GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
5451 bool SwTextNode::IsInContent() const
5453 return !GetDoc().IsInHeaderFooter( *this );
5456 void SwTextNode::HandleNonLegacyHint(const SfxHint
& rHint
)
5458 assert(!dynamic_cast<const sw::LegacyModifyHint
*>(&rHint
));
5459 sw::TextNodeNotificationSuppressor(*this);
5460 CallSwClientNotify(rHint
);
5462 SwDoc
& rDoc
= GetDoc();
5463 // #125329# - assure that text node is in document nodes array
5464 if ( !rDoc
.IsInDtor() && &rDoc
.GetNodes() == &GetNodes() )
5466 rDoc
.GetNodes().UpdateOutlineNode(*this);
5470 void SwTextNode::UpdateDocPos(const SwTwips nDocPos
, const sal_uInt32 nIndex
)
5472 const sw::DocPosUpdateAtIndex
aHint(nDocPos
, *this, nIndex
);
5473 CallSwClientNotify(aHint
);
5476 void SwTextNode::TriggerNodeUpdate(const sw::LegacyModifyHint
& rHint
)
5478 const auto pOldValue
= rHint
.m_pOld
;
5479 const auto pNewValue
= rHint
.m_pNew
;
5481 sw::TextNodeNotificationSuppressor(*this);
5483 // Override Modify so that deleting styles works properly (outline
5485 // Never call ChgTextCollUpdateNum for Nodes in Undo.
5488 && RES_FMT_CHG
== pOldValue
->Which()
5489 && GetRegisteredIn() == static_cast<const SwFormatChg
*>(pNewValue
)->pChangedFormat
5490 && GetNodes().IsDocNodes() )
5492 assert(dynamic_cast<SwTextFormatColl
const*>(static_cast<const SwFormatChg
*>(pOldValue
)->pChangedFormat
));
5493 assert(dynamic_cast<SwTextFormatColl
const*>(static_cast<const SwFormatChg
*>(pNewValue
)->pChangedFormat
));
5494 ChgTextCollUpdateNum(
5495 static_cast<const SwTextFormatColl
*>(static_cast<const SwFormatChg
*>(pOldValue
)->pChangedFormat
),
5496 static_cast<const SwTextFormatColl
*>(static_cast<const SwFormatChg
*>(pNewValue
)->pChangedFormat
) );
5499 // reset fill information
5500 if (maFillAttributes
&& pNewValue
)
5502 const sal_uInt16 nWhich
= pNewValue
->Which();
5503 bool bReset(RES_FMT_CHG
== nWhich
); // ..on format change (e.g. style changed)
5505 if(!bReset
&& RES_ATTRSET_CHG
== nWhich
) // ..on ItemChange from DrawingLayer FillAttributes
5507 SfxItemIter
aIter(*static_cast<const SwAttrSetChg
*>(pNewValue
)->GetChgSet());
5509 for(const SfxPoolItem
* pItem
= aIter
.GetCurItem(); pItem
&& !bReset
; pItem
= aIter
.NextItem())
5511 bReset
= !IsInvalidItem(pItem
) && pItem
->Which() >= XATTR_FILL_FIRST
&& pItem
->Which() <= XATTR_FILL_LAST
;
5517 maFillAttributes
.reset();
5521 if ( !mbInSetOrResetAttr
)
5523 HandleModifyAtTextNode( *this, pOldValue
, pNewValue
);
5526 SwContentNode::SwClientNotify(*this, rHint
);
5528 SwDoc
& rDoc
= GetDoc();
5529 // #125329# - assure that text node is in document nodes array
5530 if ( !rDoc
.IsInDtor() && &rDoc
.GetNodes() == &GetNodes() )
5532 rDoc
.GetNodes().UpdateOutlineNode(*this);
5536 if (pOldValue
&& (RES_REMOVE_UNO_OBJECT
== pOldValue
->Which()))
5537 { // invalidate cached uno object
5538 SetXParagraph(nullptr);
5542 void SwTextNode::SwClientNotify( const SwModify
& rModify
, const SfxHint
& rHint
)
5544 if (rHint
.GetId() == SfxHintId::SwLegacyModify
)
5546 auto pLegacyHint
= static_cast<const sw::LegacyModifyHint
*>(&rHint
);
5547 TriggerNodeUpdate(*pLegacyHint
);
5549 else if (dynamic_cast<const SwAttrHint
*>(&rHint
))
5551 if (&rModify
== GetRegisteredIn())
5556 uno::Reference
< rdf::XMetadatable
>
5557 SwTextNode::MakeUnoObject()
5559 const uno::Reference
<rdf::XMetadatable
> xMeta(
5560 SwXParagraph::CreateXParagraph(GetDoc(), this));
5564 drawinglayer::attribute::SdrAllFillAttributesHelperPtr
SwTextNode::getSdrAllFillAttributesHelper() const
5566 // create SdrAllFillAttributesHelper on demand
5567 if(!maFillAttributes
)
5569 const_cast< SwTextNode
* >(this)->maFillAttributes
= std::make_shared
<drawinglayer::attribute::SdrAllFillAttributesHelper
>(GetSwAttrSet());
5572 return maFillAttributes
;
5575 void SwTextNode::SetXParagraph(rtl::Reference
<SwXParagraph
> const & xParagraph
)
5577 m_wXParagraph
= xParagraph
.get();
5580 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */