1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/text/ReferenceFieldPart.hpp>
21 #include <com/sun/star/text/ReferenceFieldSource.hpp>
22 #include <o3tl/unreachable.hxx>
23 #include <unotools/localedatawrapper.hxx>
24 #include <unotools/charclass.hxx>
26 #include <IDocumentFieldsAccess.hxx>
27 #include <IDocumentLayoutAccess.hxx>
28 #include <IDocumentMarkAccess.hxx>
31 #include <pagefrm.hxx>
32 #include <rootfrm.hxx>
33 #include <modeltoviewhelper.hxx>
37 #include <fmtrfmrk.hxx>
38 #include <txtrfmrk.hxx>
45 #include <notxtfrm.hxx>
47 #include <pagedesc.hxx>
49 #include <crossrefbookmark.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>
67 #include <string_view>
72 using namespace ::com::sun::star
;
73 using namespace ::com::sun::star::text
;
75 static std::pair
<OUString
, bool> MakeRefNumStr(SwRootFrame
const* pLayout
,
76 const SwTextNode
& rTextNodeOfField
,
77 const SwTextNode
& rTextNodeOfReferencedItem
,
79 sal_uInt32 nRefNumFormat
,
82 static void lcl_GetLayTree( const SwFrame
* pFrame
, std::vector
<const SwFrame
*>& rArr
)
86 if( pFrame
->IsBodyFrame() ) // unspectacular
87 pFrame
= pFrame
->GetUpper();
90 rArr
.push_back( pFrame
);
92 // this is the last page
93 if( pFrame
->IsPageFrame() )
96 if( pFrame
->IsFlyFrame() )
97 pFrame
= static_cast<const SwFlyFrame
*>(pFrame
)->GetAnchorFrame();
99 pFrame
= pFrame
->GetUpper();
104 bool IsFrameBehind( const SwTextNode
& rMyNd
, sal_Int32 nMySttPos
,
105 const SwTextNode
& rBehindNd
, sal_Int32 nSttPos
)
107 const SwTextFrame
* pMyFrame
= static_cast<SwTextFrame
*>(rMyNd
.getLayoutFrame(
108 rMyNd
.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr));
109 const SwTextFrame
* pFrame
= static_cast<SwTextFrame
*>(rBehindNd
.getLayoutFrame(
110 rBehindNd
.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr));
112 if( !pFrame
|| !pMyFrame
)
115 TextFrameIndex
const nMySttPosIndex(pMyFrame
->MapModelToView(&rMyNd
, nMySttPos
));
116 TextFrameIndex
const nSttPosIndex(pFrame
->MapModelToView(&rBehindNd
, nSttPos
));
117 while (pFrame
&& !pFrame
->IsInside(nSttPosIndex
))
118 pFrame
= pFrame
->GetFollow();
119 while (pMyFrame
&& !pMyFrame
->IsInside(nMySttPosIndex
))
120 pMyFrame
= pMyFrame
->GetFollow();
122 if( !pFrame
|| !pMyFrame
|| pFrame
== pMyFrame
)
125 std::vector
<const SwFrame
*> aRefArr
, aArr
;
126 ::lcl_GetLayTree( pFrame
, aRefArr
);
127 ::lcl_GetLayTree( pMyFrame
, aArr
);
129 size_t nRefCnt
= aRefArr
.size() - 1, nCnt
= aArr
.size() - 1;
133 // Loop as long as a frame does not equal?
134 while( nRefCnt
&& nCnt
&& aRefArr
[ nRefCnt
] == aArr
[ nCnt
] )
136 const SwFrame
* pTmpFrame
= aArr
[ nCnt
];
137 bVert
= pTmpFrame
->IsVertical();
138 bR2L
= pTmpFrame
->IsRightToLeft();
143 // If a counter overflows?
144 if( aRefArr
[ nRefCnt
] == aArr
[ nCnt
] )
152 const SwFrame
* pRefFrame
= aRefArr
[ nRefCnt
];
153 const SwFrame
* pFieldFrame
= aArr
[ nCnt
];
155 // different frames, check their Y-/X-position
156 bool bRefIsLower
= false;
157 if( ( SwFrameType::Column
| SwFrameType::Cell
) & pFieldFrame
->GetType() ||
158 ( SwFrameType::Column
| SwFrameType::Cell
) & pRefFrame
->GetType() )
160 if( pFieldFrame
->GetType() == pRefFrame
->GetType() )
162 // here, the X-pos is more important
166 bRefIsLower
= pRefFrame
->getFrameArea().Top() < pFieldFrame
->getFrameArea().Top() ||
167 ( pRefFrame
->getFrameArea().Top() == pFieldFrame
->getFrameArea().Top() &&
168 pRefFrame
->getFrameArea().Left() < pFieldFrame
->getFrameArea().Left() );
170 bRefIsLower
= pRefFrame
->getFrameArea().Top() < pFieldFrame
->getFrameArea().Top() ||
171 ( pRefFrame
->getFrameArea().Top() == pFieldFrame
->getFrameArea().Top() &&
172 pRefFrame
->getFrameArea().Left() > pFieldFrame
->getFrameArea().Left() );
175 bRefIsLower
= pRefFrame
->getFrameArea().Left() > pFieldFrame
->getFrameArea().Left() ||
176 ( pRefFrame
->getFrameArea().Left() == pFieldFrame
->getFrameArea().Left() &&
177 pRefFrame
->getFrameArea().Top() < pFieldFrame
->getFrameArea().Top() );
179 bRefIsLower
= pRefFrame
->getFrameArea().Left() < pFieldFrame
->getFrameArea().Left() ||
180 ( pRefFrame
->getFrameArea().Left() == pFieldFrame
->getFrameArea().Left() &&
181 pRefFrame
->getFrameArea().Top() < pFieldFrame
->getFrameArea().Top() );
184 else if( ( SwFrameType::Column
| SwFrameType::Cell
) & pFieldFrame
->GetType() )
185 pFieldFrame
= aArr
[ nCnt
- 1 ];
187 pRefFrame
= aRefArr
[ nRefCnt
- 1 ];
190 if( pRefFrame
) // misuse as flag
195 bRefIsLower
= pRefFrame
->getFrameArea().Left() < pFieldFrame
->getFrameArea().Left() ||
196 ( pRefFrame
->getFrameArea().Left() == pFieldFrame
->getFrameArea().Left() &&
197 pRefFrame
->getFrameArea().Top() < pFieldFrame
->getFrameArea().Top() );
199 bRefIsLower
= pRefFrame
->getFrameArea().Left() > pFieldFrame
->getFrameArea().Left() ||
200 ( pRefFrame
->getFrameArea().Left() == pFieldFrame
->getFrameArea().Left() &&
201 pRefFrame
->getFrameArea().Top() < pFieldFrame
->getFrameArea().Top() );
204 bRefIsLower
= pRefFrame
->getFrameArea().Top() < pFieldFrame
->getFrameArea().Top() ||
205 ( pRefFrame
->getFrameArea().Top() == pFieldFrame
->getFrameArea().Top() &&
206 pRefFrame
->getFrameArea().Left() > pFieldFrame
->getFrameArea().Left() );
208 bRefIsLower
= pRefFrame
->getFrameArea().Top() < pFieldFrame
->getFrameArea().Top() ||
209 ( pRefFrame
->getFrameArea().Top() == pFieldFrame
->getFrameArea().Top() &&
210 pRefFrame
->getFrameArea().Left() < pFieldFrame
->getFrameArea().Left() );
215 // tdf#115319 create alternative reference formats, if the user asked for it
216 // (ReferenceFieldLanguage attribute of the reference field is not empty), and
217 // language of the text and ReferenceFieldLanguage are the same.
218 // Right now only HUNGARIAN seems to need this (as in the related issue,
219 // the reversed caption order in autocaption, solved by #i61007#)
220 static void lcl_formatReferenceLanguage( OUString
& rRefText
,
221 bool bClosingParenthesis
, LanguageType eLang
,
222 std::u16string_view rReferenceLanguage
)
224 if (eLang
!= LANGUAGE_HUNGARIAN
|| (rReferenceLanguage
!= u
"hu" && rReferenceLanguage
!= u
"Hu"))
227 // Add Hungarian definitive article (a/az) before references,
228 // similar to \aref, \apageref etc. of LaTeX Babel package.
232 // "az 1. oldalon" ("on page 1"), but
233 // "a 2. oldalon" ("on page 2")
234 // "a fentebbi", "az alábbi" (above/below)
235 // "a Lorem", "az Ipsum"
237 // Support following numberings of EU publications:
239 // 1., 1a., a), (1), (1a), iii., III., IA.
241 // (http://publications.europa.eu/code/hu/hu-120700.htm,
242 // http://publications.europa.eu/code/hu/hu-4100600.htm)
244 CharClass
aCharClass(( LanguageTag(eLang
) ));
245 sal_Int32 nLen
= rRefText
.getLength();
247 // substring of rRefText starting with letter or number
250 bool bArticleAz
= false;
251 // is numbering a number?
254 // search first member of the numbering (numbers or letters)
255 for (i
=0; i
<nLen
&& (sNumbering
.isEmpty() ||
256 ((bNum
&& aCharClass
.isDigit(rRefText
, i
)) ||
257 (!bNum
&& aCharClass
.isLetter(rRefText
, i
)))); ++i
)
259 // start of numbering within the field text
260 if (sNumbering
.isEmpty() && aCharClass
.isLetterNumeric(rRefText
, i
)) {
261 sNumbering
= rRefText
.copy(i
);
262 bNum
= aCharClass
.isDigit(rRefText
, i
);
266 // length of numbering
267 nLen
= i
- (rRefText
.getLength() - sNumbering
.getLength());
271 // az 1, 1000, 1000000, 1000000000...
273 if ((sNumbering
.startsWith("1") && (nLen
== 1 || nLen
== 4 || nLen
== 7 || nLen
== 10)) ||
274 sNumbering
.startsWith("5"))
277 else if (nLen
== 1 && sNumbering
[0] < 128)
279 // ASCII 1-letter numbering
280 // az a), e), f) ... x)
281 // az i., v. (but, a x.)
282 static const std::u16string_view sLettersStartingWithVowels
= u
"aefilmnorsuxyAEFILMNORSUXY";
283 if (sLettersStartingWithVowels
.find(sNumbering
[0]) != std::u16string_view::npos
)
285 // x), X) are letters, but x. and X. etc. are Roman numbers
286 if (bClosingParenthesis
||
287 (sNumbering
[0] != 'x' && sNumbering
[0] != 'X'))
289 } else if ((sNumbering
[0] == 'v' || sNumbering
[0] == 'V') && !bClosingParenthesis
)
290 // v), V) are letters, but v. and V. are Roman numbers
295 static const sal_Unicode sVowelsWithDiacritic
[] = {
296 0x00E1, 0x00C1, 0x00E9, 0x00C9, 0x00ED, 0x00CD,
297 0x00F3, 0x00D3, 0x00F6, 0x00D6, 0x0151, 0x0150,
298 0x00FA, 0x00DA, 0x00FC, 0x00DC, 0x0171, 0x0170, 0 };
299 static const OUString sVowels
= OUString::Concat(u
"aAeEiIoOuU") + sVowelsWithDiacritic
;
301 // handle more than 1-letter long Roman numbers and
302 // their possible combinations with letters:
303 // az IA, a IIB, a IIIC., az Ia, a IIb., a iiic), az LVIII. szonett
304 bool bRomanNumber
= false;
305 if (nLen
> 1 && (nLen
+ 1 >= sNumbering
.getLength() || sNumbering
[nLen
] == '.'))
307 sal_Unicode last
= sNumbering
[nLen
- 1];
308 OUString sNumberingTrim
;
309 if ((last
>= 'A' && last
< 'I') || (last
>= 'a' && last
< 'i'))
310 sNumberingTrim
= sNumbering
.copy(0, nLen
- 1);
312 sNumberingTrim
= sNumbering
.copy(0, nLen
);
314 sNumberingTrim
.replaceAll("i", "").replaceAll("v", "").replaceAll("x", "").replaceAll("l", "").replaceAll("c", "").isEmpty() ||
315 sNumberingTrim
.replaceAll("I", "").replaceAll("V", "").replaceAll("X", "").replaceAll("L", "").replaceAll("C", "").isEmpty();
319 // Roman number and a letter optionally
321 (sNumbering
[0] == 'i' && sNumbering
[1] != 'i' && sNumbering
[1] != 'v' && sNumbering
[1] != 'x') ||
322 (sNumbering
[0] == 'I' && sNumbering
[1] != 'I' && sNumbering
[1] != 'V' && sNumbering
[1] != 'X') ||
323 (sNumbering
[0] == 'v' && sNumbering
[1] != 'i') ||
324 (sNumbering
[0] == 'V' && sNumbering
[1] != 'I') ||
325 (sNumbering
[0] == 'l' && sNumbering
[1] != 'x') ||
326 (sNumbering
[0] == 'L' && sNumbering
[1] != 'X')) ) ||
327 // a word starting with vowel (not Roman number)
328 ( !bRomanNumber
&& sVowels
.indexOf(sNumbering
[0]) != -1))
333 // not a title text starting already with a definitive article
334 if ( sNumbering
.startsWith("A ") || sNumbering
.startsWith("Az ") ||
335 sNumbering
.startsWith("a ") || sNumbering
.startsWith("az ") )
338 // lowercase, if rReferenceLanguage == "hu", not "Hu"
341 if ( rReferenceLanguage
== u
"hu" )
349 rRefText
= sArticle
+ " " + rRefText
;
353 SwGetRefField::SwGetRefField( SwGetRefFieldType
* pFieldType
,
354 OUString aSetRef
, OUString aSetReferenceLanguage
, sal_uInt16 nSubTyp
,
355 sal_uInt16 nSequenceNo
, sal_uInt16 nFlags
, sal_uLong nFormat
)
356 : SwField(pFieldType
, nFormat
),
357 m_sSetRefName(std::move(aSetRef
)),
358 m_sSetReferenceLanguage(std::move(aSetReferenceLanguage
)),
360 m_nSeqNo(nSequenceNo
),
365 SwGetRefField::~SwGetRefField()
369 OUString
SwGetRefField::GetDescription() const
371 return SwResId(STR_REFERENCE
);
374 sal_uInt16
SwGetRefField::GetSubType() const
379 void SwGetRefField::SetSubType( sal_uInt16 n
)
385 bool SwGetRefField::IsRefToHeadingCrossRefBookmark() const
387 return GetSubType() == REF_BOOKMARK
&&
388 ::sw::mark::CrossRefHeadingBookmark::IsLegalName(m_sSetRefName
);
391 bool SwGetRefField::IsRefToNumItemCrossRefBookmark() const
393 return GetSubType() == REF_BOOKMARK
&&
394 ::sw::mark::CrossRefNumItemBookmark::IsLegalName(m_sSetRefName
);
397 const SwTextNode
* SwGetRefField::GetReferencedTextNode(SwTextNode
* pTextNode
, SwFrame
* pFrame
) const
399 SwGetRefFieldType
*pTyp
= dynamic_cast<SwGetRefFieldType
*>(GetTyp());
402 sal_Int32 nDummy
= -1;
403 return SwGetRefFieldType::FindAnchor( &pTyp
->GetDoc(), m_sSetRefName
, m_nSubType
, m_nSeqNo
, m_nFlags
, &nDummy
,
404 nullptr, nullptr, pTextNode
, pFrame
);
407 // strikethrough for tooltips using Unicode combining character
408 static OUString
lcl_formatStringByCombiningCharacter(std::u16string_view sText
, const sal_Unicode cChar
)
410 OUStringBuffer
sRet(sText
.size() * 2);
411 for (size_t i
= 0; i
< sText
.size(); ++i
)
413 sRet
.append(OUStringChar(sText
[i
]) + OUStringChar(cChar
));
415 return sRet
.makeStringAndClear();
419 OUString
SwGetRefField::GetExpandedTextOfReferencedTextNode(
420 SwRootFrame
const& rLayout
) const
422 const SwTextNode
* pReferencedTextNode( GetReferencedTextNode(/*pTextNode*/nullptr, /*pFrame*/nullptr) );
423 if ( !pReferencedTextNode
)
426 // show the referenced text without the deletions, but if the whole text was
427 // deleted, show the original text for the sake of the comfortable reviewing,
428 // but with Unicode strikethrough in the tooltip
429 OUString sRet
= sw::GetExpandTextMerged(&rLayout
, *pReferencedTextNode
, true, false, ExpandMode::HideDeletions
);
430 if ( sRet
.isEmpty() )
432 static const sal_Unicode cStrikethrough
= u
'\x0336';
433 sRet
= sw::GetExpandTextMerged(&rLayout
, *pReferencedTextNode
, true, false, ExpandMode(0));
434 sRet
= lcl_formatStringByCombiningCharacter( sRet
, cStrikethrough
);
440 void SwGetRefField::SetExpand( const OUString
& rStr
)
443 m_sTextRLHidden
= rStr
;
446 OUString
SwGetRefField::ExpandImpl(SwRootFrame
const*const pLayout
) const
448 return pLayout
&& pLayout
->IsHideRedlines() ? m_sTextRLHidden
: m_sText
;
451 OUString
SwGetRefField::GetFieldName() const
453 const OUString aName
= GetTyp()->GetName();
454 if ( !aName
.isEmpty() || !m_sSetRefName
.isEmpty() )
456 return aName
+ " " + m_sSetRefName
;
458 return ExpandImpl(nullptr);
462 static void FilterText(OUString
& rText
, LanguageType
const eLang
,
463 std::u16string_view rSetReferenceLanguage
)
465 // remove all special characters (replace them with blanks)
469 rText
= rText
.replaceAll(u
"\u00ad", "");
470 OUStringBuffer
aBuf(rText
);
471 const sal_Int32 l
= aBuf
.getLength();
472 for (sal_Int32 i
= 0; i
< l
; ++i
)
478 else if (aBuf
[i
] == 0x2011)
483 rText
= aBuf
.makeStringAndClear();
484 if (!rSetReferenceLanguage
.empty())
486 lcl_formatReferenceLanguage(rText
, false, eLang
, rSetReferenceLanguage
);
490 // #i81002# - parameter <pFieldTextAttr> added
491 void SwGetRefField::UpdateField( const SwTextField
* pFieldTextAttr
, SwFrame
* pFrame
)
493 SwDoc
& rDoc
= static_cast<SwGetRefFieldType
*>(GetTyp())->GetDoc();
495 for (SwRootFrame
const* const pLay
: rDoc
.GetAllLayouts())
497 if (pLay
->IsHideRedlines())
499 UpdateField(pFieldTextAttr
, pFrame
, pLay
, m_sTextRLHidden
);
503 UpdateField(pFieldTextAttr
, pFrame
, pLay
, m_sText
);
508 void SwGetRefField::UpdateField(const SwTextField
* pFieldTextAttr
, SwFrame
* pFrameContainingField
,
509 const SwRootFrame
* const pLayout
, OUString
& rText
)
511 SwDoc
& rDoc
= static_cast<SwGetRefFieldType
*>(GetTyp())->GetDoc();
515 // finding the reference target (the number)
516 sal_Int32 nNumStart
= -1;
517 sal_Int32 nNumEnd
= -1;
518 SwTextNode
* pTextNd
= SwGetRefFieldType::FindAnchor(
519 &rDoc
, m_sSetRefName
, m_nSubType
, m_nSeqNo
, m_nFlags
, &nNumStart
, &nNumEnd
,
520 pLayout
, pFieldTextAttr
? pFieldTextAttr
->GetpTextNode() : nullptr, pFrameContainingField
525 rText
= SwViewShell::GetShellRes()->aGetRefField_RefItemNotFound
;
530 // where is the category name (e.g. "Illustration")?
531 const OUString aText
= pTextNd
->GetText();
532 const sal_Int32 nCatStart
= aText
.indexOf(m_sSetRefName
);
533 const bool bHasCat
= nCatStart
>=0;
534 const sal_Int32 nCatEnd
= bHasCat
? nCatStart
+ m_sSetRefName
.getLength() : -1;
536 // length of the referenced text
537 const sal_Int32 nLen
= aText
.getLength();
540 switch( GetFormat() )
544 case REF_ONLYCAPTION
:
547 // needed part of Text
553 case REF_SEQUENCEFLD
:
555 switch( GetFormat() )
557 // "Category and Number"
560 nStart
= std::min(nNumStart
, nCatStart
);
561 nEnd
= std::max(nNumEnd
, nCatEnd
);
569 case REF_ONLYCAPTION
: {
570 // next alphanumeric character after category+number
571 if (const SwTextAttr
* const pTextAttr
=
572 pTextNd
->GetTextAttrForCharAt(nNumStart
, RES_TXTATR_FIELD
)
574 // start searching from nFrom
575 const sal_Int32 nFrom
= bHasCat
576 ? std::max(nNumStart
+ 1, nCatEnd
)
578 nStart
= SwGetExpField::GetReferenceTextPos( pTextAttr
->GetFormatField(), rDoc
, nFrom
);
580 nStart
= bHasCat
? std::max(nNumEnd
, nCatEnd
) : nNumEnd
;
589 nEnd
= std::min(nStart
+ 1, nLen
);
592 // "Reference" (whole Text)
605 // text is spread across multiple nodes - get whole text or only until end of node?
606 nEnd
= nNumEnd
<0 ? nLen
: nNumEnd
;
617 // get number or numString
618 for( size_t i
= 0; i
< rDoc
.GetFootnoteIdxs().size(); ++i
)
620 SwTextFootnote
* const pFootnoteIdx
= rDoc
.GetFootnoteIdxs()[i
];
621 if( m_nSeqNo
== pFootnoteIdx
->GetSeqRefNo() )
623 rText
= pFootnoteIdx
->GetFootnote().GetViewNumStr(rDoc
, pLayout
);
624 if (!m_sSetReferenceLanguage
.isEmpty())
626 lcl_formatReferenceLanguage(rText
, false, GetLanguage(), m_sSetReferenceLanguage
);
642 if( nStart
!= nEnd
) // a section?
644 if (pLayout
->IsHideRedlines())
646 if (m_nSubType
== REF_OUTLINE
647 || (m_nSubType
== REF_SEQUENCEFLD
&& REF_CONTENT
== GetFormat()))
649 rText
= sw::GetExpandTextMerged(pLayout
, *pTextNd
, false, false,
654 rText
= pTextNd
->GetExpandText(pLayout
, nStart
, nEnd
- nStart
, false, false,
655 false, ExpandMode::HideDeletions
);
660 rText
= pTextNd
->GetExpandText(pLayout
, nStart
, nEnd
- nStart
, false, false,
661 false, ExpandMode::HideDeletions
);
662 // show the referenced text without the deletions, but if the whole text was
663 // deleted, show the original text for the sake of the comfortable reviewing
664 // (with strikethrough in tooltip, see GetExpandedTextOfReferencedTextNode())
666 rText
= pTextNd
->GetExpandText(pLayout
, nStart
, nEnd
- nStart
, false, false,
667 false, ExpandMode(0));
669 FilterText(rText
, GetLanguage(), m_sSetReferenceLanguage
);
675 case REF_PAGE_PGDESC
:
677 SwTextFrame
const* pFrame
= static_cast<SwTextFrame
*>(pTextNd
->getLayoutFrame(pLayout
, nullptr, nullptr));
678 SwTextFrame
const*const pSave
= pFrame
;
681 TextFrameIndex
const nNumStartIndex(pFrame
->MapModelToView(pTextNd
, nNumStart
));
682 while (pFrame
&& !pFrame
->IsInside(nNumStartIndex
))
683 pFrame
= pFrame
->GetFollow();
686 if( pFrame
|| nullptr != ( pFrame
= pSave
))
688 sal_uInt16 nPageNo
= pFrame
->GetVirtPageNum();
689 const SwPageFrame
*pPage
;
690 if( REF_PAGE_PGDESC
== GetFormat() &&
691 nullptr != ( pPage
= pFrame
->FindPageFrame() ) &&
692 pPage
->GetPageDesc() )
694 rText
= pPage
->GetPageDesc()->GetNumType().GetNumStr(nPageNo
);
698 rText
= OUString::number(nPageNo
);
701 if (!m_sSetReferenceLanguage
.isEmpty())
702 lcl_formatReferenceLanguage(rText
, false, GetLanguage(), m_sSetReferenceLanguage
);
709 // a bit tricky: search any frame
710 SwFrame
const* const pFrame
= pTextNd
->getLayoutFrame(pLayout
);
713 SwChapterFieldType aFieldTyp
;
714 SwChapterField
aField(&aFieldTyp
, 0);
715 aField
.SetLevel(MAXLEVEL
- 1);
716 aField
.ChangeExpansion(*pFrame
, pTextNd
, true);
718 rText
= aField
.GetNumber(pLayout
);
720 if (!m_sSetReferenceLanguage
.isEmpty())
721 lcl_formatReferenceLanguage(rText
, false, GetLanguage(), m_sSetReferenceLanguage
);
729 // simplified: use parameter <pFieldTextAttr>
730 if( !pFieldTextAttr
|| !pFieldTextAttr
->GetpTextNode() )
733 LocaleDataWrapper
aLocaleData(( LanguageTag( GetLanguage() ) ));
735 // first a "short" test - in case both are in the same node
736 if( pFieldTextAttr
->GetpTextNode() == pTextNd
)
738 rText
= nNumStart
< pFieldTextAttr
->GetStart()
739 ? aLocaleData
.getAboveWord()
740 : aLocaleData
.getBelowWord();
744 rText
= ::IsFrameBehind( *pFieldTextAttr
->GetpTextNode(), pFieldTextAttr
->GetStart(),
745 *pTextNd
, nNumStart
)
746 ? aLocaleData
.getAboveWord()
747 : aLocaleData
.getBelowWord();
749 if (!m_sSetReferenceLanguage
.isEmpty())
750 lcl_formatReferenceLanguage(rText
, false, GetLanguage(), m_sSetReferenceLanguage
);
755 case REF_NUMBER_NO_CONTEXT
:
756 case REF_NUMBER_FULL_CONTEXT
:
758 if ( pFieldTextAttr
&& pFieldTextAttr
->GetpTextNode() )
761 MakeRefNumStr(pLayout
, pFieldTextAttr
->GetTextNode(), *pTextNd
, m_nSubType
, GetFormat(), GetFlags());
762 rText
= result
.first
;
763 // for differentiation of Roman numbers and letters in Hungarian article handling
764 bool bClosingParenthesis
= result
.second
;
765 if (!m_sSetReferenceLanguage
.isEmpty())
767 lcl_formatReferenceLanguage(rText
, bClosingParenthesis
, GetLanguage(), m_sSetReferenceLanguage
);
775 OSL_FAIL("<SwGetRefField::UpdateField(..)> - unknown format type");
781 static std::pair
<OUString
, bool> MakeRefNumStr(
782 SwRootFrame
const*const pLayout
,
783 const SwTextNode
& i_rTextNodeOfField
,
784 const SwTextNode
& i_rTextNodeOfReferencedItem
,
785 const sal_uInt16 nSubType
,
786 const sal_uInt32 nRefNumFormat
,
787 const sal_uInt16 nFlags
)
789 bool bHideNonNumerical
= (nSubType
== REF_STYLE
) && ((nFlags
& REFFLDFLAG_STYLE_HIDE_NON_NUMERICAL
) == REFFLDFLAG_STYLE_HIDE_NON_NUMERICAL
);
790 SwTextNode
const& rTextNodeOfField(pLayout
791 ? *sw::GetParaPropsNode(*pLayout
, i_rTextNodeOfField
)
792 : i_rTextNodeOfField
);
793 SwTextNode
const& rTextNodeOfReferencedItem(pLayout
794 ? *sw::GetParaPropsNode(*pLayout
, i_rTextNodeOfReferencedItem
)
795 : i_rTextNodeOfReferencedItem
);
796 if ( rTextNodeOfReferencedItem
.HasNumber(pLayout
) &&
797 rTextNodeOfReferencedItem
.IsCountedInList() )
799 OSL_ENSURE( rTextNodeOfReferencedItem
.GetNum(pLayout
),
800 "<SwGetRefField::MakeRefNumStr(..)> - referenced paragraph has number, but no <SwNodeNum> instance!" );
802 // Determine, up to which level the superior list labels have to be
803 // included - default is to include all superior list labels.
804 int nRestrictInclToThisLevel( 0 );
805 // Determine for format REF_NUMBER the level, up to which the superior
806 // list labels have to be restricted, if the text node of the reference
807 // field and the text node of the referenced item are in the same
809 if ( nRefNumFormat
== REF_NUMBER
&&
810 rTextNodeOfField
.FindFlyStartNode()
811 == rTextNodeOfReferencedItem
.FindFlyStartNode() &&
812 rTextNodeOfField
.FindFootnoteStartNode()
813 == rTextNodeOfReferencedItem
.FindFootnoteStartNode() &&
814 rTextNodeOfField
.FindHeaderStartNode()
815 == rTextNodeOfReferencedItem
.FindHeaderStartNode() &&
816 rTextNodeOfField
.FindFooterStartNode()
817 == rTextNodeOfReferencedItem
.FindFooterStartNode() )
819 const SwNodeNum
* pNodeNumForTextNodeOfField( nullptr );
820 if ( rTextNodeOfField
.HasNumber(pLayout
) &&
821 rTextNodeOfField
.GetNumRule() == rTextNodeOfReferencedItem
.GetNumRule() )
823 pNodeNumForTextNodeOfField
= rTextNodeOfField
.GetNum(pLayout
);
827 pNodeNumForTextNodeOfField
=
828 rTextNodeOfReferencedItem
.GetNum(pLayout
)->GetPrecedingNodeNumOf(rTextNodeOfField
);
830 if ( pNodeNumForTextNodeOfField
)
832 const SwNumberTree::tNumberVector rFieldNumVec
=
833 pNodeNumForTextNodeOfField
->GetNumberVector();
834 const SwNumberTree::tNumberVector rRefItemNumVec
=
835 rTextNodeOfReferencedItem
.GetNum()->GetNumberVector();
836 std::size_t nLevel( 0 );
837 while ( nLevel
< rFieldNumVec
.size() && nLevel
< rRefItemNumVec
.size() )
839 if ( rRefItemNumVec
[nLevel
] == rFieldNumVec
[nLevel
] )
841 nRestrictInclToThisLevel
= nLevel
+ 1;
852 // Determine, if superior list labels have to be included
853 const bool bInclSuperiorNumLabels(
854 ( nRestrictInclToThisLevel
< rTextNodeOfReferencedItem
.GetActualListLevel() &&
855 ( nRefNumFormat
== REF_NUMBER
|| nRefNumFormat
== REF_NUMBER_FULL_CONTEXT
) ) );
857 OSL_ENSURE( rTextNodeOfReferencedItem
.GetNumRule(),
858 "<SwGetRefField::MakeRefNumStr(..)> - referenced numbered paragraph has no numbering rule set!" );
859 return std::make_pair(
860 rTextNodeOfReferencedItem
.GetNumRule()->MakeRefNumString(
861 *(rTextNodeOfReferencedItem
.GetNum(pLayout
)),
862 bInclSuperiorNumLabels
,
863 nRestrictInclToThisLevel
,
865 rTextNodeOfReferencedItem
.GetNumRule()->MakeNumString(
866 *(rTextNodeOfReferencedItem
.GetNum(pLayout
)),
867 true).endsWith(")") );
870 return std::make_pair(OUString(), false);
873 std::unique_ptr
<SwField
> SwGetRefField::Copy() const
875 std::unique_ptr
<SwGetRefField
> pField( new SwGetRefField( static_cast<SwGetRefFieldType
*>(GetTyp()),
876 m_sSetRefName
, m_sSetReferenceLanguage
, m_nSubType
,
877 m_nSeqNo
, m_nFlags
, GetFormat() ) );
878 pField
->m_sText
= m_sText
;
879 pField
->m_sTextRLHidden
= m_sTextRLHidden
;
880 return std::unique_ptr
<SwField
>(pField
.release());
883 /// get reference name
884 OUString
SwGetRefField::GetPar1() const
886 return m_sSetRefName
;
889 /// set reference name
890 void SwGetRefField::SetPar1( const OUString
& rName
)
892 m_sSetRefName
= rName
;
895 OUString
SwGetRefField::GetPar2() const
897 return ExpandImpl(nullptr);
900 bool SwGetRefField::QueryValue( uno::Any
& rAny
, sal_uInt16 nWhichId
) const
904 case FIELD_PROP_USHORT1
:
909 case REF_PAGE
: nPart
= ReferenceFieldPart::PAGE
; break;
910 case REF_CHAPTER
: nPart
= ReferenceFieldPart::CHAPTER
; break;
911 case REF_CONTENT
: nPart
= ReferenceFieldPart::TEXT
; break;
912 case REF_UPDOWN
: nPart
= ReferenceFieldPart::UP_DOWN
; break;
913 case REF_PAGE_PGDESC
: nPart
= ReferenceFieldPart::PAGE_DESC
; break;
914 case REF_ONLYNUMBER
: nPart
= ReferenceFieldPart::CATEGORY_AND_NUMBER
; break;
915 case REF_ONLYCAPTION
: nPart
= ReferenceFieldPart::ONLY_CAPTION
; break;
916 case REF_ONLYSEQNO
: nPart
= ReferenceFieldPart::ONLY_SEQUENCE_NUMBER
; break;
918 case REF_NUMBER
: nPart
= ReferenceFieldPart::NUMBER
; break;
919 case REF_NUMBER_NO_CONTEXT
: nPart
= ReferenceFieldPart::NUMBER_NO_CONTEXT
; break;
920 case REF_NUMBER_FULL_CONTEXT
: nPart
= ReferenceFieldPart::NUMBER_FULL_CONTEXT
; break;
925 case FIELD_PROP_USHORT2
:
927 sal_Int16 nSource
= 0;
930 case REF_SETREFATTR
: nSource
= ReferenceFieldSource::REFERENCE_MARK
; break;
931 case REF_SEQUENCEFLD
: nSource
= ReferenceFieldSource::SEQUENCE_FIELD
; break;
932 case REF_BOOKMARK
: nSource
= ReferenceFieldSource::BOOKMARK
; break;
933 case REF_OUTLINE
: OSL_FAIL("not implemented"); break;
934 case REF_FOOTNOTE
: nSource
= ReferenceFieldSource::FOOTNOTE
; break;
935 case REF_ENDNOTE
: nSource
= ReferenceFieldSource::ENDNOTE
; break;
936 case REF_STYLE
: nSource
= ReferenceFieldSource::STYLE
; break;
941 case FIELD_PROP_USHORT3
:
944 case FIELD_PROP_PAR1
:
946 OUString
sTmp(GetPar1());
947 if(REF_SEQUENCEFLD
== m_nSubType
)
949 sal_uInt16 nPoolId
= SwStyleNameMapper::GetPoolIdFromUIName( sTmp
, SwGetPoolIdFromName::TxtColl
);
952 case RES_POOLCOLL_LABEL_ABB
:
953 case RES_POOLCOLL_LABEL_TABLE
:
954 case RES_POOLCOLL_LABEL_FRAME
:
955 case RES_POOLCOLL_LABEL_DRAWING
:
956 case RES_POOLCOLL_LABEL_FIGURE
:
958 ProgName
sTmp2(sTmp
);
959 SwStyleNameMapper::FillProgName(nPoolId
, sTmp2
) ;
960 sTmp
= sTmp2
.toString();
968 case FIELD_PROP_PAR3
:
969 rAny
<<= ExpandImpl(nullptr);
971 case FIELD_PROP_PAR4
:
972 rAny
<<= m_sSetReferenceLanguage
;
974 case FIELD_PROP_SHORT1
:
975 rAny
<<= static_cast<sal_Int16
>(m_nSeqNo
);
983 bool SwGetRefField::PutValue( const uno::Any
& rAny
, sal_uInt16 nWhichId
)
987 case FIELD_PROP_USHORT1
:
993 case ReferenceFieldPart::PAGE
: nPart
= REF_PAGE
; break;
994 case ReferenceFieldPart::CHAPTER
: nPart
= REF_CHAPTER
; break;
995 case ReferenceFieldPart::TEXT
: nPart
= REF_CONTENT
; break;
996 case ReferenceFieldPart::UP_DOWN
: nPart
= REF_UPDOWN
; break;
997 case ReferenceFieldPart::PAGE_DESC
: nPart
= REF_PAGE_PGDESC
; break;
998 case ReferenceFieldPart::CATEGORY_AND_NUMBER
: nPart
= REF_ONLYNUMBER
; break;
999 case ReferenceFieldPart::ONLY_CAPTION
: nPart
= REF_ONLYCAPTION
; break;
1000 case ReferenceFieldPart::ONLY_SEQUENCE_NUMBER
: nPart
= REF_ONLYSEQNO
; break;
1002 case ReferenceFieldPart::NUMBER
: nPart
= REF_NUMBER
; break;
1003 case ReferenceFieldPart::NUMBER_NO_CONTEXT
: nPart
= REF_NUMBER_NO_CONTEXT
; break;
1004 case ReferenceFieldPart::NUMBER_FULL_CONTEXT
: nPart
= REF_NUMBER_FULL_CONTEXT
; break;
1005 default: return false;
1010 case FIELD_PROP_USHORT2
:
1012 sal_Int16 nSource
= 0;
1016 case ReferenceFieldSource::REFERENCE_MARK
: m_nSubType
= REF_SETREFATTR
; break;
1017 case ReferenceFieldSource::SEQUENCE_FIELD
:
1019 if(REF_SEQUENCEFLD
== m_nSubType
)
1021 m_nSubType
= REF_SEQUENCEFLD
;
1022 ConvertProgrammaticToUIName();
1025 case ReferenceFieldSource::BOOKMARK
: m_nSubType
= REF_BOOKMARK
; break;
1026 case ReferenceFieldSource::FOOTNOTE
: m_nSubType
= REF_FOOTNOTE
; break;
1027 case ReferenceFieldSource::ENDNOTE
: m_nSubType
= REF_ENDNOTE
; break;
1028 case ReferenceFieldSource::STYLE
: m_nSubType
= REF_STYLE
; break;
1032 case FIELD_PROP_PAR1
:
1037 ConvertProgrammaticToUIName();
1040 case FIELD_PROP_PAR3
:
1044 SetExpand( sTmpStr
);
1047 case FIELD_PROP_PAR4
:
1048 rAny
>>= m_sSetReferenceLanguage
;
1050 case FIELD_PROP_USHORT3
:
1052 sal_uInt16 nSetFlags
= 0;
1054 m_nFlags
= nSetFlags
;
1057 case FIELD_PROP_SHORT1
:
1059 sal_Int16 nSetSeq
= 0;
1071 void SwGetRefField::ConvertProgrammaticToUIName()
1073 if(!(GetTyp() && REF_SEQUENCEFLD
== m_nSubType
))
1076 SwDoc
& rDoc
= static_cast<SwGetRefFieldType
*>(GetTyp())->GetDoc();
1077 const OUString rPar1
= GetPar1();
1078 // don't convert when the name points to an existing field type
1079 if (rDoc
.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp
, rPar1
, false))
1082 sal_uInt16 nPoolId
= SwStyleNameMapper::GetPoolIdFromProgName( ProgName(rPar1
), SwGetPoolIdFromName::TxtColl
);
1086 case RES_POOLCOLL_LABEL_ABB
:
1087 pResId
= STR_POOLCOLL_LABEL_ABB
;
1089 case RES_POOLCOLL_LABEL_TABLE
:
1090 pResId
= STR_POOLCOLL_LABEL_TABLE
;
1092 case RES_POOLCOLL_LABEL_FRAME
:
1093 pResId
= STR_POOLCOLL_LABEL_FRAME
;
1095 case RES_POOLCOLL_LABEL_DRAWING
:
1096 pResId
= STR_POOLCOLL_LABEL_DRAWING
;
1098 case RES_POOLCOLL_LABEL_FIGURE
:
1099 pResId
= STR_POOLCOLL_LABEL_FIGURE
;
1103 SetPar1(SwResId(pResId
));
1106 SwGetRefFieldType::SwGetRefFieldType( SwDoc
& rDc
)
1107 : SwFieldType( SwFieldIds::GetRef
), m_rDoc( rDc
)
1110 std::unique_ptr
<SwFieldType
> SwGetRefFieldType::Copy() const
1112 return std::make_unique
<SwGetRefFieldType
>( m_rDoc
);
1115 void SwGetRefFieldType::UpdateGetReferences()
1117 std::vector
<SwFormatField
*> vFields
;
1118 GatherFields(vFields
, false);
1119 for(auto pFormatField
: vFields
)
1121 // update only the GetRef fields
1122 //JP 3.4.2001: Task 71231 - we need the correct language
1123 SwGetRefField
* pGRef
= static_cast<SwGetRefField
*>(pFormatField
->GetField());
1124 const SwTextField
* pTField
;
1125 if(!pGRef
->GetLanguage() &&
1126 nullptr != (pTField
= pFormatField
->GetTextField()) &&
1127 pTField
->GetpTextNode())
1129 pGRef
->SetLanguage(pTField
->GetpTextNode()->GetLang(pTField
->GetStart()));
1133 pGRef
->UpdateField(pFormatField
->GetTextField(), nullptr);
1135 CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr));
1138 void SwGetRefFieldType::UpdateStyleReferences()
1140 std::vector
<SwFormatField
*> vFields
;
1141 GatherFields(vFields
, false);
1142 bool bModified
= false;
1143 for(auto pFormatField
: vFields
)
1145 // update only the GetRef fields which are also STYLEREF fields
1146 SwGetRefField
* pGRef
= static_cast<SwGetRefField
*>(pFormatField
->GetField());
1148 if (pGRef
->GetSubType() != REF_STYLE
) continue;
1150 const SwTextField
* pTField
;
1151 if(!pGRef
->GetLanguage() &&
1152 nullptr != (pTField
= pFormatField
->GetTextField()) &&
1153 pTField
->GetpTextNode())
1155 pGRef
->SetLanguage(pTField
->GetpTextNode()->GetLang(pTField
->GetStart()));
1158 pGRef
->UpdateField(pFormatField
->GetTextField(), nullptr);
1162 CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr));
1165 void SwGetRefFieldType::SwClientNotify(const SwModify
&, const SfxHint
& rHint
)
1167 if (rHint
.GetId() == SfxHintId::SwFormatChange
|| rHint
.GetId() == SfxHintId::SwObjectDying
)
1169 // forward to text fields, they "expand" the text
1170 CallSwClientNotify(rHint
);
1173 if (rHint
.GetId() == SfxHintId::SwAttrSetChange
)
1175 auto pChangeHint
= static_cast<const sw::AttrSetChangeHint
*>(&rHint
);
1176 if(!pChangeHint
->m_pNew
&& !pChangeHint
->m_pOld
)
1177 // update to all GetReference fields
1178 // hopefully, this codepath is soon dead code, and
1179 // UpdateGetReferences gets only called directly
1180 UpdateGetReferences();
1182 // forward to text fields, they "expand" the text
1183 CallSwClientNotify(rHint
);
1186 if (rHint
.GetId() == SfxHintId::SwUpdateAttr
)
1188 auto pChangeHint
= static_cast<const sw::UpdateAttrHint
*>(&rHint
);
1189 if(!pChangeHint
->m_pNew
&& !pChangeHint
->m_pOld
)
1190 // update to all GetReference fields
1191 // hopefully, this codepath is soon dead code, and
1192 // UpdateGetReferences gets only called directly
1193 UpdateGetReferences();
1195 // forward to text fields, they "expand" the text
1196 CallSwClientNotify(rHint
);
1199 if (rHint
.GetId() != SfxHintId::SwLegacyModify
)
1201 auto pLegacy
= static_cast<const sw::LegacyModifyHint
*>(&rHint
);
1202 if(!pLegacy
->m_pNew
&& !pLegacy
->m_pOld
)
1203 // update to all GetReference fields
1204 // hopefully, this codepath is soon dead code, and
1205 // UpdateGetReferences gets only called directly
1206 UpdateGetReferences();
1208 // forward to text fields, they "expand" the text
1209 CallSwClientNotify(rHint
);
1214 bool IsMarkHintHidden(SwRootFrame
const& rLayout
,
1215 SwTextNode
const& rNode
, SwTextAttrEnd
const& rHint
)
1217 if (!rLayout
.HasMergedParas())
1221 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(
1222 rNode
.getLayoutFrame(&rLayout
)));
1227 sal_Int32
const*const pEnd(rHint
.GetEnd());
1230 return pFrame
->MapModelToView(&rNode
, rHint
.GetStart())
1231 == pFrame
->MapModelToView(&rNode
, *pEnd
);
1235 assert(rHint
.HasDummyChar());
1236 return pFrame
->MapModelToView(&rNode
, rHint
.GetStart())
1237 == pFrame
->MapModelToView(&rNode
, rHint
.GetStart() + 1);
1245 enum StyleRefElementType
1248 Reference
, /* e.g. footnotes, endnotes */
1249 Marginal
, /* headers, footers */
1252 SwTextNode
* SearchForStyleAnchor(SwTextNode
* pSelf
, SwNode
* pCurrent
,
1253 std::u16string_view rStyleName
,
1254 sal_Int32
*const pStart
, sal_Int32
*const pEnd
,
1255 bool bCaseSensitive
= true)
1257 if (pCurrent
== pSelf
)
1260 SwTextNode
* pTextNode
= pCurrent
->GetTextNode();
1264 auto const & rFormatName
= pTextNode
->GetFormatColl()->GetName();
1266 ? rFormatName
== rStyleName
1267 : rFormatName
.equalsIgnoreAsciiCase(rStyleName
))
1272 *pEnd
= pTextNode
->GetText().getLength();
1277 if (auto const pHints
= pTextNode
->GetpSwpHints())
1279 for (size_t i
= 0, nCnt
= pHints
->Count(); i
< nCnt
; ++i
)
1281 auto const*const pHint(pHints
->Get(i
));
1282 if (pHint
->Which() == RES_TXTATR_CHARFMT
)
1285 ? pHint
->GetCharFormat().GetCharFormat()->HasName(rStyleName
)
1286 : pHint
->GetCharFormat().GetCharFormat()->GetName().equalsIgnoreAsciiCase(rStyleName
))
1288 *pStart
= pHint
->GetStart();
1291 *pEnd
= *pHint
->End();
1301 /// Picks the first text node with a matching style from the specified node range
1302 SwTextNode
* SearchForStyleAnchor(SwTextNode
* pSelf
, const SwNodes
& rNodes
, SwNodeOffset nNodeStart
, SwNodeOffset nNodeEnd
, bool bSearchBackward
,
1303 std::u16string_view rStyleName
,
1304 sal_Int32
*const pStart
, sal_Int32
*const pEnd
,
1305 bool bCaseSensitive
= true)
1307 if (!bSearchBackward
)
1309 for (SwNodeOffset nCurrent
= nNodeStart
; nCurrent
<= nNodeEnd
; ++nCurrent
)
1311 SwNode
* pCurrent
= rNodes
[nCurrent
];
1312 SwTextNode
* pFound
= SearchForStyleAnchor(pSelf
, pCurrent
, rStyleName
, pStart
, pEnd
, bCaseSensitive
);
1319 for (SwNodeOffset nCurrent
= nNodeEnd
; nCurrent
>= nNodeStart
; --nCurrent
)
1321 SwNode
* pCurrent
= rNodes
[nCurrent
];
1322 SwTextNode
* pFound
= SearchForStyleAnchor(pSelf
, pCurrent
, rStyleName
, pStart
, pEnd
, bCaseSensitive
);
1331 SwTextNode
* SwGetRefFieldType::FindAnchor(SwDoc
* pDoc
, const OUString
& rRefMark
,
1332 sal_uInt16 nSubType
, sal_uInt16 nSeqNo
, sal_uInt16 nFlags
,
1333 sal_Int32
* pStart
, sal_Int32
* pEnd
, SwRootFrame
const* const pLayout
,
1334 SwTextNode
* pSelf
, SwFrame
* pContentFrame
)
1336 assert( pStart
&& "Why did no one check the StartPos?" );
1338 IDocumentRedlineAccess
& rIDRA(pDoc
->getIDocumentRedlineAccess());
1339 SwTextNode
* pTextNd
= nullptr;
1342 case REF_SETREFATTR
:
1344 const SwFormatRefMark
*pRef
= pDoc
->GetRefMark( rRefMark
);
1345 SwTextRefMark
const*const pRefMark(pRef
? pRef
->GetTextRefMark() : nullptr);
1346 if (pRefMark
&& (!pLayout
|| !sw::IsMarkHintHidden(*pLayout
,
1347 pRefMark
->GetTextNode(), *pRefMark
)))
1349 pTextNd
= const_cast<SwTextNode
*>(&pRef
->GetTextRefMark()->GetTextNode());
1350 *pStart
= pRef
->GetTextRefMark()->GetStart();
1352 *pEnd
= pRef
->GetTextRefMark()->GetAnyEnd();
1357 case REF_SEQUENCEFLD
:
1359 SwFieldType
* pFieldType
= pDoc
->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp
, rRefMark
, false );
1360 if( pFieldType
&& pFieldType
->HasWriterListeners() &&
1361 nsSwGetSetExpType::GSE_SEQ
& static_cast<SwSetExpFieldType
*>(pFieldType
)->GetType() )
1363 std::vector
<SwFormatField
*> vFields
;
1364 pFieldType
->GatherFields(vFields
, false);
1365 for(auto pFormatField
: vFields
)
1367 SwTextField
*const pTextField(pFormatField
->GetTextField());
1368 if (pTextField
&& nSeqNo
==
1369 static_cast<SwSetExpField
*>(pFormatField
->GetField())->GetSeqNumber()
1370 && (!pLayout
|| !pLayout
->IsHideRedlines()
1371 || !sw::IsFieldDeletedInModel(rIDRA
, *pTextField
)))
1373 pTextNd
= pTextField
->GetpTextNode();
1374 *pStart
= pTextField
->GetStart();
1376 *pEnd
= (*pStart
) + 1;
1386 auto ppMark
= pDoc
->getIDocumentMarkAccess()->findMark(rRefMark
);
1387 if (ppMark
!= pDoc
->getIDocumentMarkAccess()->getAllMarksEnd()
1388 && (!pLayout
|| !pLayout
->IsHideRedlines()
1389 || !sw::IsMarkHidden(*pLayout
, **ppMark
)))
1391 const ::sw::mark::MarkBase
* pBkmk
= *ppMark
;
1392 const SwPosition
* pPos
= &pBkmk
->GetMarkStart();
1394 pTextNd
= pPos
->GetNode().GetTextNode();
1395 *pStart
= pPos
->GetContentIndex();
1398 if(!pBkmk
->IsExpanded())
1402 if(dynamic_cast< ::sw::mark::CrossRefBookmark
const *>(pBkmk
))
1405 "<SwGetRefFieldType::FindAnchor(..)> - node marked by cross-reference bookmark isn't a text node --> crash");
1406 *pEnd
= pTextNd
->Len();
1409 else if(pBkmk
->GetOtherMarkPos().GetNode() == pBkmk
->GetMarkPos().GetNode())
1410 *pEnd
= pBkmk
->GetMarkEnd().GetContentIndex();
1424 for( auto pFootnoteIdx
: pDoc
->GetFootnoteIdxs() )
1425 if( nSeqNo
== pFootnoteIdx
->GetSeqRefNo() )
1427 if (pLayout
&& pLayout
->IsHideRedlines()
1428 && sw::IsFootnoteDeleted(rIDRA
, *pFootnoteIdx
))
1432 // otherwise: the position at the start of the footnote
1433 // will be mapped to something visible at least...
1434 const SwNodeIndex
* pIdx
= pFootnoteIdx
->GetStartNode();
1437 SwNodeIndex
aIdx( *pIdx
, 1 );
1438 pTextNd
= aIdx
.GetNode().GetTextNode();
1439 if( nullptr == pTextNd
)
1440 pTextNd
= static_cast<SwTextNode
*>(SwNodes::GoNext(&aIdx
));
1450 pTextNd
= FindAnchorRefStyle(pDoc
, rRefMark
, nFlags
,
1451 pStart
, pEnd
, pLayout
, pSelf
, pContentFrame
);
1458 SwTextNode
* SwGetRefFieldType::FindAnchorRefStyle(SwDoc
* pDoc
, const OUString
& rRefMark
,
1460 sal_Int32
* pStart
, sal_Int32
* pEnd
, SwRootFrame
const* const pLayout
,
1461 SwTextNode
* pSelf
, SwFrame
* pContentFrame
)
1466 SwTextNode
* pTextNd
= nullptr;
1468 StyleRefElementType elementType
= StyleRefElementType::Default
;
1469 const SwTextNode
* pReference
= nullptr;
1470 IDocumentRedlineAccess
& rIDRA(pDoc
->getIDocumentRedlineAccess());
1472 /* Check if we're a footnote/endnote */
1473 for (SwTextFootnote
* pFootnoteIdx
: pDoc
->GetFootnoteIdxs())
1475 if (pLayout
&& pLayout
->IsHideRedlines()
1476 && sw::IsFootnoteDeleted(rIDRA
, *pFootnoteIdx
))
1480 const SwNodeIndex
* pIdx
= pFootnoteIdx
->GetStartNode();
1483 SwNodeIndex
aIdx(*pIdx
, 1);
1484 SwTextNode
* pFootnoteNode
= aIdx
.GetNode().GetTextNode();
1485 if (nullptr == pFootnoteNode
)
1486 pFootnoteNode
= static_cast<SwTextNode
*>(SwNodes::GoNext(&aIdx
));
1488 if (*pSelf
== *pFootnoteNode
)
1490 elementType
= StyleRefElementType::Reference
;
1491 pReference
= &pFootnoteIdx
->GetTextNode();
1496 if (pDoc
->IsInHeaderFooter(*pSelf
))
1498 elementType
= StyleRefElementType::Marginal
;
1501 if (pReference
== nullptr)
1506 // undocumented Word feature: 1 = "Heading 1" etc.
1507 OUString
const styleName(
1508 (rRefMark
.getLength() == 1 && '1' <= rRefMark
[0] && rRefMark
[0] <= '9')
1509 ? SwStyleNameMapper::GetProgName(RES_POOLCOLL_HEADLINE1
+ rRefMark
[0] - '1', rRefMark
).toString()
1512 switch (elementType
)
1515 pTextNd
= FindAnchorRefStyleMarginal(pDoc
, nFlags
,
1516 pStart
, pEnd
, pSelf
, pContentFrame
, pReference
, styleName
);
1520 pTextNd
= FindAnchorRefStyleOther(pDoc
,
1521 pStart
, pEnd
, pSelf
, pReference
, styleName
);
1524 OSL_FAIL("<SwGetRefFieldType::FindAnchorRefStyle(..)> - unknown getref element type");
1529 SwTextNode
* SwGetRefFieldType::FindAnchorRefStyleMarginal(SwDoc
* pDoc
,
1531 sal_Int32
* pStart
, sal_Int32
* pEnd
,
1532 SwTextNode
* pSelf
, SwFrame
* pContentFrame
,
1533 const SwTextNode
* pReference
, std::u16string_view styleName
)
1535 // For marginals, styleref tries to act on the current page first
1536 // 1. Get the page we're on, search it from top to bottom
1538 SwTextNode
* pTextNd
= nullptr;
1540 bool bFlagFromBottom
= (nFlags
& REFFLDFLAG_STYLE_FROM_BOTTOM
) == REFFLDFLAG_STYLE_FROM_BOTTOM
;
1543 std::pair
<Point
, bool> const tmp(aPt
, false);
1545 if (!pContentFrame
) SAL_WARN("xmloff.text", "<SwGetRefFieldType::FindAnchorRefStyleMarginal(..)>: Missing content frame for marginal styleref");
1546 const SwPageFrame
* pPageFrame
= nullptr;
1549 pPageFrame
= pContentFrame
->FindPageFrame();
1551 const SwNode
* pPageStart(nullptr);
1552 const SwNode
* pPageEnd(nullptr);
1556 const SwContentFrame
* pPageStartFrame
= pPageFrame
->FindFirstBodyContent();
1557 const SwContentFrame
* pPageEndFrame
= pPageFrame
->FindLastBodyContent();
1559 if (pPageStartFrame
)
1561 if (pPageStartFrame
->IsTextFrame())
1563 pPageStart
= static_cast<const SwTextFrame
*>(pPageStartFrame
)
1564 ->GetTextNodeFirst();
1569 = static_cast<const SwNoTextFrame
*>(pPageStartFrame
)->GetNode();
1575 if (pPageEndFrame
->IsTextFrame())
1577 pPageEnd
= static_cast<const SwTextFrame
*>(pPageEndFrame
)
1578 ->GetTextNodeFirst();
1582 pPageEnd
= static_cast<const SwNoTextFrame
*>(pPageEndFrame
)->GetNode();
1587 if (!pPageStart
|| !pPageEnd
)
1589 pPageStart
= pReference
;
1590 pPageEnd
= pReference
;
1593 SwNodeOffset nPageStart
= pPageStart
->GetIndex();
1594 SwNodeOffset nPageEnd
= pPageEnd
->GetIndex();
1595 const SwNodes
& nodes
= pDoc
->GetNodes();
1597 pTextNd
= SearchForStyleAnchor(pSelf
, nodes
, nPageStart
, nPageEnd
, bFlagFromBottom
, styleName
, pStart
, pEnd
);
1601 // 2. Search up from the top of the page
1602 pTextNd
= SearchForStyleAnchor(pSelf
, nodes
, SwNodeOffset(0), nPageStart
- 1, /*bBackwards*/true, styleName
, pStart
, pEnd
);
1606 // 3. Search down from the bottom of the page
1607 pTextNd
= SearchForStyleAnchor(pSelf
, nodes
, nPageEnd
+ 1, nodes
.Count() - 1, /*bBackwards*/false, styleName
, pStart
, pEnd
);
1611 // Word has case insensitive styles. LO has case sensitive styles. If we didn't find
1612 // it yet, maybe we could with a case insensitive search. Let's do that
1614 pTextNd
= SearchForStyleAnchor(pSelf
, nodes
, nPageStart
, nPageEnd
, bFlagFromBottom
, styleName
, pStart
, pEnd
,
1615 false /* bCaseSensitive */);
1619 pTextNd
= SearchForStyleAnchor(pSelf
, nodes
, SwNodeOffset(0), nPageStart
- 1, /*bBackwards*/true, styleName
, pStart
, pEnd
,
1620 false /* bCaseSensitive */);
1624 pTextNd
= SearchForStyleAnchor(pSelf
, nodes
, nPageEnd
+ 1, nodes
.Count() - 1, /*bBackwards*/false, styleName
, pStart
, pEnd
,
1625 false /* bCaseSensitive */);
1629 SwTextNode
* SwGetRefFieldType::FindAnchorRefStyleOther(SwDoc
* pDoc
,
1630 sal_Int32
* pStart
, sal_Int32
* pEnd
,
1632 const SwTextNode
* pReference
, std::u16string_view styleName
)
1634 // Normally, styleref does searches around the field position
1635 // For references, styleref acts from the position of the reference not the field
1636 // Happily, the previous code saves either one into pReference, so the following is generic for both
1638 const SwNodes
& nodes
= pDoc
->GetNodes();
1640 // It is possible to end up here, with a pReference pointer which points to a node which has already been
1641 // removed from the nodes array, which means that calling GetIndex() returns an incorrect index.
1642 SwNodeOffset nReference
;
1643 if (!pReference
->IsDisconnected())
1644 nReference
= pReference
->GetIndex();
1646 nReference
= nodes
.Count() - 1;
1648 SwTextNode
* pTextNd
= nullptr;
1650 // 1. Search up until we hit the top of the document
1652 pTextNd
= SearchForStyleAnchor(pSelf
, nodes
, SwNodeOffset(0), nReference
, /*bBackwards*/true, styleName
, pStart
, pEnd
);
1656 // 2. Search down until we hit the bottom of the document
1658 pTextNd
= SearchForStyleAnchor(pSelf
, nodes
, nReference
+ 1, nodes
.Count() - 1, /*bBackwards*/false, styleName
, pStart
, pEnd
);
1662 // Again, we need to remember that Word styles are not case sensitive
1664 pTextNd
= SearchForStyleAnchor(pSelf
, nodes
, SwNodeOffset(0), nReference
, /*bBackwards*/true, styleName
, pStart
, pEnd
,
1665 false /* bCaseSensitive */);
1669 pTextNd
= SearchForStyleAnchor(pSelf
, nodes
, nReference
+ 1, nodes
.Count() - 1, /*bBackwards*/false, styleName
, pStart
, pEnd
,
1670 false /* bCaseSensitive */);
1680 std::set
<sal_uInt16
> aIds
;
1681 std::set
<sal_uInt16
> aDstIds
;
1682 std::map
<sal_uInt16
, sal_uInt16
> sequencedIds
; /// ID numbers sorted by sequence number.
1685 void Init(SwDoc
& rDoc
, SwDoc
& rDestDoc
, bool bField
);
1686 static void GetNoteIdsFromDoc( SwDoc
& rDoc
, std::set
<sal_uInt16
> &rIds
);
1687 void GetFieldIdsFromDoc( SwDoc
& rDoc
, std::set
<sal_uInt16
> &rIds
);
1688 void AddId( sal_uInt16 id
, sal_uInt16 seqNum
);
1689 static sal_uInt16
GetFirstUnusedId( std::set
<sal_uInt16
> &rIds
);
1692 explicit RefIdsMap( OUString _aName
) : aName(std::move( _aName
)), bInit( false ) {}
1694 void Check( SwDoc
& rDoc
, SwDoc
& rDestDoc
, SwGetRefField
& rField
, bool bField
);
1696 const OUString
& GetName() const { return aName
; }
1701 /// Get a sorted list of the field IDs from a document.
1702 /// @param[in] rDoc The document to search.
1703 /// @param[in,out] rIds The list of IDs found in the document.
1704 void RefIdsMap::GetFieldIdsFromDoc( SwDoc
& rDoc
, std::set
<sal_uInt16
> &rIds
)
1706 SwFieldType
*const pType
= rDoc
.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp
, aName
, false);
1709 std::vector
<SwFormatField
*> vFields
;
1710 pType
->GatherFields(vFields
);
1711 for(const auto pF
: vFields
)
1712 rIds
.insert(static_cast<SwSetExpField
const*>(pF
->GetField())->GetSeqNumber());
1715 /// Get a sorted list of the footnote/endnote IDs from a document.
1716 /// @param[in] rDoc The document to search.
1717 /// @param[in,out] rIds The list of IDs found in the document.
1718 void RefIdsMap::GetNoteIdsFromDoc( SwDoc
& rDoc
, std::set
<sal_uInt16
> &rIds
)
1720 for( auto n
= rDoc
.GetFootnoteIdxs().size(); n
; )
1721 rIds
.insert( rDoc
.GetFootnoteIdxs()[ --n
]->GetSeqRefNo() );
1724 /// Initialise the aIds and aDestIds collections from the source documents.
1725 /// @param[in] rDoc The source document.
1726 /// @param[in] rDestDoc The destination document.
1727 /// @param[in] bField True if we're interested in all fields, false for footnotes.
1728 void RefIdsMap::Init( SwDoc
& rDoc
, SwDoc
& rDestDoc
, bool bField
)
1735 GetFieldIdsFromDoc( rDestDoc
, aIds
);
1736 GetFieldIdsFromDoc( rDoc
, aDstIds
);
1738 // Map all the new src fields to the next available unused id
1739 for (const auto& rId
: aDstIds
)
1740 AddId( GetFirstUnusedId(aIds
), rId
);
1742 // Change the Sequence number of all SetExp fields in the source document
1743 SwFieldType
* pType
= rDoc
.getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp
, aName
, false );
1746 std::vector
<SwFormatField
*> vFields
;
1747 pType
->GatherFields(vFields
, false);
1748 for(auto pF
: vFields
)
1750 if(!pF
->GetTextField())
1752 SwSetExpField
*const pSetExp(static_cast<SwSetExpField
*>(pF
->GetField()));
1753 sal_uInt16
const n
= pSetExp
->GetSeqNumber();
1754 pSetExp
->SetSeqNumber(sequencedIds
[n
]);
1760 GetNoteIdsFromDoc( rDestDoc
, aIds
);
1761 GetNoteIdsFromDoc( rDoc
, aDstIds
);
1763 for (const auto& rId
: aDstIds
)
1764 AddId( GetFirstUnusedId(aIds
), rId
);
1766 // Change the footnotes/endnotes in the source doc to the new ID
1767 for ( const auto pFootnoteIdx
: rDoc
.GetFootnoteIdxs() )
1769 sal_uInt16
const n
= pFootnoteIdx
->GetSeqRefNo();
1770 pFootnoteIdx
->SetSeqNo(sequencedIds
[n
]);
1776 /// Get the lowest number unused in the passed set.
1777 /// @param[in] rIds The set of used ID numbers.
1778 /// @returns The lowest number unused by the passed set
1779 sal_uInt16
RefIdsMap::GetFirstUnusedId( std::set
<sal_uInt16
> &rIds
)
1783 for( const auto& rId
: rIds
)
1794 /// Add a new ID and sequence number to the "occupied" collection.
1795 /// @param[in] id The ID number.
1796 /// @param[in] seqNum The sequence number.
1797 void RefIdsMap::AddId( sal_uInt16 id
, sal_uInt16 seqNum
)
1800 sequencedIds
[ seqNum
] = id
;
1803 void RefIdsMap::Check( SwDoc
& rDoc
, SwDoc
& rDestDoc
, SwGetRefField
& rField
,
1806 Init( rDoc
, rDestDoc
, bField
);
1808 sal_uInt16
const nSeqNo
= rField
.GetSeqNo();
1810 // check if it needs to be remapped
1811 // if sequencedIds doesn't contain the number, it means there is no
1812 // SetExp field / footnote in the source document: do not modify
1813 // the number, which works well for copy from/paste to same document
1814 // (and if it is not the same document, there's no "correct" result anyway)
1815 if (sequencedIds
.count(nSeqNo
))
1817 rField
.SetSeqNo( sequencedIds
[nSeqNo
] );
1821 /// 1. if _both_ SetExp + GetExp / Footnote + GetExp field are copied,
1822 /// ensure that both get a new unused matching number
1823 /// 2. if only SetExp / Footnote is copied, it gets a new unused number
1824 /// 3. if only GetExp field is copied, for the case of copy from / paste to
1825 /// same document it's desirable to keep the same number;
1826 /// for other cases of copy/paste or master documents it's not obvious
1827 /// what is most desirable since it's going to be wrong anyway
1828 void SwGetRefFieldType::MergeWithOtherDoc( SwDoc
& rDestDoc
)
1830 if (&rDestDoc
== &m_rDoc
)
1833 if (rDestDoc
.IsClipBoard())
1835 // when copying _to_ clipboard, expectation is that no fields exist
1836 // so no re-mapping is required to avoid collisions
1837 assert(!rDestDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef
)->HasWriterListeners());
1838 return; // don't modify the fields in the source doc
1841 // then there are RefFields in the DescDox - so all RefFields in the SourceDoc
1842 // need to be converted to have unique IDs for both documents
1843 RefIdsMap aFntMap
{ OUString() };
1844 std::vector
<std::unique_ptr
<RefIdsMap
>> aFieldMap
;
1846 std::vector
<SwFormatField
*> vFields
;
1847 GatherFields(vFields
);
1848 for(auto pField
: vFields
)
1850 SwGetRefField
& rRefField
= *static_cast<SwGetRefField
*>(pField
->GetField());
1851 switch( rRefField
.GetSubType() )
1853 case REF_SEQUENCEFLD
:
1855 RefIdsMap
* pMap
= nullptr;
1856 for( auto n
= aFieldMap
.size(); n
; )
1858 if (aFieldMap
[ --n
]->GetName() == rRefField
.GetSetRefName())
1860 pMap
= aFieldMap
[ n
].get();
1866 pMap
= new RefIdsMap( rRefField
.GetSetRefName() );
1867 aFieldMap
.push_back(std::unique_ptr
<RefIdsMap
>(pMap
));
1870 pMap
->Check(m_rDoc
, rDestDoc
, rRefField
, true);
1876 aFntMap
.Check(m_rDoc
, rDestDoc
, rRefField
, false);
1882 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */