include/xmloff/xmltoken.hxx | 1 offapi/com/sun/star/text/ReferenceFieldSource.idl | 5 schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng | 23 sw/inc/fldref.hrc | 1 sw/inc/reffld.hxx | 22 sw/qa/core/fields/data/tdf86790.docx |binary sw/qa/core/fields/fields.cxx | 228 +++++ sw/qa/extras/ooxmlexport/ooxmlexport.cxx | 2 sw/qa/extras/uiwriter/uiwriter8.cxx | 6 sw/source/core/access/accpara.cxx | 3 sw/source/core/crsr/crstrvl.cxx | 10 sw/source/core/fields/reffld.cxx | 484 +++++++++--- sw/source/core/text/txtfld.cxx | 10 sw/source/filter/ww8/wrtww8.hxx | 3 sw/source/filter/ww8/ww8atr.cxx | 87 +- sw/source/filter/ww8/ww8par5.cxx | 7 sw/source/ui/fldui/fldref.cxx | 70 + sw/source/ui/fldui/fldref.hxx | 1 sw/source/uibase/docvw/edtwin2.cxx | 2 sw/source/uibase/fldui/fldmgr.cxx | 3 sw/source/uibase/utlui/content.cxx | 2 writerfilter/source/dmapper/DomainMapper_Impl.cxx | 55 + writerfilter/source/dmapper/FieldTypes.hxx | 5 xmloff/inc/txtflde.hxx | 1 xmloff/source/core/xmltoken.cxx | 1 xmloff/source/text/txtflde.cxx | 32 xmloff/source/text/txtfldi.cxx | 5 xmloff/source/token/tokens.txt | 1 28 files changed, 920 insertions(+), 150 deletions(-)
New commits: commit 32c588dd1164aa2fc4c8120ddb74bd510cc082f9 Author: Skyler Grey <skyler.g...@collabora.com> AuthorDate: Thu Sep 14 08:48:16 2023 +0000 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Oct 20 08:53:34 2023 +0200 tdf#86790: Add support for a word-style styleref STYLEREF is a field type in Word which changes its content based on nearby paragraphs. For example, upon creating a styleref referencing "Heading 1" you will see the text of the nearest "Heading 1"-styled paragraph that is above the field. This patch implements STYLEREF in Writer as a cross-reference. By using "insert>cross-reference>styles" you'll be presented with a list of styles. Selecting one and clicking "insert" will create a field which has text from the "most relevant" instance of the style. To find the most relevant instance we first search up for paragraphs with the style, and if there are any we take the closest. If there weren't any, we search down for paragraphs with the style. This patch also updates our use of STYLEREF for chapters exported to docx by using it for all chapters not only those in headers and footers. This allows us to approximate more chapter field functionality even when moving between Writer and Word. Finally, this patch adds some tests for STYLEREF: - testTdf86790 tests that the "sample file with STYLEREF" document from tdf#86790 has the correct fields - testStyleRefSearchUp tests that the STYLEREF searches up when there are bits of text both above and below it - testStyleRefSearchDown tests that the STYLEREF searches down when there are bits of text below it only - testMarginalStyleRef tests that the STYLEREF searches from the page top when it is placed in a footer - testFootnotetyleRef tests that the STYLEREF searches from the reference mark when it is placed in a footnote Still TODO: - [ ] Update documentation - [ ] Implement reverse-searching (\l) and nondelimiter suppression (\t) - Probably these 2 will be in a followup patch Change-Id: I25dd7a6940abee5651a784b9059fe23b32547d6c Signed-off-by: Skyler Grey <skyler.g...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157456 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> 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. </rng:element> </rng:define> + <!-- TODO no proposal --> + <rng:define name="loext-style-reference"> + <rng:element name="loext:style-ref"> + <rng:interleave> + <rng:ref name="text-common-ref-content"/> + <rng:optional> + <rng:attribute name="text:reference-format"> + <rng:choice> + <rng:value>text</rng:value> + <rng:value>direction</rng:value> + <rng:value>number</rng:value> + <rng:value>number-no-superior</rng:value> + <rng:value>number-all-superior</rng:value> + </rng:choice> + </rng:attribute> + </rng:optional> + </rng:interleave> + </rng:element> + </rng:define> + <rng:define name="paragraph-content" combine="choice"> + <rng:ref name="loext-style-reference"/> + </rng:define> + <rng:define name="loext-qrcode"> <rng:element name="loext:qrcode"> <rng:attribute name="office:string-value"> 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 <SwTextField> instance, which represents the text attribute for the <SwGetRefField> instance, has to be passed to the method. - This <SwTextField> instance is needed for the reference format type REF_UPDOWN - and REF_NUMBER. + This <SwTextField> 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 <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/text/ControlCharacter.hpp> +#include <com/sun/star/text/ReferenceFieldPart.hpp> +#include <com/sun/star/text/ReferenceFieldSource.hpp> +#include <com/sun/star/text/XFootnote.hpp> +#include <com/sun/star/text/XFootnotesSupplier.hpp> +#include <com/sun/star/text/XSimpleText.hpp> +#include <com/sun/star/text/XTextField.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/text/XPageCursor.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/text/XSimpleText.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/text/XPageCursor.hpp> +#include <com/sun/star/text/XTextCursor.hpp> +#include <com/sun/star/uno/Any.h> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Sequence.h> +#include <cppunit/TestAssert.h> +#include <cppunit/extensions/HelperMacros.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> #include <swmodeltestbase.hxx> #include <com/sun/star/text/XTextDocument.hpp> @@ -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<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xFieldsAccess( + xTextFieldsSupplier->getTextFields()); + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + + // Similarly to testChapterFieldsFollowedBy, the fields are enumerated with #1 first and everything else in reverse order + std::vector<std::pair<OUString, OUString>> 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<text::XTextField> 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<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + uno::Reference<beans::XPropertySet> 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<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextField> xField( + xFactory->createInstance("com.sun.star.text.TextField.GetReference"), uno::UNO_QUERY); + + uno::Reference<beans::XPropertySet> 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<text::XTextCursor> 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<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + uno::Reference<beans::XPropertySet> xCursorPropertySet(xCursor, uno::UNO_QUERY); + + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextField> xField( + xFactory->createInstance("com.sun.star.text.TextField.GetReference"), uno::UNO_QUERY); + + uno::Reference<beans::XPropertySet> 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<text::XTextCursor> 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<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + uno::Reference<beans::XPropertySet> 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<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextField> xField( + xFactory->createInstance("com.sun.star.text.TextField.GetReference"), uno::UNO_QUERY); + + uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(mxComponent, + uno::UNO_QUERY); + uno::Reference<container::XNameContainer> xParagraphStylesContainer( + xStyleFamiliesSupplier->getStyleFamilies()->getByName("PageStyles"), uno::UNO_QUERY); + + uno::Reference<beans::XPropertySet> xPagePropertySet( + xParagraphStylesContainer->getByName("Standard"), uno::UNO_QUERY); + + xPagePropertySet->setPropertyValue("FooterIsOn", uno::Any(true)); + uno::Reference<text::XText> xFooterText(xPagePropertySet->getPropertyValue("FooterText"), + uno::UNO_QUERY); + + uno::Reference<beans::XPropertySet> 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<text::XTextRange> 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<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + uno::Reference<beans::XPropertySet> 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<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + + uno::Reference<text::XFootnote> 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<text::XTextField> xField( + xFactory->createInstance("com.sun.star.text.TextField.GetReference"), uno::UNO_QUERY); + + uno::Reference<beans::XPropertySet> 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<text::XSimpleText> xFootnoteText(xFootnote, uno::UNO_QUERY); + uno::Reference<text::XTextRange> 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<text::XText> xHeaderText = getProperty< uno::Reference<text::XText> >(getStyles("PageStyles")->getByName("Standard"), "HeaderText"); - CPPUNIT_ASSERT_EQUAL(OUString("TextFieldStart"), getProperty<OUString>(getRun(getParagraphOfText(1, xHeaderText), 1), "TextPortionType")); + CPPUNIT_ASSERT_EQUAL(OUString("TextField"), getProperty<OUString>(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<SwGetRefField const*>(fields[0]->GetField())->GetSetRefName()); - CPPUNIT_ASSERT_EQUAL(OUString("Text"), - static_cast<SwGetRefField const*>(fields[0]->GetField()) - ->GetExpandedTextOfReferencedTextNode(*pWrtShell->GetLayout())); + CPPUNIT_ASSERT_EQUAL(OUString("Text"), static_cast<SwGetRefField const*>(fields[0]->GetField()) + ->GetExpandedTextOfReferencedTextNode( + *pWrtShell->GetLayout(), nullptr, nullptr)); CPPUNIT_ASSERT_EQUAL(sal_uInt16(REF_BOOKMARK), fields[1]->GetField()->GetSubType()); CPPUNIT_ASSERT( static_cast<SwGetRefField const*>(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<const SwGetRefField*>(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 <reffld.hxx> #include <expfld.hxx> #include <txtfrm.hxx> +#include <notxtfrm.hxx> #include <flyfrm.hxx> #include <pagedesc.hxx> #include <IMark.hxx> @@ -65,6 +66,7 @@ #include <string_view> #include <map> #include <algorithm> +#include <deque> 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<SwGetRefFieldType*>(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 <pFieldTextAttr> 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<SwGetRefFieldType*>(GetTyp())->GetDoc(); + for (SwRootFrame const* const pLay : rDoc.GetAllLayouts()) + { + if (pLay->IsHideRedlines()) + { + UpdateField(pFieldTextAttr, pFrame, pLay, m_sTextRLHidden); + } + else + { + UpdateField(pFieldTextAttr, pFrame, pLay, m_sText); + } + } +} + +void SwGetRefField::UpdateField(const SwTextField* pFieldTextAttr, SwFrame* pFrameContainingField, + const SwRootFrame* const pLayout, OUString& rText) +{ SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc(); + + rText.clear(); + // finding the reference target (the number) sal_Int32 nNumStart = -1; sal_Int32 nNumEnd = -1; SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor( - &rDoc, m_sSetRefName, m_nSubType, m_nSeqNo, &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<SwTextFrame*>(pTextNd->getLayoutFrame(pLay, nullptr, nullptr)); + SwTextFrame const* pFrame = static_cast<SwTextFrame*>(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<SwNode*>& pToSearch, + std::u16string_view rStyleName, bool bCaseSensitive = true) + { + std::deque<SwNode*> pSearching(pToSearch); + while (!pSearching.empty()) + { + SwNode* pCurrent = pSearching.front(); + pSearching.pop_front(); + + if (*pCurrent == *pSelf) + continue; + + SwTextNode* pTextNode = pCurrent->GetTextNode(); + if (!pTextNode) + continue; + + if (bCaseSensitive) + { + if (pTextNode->GetFormatColl()->GetName() == rStyleName) + return pTextNode; + } + else + { + if (pTextNode->GetFormatColl()->GetName().equalsIgnoreAsciiCase(rStyleName)) + return pTextNode; + } + } + + return nullptr; + } +} + +SwTextNode* SwGetRefFieldType::FindAnchor(SwDoc* pDoc, const OUString& rRefMark, + sal_uInt16 nSubType, sal_uInt16 nSeqNo, sal_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<SwTextNode*>(pDoc->GetNodes().GoNext(&aIdx)); + + if (*pSelf == *pFootnoteNode) + { + elementType = StyleRefElementType::Reference; + pReference = &pFootnoteIdx->GetTextNode(); + } + } + } + } + + if (pDoc->IsInHeaderFooter(*pSelf)) + { + elementType = StyleRefElementType::Marginal; + } + + if (pReference == nullptr) + { + pReference = pSelf; + } + + switch (elementType) + { + case Marginal: + { + // For marginals, styleref tries to act on the current page first + // 1. Get the page we're on, search it from top to bottom + + Point aPt; + std::pair<Point, bool> const tmp(aPt, false); + + if (!pContentFrame) SAL_WARN("xmloff.text", "<SwGetRefFieldType::FindAnchor(..)>: Missing content frame for marginal styleref"); + const SwPageFrame* pPageFrame = nullptr; + + if (pContentFrame) + pPageFrame = pContentFrame->FindPageFrame(); + + const SwNode* pPageStart(nullptr); + const SwNode* pPageEnd(nullptr); + + if (pPageFrame) + { + const SwContentFrame* pPageStartFrame = pPageFrame->FindFirstBodyContent(); + const SwContentFrame* pPageEndFrame = pPageFrame->FindLastBodyContent(); + + if (pPageStartFrame) { + if (pPageStartFrame->IsTextFrame()) + { + pPageStart = static_cast<const SwTextFrame*>(pPageStartFrame) + ->GetTextNodeFirst(); + } + else + { + pPageStart + = static_cast<const SwNoTextFrame*>(pPageStartFrame)->GetNode(); + } + } + + if (pPageEndFrame) { + if (pPageEndFrame->IsTextFrame()) + { + pPageEnd = static_cast<const SwTextFrame*>(pPageEndFrame) + ->GetTextNodeFirst(); + } + else + { + pPageEnd = static_cast<const SwNoTextFrame*>(pPageEndFrame)->GetNode(); + } + } + } + + if (!pPageStart || !pPageEnd) + { + pPageStart = pReference; + pPageEnd = pReference; + } + + std::deque<SwNode*> pAbovePage; + std::deque<SwNode*> pInPage; + std::deque<SwNode*> 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<SwNode*> pNotBelowElement; + std::deque<SwNode*> 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("<SwGetRefFieldType::FindAnchor(..)> - 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<SwGetRefField*>(pField)->GetSubType(); + if (!bName && subType == REF_STYLE) + { + static_cast<SwGetRefField*>(pField)->UpdateField( + static_txtattr_cast<SwTextField const*>(pHint), pFrame); + } + { OUString const str( bName ? pField->GetFieldName() @@ -251,7 +258,8 @@ SwExpandPortion *SwTextFormatter::NewFieldPortion( SwTextFormatInfo &rInf, static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_BOOKMARKFLD; else if( subType == REF_SETREFATTR ) static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_SETREFATTRFLD; - break; + } + break; case SwFieldIds::DateTime: subType = static_cast<SwDateTimeField*>(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 <fmthdft.hxx> #include <authfld.hxx> #include <dbfld.hxx> +#include <docsh.hxx> #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<const SwChapterField*>(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<SwGetRefField*>(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<sal_uInt16>(SwFieldTypesEnum::GetRef); SwFieldTypesEnum nFieldType = static_cast<SwFieldTypesEnum>(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<sal_uInt16>(SwFieldTypesEnum::GetRef); + nSubType = REF_STYLE; + } else { + SAL_WARN("sw.ui", "<SwFieldRefPage::FillItemSet(..)> 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 <authfld.hxx> #include <flddat.hxx> #include <fldmgr.hxx> +#include <ndtxt.hxx> +#include <cntfrm.hxx> #include <flddropdown.hxx> #include <strings.hrc> #include <tox.hxx> @@ -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<style::XStyleFamiliesSupplier> xStylesSupplier( + GetTextDocument(), uno::UNO_QUERY_THROW); + uno::Reference<container::XNameAccess> xStyleFamilies + = xStylesSupplier->getStyleFamilies(); + uno::Reference<container::XNameAccess> xStyles; + xStyleFamilies->getByName(getPropertyName(PROP_PARAGRAPH_STYLES)) + >>= xStyles; + uno::Reference<css::beans::XPropertySet> 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