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