summaryrefslogtreecommitdiff
path: root/xmloff/source
diff options
context:
space:
mode:
authorMichael Stahl <michael.stahl@allotropia.de>2022-06-27 19:45:53 +0200
committerMichael Stahl <michael.stahl@allotropia.de>2022-07-08 14:32:03 +0200
commit463178fef5c22f1a04d10e54491852d56e2038b0 (patch)
treec8a813bc28fdb2921013d313f10045ad5053562a /xmloff/source
parenta3e9fede7e998979db7941a527496eb98c822e98 (diff)
xmloff, sw: ODF import/export of fieldmark separator
Previously there were only the fieldmark-start and fieldmark-end elements; now add a fieldmark-separator, so that the field command can be stored as character data (and elements if there is any formatting) instead of attribute value. The import is a bit more complicated because there are already existing documents that are missing the separator; it's not possible to predict if there will be a separator at the time when the start element is seen, so add some hack to toggle inserting the SwXFieldmark with separator at the end (when the document contains a separator, i.e. after the command) or separator at the start (when the document is missing a separator, i.e. after the result). Change-Id: I14d2f50f57d690e75643df5d14fd881ebc759a41 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136513 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
Diffstat (limited to 'xmloff/source')
-rw-r--r--xmloff/source/core/xmltoken.cxx1
-rw-r--r--xmloff/source/text/XMLTextMarkImportContext.cxx46
-rw-r--r--xmloff/source/text/XMLTextMarkImportContext.hxx3
-rw-r--r--xmloff/source/text/txtimp.cxx36
-rw-r--r--xmloff/source/text/txtparae.cxx14
-rw-r--r--xmloff/source/text/txtparai.cxx1
-rw-r--r--xmloff/source/token/tokens.txt1
7 files changed, 87 insertions, 15 deletions
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index a996c4e9fb54..1606dbc68ab3 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -3318,6 +3318,7 @@ namespace xmloff::token {
// enhanced fields
TOKEN( "fieldmark", XML_FIELDMARK ),
TOKEN( "fieldmark-start", XML_FIELDMARK_START ),
+ TOKEN( "fieldmark-separator", XML_FIELDMARK_SEPARATOR ),
TOKEN( "fieldmark-end", XML_FIELDMARK_END ),
TOKEN( "image-scale", XML_IMAGE_SCALE ),
diff --git a/xmloff/source/text/XMLTextMarkImportContext.cxx b/xmloff/source/text/XMLTextMarkImportContext.cxx
index 8e823d6c1757..4e42899e709c 100644
--- a/xmloff/source/text/XMLTextMarkImportContext.cxx
+++ b/xmloff/source/text/XMLTextMarkImportContext.cxx
@@ -35,6 +35,7 @@
#include <com/sun/star/xml/sax/XAttributeList.hpp>
#include <com/sun/star/text/ControlCharacter.hpp>
#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/text/XTextRangeCompare.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/container/XNamed.hpp>
@@ -105,7 +106,7 @@ namespace {
enum lcl_MarkType { TypeReference, TypeReferenceStart, TypeReferenceEnd,
TypeBookmark, TypeBookmarkStart, TypeBookmarkEnd,
- TypeFieldmark, TypeFieldmarkStart, TypeFieldmarkEnd
+ TypeFieldmark, TypeFieldmarkStart, TypeFieldmarkSeparator, TypeFieldmarkEnd
};
}
@@ -120,6 +121,7 @@ SvXMLEnumMapEntry<lcl_MarkType> const lcl_aMarkTypeMap[] =
{ XML_BOOKMARK_END, TypeBookmarkEnd },
{ XML_FIELDMARK, TypeFieldmark },
{ XML_FIELDMARK_START, TypeFieldmarkStart },
+ { XML_FIELDMARK_SEPARATOR, TypeFieldmarkSeparator },
{ XML_FIELDMARK_END, TypeFieldmarkEnd },
{ XML_TOKEN_INVALID, lcl_MarkType(0) },
};
@@ -173,12 +175,12 @@ void XMLTextMarkImportContext::startFastElement( sal_Int32 nElement,
}
static auto InsertFieldmark(SvXMLImport & rImport,
- XMLTextImportHelper & rHelper, OUString const& rName) -> void
+ XMLTextImportHelper & rHelper, bool const isFieldmarkSeparatorMissing) -> void
{
assert(rHelper.hasCurrentFieldCtx()); // was set up in StartElement()
// fdo#86795 check if it's actually a checkbox first
- OUString const type(rHelper.getCurrentFieldType());
+ auto const [ name, type ] = rHelper.getCurrentFieldType();
OUString const fieldmarkTypeName = lcl_getFieldmarkName(type);
if (fieldmarkTypeName == ODF_FORMCHECKBOX ||
fieldmarkTypeName == ODF_FORMDROPDOWN)
@@ -187,9 +189,20 @@ static auto InsertFieldmark(SvXMLImport & rImport,
return;
}
+ uno::Reference<text::XTextRange> const xStartRange(rHelper.getCurrentFieldStart());
+ uno::Reference<text::XTextCursor> const xCursor(
+ rHelper.GetText()->createTextCursorByRange(xStartRange));
+ uno::Reference<text::XTextRangeCompare> const xCompare(rHelper.GetText(), uno::UNO_QUERY);
+ if (xCompare->compareRegionStarts(xStartRange, rHelper.GetCursorAsRange()) < 0)
+ {
+ SAL_WARN("xmloff.text", "invalid field mark positions");
+ assert(false);
+ }
+ xCursor->gotoRange(rHelper.GetCursorAsRange(), true);
+
Reference<XTextContent> const xContent = XMLTextMarkImportContext::CreateAndInsertMark(
- rImport, "com.sun.star.text.Fieldmark",
- rName, rHelper.GetCursorAsRange());
+ rImport, "com.sun.star.text.Fieldmark", name, xCursor,
+ OUString(), isFieldmarkSeparatorMissing);
if (!xContent.is())
return;
@@ -256,7 +269,7 @@ void XMLTextMarkImportContext::endFastElement(sal_Int32 nElement)
if (!SvXMLUnitConverter::convertEnum(nTmp, SvXMLImport::getNameFromToken(nElement), lcl_aMarkTypeMap))
return;
- if (m_sBookmarkName.isEmpty() && TypeFieldmarkEnd != nTmp)
+ if (m_sBookmarkName.isEmpty() && TypeFieldmarkEnd != nTmp && TypeFieldmarkSeparator != nTmp)
return;
switch (nTmp)
@@ -412,13 +425,21 @@ void XMLTextMarkImportContext::endFastElement(sal_Int32 nElement)
// else: no start found -> ignore!
break;
}
- case TypeFieldmarkStart: // no separator, so insert at start
+ case TypeFieldmarkStart:
+ {
+ break;
+ }
+ case TypeFieldmarkSeparator:
{
- InsertFieldmark(GetImport(), m_rHelper, m_sBookmarkName);
+ InsertFieldmark(GetImport(), m_rHelper, false);
break;
}
case TypeFieldmarkEnd:
{
+ if (!m_rHelper.hasCurrentFieldSeparator())
+ { // backward compat for old files without separator
+ InsertFieldmark(GetImport(), m_rHelper, true);
+ }
PopFieldmark(m_rHelper);
break;
}
@@ -446,7 +467,8 @@ Reference<XTextContent> XMLTextMarkImportContext::CreateAndInsertMark(
const OUString& sServiceName,
const OUString& sMarkName,
const Reference<XTextRange> & rRange,
- const OUString& i_rXmlId)
+ const OUString& i_rXmlId,
+ bool const isFieldmarkSeparatorMissing)
{
// create mark
const Reference<XMultiServiceFactory> xFactory(rImport.GetModel(),
@@ -478,6 +500,12 @@ Reference<XTextContent> XMLTextMarkImportContext::CreateAndInsertMark(
}
}
+ if (isFieldmarkSeparatorMissing)
+ {
+ uno::Reference<beans::XPropertySet> const xProps(xIfc, uno::UNO_QUERY_THROW);
+ xProps->setPropertyValue("PrivateSeparatorAtStart", uno::Any(true));
+ }
+
// cast to XTextContent and attach to document
const Reference<XTextContent> xTextContent(xIfc, UNO_QUERY);
if (xTextContent.is())
diff --git a/xmloff/source/text/XMLTextMarkImportContext.hxx b/xmloff/source/text/XMLTextMarkImportContext.hxx
index 407cddbadf7f..da05f524083e 100644
--- a/xmloff/source/text/XMLTextMarkImportContext.hxx
+++ b/xmloff/source/text/XMLTextMarkImportContext.hxx
@@ -96,7 +96,8 @@ public:
const OUString& sServiceName,
const OUString& sMarkName,
const css::uno::Reference<css::text::XTextRange> & rRange,
- const OUString& i_rXmlId = OUString());
+ const OUString& i_rXmlId = OUString(),
+ bool const isFieldmarkSeparatorMissing = false);
bool FindName(
const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList);
diff --git a/xmloff/source/text/txtimp.cxx b/xmloff/source/text/txtimp.cxx
index 079e8df8d82d..f26ce21e66ab 100644
--- a/xmloff/source/text/txtimp.cxx
+++ b/xmloff/source/text/txtimp.cxx
@@ -166,7 +166,7 @@ struct XMLTextImportHelper::Impl
typedef ::std::pair< OUString, OUString> field_name_type_t;
typedef ::std::pair< OUString, OUString > field_param_t;
typedef ::std::vector< field_param_t > field_params_t;
- typedef ::std::tuple<field_name_type_t, field_params_t, uno::Reference<text::XFormField>> field_stack_item_t;
+ typedef ::std::tuple<field_name_type_t, field_params_t, uno::Reference<text::XFormField>, uno::Reference<text::XTextRange>> field_stack_item_t;
typedef ::std::stack< field_stack_item_t > field_stack_t;
field_stack_t m_FieldStack;
@@ -2105,7 +2105,7 @@ bool XMLTextImportHelper::FindAndRemoveBookmarkStartRange(
void XMLTextImportHelper::pushFieldCtx( const OUString& name, const OUString& type )
{
m_xImpl->m_FieldStack.push(Impl::field_stack_item_t(
- Impl::field_name_type_t(name, type), Impl::field_params_t(), uno::Reference<text::XFormField>{}));
+ Impl::field_name_type_t(name, type), Impl::field_params_t(), uno::Reference<text::XFormField>{}, GetCursor()->getStart()));
}
uno::Reference<text::XFormField>
@@ -2133,16 +2133,42 @@ void XMLTextImportHelper::addFieldParam( const OUString& name, const OUString& v
}
}
-OUString XMLTextImportHelper::getCurrentFieldType()
+::std::pair<OUString, OUString> XMLTextImportHelper::getCurrentFieldType() const
{
assert(!m_xImpl->m_FieldStack.empty());
if (!m_xImpl->m_FieldStack.empty())
{
- return std::get<0>(m_xImpl->m_FieldStack.top()).second;
+ return std::get<0>(m_xImpl->m_FieldStack.top());
}
else
{
- return OUString();
+ return {};
+ }
+}
+
+uno::Reference<text::XTextRange> XMLTextImportHelper::getCurrentFieldStart() const
+{
+ assert(!m_xImpl->m_FieldStack.empty());
+ if (!m_xImpl->m_FieldStack.empty())
+ {
+ return std::get<3>(m_xImpl->m_FieldStack.top());
+ }
+ else
+ {
+ return {};
+ }
+}
+
+bool XMLTextImportHelper::hasCurrentFieldSeparator() const
+{
+ assert(!m_xImpl->m_FieldStack.empty());
+ if (!m_xImpl->m_FieldStack.empty())
+ {
+ return std::get<2>(m_xImpl->m_FieldStack.top()).is();
+ }
+ else
+ {
+ return {};
}
}
diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx
index 5d9bd3768d01..63232f8b2822 100644
--- a/xmloff/source/text/txtparae.cxx
+++ b/xmloff/source/text/txtparae.cxx
@@ -181,6 +181,7 @@ constexpr OUStringLiteral gsVisitedCharStyleName(u"VisitedCharStyleName");
constexpr OUStringLiteral gsWidth(u"Width");
constexpr OUStringLiteral gsWidthType( u"WidthType" );
constexpr OUStringLiteral gsTextFieldStart( u"TextFieldStart" );
+constexpr OUStringLiteral gsTextFieldSep(u"TextFieldSeparator");
constexpr OUStringLiteral gsTextFieldEnd( u"TextFieldEnd" );
constexpr OUStringLiteral gsTextFieldStartEnd( u"TextFieldStartEnd" );
@@ -2284,6 +2285,19 @@ void XMLTextParagraphExport::exportTextRangeEnumeration(
}
}
}
+ else if (sType == gsTextFieldSep)
+ {
+ Reference<text::XFormField> const xFormField(xPropSet->getPropertyValue(gsBookmark), UNO_QUERY);
+ if (!bAutoStyles)
+ {
+ if (GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ SvXMLElementExport aElem( GetExport(), !bAutoStyles,
+ XML_NAMESPACE_FIELD, XML_FIELDMARK_SEPARATOR,
+ false, false );
+ }
+ }
+ }
else if (sType == gsTextFieldEnd)
{
if (!bAutoStyles)
diff --git a/xmloff/source/text/txtparai.cxx b/xmloff/source/text/txtparai.cxx
index 3641d7e1af20..5a37d1e46894 100644
--- a/xmloff/source/text/txtparai.cxx
+++ b/xmloff/source/text/txtparai.cxx
@@ -1433,6 +1433,7 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > XMLImpSpanContext_Impl
case XML_ELEMENT(FIELD, XML_FIELDMARK):
case XML_ELEMENT(FIELD, XML_FIELDMARK_START):
+ case XML_ELEMENT(FIELD, XML_FIELDMARK_SEPARATOR):
case XML_ELEMENT(FIELD, XML_FIELDMARK_END):
pContext = new XMLTextMarkImportContext(rImport, *rImport.GetTextImport(),
rHints.GetCrossRefHeadingBookmark());
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index 849802878614..568122e0f582 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -3093,6 +3093,7 @@ use-zero
ignore
fieldmark
fieldmark-start
+fieldmark-separator
fieldmark-end
image-scale
isotropic