/* -*- 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/. */ #include "SdtHelper.hxx" #include #include #include #include #include #include #include #include #include #include "DomainMapper_Impl.hxx" #include "StyleSheetTable.hxx" #include #include #include #include #include #include namespace writerfilter { namespace dmapper { using namespace ::com::sun::star; using namespace ::css::xml::xpath; /// w:sdt's w:dropDownList doesn't have width, so guess the size based on the longest string. static awt::Size lcl_getOptimalWidth(const StyleSheetTablePtr& pStyleSheet, OUString const& rDefault, std::vector& rItems) { OUString aLongest = rDefault; sal_Int32 nHeight = 0; for (const OUString& rItem : rItems) if (rItem.getLength() > aLongest.getLength()) aLongest = rItem; MapMode aMap(MapUnit::Map100thMM); OutputDevice* pOut = Application::GetDefaultDevice(); pOut->Push(PushFlags::FONT | PushFlags::MAPMODE); PropertyMapPtr pDefaultCharProps = pStyleSheet->GetDefaultCharProps(); vcl::Font aFont(pOut->GetFont()); boost::optional aFontName = pDefaultCharProps->getProperty(PROP_CHAR_FONT_NAME); if (aFontName) aFont.SetFamilyName(aFontName->second.get()); boost::optional aHeight = pDefaultCharProps->getProperty(PROP_CHAR_HEIGHT); if (aHeight) { nHeight = aHeight->second.get() * 35; // points -> mm100 aFont.SetFontSize(Size(0, nHeight)); } pOut->SetFont(aFont); pOut->SetMapMode(aMap); sal_Int32 nWidth = pOut->GetTextWidth(aLongest); pOut->Pop(); // Border: see PDFWriterImpl::drawFieldBorder(), border size is font height / 4, // so additional width / height needed is height / 2. sal_Int32 nBorder = nHeight / 2; // Width: space for the text + the square having the dropdown arrow. return { nWidth + nBorder + nHeight, nHeight + nBorder }; } SdtHelper::SdtHelper(DomainMapper_Impl& rDM_Impl, css::uno::Reference const& xContext) : m_rDM_Impl(rDM_Impl) , m_xComponentContext(xContext) , m_aControlType(SdtControlType::unknown) , m_bHasElements(false) , m_bOutsideAParagraph(false) { } SdtHelper::~SdtHelper() = default; std::optional SdtHelper::getValueFromDataBinding() { // No xpath - nothing to do if (m_sDataBindingXPath.isEmpty()) return {}; writerfilter::ooxml::OOXMLDocument* pDocument = m_rDM_Impl.getDocumentReference(); assert(pDocument); if (!pDocument) return {}; // Iterate all custom xmls documents and evaluate xpath to get value uno::Sequence> aCustomXmls = pDocument->getCustomXmlDomList(); for (const auto& xCustomXml : aCustomXmls) { uno::Reference xXpathAPI = XPathAPI::create(m_xComponentContext); //xXpathAPI->registerNS("ns0", m_sDataBindingPrefixMapping); xXpathAPI->registerNS("ns0", "http://schemas.microsoft.com/vsto/samples"); uno::Reference xResult = xXpathAPI->eval(xCustomXml, m_sDataBindingXPath); if (xResult.is()) { return xResult->getString(); } } return {}; } void SdtHelper::createDropDownControl() { assert(getControlType() == SdtControlType::dropDown); const bool bDropDown = officecfg::Office::Writer::Filter::Import::DOCX::ImportComboBoxAsDropDown::get(); const OUString aDefaultText = m_aSdtTexts.makeStringAndClear(); if (bDropDown) { // create field uno::Reference xControlModel( m_rDM_Impl.GetTextFactory()->createInstance("com.sun.star.text.TextField.DropDown"), uno::UNO_QUERY); const auto it = std::find_if( m_aDropDownItems.begin(), m_aDropDownItems.end(), [aDefaultText](const OUString& item) -> bool { return !item.compareTo(aDefaultText); }); if (m_aDropDownItems.end() == it) { m_aDropDownItems.push_back(aDefaultText); } // set properties uno::Reference xPropertySet(xControlModel, uno::UNO_QUERY); xPropertySet->setPropertyValue("SelectedItem", uno::makeAny(aDefaultText)); xPropertySet->setPropertyValue( "Items", uno::makeAny(comphelper::containerToSequence(m_aDropDownItems))); // add it into document m_rDM_Impl.appendTextContent(xControlModel, uno::Sequence()); m_bHasElements = true; } else { // create control uno::Reference xControlModel( m_rDM_Impl.GetTextFactory()->createInstance("com.sun.star.form.component.ComboBox"), uno::UNO_QUERY); // set properties uno::Reference xPropertySet(xControlModel, uno::UNO_QUERY); xPropertySet->setPropertyValue("DefaultText", uno::makeAny(aDefaultText)); xPropertySet->setPropertyValue("Dropdown", uno::makeAny(true)); xPropertySet->setPropertyValue( "StringItemList", uno::makeAny(comphelper::containerToSequence(m_aDropDownItems))); // add it into document createControlShape( lcl_getOptimalWidth(m_rDM_Impl.GetStyleSheetTable(), aDefaultText, m_aDropDownItems), xControlModel, uno::Sequence()); } // clean up m_aDropDownItems.clear(); setControlType(SdtControlType::unknown); } void SdtHelper::createDateContentControl() { if (!m_xDateFieldStartRange.is()) return; uno::Reference xCrsr; if (m_rDM_Impl.HasTopText()) { uno::Reference xTextAppend = m_rDM_Impl.GetTopTextAppend(); if (xTextAppend.is()) { xCrsr = xTextAppend->createTextCursorByRange(xTextAppend); } } if (xCrsr.is()) { try { xCrsr->gotoRange(m_xDateFieldStartRange, false); bool bIsInTable = (m_rDM_Impl.hasTableManager() && m_rDM_Impl.getTableManager().isInTable()) || (m_rDM_Impl.m_nTableDepth > 0); if (bIsInTable) xCrsr->goRight(1, false); xCrsr->gotoEnd(true); } catch (uno::Exception&) { OSL_ENSURE(false, "Cannot get the right text range for date field"); return; } uno::Reference xFieldInterface = m_rDM_Impl.GetTextFactory()->createInstance("com.sun.star.text.Fieldmark"); uno::Reference xFormField(xFieldInterface, uno::UNO_QUERY); uno::Reference xToInsert(xFormField, uno::UNO_QUERY); if (xFormField.is() && xToInsert.is()) { xToInsert->attach(uno::Reference(xCrsr, uno::UNO_QUERY_THROW)); xFormField->setFieldType(ODF_FORMDATE); uno::Reference xNameCont = xFormField->getParameters(); if (xNameCont.is()) { OUString sDateFormat = m_sDateFormat.makeStringAndClear(); // Replace quotation mark used for marking static strings in date format sDateFormat = sDateFormat.replaceAll("'", "\""); xNameCont->insertByName(ODF_FORMDATE_DATEFORMAT, uno::makeAny(sDateFormat)); xNameCont->insertByName(ODF_FORMDATE_DATEFORMAT_LANGUAGE, uno::makeAny(m_sLocale.makeStringAndClear())); } OUString sFullDate = m_sDate.makeStringAndClear(); std::optional oData = getValueFromDataBinding(); if (oData.has_value()) sFullDate = *oData; if (!sFullDate.isEmpty()) { sal_Int32 nTimeSep = sFullDate.indexOf("T"); if (nTimeSep != -1) sFullDate = sFullDate.copy(0, nTimeSep); xNameCont->insertByName(ODF_FORMDATE_CURRENTDATE, uno::makeAny(sFullDate)); } // Store all unused sdt parameters from grabbag xNameCont->insertByName("SdtParams", uno::makeAny(getInteropGrabBagAndClear())); } } setControlType(SdtControlType::unknown); } void SdtHelper::createControlShape(awt::Size aSize, uno::Reference const& xControlModel, const uno::Sequence& rGrabBag) { uno::Reference xControlShape( m_rDM_Impl.GetTextFactory()->createInstance("com.sun.star.drawing.ControlShape"), uno::UNO_QUERY); xControlShape->setSize(aSize); xControlShape->setControl(xControlModel); uno::Reference xPropertySet(xControlShape, uno::UNO_QUERY); xPropertySet->setPropertyValue("VertOrient", uno::makeAny(text::VertOrientation::CENTER)); if (rGrabBag.hasElements()) xPropertySet->setPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG, uno::makeAny(rGrabBag)); uno::Reference xTextContent(xControlShape, uno::UNO_QUERY); m_rDM_Impl.appendTextContent(xTextContent, uno::Sequence()); m_bHasElements = true; } void SdtHelper::appendToInteropGrabBag(const beans::PropertyValue& rValue) { m_aGrabBag.push_back(rValue); } uno::Sequence SdtHelper::getInteropGrabBagAndClear() { uno::Sequence aRet = comphelper::containerToSequence(m_aGrabBag); m_aGrabBag.clear(); return aRet; } bool SdtHelper::isInteropGrabBagEmpty() const { return m_aGrabBag.empty(); } sal_Int32 SdtHelper::getInteropGrabBagSize() const { return m_aGrabBag.size(); } bool SdtHelper::containedInInteropGrabBag(const OUString& rValueName) { for (const beans::PropertyValue& i : m_aGrabBag) if (i.Name == rValueName) return true; return false; } } // namespace dmapper } // namespace writerfilter /* vim:set shiftwidth=4 softtabstop=4 expandtab: */