summaryrefslogtreecommitdiff
path: root/xmloff/source/text/XMLRedlineExport.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'xmloff/source/text/XMLRedlineExport.cxx')
-rw-r--r--xmloff/source/text/XMLRedlineExport.cxx672
1 files changed, 672 insertions, 0 deletions
diff --git a/xmloff/source/text/XMLRedlineExport.cxx b/xmloff/source/text/XMLRedlineExport.cxx
new file mode 100644
index 000000000000..67970330f3ef
--- /dev/null
+++ b/xmloff/source/text/XMLRedlineExport.cxx
@@ -0,0 +1,672 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_xmloff.hxx"
+#include "XMLRedlineExport.hxx"
+#include <tools/debug.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/document/XRedlinesSupplier.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/text/XTextSection.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <xmloff/xmltoken.hxx>
+#include "xmlnmspe.hxx"
+#include <xmloff/xmlexp.hxx>
+#include <xmloff/xmluconv.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+using ::com::sun::star::beans::PropertyValue;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::beans::UnknownPropertyException;
+using ::com::sun::star::document::XRedlinesSupplier;
+using ::com::sun::star::container::XEnumerationAccess;
+using ::com::sun::star::container::XEnumeration;
+using ::com::sun::star::text::XText;
+using ::com::sun::star::text::XTextContent;
+using ::com::sun::star::text::XTextSection;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::util::DateTime;
+using ::rtl::OUString;
+using ::rtl::OUStringBuffer;
+using ::std::list;
+
+
+XMLRedlineExport::XMLRedlineExport(SvXMLExport& rExp)
+: sDelete(RTL_CONSTASCII_USTRINGPARAM("Delete"))
+, sDeletion(GetXMLToken(XML_DELETION))
+, sFormat(RTL_CONSTASCII_USTRINGPARAM("Format"))
+, sFormatChange(GetXMLToken(XML_FORMAT_CHANGE))
+, sInsert(RTL_CONSTASCII_USTRINGPARAM("Insert"))
+, sInsertion(GetXMLToken(XML_INSERTION))
+, sIsCollapsed(RTL_CONSTASCII_USTRINGPARAM("IsCollapsed"))
+, sIsStart(RTL_CONSTASCII_USTRINGPARAM("IsStart"))
+, sRedlineAuthor(RTL_CONSTASCII_USTRINGPARAM("RedlineAuthor"))
+, sRedlineComment(RTL_CONSTASCII_USTRINGPARAM("RedlineComment"))
+, sRedlineDateTime(RTL_CONSTASCII_USTRINGPARAM("RedlineDateTime"))
+, sRedlineSuccessorData(RTL_CONSTASCII_USTRINGPARAM("RedlineSuccessorData"))
+, sRedlineText(RTL_CONSTASCII_USTRINGPARAM("RedlineText"))
+, sRedlineType(RTL_CONSTASCII_USTRINGPARAM("RedlineType"))
+, sStyle(RTL_CONSTASCII_USTRINGPARAM("Style"))
+, sTextTable(RTL_CONSTASCII_USTRINGPARAM("TextTable"))
+, sUnknownChange(RTL_CONSTASCII_USTRINGPARAM("UnknownChange"))
+, sStartRedline(RTL_CONSTASCII_USTRINGPARAM("StartRedline"))
+, sEndRedline(RTL_CONSTASCII_USTRINGPARAM("EndRedline"))
+, sRedlineIdentifier(RTL_CONSTASCII_USTRINGPARAM("RedlineIdentifier"))
+, sIsInHeaderFooter(RTL_CONSTASCII_USTRINGPARAM("IsInHeaderFooter"))
+, sRedlineProtectionKey(RTL_CONSTASCII_USTRINGPARAM("RedlineProtectionKey"))
+, sRecordChanges(RTL_CONSTASCII_USTRINGPARAM("RecordChanges"))
+, sMergeLastPara(RTL_CONSTASCII_USTRINGPARAM("MergeLastPara"))
+, sChangePrefix(RTL_CONSTASCII_USTRINGPARAM("ct"))
+, rExport(rExp)
+, pCurrentChangesList(NULL)
+{
+}
+
+
+XMLRedlineExport::~XMLRedlineExport()
+{
+ // delete changes lists
+ for( ChangesMapType::iterator aIter = aChangeMap.begin();
+ aIter != aChangeMap.end();
+ aIter++ )
+ {
+ delete aIter->second;
+ }
+ aChangeMap.clear();
+}
+
+
+void XMLRedlineExport::ExportChange(
+ const Reference<XPropertySet> & rPropSet,
+ sal_Bool bAutoStyle)
+{
+ if (bAutoStyle)
+ {
+ // For the headers/footers, we have to collect the autostyles
+ // here. For the general case, however, it's better to collet
+ // the autostyles by iterating over the global redline
+ // list. So that's what we do: Here, we collect autostyles
+ // only if we have no current list of changes. For the
+ // main-document case, the autostyles are collected in
+ // ExportChangesListAutoStyles().
+ if (pCurrentChangesList != NULL)
+ ExportChangeAutoStyle(rPropSet);
+ }
+ else
+ {
+ ExportChangeInline(rPropSet);
+ }
+}
+
+
+void XMLRedlineExport::ExportChangesList(sal_Bool bAutoStyles)
+{
+ if (bAutoStyles)
+ {
+ ExportChangesListAutoStyles();
+ }
+ else
+ {
+ ExportChangesListElements();
+ }
+}
+
+
+void XMLRedlineExport::ExportChangesList(
+ const Reference<XText> & rText,
+ sal_Bool bAutoStyles)
+{
+ // in the header/footer case, auto styles are collected from the
+ // inline change elements.
+ if (bAutoStyles)
+ return;
+
+ // look for changes list for this XText
+ ChangesMapType::iterator aFind = aChangeMap.find(rText);
+ if (aFind != aChangeMap.end())
+ {
+ ChangesListType* pChangesList = aFind->second;
+
+ // export only if changes are found
+ if (pChangesList->size() > 0)
+ {
+ // changes container element
+ SvXMLElementExport aChanges(rExport, XML_NAMESPACE_TEXT,
+ XML_TRACKED_CHANGES,
+ sal_True, sal_True);
+
+ // iterate over changes list
+ for( ChangesListType::iterator aIter = pChangesList->begin();
+ aIter != pChangesList->end();
+ aIter++ )
+ {
+ ExportChangedRegion( *aIter );
+ }
+ }
+ // else: changes list empty -> ignore
+ }
+ // else: no changes list found -> empty
+}
+
+void XMLRedlineExport::SetCurrentXText(
+ const Reference<XText> & rText)
+{
+ if (rText.is())
+ {
+ // look for appropriate list in map; use the found one, or create new
+ ChangesMapType::iterator aIter = aChangeMap.find(rText);
+ if (aIter == aChangeMap.end())
+ {
+ ChangesListType* pList = new ChangesListType;
+ aChangeMap[rText] = pList;
+ pCurrentChangesList = pList;
+ }
+ else
+ pCurrentChangesList = aIter->second;
+ }
+ else
+ {
+ // don't record changes
+ SetCurrentXText();
+ }
+}
+
+void XMLRedlineExport::SetCurrentXText()
+{
+ pCurrentChangesList = NULL;
+}
+
+
+void XMLRedlineExport::ExportChangesListElements()
+{
+ // get redlines (aka tracked changes) from the model
+ Reference<XRedlinesSupplier> xSupplier(rExport.GetModel(), uno::UNO_QUERY);
+ if (xSupplier.is())
+ {
+ Reference<XEnumerationAccess> aEnumAccess = xSupplier->getRedlines();
+
+ // redline protection key
+ Reference<XPropertySet> aDocPropertySet( rExport.GetModel(),
+ uno::UNO_QUERY );
+ // redlining enabled?
+ sal_Bool bEnabled = *(sal_Bool*)aDocPropertySet->getPropertyValue(
+ sRecordChanges ).getValue();
+
+ // only export if we have redlines or attributes
+ if ( aEnumAccess->hasElements() || bEnabled )
+ {
+
+ // export only if we have changes, but tracking is not enabled
+ if ( !bEnabled != !aEnumAccess->hasElements() )
+ {
+ rExport.AddAttribute(
+ XML_NAMESPACE_TEXT, XML_TRACK_CHANGES,
+ bEnabled ? XML_TRUE : XML_FALSE );
+ }
+
+ // changes container element
+ SvXMLElementExport aChanges(rExport, XML_NAMESPACE_TEXT,
+ XML_TRACKED_CHANGES,
+ sal_True, sal_True);
+
+ // get enumeration and iterate over elements
+ Reference<XEnumeration> aEnum = aEnumAccess->createEnumeration();
+ while (aEnum->hasMoreElements())
+ {
+ Any aAny = aEnum->nextElement();
+ Reference<XPropertySet> xPropSet;
+ aAny >>= xPropSet;
+
+ DBG_ASSERT(xPropSet.is(),
+ "can't get XPropertySet; skipping Redline");
+ if (xPropSet.is())
+ {
+ // export only if not in header or footer
+ // (those must be exported with their XText)
+ aAny = xPropSet->getPropertyValue(sIsInHeaderFooter);
+ if (! *(sal_Bool*)aAny.getValue())
+ {
+ // and finally, export change
+ ExportChangedRegion(xPropSet);
+ }
+ }
+ // else: no XPropertySet -> no export
+ }
+ }
+ // else: no redlines -> no export
+ }
+ // else: no XRedlineSupplier -> no export
+}
+
+void XMLRedlineExport::ExportChangeAutoStyle(
+ const Reference<XPropertySet> & rPropSet)
+{
+ // record change (if changes should be recorded)
+ if (NULL != pCurrentChangesList)
+ {
+ // put redline in list if it's collapsed or the redline start
+ Any aIsStart = rPropSet->getPropertyValue(sIsStart);
+ Any aIsCollapsed = rPropSet->getPropertyValue(sIsCollapsed);
+
+ if ( *(sal_Bool*)aIsStart.getValue() ||
+ *(sal_Bool*)aIsCollapsed.getValue() )
+ pCurrentChangesList->push_back(rPropSet);
+ }
+
+ // get XText for export of redline auto styles
+ Any aAny = rPropSet->getPropertyValue(sRedlineText);
+ Reference<XText> xText;
+ aAny >>= xText;
+ if (xText.is())
+ {
+ // export the auto styles
+ rExport.GetTextParagraphExport()->collectTextAutoStyles(xText);
+ }
+}
+
+void XMLRedlineExport::ExportChangesListAutoStyles()
+{
+ // get redlines (aka tracked changes) from the model
+ Reference<XRedlinesSupplier> xSupplier(rExport.GetModel(), uno::UNO_QUERY);
+ if (xSupplier.is())
+ {
+ Reference<XEnumerationAccess> aEnumAccess = xSupplier->getRedlines();
+
+ // only export if we actually have redlines
+ if (aEnumAccess->hasElements())
+ {
+ // get enumeration and iterate over elements
+ Reference<XEnumeration> aEnum = aEnumAccess->createEnumeration();
+ while (aEnum->hasMoreElements())
+ {
+ Any aAny = aEnum->nextElement();
+ Reference<XPropertySet> xPropSet;
+ aAny >>= xPropSet;
+
+ DBG_ASSERT(xPropSet.is(),
+ "can't get XPropertySet; skipping Redline");
+ if (xPropSet.is())
+ {
+
+ // export only if not in header or footer
+ // (those must be exported with their XText)
+ aAny = xPropSet->getPropertyValue(sIsInHeaderFooter);
+ if (! *(sal_Bool*)aAny.getValue())
+ {
+ ExportChangeAutoStyle(xPropSet);
+ }
+ }
+ }
+ }
+ }
+}
+
+void XMLRedlineExport::ExportChangeInline(
+ const Reference<XPropertySet> & rPropSet)
+{
+ // determine element name (depending on collapsed, start/end)
+ enum XMLTokenEnum eElement = XML_TOKEN_INVALID;
+ Any aAny = rPropSet->getPropertyValue(sIsCollapsed);
+ sal_Bool bCollapsed = *(sal_Bool *)aAny.getValue();
+ sal_Bool bStart = sal_True; // ignored if bCollapsed = sal_True
+ if (bCollapsed)
+ {
+ eElement = XML_CHANGE;
+ }
+ else
+ {
+ aAny = rPropSet->getPropertyValue(sIsStart);
+ bStart = *(sal_Bool *)aAny.getValue();
+ eElement = bStart ? XML_CHANGE_START : XML_CHANGE_END;
+ }
+
+ if (XML_TOKEN_INVALID != eElement)
+ {
+ // we always need the ID
+ rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_CHANGE_ID,
+ GetRedlineID(rPropSet));
+
+ // export the element (no whitespace because we're in the text body)
+ SvXMLElementExport aChangeElem(rExport, XML_NAMESPACE_TEXT,
+ eElement, sal_False, sal_False);
+ }
+}
+
+
+void XMLRedlineExport::ExportChangedRegion(
+ const Reference<XPropertySet> & rPropSet)
+{
+ // Redline-ID
+ rExport.AddAttributeIdLegacy(XML_NAMESPACE_TEXT, GetRedlineID(rPropSet));
+
+ // merge-last-paragraph
+ Any aAny = rPropSet->getPropertyValue(sMergeLastPara);
+ if( ! *(sal_Bool*)aAny.getValue() )
+ rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_MERGE_LAST_PARAGRAPH,
+ XML_FALSE);
+
+ // export change region element
+ SvXMLElementExport aChangedRegion(rExport, XML_NAMESPACE_TEXT,
+ XML_CHANGED_REGION, sal_True, sal_True);
+
+
+ // scope for (first) change element
+ {
+ aAny = rPropSet->getPropertyValue(sRedlineType);
+ OUString sType;
+ aAny >>= sType;
+ SvXMLElementExport aChange(rExport, XML_NAMESPACE_TEXT,
+ ConvertTypeName(sType), sal_True, sal_True);
+
+ ExportChangeInfo(rPropSet);
+
+ // get XText from the redline and export (if the XText exists)
+ aAny = rPropSet->getPropertyValue(sRedlineText);
+ Reference<XText> xText;
+ aAny >>= xText;
+ if (xText.is())
+ {
+ rExport.GetTextParagraphExport()->exportText(xText);
+ // default parameters: bProgress, bExportParagraph ???
+ }
+ // else: no text interface -> content is inline and will
+ // be exported there
+ }
+
+ // changed change? Hierarchical changes can onl be two levels
+ // deep. Here we check for the second level.
+ aAny = rPropSet->getPropertyValue(sRedlineSuccessorData);
+ Sequence<PropertyValue> aSuccessorData;
+ aAny >>= aSuccessorData;
+
+ // if we actually got a hierarchical change, make element and
+ // process change info
+ if (aSuccessorData.getLength() > 0)
+ {
+ // The only change that can be "undone" is an insertion -
+ // after all, you can't re-insert an deletion, but you can
+ // delete an insertion. This assumption is asserted in
+ // ExportChangeInfo(Sequence<PropertyValue>&).
+ SvXMLElementExport aSecondChangeElem(
+ rExport, XML_NAMESPACE_TEXT, XML_INSERTION,
+ sal_True, sal_True);
+
+ ExportChangeInfo(aSuccessorData);
+ }
+ // else: no hierarchical change
+}
+
+
+const OUString XMLRedlineExport::ConvertTypeName(
+ const OUString& sApiName)
+{
+ if (sApiName == sDelete)
+ {
+ return sDeletion;
+ }
+ else if (sApiName == sInsert)
+ {
+ return sInsertion;
+ }
+ else if (sApiName == sFormat)
+ {
+ return sFormatChange;
+ }
+ else
+ {
+ DBG_ERROR("unknown redline type");
+ return sUnknownChange;
+ }
+}
+
+
+/** Create a Redline-ID */
+const OUString XMLRedlineExport::GetRedlineID(
+ const Reference<XPropertySet> & rPropSet)
+{
+ Any aAny = rPropSet->getPropertyValue(sRedlineIdentifier);
+ OUString sTmp;
+ aAny >>= sTmp;
+
+ OUStringBuffer sBuf(sChangePrefix);
+ sBuf.append(sTmp);
+ return sBuf.makeStringAndClear();
+}
+
+
+void XMLRedlineExport::ExportChangeInfo(
+ const Reference<XPropertySet> & rPropSet)
+{
+
+ SvXMLElementExport aChangeInfo(rExport, XML_NAMESPACE_OFFICE,
+ XML_CHANGE_INFO, sal_True, sal_True);
+
+ Any aAny = rPropSet->getPropertyValue(sRedlineAuthor);
+ OUString sTmp;
+ aAny >>= sTmp;
+ if (sTmp.getLength() > 0)
+ {
+ SvXMLElementExport aCreatorElem( rExport, XML_NAMESPACE_DC,
+ XML_CREATOR, sal_True,
+ sal_False );
+ rExport.Characters(sTmp);
+ }
+
+ aAny = rPropSet->getPropertyValue(sRedlineDateTime);
+ util::DateTime aDateTime;
+ aAny >>= aDateTime;
+ {
+ OUStringBuffer sBuf;
+ rExport.GetMM100UnitConverter().convertDateTime(sBuf, aDateTime);
+ SvXMLElementExport aDateElem( rExport, XML_NAMESPACE_DC,
+ XML_DATE, sal_True,
+ sal_False );
+ rExport.Characters(sBuf.makeStringAndClear());
+ }
+
+ // comment as <text:p> sequence
+ aAny = rPropSet->getPropertyValue(sRedlineComment);
+ aAny >>= sTmp;
+ WriteComment( sTmp );
+}
+
+void XMLRedlineExport::ExportChangeInfo(
+ const Sequence<PropertyValue> & rPropertyValues)
+{
+ OUString sComment;
+
+ sal_Int32 nCount = rPropertyValues.getLength();
+ for(sal_Int32 i = 0; i < nCount; i++)
+ {
+ const PropertyValue& rVal = rPropertyValues[i];
+
+ if( rVal.Name.equals(sRedlineAuthor) )
+ {
+ OUString sTmp;
+ rVal.Value >>= sTmp;
+ if (sTmp.getLength() > 0)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_CHG_AUTHOR, sTmp);
+ }
+ }
+ else if( rVal.Name.equals(sRedlineComment) )
+ {
+ rVal.Value >>= sComment;
+ }
+ else if( rVal.Name.equals(sRedlineDateTime) )
+ {
+ util::DateTime aDateTime;
+ rVal.Value >>= aDateTime;
+ OUStringBuffer sBuf;
+ rExport.GetMM100UnitConverter().convertDateTime(sBuf, aDateTime);
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_CHG_DATE_TIME,
+ sBuf.makeStringAndClear());
+ }
+ else if( rVal.Name.equals(sRedlineType) )
+ {
+ // check if this is an insertion; cf. comment at calling location
+ OUString sTmp;
+ rVal.Value >>= sTmp;
+ DBG_ASSERT(sTmp.equals(sInsert),
+ "hierarchical change must be insertion");
+ }
+ // else: unknown value -> ignore
+ }
+
+ // finally write element
+ SvXMLElementExport aChangeInfo(rExport, XML_NAMESPACE_OFFICE,
+ XML_CHANGE_INFO, sal_True, sal_True);
+
+ WriteComment( sComment );
+}
+
+void XMLRedlineExport::ExportStartOrEndRedline(
+ const Reference<XPropertySet> & rPropSet,
+ sal_Bool bStart)
+{
+ if( ! rPropSet.is() )
+ return;
+
+ // get appropriate (start or end) property
+ Any aAny;
+ try
+ {
+ aAny = rPropSet->getPropertyValue(bStart ? sStartRedline : sEndRedline);
+ }
+ catch( UnknownPropertyException e )
+ {
+ // If we don't have the property, there's nothing to do.
+ return;
+ }
+
+ Sequence<PropertyValue> aValues;
+ aAny >>= aValues;
+ const PropertyValue* pValues = aValues.getConstArray();
+
+ // seek for redline properties
+ sal_Bool bIsCollapsed = sal_False;
+ sal_Bool bIsStart = sal_True;
+ OUString sId;
+ sal_Bool bIdOK = sal_False; // have we seen an ID?
+ sal_Int32 nLength = aValues.getLength();
+ for(sal_Int32 i = 0; i < nLength; i++)
+ {
+ if (sRedlineIdentifier.equals(pValues[i].Name))
+ {
+ pValues[i].Value >>= sId;
+ bIdOK = sal_True;
+ }
+ else if (sIsCollapsed.equals(pValues[i].Name))
+ {
+ bIsCollapsed = *(sal_Bool*)pValues[i].Value.getValue();
+ }
+ else if (sIsStart.equals(pValues[i].Name))
+ {
+ bIsStart = *(sal_Bool*)pValues[i].Value.getValue();
+ }
+ }
+
+ if( bIdOK )
+ {
+ DBG_ASSERT( sId.getLength() > 0, "Redlines must have IDs" );
+
+ // TODO: use GetRedlineID or elimiate that function
+ OUStringBuffer sBuffer(sChangePrefix);
+ sBuffer.append(sId);
+
+ rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_CHANGE_ID,
+ sBuffer.makeStringAndClear());
+
+ // export the element
+ // (whitespace because we're not inside paragraphs)
+ SvXMLElementExport aChangeElem(
+ rExport, XML_NAMESPACE_TEXT,
+ bIsCollapsed ? XML_CHANGE :
+ ( bIsStart ? XML_CHANGE_START : XML_CHANGE_END ),
+ sal_True, sal_True);
+ }
+}
+
+void XMLRedlineExport::ExportStartOrEndRedline(
+ const Reference<XTextContent> & rContent,
+ sal_Bool bStart)
+{
+ Reference<XPropertySet> xPropSet(rContent, uno::UNO_QUERY);
+ if (xPropSet.is())
+ {
+ ExportStartOrEndRedline(xPropSet, bStart);
+ }
+ else
+ {
+ DBG_ERROR("XPropertySet expected");
+ }
+}
+
+void XMLRedlineExport::ExportStartOrEndRedline(
+ const Reference<XTextSection> & rSection,
+ sal_Bool bStart)
+{
+ Reference<XPropertySet> xPropSet(rSection, uno::UNO_QUERY);
+ if (xPropSet.is())
+ {
+ ExportStartOrEndRedline(xPropSet, bStart);
+ }
+ else
+ {
+ DBG_ERROR("XPropertySet expected");
+ }
+}
+
+void XMLRedlineExport::WriteComment(const OUString& rComment)
+{
+ if (rComment.getLength() > 0)
+ {
+ // iterate over all string-pieces separated by return (0x0a) and
+ // put each inside a paragraph element.
+ SvXMLTokenEnumerator aEnumerator(rComment, sal_Char(0x0a));
+ OUString aSubString;
+ while (aEnumerator.getNextToken(aSubString))
+ {
+ SvXMLElementExport aParagraph(
+ rExport, XML_NAMESPACE_TEXT, XML_P, sal_True, sal_False);
+ rExport.Characters(aSubString);
+ }
+ }
+}