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