1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <hintids.hxx>
21 #include <hints.hxx>
22
23 #include <comphelper/configuration.hxx>
24 #include <comphelper/lok.hxx>
25 #include <comphelper/string.hxx>
26 #include <editeng/fontitem.hxx>
27 #include <editeng/escapementitem.hxx>
28 #include <editeng/lrspitem.hxx>
29 #include <editeng/rsiditem.hxx>
30 #include <sal/log.hxx>
31 #include <osl/diagnose.h>
32 #include <anchoredobject.hxx>
33 #include <txtfld.hxx>
34 #include <txtinet.hxx>
35 #include <fmtanchr.hxx>
36 #include <fmtinfmt.hxx>
37 #include <fmtrfmrk.hxx>
38 #include <txttxmrk.hxx>
39 #include <fchrfmt.hxx>
40 #include <txtftn.hxx>
41 #include <fmtflcnt.hxx>
42 #include <fmtfld.hxx>
43 #include <frmatr.hxx>
44 #include <ftnidx.hxx>
45 #include <ftninfo.hxx>
46 #include <fmtftn.hxx>
47 #include <charfmt.hxx>
48 #include <ndtxt.hxx>
49 #include <doc.hxx>
50 #include <IDocumentUndoRedo.hxx>
51 #include <IDocumentSettingAccess.hxx>
52 #include <IDocumentListsAccess.hxx>
53 #include <IDocumentRedlineAccess.hxx>
54 #include <IDocumentLayoutAccess.hxx>
55 #include <docary.hxx>
56 #include <docufld.hxx>
57 #include <pam.hxx>
58 #include <fldbas.hxx>
59 #include <paratr.hxx>
60 #include <txtfrm.hxx>
61 #include <ftnfrm.hxx>
62 #include <pagefrm.hxx>
63 #include <rootfrm.hxx>
64 #include <expfld.hxx>
65 #include <section.hxx>
66 #include <mvsave.hxx>
67 #include <SwGrammarMarkUp.hxx>
68 #include <redline.hxx>
69 #include <IMark.hxx>
70 #include <scriptinfo.hxx>
71 #include <istyleaccess.hxx>
72 #include <SwStyleNameMapper.hxx>
73 #include <numrule.hxx>
74 #include <docsh.hxx>
75 #include <SwNodeNum.hxx>
76 #include <svl/grabbagitem.hxx>
77 #include <svl/intitem.hxx>
78 #include <sortedobjs.hxx>
79 #include <calbck.hxx>
80 #include <attrhint.hxx>
81 #include <memory>
82 #include <unoparagraph.hxx>
83 #include <unotext.hxx>
84 #include <wrtsh.hxx>
85 #include <fmtpdsc.hxx>
86 #include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
87 #include <svl/itemiter.hxx>
88 #include <undobj.hxx>
89 #include <formatflysplit.hxx>
90 #include <fmtcntnt.hxx>
91 #include <poolfmt.hxx>
92 #include <names.hxx>
93
94 using namespace ::com::sun::star;
95
96 typedef std::vector<SwTextAttr*> SwpHts;
97
98 namespace sw {
99 class TextNodeNotificationSuppressor {
100 SwTextNode& m_rNode;
101 bool m_bWasNotifiable;
102 public:
TextNodeNotificationSuppressor(SwTextNode & rNode)103 TextNodeNotificationSuppressor(SwTextNode& rNode)
104 : m_rNode(rNode)
105 , m_bWasNotifiable(rNode.m_bNotifiable)
106 {
107 m_rNode.m_bNotifiable = false;
108 }
~TextNodeNotificationSuppressor()109 ~TextNodeNotificationSuppressor()
110 {
111 m_rNode.m_bNotifiable = m_bWasNotifiable;
112 }
113 };
114 }
115
116 // unfortunately everyone can change Hints without ensuring order or the linking between them
117 #ifdef DBG_UTIL
118 #define CHECK_SWPHINTS(pNd) { if( pNd->GetpSwpHints() && \
119 !pNd->GetDoc().IsInReading() ) \
120 pNd->GetpSwpHints()->Check(true); }
121 #define CHECK_SWPHINTS_IF_FRM(pNd) { if( pNd->GetpSwpHints() && \
122 !pNd->GetDoc().IsInReading() ) \
123 pNd->GetpSwpHints()->Check(getLayoutFrame(nullptr, nullptr, nullptr) != nullptr); }
124 #else
125 #define CHECK_SWPHINTS(pNd)
126 #define CHECK_SWPHINTS_IF_FRM(pNd)
127 #endif
128
MakeTextNode(const SwNode & rWhere,SwTextFormatColl * pColl,bool const bNewFrames)129 SwTextNode *SwNodes::MakeTextNode( const SwNode& rWhere,
130 SwTextFormatColl *pColl, bool const bNewFrames)
131 {
132 OSL_ENSURE( pColl, "Collection pointer is 0." );
133
134 SwTextNode *pNode = new SwTextNode( rWhere, pColl, nullptr );
135
136 SwNodeIndex aIdx( *pNode );
137
138 // if there is no layout or it is in a hidden section, MakeFrames is not needed
139 const SwSectionNode* pSectNd;
140 if (!bNewFrames ||
141 !GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() ||
142 ( nullptr != (pSectNd = pNode->FindSectionNode()) &&
143 pSectNd->GetSection().IsHiddenFlag() ))
144 return pNode;
145
146 SwNodeIndex aTmp( rWhere );
147 do {
148 // max. 2 loops:
149 // 1. take the successor
150 // 2. take the predecessor
151
152 SwNode * pNd = & aTmp.GetNode();
153 switch (pNd->GetNodeType())
154 {
155 case SwNodeType::Table:
156 static_cast<SwTableNode*>(pNd)->MakeFramesForAdjacentContentNode(aIdx);
157 return pNode;
158
159 case SwNodeType::Section:
160 if( static_cast<SwSectionNode*>(pNd)->GetSection().IsHidden() ||
161 static_cast<SwSectionNode*>(pNd)->IsContentHidden() )
162 {
163 pNd = FindPrvNxtFrameNode( *pNode, pNode );
164 if( !pNd )
165 return pNode;
166 aTmp = *pNd;
167 break;
168 }
169 static_cast<SwSectionNode*>(pNd)->MakeFramesForAdjacentContentNode(aIdx);
170 return pNode;
171
172 case SwNodeType::Text:
173 case SwNodeType::Grf:
174 case SwNodeType::Ole:
175 static_cast<SwContentNode*>(pNd)->MakeFramesForAdjacentContentNode(*pNode);
176 return pNode;
177
178 case SwNodeType::End:
179 if( pNd->StartOfSectionNode()->IsSectionNode() &&
180 aTmp.GetIndex() < rWhere.GetIndex() )
181 {
182 if( pNd->StartOfSectionNode()->GetSectionNode()->GetSection().IsHiddenFlag())
183 {
184 if( !GoPrevSection( &aTmp, true, false ) ||
185 aTmp.GetNode().FindTableNode() !=
186 pNode->FindTableNode() )
187 return pNode;
188 }
189 else
190 aTmp = *pNd->StartOfSectionNode();
191 break;
192 }
193 else if( pNd->StartOfSectionNode()->IsTableNode() &&
194 aTmp.GetIndex() < rWhere.GetIndex() )
195 {
196 // after a table node
197 aTmp = *pNd->StartOfSectionNode();
198 break;
199 }
200 [[fallthrough]];
201 default:
202 if( &rWhere == &aTmp.GetNode() )
203 aTmp -= SwNodeOffset(2);
204 else
205 return pNode;
206 break;
207 }
208 } while( true );
209 }
210
SwTextNode(const SwNode & rWhere,SwTextFormatColl * pTextColl,const SfxItemSet * pAutoAttr)211 SwTextNode::SwTextNode( const SwNode& rWhere, SwTextFormatColl *pTextColl, const SfxItemSet* pAutoAttr )
212 : SwContentNode( rWhere, SwNodeType::Text, pTextColl ),
213 m_bContainsHiddenChars(false),
214 m_bHiddenCharsHidePara(false),
215 m_bRecalcHiddenCharFlags(false),
216 m_bLastOutlineState( false ),
217 m_bNotifiable( true ),
218 mbEmptyListStyleSetDueToSetOutlineLevelAttr( false ),
219 mbInSetOrResetAttr( false ),
220 m_bInUndo(false)
221 {
222 {
223 sw::TextNodeNotificationSuppressor(*this);
224
225 if( pAutoAttr )
226 SetAttr( *pAutoAttr );
227
228 if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
229 {
230 // #i101516#
231 // apply paragraph style's assigned outline style list level as
232 // list level of the paragraph, if it has none set already.
233 if ( !HasAttrListLevel() &&
234 pTextColl && pTextColl->IsAssignedToListLevelOfOutlineStyle() )
235 {
236 SetAttrListLevel( pTextColl->GetAssignedOutlineStyleLevel() );
237 }
238 AddToList();
239 }
240
241 // call method <UpdateOutlineNode(..)> only for the document nodes array
242 if (GetNodes().IsDocNodes())
243 GetNodes().UpdateOutlineNode(*this);
244 }
245
246 m_bContainsHiddenChars = m_bHiddenCharsHidePara = false;
247 m_bRecalcHiddenCharFlags = true;
248 }
249
~SwTextNode()250 SwTextNode::~SwTextNode()
251 {
252 // delete only removes the pointer not the array elements!
253 if ( m_pSwpHints )
254 {
255 // do not delete attributes twice when those delete their content
256 std::unique_ptr<SwpHints> pTmpHints(std::move(m_pSwpHints));
257
258 for( size_t j = pTmpHints->Count(); j; )
259 {
260 // first remove the attribute from the array otherwise
261 // if would delete itself
262 DestroyAttr( pTmpHints->Get( --j ) );
263 }
264 }
265
266 // must be removed from outline nodes by now
267 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
268 SwOutlineNodes::size_type foo;
269 assert(!GetNodes().GetOutLineNds().Seek_Entry(this, &foo));
270 #endif
271
272 RemoveFromList();
273
274 DelFrames(nullptr); // must be called here while it's still a SwTextNode
275 DelFrames_TextNodePart();
276
277 // If this Node should have Outline Numbering but that state hasn't been
278 // crystallized by SwNodes::UpdateOutlineNode yet, and so it currently isn't
279 // added to SwNodes::m_aOutlineNodes, then set LastOutlineState so it won't
280 // be added if ResetAttr() triggers UpdateOutlineNode() during destruction,
281 // and avoid leaving a dangling pointer in m_aOutlineNodes.
282 if (IsOutline() && !m_bLastOutlineState)
283 m_bLastOutlineState = true;
284
285 #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
286 if (!GetDoc().IsInDtor())
287 #endif
288 {
289 ResetAttr(RES_PAGEDESC);
290 }
291 InvalidateInSwCache();
292 }
293
FileLoadedInitHints()294 void SwTextNode::FileLoadedInitHints()
295 {
296 if (m_pSwpHints)
297 {
298 m_pSwpHints->MergePortions(*this);
299 }
300 }
301
MakeFrame(SwFrame * pSib)302 SwContentFrame *SwTextNode::MakeFrame( SwFrame* pSib )
303 {
304 SwContentFrame *pFrame = sw::MakeTextFrame(*this, pSib, sw::FrameMode::New);
305 return pFrame;
306 }
307
Len() const308 sal_Int32 SwTextNode::Len() const
309 {
310 return m_Text.getLength();
311 }
312
313 // After a split node, it's necessary to actualize the ref-pointer of the ftnfrms.
lcl_ChangeFootnoteRef(SwTextNode & rNode)314 static void lcl_ChangeFootnoteRef( SwTextNode &rNode )
315 {
316 SwpHints *pSwpHints = rNode.GetpSwpHints();
317 if( !(pSwpHints && rNode.GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()) )
318 return;
319
320 SwContentFrame* pFrame = nullptr;
321 // OD 07.11.2002 #104840# - local variable to remember first footnote
322 // of node <rNode> in order to invalidate position of its first content.
323 // Thus, in its <MakeAll()> it will checked its position relative to its reference.
324 SwFootnoteFrame* pFirstFootnoteOfNode = nullptr;
325 for( size_t j = pSwpHints->Count(); j; )
326 {
327 SwTextAttr* pHt = pSwpHints->Get(--j);
328 if (RES_TXTATR_FTN == pHt->Which())
329 {
330 if( !pFrame )
331 {
332 pFrame = SwIterator<SwContentFrame, SwTextNode, sw::IteratorMode::UnwrapMulti>(rNode).First();
333 if (!pFrame)
334 return;
335 }
336 SwTextFootnote *pAttr = static_cast<SwTextFootnote*>(pHt);
337 OSL_ENSURE( pAttr->GetStartNode(), "FootnoteAtr without StartNode." );
338 SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
339 SwContentNode *pNd = aIdx.GetNode().GetContentNode();
340 if ( !pNd )
341 pNd = SwNodes::GoNextSection(&aIdx, true, false);
342 if ( !pNd )
343 continue;
344
345 SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
346 SwContentFrame* pContent = aIter.First();
347 if( pContent )
348 {
349 OSL_ENSURE( pContent->getRootFrame() == pFrame->getRootFrame(),
350 "lcl_ChangeFootnoteRef: Layout double?" );
351 SwFootnoteFrame *pFootnote = pContent->FindFootnoteFrame();
352 if( pFootnote && pFootnote->GetAttr() == pAttr )
353 {
354 while( pFootnote->GetMaster() )
355 pFootnote = pFootnote->GetMaster();
356 // #104840# - remember footnote frame
357 pFirstFootnoteOfNode = pFootnote;
358 while ( pFootnote )
359 {
360 pFootnote->SetRef( pFrame );
361 pFootnote = pFootnote->GetFollow();
362 static_cast<SwTextFrame*>(pFrame)->SetFootnote( true );
363 }
364 }
365 #if OSL_DEBUG_LEVEL > 0
366 while( nullptr != (pContent = aIter.Next()) )
367 {
368 SwFootnoteFrame *pDbgFootnote = pContent->FindFootnoteFrame();
369 OSL_ENSURE( !pDbgFootnote || pDbgFootnote->GetRef() == pFrame,
370 "lcl_ChangeFootnoteRef: Who's that guy?" );
371 }
372 #endif
373 }
374 }
375 } // end of for-loop on <SwpHints>
376 // #104840# - invalidate
377 if ( pFirstFootnoteOfNode )
378 {
379 SwContentFrame* pContent = pFirstFootnoteOfNode->ContainsContent();
380 if ( pContent )
381 {
382 pContent->InvalidatePos_();
383 }
384 }
385 }
386
387 namespace sw {
388
389 // check if there are flys on the existing frames (now on "pNode")
390 // that need to be moved to the new frames of "this"
MoveMergedFlysAndFootnotes(std::vector<SwTextFrame * > const & rFrames,SwTextNode const & rFirstNode,SwTextNode & rSecondNode,bool isSplitNode)391 void MoveMergedFlysAndFootnotes(std::vector<SwTextFrame*> const& rFrames,
392 SwTextNode const& rFirstNode, SwTextNode & rSecondNode,
393 bool isSplitNode)
394 {
395 if (!isSplitNode)
396 {
397 lcl_ChangeFootnoteRef(rSecondNode);
398 }
399 for (SwNodeOffset nIndex = rSecondNode.GetIndex() + 1; ; ++nIndex)
400 {
401 SwNode *const pTmp(rSecondNode.GetNodes()[nIndex]);
402 if (pTmp->IsCreateFrameWhenHidingRedlines() || pTmp->IsEndNode())
403 {
404 break;
405 }
406 else if (pTmp->IsStartNode())
407 {
408 nIndex = pTmp->EndOfSectionIndex();
409 }
410 else if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst
411 && pTmp->IsTextNode())
412 {
413 lcl_ChangeFootnoteRef(*pTmp->GetTextNode());
414 }
415 }
416 for (SwTextFrame *const pFrame : rFrames)
417 {
418 if (SwSortedObjs *const pObjs = pFrame->GetDrawObjs())
419 {
420 std::vector<SwAnchoredObject*> objs;
421 objs.reserve(pObjs->size());
422 for (SwAnchoredObject *const pObj : *pObjs)
423 {
424 objs.push_back(pObj);
425 }
426 for (SwAnchoredObject *const pObj : objs)
427 {
428 SwFrameFormat* pFormat(pObj->GetFrameFormat());
429 SwFormatAnchor const& rAnchor(pFormat->GetAnchor());
430 if (rFirstNode.GetIndex() < rAnchor.GetAnchorNode()->GetIndex())
431 {
432 // move it to the new frame of "this"
433 pFormat->CallSwClientNotify(sw::LegacyModifyHint(&rAnchor, &rAnchor));
434 // note pObjs will be deleted if it becomes empty
435 assert(!pFrame->GetDrawObjs() || !pObjs->Contains(*pObj));
436 }
437 }
438 }
439 }
440 }
441
442 } // namespace
443
SplitContentNode(const SwPosition & rPos,std::function<void (SwTextNode *,sw::mark::RestoreMode,bool AtStart)> const * const pContentIndexRestore)444 SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos,
445 std::function<void (SwTextNode *, sw::mark::RestoreMode, bool AtStart)> const*const pContentIndexRestore)
446 {
447 bool isHide(false);
448 SwNode::Merge const eOldMergeFlag(GetRedlineMergeFlag());
449 bool parentIsOutline = IsOutline();
450
451 // create a node "in front" of me
452 const sal_Int32 nSplitPos = rPos.GetContentIndex();
453 const sal_Int32 nTextLen = m_Text.getLength();
454 SwTextNode* const pNode =
455 MakeNewTextNode( rPos.GetNode(), false, nSplitPos==nTextLen );
456
457 // the first paragraph gets the XmlId,
458 // _except_ if it is empty and the second is not empty
459 if (nSplitPos != 0) {
460 pNode->RegisterAsCopyOf(*this, true);
461 if (nSplitPos == nTextLen)
462 {
463 RemoveMetadataReference();
464 // NB: SwUndoSplitNode will call pNode->JoinNext,
465 // which is sufficient even in this case!
466 }
467 }
468
469 ResetAttr( RES_PARATR_LIST_ISRESTART );
470 ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
471 ResetAttr( RES_PARATR_LIST_ISCOUNTED );
472 if ( GetNumRule() == nullptr || (parentIsOutline && !IsOutline()) )
473 {
474 ResetAttr( RES_PARATR_LIST_ID );
475 ResetAttr( RES_PARATR_LIST_LEVEL );
476 }
477
478 bool bSplitFly = false;
479 std::optional<std::vector<SwFrameFormat*>> oFlys = sw::GetFlysAnchoredAt(GetDoc(), GetIndex(), false);
480 if (oFlys.has_value())
481 {
482 // See if one of the flys is a split fly. If so, we need to keep
483 // the potentially split text frames unchanged and create a new
484 // text frame at the end.
485 for (const auto& rFly : *oFlys)
486 {
487 if (rFly->GetFlySplit().GetValue())
488 {
489 bSplitFly = true;
490 break;
491 }
492 }
493 }
494
495 if ( HasWriterListeners() && !m_Text.isEmpty() && ((nTextLen / 2) < nSplitPos || bSplitFly) )
496 {
497 // optimization for SplitNode: If a split is at the end of a node then
498 // move the frames from the current to the new one and create new ones
499 // for the current one.
500
501 // If fly frames are moved, they don't need to destroy their layout
502 // frames. Set a flag that is checked in SwTextFlyCnt::SetAnchor.
503 if ( HasHints() )
504 {
505 pNode->GetOrCreateSwpHints().SetInSplitNode(true);
506 }
507
508 // Move the first part of the content to the new node and delete
509 // it in the old node.
510 SwContentIndex aIdx( this );
511 CutText( pNode, aIdx, nSplitPos );
512
513 if( GetWrong() )
514 {
515 pNode->SetWrong( GetWrong()->SplitList( nSplitPos ) );
516 }
517 SetWrongDirty(sw::WrongState::TODO);
518
519 if( GetGrammarCheck() )
520 {
521 pNode->SetGrammarCheck( GetGrammarCheck()->SplitGrammarList( nSplitPos ) );
522 }
523 SetGrammarCheckDirty( true );
524
525 SetWordCountDirty( true );
526
527 if( GetSmartTags() )
528 {
529 pNode->SetSmartTags( GetSmartTags()->SplitList( nSplitPos ) );
530 }
531 SetSmartTagDirty( true );
532
533 resetAndQueueAccessibilityCheck();
534 pNode->resetAndQueueAccessibilityCheck();
535
536 if ( pNode->HasHints() )
537 {
538 if ( pNode->m_pSwpHints->CanBeDeleted() )
539 {
540 pNode->m_pSwpHints.reset();
541 }
542 else
543 {
544 pNode->m_pSwpHints->SetInSplitNode(false);
545 }
546
547 // All fly frames anchored as char that are moved to the new
548 // node must have their layout frames deleted.
549 // This comment is sort of silly because we actually delete the
550 // layout frames of those which were not moved?
551 // JP 01.10.96: delete all empty and not-to-be-expanded attributes
552 if ( HasHints() )
553 {
554 for ( size_t j = m_pSwpHints->Count(); j; )
555 {
556 SwTextAttr* const pHt = m_pSwpHints->Get( --j );
557 if ( RES_TXTATR_FLYCNT == pHt ->Which() )
558 {
559 pHt->GetFlyCnt().GetFrameFormat()->DelFrames();
560 }
561 else if ( pHt->DontExpand() )
562 {
563 const sal_Int32* const pEnd = pHt->GetEnd();
564 if (pEnd && pHt->GetStart() == *pEnd )
565 {
566 // delete it!
567 m_pSwpHints->DeleteAtPos( j );
568 DestroyAttr( pHt );
569 }
570 }
571 }
572 }
573
574 }
575
576 if (pContentIndexRestore)
577 { // call before making frames and before RegisterToNode
578 (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys, false);
579 }
580 if (eOldMergeFlag != SwNode::Merge::None)
581 { // clear before making frames and before RegisterToNode
582 SetRedlineMergeFlag(SwNode::Merge::None);
583 } // now RegisterToNode will set merge flags in both nodes properly!
584
585 std::vector<SwTextFrame*> frames;
586 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
587 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
588 {
589 if (pFrame->getRootFrame()->HasMergedParas())
590 {
591 isHide = true;
592 }
593 frames.push_back(pFrame);
594 }
595 for (SwTextFrame * pFrame : frames)
596 {
597 pFrame->RegisterToNode( *pNode );
598 if (!pFrame->IsFollow() && pFrame->GetOffset())
599 {
600 pFrame->SetOffset( TextFrameIndex(0) );
601 }
602 }
603
604 InvalidateInSwCache();
605
606 if ( HasHints() )
607 {
608 MoveTextAttr_To_AttrSet();
609 pNode->MoveTextAttr_To_AttrSet();
610 }
611 // in case there are frames, the RegisterToNode has set the merge flag
612 pNode->MakeFramesForAdjacentContentNode(*this);
613 lcl_ChangeFootnoteRef( *this );
614 if (pContentIndexRestore)
615 { // call after making frames; listeners will take care of adding to the right frame
616 (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys, false);
617 }
618 if (eOldMergeFlag != SwNode::Merge::None)
619 {
620 MoveMergedFlysAndFootnotes(frames, *pNode, *this, true);
621 }
622 }
623 else
624 {
625 std::unique_ptr<SwWrongList> pList = ReleaseWrong();
626 SetWrongDirty(sw::WrongState::TODO);
627
628 std::unique_ptr<SwGrammarMarkUp> pList3 = ReleaseGrammarCheck();
629 SetGrammarCheckDirty( true );
630
631 SetWordCountDirty( true );
632
633 std::unique_ptr<SwWrongList> pList2 = ReleaseSmartTags();
634 SetSmartTagDirty( true );
635
636 SwContentIndex aIdx( this );
637 CutText( pNode, aIdx, nSplitPos );
638
639 // JP 01.10.96: delete all empty and not-to-be-expanded attributes
640 if ( HasHints() )
641 {
642 for ( size_t j = m_pSwpHints->Count(); j; )
643 {
644 SwTextAttr* const pHt = m_pSwpHints->Get( --j );
645 const sal_Int32* const pEnd = pHt->GetEnd();
646 if ( pHt->DontExpand() && pEnd && (pHt->GetStart() == *pEnd) )
647 {
648 // delete it!
649 m_pSwpHints->DeleteAtPos( j );
650 DestroyAttr( pHt );
651 }
652 }
653 MoveTextAttr_To_AttrSet();
654 pNode->MoveTextAttr_To_AttrSet();
655 }
656
657 if( pList )
658 {
659 pNode->SetWrong( pList->SplitList( nSplitPos ) );
660 SetWrong( std::move(pList) );
661 }
662
663 if( pList3 )
664 {
665 pNode->SetGrammarCheck( pList3->SplitGrammarList( nSplitPos ) );
666 SetGrammarCheck( std::move(pList3) );
667 }
668
669 if( pList2 )
670 {
671 pNode->SetSmartTags( pList2->SplitList( nSplitPos ) );
672 SetSmartTags( std::move(pList2) );
673 }
674
675 resetAndQueueAccessibilityCheck();
676 pNode->resetAndQueueAccessibilityCheck();
677
678 if (pContentIndexRestore)
679 { // call before making frames and before RegisterToNode
680 (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys, false);
681 }
682
683 std::vector<SwTextFrame*> frames;
684 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
685 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
686 {
687 frames.push_back(pFrame);
688 if (pFrame->getRootFrame()->HasMergedParas())
689 {
690 isHide = true;
691 }
692 }
693 bool bNonMerged(false);
694 bool bRecreateThis(false);
695 for (SwTextFrame * pFrame : frames)
696 {
697 // sw_redlinehide: for this to work properly with hidden nodes,
698 // the frame needs to listen on them too.
699 // also: have to check the frame; this->GetRedlineMergeFlag()
700 // is None in case there's a delete redline inside the paragraph,
701 // but that could still result in a merged frame after split...
702 if (pFrame->GetMergedPara())
703 {
704 // Can't special case this == First here - that could (if
705 // both nodes are still merged by redline) lead to
706 // duplicate frames on "this".
707 // Update the extents with new node; also inits merge flag,
708 // so the MakeFramesForAdjacentContentNode below respects it
709 pFrame->RegisterToNode(*pNode);
710 if (nSplitPos == 0)
711 {
712 // in this case, it was not
713 // invalidated because Cut didn't sent it any hints,
714 // so we have to invalidate it here!
715 pFrame->Prepare(PrepareHint::Clear, nullptr, false);
716 }
717 if (!pFrame->GetMergedPara() ||
718 !pFrame->GetMergedPara()->listener.IsListeningTo(this))
719 {
720 // it's no longer listening - need to recreate frame
721 // (note this is idempotent, can be done once per frame)
722 SetRedlineMergeFlag(SwNode::Merge::None);
723 bRecreateThis = true;
724 }
725 }
726 else
727 {
728 bNonMerged = true;
729 }
730 }
731 assert(!(bNonMerged && bRecreateThis)); // 2 layouts not handled yet - maybe best to simply use the other branch then?
732 if (!frames.empty() && bNonMerged)
733 {
734 // the existing frame on "this" should have been updated by Cut
735 MakeFramesForAdjacentContentNode(*pNode);
736 lcl_ChangeFootnoteRef(*pNode);
737 }
738 else if (bRecreateThis)
739 {
740 assert(pNode->HasWriterListeners()); // was just moved there
741 pNode->MakeFramesForAdjacentContentNode(*this);
742 lcl_ChangeFootnoteRef(*this);
743 }
744
745 if (pContentIndexRestore)
746 { // call after making frames; listeners will take care of adding to the right frame
747 (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys, nSplitPos == 0);
748 }
749
750 if (bRecreateThis)
751 {
752 MoveMergedFlysAndFootnotes(frames, *pNode, *this, true);
753 }
754 }
755
756 // pNode is the previous node, 'this' is the next node from the split.
757 if (nSplitPos == nTextLen && m_pSwpHints)
758 {
759 // We just created an empty next node: avoid unwanted superscript in the new node if it's
760 // there.
761 ResetAttr(RES_CHRATR_ESCAPEMENT);
762 }
763
764 #ifndef NDEBUG
765 if (isHide) // otherwise flags won't be set anyway
766 {
767 // First
768 // -> First,NonFirst
769 // -> First,Hidden
770 // -> None,First
771 // Hidden
772 // -> Hidden,Hidden (if still inside merge rl)
773 // -> NonFirst,First (if redline was split)
774 // NonFirst
775 // -> NonFirst,First (if split after end of "incoming" redline &
776 // before start of "outgoing" redline)
777 // -> NonFirst,None (if split after end of "incoming" redline)
778 // -> NonFirst,Hidden (if split after start of "outgoing" redline)
779 // -> Hidden, NonFirst (if split before end of "incoming" redline)
780 // None
781 // -> None,None
782 // -> First,NonFirst (if splitting inside a delete redline)
783 SwNode::Merge const eFirst(pNode->GetRedlineMergeFlag());
784 SwNode::Merge const eSecond(GetRedlineMergeFlag());
785 switch (eOldMergeFlag)
786 {
787 case Merge::First:
788 assert((eFirst == Merge::First && eSecond == Merge::NonFirst)
789 || (eFirst == Merge::First && eSecond == Merge::Hidden)
790 || (eFirst == Merge::None && eSecond == Merge::First));
791 break;
792 case Merge::Hidden:
793 assert((eFirst == Merge::Hidden && eSecond == Merge::Hidden)
794 || (eFirst == Merge::NonFirst && eSecond == Merge::First)
795 // next ones can happen temp. in UndoDelete :(
796 || (eFirst == Merge::Hidden && eSecond == Merge::NonFirst)
797 || (eFirst == Merge::NonFirst && eSecond == Merge::None));
798 break;
799 case Merge::NonFirst:
800 assert((eFirst == Merge::NonFirst && eSecond == Merge::First)
801 || (eFirst == Merge::NonFirst && eSecond == Merge::None)
802 || (eFirst == Merge::NonFirst && eSecond == Merge::Hidden)
803 || (eFirst == Merge::Hidden && eSecond == Merge::NonFirst));
804 break;
805 case Merge::None:
806 assert((eFirst == Merge::None && eSecond == Merge::None)
807 || (eFirst == Merge::First && eSecond == Merge::NonFirst));
808 break;
809 }
810 }
811 #else
812 (void) isHide;
813 #endif
814
815 {
816 // Send Hint for PageDesc. This should be done in the Layout when
817 // pasting the frames, but that causes other problems that look
818 // expensive to solve.
819 const SwFormatPageDesc *pItem;
820 if(HasWriterListeners() && (pItem = pNode->GetSwAttrSet().GetItemIfSet(RES_PAGEDESC)))
821 pNode->TriggerNodeUpdate(sw::LegacyModifyHint(pItem, pItem));
822 }
823 return pNode;
824 }
825
MoveTextAttr_To_AttrSet()826 void SwTextNode::MoveTextAttr_To_AttrSet()
827 {
828 OSL_ENSURE( m_pSwpHints, "MoveTextAttr_To_AttrSet without SwpHints?" );
829 for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
830 {
831 SwTextAttr *pHt = m_pSwpHints->Get(i);
832
833 if( pHt->GetStart() )
834 break;
835
836 const sal_Int32* pHtEndIdx = pHt->GetEnd();
837
838 if( !pHtEndIdx )
839 continue;
840
841 if (*pHtEndIdx < m_Text.getLength() || pHt->IsCharFormatAttr())
842 break;
843
844 if (!pHt->IsDontMoveAttr())
845 {
846 bool isInserted(false);
847 if (pHt->Which() == RES_TXTATR_AUTOFMT)
848 {
849 isInserted = SetAttr(*pHt->GetAutoFormat().GetStyleHandle());
850 }
851 else
852 {
853 isInserted = SetAttr(pHt->GetAttr());
854 }
855 if (isInserted)
856 {
857 m_pSwpHints->DeleteAtPos(i);
858 DestroyAttr( pHt );
859 --i;
860 }
861 }
862 }
863
864 }
865
866 namespace sw {
867
868 /// if first node is deleted & second survives, then the first node's frame
869 /// will be deleted too; prevent this by moving the frame to the second node
870 /// if necessary.
MoveDeletedPrevFrames(const SwTextNode & rDeletedPrev,SwTextNode & rNode)871 void MoveDeletedPrevFrames(const SwTextNode & rDeletedPrev, SwTextNode & rNode)
872 {
873 std::vector<SwTextFrame*> frames;
874 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rDeletedPrev);
875 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
876 {
877 if (pFrame->getRootFrame()->HasMergedParas())
878 {
879 frames.push_back(pFrame);
880 }
881 }
882 {
883 auto frames2(frames);
884 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIt(rNode);
885 for (SwTextFrame* pFrame = aIt.First(); pFrame; pFrame = aIt.Next())
886 {
887 if (pFrame->getRootFrame()->HasMergedParas())
888 {
889 auto const it(std::find(frames2.begin(), frames2.end(), pFrame));
890 assert(it != frames2.end());
891 frames2.erase(it);
892 }
893 }
894 assert(frames2.empty());
895 }
896 for (SwTextFrame *const pFrame : frames)
897 {
898 pFrame->RegisterToNode(rNode, true);
899 }
900 }
901
902 // typical Join:
903 // None,Node->None
904 // None,First->First
905 // First,NonFirst->First
906 // NonFirst,First->NonFirst
907 // NonFirst,None->NonFirst
908
909 /// if first node is First, its frames may need to be moved, never deleted.
910 /// if first node is NonFirst, second node's own frames (First/None) must be deleted
CheckResetRedlineMergeFlag(SwTextNode & rNode,Recreate const eRecreateMerged)911 void CheckResetRedlineMergeFlag(SwTextNode & rNode, Recreate const eRecreateMerged)
912 {
913 if (eRecreateMerged != sw::Recreate::No)
914 {
915 SwTextNode * pMergeNode(&rNode);
916 if (eRecreateMerged == sw::Recreate::Predecessor
917 // tdf#135018 check that there is a predecessor node, i.e. rNode
918 // isn't the first node after the body start node
919 && rNode.GetNodes()[rNode.GetIndex() - 1]->StartOfSectionIndex() != SwNodeOffset(0))
920 {
921 for (SwNodeOffset i = rNode.GetIndex() - 1; ; --i)
922 {
923 SwNode *const pNode(rNode.GetNodes()[i]);
924 assert(!pNode->IsStartNode());
925 if (pNode->IsEndNode())
926 {
927 i = pNode->StartOfSectionIndex();
928 }
929 else if (pNode->IsTextNode())
930 {
931 pMergeNode = pNode->GetTextNode(); // use predecessor to merge
932 break;
933 }
934 }
935 }
936 std::vector<SwTextFrame*> frames;
937 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pMergeNode);
938 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
939 {
940 if (pFrame->getRootFrame()->HasMergedParas())
941 {
942 frames.push_back(pFrame);
943 }
944 }
945 auto eMode(sw::FrameMode::Existing);
946 for (SwTextFrame * pFrame : frames)
947 {
948 SwTextNode & rFirstNode(pFrame->GetMergedPara()
949 ? *pFrame->GetMergedPara()->pFirstNode
950 : *pMergeNode);
951 assert(rFirstNode.GetIndex() <= rNode.GetIndex());
952 pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
953 *pFrame, rFirstNode, eMode));
954 // there is no merged para in case the deleted node had one but
955 // nothing was actually hidden
956 if (pFrame->GetMergedPara())
957 {
958 assert(pFrame->GetMergedPara()->listener.IsListeningTo(&rNode));
959 assert(rNode.GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex());
960 // tdf#135978 Join: recreate fly frames anchored to subsequent nodes
961 if (eRecreateMerged == sw::Recreate::ThisNode)
962 {
963 AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, rNode, nullptr);
964 }
965 }
966 eMode = sw::FrameMode::New; // Existing is not idempotent!
967 }
968 }
969 else if (rNode.GetRedlineMergeFlag() != SwNode::Merge::None)
970 {
971 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rNode);
972 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
973 {
974 if (auto const pMergedPara = pFrame->GetMergedPara())
975 {
976 if (pMergedPara->pFirstNode == pMergedPara->pLastNode)
977 {
978 assert(pMergedPara->pFirstNode == &rNode);
979 rNode.SetRedlineMergeFlag(SwNode::Merge::None);
980 }
981 break; // checking once is enough
982 }
983 else if (pFrame->getRootFrame()->HasMergedParas())
984 {
985 rNode.SetRedlineMergeFlag(SwNode::Merge::None);
986 break; // checking once is enough
987 }
988 }
989 }
990 }
991
HasNumberingWhichNeedsLayoutUpdate(const SwTextNode & rTextNode)992 bool HasNumberingWhichNeedsLayoutUpdate(const SwTextNode& rTextNode)
993 {
994 const SwNodeNum* pNodeNum = rTextNode.GetNum();
995 if (!pNodeNum)
996 {
997 return false;
998 }
999
1000 const SwNumRule* pNumRule = pNodeNum->GetNumRule();
1001 if (!pNumRule)
1002 {
1003 return false;
1004 }
1005
1006 const SwNumFormat* pFormat
1007 = pNumRule->GetNumFormat(o3tl::narrowing<sal_uInt16>(rTextNode.GetAttrListLevel()));
1008 if (!pFormat)
1009 {
1010 return false;
1011 }
1012
1013 switch (pFormat->GetNumberingType())
1014 {
1015 case SVX_NUM_NUMBER_NONE:
1016 case SVX_NUM_CHAR_SPECIAL:
1017 case SVX_NUM_BITMAP:
1018 return false;
1019 default:
1020 return true;
1021 }
1022 }
1023 } // namespace
1024
JoinNext()1025 SwContentNode *SwTextNode::JoinNext()
1026 {
1027 SwNodes& rNds = GetNodes();
1028 SwNodeIndex aIdx( *this );
1029 if( SwContentNode::CanJoinNext( &aIdx ) )
1030 {
1031 SwDoc& rDoc = rNds.GetDoc();
1032 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
1033 pContentStore->Save(rDoc, aIdx.GetIndex(), SAL_MAX_INT32);
1034 SwTextNode *pTextNode = aIdx.GetNode().GetTextNode();
1035 sal_Int32 nOldLen = m_Text.getLength();
1036
1037 // METADATA: merge
1038 JoinMetadatable(*pTextNode, !Len(), !pTextNode->Len());
1039
1040 std::unique_ptr<SwWrongList> pList = ReleaseWrong();
1041 if( pList )
1042 {
1043 pList->JoinList( pTextNode->GetWrong(), nOldLen );
1044 SetWrongDirty(sw::WrongState::TODO);
1045 }
1046 else
1047 {
1048 pList = pTextNode->ReleaseWrong();
1049 if( pList )
1050 {
1051 pList->Move( 0, nOldLen );
1052 SetWrongDirty(sw::WrongState::TODO);
1053 }
1054 }
1055
1056 std::unique_ptr<SwGrammarMarkUp> pList3 = ReleaseGrammarCheck();
1057 if( pList3 )
1058 {
1059 pList3->JoinGrammarList( pTextNode->GetGrammarCheck(), nOldLen );
1060 SetGrammarCheckDirty( true );
1061 }
1062 else
1063 {
1064 pList3 = pTextNode->ReleaseGrammarCheck();
1065 if( pList3 )
1066 {
1067 pList3->MoveGrammar( 0, nOldLen );
1068 SetGrammarCheckDirty( true );
1069 }
1070 }
1071
1072 std::unique_ptr<SwWrongList> pList2 = ReleaseSmartTags();
1073 if( pList2 )
1074 {
1075 pList2->JoinList( pTextNode->GetSmartTags(), nOldLen );
1076 SetSmartTagDirty( true );
1077 }
1078 else
1079 {
1080 pList2 = pTextNode->ReleaseSmartTags();
1081 if( pList2 )
1082 {
1083 pList2->Move( 0, nOldLen );
1084 SetSmartTagDirty( true );
1085 }
1086 }
1087
1088 { // scope for SwContentIndex
1089 pTextNode->CutText( this, SwContentIndex(pTextNode), pTextNode->Len() );
1090 }
1091 // move all Bookmarks/TOXMarks
1092 if( !pContentStore->Empty())
1093 pContentStore->Restore( rDoc, GetIndex(), nOldLen );
1094
1095 if( pTextNode->HasAnyIndex() )
1096 {
1097 // move all ShellCursor/StackCursor/UnoCursor out of delete range
1098 rDoc.CorrAbs( aIdx.GetNode(), SwPosition( *this ), nOldLen, true );
1099 }
1100 SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
1101 auto eRecreateMerged(eOldMergeFlag == SwNode::Merge::First
1102 ? sw::Recreate::ThisNode
1103 : sw::Recreate::No);
1104 if (eRecreateMerged == sw::Recreate::No)
1105 {
1106 // tdf#137318 if a delete is inside one node, flag is still None!
1107 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode);
1108 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
1109 {
1110 if (pFrame->GetMergedPara())
1111 {
1112 eRecreateMerged = sw::Recreate::ThisNode;
1113 break;
1114 }
1115 }
1116 }
1117 bool bOldHasNumberingWhichNeedsLayoutUpdate = HasNumberingWhichNeedsLayoutUpdate(*pTextNode);
1118
1119 rNds.Delete(aIdx);
1120 SetWrong( std::move(pList) );
1121 SetGrammarCheck( std::move(pList3) );
1122 SetSmartTags( std::move(pList2) );
1123
1124 resetAndQueueAccessibilityCheck();
1125
1126 if (bOldHasNumberingWhichNeedsLayoutUpdate || HasNumberingWhichNeedsLayoutUpdate(*this))
1127 {
1128 // Repaint all text frames that belong to this numbering to avoid outdated generated
1129 // numbers.
1130 InvalidateNumRule();
1131 }
1132
1133 CheckResetRedlineMergeFlag(*this, eRecreateMerged);
1134 }
1135 else {
1136 OSL_FAIL( "No TextNode." );
1137 }
1138
1139 return this;
1140 }
1141
JoinPrev()1142 void SwTextNode::JoinPrev()
1143 {
1144 SwNodes& rNds = GetNodes();
1145 SwNodeIndex aIdx( *this );
1146 if( SwContentNode::CanJoinPrev( &aIdx ) )
1147 {
1148 SwDoc& rDoc = rNds.GetDoc();
1149 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
1150 pContentStore->Save( rDoc, aIdx.GetIndex(), SAL_MAX_INT32);
1151 SwTextNode *pTextNode = aIdx.GetNode().GetTextNode();
1152 const sal_Int32 nLen = pTextNode->Len();
1153
1154 std::unique_ptr<SwWrongList> pList = pTextNode->ReleaseWrong();
1155 if( pList )
1156 {
1157 pList->JoinList( GetWrong(), Len() );
1158 SetWrongDirty(sw::WrongState::TODO);
1159 ClearWrong();
1160 }
1161 else
1162 {
1163 pList = ReleaseWrong();
1164 if( pList )
1165 {
1166 pList->Move( 0, nLen );
1167 SetWrongDirty(sw::WrongState::TODO);
1168 }
1169 }
1170
1171 std::unique_ptr<SwGrammarMarkUp> pList3 = pTextNode->ReleaseGrammarCheck();
1172 if( pList3 )
1173 {
1174 pList3->JoinGrammarList( GetGrammarCheck(), Len() );
1175 SetGrammarCheckDirty( true );
1176 ClearGrammarCheck();
1177 }
1178 else
1179 {
1180 pList3 = ReleaseGrammarCheck();
1181 if( pList3 )
1182 {
1183 pList3->MoveGrammar( 0, nLen );
1184 SetGrammarCheckDirty( true );
1185 }
1186 }
1187
1188 std::unique_ptr<SwWrongList> pList2 = pTextNode->ReleaseSmartTags();
1189 if( pList2 )
1190 {
1191 pList2->JoinList( GetSmartTags(), Len() );
1192 SetSmartTagDirty( true );
1193 ClearSmartTags();
1194 }
1195 else
1196 {
1197 pList2 = ReleaseSmartTags();
1198 if( pList2 )
1199 {
1200 pList2->Move( 0, nLen );
1201 SetSmartTagDirty( true );
1202 }
1203 }
1204
1205 { // scope for SwContentIndex
1206 pTextNode->CutText( this, SwContentIndex(this), SwContentIndex(pTextNode), nLen );
1207 }
1208 // move all Bookmarks/TOXMarks
1209 if( !pContentStore->Empty() )
1210 pContentStore->Restore( rDoc, GetIndex() );
1211
1212 if( pTextNode->HasAnyIndex() )
1213 {
1214 // move all ShellCursor/StackCursor/UnoCursor out of delete range
1215 rDoc.CorrAbs( aIdx.GetNode(), SwPosition( *this ), nLen, true );
1216 }
1217 SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
1218 if (eOldMergeFlag == SwNode::Merge::First
1219 && !IsCreateFrameWhenHidingRedlines())
1220 {
1221 sw::MoveDeletedPrevFrames(*pTextNode, *this);
1222 }
1223 rNds.Delete(aIdx);
1224 SetWrong( std::move(pList) );
1225 SetGrammarCheck( std::move(pList3) );
1226 SetSmartTags( std::move(pList2) );
1227 resetAndQueueAccessibilityCheck();
1228 InvalidateNumRule();
1229 sw::CheckResetRedlineMergeFlag(*this,
1230 eOldMergeFlag == SwNode::Merge::NonFirst
1231 ? sw::Recreate::Predecessor
1232 : sw::Recreate::No);
1233 }
1234 else {
1235 OSL_FAIL( "No TextNode." );
1236 }
1237 }
1238
1239 // create an AttrSet with ranges for Frame-/Para/Char-attributes
NewAttrSet(SwAttrPool & rPool)1240 void SwTextNode::NewAttrSet( SwAttrPool& rPool )
1241 {
1242 OSL_ENSURE( !mpAttrSet, "AttrSet is set after all" );
1243 SwAttrSet aNewAttrSet( rPool, aTextNodeSetRange );
1244
1245 // put names of parent style and conditional style:
1246 const SwFormatColl* pAnyFormatColl = &GetAnyFormatColl();
1247 const SwFormatColl* pFormatColl = GetFormatColl();
1248 ProgName sVal;
1249 SwStyleNameMapper::FillProgName( pAnyFormatColl->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
1250 SfxStringItem aAnyFormatColl( RES_FRMATR_STYLE_NAME, sVal.toString() );
1251 if ( pFormatColl != pAnyFormatColl )
1252 SwStyleNameMapper::FillProgName( pFormatColl->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
1253 SfxStringItem aFormatColl( RES_FRMATR_CONDITIONAL_STYLE_NAME, sVal.toString() );
1254 aNewAttrSet.Put( aAnyFormatColl );
1255 aNewAttrSet.Put( aFormatColl );
1256
1257 aNewAttrSet.SetParent( &pAnyFormatColl->GetAttrSet() );
1258 mpAttrSet = GetDoc().GetIStyleAccess().getAutomaticStyle( aNewAttrSet, IStyleAccess::AUTO_STYLE_PARA, &sVal );
1259 }
1260
1261 namespace
1262 {
1263 class SwContentNodeTmp : public SwContentNode
1264 {
1265 public:
SwContentNodeTmp()1266 SwContentNodeTmp() : SwContentNode() {}
NewAttrSet(SwAttrPool &)1267 virtual void NewAttrSet(SwAttrPool&) override {}
MakeFrame(SwFrame *)1268 virtual SwContentFrame *MakeFrame(SwFrame*) override { return nullptr; }
MakeCopy(SwDoc &,SwNode &,bool) const1269 virtual SwContentNode* MakeCopy(SwDoc&, SwNode&, bool /*bNewFrames*/) const override { return nullptr; };
1270 };
1271 };
1272
1273 // override SwContentIndexReg::Update => text hints do not need SwContentIndex for start/end!
Update(SwContentIndex const & rPos,const sal_Int32 nChangeLen,UpdateMode const eMode)1274 void SwTextNode::Update(
1275 SwContentIndex const & rPos,
1276 const sal_Int32 nChangeLen,
1277 UpdateMode const eMode)
1278 {
1279 assert(rPos.GetContentNode() == this);
1280 SetAutoCompleteWordDirty( true );
1281
1282 SwpHts aCollector;
1283 const sal_Int32 nChangePos = rPos.GetIndex();
1284
1285 if ( HasHints() )
1286 {
1287 if (eMode & UpdateMode::Negative)
1288 {
1289 std::vector<SwTextInputField*> aTextInputFields;
1290
1291 const sal_Int32 nChangeEnd = nChangePos + nChangeLen;
1292 for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
1293 {
1294 bool bTextAttrChanged = false;
1295 bool bStartOfTextAttrChanged = false;
1296 SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
1297 if ( pHint->GetStart() > nChangePos )
1298 {
1299 if ( pHint->GetStart() > nChangeEnd )
1300 {
1301 pHint->SetStart( pHint->GetStart() - nChangeLen );
1302 }
1303 else
1304 {
1305 pHint->SetStart( nChangePos );
1306 }
1307 bStartOfTextAttrChanged = true;
1308 }
1309
1310 const sal_Int32 * const pEnd = pHint->GetEnd();
1311 if (pEnd && *pEnd > nChangePos )
1312 {
1313 if( *pEnd > nChangeEnd )
1314 {
1315 pHint->SetEnd(*pEnd - nChangeLen);
1316 }
1317 else
1318 {
1319 pHint->SetEnd(nChangePos);
1320 }
1321 bTextAttrChanged = !bStartOfTextAttrChanged;
1322 }
1323
1324 if ( bTextAttrChanged
1325 && pHint->Which() == RES_TXTATR_INPUTFIELD )
1326 {
1327 SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint);
1328 if ( pTextInputField )
1329 aTextInputFields.push_back(pTextInputField);
1330 }
1331 }
1332
1333 //wait until all the attribute positions are correct
1334 //before updating the field contents
1335 for (SwTextInputField* pTextInputField : aTextInputFields)
1336 {
1337 pTextInputField->UpdateFieldContent();
1338 }
1339
1340 m_pSwpHints->MergePortions( *this );
1341 }
1342 else
1343 {
1344 bool bNoExp = false;
1345 bool bResort = false;
1346 bool bMergePortionsNeeded = false;
1347 const int coArrSz = RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN;
1348 std::vector<SwTextInputField*> aTextInputFields;
1349
1350 bool aDontExp[ coArrSz ] = {};
1351
1352 for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
1353 {
1354 bool bTextAttrChanged = false;
1355 SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
1356 const sal_Int32 * const pEnd = pHint->GetEnd();
1357 if ( pHint->GetStart() >= nChangePos )
1358 {
1359 pHint->SetStart( pHint->GetStart() + nChangeLen );
1360 if ( pEnd )
1361 {
1362 pHint->SetEnd(*pEnd + nChangeLen);
1363 }
1364 }
1365 else if ( pEnd && (*pEnd >= nChangePos) )
1366 {
1367 if ( (*pEnd > nChangePos) || IsIgnoreDontExpand() )
1368 {
1369 pHint->SetEnd(*pEnd + nChangeLen);
1370 bTextAttrChanged = true;
1371 }
1372 else // *pEnd == nChangePos
1373 {
1374 const sal_uInt16 nWhich = pHint->Which();
1375
1376 OSL_ENSURE(!isCHRATR(nWhich), "Update: char attr hint?");
1377 if (!(isCHRATR(nWhich) || isTXTATR_WITHEND(nWhich)))
1378 continue;
1379
1380 const sal_uInt16 nWhPos = nWhich - RES_CHRATR_BEGIN;
1381
1382 if( aDontExp[ nWhPos ] )
1383 continue;
1384
1385 if ( pHint->DontExpand() )
1386 {
1387 pHint->SetDontExpand( false );
1388 bResort = true;
1389 // could have a continuation with IgnoreStart()...
1390 if (pHint->IsFormatIgnoreEnd())
1391 {
1392 bMergePortionsNeeded = true;
1393 }
1394 if ( pHint->IsCharFormatAttr() )
1395 {
1396 bNoExp = true;
1397 aDontExp[ RES_TXTATR_CHARFMT - RES_CHRATR_BEGIN ] = true;
1398 aDontExp[ RES_TXTATR_INETFMT - RES_CHRATR_BEGIN ] = true;
1399 }
1400 else
1401 aDontExp[ nWhPos ] = true;
1402 }
1403 else if( bNoExp )
1404 {
1405 auto it = std::find_if(aCollector.begin(), aCollector.end(),
1406 [nWhich](const SwTextAttr *pTmp) { return nWhich == pTmp->Which(); });
1407 if (it != aCollector.end())
1408 {
1409 SwTextAttr *pTmp = *it;
1410 aCollector.erase( it );
1411 SwTextAttr::Destroy( pTmp );
1412 }
1413 SwTextAttr * const pTmp =
1414 MakeTextAttr( GetDoc(),
1415 pHint->GetAttr(), nChangePos, nChangePos + nChangeLen);
1416 aCollector.push_back( pTmp );
1417 }
1418 else
1419 {
1420 pHint->SetEnd(*pEnd + nChangeLen);
1421 bTextAttrChanged = true;
1422 }
1423 }
1424 }
1425
1426 if ( bTextAttrChanged
1427 && pHint->Which() == RES_TXTATR_INPUTFIELD )
1428 {
1429 SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint);
1430 if ( pTextInputField )
1431 aTextInputFields.push_back(pTextInputField);
1432 }
1433 }
1434
1435 //wait until all the attribute positions are correct
1436 //before updating the field contents
1437 for (SwTextInputField* pTextInputField : aTextInputFields)
1438 {
1439 pTextInputField->UpdateFieldContent();
1440 }
1441
1442 if (bMergePortionsNeeded)
1443 {
1444 m_pSwpHints->MergePortions(*this); // does Resort too
1445 }
1446 else if (bResort)
1447 {
1448 m_pSwpHints->Resort();
1449 }
1450 }
1451 }
1452
1453 bool bSortMarks = false;
1454 SwContentNodeTmp aTmpIdxReg;
1455 if (!(eMode & UpdateMode::Negative) && !(eMode & UpdateMode::Delete))
1456 {
1457 o3tl::sorted_vector<SwRangeRedline*> vMyRedlines;
1458 // walk the list of SwContentIndex attached to me and see if any of them are redlines
1459 const SwContentIndex* pContentNodeIndex = GetFirstIndex();
1460 while (pContentNodeIndex)
1461 {
1462 if (pContentNodeIndex->GetOwner() && pContentNodeIndex->GetOwner()->GetOwnerType() == SwContentIndexOwnerType::Redline)
1463 {
1464 auto pRedl = static_cast<SwRangeRedline*>(pContentNodeIndex->GetOwner());
1465 if (pRedl && (pRedl->HasMark() || this == &pRedl->GetPoint()->GetNode()))
1466 vMyRedlines.insert(pRedl);
1467 }
1468 pContentNodeIndex = pContentNodeIndex->GetNext();
1469 }
1470 for (SwRangeRedline* pRedl : vMyRedlines)
1471 {
1472 if ( pRedl->HasMark() )
1473 {
1474 SwPosition* const pEnd = pRedl->End();
1475 if ( *this == pEnd->GetNode() &&
1476 *pRedl->GetPoint() != *pRedl->GetMark() )
1477 {
1478 SwContentIndex & rIdx = pEnd->nContent;
1479 if (nChangePos == rIdx.GetIndex())
1480 {
1481 rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
1482 }
1483 }
1484 }
1485 else if ( this == &pRedl->GetPoint()->GetNode() )
1486 {
1487 SwContentIndex & rIdx = pRedl->GetPoint()->nContent;
1488 if (nChangePos == rIdx.GetIndex())
1489 {
1490 rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
1491 }
1492 // the unused position must not be on a SwTextNode
1493 bool const isOneUsed(&pRedl->GetBound() == pRedl->GetPoint());
1494 assert(!pRedl->GetBound(!isOneUsed).GetNode().IsTextNode());
1495 assert(!pRedl->GetBound(!isOneUsed).GetContentNode()); (void)isOneUsed;
1496 }
1497 }
1498
1499 // Bookmarks must never grow to either side, when editing (directly)
1500 // to the left or right (i#29942)! Exception: if the bookmark has
1501 // 2 positions and start == end, then expand it (tdf#96479)
1502 if (!(eMode & UpdateMode::Replace)) // Exception: Replace
1503 {
1504 bool bAtLeastOneBookmarkMoved = false;
1505 bool bAtLeastOneExpandedBookmarkAtInsertionPosition = false;
1506 // A text node already knows its marks via its SwContentIndexes.
1507 o3tl::sorted_vector<const sw::mark::MarkBase*> aSeenMarks;
1508 const SwContentIndex* next;
1509 for (const SwContentIndex* pIndex = GetFirstIndex(); pIndex; pIndex = next )
1510 {
1511 next = pIndex->GetNext();
1512 if (!pIndex->GetOwner() || pIndex->GetOwner()->GetOwnerType() != SwContentIndexOwnerType::Mark)
1513 continue;
1514 auto const pMark = static_cast<sw::mark::MarkBase const*>(pIndex->GetOwner());
1515 // filter out ones that cannot match to reduce the max size of aSeenMarks
1516 const SwPosition* pMarkPos1 = &pMark->GetMarkPos();
1517 const SwPosition* pMarkPos2 = pMark->IsExpanded() ? &pMark->GetOtherMarkPos() : nullptr;
1518 if (pMarkPos1->nContent.GetIndex() != rPos.GetIndex()
1519 && (pMarkPos2 == nullptr || pMarkPos2->nContent.GetIndex() != rPos.GetIndex()))
1520 continue;
1521 // Only handle bookmarks once, if they start and end at this node as well.
1522 if (!aSeenMarks.insert(pMark).second)
1523 continue;
1524 const SwPosition* pEnd = &pMark->GetMarkEnd();
1525 SwContentIndex & rEndIdx = const_cast<SwContentIndex&>(pEnd->nContent);
1526 if( *this == pEnd->GetNode() &&
1527 rPos.GetIndex() == rEndIdx.GetIndex() )
1528 {
1529 if (&rEndIdx == next) // nasty corner case:
1530 { // don't switch to iterating aTmpIdxReg!
1531 next = rEndIdx.GetNext();
1532 }
1533 // tdf#96479: if start == end, ignore the other position
1534 // so it is moved!
1535 rEndIdx.Assign( &aTmpIdxReg, rEndIdx.GetIndex() );
1536 bAtLeastOneBookmarkMoved = true;
1537 }
1538 else if ( !bAtLeastOneExpandedBookmarkAtInsertionPosition )
1539 {
1540 if ( pMark->IsExpanded() )
1541 {
1542 const SwPosition* pStart = &pMark->GetMarkStart();
1543 if ( this == &pStart->GetNode()
1544 && rPos.GetIndex() == pStart->GetContentIndex() )
1545 {
1546 bAtLeastOneExpandedBookmarkAtInsertionPosition = true;
1547 }
1548 }
1549 }
1550 }
1551
1552 bSortMarks = bAtLeastOneBookmarkMoved && bAtLeastOneExpandedBookmarkAtInsertionPosition;
1553 }
1554
1555 // at-char anchored flys shouldn't be moved, either.
1556 if (!m_bInUndo)
1557 {
1558 std::vector<SwFrameFormat*> const& rFlys(GetAnchoredFlys());
1559 for (size_t i = 0; i != rFlys.size(); ++i)
1560 {
1561 SwFrameFormat const*const pFormat = rFlys[i];
1562 const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
1563 const SwNode* pAnchorNode = rAnchor.GetAnchorNode();
1564 if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR && pAnchorNode)
1565 {
1566 // The fly is at-char anchored and has an anchor position.
1567 SwContentIndex& rEndIdx = const_cast<SwContentIndex&>(rAnchor.GetContentAnchor()->nContent);
1568 if (*pAnchorNode == *this && rEndIdx.GetIndex() == rPos.GetIndex())
1569 {
1570 // The anchor position is exactly our insert position.
1571 rEndIdx.Assign(&aTmpIdxReg, rEndIdx.GetIndex());
1572 }
1573 }
1574 }
1575 }
1576
1577 // The cursors of other shells shouldn't be moved, either.
1578 if (SwDocShell* pDocShell = GetDoc().GetDocShell())
1579 {
1580 if (pDocShell->GetWrtShell())
1581 {
1582 for (SwViewShell& rShell : pDocShell->GetWrtShell()->GetRingContainer())
1583 {
1584 auto pWrtShell = dynamic_cast<SwWrtShell*>(&rShell);
1585 if (!pWrtShell || pWrtShell == dynamic_cast<SwWrtShell*>(GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()))
1586 continue;
1587
1588 SwShellCursor* pCursor = pWrtShell->GetCursor_();
1589 if (!pCursor)
1590 continue;
1591
1592 SwContentIndex& rIndex = pCursor->Start()->nContent;
1593 if (pCursor->Start()->GetNode() == *this && rIndex.GetIndex() == rPos.GetIndex())
1594 {
1595 // The cursor position of this other shell is exactly our insert position.
1596 rIndex.Assign(&aTmpIdxReg, rIndex.GetIndex());
1597 }
1598 }
1599 }
1600 }
1601 }
1602
1603 // base class
1604 SwContentIndexReg::Update(rPos, nChangeLen, eMode);
1605
1606 for ( SwTextAttr* pAttr : aCollector )
1607 {
1608 m_pSwpHints->TryInsertHint( pAttr, *this );
1609 }
1610
1611 aTmpIdxReg.MoveTo( *this );
1612 if ( bSortMarks )
1613 {
1614 getIDocumentMarkAccess()->assureSortedMarkContainers();
1615 }
1616
1617 //Any drawing objects anchored into this text node may be sorted by their
1618 //anchor position which may have changed here, so resort them
1619 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> iter(*this);
1620 for (SwTextFrame* pFrame = iter.First(); pFrame; pFrame = iter.Next())
1621 {
1622 SwSortedObjs * pSortedObjs(pFrame->GetDrawObjs());
1623 if (pSortedObjs)
1624 {
1625 pSortedObjs->UpdateAll();
1626 }
1627 // also sort the objs on the page frame
1628 if (SwPageFrame *pPage = pFrame->FindPageFrame())
1629 pSortedObjs = pPage->GetSortedObjs();
1630
1631 if (pSortedObjs) // doesn't exist yet if called for inserting as-char fly
1632 {
1633 pSortedObjs->UpdateAll();
1634 }
1635 }
1636
1637 // Update the paragraph signatures.
1638 if (SwEditShell* pEditShell = GetDoc().GetEditShell())
1639 {
1640 pEditShell->ValidateParagraphSignatures(this, true);
1641 }
1642
1643 // Inform LOK clients about change in position of redlines (if any)
1644 // Don't emit notifications during save: redline flags are temporarily changed during save, but
1645 // it's not useful to let clients know about such changes.
1646 if (!comphelper::LibreOfficeKit::isActive() || GetDoc().IsInWriting())
1647 return;
1648
1649 const SwRedlineTable& rTable = GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
1650 for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
1651 {
1652 SwRangeRedline* pRedln = rTable[nRedlnPos];
1653 if (pRedln->HasMark())
1654 {
1655 if (*this == pRedln->End()->GetNode() && *pRedln->GetPoint() != *pRedln->GetMark())
1656 {
1657 // Redline is changed only when some change occurs before it
1658 if (nChangePos <= pRedln->Start()->GetContentIndex())
1659 {
1660 SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, pRedln);
1661 }
1662 }
1663 }
1664 else if (this == &pRedln->GetPoint()->GetNode())
1665 SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, pRedln);
1666 }
1667 }
1668
ChgTextCollUpdateNum(const SwTextFormatColl * pOldColl,const SwTextFormatColl * pNewColl,bool bSetListLevel)1669 void SwTextNode::ChgTextCollUpdateNum(const SwTextFormatColl* pOldColl,
1670 const SwTextFormatColl* pNewColl,
1671 bool bSetListLevel)
1672 {
1673 SwDoc& rDoc = GetDoc();
1674 // query the OutlineLevel and if it changed, notify the Nodes-Array!
1675 const int nOldLevel = pOldColl && pOldColl->IsAssignedToListLevelOfOutlineStyle()
1676 ? pOldColl->GetAssignedOutlineStyleLevel()
1677 : MAXLEVEL;
1678 const int nNewLevel = pNewColl && pNewColl->IsAssignedToListLevelOfOutlineStyle() ?
1679 pNewColl->GetAssignedOutlineStyleLevel() : MAXLEVEL;
1680
1681 if ( MAXLEVEL != nNewLevel && -1 != nNewLevel && bSetListLevel )
1682 {
1683 SetAttrListLevel(nNewLevel);
1684 }
1685 rDoc.GetNodes().UpdateOutlineNode(*this);
1686
1687 SwNodes& rNds = GetNodes();
1688 // If Level 0 (Chapter), update the footnotes!
1689 if( ( !nNewLevel || !nOldLevel) && !rDoc.GetFootnoteIdxs().empty() &&
1690 FTNNUM_CHAPTER == rDoc.GetFootnoteInfo().m_eNum &&
1691 rNds.IsDocNodes() )
1692 {
1693 rDoc.GetFootnoteIdxs().UpdateFootnote( *rNds[GetIndex()] );
1694 }
1695
1696 if( pNewColl && RES_CONDTXTFMTCOLL == pNewColl->Which() )
1697 {
1698 // check the condition of the text node again
1699 ChkCondColl();
1700 }
1701 }
1702
1703 // If positioned exactly at the end of a CharStyle or Hyperlink,
1704 // set its DontExpand flag.
DontExpandFormat(sal_Int32 nIdx,bool bFlag,bool bFormatToTextAttributes)1705 bool SwTextNode::DontExpandFormat( sal_Int32 nIdx, bool bFlag,
1706 bool bFormatToTextAttributes )
1707 {
1708 if (bFormatToTextAttributes && nIdx == m_Text.getLength())
1709 {
1710 FormatToTextAttr( this );
1711 }
1712
1713 bool bRet = false;
1714 if ( HasHints() )
1715 {
1716 m_pSwpHints->SortIfNeedBe();
1717 int nPos = m_pSwpHints->GetLastPosSortedByEnd(nIdx);
1718 for ( ; nPos >= 0; --nPos)
1719 {
1720 SwTextAttr *pTmp = m_pSwpHints->GetSortedByEnd( nPos );
1721 const sal_Int32 *pEnd = pTmp->GetEnd();
1722 if( !pEnd )
1723 continue;
1724 assert( *pEnd <= nIdx );
1725 if( nIdx != *pEnd )
1726 break;
1727 if( bFlag != pTmp->DontExpand() && !pTmp->IsLockExpandFlag()
1728 && *pEnd > pTmp->GetStart())
1729 {
1730 bRet = true;
1731 m_pSwpHints->NoteInHistory( pTmp );
1732 pTmp->SetDontExpand( bFlag );
1733 }
1734 }
1735 }
1736 return bRet;
1737 }
1738
lcl_GetTextAttrDefault(sal_Int32 nIndex,sal_Int32 nHintStart,sal_Int32 nHintEnd)1739 static bool lcl_GetTextAttrDefault(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
1740 {
1741 return ((nHintStart <= nIndex) && (nIndex < nHintEnd));
1742 }
lcl_GetTextAttrExpand(sal_Int32 nIndex,sal_Int32 nHintStart,sal_Int32 nHintEnd)1743 static bool lcl_GetTextAttrExpand(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
1744 {
1745 return ((nHintStart < nIndex) && (nIndex <= nHintEnd));
1746 }
lcl_GetTextAttrParent(sal_Int32 nIndex,sal_Int32 nHintStart,sal_Int32 nHintEnd)1747 static bool lcl_GetTextAttrParent(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
1748 {
1749 return ((nHintStart < nIndex) && (nIndex < nHintEnd));
1750 }
1751
1752 static void
lcl_GetTextAttrs(std::vector<SwTextAttr * > * const pVector,SwTextAttr ** const ppTextAttr,SwpHints const * const pSwpHints,sal_Int32 const nIndex,sal_uInt16 const nWhich,::sw::GetTextAttrMode const eMode)1753 lcl_GetTextAttrs(
1754 std::vector<SwTextAttr *> *const pVector,
1755 SwTextAttr **const ppTextAttr,
1756 SwpHints const *const pSwpHints,
1757 sal_Int32 const nIndex, sal_uInt16 const nWhich,
1758 ::sw::GetTextAttrMode const eMode)
1759 {
1760 assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END);
1761 if (!pSwpHints)
1762 return;
1763 size_t const nSize = pSwpHints->Count();
1764 sal_Int32 nPreviousIndex(0); // index of last hint with nWhich
1765 bool (*pMatchFunc)(sal_Int32, sal_Int32, sal_Int32)=nullptr;
1766 switch (eMode)
1767 {
1768 case ::sw::GetTextAttrMode::Default: pMatchFunc = &lcl_GetTextAttrDefault;
1769 break;
1770 case ::sw::GetTextAttrMode::Expand: pMatchFunc = &lcl_GetTextAttrExpand;
1771 break;
1772 case ::sw::GetTextAttrMode::Parent: pMatchFunc = &lcl_GetTextAttrParent;
1773 break;
1774 default: assert(false);
1775 }
1776
1777 for( size_t i = pSwpHints->GetFirstPosSortedByWhichAndStart(nWhich); i < nSize; ++i )
1778 {
1779 SwTextAttr *const pHint = pSwpHints->GetSortedByWhichAndStart(i);
1780 if (pHint->Which() != nWhich)
1781 break; // hints are sorted by which&start, so we are done...
1782
1783 sal_Int32 const nHintStart = pHint->GetStart();
1784 if (nIndex < nHintStart)
1785 break; // hints are sorted by which&start, so we are done...
1786
1787 sal_Int32 const*const pEndIdx = pHint->GetEnd();
1788 // cannot have hint with no end and no dummy char
1789 assert(pEndIdx || pHint->HasDummyChar());
1790 // If EXPAND is set, simulate the text input behavior, i.e.
1791 // move the start, and expand the end.
1792 bool const bContained( pEndIdx
1793 ? (*pMatchFunc)(nIndex, nHintStart, *pEndIdx)
1794 : (nHintStart == nIndex) );
1795 if (bContained)
1796 {
1797 if (pVector)
1798 {
1799 if (nPreviousIndex < nHintStart)
1800 {
1801 pVector->clear(); // clear hints that are outside pHint
1802 nPreviousIndex = nHintStart;
1803 }
1804 pVector->push_back(pHint);
1805 }
1806 else
1807 {
1808 *ppTextAttr = pHint; // and possibly overwrite outer hint
1809 }
1810 if (!pEndIdx)
1811 {
1812 break;
1813 }
1814 }
1815 }
1816 }
1817
1818 std::vector<SwTextAttr *>
GetTextAttrsAt(sal_Int32 const nIndex,sal_uInt16 const nWhich) const1819 SwTextNode::GetTextAttrsAt(sal_Int32 const nIndex, sal_uInt16 const nWhich) const
1820 {
1821 assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END);
1822 std::vector<SwTextAttr *> ret;
1823 lcl_GetTextAttrs(&ret, nullptr, m_pSwpHints.get(), nIndex, nWhich, ::sw::GetTextAttrMode::Default);
1824 return ret;
1825 }
1826
1827 SwTextAttr *
GetTextAttrAt(sal_Int32 const nIndex,sal_uInt16 const nWhich,::sw::GetTextAttrMode const eMode) const1828 SwTextNode::GetTextAttrAt(sal_Int32 const nIndex, sal_uInt16 const nWhich,
1829 ::sw::GetTextAttrMode const eMode) const
1830 {
1831 assert( (nWhich == RES_TXTATR_META)
1832 || (nWhich == RES_TXTATR_METAFIELD)
1833 || (nWhich == RES_TXTATR_AUTOFMT)
1834 || (nWhich == RES_TXTATR_INETFMT)
1835 || (nWhich == RES_TXTATR_CJK_RUBY)
1836 || (nWhich == RES_TXTATR_UNKNOWN_CONTAINER)
1837 || (nWhich == RES_TXTATR_CONTENTCONTROL)
1838 || (nWhich == RES_TXTATR_INPUTFIELD ) );
1839 // "GetTextAttrAt() will give wrong result for this hint!")
1840
1841 SwTextAttr * pRet(nullptr);
1842 lcl_GetTextAttrs(nullptr, & pRet, m_pSwpHints.get(), nIndex, nWhich, eMode);
1843 return pRet;
1844 }
1845
GetOverlappingInputField(const SwTextAttr & rTextAttr) const1846 const SwTextInputField* SwTextNode::GetOverlappingInputField( const SwTextAttr& rTextAttr ) const
1847 {
1848 const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(rTextAttr.GetStart(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent));
1849
1850 if ( pTextInputField == nullptr && rTextAttr.End() != nullptr )
1851 {
1852 pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(*(rTextAttr.End()), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent));
1853 }
1854
1855 return pTextInputField;
1856 }
1857
DelFrames_TextNodePart()1858 void SwTextNode::DelFrames_TextNodePart()
1859 {
1860 SetWrong( nullptr );
1861 SetWrongDirty(sw::WrongState::TODO);
1862
1863 SetGrammarCheck( nullptr );
1864 SetGrammarCheckDirty( true );
1865
1866 SetSmartTags( nullptr );
1867 SetSmartTagDirty( true );
1868
1869 SetWordCountDirty( true );
1870 SetAutoCompleteWordDirty( true );
1871 }
1872
GetFieldTextAttrAt(const sal_Int32 nIndex,::sw::GetTextAttrMode const eMode) const1873 SwTextField* SwTextNode::GetFieldTextAttrAt(
1874 const sal_Int32 nIndex,
1875 ::sw::GetTextAttrMode const eMode) const
1876 {
1877 SwTextField* pTextField = dynamic_cast<SwTextField*>(GetTextAttrForCharAt( nIndex, RES_TXTATR_FIELD ));
1878 if ( pTextField == nullptr )
1879 {
1880 pTextField = dynamic_cast<SwTextField*>(GetTextAttrForCharAt( nIndex, RES_TXTATR_ANNOTATION ));
1881 }
1882 if ( pTextField == nullptr )
1883 {
1884 pTextField =
1885 dynamic_cast<SwTextField*>( GetTextAttrAt(
1886 nIndex,
1887 RES_TXTATR_INPUTFIELD,
1888 eMode));
1889 }
1890
1891 return pTextField;
1892 }
1893
lcl_FindCharFormat(const SwCharFormats * pCharFormats,const UIName & rName)1894 static SwCharFormat* lcl_FindCharFormat( const SwCharFormats* pCharFormats, const UIName& rName )
1895 {
1896 if( !rName.isEmpty() )
1897 {
1898 const size_t nArrLen = pCharFormats->size();
1899 for( size_t i = 1; i < nArrLen; i++ )
1900 {
1901 SwCharFormat* pFormat = (*pCharFormats)[ i ];
1902 if( pFormat->GetName()==rName )
1903 return pFormat;
1904 }
1905 }
1906 return nullptr;
1907 }
1908
lcl_CopyHint(const sal_uInt16 nWhich,const SwTextAttr * const pHt,SwTextAttr * const pNewHt,SwDoc * const pOtherDoc,SwTextNode * const pDest)1909 static void lcl_CopyHint(
1910 const sal_uInt16 nWhich,
1911 const SwTextAttr * const pHt,
1912 SwTextAttr *const pNewHt,
1913 SwDoc *const pOtherDoc,
1914 SwTextNode *const pDest )
1915 {
1916 assert(nWhich == pHt->Which()); // wrong hint-id
1917 switch( nWhich )
1918 {
1919 // copy nodesarray section with footnote content
1920 case RES_TXTATR_FTN :
1921 assert(pDest); // "lcl_CopyHint: no destination text node?"
1922 static_cast<const SwTextFootnote*>(pHt)->CopyFootnote( *static_cast<SwTextFootnote*>(pNewHt), *pDest);
1923 break;
1924
1925 // Fields that are copied into different SwDocs must be registered
1926 // at their new FieldTypes.
1927
1928 case RES_TXTATR_FIELD :
1929 {
1930 if( pOtherDoc != nullptr )
1931 {
1932 static_txtattr_cast<const SwTextField*>(pHt)->CopyTextField(
1933 static_txtattr_cast<SwTextField*>(pNewHt));
1934 }
1935
1936 // Table Formula must be copied relative.
1937 const SwFormatField& rField = pHt->GetFormatField();
1938 if( SwFieldIds::Table == rField.GetField()->GetTyp()->Which()
1939 && static_cast<const SwTableField*>(rField.GetField())->IsIntrnlName())
1940 {
1941 // convert internal formula to external
1942 const SwTableNode* const pDstTableNd =
1943 static_txtattr_cast<const SwTextField*>(pHt)->GetTextNode().FindTableNode();
1944 if( pDstTableNd )
1945 {
1946 SwTableField* const pTableField =
1947 const_cast<SwTableField*>(static_cast<const SwTableField*>(
1948 pNewHt->GetFormatField().GetField()));
1949 pTableField->PtrToBoxNm( &pDstTableNd->GetTable() );
1950 }
1951 }
1952 }
1953 break;
1954
1955 case RES_TXTATR_INPUTFIELD :
1956 case RES_TXTATR_ANNOTATION :
1957 if( pOtherDoc != nullptr )
1958 {
1959 static_txtattr_cast<const SwTextField*>(pHt)->CopyTextField(
1960 static_txtattr_cast<SwTextField*>(pNewHt));
1961 }
1962 break;
1963
1964 case RES_TXTATR_TOXMARK :
1965 if( pOtherDoc && pDest && pDest->GetpSwpHints()
1966 && pDest->GetpSwpHints()->Contains( pNewHt ) )
1967 {
1968 // ToXMarks that are copied to different SwDocs must register
1969 // at their new ToX (sw::BroadcastingModify).
1970 static_txtattr_cast<SwTextTOXMark*>(pNewHt)->CopyTOXMark(*pOtherDoc);
1971 }
1972 break;
1973
1974 case RES_TXTATR_CHARFMT :
1975 // For CharacterStyles, the format must be copied too.
1976 if( pDest && pDest->GetpSwpHints()
1977 && pDest->GetpSwpHints()->Contains( pNewHt ) )
1978 {
1979 SwCharFormat* pFormat = pHt->GetCharFormat().GetCharFormat();
1980
1981 if (pOtherDoc)
1982 {
1983 pFormat = pOtherDoc->CopyCharFormat( *pFormat );
1984 }
1985 const_cast<SwFormatCharFormat&>(
1986 pNewHt->GetCharFormat() ).SetCharFormat( pFormat );
1987 }
1988 break;
1989 case RES_TXTATR_INETFMT :
1990 {
1991 // For Hyperlinks, the format must be copied too.
1992 if( pOtherDoc && pDest && pDest->GetpSwpHints()
1993 && pDest->GetpSwpHints()->Contains( pNewHt ) )
1994 {
1995 const SwDoc& rDoc = static_txtattr_cast<
1996 const SwTextINetFormat*>(pHt)->GetTextNode().GetDoc();
1997 const SwCharFormats* pCharFormats = rDoc.GetCharFormats();
1998 const SwFormatINetFormat& rFormat = pHt->GetINetFormat();
1999 SwCharFormat* pFormat;
2000 pFormat = lcl_FindCharFormat( pCharFormats, rFormat.GetINetFormat() );
2001 if( pFormat )
2002 pOtherDoc->CopyCharFormat( *pFormat );
2003 pFormat = lcl_FindCharFormat( pCharFormats, rFormat.GetVisitedFormat() );
2004 if( pFormat )
2005 pOtherDoc->CopyCharFormat( *pFormat );
2006 }
2007 //JP 24.04.98: The attribute must point to a text node, so that
2008 // the styles can be created.
2009 SwTextINetFormat *const pINetHt = static_txtattr_cast<SwTextINetFormat*>(pNewHt);
2010 if ( !pINetHt->GetpTextNode() )
2011 {
2012 pINetHt->ChgTextNode( pDest );
2013 }
2014
2015 //JP 22.10.97: set up link to char style
2016 pINetHt->GetCharFormat();
2017 break;
2018 }
2019 case RES_TXTATR_META:
2020 case RES_TXTATR_METAFIELD:
2021 OSL_ENSURE( pNewHt, "copying Meta should not fail!" );
2022 OSL_ENSURE( pDest && pNewHt
2023 && (CH_TXTATR_INWORD == pDest->GetText()[pNewHt->GetStart()]),
2024 "missing CH_TXTATR?");
2025 break;
2026 }
2027 }
2028
2029 /// copy attributes at position nTextStartIdx to node pDest
2030 // BP 7.6.93: Intentionally copy only attributes _with_ EndIdx!
2031 // CopyAttr is usually called when attributes are set on a
2032 // node with no text.
CopyAttr(SwTextNode * pDest,const sal_Int32 nTextStartIdx,const sal_Int32 nOldPos)2033 void SwTextNode::CopyAttr( SwTextNode *pDest, const sal_Int32 nTextStartIdx,
2034 const sal_Int32 nOldPos )
2035 {
2036 if ( HasHints() )
2037 {
2038 SwDoc* const pOtherDoc = (&pDest->GetDoc() != &GetDoc()) ?
2039 &pDest->GetDoc() : nullptr;
2040
2041 for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
2042 {
2043 SwTextAttr *const pHt = m_pSwpHints->Get(i);
2044 sal_Int32 const nAttrStartIdx = pHt->GetStart();
2045 if ( nTextStartIdx < nAttrStartIdx )
2046 break; // beyond end of text, because nLen == 0
2047
2048 const sal_Int32 *const pEndIdx = pHt->GetEnd();
2049 if ( pEndIdx && !pHt->HasDummyChar() )
2050 {
2051 sal_uInt16 const nWhich = pHt->Which();
2052 if (RES_TXTATR_INPUTFIELD != nWhich // fdo#74981 skip fields
2053 && ( *pEndIdx > nTextStartIdx
2054 || (*pEndIdx == nTextStartIdx
2055 && nAttrStartIdx == nTextStartIdx)))
2056 {
2057 if ( RES_TXTATR_REFMARK != nWhich )
2058 {
2059 // attribute in the area => copy
2060 SwTextAttr *const pNewHt =
2061 pDest->InsertItem( pHt->GetAttr(), nOldPos, nOldPos, SetAttrMode::IS_COPY);
2062 if ( pNewHt )
2063 {
2064 lcl_CopyHint( nWhich, pHt, pNewHt,
2065 pOtherDoc, pDest );
2066 }
2067 }
2068 else if( !pOtherDoc
2069 ? GetDoc().IsCopyIsMove()
2070 : nullptr == pOtherDoc->GetRefMark( pHt->GetRefMark().GetRefName() ) )
2071 {
2072 pDest->InsertItem(
2073 pHt->GetAttr(), nOldPos, nOldPos, SetAttrMode::IS_COPY);
2074 }
2075 }
2076 }
2077 }
2078 }
2079
2080 if( this != pDest )
2081 {
2082 // notify layout frames, to prevent disappearance of footnote numbers
2083 SwUpdateAttr aHint(
2084 nOldPos,
2085 nOldPos,
2086 0);
2087
2088 pDest->TriggerNodeUpdate(sw::UpdateAttrHint(&aHint, &aHint));
2089 }
2090 }
2091
2092 /// copy text and attributes to node pDest
CopyText(SwTextNode * const pDest,const SwContentIndex & rStart,const sal_Int32 nLen,const bool bForceCopyOfAllAttrs)2093 void SwTextNode::CopyText( SwTextNode *const pDest,
2094 const SwContentIndex &rStart,
2095 const sal_Int32 nLen,
2096 const bool bForceCopyOfAllAttrs )
2097 {
2098 SwContentIndex const aIdx( pDest, pDest->m_Text.getLength() );
2099 CopyText( pDest, aIdx, rStart, nLen, bForceCopyOfAllAttrs );
2100 }
2101
CopyText(SwTextNode * const pDest,const SwContentIndex & rDestStart,const SwPosition & rStart,sal_Int32 nLen,const bool bForceCopyOfAllAttrs)2102 void SwTextNode::CopyText( SwTextNode *const pDest,
2103 const SwContentIndex &rDestStart,
2104 const SwPosition &rStart,
2105 sal_Int32 nLen,
2106 const bool bForceCopyOfAllAttrs )
2107 {
2108 CopyText( pDest, rDestStart, rStart.nContent, nLen, bForceCopyOfAllAttrs );
2109 }
2110
EstablishParentChildRelationsOfComments(const SwTextNode * pDest,std::map<sal_Int32,sal_Int32> & idMapForComments,std::map<sal_Int32,SwMarkName> & nameMapForComments)2111 void SwTextNode::EstablishParentChildRelationsOfComments(
2112 const SwTextNode* pDest,
2113 std::map<sal_Int32, sal_Int32>& idMapForComments,
2114 std::map<sal_Int32, SwMarkName>& nameMapForComments
2115 )
2116 {
2117 if (idMapForComments.size() > 0)
2118 {
2119 const SwpHints &rDestHints = pDest->GetSwpHints();
2120 size_t hintCount = rDestHints.Count();
2121 for (size_t inDest = 0; inDest < hintCount; inDest++)
2122 {
2123 if (rDestHints.Get(inDest)->Which() == RES_TXTATR_ANNOTATION)
2124 {
2125 SwPostItField* copiedField = const_cast<SwPostItField*>(static_cast<const SwPostItField*>(rDestHints.Get(inDest)->GetFormatField().GetField()));
2126 if (copiedField && copiedField->GetParentPostItId() != 0)
2127 {
2128 const auto correspondingParentItem = idMapForComments.find(copiedField->GetParentPostItId());
2129 if (correspondingParentItem != idMapForComments.end())
2130 {
2131 copiedField->SetParentName(nameMapForComments[copiedField->GetParentPostItId()]); // Set name first, parent id will change.
2132 copiedField->SetParentPostItId(correspondingParentItem->second);
2133 }
2134 }
2135 }
2136 }
2137 }
2138 }
2139
CopyText(SwTextNode * const pDest,const SwContentIndex & rDestStart,const SwContentIndex & rStart,sal_Int32 nLen,const bool bForceCopyOfAllAttrs)2140 void SwTextNode::CopyText( SwTextNode *const pDest,
2141 const SwContentIndex &rDestStart,
2142 const SwContentIndex &rStart,
2143 sal_Int32 nLen,
2144 const bool bForceCopyOfAllAttrs )
2145 {
2146 CHECK_SWPHINTS_IF_FRM(this);
2147 CHECK_SWPHINTS(pDest);
2148 assert(rDestStart.GetContentNode() == pDest);
2149 assert(rStart.GetContentNode() == this);
2150 sal_Int32 nTextStartIdx = rStart.GetIndex();
2151 sal_Int32 nDestStart = rDestStart.GetIndex(); // remember old Pos
2152
2153 if (pDest->GetDoc().IsClipBoard() && GetNum())
2154 {
2155 // #i111677# cache expansion of source (for clipboard)
2156 pDest->m_oNumStringCache = (nTextStartIdx != 0)
2157 ? OUString() // fdo#49076: numbering only if copy from para start
2158 : GetNumString();
2159 }
2160
2161 if( !nLen )
2162 {
2163 // if no length is given, copy attributes at position rStart
2164 CopyAttr( pDest, nTextStartIdx, nDestStart );
2165
2166 // copy hard attributes on whole paragraph
2167 if( HasSwAttrSet() )
2168 {
2169 // i#96213 all or just the Char attributes?
2170 if ( !bForceCopyOfAllAttrs &&
2171 ( nDestStart ||
2172 pDest->HasSwAttrSet() ||
2173 nLen != pDest->GetText().getLength()))
2174 {
2175 SfxItemSetFixed<
2176 RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
2177 RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT,
2178 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
2179 aCharSet( pDest->GetDoc().GetAttrPool() );
2180 aCharSet.Put( *GetpSwAttrSet() );
2181 if( aCharSet.Count() )
2182 {
2183 pDest->SetAttr( aCharSet, nDestStart, nDestStart );
2184 }
2185 }
2186 else
2187 {
2188 GetpSwAttrSet()->CopyToModify( *pDest );
2189 }
2190 }
2191 return;
2192 }
2193
2194 // 1. copy text
2195 const sal_Int32 oldLen = pDest->m_Text.getLength();
2196 // JP 15.02.96: missing attribute handling at the end!
2197 // hence call InsertText and don't modify m_Text directly
2198 pDest->InsertText( m_Text.copy(nTextStartIdx, nLen), rDestStart,
2199 SwInsertFlags::EMPTYEXPAND );
2200
2201 // update with actual new size
2202 nLen = pDest->m_Text.getLength() - oldLen;
2203 if ( !nLen ) // string not longer?
2204 return;
2205
2206 SwDoc* const pOtherDoc = (&pDest->GetDoc() != &GetDoc()) ? &pDest->GetDoc() : nullptr;
2207
2208 // copy hard attributes on whole paragraph
2209 if( HasSwAttrSet() )
2210 {
2211 // i#96213 all or just the Char attributes?
2212 if ( !bForceCopyOfAllAttrs &&
2213 ( nDestStart ||
2214 pDest->HasSwAttrSet() ||
2215 nLen != pDest->GetText().getLength()))
2216 {
2217 SfxItemSetFixed<
2218 RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
2219 RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT,
2220 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
2221 aCharSet( pDest->GetDoc().GetAttrPool() );
2222 aCharSet.Put( *GetpSwAttrSet() );
2223 if( aCharSet.Count() )
2224 {
2225 pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen );
2226 }
2227 }
2228 else
2229 {
2230 GetpSwAttrSet()->CopyToModify( *pDest );
2231 }
2232 }
2233
2234 bool const bUndoNodes = !pOtherDoc
2235 && GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
2236
2237 // Fetch end only now, because copying into self updates the start index
2238 // and all attributes
2239 nTextStartIdx = rStart.GetIndex();
2240 const sal_Int32 nEnd = nTextStartIdx + nLen;
2241
2242 // 2. copy attributes
2243 // Iterate over attribute array until the start of the attribute
2244 // is behind the copied range
2245 const size_t nSize = m_pSwpHints ? m_pSwpHints->Count() : 0;
2246
2247 // If copying into self, inserting can delete attributes!
2248 // Hence first copy into temp-array, and then move that into hints array.
2249 SwpHts aArr;
2250
2251 // Del-Array for all RefMarks without extent
2252 SwpHts aRefMrkArr;
2253
2254 std::vector<std::pair<sal_Int32, sal_Int32>> metaFieldRanges;
2255
2256 /*
2257 Annotations are also copied along with other fields.
2258 Annotations have parentPostItId field, used for parent-child relation.
2259 So we also need to set parent ids of comments when applicable.
2260 Below map variable is for memorizing the new ids and names of parent postits in the source node, then we will use them in target node.
2261 */
2262 std::map<sal_Int32, sal_Int32> idMapForComments;
2263 std::map<sal_Int32, SwMarkName> nameMapForComments;
2264
2265 sal_Int32 nDeletedDummyChars(0);
2266 for (size_t n = 0; n < nSize; ++n)
2267 {
2268 SwTextAttr * const pHt = m_pSwpHints->Get(n);
2269
2270 const sal_Int32 nAttrStartIdx = pHt->GetStart();
2271 if ( nAttrStartIdx >= nEnd )
2272 break;
2273
2274 const sal_Int32 * const pEndIdx = pHt->GetEnd();
2275 const sal_uInt16 nWhich = pHt->Which();
2276
2277 // JP 26.04.94: RefMarks are never copied. If the refmark doesn't have
2278 // an extent, there is a dummy char in the text, which
2279 // must be removed. So we first copy the attribute,
2280 // but remember it, and when we're done delete it,
2281 // which also deletes the dummy character!
2282 // JP 14.08.95: May RefMarks be moved?
2283 const bool bCopyRefMark = RES_TXTATR_REFMARK == nWhich
2284 && ( bUndoNodes
2285 || ( !pOtherDoc
2286 ? GetDoc().IsCopyIsMove()
2287 : nullptr == pOtherDoc->GetRefMark( pHt->GetRefMark().GetRefName() ) ) );
2288
2289 if ( pEndIdx
2290 && RES_TXTATR_REFMARK == nWhich
2291 && !bCopyRefMark )
2292 {
2293 continue;
2294 }
2295
2296 // Input Fields are only copied, if completely covered by copied text
2297 if ( nWhich == RES_TXTATR_INPUTFIELD )
2298 {
2299 assert(pEndIdx != nullptr &&
2300 "<SwTextNode::CopyText(..)> - RES_TXTATR_INPUTFIELD without EndIndex!" );
2301 if ( nAttrStartIdx < nTextStartIdx
2302 || ( pEndIdx != nullptr
2303 && *pEndIdx > nEnd ) )
2304 {
2305 continue;
2306 }
2307 }
2308
2309 if (nWhich == RES_TXTATR_METAFIELD)
2310 {
2311 // Skip metadata fields. Also remember the range to strip the text later.
2312 metaFieldRanges.emplace_back(nAttrStartIdx, pEndIdx ? *pEndIdx : nEnd);
2313 continue;
2314 }
2315
2316 sal_Int32 nAttrStt = 0;
2317 sal_Int32 nAttrEnd = 0;
2318
2319 if( nAttrStartIdx < nTextStartIdx )
2320 {
2321 // start is before selection
2322 // copy hints with end and CH_TXTATR only if dummy char is copied
2323 if ( pEndIdx && (*pEndIdx > nTextStartIdx) && !pHt->HasDummyChar() )
2324 {
2325 // attribute with extent and the end is in the selection
2326 nAttrStt = nDestStart;
2327 nAttrEnd = (*pEndIdx > nEnd)
2328 ? rDestStart.GetIndex()
2329 : nDestStart + (*pEndIdx) - nTextStartIdx;
2330 }
2331 else
2332 {
2333 continue;
2334 }
2335 }
2336 else
2337 {
2338 // start is in the selection
2339 nAttrStt = nDestStart + ( nAttrStartIdx - nTextStartIdx );
2340 if( pEndIdx )
2341 {
2342 nAttrEnd = *pEndIdx > nEnd
2343 ? rDestStart.GetIndex()
2344 : nDestStart + ( *pEndIdx - nTextStartIdx );
2345 }
2346 else
2347 {
2348 nAttrEnd = nAttrStt;
2349 }
2350 }
2351
2352 SwTextAttr * pNewHt = nullptr;
2353
2354 if( pDest == this )
2355 {
2356 // copy the hint here, but insert it later
2357 pNewHt = MakeTextAttr( GetDoc(), pHt->GetAttr(),
2358 nAttrStt, nAttrEnd, CopyOrNewType::Copy, pDest );
2359
2360 lcl_CopyHint(nWhich, pHt, pNewHt, nullptr, pDest);
2361 aArr.push_back( pNewHt );
2362 }
2363 else
2364 {
2365 pNewHt = pDest->InsertItem(
2366 pHt->GetAttr(),
2367 nAttrStt - nDeletedDummyChars,
2368 nAttrEnd - nDeletedDummyChars,
2369 SetAttrMode::NOTXTATRCHR | SetAttrMode::IS_COPY);
2370 if (pNewHt)
2371 {
2372 lcl_CopyHint( nWhich, pHt, pNewHt, pOtherDoc, pDest );
2373 if (nWhich == RES_TXTATR_ANNOTATION)
2374 {
2375 const SwPostItField* annotationField = static_cast<const SwPostItField*>(pHt->GetFormatField().GetField());
2376 // Preparation for EstablishParentChildRelationsOfComments.
2377 idMapForComments[annotationField->GetPostItId()] = static_cast<const SwPostItField*>(pNewHt->GetFormatField().GetField())->GetPostItId();
2378 nameMapForComments[annotationField->GetPostItId()] = static_cast<const SwPostItField*>(pNewHt->GetFormatField().GetField())->GetName();
2379 }
2380 }
2381 else if (pHt->HasDummyChar())
2382 {
2383 // The attribute that has failed to be copied would insert
2384 // dummy char, so positions of the following attributes have
2385 // to be shifted by one to compensate for that missing char.
2386 ++nDeletedDummyChars;
2387 }
2388 }
2389
2390 if( RES_TXTATR_REFMARK == nWhich && !pEndIdx && !bCopyRefMark )
2391 {
2392 aRefMrkArr.push_back( pNewHt );
2393 }
2394 }
2395
2396 // Strip the metadata fields, since we don't copy the RDF entries
2397 // yet and so they are inconsistent upon copy/pasting.
2398 if (!metaFieldRanges.empty())
2399 {
2400 // Reverse to remove without messing the offsets.
2401 std::reverse(metaFieldRanges.begin(), metaFieldRanges.end());
2402 for (const auto& pair : metaFieldRanges)
2403 {
2404 const SwContentIndex aIdx(pDest, pair.first);
2405 pDest->EraseText(aIdx, pair.second - pair.first);
2406 }
2407 }
2408
2409 EstablishParentChildRelationsOfComments(pDest, idMapForComments, nameMapForComments);
2410
2411 // this can only happen when copying into self
2412 for (SwTextAttr* i : aArr)
2413 {
2414 InsertHint( i, SetAttrMode::NOTXTATRCHR );
2415 }
2416
2417 if( pDest->GetpSwpHints() )
2418 {
2419 for (SwTextAttr* pNewHt : aRefMrkArr)
2420 {
2421 if( pNewHt->GetEnd() )
2422 {
2423 pDest->GetpSwpHints()->Delete( pNewHt );
2424 pDest->DestroyAttr( pNewHt );
2425 }
2426 else
2427 {
2428 const SwContentIndex aIdx( pDest, pNewHt->GetStart() );
2429 pDest->EraseText( aIdx, 1 );
2430 }
2431 }
2432 }
2433
2434 CHECK_SWPHINTS_IF_FRM(this);
2435 CHECK_SWPHINTS(pDest);
2436 }
2437
InsertText(const OUString & rStr,const SwPosition & rIdx,const SwInsertFlags nMode)2438 OUString SwTextNode::InsertText( const OUString & rStr, const SwPosition & rIdx,
2439 const SwInsertFlags nMode )
2440 {
2441 return InsertText(rStr, rIdx.nContent, nMode);
2442 }
2443
InsertText(const OUString & rStr,const SwContentIndex & rIdx,const SwInsertFlags nMode)2444 OUString SwTextNode::InsertText( const OUString & rStr, const SwContentIndex & rIdx,
2445 const SwInsertFlags nMode )
2446 {
2447 assert(rIdx.GetContentNode() == this);
2448 assert(rIdx <= m_Text.getLength()); // invalid index
2449
2450 const sal_Int32 aPos = rIdx.GetIndex();
2451 sal_Int32 nLen = m_Text.getLength() - aPos;
2452 sal_Int32 const nOverflow(rStr.getLength() - GetSpaceLeft());
2453 SAL_WARN_IF(nOverflow > 0, "sw.core",
2454 "SwTextNode::InsertText: node text with insertion > capacity.");
2455 OUString const sInserted(
2456 (nOverflow > 0) ? rStr.copy(0, rStr.getLength() - nOverflow) : rStr);
2457 if (sInserted.isEmpty())
2458 {
2459 return sInserted;
2460 }
2461 if (aPos == 0 && m_Text.isEmpty())
2462 m_Text = sInserted;
2463 else
2464 m_Text = m_Text.replaceAt(aPos, 0, sInserted);
2465 assert(GetSpaceLeft()>=0);
2466 nLen = m_Text.getLength() - aPos - nLen;
2467 assert(nLen != 0);
2468
2469 bool bOldExpFlg = IsIgnoreDontExpand();
2470 if (nMode & SwInsertFlags::FORCEHINTEXPAND)
2471 {
2472 SetIgnoreDontExpand( true );
2473 }
2474
2475 Update(rIdx, nLen, UpdateMode::Default); // text content changed!
2476
2477 if (nMode & SwInsertFlags::FORCEHINTEXPAND)
2478 {
2479 SetIgnoreDontExpand( bOldExpFlg );
2480 }
2481
2482 if ( HasWriterListeners() )
2483 { // send this before messing with hints, which will send RES_UPDATE_ATTR
2484 auto aInsHint = sw::MakeInsertText(*this, aPos, nLen);
2485 CallSwClientNotify(aInsHint);
2486 }
2487
2488 if ( HasHints() )
2489 {
2490 m_pSwpHints->SortIfNeedBe();
2491 bool const bHadHints(!m_pSwpHints->CanBeDeleted());
2492 bool bMergePortionsNeeded(false);
2493 for ( size_t i = 0; i < m_pSwpHints->Count() &&
2494 rIdx >= m_pSwpHints->GetWithoutResorting(i)->GetStart(); ++i )
2495 {
2496 SwTextAttr * const pHt = m_pSwpHints->GetWithoutResorting( i );
2497 const sal_Int32 * const pEndIdx = pHt->GetEnd();
2498 if( !pEndIdx )
2499 continue;
2500
2501 if( rIdx == *pEndIdx )
2502 {
2503 if ( (nMode & SwInsertFlags::NOHINTEXPAND) ||
2504 (!(nMode & SwInsertFlags::FORCEHINTEXPAND)
2505 && pHt->DontExpand()) )
2506 {
2507 m_pSwpHints->DeleteAtPos(i);
2508 // on empty attributes also adjust Start
2509 if( rIdx == pHt->GetStart() )
2510 pHt->SetStart( pHt->GetStart() - nLen );
2511 pHt->SetEnd(*pEndIdx - nLen);
2512 // could be that pHt has IsFormatIgnoreEnd set, and it's
2513 // not a RSID-only hint - now we have the inserted text
2514 // between pHt and its continuation... which we don't know.
2515 // punt the job to MergePortions below.
2516 if (pHt->IsFormatIgnoreEnd())
2517 {
2518 bMergePortionsNeeded = true;
2519 }
2520 InsertHint( pHt, SetAttrMode::NOHINTADJUST );
2521 }
2522 // empty hints at insert position?
2523 else if ( (nMode & SwInsertFlags::EMPTYEXPAND)
2524 && (*pEndIdx == pHt->GetStart()) )
2525 {
2526 m_pSwpHints->DeleteAtPos(i);
2527 pHt->SetStart( pHt->GetStart() - nLen );
2528 const size_t nCurrentLen = m_pSwpHints->Count();
2529 InsertHint( pHt/* AUTOSTYLES:, SetAttrMode::NOHINTADJUST*/ );
2530 if ( nCurrentLen > m_pSwpHints->Count() && i )
2531 {
2532 --i;
2533 }
2534 continue;
2535 }
2536 else
2537 {
2538 continue;
2539 }
2540 }
2541 if ( !(nMode & SwInsertFlags::NOHINTEXPAND) &&
2542 rIdx == nLen && pHt->GetStart() == rIdx.GetIndex() &&
2543 !pHt->IsDontExpandStartAttr() )
2544 {
2545 // no field, at paragraph start, HintExpand
2546 m_pSwpHints->DeleteAtPos(i);
2547 pHt->SetStart( pHt->GetStart() - nLen );
2548 // no effect on format ignore flags here (para start)
2549 InsertHint( pHt, SetAttrMode::NOHINTADJUST );
2550 }
2551 }
2552 if (bMergePortionsNeeded)
2553 {
2554 m_pSwpHints->MergePortions(*this);
2555 }
2556 SAL_WARN_IF(bHadHints && m_pSwpHints->CanBeDeleted(), "sw.core",
2557 "SwTextNode::InsertText: unexpected loss of hints");
2558 }
2559
2560 // By inserting a character, the hidden flags
2561 // at the TextNode can become invalid:
2562 SetCalcHiddenCharFlags();
2563
2564 CHECK_SWPHINTS(this);
2565 return sInserted;
2566 }
2567
CutText(SwTextNode * const pDest,const SwContentIndex & rStart,const sal_Int32 nLen)2568 void SwTextNode::CutText( SwTextNode * const pDest,
2569 const SwContentIndex & rStart, const sal_Int32 nLen )
2570 {
2571 assert(pDest); // Cut requires a destination
2572 SwContentIndex aDestStt(pDest, pDest->GetText().getLength());
2573 CutImpl( pDest, aDestStt, rStart, nLen, false );
2574 }
2575
CutImpl(SwTextNode * const pDest,const SwContentIndex & rDestStart,const SwContentIndex & rStart,sal_Int32 nLen,const bool bUpdate)2576 void SwTextNode::CutImpl( SwTextNode * const pDest, const SwContentIndex & rDestStart,
2577 const SwContentIndex & rStart, sal_Int32 nLen, const bool bUpdate )
2578 {
2579 assert(pDest); // Cut requires a destination
2580
2581 assert(&GetDoc() == &pDest->GetDoc()); // must be same document
2582
2583 assert(pDest != this); // destination must be different node
2584
2585 assert(rDestStart.GetContentNode() == pDest);
2586 assert(rStart.GetContentNode() == this);
2587
2588 if( !nLen )
2589 {
2590 // if no length is given, copy attributes at position rStart
2591 CopyAttr( pDest, rStart.GetIndex(), rDestStart.GetIndex() );
2592 return;
2593 }
2594
2595 sal_Int32 nTextStartIdx = rStart.GetIndex();
2596 sal_Int32 nDestStart = rDestStart.GetIndex(); // remember old Pos
2597 const sal_Int32 nInitSize = pDest->m_Text.getLength();
2598
2599 if (pDest->GetSpaceLeft() < nLen)
2600 { // FIXME: could only happen when called from SwRangeRedline::Show.
2601 // unfortunately can't really do anything here to handle that...
2602 abort();
2603 }
2604 pDest->m_Text = pDest->m_Text.replaceAt(nDestStart, 0,
2605 m_Text.subView(nTextStartIdx, nLen));
2606 OUString const newText = m_Text.replaceAt(nTextStartIdx, nLen, u"");
2607 nLen = pDest->m_Text.getLength() - nInitSize; // update w/ current size!
2608 if (!nLen) // String didn't grow?
2609 return;
2610
2611 if (bUpdate)
2612 {
2613 // Update all SwContentIndex
2614 pDest->Update(rDestStart, nLen, UpdateMode::Default);
2615 }
2616
2617 CHECK_SWPHINTS(pDest);
2618
2619 const sal_Int32 nEnd = rStart.GetIndex() + nLen;
2620 bool const bUndoNodes =
2621 GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
2622
2623 // copy hard attributes on whole paragraph
2624 if (HasSwAttrSet())
2625 {
2626 bool hasSwAttrSet = pDest->HasSwAttrSet();
2627 if (hasSwAttrSet)
2628 {
2629 // if we have our own property set it doesn't mean
2630 // that this set defines any style different to Standard one.
2631 hasSwAttrSet = false;
2632
2633 // so, let's check deeper if property set has defined any property
2634 if (pDest->GetpSwAttrSet())
2635 {
2636 // check all items in the property set
2637 SfxItemIter aIter( *pDest->GetpSwAttrSet() );
2638 const SfxPoolItem* pItem = aIter.GetCurItem();
2639 do
2640 {
2641 // check current item
2642 const sal_uInt16 nWhich = IsInvalidItem( pItem )
2643 ? aIter.GetCurWhich()
2644 : pItem->Which();
2645 if( RES_FRMATR_STYLE_NAME != nWhich &&
2646 RES_FRMATR_CONDITIONAL_STYLE_NAME != nWhich &&
2647 RES_PAGEDESC != nWhich &&
2648 RES_BREAK != nWhich &&
2649 SfxItemState::SET == pDest->GetpSwAttrSet()->GetItemState( nWhich, false ) )
2650 {
2651 // check if parent value (original value in style) has the same value as in [pItem]
2652 const SfxPoolItem& rParentItem = pDest->GetpSwAttrSet()->GetParent()->Get( nWhich, true );
2653
2654 hasSwAttrSet = (rParentItem != *pItem);
2655
2656 // property set is not empty => no need to make anymore checks
2657 if (hasSwAttrSet)
2658 break;
2659 }
2660
2661 // let's check next item
2662 pItem = aIter.NextItem();
2663 } while (pItem);
2664 }
2665 }
2666
2667 // all or just the Char attributes?
2668 if( nInitSize || hasSwAttrSet ||
2669 nLen != pDest->GetText().getLength())
2670 {
2671 SfxItemSetFixed<
2672 RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
2673 RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT,
2674 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
2675 aCharSet( pDest->GetDoc().GetAttrPool() );
2676 aCharSet.Put( *GetpSwAttrSet() );
2677 if( aCharSet.Count() )
2678 pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen );
2679 }
2680 else
2681 {
2682 // Copy all attrs except RES_PARATR_LIST_LEVEL: it was initialized before
2683 // and current SwTextNode can contain not suitable for pDest value
2684 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_LEVEL - 1,
2685 RES_PARATR_LIST_LEVEL + 1, HINT_END>
2686 aCharSet(pDest->GetDoc().GetAttrPool());
2687 aCharSet.Put(*GetpSwAttrSet());
2688 if (aCharSet.Count())
2689 pDest->SetAttr(aCharSet, nDestStart, nDestStart + nLen);
2690 }
2691 }
2692
2693 // notify frames - before moving hints, because footnotes
2694 // want to find their anchor text frame in the follow chain
2695 // (also ignore fieldmarks, the caller will recreate frames)
2696 const sw::InsertText aInsHint(nDestStart, nLen, false, false);
2697 pDest->HandleNonLegacyHint(aInsHint);
2698 const sw::MoveText aMoveHint(pDest, nDestStart, nTextStartIdx, nLen);
2699 CallSwClientNotify(aMoveHint);
2700 const sw::DeleteText aDelText(nTextStartIdx, nLen);
2701 HandleNonLegacyHint(aDelText);
2702
2703 // 2. move attributes
2704 // Iterate over attribute array until the start of the attribute
2705 // is behind the moved range
2706 bool bMergePortionsNeeded(false);
2707 size_t nAttrCnt = 0;
2708 while (m_pSwpHints && (nAttrCnt < m_pSwpHints->Count()))
2709 {
2710 SwTextAttr * const pHt = m_pSwpHints->Get(nAttrCnt);
2711 const sal_Int32 nAttrStartIdx = pHt->GetStart();
2712 if ( nAttrStartIdx >= nEnd )
2713 break;
2714 const sal_Int32 * const pEndIdx = pHt->GetEnd();
2715 const sal_uInt16 nWhich = pHt->Which();
2716 SwTextAttr *pNewHt = nullptr;
2717
2718 // if the hint has a dummy character, then it must not be split!
2719 if(nAttrStartIdx < nTextStartIdx)
2720 {
2721 // start is before the range
2722 if (!pHt->HasDummyChar() && ( RES_TXTATR_REFMARK != nWhich
2723 || bUndoNodes ) && pEndIdx && *pEndIdx > nTextStartIdx)
2724 {
2725 // attribute with extent and end of attribute is in the range
2726 pNewHt = MakeTextAttr( pDest->GetDoc(), pHt->GetAttr(),
2727 nDestStart,
2728 nDestStart + (
2729 *pEndIdx > nEnd
2730 ? nLen
2731 : *pEndIdx - nTextStartIdx ) );
2732 }
2733 }
2734 else
2735 {
2736 // start is inside the range
2737 if (!pEndIdx || *pEndIdx < nEnd ||
2738 (!bUndoNodes && RES_TXTATR_REFMARK == nWhich)
2739 || pHt->HasDummyChar() )
2740 {
2741 // do not delete note and later add it -> sidebar flickering
2742 SwDocShell* pShell = GetDoc().GetDocShell();
2743 if (pShell)
2744 {
2745 pShell->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation));
2746 }
2747 // move attribute
2748 m_pSwpHints->Delete( pHt );
2749 // reset start/end indexes
2750 if (pHt->IsFormatIgnoreStart() || pHt->IsFormatIgnoreEnd())
2751 {
2752 bMergePortionsNeeded = true;
2753 }
2754 pHt->SetStart(nDestStart + (nAttrStartIdx - nTextStartIdx));
2755 if (pEndIdx)
2756 {
2757 pHt->SetEnd( nDestStart + (
2758 *pEndIdx > nEnd
2759 ? nLen
2760 : *pEndIdx - nTextStartIdx ) );
2761 }
2762 pDest->InsertHint( pHt,
2763 SetAttrMode::NOTXTATRCHR
2764 | SetAttrMode::DONTREPLACE );
2765 if (pShell)
2766 {
2767 pShell->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation));
2768 }
2769 continue; // iterate while loop, no ++ !
2770 }
2771 // the end is behind the range
2772 else if (RES_TXTATR_REFMARK != nWhich || bUndoNodes)
2773 {
2774 pNewHt = MakeTextAttr( GetDoc(), pHt->GetAttr(),
2775 nDestStart + (nAttrStartIdx - nTextStartIdx),
2776 nDestStart + (*pEndIdx > nEnd
2777 ? nLen
2778 : *pEndIdx - nTextStartIdx));
2779 }
2780 }
2781 if (pNewHt)
2782 {
2783 const bool bSuccess( pDest->InsertHint( pNewHt,
2784 SetAttrMode::NOTXTATRCHR
2785 | SetAttrMode::DONTREPLACE
2786 | SetAttrMode::IS_COPY) );
2787 if (bSuccess)
2788 {
2789 lcl_CopyHint( nWhich, pHt, pNewHt, nullptr, pDest );
2790 }
2791 }
2792 ++nAttrCnt;
2793 }
2794 // If there are still empty attributes around, they have a higher priority
2795 // than any attributes that become empty due to the move.
2796 // So temporarily remove them and Update the array, then re-insert the
2797 // removed ones so they overwrite the others.
2798 if (m_pSwpHints && nAttrCnt < m_pSwpHints->Count())
2799 {
2800 SwpHts aArr;
2801 while (nAttrCnt < m_pSwpHints->Count())
2802 {
2803 SwTextAttr * const pHt = m_pSwpHints->Get(nAttrCnt);
2804 if (nEnd != pHt->GetStart())
2805 break;
2806 const sal_Int32 * const pEndIdx = pHt->GetEnd();
2807 if (pEndIdx && *pEndIdx == nEnd)
2808 {
2809 aArr.push_back( pHt );
2810 m_pSwpHints->Delete( pHt );
2811 }
2812 else
2813 {
2814 ++nAttrCnt;
2815 }
2816 }
2817 Update(rStart, nLen, UpdateMode::Negative|UpdateMode::Delete);
2818
2819 for (SwTextAttr* pHt : aArr)
2820 {
2821 pHt->SetStart( rStart.GetIndex() );
2822 pHt->SetEnd( rStart.GetIndex() );
2823 InsertHint( pHt );
2824 }
2825 }
2826 else
2827 {
2828 Update(rStart, nLen, UpdateMode::Negative|UpdateMode::Delete);
2829 }
2830
2831 // set after moving hints
2832 m_Text = newText;
2833
2834 if (bMergePortionsNeeded)
2835 {
2836 m_pSwpHints->MergePortions(*this);
2837 }
2838
2839 CHECK_SWPHINTS(this);
2840
2841 TryDeleteSwpHints();
2842 }
2843
EraseText(const SwPosition & rIdx,const sal_Int32 nCount,const SwInsertFlags nMode)2844 void SwTextNode::EraseText(const SwPosition &rIdx, const sal_Int32 nCount,
2845 const SwInsertFlags nMode )
2846 {
2847 EraseText(rIdx.nContent, nCount, nMode);
2848 }
2849
EraseText(const SwContentIndex & rIdx,const sal_Int32 nCount,const SwInsertFlags nMode)2850 void SwTextNode::EraseText(const SwContentIndex &rIdx, const sal_Int32 nCount,
2851 const SwInsertFlags nMode )
2852 {
2853 assert(rIdx.GetContentNode() == this);
2854 assert(rIdx <= m_Text.getLength()); // invalid index
2855
2856 const sal_Int32 nStartIdx = rIdx.GetIndex();
2857 const sal_Int32 nCnt = (nCount==SAL_MAX_INT32)
2858 ? m_Text.getLength() - nStartIdx : nCount;
2859 const sal_Int32 nEndIdx = nStartIdx + nCnt;
2860 if (nEndIdx <= m_Text.getLength())
2861 m_Text = m_Text.replaceAt(nStartIdx, nCnt, u"");
2862
2863 // GCAttr(); don't remove all empty ones, just the ones that are in the
2864 // range but not at the end of the range.
2865
2866 for ( sal_Int32 i = 0; m_pSwpHints && i < static_cast<sal_Int32>(m_pSwpHints->Count()); ++i )
2867 {
2868 SwTextAttr *pHt = m_pSwpHints->Get(i);
2869
2870 const sal_Int32 nHintStart = pHt->GetStart();
2871
2872 if ( nHintStart < nStartIdx )
2873 continue;
2874
2875 if ( nHintStart > nEndIdx )
2876 break; // hints are sorted by end, so break here
2877
2878 const sal_Int32* pHtEndIdx = pHt->GetEnd();
2879 const sal_uInt16 nWhich = pHt->Which();
2880
2881 if( !pHtEndIdx )
2882 {
2883 // attribute with neither end nor CH_TXTATR?
2884 assert(pHt->HasDummyChar());
2885 if (isTXTATR(nWhich) && (nHintStart < nEndIdx))
2886 {
2887 m_pSwpHints->DeleteAtPos(i);
2888 DestroyAttr( pHt );
2889 --i;
2890 }
2891 continue;
2892 }
2893
2894 assert(!( (nHintStart < nEndIdx) && (*pHtEndIdx > nEndIdx)
2895 && pHt->HasDummyChar() )
2896 // next line: deleting exactly dummy char: DeleteAttributes
2897 || ((nHintStart == nStartIdx) && (nHintStart + 1 == nEndIdx)));
2898 // "ERROR: deleting left-overlapped attribute with CH_TXTATR");
2899
2900 // Delete the hint if:
2901 // 1. The hint ends before the deletion end position or
2902 // 2. The hint ends at the deletion end position and
2903 // we are not in empty expand mode and
2904 // the hint is a [toxmark|refmark|ruby|inputfield] text attribute
2905 // 3. deleting exactly the dummy char of an hint with end and dummy
2906 // char deletes the hint
2907 if ( (*pHtEndIdx < nEndIdx)
2908 || ( (*pHtEndIdx == nEndIdx) &&
2909 !(SwInsertFlags::EMPTYEXPAND & nMode) &&
2910 ( (RES_TXTATR_TOXMARK == nWhich) ||
2911 (RES_TXTATR_REFMARK == nWhich) ||
2912 (RES_TXTATR_CJK_RUBY == nWhich) ||
2913 (RES_TXTATR_INPUTFIELD == nWhich) ) )
2914 || ( (nHintStart < nEndIdx) &&
2915 pHt->HasDummyChar() )
2916 )
2917 {
2918 m_pSwpHints->DeleteAtPos(i);
2919 DestroyAttr( pHt );
2920 --i;
2921 }
2922 }
2923
2924 OSL_ENSURE(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?");
2925
2926 TryDeleteSwpHints();
2927
2928 Update(rIdx, nCnt, UpdateMode::Negative);
2929
2930 if(1 == nCnt)
2931 {
2932 const auto aHint = sw::DeleteChar(nStartIdx);
2933 CallSwClientNotify(aHint);
2934 } else {
2935 const auto aHint = sw::DeleteText(nStartIdx, nCnt);
2936 CallSwClientNotify(aHint);
2937 }
2938
2939 OSL_ENSURE(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?");
2940
2941 // By deleting a character, the hidden flags
2942 // at the TextNode can become invalid:
2943 SetCalcHiddenCharFlags();
2944
2945 CHECK_SWPHINTS(this);
2946 }
2947
GCAttr()2948 void SwTextNode::GCAttr()
2949 {
2950 if ( !HasHints() )
2951 return;
2952
2953 bool bChanged = false;
2954 sal_Int32 nMin = m_Text.getLength();
2955 sal_Int32 nMax = 0;
2956 const bool bAll = nMin != 0; // on empty paragraphs only remove INetFormats
2957
2958 for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
2959 {
2960 SwTextAttr * const pHt = m_pSwpHints->Get(i);
2961
2962 // if end and start are equal, delete it
2963 const sal_Int32 * const pEndIdx = pHt->GetEnd();
2964 if (pEndIdx && !pHt->HasDummyChar() && (*pEndIdx == pHt->GetStart())
2965 && ( bAll || pHt->Which() == RES_TXTATR_INETFMT ) )
2966 {
2967 bChanged = true;
2968 nMin = std::min( nMin, pHt->GetStart() );
2969 nMax = std::max( nMax, *pHt->GetEnd() );
2970 DestroyAttr( m_pSwpHints->Cut(i) );
2971 --i;
2972 }
2973 else
2974 {
2975 pHt->SetDontExpand( false );
2976 }
2977 }
2978 TryDeleteSwpHints();
2979
2980 if(bChanged)
2981 {
2982 // textframes react to aHint, others to aNew
2983 SwUpdateAttr aHint(
2984 nMin,
2985 nMax,
2986 0);
2987
2988 CallSwClientNotify(sw::UpdateAttrHint(nullptr, &aHint));
2989 CallSwClientNotify(SwFormatChangeHint(nullptr, GetTextColl()));
2990 }
2991 }
2992
GetNumRule(bool bInParent) const2993 SwNumRule* SwTextNode::GetNumRule(bool bInParent) const
2994 {
2995 SwNumRule* pRet = nullptr;
2996
2997 const SfxPoolItem* pItem = GetNoCondAttr( RES_PARATR_NUMRULE, bInParent );
2998 bool bNoNumRule = false;
2999 if ( pItem )
3000 {
3001 UIName sNumRuleName =
3002 static_cast<const SwNumRuleItem *>(pItem)->GetValue();
3003 if (!sNumRuleName.isEmpty())
3004 {
3005 pRet = GetDoc().FindNumRulePtr( sNumRuleName );
3006 }
3007 else // numbering is turned off
3008 bNoNumRule = true;
3009 }
3010
3011 if ( !bNoNumRule )
3012 {
3013 if ( pRet && pRet == GetDoc().GetOutlineNumRule() &&
3014 ( !HasSwAttrSet() ||
3015 SfxItemState::SET !=
3016 GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, false ) ) )
3017 {
3018 SwTextFormatColl* pColl = GetTextColl();
3019 if ( pColl )
3020 {
3021 const SwNumRuleItem& rDirectItem = pColl->GetNumRule( false );
3022 if ( rDirectItem.GetValue().isEmpty() )
3023 {
3024 pRet = nullptr;
3025 }
3026 }
3027 }
3028 }
3029
3030 return pRet;
3031 }
3032
NumRuleChgd()3033 void SwTextNode::NumRuleChgd()
3034 {
3035 if ( IsInList() )
3036 {
3037 SwNumRule* pNumRule = GetNumRule();
3038 if ( pNumRule && pNumRule != GetNum()->GetNumRule() )
3039 {
3040 mpNodeNum->ChangeNumRule( *pNumRule );
3041 if (mpNodeNumRLHidden)
3042 {
3043 mpNodeNumRLHidden->ChangeNumRule(*pNumRule);
3044 }
3045 }
3046 }
3047
3048 // Sending "noop" modify in order to cause invalidations of registered
3049 // <SwTextFrame> instances to get the list style change respectively the change
3050 // in the list tree reflected in the layout.
3051 // Important note:
3052 {
3053 SvxTextLeftMarginItem & rLR = const_cast<SvxTextLeftMarginItem&>(GetSwAttrSet().GetTextLeftMargin());
3054 CallSwClientNotify(sw::LegacyModifyHint(&rLR, &rLR));
3055 }
3056
3057 SetWordCountDirty( true );
3058 }
3059
3060 // -> #i27615#
IsNumbered(SwRootFrame const * const pLayout) const3061 bool SwTextNode::IsNumbered(SwRootFrame const*const pLayout) const
3062 {
3063 SwNumRule* pRule = GetNum(pLayout) ? GetNum(pLayout)->GetNumRule() : nullptr;
3064 return pRule && IsCountedInList();
3065 }
3066
HasMarkedLabel() const3067 bool SwTextNode::HasMarkedLabel() const
3068 {
3069 bool bResult = false;
3070
3071 if ( IsInList() )
3072 {
3073 SwList* pList = GetDoc().getIDocumentListsAccess().getListByName(GetListId());
3074 if (pList)
3075 bResult = pList->IsListLevelMarked(GetActualListLevel());
3076 }
3077
3078 return bResult;
3079 }
3080 // <- #i27615#
3081
3082 namespace
3083 {
3084 /// Decides if a list level direct formatting on a paragraph needs copying to a next, new paragraph.
CopyDirectListLevel(const SwTextNode * pTextNode)3085 bool CopyDirectListLevel(const SwTextNode* pTextNode)
3086 {
3087 SwTextFormatColl* pColl = pTextNode->GetTextColl();
3088 if (!pColl)
3089 {
3090 // No style, so can't have a conflict with a direct formatting.
3091 return false;
3092 }
3093
3094 if (&pColl->GetNextTextFormatColl() != pColl)
3095 {
3096 // Style has a custom follow style, changing list level is OK.
3097 return false;
3098 }
3099
3100 if (!pColl->IsAssignedToListLevelOfOutlineStyle())
3101 {
3102 // Paragraph style has no own list level, no conflict.
3103 return false;
3104 }
3105
3106 // Copy is needed if the old paragraph had a direct formatting, which may be different and has
3107 // to be kept during the paragraph split.
3108 return pTextNode->HasAttrListLevel();
3109 }
3110 }
3111
MakeNewTextNode(const SwNode & rPosNd,bool bNext,bool bChgFollow)3112 SwTextNode* SwTextNode::MakeNewTextNode( const SwNode& rPosNd, bool bNext,
3113 bool bChgFollow )
3114 {
3115 // ignore hard PageBreak/PageDesc/ColumnBreak from Auto-Set
3116 std::optional<SwAttrSet> oNewAttrSet;
3117 // #i75353#
3118 bool bClearHardSetNumRuleWhenFormatCollChanges( false );
3119 if( HasSwAttrSet() )
3120 {
3121 oNewAttrSet.emplace( *GetpSwAttrSet() );
3122 const SfxItemSet* pTmpSet = GetpSwAttrSet();
3123
3124 if (bNext) // successor doesn't inherit breaks!
3125 pTmpSet = &*oNewAttrSet;
3126
3127 // !bNext: remove PageBreaks/PageDesc/ColBreak from this
3128 bool bRemoveFromCache = false;
3129 std::vector<sal_uInt16> aClearWhichIds;
3130 if ( bNext )
3131 bRemoveFromCache = ( 0 != oNewAttrSet->ClearItem( RES_PAGEDESC ) );
3132 else
3133 aClearWhichIds.push_back( RES_PAGEDESC );
3134
3135 if( SfxItemState::SET == pTmpSet->GetItemState( RES_BREAK, false ) )
3136 {
3137 if ( bNext )
3138 oNewAttrSet->ClearItem( RES_BREAK );
3139 else
3140 aClearWhichIds.push_back( RES_BREAK );
3141 bRemoveFromCache = true;
3142 }
3143 if( SfxItemState::SET == pTmpSet->GetItemState( RES_KEEP, false ) )
3144 {
3145 if ( bNext )
3146 oNewAttrSet->ClearItem( RES_KEEP );
3147 else
3148 aClearWhichIds.push_back( RES_KEEP );
3149 bRemoveFromCache = true;
3150 }
3151 if( SfxItemState::SET == pTmpSet->GetItemState( RES_PARATR_SPLIT, false ) )
3152 {
3153 if ( bNext )
3154 oNewAttrSet->ClearItem( RES_PARATR_SPLIT );
3155 else
3156 aClearWhichIds.push_back( RES_PARATR_SPLIT );
3157 bRemoveFromCache = true;
3158 }
3159 if(SfxItemState::SET == pTmpSet->GetItemState(RES_PARATR_NUMRULE, false))
3160 {
3161 SwNumRule * pRule = GetNumRule();
3162
3163 if (pRule && IsOutline())
3164 {
3165 if ( bNext )
3166 oNewAttrSet->ClearItem(RES_PARATR_NUMRULE);
3167 else
3168 {
3169 // #i75353#
3170 // No clear of hard set numbering rule at an outline paragraph at this point.
3171 // Only if the paragraph style changes - see below.
3172 bClearHardSetNumRuleWhenFormatCollChanges = true;
3173 }
3174 bRemoveFromCache = true;
3175 }
3176 }
3177
3178 if ( !aClearWhichIds.empty() )
3179 bRemoveFromCache = 0 != ClearItemsFromAttrSet( aClearWhichIds );
3180
3181 if( !bNext && bRemoveFromCache )
3182 {
3183 InvalidateInSwCache();
3184 }
3185 }
3186 SwNodes& rNds = GetNodes();
3187
3188 SwTextFormatColl* pColl = GetTextColl();
3189
3190 SwTextNode *pNode = new SwTextNode( rPosNd, pColl, oNewAttrSet ? &*oNewAttrSet : nullptr );
3191
3192 oNewAttrSet.reset();
3193
3194 const SwNumRule* pRule = GetNumRule();
3195 if( pRule && pRule == pNode->GetNumRule() && rNds.IsDocNodes() )
3196 {
3197 // #i55459#
3198 // - correction: parameter <bNext> has to be checked, as it was in the
3199 // previous implementation.
3200 if ( !bNext && !IsCountedInList() )
3201 SetCountedInList(true);
3202 }
3203
3204 // In case the numbering caused a style from the pool to be assigned to
3205 // the new node, don't overwrite that here!
3206 if( pColl != pNode->GetTextColl() ||
3207 ( bChgFollow && pColl != GetTextColl() ))
3208 return pNode; // that ought to be enough?
3209
3210 bool bSetListLevel = !CopyDirectListLevel(this);
3211
3212 pNode->ChgTextCollUpdateNum( nullptr, pColl, bSetListLevel ); // for numbering/outline
3213 if( bNext || !bChgFollow )
3214 return pNode;
3215
3216 SwTextFormatColl *pNextColl = &pColl->GetNextTextFormatColl();
3217 // i#101870 perform action on different paragraph styles before applying
3218 // the new paragraph style
3219 if (pNextColl != pColl)
3220 {
3221 // #i75353#
3222 if ( bClearHardSetNumRuleWhenFormatCollChanges )
3223 {
3224 if ( ClearItemsFromAttrSet( { RES_PARATR_NUMRULE } ) != 0 )
3225 {
3226 InvalidateInSwCache();
3227 }
3228 }
3229 }
3230 ChgFormatColl( pNextColl, bSetListLevel );
3231
3232 return pNode;
3233 }
3234
AppendNode(const SwPosition & rPos)3235 SwContentNode* SwTextNode::AppendNode( const SwPosition & rPos )
3236 {
3237 // position behind which it will be inserted
3238 SwTextNode* pNew = MakeNewTextNode( *rPos.GetNodes()[rPos.GetNodeIndex() + 1] );
3239
3240 // reset list attributes at appended text node
3241 pNew->ResetAttr( RES_PARATR_LIST_ISRESTART );
3242 pNew->ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
3243 pNew->ResetAttr( RES_PARATR_LIST_ISCOUNTED );
3244 if ( pNew->GetNumRule() == nullptr )
3245 {
3246 pNew->ResetAttr( RES_PARATR_LIST_ID );
3247 pNew->ResetAttr( RES_PARATR_LIST_LEVEL );
3248 }
3249
3250 if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
3251 {
3252 AddToList();
3253 }
3254
3255 if( HasWriterListeners() )
3256 MakeFramesForAdjacentContentNode(*pNew);
3257 return pNew;
3258 }
3259
GetTextAttrForCharAt(const sal_Int32 nIndex,const sal_uInt16 nWhich) const3260 SwTextAttr * SwTextNode::GetTextAttrForCharAt(
3261 const sal_Int32 nIndex,
3262 const sal_uInt16 nWhich ) const
3263 {
3264 assert(nWhich >= RES_TXTATR_BEGIN && nWhich <= RES_TXTATR_END);
3265 if ( HasHints() )
3266 {
3267 for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
3268 {
3269 SwTextAttr * const pHint = m_pSwpHints->Get(i);
3270 const sal_Int32 nStartPos = pHint->GetStart();
3271 if ( nIndex < nStartPos )
3272 {
3273 return nullptr;
3274 }
3275 if ( (nIndex == nStartPos) && pHint->HasDummyChar() )
3276 {
3277 return ( RES_TXTATR_END == nWhich || nWhich == pHint->Which() )
3278 ? pHint : nullptr;
3279 }
3280 }
3281 }
3282 return nullptr;
3283 }
3284
GetTextAttrForEndCharAt(sal_Int32 nIndex,sal_uInt16 nWhich) const3285 SwTextAttr* SwTextNode::GetTextAttrForEndCharAt(sal_Int32 nIndex, sal_uInt16 nWhich) const
3286 {
3287 SwTextAttr* pAttr = GetTextAttrAt(nIndex, nWhich, ::sw::GetTextAttrMode::Expand);
3288 if (!pAttr)
3289 {
3290 return nullptr;
3291 }
3292
3293 if (!pAttr->End())
3294 {
3295 return nullptr;
3296 }
3297
3298 // The start-end range covers the end dummy character.
3299 if (*pAttr->End() - 1 != nIndex)
3300 {
3301 return nullptr;
3302 }
3303
3304 return pAttr;
3305 }
3306
3307 namespace
3308 {
3309
lcl_BoundListLevel(const int nActualLevel)3310 sal_uInt16 lcl_BoundListLevel(const int nActualLevel)
3311 {
3312 return o3tl::narrowing<sal_uInt16>( std::clamp( nActualLevel, 0, MAXLEVEL-1 ) );
3313 }
3314
3315 }
3316
3317 // -> #i29560#
HasNumber(SwRootFrame const * const pLayout) const3318 bool SwTextNode::HasNumber(SwRootFrame const*const pLayout) const
3319 {
3320 bool bResult = false;
3321
3322 const SwNumRule *const pRule = GetNum(pLayout) ? GetNum(pLayout)->GetNumRule() : nullptr;
3323 if ( pRule )
3324 {
3325 const SwNumFormat& aFormat(pRule->Get(lcl_BoundListLevel(GetActualListLevel())));
3326
3327 // #i40041#
3328 bResult = aFormat.IsEnumeration();
3329 }
3330
3331 return bResult;
3332 }
3333
HasBullet() const3334 bool SwTextNode::HasBullet() const
3335 {
3336 bool bResult = false;
3337
3338 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3339 if ( pRule )
3340 {
3341 const SwNumFormat& aFormat(pRule->Get(lcl_BoundListLevel(GetActualListLevel())));
3342
3343 bResult = aFormat.IsItemize();
3344 }
3345
3346 return bResult;
3347 }
3348 // <- #i29560#
3349
3350 // #128041# - introduce parameter <_bInclPrefixAndSuffixStrings>
3351 //i53420 added max outline parameter
GetNumString(const bool _bInclPrefixAndSuffixStrings,const unsigned int _nRestrictToThisLevel,SwRootFrame const * const pLayout,SwListRedlineType eRedline) const3352 OUString SwTextNode::GetNumString( const bool _bInclPrefixAndSuffixStrings,
3353 const unsigned int _nRestrictToThisLevel,
3354 SwRootFrame const*const pLayout, SwListRedlineType eRedline) const
3355 {
3356 if (GetDoc().IsClipBoard() && m_oNumStringCache)
3357 {
3358 // #i111677# do not expand number strings in clipboard documents
3359 return *m_oNumStringCache;
3360 }
3361 const SwNumRule* pRule = GetNum(pLayout, eRedline) ? GetNum(pLayout, eRedline)->GetNumRule() : nullptr;
3362 if ( pRule &&
3363 IsCountedInList() )
3364 {
3365 SvxNumberType const& rNumberType(
3366 pRule->Get( lcl_BoundListLevel(GetActualListLevel(eRedline)) ) );
3367 if (rNumberType.IsTextFormat() ||
3368
3369 (style::NumberingType::NUMBER_NONE == rNumberType.GetNumberingType()))
3370 {
3371 return pRule->MakeNumString( GetNum(pLayout, eRedline)->GetNumberVector(),
3372 _bInclPrefixAndSuffixStrings,
3373 _nRestrictToThisLevel,
3374 false,
3375 nullptr,
3376 GetLang(0));
3377 }
3378 }
3379
3380 return OUString();
3381 }
3382
GetLeftMarginWithNum(bool bTextLeft) const3383 tools::Long SwTextNode::GetLeftMarginWithNum( bool bTextLeft ) const
3384 {
3385 tools::Long nRet = 0;
3386 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3387 if( pRule )
3388 {
3389 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3390
3391 if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
3392 {
3393 nRet = rFormat.GetAbsLSpace();
3394
3395 if( !bTextLeft )
3396 {
3397 if( 0 > rFormat.GetFirstLineOffset() &&
3398 nRet > -rFormat.GetFirstLineOffset() )
3399 nRet = nRet + rFormat.GetFirstLineOffset();
3400 else
3401 nRet = 0;
3402 }
3403
3404 if( pRule->IsAbsSpaces() )
3405 {
3406 SvxFirstLineIndentItem const& rFirst(GetSwAttrSet().GetFirstLineIndent());
3407 nRet
3408 = nRet - GetSwAttrSet().GetTextLeftMargin().ResolveLeft(rFirst, /*metrics*/ {});
3409 }
3410 }
3411 else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
3412 {
3413 ::sw::ListLevelIndents const indents(AreListLevelIndentsApplicable());
3414 // note: the result is *always* added to either the left-margin
3415 // or the text-left-margin of the node itself by the caller.
3416 // so first, subtract what the caller has computed anyway,
3417 // and then add the value according to combination of
3418 // list/paragraph items. (this is rather inelegant)
3419 SvxFirstLineIndentItem firstLine(GetSwAttrSet().GetFirstLineIndent());
3420 SvxTextLeftMarginItem leftMargin(GetSwAttrSet().GetTextLeftMargin());
3421 nRet = bTextLeft ? -leftMargin.ResolveTextLeft(/*metrics*/ {})
3422 : -leftMargin.ResolveLeft(firstLine, /*metrics*/ {});
3423 if (indents & ::sw::ListLevelIndents::LeftMargin)
3424 {
3425 leftMargin.SetTextLeft(SvxIndentValue::twips(rFormat.GetIndentAt()));
3426 }
3427 if (indents & ::sw::ListLevelIndents::FirstLine)
3428 {
3429 firstLine.SetTextFirstLineOffset(
3430 SvxIndentValue{ static_cast<double>(rFormat.GetFirstLineIndent()),
3431 rFormat.GetFirstLineIndentUnit() });
3432 }
3433 nRet += bTextLeft ? leftMargin.ResolveTextLeft(/*metrics*/ {})
3434 : leftMargin.ResolveLeft(firstLine, /*metrics*/ {});
3435 }
3436 }
3437
3438 return nRet;
3439 }
3440
GetFirstLineOfsWithNum(short & rFLOffset,const SvxFontUnitMetrics & rMetrics) const3441 bool SwTextNode::GetFirstLineOfsWithNum(short& rFLOffset,
3442 const SvxFontUnitMetrics& rMetrics) const
3443 {
3444 // #i95907#
3445 rFLOffset = 0;
3446
3447 // #i51089#
3448 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3449 if ( pRule )
3450 {
3451 if ( IsCountedInList() )
3452 {
3453 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3454 if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
3455 {
3456 rFLOffset = rFormat.GetFirstLineOffset(); //TODO: overflow
3457
3458 if (!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
3459 {
3460 SvxFirstLineIndentItem const aItem(GetSwAttrSet().GetFirstLineIndent());
3461 rFLOffset = rFLOffset + aItem.ResolveTextFirstLineOffset(rMetrics);
3462 }
3463 }
3464 else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
3465 {
3466 if (AreListLevelIndentsApplicable() & ::sw::ListLevelIndents::FirstLine)
3467 {
3468 rFLOffset = rFormat.GetFirstLineIndent();
3469 }
3470 else if (!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
3471 {
3472 SvxFirstLineIndentItem const aItem(GetSwAttrSet().GetFirstLineIndent());
3473 rFLOffset = aItem.ResolveTextFirstLineOffset(rMetrics);
3474 }
3475 }
3476 }
3477
3478 return true;
3479 }
3480
3481 rFLOffset = GetSwAttrSet().GetFirstLineIndent().ResolveTextFirstLineOffset(rMetrics);
3482 return false;
3483 }
3484
GetAdditionalIndentForStartingNewList() const3485 SwTwips SwTextNode::GetAdditionalIndentForStartingNewList() const
3486 {
3487 SwTwips nAdditionalIndent = 0;
3488
3489 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3490 if ( pRule )
3491 {
3492 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3493 if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
3494 {
3495 SvxFirstLineIndentItem const& rFirst(GetSwAttrSet().GetFirstLineIndent());
3496
3497 nAdditionalIndent
3498 = GetSwAttrSet().GetTextLeftMargin().ResolveLeft(rFirst, /*metrics*/ {});
3499
3500 if (getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
3501 {
3502 nAdditionalIndent
3503 = nAdditionalIndent
3504 - GetSwAttrSet().GetFirstLineIndent().ResolveTextFirstLineOffset({});
3505 }
3506 }
3507 else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
3508 {
3509 // note: there was an apparent bug here, list GetIndentAt()
3510 // was interpreted as left-margin not text-left-margin unlike every
3511 // other use of it.
3512 ::sw::ListLevelIndents const indents(AreListLevelIndentsApplicable());
3513 SvxFirstLineIndentItem const aFirst(
3514 indents & ::sw::ListLevelIndents::FirstLine
3515 ? SvxFirstLineIndentItem(
3516 SvxIndentValue{ static_cast<double>(rFormat.GetFirstLineIndent()),
3517 rFormat.GetFirstLineIndentUnit() },
3518 RES_MARGIN_FIRSTLINE)
3519 : GetSwAttrSet().GetFirstLineIndent());
3520 SvxTextLeftMarginItem const aLeft(
3521 indents & ::sw::ListLevelIndents::LeftMargin
3522 ? SvxTextLeftMarginItem(SvxIndentValue::twips(rFormat.GetIndentAt()),
3523 RES_MARGIN_TEXTLEFT)
3524 : GetSwAttrSet().GetTextLeftMargin());
3525 nAdditionalIndent = aLeft.ResolveLeft(aFirst, /*metrics*/ {});
3526 if (!(indents & ::sw::ListLevelIndents::FirstLine))
3527 {
3528 if (getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
3529 {
3530 nAdditionalIndent = nAdditionalIndent - aFirst.ResolveTextFirstLineOffset({});
3531 }
3532 }
3533 }
3534 }
3535 else
3536 {
3537 SvxFirstLineIndentItem const& rFirst(GetSwAttrSet().GetFirstLineIndent());
3538 nAdditionalIndent = GetSwAttrSet().GetTextLeftMargin().ResolveLeft(rFirst, /*metrics*/ {});
3539 }
3540
3541 return nAdditionalIndent;
3542 }
3543
3544 // #i91133#
GetLeftMarginForTabCalculation() const3545 tools::Long SwTextNode::GetLeftMarginForTabCalculation() const
3546 {
3547 tools::Long nLeftMarginForTabCalc = 0;
3548
3549 bool bLeftMarginForTabCalcSetToListLevelIndent( false );
3550 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3551 if( pRule )
3552 {
3553 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3554 if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
3555 {
3556 if (AreListLevelIndentsApplicable() & ::sw::ListLevelIndents::LeftMargin)
3557 {
3558 nLeftMarginForTabCalc = rFormat.GetIndentAt();
3559 bLeftMarginForTabCalcSetToListLevelIndent = true;
3560 }
3561 }
3562 }
3563 if ( !bLeftMarginForTabCalcSetToListLevelIndent )
3564 {
3565 nLeftMarginForTabCalc = GetSwAttrSet().GetTextLeftMargin().ResolveTextLeft({});
3566 }
3567
3568 return nLeftMarginForTabCalc;
3569 }
3570
Replace0xFF(SwTextNode const & rNode,OUStringBuffer & rText,sal_Int32 & rTextStt,sal_Int32 nEndPos)3571 static void Replace0xFF(
3572 SwTextNode const& rNode,
3573 OUStringBuffer & rText,
3574 sal_Int32 & rTextStt,
3575 sal_Int32 nEndPos )
3576 {
3577 if (!rNode.GetpSwpHints())
3578 return;
3579
3580 sal_Unicode cSrchChr = CH_TXTATR_BREAKWORD;
3581 for( int nSrchIter = 0; 2 > nSrchIter; ++nSrchIter, cSrchChr = CH_TXTATR_INWORD )
3582 {
3583 sal_Int32 nPos = rText.indexOf(cSrchChr);
3584 while (-1 != nPos && nPos < nEndPos)
3585 {
3586 const SwTextAttr* const pAttr =
3587 rNode.GetTextAttrForCharAt(rTextStt + nPos);
3588 if( pAttr )
3589 {
3590 switch( pAttr->Which() )
3591 {
3592 case RES_TXTATR_FIELD:
3593 case RES_TXTATR_ANNOTATION:
3594 rText.remove(nPos, 1);
3595 ++rTextStt;
3596 break;
3597
3598 case RES_TXTATR_FTN:
3599 rText.remove(nPos, 1);
3600 ++rTextStt;
3601 break;
3602
3603 default:
3604 rText.remove(nPos, 1);
3605 ++rTextStt;
3606 }
3607 }
3608 else
3609 {
3610 ++nPos;
3611 ++nEndPos;
3612 }
3613 nPos = rText.indexOf(cSrchChr, nPos);
3614 }
3615 }
3616 }
3617
3618 // Expand fields
3619 // #i83479# - handling of new parameters
GetExpandText(SwRootFrame const * const pLayout,const sal_Int32 nIdx,const sal_Int32 nLen,const bool bWithNum,const bool bAddSpaceAfterListLabelStr,const bool bWithSpacesForLevel,const ExpandMode eAdditionalMode) const3620 OUString SwTextNode::GetExpandText(SwRootFrame const*const pLayout,
3621 const sal_Int32 nIdx,
3622 const sal_Int32 nLen,
3623 const bool bWithNum,
3624 const bool bAddSpaceAfterListLabelStr,
3625 const bool bWithSpacesForLevel,
3626 const ExpandMode eAdditionalMode) const
3627
3628 {
3629 ExpandMode eMode = ExpandMode::ExpandFields | eAdditionalMode;
3630 if (pLayout && pLayout->IsHideRedlines())
3631 {
3632 eMode |= ExpandMode::HideDeletions;
3633 }
3634
3635 ModelToViewHelper aConversionMap(*this, pLayout, eMode);
3636 const OUString& aExpandText = aConversionMap.getViewText();
3637 const sal_Int32 nExpandBegin = aConversionMap.ConvertToViewPosition( nIdx );
3638 sal_Int32 nEnd = nLen == -1 ? GetText().getLength() : nIdx + nLen;
3639 const sal_Int32 nExpandEnd = aConversionMap.ConvertToViewPosition( nEnd );
3640 OUStringBuffer aText(aExpandText.subView(nExpandBegin, nExpandEnd-nExpandBegin));
3641
3642 // remove dummy characters of Input Fields
3643 comphelper::string::remove(aText, CH_TXT_ATR_INPUTFIELDSTART);
3644 comphelper::string::remove(aText, CH_TXT_ATR_INPUTFIELDEND);
3645 comphelper::string::remove(aText, CH_TXTATR_BREAKWORD);
3646
3647 if( bWithNum )
3648 {
3649 if (!GetNumString(true, MAXLEVEL, pLayout).isEmpty())
3650 {
3651 if ( bAddSpaceAfterListLabelStr )
3652 {
3653 const sal_Unicode aSpace = ' ';
3654 aText.insert(0, aSpace);
3655 }
3656 aText.insert(0, GetNumString(true, MAXLEVEL, pLayout));
3657 }
3658 }
3659
3660 if (bWithSpacesForLevel)
3661 {
3662 const sal_Unicode aSpace = ' ';
3663 for (int nLevel = GetActualListLevel(); nLevel > 0; --nLevel)
3664 {
3665 aText.insert(0, aSpace);
3666 aText.insert(0, aSpace);
3667 }
3668 }
3669
3670 return aText.makeStringAndClear();
3671 }
3672
CopyExpandText(SwTextNode & rDestNd,const SwContentIndex * pDestIdx,sal_Int32 nIdx,sal_Int32 nLen,SwRootFrame const * const pLayout,bool bWithFootnote,bool bReplaceTabsWithSpaces) const3673 bool SwTextNode::CopyExpandText(SwTextNode& rDestNd, const SwContentIndex* pDestIdx,
3674 sal_Int32 nIdx, sal_Int32 nLen,
3675 SwRootFrame const*const pLayout,
3676 bool bWithFootnote, bool bReplaceTabsWithSpaces ) const
3677 {
3678 if( &rDestNd == this )
3679 return false;
3680 assert(!pDestIdx || pDestIdx->GetContentNode() == &rDestNd);
3681
3682 SwContentIndex aDestIdx(&rDestNd, rDestNd.GetText().getLength());
3683 if( pDestIdx )
3684 aDestIdx = *pDestIdx;
3685 const sal_Int32 nDestStt = aDestIdx.GetIndex();
3686
3687 // first, start with the text
3688 OUStringBuffer buf(GetText());
3689 if( bReplaceTabsWithSpaces )
3690 buf.replace('\t', ' ');
3691
3692 // mask hidden characters
3693 const sal_Unicode cChar = CH_TXTATR_BREAKWORD;
3694 SwScriptInfo::MaskHiddenRanges(*this, buf, 0, buf.getLength(), cChar);
3695
3696 buf.remove(0, nIdx);
3697 if (nLen != -1)
3698 {
3699 buf.truncate(nLen);
3700 }
3701 // remove dummy characters of Input Fields
3702 {
3703 comphelper::string::remove(buf, CH_TXT_ATR_INPUTFIELDSTART);
3704 comphelper::string::remove(buf, CH_TXT_ATR_INPUTFIELDEND);
3705 }
3706 rDestNd.InsertText(buf.makeStringAndClear(), aDestIdx);
3707 nLen = aDestIdx.GetIndex() - nDestStt;
3708
3709 // set all char attributes with Symbol font
3710 if ( HasHints() )
3711 {
3712 sal_Int32 nInsPos = nDestStt - nIdx;
3713 for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
3714 {
3715 const SwTextAttr* pHt = m_pSwpHints->Get(i);
3716 const sal_Int32 nAttrStartIdx = pHt->GetStart();
3717 const sal_uInt16 nWhich = pHt->Which();
3718 if (nIdx + nLen <= nAttrStartIdx)
3719 break; // behind end of text
3720
3721 const sal_Int32 *pEndIdx = pHt->End();
3722 if( pEndIdx && *pEndIdx > nIdx &&
3723 ( RES_CHRATR_FONT == nWhich ||
3724 RES_TXTATR_CHARFMT == nWhich ||
3725 RES_TXTATR_AUTOFMT == nWhich ))
3726 {
3727 const SvxFontItem* const pFont =
3728 CharFormat::GetItem( *pHt, RES_CHRATR_FONT );
3729 if ( pFont && RTL_TEXTENCODING_SYMBOL == pFont->GetCharSet() )
3730 {
3731 // attribute in area => copy
3732 rDestNd.InsertItem( *const_cast<SvxFontItem*>(pFont),
3733 nInsPos + nAttrStartIdx, nInsPos + *pEndIdx );
3734 }
3735 }
3736 else if ( pHt->HasDummyChar() && (nAttrStartIdx >= nIdx) )
3737 {
3738 aDestIdx = nInsPos + nAttrStartIdx;
3739 switch( nWhich )
3740 {
3741 case RES_TXTATR_FIELD:
3742 case RES_TXTATR_ANNOTATION:
3743 {
3744 OUString const aExpand(
3745 static_txtattr_cast<SwTextField const*>(pHt)->GetFormatField().GetField()->ExpandField(true, pLayout));
3746 if (!aExpand.isEmpty())
3747 {
3748 ++aDestIdx; // insert behind
3749 OUString const ins(
3750 rDestNd.InsertText( aExpand, aDestIdx));
3751 SAL_INFO_IF(ins.getLength() != aExpand.getLength(),
3752 "sw.core", "GetExpandText lossage");
3753 aDestIdx = nInsPos + nAttrStartIdx;
3754 nInsPos += ins.getLength();
3755 }
3756 rDestNd.EraseText( aDestIdx, 1 );
3757 --nInsPos;
3758 }
3759 break;
3760
3761 case RES_TXTATR_FTN:
3762 {
3763 if ( bWithFootnote )
3764 {
3765 const SwFormatFootnote& rFootnote = pHt->GetFootnote();
3766 OUString sExpand;
3767 auto const number(pLayout && pLayout->IsHideRedlines()
3768 ? rFootnote.GetNumberRLHidden()
3769 : rFootnote.GetNumber());
3770 if( !rFootnote.GetNumStr().isEmpty() )
3771 sExpand = rFootnote.GetNumStr();
3772 else if( rFootnote.IsEndNote() )
3773 sExpand = GetDoc().GetEndNoteInfo().m_aFormat.
3774 GetNumStr(number);
3775 else
3776 sExpand = GetDoc().GetFootnoteInfo().m_aFormat.
3777 GetNumStr(number);
3778 if( !sExpand.isEmpty() )
3779 {
3780 ++aDestIdx; // insert behind
3781 SvxEscapementItem aItem( SvxEscapement::Superscript, RES_CHRATR_ESCAPEMENT );
3782 rDestNd.InsertItem(
3783 aItem,
3784 aDestIdx.GetIndex(),
3785 aDestIdx.GetIndex() );
3786 OUString const ins( rDestNd.InsertText(sExpand, aDestIdx, SwInsertFlags::EMPTYEXPAND));
3787 SAL_INFO_IF(ins.getLength() != sExpand.getLength(),
3788 "sw.core", "GetExpandText lossage");
3789 aDestIdx = nInsPos + nAttrStartIdx;
3790 nInsPos += ins.getLength();
3791 }
3792 }
3793 rDestNd.EraseText( aDestIdx, 1 );
3794 --nInsPos;
3795 }
3796 break;
3797
3798 default:
3799 rDestNd.EraseText( aDestIdx, 1 );
3800 --nInsPos;
3801 }
3802 }
3803 }
3804 }
3805
3806 aDestIdx = 0;
3807 sal_Int32 nStartDelete(-1);
3808 while (aDestIdx < rDestNd.GetText().getLength())
3809 {
3810 sal_Unicode const cur(rDestNd.GetText()[aDestIdx.GetIndex()]);
3811 if ( (cChar == cur) // filter substituted hidden text
3812 || (CH_TXT_ATR_FIELDSTART == cur) // filter all fieldmarks
3813 || (CH_TXT_ATR_FIELDSEP == cur)
3814 || (CH_TXT_ATR_FIELDEND == cur)
3815 || (CH_TXT_ATR_FORMELEMENT == cur))
3816 {
3817 if (-1 == nStartDelete)
3818 {
3819 nStartDelete = aDestIdx.GetIndex(); // start deletion range
3820 }
3821 ++aDestIdx;
3822 if (aDestIdx < rDestNd.GetText().getLength())
3823 {
3824 continue;
3825 } // else: end of paragraph => delete, see below
3826 }
3827 else
3828 {
3829 if (-1 == nStartDelete)
3830 {
3831 ++aDestIdx;
3832 continue;
3833 } // else: delete, see below
3834 }
3835 assert(-1 != nStartDelete); // without delete range, would have continued
3836 rDestNd.EraseText(
3837 SwContentIndex(&rDestNd, nStartDelete),
3838 aDestIdx.GetIndex() - nStartDelete);
3839 assert(aDestIdx.GetIndex() == nStartDelete);
3840 nStartDelete = -1; // reset
3841 }
3842
3843 return true;
3844 }
3845
GetRedlineText() const3846 OUString SwTextNode::GetRedlineText() const
3847 {
3848 std::vector<sal_Int32> aRedlArr;
3849 const SwDoc& rDoc = GetDoc();
3850 SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos( *this, RedlineType::Delete );
3851 if( SwRedlineTable::npos != nRedlPos )
3852 {
3853 // some redline-delete object exists for the node
3854 const SwNodeOffset nNdIdx = GetIndex();
3855 for( ; nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ; ++nRedlPos )
3856 {
3857 const SwRangeRedline* pTmp = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
3858 if( RedlineType::Delete == pTmp->GetType() )
3859 {
3860 const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
3861 if( pRStt->GetNodeIndex() < nNdIdx )
3862 {
3863 if( pREnd->GetNodeIndex() > nNdIdx )
3864 // paragraph is fully deleted
3865 return OUString();
3866 else if( pREnd->GetNodeIndex() == nNdIdx )
3867 {
3868 // deleted from 0 to nContent
3869 aRedlArr.push_back( 0 );
3870 aRedlArr.push_back( pREnd->GetContentIndex() );
3871 }
3872 }
3873 else if( pRStt->GetNodeIndex() == nNdIdx )
3874 {
3875 //aRedlArr.Insert( pRStt->GetContentIndex(), aRedlArr.Count() );
3876 aRedlArr.push_back( pRStt->GetContentIndex() );
3877 if( pREnd->GetNodeIndex() == nNdIdx )
3878 aRedlArr.push_back( pREnd->GetContentIndex() );
3879 else
3880 {
3881 aRedlArr.push_back(GetText().getLength());
3882 break; // that was all
3883 }
3884 }
3885 else
3886 break; // that was all
3887 }
3888 }
3889 }
3890
3891 OUStringBuffer aText(GetText());
3892
3893 sal_Int32 nTextStt = 0;
3894 sal_Int32 nIdxEnd = aText.getLength();
3895 for( size_t n = 0; n < aRedlArr.size(); n += 2 )
3896 {
3897 sal_Int32 nStt = aRedlArr[ n ];
3898 sal_Int32 nEnd = aRedlArr[ n+1 ];
3899 if( ( 0 <= nStt && nStt <= nIdxEnd ) ||
3900 ( 0 <= nEnd && nEnd <= nIdxEnd ))
3901 {
3902 if( nStt < 0 ) nStt = 0;
3903 if( nIdxEnd < nEnd ) nEnd = nIdxEnd;
3904 const sal_Int32 nDelCnt = nEnd - nStt;
3905 aText.remove(nStt - nTextStt, nDelCnt);
3906 Replace0xFF(*this, aText, nTextStt, nStt - nTextStt);
3907 nTextStt += nDelCnt;
3908 }
3909 else if( nStt >= nIdxEnd )
3910 break;
3911 }
3912 Replace0xFF(*this, aText, nTextStt, aText.getLength());
3913
3914 return aText.makeStringAndClear();
3915 }
3916
ReplaceText(const SwContentIndex & rStart,const sal_Int32 nDelLen,const OUString & rStr)3917 void SwTextNode::ReplaceText( const SwContentIndex& rStart, const sal_Int32 nDelLen,
3918 const OUString & rStr)
3919 {
3920 assert(rStart.GetContentNode() == this);
3921 assert( rStart.GetIndex() < m_Text.getLength() // index out of bounds
3922 && rStart.GetIndex() + nDelLen <= m_Text.getLength());
3923
3924 sal_Int32 const nOverflow(rStr.getLength() - nDelLen - GetSpaceLeft());
3925 SAL_WARN_IF(nOverflow > 0, "sw.core",
3926 "SwTextNode::ReplaceText: node text with insertion > node capacity.");
3927 OUString const sInserted(
3928 (nOverflow > 0) ? rStr.copy(0, rStr.getLength() - nOverflow) : rStr);
3929 if (sInserted.isEmpty() && 0 == nDelLen)
3930 {
3931 return; // nothing to do
3932 }
3933
3934 const sal_Int32 nStartPos = rStart.GetIndex();
3935 sal_Int32 nEndPos = nStartPos + nDelLen;
3936 sal_Int32 nLen = nDelLen;
3937 for( sal_Int32 nPos = nStartPos; nPos < nEndPos; ++nPos )
3938 {
3939 if ((CH_TXTATR_BREAKWORD == m_Text[nPos]) ||
3940 (CH_TXTATR_INWORD == m_Text[nPos]))
3941 {
3942 SwTextAttr *const pHint = GetTextAttrForCharAt( nPos );
3943 if (pHint)
3944 {
3945 assert(!( pHint->GetEnd() && pHint->HasDummyChar()
3946 && (pHint->GetStart() < nEndPos)
3947 && (*pHint->GetEnd() > nEndPos) ));
3948 // "deleting left-overlapped attribute with CH_TXTATR"
3949 DeleteAttribute( pHint );
3950 --nEndPos;
3951 --nLen;
3952 }
3953 }
3954 }
3955
3956 bool bOldExpFlg = IsIgnoreDontExpand();
3957 SetIgnoreDontExpand( true );
3958
3959 const sal_Int32 nInsLen = sInserted.getLength();
3960 if (nLen && nInsLen)
3961 {
3962 m_Text = m_Text.replaceAt(nStartPos, nLen, sInserted);
3963
3964 if (nLen > nInsLen) // short insert
3965 {
3966 // delete up to the point that the user specified
3967 const SwContentIndex aNegIdx(rStart, nInsLen);
3968 Update(aNegIdx, nLen - nInsLen, UpdateMode::Negative);
3969 }
3970 else if (nLen < nInsLen) // long insert
3971 {
3972 const SwContentIndex aIdx(rStart, nLen);
3973 Update(aIdx, nInsLen - nLen, UpdateMode::Replace);
3974 }
3975
3976 for (sal_Int32 i = 0; i < nInsLen; i++)
3977 {
3978 ++const_cast<SwContentIndex&>(rStart);
3979 }
3980 }
3981 else
3982 {
3983 m_Text = m_Text.replaceAt(nStartPos, nLen, u"");
3984 Update(rStart, nLen, UpdateMode::Negative);
3985
3986 m_Text = m_Text.replaceAt(nStartPos, 0, sInserted);
3987 Update(rStart, sInserted.getLength(), UpdateMode::Replace);
3988 }
3989
3990 SetIgnoreDontExpand( bOldExpFlg );
3991 auto aDelHint = sw::DeleteText(nStartPos, nDelLen);
3992 CallSwClientNotify(aDelHint);
3993
3994 if (sInserted.getLength())
3995 {
3996 auto aInsHint = sw::MakeInsertText(*this, nStartPos, sInserted.getLength());
3997 CallSwClientNotify(aInsHint);
3998 }
3999 }
4000
ReplaceText(const SwPosition & rStart,const sal_Int32 nDelLen,const OUString & rStr)4001 void SwTextNode::ReplaceText( const SwPosition& rStart, const sal_Int32 nDelLen,
4002 const OUString & rStr)
4003 {
4004 ReplaceText(rStart.nContent, nDelLen, rStr);
4005 }
4006
4007 namespace {
lcl_ResetParAttrs(SwTextNode & rTextNode)4008 void lcl_ResetParAttrs( SwTextNode &rTextNode )
4009 {
4010 const o3tl::sorted_vector<sal_uInt16> aAttrs{ RES_PARATR_LIST_ID, RES_PARATR_LIST_LEVEL,
4011 RES_PARATR_LIST_ISRESTART,
4012 RES_PARATR_LIST_RESTARTVALUE,
4013 RES_PARATR_LIST_ISCOUNTED };
4014 SwPaM aPam( rTextNode );
4015 // #i96644#
4016 // suppress side effect "send data changed events"
4017 rTextNode.GetDoc().ResetAttrs( aPam, false, aAttrs, false );
4018 }
4019
4020 void HandleApplyTextNodeFormatChange( SwTextNode& rTextNode, const UIName& sNumRule, const UIName& sOldNumRule, bool bNumRuleSet, bool bParagraphStyleChanged );
4021
4022 // Helper method for special handling of modified attributes at text node.
4023 // The following is handled:
4024 // (1) on changing the paragraph style - RES_FMT_CHG:
4025 // Check, if list style of the text node is changed. If yes, add respectively
4026 // remove the text node to the corresponding list.
4027 // (2) on changing the attributes - RES_ATTRSET_CHG:
4028 // Same as (1).
4029 // (3) on changing the list style - RES_PARATR_NUMRULE:
4030 // Same as (1).
HandleModifyAtTextNode(SwTextNode & rTextNode,const SfxPoolItem * pOldValue,const SfxPoolItem * pNewValue)4031 void HandleModifyAtTextNode( SwTextNode& rTextNode,
4032 const SfxPoolItem* pOldValue,
4033 const SfxPoolItem* pNewValue )
4034 {
4035 const sal_uInt16 nWhich = pOldValue ? pOldValue->Which() :
4036 pNewValue ? pNewValue->Which() : 0 ;
4037 bool bNumRuleSet = false;
4038 bool bParagraphStyleChanged = false;
4039 UIName sNumRule;
4040 UIName sOldNumRule;
4041 switch ( nWhich )
4042 {
4043 case RES_PARATR_NUMRULE:
4044 {
4045 if ( rTextNode.GetNodes().IsDocNodes() )
4046 {
4047 const SwNumRule* pFormerNumRuleAtTextNode =
4048 rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
4049 if ( pFormerNumRuleAtTextNode )
4050 {
4051 sOldNumRule = pFormerNumRuleAtTextNode->GetName();
4052 }
4053
4054 if ( pNewValue )
4055 {
4056 // #i70748#
4057 rTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4058 bNumRuleSet = true;
4059 }
4060 // #i70748#
4061 // The new list style set at the paragraph.
4062 const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
4063 if ( pNumRuleAtTextNode )
4064 {
4065 sNumRule = pNumRuleAtTextNode->GetName();
4066 }
4067 }
4068 break;
4069 }
4070 }
4071 HandleApplyTextNodeFormatChange(rTextNode, sNumRule, sOldNumRule, bNumRuleSet, bParagraphStyleChanged);
4072 }
4073 // End of method <HandleModifyAtTextNode>
4074
HandleModifyAtTextNode(SwTextNode & rTextNode,const SwAttrSetChg *,const SwAttrSetChg * pNewValue)4075 void HandleModifyAtTextNode( SwTextNode& rTextNode,
4076 const SwAttrSetChg* /*pOldValue*/,
4077 const SwAttrSetChg* pNewValue )
4078 {
4079 bool bNumRuleSet = false;
4080 UIName sNumRule;
4081 UIName sOldNumRule;
4082 const SwNumRule* pFormerNumRuleAtTextNode =
4083 rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
4084 if ( pFormerNumRuleAtTextNode )
4085 {
4086 sOldNumRule = pFormerNumRuleAtTextNode->GetName();
4087 }
4088
4089 if ( pNewValue && pNewValue->GetChgSet()->GetItemState( RES_PARATR_NUMRULE, false ) ==
4090 SfxItemState::SET )
4091 {
4092 // #i70748#
4093 rTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4094 bNumRuleSet = true;
4095 }
4096 // #i70748#
4097 // The new list style set at the paragraph.
4098 const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
4099 if ( pNumRuleAtTextNode )
4100 {
4101 sNumRule = pNumRuleAtTextNode->GetName();
4102 }
4103 HandleApplyTextNodeFormatChange(rTextNode, sNumRule, sOldNumRule, bNumRuleSet, /*bParagraphStyleChanged*/false);
4104 }
4105
4106 // Helper method for special handling of modified attributes at text node.
4107 // The following is handled:
4108 // (1) on changing the paragraph style - RES_FMT_CHG:
4109 // Check, if list style of the text node is changed. If yes, add respectively
4110 // remove the text node to the corresponding list.
HandleModifyAtTextNodeFormatChange(SwTextNode & rTextNode)4111 void HandleModifyAtTextNodeFormatChange( SwTextNode& rTextNode )
4112 {
4113 bool bNumRuleSet = false;
4114 bool bParagraphStyleChanged = true;
4115 UIName sNumRule;
4116 UIName sOldNumRule;
4117 if( rTextNode.GetNodes().IsDocNodes() )
4118 {
4119 const SwNumRule* pFormerNumRuleAtTextNode =
4120 rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
4121 if ( pFormerNumRuleAtTextNode )
4122 {
4123 sOldNumRule = pFormerNumRuleAtTextNode->GetName();
4124 }
4125 if ( rTextNode.IsEmptyListStyleDueToSetOutlineLevelAttr() )
4126 {
4127 const SwNumRuleItem& rNumRuleItem = rTextNode.GetTextColl()->GetNumRule();
4128 if ( !rNumRuleItem.GetValue().isEmpty() )
4129 {
4130 rTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4131 }
4132 }
4133 const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
4134 if ( pNumRuleAtTextNode )
4135 {
4136 bNumRuleSet = true;
4137 sNumRule = pNumRuleAtTextNode->GetName();
4138 }
4139 }
4140 HandleApplyTextNodeFormatChange(rTextNode, sNumRule, sOldNumRule, bNumRuleSet, bParagraphStyleChanged);
4141 }
4142
HandleApplyTextNodeFormatChange(SwTextNode & rTextNode,const UIName & sNumRule,const UIName & sOldNumRule,bool bNumRuleSet,bool bParagraphStyleChanged)4143 void HandleApplyTextNodeFormatChange( SwTextNode& rTextNode, const UIName& sNumRule, const UIName& sOldNumRule, bool bNumRuleSet, bool bParagraphStyleChanged )
4144 {
4145 if ( sNumRule != sOldNumRule )
4146 {
4147 if ( bNumRuleSet )
4148 {
4149 if (sNumRule.isEmpty())
4150 {
4151 rTextNode.RemoveFromList();
4152 if ( bParagraphStyleChanged )
4153 {
4154 lcl_ResetParAttrs(rTextNode);
4155 }
4156 }
4157 else
4158 {
4159 rTextNode.RemoveFromList();
4160 // If new list style is the outline style, apply outline
4161 // level as the list level.
4162 if (sNumRule==SwNumRule::GetOutlineRuleName())
4163 {
4164 // #i70748#
4165 OSL_ENSURE( rTextNode.GetTextColl()->IsAssignedToListLevelOfOutlineStyle(),
4166 "<HandleModifyAtTextNode()> - text node with outline style, but its paragraph style is not assigned to outline style." );
4167 const int nNewListLevel =
4168 rTextNode.GetTextColl()->GetAssignedOutlineStyleLevel();
4169 if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL )
4170 {
4171 rTextNode.SetAttrListLevel( nNewListLevel );
4172 }
4173 }
4174 rTextNode.AddToList();
4175 }
4176 }
4177 else // <sNumRule.Len() == 0 && sOldNumRule.Len() != 0>
4178 {
4179 rTextNode.RemoveFromList();
4180 if ( bParagraphStyleChanged )
4181 {
4182 lcl_ResetParAttrs(rTextNode);
4183 // #i70748#
4184 if ( rTextNode.GetAttr( RES_PARATR_OUTLINELEVEL, false ).GetValue() > 0 )
4185 {
4186 rTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
4187 }
4188 }
4189 }
4190 }
4191 else if (!sNumRule.isEmpty() && !rTextNode.IsInList())
4192 {
4193 rTextNode.AddToList();
4194 }
4195 }
4196 }
4197
ChgFormatColl(SwFormatColl * pNewColl,bool bSetListLevel)4198 SwFormatColl* SwTextNode::ChgFormatColl( SwFormatColl *pNewColl, bool bSetListLevel )
4199 {
4200 OSL_ENSURE( pNewColl,"ChgFormatColl: Collectionpointer has value 0." );
4201 assert( dynamic_cast<const SwTextFormatColl *>(pNewColl) && "ChgFormatColl: is not a Text Collection pointer." );
4202
4203 SwTextFormatColl *pOldColl = GetTextColl();
4204 if( pNewColl != pOldColl )
4205 {
4206 SetCalcHiddenCharFlags();
4207 SwContentNode::ChgFormatColl( pNewColl );
4208 OSL_ENSURE( !mbInSetOrResetAttr,
4209 "DEBUG OSL_ENSURE(ON - <SwTextNode::ChgFormatColl(..)> called during <Set/ResetAttr(..)>" );
4210 if ( !mbInSetOrResetAttr )
4211 {
4212 HandleModifyAtTextNodeFormatChange( *this );
4213 }
4214
4215 // reset fill information on parent style change
4216 if(maFillAttributes)
4217 {
4218 maFillAttributes.reset();
4219 }
4220 }
4221
4222 // only for real nodes-array
4223 if( GetNodes().IsDocNodes() )
4224 {
4225 ChgTextCollUpdateNum( pOldColl, static_cast<SwTextFormatColl *>(pNewColl), bSetListLevel );
4226 }
4227
4228 return pOldColl;
4229 }
4230
GetNum(SwRootFrame const * const pLayout,SwListRedlineType eRedline) const4231 const SwNodeNum* SwTextNode::GetNum(SwRootFrame const*const pLayout, SwListRedlineType eRedline) const
4232 {
4233 // invariant: it's only in list in Hide mode if it's in list in normal mode
4234 assert(mpNodeNum || !mpNodeNumRLHidden);
4235 return (pLayout && pLayout->IsHideRedlines()) || SwListRedlineType::HIDDEN == eRedline
4236 ? mpNodeNumRLHidden.get()
4237 : ( SwListRedlineType::ORIGTEXT == eRedline ? mpNodeNumOrig.get() : mpNodeNum.get() );
4238 }
4239
DoNum(std::function<void (SwNodeNum &)> const & rFunc)4240 void SwTextNode::DoNum(std::function<void (SwNodeNum &)> const& rFunc)
4241 {
4242 // temp. clear because GetActualListLevel() may be called and the assert
4243 // there triggered during update, which is unhelpful
4244 std::unique_ptr<SwNodeNum> pBackup = std::move(mpNodeNumRLHidden);
4245 std::unique_ptr<SwNodeNum> pBackup2 = std::move(mpNodeNumOrig);
4246 assert(mpNodeNum);
4247 rFunc(*mpNodeNum);
4248 if (pBackup)
4249 {
4250 mpNodeNumRLHidden = std::move(pBackup);
4251 rFunc(*mpNodeNumRLHidden);
4252 }
4253 if (pBackup2)
4254 {
4255 mpNodeNumOrig = std::move(pBackup2);
4256 rFunc(*mpNodeNumOrig);
4257 }
4258 }
4259
4260 SwNumberTree::tNumberVector
GetNumberVector(SwRootFrame const * const pLayout,SwListRedlineType eRedline) const4261 SwTextNode::GetNumberVector(SwRootFrame const*const pLayout, SwListRedlineType eRedline) const
4262 {
4263 if (SwNodeNum const*const pNum = GetNum(pLayout, eRedline))
4264 {
4265 return pNum->GetNumberVector();
4266 }
4267 else
4268 {
4269 SwNumberTree::tNumberVector aResult;
4270 return aResult;
4271 }
4272 }
4273
IsOutline() const4274 bool SwTextNode::IsOutline() const
4275 {
4276 bool bResult = false;
4277
4278 if ( GetAttrOutlineLevel() > 0 )
4279 {
4280 bResult = !IsInRedlines();
4281 }
4282 else
4283 {
4284 const SwNumRule* pRule( GetNum() ? GetNum()->GetNumRule() : nullptr );
4285 if ( pRule && pRule->IsOutlineRule() )
4286 {
4287 bResult = !IsInRedlines();
4288 }
4289 }
4290
4291 return bResult;
4292 }
4293
IsOutlineStateChanged() const4294 bool SwTextNode::IsOutlineStateChanged() const
4295 {
4296 return IsOutline() != m_bLastOutlineState;
4297 }
4298
UpdateOutlineState()4299 void SwTextNode::UpdateOutlineState()
4300 {
4301 m_bLastOutlineState = IsOutline();
4302 }
4303
GetAttrOutlineLevel(bool bInlineHeading) const4304 int SwTextNode::GetAttrOutlineLevel(bool bInlineHeading) const
4305 {
4306 sal_uInt16 nLevel = GetAttr(RES_PARATR_OUTLINELEVEL).GetValue();
4307 // not outline node, so if bIblineHeading = true, look for the
4308 // outline level of the inline heading (i.e the outline node in
4309 // an Inline Heading frame, which frame anchored as character to this node)
4310 if ( !nLevel && bInlineHeading && HasHints() )
4311 {
4312 // are we in a fly
4313 for ( size_t j = m_pSwpHints->Count(); j; )
4314 {
4315 SwTextAttr* const pHt = m_pSwpHints->Get( --j );
4316 if ( RES_TXTATR_FLYCNT == pHt->Which() )
4317 {
4318 SwFrameFormat* pFrameFormat = pHt->GetFlyCnt().GetFrameFormat();
4319 const SwFormat* pParent = pFrameFormat->DerivedFrom();
4320 SwFormatAnchor const& rAnchor(pFrameFormat->GetAnchor());
4321 bool bInlineHeadingFrame = pParent &&
4322 pParent->GetPoolFormatId() == RES_POOLFRM_INLINE_HEADING &&
4323 RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId();
4324 const SwNodeIndex* pNdIdx = bInlineHeadingFrame
4325 ? pFrameFormat->GetContent().GetContentIdx()
4326 : nullptr;
4327 const SwNodes* pNodesArray = (pNdIdx != nullptr)
4328 ? &(pNdIdx->GetNodes())
4329 : nullptr;
4330 const SwTextNode *pTextNode = (pNodesArray != nullptr)
4331 ? (*pNodesArray)[pNdIdx->GetIndex() + 1]->GetTextNode()
4332 : nullptr;
4333 if ( pTextNode )
4334 return pTextNode->GetAttrOutlineLevel();
4335 }
4336 }
4337 }
4338 return nLevel;
4339 }
4340
SetAttrOutlineLevel(int nLevel)4341 void SwTextNode::SetAttrOutlineLevel(int nLevel)
4342 {
4343 assert(0 <= nLevel && nLevel <= MAXLEVEL); // Level Out Of Range
4344 if ( 0 <= nLevel && nLevel <= MAXLEVEL )
4345 {
4346 SetAttr( SfxUInt16Item( RES_PARATR_OUTLINELEVEL,
4347 o3tl::narrowing<sal_uInt16>(nLevel) ) );
4348 }
4349 }
4350
GetAttrOutlineContentVisible() const4351 bool SwTextNode::GetAttrOutlineContentVisible() const
4352 {
4353 bool bOutlineContentVisibleAttr = true;
4354 const SfxGrabBagItem & rGrabBagItem = GetAttr(RES_PARATR_GRABBAG);
4355 auto it = rGrabBagItem.GetGrabBag().find(u"OutlineContentVisibleAttr"_ustr);
4356 if (it != rGrabBagItem.GetGrabBag().end())
4357 it->second >>= bOutlineContentVisibleAttr;
4358 return bOutlineContentVisibleAttr;
4359 }
4360
SetAttrOutlineContentVisible(bool bVisible)4361 void SwTextNode::SetAttrOutlineContentVisible(bool bVisible)
4362 {
4363 SfxGrabBagItem aGrabBagItem(
4364 RES_PARATR_GRABBAG,
4365 std::map<OUString, css::uno::Any>{
4366 { u"OutlineContentVisibleAttr"_ustr, css::uno::Any(bVisible) } });
4367 SetAttr(aGrabBagItem);
4368 }
4369
4370 // #i70748#
4371
SetEmptyListStyleDueToSetOutlineLevelAttr()4372 void SwTextNode::SetEmptyListStyleDueToSetOutlineLevelAttr()
4373 {
4374 if ( !mbEmptyListStyleSetDueToSetOutlineLevelAttr )
4375 {
4376 SetAttr( SwNumRuleItem() );
4377 mbEmptyListStyleSetDueToSetOutlineLevelAttr = true;
4378 }
4379 }
4380
ResetEmptyListStyleDueToResetOutlineLevelAttr()4381 void SwTextNode::ResetEmptyListStyleDueToResetOutlineLevelAttr()
4382 {
4383 if ( mbEmptyListStyleSetDueToSetOutlineLevelAttr )
4384 {
4385 ResetAttr( RES_PARATR_NUMRULE );
4386 mbEmptyListStyleSetDueToSetOutlineLevelAttr = false;
4387 }
4388 }
4389
SetAttrListLevel(int nLevel)4390 void SwTextNode::SetAttrListLevel( int nLevel )
4391 {
4392 if ( nLevel < 0 || nLevel >= MAXLEVEL )
4393 {
4394 assert(false); // invalid level
4395 return;
4396 }
4397
4398 SfxInt16Item aNewListLevelItem( RES_PARATR_LIST_LEVEL,
4399 static_cast<sal_Int16>(nLevel) );
4400 SetAttr( aNewListLevelItem );
4401 }
4402
HasAttrListLevel() const4403 bool SwTextNode::HasAttrListLevel() const
4404 {
4405 return GetpSwAttrSet() &&
4406 GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_LEVEL, false ) == SfxItemState::SET;
4407 }
4408
GetAttrListLevel() const4409 int SwTextNode::GetAttrListLevel() const
4410 {
4411 int nAttrListLevel = 0;
4412
4413 const SfxInt16Item& aListLevelItem =
4414 GetAttr( RES_PARATR_LIST_LEVEL );
4415 nAttrListLevel = static_cast<int>(aListLevelItem.GetValue());
4416
4417 return nAttrListLevel;
4418 }
4419
GetActualListLevel(SwListRedlineType eRedline) const4420 int SwTextNode::GetActualListLevel(SwListRedlineType eRedline) const
4421 {
4422 assert(SwListRedlineType::SHOW != eRedline ||
4423 !GetNum(nullptr, SwListRedlineType::SHOW) || !mpNodeNumRLHidden || // must be in sync
4424 GetNum(nullptr, SwListRedlineType::SHOW)->GetLevelInListTree() ==
4425 mpNodeNumRLHidden->GetLevelInListTree());
4426 return GetNum(nullptr, eRedline) ? GetNum(nullptr, eRedline)->GetLevelInListTree() : -1;
4427 }
4428
SetListRestart(bool bRestart)4429 void SwTextNode::SetListRestart( bool bRestart )
4430 {
4431 if ( !bRestart )
4432 {
4433 // attribute not contained in paragraph style's attribute set. Thus,
4434 // it can be reset to the attribute pool default by resetting the attribute.
4435 ResetAttr( RES_PARATR_LIST_ISRESTART );
4436 }
4437 else
4438 {
4439 SfxBoolItem aNewIsRestartItem( RES_PARATR_LIST_ISRESTART,
4440 true );
4441 SetAttr( aNewIsRestartItem );
4442 }
4443 }
4444
IsListRestart() const4445 bool SwTextNode::IsListRestart() const
4446 {
4447 const SfxBoolItem& aIsRestartItem = GetAttr( RES_PARATR_LIST_ISRESTART );
4448
4449 return aIsRestartItem.GetValue();
4450 }
4451
4452 /** Returns if the paragraph has a visible numbering or bullet.
4453 This includes all kinds of numbering/bullet/outlines.
4454 The concrete list label string has to be checked, too.
4455 */
HasVisibleNumberingOrBullet() const4456 bool SwTextNode::HasVisibleNumberingOrBullet() const
4457 {
4458 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
4459 if ( pRule && IsCountedInList())
4460 {
4461 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
4462 if (getIDocumentSettingAccess()->get(DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY))
4463 // True if we have something in label text or there is a non-empty
4464 // FollowedBy separator (space, tab or whatsoever)
4465 return rFormat.GetLabelFollowedBy() != SvxNumberFormat::LabelFollowedBy::NOTHING ||
4466 !pRule->MakeNumString(*GetNum()).isEmpty();
4467 else
4468 // #i87154#
4469 // Correction of #newlistlevelattrs#:
4470 // The numbering type has to be checked for bullet lists.
4471 return SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() ||
4472 !pRule->MakeNumString(*(GetNum())).isEmpty();
4473 }
4474
4475 return false;
4476 }
4477
SetAttrListRestartValue(SwNumberTree::tSwNumTreeNumber nNumber)4478 void SwTextNode::SetAttrListRestartValue( SwNumberTree::tSwNumTreeNumber nNumber )
4479 {
4480 const bool bChanged( HasAttrListRestartValue()
4481 ? GetAttrListRestartValue() != nNumber
4482 : nNumber != USHRT_MAX );
4483
4484 if ( !bChanged && HasAttrListRestartValue() )
4485 return;
4486
4487 if ( nNumber == USHRT_MAX )
4488 {
4489 ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
4490 }
4491 else
4492 {
4493 SfxInt16Item aNewListRestartValueItem( RES_PARATR_LIST_RESTARTVALUE,
4494 static_cast<sal_Int16>(nNumber) );
4495 SetAttr( aNewListRestartValueItem );
4496 }
4497 }
4498
HasAttrListRestartValue() const4499 bool SwTextNode::HasAttrListRestartValue() const
4500 {
4501 return GetpSwAttrSet() &&
4502 GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_RESTARTVALUE, false ) == SfxItemState::SET;
4503 }
GetAttrListRestartValue() const4504 SwNumberTree::tSwNumTreeNumber SwTextNode::GetAttrListRestartValue() const
4505 {
4506 OSL_ENSURE( HasAttrListRestartValue(),
4507 "<SwTextNode::GetAttrListRestartValue()> - only ask for list restart value, if attribute is set at text node." );
4508
4509 const SfxInt16Item& aListRestartValueItem =
4510 GetAttr( RES_PARATR_LIST_RESTARTVALUE );
4511 return static_cast<SwNumberTree::tSwNumTreeNumber>(aListRestartValueItem.GetValue());
4512 }
4513
GetActualListStartValue() const4514 SwNumberTree::tSwNumTreeNumber SwTextNode::GetActualListStartValue() const
4515 {
4516 SwNumberTree::tSwNumTreeNumber nListRestartValue = 1;
4517
4518 if ( IsListRestart() && HasAttrListRestartValue() )
4519 {
4520 nListRestartValue = GetAttrListRestartValue();
4521 }
4522 else
4523 {
4524 SwNumRule* pRule = GetNumRule();
4525 if ( pRule )
4526 {
4527 const SwNumFormat* pFormat =
4528 pRule->GetNumFormat( o3tl::narrowing<sal_uInt16>(GetAttrListLevel()) );
4529 if ( pFormat )
4530 {
4531 nListRestartValue = pFormat->GetStart();
4532 }
4533 }
4534 }
4535
4536 return nListRestartValue;
4537 }
4538
IsNotifiable() const4539 bool SwTextNode::IsNotifiable() const
4540 {
4541 return m_bNotifiable && IsNotificationEnabled();
4542 }
4543
IsNotificationEnabled() const4544 bool SwTextNode::IsNotificationEnabled() const
4545 {
4546 const SwDoc& rDoc = GetDoc();
4547 return !rDoc.IsInReading() && !rDoc.IsInDtor();
4548 }
4549
SetCountedInList(bool bCounted)4550 void SwTextNode::SetCountedInList( bool bCounted )
4551 {
4552 if ( bCounted )
4553 {
4554 // attribute not contained in paragraph style's attribute set. Thus,
4555 // it can be reset to the attribute pool default by resetting the attribute.
4556 ResetAttr( RES_PARATR_LIST_ISCOUNTED );
4557 }
4558 else
4559 {
4560 SfxBoolItem aIsCountedInListItem( RES_PARATR_LIST_ISCOUNTED, false );
4561 SetAttr( aIsCountedInListItem );
4562 }
4563 }
4564
IsCountedInList() const4565 bool SwTextNode::IsCountedInList() const
4566 {
4567 const SfxBoolItem& aIsCountedInListItem = GetAttr( RES_PARATR_LIST_ISCOUNTED );
4568
4569 return aIsCountedInListItem.GetValue();
4570 }
4571
FindList(SwTextNode * const pNode)4572 static SwList * FindList(SwTextNode *const pNode)
4573 {
4574 const OUString sListId = pNode->GetListId();
4575 if (!sListId.isEmpty())
4576 {
4577 auto & rIDLA(pNode->GetDoc().getIDocumentListsAccess());
4578 SwList* pList = rIDLA.getListByName( sListId );
4579 if ( pList == nullptr )
4580 {
4581 // Create corresponding list.
4582 SwNumRule* pNumRule = pNode->GetNumRule();
4583 if ( pNumRule )
4584 {
4585 pList = rIDLA.createList(sListId, pNode->GetNumRule()->GetName());
4586 }
4587 }
4588 OSL_ENSURE( pList != nullptr,
4589 "<SwTextNode::AddToList()> - no list for given list id. Serious defect" );
4590 return pList;
4591 }
4592 return nullptr;
4593 }
4594
AddToList()4595 void SwTextNode::AddToList()
4596 {
4597 if ( IsInList() )
4598 {
4599 OSL_FAIL( "<SwTextNode::AddToList()> - the text node is already added to a list. Serious defect" );
4600 return;
4601 }
4602
4603 SwList *const pList(FindList(this));
4604 if (!(pList && GetNodes().IsDocNodes())) // not for undo nodes
4605 return;
4606
4607 assert(!mpNodeNum);
4608 mpNodeNum.reset(new SwNodeNum(this, false));
4609 pList->InsertListItem(*mpNodeNum, SwListRedlineType::SHOW, GetAttrListLevel(), GetDoc());
4610
4611 // set redline lists
4612 // "default" list: visible items in Show Changes mode (tracked insertions and deletions)
4613 // "hidden" list: visible items in Hide Changes mode (tracked insertions, but not deletions)
4614 // "orig" list: visible items rejecting all changes (no tracked insertions and deletions)
4615 SwDocShell* pShell = GetDoc().GetDocShell();
4616 bool bRecordChanges = pShell && pShell->IsChangeRecording();
4617 if (!bRecordChanges || GetDoc().IsInXMLImport() || GetDoc().IsInWriterfilterImport() )
4618 {
4619 const SwRedlineTable& rRedTable = GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
4620 SwRedlineTable::size_type nRedlPos = GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Insert);
4621 // paragraph start is not in a tracked insertion
4622 if ( SwRedlineTable::npos == nRedlPos || GetIndex() <= rRedTable[nRedlPos]->Start()->GetNode().GetIndex() )
4623 {
4624 AddToListOrig();
4625
4626 // if the paragraph is not deleted, add to the "hidden" list, too
4627 SwRedlineTable::size_type nRedlPosDel = GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Delete);
4628 if ( SwRedlineTable::npos == nRedlPosDel )
4629 AddToListRLHidden();
4630 else
4631 {
4632 const SwNodeOffset nNdIdx = GetIndex();
4633 const SwRangeRedline* pTmp = rRedTable[nRedlPosDel];
4634 const SwPosition* pRStt = pTmp->Start();
4635 if (pRStt->GetNodeIndex() >= nNdIdx)
4636 {
4637 // paragraph is partly deleted, add to the "hidden" list, too
4638 AddToListRLHidden();
4639 }
4640 }
4641 }
4642 // inserted paragraph, e.g. during file load, add to the "hidden" list
4643 else if ( SwRedlineTable::npos != nRedlPos )
4644 AddToListRLHidden();
4645 }
4646 else if ( bRecordChanges )
4647 AddToListRLHidden();
4648
4649 // iterate all frames & if there's one with hidden layout...
4650 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> iter(*this);
4651 for (SwTextFrame* pFrame = iter.First(); pFrame && !mpNodeNumRLHidden; pFrame = iter.Next())
4652 {
4653 if (pFrame->getRootFrame()->IsHideRedlines())
4654 {
4655 if (pFrame->GetTextNodeForParaProps() == this)
4656 {
4657 AddToListRLHidden();
4658 }
4659 break; // assume it's consistent, need to check only once
4660 }
4661 }
4662 }
4663
AddToListRLHidden()4664 void SwTextNode::AddToListRLHidden()
4665 {
4666 if (mpNodeNumRLHidden)
4667 return;
4668
4669 SwList *const pList(FindList(this));
4670 if (pList)
4671 {
4672 assert(!mpNodeNumRLHidden);
4673 mpNodeNumRLHidden.reset(new SwNodeNum(this, true));
4674 pList->InsertListItem(*mpNodeNumRLHidden, SwListRedlineType::HIDDEN, GetAttrListLevel(), GetDoc());
4675 }
4676 }
4677
AddToListOrig()4678 void SwTextNode::AddToListOrig()
4679 {
4680 if (mpNodeNumOrig)
4681 return;
4682
4683 SwList *const pList(FindList(this));
4684 if (pList)
4685 {
4686 assert(!mpNodeNumOrig);
4687 mpNodeNumOrig.reset(new SwNodeNum(this, true));
4688 pList->InsertListItem(*mpNodeNumOrig, SwListRedlineType::ORIGTEXT, GetAttrListLevel(), GetDoc());
4689 }
4690 }
4691
RemoveFromList()4692 void SwTextNode::RemoveFromList()
4693 {
4694 // sw_redlinehide: ensure it's removed from the other half too!
4695 RemoveFromListRLHidden();
4696 RemoveFromListOrig();
4697 if ( IsInList() )
4698 {
4699 SwList::RemoveListItem(*mpNodeNum, GetDoc());
4700 mpNodeNum.reset();
4701
4702 SetWordCountDirty( true );
4703 }
4704 }
4705
RemoveFromListRLHidden()4706 void SwTextNode::RemoveFromListRLHidden()
4707 {
4708 if (mpNodeNumRLHidden) // direct access because RemoveFromList doesn't have layout
4709 {
4710 assert(mpNodeNumRLHidden->GetParent() || !GetNodes().IsDocNodes());
4711 SwList::RemoveListItem(*mpNodeNumRLHidden, GetDoc());
4712 mpNodeNumRLHidden.reset();
4713
4714 SetWordCountDirty( true );
4715 }
4716 }
4717
RemoveFromListOrig()4718 void SwTextNode::RemoveFromListOrig()
4719 {
4720 if (mpNodeNumOrig) // direct access because RemoveFromList doesn't have layout
4721 {
4722 assert(mpNodeNumOrig->GetParent() || !GetNodes().IsDocNodes());
4723 SwList::RemoveListItem(*mpNodeNumOrig, GetDoc());
4724 mpNodeNumOrig.reset();
4725
4726 SetWordCountDirty( true );
4727 }
4728 }
4729
IsInList() const4730 bool SwTextNode::IsInList() const
4731 {
4732 return GetNum() != nullptr && GetNum()->GetParent() != nullptr;
4733 }
4734
IsInListFromStyle() const4735 bool SwTextNode::IsInListFromStyle() const
4736 {
4737 if (IsInList())
4738 {
4739 const SwFormatColl* pFormatColl = GetFormatColl();
4740 if (pFormatColl->GetItemState(RES_PARATR_NUMRULE, true) == SfxItemState::SET)
4741 return true;
4742 }
4743 return false;
4744
4745 }
IsFirstOfNumRule(SwRootFrame const & rLayout) const4746 bool SwTextNode::IsFirstOfNumRule(SwRootFrame const& rLayout) const
4747 {
4748 bool bResult = false;
4749
4750 SwNodeNum const*const pNum(GetNum(&rLayout));
4751 if (pNum && pNum->GetNumRule())
4752 bResult = pNum->IsFirst();
4753
4754 return bResult;
4755 }
4756
SetListId(OUString const & rListId)4757 void SwTextNode::SetListId(OUString const& rListId)
4758 {
4759 const SfxStringItem& rListIdItem =
4760 GetAttr( RES_PARATR_LIST_ID );
4761 if (rListIdItem.GetValue() != rListId)
4762 {
4763 if (rListId.isEmpty())
4764 {
4765 ResetAttr( RES_PARATR_LIST_ID );
4766 }
4767 else
4768 {
4769 SfxStringItem aNewListIdItem(RES_PARATR_LIST_ID, rListId);
4770 SetAttr( aNewListIdItem );
4771 }
4772 }
4773 }
4774
GetListId() const4775 OUString SwTextNode::GetListId() const
4776 {
4777 const SfxStringItem& rListIdItem =
4778 GetAttr( RES_PARATR_LIST_ID );
4779 const OUString& sListId {rListIdItem.GetValue()};
4780
4781 // As long as no explicit list id attribute is set, use the list id of
4782 // the list, which has been created for the applied list style.
4783 if (sListId.isEmpty())
4784 {
4785 SwNumRule* pRule = GetNumRule();
4786 if ( pRule )
4787 {
4788 return pRule->GetDefaultListId();
4789 }
4790 }
4791
4792 return sListId;
4793 }
4794
4795 /** Determines, if the list level indent attributes can be applied to the
4796 paragraph.
4797
4798 The list level indents can be applied to the paragraph under the one
4799 of following conditions:
4800 - the list style is directly applied to the paragraph and the paragraph
4801 has no own indent attributes.
4802 - the list style is applied to the paragraph through one of its paragraph
4803 styles, the paragraph has no own indent attributes and on the paragraph
4804 style hierarchy from the paragraph to the paragraph style with the
4805 list style no indent attributes are found.
4806
4807 @return bitmask
4808 */
AreListLevelIndentsApplicable() const4809 ::sw::ListLevelIndents SwTextNode::AreListLevelIndentsApplicable() const
4810 {
4811 ::sw::ListLevelIndents ret(::sw::ListLevelIndents::No);
4812 if (AreListLevelIndentsApplicableImpl(RES_MARGIN_FIRSTLINE))
4813 {
4814 ret |= ::sw::ListLevelIndents::FirstLine;
4815 }
4816 if (AreListLevelIndentsApplicableImpl(RES_MARGIN_TEXTLEFT))
4817 {
4818 ret |= ::sw::ListLevelIndents::LeftMargin;
4819 }
4820 return ret;
4821 }
4822
AreListLevelIndentsApplicableImpl(sal_uInt16 const nWhich) const4823 bool SwTextNode::AreListLevelIndentsApplicableImpl(sal_uInt16 const nWhich) const
4824 {
4825 bool bAreListLevelIndentsApplicable( true );
4826
4827 if ( !GetNum() || !GetNum()->GetNumRule() )
4828 {
4829 // no list style applied to paragraph
4830 bAreListLevelIndentsApplicable = false;
4831 }
4832 else if ( HasSwAttrSet() &&
4833 GetpSwAttrSet()->GetItemState(nWhich, false) == SfxItemState::SET)
4834 {
4835 // paragraph has hard-set indent attributes
4836 bAreListLevelIndentsApplicable = false;
4837 }
4838 else if ( HasSwAttrSet() &&
4839 GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
4840 {
4841 // list style is directly applied to paragraph and paragraph has no
4842 // hard-set indent attributes
4843 bAreListLevelIndentsApplicable = true;
4844 }
4845 else
4846 {
4847 // list style is applied through one of the paragraph styles and
4848 // paragraph has no hard-set indent attributes
4849
4850 // check, paragraph's
4851 const SwTextFormatColl* pColl = GetTextColl();
4852 while ( pColl )
4853 {
4854 if (pColl->GetAttrSet().GetItemState(nWhich, false) == SfxItemState::SET)
4855 {
4856 // indent attributes found in the paragraph style hierarchy.
4857 bAreListLevelIndentsApplicable = false;
4858 break;
4859 }
4860
4861 if ( pColl->GetAttrSet().GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
4862 {
4863 // paragraph style with the list style found and until now no
4864 // indent attributes are found in the paragraph style hierarchy.
4865 bAreListLevelIndentsApplicable = true;
4866 break;
4867 }
4868
4869 pColl = dynamic_cast<const SwTextFormatColl*>(pColl->DerivedFrom());
4870 OSL_ENSURE( pColl,
4871 "<SwTextNode::AreListLevelIndentsApplicable()> - something wrong in paragraph's style hierarchy. The applied list style is not found." );
4872 }
4873 }
4874
4875 return bAreListLevelIndentsApplicable;
4876 }
4877
4878 /** Retrieves the list tab stop position, if the paragraph's list level defines
4879 one and this list tab stop has to merged into the tap stops of the paragraph
4880
4881 @param nListTabStopPosition
4882 output parameter - containing the list tab stop position
4883
4884 @return boolean - indicating, if a list tab stop position is provided
4885 */
GetListTabStopPosition(tools::Long & nListTabStopPosition) const4886 bool SwTextNode::GetListTabStopPosition( tools::Long& nListTabStopPosition ) const
4887 {
4888 bool bListTabStopPositionProvided(false);
4889
4890 const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
4891 if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 )
4892 {
4893 const SwNumFormat& rFormat = pNumRule->Get( o3tl::narrowing<sal_uInt16>(GetActualListLevel()) );
4894 if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT &&
4895 rFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB )
4896 {
4897 bListTabStopPositionProvided = true;
4898 nListTabStopPosition = rFormat.GetListtabPos();
4899
4900 if ( getIDocumentSettingAccess()->get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) )
4901 {
4902 // tab stop position are treated to be relative to the "before text"
4903 // indent value of the paragraph. Thus, adjust <nListTabStopPos>.
4904 if (AreListLevelIndentsApplicable() & ::sw::ListLevelIndents::LeftMargin)
4905 {
4906 nListTabStopPosition -= rFormat.GetIndentAt();
4907 }
4908 else if (!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
4909 {
4910 SvxTextLeftMarginItem const aItem(GetSwAttrSet().GetTextLeftMargin());
4911 nListTabStopPosition -= aItem.ResolveTextLeft({});
4912 }
4913 }
4914 }
4915 }
4916
4917 return bListTabStopPositionProvided;
4918 }
4919
GetLabelFollowedBy() const4920 OUString SwTextNode::GetLabelFollowedBy() const
4921 {
4922 const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
4923 if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 )
4924 {
4925 const SwNumFormat& rFormat = pNumRule->Get( o3tl::narrowing<sal_uInt16>(GetActualListLevel()) );
4926 if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
4927 {
4928 return rFormat.GetLabelFollowedByAsString();
4929 }
4930 }
4931
4932 return OUString();
4933 }
4934
CalcHiddenCharFlags() const4935 void SwTextNode::CalcHiddenCharFlags() const
4936 {
4937 sal_Int32 nStartPos;
4938 sal_Int32 nEndPos;
4939 // Update of the flags is done inside GetBoundsOfHiddenRange()
4940 SwScriptInfo::GetBoundsOfHiddenRange( *this, 0, nStartPos, nEndPos );
4941 }
4942
4943 // #i12836# enhanced pdf export
IsHidden() const4944 bool SwTextNode::IsHidden() const
4945 {
4946 if ( IsHiddenByParaField() || HasHiddenCharAttribute( true ) )
4947 return true;
4948
4949 const SwSectionNode* pSectNd = FindSectionNode();
4950 return pSectNd && pSectNd->GetSection().IsHiddenFlag();
4951 }
4952
4953 namespace {
4954 // Helper class for special handling of setting attributes at text node:
4955 // In constructor an instance of the helper class recognize whose attributes
4956 // are set and perform corresponding actions before the intrinsic set of
4957 // attributes has been taken place.
4958 // In the destructor - after the attributes have been set at the text
4959 // node - corresponding actions are performed.
4960 // The following is handled:
4961 // (1) When the list style attribute - RES_PARATR_NUMRULE - is set,
4962 // (A) list style attribute is empty -> the text node is removed from
4963 // its list.
4964 // (B) list style attribute is not empty
4965 // (a) text node has no list style -> add text node to its list after
4966 // the attributes have been set.
4967 // (b) text node has list style -> change of list style is notified
4968 // after the attributes have been set.
4969 // (2) When the list id attribute - RES_PARATR_LIST_ID - is set and changed,
4970 // the text node is removed from its current list before the attributes
4971 // are set and added to its new list after the attributes have been set.
4972 // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is set
4973 // and changed after the attributes have been set
4974 // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is set
4975 // and changed after the attributes have been set
4976 // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE -
4977 // is set and changed after the attributes have been set
4978 // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is set
4979 // and changed after the attributes have been set
4980 // (7) Set or Reset empty list style due to changed outline level - RES_PARATR_OUTLINELEVEL.
4981 class HandleSetAttrAtTextNode
4982 {
4983 public:
4984 HandleSetAttrAtTextNode( SwTextNode& rTextNode,
4985 const SfxPoolItem& pItem );
4986 HandleSetAttrAtTextNode( SwTextNode& rTextNode,
4987 const SfxItemSet& rItemSet );
4988 ~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE;
4989
4990 private:
4991 SwTextNode& mrTextNode;
4992 bool mbAddTextNodeToList;
4993 bool mbUpdateListLevel;
4994 bool mbUpdateListRestart;
4995 bool mbUpdateListCount;
4996 // #i70748#
4997 bool mbOutlineLevelSet;
4998 };
4999
HandleSetAttrAtTextNode(SwTextNode & rTextNode,const SfxPoolItem & pItem)5000 HandleSetAttrAtTextNode::HandleSetAttrAtTextNode( SwTextNode& rTextNode,
5001 const SfxPoolItem& pItem )
5002 : mrTextNode( rTextNode ),
5003 mbAddTextNodeToList( false ),
5004 mbUpdateListLevel( false ),
5005 mbUpdateListRestart( false ),
5006 mbUpdateListCount( false ),
5007 // #i70748#
5008 mbOutlineLevelSet( false )
5009 {
5010 switch ( pItem.Which() )
5011 {
5012 // handle RES_PARATR_NUMRULE
5013 case RES_PARATR_NUMRULE:
5014 {
5015 mrTextNode.RemoveFromList();
5016
5017 const SwNumRuleItem& rNumRuleItem = pItem.StaticWhichCast(RES_PARATR_NUMRULE);
5018 if ( !rNumRuleItem.GetValue().isEmpty() )
5019 {
5020 mbAddTextNodeToList = true;
5021 // #i105562#
5022
5023 mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5024 }
5025 }
5026 break;
5027
5028 // handle RES_PARATR_LIST_ID
5029 case RES_PARATR_LIST_ID:
5030 {
5031 const SfxStringItem& rListIdItem = pItem.StaticWhichCast(RES_PARATR_LIST_ID);
5032 OSL_ENSURE( rListIdItem.GetValue().getLength() > 0,
5033 "<HandleSetAttrAtTextNode(..)> - empty list id attribute not expected. Serious defect." );
5034 const OUString sListIdOfTextNode = rTextNode.GetListId();
5035 if ( rListIdItem.GetValue() != sListIdOfTextNode )
5036 {
5037 mbAddTextNodeToList = true;
5038 if ( mrTextNode.IsInList() )
5039 {
5040 mrTextNode.RemoveFromList();
5041 }
5042 }
5043 }
5044 break;
5045
5046 // handle RES_PARATR_LIST_LEVEL
5047 case RES_PARATR_LIST_LEVEL:
5048 {
5049 const SfxInt16Item& aListLevelItem = pItem.StaticWhichCast(RES_PARATR_LIST_LEVEL);
5050 if ( aListLevelItem.GetValue() != mrTextNode.GetAttrListLevel() )
5051 {
5052 mbUpdateListLevel = true;
5053 }
5054 }
5055 break;
5056
5057 // handle RES_PARATR_LIST_ISRESTART
5058 case RES_PARATR_LIST_ISRESTART:
5059 {
5060 const SfxBoolItem& aListIsRestartItem = pItem.StaticWhichCast(RES_PARATR_LIST_ISRESTART);
5061 if ( aListIsRestartItem.GetValue() !=
5062 mrTextNode.IsListRestart() )
5063 {
5064 mbUpdateListRestart = true;
5065 }
5066 }
5067 break;
5068
5069 // handle RES_PARATR_LIST_RESTARTVALUE
5070 case RES_PARATR_LIST_RESTARTVALUE:
5071 {
5072 const SfxInt16Item& aListRestartValueItem = pItem.StaticWhichCast(RES_PARATR_LIST_RESTARTVALUE);
5073 if ( !mrTextNode.HasAttrListRestartValue() ||
5074 aListRestartValueItem.GetValue() != mrTextNode.GetAttrListRestartValue() )
5075 {
5076 mbUpdateListRestart = true;
5077 }
5078 }
5079 break;
5080
5081 // handle RES_PARATR_LIST_ISCOUNTED
5082 case RES_PARATR_LIST_ISCOUNTED:
5083 {
5084 const SfxBoolItem& aIsCountedInListItem = pItem.StaticWhichCast(RES_PARATR_LIST_ISCOUNTED);
5085 if ( aIsCountedInListItem.GetValue() !=
5086 mrTextNode.IsCountedInList() )
5087 {
5088 mbUpdateListCount = true;
5089 }
5090 }
5091 break;
5092
5093 // #i70748#
5094 // handle RES_PARATR_OUTLINELEVEL
5095 case RES_PARATR_OUTLINELEVEL:
5096 {
5097 const SfxUInt16Item& aOutlineLevelItem = pItem.StaticWhichCast(RES_PARATR_OUTLINELEVEL);
5098 if ( aOutlineLevelItem.GetValue() != mrTextNode.GetAttrOutlineLevel() )
5099 {
5100 mbOutlineLevelSet = true;
5101 }
5102 }
5103 break;
5104 }
5105
5106 }
5107
HandleSetAttrAtTextNode(SwTextNode & rTextNode,const SfxItemSet & rItemSet)5108 HandleSetAttrAtTextNode::HandleSetAttrAtTextNode( SwTextNode& rTextNode,
5109 const SfxItemSet& rItemSet )
5110 : mrTextNode( rTextNode ),
5111 mbAddTextNodeToList( false ),
5112 mbUpdateListLevel( false ),
5113 mbUpdateListRestart( false ),
5114 mbUpdateListCount( false ),
5115 // #i70748#
5116 mbOutlineLevelSet( false )
5117 {
5118 // handle RES_PARATR_NUMRULE
5119 if ( const SwNumRuleItem* pNumRuleItem = rItemSet.GetItemIfSet( RES_PARATR_NUMRULE, false ) )
5120 {
5121 mrTextNode.RemoveFromList();
5122
5123 if ( !pNumRuleItem->GetValue().isEmpty() )
5124 {
5125 mbAddTextNodeToList = true;
5126 // #i70748#
5127 mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5128 }
5129 }
5130
5131 // handle RES_PARATR_LIST_ID
5132 if ( const SfxStringItem* pListIdItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_ID, false ) )
5133 {
5134 const OUString sListIdOfTextNode = mrTextNode.GetListId();
5135 if ( pListIdItem->GetValue() != sListIdOfTextNode )
5136 {
5137 mbAddTextNodeToList = true;
5138 if ( mrTextNode.IsInList() )
5139 {
5140 mrTextNode.RemoveFromList();
5141 }
5142 }
5143 }
5144
5145 // handle RES_PARATR_LIST_LEVEL
5146 if ( const SfxInt16Item* pListLevelItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_LEVEL, false ) )
5147 {
5148 if (pListLevelItem->GetValue() != mrTextNode.GetAttrListLevel())
5149 {
5150 mbUpdateListLevel = true;
5151 }
5152 }
5153
5154 // handle RES_PARATR_LIST_ISRESTART
5155 if ( const SfxBoolItem* pListIsRestartItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_ISRESTART, false ) )
5156 {
5157 if (pListIsRestartItem->GetValue() != mrTextNode.IsListRestart())
5158 {
5159 mbUpdateListRestart = true;
5160 }
5161 }
5162
5163 // handle RES_PARATR_LIST_RESTARTVALUE
5164 if ( const SfxInt16Item* pListRestartValueItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_RESTARTVALUE, false ) )
5165 {
5166 if ( !mrTextNode.HasAttrListRestartValue() ||
5167 pListRestartValueItem->GetValue() != mrTextNode.GetAttrListRestartValue() )
5168 {
5169 mbUpdateListRestart = true;
5170 }
5171 }
5172
5173 // handle RES_PARATR_LIST_ISCOUNTED
5174 if ( const SfxBoolItem* pIsCountedInListItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_ISCOUNTED, false ) )
5175 {
5176 if (pIsCountedInListItem->GetValue() != mrTextNode.IsCountedInList())
5177 {
5178 mbUpdateListCount = true;
5179 }
5180 }
5181
5182 // #i70748#
5183 // handle RES_PARATR_OUTLINELEVEL
5184 if ( const SfxUInt16Item* pOutlineLevelItem = rItemSet.GetItemIfSet( RES_PARATR_OUTLINELEVEL, false ) )
5185 {
5186 if (pOutlineLevelItem->GetValue() != mrTextNode.GetAttrOutlineLevel())
5187 {
5188 mbOutlineLevelSet = true;
5189 }
5190 }
5191 }
5192
~HandleSetAttrAtTextNode()5193 HandleSetAttrAtTextNode::~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
5194 {
5195 if ( mbAddTextNodeToList )
5196 {
5197 SwNumRule* pNumRuleAtTextNode = mrTextNode.GetNumRule();
5198 if ( pNumRuleAtTextNode )
5199 {
5200 mrTextNode.AddToList();
5201 }
5202 }
5203 else
5204 {
5205 if ( mbUpdateListLevel && mrTextNode.IsInList() )
5206 {
5207 auto const nLevel(mrTextNode.GetAttrListLevel());
5208 const SwDoc& rDoc(mrTextNode.GetDoc());
5209 mrTextNode.DoNum(
5210 [nLevel, &rDoc](SwNodeNum & rNum) { rNum.SetLevelInListTree(nLevel, rDoc); });
5211 }
5212
5213 if ( mbUpdateListRestart && mrTextNode.IsInList() )
5214 {
5215 const SwDoc& rDoc(mrTextNode.GetDoc());
5216 mrTextNode.DoNum(
5217 [&rDoc](SwNodeNum & rNum) {
5218 rNum.InvalidateMe();
5219 rNum.NotifyInvalidSiblings(rDoc);
5220 });
5221 }
5222
5223 if (mbUpdateListCount && mrTextNode.IsInList() && HasNumberingWhichNeedsLayoutUpdate(mrTextNode))
5224 {
5225 // Repaint all text frames that belong to this numbering to avoid outdated generated
5226 // numbers.
5227 const SwDoc& rDoc(mrTextNode.GetDoc());
5228 mrTextNode.DoNum(
5229 [&rDoc](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(rDoc); });
5230 }
5231 }
5232
5233 // #i70748#
5234 if (!mbOutlineLevelSet)
5235 return;
5236
5237 mrTextNode.GetNodes().UpdateOutlineNode(mrTextNode);
5238 if (mrTextNode.GetAttrOutlineLevel() == 0)
5239 {
5240 mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5241 }
5242 else
5243 {
5244 if ( mrTextNode.GetSwAttrSet().GetItemState( RES_PARATR_NUMRULE )
5245 != SfxItemState::SET )
5246 {
5247 mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
5248 }
5249 }
5250 }
5251 // End of class <HandleSetAttrAtTextNode>
5252 }
5253
SetAttr(const SfxPoolItem & pItem)5254 bool SwTextNode::SetAttr( const SfxPoolItem& pItem )
5255 {
5256 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5257 mbInSetOrResetAttr = true;
5258
5259 HandleSetAttrAtTextNode aHandleSetAttr( *this, pItem );
5260
5261 bool bRet = SwContentNode::SetAttr( pItem );
5262
5263 mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5264
5265 return bRet;
5266 }
5267
SetAttr(const SfxItemSet & rSet)5268 bool SwTextNode::SetAttr( const SfxItemSet& rSet )
5269 {
5270 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5271 mbInSetOrResetAttr = true;
5272
5273 HandleSetAttrAtTextNode aHandleSetAttr( *this, rSet );
5274
5275 bool bRet = SwContentNode::SetAttr( rSet );
5276
5277 mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5278
5279 return bRet;
5280 }
5281
SetInSwUndo(bool bInUndo)5282 void SwTextNode::SetInSwUndo(bool bInUndo)
5283 {
5284 m_bInUndo = bInUndo;
5285 }
5286
5287 namespace {
5288 // Helper class for special handling of resetting attributes at text node:
5289 // In constructor an instance of the helper class recognize whose attributes
5290 // are reset and perform corresponding actions before the intrinsic reset of
5291 // attributes has been taken place.
5292 // In the destructor - after the attributes have been reset at the text
5293 // node - corresponding actions are performed.
5294 // The following is handled:
5295 // (1) When the list style attribute - RES_PARATR_NUMRULE - is reset,
5296 // the text is removed from its list before the attributes have been reset.
5297 // (2) When the list id attribute - RES_PARATR_LIST_ID - is reset,
5298 // the text is removed from its list before the attributes have been reset.
5299 // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is reset.
5300 // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is reset.
5301 // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE - is reset.
5302 // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is reset.
5303 // (7) Reset empty list style, if outline level attribute - RES_PARATR_OUTLINELEVEL - is reset.
5304 class HandleResetAttrAtTextNode
5305 {
5306 public:
5307 HandleResetAttrAtTextNode( SwTextNode& rTextNode,
5308 const sal_uInt16 nWhich1,
5309 sal_uInt16 nWhich2 );
5310 HandleResetAttrAtTextNode( SwTextNode& rTextNode,
5311 const std::vector<sal_uInt16>& rWhichArr );
5312 explicit HandleResetAttrAtTextNode( SwTextNode& rTextNode );
5313
5314 ~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE;
5315
5316 private:
5317 SwTextNode& mrTextNode;
5318 bool mbListStyleOrIdReset;
5319 bool mbUpdateListLevel;
5320 bool mbUpdateListRestart;
5321 bool mbUpdateListCount;
5322
5323 void init( sal_uInt16 nWhich, bool& rbRemoveFromList );
5324 };
5325
HandleResetAttrAtTextNode(SwTextNode & rTextNode,const sal_uInt16 nWhich1,sal_uInt16 nWhich2)5326 HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode,
5327 const sal_uInt16 nWhich1,
5328 sal_uInt16 nWhich2 )
5329 : mrTextNode( rTextNode ),
5330 mbListStyleOrIdReset( false ),
5331 mbUpdateListLevel( false ),
5332 mbUpdateListRestart( false ),
5333 mbUpdateListCount( false )
5334 {
5335 if ( nWhich2 < nWhich1 )
5336 nWhich2 = nWhich1;
5337 bool bRemoveFromList( false );
5338 for ( sal_uInt16 nWhich = nWhich1; nWhich <= nWhich2; ++nWhich )
5339 init( nWhich, bRemoveFromList );
5340 if ( bRemoveFromList && mrTextNode.IsInList() )
5341 mrTextNode.RemoveFromList();
5342 }
5343
HandleResetAttrAtTextNode(SwTextNode & rTextNode,const std::vector<sal_uInt16> & rWhichArr)5344 HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode,
5345 const std::vector<sal_uInt16>& rWhichArr )
5346 : mrTextNode( rTextNode ),
5347 mbListStyleOrIdReset( false ),
5348 mbUpdateListLevel( false ),
5349 mbUpdateListRestart( false ),
5350 mbUpdateListCount( false )
5351 {
5352 bool bRemoveFromList( false );
5353 for ( sal_uInt16 nWhich : rWhichArr )
5354 init( nWhich, bRemoveFromList );
5355 if ( bRemoveFromList && mrTextNode.IsInList() )
5356 mrTextNode.RemoveFromList();
5357 }
5358
HandleResetAttrAtTextNode(SwTextNode & rTextNode)5359 HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode )
5360 : mrTextNode( rTextNode ),
5361 mbListStyleOrIdReset( true ),
5362 mbUpdateListLevel( false ),
5363 mbUpdateListRestart( false ),
5364 mbUpdateListCount( false )
5365 {
5366 if ( rTextNode.IsInList() )
5367 {
5368 rTextNode.RemoveFromList();
5369 }
5370 // #i70748#
5371 mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5372 }
5373
init(sal_uInt16 rWhich,bool & rbRemoveFromList)5374 void HandleResetAttrAtTextNode::init( sal_uInt16 rWhich, bool& rbRemoveFromList )
5375 {
5376 if ( rWhich == RES_PARATR_NUMRULE )
5377 {
5378 rbRemoveFromList = rbRemoveFromList ||
5379 mrTextNode.GetNumRule() != nullptr;
5380 mbListStyleOrIdReset = true;
5381 }
5382 else if ( rWhich == RES_PARATR_LIST_ID )
5383 {
5384 rbRemoveFromList = rbRemoveFromList ||
5385 ( mrTextNode.GetpSwAttrSet() &&
5386 mrTextNode.GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_ID, false ) == SfxItemState::SET );
5387 mbListStyleOrIdReset = true;
5388 }
5389 else if ( rWhich == RES_PARATR_OUTLINELEVEL )
5390 mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5391 else if ( rWhich == RES_BACKGROUND )
5392 mrTextNode.ResetAttr( XATTR_FILL_FIRST, XATTR_FILL_LAST );
5393
5394 if ( !rbRemoveFromList )
5395 {
5396 // RES_PARATR_LIST_LEVEL
5397 mbUpdateListLevel = mbUpdateListLevel ||
5398 ( rWhich == RES_PARATR_LIST_LEVEL &&
5399 mrTextNode.HasAttrListLevel() );
5400
5401 // RES_PARATR_LIST_ISRESTART and RES_PARATR_LIST_RESTARTVALUE
5402 mbUpdateListRestart = mbUpdateListRestart ||
5403 ( rWhich == RES_PARATR_LIST_ISRESTART &&
5404 mrTextNode.IsListRestart() ) ||
5405 ( rWhich == RES_PARATR_LIST_RESTARTVALUE &&
5406 mrTextNode.HasAttrListRestartValue() );
5407
5408 // RES_PARATR_LIST_ISCOUNTED
5409 mbUpdateListCount = mbUpdateListCount ||
5410 ( rWhich == RES_PARATR_LIST_ISCOUNTED &&
5411 !mrTextNode.IsCountedInList() );
5412 }
5413 }
5414
~HandleResetAttrAtTextNode()5415 HandleResetAttrAtTextNode::~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
5416 {
5417 if ( mbListStyleOrIdReset && !mrTextNode.IsInList() )
5418 {
5419 // check, if in spite of the reset of the list style or the list id
5420 // the paragraph still has to be added to a list.
5421 if (mrTextNode.GetNumRule() && !mrTextNode.GetListId().isEmpty())
5422 {
5423 // #i96062#
5424 // If paragraph has no list level attribute set and list style
5425 // is the outline style, apply outline level as the list level.
5426 if ( !mrTextNode.HasAttrListLevel() &&
5427 mrTextNode.GetNumRule()->GetName()==SwNumRule::GetOutlineRuleName() &&
5428 mrTextNode.GetTextColl()->IsAssignedToListLevelOfOutlineStyle() )
5429 {
5430 int nNewListLevel = mrTextNode.GetTextColl()->GetAssignedOutlineStyleLevel();
5431 if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL )
5432 {
5433 mrTextNode.SetAttrListLevel( nNewListLevel );
5434 }
5435 }
5436 mrTextNode.AddToList();
5437 }
5438 // #i70748#
5439 // #i105562#
5440 else
5441 {
5442 if (mrTextNode.GetpSwAttrSet()
5443 && mrTextNode.GetAttr(RES_PARATR_OUTLINELEVEL, false).GetValue() > 0)
5444 {
5445 mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
5446 }
5447 }
5448 }
5449
5450 if ( !mrTextNode.IsInList() )
5451 return;
5452
5453 // just incredibly slow to do this
5454 if (comphelper::IsFuzzing())
5455 return;
5456
5457 if ( mbUpdateListLevel )
5458 {
5459 auto const nLevel(mrTextNode.GetAttrListLevel());
5460 const SwDoc& rDoc(mrTextNode.GetDoc());
5461 mrTextNode.DoNum(
5462 [nLevel, &rDoc](SwNodeNum & rNum) { rNum.SetLevelInListTree(nLevel, rDoc); });
5463 }
5464
5465 if ( mbUpdateListRestart )
5466 {
5467 const SwDoc& rDoc(mrTextNode.GetDoc());
5468 mrTextNode.DoNum(
5469 [&rDoc](SwNodeNum & rNum) {
5470 rNum.InvalidateMe();
5471 rNum.NotifyInvalidSiblings(rDoc);
5472 });
5473 }
5474
5475 if ( mbUpdateListCount )
5476 {
5477 const SwDoc& rDoc(mrTextNode.GetDoc());
5478 mrTextNode.DoNum(
5479 [&rDoc](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(rDoc); });
5480 }
5481 }
5482 // End of class <HandleResetAttrAtTextNode>
5483 }
5484
ResetAttr(sal_uInt16 nWhich1,sal_uInt16 nWhich2)5485 bool SwTextNode::ResetAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
5486 {
5487 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5488 mbInSetOrResetAttr = true;
5489
5490 HandleResetAttrAtTextNode aHandleResetAttr( *this, nWhich1, nWhich2 );
5491
5492 bool bRet = SwContentNode::ResetAttr( nWhich1, nWhich2 );
5493
5494 mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5495
5496 return bRet;
5497 }
5498
ResetAttr(const std::vector<sal_uInt16> & rWhichArr)5499 bool SwTextNode::ResetAttr( const std::vector<sal_uInt16>& rWhichArr )
5500 {
5501 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5502 mbInSetOrResetAttr = true;
5503
5504 HandleResetAttrAtTextNode aHandleResetAttr( *this, rWhichArr );
5505
5506 bool bRet = SwContentNode::ResetAttr( rWhichArr );
5507
5508 mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5509
5510 return bRet;
5511 }
5512
ResetAllAttr()5513 sal_uInt16 SwTextNode::ResetAllAttr()
5514 {
5515 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5516 mbInSetOrResetAttr = true;
5517
5518 HandleResetAttrAtTextNode aHandleResetAttr( *this );
5519
5520 const sal_uInt16 nRet = SwContentNode::ResetAllAttr();
5521
5522 mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5523
5524 return nRet;
5525 }
5526
dumpAsXml(xmlTextWriterPtr pWriter) const5527 void SwTextNode::dumpAsXml(xmlTextWriterPtr pWriter) const
5528 {
5529 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextNode"));
5530 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
5531 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
5532
5533 OUString sText = GetText();
5534 for (int i = 0; i < 32; ++i)
5535 sText = sText.replace(i, '*');
5536 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_Text"));
5537 (void)xmlTextWriterWriteString(pWriter, BAD_CAST(sText.toUtf8().getStr()));
5538 (void)xmlTextWriterEndElement(pWriter);
5539
5540 if (GetFormatColl())
5541 {
5542 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColl"));
5543 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetFormatColl()->GetName().toString().toUtf8().getStr()));
5544 (void)xmlTextWriterEndElement(pWriter);
5545 }
5546
5547 if (HasSwAttrSet())
5548 {
5549 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwAttrSet"));
5550 GetSwAttrSet().dumpAsXml(pWriter);
5551 (void)xmlTextWriterEndElement(pWriter);
5552 }
5553
5554 if (HasHints())
5555 {
5556 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwpHints"));
5557 const SwpHints& rHints = GetSwpHints();
5558 for (size_t i = 0; i < rHints.Count(); ++i)
5559 rHints.Get(i)->dumpAsXml(pWriter);
5560 (void)xmlTextWriterEndElement(pWriter);
5561 }
5562
5563 (void)xmlTextWriterEndElement(pWriter);
5564 }
5565
GetRsid(sal_Int32 nStt,sal_Int32 nEnd) const5566 sal_uInt32 SwTextNode::GetRsid( sal_Int32 nStt, sal_Int32 nEnd ) const
5567 {
5568 SfxItemSetFixed<RES_CHRATR_RSID, RES_CHRATR_RSID> aSet( const_cast<SwAttrPool&>((GetDoc().GetAttrPool())) );
5569 if (GetParaAttr(aSet, nStt, nEnd))
5570 {
5571 const SvxRsidItem* pRsid = aSet.GetItem<SvxRsidItem>(RES_CHRATR_RSID);
5572 if( pRsid )
5573 return pRsid->GetValue();
5574 }
5575
5576 return 0;
5577 }
5578
GetParRsid() const5579 sal_uInt32 SwTextNode::GetParRsid() const
5580 {
5581 return reinterpret_cast<const SvxRsidItem&>(GetAttr( RES_PARATR_RSID )).GetValue();
5582 }
5583
CompareParRsid(const SwTextNode & rTextNode) const5584 bool SwTextNode::CompareParRsid( const SwTextNode &rTextNode ) const
5585 {
5586 sal_uInt32 nThisRsid = GetParRsid();
5587 sal_uInt32 nRsid = rTextNode.GetParRsid();
5588
5589 return nThisRsid == nRsid;
5590 }
5591
CompareRsid(const SwTextNode & rTextNode,sal_Int32 nStt1,sal_Int32 nStt2) const5592 bool SwTextNode::CompareRsid( const SwTextNode &rTextNode, sal_Int32 nStt1, sal_Int32 nStt2 ) const
5593 {
5594 sal_uInt32 nThisRsid = GetRsid( nStt1, nStt1 );
5595 sal_uInt32 nRsid = rTextNode.GetRsid( nStt2, nStt2 );
5596
5597 return nThisRsid == nRsid;
5598 }
5599
5600 // sw::Metadatable
GetRegistry()5601 ::sfx2::IXmlIdRegistry& SwTextNode::GetRegistry()
5602 {
5603 return GetDoc().GetXmlIdRegistry();
5604 }
5605
IsInClipboard() const5606 bool SwTextNode::IsInClipboard() const
5607 {
5608 return GetDoc().IsClipBoard();
5609 }
5610
IsInUndo() const5611 bool SwTextNode::IsInUndo() const
5612 {
5613 return GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
5614 }
5615
IsInContent() const5616 bool SwTextNode::IsInContent() const
5617 {
5618 return !GetDoc().IsInHeaderFooter( *this );
5619 }
5620
HandleNonLegacyHint(const SfxHint & rHint)5621 void SwTextNode::HandleNonLegacyHint(const SfxHint& rHint)
5622 {
5623 assert(!dynamic_cast<const sw::LegacyModifyHint*>(&rHint));
5624 sw::TextNodeNotificationSuppressor(*this);
5625 CallSwClientNotify(rHint);
5626
5627 SwDoc& rDoc = GetDoc();
5628 // #125329# - assure that text node is in document nodes array
5629 if ( !rDoc.IsInDtor() && &rDoc.GetNodes() == &GetNodes() )
5630 {
5631 rDoc.GetNodes().UpdateOutlineNode(*this);
5632 }
5633 }
5634
UpdateDocPos(const SwTwips nDocPos,const sal_uInt32 nIndex)5635 void SwTextNode::UpdateDocPos(const SwTwips nDocPos, const sal_uInt32 nIndex)
5636 {
5637 const sw::DocPosUpdateAtIndex aHint(nDocPos, *this, nIndex);
5638 CallSwClientNotify(aHint);
5639 }
5640
TriggerNodeUpdate(const sw::LegacyModifyHint & rHint)5641 void SwTextNode::TriggerNodeUpdate(const sw::LegacyModifyHint& rHint)
5642 {
5643 const auto pOldValue = rHint.m_pOld;
5644 const auto pNewValue = rHint.m_pNew;
5645 {
5646 sw::TextNodeNotificationSuppressor(*this);
5647
5648 if ( !mbInSetOrResetAttr )
5649 {
5650 HandleModifyAtTextNode( *this, pOldValue, pNewValue );
5651 }
5652
5653 SwContentNode::SwClientNotify(*this, rHint);
5654
5655 SwDoc& rDoc = GetDoc();
5656 // #125329# - assure that text node is in document nodes array
5657 if ( !rDoc.IsInDtor() && &rDoc.GetNodes() == &GetNodes() )
5658 {
5659 rDoc.GetNodes().UpdateOutlineNode(*this);
5660 }
5661 }
5662 }
5663
TriggerNodeUpdate(const SfxHint & rHint)5664 void SwTextNode::TriggerNodeUpdate(const SfxHint& rHint)
5665 {
5666 sw::TextNodeNotificationSuppressor(*this);
5667
5668 SwContentNode::SwClientNotify(*this, rHint);
5669
5670 SwDoc& rDoc = GetDoc();
5671 // #125329# - assure that text node is in document nodes array
5672 if ( !rDoc.IsInDtor() && &rDoc.GetNodes() == &GetNodes() )
5673 {
5674 rDoc.GetNodes().UpdateOutlineNode(*this);
5675 }
5676 }
5677
TriggerNodeUpdate(const sw::AttrSetChangeHint & rHint)5678 void SwTextNode::TriggerNodeUpdate(const sw::AttrSetChangeHint& rHint)
5679 {
5680 const SwAttrSetChg* pOldValue = rHint.m_pOld;
5681 const SwAttrSetChg* pNewValue = rHint.m_pNew;
5682 {
5683 sw::TextNodeNotificationSuppressor(*this);
5684
5685 // reset fill information
5686 if (maFillAttributes && pNewValue)
5687 {
5688 bool bReset(false);
5689
5690 // ..on ItemChange from DrawingLayer FillAttributes
5691 SfxItemIter aIter(*pNewValue->GetChgSet());
5692
5693 for(const SfxPoolItem* pItem = aIter.GetCurItem(); pItem && !bReset; pItem = aIter.NextItem())
5694 {
5695 bReset = !IsInvalidItem(pItem) && pItem->Which() >= XATTR_FILL_FIRST && pItem->Which() <= XATTR_FILL_LAST;
5696 }
5697
5698 if(bReset)
5699 {
5700 maFillAttributes.reset();
5701 }
5702 }
5703
5704 if ( !mbInSetOrResetAttr )
5705 {
5706 HandleModifyAtTextNode( *this, pOldValue, pNewValue );
5707 }
5708
5709 SwContentNode::SwClientNotify(*this, rHint);
5710
5711 SwDoc& rDoc = GetDoc();
5712 // #125329# - assure that text node is in document nodes array
5713 if ( !rDoc.IsInDtor() && &rDoc.GetNodes() == &GetNodes() )
5714 {
5715 rDoc.GetNodes().UpdateOutlineNode(*this);
5716 }
5717 }
5718 }
5719
TriggerNodeUpdate(const SwFormatChangeHint & rHint)5720 void SwTextNode::TriggerNodeUpdate(const SwFormatChangeHint& rHint)
5721 {
5722 sw::TextNodeNotificationSuppressor(*this);
5723
5724 // Override Modify so that deleting styles works properly (outline
5725 // numbering!).
5726 // Never call ChgTextCollUpdateNum for Nodes in Undo.
5727 if( rHint.m_pOldFormat
5728 && rHint.m_pNewFormat
5729 && GetRegisteredIn() == rHint.m_pNewFormat
5730 && GetNodes().IsDocNodes() )
5731 {
5732 assert(dynamic_cast<const SwTextFormatColl*>(rHint.m_pNewFormat));
5733 if (const SwTextFormatColl* pTxtFmtColOld = dynamic_cast<const SwTextFormatColl*>(rHint.m_pOldFormat))
5734 {
5735 ChgTextCollUpdateNum(
5736 pTxtFmtColOld, static_cast<const SwTextFormatColl*>(rHint.m_pNewFormat));
5737 }
5738 }
5739
5740 // reset fill information
5741 if (maFillAttributes && rHint.m_pNewFormat)
5742 {
5743 // ..on format change (e.g. style changed)
5744 maFillAttributes.reset();
5745 }
5746
5747 if ( !mbInSetOrResetAttr )
5748 {
5749 HandleModifyAtTextNodeFormatChange( *this );
5750 }
5751
5752 SwContentNode::SwClientNotify(*this, rHint);
5753
5754 SwDoc& rDoc = GetDoc();
5755 // #125329# - assure that text node is in document nodes array
5756 if ( !rDoc.IsInDtor() && &rDoc.GetNodes() == &GetNodes() )
5757 {
5758 rDoc.GetNodes().UpdateOutlineNode(*this);
5759 }
5760 }
5761
SwClientNotify(const SwModify & rModify,const SfxHint & rHint)5762 void SwTextNode::SwClientNotify( const SwModify& rModify, const SfxHint& rHint )
5763 {
5764 if(rHint.GetId() == SfxHintId::SwAutoFormatUsedHint)
5765 {
5766 static_cast<const sw::AutoFormatUsedHint&>(rHint).CheckNode(this);
5767 }
5768 else if(SfxHintId::SwRemoveUnoObject == rHint.GetId())
5769 {
5770 TriggerNodeUpdate(static_cast<const sw::RemoveUnoObjectHint&>(rHint));
5771 }
5772 else if (rHint.GetId() == SfxHintId::SwObjectDying)
5773 {
5774 auto pDyingHint = static_cast<const sw::ObjectDyingHint*>(&rHint);
5775 TriggerNodeUpdate(*pDyingHint);
5776 }
5777 else if (rHint.GetId() == SfxHintId::SwLegacyModify)
5778 {
5779 auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
5780 TriggerNodeUpdate(*pLegacyHint);
5781 }
5782 else if (rHint.GetId() == SfxHintId::SwUpdateAttr)
5783 {
5784 auto pUpdateHint = static_cast<const sw::UpdateAttrHint*>(&rHint);
5785 TriggerNodeUpdate(*pUpdateHint);
5786 }
5787 else if (rHint.GetId() == SfxHintId::SwAttrSetChange)
5788 {
5789 auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
5790 TriggerNodeUpdate(*pChangeHint);
5791 }
5792 else if (rHint.GetId() == SfxHintId::SwFormatChange)
5793 {
5794 auto pChangeHint = static_cast<const SwFormatChangeHint*>(&rHint);
5795 TriggerNodeUpdate(*pChangeHint);
5796 }
5797 else if (rHint.GetId() == SfxHintId::SwVirtPageNumHint)
5798 {
5799 CallSwClientNotify(rHint);
5800 }
5801 else if (rHint.GetId() == SfxHintId::SwAttr)
5802 {
5803 if (&rModify == GetRegisteredIn())
5804 ChkCondColl();
5805 }
5806 }
5807
5808 uno::Reference< rdf::XMetadatable >
MakeUnoObject()5809 SwTextNode::MakeUnoObject()
5810 {
5811 const rtl::Reference<SwXParagraph> xMeta(
5812 SwXParagraph::CreateXParagraph(GetDoc(), this, nullptr));
5813 return xMeta;
5814 }
5815
getSdrAllFillAttributesHelper() const5816 drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwTextNode::getSdrAllFillAttributesHelper() const
5817 {
5818 // create SdrAllFillAttributesHelper on demand
5819 if(!maFillAttributes)
5820 {
5821 const_cast< SwTextNode* >(this)->maFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(GetSwAttrSet());
5822 }
5823
5824 return maFillAttributes;
5825 }
5826
SetXParagraph(rtl::Reference<SwXParagraph> const & xParagraph)5827 void SwTextNode::SetXParagraph(rtl::Reference<SwXParagraph> const & xParagraph)
5828 {
5829 m_wXParagraph = xParagraph.get();
5830 }
5831
5832 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
5833