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 <com/sun/star/text/ReferenceFieldPart.hpp> 21 #include <com/sun/star/text/ReferenceFieldSource.hpp> 22 #include <o3tl/unreachable.hxx> 23 #include <unotools/localedatawrapper.hxx> 24 #include <unotools/charclass.hxx> 25 #include <doc.hxx> 26 #include <IDocumentFieldsAccess.hxx> 27 #include <IDocumentLayoutAccess.hxx> 28 #include <IDocumentMarkAccess.hxx> 29 #include <pam.hxx> 30 #include <cntfrm.hxx> 31 #include <pagefrm.hxx> 32 #include <rootfrm.hxx> 33 #include <modeltoviewhelper.hxx> 34 #include <fmtfld.hxx> 35 #include <txtfld.hxx> 36 #include <txtftn.hxx> 37 #include <fmtrfmrk.hxx> 38 #include <txtrfmrk.hxx> 39 #include <fmtftn.hxx> 40 #include <ndtxt.hxx> 41 #include <chpfld.hxx> 42 #include <reffld.hxx> 43 #include <expfld.hxx> 44 #include <txtfrm.hxx> 45 #include <flyfrm.hxx> 46 #include <pagedesc.hxx> 47 #include <IMark.hxx> 48 #include <crossrefbookmark.hxx> 49 #include <ftnidx.hxx> 50 #include <utility> 51 #include <viewsh.hxx> 52 #include <unofldmid.h> 53 #include <SwStyleNameMapper.hxx> 54 #include <shellres.hxx> 55 #include <poolfmt.hxx> 56 #include <strings.hrc> 57 #include <numrule.hxx> 58 #include <SwNodeNum.hxx> 59 #include <calbck.hxx> 60 61 #include <cstddef> 62 #include <memory> 63 #include <vector> 64 #include <set> 65 #include <string_view> 66 #include <map> 67 #include <algorithm> 68 69 using namespace ::com::sun::star; 70 using namespace ::com::sun::star::text; 71 using namespace ::com::sun::star::lang; 72 73 static std::pair<OUString, bool> MakeRefNumStr(SwRootFrame const* pLayout, 74 const SwTextNode& rTextNodeOfField, 75 const SwTextNode& rTextNodeOfReferencedItem, 76 sal_uInt32 nRefNumFormat); 77 78 static void lcl_GetLayTree( const SwFrame* pFrame, std::vector<const SwFrame*>& rArr ) 79 { 80 while( pFrame ) 81 { 82 if( pFrame->IsBodyFrame() ) // unspectacular 83 pFrame = pFrame->GetUpper(); 84 else 85 { 86 rArr.push_back( pFrame ); 87 88 // this is the last page 89 if( pFrame->IsPageFrame() ) 90 break; 91 92 if( pFrame->IsFlyFrame() ) 93 pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame(); 94 else 95 pFrame = pFrame->GetUpper(); 96 } 97 } 98 } 99 100 bool IsFrameBehind( const SwTextNode& rMyNd, sal_Int32 nMySttPos, 101 const SwTextNode& rBehindNd, sal_Int32 nSttPos ) 102 { 103 const SwTextFrame * pMyFrame = static_cast<SwTextFrame*>(rMyNd.getLayoutFrame( 104 rMyNd.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)); 105 const SwTextFrame * pFrame = static_cast<SwTextFrame*>(rBehindNd.getLayoutFrame( 106 rBehindNd.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)); 107 108 if( !pFrame || !pMyFrame) 109 return false; 110 111 TextFrameIndex const nMySttPosIndex(pMyFrame->MapModelToView(&rMyNd, nMySttPos)); 112 TextFrameIndex const nSttPosIndex(pFrame->MapModelToView(&rBehindNd, nSttPos)); 113 while (pFrame && !pFrame->IsInside(nSttPosIndex)) 114 pFrame = pFrame->GetFollow(); 115 while (pMyFrame && !pMyFrame->IsInside(nMySttPosIndex)) 116 pMyFrame = pMyFrame->GetFollow(); 117 118 if( !pFrame || !pMyFrame || pFrame == pMyFrame ) 119 return false; 120 121 std::vector<const SwFrame*> aRefArr, aArr; 122 ::lcl_GetLayTree( pFrame, aRefArr ); 123 ::lcl_GetLayTree( pMyFrame, aArr ); 124 125 size_t nRefCnt = aRefArr.size() - 1, nCnt = aArr.size() - 1; 126 bool bVert = false; 127 bool bR2L = false; 128 129 // Loop as long as a frame does not equal? 130 while( nRefCnt && nCnt && aRefArr[ nRefCnt ] == aArr[ nCnt ] ) 131 { 132 const SwFrame* pTmpFrame = aArr[ nCnt ]; 133 bVert = pTmpFrame->IsVertical(); 134 bR2L = pTmpFrame->IsRightToLeft(); 135 --nCnt; 136 --nRefCnt; 137 } 138 139 // If a counter overflows? 140 if( aRefArr[ nRefCnt ] == aArr[ nCnt ] ) 141 { 142 if( nCnt ) 143 --nCnt; 144 else 145 --nRefCnt; 146 } 147 148 const SwFrame* pRefFrame = aRefArr[ nRefCnt ]; 149 const SwFrame* pFieldFrame = aArr[ nCnt ]; 150 151 // different frames, check their Y-/X-position 152 bool bRefIsLower = false; 153 if( ( SwFrameType::Column | SwFrameType::Cell ) & pFieldFrame->GetType() || 154 ( SwFrameType::Column | SwFrameType::Cell ) & pRefFrame->GetType() ) 155 { 156 if( pFieldFrame->GetType() == pRefFrame->GetType() ) 157 { 158 // here, the X-pos is more important 159 if( bVert ) 160 { 161 if( bR2L ) 162 bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || 163 ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && 164 pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() ); 165 else 166 bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || 167 ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && 168 pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() ); 169 } 170 else if( bR2L ) 171 bRefIsLower = pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() || 172 ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && 173 pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); 174 else 175 bRefIsLower = pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() || 176 ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && 177 pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); 178 pRefFrame = nullptr; 179 } 180 else if( ( SwFrameType::Column | SwFrameType::Cell ) & pFieldFrame->GetType() ) 181 pFieldFrame = aArr[ nCnt - 1 ]; 182 else 183 pRefFrame = aRefArr[ nRefCnt - 1 ]; 184 } 185 186 if( pRefFrame ) // misuse as flag 187 { 188 if( bVert ) 189 { 190 if( bR2L ) 191 bRefIsLower = pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() || 192 ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && 193 pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); 194 else 195 bRefIsLower = pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() || 196 ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && 197 pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); 198 } 199 else if( bR2L ) 200 bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || 201 ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && 202 pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() ); 203 else 204 bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || 205 ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && 206 pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() ); 207 } 208 return bRefIsLower; 209 } 210 211 // tdf#115319 create alternative reference formats, if the user asked for it 212 // (ReferenceFieldLanguage attribute of the reference field is not empty), and 213 // language of the text and ReferenceFieldLanguage are the same. 214 // Right now only HUNGARIAN seems to need this (as in the related issue, 215 // the reversed caption order in autocaption, solved by #i61007#) 216 static void lcl_formatReferenceLanguage( OUString& rRefText, 217 bool bClosingParenthesis, LanguageType eLang, 218 std::u16string_view rReferenceLanguage) 219 { 220 if (eLang != LANGUAGE_HUNGARIAN || (rReferenceLanguage != u"hu" && rReferenceLanguage != u"Hu")) 221 return; 222 223 // Add Hungarian definitive article (a/az) before references, 224 // similar to \aref, \apageref etc. of LaTeX Babel package. 225 // 226 // for example: 227 // 228 // "az 1. oldalon" ("on page 1"), but 229 // "a 2. oldalon" ("on page 2") 230 // "a fentebbi", "az alábbi" (above/below) 231 // "a Lorem", "az Ipsum" 232 // 233 // Support following numberings of EU publications: 234 // 235 // 1., 1a., a), (1), (1a), iii., III., IA. 236 // 237 // (http://publications.europa.eu/code/hu/hu-120700.htm, 238 // http://publications.europa.eu/code/hu/hu-4100600.htm) 239 240 CharClass aCharClass(( LanguageTag(eLang) )); 241 sal_Int32 nLen = rRefText.getLength(); 242 sal_Int32 i; 243 // substring of rRefText starting with letter or number 244 OUString sNumbering; 245 // is article "az"? 246 bool bArticleAz = false; 247 // is numbering a number? 248 bool bNum = false; 249 250 // search first member of the numbering (numbers or letters) 251 for (i=0; i<nLen && (sNumbering.isEmpty() || 252 ((bNum && aCharClass.isDigit(rRefText, i)) || 253 (!bNum && aCharClass.isLetter(rRefText, i)))); ++i) 254 { 255 // start of numbering within the field text 256 if (sNumbering.isEmpty() && aCharClass.isLetterNumeric(rRefText, i)) { 257 sNumbering = rRefText.copy(i); 258 bNum = aCharClass.isDigit(rRefText, i); 259 } 260 } 261 262 // length of numbering 263 nLen = i - (rRefText.getLength() - sNumbering.getLength()); 264 265 if (bNum) 266 { 267 // az 1, 1000, 1000000, 1000000000... 268 // az 5, 50, 500... 269 if ((sNumbering.startsWith("1") && (nLen == 1 || nLen == 4 || nLen == 7 || nLen == 10)) || 270 sNumbering.startsWith("5")) 271 bArticleAz = true; 272 } 273 else if (nLen == 1 && sNumbering[0] < 128) 274 { 275 // ASCII 1-letter numbering 276 // az a), e), f) ... x) 277 // az i., v. (but, a x.) 278 static const std::u16string_view sLettersStartingWithVowels = u"aefilmnorsuxyAEFILMNORSUXY"; 279 if (sLettersStartingWithVowels.find(sNumbering[0]) != std::u16string_view::npos) 280 { 281 // x), X) are letters, but x. and X. etc. are Roman numbers 282 if (bClosingParenthesis || 283 (sNumbering[0] != 'x' && sNumbering[0] != 'X')) 284 bArticleAz = true; 285 } else if ((sNumbering[0] == 'v' || sNumbering[0] == 'V') && !bClosingParenthesis) 286 // v), V) are letters, but v. and V. are Roman numbers 287 bArticleAz = true; 288 } 289 else 290 { 291 static const sal_Unicode sVowelsWithDiacritic[] = { 292 0x00E1, 0x00C1, 0x00E9, 0x00C9, 0x00ED, 0x00CD, 293 0x00F3, 0x00D3, 0x00F6, 0x00D6, 0x0151, 0x0150, 294 0x00FA, 0x00DA, 0x00FC, 0x00DC, 0x0171, 0x0170, 0 }; 295 static const OUString sVowels = OUString::Concat(u"aAeEiIoOuU") + sVowelsWithDiacritic; 296 297 // handle more than 1-letter long Roman numbers and 298 // their possible combinations with letters: 299 // az IA, a IIB, a IIIC., az Ia, a IIb., a iiic), az LVIII. szonett 300 bool bRomanNumber = false; 301 if (nLen > 1 && (nLen + 1 >= sNumbering.getLength() || sNumbering[nLen] == '.')) 302 { 303 sal_Unicode last = sNumbering[nLen - 1]; 304 OUString sNumberingTrim; 305 if ((last >= 'A' && last < 'I') || (last >= 'a' && last < 'i')) 306 sNumberingTrim = sNumbering.copy(0, nLen - 1); 307 else 308 sNumberingTrim = sNumbering.copy(0, nLen); 309 bRomanNumber = 310 sNumberingTrim.replaceAll("i", "").replaceAll("v", "").replaceAll("x", "").replaceAll("l", "").replaceAll("c", "").isEmpty() || 311 sNumberingTrim.replaceAll("I", "").replaceAll("V", "").replaceAll("X", "").replaceAll("L", "").replaceAll("C", "").isEmpty(); 312 } 313 314 if ( 315 // Roman number and a letter optionally 316 ( bRomanNumber && ( 317 (sNumbering[0] == 'i' && sNumbering[1] != 'i' && sNumbering[1] != 'v' && sNumbering[1] != 'x') || 318 (sNumbering[0] == 'I' && sNumbering[1] != 'I' && sNumbering[1] != 'V' && sNumbering[1] != 'X') || 319 (sNumbering[0] == 'v' && sNumbering[1] != 'i') || 320 (sNumbering[0] == 'V' && sNumbering[1] != 'I') || 321 (sNumbering[0] == 'l' && sNumbering[1] != 'x') || 322 (sNumbering[0] == 'L' && sNumbering[1] != 'X')) ) || 323 // a word starting with vowel (not Roman number) 324 ( !bRomanNumber && sVowels.indexOf(sNumbering[0]) != -1)) 325 { 326 bArticleAz = true; 327 } 328 } 329 // not a title text starting already with a definitive article 330 if ( sNumbering.startsWith("A ") || sNumbering.startsWith("Az ") || 331 sNumbering.startsWith("a ") || sNumbering.startsWith("az ") ) 332 return; 333 334 // lowercase, if rReferenceLanguage == "hu", not "Hu" 335 OUString sArticle; 336 337 if ( rReferenceLanguage == u"hu" ) 338 sArticle = "a"; 339 else 340 sArticle = "A"; 341 342 if (bArticleAz) 343 sArticle += "z"; 344 345 rRefText = sArticle + " " + rRefText; 346 } 347 348 /// get references 349 SwGetRefField::SwGetRefField( SwGetRefFieldType* pFieldType, 350 OUString aSetRef, OUString aSetReferenceLanguage, sal_uInt16 nSubTyp, 351 sal_uInt16 nSequenceNo, sal_uLong nFormat ) 352 : SwField( pFieldType, nFormat ), 353 m_sSetRefName( std::move(aSetRef) ), 354 m_sSetReferenceLanguage( std::move(aSetReferenceLanguage) ), 355 m_nSubType( nSubTyp ), 356 m_nSeqNo( nSequenceNo ) 357 { 358 } 359 360 SwGetRefField::~SwGetRefField() 361 { 362 } 363 364 OUString SwGetRefField::GetDescription() const 365 { 366 return SwResId(STR_REFERENCE); 367 } 368 369 sal_uInt16 SwGetRefField::GetSubType() const 370 { 371 return m_nSubType; 372 } 373 374 void SwGetRefField::SetSubType( sal_uInt16 n ) 375 { 376 m_nSubType = n; 377 } 378 379 // #i81002# 380 bool SwGetRefField::IsRefToHeadingCrossRefBookmark() const 381 { 382 return GetSubType() == REF_BOOKMARK && 383 ::sw::mark::CrossRefHeadingBookmark::IsLegalName(m_sSetRefName); 384 } 385 386 bool SwGetRefField::IsRefToNumItemCrossRefBookmark() const 387 { 388 return GetSubType() == REF_BOOKMARK && 389 ::sw::mark::CrossRefNumItemBookmark::IsLegalName(m_sSetRefName); 390 } 391 392 const SwTextNode* SwGetRefField::GetReferencedTextNode() const 393 { 394 SwGetRefFieldType *pTyp = dynamic_cast<SwGetRefFieldType*>(GetTyp()); 395 if (!pTyp) 396 return nullptr; 397 sal_Int32 nDummy = -1; 398 return SwGetRefFieldType::FindAnchor( &pTyp->GetDoc(), m_sSetRefName, m_nSubType, m_nSeqNo, &nDummy ); 399 } 400 401 // strikethrough for tooltips using Unicode combining character 402 static OUString lcl_formatStringByCombiningCharacter(std::u16string_view sText, const sal_Unicode cChar) 403 { 404 OUStringBuffer sRet(sText.size() * 2); 405 for (size_t i = 0; i < sText.size(); ++i) 406 { 407 sRet.append(sText[i]); 408 sRet.append(cChar); 409 } 410 return sRet.makeStringAndClear(); 411 } 412 413 // #i85090# 414 OUString SwGetRefField::GetExpandedTextOfReferencedTextNode( 415 SwRootFrame const& rLayout) const 416 { 417 const SwTextNode* pReferencedTextNode( GetReferencedTextNode() ); 418 if ( !pReferencedTextNode ) 419 return OUString(); 420 421 // show the referenced text without the deletions, but if the whole text was 422 // deleted, show the original text for the sake of the comfortable reviewing, 423 // but with Unicode strikethrough in the tooltip 424 OUString sRet = sw::GetExpandTextMerged(&rLayout, *pReferencedTextNode, true, false, ExpandMode::HideDeletions); 425 if ( sRet.isEmpty() ) 426 { 427 static const sal_Unicode cStrikethrough = u'\x0336'; 428 sRet = sw::GetExpandTextMerged(&rLayout, *pReferencedTextNode, true, false, ExpandMode(0)); 429 sRet = lcl_formatStringByCombiningCharacter( sRet, cStrikethrough ); 430 } 431 return sRet; 432 } 433 434 void SwGetRefField::SetExpand( const OUString& rStr ) 435 { 436 m_sText = rStr; 437 m_sTextRLHidden = rStr; 438 } 439 440 OUString SwGetRefField::ExpandImpl(SwRootFrame const*const pLayout) const 441 { 442 return pLayout && pLayout->IsHideRedlines() ? m_sTextRLHidden : m_sText; 443 } 444 445 OUString SwGetRefField::GetFieldName() const 446 { 447 const OUString aName = GetTyp()->GetName(); 448 if ( !aName.isEmpty() || !m_sSetRefName.isEmpty() ) 449 { 450 return aName + " " + m_sSetRefName; 451 } 452 return ExpandImpl(nullptr); 453 } 454 455 456 static void FilterText(OUString & rText, LanguageType const eLang, 457 std::u16string_view rSetReferenceLanguage) 458 { 459 // remove all special characters (replace them with blanks) 460 if (rText.isEmpty()) 461 return; 462 463 rText = rText.replaceAll(u"\u00ad", ""); 464 OUStringBuffer aBuf(rText); 465 const sal_Int32 l = aBuf.getLength(); 466 for (sal_Int32 i = 0; i < l; ++i) 467 { 468 if (aBuf[i] < ' ') 469 { 470 aBuf[i] = ' '; 471 } 472 else if (aBuf[i] == 0x2011) 473 { 474 aBuf[i] = '-'; 475 } 476 } 477 rText = aBuf.makeStringAndClear(); 478 if (!rSetReferenceLanguage.empty()) 479 { 480 lcl_formatReferenceLanguage(rText, false, eLang, rSetReferenceLanguage); 481 } 482 } 483 484 // #i81002# - parameter <pFieldTextAttr> added 485 void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr ) 486 { 487 m_sText.clear(); 488 m_sTextRLHidden.clear(); 489 490 SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc(); 491 // finding the reference target (the number) 492 sal_Int32 nNumStart = -1; 493 sal_Int32 nNumEnd = -1; 494 SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor( 495 &rDoc, m_sSetRefName, m_nSubType, m_nSeqNo, &nNumStart, &nNumEnd 496 ); 497 // not found? 498 if ( !pTextNd ) 499 { 500 m_sText = SwViewShell::GetShellRes()->aGetRefField_RefItemNotFound; 501 m_sTextRLHidden = m_sText; 502 return ; 503 } 504 505 SwRootFrame const* pLayout(nullptr); 506 SwRootFrame const* pLayoutRLHidden(nullptr); 507 for (SwRootFrame const*const pLay : rDoc.GetAllLayouts()) 508 { 509 if (pLay->IsHideRedlines()) 510 { 511 pLayoutRLHidden = pLay; 512 } 513 else 514 { 515 pLayout = pLay; 516 } 517 } 518 519 // where is the category name (e.g. "Illustration")? 520 const OUString aText = pTextNd->GetText(); 521 const sal_Int32 nCatStart = aText.indexOf(m_sSetRefName); 522 const bool bHasCat = nCatStart>=0; 523 const sal_Int32 nCatEnd = bHasCat ? nCatStart + m_sSetRefName.getLength() : -1; 524 525 // length of the referenced text 526 const sal_Int32 nLen = aText.getLength(); 527 528 // which format? 529 switch( GetFormat() ) 530 { 531 case REF_CONTENT: 532 case REF_ONLYNUMBER: 533 case REF_ONLYCAPTION: 534 case REF_ONLYSEQNO: 535 { 536 // needed part of Text 537 sal_Int32 nStart; 538 sal_Int32 nEnd; 539 540 switch( m_nSubType ) 541 { 542 case REF_SEQUENCEFLD: 543 544 switch( GetFormat() ) 545 { 546 // "Category and Number" 547 case REF_ONLYNUMBER: 548 if (bHasCat) { 549 nStart = std::min(nNumStart, nCatStart); 550 nEnd = std::max(nNumEnd, nCatEnd); 551 } else { 552 nStart = nNumStart; 553 nEnd = nNumEnd; 554 } 555 break; 556 557 // "Caption Text" 558 case REF_ONLYCAPTION: { 559 // next alphanumeric character after category+number 560 if (const SwTextAttr* const pTextAttr = 561 pTextNd->GetTextAttrForCharAt(nNumStart, RES_TXTATR_FIELD) 562 ) { 563 // start searching from nFrom 564 const sal_Int32 nFrom = bHasCat 565 ? std::max(nNumStart + 1, nCatEnd) 566 : nNumStart + 1; 567 nStart = SwGetExpField::GetReferenceTextPos( pTextAttr->GetFormatField(), rDoc, nFrom ); 568 } else { 569 nStart = bHasCat ? std::max(nNumEnd, nCatEnd) : nNumEnd; 570 } 571 nEnd = nLen; 572 break; 573 } 574 575 // "Numbering" 576 case REF_ONLYSEQNO: 577 nStart = nNumStart; 578 nEnd = std::min(nStart + 1, nLen); 579 break; 580 581 // "Reference" (whole Text) 582 case REF_CONTENT: 583 nStart = 0; 584 nEnd = nLen; 585 break; 586 587 default: 588 O3TL_UNREACHABLE; 589 } 590 break; 591 592 case REF_BOOKMARK: 593 nStart = nNumStart; 594 // text is spread across multiple nodes - get whole text or only until end of node? 595 nEnd = nNumEnd<0 ? nLen : nNumEnd; 596 break; 597 598 case REF_OUTLINE: 599 nStart = nNumStart; 600 nEnd = nNumEnd; 601 break; 602 603 case REF_FOOTNOTE: 604 case REF_ENDNOTE: 605 // get number or numString 606 for( size_t i = 0; i < rDoc.GetFootnoteIdxs().size(); ++i ) 607 { 608 SwTextFootnote* const pFootnoteIdx = rDoc.GetFootnoteIdxs()[i]; 609 if( m_nSeqNo == pFootnoteIdx->GetSeqRefNo() ) 610 { 611 m_sText = pFootnoteIdx->GetFootnote().GetViewNumStr(rDoc, nullptr); 612 m_sTextRLHidden = pFootnoteIdx->GetFootnote().GetViewNumStr(rDoc, pLayoutRLHidden); 613 if (!m_sSetReferenceLanguage.isEmpty()) 614 { 615 lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage); 616 lcl_formatReferenceLanguage(m_sTextRLHidden, false, GetLanguage(), m_sSetReferenceLanguage); 617 } 618 break; 619 } 620 } 621 return; 622 623 case REF_SETREFATTR: 624 nStart = nNumStart; 625 nEnd = nNumEnd; 626 break; 627 628 default: 629 O3TL_UNREACHABLE; 630 } 631 632 if( nStart != nEnd ) // a section? 633 { 634 m_sText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, false, ExpandMode::HideDeletions); 635 // show the referenced text without the deletions, but if the whole text was 636 // deleted, show the original text for the sake of the comfortable reviewing 637 // (with strikethrough in tooltip, see GetExpandedTextOfReferencedTextNode()) 638 if ( m_sText.isEmpty() ) 639 m_sText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, false, ExpandMode(0)); 640 641 if (m_nSubType == REF_OUTLINE 642 || (m_nSubType == REF_SEQUENCEFLD && REF_CONTENT == GetFormat())) 643 { 644 m_sTextRLHidden = sw::GetExpandTextMerged( 645 pLayoutRLHidden, *pTextNd, false, false, ExpandMode(0)); 646 } 647 else 648 { 649 m_sTextRLHidden = pTextNd->GetExpandText(pLayoutRLHidden, 650 nStart, nEnd - nStart, false, false, false, ExpandMode::HideDeletions); 651 } 652 FilterText(m_sText, GetLanguage(), m_sSetReferenceLanguage); 653 FilterText(m_sTextRLHidden, GetLanguage(), m_sSetReferenceLanguage); 654 } 655 } 656 break; 657 658 case REF_PAGE: 659 case REF_PAGE_PGDESC: 660 { 661 auto const func = 662 [this, pTextNd, nNumStart](OUString & rText, SwRootFrame const*const pLay) 663 { 664 SwTextFrame const* pFrame = static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLay, nullptr, nullptr)); 665 SwTextFrame const*const pSave = pFrame; 666 if (pFrame) 667 { 668 TextFrameIndex const nNumStartIndex(pFrame->MapModelToView(pTextNd, nNumStart)); 669 while (pFrame && !pFrame->IsInside(nNumStartIndex)) 670 pFrame = pFrame->GetFollow(); 671 } 672 673 if( pFrame || nullptr != ( pFrame = pSave )) 674 { 675 sal_uInt16 nPageNo = pFrame->GetVirtPageNum(); 676 const SwPageFrame *pPage; 677 if( REF_PAGE_PGDESC == GetFormat() && 678 nullptr != ( pPage = pFrame->FindPageFrame() ) && 679 pPage->GetPageDesc() ) 680 { 681 rText = pPage->GetPageDesc()->GetNumType().GetNumStr(nPageNo); 682 } 683 else 684 { 685 rText = OUString::number(nPageNo); 686 } 687 688 if (!m_sSetReferenceLanguage.isEmpty()) 689 lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage); 690 } 691 }; 692 // sw_redlinehide: currently only one of these layouts will exist, 693 // so the getLayoutFrame will use the same frame in both cases 694 func(m_sText, pLayout); 695 func(m_sTextRLHidden, pLayoutRLHidden); 696 } 697 break; 698 699 case REF_CHAPTER: 700 { 701 auto const func = 702 [this, pTextNd](OUString & rText, SwRootFrame const*const pLay) 703 { 704 // a bit tricky: search any frame 705 SwFrame const*const pFrame = pTextNd->getLayoutFrame(pLay); 706 if( pFrame ) 707 { 708 SwChapterFieldType aFieldTyp; 709 SwChapterField aField( &aFieldTyp, 0 ); 710 aField.SetLevel( MAXLEVEL - 1 ); 711 aField.ChangeExpansion( *pFrame, pTextNd, true ); 712 rText = aField.GetNumber(pLay); 713 714 if (!m_sSetReferenceLanguage.isEmpty()) 715 lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage); 716 } 717 }; 718 func(m_sText, pLayout); 719 func(m_sTextRLHidden, pLayoutRLHidden); 720 } 721 break; 722 723 case REF_UPDOWN: 724 { 725 // #i81002# 726 // simplified: use parameter <pFieldTextAttr> 727 if( !pFieldTextAttr || !pFieldTextAttr->GetpTextNode() ) 728 break; 729 730 LocaleDataWrapper aLocaleData(( LanguageTag( GetLanguage() ) )); 731 732 // first a "short" test - in case both are in the same node 733 if( pFieldTextAttr->GetpTextNode() == pTextNd ) 734 { 735 m_sText = nNumStart < pFieldTextAttr->GetStart() 736 ? aLocaleData.getAboveWord() 737 : aLocaleData.getBelowWord(); 738 m_sTextRLHidden = m_sText; 739 break; 740 } 741 742 m_sText = ::IsFrameBehind( *pFieldTextAttr->GetpTextNode(), pFieldTextAttr->GetStart(), 743 *pTextNd, nNumStart ) 744 ? aLocaleData.getAboveWord() 745 : aLocaleData.getBelowWord(); 746 747 if (!m_sSetReferenceLanguage.isEmpty()) 748 lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage); 749 750 m_sTextRLHidden = m_sText; 751 } 752 break; 753 // #i81002# 754 case REF_NUMBER: 755 case REF_NUMBER_NO_CONTEXT: 756 case REF_NUMBER_FULL_CONTEXT: 757 { 758 if ( pFieldTextAttr && pFieldTextAttr->GetpTextNode() ) 759 { 760 auto result = 761 MakeRefNumStr(pLayout, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat()); 762 m_sText = result.first; 763 // for differentiation of Roman numbers and letters in Hungarian article handling 764 bool bClosingParenthesis = result.second; 765 if (!m_sSetReferenceLanguage.isEmpty()) 766 { 767 lcl_formatReferenceLanguage(m_sText, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage); 768 } 769 result = 770 MakeRefNumStr(pLayoutRLHidden, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat()); 771 m_sTextRLHidden = result.first; 772 bClosingParenthesis = result.second; 773 if (!m_sSetReferenceLanguage.isEmpty()) 774 { 775 lcl_formatReferenceLanguage(m_sTextRLHidden, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage); 776 } 777 } 778 } 779 break; 780 781 default: 782 OSL_FAIL("<SwGetRefField::UpdateField(..)> - unknown format type"); 783 } 784 } 785 786 // #i81002# 787 static std::pair<OUString, bool> MakeRefNumStr( 788 SwRootFrame const*const pLayout, 789 const SwTextNode& i_rTextNodeOfField, 790 const SwTextNode& i_rTextNodeOfReferencedItem, 791 const sal_uInt32 nRefNumFormat) 792 { 793 SwTextNode const& rTextNodeOfField(pLayout 794 ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfField) 795 : i_rTextNodeOfField); 796 SwTextNode const& rTextNodeOfReferencedItem(pLayout 797 ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfReferencedItem) 798 : i_rTextNodeOfReferencedItem); 799 if ( rTextNodeOfReferencedItem.HasNumber(pLayout) && 800 rTextNodeOfReferencedItem.IsCountedInList() ) 801 { 802 OSL_ENSURE( rTextNodeOfReferencedItem.GetNum(pLayout), 803 "<SwGetRefField::MakeRefNumStr(..)> - referenced paragraph has number, but no <SwNodeNum> instance!" ); 804 805 // Determine, up to which level the superior list labels have to be 806 // included - default is to include all superior list labels. 807 int nRestrictInclToThisLevel( 0 ); 808 // Determine for format REF_NUMBER the level, up to which the superior 809 // list labels have to be restricted, if the text node of the reference 810 // field and the text node of the referenced item are in the same 811 // document context. 812 if ( nRefNumFormat == REF_NUMBER && 813 rTextNodeOfField.FindFlyStartNode() 814 == rTextNodeOfReferencedItem.FindFlyStartNode() && 815 rTextNodeOfField.FindFootnoteStartNode() 816 == rTextNodeOfReferencedItem.FindFootnoteStartNode() && 817 rTextNodeOfField.FindHeaderStartNode() 818 == rTextNodeOfReferencedItem.FindHeaderStartNode() && 819 rTextNodeOfField.FindFooterStartNode() 820 == rTextNodeOfReferencedItem.FindFooterStartNode() ) 821 { 822 const SwNodeNum* pNodeNumForTextNodeOfField( nullptr ); 823 if ( rTextNodeOfField.HasNumber(pLayout) && 824 rTextNodeOfField.GetNumRule() == rTextNodeOfReferencedItem.GetNumRule() ) 825 { 826 pNodeNumForTextNodeOfField = rTextNodeOfField.GetNum(pLayout); 827 } 828 else 829 { 830 pNodeNumForTextNodeOfField = 831 rTextNodeOfReferencedItem.GetNum(pLayout)->GetPrecedingNodeNumOf(rTextNodeOfField); 832 } 833 if ( pNodeNumForTextNodeOfField ) 834 { 835 const SwNumberTree::tNumberVector rFieldNumVec = 836 pNodeNumForTextNodeOfField->GetNumberVector(); 837 const SwNumberTree::tNumberVector rRefItemNumVec = 838 rTextNodeOfReferencedItem.GetNum()->GetNumberVector(); 839 std::size_t nLevel( 0 ); 840 while ( nLevel < rFieldNumVec.size() && nLevel < rRefItemNumVec.size() ) 841 { 842 if ( rRefItemNumVec[nLevel] == rFieldNumVec[nLevel] ) 843 { 844 nRestrictInclToThisLevel = nLevel + 1; 845 } 846 else 847 { 848 break; 849 } 850 ++nLevel; 851 } 852 } 853 } 854 855 // Determine, if superior list labels have to be included 856 const bool bInclSuperiorNumLabels( 857 ( nRestrictInclToThisLevel < rTextNodeOfReferencedItem.GetActualListLevel() && 858 ( nRefNumFormat == REF_NUMBER || nRefNumFormat == REF_NUMBER_FULL_CONTEXT ) ) ); 859 860 OSL_ENSURE( rTextNodeOfReferencedItem.GetNumRule(), 861 "<SwGetRefField::MakeRefNumStr(..)> - referenced numbered paragraph has no numbering rule set!" ); 862 return std::make_pair( 863 rTextNodeOfReferencedItem.GetNumRule()->MakeRefNumString( 864 *(rTextNodeOfReferencedItem.GetNum(pLayout)), 865 bInclSuperiorNumLabels, 866 nRestrictInclToThisLevel ), 867 rTextNodeOfReferencedItem.GetNumRule()->MakeNumString( 868 *(rTextNodeOfReferencedItem.GetNum(pLayout)), 869 true).endsWith(")") ); 870 } 871 872 return std::make_pair(OUString(), false); 873 } 874 875 std::unique_ptr<SwField> SwGetRefField::Copy() const 876 { 877 std::unique_ptr<SwGetRefField> pField( new SwGetRefField( static_cast<SwGetRefFieldType*>(GetTyp()), 878 m_sSetRefName, m_sSetReferenceLanguage, m_nSubType, 879 m_nSeqNo, GetFormat() ) ); 880 pField->m_sText = m_sText; 881 pField->m_sTextRLHidden = m_sTextRLHidden; 882 return std::unique_ptr<SwField>(pField.release()); 883 } 884 885 /// get reference name 886 OUString SwGetRefField::GetPar1() const 887 { 888 return m_sSetRefName; 889 } 890 891 /// set reference name 892 void SwGetRefField::SetPar1( const OUString& rName ) 893 { 894 m_sSetRefName = rName; 895 } 896 897 OUString SwGetRefField::GetPar2() const 898 { 899 return ExpandImpl(nullptr); 900 } 901 902 bool SwGetRefField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const 903 { 904 switch( nWhichId ) 905 { 906 case FIELD_PROP_USHORT1: 907 { 908 sal_Int16 nPart = 0; 909 switch(GetFormat()) 910 { 911 case REF_PAGE : nPart = ReferenceFieldPart::PAGE ; break; 912 case REF_CHAPTER : nPart = ReferenceFieldPart::CHAPTER ; break; 913 case REF_CONTENT : nPart = ReferenceFieldPart::TEXT ; break; 914 case REF_UPDOWN : nPart = ReferenceFieldPart::UP_DOWN ; break; 915 case REF_PAGE_PGDESC: nPart = ReferenceFieldPart::PAGE_DESC ; break; 916 case REF_ONLYNUMBER : nPart = ReferenceFieldPart::CATEGORY_AND_NUMBER ; break; 917 case REF_ONLYCAPTION: nPart = ReferenceFieldPart::ONLY_CAPTION ; break; 918 case REF_ONLYSEQNO : nPart = ReferenceFieldPart::ONLY_SEQUENCE_NUMBER; break; 919 // #i81002# 920 case REF_NUMBER: nPart = ReferenceFieldPart::NUMBER; break; 921 case REF_NUMBER_NO_CONTEXT: nPart = ReferenceFieldPart::NUMBER_NO_CONTEXT; break; 922 case REF_NUMBER_FULL_CONTEXT: nPart = ReferenceFieldPart::NUMBER_FULL_CONTEXT; break; 923 } 924 rAny <<= nPart; 925 } 926 break; 927 case FIELD_PROP_USHORT2: 928 { 929 sal_Int16 nSource = 0; 930 switch(m_nSubType) 931 { 932 case REF_SETREFATTR : nSource = ReferenceFieldSource::REFERENCE_MARK; break; 933 case REF_SEQUENCEFLD: nSource = ReferenceFieldSource::SEQUENCE_FIELD; break; 934 case REF_BOOKMARK : nSource = ReferenceFieldSource::BOOKMARK; break; 935 case REF_OUTLINE : OSL_FAIL("not implemented"); break; 936 case REF_FOOTNOTE : nSource = ReferenceFieldSource::FOOTNOTE; break; 937 case REF_ENDNOTE : nSource = ReferenceFieldSource::ENDNOTE; break; 938 } 939 rAny <<= nSource; 940 } 941 break; 942 case FIELD_PROP_PAR1: 943 { 944 OUString sTmp(GetPar1()); 945 if(REF_SEQUENCEFLD == m_nSubType) 946 { 947 sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( sTmp, SwGetPoolIdFromName::TxtColl ); 948 switch( nPoolId ) 949 { 950 case RES_POOLCOLL_LABEL_ABB: 951 case RES_POOLCOLL_LABEL_TABLE: 952 case RES_POOLCOLL_LABEL_FRAME: 953 case RES_POOLCOLL_LABEL_DRAWING: 954 case RES_POOLCOLL_LABEL_FIGURE: 955 SwStyleNameMapper::FillProgName(nPoolId, sTmp) ; 956 break; 957 } 958 } 959 rAny <<= sTmp; 960 } 961 break; 962 case FIELD_PROP_PAR3: 963 rAny <<= ExpandImpl(nullptr); 964 break; 965 case FIELD_PROP_PAR4: 966 rAny <<= m_sSetReferenceLanguage; 967 break; 968 case FIELD_PROP_SHORT1: 969 rAny <<= static_cast<sal_Int16>(m_nSeqNo); 970 break; 971 default: 972 assert(false); 973 } 974 return true; 975 } 976 977 bool SwGetRefField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) 978 { 979 switch( nWhichId ) 980 { 981 case FIELD_PROP_USHORT1: 982 { 983 sal_Int16 nPart = 0; 984 rAny >>= nPart; 985 switch(nPart) 986 { 987 case ReferenceFieldPart::PAGE: nPart = REF_PAGE; break; 988 case ReferenceFieldPart::CHAPTER: nPart = REF_CHAPTER; break; 989 case ReferenceFieldPart::TEXT: nPart = REF_CONTENT; break; 990 case ReferenceFieldPart::UP_DOWN: nPart = REF_UPDOWN; break; 991 case ReferenceFieldPart::PAGE_DESC: nPart = REF_PAGE_PGDESC; break; 992 case ReferenceFieldPart::CATEGORY_AND_NUMBER: nPart = REF_ONLYNUMBER; break; 993 case ReferenceFieldPart::ONLY_CAPTION: nPart = REF_ONLYCAPTION; break; 994 case ReferenceFieldPart::ONLY_SEQUENCE_NUMBER : nPart = REF_ONLYSEQNO; break; 995 // #i81002# 996 case ReferenceFieldPart::NUMBER: nPart = REF_NUMBER; break; 997 case ReferenceFieldPart::NUMBER_NO_CONTEXT: nPart = REF_NUMBER_NO_CONTEXT; break; 998 case ReferenceFieldPart::NUMBER_FULL_CONTEXT: nPart = REF_NUMBER_FULL_CONTEXT; break; 999 default: return false; 1000 } 1001 SetFormat(nPart); 1002 } 1003 break; 1004 case FIELD_PROP_USHORT2: 1005 { 1006 sal_Int16 nSource = 0; 1007 rAny >>= nSource; 1008 switch(nSource) 1009 { 1010 case ReferenceFieldSource::REFERENCE_MARK : m_nSubType = REF_SETREFATTR ; break; 1011 case ReferenceFieldSource::SEQUENCE_FIELD : 1012 { 1013 if(REF_SEQUENCEFLD == m_nSubType) 1014 break; 1015 m_nSubType = REF_SEQUENCEFLD; 1016 ConvertProgrammaticToUIName(); 1017 } 1018 break; 1019 case ReferenceFieldSource::BOOKMARK : m_nSubType = REF_BOOKMARK ; break; 1020 case ReferenceFieldSource::FOOTNOTE : m_nSubType = REF_FOOTNOTE ; break; 1021 case ReferenceFieldSource::ENDNOTE : m_nSubType = REF_ENDNOTE ; break; 1022 } 1023 } 1024 break; 1025 case FIELD_PROP_PAR1: 1026 { 1027 OUString sTmpStr; 1028 rAny >>= sTmpStr; 1029 SetPar1(sTmpStr); 1030 ConvertProgrammaticToUIName(); 1031 } 1032 break; 1033 case FIELD_PROP_PAR3: 1034 { 1035 OUString sTmpStr; 1036 rAny >>= sTmpStr; 1037 SetExpand( sTmpStr ); 1038 } 1039 break; 1040 case FIELD_PROP_PAR4: 1041 rAny >>= m_sSetReferenceLanguage; 1042 break; 1043 case FIELD_PROP_SHORT1: 1044 { 1045 sal_Int16 nSetSeq = 0; 1046 rAny >>= nSetSeq; 1047 if(nSetSeq >= 0) 1048 m_nSeqNo = nSetSeq; 1049 } 1050 break; 1051 default: 1052 assert(false); 1053 } 1054 return true; 1055 } 1056 1057 void SwGetRefField::ConvertProgrammaticToUIName() 1058 { 1059 if(!(GetTyp() && REF_SEQUENCEFLD == m_nSubType)) 1060 return; 1061 1062 SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc(); 1063 const OUString rPar1 = GetPar1(); 1064 // don't convert when the name points to an existing field type 1065 if (rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, rPar1, false)) 1066 return; 1067 1068 sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromProgName( rPar1, SwGetPoolIdFromName::TxtColl ); 1069 TranslateId pResId; 1070 switch( nPoolId ) 1071 { 1072 case RES_POOLCOLL_LABEL_ABB: 1073 pResId = STR_POOLCOLL_LABEL_ABB; 1074 break; 1075 case RES_POOLCOLL_LABEL_TABLE: 1076 pResId = STR_POOLCOLL_LABEL_TABLE; 1077 break; 1078 case RES_POOLCOLL_LABEL_FRAME: 1079 pResId = STR_POOLCOLL_LABEL_FRAME; 1080 break; 1081 case RES_POOLCOLL_LABEL_DRAWING: 1082 pResId = STR_POOLCOLL_LABEL_DRAWING; 1083 break; 1084 case RES_POOLCOLL_LABEL_FIGURE: 1085 pResId = STR_POOLCOLL_LABEL_FIGURE; 1086 break; 1087 } 1088 if (pResId) 1089 SetPar1(SwResId(pResId)); 1090 } 1091 1092 SwGetRefFieldType::SwGetRefFieldType( SwDoc& rDc ) 1093 : SwFieldType( SwFieldIds::GetRef ), m_rDoc( rDc ) 1094 {} 1095 1096 std::unique_ptr<SwFieldType> SwGetRefFieldType::Copy() const 1097 { 1098 return std::make_unique<SwGetRefFieldType>( m_rDoc ); 1099 } 1100 1101 void SwGetRefFieldType::UpdateGetReferences() 1102 { 1103 std::vector<SwFormatField*> vFields; 1104 GatherFields(vFields, false); 1105 for(auto pFormatField: vFields) 1106 { 1107 // update only the GetRef fields 1108 //JP 3.4.2001: Task 71231 - we need the correct language 1109 SwGetRefField* pGRef = static_cast<SwGetRefField*>(pFormatField->GetField()); 1110 const SwTextField* pTField; 1111 if(!pGRef->GetLanguage() && 1112 nullptr != (pTField = pFormatField->GetTextField()) && 1113 pTField->GetpTextNode()) 1114 { 1115 pGRef->SetLanguage(pTField->GetpTextNode()->GetLang(pTField->GetStart())); 1116 } 1117 1118 // #i81002# 1119 pGRef->UpdateField(pFormatField->GetTextField()); 1120 } 1121 CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr)); 1122 } 1123 1124 void SwGetRefFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint) 1125 { 1126 if (rHint.GetId() != SfxHintId::SwLegacyModify) 1127 return; 1128 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint); 1129 if(!pLegacy->m_pNew && !pLegacy->m_pOld) 1130 // update to all GetReference fields 1131 // hopefully, this codepath is soon dead code, and 1132 // UpdateGetReferences gets only called directly 1133 UpdateGetReferences(); 1134 else 1135 // forward to text fields, they "expand" the text 1136 CallSwClientNotify(rHint); 1137 } 1138 1139 namespace sw { 1140 1141 bool IsMarkHintHidden(SwRootFrame const& rLayout, 1142 SwTextNode const& rNode, SwTextAttrEnd const& rHint) 1143 { 1144 if (!rLayout.HasMergedParas()) 1145 { 1146 return false; 1147 } 1148 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>( 1149 rNode.getLayoutFrame(&rLayout))); 1150 if (!pFrame) 1151 { 1152 return true; 1153 } 1154 sal_Int32 const*const pEnd(rHint.GetEnd()); 1155 if (pEnd) 1156 { 1157 return pFrame->MapModelToView(&rNode, rHint.GetStart()) 1158 == pFrame->MapModelToView(&rNode, *pEnd); 1159 } 1160 else 1161 { 1162 assert(rHint.HasDummyChar()); 1163 return pFrame->MapModelToView(&rNode, rHint.GetStart()) 1164 == pFrame->MapModelToView(&rNode, rHint.GetStart() + 1); 1165 } 1166 } 1167 1168 } // namespace sw 1169 1170 SwTextNode* SwGetRefFieldType::FindAnchor( SwDoc* pDoc, const OUString& rRefMark, 1171 sal_uInt16 nSubType, sal_uInt16 nSeqNo, 1172 sal_Int32* pStt, sal_Int32* pEnd, 1173 SwRootFrame const*const pLayout) 1174 { 1175 OSL_ENSURE( pStt, "Why did no one check the StartPos?" ); 1176 1177 IDocumentRedlineAccess & rIDRA(pDoc->getIDocumentRedlineAccess()); 1178 SwTextNode* pTextNd = nullptr; 1179 switch( nSubType ) 1180 { 1181 case REF_SETREFATTR: 1182 { 1183 const SwFormatRefMark *pRef = pDoc->GetRefMark( rRefMark ); 1184 SwTextRefMark const*const pRefMark(pRef ? pRef->GetTextRefMark() : nullptr); 1185 if (pRefMark && (!pLayout || !sw::IsMarkHintHidden(*pLayout, 1186 pRefMark->GetTextNode(), *pRefMark))) 1187 { 1188 pTextNd = const_cast<SwTextNode*>(&pRef->GetTextRefMark()->GetTextNode()); 1189 *pStt = pRef->GetTextRefMark()->GetStart(); 1190 if( pEnd ) 1191 *pEnd = pRef->GetTextRefMark()->GetAnyEnd(); 1192 } 1193 } 1194 break; 1195 1196 case REF_SEQUENCEFLD: 1197 { 1198 SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp, rRefMark, false ); 1199 if( pFieldType && pFieldType->HasWriterListeners() && 1200 nsSwGetSetExpType::GSE_SEQ & static_cast<SwSetExpFieldType*>(pFieldType)->GetType() ) 1201 { 1202 std::vector<SwFormatField*> vFields; 1203 pFieldType->GatherFields(vFields, false); 1204 for(auto pFormatField: vFields) 1205 { 1206 SwTextField *const pTextField(pFormatField->GetTextField()); 1207 if (pTextField && nSeqNo == 1208 static_cast<SwSetExpField*>(pFormatField->GetField())->GetSeqNumber() 1209 && (!pLayout || !pLayout->IsHideRedlines() 1210 || !sw::IsFieldDeletedInModel(rIDRA, *pTextField))) 1211 { 1212 pTextNd = pTextField->GetpTextNode(); 1213 *pStt = pTextField->GetStart(); 1214 if( pEnd ) 1215 *pEnd = (*pStt) + 1; 1216 break; 1217 } 1218 } 1219 } 1220 } 1221 break; 1222 1223 case REF_BOOKMARK: 1224 { 1225 IDocumentMarkAccess::const_iterator_t ppMark = pDoc->getIDocumentMarkAccess()->findMark(rRefMark); 1226 if (ppMark != pDoc->getIDocumentMarkAccess()->getAllMarksEnd() 1227 && (!pLayout || !pLayout->IsHideRedlines() 1228 || !sw::IsMarkHidden(*pLayout, **ppMark))) 1229 { 1230 const ::sw::mark::IMark* pBkmk = *ppMark; 1231 const SwPosition* pPos = &pBkmk->GetMarkStart(); 1232 1233 pTextNd = pPos->GetNode().GetTextNode(); 1234 *pStt = pPos->GetContentIndex(); 1235 if(pEnd) 1236 { 1237 if(!pBkmk->IsExpanded()) 1238 { 1239 *pEnd = *pStt; 1240 // #i81002# 1241 if(dynamic_cast< ::sw::mark::CrossRefBookmark const *>(pBkmk)) 1242 { 1243 OSL_ENSURE( pTextNd, 1244 "<SwGetRefFieldType::FindAnchor(..)> - node marked by cross-reference bookmark isn't a text node --> crash" ); 1245 *pEnd = pTextNd->Len(); 1246 } 1247 } 1248 else if(pBkmk->GetOtherMarkPos().GetNode() == pBkmk->GetMarkPos().GetNode()) 1249 *pEnd = pBkmk->GetMarkEnd().GetContentIndex(); 1250 else 1251 *pEnd = -1; 1252 } 1253 } 1254 } 1255 break; 1256 1257 case REF_OUTLINE: 1258 break; 1259 1260 case REF_FOOTNOTE: 1261 case REF_ENDNOTE: 1262 { 1263 for( auto pFootnoteIdx : pDoc->GetFootnoteIdxs() ) 1264 if( nSeqNo == pFootnoteIdx->GetSeqRefNo() ) 1265 { 1266 if (pLayout && pLayout->IsHideRedlines() 1267 && sw::IsFootnoteDeleted(rIDRA, *pFootnoteIdx)) 1268 { 1269 return nullptr; 1270 } 1271 // otherwise: the position at the start of the footnote 1272 // will be mapped to something visible at least... 1273 const SwNodeIndex* pIdx = pFootnoteIdx->GetStartNode(); 1274 if( pIdx ) 1275 { 1276 SwNodeIndex aIdx( *pIdx, 1 ); 1277 pTextNd = aIdx.GetNode().GetTextNode(); 1278 if( nullptr == pTextNd ) 1279 pTextNd = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext( &aIdx )); 1280 } 1281 *pStt = 0; 1282 if( pEnd ) 1283 *pEnd = 0; 1284 break; 1285 } 1286 } 1287 break; 1288 } 1289 1290 return pTextNd; 1291 } 1292 1293 namespace { 1294 1295 struct RefIdsMap 1296 { 1297 private: 1298 OUString aName; 1299 std::set<sal_uInt16> aIds; 1300 std::set<sal_uInt16> aDstIds; 1301 std::map<sal_uInt16, sal_uInt16> sequencedIds; /// ID numbers sorted by sequence number. 1302 bool bInit; 1303 1304 void Init(SwDoc& rDoc, SwDoc& rDestDoc, bool bField ); 1305 static void GetNoteIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds ); 1306 void GetFieldIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds ); 1307 void AddId( sal_uInt16 id, sal_uInt16 seqNum ); 1308 static sal_uInt16 GetFirstUnusedId( std::set<sal_uInt16> &rIds ); 1309 1310 public: 1311 explicit RefIdsMap( OUString _aName ) : aName(std::move( _aName )), bInit( false ) {} 1312 1313 void Check( SwDoc& rDoc, SwDoc& rDestDoc, SwGetRefField& rField, bool bField ); 1314 1315 const OUString& GetName() const { return aName; } 1316 }; 1317 1318 } 1319 1320 /// Get a sorted list of the field IDs from a document. 1321 /// @param[in] rDoc The document to search. 1322 /// @param[in,out] rIds The list of IDs found in the document. 1323 void RefIdsMap::GetFieldIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds) 1324 { 1325 SwFieldType *const pType = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, aName, false); 1326 if (!pType) 1327 return; 1328 std::vector<SwFormatField*> vFields; 1329 pType->GatherFields(vFields); 1330 for(const auto pF: vFields) 1331 rIds.insert(static_cast<SwSetExpField const*>(pF->GetField())->GetSeqNumber()); 1332 } 1333 1334 /// Get a sorted list of the footnote/endnote IDs from a document. 1335 /// @param[in] rDoc The document to search. 1336 /// @param[in,out] rIds The list of IDs found in the document. 1337 void RefIdsMap::GetNoteIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds) 1338 { 1339 for( auto n = rDoc.GetFootnoteIdxs().size(); n; ) 1340 rIds.insert( rDoc.GetFootnoteIdxs()[ --n ]->GetSeqRefNo() ); 1341 } 1342 1343 /// Initialise the aIds and aDestIds collections from the source documents. 1344 /// @param[in] rDoc The source document. 1345 /// @param[in] rDestDoc The destination document. 1346 /// @param[in] bField True if we're interested in all fields, false for footnotes. 1347 void RefIdsMap::Init( SwDoc& rDoc, SwDoc& rDestDoc, bool bField ) 1348 { 1349 if( bInit ) 1350 return; 1351 1352 if( bField ) 1353 { 1354 GetFieldIdsFromDoc( rDestDoc, aIds ); 1355 GetFieldIdsFromDoc( rDoc, aDstIds ); 1356 1357 // Map all the new src fields to the next available unused id 1358 for (const auto& rId : aDstIds) 1359 AddId( GetFirstUnusedId(aIds), rId ); 1360 1361 // Change the Sequence number of all SetExp fields in the source document 1362 SwFieldType* pType = rDoc.getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp, aName, false ); 1363 if(pType) 1364 { 1365 std::vector<SwFormatField*> vFields; 1366 pType->GatherFields(vFields, false); 1367 for(auto pF: vFields) 1368 { 1369 if(!pF->GetTextField()) 1370 continue; 1371 SwSetExpField *const pSetExp(static_cast<SwSetExpField *>(pF->GetField())); 1372 sal_uInt16 const n = pSetExp->GetSeqNumber(); 1373 pSetExp->SetSeqNumber(sequencedIds[n]); 1374 } 1375 } 1376 } 1377 else 1378 { 1379 GetNoteIdsFromDoc( rDestDoc, aIds ); 1380 GetNoteIdsFromDoc( rDoc, aDstIds ); 1381 1382 for (const auto& rId : aDstIds) 1383 AddId( GetFirstUnusedId(aIds), rId ); 1384 1385 // Change the footnotes/endnotes in the source doc to the new ID 1386 for ( const auto pFootnoteIdx : rDoc.GetFootnoteIdxs() ) 1387 { 1388 sal_uInt16 const n = pFootnoteIdx->GetSeqRefNo(); 1389 pFootnoteIdx->SetSeqNo(sequencedIds[n]); 1390 } 1391 } 1392 bInit = true; 1393 } 1394 1395 /// Get the lowest number unused in the passed set. 1396 /// @param[in] rIds The set of used ID numbers. 1397 /// @returns The lowest number unused by the passed set 1398 sal_uInt16 RefIdsMap::GetFirstUnusedId( std::set<sal_uInt16> &rIds ) 1399 { 1400 sal_uInt16 num(0); 1401 1402 for( const auto& rId : rIds ) 1403 { 1404 if( num != rId ) 1405 { 1406 return num; 1407 } 1408 ++num; 1409 } 1410 return num; 1411 } 1412 1413 /// Add a new ID and sequence number to the "occupied" collection. 1414 /// @param[in] id The ID number. 1415 /// @param[in] seqNum The sequence number. 1416 void RefIdsMap::AddId( sal_uInt16 id, sal_uInt16 seqNum ) 1417 { 1418 aIds.insert( id ); 1419 sequencedIds[ seqNum ] = id; 1420 } 1421 1422 void RefIdsMap::Check( SwDoc& rDoc, SwDoc& rDestDoc, SwGetRefField& rField, 1423 bool bField ) 1424 { 1425 Init( rDoc, rDestDoc, bField); 1426 1427 sal_uInt16 const nSeqNo = rField.GetSeqNo(); 1428 1429 // check if it needs to be remapped 1430 // if sequencedIds doesn't contain the number, it means there is no 1431 // SetExp field / footnote in the source document: do not modify 1432 // the number, which works well for copy from/paste to same document 1433 // (and if it is not the same document, there's no "correct" result anyway) 1434 if (sequencedIds.count(nSeqNo)) 1435 { 1436 rField.SetSeqNo( sequencedIds[nSeqNo] ); 1437 } 1438 } 1439 1440 /// 1. if _both_ SetExp + GetExp / Footnote + GetExp field are copied, 1441 /// ensure that both get a new unused matching number 1442 /// 2. if only SetExp / Footnote is copied, it gets a new unused number 1443 /// 3. if only GetExp field is copied, for the case of copy from / paste to 1444 /// same document it's desirable to keep the same number; 1445 /// for other cases of copy/paste or master documents it's not obvious 1446 /// what is most desirable since it's going to be wrong anyway 1447 void SwGetRefFieldType::MergeWithOtherDoc( SwDoc& rDestDoc ) 1448 { 1449 if (&rDestDoc == &m_rDoc) 1450 return; 1451 1452 if (rDestDoc.IsClipBoard()) 1453 { 1454 // when copying _to_ clipboard, expectation is that no fields exist 1455 // so no re-mapping is required to avoid collisions 1456 assert(!rDestDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef)->HasWriterListeners()); 1457 return; // don't modify the fields in the source doc 1458 } 1459 1460 // then there are RefFields in the DescDox - so all RefFields in the SourceDoc 1461 // need to be converted to have unique IDs for both documents 1462 RefIdsMap aFntMap { OUString() }; 1463 std::vector<std::unique_ptr<RefIdsMap>> aFieldMap; 1464 1465 std::vector<SwFormatField*> vFields; 1466 GatherFields(vFields); 1467 for(auto pField: vFields) 1468 { 1469 SwGetRefField& rRefField = *static_cast<SwGetRefField*>(pField->GetField()); 1470 switch( rRefField.GetSubType() ) 1471 { 1472 case REF_SEQUENCEFLD: 1473 { 1474 RefIdsMap* pMap = nullptr; 1475 for( auto n = aFieldMap.size(); n; ) 1476 { 1477 if (aFieldMap[ --n ]->GetName() == rRefField.GetSetRefName()) 1478 { 1479 pMap = aFieldMap[ n ].get(); 1480 break; 1481 } 1482 } 1483 if( !pMap ) 1484 { 1485 pMap = new RefIdsMap( rRefField.GetSetRefName() ); 1486 aFieldMap.push_back(std::unique_ptr<RefIdsMap>(pMap)); 1487 } 1488 1489 pMap->Check(m_rDoc, rDestDoc, rRefField, true); 1490 } 1491 break; 1492 1493 case REF_FOOTNOTE: 1494 case REF_ENDNOTE: 1495 aFntMap.Check(m_rDoc, rDestDoc, rRefField, false); 1496 break; 1497 } 1498 } 1499 } 1500 1501 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1502
