diff options
Diffstat (limited to 'sw/source/core/fields/reffld.cxx')
-rw-r--r-- | sw/source/core/fields/reffld.cxx | 1462 |
1 files changed, 1462 insertions, 0 deletions
diff --git a/sw/source/core/fields/reffld.cxx b/sw/source/core/fields/reffld.cxx new file mode 100644 index 000000000..db2cdc3bf --- /dev/null +++ b/sw/source/core/fields/reffld.cxx @@ -0,0 +1,1462 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/text/ReferenceFieldPart.hpp> +#include <com/sun/star/text/ReferenceFieldSource.hpp> +#include <o3tl/unreachable.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/charclass.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <pam.hxx> +#include <cntfrm.hxx> +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <modeltoviewhelper.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <txtftn.hxx> +#include <fmtrfmrk.hxx> +#include <txtrfmrk.hxx> +#include <fmtftn.hxx> +#include <ndtxt.hxx> +#include <chpfld.hxx> +#include <reffld.hxx> +#include <expfld.hxx> +#include <txtfrm.hxx> +#include <flyfrm.hxx> +#include <pagedesc.hxx> +#include <IMark.hxx> +#include <crossrefbookmark.hxx> +#include <ftnidx.hxx> +#include <viewsh.hxx> +#include <unofldmid.h> +#include <SwStyleNameMapper.hxx> +#include <shellres.hxx> +#include <poolfmt.hxx> +#include <strings.hrc> +#include <numrule.hxx> +#include <SwNodeNum.hxx> +#include <calbck.hxx> + +#include <cstddef> +#include <memory> +#include <vector> +#include <set> +#include <map> +#include <algorithm> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::lang; + +static std::pair<OUString, bool> MakeRefNumStr(SwRootFrame const* pLayout, + const SwTextNode& rTextNodeOfField, + const SwTextNode& rTextNodeOfReferencedItem, + sal_uInt32 nRefNumFormat); + +static void lcl_GetLayTree( const SwFrame* pFrame, std::vector<const SwFrame*>& rArr ) +{ + while( pFrame ) + { + if( pFrame->IsBodyFrame() ) // unspectacular + pFrame = pFrame->GetUpper(); + else + { + rArr.push_back( pFrame ); + + // this is the last page + if( pFrame->IsPageFrame() ) + break; + + if( pFrame->IsFlyFrame() ) + pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame(); + else + pFrame = pFrame->GetUpper(); + } + } +} + +bool IsFrameBehind( const SwTextNode& rMyNd, sal_Int32 nMySttPos, + const SwTextNode& rBehindNd, sal_Int32 nSttPos ) +{ + const SwTextFrame * pMyFrame = static_cast<SwTextFrame*>(rMyNd.getLayoutFrame( + rMyNd.GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)); + const SwTextFrame * pFrame = static_cast<SwTextFrame*>(rBehindNd.getLayoutFrame( + rBehindNd.GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)); + + if( !pFrame || !pMyFrame) + return false; + + TextFrameIndex const nMySttPosIndex(pMyFrame->MapModelToView(&rMyNd, nMySttPos)); + TextFrameIndex const nSttPosIndex(pFrame->MapModelToView(&rBehindNd, nSttPos)); + while (pFrame && !pFrame->IsInside(nSttPosIndex)) + pFrame = pFrame->GetFollow(); + while (pMyFrame && !pMyFrame->IsInside(nMySttPosIndex)) + pMyFrame = pMyFrame->GetFollow(); + + if( !pFrame || !pMyFrame || pFrame == pMyFrame ) + return false; + + std::vector<const SwFrame*> aRefArr, aArr; + ::lcl_GetLayTree( pFrame, aRefArr ); + ::lcl_GetLayTree( pMyFrame, aArr ); + + size_t nRefCnt = aRefArr.size() - 1, nCnt = aArr.size() - 1; + bool bVert = false; + bool bR2L = false; + + // Loop as long as a frame does not equal? + while( nRefCnt && nCnt && aRefArr[ nRefCnt ] == aArr[ nCnt ] ) + { + const SwFrame* pTmpFrame = aArr[ nCnt ]; + bVert = pTmpFrame->IsVertical(); + bR2L = pTmpFrame->IsRightToLeft(); + --nCnt; + --nRefCnt; + } + + // If a counter overflows? + if( aRefArr[ nRefCnt ] == aArr[ nCnt ] ) + { + if( nCnt ) + --nCnt; + else + --nRefCnt; + } + + const SwFrame* pRefFrame = aRefArr[ nRefCnt ]; + const SwFrame* pFieldFrame = aArr[ nCnt ]; + + // different frames, check their Y-/X-position + bool bRefIsLower = false; + if( ( SwFrameType::Column | SwFrameType::Cell ) & pFieldFrame->GetType() || + ( SwFrameType::Column | SwFrameType::Cell ) & pRefFrame->GetType() ) + { + if( pFieldFrame->GetType() == pRefFrame->GetType() ) + { + // here, the X-pos is more important + if( bVert ) + { + if( bR2L ) + bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || + ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && + pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() ); + else + bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || + ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && + pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() ); + } + else if( bR2L ) + bRefIsLower = pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() || + ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && + pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); + else + bRefIsLower = pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() || + ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && + pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); + pRefFrame = nullptr; + } + else if( ( SwFrameType::Column | SwFrameType::Cell ) & pFieldFrame->GetType() ) + pFieldFrame = aArr[ nCnt - 1 ]; + else + pRefFrame = aRefArr[ nRefCnt - 1 ]; + } + + if( pRefFrame ) // misuse as flag + { + if( bVert ) + { + if( bR2L ) + bRefIsLower = pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() || + ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && + pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); + else + bRefIsLower = pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() || + ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && + pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); + } + else if( bR2L ) + bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || + ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && + pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() ); + else + bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || + ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && + pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() ); + } + return bRefIsLower; +} + +// tdf#115319 create alternative reference formats, if the user asked for it +// (ReferenceFieldLanguage attribute of the reference field is not empty), and +// language of the text and ReferenceFieldLanguage are the same. +// Right now only HUNGARIAN seems to need this (as in the related issue, +// the reversed caption order in autocaption, solved by #i61007#) +static void lcl_formatReferenceLanguage( OUString& rRefText, + bool bClosingParenthesis, LanguageType eLang, + const OUString& rReferenceLanguage) +{ + if (eLang != LANGUAGE_HUNGARIAN || (rReferenceLanguage != "hu" && rReferenceLanguage != "Hu")) + return; + + // Add Hungarian definitive article (a/az) before references, + // similar to \aref, \apageref etc. of LaTeX Babel package. + // + // for example: + // + // "az 1. oldalon" ("on page 1"), but + // "a 2. oldalon" ("on page 2") + // "a fentebbi", "az alábbi" (above/below) + // "a Lorem", "az Ipsum" + // + // Support following numberings of EU publications: + // + // 1., 1a., a), (1), (1a), iii., III., IA. + // + // (http://publications.europa.eu/code/hu/hu-120700.htm, + // http://publications.europa.eu/code/hu/hu-4100600.htm) + + LanguageTag aLanguageTag(eLang); + CharClass aCharClass( aLanguageTag ); + sal_Int32 nLen = rRefText.getLength(); + sal_Int32 i; + // substring of rRefText starting with letter or number + OUString sNumbering; + // is article "az"? + bool bArticleAz = false; + // is numbering a number? + bool bNum = false; + + // search first member of the numbering (numbers or letters) + for (i=0; i<nLen && (sNumbering.isEmpty() || + ((bNum && aCharClass.isDigit(rRefText, i)) || + (!bNum && aCharClass.isLetter(rRefText, i)))); ++i) + { + // start of numbering within the field text + if (sNumbering.isEmpty() && aCharClass.isLetterNumeric(rRefText, i)) { + sNumbering = rRefText.copy(i); + bNum = aCharClass.isDigit(rRefText, i); + } + } + + // length of numbering + nLen = i - (rRefText.getLength() - sNumbering.getLength()); + + if (bNum) + { + // az 1, 1000, 1000000, 1000000000... + // az 5, 50, 500... + if ((sNumbering.startsWith("1") && (nLen == 1 || nLen == 4 || nLen == 7 || nLen == 10)) || + sNumbering.startsWith("5")) + bArticleAz = true; + } + else if (nLen == 1 && sNumbering[0] < 128) + { + // ASCII 1-letter numbering + // az a), e), f) ... x) + // az i., v. (but, a x.) + static const OUString sLettersStartingWithVowels = "aefilmnorsuxyAEFILMNORSUXY"; + if (sLettersStartingWithVowels.indexOf(sNumbering[0]) != -1) + { + // x), X) are letters, but x. and X. etc. are Roman numbers + if (bClosingParenthesis || + (sNumbering[0] != 'x' && sNumbering[0] != 'X')) + bArticleAz = true; + } else if ((sNumbering[0] == 'v' || sNumbering[0] == 'V') && !bClosingParenthesis) + // v), V) are letters, but v. and V. are Roman numbers + bArticleAz = true; + } + else + { + static const sal_Unicode sVowelsWithDiacritic[] = { + 0x00E1, 0x00C1, 0x00E9, 0x00C9, 0x00ED, 0x00CD, + 0x00F3, 0x00D3, 0x00F6, 0x00D6, 0x0151, 0x0150, + 0x00FA, 0x00DA, 0x00FC, 0x00DC, 0x0171, 0x0170, 0 }; + static OUString sVowels = OUStringLiteral("aAeEiIoOuU") + sVowelsWithDiacritic; + + // handle more than 1-letter long Roman numbers and + // their possible combinations with letters: + // az IA, a IIB, a IIIC., az Ia, a IIb., a iiic), az LVIII. szonett + bool bRomanNumber = false; + if (nLen > 1 && (nLen + 1 >= sNumbering.getLength() || sNumbering[nLen] == '.')) + { + sal_Unicode last = sNumbering[nLen - 1]; + OUString sNumberingTrim; + if ((last >= 'A' && last < 'I') || (last >= 'a' && last < 'i')) + sNumberingTrim = sNumbering.copy(0, nLen - 1); + else + sNumberingTrim = sNumbering.copy(0, nLen); + bRomanNumber = + sNumberingTrim.replaceAll("i", "").replaceAll("v", "").replaceAll("x", "").replaceAll("l", "").replaceAll("c", "").isEmpty() || + sNumberingTrim.replaceAll("I", "").replaceAll("V", "").replaceAll("X", "").replaceAll("L", "").replaceAll("C", "").isEmpty(); + } + + if ( + // Roman number and a letter optionally + ( bRomanNumber && ( + (sNumbering[0] == 'i' && sNumbering[1] != 'i' && sNumbering[1] != 'v' && sNumbering[1] != 'x') || + (sNumbering[0] == 'I' && sNumbering[1] != 'I' && sNumbering[1] != 'V' && sNumbering[1] != 'X') || + (sNumbering[0] == 'v' && sNumbering[1] != 'i') || + (sNumbering[0] == 'V' && sNumbering[1] != 'I') || + (sNumbering[0] == 'l' && sNumbering[1] != 'x') || + (sNumbering[0] == 'L' && sNumbering[1] != 'X')) ) || + // a word starting with vowel (not Roman number) + ( !bRomanNumber && sVowels.indexOf(sNumbering[0]) != -1)) + { + bArticleAz = true; + } + } + // not a title text starting already with a definitive article + if ( !sNumbering.startsWith("A ") && !sNumbering.startsWith("Az ") && + !sNumbering.startsWith("a ") && !sNumbering.startsWith("az ") ) + { + // lowercase, if rReferenceLanguage == "hu", not "Hu" + OUString sArticle; + + if ( rReferenceLanguage == "hu" ) + sArticle = "a"; + else + sArticle = "A"; + + if (bArticleAz) + sArticle += "z"; + + rRefText = sArticle + " " + rRefText; + } +} + +/// get references +SwGetRefField::SwGetRefField( SwGetRefFieldType* pFieldType, + const OUString& rSetRef, const OUString& rSetReferenceLanguage, sal_uInt16 nSubTyp, + sal_uInt16 nSequenceNo, sal_uLong nFormat ) + : SwField( pFieldType, nFormat ), + m_sSetRefName( rSetRef ), + m_sSetReferenceLanguage( rSetReferenceLanguage ), + m_nSubType( nSubTyp ), + m_nSeqNo( nSequenceNo ) +{ +} + +SwGetRefField::~SwGetRefField() +{ +} + +OUString SwGetRefField::GetDescription() const +{ + return SwResId(STR_REFERENCE); +} + +sal_uInt16 SwGetRefField::GetSubType() const +{ + return m_nSubType; +} + +void SwGetRefField::SetSubType( sal_uInt16 n ) +{ + m_nSubType = n; +} + +// #i81002# +bool SwGetRefField::IsRefToHeadingCrossRefBookmark() const +{ + return GetSubType() == REF_BOOKMARK && + ::sw::mark::CrossRefHeadingBookmark::IsLegalName(m_sSetRefName); +} + +bool SwGetRefField::IsRefToNumItemCrossRefBookmark() const +{ + return GetSubType() == REF_BOOKMARK && + ::sw::mark::CrossRefNumItemBookmark::IsLegalName(m_sSetRefName); +} + +const SwTextNode* SwGetRefField::GetReferencedTextNode() const +{ + SwGetRefFieldType *pTyp = dynamic_cast<SwGetRefFieldType*>(GetTyp()); + if (!pTyp) + return nullptr; + sal_Int32 nDummy = -1; + return SwGetRefFieldType::FindAnchor( pTyp->GetDoc(), m_sSetRefName, m_nSubType, m_nSeqNo, &nDummy ); +} + +// #i85090# +OUString SwGetRefField::GetExpandedTextOfReferencedTextNode( + SwRootFrame const& rLayout) const +{ + const SwTextNode* pReferencedTextNode( GetReferencedTextNode() ); + return pReferencedTextNode + ? sw::GetExpandTextMerged(&rLayout, *pReferencedTextNode, true, false, ExpandMode(0)) + : OUString(); +} + +void SwGetRefField::SetExpand( const OUString& rStr ) +{ + m_sText = rStr; + m_sTextRLHidden = rStr; +} + +OUString SwGetRefField::ExpandImpl(SwRootFrame const*const pLayout) const +{ + return pLayout && pLayout->IsHideRedlines() ? m_sTextRLHidden : m_sText; +} + +OUString SwGetRefField::GetFieldName() const +{ + const OUString aName = GetTyp()->GetName(); + if ( !aName.isEmpty() || !m_sSetRefName.isEmpty() ) + { + return aName + " " + m_sSetRefName; + } + return ExpandImpl(nullptr); +} + + +static void FilterText(OUString & rText, LanguageType const eLang, + OUString const& rSetReferenceLanguage) +{ + // remove all special characters (replace them with blanks) + if (!rText.isEmpty()) + { + rText = rText.replaceAll(u"\u00ad", ""); + OUStringBuffer aBuf(rText); + const sal_Int32 l = aBuf.getLength(); + for (sal_Int32 i = 0; i < l; ++i) + { + if (aBuf[i] < ' ') + { + aBuf[i] = ' '; + } + else if (aBuf[i] == 0x2011) + { + aBuf[i] = '-'; + } + } + rText = aBuf.makeStringAndClear(); + if (!rSetReferenceLanguage.isEmpty()) + { + lcl_formatReferenceLanguage(rText, false, eLang, rSetReferenceLanguage); + } + } +} + +// #i81002# - parameter <pFieldTextAttr> added +void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr ) +{ + m_sText.clear(); + m_sTextRLHidden.clear(); + + SwDoc* pDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc(); + // finding the reference target (the number) + sal_Int32 nNumStart = -1; + sal_Int32 nNumEnd = -1; + SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor( + pDoc, m_sSetRefName, m_nSubType, m_nSeqNo, &nNumStart, &nNumEnd + ); + // not found? + if ( !pTextNd ) + { + m_sText = SwViewShell::GetShellRes()->aGetRefField_RefItemNotFound; + m_sTextRLHidden = m_sText; + return ; + } + + SwRootFrame const* pLayout(nullptr); + SwRootFrame const* pLayoutRLHidden(nullptr); + for (SwRootFrame const*const pLay : pDoc->GetAllLayouts()) + { + if (pLay->IsHideRedlines()) + { + pLayoutRLHidden = pLay; + } + else + { + pLayout = pLay; + } + } + + // where is the category name (e.g. "Illustration")? + const OUString aText = pTextNd->GetText(); + const sal_Int32 nCatStart = aText.indexOf(m_sSetRefName); + const bool bHasCat = nCatStart>=0; + const sal_Int32 nCatEnd = bHasCat ? nCatStart + m_sSetRefName.getLength() : -1; + + // length of the referenced text + const sal_Int32 nLen = aText.getLength(); + + // which format? + switch( GetFormat() ) + { + case REF_CONTENT: + case REF_ONLYNUMBER: + case REF_ONLYCAPTION: + case REF_ONLYSEQNO: + { + // needed part of Text + sal_Int32 nStart; + sal_Int32 nEnd; + + switch( m_nSubType ) + { + case REF_SEQUENCEFLD: + + switch( GetFormat() ) + { + // "Category and Number" + case REF_ONLYNUMBER: + if (bHasCat) { + nStart = std::min(nNumStart, nCatStart); + nEnd = std::max(nNumEnd, nCatEnd); + } else { + nStart = nNumStart; + nEnd = nNumEnd; + } + break; + + // "Caption Text" + case REF_ONLYCAPTION: { + // next alphanumeric character after category+number + if (const SwTextAttr* const pTextAttr = + pTextNd->GetTextAttrForCharAt(nNumStart, RES_TXTATR_FIELD) + ) { + // start searching from nFrom + const sal_Int32 nFrom = bHasCat + ? std::max(nNumStart + 1, nCatEnd) + : nNumStart + 1; + nStart = SwGetExpField::GetReferenceTextPos( pTextAttr->GetFormatField(), *pDoc, nFrom ); + } else { + nStart = bHasCat ? std::max(nNumEnd, nCatEnd) : nNumEnd; + } + nEnd = nLen; + break; + } + + // "Numbering" + case REF_ONLYSEQNO: + nStart = nNumStart; + nEnd = std::min(nStart + 1, nLen); + break; + + // "Reference" (whole Text) + case REF_CONTENT: + nStart = 0; + nEnd = nLen; + break; + + default: + O3TL_UNREACHABLE; + } + break; + + case REF_BOOKMARK: + nStart = nNumStart; + // text is spread across multiple nodes - get whole text or only until end of node? + nEnd = nNumEnd<0 ? nLen : nNumEnd; + break; + + case REF_OUTLINE: + nStart = nNumStart; + nEnd = nNumEnd; + break; + + case REF_FOOTNOTE: + case REF_ENDNOTE: + // get number or numString + for( size_t i = 0; i < pDoc->GetFootnoteIdxs().size(); ++i ) + { + SwTextFootnote* const pFootnoteIdx = pDoc->GetFootnoteIdxs()[i]; + if( m_nSeqNo == pFootnoteIdx->GetSeqRefNo() ) + { + m_sText = pFootnoteIdx->GetFootnote().GetViewNumStr(*pDoc, nullptr); + m_sTextRLHidden = pFootnoteIdx->GetFootnote().GetViewNumStr(*pDoc, pLayoutRLHidden); + if (!m_sSetReferenceLanguage.isEmpty()) + { + lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage); + lcl_formatReferenceLanguage(m_sTextRLHidden, false, GetLanguage(), m_sSetReferenceLanguage); + } + break; + } + } + return; + + case REF_SETREFATTR: + nStart = nNumStart; + nEnd = nNumEnd; + break; + + default: + O3TL_UNREACHABLE; + } + + if( nStart != nEnd ) // a section? + { + m_sText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, false, ExpandMode(0)); + if (m_nSubType == REF_OUTLINE + || (m_nSubType == REF_SEQUENCEFLD && REF_CONTENT == GetFormat())) + { + m_sTextRLHidden = sw::GetExpandTextMerged( + pLayoutRLHidden, *pTextNd, false, false, ExpandMode(0)); + } + else + { + m_sTextRLHidden = pTextNd->GetExpandText(pLayoutRLHidden, + nStart, nEnd - nStart, false, false, false, ExpandMode::HideDeletions); + } + FilterText(m_sText, GetLanguage(), m_sSetReferenceLanguage); + FilterText(m_sTextRLHidden, GetLanguage(), m_sSetReferenceLanguage); + } + } + break; + + case REF_PAGE: + case REF_PAGE_PGDESC: + { + auto const func = + [this, pTextNd, nNumStart](OUString & rText, SwRootFrame const*const pLay) + { + SwTextFrame const* pFrame = static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLay, nullptr, nullptr)); + SwTextFrame const*const pSave = pFrame; + if (pFrame) + { + TextFrameIndex const nNumStartIndex(pFrame->MapModelToView(pTextNd, nNumStart)); + while (pFrame && !pFrame->IsInside(nNumStartIndex)) + pFrame = pFrame->GetFollow(); + } + + if( pFrame || nullptr != ( pFrame = pSave )) + { + sal_uInt16 nPageNo = pFrame->GetVirtPageNum(); + const SwPageFrame *pPage; + if( REF_PAGE_PGDESC == GetFormat() && + nullptr != ( pPage = pFrame->FindPageFrame() ) && + pPage->GetPageDesc() ) + { + rText = pPage->GetPageDesc()->GetNumType().GetNumStr(nPageNo); + } + else + { + rText = OUString::number(nPageNo); + } + + if (!m_sSetReferenceLanguage.isEmpty()) + lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage); + } + }; + // sw_redlinehide: currently only one of these layouts will exist, + // so the getLayoutFrame will use the same frame in both cases + func(m_sText, pLayout); + func(m_sTextRLHidden, pLayoutRLHidden); + } + break; + + case REF_CHAPTER: + { + auto const func = + [this, pTextNd](OUString & rText, SwRootFrame const*const pLay) + { + // a bit tricky: search any frame + SwFrame const*const pFrame = pTextNd->getLayoutFrame(pLay); + if( pFrame ) + { + SwChapterFieldType aFieldTyp; + SwChapterField aField( &aFieldTyp, 0 ); + aField.SetLevel( MAXLEVEL - 1 ); + aField.ChangeExpansion( *pFrame, pTextNd, true ); + rText = aField.GetNumber(pLay); + + if (!m_sSetReferenceLanguage.isEmpty()) + lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage); + } + }; + func(m_sText, pLayout); + func(m_sTextRLHidden, pLayoutRLHidden); + } + break; + + case REF_UPDOWN: + { + // #i81002# + // simplified: use parameter <pFieldTextAttr> + if( !pFieldTextAttr || !pFieldTextAttr->GetpTextNode() ) + break; + + LanguageTag aLanguageTag( GetLanguage()); + LocaleDataWrapper aLocaleData( aLanguageTag ); + + // first a "short" test - in case both are in the same node + if( pFieldTextAttr->GetpTextNode() == pTextNd ) + { + m_sText = nNumStart < pFieldTextAttr->GetStart() + ? aLocaleData.getAboveWord() + : aLocaleData.getBelowWord(); + m_sTextRLHidden = m_sText; + break; + } + + m_sText = ::IsFrameBehind( *pFieldTextAttr->GetpTextNode(), pFieldTextAttr->GetStart(), + *pTextNd, nNumStart ) + ? aLocaleData.getAboveWord() + : aLocaleData.getBelowWord(); + + if (!m_sSetReferenceLanguage.isEmpty()) + lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage); + + m_sTextRLHidden = m_sText; + } + break; + // #i81002# + case REF_NUMBER: + case REF_NUMBER_NO_CONTEXT: + case REF_NUMBER_FULL_CONTEXT: + { + if ( pFieldTextAttr && pFieldTextAttr->GetpTextNode() ) + { + auto result = + MakeRefNumStr(pLayout, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat()); + m_sText = result.first; + // for differentiation of Roman numbers and letters in Hungarian article handling + bool bClosingParenthesis = result.second; + if (!m_sSetReferenceLanguage.isEmpty()) + { + lcl_formatReferenceLanguage(m_sText, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage); + } + result = + MakeRefNumStr(pLayoutRLHidden, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat()); + m_sTextRLHidden = result.first; + bClosingParenthesis = result.second; + if (!m_sSetReferenceLanguage.isEmpty()) + { + lcl_formatReferenceLanguage(m_sTextRLHidden, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage); + } + } + } + break; + + default: + OSL_FAIL("<SwGetRefField::UpdateField(..)> - unknown format type"); + } +} + +// #i81002# +static std::pair<OUString, bool> MakeRefNumStr( + SwRootFrame const*const pLayout, + const SwTextNode& i_rTextNodeOfField, + const SwTextNode& i_rTextNodeOfReferencedItem, + const sal_uInt32 nRefNumFormat) +{ + SwTextNode const& rTextNodeOfField(pLayout + ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfField) + : i_rTextNodeOfField); + SwTextNode const& rTextNodeOfReferencedItem(pLayout + ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfReferencedItem) + : i_rTextNodeOfReferencedItem); + if ( rTextNodeOfReferencedItem.HasNumber() && + rTextNodeOfReferencedItem.IsCountedInList() ) + { + OSL_ENSURE( rTextNodeOfReferencedItem.GetNum(pLayout), + "<SwGetRefField::MakeRefNumStr(..)> - referenced paragraph has number, but no <SwNodeNum> instance!" ); + + // Determine, up to which level the superior list labels have to be + // included - default is to include all superior list labels. + int nRestrictInclToThisLevel( 0 ); + // Determine for format REF_NUMBER the level, up to which the superior + // list labels have to be restricted, if the text node of the reference + // field and the text node of the referenced item are in the same + // document context. + if ( nRefNumFormat == REF_NUMBER && + rTextNodeOfField.FindFlyStartNode() + == rTextNodeOfReferencedItem.FindFlyStartNode() && + rTextNodeOfField.FindFootnoteStartNode() + == rTextNodeOfReferencedItem.FindFootnoteStartNode() && + rTextNodeOfField.FindHeaderStartNode() + == rTextNodeOfReferencedItem.FindHeaderStartNode() && + rTextNodeOfField.FindFooterStartNode() + == rTextNodeOfReferencedItem.FindFooterStartNode() ) + { + const SwNodeNum* pNodeNumForTextNodeOfField( nullptr ); + if ( rTextNodeOfField.HasNumber() && + rTextNodeOfField.GetNumRule() == rTextNodeOfReferencedItem.GetNumRule() ) + { + pNodeNumForTextNodeOfField = rTextNodeOfField.GetNum(pLayout); + } + else + { + pNodeNumForTextNodeOfField = + rTextNodeOfReferencedItem.GetNum(pLayout)->GetPrecedingNodeNumOf(rTextNodeOfField); + } + if ( pNodeNumForTextNodeOfField ) + { + const SwNumberTree::tNumberVector rFieldNumVec = + pNodeNumForTextNodeOfField->GetNumberVector(); + const SwNumberTree::tNumberVector rRefItemNumVec = + rTextNodeOfReferencedItem.GetNum()->GetNumberVector(); + std::size_t nLevel( 0 ); + while ( nLevel < rFieldNumVec.size() && nLevel < rRefItemNumVec.size() ) + { + if ( rRefItemNumVec[nLevel] == rFieldNumVec[nLevel] ) + { + nRestrictInclToThisLevel = nLevel + 1; + } + else + { + break; + } + ++nLevel; + } + } + } + + // Determine, if superior list labels have to be included + const bool bInclSuperiorNumLabels( + ( nRestrictInclToThisLevel < rTextNodeOfReferencedItem.GetActualListLevel() && + ( nRefNumFormat == REF_NUMBER || nRefNumFormat == REF_NUMBER_FULL_CONTEXT ) ) ); + + OSL_ENSURE( rTextNodeOfReferencedItem.GetNumRule(), + "<SwGetRefField::MakeRefNumStr(..)> - referenced numbered paragraph has no numbering rule set!" ); + return std::make_pair( + rTextNodeOfReferencedItem.GetNumRule()->MakeRefNumString( + *(rTextNodeOfReferencedItem.GetNum(pLayout)), + bInclSuperiorNumLabels, + nRestrictInclToThisLevel ), + rTextNodeOfReferencedItem.GetNumRule()->MakeNumString( + *(rTextNodeOfReferencedItem.GetNum(pLayout)), + true).endsWith(")") ); + } + + return std::make_pair(OUString(), false); +} + +std::unique_ptr<SwField> SwGetRefField::Copy() const +{ + std::unique_ptr<SwGetRefField> pField( new SwGetRefField( static_cast<SwGetRefFieldType*>(GetTyp()), + m_sSetRefName, m_sSetReferenceLanguage, m_nSubType, + m_nSeqNo, GetFormat() ) ); + pField->m_sText = m_sText; + pField->m_sTextRLHidden = m_sTextRLHidden; + return std::unique_ptr<SwField>(pField.release()); +} + +/// get reference name +OUString SwGetRefField::GetPar1() const +{ + return m_sSetRefName; +} + +/// set reference name +void SwGetRefField::SetPar1( const OUString& rName ) +{ + m_sSetRefName = rName; +} + +OUString SwGetRefField::GetPar2() const +{ + return ExpandImpl(nullptr); +} + +bool SwGetRefField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_USHORT1: + { + sal_Int16 nPart = 0; + switch(GetFormat()) + { + case REF_PAGE : nPart = ReferenceFieldPart::PAGE ; break; + case REF_CHAPTER : nPart = ReferenceFieldPart::CHAPTER ; break; + case REF_CONTENT : nPart = ReferenceFieldPart::TEXT ; break; + case REF_UPDOWN : nPart = ReferenceFieldPart::UP_DOWN ; break; + case REF_PAGE_PGDESC: nPart = ReferenceFieldPart::PAGE_DESC ; break; + case REF_ONLYNUMBER : nPart = ReferenceFieldPart::CATEGORY_AND_NUMBER ; break; + case REF_ONLYCAPTION: nPart = ReferenceFieldPart::ONLY_CAPTION ; break; + case REF_ONLYSEQNO : nPart = ReferenceFieldPart::ONLY_SEQUENCE_NUMBER; break; + // #i81002# + case REF_NUMBER: nPart = ReferenceFieldPart::NUMBER; break; + case REF_NUMBER_NO_CONTEXT: nPart = ReferenceFieldPart::NUMBER_NO_CONTEXT; break; + case REF_NUMBER_FULL_CONTEXT: nPart = ReferenceFieldPart::NUMBER_FULL_CONTEXT; break; + } + rAny <<= nPart; + } + break; + case FIELD_PROP_USHORT2: + { + sal_Int16 nSource = 0; + switch(m_nSubType) + { + case REF_SETREFATTR : nSource = ReferenceFieldSource::REFERENCE_MARK; break; + case REF_SEQUENCEFLD: nSource = ReferenceFieldSource::SEQUENCE_FIELD; break; + case REF_BOOKMARK : nSource = ReferenceFieldSource::BOOKMARK; break; + case REF_OUTLINE : OSL_FAIL("not implemented"); break; + case REF_FOOTNOTE : nSource = ReferenceFieldSource::FOOTNOTE; break; + case REF_ENDNOTE : nSource = ReferenceFieldSource::ENDNOTE; break; + } + rAny <<= nSource; + } + break; + case FIELD_PROP_PAR1: + { + OUString sTmp(GetPar1()); + if(REF_SEQUENCEFLD == m_nSubType) + { + sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( sTmp, SwGetPoolIdFromName::TxtColl ); + switch( nPoolId ) + { + case RES_POOLCOLL_LABEL_ABB: + case RES_POOLCOLL_LABEL_TABLE: + case RES_POOLCOLL_LABEL_FRAME: + case RES_POOLCOLL_LABEL_DRAWING: + case RES_POOLCOLL_LABEL_FIGURE: + SwStyleNameMapper::FillProgName(nPoolId, sTmp) ; + break; + } + } + rAny <<= sTmp; + } + break; + case FIELD_PROP_PAR3: + rAny <<= ExpandImpl(nullptr); + break; + case FIELD_PROP_PAR4: + rAny <<= m_sSetReferenceLanguage; + break; + case FIELD_PROP_SHORT1: + rAny <<= static_cast<sal_Int16>(m_nSeqNo); + break; + default: + assert(false); + } + return true; +} + +bool SwGetRefField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_USHORT1: + { + sal_Int16 nPart = 0; + rAny >>= nPart; + switch(nPart) + { + case ReferenceFieldPart::PAGE: nPart = REF_PAGE; break; + case ReferenceFieldPart::CHAPTER: nPart = REF_CHAPTER; break; + case ReferenceFieldPart::TEXT: nPart = REF_CONTENT; break; + case ReferenceFieldPart::UP_DOWN: nPart = REF_UPDOWN; break; + case ReferenceFieldPart::PAGE_DESC: nPart = REF_PAGE_PGDESC; break; + case ReferenceFieldPart::CATEGORY_AND_NUMBER: nPart = REF_ONLYNUMBER; break; + case ReferenceFieldPart::ONLY_CAPTION: nPart = REF_ONLYCAPTION; break; + case ReferenceFieldPart::ONLY_SEQUENCE_NUMBER : nPart = REF_ONLYSEQNO; break; + // #i81002# + case ReferenceFieldPart::NUMBER: nPart = REF_NUMBER; break; + case ReferenceFieldPart::NUMBER_NO_CONTEXT: nPart = REF_NUMBER_NO_CONTEXT; break; + case ReferenceFieldPart::NUMBER_FULL_CONTEXT: nPart = REF_NUMBER_FULL_CONTEXT; break; + default: return false; + } + SetFormat(nPart); + } + break; + case FIELD_PROP_USHORT2: + { + sal_Int16 nSource = 0; + rAny >>= nSource; + switch(nSource) + { + case ReferenceFieldSource::REFERENCE_MARK : m_nSubType = REF_SETREFATTR ; break; + case ReferenceFieldSource::SEQUENCE_FIELD : + { + if(REF_SEQUENCEFLD == m_nSubType) + break; + m_nSubType = REF_SEQUENCEFLD; + ConvertProgrammaticToUIName(); + } + break; + case ReferenceFieldSource::BOOKMARK : m_nSubType = REF_BOOKMARK ; break; + case ReferenceFieldSource::FOOTNOTE : m_nSubType = REF_FOOTNOTE ; break; + case ReferenceFieldSource::ENDNOTE : m_nSubType = REF_ENDNOTE ; break; + } + } + break; + case FIELD_PROP_PAR1: + { + OUString sTmpStr; + rAny >>= sTmpStr; + SetPar1(sTmpStr); + ConvertProgrammaticToUIName(); + } + break; + case FIELD_PROP_PAR3: + { + OUString sTmpStr; + rAny >>= sTmpStr; + SetExpand( sTmpStr ); + } + break; + case FIELD_PROP_PAR4: + rAny >>= m_sSetReferenceLanguage; + break; + case FIELD_PROP_SHORT1: + { + sal_Int16 nSetSeq = 0; + rAny >>= nSetSeq; + if(nSetSeq >= 0) + m_nSeqNo = nSetSeq; + } + break; + default: + assert(false); + } + return true; +} + +void SwGetRefField::ConvertProgrammaticToUIName() +{ + if(GetTyp() && REF_SEQUENCEFLD == m_nSubType) + { + SwDoc* pDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc(); + const OUString rPar1 = GetPar1(); + // don't convert when the name points to an existing field type + if(!pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, rPar1, false)) + { + sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromProgName( rPar1, SwGetPoolIdFromName::TxtColl ); + const char* pResId = nullptr; + switch( nPoolId ) + { + case RES_POOLCOLL_LABEL_ABB: + pResId = STR_POOLCOLL_LABEL_ABB; + break; + case RES_POOLCOLL_LABEL_TABLE: + pResId = STR_POOLCOLL_LABEL_TABLE; + break; + case RES_POOLCOLL_LABEL_FRAME: + pResId = STR_POOLCOLL_LABEL_FRAME; + break; + case RES_POOLCOLL_LABEL_DRAWING: + pResId = STR_POOLCOLL_LABEL_DRAWING; + break; + case RES_POOLCOLL_LABEL_FIGURE: + pResId = STR_POOLCOLL_LABEL_FIGURE; + break; + } + if (pResId) + SetPar1(SwResId(pResId)); + } + } +} + +SwGetRefFieldType::SwGetRefFieldType( SwDoc* pDc ) + : SwFieldType( SwFieldIds::GetRef ), m_pDoc( pDc ) +{} + +std::unique_ptr<SwFieldType> SwGetRefFieldType::Copy() const +{ + return std::make_unique<SwGetRefFieldType>( m_pDoc ); +} + +void SwGetRefFieldType::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + // update to all GetReference fields + if( !pNew && !pOld ) + { + std::vector<SwFormatField*> vFields; + GatherFields(vFields, false); + for(auto pFormatField: vFields) + { + // update only the GetRef fields + //JP 3.4.2001: Task 71231 - we need the correct language + SwGetRefField* pGRef = static_cast<SwGetRefField*>(pFormatField->GetField()); + const SwTextField* pTField; + if( !pGRef->GetLanguage() && + nullptr != ( pTField = pFormatField->GetTextField()) && + pTField->GetpTextNode() ) + { + pGRef->SetLanguage( pTField->GetpTextNode()->GetLang( + pTField->GetStart() ) ); + } + + // #i81002# + pGRef->UpdateField( pFormatField->GetTextField() ); + } + } + // forward to text fields, they "expand" the text + NotifyClients( pOld, pNew ); +} + +namespace sw { + +bool IsMarkHintHidden(SwRootFrame const& rLayout, + SwTextNode const& rNode, SwTextAttrEnd const& rHint) +{ + if (!rLayout.IsHideRedlines()) + { + return false; + } + SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>( + rNode.getLayoutFrame(&rLayout))); + if (!pFrame) + { + return true; + } + sal_Int32 const*const pEnd(rHint.GetEnd()); + if (pEnd) + { + return pFrame->MapModelToView(&rNode, rHint.GetStart()) + == pFrame->MapModelToView(&rNode, *pEnd); + } + else + { + assert(rHint.HasDummyChar()); + return pFrame->MapModelToView(&rNode, rHint.GetStart()) + == pFrame->MapModelToView(&rNode, rHint.GetStart() + 1); + } +} + +} // namespace sw + +SwTextNode* SwGetRefFieldType::FindAnchor( SwDoc* pDoc, const OUString& rRefMark, + sal_uInt16 nSubType, sal_uInt16 nSeqNo, + sal_Int32* pStt, sal_Int32* pEnd, + SwRootFrame const*const pLayout) +{ + OSL_ENSURE( pStt, "Why did no one check the StartPos?" ); + + IDocumentRedlineAccess & rIDRA(pDoc->getIDocumentRedlineAccess()); + SwTextNode* pTextNd = nullptr; + switch( nSubType ) + { + case REF_SETREFATTR: + { + const SwFormatRefMark *pRef = pDoc->GetRefMark( rRefMark ); + SwTextRefMark const*const pRefMark(pRef ? pRef->GetTextRefMark() : nullptr); + if (pRefMark && (!pLayout || !sw::IsMarkHintHidden(*pLayout, + pRefMark->GetTextNode(), *pRefMark))) + { + pTextNd = const_cast<SwTextNode*>(&pRef->GetTextRefMark()->GetTextNode()); + *pStt = pRef->GetTextRefMark()->GetStart(); + if( pEnd ) + *pEnd = pRef->GetTextRefMark()->GetAnyEnd(); + } + } + break; + + case REF_SEQUENCEFLD: + { + SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp, rRefMark, false ); + if( pFieldType && pFieldType->HasWriterListeners() && + nsSwGetSetExpType::GSE_SEQ & static_cast<SwSetExpFieldType*>(pFieldType)->GetType() ) + { + std::vector<SwFormatField*> vFields; + pFieldType->GatherFields(vFields, false); + for(auto pFormatField: vFields) + { + SwTextField *const pTextField(pFormatField->GetTextField()); + if (pTextField && nSeqNo == + static_cast<SwSetExpField*>(pFormatField->GetField())->GetSeqNumber() + && (!pLayout || !pLayout->IsHideRedlines() + || !sw::IsFieldDeletedInModel(rIDRA, *pTextField))) + { + pTextNd = pTextField->GetpTextNode(); + *pStt = pTextField->GetStart(); + if( pEnd ) + *pEnd = (*pStt) + 1; + break; + } + } + } + } + break; + + case REF_BOOKMARK: + { + IDocumentMarkAccess::const_iterator_t ppMark = pDoc->getIDocumentMarkAccess()->findMark(rRefMark); + if (ppMark != pDoc->getIDocumentMarkAccess()->getAllMarksEnd() + && (!pLayout || !pLayout->IsHideRedlines() + || !sw::IsMarkHidden(*pLayout, **ppMark))) + { + const ::sw::mark::IMark* pBkmk = *ppMark; + const SwPosition* pPos = &pBkmk->GetMarkStart(); + + pTextNd = pPos->nNode.GetNode().GetTextNode(); + *pStt = pPos->nContent.GetIndex(); + if(pEnd) + { + if(!pBkmk->IsExpanded()) + { + *pEnd = *pStt; + // #i81002# + if(dynamic_cast< ::sw::mark::CrossRefBookmark const *>(pBkmk)) + { + OSL_ENSURE( pTextNd, + "<SwGetRefFieldType::FindAnchor(..)> - node marked by cross-reference bookmark isn't a text node --> crash" ); + *pEnd = pTextNd->Len(); + } + } + else if(pBkmk->GetOtherMarkPos().nNode == pBkmk->GetMarkPos().nNode) + *pEnd = pBkmk->GetMarkEnd().nContent.GetIndex(); + else + *pEnd = -1; + } + } + } + break; + + case REF_OUTLINE: + break; + + case REF_FOOTNOTE: + case REF_ENDNOTE: + { + for( auto pFootnoteIdx : pDoc->GetFootnoteIdxs() ) + if( nSeqNo == pFootnoteIdx->GetSeqRefNo() ) + { + if (pLayout && pLayout->IsHideRedlines() + && sw::IsFootnoteDeleted(rIDRA, *pFootnoteIdx)) + { + return nullptr; + } + // otherwise: the position at the start of the footnote + // will be mapped to something visible at least... + SwNodeIndex* pIdx = pFootnoteIdx->GetStartNode(); + if( pIdx ) + { + SwNodeIndex aIdx( *pIdx, 1 ); + if( nullptr == ( pTextNd = aIdx.GetNode().GetTextNode())) + pTextNd = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext( &aIdx )); + } + *pStt = 0; + if( pEnd ) + *pEnd = 0; + break; + } + } + break; + } + + return pTextNd; +} + +namespace { + +struct RefIdsMap +{ +private: + OUString aName; + std::set<sal_uInt16> aIds; + std::set<sal_uInt16> aDstIds; + std::map<sal_uInt16, sal_uInt16> sequencedIds; /// ID numbers sorted by sequence number. + bool bInit; + + void Init(SwDoc& rDoc, SwDoc& rDestDoc, bool bField ); + static void GetNoteIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds ); + void GetFieldIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds ); + void AddId( sal_uInt16 id, sal_uInt16 seqNum ); + static sal_uInt16 GetFirstUnusedId( std::set<sal_uInt16> &rIds ); + +public: + explicit RefIdsMap( const OUString& rName ) : aName( rName ), bInit( false ) {} + + void Check( SwDoc& rDoc, SwDoc& rDestDoc, SwGetRefField& rField, bool bField ); + + const OUString& GetName() const { return aName; } +}; + +} + +/// Get a sorted list of the field IDs from a document. +/// @param[in] rDoc The document to search. +/// @param[in,out] rIds The list of IDs found in the document. +void RefIdsMap::GetFieldIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds) +{ + SwFieldType *const pType = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, aName, false); + if (!pType) + return; + std::vector<SwFormatField*> vFields; + pType->GatherFields(vFields); + for(const auto pF: vFields) + rIds.insert(static_cast<SwSetExpField const*>(pF->GetField())->GetSeqNumber()); +} + +/// Get a sorted list of the footnote/endnote IDs from a document. +/// @param[in] rDoc The document to search. +/// @param[in,out] rIds The list of IDs found in the document. +void RefIdsMap::GetNoteIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds) +{ + for( auto n = rDoc.GetFootnoteIdxs().size(); n; ) + rIds.insert( rDoc.GetFootnoteIdxs()[ --n ]->GetSeqRefNo() ); +} + +/// Initialise the aIds and aDestIds collections from the source documents. +/// @param[in] rDoc The source document. +/// @param[in] rDestDoc The destination document. +/// @param[in] bField True if we're interested in all fields, false for footnotes. +void RefIdsMap::Init( SwDoc& rDoc, SwDoc& rDestDoc, bool bField ) +{ + if( bInit ) + return; + + if( bField ) + { + GetFieldIdsFromDoc( rDestDoc, aIds ); + GetFieldIdsFromDoc( rDoc, aDstIds ); + + // Map all the new src fields to the next available unused id + for (const auto& rId : aDstIds) + AddId( GetFirstUnusedId(aIds), rId ); + + // Change the Sequence number of all SetExp fields in the source document + SwFieldType* pType = rDoc.getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp, aName, false ); + if(pType) + { + std::vector<SwFormatField*> vFields; + pType->GatherFields(vFields, false); + for(auto pF: vFields) + { + if(!pF->GetTextField()) + continue; + SwSetExpField *const pSetExp(static_cast<SwSetExpField *>(pF->GetField())); + sal_uInt16 const n = pSetExp->GetSeqNumber(); + pSetExp->SetSeqNumber(sequencedIds[n]); + } + } + } + else + { + GetNoteIdsFromDoc( rDestDoc, aIds ); + GetNoteIdsFromDoc( rDoc, aDstIds ); + + for (const auto& rId : aDstIds) + AddId( GetFirstUnusedId(aIds), rId ); + + // Change the footnotes/endnotes in the source doc to the new ID + for ( const auto pFootnoteIdx : rDoc.GetFootnoteIdxs() ) + { + sal_uInt16 const n = pFootnoteIdx->GetSeqRefNo(); + pFootnoteIdx->SetSeqNo(sequencedIds[n]); + } + } + bInit = true; +} + +/// Get the lowest number unused in the passed set. +/// @param[in] rIds The set of used ID numbers. +/// @returns The lowest number unused by the passed set +sal_uInt16 RefIdsMap::GetFirstUnusedId( std::set<sal_uInt16> &rIds ) +{ + sal_uInt16 num(0); + + for( const auto& rId : rIds ) + { + if( num != rId ) + { + return num; + } + ++num; + } + return num; +} + +/// Add a new ID and sequence number to the "occupied" collection. +/// @param[in] id The ID number. +/// @param[in] seqNum The sequence number. +void RefIdsMap::AddId( sal_uInt16 id, sal_uInt16 seqNum ) +{ + aIds.insert( id ); + sequencedIds[ seqNum ] = id; +} + +void RefIdsMap::Check( SwDoc& rDoc, SwDoc& rDestDoc, SwGetRefField& rField, + bool bField ) +{ + Init( rDoc, rDestDoc, bField); + + sal_uInt16 const nSeqNo = rField.GetSeqNo(); + + // check if it needs to be remapped + // if sequencedIds doesn't contain the number, it means there is no + // SetExp field / footnote in the source document: do not modify + // the number, which works well for copy from/paste to same document + // (and if it is not the same document, there's no "correct" result anyway) + if (sequencedIds.count(nSeqNo)) + { + rField.SetSeqNo( sequencedIds[nSeqNo] ); + } +} + +/// 1. if _both_ SetExp + GetExp / Footnote + GetExp field are copied, +/// ensure that both get a new unused matching number +/// 2. if only SetExp / Footnote is copied, it gets a new unused number +/// 3. if only GetExp field is copied, for the case of copy from / paste to +/// same document it's desirable to keep the same number; +/// for other cases of copy/paste or master documents it's not obvious +/// what is most desirable since it's going to be wrong anyway +void SwGetRefFieldType::MergeWithOtherDoc( SwDoc& rDestDoc ) +{ + if( &rDestDoc != m_pDoc ) + { + if (rDestDoc.IsClipBoard()) + { + // when copying _to_ clipboard, expectation is that no fields exist + // so no re-mapping is required to avoid collisions + assert(!rDestDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef)->HasWriterListeners()); + return; // don't modify the fields in the source doc + } + + // then there are RefFields in the DescDox - so all RefFields in the SourceDoc + // need to be converted to have unique IDs for both documents + RefIdsMap aFntMap { OUString() }; + std::vector<std::unique_ptr<RefIdsMap>> aFieldMap; + + std::vector<SwFormatField*> vFields; + GatherFields(vFields); + for(auto pField: vFields) + { + SwGetRefField& rRefField = *static_cast<SwGetRefField*>(pField->GetField()); + switch( rRefField.GetSubType() ) + { + case REF_SEQUENCEFLD: + { + RefIdsMap* pMap = nullptr; + for( auto n = aFieldMap.size(); n; ) + { + if (aFieldMap[ --n ]->GetName() == rRefField.GetSetRefName()) + { + pMap = aFieldMap[ n ].get(); + break; + } + } + if( !pMap ) + { + pMap = new RefIdsMap( rRefField.GetSetRefName() ); + aFieldMap.push_back(std::unique_ptr<RefIdsMap>(pMap)); + } + + pMap->Check( *m_pDoc, rDestDoc, rRefField, true ); + } + break; + + case REF_FOOTNOTE: + case REF_ENDNOTE: + aFntMap.Check( *m_pDoc, rDestDoc, rRefField, false ); + break; + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |