xref: /core/sw/source/core/doc/doctxm.cxx (revision 5f9ffc31)
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 <limits.h>
21 #include <hintids.hxx>
22 #include <editeng/formatbreakitem.hxx>
23 #include <comphelper/classids.hxx>
24 #include <docsh.hxx>
25 #include <ndole.hxx>
26 #include <txttxmrk.hxx>
27 #include <fmtpdsc.hxx>
28 #include <frmatr.hxx>
29 #include <pagedesc.hxx>
30 #include <doc.hxx>
31 #include <IDocumentUndoRedo.hxx>
32 #include <DocumentSettingManager.hxx>
33 #include <IDocumentRedlineAccess.hxx>
34 #include <IDocumentFieldsAccess.hxx>
35 #include <IDocumentState.hxx>
36 #include <IDocumentLayoutAccess.hxx>
37 #include <IDocumentStylePoolAccess.hxx>
38 #include <pagefrm.hxx>
39 #include <ndtxt.hxx>
40 #include <swtable.hxx>
41 #include <doctxm.hxx>
42 #include <txmsrt.hxx>
43 #include <rolbck.hxx>
44 #include <poolfmt.hxx>
45 #include <txtfrm.hxx>
46 #include <rootfrm.hxx>
47 #include <UndoAttribute.hxx>
48 #include <UndoSection.hxx>
49 #include <swundo.hxx>
50 #include <mdiexp.hxx>
51 #include <docary.hxx>
52 #include <charfmt.hxx>
53 #include <fchrfmt.hxx>
54 #include <fldbas.hxx>
55 #include <fmtfld.hxx>
56 #include <txtfld.hxx>
57 #include <expfld.hxx>
58 #include <mvsave.hxx>
59 #include <node2lay.hxx>
60 #include <SwStyleNameMapper.hxx>
61 #include <breakit.hxx>
62 #include <calbck.hxx>
63 #include <ToxTextGenerator.hxx>
64 #include <ToxTabStopTokenHandler.hxx>
65 #include <frameformats.hxx>
66 #include <tools/datetimeutils.hxx>
67 #include <tools/globname.hxx>
68 #include <com/sun/star/embed/XEmbeddedObject.hpp>
69 #include <o3tl/safeint.hxx>
70 #include <osl/diagnose.h>
71 
72 #include <memory>
73 
74 using namespace ::com::sun::star;
75 
76 template<typename T, typename... Args> static
77 typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
78 MakeSwTOXSortTabBase(SwRootFrame const*const pLayout, Args&& ... args)
79 {
80     std::unique_ptr<T> pRet(new T(std::forward<Args>(args)...));
81     pRet->InitText(pLayout); // ensure it's expanded with the layout
82     return pRet;
83 }
84 
85 void SwDoc::GetTOIKeys(SwTOIKeyType eTyp, std::vector<OUString>& rArr,
86         SwRootFrame const& rLayout) const
87 {
88     rArr.clear();
89 
90     // Look up all Primary and Secondary via the Pool
91     for (const SfxPoolItem* pPoolItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_TOXMARK))
92     {
93         const SwTOXMark* pItem = dynamic_cast<const SwTOXMark*>(pPoolItem);
94         if( !pItem )
95             continue;
96         const SwTOXType* pTOXType = pItem->GetTOXType();
97         if ( !pTOXType || pTOXType->GetType()!=TOX_INDEX )
98             continue;
99         const SwTextTOXMark* pMark = pItem->GetTextTOXMark();
100         if ( pMark && pMark->GetpTextNd() &&
101              pMark->GetpTextNd()->GetNodes().IsDocNodes() &&
102              (!rLayout.IsHideRedlines()
103                 || !sw::IsMarkHintHidden(rLayout, *pMark->GetpTextNd(), *pMark)))
104         {
105             const OUString sStr = TOI_PRIMARY == eTyp
106                 ? pItem->GetPrimaryKey()
107                 : pItem->GetSecondaryKey();
108 
109             if( !sStr.isEmpty() )
110                 rArr.push_back( sStr );
111         }
112     }
113 }
114 
115 /// Get current table of contents Mark.
116 sal_uInt16 SwDoc::GetCurTOXMark( const SwPosition& rPos,
117                                 SwTOXMarks& rArr )
118 {
119     // search on Position rPos for all SwTOXMarks
120     SwTextNode *const pTextNd = rPos.nNode.GetNode().GetTextNode();
121     if( !pTextNd || !pTextNd->GetpSwpHints() )
122         return 0;
123 
124     const SwpHints & rHts = *pTextNd->GetpSwpHints();
125     sal_Int32 nSttIdx;
126     const sal_Int32 *pEndIdx;
127 
128     const sal_Int32 nCurrentPos = rPos.nContent.GetIndex();
129 
130     for( size_t n = 0; n < rHts.Count(); ++n )
131     {
132         const SwTextAttr* pHt = rHts.Get(n);
133         if( RES_TXTATR_TOXMARK != pHt->Which() )
134             continue;
135         if( ( nSttIdx = pHt->GetStart() ) < nCurrentPos )
136         {
137             // also check the end
138             pEndIdx = pHt->End();
139             if( nullptr == pEndIdx || *pEndIdx <= nCurrentPos )
140                 continue;       // keep searching
141         }
142         else if( nSttIdx > nCurrentPos )
143             // If Hint's Start is greater than rPos, break, because
144             // the attributes are sorted by Start!
145             break;
146 
147         SwTOXMark* pTMark = const_cast<SwTOXMark*>(&pHt->GetTOXMark());
148         rArr.push_back( pTMark );
149     }
150     return rArr.size();
151 }
152 
153 /// Delete table of contents Mark
154 void SwDoc::DeleteTOXMark( const SwTOXMark* pTOXMark )
155 {
156     const SwTextTOXMark* pTextTOXMark = pTOXMark->GetTextTOXMark();
157     assert(pTextTOXMark);
158 
159     SwTextNode& rTextNd = const_cast<SwTextNode&>(pTextTOXMark->GetTextNode());
160     assert(rTextNd.GetpSwpHints());
161 
162     if (pTextTOXMark->HasDummyChar())
163     {
164         // tdf#106377 don't use SwUndoResetAttr, it uses NOTXTATRCHR
165         SwPaM tmp(rTextNd, pTextTOXMark->GetStart(),
166                   rTextNd, pTextTOXMark->GetStart()+1);
167         assert(rTextNd.GetText()[pTextTOXMark->GetStart()] == CH_TXTATR_INWORD);
168         getIDocumentContentOperations().DeleteRange(tmp);
169     }
170     else
171     {
172         std::unique_ptr<SwRegHistory> aRHst;
173         if (GetIDocumentUndoRedo().DoesUndo())
174         {
175             // save attributes for Undo
176             SwUndoResetAttr* pUndo = new SwUndoResetAttr(
177                 SwPosition( rTextNd, SwIndex( &rTextNd, pTextTOXMark->GetStart() ) ),
178                 RES_TXTATR_TOXMARK );
179             GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
180 
181             aRHst.reset(new SwRegHistory(rTextNd, &pUndo->GetHistory()));
182             rTextNd.GetpSwpHints()->Register(aRHst.get());
183         }
184 
185         rTextNd.DeleteAttribute( const_cast<SwTextTOXMark*>(pTextTOXMark) );
186 
187         if (GetIDocumentUndoRedo().DoesUndo())
188         {
189             if( rTextNd.GetpSwpHints() )
190                 rTextNd.GetpSwpHints()->DeRegister();
191         }
192     }
193 
194     getIDocumentState().SetModified();
195 }
196 
197 namespace {
198 
199 /// Travel between table of content Marks
200 class CompareNodeContent
201 {
202     SwNodeOffset m_nNode;
203     sal_Int32 m_nContent;
204 public:
205     CompareNodeContent( SwNodeOffset nNd, sal_Int32 nCnt )
206         : m_nNode( nNd ), m_nContent( nCnt ) {}
207 
208     bool operator==( const CompareNodeContent& rCmp ) const
209         { return m_nNode == rCmp.m_nNode && m_nContent == rCmp.m_nContent; }
210     bool operator!=( const CompareNodeContent& rCmp ) const
211         { return m_nNode != rCmp.m_nNode || m_nContent != rCmp.m_nContent; }
212     bool operator< ( const CompareNodeContent& rCmp ) const
213         { return m_nNode < rCmp.m_nNode ||
214             ( m_nNode == rCmp.m_nNode && m_nContent < rCmp.m_nContent); }
215     bool operator<=( const CompareNodeContent& rCmp ) const
216         { return m_nNode < rCmp.m_nNode ||
217             ( m_nNode == rCmp.m_nNode && m_nContent <= rCmp.m_nContent); }
218     bool operator> ( const CompareNodeContent& rCmp ) const
219         { return m_nNode > rCmp.m_nNode ||
220             ( m_nNode == rCmp.m_nNode && m_nContent > rCmp.m_nContent); }
221     bool operator>=( const CompareNodeContent& rCmp ) const
222         { return m_nNode > rCmp.m_nNode ||
223             ( m_nNode == rCmp.m_nNode && m_nContent >= rCmp.m_nContent); }
224 };
225 
226 }
227 
228 const SwTOXMark& SwDoc::GotoTOXMark( const SwTOXMark& rCurTOXMark,
229                                     SwTOXSearch eDir, bool bInReadOnly )
230 {
231     const SwTextTOXMark* pMark = rCurTOXMark.GetTextTOXMark();
232 
233     CompareNodeContent aAbsIdx(pMark ? pMark->GetpTextNd()->GetIndex() : SwNodeOffset(0), pMark ? pMark->GetStart() : 0);
234     CompareNodeContent aPrevPos( SwNodeOffset(0), 0 );
235     CompareNodeContent aNextPos( NODE_OFFSET_MAX, SAL_MAX_INT32 );
236     CompareNodeContent aMax( SwNodeOffset(0), 0 );
237     CompareNodeContent aMin( NODE_OFFSET_MAX, SAL_MAX_INT32 );
238 
239     const SwTOXMark*    pNew    = nullptr;
240     const SwTOXMark*    pMax    = &rCurTOXMark;
241     const SwTOXMark*    pMin    = &rCurTOXMark;
242 
243     const SwTOXType* pType = rCurTOXMark.GetTOXType();
244     SwTOXMarks aMarks;
245     pType->CollectTextMarks(aMarks);
246 
247     for(SwTOXMark* pTOXMark : aMarks)
248     {
249         if ( pTOXMark == &rCurTOXMark )
250             continue;
251 
252         pMark = pTOXMark->GetTextTOXMark();
253         if (!pMark)
254             continue;
255 
256         SwTextNode const*const pTOXSrc = pMark->GetpTextNd();
257         if (!pTOXSrc)
258             continue;
259 
260         Point aPt;
261         std::pair<Point, bool> const tmp(aPt, false);
262         const SwContentFrame* pCFrame = pTOXSrc->getLayoutFrame(
263                 getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp);
264         if (!pCFrame)
265             continue;
266 
267         if ( bInReadOnly || !pCFrame->IsProtected() )
268         {
269             CompareNodeContent aAbsNew( pTOXSrc->GetIndex(), pMark->GetStart() );
270             switch( eDir )
271             {
272             // The following (a bit more complicated) statements make it
273             // possible to also travel across Entries on the same (!)
274             // position. If someone has time, please feel free to optimize.
275             case TOX_SAME_PRV:
276                 if (pTOXMark->GetText(nullptr) != rCurTOXMark.GetText(nullptr))
277                     break;
278                 [[fallthrough]];
279             case TOX_PRV:
280                 if ( (aAbsNew < aAbsIdx && aAbsNew > aPrevPos) ||
281                      (aAbsIdx == aAbsNew &&
282                       (reinterpret_cast<sal_uLong>(&rCurTOXMark) > reinterpret_cast<sal_uLong>(pTOXMark) &&
283                        (!pNew || aPrevPos < aAbsIdx || reinterpret_cast<sal_uLong>(pNew) < reinterpret_cast<sal_uLong>(pTOXMark) ) )) ||
284                      (aPrevPos == aAbsNew && aAbsIdx != aAbsNew &&
285                       reinterpret_cast<sal_uLong>(pTOXMark) > reinterpret_cast<sal_uLong>(pNew)) )
286                 {
287                     pNew = pTOXMark;
288                     aPrevPos = aAbsNew;
289                     if ( aAbsNew >= aMax )
290                     {
291                         aMax = aAbsNew;
292                         pMax = pTOXMark;
293                     }
294                 }
295                 break;
296 
297             case TOX_SAME_NXT:
298                 if (pTOXMark->GetText(nullptr) != rCurTOXMark.GetText(nullptr))
299                     break;
300                 [[fallthrough]];
301             case TOX_NXT:
302                 if ( (aAbsNew > aAbsIdx && aAbsNew < aNextPos) ||
303                      (aAbsIdx == aAbsNew &&
304                       (reinterpret_cast<sal_uLong>(&rCurTOXMark) < reinterpret_cast<sal_uLong>(pTOXMark) &&
305                        (!pNew || aNextPos > aAbsIdx || reinterpret_cast<sal_uLong>(pNew) > reinterpret_cast<sal_uLong>(pTOXMark)) )) ||
306                      (aNextPos == aAbsNew && aAbsIdx != aAbsNew &&
307                       reinterpret_cast<sal_uLong>(pTOXMark) < reinterpret_cast<sal_uLong>(pNew)) )
308                 {
309                     pNew = pTOXMark;
310                     aNextPos = aAbsNew;
311                     if ( aAbsNew <= aMin )
312                     {
313                         aMin = aAbsNew;
314                         pMin = pTOXMark;
315                     }
316                 }
317                 break;
318             }
319         }
320     }
321 
322     // We couldn't find a successor
323     // Use minimum or maximum
324     if(!pNew)
325     {
326         switch(eDir)
327         {
328         case TOX_PRV:
329         case TOX_SAME_PRV:
330             pNew = pMax;
331             break;
332         case TOX_NXT:
333         case TOX_SAME_NXT:
334             pNew = pMin;
335             break;
336         default:
337             pNew = &rCurTOXMark;
338         }
339     }
340     return *pNew;
341 }
342 
343 SwTOXBaseSection* SwDoc::InsertTableOf( const SwPosition& rPos,
344                                         const SwTOXBase& rTOX,
345                                         const SfxItemSet* pSet,
346                                         bool bExpand,
347                                         SwRootFrame const*const pLayout)
348 {
349     SwPaM aPam( rPos );
350     return InsertTableOf( aPam, rTOX, pSet, bExpand, pLayout );
351 }
352 
353 SwTOXBaseSection* SwDoc::InsertTableOf( const SwPaM& aPam,
354                                         const SwTOXBase& rTOX,
355                                         const SfxItemSet* pSet,
356                                         bool bExpand,
357                                         SwRootFrame const*const pLayout )
358 {
359     assert(!bExpand || pLayout != nullptr);
360     GetIDocumentUndoRedo().StartUndo( SwUndoId::INSTOX, nullptr );
361 
362     OUString sSectNm = GetUniqueTOXBaseName( *rTOX.GetTOXType(), rTOX.GetTOXName() );
363     SwSectionData aSectionData( SectionType::ToxContent, sSectNm );
364 
365     std::tuple<SwTOXBase const*, sw::RedlineMode, sw::FieldmarkMode> const tmp(
366         &rTOX,
367         pLayout && pLayout->IsHideRedlines()
368             ? sw::RedlineMode::Hidden
369             : sw::RedlineMode::Shown,
370         pLayout ? pLayout->GetFieldmarkMode() : sw::FieldmarkMode::ShowBoth);
371     SwTOXBaseSection *const pNewSection = dynamic_cast<SwTOXBaseSection *>(
372         InsertSwSection(aPam, aSectionData, & tmp, pSet, false));
373     if (pNewSection)
374     {
375         SwSectionNode *const pSectNd = pNewSection->GetFormat()->GetSectionNode();
376         pNewSection->SetTOXName(sSectNm); // rTOX may have had no name...
377 
378         if( bExpand )
379         {
380             // add value for 2nd parameter = true to
381             // indicate, that a creation of a new table of content has to be performed.
382             // Value of 1st parameter = default value.
383             pNewSection->Update( nullptr, pLayout, true );
384         }
385         else if( rTOX.GetTitle().getLength()==1 && IsInReading() )
386         // insert title of TOX
387         {
388             // then insert the headline section
389             SwNodeIndex aIdx( *pSectNd, +1 );
390 
391             SwTextNode* pHeadNd = GetNodes().MakeTextNode( aIdx,
392                             getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
393 
394             SwSectionData headerData( SectionType::ToxHeader, pNewSection->GetTOXName()+"_Head" );
395 
396             SwNodeIndex aStt( *pHeadNd ); --aIdx;
397             SwSectionFormat* pSectFormat = MakeSectionFormat();
398             GetNodes().InsertTextSection(
399                     aStt, *pSectFormat, headerData, nullptr, &aIdx, true, false);
400         }
401     }
402 
403     GetIDocumentUndoRedo().EndUndo( SwUndoId::INSTOX, nullptr );
404 
405     return pNewSection;
406 }
407 
408 void SwDoc::InsertTableOf( SwNodeOffset nSttNd, SwNodeOffset nEndNd,
409                                                 const SwTOXBase& rTOX,
410                                                 const SfxItemSet* pSet )
411 {
412     // check for recursive TOX
413     SwNode* pNd = GetNodes()[ nSttNd ];
414     SwSectionNode* pSectNd = pNd->FindSectionNode();
415     while( pSectNd )
416     {
417         SectionType eT = pSectNd->GetSection().GetType();
418         if( SectionType::ToxHeader == eT || SectionType::ToxContent == eT )
419             return;
420         pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
421     }
422 
423     const OUString sSectNm = GetUniqueTOXBaseName(*rTOX.GetTOXType(), rTOX.GetTOXName());
424 
425     SwSectionData aSectionData( SectionType::ToxContent, sSectNm );
426 
427     SwNodeIndex aStt( GetNodes(), nSttNd ), aEnd( GetNodes(), nEndNd );
428     SwSectionFormat* pFormat = MakeSectionFormat();
429     if(pSet)
430         pFormat->SetFormatAttr(*pSet);
431 
432     SwSectionNode *const pNewSectionNode =
433         GetNodes().InsertTextSection(aStt, *pFormat, aSectionData, &rTOX, &aEnd);
434     if (!pNewSectionNode)
435     {
436         DelSectionFormat( pFormat );
437         return;
438     }
439 
440     SwTOXBaseSection *const pNewSection(
441         dynamic_cast<SwTOXBaseSection*>(& pNewSectionNode->GetSection()));
442     if (pNewSection)
443         pNewSection->SetTOXName(sSectNm); // rTOX may have had no name...
444 }
445 
446 /// Get current table of contents
447 SwTOXBase* SwDoc::GetCurTOX( const SwPosition& rPos )
448 {
449     SwNode& rNd = rPos.nNode.GetNode();
450     SwSectionNode* pSectNd = rNd.FindSectionNode();
451     while( pSectNd )
452     {
453         SectionType eT = pSectNd->GetSection().GetType();
454         if( SectionType::ToxContent == eT )
455         {
456             assert( dynamic_cast< const SwTOXBaseSection *>( &pSectNd->GetSection()) &&
457                     "no TOXBaseSection!" );
458             SwTOXBaseSection& rTOXSect = static_cast<SwTOXBaseSection&>(
459                                                 pSectNd->GetSection());
460             return &rTOXSect;
461         }
462         pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
463     }
464     return nullptr;
465 }
466 
467 const SwAttrSet& SwDoc::GetTOXBaseAttrSet(const SwTOXBase& rTOXBase)
468 {
469     assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
470     const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase);
471     SwSectionFormat const * pFormat = rTOXSect.GetFormat();
472     OSL_ENSURE( pFormat, "invalid TOXBaseSection!" );
473     return pFormat->GetAttrSet();
474 }
475 
476 const SwTOXBase* SwDoc::GetDefaultTOXBase( TOXTypes eTyp, bool bCreate )
477 {
478     std::unique_ptr<SwTOXBase>* prBase = nullptr;
479     switch(eTyp)
480     {
481     case  TOX_CONTENT:          prBase = &mpDefTOXBases->pContBase; break;
482     case  TOX_INDEX:            prBase = &mpDefTOXBases->pIdxBase;  break;
483     case  TOX_USER:             prBase = &mpDefTOXBases->pUserBase; break;
484     case  TOX_TABLES:           prBase = &mpDefTOXBases->pTableBase;  break;
485     case  TOX_OBJECTS:          prBase = &mpDefTOXBases->pObjBase;  break;
486     case  TOX_ILLUSTRATIONS:    prBase = &mpDefTOXBases->pIllBase;  break;
487     case  TOX_AUTHORITIES:      prBase = &mpDefTOXBases->pAuthBase; break;
488     case  TOX_BIBLIOGRAPHY:      prBase = &mpDefTOXBases->pBiblioBase; break;
489     case  TOX_CITATION: /** TODO */break;
490     }
491     if (!prBase)
492         return nullptr;
493     if(!(*prBase) && bCreate)
494     {
495         SwForm aForm(eTyp);
496         const SwTOXType* pType = GetTOXType(eTyp, 0);
497         prBase->reset(new SwTOXBase(pType, aForm, SwTOXElement::NONE, pType->GetTypeName()));
498     }
499     return prBase->get();
500 }
501 
502 void    SwDoc::SetDefaultTOXBase(const SwTOXBase& rBase)
503 {
504     std::unique_ptr<SwTOXBase>* prBase = nullptr;
505     switch(rBase.GetType())
506     {
507     case  TOX_CONTENT:          prBase = &mpDefTOXBases->pContBase; break;
508     case  TOX_INDEX:            prBase = &mpDefTOXBases->pIdxBase;  break;
509     case  TOX_USER:             prBase = &mpDefTOXBases->pUserBase; break;
510     case  TOX_TABLES:           prBase = &mpDefTOXBases->pTableBase;  break;
511     case  TOX_OBJECTS:          prBase = &mpDefTOXBases->pObjBase;  break;
512     case  TOX_ILLUSTRATIONS:    prBase = &mpDefTOXBases->pIllBase;  break;
513     case  TOX_AUTHORITIES:      prBase = &mpDefTOXBases->pAuthBase; break;
514     case  TOX_BIBLIOGRAPHY:      prBase = &mpDefTOXBases->pBiblioBase; break;
515     case  TOX_CITATION: /** TODO */break;
516     }
517     if (!prBase)
518         return;
519     prBase->reset(new SwTOXBase(rBase));
520 }
521 
522 /// Delete table of contents
523 bool SwDoc::DeleteTOX( const SwTOXBase& rTOXBase, bool bDelNodes )
524 {
525     // We only delete the TOX, not the Nodes
526     bool bRet = false;
527     assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
528 
529     const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase);
530     SwSectionFormat const * pFormat = rTOXSect.GetFormat();
531     /* Save the start node of the TOX' section. */
532     SwSectionNode const * pMyNode = pFormat ? pFormat->GetSectionNode() : nullptr;
533     if (pMyNode)
534     {
535         GetIDocumentUndoRedo().StartUndo( SwUndoId::CLEARTOXRANGE, nullptr );
536 
537         /* Save start node of section's surrounding. */
538         SwNode const * pStartNd = pMyNode->StartOfSectionNode();
539 
540         /* Look for the point where to move the cursors in the area to
541            delete to. This is done by first searching forward from the
542            end of the TOX' section. If no content node is found behind
543            the TOX one is searched before it. If this is not
544            successful, too, insert new text node behind the end of
545            the TOX' section. The cursors from the TOX' section will be
546            moved to the content node found or the new text node. */
547 
548         /* Set PaM to end of TOX' section and search following content node.
549            aSearchPam will contain the point where to move the cursors
550            to. */
551         SwPaM aSearchPam(*pMyNode->EndOfSectionNode());
552         SwPosition aEndPos(*pStartNd->EndOfSectionNode());
553         if (! aSearchPam.Move() /* no content node found */
554             || *aSearchPam.GetPoint() >= aEndPos /* content node found
555                                                     outside surrounding */
556             )
557         {
558             /* Set PaM to beginning of TOX' section and search previous
559                content node */
560             SwPaM aTmpPam(*pMyNode);
561             aSearchPam = aTmpPam;
562             SwPosition aStartPos(*pStartNd);
563 
564             if ( ! aSearchPam.Move(fnMoveBackward) /* no content node found */
565                  || *aSearchPam.GetPoint() <= aStartPos  /* content node
566                                                             found outside
567                                                             surrounding */
568                  )
569             {
570                 /* There is no content node in the surrounding of
571                    TOX'. Append text node behind TOX' section. */
572 
573                 SwPosition aInsPos(*pMyNode->EndOfSectionNode());
574                 getIDocumentContentOperations().AppendTextNode(aInsPos);
575 
576                 SwPaM aTmpPam1(aInsPos);
577                 aSearchPam = aTmpPam1;
578             }
579         }
580 
581         /* PaM containing the TOX. */
582         SwPaM aPam(*pMyNode->EndOfSectionNode(), *pMyNode);
583 
584         /* Move cursors contained in TOX to the above calculated point. */
585         PaMCorrAbs(aPam, *aSearchPam.GetPoint());
586 
587         if( !bDelNodes )
588         {
589             SwSections aArr( 0 );
590             pFormat->GetChildSections( aArr, SectionSort::Not, false );
591             for( const auto pSect : aArr )
592             {
593                 if( SectionType::ToxHeader == pSect->GetType() )
594                 {
595                     DelSectionFormat( pSect->GetFormat(), bDelNodes );
596                 }
597             }
598         }
599 
600         DelSectionFormat( const_cast<SwSectionFormat *>(pFormat), bDelNodes );
601 
602         GetIDocumentUndoRedo().EndUndo( SwUndoId::CLEARTOXRANGE, nullptr );
603         bRet = true;
604     }
605 
606     return bRet;
607 }
608 
609 /// Manage table of content types
610 sal_uInt16 SwDoc::GetTOXTypeCount(TOXTypes eTyp) const
611 {
612     sal_uInt16 nCnt = 0;
613     for( auto const & pTOXType : *mpTOXTypes )
614         if( eTyp == pTOXType->GetType() )
615             ++nCnt;
616     return nCnt;
617 }
618 
619 const SwTOXType* SwDoc::GetTOXType( TOXTypes eTyp, sal_uInt16 nId ) const
620 {
621     sal_uInt16 nCnt = 0;
622     for( auto const & pTOXType : *mpTOXTypes )
623         if( eTyp == pTOXType->GetType() && nCnt++ == nId )
624             return pTOXType.get();
625     return nullptr;
626 }
627 
628 const SwTOXType* SwDoc::InsertTOXType( const SwTOXType& rTyp )
629 {
630     SwTOXType * pNew = new SwTOXType(rTyp);
631     mpTOXTypes->emplace_back( pNew );
632     return pNew;
633 }
634 
635 OUString SwDoc::GetUniqueTOXBaseName( const SwTOXType& rType,
636                                       const OUString& sChkStr ) const
637 {
638     if( IsInMailMerge())
639     {
640         OUString newName = "MailMergeTOX"
641             + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
642             + OUString::number( mpSectionFormatTable->size() + 1 );
643         if( !sChkStr.isEmpty())
644             newName += sChkStr;
645         return newName;
646     }
647 
648     bool bUseChkStr = !sChkStr.isEmpty();
649     const OUString& aName( rType.GetTypeName() );
650     const sal_Int32 nNmLen = aName.getLength();
651 
652     SwSectionFormats::size_type nNum = 0;
653     const SwSectionFormats::size_type nFlagSize = ( mpSectionFormatTable->size() / 8 ) +2;
654     std::unique_ptr<sal_uInt8[]> pSetFlags(new sal_uInt8[ nFlagSize ]);
655     memset( pSetFlags.get(), 0, nFlagSize );
656 
657     for( auto pSectionFormat : *mpSectionFormatTable )
658     {
659         const SwSectionNode *pSectNd = pSectionFormat->GetSectionNode();
660         if ( !pSectNd )
661             continue;
662 
663         const SwSection& rSect = pSectNd->GetSection();
664         if (rSect.GetType()==SectionType::ToxContent)
665         {
666             const OUString& rNm = rSect.GetSectionName();
667             if ( rNm.startsWith(aName) )
668             {
669                 // Calculate number and set the Flag
670                 nNum = rNm.copy( nNmLen ).toInt32();
671                 if( nNum-- && nNum < mpSectionFormatTable->size() )
672                     pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
673             }
674             if ( bUseChkStr && sChkStr==rNm )
675                 bUseChkStr = false;
676         }
677     }
678 
679     if( !bUseChkStr )
680     {
681         // All Numbers have been flagged accordingly, so get the right Number
682         nNum = mpSectionFormatTable->size();
683         for( SwSectionFormats::size_type n = 0; n < nFlagSize; ++n )
684         {
685             sal_uInt8 nTmp = pSetFlags[ n ];
686             if( nTmp != 0xff )
687             {
688                 // so get the Number
689                 nNum = n * 8;
690                 while( nTmp & 1 )
691                 {
692                     ++nNum;
693                     nTmp >>= 1;
694                 }
695                 break;
696             }
697         }
698     }
699     if ( bUseChkStr )
700         return sChkStr;
701     return aName + OUString::number( ++nNum );
702 }
703 
704 bool SwDoc::SetTOXBaseName(const SwTOXBase& rTOXBase, const OUString& rName)
705 {
706     assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
707     SwTOXBaseSection* pTOX = const_cast<SwTOXBaseSection*>(static_cast<const SwTOXBaseSection*>(&rTOXBase));
708 
709     if (GetUniqueTOXBaseName(*rTOXBase.GetTOXType(), rName) == rName)
710     {
711         pTOX->SetTOXName(rName);
712         pTOX->SetSectionName(rName);
713         getIDocumentState().SetModified();
714         return true;
715     }
716     return false;
717 }
718 
719 static const SwTextNode* lcl_FindChapterNode( const SwNode& rNd,
720         SwRootFrame const*const pLayout, sal_uInt8 const nLvl = 0 )
721 {
722     const SwNode* pNd = &rNd;
723     if( pNd->GetNodes().GetEndOfExtras().GetIndex() > pNd->GetIndex() )
724     {
725         // then find the "Anchor" (Body) position
726         Point aPt;
727         SwNode2Layout aNode2Layout( *pNd, pNd->GetIndex() );
728         const SwFrame* pFrame = aNode2Layout.GetFrame( &aPt );
729 
730         if( pFrame )
731         {
732             SwPosition aPos( *pNd );
733             pNd = GetBodyTextNode( pNd->GetDoc(), aPos, *pFrame );
734             OSL_ENSURE( pNd, "Where's the paragraph?" );
735         }
736     }
737     return pNd ? pNd->FindOutlineNodeOfLevel(nLvl, pLayout) : nullptr;
738 }
739 
740 // Table of contents class
741 SwTOXBaseSection::SwTOXBaseSection(SwTOXBase const& rBase, SwSectionFormat & rFormat)
742     : SwTOXBase( rBase )
743     , SwSection( SectionType::ToxContent, OUString(), rFormat )
744 {
745     SetProtect( rBase.IsProtected() );
746     SetSectionName( GetTOXName() );
747 }
748 
749 SwTOXBaseSection::~SwTOXBaseSection()
750 {
751 }
752 
753 bool SwTOXBaseSection::SetPosAtStartEnd( SwPosition& rPos ) const
754 {
755     bool bRet = false;
756     const SwSectionNode* pSectNd = GetFormat()->GetSectionNode();
757     if( pSectNd )
758     {
759         rPos.nNode = *pSectNd;
760         SwContentNode* pCNd = pSectNd->GetDoc().GetNodes().GoNext( &rPos.nNode );
761         rPos.nContent.Assign( pCNd, 0 );
762         bRet = true;
763     }
764     return bRet;
765 }
766 
767 /// Collect table of contents content
768 void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
769                               SwRootFrame const*const pLayout,
770                               const bool        _bNewTOX)
771 {
772     if (!GetFormat())
773         return;
774     SwSectionNode const*const pSectNd(GetFormat()->GetSectionNode());
775     if (nullptr == pSectNd ||
776         !pSectNd->GetNodes().IsDocNodes() ||
777         IsHiddenFlag() ||
778         (pLayout->HasMergedParas() && pSectNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden))
779     {
780         return;
781     }
782 
783     if ( !mbKeepExpression )
784     {
785         maMSTOCExpression.clear();
786     }
787 
788     SwDoc& rDoc = const_cast<SwDoc&>(pSectNd->GetDoc());
789 
790     if (pAttr && GetFormat())
791         rDoc.ChgFormat(*GetFormat(), *pAttr);
792 
793     // determine default page description, which will be used by the content nodes,
794     // if no appropriate one is found.
795     const SwPageDesc* pDefaultPageDesc;
796     {
797         pDefaultPageDesc =
798             pSectNd->GetSection().GetFormat()->GetPageDesc().GetPageDesc();
799         if ( !_bNewTOX && !pDefaultPageDesc )
800         {
801             // determine page description of table-of-content
802             SwNodeOffset nPgDescNdIdx = pSectNd->GetIndex() + 1;
803             SwNodeOffset* pPgDescNdIdx = &nPgDescNdIdx;
804             pDefaultPageDesc = pSectNd->FindPageDesc( pPgDescNdIdx );
805             if ( nPgDescNdIdx < pSectNd->GetIndex() )
806             {
807                 pDefaultPageDesc = nullptr;
808             }
809         }
810         // consider end node of content section in the node array.
811         if ( !pDefaultPageDesc &&
812              ( pSectNd->EndOfSectionNode()->GetIndex() <
813                  (pSectNd->GetNodes().GetEndOfContent().GetIndex() - 1) )
814            )
815         {
816             // determine page description of content after table-of-content
817             SwNodeIndex aIdx( *(pSectNd->EndOfSectionNode()) );
818             const SwContentNode* pNdAfterTOX = pSectNd->GetNodes().GoNext( &aIdx );
819             const SwAttrSet& aNdAttrSet = pNdAfterTOX->GetSwAttrSet();
820             const SvxBreak eBreak = aNdAttrSet.GetBreak().GetBreak();
821             if ( eBreak != SvxBreak::PageBefore && eBreak != SvxBreak::PageBoth )
822             {
823                 pDefaultPageDesc = pNdAfterTOX->FindPageDesc();
824             }
825         }
826         // consider start node of content section in the node array.
827         if ( !pDefaultPageDesc &&
828              ( pSectNd->GetIndex() >
829                  (pSectNd->GetNodes().GetEndOfContent().StartOfSectionIndex() + 1) )
830            )
831         {
832             // determine page description of content before table-of-content
833             SwNodeIndex aIdx( *pSectNd );
834             pDefaultPageDesc =
835                 SwNodes::GoPrevious( &aIdx )->FindPageDesc();
836 
837         }
838         if ( !pDefaultPageDesc )
839         {
840             // determine default page description
841             pDefaultPageDesc = &rDoc.GetPageDesc( 0 );
842         }
843     }
844 
845     rDoc.getIDocumentState().SetModified();
846 
847     // get current Language
848     SwTOXInternational aIntl(  GetLanguage(),
849                                TOX_INDEX == GetTOXType()->GetType() ?
850                                GetOptions() : SwTOIOptions::NONE,
851                                GetSortAlgorithm() );
852 
853     m_aSortArr.clear();
854 
855     // find the first layout node for this TOX, if it only find the content
856     // in his own chapter
857     const SwTextNode* pOwnChapterNode = IsFromChapter()
858             ? ::lcl_FindChapterNode( *pSectNd, pLayout )
859             : nullptr;
860 
861     SwNode2LayoutSaveUpperFrames aN2L(*pSectNd);
862     const_cast<SwSectionNode*>(pSectNd)->DelFrames();
863 
864     // This would be a good time to update the Numbering
865     rDoc.UpdateNumRule();
866 
867     if( GetCreateType() & SwTOXElement::Mark )
868         UpdateMarks( aIntl, pOwnChapterNode, pLayout );
869 
870     if( GetCreateType() & SwTOXElement::OutlineLevel )
871         UpdateOutline( pOwnChapterNode, pLayout );
872 
873     if( GetCreateType() & SwTOXElement::Template )
874         UpdateTemplate( pOwnChapterNode, pLayout );
875 
876     if( GetCreateType() & SwTOXElement::Ole ||
877             TOX_OBJECTS == SwTOXBase::GetType())
878         UpdateContent( SwTOXElement::Ole, pOwnChapterNode, pLayout );
879 
880     if( GetCreateType() & SwTOXElement::Table ||
881             (TOX_TABLES == SwTOXBase::GetType() && IsFromObjectNames()) )
882         UpdateTable( pOwnChapterNode, pLayout );
883 
884     if( GetCreateType() & SwTOXElement::Graphic ||
885         (TOX_ILLUSTRATIONS == SwTOXBase::GetType() && IsFromObjectNames()))
886         UpdateContent( SwTOXElement::Graphic, pOwnChapterNode, pLayout );
887 
888     if( !GetSequenceName().isEmpty() && !IsFromObjectNames() &&
889         (TOX_TABLES == SwTOXBase::GetType() ||
890          TOX_ILLUSTRATIONS == SwTOXBase::GetType() ) )
891         UpdateSequence( pOwnChapterNode, pLayout );
892 
893     if( GetCreateType() & SwTOXElement::Frame )
894         UpdateContent( SwTOXElement::Frame, pOwnChapterNode, pLayout );
895 
896     if(TOX_AUTHORITIES == SwTOXBase::GetType())
897         UpdateAuthorities( aIntl, pLayout );
898 
899     // Insert AlphaDelimiters if needed (just for keywords)
900     if( TOX_INDEX == SwTOXBase::GetType() &&
901         ( GetOptions() & SwTOIOptions::AlphaDelimiter ) )
902         InsertAlphaDelimiter( aIntl );
903 
904     // remove old content an insert one empty textnode (to hold the layout!)
905     SwTextNode* pFirstEmptyNd;
906 
907     SwUndoUpdateIndex * pUndo(nullptr);
908     {
909         rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSectNd, true, RedlineType::Any );
910 
911         SwNodeIndex aSttIdx( *pSectNd, +1 );
912         SwNodeIndex aEndIdx( *pSectNd->EndOfSectionNode() );
913         pFirstEmptyNd = rDoc.GetNodes().MakeTextNode( aEndIdx,
914                         rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
915 
916         {
917             // Task 70995 - save and restore PageDesc and Break Attributes
918             SwNodeIndex aNxtIdx( aSttIdx );
919             const SwContentNode* pCNd = aNxtIdx.GetNode().GetContentNode();
920             if( !pCNd )
921                 pCNd = rDoc.GetNodes().GoNext( &aNxtIdx );
922             assert(pCNd != pFirstEmptyNd);
923             assert(pCNd->GetIndex() < pFirstEmptyNd->GetIndex());
924             if( pCNd->HasSwAttrSet() )
925             {
926                 SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange );
927                 aBrkSet.Put( *pCNd->GetpSwAttrSet() );
928                 if( aBrkSet.Count() )
929                     pFirstEmptyNd->SetAttr( aBrkSet );
930             }
931         }
932 
933         if (rDoc.GetIDocumentUndoRedo().DoesUndo())
934         {
935             // note: this will first append a SwUndoDelSection from the ctor...
936             pUndo = new SwUndoUpdateIndex(*this);
937             // tdf#123313 insert Undo *after* all CrossRefBookmark Undos have
938             // been inserted by the Update*() functions
939             rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndoUpdateIndex>(pUndo));
940         }
941         else
942         {
943             --aEndIdx;
944             SwPosition aPos( aEndIdx, SwIndex( pFirstEmptyNd, 0 ));
945             SwDoc::CorrAbs( aSttIdx, aEndIdx, aPos, true );
946 
947             // delete flys in whole range including start node which requires
948             // giving the node before start node as Mark parameter, hence -1.
949             // (flys must be deleted because the anchor nodes are removed)
950             DelFlyInRange( SwNodeIndex(aSttIdx, -1), aEndIdx );
951 
952             rDoc.GetNodes().Delete( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() );
953         }
954     }
955 
956     // insert title of TOX
957     if ( !GetTitle().isEmpty() )
958     {
959         // then insert the headline section
960         SwNodeIndex aIdx( *pSectNd, +1 );
961 
962         SwTextNode* pHeadNd = rDoc.GetNodes().MakeTextNode( aIdx,
963                                 GetTextFormatColl( FORM_TITLE ) );
964         pHeadNd->InsertText( GetTitle(), SwIndex( pHeadNd ) );
965 
966         SwSectionData headerData( SectionType::ToxHeader, GetTOXName()+"_Head" );
967 
968         SwNodeIndex aStt( *pHeadNd ); --aIdx;
969         SwSectionFormat* pSectFormat = rDoc.MakeSectionFormat();
970         rDoc.GetNodes().InsertTextSection(
971                 aStt, *pSectFormat, headerData, nullptr, &aIdx, true, false);
972 
973         if (pUndo)
974         {
975             pUndo->TitleSectionInserted(*pSectFormat);
976         }
977     }
978 
979     // Sort the List of all TOC Marks and TOC Sections
980     std::vector<SwTextFormatColl*> aCollArr( GetTOXForm().GetFormMax(), nullptr );
981     std::unordered_map<OUString, int> markURLs;
982     SwNodeIndex aInsPos( *pFirstEmptyNd, 1 );
983     for( size_t nCnt = 0; nCnt < m_aSortArr.size(); ++nCnt )
984     {
985         ::SetProgressState( 0, rDoc.GetDocShell() );
986 
987         // Put the Text into the TOC
988         sal_uInt16 nLvl = m_aSortArr[ nCnt ]->GetLevel();
989         SwTextFormatColl* pColl = aCollArr[ nLvl ];
990         if( !pColl )
991         {
992             pColl = GetTextFormatColl( nLvl );
993             aCollArr[ nLvl ] = pColl;
994         }
995 
996         // Generate: Set dynamic TabStops
997         SwTextNode* pTOXNd = rDoc.GetNodes().MakeTextNode( aInsPos , pColl );
998         m_aSortArr[ nCnt ]->pTOXNd = pTOXNd;
999 
1000         // Generate: Evaluate Form and insert the place holder for the
1001         // page number. If it is a TOX_INDEX and the SwForm IsCommaSeparated()
1002         // then a range of entries must be generated into one paragraph
1003         size_t nRange = 1;
1004         if(TOX_INDEX == SwTOXBase::GetType() &&
1005                 GetTOXForm().IsCommaSeparated() &&
1006                 m_aSortArr[nCnt]->GetType() == TOX_SORT_INDEX)
1007         {
1008             const SwTOXMark& rMark = m_aSortArr[nCnt]->pTextMark->GetTOXMark();
1009             const OUString& sPrimKey = rMark.GetPrimaryKey();
1010             const OUString& sSecKey = rMark.GetSecondaryKey();
1011             const SwTOXMark* pNextMark = nullptr;
1012             while(m_aSortArr.size() > (nCnt + nRange) &&
1013                     m_aSortArr[nCnt + nRange]->GetType() == TOX_SORT_INDEX )
1014             {
1015                 pNextMark = &(m_aSortArr[nCnt + nRange]->pTextMark->GetTOXMark());
1016                 if( !pNextMark ||
1017                     pNextMark->GetPrimaryKey() != sPrimKey ||
1018                     pNextMark->GetSecondaryKey() != sSecKey)
1019                     break;
1020                 nRange++;
1021             }
1022         }
1023         // pass node index of table-of-content section and default page description
1024         // to method <GenerateText(..)>.
1025         ::SetProgressState( 0, rDoc.GetDocShell() );
1026 
1027         std::shared_ptr<sw::ToxTabStopTokenHandler> tabStopTokenHandler =
1028                 std::make_shared<sw::DefaultToxTabStopTokenHandler>(
1029                         pSectNd->GetIndex(), *pDefaultPageDesc, GetTOXForm().IsRelTabPos(),
1030                         rDoc.GetDocumentSettingManager().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) ?
1031                                 sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_INDENT :
1032                                 sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_PAGE);
1033         sw::ToxTextGenerator ttgn(GetTOXForm(), tabStopTokenHandler);
1034         ttgn.GenerateText(GetFormat()->GetDoc(), markURLs, m_aSortArr, nCnt, nRange, pLayout);
1035         nCnt += nRange - 1;
1036     }
1037 
1038     // delete the first dummy node and remove all Cursor into the previous node
1039     aInsPos = *pFirstEmptyNd;
1040     {
1041         SwPaM aCorPam( *pFirstEmptyNd );
1042         aCorPam.GetPoint()->nContent.Assign( pFirstEmptyNd, 0 );
1043         if( !aCorPam.Move( fnMoveForward ) )
1044             aCorPam.Move( fnMoveBackward );
1045         SwNodeIndex aEndIdx( aInsPos, 1 );
1046         SwDoc::CorrAbs( aInsPos, aEndIdx, *aCorPam.GetPoint(), true );
1047 
1048         // Task 70995 - save and restore PageDesc and Break Attributes
1049         if( pFirstEmptyNd->HasSwAttrSet() )
1050         {
1051             if( !GetTitle().isEmpty() )
1052                 aEndIdx = *pSectNd;
1053             else
1054                 aEndIdx = *pFirstEmptyNd;
1055             SwContentNode* pCNd = rDoc.GetNodes().GoNext( &aEndIdx );
1056             if( pCNd ) // Robust against defect documents, e.g. i60336
1057                 pCNd->SetAttr( *pFirstEmptyNd->GetpSwAttrSet() );
1058         }
1059     }
1060 
1061     // now create the new Frames
1062     SwNodeOffset nIdx = pSectNd->GetIndex();
1063     // don't delete if index is empty
1064     if(nIdx + SwNodeOffset(2) < pSectNd->EndOfSectionIndex())
1065         rDoc.GetNodes().Delete( aInsPos );
1066 
1067     aN2L.RestoreUpperFrames( rDoc.GetNodes(), nIdx, nIdx + 1 );
1068     o3tl::sorted_vector<SwRootFrame*> aAllLayouts = rDoc.GetAllLayouts();
1069     for ( const auto& rpLayout : aAllLayouts )
1070     {
1071         SwFrame::CheckPageDescs( static_cast<SwPageFrame*>(rpLayout->Lower()) );
1072     }
1073 
1074     SetProtect( SwTOXBase::IsProtected() );
1075 }
1076 
1077 void SwTOXBaseSection::InsertAlphaDelimiter( const SwTOXInternational& rIntl )
1078 {
1079     SwDoc* pDoc = GetFormat()->GetDoc();
1080     OUString sLastDeli;
1081     size_t i = 0;
1082     while( i < m_aSortArr.size() )
1083     {
1084         ::SetProgressState( 0, pDoc->GetDocShell() );
1085 
1086         sal_uInt16 nLevel = m_aSortArr[i]->GetLevel();
1087 
1088         // Skip AlphaDelimiter
1089         if( nLevel == FORM_ALPHA_DELIMITER )
1090             continue;
1091 
1092         const OUString sDeli = rIntl.GetIndexKey( m_aSortArr[i]->GetText(),
1093                                    m_aSortArr[i]->GetLocale() );
1094 
1095         // Do we already have a Delimiter?
1096         if( !sDeli.isEmpty() && sLastDeli != sDeli )
1097         {
1098             // We skip all that are less than a small Blank (these are special characters)
1099             if( ' ' <= sDeli[0] )
1100             {
1101                 std::unique_ptr<SwTOXCustom> pCst(
1102                         MakeSwTOXSortTabBase<SwTOXCustom>(nullptr,
1103                                 TextAndReading(sDeli, OUString()),
1104                                                      FORM_ALPHA_DELIMITER,
1105                                                      rIntl, m_aSortArr[i]->GetLocale() ));
1106                 m_aSortArr.insert( m_aSortArr.begin() + i, std::move(pCst));
1107                 i++;
1108             }
1109             sLastDeli = sDeli;
1110         }
1111 
1112         // Skip until we get to the same or a lower Level
1113         do {
1114             i++;
1115         } while (i < m_aSortArr.size() && m_aSortArr[i]->GetLevel() > nLevel);
1116     }
1117 }
1118 
1119 /// Evaluate Template
1120 SwTextFormatColl* SwTOXBaseSection::GetTextFormatColl( sal_uInt16 nLevel )
1121 {
1122     SwDoc* pDoc = GetFormat()->GetDoc();
1123     const OUString& rName = GetTOXForm().GetTemplate( nLevel );
1124     SwTextFormatColl* pColl = !rName.isEmpty() ? pDoc->FindTextFormatCollByName(rName) :nullptr;
1125     if( !pColl )
1126     {
1127         sal_uInt16 nPoolFormat = 0;
1128         const TOXTypes eMyType = SwTOXBase::GetType();
1129         switch( eMyType )
1130         {
1131         case TOX_INDEX:         nPoolFormat = RES_POOLCOLL_TOX_IDXH;       break;
1132         case TOX_USER:
1133             if( nLevel < 6 )
1134                 nPoolFormat = RES_POOLCOLL_TOX_USERH;
1135             else
1136                 nPoolFormat = RES_POOLCOLL_TOX_USER6 - 6;
1137             break;
1138         case TOX_ILLUSTRATIONS: nPoolFormat = RES_POOLCOLL_TOX_ILLUSH;     break;
1139         case TOX_OBJECTS:       nPoolFormat = RES_POOLCOLL_TOX_OBJECTH;    break;
1140         case TOX_TABLES:        nPoolFormat = RES_POOLCOLL_TOX_TABLESH;    break;
1141         case TOX_AUTHORITIES:
1142         case TOX_BIBLIOGRAPHY:
1143             nPoolFormat = RES_POOLCOLL_TOX_AUTHORITIESH; break;
1144         case  TOX_CITATION: /** TODO */break;
1145         case TOX_CONTENT:
1146             // There's a jump in the ContentArea!
1147             if( nLevel < 6 )
1148                 nPoolFormat = RES_POOLCOLL_TOX_CNTNTH;
1149             else
1150                 nPoolFormat = RES_POOLCOLL_TOX_CNTNT6 - 6;
1151             break;
1152         }
1153 
1154         if(eMyType == TOX_AUTHORITIES && nLevel)
1155             nPoolFormat = nPoolFormat + 1;
1156         else if(eMyType == TOX_INDEX && nLevel)
1157         {
1158             // pool: Level 1,2,3, Delimiter
1159             // SwForm: Delimiter, Level 1,2,3
1160             nPoolFormat += 1 == nLevel ? nLevel + 3 : nLevel - 1;
1161         }
1162         else
1163             nPoolFormat = nPoolFormat + nLevel;
1164         pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolFormat );
1165     }
1166     return pColl;
1167 }
1168 
1169 void SwTOXBaseSection::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
1170 {
1171     if (auto pFindHint = dynamic_cast<const sw::FindContentFrameHint*>(&rHint))
1172     {
1173         if(pFindHint->m_rpContentFrame)
1174             return;
1175         auto pSectFormat = GetFormat();
1176         if(!pSectFormat)
1177             return;
1178         const SwSectionNode* pSectNd = pSectFormat->GetSectionNode();
1179         if(!pSectNd)
1180             return;
1181         SwNodeIndex aIdx(*pSectNd, 1);
1182         SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
1183         if(!pCNd)
1184             pCNd = pFindHint->m_rDoc.GetNodes().GoNext(&aIdx);
1185         if(!pCNd)
1186             return;
1187         if(pCNd->EndOfSectionIndex() >= pSectNd->EndOfSectionIndex())
1188             return;
1189         pFindHint->m_rpContentFrame = pCNd->getLayoutFrame(&pFindHint->m_rLayout);
1190     } else
1191         SwTOXBase::SwClientNotify(rModify, rHint);
1192 }
1193 
1194 /// Create from Marks
1195 void SwTOXBaseSection::UpdateMarks(const SwTOXInternational& rIntl,
1196         const SwTextNode* pOwnChapterNode,
1197         SwRootFrame const*const pLayout)
1198 {
1199     const auto pType = static_cast<SwTOXType*>(SwTOXBase::GetRegisteredIn());
1200     auto pShell = GetFormat()->GetDoc()->GetDocShell();
1201     const TOXTypes eTOXTyp = GetTOXType()->GetType();
1202     std::vector<std::reference_wrapper<SwTextTOXMark>> vMarks;
1203     pType->CollectTextTOXMarksForLayout(vMarks, pLayout);
1204     for(auto& rMark: vMarks)
1205     {
1206         ::SetProgressState(0, pShell);
1207         auto& rNode = rMark.get().GetTextNode();
1208         if(IsFromChapter() && ::lcl_FindChapterNode(rNode, pLayout) != pOwnChapterNode)
1209             continue;
1210         auto rTOXMark = rMark.get().GetTOXMark();
1211         if(TOX_INDEX == eTOXTyp)
1212         {
1213             // index entry mark
1214             assert(g_pBreakIt);
1215             lang::Locale aLocale = g_pBreakIt->GetLocale(rNode.GetLang(rMark.get().GetStart()));
1216             InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_ENTRY, rIntl, aLocale));
1217             if(GetOptions() & SwTOIOptions::KeyAsEntry && !rTOXMark.GetPrimaryKey().isEmpty())
1218             {
1219                 InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_PRIMARY_KEY, rIntl, aLocale));
1220                 if (!rTOXMark.GetSecondaryKey().isEmpty())
1221                 {
1222                     InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_SECONDARY_KEY, rIntl, aLocale));
1223                 }
1224             }
1225         }
1226         else if(TOX_USER == eTOXTyp || rTOXMark.GetLevel() <= GetLevel())
1227         {   // table of content mark, also used for user marks
1228             InsertSorted(MakeSwTOXSortTabBase<SwTOXContent>(pLayout, rNode, &rMark.get(), rIntl));
1229         }
1230     }
1231 }
1232 
1233 /// Generate table of contents from outline
1234 void SwTOXBaseSection::UpdateOutline( const SwTextNode* pOwnChapterNode,
1235         SwRootFrame const*const pLayout)
1236 {
1237     SwDoc* pDoc = GetFormat()->GetDoc();
1238     SwNodes& rNds = pDoc->GetNodes();
1239 
1240     const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
1241     for( auto pOutlineNode : rOutlNds )
1242     {
1243         ::SetProgressState( 0, pDoc->GetDocShell() );
1244         SwTextNode* pTextNd = pOutlineNode->GetTextNode();
1245         if( pTextNd && pTextNd->Len() && pTextNd->HasWriterListeners() &&
1246             o3tl::make_unsigned( pTextNd->GetAttrOutlineLevel()) <= GetLevel() &&
1247             pTextNd->getLayoutFrame(pLayout) &&
1248            !pTextNd->IsHiddenByParaField() &&
1249            !pTextNd->HasHiddenCharAttribute( true ) &&
1250            (!pLayout || !pLayout->HasMergedParas()
1251                 || static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) &&
1252             ( !IsFromChapter() ||
1253                ::lcl_FindChapterNode(*pTextNd, pLayout) == pOwnChapterNode ))
1254         {
1255             InsertSorted(MakeSwTOXSortTabBase<SwTOXPara>(pLayout, *pTextNd, SwTOXElement::OutlineLevel));
1256         }
1257     }
1258 }
1259 
1260 /// Generate table of contents from template areas
1261 void SwTOXBaseSection::UpdateTemplate(const SwTextNode* pOwnChapterNode,
1262         SwRootFrame const*const pLayout)
1263 {
1264     SwDoc* pDoc = GetFormat()->GetDoc();
1265     for(sal_uInt16 i = 0; i < MAXLEVEL; i++)
1266     {
1267         const OUString sTmpStyleNames = GetStyleNames(i);
1268         if (sTmpStyleNames.isEmpty())
1269             continue;
1270 
1271         sal_Int32 nIndex = 0;
1272         while (nIndex >= 0)
1273         {
1274             SwTextFormatColl* pColl = pDoc->FindTextFormatCollByName(
1275                                     sTmpStyleNames.getToken( 0, TOX_STYLE_DELIMITER, nIndex ));
1276             //TODO: no outline Collections in content indexes if OutlineLevels are already included
1277             if( !pColl ||
1278                 ( TOX_CONTENT == SwTOXBase::GetType() &&
1279                   GetCreateType() & SwTOXElement::OutlineLevel &&
1280                     pColl->IsAssignedToListLevelOfOutlineStyle()) )
1281                 continue;
1282 
1283             SwIterator<SwTextNode,SwFormatColl> aIter( *pColl );
1284             for( SwTextNode* pTextNd = aIter.First(); pTextNd; pTextNd = aIter.Next() )
1285             {
1286                 ::SetProgressState( 0, pDoc->GetDocShell() );
1287 
1288                 if (pTextNd->GetText().getLength() &&
1289                     pTextNd->getLayoutFrame(pLayout) &&
1290                     pTextNd->GetNodes().IsDocNodes() &&
1291                     (!pLayout || !pLayout->HasMergedParas()
1292                         || static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) &&
1293                     (!IsFromChapter() || pOwnChapterNode ==
1294                         ::lcl_FindChapterNode(*pTextNd, pLayout)))
1295                 {
1296                     InsertSorted(MakeSwTOXSortTabBase<SwTOXPara>(pLayout, *pTextNd, SwTOXElement::Template, i + 1));
1297                 }
1298             }
1299         }
1300     }
1301 }
1302 
1303 /// Generate content from sequence fields
1304 void SwTOXBaseSection::UpdateSequence(const SwTextNode* pOwnChapterNode,
1305         SwRootFrame const*const pLayout)
1306 {
1307     SwDoc* pDoc = GetFormat()->GetDoc();
1308     SwFieldType* pSeqField = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, GetSequenceName(), false);
1309     if(!pSeqField)
1310         return;
1311 
1312     std::vector<SwFormatField*> vFields;
1313     pSeqField->GatherFields(vFields);
1314     for(auto pFormatField: vFields)
1315     {
1316         const SwTextField* pTextField = pFormatField->GetTextField();
1317         SwTextNode& rTextNode = pTextField->GetTextNode();
1318         ::SetProgressState( 0, pDoc->GetDocShell() );
1319 
1320         if (rTextNode.GetText().getLength() &&
1321             rTextNode.getLayoutFrame(pLayout) &&
1322             ( !IsFromChapter() ||
1323                 ::lcl_FindChapterNode(rTextNode, pLayout) == pOwnChapterNode)
1324             && (!pLayout || !pLayout->IsHideRedlines()
1325                 || !sw::IsFieldDeletedInModel(pDoc->getIDocumentRedlineAccess(), *pTextField)))
1326         {
1327             const SwSetExpField& rSeqField = dynamic_cast<const SwSetExpField&>(*(pFormatField->GetField()));
1328             const OUString sName = GetSequenceName()
1329                 + OUStringChar(cSequenceMarkSeparator)
1330                 + OUString::number( rSeqField.GetSeqNumber() );
1331             std::unique_ptr<SwTOXPara> pNew(new SwTOXPara( rTextNode, SwTOXElement::Sequence, 1, sName ));
1332             // set indexes if the number or the reference text are to be displayed
1333             if( GetCaptionDisplay() == CAPTION_TEXT )
1334             {
1335                 pNew->SetStartIndex(
1336                     SwGetExpField::GetReferenceTextPos( *pFormatField, *pDoc ));
1337             }
1338             else if(GetCaptionDisplay() == CAPTION_NUMBER)
1339             {
1340                 pNew->SetEndIndex(pTextField->GetStart() + 1);
1341             }
1342             pNew->InitText(pLayout);
1343             InsertSorted(std::move(pNew));
1344         }
1345     }
1346 }
1347 
1348 void SwTOXBaseSection::UpdateAuthorities(const SwTOXInternational& rIntl,
1349         SwRootFrame const*const pLayout)
1350 {
1351     SwDoc* pDoc = GetFormat()->GetDoc();
1352     SwFieldType* pAuthField = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::TableOfAuthorities, OUString(), false);
1353     if(!pAuthField)
1354         return;
1355 
1356     std::vector<SwFormatField*> vFields;
1357     pAuthField->GatherFields(vFields);
1358     for(auto pFormatField: vFields)
1359     {
1360         const auto pTextField = pFormatField->GetTextField();
1361         const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode();
1362         ::SetProgressState( 0, pDoc->GetDocShell() );
1363 
1364         if (rTextNode.GetText().getLength() &&
1365             rTextNode.getLayoutFrame(pLayout) &&
1366             (!pLayout || !pLayout->IsHideRedlines()
1367                 || !sw::IsFieldDeletedInModel(pDoc->getIDocumentRedlineAccess(), *pTextField)))
1368         {
1369             //#106485# the body node has to be used!
1370             SwContentFrame *const pFrame = rTextNode.getLayoutFrame(pLayout);
1371             SwPosition aFieldPos(rTextNode);
1372             const SwTextNode* pTextNode = nullptr;
1373             if(pFrame && !pFrame->IsInDocBody())
1374                 pTextNode = GetBodyTextNode( *pDoc, aFieldPos, *pFrame );
1375             if(!pTextNode)
1376                 pTextNode = &rTextNode;
1377 
1378             InsertSorted(MakeSwTOXSortTabBase<SwTOXAuthority>(pLayout, *pTextNode, *pFormatField, rIntl));
1379         }
1380     }
1381 }
1382 
1383 static SwTOOElements lcl_IsSOObject( const SvGlobalName& rFactoryNm )
1384 {
1385     static const struct SoObjType {
1386         SwTOOElements nFlag;
1387         // GlobalNameId
1388         struct {
1389             sal_uInt32 n1;
1390             sal_uInt16 n2, n3;
1391             sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
1392         } aGlNmIds[4];
1393     } aArr[] = {
1394         { SwTOOElements::Math,
1395           { {SO3_SM_CLASSID_60},{SO3_SM_CLASSID_50},
1396             {SO3_SM_CLASSID_40},{SO3_SM_CLASSID_30} } },
1397         { SwTOOElements::Chart,
1398           { {SO3_SCH_CLASSID_60},{SO3_SCH_CLASSID_50},
1399             {SO3_SCH_CLASSID_40},{SO3_SCH_CLASSID_30} } },
1400         { SwTOOElements::Calc,
1401           { {SO3_SC_CLASSID_60},{SO3_SC_CLASSID_50},
1402             {SO3_SC_CLASSID_40},{SO3_SC_CLASSID_30} } },
1403         { SwTOOElements::DrawImpress,
1404           { {SO3_SIMPRESS_CLASSID_60},{SO3_SIMPRESS_CLASSID_50},
1405             {SO3_SIMPRESS_CLASSID_40},{SO3_SIMPRESS_CLASSID_30} } },
1406         { SwTOOElements::DrawImpress,
1407           { {SO3_SDRAW_CLASSID_60},{SO3_SDRAW_CLASSID_50} } }
1408     };
1409 
1410     for( SoObjType const & rArr : aArr )
1411         for (auto & rId : rArr.aGlNmIds)
1412         {
1413             if( !rId.n1 )
1414                 break;
1415             SvGlobalName aGlbNm( rId.n1, rId.n2, rId.n3,
1416                         rId.b8, rId.b9, rId.b10, rId.b11,
1417                         rId.b12, rId.b13, rId.b14, rId.b15 );
1418             if( rFactoryNm == aGlbNm )
1419             {
1420                 return rArr.nFlag;
1421             }
1422         }
1423 
1424     return SwTOOElements::NONE;
1425 }
1426 
1427 void SwTOXBaseSection::UpdateContent( SwTOXElement eMyType,
1428         const SwTextNode* pOwnChapterNode,
1429         SwRootFrame const*const pLayout)
1430 {
1431     SwDoc* pDoc = GetFormat()->GetDoc();
1432     SwNodes& rNds = pDoc->GetNodes();
1433     // on the 1st Node of the 1st Section
1434     SwNodeOffset nIdx = rNds.GetEndOfAutotext().StartOfSectionIndex() + SwNodeOffset(2),
1435          nEndIdx = rNds.GetEndOfAutotext().GetIndex();
1436 
1437     while( nIdx < nEndIdx )
1438     {
1439         ::SetProgressState( 0, pDoc->GetDocShell() );
1440 
1441         SwNode* pNd = rNds[ nIdx ];
1442         SwContentNode* pCNd = nullptr;
1443         switch( eMyType )
1444         {
1445         case SwTOXElement::Frame:
1446             if( !pNd->IsNoTextNode() )
1447             {
1448                 pCNd = pNd->GetContentNode();
1449                 if( !pCNd )
1450                 {
1451                     SwNodeIndex aTmp( *pNd );
1452                     pCNd = rNds.GoNext( &aTmp );
1453                 }
1454             }
1455             break;
1456         case SwTOXElement::Graphic:
1457             if( pNd->IsGrfNode() )
1458                 pCNd = static_cast<SwContentNode*>(pNd);
1459             break;
1460         case SwTOXElement::Ole:
1461             if( pNd->IsOLENode() )
1462             {
1463                 bool bInclude = true;
1464                 if(TOX_OBJECTS == SwTOXBase::GetType())
1465                 {
1466                     SwOLENode* pOLENode = pNd->GetOLENode();
1467                     SwTOOElements nMyOLEOptions = GetOLEOptions();
1468                     SwOLEObj& rOLEObj = pOLENode->GetOLEObj();
1469 
1470                     if( rOLEObj.IsOleRef() )    // Not yet loaded
1471                     {
1472                         SvGlobalName aTmpName( rOLEObj.GetOleRef()->getClassID() );
1473                         SwTOOElements nObj = ::lcl_IsSOObject( aTmpName );
1474                         bInclude = ( (nMyOLEOptions & SwTOOElements::Other) && SwTOOElements::NONE == nObj )
1475                                    || (nMyOLEOptions & nObj);
1476                     }
1477                     else
1478                     {
1479                         OSL_FAIL("OLE Object no loaded?");
1480                         bInclude = false;
1481                     }
1482                 }
1483 
1484                 if(bInclude)
1485                     pCNd = static_cast<SwContentNode*>(pNd);
1486             }
1487             break;
1488         default: break;
1489         }
1490 
1491         if( pCNd )
1492         {
1493             // find node in body text
1494             int nSetLevel = USHRT_MAX;
1495 
1496             //#111105# tables of tables|illustrations|objects don't support hierarchies
1497             if( IsLevelFromChapter() &&
1498                     TOX_TABLES != SwTOXBase::GetType() &&
1499                     TOX_ILLUSTRATIONS != SwTOXBase::GetType() &&
1500                     TOX_OBJECTS != SwTOXBase::GetType() )
1501             {
1502                 const SwTextNode* pOutlNd = ::lcl_FindChapterNode( *pCNd,
1503                                                 pLayout, MAXLEVEL - 1);
1504                 if( pOutlNd )
1505                 {
1506                     if( pOutlNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle())
1507                     {
1508                         nSetLevel = pOutlNd->GetTextColl()->GetAttrOutlineLevel();
1509                     }
1510                 }
1511             }
1512 
1513             if (pCNd->getLayoutFrame(pLayout)
1514                 && (!pLayout || !pLayout->HasMergedParas()
1515                     || pCNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
1516                 && ( !IsFromChapter() ||
1517                     ::lcl_FindChapterNode(*pCNd, pLayout) == pOwnChapterNode ))
1518             {
1519                 std::unique_ptr<SwTOXPara> pNew( MakeSwTOXSortTabBase<SwTOXPara>(
1520                         pLayout, *pCNd, eMyType,
1521                             ( USHRT_MAX != nSetLevel )
1522                             ? o3tl::narrowing<sal_uInt16>(nSetLevel)
1523                             : FORM_ALPHA_DELIMITER ) );
1524                 InsertSorted( std::move(pNew) );
1525             }
1526         }
1527 
1528         nIdx = pNd->StartOfSectionNode()->EndOfSectionIndex() + SwNodeOffset(2);  // 2 == End/Start Node
1529     }
1530 }
1531 
1532 /// Collect table entries
1533 void SwTOXBaseSection::UpdateTable(const SwTextNode* pOwnChapterNode,
1534         SwRootFrame const*const pLayout)
1535 {
1536     SwDoc* pDoc = GetFormat()->GetDoc();
1537     SwNodes& rNds = pDoc->GetNodes();
1538     const SwFrameFormats& rArr = *pDoc->GetTableFrameFormats();
1539 
1540     for( auto pFrameFormat : rArr )
1541     {
1542         ::SetProgressState( 0, pDoc->GetDocShell() );
1543 
1544         SwTable* pTmpTable = SwTable::FindTable( pFrameFormat );
1545         SwTableBox* pFBox;
1546         if( pTmpTable && nullptr != (pFBox = pTmpTable->GetTabSortBoxes()[0] ) &&
1547             pFBox->GetSttNd() && pFBox->GetSttNd()->GetNodes().IsDocNodes() )
1548         {
1549             const SwTableNode* pTableNd = pFBox->GetSttNd()->FindTableNode();
1550             SwNodeIndex aContentIdx( *pTableNd, 1 );
1551 
1552             SwContentNode* pCNd;
1553             while( nullptr != ( pCNd = rNds.GoNext( &aContentIdx ) ) &&
1554                 aContentIdx.GetIndex() < pTableNd->EndOfSectionIndex() )
1555             {
1556                 if (pCNd->getLayoutFrame(pLayout)
1557                     && (!pLayout || !pLayout->HasMergedParas()
1558                         || pCNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
1559                     && (!IsFromChapter()
1560                         || ::lcl_FindChapterNode(*pCNd, pLayout) == pOwnChapterNode))
1561                 {
1562                     std::unique_ptr<SwTOXTable> pNew(new SwTOXTable( *pCNd ));
1563                     if( IsLevelFromChapter() && TOX_TABLES != SwTOXBase::GetType())
1564                     {
1565                         const SwTextNode* pOutlNd =
1566                             ::lcl_FindChapterNode(*pCNd, pLayout, MAXLEVEL - 1);
1567                         if( pOutlNd )
1568                         {
1569                             if( pOutlNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle())
1570                             {
1571                                 const int nTmp = pOutlNd->GetTextColl()->GetAttrOutlineLevel();
1572                                 pNew->SetLevel(o3tl::narrowing<sal_uInt16>(nTmp));
1573                             }
1574                         }
1575                     }
1576                     pNew->InitText(pLayout);
1577                     InsertSorted(std::move(pNew));
1578                     break;
1579                 }
1580             }
1581         }
1582     }
1583 }
1584 
1585 /// Calculate PageNumber and insert after formatting
1586 void SwTOXBaseSection::UpdatePageNum()
1587 {
1588     if( m_aSortArr.empty() )
1589         return ;
1590 
1591     // Insert the current PageNumber into the TOC
1592     SwPageFrame*  pCurrentPage    = nullptr;
1593     sal_uInt16      nPage       = 0;
1594     SwDoc* pDoc = GetFormat()->GetDoc();
1595 
1596     SwTOXInternational aIntl( GetLanguage(),
1597                               TOX_INDEX == GetTOXType()->GetType() ?
1598                               GetOptions() : SwTOIOptions::NONE,
1599                               GetSortAlgorithm() );
1600 
1601     for( size_t nCnt = 0; nCnt < m_aSortArr.size(); ++nCnt )
1602     {
1603         // Loop over all SourceNodes
1604 
1605         // process run in lines
1606         size_t nRange = 0;
1607         if(GetTOXForm().IsCommaSeparated() &&
1608                 m_aSortArr[nCnt]->GetType() == TOX_SORT_INDEX)
1609         {
1610             const SwTOXMark& rMark = m_aSortArr[nCnt]->pTextMark->GetTOXMark();
1611             const OUString& sPrimKey = rMark.GetPrimaryKey();
1612             const OUString& sSecKey = rMark.GetSecondaryKey();
1613             const SwTOXMark* pNextMark = nullptr;
1614             while(m_aSortArr.size() > (nCnt + nRange)&&
1615                     m_aSortArr[nCnt + nRange]->GetType() == TOX_SORT_INDEX &&
1616                     nullptr != (pNextMark = &(m_aSortArr[nCnt + nRange]->pTextMark->GetTOXMark())) &&
1617                     pNextMark->GetPrimaryKey() == sPrimKey &&
1618                     pNextMark->GetSecondaryKey() == sSecKey)
1619                 nRange++;
1620         }
1621         else
1622             nRange = 1;
1623 
1624         for(size_t nRunInEntry = nCnt; nRunInEntry < nCnt + nRange; ++nRunInEntry)
1625         {
1626             std::vector<sal_uInt16> aNums; // the PageNumber
1627             std::vector<SwPageDesc*> aDescs; // The PageDescriptors matching the PageNumbers
1628             std::vector<sal_uInt16> aMainNums; // contains page numbers of main entries
1629             SwTOXSortTabBase* pSortBase = m_aSortArr[nRunInEntry].get();
1630             size_t nSize = pSortBase->aTOXSources.size();
1631             for (size_t j = 0; j < nSize; ++j)
1632             {
1633                 ::SetProgressState( 0, pDoc->GetDocShell() );
1634 
1635                 SwTOXSource& rTOXSource = pSortBase->aTOXSources[j];
1636                 if( rTOXSource.pNd )
1637                 {
1638                     SwContentFrame* pFrame = rTOXSource.pNd->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() );
1639                     OSL_ENSURE( pFrame || pDoc->IsUpdateTOX(), "TOX, no Frame found");
1640                     if( !pFrame )
1641                         continue;
1642                     if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->HasFollow() )
1643                     {
1644                         // find the right one
1645                         SwTextFrame* pNext;
1646                         TextFrameIndex const nPos(static_cast<SwTextFrame*>(pFrame)
1647                             ->MapModelToView(static_cast<SwTextNode const*>(rTOXSource.pNd),
1648                                 rTOXSource.nPos));
1649                         for (;;)
1650                         {
1651                             pNext = static_cast<SwTextFrame*>(pFrame->GetFollow());
1652                             if (!pNext || nPos < pNext->GetOffset())
1653                                 break;
1654                             pFrame = pNext;
1655                         }
1656                     }
1657 
1658                     SwPageFrame*  pTmpPage = pFrame->FindPageFrame();
1659                     if( pTmpPage != pCurrentPage )
1660                     {
1661                         nPage       = pTmpPage->GetVirtPageNum();
1662                         pCurrentPage    = pTmpPage;
1663                     }
1664 
1665                     // Insert as sorted
1666                     std::vector<sal_uInt16>::size_type i;
1667                     for( i = 0; i < aNums.size() && aNums[i] < nPage; ++i )
1668                         ;
1669 
1670                     if( i >= aNums.size() || aNums[ i ] != nPage )
1671                     {
1672                         aNums.insert(aNums.begin() + i, nPage);
1673                         aDescs.insert(aDescs.begin() + i, pCurrentPage->GetPageDesc() );
1674                     }
1675                     // is it a main entry?
1676                     if(TOX_SORT_INDEX == pSortBase->GetType() &&
1677                         rTOXSource.bMainEntry)
1678                     {
1679                         aMainNums.push_back(nPage);
1680                     }
1681                 }
1682             }
1683             // Insert the PageNumber into the TOC TextNode
1684             const SwTOXSortTabBase* pBase = m_aSortArr[ nCnt ].get();
1685             if(pBase->pTOXNd)
1686             {
1687                 const SwTextNode* pTextNd = pBase->pTOXNd->GetTextNode();
1688                 OSL_ENSURE( pTextNd, "no TextNode, wrong TOC" );
1689 
1690                 UpdatePageNum_( const_cast<SwTextNode*>(pTextNd), aNums, aDescs, &aMainNums,
1691                                 aIntl );
1692             }
1693         }
1694     }
1695     // Delete the mapping array after setting the right PageNumber
1696     m_aSortArr.clear();
1697 }
1698 
1699 /// Replace the PageNumber place holders. Search for the page no. in the array
1700 /// of main entry page numbers.
1701 static bool lcl_HasMainEntry( const std::vector<sal_uInt16>* pMainEntryNums, sal_uInt16 nToFind )
1702 {
1703     if (!pMainEntryNums)
1704         return false;
1705 
1706     for( auto nMainEntry : *pMainEntryNums )
1707         if (nToFind == nMainEntry)
1708             return true;
1709     return false;
1710 }
1711 
1712 void SwTOXBaseSection::UpdatePageNum_( SwTextNode* pNd,
1713                                     const std::vector<sal_uInt16>& rNums,
1714                                     const std::vector<SwPageDesc*>& rDescs,
1715                                     const std::vector<sal_uInt16>* pMainEntryNums,
1716                                     const SwTOXInternational& rIntl )
1717 {
1718     // collect starts end ends of main entry character style
1719     std::optional< std::vector<sal_uInt16> > xCharStyleIdx;
1720     if (pMainEntryNums)
1721         xCharStyleIdx.emplace();
1722 
1723     OUString sSrchStr
1724         = OUStringChar(C_NUM_REPL) + SwTOXMark::S_PAGE_DELI + OUStringChar(C_NUM_REPL);
1725     sal_Int32 nStartPos = pNd->GetText().indexOf(sSrchStr);
1726     sSrchStr = OUStringChar(C_NUM_REPL) + OUStringChar(C_END_PAGE_NUM);
1727     sal_Int32 nEndPos = pNd->GetText().indexOf(sSrchStr);
1728 
1729     if (-1 == nEndPos || rNums.empty())
1730         return;
1731 
1732     if (-1 == nStartPos || nStartPos > nEndPos)
1733         nStartPos = nEndPos;
1734 
1735     sal_uInt16 nOld = rNums[0],
1736            nBeg = nOld,
1737            nCount  = 0;
1738     OUString aNumStr( rDescs[0]->GetNumType().GetNumStr( nBeg ) );
1739     if( xCharStyleIdx && lcl_HasMainEntry( pMainEntryNums, nBeg ))
1740     {
1741         xCharStyleIdx->push_back( 0 );
1742     }
1743 
1744     // Delete place holder
1745     SwIndex aPos(pNd, nStartPos);
1746     SwCharFormat* pPageNoCharFormat = nullptr;
1747     SwpHints* pHints = pNd->GetpSwpHints();
1748     if(pHints)
1749         for(size_t nHintIdx = 0; nHintIdx < pHints->Count(); ++nHintIdx)
1750         {
1751             const SwTextAttr* pAttr = pHints->Get(nHintIdx);
1752             const sal_Int32 nTmpEnd = pAttr->End() ? *pAttr->End() : 0;
1753             if( nStartPos >= pAttr->GetStart() &&
1754                 (nStartPos + 2) <= nTmpEnd &&
1755                 pAttr->Which() == RES_TXTATR_CHARFMT)
1756             {
1757                 pPageNoCharFormat = pAttr->GetCharFormat().GetCharFormat();
1758                 break;
1759             }
1760         }
1761     pNd->EraseText(aPos, nEndPos - nStartPos + 2);
1762 
1763     std::vector<sal_uInt16>::size_type i;
1764     for( i = 1; i < rNums.size(); ++i)
1765     {
1766         SvxNumberType aType( rDescs[i]->GetNumType() );
1767         if( TOX_INDEX == SwTOXBase::GetType() )
1768         {   // Summarize for the following
1769             // Add up all following
1770             // break up if main entry starts or ends and
1771             // insert a char style index
1772             bool bMainEntryChanges = lcl_HasMainEntry(pMainEntryNums, nOld)
1773                     != lcl_HasMainEntry(pMainEntryNums, rNums[i]);
1774 
1775             if(nOld == rNums[i]-1 && !bMainEntryChanges &&
1776                 (GetOptions() & (SwTOIOptions::FF|SwTOIOptions::Dash)))
1777                 nCount++;
1778             else
1779             {
1780                 // Flush for the following old values
1781                 if(GetOptions() & SwTOIOptions::FF)
1782                 {
1783                     if ( nCount >= 1 )
1784                         aNumStr += rIntl.GetFollowingText( nCount > 1 );
1785                 }
1786                 else if (nCount) //#58127# If nCount == 0, then the only PageNumber is already in aNumStr!
1787                 {
1788                     if (nCount == 1 )
1789                         aNumStr += SwTOXMark::S_PAGE_DELI;
1790                     else
1791                         aNumStr += "-";
1792 
1793                     aNumStr += aType.GetNumStr( nBeg + nCount );
1794                 }
1795 
1796                 // Create new String
1797                 nBeg     = rNums[i];
1798                 aNumStr += SwTOXMark::S_PAGE_DELI;
1799                 //the change of the character style must apply after sPageDeli is appended
1800                 if (xCharStyleIdx && bMainEntryChanges)
1801                 {
1802                     xCharStyleIdx->push_back(aNumStr.getLength());
1803                 }
1804                 aNumStr += aType.GetNumStr( nBeg );
1805                 nCount   = 0;
1806             }
1807             nOld = rNums[i];
1808         }
1809         else
1810         {   // Insert all Numbers
1811             aNumStr += aType.GetNumStr( rNums[i] );
1812             if (i+1 != rNums.size())
1813                 aNumStr += SwTOXMark::S_PAGE_DELI;
1814         }
1815     }
1816     // Flush when ending and the following old values
1817     if( TOX_INDEX == SwTOXBase::GetType() )
1818     {
1819         if(GetOptions() & SwTOIOptions::FF)
1820         {
1821             if( nCount >= 1 )
1822                 aNumStr += rIntl.GetFollowingText( nCount > 1 );
1823         }
1824         else
1825         {
1826             if(nCount >= 2)
1827                 aNumStr += "-";
1828             else if(nCount == 1)
1829                 aNumStr += SwTOXMark::S_PAGE_DELI;
1830             //#58127# If nCount == 0, then the only PageNumber is already in aNumStr!
1831             if(nCount)
1832                 aNumStr += rDescs[i-1]->GetNumType().GetNumStr( nBeg+nCount );
1833         }
1834     }
1835     pNd->InsertText( aNumStr, aPos, SwInsertFlags::EMPTYEXPAND | SwInsertFlags::FORCEHINTEXPAND );
1836     if(pPageNoCharFormat)
1837     {
1838         SwFormatCharFormat aCharFormat( pPageNoCharFormat );
1839         pNd->InsertItem(aCharFormat, nStartPos, nStartPos + aNumStr.getLength(), SetAttrMode::DONTEXPAND);
1840     }
1841 
1842     // The main entries should get their character style
1843     if (!xCharStyleIdx || xCharStyleIdx->empty() || GetMainEntryCharStyle().isEmpty())
1844         return;
1845 
1846     // eventually the last index must me appended
1847     if (xCharStyleIdx->size()&0x01)
1848         xCharStyleIdx->push_back(aNumStr.getLength());
1849 
1850     // search by name
1851     SwDoc& rDoc = pNd->GetDoc();
1852     sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( GetMainEntryCharStyle(), SwGetPoolIdFromName::ChrFmt );
1853     SwCharFormat* pCharFormat = nullptr;
1854     if(USHRT_MAX != nPoolId)
1855         pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(nPoolId);
1856     else
1857         pCharFormat = rDoc.FindCharFormatByName( GetMainEntryCharStyle() );
1858     if(!pCharFormat)
1859         pCharFormat = rDoc.MakeCharFormat(GetMainEntryCharStyle(), nullptr);
1860 
1861     // find the page numbers in aNumStr and set the character style
1862     sal_Int32 nOffset = pNd->GetText().getLength() - aNumStr.getLength();
1863     SwFormatCharFormat aCharFormat(pCharFormat);
1864     for (size_t j = 0; j < xCharStyleIdx->size(); j += 2)
1865     {
1866         sal_Int32 nStartIdx = (*xCharStyleIdx)[j] + nOffset;
1867         sal_Int32 nEndIdx   = (*xCharStyleIdx)[j + 1]  + nOffset;
1868         pNd->InsertItem(aCharFormat, nStartIdx, nEndIdx, SetAttrMode::DONTEXPAND);
1869     }
1870 }
1871 
1872 void SwTOXBaseSection::InsertSorted(std::unique_ptr<SwTOXSortTabBase> pNew)
1873 {
1874     Range aRange(0, m_aSortArr.size());
1875     if( TOX_INDEX == SwTOXBase::GetType() && pNew->pTextMark )
1876     {
1877         const SwTOXMark& rMark = pNew->pTextMark->GetTOXMark();
1878         // Evaluate Key
1879         // Calculate the range where to insert
1880         if( !(GetOptions() & SwTOIOptions::KeyAsEntry) &&
1881             !rMark.GetPrimaryKey().isEmpty() )
1882         {
1883             aRange = GetKeyRange( rMark.GetPrimaryKey(),
1884                                   rMark.GetPrimaryKeyReading(),
1885                                   *pNew, FORM_PRIMARY_KEY, aRange );
1886 
1887             if( !rMark.GetSecondaryKey().isEmpty() )
1888                 aRange = GetKeyRange( rMark.GetSecondaryKey(),
1889                                       rMark.GetSecondaryKeyReading(),
1890                                       *pNew, FORM_SECONDARY_KEY, aRange );
1891         }
1892     }
1893     // Search for identical entries and remove the trailing one
1894     if(TOX_AUTHORITIES == SwTOXBase::GetType())
1895     {
1896         for(short i = static_cast<short>(aRange.Min()); i < static_cast<short>(aRange.Max()); ++i)
1897         {
1898             SwTOXSortTabBase* pOld = m_aSortArr[i].get();
1899             if (pOld->equivalent(*pNew))
1900             {
1901                 if (pOld->sort_lt(*pNew))
1902                 {
1903                     return;
1904                 }
1905                 else
1906                 {
1907                     // remove the old content
1908                     m_aSortArr.erase( m_aSortArr.begin() + i );
1909                     aRange.Max()--;
1910                     break;
1911                 }
1912             }
1913         }
1914     }
1915 
1916     // find position and insert
1917     tools::Long i;
1918 
1919     for( i = aRange.Min(); i < aRange.Max(); ++i)
1920     {   // Only check for same level
1921         SwTOXSortTabBase* pOld = m_aSortArr[i].get();
1922         if (pOld->equivalent(*pNew))
1923         {
1924             if(TOX_AUTHORITIES != SwTOXBase::GetType())
1925             {
1926                 // Own entry for double entries or keywords
1927                 if( pOld->GetType() == TOX_SORT_CUSTOM &&
1928                     SwTOXSortTabBase::GetOptions() & SwTOIOptions::KeyAsEntry)
1929                     continue;
1930 
1931                 if(!(SwTOXSortTabBase::GetOptions() & SwTOIOptions::SameEntry))
1932                 {   // Own entry
1933                     m_aSortArr.insert(m_aSortArr.begin() + i, std::move(pNew));
1934                     return;
1935                 }
1936                 // If the own entry is already present, add it to the references list
1937                 pOld->aTOXSources.push_back(pNew->aTOXSources[0]);
1938 
1939                 return;
1940             }
1941 #if OSL_DEBUG_LEVEL > 0
1942             else
1943                 OSL_FAIL("Bibliography entries cannot be found here");
1944 #endif
1945         }
1946         if (pNew->sort_lt(*pOld))
1947             break;
1948     }
1949     // Skip SubLevel
1950     while( TOX_INDEX == SwTOXBase::GetType() && i < aRange.Max() &&
1951           m_aSortArr[i]->GetLevel() > pNew->GetLevel() )
1952         i++;
1953 
1954     // Insert at position i
1955     m_aSortArr.insert(m_aSortArr.begin()+i, std::move(pNew));
1956 }
1957 
1958 /// Find Key Range and insert if possible
1959 Range SwTOXBaseSection::GetKeyRange(const OUString& rStr, const OUString& rStrReading,
1960                                     const SwTOXSortTabBase& rNew,
1961                                     sal_uInt16 nLevel, const Range& rRange )
1962 {
1963     const SwTOXInternational& rIntl = *rNew.pTOXIntl;
1964     TextAndReading aToCompare(rStr, rStrReading);
1965 
1966     if( SwTOIOptions::InitialCaps & GetOptions() )
1967     {
1968         aToCompare.sText = rIntl.ToUpper( aToCompare.sText, 0 )
1969                          + aToCompare.sText.subView(1);
1970     }
1971 
1972     OSL_ENSURE(rRange.Min() >= 0 && rRange.Max() >= 0, "Min Max < 0");
1973 
1974     const tools::Long nMin = rRange.Min();
1975     const tools::Long nMax = rRange.Max();
1976 
1977     tools::Long i;
1978 
1979     for( i = nMin; i < nMax; ++i)
1980     {
1981         SwTOXSortTabBase* pBase = m_aSortArr[i].get();
1982 
1983         if( rIntl.IsEqual( pBase->GetText(), pBase->GetLocale(),
1984                            aToCompare, rNew.GetLocale() )  &&
1985                     pBase->GetLevel() == nLevel )
1986             break;
1987     }
1988     if(i == nMax)
1989     {   // If not already present, create and insert
1990         std::unique_ptr<SwTOXCustom> pKey(MakeSwTOXSortTabBase<SwTOXCustom>(
1991                     nullptr, aToCompare, nLevel, rIntl, rNew.GetLocale() ));
1992         for(i = nMin; i < nMax; ++i)
1993         {
1994             if (nLevel == m_aSortArr[i]->GetLevel() && pKey->sort_lt(*m_aSortArr[i]))
1995                 break;
1996         }
1997         m_aSortArr.insert(m_aSortArr.begin() + i, std::move(pKey));
1998     }
1999     const tools::Long nStart = i+1;
2000     const tools::Long nEnd   = m_aSortArr.size();
2001 
2002     // Find end of range
2003     for(i = nStart; i < nEnd; ++i)
2004     {
2005         if(m_aSortArr[i]->GetLevel() <= nLevel)
2006         {
2007             return Range(nStart, i);
2008         }
2009     }
2010     return Range(nStart, nEnd);
2011 }
2012 
2013 bool SwTOXBase::IsTOXBaseInReadonly() const
2014 {
2015     const SwTOXBaseSection *pSect = dynamic_cast<const SwTOXBaseSection*>(this);
2016     if (!pSect || !pSect->GetFormat())
2017         return false;
2018 
2019     const SwSectionNode* pSectNode = pSect->GetFormat()->GetSectionNode();
2020     if (!pSectNode)
2021         return false;
2022 
2023     const SwDocShell* pDocSh = pSectNode->GetDoc().GetDocShell();
2024     if (!pDocSh)
2025         return false;
2026 
2027     if (pDocSh->IsReadOnly())
2028         return true;
2029 
2030     pSectNode = pSectNode->StartOfSectionNode()->FindSectionNode();
2031     if (!pSectNode)
2032         return false;
2033 
2034     return pSectNode->GetSection().IsProtectFlag();
2035 }
2036 
2037 const SfxItemSet* SwTOXBase::GetAttrSet() const
2038 {
2039     const SwTOXBaseSection *pSect = dynamic_cast<const SwTOXBaseSection*>(this);
2040     if(pSect && pSect->GetFormat())
2041         return &pSect->GetFormat()->GetAttrSet();
2042     return nullptr;
2043 }
2044 
2045 void SwTOXBase::SetAttrSet( const SfxItemSet& rSet )
2046 {
2047     SwTOXBaseSection *pSect = dynamic_cast<SwTOXBaseSection*>(this);
2048     if( pSect && pSect->GetFormat() )
2049         pSect->GetFormat()->SetFormatAttr( rSet );
2050 }
2051 
2052 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2053