nss: upgrade to release 3.73
[LibreOffice.git] / sw / source / core / fields / reffld.cxx
blobd9f9e361bc6577ab4ae33f008ff52a6000a39ab8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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 <viewsh.hxx>
51 #include <unofldmid.h>
52 #include <SwStyleNameMapper.hxx>
53 #include <shellres.hxx>
54 #include <poolfmt.hxx>
55 #include <strings.hrc>
56 #include <numrule.hxx>
57 #include <SwNodeNum.hxx>
58 #include <calbck.hxx>
60 #include <cstddef>
61 #include <memory>
62 #include <vector>
63 #include <set>
64 #include <string_view>
65 #include <map>
66 #include <algorithm>
68 using namespace ::com::sun::star;
69 using namespace ::com::sun::star::text;
70 using namespace ::com::sun::star::lang;
72 static std::pair<OUString, bool> MakeRefNumStr(SwRootFrame const* pLayout,
73 const SwTextNode& rTextNodeOfField,
74 const SwTextNode& rTextNodeOfReferencedItem,
75 sal_uInt32 nRefNumFormat);
77 static void lcl_GetLayTree( const SwFrame* pFrame, std::vector<const SwFrame*>& rArr )
79 while( pFrame )
81 if( pFrame->IsBodyFrame() ) // unspectacular
82 pFrame = pFrame->GetUpper();
83 else
85 rArr.push_back( pFrame );
87 // this is the last page
88 if( pFrame->IsPageFrame() )
89 break;
91 if( pFrame->IsFlyFrame() )
92 pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
93 else
94 pFrame = pFrame->GetUpper();
99 bool IsFrameBehind( const SwTextNode& rMyNd, sal_Int32 nMySttPos,
100 const SwTextNode& rBehindNd, sal_Int32 nSttPos )
102 const SwTextFrame * pMyFrame = static_cast<SwTextFrame*>(rMyNd.getLayoutFrame(
103 rMyNd.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr));
104 const SwTextFrame * pFrame = static_cast<SwTextFrame*>(rBehindNd.getLayoutFrame(
105 rBehindNd.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr));
107 if( !pFrame || !pMyFrame)
108 return false;
110 TextFrameIndex const nMySttPosIndex(pMyFrame->MapModelToView(&rMyNd, nMySttPos));
111 TextFrameIndex const nSttPosIndex(pFrame->MapModelToView(&rBehindNd, nSttPos));
112 while (pFrame && !pFrame->IsInside(nSttPosIndex))
113 pFrame = pFrame->GetFollow();
114 while (pMyFrame && !pMyFrame->IsInside(nMySttPosIndex))
115 pMyFrame = pMyFrame->GetFollow();
117 if( !pFrame || !pMyFrame || pFrame == pMyFrame )
118 return false;
120 std::vector<const SwFrame*> aRefArr, aArr;
121 ::lcl_GetLayTree( pFrame, aRefArr );
122 ::lcl_GetLayTree( pMyFrame, aArr );
124 size_t nRefCnt = aRefArr.size() - 1, nCnt = aArr.size() - 1;
125 bool bVert = false;
126 bool bR2L = false;
128 // Loop as long as a frame does not equal?
129 while( nRefCnt && nCnt && aRefArr[ nRefCnt ] == aArr[ nCnt ] )
131 const SwFrame* pTmpFrame = aArr[ nCnt ];
132 bVert = pTmpFrame->IsVertical();
133 bR2L = pTmpFrame->IsRightToLeft();
134 --nCnt;
135 --nRefCnt;
138 // If a counter overflows?
139 if( aRefArr[ nRefCnt ] == aArr[ nCnt ] )
141 if( nCnt )
142 --nCnt;
143 else
144 --nRefCnt;
147 const SwFrame* pRefFrame = aRefArr[ nRefCnt ];
148 const SwFrame* pFieldFrame = aArr[ nCnt ];
150 // different frames, check their Y-/X-position
151 bool bRefIsLower = false;
152 if( ( SwFrameType::Column | SwFrameType::Cell ) & pFieldFrame->GetType() ||
153 ( SwFrameType::Column | SwFrameType::Cell ) & pRefFrame->GetType() )
155 if( pFieldFrame->GetType() == pRefFrame->GetType() )
157 // here, the X-pos is more important
158 if( bVert )
160 if( bR2L )
161 bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ||
162 ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() &&
163 pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() );
164 else
165 bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ||
166 ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() &&
167 pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() );
169 else if( bR2L )
170 bRefIsLower = pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() ||
171 ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() &&
172 pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() );
173 else
174 bRefIsLower = pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() ||
175 ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() &&
176 pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() );
177 pRefFrame = nullptr;
179 else if( ( SwFrameType::Column | SwFrameType::Cell ) & pFieldFrame->GetType() )
180 pFieldFrame = aArr[ nCnt - 1 ];
181 else
182 pRefFrame = aRefArr[ nRefCnt - 1 ];
185 if( pRefFrame ) // misuse as flag
187 if( bVert )
189 if( bR2L )
190 bRefIsLower = pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() ||
191 ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() &&
192 pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() );
193 else
194 bRefIsLower = pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() ||
195 ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() &&
196 pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() );
198 else if( bR2L )
199 bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ||
200 ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() &&
201 pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() );
202 else
203 bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ||
204 ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() &&
205 pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() );
207 return bRefIsLower;
210 // tdf#115319 create alternative reference formats, if the user asked for it
211 // (ReferenceFieldLanguage attribute of the reference field is not empty), and
212 // language of the text and ReferenceFieldLanguage are the same.
213 // Right now only HUNGARIAN seems to need this (as in the related issue,
214 // the reversed caption order in autocaption, solved by #i61007#)
215 static void lcl_formatReferenceLanguage( OUString& rRefText,
216 bool bClosingParenthesis, LanguageType eLang,
217 const OUString& rReferenceLanguage)
219 if (eLang != LANGUAGE_HUNGARIAN || (rReferenceLanguage != "hu" && rReferenceLanguage != "Hu"))
220 return;
222 // Add Hungarian definitive article (a/az) before references,
223 // similar to \aref, \apageref etc. of LaTeX Babel package.
225 // for example:
227 // "az 1. oldalon" ("on page 1"), but
228 // "a 2. oldalon" ("on page 2")
229 // "a fentebbi", "az alábbi" (above/below)
230 // "a Lorem", "az Ipsum"
232 // Support following numberings of EU publications:
234 // 1., 1a., a), (1), (1a), iii., III., IA.
236 // (http://publications.europa.eu/code/hu/hu-120700.htm,
237 // http://publications.europa.eu/code/hu/hu-4100600.htm)
239 LanguageTag aLanguageTag(eLang);
240 CharClass aCharClass( aLanguageTag );
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;
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)
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);
262 // length of numbering
263 nLen = i - (rRefText.getLength() - sNumbering.getLength());
265 if (bNum)
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;
273 else if (nLen == 1 && sNumbering[0] < 128)
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)
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;
289 else
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 OUString sVowels = OUStringLiteral(u"aAeEiIoOuU") + sVowelsWithDiacritic;
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] == '.'))
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();
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))
326 bArticleAz = true;
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;
334 // lowercase, if rReferenceLanguage == "hu", not "Hu"
335 OUString sArticle;
337 if ( rReferenceLanguage == "hu" )
338 sArticle = "a";
339 else
340 sArticle = "A";
342 if (bArticleAz)
343 sArticle += "z";
345 rRefText = sArticle + " " + rRefText;
348 /// get references
349 SwGetRefField::SwGetRefField( SwGetRefFieldType* pFieldType,
350 const OUString& rSetRef, const OUString& rSetReferenceLanguage, sal_uInt16 nSubTyp,
351 sal_uInt16 nSequenceNo, sal_uLong nFormat )
352 : SwField( pFieldType, nFormat ),
353 m_sSetRefName( rSetRef ),
354 m_sSetReferenceLanguage( rSetReferenceLanguage ),
355 m_nSubType( nSubTyp ),
356 m_nSeqNo( nSequenceNo )
360 SwGetRefField::~SwGetRefField()
364 OUString SwGetRefField::GetDescription() const
366 return SwResId(STR_REFERENCE);
369 sal_uInt16 SwGetRefField::GetSubType() const
371 return m_nSubType;
374 void SwGetRefField::SetSubType( sal_uInt16 n )
376 m_nSubType = n;
379 // #i81002#
380 bool SwGetRefField::IsRefToHeadingCrossRefBookmark() const
382 return GetSubType() == REF_BOOKMARK &&
383 ::sw::mark::CrossRefHeadingBookmark::IsLegalName(m_sSetRefName);
386 bool SwGetRefField::IsRefToNumItemCrossRefBookmark() const
388 return GetSubType() == REF_BOOKMARK &&
389 ::sw::mark::CrossRefNumItemBookmark::IsLegalName(m_sSetRefName);
392 const SwTextNode* SwGetRefField::GetReferencedTextNode() const
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 );
401 // #i85090#
402 OUString SwGetRefField::GetExpandedTextOfReferencedTextNode(
403 SwRootFrame const& rLayout) const
405 const SwTextNode* pReferencedTextNode( GetReferencedTextNode() );
406 return pReferencedTextNode
407 ? sw::GetExpandTextMerged(&rLayout, *pReferencedTextNode, true, false, ExpandMode(0))
408 : OUString();
411 void SwGetRefField::SetExpand( const OUString& rStr )
413 m_sText = rStr;
414 m_sTextRLHidden = rStr;
417 OUString SwGetRefField::ExpandImpl(SwRootFrame const*const pLayout) const
419 return pLayout && pLayout->IsHideRedlines() ? m_sTextRLHidden : m_sText;
422 OUString SwGetRefField::GetFieldName() const
424 const OUString aName = GetTyp()->GetName();
425 if ( !aName.isEmpty() || !m_sSetRefName.isEmpty() )
427 return aName + " " + m_sSetRefName;
429 return ExpandImpl(nullptr);
433 static void FilterText(OUString & rText, LanguageType const eLang,
434 OUString const& rSetReferenceLanguage)
436 // remove all special characters (replace them with blanks)
437 if (rText.isEmpty())
438 return;
440 rText = rText.replaceAll(u"\u00ad", "");
441 OUStringBuffer aBuf(rText);
442 const sal_Int32 l = aBuf.getLength();
443 for (sal_Int32 i = 0; i < l; ++i)
445 if (aBuf[i] < ' ')
447 aBuf[i] = ' ';
449 else if (aBuf[i] == 0x2011)
451 aBuf[i] = '-';
454 rText = aBuf.makeStringAndClear();
455 if (!rSetReferenceLanguage.isEmpty())
457 lcl_formatReferenceLanguage(rText, false, eLang, rSetReferenceLanguage);
461 // #i81002# - parameter <pFieldTextAttr> added
462 void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr )
464 m_sText.clear();
465 m_sTextRLHidden.clear();
467 SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc();
468 // finding the reference target (the number)
469 sal_Int32 nNumStart = -1;
470 sal_Int32 nNumEnd = -1;
471 SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor(
472 &rDoc, m_sSetRefName, m_nSubType, m_nSeqNo, &nNumStart, &nNumEnd
474 // not found?
475 if ( !pTextNd )
477 m_sText = SwViewShell::GetShellRes()->aGetRefField_RefItemNotFound;
478 m_sTextRLHidden = m_sText;
479 return ;
482 SwRootFrame const* pLayout(nullptr);
483 SwRootFrame const* pLayoutRLHidden(nullptr);
484 for (SwRootFrame const*const pLay : rDoc.GetAllLayouts())
486 if (pLay->IsHideRedlines())
488 pLayoutRLHidden = pLay;
490 else
492 pLayout = pLay;
496 // where is the category name (e.g. "Illustration")?
497 const OUString aText = pTextNd->GetText();
498 const sal_Int32 nCatStart = aText.indexOf(m_sSetRefName);
499 const bool bHasCat = nCatStart>=0;
500 const sal_Int32 nCatEnd = bHasCat ? nCatStart + m_sSetRefName.getLength() : -1;
502 // length of the referenced text
503 const sal_Int32 nLen = aText.getLength();
505 // which format?
506 switch( GetFormat() )
508 case REF_CONTENT:
509 case REF_ONLYNUMBER:
510 case REF_ONLYCAPTION:
511 case REF_ONLYSEQNO:
513 // needed part of Text
514 sal_Int32 nStart;
515 sal_Int32 nEnd;
517 switch( m_nSubType )
519 case REF_SEQUENCEFLD:
521 switch( GetFormat() )
523 // "Category and Number"
524 case REF_ONLYNUMBER:
525 if (bHasCat) {
526 nStart = std::min(nNumStart, nCatStart);
527 nEnd = std::max(nNumEnd, nCatEnd);
528 } else {
529 nStart = nNumStart;
530 nEnd = nNumEnd;
532 break;
534 // "Caption Text"
535 case REF_ONLYCAPTION: {
536 // next alphanumeric character after category+number
537 if (const SwTextAttr* const pTextAttr =
538 pTextNd->GetTextAttrForCharAt(nNumStart, RES_TXTATR_FIELD)
540 // start searching from nFrom
541 const sal_Int32 nFrom = bHasCat
542 ? std::max(nNumStart + 1, nCatEnd)
543 : nNumStart + 1;
544 nStart = SwGetExpField::GetReferenceTextPos( pTextAttr->GetFormatField(), rDoc, nFrom );
545 } else {
546 nStart = bHasCat ? std::max(nNumEnd, nCatEnd) : nNumEnd;
548 nEnd = nLen;
549 break;
552 // "Numbering"
553 case REF_ONLYSEQNO:
554 nStart = nNumStart;
555 nEnd = std::min(nStart + 1, nLen);
556 break;
558 // "Reference" (whole Text)
559 case REF_CONTENT:
560 nStart = 0;
561 nEnd = nLen;
562 break;
564 default:
565 O3TL_UNREACHABLE;
567 break;
569 case REF_BOOKMARK:
570 nStart = nNumStart;
571 // text is spread across multiple nodes - get whole text or only until end of node?
572 nEnd = nNumEnd<0 ? nLen : nNumEnd;
573 break;
575 case REF_OUTLINE:
576 nStart = nNumStart;
577 nEnd = nNumEnd;
578 break;
580 case REF_FOOTNOTE:
581 case REF_ENDNOTE:
582 // get number or numString
583 for( size_t i = 0; i < rDoc.GetFootnoteIdxs().size(); ++i )
585 SwTextFootnote* const pFootnoteIdx = rDoc.GetFootnoteIdxs()[i];
586 if( m_nSeqNo == pFootnoteIdx->GetSeqRefNo() )
588 m_sText = pFootnoteIdx->GetFootnote().GetViewNumStr(rDoc, nullptr);
589 m_sTextRLHidden = pFootnoteIdx->GetFootnote().GetViewNumStr(rDoc, pLayoutRLHidden);
590 if (!m_sSetReferenceLanguage.isEmpty())
592 lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage);
593 lcl_formatReferenceLanguage(m_sTextRLHidden, false, GetLanguage(), m_sSetReferenceLanguage);
595 break;
598 return;
600 case REF_SETREFATTR:
601 nStart = nNumStart;
602 nEnd = nNumEnd;
603 break;
605 default:
606 O3TL_UNREACHABLE;
609 if( nStart != nEnd ) // a section?
611 m_sText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, false, ExpandMode(0));
612 if (m_nSubType == REF_OUTLINE
613 || (m_nSubType == REF_SEQUENCEFLD && REF_CONTENT == GetFormat()))
615 m_sTextRLHidden = sw::GetExpandTextMerged(
616 pLayoutRLHidden, *pTextNd, false, false, ExpandMode(0));
618 else
620 m_sTextRLHidden = pTextNd->GetExpandText(pLayoutRLHidden,
621 nStart, nEnd - nStart, false, false, false, ExpandMode::HideDeletions);
623 FilterText(m_sText, GetLanguage(), m_sSetReferenceLanguage);
624 FilterText(m_sTextRLHidden, GetLanguage(), m_sSetReferenceLanguage);
627 break;
629 case REF_PAGE:
630 case REF_PAGE_PGDESC:
632 auto const func =
633 [this, pTextNd, nNumStart](OUString & rText, SwRootFrame const*const pLay)
635 SwTextFrame const* pFrame = static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLay, nullptr, nullptr));
636 SwTextFrame const*const pSave = pFrame;
637 if (pFrame)
639 TextFrameIndex const nNumStartIndex(pFrame->MapModelToView(pTextNd, nNumStart));
640 while (pFrame && !pFrame->IsInside(nNumStartIndex))
641 pFrame = pFrame->GetFollow();
644 if( pFrame || nullptr != ( pFrame = pSave ))
646 sal_uInt16 nPageNo = pFrame->GetVirtPageNum();
647 const SwPageFrame *pPage;
648 if( REF_PAGE_PGDESC == GetFormat() &&
649 nullptr != ( pPage = pFrame->FindPageFrame() ) &&
650 pPage->GetPageDesc() )
652 rText = pPage->GetPageDesc()->GetNumType().GetNumStr(nPageNo);
654 else
656 rText = OUString::number(nPageNo);
659 if (!m_sSetReferenceLanguage.isEmpty())
660 lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage);
663 // sw_redlinehide: currently only one of these layouts will exist,
664 // so the getLayoutFrame will use the same frame in both cases
665 func(m_sText, pLayout);
666 func(m_sTextRLHidden, pLayoutRLHidden);
668 break;
670 case REF_CHAPTER:
672 auto const func =
673 [this, pTextNd](OUString & rText, SwRootFrame const*const pLay)
675 // a bit tricky: search any frame
676 SwFrame const*const pFrame = pTextNd->getLayoutFrame(pLay);
677 if( pFrame )
679 SwChapterFieldType aFieldTyp;
680 SwChapterField aField( &aFieldTyp, 0 );
681 aField.SetLevel( MAXLEVEL - 1 );
682 aField.ChangeExpansion( *pFrame, pTextNd, true );
683 rText = aField.GetNumber(pLay);
685 if (!m_sSetReferenceLanguage.isEmpty())
686 lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage);
689 func(m_sText, pLayout);
690 func(m_sTextRLHidden, pLayoutRLHidden);
692 break;
694 case REF_UPDOWN:
696 // #i81002#
697 // simplified: use parameter <pFieldTextAttr>
698 if( !pFieldTextAttr || !pFieldTextAttr->GetpTextNode() )
699 break;
701 LanguageTag aLanguageTag( GetLanguage());
702 LocaleDataWrapper aLocaleData( aLanguageTag );
704 // first a "short" test - in case both are in the same node
705 if( pFieldTextAttr->GetpTextNode() == pTextNd )
707 m_sText = nNumStart < pFieldTextAttr->GetStart()
708 ? aLocaleData.getAboveWord()
709 : aLocaleData.getBelowWord();
710 m_sTextRLHidden = m_sText;
711 break;
714 m_sText = ::IsFrameBehind( *pFieldTextAttr->GetpTextNode(), pFieldTextAttr->GetStart(),
715 *pTextNd, nNumStart )
716 ? aLocaleData.getAboveWord()
717 : aLocaleData.getBelowWord();
719 if (!m_sSetReferenceLanguage.isEmpty())
720 lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage);
722 m_sTextRLHidden = m_sText;
724 break;
725 // #i81002#
726 case REF_NUMBER:
727 case REF_NUMBER_NO_CONTEXT:
728 case REF_NUMBER_FULL_CONTEXT:
730 if ( pFieldTextAttr && pFieldTextAttr->GetpTextNode() )
732 auto result =
733 MakeRefNumStr(pLayout, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat());
734 m_sText = result.first;
735 // for differentiation of Roman numbers and letters in Hungarian article handling
736 bool bClosingParenthesis = result.second;
737 if (!m_sSetReferenceLanguage.isEmpty())
739 lcl_formatReferenceLanguage(m_sText, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage);
741 result =
742 MakeRefNumStr(pLayoutRLHidden, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat());
743 m_sTextRLHidden = result.first;
744 bClosingParenthesis = result.second;
745 if (!m_sSetReferenceLanguage.isEmpty())
747 lcl_formatReferenceLanguage(m_sTextRLHidden, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage);
751 break;
753 default:
754 OSL_FAIL("<SwGetRefField::UpdateField(..)> - unknown format type");
758 // #i81002#
759 static std::pair<OUString, bool> MakeRefNumStr(
760 SwRootFrame const*const pLayout,
761 const SwTextNode& i_rTextNodeOfField,
762 const SwTextNode& i_rTextNodeOfReferencedItem,
763 const sal_uInt32 nRefNumFormat)
765 SwTextNode const& rTextNodeOfField(pLayout
766 ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfField)
767 : i_rTextNodeOfField);
768 SwTextNode const& rTextNodeOfReferencedItem(pLayout
769 ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfReferencedItem)
770 : i_rTextNodeOfReferencedItem);
771 if ( rTextNodeOfReferencedItem.HasNumber() &&
772 rTextNodeOfReferencedItem.IsCountedInList() )
774 OSL_ENSURE( rTextNodeOfReferencedItem.GetNum(pLayout),
775 "<SwGetRefField::MakeRefNumStr(..)> - referenced paragraph has number, but no <SwNodeNum> instance!" );
777 // Determine, up to which level the superior list labels have to be
778 // included - default is to include all superior list labels.
779 int nRestrictInclToThisLevel( 0 );
780 // Determine for format REF_NUMBER the level, up to which the superior
781 // list labels have to be restricted, if the text node of the reference
782 // field and the text node of the referenced item are in the same
783 // document context.
784 if ( nRefNumFormat == REF_NUMBER &&
785 rTextNodeOfField.FindFlyStartNode()
786 == rTextNodeOfReferencedItem.FindFlyStartNode() &&
787 rTextNodeOfField.FindFootnoteStartNode()
788 == rTextNodeOfReferencedItem.FindFootnoteStartNode() &&
789 rTextNodeOfField.FindHeaderStartNode()
790 == rTextNodeOfReferencedItem.FindHeaderStartNode() &&
791 rTextNodeOfField.FindFooterStartNode()
792 == rTextNodeOfReferencedItem.FindFooterStartNode() )
794 const SwNodeNum* pNodeNumForTextNodeOfField( nullptr );
795 if ( rTextNodeOfField.HasNumber() &&
796 rTextNodeOfField.GetNumRule() == rTextNodeOfReferencedItem.GetNumRule() )
798 pNodeNumForTextNodeOfField = rTextNodeOfField.GetNum(pLayout);
800 else
802 pNodeNumForTextNodeOfField =
803 rTextNodeOfReferencedItem.GetNum(pLayout)->GetPrecedingNodeNumOf(rTextNodeOfField);
805 if ( pNodeNumForTextNodeOfField )
807 const SwNumberTree::tNumberVector rFieldNumVec =
808 pNodeNumForTextNodeOfField->GetNumberVector();
809 const SwNumberTree::tNumberVector rRefItemNumVec =
810 rTextNodeOfReferencedItem.GetNum()->GetNumberVector();
811 std::size_t nLevel( 0 );
812 while ( nLevel < rFieldNumVec.size() && nLevel < rRefItemNumVec.size() )
814 if ( rRefItemNumVec[nLevel] == rFieldNumVec[nLevel] )
816 nRestrictInclToThisLevel = nLevel + 1;
818 else
820 break;
822 ++nLevel;
827 // Determine, if superior list labels have to be included
828 const bool bInclSuperiorNumLabels(
829 ( nRestrictInclToThisLevel < rTextNodeOfReferencedItem.GetActualListLevel() &&
830 ( nRefNumFormat == REF_NUMBER || nRefNumFormat == REF_NUMBER_FULL_CONTEXT ) ) );
832 OSL_ENSURE( rTextNodeOfReferencedItem.GetNumRule(),
833 "<SwGetRefField::MakeRefNumStr(..)> - referenced numbered paragraph has no numbering rule set!" );
834 return std::make_pair(
835 rTextNodeOfReferencedItem.GetNumRule()->MakeRefNumString(
836 *(rTextNodeOfReferencedItem.GetNum(pLayout)),
837 bInclSuperiorNumLabels,
838 nRestrictInclToThisLevel ),
839 rTextNodeOfReferencedItem.GetNumRule()->MakeNumString(
840 *(rTextNodeOfReferencedItem.GetNum(pLayout)),
841 true).endsWith(")") );
844 return std::make_pair(OUString(), false);
847 std::unique_ptr<SwField> SwGetRefField::Copy() const
849 std::unique_ptr<SwGetRefField> pField( new SwGetRefField( static_cast<SwGetRefFieldType*>(GetTyp()),
850 m_sSetRefName, m_sSetReferenceLanguage, m_nSubType,
851 m_nSeqNo, GetFormat() ) );
852 pField->m_sText = m_sText;
853 pField->m_sTextRLHidden = m_sTextRLHidden;
854 return std::unique_ptr<SwField>(pField.release());
857 /// get reference name
858 OUString SwGetRefField::GetPar1() const
860 return m_sSetRefName;
863 /// set reference name
864 void SwGetRefField::SetPar1( const OUString& rName )
866 m_sSetRefName = rName;
869 OUString SwGetRefField::GetPar2() const
871 return ExpandImpl(nullptr);
874 bool SwGetRefField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
876 switch( nWhichId )
878 case FIELD_PROP_USHORT1:
880 sal_Int16 nPart = 0;
881 switch(GetFormat())
883 case REF_PAGE : nPart = ReferenceFieldPart::PAGE ; break;
884 case REF_CHAPTER : nPart = ReferenceFieldPart::CHAPTER ; break;
885 case REF_CONTENT : nPart = ReferenceFieldPart::TEXT ; break;
886 case REF_UPDOWN : nPart = ReferenceFieldPart::UP_DOWN ; break;
887 case REF_PAGE_PGDESC: nPart = ReferenceFieldPart::PAGE_DESC ; break;
888 case REF_ONLYNUMBER : nPart = ReferenceFieldPart::CATEGORY_AND_NUMBER ; break;
889 case REF_ONLYCAPTION: nPart = ReferenceFieldPart::ONLY_CAPTION ; break;
890 case REF_ONLYSEQNO : nPart = ReferenceFieldPart::ONLY_SEQUENCE_NUMBER; break;
891 // #i81002#
892 case REF_NUMBER: nPart = ReferenceFieldPart::NUMBER; break;
893 case REF_NUMBER_NO_CONTEXT: nPart = ReferenceFieldPart::NUMBER_NO_CONTEXT; break;
894 case REF_NUMBER_FULL_CONTEXT: nPart = ReferenceFieldPart::NUMBER_FULL_CONTEXT; break;
896 rAny <<= nPart;
898 break;
899 case FIELD_PROP_USHORT2:
901 sal_Int16 nSource = 0;
902 switch(m_nSubType)
904 case REF_SETREFATTR : nSource = ReferenceFieldSource::REFERENCE_MARK; break;
905 case REF_SEQUENCEFLD: nSource = ReferenceFieldSource::SEQUENCE_FIELD; break;
906 case REF_BOOKMARK : nSource = ReferenceFieldSource::BOOKMARK; break;
907 case REF_OUTLINE : OSL_FAIL("not implemented"); break;
908 case REF_FOOTNOTE : nSource = ReferenceFieldSource::FOOTNOTE; break;
909 case REF_ENDNOTE : nSource = ReferenceFieldSource::ENDNOTE; break;
911 rAny <<= nSource;
913 break;
914 case FIELD_PROP_PAR1:
916 OUString sTmp(GetPar1());
917 if(REF_SEQUENCEFLD == m_nSubType)
919 sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( sTmp, SwGetPoolIdFromName::TxtColl );
920 switch( nPoolId )
922 case RES_POOLCOLL_LABEL_ABB:
923 case RES_POOLCOLL_LABEL_TABLE:
924 case RES_POOLCOLL_LABEL_FRAME:
925 case RES_POOLCOLL_LABEL_DRAWING:
926 case RES_POOLCOLL_LABEL_FIGURE:
927 SwStyleNameMapper::FillProgName(nPoolId, sTmp) ;
928 break;
931 rAny <<= sTmp;
933 break;
934 case FIELD_PROP_PAR3:
935 rAny <<= ExpandImpl(nullptr);
936 break;
937 case FIELD_PROP_PAR4:
938 rAny <<= m_sSetReferenceLanguage;
939 break;
940 case FIELD_PROP_SHORT1:
941 rAny <<= static_cast<sal_Int16>(m_nSeqNo);
942 break;
943 default:
944 assert(false);
946 return true;
949 bool SwGetRefField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
951 switch( nWhichId )
953 case FIELD_PROP_USHORT1:
955 sal_Int16 nPart = 0;
956 rAny >>= nPart;
957 switch(nPart)
959 case ReferenceFieldPart::PAGE: nPart = REF_PAGE; break;
960 case ReferenceFieldPart::CHAPTER: nPart = REF_CHAPTER; break;
961 case ReferenceFieldPart::TEXT: nPart = REF_CONTENT; break;
962 case ReferenceFieldPart::UP_DOWN: nPart = REF_UPDOWN; break;
963 case ReferenceFieldPart::PAGE_DESC: nPart = REF_PAGE_PGDESC; break;
964 case ReferenceFieldPart::CATEGORY_AND_NUMBER: nPart = REF_ONLYNUMBER; break;
965 case ReferenceFieldPart::ONLY_CAPTION: nPart = REF_ONLYCAPTION; break;
966 case ReferenceFieldPart::ONLY_SEQUENCE_NUMBER : nPart = REF_ONLYSEQNO; break;
967 // #i81002#
968 case ReferenceFieldPart::NUMBER: nPart = REF_NUMBER; break;
969 case ReferenceFieldPart::NUMBER_NO_CONTEXT: nPart = REF_NUMBER_NO_CONTEXT; break;
970 case ReferenceFieldPart::NUMBER_FULL_CONTEXT: nPart = REF_NUMBER_FULL_CONTEXT; break;
971 default: return false;
973 SetFormat(nPart);
975 break;
976 case FIELD_PROP_USHORT2:
978 sal_Int16 nSource = 0;
979 rAny >>= nSource;
980 switch(nSource)
982 case ReferenceFieldSource::REFERENCE_MARK : m_nSubType = REF_SETREFATTR ; break;
983 case ReferenceFieldSource::SEQUENCE_FIELD :
985 if(REF_SEQUENCEFLD == m_nSubType)
986 break;
987 m_nSubType = REF_SEQUENCEFLD;
988 ConvertProgrammaticToUIName();
990 break;
991 case ReferenceFieldSource::BOOKMARK : m_nSubType = REF_BOOKMARK ; break;
992 case ReferenceFieldSource::FOOTNOTE : m_nSubType = REF_FOOTNOTE ; break;
993 case ReferenceFieldSource::ENDNOTE : m_nSubType = REF_ENDNOTE ; break;
996 break;
997 case FIELD_PROP_PAR1:
999 OUString sTmpStr;
1000 rAny >>= sTmpStr;
1001 SetPar1(sTmpStr);
1002 ConvertProgrammaticToUIName();
1004 break;
1005 case FIELD_PROP_PAR3:
1007 OUString sTmpStr;
1008 rAny >>= sTmpStr;
1009 SetExpand( sTmpStr );
1011 break;
1012 case FIELD_PROP_PAR4:
1013 rAny >>= m_sSetReferenceLanguage;
1014 break;
1015 case FIELD_PROP_SHORT1:
1017 sal_Int16 nSetSeq = 0;
1018 rAny >>= nSetSeq;
1019 if(nSetSeq >= 0)
1020 m_nSeqNo = nSetSeq;
1022 break;
1023 default:
1024 assert(false);
1026 return true;
1029 void SwGetRefField::ConvertProgrammaticToUIName()
1031 if(!(GetTyp() && REF_SEQUENCEFLD == m_nSubType))
1032 return;
1034 SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc();
1035 const OUString rPar1 = GetPar1();
1036 // don't convert when the name points to an existing field type
1037 if (rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, rPar1, false))
1038 return;
1040 sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromProgName( rPar1, SwGetPoolIdFromName::TxtColl );
1041 const char* pResId = nullptr;
1042 switch( nPoolId )
1044 case RES_POOLCOLL_LABEL_ABB:
1045 pResId = STR_POOLCOLL_LABEL_ABB;
1046 break;
1047 case RES_POOLCOLL_LABEL_TABLE:
1048 pResId = STR_POOLCOLL_LABEL_TABLE;
1049 break;
1050 case RES_POOLCOLL_LABEL_FRAME:
1051 pResId = STR_POOLCOLL_LABEL_FRAME;
1052 break;
1053 case RES_POOLCOLL_LABEL_DRAWING:
1054 pResId = STR_POOLCOLL_LABEL_DRAWING;
1055 break;
1056 case RES_POOLCOLL_LABEL_FIGURE:
1057 pResId = STR_POOLCOLL_LABEL_FIGURE;
1058 break;
1060 if (pResId)
1061 SetPar1(SwResId(pResId));
1064 SwGetRefFieldType::SwGetRefFieldType( SwDoc& rDc )
1065 : SwFieldType( SwFieldIds::GetRef ), m_rDoc( rDc )
1068 std::unique_ptr<SwFieldType> SwGetRefFieldType::Copy() const
1070 return std::make_unique<SwGetRefFieldType>( m_rDoc );
1073 void SwGetRefFieldType::UpdateGetReferences()
1075 std::vector<SwFormatField*> vFields;
1076 GatherFields(vFields, false);
1077 for(auto pFormatField: vFields)
1079 // update only the GetRef fields
1080 //JP 3.4.2001: Task 71231 - we need the correct language
1081 SwGetRefField* pGRef = static_cast<SwGetRefField*>(pFormatField->GetField());
1082 const SwTextField* pTField;
1083 if(!pGRef->GetLanguage() &&
1084 nullptr != (pTField = pFormatField->GetTextField()) &&
1085 pTField->GetpTextNode())
1087 pGRef->SetLanguage(pTField->GetpTextNode()->GetLang(pTField->GetStart()));
1090 // #i81002#
1091 pGRef->UpdateField(pFormatField->GetTextField());
1093 NotifyClients(nullptr, nullptr);
1096 void SwGetRefFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint)
1098 auto pLegacy = dynamic_cast<const sw::LegacyModifyHint*>(&rHint);
1099 if(!pLegacy)
1100 return;
1101 if(!pLegacy->m_pNew && !pLegacy->m_pOld)
1102 // update to all GetReference fields
1103 // hopefully, this codepath is soon dead code, and
1104 // UpdateGetReferences gets only called directly
1105 UpdateGetReferences();
1106 else
1107 // forward to text fields, they "expand" the text
1108 NotifyClients(pLegacy->m_pOld, pLegacy->m_pNew);
1111 namespace sw {
1113 bool IsMarkHintHidden(SwRootFrame const& rLayout,
1114 SwTextNode const& rNode, SwTextAttrEnd const& rHint)
1116 if (!rLayout.HasMergedParas())
1118 return false;
1120 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
1121 rNode.getLayoutFrame(&rLayout)));
1122 if (!pFrame)
1124 return true;
1126 sal_Int32 const*const pEnd(rHint.GetEnd());
1127 if (pEnd)
1129 return pFrame->MapModelToView(&rNode, rHint.GetStart())
1130 == pFrame->MapModelToView(&rNode, *pEnd);
1132 else
1134 assert(rHint.HasDummyChar());
1135 return pFrame->MapModelToView(&rNode, rHint.GetStart())
1136 == pFrame->MapModelToView(&rNode, rHint.GetStart() + 1);
1140 } // namespace sw
1142 SwTextNode* SwGetRefFieldType::FindAnchor( SwDoc* pDoc, const OUString& rRefMark,
1143 sal_uInt16 nSubType, sal_uInt16 nSeqNo,
1144 sal_Int32* pStt, sal_Int32* pEnd,
1145 SwRootFrame const*const pLayout)
1147 OSL_ENSURE( pStt, "Why did no one check the StartPos?" );
1149 IDocumentRedlineAccess & rIDRA(pDoc->getIDocumentRedlineAccess());
1150 SwTextNode* pTextNd = nullptr;
1151 switch( nSubType )
1153 case REF_SETREFATTR:
1155 const SwFormatRefMark *pRef = pDoc->GetRefMark( rRefMark );
1156 SwTextRefMark const*const pRefMark(pRef ? pRef->GetTextRefMark() : nullptr);
1157 if (pRefMark && (!pLayout || !sw::IsMarkHintHidden(*pLayout,
1158 pRefMark->GetTextNode(), *pRefMark)))
1160 pTextNd = const_cast<SwTextNode*>(&pRef->GetTextRefMark()->GetTextNode());
1161 *pStt = pRef->GetTextRefMark()->GetStart();
1162 if( pEnd )
1163 *pEnd = pRef->GetTextRefMark()->GetAnyEnd();
1166 break;
1168 case REF_SEQUENCEFLD:
1170 SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp, rRefMark, false );
1171 if( pFieldType && pFieldType->HasWriterListeners() &&
1172 nsSwGetSetExpType::GSE_SEQ & static_cast<SwSetExpFieldType*>(pFieldType)->GetType() )
1174 std::vector<SwFormatField*> vFields;
1175 pFieldType->GatherFields(vFields, false);
1176 for(auto pFormatField: vFields)
1178 SwTextField *const pTextField(pFormatField->GetTextField());
1179 if (pTextField && nSeqNo ==
1180 static_cast<SwSetExpField*>(pFormatField->GetField())->GetSeqNumber()
1181 && (!pLayout || !pLayout->IsHideRedlines()
1182 || !sw::IsFieldDeletedInModel(rIDRA, *pTextField)))
1184 pTextNd = pTextField->GetpTextNode();
1185 *pStt = pTextField->GetStart();
1186 if( pEnd )
1187 *pEnd = (*pStt) + 1;
1188 break;
1193 break;
1195 case REF_BOOKMARK:
1197 IDocumentMarkAccess::const_iterator_t ppMark = pDoc->getIDocumentMarkAccess()->findMark(rRefMark);
1198 if (ppMark != pDoc->getIDocumentMarkAccess()->getAllMarksEnd()
1199 && (!pLayout || !pLayout->IsHideRedlines()
1200 || !sw::IsMarkHidden(*pLayout, **ppMark)))
1202 const ::sw::mark::IMark* pBkmk = *ppMark;
1203 const SwPosition* pPos = &pBkmk->GetMarkStart();
1205 pTextNd = pPos->nNode.GetNode().GetTextNode();
1206 *pStt = pPos->nContent.GetIndex();
1207 if(pEnd)
1209 if(!pBkmk->IsExpanded())
1211 *pEnd = *pStt;
1212 // #i81002#
1213 if(dynamic_cast< ::sw::mark::CrossRefBookmark const *>(pBkmk))
1215 OSL_ENSURE( pTextNd,
1216 "<SwGetRefFieldType::FindAnchor(..)> - node marked by cross-reference bookmark isn't a text node --> crash" );
1217 *pEnd = pTextNd->Len();
1220 else if(pBkmk->GetOtherMarkPos().nNode == pBkmk->GetMarkPos().nNode)
1221 *pEnd = pBkmk->GetMarkEnd().nContent.GetIndex();
1222 else
1223 *pEnd = -1;
1227 break;
1229 case REF_OUTLINE:
1230 break;
1232 case REF_FOOTNOTE:
1233 case REF_ENDNOTE:
1235 for( auto pFootnoteIdx : pDoc->GetFootnoteIdxs() )
1236 if( nSeqNo == pFootnoteIdx->GetSeqRefNo() )
1238 if (pLayout && pLayout->IsHideRedlines()
1239 && sw::IsFootnoteDeleted(rIDRA, *pFootnoteIdx))
1241 return nullptr;
1243 // otherwise: the position at the start of the footnote
1244 // will be mapped to something visible at least...
1245 SwNodeIndex* pIdx = pFootnoteIdx->GetStartNode();
1246 if( pIdx )
1248 SwNodeIndex aIdx( *pIdx, 1 );
1249 pTextNd = aIdx.GetNode().GetTextNode();
1250 if( nullptr == pTextNd )
1251 pTextNd = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext( &aIdx ));
1253 *pStt = 0;
1254 if( pEnd )
1255 *pEnd = 0;
1256 break;
1259 break;
1262 return pTextNd;
1265 namespace {
1267 struct RefIdsMap
1269 private:
1270 OUString aName;
1271 std::set<sal_uInt16> aIds;
1272 std::set<sal_uInt16> aDstIds;
1273 std::map<sal_uInt16, sal_uInt16> sequencedIds; /// ID numbers sorted by sequence number.
1274 bool bInit;
1276 void Init(SwDoc& rDoc, SwDoc& rDestDoc, bool bField );
1277 static void GetNoteIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds );
1278 void GetFieldIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds );
1279 void AddId( sal_uInt16 id, sal_uInt16 seqNum );
1280 static sal_uInt16 GetFirstUnusedId( std::set<sal_uInt16> &rIds );
1282 public:
1283 explicit RefIdsMap( const OUString& rName ) : aName( rName ), bInit( false ) {}
1285 void Check( SwDoc& rDoc, SwDoc& rDestDoc, SwGetRefField& rField, bool bField );
1287 const OUString& GetName() const { return aName; }
1292 /// Get a sorted list of the field IDs from a document.
1293 /// @param[in] rDoc The document to search.
1294 /// @param[in,out] rIds The list of IDs found in the document.
1295 void RefIdsMap::GetFieldIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds)
1297 SwFieldType *const pType = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, aName, false);
1298 if (!pType)
1299 return;
1300 std::vector<SwFormatField*> vFields;
1301 pType->GatherFields(vFields);
1302 for(const auto pF: vFields)
1303 rIds.insert(static_cast<SwSetExpField const*>(pF->GetField())->GetSeqNumber());
1306 /// Get a sorted list of the footnote/endnote IDs from a document.
1307 /// @param[in] rDoc The document to search.
1308 /// @param[in,out] rIds The list of IDs found in the document.
1309 void RefIdsMap::GetNoteIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds)
1311 for( auto n = rDoc.GetFootnoteIdxs().size(); n; )
1312 rIds.insert( rDoc.GetFootnoteIdxs()[ --n ]->GetSeqRefNo() );
1315 /// Initialise the aIds and aDestIds collections from the source documents.
1316 /// @param[in] rDoc The source document.
1317 /// @param[in] rDestDoc The destination document.
1318 /// @param[in] bField True if we're interested in all fields, false for footnotes.
1319 void RefIdsMap::Init( SwDoc& rDoc, SwDoc& rDestDoc, bool bField )
1321 if( bInit )
1322 return;
1324 if( bField )
1326 GetFieldIdsFromDoc( rDestDoc, aIds );
1327 GetFieldIdsFromDoc( rDoc, aDstIds );
1329 // Map all the new src fields to the next available unused id
1330 for (const auto& rId : aDstIds)
1331 AddId( GetFirstUnusedId(aIds), rId );
1333 // Change the Sequence number of all SetExp fields in the source document
1334 SwFieldType* pType = rDoc.getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp, aName, false );
1335 if(pType)
1337 std::vector<SwFormatField*> vFields;
1338 pType->GatherFields(vFields, false);
1339 for(auto pF: vFields)
1341 if(!pF->GetTextField())
1342 continue;
1343 SwSetExpField *const pSetExp(static_cast<SwSetExpField *>(pF->GetField()));
1344 sal_uInt16 const n = pSetExp->GetSeqNumber();
1345 pSetExp->SetSeqNumber(sequencedIds[n]);
1349 else
1351 GetNoteIdsFromDoc( rDestDoc, aIds );
1352 GetNoteIdsFromDoc( rDoc, aDstIds );
1354 for (const auto& rId : aDstIds)
1355 AddId( GetFirstUnusedId(aIds), rId );
1357 // Change the footnotes/endnotes in the source doc to the new ID
1358 for ( const auto pFootnoteIdx : rDoc.GetFootnoteIdxs() )
1360 sal_uInt16 const n = pFootnoteIdx->GetSeqRefNo();
1361 pFootnoteIdx->SetSeqNo(sequencedIds[n]);
1364 bInit = true;
1367 /// Get the lowest number unused in the passed set.
1368 /// @param[in] rIds The set of used ID numbers.
1369 /// @returns The lowest number unused by the passed set
1370 sal_uInt16 RefIdsMap::GetFirstUnusedId( std::set<sal_uInt16> &rIds )
1372 sal_uInt16 num(0);
1374 for( const auto& rId : rIds )
1376 if( num != rId )
1378 return num;
1380 ++num;
1382 return num;
1385 /// Add a new ID and sequence number to the "occupied" collection.
1386 /// @param[in] id The ID number.
1387 /// @param[in] seqNum The sequence number.
1388 void RefIdsMap::AddId( sal_uInt16 id, sal_uInt16 seqNum )
1390 aIds.insert( id );
1391 sequencedIds[ seqNum ] = id;
1394 void RefIdsMap::Check( SwDoc& rDoc, SwDoc& rDestDoc, SwGetRefField& rField,
1395 bool bField )
1397 Init( rDoc, rDestDoc, bField);
1399 sal_uInt16 const nSeqNo = rField.GetSeqNo();
1401 // check if it needs to be remapped
1402 // if sequencedIds doesn't contain the number, it means there is no
1403 // SetExp field / footnote in the source document: do not modify
1404 // the number, which works well for copy from/paste to same document
1405 // (and if it is not the same document, there's no "correct" result anyway)
1406 if (sequencedIds.count(nSeqNo))
1408 rField.SetSeqNo( sequencedIds[nSeqNo] );
1412 /// 1. if _both_ SetExp + GetExp / Footnote + GetExp field are copied,
1413 /// ensure that both get a new unused matching number
1414 /// 2. if only SetExp / Footnote is copied, it gets a new unused number
1415 /// 3. if only GetExp field is copied, for the case of copy from / paste to
1416 /// same document it's desirable to keep the same number;
1417 /// for other cases of copy/paste or master documents it's not obvious
1418 /// what is most desirable since it's going to be wrong anyway
1419 void SwGetRefFieldType::MergeWithOtherDoc( SwDoc& rDestDoc )
1421 if (&rDestDoc == &m_rDoc)
1422 return;
1424 if (rDestDoc.IsClipBoard())
1426 // when copying _to_ clipboard, expectation is that no fields exist
1427 // so no re-mapping is required to avoid collisions
1428 assert(!rDestDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef)->HasWriterListeners());
1429 return; // don't modify the fields in the source doc
1432 // then there are RefFields in the DescDox - so all RefFields in the SourceDoc
1433 // need to be converted to have unique IDs for both documents
1434 RefIdsMap aFntMap { OUString() };
1435 std::vector<std::unique_ptr<RefIdsMap>> aFieldMap;
1437 std::vector<SwFormatField*> vFields;
1438 GatherFields(vFields);
1439 for(auto pField: vFields)
1441 SwGetRefField& rRefField = *static_cast<SwGetRefField*>(pField->GetField());
1442 switch( rRefField.GetSubType() )
1444 case REF_SEQUENCEFLD:
1446 RefIdsMap* pMap = nullptr;
1447 for( auto n = aFieldMap.size(); n; )
1449 if (aFieldMap[ --n ]->GetName() == rRefField.GetSetRefName())
1451 pMap = aFieldMap[ n ].get();
1452 break;
1455 if( !pMap )
1457 pMap = new RefIdsMap( rRefField.GetSetRefName() );
1458 aFieldMap.push_back(std::unique_ptr<RefIdsMap>(pMap));
1461 pMap->Check(m_rDoc, rDestDoc, rRefField, true);
1463 break;
1465 case REF_FOOTNOTE:
1466 case REF_ENDNOTE:
1467 aFntMap.Check(m_rDoc, rDestDoc, rRefField, false);
1468 break;
1473 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */