summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Kaganski <mike.kaganski@collabora.com>2018-05-18 18:48:38 +0300
committerMike Kaganski <mike.kaganski@collabora.com>2018-05-28 19:49:45 +0200
commit08071d4de2935b64a07cb4e9bdf9fa88e850c86b (patch)
treeb9f670a68d7ae3668b3c5d1da6af1aa89855bd18
parentca8f4e2961fd1a49af92e348da618827189ac7c0 (diff)
tdf#35798: Hide empty Database fields' paragraphs (+ compat option)
With this change, Database fields that expand to empty values behave as if they are "Hidden Paragraph" fields. A compatibility option to enable this behaviour is added. The option is enabled by default, and for any non-native documents (for compatibility with other office suites). For existing (F)ODT documents, the option is disabled for those documents that don't have this setting set, to keep the layout of legacy documents. Change-Id: Ic5e8cb15a3a7d1a765a984eef4b0d97666df7dfd Reviewed-on: https://gerrit.libreoffice.org/54552 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/54929 Tested-by: Mike Kaganski <mike.kaganski@collabora.com>
-rw-r--r--filter/source/xmlfilteradaptor/XmlFilterAdaptor.cxx13
-rw-r--r--include/unotools/compatibility.hxx1
-rw-r--r--officecfg/registry/schema/org/openoffice/Office/Compatibility.xcs6
-rw-r--r--sw/CppunitTest_sw_mailmerge.mk4
-rw-r--r--sw/inc/IDocumentSettingAccess.hxx1
-rw-r--r--sw/inc/doc.hxx3
-rw-r--r--sw/inc/ndhints.hxx26
-rw-r--r--sw/inc/ndtxt.hxx12
-rw-r--r--sw/inc/viewsh.hxx2
-rw-r--r--sw/qa/extras/mailmerge/data/5-with-blanks.odsbin0 -> 9193 bytes
-rw-r--r--sw/qa/extras/mailmerge/data/tdf35798-legacy.fodt37
-rw-r--r--sw/qa/extras/mailmerge/data/tdf35798-legacy.odtbin0 -> 9557 bytes
-rw-r--r--sw/qa/extras/mailmerge/data/tdf35798-new.fodt38
-rw-r--r--sw/qa/extras/mailmerge/data/tdf35798-new.odtbin0 -> 9556 bytes
-rw-r--r--sw/qa/extras/mailmerge/mailmerge.cxx141
-rw-r--r--sw/source/core/doc/DocumentSettingManager.cxx8
-rw-r--r--sw/source/core/doc/doc.cxx128
-rw-r--r--sw/source/core/doc/doctxm.cxx4
-rw-r--r--sw/source/core/inc/DocumentSettingManager.hxx1
-rw-r--r--sw/source/core/text/txtfrm.cxx9
-rw-r--r--sw/source/core/txtnode/atrfld.cxx26
-rw-r--r--sw/source/core/txtnode/ndtxt.cxx2
-rw-r--r--sw/source/core/txtnode/thints.cxx65
-rw-r--r--sw/source/core/view/viewsh.cxx20
-rw-r--r--sw/source/filter/xml/xmlimp.cxx26
-rw-r--r--sw/source/ui/config/optcomp.cxx16
-rw-r--r--sw/source/uibase/app/docshini.cxx22
-rw-r--r--sw/source/uibase/uno/SwXDocumentSettings.cxx18
-rw-r--r--sw/uiconfig/swriter/ui/optcompatpage.ui1
-rw-r--r--unotools/source/config/compatibility.cxx4
30 files changed, 513 insertions, 121 deletions
diff --git a/filter/source/xmlfilteradaptor/XmlFilterAdaptor.cxx b/filter/source/xmlfilteradaptor/XmlFilterAdaptor.cxx
index 892cb3989593..3cc2cdb9842d 100644
--- a/filter/source/xmlfilteradaptor/XmlFilterAdaptor.cxx
+++ b/filter/source/xmlfilteradaptor/XmlFilterAdaptor.cxx
@@ -87,12 +87,25 @@ bool SAL_CALL XmlFilterAdaptor::importImpl( const Sequence< css::beans::Property
PropertyMapEntry aImportInfoMap[] =
{
{ OUString("BaseURI"), 0, ::cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},
+ { OUString("DefaultDocumentSettings"), 0,
+ ::cppu::UnoType<Sequence<PropertyValue>>::get(), PropertyAttribute::MAYBEVOID, 0 },
{ OUString(), 0, css::uno::Type(), 0, 0 }
};
Reference< XPropertySet > xInfoSet(
GenericPropertySet_CreateInstance( new PropertySetInfo( aImportInfoMap ) ) );
xInfoSet->setPropertyValue( "BaseURI", makeAny( aBaseURI ));
+
+ OUString aFilterName;
+ auto It = aMediaMap.find(OUString("FilterName"));
+ if (It != aMediaMap.end() && (It->second >>= aFilterName)
+ && aFilterName == "OpenDocument Text Flat XML")
+ {
+ PropertyValue EmptyDbFieldHidesPara("EmptyDbFieldHidesPara", 0, Any(false),
+ PropertyState::PropertyState_DIRECT_VALUE);
+ Sequence<PropertyValue> aSettings{ EmptyDbFieldHidesPara };
+ xInfoSet->setPropertyValue("DefaultDocumentSettings", makeAny(aSettings));
+ }
aAnys[0] <<= xInfoSet;
diff --git a/include/unotools/compatibility.hxx b/include/unotools/compatibility.hxx
index f281f58aad1d..ac8add44e025 100644
--- a/include/unotools/compatibility.hxx
+++ b/include/unotools/compatibility.hxx
@@ -60,6 +60,7 @@ class UNOTOOLS_DLLPUBLIC SvtCompatibilityEntry
ProtectForm,
MsWordTrailingBlanks,
SubtractFlysAnchoredAtFlys,
+ EmptyDbFieldHidesPara,
/* Should be at the end. Do not remove it. */
INVALID
diff --git a/officecfg/registry/schema/org/openoffice/Office/Compatibility.xcs b/officecfg/registry/schema/org/openoffice/Office/Compatibility.xcs
index f71c66205871..c12568de94dd 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Compatibility.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Compatibility.xcs
@@ -122,6 +122,12 @@
</info>
<value>false</value>
</prop>
+ <prop oor:name="EmptyDbFieldHidesPara" oor:type="xs:boolean" oor:nillable="false">
+ <info>
+ <desc>A database field (e.g., MailMerge) with empty value hides its paragraph</desc>
+ </info>
+ <value>true</value>
+ </prop>
</group>
</templates>
<component>
diff --git a/sw/CppunitTest_sw_mailmerge.mk b/sw/CppunitTest_sw_mailmerge.mk
index 336b235a7df1..480d2a8a6981 100644
--- a/sw/CppunitTest_sw_mailmerge.mk
+++ b/sw/CppunitTest_sw_mailmerge.mk
@@ -45,7 +45,10 @@ $(eval $(call gb_CppunitTest_use_components,sw_mailmerge, \
dbaccess/util/dba \
embeddedobj/util/embobj \
filter/source/config/cache/filterconfig1 \
+ filter/source/odfflatxml/odfflatxml \
filter/source/storagefilterdetect/storagefd \
+ filter/source/xmlfilteradaptor/xmlfa \
+ filter/source/xmlfilterdetect/xmlfd \
forms/util/frm \
framework/util/fwk \
i18npool/util/i18npool \
@@ -78,6 +81,7 @@ $(eval $(call gb_CppunitTest_use_components,sw_mailmerge, \
) \
) \
xmloff/util/xo \
+ xmlscript/util/xmlscript \
))
$(eval $(call gb_CppunitTest_use_configuration,sw_mailmerge))
diff --git a/sw/inc/IDocumentSettingAccess.hxx b/sw/inc/IDocumentSettingAccess.hxx
index 41b2c4ff79c3..71ba245ce3a1 100644
--- a/sw/inc/IDocumentSettingAccess.hxx
+++ b/sw/inc/IDocumentSettingAccess.hxx
@@ -83,6 +83,7 @@ enum class DocumentSettingId
SUBTRACT_FLYS,
// tdf#112443 disable off-page content positioning
DISABLE_OFF_PAGE_POSITIONING,
+ EMPTY_DB_FIELD_HIDES_PARA,
// COMPATIBILITY FLAGS END
BROWSE_MODE,
HTML_MODE,
diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx
index 2506252585c7..b58286c80536 100644
--- a/sw/inc/doc.hxx
+++ b/sw/inc/doc.hxx
@@ -511,6 +511,9 @@ public:
::sw::DocumentFieldsManager & GetDocumentFieldsManager();
+ bool FieldCanHidePara(SwFieldIds eFieldId) const;
+ bool FieldHidesPara(const SwField& rField) const;
+
// IDocumentContentOperations
IDocumentContentOperations const & getIDocumentContentOperations() const;
IDocumentContentOperations & getIDocumentContentOperations();
diff --git a/sw/inc/ndhints.hxx b/sw/inc/ndhints.hxx
index 77aebc67d2be..0d66f9c3ead2 100644
--- a/sw/inc/ndhints.hxx
+++ b/sw/inc/ndhints.hxx
@@ -77,6 +77,8 @@ class SwpHtEnd : public o3tl::sorted_vector<SwTextAttr*, CompareSwpHtEnd,
class SwpHints
{
private:
+ const SwTextNode& m_rParent;
+
// SAL_MAX_SIZE is used by GetStartOf to return
// failure, so just allow SAL_MAX_SIZE-1 hints
static const size_t MAX_HINTS = SAL_MAX_SIZE-1;
@@ -88,9 +90,11 @@ private:
/// true: the Node is in Split and Frames are moved
bool m_bInSplitNode : 1;
- /// m_bHasHiddenParaField is invalid, call CalcHiddenParaField()
- bool m_bCalcHiddenParaField : 1;
- bool m_bHasHiddenParaField : 1; ///< HiddenParaField
+ // m_bHiddenByParaField is invalid, call CalcHiddenParaField()
+ mutable bool m_bCalcHiddenParaField : 1;
+ // if all fields controlling visibility of the paragraph require to hide it
+ // (if there's no such fields, or if any field requires to show, then this is false)
+ mutable bool m_bHiddenByParaField : 1;
bool m_bFootnote : 1; ///< footnotes
bool m_bDDEFields : 1; ///< the TextNode has DDE fields
@@ -108,15 +112,15 @@ private:
void Delete( SwTextAttr const * pTextHt );
void SetInSplitNode(bool bInSplit) { m_bInSplitNode = bInSplit; }
- void SetCalcHiddenParaField() { m_bCalcHiddenParaField = true; }
- void SetHiddenParaField( const bool bNew ) { m_bHasHiddenParaField = bNew; }
- bool HasHiddenParaField() const
+ void SetCalcHiddenParaField() const { m_bCalcHiddenParaField = true; }
+ void SetHiddenByParaField( const bool bNew ) const { m_bHiddenByParaField = bNew; }
+ bool IsHiddenByParaField() const
{
if ( m_bCalcHiddenParaField )
{
- const_cast<SwpHints*>(this)->CalcHiddenParaField();
+ CalcHiddenParaField();
}
- return m_bHasHiddenParaField;
+ return m_bHiddenByParaField;
}
void InsertNesting(SwTextAttrNesting & rNewHint);
@@ -144,7 +148,7 @@ private:
#endif
public:
- SwpHints();
+ SwpHints(const SwTextNode& rParent);
size_t Count() const { return m_HintsByStart.size(); }
bool Contains( const SwTextAttr *pHt ) const;
@@ -179,8 +183,8 @@ public:
bool HasFootnote() const { return m_bFootnote; }
bool IsInSplitNode() const { return m_bInSplitNode; }
- /// calc current value of m_bHasHiddenParaField, returns true iff changed
- bool CalcHiddenParaField();
+ // calc current value of m_bHiddenByParaField, returns true iff changed
+ bool CalcHiddenParaField() const; // changes mutable state
DECL_FIXEDMEMPOOL_NEWDEL(SwpHints)
};
diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx
index 1992227207b5..5d43ebf4898a 100644
--- a/sw/inc/ndtxt.hxx
+++ b/sw/inc/ndtxt.hxx
@@ -21,6 +21,7 @@
#include <cppuhelper/weakref.hxx>
+#include "doc.hxx"
#include "swdllapi.h"
#include "node.hxx"
#include "hintids.hxx"
@@ -700,8 +701,13 @@ public:
{ if (m_pSwpHints) m_pSwpHints->SetCalcHiddenParaField(); }
/// is the paragraph visible?
- bool HasHiddenParaField() const
- { return m_pSwpHints && m_pSwpHints->HasHiddenParaField(); }
+ bool IsHiddenByParaField() const
+ { return m_pSwpHints && m_pSwpHints->IsHiddenByParaField(); }
+
+ bool FieldCanHidePara(SwFieldIds eFieldId) const
+ { return GetDoc()->FieldCanHidePara(eFieldId); }
+ bool FieldHidesPara(const SwField& rField) const
+ { return GetDoc()->FieldHidesPara(rField); }
/// Hidden Paragraph Field:
@@ -810,7 +816,7 @@ inline SwpHints& SwTextNode::GetOrCreateSwpHints()
{
if ( !m_pSwpHints )
{
- m_pSwpHints = new SwpHints;
+ m_pSwpHints = new SwpHints(*this);
}
return *m_pSwpHints;
}
diff --git a/sw/inc/viewsh.hxx b/sw/inc/viewsh.hxx
index 566653342451..e492465691f3 100644
--- a/sw/inc/viewsh.hxx
+++ b/sw/inc/viewsh.hxx
@@ -420,6 +420,8 @@ public:
void SetSubtractFlysAnchoredAtFlys(bool bSubtractFlysAnchoredAtFlys);
+ void SetEmptyDbFieldHidesPara(bool bEmptyDbFieldHidesPara);
+
// DOCUMENT COMPATIBILITY FLAGS END
// Calls Idle-formatter of Layout.
diff --git a/sw/qa/extras/mailmerge/data/5-with-blanks.ods b/sw/qa/extras/mailmerge/data/5-with-blanks.ods
new file mode 100644
index 000000000000..722f74830688
--- /dev/null
+++ b/sw/qa/extras/mailmerge/data/5-with-blanks.ods
Binary files differ
diff --git a/sw/qa/extras/mailmerge/data/tdf35798-legacy.fodt b/sw/qa/extras/mailmerge/data/tdf35798-legacy.fodt
new file mode 100644
index 000000000000..c6dd8661e869
--- /dev/null
+++ b/sw/qa/extras/mailmerge/data/tdf35798-legacy.fodt
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:meta><meta:creation-date>2014-10-22T13:27:38.673154279</meta:creation-date><dc:date>2018-05-19T13:45:25.911000000</dc:date><meta:editing-duration>PT6M54S</meta:editing-duration><meta:editing-cycles>4</meta:editing-cycles><meta:generator>LibreOfficeDev/6.1.0.0.alpha1$Windows_X86_64 LibreOffice_project/dd1ab570d5791145c10a4e8f28b048ec8f70edb0</meta:generator><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="8" meta:word-count="37" meta:character-count="228"/></office:meta>
+ <office:settings>
+ <config:config-item-set config:name="ooo:configuration-settings">
+ <config:config-item config:name="CurrentDatabaseDataSource" config:type="string">5-with-blanks</config:config-item>
+ <config:config-item config:name="CurrentDatabaseCommand" config:type="string">names</config:config-item>
+ <config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
+ </config:config-item-set>
+ </office:settings>
+ <office:scripts>
+ <office:script script:language="ooo:Basic">
+ <ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
+ </office:script>
+ </office:scripts>
+ <office:automatic-styles>
+ <style:page-layout style:name="pm1">
+ <style:page-layout-properties fo:page-width="21cm" fo:page-height="29.7cm" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm"/>
+ </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+ <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+ <office:text>
+ <text:p>Heading</text:p>
+ <text:p>Title: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Title" text:database-name="5-with-blanks">&lt;Title&gt;</text:database-display></text:p>
+ <text:p>First Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="First Name" text:database-name="5-with-blanks">&lt;First Name&gt;</text:database-display></text:p>
+ <text:p>Last Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Last Name" text:database-name="5-with-blanks">&lt;Last Name&gt;</text:database-display></text:p>
+ <text:p>Title: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Title" text:database-name="5-with-blanks">&lt;Title&gt;</text:database-display><text:s/>First Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="First Name" text:database-name="5-with-blanks">&lt;First Name&gt;</text:database-display></text:p>
+ <text:p>First Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="First Name" text:database-name="5-with-blanks">&lt;First Name&gt;</text:database-display><text:s/>Last Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Last Name" text:database-name="5-with-blanks">&lt;Last Name&gt;</text:database-display></text:p>
+ <text:p>Title: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Title" text:database-name="5-with-blanks">&lt;Title&gt;</text:database-display><text:s/>First Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="First Name" text:database-name="5-with-blanks">&lt;First Name&gt;</text:database-display><text:s/>Last Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Last Name" text:database-name="5-with-blanks">&lt;Last Name&gt;</text:database-display></text:p>
+ <text:p>Trailing text</text:p>
+ </office:text>
+ </office:body>
+</office:document> \ No newline at end of file
diff --git a/sw/qa/extras/mailmerge/data/tdf35798-legacy.odt b/sw/qa/extras/mailmerge/data/tdf35798-legacy.odt
new file mode 100644
index 000000000000..fe37e5e962fd
--- /dev/null
+++ b/sw/qa/extras/mailmerge/data/tdf35798-legacy.odt
Binary files differ
diff --git a/sw/qa/extras/mailmerge/data/tdf35798-new.fodt b/sw/qa/extras/mailmerge/data/tdf35798-new.fodt
new file mode 100644
index 000000000000..8de149cab75a
--- /dev/null
+++ b/sw/qa/extras/mailmerge/data/tdf35798-new.fodt
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:meta><meta:creation-date>2014-10-22T13:27:38.673154279</meta:creation-date><dc:date>2018-05-19T13:45:25.911000000</dc:date><meta:editing-duration>PT6M54S</meta:editing-duration><meta:editing-cycles>4</meta:editing-cycles><meta:generator>LibreOfficeDev/6.1.0.0.alpha1$Windows_X86_64 LibreOffice_project/dd1ab570d5791145c10a4e8f28b048ec8f70edb0</meta:generator><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="8" meta:word-count="37" meta:character-count="228"/></office:meta>
+ <office:settings>
+ <config:config-item-set config:name="ooo:configuration-settings">
+ <config:config-item config:name="CurrentDatabaseDataSource" config:type="string">5-with-blanks</config:config-item>
+ <config:config-item config:name="CurrentDatabaseCommand" config:type="string">names</config:config-item>
+ <config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
+ <config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">true</config:config-item>
+ </config:config-item-set>
+ </office:settings>
+ <office:scripts>
+ <office:script script:language="ooo:Basic">
+ <ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
+ </office:script>
+ </office:scripts>
+ <office:automatic-styles>
+ <style:page-layout style:name="pm1">
+ <style:page-layout-properties fo:page-width="21cm" fo:page-height="29.7cm" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm"/>
+ </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+ <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+ <office:text>
+ <text:p>Heading</text:p>
+ <text:p>Title: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Title" text:database-name="5-with-blanks">&lt;Title&gt;</text:database-display></text:p>
+ <text:p>First Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="First Name" text:database-name="5-with-blanks">&lt;First Name&gt;</text:database-display></text:p>
+ <text:p>Last Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Last Name" text:database-name="5-with-blanks">&lt;Last Name&gt;</text:database-display></text:p>
+ <text:p>Title: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Title" text:database-name="5-with-blanks">&lt;Title&gt;</text:database-display><text:s/>First Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="First Name" text:database-name="5-with-blanks">&lt;First Name&gt;</text:database-display></text:p>
+ <text:p>First Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="First Name" text:database-name="5-with-blanks">&lt;First Name&gt;</text:database-display><text:s/>Last Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Last Name" text:database-name="5-with-blanks">&lt;Last Name&gt;</text:database-display></text:p>
+ <text:p>Title: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Title" text:database-name="5-with-blanks">&lt;Title&gt;</text:database-display><text:s/>First Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="First Name" text:database-name="5-with-blanks">&lt;First Name&gt;</text:database-display><text:s/>Last Name: <text:database-display text:table-name="names" text:table-type="table" text:column-name="Last Name" text:database-name="5-with-blanks">&lt;Last Name&gt;</text:database-display></text:p>
+ <text:p>Trailing text</text:p>
+ </office:text>
+ </office:body>
+</office:document> \ No newline at end of file
diff --git a/sw/qa/extras/mailmerge/data/tdf35798-new.odt b/sw/qa/extras/mailmerge/data/tdf35798-new.odt
new file mode 100644
index 000000000000..84323b96e658
--- /dev/null
+++ b/sw/qa/extras/mailmerge/data/tdf35798-new.odt
Binary files differ
diff --git a/sw/qa/extras/mailmerge/mailmerge.cxx b/sw/qa/extras/mailmerge/mailmerge.cxx
index 0a0f40bb4cf3..e21f0fe44d0c 100644
--- a/sw/qa/extras/mailmerge/mailmerge.cxx
+++ b/sw/qa/extras/mailmerge/mailmerge.cxx
@@ -677,5 +677,146 @@ DECLARE_FILE_MAILMERGE_TEST(testTdf102010, "empty.odt", "10-testing-addresses.od
loadMailMergeDocument( 1 );
}
+namespace
+{
+constexpr char const* const EmptyValuesLegacyData[][8]
+ = { { "Heading", "Title: ", "First Name: firstname1", "Last Name: lastname1",
+ "Title: First Name: firstname1", "First Name: firstname1 Last Name: lastname1",
+ "Title: First Name: firstname1 Last Name: lastname1", "Trailing text" },
+ { "Heading", "Title: title2", "First Name: ", "Last Name: lastname2",
+ "Title: title2 First Name: ", "First Name: Last Name: lastname2",
+ "Title: title2 First Name: Last Name: lastname2", "Trailing text" },
+ { "Heading", "Title: title3", "First Name: firstname3", "Last Name: ",
+ "Title: title3 First Name: firstname3", "First Name: firstname3 Last Name: ",
+ "Title: title3 First Name: firstname3 Last Name: ", "Trailing text" },
+ { "Heading", "Title: ", "First Name: ", "Last Name: lastname4",
+ "Title: First Name: ", "First Name: Last Name: lastname4",
+ "Title: First Name: Last Name: lastname4", "Trailing text" },
+ { "Heading", "Title: title5", "First Name: ", "Last Name: ", "Title: title5 First Name: ",
+ "First Name: Last Name: ", "Title: title5 First Name: Last Name: ", "Trailing text" } };
+constexpr char const* const EmptyValuesNewData[][8]
+ = { { "Heading", "First Name: firstname1", "Last Name: lastname1",
+ "Title: First Name: firstname1", "First Name: firstname1 Last Name: lastname1",
+ "Title: First Name: firstname1 Last Name: lastname1", "Trailing text" },
+ { "Heading", "Title: title2", "Last Name: lastname2",
+ "Title: title2 First Name: ", "First Name: Last Name: lastname2",
+ "Title: title2 First Name: Last Name: lastname2", "Trailing text" },
+ { "Heading", "Title: title3", "First Name: firstname3",
+ "Title: title3 First Name: firstname3", "First Name: firstname3 Last Name: ",
+ "Title: title3 First Name: firstname3 Last Name: ", "Trailing text" },
+ { "Heading", "Last Name: lastname4", "First Name: Last Name: lastname4",
+ "Title: First Name: Last Name: lastname4", "Trailing text" },
+ { "Heading", "Title: title5", "Title: title5 First Name: ",
+ "Title: title5 First Name: Last Name: ", "Trailing text" } };
+}
+
+// The following four tests (testEmptyValuesLegacyODT, testEmptyValuesNewODT, (testEmptyValuesLegacyFODT, testEmptyValuesNewFODT)
+// check that for native documents without "EmptyDbFieldHidesPara" compatibility option, all paragraphs are exported visible,
+// while for documents with the option enabled, the paragraphs with all Database fields having empty values are hidden.
+
+DECLARE_FILE_MAILMERGE_TEST(testEmptyValuesLegacyODT, "tdf35798-legacy.odt", "5-with-blanks.ods",
+ "names")
+{
+ executeMailMerge();
+ for (int doc = 0; doc < 5; ++doc)
+ {
+ loadMailMergeDocument(doc);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ pDoc->RemoveInvisibleContent();
+ CPPUNIT_ASSERT_EQUAL(1, getPages());
+ for (int i = 0; i < 8; ++i)
+ {
+ auto xPara = getParagraph(i+1);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ OString("in doc " + OString::number(doc) + " paragraph " + OString::number(i + 1))
+ .getStr(),
+ OUString::createFromAscii(EmptyValuesLegacyData[doc][i]), xPara->getString());
+ }
+ CPPUNIT_ASSERT_EQUAL(8, getParagraphs());
+ }
+}
+
+DECLARE_FILE_MAILMERGE_TEST(testEmptyValuesNewODT, "tdf35798-new.odt", "5-with-blanks.ods",
+ "names")
+{
+ executeMailMerge();
+ for (int doc = 0; doc < 5; ++doc)
+ {
+ loadMailMergeDocument(doc);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ pDoc->RemoveInvisibleContent();
+ CPPUNIT_ASSERT_EQUAL(1, getPages());
+ int i;
+ for (i = 0; i < 8; ++i)
+ {
+ const char* pExpected = EmptyValuesNewData[doc][i];
+ if (!pExpected)
+ break;
+ auto xPara = getParagraph(i + 1);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ OString("in doc " + OString::number(doc) + " paragraph " + OString::number(i + 1))
+ .getStr(),
+ OUString::createFromAscii(pExpected), xPara->getString());
+ }
+ CPPUNIT_ASSERT_EQUAL(i, getParagraphs());
+ }
+}
+
+DECLARE_FILE_MAILMERGE_TEST(testEmptyValuesLegacyFODT, "tdf35798-legacy.fodt", "5-with-blanks.ods",
+ "names")
+{
+ executeMailMerge();
+ for (int doc = 0; doc < 5; ++doc)
+ {
+ loadMailMergeDocument(doc);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ pDoc->RemoveInvisibleContent();
+ CPPUNIT_ASSERT_EQUAL(1, getPages());
+ for (int i = 0; i < 8; ++i)
+ {
+ auto xPara = getParagraph(i + 1);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ OString("in doc " + OString::number(doc) + " paragraph " + OString::number(i + 1))
+ .getStr(),
+ OUString::createFromAscii(EmptyValuesLegacyData[doc][i]), xPara->getString());
+ }
+ CPPUNIT_ASSERT_EQUAL(8, getParagraphs());
+ }
+}
+
+DECLARE_FILE_MAILMERGE_TEST(testEmptyValuesNewFODT, "tdf35798-new.fodt", "5-with-blanks.ods",
+ "names")
+{
+ executeMailMerge();
+ for (int doc = 0; doc < 5; ++doc)
+ {
+ loadMailMergeDocument(doc);
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ pDoc->RemoveInvisibleContent();
+ CPPUNIT_ASSERT_EQUAL(1, getPages());
+ int i;
+ for (i = 0; i < 8; ++i)
+ {
+ const char* pExpected = EmptyValuesNewData[doc][i];
+ if (!pExpected)
+ break;
+ auto xPara = getParagraph(i + 1);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ OString("in doc " + OString::number(doc) + " paragraph " + OString::number(i + 1))
+ .getStr(),
+ OUString::createFromAscii(pExpected), xPara->getString());
+ }
+ CPPUNIT_ASSERT_EQUAL(i, getParagraphs());
+ }
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentSettingManager.cxx b/sw/source/core/doc/DocumentSettingManager.cxx
index 77de92017537..c900a985dee2 100644
--- a/sw/source/core/doc/DocumentSettingManager.cxx
+++ b/sw/source/core/doc/DocumentSettingManager.cxx
@@ -114,6 +114,8 @@ sw::DocumentSettingManager::DocumentSettingManager(SwDoc &rDoc)
mbProtectForm = aOptions.GetDefault( SvtCompatibilityEntry::Index::ProtectForm );
mbMsWordCompTrailingBlanks = aOptions.GetDefault( SvtCompatibilityEntry::Index::MsWordTrailingBlanks );
mbSubtractFlys = aOptions.GetDefault( SvtCompatibilityEntry::Index::SubtractFlysAnchoredAtFlys );
+ mbEmptyDbFieldHidesPara
+ = aOptions.GetDefault(SvtCompatibilityEntry::Index::EmptyDbFieldHidesPara);
}
else
{
@@ -131,6 +133,7 @@ sw::DocumentSettingManager::DocumentSettingManager(SwDoc &rDoc)
mbProtectForm = false;
mbMsWordCompTrailingBlanks = false;
mbSubtractFlys = false;
+ mbEmptyDbFieldHidesPara = true;
}
// COMPATIBILITY FLAGS END
@@ -204,6 +207,7 @@ bool sw::DocumentSettingManager::get(/*[in]*/ DocumentSettingId id) const
case DocumentSettingId::EMBED_SYSTEM_FONTS: return mEmbedSystemFonts;
case DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING: return mApplyParagraphMarkFormatToNumbering;
case DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING: return mbDisableOffPagePositioning;
+ case DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA: return mbEmptyDbFieldHidesPara;
default:
OSL_FAIL("Invalid setting id");
}
@@ -421,6 +425,9 @@ void sw::DocumentSettingManager::set(/*[in]*/ DocumentSettingId id, /*[in]*/ boo
case DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING:
mbDisableOffPagePositioning = value;
break;
+ case DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA:
+ mbEmptyDbFieldHidesPara = value;
+ break;
default:
OSL_FAIL("Invalid setting id");
}
@@ -562,6 +569,7 @@ void sw::DocumentSettingManager::ReplaceCompatibilityOptions(const DocumentSetti
mbTabRelativeToIndent = rSource.mbTabRelativeToIndent;
mbTabAtLeftIndentForParagraphsInList = rSource.mbTabAtLeftIndentForParagraphsInList;
mbMsWordCompTrailingBlanks = rSource.mbMsWordCompTrailingBlanks;
+ mbEmptyDbFieldHidesPara = rSource.mbEmptyDbFieldHidesPara;
}
sal_uInt32 sw::DocumentSettingManager::Getn32DummyCompatibilityOptions1() const
diff --git a/sw/source/core/doc/doc.cxx b/sw/source/core/doc/doc.cxx
index e9cdbc8b1174..d5b299373abd 100644
--- a/sw/source/core/doc/doc.cxx
+++ b/sw/source/core/doc/doc.cxx
@@ -1317,6 +1317,73 @@ void SwDoc::Summary( SwDoc* pExtDoc, sal_uInt8 nLevel, sal_uInt8 nPara, bool bIm
}
}
+namespace
+{
+void RemoveOrDeleteContents(SwTextNode* pTextNd, IDocumentContentOperations& xOperations)
+{
+ SwPaM aPam(*pTextNd, 0, *pTextNd, pTextNd->GetText().getLength());
+
+ // Remove hidden paragraph or delete contents:
+ // Delete contents if
+ // 1. removing the paragraph would result in an empty section or
+ // 2. if the paragraph is the last paragraph in the section and
+ // there is no paragraph in front of the paragraph:
+ if ((2 == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex())
+ || (1 == pTextNd->EndOfSectionIndex() - pTextNd->GetIndex()
+ && !pTextNd->GetNodes()[pTextNd->GetIndex() - 1]->GetTextNode()))
+ {
+ xOperations.DeleteRange(aPam);
+ }
+ else
+ {
+ aPam.DeleteMark();
+ xOperations.DelFullPara(aPam);
+ }
+}
+// Returns if the data was actually modified
+bool HandleHidingField(SwFormatField& rFormatField, const SwNodes& rNodes,
+ IDocumentContentOperations& xOperations)
+{
+ SwTextNode* pTextNd;
+ if (rFormatField.GetTextField()
+ && nullptr != (pTextNd = rFormatField.GetTextField()->GetpTextNode())
+ && pTextNd->GetpSwpHints() && pTextNd->IsHiddenByParaField()
+ && &pTextNd->GetNodes() == &rNodes)
+ {
+ RemoveOrDeleteContents(pTextNd, xOperations);
+ return true;
+ }
+ return false;
+}
+}
+
+bool SwDoc::FieldCanHidePara(SwFieldIds eFieldId) const
+{
+ switch (eFieldId)
+ {
+ case SwFieldIds::HiddenPara:
+ return true;
+ case SwFieldIds::Database:
+ return GetDocumentSettingManager().get(
+ DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA);
+ default:
+ return false;
+ }
+}
+
+bool SwDoc::FieldHidesPara(const SwField& rField) const
+{
+ switch (rField.GetTyp()->Which())
+ {
+ case SwFieldIds::HiddenPara:
+ return static_cast<const SwHiddenParaField&>(rField).IsHidden();
+ case SwFieldIds::Database:
+ return FieldCanHidePara(SwFieldIds::Database) && rField.ExpandField(true).isEmpty();
+ default:
+ return false;
+ }
+}
+
/// Remove the invisible content from the document e.g. hidden areas, hidden paragraphs
bool SwDoc::RemoveInvisibleContent()
{
@@ -1324,35 +1391,22 @@ bool SwDoc::RemoveInvisibleContent()
GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_DELETE_INVISIBLECNTNT, nullptr );
{
- SwTextNode* pTextNd;
- SwIterator<SwFormatField,SwFieldType> aIter( *getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenPara ) );
- for( SwFormatField* pFormatField = aIter.First(); pFormatField; pFormatField = aIter.Next() )
+ // Removing some nodes for one SwFieldIds::Database type might remove the type from
+ // document's field types, invalidating iterators. So, we need to create own list of
+ // matching types prior to processing them.
+ std::vector<const SwFieldType*> aHidingFieldTypes;
+ for (const auto* pType : *getIDocumentFieldsAccess().GetFieldTypes())
{
- if( pFormatField->GetTextField() &&
- nullptr != ( pTextNd = pFormatField->GetTextField()->GetpTextNode() ) &&
- pTextNd->GetpSwpHints() && pTextNd->HasHiddenParaField() &&
- &pTextNd->GetNodes() == &GetNodes() )
- {
- bRet = true;
- SwPaM aPam(*pTextNd, 0, *pTextNd, pTextNd->GetText().getLength());
-
- // Remove hidden paragraph or delete contents:
- // Delete contents if
- // 1. removing the paragraph would result in an empty section or
- // 2. if the paragraph is the last paragraph in the section and
- // there is no paragraph in front of the paragraph:
- if ( ( 2 == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex() ) ||
- ( 1 == pTextNd->EndOfSectionIndex() - pTextNd->GetIndex() &&
- !GetNodes()[ pTextNd->GetIndex() - 1 ]->GetTextNode() ) )
- {
- getIDocumentContentOperations().DeleteRange( aPam );
- }
- else
- {
- aPam.DeleteMark();
- getIDocumentContentOperations().DelFullPara( aPam );
- }
- }
+ if (FieldCanHidePara(pType->Which()))
+ aHidingFieldTypes.push_back(pType);
+ }
+ for (const auto* pType : aHidingFieldTypes)
+ {
+ SwIterator<SwFormatField, SwFieldType> aIter(*pType);
+ for (SwFormatField* pFormatField = aIter.First(); pFormatField;
+ pFormatField = aIter.Next())
+ bRet |= HandleHidingField(*pFormatField, GetNodes(),
+ getIDocumentContentOperations());
}
}
@@ -1368,23 +1422,7 @@ bool SwDoc::RemoveInvisibleContent()
{
bRemoved = true;
bRet = true;
-
- // Remove hidden paragraph or delete contents:
- // Delete contents if
- // 1. removing the paragraph would result in an empty section or
- // 2. if the paragraph is the last paragraph in the section and
- // there is no paragraph in front of the paragraph:
- if ( ( 2 == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex() ) ||
- ( 1 == pTextNd->EndOfSectionIndex() - pTextNd->GetIndex() &&
- !GetNodes()[ pTextNd->GetIndex() - 1 ]->GetTextNode() ) )
- {
- getIDocumentContentOperations().DeleteRange( aPam );
- }
- else
- {
- aPam.DeleteMark();
- getIDocumentContentOperations().DelFullPara( aPam );
- }
+ RemoveOrDeleteContents(pTextNd, getIDocumentContentOperations());
}
else if ( pTextNd->HasHiddenCharAttribute( false ) )
{
diff --git a/sw/source/core/doc/doctxm.cxx b/sw/source/core/doc/doctxm.cxx
index 71b961e1522b..7e635d76a04c 100644
--- a/sw/source/core/doc/doctxm.cxx
+++ b/sw/source/core/doc/doctxm.cxx
@@ -1153,7 +1153,7 @@ void SwTOXBaseSection::UpdateMarks( const SwTOXInternational& rIntl,
pTOXSrc->GetText().getLength() && pTOXSrc->HasWriterListeners() &&
pTOXSrc->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() ) &&
(!IsFromChapter() || ::lcl_FindChapterNode( *pTOXSrc ) == pOwnChapterNode ) &&
- !pTOXSrc->HasHiddenParaField() &&
+ !pTOXSrc->IsHiddenByParaField() &&
!SwScriptInfo::IsInHiddenRange( *pTOXSrc, pTextMark->GetStart() ) )
{
SwTOXSortTabBase* pBase = nullptr;
@@ -1206,7 +1206,7 @@ void SwTOXBaseSection::UpdateOutline( const SwTextNode* pOwnChapterNode )
if( pTextNd && pTextNd->Len() && pTextNd->HasWriterListeners() &&
sal_uInt16( pTextNd->GetAttrOutlineLevel()) <= GetLevel() &&
pTextNd->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() ) &&
- !pTextNd->HasHiddenParaField() &&
+ !pTextNd->IsHiddenByParaField() &&
!pTextNd->HasHiddenCharAttribute( true ) &&
( !IsFromChapter() ||
::lcl_FindChapterNode( *pTextNd ) == pOwnChapterNode ))
diff --git a/sw/source/core/inc/DocumentSettingManager.hxx b/sw/source/core/inc/DocumentSettingManager.hxx
index 5f762d099ca8..1cf9a9d9fea9 100644
--- a/sw/source/core/inc/DocumentSettingManager.hxx
+++ b/sw/source/core/inc/DocumentSettingManager.hxx
@@ -155,6 +155,7 @@ class DocumentSettingManager :
bool mbLastBrowseMode : 1;
bool mbDisableOffPagePositioning; // tdf#112443
+ bool mbEmptyDbFieldHidesPara;
public:
diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx
index 05037aa7ead3..6cccb9653a3e 100644
--- a/sw/source/core/text/txtfrm.cxx
+++ b/sw/source/core/text/txtfrm.cxx
@@ -457,12 +457,11 @@ bool SwTextFrame::IsHiddenNow() const
return true;
}
- const bool bHiddenCharsHidePara = GetTextNode()->HasHiddenCharAttribute( true );
- const bool bHiddenParaField = GetTextNode()->HasHiddenParaField();
- const SwViewShell* pVsh = getRootFrame()->GetCurrShell();
-
- if ( pVsh && ( bHiddenCharsHidePara || bHiddenParaField ) )
+ if ( const SwViewShell* pVsh = getRootFrame()->GetCurrShell() )
{
+ const bool bHiddenCharsHidePara = GetTextNode()->HasHiddenCharAttribute(true);
+ const bool bHiddenParaField = GetTextNode()->IsHiddenByParaField();
+
if (
( bHiddenParaField &&
( !pVsh->GetViewOptions()->IsShowHiddenPara() &&
diff --git a/sw/source/core/txtnode/atrfld.cxx b/sw/source/core/txtnode/atrfld.cxx
index c1ddb549defc..ceec40078ec0 100644
--- a/sw/source/core/txtnode/atrfld.cxx
+++ b/sw/source/core/txtnode/atrfld.cxx
@@ -379,18 +379,26 @@ void SwTextField::ExpandTextField(const bool bForceNotify) const
const SwField* pField = GetFormatField().GetField();
const OUString aNewExpand( pField->ExpandField(m_pTextNode->GetDoc()->IsClipBoard()) );
+ const SwFieldIds nWhich = pField->GetTyp()->Which();
+ const bool bSameExpandSimpleNotification
+ = SwFieldIds::Chapter != nWhich && SwFieldIds::PageNumber != nWhich
+ && SwFieldIds::RefPageGet != nWhich
+ // Page count fields to not use aExpand during formatting,
+ // therefore an invalidation of the text frame has to be triggered even if aNewExpand == aExpand:
+ && (SwFieldIds::DocStat != nWhich
+ || DS_PAGE != static_cast<const SwDocStatField*>(pField)->GetSubType())
+ && (SwFieldIds::GetExp != nWhich
+ || static_cast<const SwGetExpField*>(pField)->IsInBodyText());
+
+ bool bHiddenParaChanged = false;
+ if (aNewExpand != m_aExpand || bSameExpandSimpleNotification)
+ bHiddenParaChanged = m_pTextNode->CalcHiddenParaField();
+
if (aNewExpand == m_aExpand)
{
- const SwFieldIds nWhich = pField->GetTyp()->Which();
- if ( SwFieldIds::Chapter != nWhich
- && SwFieldIds::PageNumber != nWhich
- && SwFieldIds::RefPageGet != nWhich
- // Page count fields to not use aExpand during formatting,
- // therefore an invalidation of the text frame has to be triggered even if aNewExpand == aExpand:
- && ( SwFieldIds::DocStat != nWhich || DS_PAGE != static_cast<const SwDocStatField*>(pField)->GetSubType() )
- && ( SwFieldIds::GetExp != nWhich || static_cast<const SwGetExpField*>(pField)->IsInBodyText() ) )
+ if ( bSameExpandSimpleNotification )
{
- if( m_pTextNode->CalcHiddenParaField() )
+ if( bHiddenParaChanged )
{
m_pTextNode->ModifyNotification( nullptr, nullptr );
}
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index 193b3a654eb7..03fbb0c39da6 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -4168,7 +4168,7 @@ void SwTextNode::CalcHiddenCharFlags() const
// #i12836# enhanced pdf export
bool SwTextNode::IsHidden() const
{
- if ( HasHiddenParaField() || HasHiddenCharAttribute( true ) )
+ if ( IsHiddenByParaField() || HasHiddenCharAttribute( true ) )
return true;
const SwSectionNode* pSectNd = FindSectionNode();
diff --git a/sw/source/core/txtnode/thints.cxx b/sw/source/core/txtnode/thints.cxx
index e77276f1fc35..bc111093273b 100644
--- a/sw/source/core/txtnode/thints.cxx
+++ b/sw/source/core/txtnode/thints.cxx
@@ -19,6 +19,7 @@
#include <sal/config.h>
+#include <DocumentSettingManager.hxx>
#include <hintids.hxx>
#include <editeng/xmlcnitm.hxx>
#include <editeng/rsiditem.hxx>
@@ -88,11 +89,12 @@
using namespace ::com::sun::star::i18n;
-SwpHints::SwpHints()
- : m_pHistory(nullptr)
+SwpHints::SwpHints(const SwTextNode& rParent)
+ : m_rParent(rParent)
+ , m_pHistory(nullptr)
, m_bInSplitNode(false)
, m_bCalcHiddenParaField(false)
- , m_bHasHiddenParaField(false)
+ , m_bHiddenByParaField(false)
, m_bFootnote(false)
, m_bDDEFields(false)
{
@@ -1145,19 +1147,21 @@ void SwTextNode::DestroyAttr( SwTextAttr* pAttr )
if( !pDoc->IsInDtor() )
{
SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pAttr));
- const SwField* pField = pAttr->GetFormatField().GetField();
+ SwFieldType* pFieldType = pAttr->GetFormatField().GetField()->GetTyp();
//JP 06-08-95: DDE-fields are an exception
- assert(SwFieldIds::Dde == pField->GetTyp()->Which() ||
+ assert(SwFieldIds::Dde == pFieldType->Which() ||
this == pTextField->GetpTextNode());
// certain fields must update the SwDoc's calculation flags
- switch( pField->GetTyp()->Which() )
+
+ // Certain fields (like HiddenParaField) must trigger recalculation of visible flag
+ if (FieldCanHidePara(pFieldType->Which()))
+ SetCalcHiddenParaField();
+
+ switch( pFieldType->Which() )
{
case SwFieldIds::HiddenPara:
- // HiddenParaField must trigger recalculation of visible flag
- SetCalcHiddenParaField();
- SAL_FALLTHROUGH;
case SwFieldIds::DbSetNumber:
case SwFieldIds::GetExp:
case SwFieldIds::Database:
@@ -1170,7 +1174,7 @@ void SwTextNode::DestroyAttr( SwTextAttr* pAttr )
break;
case SwFieldIds::Dde:
if (GetNodes().IsDocNodes() && pTextField->GetpTextNode())
- static_cast<SwDDEFieldType*>(pField->GetTyp())->DecRefCnt();
+ static_cast<SwDDEFieldType*>(pFieldType)->DecRefCnt();
break;
case SwFieldIds::Postit:
{
@@ -1461,8 +1465,8 @@ bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode )
case RES_TXTATR_FIELD:
{
- // trigger notification for HiddenParaFields
- if( SwFieldIds::HiddenPara == pAttr->GetFormatField().GetField()->GetTyp()->Which() )
+ // trigger notification for relevant fields, like HiddenParaFields
+ if (FieldCanHidePara(pAttr->GetFormatField().GetField()->GetTyp()->Which()))
{
bHiddenPara = true;
}
@@ -2586,38 +2590,32 @@ void SwpHints::CalcFlags()
}
}
-bool SwpHints::CalcHiddenParaField()
+bool SwpHints::CalcHiddenParaField() const
{
m_bCalcHiddenParaField = false;
- bool bOldHasHiddenParaField = m_bHasHiddenParaField;
- bool bNewHasHiddenParaField = false;
+ const bool bOldHiddenByParaField = m_bHiddenByParaField;
+ bool bNewHiddenByParaField = false;
const size_t nSize = Count();
- const SwTextAttr *pTextHt;
+ const SwTextAttr* pTextHt;
- for( size_t nPos = 0; nPos < nSize; ++nPos )
+ for (size_t nPos = 0; nPos < nSize; ++nPos)
{
pTextHt = Get(nPos);
const sal_uInt16 nWhich = pTextHt->Which();
- if( RES_TXTATR_FIELD == nWhich )
+ if (RES_TXTATR_FIELD == nWhich)
{
const SwFormatField& rField = pTextHt->GetFormatField();
- if( SwFieldIds::HiddenPara == rField.GetField()->GetTyp()->Which() )
+ if (m_rParent.FieldCanHidePara(rField.GetField()->GetTyp()->Which())
+ && !(bNewHiddenByParaField = m_rParent.FieldHidesPara(*rField.GetField())))
{
- if( !static_cast<const SwHiddenParaField*>(rField.GetField())->IsHidden() )
- {
- SetHiddenParaField(false);
- return bOldHasHiddenParaField != bNewHasHiddenParaField;
- }
- else
- {
- bNewHasHiddenParaField = true;
- }
+ // If there's at least one field telling not to hide, so be it
+ break;
}
}
}
- SetHiddenParaField( bNewHasHiddenParaField );
- return bOldHasHiddenParaField != bNewHasHiddenParaField;
+ SetHiddenByParaField(bNewHiddenByParaField);
+ return bOldHiddenByParaField != bNewHiddenByParaField;
}
void SwpHints::NoteInHistory( SwTextAttr *pAttr, const bool bNew )
@@ -3285,7 +3283,8 @@ void SwpHints::DeleteAtPos( const size_t nPos )
if( pHint->Which() == RES_TXTATR_FIELD )
{
SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
- const SwFieldType* pFieldTyp = pTextField->GetFormatField().GetField()->GetTyp();
+ const SwField* pField = pTextField->GetFormatField().GetField();
+ const SwFieldType* pFieldTyp = pField->GetTyp();
if( SwFieldIds::Dde == pFieldTyp->Which() )
{
const SwTextNode* pNd = pTextField->GetpTextNode();
@@ -3293,8 +3292,8 @@ void SwpHints::DeleteAtPos( const size_t nPos )
const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pFieldTyp))->DecRefCnt();
pTextField->ChgTextNode(nullptr);
}
- else if ( m_bHasHiddenParaField &&
- SwFieldIds::HiddenPara == pFieldTyp->Which() )
+ else if (m_bHiddenByParaField
+ && m_rParent.FieldCanHidePara(pField->GetTyp()->Which()))
{
m_bCalcHiddenParaField = true;
}
diff --git a/sw/source/core/view/viewsh.cxx b/sw/source/core/view/viewsh.cxx
index bb1ab6b3f454..fa81d1736f2c 100644
--- a/sw/source/core/view/viewsh.cxx
+++ b/sw/source/core/view/viewsh.cxx
@@ -923,6 +923,26 @@ void SwViewShell::SetSubtractFlysAnchoredAtFlys(bool bSubtractFlysAnchoredAtFlys
rIDSA.set(DocumentSettingId::SUBTRACT_FLYS, bSubtractFlysAnchoredAtFlys);
}
+void SwViewShell::SetEmptyDbFieldHidesPara(bool bEmptyDbFieldHidesPara)
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if (rIDSA.get(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA) != bEmptyDbFieldHidesPara)
+ {
+ SwWait aWait(*GetDoc()->GetDocShell(), true);
+ rIDSA.set(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA, bEmptyDbFieldHidesPara);
+ StartAction();
+ GetDoc()->getIDocumentState().SetModified();
+ for (auto* pFieldType : *GetDoc()->getIDocumentFieldsAccess().GetFieldTypes())
+ {
+ if (pFieldType->Which() == SwFieldIds::Database)
+ {
+ pFieldType->ModifyNotification(nullptr, nullptr);
+ }
+ }
+ EndAction();
+ }
+}
+
void SwViewShell::Reformat()
{
SwWait aWait( *GetDoc()->GetDocShell(), true );
diff --git a/sw/source/filter/xml/xmlimp.cxx b/sw/source/filter/xml/xmlimp.cxx
index dfbc7f832105..ff9aaf182fe2 100644
--- a/sw/source/filter/xml/xmlimp.cxx
+++ b/sw/source/filter/xml/xmlimp.cxx
@@ -568,6 +568,32 @@ void SwXMLImport::startDocument()
m_bOrganizerMode = true;
}
}
+
+ // default document properties
+ const OUString sDefSettings("DefaultDocumentSettings");
+ if (xPropertySetInfo->hasPropertyByName(sDefSettings))
+ {
+ aAny = xImportInfo->getPropertyValue(sDefSettings);
+ Sequence<PropertyValue> aProps;
+ if (aAny >>= aProps)
+ {
+ Reference<lang::XMultiServiceFactory> xFac(GetModel(), UNO_QUERY);
+ Reference<XPropertySet> xProps(
+ xFac->createInstance("com.sun.star.document.Settings"), UNO_QUERY);
+ Reference<XPropertySetInfo> xInfo(xProps->getPropertySetInfo());
+
+ if (xProps.is() && xInfo.is())
+ {
+ for (const auto& rProp : aProps)
+ {
+ if (xInfo->hasPropertyByName(rProp.Name))
+ {
+ xProps->setPropertyValue(rProp.Name, rProp.Value);
+ }
+ }
+ }
+ }
+ }
}
// There only is a text cursor by now if we are in insert mode. In any
diff --git a/sw/source/ui/config/optcomp.cxx b/sw/source/ui/config/optcomp.cxx
index c204998f35a0..7ed785f197e9 100644
--- a/sw/source/ui/config/optcomp.cxx
+++ b/sw/source/ui/config/optcomp.cxx
@@ -115,7 +115,8 @@ sal_uLong convertBools2Ulong_Impl
bool _bExpandWordSpace,
bool _bProtectForm,
bool _bMsWordCompTrailingBlanks,
- bool bSubtractFlysAnchoredAtFlys
+ bool bSubtractFlysAnchoredAtFlys,
+ bool bEmptyDbFieldHidesPara
)
{
sal_uLong nRet = 0;
@@ -162,6 +163,9 @@ sal_uLong convertBools2Ulong_Impl
nSetBit = nSetBit << 1;
if (bSubtractFlysAnchoredAtFlys)
nRet |= nSetBit;
+ nSetBit = nSetBit << 1;
+ if (bEmptyDbFieldHidesPara)
+ nRet |= nSetBit;
return nRet;
}
@@ -246,7 +250,8 @@ void SwCompatibilityOptPage::InitControls( const SfxItemSet& rSet )
aEntry.getValue<bool>( SvtCompatibilityEntry::Index::ExpandWordSpace ),
aEntry.getValue<bool>( SvtCompatibilityEntry::Index::ProtectForm ),
aEntry.getValue<bool>( SvtCompatibilityEntry::Index::MsWordTrailingBlanks ),
- aEntry.getValue<bool>( SvtCompatibilityEntry::Index::SubtractFlysAnchoredAtFlys ) );
+ aEntry.getValue<bool>( SvtCompatibilityEntry::Index::SubtractFlysAnchoredAtFlys ),
+ aEntry.getValue<bool>( SvtCompatibilityEntry::Index::EmptyDbFieldHidesPara ) );
m_pFormattingLB->SetEntryData( nPos, reinterpret_cast<void*>((sal_IntPtr)nOptions) );
}
@@ -319,7 +324,8 @@ sal_uLong SwCompatibilityOptPage::GetDocumentOptions() const
!rIDocumentSettingAccess.get( DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK ),
rIDocumentSettingAccess.get( DocumentSettingId::PROTECT_FORM ),
rIDocumentSettingAccess.get( DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS ),
- rIDocumentSettingAccess.get( DocumentSettingId::SUBTRACT_FLYS ) );
+ rIDocumentSettingAccess.get( DocumentSettingId::SUBTRACT_FLYS ),
+ rIDocumentSettingAccess.get( DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA ) );
}
return nRet;
}
@@ -411,6 +417,10 @@ bool SwCompatibilityOptPage::FillItemSet( SfxItemSet* )
m_pWrtShell->SetSubtractFlysAnchoredAtFlys(bChecked);
break;
+ case SvtCompatibilityEntry::Index::EmptyDbFieldHidesPara:
+ m_pWrtShell->SetEmptyDbFieldHidesPara(bChecked);
+ break;
+
default:
break;
}
diff --git a/sw/source/uibase/app/docshini.cxx b/sw/source/uibase/app/docshini.cxx
index fe99774d7a6c..390f1c6c717d 100644
--- a/sw/source/uibase/app/docshini.cxx
+++ b/sw/source/uibase/app/docshini.cxx
@@ -88,6 +88,7 @@
#include <globals.hrc>
#include <unochart.hxx>
#include <drawdoc.hxx>
+#include <DocumentSettingManager.hxx>
#include <svx/CommonStyleManager.hxx>
@@ -483,14 +484,6 @@ bool SwDocShell::Load( SfxMedium& rMedium )
{
bool bRet = false;
- // If this is an ODF file being loaded, then by default, use legacy processing
- // for tdf#99729 (if required, it will be overridden in *::ReadUserDataSequence())
- if (IsOwnStorageFormat(rMedium))
- {
- if (m_xDoc.get() && m_xDoc->getIDocumentDrawModelAccess().GetDrawModel())
- m_xDoc->getIDocumentDrawModelAccess().GetDrawModel()->SetAnchoredTextOverflowLegacy(true);
- }
-
if (SfxObjectShell::Load(rMedium))
{
comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = getEmbeddedObjectContainer();
@@ -502,6 +495,19 @@ bool SwDocShell::Load( SfxMedium& rMedium )
AddLink(); // set Link and update Data!!
+ // Define some settings for legacy ODF files that have different default values now
+ // (if required, they will be overridden later when settings will be read)
+ if (IsOwnStorageFormat(rMedium))
+ {
+ // legacy processing for tdf#99729
+ if (m_xDoc->getIDocumentDrawModelAccess().GetDrawModel())
+ m_xDoc->getIDocumentDrawModelAccess().GetDrawModel()->SetAnchoredTextOverflowLegacy(
+ true);
+ // legacy behaviour (not hiding paragraph) for Database (MailMerge) fields
+ m_xDoc->GetDocumentSettingManager().set(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA,
+ false);
+ }
+
// Loading
// for MD
OSL_ENSURE( !m_xBasePool.is(), "who hasn't destroyed their Pool?" );
diff --git a/sw/source/uibase/uno/SwXDocumentSettings.cxx b/sw/source/uibase/uno/SwXDocumentSettings.cxx
index ee86063fea52..40425840c6b8 100644
--- a/sw/source/uibase/uno/SwXDocumentSettings.cxx
+++ b/sw/source/uibase/uno/SwXDocumentSettings.cxx
@@ -135,6 +135,7 @@ enum SwDocumentSettingsPropertyHandles
HANDLE_PROP_LINE_SPACING_SHRINKS_FIRST_LINE,
HANDLE_SUBTRACT_FLYS,
HANDLE_DISABLE_OFF_PAGE_POSITIONING,
+ HANDLE_EMPTY_DB_FIELD_HIDES_PARA,
};
static MasterPropertySetInfo * lcl_createSettingsInfo()
@@ -212,6 +213,7 @@ static MasterPropertySetInfo * lcl_createSettingsInfo()
{ OUString("PropLineSpacingShrinksFirstLine"), HANDLE_PROP_LINE_SPACING_SHRINKS_FIRST_LINE, cppu::UnoType<bool>::get(), 0},
{ OUString("SubtractFlysAnchoredAtFlys"), HANDLE_SUBTRACT_FLYS, cppu::UnoType<bool>::get(), 0},
{ OUString("DisableOffPagePositioning"), HANDLE_DISABLE_OFF_PAGE_POSITIONING, cppu::UnoType<bool>::get(), 0},
+ { OUString("EmptyDbFieldHidesPara"), HANDLE_EMPTY_DB_FIELD_HIDES_PARA, cppu::UnoType<bool>::get(), 0 },
/*
* As OS said, we don't have a view when we need to set this, so I have to
* find another solution before adding them to this property set - MTG
@@ -867,6 +869,16 @@ void SwXDocumentSettings::_setSingleValue( const comphelper::PropertyInfo & rInf
}
}
break;
+ case HANDLE_EMPTY_DB_FIELD_HIDES_PARA:
+ {
+ bool bTmp;
+ if (rValue >>= bTmp)
+ {
+ mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA,
+ bTmp);
+ }
+ }
+ break;
default:
throw UnknownPropertyException();
}
@@ -1286,6 +1298,12 @@ void SwXDocumentSettings::_getSingleValue( const comphelper::PropertyInfo & rInf
rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING);
}
break;
+ case HANDLE_EMPTY_DB_FIELD_HIDES_PARA:
+ {
+ rValue <<= mpDoc->getIDocumentSettingAccess().get(
+ DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA);
+ }
+ break;
default:
throw UnknownPropertyException();
}
diff --git a/sw/uiconfig/swriter/ui/optcompatpage.ui b/sw/uiconfig/swriter/ui/optcompatpage.ui
index f297aeb6bc04..4b3e888d77e8 100644
--- a/sw/uiconfig/swriter/ui/optcompatpage.ui
+++ b/sw/uiconfig/swriter/ui/optcompatpage.ui
@@ -67,6 +67,7 @@
<item translatable="yes" context="optcompatpage|format">Protect form</item>
<item translatable="yes" context="optcompatpage|format">MS Word-compatible trailing blanks</item>
<item translatable="yes" context="optcompatpage|format">Tolerate white lines of PDF page backgrounds for compatibility with old documents</item>
+ <item translatable="yes" context="optcompatpage|format">A database field (e.g., MailMerge) with empty value hides its paragraph</item>
<item translatable="yes" context="optcompatpage|format">&lt;User settings&gt;</item>
</items>
</object>
diff --git a/unotools/source/config/compatibility.cxx b/unotools/source/config/compatibility.cxx
index aa10fbc18601..de876fd30fec 100644
--- a/unotools/source/config/compatibility.cxx
+++ b/unotools/source/config/compatibility.cxx
@@ -62,6 +62,7 @@ SvtCompatibilityEntry::SvtCompatibilityEntry()
setValue<bool>( Index::ProtectForm, false );
setValue<bool>( Index::MsWordTrailingBlanks, false );
setValue<bool>( Index::SubtractFlysAnchoredAtFlys, false );
+ setValue<bool>( Index::EmptyDbFieldHidesPara, true );
setDefaultEntry( false );
}
@@ -92,7 +93,8 @@ OUString SvtCompatibilityEntry::getName( const Index rIdx )
"ExpandWordSpace",
"ProtectForm",
"MsWordCompTrailingBlanks",
- "SubtractFlysAnchoredAtFlys"
+ "SubtractFlysAnchoredAtFlys",
+ "EmptyDbFieldHidesPara",
};
/* Size of sPropertyName array not equal size of the SvtCompatibilityEntry::Index enum class */