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