xref: /core/sw/source/core/docnode/node.cxx (revision 9d3b6364)
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 <editeng/protitem.hxx>
22 #include <osl/diagnose.h>
23 #include <tools/gen.hxx>
24 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
25 #include <com/sun/star/i18n/XBreakIterator.hpp>
26 #include <fmtcntnt.hxx>
27 #include <fmtanchr.hxx>
28 #include <frmfmt.hxx>
29 #include <txtftn.hxx>
30 #include <ftnfrm.hxx>
31 #include <doc.hxx>
32 #include <node.hxx>
33 #include <ndindex.hxx>
34 #include <numrule.hxx>
35 #include <swtable.hxx>
36 #include <ndtxt.hxx>
37 #include <pam.hxx>
38 #include <swcache.hxx>
39 #include <section.hxx>
40 #include <cntfrm.hxx>
41 #include <flyfrm.hxx>
42 #include <txtfrm.hxx>
43 #include <tabfrm.hxx>
44 #include <viewsh.hxx>
45 #include <paratr.hxx>
46 #include <ftnidx.hxx>
47 #include <fmtftn.hxx>
48 #include <fmthdft.hxx>
49 #include <frmatr.hxx>
50 #include <fmtautofmt.hxx>
51 #include <frmtool.hxx>
52 #include <pagefrm.hxx>
53 #include <node2lay.hxx>
54 #include <pagedesc.hxx>
55 #include <fmtpdsc.hxx>
56 #include <breakit.hxx>
57 #include <SwStyleNameMapper.hxx>
58 #include <scriptinfo.hxx>
59 #include <rootfrm.hxx>
60 #include <istyleaccess.hxx>
61 #include <IDocumentListItems.hxx>
62 #include <DocumentSettingManager.hxx>
63 #include <IDocumentLinksAdministration.hxx>
64 #include <IDocumentRedlineAccess.hxx>
65 #include <IDocumentLayoutAccess.hxx>
66 #include <calbck.hxx>
67 #include <ndole.hxx>
68 #include <memory>
69 #include <swcrsr.hxx>
70 #include <hints.hxx>
71 #include <frameformats.hxx>
72 #ifdef DBG_UTIL
73 #include <sal/backtrace.hxx>
74 #endif
75 
76 using namespace ::com::sun::star::i18n;
77 
78 
79 /*
80  * Some local helper functions for the attribute set handle of a content node.
81  * Since the attribute set of a content node may not be modified directly,
82  * we always have to create a new SwAttrSet, do the modifications, and get
83  * a new handle from the style access
84  */
85 
86 namespace AttrSetHandleHelper
87 {
88 
89 static void GetNewAutoStyle( std::shared_ptr<const SfxItemSet>& rpAttrSet,
90                       const SwContentNode& rNode,
91                       SwAttrSet const & rNewAttrSet )
92 {
93     const SwAttrSet* pAttrSet = static_cast<const SwAttrSet*>(rpAttrSet.get());
94     if( rNode.GetModifyAtAttr() )
95         const_cast<SwAttrSet*>(pAttrSet)->SetModifyAtAttr( nullptr );
96     IStyleAccess& rSA = pAttrSet->GetPool()->GetDoc()->GetIStyleAccess();
97     rpAttrSet = rSA.getAutomaticStyle( rNewAttrSet, rNode.IsTextNode() ?
98                                                      IStyleAccess::AUTO_STYLE_PARA :
99                                                      IStyleAccess::AUTO_STYLE_NOTXT );
100     const bool bSetModifyAtAttr = const_cast<SwAttrSet*>(static_cast<const SwAttrSet*>(rpAttrSet.get()))->SetModifyAtAttr( &rNode );
101     rNode.SetModifyAtAttr( bSetModifyAtAttr );
102 }
103 
104 static void SetParent( std::shared_ptr<const SfxItemSet>& rpAttrSet,
105                 const SwContentNode& rNode,
106                 const SwFormat* pParentFormat,
107                 const SwFormat* pConditionalFormat )
108 {
109     const SwAttrSet* pAttrSet = static_cast<const SwAttrSet*>(rpAttrSet.get());
110     OSL_ENSURE( pAttrSet, "no SwAttrSet" );
111     OSL_ENSURE( pParentFormat || !pConditionalFormat, "ConditionalFormat without ParentFormat?" );
112 
113     const SwAttrSet* pParentSet = pParentFormat ? &pParentFormat->GetAttrSet() : nullptr;
114 
115     if ( pParentSet == pAttrSet->GetParent() )
116         return;
117 
118     SwAttrSet aNewSet( *pAttrSet );
119     aNewSet.SetParent( pParentSet );
120     aNewSet.ClearItem( RES_FRMATR_STYLE_NAME );
121     aNewSet.ClearItem( RES_FRMATR_CONDITIONAL_STYLE_NAME );
122 
123     if ( pParentFormat )
124     {
125         OUString sVal;
126         SwStyleNameMapper::FillProgName( pParentFormat->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
127         const SfxStringItem aAnyFormatColl( RES_FRMATR_STYLE_NAME, sVal );
128         aNewSet.Put( aAnyFormatColl );
129 
130         if ( pConditionalFormat != pParentFormat )
131             SwStyleNameMapper::FillProgName( pConditionalFormat->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
132 
133         const SfxStringItem aFormatColl( RES_FRMATR_CONDITIONAL_STYLE_NAME, sVal );
134         aNewSet.Put( aFormatColl );
135     }
136 
137     GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
138 }
139 
140 static const SfxPoolItem* Put( std::shared_ptr<const SfxItemSet>& rpAttrSet,
141                         const SwContentNode& rNode,
142                         const SfxPoolItem& rAttr )
143 {
144     SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
145     const SfxPoolItem* pRet = aNewSet.Put( rAttr );
146     if ( pRet )
147         GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
148     return pRet;
149 }
150 
151 static bool Put( std::shared_ptr<const SfxItemSet>& rpAttrSet, const SwContentNode& rNode,
152          const SfxItemSet& rSet )
153 {
154     SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
155 
156     // #i76273# Robust
157     std::optional<SfxItemSetFixed<RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME>> pStyleNames;
158     if ( SfxItemState::SET == rSet.GetItemState( RES_FRMATR_STYLE_NAME, false ) )
159     {
160         pStyleNames.emplace( *aNewSet.GetPool() );
161         pStyleNames->Put( aNewSet );
162     }
163 
164     const bool bRet = aNewSet.Put( rSet );
165 
166     // #i76273# Robust
167     if ( pStyleNames )
168     {
169         aNewSet.Put( *pStyleNames );
170     }
171 
172     if ( bRet )
173         GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
174 
175     return bRet;
176 }
177 
178 static bool Put_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
179             const SwContentNode& rNode, const SfxPoolItem& rAttr,
180             SwAttrSet* pOld, SwAttrSet* pNew )
181 {
182     SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
183 
184     // for a correct broadcast, we need to do a SetModifyAtAttr with the items
185     // from aNewSet. The 'regular' SetModifyAtAttr is done in GetNewAutoStyle
186     if( rNode.GetModifyAtAttr() )
187         aNewSet.SetModifyAtAttr( &rNode );
188 
189     const bool bRet = aNewSet.Put_BC( rAttr, pOld, pNew );
190 
191     if ( bRet )
192         GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
193 
194     return bRet;
195 }
196 
197 static bool Put_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
198             const SwContentNode& rNode, const SfxItemSet& rSet,
199             SwAttrSet* pOld, SwAttrSet* pNew )
200 {
201     SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
202 
203     // #i76273# Robust
204     std::optional<SfxItemSetFixed<RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME>> pStyleNames;
205     if ( SfxItemState::SET == rSet.GetItemState( RES_FRMATR_STYLE_NAME, false ) )
206     {
207         pStyleNames.emplace( *aNewSet.GetPool() );
208         pStyleNames->Put( aNewSet );
209     }
210 
211     // for a correct broadcast, we need to do a SetModifyAtAttr with the items
212     // from aNewSet. The 'regular' SetModifyAtAttr is done in GetNewAutoStyle
213     if( rNode.GetModifyAtAttr() )
214         aNewSet.SetModifyAtAttr( &rNode );
215 
216     const bool bRet = aNewSet.Put_BC( rSet, pOld, pNew );
217 
218     // #i76273# Robust
219     if ( pStyleNames )
220     {
221         aNewSet.Put( *pStyleNames );
222     }
223 
224     if ( bRet )
225         GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
226 
227     return bRet;
228 }
229 
230 static sal_uInt16 ClearItem_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
231                      const SwContentNode& rNode, sal_uInt16 nWhich,
232                      SwAttrSet* pOld, SwAttrSet* pNew )
233 {
234     SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
235     if( rNode.GetModifyAtAttr() )
236         aNewSet.SetModifyAtAttr( &rNode );
237     const sal_uInt16 nRet = aNewSet.ClearItem_BC( nWhich, pOld, pNew );
238     if ( nRet )
239         GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
240     return nRet;
241 }
242 
243 static sal_uInt16 ClearItem_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
244                      const SwContentNode& rNode,
245                      sal_uInt16 nWhich1, sal_uInt16 nWhich2,
246                      SwAttrSet* pOld, SwAttrSet* pNew )
247 {
248     SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
249     if( rNode.GetModifyAtAttr() )
250         aNewSet.SetModifyAtAttr( &rNode );
251     const sal_uInt16 nRet = aNewSet.ClearItem_BC( nWhich1, nWhich2, pOld, pNew );
252     if ( nRet )
253         GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
254     return nRet;
255 }
256 
257 }
258 
259 /** Returns the section level at the position given by aIndex.
260  *
261  * We use the following logic:
262  * S = Start, E = End, C = ContentNode
263  * Level   0 = E
264  *         1 = S E
265  *         2 = SC
266  *
267  * All EndNodes of the BaseSection have level 0
268  * All StartNodes of the BaseSection have level 1
269  */
270 sal_uInt16 SwNode::GetSectionLevel() const
271 {
272     // EndNode of a BaseSection? They are always 0!
273     if( IsEndNode() && SwNodeOffset(0) == m_pStartOfSection->StartOfSectionIndex() )
274         return 0;
275 
276     sal_uInt16 nLevel;
277     const SwNode* pNode = IsStartNode() ? this : m_pStartOfSection;
278     for( nLevel = 1; SwNodeOffset(0) != pNode->StartOfSectionIndex(); ++nLevel )
279         pNode = pNode->m_pStartOfSection;
280     return IsEndNode() ? nLevel-1 : nLevel;
281 }
282 
283 #ifdef DBG_UTIL
284 tools::Long SwNode::s_nSerial = 0;
285 #endif
286 
287 SwNode::SwNode( const SwNodeIndex &rWhere, const SwNodeType nNdType )
288     : m_nNodeType( nNdType )
289     , m_nAFormatNumLvl( 0 )
290     , m_bIgnoreDontExpand( false)
291     , m_eMerge(Merge::None)
292 #ifdef DBG_UTIL
293     , m_nSerial( s_nSerial++)
294 #endif
295     , m_pStartOfSection( nullptr )
296 {
297     if( !rWhere.GetIndex() )
298         return;
299 
300     SwNodes& rNodes = const_cast<SwNodes&> (rWhere.GetNodes());
301     SwNode* pNd = rNodes[ rWhere.GetIndex() -1 ];
302     rNodes.InsertNode( this, rWhere );
303     m_pStartOfSection = pNd->GetStartNode();
304     if( nullptr == m_pStartOfSection )
305     {
306         m_pStartOfSection = pNd->m_pStartOfSection;
307         if( pNd->GetEndNode() )     // Skip EndNode ? Section
308         {
309             pNd = m_pStartOfSection;
310             m_pStartOfSection = pNd->m_pStartOfSection;
311         }
312     }
313 }
314 
315 /** Inserts a node into the rNodes array at the rWhere position
316  *
317  * @param rNodes the variable array in that the node will be inserted
318  * @param nPos position within the array where the node will be inserted
319  * @param nNdType the type of node to insert
320  */
321 SwNode::SwNode( SwNodes& rNodes, SwNodeOffset nPos, const SwNodeType nNdType )
322     : m_nNodeType( nNdType )
323     , m_nAFormatNumLvl( 0 )
324     , m_bIgnoreDontExpand( false)
325     , m_eMerge(Merge::None)
326 #ifdef DBG_UTIL
327     , m_nSerial( s_nSerial++)
328 #endif
329     , m_pStartOfSection( nullptr )
330 {
331     if( !nPos )
332         return;
333 
334     SwNode* pNd = rNodes[ nPos - 1 ];
335     rNodes.InsertNode( this, nPos );
336     m_pStartOfSection = pNd->GetStartNode();
337     if( nullptr == m_pStartOfSection )
338     {
339         m_pStartOfSection = pNd->m_pStartOfSection;
340         if( pNd->GetEndNode() )     // Skip EndNode ? Section!
341         {
342             pNd = m_pStartOfSection;
343             m_pStartOfSection = pNd->m_pStartOfSection;
344         }
345     }
346 }
347 
348 SwNode::~SwNode()
349 {
350     assert(m_aAnchoredFlys.empty() || GetDoc().IsInDtor()); // must all be deleted
351     InvalidateInSwCache(RES_OBJECTDYING);
352     assert(!IsInCache());
353 }
354 
355 /// Find the TableNode in which it is located.
356 /// If we're not in a table: return 0
357 SwTableNode* SwNode::FindTableNode()
358 {
359     if( IsTableNode() )
360         return GetTableNode();
361     SwStartNode* pTmp = m_pStartOfSection;
362     while( !pTmp->IsTableNode() && pTmp->GetIndex() )
363         pTmp = pTmp->m_pStartOfSection;
364     return pTmp->GetTableNode();
365 }
366 
367 /// Is the node located in the visible area of the Shell?
368 bool SwNode::IsInVisibleArea( SwViewShell const * pSh ) const
369 {
370     bool bRet = false;
371     const SwContentNode* pNd;
372 
373     if( SwNodeType::Start & m_nNodeType )
374     {
375         SwNodeIndex aIdx( *this );
376         pNd = GetNodes().GoNext( &aIdx );
377     }
378     else if( SwNodeType::End & m_nNodeType )
379     {
380         SwNodeIndex aIdx( *EndOfSectionNode() );
381         pNd = SwNodes::GoPrevious( &aIdx );
382     }
383     else
384         pNd = GetContentNode();
385 
386     if( !pSh )
387         // Get the Shell from the Doc
388         pSh = GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
389 
390     if( pSh )
391     {
392         const SwFrame* pFrame;
393         if (pNd && nullptr != (pFrame = pNd->getLayoutFrame(pSh->GetLayout(), nullptr, nullptr)))
394         {
395 
396             if ( pFrame->IsInTab() )
397                 pFrame = pFrame->FindTabFrame();
398 
399             if( !pFrame->isFrameAreaDefinitionValid() )
400             {
401                 do
402                 {
403                     pFrame = pFrame->FindPrev();
404                 }
405                 while ( pFrame && !pFrame->isFrameAreaDefinitionValid() );
406             }
407 
408             if( !pFrame || pSh->VisArea().Overlaps( pFrame->getFrameArea() ) )
409                 bRet = true;
410         }
411     }
412 
413     return bRet;
414 }
415 
416 bool SwNode::IsInProtectSect() const
417 {
418     const SwNode* pNd = SwNodeType::Section == m_nNodeType ? m_pStartOfSection : this;
419     const SwSectionNode* pSectNd = pNd->FindSectionNode();
420     return pSectNd && pSectNd->GetSection().IsProtectFlag();
421 }
422 
423 /// Does the node contain anything protected?
424 /// I.e.: Area/Frame/Table rows/... including the Anchor for
425 /// Frames/Footnotes/...
426 bool SwNode::IsProtect() const
427 {
428     const SwNode* pNd = SwNodeType::Section == m_nNodeType ? m_pStartOfSection : this;
429     const SwStartNode* pSttNd = pNd->FindSectionNode();
430     if( pSttNd && static_cast<const SwSectionNode*>(pSttNd)->GetSection().IsProtectFlag() )
431         return true;
432 
433     pSttNd = FindTableBoxStartNode();
434     if( nullptr != pSttNd )
435     {
436         SwContentFrame* pCFrame;
437         if( IsContentNode() && nullptr != (pCFrame = static_cast<const SwContentNode*>(this)->getLayoutFrame( GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) ))
438             return pCFrame->IsProtected();
439 
440         const SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().
441                                         GetTableBox( pSttNd->GetIndex() );
442         //Robust #149568
443         if( pBox && pBox->GetFrameFormat()->GetProtect().IsContentProtected() )
444             return true;
445     }
446 
447     SwFrameFormat* pFlyFormat = GetFlyFormat();
448     if( pFlyFormat )
449     {
450         if (pFlyFormat->GetProtect().IsContentProtected())
451             return true;
452         const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
453         const SwPosition* pAnchorPos = rAnchor.GetContentAnchor();
454         if (!pAnchorPos)
455             return false;
456         const SwNode& rAnchorNd = pAnchorPos->nNode.GetNode();
457         return &rAnchorNd != this && rAnchorNd.IsProtect();
458     }
459 
460     pSttNd = FindFootnoteStartNode();
461     if( nullptr != pSttNd )
462     {
463         const SwTextFootnote* pTFootnote = GetDoc().GetFootnoteIdxs().SeekEntry(
464                                 SwNodeIndex( *pSttNd ) );
465         if( pTFootnote )
466             return pTFootnote->GetTextNode().IsProtect();
467     }
468 
469     return false;
470 }
471 
472 /// Find the PageDesc that is used to format this node. If the Layout is available,
473 /// we search through that. Else we can only do it the hard way by searching onwards through the nodes.
474 const SwPageDesc* SwNode::FindPageDesc( SwNodeOffset* pPgDescNdIdx ) const
475 {
476     if ( !GetNodes().IsDocNodes() )
477     {
478         return nullptr;
479     }
480 
481     const SwPageDesc* pPgDesc = nullptr;
482 
483     const SwContentNode* pNode;
484     if( SwNodeType::Start & m_nNodeType )
485     {
486         SwNodeIndex aIdx( *this );
487         pNode = GetNodes().GoNext( &aIdx );
488     }
489     else if( SwNodeType::End & m_nNodeType )
490     {
491         SwNodeIndex aIdx( *EndOfSectionNode() );
492         pNode = SwNodes::GoPrevious( &aIdx );
493     }
494     else
495     {
496         pNode = GetContentNode();
497         if( pNode )
498             pPgDesc = static_cast<const SwFormatPageDesc&>(pNode->GetAttr( RES_PAGEDESC )).GetPageDesc();
499     }
500 
501     // Are we going through the layout?
502     if( !pPgDesc )
503     {
504         const SwFrame* pFrame;
505         const SwPageFrame* pPage;
506         if (pNode && nullptr != (pFrame = pNode->getLayoutFrame(pNode->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)) &&
507             nullptr != ( pPage = pFrame->FindPageFrame() ) )
508         {
509             pPgDesc = pPage->GetPageDesc();
510             if ( pPgDescNdIdx )
511             {
512                 *pPgDescNdIdx = pNode->GetIndex();
513             }
514         }
515     }
516 
517     if( !pPgDesc )
518     {
519         // Thus via the nodes array
520         const SwDoc& rDoc = GetDoc();
521         const SwNode* pNd = this;
522         const SwStartNode* pSttNd;
523         if( pNd->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() &&
524             nullptr != ( pSttNd = pNd->FindFlyStartNode() ) )
525         {
526             // Find the right Anchor first
527             const SwFrameFormat* pFormat = nullptr;
528             const SwFrameFormats& rFormats = *rDoc.GetSpzFrameFormats();
529 
530             for( size_t n = 0; n < rFormats.size(); ++n )
531             {
532                 const SwFrameFormat* pFrameFormat = rFormats[ n ];
533                 const SwFormatContent& rContent = pFrameFormat->GetContent();
534                 if( rContent.GetContentIdx() &&
535                     &rContent.GetContentIdx()->GetNode() == static_cast<SwNode const *>(pSttNd) )
536                 {
537                     pFormat = pFrameFormat;
538                     break;
539                 }
540             }
541 
542             if( pFormat )
543             {
544                 const SwFormatAnchor* pAnchor = &pFormat->GetAnchor();
545                 if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) &&
546                     pAnchor->GetContentAnchor() )
547                 {
548                     pNd = &pAnchor->GetContentAnchor()->nNode.GetNode();
549                     const SwNode* pFlyNd = pNd->FindFlyStartNode();
550                     while( pFlyNd )
551                     {
552                         // Get up through the Anchor
553                         size_t n;
554                         for( n = 0; n < rFormats.size(); ++n )
555                         {
556                             const SwFrameFormat* pFrameFormat = rFormats[ n ];
557                             const SwNodeIndex* pIdx = pFrameFormat->GetContent().
558                                                         GetContentIdx();
559                             if( pIdx && pFlyNd == &pIdx->GetNode() )
560                             {
561                                 if( pFormat == pFrameFormat )
562                                 {
563                                     pNd = pFlyNd;
564                                     pFlyNd = nullptr;
565                                     break;
566                                 }
567                                 pAnchor = &pFrameFormat->GetAnchor();
568                                 if ((RndStdIds::FLY_AT_PAGE == pAnchor->GetAnchorId()) ||
569                                     !pAnchor->GetContentAnchor() )
570                                 {
571                                     pFlyNd = nullptr;
572                                     break;
573                                 }
574 
575                                 pFlyNd = pAnchor->GetContentAnchor()->nNode.
576                                         GetNode().FindFlyStartNode();
577                                 break;
578                             }
579                         }
580                         if( n >= rFormats.size() )
581                         {
582                             OSL_ENSURE( false, "FlySection, but no Format found" );
583                             return nullptr;
584                         }
585                     }
586                 }
587             }
588             // pNd should now contain the correct Anchor or it's still this
589         }
590 
591         if( pNd->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() )
592         {
593             if( pNd->GetIndex() > GetNodes().GetEndOfAutotext().GetIndex() )
594             {
595                 pPgDesc = &rDoc.GetPageDesc( 0 );
596                 pNd = nullptr;
597             }
598             else
599             {
600                 // Find the Body text node
601                 if( nullptr != ( pSttNd = pNd->FindHeaderStartNode() ) ||
602                     nullptr != ( pSttNd = pNd->FindFooterStartNode() ))
603                 {
604                     // Then find this StartNode in the PageDescs
605                     sal_uInt16 nId;
606                     UseOnPage eAskUse;
607                     if( SwHeaderStartNode == pSttNd->GetStartNodeType())
608                     {
609                         nId = RES_HEADER;
610                         eAskUse = UseOnPage::HeaderShare;
611                     }
612                     else
613                     {
614                         nId = RES_FOOTER;
615                         eAskUse = UseOnPage::FooterShare;
616                     }
617 
618                     for( size_t n = rDoc.GetPageDescCnt(); n && !pPgDesc; )
619                     {
620                         const SwPageDesc& rPgDsc = rDoc.GetPageDesc( --n );
621                         const SwFrameFormat* pFormat = &rPgDsc.GetMaster();
622                         int nStt = 0, nLast = 1;
623                         if( !( eAskUse & rPgDsc.ReadUseOn() )) ++nLast;
624 
625                         for( ; nStt < nLast; ++nStt, pFormat = &rPgDsc.GetLeft() )
626                         {
627                             const SwFrameFormat * pHdFtFormat = nId == RES_HEADER
628                                 ? static_cast<SwFormatHeader const &>(
629                                     pFormat->GetFormatAttr(nId)).GetHeaderFormat()
630                                 : static_cast<SwFormatFooter const &>(
631                                     pFormat->GetFormatAttr(nId)).GetFooterFormat();
632                             if( pHdFtFormat )
633                             {
634                                 const SwFormatContent& rContent = pHdFtFormat->GetContent();
635                                 if( rContent.GetContentIdx() &&
636                                     &rContent.GetContentIdx()->GetNode() ==
637                                     static_cast<SwNode const *>(pSttNd) )
638                                 {
639                                     pPgDesc = &rPgDsc;
640                                     break;
641                                 }
642                             }
643                         }
644                     }
645 
646                     if( !pPgDesc )
647                         pPgDesc = &rDoc.GetPageDesc( 0 );
648                     pNd = nullptr;
649                 }
650                 else if( nullptr != ( pSttNd = pNd->FindFootnoteStartNode() ))
651                 {
652                     // the Anchor can only be in the Body text
653                     const SwTextFootnote* pTextFootnote;
654                     const SwFootnoteIdxs& rFootnoteArr = rDoc.GetFootnoteIdxs();
655                     for( size_t n = 0; n < rFootnoteArr.size(); ++n )
656                         if( nullptr != ( pTextFootnote = rFootnoteArr[ n ])->GetStartNode() &&
657                             static_cast<SwNode const *>(pSttNd) ==
658                             &pTextFootnote->GetStartNode()->GetNode() )
659                         {
660                             pNd = &pTextFootnote->GetTextNode();
661                             break;
662                         }
663                 }
664                 else
665                 {
666                     // Can only be a page-bound Fly (or something newer).
667                     // we can only return the standard here
668                     OSL_ENSURE( pNd->FindFlyStartNode(),
669                             "Where is this Node?" );
670 
671                     pPgDesc = &rDoc.GetPageDesc( 0 );
672                     pNd = nullptr;
673                 }
674             }
675         }
676 
677         if( pNd )
678         {
679             SwFindNearestNode aInfo( *pNd );
680             // Over all Nodes of all PageDescs
681             for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_PAGEDESC))
682             {
683                 auto pPageDescItem = dynamic_cast<const SwFormatPageDesc*>(pItem);
684                 if( pPageDescItem && pPageDescItem->GetDefinedIn() )
685                 {
686                     const sw::BroadcastingModify* pMod = pPageDescItem->GetDefinedIn();
687                     if( auto pContentNode = dynamic_cast<const SwContentNode*>( pMod) )
688                         aInfo.CheckNode( *pContentNode );
689                     else if( auto pFormat = dynamic_cast<const SwFormat*>( pMod) )
690                         pFormat->GetInfo( aInfo );
691                 }
692             }
693 
694             pNd = aInfo.GetFoundNode();
695             if( nullptr != pNd )
696             {
697                 if( pNd->IsContentNode() )
698                     pPgDesc = static_cast<const SwFormatPageDesc&>(pNd->GetContentNode()->
699                                 GetAttr( RES_PAGEDESC )).GetPageDesc();
700                 else if( pNd->IsTableNode() )
701                     pPgDesc = pNd->GetTableNode()->GetTable().
702                             GetFrameFormat()->GetPageDesc().GetPageDesc();
703                 else if( pNd->IsSectionNode() )
704                     pPgDesc = pNd->GetSectionNode()->GetSection().
705                             GetFormat()->GetPageDesc().GetPageDesc();
706                 if ( pPgDescNdIdx )
707                 {
708                     *pPgDescNdIdx = pNd->GetIndex();
709                 }
710             }
711             if( !pPgDesc )
712                 pPgDesc = &rDoc.GetPageDesc( 0 );
713         }
714     }
715     return pPgDesc;
716 }
717 
718 /// If the node is located in a Fly, we return it formatted accordingly
719 SwFrameFormat* SwNode::GetFlyFormat() const
720 {
721     SwFrameFormat* pRet = nullptr;
722     const SwNode* pSttNd = FindFlyStartNode();
723     if( pSttNd )
724     {
725         if( IsContentNode() )
726         {
727             SwContentFrame* pFrame = SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<const SwContentNode*>(this)).First();
728             if( pFrame )
729                 pRet = pFrame->FindFlyFrame()->GetFormat();
730         }
731         if( !pRet )
732         {
733             // The hard way through the Doc is our last way out
734             const SwFrameFormats& rFrameFormatTable = *GetDoc().GetSpzFrameFormats();
735             for( size_t n = 0; n < rFrameFormatTable.size(); ++n )
736             {
737                 SwFrameFormat* pFormat = rFrameFormatTable[n];
738                 // Only Writer fly frames can contain Writer nodes.
739                 if (pFormat->Which() != RES_FLYFRMFMT)
740                     continue;
741                 const SwFormatContent& rContent = pFormat->GetContent();
742                 if( rContent.GetContentIdx() &&
743                     &rContent.GetContentIdx()->GetNode() == pSttNd )
744                 {
745                     pRet = pFormat;
746                     break;
747                 }
748             }
749         }
750     }
751     return pRet;
752 }
753 
754 SwTableBox* SwNode::GetTableBox() const
755 {
756     SwTableBox* pBox = nullptr;
757     const SwNode* pSttNd = FindTableBoxStartNode();
758     if( pSttNd )
759         pBox = const_cast<SwTableBox*>(pSttNd->FindTableNode()->GetTable().GetTableBox(
760                                                     pSttNd->GetIndex() ));
761     return pBox;
762 }
763 
764 SwStartNode* SwNode::FindSttNodeByType( SwStartNodeType eTyp )
765 {
766     SwStartNode* pTmp = IsStartNode() ? static_cast<SwStartNode*>(this) : m_pStartOfSection;
767 
768     while( eTyp != pTmp->GetStartNodeType() && pTmp->GetIndex() )
769         pTmp = pTmp->m_pStartOfSection;
770     return eTyp == pTmp->GetStartNodeType() ? pTmp : nullptr;
771 }
772 
773 const SwTextNode* SwNode::FindOutlineNodeOfLevel(sal_uInt8 const nLvl,
774         SwRootFrame const*const pLayout) const
775 {
776     const SwTextNode* pRet = nullptr;
777     const SwOutlineNodes& rONds = GetNodes().GetOutLineNds();
778     if( MAXLEVEL > nLvl && !rONds.empty() )
779     {
780         SwOutlineNodes::size_type nPos;
781         SwNode* pNd = const_cast<SwNode*>(this);
782         bool bCheckFirst = false;
783         if( !rONds.Seek_Entry( pNd, &nPos ))
784         {
785             if (nPos == 0)
786                 bCheckFirst = true;
787         }
788         else
789         {
790             ++nPos;
791         }
792 
793         if( bCheckFirst )
794         {
795             // The first OutlineNode comes after the one asking.
796             // Test if both are on the same page.
797             // If not it's invalid.
798             for (nPos = 0; nPos < rONds.size(); ++nPos)
799             {
800                 pRet = rONds[nPos]->GetTextNode();
801                 if (!pLayout || sw::IsParaPropsNode(*pLayout, *pRet))
802                 {
803                     break;
804                 }
805             }
806             if (nPos == rONds.size())
807             {
808                 return nullptr;
809             }
810 
811             const SwContentNode* pCNd = GetContentNode();
812 
813             Point aPt( 0, 0 );
814             std::pair<Point, bool> const tmp(aPt, false);
815             const SwFrame* pFrame = pRet->getLayoutFrame(pRet->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp),
816                        * pMyFrame = pCNd ? pCNd->getLayoutFrame(pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
817             const SwPageFrame* pPgFrame = pFrame ? pFrame->FindPageFrame() : nullptr;
818             if( pPgFrame && pMyFrame &&
819                 pPgFrame->getFrameArea().Top() > pMyFrame->getFrameArea().Top() )
820             {
821                 // The one asking precedes the Page, thus its invalid
822                 pRet = nullptr;
823             }
824         }
825         else
826         {
827             for ( ; 0 < nPos; --nPos)
828             {
829                 SwTextNode const*const pNode = rONds[nPos - 1]->GetTextNode();
830                 if ((nPos == 1 /*as before*/ || pNode->GetAttrOutlineLevel() - 1 <= nLvl)
831                     && (!pLayout || sw::IsParaPropsNode(*pLayout, *pNode)))
832                 {
833                     pRet = pNode;
834                     break;
835                 }
836             }
837         }
838     }
839     return pRet;
840 }
841 
842 static bool IsValidNextPrevNd( const SwNode& rNd )
843 {
844     return SwNodeType::Table == rNd.GetNodeType() ||
845            ( SwNodeType::ContentMask & rNd.GetNodeType() ) ||
846             ( SwNodeType::End == rNd.GetNodeType() && rNd.StartOfSectionNode() &&
847             SwNodeType::Table == rNd.StartOfSectionNode()->GetNodeType() );
848 }
849 
850 sal_uInt8 SwNode::HasPrevNextLayNode() const
851 {
852     // assumption: <this> node is a node inside the document nodes array section.
853 
854     sal_uInt8 nRet = 0;
855     if( IsValidNextPrevNd( *this ))
856     {
857         SwNodeIndex aIdx( *this, -1 );
858         // #i77805# - skip section start and end nodes
859         while ( aIdx.GetNode().IsSectionNode() ||
860                 ( aIdx.GetNode().IsEndNode() &&
861                   aIdx.GetNode().StartOfSectionNode()->IsSectionNode() ) )
862         {
863             --aIdx;
864         }
865         if( IsValidNextPrevNd( aIdx.GetNode() ))
866             nRet |= ND_HAS_PREV_LAYNODE;
867         // #i77805# - skip section start and end nodes
868         aIdx.Assign(*this, +1);
869         while ( aIdx.GetNode().IsSectionNode() ||
870                 ( aIdx.GetNode().IsEndNode() &&
871                   aIdx.GetNode().StartOfSectionNode()->IsSectionNode() ) )
872         {
873             ++aIdx;
874         }
875         if( IsValidNextPrevNd( aIdx.GetNode() ))
876             nRet |= ND_HAS_NEXT_LAYNODE;
877     }
878     return nRet;
879 }
880 
881 void SwNode::dumpAsXml(xmlTextWriterPtr pWriter) const
882 {
883     const char* pName = "???";
884     switch (GetNodeType())
885     {
886     case SwNodeType::End:
887         pName = "end";
888         break;
889     case SwNodeType::Start:
890     case SwNodeType::Text:
891     case SwNodeType::Ole:
892         abort(); // overridden
893     case SwNodeType::Table:
894         pName = "table";
895         break;
896     case SwNodeType::Grf:
897         pName = "grf";
898         break;
899     default: break;
900     }
901     (void)xmlTextWriterStartElement(pWriter, BAD_CAST(pName));
902 
903     (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
904     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr()));
905     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
906 
907     switch (GetNodeType())
908     {
909         case SwNodeType::Grf:
910         {
911             auto pNoTextNode = static_cast<const SwNoTextNode*>(this);
912             const tools::PolyPolygon* pContour = pNoTextNode->HasContour();
913             if (pContour)
914             {
915                 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pContour"));
916                 for (sal_uInt16 i = 0; i < pContour->Count(); ++i)
917                 {
918                     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("polygon"));
919                     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
920                                                 BAD_CAST(OString::number(i).getStr()));
921                     const tools::Polygon& rPolygon = pContour->GetObject(i);
922                     for (sal_uInt16 j = 0; j < rPolygon.GetSize(); ++j)
923                     {
924                         (void)xmlTextWriterStartElement(pWriter, BAD_CAST("point"));
925                         (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
926                                                     BAD_CAST(OString::number(j).getStr()));
927                         const Point& rPoint = rPolygon.GetPoint(j);
928                         (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("x"),
929                                                     BAD_CAST(OString::number(rPoint.X()).getStr()));
930                         (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("y"),
931                                                     BAD_CAST(OString::number(rPoint.Y()).getStr()));
932                         (void)xmlTextWriterEndElement(pWriter);
933                     }
934                     (void)xmlTextWriterEndElement(pWriter);
935                 }
936                 (void)xmlTextWriterEndElement(pWriter);
937             }
938         }
939         break;
940         default:
941             break;
942     }
943 
944     (void)xmlTextWriterEndElement(pWriter);
945     if (GetNodeType() == SwNodeType::End)
946         (void)xmlTextWriterEndElement(pWriter); // end start node
947 }
948 
949 SwStartNode::SwStartNode( const SwNodeIndex &rWhere, const SwNodeType nNdType,
950                             SwStartNodeType eSttNd )
951     : SwNode( rWhere, nNdType ), m_eStartNodeType( eSttNd )
952 {
953     if( !rWhere.GetIndex() )
954     {
955         SwNodes& rNodes = const_cast<SwNodes&> (rWhere.GetNodes());
956         rNodes.InsertNode( this, rWhere );
957         m_pStartOfSection = this;
958     }
959     // Just do this temporarily until the EndNode is inserted
960     m_pEndOfSection = reinterpret_cast<SwEndNode*>(this);
961 }
962 
963 SwStartNode::SwStartNode( SwNodes& rNodes, SwNodeOffset nPos )
964     : SwNode( rNodes, nPos, SwNodeType::Start ), m_eStartNodeType( SwNormalStartNode )
965 {
966     if( !nPos )
967     {
968         rNodes.InsertNode( this, nPos );
969         m_pStartOfSection = this;
970     }
971     // Just do this temporarily until the EndNode is inserted
972     m_pEndOfSection = reinterpret_cast<SwEndNode*>(this);
973 }
974 
975 void SwStartNode::CheckSectionCondColl() const
976 {
977 //FEATURE::CONDCOLL
978     SwNodeIndex aIdx( *this );
979     SwNodeOffset nEndIdx = EndOfSectionIndex();
980     const SwNodes& rNds = GetNodes();
981     SwContentNode* pCNd;
982     while( nullptr != ( pCNd = rNds.GoNext( &aIdx )) && pCNd->GetIndex() < nEndIdx )
983         pCNd->ChkCondColl();
984 //FEATURE::CONDCOLL
985 }
986 
987 void SwStartNode::dumpAsXml(xmlTextWriterPtr pWriter) const
988 {
989     const char* pName = "???";
990     switch (GetNodeType())
991     {
992     case SwNodeType::Table:
993         pName = "table";
994         break;
995     default:
996         switch(GetStartNodeType())
997         {
998         case SwNormalStartNode:
999             pName = "start";
1000             break;
1001         case SwTableBoxStartNode:
1002             pName = "tablebox";
1003             break;
1004         case SwFlyStartNode:
1005             pName = "fly";
1006             break;
1007         case SwFootnoteStartNode:
1008             pName = "footnote";
1009             break;
1010         case SwHeaderStartNode:
1011             pName = "header";
1012             break;
1013         case SwFooterStartNode:
1014             pName = "footer";
1015             break;
1016         }
1017         break;
1018     }
1019 
1020     (void)xmlTextWriterStartElement(pWriter, BAD_CAST(pName));
1021     (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
1022     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr()));
1023     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
1024 
1025     if (IsTableNode())
1026     {
1027         (void)xmlTextWriterStartElement(pWriter, BAD_CAST("attrset"));
1028         GetTableNode()->GetTable().GetFrameFormat()->GetAttrSet().dumpAsXml(pWriter);
1029         (void)xmlTextWriterEndElement(pWriter);
1030     }
1031     else if (GetStartNodeType() == SwTableBoxStartNode)
1032     {
1033         if (SwTableBox* pBox = GetTableBox())
1034             (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("rowspan"), BAD_CAST(OString::number(pBox->getRowSpan()).getStr()));
1035         (void)xmlTextWriterStartElement(pWriter, BAD_CAST("attrset"));
1036         if (SwTableBox* pBox = GetTableBox())
1037             pBox->GetFrameFormat()->GetAttrSet().dumpAsXml(pWriter);
1038         (void)xmlTextWriterEndElement(pWriter);
1039     }
1040 
1041     // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
1042 }
1043 
1044 
1045 /** Insert a node into the array
1046  *
1047  * The StartOfSection pointer is set to the given node.
1048  *
1049  * The EndOfSection pointer of the corresponding start node is set to this node.
1050  *
1051  * @param rWhere position where the node shoul be inserted
1052  * @param rSttNd the start note of the section
1053  */
1054 
1055 SwEndNode::SwEndNode( const SwNodeIndex &rWhere, SwStartNode& rSttNd )
1056     : SwNode( rWhere, SwNodeType::End )
1057 {
1058     m_pStartOfSection = &rSttNd;
1059     m_pStartOfSection->m_pEndOfSection = this;
1060 }
1061 
1062 SwEndNode::SwEndNode( SwNodes& rNds, SwNodeOffset nPos, SwStartNode& rSttNd )
1063     : SwNode( rNds, nPos, SwNodeType::End )
1064 {
1065     m_pStartOfSection = &rSttNd;
1066     m_pStartOfSection->m_pEndOfSection = this;
1067 }
1068 
1069 SwContentNode::SwContentNode( const SwNodeIndex &rWhere, const SwNodeType nNdType,
1070                             SwFormatColl *pColl )
1071     : SwNode( rWhere, nNdType )
1072     , m_aCondCollListener( *this )
1073     , m_pCondColl( nullptr )
1074     , mbSetModifyAtAttr( false )
1075 {
1076     if(pColl)
1077         pColl->Add(this);
1078 }
1079 
1080 SwContentNode::~SwContentNode()
1081 {
1082     // The base class SwClient of SwFrame excludes itself from the dependency list!
1083     // Thus, we need to delete all Frames in the dependency list.
1084     if (!IsTextNode()) // see ~SwTextNode
1085     {
1086         DelFrames(nullptr);
1087     }
1088 
1089     m_aCondCollListener.EndListeningAll();
1090     m_pCondColl = nullptr;
1091 
1092     if ( mpAttrSet && mbSetModifyAtAttr )
1093         const_cast<SwAttrSet*>(static_cast<const SwAttrSet*>(mpAttrSet.get()))->SetModifyAtAttr( nullptr );
1094     InvalidateInSwCache(RES_OBJECTDYING);
1095 }
1096 void SwContentNode::UpdateAttr(const SwUpdateAttr& rUpdate)
1097 {
1098     if (GetNodes().IsDocNodes()
1099             && IsTextNode()
1100             && RES_ATTRSET_CHG == rUpdate.getWhichAttr())
1101         static_cast<SwTextNode*>(this)->SetCalcHiddenCharFlags();
1102     CallSwClientNotify(sw::LegacyModifyHint(&rUpdate, &rUpdate));
1103 }
1104 
1105 void SwContentNode::SwClientNotify( const SwModify&, const SfxHint& rHint)
1106 {
1107     if (rHint.GetId() == SfxHintId::SwLegacyModify)
1108     {
1109         auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
1110         const sal_uInt16 nWhich = pLegacyHint->GetWhich();
1111         InvalidateInSwCache(nWhich);
1112 
1113         bool bSetParent = false;
1114         bool bCalcHidden = false;
1115         SwFormatColl* pFormatColl = nullptr;
1116         switch(nWhich)
1117         {
1118             case RES_OBJECTDYING:
1119                 {
1120                     SwFormat* pFormat = pLegacyHint->m_pNew
1121                             ? static_cast<SwFormat*>(static_cast<const SwPtrMsgPoolItem*>(pLegacyHint->m_pNew)->pObject)
1122                             : nullptr;
1123                     // Do not mangle pointers if it is the upper-most format!
1124                     if(pFormat && GetRegisteredIn() == pFormat)
1125                     {
1126                         if(pFormat->GetRegisteredIn())
1127                         {
1128                             // If Parent, register anew in the new Parent
1129                             pFormat->GetRegisteredIn()->Add(this);
1130                             pFormatColl = GetFormatColl();
1131                         }
1132                         else
1133                             EndListeningAll();
1134                         bSetParent = true;
1135                     }
1136                 }
1137                 break;
1138 
1139             case RES_FMT_CHG:
1140                 // If the Format parent was switched, register the Attrset at the new one
1141                 // Skip own Modify!
1142                 if(GetpSwAttrSet()
1143                         && pLegacyHint->m_pNew
1144                         && static_cast<const SwFormatChg*>(pLegacyHint->m_pNew)->pChangedFormat == GetRegisteredIn())
1145                 {
1146                     pFormatColl = GetFormatColl();
1147                     bSetParent = true;
1148                 }
1149                 break;
1150 
1151             case RES_ATTRSET_CHG:
1152                 if (GetNodes().IsDocNodes()
1153                         && IsTextNode()
1154                         && pLegacyHint->m_pOld
1155                         && SfxItemState::SET == pLegacyHint->m_pOld->StaticWhichCast(RES_ATTRSET_CHG).GetChgSet()->GetItemState(RES_CHRATR_HIDDEN, false))
1156                     bCalcHidden = true;
1157                 break;
1158 
1159             case RES_UPDATE_ATTR:
1160                 // RES_UPDATE_ATTR _should_ always contain a SwUpdateAttr hint in old and new.
1161                 // However, faking one with just a basic SfxPoolItem setting a WhichId has been observed.
1162                 // This makes the crude "WhichId" type divert from the true type, which is bad.
1163                 // Thus we are asserting here, but falling back to an proper
1164                 // hint instead. so that we at least will not spread such poison further.
1165 #ifdef DBG_UTIL
1166                 if(pLegacyHint->m_pNew != pLegacyHint->m_pOld)
1167                 {
1168                     auto pBT = sal::backtrace_get(20);
1169                     SAL_WARN("sw.core", "UpdateAttr not matching! " << sal::backtrace_to_string(pBT.get()));
1170                 }
1171 #endif
1172                 assert(pLegacyHint->m_pNew == pLegacyHint->m_pOld);
1173                 assert(dynamic_cast<const SwUpdateAttr*>(pLegacyHint->m_pNew));
1174                 const SwUpdateAttr aFallbackHint(0,0,0);
1175                 const SwUpdateAttr& rUpdateAttr = pLegacyHint->m_pNew ? *static_cast<const SwUpdateAttr*>(pLegacyHint->m_pNew) : aFallbackHint;
1176                 UpdateAttr(rUpdateAttr);
1177                 return;
1178         }
1179         if(bSetParent && GetpSwAttrSet())
1180             AttrSetHandleHelper::SetParent(mpAttrSet, *this, pFormatColl, pFormatColl);
1181         if(bCalcHidden)
1182             static_cast<SwTextNode*>(this)->SetCalcHiddenCharFlags();
1183         CallSwClientNotify(rHint);
1184     }
1185     else if (auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint))
1186     {
1187         m_pCondColl = const_cast<SwFormatColl*>(static_cast<const SwFormatColl*>(pModifyChangedHint->m_pNew));
1188     }
1189     else if(auto pCondCollCondChgHint = dynamic_cast<const sw::CondCollCondChg*>(&rHint))
1190     {
1191         ChkCondColl(&pCondCollCondChgHint->m_rColl);
1192     }
1193 }
1194 
1195 bool SwContentNode::InvalidateNumRule()
1196 {
1197     SwNumRule* pRule = nullptr;
1198     const SfxPoolItem* pItem;
1199     if( GetNodes().IsDocNodes() &&
1200         nullptr != ( pItem = GetNoCondAttr( RES_PARATR_NUMRULE, true )) &&
1201         !static_cast<const SwNumRuleItem*>(pItem)->GetValue().isEmpty() &&
1202         nullptr != (pRule = GetDoc().FindNumRulePtr(
1203                                 static_cast<const SwNumRuleItem*>(pItem)->GetValue() ) ) )
1204     {
1205         pRule->SetInvalidRule( true );
1206     }
1207     return nullptr != pRule;
1208 }
1209 
1210 SwContentFrame *SwContentNode::getLayoutFrame( const SwRootFrame* _pRoot,
1211     const SwPosition *const pPos,
1212     std::pair<Point, bool> const*const pViewPosAndCalcFrame) const
1213 {
1214     return static_cast<SwContentFrame*>( ::GetFrameOfModify( _pRoot, *this, FRM_CNTNT,
1215                                             pPos, pViewPosAndCalcFrame));
1216 }
1217 
1218 SwRect SwContentNode::FindLayoutRect( const bool bPrtArea, const Point* pPoint ) const
1219 {
1220     SwRect aRet;
1221     std::pair<Point, bool> tmp;
1222     if (pPoint)
1223     {
1224         tmp.first = *pPoint;
1225         tmp.second = false;
1226     }
1227     SwContentFrame* pFrame = static_cast<SwContentFrame*>( ::GetFrameOfModify( nullptr, *this,
1228                                 FRM_CNTNT, nullptr, pPoint ? &tmp : nullptr) );
1229     if( pFrame )
1230         aRet = bPrtArea ? pFrame->getFramePrintArea() : pFrame->getFrameArea();
1231     return aRet;
1232 }
1233 
1234 SwRect SwContentNode::FindPageFrameRect() const
1235 {
1236     SwRect aRet;
1237     SwFrame* pFrame = ::GetFrameOfModify( nullptr, *this, FRM_CNTNT );
1238     if( pFrame && nullptr != ( pFrame = pFrame->FindPageFrame() ))
1239         aRet = pFrame->getFrameArea();
1240     return aRet;
1241 }
1242 
1243 sal_Int32 SwContentNode::Len() const { return 0; }
1244 
1245 SwFormatColl *SwContentNode::ChgFormatColl( SwFormatColl *pNewColl )
1246 {
1247     OSL_ENSURE( pNewColl, "Collectionpointer is 0." );
1248     SwFormatColl *pOldColl = GetFormatColl();
1249 
1250     if( pNewColl != pOldColl )
1251     {
1252         pNewColl->Add( this );
1253 
1254         // Set the Parent of out AutoAttributes to the new Collection
1255         if( GetpSwAttrSet() )
1256             AttrSetHandleHelper::SetParent( mpAttrSet, *this, pNewColl, pNewColl );
1257 
1258         SetCondFormatColl( nullptr );
1259 
1260         if( !IsModifyLocked() )
1261         {
1262             assert(dynamic_cast<SwTextFormatColl*>(pNewColl));
1263             ChkCondColl(static_cast<SwTextFormatColl*>(pNewColl));
1264             SwFormatChg aTmp1( pOldColl );
1265             SwFormatChg aTmp2( pNewColl );
1266             SwClientNotify( *this, sw::LegacyModifyHint(&aTmp1, &aTmp2) );
1267         }
1268     }
1269     InvalidateInSwCache(RES_ATTRSET_CHG);
1270     return pOldColl;
1271 }
1272 
1273 bool SwContentNode::GoNext(SwIndex * pIdx, sal_uInt16 nMode ) const
1274 {
1275     bool bRet = true;
1276     if( pIdx->GetIndex() < Len() )
1277     {
1278         if( !IsTextNode() )
1279             ++(*pIdx);
1280         else
1281         {
1282             const SwTextNode& rTNd = *GetTextNode();
1283             sal_Int32 nPos = pIdx->GetIndex();
1284             assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
1285             sal_Int32 nDone = 0;
1286             sal_uInt16 nItrMode = ( CRSR_SKIP_CELLS & nMode ) ?
1287                                     CharacterIteratorMode::SKIPCELL :
1288                                     CharacterIteratorMode::SKIPCONTROLCHARACTER;
1289             nPos = g_pBreakIt->GetBreakIter()->nextCharacters( rTNd.GetText(), nPos,
1290                                g_pBreakIt->GetLocale( rTNd.GetLang( nPos ) ),
1291                                nItrMode, 1, nDone );
1292 
1293             // Check if nPos is inside hidden text range:
1294             if ( CRSR_SKIP_HIDDEN & nMode )
1295             {
1296                 sal_Int32 nHiddenStart;
1297                 sal_Int32 nHiddenEnd;
1298                 SwScriptInfo::GetBoundsOfHiddenRange( rTNd, nPos, nHiddenStart, nHiddenEnd );
1299                 if ( nHiddenStart != COMPLETE_STRING && nHiddenStart != nPos )
1300                      nPos = nHiddenEnd;
1301             }
1302 
1303             if( 1 == nDone )
1304                 *pIdx = nPos;
1305             else
1306                 bRet = false;
1307         }
1308     }
1309     else
1310         bRet = false;
1311     return bRet;
1312 }
1313 
1314 bool SwContentNode::GoPrevious(SwIndex * pIdx, sal_uInt16 nMode ) const
1315 {
1316     bool bRet = true;
1317     if( pIdx->GetIndex() > 0 )
1318     {
1319         if( !IsTextNode() )
1320             --(*pIdx);
1321         else
1322         {
1323             const SwTextNode& rTNd = *GetTextNode();
1324             sal_Int32 nPos = pIdx->GetIndex();
1325             assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
1326             sal_Int32 nDone = 0;
1327             sal_uInt16 nItrMode = ( CRSR_SKIP_CELLS & nMode ) ?
1328                                     CharacterIteratorMode::SKIPCELL :
1329                                     CharacterIteratorMode::SKIPCONTROLCHARACTER;
1330             nPos = g_pBreakIt->GetBreakIter()->previousCharacters( rTNd.GetText(), nPos,
1331                                g_pBreakIt->GetLocale( rTNd.GetLang( nPos ) ),
1332                                nItrMode, 1, nDone );
1333 
1334             // Check if nPos is inside hidden text range:
1335             if ( CRSR_SKIP_HIDDEN & nMode )
1336             {
1337                 sal_Int32 nHiddenStart;
1338                 sal_Int32 nHiddenEnd;
1339                 SwScriptInfo::GetBoundsOfHiddenRange( rTNd, nPos, nHiddenStart, nHiddenEnd );
1340                 if ( nHiddenStart != COMPLETE_STRING )
1341                      nPos = nHiddenStart;
1342             }
1343 
1344             if( 1 == nDone )
1345                 *pIdx = nPos;
1346             else
1347                 bRet = false;
1348         }
1349     }
1350     else
1351         bRet = false;
1352     return bRet;
1353 }
1354 
1355 /**
1356  * Creates all Views for the Doc for this Node.
1357  * The created ContentFrames are attached to the corresponding Layout.
1358  */
1359 void SwContentNode::MakeFramesForAdjacentContentNode(SwContentNode& rNode)
1360 {
1361     OSL_ENSURE( &rNode != this,
1362             "No ContentNode or CopyNode and new Node identical." );
1363 
1364     if( !HasWriterListeners() || &rNode == this )   // Do we actually have Frames?
1365         return;
1366 
1367     SwFrame *pFrame;
1368     SwLayoutFrame *pUpper;
1369     // Create Frames for Nodes which come after the Table?
1370     OSL_ENSURE( FindTableNode() == rNode.FindTableNode(), "Table confusion" );
1371 
1372     SwNode2Layout aNode2Layout( *this, rNode.GetIndex() );
1373 
1374     while( nullptr != (pUpper = aNode2Layout.UpperFrame( pFrame, rNode )) )
1375     {
1376         if (pUpper->getRootFrame()->HasMergedParas()
1377             && !rNode.IsCreateFrameWhenHidingRedlines())
1378         {
1379             continue;
1380         }
1381         SwFrame *pNew = rNode.MakeFrame( pUpper );
1382         pNew->Paste( pUpper, pFrame );
1383         // #i27138#
1384         // notify accessibility paragraphs objects about changed
1385         // CONTENT_FLOWS_FROM/_TO relation.
1386         // Relation CONTENT_FLOWS_FROM for next paragraph will change
1387         // and relation CONTENT_FLOWS_TO for previous paragraph will change.
1388         if ( pNew->IsTextFrame() )
1389         {
1390             SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
1391             if ( pViewShell && pViewShell->GetLayout() &&
1392                  pViewShell->GetLayout()->IsAnyShellAccessible() )
1393             {
1394                 auto pNext = pNew->FindNextCnt( true );
1395                 auto pPrev = pNew->FindPrevCnt();
1396                 pViewShell->InvalidateAccessibleParaFlowRelation(
1397                             pNext ? pNext->DynCastTextFrame() : nullptr,
1398                             pPrev ? pPrev->DynCastTextFrame() : nullptr );
1399             }
1400         }
1401     }
1402 }
1403 
1404 /**
1405  * Deletes all Views from the Doc for this Node.
1406  * The ContentFrames are removed from the corresponding Layout.
1407  */
1408 void SwContentNode::DelFrames(SwRootFrame const*const pLayout)
1409 {
1410     if( !HasWriterListeners() )
1411         return;
1412 
1413     SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
1414     for( SwContentFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
1415     {
1416         if (pLayout && pLayout != pFrame->getRootFrame())
1417         {
1418             continue; // skip it
1419         }
1420         if (pFrame->IsTextFrame())
1421         {
1422             if (sw::MergedPara * pMerged =
1423                     static_cast<SwTextFrame *>(pFrame)->GetMergedPara())
1424             {
1425                 if (this != pMerged->pFirstNode)
1426                 {
1427                     // SwNodes::RemoveNode iterates *backwards* - so
1428                     // ensure there are no more extents pointing to this
1429                     // node as SwFrame::InvalidatePage() will access them.
1430                     // Note: cannot send via SwClientNotify from dtor
1431                     // because that would access deleted wrong-lists
1432                     sw::UpdateMergedParaForDelete(*pMerged, true,
1433                             *static_cast<SwTextNode*>(this), 0, Len());
1434                     if (this == pMerged->pParaPropsNode)
1435                     {
1436                         // otherwise pointer should have been updated to a different node
1437                         assert(this == pMerged->pLastNode);
1438                         assert(pMerged->extents.empty());
1439                         for (SwNodeOffset i = pMerged->pLastNode->GetIndex() - 1;;
1440                                 --i)
1441                         {
1442                             assert(pMerged->pFirstNode->GetIndex() <= i);
1443                             SwNode *const pNode(GetNodes()[i]);
1444                             if (pNode->IsTextNode()
1445                                 && pNode->GetRedlineMergeFlag() != Merge::Hidden)
1446                             {
1447                                 pMerged->pParaPropsNode = pNode->GetTextNode();
1448                                 break;
1449                             }
1450                         }
1451                         assert(pMerged->listener.IsListeningTo(pMerged->pParaPropsNode));
1452                     }
1453                     assert(GetIndex() <= pMerged->pLastNode->GetIndex());
1454                     if (this == pMerged->pLastNode)
1455                     {
1456                         // tdf#130680 find the previous node that is a
1457                         // listener of pMerged; see CheckParaRedlineMerge()
1458                         for (SwNodeOffset i = GetIndex() - 1;
1459                              this == pMerged->pLastNode; --i)
1460                         {
1461                             SwNode *const pNode = GetNodes()[i];
1462                             if (pNode->IsTextNode())
1463                             {
1464                                 pMerged->pLastNode = pNode->GetTextNode();
1465                             }
1466                             else if (SwEndNode const*const pEnd = pNode->GetEndNode())
1467                             {
1468                                 SwStartNode const*const pStart(pEnd->StartOfSectionNode());
1469                                 i = pStart->GetIndex(); // skip table or section
1470                             }
1471                         }
1472                         assert(pMerged->pFirstNode->GetIndex() <= pMerged->pLastNode->GetIndex());
1473                         assert(pMerged->listener.IsListeningTo(pMerged->pLastNode));
1474                     }
1475                     // avoid re-parenting mess (ModifyChangedHint)
1476                     pMerged->listener.EndListening(this);
1477                     continue; // don't delete
1478                 }
1479             }
1480         // #i27138#
1481         // notify accessibility paragraphs objects about changed
1482         // CONTENT_FLOWS_FROM/_TO relation.
1483         // Relation CONTENT_FLOWS_FROM for current next paragraph will change
1484         // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
1485             SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
1486             if ( pViewShell && pViewShell->GetLayout() &&
1487                  pViewShell->GetLayout()->IsAnyShellAccessible() )
1488             {
1489                 auto pNext = pFrame->FindNextCnt( true );
1490                 auto pPrev = pFrame->FindPrevCnt();
1491                 pViewShell->InvalidateAccessibleParaFlowRelation(
1492                             pNext ? pNext->DynCastTextFrame() : nullptr,
1493                             pPrev ? pPrev->DynCastTextFrame() : nullptr );
1494             }
1495         }
1496 
1497         if( pFrame->IsFollow() )
1498         {
1499             SwContentFrame* pMaster = pFrame->FindMaster();
1500             pMaster->SetFollow( pFrame->GetFollow() );
1501         }
1502         pFrame->SetFollow( nullptr );//So it doesn't get funny ideas.
1503                                 //Otherwise it could be possible that a follow
1504                                 //gets destroyed before its master. Following
1505                                 //the now invalid pointer will then lead to an
1506                                 //illegal memory access. The chain can be
1507                                 //crushed here because we'll destroy all of it
1508                                 //anyway.
1509 
1510         if( pFrame->GetUpper() && pFrame->IsInFootnote() && !pFrame->GetIndNext() &&
1511             !pFrame->GetIndPrev() )
1512         {
1513             SwFootnoteFrame *pFootnote = pFrame->FindFootnoteFrame();
1514             OSL_ENSURE( pFootnote, "You promised a FootnoteFrame?" );
1515             SwContentFrame* pCFrame;
1516             if( !pFootnote->GetFollow() && !pFootnote->GetMaster() &&
1517                 nullptr != ( pCFrame = pFootnote->GetRefFromAttr()) && pCFrame->IsFollow() )
1518             {
1519                 OSL_ENSURE( pCFrame->IsTextFrame(), "NoTextFrame has Footnote?" );
1520                 pCFrame->FindMaster()->Prepare( PrepareHint::FootnoteInvalidationGone );
1521             }
1522         }
1523         pFrame->Cut();
1524         SwFrame::DestroyFrame(pFrame);
1525     }
1526 }
1527 
1528 SwContentNode *SwContentNode::JoinNext()
1529 {
1530     return this;
1531 }
1532 
1533 /// Get info from Modify
1534 bool SwContentNode::GetInfo( SfxPoolItem& rInfo ) const
1535 {
1536     switch( rInfo.Which() )
1537     {
1538     case RES_AUTOFMT_DOCNODE:
1539         if( &GetNodes() == static_cast<SwAutoFormatGetDocNode&>(rInfo).pNodes )
1540         {
1541             return false;
1542         }
1543         break;
1544 
1545     case RES_FINDNEARESTNODE:
1546         if( static_cast<const SwFormatPageDesc&>(GetAttr( RES_PAGEDESC )).GetPageDesc() )
1547             static_cast<SwFindNearestNode&>(rInfo).CheckNode( *this );
1548         return true;
1549 
1550     case RES_CONTENT_VISIBLE:
1551         {
1552             static_cast<SwPtrMsgPoolItem&>(rInfo).pObject =
1553                 SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*this).First();
1554         }
1555         return false;
1556     }
1557 
1558     return sw::BroadcastingModify::GetInfo( rInfo );
1559 }
1560 
1561 /// @param rAttr the attribute to set
1562 bool SwContentNode::SetAttr(const SfxPoolItem& rAttr )
1563 {
1564     if( !GetpSwAttrSet() ) // Have the Nodes created by the corresponding AttrSets
1565         NewAttrSet( GetDoc().GetAttrPool() );
1566 
1567     OSL_ENSURE( GetpSwAttrSet(), "Why did't we create an AttrSet?");
1568 
1569     InvalidateInSwCache(RES_ATTRSET_CHG);
1570 
1571     bool bRet = false;
1572     // If Modify is locked, we do not send any Modifys
1573     if( IsModifyLocked() ||
1574         ( !HasWriterListeners() &&  RES_PARATR_NUMRULE != rAttr.Which() ))
1575     {
1576         bRet = nullptr != AttrSetHandleHelper::Put( mpAttrSet, *this, rAttr );
1577     }
1578     else
1579     {
1580         SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
1581                   aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
1582         bRet = AttrSetHandleHelper::Put_BC( mpAttrSet, *this, rAttr, &aOld, &aNew );
1583         if( bRet )
1584             sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
1585     }
1586     return bRet;
1587 }
1588 
1589 bool SwContentNode::SetAttr( const SfxItemSet& rSet )
1590 {
1591     InvalidateInSwCache(RES_ATTRSET_CHG);
1592 
1593     const SfxPoolItem* pFnd = nullptr;
1594     if( SfxItemState::SET == rSet.GetItemState( RES_AUTO_STYLE, false, &pFnd ) )
1595     {
1596         OSL_ENSURE( rSet.Count() == 1, "SetAutoStyle mixed with other attributes?!" );
1597         const SwFormatAutoFormat* pTmp = static_cast<const SwFormatAutoFormat*>(pFnd);
1598 
1599         // If there already is an attribute set (usually containing a numbering
1600         // item), we have to merge the attribute of the new set into the old set:
1601         bool bSetParent = true;
1602         if ( GetpSwAttrSet() )
1603         {
1604             bSetParent = false;
1605             AttrSetHandleHelper::Put( mpAttrSet, *this, *pTmp->GetStyleHandle() );
1606         }
1607         else
1608         {
1609             mpAttrSet = pTmp->GetStyleHandle();
1610         }
1611 
1612         if ( bSetParent )
1613         {
1614             // If the content node has a conditional style, we have to set the
1615             // string item containing the correct conditional style name (the
1616             // style name property has already been set during the import!)
1617             // In case we do not have a conditional style, we make use of the
1618             // fact that nobody else uses the attribute set behind the handle.
1619             // FME 2007-07-10 #i78124# If autostyle does not have a parent,
1620             // the string is empty.
1621             const SfxPoolItem* pNameItem = nullptr;
1622             if ( nullptr != GetCondFormatColl() ||
1623                  SfxItemState::SET != mpAttrSet->GetItemState( RES_FRMATR_STYLE_NAME, false, &pNameItem ) ||
1624                  static_cast<const SfxStringItem*>(pNameItem)->GetValue().isEmpty() )
1625                 AttrSetHandleHelper::SetParent( mpAttrSet, *this, &GetAnyFormatColl(), GetFormatColl() );
1626             else
1627                 const_cast<SfxItemSet*>(mpAttrSet.get())->SetParent( &GetFormatColl()->GetAttrSet() );
1628         }
1629 
1630         return true;
1631     }
1632 
1633     if( !GetpSwAttrSet() ) // Have the AttrsSets created by the corresponding Nodes
1634         NewAttrSet( GetDoc().GetAttrPool() );
1635 
1636     bool bRet = false;
1637     // If Modify is locked, do not send any Modifys
1638     if ( IsModifyLocked() ||
1639          ( !HasWriterListeners() &&
1640            SfxItemState::SET != rSet.GetItemState( RES_PARATR_NUMRULE, false ) ) )
1641     {
1642         // Some special treatment for Attributes
1643         bRet = AttrSetHandleHelper::Put( mpAttrSet, *this, rSet );
1644     }
1645     else
1646     {
1647         SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
1648                   aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
1649         bRet = AttrSetHandleHelper::Put_BC( mpAttrSet, *this, rSet, &aOld, &aNew );
1650         if( bRet )
1651             sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
1652     }
1653     return bRet;
1654 }
1655 
1656 // With nWhich it takes the Hint from the Delta array
1657 bool SwContentNode::ResetAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
1658 {
1659     if( !GetpSwAttrSet() )
1660         return false;
1661 
1662     InvalidateInSwCache(RES_ATTRSET_CHG);
1663 
1664     // If Modify is locked, do not send out any Modifys
1665     if( IsModifyLocked() )
1666     {
1667         sal_uInt16 nDel = 0;
1668         if ( !nWhich2 || nWhich2 < nWhich1 )
1669         {
1670             nDel = ClearItemsFromAttrSet( { nWhich1 } );
1671         }
1672         else
1673             nDel = AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, nWhich1, nWhich2, nullptr, nullptr );
1674 
1675         if( !GetpSwAttrSet()->Count() ) // Empty? Delete
1676             mpAttrSet.reset();
1677         return 0 != nDel;
1678     }
1679 
1680     // No valid area defined?
1681     if( !nWhich2 || nWhich2 < nWhich1 )
1682         nWhich2 = nWhich1; // Then set only this Item to 1st Id
1683 
1684     SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
1685               aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
1686     bool bRet = 0 != AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, nWhich1, nWhich2, &aOld, &aNew );
1687 
1688     if( bRet )
1689     {
1690         sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
1691 
1692         if( !GetpSwAttrSet()->Count() ) // Empty?, delete it
1693             mpAttrSet.reset();
1694     }
1695     return bRet;
1696 }
1697 
1698 bool SwContentNode::ResetAttr( const std::vector<sal_uInt16>& rWhichArr )
1699 {
1700     if( !GetpSwAttrSet() )
1701         return false;
1702 
1703     InvalidateInSwCache(RES_ATTRSET_CHG);
1704     // If Modify is locked, do not send out any Modifys
1705     sal_uInt16 nDel = 0;
1706     if( IsModifyLocked() )
1707     {
1708         nDel = ClearItemsFromAttrSet( rWhichArr );
1709     }
1710     else
1711     {
1712         SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
1713                   aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
1714 
1715         for ( const auto& rWhich : rWhichArr )
1716             if( AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, rWhich, &aOld, &aNew ))
1717                 ++nDel;
1718 
1719         if( nDel )
1720             sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
1721     }
1722     if( !GetpSwAttrSet()->Count() ) // Empty?, delete it
1723         mpAttrSet.reset();
1724     return 0 != nDel ;
1725 }
1726 
1727 sal_uInt16 SwContentNode::ResetAllAttr()
1728 {
1729     if( !GetpSwAttrSet() )
1730         return 0;
1731     InvalidateInSwCache(RES_ATTRSET_CHG);
1732 
1733     // If Modify is locked, do not send out any Modifys
1734     if( IsModifyLocked() )
1735     {
1736         sal_uInt16 nDel = ClearItemsFromAttrSet( { 0 } );
1737         if( !GetpSwAttrSet()->Count() ) // Empty? Delete
1738             mpAttrSet.reset();
1739         return nDel;
1740     }
1741 
1742     SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
1743               aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
1744     bool bRet = 0 != AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, 0, &aOld, &aNew );
1745 
1746     if( bRet )
1747     {
1748         sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
1749         if( !GetpSwAttrSet()->Count() ) // Empty? Delete
1750             mpAttrSet.reset();
1751     }
1752     return aNew.Count();
1753 }
1754 
1755 bool SwContentNode::GetAttr( SfxItemSet& rSet ) const
1756 {
1757     if( rSet.Count() )
1758         rSet.ClearItem();
1759 
1760     const SwAttrSet& rAttrSet = GetSwAttrSet();
1761     return rSet.Set( rAttrSet );
1762 }
1763 
1764 sal_uInt16 SwContentNode::ClearItemsFromAttrSet( const std::vector<sal_uInt16>& rWhichIds )
1765 {
1766     sal_uInt16 nRet = 0;
1767     if ( rWhichIds.empty() )
1768         return nRet;
1769 
1770     OSL_ENSURE( GetpSwAttrSet(), "no item set" );
1771     SwAttrSet aNewAttrSet( *GetpSwAttrSet() );
1772     for ( const auto& rWhichId : rWhichIds )
1773     {
1774         nRet = nRet + aNewAttrSet.ClearItem( rWhichId );
1775     }
1776     if ( nRet )
1777         AttrSetHandleHelper::GetNewAutoStyle( mpAttrSet, *this, aNewAttrSet );
1778 
1779     return nRet;
1780 }
1781 
1782 const SfxPoolItem* SwContentNode::GetNoCondAttr( sal_uInt16 nWhich,
1783                                                bool bInParents ) const
1784 {
1785     const SfxPoolItem* pFnd = nullptr;
1786     if( m_pCondColl && m_pCondColl->GetRegisteredIn() )
1787     {
1788         if( !GetpSwAttrSet() || ( SfxItemState::SET != GetpSwAttrSet()->GetItemState(
1789                     nWhich, false, &pFnd ) && bInParents ))
1790         {
1791             (void)static_cast<const SwFormat*>(GetRegisteredIn())->GetItemState( nWhich, bInParents, &pFnd );
1792         }
1793     }
1794     // undo change of issue #i51029#
1795     // Note: <GetSwAttrSet()> returns <mpAttrSet>, if set, otherwise it returns
1796     //       the attribute set of the paragraph style, which is valid for the
1797     //       content node - see file <node.hxx>
1798     else
1799     {
1800         GetSwAttrSet().GetItemState( nWhich, bInParents, &pFnd );
1801     }
1802     return pFnd;
1803 }
1804 
1805 static bool lcl_CheckMaxLength(SwNode const& rPrev, SwNode const& rNext)
1806 {
1807     if (rPrev.GetNodeType() != rNext.GetNodeType())
1808     {
1809         return false;
1810     }
1811     if (!rPrev.IsTextNode())
1812     {
1813         return true;
1814     }
1815 
1816     // Check if a node can contain the other (order is not significant)
1817     return rPrev.GetTextNode()->GetSpaceLeft() > rNext.GetTextNode()->Len();
1818 }
1819 
1820 /// Can we join two Nodes?
1821 /// We can return the 2nd position in pIdx.
1822 bool SwContentNode::CanJoinNext( SwNodeIndex* pIdx ) const
1823 {
1824     const SwNodes& rNds = GetNodes();
1825     SwNodeIndex aIdx( *this, 1 );
1826 
1827     const SwNode* pNd = this;
1828     while( aIdx < rNds.Count()-1 &&
1829         (( pNd = &aIdx.GetNode())->IsSectionNode() ||
1830             ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )))
1831         ++aIdx;
1832 
1833     if (rNds.Count()-1 == aIdx.GetIndex())
1834         return false;
1835     if (!lcl_CheckMaxLength(*this, *pNd))
1836     {
1837         return false;
1838     }
1839     if( pIdx )
1840         *pIdx = aIdx;
1841     return true;
1842 }
1843 
1844 /// Can we join two Nodes?
1845 /// We can return the 2nd position in pIdx.
1846 bool SwContentNode::CanJoinPrev( SwNodeIndex* pIdx ) const
1847 {
1848     SwNodeIndex aIdx( *this, -1 );
1849 
1850     const SwNode* pNd = this;
1851     while( aIdx.GetIndex() &&
1852         (( pNd = &aIdx.GetNode())->IsSectionNode() ||
1853             ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )))
1854         --aIdx;
1855 
1856     if (SwNodeOffset(0) == aIdx.GetIndex())
1857         return false;
1858     if (!lcl_CheckMaxLength(*pNd, *this))
1859     {
1860         return false;
1861     }
1862     if( pIdx )
1863         *pIdx = aIdx;
1864     return true;
1865 }
1866 
1867 void SwContentNode::SetCondFormatColl(SwFormatColl* pColl)
1868 {
1869     if( !((!pColl && m_pCondColl) || ( pColl && !m_pCondColl ) ||
1870         ( pColl && pColl != m_pCondColl->GetRegisteredIn() )) )
1871         return;
1872 
1873     SwFormatColl* pOldColl = GetCondFormatColl();
1874     m_aCondCollListener.EndListeningAll();
1875     if(pColl)
1876         m_aCondCollListener.StartListening(pColl);
1877     m_pCondColl = pColl;
1878     if(GetpSwAttrSet())
1879         AttrSetHandleHelper::SetParent(mpAttrSet, *this, &GetAnyFormatColl(), GetFormatColl());
1880 
1881     if(!IsModifyLocked())
1882     {
1883         SwFormatChg aTmp1(pOldColl ? pOldColl : GetFormatColl());
1884         SwFormatChg aTmp2(pColl ? pColl : GetFormatColl());
1885         CallSwClientNotify(sw::LegacyModifyHint(&aTmp1, &aTmp2));
1886     }
1887     InvalidateInSwCache(RES_ATTRSET_CHG);
1888 }
1889 
1890 bool SwContentNode::IsAnyCondition( SwCollCondition& rTmp ) const
1891 {
1892     const SwNodes& rNds = GetNodes();
1893     {
1894         Master_CollCondition nCond = Master_CollCondition::NONE;
1895         const SwStartNode* pSttNd = StartOfSectionNode();
1896         while( pSttNd )
1897         {
1898             switch( pSttNd->GetNodeType() )
1899             {
1900             case SwNodeType::Table:      nCond = Master_CollCondition::PARA_IN_TABLEBODY; break;
1901             case SwNodeType::Section:    nCond = Master_CollCondition::PARA_IN_SECTION; break;
1902 
1903             default:
1904                 switch( pSttNd->GetStartNodeType() )
1905                 {
1906                 case SwTableBoxStartNode:
1907                     {
1908                         nCond = Master_CollCondition::PARA_IN_TABLEBODY;
1909                         const SwTableNode* pTableNd = pSttNd->FindTableNode();
1910                         const SwTableBox* pBox;
1911                         if( pTableNd && nullptr != ( pBox = pTableNd->GetTable().
1912                             GetTableBox(pSttNd->GetIndex()) ) &&
1913                             pBox->IsInHeadline( &pTableNd->GetTable() ) )
1914                             nCond = Master_CollCondition::PARA_IN_TABLEHEAD;
1915                     }
1916                     break;
1917                 case SwFlyStartNode:        nCond = Master_CollCondition::PARA_IN_FRAME; break;
1918                 case SwFootnoteStartNode:
1919                     {
1920                         nCond = Master_CollCondition::PARA_IN_FOOTNOTE;
1921                         const SwFootnoteIdxs& rFootnoteArr = rNds.GetDoc().GetFootnoteIdxs();
1922                         const SwTextFootnote* pTextFootnote;
1923                         const SwNode* pSrchNd = pSttNd;
1924 
1925                         for( size_t n = 0; n < rFootnoteArr.size(); ++n )
1926                             if( nullptr != ( pTextFootnote = rFootnoteArr[ n ])->GetStartNode() &&
1927                                 pSrchNd == &pTextFootnote->GetStartNode()->GetNode() )
1928                             {
1929                                 if( pTextFootnote->GetFootnote().IsEndNote() )
1930                                     nCond = Master_CollCondition::PARA_IN_ENDNOTE;
1931                                 break;
1932                             }
1933                     }
1934                     break;
1935                 case SwHeaderStartNode:     nCond = Master_CollCondition::PARA_IN_HEADER; break;
1936                 case SwFooterStartNode:     nCond = Master_CollCondition::PARA_IN_FOOTER; break;
1937                 case SwNormalStartNode:     break;
1938                 }
1939             }
1940 
1941             if( nCond != Master_CollCondition::NONE )
1942             {
1943                 rTmp.SetCondition( nCond, 0 );
1944                 return true;
1945             }
1946             pSttNd = pSttNd->GetIndex()
1947                         ? pSttNd->StartOfSectionNode()
1948                         : nullptr;
1949         }
1950     }
1951 
1952     {
1953         SwOutlineNodes::size_type nPos;
1954         const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
1955         if( !rOutlNds.empty() )
1956         {
1957             if( !rOutlNds.Seek_Entry( const_cast<SwContentNode*>(this), &nPos ) && nPos )
1958                 --nPos;
1959             if( nPos < rOutlNds.size() &&
1960                 rOutlNds[ nPos ]->GetIndex() < GetIndex() )
1961             {
1962                 SwTextNode* pOutlNd = rOutlNds[ nPos ]->GetTextNode();
1963 
1964                 if( pOutlNd->IsOutline())
1965                 {
1966                     rTmp.SetCondition( Master_CollCondition::PARA_IN_OUTLINE, pOutlNd->GetAttrOutlineLevel() - 1 );
1967                     return true;
1968                 }
1969             }
1970         }
1971     }
1972 
1973     return false;
1974 }
1975 
1976 void SwContentNode::ChkCondColl(const SwTextFormatColl* pColl)
1977 {
1978     if(pColl != GetRegisteredIn())
1979     {
1980         SAL_WARN("sw.core", "Wrong cond collection, skipping check of Cond Colls.");
1981         return;
1982     }
1983     if(&GetNodes() != &GetDoc().GetNodes())
1984     {
1985         SAL_WARN("sw.core", "Nodes amiss, skipping check of Cond Colls.");
1986         return;
1987     }
1988     // Check, just to be sure
1989     if( RES_CONDTXTFMTCOLL != GetFormatColl()->Which() )
1990         return;
1991 
1992     SwCollCondition aTmp( nullptr, Master_CollCondition::NONE, 0 );
1993     const SwCollCondition* pCColl;
1994 
1995     bool bDone = false;
1996 
1997     if( IsAnyCondition( aTmp ))
1998     {
1999         pCColl = static_cast<SwConditionTextFormatColl*>(GetFormatColl())
2000             ->HasCondition( aTmp );
2001 
2002         if (pCColl)
2003         {
2004             SetCondFormatColl( pCColl->GetTextFormatColl() );
2005             bDone = true;
2006         }
2007     }
2008 
2009     if (bDone)
2010         return;
2011 
2012     if( IsTextNode() && static_cast<SwTextNode*>(this)->GetNumRule())
2013     {
2014         // Is at which Level in a list?
2015         aTmp.SetCondition( Master_CollCondition::PARA_IN_LIST,
2016                         static_cast<SwTextNode*>(this)->GetActualListLevel() );
2017         pCColl = static_cast<SwConditionTextFormatColl*>(GetFormatColl())->
2018                         HasCondition( aTmp );
2019     }
2020     else
2021         pCColl = nullptr;
2022 
2023     if( pCColl )
2024         SetCondFormatColl( pCColl->GetTextFormatColl() );
2025     else if( m_pCondColl )
2026         SetCondFormatColl( nullptr );
2027 }
2028 
2029 // #i42921#
2030 SvxFrameDirection SwContentNode::GetTextDirection( const SwPosition& rPos,
2031                                      const Point* pPt ) const
2032 {
2033     SvxFrameDirection nRet = SvxFrameDirection::Unknown;
2034 
2035     Point aPt;
2036     if( pPt )
2037         aPt = *pPt;
2038 
2039     // #i72024# - No format of the frame, because this can cause recursive layout actions
2040     std::pair<Point, bool> const tmp(aPt, false);
2041     SwFrame* pFrame = getLayoutFrame(GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), &rPos, &tmp);
2042 
2043     if ( pFrame )
2044     {
2045         if ( pFrame->IsVertical() )
2046         {
2047             if (pFrame->IsVertLRBT())
2048                 nRet = SvxFrameDirection::Vertical_LR_BT;
2049             else if (pFrame->IsRightToLeft())
2050                 nRet = SvxFrameDirection::Vertical_LR_TB;
2051             else
2052                 nRet = SvxFrameDirection::Vertical_RL_TB;
2053         }
2054         else
2055         {
2056             if ( pFrame->IsRightToLeft() )
2057                 nRet = SvxFrameDirection::Horizontal_RL_TB;
2058             else
2059                 nRet = SvxFrameDirection::Horizontal_LR_TB;
2060         }
2061     }
2062 
2063     return nRet;
2064 }
2065 
2066 std::unique_ptr<SwOLENodes> SwContentNode::CreateOLENodesArray( const SwFormatColl& rColl, bool bOnlyWithInvalidSize )
2067 {
2068     std::unique_ptr<SwOLENodes> pNodes;
2069     SwIterator<SwContentNode,SwFormatColl> aIter( rColl );
2070     for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() )
2071     {
2072         SwOLENode *pONd = pNd->GetOLENode();
2073         if ( pONd && (!bOnlyWithInvalidSize || pONd->IsOLESizeInvalid()) )
2074         {
2075             if ( !pNodes  )
2076                 pNodes.reset(new SwOLENodes);
2077             pNodes->push_back( pONd );
2078         }
2079     }
2080 
2081     return pNodes;
2082 }
2083 
2084 drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwContentNode::getSdrAllFillAttributesHelper() const
2085 {
2086     return drawinglayer::attribute::SdrAllFillAttributesHelperPtr();
2087 }
2088 
2089 /*
2090  * Document Interface Access
2091  */
2092 const IDocumentSettingAccess* SwNode::getIDocumentSettingAccess() const { return &GetDoc().GetDocumentSettingManager(); }
2093 const IDocumentDeviceAccess& SwNode::getIDocumentDeviceAccess() const { return GetDoc().getIDocumentDeviceAccess(); }
2094 const IDocumentRedlineAccess& SwNode::getIDocumentRedlineAccess() const { return GetDoc().getIDocumentRedlineAccess(); }
2095 const IDocumentStylePoolAccess& SwNode::getIDocumentStylePoolAccess() const { return GetDoc().getIDocumentStylePoolAccess(); }
2096 const IDocumentDrawModelAccess& SwNode::getIDocumentDrawModelAccess() const { return GetDoc().getIDocumentDrawModelAccess(); }
2097 const IDocumentLayoutAccess& SwNode::getIDocumentLayoutAccess() const { return GetDoc().getIDocumentLayoutAccess(); }
2098 IDocumentLayoutAccess& SwNode::getIDocumentLayoutAccess() { return GetDoc().getIDocumentLayoutAccess(); }
2099 const IDocumentLinksAdministration& SwNode::getIDocumentLinksAdministration() const { return GetDoc().getIDocumentLinksAdministration(); }
2100 IDocumentLinksAdministration& SwNode::getIDocumentLinksAdministration() { return GetDoc().getIDocumentLinksAdministration(); }
2101 const IDocumentFieldsAccess& SwNode::getIDocumentFieldsAccess() const { return GetDoc().getIDocumentFieldsAccess(); }
2102 IDocumentFieldsAccess& SwNode::getIDocumentFieldsAccess() { return GetDoc().getIDocumentFieldsAccess(); }
2103 IDocumentContentOperations& SwNode::getIDocumentContentOperations() { return GetDoc().getIDocumentContentOperations(); }
2104 IDocumentListItems& SwNode::getIDocumentListItems() { return GetDoc().getIDocumentListItems(); } // #i83479#
2105 
2106 const IDocumentMarkAccess* SwNode::getIDocumentMarkAccess() const { return GetDoc().getIDocumentMarkAccess(); }
2107 IStyleAccess& SwNode::getIDocumentStyleAccess() { return GetDoc().GetIStyleAccess(); }
2108 
2109 bool SwNode::IsInRedlines() const
2110 {
2111     const SwDoc& rDoc = GetDoc();
2112 
2113     return rDoc.getIDocumentRedlineAccess().IsInRedlines(*this);
2114 }
2115 
2116 void SwNode::AddAnchoredFly(SwFrameFormat *const pFlyFormat)
2117 {
2118     assert(pFlyFormat);
2119     assert(&pFlyFormat->GetAnchor(false).GetContentAnchor()->nNode.GetNode() == this);
2120     // check node type, cf. SwFormatAnchor::SetAnchor()
2121     assert(IsTextNode() || IsStartNode() || IsTableNode());
2122     m_aAnchoredFlys.push_back(pFlyFormat);
2123 }
2124 
2125 void SwNode::RemoveAnchoredFly(SwFrameFormat *const pFlyFormat)
2126 {
2127     assert(pFlyFormat);
2128     // cannot assert this in Remove because it is called when new anchor is already set
2129 //    assert(&pFlyFormat->GetAnchor(false).GetContentAnchor()->nNode.GetNode() == this);
2130     assert(IsTextNode() || IsStartNode() || IsTableNode());
2131     auto it(std::find(m_aAnchoredFlys.begin(), m_aAnchoredFlys.end(), pFlyFormat));
2132     assert(it != m_aAnchoredFlys.end());
2133     m_aAnchoredFlys.erase(it);
2134 }
2135 
2136 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2137