Files
loongoffice/xmloff/source/text/xmlcontentcontrolcontext.cxx
Justin Luth 54599383a2 sw content controls: enhance preserve id
Follow-up to 100c914d44ae8f362924fe567d7d41d0033ae8ad
which added the initial id preservation for DOCX.

adding DOCX block SDT grabbaging, ODF import/export
[content controls can't exist in DOC format]

The ID field is CRITICAL to preserve for VBA purposes.
This patch adjusts BlockSDT to also round-trip the id
instead of just creating a random one.

m_nRunSdtPrToken <never equals> FSNS(XML_w, XML_id) since 2021
with 5f3af56b2c0ef6c628a7cfe5ce6e86f8e1765f5f,
so I removed that part of the clause.

I had thought about changing the ID to use a string instead of an int,
but then the integer version was adopted to fix a regression
in the commit mentioned earlier.

I think it is AWFUL to have a number as the identifier
when it will be used in StarBASIC. The VBA guys have to deal
with it, but it would be nice to do something reasonable
for LO native access to content controls.

However, the concern would be if we allow VBA macro content created in LO
to be exported to DOCX format - that would cause problems converting
from a string ID to a number ID. VBA editing already is happening to
some extent, and mmeeks seems interested in enabling it.
So over-all it seems best to just stick with an integer ID.

I used the commits for <w:alias> and <w:tag> to compose this patch.
XML_ID already existed in include/xmloff/xmltoken.hxx
and "id" already exists in xmloff/source/token/tokens.txt

The ID can be used in VBA to select a specific control.
The id (which is a positive or negative integer in DOCX)
specifies a unique control - either by passing the number as a string
(of the UNSIGNED value) or by passing as a float (specified with #).

For example:
msgbox ActiveDocument.ContentControls(2587720202#).ID
msgbox ActiveDocument.ContentControls("2587720202").ID
but not as an integer since that is used for index access.
dim index as integer
index = 1
msgbox ActiveDocument.ContentControls(index).ID

make CppunitTest_writerfilter_dmapper CPPUNIT_TEST_NAME=testSdtRunRichText
make CppunitTest_sw_ooxmlexport17 CPPUNIT_TEST_NAME=testDateContentControlExport
make CppunitTest_sw_ooxmlexport18 CPPUNIT_TEST_NAME=testTdf151912
make CppunitTest_sw_core_unocore CPPUNIT_TEST_NAME=testContentControlDate
make CppunitTest_xmloff_text CPPUNIT_TEST_NAME=testAliasContentControlExport
make CppunitTest_xmloff_text CPPUNIT_TEST_NAME=testAliasContentControlImport

Change-Id: I5c4022dc932d941fad9da6d75ce899ee1ff66ff5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142818
Tested-by: Jenkins
Reviewed-by: Justin Luth <jluth@mail.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
2022-12-16 07:34:20 +00:00

370 lines
11 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 "xmlcontentcontrolcontext.hxx"
#include <com/sun/star/beans/XPropertySet.hpp>
#include <sax/tools/converter.hxx>
#include <xmloff/xmlimp.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmltoken.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/sequence.hxx>
#include "XMLTextMarkImportContext.hxx"
#include "txtparai.hxx"
using namespace com::sun::star;
using namespace xmloff::token;
XMLContentControlContext::XMLContentControlContext(SvXMLImport& rImport, sal_Int32 /*nElement*/,
XMLHints_Impl& rHints, bool& rIgnoreLeadingSpace)
: SvXMLImportContext(rImport)
, m_rHints(rHints)
, m_rIgnoreLeadingSpace(rIgnoreLeadingSpace)
, m_xStart(GetImport().GetTextImport()->GetCursorAsRange()->getStart())
{
}
void XMLContentControlContext::startFastElement(
sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
{
for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
{
bool bTmp = false;
sal_Int32 nTmp = 0;
switch (rIter.getToken())
{
case XML_ELEMENT(LO_EXT, XML_SHOWING_PLACE_HOLDER):
{
if (sax::Converter::convertBool(bTmp, rIter.toView()))
{
m_bShowingPlaceHolder = bTmp;
}
break;
}
case XML_ELEMENT(LO_EXT, XML_CHECKBOX):
{
if (sax::Converter::convertBool(bTmp, rIter.toView()))
{
m_bCheckbox = bTmp;
}
break;
}
case XML_ELEMENT(LO_EXT, XML_CHECKED):
{
if (sax::Converter::convertBool(bTmp, rIter.toView()))
{
m_bChecked = bTmp;
}
break;
}
case XML_ELEMENT(LO_EXT, XML_CHECKED_STATE):
{
m_aCheckedState = rIter.toString();
break;
}
case XML_ELEMENT(LO_EXT, XML_UNCHECKED_STATE):
{
m_aUncheckedState = rIter.toString();
break;
}
case XML_ELEMENT(LO_EXT, XML_PICTURE):
{
if (sax::Converter::convertBool(bTmp, rIter.toView()))
{
m_bPicture = bTmp;
}
break;
}
case XML_ELEMENT(LO_EXT, XML_DATE):
{
if (sax::Converter::convertBool(bTmp, rIter.toView()))
{
m_bDate = bTmp;
}
break;
}
case XML_ELEMENT(LO_EXT, XML_DATE_FORMAT):
{
m_aDateFormat = rIter.toString();
break;
}
case XML_ELEMENT(LO_EXT, XML_DATE_RFC_LANGUAGE_TAG):
{
m_aDateLanguage = rIter.toString();
break;
}
case XML_ELEMENT(LO_EXT, XML_CURRENT_DATE):
{
m_aCurrentDate = rIter.toString();
break;
}
case XML_ELEMENT(LO_EXT, XML_PLAIN_TEXT):
{
if (sax::Converter::convertBool(bTmp, rIter.toView()))
{
m_bPlainText = bTmp;
}
break;
}
case XML_ELEMENT(LO_EXT, XML_COMBOBOX):
{
if (sax::Converter::convertBool(bTmp, rIter.toView()))
{
m_bComboBox = bTmp;
}
break;
}
case XML_ELEMENT(LO_EXT, XML_DROPDOWN):
{
if (sax::Converter::convertBool(bTmp, rIter.toView()))
{
m_bDropDown = bTmp;
}
break;
}
case XML_ELEMENT(LO_EXT, XML_ALIAS):
{
m_aAlias = rIter.toString();
break;
}
case XML_ELEMENT(LO_EXT, XML_TAG):
{
m_aTag = rIter.toString();
break;
}
case XML_ELEMENT(LO_EXT, XML_ID):
{
if (sax::Converter::convertNumber(nTmp, rIter.toView()))
{
m_nId = nTmp;
}
break;
}
case XML_ELEMENT(LO_EXT, XML_TAB_INDEX):
{
if (sax::Converter::convertNumber(nTmp, rIter.toView()))
{
m_nTabIndex = nTmp;
}
break;
}
case XML_ELEMENT(LO_EXT, XML_LOCK):
{
m_aLock = rIter.toString();
break;
}
default:
XMLOFF_WARN_UNKNOWN("xmloff", rIter);
}
}
}
void XMLContentControlContext::endFastElement(sal_Int32)
{
if (!m_xStart.is())
{
SAL_WARN("xmloff.text", "XMLContentControlContext::endFastElement: no m_xStart");
return;
}
uno::Reference<text::XTextRange> xEndRange
= GetImport().GetTextImport()->GetCursorAsRange()->getStart();
// Create range for insertion.
uno::Reference<text::XTextCursor> xInsertionCursor
= GetImport().GetTextImport()->GetText()->createTextCursorByRange(xEndRange);
xInsertionCursor->gotoRange(m_xStart, /*bExpand=*/true);
uno::Reference<text::XTextContent> xContentControl
= XMLTextMarkImportContext::CreateAndInsertMark(
GetImport(), "com.sun.star.text.ContentControl", OUString(), xInsertionCursor);
if (!xContentControl.is())
{
SAL_WARN("xmloff.text", "cannot insert content control");
return;
}
uno::Reference<beans::XPropertySet> xPropertySet(xContentControl, uno::UNO_QUERY);
if (!xPropertySet.is())
{
return;
}
if (m_bShowingPlaceHolder)
{
xPropertySet->setPropertyValue("ShowingPlaceHolder", uno::Any(m_bShowingPlaceHolder));
}
if (m_bCheckbox)
{
xPropertySet->setPropertyValue("Checkbox", uno::Any(m_bCheckbox));
}
if (m_bChecked)
{
xPropertySet->setPropertyValue("Checked", uno::Any(m_bChecked));
}
if (!m_aCheckedState.isEmpty())
{
xPropertySet->setPropertyValue("CheckedState", uno::Any(m_aCheckedState));
}
if (!m_aUncheckedState.isEmpty())
{
xPropertySet->setPropertyValue("UncheckedState", uno::Any(m_aUncheckedState));
}
if (!m_aListItems.empty())
{
xPropertySet->setPropertyValue("ListItems",
uno::Any(comphelper::containerToSequence(m_aListItems)));
}
if (m_bPicture)
{
xPropertySet->setPropertyValue("Picture", uno::Any(m_bPicture));
}
if (m_bDate)
{
xPropertySet->setPropertyValue("Date", uno::Any(m_bDate));
}
if (!m_aDateFormat.isEmpty())
{
xPropertySet->setPropertyValue("DateFormat", uno::Any(m_aDateFormat));
}
if (!m_aDateLanguage.isEmpty())
{
xPropertySet->setPropertyValue("DateLanguage", uno::Any(m_aDateLanguage));
}
if (!m_aCurrentDate.isEmpty())
{
xPropertySet->setPropertyValue("CurrentDate", uno::Any(m_aCurrentDate));
}
if (m_bPlainText)
{
xPropertySet->setPropertyValue("PlainText", uno::Any(m_bPlainText));
}
if (m_bComboBox)
{
xPropertySet->setPropertyValue("ComboBox", uno::Any(m_bComboBox));
}
if (m_bDropDown)
{
xPropertySet->setPropertyValue("DropDown", uno::Any(m_bDropDown));
}
if (!m_aAlias.isEmpty())
{
xPropertySet->setPropertyValue("Alias", uno::Any(m_aAlias));
}
if (!m_aTag.isEmpty())
{
xPropertySet->setPropertyValue("Tag", uno::Any(m_aTag));
}
if (m_nId)
{
xPropertySet->setPropertyValue("Id", uno::Any(m_nId));
}
if (m_nTabIndex)
{
xPropertySet->setPropertyValue("TabIndex", uno::Any(m_nTabIndex));
}
if (!m_aLock.isEmpty())
{
xPropertySet->setPropertyValue("Lock", uno::Any(m_aLock));
}
}
css::uno::Reference<css::xml::sax::XFastContextHandler>
XMLContentControlContext::createFastChildContext(
sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
{
switch (nElement)
{
case XML_ELEMENT(LO_EXT, XML_LIST_ITEM):
return new XMLListItemContext(GetImport(), *this);
break;
default:
break;
}
return XMLImpSpanContext_Impl::CreateSpanContext(GetImport(), nElement, xAttrList, m_rHints,
m_rIgnoreLeadingSpace);
}
void XMLContentControlContext::characters(const OUString& rChars)
{
GetImport().GetTextImport()->InsertString(rChars, m_rIgnoreLeadingSpace);
}
void XMLContentControlContext::AppendListItem(const css::beans::PropertyValues& rListItem)
{
m_aListItems.push_back(rListItem);
}
XMLListItemContext::XMLListItemContext(SvXMLImport& rImport,
XMLContentControlContext& rContentControl)
: SvXMLImportContext(rImport)
, m_rContentControl(rContentControl)
{
}
void XMLListItemContext::startFastElement(
sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
{
OUString aDisplayText;
OUString aValue;
for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
{
switch (rIter.getToken())
{
case XML_ELEMENT(LO_EXT, XML_DISPLAY_TEXT):
{
aDisplayText = rIter.toString();
break;
}
case XML_ELEMENT(LO_EXT, XML_VALUE):
{
aValue = rIter.toString();
break;
}
default:
XMLOFF_WARN_UNKNOWN("xmloff", rIter);
}
}
uno::Sequence<beans::PropertyValue> aListItem = {
comphelper::makePropertyValue("DisplayText", uno::Any(aDisplayText)),
comphelper::makePropertyValue("Value", uno::Any(aValue)),
};
m_rContentControl.AppendListItem(aListItem);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */