Change-Id: I99eb764842838b1481483b69d9183e52834e1298 Reviewed-on: https://gerrit.libreoffice.org/43629 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
1633 lines
71 KiB
C++
1633 lines
71 KiB
C++
/* -*- 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 .
|
|
*/
|
|
|
|
#include <editsh.hxx>
|
|
|
|
#include <com/sun/star/container/XEnumerationAccess.hpp>
|
|
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
|
|
#include <com/sun/star/document/XActionLockable.hpp>
|
|
#include <com/sun/star/drawing/FillStyle.hpp>
|
|
#include <com/sun/star/drawing/HomogenMatrix3.hpp>
|
|
#include <com/sun/star/drawing/LineStyle.hpp>
|
|
#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
|
|
#include <com/sun/star/lang/XServiceInfo.hpp>
|
|
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
|
|
#include <com/sun/star/text/RelOrientation.hpp>
|
|
#include <com/sun/star/text/TextContentAnchorType.hpp>
|
|
#include <com/sun/star/text/VertOrientation.hpp>
|
|
#include <com/sun/star/text/WrapTextMode.hpp>
|
|
#include <com/sun/star/text/XTextContent.hpp>
|
|
#include <com/sun/star/text/XTextField.hpp>
|
|
#include <com/sun/star/text/XTextRange.hpp>
|
|
#include <com/sun/star/xml/crypto/SEInitializer.hpp>
|
|
#include <com/sun/star/rdf/XMetadatable.hpp>
|
|
#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
|
|
#include <com/sun/star/security/XCertificate.hpp>
|
|
|
|
#include <basegfx/matrix/b2dhommatrix.hxx>
|
|
#include <comphelper/propertysequence.hxx>
|
|
#include <comphelper/propertyvalue.hxx>
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <comphelper/sequence.hxx>
|
|
#include <comphelper/scopeguard.hxx>
|
|
#include <comphelper/string.hxx>
|
|
#include <editeng/formatbreakitem.hxx>
|
|
#include <editeng/unoprnms.hxx>
|
|
#include <sfx2/classificationhelper.hxx>
|
|
#include <svl/cryptosign.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
|
|
#include <hintids.hxx>
|
|
#include <doc.hxx>
|
|
#include <IDocumentUndoRedo.hxx>
|
|
#include <edimp.hxx>
|
|
#include <ndtxt.hxx>
|
|
#include <paratr.hxx>
|
|
#include <fmtpdsc.hxx>
|
|
#include <viewopt.hxx>
|
|
#include <SwRewriter.hxx>
|
|
#include <numrule.hxx>
|
|
#include <swundo.hxx>
|
|
#include <docary.hxx>
|
|
#include <docsh.hxx>
|
|
#include <unoprnms.hxx>
|
|
#include <rootfrm.hxx>
|
|
#include <pagefrm.hxx>
|
|
#include <rdfhelper.hxx>
|
|
#include <sfx2/watermarkitem.hxx>
|
|
|
|
#include <unoparagraph.hxx>
|
|
#include <unotextrange.hxx>
|
|
#include <cppuhelper/bootstrap.hxx>
|
|
#include <modeltoviewhelper.hxx>
|
|
#include <strings.hrc>
|
|
#include <undobj.hxx>
|
|
#include <UndoParagraphSignature.hxx>
|
|
#include <txtatr.hxx>
|
|
|
|
#include <officecfg/Office/Common.hxx>
|
|
#include <com/sun/star/beans/PropertyAttribute.hpp>
|
|
|
|
#define WATERMARK_NAME "PowerPlusWaterMarkObject"
|
|
|
|
namespace
|
|
{
|
|
static const OUString MetaFilename("bails.rdf");
|
|
static const OUString MetaNS("urn:bails");
|
|
static const OUString ParagraphSignatureRDFName = "loext:paragraph:signature";
|
|
static const OUString ParagraphSignatureUsageRDFName = "loext:paragraph:signature:usage";
|
|
static const OUString ParagraphClassificationNameRDFName = "loext:paragraph:classification:name";
|
|
static const OUString ParagraphClassificationValueRDFName = "loext:paragraph:classification:value";
|
|
static const OUString MetadataFieldServiceName = "com.sun.star.text.textfield.MetadataField";
|
|
static const OUString DocInfoServiceName = "com.sun.star.text.TextField.DocInfo.Custom";
|
|
|
|
/// Find all page styles which are currently used in the document.
|
|
std::vector<OUString> lcl_getUsedPageStyles(SwViewShell const * pShell)
|
|
{
|
|
std::vector<OUString> aReturn;
|
|
|
|
SwRootFrame* pLayout = pShell->GetLayout();
|
|
for (SwFrame* pFrame = pLayout->GetLower(); pFrame; pFrame = pFrame->GetNext())
|
|
{
|
|
SwPageFrame* pPage = static_cast<SwPageFrame*>(pFrame);
|
|
if (const SwPageDesc *pDesc = pPage->FindPageDesc())
|
|
aReturn.push_back(pDesc->GetName());
|
|
}
|
|
|
|
return aReturn;
|
|
}
|
|
|
|
/// Search for a field named rFieldName of type rServiceName in xText and return it.
|
|
uno::Reference<text::XTextField> lcl_findClassificationField(const uno::Reference<text::XText>& xText, const OUString& rServiceName, const OUString& rFieldName)
|
|
{
|
|
uno::Reference<text::XTextField> xField;
|
|
uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xText, uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
|
|
while (xParagraphs->hasMoreElements())
|
|
{
|
|
uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
|
|
while (xTextPortions->hasMoreElements())
|
|
{
|
|
uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
|
|
OUString aTextPortionType;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
|
|
if (aTextPortionType != UNO_NAME_TEXT_FIELD)
|
|
continue;
|
|
|
|
uno::Reference<lang::XServiceInfo> xTextField;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
|
|
if (!xTextField->supportsService(rServiceName))
|
|
continue;
|
|
|
|
OUString aName;
|
|
uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
|
|
xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
|
|
if (aName == rFieldName)
|
|
{
|
|
xField = uno::Reference<text::XTextField>(xTextField, uno::UNO_QUERY);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return xField;
|
|
}
|
|
|
|
/// Search for a field named rFieldName of type rServiceName in xText and return true iff found.
|
|
bool lcl_hasField(const uno::Reference<text::XText>& xText, const OUString& rServiceName, const OUString& rFieldName)
|
|
{
|
|
return lcl_findClassificationField(xText, rServiceName, rFieldName).is();
|
|
}
|
|
|
|
/// Search for a frame with WATERMARK_NAME in name of type rServiceName in xText. Returns found name in rShapeName.
|
|
uno::Reference<drawing::XShape> lcl_getWatermark(const uno::Reference<text::XText>& xText, const OUString& rServiceName, OUString& rShapeName)
|
|
{
|
|
uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xText, uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
|
|
while (xParagraphs->hasMoreElements())
|
|
{
|
|
uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
|
|
if (!xTextPortionEnumerationAccess.is())
|
|
continue;
|
|
|
|
uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
|
|
while (xTextPortions->hasMoreElements())
|
|
{
|
|
uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
|
|
OUString aTextPortionType;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
|
|
if (aTextPortionType != "Frame")
|
|
continue;
|
|
|
|
uno::Reference<container::XContentEnumerationAccess> xContentEnumerationAccess(xTextPortion, uno::UNO_QUERY);
|
|
if (!xContentEnumerationAccess.is())
|
|
continue;
|
|
|
|
uno::Reference<container::XEnumeration> xEnumeration = xContentEnumerationAccess->createContentEnumeration("com.sun.star.text.TextContent");
|
|
if (!xEnumeration->hasMoreElements())
|
|
continue;
|
|
|
|
uno::Reference<lang::XServiceInfo> xWatermark(xEnumeration->nextElement(), uno::UNO_QUERY);
|
|
if (!xWatermark->supportsService(rServiceName))
|
|
continue;
|
|
|
|
uno::Reference<container::XNamed> xNamed(xWatermark, uno::UNO_QUERY);
|
|
|
|
if (!xNamed->getName().match(WATERMARK_NAME))
|
|
continue;
|
|
|
|
rShapeName = xNamed->getName();
|
|
|
|
uno::Reference<drawing::XShape> xShape(xWatermark, uno::UNO_QUERY);
|
|
return xShape;
|
|
}
|
|
}
|
|
|
|
return uno::Reference<drawing::XShape>();
|
|
}
|
|
|
|
/// Extract the text of the paragraph without any of the fields.
|
|
/// TODO: Consider moving to SwTextNode, or extend ModelToViewHelper.
|
|
OString lcl_getParagraphBodyText(const uno::Reference<text::XTextContent>& xText)
|
|
{
|
|
OUStringBuffer strBuf;
|
|
uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xText, uno::UNO_QUERY);
|
|
if (!xTextPortionEnumerationAccess.is())
|
|
return OString();
|
|
|
|
uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
|
|
while (xTextPortions->hasMoreElements())
|
|
{
|
|
uno::Any elem = xTextPortions->nextElement();
|
|
|
|
//TODO: Consider including hidden and conditional texts/portions.
|
|
OUString aTextPortionType;
|
|
uno::Reference<beans::XPropertySet> xPropertySet(elem, uno::UNO_QUERY);
|
|
xPropertySet->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
|
|
if (aTextPortionType == "Text")
|
|
{
|
|
uno::Reference<text::XTextRange> xTextRange(elem, uno::UNO_QUERY);
|
|
if (xTextRange.is())
|
|
strBuf.append(xTextRange->getString());
|
|
}
|
|
}
|
|
|
|
// Cleanup the dummy characters added by fields (which we exclude).
|
|
comphelper::string::remove(strBuf, CH_TXT_ATR_INPUTFIELDSTART);
|
|
comphelper::string::remove(strBuf, CH_TXT_ATR_INPUTFIELDEND);
|
|
comphelper::string::remove(strBuf, CH_TXTATR_BREAKWORD);
|
|
|
|
return strBuf.makeStringAndClear().trim().toUtf8();
|
|
}
|
|
|
|
/// Returns RDF (key, value) pair associated with the field, if any.
|
|
std::pair<OUString, OUString> lcl_getFieldRDF(const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<css::text::XTextField>& xField,
|
|
const OUString& rRDFName)
|
|
{
|
|
const css::uno::Reference<css::rdf::XResource> xSubject(xField, uno::UNO_QUERY);
|
|
std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xSubject);
|
|
const auto it = aStatements.find(rRDFName);
|
|
return it != aStatements.end() ? std::make_pair(it->first, it->second) : std::make_pair(OUString(), OUString());
|
|
}
|
|
|
|
/// Returns true iff the field in question is paragraph signature.
|
|
/// Note: must have associated RDF, since signatures are otherwise just metadata fields.
|
|
bool lcl_IsParagraphSignatureField(const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<css::text::XTextField>& xField)
|
|
{
|
|
return lcl_getFieldRDF(xModel, xField, ParagraphSignatureRDFName).first == ParagraphSignatureRDFName;
|
|
}
|
|
|
|
/// Validate and return validation result and signature field display text.
|
|
std::pair<bool, OUString>
|
|
lcl_MakeParagraphSignatureFieldText(const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<css::text::XTextField>& xField,
|
|
const OString& utf8Text)
|
|
{
|
|
OUString msg = SwResId(STR_INVALID_SIGNATURE);
|
|
bool valid = false;
|
|
|
|
const css::uno::Reference<css::rdf::XResource> xSubject(xField, uno::UNO_QUERY);
|
|
std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xSubject);
|
|
const auto it = aStatements.find(ParagraphSignatureRDFName);
|
|
if (it != aStatements.end())
|
|
{
|
|
const sal_Char* pData = utf8Text.getStr();
|
|
const std::vector<unsigned char> data(pData, pData + utf8Text.getLength());
|
|
|
|
OString encSignature;
|
|
if (it->second.convertToString(&encSignature, RTL_TEXTENCODING_UTF8, 0))
|
|
{
|
|
const std::vector<unsigned char> sig(svl::crypto::DecodeHexString(encSignature));
|
|
SignatureInformation aInfo(0);
|
|
valid = svl::crypto::Signing::Verify(data, false, sig, aInfo);
|
|
valid = valid && aInfo.nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
|
|
|
|
const auto it2 = aStatements.find(ParagraphSignatureUsageRDFName);
|
|
msg = (it2 != aStatements.end() ? (it2->second + ", ") : OUString());
|
|
msg += SwResId(STR_SIGNED_BY) + ": " + aInfo.ouSubject + ", " + aInfo.ouDateTime + ": ";
|
|
if (valid)
|
|
msg += SwResId(STR_VALID);
|
|
else
|
|
msg += SwResId(STR_INVALID);
|
|
}
|
|
}
|
|
|
|
return std::make_pair(valid, msg);
|
|
}
|
|
|
|
/// Creates and inserts Paragraph Signature Metadata field and creates the RDF entry
|
|
uno::Reference<text::XTextField> lcl_InsertParagraphSignature(const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<text::XTextContent>& xParent,
|
|
const OUString& signature,
|
|
const OUString& usage)
|
|
{
|
|
uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
|
|
auto xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
|
|
|
|
// Add the signature at the end.
|
|
xField->attach(xParent->getAnchor()->getEnd());
|
|
|
|
const css::uno::Reference<css::rdf::XResource> xSubject(xField, uno::UNO_QUERY);
|
|
SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphSignatureRDFName, signature);
|
|
SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphSignatureUsageRDFName, usage);
|
|
|
|
return xField;
|
|
}
|
|
|
|
/// Updates the signature field text if changed and returns true only iff updated.
|
|
bool lcl_UpdateParagraphSignatureField(SwDoc* pDoc,
|
|
const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<css::text::XTextField>& xField,
|
|
const OString& utf8Text)
|
|
{
|
|
// Disable undo to avoid introducing noise when we edit the metadata field.
|
|
const bool isUndoEnabled = pDoc->GetIDocumentUndoRedo().DoesUndo();
|
|
pDoc->GetIDocumentUndoRedo().DoUndo(false);
|
|
comphelper::ScopeGuard const g([pDoc, isUndoEnabled] () {
|
|
pDoc->GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
|
|
});
|
|
|
|
const std::pair<bool, OUString> res = lcl_MakeParagraphSignatureFieldText(xModel, xField, utf8Text);
|
|
uno::Reference<css::text::XTextRange> xText(xField, uno::UNO_QUERY);
|
|
const OUString curText = xText->getString();
|
|
if (curText != res.second)
|
|
{
|
|
xText->setString(res.second + " ");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void lcl_RemoveParagraphMetadataField(const uno::Reference<css::text::XTextField>& xField)
|
|
{
|
|
uno::Reference<css::text::XTextContent> xFieldTextContent(xField, uno::UNO_QUERY);
|
|
uno::Reference<css::text::XTextRange> xParagraph(xFieldTextContent->getAnchor());
|
|
uno::Reference<css::text::XText> xParagraphText(xParagraph->getText(), uno::UNO_QUERY);
|
|
xParagraphText->removeTextContent(xFieldTextContent);
|
|
}
|
|
|
|
/// Returns true iff the field in question is paragraph classification.
|
|
/// Note: must have associated RDF, since classifications are otherwise just metadata fields.
|
|
bool lcl_IsParagraphClassificationField(const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<css::text::XTextField>& xField,
|
|
const OUString& sKey = OUString())
|
|
{
|
|
const std::pair<OUString, OUString> rdfPair = lcl_getFieldRDF(xModel, xField, ParagraphClassificationNameRDFName);
|
|
return rdfPair.first == ParagraphClassificationNameRDFName && (sKey.isEmpty() || rdfPair.second == sKey);
|
|
}
|
|
|
|
uno::Reference<text::XTextField> lcl_FindParagraphClassificationField(const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<text::XTextContent>& xParagraph,
|
|
const OUString& sKey = OUString())
|
|
{
|
|
uno::Reference<text::XTextField> xTextField;
|
|
|
|
uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
|
|
if (!xTextPortionEnumerationAccess.is())
|
|
return xTextField;
|
|
|
|
uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
|
|
while (xTextPortions->hasMoreElements())
|
|
{
|
|
uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
|
|
OUString aTextPortionType;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
|
|
if (aTextPortionType != UNO_NAME_TEXT_FIELD)
|
|
continue;
|
|
|
|
uno::Reference<lang::XServiceInfo> xServiceInfo;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xServiceInfo;
|
|
if (!xServiceInfo->supportsService(MetadataFieldServiceName))
|
|
continue;
|
|
|
|
uno::Reference<text::XTextField> xField(xServiceInfo, uno::UNO_QUERY);
|
|
if (lcl_IsParagraphClassificationField(xModel, xField, sKey))
|
|
{
|
|
uno::Reference<css::text::XTextRange> xText(xField, uno::UNO_QUERY);
|
|
xTextField = xField;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return xTextField;
|
|
}
|
|
|
|
/// Creates and inserts Paragraph Classification Metadata field and creates the RDF entry
|
|
uno::Reference<text::XTextField> lcl_InsertParagraphClassification(const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<text::XTextContent>& xParent)
|
|
{
|
|
uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
|
|
auto xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
|
|
|
|
// Add the classification at the start.
|
|
xField->attach(xParent->getAnchor()->getStart());
|
|
return xField;
|
|
}
|
|
|
|
/// Updates the paragraph classification field text if changed and returns true only iff updated.
|
|
bool lcl_UpdateParagraphClassificationField(SwDoc* pDoc,
|
|
const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<css::text::XTextField>& xField,
|
|
const OUString& sKey,
|
|
const OUString& sValue,
|
|
const OUString& sDisplayText)
|
|
{
|
|
// Disable undo to avoid introducing noise when we edit the metadata field.
|
|
const bool isUndoEnabled = pDoc->GetIDocumentUndoRedo().DoesUndo();
|
|
pDoc->GetIDocumentUndoRedo().DoUndo(false);
|
|
comphelper::ScopeGuard const g([pDoc, isUndoEnabled] () {
|
|
pDoc->GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
|
|
});
|
|
|
|
const css::uno::Reference<css::rdf::XResource> xSubject(xField, uno::UNO_QUERY);
|
|
SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphClassificationNameRDFName, sKey);
|
|
SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphClassificationValueRDFName, sValue);
|
|
|
|
uno::Reference<css::text::XTextRange> xText(xField, uno::UNO_QUERY);
|
|
const OUString curDisplayText = xText->getString();
|
|
if (curDisplayText != sDisplayText)
|
|
{
|
|
xText->setString(sDisplayText);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
SwTextFormatColl& SwEditShell::GetDfltTextFormatColl() const
|
|
{
|
|
return *GetDoc()->GetDfltTextFormatColl();
|
|
}
|
|
|
|
sal_uInt16 SwEditShell::GetTextFormatCollCount() const
|
|
{
|
|
return GetDoc()->GetTextFormatColls()->size();
|
|
}
|
|
|
|
SwTextFormatColl& SwEditShell::GetTextFormatColl(sal_uInt16 nFormatColl) const
|
|
{
|
|
return *((*(GetDoc()->GetTextFormatColls()))[nFormatColl]);
|
|
}
|
|
|
|
OUString lcl_getProperty(uno::Reference<beans::XPropertyContainer> const & rxPropertyContainer, const OUString& rName)
|
|
{
|
|
try
|
|
{
|
|
uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
|
|
return xPropertySet->getPropertyValue(rName).get<OUString>();
|
|
}
|
|
catch (const css::uno::Exception&)
|
|
{
|
|
}
|
|
|
|
return OUString();
|
|
}
|
|
|
|
static bool lcl_containsProperty(const uno::Sequence<beans::Property> & rProperties, const OUString& rName)
|
|
{
|
|
return std::find_if(rProperties.begin(), rProperties.end(), [&](const beans::Property& rProperty)
|
|
{
|
|
return rProperty.Name == rName;
|
|
}) != rProperties.end();
|
|
}
|
|
|
|
static void lcl_removeAllProperties(uno::Reference<beans::XPropertyContainer> const & rxPropertyContainer)
|
|
{
|
|
uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
|
|
uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
|
|
|
|
for (const beans::Property& rProperty : aProperties)
|
|
{
|
|
rxPropertyContainer->removeProperty(rProperty.Name);
|
|
}
|
|
}
|
|
|
|
bool addOrInsertDocumentProperty(uno::Reference<beans::XPropertyContainer> const & rxPropertyContainer, OUString const & rsKey, OUString const & rsValue)
|
|
{
|
|
uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
|
|
|
|
try
|
|
{
|
|
if (lcl_containsProperty(xPropertySet->getPropertySetInfo()->getProperties(), rsKey))
|
|
xPropertySet->setPropertyValue(rsKey, uno::makeAny(rsValue));
|
|
else
|
|
rxPropertyContainer->addProperty(rsKey, beans::PropertyAttribute::REMOVABLE, uno::makeAny(rsValue));
|
|
}
|
|
catch (const uno::Exception& /*rException*/)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void insertFieldToDocument(uno::Reference<lang::XMultiServiceFactory> const & rxMultiServiceFactory, uno::Reference<text::XText> const & rxText, OUString const & rsKey)
|
|
{
|
|
if (!lcl_hasField(rxText, DocInfoServiceName, rsKey))
|
|
{
|
|
uno::Reference<beans::XPropertySet> xField(rxMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
|
|
xField->setPropertyValue(UNO_NAME_NAME, uno::makeAny(rsKey));
|
|
uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
|
|
rxText->insertTextContent(rxText->getEnd(), xTextContent, false);
|
|
}
|
|
}
|
|
|
|
void SwEditShell::ApplyAdvancedClassification(std::vector<svx::ClassificationResult> const & rResults)
|
|
{
|
|
SwDocShell* pDocShell = GetDoc()->GetDocShell();
|
|
if (!pDocShell)
|
|
return;
|
|
|
|
uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
|
|
uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamilies(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
|
|
|
|
uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
|
|
|
|
uno::Reference<document::XDocumentProperties> xDocumentProperties = SfxObjectShell::Current()->getDocProperties();
|
|
|
|
// First, we need to remove the old ones.
|
|
//TODO: we should get this as a param, since we pass it to the dialog.
|
|
const OUString sPolicy = SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
|
|
const std::vector<OUString> aUsedPageStyles = lcl_getUsedPageStyles(this);
|
|
for (const OUString& rPageStyleName : aUsedPageStyles)
|
|
{
|
|
uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
|
|
|
|
// HEADER
|
|
bool bHeaderIsOn = false;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
|
|
uno::Reference<text::XText> xHeaderText;
|
|
if (bHeaderIsOn)
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
|
|
|
|
// FOOTER
|
|
bool bFooterIsOn = false;
|
|
xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
|
|
uno::Reference<text::XText> xFooterText;
|
|
if (bFooterIsOn)
|
|
xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
|
|
|
|
sal_Int32 nTextNumber = 1;
|
|
OUString sKey;
|
|
for (svx::ClassificationResult const & rResult : CollectAdvancedClassification())
|
|
{
|
|
sKey = "";
|
|
switch(rResult.meType)
|
|
{
|
|
case svx::ClassificationType::TEXT:
|
|
{
|
|
sKey = sPolicy + "Marking:Text:" + OUString::number(nTextNumber++);
|
|
}
|
|
break;
|
|
|
|
case svx::ClassificationType::CATEGORY:
|
|
{
|
|
sKey = sPolicy + "BusinessAuthorizationCategory:Name";
|
|
}
|
|
break;
|
|
|
|
case svx::ClassificationType::MARKING:
|
|
{
|
|
sKey = sPolicy + "Extension:Marking";
|
|
}
|
|
break;
|
|
|
|
case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
|
|
{
|
|
sKey = sPolicy + "Extension:IntellectualPropertyPart";
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!sKey.isEmpty())
|
|
{
|
|
uno::Reference<css::text::XTextField> xField = lcl_findClassificationField(xHeaderText, DocInfoServiceName, sKey);
|
|
if (xField.is())
|
|
{
|
|
if (xHeaderText.is())
|
|
xHeaderText->removeTextContent(xField);
|
|
|
|
if (xFooterText.is())
|
|
xFooterText->removeTextContent(xField);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear properties
|
|
uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
|
|
lcl_removeAllProperties(xPropertyContainer);
|
|
|
|
SfxClassificationHelper aHelper(xDocumentProperties);
|
|
|
|
// Apply properties from the BA policy
|
|
for (svx::ClassificationResult const & rResult : rResults)
|
|
{
|
|
if (rResult.meType == svx::ClassificationType::CATEGORY)
|
|
{
|
|
aHelper.SetBACName(rResult.msString, SfxClassificationHelper::getPolicyType());
|
|
}
|
|
}
|
|
|
|
for (const OUString& rPageStyleName : aUsedPageStyles)
|
|
{
|
|
uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
|
|
|
|
// HEADER
|
|
bool bHeaderIsOn = false;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
|
|
if (!bHeaderIsOn)
|
|
xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::makeAny(true));
|
|
uno::Reference<text::XText> xHeaderText;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
|
|
|
|
// FOOTER
|
|
bool bFooterIsOn = false;
|
|
xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
|
|
if (!bFooterIsOn)
|
|
xPageStyle->setPropertyValue(UNO_NAME_FOOTER_IS_ON, uno::makeAny(true));
|
|
uno::Reference<text::XText> xFooterText;
|
|
xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
|
|
|
|
sal_Int32 nTextNumber = 1;
|
|
|
|
for (svx::ClassificationResult const & rResult : rResults)
|
|
{
|
|
switch(rResult.meType)
|
|
{
|
|
case svx::ClassificationType::TEXT:
|
|
{
|
|
OUString sKey = sPolicy + "Marking:Text:" + OUString::number(nTextNumber);
|
|
nTextNumber++;
|
|
|
|
addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msString);
|
|
insertFieldToDocument(xMultiServiceFactory, xHeaderText, sKey);
|
|
insertFieldToDocument(xMultiServiceFactory, xFooterText, sKey);
|
|
}
|
|
break;
|
|
|
|
case svx::ClassificationType::CATEGORY:
|
|
{
|
|
OUString sKey = sPolicy + "BusinessAuthorizationCategory:Name";
|
|
insertFieldToDocument(xMultiServiceFactory, xHeaderText, sKey);
|
|
insertFieldToDocument(xMultiServiceFactory, xFooterText, sKey);
|
|
}
|
|
break;
|
|
|
|
case svx::ClassificationType::MARKING:
|
|
{
|
|
OUString sKey = sPolicy + "Extension:Marking";
|
|
addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msString);
|
|
insertFieldToDocument(xMultiServiceFactory, xHeaderText, sKey);
|
|
insertFieldToDocument(xMultiServiceFactory, xFooterText, sKey);
|
|
}
|
|
break;
|
|
|
|
case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
|
|
{
|
|
OUString sKey = sPolicy + "Extension:IntellectualPropertyPart";
|
|
addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msString);
|
|
insertFieldToDocument(xMultiServiceFactory, xHeaderText, sKey);
|
|
insertFieldToDocument(xMultiServiceFactory, xFooterText, sKey);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<svx::ClassificationResult> SwEditShell::CollectAdvancedClassification()
|
|
{
|
|
std::vector<svx::ClassificationResult> aResult;
|
|
|
|
SwDocShell* pDocShell = GetDoc()->GetDocShell();
|
|
|
|
if (!pDocShell)
|
|
return aResult;
|
|
|
|
uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
|
|
uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamilies(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
|
|
|
|
std::vector<OUString> aPageStyles = lcl_getUsedPageStyles(this);
|
|
OUString aPageStyleString = aPageStyles.back();
|
|
uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(aPageStyleString), uno::UNO_QUERY);
|
|
|
|
bool bHeaderIsOn = false;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
|
|
if (!bHeaderIsOn)
|
|
return aResult;
|
|
|
|
uno::Reference<text::XText> xHeaderText;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
|
|
|
|
uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xHeaderText, uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
|
|
|
|
uno::Reference<document::XDocumentProperties> xDocumentProperties = SfxObjectShell::Current()->getDocProperties();
|
|
uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
|
|
|
|
OUString sPolicy = SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
|
|
sal_Int32 nParagraph = 1;
|
|
|
|
while (xParagraphs->hasMoreElements())
|
|
{
|
|
uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
|
|
while (xTextPortions->hasMoreElements())
|
|
{
|
|
uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
|
|
OUString aTextPortionType;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
|
|
if (aTextPortionType != UNO_NAME_TEXT_FIELD)
|
|
continue;
|
|
|
|
uno::Reference<lang::XServiceInfo> xTextField;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
|
|
if (!xTextField->supportsService(DocInfoServiceName))
|
|
continue;
|
|
|
|
OUString aName;
|
|
uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
|
|
xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
|
|
if (aName.startsWith(sPolicy + "Marking:Text:"))
|
|
{
|
|
const OUString aValue = lcl_getProperty(xPropertyContainer, aName);
|
|
if (!aValue.isEmpty())
|
|
aResult.push_back({ svx::ClassificationType::TEXT, aValue, nParagraph });
|
|
}
|
|
else if (aName.startsWith(sPolicy + "BusinessAuthorizationCategory:Name"))
|
|
{
|
|
const OUString aValue = lcl_getProperty(xPropertyContainer, aName);
|
|
if (!aValue.isEmpty())
|
|
aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, nParagraph });
|
|
}
|
|
else if (aName.startsWith(sPolicy + "Extension:Marking"))
|
|
{
|
|
const OUString aValue = lcl_getProperty(xPropertyContainer, aName);
|
|
if (!aValue.isEmpty())
|
|
aResult.push_back({ svx::ClassificationType::MARKING, aValue, nParagraph });
|
|
}
|
|
else if (aName.startsWith(sPolicy + "Extension:IntellectualPropertyPart"))
|
|
{
|
|
const OUString aValue = lcl_getProperty(xPropertyContainer, aName);
|
|
if (!aValue.isEmpty())
|
|
aResult.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, aValue, nParagraph });
|
|
}
|
|
}
|
|
++nParagraph;
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
void SwEditShell::SetClassification(const OUString& rName, SfxClassificationPolicyType eType)
|
|
{
|
|
SwDocShell* pDocShell = GetDoc()->GetDocShell();
|
|
if (!pDocShell)
|
|
return;
|
|
|
|
SfxClassificationHelper aHelper(pDocShell->getDocProperties());
|
|
|
|
const bool bHadWatermark = !aHelper.GetDocumentWatermark().isEmpty();
|
|
|
|
// This updates the infobar as well.
|
|
aHelper.SetBACName(rName, eType);
|
|
|
|
bool bHeaderIsNeeded = aHelper.HasDocumentHeader();
|
|
bool bFooterIsNeeded = aHelper.HasDocumentFooter();
|
|
OUString aWatermark = aHelper.GetDocumentWatermark();
|
|
bool bWatermarkIsNeeded = !aWatermark.isEmpty();
|
|
|
|
if (!bHeaderIsNeeded && !bFooterIsNeeded && !bWatermarkIsNeeded && !bHadWatermark)
|
|
return;
|
|
|
|
uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
|
|
uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamilies(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
|
|
uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
|
|
|
|
for (const OUString& rPageStyleName : aStyles)
|
|
{
|
|
uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
|
|
uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
|
|
|
|
if (bHeaderIsNeeded || bWatermarkIsNeeded || bHadWatermark)
|
|
{
|
|
// If the header is off, turn it on.
|
|
bool bHeaderIsOn = false;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
|
|
if (!bHeaderIsOn)
|
|
xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::makeAny(true));
|
|
|
|
// If the header already contains a document header field, no need to do anything.
|
|
uno::Reference<text::XText> xHeaderText;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
|
|
|
|
if (bHeaderIsNeeded)
|
|
{
|
|
if (!lcl_hasField(xHeaderText, DocInfoServiceName, SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCHEADER()))
|
|
{
|
|
// Append a field to the end of the header text.
|
|
uno::Reference<beans::XPropertySet> xField(xMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
|
|
xField->setPropertyValue(UNO_NAME_NAME, uno::makeAny(SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCHEADER()));
|
|
uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
|
|
xHeaderText->insertTextContent(xHeaderText->getEnd(), xTextContent, /*bAbsorb=*/false);
|
|
}
|
|
}
|
|
|
|
SfxWatermarkItem aWatermarkItem;
|
|
aWatermarkItem.SetText(aWatermark);
|
|
SetWatermark(aWatermarkItem);
|
|
}
|
|
|
|
if (bFooterIsNeeded)
|
|
{
|
|
// If the footer is off, turn it on.
|
|
bool bFooterIsOn = false;
|
|
xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
|
|
if (!bFooterIsOn)
|
|
xPageStyle->setPropertyValue(UNO_NAME_FOOTER_IS_ON, uno::makeAny(true));
|
|
|
|
// If the footer already contains a document header field, no need to do anything.
|
|
uno::Reference<text::XText> xFooterText;
|
|
xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
|
|
static OUString sFooter = SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCFOOTER();
|
|
if (!lcl_hasField(xFooterText, DocInfoServiceName, sFooter))
|
|
{
|
|
// Append a field to the end of the footer text.
|
|
uno::Reference<beans::XPropertySet> xField(xMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
|
|
xField->setPropertyValue(UNO_NAME_NAME, uno::makeAny(sFooter));
|
|
uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
|
|
xFooterText->insertTextContent(xFooterText->getEnd(), xTextContent, /*bAbsorb=*/false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SwEditShell::ApplyParagraphClassification(std::vector<svx::ClassificationResult> aResults)
|
|
{
|
|
SwDocShell* pDocShell = GetDoc()->GetDocShell();
|
|
if (!pDocShell)
|
|
return;
|
|
|
|
SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
|
|
if (pNode == nullptr)
|
|
return;
|
|
|
|
uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(*pNode->GetDoc(), pNode);
|
|
uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
|
|
uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
|
|
|
|
const OUString sPolicy = SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
|
|
|
|
// Prevent recursive validation since this is triggered on node updates, which we do below.
|
|
const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
|
|
comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
|
|
SetParagraphSignatureValidation(bOldValidationFlag);
|
|
});
|
|
|
|
// Remove all paragraph classification fields.
|
|
for (;;)
|
|
{
|
|
uno::Reference<text::XTextField> xTextField = lcl_FindParagraphClassificationField(xModel, xParent);
|
|
if (!xTextField.is())
|
|
break;
|
|
lcl_RemoveParagraphMetadataField(xTextField);
|
|
}
|
|
|
|
// Since we always insert at the start of the paragraph,
|
|
// need to insert in reverse order.
|
|
std::reverse(aResults.begin(), aResults.end());
|
|
sal_Int32 nTextNumber = 1;
|
|
for (size_t nIndex = 0; nIndex < aResults.size(); ++nIndex)
|
|
{
|
|
const svx::ClassificationResult& rResult = aResults[nIndex];
|
|
const bool isLast = nIndex == 0;
|
|
const bool isFirst = nIndex == aResults.size() - 1;
|
|
OUString sKey;
|
|
switch(rResult.meType)
|
|
{
|
|
case svx::ClassificationType::TEXT:
|
|
{
|
|
sKey = sPolicy + "Marking:Text:" + OUString::number(nTextNumber++);
|
|
}
|
|
break;
|
|
|
|
case svx::ClassificationType::CATEGORY:
|
|
{
|
|
sKey = sPolicy + "BusinessAuthorizationCategory:Name";
|
|
}
|
|
break;
|
|
|
|
case svx::ClassificationType::MARKING:
|
|
{
|
|
sKey = sPolicy + "Extension:Marking";
|
|
}
|
|
break;
|
|
|
|
case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
|
|
{
|
|
sKey = sPolicy + "Extension:IntellectualPropertyPart";
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uno::Reference<text::XTextField> xTextField = lcl_InsertParagraphClassification(xModel, xParent);
|
|
const OUString sValue = rResult.msString;
|
|
OUString sDisplayText = (isFirst ? ("(" + sValue) : sValue);
|
|
if (isLast)
|
|
sDisplayText += ")";
|
|
lcl_UpdateParagraphClassificationField(GetDoc(), xModel, xTextField, sKey, sValue, sDisplayText);
|
|
}
|
|
}
|
|
|
|
std::vector<svx::ClassificationResult> SwEditShell::CollectParagraphClassification()
|
|
{
|
|
std::vector<svx::ClassificationResult> aResult;
|
|
|
|
SwDocShell* pDocShell = GetDoc()->GetDocShell();
|
|
if (!pDocShell)
|
|
return aResult;
|
|
|
|
SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
|
|
if (pNode == nullptr)
|
|
return aResult;
|
|
|
|
uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(*pNode->GetDoc(), pNode);
|
|
uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
|
|
uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
|
|
|
|
uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParent, uno::UNO_QUERY);
|
|
if (!xTextPortionEnumerationAccess.is())
|
|
return aResult;
|
|
|
|
uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
|
|
|
|
const OUString sPolicy = SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
|
|
const sal_Int32 nParagraph = 1;
|
|
|
|
while (xTextPortions->hasMoreElements())
|
|
{
|
|
uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
|
|
OUString aTextPortionType;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
|
|
if (aTextPortionType != UNO_NAME_TEXT_FIELD)
|
|
continue;
|
|
|
|
uno::Reference<lang::XServiceInfo> xField;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xField;
|
|
if (!xField->supportsService(MetadataFieldServiceName))
|
|
continue;
|
|
|
|
uno::Reference<text::XTextField> xTextField(xField, uno::UNO_QUERY);
|
|
const std::pair<OUString, OUString> rdfNamePair = lcl_getFieldRDF(xModel, xTextField, ParagraphClassificationNameRDFName);
|
|
const std::pair<OUString, OUString> rdfValuePair = lcl_getFieldRDF(xModel, xTextField, ParagraphClassificationValueRDFName);
|
|
|
|
uno::Reference<text::XTextRange> xTextRange(xField, uno::UNO_QUERY);
|
|
const OUString aName = rdfNamePair.second;
|
|
const OUString aValue = rdfValuePair.second;
|
|
if (aName.startsWith(sPolicy + "Marking:Text:"))
|
|
{
|
|
aResult.push_back({ svx::ClassificationType::TEXT, aValue, nParagraph });
|
|
}
|
|
else if (aName.startsWith(sPolicy + "BusinessAuthorizationCategory:Name"))
|
|
{
|
|
aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, nParagraph });
|
|
}
|
|
else if (aName.startsWith(sPolicy + "Extension:Marking"))
|
|
{
|
|
aResult.push_back({ svx::ClassificationType::MARKING, aValue, nParagraph });
|
|
}
|
|
else if (aName.startsWith(sPolicy + "Extension:IntellectualPropertyPart"))
|
|
{
|
|
aResult.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, xTextRange->getString(), nParagraph });
|
|
}
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
sal_Int16 lcl_GetAngle(const drawing::HomogenMatrix3& rMatrix)
|
|
{
|
|
basegfx::B2DHomMatrix aTransformation;
|
|
basegfx::B2DTuple aScale;
|
|
basegfx::B2DTuple aTranslate;
|
|
double fRotate = 0;
|
|
double fShear = 0;
|
|
|
|
aTransformation.set(0, 0, rMatrix.Line1.Column1);
|
|
aTransformation.set(0, 1, rMatrix.Line1.Column2);
|
|
aTransformation.set(0, 2, rMatrix.Line1.Column3);
|
|
aTransformation.set(1, 0, rMatrix.Line2.Column1);
|
|
aTransformation.set(1, 1, rMatrix.Line2.Column2);
|
|
aTransformation.set(1, 2, rMatrix.Line2.Column3);
|
|
aTransformation.set(2, 0, rMatrix.Line3.Column1);
|
|
aTransformation.set(2, 1, rMatrix.Line3.Column2);
|
|
aTransformation.set(2, 2, rMatrix.Line3.Column3);
|
|
|
|
aTransformation.decompose(aScale, aTranslate, fRotate, fShear);
|
|
sal_Int16 nDeg = round(basegfx::rad2deg(fRotate));
|
|
return nDeg < 0 ? round(nDeg) * -1 : round(360.0 - nDeg);
|
|
}
|
|
|
|
SfxWatermarkItem SwEditShell::GetWatermark()
|
|
{
|
|
SwDocShell* pDocShell = GetDoc()->GetDocShell();
|
|
if (!pDocShell)
|
|
return SfxWatermarkItem();
|
|
|
|
uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
|
|
uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamilies(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
|
|
std::vector<OUString> aUsedPageStyles = lcl_getUsedPageStyles(this);
|
|
for (const OUString& rPageStyleName : aUsedPageStyles)
|
|
{
|
|
uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
|
|
uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
|
|
|
|
bool bHeaderIsOn = false;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
|
|
if (!bHeaderIsOn)
|
|
return SfxWatermarkItem();
|
|
|
|
uno::Reference<text::XText> xHeaderText;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
|
|
|
|
OUString aShapeServiceName = "com.sun.star.drawing.CustomShape";
|
|
OUString sWatermark = "";
|
|
uno::Reference<drawing::XShape> xWatermark = lcl_getWatermark(xHeaderText, aShapeServiceName, sWatermark);
|
|
|
|
if (xWatermark.is())
|
|
{
|
|
SfxWatermarkItem aItem;
|
|
uno::Reference<text::XTextRange> xTextRange(xWatermark, uno::UNO_QUERY);
|
|
uno::Reference<beans::XPropertySet> xPropertySet(xWatermark, uno::UNO_QUERY);
|
|
sal_uInt32 nColor;
|
|
sal_Int16 nTransparency;
|
|
OUString aFont;
|
|
drawing::HomogenMatrix3 aMatrix;
|
|
|
|
aItem.SetText(xTextRange->getString());
|
|
|
|
if (xPropertySet->getPropertyValue(UNO_NAME_CHAR_FONT_NAME) >>= aFont)
|
|
aItem.SetFont(aFont);
|
|
if (xPropertySet->getPropertyValue(UNO_NAME_FILLCOLOR) >>= nColor)
|
|
aItem.SetColor(nColor);
|
|
if (xPropertySet->getPropertyValue("Transformation") >>= aMatrix)
|
|
aItem.SetAngle(lcl_GetAngle(aMatrix));
|
|
if (xPropertySet->getPropertyValue(UNO_NAME_FILL_TRANSPARENCE) >>= nTransparency)
|
|
aItem.SetTransparency(nTransparency);
|
|
|
|
return aItem;
|
|
}
|
|
}
|
|
return SfxWatermarkItem();
|
|
}
|
|
|
|
void lcl_placeWatermarkInHeader(const SfxWatermarkItem& rWatermark,
|
|
const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<beans::XPropertySet>& xPageStyle,
|
|
const uno::Reference<text::XText>& xHeaderText)
|
|
{
|
|
uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
|
|
OUString aShapeServiceName = "com.sun.star.drawing.CustomShape";
|
|
OUString sWatermark = WATERMARK_NAME;
|
|
uno::Reference<drawing::XShape> xWatermark = lcl_getWatermark(xHeaderText, aShapeServiceName, sWatermark);
|
|
|
|
bool bDeleteWatermark = rWatermark.GetText().isEmpty();
|
|
if (xWatermark.is())
|
|
{
|
|
drawing::HomogenMatrix3 aMatrix;
|
|
sal_uInt32 nColor = 0xc0c0c0;
|
|
sal_Int16 nTransparency = 50;
|
|
sal_Int16 nAngle = 45;
|
|
OUString aFont = "";
|
|
|
|
uno::Reference<beans::XPropertySet> xPropertySet(xWatermark, uno::UNO_QUERY);
|
|
xPropertySet->getPropertyValue(UNO_NAME_CHAR_FONT_NAME) >>= aFont;
|
|
xPropertySet->getPropertyValue(UNO_NAME_FILLCOLOR) >>= nColor;
|
|
xPropertySet->getPropertyValue(UNO_NAME_FILL_TRANSPARENCE) >>= nTransparency;
|
|
xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
|
|
nAngle = lcl_GetAngle(aMatrix);
|
|
|
|
// If the header already contains a watermark, see if it its text is up to date.
|
|
uno::Reference<text::XTextRange> xTextRange(xWatermark, uno::UNO_QUERY);
|
|
if (xTextRange->getString() != rWatermark.GetText()
|
|
|| aFont != rWatermark.GetFont()
|
|
|| nColor != rWatermark.GetColor()
|
|
|| nAngle != rWatermark.GetAngle()
|
|
|| nTransparency != rWatermark.GetTransparency()
|
|
|| bDeleteWatermark)
|
|
{
|
|
// No: delete it and we'll insert a replacement.
|
|
uno::Reference<lang::XComponent> xComponent(xWatermark, uno::UNO_QUERY);
|
|
xComponent->dispose();
|
|
xWatermark.clear();
|
|
}
|
|
}
|
|
|
|
if (!xWatermark.is() && !bDeleteWatermark)
|
|
{
|
|
OUString sFont = rWatermark.GetFont();
|
|
sal_Int16 nAngle = rWatermark.GetAngle();
|
|
sal_Int16 nTransparency = rWatermark.GetTransparency();
|
|
sal_uInt32 nColor = rWatermark.GetColor();
|
|
|
|
// Calc the ratio.
|
|
double fRatio = 0;
|
|
OutputDevice* pOut = Application::GetDefaultDevice();
|
|
vcl::Font aFont(pOut->GetFont());
|
|
aFont.SetFamilyName(sFont);
|
|
auto nTextWidth = pOut->GetTextWidth(rWatermark.GetText());
|
|
if (nTextWidth)
|
|
{
|
|
fRatio = aFont.GetFontSize().Height();
|
|
fRatio /= nTextWidth;
|
|
}
|
|
|
|
// Calc the size.
|
|
sal_Int32 nWidth = 0;
|
|
awt::Size aSize;
|
|
xPageStyle->getPropertyValue(UNO_NAME_SIZE) >>= aSize;
|
|
if (aSize.Width < aSize.Height)
|
|
{
|
|
// Portrait.
|
|
sal_Int32 nLeftMargin = 0;
|
|
xPageStyle->getPropertyValue(UNO_NAME_LEFT_MARGIN) >>= nLeftMargin;
|
|
sal_Int32 nRightMargin = 0;
|
|
xPageStyle->getPropertyValue(UNO_NAME_RIGHT_MARGIN) >>= nRightMargin;
|
|
nWidth = aSize.Width - nLeftMargin - nRightMargin;
|
|
}
|
|
else
|
|
{
|
|
// Landscape.
|
|
sal_Int32 nTopMargin = 0;
|
|
xPageStyle->getPropertyValue(UNO_NAME_TOP_MARGIN) >>= nTopMargin;
|
|
sal_Int32 nBottomMargin = 0;
|
|
xPageStyle->getPropertyValue(UNO_NAME_BOTTOM_MARGIN) >>= nBottomMargin;
|
|
nWidth = aSize.Height - nTopMargin - nBottomMargin;
|
|
}
|
|
sal_Int32 nHeight = nWidth * fRatio;
|
|
|
|
// Create and insert the shape.
|
|
uno::Reference<drawing::XShape> xShape(xMultiServiceFactory->createInstance(aShapeServiceName), uno::UNO_QUERY);
|
|
basegfx::B2DHomMatrix aTransformation;
|
|
aTransformation.identity();
|
|
aTransformation.scale(nWidth, nHeight);
|
|
aTransformation.rotate(F_PI180 * -1 * nAngle);
|
|
drawing::HomogenMatrix3 aMatrix;
|
|
aMatrix.Line1.Column1 = aTransformation.get(0, 0);
|
|
aMatrix.Line1.Column2 = aTransformation.get(0, 1);
|
|
aMatrix.Line1.Column3 = aTransformation.get(0, 2);
|
|
aMatrix.Line2.Column1 = aTransformation.get(1, 0);
|
|
aMatrix.Line2.Column2 = aTransformation.get(1, 1);
|
|
aMatrix.Line2.Column3 = aTransformation.get(1, 2);
|
|
aMatrix.Line3.Column1 = aTransformation.get(2, 0);
|
|
aMatrix.Line3.Column2 = aTransformation.get(2, 1);
|
|
aMatrix.Line3.Column3 = aTransformation.get(2, 2);
|
|
uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
|
|
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
|
|
uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY);
|
|
xHeaderText->insertTextContent(xHeaderText->getEnd(), xTextContent, false);
|
|
|
|
// The remaining properties have to be set after the shape is inserted: do that in one batch to avoid flickering.
|
|
uno::Reference<document::XActionLockable> xLockable(xShape, uno::UNO_QUERY);
|
|
xLockable->addActionLock();
|
|
xPropertySet->setPropertyValue(UNO_NAME_FILLCOLOR, uno::makeAny(static_cast<sal_Int32>(nColor)));
|
|
xPropertySet->setPropertyValue(UNO_NAME_FILLSTYLE, uno::makeAny(drawing::FillStyle_SOLID));
|
|
xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::makeAny(nTransparency));
|
|
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, uno::makeAny(static_cast<sal_Int16>(text::RelOrientation::PAGE_PRINT_AREA)));
|
|
xPropertySet->setPropertyValue(UNO_NAME_LINESTYLE, uno::makeAny(drawing::LineStyle_NONE));
|
|
xPropertySet->setPropertyValue(UNO_NAME_OPAQUE, uno::makeAny(false));
|
|
xPropertySet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT, uno::makeAny(false));
|
|
xPropertySet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWWIDTH, uno::makeAny(false));
|
|
xPropertySet->setPropertyValue(UNO_NAME_TEXT_MINFRAMEHEIGHT, uno::makeAny(nHeight));
|
|
xPropertySet->setPropertyValue(UNO_NAME_TEXT_MINFRAMEWIDTH, uno::makeAny(nWidth));
|
|
xPropertySet->setPropertyValue(UNO_NAME_TEXT_WRAP, uno::makeAny(text::WrapTextMode_THROUGH));
|
|
xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION, uno::makeAny(static_cast<sal_Int16>(text::RelOrientation::PAGE_PRINT_AREA)));
|
|
xPropertySet->setPropertyValue(UNO_NAME_CHAR_FONT_NAME, uno::makeAny(sFont));
|
|
xPropertySet->setPropertyValue("Transformation", uno::makeAny(aMatrix));
|
|
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT, uno::makeAny(static_cast<sal_Int16>(text::HoriOrientation::CENTER)));
|
|
xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT, uno::makeAny(static_cast<sal_Int16>(text::VertOrientation::CENTER)));
|
|
|
|
uno::Reference<text::XTextRange> xTextRange(xShape, uno::UNO_QUERY);
|
|
xTextRange->setString(rWatermark.GetText());
|
|
|
|
uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter(xShape, uno::UNO_QUERY);
|
|
xDefaulter->createCustomShapeDefaults("fontwork-plain-text");
|
|
|
|
auto aGeomPropSeq = xPropertySet->getPropertyValue("CustomShapeGeometry").get< uno::Sequence<beans::PropertyValue> >();
|
|
auto aGeomPropVec = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aGeomPropSeq);
|
|
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
|
|
{
|
|
{"TextPath", uno::makeAny(true)},
|
|
}));
|
|
auto it = std::find_if(aGeomPropVec.begin(), aGeomPropVec.end(), [](const beans::PropertyValue& rValue)
|
|
{
|
|
return rValue.Name == "TextPath";
|
|
});
|
|
if (it == aGeomPropVec.end())
|
|
aGeomPropVec.push_back(comphelper::makePropertyValue("TextPath", aPropertyValues));
|
|
else
|
|
it->Value <<= aPropertyValues;
|
|
xPropertySet->setPropertyValue("CustomShapeGeometry", uno::makeAny(comphelper::containerToSequence(aGeomPropVec)));
|
|
|
|
// tdf#108494, tdf#109313 the header height was switched to height of a watermark
|
|
// and shape was moved to the lower part of a page, force position update
|
|
xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
|
|
xPropertySet->setPropertyValue("Transformation", uno::makeAny(aMatrix));
|
|
|
|
uno::Reference<container::XNamed> xNamed(xShape, uno::UNO_QUERY);
|
|
xNamed->setName(sWatermark);
|
|
xLockable->removeActionLock();
|
|
}
|
|
}
|
|
|
|
void SwEditShell::SetWatermark(const SfxWatermarkItem& rWatermark)
|
|
{
|
|
SwDocShell* pDocShell = GetDoc()->GetDocShell();
|
|
if (!pDocShell)
|
|
return;
|
|
|
|
uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
|
|
uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamilies(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
|
|
uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
|
|
|
|
for (const OUString& rPageStyleName : aStyles)
|
|
{
|
|
uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
|
|
|
|
// If the header is off, turn it on.
|
|
bool bHeaderIsOn = false;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
|
|
if (!bHeaderIsOn)
|
|
xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::makeAny(true));
|
|
|
|
// backup header height
|
|
bool bDynamicHeight = true;
|
|
sal_Int32 nOldValue;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_HEIGHT) >>= nOldValue;
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT) >>= bDynamicHeight;
|
|
xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(false));
|
|
|
|
// If the header already contains a document header field, no need to do anything.
|
|
uno::Reference<text::XText> xHeaderText;
|
|
uno::Reference<text::XText> xHeaderTextFirst;
|
|
uno::Reference<text::XText> xHeaderTextLeft;
|
|
uno::Reference<text::XText> xHeaderTextRight;
|
|
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
|
|
lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderText);
|
|
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_FIRST) >>= xHeaderTextFirst;
|
|
lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextFirst);
|
|
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_LEFT) >>= xHeaderTextLeft;
|
|
lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextLeft);
|
|
|
|
xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_RIGHT) >>= xHeaderTextRight;
|
|
lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextRight);
|
|
|
|
// tdf#108494 the header height was switched to height of a watermark
|
|
// and shape was moved to the lower part of a page
|
|
xPageStyle->setPropertyValue(UNO_NAME_HEADER_HEIGHT, uno::makeAny((sal_Int32)11));
|
|
xPageStyle->setPropertyValue(UNO_NAME_HEADER_HEIGHT, uno::makeAny(nOldValue));
|
|
xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(bDynamicHeight));
|
|
}
|
|
}
|
|
|
|
SwUndoParagraphSigning::SwUndoParagraphSigning(const SwPosition& rPos,
|
|
const uno::Reference<text::XTextField>& xField,
|
|
const uno::Reference<text::XTextContent>& xParent,
|
|
const bool bRemove)
|
|
: SwUndo(SwUndoId::PARA_SIGN_ADD, rPos.GetDoc()),
|
|
m_pDoc(rPos.GetDoc()),
|
|
m_xField(xField),
|
|
m_xParent(xParent),
|
|
m_bRemove(bRemove)
|
|
{
|
|
// Save the metadata and field content to undo/redo.
|
|
uno::Reference<frame::XModel> xModel = m_pDoc->GetDocShell()->GetBaseModel();
|
|
const css::uno::Reference<css::rdf::XResource> xSubject(m_xField, uno::UNO_QUERY);
|
|
std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xSubject);
|
|
const auto it = aStatements.find(ParagraphSignatureRDFName);
|
|
if (it != aStatements.end())
|
|
m_signature = it->second;
|
|
|
|
const auto it2 = aStatements.find(ParagraphSignatureUsageRDFName);
|
|
if (it2 != aStatements.end())
|
|
m_usage = it2->second;
|
|
|
|
uno::Reference<css::text::XTextRange> xText(m_xField, uno::UNO_QUERY);
|
|
m_display = xText->getString();
|
|
}
|
|
|
|
void SwUndoParagraphSigning::UndoImpl(::sw::UndoRedoContext&)
|
|
{
|
|
if (m_bRemove)
|
|
Remove();
|
|
else
|
|
Insert();
|
|
}
|
|
|
|
void SwUndoParagraphSigning::RedoImpl(::sw::UndoRedoContext&)
|
|
{
|
|
if (m_bRemove)
|
|
Insert();
|
|
else
|
|
Remove();
|
|
}
|
|
|
|
void SwUndoParagraphSigning::RepeatImpl(::sw::RepeatContext&)
|
|
{
|
|
}
|
|
|
|
void SwUndoParagraphSigning::Insert()
|
|
{
|
|
// Disable undo to avoid introducing noise when we edit the metadata field.
|
|
const bool isUndoEnabled = m_pDoc->GetIDocumentUndoRedo().DoesUndo();
|
|
m_pDoc->GetIDocumentUndoRedo().DoUndo(false);
|
|
|
|
// Prevent validation since this will trigger a premature validation
|
|
// upon inserting, but before setting the metadata.
|
|
SwEditShell* pEditSh = m_pDoc->GetEditShell();
|
|
const bool bOldValidationFlag = pEditSh->SetParagraphSignatureValidation(false);
|
|
comphelper::ScopeGuard const g([&] () {
|
|
pEditSh->SetParagraphSignatureValidation(bOldValidationFlag);
|
|
m_pDoc->GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
|
|
});
|
|
|
|
m_xField = lcl_InsertParagraphSignature(m_pDoc->GetDocShell()->GetBaseModel(), m_xParent, m_signature, m_usage);
|
|
|
|
uno::Reference<css::text::XTextRange> xText(m_xField, uno::UNO_QUERY);
|
|
xText->setString(m_display);
|
|
}
|
|
|
|
void SwUndoParagraphSigning::Remove()
|
|
{
|
|
// Disable undo to avoid introducing noise when we edit the metadata field.
|
|
const bool isUndoEnabled = m_pDoc->GetIDocumentUndoRedo().DoesUndo();
|
|
m_pDoc->GetIDocumentUndoRedo().DoUndo(false);
|
|
|
|
// Prevent validation since this will trigger a premature validation
|
|
// upon removing.
|
|
SwEditShell* pEditSh = m_pDoc->GetEditShell();
|
|
const bool bOldValidationFlag = pEditSh->SetParagraphSignatureValidation(false);
|
|
comphelper::ScopeGuard const g([&] () {
|
|
pEditSh->SetParagraphSignatureValidation(bOldValidationFlag);
|
|
m_pDoc->GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
|
|
});
|
|
|
|
lcl_RemoveParagraphMetadataField(m_xField);
|
|
}
|
|
|
|
void SwEditShell::SignParagraph()
|
|
{
|
|
SwDocShell* pDocShell = GetDoc()->GetDocShell();
|
|
if (!pDocShell)
|
|
return;
|
|
const SwPosition* pPosStart = GetCursor()->Start();
|
|
if (!pPosStart)
|
|
return;
|
|
SwTextNode* pNode = pPosStart->nNode.GetNode().GetTextNode();
|
|
if (!pNode)
|
|
return;
|
|
|
|
// 1. Get the text (without fields).
|
|
const uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(*pNode->GetDoc(), pNode);
|
|
const OString utf8Text = lcl_getParagraphBodyText(xParent);
|
|
if (utf8Text.isEmpty())
|
|
return;
|
|
|
|
// 2. Get certificate.
|
|
uno::Reference<security::XDocumentDigitalSignatures> xSigner(
|
|
security::DocumentDigitalSignatures::createWithVersion(
|
|
comphelper::getProcessComponentContext(), "1.2" ) );
|
|
|
|
uno::Sequence<css::beans::PropertyValue> aProperties;
|
|
uno::Reference<security::XCertificate> xCertificate = xSigner->chooseCertificateWithProps(aProperties);
|
|
if (!xCertificate.is())
|
|
return;
|
|
|
|
// 3. Sign it.
|
|
svl::crypto::Signing signing(xCertificate);
|
|
signing.AddDataRange(utf8Text.getStr(), utf8Text.getLength());
|
|
OStringBuffer sigBuf;
|
|
if (!signing.Sign(sigBuf))
|
|
return;
|
|
|
|
const OUString signature = OStringToOUString(sigBuf.makeStringAndClear(), RTL_TEXTENCODING_UTF8, 0);
|
|
|
|
std::vector<css::beans::PropertyValue> vec = comphelper::sequenceToContainer<std::vector<css::beans::PropertyValue>>(aProperties);
|
|
auto it = std::find_if(vec.begin(), vec.end(), [](const beans::PropertyValue& rValue)
|
|
{
|
|
return rValue.Name == "Usage";
|
|
});
|
|
|
|
OUString aUsage;
|
|
if (it != vec.end())
|
|
it->Value >>= aUsage;
|
|
|
|
// 4. Add metadata
|
|
// Prevent validation since this will trigger a premature validation
|
|
// upon inserting, but before setting the metadata.
|
|
const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
|
|
comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
|
|
SetParagraphSignatureValidation(bOldValidationFlag);
|
|
});
|
|
|
|
GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
|
|
|
|
const uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
|
|
uno::Reference<css::text::XTextField> xField = lcl_InsertParagraphSignature(xModel, xParent, signature, aUsage);
|
|
|
|
lcl_UpdateParagraphSignatureField(GetDoc(), xModel, xField, utf8Text);
|
|
|
|
SwUndoParagraphSigning* pUndo = new SwUndoParagraphSigning(SwPosition(*pNode), xField, xParent, true);
|
|
GetDoc()->GetIDocumentUndoRedo().AppendUndo(pUndo);
|
|
|
|
GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
|
|
}
|
|
|
|
void SwEditShell::ValidateParagraphSignatures(bool updateDontRemove)
|
|
{
|
|
SwDocShell* pDocShell = GetDoc()->GetDocShell();
|
|
if (!pDocShell || !IsParagraphSignatureValidationEnabled())
|
|
return;
|
|
|
|
SwPaM* pPaM = GetCursor();
|
|
const SwPosition* pPosStart = pPaM->Start();
|
|
SwTextNode* pNode = pPosStart->nNode.GetNode().GetTextNode();
|
|
if (!pNode)
|
|
return;
|
|
|
|
// 2. For each signature field, update it.
|
|
const uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(*pNode->GetDoc(), pNode);
|
|
uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParent, uno::UNO_QUERY);
|
|
if (!xTextPortionEnumerationAccess.is())
|
|
return;
|
|
|
|
uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
|
|
uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
|
|
|
|
// Prevent recursive validation since this is triggered on node updates, which we do below.
|
|
const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
|
|
comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
|
|
SetParagraphSignatureValidation(bOldValidationFlag);
|
|
});
|
|
|
|
// 2. Get the text (without fields).
|
|
const OString utf8Text = lcl_getParagraphBodyText(xParent);
|
|
if (utf8Text.isEmpty())
|
|
return;
|
|
|
|
while (xTextPortions->hasMoreElements())
|
|
{
|
|
uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
|
|
OUString aTextPortionType;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
|
|
if (aTextPortionType != UNO_NAME_TEXT_FIELD)
|
|
continue;
|
|
|
|
uno::Reference<lang::XServiceInfo> xTextField;
|
|
xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
|
|
if (!xTextField->supportsService(MetadataFieldServiceName))
|
|
continue;
|
|
|
|
uno::Reference<text::XTextField> xField(xTextField, uno::UNO_QUERY);
|
|
if (!lcl_IsParagraphSignatureField(xModel, xField))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (updateDontRemove)
|
|
{
|
|
lcl_UpdateParagraphSignatureField(GetDoc(), xModel, xField, utf8Text);
|
|
}
|
|
else if (!lcl_MakeParagraphSignatureFieldText(xModel, xField, utf8Text).first)
|
|
{
|
|
GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
|
|
SwUndoParagraphSigning* pUndo = new SwUndoParagraphSigning(SwPosition(*pNode), xField, xParent, false);
|
|
GetDoc()->GetIDocumentUndoRedo().AppendUndo(pUndo);
|
|
lcl_RemoveParagraphMetadataField(xField);
|
|
GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SwEditShell::IsCursorInParagraphMetadataField() const
|
|
{
|
|
SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
|
|
if (pNode != nullptr)
|
|
{
|
|
SwTextAttr* pAttr = pNode->GetTextAttrAt(GetCursor()->Start()->nContent.GetIndex(), RES_TXTATR_METAFIELD);
|
|
SwTextMeta* pTextMeta = static_txtattr_cast<SwTextMeta*>(pAttr);
|
|
if (pTextMeta != nullptr)
|
|
{
|
|
SwFormatMeta& rFormatMeta(static_cast<SwFormatMeta&>(pTextMeta->GetAttr()));
|
|
if (::sw::Meta* pMeta = rFormatMeta.GetMeta())
|
|
{
|
|
if (const SwDocShell* pDocSh = GetDoc()->GetDocShell())
|
|
{
|
|
const css::uno::Reference<css::rdf::XResource> xSubject(pMeta->MakeUnoObject(), uno::UNO_QUERY);
|
|
uno::Reference<frame::XModel> xModel = pDocSh->GetBaseModel();
|
|
const std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, MetaNS, xSubject);
|
|
if (aStatements.find(ParagraphSignatureRDFName) != aStatements.end() ||
|
|
aStatements.find(ParagraphClassificationNameRDFName) != aStatements.end())
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// #i62675#
|
|
void SwEditShell::SetTextFormatColl(SwTextFormatColl *pFormat,
|
|
const bool bResetListAttrs)
|
|
{
|
|
SwTextFormatColl *pLocal = pFormat? pFormat: (*GetDoc()->GetTextFormatColls())[0];
|
|
StartAllAction();
|
|
|
|
SwRewriter aRewriter;
|
|
aRewriter.AddRule(UndoArg1, pLocal->GetName());
|
|
|
|
GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::SETFMTCOLL, &aRewriter);
|
|
for(SwPaM& rPaM : GetCursor()->GetRingContainer())
|
|
{
|
|
|
|
if ( !rPaM.HasReadonlySel( GetViewOptions()->IsFormView() ) )
|
|
{
|
|
// Change the paragraph style to pLocal and remove all direct paragraph formatting.
|
|
GetDoc()->SetTextFormatColl( rPaM, pLocal, true, bResetListAttrs );
|
|
|
|
// If there are hints on the nodes which cover the whole node, then remove those, too.
|
|
SwPaM aPaM(*rPaM.Start(), *rPaM.End());
|
|
if (SwTextNode* pEndTextNode = aPaM.End()->nNode.GetNode().GetTextNode())
|
|
{
|
|
aPaM.Start()->nContent = 0;
|
|
aPaM.End()->nContent = pEndTextNode->GetText().getLength();
|
|
}
|
|
GetDoc()->RstTextAttrs(aPaM, /*bInclRefToxMark=*/false, /*bExactRange=*/true);
|
|
}
|
|
|
|
}
|
|
GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::SETFMTCOLL, &aRewriter);
|
|
EndAllAction();
|
|
}
|
|
|
|
SwTextFormatColl* SwEditShell::MakeTextFormatColl(const OUString& rFormatCollName,
|
|
SwTextFormatColl* pParent)
|
|
{
|
|
SwTextFormatColl *pColl;
|
|
if ( pParent == nullptr )
|
|
pParent = &GetTextFormatColl(0);
|
|
if ( (pColl=GetDoc()->MakeTextFormatColl(rFormatCollName, pParent)) == nullptr )
|
|
{
|
|
OSL_FAIL( "MakeTextFormatColl failed" );
|
|
}
|
|
return pColl;
|
|
|
|
}
|
|
|
|
void SwEditShell::FillByEx(SwTextFormatColl* pColl)
|
|
{
|
|
SwPaM * pCursor = GetCursor();
|
|
SwContentNode * pCnt = pCursor->GetContentNode();
|
|
const SfxItemSet* pSet = pCnt->GetpSwAttrSet();
|
|
if( pSet )
|
|
{
|
|
// JP 05.10.98: Special treatment if one of the attribues Break/PageDesc/NumRule(auto) is
|
|
// in the ItemSet. Otherwise there will be too much or wrong processing (NumRules!)
|
|
// Bug 57568
|
|
|
|
// Do NOT copy AutoNumRules into the template
|
|
const SfxPoolItem* pItem;
|
|
const SwNumRule* pRule = nullptr;
|
|
if( SfxItemState::SET == pSet->GetItemState( RES_BREAK, false ) ||
|
|
SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC,false ) ||
|
|
( SfxItemState::SET == pSet->GetItemState( RES_PARATR_NUMRULE,
|
|
false, &pItem ) && nullptr != (pRule = GetDoc()->FindNumRulePtr(
|
|
static_cast<const SwNumRuleItem*>(pItem)->GetValue() )) &&
|
|
pRule && pRule->IsAutoRule() )
|
|
)
|
|
{
|
|
SfxItemSet aSet( *pSet );
|
|
aSet.ClearItem( RES_BREAK );
|
|
aSet.ClearItem( RES_PAGEDESC );
|
|
|
|
if( pRule || (SfxItemState::SET == pSet->GetItemState( RES_PARATR_NUMRULE,
|
|
false, &pItem ) && nullptr != (pRule = GetDoc()->FindNumRulePtr(
|
|
static_cast<const SwNumRuleItem*>(pItem)->GetValue() )) &&
|
|
pRule && pRule->IsAutoRule() ))
|
|
aSet.ClearItem( RES_PARATR_NUMRULE );
|
|
|
|
if( aSet.Count() )
|
|
GetDoc()->ChgFormat(*pColl, aSet );
|
|
}
|
|
else
|
|
GetDoc()->ChgFormat(*pColl, *pSet );
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|