diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sw/source/core/fields | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/fields')
-rw-r--r-- | sw/source/core/fields/authfld.cxx | 782 | ||||
-rw-r--r-- | sw/source/core/fields/cellfml.cxx | 1253 | ||||
-rw-r--r-- | sw/source/core/fields/chpfld.cxx | 309 | ||||
-rw-r--r-- | sw/source/core/fields/dbfld.cxx | 869 | ||||
-rw-r--r-- | sw/source/core/fields/ddefld.cxx | 389 | ||||
-rw-r--r-- | sw/source/core/fields/ddetbl.cxx | 216 | ||||
-rw-r--r-- | sw/source/core/fields/docufld.cxx | 2660 | ||||
-rw-r--r-- | sw/source/core/fields/expfld.cxx | 1445 | ||||
-rw-r--r-- | sw/source/core/fields/fldbas.cxx | 865 | ||||
-rw-r--r-- | sw/source/core/fields/flddat.cxx | 226 | ||||
-rw-r--r-- | sw/source/core/fields/flddropdown.cxx | 217 | ||||
-rw-r--r-- | sw/source/core/fields/fldlst.cxx | 151 | ||||
-rw-r--r-- | sw/source/core/fields/macrofld.cxx | 222 | ||||
-rw-r--r-- | sw/source/core/fields/postithelper.cxx | 279 | ||||
-rw-r--r-- | sw/source/core/fields/reffld.cxx | 1500 | ||||
-rw-r--r-- | sw/source/core/fields/scrptfld.cxx | 119 | ||||
-rw-r--r-- | sw/source/core/fields/tblcalc.cxx | 211 | ||||
-rw-r--r-- | sw/source/core/fields/textapi.cxx | 193 | ||||
-rw-r--r-- | sw/source/core/fields/usrfld.cxx | 384 |
19 files changed, 12290 insertions, 0 deletions
diff --git a/sw/source/core/fields/authfld.cxx b/sw/source/core/fields/authfld.cxx new file mode 100644 index 000000000..8a6212e22 --- /dev/null +++ b/sw/source/core/fields/authfld.cxx @@ -0,0 +1,782 @@ +/* -*- 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 <memory> + +#include <libxml/xmlwriter.h> + +#include <comphelper/string.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <o3tl/any.hxx> +#include <o3tl/string_view.hxx> +#include <osl/diagnose.h> +#include <tools/urlobj.hxx> +#include <swtypes.hxx> +#include <strings.hrc> +#include <authfld.hxx> +#include <expfld.hxx> +#include <pam.hxx> +#include <cntfrm.hxx> +#include <rootfrm.hxx> +#include <tox.hxx> +#include <txmsrt.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <ndtxt.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <unofldmid.h> +#include <unoprnms.hxx> +#include <docsh.hxx> + +#include <com/sun/star/beans/PropertyValues.hpp> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; + +SwAuthEntry::SwAuthEntry(const SwAuthEntry& rCopy) + : SimpleReferenceObject() +{ + for(int i = 0; i < AUTH_FIELD_END; ++i) + m_aAuthFields[i] = rCopy.m_aAuthFields[i]; +} + +bool SwAuthEntry::operator==(const SwAuthEntry& rComp) const +{ + for(int i = 0; i < AUTH_FIELD_END; ++i) + if(m_aAuthFields[i] != rComp.m_aAuthFields[i]) + return false; + return true; +} + +SwAuthorityFieldType::SwAuthorityFieldType(SwDoc* pDoc) + : SwFieldType( SwFieldIds::TableOfAuthorities ), + m_pDoc(pDoc), + m_cPrefix('['), + m_cSuffix(']'), + m_bIsSequence(false), + m_bSortByDocument(true), + m_eLanguage(::GetAppLanguage()) +{ +} + +SwAuthorityFieldType::~SwAuthorityFieldType() +{ +} + +std::unique_ptr<SwFieldType> SwAuthorityFieldType::Copy() const +{ + return std::make_unique<SwAuthorityFieldType>(m_pDoc); +} + +void SwAuthorityFieldType::RemoveField(const SwAuthEntry* pEntry) +{ + for(SwAuthDataArr::size_type j = 0; j < m_DataArr.size(); ++j) + { + if(m_DataArr[j].get() == pEntry) + { + if (m_DataArr[j]->m_nCount <= 1) + { + m_DataArr.erase(m_DataArr.begin() + j); + //re-generate positions of the fields + DelSequenceArray(); + } + return; + } + } + assert(false && "SwAuthorityFieldType::RemoveField: pEntry was not added previously"); +} + +SwAuthEntry* SwAuthorityFieldType::AddField(std::u16string_view rFieldContents) +{ + rtl::Reference<SwAuthEntry> pEntry(new SwAuthEntry); + sal_Int32 nIdx{ 0 }; + for( sal_Int32 i = 0; i < AUTH_FIELD_END; ++i ) + pEntry->SetAuthorField( static_cast<ToxAuthorityField>(i), + OUString(o3tl::getToken(rFieldContents, 0, TOX_STYLE_DELIMITER, nIdx ))); + + for (const auto &rpTemp : m_DataArr) + { + if (*rpTemp == *pEntry) + { + return rpTemp.get(); + } + } + + //if it is a new Entry - insert + m_DataArr.push_back(std::move(pEntry)); + //re-generate positions of the fields + DelSequenceArray(); + return m_DataArr.back().get(); +} + +void SwAuthorityFieldType::GetAllEntryIdentifiers( + std::vector<OUString>& rToFill )const +{ + for (const auto & rpTemp : m_DataArr) + { + rToFill.push_back(rpTemp->GetAuthorField(AUTH_FIELD_IDENTIFIER)); + } +} + +SwAuthEntry* SwAuthorityFieldType::GetEntryByIdentifier( + std::u16string_view rIdentifier)const +{ + for (const auto &rpTemp : m_DataArr) + { + if (rIdentifier == rpTemp->GetAuthorField(AUTH_FIELD_IDENTIFIER)) + { + return rpTemp.get(); + } + } + return nullptr; +} + +bool SwAuthorityFieldType::ChangeEntryContent(const SwAuthEntry* pNewEntry) +{ + for (auto &rpTemp : m_DataArr) + { + if (rpTemp->GetAuthorField(AUTH_FIELD_IDENTIFIER) == + pNewEntry->GetAuthorField(AUTH_FIELD_IDENTIFIER)) + { + for(int i = 0; i < AUTH_FIELD_END; ++i) + { + rpTemp->SetAuthorField(static_cast<ToxAuthorityField>(i), + pNewEntry->GetAuthorField(static_cast<ToxAuthorityField>(i))); + } + return true; + } + } + return false; +} + +/// appends a new entry (if new) and returns the copied entry +SwAuthEntry* SwAuthorityFieldType::AppendField( const SwAuthEntry& rInsert ) +{ + for( SwAuthDataArr::size_type nRet = 0; nRet < m_DataArr.size(); ++nRet ) + { + if( *m_DataArr[ nRet ] == rInsert ) + return m_DataArr[ nRet ].get(); + } + + //if it is a new Entry - insert + m_DataArr.push_back(new SwAuthEntry(rInsert)); + return m_DataArr.back().get(); +} + +std::unique_ptr<SwTOXInternational> SwAuthorityFieldType::CreateTOXInternational() const +{ + return std::make_unique<SwTOXInternational>(m_eLanguage, SwTOIOptions::NONE, m_sSortAlgorithm); +} + +sal_uInt16 SwAuthorityFieldType::GetSequencePos(const SwAuthEntry* pAuthEntry, + SwRootFrame const*const pLayout) +{ + //find the field in a sorted array of handles, + if(!m_SequArr.empty() && m_SequArr.size() != m_DataArr.size()) + DelSequenceArray(); + if(m_SequArr.empty()) + { + IDocumentRedlineAccess const& rIDRA(m_pDoc->getIDocumentRedlineAccess()); + std::unique_ptr<SwTOXInternational> pIntl = CreateTOXInternational(); + // sw_redlinehide: need 2 arrays because the sorting may be different, + // if multiple fields refer to the same entry and first one is deleted + std::vector<std::unique_ptr<SwTOXSortTabBase>> aSortArr; + std::vector<std::unique_ptr<SwTOXSortTabBase>> aSortArrRLHidden; + std::vector<SwFormatField*> vFields; + GatherFields(vFields); + for(SwFormatField* pFormatField : vFields) + { + const SwTextField* pTextField = pFormatField->GetTextField(); + if(!pTextField || !pTextField->GetpTextNode()) + { + continue; + } + const SwTextNode& rFieldTextNode = pTextField->GetTextNode(); + SwPosition aFieldPos(rFieldTextNode); + SwDoc& rDoc = const_cast<SwDoc&>(rFieldTextNode.GetDoc()); + SwContentFrame *pFrame = rFieldTextNode.getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ); + const SwTextNode* pTextNode = nullptr; + if(pFrame && !pFrame->IsInDocBody()) + pTextNode = GetBodyTextNode( rDoc, aFieldPos, *pFrame ); + //if no text node could be found or the field is in the document + //body the directly available text node will be used + if(!pTextNode) + pTextNode = &rFieldTextNode; + if (pTextNode->GetText().isEmpty() + || !pTextNode->getLayoutFrame(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()) + || !pTextNode->GetNodes().IsDocNodes()) + { + continue; + } + auto const InsertImpl = [&pIntl, pTextNode, pFormatField] + (std::vector<std::unique_ptr<SwTOXSortTabBase>> & rSortArr) + { + std::unique_ptr<SwTOXAuthority> pNew( + new SwTOXAuthority(*pTextNode, *pFormatField, *pIntl)); + + for (size_t i = 0; i < rSortArr.size(); ++i) + { + SwTOXSortTabBase* pOld = rSortArr[i].get(); + if (pOld->equivalent(*pNew)) + { + //only the first occurrence in the document + //has to be in the array + if (pOld->sort_lt(*pNew)) + pNew.reset(); + else // remove the old content + rSortArr.erase(rSortArr.begin() + i); + break; + } + } + //if it still exists - insert at the correct position + if (pNew) + { + size_t j {0}; + + while (j < rSortArr.size()) + { + SwTOXSortTabBase* pOld = rSortArr[j].get(); + if (pNew->sort_lt(*pOld)) + break; + ++j; + } + rSortArr.insert(rSortArr.begin() + j, std::move(pNew)); + } + }; + InsertImpl(aSortArr); + if (!sw::IsFieldDeletedInModel(rIDRA, *pTextField)) + { + InsertImpl(aSortArrRLHidden); + } + } + + for(auto & pBase : aSortArr) + { + SwFormatField& rFormatField = static_cast<SwTOXAuthority&>(*pBase).GetFieldFormat(); + SwAuthorityField* pAField = static_cast<SwAuthorityField*>(rFormatField.GetField()); + m_SequArr.push_back(pAField->GetAuthEntry()); + } + for (auto & pBase : aSortArrRLHidden) + { + SwFormatField& rFormatField = static_cast<SwTOXAuthority&>(*pBase).GetFieldFormat(); + SwAuthorityField* pAField = static_cast<SwAuthorityField*>(rFormatField.GetField()); + m_SequArrRLHidden.push_back(pAField->GetAuthEntry()); + } + } + //find nHandle + auto const& rSequArr(pLayout && pLayout->IsHideRedlines() ? m_SequArrRLHidden : m_SequArr); + for (std::vector<sal_IntPtr>::size_type i = 0; i < rSequArr.size(); ++i) + { + if (rSequArr[i] == pAuthEntry) + { + return i + 1; + } + } + return 0; +} + +void SwAuthorityFieldType::QueryValue( Any& rVal, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + case FIELD_PROP_PAR2: + { + OUString sVal; + sal_Unicode uRet = FIELD_PROP_PAR1 == nWhichId ? m_cPrefix : m_cSuffix; + if(uRet) + sVal = OUString(uRet); + rVal <<= sVal; + } + break; + case FIELD_PROP_PAR3: + rVal <<= GetSortAlgorithm(); + break; + + case FIELD_PROP_BOOL1: + rVal <<= m_bIsSequence; + break; + + case FIELD_PROP_BOOL2: + rVal <<= m_bSortByDocument; + break; + + case FIELD_PROP_LOCALE: + rVal <<= LanguageTag(GetLanguage()).getLocale(); + break; + + case FIELD_PROP_PROP_SEQ: + { + Sequence<PropertyValues> aRet(m_SortKeyArr.size()); + PropertyValues* pValues = aRet.getArray(); + for(SortKeyArr::size_type i = 0; i < m_SortKeyArr.size(); ++i) + { + const SwTOXSortKey* pKey = &m_SortKeyArr[i]; + pValues[i].realloc(2); + PropertyValue* pValue = pValues[i].getArray(); + pValue[0].Name = UNO_NAME_SORT_KEY; + pValue[0].Value <<= sal_Int16(pKey->eField); + pValue[1].Name = UNO_NAME_IS_SORT_ASCENDING; + pValue[1].Value <<= pKey->bSortAscending; + } + rVal <<= aRet; + } + break; + default: + assert(false); + } +} + +void SwAuthorityFieldType::PutValue( const Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + case FIELD_PROP_PAR2: + { + OUString sTmp; + rAny >>= sTmp; + const sal_Unicode uSet = !sTmp.isEmpty() ? sTmp[0] : 0; + if( FIELD_PROP_PAR1 == nWhichId ) + m_cPrefix = uSet; + else + m_cSuffix = uSet; + } + break; + case FIELD_PROP_PAR3: + { + OUString sTmp; + rAny >>= sTmp; + SetSortAlgorithm(sTmp); + break; + } + case FIELD_PROP_BOOL1: + m_bIsSequence = *o3tl::doAccess<bool>(rAny); + break; + case FIELD_PROP_BOOL2: + m_bSortByDocument = *o3tl::doAccess<bool>(rAny); + break; + + case FIELD_PROP_LOCALE: + { + css::lang::Locale aLocale; + if( rAny >>= aLocale ) + SetLanguage( LanguageTag::convertToLanguageType( aLocale )); + } + break; + + case FIELD_PROP_PROP_SEQ: + { + Sequence<PropertyValues> aSeq; + if( rAny >>= aSeq ) + { + m_SortKeyArr.clear(); + const PropertyValues* pValues = aSeq.getConstArray(); + //TODO: Limiting to the first SAL_MAX_UINT16 elements of aSeq so that size of + // m_SortKeyArr remains in range of sal_uInt16, as GetSortKeyCount and GetSortKey + // still expect m_SortKeyArr to be indexed by sal_uInt16: + auto nSize = std::min<sal_Int32>(aSeq.getLength(), SAL_MAX_UINT16); + for(sal_Int32 i = 0; i < nSize; i++) + { + SwTOXSortKey aSortKey; + for(const PropertyValue& rValue : pValues[i]) + { + if(rValue.Name == UNO_NAME_SORT_KEY) + { + sal_Int16 nVal = -1; rValue.Value >>= nVal; + if(nVal >= 0 && nVal < AUTH_FIELD_END) + aSortKey.eField = static_cast<ToxAuthorityField>(nVal); + } + else if(rValue.Name == UNO_NAME_IS_SORT_ASCENDING) + { + aSortKey.bSortAscending = *o3tl::doAccess<bool>(rValue.Value); + } + } + m_SortKeyArr.push_back(aSortKey); + } + } + } + break; + default: + assert(false); + } +} + +void SwAuthorityFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint) +{ + //re-generate positions of the fields + DelSequenceArray(); + CallSwClientNotify(rHint); +} + +sal_uInt16 SwAuthorityFieldType::GetSortKeyCount() const +{ + return m_SortKeyArr.size(); +} + +const SwTOXSortKey* SwAuthorityFieldType::GetSortKey(sal_uInt16 nIdx) const +{ + if(m_SortKeyArr.size() > nIdx) + return &m_SortKeyArr[nIdx]; + OSL_FAIL("Sort key not found"); + return nullptr; +} + +void SwAuthorityFieldType::SetSortKeys(sal_uInt16 nKeyCount, SwTOXSortKey const aKeys[]) +{ + m_SortKeyArr.clear(); + for(sal_uInt16 i = 0; i < nKeyCount; i++) + if(aKeys[i].eField < AUTH_FIELD_END) + m_SortKeyArr.push_back(aKeys[i]); +} + +SwAuthorityField::SwAuthorityField( SwAuthorityFieldType* pInitType, + std::u16string_view rFieldContents ) + : SwField(pInitType) + , m_nTempSequencePos( -1 ) + , m_nTempSequencePosRLHidden( -1 ) +{ + m_xAuthEntry = pInitType->AddField( rFieldContents ); +} + +SwAuthorityField::SwAuthorityField( SwAuthorityFieldType* pInitType, + SwAuthEntry* pAuthEntry ) + : SwField( pInitType ) + , m_xAuthEntry( pAuthEntry ) + , m_nTempSequencePos( -1 ) + , m_nTempSequencePosRLHidden( -1 ) +{ +} + +SwAuthorityField::~SwAuthorityField() +{ + static_cast<SwAuthorityFieldType* >(GetTyp())->RemoveField(m_xAuthEntry.get()); +} + +OUString SwAuthorityField::ExpandImpl(SwRootFrame const*const pLayout) const +{ + return ConditionalExpandAuthIdentifier(pLayout); +} + +OUString SwAuthorityField::ConditionalExpandAuthIdentifier( + SwRootFrame const*const pLayout) const +{ + SwAuthorityFieldType* pAuthType = static_cast<SwAuthorityFieldType*>(GetTyp()); + OUString sRet; + if(pAuthType->GetPrefix()) + sRet = OUString(pAuthType->GetPrefix()); + + if( pAuthType->IsSequence() ) + { + sal_IntPtr & rnTempSequencePos(pLayout && pLayout->IsHideRedlines() + ? m_nTempSequencePosRLHidden : m_nTempSequencePos); + if(!pAuthType->GetDoc()->getIDocumentFieldsAccess().IsExpFieldsLocked()) + rnTempSequencePos = pAuthType->GetSequencePos(m_xAuthEntry.get(), pLayout); + if (0 <= rnTempSequencePos) + sRet += OUString::number(rnTempSequencePos); + } + else + { + //TODO: Expand to: identifier, number sequence, ... + if(m_xAuthEntry) + { + OUString sIdentifier(m_xAuthEntry->GetAuthorField(AUTH_FIELD_IDENTIFIER)); + // tdf#107784 Use title if it's a ooxml citation + if (o3tl::starts_with(o3tl::trim(sIdentifier), u"CITATION")) + return m_xAuthEntry->GetAuthorField(AUTH_FIELD_TITLE); + else + sRet += sIdentifier; + } + } + if(pAuthType->GetSuffix()) + sRet += OUStringChar(pAuthType->GetSuffix()); + return sRet; +} + +OUString SwAuthorityField::ExpandCitation(ToxAuthorityField eField, + SwRootFrame const*const pLayout) const +{ + SwAuthorityFieldType* pAuthType = static_cast<SwAuthorityFieldType*>(GetTyp()); + OUString sRet; + + if( pAuthType->IsSequence() ) + { + sal_IntPtr & rnTempSequencePos(pLayout && pLayout->IsHideRedlines() + ? m_nTempSequencePosRLHidden : m_nTempSequencePos); + if(!pAuthType->GetDoc()->getIDocumentFieldsAccess().IsExpFieldsLocked()) + rnTempSequencePos = pAuthType->GetSequencePos(m_xAuthEntry.get(), pLayout); + if (0 <= rnTempSequencePos) + sRet += OUString::number(rnTempSequencePos); + } + else + { + //TODO: Expand to: identifier, number sequence, ... + if(m_xAuthEntry) + sRet += m_xAuthEntry->GetAuthorField(eField); + } + return sRet; +} + +std::unique_ptr<SwField> SwAuthorityField::Copy() const +{ + SwAuthorityFieldType* pAuthType = static_cast<SwAuthorityFieldType*>(GetTyp()); + return std::make_unique<SwAuthorityField>(pAuthType, m_xAuthEntry.get()); +} + +const OUString & SwAuthorityField::GetFieldText(ToxAuthorityField eField) const +{ + return m_xAuthEntry->GetAuthorField( eField ); +} + +void SwAuthorityField::SetPar1(const OUString& rStr) +{ + SwAuthorityFieldType* pInitType = static_cast<SwAuthorityFieldType* >(GetTyp()); + pInitType->RemoveField(m_xAuthEntry.get()); + m_xAuthEntry = pInitType->AddField(rStr); +} + +OUString SwAuthorityField::GetDescription() const +{ + return SwResId(STR_AUTHORITY_ENTRY); +} + +OUString SwAuthorityField::GetAuthority(const SwTextAttr* pTextAttr, + const SwRootFrame* pLayout) const +{ + OUString aText; + + SwForm aForm(TOX_AUTHORITIES); + if (!pTextAttr) + { + return aText; + } + + auto& rFormatField = const_cast<SwFormatField&>(pTextAttr->GetFormatField()); + SwTextField* pTextField = rFormatField.GetTextField(); + if (!pTextField) + { + return aText; + } + + const SwTextNode& rNode = pTextField->GetTextNode(); + const auto pFieldType = static_cast<const SwAuthorityFieldType*>(GetTyp()); + std::unique_ptr<SwTOXInternational> pIntl(pFieldType->CreateTOXInternational()); + SwTOXAuthority aAuthority(rNode, rFormatField, *pIntl); + sal_uInt16 nLevel = aAuthority.GetLevel(); + SwFormTokens aPattern = aForm.GetPattern(nLevel); + aAuthority.InitText(pLayout); + for (const auto& rToken : aPattern) + { + switch (rToken.eTokenType) + { + case TOKEN_TEXT: + { + aText += rToken.sText; + break; + } + case TOKEN_AUTHORITY: + { + sal_uInt16 eField = rToken.nAuthorityField; + aText += aAuthority.GetText(eField, pLayout); + break; + } + default: + break; + } + } + + return aText; +} + +bool SwAuthorityField::HasURL() const +{ + const OUString& rURL = GetAuthEntry()->GetAuthorField(AUTH_FIELD_URL); + return !rURL.isEmpty(); +} + +OUString SwAuthorityField::GetAbsoluteURL() const +{ + const OUString& rURL = GetAuthEntry()->GetAuthorField(AUTH_FIELD_URL); + SwDoc* pDoc = static_cast<SwAuthorityFieldType*>(GetTyp())->GetDoc(); + SwDocShell* pDocShell = pDoc->GetDocShell(); + OUString aBasePath = pDocShell->getDocumentBaseURL(); + return INetURLObject::GetAbsURL(aBasePath, rURL, INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::WithCharset); +} + +void SwAuthorityField::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwAuthorityField")); + SwField::dumpAsXml(pWriter); + + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_xAuthEntry")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", m_xAuthEntry.get()); + if (m_xAuthEntry.is()) + { + m_xAuthEntry->dumpAsXml(pWriter); + } + (void)xmlTextWriterEndElement(pWriter); + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_nTempSequencePos")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::number(m_nTempSequencePos).getStr())); + (void)xmlTextWriterEndElement(pWriter); + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_nTempSequencePosRLHidden")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::number(m_nTempSequencePosRLHidden).getStr())); + (void)xmlTextWriterEndElement(pWriter); + + (void)xmlTextWriterEndElement(pWriter); +} + +const char* const aFieldNames[] = +{ + "Identifier", + "BibiliographicType", + "Address", + "Annote", + "Author", + "Booktitle", + "Chapter", + "Edition", + "Editor", + "Howpublished", + "Institution", + "Journal", + "Month", + "Note", + "Number", + "Organizations", + "Pages", + "Publisher", + "School", + "Series", + "Title", + "Report_Type", + "Volume", + "Year", + "URL", + "Custom1", + "Custom2", + "Custom3", + "Custom4", + "Custom5", + "ISBN", + "LocalURL" +}; + +void SwAuthEntry::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwAuthEntry")); + + for (int i = 0; i < AUTH_FIELD_END; ++i) + { + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_aAuthField")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("key"), BAD_CAST(aFieldNames[i])); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(m_aAuthFields[i].toUtf8().getStr())); + (void)xmlTextWriterEndElement(pWriter); + } + + (void)xmlTextWriterEndElement(pWriter); +} + +bool SwAuthorityField::QueryValue( Any& rAny, sal_uInt16 /*nWhichId*/ ) const +{ + if(!GetTyp()) + return false; + if(!m_xAuthEntry) + return false; + Sequence <PropertyValue> aRet(AUTH_FIELD_END); + PropertyValue* pValues = aRet.getArray(); + for(int i = 0; i < AUTH_FIELD_END; ++i) + { + pValues[i].Name = OUString::createFromAscii(aFieldNames[i]); + const OUString& sField = m_xAuthEntry->GetAuthorField(static_cast<ToxAuthorityField>(i)); + if(i == AUTH_FIELD_AUTHORITY_TYPE) + pValues[i].Value <<= sal_Int16(sField.toInt32()); + else + pValues[i].Value <<= sField; + } + rAny <<= aRet; + /* FIXME: it is weird that we always return false here */ + return false; +} + +static sal_Int32 lcl_Find(std::u16string_view rFieldName) +{ + for(sal_Int32 i = 0; i < AUTH_FIELD_END; ++i) + if(o3tl::equalsAscii(rFieldName, aFieldNames[i])) + return i; + return -1; +} + +bool SwAuthorityField::PutValue( const Any& rAny, sal_uInt16 /*nWhichId*/ ) +{ + if(!GetTyp() || !m_xAuthEntry) + return false; + + Sequence <PropertyValue> aParam; + if(!(rAny >>= aParam)) + return false; + + OUStringBuffer sBuf(+AUTH_FIELD_LOCAL_URL); + comphelper::string::padToLength(sBuf, AUTH_FIELD_LOCAL_URL, TOX_STYLE_DELIMITER); + OUString sToSet(sBuf.makeStringAndClear()); + for(const PropertyValue& rParam : std::as_const(aParam)) + { + const sal_Int32 nFound = lcl_Find(rParam.Name); + if(nFound >= 0) + { + OUString sContent; + if(AUTH_FIELD_AUTHORITY_TYPE == nFound) + { + sal_Int16 nVal = 0; + rParam.Value >>= nVal; + sContent = OUString::number(nVal); + } + else + rParam.Value >>= sContent; + sToSet = comphelper::string::setToken(sToSet, nFound, TOX_STYLE_DELIMITER, sContent); + } + } + + static_cast<SwAuthorityFieldType*>(GetTyp())->RemoveField(m_xAuthEntry.get()); + m_xAuthEntry = static_cast<SwAuthorityFieldType*>(GetTyp())->AddField(sToSet); + + /* FIXME: it is weird that we always return false here */ + return false; +} + +SwFieldType* SwAuthorityField::ChgTyp( SwFieldType* pFieldTyp ) +{ + SwAuthorityFieldType* pSrcTyp = static_cast<SwAuthorityFieldType*>(GetTyp()), + * pDstTyp = static_cast<SwAuthorityFieldType*>(pFieldTyp); + if( pSrcTyp != pDstTyp ) + { + const SwAuthEntry* pSrcEntry = m_xAuthEntry.get(); + m_xAuthEntry = pDstTyp->AppendField( *pSrcEntry ); + pSrcTyp->RemoveField( pSrcEntry ); + SwField::ChgTyp( pFieldTyp ); + } + return pSrcTyp; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/cellfml.cxx b/sw/source/core/fields/cellfml.cxx new file mode 100644 index 000000000..339a0337b --- /dev/null +++ b/sw/source/core/fields/cellfml.cxx @@ -0,0 +1,1253 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <float.h> +#include <hintids.hxx> +#include <hints.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <frmfmt.hxx> +#include <layfrm.hxx> +#include <cntfrm.hxx> +#include <tabfrm.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <ndtxt.hxx> +#include <swtable.hxx> +#include <tblsel.hxx> +#include <cellfml.hxx> +#include <calc.hxx> +#include <expfld.hxx> +#include <usrfld.hxx> +#include <flddat.hxx> +#include <cellatr.hxx> +#include <ndindex.hxx> +#include <frameformats.hxx> +#include <comphelper/string.hxx> +#include <o3tl/string_view.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <svl/numformat.hxx> + +namespace +{ + +const sal_Unicode cRelSeparator = ','; +const sal_Unicode cRelIdentifier = '\x12'; // CTRL-R + +enum +{ + cMAXSTACKSIZE = 50 +}; + +} + +static const SwFrame* lcl_GetBoxFrame( const SwTableBox& rBox ); +static sal_Int32 lcl_GetLongBoxNum( OUString& rStr ); +static const SwTableBox* lcl_RelToBox( const SwTable& rTable, + const SwTableBox* pRefBox, + const OUString& sGetName); +static OUString lcl_BoxNmToRel( const SwTable& rTable, + const SwTableNode& rTableNd, + const OUString& sRefBoxNm, + const OUString& sGetStr, + bool bExtrnlNm); + +/** Get value of this box. + * + * The value is comes from the first TextNode. If it starts with a number/ + * formula then calculate it, if it starts with a field then get the value. + * All other conditions return 0 (and an error?). + */ +double SwTableBox::GetValue( SwTableCalcPara& rCalcPara ) const +{ + double nRet = 0; + + if( rCalcPara.m_rCalc.IsCalcError() ) + return nRet; // stop if there is already an error set + + rCalcPara.m_rCalc.SetCalcError( SwCalcError::Syntax ); // default: error + + // no content box? + if( !m_pStartNode ) + return nRet; + + if( rCalcPara.IncStackCnt() ) + return nRet; + + rCalcPara.SetLastTableBox( this ); + + // Does it create a recursion? + SwTableBox* pBox = const_cast<SwTableBox*>(this); + if( rCalcPara.m_pBoxStack->find( pBox ) != rCalcPara.m_pBoxStack->end() ) + return nRet; // already on the stack: error + + // re-start with this box + rCalcPara.SetLastTableBox( this ); + + rCalcPara.m_pBoxStack->insert( pBox ); // add + do { // Middle-Check-Loop, so that we can jump from here. Used so that the box pointer + // will be removed from stack at the end. + SwDoc* pDoc = GetFrameFormat()->GetDoc(); + + if( const SwTableBoxFormula* pFormulaItem = GetFrameFormat()->GetItemIfSet( + RES_BOXATR_FORMULA, false ) ) + { + rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status + if( !pFormulaItem->IsValid() ) + { + // calculate + const SwTable* pTmp = rCalcPara.m_pTable; + rCalcPara.m_pTable = &pBox->GetSttNd()->FindTableNode()->GetTable(); + const_cast<SwTableBoxFormula*>(pFormulaItem)->Calc( rCalcPara, nRet ); + + if( !rCalcPara.IsStackOverflow() ) + { + SwFrameFormat* pFormat = pBox->ClaimFrameFormat(); + SfxItemSetFixed<RES_BOXATR_BEGIN,RES_BOXATR_END-1> aTmp( pDoc->GetAttrPool() ); + aTmp.Put( SwTableBoxValue( nRet ) ); + if( SfxItemState::SET != pFormat->GetItemState( RES_BOXATR_FORMAT )) + aTmp.Put( SwTableBoxNumFormat( 0 )); + pFormat->SetFormatAttr( aTmp ); + } + rCalcPara.m_pTable = pTmp; + } + else + nRet = GetFrameFormat()->GetTableBoxValue().GetValue(); + break; + } + else if( const SwTableBoxValue* pBoxValueItem = pBox->GetFrameFormat()->GetItemIfSet( + RES_BOXATR_VALUE, false ) ) + { + rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status + nRet = pBoxValueItem->GetValue(); + break; + } + + SwTextNode* pTextNd = pDoc->GetNodes()[ m_pStartNode->GetIndex() + 1 ]->GetTextNode(); + if( !pTextNd ) + break; + + sal_Int32 nSttPos = 0; + OUString sText = pTextNd->GetText(); + + // use text of the tracked changes + if ( sText.getLength() > 0 && + sText[0] != CH_TXTATR_BREAKWORD && sText[0] != CH_TXTATR_INWORD ) + { + sText = pTextNd->GetRedlineText(); + } + + while ( nSttPos < sText.getLength() && ( sText[nSttPos]==' ' || sText[nSttPos]=='\t' ) ) + ++nSttPos; + + // if there is a calculation field at position 1, get the value of it + const bool bOK = nSttPos<sText.getLength(); + const sal_Unicode Char = bOK ? sText[nSttPos] : 0; + SwTextField * pTextField = nullptr; + if ( bOK && (Char==CH_TXTATR_BREAKWORD || Char==CH_TXTATR_INWORD) ) + { + pTextField = static_txtattr_cast<SwTextField*>(pTextNd->GetTextAttrForCharAt(nSttPos, RES_TXTATR_FIELD)); + } + if ( pTextField != nullptr ) + { + rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status + + const SwField* pField = pTextField->GetFormatField().GetField(); + switch ( pField->GetTyp()->Which() ) + { + case SwFieldIds::SetExp: + nRet = static_cast<const SwSetExpField*>(pField)->GetValue(rCalcPara.m_pLayout); + break; + case SwFieldIds::User: + nRet = static_cast<const SwUserField*>(pField)->GetValue(); + break; + case SwFieldIds::Table: + { + SwTableField* pTableField = const_cast<SwTableField*>(static_cast<const SwTableField*>(pField)); + if( !pTableField->IsValid() ) + { + // use the right table! + const SwTable* pTmp = rCalcPara.m_pTable; + rCalcPara.m_pTable = &pTextNd->FindTableNode()->GetTable(); + pTableField->CalcField( rCalcPara ); + rCalcPara.m_pTable = pTmp; + } + nRet = pTableField->GetValue(); + } + break; + + case SwFieldIds::DateTime: + nRet = static_cast<const SwDateTimeField*>( pField )->GetValue(); + break; + + case SwFieldIds::JumpEdit: + //JP 14.09.98: Bug 56112 - placeholder never have the right content! + nRet = 0; + break; + + default: + nRet = rCalcPara.m_rCalc.Calculate( pField->ExpandField(true, nullptr) ).GetDouble(); + } + } + else if ( nSttPos < sText.getLength() + && Char == CH_TXT_ATR_INPUTFIELDSTART ) + { + const SwTextInputField * pTextInputField = + dynamic_cast< const SwTextInputField* >( + pTextNd->GetTextAttrAt( nSttPos, RES_TXTATR_INPUTFIELD ) ); + if ( pTextInputField == nullptr ) + break; + nRet = rCalcPara.m_rCalc.Calculate( pTextInputField->GetFieldContent() ).GetDouble(); + } + else if ( Char != CH_TXTATR_BREAKWORD ) + { + // result is 0 but no error! + rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status + + double aNum = 0.0; + sText = bOK ? sText.copy( nSttPos ) : OUString(); + sal_uInt32 nFormatIndex = GetFrameFormat()->GetTableBoxNumFormat().GetValue(); + + SvNumberFormatter* pNumFormatr = pDoc->GetNumberFormatter(); + + const SvNumFormatType nFormatType = pNumFormatr->GetType( nFormatIndex ); + if( nFormatType == SvNumFormatType::TEXT ) + nFormatIndex = 0; + // JP 22.04.98: Bug 49659 - special treatment for percentages + else if( !sText.isEmpty() && + SvNumFormatType::PERCENT == nFormatType) + { + sal_uInt32 nTmpFormat = 0; + if( pDoc->IsNumberFormat( sText, nTmpFormat, aNum ) && + SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat )) + sText += "%"; + } + + if( pDoc->IsNumberFormat( sText, nFormatIndex, aNum )) + nRet = aNum; + else + rCalcPara.m_rCalc.SetCalcError( SwCalcError::NaN ); // set for interoperability functions + } + // ?? otherwise it is an error + } while( false ); + + if( !rCalcPara.IsStackOverflow() ) + { + rCalcPara.m_pBoxStack->erase( pBox ); // remove from stack + rCalcPara.DecStackCnt(); + } + + //JP 12.01.99: error detection, Bug 60794 + if( DBL_MAX == nRet ) + rCalcPara.m_rCalc.SetCalcError( SwCalcError::Syntax ); // set error + + return nRet; +} + +// structure needed for calculation of tables + +SwTableCalcPara::SwTableCalcPara(SwCalc& rCalculator, const SwTable& rTable, + SwRootFrame const*const pLayout) + : m_pLastTableBox(nullptr) + , m_nStackCount( 0 ) + , m_nMaxSize( cMAXSTACKSIZE ) + , m_pLayout(pLayout) + , m_pBoxStack( new SwTableSortBoxes ) + , m_rCalc( rCalculator ) + , m_pTable( &rTable ) +{ +} + +SwTableCalcPara::~SwTableCalcPara() +{ +} + +bool SwTableCalcPara::CalcWithStackOverflow() +{ + // If a stack overflow was detected, redo with last box. + sal_uInt16 nSaveMaxSize = m_nMaxSize; + + m_nMaxSize = cMAXSTACKSIZE - 5; + sal_uInt16 nCnt = 0; + SwTableBoxes aStackOverflows; + do { + SwTableBox* pBox = const_cast<SwTableBox*>(m_pLastTableBox); + m_nStackCount = 0; + m_rCalc.SetCalcError( SwCalcError::NONE ); + aStackOverflows.insert( aStackOverflows.begin() + nCnt++, pBox ); + + m_pBoxStack->erase( pBox ); + pBox->GetValue( *this ); + } while( IsStackOverflow() ); + + m_nMaxSize = cMAXSTACKSIZE - 3; // decrease at least one level + + // if recursion was detected + m_nStackCount = 0; + m_rCalc.SetCalcError( SwCalcError::NONE ); + m_pBoxStack->clear(); + + while( !m_rCalc.IsCalcError() && nCnt ) + { + aStackOverflows[ --nCnt ]->GetValue( *this ); + if( IsStackOverflow() && !CalcWithStackOverflow() ) + break; + } + + m_nMaxSize = nSaveMaxSize; + aStackOverflows.clear(); + return !m_rCalc.IsCalcError(); +} + +SwTableFormula::SwTableFormula( const OUString& rFormula ) +: m_sFormula( rFormula ) +, m_eNmType( EXTRNL_NAME ) +, m_bValidValue( false ) +{ +} + +SwTableFormula::~SwTableFormula() +{ +} + +void SwTableFormula::MakeFormula_( const SwTable& rTable, OUStringBuffer& rNewStr, + OUString& rFirstBox, OUString* pLastBox, void* pPara ) const +{ + SwTableCalcPara* pCalcPara = static_cast<SwTableCalcPara*>(pPara); + if( pCalcPara->m_rCalc.IsCalcError() ) // stop if there is already an error set + return; + + SwTableBox *pEndBox = nullptr; + + rFirstBox = rFirstBox.copy(1); // erase label of this box + // a region in this area? + if( pLastBox ) + { + pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64())); + + // Is it actually a valid pointer? + if( rTable.GetTabSortBoxes().find( pEndBox ) == rTable.GetTabSortBoxes().end() ) + pEndBox = nullptr; + rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 ); + } + SwTableBox* pSttBox = reinterpret_cast<SwTableBox*>( + sal::static_int_cast<sal_IntPtr>(rFirstBox.toInt64())); + // Is it actually a valid pointer? + if( rTable.GetTabSortBoxes().find( pSttBox ) == rTable.GetTabSortBoxes().end() ) + pSttBox = nullptr; + + rNewStr.append(" "); + if( pEndBox && pSttBox ) // area? + { + // get all selected boxes via layout and calculate their values + SwSelBoxes aBoxes; + GetBoxes( *pSttBox, *pEndBox, aBoxes ); + + // don't use empty cells or cells with text content as zeroes in interoperability functions + sal_Int16 nUseOnlyNumber = -1; + + rNewStr.append("("); + bool bDelim = false; + for (size_t n = 0; n < aBoxes.size() && + !pCalcPara->m_rCalc.IsCalcError(); ++n) + { + const SwTableBox* pTableBox = aBoxes[n]; + if ( pTableBox->getRowSpan() >= 1 ) + { + double fVal = pTableBox->GetValue( *pCalcPara ); + + if ( pCalcPara->m_rCalc.IsCalcNotANumber() ) + { + if ( nUseOnlyNumber == -1 ) + { + OUString sFormula = rNewStr.toString().toAsciiUpperCase(); + nUseOnlyNumber = sal_Int16( + sFormula.lastIndexOf("AVERAGE") > -1 || + sFormula.lastIndexOf("COUNT") > -1 || + sFormula.lastIndexOf("PRODUCT") > -1 ); + } + if ( nUseOnlyNumber > 0 ) + continue; + } + + if( bDelim ) + rNewStr.append(cListDelim); + bDelim = true; + rNewStr.append(pCalcPara->m_rCalc.GetStrResult( fVal )); + } + } + rNewStr.append(")"); + } + else if( pSttBox && !pLastBox ) // only the StartBox? + { + // JP 12.01.99: and no EndBox in the formula! + // calculate the value of the box + if ( pSttBox->getRowSpan() >= 1 ) + { + rNewStr.append("("); + double fVal = pSttBox->GetValue( *pCalcPara ); + // don't use empty cell or a cell with text content as zero in interoperability functions + // (except PRODUCT, where the result is correct anyway) + if ( !pCalcPara->m_rCalc.IsCalcNotANumber() || + ( rNewStr.toString().toAsciiUpperCase().lastIndexOf("AVERAGE") == -1 && + rNewStr.toString().toAsciiUpperCase().lastIndexOf("COUNT") == -1 ) ) + { + rNewStr.append(pCalcPara->m_rCalc.GetStrResult( fVal )); + } + rNewStr.append(")"); + } + } + else + pCalcPara->m_rCalc.SetCalcError( SwCalcError::Syntax ); // set error + rNewStr.append(" "); +} + +void SwTableFormula::RelNmsToBoxNms( const SwTable& rTable, OUStringBuffer& rNewStr, + OUString& rFirstBox, OUString* pLastBox, void* pPara ) const +{ + // relative name w.r.t. box name (external presentation) + SwNode* pNd = static_cast<SwNode*>(pPara); + OSL_ENSURE( pNd, "Field isn't in any TextNode" ); + const SwTableBox *pBox = rTable.GetTableBox( + pNd->FindTableBoxStartNode()->GetIndex() ); + + rNewStr.append(rFirstBox[0]); // get label for the box + rFirstBox = rFirstBox.copy(1); + if( pLastBox ) + { + const SwTableBox *pRelLastBox = lcl_RelToBox( rTable, pBox, *pLastBox ); + if ( pRelLastBox ) + rNewStr.append(pRelLastBox->GetName()); + else + rNewStr.append("A1"); + rNewStr.append(":"); + rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 ); + } + + const SwTableBox *pRelFirstBox = lcl_RelToBox( rTable, pBox, rFirstBox ); + + if (pRelFirstBox) + rNewStr.append(pRelFirstBox->GetName()); + else + rNewStr.append("A1"); + + // get label for the box + rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]); +} + +void SwTableFormula::RelBoxNmsToPtr( const SwTable& rTable, OUStringBuffer& rNewStr, + OUString& rFirstBox, OUString* pLastBox, void* pPara ) const +{ + // relative name w.r.t. box name (internal presentation) + SwNode* pNd = static_cast<SwNode*>(pPara); + OSL_ENSURE( pNd, "Field not placed in any Node" ); + const SwTableBox *pBox = rTable.GetTableBox( + pNd->FindTableBoxStartNode()->GetIndex() ); + + rNewStr.append(rFirstBox[0]); // get label for the box + rFirstBox = rFirstBox.copy(1); + if( pLastBox ) + { + const SwTableBox *pRelLastBox = lcl_RelToBox( rTable, pBox, *pLastBox ); + if ( pRelLastBox ) + rNewStr.append(reinterpret_cast<sal_PtrDiff>(pRelLastBox)); + else + rNewStr.append("0"); + rNewStr.append(":"); + rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 ); + } + + const SwTableBox *pRelFirstBox = lcl_RelToBox( rTable, pBox, rFirstBox ); + if ( pRelFirstBox ) + rNewStr.append(reinterpret_cast<sal_PtrDiff>(pRelFirstBox)); + else + rNewStr.append("0"); + + // get label for the box + rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]); +} + +void SwTableFormula::BoxNmsToRelNm( const SwTable& rTable, OUStringBuffer& rNewStr, + OUString& rFirstBox, OUString* pLastBox, void* pPara ) const +{ + // box name (external presentation) w.r.t. relative name + SwNode* pNd = static_cast<SwNode*>(pPara); + OSL_ENSURE( pNd, "Field not placed in any Node" ); + const SwTableNode* pTableNd = pNd->FindTableNode(); + + OUString sRefBoxNm; + if( &pTableNd->GetTable() == &rTable ) + { + const SwTableBox *pBox = rTable.GetTableBox( + pNd->FindTableBoxStartNode()->GetIndex() ); + OSL_ENSURE( pBox, "Field not placed in any Table" ); + sRefBoxNm = pBox->GetName(); + } + + rNewStr.append(rFirstBox[0]); // get label for the box + rFirstBox = rFirstBox.copy(1); + if( pLastBox ) + { + rNewStr.append(lcl_BoxNmToRel( rTable, *pTableNd, sRefBoxNm, *pLastBox, + m_eNmType == EXTRNL_NAME )); + rNewStr.append(":"); + rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 ); + } + + rNewStr.append(lcl_BoxNmToRel( rTable, *pTableNd, sRefBoxNm, rFirstBox, + m_eNmType == EXTRNL_NAME )); + + // get label for the box + rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]); +} + +void SwTableFormula::PtrToBoxNms( const SwTable& rTable, OUStringBuffer& rNewStr, + OUString& rFirstBox, OUString* pLastBox, void* ) const +{ + // area in these parentheses? + SwTableBox* pBox; + + rNewStr.append(rFirstBox[0]); // get label for the box + rFirstBox = rFirstBox.copy(1); + if( pLastBox ) + { + pBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64())); + + // Is it actually a valid pointer? + if( rTable.GetTabSortBoxes().find( pBox ) != rTable.GetTabSortBoxes().end() ) + rNewStr.append(pBox->GetName()); + else + rNewStr.append("?"); + rNewStr.append(":"); + rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 ); + } + + pBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.toInt64())); + // Is it actually a valid pointer? + if( rTable.GetTabSortBoxes().find( pBox ) != rTable.GetTabSortBoxes().end() ) + rNewStr.append(pBox->GetName()); + else + rNewStr.append("?"); + + // get label for the box + rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]); +} + +void SwTableFormula::BoxNmsToPtr( const SwTable& rTable, OUStringBuffer& rNewStr, + OUString& rFirstBox, OUString* pLastBox, void* ) const +{ + // area in these parentheses? + const SwTableBox* pBox; + + rNewStr.append(rFirstBox[0]); // get label for the box + rFirstBox = rFirstBox.copy(1); + if( pLastBox ) + { + pBox = rTable.GetTableBox( *pLastBox ); + rNewStr.append(OUString::number(reinterpret_cast<sal_PtrDiff>(pBox)) + + ":"); + rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 ); + } + + pBox = rTable.GetTableBox( rFirstBox ); + rNewStr.append(reinterpret_cast<sal_PtrDiff>(pBox)) + .append(rFirstBox[ rFirstBox.getLength()-1 ]); // get label for the box +} + +/// create external formula (for UI) +void SwTableFormula::PtrToBoxNm( const SwTable* pTable ) +{ + const SwNode* pNd = nullptr; + FnScanFormula fnFormula = nullptr; + switch (m_eNmType) + { + case INTRNL_NAME: + if( pTable ) + fnFormula = &SwTableFormula::PtrToBoxNms; + break; + case REL_NAME: + if( pTable ) + { + fnFormula = &SwTableFormula::RelNmsToBoxNms; + pNd = GetNodeOfFormula(); + } + break; + case EXTRNL_NAME: + return; + } + m_sFormula = ScanString( fnFormula, *pTable, const_cast<void*>(static_cast<void const *>(pNd)) ); + m_eNmType = EXTRNL_NAME; +} + +/// create internal formula (in CORE) +void SwTableFormula::BoxNmToPtr( const SwTable* pTable ) +{ + const SwNode* pNd = nullptr; + FnScanFormula fnFormula = nullptr; + switch (m_eNmType) + { + case EXTRNL_NAME: + if( pTable ) + fnFormula = &SwTableFormula::BoxNmsToPtr; + break; + case REL_NAME: + if( pTable ) + { + fnFormula = &SwTableFormula::RelBoxNmsToPtr; + pNd = GetNodeOfFormula(); + } + break; + case INTRNL_NAME: + return; + } + m_sFormula = ScanString( fnFormula, *pTable, const_cast<void*>(static_cast<void const *>(pNd)) ); + m_eNmType = INTRNL_NAME; +} + +/// create relative formula (for copy) +void SwTableFormula::ToRelBoxNm( const SwTable* pTable ) +{ + const SwNode* pNd = nullptr; + FnScanFormula fnFormula = nullptr; + switch (m_eNmType) + { + case INTRNL_NAME: + case EXTRNL_NAME: + if( pTable ) + { + fnFormula = &SwTableFormula::BoxNmsToRelNm; + pNd = GetNodeOfFormula(); + } + break; + case REL_NAME: + return; + } + m_sFormula = ScanString( fnFormula, *pTable, const_cast<void*>(static_cast<void const *>(pNd)) ); + m_eNmType = REL_NAME; +} + +OUString SwTableFormula::ScanString( FnScanFormula fnFormula, const SwTable& rTable, + void* pPara ) const +{ + OUStringBuffer aStr; + sal_Int32 nFormula = 0; + sal_Int32 nEnd = 0; + + do { + // If the formula is preceded by a name, use this table! + const SwTable* pTable = &rTable; + + sal_Int32 nStt = m_sFormula.indexOf( '<', nFormula ); + if ( nStt>=0 ) + { + while ( nStt>=0 ) + { + const sal_Int32 nNxt = nStt+1; + if (nNxt>=m_sFormula.getLength()) + { + nStt = -1; + break; + } + if ( m_sFormula[nNxt]!=' ' && m_sFormula[nNxt]!='=' ) + break; + nStt = m_sFormula.indexOf( '<', nNxt ); + } + + if ( nStt>=0 ) + // Start searching from current position, which is valid for sure + nEnd = m_sFormula.indexOf( '>', nStt ); + } + if (nStt<0 || nEnd<0 ) + { + // set the rest and finish + aStr.append(m_sFormula.subView(nFormula)); + break; + } + + // write beginning + aStr.append(m_sFormula.subView(nFormula, nStt - nFormula)); + + if (fnFormula) + { + sal_Int32 nSeparator = 0; + // Is a table name preceded? + // JP 16.02.99: SplitMergeBoxNm take care of the name themself + // JP 22.02.99: Linux compiler needs cast + // JP 28.06.99: rel. BoxName has no preceding tablename! + if( fnFormula != &SwTableFormula::SplitMergeBoxNm_ && + m_sFormula.getLength()>(nStt+1) && cRelIdentifier != m_sFormula[nStt+1] && + (nSeparator = m_sFormula.indexOf( '.', nStt ))>=0 + && nSeparator < nEnd ) + { + OUString sTableNm( m_sFormula.copy( nStt, nEnd - nStt )); + + // If there are dots in the name, then they appear in pairs (e.g. A1.1.1)! + if( (comphelper::string::getTokenCount(sTableNm, '.') - 1) & 1 ) + { + sTableNm = sTableNm.copy( 0, nSeparator - nStt ); + + // when creating a formula the table name is unwanted + if( fnFormula != &SwTableFormula::MakeFormula_ ) + aStr.append(sTableNm); + nStt = nSeparator; + + sTableNm = sTableNm.copy( 1 ); // delete separator + if( sTableNm != rTable.GetFrameFormat()->GetName() ) + { + // then search for table + const SwTable* pFnd = FindTable( + *rTable.GetFrameFormat()->GetDoc(), + sTableNm ); + if( pFnd ) + pTable = pFnd; + // ?? + OSL_ENSURE( pFnd, "No table found. What now?" ); + } + } + } + + OUString sBox( m_sFormula.copy( nStt, nEnd - nStt + 1 )); + // area in these parentheses? + nSeparator = m_sFormula.indexOf( ':', nStt ); + if ( nSeparator>=0 && nSeparator<nEnd ) + { + // without opening parenthesis + OUString aFirstBox( m_sFormula.copy( nStt+1, nSeparator - nStt - 1 )); + (this->*fnFormula)( *pTable, aStr, sBox, &aFirstBox, pPara ); + } + else + (this->*fnFormula)( *pTable, aStr, sBox, nullptr, pPara ); + } + + nFormula = nEnd+1; + } while( true ); + return aStr.makeStringAndClear(); +} + +const SwTable* SwTableFormula::FindTable( SwDoc& rDoc, std::u16string_view rNm ) +{ + const SwFrameFormats& rTableFormats = *rDoc.GetTableFrameFormats(); + const SwTable* pTmpTable = nullptr, *pRet = nullptr; + for( auto nFormatCnt = rTableFormats.size(); nFormatCnt; ) + { + SwFrameFormat* pFormat = rTableFormats[ --nFormatCnt ]; + // if we are called from Sw3Writer, a number is dependent on the format name + SwTableBox* pFBox; + if ( rNm == o3tl::getToken(pFormat->GetName(), 0, 0x0a) && + nullptr != ( pTmpTable = SwTable::FindTable( pFormat ) ) && + nullptr != (pFBox = pTmpTable->GetTabSortBoxes()[0] ) && + pFBox->GetSttNd() && + pFBox->GetSttNd()->GetNodes().IsDocNodes() ) + { + // a table in the normal NodesArr + pRet = pTmpTable; + break; + } + } + return pRet; +} + +static const SwFrame* lcl_GetBoxFrame( const SwTableBox& rBox ) +{ + SwNodeIndex aIdx( *rBox.GetSttNd() ); + SwContentNode* pCNd = aIdx.GetNodes().GoNext( &aIdx ); + OSL_ENSURE( pCNd, "Box has no TextNode" ); + Point aPt; // get the first frame of the layout - table headline + std::pair<Point, bool> const tmp(aPt, false); + return pCNd->getLayoutFrame(pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp); +} + +static sal_Int32 lcl_GetLongBoxNum( OUString& rStr ) +{ + sal_Int32 nRet; + const sal_Int32 nPos = rStr.indexOf( cRelSeparator ); + if ( nPos<0 ) + { + nRet = rStr.toInt32(); + rStr.clear(); + } + else + { + nRet = o3tl::toInt32(rStr.subView( 0, nPos )); + rStr = rStr.copy( nPos+1 ); + } + return nRet; +} + +static const SwTableBox* lcl_RelToBox( const SwTable& rTable, + const SwTableBox* pRefBox, + const OUString& _sGetName ) +{ + // get line + const SwTableBox* pBox = nullptr; + OUString sGetName = _sGetName; + + // Is it really a relative value? + if ( cRelIdentifier == sGetName[0] ) // yes + { + if( !pRefBox ) + return nullptr; + + sGetName = sGetName.copy( 1 ); + + const SwTableLines* pLines = &rTable.GetTabLines(); + const SwTableBoxes* pBoxes; + const SwTableLine* pLine; + + // determine starting values of the box,... + pBox = pRefBox; + pLine = pBox->GetUpper(); + while( pLine->GetUpper() ) + { + pBox = pLine->GetUpper(); + pLine = pBox->GetUpper(); + } + sal_uInt16 nSttBox = pLine->GetBoxPos( pBox ); + sal_uInt16 nSttLine = rTable.GetTabLines().GetPos( pLine ); + + const sal_Int32 nBoxOffset = lcl_GetLongBoxNum( sGetName ) + nSttBox; + const sal_Int32 nLineOffset = lcl_GetLongBoxNum( sGetName ) + nSttLine; + + if( nBoxOffset < 0 || + nLineOffset < 0 ) + return nullptr; + + if( o3tl::make_unsigned(nLineOffset) >= pLines->size() ) + return nullptr; + + pLine = (*pLines)[ nLineOffset ]; + + // ... then search the box + pBoxes = &pLine->GetTabBoxes(); + if( o3tl::make_unsigned(nBoxOffset) >= pBoxes->size() ) + return nullptr; + pBox = (*pBoxes)[ nBoxOffset ]; + + while (!sGetName.isEmpty()) + { + nSttBox = SwTable::GetBoxNum( sGetName ); + pLines = &pBox->GetTabLines(); + if( nSttBox ) + --nSttBox; + + nSttLine = SwTable::GetBoxNum( sGetName ); + + // determine line + if( !nSttLine || nSttLine > pLines->size() ) + break; + pLine = (*pLines)[ nSttLine-1 ]; + + // determine box + pBoxes = &pLine->GetTabBoxes(); + if( nSttBox >= pBoxes->size() ) + break; + pBox = (*pBoxes)[ nSttBox ]; + } + + if( pBox ) + { + if( !pBox->GetSttNd() ) + // "bubble up" to first box + while( !pBox->GetTabLines().empty() ) + pBox = pBox->GetTabLines().front()->GetTabBoxes().front(); + } + } + else + { + // otherwise it is an absolute external presentation + pBox = rTable.GetTableBox( sGetName ); + } + return pBox; +} + +static OUString lcl_BoxNmToRel( const SwTable& rTable, const SwTableNode& rTableNd, + const OUString& _sRefBoxNm, const OUString& _sTmp, bool bExtrnlNm ) +{ + OUString sTmp = _sTmp; + OUString sRefBoxNm = _sRefBoxNm; + if( !bExtrnlNm ) + { + // convert into external presentation + SwTableBox* pBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(sTmp.toInt64())); + if( rTable.GetTabSortBoxes().find( pBox ) == rTable.GetTabSortBoxes().end() ) + return OUString('?'); + sTmp = pBox->GetName(); + } + + // If the formula is spanning over a table then keep external presentation + if( &rTable == &rTableNd.GetTable() ) + { + tools::Long nBox = SwTable::GetBoxNum( sTmp, true ); + nBox -= SwTable::GetBoxNum( sRefBoxNm, true ); + tools::Long nLine = SwTable::GetBoxNum( sTmp ); + nLine -= SwTable::GetBoxNum( sRefBoxNm ); + + const OUString sCpy = sTmp; //JP 01.11.95: add rest from box name + + sTmp = OUStringChar(cRelIdentifier) + OUString::number( nBox ) + + OUStringChar(cRelSeparator) + OUString::number( nLine ); + + if (!sCpy.isEmpty()) + { + sTmp += OUStringChar(cRelSeparator) + sCpy; + } + } + + if (sTmp.endsWith(">")) + return sTmp.copy(0, sTmp.getLength()-1 ); + + return sTmp; +} + +void SwTableFormula::GetBoxesOfFormula( const SwTable& rTable, + SwSelBoxes& rBoxes ) +{ + rBoxes.clear(); + + BoxNmToPtr( &rTable ); + ScanString( &SwTableFormula::GetFormulaBoxes, rTable, &rBoxes ); +} + +void SwTableFormula::GetFormulaBoxes( const SwTable& rTable, OUStringBuffer& , + OUString& rFirstBox, OUString* pLastBox, void* pPara ) const +{ + SwSelBoxes* pBoxes = static_cast<SwSelBoxes*>(pPara); + SwTableBox* pEndBox = nullptr; + + rFirstBox = rFirstBox.copy(1); // delete box label + // area in these parentheses? + if( pLastBox ) + { + pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64())); + + // Is it actually a valid pointer? + if( rTable.GetTabSortBoxes().find( pEndBox ) == rTable.GetTabSortBoxes().end() ) + pEndBox = nullptr; + rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 ); + } + + SwTableBox *pSttBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.toInt64())); + // Is it actually a valid pointer? + if( !pSttBox || rTable.GetTabSortBoxes().find( pSttBox ) == rTable.GetTabSortBoxes().end() ) + return; + + if ( pEndBox ) // area? + { + // get all selected boxes via layout and calculate their values + SwSelBoxes aBoxes; + GetBoxes( *pSttBox, *pEndBox, aBoxes ); + pBoxes->insert( aBoxes ); + } + else // only the StartBox? + pBoxes->insert( pSttBox ); +} + +void SwTableFormula::GetBoxes( const SwTableBox& rSttBox, + const SwTableBox& rEndBox, + SwSelBoxes& rBoxes ) +{ + // get all selected boxes via layout + const SwLayoutFrame *pStt, *pEnd; + const SwFrame* pFrame = lcl_GetBoxFrame( rSttBox ); + pStt = pFrame ? pFrame->GetUpper() : nullptr; + pFrame = lcl_GetBoxFrame( rEndBox ); + pEnd = pFrame ? pFrame->GetUpper() : nullptr; + if( !pStt || !pEnd ) + return ; // no valid selection + + GetTableSel( pStt, pEnd, rBoxes, nullptr ); + + const SwTable* pTable = pStt->FindTabFrame()->GetTable(); + + // filter headline boxes + if( pTable->GetRowsToRepeat() <= 0 ) + return; + + do { // middle-check loop + const SwTableLine* pLine = rSttBox.GetUpper(); + while( pLine->GetUpper() ) + pLine = pLine->GetUpper()->GetUpper(); + + if( pTable->IsHeadline( *pLine ) ) + break; // headline in this area! + + // maybe start and end are swapped + pLine = rEndBox.GetUpper(); + while ( pLine->GetUpper() ) + pLine = pLine->GetUpper()->GetUpper(); + + if( pTable->IsHeadline( *pLine ) ) + break; // headline in this area! + + const SwTabFrame *pStartTable = pStt->FindTabFrame(); + const SwTabFrame *pEndTable = pEnd->FindTabFrame(); + + if (pStartTable == pEndTable) // no split table + break; + + // then remove table headers + for (size_t n = 0; n < rBoxes.size(); ++n) + { + pLine = rBoxes[n]->GetUpper(); + while( pLine->GetUpper() ) + pLine = pLine->GetUpper()->GetUpper(); + + if( pTable->IsHeadline( *pLine ) ) + rBoxes.erase( rBoxes.begin() + n-- ); + } + } while( false ); +} + +/// Are all boxes valid that are referenced by the formula? +void SwTableFormula::HasValidBoxes_( const SwTable& rTable, OUStringBuffer& , + OUString& rFirstBox, OUString* pLastBox, void* pPara ) const +{ + bool* pBValid = static_cast<bool*>(pPara); + if( !(*pBValid) ) // wrong is wrong + return; + + SwTableBox* pSttBox = nullptr, *pEndBox = nullptr; + rFirstBox = rFirstBox.copy(1); // delete identifier of box + + // area in this parenthesis? + if( pLastBox ) + rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 ); + + switch (m_eNmType) + { + case INTRNL_NAME: + if( pLastBox ) + pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64())); + pSttBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.toInt64())); + break; + + case REL_NAME: + { + const SwNode* pNd = GetNodeOfFormula(); + const SwTableBox* pBox = !pNd ? nullptr + : const_cast<SwTableBox *>(rTable.GetTableBox( + pNd->FindTableBoxStartNode()->GetIndex() )); + if( pLastBox ) + pEndBox = const_cast<SwTableBox*>(lcl_RelToBox( rTable, pBox, *pLastBox )); + pSttBox = const_cast<SwTableBox*>(lcl_RelToBox( rTable, pBox, rFirstBox )); + } + break; + + case EXTRNL_NAME: + if( pLastBox ) + pEndBox = const_cast<SwTableBox*>(rTable.GetTableBox( *pLastBox )); + pSttBox = const_cast<SwTableBox*>(rTable.GetTableBox( rFirstBox )); + break; + } + + // Are these valid pointers? + if( ( pLastBox && + ( !pEndBox || rTable.GetTabSortBoxes().find( pEndBox ) == rTable.GetTabSortBoxes().end() ) ) || + ( !pSttBox || rTable.GetTabSortBoxes().find( pSttBox ) == rTable.GetTabSortBoxes().end() ) ) + *pBValid = false; +} + +bool SwTableFormula::HasValidBoxes() const +{ + bool bRet = true; + const SwNode* pNd = GetNodeOfFormula(); + if( pNd && nullptr != ( pNd = pNd->FindTableNode() ) ) + ScanString( &SwTableFormula::HasValidBoxes_, + static_cast<const SwTableNode*>(pNd)->GetTable(), &bRet ); + return bRet; +} + +sal_uInt16 SwTableFormula::GetLnPosInTable( const SwTable& rTable, const SwTableBox* pBox ) +{ + sal_uInt16 nRet = USHRT_MAX; + if( pBox ) + { + const SwTableLine* pLn = pBox->GetUpper(); + while( pLn->GetUpper() ) + pLn = pLn->GetUpper()->GetUpper(); + nRet = rTable.GetTabLines().GetPos( pLn ); + } + return nRet; +} + +void SwTableFormula::SplitMergeBoxNm_( const SwTable& rTable, OUStringBuffer& rNewStr, + OUString& rFirstBox, OUString* pLastBox, void* pPara ) const +{ + SwTableFormulaUpdate& rTableUpd = *static_cast<SwTableFormulaUpdate*>(pPara); + + rNewStr.append(rFirstBox[0]); // get label for the box + rFirstBox = rFirstBox.copy(1); + + OUString sTableNm; + const SwTable* pTable = &rTable; + + OUString* pTableNmBox = pLastBox ? pLastBox : &rFirstBox; + + const sal_Int32 nLastBoxLen = pTableNmBox->getLength(); + const sal_Int32 nSeparator = pTableNmBox->indexOf('.'); + if ( nSeparator>=0 && + // If there are dots in the name, then these appear in pairs (e.g. A1.1.1)! + (comphelper::string::getTokenCount(*pTableNmBox, '.') - 1) & 1 ) + { + sTableNm = pTableNmBox->copy( 0, nSeparator ); + *pTableNmBox = pTableNmBox->copy( nSeparator + 1); // remove dot + const SwTable* pFnd = FindTable( *rTable.GetFrameFormat()->GetDoc(), sTableNm ); + if( pFnd ) + pTable = pFnd; + + if( TBL_MERGETBL == rTableUpd.m_eFlags ) + { + if( pFnd ) + { + if( pFnd == rTableUpd.m_aData.pDelTable ) + { + if( rTableUpd.m_pTable != &rTable ) // not the current one + rNewStr.append(rTableUpd.m_pTable->GetFrameFormat()->GetName() + "."); // set new table name + rTableUpd.m_bModified = true; + } + else if( pFnd != rTableUpd.m_pTable || + ( rTableUpd.m_pTable != &rTable && &rTable != rTableUpd.m_aData.pDelTable)) + rNewStr.append(sTableNm + "."); // keep table name + else + rTableUpd.m_bModified = true; + } + else + rNewStr.append(sTableNm + "."); // keep table name + } + } + if( pTableNmBox == pLastBox ) + rFirstBox = rFirstBox.copy( nLastBoxLen + 1 ); + + SwTableBox* pSttBox = nullptr, *pEndBox = nullptr; + switch (m_eNmType) + { + case INTRNL_NAME: + if( pLastBox ) + pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64())); + pSttBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.toInt64())); + break; + + case REL_NAME: + { + const SwNode* pNd = GetNodeOfFormula(); + const SwTableBox* pBox = pNd ? pTable->GetTableBox( + pNd->FindTableBoxStartNode()->GetIndex() ) : nullptr; + if( pLastBox ) + pEndBox = const_cast<SwTableBox*>(lcl_RelToBox( *pTable, pBox, *pLastBox )); + pSttBox = const_cast<SwTableBox*>(lcl_RelToBox( *pTable, pBox, rFirstBox )); + } + break; + + case EXTRNL_NAME: + if( pLastBox ) + pEndBox = const_cast<SwTableBox*>(pTable->GetTableBox( *pLastBox )); + pSttBox = const_cast<SwTableBox*>(pTable->GetTableBox( rFirstBox )); + break; + } + + if( pLastBox && pTable->GetTabSortBoxes().find( pEndBox ) == pTable->GetTabSortBoxes().end() ) + pEndBox = nullptr; + if( pTable->GetTabSortBoxes().find( pSttBox ) == pTable->GetTabSortBoxes().end() ) + pSttBox = nullptr; + + if( TBL_SPLITTBL == rTableUpd.m_eFlags ) + { + // Where are the boxes - in the old or in the new table? + bool bInNewTable = false; + if( pLastBox ) + { + // It is the "first" box in this selection. It determines if the formula is placed in + // the new or the old table. + sal_uInt16 nEndLnPos = SwTableFormula::GetLnPosInTable( *pTable, pEndBox ), + nSttLnPos = SwTableFormula::GetLnPosInTable( *pTable, pSttBox ); + + if( USHRT_MAX != nSttLnPos && USHRT_MAX != nEndLnPos && + ((rTableUpd.m_nSplitLine <= nSttLnPos) == + (rTableUpd.m_nSplitLine <= nEndLnPos)) ) + { + // stay in same table + bInNewTable = rTableUpd.m_nSplitLine <= nEndLnPos && + pTable == rTableUpd.m_pTable; + } + else + { + // this is definitely an invalid formula, also mark as modified for Undo + rTableUpd.m_bModified = true; + if( pEndBox ) + bInNewTable = USHRT_MAX != nEndLnPos && + rTableUpd.m_nSplitLine <= nEndLnPos && + pTable == rTableUpd.m_pTable; + } + } + else + { + sal_uInt16 nSttLnPos = SwTableFormula::GetLnPosInTable( *pTable, pSttBox ); + // Put it in the new table? + bInNewTable = USHRT_MAX != nSttLnPos && + rTableUpd.m_nSplitLine <= nSttLnPos && + pTable == rTableUpd.m_pTable; + } + + // formula goes into new table + if( rTableUpd.m_bBehindSplitLine ) + { + if( !bInNewTable ) + { + rTableUpd.m_bModified = true; + rNewStr.append(rTableUpd.m_pTable->GetFrameFormat()->GetName() + "."); + } + else if( !sTableNm.isEmpty() ) + rNewStr.append(sTableNm + "."); + } + else if( bInNewTable ) + { + rTableUpd.m_bModified = true; + rNewStr.append(*rTableUpd.m_aData.pNewTableNm + "."); + } + else if( !sTableNm.isEmpty() ) + rNewStr.append(sTableNm + "."); + } + + if( pLastBox ) + rNewStr.append(OUString::number(reinterpret_cast<sal_PtrDiff>(pEndBox)) + ":"); + + rNewStr.append(reinterpret_cast<sal_PtrDiff>(pSttBox)) + .append(rFirstBox[ rFirstBox.getLength()-1] ); +} + +/// Create external formula but remember that the formula is placed in a split/merged table +void SwTableFormula::ToSplitMergeBoxNm( SwTableFormulaUpdate& rTableUpd ) +{ + const SwTable* pTable; + const SwNode* pNd = GetNodeOfFormula(); + if( pNd && nullptr != ( pNd = pNd->FindTableNode() )) + pTable = &static_cast<const SwTableNode*>(pNd)->GetTable(); + else + pTable = rTableUpd.m_pTable; + + m_sFormula = ScanString( &SwTableFormula::SplitMergeBoxNm_, *pTable, static_cast<void*>(&rTableUpd) ); + m_eNmType = INTRNL_NAME; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/chpfld.cxx b/sw/source/core/fields/chpfld.cxx new file mode 100644 index 000000000..fe591d11a --- /dev/null +++ b/sw/source/core/fields/chpfld.cxx @@ -0,0 +1,309 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <com/sun/star/text/ChapterFormat.hpp> +#include <osl/diagnose.h> +#include <doc.hxx> +#include <frame.hxx> +#include <rootfrm.hxx> +#include <txtfrm.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <chpfld.hxx> +#include <expfld.hxx> +#include <unofldmid.h> +#include <numrule.hxx> + +using namespace ::com::sun::star; + +namespace +{ + +OUString removeControlChars(std::u16string_view sIn) +{ + OUStringBuffer aBuf(sIn); + aBuf = aBuf.replace('\n', ' ').replace('\t', ' '); + sal_Int32 nLen = aBuf.getLength(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + if (aBuf[i] < ' ') + { + sal_Int32 j = i+1; + while (j<nLen && aBuf[j]<' ') ++j; + aBuf.remove(i, j-i); + nLen = aBuf.getLength(); + } + } + return aBuf.makeStringAndClear(); +} + +} + +SwChapterFieldType::SwChapterFieldType() + : SwFieldType( SwFieldIds::Chapter ) +{ +} + +std::unique_ptr<SwFieldType> SwChapterFieldType::Copy() const +{ + return std::make_unique<SwChapterFieldType>(); +} + +// chapter field + +SwChapterField::SwChapterField(SwChapterFieldType* pTyp, sal_uInt32 nFormat) + : SwField(pTyp, nFormat) +{ +} + +sal_uInt8 SwChapterField::GetLevel(SwRootFrame const*const pLayout) const +{ + State const& rState(pLayout && pLayout->IsHideRedlines() ? m_StateRLHidden : m_State); + return rState.nLevel; +} + +// this is called from UI or from import filters, so override both states +void SwChapterField::SetLevel(sal_uInt8 nLev) +{ + m_State.nLevel = nLev; + m_StateRLHidden.nLevel = nLev; +} + +const OUString& SwChapterField::GetNumber(SwRootFrame const*const pLayout) const +{ + State const& rState(pLayout && pLayout->IsHideRedlines() ? m_StateRLHidden : m_State); + return rState.sNumber; +} + +const OUString& SwChapterField::GetTitle(SwRootFrame const*const pLayout) const +{ + State const& rState(pLayout && pLayout->IsHideRedlines() ? m_StateRLHidden : m_State); + return rState.sTitle; +} + +OUString SwChapterField::ExpandImpl(SwRootFrame const*const pLayout) const +{ + State const& rState(pLayout && pLayout->IsHideRedlines() ? m_StateRLHidden : m_State); + switch( GetFormat() ) + { + case CF_TITLE: + return rState.sTitle; + case CF_NUMBER: + return rState.sPre + rState.sNumber + rState.sPost; + case CF_NUM_TITLE: + return rState.sPre + rState.sNumber + rState.sPost + rState.sLabelFollowedBy + rState.sTitle; + case CF_NUM_NOPREPST_TITLE: + return rState.sNumber + rState.sLabelFollowedBy + rState.sTitle; + } + // CF_NUMBER_NOPREPST + return rState.sNumber; +} + +std::unique_ptr<SwField> SwChapterField::Copy() const +{ + std::unique_ptr<SwChapterField> pTmp( + new SwChapterField(static_cast<SwChapterFieldType*>(GetTyp()), GetFormat())); + pTmp->m_State = m_State; + pTmp->m_StateRLHidden = m_StateRLHidden; + + return std::unique_ptr<SwField>(pTmp.release()); +} + +// #i53420# +void SwChapterField::ChangeExpansion(const SwFrame & rFrame, + const SwContentNode* pContentNode, + bool bSrchNum ) +{ + SwDoc& rDoc = const_cast<SwDoc&>(pContentNode->GetDoc()); + + const SwTextNode* pTextNode = dynamic_cast<const SwTextNode*>(pContentNode); + if (!pTextNode || !rFrame.IsInDocBody()) + { + SwPosition aDummyPos( rDoc.GetNodes().GetEndOfContent() ); + pTextNode = GetBodyTextNode( rDoc, aDummyPos, rFrame ); + } + + if ( pTextNode ) + { + ChangeExpansion( *pTextNode, bSrchNum, rFrame.getRootFrame() ); + } +} + +void SwChapterField::ChangeExpansion(const SwTextNode &rTextNd, bool bSrchNum, + SwRootFrame const*const pLayout) +{ + State & rState(pLayout && pLayout->IsHideRedlines() ? m_StateRLHidden : m_State); + rState.sNumber.clear(); + rState.sLabelFollowedBy.clear(); + rState.sTitle.clear(); + rState.sPost.clear(); + rState.sPre.clear(); + + SwDoc& rDoc = const_cast<SwDoc&>(rTextNd.GetDoc()); + const SwTextNode *pTextNd = rTextNd.FindOutlineNodeOfLevel(rState.nLevel, pLayout); + if( !pTextNd ) + return; + + if( bSrchNum ) + { + const SwTextNode* pONd = pTextNd; + do { + if( pONd && pONd->GetTextColl() ) + { + sal_uInt8 nPrevLvl = rState.nLevel; + + OSL_ENSURE( pONd->GetAttrOutlineLevel() >= 0 && pONd->GetAttrOutlineLevel() <= MAXLEVEL, + "<SwChapterField::ChangeExpansion(..)> - outline node with inconsistent outline level. Serious defect." ); + rState.nLevel = static_cast<sal_uInt8>(pONd->GetAttrOutlineLevel()); + + if (nPrevLvl < rState.nLevel) + rState.nLevel = nPrevLvl; + else if( SVX_NUM_NUMBER_NONE != rDoc.GetOutlineNumRule() + ->Get( rState.nLevel ).GetNumberingType() ) + { + pTextNd = pONd; + break; + } + + if (!rState.nLevel--) + break; + pONd = pTextNd->FindOutlineNodeOfLevel(rState.nLevel, pLayout); + } + else + break; + } while( true ); + } + + // get the number without Pre-/Post-fixstrings + + if ( pTextNd->IsOutline() ) + { + // correction of refactoring done by cws swnumtree: + // retrieve numbering string without prefix and suffix strings + // as stated in the above german comment. + rState.sNumber = pTextNd->GetNumString(false, MAXLEVEL, pLayout); + + SwNumRule* pRule( pTextNd->GetNumRule() ); + if ( pTextNd->IsCountedInList() && pRule ) + { + int nListLevel = pTextNd->GetActualListLevel(); + if (nListLevel < 0) + nListLevel = 0; + if (nListLevel >= MAXLEVEL) + nListLevel = MAXLEVEL - 1; + + const SwNumFormat& rNFormat = pRule->Get(nListLevel); + rState.sPost = rNFormat.GetSuffix(); + rState.sPre = rNFormat.GetPrefix(); + rState.sLabelFollowedBy = removeControlChars(rNFormat.GetLabelFollowedByAsString()); + } + } + else + { + rState.sNumber = "??"; + } + + rState.sTitle = removeControlChars(sw::GetExpandTextMerged(pLayout, + *pTextNd, false, false, ExpandMode(0))); +} + +bool SwChapterField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_BYTE1: + rAny <<= static_cast<sal_Int8>(m_State.nLevel); + break; + + case FIELD_PROP_USHORT1: + { + sal_Int16 nRet; + switch( GetFormat() ) + { + case CF_NUMBER: nRet = text::ChapterFormat::NUMBER; break; + case CF_TITLE: nRet = text::ChapterFormat::NAME; break; + case CF_NUMBER_NOPREPST: + nRet = text::ChapterFormat::DIGIT; + break; + case CF_NUM_NOPREPST_TITLE: + nRet = text::ChapterFormat::NO_PREFIX_SUFFIX; + break; + case CF_NUM_TITLE: + default: nRet = text::ChapterFormat::NAME_NUMBER; + } + rAny <<= nRet; + } + break; + + default: + assert(false); + } + return true; +} + +bool SwChapterField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + bool bRet = true; + switch( nWhichId ) + { + case FIELD_PROP_BYTE1: + { + sal_Int8 nTmp = 0; + rAny >>= nTmp; + if(nTmp >= 0 && nTmp < MAXLEVEL) + { + m_State.nLevel = nTmp; + m_StateRLHidden.nLevel = nTmp; + } + else + bRet = false; + break; + } + + case FIELD_PROP_USHORT1: + { + sal_Int16 nVal = 0; + rAny >>= nVal; + switch( nVal ) + { + case text::ChapterFormat::NAME: SetFormat(CF_TITLE); break; + case text::ChapterFormat::NUMBER: SetFormat(CF_NUMBER); break; + case text::ChapterFormat::NO_PREFIX_SUFFIX: + SetFormat(CF_NUM_NOPREPST_TITLE); + break; + case text::ChapterFormat::DIGIT: + SetFormat(CF_NUMBER_NOPREPST); + break; + + default: SetFormat(CF_NUM_TITLE); + } + } + break; + + default: + assert(false); + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/dbfld.cxx b/sw/source/core/fields/dbfld.cxx new file mode 100644 index 000000000..7263997fe --- /dev/null +++ b/sw/source/core/fields/dbfld.cxx @@ -0,0 +1,869 @@ +/* -*- 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 <float.h> +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <svl/numformat.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <calc.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <docary.hxx> +#include <fldbas.hxx> +#include <dbfld.hxx> +#include <dbmgr.hxx> +#include <unofldmid.h> +#include <o3tl/string_view.hxx> + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star; + +/// replace database separator by dots for display +static OUString lcl_DBSeparatorConvert(const OUString& aContent) +{ + return aContent.replaceAll(OUStringChar(DB_DELIM), "."); +} + +// database field type + +SwDBFieldType::SwDBFieldType(SwDoc* pDocPtr, const OUString& rNam, const SwDBData& rDBData ) : + SwValueFieldType( pDocPtr, SwFieldIds::Database ), + m_aDBData(rDBData), + m_sName(rNam), + m_sColumn(rNam), + m_nRefCnt(0) +{ + if(!m_aDBData.sDataSource.isEmpty() || !m_aDBData.sCommand.isEmpty()) + { + m_sName = m_aDBData.sDataSource + + OUStringChar(DB_DELIM) + + m_aDBData.sCommand + + OUStringChar(DB_DELIM) + + m_sName; + } +} + +SwDBFieldType::~SwDBFieldType() +{ +} + +std::unique_ptr<SwFieldType> SwDBFieldType::Copy() const +{ + return std::make_unique<SwDBFieldType>(GetDoc(), m_sColumn, m_aDBData); +} + +OUString SwDBFieldType::GetName() const +{ + return m_sName; +} + +void SwDBFieldType::ReleaseRef() +{ + OSL_ENSURE(m_nRefCnt > 0, "RefCount < 0!"); + + if (--m_nRefCnt > 0) + return; + + size_t nPos = 0; + for (auto const & pFieldType : *GetDoc()->getIDocumentFieldsAccess().GetFieldTypes()) + { + if (pFieldType.get() == this) + break; + ++nPos; + } + if (nPos < GetDoc()->getIDocumentFieldsAccess().GetFieldTypes()->size()) + { + GetDoc()->getIDocumentFieldsAccess().RemoveFieldType(nPos); + delete this; + } +} + +void SwDBFieldType::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= m_aDBData.sDataSource; + break; + case FIELD_PROP_PAR2: + rAny <<= m_aDBData.sCommand; + break; + case FIELD_PROP_PAR3: + rAny <<= m_sColumn; + break; + case FIELD_PROP_SHORT1: + rAny <<= m_aDBData.nCommandType; + break; + default: + assert(false); + } +} + +void SwDBFieldType::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny >>= m_aDBData.sDataSource; + break; + case FIELD_PROP_PAR2: + rAny >>= m_aDBData.sCommand; + break; + case FIELD_PROP_PAR3: + { + OUString sTmp; + rAny >>= sTmp; + if( sTmp != m_sColumn ) + { + m_sColumn = sTmp; + std::vector<SwFormatField*> vFields; + GatherFields(vFields); + for(auto pFormatField: vFields) + { + SwDBField* pDBField = static_cast<SwDBField*>(pFormatField->GetField()); + pDBField->ClearInitialized(); + pDBField->InitContent(); + } + } + } + break; + case FIELD_PROP_SHORT1: + rAny >>= m_aDBData.nCommandType; + break; + default: + assert(false); + } +} + +// database field + +SwDBField::SwDBField(SwDBFieldType* pTyp, sal_uInt32 nFormat) + : SwValueField(pTyp, nFormat), + m_nSubType(0), + m_bIsInBodyText(true), + m_bValidValue(false), + m_bInitialized(false) +{ + if (GetTyp()) + static_cast<SwDBFieldType*>(GetTyp())->AddRef(); + InitContent(); +} + +SwDBField::~SwDBField() +{ + if (GetTyp()) + static_cast<SwDBFieldType*>(GetTyp())->ReleaseRef(); +} + +void SwDBField::InitContent() +{ + if (!IsInitialized()) + { + m_aContent = "<" + static_cast<const SwDBFieldType*>(GetTyp())->GetColumnName() + ">"; + } +} + +void SwDBField::InitContent(const OUString& rExpansion) +{ + if (rExpansion.startsWith("<") && rExpansion.endsWith(">")) + { + const OUString sColumn( rExpansion.copy( 1, rExpansion.getLength() - 2 ) ); + if( ::GetAppCmpStrIgnore().isEqual( sColumn, + static_cast<SwDBFieldType *>(GetTyp())->GetColumnName() )) + { + InitContent(); + return; + } + } + SetExpansion( rExpansion ); +} + +OUString SwDBField::ExpandImpl(SwRootFrame const*const) const +{ + if(0 ==(GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE)) + return lcl_DBSeparatorConvert(m_aContent); + return OUString(); +} + +std::unique_ptr<SwField> SwDBField::Copy() const +{ + std::unique_ptr<SwDBField> pTmp(new SwDBField(static_cast<SwDBFieldType*>(GetTyp()), GetFormat())); + pTmp->m_aContent = m_aContent; + pTmp->m_bIsInBodyText = m_bIsInBodyText; + pTmp->m_bValidValue = m_bValidValue; + pTmp->m_bInitialized = m_bInitialized; + pTmp->m_nSubType = m_nSubType; + pTmp->SetValue(GetValue()); + pTmp->m_sFieldCode = m_sFieldCode; + + return std::unique_ptr<SwField>(pTmp.release()); +} + +OUString SwDBField::GetFieldName() const +{ + const OUString rDBName = static_cast<SwDBFieldType*>(GetTyp())->GetName(); + + OUString sContent( rDBName.getToken(0, DB_DELIM) ); + + if (sContent.getLength() > 1) + { + sContent += OUStringChar(DB_DELIM) + + o3tl::getToken(rDBName, 1, DB_DELIM) + + OUStringChar(DB_DELIM) + + o3tl::getToken(rDBName, 2, DB_DELIM); + } + return lcl_DBSeparatorConvert(sContent); +} + +void SwDBField::ChgValue( double d, bool bVal ) +{ + m_bValidValue = bVal; + SetValue(d); + + if( m_bValidValue ) + m_aContent = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue(d, GetFormat(), GetLanguage()); +} + +SwFieldType* SwDBField::ChgTyp( SwFieldType* pNewType ) +{ + SwFieldType* pOld = SwValueField::ChgTyp( pNewType ); + + static_cast<SwDBFieldType*>(pNewType)->AddRef(); + static_cast<SwDBFieldType*>(pOld)->ReleaseRef(); + + return pOld; +} + +bool SwDBField::FormatValue( SvNumberFormatter const * pDocFormatter, OUString const &aString, sal_uInt32 nFormat, + double &aNumber, sal_Int32 nColumnType, SwDBField *pField ) +{ + bool bValidValue = false; + + if( DBL_MAX != aNumber ) + { + if( DataType::DATE == nColumnType || DataType::TIME == nColumnType || + DataType::TIMESTAMP == nColumnType ) + { + Date aStandard( 1, 1, 1900 ); + if( pDocFormatter->GetNullDate() != aStandard ) + aNumber += (aStandard - pDocFormatter->GetNullDate()); + } + bValidValue = true; + if( pField ) + pField->SetValue( aNumber ); + } + else + { + SwSbxValue aVal; + aVal.PutString( aString ); + + if (aVal.IsNumeric()) + { + if( pField ) + pField->SetValue(aVal.GetDouble()); + else + aNumber = aVal.GetDouble(); + + if (nFormat && nFormat != SAL_MAX_UINT32 && !pDocFormatter->IsTextFormat(nFormat)) + bValidValue = true; // because of bug #60339 not for all strings + } + else + { + // if string length > 0 then true, else false + if( pField ) + pField->SetValue(aString.isEmpty() ? 0 : 1); + else + aNumber = aString.isEmpty() ? 0 : 1; + } + } + + return bValidValue; +} + +/// get current field value and cache it +void SwDBField::Evaluate() +{ + SwDBManager* pMgr = GetDoc()->GetDBManager(); + + // first delete + m_bValidValue = false; + double nValue = DBL_MAX; + const SwDBData& aTmpData = GetDBData(); + + if(!pMgr || !pMgr->IsDataSourceOpen(aTmpData.sDataSource, aTmpData.sCommand, true)) + return ; + + sal_uInt32 nFormat = 0; + + // search corresponding column name + OUString aColNm( static_cast<SwDBFieldType*>(GetTyp())->GetColumnName() ); + + SvNumberFormatter* pDocFormatter = GetDoc()->GetNumberFormatter(); + pMgr->GetMergeColumnCnt(aColNm, GetLanguage(), m_aContent, &nValue); + if( !( m_nSubType & nsSwExtendedSubType::SUB_OWN_FMT ) ) + { + nFormat = pMgr->GetColumnFormat( aTmpData.sDataSource, aTmpData.sCommand, + aColNm, pDocFormatter, GetLanguage() ); + SetFormat( nFormat ); + } + + sal_Int32 nColumnType = nValue == DBL_MAX + ? 0 + : pMgr->GetColumnType(aTmpData.sDataSource, aTmpData.sCommand, aColNm); + + m_bValidValue = FormatValue( pDocFormatter, m_aContent, nFormat, nValue, nColumnType, this ); + + if( DBL_MAX != nValue ) + m_aContent = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue(nValue, GetFormat(), GetLanguage()); + + m_bInitialized = true; +} + +/// get name +OUString SwDBField::GetPar1() const +{ + return static_cast<const SwDBFieldType*>(GetTyp())->GetName(); +} + +sal_uInt16 SwDBField::GetSubType() const +{ + return m_nSubType; +} + +void SwDBField::SetSubType(sal_uInt16 nType) +{ + m_nSubType = nType; +} + +bool SwDBField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL1: + rAny <<= 0 == (GetSubType()&nsSwExtendedSubType::SUB_OWN_FMT); + break; + case FIELD_PROP_BOOL2: + rAny <<= 0 == (GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE); + break; + case FIELD_PROP_FORMAT: + rAny <<= static_cast<sal_Int32>(GetFormat()); + break; + case FIELD_PROP_PAR1: + rAny <<= m_aContent; + break; + case FIELD_PROP_PAR2: + rAny <<= m_sFieldCode; + break; + default: + OSL_FAIL("illegal property"); + } + return true; +} + +bool SwDBField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL1: + if( *o3tl::doAccess<bool>(rAny) ) + SetSubType(GetSubType()&~nsSwExtendedSubType::SUB_OWN_FMT); + else + SetSubType(GetSubType()|nsSwExtendedSubType::SUB_OWN_FMT); + break; + case FIELD_PROP_BOOL2: + { + sal_uInt16 nSubTyp = GetSubType(); + bool bVisible = false; + if(!(rAny >>= bVisible)) + return false; + if(bVisible) + nSubTyp &= ~nsSwExtendedSubType::SUB_INVISIBLE; + else + nSubTyp |= nsSwExtendedSubType::SUB_INVISIBLE; + SetSubType(nSubTyp); + //invalidate text node + auto pType = GetTyp(); + if(!pType) + break; + std::vector<SwFormatField*> vFields; + pType->GatherFields(vFields, false); + for(auto pFormatField: vFields) + { + SwTextField* pTextField = pFormatField->GetTextField(); + if(pTextField && static_cast<SwDBField*>(pFormatField->GetField()) == this) + { + //notify the change + pTextField->NotifyContentChange(*pFormatField); + break; + } + } + } + break; + case FIELD_PROP_FORMAT: + { + sal_Int32 nTemp = 0; + rAny >>= nTemp; + SetFormat(nTemp); + } + break; + case FIELD_PROP_PAR1: + rAny >>= m_aContent; + break; + case FIELD_PROP_PAR2: + rAny >>= m_sFieldCode; + break; + default: + OSL_FAIL("illegal property"); + } + return true; +} + +// base class for all further database fields + +SwDBNameInfField::SwDBNameInfField(SwFieldType* pTyp, const SwDBData& rDBData, sal_uInt32 nFormat) : + SwField(pTyp, nFormat), + m_aDBData(rDBData), + m_nSubType(0) +{ +} + +SwDBData SwDBNameInfField::GetDBData(SwDoc* pDoc) +{ + SwDBData aRet; + if(!m_aDBData.sDataSource.isEmpty()) + aRet = m_aDBData; + else + aRet = pDoc->GetDBData(); + return aRet; +} + +void SwDBNameInfField::SetDBData(const SwDBData & rDBData) +{ + m_aDBData = rDBData; +} + +OUString SwDBNameInfField::GetFieldName() const +{ + OUString sStr( SwField::GetFieldName() ); + if (!m_aDBData.sDataSource.isEmpty()) + { + sStr += ":" + + m_aDBData.sDataSource + + OUStringChar(DB_DELIM) + + m_aDBData.sCommand; + } + return lcl_DBSeparatorConvert(sStr); +} + +bool SwDBNameInfField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= m_aDBData.sDataSource; + break; + case FIELD_PROP_PAR2: + rAny <<= m_aDBData.sCommand; + break; + case FIELD_PROP_SHORT1: + rAny <<= m_aDBData.nCommandType; + break; + case FIELD_PROP_BOOL2: + rAny <<= 0 == (GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE); + break; + default: + assert(false); + } + return true; +} + +bool SwDBNameInfField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny >>= m_aDBData.sDataSource; + break; + case FIELD_PROP_PAR2: + rAny >>= m_aDBData.sCommand; + break; + case FIELD_PROP_SHORT1: + rAny >>= m_aDBData.nCommandType; + break; + case FIELD_PROP_BOOL2: + { + sal_uInt16 nSubTyp = GetSubType(); + bool bVisible = false; + if(!(rAny >>= bVisible)) + return false; + if(bVisible) + nSubTyp &= ~nsSwExtendedSubType::SUB_INVISIBLE; + else + nSubTyp |= nsSwExtendedSubType::SUB_INVISIBLE; + SetSubType(nSubTyp); + } + break; + default: + assert(false); + } + return true; +} + +sal_uInt16 SwDBNameInfField::GetSubType() const +{ + return m_nSubType; +} + +void SwDBNameInfField::SetSubType(sal_uInt16 nType) +{ + m_nSubType = nType; +} + +// next dataset + +SwDBNextSetFieldType::SwDBNextSetFieldType() + : SwFieldType( SwFieldIds::DbNextSet ) +{ +} + +std::unique_ptr<SwFieldType> SwDBNextSetFieldType::Copy() const +{ + return std::make_unique<SwDBNextSetFieldType>(); +} + +// SwDBSetField + +SwDBNextSetField::SwDBNextSetField(SwDBNextSetFieldType* pTyp, + const OUString& rCond, + const SwDBData& rDBData) : + SwDBNameInfField(pTyp, rDBData), m_aCond(rCond), m_bCondValid(true) +{} + +OUString SwDBNextSetField::ExpandImpl(SwRootFrame const*const) const +{ + return OUString(); +} + +std::unique_ptr<SwField> SwDBNextSetField::Copy() const +{ + std::unique_ptr<SwDBNextSetField> pTmp(new SwDBNextSetField(static_cast<SwDBNextSetFieldType*>(GetTyp()), + m_aCond, GetDBData())); + pTmp->SetSubType(GetSubType()); + pTmp->m_bCondValid = m_bCondValid; + return std::unique_ptr<SwField>(pTmp.release()); +} + +void SwDBNextSetField::Evaluate(const SwDoc& rDoc) +{ + SwDBManager* pMgr = rDoc.GetDBManager(); + const SwDBData& rData = GetDBData(); + if( !m_bCondValid || + !pMgr || !pMgr->IsDataSourceOpen(rData.sDataSource, rData.sCommand, false)) + return ; + pMgr->ToNextRecord(rData.sDataSource, rData.sCommand); +} + +/// get condition +OUString SwDBNextSetField::GetPar1() const +{ + return m_aCond; +} + +/// set condition +void SwDBNextSetField::SetPar1(const OUString& rStr) +{ + m_aCond = rStr; +} + +bool SwDBNextSetField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + bool bRet = true; + switch( nWhichId ) + { + case FIELD_PROP_PAR3: + rAny <<= m_aCond; + break; + default: + bRet = SwDBNameInfField::QueryValue( rAny, nWhichId ); + } + return bRet; +} + +bool SwDBNextSetField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + bool bRet = true; + switch( nWhichId ) + { + case FIELD_PROP_PAR3: + rAny >>= m_aCond; + break; + default: + bRet = SwDBNameInfField::PutValue( rAny, nWhichId ); + } + return bRet; +} + +// dataset with certain ID + +SwDBNumSetFieldType::SwDBNumSetFieldType() : + SwFieldType( SwFieldIds::DbNumSet ) +{ +} + +std::unique_ptr<SwFieldType> SwDBNumSetFieldType::Copy() const +{ + return std::make_unique<SwDBNumSetFieldType>(); +} + +SwDBNumSetField::SwDBNumSetField(SwDBNumSetFieldType* pTyp, + const OUString& rCond, + const OUString& rDBNum, + const SwDBData& rDBData) : + SwDBNameInfField(pTyp, rDBData), + m_aCond(rCond), + m_aPar2(rDBNum), + m_bCondValid(true) +{} + +OUString SwDBNumSetField::ExpandImpl(SwRootFrame const*const) const +{ + return OUString(); +} + +std::unique_ptr<SwField> SwDBNumSetField::Copy() const +{ + std::unique_ptr<SwDBNumSetField> pTmp(new SwDBNumSetField(static_cast<SwDBNumSetFieldType*>(GetTyp()), + m_aCond, m_aPar2, GetDBData())); + pTmp->m_bCondValid = m_bCondValid; + pTmp->SetSubType(GetSubType()); + return std::unique_ptr<SwField>(pTmp.release()); +} + +void SwDBNumSetField::Evaluate(const SwDoc& rDoc) +{ + SwDBManager* pMgr = rDoc.GetDBManager(); + const SwDBData& aTmpData = GetDBData(); + + if( m_bCondValid && pMgr && pMgr->IsInMerge() && + pMgr->IsDataSourceOpen(aTmpData.sDataSource, aTmpData.sCommand, true)) + { // condition OK -> adjust current Set + pMgr->ToRecordId(std::max(o3tl::narrowing<sal_uInt16>(m_aPar2.toInt32()), sal_uInt16(1))-1); + } +} + +/// get LogDBName +OUString SwDBNumSetField::GetPar1() const +{ + return m_aCond; +} + +/// set LogDBName +void SwDBNumSetField::SetPar1(const OUString& rStr) +{ + m_aCond = rStr; +} + +/// get condition +OUString SwDBNumSetField::GetPar2() const +{ + return m_aPar2; +} + +/// set condition +void SwDBNumSetField::SetPar2(const OUString& rStr) +{ + m_aPar2 = rStr; +} + +bool SwDBNumSetField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + bool bRet = true; + switch( nWhichId ) + { + case FIELD_PROP_PAR3: + rAny <<= m_aCond; + break; + case FIELD_PROP_FORMAT: + rAny <<= m_aPar2.toInt32(); + break; + default: + bRet = SwDBNameInfField::QueryValue(rAny, nWhichId ); + } + return bRet; +} + +bool SwDBNumSetField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + bool bRet = true; + switch( nWhichId ) + { + case FIELD_PROP_PAR3: + rAny >>= m_aCond; + break; + case FIELD_PROP_FORMAT: + { + sal_Int32 nVal = 0; + rAny >>= nVal; + m_aPar2 = OUString::number(nVal); + } + break; + default: + bRet = SwDBNameInfField::PutValue(rAny, nWhichId ); + } + return bRet; +} + +SwDBNameFieldType::SwDBNameFieldType(SwDoc* pDocument) + : SwFieldType( SwFieldIds::DatabaseName ) +{ + m_pDoc = pDocument; +} + +OUString SwDBNameFieldType::Expand() const +{ + const SwDBData aData = m_pDoc->GetDBData(); + return aData.sDataSource + "." + aData.sCommand; +} + +std::unique_ptr<SwFieldType> SwDBNameFieldType::Copy() const +{ + return std::make_unique<SwDBNameFieldType>(m_pDoc); +} + +// name of the connected database + +SwDBNameField::SwDBNameField(SwDBNameFieldType* pTyp, const SwDBData& rDBData) + : SwDBNameInfField(pTyp, rDBData, 0) +{} + +OUString SwDBNameField::ExpandImpl(SwRootFrame const*const) const +{ + if(0 ==(GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE)) + return static_cast<SwDBNameFieldType*>(GetTyp())->Expand(); + return OUString(); +} + +std::unique_ptr<SwField> SwDBNameField::Copy() const +{ + std::unique_ptr<SwDBNameField> pTmp(new SwDBNameField(static_cast<SwDBNameFieldType*>(GetTyp()), GetDBData())); + pTmp->ChangeFormat(GetFormat()); + pTmp->SetLanguage(GetLanguage()); + pTmp->SetSubType(GetSubType()); + return std::unique_ptr<SwField>(pTmp.release()); +} + +bool SwDBNameField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + return SwDBNameInfField::QueryValue(rAny, nWhichId ); +} + +bool SwDBNameField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + return SwDBNameInfField::PutValue(rAny, nWhichId ); +} + +SwDBSetNumberFieldType::SwDBSetNumberFieldType() + : SwFieldType( SwFieldIds::DbSetNumber ) +{ +} + +std::unique_ptr<SwFieldType> SwDBSetNumberFieldType::Copy() const +{ + return std::make_unique<SwDBSetNumberFieldType>(); +} + +// set-number of the connected database + +SwDBSetNumberField::SwDBSetNumberField(SwDBSetNumberFieldType* pTyp, + const SwDBData& rDBData, + sal_uInt32 nFormat) + : SwDBNameInfField(pTyp, rDBData, nFormat), m_nNumber(0) +{} + +OUString SwDBSetNumberField::ExpandImpl(SwRootFrame const*const) const +{ + if(0 !=(GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE) || m_nNumber == 0) + return OUString(); + return FormatNumber(m_nNumber, static_cast<SvxNumType>(GetFormat())); +} + +void SwDBSetNumberField::Evaluate(const SwDoc& rDoc) +{ + SwDBManager* pMgr = rDoc.GetDBManager(); + + const SwDBData& aTmpData = GetDBData(); + if (!pMgr || !pMgr->IsInMerge() || + !pMgr->IsDataSourceOpen(aTmpData.sDataSource, aTmpData.sCommand, false)) + return; + m_nNumber = pMgr->GetSelectedRecordId(); +} + +std::unique_ptr<SwField> SwDBSetNumberField::Copy() const +{ + std::unique_ptr<SwDBSetNumberField> pTmp( + new SwDBSetNumberField(static_cast<SwDBSetNumberFieldType*>(GetTyp()), GetDBData(), GetFormat())); + pTmp->SetLanguage(GetLanguage()); + pTmp->SetSetNumber(m_nNumber); + pTmp->SetSubType(GetSubType()); + return std::unique_ptr<SwField>(pTmp.release()); +} + +bool SwDBSetNumberField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + bool bRet = true; + switch( nWhichId ) + { + case FIELD_PROP_USHORT1: + rAny <<= static_cast<sal_Int16>(GetFormat()); + break; + case FIELD_PROP_FORMAT: + rAny <<= m_nNumber; + break; + default: + bRet = SwDBNameInfField::QueryValue( rAny, nWhichId ); + } + return bRet; +} + +bool SwDBSetNumberField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + bool bRet = true; + switch( nWhichId ) + { + case FIELD_PROP_USHORT1: + { + sal_Int16 nSet = 0; + rAny >>= nSet; + if(nSet < css::style::NumberingType::NUMBER_NONE ) + SetFormat(nSet); + } + break; + case FIELD_PROP_FORMAT: + rAny >>= m_nNumber; + break; + default: + bRet = SwDBNameInfField::PutValue( rAny, nWhichId ); + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/ddefld.cxx b/sw/source/core/fields/ddefld.cxx new file mode 100644 index 000000000..bbb567aaf --- /dev/null +++ b/sw/source/core/fields/ddefld.cxx @@ -0,0 +1,389 @@ +/* -*- 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 <sal/config.h> + +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <osl/thread.h> +#include <rtl/ustrbuf.hxx> +#include <sfx2/linkmgr.hxx> +#include <sot/exchange.hxx> +#include <doc.hxx> +#include <IDocumentLinksAdministration.hxx> +#include <IDocumentState.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <editsh.hxx> +#include <fmtfld.hxx> +#include <ddefld.hxx> +#include <swddetbl.hxx> +#include <swbaslnk.hxx> +#include <unofldmid.h> +#include <hints.hxx> + +using namespace ::com::sun::star; + +namespace { + +class SwIntrnlRefLink : public SwBaseLink +{ + SwDDEFieldType& m_rFieldType; + +public: + SwIntrnlRefLink(SwDDEFieldType& rType, SfxLinkUpdateMode nUpdateType) + : SwBaseLink(nUpdateType, SotClipboardFormatId::STRING) + , m_rFieldType(rType) + {} + + virtual void Closed() override; + virtual ::sfx2::SvBaseLink::UpdateResult DataChanged( + const OUString& rMimeType, const css::uno::Any & rValue ) override; + + virtual const SwNode* GetAnchor() const override; + virtual bool IsInRange( SwNodeOffset nSttNd, SwNodeOffset nEndNd ) const override; +}; + +} + +::sfx2::SvBaseLink::UpdateResult SwIntrnlRefLink::DataChanged( const OUString& rMimeType, + const uno::Any & rValue ) +{ + switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) ) + { + case SotClipboardFormatId::STRING: + if( !IsNoDataFlag() ) + { + OUString sStr; + if (!(rValue >>= sStr)) + { + uno::Sequence< sal_Int8 > aSeq; + rValue >>= aSeq; + sStr = OUString(reinterpret_cast<char const*>(aSeq.getConstArray()), aSeq.getLength(), osl_getThreadTextEncoding()); + } + + // remove not needed CR-LF at the end + sal_Int32 n = sStr.getLength(); + while( n && 0 == sStr[ n-1 ] ) + --n; + if( n && 0x0a == sStr[ n-1 ] ) + --n; + if( n && 0x0d == sStr[ n-1 ] ) + --n; + + bool bDel = n != sStr.getLength(); + if( bDel ) + sStr = sStr.copy( 0, n ); + + m_rFieldType.SetExpansion(sStr); + // set Expansion first! (otherwise this flag will be deleted) + m_rFieldType.SetCRLFDelFlag(bDel); + } + break; + + // other formats + default: + return SUCCESS; + } + + if(!ChkNoDataFlag()) + m_rFieldType.UpdateDDE(); + + return SUCCESS; +} + +void SwIntrnlRefLink::Closed() +{ + if (m_rFieldType.GetDoc() && !m_rFieldType.GetDoc()->IsInDtor()) + { + // advise goes, convert all fields into text? + SwViewShell* pSh = m_rFieldType.GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + if (SwEditShell* pESh = m_rFieldType.GetDoc()->GetEditShell()) + { + pESh->StartAllAction(); + pESh->FieldToText(&m_rFieldType); + pESh->EndAllAction(); + } + else + { + pSh->StartAction(); + // to call at the doc ?? + pSh->EndAction(); + } + } + SvBaseLink::Closed(); +} + +sw::LinkAnchorSearchHint::~LinkAnchorSearchHint() {}; + +const SwNode* SwIntrnlRefLink::GetAnchor() const +{ + // here, any anchor of the normal NodesArray should be sufficient + const SwNode* pNd = nullptr; + m_rFieldType.CallSwClientNotify( + sw::LinkAnchorSearchHint(m_rFieldType.GetDoc()->GetNodes(), pNd)); + return pNd; +} + +bool SwIntrnlRefLink::IsInRange( SwNodeOffset nSttNd, SwNodeOffset nEndNd ) const +{ + bool bInRange = false; + m_rFieldType.CallSwClientNotify(sw::InRangeSearchHint(nSttNd, nEndNd, bInRange)); + return bInRange; +} + +SwDDEFieldType::SwDDEFieldType(const OUString& rName, + const OUString& rCmd, SfxLinkUpdateMode nUpdateType ) + : SwFieldType( SwFieldIds::Dde ), + m_aName( rName ), m_pDoc( nullptr ), m_nRefCount( 0 ) +{ + m_bCRLFFlag = m_bDeleted = false; + m_RefLink = new SwIntrnlRefLink( *this, nUpdateType ); + SetCmd( rCmd ); +} + +SwDDEFieldType::~SwDDEFieldType() +{ + if( m_pDoc && !m_pDoc->IsInDtor() ) + m_pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() ); + m_RefLink->Disconnect(); +} + +std::unique_ptr<SwFieldType> SwDDEFieldType::Copy() const +{ + std::unique_ptr<SwDDEFieldType> pType(new SwDDEFieldType( m_aName, GetCmd(), GetType() )); + pType->m_aExpansion = m_aExpansion; + pType->m_bCRLFFlag = m_bCRLFFlag; + pType->m_bDeleted = m_bDeleted; + pType->SetDoc( m_pDoc ); + return pType; +} + +OUString SwDDEFieldType::GetName() const +{ + return m_aName; +} + +void SwDDEFieldType::SetCmd( const OUString& _aStr ) +{ + OUString aStr = _aStr; + sal_Int32 nIndex = 0; + do + { + aStr = aStr.replaceFirst(" ", " ", &nIndex); + } while (nIndex>=0); + m_RefLink->SetLinkSourceName( aStr ); +} + +OUString const & SwDDEFieldType::GetCmd() const +{ + return m_RefLink->GetLinkSourceName(); +} + +void SwDDEFieldType::SetDoc( SwDoc* pNewDoc ) +{ + if( pNewDoc == m_pDoc ) + return; + + if( m_pDoc && m_RefLink.is() ) + { + OSL_ENSURE( !m_nRefCount, "How do we get the references?" ); + m_pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() ); + } + + m_pDoc = pNewDoc; + if( m_pDoc && m_nRefCount ) + { + m_RefLink->SetVisible( m_pDoc->getIDocumentLinksAdministration().IsVisibleLinks() ); + m_pDoc->getIDocumentLinksAdministration().GetLinkManager().InsertDDELink( m_RefLink.get() ); + } +} + +void SwDDEFieldType::RefCntChgd() +{ + if( m_nRefCount ) + { + m_RefLink->SetVisible( m_pDoc->getIDocumentLinksAdministration().IsVisibleLinks() ); + m_pDoc->getIDocumentLinksAdministration().GetLinkManager().InsertDDELink( m_RefLink.get() ); + if( m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) + m_RefLink->Update(); + } + else + { + Disconnect(); + m_pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() ); + } +} + +void SwDDEFieldType::QueryValue( uno::Any& rVal, sal_uInt16 nWhichId ) const +{ + sal_Int32 nPart = -1; + switch( nWhichId ) + { + case FIELD_PROP_PAR2: nPart = 2; break; + case FIELD_PROP_PAR4: nPart = 1; break; + case FIELD_PROP_SUBTYPE: nPart = 0; break; + case FIELD_PROP_BOOL1: + rVal <<= GetType() == SfxLinkUpdateMode::ALWAYS; + break; + case FIELD_PROP_PAR5: + rVal <<= m_aExpansion; + break; + default: + assert(false); + } + if ( nPart>=0 ) + rVal <<= GetCmd().getToken(nPart, sfx2::cTokenSeparator); +} + +void SwDDEFieldType::PutValue( const uno::Any& rVal, sal_uInt16 nWhichId ) +{ + sal_Int32 nPart = -1; + switch( nWhichId ) + { + case FIELD_PROP_PAR2: nPart = 2; break; + case FIELD_PROP_PAR4: nPart = 1; break; + case FIELD_PROP_SUBTYPE: nPart = 0; break; + case FIELD_PROP_BOOL1: + SetType( *o3tl::doAccess<bool>(rVal) ? + SfxLinkUpdateMode::ALWAYS : + SfxLinkUpdateMode::ONCALL ); + break; + case FIELD_PROP_PAR5: + rVal >>= m_aExpansion; + break; + default: + assert(false); + } + if( nPart<0 ) + return; + + const OUString sOldCmd( GetCmd() ); + OUStringBuffer sNewCmd; + sal_Int32 nIndex = 0; + for (sal_Int32 i=0; i<3; ++i) + { + OUString sToken = sOldCmd.getToken(0, sfx2::cTokenSeparator, nIndex); + if (i==nPart) + { + rVal >>= sToken; + } + sNewCmd.append((i < 2) + ? sToken + OUStringChar(sfx2::cTokenSeparator) : sToken); + } + SetCmd( sNewCmd.makeStringAndClear() ); +} + +void SwDDEFieldType::UpdateDDE(const bool bNotifyShells) +{ + auto pDoc = GetDoc(); + assert(pDoc); + if(IsModifyLocked()) + return; + SwViewShell* pSh = bNotifyShells ? pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() : nullptr; + SwEditShell* pESh = bNotifyShells ? pDoc->GetEditShell() : nullptr; + + // Search for fields. If no valid found, disconnect. + LockModify(); + + std::vector<SwFormatField*> vFields; + std::vector<SwDDETable*> vTables; + GatherFields(vFields, false); + GatherDdeTables(vTables); + const bool bDoAction = vFields.size() || vTables.size(); + if(bDoAction) + { + if(pESh) + pESh->StartAllAction(); + else if(pSh) + pSh->StartAction(); + } + + // DDE fields attribute in the text + SwMsgPoolItem aUpdateDDE(RES_UPDATEDDETBL); + for(auto pFormatField: vFields) + { + if(pFormatField->GetTextField()) + pFormatField->UpdateTextNode( nullptr, &aUpdateDDE ); + } + // a DDE tables in the text + for(auto pTable: vTables) + pTable->ChangeContent(); + + UnlockModify(); + + if(bDoAction) + { + if(pESh) + pESh->EndAllAction(); + else if(pSh) + pSh->EndAction(); + + if(pSh) + pSh->GetDoc()->getIDocumentState().SetModified(); + } +} + +SwDDEField::SwDDEField( SwDDEFieldType* pInitType ) + : SwField(pInitType) +{ +} + +SwDDEField::~SwDDEField() +{ + if( GetTyp()->HasOnlyOneListener() ) + static_cast<SwDDEFieldType*>(GetTyp())->Disconnect(); +} + +OUString SwDDEField::ExpandImpl(SwRootFrame const*const) const +{ + OUString aStr = static_cast<SwDDEFieldType*>(GetTyp())->GetExpansion(); + aStr = aStr.replaceAll("\r", ""); + aStr = aStr.replaceAll("\t", " "); + aStr = aStr.replaceAll("\n", "|"); + if (aStr.endsWith("|")) + { + return aStr.copy(0, aStr.getLength()-1); + } + return aStr; +} + +std::unique_ptr<SwField> SwDDEField::Copy() const +{ + return std::make_unique<SwDDEField>(static_cast<SwDDEFieldType*>(GetTyp())); +} + +/// get field type name +OUString SwDDEField::GetPar1() const +{ + return static_cast<const SwDDEFieldType*>(GetTyp())->GetName(); +} + +/// get field type command +OUString SwDDEField::GetPar2() const +{ + return static_cast<const SwDDEFieldType*>(GetTyp())->GetCmd(); +} + +/// set field type command +void SwDDEField::SetPar2(const OUString& rStr) +{ + static_cast<SwDDEFieldType*>(GetTyp())->SetCmd(rStr); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/ddetbl.cxx b/sw/source/core/fields/ddetbl.cxx new file mode 100644 index 000000000..b1576e1a1 --- /dev/null +++ b/sw/source/core/fields/ddetbl.cxx @@ -0,0 +1,216 @@ +/* -*- 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 <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <index.hxx> +#include <ndtxt.hxx> +#include <swtable.hxx> +#include <swddetbl.hxx> +#include <fmtfld.hxx> +#include <ddefld.hxx> +#include <ndindex.hxx> +#include <fldupde.hxx> +#include <swtblfmt.hxx> +#include <fieldhint.hxx> +#include <osl/diagnose.h> +#include <pam.hxx> + +/// Ctor moves all lines/boxes from a SwTable into itself. +/// Afterwards the SwTable is empty and must be deleted. +SwDDETable::SwDDETable( SwTable& rTable, SwDDEFieldType* pDDEType, bool bUpdate ) + : SwTable(rTable), m_aDepends(*this), m_pDDEType(pDDEType) +{ + m_aDepends.StartListening(m_pDDEType); + // copy the table data + m_TabSortContentBoxes.insert(rTable.GetTabSortBoxes()); + rTable.GetTabSortBoxes().clear(); + + m_aLines.insert( m_aLines.begin(), + rTable.GetTabLines().begin(), rTable.GetTabLines().end() ); // move lines + rTable.GetTabLines().clear(); + + if( !m_aLines.empty() ) + { + const SwNode& rNd = *GetTabSortBoxes()[0]->GetSttNd(); + if( rNd.GetNodes().IsDocNodes() ) + { + pDDEType->IncRefCnt(); + + // update box content only if update flag is set (false in import) + if (bUpdate) + ChangeContent(); + } + } +} + +SwDDETable::~SwDDETable() +{ + SwDoc* pDoc = GetFrameFormat()->GetDoc(); + if (!pDoc->IsInDtor() && !m_aLines.empty()) + { + assert(m_pTableNode); + if (m_pTableNode->GetNodes().IsDocNodes()) + { + m_pDDEType->DecRefCnt(); + } + } + + // If it is the last dependent of the "deleted field" than delete it finally + if( m_pDDEType->IsDeleted() && m_pDDEType->HasOnlyOneListener() ) + { + m_aDepends.EndListeningAll(); + delete m_pDDEType; + m_pDDEType = nullptr; + } +} + +void SwDDETable::SwClientNotify(const SwModify& rModify, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::SwLegacyModify) + { + auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint); + switch(pLegacy->GetWhich()) + { + case RES_UPDATEDDETBL: + ChangeContent(); + break; + default: + SwTable::SwClientNotify(rModify, rHint); + } + } + else if(auto pFieldHint = dynamic_cast<const SwFieldHint*>(&rHint)) + { + pFieldHint->m_pPaM->DeleteMark(); // TODO: this is really hackish + // replace DDETable by real table + NoDDETable(); + } + else if(const auto pLinkAnchorHint = dynamic_cast<const sw::LinkAnchorSearchHint*>(&rHint)) + { + if(pLinkAnchorHint->m_rpFoundNode) + return; + const auto pNd = GetTabSortBoxes()[0]->GetSttNd(); + if( pNd && &pLinkAnchorHint->m_rNodes == &pNd->GetNodes() ) + pLinkAnchorHint->m_rpFoundNode = pNd; + } + else if(const sw::InRangeSearchHint* pInRangeHint = dynamic_cast<const sw::InRangeSearchHint*>(&rHint)) + { + if(pInRangeHint->m_rIsInRange) + return; + const SwTableNode* pTableNd = GetTabSortBoxes()[0]->GetSttNd()->FindTableNode(); + if( pTableNd->GetNodes().IsDocNodes() && + pInRangeHint->m_nSttNd < pTableNd->EndOfSectionIndex() && + pInRangeHint->m_nEndNd > pTableNd->GetIndex() ) + pInRangeHint->m_rIsInRange = true; + } else if (const auto pGatherDdeTablesHint = dynamic_cast<const sw::GatherDdeTablesHint*>(&rHint)) + { + pGatherDdeTablesHint->m_rvTables.push_back(this); + } + else if (auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint)) + { + if(m_pDDEType == &rModify) + m_pDDEType = const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pModifyChangedHint->m_pNew)); + } +} + +void SwDDETable::ChangeContent() +{ + OSL_ENSURE( GetFrameFormat(), "No FrameFormat" ); + + // Is this the correct NodesArray? (because of UNDO) + if( m_aLines.empty() ) + return; + OSL_ENSURE( !GetTabSortBoxes().empty(), "Table without content?" ); + if( !GetTabSortBoxes()[0]->GetSttNd()->GetNodes().IsDocNodes() ) + return; + + + OUString aExpand = m_pDDEType->GetExpansion().replaceAll("\r", ""); + sal_Int32 nExpandTokenPos = 0; + + for( size_t n = 0; n < m_aLines.size(); ++n ) + { + OUString aLine = aExpand.getToken( 0, '\n', nExpandTokenPos ); + sal_Int32 nLineTokenPos = 0; + SwTableLine* pLine = m_aLines[ n ]; + for( size_t i = 0; i < pLine->GetTabBoxes().size(); ++i ) + { + SwTableBox* pBox = pLine->GetTabBoxes()[ i ]; + OSL_ENSURE( pBox->GetSttIdx(), "no content box" ); + SwNodeIndex aNdIdx( *pBox->GetSttNd(), 1 ); + SwTextNode* pTextNode = aNdIdx.GetNode().GetTextNode(); + OSL_ENSURE( pTextNode, "No Node" ); + SwIndex aCntIdx( pTextNode, 0 ); + pTextNode->EraseText( aCntIdx ); + pTextNode->InsertText( aLine.getToken( 0, '\t', nLineTokenPos ), aCntIdx ); + + SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat()); + pBoxFormat->LockModify(); + pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE ); + pBoxFormat->UnlockModify(); + } + } + + const IDocumentSettingAccess& rIDSA = GetFrameFormat()->getIDocumentSettingAccess(); + SwDoc* pDoc = GetFrameFormat()->GetDoc(); + if( AUTOUPD_FIELD_AND_CHARTS == rIDSA.getFieldUpdateFlags(true) ) + pDoc->getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) ); +} + +SwDDEFieldType* SwDDETable::GetDDEFieldType() +{ + return m_pDDEType; +} + +void SwDDETable::NoDDETable() +{ + // search table node + OSL_ENSURE( GetFrameFormat(), "No FrameFormat" ); + SwDoc* pDoc = GetFrameFormat()->GetDoc(); + + // Is this the correct NodesArray? (because of UNDO) + if( m_aLines.empty() ) + return; + OSL_ENSURE( !GetTabSortBoxes().empty(), "Table without content?" ); + SwNode* pNd = const_cast<SwNode*>(static_cast<SwNode const *>(GetTabSortBoxes()[0]->GetSttNd())); + if( !pNd->GetNodes().IsDocNodes() ) + return; + + SwTableNode* pTableNd = pNd->FindTableNode(); + OSL_ENSURE( pTableNd, "Where is the table?"); + + std::unique_ptr<SwTable> pNewTable(new SwTable( *this )); + + // copy the table data + pNewTable->GetTabSortBoxes().insert( GetTabSortBoxes() ); // move content boxes + GetTabSortBoxes().clear(); + + pNewTable->GetTabLines().insert( pNewTable->GetTabLines().begin(), + GetTabLines().begin(), GetTabLines().end() ); // move lines + GetTabLines().clear(); + + if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) + m_pDDEType->DecRefCnt(); + + pTableNd->SetNewTable( std::move(pNewTable) ); // replace table +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/docufld.cxx b/sw/source/core/fields/docufld.cxx new file mode 100644 index 000000000..45793b492 --- /dev/null +++ b/sw/source/core/fields/docufld.cxx @@ -0,0 +1,2660 @@ +/* -*- 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 <config_features.h> +#include <config_fuzzers.h> + +#include <textapi.hxx> + +#include <hintids.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/script/Converter.hpp> +#include <com/sun/star/text/PlaceholderType.hpp> +#include <com/sun/star/text/TemplateDisplayFormat.hpp> +#include <com/sun/star/text/PageNumberType.hpp> +#include <com/sun/star/text/FilenameDisplayFormat.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Duration.hpp> +#include <o3tl/any.hxx> +#include <unotools/localedatawrapper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <tools/urlobj.hxx> +#include <svl/numformat.hxx> +#include <svl/urihelper.hxx> +#include <unotools/useroptions.hxx> +#include <unotools/syslocale.hxx> +#include <libxml/xmlstring.h> +#include <libxml/xmlwriter.h> + +#include <tools/time.hxx> +#include <tools/datetime.hxx> + +#include <com/sun/star/util/DateTime.hpp> + +#include <swmodule.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/doctempl.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <charfmt.hxx> +#include <docstat.hxx> +#include <pagedesc.hxx> +#include <fmtpdsc.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <cntfrm.hxx> +#include <pam.hxx> +#include <viewsh.hxx> +#include <dbmgr.hxx> +#include <shellres.hxx> +#include <docufld.hxx> +#include <flddat.hxx> +#include <docfld.hxx> +#include <ndtxt.hxx> +#include <expfld.hxx> +#include <poolfmt.hxx> +#include <docsh.hxx> +#include <unofldmid.h> +#include <swunohelper.hxx> +#include <strings.hrc> + +#include <editeng/outlobj.hxx> +#include <calbck.hxx> +#include <hints.hxx> + +#define URL_DECODE INetURLObject::DecodeMechanism::Unambiguous + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace nsSwDocInfoSubType; + +SwPageNumberFieldType::SwPageNumberFieldType() + : SwFieldType( SwFieldIds::PageNumber ), + m_nNumberingType( SVX_NUM_ARABIC ), + m_bVirtual( false ) +{ +} + +OUString SwPageNumberFieldType::Expand( SvxNumType nFormat, short nOff, + sal_uInt16 const nPageNumber, sal_uInt16 const nMaxPage, + const OUString& rUserStr, LanguageType nLang ) const +{ + SvxNumType nTmpFormat = (SVX_NUM_PAGEDESC == nFormat) ? m_nNumberingType : nFormat; + int const nTmp = nPageNumber + nOff; + + if (0 > nTmp || SVX_NUM_NUMBER_NONE == nTmpFormat || (!m_bVirtual && nTmp > nMaxPage)) + return OUString(); + + if( SVX_NUM_CHAR_SPECIAL == nTmpFormat ) + return rUserStr; + + return FormatNumber( nTmp, nTmpFormat, nLang ); +} + +std::unique_ptr<SwFieldType> SwPageNumberFieldType::Copy() const +{ + std::unique_ptr<SwPageNumberFieldType> pTmp(new SwPageNumberFieldType()); + + pTmp->m_nNumberingType = m_nNumberingType; + pTmp->m_bVirtual = m_bVirtual; + + return pTmp; +} + +void SwPageNumberFieldType::ChangeExpansion( SwDoc* pDoc, + bool bVirt, + const SvxNumType* pNumFormat ) +{ + if( pNumFormat ) + m_nNumberingType = *pNumFormat; + + m_bVirtual = false; + if (!(bVirt && pDoc)) + return; + + // check the flag since the layout NEVER sets it back + const SfxItemPool &rPool = pDoc->GetAttrPool(); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_PAGEDESC)) + { + auto pDesc = dynamic_cast<const SwFormatPageDesc*>(pItem); + if( pDesc && pDesc->GetNumOffset() && pDesc->GetDefinedIn() ) + { + const SwContentNode* pNd = dynamic_cast<const SwContentNode*>( pDesc->GetDefinedIn() ); + if( pNd ) + { + if (SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*pNd).First()) + // sw_redlinehide: not sure if this should happen only if + // it's the first node, because that's where RES_PAGEDESC + // is effective? + m_bVirtual = true; + } + else if( dynamic_cast< const SwFormat* >(pDesc->GetDefinedIn()) != nullptr) + { + SwAutoFormatGetDocNode aGetHt( &pDoc->GetNodes() ); + m_bVirtual = !pDesc->GetDefinedIn()->GetInfo( aGetHt ); + break; + } + } + } +} + +SwPageNumberField::SwPageNumberField(SwPageNumberFieldType* pTyp, + sal_uInt16 nSub, sal_uInt32 nFormat, short nOff, + sal_uInt16 const nPageNumber, sal_uInt16 const nMaxPage) + : SwField(pTyp, nFormat), m_nSubType(nSub), m_nOffset(nOff) + , m_nPageNumber(nPageNumber) + , m_nMaxPage(nMaxPage) +{ +} + +void SwPageNumberField::ChangeExpansion(sal_uInt16 const nPageNumber, + sal_uInt16 const nMaxPage) +{ + m_nPageNumber = nPageNumber; + m_nMaxPage = nMaxPage; +} + +OUString SwPageNumberField::ExpandImpl(SwRootFrame const*const) const +{ + OUString sRet; + SwPageNumberFieldType* pFieldType = static_cast<SwPageNumberFieldType*>(GetTyp()); + + if( PG_NEXT == m_nSubType && 1 != m_nOffset ) + { + sRet = pFieldType->Expand(static_cast<SvxNumType>(GetFormat()), 1, m_nPageNumber, m_nMaxPage, m_sUserStr, GetLanguage()); + if (!sRet.isEmpty()) + { + sRet = pFieldType->Expand(static_cast<SvxNumType>(GetFormat()), m_nOffset, m_nPageNumber, m_nMaxPage, m_sUserStr, GetLanguage()); + } + } + else if( PG_PREV == m_nSubType && -1 != m_nOffset ) + { + sRet = pFieldType->Expand(static_cast<SvxNumType>(GetFormat()), -1, m_nPageNumber, m_nMaxPage, m_sUserStr, GetLanguage()); + if (!sRet.isEmpty()) + { + sRet = pFieldType->Expand(static_cast<SvxNumType>(GetFormat()), m_nOffset, m_nPageNumber, m_nMaxPage, m_sUserStr, GetLanguage()); + } + } + else + sRet = pFieldType->Expand(static_cast<SvxNumType>(GetFormat()), m_nOffset, m_nPageNumber, m_nMaxPage, m_sUserStr, GetLanguage()); + return sRet; +} + +std::unique_ptr<SwField> SwPageNumberField::Copy() const +{ + std::unique_ptr<SwPageNumberField> pTmp(new SwPageNumberField( + static_cast<SwPageNumberFieldType*>(GetTyp()), m_nSubType, + GetFormat(), m_nOffset, m_nPageNumber, m_nMaxPage)); + pTmp->SetLanguage( GetLanguage() ); + pTmp->SetUserString( m_sUserStr ); + return std::unique_ptr<SwField>(pTmp.release()); +} + +OUString SwPageNumberField::GetPar2() const +{ + return OUString::number(m_nOffset); +} + +void SwPageNumberField::SetPar2(const OUString& rStr) +{ + m_nOffset = static_cast<short>(rStr.toInt32()); +} + +sal_uInt16 SwPageNumberField::GetSubType() const +{ + return m_nSubType; +} + +bool SwPageNumberField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_FORMAT: + rAny <<= static_cast<sal_Int16>(GetFormat()); + break; + case FIELD_PROP_USHORT1: + rAny <<= m_nOffset; + break; + case FIELD_PROP_SUBTYPE: + { + text::PageNumberType eType; + eType = text::PageNumberType_CURRENT; + if(m_nSubType == PG_PREV) + eType = text::PageNumberType_PREV; + else if(m_nSubType == PG_NEXT) + eType = text::PageNumberType_NEXT; + rAny <<= eType; + } + break; + case FIELD_PROP_PAR1: + rAny <<= m_sUserStr; + break; + + default: + assert(false); + } + return true; +} + +bool SwPageNumberField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + bool bRet = true; + sal_Int16 nSet = 0; + switch( nWhichId ) + { + case FIELD_PROP_FORMAT: + rAny >>= nSet; + + // TODO: where do the defines come from? + if(nSet <= SVX_NUM_PAGEDESC ) + SetFormat(nSet); + break; + case FIELD_PROP_USHORT1: + rAny >>= nSet; + m_nOffset = nSet; + break; + case FIELD_PROP_SUBTYPE: + switch( static_cast<text::PageNumberType>(SWUnoHelper::GetEnumAsInt32( rAny )) ) + { + case text::PageNumberType_CURRENT: + m_nSubType = PG_RANDOM; + break; + case text::PageNumberType_PREV: + m_nSubType = PG_PREV; + break; + case text::PageNumberType_NEXT: + m_nSubType = PG_NEXT; + break; + default: + bRet = false; + } + break; + case FIELD_PROP_PAR1: + rAny >>= m_sUserStr; + break; + + default: + assert(false); + } + return bRet; +} + +SwAuthorFieldType::SwAuthorFieldType() + : SwFieldType( SwFieldIds::Author ) +{ +} + +OUString SwAuthorFieldType::Expand(sal_uLong nFormat) +{ + SvtUserOptions& rOpt = SW_MOD()->GetUserOptions(); + if((nFormat & 0xff) == AF_NAME) + return rOpt.GetFullName(); + + return rOpt.GetID(); +} + +std::unique_ptr<SwFieldType> SwAuthorFieldType::Copy() const +{ + return std::make_unique<SwAuthorFieldType>(); +} + +SwAuthorField::SwAuthorField(SwAuthorFieldType* pTyp, sal_uInt32 nFormat) + : SwField(pTyp, nFormat) +{ + m_aContent = SwAuthorFieldType::Expand(GetFormat()); +} + +OUString SwAuthorField::ExpandImpl(SwRootFrame const*const) const +{ + if (!IsFixed()) + const_cast<SwAuthorField*>(this)->m_aContent = + SwAuthorFieldType::Expand(GetFormat()); + + return m_aContent; +} + +std::unique_ptr<SwField> SwAuthorField::Copy() const +{ + std::unique_ptr<SwAuthorField> pTmp(new SwAuthorField( static_cast<SwAuthorFieldType*>(GetTyp()), + GetFormat())); + pTmp->SetExpansion(m_aContent); + return std::unique_ptr<SwField>(pTmp.release()); +} + +bool SwAuthorField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL1: + rAny <<= (GetFormat() & 0xff) == AF_NAME; + break; + + case FIELD_PROP_BOOL2: + rAny <<= IsFixed(); + break; + + case FIELD_PROP_PAR1: + rAny <<= m_aContent; + break; + + case FIELD_PROP_TITLE: + break; + + default: + assert(false); + } + return true; +} + +bool SwAuthorField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL1: + SetFormat( *o3tl::doAccess<bool>(rAny) ? AF_NAME : AF_SHORTCUT ); + break; + + case FIELD_PROP_BOOL2: + if( *o3tl::doAccess<bool>(rAny) ) + SetFormat( GetFormat() | AF_FIXED); + else + SetFormat( GetFormat() & ~AF_FIXED); + break; + + case FIELD_PROP_PAR1: + rAny >>= m_aContent; + break; + + case FIELD_PROP_TITLE: + break; + + default: + assert(false); + } + return true; +} + +SwFileNameFieldType::SwFileNameFieldType(SwDoc& rDocument) + : SwFieldType( SwFieldIds::Filename ) + , m_rDoc(rDocument) +{ +} + +OUString SwFileNameFieldType::Expand(sal_uLong nFormat) const +{ + OUString aRet; + const SwDocShell* pDShell = m_rDoc.GetDocShell(); + if( pDShell && pDShell->HasName() ) + { + const INetURLObject& rURLObj = pDShell->GetMedium()->GetURLObject(); + switch( nFormat & ~FF_FIXED ) + { + case FF_PATH: + { + if( INetProtocol::File == rURLObj.GetProtocol() ) + { + INetURLObject aTemp(rURLObj); + aTemp.removeSegment(); + // last slash should belong to the pathname + aRet = aTemp.PathToFileName(); + } + else + { + aRet = URIHelper::removePassword( + rURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), + INetURLObject::EncodeMechanism::WasEncoded, URL_DECODE ); + const sal_Int32 nPos = aRet.indexOf(rURLObj.GetLastName( URL_DECODE )); + if (nPos>=0) + { + aRet = aRet.copy(0, nPos); + } + } + } + break; + + case FF_NAME: + aRet = rURLObj.GetLastName( INetURLObject::DecodeMechanism::WithCharset ); + break; + + case FF_NAME_NOEXT: + aRet = rURLObj.GetBase(); + break; + + default: + if( INetProtocol::File == rURLObj.GetProtocol() ) + aRet = rURLObj.GetFull(); + else + aRet = URIHelper::removePassword( + rURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), + INetURLObject::EncodeMechanism::WasEncoded, URL_DECODE ); + } + } + return aRet; +} + +std::unique_ptr<SwFieldType> SwFileNameFieldType::Copy() const +{ + return std::make_unique<SwFileNameFieldType>(m_rDoc); +} + +SwFileNameField::SwFileNameField(SwFileNameFieldType* pTyp, sal_uInt32 nFormat) + : SwField(pTyp, nFormat) +{ + m_aContent = static_cast<SwFileNameFieldType*>(GetTyp())->Expand(GetFormat()); +} + +OUString SwFileNameField::ExpandImpl(SwRootFrame const*const) const +{ + if (!IsFixed()) + const_cast<SwFileNameField*>(this)->m_aContent = static_cast<SwFileNameFieldType*>(GetTyp())->Expand(GetFormat()); + + return m_aContent; +} + +std::unique_ptr<SwField> SwFileNameField::Copy() const +{ + std::unique_ptr<SwFileNameField> pTmp( + new SwFileNameField(static_cast<SwFileNameFieldType*>(GetTyp()), GetFormat())); + pTmp->SetExpansion(m_aContent); + + return std::unique_ptr<SwField>(pTmp.release()); +} + +bool SwFileNameField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_FORMAT: + { + sal_Int16 nRet; + switch( GetFormat() &(~FF_FIXED) ) + { + case FF_PATH: + nRet = text::FilenameDisplayFormat::PATH; + break; + case FF_NAME_NOEXT: + nRet = text::FilenameDisplayFormat::NAME; + break; + case FF_NAME: + nRet = text::FilenameDisplayFormat::NAME_AND_EXT; + break; + default: nRet = text::FilenameDisplayFormat::FULL; + } + rAny <<= nRet; + } + break; + + case FIELD_PROP_BOOL2: + rAny <<= IsFixed(); + break; + + case FIELD_PROP_PAR3: + rAny <<= m_aContent; + break; + + default: + assert(false); + } + return true; +} + +bool SwFileNameField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_FORMAT: + { + //JP 24.10.2001: int32 because in UnoField.cxx a putvalue is + // called with a int32 value! But normally we need + // here only a int16 + sal_Int32 nType = 0; + rAny >>= nType; + bool bFixed = IsFixed(); + switch( nType ) + { + case text::FilenameDisplayFormat::PATH: + nType = FF_PATH; + break; + case text::FilenameDisplayFormat::NAME: + nType = FF_NAME_NOEXT; + break; + case text::FilenameDisplayFormat::NAME_AND_EXT: + nType = FF_NAME; + break; + default: nType = FF_PATHNAME; + } + if(bFixed) + nType |= FF_FIXED; + SetFormat(nType); + } + break; + + case FIELD_PROP_BOOL2: + if( *o3tl::doAccess<bool>(rAny) ) + SetFormat( GetFormat() | FF_FIXED); + else + SetFormat( GetFormat() & ~FF_FIXED); + break; + + case FIELD_PROP_PAR3: + rAny >>= m_aContent; + break; + + default: + assert(false); + } + return true; +} + +SwTemplNameFieldType::SwTemplNameFieldType(SwDoc& rDocument) + : SwFieldType( SwFieldIds::TemplateName ) + , m_rDoc(rDocument) +{ +} + +OUString SwTemplNameFieldType::Expand(sal_uLong nFormat) const +{ + OSL_ENSURE( nFormat < FF_END, "Expand: no valid Format!" ); + + OUString aRet; + SwDocShell *pDocShell(m_rDoc.GetDocShell()); + OSL_ENSURE(pDocShell, "no SwDocShell"); + if (pDocShell) { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps( + xDPS->getDocumentProperties()); + OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties"); + + if( FF_UI_NAME == nFormat ) + aRet = xDocProps->getTemplateName(); + else if( !xDocProps->getTemplateURL().isEmpty() ) + { + if( FF_UI_RANGE == nFormat ) + { + // for getting region names! + SfxDocumentTemplates aFac; + OUString sTmp; + OUString sRegion; + aFac.GetLogicNames( xDocProps->getTemplateURL(), sRegion, sTmp ); + aRet = sRegion; + } + else + { + INetURLObject aPathName( xDocProps->getTemplateURL() ); + if( FF_NAME == nFormat ) + aRet = aPathName.GetLastName(URL_DECODE); + else if( FF_NAME_NOEXT == nFormat ) + aRet = aPathName.GetBase(); + else + { + if( FF_PATH == nFormat ) + { + aPathName.removeSegment(); + aRet = aPathName.GetFull(); + } + else + aRet = aPathName.GetFull(); + } + } + } + } + return aRet; +} + +std::unique_ptr<SwFieldType> SwTemplNameFieldType::Copy() const +{ + return std::make_unique<SwTemplNameFieldType>(m_rDoc); +} + +SwTemplNameField::SwTemplNameField(SwTemplNameFieldType* pTyp, sal_uInt32 nFormat) + : SwField(pTyp, nFormat) +{} + +OUString SwTemplNameField::ExpandImpl(SwRootFrame const*const) const +{ + return static_cast<SwTemplNameFieldType*>(GetTyp())->Expand(GetFormat()); +} + +std::unique_ptr<SwField> SwTemplNameField::Copy() const +{ + return std::make_unique<SwTemplNameField>(static_cast<SwTemplNameFieldType*>(GetTyp()), GetFormat()); +} + +bool SwTemplNameField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch ( nWhichId ) + { + case FIELD_PROP_FORMAT: + { + sal_Int16 nRet; + switch( GetFormat() ) + { + case FF_PATH: nRet = text::FilenameDisplayFormat::PATH; break; + case FF_NAME_NOEXT: nRet = text::FilenameDisplayFormat::NAME; break; + case FF_NAME: nRet = text::FilenameDisplayFormat::NAME_AND_EXT; break; + case FF_UI_RANGE: nRet = text::TemplateDisplayFormat::AREA; break; + case FF_UI_NAME: nRet = text::TemplateDisplayFormat::TITLE; break; + default: nRet = text::FilenameDisplayFormat::FULL; + + } + rAny <<= nRet; + } + break; + + default: + assert(false); + } + return true; +} + +bool SwTemplNameField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch ( nWhichId ) + { + case FIELD_PROP_FORMAT: + { + //JP 24.10.2001: int32 because in UnoField.cxx a putvalue is + // called with a int32 value! But normally we need + // here only a int16 + sal_Int32 nType = 0; + rAny >>= nType; + switch( nType ) + { + case text::FilenameDisplayFormat::PATH: + SetFormat(FF_PATH); + break; + case text::FilenameDisplayFormat::NAME: + SetFormat(FF_NAME_NOEXT); + break; + case text::FilenameDisplayFormat::NAME_AND_EXT: + SetFormat(FF_NAME); + break; + case text::TemplateDisplayFormat::AREA : + SetFormat(FF_UI_RANGE); + break; + case text::TemplateDisplayFormat::TITLE : + SetFormat(FF_UI_NAME); + break; + default: SetFormat(FF_PATHNAME); + } + } + break; + + default: + assert(false); + } + return true; +} + +SwDocStatFieldType::SwDocStatFieldType(SwDoc& rDocument) + : SwFieldType(SwFieldIds::DocStat) + , m_rDoc(rDocument) + , m_nNumberingType(SVX_NUM_ARABIC) +{ +} + +OUString SwDocStatFieldType::Expand(sal_uInt16 nSubType, SvxNumType nFormat) const +{ + sal_uInt32 nVal = 0; + const SwDocStat& rDStat = m_rDoc.getIDocumentStatistics().GetDocStat(); + switch( nSubType ) + { + case DS_TBL: nVal = rDStat.nTable; break; + case DS_GRF: nVal = rDStat.nGrf; break; + case DS_OLE: nVal = rDStat.nOLE; break; + case DS_PARA: nVal = rDStat.nPara; break; + case DS_WORD: nVal = rDStat.nWord; break; + case DS_CHAR: nVal = rDStat.nChar; break; + case DS_PAGE: + if( m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ) + const_cast<SwDocStat &>(rDStat).nPage = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->GetPageNum(); + nVal = rDStat.nPage; + if( SVX_NUM_PAGEDESC == nFormat ) + nFormat = m_nNumberingType; + break; + default: + OSL_FAIL( "SwDocStatFieldType::Expand: unknown SubType" ); + } + + if( nVal <= SHRT_MAX ) + return FormatNumber( nVal, nFormat ); + + return OUString::number( nVal ); +} + +std::unique_ptr<SwFieldType> SwDocStatFieldType::Copy() const +{ + return std::make_unique<SwDocStatFieldType>(m_rDoc); +} + +/** + * @param pTyp + * @param nSub SubType + * @param nFormat + */ +SwDocStatField::SwDocStatField(SwDocStatFieldType* pTyp, sal_uInt16 nSub, sal_uInt32 nFormat) + : SwField(pTyp, nFormat), + m_nSubType(nSub) +{} + +OUString SwDocStatField::ExpandImpl(SwRootFrame const*const) const +{ + return static_cast<SwDocStatFieldType*>(GetTyp())->Expand(m_nSubType, static_cast<SvxNumType>(GetFormat())); +} + +std::unique_ptr<SwField> SwDocStatField::Copy() const +{ + return std::make_unique<SwDocStatField>( + static_cast<SwDocStatFieldType*>(GetTyp()), m_nSubType, GetFormat() ); +} + +sal_uInt16 SwDocStatField::GetSubType() const +{ + return m_nSubType; +} + +void SwDocStatField::SetSubType(sal_uInt16 nSub) +{ + m_nSubType = nSub; +} + +void SwDocStatField::ChangeExpansion( const SwFrame* pFrame ) +{ + if( DS_PAGE == m_nSubType && SVX_NUM_PAGEDESC == GetFormat() ) + static_cast<SwDocStatFieldType*>(GetTyp())->SetNumFormat( + pFrame->FindPageFrame()->GetPageDesc()->GetNumType().GetNumberingType() ); +} + +bool SwDocStatField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch ( nWhichId ) + { + case FIELD_PROP_USHORT2: + rAny <<= static_cast<sal_Int16>(GetFormat()); + break; + + default: + assert(false); + } + return true; +} + +bool SwDocStatField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + bool bRet = false; + switch ( nWhichId ) + { + case FIELD_PROP_USHORT2: + { + sal_Int16 nSet = 0; + rAny >>= nSet; + if(nSet <= SVX_NUM_CHARS_LOWER_LETTER_N && + nSet != SVX_NUM_CHAR_SPECIAL && + nSet != SVX_NUM_BITMAP) + { + SetFormat(nSet); + bRet = true; + } + } + break; + + default: + assert(false); + } + return bRet; +} + +// Document info field type + +SwDocInfoFieldType::SwDocInfoFieldType(SwDoc* pDc) + : SwValueFieldType( pDc, SwFieldIds::DocInfo ) +{ +} + +std::unique_ptr<SwFieldType> SwDocInfoFieldType::Copy() const +{ + return std::make_unique<SwDocInfoFieldType>(GetDoc()); +} + +static void lcl_GetLocalDataWrapper( LanguageType nLang, + const LocaleDataWrapper **ppAppLocalData, + const LocaleDataWrapper **ppLocalData ) +{ + SvtSysLocale aLocale; + *ppAppLocalData = &aLocale.GetLocaleData(); + *ppLocalData = *ppAppLocalData; + if( nLang != (*ppLocalData)->getLanguageTag().getLanguageType() ) + *ppLocalData = new LocaleDataWrapper(LanguageTag( nLang )); +} + +OUString SwDocInfoFieldType::Expand( sal_uInt16 nSub, sal_uInt32 nFormat, + LanguageType nLang, const OUString& rName ) const +{ + const LocaleDataWrapper *pAppLocalData = nullptr, *pLocalData = nullptr; + SwDocShell *pDocShell(GetDoc()->GetDocShell()); + OSL_ENSURE(pDocShell, "no SwDocShell"); + if (!pDocShell) { return OUString(); } + + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps( + xDPS->getDocumentProperties()); + OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties"); + + sal_uInt16 nExtSub = nSub & 0xff00; + nSub &= 0xff; // do not consider extended SubTypes + + OUString aStr; + switch(nSub) + { + case DI_TITLE: aStr = xDocProps->getTitle(); break; + case DI_SUBJECT:aStr = xDocProps->getSubject(); break; + case DI_KEYS: aStr = ::comphelper::string::convertCommaSeparated( + xDocProps->getKeywords()); + break; + case DI_COMMENT:aStr = xDocProps->getDescription(); break; + case DI_DOCNO: aStr = OUString::number( + xDocProps->getEditingCycles() ); + break; + case DI_EDIT: + if ( !nFormat ) + { + lcl_GetLocalDataWrapper( nLang, &pAppLocalData, &pLocalData ); + sal_Int32 dur = xDocProps->getEditingDuration(); + // If Seconds > 0 then bSec should be TRUE otherwise Seconds + // information will be lost if file has EditTime in Seconds format. + aStr = pLocalData->getTime( tools::Time(dur/3600, (dur%3600)/60, dur%60), + dur%60 > 0); + } + else + { + sal_Int32 dur = xDocProps->getEditingDuration(); + double fVal = tools::Time(dur/3600, (dur%3600)/60, dur%60).GetTimeInDays(); + aStr = ExpandValue(fVal, nFormat, nLang); + } + break; + case DI_CUSTOM: + { + OUString sVal; + try + { + uno::Any aAny; + uno::Reference < beans::XPropertySet > xSet( + xDocProps->getUserDefinedProperties(), + uno::UNO_QUERY_THROW); + aAny = xSet->getPropertyValue( rName ); + + uno::Reference < script::XTypeConverter > xConverter( script::Converter::create(comphelper::getProcessComponentContext()) ); + uno::Any aNew = xConverter->convertToSimpleType( aAny, uno::TypeClass_STRING ); + aNew >>= sVal; + } + catch (uno::Exception&) {} + return sVal; + } + + default: + { + OUString aName( xDocProps->getAuthor() ); + util::DateTime uDT( xDocProps->getCreationDate() ); + DateTime aDate(uDT); + if( nSub == DI_CREATE ) + ; // that's it !! + else if( nSub == DI_CHANGE ) + { + aName = xDocProps->getModifiedBy(); + uDT = xDocProps->getModificationDate(); + aDate = DateTime(uDT); + } + else if( nSub == DI_PRINT ) + { + aName = xDocProps->getPrintedBy(); + uDT = xDocProps->getPrintDate(); + aDate = DateTime(uDT); + } + else + break; + + if (aDate.IsValidAndGregorian()) + { + switch (nExtSub & ~DI_SUB_FIXED) + { + case DI_SUB_AUTHOR: + aStr = aName; + break; + + case DI_SUB_TIME: + if (!nFormat) + { + lcl_GetLocalDataWrapper( nLang, &pAppLocalData, + &pLocalData ); + aStr = pLocalData->getTime( aDate, + false); + } + else + { + // start the number formatter + double fVal = SwDateTimeField::GetDateTime( *GetDoc(), + aDate); + aStr = ExpandValue(fVal, nFormat, nLang); + } + break; + + case DI_SUB_DATE: + if (!nFormat) + { + lcl_GetLocalDataWrapper( nLang, &pAppLocalData, + &pLocalData ); + aStr = pLocalData->getDate( aDate ); + } + else + { + // start the number formatter + double fVal = SwDateTimeField::GetDateTime( *GetDoc(), + aDate); + aStr = ExpandValue(fVal, nFormat, nLang); + } + break; + } + } + } + break; + } + + if( pAppLocalData != pLocalData ) + delete pLocalData; + + return aStr; +} + +// document info field + +SwDocInfoField::SwDocInfoField(SwDocInfoFieldType* pTyp, sal_uInt16 nSub, const OUString& rName, sal_uInt32 nFormat) : + SwValueField(pTyp, nFormat), m_nSubType(nSub) +{ + m_aName = rName; + m_aContent = static_cast<SwDocInfoFieldType*>(GetTyp())->Expand(m_nSubType, nFormat, GetLanguage(), m_aName); +} + +SwDocInfoField::SwDocInfoField(SwDocInfoFieldType* pTyp, sal_uInt16 nSub, const OUString& rName, const OUString& rValue, sal_uInt32 nFormat) : + SwValueField(pTyp, nFormat), m_nSubType(nSub) +{ + m_aName = rName; + m_aContent = rValue; +} + +template<class T> +static double lcl_TimeToDouble( const T& rTime ) +{ + const double fNanoSecondsPerDay = 86400000000000.0; + return ( (rTime.Hours * SAL_CONST_INT64(3600000000000)) + + (rTime.Minutes * SAL_CONST_INT64( 60000000000)) + + (rTime.Seconds * SAL_CONST_INT64( 1000000000)) + + (rTime.NanoSeconds)) + / fNanoSecondsPerDay; +} + +template<class D> +static double lcl_DateToDouble( const D& rDate, const Date& rNullDate ) +{ + tools::Long nDate = Date::DateToDays( rDate.Day, rDate.Month, rDate.Year ); + tools::Long nNullDate = Date::DateToDays( rNullDate.GetDay(), rNullDate.GetMonth(), rNullDate.GetYear() ); + return double( nDate - nNullDate ); +} + +OUString SwDocInfoField::ExpandImpl(SwRootFrame const*const) const +{ + if ( ( m_nSubType & 0xFF ) == DI_CUSTOM ) + { + // custom properties currently need special treatment + // We don't have a secure way to detect "real" custom properties in Word import of text + // fields, so we treat *every* unknown property as a custom property, even the "built-in" + // section in Word's document summary information stream as these properties have not been + // inserted when the document summary information was imported, we do it here. + // This approach is still a lot better than the old one to import such fields as + // "user fields" and simple text + SwDocShell* pDocShell = GetDoc()->GetDocShell(); + if( !pDocShell ) + return m_aContent; + try + { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps( xDPS->getDocumentProperties()); + uno::Reference < beans::XPropertySet > xSet( xDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW); + uno::Reference < beans::XPropertySetInfo > xSetInfo = xSet->getPropertySetInfo(); + + uno::Any aAny; + if( xSetInfo->hasPropertyByName( m_aName ) ) + aAny = xSet->getPropertyValue( m_aName ); + if ( aAny.getValueType() != cppu::UnoType<void>::get() ) + { + // "void" type means that the property has not been inserted until now + if ( !IsFixed() ) + { + // if the field is "fixed" we don't update it from the property + OUString sVal; + uno::Reference < script::XTypeConverter > xConverter( script::Converter::create(comphelper::getProcessComponentContext()) ); + util::Date aDate; + util::DateTime aDateTime; + util::Duration aDuration; + if( aAny >>= aDate) + { + SvNumberFormatter* pFormatter = pDocShell->GetDoc()->GetNumberFormatter(); + const Date& rNullDate = pFormatter->GetNullDate(); + sVal = ExpandValue( lcl_DateToDouble<util::Date>( aDate, rNullDate ), GetFormat(), GetLanguage()); + } + else if( aAny >>= aDateTime ) + { + double fDateTime = lcl_TimeToDouble<util::DateTime>( aDateTime ); + SvNumberFormatter* pFormatter = pDocShell->GetDoc()->GetNumberFormatter(); + const Date& rNullDate = pFormatter->GetNullDate(); + fDateTime += lcl_DateToDouble<util::DateTime>( aDateTime, rNullDate ); + sVal = ExpandValue( fDateTime, GetFormat(), GetLanguage()); + } + else if( aAny >>= aDuration ) + { + sVal = OUStringChar(aDuration.Negative ? '-' : '+') + + SwViewShell::GetShellRes()->sDurationFormat; + sVal = sVal.replaceFirst("%1", OUString::number( aDuration.Years ) ); + sVal = sVal.replaceFirst("%2", OUString::number( aDuration.Months ) ); + sVal = sVal.replaceFirst("%3", OUString::number( aDuration.Days ) ); + sVal = sVal.replaceFirst("%4", OUString::number( aDuration.Hours ) ); + sVal = sVal.replaceFirst("%5", OUString::number( aDuration.Minutes) ); + sVal = sVal.replaceFirst("%6", OUString::number( aDuration.Seconds) ); + } + else + { + uno::Any aNew = xConverter->convertToSimpleType( aAny, uno::TypeClass_STRING ); + aNew >>= sVal; + } + const_cast<SwDocInfoField*>(this)->m_aContent = sVal; + } + } + } + catch (uno::Exception&) {} + } + else if ( !IsFixed() ) + const_cast<SwDocInfoField*>(this)->m_aContent = static_cast<SwDocInfoFieldType*>(GetTyp())->Expand(m_nSubType, GetFormat(), GetLanguage(), m_aName); + + return m_aContent; +} + +OUString SwDocInfoField::GetFieldName() const +{ + OUString aStr(SwFieldType::GetTypeStr(GetTypeId()) + ":"); + + sal_uInt16 const nSub = m_nSubType & 0xff; + + switch (nSub) + { + case DI_CUSTOM: + aStr += m_aName; + break; + + default: + aStr += SwViewShell::GetShellRes() + ->aDocInfoLst[ nSub - DI_SUBTYPE_BEGIN ]; + break; + } + if (IsFixed()) + { + aStr += " " + SwViewShell::GetShellRes()->aFixedStr; + } + return aStr; +} + +std::unique_ptr<SwField> SwDocInfoField::Copy() const +{ + std::unique_ptr<SwDocInfoField> pField(new SwDocInfoField(static_cast<SwDocInfoFieldType*>(GetTyp()), m_nSubType, m_aName, GetFormat())); + pField->SetAutomaticLanguage(IsAutomaticLanguage()); + pField->m_aContent = m_aContent; + + return std::unique_ptr<SwField>(pField.release()); +} + +sal_uInt16 SwDocInfoField::GetSubType() const +{ + return m_nSubType; +} + +void SwDocInfoField::SetSubType(sal_uInt16 nSub) +{ + m_nSubType = nSub; +} + +void SwDocInfoField::SetLanguage(LanguageType nLng) +{ + if (!GetFormat()) + SwField::SetLanguage(nLng); + else + SwValueField::SetLanguage(nLng); +} + +bool SwDocInfoField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= m_aContent; + break; + + case FIELD_PROP_PAR4: + rAny <<= m_aName; + break; + + case FIELD_PROP_USHORT1: + rAny <<= static_cast<sal_Int16>(m_aContent.toInt32()); + break; + + case FIELD_PROP_BOOL1: + rAny <<= 0 != (m_nSubType & DI_SUB_FIXED); + break; + + case FIELD_PROP_FORMAT: + rAny <<= static_cast<sal_Int32>(GetFormat()); + break; + + case FIELD_PROP_DOUBLE: + { + double fVal = GetValue(); + rAny <<= fVal; + } + break; + case FIELD_PROP_PAR3: + rAny <<= ExpandImpl(nullptr); + break; + case FIELD_PROP_BOOL2: + { + sal_uInt16 nExtSub = (m_nSubType & 0xff00) & ~DI_SUB_FIXED; + rAny <<= nExtSub == DI_SUB_DATE; + } + break; + default: + return SwField::QueryValue(rAny, nWhichId); + } + return true; +} + +bool SwDocInfoField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + sal_Int32 nValue = 0; + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + if( m_nSubType & DI_SUB_FIXED ) + rAny >>= m_aContent; + break; + + case FIELD_PROP_USHORT1: + if( m_nSubType & DI_SUB_FIXED ) + { + rAny >>= nValue; + m_aContent = OUString::number(nValue); + } + break; + + case FIELD_PROP_BOOL1: + if(*o3tl::doAccess<bool>(rAny)) + m_nSubType |= DI_SUB_FIXED; + else + m_nSubType &= ~DI_SUB_FIXED; + break; + case FIELD_PROP_FORMAT: + { + rAny >>= nValue; + if( nValue >= 0) + SetFormat(nValue); + } + break; + + case FIELD_PROP_PAR3: + rAny >>= m_aContent; + break; + case FIELD_PROP_BOOL2: + m_nSubType &= 0xf0ff; + if(*o3tl::doAccess<bool>(rAny)) + m_nSubType |= DI_SUB_DATE; + else + m_nSubType |= DI_SUB_TIME; + break; + default: + return SwField::PutValue(rAny, nWhichId); + } + return true; +} + +SwHiddenTextFieldType::SwHiddenTextFieldType( bool bSetHidden ) + : SwFieldType( SwFieldIds::HiddenText ), m_bHidden( bSetHidden ) +{ +} + +std::unique_ptr<SwFieldType> SwHiddenTextFieldType::Copy() const +{ + return std::make_unique<SwHiddenTextFieldType>( m_bHidden ); +} + +void SwHiddenTextFieldType::SetHiddenFlag( bool bSetHidden ) +{ + if( m_bHidden != bSetHidden ) + { + m_bHidden = bSetHidden; + UpdateFields(); // notify all HiddenTexts + } +} + +SwHiddenTextField::SwHiddenTextField( SwHiddenTextFieldType* pFieldType, + bool bConditional, + const OUString& rCond, + const OUString& rStr, + bool bHidden, + SwFieldTypesEnum nSub) : + SwField( pFieldType ), m_aCond(rCond), m_nSubType(nSub), + m_bCanToggle(bConditional), m_bIsHidden(bHidden), m_bValid(false) +{ + if(m_nSubType == SwFieldTypesEnum::ConditionalText) + { + sal_Int32 nPos = 0; + m_aTRUEText = rStr.getToken(0, '|', nPos); + + if(nPos != -1) + { + m_aFALSEText = rStr.getToken(0, '|', nPos); + if(nPos != -1) + { + m_aContent = rStr.getToken(0, '|', nPos); + m_bValid = true; + } + } + } + else + m_aTRUEText = rStr; +} + +SwHiddenTextField::SwHiddenTextField( SwHiddenTextFieldType* pFieldType, + const OUString& rCond, + const OUString& rTrue, + const OUString& rFalse, + SwFieldTypesEnum nSub) + : SwField( pFieldType ), m_aTRUEText(rTrue), m_aFALSEText(rFalse), m_aCond(rCond), m_nSubType(nSub), + m_bIsHidden(true), m_bValid(false) +{ + m_bCanToggle = !m_aCond.isEmpty(); +} + +OUString SwHiddenTextField::ExpandImpl(SwRootFrame const*const) const +{ + // Type: !Hidden -> show always + // Hide -> evaluate condition + + if( SwFieldTypesEnum::ConditionalText == m_nSubType ) + { + if( m_bValid ) + return m_aContent; + + if( m_bCanToggle && !m_bIsHidden ) + return m_aTRUEText; + } + else if( !static_cast<SwHiddenTextFieldType*>(GetTyp())->GetHiddenFlag() || + ( m_bCanToggle && m_bIsHidden )) + return m_aTRUEText; + + return m_aFALSEText; +} + +/// get current field value and cache it +void SwHiddenTextField::Evaluate(SwDoc& rDoc) +{ + if( SwFieldTypesEnum::ConditionalText != m_nSubType ) + return; + +#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS + (void) rDoc; +#else + SwDBManager* pMgr = rDoc.GetDBManager(); +#endif + m_bValid = false; + OUString sTmpName = (m_bCanToggle && !m_bIsHidden) ? m_aTRUEText : m_aFALSEText; + + // Database expressions need to be different from normal text. Therefore, normal text is set + // in quotes. If the latter exist they will be removed. If not, check if potential DB name. + // Only if there are two or more dots and no quotes, we assume a database. + if (sTmpName.getLength()>1 && + sTmpName.startsWith("\"") && + sTmpName.endsWith("\"")) + { + m_aContent = sTmpName.copy(1, sTmpName.getLength() - 2); + m_bValid = true; + } + else if(sTmpName.indexOf('\"')<0 && + comphelper::string::getTokenCount(sTmpName, '.') > 2) + { + sTmpName = ::ReplacePoint(sTmpName); + if(sTmpName.startsWith("[") && sTmpName.endsWith("]")) + { // remove brackets + sTmpName = sTmpName.copy(1, sTmpName.getLength() - 2); + } +#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS + if( pMgr) + { + sal_Int32 nIdx{ 0 }; + OUString sDBName( GetDBName( sTmpName, rDoc )); + OUString sDataSource(sDBName.getToken(0, DB_DELIM, nIdx)); + OUString sDataTableOrQuery(sDBName.getToken(0, DB_DELIM, nIdx)); + if( pMgr->IsInMerge() && !sDBName.isEmpty() && + pMgr->IsDataSourceOpen( sDataSource, + sDataTableOrQuery, false)) + { + double fNumber; + pMgr->GetMergeColumnCnt(GetColumnName( sTmpName ), + GetLanguage(), m_aContent, &fNumber ); + m_bValid = true; + } + else if( !sDBName.isEmpty() && !sDataSource.isEmpty() && + !sDataTableOrQuery.isEmpty() ) + m_bValid = true; + } +#endif + } +} + +OUString SwHiddenTextField::GetFieldName() const +{ + OUString aStr = SwFieldType::GetTypeStr(m_nSubType) + + " " + m_aCond + " " + m_aTRUEText; + + if (m_nSubType == SwFieldTypesEnum::ConditionalText) + { + aStr += " : " + m_aFALSEText; + } + return aStr; +} + +std::unique_ptr<SwField> SwHiddenTextField::Copy() const +{ + std::unique_ptr<SwHiddenTextField> pField( + new SwHiddenTextField(static_cast<SwHiddenTextFieldType*>(GetTyp()), m_aCond, + m_aTRUEText, m_aFALSEText)); + pField->m_bIsHidden = m_bIsHidden; + pField->m_bValid = m_bValid; + pField->m_aContent = m_aContent; + pField->SetFormat(GetFormat()); + pField->m_nSubType = m_nSubType; + return std::unique_ptr<SwField>(pField.release()); +} + +/// set condition +void SwHiddenTextField::SetPar1(const OUString& rStr) +{ + m_aCond = rStr; + m_bCanToggle = !m_aCond.isEmpty(); +} + +OUString SwHiddenTextField::GetPar1() const +{ + return m_aCond; +} + +/// set True/False text +void SwHiddenTextField::SetPar2(const OUString& rStr) +{ + if (m_nSubType == SwFieldTypesEnum::ConditionalText) + { + sal_Int32 nPos = rStr.indexOf('|'); + if (nPos == -1) + m_aTRUEText = rStr; + else + { + m_aTRUEText = rStr.copy(0, nPos); + m_aFALSEText = rStr.copy(nPos + 1); + } + } + else + m_aTRUEText = rStr; +} + +/// get True/False text +OUString SwHiddenTextField::GetPar2() const +{ + if(m_nSubType != SwFieldTypesEnum::ConditionalText) + { + return m_aTRUEText; + } + return m_aTRUEText + "|" + m_aFALSEText; +} + +sal_uInt16 SwHiddenTextField::GetSubType() const +{ + return static_cast<sal_uInt16>(m_nSubType); +} + +bool SwHiddenTextField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= m_aCond; + break; + case FIELD_PROP_PAR2: + rAny <<= m_aTRUEText; + break; + case FIELD_PROP_PAR3: + rAny <<= m_aFALSEText; + break; + case FIELD_PROP_PAR4 : + rAny <<= m_aContent; + break; + case FIELD_PROP_BOOL1: + rAny <<= m_bIsHidden; + break; + default: + assert(false); + } + return true; +} + +bool SwHiddenTextField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + { + OUString sVal; + rAny >>= sVal; + SetPar1(sVal); + } + break; + case FIELD_PROP_PAR2: + rAny >>= m_aTRUEText; + break; + case FIELD_PROP_PAR3: + rAny >>= m_aFALSEText; + break; + case FIELD_PROP_BOOL1: + m_bIsHidden = *o3tl::doAccess<bool>(rAny); + break; + case FIELD_PROP_PAR4: + rAny >>= m_aContent; + m_bValid = true; + break; + default: + assert(false); + } + return true; +} + +OUString SwHiddenTextField::GetColumnName(const OUString& rName) +{ + sal_Int32 nPos = rName.indexOf(DB_DELIM); + if( nPos>=0 ) + { + nPos = rName.indexOf(DB_DELIM, nPos + 1); + + if( nPos>=0 ) + return rName.copy(nPos + 1); + } + return rName; +} + +OUString SwHiddenTextField::GetDBName(std::u16string_view rName, SwDoc& rDoc) +{ + size_t nPos = rName.find(DB_DELIM); + if( nPos != std::u16string_view::npos ) + { + nPos = rName.find(DB_DELIM, nPos + 1); + + if( nPos != std::u16string_view::npos ) + return OUString(rName.substr(0, nPos)); + } + + SwDBData aData = rDoc.GetDBData(); + return aData.sDataSource + OUStringChar(DB_DELIM) + aData.sCommand; +} + +// [aFieldDefinition] value sample : " IF A == B \"TrueText\" \"FalseText\"" +void SwHiddenTextField::ParseIfFieldDefinition(const OUString& aFieldDefinition, + OUString& rCondition, + OUString& rTrue, + OUString& rFalse) +{ + // get all positions inside the input string where words are started + // + // In: " IF A == B \"TrueText\" \"FalseText\"" + // 0 1 2 3 + // 01234567890 123456789 01 2345678901 2 + // + // result: + // [1, 4, 6, 9, 11, 22] + std::vector<sal_Int32> wordPosition; + { + bool quoted = false; + bool insideWord = false; + for (sal_Int32 i = 0; i < aFieldDefinition.getLength(); i++) + { + if (quoted) + { + if (aFieldDefinition[i] == '\"') + { + quoted = false; + insideWord = false; + } + } + else + { + if (aFieldDefinition[i] == ' ') + { + // word delimiter + insideWord = false; + } + else + { + if (insideWord) + { + quoted = (aFieldDefinition[i] == '\"'); + } + else + { + insideWord = true; + wordPosition.push_back(i); + quoted = (aFieldDefinition[i] == '\"'); + } + } + } + } + } + + // first word is always "IF" + // last two words are: true-case and false-case, + // everything before is treated as condition expression + // => we need at least 4 words to be inside the input string + if (wordPosition.size() < 4) + { + return; + } + + + const sal_Int32 conditionBegin = wordPosition[1]; + const sal_Int32 trueBegin = wordPosition[wordPosition.size() - 2]; + const sal_Int32 falseBegin = wordPosition[wordPosition.size() - 1]; + + const sal_Int32 conditionLength = trueBegin - conditionBegin; + const sal_Int32 trueLength = falseBegin - trueBegin; + + // Syntax + // OUString::copy( sal_Int32 beginIndex, sal_Int32 count ) + rCondition = aFieldDefinition.copy(conditionBegin, conditionLength); + rTrue = aFieldDefinition.copy(trueBegin, trueLength); + rFalse = aFieldDefinition.copy(falseBegin); + + // trim + rCondition = rCondition.trim(); + rTrue = rTrue.trim(); + rFalse = rFalse.trim(); + + // remove quotes + if (rCondition.getLength() >= 2) + { + if (rCondition[0] == '\"' && rCondition[rCondition.getLength() - 1] == '\"') + rCondition = rCondition.copy(1, rCondition.getLength() - 2); + } + if (rTrue.getLength() >= 2) + { + if (rTrue[0] == '\"' && rTrue[rTrue.getLength() - 1] == '\"') + rTrue = rTrue.copy(1, rTrue.getLength() - 2); + } + if (rFalse.getLength() >= 2) + { + if (rFalse[0] == '\"' && rFalse[rFalse.getLength() - 1] == '\"') + rFalse = rFalse.copy(1, rFalse.getLength() - 2); + } + + // Note: do not make trim once again, while this is a user defined data +} + +// field type for line height 0 + +SwHiddenParaFieldType::SwHiddenParaFieldType() + : SwFieldType( SwFieldIds::HiddenPara ) +{ +} + +std::unique_ptr<SwFieldType> SwHiddenParaFieldType::Copy() const +{ + return std::make_unique<SwHiddenParaFieldType>(); +} + +// field for line height 0 + +SwHiddenParaField::SwHiddenParaField(SwHiddenParaFieldType* pTyp, const OUString& rStr) + : SwField(pTyp), m_aCond(rStr) +{ + m_bIsHidden = false; +} + +OUString SwHiddenParaField::ExpandImpl(SwRootFrame const*const) const +{ + return OUString(); +} + +std::unique_ptr<SwField> SwHiddenParaField::Copy() const +{ + std::unique_ptr<SwHiddenParaField> pField(new SwHiddenParaField(static_cast<SwHiddenParaFieldType*>(GetTyp()), m_aCond)); + pField->m_bIsHidden = m_bIsHidden; + return std::unique_ptr<SwField>(pField.release()); +} + +bool SwHiddenParaField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch ( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= m_aCond; + break; + case FIELD_PROP_BOOL1: + rAny <<= m_bIsHidden; + break; + + default: + assert(false); + } + return true; +} + +bool SwHiddenParaField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch ( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny >>= m_aCond; + break; + case FIELD_PROP_BOOL1: + m_bIsHidden = *o3tl::doAccess<bool>(rAny); + break; + + default: + assert(false); + } + return true; +} + +/// set condition +void SwHiddenParaField::SetPar1(const OUString& rStr) +{ + m_aCond = rStr; +} + +OUString SwHiddenParaField::GetPar1() const +{ + return m_aCond; +} + +// PostIt field type + +SwPostItFieldType::SwPostItFieldType(SwDoc& rDoc) + : SwFieldType( SwFieldIds::Postit ) + , mrDoc(rDoc) +{} + +std::unique_ptr<SwFieldType> SwPostItFieldType::Copy() const +{ + return std::make_unique<SwPostItFieldType>(mrDoc); +} + +// PostIt field + +sal_uInt32 SwPostItField::s_nLastPostItId = 1; + +SwPostItField::SwPostItField( SwPostItFieldType* pT, + const OUString& rAuthor, + const OUString& rText, + const OUString& rInitials, + const OUString& rName, + const DateTime& rDateTime, + const bool bResolved, + const sal_uInt32 nPostItId +) + : SwField( pT ) + , m_sText( rText ) + , m_sAuthor( rAuthor ) + , m_sInitials( rInitials ) + , m_sName( rName ) + , m_aDateTime( rDateTime ) + , m_bResolved( bResolved ) +{ + m_nPostItId = nPostItId == 0 ? s_nLastPostItId++ : nPostItId; +} + +SwPostItField::~SwPostItField() +{ + if ( m_xTextObject.is() ) + { + m_xTextObject->DisposeEditSource(); + } + + mpText.reset(); +} + +OUString SwPostItField::ExpandImpl(SwRootFrame const*const) const +{ + return OUString(); +} + +OUString SwPostItField::GetDescription() const +{ + return SwResId(STR_NOTE); +} + +void SwPostItField::SetResolved(bool bNewState) +{ + m_bResolved = bNewState; +} + +void SwPostItField::ToggleResolved() +{ + m_bResolved = !m_bResolved; +} + +bool SwPostItField::GetResolved() const +{ + return m_bResolved; +} + +std::unique_ptr<SwField> SwPostItField::Copy() const +{ + std::unique_ptr<SwPostItField> pRet(new SwPostItField( static_cast<SwPostItFieldType*>(GetTyp()), m_sAuthor, m_sText, m_sInitials, m_sName, + m_aDateTime, m_bResolved, m_nPostItId)); + if (mpText) + pRet->SetTextObject( *mpText ); + + // Note: member <m_xTextObject> not copied. + + return std::unique_ptr<SwField>(pRet.release()); +} + +/// set author +void SwPostItField::SetPar1(const OUString& rStr) +{ + m_sAuthor = rStr; +} + +/// get author +OUString SwPostItField::GetPar1() const +{ + return m_sAuthor; +} + +/// set the PostIt's text +void SwPostItField::SetPar2(const OUString& rStr) +{ + m_sText = rStr; +} + +/// get the PostIt's text +OUString SwPostItField::GetPar2() const +{ + return m_sText; +} + + +void SwPostItField::SetName(const OUString& rName) +{ + m_sName = rName; +} + + +void SwPostItField::SetTextObject( std::optional<OutlinerParaObject> pText ) +{ + mpText = std::move(pText); +} + +sal_Int32 SwPostItField::GetNumberOfParagraphs() const +{ + return mpText ? mpText->Count() : 1; +} + +void SwPostItField::SetPostItId(const sal_uInt32 nPostItId) +{ + m_nPostItId = nPostItId == 0 ? s_nLastPostItId++ : nPostItId; +} + +bool SwPostItField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= m_sAuthor; + break; + case FIELD_PROP_PAR2: + { + rAny <<= m_sText; + break; + } + case FIELD_PROP_PAR3: + rAny <<= m_sInitials; + break; + case FIELD_PROP_PAR4: + rAny <<= m_sName; + break; + case FIELD_PROP_BOOL1: + rAny <<= m_bResolved; + break; + case FIELD_PROP_TEXT: + { + if ( !m_xTextObject.is() ) + { + SwPostItFieldType* pGetType = static_cast<SwPostItFieldType*>(GetTyp()); + SwDoc& rDoc = pGetType->GetDoc(); + auto pObj = std::make_unique<SwTextAPIEditSource>( &rDoc ); + const_cast <SwPostItField*> (this)->m_xTextObject = new SwTextAPIObject( std::move(pObj) ); + } + + if ( mpText ) + m_xTextObject->SetText( *mpText ); + else + m_xTextObject->SetString( m_sText ); + + uno::Reference < text::XText > xText( m_xTextObject ); + rAny <<= xText; + break; + } + case FIELD_PROP_DATE: + { + rAny <<= m_aDateTime.GetUNODate(); + } + break; + case FIELD_PROP_DATE_TIME: + { + rAny <<= m_aDateTime.GetUNODateTime(); + } + break; + default: + assert(false); + } + return true; +} + +bool SwPostItField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny >>= m_sAuthor; + break; + case FIELD_PROP_PAR2: + rAny >>= m_sText; + //#i100374# new string via api, delete complex text object so SwPostItNote picks up the new string + mpText.reset(); + break; + case FIELD_PROP_PAR3: + rAny >>= m_sInitials; + break; + case FIELD_PROP_PAR4: + rAny >>= m_sName; + break; + case FIELD_PROP_BOOL1: + rAny >>= m_bResolved; + break; + case FIELD_PROP_TEXT: + OSL_FAIL("Not implemented!"); + break; + case FIELD_PROP_DATE: + if( auto aSetDate = o3tl::tryAccess<util::Date>(rAny) ) + { + m_aDateTime = Date(aSetDate->Day, aSetDate->Month, aSetDate->Year); + } + break; + case FIELD_PROP_DATE_TIME: + { + util::DateTime aDateTimeValue; + if(!(rAny >>= aDateTimeValue)) + return false; + m_aDateTime = DateTime(aDateTimeValue); + } + break; + default: + assert(false); + } + return true; +} + +void SwPostItField::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPostItField")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr())); + + SwField::dumpAsXml(pWriter); + + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mpText")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", mpText ? &*mpText : nullptr); + if (mpText) + mpText->dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); + + (void)xmlTextWriterEndElement(pWriter); +} + +// extended user information field type + +SwExtUserFieldType::SwExtUserFieldType() + : SwFieldType( SwFieldIds::ExtUser ) +{ +} + +std::unique_ptr<SwFieldType> SwExtUserFieldType::Copy() const +{ + return std::make_unique<SwExtUserFieldType>(); +} + +OUString SwExtUserFieldType::Expand(sal_uInt16 nSub ) +{ + UserOptToken nRet = static_cast<UserOptToken>(USHRT_MAX); + switch(nSub) + { + case EU_FIRSTNAME: nRet = UserOptToken::FirstName; break; + case EU_NAME: nRet = UserOptToken::LastName; break; + case EU_SHORTCUT: nRet = UserOptToken::ID; break; + + case EU_COMPANY: nRet = UserOptToken::Company; break; + case EU_STREET: nRet = UserOptToken::Street; break; + case EU_TITLE: nRet = UserOptToken::Title; break; + case EU_POSITION: nRet = UserOptToken::Position; break; + case EU_PHONE_PRIVATE: nRet = UserOptToken::TelephoneHome; break; + case EU_PHONE_COMPANY: nRet = UserOptToken::TelephoneWork; break; + case EU_FAX: nRet = UserOptToken::Fax; break; + case EU_EMAIL: nRet = UserOptToken::Email; break; + case EU_COUNTRY: nRet = UserOptToken::Country; break; + case EU_ZIP: nRet = UserOptToken::Zip; break; + case EU_CITY: nRet = UserOptToken::City; break; + case EU_STATE: nRet = UserOptToken::State; break; + case EU_FATHERSNAME: nRet = UserOptToken::FathersName; break; + case EU_APARTMENT: nRet = UserOptToken::Apartment; break; + default: OSL_ENSURE( false, "Field unknown"); + } + if( static_cast<UserOptToken>(USHRT_MAX) != nRet ) + { + SvtUserOptions& rUserOpt = SW_MOD()->GetUserOptions(); + return rUserOpt.GetToken( nRet ); + } + return OUString(); +} + +// extended user information field + +SwExtUserField::SwExtUserField(SwExtUserFieldType* pTyp, sal_uInt16 nSubTyp, sal_uInt32 nFormat) : + SwField(pTyp, nFormat), m_nType(nSubTyp) +{ + m_aContent = SwExtUserFieldType::Expand(m_nType); +} + +OUString SwExtUserField::ExpandImpl(SwRootFrame const*const) const +{ + if (!IsFixed()) + const_cast<SwExtUserField*>(this)->m_aContent = SwExtUserFieldType::Expand(m_nType); + + return m_aContent; +} + +std::unique_ptr<SwField> SwExtUserField::Copy() const +{ + std::unique_ptr<SwExtUserField> pField(new SwExtUserField(static_cast<SwExtUserFieldType*>(GetTyp()), m_nType, GetFormat())); + pField->SetExpansion(m_aContent); + + return std::unique_ptr<SwField>(pField.release()); +} + +sal_uInt16 SwExtUserField::GetSubType() const +{ + return m_nType; +} + +void SwExtUserField::SetSubType(sal_uInt16 nSub) +{ + m_nType = nSub; +} + +bool SwExtUserField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= m_aContent; + break; + + case FIELD_PROP_USHORT1: + { + sal_Int16 nTmp = m_nType; + rAny <<= nTmp; + } + break; + case FIELD_PROP_BOOL1: + rAny <<= IsFixed(); + break; + default: + assert(false); + } + return true; +} + +bool SwExtUserField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny >>= m_aContent; + break; + + case FIELD_PROP_USHORT1: + { + sal_Int16 nTmp = 0; + rAny >>= nTmp; + m_nType = nTmp; + } + break; + case FIELD_PROP_BOOL1: + if( *o3tl::doAccess<bool>(rAny) ) + SetFormat(GetFormat() | AF_FIXED); + else + SetFormat(GetFormat() & ~AF_FIXED); + break; + default: + assert(false); + } + return true; +} + +// field type for relative page numbers + +SwRefPageSetFieldType::SwRefPageSetFieldType() + : SwFieldType( SwFieldIds::RefPageSet ) +{ +} + +std::unique_ptr<SwFieldType> SwRefPageSetFieldType::Copy() const +{ + return std::make_unique<SwRefPageSetFieldType>(); +} + +// overridden since there is nothing to update +void SwRefPageSetFieldType::SwClientNotify(const SwModify&, const SfxHint&) +{ +} + +// field for relative page numbers + +SwRefPageSetField::SwRefPageSetField( SwRefPageSetFieldType* pTyp, + short nOff, bool bFlag ) + : SwField( pTyp ), m_nOffset( nOff ), m_bOn( bFlag ) +{ +} + +OUString SwRefPageSetField::ExpandImpl(SwRootFrame const*const) const +{ + return OUString(); +} + +std::unique_ptr<SwField> SwRefPageSetField::Copy() const +{ + return std::make_unique<SwRefPageSetField>( static_cast<SwRefPageSetFieldType*>(GetTyp()), m_nOffset, m_bOn ); +} + +OUString SwRefPageSetField::GetPar2() const +{ + return OUString::number(GetOffset()); +} + +void SwRefPageSetField::SetPar2(const OUString& rStr) +{ + SetOffset( static_cast<short>(rStr.toInt32()) ); +} + +bool SwRefPageSetField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL1: + rAny <<= m_bOn; + break; + case FIELD_PROP_USHORT1: + rAny <<= static_cast<sal_Int16>(m_nOffset); + break; + default: + assert(false); + } + return true; +} + +bool SwRefPageSetField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL1: + m_bOn = *o3tl::doAccess<bool>(rAny); + break; + case FIELD_PROP_USHORT1: + rAny >>=m_nOffset; + break; + default: + assert(false); + } + return true; +} + +// relative page numbers - query field + +SwRefPageGetFieldType::SwRefPageGetFieldType( SwDoc& rDc ) + : SwFieldType( SwFieldIds::RefPageGet ), m_rDoc( rDc ), m_nNumberingType( SVX_NUM_ARABIC ) +{ +} + +std::unique_ptr<SwFieldType> SwRefPageGetFieldType::Copy() const +{ + std::unique_ptr<SwRefPageGetFieldType> pNew(new SwRefPageGetFieldType( m_rDoc )); + pNew->m_nNumberingType = m_nNumberingType; + return pNew; +} + +void SwRefPageGetFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint) +{ + if (rHint.GetId() != SfxHintId::SwLegacyModify) + return; + auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint); + auto const ModifyImpl = [this](SwRootFrame const*const pLayout) + { + // first collect all SetPageRefFields + SetGetExpFields aTmpLst; + if (MakeSetList(aTmpLst, pLayout)) + { + std::vector<SwFormatField*> vFields; + GatherFields(vFields); + for(auto pFormatField: vFields) + UpdateField(pFormatField->GetTextField(), aTmpLst, pLayout); + } + }; + + // update all GetReference fields + if( !pLegacy->m_pNew && !pLegacy->m_pOld && HasWriterListeners() ) + { + SwRootFrame const* pLayout(nullptr); + SwRootFrame const* pLayoutRLHidden(nullptr); + for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts()) + { + if (pLay->IsHideRedlines()) + { + pLayoutRLHidden = pLay; + } + else + { + pLayout = pLay; + } + } + ModifyImpl(pLayout); + if (pLayoutRLHidden) + { + ModifyImpl(pLayoutRLHidden); + } + } + + // forward to text fields, they "expand" the text + CallSwClientNotify(rHint); +} + +bool SwRefPageGetFieldType::MakeSetList(SetGetExpFields& rTmpLst, + SwRootFrame const*const pLayout) +{ + IDocumentRedlineAccess const& rIDRA(m_rDoc.getIDocumentRedlineAccess()); + std::vector<SwFormatField*> vFields; + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::RefPageSet)->GatherFields(vFields); + for(auto pFormatField: vFields) + { + // update only the GetRef fields + const SwTextField* pTField = pFormatField->GetTextField(); + if (!pLayout || !pLayout->IsHideRedlines() || !sw::IsFieldDeletedInModel(rIDRA, *pTField)) + { + const SwTextNode& rTextNd = pTField->GetTextNode(); + + // Always the first! (in Tab-Headline, header/footer ) + Point aPt; + std::pair<Point, bool> const tmp(aPt, false); + const SwContentFrame *const pFrame = rTextNd.getLayoutFrame( + pLayout, nullptr, &tmp); + + std::unique_ptr<SetGetExpField> pNew; + + if( !pFrame || + pFrame->IsInDocBody() || + // #i31868# + // Check if pFrame is not yet connected to the layout. + !pFrame->FindPageFrame() ) + { + // create index for determination of the TextNode + SwNodeIndex aIdx( rTextNd ); + pNew.reset( new SetGetExpField( aIdx, pTField ) ); + } + else + { + // create index for determination of the TextNode + SwPosition aPos( m_rDoc.GetNodes().GetEndOfPostIts() ); + bool const bResult = GetBodyTextNode( m_rDoc, aPos, *pFrame ); + OSL_ENSURE(bResult, "where is the Field?"); + pNew.reset( new SetGetExpField( aPos.nNode, pTField, + &aPos.nContent ) ); + } + + rTmpLst.insert( std::move(pNew) ); + } + } + return !rTmpLst.empty(); +} + +void SwRefPageGetFieldType::UpdateField( SwTextField const * pTextField, + SetGetExpFields const & rSetList, + SwRootFrame const*const pLayout) +{ + SwRefPageGetField* pGetField = const_cast<SwRefPageGetField*>(static_cast<const SwRefPageGetField*>(pTextField->GetFormatField().GetField())); + pGetField->SetText( OUString(), pLayout ); + + // then search the correct RefPageSet field + SwTextNode* pTextNode = &pTextField->GetTextNode(); + if( pTextNode->StartOfSectionIndex() > + m_rDoc.GetNodes().GetEndOfExtras().GetIndex() ) + { + SwNodeIndex aIdx( *pTextNode ); + SetGetExpField aEndField( aIdx, pTextField ); + + SetGetExpFields::const_iterator itLast = rSetList.lower_bound( &aEndField ); + + if( itLast != rSetList.begin() ) + { + --itLast; + const SwTextField* pRefTextField = (*itLast)->GetTextField(); + const SwRefPageSetField* pSetField = + static_cast<const SwRefPageSetField*>(pRefTextField->GetFormatField().GetField()); + if( pSetField->IsOn() ) + { + // determine the correct offset + Point aPt; + std::pair<Point, bool> const tmp(aPt, false); + const SwContentFrame *const pFrame = pTextNode->getLayoutFrame( + pLayout, nullptr, &tmp); + const SwContentFrame *const pRefFrame = pRefTextField->GetTextNode().getLayoutFrame( + pLayout, nullptr, &tmp); + const SwPageFrame* pPgFrame = nullptr; + short nDiff = 1; + if ( pFrame && pRefFrame ) + { + pPgFrame = pFrame->FindPageFrame(); + nDiff = pPgFrame->GetPhyPageNum() - + pRefFrame->FindPageFrame()->GetPhyPageNum() + 1; + } + + SvxNumType nTmpFormat = SVX_NUM_PAGEDESC == static_cast<SvxNumType>(pGetField->GetFormat()) + ? ( !pPgFrame + ? SVX_NUM_ARABIC + : pPgFrame->GetPageDesc()->GetNumType().GetNumberingType() ) + : static_cast<SvxNumType>(pGetField->GetFormat()); + const short nPageNum = std::max<short>(0, pSetField->GetOffset() + nDiff); + pGetField->SetText(FormatNumber(nPageNum, nTmpFormat), pLayout); + } + } + } + // start formatting + const_cast<SwFormatField&>(pTextField->GetFormatField()).UpdateTextNode(nullptr, nullptr); +} + +// queries for relative page numbering + +SwRefPageGetField::SwRefPageGetField( SwRefPageGetFieldType* pTyp, + sal_uInt32 nFormat ) + : SwField( pTyp, nFormat ) +{ +} + +void SwRefPageGetField::SetText(const OUString& rText, + SwRootFrame const*const pLayout) +{ + if (!pLayout || !pLayout->IsHideRedlines()) + { + m_sText = rText; + } + if (!pLayout || pLayout->IsHideRedlines()) + { + m_sTextRLHidden = rText; + } +} + +OUString SwRefPageGetField::ExpandImpl(SwRootFrame const*const pLayout) const +{ + return pLayout && pLayout->IsHideRedlines() ? m_sTextRLHidden : m_sText; +} + +std::unique_ptr<SwField> SwRefPageGetField::Copy() const +{ + std::unique_ptr<SwRefPageGetField> pCpy(new SwRefPageGetField( + static_cast<SwRefPageGetFieldType*>(GetTyp()), GetFormat() )); + pCpy->m_sText = m_sText; + pCpy->m_sTextRLHidden = m_sTextRLHidden; + return std::unique_ptr<SwField>(pCpy.release()); +} + +void SwRefPageGetField::ChangeExpansion(const SwFrame& rFrame, + const SwTextField* pField ) +{ + // only fields in Footer, Header, FootNote, Flys + SwRefPageGetFieldType* pGetType = static_cast<SwRefPageGetFieldType*>(GetTyp()); + SwDoc& rDoc = pGetType->GetDoc(); + if( pField->GetTextNode().StartOfSectionIndex() > + rDoc.GetNodes().GetEndOfExtras().GetIndex() ) + return; + + SwRootFrame const& rLayout(*rFrame.getRootFrame()); + OUString & rText(rLayout.IsHideRedlines() ? m_sTextRLHidden : m_sText); + rText.clear(); + + OSL_ENSURE(!rFrame.IsInDocBody(), "Flag incorrect, frame is in DocBody"); + + // collect all SetPageRefFields + SetGetExpFields aTmpLst; + if (!pGetType->MakeSetList(aTmpLst, &rLayout)) + return ; + + // create index for determination of the TextNode + SwPosition aPos( SwNodeIndex( rDoc.GetNodes() ) ); + SwTextNode* pTextNode = const_cast<SwTextNode*>(GetBodyTextNode(rDoc, aPos, rFrame)); + + // If no layout exists, ChangeExpansion is called for header and + // footer lines via layout formatting without existing TextNode. + if(!pTextNode) + return; + + SetGetExpField aEndField( aPos.nNode, pField, &aPos.nContent ); + + SetGetExpFields::const_iterator itLast = aTmpLst.lower_bound( &aEndField ); + + if( itLast == aTmpLst.begin() ) + return; // there is no corresponding set-field in front + --itLast; + + const SwTextField* pRefTextField = (*itLast)->GetTextField(); + const SwRefPageSetField* pSetField = + static_cast<const SwRefPageSetField*>(pRefTextField->GetFormatField().GetField()); + Point aPt; + std::pair<Point, bool> const tmp(aPt, false); + const SwContentFrame *const pRefFrame = pRefTextField->GetTextNode().getLayoutFrame( + &rLayout, nullptr, &tmp); + if( !(pSetField->IsOn() && pRefFrame) ) + return; + + // determine the correct offset + const SwPageFrame* pPgFrame = rFrame.FindPageFrame(); + const short nDiff = pPgFrame->GetPhyPageNum() - + pRefFrame->FindPageFrame()->GetPhyPageNum() + 1; + + SwRefPageGetField* pGetField = const_cast<SwRefPageGetField*>(static_cast<const SwRefPageGetField*>(pField->GetFormatField().GetField())); + SvxNumType nTmpFormat = SVX_NUM_PAGEDESC == pGetField->GetFormat() + ? pPgFrame->GetPageDesc()->GetNumType().GetNumberingType() + : static_cast<SvxNumType>(pGetField->GetFormat()); + const short nPageNum = std::max<short>(0, pSetField->GetOffset() + nDiff); + pGetField->SetText(FormatNumber(nPageNum, nTmpFormat), &rLayout); +} + +bool SwRefPageGetField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_USHORT1: + rAny <<= static_cast<sal_Int16>(GetFormat()); + break; + case FIELD_PROP_PAR1: + rAny <<= m_sText; + break; + default: + assert(false); + } + return true; +} + +bool SwRefPageGetField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_USHORT1: + { + sal_Int16 nSet = 0; + rAny >>= nSet; + if(nSet <= SVX_NUM_PAGEDESC ) + SetFormat(nSet); + } + break; + case FIELD_PROP_PAR1: + rAny >>= m_sText; + m_sTextRLHidden = m_sText; + break; + default: + assert(false); + } + return true; +} + +// field type to jump to and edit + +SwJumpEditFieldType::SwJumpEditFieldType( SwDoc& rD ) + : SwFieldType( SwFieldIds::JumpEdit ), m_rDoc( rD ), m_aDep( *this ) +{ +} + +std::unique_ptr<SwFieldType> SwJumpEditFieldType::Copy() const +{ + return std::make_unique<SwJumpEditFieldType>( m_rDoc ); +} + +SwCharFormat* SwJumpEditFieldType::GetCharFormat() +{ + SwCharFormat* pFormat = m_rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_JUMPEDIT ); + m_aDep.StartListening(pFormat); + return pFormat; +} + +SwJumpEditField::SwJumpEditField( SwJumpEditFieldType* pTyp, sal_uInt32 nForm, + const OUString& rText, const OUString& rHelp ) + : SwField( pTyp, nForm ), m_sText( rText ), m_sHelp( rHelp ) +{ +} + +OUString SwJumpEditField::ExpandImpl(SwRootFrame const*const) const +{ + return "<" + m_sText + ">"; +} + +std::unique_ptr<SwField> SwJumpEditField::Copy() const +{ + return std::make_unique<SwJumpEditField>( static_cast<SwJumpEditFieldType*>(GetTyp()), GetFormat(), + m_sText, m_sHelp ); +} + +/// get place holder text +OUString SwJumpEditField::GetPar1() const +{ + return m_sText; +} + +/// set place holder text +void SwJumpEditField::SetPar1(const OUString& rStr) +{ + m_sText = rStr; +} + +/// get hint text +OUString SwJumpEditField::GetPar2() const +{ + return m_sHelp; +} + +/// set hint text +void SwJumpEditField::SetPar2(const OUString& rStr) +{ + m_sHelp = rStr; +} + +bool SwJumpEditField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_USHORT1: + { + sal_Int16 nRet; + switch( GetFormat() ) + { + case JE_FMT_TABLE: nRet = text::PlaceholderType::TABLE; break; + case JE_FMT_FRAME: nRet = text::PlaceholderType::TEXTFRAME; break; + case JE_FMT_GRAPHIC:nRet = text::PlaceholderType::GRAPHIC; break; + case JE_FMT_OLE: nRet = text::PlaceholderType::OBJECT; break; + default: + nRet = text::PlaceholderType::TEXT; break; + } + rAny <<= nRet; + } + break; + case FIELD_PROP_PAR1 : + rAny <<= m_sHelp; + break; + case FIELD_PROP_PAR2 : + rAny <<= m_sText; + break; + default: + assert(false); + } + return true; +} + +bool SwJumpEditField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_USHORT1: + { + //JP 24.10.2001: int32 because in UnoField.cxx a putvalue is + // called with a int32 value! But normally we need + // here only a int16 + sal_Int32 nSet = 0; + rAny >>= nSet; + switch( nSet ) + { + case text::PlaceholderType::TEXT : SetFormat(JE_FMT_TEXT); break; + case text::PlaceholderType::TABLE : SetFormat(JE_FMT_TABLE); break; + case text::PlaceholderType::TEXTFRAME: SetFormat(JE_FMT_FRAME); break; + case text::PlaceholderType::GRAPHIC : SetFormat(JE_FMT_GRAPHIC); break; + case text::PlaceholderType::OBJECT : SetFormat(JE_FMT_OLE); break; + } + } + break; + case FIELD_PROP_PAR1 : + rAny >>= m_sHelp; + break; + case FIELD_PROP_PAR2 : + rAny >>= m_sText; + break; + default: + assert(false); + } + return true; +} + +// combined character field type + +SwCombinedCharFieldType::SwCombinedCharFieldType() + : SwFieldType( SwFieldIds::CombinedChars ) +{ +} + +std::unique_ptr<SwFieldType> SwCombinedCharFieldType::Copy() const +{ + return std::make_unique<SwCombinedCharFieldType>(); +} + +// combined character field + +SwCombinedCharField::SwCombinedCharField( SwCombinedCharFieldType* pFTyp, + const OUString& rChars ) + : SwField( pFTyp, 0 ), + m_sCharacters( rChars.copy( 0, std::min<sal_Int32>(rChars.getLength(), MAX_COMBINED_CHARACTERS) )) +{ +} + +OUString SwCombinedCharField::ExpandImpl(SwRootFrame const*const) const +{ + return m_sCharacters; +} + +std::unique_ptr<SwField> SwCombinedCharField::Copy() const +{ + return std::make_unique<SwCombinedCharField>( static_cast<SwCombinedCharFieldType*>(GetTyp()), + m_sCharacters ); +} + +OUString SwCombinedCharField::GetPar1() const +{ + return m_sCharacters; +} + +void SwCombinedCharField::SetPar1(const OUString& rStr) +{ + m_sCharacters = rStr.copy(0, std::min<sal_Int32>(rStr.getLength(), MAX_COMBINED_CHARACTERS)); +} + +bool SwCombinedCharField::QueryValue( uno::Any& rAny, + sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= m_sCharacters; + break; + default: + assert(false); + } + return true; +} + +bool SwCombinedCharField::PutValue( const uno::Any& rAny, + sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + { + OUString sTmp; + rAny >>= sTmp; + SetPar1(sTmp); + } + break; + default: + assert(false); + } + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/expfld.cxx b/sw/source/core/fields/expfld.cxx new file mode 100644 index 000000000..e6d810506 --- /dev/null +++ b/sw/source/core/fields/expfld.cxx @@ -0,0 +1,1445 @@ +/* -*- 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 <sal/config.h> + +#include <limits> + +#include <UndoTable.hxx> +#include <hintids.hxx> +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <unotools/collatorwrapper.hxx> +#include <unotools/charclass.hxx> +#include <editeng/langitem.hxx> +#include <editeng/fontitem.hxx> +#include <com/sun/star/text/SetVariableType.hpp> +#include <unofield.hxx> +#include <frmfmt.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <fmtanchr.hxx> +#include <txtftn.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <layfrm.hxx> +#include <pagefrm.hxx> +#include <cntfrm.hxx> +#include <txtfrm.hxx> +#include <rootfrm.hxx> +#include <tabfrm.hxx> +#include <flyfrm.hxx> +#include <ftnfrm.hxx> +#include <rowfrm.hxx> +#include <expfld.hxx> +#include <usrfld.hxx> +#include <ndtxt.hxx> +#include <calc.hxx> +#include <pam.hxx> +#include <docfld.hxx> +#include <swtable.hxx> +#include <breakit.hxx> +#include <SwStyleNameMapper.hxx> +#include <unofldmid.h> +#include <numrule.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; + +static sal_Int16 lcl_SubTypeToAPI(sal_uInt16 nSubType) +{ + sal_Int16 nRet = 0; + switch(nSubType) + { + case nsSwGetSetExpType::GSE_EXPR: + nRet = SetVariableType::VAR; // 0 + break; + case nsSwGetSetExpType::GSE_SEQ: + nRet = SetVariableType::SEQUENCE; // 1 + break; + case nsSwGetSetExpType::GSE_FORMULA: + nRet = SetVariableType::FORMULA; // 2 + break; + case nsSwGetSetExpType::GSE_STRING: + nRet = SetVariableType::STRING; // 3 + break; + } + return nRet; +} + +static sal_Int32 lcl_APIToSubType(const uno::Any& rAny) +{ + sal_Int16 nVal = 0; + rAny >>= nVal; + sal_Int32 nSet = 0; + switch(nVal) + { + case SetVariableType::VAR: nSet = nsSwGetSetExpType::GSE_EXPR; break; + case SetVariableType::SEQUENCE: nSet = nsSwGetSetExpType::GSE_SEQ; break; + case SetVariableType::FORMULA: nSet = nsSwGetSetExpType::GSE_FORMULA; break; + case SetVariableType::STRING: nSet = nsSwGetSetExpType::GSE_STRING; break; + default: + OSL_FAIL("wrong value"); + nSet = -1; + } + return nSet; +} + +OUString ReplacePoint( const OUString& rTmpName, bool bWithCommandType ) +{ + // replace first and last (if bWithCommandType: last two) dot + // since table names may contain dots + + sal_Int32 nIndex = rTmpName.lastIndexOf('.'); + if (nIndex<0) + { + return rTmpName; + } + + OUString sRes = rTmpName.replaceAt(nIndex, 1, rtl::OUStringChar(DB_DELIM)); + + if (bWithCommandType) + { + nIndex = sRes.lastIndexOf('.', nIndex); + if (nIndex<0) + { + return sRes; + } + sRes = sRes.replaceAt(nIndex, 1, rtl::OUStringChar(DB_DELIM)); + } + + nIndex = sRes.indexOf('.'); + if (nIndex>=0) + { + sRes = sRes.replaceAt(nIndex, 1, rtl::OUStringChar(DB_DELIM)); + } + return sRes; +} + +static SwTextNode* GetFirstTextNode( const SwDoc& rDoc, SwPosition& rPos, + const SwContentFrame *pCFrame, Point &rPt ) +{ + SwTextNode* pTextNode = nullptr; + if ( !pCFrame ) + { + const SwNodes& rNodes = rDoc.GetNodes(); + rPos.nNode = *rNodes.GetEndOfContent().StartOfSectionNode(); + SwContentNode* pCNd; + while( nullptr != (pCNd = rNodes.GoNext( &rPos.nNode ) ) && + nullptr == ( pTextNode = pCNd->GetTextNode() ) ) + ; + OSL_ENSURE( pTextNode, "Where is the 1. TextNode?" ); + rPos.nContent.Assign( pTextNode, 0 ); + } + else if ( !pCFrame->isFrameAreaDefinitionValid() ) + { + assert(pCFrame->IsTextFrame()); + rPos = static_cast<SwTextFrame const*>(pCFrame)->MapViewToModelPos(TextFrameIndex(0)); + } + else + { + pCFrame->GetModelPositionForViewPoint( &rPos, rPt ); + pTextNode = rPos.nNode.GetNode().GetTextNode(); + } + return pTextNode; +} + +const SwTextNode* GetBodyTextNode( const SwDoc& rDoc, SwPosition& rPos, + const SwFrame& rFrame ) +{ + const SwLayoutFrame* pLayout = rFrame.GetUpper(); + const SwTextNode* pTextNode = nullptr; + + while( pLayout ) + { + if( pLayout->IsFlyFrame() ) + { + // get the FlyFormat + const SwFrameFormat* pFlyFormat = static_cast<const SwFlyFrame*>(pLayout)->GetFormat(); + OSL_ENSURE( pFlyFormat, "Could not find FlyFormat, where is the field?" ); + + const SwFormatAnchor &rAnchor = pFlyFormat->GetAnchor(); + + if( RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId() ) + { + // the fly needs to be attached somewhere, so ask it + pLayout = static_cast<const SwLayoutFrame*>(static_cast<const SwFlyFrame*>(pLayout)->GetAnchorFrame()); + continue; + } + else if ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId())) + { + OSL_ENSURE( rAnchor.GetContentAnchor(), "no valid position" ); + rPos = *rAnchor.GetContentAnchor(); + pTextNode = rPos.nNode.GetNode().GetTextNode(); + if ( RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId() ) + { + const_cast<SwTextNode*>(pTextNode)->MakeStartIndex( + &rPos.nContent ); + } + + // do not break yet, might be as well in Header/Footer/Footnote/Fly + pLayout = static_cast<const SwFlyFrame*>(pLayout)->GetAnchorFrame() + ? static_cast<const SwFlyFrame*>(pLayout)->GetAnchorFrame()->GetUpper() : nullptr; + continue; + } + else + { + pLayout->FindPageFrame()->GetContentPosition( + pLayout->getFrameArea().Pos(), rPos ); + pTextNode = rPos.nNode.GetNode().GetTextNode(); + } + } + else if( pLayout->IsFootnoteFrame() ) + { + // get the anchor's node + const SwTextFootnote* pFootnote = static_cast<const SwFootnoteFrame*>(pLayout)->GetAttr(); + pTextNode = &pFootnote->GetTextNode(); + rPos.nNode = *pTextNode; + rPos.nContent = pFootnote->GetStart(); + } + else if( pLayout->IsHeaderFrame() || pLayout->IsFooterFrame() ) + { + const SwContentFrame* pContentFrame; + const SwPageFrame* pPgFrame = pLayout->FindPageFrame(); + if( pLayout->IsHeaderFrame() ) + { + const SwTabFrame *pTab; + if( nullptr != ( pContentFrame = pPgFrame->FindFirstBodyContent()) && + nullptr != (pTab = pContentFrame->FindTabFrame()) && pTab->IsFollow() && + pTab->GetTable()->GetRowsToRepeat() > 0 && + pTab->IsInHeadline( *pContentFrame ) ) + { + // take the next line + const SwLayoutFrame* pRow = pTab->GetFirstNonHeadlineRow(); + pContentFrame = pRow->ContainsContent(); + } + } + else + pContentFrame = pPgFrame->FindLastBodyContent(); + + if( pContentFrame ) + { + assert(pContentFrame->IsTextFrame()); + SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pContentFrame)); + rPos = pFrame->MapViewToModelPos(TextFrameIndex(pFrame->GetText().getLength())); + pTextNode = rPos.nNode.GetNode().GetTextNode(); + assert(pTextNode); + } + else + { + Point aPt( pLayout->getFrameArea().Pos() ); + aPt.AdjustY( 1 ); // get out of the header + pContentFrame = pPgFrame->GetContentPos( aPt, false, true ); + pTextNode = GetFirstTextNode( rDoc, rPos, pContentFrame, aPt ); + } + } + else + { + pLayout = pLayout->GetUpper(); + continue; + } + break; // found, so finish loop + } + return pTextNode; +} + +SwGetExpFieldType::SwGetExpFieldType(SwDoc* pDc) + : SwValueFieldType( pDc, SwFieldIds::GetExp ) +{ +} + +std::unique_ptr<SwFieldType> SwGetExpFieldType::Copy() const +{ + return std::make_unique<SwGetExpFieldType>(GetDoc()); +} + +void SwGetExpFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint) +{ + if (rHint.GetId() != SfxHintId::SwLegacyModify) + return; + auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint); + // do not expand anything else + if(pLegacy->GetWhich() != RES_DOCPOS_UPDATE) + return; + CallSwClientNotify(rHint); +} + +SwGetExpField::SwGetExpField(SwGetExpFieldType* pTyp, const OUString& rFormel, + sal_uInt16 nSub, sal_uLong nFormat) + : SwFormulaField( pTyp, nFormat, 0.0 ) + , m_fValueRLHidden(0.0) + , + m_bIsInBodyText( true ), + m_nSubType(nSub), + m_bLateInitialization( false ) +{ + SetFormula( rFormel ); +} + +void SwGetExpField::ChgExpStr(const OUString& rExpand, SwRootFrame const*const pLayout) +{ + if (!pLayout || pLayout->IsHideRedlines()) + { + m_sExpandRLHidden = rExpand; + } + if (!pLayout || !pLayout->IsHideRedlines()) + { + m_sExpand = rExpand; + } +} + +OUString SwGetExpField::ExpandImpl(SwRootFrame const*const pLayout) const +{ + if(m_nSubType & nsSwExtendedSubType::SUB_CMD) + return GetFormula(); + + return (pLayout && pLayout->IsHideRedlines()) ? m_sExpandRLHidden : m_sExpand; +} + +OUString SwGetExpField::GetFieldName() const +{ + const SwFieldTypesEnum nType = + (nsSwGetSetExpType::GSE_FORMULA & m_nSubType) + ? SwFieldTypesEnum::Formel + : SwFieldTypesEnum::Get; + + return SwFieldType::GetTypeStr(nType) + " " + GetFormula(); +} + +std::unique_ptr<SwField> SwGetExpField::Copy() const +{ + std::unique_ptr<SwGetExpField> pTmp(new SwGetExpField(static_cast<SwGetExpFieldType*>(GetTyp()), + GetFormula(), m_nSubType, GetFormat())); + pTmp->SetLanguage(GetLanguage()); + pTmp->m_fValueRLHidden = m_fValueRLHidden; + pTmp->SwValueField::SetValue(GetValue()); + pTmp->m_sExpand = m_sExpand; + pTmp->m_sExpandRLHidden = m_sExpandRLHidden; + pTmp->m_bIsInBodyText = m_bIsInBodyText; + pTmp->SetAutomaticLanguage(IsAutomaticLanguage()); + if( m_bLateInitialization ) + pTmp->SetLateInitialization(); + + return std::unique_ptr<SwField>(pTmp.release()); +} + +void SwGetExpField::ChangeExpansion( const SwFrame& rFrame, const SwTextField& rField ) +{ + if( m_bIsInBodyText ) // only fields in Footer, Header, FootNote, Flys + return; + + OSL_ENSURE( !rFrame.IsInDocBody(), "Flag incorrect, frame is in DocBody" ); + + // determine document (or is there an easier way?) + const SwTextNode* pTextNode = &rField.GetTextNode(); + SwDoc& rDoc = const_cast<SwDoc&>(pTextNode->GetDoc()); + + // create index for determination of the TextNode + SwPosition aPos( SwNodeIndex( rDoc.GetNodes() ) ); + pTextNode = GetBodyTextNode( rDoc, aPos, rFrame ); + + // If no layout exists, ChangeExpansion is called for header and + // footer lines via layout formatting without existing TextNode. + if(!pTextNode) + return; + // #i82544# + if( m_bLateInitialization ) + { + SwFieldType* pSetExpField = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, GetFormula(), false); + if( pSetExpField ) + { + m_bLateInitialization = false; + if( !(GetSubType() & nsSwGetSetExpType::GSE_STRING) && + static_cast< SwSetExpFieldType* >(pSetExpField)->GetType() == nsSwGetSetExpType::GSE_STRING ) + SetSubType( nsSwGetSetExpType::GSE_STRING ); + } + } + + SwRootFrame const& rLayout(*rFrame.getRootFrame()); + OUString & rExpand(rLayout.IsHideRedlines() ? m_sExpandRLHidden : m_sExpand); + // here a page number is needed to sort correctly + SetGetExpField aEndField(aPos.nNode, &rField, &aPos.nContent, rFrame.GetPhyPageNum()); + if(GetSubType() & nsSwGetSetExpType::GSE_STRING) + { + SwHashTable<HashStr> aHashTable(0); + rDoc.getIDocumentFieldsAccess().FieldsToExpand(aHashTable, aEndField, rLayout); + rExpand = LookString( aHashTable, GetFormula() ); + } + else + { + // fill calculator with values + SwCalc aCalc( rDoc ); + rDoc.getIDocumentFieldsAccess().FieldsToCalc(aCalc, aEndField, &rLayout); + + // calculate value + SetValue(aCalc.Calculate(GetFormula()).GetDouble(), &rLayout); + + // analyse based on format + rExpand = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue( + GetValue(&rLayout), GetFormat(), GetLanguage()); + } +} + +OUString SwGetExpField::GetPar2() const +{ + return GetFormula(); +} + +void SwGetExpField::SetPar2(const OUString& rStr) +{ + SetFormula(rStr); +} + +sal_uInt16 SwGetExpField::GetSubType() const +{ + return m_nSubType; +} + +void SwGetExpField::SetSubType(sal_uInt16 nType) +{ + m_nSubType = nType; +} + +void SwGetExpField::SetLanguage(LanguageType nLng) +{ + if (m_nSubType & nsSwExtendedSubType::SUB_CMD) + SwField::SetLanguage(nLng); + else + SwValueField::SetLanguage(nLng); +} + +bool SwGetExpField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_DOUBLE: + rAny <<= GetValue(); + break; + case FIELD_PROP_FORMAT: + rAny <<= static_cast<sal_Int32>(GetFormat()); + break; + case FIELD_PROP_USHORT1: + rAny <<= static_cast<sal_Int16>(m_nSubType); + break; + case FIELD_PROP_PAR1: + rAny <<= GetFormula(); + break; + case FIELD_PROP_SUBTYPE: + { + sal_Int16 nRet = lcl_SubTypeToAPI(GetSubType() & 0xff); + rAny <<= nRet; + } + break; + case FIELD_PROP_BOOL2: + rAny <<= 0 != (m_nSubType & nsSwExtendedSubType::SUB_CMD); + break; + case FIELD_PROP_PAR4: + rAny <<= m_sExpand; + break; + default: + return SwField::QueryValue(rAny, nWhichId); + } + return true; +} + +bool SwGetExpField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + sal_Int32 nTmp = 0; + switch( nWhichId ) + { + case FIELD_PROP_DOUBLE: + SwValueField::SetValue(*o3tl::doAccess<double>(rAny)); + m_fValueRLHidden = *o3tl::doAccess<double>(rAny); + break; + case FIELD_PROP_FORMAT: + rAny >>= nTmp; + SetFormat(nTmp); + break; + case FIELD_PROP_USHORT1: + rAny >>= nTmp; + m_nSubType = o3tl::narrowing<sal_uInt16>(nTmp); + break; + case FIELD_PROP_PAR1: + { + OUString sTmp; + rAny >>= sTmp; + SetFormula(sTmp); + break; + } + case FIELD_PROP_SUBTYPE: + nTmp = lcl_APIToSubType(rAny); + if( nTmp >=0 ) + SetSubType( o3tl::narrowing<sal_uInt16>((GetSubType() & 0xff00) | nTmp)); + break; + case FIELD_PROP_BOOL2: + if(*o3tl::doAccess<bool>(rAny)) + m_nSubType |= nsSwExtendedSubType::SUB_CMD; + else + m_nSubType &= (~nsSwExtendedSubType::SUB_CMD); + break; + case FIELD_PROP_PAR4: + { + OUString sTmp; + rAny >>= sTmp; + ChgExpStr(sTmp, nullptr); + break; + } + default: + return SwField::PutValue(rAny, nWhichId); + } + return true; +} + +SwSetExpFieldType::SwSetExpFieldType( SwDoc* pDc, const OUString& rName, sal_uInt16 nTyp ) + : SwValueFieldType( pDc, SwFieldIds::SetExp ), + m_sName( rName ), + m_sDelim( "." ), + m_nType(nTyp), m_nLevel( UCHAR_MAX ), + m_bDeleted( false ) +{ + if( ( nsSwGetSetExpType::GSE_SEQ | nsSwGetSetExpType::GSE_STRING ) & m_nType ) + EnableFormat(false); // do not use Numberformatter +} + +std::unique_ptr<SwFieldType> SwSetExpFieldType::Copy() const +{ + std::unique_ptr<SwSetExpFieldType> pNew(new SwSetExpFieldType(GetDoc(), m_sName, m_nType)); + pNew->m_bDeleted = m_bDeleted; + pNew->m_sDelim = m_sDelim; + pNew->m_nLevel = m_nLevel; + + return pNew; +} + +OUString SwSetExpFieldType::GetName() const +{ + return m_sName; +} + +const OUString& SwSetExpField::GetExpStr(SwRootFrame const*const pLayout) const +{ + return (pLayout && pLayout->IsHideRedlines()) ? msExpandRLHidden : msExpand; +} + +void SwSetExpField::ChgExpStr(const OUString& rExpand, SwRootFrame const*const pLayout) +{ + if (!pLayout || pLayout->IsHideRedlines()) + { + msExpandRLHidden = rExpand; + } + if (!pLayout || !pLayout->IsHideRedlines()) + { + msExpand = rExpand; + } +} + +void SwSetExpFieldType::SwClientNotify(const SwModify&, const SfxHint&) +{ + // do not expand further +} + +void SwSetExpFieldType::SetSeqFormat(sal_uLong nFormat) +{ + std::vector<SwFormatField*> vFields; + GatherFields(vFields, false); + for(auto pFormatField: vFields) + pFormatField->GetField()->ChangeFormat(nFormat); +} + +sal_uLong SwSetExpFieldType::GetSeqFormat() const +{ + if( !HasWriterListeners() ) + return SVX_NUM_ARABIC; + + std::vector<SwFormatField*> vFields; + GatherFields(vFields, false); + return vFields.front()->GetField()->GetFormat(); +} + +void SwSetExpFieldType::SetSeqRefNo( SwSetExpField& rField ) +{ + if( !HasWriterListeners() || !(nsSwGetSetExpType::GSE_SEQ & m_nType) ) + return; + + std::vector<sal_uInt16> aArr; + + // check if number is already used and if a new one needs to be created + std::vector<SwFormatField*> vFields; + GatherFields(vFields); + for(SwFormatField* pF: vFields) + if(pF->GetField() != &rField) + InsertSort(aArr, static_cast<SwSetExpField*>(pF->GetField())->GetSeqNumber()); + + // check first if number already exists + sal_uInt16 nNum = rField.GetSeqNumber(); + if( USHRT_MAX != nNum ) + { + std::vector<sal_uInt16>::size_type n {0}; + + for( n = 0; n < aArr.size(); ++n ) + if( aArr[ n ] >= nNum ) + break; + + if( n == aArr.size() || aArr[ n ] > nNum ) + return; // no -> use it + } + + // flagged all numbers, so determine the right number + std::vector<sal_uInt16>::size_type n = aArr.size(); + OSL_ENSURE( n <= std::numeric_limits<sal_uInt16>::max(), "Array is too big for using a sal_uInt16 index" ); + + if ( n > 0 && aArr[ n-1 ] != n-1 ) + { + for( n = 0; n < aArr.size(); ++n ) + if( n != aArr[ n ] ) + break; + } + + rField.SetSeqNumber( n ); +} + +size_t SwSetExpFieldType::GetSeqFieldList(SwSeqFieldList& rList, + SwRootFrame const*const pLayout) +{ + rList.Clear(); + + IDocumentRedlineAccess const& rIDRA(GetDoc()->getIDocumentRedlineAccess()); + + std::vector<SwFormatField*> vFields; + GatherFields(vFields); + for(SwFormatField* pF: vFields) + { + const SwTextNode* pNd; + if( nullptr != ( pNd = pF->GetTextField()->GetpTextNode() ) + && (!pLayout || !pLayout->IsHideRedlines() + || !sw::IsFieldDeletedInModel(rIDRA, *pF->GetTextField()))) + { + SeqFieldLstElem aNew( + pNd->GetExpandText(pLayout), + static_cast<SwSetExpField*>(pF->GetField())->GetSeqNumber() ); + rList.InsertSort( aNew ); + } + } + return rList.Count(); +} + +void SwSetExpFieldType::SetChapter(SwSetExpField& rField, const SwNode& rNd, + SwRootFrame const*const pLayout) +{ + const SwTextNode* pTextNd = rNd.FindOutlineNodeOfLevel(m_nLevel, pLayout); + if( !pTextNd ) + return; + + SwNumRule * pRule = pTextNd->GetNumRule(); + + if (!pRule) + return; + + // --> OD 2005-11-02 #i51089 - TUNING# + if (SwNodeNum const*const pNum = pTextNd->GetNum(pLayout)) + { + // only get the number, without pre-/post-fixstrings + OUString const sNumber(pRule->MakeNumString(*pNum, false)); + + if( !sNumber.isEmpty() ) + rField.ChgExpStr(sNumber + m_sDelim + rField.GetExpStr(pLayout), pLayout); + } + else + { + OSL_ENSURE(pTextNd->GetNum(nullptr), "<SwSetExpFieldType::SetChapter(..)> - text node with numbering rule, but without number. This is a serious defect"); + } +} + +void SwSetExpFieldType::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_SUBTYPE: + { + sal_Int16 nRet = lcl_SubTypeToAPI(GetType()); + rAny <<= nRet; + } + break; + case FIELD_PROP_PAR2: + rAny <<= GetDelimiter(); + break; + case FIELD_PROP_SHORT1: + { + sal_Int8 nRet = m_nLevel < MAXLEVEL? m_nLevel : -1; + rAny <<= nRet; + } + break; + default: + assert(false); + } +} + +void SwSetExpFieldType::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_SUBTYPE: + { + sal_Int32 nSet = lcl_APIToSubType(rAny); + if(nSet >=0) + SetType(o3tl::narrowing<sal_uInt16>(nSet)); + } + break; + case FIELD_PROP_PAR2: + { + OUString sTmp; + rAny >>= sTmp; + if( !sTmp.isEmpty() ) + SetDelimiter( sTmp ); + else + SetDelimiter( " " ); + } + break; + case FIELD_PROP_SHORT1: + { + sal_Int8 nLvl = 0; + rAny >>= nLvl; + if(nLvl < 0 || nLvl >= MAXLEVEL) + SetOutlineLvl(UCHAR_MAX); + else + SetOutlineLvl(nLvl); + } + break; + default: + assert(false); + } +} + +bool SwSeqFieldList::InsertSort( SeqFieldLstElem aNew ) +{ + OUStringBuffer aBuf(aNew.sDlgEntry); + const sal_Int32 nLen = aBuf.getLength(); + for (sal_Int32 i = 0; i < nLen; ++i) + { + if (aBuf[i]<' ') + { + aBuf[i]=' '; + } + } + aNew.sDlgEntry = aBuf.makeStringAndClear(); + + size_t nPos = 0; + bool bRet = SeekEntry( aNew, &nPos ); + if( !bRet ) + maData.insert( maData.begin() + nPos, aNew ); + return bRet; +} + +bool SwSeqFieldList::SeekEntry( const SeqFieldLstElem& rNew, size_t* pP ) const +{ + size_t nO = maData.size(); + size_t nU = 0; + if( nO > 0 ) + { + CollatorWrapper & rCaseColl = ::GetAppCaseCollator(), + & rColl = ::GetAppCollator(); + const CharClass& rCC = GetAppCharClass(); + + //#59900# Sorting should sort number correctly (e.g. "10" after "9" not after "1") + const OUString rTmp2 = rNew.sDlgEntry; + sal_Int32 nFndPos2 = 0; + const OUString sNum2( rTmp2.getToken( 0, ' ', nFndPos2 )); + bool bIsNum2IsNumeric = CharClass::isAsciiNumeric( sNum2 ); + sal_Int32 nNum2 = bIsNum2IsNumeric ? sNum2.toInt32() : 0; + + nO--; + while( nU <= nO ) + { + const size_t nM = nU + ( nO - nU ) / 2; + + //#59900# Sorting should sort number correctly (e.g. "10" after "9" not after "1") + const OUString rTmp1 = maData[nM].sDlgEntry; + sal_Int32 nFndPos1 = 0; + const OUString sNum1( rTmp1.getToken( 0, ' ', nFndPos1 )); + sal_Int32 nCmp; + + if( bIsNum2IsNumeric && rCC.isNumeric( sNum1 ) ) + { + sal_Int32 nNum1 = sNum1.toInt32(); + nCmp = nNum2 - nNum1; + if( 0 == nCmp ) + { + OUString aTmp1 = nFndPos1 != -1 ? rTmp1.copy(nFndPos1) : OUString(); + OUString aTmp2 = nFndPos2 != -1 ? rTmp2.copy(nFndPos2) : OUString(); + nCmp = rCaseColl.compareString(aTmp2, aTmp1); + } + } + else + nCmp = rColl.compareString( rTmp2, rTmp1 ); + + if( 0 == nCmp ) + { + if( pP ) *pP = nM; + return true; + } + else if( 0 < nCmp ) + nU = nM + 1; + else if( nM == 0 ) + break; + else + nO = nM - 1; + } + } + if( pP ) *pP = nU; + return false; +} + +SwSetExpField::SwSetExpField(SwSetExpFieldType* pTyp, const OUString& rFormel, + sal_uLong nFormat) + : SwFormulaField( pTyp, nFormat, 0.0 ) + , m_fValueRLHidden(0.0) + , mnSeqNo( USHRT_MAX ) + , mnSubType(0) + , mpFormatField(nullptr) +{ + SetFormula(rFormel); + // ignore SubType + mbInput = false; + if( IsSequenceField() ) + { + SwValueField::SetValue(1.0); + m_fValueRLHidden = 1.0; + if( rFormel.isEmpty() ) + { + SetFormula(pTyp->GetName() + "+1"); + } + } +} + +void SwSetExpField::SetFormatField(SwFormatField & rFormatField) +{ + mpFormatField = &rFormatField; +} + +OUString SwSetExpField::ExpandImpl(SwRootFrame const*const pLayout) const +{ + if (mnSubType & nsSwExtendedSubType::SUB_CMD) + { // we need the CommandString + return GetTyp()->GetName() + " = " + GetFormula(); + } + if(!(mnSubType & nsSwExtendedSubType::SUB_INVISIBLE)) + { // value is visible + return (pLayout && pLayout->IsHideRedlines()) ? msExpandRLHidden : msExpand; + } + return OUString(); +} + +/// @return the field name +OUString SwSetExpField::GetFieldName() const +{ + SwFieldTypesEnum const nStrType( (IsSequenceField()) + ? SwFieldTypesEnum::Sequence + : mbInput + ? SwFieldTypesEnum::SetInput + : SwFieldTypesEnum::Set ); + + OUString aStr( + SwFieldType::GetTypeStr( nStrType ) + + " " + + GetTyp()->GetName() ); + + // Sequence: without formula + if (SwFieldTypesEnum::Sequence != nStrType) + { + aStr += " = " + GetFormula(); + } + return aStr; +} + +std::unique_ptr<SwField> SwSetExpField::Copy() const +{ + std::unique_ptr<SwSetExpField> pTmp(new SwSetExpField(static_cast<SwSetExpFieldType*>(GetTyp()), + GetFormula(), GetFormat())); + pTmp->SwValueField::SetValue(GetValue()); + pTmp->m_fValueRLHidden = m_fValueRLHidden; + pTmp->msExpand = msExpand; + pTmp->msExpandRLHidden = msExpandRLHidden; + pTmp->SetAutomaticLanguage(IsAutomaticLanguage()); + pTmp->SetLanguage(GetLanguage()); + pTmp->maPText = maPText; + pTmp->mbInput = mbInput; + pTmp->mnSeqNo = mnSeqNo; + pTmp->SetSubType(GetSubType()); + + return std::unique_ptr<SwField>(pTmp.release()); +} + +void SwSetExpField::SetSubType(sal_uInt16 nSub) +{ + static_cast<SwSetExpFieldType*>(GetTyp())->SetType(nSub & 0xff); + mnSubType = nSub & 0xff00; + + OSL_ENSURE( (nSub & 0xff) != 3, "SubType is illegal!" ); +} + +sal_uInt16 SwSetExpField::GetSubType() const +{ + return static_cast<SwSetExpFieldType*>(GetTyp())->GetType() | mnSubType; +} + +void SwSetExpField::SetValue( const double& rAny ) +{ + SwValueField::SetValue(rAny); + + if( IsSequenceField() ) + msExpand = FormatNumber( GetValue(), static_cast<SvxNumType>(GetFormat()), GetLanguage() ); + else + msExpand = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue( rAny, + GetFormat(), GetLanguage()); +} + +void SwSetExpField::SetValue(const double& rValue, SwRootFrame const*const pLayout) +{ + if (!pLayout || !pLayout->IsHideRedlines()) + { + SetValue(rValue); + } + if (pLayout && !pLayout->IsHideRedlines()) + return; + + m_fValueRLHidden = rValue; + if (IsSequenceField()) + { + msExpandRLHidden = FormatNumber(rValue, static_cast<SvxNumType>(GetFormat()), GetLanguage()); + } + else + { + msExpandRLHidden = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue( + rValue, GetFormat(), GetLanguage()); + } +} + +double SwSetExpField::GetValue(SwRootFrame const* pLayout) const +{ + return (pLayout && pLayout->IsHideRedlines()) ? m_fValueRLHidden : GetValue(); +} + +void SwGetExpField::SetValue( const double& rAny ) +{ + SwValueField::SetValue(rAny); + m_sExpand = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue( rAny, GetFormat(), + GetLanguage()); +} + +void SwGetExpField::SetValue(const double& rValue, SwRootFrame const*const pLayout) +{ + if (!pLayout || !pLayout->IsHideRedlines()) + { + SetValue(rValue); + } + if (!pLayout || pLayout->IsHideRedlines()) + { + m_fValueRLHidden = rValue; + m_sExpandRLHidden = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue( + rValue, GetFormat(), GetLanguage()); + } +} + +double SwGetExpField::GetValue(SwRootFrame const* pLayout) const +{ + return (pLayout && pLayout->IsHideRedlines()) ? m_fValueRLHidden : GetValue(); +} + +/** Find the index of the reference text following the current field + * + * @param rFormat + * @param rDoc + * @param nHint search starting position after the current field (or 0 if default) + * @return + */ +sal_Int32 SwGetExpField::GetReferenceTextPos( const SwFormatField& rFormat, SwDoc& rDoc, sal_Int32 nHint) +{ + + const SwTextField* pTextField = rFormat.GetTextField(); + const SwTextNode& rTextNode = pTextField->GetTextNode(); + + sal_Int32 nRet = nHint ? nHint : pTextField->GetStart() + 1; + OUString sNodeText = rTextNode.GetText(); + + if(nRet<sNodeText.getLength()) + { + sNodeText = sNodeText.copy(nRet); + + // now check if sNodeText starts with a non-alphanumeric character plus blanks + sal_uInt16 nSrcpt = g_pBreakIt->GetRealScriptOfText( sNodeText, 0 ); + + static const WhichRangesContainer nIds(svl::Items< + RES_CHRATR_FONT, RES_CHRATR_FONT, + RES_CHRATR_LANGUAGE, RES_CHRATR_LANGUAGE, + RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONT, + RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_LANGUAGE + >); + SwAttrSet aSet(rDoc.GetAttrPool(), nIds); + rTextNode.GetParaAttr(aSet, nRet, nRet+1); + + TypedWhichId<SvxFontItem> nFontWhich = GetWhichOfScript( RES_CHRATR_FONT, nSrcpt ); + if( RTL_TEXTENCODING_SYMBOL != aSet.Get( nFontWhich ).GetCharSet() ) + { + TypedWhichId<SvxLanguageItem> nLangWhich = GetWhichOfScript( RES_CHRATR_LANGUAGE, nSrcpt ) ; + LanguageType eLang = aSet.Get(nLangWhich).GetLanguage(); + CharClass aCC(( LanguageTag(eLang) )); + sal_Unicode c0 = sNodeText[0]; + bool bIsAlphaNum = aCC.isAlphaNumeric( sNodeText, 0 ); + if( !bIsAlphaNum || + (c0 == ' ' || c0 == '\t')) + { + // ignoring blanks + nRet++; + const sal_Int32 nLen = sNodeText.getLength(); + for (sal_Int32 i = 1; + i<nLen && (sNodeText[i]==' ' || sNodeText[i]=='\t'); + ++i + ) + ++nRet; + } + } + } + return nRet; +} + +OUString SwSetExpField::GetPar1() const +{ + return static_cast<const SwSetExpFieldType*>(GetTyp())->GetName(); +} + +OUString SwSetExpField::GetPar2() const +{ + sal_uInt16 nType = static_cast<SwSetExpFieldType*>(GetTyp())->GetType(); + + if (nType & nsSwGetSetExpType::GSE_STRING) + return GetFormula(); + return GetExpandedFormula(); +} + +void SwSetExpField::SetPar2(const OUString& rStr) +{ + sal_uInt16 nType = static_cast<SwSetExpFieldType*>(GetTyp())->GetType(); + + if( !(nType & nsSwGetSetExpType::GSE_SEQ) || !rStr.isEmpty() ) + { + if (nType & nsSwGetSetExpType::GSE_STRING) + SetFormula(rStr); + else + SetExpandedFormula(rStr); + } +} + +bool SwSetExpField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + sal_Int32 nTmp32 = 0; + sal_Int16 nTmp16 = 0; + switch( nWhichId ) + { + case FIELD_PROP_BOOL2: + if(*o3tl::doAccess<bool>(rAny)) + mnSubType &= ~nsSwExtendedSubType::SUB_INVISIBLE; + else + mnSubType |= nsSwExtendedSubType::SUB_INVISIBLE; + break; + case FIELD_PROP_FORMAT: + rAny >>= nTmp32; + SetFormat(nTmp32); + break; + case FIELD_PROP_USHORT2: + { + rAny >>= nTmp16; + if(nTmp16 <= css::style::NumberingType::NUMBER_NONE ) + SetFormat(nTmp16); + else { + //exception(wrong_value) + ; + } + } + break; + case FIELD_PROP_USHORT1: + rAny >>= nTmp16; + mnSeqNo = nTmp16; + break; + case FIELD_PROP_PAR1: + { + OUString sTmp; + rAny >>= sTmp; + SetPar1( SwStyleNameMapper::GetUIName( sTmp, SwGetPoolIdFromName::TxtColl ) ); + } + break; + case FIELD_PROP_PAR2: + { + OUString uTmp; + rAny >>= uTmp; + //I18N - if the formula contains only "TypeName+1" + //and it's one of the initially created sequence fields + //then the localized names has to be replaced by a programmatic name + OUString sMyFormula = SwXFieldMaster::LocalizeFormula(*this, uTmp, false); + SetFormula( sMyFormula ); + } + break; + case FIELD_PROP_DOUBLE: + { + double fVal = 0.0; + rAny >>= fVal; + SetValue(fVal); + m_fValueRLHidden = fVal; + } + break; + case FIELD_PROP_SUBTYPE: + nTmp32 = lcl_APIToSubType(rAny); + if(nTmp32 >= 0) + SetSubType(o3tl::narrowing<sal_uInt16>((GetSubType() & 0xff00) | nTmp32)); + break; + case FIELD_PROP_PAR3: + rAny >>= maPText; + break; + case FIELD_PROP_BOOL3: + if(*o3tl::doAccess<bool>(rAny)) + mnSubType |= nsSwExtendedSubType::SUB_CMD; + else + mnSubType &= (~nsSwExtendedSubType::SUB_CMD); + break; + case FIELD_PROP_BOOL1: + { + bool newInput(*o3tl::doAccess<bool>(rAny)); + if (newInput != GetInputFlag()) + { + if (static_cast<SwSetExpFieldType*>(GetTyp())->GetType() + & nsSwGetSetExpType::GSE_STRING) + { + SwXTextField::TransmuteLeadToInputField(*this); + } + else + { + SetInputFlag(newInput); + } + } + } + break; + case FIELD_PROP_PAR4: + { + OUString sTmp; + rAny >>= sTmp; + ChgExpStr(sTmp, nullptr); + } + break; + default: + return SwField::PutValue(rAny, nWhichId); + } + return true; +} + +bool SwSetExpField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL2: + rAny <<= 0 == (mnSubType & nsSwExtendedSubType::SUB_INVISIBLE); + break; + case FIELD_PROP_FORMAT: + rAny <<= static_cast<sal_Int32>(GetFormat()); + break; + case FIELD_PROP_USHORT2: + rAny <<= static_cast<sal_Int16>(GetFormat()); + break; + case FIELD_PROP_USHORT1: + rAny <<= static_cast<sal_Int16>(mnSeqNo); + break; + case FIELD_PROP_PAR1: + rAny <<= SwStyleNameMapper::GetProgName(GetPar1(), SwGetPoolIdFromName::TxtColl ); + break; + case FIELD_PROP_PAR2: + { + //I18N - if the formula contains only "TypeName+1" + //and it's one of the initially created sequence fields + //then the localized names has to be replaced by a programmatic name + OUString sMyFormula = SwXFieldMaster::LocalizeFormula(*this, GetFormula(), true); + rAny <<= sMyFormula; + } + break; + case FIELD_PROP_DOUBLE: + rAny <<= GetValue(); + break; + case FIELD_PROP_SUBTYPE: + { + sal_Int16 nRet = lcl_SubTypeToAPI(GetSubType() & 0xff); + rAny <<= nRet; + } + break; + case FIELD_PROP_PAR3: + rAny <<= maPText; + break; + case FIELD_PROP_BOOL3: + rAny <<= 0 != (mnSubType & nsSwExtendedSubType::SUB_CMD); + break; + case FIELD_PROP_BOOL1: + rAny <<= GetInputFlag(); + break; + case FIELD_PROP_PAR4: + rAny <<= GetExpStr(nullptr); + break; + default: + return SwField::QueryValue(rAny, nWhichId); + } + return true; +} + +SwInputFieldType::SwInputFieldType( SwDoc* pD ) + : SwFieldType( SwFieldIds::Input ) + , mpDoc( pD ) +{ +} + +std::unique_ptr<SwFieldType> SwInputFieldType::Copy() const +{ + return std::make_unique<SwInputFieldType>( mpDoc ); +} + +SwInputField::SwInputField( SwInputFieldType* pFieldType, + const OUString& rContent, + const OUString& rPrompt, + sal_uInt16 nSub, + sal_uLong nFormat, + bool bIsFormField ) + : SwField( pFieldType, nFormat, LANGUAGE_SYSTEM, false ) + , maContent(rContent) + , maPText(rPrompt) + , mnSubType(nSub) + , mbIsFormField( bIsFormField ) + , mpFormatField( nullptr ) +{ +} + +SwInputField::~SwInputField() +{ +} + +void SwInputField::SetFormatField( SwFormatField& rFormatField ) +{ + mpFormatField = &rFormatField; +} + + +void SwInputField::applyFieldContent( const OUString& rNewFieldContent ) +{ + if ( (mnSubType & 0x00ff) == INP_TXT ) + { + maContent = rNewFieldContent; + } + else if( (mnSubType & 0x00ff) == INP_USR ) + { + SwUserFieldType* pUserTyp = static_cast<SwUserFieldType*>( + static_cast<SwInputFieldType*>(GetTyp())->GetDoc()->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::User, getContent(), false ) ); + if( pUserTyp ) + { + pUserTyp->SetContent( rNewFieldContent ); + if (!pUserTyp->IsModifyLocked()) + { + // trigger update of the corresponding User Fields and other + // related Input Fields + bool bUnlock(false); + if (GetFormatField() != nullptr) + { + SwTextInputField *const pTextInputField = + dynamic_cast<SwTextInputField*>(GetFormatField()->GetTextField()); + if (pTextInputField != nullptr) + { + bUnlock = pTextInputField->LockNotifyContentChange(); + } + } + pUserTyp->UpdateFields(); + if (bUnlock) + { + SwTextInputField *const pTextInputField = + dynamic_cast<SwTextInputField*>(GetFormatField()->GetTextField()); + if (pTextInputField != nullptr) + { + pTextInputField->UnlockNotifyContentChange(); + } + } + } + } + } +} + +OUString SwInputField::GetFieldName() const +{ + OUString aStr(SwField::GetFieldName()); + if ((mnSubType & 0x00ff) == INP_USR) + { + aStr += GetTyp()->GetName() + " " + getContent(); + } + return aStr; +} + +std::unique_ptr<SwField> SwInputField::Copy() const +{ + std::unique_ptr<SwInputField> pField( + new SwInputField( + static_cast<SwInputFieldType*>(GetTyp()), + getContent(), + maPText, + GetSubType(), + GetFormat(), + mbIsFormField )); + + pField->SetHelp( maHelp ); + pField->SetToolTip( maToolTip ); + pField->maGrabBag = maGrabBag; + + pField->SetAutomaticLanguage(IsAutomaticLanguage()); + return std::unique_ptr<SwField>(pField.release()); +} + +OUString SwInputField::ExpandImpl(SwRootFrame const*const) const +{ + if((mnSubType & 0x00ff) == INP_TXT) + { + return getContent(); + } + + if( (mnSubType & 0x00ff) == INP_USR ) + { + SwUserFieldType* pUserTyp = static_cast<SwUserFieldType*>( + static_cast<SwInputFieldType*>(GetTyp())->GetDoc()->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::User, getContent(), false ) ); + if( pUserTyp ) + return pUserTyp->GetContent(); + } + + return OUString(); +} + +bool SwInputField::isFormField() const +{ + return mbIsFormField + || !maHelp.isEmpty() + || !maToolTip.isEmpty(); +} + +bool SwInputField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= getContent(); + break; + case FIELD_PROP_PAR2: + rAny <<= maPText; + break; + case FIELD_PROP_PAR3: + rAny <<= maHelp; + break; + case FIELD_PROP_PAR4: + rAny <<= maToolTip; + break; + case FIELD_PROP_GRABBAG: + rAny <<= maGrabBag; + break; + case FIELD_PROP_TITLE: + break; + default: + assert(false); + } + return true; +} + +bool SwInputField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny >>= maContent; + break; + case FIELD_PROP_PAR2: + rAny >>= maPText; + break; + case FIELD_PROP_PAR3: + rAny >>= maHelp; + break; + case FIELD_PROP_PAR4: + rAny >>= maToolTip; + break; + case FIELD_PROP_GRABBAG: + rAny >>= maGrabBag; + break; + case FIELD_PROP_TITLE: + break; + default: + assert(false); + } + return true; +} + +/// set condition +void SwInputField::SetPar1(const OUString& rStr) +{ + maContent = rStr; +} + +OUString SwInputField::GetPar1() const +{ + return getContent(); +} + +void SwInputField::SetPar2(const OUString& rStr) +{ + maPText = rStr; +} + +OUString SwInputField::GetPar2() const +{ + return maPText; +} + +void SwInputField::SetHelp(const OUString & rStr) +{ + maHelp = rStr; +} + +const OUString& SwInputField::GetHelp() const +{ + return maHelp; +} + +void SwInputField::SetToolTip(const OUString & rStr) +{ + maToolTip = rStr; +} + +const OUString& SwInputField::GetToolTip() const +{ + return maToolTip; +} + +sal_uInt16 SwInputField::GetSubType() const +{ + return mnSubType; +} + +void SwInputField::SetSubType(sal_uInt16 nSub) +{ + mnSubType = nSub; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/fldbas.cxx b/sw/source/core/fields/fldbas.cxx new file mode 100644 index 000000000..59e54e291 --- /dev/null +++ b/sw/source/core/fields/fldbas.cxx @@ -0,0 +1,865 @@ +/* -*- 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 <fldbas.hxx> + +#include <float.h> + +#include <libxml/xmlwriter.h> + +#include <rtl/math.hxx> +#include <svl/numformat.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <o3tl/enumarray.hxx> +#include <osl/diagnose.h> +#include <unofldmid.h> +#include <doc.hxx> +#include <fmtfld.hxx> +#include <docufld.hxx> +#include <expfld.hxx> +#include <shellres.hxx> +#include <calc.hxx> +#include <strings.hrc> +#include <docary.hxx> +#include <authfld.hxx> +#include <calbck.hxx> +#include <viewsh.hxx> +#include <hints.hxx> + +using namespace ::com::sun::star; +using namespace nsSwDocInfoSubType; + +static LanguageType lcl_GetLanguageOfFormat( LanguageType nLng, sal_uLong nFormat, + const SvNumberFormatter& rFormatter ) +{ + if( nLng == LANGUAGE_NONE ) // Bug #60010 + nLng = LANGUAGE_SYSTEM; + else if( nLng == ::GetAppLanguage() ) + switch( rFormatter.GetIndexTableOffset( nFormat )) + { + case NF_NUMBER_SYSTEM: + case NF_DATE_SYSTEM_SHORT: + case NF_DATE_SYSTEM_LONG: + case NF_DATETIME_SYSTEM_SHORT_HHMM: + nLng = LANGUAGE_SYSTEM; + break; + default: break; + } + return nLng; +} + +// Globals + +/// field names +std::vector<OUString>* SwFieldType::s_pFieldNames = nullptr; + +namespace +{ + + const o3tl::enumarray<SwFieldIds,SwFieldTypesEnum> aTypeTab { + /* SwFieldIds::Database */ SwFieldTypesEnum::Database, + /* SwFieldIds::User */ SwFieldTypesEnum::User, + /* SwFieldIds::Filename */ SwFieldTypesEnum::Filename, + /* SwFieldIds::DatabaseName */ SwFieldTypesEnum::DatabaseName, + /* SwFieldIds::Date */ SwFieldTypesEnum::Date, + /* SwFieldIds::Time */ SwFieldTypesEnum::Time, + /* SwFieldIds::PageNumber */ SwFieldTypesEnum::PageNumber, // dynamic + /* SwFieldIds::Author */ SwFieldTypesEnum::Author, + /* SwFieldIds::Chapter */ SwFieldTypesEnum::Chapter, + /* SwFieldIds::DocStat */ SwFieldTypesEnum::DocumentStatistics, + /* SwFieldIds::GetExp */ SwFieldTypesEnum::Get, // dynamic + /* SwFieldIds::SetExp */ SwFieldTypesEnum::Set, // dynamic + /* SwFieldIds::GetRef */ SwFieldTypesEnum::GetRef, + /* SwFieldIds::HiddenText */ SwFieldTypesEnum::HiddenText, + /* SwFieldIds::Postit */ SwFieldTypesEnum::Postit, + /* SwFieldIds::FixDate */ SwFieldTypesEnum::FixedDate, + /* SwFieldIds::FixTime */ SwFieldTypesEnum::FixedTime, + /* SwFieldIds::Reg */ SwFieldTypesEnum::Begin, // old (no change since 2000) + /* SwFieldIds::VarReg */ SwFieldTypesEnum::Begin, // old (no change since 2000) + /* SwFieldIds::SetRef */ SwFieldTypesEnum::SetRef, + /* SwFieldIds::Input */ SwFieldTypesEnum::Input, + /* SwFieldIds::Macro */ SwFieldTypesEnum::Macro, + /* SwFieldIds::Dde */ SwFieldTypesEnum::DDE, + /* SwFieldIds::Table */ SwFieldTypesEnum::Formel, + /* SwFieldIds::HiddenPara */ SwFieldTypesEnum::HiddenParagraph, + /* SwFieldIds::DocInfo */ SwFieldTypesEnum::DocumentInfo, + /* SwFieldIds::TemplateName */ SwFieldTypesEnum::TemplateName, + /* SwFieldIds::DbNextSet */ SwFieldTypesEnum::DatabaseNextSet, + /* SwFieldIds::DbNumSet */ SwFieldTypesEnum::DatabaseNumberSet, + /* SwFieldIds::DbSetNumber */ SwFieldTypesEnum::DatabaseSetNumber, + /* SwFieldIds::ExtUser */ SwFieldTypesEnum::ExtendedUser, + /* SwFieldIds::RefPageSet */ SwFieldTypesEnum::SetRefPage, + /* SwFieldIds::RefPageGet */ SwFieldTypesEnum::GetRefPage, + /* SwFieldIds::Internet */ SwFieldTypesEnum::Internet, + /* SwFieldIds::JumpEdit */ SwFieldTypesEnum::JumpEdit, + /* SwFieldIds::Script */ SwFieldTypesEnum::Script, + /* SwFieldIds::DateTime */ SwFieldTypesEnum::Begin, // dynamic + /* SwFieldIds::TableOfAuthorities*/ SwFieldTypesEnum::Authority, + /* SwFieldIds::CombinedChars */ SwFieldTypesEnum::CombinedChars, + /* SwFieldIds::Dropdown */ SwFieldTypesEnum::Dropdown, + /* SwFieldIds::ParagraphSignature */ SwFieldTypesEnum::ParagraphSignature + }; + +} + +const OUString & SwFieldType::GetTypeStr(SwFieldTypesEnum nTypeId) +{ + if (!s_pFieldNames) + GetFieldName_(); + + return (*SwFieldType::s_pFieldNames)[static_cast<int>(nTypeId)]; +} + +// each field references a field type that is unique for each document +SwFieldType::SwFieldType( SwFieldIds nWhichId ) + : sw::BroadcastingModify() + , m_nWhich(nWhichId) +{ +} + +OUString SwFieldType::GetName() const +{ + return OUString(); +} + +void SwFieldType::QueryValue( uno::Any&, sal_uInt16 ) const +{ +} +void SwFieldType::PutValue( const uno::Any& , sal_uInt16 ) +{ +} + +void SwFieldType::PrintHiddenPara() +{ + const SwMsgPoolItem aHint(RES_HIDDENPARA_PRINT); + SwClientNotify(*this, sw::LegacyModifyHint(&aHint, nullptr)); +} + +void SwFieldType::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + std::vector<SwFormatField*> vFields; + GatherFields(vFields); + if(!vFields.size()) + return; + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFieldType")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name())); + for(const auto pFormatField: vFields) + pFormatField->dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); +} + +SwFormatField* SwFieldType::FindFormatForField(const SwField* pField) const { + SwFormatField* pFormat = nullptr; + CallSwClientNotify(sw::FindFormatForFieldHint(pField, pFormat)); + return pFormat; +} + +SwFormatField* SwFieldType::FindFormatForPostItId(sal_uInt32 nPostItId) const { + SwFormatField* pFormat = nullptr; + CallSwClientNotify(sw::FindFormatForPostItIdHint(nPostItId, pFormat)); + return pFormat; +} + +void SwFieldType::CollectPostIts(std::vector<SwFormatField*>& rvFormatFields, IDocumentRedlineAccess const& rIDRA, const bool bHideRedlines) +{ + CallSwClientNotify(sw::CollectPostItsHint(rvFormatFields, rIDRA, bHideRedlines)); +} + +bool SwFieldType::HasHiddenInformationNotes() const +{ + bool bHasHiddenInformationNotes = false; + CallSwClientNotify(sw::HasHiddenInformationNotesHint(bHasHiddenInformationNotes)); + return bHasHiddenInformationNotes; +} + +void SwFieldType::GatherNodeIndex(std::vector<SwNodeOffset>& rvNodeIndex) +{ + CallSwClientNotify(sw::GatherNodeIndexHint(rvNodeIndex)); +} + +void SwFieldType::GatherRefFields(std::vector<SwGetRefField*>& rvRFields, const sal_uInt16 nTyp) +{ + CallSwClientNotify(sw::GatherRefFieldsHint(rvRFields, nTyp)); +} + +void SwFieldType::GatherFields(std::vector<SwFormatField*>& rvFields, bool bCollectOnlyInDocNodes) const +{ + CallSwClientNotify(sw::GatherFieldsHint(rvFields, bCollectOnlyInDocNodes)); +} + +void SwFieldType::GatherDdeTables(std::vector<SwDDETable*>& rvTables) const +{ + CallSwClientNotify(sw::GatherDdeTablesHint(rvTables)); +} + +void SwFieldType::UpdateFields() +{ + CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr)); +}; + +void SwFieldTypes::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFieldTypes")); + sal_uInt16 nCount = size(); + for (sal_uInt16 nType = 0; nType < nCount; ++nType) + (*this)[nType]->dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); +} + +// Base class for all fields. +// A field (multiple can exist) references a field type (can exists only once) +SwField::SwField( + SwFieldType* pType, + sal_uInt32 nFormat, + LanguageType nLang, + bool bUseFieldValueCache) + : m_pType( pType ) + , m_nFormat( nFormat ) + , m_nLang( nLang ) + , m_bUseFieldValueCache( bUseFieldValueCache ) + , m_bIsAutomaticLanguage( true ) +{ + assert(m_pType); +} + +SwField::~SwField() +{ +} + +// instead of indirectly via the type + +#ifdef DBG_UTIL +SwFieldIds SwField::Which() const +{ + assert(m_pType); + return m_pType->Which(); +} +#endif + +SwFieldTypesEnum SwField::GetTypeId() const +{ + + SwFieldTypesEnum nRet; + switch (m_pType->Which()) + { + case SwFieldIds::DateTime: + if (GetSubType() & FIXEDFLD) + nRet = GetSubType() & DATEFLD ? SwFieldTypesEnum::FixedDate : SwFieldTypesEnum::FixedTime; + else + nRet = GetSubType() & DATEFLD ? SwFieldTypesEnum::Date : SwFieldTypesEnum::Time; + break; + case SwFieldIds::GetExp: + nRet = nsSwGetSetExpType::GSE_FORMULA & GetSubType() ? SwFieldTypesEnum::Formel : SwFieldTypesEnum::Get; + break; + + case SwFieldIds::HiddenText: + nRet = static_cast<SwFieldTypesEnum>(GetSubType()); + break; + + case SwFieldIds::SetExp: + if( nsSwGetSetExpType::GSE_SEQ & GetSubType() ) + nRet = SwFieldTypesEnum::Sequence; + else if( static_cast<const SwSetExpField*>(this)->GetInputFlag() ) + nRet = SwFieldTypesEnum::SetInput; + else + nRet = SwFieldTypesEnum::Set; + break; + + case SwFieldIds::PageNumber: + { + auto nSubType = GetSubType(); + if( PG_NEXT == nSubType ) + nRet = SwFieldTypesEnum::NextPage; + else if( PG_PREV == nSubType ) + nRet = SwFieldTypesEnum::PreviousPage; + else + nRet = SwFieldTypesEnum::PageNumber; + } + break; + + default: + nRet = aTypeTab[ m_pType->Which() ]; + } + return nRet; +} + +/// get name or content +OUString SwField::GetFieldName() const +{ + SwFieldTypesEnum nTypeId = GetTypeId(); + if (SwFieldIds::DateTime == GetTyp()->Which()) + { + nTypeId = + ((GetSubType() & DATEFLD) != 0) ? SwFieldTypesEnum::Date : SwFieldTypesEnum::Time; + } + OUString sRet = SwFieldType::GetTypeStr( nTypeId ); + if (IsFixed()) + { + sRet += " " + SwViewShell::GetShellRes()->aFixedStr; + } + return sRet; +} + +OUString SwField::GetPar1() const +{ + return OUString(); +} + +OUString SwField::GetPar2() const +{ + return OUString(); +} + +OUString SwField::GetFormula() const +{ + return GetPar2(); +} + +void SwField::SetPar1(const OUString& ) +{} + +void SwField::SetPar2(const OUString& ) +{} + +sal_uInt16 SwField::GetSubType() const +{ + return 0; +} + +void SwField::SetSubType(sal_uInt16 ) +{ +} + +bool SwField::QueryValue( uno::Any& rVal, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL4: + rVal <<= !m_bIsAutomaticLanguage; + break; + case FIELD_PROP_TITLE: + { + rVal <<= m_aTitle; + } + break; + default: + assert(false); + } + return true; +} + +bool SwField::PutValue( const uno::Any& rVal, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL4: + { + bool bFixed = false; + if(rVal >>= bFixed) + m_bIsAutomaticLanguage = !bFixed; + } + break; + case FIELD_PROP_TITLE: + { + OUString aTitle; + if (rVal >>= aTitle) + { + m_aTitle = aTitle; + } + } + break; + default: + assert(false); + } + return true; +} + +/** Set a new type + * + * This is needed/used for copying between documents. + * Needs to be always of the same type. + * @param pNewType The new type. + * @return The old type. + */ +SwFieldType* SwField::ChgTyp( SwFieldType* pNewType ) +{ + assert(pNewType && pNewType->Which() == m_pType->Which()); + + SwFieldType* pOld = m_pType; + m_pType = pNewType; + return pOld; +} + +/// Does the field have an action on a ClickHandler? (E.g. INetFields,...) +bool SwField::HasClickHdl() const +{ + bool bRet = false; + switch (m_pType->Which()) + { + case SwFieldIds::Internet: + case SwFieldIds::JumpEdit: + case SwFieldIds::GetRef: + case SwFieldIds::Macro: + case SwFieldIds::Input: + case SwFieldIds::Dropdown : + bRet = true; + break; + + case SwFieldIds::SetExp: + bRet = static_cast<const SwSetExpField*>(this)->GetInputFlag(); + break; + + case SwFieldIds::TableOfAuthorities: + { + const auto pAuthorityField = static_cast<const SwAuthorityField*>(this); + bRet = pAuthorityField->HasURL(); + break; + } + + default: break; + } + return bRet; +} + +void SwField::SetLanguage(LanguageType const nLang) +{ + m_nLang = nLang; +} + +void SwField::ChangeFormat(sal_uInt32 const nFormat) +{ + m_nFormat = nFormat; +} + +bool SwField::IsFixed() const +{ + bool bRet = false; + switch (m_pType->Which()) + { + case SwFieldIds::FixDate: + case SwFieldIds::FixTime: + bRet = true; + break; + + case SwFieldIds::DateTime: + bRet = 0 != (GetSubType() & FIXEDFLD); + break; + + case SwFieldIds::ExtUser: + case SwFieldIds::Author: + bRet = 0 != (GetFormat() & AF_FIXED); + break; + + case SwFieldIds::Filename: + bRet = 0 != (GetFormat() & FF_FIXED); + break; + + case SwFieldIds::DocInfo: + bRet = 0 != (GetSubType() & DI_SUB_FIXED); + break; + default: break; + } + return bRet; +} + +OUString +SwField::ExpandField(bool const bCached, SwRootFrame const*const pLayout) const +{ + if ( m_bUseFieldValueCache ) + { + if (!bCached) // #i85766# do not expand fields in clipboard documents + { + if (GetTypeId() == SwFieldTypesEnum::Authority) + { + const SwAuthorityField* pAuthorityField = static_cast<const SwAuthorityField*>(this); + m_Cache = pAuthorityField->ConditionalExpandAuthIdentifier(pLayout); + } + else + m_Cache = ExpandImpl(pLayout); + } + return m_Cache; + } + + return ExpandImpl(pLayout); +} + +std::unique_ptr<SwField> SwField::CopyField() const +{ + std::unique_ptr<SwField> pNew = Copy(); + // #i85766# cache expansion of source (for clipboard) + // use this->cache, not this->Expand(): only text formatting calls Expand() + pNew->m_Cache = m_Cache; + pNew->m_bUseFieldValueCache = m_bUseFieldValueCache; + + return pNew; +} + +/// expand numbering +OUString FormatNumber(sal_uInt32 nNum, SvxNumType nFormat, LanguageType nLang) +{ + if(SVX_NUM_PAGEDESC == nFormat) + return OUString::number( nNum ); + SvxNumberType aNumber; + + OSL_ENSURE(nFormat != SVX_NUM_NUMBER_NONE, "wrong number format" ); + + aNumber.SetNumberingType(nFormat); + + if (nLang == LANGUAGE_NONE) + return aNumber.GetNumStr(nNum); + else + return aNumber.GetNumStr(nNum, LanguageTag::convertToLocale(nLang)); +} + +SwValueFieldType::SwValueFieldType(SwDoc *const pDoc, SwFieldIds const nWhichId) + : SwFieldType(nWhichId) + , m_pDoc(pDoc) + , m_bUseFormat(true) +{ +} + +SwValueFieldType::SwValueFieldType( const SwValueFieldType& rTyp ) + : SwFieldType(rTyp.Which()) + , m_pDoc(rTyp.GetDoc()) + , m_bUseFormat(rTyp.UseFormat()) +{ +} + +/// return value formatted as string +OUString SwValueFieldType::ExpandValue( const double& rVal, + sal_uInt32 nFormat, LanguageType nLng) const +{ + if (rVal >= DBL_MAX) // error string for calculator + return SwViewShell::GetShellRes()->aCalc_Error; + + OUString sExpand; + SvNumberFormatter* pFormatter = m_pDoc->GetNumberFormatter(); + const Color* pCol = nullptr; + + // Bug #60010 + LanguageType nFormatLng = ::lcl_GetLanguageOfFormat( nLng, nFormat, *pFormatter ); + + if( nFormat < SV_COUNTRY_LANGUAGE_OFFSET && LANGUAGE_SYSTEM != nFormatLng ) + { + const SvNumberformat* pEntry = pFormatter->GetEntry(nFormat); + + if (pEntry && nLng != pEntry->GetLanguage()) + { + sal_uInt32 nNewFormat = pFormatter->GetFormatForLanguageIfBuiltIn(nFormat, + nFormatLng); + if (nNewFormat == nFormat) + { + // probably user-defined format + OUString sFormat(pEntry->GetFormatstring()); + sal_Int32 nDummy; + SvNumFormatType nType = SvNumFormatType::DEFINED; + + pFormatter->PutandConvertEntry(sFormat, nDummy, nType, nFormat, + pEntry->GetLanguage(), nFormatLng, false); + } + else + nFormat = nNewFormat; + } + OSL_ENSURE(pEntry, "unknown number format!"); + } + + if( pFormatter->IsTextFormat( nFormat ) ) + { + pFormatter->GetOutputString(DoubleToString(rVal, nFormatLng), nFormat, + sExpand, &pCol); + } + else + { + pFormatter->GetOutputString(rVal, nFormat, sExpand, &pCol); + } + return sExpand; +} + +OUString SwValueFieldType::DoubleToString(const double &rVal, + sal_uInt32 nFormat) const +{ + SvNumberFormatter* pFormatter = m_pDoc->GetNumberFormatter(); + const SvNumberformat* pEntry = pFormatter->GetEntry(nFormat); + + if (!pEntry) + return OUString(); + + return DoubleToString(rVal, pEntry->GetLanguage()); +} + +OUString SwValueFieldType::DoubleToString( const double &rVal, + LanguageType nLng ) const +{ + SvNumberFormatter* pFormatter = m_pDoc->GetNumberFormatter(); + + // Bug #60010 + if( nLng == LANGUAGE_NONE ) + nLng = LANGUAGE_SYSTEM; + + pFormatter->ChangeIntl( nLng ); // get separator in the correct language + return ::rtl::math::doubleToUString( rVal, rtl_math_StringFormat_F, 12, + pFormatter->GetNumDecimalSep()[0], true ); +} + +SwValueField::SwValueField( SwValueFieldType* pFieldType, sal_uInt32 nFormat, + LanguageType nLng, const double fVal ) + : SwField(pFieldType, nFormat, nLng) + , m_fValue(fVal) +{ +} + +SwValueField::SwValueField( const SwValueField& rField ) + : SwField(rField) + , m_fValue(rField.GetValue()) +{ +} + +SwValueField::~SwValueField() +{ +} + +/** Set a new type + * + * This is needed/used for copying between documents. + * Needs to be always of the same type. + * @param pNewType The new type. + * @return The old type. + */ +SwFieldType* SwValueField::ChgTyp( SwFieldType* pNewType ) +{ + SwDoc* pNewDoc = static_cast<SwValueFieldType *>(pNewType)->GetDoc(); + SwDoc* pDoc = GetDoc(); + + if( pNewDoc && pDoc && pDoc != pNewDoc) + { + SvNumberFormatter* pFormatter = pNewDoc->GetNumberFormatter(); + + if( pFormatter && pFormatter->HasMergeFormatTable() && + static_cast<SwValueFieldType *>(GetTyp())->UseFormat() ) + SetFormat(pFormatter->GetMergeFormatIndex( GetFormat() )); + } + + return SwField::ChgTyp(pNewType); +} + +/// get format in office language +sal_uInt32 SwValueField::GetSystemFormat(SvNumberFormatter* pFormatter, sal_uInt32 nFormat) +{ + const SvNumberformat* pEntry = pFormatter->GetEntry(nFormat); + LanguageType nLng = SvtSysLocale().GetLanguageTag().getLanguageType(); + + if (pEntry && nLng != pEntry->GetLanguage()) + { + sal_uInt32 nNewFormat = pFormatter->GetFormatForLanguageIfBuiltIn(nFormat, + nLng); + + if (nNewFormat == nFormat) + { + // probably user-defined format + SvNumFormatType nType = SvNumFormatType::DEFINED; + sal_Int32 nDummy; + + OUString sFormat(pEntry->GetFormatstring()); + + sal_uInt32 nTempFormat = nFormat; + pFormatter->PutandConvertEntry(sFormat, nDummy, nType, + nTempFormat, pEntry->GetLanguage(), nLng, true); + nFormat = nTempFormat; + } + else + nFormat = nNewFormat; + } + + return nFormat; +} + +void SwValueField::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwValueField")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_fValue"), BAD_CAST(OString::number(m_fValue).getStr())); + SwField::dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); +} + +/// set language of the format +void SwValueField::SetLanguage( LanguageType nLng ) +{ + if( IsAutomaticLanguage() && + static_cast<SwValueFieldType *>(GetTyp())->UseFormat() && + GetFormat() != SAL_MAX_UINT32 ) + { + // Bug #60010 + SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter(); + LanguageType nFormatLng = ::lcl_GetLanguageOfFormat( nLng, GetFormat(), + *pFormatter ); + + if( (GetFormat() >= SV_COUNTRY_LANGUAGE_OFFSET || + LANGUAGE_SYSTEM != nFormatLng ) && + !(Which() == SwFieldIds::User && (GetSubType()&nsSwExtendedSubType::SUB_CMD) ) ) + { + const SvNumberformat* pEntry = pFormatter->GetEntry(GetFormat()); + + if( pEntry && nFormatLng != pEntry->GetLanguage() ) + { + sal_uInt32 nNewFormat = pFormatter->GetFormatForLanguageIfBuiltIn( + GetFormat(), nFormatLng ); + + if( nNewFormat == GetFormat() ) + { + // probably user-defined format + SvNumFormatType nType = SvNumFormatType::DEFINED; + sal_Int32 nDummy; + OUString sFormat( pEntry->GetFormatstring() ); + pFormatter->PutandConvertEntry( sFormat, nDummy, nType, + nNewFormat, + pEntry->GetLanguage(), + nFormatLng, false); + } + SetFormat( nNewFormat ); + } + OSL_ENSURE(pEntry, "unknown number format!"); + } + } + + SwField::SetLanguage(nLng); +} + +double SwValueField::GetValue() const +{ + return m_fValue; +} + +void SwValueField::SetValue( const double& rVal ) +{ + m_fValue = rVal; +} + +SwFormulaField::SwFormulaField( SwValueFieldType* pFieldType, sal_uInt32 nFormat, const double fVal) + : SwValueField(pFieldType, nFormat, LANGUAGE_SYSTEM, fVal) +{ +} + +SwFormulaField::SwFormulaField( const SwFormulaField& rField ) + : SwValueField(static_cast<SwValueFieldType *>(rField.GetTyp()), rField.GetFormat(), + rField.GetLanguage(), rField.GetValue()) +{ +} + +OUString SwFormulaField::GetFormula() const +{ + return m_sFormula; +} + +void SwFormulaField::SetFormula(const OUString& rStr) +{ + m_sFormula = rStr; + + sal_uLong nFormat(GetFormat()); + + if( nFormat && SAL_MAX_UINT32 != nFormat ) + { + sal_Int32 nPos = 0; + double fTmpValue; + if( SwCalc::Str2Double( rStr, nPos, fTmpValue, GetDoc() ) ) + SwValueField::SetValue( fTmpValue ); + } +} + +void SwFormulaField::SetExpandedFormula( const OUString& rStr ) +{ + sal_uInt32 nFormat(GetFormat()); + + if (nFormat && nFormat != SAL_MAX_UINT32 && static_cast<SwValueFieldType *>(GetTyp())->UseFormat()) + { + double fTmpValue; + + if (GetDoc()->IsNumberFormat(rStr, nFormat, fTmpValue)) + { + SwValueField::SetValue(fTmpValue); + + m_sFormula = static_cast<SwValueFieldType *>(GetTyp())->DoubleToString(fTmpValue, nFormat); + return; + } + } + m_sFormula = rStr; +} + +OUString SwFormulaField::GetExpandedFormula() const +{ + sal_uInt32 nFormat(GetFormat()); + + if (nFormat && nFormat != SAL_MAX_UINT32 && static_cast<SwValueFieldType *>(GetTyp())->UseFormat()) + { + OUString sFormattedValue; + const Color* pCol = nullptr; + + SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter(); + + if (pFormatter->IsTextFormat(nFormat)) + { + OUString sTempIn(static_cast<SwValueFieldType *>(GetTyp())->DoubleToString(GetValue(), nFormat)); + pFormatter->GetOutputString(sTempIn, nFormat, sFormattedValue, &pCol); + } + else + { + pFormatter->GetOutputString(GetValue(), nFormat, sFormattedValue, &pCol); + } + return sFormattedValue; + } + else + return GetFormula(); +} + +OUString SwField::GetDescription() const +{ + return SwResId(STR_FIELD); +} + +bool SwField::IsClickable() const +{ + switch (Which()) + { + case SwFieldIds::JumpEdit: + case SwFieldIds::Macro: + case SwFieldIds::GetRef: + case SwFieldIds::Input: + case SwFieldIds::SetExp: + case SwFieldIds::Dropdown: + case SwFieldIds::TableOfAuthorities: + return true; + default: break; + } + return false; +} + +void SwField::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwField")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name())); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nFormat"), BAD_CAST(OString::number(m_nFormat).getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nLang"), BAD_CAST(OString::number(m_nLang.get()).getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_aTitle"), BAD_CAST(m_aTitle.toUtf8().getStr())); + + (void)xmlTextWriterEndElement(pWriter); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/flddat.cxx b/sw/source/core/fields/flddat.cxx new file mode 100644 index 000000000..51246f6ee --- /dev/null +++ b/sw/source/core/fields/flddat.cxx @@ -0,0 +1,226 @@ +/* -*- 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 <sal/config.h> + +#include <o3tl/any.hxx> +#include <o3tl/temporary.hxx> +#include <tools/datetime.hxx> +#include <svl/numformat.hxx> +#include <svl/zforlist.hxx> +#include <com/sun/star/util/DateTime.hpp> +#include <doc.hxx> +#include <fldbas.hxx> +#include <flddat.hxx> +#include <unofldmid.h> + +using namespace ::com::sun::star; + +SwDateTimeFieldType::SwDateTimeFieldType(SwDoc* pInitDoc) + : SwValueFieldType( pInitDoc, SwFieldIds::DateTime ) +{} + +std::unique_ptr<SwFieldType> SwDateTimeFieldType::Copy() const +{ + return std::make_unique<SwDateTimeFieldType>(GetDoc()); +} + +SwDateTimeField::SwDateTimeField(SwDateTimeFieldType* pInitType, sal_uInt16 nSub, sal_uLong nFormat, LanguageType nLng) + : SwValueField(pInitType, nFormat, nLng, 0.0), + m_nSubType(nSub), + m_nOffset(0) +{ + if (!nFormat) + { + SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter(); + if (m_nSubType & DATEFLD) + ChangeFormat(pFormatter->GetFormatIndex(NF_DATE_SYSTEM_SHORT, GetLanguage())); + else + ChangeFormat(pFormatter->GetFormatIndex(NF_TIME_HHMMSS, GetLanguage())); + } + if (IsFixed()) + { + DateTime aDateTime( DateTime::SYSTEM ); + SetDateTime(aDateTime); + } +} + +OUString SwDateTimeField::ExpandImpl(SwRootFrame const*const) const +{ + double fVal; + + if (!(IsFixed())) + { + DateTime aDateTime( DateTime::SYSTEM ); + fVal = GetDateTime(*GetDoc(), aDateTime); + } + else + fVal = GetValue(); + + if (m_nOffset) + fVal += m_nOffset * ( 60 / 86400.0 ); + + return ExpandValue(fVal, GetFormat(), GetLanguage()); +} + +std::unique_ptr<SwField> SwDateTimeField::Copy() const +{ + std::unique_ptr<SwDateTimeField> pTmp( + new SwDateTimeField(static_cast<SwDateTimeFieldType*>(GetTyp()), m_nSubType, + GetFormat(), GetLanguage()) ); + + pTmp->SetValue(GetValue()); + pTmp->SetOffset(m_nOffset); + pTmp->SetAutomaticLanguage(IsAutomaticLanguage()); + + return std::unique_ptr<SwField>(pTmp.release()); +} + +sal_uInt16 SwDateTimeField::GetSubType() const +{ + return m_nSubType; +} + +void SwDateTimeField::SetSubType(sal_uInt16 nType) +{ + m_nSubType = nType; +} + +void SwDateTimeField::SetPar2(const OUString& rStr) +{ + m_nOffset = rStr.toInt32(); +} + +OUString SwDateTimeField::GetPar2() const +{ + if (m_nOffset) + return OUString::number(m_nOffset); + return OUString(); +} + +void SwDateTimeField::SetDateTime(const DateTime& rDT) +{ + SetValue(GetDateTime(*GetDoc(), rDT)); +} + +double SwDateTimeField::GetDateTime(SwDoc& rDoc, const DateTime& rDT) +{ + SvNumberFormatter* pFormatter = rDoc.GetNumberFormatter(); + const Date& rNullDate = pFormatter->GetNullDate(); + + double fResult = rDT - DateTime(rNullDate); + + return fResult; +} + +double SwDateTimeField::GetValue() const +{ + if (IsFixed()) + return SwValueField::GetValue(); + else + return GetDateTime(*GetDoc(), DateTime( DateTime::SYSTEM )); +} + +Date SwDateTimeField::GetDate() const +{ + SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter(); + const Date& rNullDate = pFormatter->GetNullDate(); + + tools::Long nVal = static_cast<tools::Long>( GetValue() ); + + Date aDate = rNullDate + nVal; + + return aDate; +} + +tools::Time SwDateTimeField::GetTime() const +{ + double fFract = modf(GetValue(), &o3tl::temporary(double())); + DateTime aDT( DateTime::EMPTY ); + aDT.AddTime(fFract); + return static_cast<tools::Time>(aDT); +} + +bool SwDateTimeField::QueryValue( uno::Any& rVal, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL1: + rVal <<= IsFixed(); + break; + case FIELD_PROP_BOOL2: + rVal <<= (m_nSubType & DATEFLD) != 0; + break; + case FIELD_PROP_FORMAT: + rVal <<= static_cast<sal_Int32>(GetFormat()); + break; + case FIELD_PROP_SUBTYPE: + rVal <<= static_cast<sal_Int32>(m_nOffset); + break; + case FIELD_PROP_DATE_TIME: + { + DateTime aDateTime(GetDate(), GetTime()); + rVal <<= aDateTime.GetUNODateTime(); + } + break; + default: + return SwField::QueryValue(rVal, nWhichId); + } + return true; +} + +bool SwDateTimeField::PutValue( const uno::Any& rVal, sal_uInt16 nWhichId ) +{ + sal_Int32 nTmp = 0; + switch( nWhichId ) + { + case FIELD_PROP_BOOL1: + if(*o3tl::doAccess<bool>(rVal)) + m_nSubType |= FIXEDFLD; + else + m_nSubType &= ~FIXEDFLD; + break; + case FIELD_PROP_BOOL2: + m_nSubType &= ~(DATEFLD|TIMEFLD); + m_nSubType |= *o3tl::doAccess<bool>(rVal) ? DATEFLD : TIMEFLD; + break; + case FIELD_PROP_FORMAT: + rVal >>= nTmp; + ChangeFormat(nTmp); + break; + case FIELD_PROP_SUBTYPE: + rVal >>= nTmp; + m_nOffset = nTmp; + break; + case FIELD_PROP_DATE_TIME: + { + util::DateTime aDateTimeValue; + if(!(rVal >>= aDateTimeValue)) + return false; + DateTime aDateTime(aDateTimeValue); + SetDateTime(aDateTime); + } + break; + default: + return SwField::PutValue(rVal, nWhichId); + } + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/flddropdown.cxx b/sw/source/core/fields/flddropdown.cxx new file mode 100644 index 000000000..57106978e --- /dev/null +++ b/sw/source/core/fields/flddropdown.cxx @@ -0,0 +1,217 @@ +/* -*- 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 <flddropdown.hxx> + +#include <algorithm> + +#include <svl/poolitem.hxx> +#include <comphelper/sequence.hxx> + +#include <unofldmid.h> + +using namespace com::sun::star; + +using std::vector; + +SwDropDownFieldType::SwDropDownFieldType() + : SwFieldType(SwFieldIds::Dropdown) +{ +} + +SwDropDownFieldType::~SwDropDownFieldType() +{ +} + +std::unique_ptr<SwFieldType> SwDropDownFieldType::Copy() const +{ + return std::make_unique<SwDropDownFieldType>(); +} + +SwDropDownField::SwDropDownField(SwFieldType * pTyp) + : SwField(pTyp, 0, LANGUAGE_SYSTEM) +{ +} + +SwDropDownField::SwDropDownField(const SwDropDownField & rSrc) + : SwField(rSrc.GetTyp(), rSrc.GetFormat(), rSrc.GetLanguage()), + m_aValues(rSrc.m_aValues), m_aSelectedItem(rSrc.m_aSelectedItem), + m_aName(rSrc.m_aName), m_aHelp(rSrc.m_aHelp), m_aToolTip(rSrc.m_aToolTip) +{ +} + +SwDropDownField::~SwDropDownField() +{ +} + +OUString SwDropDownField::ExpandImpl(SwRootFrame const*const) const +{ + OUString sSelect = GetSelectedItem(); + if (sSelect.isEmpty()) + { + vector<OUString>::const_iterator aIt = m_aValues.begin(); + if ( aIt != m_aValues.end()) + sSelect = *aIt; + } + // if still no list value is available a default text of 10 spaces is to be set + if (sSelect.isEmpty()) + sSelect = " "; + return sSelect; +} + +std::unique_ptr<SwField> SwDropDownField::Copy() const +{ + return std::make_unique<SwDropDownField>(*this); +} + +OUString SwDropDownField::GetPar1() const +{ + return GetSelectedItem(); +} + +OUString SwDropDownField::GetPar2() const +{ + return GetName(); +} + +void SwDropDownField::SetPar1(const OUString & rStr) +{ + SetSelectedItem(rStr); +} + +void SwDropDownField::SetPar2(const OUString & rName) +{ + SetName(rName); +} + +void SwDropDownField::SetItems(vector<OUString> && rItems) +{ + m_aValues = std::move(rItems); + m_aSelectedItem.clear(); +} + +void SwDropDownField::SetItems(const uno::Sequence<OUString> & rItems) +{ + m_aValues.clear(); + + comphelper::sequenceToContainer(m_aValues, rItems); + + m_aSelectedItem.clear(); +} + +uno::Sequence<OUString> SwDropDownField::GetItemSequence() const +{ + return comphelper::containerToSequence(m_aValues); +} + + +void SwDropDownField::SetSelectedItem(const OUString & rItem) +{ + vector<OUString>::const_iterator aIt = + std::find(m_aValues.begin(), m_aValues.end(), rItem); + + if (aIt != m_aValues.end()) + m_aSelectedItem = *aIt; + else + m_aSelectedItem.clear(); +} + +void SwDropDownField::SetName(const OUString & rName) +{ + m_aName = rName; +} + +void SwDropDownField::SetHelp(const OUString & rHelp) +{ + m_aHelp = rHelp; +} + +void SwDropDownField::SetToolTip(const OUString & rToolTip) +{ + m_aToolTip = rToolTip; +} + +bool SwDropDownField::QueryValue(::uno::Any &rVal, sal_uInt16 nWhich) const +{ + nWhich &= ~CONVERT_TWIPS; + switch( nWhich ) + { + case FIELD_PROP_PAR1: + rVal <<= m_aSelectedItem; + break; + case FIELD_PROP_PAR2: + rVal <<= m_aName; + break; + case FIELD_PROP_PAR3: + rVal <<= m_aHelp; + break; + case FIELD_PROP_PAR4: + rVal <<= m_aToolTip; + break; + case FIELD_PROP_STRINGS: + rVal <<= GetItemSequence(); + break; + + default: + assert(false); + } + return true; +} + +bool SwDropDownField::PutValue(const uno::Any &rVal, + sal_uInt16 nWhich) +{ + switch( nWhich ) + { + case FIELD_PROP_PAR1: + { + OUString aTmpStr; + rVal >>= aTmpStr; + + SetSelectedItem(aTmpStr); + } + break; + + case FIELD_PROP_PAR2: + rVal >>= m_aName; + break; + + case FIELD_PROP_PAR3: + rVal >>= m_aHelp; + break; + + case FIELD_PROP_PAR4: + rVal >>= m_aToolTip; + break; + + case FIELD_PROP_STRINGS: + { + uno::Sequence<OUString> aSeq; + rVal >>= aSeq; + SetItems(aSeq); + } + break; + + default: + assert(false); + } + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/fldlst.cxx b/sw/source/core/fields/fldlst.cxx new file mode 100644 index 000000000..24eb3e1a4 --- /dev/null +++ b/sw/source/core/fields/fldlst.cxx @@ -0,0 +1,151 @@ +/* -*- 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 <editsh.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <docary.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <expfld.hxx> +#include <docfld.hxx> +#include <ndtxt.hxx> + +#include <osl/diagnose.h> + +// sort input values + +SwInputFieldList::SwInputFieldList( SwEditShell* pShell, bool bBuildTmpLst ) + : mpSh(pShell) +{ + // create sorted list of all input fields + mpSrtLst.reset( new SetGetExpFields ); + + const SwFieldTypes& rFieldTypes = *mpSh->GetDoc()->getIDocumentFieldsAccess().GetFieldTypes(); + const size_t nSize = rFieldTypes.size(); + + // iterate over all types + std::vector<SwFormatField*> vFields; + for(size_t i = 0; i < nSize; ++i) + { + SwFieldType* pFieldType = rFieldTypes[ i ].get(); + const SwFieldIds nType = pFieldType->Which(); + if(SwFieldIds::SetExp == nType || SwFieldIds::Input == nType || SwFieldIds::Dropdown == nType) + pFieldType->GatherFields(vFields); + } + for(auto pFormatField: vFields) + { + auto pSetExpField = dynamic_cast<SwSetExpField*>(pFormatField->GetField()); + if(pSetExpField && !pSetExpField->GetInputFlag()) + continue; + const SwTextField* pTextField = pFormatField->GetTextField(); + if(bBuildTmpLst) + maTmpLst.insert(pTextField); + else + { + SwNodeIndex aIdx(pTextField->GetTextNode()); + std::unique_ptr<SetGetExpField> pNew(new SetGetExpField(aIdx, pTextField)); + mpSrtLst->insert(std::move(pNew)); + } + } +} + +SwInputFieldList::~SwInputFieldList() +{ +} + +size_t SwInputFieldList::Count() const +{ + return mpSrtLst->size(); +} + +// get field from list in sorted order +SwField* SwInputFieldList::GetField(size_t nId) +{ + const SwTextField* pTextField = (*mpSrtLst)[ nId ]->GetTextField(); + OSL_ENSURE( pTextField, "no TextField" ); + return const_cast<SwField*>(pTextField->GetFormatField().GetField()); +} + +/// save cursor +void SwInputFieldList::PushCursor() +{ + mpSh->Push(); + mpSh->ClearMark(); +} + +/// get cursor +void SwInputFieldList::PopCursor() +{ + mpSh->Pop(SwCursorShell::PopMode::DeleteCurrent); +} + +/// go to position of a field +void SwInputFieldList::GotoFieldPos(size_t nId) +{ + mpSh->StartAllAction(); + (*mpSrtLst)[ nId ]->GetPosOfContent( *mpSh->GetCursor()->GetPoint() ); + mpSh->EndAllAction(); +} + +/** Compare TmpLst with current fields. + * + * All new ones are added to SortList so that they can be updated. + * For text blocks: update only input fields. + * + * @return true if not empty + */ +bool SwInputFieldList::BuildSortLst() +{ + const SwFieldTypes& rFieldTypes = *mpSh->GetDoc()->getIDocumentFieldsAccess().GetFieldTypes(); + const size_t nSize = rFieldTypes.size(); + + // iterate over all types + std::vector<SwFormatField*> vFields; + for(size_t i = 0; i < nSize; ++i) + { + SwFieldType* pFieldType = rFieldTypes[ i ].get(); + const SwFieldIds nType = pFieldType->Which(); + if(SwFieldIds::SetExp == nType || SwFieldIds::Input == nType) + pFieldType->GatherFields(vFields); + } + for(auto pFormatField: vFields) + { + auto pSetExpField = dynamic_cast<SwSetExpField*>(pFormatField->GetField()); + if(pSetExpField && !pSetExpField->GetInputFlag()) + continue; + const SwTextField* pTextField = pFormatField->GetTextField(); + // not in TempList, thus add to SortList + auto it = maTmpLst.find(pTextField); + if(maTmpLst.end() == it) + { + SwNodeIndex aIdx(pTextField->GetTextNode()); + std::unique_ptr<SetGetExpField> pNew(new SetGetExpField(aIdx, pTextField )); + mpSrtLst->insert(std::move(pNew)); + } + else + maTmpLst.erase(it); + } + + // the pointers are not needed anymore + maTmpLst.clear(); + return !mpSrtLst->empty(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/macrofld.cxx b/sw/source/core/fields/macrofld.cxx new file mode 100644 index 000000000..2430a859a --- /dev/null +++ b/sw/source/core/fields/macrofld.cxx @@ -0,0 +1,222 @@ +/* -*- 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 <doc.hxx> +#include <docufld.hxx> +#include <unofldmid.h> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp> +#include <comphelper/processfactory.hxx> +#include <osl/diagnose.h> + +using namespace ::com::sun::star; + +SwMacroFieldType::SwMacroFieldType(SwDoc& rDocument) + : SwFieldType( SwFieldIds::Macro ) + , m_rDoc(rDocument) +{ +} + +std::unique_ptr<SwFieldType> SwMacroFieldType::Copy() const +{ + return std::make_unique<SwMacroFieldType>(m_rDoc); +} + +SwMacroField::SwMacroField(SwMacroFieldType* pInitType, + const OUString& rLibAndName, const OUString& rText) : + SwField(pInitType), m_aMacro(rLibAndName), m_aText(rText), m_bIsScriptURL(false) +{ + m_bIsScriptURL = isScriptURL(m_aMacro); +} + +OUString SwMacroField::ExpandImpl(SwRootFrame const*const) const +{ + return m_aText ; +} + +std::unique_ptr<SwField> SwMacroField::Copy() const +{ + return std::make_unique<SwMacroField>(static_cast<SwMacroFieldType*>(GetTyp()), m_aMacro, m_aText); +} + +OUString SwMacroField::GetFieldName() const +{ + return GetTyp()->GetName() + " " + m_aMacro; +} + +OUString SwMacroField::GetLibName() const +{ + // if it is a Scripting Framework macro return an empty string + if (m_bIsScriptURL) + { + return OUString(); + } + + if (!m_aMacro.isEmpty()) + { + sal_Int32 nPos = m_aMacro.getLength(); + + for (sal_Int32 i = 0; i < 3 && nPos > 0; i++) + while (m_aMacro[--nPos] != '.' && nPos > 0) ; + + return m_aMacro.copy(0, nPos); + } + + OSL_FAIL("No LibName"); + return OUString(); +} + +OUString SwMacroField::GetMacroName() const +{ + if (!m_aMacro.isEmpty()) + { + if (m_bIsScriptURL) + { + return m_aMacro; + } + else + { + sal_Int32 nPos = m_aMacro.getLength(); + + for (sal_Int32 i = 0; i < 3 && nPos > 0; i++) + while (m_aMacro[--nPos] != '.' && nPos > 0) ; + + return m_aMacro.copy( ++nPos ); + } + } + + OSL_FAIL("No MacroName"); + return OUString(); +} + +SvxMacro SwMacroField::GetSvxMacro() const +{ + if (m_bIsScriptURL) + { + return SvxMacro(m_aMacro, OUString(), EXTENDED_STYPE); + } + else + { + return SvxMacro(GetMacroName(), GetLibName(), STARBASIC); + } +} + +/// LibName and MacroName +void SwMacroField::SetPar1(const OUString& rStr) +{ + m_aMacro = rStr; + m_bIsScriptURL = isScriptURL(m_aMacro); +} + +/// Get macro +OUString SwMacroField::GetPar1() const +{ + return m_aMacro; +} + +/// set macro text +void SwMacroField::SetPar2(const OUString& rStr) +{ + m_aText = rStr; +} + +/// get macro text +OUString SwMacroField::GetPar2() const +{ + return m_aText; +} + +bool SwMacroField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= GetMacroName(); + break; + case FIELD_PROP_PAR2: + rAny <<= m_aText; + break; + case FIELD_PROP_PAR3: + rAny <<= GetLibName(); + break; + case FIELD_PROP_PAR4: + rAny <<= m_bIsScriptURL ? GetMacroName() : OUString(); + break; + default: + assert(false); + } + return true; +} + +bool SwMacroField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + OUString sTmp; + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny >>= sTmp; + CreateMacroString( m_aMacro, sTmp, GetLibName()); + break; + case FIELD_PROP_PAR2: + rAny >>= m_aText; + break; + case FIELD_PROP_PAR3: + rAny >>= sTmp; + CreateMacroString(m_aMacro, GetMacroName(), sTmp ); + break; + case FIELD_PROP_PAR4: + rAny >>= m_aMacro; + m_bIsScriptURL = isScriptURL(m_aMacro); + break; + default: + assert(false); + } + + return true; +} + +/// create an internally used macro name from the library and macro name parts +void SwMacroField::CreateMacroString( + OUString& rMacro, + std::u16string_view rMacroName, + const OUString& rLibraryName ) +{ + // concatenate library and name; use dot only if both strings have content + rMacro = rLibraryName; + if ( !rLibraryName.isEmpty() && !rMacroName.empty() ) + rMacro += "."; + rMacro += rMacroName; +} + +bool SwMacroField::isScriptURL( const OUString& str ) +{ + try + { + uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext(); + uno::Reference<uri::XUriReferenceFactory> xFactory = uri::UriReferenceFactory::create(xContext); + uno::Reference<uri::XVndSunStarScriptUrl> xUrl(xFactory->parse(str), uno::UNO_QUERY); + return xUrl.is(); + } + catch (...) + { + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/postithelper.cxx b/sw/source/core/fields/postithelper.cxx new file mode 100644 index 000000000..26d81b373 --- /dev/null +++ b/sw/source/core/fields/postithelper.cxx @@ -0,0 +1,279 @@ +/* -*- 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 <postithelper.hxx> +#include <PostItMgr.hxx> +#include <AnnotationWin.hxx> + +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <ndtxt.hxx> +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <txtfrm.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <redline.hxx> +#include <scriptinfo.hxx> +#include <calbck.hxx> +#include <IMark.hxx> +#include <sortedobjs.hxx> +#include <anchoredobject.hxx> +#include <fmtanchr.hxx> + +class Point; + +namespace +{ +/// Checks if pAnnotationMark covers exactly rAnchorPos (the comment anchor). +bool AnnotationMarkCoversCommentAnchor(const sw::mark::IMark* pAnnotationMark, + const SwPosition& rAnchorPos) +{ + if (!pAnnotationMark) + { + return false; + } + + const SwPosition& rMarkStart = pAnnotationMark->GetMarkStart(); + const SwPosition& rMarkEnd = pAnnotationMark->GetMarkEnd(); + + if (rMarkStart != rAnchorPos) + { + // This can be the as-char case: the comment placeholder character is exactly between the + // annotation mark start and end. + SwPosition aPosition(rMarkStart); + ++aPosition.nContent; + if (aPosition != rAnchorPos) + { + return false; + } + + ++aPosition.nContent; + if (aPosition != rMarkEnd) + { + return false; + } + + return true; + } + + if (rMarkStart.nNode != rMarkEnd.nNode) + { + return false; + } + + return rMarkEnd.nContent.GetIndex() == rMarkStart.nContent.GetIndex() + 1; +} + +/** + * Finds the first draw object of rTextFrame which has the same anchor position as the start of + * rAnnotationMark. + */ +SwAnchoredObject* GetAnchoredObjectOfAnnotationMark(const sw::mark::IMark& rAnnotationMark, + const SwTextFrame& rTextFrame) +{ + const SwSortedObjs* pAnchored = rTextFrame.GetDrawObjs(); + if (!pAnchored) + { + return nullptr; + } + + for (SwAnchoredObject* pObject : *pAnchored) + { + SwFrameFormat& rFrameFormat = pObject->GetFrameFormat(); + const SwPosition* pFrameAnchor = rFrameFormat.GetAnchor().GetContentAnchor(); + if (!pFrameAnchor) + { + continue; + } + + if (rAnnotationMark.GetMarkStart() == *pFrameAnchor) + { + return pObject; + } + } + + return nullptr; +} +} + +SwSidebarItem::SwSidebarItem(const bool aFocus) + : mpPostIt(nullptr) + , mbShow(true) + , mbFocus(aFocus) + , mbPendingLayout(false) + , mLayoutStatus(SwPostItHelper::INVISIBLE) +{ +} + +SwSidebarItem::~SwSidebarItem() {} + +SwPostItHelper::SwLayoutStatus SwPostItHelper::getLayoutInfos( + SwLayoutInfo& o_rInfo, + const SwPosition& rAnchorPos, + const sw::mark::IMark* pAnnotationMark ) +{ + SwLayoutStatus aRet = INVISIBLE; + SwTextNode* pTextNode = rAnchorPos.nNode.GetNode().GetTextNode(); + if ( pTextNode == nullptr ) + return aRet; + + SwIterator<SwTextFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode); + for( SwTextFrame* pTextFrame = aIter.First(); pTextFrame != nullptr; pTextFrame = aIter.Next() ) + { + if( !pTextFrame->IsFollow() ) + { + pTextFrame = pTextFrame->GetFrameAtPos( rAnchorPos ); + SwPageFrame *pPage = pTextFrame ? pTextFrame->FindPageFrame() : nullptr; + if ( pPage != nullptr && !pPage->IsInvalid() && !pPage->IsInvalidFly() ) + { + aRet = VISIBLE; + + o_rInfo.mpAnchorFrame = pTextFrame; + { + DisableCallbackAction a(*pTextFrame->getRootFrame()); + bool bPositionFromCommentAnchor = true; + if (AnnotationMarkCoversCommentAnchor(pAnnotationMark, rAnchorPos)) + { + SwAnchoredObject* pFrame + = GetAnchoredObjectOfAnnotationMark(*pAnnotationMark, *pTextFrame); + if (pFrame) + { + o_rInfo.mPosition = pFrame->GetObjRect(); + bPositionFromCommentAnchor = false; + } + } + if (bPositionFromCommentAnchor) + { + pTextFrame->GetCharRect(o_rInfo.mPosition, rAnchorPos, nullptr, false); + } + o_rInfo.mPositionFromCommentAnchor = bPositionFromCommentAnchor; + } + if (pAnnotationMark != nullptr) + { + const SwPosition& rAnnotationStartPos = pAnnotationMark->GetMarkStart(); + o_rInfo.mnStartNodeIdx = rAnnotationStartPos.nNode.GetIndex(); + o_rInfo.mnStartContent = rAnnotationStartPos.nContent.GetIndex(); + } + else + { + o_rInfo.mnStartNodeIdx = SwNodeOffset(0); + o_rInfo.mnStartContent = -1; + } + o_rInfo.mPageFrame = pPage->getFrameArea(); + o_rInfo.mPagePrtArea = pPage->getFramePrintArea(); + o_rInfo.mPagePrtArea.Pos() += o_rInfo.mPageFrame.Pos(); + o_rInfo.mnPageNumber = pPage->GetPhyPageNum(); + o_rInfo.meSidebarPosition = pPage->SidebarPosition(); + o_rInfo.mRedlineAuthor = 0; + + const IDocumentRedlineAccess& rIDRA = pTextNode->getIDocumentRedlineAccess(); + if( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) ) + { + const SwRangeRedline* pRedline = rIDRA.GetRedline( rAnchorPos, nullptr ); + if( pRedline ) + { + if( RedlineType::Insert == pRedline->GetType() ) + aRet = INSERTED; + else if( RedlineType::Delete == pRedline->GetType() ) + { + bool bDeleted = pAnnotationMark == nullptr; + if( !bDeleted ) + { + IDocumentMarkAccess& rDMA(*pTextNode->GetDoc().getIDocumentMarkAccess()); + IDocumentMarkAccess::const_iterator_t pAnnotationBookmark = + rDMA.findAnnotationBookmark(pAnnotationMark->GetName()); + // tdf#140980 only really deleted, if there is no helper bookmark + // in ChangesInMargin mode + if ( pAnnotationBookmark == rDMA.getBookmarksEnd() ) + bDeleted = true; + } + if ( bDeleted ) + aRet = DELETED; + } + o_rInfo.mRedlineAuthor = pRedline->GetAuthor(); + } + } + } + } + } + + return ( (aRet==VISIBLE) && SwScriptInfo::IsInHiddenRange( *pTextNode , rAnchorPos.nContent.GetIndex()) ) + ? HIDDEN + : aRet; +} + +tools::Long SwPostItHelper::getLayoutHeight( const SwRootFrame* pRoot ) +{ + tools::Long nRet = pRoot ? pRoot->getFrameArea().Height() : 0; + return nRet; +} + +void SwPostItHelper::setSidebarChanged( SwRootFrame* pRoot, bool bBrowseMode ) +{ + if( pRoot ) + { + pRoot->SetSidebarChanged(); + if( bBrowseMode ) + pRoot->InvalidateBrowseWidth(); + } +} + +tools::ULong SwPostItHelper::getPageInfo( SwRect& rPageFrame, const SwRootFrame* pRoot, const Point& rPoint ) +{ + tools::ULong nRet = 0; + const SwFrame* pPage = pRoot->GetPageAtPos( rPoint, nullptr, true ); + if( pPage ) + { + nRet = pPage->GetPhyPageNum(); + rPageFrame = pPage->getFrameArea(); + } + return nRet; +} + +SwPosition SwAnnotationItem::GetAnchorPosition() const +{ + SwTextField* pTextField = mrFormatField.GetTextField(); + SwTextNode* pTextNode = pTextField->GetpTextNode(); + + SwPosition aPos( *pTextNode ); + aPos.nContent.Assign( pTextNode, pTextField->GetStart() ); + return aPos; +} + +bool SwAnnotationItem::UseElement(SwRootFrame const& rLayout, + IDocumentRedlineAccess const& rIDRA) +{ + return mrFormatField.IsFieldInDoc() + && (!rLayout.IsHideRedlines() + || !sw::IsFieldDeletedInModel(rIDRA, *mrFormatField.GetTextField())); +} + +VclPtr<sw::annotation::SwAnnotationWin> SwAnnotationItem::GetSidebarWindow( + SwEditWin& rEditWin, + SwPostItMgr& aMgr) +{ + return VclPtr<sw::annotation::SwAnnotationWin>::Create( rEditWin, + aMgr, + *this, + &mrFormatField ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/reffld.cxx b/sw/source/core/fields/reffld.cxx new file mode 100644 index 000000000..a79c314a3 --- /dev/null +++ b/sw/source/core/fields/reffld.cxx @@ -0,0 +1,1500 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/text/ReferenceFieldPart.hpp> +#include <com/sun/star/text/ReferenceFieldSource.hpp> +#include <o3tl/unreachable.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/charclass.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <pam.hxx> +#include <cntfrm.hxx> +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <modeltoviewhelper.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <txtftn.hxx> +#include <fmtrfmrk.hxx> +#include <txtrfmrk.hxx> +#include <fmtftn.hxx> +#include <ndtxt.hxx> +#include <chpfld.hxx> +#include <reffld.hxx> +#include <expfld.hxx> +#include <txtfrm.hxx> +#include <flyfrm.hxx> +#include <pagedesc.hxx> +#include <IMark.hxx> +#include <crossrefbookmark.hxx> +#include <ftnidx.hxx> +#include <viewsh.hxx> +#include <unofldmid.h> +#include <SwStyleNameMapper.hxx> +#include <shellres.hxx> +#include <poolfmt.hxx> +#include <strings.hrc> +#include <numrule.hxx> +#include <SwNodeNum.hxx> +#include <calbck.hxx> + +#include <cstddef> +#include <memory> +#include <vector> +#include <set> +#include <string_view> +#include <map> +#include <algorithm> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::lang; + +static std::pair<OUString, bool> MakeRefNumStr(SwRootFrame const* pLayout, + const SwTextNode& rTextNodeOfField, + const SwTextNode& rTextNodeOfReferencedItem, + sal_uInt32 nRefNumFormat); + +static void lcl_GetLayTree( const SwFrame* pFrame, std::vector<const SwFrame*>& rArr ) +{ + while( pFrame ) + { + if( pFrame->IsBodyFrame() ) // unspectacular + pFrame = pFrame->GetUpper(); + else + { + rArr.push_back( pFrame ); + + // this is the last page + if( pFrame->IsPageFrame() ) + break; + + if( pFrame->IsFlyFrame() ) + pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame(); + else + pFrame = pFrame->GetUpper(); + } + } +} + +bool IsFrameBehind( const SwTextNode& rMyNd, sal_Int32 nMySttPos, + const SwTextNode& rBehindNd, sal_Int32 nSttPos ) +{ + const SwTextFrame * pMyFrame = static_cast<SwTextFrame*>(rMyNd.getLayoutFrame( + rMyNd.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)); + const SwTextFrame * pFrame = static_cast<SwTextFrame*>(rBehindNd.getLayoutFrame( + rBehindNd.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)); + + if( !pFrame || !pMyFrame) + return false; + + TextFrameIndex const nMySttPosIndex(pMyFrame->MapModelToView(&rMyNd, nMySttPos)); + TextFrameIndex const nSttPosIndex(pFrame->MapModelToView(&rBehindNd, nSttPos)); + while (pFrame && !pFrame->IsInside(nSttPosIndex)) + pFrame = pFrame->GetFollow(); + while (pMyFrame && !pMyFrame->IsInside(nMySttPosIndex)) + pMyFrame = pMyFrame->GetFollow(); + + if( !pFrame || !pMyFrame || pFrame == pMyFrame ) + return false; + + std::vector<const SwFrame*> aRefArr, aArr; + ::lcl_GetLayTree( pFrame, aRefArr ); + ::lcl_GetLayTree( pMyFrame, aArr ); + + size_t nRefCnt = aRefArr.size() - 1, nCnt = aArr.size() - 1; + bool bVert = false; + bool bR2L = false; + + // Loop as long as a frame does not equal? + while( nRefCnt && nCnt && aRefArr[ nRefCnt ] == aArr[ nCnt ] ) + { + const SwFrame* pTmpFrame = aArr[ nCnt ]; + bVert = pTmpFrame->IsVertical(); + bR2L = pTmpFrame->IsRightToLeft(); + --nCnt; + --nRefCnt; + } + + // If a counter overflows? + if( aRefArr[ nRefCnt ] == aArr[ nCnt ] ) + { + if( nCnt ) + --nCnt; + else + --nRefCnt; + } + + const SwFrame* pRefFrame = aRefArr[ nRefCnt ]; + const SwFrame* pFieldFrame = aArr[ nCnt ]; + + // different frames, check their Y-/X-position + bool bRefIsLower = false; + if( ( SwFrameType::Column | SwFrameType::Cell ) & pFieldFrame->GetType() || + ( SwFrameType::Column | SwFrameType::Cell ) & pRefFrame->GetType() ) + { + if( pFieldFrame->GetType() == pRefFrame->GetType() ) + { + // here, the X-pos is more important + if( bVert ) + { + if( bR2L ) + bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || + ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && + pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() ); + else + bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || + ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && + pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() ); + } + else if( bR2L ) + bRefIsLower = pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() || + ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && + pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); + else + bRefIsLower = pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() || + ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && + pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); + pRefFrame = nullptr; + } + else if( ( SwFrameType::Column | SwFrameType::Cell ) & pFieldFrame->GetType() ) + pFieldFrame = aArr[ nCnt - 1 ]; + else + pRefFrame = aRefArr[ nRefCnt - 1 ]; + } + + if( pRefFrame ) // misuse as flag + { + if( bVert ) + { + if( bR2L ) + bRefIsLower = pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() || + ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && + pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); + else + bRefIsLower = pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() || + ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() && + pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ); + } + else if( bR2L ) + bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || + ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && + pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() ); + else + bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() || + ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() && + pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() ); + } + return bRefIsLower; +} + +// tdf#115319 create alternative reference formats, if the user asked for it +// (ReferenceFieldLanguage attribute of the reference field is not empty), and +// language of the text and ReferenceFieldLanguage are the same. +// Right now only HUNGARIAN seems to need this (as in the related issue, +// the reversed caption order in autocaption, solved by #i61007#) +static void lcl_formatReferenceLanguage( OUString& rRefText, + bool bClosingParenthesis, LanguageType eLang, + 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, + const OUString& rSetRef, const OUString& rSetReferenceLanguage, sal_uInt16 nSubTyp, + sal_uInt16 nSequenceNo, sal_uLong nFormat ) + : SwField( pFieldType, nFormat ), + m_sSetRefName( rSetRef ), + m_sSetReferenceLanguage( rSetReferenceLanguage ), + m_nSubType( nSubTyp ), + m_nSeqNo( nSequenceNo ) +{ +} + +SwGetRefField::~SwGetRefField() +{ +} + +OUString SwGetRefField::GetDescription() const +{ + return SwResId(STR_REFERENCE); +} + +sal_uInt16 SwGetRefField::GetSubType() const +{ + return m_nSubType; +} + +void SwGetRefField::SetSubType( sal_uInt16 n ) +{ + m_nSubType = n; +} + +// #i81002# +bool SwGetRefField::IsRefToHeadingCrossRefBookmark() const +{ + return GetSubType() == REF_BOOKMARK && + ::sw::mark::CrossRefHeadingBookmark::IsLegalName(m_sSetRefName); +} + +bool SwGetRefField::IsRefToNumItemCrossRefBookmark() const +{ + return GetSubType() == REF_BOOKMARK && + ::sw::mark::CrossRefNumItemBookmark::IsLegalName(m_sSetRefName); +} + +const SwTextNode* SwGetRefField::GetReferencedTextNode() const +{ + SwGetRefFieldType *pTyp = dynamic_cast<SwGetRefFieldType*>(GetTyp()); + if (!pTyp) + return nullptr; + sal_Int32 nDummy = -1; + return SwGetRefFieldType::FindAnchor( &pTyp->GetDoc(), m_sSetRefName, m_nSubType, m_nSeqNo, &nDummy ); +} + +// strikethrough for tooltips using Unicode combining character +static OUString lcl_formatStringByCombiningCharacter(const OUString& sText, const sal_Unicode cChar) +{ + OUStringBuffer sRet; + for (sal_Int32 i = 0; i < sText.getLength(); ++i) + { + sRet.append(sText[i]); + sRet.append(cChar); + } + return sRet.makeStringAndClear(); +} + +// #i85090# +OUString SwGetRefField::GetExpandedTextOfReferencedTextNode( + SwRootFrame const& rLayout) const +{ + const SwTextNode* pReferencedTextNode( GetReferencedTextNode() ); + 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 ) +{ + m_sText.clear(); + m_sTextRLHidden.clear(); + + SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc(); + // 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, &nNumStart, &nNumEnd + ); + // not found? + if ( !pTextNd ) + { + m_sText = SwViewShell::GetShellRes()->aGetRefField_RefItemNotFound; + m_sTextRLHidden = m_sText; + return ; + } + + SwRootFrame const* pLayout(nullptr); + SwRootFrame const* pLayoutRLHidden(nullptr); + for (SwRootFrame const*const pLay : rDoc.GetAllLayouts()) + { + if (pLay->IsHideRedlines()) + { + pLayoutRLHidden = pLay; + } + else + { + pLayout = pLay; + } + } + + // where is the category name (e.g. "Illustration")? + const OUString aText = pTextNd->GetText(); + const sal_Int32 nCatStart = aText.indexOf(m_sSetRefName); + const bool bHasCat = nCatStart>=0; + const sal_Int32 nCatEnd = bHasCat ? nCatStart + m_sSetRefName.getLength() : -1; + + // length of the referenced text + const sal_Int32 nLen = aText.getLength(); + + // which format? + switch( GetFormat() ) + { + case REF_CONTENT: + case REF_ONLYNUMBER: + case REF_ONLYCAPTION: + case REF_ONLYSEQNO: + { + // needed part of Text + sal_Int32 nStart; + sal_Int32 nEnd; + + switch( m_nSubType ) + { + case REF_SEQUENCEFLD: + + switch( GetFormat() ) + { + // "Category and Number" + case REF_ONLYNUMBER: + if (bHasCat) { + nStart = std::min(nNumStart, nCatStart); + nEnd = std::max(nNumEnd, nCatEnd); + } else { + nStart = nNumStart; + nEnd = nNumEnd; + } + break; + + // "Caption Text" + case REF_ONLYCAPTION: { + // next alphanumeric character after category+number + if (const SwTextAttr* const pTextAttr = + pTextNd->GetTextAttrForCharAt(nNumStart, RES_TXTATR_FIELD) + ) { + // start searching from nFrom + const sal_Int32 nFrom = bHasCat + ? std::max(nNumStart + 1, nCatEnd) + : nNumStart + 1; + nStart = SwGetExpField::GetReferenceTextPos( pTextAttr->GetFormatField(), 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() ) + { + m_sText = pFootnoteIdx->GetFootnote().GetViewNumStr(rDoc, nullptr); + m_sTextRLHidden = pFootnoteIdx->GetFootnote().GetViewNumStr(rDoc, pLayoutRLHidden); + if (!m_sSetReferenceLanguage.isEmpty()) + { + lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage); + lcl_formatReferenceLanguage(m_sTextRLHidden, false, GetLanguage(), m_sSetReferenceLanguage); + } + break; + } + } + return; + + case REF_SETREFATTR: + nStart = nNumStart; + nEnd = nNumEnd; + break; + + default: + O3TL_UNREACHABLE; + } + + if( nStart != nEnd ) // a section? + { + m_sText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, false, ExpandMode::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 ( m_sText.isEmpty() ) + m_sText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, false, ExpandMode(0)); + + if (m_nSubType == REF_OUTLINE + || (m_nSubType == REF_SEQUENCEFLD && REF_CONTENT == GetFormat())) + { + m_sTextRLHidden = sw::GetExpandTextMerged( + pLayoutRLHidden, *pTextNd, false, false, ExpandMode(0)); + } + else + { + m_sTextRLHidden = pTextNd->GetExpandText(pLayoutRLHidden, + nStart, nEnd - nStart, false, false, false, ExpandMode::HideDeletions); + } + FilterText(m_sText, GetLanguage(), m_sSetReferenceLanguage); + FilterText(m_sTextRLHidden, GetLanguage(), m_sSetReferenceLanguage); + } + } + break; + + case REF_PAGE: + case REF_PAGE_PGDESC: + { + auto const func = + [this, pTextNd, nNumStart](OUString & rText, SwRootFrame const*const pLay) + { + SwTextFrame const* pFrame = static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLay, nullptr, nullptr)); + SwTextFrame const*const pSave = pFrame; + if (pFrame) + { + TextFrameIndex const nNumStartIndex(pFrame->MapModelToView(pTextNd, nNumStart)); + while (pFrame && !pFrame->IsInside(nNumStartIndex)) + pFrame = pFrame->GetFollow(); + } + + if( pFrame || nullptr != ( pFrame = pSave )) + { + sal_uInt16 nPageNo = pFrame->GetVirtPageNum(); + const SwPageFrame *pPage; + if( REF_PAGE_PGDESC == GetFormat() && + nullptr != ( pPage = pFrame->FindPageFrame() ) && + pPage->GetPageDesc() ) + { + rText = pPage->GetPageDesc()->GetNumType().GetNumStr(nPageNo); + } + else + { + rText = OUString::number(nPageNo); + } + + if (!m_sSetReferenceLanguage.isEmpty()) + lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage); + } + }; + // sw_redlinehide: currently only one of these layouts will exist, + // so the getLayoutFrame will use the same frame in both cases + func(m_sText, pLayout); + func(m_sTextRLHidden, pLayoutRLHidden); + } + break; + + case REF_CHAPTER: + { + auto const func = + [this, pTextNd](OUString & rText, SwRootFrame const*const pLay) + { + // a bit tricky: search any frame + SwFrame const*const pFrame = pTextNd->getLayoutFrame(pLay); + if( pFrame ) + { + SwChapterFieldType aFieldTyp; + SwChapterField aField( &aFieldTyp, 0 ); + aField.SetLevel( MAXLEVEL - 1 ); + aField.ChangeExpansion( *pFrame, pTextNd, true ); + rText = aField.GetNumber(pLay); + + if (!m_sSetReferenceLanguage.isEmpty()) + lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage); + } + }; + func(m_sText, pLayout); + func(m_sTextRLHidden, pLayoutRLHidden); + } + break; + + case REF_UPDOWN: + { + // #i81002# + // simplified: use parameter <pFieldTextAttr> + if( !pFieldTextAttr || !pFieldTextAttr->GetpTextNode() ) + break; + + LocaleDataWrapper aLocaleData(( LanguageTag( GetLanguage() ) )); + + // first a "short" test - in case both are in the same node + if( pFieldTextAttr->GetpTextNode() == pTextNd ) + { + m_sText = nNumStart < pFieldTextAttr->GetStart() + ? aLocaleData.getAboveWord() + : aLocaleData.getBelowWord(); + m_sTextRLHidden = m_sText; + break; + } + + m_sText = ::IsFrameBehind( *pFieldTextAttr->GetpTextNode(), pFieldTextAttr->GetStart(), + *pTextNd, nNumStart ) + ? aLocaleData.getAboveWord() + : aLocaleData.getBelowWord(); + + if (!m_sSetReferenceLanguage.isEmpty()) + lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage); + + m_sTextRLHidden = m_sText; + } + break; + // #i81002# + case REF_NUMBER: + case REF_NUMBER_NO_CONTEXT: + case REF_NUMBER_FULL_CONTEXT: + { + if ( pFieldTextAttr && pFieldTextAttr->GetpTextNode() ) + { + auto result = + MakeRefNumStr(pLayout, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat()); + m_sText = result.first; + // for differentiation of Roman numbers and letters in Hungarian article handling + bool bClosingParenthesis = result.second; + if (!m_sSetReferenceLanguage.isEmpty()) + { + lcl_formatReferenceLanguage(m_sText, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage); + } + result = + MakeRefNumStr(pLayoutRLHidden, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat()); + m_sTextRLHidden = result.first; + bClosingParenthesis = result.second; + if (!m_sSetReferenceLanguage.isEmpty()) + { + lcl_formatReferenceLanguage(m_sTextRLHidden, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage); + } + } + } + break; + + default: + OSL_FAIL("<SwGetRefField::UpdateField(..)> - unknown format type"); + } +} + +// #i81002# +static std::pair<OUString, bool> MakeRefNumStr( + SwRootFrame const*const pLayout, + const SwTextNode& i_rTextNodeOfField, + const SwTextNode& i_rTextNodeOfReferencedItem, + const sal_uInt32 nRefNumFormat) +{ + SwTextNode const& rTextNodeOfField(pLayout + ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfField) + : i_rTextNodeOfField); + SwTextNode const& rTextNodeOfReferencedItem(pLayout + ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfReferencedItem) + : i_rTextNodeOfReferencedItem); + if ( rTextNodeOfReferencedItem.HasNumber(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 ), + rTextNodeOfReferencedItem.GetNumRule()->MakeNumString( + *(rTextNodeOfReferencedItem.GetNum(pLayout)), + true).endsWith(")") ); + } + + return std::make_pair(OUString(), false); +} + +std::unique_ptr<SwField> SwGetRefField::Copy() const +{ + std::unique_ptr<SwGetRefField> pField( new SwGetRefField( static_cast<SwGetRefFieldType*>(GetTyp()), + m_sSetRefName, m_sSetReferenceLanguage, m_nSubType, + m_nSeqNo, GetFormat() ) ); + pField->m_sText = m_sText; + pField->m_sTextRLHidden = m_sTextRLHidden; + return std::unique_ptr<SwField>(pField.release()); +} + +/// get reference name +OUString SwGetRefField::GetPar1() const +{ + return m_sSetRefName; +} + +/// set reference name +void SwGetRefField::SetPar1( const OUString& rName ) +{ + m_sSetRefName = rName; +} + +OUString SwGetRefField::GetPar2() const +{ + return ExpandImpl(nullptr); +} + +bool SwGetRefField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_USHORT1: + { + sal_Int16 nPart = 0; + switch(GetFormat()) + { + case REF_PAGE : nPart = ReferenceFieldPart::PAGE ; break; + case REF_CHAPTER : nPart = ReferenceFieldPart::CHAPTER ; break; + case REF_CONTENT : nPart = ReferenceFieldPart::TEXT ; break; + case REF_UPDOWN : nPart = ReferenceFieldPart::UP_DOWN ; break; + case REF_PAGE_PGDESC: nPart = ReferenceFieldPart::PAGE_DESC ; break; + case REF_ONLYNUMBER : nPart = ReferenceFieldPart::CATEGORY_AND_NUMBER ; break; + case REF_ONLYCAPTION: nPart = ReferenceFieldPart::ONLY_CAPTION ; break; + case REF_ONLYSEQNO : nPart = ReferenceFieldPart::ONLY_SEQUENCE_NUMBER; break; + // #i81002# + case REF_NUMBER: nPart = ReferenceFieldPart::NUMBER; break; + case REF_NUMBER_NO_CONTEXT: nPart = ReferenceFieldPart::NUMBER_NO_CONTEXT; break; + case REF_NUMBER_FULL_CONTEXT: nPart = ReferenceFieldPart::NUMBER_FULL_CONTEXT; break; + } + rAny <<= nPart; + } + break; + case FIELD_PROP_USHORT2: + { + sal_Int16 nSource = 0; + switch(m_nSubType) + { + case REF_SETREFATTR : nSource = ReferenceFieldSource::REFERENCE_MARK; break; + case REF_SEQUENCEFLD: nSource = ReferenceFieldSource::SEQUENCE_FIELD; break; + case REF_BOOKMARK : nSource = ReferenceFieldSource::BOOKMARK; break; + case REF_OUTLINE : OSL_FAIL("not implemented"); break; + case REF_FOOTNOTE : nSource = ReferenceFieldSource::FOOTNOTE; break; + case REF_ENDNOTE : nSource = ReferenceFieldSource::ENDNOTE; break; + } + rAny <<= nSource; + } + break; + case FIELD_PROP_PAR1: + { + OUString sTmp(GetPar1()); + if(REF_SEQUENCEFLD == m_nSubType) + { + sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( sTmp, SwGetPoolIdFromName::TxtColl ); + switch( nPoolId ) + { + case RES_POOLCOLL_LABEL_ABB: + case RES_POOLCOLL_LABEL_TABLE: + case RES_POOLCOLL_LABEL_FRAME: + case RES_POOLCOLL_LABEL_DRAWING: + case RES_POOLCOLL_LABEL_FIGURE: + SwStyleNameMapper::FillProgName(nPoolId, sTmp) ; + break; + } + } + rAny <<= sTmp; + } + break; + case FIELD_PROP_PAR3: + rAny <<= ExpandImpl(nullptr); + break; + case FIELD_PROP_PAR4: + rAny <<= m_sSetReferenceLanguage; + break; + case FIELD_PROP_SHORT1: + rAny <<= static_cast<sal_Int16>(m_nSeqNo); + break; + default: + assert(false); + } + return true; +} + +bool SwGetRefField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_USHORT1: + { + sal_Int16 nPart = 0; + rAny >>= nPart; + switch(nPart) + { + case ReferenceFieldPart::PAGE: nPart = REF_PAGE; break; + case ReferenceFieldPart::CHAPTER: nPart = REF_CHAPTER; break; + case ReferenceFieldPart::TEXT: nPart = REF_CONTENT; break; + case ReferenceFieldPart::UP_DOWN: nPart = REF_UPDOWN; break; + case ReferenceFieldPart::PAGE_DESC: nPart = REF_PAGE_PGDESC; break; + case ReferenceFieldPart::CATEGORY_AND_NUMBER: nPart = REF_ONLYNUMBER; break; + case ReferenceFieldPart::ONLY_CAPTION: nPart = REF_ONLYCAPTION; break; + case ReferenceFieldPart::ONLY_SEQUENCE_NUMBER : nPart = REF_ONLYSEQNO; break; + // #i81002# + case ReferenceFieldPart::NUMBER: nPart = REF_NUMBER; break; + case ReferenceFieldPart::NUMBER_NO_CONTEXT: nPart = REF_NUMBER_NO_CONTEXT; break; + case ReferenceFieldPart::NUMBER_FULL_CONTEXT: nPart = REF_NUMBER_FULL_CONTEXT; break; + default: return false; + } + SetFormat(nPart); + } + break; + case FIELD_PROP_USHORT2: + { + sal_Int16 nSource = 0; + rAny >>= nSource; + switch(nSource) + { + case ReferenceFieldSource::REFERENCE_MARK : m_nSubType = REF_SETREFATTR ; break; + case ReferenceFieldSource::SEQUENCE_FIELD : + { + if(REF_SEQUENCEFLD == m_nSubType) + break; + m_nSubType = REF_SEQUENCEFLD; + ConvertProgrammaticToUIName(); + } + break; + case ReferenceFieldSource::BOOKMARK : m_nSubType = REF_BOOKMARK ; break; + case ReferenceFieldSource::FOOTNOTE : m_nSubType = REF_FOOTNOTE ; break; + case ReferenceFieldSource::ENDNOTE : m_nSubType = REF_ENDNOTE ; break; + } + } + break; + case FIELD_PROP_PAR1: + { + OUString sTmpStr; + rAny >>= sTmpStr; + SetPar1(sTmpStr); + ConvertProgrammaticToUIName(); + } + break; + case FIELD_PROP_PAR3: + { + OUString sTmpStr; + rAny >>= sTmpStr; + SetExpand( sTmpStr ); + } + break; + case FIELD_PROP_PAR4: + rAny >>= m_sSetReferenceLanguage; + break; + case FIELD_PROP_SHORT1: + { + sal_Int16 nSetSeq = 0; + rAny >>= nSetSeq; + if(nSetSeq >= 0) + m_nSeqNo = nSetSeq; + } + break; + default: + assert(false); + } + return true; +} + +void SwGetRefField::ConvertProgrammaticToUIName() +{ + if(!(GetTyp() && REF_SEQUENCEFLD == m_nSubType)) + 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()); + } + 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 + +SwTextNode* SwGetRefFieldType::FindAnchor( SwDoc* pDoc, const OUString& rRefMark, + sal_uInt16 nSubType, sal_uInt16 nSeqNo, + sal_Int32* pStt, sal_Int32* pEnd, + SwRootFrame const*const pLayout) +{ + OSL_ENSURE( pStt, "Why did no one check the StartPos?" ); + + IDocumentRedlineAccess & rIDRA(pDoc->getIDocumentRedlineAccess()); + SwTextNode* pTextNd = nullptr; + switch( nSubType ) + { + case REF_SETREFATTR: + { + const SwFormatRefMark *pRef = pDoc->GetRefMark( rRefMark ); + SwTextRefMark const*const pRefMark(pRef ? pRef->GetTextRefMark() : nullptr); + if (pRefMark && (!pLayout || !sw::IsMarkHintHidden(*pLayout, + pRefMark->GetTextNode(), *pRefMark))) + { + pTextNd = const_cast<SwTextNode*>(&pRef->GetTextRefMark()->GetTextNode()); + *pStt = pRef->GetTextRefMark()->GetStart(); + if( pEnd ) + *pEnd = pRef->GetTextRefMark()->GetAnyEnd(); + } + } + break; + + case REF_SEQUENCEFLD: + { + SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp, rRefMark, false ); + if( pFieldType && pFieldType->HasWriterListeners() && + nsSwGetSetExpType::GSE_SEQ & static_cast<SwSetExpFieldType*>(pFieldType)->GetType() ) + { + std::vector<SwFormatField*> vFields; + pFieldType->GatherFields(vFields, false); + for(auto pFormatField: vFields) + { + SwTextField *const pTextField(pFormatField->GetTextField()); + if (pTextField && nSeqNo == + static_cast<SwSetExpField*>(pFormatField->GetField())->GetSeqNumber() + && (!pLayout || !pLayout->IsHideRedlines() + || !sw::IsFieldDeletedInModel(rIDRA, *pTextField))) + { + pTextNd = pTextField->GetpTextNode(); + *pStt = pTextField->GetStart(); + if( pEnd ) + *pEnd = (*pStt) + 1; + break; + } + } + } + } + break; + + case REF_BOOKMARK: + { + IDocumentMarkAccess::const_iterator_t ppMark = pDoc->getIDocumentMarkAccess()->findMark(rRefMark); + if (ppMark != pDoc->getIDocumentMarkAccess()->getAllMarksEnd() + && (!pLayout || !pLayout->IsHideRedlines() + || !sw::IsMarkHidden(*pLayout, **ppMark))) + { + const ::sw::mark::IMark* pBkmk = *ppMark; + const SwPosition* pPos = &pBkmk->GetMarkStart(); + + pTextNd = pPos->nNode.GetNode().GetTextNode(); + *pStt = pPos->nContent.GetIndex(); + if(pEnd) + { + if(!pBkmk->IsExpanded()) + { + *pEnd = *pStt; + // #i81002# + if(dynamic_cast< ::sw::mark::CrossRefBookmark const *>(pBkmk)) + { + OSL_ENSURE( pTextNd, + "<SwGetRefFieldType::FindAnchor(..)> - node marked by cross-reference bookmark isn't a text node --> crash" ); + *pEnd = pTextNd->Len(); + } + } + else if(pBkmk->GetOtherMarkPos().nNode == pBkmk->GetMarkPos().nNode) + *pEnd = pBkmk->GetMarkEnd().nContent.GetIndex(); + else + *pEnd = -1; + } + } + } + break; + + case REF_OUTLINE: + break; + + case REF_FOOTNOTE: + case REF_ENDNOTE: + { + for( auto pFootnoteIdx : pDoc->GetFootnoteIdxs() ) + if( nSeqNo == pFootnoteIdx->GetSeqRefNo() ) + { + if (pLayout && pLayout->IsHideRedlines() + && sw::IsFootnoteDeleted(rIDRA, *pFootnoteIdx)) + { + return nullptr; + } + // otherwise: the position at the start of the footnote + // will be mapped to something visible at least... + SwNodeIndex* pIdx = pFootnoteIdx->GetStartNode(); + if( pIdx ) + { + SwNodeIndex aIdx( *pIdx, 1 ); + pTextNd = aIdx.GetNode().GetTextNode(); + if( nullptr == pTextNd ) + pTextNd = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext( &aIdx )); + } + *pStt = 0; + if( pEnd ) + *pEnd = 0; + break; + } + } + break; + } + + return pTextNd; +} + +namespace { + +struct RefIdsMap +{ +private: + OUString aName; + std::set<sal_uInt16> aIds; + std::set<sal_uInt16> aDstIds; + std::map<sal_uInt16, sal_uInt16> sequencedIds; /// ID numbers sorted by sequence number. + bool bInit; + + void Init(SwDoc& rDoc, SwDoc& rDestDoc, bool bField ); + static void GetNoteIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds ); + void GetFieldIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds ); + void AddId( sal_uInt16 id, sal_uInt16 seqNum ); + static sal_uInt16 GetFirstUnusedId( std::set<sal_uInt16> &rIds ); + +public: + explicit RefIdsMap( const OUString& rName ) : aName( rName ), bInit( false ) {} + + void Check( SwDoc& rDoc, SwDoc& rDestDoc, SwGetRefField& rField, bool bField ); + + const OUString& GetName() const { return aName; } +}; + +} + +/// Get a sorted list of the field IDs from a document. +/// @param[in] rDoc The document to search. +/// @param[in,out] rIds The list of IDs found in the document. +void RefIdsMap::GetFieldIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds) +{ + SwFieldType *const pType = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, aName, false); + if (!pType) + return; + std::vector<SwFormatField*> vFields; + pType->GatherFields(vFields); + for(const auto pF: vFields) + rIds.insert(static_cast<SwSetExpField const*>(pF->GetField())->GetSeqNumber()); +} + +/// Get a sorted list of the footnote/endnote IDs from a document. +/// @param[in] rDoc The document to search. +/// @param[in,out] rIds The list of IDs found in the document. +void RefIdsMap::GetNoteIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds) +{ + for( auto n = rDoc.GetFootnoteIdxs().size(); n; ) + rIds.insert( rDoc.GetFootnoteIdxs()[ --n ]->GetSeqRefNo() ); +} + +/// Initialise the aIds and aDestIds collections from the source documents. +/// @param[in] rDoc The source document. +/// @param[in] rDestDoc The destination document. +/// @param[in] bField True if we're interested in all fields, false for footnotes. +void RefIdsMap::Init( SwDoc& rDoc, SwDoc& rDestDoc, bool bField ) +{ + if( bInit ) + return; + + if( bField ) + { + GetFieldIdsFromDoc( rDestDoc, aIds ); + GetFieldIdsFromDoc( rDoc, aDstIds ); + + // Map all the new src fields to the next available unused id + for (const auto& rId : aDstIds) + AddId( GetFirstUnusedId(aIds), rId ); + + // Change the Sequence number of all SetExp fields in the source document + SwFieldType* pType = rDoc.getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp, aName, false ); + if(pType) + { + std::vector<SwFormatField*> vFields; + pType->GatherFields(vFields, false); + for(auto pF: vFields) + { + if(!pF->GetTextField()) + continue; + SwSetExpField *const pSetExp(static_cast<SwSetExpField *>(pF->GetField())); + sal_uInt16 const n = pSetExp->GetSeqNumber(); + pSetExp->SetSeqNumber(sequencedIds[n]); + } + } + } + else + { + GetNoteIdsFromDoc( rDestDoc, aIds ); + GetNoteIdsFromDoc( rDoc, aDstIds ); + + for (const auto& rId : aDstIds) + AddId( GetFirstUnusedId(aIds), rId ); + + // Change the footnotes/endnotes in the source doc to the new ID + for ( const auto pFootnoteIdx : rDoc.GetFootnoteIdxs() ) + { + sal_uInt16 const n = pFootnoteIdx->GetSeqRefNo(); + pFootnoteIdx->SetSeqNo(sequencedIds[n]); + } + } + bInit = true; +} + +/// Get the lowest number unused in the passed set. +/// @param[in] rIds The set of used ID numbers. +/// @returns The lowest number unused by the passed set +sal_uInt16 RefIdsMap::GetFirstUnusedId( std::set<sal_uInt16> &rIds ) +{ + sal_uInt16 num(0); + + for( const auto& rId : rIds ) + { + if( num != rId ) + { + return num; + } + ++num; + } + return num; +} + +/// Add a new ID and sequence number to the "occupied" collection. +/// @param[in] id The ID number. +/// @param[in] seqNum The sequence number. +void RefIdsMap::AddId( sal_uInt16 id, sal_uInt16 seqNum ) +{ + aIds.insert( id ); + sequencedIds[ seqNum ] = id; +} + +void RefIdsMap::Check( SwDoc& rDoc, SwDoc& rDestDoc, SwGetRefField& rField, + bool bField ) +{ + Init( rDoc, rDestDoc, bField); + + sal_uInt16 const nSeqNo = rField.GetSeqNo(); + + // check if it needs to be remapped + // if sequencedIds doesn't contain the number, it means there is no + // SetExp field / footnote in the source document: do not modify + // the number, which works well for copy from/paste to same document + // (and if it is not the same document, there's no "correct" result anyway) + if (sequencedIds.count(nSeqNo)) + { + rField.SetSeqNo( sequencedIds[nSeqNo] ); + } +} + +/// 1. if _both_ SetExp + GetExp / Footnote + GetExp field are copied, +/// ensure that both get a new unused matching number +/// 2. if only SetExp / Footnote is copied, it gets a new unused number +/// 3. if only GetExp field is copied, for the case of copy from / paste to +/// same document it's desirable to keep the same number; +/// for other cases of copy/paste or master documents it's not obvious +/// what is most desirable since it's going to be wrong anyway +void SwGetRefFieldType::MergeWithOtherDoc( SwDoc& rDestDoc ) +{ + if (&rDestDoc == &m_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: */ diff --git a/sw/source/core/fields/scrptfld.cxx b/sw/source/core/fields/scrptfld.cxx new file mode 100644 index 000000000..ded42cc7f --- /dev/null +++ b/sw/source/core/fields/scrptfld.cxx @@ -0,0 +1,119 @@ +/* -*- 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 <docufld.hxx> +#include <unofldmid.h> +#include <strings.hrc> +#include <o3tl/any.hxx> +#include <swtypes.hxx> + +using namespace ::com::sun::star; + +SwScriptFieldType::SwScriptFieldType( SwDoc& rD ) + : SwFieldType( SwFieldIds::Script ), m_rDoc( rD ) +{} + +std::unique_ptr<SwFieldType> SwScriptFieldType::Copy() const +{ + return std::make_unique<SwScriptFieldType>( m_rDoc ); +} + +SwScriptField::SwScriptField( SwScriptFieldType* pInitType, + const OUString& rType, const OUString& rCode, + bool bURL ) + : SwField( pInitType ), m_sType( rType ), m_sCode( rCode ), m_bCodeURL( bURL ) +{ +} + +OUString SwScriptField::GetDescription() const +{ + return SwResId(STR_SCRIPT); +} + +OUString SwScriptField::ExpandImpl(SwRootFrame const*const) const +{ + return OUString(); +} + +std::unique_ptr<SwField> SwScriptField::Copy() const +{ + return std::make_unique<SwScriptField>( static_cast<SwScriptFieldType*>(GetTyp()), m_sType, m_sCode, m_bCodeURL ); +} + +/// set type +void SwScriptField::SetPar1( const OUString& rStr ) +{ + m_sType = rStr; +} + +OUString SwScriptField::GetPar1() const +{ + return m_sType; +} + +/// set code +void SwScriptField::SetPar2( const OUString& rStr ) +{ + m_sCode = rStr; +} + +OUString SwScriptField::GetPar2() const +{ + return m_sCode; +} + +bool SwScriptField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny <<= m_sType; + break; + case FIELD_PROP_PAR2: + rAny <<= m_sCode; + break; + case FIELD_PROP_BOOL1: + rAny <<= m_bCodeURL; + break; + default: + assert(false); + } + return true; +} + +bool SwScriptField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_PAR1: + rAny >>= m_sType; + break; + case FIELD_PROP_PAR2: + rAny >>= m_sCode; + break; + case FIELD_PROP_BOOL1: + m_bCodeURL = *o3tl::doAccess<bool>(rAny); + break; + default: + assert(false); + } + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/tblcalc.cxx b/sw/source/core/fields/tblcalc.cxx new file mode 100644 index 000000000..d9c78be88 --- /dev/null +++ b/sw/source/core/fields/tblcalc.cxx @@ -0,0 +1,211 @@ +/* -*- 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 <sal/config.h> + +#include <o3tl/any.hxx> + +#include <calc.hxx> +#include <doc.hxx> +#include <ndtxt.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <expfld.hxx> +#include <unofldmid.h> + +using namespace ::com::sun::star; + +SwTableFieldType::SwTableFieldType(SwDoc* pDocPtr) + : SwValueFieldType( pDocPtr, SwFieldIds::Table ) +{} + +std::unique_ptr<SwFieldType> SwTableFieldType::Copy() const +{ + return std::make_unique<SwTableFieldType>(GetDoc()); +} + +void SwTableField::CalcField( SwTableCalcPara& rCalcPara ) +{ + if( rCalcPara.m_rCalc.IsCalcError() ) // stop if there is already an error set + return; + + // create pointers from box name + BoxNmToPtr( rCalcPara.m_pTable ); + OUString sFormula( MakeFormula( rCalcPara )); + SetValue( rCalcPara.m_rCalc.Calculate( sFormula ).GetDouble() ); + ChgValid( !rCalcPara.IsStackOverflow() ); // is the value again valid? +} + +SwTableField::SwTableField( SwTableFieldType* pInitType, const OUString& rFormel, + sal_uInt16 nType, sal_uLong nFormat ) + : SwValueField( pInitType, nFormat ), SwTableFormula( rFormel ), + m_nSubType(nType) +{ + m_sExpand = "0"; +} + +std::unique_ptr<SwField> SwTableField::Copy() const +{ + std::unique_ptr<SwTableField> pTmp(new SwTableField( static_cast<SwTableFieldType*>(GetTyp()), + SwTableFormula::GetFormula(), m_nSubType, GetFormat() )); + pTmp->m_sExpand = m_sExpand; + pTmp->SwValueField::SetValue(GetValue()); + pTmp->SwTableFormula::operator=( *this ); + pTmp->SetAutomaticLanguage(IsAutomaticLanguage()); + return std::unique_ptr<SwField>(pTmp.release()); +} + +OUString SwTableField::GetFieldName() const +{ + return GetTyp()->GetName() + " " + const_cast<SwTableField *>(this)->GetCommand(); +} + +/// search TextNode containing this field +const SwNode* SwTableField::GetNodeOfFormula() const +{ + auto pFormat = GetTyp()->FindFormatForField(this); + return pFormat ? &pFormat->GetTextField()->GetTextNode() : nullptr; +} + +OUString SwTableField::GetCommand() +{ + if (EXTRNL_NAME != GetNameType()) + { + SwNode const*const pNd = GetNodeOfFormula(); + SwTableNode const*const pTableNd = pNd ? pNd->FindTableNode() : nullptr; + if (pTableNd) + { + PtrToBoxNm( &pTableNd->GetTable() ); + } + } + return (EXTRNL_NAME == GetNameType()) + ? SwTableFormula::GetFormula() + : OUString(); +} + +OUString SwTableField::ExpandImpl(SwRootFrame const*const) const +{ + if (m_nSubType & nsSwExtendedSubType::SUB_CMD) + { + return const_cast<SwTableField *>(this)->GetCommand(); + } + + if(m_nSubType & nsSwGetSetExpType::GSE_STRING) + { + // it is a string + return m_sExpand.copy(1, m_sExpand.getLength()-2); + } + + return m_sExpand; +} + +sal_uInt16 SwTableField::GetSubType() const +{ + return m_nSubType; +} + +void SwTableField::SetSubType(sal_uInt16 nType) +{ + m_nSubType = nType; +} + +void SwTableField::SetValue( const double& rVal ) +{ + SwValueField::SetValue(rVal); + m_sExpand = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue(rVal, GetFormat(), GetLanguage()); +} + +OUString SwTableField::GetPar2() const +{ + return SwTableFormula::GetFormula(); +} + +void SwTableField::SetPar2(const OUString& rStr) +{ + SetFormula( rStr ); +} + +bool SwTableField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + bool bRet = true; + switch ( nWhichId ) + { + case FIELD_PROP_PAR2: + { + sal_uInt16 nOldSubType = m_nSubType; + SwTableField* pThis = const_cast<SwTableField*>(this); + pThis->m_nSubType |= nsSwExtendedSubType::SUB_CMD; + rAny <<= ExpandImpl(nullptr); + pThis->m_nSubType = nOldSubType; + } + break; + case FIELD_PROP_BOOL1: + rAny <<= 0 != (nsSwExtendedSubType::SUB_CMD & m_nSubType); + break; + case FIELD_PROP_PAR1: + rAny <<= m_sExpand; + break; + case FIELD_PROP_FORMAT: + rAny <<= static_cast<sal_Int32>(GetFormat()); + break; + default: + bRet = false; + } + return bRet; +} + +bool SwTableField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + bool bRet = true; + switch ( nWhichId ) + { + case FIELD_PROP_PAR2: + { + OUString sTmp; + rAny >>= sTmp; + SetFormula( sTmp ); + } + break; + case FIELD_PROP_BOOL1: + if(*o3tl::doAccess<bool>(rAny)) + m_nSubType = nsSwGetSetExpType::GSE_FORMULA|nsSwExtendedSubType::SUB_CMD; + else + m_nSubType = nsSwGetSetExpType::GSE_FORMULA; + break; + case FIELD_PROP_PAR1: + { + OUString sTmp; + rAny >>= sTmp; + ChgExpStr( sTmp ); + } + break; + case FIELD_PROP_FORMAT: + { + sal_Int32 nTmp = 0; + rAny >>= nTmp; + SetFormat(nTmp); + } + break; + default: + bRet = false; + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/textapi.cxx b/sw/source/core/fields/textapi.cxx new file mode 100644 index 000000000..2efb97ef6 --- /dev/null +++ b/sw/source/core/fields/textapi.cxx @@ -0,0 +1,193 @@ +/* -*- 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 <textapi.hxx> +#include <doc.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <docsh.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editeng.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/outliner.hxx> +#include <editeng/unoprnms.hxx> +#include <editeng/unoforou.hxx> +#include <editeng/unoipset.hxx> + +#include <com/sun/star/text/XTextField.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/lang/Locale.hpp> + +using namespace com::sun::star; + +static const SvxItemPropertySet* ImplGetSvxTextPortionPropertySet() +{ + static const SfxItemPropertyMapEntry aSvxTextPortionPropertyMap[] = + { + SVX_UNOEDIT_CHAR_PROPERTIES, + SVX_UNOEDIT_FONT_PROPERTIES, + SVX_UNOEDIT_OUTLINER_PROPERTIES, + SVX_UNOEDIT_PARA_PROPERTIES, + {u"TextField", EE_FEATURE_FIELD, + cppu::UnoType<text::XTextField>::get(), beans::PropertyAttribute::READONLY, 0 }, + {u"TextPortionType", WID_PORTIONTYPE, + ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 }, + {u"TextUserDefinedAttributes", EE_CHAR_XMLATTRIBS, + cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, + {u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, + cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + static SvxItemPropertySet aSvxTextPortionPropertySet( aSvxTextPortionPropertyMap, EditEngine::GetGlobalItemPool() ); + return &aSvxTextPortionPropertySet; +} + +SwTextAPIObject::SwTextAPIObject( std::unique_ptr<SwTextAPIEditSource> p ) +: SvxUnoText( p.get(), ImplGetSvxTextPortionPropertySet(), uno::Reference < text::XText >() ) +, m_pSource(std::move(p)) +{ +} + +SwTextAPIObject::~SwTextAPIObject() noexcept +{ + m_pSource->Dispose(); + m_pSource.reset(); +} + +struct SwTextAPIEditSource_Impl +{ + // needed for "internal" refcounting + SfxItemPool* mpPool; + SwDoc* mpDoc; + std::unique_ptr<Outliner> mpOutliner; + std::unique_ptr<SvxOutlinerForwarder> mpTextForwarder; + sal_Int32 mnRef; +}; + +SwTextAPIEditSource::SwTextAPIEditSource( const SwTextAPIEditSource& rSource ) +: SvxEditSource( *this ) +{ + // shallow copy; uses internal refcounting + m_pImpl = rSource.m_pImpl; + m_pImpl->mnRef++; +} + +std::unique_ptr<SvxEditSource> SwTextAPIEditSource::Clone() const +{ + return std::unique_ptr<SvxEditSource>(new SwTextAPIEditSource( *this )); +} + +void SwTextAPIEditSource::UpdateData() +{ + // data is kept in outliner all the time +} + +SwTextAPIEditSource::SwTextAPIEditSource(SwDoc* pDoc) +: m_pImpl(new SwTextAPIEditSource_Impl) +{ + m_pImpl->mpPool = &pDoc->GetDocShell()->GetPool(); + m_pImpl->mpDoc = pDoc; + m_pImpl->mnRef = 1; +} + +SwTextAPIEditSource::~SwTextAPIEditSource() +{ + if (!--m_pImpl->mnRef) + delete m_pImpl; +} + +void SwTextAPIEditSource::Dispose() +{ + m_pImpl->mpPool=nullptr; + m_pImpl->mpDoc=nullptr; + m_pImpl->mpTextForwarder.reset(); + m_pImpl->mpOutliner.reset(); +} + +SvxTextForwarder* SwTextAPIEditSource::GetTextForwarder() +{ + if( !m_pImpl->mpPool ) + return nullptr; // mpPool == 0 can be used to flag this as disposed + + if( !m_pImpl->mpOutliner ) + { + //init draw model first + m_pImpl->mpDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + m_pImpl->mpOutliner.reset(new Outliner(m_pImpl->mpPool, OutlinerMode::TextObject)); + m_pImpl->mpDoc->SetCalcFieldValueHdl(m_pImpl->mpOutliner.get()); + } + + if( !m_pImpl->mpTextForwarder ) + { + m_pImpl->mpTextForwarder.reset(new SvxOutlinerForwarder(*m_pImpl->mpOutliner, false)); + } + + return m_pImpl->mpTextForwarder.get(); +} + +void SwTextAPIEditSource::SetText( OutlinerParaObject const & rText ) +{ + if ( m_pImpl->mpPool ) + { + if( !m_pImpl->mpOutliner ) + { + //init draw model first + m_pImpl->mpDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + m_pImpl->mpOutliner.reset(new Outliner(m_pImpl->mpPool, OutlinerMode::TextObject)); + m_pImpl->mpDoc->SetCalcFieldValueHdl(m_pImpl->mpOutliner.get()); + } + + m_pImpl->mpOutliner->SetText( rText ); + } +} + +void SwTextAPIEditSource::SetString( const OUString& rText ) +{ + if ( !m_pImpl->mpPool ) + return; + + if( !m_pImpl->mpOutliner ) + { + //init draw model first + m_pImpl->mpDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + m_pImpl->mpOutliner.reset(new Outliner(m_pImpl->mpPool, OutlinerMode::TextObject)); + m_pImpl->mpDoc->SetCalcFieldValueHdl(m_pImpl->mpOutliner.get()); + } + else + m_pImpl->mpOutliner->Clear(); + m_pImpl->mpOutliner->Insert( rText ); +} + +std::optional<OutlinerParaObject> SwTextAPIEditSource::CreateText() +{ + if ( m_pImpl->mpPool && m_pImpl->mpOutliner ) + return m_pImpl->mpOutliner->CreateParaObject(); + else + return std::nullopt; +} + +OUString SwTextAPIEditSource::GetText() const +{ + if ( m_pImpl->mpPool && m_pImpl->mpOutliner ) + return m_pImpl->mpOutliner->GetEditEngine().GetText(); + else + return OUString(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/fields/usrfld.cxx b/sw/source/core/fields/usrfld.cxx new file mode 100644 index 000000000..0067d3b6d --- /dev/null +++ b/sw/source/core/fields/usrfld.cxx @@ -0,0 +1,384 @@ +/* -*- 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 <sal/config.h> + +#include <libxml/xmlwriter.h> + +#include <o3tl/any.hxx> + +#include <svl/numformat.hxx> +#include <unotools/charclass.hxx> + +#include <calc.hxx> +#include <usrfld.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentState.hxx> +#include <unofldmid.h> + +using namespace ::com::sun::star; + +namespace +{ +/** + * Returns the language used for float <-> string conversions in + * SwUserFieldType. + */ +LanguageType GetFieldTypeLanguage() +{ + return LANGUAGE_SYSTEM; +} +} + +// Userfields + +SwUserField::SwUserField(SwUserFieldType* pTyp, sal_uInt16 nSub, sal_uInt32 nFormat) + : SwValueField(pTyp, nFormat), + m_nSubType(nSub) +{ +} + +OUString SwUserField::ExpandImpl(SwRootFrame const*const) const +{ + if(!(m_nSubType & nsSwExtendedSubType::SUB_INVISIBLE)) + return static_cast<SwUserFieldType*>(GetTyp())->Expand(GetFormat(), m_nSubType, GetLanguage()); + + return OUString(); +} + +std::unique_ptr<SwField> SwUserField::Copy() const +{ + std::unique_ptr<SwField> pTmp(new SwUserField(static_cast<SwUserFieldType*>(GetTyp()), m_nSubType, GetFormat())); + pTmp->SetAutomaticLanguage(IsAutomaticLanguage()); + pTmp->SetTitle(GetTitle()); + return pTmp; +} + +OUString SwUserField::GetFieldName() const +{ + return SwFieldType::GetTypeStr(SwFieldTypesEnum::User) + + " " + GetTyp()->GetName() + " = " + + static_cast<SwUserFieldType*>(GetTyp())->GetContent(); +} + +double SwUserField::GetValue() const +{ + return static_cast<SwUserFieldType*>(GetTyp())->GetValue(); +} + +void SwUserField::SetValue( const double& rVal ) +{ + static_cast<SwUserFieldType*>(GetTyp())->SetValue(rVal); +} + +/// Get name +OUString SwUserField::GetPar1() const +{ + return static_cast<const SwUserFieldType*>(GetTyp())->GetName(); +} + +/// Get content +OUString SwUserField::GetPar2() const +{ + return static_cast<SwUserFieldType*>(GetTyp())->GetContent(GetFormat()); +} + +void SwUserField::SetPar2(const OUString& rStr) +{ + static_cast<SwUserFieldType*>(GetTyp())->SetContent(rStr, GetFormat()); +} + +sal_uInt16 SwUserField::GetSubType() const +{ + return static_cast<SwUserFieldType*>(GetTyp())->GetType() | m_nSubType; +} + +void SwUserField::SetSubType(sal_uInt16 nSub) +{ + static_cast<SwUserFieldType*>(GetTyp())->SetType(nSub & 0x00ff); + m_nSubType = nSub & 0xff00; +} + +bool SwUserField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL2: + rAny <<= 0 != (m_nSubType & nsSwExtendedSubType::SUB_CMD); + break; + case FIELD_PROP_BOOL1: + rAny <<= 0 == (m_nSubType & nsSwExtendedSubType::SUB_INVISIBLE); + break; + case FIELD_PROP_FORMAT: + rAny <<= static_cast<sal_Int32>(GetFormat()); + break; + default: + return SwField::QueryValue(rAny, nWhichId); + } + return true; +} + +bool SwUserField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_BOOL1: + if(*o3tl::doAccess<bool>(rAny)) + m_nSubType &= (~nsSwExtendedSubType::SUB_INVISIBLE); + else + m_nSubType |= nsSwExtendedSubType::SUB_INVISIBLE; + break; + case FIELD_PROP_BOOL2: + if(*o3tl::doAccess<bool>(rAny)) + m_nSubType |= nsSwExtendedSubType::SUB_CMD; + else + m_nSubType &= (~nsSwExtendedSubType::SUB_CMD); + break; + case FIELD_PROP_FORMAT: + { + sal_Int32 nTmp = 0; + rAny >>= nTmp; + SetFormat(nTmp); + } + break; + default: + return SwField::PutValue(rAny, nWhichId); + } + return true; +} + +void SwUserField::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUserField")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nSubType"), BAD_CAST(OString::number(m_nSubType).getStr())); + SwValueField::dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); +} + +SwUserFieldType::SwUserFieldType( SwDoc* pDocPtr, const OUString& aNam ) + : SwValueFieldType( pDocPtr, SwFieldIds::User ), + m_nValue( 0 ), + m_nType(nsSwGetSetExpType::GSE_STRING) +{ + m_bValidValue = m_bDeleted = false; + m_aName = aNam; + + EnableFormat(false); // Do not use a Numberformatter for nsSwGetSetExpType::GSE_STRING +} + +OUString SwUserFieldType::Expand(sal_uInt32 nFormat, sal_uInt16 nSubType, LanguageType nLng) +{ + if((m_nType & nsSwGetSetExpType::GSE_EXPR) && !(nSubType & nsSwExtendedSubType::SUB_CMD)) + { + EnableFormat(); + return ExpandValue(m_nValue, nFormat, nLng); + } + + EnableFormat(false); // Do not use a Numberformatter + return m_aContent; +} + +std::unique_ptr<SwFieldType> SwUserFieldType::Copy() const +{ + std::unique_ptr<SwUserFieldType> pTmp(new SwUserFieldType( GetDoc(), m_aName )); + pTmp->m_aContent = m_aContent; + pTmp->m_aContentLang = m_aContentLang; + pTmp->m_nType = m_nType; + pTmp->m_bValidValue = m_bValidValue; + pTmp->m_nValue = m_nValue; + pTmp->m_bDeleted = m_bDeleted; + + return pTmp; +} + +OUString SwUserFieldType::GetName() const +{ + return m_aName; +} + +void SwUserFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::SwLegacyModify) + { + auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint); + if (!pLegacy->m_pOld && !pLegacy->m_pNew) + m_bValidValue = false; + } + + CallSwClientNotify(rHint); + // update input fields that might be connected to the user field + if (!IsModifyLocked()) + { + LockModify(); + GetDoc()->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Input)->UpdateFields(); + UnlockModify(); + } +} + +double SwUserFieldType::GetValue( SwCalc& rCalc ) +{ + if(m_bValidValue) + return m_nValue; + + if(!rCalc.Push( this )) + { + rCalc.SetCalcError( SwCalcError::Syntax ); + return 0; + } + + // See if we need to temporarily switch rCalc's language: in case it + // differs from the field type locale. + const CharClass* pCharClass = rCalc.GetCharClass(); + LanguageTag aCharClassLanguage = pCharClass->getLanguageTag(); + LanguageTag aContentLang(m_aContentLang); + + // for the call of calculate we need the language that was used for putting/setting + // the m_aContent string, otherwise the aContent could be interpreted wrongly, + + bool bSwitchLanguage = m_aContentLang != aCharClassLanguage.getBcp47(); + + if (bSwitchLanguage) + rCalc.SetCharClass(aContentLang); + + m_nValue = rCalc.Calculate( m_aContent ).GetDouble(); + + // we than have to set the proper char class languageTag again + + if (bSwitchLanguage) + rCalc.SetCharClass(aCharClassLanguage); + + rCalc.Pop(); + + if( !rCalc.IsCalcError() ) + m_bValidValue = true; + else + m_nValue = 0; + + return m_nValue; +} + +OUString SwUserFieldType::GetContent( sal_uInt32 nFormat ) +{ + if (nFormat && nFormat != SAL_MAX_UINT32) + { + OUString sFormattedValue; + const Color* pCol = nullptr; + + SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter(); + + pFormatter->GetOutputString(GetValue(), nFormat, sFormattedValue, &pCol); + return sFormattedValue; + } + + return m_aContent; +} + +void SwUserFieldType::SetContent( const OUString& rStr, sal_uInt32 nFormat ) +{ + if( m_aContent == rStr ) + return; + + m_aContent = rStr; + + if (nFormat && nFormat != SAL_MAX_UINT32) + { + double fValue; + + if (GetDoc()->IsNumberFormat(rStr, nFormat, fValue)) + { + SetValue(fValue); + LanguageTag aContentLanguage(GetFieldTypeLanguage()); + m_aContentLang = aContentLanguage.getBcp47(); + m_aContent = DoubleToString(fValue, nFormat); + } + } + + bool bModified = GetDoc()->getIDocumentState().IsModified(); + GetDoc()->getIDocumentState().SetModified(); + if( !bModified ) // Bug 57028 + { + GetDoc()->GetIDocumentUndoRedo().SetUndoNoResetModified(); + } +} + +void SwUserFieldType::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const +{ + switch( nWhichId ) + { + case FIELD_PROP_DOUBLE: + rAny <<= m_nValue; + break; + case FIELD_PROP_PAR2: + rAny <<= m_aContent; + break; + case FIELD_PROP_BOOL1: + rAny <<= 0 != (nsSwGetSetExpType::GSE_EXPR&m_nType); + break; + default: + assert(false); + } +} + +void SwUserFieldType::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) +{ + switch( nWhichId ) + { + case FIELD_PROP_DOUBLE: + { + double fVal = 0; + rAny >>= fVal; + m_nValue = fVal; + LanguageTag aContentLanguage(GetFieldTypeLanguage()); + m_aContentLang = aContentLanguage.getBcp47(); + m_aContent = DoubleToString(m_nValue, static_cast<sal_uInt16>(GetFieldTypeLanguage())); + } + break; + case FIELD_PROP_PAR2: + rAny >>= m_aContent; + break; + case FIELD_PROP_BOOL1: + if(*o3tl::doAccess<bool>(rAny)) + { + m_nType |= nsSwGetSetExpType::GSE_EXPR; + m_nType &= ~nsSwGetSetExpType::GSE_STRING; + } + else + { + m_nType &= ~nsSwGetSetExpType::GSE_EXPR; + m_nType |= nsSwGetSetExpType::GSE_STRING; + } + break; + default: + assert(false); + } +} + +void SwUserFieldType::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUserFieldType")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nValue"), BAD_CAST(OString::number(m_nValue).getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aContent"), BAD_CAST(m_aContent.toUtf8().getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aContentLang"), BAD_CAST(m_aContentLang.toUtf8().getStr())); + SwFieldType::dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |