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