android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / doc / doctxm.cxx
blob82051e0be6a8846ea4c0955a540ffc2d9e420d3a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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>
73 #include <memory>
75 using namespace ::com::sun::star;
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)
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;
86 void SwDoc::GetTOIKeys(SwTOIKeyType eTyp, std::vector<OUString>& rArr,
87 SwRootFrame const& rLayout) const
89 rArr.clear();
91 // Look up all Primary and Secondary via the Pool
92 for (const SfxPoolItem* pPoolItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_TOXMARK))
94 const SwTOXMark* pItem = dynamic_cast<const SwTOXMark*>(pPoolItem);
95 if( !pItem )
96 continue;
97 const SwTOXType* pTOXType = pItem->GetTOXType();
98 if ( !pTOXType || pTOXType->GetType()!=TOX_INDEX )
99 continue;
100 const SwTextTOXMark* pMark = pItem->GetTextTOXMark();
101 if ( pMark && pMark->GetpTextNd() &&
102 pMark->GetpTextNd()->GetNodes().IsDocNodes() &&
103 (!rLayout.IsHideRedlines()
104 || !sw::IsMarkHintHidden(rLayout, *pMark->GetpTextNd(), *pMark)))
106 const OUString sStr = TOI_PRIMARY == eTyp
107 ? pItem->GetPrimaryKey()
108 : pItem->GetSecondaryKey();
110 if( !sStr.isEmpty() )
111 rArr.push_back( sStr );
116 /// Get current table of contents Mark.
117 sal_uInt16 SwDoc::GetCurTOXMark( const SwPosition& rPos,
118 SwTOXMarks& rArr )
120 // search on Position rPos for all SwTOXMarks
121 SwTextNode *const pTextNd = rPos.GetNode().GetTextNode();
122 if( !pTextNd || !pTextNd->GetpSwpHints() )
123 return 0;
125 const SwpHints & rHts = *pTextNd->GetpSwpHints();
126 sal_Int32 nSttIdx;
127 const sal_Int32 *pEndIdx;
129 const sal_Int32 nCurrentPos = rPos.GetContentIndex();
131 for( size_t n = 0; n < rHts.Count(); ++n )
133 const SwTextAttr* pHt = rHts.Get(n);
134 if( RES_TXTATR_TOXMARK != pHt->Which() )
135 continue;
136 if( ( nSttIdx = pHt->GetStart() ) < nCurrentPos )
138 // also check the end
139 pEndIdx = pHt->End();
140 if( nullptr == pEndIdx || *pEndIdx <= nCurrentPos )
141 continue; // keep searching
143 else if( nSttIdx > nCurrentPos )
144 // If Hint's Start is greater than rPos, break, because
145 // the attributes are sorted by Start!
146 break;
148 SwTOXMark* pTMark = const_cast<SwTOXMark*>(&pHt->GetTOXMark());
149 rArr.push_back( pTMark );
151 return rArr.size();
154 /// Delete table of contents Mark
155 void SwDoc::DeleteTOXMark( const SwTOXMark* pTOXMark )
157 const SwTextTOXMark* pTextTOXMark = pTOXMark->GetTextTOXMark();
158 assert(pTextTOXMark);
160 SwTextNode& rTextNd = const_cast<SwTextNode&>(pTextTOXMark->GetTextNode());
161 assert(rTextNd.GetpSwpHints());
163 if (pTextTOXMark->HasDummyChar())
165 // tdf#106377 don't use SwUndoResetAttr, it uses NOTXTATRCHR
166 SwPaM tmp(rTextNd, pTextTOXMark->GetStart(),
167 rTextNd, pTextTOXMark->GetStart()+1);
168 assert(rTextNd.GetText()[pTextTOXMark->GetStart()] == CH_TXTATR_INWORD);
169 getIDocumentContentOperations().DeleteRange(tmp);
171 else
173 std::unique_ptr<SwRegHistory> aRHst;
174 if (GetIDocumentUndoRedo().DoesUndo())
176 // save attributes for Undo
177 SwUndoResetAttr* pUndo = new SwUndoResetAttr(
178 SwPosition( rTextNd, pTextTOXMark->GetStart() ),
179 RES_TXTATR_TOXMARK );
180 GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
182 aRHst.reset(new SwRegHistory(rTextNd, &pUndo->GetHistory()));
183 rTextNd.GetpSwpHints()->Register(aRHst.get());
186 rTextNd.DeleteAttribute( const_cast<SwTextTOXMark*>(pTextTOXMark) );
188 if (GetIDocumentUndoRedo().DoesUndo())
190 if( rTextNd.GetpSwpHints() )
191 rTextNd.GetpSwpHints()->DeRegister();
195 getIDocumentState().SetModified();
198 namespace {
200 /// Travel between table of content Marks
201 class CompareNodeContent
203 SwNodeOffset m_nNode;
204 sal_Int32 m_nContent;
205 public:
206 CompareNodeContent( SwNodeOffset nNd, sal_Int32 nCnt )
207 : m_nNode( nNd ), m_nContent( nCnt ) {}
209 bool operator==( const CompareNodeContent& rCmp ) const
210 { return m_nNode == rCmp.m_nNode && m_nContent == rCmp.m_nContent; }
211 bool operator!=( const CompareNodeContent& rCmp ) const
212 { return m_nNode != rCmp.m_nNode || m_nContent != rCmp.m_nContent; }
213 bool operator< ( const CompareNodeContent& rCmp ) const
214 { return m_nNode < rCmp.m_nNode ||
215 ( m_nNode == rCmp.m_nNode && m_nContent < rCmp.m_nContent); }
216 bool operator<=( const CompareNodeContent& rCmp ) const
217 { return m_nNode < rCmp.m_nNode ||
218 ( m_nNode == rCmp.m_nNode && m_nContent <= rCmp.m_nContent); }
219 bool operator> ( const CompareNodeContent& rCmp ) const
220 { return m_nNode > rCmp.m_nNode ||
221 ( m_nNode == rCmp.m_nNode && m_nContent > rCmp.m_nContent); }
222 bool operator>=( const CompareNodeContent& rCmp ) const
223 { return m_nNode > rCmp.m_nNode ||
224 ( m_nNode == rCmp.m_nNode && m_nContent >= rCmp.m_nContent); }
229 const SwTOXMark& SwDoc::GotoTOXMark( const SwTOXMark& rCurTOXMark,
230 SwTOXSearch eDir, bool bInReadOnly )
232 const SwTextTOXMark* pMark = rCurTOXMark.GetTextTOXMark();
234 CompareNodeContent aAbsIdx(pMark ? pMark->GetpTextNd()->GetIndex() : SwNodeOffset(0), pMark ? pMark->GetStart() : 0);
235 CompareNodeContent aPrevPos( SwNodeOffset(0), 0 );
236 CompareNodeContent aNextPos( NODE_OFFSET_MAX, SAL_MAX_INT32 );
237 CompareNodeContent aMax( SwNodeOffset(0), 0 );
238 CompareNodeContent aMin( NODE_OFFSET_MAX, SAL_MAX_INT32 );
240 const SwTOXMark* pNew = nullptr;
241 const SwTOXMark* pMax = &rCurTOXMark;
242 const SwTOXMark* pMin = &rCurTOXMark;
244 const SwTOXType* pType = rCurTOXMark.GetTOXType();
245 SwTOXMarks aMarks;
246 pType->CollectTextMarks(aMarks);
248 for(SwTOXMark* pTOXMark : aMarks)
250 if ( pTOXMark == &rCurTOXMark )
251 continue;
253 pMark = pTOXMark->GetTextTOXMark();
254 if (!pMark)
255 continue;
257 SwTextNode const*const pTOXSrc = pMark->GetpTextNd();
258 if (!pTOXSrc)
259 continue;
261 Point aPt;
262 std::pair<Point, bool> const tmp(aPt, false);
263 const SwContentFrame* pCFrame = pTOXSrc->getLayoutFrame(
264 getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp);
265 if (!pCFrame)
266 continue;
268 if ( bInReadOnly || !pCFrame->IsProtected() )
270 CompareNodeContent aAbsNew( pTOXSrc->GetIndex(), pMark->GetStart() );
271 switch( eDir )
273 // The following (a bit more complicated) statements make it
274 // possible to also travel across Entries on the same (!)
275 // position. If someone has time, please feel free to optimize.
276 case TOX_SAME_PRV:
277 if (pTOXMark->GetText(nullptr) != rCurTOXMark.GetText(nullptr))
278 break;
279 [[fallthrough]];
280 case TOX_PRV:
281 if ( (aAbsNew < aAbsIdx && aAbsNew > aPrevPos) ||
282 (aAbsIdx == aAbsNew &&
283 (reinterpret_cast<sal_uLong>(&rCurTOXMark) > reinterpret_cast<sal_uLong>(pTOXMark) &&
284 (!pNew || aPrevPos < aAbsIdx || reinterpret_cast<sal_uLong>(pNew) < reinterpret_cast<sal_uLong>(pTOXMark) ) )) ||
285 (aPrevPos == aAbsNew && aAbsIdx != aAbsNew &&
286 reinterpret_cast<sal_uLong>(pTOXMark) > reinterpret_cast<sal_uLong>(pNew)) )
288 pNew = pTOXMark;
289 aPrevPos = aAbsNew;
290 if ( aAbsNew >= aMax )
292 aMax = aAbsNew;
293 pMax = pTOXMark;
296 break;
298 case TOX_SAME_NXT:
299 if (pTOXMark->GetText(nullptr) != rCurTOXMark.GetText(nullptr))
300 break;
301 [[fallthrough]];
302 case TOX_NXT:
303 if ( (aAbsNew > aAbsIdx && aAbsNew < aNextPos) ||
304 (aAbsIdx == aAbsNew &&
305 (reinterpret_cast<sal_uLong>(&rCurTOXMark) < reinterpret_cast<sal_uLong>(pTOXMark) &&
306 (!pNew || aNextPos > aAbsIdx || reinterpret_cast<sal_uLong>(pNew) > reinterpret_cast<sal_uLong>(pTOXMark)) )) ||
307 (aNextPos == aAbsNew && aAbsIdx != aAbsNew &&
308 reinterpret_cast<sal_uLong>(pTOXMark) < reinterpret_cast<sal_uLong>(pNew)) )
310 pNew = pTOXMark;
311 aNextPos = aAbsNew;
312 if ( aAbsNew <= aMin )
314 aMin = aAbsNew;
315 pMin = pTOXMark;
318 break;
323 // We couldn't find a successor
324 // Use minimum or maximum
325 if(!pNew)
327 switch(eDir)
329 case TOX_PRV:
330 case TOX_SAME_PRV:
331 pNew = pMax;
332 break;
333 case TOX_NXT:
334 case TOX_SAME_NXT:
335 pNew = pMin;
336 break;
337 default:
338 pNew = &rCurTOXMark;
341 return *pNew;
344 SwTOXBaseSection* SwDoc::InsertTableOf( const SwPosition& rPos,
345 const SwTOXBase& rTOX,
346 const SfxItemSet* pSet,
347 bool bExpand,
348 SwRootFrame const*const pLayout)
350 SwPaM aPam( rPos );
351 return InsertTableOf( aPam, rTOX, pSet, bExpand, pLayout );
354 SwTOXBaseSection* SwDoc::InsertTableOf( const SwPaM& aPam,
355 const SwTOXBase& rTOX,
356 const SfxItemSet* pSet,
357 bool bExpand,
358 SwRootFrame const*const pLayout )
360 assert(!bExpand || pLayout != nullptr);
361 GetIDocumentUndoRedo().StartUndo( SwUndoId::INSTOX, nullptr );
363 OUString sSectNm = GetUniqueTOXBaseName( *rTOX.GetTOXType(), rTOX.GetTOXName() );
364 SwSectionData aSectionData( SectionType::ToxContent, sSectNm );
366 std::tuple<SwTOXBase const*, sw::RedlineMode, sw::FieldmarkMode, sw::ParagraphBreakMode> const tmp(
367 &rTOX,
368 pLayout && pLayout->IsHideRedlines()
369 ? sw::RedlineMode::Hidden
370 : sw::RedlineMode::Shown,
371 pLayout ? pLayout->GetFieldmarkMode() : sw::FieldmarkMode::ShowBoth,
372 pLayout ? pLayout->GetParagraphBreakMode() : sw::ParagraphBreakMode::Shown);
373 SwTOXBaseSection *const pNewSection = dynamic_cast<SwTOXBaseSection *>(
374 InsertSwSection(aPam, aSectionData, & tmp, pSet, false));
375 if (pNewSection)
377 SwSectionNode *const pSectNd = pNewSection->GetFormat()->GetSectionNode();
378 pNewSection->SetTOXName(sSectNm); // rTOX may have had no name...
380 if( bExpand )
382 // add value for 2nd parameter = true to
383 // indicate, that a creation of a new table of content has to be performed.
384 // Value of 1st parameter = default value.
385 pNewSection->Update( nullptr, pLayout, true );
387 else if( rTOX.GetTitle().getLength()==1 && IsInReading() )
388 // insert title of TOX
390 // then insert the headline section
391 SwNodeIndex aIdx( *pSectNd, +1 );
393 SwTextNode* pHeadNd = GetNodes().MakeTextNode( aIdx.GetNode(),
394 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
396 SwSectionData headerData( SectionType::ToxHeader, pNewSection->GetTOXName()+"_Head" );
398 --aIdx;
399 SwSectionFormat* pSectFormat = MakeSectionFormat();
400 GetNodes().InsertTextSection(
401 *pHeadNd, *pSectFormat, headerData, nullptr, &aIdx.GetNode(), true, false);
405 GetIDocumentUndoRedo().EndUndo( SwUndoId::INSTOX, nullptr );
407 return pNewSection;
410 void SwDoc::InsertTableOf( SwNodeOffset nSttNd, SwNodeOffset nEndNd,
411 const SwTOXBase& rTOX,
412 const SfxItemSet* pSet )
414 // check for recursive TOX
415 SwNode* pNd = GetNodes()[ nSttNd ];
416 SwSectionNode* pSectNd = pNd->FindSectionNode();
417 while( pSectNd )
419 SectionType eT = pSectNd->GetSection().GetType();
420 if( SectionType::ToxHeader == eT || SectionType::ToxContent == eT )
421 return;
422 pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
425 const OUString sSectNm = GetUniqueTOXBaseName(*rTOX.GetTOXType(), rTOX.GetTOXName());
427 SwSectionData aSectionData( SectionType::ToxContent, sSectNm );
429 SwNodeIndex aStt( GetNodes(), nSttNd ), aEnd( GetNodes(), nEndNd );
430 SwSectionFormat* pFormat = MakeSectionFormat();
431 if(pSet)
432 pFormat->SetFormatAttr(*pSet);
434 SwSectionNode *const pNewSectionNode =
435 GetNodes().InsertTextSection(aStt.GetNode(), *pFormat, aSectionData, &rTOX, &aEnd.GetNode());
436 if (!pNewSectionNode)
438 DelSectionFormat( pFormat );
439 return;
442 SwTOXBaseSection *const pNewSection(
443 dynamic_cast<SwTOXBaseSection*>(& pNewSectionNode->GetSection()));
444 if (pNewSection)
445 pNewSection->SetTOXName(sSectNm); // rTOX may have had no name...
448 /// Get current table of contents
449 SwTOXBase* SwDoc::GetCurTOX( const SwPosition& rPos )
451 SwNode& rNd = rPos.GetNode();
452 SwSectionNode* pSectNd = rNd.FindSectionNode();
453 while( pSectNd )
455 SectionType eT = pSectNd->GetSection().GetType();
456 if( SectionType::ToxContent == eT )
458 assert( dynamic_cast< const SwTOXBaseSection *>( &pSectNd->GetSection()) &&
459 "no TOXBaseSection!" );
460 SwTOXBaseSection& rTOXSect = static_cast<SwTOXBaseSection&>(
461 pSectNd->GetSection());
462 return &rTOXSect;
464 pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
466 return nullptr;
469 const SwAttrSet& SwDoc::GetTOXBaseAttrSet(const SwTOXBase& rTOXBase)
471 assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
472 const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase);
473 SwSectionFormat const * pFormat = rTOXSect.GetFormat();
474 OSL_ENSURE( pFormat, "invalid TOXBaseSection!" );
475 return pFormat->GetAttrSet();
478 const SwTOXBase* SwDoc::GetDefaultTOXBase( TOXTypes eTyp, bool bCreate )
480 std::unique_ptr<SwTOXBase>* prBase = nullptr;
481 switch(eTyp)
483 case TOX_CONTENT: prBase = &mpDefTOXBases->pContBase; break;
484 case TOX_INDEX: prBase = &mpDefTOXBases->pIdxBase; break;
485 case TOX_USER: prBase = &mpDefTOXBases->pUserBase; break;
486 case TOX_TABLES: prBase = &mpDefTOXBases->pTableBase; break;
487 case TOX_OBJECTS: prBase = &mpDefTOXBases->pObjBase; break;
488 case TOX_ILLUSTRATIONS: prBase = &mpDefTOXBases->pIllBase; break;
489 case TOX_AUTHORITIES: prBase = &mpDefTOXBases->pAuthBase; break;
490 case TOX_BIBLIOGRAPHY: prBase = &mpDefTOXBases->pBiblioBase; break;
491 case TOX_CITATION: /** TODO */break;
493 if (!prBase)
494 return nullptr;
495 if(!(*prBase) && bCreate)
497 SwForm aForm(eTyp);
498 const SwTOXType* pType = GetTOXType(eTyp, 0);
499 prBase->reset(new SwTOXBase(pType, aForm, SwTOXElement::NONE, pType->GetTypeName()));
501 return prBase->get();
504 void SwDoc::SetDefaultTOXBase(const SwTOXBase& rBase)
506 std::unique_ptr<SwTOXBase>* prBase = nullptr;
507 switch(rBase.GetType())
509 case TOX_CONTENT: prBase = &mpDefTOXBases->pContBase; break;
510 case TOX_INDEX: prBase = &mpDefTOXBases->pIdxBase; break;
511 case TOX_USER: prBase = &mpDefTOXBases->pUserBase; break;
512 case TOX_TABLES: prBase = &mpDefTOXBases->pTableBase; break;
513 case TOX_OBJECTS: prBase = &mpDefTOXBases->pObjBase; break;
514 case TOX_ILLUSTRATIONS: prBase = &mpDefTOXBases->pIllBase; break;
515 case TOX_AUTHORITIES: prBase = &mpDefTOXBases->pAuthBase; break;
516 case TOX_BIBLIOGRAPHY: prBase = &mpDefTOXBases->pBiblioBase; break;
517 case TOX_CITATION: /** TODO */break;
519 if (!prBase)
520 return;
521 prBase->reset(new SwTOXBase(rBase));
524 /// Delete table of contents
525 bool SwDoc::DeleteTOX( const SwTOXBase& rTOXBase, bool bDelNodes )
527 // We only delete the TOX, not the Nodes
528 bool bRet = false;
529 assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
531 const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase);
532 SwSectionFormat const * pFormat = rTOXSect.GetFormat();
533 /* Save the start node of the TOX' section. */
534 SwSectionNode const * pMyNode = pFormat ? pFormat->GetSectionNode() : nullptr;
535 if (pMyNode)
537 GetIDocumentUndoRedo().StartUndo( SwUndoId::CLEARTOXRANGE, nullptr );
539 /* Save start node of section's surrounding. */
540 SwNode const * pStartNd = pMyNode->StartOfSectionNode();
542 /* Look for the point where to move the cursors in the area to
543 delete to. This is done by first searching forward from the
544 end of the TOX' section. If no content node is found behind
545 the TOX one is searched before it. If this is not
546 successful, too, insert new text node behind the end of
547 the TOX' section. The cursors from the TOX' section will be
548 moved to the content node found or the new text node. */
550 /* Set PaM to end of TOX' section and search following content node.
551 aSearchPam will contain the point where to move the cursors
552 to. */
553 SwPaM aSearchPam(*pMyNode->EndOfSectionNode());
554 SwPosition aEndPos(*pStartNd->EndOfSectionNode());
555 if (! aSearchPam.Move() /* no content node found */
556 || *aSearchPam.GetPoint() >= aEndPos /* content node found
557 outside surrounding */
560 /* Set PaM to beginning of TOX' section and search previous
561 content node */
562 SwPaM aTmpPam(*pMyNode);
563 aSearchPam = aTmpPam;
564 SwPosition aStartPos(*pStartNd);
566 if ( ! aSearchPam.Move(fnMoveBackward) /* no content node found */
567 || *aSearchPam.GetPoint() <= aStartPos /* content node
568 found outside
569 surrounding */
572 /* There is no content node in the surrounding of
573 TOX'. Append text node behind TOX' section. */
575 SwPosition aInsPos(*pMyNode->EndOfSectionNode());
576 getIDocumentContentOperations().AppendTextNode(aInsPos);
578 SwPaM aTmpPam1(aInsPos);
579 aSearchPam = aTmpPam1;
583 /* PaM containing the TOX. */
584 SwPaM aPam(*pMyNode->EndOfSectionNode(), *pMyNode);
586 /* Move cursors contained in TOX to the above calculated point. */
587 PaMCorrAbs(aPam, *aSearchPam.GetPoint());
589 if( !bDelNodes )
591 SwSections aArr( 0 );
592 pFormat->GetChildSections( aArr, SectionSort::Not, false );
593 for( const auto pSect : aArr )
595 if( SectionType::ToxHeader == pSect->GetType() )
597 DelSectionFormat( pSect->GetFormat(), bDelNodes );
602 DelSectionFormat( const_cast<SwSectionFormat *>(pFormat), bDelNodes );
604 GetIDocumentUndoRedo().EndUndo( SwUndoId::CLEARTOXRANGE, nullptr );
605 bRet = true;
608 return bRet;
611 /// Manage table of content types
612 sal_uInt16 SwDoc::GetTOXTypeCount(TOXTypes eTyp) const
614 sal_uInt16 nCnt = 0;
615 for( auto const & pTOXType : *mpTOXTypes )
616 if( eTyp == pTOXType->GetType() )
617 ++nCnt;
618 return nCnt;
621 const SwTOXType* SwDoc::GetTOXType( TOXTypes eTyp, sal_uInt16 nId ) const
623 sal_uInt16 nCnt = 0;
624 for( auto const & pTOXType : *mpTOXTypes )
625 if( eTyp == pTOXType->GetType() && nCnt++ == nId )
626 return pTOXType.get();
627 return nullptr;
630 const SwTOXType* SwDoc::InsertTOXType( const SwTOXType& rTyp )
632 SwTOXType * pNew = new SwTOXType(rTyp);
633 mpTOXTypes->emplace_back( pNew );
634 return pNew;
637 OUString SwDoc::GetUniqueTOXBaseName( const SwTOXType& rType,
638 const OUString& sChkStr ) const
640 if( IsInMailMerge())
642 OUString newName = "MailMergeTOX"
643 + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
644 + OUString::number( mpSectionFormatTable->size() + 1 );
645 if( !sChkStr.isEmpty())
646 newName += sChkStr;
647 return newName;
650 bool bUseChkStr = !sChkStr.isEmpty();
651 const OUString& aName( rType.GetTypeName() );
652 const sal_Int32 nNmLen = aName.getLength();
654 SwSectionFormats::size_type nNum = 0;
655 const SwSectionFormats::size_type nFlagSize = ( mpSectionFormatTable->size() / 8 ) +2;
656 std::unique_ptr<sal_uInt8[]> pSetFlags(new sal_uInt8[ nFlagSize ]);
657 memset( pSetFlags.get(), 0, nFlagSize );
659 for( auto pSectionFormat : *mpSectionFormatTable )
661 const SwSectionNode *pSectNd = pSectionFormat->GetSectionNode();
662 if ( !pSectNd )
663 continue;
665 const SwSection& rSect = pSectNd->GetSection();
666 if (rSect.GetType()==SectionType::ToxContent)
668 const OUString& rNm = rSect.GetSectionName();
669 if ( rNm.startsWith(aName) )
671 // Calculate number and set the Flag
672 nNum = o3tl::toInt32(rNm.subView( nNmLen ));
673 if( nNum-- && nNum < mpSectionFormatTable->size() )
674 pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
676 if ( bUseChkStr && sChkStr==rNm )
677 bUseChkStr = false;
681 if( !bUseChkStr )
683 // All Numbers have been flagged accordingly, so get the right Number
684 nNum = mpSectionFormatTable->size();
685 for( SwSectionFormats::size_type n = 0; n < nFlagSize; ++n )
687 sal_uInt8 nTmp = pSetFlags[ n ];
688 if( nTmp != 0xff )
690 // so get the Number
691 nNum = n * 8;
692 while( nTmp & 1 )
694 ++nNum;
695 nTmp >>= 1;
697 break;
701 if ( bUseChkStr )
702 return sChkStr;
703 return aName + OUString::number( ++nNum );
706 bool SwDoc::SetTOXBaseName(const SwTOXBase& rTOXBase, const OUString& rName)
708 assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
709 SwTOXBaseSection* pTOX = const_cast<SwTOXBaseSection*>(static_cast<const SwTOXBaseSection*>(&rTOXBase));
711 if (GetUniqueTOXBaseName(*rTOXBase.GetTOXType(), rName) == rName)
713 pTOX->SetTOXName(rName);
714 pTOX->SetSectionName(rName);
715 getIDocumentState().SetModified();
716 return true;
718 return false;
721 static const SwTextNode* lcl_FindChapterNode( const SwNode& rNd,
722 SwRootFrame const*const pLayout, sal_uInt8 const nLvl = 0 )
724 const SwNode* pNd = &rNd;
725 if( pNd->GetNodes().GetEndOfExtras().GetIndex() > pNd->GetIndex() )
727 // then find the "Anchor" (Body) position
728 Point aPt;
729 SwNode2Layout aNode2Layout( *pNd, pNd->GetIndex() );
730 const SwFrame* pFrame = aNode2Layout.GetFrame( &aPt );
732 if( pFrame )
734 SwPosition aPos( *pNd );
735 pNd = GetBodyTextNode( pNd->GetDoc(), aPos, *pFrame );
736 OSL_ENSURE( pNd, "Where's the paragraph?" );
737 // tdf#151462 - search for outline node containing the current node
738 return pNd ? pNd->FindOutlineNodeOfLevel(pNd->GetSectionLevel() - 1, pLayout) : nullptr;
741 return pNd->FindOutlineNodeOfLevel(nLvl, pLayout);
744 static bool IsHeadingContained(const SwTextNode* pChptrNd, const SwNode& rNd)
746 const SwNode* pNd = &rNd;
747 const SwOutlineNodes& rONds = pNd->GetNodes().GetOutLineNds();
748 bool bIsHeadingContained = false;
749 if (!rONds.empty())
751 bool bCheckFirst = false;
752 SwOutlineNodes::size_type nPos;
754 if (!rONds.Seek_Entry(const_cast<SwNode*>(pNd), &nPos))
756 if (nPos == 0)
757 bCheckFirst = true;
758 else
759 nPos--;
762 if (bCheckFirst)
764 const SwContentNode* pCNd = pNd->GetContentNode();
766 Point aPt(0, 0);
767 std::pair<Point, bool> const tmp(aPt, false);
769 const SwFrame* pChptrFrame = pChptrNd ? pChptrNd->getLayoutFrame(
770 pChptrNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
771 const SwPageFrame* pChptrPgFrame = pChptrFrame ? pChptrFrame->FindPageFrame() : nullptr;
772 const SwFrame* pNdFrame
773 = pCNd ? pCNd->getLayoutFrame(
774 pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp)
775 : nullptr;
777 // Check if the one asking doesn't precede the page of the specified chapter note
778 bIsHeadingContained
779 = pNdFrame && pChptrPgFrame
780 && pChptrPgFrame->getFrameArea().Top() <= pNdFrame->getFrameArea().Top();
781 // Check if the one asking doesn't succeed the specified chapter note
782 if (bIsHeadingContained)
784 const SwNode* aChptrNd = pChptrNd;
785 if (!rONds.Seek_Entry(const_cast<SwNode*>(aChptrNd), &nPos) && nPos)
786 nPos--;
787 // Search for the next outline node with a larger level than the specified chapter node
788 while (nPos < rONds.size() - 1
789 && pChptrNd->GetAttrOutlineLevel()
790 < rONds[nPos + 1]->GetTextNode()->GetAttrOutlineLevel())
791 nPos++;
792 // If there exists such an outline node, check if the one asking doesn't succeed
793 // the specified chapter node
794 if (nPos < rONds.size() - 1) {
795 nPos++;
796 const auto aONdsTxtNd = rONds[nPos]->GetTextNode();
797 pChptrFrame = aONdsTxtNd->getLayoutFrame(
798 aONdsTxtNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr,
799 &tmp);
800 pChptrPgFrame = pChptrFrame ? pChptrFrame->FindPageFrame() : nullptr;
801 bIsHeadingContained
802 = pNdFrame && pChptrPgFrame
803 && pChptrPgFrame->getFrameArea().Top() >= pNdFrame->getFrameArea().Top();
807 else
809 // Search for the next outline node which lies not within the current chapter node
810 while (nPos > 0
811 && pChptrNd->GetAttrOutlineLevel()
812 < rONds[nPos]->GetTextNode()->GetAttrOutlineLevel())
813 nPos--;
814 bIsHeadingContained = pChptrNd == rONds[nPos]->GetTextNode();
817 else
819 // If there are no outline nodes, consider the heading contained,
820 // otherwise the _XDocumentIndex._update() test fails
821 bIsHeadingContained = true;
823 return bIsHeadingContained;
826 // Table of contents class
827 SwTOXBaseSection::SwTOXBaseSection(SwTOXBase const& rBase, SwSectionFormat & rFormat)
828 : SwTOXBase( rBase )
829 , SwSection( SectionType::ToxContent, OUString(), rFormat )
831 SetProtect( rBase.IsProtected() );
832 SetSectionName( GetTOXName() );
835 SwTOXBaseSection::~SwTOXBaseSection()
839 bool SwTOXBaseSection::SetPosAtStartEnd( SwPosition& rPos ) const
841 bool bRet = false;
842 const SwSectionNode* pSectNd = GetFormat()->GetSectionNode();
843 if( pSectNd )
845 rPos.Assign(*pSectNd);
846 pSectNd->GetDoc().GetNodes().GoNext( &rPos );
847 bRet = true;
849 return bRet;
852 /// Collect table of contents content
853 void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
854 SwRootFrame const*const pLayout,
855 const bool _bNewTOX)
857 if (!GetFormat())
858 return;
859 SwSectionNode const*const pSectNd(GetFormat()->GetSectionNode());
860 if (nullptr == pSectNd ||
861 !pSectNd->GetNodes().IsDocNodes() ||
862 IsHiddenFlag() ||
863 (pLayout->HasMergedParas() && pSectNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden))
865 return;
868 if ( !mbKeepExpression )
870 maMSTOCExpression.clear();
873 SwDoc& rDoc = const_cast<SwDoc&>(pSectNd->GetDoc());
875 if (pAttr && GetFormat())
876 rDoc.ChgFormat(*GetFormat(), *pAttr);
878 // determine default page description, which will be used by the content nodes,
879 // if no appropriate one is found.
880 const SwPageDesc* pDefaultPageDesc;
882 pDefaultPageDesc =
883 pSectNd->GetSection().GetFormat()->GetPageDesc().GetPageDesc();
884 if ( !_bNewTOX && !pDefaultPageDesc )
886 // determine page description of table-of-content
887 SwNodeOffset nPgDescNdIdx = pSectNd->GetIndex() + 1;
888 SwNodeOffset* pPgDescNdIdx = &nPgDescNdIdx;
889 pDefaultPageDesc = pSectNd->FindPageDesc( pPgDescNdIdx );
890 if ( nPgDescNdIdx < pSectNd->GetIndex() )
892 pDefaultPageDesc = nullptr;
895 // consider end node of content section in the node array.
896 if ( !pDefaultPageDesc &&
897 ( pSectNd->EndOfSectionNode()->GetIndex() <
898 (pSectNd->GetNodes().GetEndOfContent().GetIndex() - 1) )
901 // determine page description of content after table-of-content
902 SwNodeIndex aIdx( *(pSectNd->EndOfSectionNode()) );
903 const SwContentNode* pNdAfterTOX = pSectNd->GetNodes().GoNext( &aIdx );
904 const SwAttrSet& aNdAttrSet = pNdAfterTOX->GetSwAttrSet();
905 const SvxBreak eBreak = aNdAttrSet.GetBreak().GetBreak();
906 if ( eBreak != SvxBreak::PageBefore && eBreak != SvxBreak::PageBoth )
908 pDefaultPageDesc = pNdAfterTOX->FindPageDesc();
911 // consider start node of content section in the node array.
912 if ( !pDefaultPageDesc &&
913 ( pSectNd->GetIndex() >
914 (pSectNd->GetNodes().GetEndOfContent().StartOfSectionIndex() + 1) )
917 // determine page description of content before table-of-content
918 SwNodeIndex aIdx( *pSectNd );
919 SwContentNode* pTmp = SwNodes::GoPrevious( &aIdx );
920 assert(pTmp); // make coverity happy
921 pDefaultPageDesc = pTmp->FindPageDesc();
924 if ( !pDefaultPageDesc )
926 // determine default page description
927 pDefaultPageDesc = &rDoc.GetPageDesc( 0 );
931 rDoc.getIDocumentState().SetModified();
933 // get current Language
934 SwTOXInternational aIntl( GetLanguage(),
935 TOX_INDEX == GetTOXType()->GetType() ?
936 GetOptions() : SwTOIOptions::NONE,
937 GetSortAlgorithm() );
939 m_aSortArr.clear();
941 // find the first layout node for this TOX, if it only find the content
942 // in his own chapter
943 const SwSectionNode* pChapterSectNd = IsFromChapter() ? pSectNd->FindSectionNode() : nullptr;
944 const SwTextNode* pOwnChapterNode = pChapterSectNd
945 ? ::lcl_FindChapterNode( *pSectNd, pLayout, pChapterSectNd->GetSectionLevel() + 1 )
946 : nullptr;
948 SwNode2LayoutSaveUpperFrames aN2L(*pSectNd);
949 const_cast<SwSectionNode*>(pSectNd)->DelFrames();
951 // This would be a good time to update the Numbering
952 rDoc.UpdateNumRule();
954 if( GetCreateType() & SwTOXElement::Mark )
955 UpdateMarks( aIntl, pOwnChapterNode, pLayout );
957 if( GetCreateType() & SwTOXElement::OutlineLevel )
958 UpdateOutline( pOwnChapterNode, pLayout );
960 if( GetCreateType() & SwTOXElement::Template )
961 UpdateTemplate( pOwnChapterNode, pLayout );
963 if( GetCreateType() & SwTOXElement::Ole ||
964 TOX_OBJECTS == SwTOXBase::GetType())
965 UpdateContent( SwTOXElement::Ole, pOwnChapterNode, pLayout );
967 if( GetCreateType() & SwTOXElement::Table ||
968 (TOX_TABLES == SwTOXBase::GetType() && IsFromObjectNames()) )
969 UpdateTable( pOwnChapterNode, pLayout );
971 if( GetCreateType() & SwTOXElement::Graphic ||
972 (TOX_ILLUSTRATIONS == SwTOXBase::GetType() && IsFromObjectNames()))
973 UpdateContent( SwTOXElement::Graphic, pOwnChapterNode, pLayout );
975 if( !GetSequenceName().isEmpty() && !IsFromObjectNames() &&
976 (TOX_TABLES == SwTOXBase::GetType() ||
977 TOX_ILLUSTRATIONS == SwTOXBase::GetType() ) )
978 UpdateSequence( pOwnChapterNode, pLayout );
980 if( GetCreateType() & SwTOXElement::Frame )
981 UpdateContent( SwTOXElement::Frame, pOwnChapterNode, pLayout );
983 if(TOX_AUTHORITIES == SwTOXBase::GetType())
984 UpdateAuthorities( aIntl, pLayout );
986 // Insert AlphaDelimiters if needed (just for keywords)
987 if( TOX_INDEX == SwTOXBase::GetType() &&
988 ( GetOptions() & SwTOIOptions::AlphaDelimiter ) )
989 InsertAlphaDelimiter( aIntl );
991 // remove old content an insert one empty textnode (to hold the layout!)
992 SwTextNode* pFirstEmptyNd;
994 SwUndoUpdateIndex * pUndo(nullptr);
996 rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSectNd, true, RedlineType::Any );
998 SwNodeIndex aSttIdx( *pSectNd, +1 );
999 SwNodeIndex aEndIdx( *pSectNd->EndOfSectionNode() );
1000 pFirstEmptyNd = rDoc.GetNodes().MakeTextNode( aEndIdx.GetNode(),
1001 rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
1004 // Task 70995 - save and restore PageDesc and Break Attributes
1005 SwNodeIndex aNxtIdx( aSttIdx );
1006 const SwContentNode* pCNd = aNxtIdx.GetNode().GetContentNode();
1007 if( !pCNd )
1008 pCNd = rDoc.GetNodes().GoNext( &aNxtIdx );
1009 assert(pCNd != pFirstEmptyNd);
1010 assert(pCNd->GetIndex() < pFirstEmptyNd->GetIndex());
1011 if( pCNd->HasSwAttrSet() )
1013 SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange );
1014 aBrkSet.Put( *pCNd->GetpSwAttrSet() );
1015 if( aBrkSet.Count() )
1016 pFirstEmptyNd->SetAttr( aBrkSet );
1020 if (rDoc.GetIDocumentUndoRedo().DoesUndo())
1022 // note: this will first append a SwUndoDelSection from the ctor...
1023 pUndo = new SwUndoUpdateIndex(*this);
1024 // tdf#123313 insert Undo *after* all CrossRefBookmark Undos have
1025 // been inserted by the Update*() functions
1026 rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndoUpdateIndex>(pUndo));
1028 else
1030 --aEndIdx;
1031 SwPosition aPos( aEndIdx, pFirstEmptyNd, 0 );
1032 SwDoc::CorrAbs( aSttIdx, aEndIdx, aPos, true );
1034 // delete flys in whole range including start node which requires
1035 // giving the node before start node as Mark parameter, hence -1.
1036 // (flys must be deleted because the anchor nodes are removed)
1037 DelFlyInRange( SwNodeIndex(aSttIdx, -1).GetNode(), aEndIdx.GetNode() );
1039 rDoc.GetNodes().Delete( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() );
1043 // insert title of TOX
1044 if ( !GetTitle().isEmpty() )
1046 // then insert the headline section
1047 SwNodeIndex aIdx( *pSectNd, +1 );
1049 SwTextNode* pHeadNd = rDoc.GetNodes().MakeTextNode( aIdx.GetNode(),
1050 GetTextFormatColl( FORM_TITLE ) );
1051 pHeadNd->InsertText( GetTitle(), SwContentIndex( pHeadNd ) );
1053 SwSectionData headerData( SectionType::ToxHeader, GetTOXName()+"_Head" );
1055 --aIdx;
1056 SwSectionFormat* pSectFormat = rDoc.MakeSectionFormat();
1057 rDoc.GetNodes().InsertTextSection(
1058 *pHeadNd, *pSectFormat, headerData, nullptr, &aIdx.GetNode(), true, false);
1060 if (pUndo)
1062 pUndo->TitleSectionInserted(*pSectFormat);
1066 // Sort the List of all TOC Marks and TOC Sections
1067 std::vector<SwTextFormatColl*> aCollArr( GetTOXForm().GetFormMax(), nullptr );
1068 std::unordered_map<OUString, int> markURLs;
1069 SwNodeIndex aInsPos( *pFirstEmptyNd, 1 );
1070 for( size_t nCnt = 0; nCnt < m_aSortArr.size(); ++nCnt )
1072 ::SetProgressState( 0, rDoc.GetDocShell() );
1074 // Put the Text into the TOC
1075 sal_uInt16 nLvl = m_aSortArr[ nCnt ]->GetLevel();
1076 SwTextFormatColl* pColl = aCollArr[ nLvl ];
1077 if( !pColl )
1079 pColl = GetTextFormatColl( nLvl );
1080 aCollArr[ nLvl ] = pColl;
1083 // Generate: Set dynamic TabStops
1084 SwTextNode* pTOXNd = rDoc.GetNodes().MakeTextNode( aInsPos.GetNode() , pColl );
1085 m_aSortArr[ nCnt ]->pTOXNd = pTOXNd;
1087 // Generate: Evaluate Form and insert the place holder for the
1088 // page number. If it is a TOX_INDEX and the SwForm IsCommaSeparated()
1089 // then a range of entries must be generated into one paragraph
1090 size_t nRange = 1;
1091 if(TOX_INDEX == SwTOXBase::GetType() &&
1092 GetTOXForm().IsCommaSeparated() &&
1093 m_aSortArr[nCnt]->GetType() == TOX_SORT_INDEX)
1095 const SwTOXMark& rMark = m_aSortArr[nCnt]->pTextMark->GetTOXMark();
1096 const OUString& sPrimKey = rMark.GetPrimaryKey();
1097 const OUString& sSecKey = rMark.GetSecondaryKey();
1098 const SwTOXMark* pNextMark = nullptr;
1099 while(m_aSortArr.size() > (nCnt + nRange) &&
1100 m_aSortArr[nCnt + nRange]->GetType() == TOX_SORT_INDEX )
1102 pNextMark = &(m_aSortArr[nCnt + nRange]->pTextMark->GetTOXMark());
1103 if( !pNextMark ||
1104 pNextMark->GetPrimaryKey() != sPrimKey ||
1105 pNextMark->GetSecondaryKey() != sSecKey)
1106 break;
1107 nRange++;
1110 // pass node index of table-of-content section and default page description
1111 // to method <GenerateText(..)>.
1112 ::SetProgressState( 0, rDoc.GetDocShell() );
1114 std::shared_ptr<sw::ToxTabStopTokenHandler> tabStopTokenHandler =
1115 std::make_shared<sw::DefaultToxTabStopTokenHandler>(
1116 pSectNd->GetIndex(), *pDefaultPageDesc, GetTOXForm().IsRelTabPos(),
1117 rDoc.GetDocumentSettingManager().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) ?
1118 sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_INDENT :
1119 sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_PAGE);
1120 sw::ToxTextGenerator ttgn(GetTOXForm(), tabStopTokenHandler);
1121 ttgn.GenerateText(GetFormat()->GetDoc(), markURLs, m_aSortArr, nCnt, nRange, pLayout);
1122 nCnt += nRange - 1;
1125 // delete the first dummy node and remove all Cursor into the previous node
1126 aInsPos = *pFirstEmptyNd;
1128 SwPaM aCorPam( *pFirstEmptyNd );
1129 if( !aCorPam.Move( fnMoveForward ) )
1130 aCorPam.Move( fnMoveBackward );
1131 SwNodeIndex aEndIdx( aInsPos, 1 );
1132 SwDoc::CorrAbs( aInsPos, aEndIdx, *aCorPam.GetPoint(), true );
1134 // Task 70995 - save and restore PageDesc and Break Attributes
1135 if( pFirstEmptyNd->HasSwAttrSet() )
1137 if( !GetTitle().isEmpty() )
1138 aEndIdx = *pSectNd;
1139 else
1140 aEndIdx = *pFirstEmptyNd;
1141 SwContentNode* pCNd = rDoc.GetNodes().GoNext( &aEndIdx );
1142 if( pCNd ) // Robust against defect documents, e.g. i60336
1143 pCNd->SetAttr( *pFirstEmptyNd->GetpSwAttrSet() );
1147 // now create the new Frames
1148 SwNodeOffset nIdx = pSectNd->GetIndex();
1149 // don't delete if index is empty
1150 if(nIdx + SwNodeOffset(2) < pSectNd->EndOfSectionIndex())
1151 rDoc.GetNodes().Delete( aInsPos );
1153 aN2L.RestoreUpperFrames( rDoc.GetNodes(), nIdx, nIdx + 1 );
1154 o3tl::sorted_vector<SwRootFrame*> aAllLayouts = rDoc.GetAllLayouts();
1155 for ( const auto& rpLayout : aAllLayouts )
1157 SwFrame::CheckPageDescs( static_cast<SwPageFrame*>(rpLayout->Lower()) );
1160 SetProtect( SwTOXBase::IsProtected() );
1163 void SwTOXBaseSection::InsertAlphaDelimiter( const SwTOXInternational& rIntl )
1165 SwDoc* pDoc = GetFormat()->GetDoc();
1166 OUString sLastDeli;
1167 size_t i = 0;
1168 while( i < m_aSortArr.size() )
1170 ::SetProgressState( 0, pDoc->GetDocShell() );
1172 sal_uInt16 nLevel = m_aSortArr[i]->GetLevel();
1174 // Skip AlphaDelimiter
1175 if( nLevel == FORM_ALPHA_DELIMITER )
1176 continue;
1178 const OUString sDeli = rIntl.GetIndexKey( m_aSortArr[i]->GetText(),
1179 m_aSortArr[i]->GetLocale() );
1181 // Do we already have a Delimiter?
1182 if( !sDeli.isEmpty() && sLastDeli != sDeli )
1184 // We skip all that are less than a small Blank (these are special characters)
1185 if( ' ' <= sDeli[0] )
1187 std::unique_ptr<SwTOXCustom> pCst(
1188 MakeSwTOXSortTabBase<SwTOXCustom>(nullptr,
1189 TextAndReading(sDeli, OUString()),
1190 FORM_ALPHA_DELIMITER,
1191 rIntl, m_aSortArr[i]->GetLocale() ));
1192 m_aSortArr.insert( m_aSortArr.begin() + i, std::move(pCst));
1193 i++;
1195 sLastDeli = sDeli;
1198 // Skip until we get to the same or a lower Level
1199 do {
1200 i++;
1201 } while (i < m_aSortArr.size() && m_aSortArr[i]->GetLevel() > nLevel);
1205 /// Evaluate Template
1206 SwTextFormatColl* SwTOXBaseSection::GetTextFormatColl( sal_uInt16 nLevel )
1208 SwDoc* pDoc = GetFormat()->GetDoc();
1209 const OUString& rName = GetTOXForm().GetTemplate( nLevel );
1210 SwTextFormatColl* pColl = !rName.isEmpty() ? pDoc->FindTextFormatCollByName(rName) :nullptr;
1211 if( !pColl )
1213 sal_uInt16 nPoolFormat = 0;
1214 const TOXTypes eMyType = SwTOXBase::GetType();
1215 switch( eMyType )
1217 case TOX_INDEX: nPoolFormat = RES_POOLCOLL_TOX_IDXH; break;
1218 case TOX_USER:
1219 if( nLevel < 6 )
1220 nPoolFormat = RES_POOLCOLL_TOX_USERH;
1221 else
1222 nPoolFormat = RES_POOLCOLL_TOX_USER6 - 6;
1223 break;
1224 case TOX_ILLUSTRATIONS: nPoolFormat = RES_POOLCOLL_TOX_ILLUSH; break;
1225 case TOX_OBJECTS: nPoolFormat = RES_POOLCOLL_TOX_OBJECTH; break;
1226 case TOX_TABLES: nPoolFormat = RES_POOLCOLL_TOX_TABLESH; break;
1227 case TOX_AUTHORITIES:
1228 case TOX_BIBLIOGRAPHY:
1229 nPoolFormat = RES_POOLCOLL_TOX_AUTHORITIESH; break;
1230 case TOX_CITATION: /** TODO */break;
1231 case TOX_CONTENT:
1232 // There's a jump in the ContentArea!
1233 if( nLevel < 6 )
1234 nPoolFormat = RES_POOLCOLL_TOX_CNTNTH;
1235 else
1236 nPoolFormat = RES_POOLCOLL_TOX_CNTNT6 - 6;
1237 break;
1240 if(eMyType == TOX_AUTHORITIES && nLevel)
1241 nPoolFormat = nPoolFormat + 1;
1242 else if(eMyType == TOX_INDEX && nLevel)
1244 // pool: Level 1,2,3, Delimiter
1245 // SwForm: Delimiter, Level 1,2,3
1246 nPoolFormat += 1 == nLevel ? nLevel + 3 : nLevel - 1;
1248 else
1249 nPoolFormat = nPoolFormat + nLevel;
1250 pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolFormat );
1252 return pColl;
1255 void SwTOXBaseSection::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
1257 if (auto pFindHint = dynamic_cast<const sw::FindContentFrameHint*>(&rHint))
1259 if(pFindHint->m_rpContentFrame)
1260 return;
1261 auto pSectFormat = GetFormat();
1262 if(!pSectFormat)
1263 return;
1264 const SwSectionNode* pSectNd = pSectFormat->GetSectionNode();
1265 if(!pSectNd)
1266 return;
1267 SwNodeIndex aIdx(*pSectNd, 1);
1268 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
1269 if(!pCNd)
1270 pCNd = pFindHint->m_rDoc.GetNodes().GoNext(&aIdx);
1271 if(!pCNd)
1272 return;
1273 if(pCNd->EndOfSectionIndex() >= pSectNd->EndOfSectionIndex())
1274 return;
1275 pFindHint->m_rpContentFrame = pCNd->getLayoutFrame(&pFindHint->m_rLayout);
1276 } else
1277 SwTOXBase::SwClientNotify(rModify, rHint);
1280 /// Create from Marks
1281 void SwTOXBaseSection::UpdateMarks(const SwTOXInternational& rIntl,
1282 const SwTextNode* pOwnChapterNode,
1283 SwRootFrame const*const pLayout)
1285 const auto pType = static_cast<SwTOXType*>(SwTOXBase::GetRegisteredIn());
1286 auto pShell = GetFormat()->GetDoc()->GetDocShell();
1287 const TOXTypes eTOXTyp = GetTOXType()->GetType();
1288 std::vector<std::reference_wrapper<SwTextTOXMark>> vMarks;
1289 pType->CollectTextTOXMarksForLayout(vMarks, pLayout);
1290 for(auto& rMark: vMarks)
1292 ::SetProgressState(0, pShell);
1293 auto& rNode = rMark.get().GetTextNode();
1294 if(IsFromChapter() && !IsHeadingContained(pOwnChapterNode, rNode))
1295 continue;
1296 auto rTOXMark = rMark.get().GetTOXMark();
1297 if(TOX_INDEX == eTOXTyp)
1299 // index entry mark
1300 assert(g_pBreakIt);
1301 lang::Locale aLocale = g_pBreakIt->GetLocale(rNode.GetLang(rMark.get().GetStart()));
1302 InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_ENTRY, rIntl, aLocale));
1303 if(GetOptions() & SwTOIOptions::KeyAsEntry && !rTOXMark.GetPrimaryKey().isEmpty())
1305 InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_PRIMARY_KEY, rIntl, aLocale));
1306 if (!rTOXMark.GetSecondaryKey().isEmpty())
1308 InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_SECONDARY_KEY, rIntl, aLocale));
1312 else if(TOX_USER == eTOXTyp || rTOXMark.GetLevel() <= GetLevel())
1313 { // table of content mark, also used for user marks
1314 InsertSorted(MakeSwTOXSortTabBase<SwTOXContent>(pLayout, rNode, &rMark.get(), rIntl));
1319 /// Generate table of contents from outline
1320 void SwTOXBaseSection::UpdateOutline( const SwTextNode* pOwnChapterNode,
1321 SwRootFrame const*const pLayout)
1323 SwDoc* pDoc = GetFormat()->GetDoc();
1324 SwNodes& rNds = pDoc->GetNodes();
1326 const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
1327 for( auto pOutlineNode : rOutlNds )
1329 ::SetProgressState( 0, pDoc->GetDocShell() );
1330 SwTextNode* pTextNd = pOutlineNode->GetTextNode();
1331 if( pTextNd && pTextNd->Len() && pTextNd->HasWriterListeners() &&
1332 o3tl::make_unsigned( pTextNd->GetAttrOutlineLevel()) <= GetLevel() &&
1333 pTextNd->getLayoutFrame(pLayout) &&
1334 !pTextNd->IsHiddenByParaField() &&
1335 !pTextNd->HasHiddenCharAttribute( true ) &&
1336 (!pLayout || !pLayout->HasMergedParas()
1337 || static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) &&
1338 ( !IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pTextNd) ))
1340 InsertSorted(MakeSwTOXSortTabBase<SwTOXPara>(pLayout, *pTextNd, SwTOXElement::OutlineLevel));
1345 /// Generate table of contents from template areas
1346 void SwTOXBaseSection::UpdateTemplate(const SwTextNode* pOwnChapterNode,
1347 SwRootFrame const*const pLayout)
1349 SwDoc* pDoc = GetFormat()->GetDoc();
1350 for(sal_uInt16 i = 0; i < MAXLEVEL; i++)
1352 const OUString sTmpStyleNames = GetStyleNames(i);
1353 if (sTmpStyleNames.isEmpty())
1354 continue;
1356 sal_Int32 nIndex = 0;
1357 while (nIndex >= 0)
1359 SwTextFormatColl* pColl = pDoc->FindTextFormatCollByName(
1360 sTmpStyleNames.getToken( 0, TOX_STYLE_DELIMITER, nIndex ));
1361 //TODO: no outline Collections in content indexes if OutlineLevels are already included
1362 if( !pColl ||
1363 ( TOX_CONTENT == SwTOXBase::GetType() &&
1364 GetCreateType() & SwTOXElement::OutlineLevel &&
1365 pColl->IsAssignedToListLevelOfOutlineStyle()) )
1366 continue;
1368 SwIterator<SwTextNode,SwFormatColl> aIter( *pColl );
1369 for( SwTextNode* pTextNd = aIter.First(); pTextNd; pTextNd = aIter.Next() )
1371 ::SetProgressState( 0, pDoc->GetDocShell() );
1373 if (pTextNd->GetText().getLength() &&
1374 pTextNd->getLayoutFrame(pLayout) &&
1375 pTextNd->GetNodes().IsDocNodes() &&
1376 // tdf#40142 - consider level settings of the various text nodes
1377 o3tl::make_unsigned(pTextNd->GetAttrOutlineLevel()) <= GetLevel() &&
1378 (!pLayout || !pLayout->HasMergedParas()
1379 || static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) &&
1380 (!IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pTextNd)))
1382 InsertSorted(MakeSwTOXSortTabBase<SwTOXPara>(pLayout, *pTextNd, SwTOXElement::Template, i + 1));
1389 /// Generate content from sequence fields
1390 void SwTOXBaseSection::UpdateSequence(const SwTextNode* pOwnChapterNode,
1391 SwRootFrame const*const pLayout)
1393 SwDoc* pDoc = GetFormat()->GetDoc();
1394 SwFieldType* pSeqField = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, GetSequenceName(), false);
1395 if(!pSeqField)
1396 return;
1398 std::vector<SwFormatField*> vFields;
1399 pSeqField->GatherFields(vFields);
1400 for(auto pFormatField: vFields)
1402 const SwTextField* pTextField = pFormatField->GetTextField();
1403 SwTextNode& rTextNode = pTextField->GetTextNode();
1404 ::SetProgressState( 0, pDoc->GetDocShell() );
1406 if (rTextNode.GetText().getLength() &&
1407 rTextNode.getLayoutFrame(pLayout) &&
1408 ( !IsFromChapter() || IsHeadingContained(pOwnChapterNode, rTextNode))
1409 && (!pLayout || !pLayout->IsHideRedlines()
1410 || !sw::IsFieldDeletedInModel(pDoc->getIDocumentRedlineAccess(), *pTextField)))
1412 const SwSetExpField& rSeqField = dynamic_cast<const SwSetExpField&>(*(pFormatField->GetField()));
1413 const OUString sName = GetSequenceName()
1414 + OUStringChar(cSequenceMarkSeparator)
1415 + OUString::number( rSeqField.GetSeqNumber() );
1416 std::unique_ptr<SwTOXPara> pNew(new SwTOXPara( rTextNode, SwTOXElement::Sequence, 1, sName ));
1417 // set indexes if the number or the reference text are to be displayed
1418 if( GetCaptionDisplay() == CAPTION_TEXT )
1420 pNew->SetStartIndex(
1421 SwGetExpField::GetReferenceTextPos( *pFormatField, *pDoc ));
1423 else if(GetCaptionDisplay() == CAPTION_NUMBER)
1425 pNew->SetEndIndex(pTextField->GetStart() + 1);
1427 pNew->InitText(pLayout);
1428 InsertSorted(std::move(pNew));
1433 void SwTOXBaseSection::UpdateAuthorities(const SwTOXInternational& rIntl,
1434 SwRootFrame const*const pLayout)
1436 SwDoc* pDoc = GetFormat()->GetDoc();
1437 SwFieldType* pAuthField = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::TableOfAuthorities, OUString(), false);
1438 if(!pAuthField)
1439 return;
1441 std::vector<SwFormatField*> vFields;
1442 pAuthField->GatherFields(vFields);
1443 for(auto pFormatField: vFields)
1445 const auto pTextField = pFormatField->GetTextField();
1446 const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode();
1447 ::SetProgressState( 0, pDoc->GetDocShell() );
1449 if (rTextNode.GetText().getLength() &&
1450 rTextNode.getLayoutFrame(pLayout) &&
1451 (!pLayout || !pLayout->IsHideRedlines()
1452 || !sw::IsFieldDeletedInModel(pDoc->getIDocumentRedlineAccess(), *pTextField)))
1454 //#106485# the body node has to be used!
1455 SwContentFrame *const pFrame = rTextNode.getLayoutFrame(pLayout);
1456 SwPosition aFieldPos(rTextNode);
1457 const SwTextNode* pTextNode = nullptr;
1458 if(pFrame && !pFrame->IsInDocBody())
1459 pTextNode = GetBodyTextNode( *pDoc, aFieldPos, *pFrame );
1460 if(!pTextNode)
1461 pTextNode = &rTextNode;
1463 InsertSorted(MakeSwTOXSortTabBase<SwTOXAuthority>(pLayout, *pTextNode, *pFormatField, rIntl));
1468 static SwTOOElements lcl_IsSOObject( const SvGlobalName& rFactoryNm )
1470 static const struct SoObjType {
1471 SwTOOElements nFlag;
1472 // GlobalNameId
1473 struct {
1474 sal_uInt32 n1;
1475 sal_uInt16 n2, n3;
1476 sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
1477 } aGlNmIds[4];
1478 } aArr[] = {
1479 { SwTOOElements::Math,
1480 { {SO3_SM_CLASSID_60},{SO3_SM_CLASSID_50},
1481 {SO3_SM_CLASSID_40},{SO3_SM_CLASSID_30} } },
1482 { SwTOOElements::Chart,
1483 { {SO3_SCH_CLASSID_60},{SO3_SCH_CLASSID_50},
1484 {SO3_SCH_CLASSID_40},{SO3_SCH_CLASSID_30} } },
1485 { SwTOOElements::Calc,
1486 { {SO3_SC_CLASSID_60},{SO3_SC_CLASSID_50},
1487 {SO3_SC_CLASSID_40},{SO3_SC_CLASSID_30} } },
1488 { SwTOOElements::DrawImpress,
1489 { {SO3_SIMPRESS_CLASSID_60},{SO3_SIMPRESS_CLASSID_50},
1490 {SO3_SIMPRESS_CLASSID_40},{SO3_SIMPRESS_CLASSID_30} } },
1491 { SwTOOElements::DrawImpress,
1492 { {SO3_SDRAW_CLASSID_60},{SO3_SDRAW_CLASSID_50} } }
1495 for( SoObjType const & rArr : aArr )
1496 for (auto & rId : rArr.aGlNmIds)
1498 if( !rId.n1 )
1499 break;
1500 SvGlobalName aGlbNm( rId.n1, rId.n2, rId.n3,
1501 rId.b8, rId.b9, rId.b10, rId.b11,
1502 rId.b12, rId.b13, rId.b14, rId.b15 );
1503 if( rFactoryNm == aGlbNm )
1505 return rArr.nFlag;
1509 return SwTOOElements::NONE;
1512 void SwTOXBaseSection::UpdateContent( SwTOXElement eMyType,
1513 const SwTextNode* pOwnChapterNode,
1514 SwRootFrame const*const pLayout)
1516 SwDoc* pDoc = GetFormat()->GetDoc();
1517 SwNodes& rNds = pDoc->GetNodes();
1518 // on the 1st Node of the 1st Section
1519 SwNodeOffset nIdx = rNds.GetEndOfAutotext().StartOfSectionIndex() + SwNodeOffset(2),
1520 nEndIdx = rNds.GetEndOfAutotext().GetIndex();
1522 while( nIdx < nEndIdx )
1524 ::SetProgressState( 0, pDoc->GetDocShell() );
1526 SwNode* pNd = rNds[ nIdx ];
1527 SwContentNode* pCNd = nullptr;
1528 switch( eMyType )
1530 case SwTOXElement::Frame:
1531 if( !pNd->IsNoTextNode() )
1533 pCNd = pNd->GetContentNode();
1534 if( !pCNd )
1536 SwNodeIndex aTmp( *pNd );
1537 pCNd = rNds.GoNext( &aTmp );
1540 break;
1541 case SwTOXElement::Graphic:
1542 if( pNd->IsGrfNode() )
1543 pCNd = static_cast<SwContentNode*>(pNd);
1544 break;
1545 case SwTOXElement::Ole:
1546 if( pNd->IsOLENode() )
1548 bool bInclude = true;
1549 if(TOX_OBJECTS == SwTOXBase::GetType())
1551 SwOLENode* pOLENode = pNd->GetOLENode();
1552 SwTOOElements nMyOLEOptions = GetOLEOptions();
1553 SwOLEObj& rOLEObj = pOLENode->GetOLEObj();
1555 if( rOLEObj.IsOleRef() ) // Not yet loaded
1557 SvGlobalName aTmpName( rOLEObj.GetOleRef()->getClassID() );
1558 SwTOOElements nObj = ::lcl_IsSOObject( aTmpName );
1559 bInclude = ( (nMyOLEOptions & SwTOOElements::Other) && SwTOOElements::NONE == nObj )
1560 || (nMyOLEOptions & nObj);
1562 else
1564 OSL_FAIL("OLE Object no loaded?");
1565 bInclude = false;
1569 if(bInclude)
1570 pCNd = static_cast<SwContentNode*>(pNd);
1572 break;
1573 default: break;
1576 if( pCNd )
1578 // find node in body text
1579 int nSetLevel = USHRT_MAX;
1581 //#111105# tables of tables|illustrations|objects don't support hierarchies
1582 if( IsLevelFromChapter() &&
1583 TOX_TABLES != SwTOXBase::GetType() &&
1584 TOX_ILLUSTRATIONS != SwTOXBase::GetType() &&
1585 TOX_OBJECTS != SwTOXBase::GetType() )
1587 const SwTextNode* pOutlNd = ::lcl_FindChapterNode( *pCNd,
1588 pLayout, MAXLEVEL - 1);
1589 if( pOutlNd )
1591 if( pOutlNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle())
1593 nSetLevel = pOutlNd->GetTextColl()->GetAttrOutlineLevel();
1598 if (pCNd->getLayoutFrame(pLayout)
1599 && (!pLayout || !pLayout->HasMergedParas()
1600 || pCNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
1601 && ( !IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pCNd)))
1603 std::unique_ptr<SwTOXPara> pNew( MakeSwTOXSortTabBase<SwTOXPara>(
1604 pLayout, *pCNd, eMyType,
1605 ( USHRT_MAX != nSetLevel )
1606 ? o3tl::narrowing<sal_uInt16>(nSetLevel)
1607 : FORM_ALPHA_DELIMITER ) );
1608 InsertSorted( std::move(pNew) );
1612 nIdx = pNd->StartOfSectionNode()->EndOfSectionIndex() + SwNodeOffset(2); // 2 == End/Start Node
1616 /// Collect table entries
1617 void SwTOXBaseSection::UpdateTable(const SwTextNode* pOwnChapterNode,
1618 SwRootFrame const*const pLayout)
1620 SwDoc* pDoc = GetFormat()->GetDoc();
1621 SwNodes& rNds = pDoc->GetNodes();
1623 for(SwTableFormat* pFrameFormat: *pDoc->GetTableFrameFormats())
1625 ::SetProgressState( 0, pDoc->GetDocShell() );
1627 SwTable* pTmpTable = SwTable::FindTable( pFrameFormat );
1628 SwTableBox* pFBox;
1629 if( pTmpTable && nullptr != (pFBox = pTmpTable->GetTabSortBoxes()[0] ) &&
1630 pFBox->GetSttNd() && pFBox->GetSttNd()->GetNodes().IsDocNodes() )
1632 const SwTableNode* pTableNd = pFBox->GetSttNd()->FindTableNode();
1633 SwNodeIndex aContentIdx( *pTableNd, 1 );
1635 SwContentNode* pCNd;
1636 while( nullptr != ( pCNd = rNds.GoNext( &aContentIdx ) ) &&
1637 aContentIdx.GetIndex() < pTableNd->EndOfSectionIndex() )
1639 if (pCNd->getLayoutFrame(pLayout)
1640 && (!pLayout || !pLayout->HasMergedParas()
1641 || pCNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
1642 && (!IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pCNd)))
1644 std::unique_ptr<SwTOXTable> pNew(new SwTOXTable( *pCNd ));
1645 if( IsLevelFromChapter() && TOX_TABLES != SwTOXBase::GetType())
1647 const SwTextNode* pOutlNd =
1648 ::lcl_FindChapterNode(*pCNd, pLayout, MAXLEVEL - 1);
1649 if( pOutlNd )
1651 if( pOutlNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle())
1653 const int nTmp = pOutlNd->GetTextColl()->GetAttrOutlineLevel();
1654 pNew->SetLevel(o3tl::narrowing<sal_uInt16>(nTmp));
1658 pNew->InitText(pLayout);
1659 InsertSorted(std::move(pNew));
1660 break;
1667 /// Calculate PageNumber and insert after formatting
1668 void SwTOXBaseSection::UpdatePageNum()
1670 if( m_aSortArr.empty() )
1671 return ;
1673 // Insert the current PageNumber into the TOC
1674 SwPageFrame* pCurrentPage = nullptr;
1675 sal_uInt16 nPage = 0;
1676 SwDoc* pDoc = GetFormat()->GetDoc();
1678 SwTOXInternational aIntl( GetLanguage(),
1679 TOX_INDEX == GetTOXType()->GetType() ?
1680 GetOptions() : SwTOIOptions::NONE,
1681 GetSortAlgorithm() );
1683 for( size_t nCnt = 0; nCnt < m_aSortArr.size(); ++nCnt )
1685 // Loop over all SourceNodes
1687 // process run in lines
1688 size_t nRange = 0;
1689 if(GetTOXForm().IsCommaSeparated() &&
1690 m_aSortArr[nCnt]->GetType() == TOX_SORT_INDEX)
1692 const SwTOXMark& rMark = m_aSortArr[nCnt]->pTextMark->GetTOXMark();
1693 const OUString& sPrimKey = rMark.GetPrimaryKey();
1694 const OUString& sSecKey = rMark.GetSecondaryKey();
1695 const SwTOXMark* pNextMark = nullptr;
1696 while(m_aSortArr.size() > (nCnt + nRange)&&
1697 m_aSortArr[nCnt + nRange]->GetType() == TOX_SORT_INDEX &&
1698 nullptr != (pNextMark = &(m_aSortArr[nCnt + nRange]->pTextMark->GetTOXMark())) &&
1699 pNextMark->GetPrimaryKey() == sPrimKey &&
1700 pNextMark->GetSecondaryKey() == sSecKey)
1701 nRange++;
1703 else
1704 nRange = 1;
1706 for(size_t nRunInEntry = nCnt; nRunInEntry < nCnt + nRange; ++nRunInEntry)
1708 std::vector<sal_uInt16> aNums; // the PageNumber
1709 std::vector<SwPageDesc*> aDescs; // The PageDescriptors matching the PageNumbers
1710 std::vector<sal_uInt16> aMainNums; // contains page numbers of main entries
1711 SwTOXSortTabBase* pSortBase = m_aSortArr[nRunInEntry].get();
1712 size_t nSize = pSortBase->aTOXSources.size();
1713 for (size_t j = 0; j < nSize; ++j)
1715 ::SetProgressState( 0, pDoc->GetDocShell() );
1717 SwTOXSource& rTOXSource = pSortBase->aTOXSources[j];
1718 if( rTOXSource.pNd )
1720 SwContentFrame* pFrame = rTOXSource.pNd->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() );
1721 OSL_ENSURE( pFrame || pDoc->IsUpdateTOX(), "TOX, no Frame found");
1722 if( !pFrame )
1723 continue;
1724 if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->HasFollow() )
1726 // find the right one
1727 SwTextFrame* pNext;
1728 TextFrameIndex const nPos(static_cast<SwTextFrame*>(pFrame)
1729 ->MapModelToView(static_cast<SwTextNode const*>(rTOXSource.pNd),
1730 rTOXSource.nPos));
1731 for (;;)
1733 pNext = static_cast<SwTextFrame*>(pFrame->GetFollow());
1734 if (!pNext || nPos < pNext->GetOffset())
1735 break;
1736 pFrame = pNext;
1740 SwPageFrame* pTmpPage = pFrame->FindPageFrame();
1741 if( pTmpPage != pCurrentPage )
1743 nPage = pTmpPage->GetVirtPageNum();
1744 pCurrentPage = pTmpPage;
1747 // Insert as sorted
1748 std::vector<sal_uInt16>::size_type i;
1749 for( i = 0; i < aNums.size() && aNums[i] < nPage; ++i )
1752 if( i >= aNums.size() || aNums[ i ] != nPage )
1754 aNums.insert(aNums.begin() + i, nPage);
1755 aDescs.insert(aDescs.begin() + i, pCurrentPage->GetPageDesc() );
1757 // is it a main entry?
1758 if(TOX_SORT_INDEX == pSortBase->GetType() &&
1759 rTOXSource.bMainEntry)
1761 aMainNums.push_back(nPage);
1765 // Insert the PageNumber into the TOC TextNode
1766 const SwTOXSortTabBase* pBase = m_aSortArr[ nCnt ].get();
1767 if(pBase->pTOXNd)
1769 const SwTextNode* pTextNd = pBase->pTOXNd->GetTextNode();
1770 OSL_ENSURE( pTextNd, "no TextNode, wrong TOC" );
1772 UpdatePageNum_( const_cast<SwTextNode*>(pTextNd), aNums, aDescs, &aMainNums,
1773 aIntl );
1777 // Delete the mapping array after setting the right PageNumber
1778 m_aSortArr.clear();
1781 /// Replace the PageNumber place holders. Search for the page no. in the array
1782 /// of main entry page numbers.
1783 static bool lcl_HasMainEntry( const std::vector<sal_uInt16>* pMainEntryNums, sal_uInt16 nToFind )
1785 if (!pMainEntryNums)
1786 return false;
1788 for( auto nMainEntry : *pMainEntryNums )
1789 if (nToFind == nMainEntry)
1790 return true;
1791 return false;
1794 void SwTOXBaseSection::UpdatePageNum_( SwTextNode* pNd,
1795 const std::vector<sal_uInt16>& rNums,
1796 const std::vector<SwPageDesc*>& rDescs,
1797 const std::vector<sal_uInt16>* pMainEntryNums,
1798 const SwTOXInternational& rIntl )
1800 // collect starts end ends of main entry character style
1801 std::optional< std::vector<sal_uInt16> > xCharStyleIdx;
1802 if (pMainEntryNums)
1803 xCharStyleIdx.emplace();
1805 OUString sSrchStr
1806 = OUStringChar(C_NUM_REPL) + SwTOXMark::S_PAGE_DELI + OUStringChar(C_NUM_REPL);
1807 sal_Int32 nStartPos = pNd->GetText().indexOf(sSrchStr);
1808 sSrchStr = OUStringChar(C_NUM_REPL) + OUStringChar(C_END_PAGE_NUM);
1809 sal_Int32 nEndPos = pNd->GetText().indexOf(sSrchStr);
1811 if (-1 == nEndPos || rNums.empty())
1812 return;
1814 if (-1 == nStartPos || nStartPos > nEndPos)
1815 nStartPos = nEndPos;
1817 sal_uInt16 nOld = rNums[0],
1818 nBeg = nOld,
1819 nCount = 0;
1820 OUString aNumStr( rDescs[0]->GetNumType().GetNumStr( nBeg ) );
1821 if( xCharStyleIdx && lcl_HasMainEntry( pMainEntryNums, nBeg ))
1823 xCharStyleIdx->push_back( 0 );
1826 // Delete place holder
1827 SwContentIndex aPos(pNd, nStartPos);
1828 SwCharFormat* pPageNoCharFormat = nullptr;
1829 SwpHints* pHints = pNd->GetpSwpHints();
1830 if(pHints)
1831 for(size_t nHintIdx = 0; nHintIdx < pHints->Count(); ++nHintIdx)
1833 const SwTextAttr* pAttr = pHints->Get(nHintIdx);
1834 const sal_Int32 nTmpEnd = pAttr->End() ? *pAttr->End() : 0;
1835 if( nStartPos >= pAttr->GetStart() &&
1836 (nStartPos + 2) <= nTmpEnd &&
1837 pAttr->Which() == RES_TXTATR_CHARFMT)
1839 pPageNoCharFormat = pAttr->GetCharFormat().GetCharFormat();
1840 break;
1843 pNd->EraseText(aPos, nEndPos - nStartPos + 2);
1845 std::vector<sal_uInt16>::size_type i;
1846 for( i = 1; i < rNums.size(); ++i)
1848 SvxNumberType aType( rDescs[i]->GetNumType() );
1849 if( TOX_INDEX == SwTOXBase::GetType() )
1850 { // Summarize for the following
1851 // Add up all following
1852 // break up if main entry starts or ends and
1853 // insert a char style index
1854 bool bMainEntryChanges = lcl_HasMainEntry(pMainEntryNums, nOld)
1855 != lcl_HasMainEntry(pMainEntryNums, rNums[i]);
1857 if(nOld == rNums[i]-1 && !bMainEntryChanges &&
1858 (GetOptions() & (SwTOIOptions::FF|SwTOIOptions::Dash)))
1859 nCount++;
1860 else
1862 // Flush for the following old values
1863 if(GetOptions() & SwTOIOptions::FF)
1865 if ( nCount >= 1 )
1866 aNumStr += rIntl.GetFollowingText( nCount > 1 );
1868 else if (nCount) //#58127# If nCount == 0, then the only PageNumber is already in aNumStr!
1870 if (nCount == 1 )
1871 aNumStr += SwTOXMark::S_PAGE_DELI;
1872 else
1873 aNumStr += "-";
1875 aNumStr += aType.GetNumStr( nBeg + nCount );
1878 // Create new String
1879 nBeg = rNums[i];
1880 aNumStr += SwTOXMark::S_PAGE_DELI;
1881 //the change of the character style must apply after sPageDeli is appended
1882 if (xCharStyleIdx && bMainEntryChanges)
1884 xCharStyleIdx->push_back(aNumStr.getLength());
1886 aNumStr += aType.GetNumStr( nBeg );
1887 nCount = 0;
1889 nOld = rNums[i];
1891 else
1892 { // Insert all Numbers
1893 aNumStr += aType.GetNumStr( rNums[i] );
1894 if (i+1 != rNums.size())
1895 aNumStr += SwTOXMark::S_PAGE_DELI;
1898 // Flush when ending and the following old values
1899 if( TOX_INDEX == SwTOXBase::GetType() )
1901 if(GetOptions() & SwTOIOptions::FF)
1903 if( nCount >= 1 )
1904 aNumStr += rIntl.GetFollowingText( nCount > 1 );
1906 else
1908 if(nCount >= 2)
1909 aNumStr += "-";
1910 else if(nCount == 1)
1911 aNumStr += SwTOXMark::S_PAGE_DELI;
1912 //#58127# If nCount == 0, then the only PageNumber is already in aNumStr!
1913 if(nCount)
1914 aNumStr += rDescs[i-1]->GetNumType().GetNumStr( nBeg+nCount );
1917 pNd->InsertText( aNumStr, aPos, SwInsertFlags::EMPTYEXPAND | SwInsertFlags::FORCEHINTEXPAND );
1918 if(pPageNoCharFormat)
1920 SwFormatCharFormat aCharFormat( pPageNoCharFormat );
1921 pNd->InsertItem(aCharFormat, nStartPos, nStartPos + aNumStr.getLength(), SetAttrMode::DONTEXPAND);
1924 // The main entries should get their character style
1925 if (!xCharStyleIdx || xCharStyleIdx->empty() || GetMainEntryCharStyle().isEmpty())
1926 return;
1928 // eventually the last index must me appended
1929 if (xCharStyleIdx->size()&0x01)
1930 xCharStyleIdx->push_back(aNumStr.getLength());
1932 // search by name
1933 SwDoc& rDoc = pNd->GetDoc();
1934 sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( GetMainEntryCharStyle(), SwGetPoolIdFromName::ChrFmt );
1935 SwCharFormat* pCharFormat = nullptr;
1936 if(USHRT_MAX != nPoolId)
1937 pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(nPoolId);
1938 else
1939 pCharFormat = rDoc.FindCharFormatByName( GetMainEntryCharStyle() );
1940 if(!pCharFormat)
1941 pCharFormat = rDoc.MakeCharFormat(GetMainEntryCharStyle(), nullptr);
1943 // find the page numbers in aNumStr and set the character style
1944 sal_Int32 nOffset = pNd->GetText().getLength() - aNumStr.getLength();
1945 SwFormatCharFormat aCharFormat(pCharFormat);
1946 for (size_t j = 0; j < xCharStyleIdx->size(); j += 2)
1948 sal_Int32 nStartIdx = (*xCharStyleIdx)[j] + nOffset;
1949 sal_Int32 nEndIdx = (*xCharStyleIdx)[j + 1] + nOffset;
1950 pNd->InsertItem(aCharFormat, nStartIdx, nEndIdx, SetAttrMode::DONTEXPAND);
1954 void SwTOXBaseSection::InsertSorted(std::unique_ptr<SwTOXSortTabBase> pNew)
1956 Range aRange(0, m_aSortArr.size());
1957 if( TOX_INDEX == SwTOXBase::GetType() && pNew->pTextMark )
1959 const SwTOXMark& rMark = pNew->pTextMark->GetTOXMark();
1960 // Evaluate Key
1961 // Calculate the range where to insert
1962 if( !(GetOptions() & SwTOIOptions::KeyAsEntry) &&
1963 !rMark.GetPrimaryKey().isEmpty() )
1965 aRange = GetKeyRange( rMark.GetPrimaryKey(),
1966 rMark.GetPrimaryKeyReading(),
1967 *pNew, FORM_PRIMARY_KEY, aRange );
1969 if( !rMark.GetSecondaryKey().isEmpty() )
1970 aRange = GetKeyRange( rMark.GetSecondaryKey(),
1971 rMark.GetSecondaryKeyReading(),
1972 *pNew, FORM_SECONDARY_KEY, aRange );
1975 // Search for identical entries and remove the trailing one
1976 if(TOX_AUTHORITIES == SwTOXBase::GetType())
1978 for(short i = static_cast<short>(aRange.Min()); i < static_cast<short>(aRange.Max()); ++i)
1980 SwTOXSortTabBase* pOld = m_aSortArr[i].get();
1981 if (pOld->equivalent(*pNew))
1983 if (pOld->sort_lt(*pNew))
1985 return;
1987 else
1989 // remove the old content
1990 m_aSortArr.erase( m_aSortArr.begin() + i );
1991 aRange.Max()--;
1992 break;
1998 // find position and insert
1999 tools::Long i;
2001 for( i = aRange.Min(); i < aRange.Max(); ++i)
2002 { // Only check for same level
2003 SwTOXSortTabBase* pOld = m_aSortArr[i].get();
2004 if (pOld->equivalent(*pNew))
2006 if(TOX_AUTHORITIES != SwTOXBase::GetType())
2008 // Own entry for double entries or keywords
2009 if( pOld->GetType() == TOX_SORT_CUSTOM &&
2010 SwTOXSortTabBase::GetOptions() & SwTOIOptions::KeyAsEntry)
2011 continue;
2013 if(!(SwTOXSortTabBase::GetOptions() & SwTOIOptions::SameEntry))
2014 { // Own entry
2015 m_aSortArr.insert(m_aSortArr.begin() + i, std::move(pNew));
2016 return;
2018 // If the own entry is already present, add it to the references list
2019 pOld->aTOXSources.push_back(pNew->aTOXSources[0]);
2021 return;
2023 #if OSL_DEBUG_LEVEL > 0
2024 else
2025 OSL_FAIL("Bibliography entries cannot be found here");
2026 #endif
2028 if (pNew->sort_lt(*pOld))
2029 break;
2031 // Skip SubLevel
2032 while( TOX_INDEX == SwTOXBase::GetType() && i < aRange.Max() &&
2033 m_aSortArr[i]->GetLevel() > pNew->GetLevel() )
2034 i++;
2036 // Insert at position i
2037 m_aSortArr.insert(m_aSortArr.begin()+i, std::move(pNew));
2040 /// Find Key Range and insert if possible
2041 Range SwTOXBaseSection::GetKeyRange(const OUString& rStr, const OUString& rStrReading,
2042 const SwTOXSortTabBase& rNew,
2043 sal_uInt16 nLevel, const Range& rRange )
2045 const SwTOXInternational& rIntl = *rNew.pTOXIntl;
2046 TextAndReading aToCompare(rStr, rStrReading);
2048 if( SwTOIOptions::InitialCaps & GetOptions() )
2050 aToCompare.sText = rIntl.ToUpper( aToCompare.sText, 0 )
2051 + aToCompare.sText.subView(1);
2054 OSL_ENSURE(rRange.Min() >= 0 && rRange.Max() >= 0, "Min Max < 0");
2056 const tools::Long nMin = rRange.Min();
2057 const tools::Long nMax = rRange.Max();
2059 tools::Long i;
2061 for( i = nMin; i < nMax; ++i)
2063 SwTOXSortTabBase* pBase = m_aSortArr[i].get();
2065 if( rIntl.IsEqual( pBase->GetText(), pBase->GetLocale(),
2066 aToCompare, rNew.GetLocale() ) &&
2067 pBase->GetLevel() == nLevel )
2068 break;
2070 if(i == nMax)
2071 { // If not already present, create and insert
2072 std::unique_ptr<SwTOXCustom> pKey(MakeSwTOXSortTabBase<SwTOXCustom>(
2073 nullptr, aToCompare, nLevel, rIntl, rNew.GetLocale() ));
2074 for(i = nMin; i < nMax; ++i)
2076 if (nLevel == m_aSortArr[i]->GetLevel() && pKey->sort_lt(*m_aSortArr[i]))
2077 break;
2079 m_aSortArr.insert(m_aSortArr.begin() + i, std::move(pKey));
2081 const tools::Long nStart = i+1;
2082 const tools::Long nEnd = m_aSortArr.size();
2084 // Find end of range
2085 for(i = nStart; i < nEnd; ++i)
2087 if(m_aSortArr[i]->GetLevel() <= nLevel)
2089 return Range(nStart, i);
2092 return Range(nStart, nEnd);
2095 bool SwTOXBase::IsTOXBaseInReadonly() const
2097 const SwTOXBaseSection *pSect = dynamic_cast<const SwTOXBaseSection*>(this);
2098 if (!pSect || !pSect->GetFormat())
2099 return false;
2101 const SwSectionNode* pSectNode = pSect->GetFormat()->GetSectionNode();
2102 if (!pSectNode)
2103 return false;
2105 const SwDocShell* pDocSh = pSectNode->GetDoc().GetDocShell();
2106 if (!pDocSh)
2107 return false;
2109 if (pDocSh->IsReadOnly())
2110 return true;
2112 pSectNode = pSectNode->StartOfSectionNode()->FindSectionNode();
2113 if (!pSectNode)
2114 return false;
2116 return pSectNode->GetSection().IsProtectFlag();
2119 const SfxItemSet* SwTOXBase::GetAttrSet() const
2121 const SwTOXBaseSection *pSect = dynamic_cast<const SwTOXBaseSection*>(this);
2122 if(pSect && pSect->GetFormat())
2123 return &pSect->GetFormat()->GetAttrSet();
2124 return nullptr;
2127 void SwTOXBase::SetAttrSet( const SfxItemSet& rSet )
2129 SwTOXBaseSection *pSect = dynamic_cast<SwTOXBaseSection*>(this);
2130 if( pSect && pSect->GetFormat() )
2131 pSect->GetFormat()->SetFormatAttr( rSet );
2134 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */