summaryrefslogtreecommitdiffstats
path: root/sw/source/core/fields/reffld.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/fields/reffld.cxx')
-rw-r--r--sw/source/core/fields/reffld.cxx1829
1 files changed, 1829 insertions, 0 deletions
diff --git a/sw/source/core/fields/reffld.cxx b/sw/source/core/fields/reffld.cxx
new file mode 100644
index 0000000000..0ee26771bb
--- /dev/null
+++ b/sw/source/core/fields/reffld.cxx
@@ -0,0 +1,1829 @@
+/* -*- 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 <notxtfrm.hxx>
+#include <flyfrm.hxx>
+#include <pagedesc.hxx>
+#include <IMark.hxx>
+#include <crossrefbookmark.hxx>
+#include <ftnidx.hxx>
+#include <utility>
+#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 <string_view>
+#include <map>
+#include <algorithm>
+#include <deque>
+
+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_uInt16 nSubType,
+ sal_uInt32 nRefNumFormat,
+ sal_uInt16 nFlags);
+
+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,
+ std::u16string_view rReferenceLanguage)
+{
+ if (eLang != LANGUAGE_HUNGARIAN || (rReferenceLanguage != u"hu" && rReferenceLanguage != u"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)
+
+ CharClass aCharClass(( LanguageTag(eLang) ));
+ 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 std::u16string_view sLettersStartingWithVowels = u"aefilmnorsuxyAEFILMNORSUXY";
+ if (sLettersStartingWithVowels.find(sNumbering[0]) != std::u16string_view::npos)
+ {
+ // 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 const OUString sVowels = OUString::Concat(u"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 ") )
+ return;
+
+ // lowercase, if rReferenceLanguage == "hu", not "Hu"
+ OUString sArticle;
+
+ if ( rReferenceLanguage == u"hu" )
+ sArticle = "a";
+ else
+ sArticle = "A";
+
+ if (bArticleAz)
+ sArticle += "z";
+
+ rRefText = sArticle + " " + rRefText;
+}
+
+/// get references
+SwGetRefField::SwGetRefField( SwGetRefFieldType* pFieldType,
+ OUString aSetRef, OUString aSetReferenceLanguage, sal_uInt16 nSubTyp,
+ sal_uInt16 nSequenceNo, sal_uInt16 nFlags, sal_uLong nFormat )
+ : SwField(pFieldType, nFormat),
+ m_sSetRefName(std::move(aSetRef)),
+ m_sSetReferenceLanguage(std::move(aSetReferenceLanguage)),
+ m_nSubType(nSubTyp),
+ m_nSeqNo(nSequenceNo),
+ m_nFlags(nFlags)
+{
+}
+
+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(SwTextNode* pTextNode, SwFrame* pFrame) 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, m_nFlags, &nDummy,
+ nullptr, nullptr, pTextNode, pFrame );
+}
+
+// strikethrough for tooltips using Unicode combining character
+static OUString lcl_formatStringByCombiningCharacter(std::u16string_view sText, const sal_Unicode cChar)
+{
+ OUStringBuffer sRet(sText.size() * 2);
+ for (size_t i = 0; i < sText.size(); ++i)
+ {
+ sRet.append(OUStringChar(sText[i]) + OUStringChar(cChar));
+ }
+ return sRet.makeStringAndClear();
+}
+
+// #i85090#
+OUString SwGetRefField::GetExpandedTextOfReferencedTextNode(
+ SwRootFrame const& rLayout, SwTextNode* pTextNode, SwFrame* pFrame) const
+{
+ const SwTextNode* pReferencedTextNode( GetReferencedTextNode(pTextNode, pFrame) );
+ if ( !pReferencedTextNode )
+ return OUString();
+
+ // show the referenced text without the deletions, but if the whole text was
+ // deleted, show the original text for the sake of the comfortable reviewing,
+ // but with Unicode strikethrough in the tooltip
+ OUString sRet = sw::GetExpandTextMerged(&rLayout, *pReferencedTextNode, true, false, ExpandMode::HideDeletions);
+ if ( sRet.isEmpty() )
+ {
+ static const sal_Unicode cStrikethrough = u'\x0336';
+ sRet = sw::GetExpandTextMerged(&rLayout, *pReferencedTextNode, true, false, ExpandMode(0));
+ sRet = lcl_formatStringByCombiningCharacter( sRet, cStrikethrough );
+ }
+
+ return sRet;
+}
+
+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,
+ std::u16string_view rSetReferenceLanguage)
+{
+ // remove all special characters (replace them with blanks)
+ if (rText.isEmpty())
+ return;
+
+ 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.empty())
+ {
+ lcl_formatReferenceLanguage(rText, false, eLang, rSetReferenceLanguage);
+ }
+}
+
+// #i81002# - parameter <pFieldTextAttr> added
+void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr, SwFrame* pFrame )
+{
+ SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc();
+
+ for (SwRootFrame const* const pLay : rDoc.GetAllLayouts())
+ {
+ if (pLay->IsHideRedlines())
+ {
+ UpdateField(pFieldTextAttr, pFrame, pLay, m_sTextRLHidden);
+ }
+ else
+ {
+ UpdateField(pFieldTextAttr, pFrame, pLay, m_sText);
+ }
+ }
+}
+
+void SwGetRefField::UpdateField(const SwTextField* pFieldTextAttr, SwFrame* pFrameContainingField,
+ const SwRootFrame* const pLayout, OUString& rText)
+{
+ SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc();
+
+ rText.clear();
+
+ // finding the reference target (the number)
+ sal_Int32 nNumStart = -1;
+ sal_Int32 nNumEnd = -1;
+ SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor(
+ &rDoc, m_sSetRefName, m_nSubType, m_nSeqNo, m_nFlags, &nNumStart, &nNumEnd,
+ pLayout, pFieldTextAttr ? pFieldTextAttr->GetpTextNode() : nullptr, pFrameContainingField
+ );
+ // not found?
+ if ( !pTextNd )
+ {
+ rText = SwViewShell::GetShellRes()->aGetRefField_RefItemNotFound;
+
+ return;
+ }
+
+ // 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(), rDoc, 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 < rDoc.GetFootnoteIdxs().size(); ++i )
+ {
+ SwTextFootnote* const pFootnoteIdx = rDoc.GetFootnoteIdxs()[i];
+ if( m_nSeqNo == pFootnoteIdx->GetSeqRefNo() )
+ {
+ rText = pFootnoteIdx->GetFootnote().GetViewNumStr(rDoc, pLayout);
+ if (!m_sSetReferenceLanguage.isEmpty())
+ {
+ lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage);
+ }
+ break;
+ }
+ }
+ return;
+
+ case REF_STYLE:
+ nStart = 0;
+ nEnd = nLen;
+ break;
+
+ case REF_SETREFATTR:
+ nStart = nNumStart;
+ nEnd = nNumEnd;
+ break;
+
+ default:
+ O3TL_UNREACHABLE;
+ }
+
+ if( nStart != nEnd ) // a section?
+ {
+ if (pLayout->IsHideRedlines())
+ {
+ if (m_nSubType == REF_OUTLINE
+ || (m_nSubType == REF_SEQUENCEFLD && REF_CONTENT == GetFormat()))
+ {
+ rText = sw::GetExpandTextMerged(pLayout, *pTextNd, false, false,
+ ExpandMode(0));
+ }
+ else
+ {
+ rText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false,
+ false, ExpandMode::HideDeletions);
+ }
+ }
+ else
+ {
+ rText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false,
+ false, ExpandMode::HideDeletions);
+ // show the referenced text without the deletions, but if the whole text was
+ // deleted, show the original text for the sake of the comfortable reviewing
+ // (with strikethrough in tooltip, see GetExpandedTextOfReferencedTextNode())
+ if (rText.isEmpty())
+ rText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false,
+ false, ExpandMode(0));
+ }
+ FilterText(rText, GetLanguage(), m_sSetReferenceLanguage);
+ }
+ }
+ break;
+
+ case REF_PAGE:
+ case REF_PAGE_PGDESC:
+ {
+ SwTextFrame const* pFrame = static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout, 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);
+ }
+ }
+ break;
+
+ case REF_CHAPTER:
+ {
+ // a bit tricky: search any frame
+ SwFrame const* const pFrame = pTextNd->getLayoutFrame(pLayout);
+ if (pFrame)
+ {
+ SwChapterFieldType aFieldTyp;
+ SwChapterField aField(&aFieldTyp, 0);
+ aField.SetLevel(MAXLEVEL - 1);
+ aField.ChangeExpansion(*pFrame, pTextNd, true);
+
+ rText = aField.GetNumber(pLayout);
+
+ if (!m_sSetReferenceLanguage.isEmpty())
+ lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage);
+ }
+ }
+ break;
+
+ case REF_UPDOWN:
+ {
+ // #i81002#
+ // simplified: use parameter <pFieldTextAttr>
+ if( !pFieldTextAttr || !pFieldTextAttr->GetpTextNode() )
+ break;
+
+ LocaleDataWrapper aLocaleData(( LanguageTag( GetLanguage() ) ));
+
+ // first a "short" test - in case both are in the same node
+ if( pFieldTextAttr->GetpTextNode() == pTextNd )
+ {
+ rText = nNumStart < pFieldTextAttr->GetStart()
+ ? aLocaleData.getAboveWord()
+ : aLocaleData.getBelowWord();
+ break;
+ }
+
+ rText = ::IsFrameBehind( *pFieldTextAttr->GetpTextNode(), pFieldTextAttr->GetStart(),
+ *pTextNd, nNumStart )
+ ? aLocaleData.getAboveWord()
+ : aLocaleData.getBelowWord();
+
+ if (!m_sSetReferenceLanguage.isEmpty())
+ lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage);
+ }
+ 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, m_nSubType, GetFormat(), GetFlags());
+ rText = result.first;
+ // for differentiation of Roman numbers and letters in Hungarian article handling
+ bool bClosingParenthesis = result.second;
+ if (!m_sSetReferenceLanguage.isEmpty())
+ {
+ lcl_formatReferenceLanguage(rText, 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_uInt16 nSubType,
+ const sal_uInt32 nRefNumFormat,
+ const sal_uInt16 nFlags)
+{
+ bool bHideNonNumerical = (nSubType == REF_STYLE) && ((nFlags & REFFLDFLAG_STYLE_HIDE_NON_NUMERICAL) == REFFLDFLAG_STYLE_HIDE_NON_NUMERICAL);
+ 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(pLayout) &&
+ 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(pLayout) &&
+ 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,
+ bHideNonNumerical ),
+ 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, m_nFlags, 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;
+ case REF_STYLE : nSource = ReferenceFieldSource::STYLE; break;
+ }
+ rAny <<= nSource;
+ }
+ break;
+ case FIELD_PROP_USHORT3:
+ rAny <<= m_nFlags;
+ 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;
+ case ReferenceFieldSource::STYLE : m_nSubType = REF_STYLE ; 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_USHORT3:
+ {
+ sal_uInt16 nSetFlags = 0;
+ rAny >>= nSetFlags;
+ m_nFlags = nSetFlags;
+ }
+ 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))
+ return;
+
+ SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc();
+ const OUString rPar1 = GetPar1();
+ // don't convert when the name points to an existing field type
+ if (rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, rPar1, false))
+ return;
+
+ sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromProgName( rPar1, SwGetPoolIdFromName::TxtColl );
+ TranslateId pResId;
+ 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& rDc )
+ : SwFieldType( SwFieldIds::GetRef ), m_rDoc( rDc )
+{}
+
+std::unique_ptr<SwFieldType> SwGetRefFieldType::Copy() const
+{
+ return std::make_unique<SwGetRefFieldType>( m_rDoc );
+}
+
+void SwGetRefFieldType::UpdateGetReferences()
+{
+ 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(), nullptr);
+ }
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr));
+}
+
+void SwGetRefFieldType::UpdateStyleReferences()
+{
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields, false);
+ bool bModified = false;
+ for(auto pFormatField: vFields)
+ {
+ // update only the GetRef fields which are also STYLEREF fields
+ SwGetRefField* pGRef = static_cast<SwGetRefField*>(pFormatField->GetField());
+
+ if (pGRef->GetSubType() != REF_STYLE) continue;
+
+ const SwTextField* pTField;
+ if(!pGRef->GetLanguage() &&
+ nullptr != (pTField = pFormatField->GetTextField()) &&
+ pTField->GetpTextNode())
+ {
+ pGRef->SetLanguage(pTField->GetpTextNode()->GetLang(pTField->GetStart()));
+ }
+
+ pGRef->UpdateField(pFormatField->GetTextField(), nullptr);
+ bModified = true;
+ }
+ if (bModified)
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr));
+}
+
+void SwGetRefFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(!pLegacy->m_pNew && !pLegacy->m_pOld)
+ // update to all GetReference fields
+ // hopefully, this codepath is soon dead code, and
+ // UpdateGetReferences gets only called directly
+ UpdateGetReferences();
+ else
+ // forward to text fields, they "expand" the text
+ CallSwClientNotify(rHint);
+}
+
+namespace sw {
+
+bool IsMarkHintHidden(SwRootFrame const& rLayout,
+ SwTextNode const& rNode, SwTextAttrEnd const& rHint)
+{
+ if (!rLayout.HasMergedParas())
+ {
+ 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
+
+namespace
+{
+ enum StyleRefElementType
+ {
+ Default,
+ Reference, /* e.g. footnotes, endnotes */
+ Marginal, /* headers, footers */
+ };
+
+ /// Picks the first text node with a matching style from a double ended queue, starting at the front
+ /// This allows us to use the deque either as a stack or as a queue depending on whether we want to search up or down
+ SwTextNode* SearchForStyleAnchor(SwTextNode* pSelf, const std::deque<SwNode*>& pToSearch,
+ std::u16string_view rStyleName, bool bCaseSensitive = true)
+ {
+ std::deque<SwNode*> pSearching(pToSearch);
+ while (!pSearching.empty())
+ {
+ SwNode* pCurrent = pSearching.front();
+ pSearching.pop_front();
+
+ if (*pCurrent == *pSelf)
+ continue;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ if (!pTextNode)
+ continue;
+
+ if (bCaseSensitive)
+ {
+ if (pTextNode->GetFormatColl()->GetName() == rStyleName)
+ return pTextNode;
+ }
+ else
+ {
+ if (pTextNode->GetFormatColl()->GetName().equalsIgnoreAsciiCase(rStyleName))
+ return pTextNode;
+ }
+ }
+
+ return nullptr;
+ }
+}
+
+SwTextNode* SwGetRefFieldType::FindAnchor(SwDoc* pDoc, const OUString& rRefMark,
+ sal_uInt16 nSubType, sal_uInt16 nSeqNo, sal_uInt16 nFlags,
+ sal_Int32* pStt, sal_Int32* pEnd, SwRootFrame const* const pLayout,
+ SwTextNode* pSelf, SwFrame* pContentFrame)
+{
+ 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->GetNode().GetTextNode();
+ *pStt = pPos->GetContentIndex();
+ 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().GetNode() == pBkmk->GetMarkPos().GetNode())
+ *pEnd = pBkmk->GetMarkEnd().GetContentIndex();
+ 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...
+ const SwNodeIndex* pIdx = pFootnoteIdx->GetStartNode();
+ if( pIdx )
+ {
+ SwNodeIndex aIdx( *pIdx, 1 );
+ pTextNd = aIdx.GetNode().GetTextNode();
+ if( nullptr == pTextNd )
+ pTextNd = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext( &aIdx ));
+ }
+ *pStt = 0;
+ if( pEnd )
+ *pEnd = 0;
+ break;
+ }
+ }
+ break;
+ case REF_STYLE:
+ if (!pSelf) break;
+
+ const SwNodes& nodes = pDoc->GetNodes();
+
+ StyleRefElementType elementType = StyleRefElementType::Default;
+ const SwTextNode* pReference = nullptr;
+
+ { /* Check if we're a footnote/endnote */
+ for (SwTextFootnote* pFootnoteIdx : pDoc->GetFootnoteIdxs())
+ {
+ if (pLayout && pLayout->IsHideRedlines()
+ && sw::IsFootnoteDeleted(rIDRA, *pFootnoteIdx))
+ {
+ continue;
+ }
+ const SwNodeIndex* pIdx = pFootnoteIdx->GetStartNode();
+ if (pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ SwTextNode* pFootnoteNode = aIdx.GetNode().GetTextNode();
+ if (nullptr == pFootnoteNode)
+ pFootnoteNode
+ = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext(&aIdx));
+
+ if (*pSelf == *pFootnoteNode)
+ {
+ elementType = StyleRefElementType::Reference;
+ pReference = &pFootnoteIdx->GetTextNode();
+ }
+ }
+ }
+ }
+
+ if (pDoc->IsInHeaderFooter(*pSelf))
+ {
+ elementType = StyleRefElementType::Marginal;
+ }
+
+ if (pReference == nullptr)
+ {
+ pReference = pSelf;
+ }
+
+ switch (elementType)
+ {
+ case Marginal:
+ {
+ // For marginals, styleref tries to act on the current page first
+ // 1. Get the page we're on, search it from top to bottom
+
+ bool bFlagFromBottom = (nFlags & REFFLDFLAG_STYLE_FROM_BOTTOM) == REFFLDFLAG_STYLE_FROM_BOTTOM;
+
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+
+ if (!pContentFrame) SAL_WARN("xmloff.text", "<SwGetRefFieldType::FindAnchor(..)>: Missing content frame for marginal styleref");
+ const SwPageFrame* pPageFrame = nullptr;
+
+ if (pContentFrame)
+ pPageFrame = pContentFrame->FindPageFrame();
+
+ const SwNode* pPageStart(nullptr);
+ const SwNode* pPageEnd(nullptr);
+
+ if (pPageFrame)
+ {
+ const SwContentFrame* pPageStartFrame = pPageFrame->FindFirstBodyContent();
+ const SwContentFrame* pPageEndFrame = pPageFrame->FindLastBodyContent();
+
+ if (pPageStartFrame) {
+ if (pPageStartFrame->IsTextFrame())
+ {
+ pPageStart = static_cast<const SwTextFrame*>(pPageStartFrame)
+ ->GetTextNodeFirst();
+ }
+ else
+ {
+ pPageStart
+ = static_cast<const SwNoTextFrame*>(pPageStartFrame)->GetNode();
+ }
+ }
+
+ if (pPageEndFrame) {
+ if (pPageEndFrame->IsTextFrame())
+ {
+ pPageEnd = static_cast<const SwTextFrame*>(pPageEndFrame)
+ ->GetTextNodeFirst();
+ }
+ else
+ {
+ pPageEnd = static_cast<const SwNoTextFrame*>(pPageEndFrame)->GetNode();
+ }
+ }
+ }
+
+ if (!pPageStart || !pPageEnd)
+ {
+ pPageStart = pReference;
+ pPageEnd = pReference;
+ }
+
+ std::deque<SwNode*> pSearchSecond;
+ std::deque<SwNode*> pInPage; /* or pSearchFirst */
+ std::deque<SwNode*> pSearchThird;
+
+ bool beforeStart = true;
+ bool beforeEnd = true;
+
+ for (SwNodeOffset n(0); n < nodes.Count(); n++)
+ {
+ if (beforeStart && *pPageStart == *nodes[n])
+ {
+ beforeStart = false;
+ }
+
+ if (beforeStart)
+ {
+ pSearchSecond.push_front(nodes[n]);
+ }
+ else if (beforeEnd)
+ {
+ if (bFlagFromBottom)
+ pInPage.push_front(nodes[n]);
+ else
+ pInPage.push_back(nodes[n]);
+
+ if (*pPageEnd == *nodes[n])
+ {
+ beforeEnd = false;
+ }
+ }
+ else
+ pSearchThird.push_back(nodes[n]);
+ }
+
+ pTextNd = SearchForStyleAnchor(pSelf, pInPage, rRefMark);
+ if (pTextNd)
+ {
+ break;
+ }
+
+ // 2. Search up from the top of the page
+ pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark);
+ if (pTextNd)
+ {
+ break;
+ }
+
+ // 3. Search down from the bottom of the page
+ pTextNd = SearchForStyleAnchor(pSelf, pSearchThird, rRefMark);
+ if (pTextNd)
+ {
+ break;
+ }
+
+ // Word has case insensitive styles. LO has case sensitive styles. If we didn't find
+ // it yet, maybe we could with a case insensitive search. Let's do that
+
+ pTextNd = SearchForStyleAnchor(pSelf, pInPage, rRefMark,
+ false /* bCaseSensitive */);
+ if (pTextNd)
+ {
+ break;
+ }
+
+ pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark,
+ false /* bCaseSensitive */);
+ if (pTextNd)
+ {
+ break;
+ }
+
+ pTextNd = SearchForStyleAnchor(pSelf, pSearchThird, rRefMark,
+ false /* bCaseSensitive */);
+ break;
+ }
+ case Reference:
+ case Default:
+ {
+ // Normally, styleref does searches around the field position
+ // For references, styleref acts from the position of the reference not the field
+ // Happily, the previous code saves either one into pReference, so the following is generic for both
+
+ std::deque<SwNode*> pSearchFirst;
+ std::deque<SwNode*> pSearchSecond;
+
+ bool beforeElement = true;
+
+ for (SwNodeOffset n(0); n < nodes.Count(); n++)
+ {
+ if (beforeElement)
+ {
+ pSearchFirst.push_front(nodes[n]);
+
+ if (*pReference == *nodes[n])
+ {
+ beforeElement = false;
+ }
+ }
+ pSearchSecond.push_back(nodes[n]);
+ }
+
+ // 1. Search up until we hit the top of the document
+
+ pTextNd = SearchForStyleAnchor(pSelf, pSearchFirst, rRefMark);
+ if (pTextNd)
+ {
+ break;
+ }
+
+ // 2. Search down until we hit the bottom of the document
+
+ pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark);
+ if (pTextNd)
+ {
+ break;
+ }
+
+ // Again, we need to remember that Word styles are not case sensitive
+
+ pTextNd = SearchForStyleAnchor(pSelf, pSearchFirst, rRefMark,
+ false /* bCaseSensitive */);
+ if (pTextNd)
+ {
+ break;
+ }
+
+ pTextNd = SearchForStyleAnchor(pSelf, pSearchSecond, rRefMark,
+ false /* bCaseSensitive */);
+ break;
+ }
+ default:
+ OSL_FAIL("<SwGetRefFieldType::FindAnchor(..)> - unknown getref element type");
+ }
+
+ if (pTextNd)
+ {
+ *pStt = 0;
+ if (pEnd)
+ *pEnd = pTextNd->GetText().getLength();
+ }
+
+ 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( OUString _aName ) : aName(std::move( _aName )), 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_rDoc)
+ return;
+
+ 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_rDoc, rDestDoc, rRefField, true);
+ }
+ break;
+
+ case REF_FOOTNOTE:
+ case REF_ENDNOTE:
+ aFntMap.Check(m_rDoc, rDestDoc, rRefField, false);
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */