1851 lines
67 KiB
C++
1851 lines
67 KiB
C++
/* -*- 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;
|
|
|
|
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) const
|
|
{
|
|
const SwTextNode* pReferencedTextNode( GetReferencedTextNode(/*pTextNode*/nullptr, /*pFrame*/nullptr) );
|
|
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:
|
|
case REF_SETREFATTR:
|
|
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;
|
|
|
|
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::SwFormatChange)
|
|
{
|
|
// forward to text fields, they "expand" the text
|
|
CallSwClientNotify(rHint);
|
|
return;
|
|
}
|
|
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 */
|
|
};
|
|
|
|
SwTextNode* SearchForStyleAnchor(SwTextNode* pSelf, SwNode* pCurrent,
|
|
std::u16string_view rStyleName,
|
|
sal_Int32 *const pStart, sal_Int32 *const pEnd,
|
|
bool bCaseSensitive = true)
|
|
{
|
|
if (*pCurrent == *pSelf)
|
|
return nullptr;
|
|
|
|
SwTextNode* pTextNode = pCurrent->GetTextNode();
|
|
if (!pTextNode)
|
|
return nullptr;
|
|
|
|
if (bCaseSensitive
|
|
? pTextNode->GetFormatColl()->GetName() == rStyleName
|
|
: pTextNode->GetFormatColl()->GetName().equalsIgnoreAsciiCase(rStyleName))
|
|
{
|
|
*pStart = 0;
|
|
if (pEnd)
|
|
{
|
|
*pEnd = pTextNode->GetText().getLength();
|
|
}
|
|
return pTextNode;
|
|
}
|
|
|
|
if (auto const pHints = pTextNode->GetpSwpHints())
|
|
{
|
|
for (size_t i = 0; i < pHints->Count(); ++i)
|
|
{
|
|
auto const*const pHint(pHints->Get(i));
|
|
if (pHint->Which() == RES_TXTATR_CHARFMT)
|
|
{
|
|
if (bCaseSensitive
|
|
? pHint->GetCharFormat().GetCharFormat()->HasName(rStyleName)
|
|
: pHint->GetCharFormat().GetCharFormat()->GetName().equalsIgnoreAsciiCase(rStyleName))
|
|
{
|
|
*pStart = pHint->GetStart();
|
|
if (pEnd)
|
|
{
|
|
*pEnd = *pHint->End();
|
|
}
|
|
return pTextNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
/// Picks the first text node with a matching style from the specified node range
|
|
SwTextNode* SearchForStyleAnchor(SwTextNode* pSelf, const SwNodes& rNodes, SwNodeOffset nNodeStart, SwNodeOffset nNodeEnd, bool bSearchBackward,
|
|
std::u16string_view rStyleName,
|
|
sal_Int32 *const pStart, sal_Int32 *const pEnd,
|
|
bool bCaseSensitive = true)
|
|
{
|
|
if (!bSearchBackward)
|
|
{
|
|
for (SwNodeOffset nCurrent = nNodeStart; nCurrent <= nNodeEnd; ++nCurrent)
|
|
{
|
|
SwNode* pCurrent = rNodes[nCurrent];
|
|
SwTextNode* pFound = SearchForStyleAnchor(pSelf, pCurrent, rStyleName, pStart, pEnd, bCaseSensitive);
|
|
if (pFound)
|
|
return pFound;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (SwNodeOffset nCurrent = nNodeEnd; nCurrent >= nNodeStart; --nCurrent)
|
|
{
|
|
SwNode* pCurrent = rNodes[nCurrent];
|
|
SwTextNode* pFound = SearchForStyleAnchor(pSelf, pCurrent, rStyleName, pStart, pEnd, bCaseSensitive);
|
|
if (pFound)
|
|
return pFound;
|
|
}
|
|
}
|
|
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:
|
|
{
|
|
auto ppMark = pDoc->getIDocumentMarkAccess()->findMark(rRefMark);
|
|
if (ppMark != pDoc->getIDocumentMarkAccess()->getAllMarksEnd()
|
|
&& (!pLayout || !pLayout->IsHideRedlines()
|
|
|| !sw::IsMarkHidden(*pLayout, **ppMark)))
|
|
{
|
|
const ::sw::mark::MarkBase* 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))
|
|
{
|
|
assert(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*>(SwNodes::GoNext(&aIdx));
|
|
}
|
|
*pStt = 0;
|
|
if( pEnd )
|
|
*pEnd = 0;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case REF_STYLE:
|
|
pTextNd = FindAnchorRefStyle(pDoc, rRefMark, nFlags,
|
|
pStt, pEnd, pLayout, pSelf, pContentFrame);
|
|
break;
|
|
}
|
|
|
|
return pTextNd;
|
|
}
|
|
|
|
SwTextNode* SwGetRefFieldType::FindAnchorRefStyle(SwDoc* pDoc, const OUString& rRefMark,
|
|
sal_uInt16 nFlags,
|
|
sal_Int32* pStt, sal_Int32* pEnd, SwRootFrame const* const pLayout,
|
|
SwTextNode* pSelf, SwFrame* pContentFrame)
|
|
{
|
|
if (!pSelf)
|
|
return nullptr;
|
|
|
|
SwTextNode* pTextNd = nullptr;
|
|
|
|
StyleRefElementType elementType = StyleRefElementType::Default;
|
|
const SwTextNode* pReference = nullptr;
|
|
IDocumentRedlineAccess & rIDRA(pDoc->getIDocumentRedlineAccess());
|
|
|
|
/* 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*>(SwNodes::GoNext(&aIdx));
|
|
|
|
if (*pSelf == *pFootnoteNode)
|
|
{
|
|
elementType = StyleRefElementType::Reference;
|
|
pReference = &pFootnoteIdx->GetTextNode();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pDoc->IsInHeaderFooter(*pSelf))
|
|
{
|
|
elementType = StyleRefElementType::Marginal;
|
|
}
|
|
|
|
if (pReference == nullptr)
|
|
{
|
|
pReference = pSelf;
|
|
}
|
|
|
|
// undocumented Word feature: 1 = "Heading 1" etc.
|
|
OUString const styleName(
|
|
(rRefMark.getLength() == 1 && '1' <= rRefMark[0] && rRefMark[0] <= '9')
|
|
? SwStyleNameMapper::GetProgName(RES_POOLCOLL_HEADLINE1 + rRefMark[0] - '1', rRefMark)
|
|
: rRefMark);
|
|
|
|
switch (elementType)
|
|
{
|
|
case Marginal:
|
|
pTextNd = FindAnchorRefStyleMarginal(pDoc, nFlags,
|
|
pStt, pEnd, pSelf, pContentFrame, pReference, styleName);
|
|
break;
|
|
case Reference:
|
|
case Default:
|
|
pTextNd = FindAnchorRefStyleOther(pDoc,
|
|
pStt, pEnd, pSelf, pReference, styleName);
|
|
break;
|
|
default:
|
|
OSL_FAIL("<SwGetRefFieldType::FindAnchor(..)> - unknown getref element type");
|
|
}
|
|
return pTextNd;
|
|
}
|
|
|
|
SwTextNode* SwGetRefFieldType::FindAnchorRefStyleMarginal(SwDoc* pDoc,
|
|
sal_uInt16 nFlags,
|
|
sal_Int32* pStt, sal_Int32* pEnd,
|
|
SwTextNode* pSelf, SwFrame* pContentFrame,
|
|
const SwTextNode* pReference, std::u16string_view styleName)
|
|
{
|
|
// For marginals, styleref tries to act on the current page first
|
|
// 1. Get the page we're on, search it from top to bottom
|
|
|
|
SwTextNode* pTextNd = nullptr;
|
|
|
|
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;
|
|
}
|
|
|
|
SwNodeOffset nPageStart = pPageStart->GetIndex();
|
|
SwNodeOffset nPageEnd = pPageEnd->GetIndex();
|
|
const SwNodes& nodes = pDoc->GetNodes();
|
|
|
|
pTextNd = SearchForStyleAnchor(pSelf, nodes, nPageStart, nPageEnd, bFlagFromBottom, styleName, pStt, pEnd);
|
|
if (pTextNd)
|
|
return pTextNd;
|
|
|
|
// 2. Search up from the top of the page
|
|
pTextNd = SearchForStyleAnchor(pSelf, nodes, SwNodeOffset(0), nPageStart - 1, /*bBackwards*/true, styleName, pStt, pEnd);
|
|
if (pTextNd)
|
|
return pTextNd;
|
|
|
|
// 3. Search down from the bottom of the page
|
|
pTextNd = SearchForStyleAnchor(pSelf, nodes, nPageEnd + 1, nodes.Count() - 1, /*bBackwards*/false, styleName, pStt, pEnd);
|
|
if (pTextNd)
|
|
return pTextNd;
|
|
|
|
// 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, nodes, nPageStart, nPageEnd, bFlagFromBottom, styleName, pStt, pEnd,
|
|
false /* bCaseSensitive */);
|
|
if (pTextNd)
|
|
return pTextNd;
|
|
|
|
pTextNd = SearchForStyleAnchor(pSelf, nodes, SwNodeOffset(0), nPageStart - 1, /*bBackwards*/true, styleName, pStt, pEnd,
|
|
false /* bCaseSensitive */);
|
|
if (pTextNd)
|
|
return pTextNd;
|
|
|
|
pTextNd = SearchForStyleAnchor(pSelf, nodes, nPageEnd + 1, nodes.Count() - 1, /*bBackwards*/false, styleName, pStt, pEnd,
|
|
false /* bCaseSensitive */);
|
|
return pTextNd;
|
|
}
|
|
|
|
SwTextNode* SwGetRefFieldType::FindAnchorRefStyleOther(SwDoc* pDoc,
|
|
sal_Int32* pStt, sal_Int32* pEnd,
|
|
SwTextNode* pSelf,
|
|
const SwTextNode* pReference, std::u16string_view styleName)
|
|
{
|
|
// 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
|
|
|
|
SwNodeOffset nReference = pReference->GetIndex();
|
|
const SwNodes& nodes = pDoc->GetNodes();
|
|
|
|
if (&nodes != &pReference->GetNodes())
|
|
return nullptr;
|
|
|
|
// It is possible to end up here, with a pReference pointer which points to a node which has already been
|
|
// removed from the nodes array, which means that calling GetIndex() returns an incorrect index.
|
|
if (nReference >= nodes.Count() || nodes[nReference] != pReference)
|
|
nReference = nodes.Count() - 1;
|
|
|
|
SwTextNode* pTextNd = nullptr;
|
|
|
|
// 1. Search up until we hit the top of the document
|
|
|
|
pTextNd = SearchForStyleAnchor(pSelf, nodes, SwNodeOffset(0), nReference, /*bBackwards*/true, styleName, pStt, pEnd);
|
|
if (pTextNd)
|
|
return pTextNd;
|
|
|
|
// 2. Search down until we hit the bottom of the document
|
|
|
|
pTextNd = SearchForStyleAnchor(pSelf, nodes, nReference + 1, nodes.Count() - 1, /*bBackwards*/false, styleName, pStt, pEnd);
|
|
if (pTextNd)
|
|
return pTextNd;
|
|
|
|
// Again, we need to remember that Word styles are not case sensitive
|
|
|
|
pTextNd = SearchForStyleAnchor(pSelf, nodes, SwNodeOffset(0), nReference, /*bBackwards*/true, styleName, pStt, pEnd,
|
|
false /* bCaseSensitive */);
|
|
if (pTextNd)
|
|
return pTextNd;
|
|
|
|
pTextNd = SearchForStyleAnchor(pSelf, nodes, nReference + 1, nodes.Count() - 1, /*bBackwards*/false, styleName, pStt, pEnd,
|
|
false /* bCaseSensitive */);
|
|
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: */
|