diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 773d54f79318..00d0d5f349e1 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -1902,6 +1902,7 @@ namespace xmloff::token { XML_STRUCTURE_PROTECTED, XML_STYLE, XML_STYLE_NAME, + XML_STYLE_REF, XML_STYLES, XML_STYLESHEET, XML_SUB_TABLE, diff --git a/offapi/com/sun/star/text/ReferenceFieldSource.idl b/offapi/com/sun/star/text/ReferenceFieldSource.idl index 1892fdc04cd8..91295fef2844 100644 --- a/offapi/com/sun/star/text/ReferenceFieldSource.idl +++ b/offapi/com/sun/star/text/ReferenceFieldSource.idl @@ -44,6 +44,11 @@ published constants ReferenceFieldSource /** The source is an endnote. */ const short ENDNOTE = 4; + + /** The source is a style. + @since LibreOffice 24.2 + */ + const short STYLE = 5; }; diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng index 6b52ab7307df..ec0a86afaae4 100644 --- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng @@ -2179,6 +2179,29 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. + + + + + + + + + text + direction + number + number-no-superior + number-all-superior + + + + + + + + + + diff --git a/sw/inc/fldref.hrc b/sw/inc/fldref.hrc index d28a1ea43cb5..a48d2d4e4a06 100644 --- a/sw/inc/fldref.hrc +++ b/sw/inc/fldref.hrc @@ -31,6 +31,7 @@ const TranslateId FLD_REF_PAGE_TYPES[] = NC_("fldrefpage|liststore1", "Endnotes"), NC_("fldrefpage|liststore1", "Headings"), NC_("fldrefpage|liststore1", "Numbered Paragraphs"), + NC_("fldrefpage|liststore1", "Styles"), }; #endif diff --git a/sw/inc/reffld.hxx b/sw/inc/reffld.hxx index 931441d27725..bf9d563ab2ac 100644 --- a/sw/inc/reffld.hxx +++ b/sw/inc/reffld.hxx @@ -26,8 +26,10 @@ class SfxPoolItem; class SwDoc; class SwTextNode; +class SwContentFrame; class SwTextField; class SwRootFrame; +class SwFrame; bool IsFrameBehind( const SwTextNode& rMyNd, sal_Int32 nMySttPos, const SwTextNode& rBehindNd, sal_Int32 nSttPos ); @@ -39,7 +41,8 @@ enum REFERENCESUBTYPE REF_BOOKMARK, REF_OUTLINE, REF_FOOTNOTE, - REF_ENDNOTE + REF_ENDNOTE, + REF_STYLE }; enum REFERENCEMARK @@ -80,7 +83,8 @@ public: static SwTextNode* FindAnchor( SwDoc* pDoc, const OUString& rRefMark, sal_uInt16 nSubType, sal_uInt16 nSeqNo, sal_Int32* pStt, sal_Int32* pEnd = nullptr, - SwRootFrame const* pLayout = nullptr); + SwRootFrame const* pLayout = nullptr, + SwTextNode* pSelf = nullptr, SwFrame* pFrame = nullptr); void UpdateGetReferences(); }; @@ -111,11 +115,13 @@ public: // #i81002# /** The instance, which represents the text attribute for the instance, has to be passed to the method. - This instance is needed for the reference format type REF_UPDOWN - and REF_NUMBER. + This instance is needed for the reference format type REF_UPDOWN, REF_NUMBER + and REF_STYLE. Note: This instance may be NULL (field in Undo/Redo). This will cause no update for these reference format types. */ - void UpdateField( const SwTextField* pFieldTextAttr ); + void UpdateField( const SwTextField* pFieldTextAttr, SwFrame* pFrame ); + void UpdateField( const SwTextField* pFieldTextAttr, SwFrame* pFrame, + const SwRootFrame* const pLayout, OUString& rText ); void SetExpand( const OUString& rStr ); @@ -126,9 +132,9 @@ public: // --> #i81002# bool IsRefToHeadingCrossRefBookmark() const; bool IsRefToNumItemCrossRefBookmark() const; - const SwTextNode* GetReferencedTextNode() const; + const SwTextNode* GetReferencedTextNode(SwTextNode* pTextNode, SwFrame* pFrame) const; // #i85090# - OUString GetExpandedTextOfReferencedTextNode(SwRootFrame const& rLayout) const; + OUString GetExpandedTextOfReferencedTextNode(SwRootFrame const& rLayout, SwTextNode* pTextNode, SwFrame* pFrame) const; /// Get/set SequenceNo (of interest only for REF_SEQUENCEFLD). sal_uInt16 GetSeqNo() const { return m_nSeqNo; } @@ -138,6 +144,8 @@ public: virtual OUString GetPar1() const override; virtual void SetPar1(const OUString& rStr) override; + void SetText(OUString sText, SwRootFrame* pLayout); + virtual OUString GetPar2() const override; virtual bool QueryValue( css::uno::Any& rVal, sal_uInt16 nWhichId ) const override; virtual bool PutValue( const css::uno::Any& rVal, sal_uInt16 nWhichId ) override; diff --git a/sw/qa/core/fields/data/tdf86790.docx b/sw/qa/core/fields/data/tdf86790.docx new file mode 100644 index 000000000000..a45c03f6b898 Binary files /dev/null and b/sw/qa/core/fields/data/tdf86790.docx differ diff --git a/sw/qa/core/fields/fields.cxx b/sw/qa/core/fields/fields.cxx index f2fdd808734f..158e1c35dc2b 100644 --- a/sw/qa/core/fields/fields.cxx +++ b/sw/qa/core/fields/fields.cxx @@ -7,6 +7,36 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -132,6 +162,204 @@ CPPUNIT_TEST_FIXTURE(Test, testChapterFieldsFollowedBy) CPPUNIT_ASSERT_EQUAL(sValue, xField->getPresentation(false)); } } + +CPPUNIT_TEST_FIXTURE(Test, testTdf86790) +{ + loadFromURL(u"tdf86790.docx"); + + uno::Reference xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference xFieldsAccess( + xTextFieldsSupplier->getTextFields()); + uno::Reference xFields(xFieldsAccess->createEnumeration()); + + // Similarly to testChapterFieldsFollowedBy, the fields are enumerated with #1 first and everything else in reverse order + std::vector> aFieldValues = { + { " Heading 1", "1" }, // #1 + { " foobar", "22.2" }, // #5 + { " foobar", "4" }, // #4 + { " Heading 2", "1.1" }, // #3 + { " Heading 2", "1.1" }, // #2 + }; + + for (const auto& sValue : aFieldValues) + { + CPPUNIT_ASSERT(xFields->hasMoreElements()); + uno::Reference xField(xFields->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sValue.first, xField->getPresentation(true)); + CPPUNIT_ASSERT_EQUAL(sValue.second, xField->getPresentation(false)); + } +} + +/// If there is referenced text both above and below, STYLEREF searches up +CPPUNIT_TEST_FIXTURE(Test, testStyleRefSearchUp) +{ + createSwDoc(); + + uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference xText = xTextDocument->getText(); + + uno::Reference xCursor = xText->createTextCursor(); + uno::Reference xCursorPropertySet(xCursor, uno::UNO_QUERY); + + xCursorPropertySet->setPropertyValue("ParaStyleName", uno::Any(OUString("Heading 1"))); + xText->insertString(xCursor, "Heading far above field", false); + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false); + + xCursorPropertySet->setPropertyValue("ParaStyleName", uno::Any(OUString("Heading 1"))); + xText->insertString(xCursor, "Heading above field", false); + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false); + + uno::Reference xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference xField( + xFactory->createInstance("com.sun.star.text.TextField.GetReference"), uno::UNO_QUERY); + + uno::Reference xFieldPropertySet(xField, uno::UNO_QUERY); + xFieldPropertySet->setPropertyValue("ReferenceFieldSource", + uno::Any(sal_Int16(text::ReferenceFieldSource::STYLE))); + xFieldPropertySet->setPropertyValue("ReferenceFieldPart", + uno::Any(sal_Int16(text::ReferenceFieldPart::TEXT))); + xFieldPropertySet->setPropertyValue("SourceName", uno::Any(OUString("Heading 1"))); + + uno::Reference xFieldCursor = xText->createTextCursor(); + + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false); + xCursorPropertySet->setPropertyValue("ParaStyleName", uno::Any(OUString("Heading 1"))); + xText->insertString(xCursor, "Heading below field", false); + + xField->attach(xFieldCursor); + CPPUNIT_ASSERT_EQUAL(OUString("Heading above field"), xField->getPresentation(false)); +} + +/// If there is referenced text both above and below, STYLEREF searches down +CPPUNIT_TEST_FIXTURE(Test, testStyleRefSearchDown) +{ + createSwDoc(); + + uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference xText = xTextDocument->getText(); + + uno::Reference xCursor = xText->createTextCursor(); + uno::Reference xCursorPropertySet(xCursor, uno::UNO_QUERY); + + uno::Reference xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference xField( + xFactory->createInstance("com.sun.star.text.TextField.GetReference"), uno::UNO_QUERY); + + uno::Reference xFieldPropertySet(xField, uno::UNO_QUERY); + xFieldPropertySet->setPropertyValue("ReferenceFieldSource", + uno::Any(sal_Int16(text::ReferenceFieldSource::STYLE))); + xFieldPropertySet->setPropertyValue("ReferenceFieldPart", + uno::Any(sal_Int16(text::ReferenceFieldPart::TEXT))); + xFieldPropertySet->setPropertyValue("SourceName", uno::Any(OUString("Heading 1"))); + + uno::Reference xFieldCursor = xText->createTextCursor(); + + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false); + xCursorPropertySet->setPropertyValue("ParaStyleName", uno::Any(OUString("Heading 1"))); + xText->insertString(xCursor, "Heading below field", false); + + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false); + xCursorPropertySet->setPropertyValue("ParaStyleName", uno::Any(OUString("Heading 1"))); + xText->insertString(xCursor, "Heading far below field", false); + + xField->attach(xFieldCursor); + CPPUNIT_ASSERT_EQUAL(OUString("Heading below field"), xField->getPresentation(false)); +} + +/// STYLEREFs in marginals (headers or footers) should search in the page they are on first, regardless if there is anything above them +CPPUNIT_TEST_FIXTURE(Test, testMarginalStyleRef) +{ + createSwDoc(); + + uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference xText = xTextDocument->getText(); + + uno::Reference xCursor = xText->createTextCursor(); + uno::Reference xCursorPropertySet(xCursor, uno::UNO_QUERY); + + xCursorPropertySet->setPropertyValue("ParaStyleName", uno::Any(OUString("Heading 1"))); + xText->insertString(xCursor, "Top heading on page", false); + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false); + + xCursorPropertySet->setPropertyValue("ParaStyleName", uno::Any(OUString("Heading 1"))); + xText->insertString(xCursor, "Bottom heading on page", false); + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false); + + uno::Reference xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference xField( + xFactory->createInstance("com.sun.star.text.TextField.GetReference"), uno::UNO_QUERY); + + uno::Reference xStyleFamiliesSupplier(mxComponent, + uno::UNO_QUERY); + uno::Reference xParagraphStylesContainer( + xStyleFamiliesSupplier->getStyleFamilies()->getByName("PageStyles"), uno::UNO_QUERY); + + uno::Reference xPagePropertySet( + xParagraphStylesContainer->getByName("Standard"), uno::UNO_QUERY); + + xPagePropertySet->setPropertyValue("FooterIsOn", uno::Any(true)); + uno::Reference xFooterText(xPagePropertySet->getPropertyValue("FooterText"), + uno::UNO_QUERY); + + uno::Reference xFieldPropertySet(xField, uno::UNO_QUERY); + xFieldPropertySet->setPropertyValue("ReferenceFieldSource", + uno::Any(sal_Int16(text::ReferenceFieldSource::STYLE))); + xFieldPropertySet->setPropertyValue("ReferenceFieldPart", + uno::Any(sal_Int16(text::ReferenceFieldPart::TEXT))); + xFieldPropertySet->setPropertyValue("SourceName", uno::Any(OUString("Heading 1"))); + + uno::Reference xFooterCursor = xFooterText->createTextCursor(); + xField->attach(xFooterCursor); + + CPPUNIT_ASSERT_EQUAL(OUString("Top heading on page"), xField->getPresentation(false)); +} + +/// STYLEREFs in footnotes should search from the point of the reference mark +CPPUNIT_TEST_FIXTURE(Test, testFootnoteStyleRef) +{ + createSwDoc(); + + uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference xText = xTextDocument->getText(); + + uno::Reference xCursor = xText->createTextCursor(); + uno::Reference xCursorPropertySet(xCursor, uno::UNO_QUERY); + + xCursorPropertySet->setPropertyValue("ParaStyleName", uno::Any(OUString("Heading 1"))); + xText->insertString(xCursor, "Heading far above reference mark", false); + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false); + + xCursorPropertySet->setPropertyValue("ParaStyleName", uno::Any(OUString("Heading 1"))); + xText->insertString(xCursor, "Heading above reference mark", false); + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false); + + uno::Reference xFactory(mxComponent, uno::UNO_QUERY); + + uno::Reference xFootnote( + xFactory->createInstance("com.sun.star.text.Footnote"), uno::UNO_QUERY); + xFootnote->setLabel("Style reference mark"); + xText->insertTextContent(xCursor, xFootnote, false); + + xText->insertControlCharacter(xCursor, text::ControlCharacter::PARAGRAPH_BREAK, false); + xCursorPropertySet->setPropertyValue("ParaStyleName", uno::Any(OUString("Heading 1"))); + xText->insertString(xCursor, "Heading below reference mark", false); + + uno::Reference xField( + xFactory->createInstance("com.sun.star.text.TextField.GetReference"), uno::UNO_QUERY); + + uno::Reference xFieldPropertySet(xField, uno::UNO_QUERY); + xFieldPropertySet->setPropertyValue("ReferenceFieldSource", + uno::Any(sal_Int16(text::ReferenceFieldSource::STYLE))); + xFieldPropertySet->setPropertyValue("ReferenceFieldPart", + uno::Any(sal_Int16(text::ReferenceFieldPart::TEXT))); + xFieldPropertySet->setPropertyValue("SourceName", uno::Any(OUString("Heading 1"))); + + uno::Reference xFootnoteText(xFootnote, uno::UNO_QUERY); + uno::Reference xFootnoteCursor = xFootnoteText->createTextCursor(); + xField->attach(xFootnoteCursor); + + CPPUNIT_ASSERT_EQUAL(OUString("Heading above reference mark"), xField->getPresentation(false)); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx index cbca150319b7..c4530aa44f6a 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx @@ -582,7 +582,7 @@ DECLARE_OOXMLEXPORT_TEST(testMultiPageToc, "multi-page-toc.docx") CPPUNIT_ASSERT_EQUAL(OUString("Table of Contents1"), xTextSection->getName()); // There should be a field in the header as well. uno::Reference xHeaderText = getProperty< uno::Reference >(getStyles("PageStyles")->getByName("Standard"), "HeaderText"); - CPPUNIT_ASSERT_EQUAL(OUString("TextFieldStart"), getProperty(getRun(getParagraphOfText(1, xHeaderText), 1), "TextPortionType")); + CPPUNIT_ASSERT_EQUAL(OUString("TextField"), getProperty(getRun(getParagraphOfText(1, xHeaderText), 1), "TextPortionType")); } DECLARE_OOXMLEXPORT_TEST(testTextboxTable, "textbox-table.docx") diff --git a/sw/qa/extras/uiwriter/uiwriter8.cxx b/sw/qa/extras/uiwriter/uiwriter8.cxx index d61bbf289663..8e4869c5d694 100644 --- a/sw/qa/extras/uiwriter/uiwriter8.cxx +++ b/sw/qa/extras/uiwriter/uiwriter8.cxx @@ -2454,9 +2454,9 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, testTdf128106) CPPUNIT_ASSERT_EQUAL(sal_uInt16(REF_BOOKMARK), fields[0]->GetField()->GetSubType()); CPPUNIT_ASSERT_EQUAL(OUString("bookmarkchapter1_text"), static_cast(fields[0]->GetField())->GetSetRefName()); - CPPUNIT_ASSERT_EQUAL(OUString("Text"), - static_cast(fields[0]->GetField()) - ->GetExpandedTextOfReferencedTextNode(*pWrtShell->GetLayout())); + CPPUNIT_ASSERT_EQUAL(OUString("Text"), static_cast(fields[0]->GetField()) + ->GetExpandedTextOfReferencedTextNode( + *pWrtShell->GetLayout(), nullptr, nullptr)); CPPUNIT_ASSERT_EQUAL(sal_uInt16(REF_BOOKMARK), fields[1]->GetField()->GetSubType()); CPPUNIT_ASSERT( static_cast(fields[1]->GetField())->IsRefToHeadingCrossRefBookmark()); diff --git a/sw/source/core/access/accpara.cxx b/sw/source/core/access/accpara.cxx index 578c71b7c21a..87d8dc1f73c5 100644 --- a/sw/source/core/access/accpara.cxx +++ b/sw/source/core/access/accpara.cxx @@ -1228,6 +1228,9 @@ OUString SwAccessibleParagraph::GetFieldTypeNameAtIndex(sal_Int32 nIndex) case REF_SEQUENCEFLD: sEntry = static_cast(pField)->GetSetRefName(); break; + case REF_STYLE: + sEntry = "StyleRef"; + break; } //Get format string strTypeName = sEntry; diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx index 3face1677c13..93d22362745c 100644 --- a/sw/source/core/crsr/crstrvl.cxx +++ b/sw/source/core/crsr/crstrvl.cxx @@ -1409,8 +1409,14 @@ bool SwCursorShell::GotoRefMark( const OUString& rRefMark, sal_uInt16 nSubType, SwCursorSaveState aSaveState( *m_pCurrentCursor ); sal_Int32 nPos = -1; - SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor( GetDoc(), rRefMark, - nSubType, nSeqNo, &nPos, nullptr, GetLayout()); + + SwPaM* pCursor = GetCursor(); + SwPosition* pPos = pCursor->GetPoint(); + SwTextNode* pRefTextNd = pPos->GetNode().GetTextNode(); + SwContentFrame* pRefFrame = GetCurrFrame(); + + SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor(GetDoc(), rRefMark, + nSubType, nSeqNo, &nPos, nullptr, GetLayout(), pRefTextNd, pRefFrame); if( !pTextNd || !pTextNd->GetNodes().IsDocNodes() ) return false; diff --git a/sw/source/core/fields/reffld.cxx b/sw/source/core/fields/reffld.cxx index d8a66d6ec7a7..442e1644a753 100644 --- a/sw/source/core/fields/reffld.cxx +++ b/sw/source/core/fields/reffld.cxx @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,7 @@ #include #include #include +#include using namespace ::com::sun::star; using namespace ::com::sun::star::text; @@ -349,11 +351,11 @@ static void lcl_formatReferenceLanguage( OUString& rRefText, SwGetRefField::SwGetRefField( SwGetRefFieldType* pFieldType, OUString aSetRef, OUString aSetReferenceLanguage, sal_uInt16 nSubTyp, sal_uInt16 nSequenceNo, sal_uLong nFormat ) - : SwField( pFieldType, nFormat ), - m_sSetRefName( std::move(aSetRef) ), - m_sSetReferenceLanguage( std::move(aSetReferenceLanguage) ), - m_nSubType( nSubTyp ), - m_nSeqNo( nSequenceNo ) + : SwField(pFieldType, nFormat), + m_sSetRefName(std::move(aSetRef)), + m_sSetReferenceLanguage(std::move(aSetReferenceLanguage)), + m_nSubType(nSubTyp), + m_nSeqNo(nSequenceNo) { } @@ -361,6 +363,18 @@ SwGetRefField::~SwGetRefField() { } +void SwGetRefField::SetText(OUString sText, SwRootFrame* pLayout) +{ + if (pLayout->IsHideRedlines()) + { + m_sTextRLHidden = sText; + } + else + { + m_sText = sText; + } +} + OUString SwGetRefField::GetDescription() const { return SwResId(STR_REFERENCE); @@ -389,13 +403,14 @@ bool SwGetRefField::IsRefToNumItemCrossRefBookmark() const ::sw::mark::CrossRefNumItemBookmark::IsLegalName(m_sSetRefName); } -const SwTextNode* SwGetRefField::GetReferencedTextNode() const +const SwTextNode* SwGetRefField::GetReferencedTextNode(SwTextNode* pTextNode, SwFrame* pFrame) const { SwGetRefFieldType *pTyp = dynamic_cast(GetTyp()); if (!pTyp) return nullptr; sal_Int32 nDummy = -1; - return SwGetRefFieldType::FindAnchor( &pTyp->GetDoc(), m_sSetRefName, m_nSubType, m_nSeqNo, &nDummy ); + return SwGetRefFieldType::FindAnchor( &pTyp->GetDoc(), m_sSetRefName, m_nSubType, m_nSeqNo, &nDummy, + nullptr, nullptr, pTextNode, pFrame ); } // strikethrough for tooltips using Unicode combining character @@ -411,9 +426,9 @@ static OUString lcl_formatStringByCombiningCharacter(std::u16string_view sText, // #i85090# OUString SwGetRefField::GetExpandedTextOfReferencedTextNode( - SwRootFrame const& rLayout) const + SwRootFrame const& rLayout, SwTextNode* pTextNode, SwFrame* pFrame) const { - const SwTextNode* pReferencedTextNode( GetReferencedTextNode() ); + const SwTextNode* pReferencedTextNode( GetReferencedTextNode(pTextNode, pFrame) ); if ( !pReferencedTextNode ) return OUString(); @@ -481,38 +496,43 @@ static void FilterText(OUString & rText, LanguageType const eLang, } // #i81002# - parameter added -void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr ) +void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr, SwFrame* pFrame ) { - m_sText.clear(); - m_sTextRLHidden.clear(); - SwDoc& rDoc = static_cast(GetTyp())->GetDoc(); + + for (SwRootFrame const* const pLay : rDoc.GetAllLayouts()) + { + if (pLay->IsHideRedlines()) + { + UpdateField(pFieldTextAttr, pFrame, pLay, m_sTextRLHidden); + } + else + { + UpdateField(pFieldTextAttr, pFrame, pLay, m_sText); + } + } +} + +void SwGetRefField::UpdateField(const SwTextField* pFieldTextAttr, SwFrame* pFrameContainingField, + const SwRootFrame* const pLayout, OUString& rText) +{ + SwDoc& rDoc = static_cast(GetTyp())->GetDoc(); + + rText.clear(); + // finding the reference target (the number) sal_Int32 nNumStart = -1; sal_Int32 nNumEnd = -1; SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor( - &rDoc, m_sSetRefName, m_nSubType, m_nSeqNo, &nNumStart, &nNumEnd + &rDoc, m_sSetRefName, m_nSubType, m_nSeqNo, &nNumStart, &nNumEnd, + pLayout, pFieldTextAttr ? pFieldTextAttr->GetpTextNode() : nullptr, pFrameContainingField ); // not found? if ( !pTextNd ) { - m_sText = SwViewShell::GetShellRes()->aGetRefField_RefItemNotFound; - m_sTextRLHidden = m_sText; - return ; - } + rText = SwViewShell::GetShellRes()->aGetRefField_RefItemNotFound; - SwRootFrame const* pLayout(nullptr); - SwRootFrame const* pLayoutRLHidden(nullptr); - for (SwRootFrame const*const pLay : rDoc.GetAllLayouts()) - { - if (pLay->IsHideRedlines()) - { - pLayoutRLHidden = pLay; - } - else - { - pLayout = pLay; - } + return; } // where is the category name (e.g. "Illustration")? @@ -607,18 +627,21 @@ void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr ) 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); + rText = pFootnoteIdx->GetFootnote().GetViewNumStr(rDoc, pLayout); if (!m_sSetReferenceLanguage.isEmpty()) { - lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage); - lcl_formatReferenceLanguage(m_sTextRLHidden, false, GetLanguage(), m_sSetReferenceLanguage); + lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage); } break; } } return; + case REF_STYLE: + nStart = 0; + nEnd = nLen; + break; + case REF_SETREFATTR: nStart = nNumStart; nEnd = nNumEnd; @@ -630,26 +653,32 @@ void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr ) 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())) + if (pLayout->IsHideRedlines()) { - m_sTextRLHidden = sw::GetExpandTextMerged( - pLayoutRLHidden, *pTextNd, false, false, ExpandMode(0)); + if (m_nSubType == REF_OUTLINE + || (m_nSubType == REF_SEQUENCEFLD && REF_CONTENT == GetFormat())) + { + rText = sw::GetExpandTextMerged(pLayout, *pTextNd, false, false, + ExpandMode(0)); + } + else + { + rText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, + false, ExpandMode::HideDeletions); + } } else { - m_sTextRLHidden = pTextNd->GetExpandText(pLayoutRLHidden, - nStart, nEnd - nStart, false, false, false, ExpandMode::HideDeletions); + rText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, + false, ExpandMode::HideDeletions); + // show the referenced text without the deletions, but if the whole text was + // deleted, show the original text for the sake of the comfortable reviewing + // (with strikethrough in tooltip, see GetExpandedTextOfReferencedTextNode()) + if (rText.isEmpty()) + rText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, + false, ExpandMode(0)); } FilterText(m_sText, GetLanguage(), m_sSetReferenceLanguage); - FilterText(m_sTextRLHidden, GetLanguage(), m_sSetReferenceLanguage); } } break; @@ -657,10 +686,7 @@ void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr ) case REF_PAGE: case REF_PAGE_PGDESC: { - auto const func = - [this, pTextNd, nNumStart](OUString & rText, SwRootFrame const*const pLay) - { - SwTextFrame const* pFrame = static_cast(pTextNd->getLayoutFrame(pLay, nullptr, nullptr)); + SwTextFrame const* pFrame = static_cast(pTextNd->getLayoutFrame(pLayout, nullptr, nullptr)); SwTextFrame const*const pSave = pFrame; if (pFrame) { @@ -687,35 +713,25 @@ void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr ) 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: + { + // a bit tricky: search any frame + SwFrame const* const pFrame = pTextNd->getLayoutFrame(pLayout); + if (pFrame) { - 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); + SwChapterFieldType aFieldTyp; + SwChapterField aField(&aFieldTyp, 0); + aField.SetLevel(MAXLEVEL - 1); + aField.ChangeExpansion(*pFrame, pTextNd, true); - if (!m_sSetReferenceLanguage.isEmpty()) - lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage); - } - }; - func(m_sText, pLayout); - func(m_sTextRLHidden, pLayoutRLHidden); + rText = aField.GetNumber(pLayout); + + if (!m_sSetReferenceLanguage.isEmpty()) + lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage); + } } break; @@ -731,22 +747,19 @@ void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr ) // first a "short" test - in case both are in the same node if( pFieldTextAttr->GetpTextNode() == pTextNd ) { - m_sText = nNumStart < pFieldTextAttr->GetStart() + rText = nNumStart < pFieldTextAttr->GetStart() ? aLocaleData.getAboveWord() : aLocaleData.getBelowWord(); - m_sTextRLHidden = m_sText; break; } - m_sText = ::IsFrameBehind( *pFieldTextAttr->GetpTextNode(), pFieldTextAttr->GetStart(), + rText = ::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; + lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage); } break; // #i81002# @@ -758,20 +771,12 @@ void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr ) { auto result = MakeRefNumStr(pLayout, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat()); - m_sText = result.first; + rText = 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); + lcl_formatReferenceLanguage(rText, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage); } } } @@ -934,6 +939,7 @@ bool SwGetRefField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const case REF_OUTLINE : OSL_FAIL("not implemented"); break; case REF_FOOTNOTE : nSource = ReferenceFieldSource::FOOTNOTE; break; case REF_ENDNOTE : nSource = ReferenceFieldSource::ENDNOTE; break; + case REF_STYLE : nSource = ReferenceFieldSource::STYLE; break; } rAny <<= nSource; } @@ -1018,6 +1024,7 @@ bool SwGetRefField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId ) case ReferenceFieldSource::BOOKMARK : m_nSubType = REF_BOOKMARK ; break; case ReferenceFieldSource::FOOTNOTE : m_nSubType = REF_FOOTNOTE ; break; case ReferenceFieldSource::ENDNOTE : m_nSubType = REF_ENDNOTE ; break; + case ReferenceFieldSource::STYLE : m_nSubType = REF_STYLE ; break; } } break; @@ -1115,7 +1122,7 @@ void SwGetRefFieldType::UpdateGetReferences() } // #i81002# - pGRef->UpdateField(pFormatField->GetTextField()); + pGRef->UpdateField(pFormatField->GetTextField(), nullptr); } CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr)); } @@ -1166,10 +1173,53 @@ bool IsMarkHintHidden(SwRootFrame const& rLayout, } // 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) +namespace +{ + enum StyleRefElementType + { + Default, + Reference, /* e.g. footnotes, endnotes */ + Marginal, /* headers, footers */ + }; + + /// Picks the first text node with a matching style from a double ended queue, starting at the front + /// This allows us to use the deque either as a stack or as a queue depending on whether we want to search up or down + SwTextNode* SearchForStyleAnchor(SwTextNode* pSelf, const std::deque& pToSearch, + std::u16string_view rStyleName, bool bCaseSensitive = true) + { + std::deque pSearching(pToSearch); + while (!pSearching.empty()) + { + SwNode* pCurrent = pSearching.front(); + pSearching.pop_front(); + + if (*pCurrent == *pSelf) + continue; + + SwTextNode* pTextNode = pCurrent->GetTextNode(); + if (!pTextNode) + continue; + + if (bCaseSensitive) + { + if (pTextNode->GetFormatColl()->GetName() == rStyleName) + return pTextNode; + } + else + { + if (pTextNode->GetFormatColl()->GetName().equalsIgnoreAsciiCase(rStyleName)) + return pTextNode; + } + } + + return nullptr; + } +} + +SwTextNode* SwGetRefFieldType::FindAnchor(SwDoc* pDoc, const OUString& rRefMark, + sal_uInt16 nSubType, sal_uInt16 nSeqNo, sal_Int32* pStt, + sal_Int32* pEnd, SwRootFrame const* const pLayout, + SwTextNode* pSelf, SwFrame* pContentFrame) { OSL_ENSURE( pStt, "Why did no one check the StartPos?" ); @@ -1284,6 +1334,250 @@ SwTextNode* SwGetRefFieldType::FindAnchor( SwDoc* pDoc, const OUString& rRefMark } } break; + case REF_STYLE: + if (!pSelf) break; + + const SwNodes& nodes = pDoc->GetNodes(); + + StyleRefElementType elementType = StyleRefElementType::Default; + const SwTextNode* pReference = nullptr; + + { /* Check if we're a footnote/endnote */ + for (SwTextFootnote* pFootnoteIdx : pDoc->GetFootnoteIdxs()) + { + if (pLayout && pLayout->IsHideRedlines() + && sw::IsFootnoteDeleted(rIDRA, *pFootnoteIdx)) + { + continue; + } + const SwNodeIndex* pIdx = pFootnoteIdx->GetStartNode(); + if (pIdx) + { + SwNodeIndex aIdx(*pIdx, 1); + SwTextNode* pFootnoteNode = aIdx.GetNode().GetTextNode(); + if (nullptr == pFootnoteNode) + pFootnoteNode + = static_cast(pDoc->GetNodes().GoNext(&aIdx)); + + if (*pSelf == *pFootnoteNode) + { + elementType = StyleRefElementType::Reference; + pReference = &pFootnoteIdx->GetTextNode(); + } + } + } + } + + if (pDoc->IsInHeaderFooter(*pSelf)) + { + elementType = StyleRefElementType::Marginal; + } + + if (pReference == nullptr) + { + pReference = pSelf; + } + + switch (elementType) + { + case Marginal: + { + // For marginals, styleref tries to act on the current page first + // 1. Get the page we're on, search it from top to bottom + + Point aPt; + std::pair const tmp(aPt, false); + + if (!pContentFrame) SAL_WARN("xmloff.text", ": Missing content frame for marginal styleref"); + const SwPageFrame* pPageFrame = nullptr; + + if (pContentFrame) + pPageFrame = pContentFrame->FindPageFrame(); + + const SwNode* pPageStart(nullptr); + const SwNode* pPageEnd(nullptr); + + if (pPageFrame) + { + const SwContentFrame* pPageStartFrame = pPageFrame->FindFirstBodyContent(); + const SwContentFrame* pPageEndFrame = pPageFrame->FindLastBodyContent(); + + if (pPageStartFrame) { + if (pPageStartFrame->IsTextFrame()) + { + pPageStart = static_cast(pPageStartFrame) + ->GetTextNodeFirst(); + } + else + { + pPageStart + = static_cast(pPageStartFrame)->GetNode(); + } + } + + if (pPageEndFrame) { + if (pPageEndFrame->IsTextFrame()) + { + pPageEnd = static_cast(pPageEndFrame) + ->GetTextNodeFirst(); + } + else + { + pPageEnd = static_cast(pPageEndFrame)->GetNode(); + } + } + } + + if (!pPageStart || !pPageEnd) + { + pPageStart = pReference; + pPageEnd = pReference; + } + + std::deque pAbovePage; + std::deque pInPage; + std::deque pBelowPage; + + bool beforeStart = true; + bool beforeEnd = true; + + for (SwNodeOffset n(0); n < nodes.Count(); n++) + { + if (beforeStart && *pPageStart == *nodes[n]) + { + beforeStart = false; + } + + if (beforeStart) + { + pAbovePage.push_front(nodes[n]); + } + else if (beforeEnd) + { + pInPage.push_back(nodes[n]); + + if (*pPageEnd == *nodes[n]) + { + beforeEnd = false; + } + } + else + { + pBelowPage.push_back(nodes[n]); + } + } + + pTextNd = SearchForStyleAnchor(pSelf, pInPage, rRefMark); + if (pTextNd) + { + break; + } + + // 2. Search up from the top of the page + pTextNd = SearchForStyleAnchor(pSelf, pAbovePage, rRefMark); + if (pTextNd) + { + break; + } + + // 3. Search down from the bottom of the page + pTextNd = SearchForStyleAnchor(pSelf, pBelowPage, rRefMark); + if (pTextNd) + { + break; + } + + // Word has case insensitive styles. LO has case sensitive styles. If we didn't find + // it yet, maybe we could with a case insensitive search. Let's do that + + pTextNd = SearchForStyleAnchor(pSelf, pInPage, rRefMark, + false /* bCaseSensitive */); + if (pTextNd) + { + break; + } + + pTextNd = SearchForStyleAnchor(pSelf, pAbovePage, rRefMark, + false /* bCaseSensitive */); + if (pTextNd) + { + break; + } + + pTextNd = SearchForStyleAnchor(pSelf, pBelowPage, rRefMark, + false /* bCaseSensitive */); + break; + } + case Reference: + case Default: + { + // Normally, styleref does searches around the field position + // For references, styleref acts from the position of the reference not the field + // Happily, the previous code saves either one into pReference, so the following is generic for both + + std::deque pNotBelowElement; + std::deque pBelowElement; + + bool beforeElement = true; + + for (SwNodeOffset n(0); n < nodes.Count(); n++) + { + if (beforeElement) + { + pNotBelowElement.push_front(nodes[n]); + + if (*pReference == *nodes[n]) + { + beforeElement = false; + } + } + else + { + pBelowElement.push_back(nodes[n]); + } + } + + // 1. Search up until we hit the top of the document + + pTextNd = SearchForStyleAnchor(pSelf, pNotBelowElement, rRefMark); + if (pTextNd) + { + break; + } + + // 2. Search down until we hit the bottom of the document + + pTextNd = SearchForStyleAnchor(pSelf, pBelowElement, rRefMark); + if (pTextNd) + { + break; + } + + // Again, we need to remember that Word styles are not case sensitive + + pTextNd = SearchForStyleAnchor(pSelf, pNotBelowElement, rRefMark, + false /* bCaseSensitive */); + if (pTextNd) + { + break; + } + + pTextNd = SearchForStyleAnchor(pSelf, pBelowElement, rRefMark, + false /* bCaseSensitive */); + break; + } + default: + OSL_FAIL(" - unknown getref element type"); + } + + if (pTextNd) + { + *pStt = 0; + if (pEnd) + *pEnd = pTextNd->GetText().getLength(); + } + + break; } return pTextNd; diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx index 464781428010..a912e169995b 100644 --- a/sw/source/core/text/txtfld.cxx +++ b/sw/source/core/text/txtfld.cxx @@ -240,7 +240,14 @@ SwExpandPortion *SwTextFormatter::NewFieldPortion( SwTextFormatInfo &rInf, bPlaceHolder = true; break; case SwFieldIds::GetRef: + { subType = static_cast(pField)->GetSubType(); + if (!bName && subType == REF_STYLE) + { + static_cast(pField)->UpdateField( + static_txtattr_cast(pHint), pFrame); + } + { OUString const str( bName ? pField->GetFieldName() @@ -251,7 +258,8 @@ SwExpandPortion *SwTextFormatter::NewFieldPortion( SwTextFormatInfo &rInf, static_cast(pRet)->m_nAttrFieldType = ATTR_BOOKMARKFLD; else if( subType == REF_SETREFATTR ) static_cast(pRet)->m_nAttrFieldType = ATTR_SETREFATTRFLD; - break; + } + break; case SwFieldIds::DateTime: subType = static_cast(pField)->GetSubType(); { diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx index 50d772d6b71c..04737fb21410 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -642,6 +642,9 @@ public: /// Find the bookmark name. OUString GetBookmarkName( sal_uInt16 nTyp, const OUString* pName, sal_uInt16 nSeqNo ); + /// Find out which style we should use in OOXML + OUString GetStyleRefName(const OUString& rName); + /// Use OutputItem() on an item set according to the parameters. void OutputItemSet( const SfxItemSet& rSet, bool bPapFormat, bool bChpFormat, sal_uInt16 nScript, bool bExportParentItemSet ); diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx index 4751047e0f74..eda8e17caa53 100644 --- a/sw/source/filter/ww8/ww8atr.cxx +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -128,6 +128,7 @@ #include #include #include +#include #include "sprmids.hxx" @@ -1071,6 +1072,18 @@ OUString MSWordExportBase::GetBookmarkName( sal_uInt16 nTyp, const OUString* pNa return BookmarkToWord( sRet ); // #i43956# - encode bookmark accordingly } +OUString MSWordExportBase::GetStyleRefName(const OUString& rName) +{ + SwTextFormatColls* pTextFormatColls = m_rDoc.GetTextFormatColls(); + SwTextFormatColl* pTextFormat = pTextFormatColls->FindFormatByName(rName); + + if (pTextFormat == nullptr) + return "\"" + rName + "\""; + // Didn't find the style, just keep the original name + + return "\"" + m_pStyles->GetStyleWWName(pTextFormat) + "\""; +} + /* File CHRATR.HXX: */ void WW8AttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) { @@ -3319,26 +3332,45 @@ void AttributeOutputBase::TextField( const SwFormatField& rField ) sStr = FieldString(eField) + GetExport().GetBookmarkName(nSubType, nullptr, rRField.GetSeqNo()); break; + case REF_STYLE: + sStr = FieldString(ww::eSTYLEREF) + + GetExport().GetStyleRefName(pField->GetPar1()); + eField = ww::eSTYLEREF; + break; } - if (eField != ww::eNONE) + OUString sExtraFlags = "\\h "; // by default, include a hyperlink + + switch (eField) { - switch (pField->GetFormat()) - { - case REF_UPDOWN: - sStr += " \\p \\h "; // with hyperlink - break; - case REF_CHAPTER: - sStr += " \\n \\h "; // with hyperlink - break; - default: - sStr += " \\h "; // insert hyperlink - break; - } - GetExport().OutputField(pField, eField, sStr); + case ww::eNONE: + bWriteExpand = true; + break; + case ww::eSTYLEREF: + sExtraFlags = ""; // styleref fields do not work if they have a hyperlink + [[fallthrough]]; + default: + switch (pField->GetFormat()) + { + case REF_NUMBER: + sStr += " \\r " + sExtraFlags; + break; + case REF_NUMBER_FULL_CONTEXT: + sStr += " \\w " + sExtraFlags; + break; + case REF_UPDOWN: + sStr += " \\p " + sExtraFlags; + break; + case REF_NUMBER_NO_CONTEXT: + case REF_CHAPTER: + sStr += " \\n " + sExtraFlags; + break; + default: + sStr += " " + sExtraFlags; + break; + } + GetExport().OutputField(pField, eField, sStr); } - else - bWriteExpand = true; } break; case SwFieldIds::CombinedChars: @@ -3388,7 +3420,8 @@ void AttributeOutputBase::TextField( const SwFormatField& rField ) break; case SwFieldIds::Chapter: bWriteExpand = true; - if (GetExport().m_bOutKF && rField.GetTextField()) + + if (rField.GetTextField()) { const SwTextNode *pTextNd = GetExport().GetHdFtPageRoot(); if (!pTextNd) @@ -3400,10 +3433,22 @@ void AttributeOutputBase::TextField( const SwFormatField& rField ) { SwChapterField aCopy(*static_cast(pField)); aCopy.ChangeExpansion(*pTextNd, false); - const OUString sStr = FieldString(ww::eSTYLEREF) - + " " - + OUString::number(aCopy.GetLevel() + 1) - + " \\* MERGEFORMAT "; + + OUString sStr; + if (GetExport().m_bOutKF) { + // In headers and footers, use the chapter number as the style name + sStr = FieldString(ww::eSTYLEREF) + + " " + + OUString::number(aCopy.GetLevel() + 1) + + " \\* MERGEFORMAT "; + } else { + // Otherwise, get the style of the text and use it as the style name + const SwTextNode* pOutlineNd = pTextNd->FindOutlineNodeOfLevel(aCopy.GetLevel()); + + sStr = FieldString(ww::eSTYLEREF) + + GetExport().GetStyleRefName(pOutlineNd->GetFormatColl()->GetName()); + } + GetExport().OutputField(pField, ww::eSTYLEREF, sStr); bWriteExpand = false; } diff --git a/sw/source/filter/ww8/ww8par5.cxx b/sw/source/filter/ww8/ww8par5.cxx index 894b76f94891..400b357009df 100644 --- a/sw/source/filter/ww8/ww8par5.cxx +++ b/sw/source/filter/ww8/ww8par5.cxx @@ -969,8 +969,7 @@ tools::Long SwWW8ImplReader::Read_Field(WW8PLCFManResult* pRes) bool bHasHandler = aWW8FieldTab[aF.nId] != nullptr; if (aF.nId == 10) // STYLEREF { - // STYLEREF, by default these are not handled. - bHasHandler = false; + bool bHandledByChapter = false; sal_uInt64 nOldPos = m_pStrm->Tell(); OUString aStr; aF.nLCode = m_xSBase->WW8ReadString(*m_pStrm, aStr, m_xPlcxMan->GetCpOfs() + aF.nSCode, aF.nLCode, m_eTextCharSet); @@ -980,9 +979,9 @@ tools::Long SwWW8ImplReader::Read_Field(WW8PLCFManResult* pRes) sal_Int32 nRet = aReadParam.SkipToNextToken(); if (nRet == -2 && !aReadParam.GetResult().isEmpty()) // Single numeric argument: this can be handled by SwChapterField. - bHasHandler = rtl::isAsciiDigit(aReadParam.GetResult()[0]); + bHandledByChapter = rtl::isAsciiDigit(aReadParam.GetResult()[0]); - if (bHasHandler) + if (bHandledByChapter) { nRet = aReadParam.SkipToNextToken(); // Handle using SwChapterField only in case there is no \[a-z] diff --git a/sw/source/ui/fldui/fldref.cxx b/sw/source/ui/fldui/fldref.cxx index 54f9c9fc98dc..61083223eb3f 100644 --- a/sw/source/ui/fldui/fldref.cxx +++ b/sw/source/ui/fldui/fldref.cxx @@ -47,6 +47,9 @@ // #i83479# #define REFFLDFLAG_HEADING 0x7100 #define REFFLDFLAG_NUMITEM 0x7200 +#define REFFLDFLAG_STYLE 0xc000 +/* we skip past 0x8000, 0x9000, 0xa000 and 0xb000 as when we bitwise 'and' + with REFFLDFLAG they are false */ static sal_uInt16 nFieldDlgFormatSel = 0; @@ -82,6 +85,7 @@ SwFieldRefPage::SwFieldRefPage(weld::Container* pPage, weld::DialogController* p // #i83479# m_sHeadingText = m_xTypeLB->get_text(3); m_sNumItemText = m_xTypeLB->get_text(4); + m_sStyleText = m_xTypeLB->get_text(5); auto nHeight = m_xTypeLB->get_height_rows(8); auto nWidth = m_xTypeLB->get_approximate_digit_width() * FIELD_COLUMN_WIDTH; @@ -239,6 +243,9 @@ void SwFieldRefPage::Reset(const SfxItemSet* ) m_xTypeLB->append(OUString::number(REFFLDFLAG_ENDNOTE), m_sEndnoteText); } + // stylerefs + m_xTypeLB->append(OUString::number(REFFLDFLAG_STYLE), m_sStyleText); + m_xTypeLB->thaw(); // select old Pos @@ -274,7 +281,7 @@ void SwFieldRefPage::Reset(const SfxItemSet* ) } } TypeHdl(*m_xTypeLB); - if (nFormatBoxPosition < m_xFormatLB->n_children()) + if (!IsFieldEdit() && nFormatBoxPosition < m_xFormatLB->n_children()) { m_xFormatLB->select(nFormatBoxPosition); } @@ -356,6 +363,17 @@ IMPL_LINK_NOARG(SwFieldRefPage, TypeHdl, weld::TreeView&, void) nFlag = REFFLDFLAG; break; } + + case REF_STYLE: + { + SwGetRefField const*const pRefField(dynamic_cast(GetCurField())); + if (pRefField) + { + sName = pRefField->GetPar1(); + } + nFlag = REFFLDFLAG_STYLE; + break; + } } if (m_xTypeLB->find_text(sName) == -1) // reference to deleted mark @@ -610,7 +628,7 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) m_xSelectionToolTipLB->append(sId, pIDoc->getOutlineText(nOutlIdx, pSh->GetLayout(), true, true, false)); if ((IsFieldEdit() && pRefField - && pRefField->GetReferencedTextNode() == maOutlineNodes[nOutlIdx]) + && pRefField->GetReferencedTextNode(nullptr, nullptr) == maOutlineNodes[nOutlIdx]) || mpSavedSelectedTextNode == maOutlineNodes[nOutlIdx]) { m_sSelectionToolTipLBId = sId; @@ -645,7 +663,7 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) m_xSelectionToolTipLB->append(sId, pIDoc->getListItemText(*maNumItems[nNumItemIdx], *pSh->GetLayout())); if ((IsFieldEdit() && pRefField - && pRefField->GetReferencedTextNode() == maNumItems[nNumItemIdx]->GetTextNode()) + && pRefField->GetReferencedTextNode(nullptr, nullptr) == maNumItems[nNumItemIdx]->GetTextNode()) || mpSavedSelectedTextNode == maNumItems[nNumItemIdx]->GetTextNode()) { m_sSelectionToolTipLBId = sId; @@ -660,6 +678,32 @@ void SwFieldRefPage::UpdateSubType(const OUString& filterString) } } } + else if (nTypeId == REFFLDFLAG_STYLE) + { + const IDocumentOutlineNodes* pIDoc(pSh->getIDocumentOutlineNodesAccess()); + pIDoc->getOutlineNodes(maOutlineNodes); + + SfxStyleSheetBasePool* pStyleSheetPool + = pSh->GetDoc()->GetDocShell()->GetStyleSheetPool(); + auto stylesheetIterator + = pStyleSheetPool->CreateIterator(SfxStyleFamily::Para, SfxStyleSearchBits::Used); + + SfxStyleSheetBase* pStyle = stylesheetIterator->First(); + while (pStyle != nullptr) + { + bool isSubstring = MatchSubstring(pStyle->GetName(), filterString); + + if (isSubstring) + { + m_xSelectionLB->append_text(pStyle->GetName()); + } + + pStyle = stylesheetIterator->Next(); + } + + if (IsFieldEdit() && pRefField) + sOldSel = pRefField->GetPar1(); + } else { // get the fields to Seq-FieldType: @@ -803,6 +847,7 @@ sal_Int32 SwFieldRefPage::FillFormatLB(sal_uInt16 nTypeId) // reference has less that the annotation sal_uInt16 nSize( 0 ); + sal_uInt16 nOffset( 0 ); bool bAddCrossRefFormats( false ); switch (nTypeId) { @@ -818,6 +863,11 @@ sal_Int32 SwFieldRefPage::FillFormatLB(sal_uInt16 nTypeId) case REFFLDFLAG_ENDNOTE: nSize = FMT_REF_PAGE_PGDSC_IDX + 1; break; + case REFFLDFLAG_STYLE: + nOffset = FMT_REF_TEXT_IDX; + nSize = FMT_REF_UPDOWN_IDX + 1 - nOffset; + bAddCrossRefFormats = true; + break; default: // #i83479# @@ -837,7 +887,7 @@ sal_Int32 SwFieldRefPage::FillFormatLB(sal_uInt16 nTypeId) nTypeId = static_cast(SwFieldTypesEnum::GetRef); SwFieldTypesEnum nFieldType = static_cast(nTypeId); - for (sal_uInt16 i = 0; i < nSize; i++) + for (sal_uInt16 i = nOffset; i < nSize + nOffset; i++) { OUString sId(OUString::number(GetFieldMgr().GetFormatId( nFieldType, i ))); m_xFormatLB->append(sId, GetFieldMgr().GetFormatStr(nFieldType, i)); @@ -1077,6 +1127,18 @@ bool SwFieldRefPage::FillItemSet(SfxItemSet* ) } } } + else if (nTypeId == REFFLDFLAG_STYLE) + { + int nEntry = m_xSelectionLB->get_selected_index(); + if (nEntry != -1) + { + aName = m_xSelectionLB->get_text(nEntry); + nTypeId = static_cast(SwFieldTypesEnum::GetRef); + nSubType = REF_STYLE; + } else { + SAL_WARN("sw.ui", " no entry selected in selection listbox!"); + } + } else // SequenceFields { // get fields for Seq-FieldType: diff --git a/sw/source/ui/fldui/fldref.hxx b/sw/source/ui/fldui/fldref.hxx index 68dc6f6480be..872e6f526bb6 100644 --- a/sw/source/ui/fldui/fldref.hxx +++ b/sw/source/ui/fldui/fldref.hxx @@ -34,6 +34,7 @@ class SwFieldRefPage : public SwFieldPage // #i83479# OUString m_sHeadingText; OUString m_sNumItemText; + OUString m_sStyleText; IDocumentOutlineNodes::tSortedOutlineNodeList maOutlineNodes; IDocumentListItems::tSortedNodeNumList maNumItems; diff --git a/sw/source/uibase/docvw/edtwin2.cxx b/sw/source/uibase/docvw/edtwin2.cxx index c3bea9094135..0c47b2ec5540 100644 --- a/sw/source/uibase/docvw/edtwin2.cxx +++ b/sw/source/uibase/docvw/edtwin2.cxx @@ -567,7 +567,7 @@ void SwEditWin::RequestHelp(const HelpEvent &rEvt) if ( pRefField->IsRefToHeadingCrossRefBookmark() || pRefField->IsRefToNumItemCrossRefBookmark() ) { - sText = pRefField->GetExpandedTextOfReferencedTextNode(*rSh.GetLayout()); + sText = pRefField->GetExpandedTextOfReferencedTextNode(*rSh.GetLayout(), nullptr, nullptr); if ( sText.getLength() > 80 ) { sText = OUString::Concat(sText.subView(0, 80)) + "..."; diff --git a/sw/source/uibase/fldui/fldmgr.cxx b/sw/source/uibase/fldui/fldmgr.cxx index bf53ae29b0a4..c61c9c26cba5 100644 --- a/sw/source/uibase/fldui/fldmgr.cxx +++ b/sw/source/uibase/fldui/fldmgr.cxx @@ -66,6 +66,8 @@ #include #include #include +#include +#include #include #include #include @@ -1132,6 +1134,7 @@ bool SwFieldMgr::InsertField( } nFormatId %= SAL_N_ELEMENTS(FMT_REF_ARY); } + pField.reset(new SwGetRefField(pTyp, rData.m_sPar1, sReferenceLanguage, nSubType, nSeqNo, nFormatId)); bExp = true; break; diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx index 5d56eada213b..43bb9e78bb5f 100644 --- a/sw/source/uibase/utlui/content.cxx +++ b/sw/source/uibase/utlui/content.cxx @@ -707,7 +707,7 @@ void SwContentType::FillMemberList(bool* pbContentChanged) { OUString sExpandedTextOfReferencedTextNode = pRefField->GetExpandedTextOfReferencedTextNode( - *m_pWrtShell->GetLayout()); + *m_pWrtShell->GetLayout(), nullptr, nullptr); if (sExpandedTextOfReferencedTextNode.getLength() > 80) { sExpandedTextOfReferencedTextNode = OUString::Concat( diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index 67fe56280c18..fd27e92510d5 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -5829,7 +5829,7 @@ static const FieldConversionMap_t & lcl_GetFieldConversion() {"SEQ", {"SetExpression", FIELD_SEQ }}, {"SET", {"SetExpression", FIELD_SET }}, // {"SKIPIF", {"", FIELD_SKIPIF }}, -// {"STYLEREF", {"", FIELD_STYLEREF }}, + {"STYLEREF", {"GetReference", FIELD_STYLEREF }}, {"SUBJECT", {"DocInfo.Subject", FIELD_SUBJECT }}, {"SYMBOL", {"", FIELD_SYMBOL }}, {"TEMPLATE", {"TemplateName", FIELD_TEMPLATE }}, @@ -7519,9 +7519,11 @@ void DomainMapper_Impl::CloseFieldCommand() break; case FIELD_PAGEREF: case FIELD_REF: + case FIELD_STYLEREF: if (xFieldProperties.is() && !IsInTOC()) { bool bPageRef = aIt->second.eFieldId == FIELD_PAGEREF; + bool bStyleRef = aIt->second.eFieldId == FIELD_STYLEREF; // Do we need a GetReference (default) or a GetExpression field? uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY ); @@ -7531,12 +7533,50 @@ void DomainMapper_Impl::CloseFieldCommand() "com.sun.star.text.FieldMaster.SetExpression." + sFirstParam)) { - xFieldProperties->setPropertyValue( - getPropertyName(PROP_REFERENCE_FIELD_SOURCE), - uno::Any( sal_Int16(text::ReferenceFieldSource::BOOKMARK)) ); - xFieldProperties->setPropertyValue( - getPropertyName(PROP_SOURCE_NAME), - uno::Any(sFirstParam) ); + if (bStyleRef) + { + xFieldProperties->setPropertyValue( + getPropertyName(PROP_REFERENCE_FIELD_SOURCE), + uno::Any(sal_Int16(text::ReferenceFieldSource::STYLE))); + + OUString sStyleSheetName + = GetStyleSheetTable()->ConvertStyleName(sFirstParam, true); + + uno::Any aStyleDisplayName; + + uno::Reference xStylesSupplier( + GetTextDocument(), uno::UNO_QUERY_THROW); + uno::Reference xStyleFamilies + = xStylesSupplier->getStyleFamilies(); + uno::Reference xStyles; + xStyleFamilies->getByName(getPropertyName(PROP_PARAGRAPH_STYLES)) + >>= xStyles; + uno::Reference xStyle; + + try + { + xStyles->getByName(sStyleSheetName) >>= xStyle; + aStyleDisplayName = xStyle->getPropertyValue("DisplayName"); + } + catch (css::container::NoSuchElementException) + { + aStyleDisplayName <<= sStyleSheetName; + } + + xFieldProperties->setPropertyValue( + getPropertyName(PROP_SOURCE_NAME), aStyleDisplayName); + } + else + { + xFieldProperties->setPropertyValue( + getPropertyName(PROP_REFERENCE_FIELD_SOURCE), + uno::Any( sal_Int16(text::ReferenceFieldSource::BOOKMARK)) ); + + xFieldProperties->setPropertyValue( + getPropertyName(PROP_SOURCE_NAME), + uno::Any(sFirstParam)); + } + sal_Int16 nFieldPart = (bPageRef ? text::ReferenceFieldPart::PAGE : text::ReferenceFieldPart::TEXT); OUString sValue; if( lcl_FindInCommand( pContext->GetCommand(), 'p', sValue )) @@ -7631,7 +7671,6 @@ void DomainMapper_Impl::CloseFieldCommand() handleFieldSet(pContext, xFieldInterface, xFieldProperties); break; case FIELD_SKIPIF : break; - case FIELD_STYLEREF : break; case FIELD_SUBJECT : { if (!sFirstParam.isEmpty()) diff --git a/writerfilter/source/dmapper/FieldTypes.hxx b/writerfilter/source/dmapper/FieldTypes.hxx index a907a7af6c22..144d1efe1370 100644 --- a/writerfilter/source/dmapper/FieldTypes.hxx +++ b/writerfilter/source/dmapper/FieldTypes.hxx @@ -209,8 +209,9 @@ enum FieldId */ ,FIELD_SKIPIF /* STYLEREF stylename \* MERGEFORMAT -> - not imported in old ww8 filter - todo: add an equivalent field type + implemented using GetReference, but some switches are not implemented yet + \l isn't implemented + \t isn't implemented */ ,FIELD_STYLEREF /* SUBJECT subject \* Defaultswitch \* MERGEFORMAT -> diff --git a/xmloff/inc/txtflde.hxx b/xmloff/inc/txtflde.hxx index 8670cac40cf6..459dea29f550 100644 --- a/xmloff/inc/txtflde.hxx +++ b/xmloff/inc/txtflde.hxx @@ -119,6 +119,7 @@ enum FieldIdEnum { FIELD_ID_REF_BOOKMARK, // get reference field (bookmark) FIELD_ID_REF_FOOTNOTE, // get reference field (footnote) FIELD_ID_REF_ENDNOTE, // get reference field (endnote) + FIELD_ID_REF_STYLE, // styleref field FIELD_ID_DDE, // DDE field FIELD_ID_BIBLIOGRAPHY, // bibliography index entry diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 38ee9bb853c3..6519570d6a03 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -1915,6 +1915,7 @@ namespace xmloff::token { TOKEN( "structure-protected", XML_STRUCTURE_PROTECTED ), TOKEN( "style", XML_STYLE ), TOKEN( "style-name", XML_STYLE_NAME ), + TOKEN( "style-ref", XML_STYLE_REF ), TOKEN( "styles", XML_STYLES ), TOKEN( "stylesheet", XML_STYLESHEET ), TOKEN( "sub-table", XML_SUB_TABLE ), diff --git a/xmloff/source/text/txtflde.cxx b/xmloff/source/text/txtflde.cxx index 2299773206db..5a109cef0a43 100644 --- a/xmloff/source/text/txtflde.cxx +++ b/xmloff/source/text/txtflde.cxx @@ -563,6 +563,9 @@ enum FieldIdEnum XMLTextFieldExport::MapFieldName( case ReferenceFieldSource::ENDNOTE: nToken = FIELD_ID_REF_ENDNOTE; break; + case ReferenceFieldSource::STYLE: + nToken = FIELD_ID_REF_STYLE; + break; default: nToken = FIELD_ID_UNKNOWN; break; @@ -703,6 +706,7 @@ bool XMLTextFieldExport::IsStringField( case FIELD_ID_REF_BOOKMARK: case FIELD_ID_REF_FOOTNOTE: case FIELD_ID_REF_ENDNOTE: + case FIELD_ID_REF_STYLE: case FIELD_ID_MACRO: case FIELD_ID_TEMPLATE_NAME: case FIELD_ID_CHAPTER: @@ -914,6 +918,7 @@ void XMLTextFieldExport::ExportFieldAutoStyle( case FIELD_ID_REF_BOOKMARK: case FIELD_ID_REF_FOOTNOTE: case FIELD_ID_REF_ENDNOTE: + case FIELD_ID_REF_STYLE: case FIELD_ID_MACRO: case FIELD_ID_REFPAGE_SET: case FIELD_ID_REFPAGE_GET: @@ -1646,6 +1651,30 @@ void XMLTextFieldExport::ExportFieldHelper( sPresentation); break; + case FIELD_ID_REF_STYLE: + { + ProcessString(XML_REFERENCE_FORMAT, + MapReferenceType(GetInt16Property(gsPropertyReferenceFieldPart, rPropSet)), + XML_TEMPLATE); + ProcessString(XML_REF_NAME, GetStringProperty(gsPropertySourceName, rPropSet)); + if (xPropSetInfo->hasPropertyByName(gsPropertyReferenceFieldLanguage) + && GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) + { + // export text:reference-language attribute, if not empty + ProcessString(XML_REFERENCE_LANGUAGE, + GetStringProperty(gsPropertyReferenceFieldLanguage, rPropSet), true, + XML_NAMESPACE_LO_EXT); + } + SvXMLElementExport aElem( + GetExport(), + XML_NAMESPACE_LO_EXT, + MapReferenceSource(GetInt16Property(gsPropertyReferenceFieldSource, rPropSet)), + false, + false); + GetExport().Characters(sPresentation); + break; + } + case FIELD_ID_DDE: // name from field master ProcessString(XML_CONNECTION_NAME, @@ -3160,6 +3189,9 @@ enum XMLTokenEnum XMLTextFieldExport::MapReferenceSource(sal_Int16 nType) case ReferenceFieldSource::ENDNOTE: eElement = XML_NOTE_REF; break; + case ReferenceFieldSource::STYLE: + eElement = XML_STYLE_REF; + break; default: OSL_FAIL("unknown reference source"); break; diff --git a/xmloff/source/text/txtfldi.cxx b/xmloff/source/text/txtfldi.cxx index dc6fb6646857..05086325da2b 100644 --- a/xmloff/source/text/txtfldi.cxx +++ b/xmloff/source/text/txtfldi.cxx @@ -407,6 +407,7 @@ XMLTextFieldImportContext::CreateTextFieldImportContext( case XML_ELEMENT(TEXT, XML_BOOKMARK_REF): case XML_ELEMENT(TEXT, XML_NOTE_REF): case XML_ELEMENT(TEXT, XML_SEQUENCE_REF): + case XML_ELEMENT(LO_EXT, XML_STYLE_REF): pContext = new XMLReferenceFieldImportContext( rImport, rHlp, nToken ); break; @@ -2509,6 +2510,9 @@ void XMLReferenceFieldImportContext::startFastElement( case XML_ELEMENT(TEXT, XML_SEQUENCE_REF): nSource = ReferenceFieldSource::SEQUENCE_FIELD; break; + case XML_ELEMENT(LO_EXT, XML_STYLE_REF): + nSource = ReferenceFieldSource::STYLE; + break; default: XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElementToken); bTypeOK = false; @@ -2577,6 +2581,7 @@ void XMLReferenceFieldImportContext::PrepareField( { case XML_ELEMENT(TEXT, XML_REFERENCE_REF): case XML_ELEMENT(TEXT, XML_BOOKMARK_REF): + case XML_ELEMENT(LO_EXT, XML_STYLE_REF): xPropertySet->setPropertyValue("SourceName", Any(sName)); break; diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 58f8ff8e8c4e..59ae40480d70 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -1815,6 +1815,7 @@ stroke-width structure-protected style style-name +style-ref styles stylesheet sub-table