xref: /core/sw/source/core/fields/reffld.cxx (revision 71337b43)
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