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