/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
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;
void XMLRedlineExport::ExportChange( const Reference<XPropertySet> & rPropSet, bool bAutoStyle)
{ if (bAutoStyle)
{ // For the headers/footers, we have to collect the autostyles // here. For the general case, however, it's better to collect // 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 != nullptr)
ExportChangeAutoStyle(rPropSet);
} else
{
ExportChangeInline(rPropSet);
}
}
void XMLRedlineExport::ExportChangesList( const Reference<XText> & rText, 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()) return;
ChangesVectorType& rChangesList = aFind->second;
// export only if changes are found if (rChangesList.empty()) return;
// changes container element
SvXMLElementExport aChanges(rExport, XML_NAMESPACE_TEXT,
XML_TRACKED_CHANGES, true, true);
// iterate over changes list for (autoconst& change : rChangesList)
{
ExportChangedRegion(change);
} // 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())
{ auto rv = aChangeMap.emplace(std::piecewise_construct, std::forward_as_tuple(rText), std::forward_as_tuple());
pCurrentChangesList = &rv.first->second;
} else
pCurrentChangesList = &aIter->second;
} else
{ // don't record changes
SetCurrentXText();
}
}
void XMLRedlineExport::ExportChangesListElements()
{ // get redlines (aka tracked changes) from the model
Reference<XRedlinesSupplier> xSupplier(rExport.GetModel(), uno::UNO_QUERY); if (!xSupplier.is()) return;
// only export if we have redlines or attributes if ( !(aEnumAccess->hasElements() || bEnabled) ) return;
// 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, true, 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(u"IsInHeaderFooter"_ustr); if (! *o3tl::doAccess<bool>(aAny))
{ // 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 (nullptr != pCurrentChangesList)
{ // put redline in list if it's collapsed or the redline start
Any aIsStart = rPropSet->getPropertyValue(u"IsStart"_ustr);
Any aIsCollapsed = rPropSet->getPropertyValue(u"IsCollapsed"_ustr);
if ( *o3tl::doAccess<bool>(aIsStart) ||
*o3tl::doAccess<bool>(aIsCollapsed) )
pCurrentChangesList->push_back(rPropSet);
}
// get XText for export of redline auto styles
Any aAny = rPropSet->getPropertyValue(u"RedlineText"_ustr);
Reference<XText> xText;
aAny >>= xText; if (xText.is())
{ // export the auto styles
rExport.GetTextParagraphExport()->collectTextAutoStyles(xText);
}
SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion(); if (eVersion & SvtSaveOptions::ODFSVER_EXTENDED)
{ // See if the format redline has an autostyle for old direct formatting: if so, export that as // an autostyle.
aAny = rPropSet->getPropertyValue(u"RedlineAutoFormat"_ustr);
uno::Reference<beans::XPropertySet> xAutoFormat;
aAny >>= xAutoFormat; if (xAutoFormat.is())
{ // Check for parent style when declaring the automatic style.
rExport.GetTextParagraphExport()->Add(XmlStyleFamily::TEXT_TEXT, xAutoFormat, /*aAddStates=*/{}, /*bCheckParent=*/true);
}
}
}
void XMLRedlineExport::ExportChangesListAutoStyles()
{ // get redlines (aka tracked changes) from the model
Reference<XRedlinesSupplier> xSupplier(rExport.GetModel(), uno::UNO_QUERY); if (!xSupplier.is()) return;
// only export if we actually have redlines if (!aEnumAccess->hasElements()) return;
// 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(u"IsInHeaderFooter"_ustr); if (! *o3tl::doAccess<bool>(aAny))
{
ExportChangeAutoStyle(xPropSet);
}
}
}
}
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, false, false);
}
}
// export change region element
SvXMLElementExport aChangedRegion(rExport, XML_NAMESPACE_TEXT,
XML_CHANGED_REGION, true, true);
// scope for (first) change element
{
aAny = rPropSet->getPropertyValue(u"RedlineType"_ustr);
OUString sType;
aAny >>= sType;
SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion(); if (eVersion & SvtSaveOptions::ODFSVER_EXTENDED)
{ // See if the format redline has an autostyle for old direct formatting: if so, refer to the // already exported autostyle.
aAny = rPropSet->getPropertyValue(u"RedlineAutoFormat"_ustr);
uno::Reference<beans::XPropertySet> xAutoStyle;
aAny >>= xAutoStyle; if (xAutoStyle.is())
{ bool bIsUICharStyle; bool bHasAutoStyle;
OUString aParentName;
uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xAutoStyle->getPropertySetInfo(); if (xPropSetInfo->hasPropertyByName("CharStyleName"))
{ // Consider parent style when referring to the declared automatic style.
xAutoStyle->getPropertyValue("CharStyleName") >>= aParentName;
}
OUString sStyle = rExport.GetTextParagraphExport()->FindTextStyle(
xAutoStyle, bIsUICharStyle, bHasAutoStyle, /*pAddState=*/nullptr, &aParentName); if (!sStyle.isEmpty())
{
rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_STYLE_NAME, rExport.EncodeStyleName(sStyle));
}
}
}
// get XText from the redline and export (if the XText exists)
aAny = rPropSet->getPropertyValue(u"RedlineText"_ustr);
Reference<XText> xText;
aAny >>= xText; // Avoid double export for format-on-delete: no write on format, only on delete. if (xText.is() && sType == u"Delete")
{
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 only be two levels // deep. Here we check for the second level.
aAny = rPropSet->getPropertyValue(u"RedlineSuccessorData"_ustr);
Sequence<PropertyValue> aSuccessorData;
aAny >>= aSuccessorData;
// if we actually got a hierarchical change, make element and // process change info if (aSuccessorData.hasElements())
{ // Look up the type of the change. In practice this is always insert or delete, other types // can't have additional redlines on top of them.
OUString sType;
comphelper::SequenceAsHashMap aMap(aSuccessorData); auto it = aMap.find("RedlineType"); if (it != aMap.end())
{
it->second >>= sType;
}
SvXMLElementExport aSecondChangeElem(
rExport, XML_NAMESPACE_TEXT, ConvertTypeName(sType), true, true);
ExportChangeInfo(aSuccessorData);
it = aMap.find("RedlineText"); if (it != aMap.end())
{ // Delete has its own content outside the body text: export it here.
uno::Reference<text::XText> xText;
it->second >>= xText; if (xText.is())
{
rExport.GetTextParagraphExport()->exportText(xText);
}
}
} // else: no hierarchical change
}
// get appropriate (start or end) property
Any aAny; try
{
aAny = rPropSet->getPropertyValue(bStart ? u"StartRedline"_ustr : u"EndRedline"_ustr);
} catch(const UnknownPropertyException&)
{ // If we don't have the property, there's nothing to do. return;
}
void XMLRedlineExport::WriteComment(std::u16string_view rComment)
{ if (rComment.empty()) return;
// iterate over all string-pieces separated by return (0x0a) and // put each inside a paragraph element.
SvXMLTokenEnumerator aEnumerator(rComment, char(0x0a));
std::u16string_view aSubString; while (aEnumerator.getNextToken(aSubString))
{
SvXMLElementExport aParagraph(
rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
rExport.Characters(OUString(aSubString));
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.