diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /xmloff/source/forms | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xmloff/source/forms')
49 files changed, 13912 insertions, 0 deletions
diff --git a/xmloff/source/forms/callbacks.hxx b/xmloff/source/forms/callbacks.hxx new file mode 100644 index 000000000..0134152b8 --- /dev/null +++ b/xmloff/source/forms/callbacks.hxx @@ -0,0 +1,79 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <rtl/ref.hxx> + +class SvXMLExport; +class SvXMLExportPropertyMapper; + +namespace xmloff +{ + + //= IFormsExportContext + class IFormsExportContext + { + public: + virtual SvXMLExport& getGlobalContext() = 0; + virtual ::rtl::Reference< SvXMLExportPropertyMapper > getStylePropertyMapper() = 0; + + /** steps through a collection and exports all children of this collection + */ + virtual void exportCollectionElements( + const css::uno::Reference< css::container::XIndexAccess >& _rxCollection) = 0; + + virtual OUString getObjectStyleName( + const css::uno::Reference< css::beans::XPropertySet >& _rxObject ) = 0; + + protected: + ~IFormsExportContext() {} + }; + + //= IEventAttacherManager + class IEventAttacherManager + { + public: + virtual void registerEvents( + const css::uno::Reference< css::beans::XPropertySet >& _rxElement, + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rEvents + ) = 0; + + protected: + ~IEventAttacherManager() {} + }; + + //= IEventAttacher + class IEventAttacher + { + public: + virtual void registerEvents( + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rEvents + ) = 0; + + protected: + ~IEventAttacher() {} + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/controlelement.cxx b/xmloff/source/forms/controlelement.cxx new file mode 100644 index 000000000..af752ae4b --- /dev/null +++ b/xmloff/source/forms/controlelement.cxx @@ -0,0 +1,87 @@ +/* -*- 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 "controlelement.hxx" +#include <xmloff/xmltoken.hxx> + +using namespace ::xmloff::token; + +namespace xmloff +{ + + //= OControlElement + const char* OControlElement::getElementName(ElementType _eType) + { + switch (_eType) + { + case TEXT: return "text"; + case TEXT_AREA: return "textarea"; + case PASSWORD: return "password"; + case FILE: return "file"; + case FORMATTED_TEXT: return "formatted-text"; + case FIXED_TEXT: return "fixed-text"; + case COMBOBOX: return "combobox"; + case LISTBOX: return "listbox"; + case BUTTON: return "button"; + case IMAGE: return "image"; + case CHECKBOX: return "checkbox"; + case RADIO: return "radio"; + case FRAME: return "frame"; + case IMAGE_FRAME: return "image-frame"; + case HIDDEN: return "hidden"; + case GRID: return "grid"; + case VALUERANGE: return "value-range"; + case TIME: return "time"; + case DATE: return "date"; + + default: return "generic-control"; + } + } + + sal_Int32 OControlElement::getElementToken(ElementType _eType) + { + switch (_eType) + { + case TEXT: return XML_TEXT; + case TEXT_AREA: return XML_TEXTAREA; + case PASSWORD: return XML_PASSWORD; + case FILE: return XML_FILE; + case FORMATTED_TEXT: return XML_FORMATTED_TEXT; + case FIXED_TEXT: return XML_FIXED_TEXT; + case COMBOBOX: return XML_COMBOBOX; + case LISTBOX: return XML_LISTBOX; + case BUTTON: return XML_BUTTON; + case IMAGE: return XML_IMAGE; + case CHECKBOX: return XML_CHECKBOX; + case RADIO: return XML_RADIO; + case FRAME: return XML_FRAME; + case IMAGE_FRAME: return XML_IMAGE_FRAME; + case HIDDEN: return XML_HIDDEN; + case GRID: return XML_GRID; + case VALUERANGE: return XML_VALUE_RANGE; + case TIME: return XML_TIME; + case DATE: return XML_DATE; + + default: return XML_GENERIC_CONTROL; + } + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/controlelement.hxx b/xmloff/source/forms/controlelement.hxx new file mode 100644 index 000000000..df7f12a43 --- /dev/null +++ b/xmloff/source/forms/controlelement.hxx @@ -0,0 +1,88 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> + +namespace xmloff +{ + + //= OControlElement + /** helper for translating between control types and XML tags + */ + class OControlElement + { + public: + enum ElementType + { + TEXT = 0, + TEXT_AREA, + PASSWORD, + FILE, + FORMATTED_TEXT, + FIXED_TEXT, + COMBOBOX, + LISTBOX, + BUTTON, + IMAGE, + CHECKBOX, + RADIO, + FRAME, + IMAGE_FRAME, + HIDDEN, + GRID, + VALUERANGE, + GENERIC_CONTROL, + TIME, + DATE, + + UNKNOWN // must be the last element + }; + + protected: + /** ctor. + <p>This default constructor is protected, 'cause this class is not intended to be instantiated + directly. Instead, the derived classes should be used.</p> + */ + OControlElement() { } + + public: + /** retrieves the tag name to be used to describe a control of the given type + + <p>The returned string is the pure element name, without any namespace.</p> + + @param _eType + the element type + */ + static const char* getElementName(ElementType _eType); + + /** retrieves the tag name to be used to describe a control of the given type + + <p>The returned string is the pure token, without any namespace.</p> + + @param _eType + the element type + */ + static sal_Int32 getElementToken(ElementType _eType); + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/controlpropertyhdl.cxx b/xmloff/source/forms/controlpropertyhdl.cxx new file mode 100644 index 000000000..cb3badee2 --- /dev/null +++ b/xmloff/source/forms/controlpropertyhdl.cxx @@ -0,0 +1,343 @@ +/* -*- 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 <xmloff/controlpropertyhdl.hxx> + +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/awt/TextAlign.hpp> +#include <com/sun/star/awt/FontEmphasisMark.hpp> + +#include <sax/tools/converter.hxx> + +#include <xmloff/xmltypes.hxx> +#include <xmloff/NamedBoolPropertyHdl.hxx> +#include "formenums.hxx" +#include <xmloff/xmluconv.hxx> +#include <xmloff/xmltoken.hxx> +#include <rtl/ustrbuf.hxx> +#include <xmloff/XMLConstantsPropertyHandler.hxx> + +namespace xmloff +{ + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::beans; + using namespace ::xmloff::token; + + //= OControlPropertyHandlerFactory + OControlPropertyHandlerFactory::OControlPropertyHandlerFactory() + { + } + + const XMLPropertyHandler* OControlPropertyHandlerFactory::GetPropertyHandler(sal_Int32 _nType) const + { + const XMLPropertyHandler* pHandler = nullptr; + + switch (_nType) + { + case XML_TYPE_TEXT_ALIGN: + if (!m_pTextAlignHandler) + m_pTextAlignHandler = std::make_unique<XMLConstantsPropertyHandler>(aTextAlignMap, XML_TOKEN_INVALID ); + pHandler = m_pTextAlignHandler.get(); + break; + + case XML_TYPE_CONTROL_BORDER: + if (!m_pControlBorderStyleHandler) + m_pControlBorderStyleHandler = std::make_unique<OControlBorderHandler>( OControlBorderHandler::STYLE ); + pHandler = m_pControlBorderStyleHandler.get(); + break; + + case XML_TYPE_CONTROL_BORDER_COLOR: + if ( !m_pControlBorderColorHandler ) + m_pControlBorderColorHandler = std::make_unique<OControlBorderHandler>( OControlBorderHandler::COLOR ); + pHandler = m_pControlBorderColorHandler.get(); + break; + + case XML_TYPE_ROTATION_ANGLE: + if (!m_pRotationAngleHandler) + m_pRotationAngleHandler = std::make_unique<ORotationAngleHandler>(); + pHandler = m_pRotationAngleHandler.get(); + break; + + case XML_TYPE_FONT_WIDTH: + if (!m_pFontWidthHandler) + m_pFontWidthHandler = std::make_unique<OFontWidthHandler>(); + pHandler = m_pFontWidthHandler.get(); + break; + + case XML_TYPE_CONTROL_TEXT_EMPHASIZE: + if (!m_pFontEmphasisHandler) + m_pFontEmphasisHandler = std::make_unique<XMLConstantsPropertyHandler>( aFontEmphasisMap, XML_NONE ); + pHandler = m_pFontEmphasisHandler.get(); + break; + + case XML_TYPE_TEXT_FONT_RELIEF: + if (!m_pFontReliefHandler) + m_pFontReliefHandler = std::make_unique<XMLConstantsPropertyHandler>( aFontReliefMap, XML_NONE ); + pHandler = m_pFontReliefHandler.get(); + break; + case XML_TYPE_TEXT_LINE_MODE: + if (!m_pTextLineModeHandler) + { + m_pTextLineModeHandler = std::make_unique<XMLNamedBoolPropertyHdl>( + ::xmloff::token::XML_SKIP_WHITE_SPACE, + ::xmloff::token::XML_CONTINUOUS); + } + pHandler = m_pTextLineModeHandler.get(); + break; + } + + if (!pHandler) + pHandler = XMLPropertyHandlerFactory::GetPropertyHandler(_nType); + return pHandler; + } + + //= OControlTextEmphasisHandler + OControlTextEmphasisHandler::OControlTextEmphasisHandler() + { + } + + bool OControlTextEmphasisHandler::exportXML( OUString& _rStrExpValue, const Any& _rValue, const SvXMLUnitConverter& ) const + { + bool bSuccess = false; + sal_Int16 nFontEmphasis = sal_Int16(); + if (_rValue >>= nFontEmphasis) + { + // the type + sal_uInt16 nType = nFontEmphasis & ~(awt::FontEmphasisMark::ABOVE | awt::FontEmphasisMark::BELOW); + // the position of the mark + bool bBelow = 0 != (nFontEmphasis & awt::FontEmphasisMark::BELOW); + + // convert + OUStringBuffer aReturn; + bSuccess = SvXMLUnitConverter::convertEnum(aReturn, nType, aFontEmphasisMap, XML_NONE); + if (bSuccess) + { + aReturn.append( ' ' ); + aReturn.append( GetXMLToken(bBelow ? XML_BELOW : XML_ABOVE) ); + + _rStrExpValue = aReturn.makeStringAndClear(); + } + } + + return bSuccess; + } + + bool OControlTextEmphasisHandler::importXML( const OUString& _rStrImpValue, Any& _rValue, const SvXMLUnitConverter& ) const + { + bool bSuccess = true; + sal_uInt16 nEmphasis = awt::FontEmphasisMark::NONE; + + bool bBelow = false; + bool bHasPos = false, bHasType = false; + + std::u16string_view sToken; + SvXMLTokenEnumerator aTokenEnum(_rStrImpValue); + while (aTokenEnum.getNextToken(sToken)) + { + if (!bHasPos) + { + if (IsXMLToken(sToken, XML_ABOVE)) + { + bBelow = false; + bHasPos = true; + } + else if (IsXMLToken(sToken, XML_BELOW)) + { + bBelow = true; + bHasPos = true; + } + } + if (!bHasType) + { + if (SvXMLUnitConverter::convertEnum(nEmphasis, sToken, aFontEmphasisMap)) + { + bHasType = true; + } + else + { + bSuccess = false; + break; + } + } + } + + if (bSuccess) + { + nEmphasis |= bBelow ? awt::FontEmphasisMark::BELOW : awt::FontEmphasisMark::ABOVE; + _rValue <<= nEmphasis; + } + + return bSuccess; + } + + //= OControlBorderHandlerBase + OControlBorderHandler::OControlBorderHandler( const OControlBorderHandler::BorderFacet _eFacet ) + :m_eFacet( _eFacet ) + { + } + + bool OControlBorderHandler::importXML( const OUString& _rStrImpValue, Any& _rValue, const SvXMLUnitConverter& ) const + { + std::u16string_view sToken; + SvXMLTokenEnumerator aTokens(_rStrImpValue); + + sal_uInt16 nStyle = 1; + + while ( aTokens.getNextToken(sToken) // have a new token + && (!sToken.empty()) // really have a new token + ) + { + // try interpreting the token as border style + if ( m_eFacet == STYLE ) + { + // is it a valid enum value? + if ( SvXMLUnitConverter::convertEnum( nStyle, sToken, aBorderTypeMap ) ) + { + _rValue <<= nStyle; + return true; + } + } + + // try interpreting it as color value + if ( m_eFacet == COLOR ) + { + sal_Int32 nColor(0); + if (::sax::Converter::convertColor( nColor, sToken )) + { + _rValue <<= nColor; + return true; + } + } + } + + return false; + } + + bool OControlBorderHandler::exportXML( OUString& _rStrExpValue, const Any& _rValue, const SvXMLUnitConverter& ) const + { + bool bSuccess = false; + + OUStringBuffer aOut; + switch ( m_eFacet ) + { + case STYLE: + { + sal_uInt16 nBorder = 0; + bSuccess = (_rValue >>= nBorder) + && SvXMLUnitConverter::convertEnum( aOut, nBorder, aBorderTypeMap ); + } + break; + case COLOR: + { + sal_Int32 nBorderColor = 0; + if ( _rValue >>= nBorderColor ) + { + ::sax::Converter::convertColor(aOut, nBorderColor); + bSuccess = true; + } + } + break; + } // switch ( m_eFacet ) + + if ( !bSuccess ) + return false; + + if ( !_rStrExpValue.isEmpty() ) + _rStrExpValue += " "; + _rStrExpValue += aOut; + + return true; + } + + //= OFontWidthHandler + OFontWidthHandler::OFontWidthHandler() + { + } + + bool OFontWidthHandler::importXML( const OUString& _rStrImpValue, Any& _rValue, const SvXMLUnitConverter& ) const + { + sal_Int32 nWidth = 0; + bool const bSuccess = ::sax::Converter::convertMeasure( + nWidth, _rStrImpValue, util::MeasureUnit::POINT); + if (bSuccess) + _rValue <<= static_cast<sal_Int16>(nWidth); + + return bSuccess; + } + + bool OFontWidthHandler::exportXML( OUString& _rStrExpValue, const Any& _rValue, const SvXMLUnitConverter& ) const + { + sal_Int16 nWidth = 0; + OUStringBuffer aResult; + if (_rValue >>= nWidth) + { + ::sax::Converter::convertMeasure(aResult, nWidth, + util::MeasureUnit::POINT, util::MeasureUnit::POINT); + } + _rStrExpValue = aResult.makeStringAndClear(); + + return !_rStrExpValue.isEmpty(); + } + + //= ORotationAngleHandler + ORotationAngleHandler::ORotationAngleHandler() + { + } + + bool ORotationAngleHandler::importXML( const OUString& _rStrImpValue, Any& _rValue, const SvXMLUnitConverter& ) const + { + double fValue; + bool const bSucces = + ::sax::Converter::convertDouble(fValue, _rStrImpValue); + if (bSucces) + { + fValue *= 10; + _rValue <<= static_cast<float>(fValue); + } + + return bSucces; + } + + bool ORotationAngleHandler::exportXML( OUString& _rStrExpValue, const Any& _rValue, const SvXMLUnitConverter& ) const + { + float fAngle = 0; + bool bSuccess = (_rValue >>= fAngle); + + if (bSuccess) + { + OUStringBuffer sValue; + ::sax::Converter::convertDouble(sValue, static_cast<double>(fAngle) / 10); + _rStrExpValue = sValue.makeStringAndClear(); + } + + return bSuccess; + } + + //= ImageScaleModeHandler + ImageScaleModeHandler::ImageScaleModeHandler() + :XMLConstantsPropertyHandler( aScaleModeMap, XML_STRETCH ) + { + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/controlpropertymap.cxx b/xmloff/source/forms/controlpropertymap.cxx new file mode 100644 index 000000000..d0716bd8d --- /dev/null +++ b/xmloff/source/forms/controlpropertymap.cxx @@ -0,0 +1,119 @@ +/* -*- 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 <rtl/ref.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/maptype.hxx> +#include <xmloff/xmltypes.hxx> +#include <algorithm> +#include "strings.hxx" +#include <xmloff/contextid.hxx> +#include "controlpropertymap.hxx" + +#include <string.h> + +using namespace ::xmloff::token; + +namespace xmloff +{ + +#define MAP_ASCII( name, prefix, token, type, context ) { name, prefix, token, type|XML_TYPE_PROP_TEXT, context, SvtSaveOptions::ODFSVER_010, false } +#define MAP_CONST( name, prefix, token, type, context ) { name, prefix, token, type|XML_TYPE_PROP_TEXT, context, SvtSaveOptions::ODFSVER_010, false } +#define MAP_CONST_P( name, prefix, token, type, context ){ name, prefix, token, type|XML_TYPE_PROP_PARAGRAPH, context, SvtSaveOptions::ODFSVER_010, false } +#define MAP_END() { nullptr, 0, XML_TOKEN_INVALID, 0, 0, SvtSaveOptions::ODFSVER_010, false } + + XMLPropertyMapEntry const aControlStyleProperties[] = + { + MAP_CONST_P( PROPERTY_ALIGN, XML_NAMESPACE_FO, XML_TEXT_ALIGN, XML_TYPE_TEXT_ALIGN, 0 ), + MAP_CONST( PROPERTY_BACKGROUNDCOLOR, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_COLOR, 0 ), + MAP_CONST( PROPERTY_BORDER, XML_NAMESPACE_FO, XML_BORDER, XML_TYPE_CONTROL_BORDER|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ), + MAP_ASCII( "BorderColor", XML_NAMESPACE_FO, XML_BORDER, XML_TYPE_CONTROL_BORDER_COLOR|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ), + MAP_ASCII( "FontCharWidth", XML_NAMESPACE_STYLE, XML_FONT_CHAR_WIDTH, XML_TYPE_NUMBER16, 0 ), + MAP_ASCII( "FontCharset", XML_NAMESPACE_STYLE, XML_FONT_CHARSET, XML_TYPE_TEXT_FONTENCODING, 0 ), + MAP_ASCII( "FontEmphasisMark", XML_NAMESPACE_STYLE, XML_TEXT_EMPHASIZE, XML_TYPE_CONTROL_TEXT_EMPHASIZE, 0 ), + MAP_ASCII( "FontFamily", XML_NAMESPACE_STYLE, XML_FONT_FAMILY_GENERIC, XML_TYPE_TEXT_FONTFAMILY, 0 ), + MAP_ASCII( "FontHeight", XML_NAMESPACE_FO, XML_FONT_SIZE, XML_TYPE_CHAR_HEIGHT, 0 ), + MAP_ASCII( "FontKerning", XML_NAMESPACE_STYLE, XML_LETTER_KERNING, XML_TYPE_BOOL, 0 ), + MAP_ASCII( "FontName", XML_NAMESPACE_STYLE, XML_FONT_NAME, XML_TYPE_STRING, 0 ), + MAP_ASCII( "FontOrientation", XML_NAMESPACE_STYLE, XML_ROTATION_ANGLE, XML_TYPE_ROTATION_ANGLE, 0 ), + MAP_ASCII( "FontPitch", XML_NAMESPACE_STYLE, XML_FONT_PITCH, XML_TYPE_TEXT_FONTPITCH, 0 ), + MAP_ASCII( "FontRelief", XML_NAMESPACE_STYLE, XML_FONT_RELIEF, XML_TYPE_TEXT_FONT_RELIEF|MID_FLAG_MULTI_PROPERTY, 0 ), + MAP_ASCII( "FontSlant", XML_NAMESPACE_FO, XML_FONT_STYLE, XML_TYPE_TEXT_POSTURE, 0 ), + + MAP_ASCII( "FontStrikeout", XML_NAMESPACE_STYLE, XML_TEXT_LINE_THROUGH_STYLE, XML_TYPE_TEXT_CROSSEDOUT_STYLE|MID_FLAG_MERGE_PROPERTY, 0), + MAP_ASCII( "FontStrikeout", XML_NAMESPACE_STYLE, XML_TEXT_LINE_THROUGH_TYPE, XML_TYPE_TEXT_CROSSEDOUT_TYPE|MID_FLAG_MERGE_PROPERTY, 0), + MAP_ASCII( "FontStrikeout", XML_NAMESPACE_STYLE, XML_TEXT_LINE_THROUGH_WIDTH, XML_TYPE_TEXT_CROSSEDOUT_WIDTH|MID_FLAG_MERGE_PROPERTY, 0), + MAP_ASCII( "FontStrikeout", XML_NAMESPACE_STYLE, XML_TEXT_LINE_THROUGH_TEXT, XML_TYPE_TEXT_CROSSEDOUT_TEXT|MID_FLAG_MERGE_PROPERTY, 0), + + MAP_ASCII( "FontStyleName", XML_NAMESPACE_STYLE, XML_FONT_STYLE_NAME, XML_TYPE_STRING, 0 ), + MAP_ASCII( "FontUnderline", XML_NAMESPACE_STYLE, XML_TEXT_UNDERLINE_STYLE, XML_TYPE_TEXT_UNDERLINE_STYLE|MID_FLAG_MERGE_PROPERTY, 0 ), + MAP_ASCII( "FontUnderline", XML_NAMESPACE_STYLE, XML_TEXT_UNDERLINE_TYPE, XML_TYPE_TEXT_UNDERLINE_TYPE|MID_FLAG_MERGE_PROPERTY, 0 ), + MAP_ASCII( "FontUnderline", XML_NAMESPACE_STYLE, XML_TEXT_UNDERLINE_WIDTH, XML_TYPE_TEXT_UNDERLINE_WIDTH|MID_FLAG_MERGE_PROPERTY, 0 ), + MAP_ASCII( "FontWeight", XML_NAMESPACE_FO, XML_FONT_WEIGHT, XML_TYPE_TEXT_WEIGHT, 0 ), + MAP_ASCII( "FontWidth", XML_NAMESPACE_STYLE, XML_FONT_WIDTH, XML_TYPE_FONT_WIDTH, 0 ), + MAP_ASCII( "FontWordLineMode", XML_NAMESPACE_FO, XML_SCORE_SPACES, XML_TYPE_NBOOL, 0 ), + + MAP_CONST( PROPERTY_FORMATKEY, XML_NAMESPACE_STYLE, XML_DATA_STYLE_NAME, XML_TYPE_STRING | MID_FLAG_NO_PROPERTY_EXPORT | MID_FLAG_SPECIAL_ITEM, CTF_FORMS_DATA_STYLE ), + + MAP_ASCII( "SymbolColor", XML_NAMESPACE_STYLE, XML_COLOR, XML_TYPE_COLOR, 0 ), + MAP_ASCII( "TextColor", XML_NAMESPACE_FO, XML_COLOR, XML_TYPE_COLOR, 0 ), + MAP_ASCII( "TextLineColor", XML_NAMESPACE_STYLE, XML_TEXT_UNDERLINE_COLOR, XML_TYPE_TEXT_UNDERLINE_COLOR|MID_FLAG_MULTI_PROPERTY, 0 ), + MAP_END() + }; + + const XMLPropertyMapEntry* getControlStylePropertyMap( ) + { + return aControlStyleProperties; + } + + void initializePropertyMaps() + { + static bool bSorted = false; + if (!bSorted) + { + XMLPropertyMapEntry const * pEnd; + // determine the last element + for ( pEnd = aControlStyleProperties; pEnd->msApiName; ++pEnd) + ; + assert( ::std::is_sorted(aControlStyleProperties, pEnd, + [](const XMLPropertyMapEntry& _rLeft, const XMLPropertyMapEntry& _rRight) + { return strcmp(_rLeft.msApiName, _rRight.msApiName) < 0; }) ); + bSorted = true; + } + } + + //= OFormComponentStyleExportMapper + OFormComponentStyleExportMapper::OFormComponentStyleExportMapper( const rtl::Reference< XMLPropertySetMapper >& _rMapper ) + :SvXMLExportPropertyMapper( _rMapper ) + { + } + + void OFormComponentStyleExportMapper::handleSpecialItem( SvXMLAttributeList& _rAttrList, const XMLPropertyState& _rProperty, const SvXMLUnitConverter& _rUnitConverter, + const SvXMLNamespaceMap& _rNamespaceMap, const ::std::vector< XMLPropertyState >* _pProperties, + sal_uInt32 _nIdx ) const + { + // ignore the number style of grid columns - this is formatted elsewhere + if ( CTF_FORMS_DATA_STYLE != getPropertySetMapper()->GetEntryContextId( _rProperty.mnIndex ) ) + SvXMLExportPropertyMapper::handleSpecialItem( _rAttrList, _rProperty, _rUnitConverter, _rNamespaceMap, _pProperties, _nIdx ); + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/controlpropertymap.hxx b/xmloff/source/forms/controlpropertymap.hxx new file mode 100644 index 000000000..ff575c137 --- /dev/null +++ b/xmloff/source/forms/controlpropertymap.hxx @@ -0,0 +1,51 @@ +/* -*- 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 . + */ + +#pragma once + +#include <xmloff/xmlprmap.hxx> +#include <xmloff/xmlexppr.hxx> + +struct XMLPropertyMapEntry; +namespace xmloff +{ + + const XMLPropertyMapEntry* getControlStylePropertyMap( ); + + void initializePropertyMaps(); + + //= OFormComponentStyleExportMapper + class OFormComponentStyleExportMapper : public SvXMLExportPropertyMapper + { + public: + explicit OFormComponentStyleExportMapper( const rtl::Reference< XMLPropertySetMapper >& _rMapper ); + + void handleSpecialItem( + SvXMLAttributeList& _rAttrList, + const XMLPropertyState& _rProperty, + const SvXMLUnitConverter& _rUnitConverter, + const SvXMLNamespaceMap& _rNamespaceMap, + const ::std::vector< XMLPropertyState >* _pProperties, + sal_uInt32 _nIdx + ) const override; + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/elementexport.cxx b/xmloff/source/forms/elementexport.cxx new file mode 100644 index 000000000..72cb6e0ef --- /dev/null +++ b/xmloff/source/forms/elementexport.cxx @@ -0,0 +1,2185 @@ +/* -*- 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 "elementexport.hxx" + +#include "strings.hxx" +#include <xmloff/xmlnamespace.hxx> +#include "eventexport.hxx" +#include "formenums.hxx" +#include "formcellbinding.hxx" +#include <xmloff/xformsexport.hxx> +#include "property_meta_data.hxx" + +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/io/XPersistObject.hpp> +#include <com/sun/star/util/Duration.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/form/FormSubmitEncoding.hpp> +#include <com/sun/star/form/FormSubmitMethod.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/form/NavigationBarMode.hpp> +#include <com/sun/star/form/TabulatorCycle.hpp> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/form/ListSourceType.hpp> +#include <com/sun/star/awt/ImagePosition.hpp> + +#include <sax/tools/converter.hxx> +#include <tools/gen.hxx> +#include <xmloff/txtprmap.hxx> +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <tools/urlobj.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/XMLEventExport.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/maptype.hxx> +#include <tools/time.hxx> +#include <tools/diagnose_ex.h> +#include <comphelper/extract.hxx> +#include <sal/macros.h> +#include <sal/log.hxx> + +#include <algorithm> +#include <string_view> + +namespace xmloff +{ + + #if OSL_DEBUG_LEVEL > 0 + #define RESET_BIT( bitfield, bit ) \ + bitfield = bitfield & ~bit + #else + #define RESET_BIT( bitfield, bit ) + #endif + + using namespace ::xmloff::token; + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::io; + using namespace ::com::sun::star::table; + using namespace ::com::sun::star::text; + using namespace ::com::sun::star::form::binding; + + //= OElementExport + OElementExport::OElementExport(IFormsExportContext& _rContext, const Reference< XPropertySet >& _rxProps, + const Sequence< ScriptEventDescriptor >& _rEvents) + :OPropertyExport(_rContext, _rxProps) + ,m_aEvents(_rEvents) + { + } + + OElementExport::~OElementExport() + { + } + + void OElementExport::doExport() + { + // collect some general information about the element + examine(); + + // first add the attributes necessary for the element + m_rContext.getGlobalContext().ClearAttrList(); + + // add the attributes + exportAttributes(); + + // start the XML element + implStartElement(getXMLElementName()); + + // the sub elements (mostly control type dependent) + exportSubTags(); + + implEndElement(); + } + + void OElementExport::examine() + { + // nothing to do here + } + + void OElementExport::exportAttributes() + { + // nothing to do here + } + + void OElementExport::exportSubTags() + { + // the properties which where not exported 'til now + exportRemainingProperties(); + + // the script:events sub tags + exportEvents(); + } + + void OElementExport::implStartElement(const char* _pName) + { + m_pXMLElement = std::make_unique<SvXMLElementExport>(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, _pName, true, true); + } + + void OElementExport::implEndElement() + { + m_pXMLElement.reset(); + } + + void OElementExport::exportServiceNameAttribute() + { + Reference< XPersistObject > xPersistence(m_xProps, UNO_QUERY); + if (!xPersistence.is()) + { + OSL_FAIL("OElementExport::exportServiceNameAttribute: no XPersistObject!"); + return; + } + + OUString sServiceName = xPersistence->getServiceName(); + // we don't want to write the old service name directly: it's a name used for compatibility reasons, but + // as we start some kind of new file format here (with this xml export), we don't care about + // compatibility ... + // So we translate the old persistence service name into new ones, if possible + + OUString sToWriteServiceName = sServiceName; +#define CHECK_N_TRANSLATE( name ) \ + else if (sServiceName == SERVICE_PERSISTENT_COMPONENT_##name) \ + sToWriteServiceName = SERVICE_##name + + if (sServiceName == SERVICE_PERSISTENT_COMPONENT_EDIT) + { + // special handling for the edit field: we have two controls using this as persistence service name + sToWriteServiceName = SERVICE_EDIT; + Reference< XServiceInfo > xSI(m_xProps, UNO_QUERY); + if (xSI.is() && xSI->supportsService(SERVICE_FORMATTEDFIELD)) + sToWriteServiceName = SERVICE_FORMATTEDFIELD; + } + CHECK_N_TRANSLATE( FORM ); + CHECK_N_TRANSLATE( LISTBOX ); + CHECK_N_TRANSLATE( COMBOBOX ); + CHECK_N_TRANSLATE( RADIOBUTTON ); + CHECK_N_TRANSLATE( GROUPBOX ); + CHECK_N_TRANSLATE( FIXEDTEXT ); + CHECK_N_TRANSLATE( COMMANDBUTTON ); + CHECK_N_TRANSLATE( CHECKBOX ); + CHECK_N_TRANSLATE( GRID ); + CHECK_N_TRANSLATE( IMAGEBUTTON ); + CHECK_N_TRANSLATE( FILECONTROL ); + CHECK_N_TRANSLATE( TIMEFIELD ); + CHECK_N_TRANSLATE( DATEFIELD ); + CHECK_N_TRANSLATE( NUMERICFIELD ); + CHECK_N_TRANSLATE( CURRENCYFIELD ); + CHECK_N_TRANSLATE( PATTERNFIELD ); + CHECK_N_TRANSLATE( HIDDENCONTROL ); + CHECK_N_TRANSLATE( IMAGECONTROL ); + CHECK_N_TRANSLATE( FORMATTEDFIELD ); +#if OSL_DEBUG_LEVEL > 0 + Reference< XServiceInfo > xSI(m_xProps, UNO_QUERY); + OSL_ENSURE(xSI.is() && xSI->supportsService(sToWriteServiceName), + "OElementExport::exportServiceNameAttribute: wrong service name translation!"); + +#endif + sToWriteServiceName = + m_rContext.getGlobalContext().GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_OOO, sToWriteServiceName ); + + // now write this + AddAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::ServiceName), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::ServiceName), + sToWriteServiceName); + } + + void OElementExport::exportEvents() + { + if (!m_aEvents.hasElements()) + // nothing to do + return; + + Reference< XNameReplace > xWrapper = new OEventDescriptorMapper(m_aEvents); + m_rContext.getGlobalContext().GetEventExport().Export(xWrapper); + } + + //= OControlExport + OControlExport::OControlExport(IFormsExportContext& _rContext, const Reference< XPropertySet >& _rxControl, + const OUString& _rControlId, const OUString& _rReferringControls, + const Sequence< ScriptEventDescriptor >& _rEvents) + :OElementExport(_rContext, _rxControl, _rEvents) + ,m_sControlId(_rControlId) + ,m_sReferringControls(_rReferringControls) + ,m_nClassId(FormComponentType::CONTROL) + ,m_eType( UNKNOWN ) + ,m_nIncludeCommon(CCAFlags::NONE) + ,m_nIncludeDatabase(DAFlags::NONE) + ,m_nIncludeSpecial(SCAFlags::NONE) + ,m_nIncludeEvents(EAFlags::NONE) + ,m_nIncludeBindings(BAFlags::NONE) + { + OSL_ENSURE(m_xProps.is(), "OControlExport::OControlExport: invalid arguments!"); + } + + void OControlExport::exportOuterAttributes() + { + // the control id + if (CCAFlags::Name & m_nIncludeCommon) + { + exportStringPropertyAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Name), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Name), + PROPERTY_NAME + ); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::Name; + #endif + } + + // the service name + if (m_nIncludeCommon & CCAFlags::ServiceName) + { + exportServiceNameAttribute(); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::ServiceName; + #endif + } + } + + void OControlExport::exportInnerAttributes() + { + // the control id + if (CCAFlags::ControlId & m_nIncludeCommon) + { + OSL_ENSURE(!m_sControlId.isEmpty(), "OControlExport::exportInnerAttributes: have no control id for the control!"); + m_rContext.getGlobalContext().AddAttributeIdLegacy( + XML_NAMESPACE_FORM, m_sControlId); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::ControlId; + #endif + } + + // "new-style" properties ... + exportGenericHandlerAttributes(); + + // common control attributes + exportCommonControlAttributes(); + + // common database attributes + exportDatabaseAttributes(); + + // attributes related to external bindings + exportBindingAttributes(); + + // attributes special to the respective control type + exportSpecialAttributes(); + + // add the style references to the attributes + flagStyleProperties(); + } + + void OControlExport::exportAttributes() + { + exportOuterAttributes(); + } + + void OControlExport::exportSubTags() + { + // for the upcoming exportRemainingProperties: + // if a control has the LabelControl property, this is not stored with the control itself, but instead with + // the control which is referenced by this property. As the base class' exportRemainingProperties doesn't + // know anything about this, we need to prevent that it tries to export this property + exportedProperty(PROPERTY_CONTROLLABEL); + + // if it's a control supporting XText, then we need to declare all text-related properties + // as "already exported". This prevents them from being exported as generic "form:property"-tags. + // *If* we would export them this way, they would be completely superfluous, and sometimes even + // disastrous, since they may, at import time, override paragraph properties which already have + // been set before + Reference< XText > xControlText( m_xProps, UNO_QUERY ); + if ( xControlText.is() ) + { + const XMLPropertyMapEntry* pCharAttributeProperties = XMLTextPropertySetMapper::getPropertyMapForType( TextPropMap::TEXT ); + while ( pCharAttributeProperties->msApiName ) + { + exportedProperty( OUString::createFromAscii( pCharAttributeProperties->msApiName ) ); + ++pCharAttributeProperties; + } + + const XMLPropertyMapEntry* pParaAttributeProperties = XMLTextPropertySetMapper::getPropertyMapForType( TextPropMap::SHAPE_PARA ); + while ( pParaAttributeProperties->msApiName ) + { + exportedProperty( OUString::createFromAscii( pParaAttributeProperties->msApiName ) ); + ++pParaAttributeProperties; + } + + // the RichText property is not exported. The presence of the text:p element + // will be used - upon reading - as indicator for the value of the RichText property + exportedProperty( PROPERTY_RICH_TEXT ); + + // strange thing: paragraphs support both a CharStrikeout and a CharCrossedOut property + // The former is a short/enum value, the latter a boolean. The former has a real meaning + // (the strikeout type), the latter hasn't. But, when the CharCrossedOut is exported and + // later on imported, it overwrites anything which has previously been imported for + // CharStrikeout. + // #i27729# + exportedProperty( "CharCrossedOut" ); + } + + if ( m_eType == LISTBOX ) + { + // will be exported in exportListSourceAsElements: + if ( controlHasUserSuppliedListEntries() ) + exportedProperty( PROPERTY_DEFAULT_SELECT_SEQ ); + + // will not be exported in a generic way. Either exportListSourceAsElements cares + // for them, or we don't need them + exportedProperty( PROPERTY_STRING_ITEM_LIST ); + exportedProperty( PROPERTY_VALUE_SEQ ); + exportedProperty( PROPERTY_SELECT_SEQ ); + exportedProperty( PROPERTY_LISTSOURCE ); + } + if ( m_eType == COMBOBOX ) + exportedProperty( PROPERTY_STRING_ITEM_LIST ); + + // let the base class export the remaining properties and the events + OElementExport::exportSubTags(); + + // special sub tags for some controls + switch (m_eType) + { + case LISTBOX: + // don't export the list entries if the are not provided by the user, but obtained implicitly + // from other sources + // #i26944# + if ( controlHasUserSuppliedListEntries() ) + exportListSourceAsElements(); + break; + case GRID: + { // a grid control requires us to store all columns as sub elements + Reference< XIndexAccess > xColumnContainer(m_xProps, UNO_QUERY); + OSL_ENSURE(xColumnContainer.is(), "OControlExport::exportSubTags: a grid control which is no IndexAccess?!!"); + if (xColumnContainer.is()) + m_rContext.exportCollectionElements(xColumnContainer); + } + break; + case COMBOBOX: + { // a combox box description has sub elements: the items + DBG_CHECK_PROPERTY( PROPERTY_STRING_ITEM_LIST, Sequence< OUString > ); + + // don't export the list entries if the are not provided by the user, but obtained implicitly + // from other sources + // #i26944# + if ( controlHasUserSuppliedListEntries() ) + { + // get the item list + Sequence< OUString > aListItems; + m_xProps->getPropertyValue(PROPERTY_STRING_ITEM_LIST) >>= aListItems; + // loop through it and write the sub elements + for (const auto& rListItem : std::as_const(aListItems)) + { + m_rContext.getGlobalContext().ClearAttrList(); + AddAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Label), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Label), + rListItem); + SvXMLElementExport aFormElement(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, "item", true, true); + } + } + } + break; + + case TEXT_AREA: + { + // if we act as rich text control, we need to export some text:p elements + if ( xControlText.is() ) + { + bool bActingAsRichText = false; + if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_RICH_TEXT ) ) + { + OSL_VERIFY(m_xProps->getPropertyValue( PROPERTY_RICH_TEXT ) >>= bActingAsRichText ); + } + + if ( bActingAsRichText ) + m_rContext.getGlobalContext().GetTextParagraphExport()->exportText( xControlText ); + } + } + break; + default: + // nothing do to + break; + } + } + + void OControlExport::exportGenericHandlerAttributes() + { + const Sequence< Property > aProperties = m_xPropertyInfo->getProperties(); + for ( auto const & prop : aProperties ) + { + try + { + // see if this property can already be handled with an IPropertyHandler (which, on the long + // term, should be the case for most, if not all, properties) + const PropertyDescription* propDescription = metadata::getPropertyDescription( prop.Name ); + if ( propDescription == nullptr ) + continue; + + // let the factory provide the concrete handler. Note that caching, if desired, is the task + // of the factory + PPropertyHandler handler = (*propDescription->factory)( propDescription->propertyId ); + if ( !handler ) + { + SAL_WARN( "xmloff.forms", "OControlExport::exportGenericHandlerAttributes: invalid property handler provided by the factory!" ); + continue; + } + + // that's a property which has a direct mapping to an attribute + if ( !shouldExportProperty( prop.Name ) ) + // TODO: in the future, we surely need a more sophisticated approach to this, involving the property + // handler, or the property description + { + exportedProperty( prop.Name ); + continue; + } + + const Any propValue = m_xProps->getPropertyValue( prop.Name ); + OUString attributeValue = handler->getAttributeValue( propValue ); + + AddAttribute( + propDescription->attribute.namespacePrefix, + token::GetXMLToken( propDescription->attribute.attributeToken ), + attributeValue + ); + + exportedProperty( prop.Name ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.forms"); + } + } + } + + void OControlExport::exportCommonControlAttributes() + { + size_t i=0; + + // I decided to handle all the properties here with some static arrays describing the property-attribute + // relations. This leads to somewhat ugly code :), but the only alternative I can think of right now + // would require maps and O(log n) searches, which seems somewhat expensive as this code is used + // very frequently. + + // the extra indents for the respective blocks are to ensure that there is no copy'n'paste error, using + // map identifiers from the wrong block + + // some string properties + { + // the attribute ids of all properties which are expected to be of type string + static const CCAFlags nStringPropertyAttributeIds[] = + { + CCAFlags::Label, CCAFlags::Title + }; + // the names of all properties which are expected to be of type string + static const char * aStringPropertyNames[] = + { + PROPERTY_LABEL, PROPERTY_TITLE + }; + OSL_ENSURE( SAL_N_ELEMENTS(aStringPropertyNames) == + SAL_N_ELEMENTS(nStringPropertyAttributeIds), + "OControlExport::exportCommonControlAttributes: somebody tampered with the maps (1)!"); + + for (i=0; i<SAL_N_ELEMENTS(nStringPropertyAttributeIds); ++i) + if (nStringPropertyAttributeIds[i] & m_nIncludeCommon) + { + exportStringPropertyAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(nStringPropertyAttributeIds[i]), + OAttributeMetaData::getCommonControlAttributeName(nStringPropertyAttributeIds[i]), + OUString::createFromAscii(aStringPropertyNames[i]) + ); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~nStringPropertyAttributeIds[i]; + #endif + } + } + + // some boolean properties + { + static const CCAFlags nBooleanPropertyAttributeIds[] = + { // attribute flags + CCAFlags::CurrentSelected, CCAFlags::Disabled, CCAFlags::Dropdown, CCAFlags::Printable, CCAFlags::ReadOnly, CCAFlags::Selected, CCAFlags::TabStop, CCAFlags::EnableVisible + }; + static const char * pBooleanPropertyNames[] = + { // property names + PROPERTY_STATE, PROPERTY_ENABLED, + PROPERTY_DROPDOWN, PROPERTY_PRINTABLE, + PROPERTY_READONLY, PROPERTY_DEFAULT_STATE, + PROPERTY_TABSTOP, PROPERTY_ENABLEVISIBLE + }; + static const BoolAttrFlags nBooleanPropertyAttrFlags[] = + { // attribute defaults + BoolAttrFlags::DefaultFalse, BoolAttrFlags::DefaultFalse | BoolAttrFlags::InverseSemantics, BoolAttrFlags::DefaultFalse, BoolAttrFlags::DefaultTrue, BoolAttrFlags::DefaultFalse, BoolAttrFlags::DefaultFalse, BoolAttrFlags::DefaultVoid, BoolAttrFlags::DefaultFalse + }; + #if OSL_DEBUG_LEVEL > 0 + static const sal_Int32 nIdCount = SAL_N_ELEMENTS(nBooleanPropertyAttributeIds); + static const sal_Int32 nNameCount = SAL_N_ELEMENTS(pBooleanPropertyNames); + static const sal_Int32 nFlagsCount = SAL_N_ELEMENTS(nBooleanPropertyAttrFlags); + OSL_ENSURE((nIdCount == nNameCount) && (nNameCount == nFlagsCount), + "OControlExport::exportCommonControlAttributes: somebody tampered with the maps (2)!"); + #endif + for (i=0; i<SAL_N_ELEMENTS(nBooleanPropertyAttributeIds); ++i) + if (nBooleanPropertyAttributeIds[i] & m_nIncludeCommon) + { + exportBooleanPropertyAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(nBooleanPropertyAttributeIds[i]), + OAttributeMetaData::getCommonControlAttributeName(nBooleanPropertyAttributeIds[i]), + OUString::createFromAscii(pBooleanPropertyNames[i]), + nBooleanPropertyAttrFlags[i]); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~nBooleanPropertyAttributeIds[i]; + #endif + } + } + + // some integer properties + { + // now the common handling + static const CCAFlags nIntegerPropertyAttributeIds[] = + { // attribute flags + CCAFlags::Size, CCAFlags::TabIndex + }; + static const char * pIntegerPropertyNames[] = + { // property names + PROPERTY_LINECOUNT, PROPERTY_TABINDEX + }; + static const sal_Int16 nIntegerPropertyAttrDefaults[] = + { // attribute defaults + 5, 0 + }; + + if ( m_nIncludeCommon & CCAFlags::MaxLength ) + exportedProperty(PROPERTY_MAXTEXTLENGTH); + + #if OSL_DEBUG_LEVEL > 0 + static const sal_Int32 nIdCount = SAL_N_ELEMENTS(nIntegerPropertyAttributeIds); + static const sal_Int32 nNameCount = SAL_N_ELEMENTS(pIntegerPropertyNames); + static const sal_Int32 nDefaultCount = SAL_N_ELEMENTS(nIntegerPropertyAttrDefaults); + OSL_ENSURE((nIdCount == nNameCount) && (nNameCount == nDefaultCount), + "OControlExport::exportCommonControlAttributes: somebody tampered with the maps (3)!"); + #endif + for (i=0; i<SAL_N_ELEMENTS(nIntegerPropertyAttributeIds); ++i) + if (nIntegerPropertyAttributeIds[i] & m_nIncludeCommon) + { + exportInt16PropertyAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(nIntegerPropertyAttributeIds[i]), + OAttributeMetaData::getCommonControlAttributeName(nIntegerPropertyAttributeIds[i]), + OUString::createFromAscii(pIntegerPropertyNames[i]), + nIntegerPropertyAttrDefaults[i]); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~nIntegerPropertyAttributeIds[i]; + #endif + } + + } + + // some enum properties + { + if (m_nIncludeCommon & CCAFlags::ButtonType) + { + exportEnumPropertyAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::ButtonType), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::ButtonType), + PROPERTY_BUTTONTYPE, + aFormButtonTypeMap, + FormButtonType_PUSH); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::ButtonType; + #endif + } + if ( m_nIncludeCommon & CCAFlags::Orientation ) + { + exportEnumPropertyAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace( CCAFlags::Orientation ), + OAttributeMetaData::getCommonControlAttributeName( CCAFlags::Orientation ), + PROPERTY_ORIENTATION, + aOrientationMap, + ScrollBarOrientation::HORIZONTAL + ); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::Orientation; + #endif + } + + if ( m_nIncludeCommon & CCAFlags::VisualEffect ) + { + exportEnumPropertyAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace( CCAFlags::VisualEffect ), + OAttributeMetaData::getCommonControlAttributeName( CCAFlags::VisualEffect ), + PROPERTY_VISUAL_EFFECT, + aVisualEffectMap, + VisualEffect::LOOK3D + ); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::VisualEffect; + #endif + } + } + + // some properties which require a special handling + + // the target frame + if (m_nIncludeCommon & CCAFlags::TargetFrame) + { + exportTargetFrameAttribute(); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::TargetFrame; + #endif + } + + // max text length + if ( m_nIncludeCommon & CCAFlags::MaxLength ) + { + // normally, the respective property would be "MaxTextLen" + // However, if the model has a property "PersistenceMaxTextLength", then we prefer this + + // determine the name of the property to export + OUString sTextLenPropertyName( PROPERTY_MAXTEXTLENGTH ); + if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_PERSISTENCE_MAXTEXTLENGTH ) ) + sTextLenPropertyName = PROPERTY_PERSISTENCE_MAXTEXTLENGTH; + + // export it + exportInt16PropertyAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace( CCAFlags::MaxLength ), + OAttributeMetaData::getCommonControlAttributeName( CCAFlags::MaxLength ), + sTextLenPropertyName, + 0 + ); + + // in either way, both properties count as "exported" + exportedProperty( PROPERTY_MAXTEXTLENGTH ); + exportedProperty( PROPERTY_PERSISTENCE_MAXTEXTLENGTH ); + + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::MaxLength; + #endif + } + + if (m_nIncludeCommon & CCAFlags::TargetLocation) + { + exportTargetLocationAttribute(false); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::TargetLocation; + #endif + } + + // OJ #99721# + if (m_nIncludeCommon & CCAFlags::ImageData) + { + exportImageDataAttribute(); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::ImageData; + #endif + } + + // the for attribute + // the target frame + if (m_nIncludeCommon & CCAFlags::For) + { + if (!m_sReferringControls.isEmpty()) + { // there is at least one control referring to the one we're handling currently + AddAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::For), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::For), + m_sReferringControls); + } + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::For; + #endif + } + + if ((CCAFlags::CurrentValue | CCAFlags::Value) & m_nIncludeCommon) + { + const char* pCurrentValuePropertyName = nullptr; + const char* pValuePropertyName = nullptr; + + // get the property names + getValuePropertyNames(m_eType, m_nClassId, pCurrentValuePropertyName, pValuePropertyName); + + // add the attributes if necessary and possible + if (pCurrentValuePropertyName && (CCAFlags::CurrentValue & m_nIncludeCommon)) + { + static const OUString pCurrentValueAttributeName = OAttributeMetaData::getCommonControlAttributeName(CCAFlags::CurrentValue); + // don't export the current-value if this value originates from a data binding + // #i26944# + if ( controlHasActiveDataBinding() ) + exportedProperty( OUString::createFromAscii( pCurrentValuePropertyName ) ); + else + { + static const sal_uInt16 nCurrentValueAttributeNamespaceKey = OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::CurrentValue); + exportGenericPropertyAttribute( + nCurrentValueAttributeNamespaceKey, + pCurrentValueAttributeName, + pCurrentValuePropertyName + ); + } + } + + if (pValuePropertyName && (CCAFlags::Value & m_nIncludeCommon)) + { + static const OUString pValueAttributeName = OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Value); + static const sal_uInt16 nValueAttributeNamespaceKey = OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Value); + exportGenericPropertyAttribute( + nValueAttributeNamespaceKey, + pValueAttributeName, + pValuePropertyName); + } + + OSL_ENSURE((nullptr == pValuePropertyName) == (CCAFlags::NONE == (CCAFlags::Value & m_nIncludeCommon)), + "OControlExport::exportCommonControlAttributes: no property found for the value attribute!"); + OSL_ENSURE((nullptr == pCurrentValuePropertyName ) == (CCAFlags::NONE == (CCAFlags::CurrentValue & m_nIncludeCommon)), + "OControlExport::exportCommonControlAttributes: no property found for the current-value attribute!"); + + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags(CCAFlags::CurrentValue | CCAFlags::Value); + #endif + } + + OSL_ENSURE(CCAFlags::NONE == m_nIncludeCommon, + "OControlExport::exportCommonControlAttributes: forgot some flags!"); + // in the dbg_util version, we should have removed every bit we handled from the mask, so it should + // be 0 now ... + } + + void OControlExport::exportDatabaseAttributes() + { +#if OSL_DEBUG_LEVEL > 0 + DAFlags nIncludeDatabase = m_nIncludeDatabase; +#endif + // the only string property: DataField + if (DAFlags::DataField & m_nIncludeDatabase) + { + exportStringPropertyAttribute( + OAttributeMetaData::getDatabaseAttributeNamespace(), + OAttributeMetaData::getDatabaseAttributeName(DAFlags::DataField), + PROPERTY_DATAFIELD); + RESET_BIT( nIncludeDatabase, DAFlags::DataField ); + } + + // InputRequired + if ( DAFlags::InputRequired & m_nIncludeDatabase ) + { + exportBooleanPropertyAttribute( + OAttributeMetaData::getDatabaseAttributeNamespace(), + OAttributeMetaData::getDatabaseAttributeName( DAFlags::InputRequired ), + PROPERTY_INPUT_REQUIRED, + BoolAttrFlags::DefaultFalse | BoolAttrFlags::DefaultVoid + ); + RESET_BIT( nIncludeDatabase, DAFlags::InputRequired ); + } + + // the only int16 property: BoundColumn + if (DAFlags::BoundColumn & m_nIncludeDatabase) + { + exportInt16PropertyAttribute( + OAttributeMetaData::getDatabaseAttributeNamespace(), + OAttributeMetaData::getDatabaseAttributeName(DAFlags::BoundColumn), + PROPERTY_BOUNDCOLUMN, + 0, + true); + RESET_BIT( nIncludeDatabase, DAFlags::BoundColumn ); + } + + // ConvertEmptyToNull + if (DAFlags::ConvertEmpty & m_nIncludeDatabase) + { + exportBooleanPropertyAttribute( + OAttributeMetaData::getDatabaseAttributeNamespace(), + OAttributeMetaData::getDatabaseAttributeName(DAFlags::ConvertEmpty), + PROPERTY_EMPTY_IS_NULL, + BoolAttrFlags::DefaultFalse + ); + RESET_BIT( nIncludeDatabase, DAFlags::ConvertEmpty ); + } + + // the only enum property: ListSourceType + if (DAFlags::ListSource_TYPE & m_nIncludeDatabase) + { + exportEnumPropertyAttribute( + OAttributeMetaData::getDatabaseAttributeNamespace(), + OAttributeMetaData::getDatabaseAttributeName(DAFlags::ListSource_TYPE), + PROPERTY_LISTSOURCETYPE, + aListSourceTypeMap, + ListSourceType_VALUELIST + ); + RESET_BIT( nIncludeDatabase, DAFlags::ListSource_TYPE ); + } + + if (m_nIncludeDatabase & DAFlags::ListSource) + { + exportListSourceAsAttribute(); + RESET_BIT( nIncludeDatabase, DAFlags::ListSource ); + } + +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE(DAFlags::NONE == nIncludeDatabase, + "OControlExport::exportDatabaseAttributes: forgot some flags!"); + // in the dbg_util version, we should have removed every bit we handled from the mask, so it should + // be 0 now ... +#endif + } + + void OControlExport::exportBindingAttributes() + { +#if OSL_DEBUG_LEVEL > 0 + BAFlags nIncludeBinding = m_nIncludeBindings; +#endif + + if ( m_nIncludeBindings & BAFlags::LinkedCell ) + { + exportCellBindingAttributes( bool(m_nIncludeBindings & BAFlags::ListLinkingType) ); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + nIncludeBinding = nIncludeBinding & ~BAFlags( BAFlags::LinkedCell | BAFlags::ListLinkingType ); + #endif + } + + if ( m_nIncludeBindings & BAFlags::ListCellRange ) + { + exportCellListSourceRange(); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + nIncludeBinding = nIncludeBinding & ~BAFlags::ListCellRange; + #endif + } + + if ( m_nIncludeBindings & BAFlags::XFormsBind ) + { + exportXFormsBindAttributes(); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + nIncludeBinding = nIncludeBinding & ~BAFlags::XFormsBind; + #endif + } + + if ( m_nIncludeBindings & BAFlags::XFormsListBind ) + { + exportXFormsListAttributes(); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + nIncludeBinding = nIncludeBinding & ~BAFlags::XFormsListBind; + #endif + } + + if ( m_nIncludeBindings & BAFlags::XFormsSubmission ) + { + exportXFormsSubmissionAttributes(); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + nIncludeBinding = nIncludeBinding & ~BAFlags::XFormsSubmission; + #endif + } + + #if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE( BAFlags::NONE == nIncludeBinding, + "OControlExport::exportBindingAttributes: forgot some flags!"); + // in the debug version, we should have removed every bit we handled from the mask, so it should + // be 0 now ... + #endif + } + + void OControlExport::exportSpecialAttributes() + { + sal_Int32 i=0; + + // the boolean properties + { + static const SCAFlags nBooleanPropertyAttributeIds[] = + { // attribute flags + SCAFlags::Validation, SCAFlags::MultiLine, SCAFlags::AutoCompletion, SCAFlags::Multiple, SCAFlags::DefaultButton, SCAFlags::IsTristate, + SCAFlags::Toggle, SCAFlags::FocusOnClick + }; + static const char * pBooleanPropertyNames[] = + { // property names + PROPERTY_STRICTFORMAT, PROPERTY_MULTILINE, + PROPERTY_AUTOCOMPLETE, + PROPERTY_MULTISELECTION, + PROPERTY_DEFAULTBUTTON, PROPERTY_TRISTATE, + PROPERTY_TOGGLE, PROPERTY_FOCUS_ON_CLICK + }; + static const sal_Int32 nIdCount = SAL_N_ELEMENTS(nBooleanPropertyAttributeIds); + #if OSL_DEBUG_LEVEL > 0 + static const sal_Int32 nNameCount = SAL_N_ELEMENTS(pBooleanPropertyNames); + OSL_ENSURE((nIdCount == nNameCount), + "OControlExport::exportSpecialAttributes: somebody tampered with the maps (1)!"); + #endif + const SCAFlags* pAttributeId = nBooleanPropertyAttributeIds; + for ( i = 0; i < nIdCount; ++i, ++pAttributeId ) + { + if ( *pAttributeId & m_nIncludeSpecial) + { + exportBooleanPropertyAttribute( + OAttributeMetaData::getSpecialAttributeNamespace( *pAttributeId ), + OAttributeMetaData::getSpecialAttributeName( *pAttributeId ), + OUString::createFromAscii(pBooleanPropertyNames[i]), + ( *pAttributeId == SCAFlags::FocusOnClick ) ? BoolAttrFlags::DefaultTrue : BoolAttrFlags::DefaultFalse + ); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeSpecial = m_nIncludeSpecial & ~*pAttributeId; + #endif + } + } + } + + // the integer properties + { + static const SCAFlags nIntegerPropertyAttributeIds[] = + { // attribute flags + SCAFlags::PageStepSize + }; + static const char * pIntegerPropertyNames[] = + { // property names + PROPERTY_BLOCK_INCREMENT + }; + static const sal_Int32 nIntegerPropertyAttrDefaults[] = + { // attribute defaults (XML defaults, not runtime defaults!) + 10 + }; + + static const sal_Int32 nIdCount = SAL_N_ELEMENTS( nIntegerPropertyAttributeIds ); + #if OSL_DEBUG_LEVEL > 0 + static const sal_Int32 nNameCount = SAL_N_ELEMENTS( pIntegerPropertyNames ); + OSL_ENSURE( ( nIdCount == nNameCount ), + "OControlExport::exportSpecialAttributes: somebody tampered with the maps (2)!" ); + static const sal_Int32 nDefaultCount = SAL_N_ELEMENTS( nIntegerPropertyAttrDefaults ); + OSL_ENSURE( ( nIdCount == nDefaultCount ), + "OControlExport::exportSpecialAttributes: somebody tampered with the maps (3)!" ); + #endif + for ( i = 0; i < nIdCount; ++i ) + if ( nIntegerPropertyAttributeIds[i] & m_nIncludeSpecial ) + { + exportInt32PropertyAttribute( + OAttributeMetaData::getSpecialAttributeNamespace( nIntegerPropertyAttributeIds[i] ), + OAttributeMetaData::getSpecialAttributeName( nIntegerPropertyAttributeIds[i] ), + OUString::createFromAscii(pIntegerPropertyNames[i]), + nIntegerPropertyAttrDefaults[i] + ); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeSpecial = m_nIncludeSpecial & ~nIntegerPropertyAttributeIds[i]; + #endif + } + + if ( SCAFlags::StepSize & m_nIncludeSpecial ) + { + OUString sPropertyName; + if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_LINE_INCREMENT ) ) + sPropertyName = PROPERTY_LINE_INCREMENT; + else if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_SPIN_INCREMENT ) ) + sPropertyName = PROPERTY_SPIN_INCREMENT; + else + OSL_FAIL( "OControlExport::exportSpecialAttributes: not property which can be mapped to step-size attribute!" ); + + if ( !sPropertyName.isEmpty() ) + exportInt32PropertyAttribute( + OAttributeMetaData::getSpecialAttributeNamespace( SCAFlags::StepSize ), + OAttributeMetaData::getSpecialAttributeName( SCAFlags::StepSize ), + sPropertyName, + 1 + ); + + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags::StepSize; + #endif + } + + } + + // the enum properties + { + if (SCAFlags::State & m_nIncludeSpecial) + { + exportEnumPropertyAttribute( + OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags::State), + OAttributeMetaData::getSpecialAttributeName(SCAFlags::State), + PROPERTY_DEFAULT_STATE, + aCheckStateMap, + TRISTATE_FALSE); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags::State; + #endif + } + + if (SCAFlags::CurrentState & m_nIncludeSpecial) + { + exportEnumPropertyAttribute( + OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags::CurrentState), + OAttributeMetaData::getSpecialAttributeName(SCAFlags::CurrentState), + PROPERTY_STATE, + aCheckStateMap, + TRISTATE_FALSE); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags::CurrentState; + #endif + } + } + + // some properties which require a special handling + // the repeat delay + { + if ( m_nIncludeSpecial & SCAFlags::RepeatDelay ) + { + DBG_CHECK_PROPERTY( PROPERTY_REPEAT_DELAY, sal_Int32 ); + + sal_Int32 nRepeatDelay = 0; + m_xProps->getPropertyValue( PROPERTY_REPEAT_DELAY ) >>= nRepeatDelay; + tools::Time aTime( tools::Time::SYSTEM ); + aTime.MakeTimeFromMS( nRepeatDelay ); + util::Duration aDuration; + aDuration.Hours = aTime.GetHour(); + aDuration.Minutes = aTime.GetMin(); + aDuration.Seconds = aTime.GetSec(); + aDuration.NanoSeconds = (nRepeatDelay % 1000) * 1000000; + + OUStringBuffer buf; + ::sax::Converter::convertDuration(buf, aDuration); + AddAttribute(OAttributeMetaData::getSpecialAttributeNamespace( SCAFlags::RepeatDelay ) + ,OAttributeMetaData::getSpecialAttributeName( SCAFlags::RepeatDelay ) + ,buf.makeStringAndClear()); + + exportedProperty( PROPERTY_REPEAT_DELAY ); + + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags::RepeatDelay; + #endif + } + } + + // the EchoChar property needs special handling, cause it's a Int16, but must be stored as one-character-string + { + if (SCAFlags::EchoChar & m_nIncludeSpecial) + { + DBG_CHECK_PROPERTY( PROPERTY_ECHO_CHAR, sal_Int16 ); + sal_Int16 nValue(0); + m_xProps->getPropertyValue(PROPERTY_ECHO_CHAR) >>= nValue; + if (nValue) + { + OUString sCharacter(reinterpret_cast<const sal_Unicode*>(&nValue), 1); + AddAttribute( + OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags::EchoChar), + OAttributeMetaData::getSpecialAttributeName(SCAFlags::EchoChar), + sCharacter); + } + exportedProperty(PROPERTY_ECHO_CHAR); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags::EchoChar; + #endif + } + } + + // the string properties + { + static const SCAFlags nStringPropertyAttributeIds[] = + { // attribute flags + SCAFlags::GroupName + }; + static const std::u16string_view pStringPropertyNames[] = + { // property names + u"" PROPERTY_GROUP_NAME + }; + + static const sal_Int32 nIdCount = SAL_N_ELEMENTS( nStringPropertyAttributeIds ); + #if OSL_DEBUG_LEVEL > 0 + static const sal_Int32 nNameCount = SAL_N_ELEMENTS( pStringPropertyNames ); + OSL_ENSURE( ( nIdCount == nNameCount ), + "OControlExport::exportSpecialAttributes: somebody tampered with the maps (2)!" ); + #endif + for ( i = 0; i < nIdCount; ++i ) + if ( nStringPropertyAttributeIds[i] & m_nIncludeSpecial ) + { + exportStringPropertyAttribute( + OAttributeMetaData::getSpecialAttributeNamespace( nStringPropertyAttributeIds[i] ), + OAttributeMetaData::getSpecialAttributeName( nStringPropertyAttributeIds[i] ), + OUString(pStringPropertyNames[i]) + ); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeSpecial = m_nIncludeSpecial & ~nStringPropertyAttributeIds[i]; + #endif + } + } + + if ((SCAFlags::MinValue | SCAFlags::MaxValue) & m_nIncludeSpecial) + { + // need to export the min value and the max value as attributes + // It depends on the real type (FormComponentType) of the control, which properties hold these + // values + const char* pMinValuePropertyName = nullptr; + const char* pMaxValuePropertyName = nullptr; + getValueLimitPropertyNames(m_nClassId, pMinValuePropertyName, pMaxValuePropertyName); + + OSL_ENSURE((nullptr == pMinValuePropertyName) == (SCAFlags::NONE == (SCAFlags::MinValue & m_nIncludeSpecial)), + "OControlExport::exportCommonControlAttributes: no property found for the min value attribute!"); + OSL_ENSURE((nullptr == pMaxValuePropertyName) == (SCAFlags::NONE == (SCAFlags::MaxValue & m_nIncludeSpecial)), + "OControlExport::exportCommonControlAttributes: no property found for the max value attribute!"); + + // add the two attributes + static const OUString pMinValueAttributeName = OAttributeMetaData::getSpecialAttributeName(SCAFlags::MinValue); + static const OUString pMaxValueAttributeName = OAttributeMetaData::getSpecialAttributeName(SCAFlags::MaxValue); + static const sal_uInt16 nMinValueNamespaceKey = OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags::MinValue); + static const sal_uInt16 nMaxValueNamespaceKey = OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags::MaxValue); + + if (pMinValuePropertyName && (SCAFlags::MinValue & m_nIncludeSpecial)) + exportGenericPropertyAttribute( + nMinValueNamespaceKey, + pMinValueAttributeName, + pMinValuePropertyName); + + if (pMaxValuePropertyName && (SCAFlags::MaxValue & m_nIncludeSpecial)) + exportGenericPropertyAttribute( + nMaxValueNamespaceKey, + pMaxValueAttributeName, + pMaxValuePropertyName); + #if OSL_DEBUG_LEVEL > 0 + // reset the bit for later checking + m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags(SCAFlags::MinValue | SCAFlags::MaxValue); + #endif + } + + if ( SCAFlags::ImagePosition & m_nIncludeSpecial ) + { + exportImagePositionAttributes(); + RESET_BIT( m_nIncludeSpecial, SCAFlags::ImagePosition ); + } + + OSL_ENSURE(SCAFlags::NONE == m_nIncludeSpecial, + "OControlExport::exportSpecialAttributes: forgot some flags!"); + // in the dbg_util version, we should have removed every bit we handled from the mask, so it should + // be 0 now ... + } + + OUString OControlExport::getScalarListSourceValue() const + { + OUString sListSource; + Any aListSource = m_xProps->getPropertyValue( PROPERTY_LISTSOURCE ); + if ( !( aListSource >>= sListSource ) ) + { + Sequence< OUString > aListSourceSequence; + aListSource >>= aListSourceSequence; + if ( aListSourceSequence.hasElements() ) + sListSource = aListSourceSequence[ 0 ]; + } + return sListSource; + } + + void OControlExport::exportListSourceAsAttribute() + { + // DAFlags::ListSource needs some special handling + DBG_CHECK_PROPERTY_NO_TYPE( PROPERTY_LISTSOURCE ); + + OUString sListSource = getScalarListSourceValue(); + if ( !sListSource.isEmpty() ) + { // the ListSource property needs to be exported as attribute, and it is not empty + AddAttribute( + OAttributeMetaData::getDatabaseAttributeNamespace(), + OAttributeMetaData::getDatabaseAttributeName(DAFlags::ListSource), + sListSource); + } + + exportedProperty( PROPERTY_LISTSOURCE ); + } + + void OControlExport::getSequenceInt16PropertyAsSet(const OUString& _rPropertyName, Int16Set& _rOut) + { + Sequence< sal_Int16 > aValueSequence; + DBG_CHECK_PROPERTY(_rPropertyName, Sequence< sal_Int16 >); + m_xProps->getPropertyValue(_rPropertyName) >>= aValueSequence; + + for (const auto& rValue : std::as_const(aValueSequence)) + _rOut.insert(rValue); + } + + void OControlExport::exportListSourceAsElements() + { + // the string lists + Sequence< OUString > aItems, aValues; + DBG_CHECK_PROPERTY( PROPERTY_STRING_ITEM_LIST, Sequence< OUString > ); + m_xProps->getPropertyValue(PROPERTY_STRING_ITEM_LIST) >>= aItems; + + DBG_CHECK_PROPERTY( PROPERTY_LISTSOURCE, Sequence< OUString > ); + if ( DAFlags::NONE == ( m_nIncludeDatabase & DAFlags::ListSource ) ) + m_xProps->getPropertyValue(PROPERTY_LISTSOURCE) >>= aValues; + // if we exported the list source as attribute, we do not repeat it as sub elements + + // the selection lists + Int16Set aSelection, aDefaultSelection; + getSequenceInt16PropertyAsSet(PROPERTY_SELECT_SEQ, aSelection); + getSequenceInt16PropertyAsSet(PROPERTY_DEFAULT_SELECT_SEQ, aDefaultSelection); + + // the string for "true" + OUString sTrue; + OUStringBuffer sBuffer; + ::sax::Converter::convertBool(sBuffer, true); + sTrue = sBuffer.makeStringAndClear(); + + // loop through both lists ('til the maximum of both lengths) + const OUString* pItems = aItems.getConstArray(); + const OUString* pValues = aValues.getConstArray(); + + sal_Int32 nItems = aItems.getLength(); + sal_Int32 nValues = aValues.getLength(); + + sal_Int16 nMaxLen = static_cast<sal_Int16>(std::max(nItems, nValues)); + + for (sal_Int16 i=0; i<nMaxLen; ++i ) + { + m_rContext.getGlobalContext().ClearAttrList(); + if (i < nItems) + { + // there is an item at this position + AddAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Label), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Label), + *pItems); + ++pItems; + } + if (i < nValues) + { + // there is a value at this position + AddAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Value), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Value), + *pValues); + ++pValues; + } + + Int16Set::const_iterator aSelectedPos = aSelection.find(i); + if (aSelection.end() != aSelectedPos) + { // the item at this position is selected + AddAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::CurrentSelected), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::CurrentSelected), + sTrue + ); + aSelection.erase(aSelectedPos); + } + + Int16Set::const_iterator aDefaultSelectedPos = aDefaultSelection.find(i); + if (aDefaultSelection.end() != aDefaultSelectedPos) + { // the item at this position is selected as default + AddAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Selected), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Selected), + sTrue + ); + aDefaultSelection.erase(aDefaultSelectedPos); + } + SvXMLElementExport aFormElement(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, "option", true, true); + } + + // There may be more "selected" or "default-selected" items than there are in the lists in real, + // so we need to store some additional "form:option" items which have no name and no label, but + // one or both of the selected flags. + // 21.05.2001 - 85388 - frank.schoenheit@germany.sun.com + + if (aSelection.empty() && aDefaultSelection.empty()) + return; + + sal_Int16 nLastSelected = -1; + if ( !aSelection.empty() ) + nLastSelected = *(--aSelection.end()); + + sal_Int16 nLastDefaultSelected = -1; + if ( !aDefaultSelection.empty() ) + nLastDefaultSelected = *(--aDefaultSelection.end()); + + // the maximum element in both sets + sal_Int16 nLastReferredEntry = std::max(nLastSelected, nLastDefaultSelected); + OSL_ENSURE(nLastReferredEntry >= nMaxLen, "OControlExport::exportListSourceAsElements: inconsistence!"); + // if the maximum (selected or default selected) entry number is less than the maximum item count + // in both lists, the entry number should have been removed from the set + + for (sal_Int16 i=nMaxLen; i<=nLastReferredEntry; ++i) + { + if (aSelection.end() != aSelection.find(i)) + { // the (not existent) item at this position is selected + AddAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::CurrentSelected), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::CurrentSelected), + sTrue + ); + } + + if (aDefaultSelection.end() != aDefaultSelection.find(i)) + { // the (not existent) item at this position is selected as default + AddAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Selected), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Selected), + sTrue + ); + } + SvXMLElementExport aFormElement(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, "option", true, true); + } + } + + void OControlExport::implStartElement(const char* _pName) + { + // before we let the base class start it's outer element, we add a wrapper element + const char *pOuterElementName = getOuterXMLElementName(); + if (pOuterElementName) + m_pOuterElement = std::make_unique<SvXMLElementExport>( + m_rContext.getGlobalContext(), + XML_NAMESPACE_FORM, + pOuterElementName, true, + true); + + // add the attributes for the inner element + exportInnerAttributes(); + + // and start the inner element + OElementExport::implStartElement(_pName); + } + + void OControlExport::implEndElement() + { + // end the inner element + OElementExport::implEndElement(); + + // end the outer element if it exists + m_pOuterElement.reset(); + } + + const char* OControlExport::getOuterXMLElementName() const + { + return nullptr; + } + + const char* OControlExport::getXMLElementName() const + { + return getElementName(m_eType); + } + + void OControlExport::examine() + { + OSL_ENSURE( ( m_nIncludeCommon == CCAFlags::NONE ) && ( m_nIncludeSpecial == SCAFlags::NONE ) && ( m_nIncludeDatabase == DAFlags::NONE ) + && ( m_nIncludeEvents == EAFlags::NONE ) && ( m_nIncludeBindings == BAFlags::NONE), + "OControlExport::examine: called me twice? Not initialized?" ); + + // get the class id to decide which kind of element we need in the XML stream + m_nClassId = FormComponentType::CONTROL; + DBG_CHECK_PROPERTY( PROPERTY_CLASSID, sal_Int16 ); + m_xProps->getPropertyValue(PROPERTY_CLASSID) >>= m_nClassId; + bool knownType = false; + switch (m_nClassId) + { + case FormComponentType::DATEFIELD: + m_eType = DATE; + knownType = true; + [[fallthrough]]; + case FormComponentType::TIMEFIELD: + if ( !knownType ) + { + m_eType = TIME; + knownType = true; + } + m_nIncludeSpecial |= SCAFlags::Validation; + [[fallthrough]]; + case FormComponentType::NUMERICFIELD: + case FormComponentType::CURRENCYFIELD: + case FormComponentType::PATTERNFIELD: + if ( !knownType ) + { + m_eType = FORMATTED_TEXT; + knownType = true; + } + [[fallthrough]]; + case FormComponentType::TEXTFIELD: + { // it's some kind of edit. To know which type we need further investigation + + if ( !knownType ) + { + // check if it's a formatted field + if (m_xPropertyInfo->hasPropertyByName(PROPERTY_FORMATKEY)) + { + m_eType = FORMATTED_TEXT; + } + else + { + // all other controls are represented by an ordinary edit control, but which XML control type + // it is depends on the current values of some properties + + // if the EchoChar string is not empty, it is a password field + sal_Int16 nEchoChar = 0; + if (m_xPropertyInfo->hasPropertyByName(PROPERTY_ECHOCHAR)) + // grid columns do not have this property... + m_xProps->getPropertyValue(PROPERTY_ECHOCHAR) >>= nEchoChar; + if (nEchoChar) + { + m_eType = PASSWORD; + m_nIncludeSpecial |= SCAFlags::EchoChar; + } + else + { + // if the MultiLine property is sal_True, it is a TextArea + bool bMultiLine = false; + if (m_xPropertyInfo->hasPropertyByName(PROPERTY_MULTILINE)) + // grid columns do not have this property... + bMultiLine = ::cppu::any2bool(m_xProps->getPropertyValue(PROPERTY_MULTILINE)); + + if ( bMultiLine ) + m_eType = TEXT_AREA; + else + // the only case left is represented by a Text element + m_eType = TEXT; + } + } + } + + // attributes which are common to all the types: + // common attributes + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | + CCAFlags::Printable | CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title; + + if ( ( m_nClassId != FormComponentType::DATEFIELD ) + && ( m_nClassId != FormComponentType::TIMEFIELD ) + ) + // date and time field values are handled differently nowadays + m_nIncludeCommon |= CCAFlags::Value; + + // database attributes + m_nIncludeDatabase = DAFlags::DataField | DAFlags::InputRequired; + + // event attributes + m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnChange | EAFlags::OnSelect; + + // only text and pattern fields have a ConvertEmptyToNull property + if ( ( m_nClassId == FormComponentType::TEXTFIELD ) + || ( m_nClassId == FormComponentType::PATTERNFIELD ) + ) + m_nIncludeDatabase |= DAFlags::ConvertEmpty; + + // all controls but the file control fields have a readonly property + if ( m_nClassId != FormComponentType::FILECONTROL ) + m_nIncludeCommon |= CCAFlags::ReadOnly; + + // a text field has a max text len + if ( m_nClassId == FormComponentType::TEXTFIELD ) + m_nIncludeCommon |= CCAFlags::MaxLength; + + // max and min values and validation: + if (FORMATTED_TEXT == m_eType) + { // in general all controls represented as formatted-text have these props + if ( FormComponentType::PATTERNFIELD != m_nClassId ) // except the PatternField + m_nIncludeSpecial |= SCAFlags::MaxValue | SCAFlags::MinValue; + + if (FormComponentType::TEXTFIELD != m_nClassId) + // and the FormattedField does not have a validation flag + m_nIncludeSpecial |= SCAFlags::Validation; + } + + // if it's not a password field or rich text control, the CurrentValue needs to be stored, too + if ( ( PASSWORD != m_eType ) + && ( DATE != m_eType ) + && ( TIME != m_eType ) + ) + { + m_nIncludeCommon |= CCAFlags::CurrentValue; + } + } + break; + + case FormComponentType::FILECONTROL: + m_eType = FILE; + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::CurrentValue | CCAFlags::Disabled | + CCAFlags::Printable | CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title | + CCAFlags::Value; + m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnChange | EAFlags::OnSelect; + break; + + case FormComponentType::FIXEDTEXT: + m_eType = FIXED_TEXT; + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Label | + CCAFlags::Printable | CCAFlags::Title | CCAFlags::For; + m_nIncludeSpecial = SCAFlags::MultiLine; + m_nIncludeEvents = EAFlags::ControlEvents; + break; + + case FormComponentType::COMBOBOX: + m_eType = COMBOBOX; + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::CurrentValue | + CCAFlags::Disabled | CCAFlags::Dropdown | CCAFlags::MaxLength | CCAFlags::Printable | CCAFlags::ReadOnly | CCAFlags::Size | + CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title | CCAFlags::Value; + m_nIncludeSpecial = SCAFlags::AutoCompletion; + m_nIncludeDatabase = DAFlags::ConvertEmpty | DAFlags::DataField | DAFlags::InputRequired | DAFlags::ListSource | DAFlags::ListSource_TYPE; + m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnChange | EAFlags::OnSelect; + break; + + case FormComponentType::LISTBOX: + m_eType = LISTBOX; + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Dropdown | + CCAFlags::Printable | CCAFlags::Size | CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title; + m_nIncludeSpecial = SCAFlags::Multiple; + m_nIncludeDatabase = DAFlags::BoundColumn | DAFlags::DataField | DAFlags::InputRequired | DAFlags::ListSource_TYPE; + m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnChange | EAFlags::OnClick | EAFlags::OnDoubleClick; + // check if we need to export the ListSource as attribute + { + // for a list box, if the ListSourceType is VALUE_LIST, no ListSource is stored, but instead + // a sequence of pairs which is build from the StringItemList and the ValueList + ListSourceType eListSourceType = ListSourceType_VALUELIST; + bool bSuccess = + m_xProps->getPropertyValue(PROPERTY_LISTSOURCETYPE) >>= eListSourceType; + OSL_ENSURE(bSuccess, "OControlExport::examineControl: could not retrieve the ListSourceType!"); + if (ListSourceType_VALUELIST != eListSourceType) + { + m_nIncludeDatabase |= DAFlags::ListSource; + } + } + + break; + + case FormComponentType::COMMANDBUTTON: + m_eType = BUTTON; + m_nIncludeCommon |= CCAFlags::TabStop | CCAFlags::Label; + m_nIncludeSpecial = SCAFlags::DefaultButton | SCAFlags::Toggle | SCAFlags::FocusOnClick | SCAFlags::ImagePosition | SCAFlags::RepeatDelay; + [[fallthrough]]; + case FormComponentType::IMAGEBUTTON: + if (BUTTON != m_eType) + { + // not coming from the previous case + m_eType = IMAGE; + } + m_nIncludeCommon |= + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::ButtonType | CCAFlags::Disabled | + CCAFlags::ImageData | CCAFlags::Printable | CCAFlags::TabIndex | CCAFlags::TargetFrame | + CCAFlags::TargetLocation | CCAFlags::Title; + m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnClick | EAFlags::OnDoubleClick; + break; + + case FormComponentType::CHECKBOX: + m_eType = CHECKBOX; + m_nIncludeSpecial = SCAFlags::CurrentState | SCAFlags::IsTristate | SCAFlags::State; + [[fallthrough]]; + case FormComponentType::RADIOBUTTON: + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Label | CCAFlags::Printable | + CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title | CCAFlags::Value | CCAFlags::VisualEffect; + if (CHECKBOX != m_eType) + { // not coming from the previous case + m_eType = RADIO; + m_nIncludeCommon |= CCAFlags::CurrentSelected | CCAFlags::Selected; + } + if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_IMAGE_POSITION ) ) + m_nIncludeSpecial |= SCAFlags::ImagePosition; + if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_GROUP_NAME ) ) + m_nIncludeSpecial |= SCAFlags::GroupName; + m_nIncludeDatabase = DAFlags::DataField | DAFlags::InputRequired; + m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnChange; + break; + + case FormComponentType::GROUPBOX: + m_eType = FRAME; + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Label | + CCAFlags::Printable | CCAFlags::Title | CCAFlags::For; + m_nIncludeEvents = EAFlags::ControlEvents; + break; + + case FormComponentType::IMAGECONTROL: + m_eType = IMAGE_FRAME; + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::ImageData | + CCAFlags::Printable | CCAFlags::ReadOnly | CCAFlags::Title; + m_nIncludeDatabase = DAFlags::DataField | DAFlags::InputRequired; + m_nIncludeEvents = EAFlags::ControlEvents; + break; + + case FormComponentType::HIDDENCONTROL: + m_eType = HIDDEN; + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Value; + break; + + case FormComponentType::GRIDCONTROL: + m_eType = GRID; + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Printable | + CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title; + m_nIncludeEvents = EAFlags::ControlEvents; + break; + + case FormComponentType::SCROLLBAR: + case FormComponentType::SPINBUTTON: + m_eType = VALUERANGE; + m_nIncludeCommon = + CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Printable | + CCAFlags::Title | CCAFlags::CurrentValue | CCAFlags::Value | CCAFlags::Orientation; + m_nIncludeSpecial = SCAFlags::MaxValue | SCAFlags::StepSize | SCAFlags::MinValue | SCAFlags::RepeatDelay; + + if ( m_nClassId == FormComponentType::SCROLLBAR ) + m_nIncludeSpecial |= SCAFlags::PageStepSize ; + + m_nIncludeEvents = EAFlags::ControlEvents; + break; + + default: + OSL_FAIL("OControlExport::examineControl: unknown control type (class id)!"); + [[fallthrough]]; + + case FormComponentType::NAVIGATIONBAR: + // TODO: should we have an own file format for this? + // NO break + + case FormComponentType::CONTROL: + m_eType = GENERIC_CONTROL; + // unknown control type + m_nIncludeCommon = CCAFlags::Name | CCAFlags::ServiceName; + // at least a name should be there, 'cause without a name the control could never have been + // inserted into its parent container + // In addition, the service name is absolutely necessary to create the control upon reading. + m_nIncludeEvents = EAFlags::ControlEvents; + // we always should be able to export events - this is not control type dependent + break; + } + + // in general, all control types need to export the control id + m_nIncludeCommon |= CCAFlags::ControlId; + + // is it a control bound to a calc cell? + if ( FormCellBindingHelper::livesInSpreadsheetDocument( m_xProps ) ) + { + FormCellBindingHelper aHelper( m_xProps, nullptr ); + { + if ( FormCellBindingHelper::isCellBinding( aHelper.getCurrentBinding( ) ) ) + { + m_nIncludeBindings |= BAFlags::LinkedCell; + if ( m_nClassId == FormComponentType::LISTBOX ) + m_nIncludeBindings |= BAFlags::ListLinkingType; + } + } + + // is it a list-like control which uses a calc cell range as list source? + { + if ( FormCellBindingHelper::isCellRangeListSource( aHelper.getCurrentListSource( ) ) ) + m_nIncludeBindings |= BAFlags::ListCellRange; + } + } + + // is control bound to XForms? + if( !getXFormsBindName( m_xProps ).isEmpty() ) + { + m_nIncludeBindings |= BAFlags::XFormsBind; + } + + // is (list-)control bound to XForms list? + if( !getXFormsListBindName( m_xProps ).isEmpty() ) + { + m_nIncludeBindings |= BAFlags::XFormsListBind; + } + + // does the control have an XForms submission? + if( !getXFormsSubmissionName( m_xProps ).isEmpty() ) + { + m_nIncludeBindings |= BAFlags::XFormsSubmission; + } + } + + void OControlExport::exportCellBindingAttributes( bool _bIncludeListLinkageType ) + { + try + { + FormCellBindingHelper aHelper( m_xProps, nullptr ); + Reference< XValueBinding > xBinding( aHelper.getCurrentBinding() ); + OSL_ENSURE( xBinding.is(), "OControlExport::exportCellBindingAttributes: invalid bindable or invalid binding!" ); + if ( xBinding.is() ) + { + AddAttribute( + OAttributeMetaData::getBindingAttributeNamespace(), + OAttributeMetaData::getBindingAttributeName( BAFlags::LinkedCell ), + aHelper.getStringAddressFromCellBinding( xBinding ) + ); + + if ( _bIncludeListLinkageType ) + { + sal_Int16 nLinkageType = FormCellBindingHelper::isCellIntegerBinding( xBinding ) ? 1 : 0; + + OUStringBuffer sBuffer; + SvXMLUnitConverter::convertEnum( + sBuffer, + nLinkageType, + aListLinkageMap + ); + + AddAttribute( + OAttributeMetaData::getBindingAttributeNamespace(), + OAttributeMetaData::getBindingAttributeName( BAFlags::ListLinkingType ), + sBuffer.makeStringAndClear() + ); + } + + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "xmloff.forms", "OControlExport::exportCellBindingAttributes" ); + } + } + + void OControlExport::exportXFormsBindAttributes() + { + OUString sBindName = getXFormsBindName( m_xProps ); + AddAttribute( XML_NAMESPACE_XFORMS, XML_BIND, sBindName ); + } + void OControlExport::exportXFormsListAttributes() + { + OUString sBindName = getXFormsListBindName( m_xProps ); + AddAttribute( XML_NAMESPACE_FORM, XML_XFORMS_LIST_SOURCE, sBindName ); + } + void OControlExport::exportXFormsSubmissionAttributes() + { + OUString sSubmission = getXFormsSubmissionName( m_xProps ); + AddAttribute( XML_NAMESPACE_FORM, XML_XFORMS_SUBMISSION, sSubmission ); + } + void OControlExport::exportCellListSourceRange( ) + { + try + { + Reference< XListEntrySink > xSink( m_xProps, UNO_QUERY ); + Reference< XListEntrySource > xSource; + if ( xSink.is() ) + xSource = xSink->getListEntrySource(); + OSL_ENSURE( xSource.is(), "OControlExport::exportCellListSourceRange: list source or sink!" ); + if ( xSource.is() ) + { + FormCellBindingHelper aHelper( m_xProps, nullptr ); + + AddAttribute( + OAttributeMetaData::getBindingAttributeNamespace(), + OAttributeMetaData::getBindingAttributeName( BAFlags::ListCellRange ), + aHelper.getStringAddressFromCellListSource( xSource ) + ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "xmloff.forms", "OControlExport::exportCellListSourceRange" ); + } + } + + void OControlExport::exportImagePositionAttributes() + { + try + { + sal_Int16 nImagePosition = ImagePosition::Centered; + OSL_VERIFY( m_xProps->getPropertyValue( PROPERTY_IMAGE_POSITION ) >>= nImagePosition ); + OSL_ENSURE( ( nImagePosition >= ImagePosition::LeftTop ) && ( nImagePosition <= ImagePosition::Centered ), + "OControlExport::exportImagePositionAttributes: don't know this image position!" ); + + if ( ( nImagePosition < ImagePosition::LeftTop ) || ( nImagePosition > ImagePosition::Centered ) ) + // this is important to prevent potential buffer overflows below, so don't optimize + nImagePosition = ImagePosition::Centered; + + if ( nImagePosition == ImagePosition::Centered ) + { + AddAttribute( XML_NAMESPACE_FORM, GetXMLToken( XML_IMAGE_POSITION ), GetXMLToken( XML_CENTER ) ); + } + else + { + const XMLTokenEnum eXmlImagePositions[] = + { + XML_START, XML_END, XML_TOP, XML_BOTTOM + }; + const XMLTokenEnum eXmlImageAligns[] = + { + XML_START, XML_CENTER, XML_END + }; + + XMLTokenEnum eXmlImagePosition = eXmlImagePositions[ nImagePosition / 3 ]; + XMLTokenEnum eXmlImageAlign = eXmlImageAligns [ nImagePosition % 3 ]; + + AddAttribute( XML_NAMESPACE_FORM, GetXMLToken( XML_IMAGE_POSITION ), GetXMLToken( eXmlImagePosition ) ); + AddAttribute( XML_NAMESPACE_FORM, GetXMLToken( XML_IMAGE_ALIGN ), GetXMLToken( eXmlImageAlign ) ); + } + + exportedProperty( PROPERTY_IMAGE_POSITION ); + // some of the controls which have an ImagePosition also have an ImageAlign for compatibility + // reasons. Since the ImageAlign values simply represent a sub set of the ImagePosition values, + // we don't need to export ImageAlign anymore + exportedProperty( PROPERTY_IMAGE_ALIGN ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.forms"); + } + } + + bool OControlExport::controlHasActiveDataBinding() const + { + try + { + // currently exchanging the data with a database column? + OUString sBoundFieldPropertyName( "BoundField" ); + if ( m_xPropertyInfo.is() && m_xPropertyInfo->hasPropertyByName( sBoundFieldPropertyName ) ) + { + Reference< XPropertySet > xBoundField; + m_xProps->getPropertyValue( sBoundFieldPropertyName ) >>= xBoundField; + if ( xBoundField.is() ) + return true; + } + + // currently exchanging data with an external binding? + Reference< XBindableValue > xBindable( m_xProps, UNO_QUERY ); + if ( xBindable.is() && xBindable->getValueBinding().is() ) + return true; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "xmloff.forms", "OColumnExport::controlHasActiveDataBinding" ); + } + + return false; + } + + bool OControlExport::controlHasUserSuppliedListEntries() const + { + try + { + // an external list source? + Reference< XListEntrySink > xEntrySink( m_xProps, UNO_QUERY ); + if ( xEntrySink.is() && xEntrySink->getListEntrySource().is() ) + return false; + + if ( m_xPropertyInfo.is() && m_xPropertyInfo->hasPropertyByName( PROPERTY_LISTSOURCETYPE ) ) + { + ListSourceType eListSourceType = ListSourceType_VALUELIST; + OSL_VERIFY( m_xProps->getPropertyValue( PROPERTY_LISTSOURCETYPE ) >>= eListSourceType ); + if ( eListSourceType == ListSourceType_VALUELIST ) + // for value lists, the list entries as entered by the user are used + return true; + + // for every other type, the list entries are filled with some data obtained + // from a database - if and only if the ListSource property is not empty + return getScalarListSourceValue().isEmpty(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.forms"); + } + + OSL_FAIL( "OControlExport::controlHasUserSuppliedListEntries: unreachable code!" ); + // this method should be called for list and combo boxes only + return true; + } + + //= OColumnExport + OColumnExport::OColumnExport(IFormsExportContext& _rContext, const Reference< XPropertySet >& _rxControl, const OUString& _rControlId, + const Sequence< ScriptEventDescriptor >& _rEvents) + :OControlExport(_rContext, _rxControl, _rControlId, OUString(), _rEvents) + { + } + + OColumnExport::~OColumnExport() + { + } + + void OColumnExport::exportServiceNameAttribute() + { + // the attribute "service name" (which has a slightly different meaning for columns + DBG_CHECK_PROPERTY( PROPERTY_COLUMNSERVICENAME, OUString ); + OUString sColumnServiceName; + m_xProps->getPropertyValue(PROPERTY_COLUMNSERVICENAME) >>= sColumnServiceName; + // the service name is a full qualified one (i.e. com.sun.star.form.TextField), but the + // real service name for the column (for use with the XGridColumnFactory) is only the last + // token of this complete name. + sal_Int32 nLastSep = sColumnServiceName.lastIndexOf('.'); + OSL_ENSURE(-1 != nLastSep, "OColumnExport::startExportElement: invalid service name!"); + sColumnServiceName = sColumnServiceName.copy(nLastSep + 1); + sColumnServiceName = + m_rContext.getGlobalContext().GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_OOO, sColumnServiceName ); + // add the attribute + AddAttribute( OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::ServiceName) + , OAttributeMetaData::getCommonControlAttributeName(CCAFlags::ServiceName) + , sColumnServiceName); + // flag the property as "handled" + exportedProperty(PROPERTY_COLUMNSERVICENAME); + + } + + const char* OColumnExport::getOuterXMLElementName() const + { + return "column"; + } + + void OColumnExport::exportAttributes() + { + OControlExport::exportAttributes(); + + // the attribute "label" + exportStringPropertyAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Label), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Label), + PROPERTY_LABEL); + + // the style attribute + OUString sStyleName = m_rContext.getObjectStyleName( m_xProps ); + if ( !sStyleName.isEmpty() ) + { + AddAttribute( + OAttributeMetaData::getSpecialAttributeNamespace( SCAFlags::ColumnStyleName ), + OAttributeMetaData::getSpecialAttributeName( SCAFlags::ColumnStyleName ), + sStyleName + ); + } + } + + void OColumnExport::examine() + { + OControlExport::examine(); + + // grid columns miss some properties of the controls they're representing + m_nIncludeCommon &= ~CCAFlags(CCAFlags::For | CCAFlags::Printable | CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Label); + m_nIncludeSpecial &= ~SCAFlags(SCAFlags::EchoChar | SCAFlags::AutoCompletion | SCAFlags::Multiple | SCAFlags::MultiLine); + + if (FormComponentType::DATEFIELD != m_nClassId) + // except date fields, no column has the DropDown property + m_nIncludeCommon &= ~CCAFlags::Dropdown; + } + + //= OFormExport + OFormExport::OFormExport(IFormsExportContext& _rContext, const Reference< XPropertySet >& _rxForm, + const Sequence< ScriptEventDescriptor >& _rEvents) + :OElementExport(_rContext, _rxForm, _rEvents) + ,m_bCreateConnectionResourceElement(false) + { + OSL_ENSURE(m_xProps.is(), "OFormExport::OFormExport: invalid arguments!"); + } + + const char* OFormExport::getXMLElementName() const + { + return "form"; + } + + void OFormExport::exportSubTags() + { + if ( m_bCreateConnectionResourceElement && m_xProps.is() ) + { + m_rContext.getGlobalContext().ClearAttrList(); + OUString sPropValue; + m_xProps->getPropertyValue( PROPERTY_DATASOURCENAME ) >>= sPropValue; // if set it is a file url + if ( sPropValue.isEmpty() ) + m_xProps->getPropertyValue( PROPERTY_URL ) >>= sPropValue; + if ( !sPropValue.isEmpty() ) + AddAttribute( + OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::TargetLocation), + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::TargetLocation), + m_rContext.getGlobalContext().GetRelativeReference(sPropValue)); + if ( m_rContext.getGlobalContext().GetAttrList().getLength() ) + { + SvXMLElementExport aFormElement(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, xmloff::token::XML_CONNECTION_RESOURCE, true, true); + } + } + + // let the base class export the remaining properties and the events + OElementExport::exportSubTags(); + // loop through all children + Reference< XIndexAccess > xCollection(m_xProps, UNO_QUERY); + OSL_ENSURE(xCollection.is(), "OFormLayerXMLExport::implExportForm: a form which is not an index access? Suspicious!"); + + if (xCollection.is()) + m_rContext.exportCollectionElements(xCollection); + } + + void OFormExport::exportAttributes() + { + sal_Int32 i=0; + + // the string properties + { + static const FormAttributes eStringPropertyIds[] = + { + faName, /*faAction,*/ faCommand, faFilter, faOrder + }; + static const char * aStringPropertyNames[] = + { + PROPERTY_NAME, /*PROPERTY_TARGETURL,*/ PROPERTY_COMMAND, PROPERTY_FILTER, PROPERTY_ORDER + }; + static const sal_Int32 nIdCount = SAL_N_ELEMENTS(eStringPropertyIds); + #if OSL_DEBUG_LEVEL > 0 + static const sal_Int32 nNameCount = SAL_N_ELEMENTS(aStringPropertyNames); + OSL_ENSURE((nIdCount == nNameCount), + "OFormExport::exportAttributes: somebody tampered with the maps (1)!"); + #endif + for (i=0; i<nIdCount; ++i) + exportStringPropertyAttribute( + OAttributeMetaData::getFormAttributeNamespace(eStringPropertyIds[i]), + OAttributeMetaData::getFormAttributeName(eStringPropertyIds[i]), + OUString::createFromAscii(aStringPropertyNames[i])); + + // #i112082# xlink:type is added as part of exportTargetLocationAttribute + + // now export the data source name or databaselocation or connection resource + OUString sPropValue; + m_xProps->getPropertyValue( PROPERTY_DATASOURCENAME ) >>= sPropValue; + m_bCreateConnectionResourceElement = sPropValue.isEmpty(); + if ( !m_bCreateConnectionResourceElement ) + { + INetURLObject aURL(sPropValue); + m_bCreateConnectionResourceElement = ( aURL.GetProtocol() == INetProtocol::File ); + if ( !m_bCreateConnectionResourceElement ) + exportStringPropertyAttribute( + OAttributeMetaData::getFormAttributeNamespace(faDatasource), + OAttributeMetaData::getFormAttributeName(faDatasource), + PROPERTY_DATASOURCENAME); + } + else + exportedProperty(PROPERTY_URL); + if ( m_bCreateConnectionResourceElement ) + exportedProperty(PROPERTY_DATASOURCENAME); + } + + // the boolean properties + { + static const FormAttributes eBooleanPropertyIds[] = + { + faAllowDeletes, faAllowInserts, faAllowUpdates, faApplyFilter, faEscapeProcessing, faIgnoreResult + }; + static const char * pBooleanPropertyNames[] = + { + PROPERTY_ALLOWDELETES, + PROPERTY_ALLOWINSERTS, + PROPERTY_ALLOWUPDATES, + PROPERTY_APPLYFILTER, + PROPERTY_ESCAPEPROCESSING, + PROPERTY_IGNORERESULT + }; + static const BoolAttrFlags nBooleanPropertyAttrFlags[] = + { + BoolAttrFlags::DefaultTrue, BoolAttrFlags::DefaultTrue, BoolAttrFlags::DefaultTrue, BoolAttrFlags::DefaultFalse, BoolAttrFlags::DefaultTrue, BoolAttrFlags::DefaultFalse + }; + static const sal_Int32 nIdCount = SAL_N_ELEMENTS(eBooleanPropertyIds); + #if OSL_DEBUG_LEVEL > 0 + static const sal_Int32 nNameCount = SAL_N_ELEMENTS(pBooleanPropertyNames); + static const sal_Int32 nFlagsCount = SAL_N_ELEMENTS(nBooleanPropertyAttrFlags); + OSL_ENSURE((nIdCount == nNameCount) && (nNameCount == nFlagsCount), + "OFormExport::exportAttributes: somebody tampered with the maps (2)!"); + #endif + for (i=0; i<nIdCount; ++i) + exportBooleanPropertyAttribute( + OAttributeMetaData::getFormAttributeNamespace(eBooleanPropertyIds[i]), + OAttributeMetaData::getFormAttributeName(eBooleanPropertyIds[i]), + OUString::createFromAscii(pBooleanPropertyNames[i]), + nBooleanPropertyAttrFlags[i] + ); + } + + // the enum properties + { + exportEnumPropertyAttribute( + OAttributeMetaData::getFormAttributeNamespace(faEnctype), + OAttributeMetaData::getFormAttributeName(faEnctype), + PROPERTY_SUBMIT_ENCODING, + aSubmitEncodingMap, + FormSubmitEncoding_URL, + false + ); + exportEnumPropertyAttribute( + OAttributeMetaData::getFormAttributeNamespace(faMethod), + OAttributeMetaData::getFormAttributeName(faMethod), + PROPERTY_SUBMIT_METHOD, + aSubmitMethodMap, + FormSubmitMethod_GET, + false + ); + exportEnumPropertyAttribute( + OAttributeMetaData::getFormAttributeNamespace(faCommandType), + OAttributeMetaData::getFormAttributeName(faCommandType), + PROPERTY_COMMAND_TYPE, + aCommandTypeMap, + CommandType::COMMAND, + false + ); + exportEnumPropertyAttribute( + OAttributeMetaData::getFormAttributeNamespace(faNavigationMode), + OAttributeMetaData::getFormAttributeName(faNavigationMode), + PROPERTY_NAVIGATION, + aNavigationTypeMap, + NavigationBarMode_CURRENT, + false + ); + exportEnumPropertyAttribute( + OAttributeMetaData::getFormAttributeNamespace(faTabbingCycle), + OAttributeMetaData::getFormAttributeName(faTabbingCycle), + PROPERTY_CYCLE, + aTabulatorCycleMap, + TabulatorCycle_RECORDS, + true + ); + } + + // the service name + exportServiceNameAttribute(); + // the target frame + exportTargetFrameAttribute(); + // the target URL + exportTargetLocationAttribute(true); // #i110911# add type attribute (for form, but not for control) + + // master fields + exportStringSequenceAttribute( + OAttributeMetaData::getFormAttributeNamespace(faMasterFields), + OAttributeMetaData::getFormAttributeName(faMasterFields), + PROPERTY_MASTERFIELDS); + // detail fields + exportStringSequenceAttribute( + OAttributeMetaData::getFormAttributeNamespace(faDetailFields), + OAttributeMetaData::getFormAttributeName(faDetailFields), + PROPERTY_DETAILFIELDS); + } +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/elementexport.hxx b/xmloff/source/forms/elementexport.hxx new file mode 100644 index 000000000..deb9cfea6 --- /dev/null +++ b/xmloff/source/forms/elementexport.hxx @@ -0,0 +1,311 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <memory> +#include <o3tl/sorted_vector.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <xmloff/xmlexp.hxx> +#include "propertyexport.hxx" +#include "callbacks.hxx" +#include "controlelement.hxx" +#include "valueproperties.hxx" + +class SvXMLElementExport; +namespace xmloff +{ + + //= OElementExport + class OElementExport : public OPropertyExport + { + protected: + css::uno::Sequence< css::script::ScriptEventDescriptor > + m_aEvents; + + std::unique_ptr<SvXMLElementExport> m_pXMLElement; // XML element doing the concrete startElement etc. + + public: + OElementExport(IFormsExportContext& _rContext, + const css::uno::Reference< css::beans::XPropertySet >& _rxProps, + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rEvents); + virtual ~OElementExport(); + + void doExport(); + + protected: + /// get the name of the XML element + virtual const char* getXMLElementName() const = 0; + /// examine the element we're exporting + virtual void examine(); + /// export the attributes + virtual void exportAttributes(); + /// export any sub tags + virtual void exportSubTags(); + + /** exports the events (as script:events tag) + */ + void exportEvents(); + + /** add the service-name attribute to the export context + */ + virtual void exportServiceNameAttribute(); + + /// start the XML element + virtual void implStartElement(const char* _pName); + + /// ends the XML element + virtual void implEndElement(); + }; + + //= OControlExport + /** Helper class for handling xml elements representing a form control + */ + class OControlExport + :public OControlElement + ,public OValuePropertiesMetaData + ,public OElementExport + { + protected: + typedef o3tl::sorted_vector<sal_Int16> Int16Set; + // used below + + OUString m_sControlId; // the control id to use when exporting + OUString m_sReferringControls; // list of referring controls (i.e. their id's) + sal_Int16 m_nClassId; // class id of the control we're representing + ElementType m_eType; // (XML) type of the control we're representing + CCAFlags m_nIncludeCommon; // common control attributes to include + DAFlags m_nIncludeDatabase; // common database attributes to include + SCAFlags m_nIncludeSpecial; // special attributes to include + EAFlags m_nIncludeEvents; // events to include + BAFlags m_nIncludeBindings; // binding attributes to include + + std::unique_ptr<SvXMLElementExport> m_pOuterElement; // XML element doing the concrete startElement etc. for the outer element + + public: + /** constructs an object capable of exporting controls + + <p>You need at least two pre-requisites from outside: The control to be exported needs to have a class id + assigned, and you need the list control-ids of all the controls referring to this one as LabelControl.<br/> + This information can't be collected when known only the control itself and not it's complete context.</p> + + @param _rControlId + the control id to use when exporting the control + @param _rReferringControls + the comma-separated list of control-ids of all the controls referring to this one as LabelControl + */ + OControlExport(IFormsExportContext& _rContext, + const css::uno::Reference< css::beans::XPropertySet >& _rxControl, + const OUString& _rControlId, + const OUString& _rReferringControls, + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rxEvents); + + protected: + /// start the XML element + virtual void implStartElement(const char* _pName) override; + + /// ends the XML element + virtual void implEndElement() override; + + /// get the name of the outer XML element + virtual const char* getOuterXMLElementName() const; + + // get the name of the XML element + virtual const char* getXMLElementName() const override; + + /** examine the control. Some kind of CtorImpl. + */ + virtual void examine() override; + + /// exports the attributes for the outer element + void exportOuterAttributes(); + + /// exports the attributes for the inner element + void exportInnerAttributes(); + + /// export the attributes + virtual void exportAttributes() override; + + /** writes everything which needs to be represented as sub tag + */ + void exportSubTags() override; + + /** adds the attributes which are handled via generic IPropertyHandlers + + <p>In the future, this really should be *all* attributes, instead of this shitload of + hand-crafted code we have currently...</p> + */ + void exportGenericHandlerAttributes(); + + /** adds common control attributes to the XMLExport context given + + <p>The attribute list of the context is not cleared initially, this is the responsibility of the caller.</p> + */ + void exportCommonControlAttributes(); + + /** adds database attributes to the XMLExport context given + + <p>The attribute list of the context is not cleared initially, this is the responsibility of the caller.</p> + */ + void exportDatabaseAttributes(); + + /** adds the XML attributes which are related to binding controls to + external values and/or list sources + */ + void exportBindingAttributes(); + + /** adds attributes which are special to a control type to the export context's attribute list + */ + void exportSpecialAttributes(); + + /** exports the ListSource property of a control as attribute + + The ListSource property may be exported in different ways: For a ComboBox, it is an attribute + of the form:combobox element. + + For a ListBox, it's an attribute if the ListSourceType states that the ListBox does <em>not</em> + display a value list. In case of a value list, the ListSource is not exported, and the pairs of + StringItem/ValueItem are exported as sub-elements. + + This method does the attribute part: It exports the ListSource property as attribute, not caring + about whether the object is a ComboBox or a ListBox. + */ + void exportListSourceAsAttribute(); + + /** exports the ListSource property of a control as XML elements + + @see exportListSourceAsAttribute + */ + void exportListSourceAsElements(); + + /** gets a Sequence< sal_Int16 > property value as set of sal_Int16's + @param _rPropertyName + the property name to use + @param _rOut + out parameter. The set of integers. + */ + void getSequenceInt16PropertyAsSet(const OUString& _rPropertyName, Int16Set& _rOut); + + /** exports the attribute which descrives a cell value binding of a control + in a spreadsheet document + */ + void exportCellBindingAttributes( bool _bIncludeListLinkageType ); + + /** exports the attribute(s) which bind this control to XForms */ + void exportXFormsBindAttributes(); + + /** exports the attribute(s) which bind the list of a list + control to XForms */ + void exportXFormsListAttributes(); + + /** exports the attribute(s) for an XForms submission */ + void exportXFormsSubmissionAttributes(); + + /** exports the attribute which descrives a cell range which acts as list source for + a list-like control + */ + void exportCellListSourceRange( ); + + /** exports the attribute(s) for the ImagePosition property + */ + void exportImagePositionAttributes(); + + /** determines whether the control we're exporting has an active data binding. + + Bindings which count here are: + <ul><li>an established connection to a database field</li> + <li>a binding to an external value supplier (<type scope="css::form::binding">XValueBinding</type>)</li> + </ul> + */ + bool controlHasActiveDataBinding() const; + + /** retrieves the string specifying the ListSource of a list or combo box + */ + OUString getScalarListSourceValue() const; + + /** determines whether the list entries (of a combo or list box) are supplied by the user + + List entries may be + <ul><li>specified by the user</li> + <li>specified by an external list source (<type scope="css::form::binding">XListEntrySource</type>)</li> + <li>obtained from a database query (in various ways)</li> + </ul> + + In the latter two cases, this method will return <FALSE/> + */ + bool controlHasUserSuppliedListEntries() const; + }; + + //= OColumnExport + /** Helper class for exporting a grid column + */ + class OColumnExport : public OControlExport + { + public: + /** ctor + @see OColumnExport::OColumnExport + */ + OColumnExport(IFormsExportContext& _rContext, + const css::uno::Reference< css::beans::XPropertySet >& _rxControl, + const OUString& _rControlId, + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rxEvents); + + virtual ~OColumnExport() override; + + protected: + // OControlExport overridables + virtual const char* getOuterXMLElementName() const override; + virtual void exportServiceNameAttribute() override; + virtual void exportAttributes() override; + + // OElementExport overridables + virtual void examine() override; + }; + + //= OFormExport + /** Helper class for handling xml elements representing a form + + <p>In opposite to the class <type>OControlExport</type>, OFormExport is unable to export a <em>complete</em> + form. Instead the client has to care for sub elements of the form itself.</p> + */ + class OFormExport + :public OControlElement + ,public OElementExport + { + bool m_bCreateConnectionResourceElement; + public: + /** constructs an object capable of exporting controls + */ + OFormExport(IFormsExportContext& _rContext, + const css::uno::Reference< css::beans::XPropertySet >& _rxForm, + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rxEvents + ); + + protected: + virtual const char* getXMLElementName() const override; + virtual void exportSubTags() override; + virtual void exportAttributes() override; + }; +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/elementimport.cxx b/xmloff/source/forms/elementimport.cxx new file mode 100644 index 000000000..19f247aae --- /dev/null +++ b/xmloff/source/forms/elementimport.cxx @@ -0,0 +1,2073 @@ +/* -*- 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 "elementimport.hxx" +#include <xmloff/xmlimp.hxx> +#include <xmloff/namespacemap.hxx> +#include "strings.hxx" +#include "callbacks.hxx" +#include <xmloff/xmlnamespace.hxx> +#include "eventimport.hxx" +#include <xmloff/txtstyli.hxx> +#include "formenums.hxx" +#include <xmloff/xmltoken.hxx> +#include "gridcolumnproptranslator.hxx" +#include "property_description.hxx" +#include "property_meta_data.hxx" + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/util/Duration.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/awt/ImagePosition.hpp> +#include <com/sun/star/beans/XMultiPropertySet.hpp> +#include <com/sun/star/beans/XPropertyContainer.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +#include <sax/tools/converter.hxx> +#include <tools/urlobj.hxx> +#include <tools/diagnose_ex.h> +#include <rtl/strbuf.hxx> +#include <sal/log.hxx> +#include <comphelper/extract.hxx> +#include <comphelper/types.hxx> +#include <comphelper/sequence.hxx> +#include <o3tl/string_view.hxx> + +#include <algorithm> + +namespace xmloff +{ + + using namespace ::xmloff::token; + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::xml; + using namespace ::com::sun::star::xml::sax; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::text; + using namespace ::comphelper; + using ::com::sun::star::xml::sax::XAttributeList; + +#define PROPID_VALUE 1 +#define PROPID_CURRENT_VALUE 2 +#define PROPID_MIN_VALUE 3 +#define PROPID_MAX_VALUE 4 + + namespace { + + struct PropertyValueLess + { + bool operator()(const PropertyValue& _rLeft, const PropertyValue& _rRight) + { + return _rLeft.Name < _rRight.Name; + } + }; + + } + + //= OElementNameMap + std::map<sal_Int32, OControlElement::ElementType> OElementNameMap::s_sElementTranslations2; + + const OControlElement::ElementType& operator ++(OControlElement::ElementType& _e) + { + OControlElement::ElementType e = _e; + sal_Int32 nAsInt = static_cast<sal_Int32>(e); + _e = static_cast<OControlElement::ElementType>( ++nAsInt ); + return _e; + } + + OControlElement::ElementType OElementNameMap::getElementType(sal_Int32 nElement) + { + if ( s_sElementTranslations2.empty() ) + { // initialize + for (ElementType eType=ElementType(0); eType<UNKNOWN; ++eType) + s_sElementTranslations2[getElementToken(eType)] = eType; + } + auto aPos = s_sElementTranslations2.find(nElement & TOKEN_MASK); + if (s_sElementTranslations2.end() != aPos) + return aPos->second; + + return UNKNOWN; + } + + //= OElementImport + OElementImport::OElementImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer) + :OPropertyImport(_rImport) + ,m_rFormImport(_rImport) + ,m_rEventManager(_rEventManager) + ,m_pStyleElement( nullptr ) + ,m_xParentContainer(_rxParentContainer) + ,m_bImplicitGenericAttributeHandling( true ) + { + OSL_ENSURE(m_xParentContainer.is(), "OElementImport::OElementImport: invalid parent container!"); + } + + OElementImport::~OElementImport() + { + } + + OUString OElementImport::determineDefaultServiceName() const + { + return OUString(); + } + + void OElementImport::startFastElement(sal_Int32 nElement, const Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) + { + ENTER_LOG_CONTEXT( "xmloff::OElementImport - importing one element" ); + + const OUString sControlImplementation = _rxAttrList->getOptionalValue( XML_ELEMENT(FORM, XML_CONTROL_IMPLEMENTATION) ); + + // retrieve the service name + if ( !sControlImplementation.isEmpty() ) + { + OUString sOOoImplementationName; + const sal_uInt16 nImplPrefix = GetImport().GetNamespaceMap().GetKeyByAttrValueQName( sControlImplementation, &sOOoImplementationName ); + m_sServiceName = ( nImplPrefix == XML_NAMESPACE_OOO ) ? sOOoImplementationName : sControlImplementation; + } + + if ( m_sServiceName.isEmpty() ) + m_sServiceName = determineDefaultServiceName(); + + // create the object *now*. This allows setting properties in the various handleAttribute methods. + // (Though currently not all code is migrated to this pattern, most attributes are still handled + // by remembering the value (via implPushBackPropertyValue), and setting the correct property value + // later (in OControlImport::StartElement).) + m_xElement = createElement(); + if ( m_xElement.is() ) + m_xInfo = m_xElement->getPropertySetInfo(); + + // call the base class + OPropertyImport::startFastElement( nElement, _rxAttrList ); + } + + css::uno::Reference< css::xml::sax::XFastContextHandler > OElementImport::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList ) + { + if( nElement == XML_ELEMENT(OFFICE, XML_EVENT_LISTENERS) ) + return new OFormEventsImportContext(m_rFormImport.getGlobalContext(), *this); + + return OPropertyImport::createFastChildContext(nElement, _rxAttrList); + } + + void OElementImport::endFastElement(sal_Int32 ) + { + OSL_ENSURE(m_xElement.is(), "OElementImport::EndElement: invalid element created!"); + if (!m_xElement.is()) + return; + + // apply the non-generic properties + implApplySpecificProperties(); + + // set the generic properties + implApplyGenericProperties(); + + // set the style properties + if ( m_pStyleElement && m_xElement.is() ) + { + Reference< XPropertySet > xPropTranslation = + new OGridColumnPropertyTranslator( Reference< XMultiPropertySet >( m_xElement, UNO_QUERY ) ); + const_cast< XMLTextStyleContext* >( m_pStyleElement )->FillPropertySet( xPropTranslation ); + + const OUString sNumberStyleName = m_pStyleElement->GetDataStyleName( ); + if ( !sNumberStyleName.isEmpty() ) + // the style also has a number (sub) style + m_rContext.applyControlNumberStyle( m_xElement, sNumberStyleName ); + } + + // insert the element into the parent container + if (m_sName.isEmpty()) + { + OSL_FAIL("OElementImport::EndElement: did not find a name attribute!"); + m_sName = implGetDefaultName(); + } + + if (m_xParentContainer.is()) + m_xParentContainer->insertByName(m_sName, Any(m_xElement)); + + LEAVE_LOG_CONTEXT( ); + } + + void OElementImport::implApplySpecificProperties() + { + if ( m_aValues.empty() ) + return; + + // set all the properties we collected +#if OSL_DEBUG_LEVEL > 0 + // check if the object has all the properties + // (We do this in the non-pro version only. Doing it all the time would be too much expensive) + if ( m_xInfo.is() ) + { + for ( const auto& rCheck : m_aValues ) + { + OSL_ENSURE(m_xInfo->hasPropertyByName(rCheck.Name), + OStringBuffer("OElementImport::implApplySpecificProperties: read a property (" + + OUStringToOString(rCheck.Name, RTL_TEXTENCODING_ASCII_US) + + ") which does not exist on the element!").getStr()); + } + } +#endif + + // set the properties + const Reference< XMultiPropertySet > xMultiProps(m_xElement, UNO_QUERY); + bool bSuccess = false; + if (xMultiProps.is()) + { + // translate our properties so that the XMultiPropertySet can handle them + + // sort our property value array so that we can use it in a setPropertyValues + ::std::sort( m_aValues.begin(), m_aValues.end(), PropertyValueLess()); + + // the names + Sequence< OUString > aNames(m_aValues.size()); + OUString* pNames = aNames.getArray(); + // the values + Sequence< Any > aValues(m_aValues.size()); + Any* pValues = aValues.getArray(); + // copy + + for ( const auto& rPropValues : m_aValues ) + { + *pNames = rPropValues.Name; + *pValues = rPropValues.Value; + ++pNames; + ++pValues; + } + + try + { + xMultiProps->setPropertyValues(aNames, aValues); + bSuccess = true; + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("xmloff.forms"); + OSL_FAIL("OElementImport::implApplySpecificProperties: could not set the properties (using the XMultiPropertySet)!"); + } + } + + if (bSuccess) + return; + + // no XMultiPropertySet or setting all properties at once failed + for ( const auto& rPropValues : m_aValues ) + { + // this try/catch here is expensive, but because this is just a fallback which should normally not be + // used it's acceptable this way ... + try + { + m_xElement->setPropertyValue(rPropValues.Name, rPropValues.Value); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("xmloff.forms"); + OSL_FAIL(OStringBuffer("OElementImport::implApplySpecificProperties: could not set the property \"" + + OUStringToOString(rPropValues.Name, RTL_TEXTENCODING_ASCII_US) + + "\"!").getStr()); + } + } + } + + void OElementImport::implApplyGenericProperties() + { + if ( m_aGenericValues.empty() ) + return; + + Reference< XPropertyContainer > xDynamicProperties( m_xElement, UNO_QUERY ); + + // PropertyValueArray::iterator aEnd = m_aGenericValues.end(); + for ( auto& rPropValues : m_aGenericValues ) + { + // check property type for numeric types before setting + // the property + try + { + // if such a property does not yet exist at the element, create it if necessary + const bool bExistentProperty = m_xInfo->hasPropertyByName( rPropValues.Name ); + if ( !bExistentProperty ) + { + if ( !xDynamicProperties.is() ) + { + SAL_WARN( "xmloff", "OElementImport::implApplyGenericProperties: encountered an unknown property (" + << rPropValues.Name << "), but component is no PropertyBag!"); + continue; + } + + xDynamicProperties->addProperty( + rPropValues.Name, + PropertyAttribute::BOUND | PropertyAttribute::REMOVABLE, + rPropValues.Value + ); + + // re-fetch the PropertySetInfo + m_xInfo = m_xElement->getPropertySetInfo(); + } + + // determine the type of the value (source for the following conversion) + TypeClass eValueTypeClass = rPropValues.Value.getValueTypeClass(); + const bool bValueIsSequence = TypeClass_SEQUENCE == eValueTypeClass; + if ( bValueIsSequence ) + { + uno::Type aSimpleType( getSequenceElementType( rPropValues.Value.getValueType() ) ); + eValueTypeClass = aSimpleType.getTypeClass(); + } + + // determine the type of the property (target for the following conversion) + const Property aProperty( m_xInfo->getPropertyByName( rPropValues.Name ) ); + TypeClass ePropTypeClass = aProperty.Type.getTypeClass(); + const bool bPropIsSequence = TypeClass_SEQUENCE == ePropTypeClass; + if( bPropIsSequence ) + { + uno::Type aSimpleType( ::comphelper::getSequenceElementType( aProperty.Type ) ); + ePropTypeClass = aSimpleType.getTypeClass(); + } + + if ( bPropIsSequence != bValueIsSequence ) + { + OSL_FAIL( "OElementImport::implImportGenericProperties: either both value and property should be a sequence, or none of them!" ); + continue; + } + + if ( bValueIsSequence ) + { + Sequence< Any > aXMLValueList; + rPropValues.Value >>= aXMLValueList; + // just skip this part if empty sequence + if (!aXMLValueList.getLength()) + continue; + + Sequence< sal_Int16 > aPropertyValueList( aXMLValueList.getLength() ); + + SAL_WARN_IF( eValueTypeClass != TypeClass_ANY, "xmloff", + "OElementImport::implApplyGenericProperties: only ANYs should have been imported as generic list property!" ); + // (OPropertyImport should produce only Sequencer< Any >, since it cannot know the real type + + SAL_WARN_IF( ePropTypeClass != TypeClass_SHORT, "xmloff", + "OElementImport::implApplyGenericProperties: conversion to sequences other than 'sequence< short >' not implemented, yet!" ); + + + std::transform(std::cbegin(aXMLValueList), std::cend(aXMLValueList), aPropertyValueList.getArray(), + [](const Any& rXMLValue) -> sal_Int16 { + // only value sequences of numeric types implemented so far. + double nVal( 0 ); + OSL_VERIFY( rXMLValue >>= nVal ); + return static_cast< sal_Int16 >( nVal ); + }); + + rPropValues.Value <<= aPropertyValueList; + } + else if ( ePropTypeClass != eValueTypeClass ) + { + switch ( eValueTypeClass ) + { + case TypeClass_DOUBLE: + { + double nVal = 0; + rPropValues.Value >>= nVal; + switch( ePropTypeClass ) + { + case TypeClass_BYTE: + rPropValues.Value <<= static_cast< sal_Int8 >( nVal ); + break; + case TypeClass_SHORT: + rPropValues.Value <<= static_cast< sal_Int16 >( nVal ); + break; + case TypeClass_UNSIGNED_SHORT: + rPropValues.Value <<= static_cast< sal_uInt16 >( nVal ); + break; + case TypeClass_LONG: + case TypeClass_ENUM: + rPropValues.Value <<= static_cast< sal_Int32 >( nVal ); + break; + case TypeClass_UNSIGNED_LONG: + rPropValues.Value <<= static_cast< sal_uInt32 >( nVal ); + break; + case TypeClass_UNSIGNED_HYPER: + rPropValues.Value <<= static_cast< sal_uInt64 >( nVal ); + break; + case TypeClass_HYPER: + rPropValues.Value <<= static_cast< sal_Int64 >( nVal ); + break; + default: + OSL_FAIL( "OElementImport::implImportGenericProperties: unsupported value type!" ); + break; + } + } + break; + default: + OSL_FAIL( "OElementImport::implImportGenericProperties: non-double values not supported!" ); + break; + } + } + + m_xElement->setPropertyValue( rPropValues.Name, rPropValues.Value ); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("xmloff.forms"); + OSL_FAIL(OStringBuffer("OElementImport::EndElement: could not set the property \"" + + OUStringToOString(rPropValues.Name, RTL_TEXTENCODING_ASCII_US) + + "\"!").getStr()); + } + } + } + + OUString OElementImport::implGetDefaultName() const + { + // no optimization here. If this method gets called, the XML stream did not contain a name for the + // element, which is a heavy error. So in this case we don't care for performance + static constexpr OUStringLiteral sUnnamedName = u"unnamed"; + OSL_ENSURE(m_xParentContainer.is(), "OElementImport::implGetDefaultName: no parent container!"); + if (!m_xParentContainer.is()) + return sUnnamedName; + Sequence< OUString > aNames = m_xParentContainer->getElementNames(); + + for (sal_Int32 i=0; i<32768; ++i) // the limit is nearly arbitrary... + { + // assemble the new name (suggestion) + OUString sReturn = sUnnamedName + OUString::number(i); + // check the existence (this is the bad performance part...) + if (comphelper::findValue(aNames, sReturn) == -1) + // not found the name + return sReturn; + } + OSL_FAIL("OElementImport::implGetDefaultName: did not find a free name!"); + return sUnnamedName; + } + + PropertyGroups::const_iterator OElementImport::impl_matchPropertyGroup( const PropertyGroups& i_propertyGroups ) const + { + ENSURE_OR_RETURN( m_xInfo.is(), "OElementImport::impl_matchPropertyGroup: no property set info!", i_propertyGroups.end() ); + + return std::find_if(i_propertyGroups.cbegin(), i_propertyGroups.cend(), [&](const PropertyDescriptionList& rGroup) { + return std::all_of(rGroup.cbegin(), rGroup.cend(), [&](const PropertyDescription* prop) { + return m_xInfo->hasPropertyByName( prop->propertyName ); + }); + }); + } + + bool OElementImport::tryGenericAttribute( sal_Int32 nElement, const OUString& _rValue ) + { + // the generic approach (which I hope all props will be migrated to, on the medium term): property handlers + const AttributeDescription attribute( metadata::getAttributeDescription( nElement ) ); + if ( attribute.attributeToken != XML_TOKEN_INVALID ) + { + PropertyGroups propertyGroups; + metadata::getPropertyGroupList( attribute, propertyGroups ); + const PropertyGroups::const_iterator pos = impl_matchPropertyGroup( propertyGroups ); + if ( pos == propertyGroups.end() ) + return false; + + do + { + const PropertyDescriptionList& rProperties( *pos ); + const PropertyDescription* first = *rProperties.begin(); + if ( !first ) + { + SAL_WARN( "xmloff.forms", "OElementImport::handleAttribute: invalid property description!" ); + break; + } + + const PPropertyHandler handler = (*first->factory)( first->propertyId ); + if ( !handler ) + { + SAL_WARN( "xmloff.forms", "OElementImport::handleAttribute: invalid property handler!" ); + break; + } + + PropertyValues aValues; + for ( const auto& propDesc : rProperties ) + { + aValues[ propDesc->propertyId ] = Any(); + } + if ( handler->getPropertyValues( _rValue, aValues ) ) + { + for ( const auto& propDesc : rProperties ) + { + implPushBackPropertyValue( propDesc->propertyName, aValues[ propDesc->propertyId ] ); + } + } + } + while ( false ); + + // handled + return true; + } + return false; + } + + bool OElementImport::handleAttribute(sal_Int32 nElement, const OUString& _rValue) + { + auto nLocal = nElement & TOKEN_MASK; + if ( nLocal == XML_CONTROL_IMPLEMENTATION ) + // ignore this, it has already been handled in OElementImport::StartElement + return true; + + if ( nLocal == XML_NAME ) + { + if ( m_sName.isEmpty() ) + // remember the name for later use in EndElement + m_sName = _rValue; + return true; + } + + // maybe it's the style attribute? + if ( nLocal == XML_TEXT_STYLE_NAME ) + { + const SvXMLStyleContext* pStyleContext = m_rContext.getStyleElement( _rValue ); + OSL_ENSURE( pStyleContext, "OElementImport::handleAttribute: do not know the style!" ); + // remember the element for later usage. + m_pStyleElement = dynamic_cast<const XMLTextStyleContext*>( pStyleContext ); + return true; + } + + if ( m_bImplicitGenericAttributeHandling ) + if ( tryGenericAttribute( nElement, _rValue ) ) + return true; + + // let the base class handle it + return OPropertyImport::handleAttribute( nElement, _rValue); + } + + Reference< XPropertySet > OElementImport::createElement() + { + Reference< XPropertySet > xReturn; + if (!m_sServiceName.isEmpty()) + { + Reference< XComponentContext > xContext = m_rFormImport.getGlobalContext().GetComponentContext(); + Reference< XInterface > xPure = xContext->getServiceManager()->createInstanceWithContext(m_sServiceName, xContext); + OSL_ENSURE(xPure.is(), + OStringBuffer("OElementImport::createElement: service factory gave me no object (service name: " + + OUStringToOString(m_sServiceName, RTL_TEXTENCODING_ASCII_US) + + ")!").getStr()); + xReturn.set(xPure, UNO_QUERY); + } + else + OSL_FAIL("OElementImport::createElement: no service name to create an element!"); + + return xReturn; + } + + void OElementImport::registerEvents(const Sequence< ScriptEventDescriptor >& _rEvents) + { + OSL_ENSURE(m_xElement.is(), "OElementImport::registerEvents: no element to register events for!"); + m_rEventManager.registerEvents(m_xElement, _rEvents); + } + + void OElementImport::simulateDefaultedAttribute(sal_Int32 nElement, const OUString& _rPropertyName, const char* _pAttributeDefault) + { + OSL_ENSURE( m_xInfo.is(), "OPropertyImport::simulateDefaultedAttribute: the component should be more gossipy about it's properties!" ); + + if ( !m_xInfo.is() || m_xInfo->hasPropertyByName( _rPropertyName ) ) + { + if ( !encounteredAttribute( nElement ) ) + OSL_VERIFY( handleAttribute( XML_ELEMENT(FORM, (nElement & TOKEN_MASK)), OUString::createFromAscii( _pAttributeDefault ) ) ); + } + } + + //= OControlImport + OControlImport::OControlImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer) + :OElementImport(_rImport, _rEventManager, _rxParentContainer) + ,m_eElementType(OControlElement::UNKNOWN) + { + disableImplicitGenericAttributeHandling(); + } + + OControlImport::OControlImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer, OControlElement::ElementType _eType) + :OElementImport(_rImport, _rEventManager, _rxParentContainer) + ,m_eElementType(_eType) + { + disableImplicitGenericAttributeHandling(); + } + + OUString OControlImport::determineDefaultServiceName() const + { + const char* pServiceName = nullptr; + switch ( m_eElementType ) + { + case OControlElement::TEXT: + case OControlElement::TEXT_AREA: + case OControlElement::PASSWORD: pServiceName = "com.sun.star.form.component.TextField"; break; + case OControlElement::FILE: pServiceName = "com.sun.star.form.component.FileControl"; break; + case OControlElement::FORMATTED_TEXT: pServiceName = "com.sun.star.form.component.FormattedField"; break; + case OControlElement::FIXED_TEXT: pServiceName = "com.sun.star.form.component.FixedText"; break; + case OControlElement::COMBOBOX: pServiceName = "com.sun.star.form.component.ComboBox"; break; + case OControlElement::LISTBOX: pServiceName = "com.sun.star.form.component.ListBox"; break; + case OControlElement::BUTTON: pServiceName = "com.sun.star.form.component.CommandButton"; break; + case OControlElement::IMAGE: pServiceName = "com.sun.star.form.component.ImageButton"; break; + case OControlElement::CHECKBOX: pServiceName = "com.sun.star.form.component.CheckBox"; break; + case OControlElement::RADIO: pServiceName = "com.sun.star.form.component.RadioButton"; break; + case OControlElement::FRAME: pServiceName = "com.sun.star.form.component.GroupBox"; break; + case OControlElement::IMAGE_FRAME: pServiceName = "com.sun.star.form.component.DatabaseImageControl"; break; + case OControlElement::HIDDEN: pServiceName = "com.sun.star.form.component.HiddenControl"; break; + case OControlElement::GRID: pServiceName = "com.sun.star.form.component.GridControl"; break; + case OControlElement::VALUERANGE: pServiceName = "com.sun.star.form.component.ScrollBar"; break; + case OControlElement::TIME: pServiceName = "com.sun.star.form.component.TimeField"; break; + case OControlElement::DATE: pServiceName = "com.sun.star.form.component.DateField"; break; + default: break; + } + if ( pServiceName != nullptr ) + return OUString::createFromAscii( pServiceName ); + return OUString(); + } + + void OControlImport::addOuterAttributes(const Reference< XFastAttributeList >& _rxOuterAttribs) + { + OSL_ENSURE(!m_xOuterAttributes.is(), "OControlImport::addOuterAttributes: already have these attributes!"); + m_xOuterAttributes = _rxOuterAttribs; + } + + bool OControlImport::handleAttribute(sal_Int32 nElement, const OUString& _rValue) + { + static sal_Int32 nLinkedCellAttributeName = OAttributeMetaData::getBindingAttributeToken(BAFlags::LinkedCell); + + if ((nElement & TOKEN_MASK) == XML_ID) + { // it's the control id + if (IsTokenInNamespace(nElement, XML_NAMESPACE_XML)) + { + m_sControlId = _rValue; + } + else if (IsTokenInNamespace(nElement, XML_NAMESPACE_FORM)) + { + if (m_sControlId.isEmpty()) + { + m_sControlId = _rValue; + } + } + return true; + } + + if ( (nElement & TOKEN_MASK) == nLinkedCellAttributeName ) + { // it's the address of a spreadsheet cell + m_sBoundCellAddress = _rValue; + return true; + } + + if ( nElement == XML_ELEMENT(XFORMS, XML_BIND ) ) + { + m_sBindingID = _rValue; + return true; + } + + if ( nElement == XML_ELEMENT(FORM, XML_XFORMS_LIST_SOURCE) ) + { + m_sListBindingID = _rValue; + return true; + } + + if ( nElement == XML_ELEMENT(FORM, XML_XFORMS_SUBMISSION) + || nElement == XML_ELEMENT(XFORMS, XML_SUBMISSION) ) + { + m_sSubmissionID = _rValue; + return true; + } + + if ( OElementImport::tryGenericAttribute( nElement, _rValue ) ) + return true; + + static const sal_Int32 nValueAttributeName = OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Value); + static const sal_Int32 nCurrentValueAttributeName = OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::CurrentValue); + static const sal_Int32 nMinValueAttributeName = OAttributeMetaData::getSpecialAttributeToken(SCAFlags::MinValue); + static const sal_Int32 nMaxValueAttributeName = OAttributeMetaData::getSpecialAttributeToken(SCAFlags::MaxValue); + static const sal_Int32 nRepeatDelayAttributeName = OAttributeMetaData::getSpecialAttributeToken( SCAFlags::RepeatDelay ); + + sal_Int32 nHandle = -1; + if ( (nElement & TOKEN_MASK) == nValueAttributeName ) + nHandle = PROPID_VALUE; + else if ( (nElement & TOKEN_MASK) == nCurrentValueAttributeName ) + nHandle = PROPID_CURRENT_VALUE; + else if ( (nElement & TOKEN_MASK) == nMinValueAttributeName ) + nHandle = PROPID_MIN_VALUE; + else if ( (nElement & TOKEN_MASK) == nMaxValueAttributeName ) + nHandle = PROPID_MAX_VALUE; + if ( nHandle != -1 ) + { + // for the moment, simply remember the name and the value + PropertyValue aProp; + aProp.Name = SvXMLImport::getNameFromToken(nElement); + aProp.Handle = nHandle; + aProp.Value <<= _rValue; + m_aValueProperties.push_back(aProp); + return true; + } + + if ( (nElement & TOKEN_MASK) == nRepeatDelayAttributeName ) + { + util::Duration aDuration; + if (::sax::Converter::convertDuration(aDuration, _rValue)) + { + PropertyValue aProp; + aProp.Name = PROPERTY_REPEAT_DELAY; + sal_Int32 const nMS = + ((aDuration.Hours * 60 + aDuration.Minutes) * 60 + + aDuration.Seconds) * 1000 + aDuration.NanoSeconds/1000000; + aProp.Value <<= nMS; + + implPushBackPropertyValue(aProp); + } + return true; + } + + return OElementImport::handleAttribute( nElement, _rValue ); + } + + void OControlImport::startFastElement(sal_Int32 nElement, const Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) + { + css::uno::Reference< css::xml::sax::XFastAttributeList > xMergedAttributes; + if( m_xOuterAttributes.is() ) + { + // merge the attribute lists, our own one + rtl::Reference<sax_fastparser::FastAttributeList> xMerger(new sax_fastparser::FastAttributeList(_rxAttrList)); + // and the ones of our enclosing element + xMerger->add(m_xOuterAttributes); + xMergedAttributes = xMerger.get(); + } + else + { + xMergedAttributes = _rxAttrList; + } + + // let the base class handle all the attributes + OElementImport::startFastElement(nElement, xMergedAttributes); + + if ( m_aValueProperties.empty() || !m_xElement.is()) + return; + + // get the property set info + if (!m_xInfo.is()) + { + OSL_FAIL("OControlImport::StartElement: no PropertySetInfo!"); + return; + } + + const char* pValueProperty = nullptr; + const char* pCurrentValueProperty = nullptr; + const char* pMinValueProperty = nullptr; + const char* pMaxValueProperty = nullptr; + + bool bRetrievedValues = false; + bool bRetrievedValueLimits = false; + + // get the class id of our element + sal_Int16 nClassId = FormComponentType::CONTROL; + m_xElement->getPropertyValue(PROPERTY_CLASSID) >>= nClassId; + + // translate the value properties we collected in handleAttributes + for ( auto& rValueProps : m_aValueProperties ) + { + bool bSuccess = false; + switch (rValueProps.Handle) + { + case PROPID_VALUE: + case PROPID_CURRENT_VALUE: + { + // get the property names + if (!bRetrievedValues) + { + getValuePropertyNames(m_eElementType, nClassId, pCurrentValueProperty, pValueProperty); + if ( !pCurrentValueProperty && !pValueProperty ) + { + SAL_WARN( "xmloff.forms", "OControlImport::StartElement: illegal value property names!" ); + break; + } + + bRetrievedValues = true; + } + if ( PROPID_VALUE == rValueProps.Handle && !pValueProperty ) + { + SAL_WARN( "xmloff.forms", "OControlImport::StartElement: the control does not have a value property!"); + break; + } + + if ( PROPID_CURRENT_VALUE == rValueProps.Handle && !pCurrentValueProperty ) + { + SAL_WARN( "xmloff.forms", "OControlImport::StartElement: the control does not have a current-value property!"); + break; + } + + // transfer the name + if (PROPID_VALUE == rValueProps.Handle) + rValueProps.Name = OUString::createFromAscii(pValueProperty); + else + rValueProps.Name = OUString::createFromAscii(pCurrentValueProperty); + bSuccess = true; + } + break; + case PROPID_MIN_VALUE: + case PROPID_MAX_VALUE: + { + // get the property names + if (!bRetrievedValueLimits) + { + getValueLimitPropertyNames(nClassId, pMinValueProperty, pMaxValueProperty); + if ( !pMinValueProperty || !pMaxValueProperty ) + { + SAL_WARN( "xmloff.forms", "OControlImport::StartElement: illegal value limit property names!" ); + break; + } + + bRetrievedValueLimits = true; + } + OSL_ENSURE((PROPID_MIN_VALUE != rValueProps.Handle) || pMinValueProperty, + "OControlImport::StartElement: the control does not have a value property!"); + OSL_ENSURE((PROPID_MAX_VALUE != rValueProps.Handle) || pMaxValueProperty, + "OControlImport::StartElement: the control does not have a current-value property!"); + + // transfer the name + if (PROPID_MIN_VALUE == rValueProps.Handle) + rValueProps.Name = OUString::createFromAscii(pMinValueProperty); + else + rValueProps.Name = OUString::createFromAscii(pMaxValueProperty); + bSuccess = true; + } + break; + } + + if ( !bSuccess ) + continue; + + // translate the value + implTranslateValueProperty(m_xInfo, rValueProps); + // add the property to the base class' array + implPushBackPropertyValue(rValueProps); + } + + } + + void OControlImport::implTranslateValueProperty(const Reference< XPropertySetInfo >& _rxPropInfo, + PropertyValue& _rPropValue) + { + OSL_ENSURE(_rxPropInfo->hasPropertyByName(_rPropValue.Name), + "OControlImport::implTranslateValueProperty: invalid property name!"); + + // retrieve the type of the property + Property aProp = _rxPropInfo->getPropertyByName(_rPropValue.Name); + // the untranslated string value as read in handleAttribute + OUString sValue; + bool bSuccess = _rPropValue.Value >>= sValue; + OSL_ENSURE(bSuccess, "OControlImport::implTranslateValueProperty: supposed to be called with non-translated string values!"); + + if (TypeClass_ANY == aProp.Type.getTypeClass()) + { + // we have exactly 2 properties where this type class is allowed: + SAL_WARN_IF( + _rPropValue.Name != PROPERTY_EFFECTIVE_VALUE + && _rPropValue.Name != PROPERTY_EFFECTIVE_DEFAULT, "xmloff", + "OControlImport::implTranslateValueProperty: invalid property type/name combination, Any and " << _rPropValue.Name); + + // Both properties are allowed to have a double or a string value, + // so first try to convert the string into a number + double nValue; + if (::sax::Converter::convertDouble(nValue, sValue)) + _rPropValue.Value <<= nValue; + else + _rPropValue.Value <<= sValue; + } + else + _rPropValue.Value = PropertyConversion::convertString(aProp.Type, sValue); + } + + void OControlImport::endFastElement(sal_Int32 nElement) + { + OSL_ENSURE(m_xElement.is(), "OControlImport::EndElement: invalid control!"); + if ( !m_xElement.is() ) + return; + + // register our control with its id + if (!m_sControlId.isEmpty()) + m_rFormImport.registerControlId(m_xElement, m_sControlId); + // it's allowed to have no control id. In this case we're importing a column + + // one more pre-work to do: + // when we set default values, then by definition the respective value is set + // to this default value, too. This means if the sequence contains for example + // a DefaultText value, then the Text will be affected by this, too. + // In case the Text is not part of the property sequence (or occurs _before_ + // the DefaultText, which can happen for other value/default-value property names), + // this means that the Text (the value property) is incorrectly imported. + + bool bRestoreValuePropertyValue = false; + Any aValuePropertyValue; + + sal_Int16 nClassId = FormComponentType::CONTROL; + try + { + // get the class id of our element + m_xElement->getPropertyValue(PROPERTY_CLASSID) >>= nClassId; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.forms", + "caught an exception while retrieving the class id!"); + } + + const char* pValueProperty = nullptr; + const char* pDefaultValueProperty = nullptr; + getRuntimeValuePropertyNames(m_eElementType, nClassId, pValueProperty, pDefaultValueProperty); + if ( pDefaultValueProperty && pValueProperty ) + { + bool bNonDefaultValuePropertyValue = false; + // is the "value property" part of the sequence? + + // look up this property in our sequence + for ( const auto& rCheck : m_aValues ) + { + if ( rCheck.Name.equalsAscii( pDefaultValueProperty ) ) + bRestoreValuePropertyValue = true; + else if ( rCheck.Name.equalsAscii( pValueProperty ) ) + { + bNonDefaultValuePropertyValue = true; + // we need to restore the value property we found here, nothing else + aValuePropertyValue = rCheck.Value; + } + } + + if ( bRestoreValuePropertyValue && !bNonDefaultValuePropertyValue ) + { + // found it -> need to remember (and restore) the "value property value", which is not set explicitly + try + { + aValuePropertyValue = m_xElement->getPropertyValue( OUString::createFromAscii( pValueProperty ) ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( + "xmloff.forms", + "caught an exception while retrieving the current value property!"); + } + } + } + + // let the base class set all the values + OElementImport::endFastElement(nElement); + + // restore the "value property value", if necessary + if ( bRestoreValuePropertyValue && pValueProperty ) + { + try + { + m_xElement->setPropertyValue( OUString::createFromAscii( pValueProperty ), aValuePropertyValue ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.forms", + "caught an exception while restoring the value property!"); + } + } + + // the external cell binding, if applicable + if ( m_xElement.is() && !m_sBoundCellAddress.isEmpty() ) + doRegisterCellValueBinding( m_sBoundCellAddress ); + + // XForms binding, if applicable + if ( m_xElement.is() && !m_sBindingID.isEmpty() ) + doRegisterXFormsValueBinding( m_sBindingID ); + + // XForms list binding, if applicable + if ( m_xElement.is() && !m_sListBindingID.isEmpty() ) + doRegisterXFormsListBinding( m_sListBindingID ); + + // XForms submission, if applicable + if ( m_xElement.is() && !m_sSubmissionID.isEmpty() ) + doRegisterXFormsSubmission( m_sSubmissionID ); + } + + void OControlImport::doRegisterCellValueBinding( const OUString& _rBoundCellAddress ) + { + OSL_PRECOND( m_xElement.is(), "OControlImport::doRegisterCellValueBinding: invalid element!" ); + OSL_PRECOND( !_rBoundCellAddress.isEmpty(), + "OControlImport::doRegisterCellValueBinding: invalid address!" ); + + m_rContext.registerCellValueBinding( m_xElement, _rBoundCellAddress ); + } + + void OControlImport::doRegisterXFormsValueBinding( const OUString& _rBindingID ) + { + OSL_PRECOND( m_xElement.is(), "need element" ); + OSL_PRECOND( !_rBindingID.isEmpty(), "binding ID is not valid" ); + + m_rContext.registerXFormsValueBinding( m_xElement, _rBindingID ); + } + + void OControlImport::doRegisterXFormsListBinding( const OUString& _rBindingID ) + { + OSL_PRECOND( m_xElement.is(), "need element" ); + OSL_PRECOND( !_rBindingID.isEmpty(), "binding ID is not valid" ); + + m_rContext.registerXFormsListBinding( m_xElement, _rBindingID ); + } + + void OControlImport::doRegisterXFormsSubmission( const OUString& _rSubmissionID ) + { + OSL_PRECOND( m_xElement.is(), "need element" ); + OSL_PRECOND( !_rSubmissionID.isEmpty(), "binding ID is not valid" ); + + m_rContext.registerXFormsSubmission( m_xElement, _rSubmissionID ); + } + + Reference< XPropertySet > OControlImport::createElement() + { + const Reference<XPropertySet> xPropSet = OElementImport::createElement(); + if ( xPropSet.is() ) + { + m_xInfo = xPropSet->getPropertySetInfo(); + if ( m_xInfo.is() && m_xInfo->hasPropertyByName(PROPERTY_ALIGN) ) + { + Any aValue; + xPropSet->setPropertyValue(PROPERTY_ALIGN,aValue); + } + } + return xPropSet; + } + + //= OImagePositionImport + OImagePositionImport::OImagePositionImport( OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType ) + :OControlImport( _rImport, _rEventManager, _rxParentContainer, _eType ) + ,m_nImagePosition( -1 ) + ,m_nImageAlign( 0 ) + ,m_bHaveImagePosition( false ) + { + } + + bool OImagePositionImport::handleAttribute( sal_Int32 nElement, + const OUString& _rValue ) + { + static const sal_Int32 s_nImageDataAttributeName = OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::ImageData); + + if ( (nElement & TOKEN_MASK) == s_nImageDataAttributeName) + { + m_xGraphic = m_rContext.getGlobalContext().loadGraphicByURL(_rValue); + return true; + } + else if ( (nElement & TOKEN_MASK) == XML_IMAGE_POSITION ) + { + OSL_VERIFY( PropertyConversion::convertString( + cppu::UnoType<decltype(m_nImagePosition)>::get(), + _rValue, aImagePositionMap + ) >>= m_nImagePosition ); + m_bHaveImagePosition = true; + return true; + } + else if ( (nElement & TOKEN_MASK) == XML_IMAGE_ALIGN ) + { + OSL_VERIFY( PropertyConversion::convertString( + cppu::UnoType<decltype(m_nImageAlign)>::get(), + _rValue, aImageAlignMap + ) >>= m_nImageAlign ); + return true; + } + + return OControlImport::handleAttribute( nElement, _rValue ); + } + + void OImagePositionImport::startFastElement(sal_Int32 nElement, const Reference< XFastAttributeList >& _rxAttrList) + { + OControlImport::startFastElement( nElement, _rxAttrList ); + + if (m_xGraphic.is()) + { + PropertyValue aGraphicProperty; + aGraphicProperty.Name = PROPERTY_GRAPHIC; + aGraphicProperty.Value <<= m_xGraphic; + implPushBackPropertyValue(aGraphicProperty); + } + if ( !m_bHaveImagePosition ) + return; + + sal_Int16 nUnoImagePosition = ImagePosition::Centered; + if ( m_nImagePosition >= 0 ) + { + OSL_ENSURE( ( m_nImagePosition <= 3 ) && ( m_nImageAlign >= 0 ) && ( m_nImageAlign < 3 ), + "OImagePositionImport::StartElement: unknown image align and/or position!" ); + nUnoImagePosition = m_nImagePosition * 3 + m_nImageAlign; + } + + PropertyValue aImagePosition; + aImagePosition.Name = PROPERTY_IMAGE_POSITION; + aImagePosition.Value <<= nUnoImagePosition; + implPushBackPropertyValue( aImagePosition ); + } + + //= OReferredControlImport + OReferredControlImport::OReferredControlImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer ) + :OControlImport(_rImport, _rEventManager, _rxParentContainer) + { + } + + void OReferredControlImport::startFastElement(sal_Int32 nElement, const Reference< XFastAttributeList >& _rxAttrList) + { + OControlImport::startFastElement(nElement, _rxAttrList); + + // the base class should have created the control, so we can register it + if ( !m_sReferringControls.isEmpty() ) + m_rFormImport.registerControlReferences(m_xElement, m_sReferringControls); + } + + bool OReferredControlImport::handleAttribute(sal_Int32 nElement, + const OUString& _rValue) + { + static const sal_Int32 s_nReferenceAttributeName = OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::For); + if ((nElement & TOKEN_MASK) == s_nReferenceAttributeName) + { + m_sReferringControls = _rValue; + return true; + } + return OControlImport::handleAttribute(nElement, _rValue); + } + + //= OPasswordImport + OPasswordImport::OPasswordImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer, OControlElement::ElementType _eType) + :OControlImport(_rImport, _rEventManager, _rxParentContainer, _eType) + { + } + + bool OPasswordImport::handleAttribute(sal_Int32 nElement, const OUString& _rValue) + { + static const sal_Int32 s_nEchoCharAttributeName = OAttributeMetaData::getSpecialAttributeToken(SCAFlags::EchoChar); + if ((nElement & TOKEN_MASK) == s_nEchoCharAttributeName) + { + // need a special handling for the EchoChar property + PropertyValue aEchoChar; + aEchoChar.Name = PROPERTY_ECHOCHAR; + OSL_ENSURE(_rValue.getLength() == 1, "OPasswordImport::handleAttribute: invalid echo char attribute!"); + // we ourself should not have written values other than of length 1 + if (_rValue.getLength() >= 1) + aEchoChar.Value <<= static_cast<sal_Int16>(_rValue[0]); + else + aEchoChar.Value <<= sal_Int16(0); + implPushBackPropertyValue(aEchoChar); + return true; + } + return OControlImport::handleAttribute(nElement, _rValue); + } + + //= ORadioImport + ORadioImport::ORadioImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer, OControlElement::ElementType _eType) + :OImagePositionImport( _rImport, _rEventManager, _rxParentContainer, _eType ) + { + } + + bool ORadioImport::handleAttribute(sal_Int32 nElement, const OUString& _rValue) + { + // need special handling for the State & CurrentState properties: + // they're stored as booleans, but expected to be int16 properties + static const sal_Int32 nCurrentSelectedAttributeName = OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::CurrentSelected); + static const sal_Int32 nSelectedAttributeName = OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Selected); + if ( (nElement & TOKEN_MASK) == nCurrentSelectedAttributeName + || (nElement & TOKEN_MASK) == nSelectedAttributeName + ) + { + const OAttribute2Property::AttributeAssignment* pProperty = m_rContext.getAttributeMap().getAttributeTranslation(nElement & TOKEN_MASK); + assert(pProperty && "ORadioImport::handleAttribute: invalid property map!"); + if (pProperty) + { + const Any aBooleanValue( PropertyConversion::convertString(pProperty->aPropertyType, _rValue, pProperty->pEnumMap) ); + + // create and store a new PropertyValue + PropertyValue aNewValue; + aNewValue.Name = pProperty->sPropertyName; + aNewValue.Value <<= static_cast<sal_Int16>(::cppu::any2bool(aBooleanValue)); + + implPushBackPropertyValue(aNewValue); + } + return true; + } + return OImagePositionImport::handleAttribute( nElement, _rValue ); + } + + //= OURLReferenceImport + OURLReferenceImport::OURLReferenceImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType) + :OImagePositionImport(_rImport, _rEventManager, _rxParentContainer, _eType) + { + } + + bool OURLReferenceImport::handleAttribute(sal_Int32 nElement, const OUString& _rValue) + { + static const sal_Int32 s_nTargetLocationAttributeName = OAttributeMetaData::getCommonControlAttributeToken( CCAFlags::TargetLocation ); + static const sal_Int32 s_nImageDataAttributeName = OAttributeMetaData::getCommonControlAttributeToken( CCAFlags::ImageData ); + + // need to make the URL absolute if + // * it's the image-data attribute + // * it's the target-location attribute, and we're dealing with an object which has the respective property + bool bMakeAbsolute = + (nElement & TOKEN_MASK) == s_nImageDataAttributeName + || ( (nElement & TOKEN_MASK) == s_nTargetLocationAttributeName + && ( ( OControlElement::BUTTON == m_eElementType ) + || ( OControlElement::IMAGE == m_eElementType ) + ) + ); + + if (bMakeAbsolute && !_rValue.isEmpty()) + { + OUString sAdjustedValue = _rValue; + if ((nElement & TOKEN_MASK) != s_nImageDataAttributeName) + sAdjustedValue = m_rContext.getGlobalContext().GetAbsoluteReference( _rValue ); + return OImagePositionImport::handleAttribute( nElement, sAdjustedValue ); + } + + return OImagePositionImport::handleAttribute( nElement, _rValue ); + } + + //= OButtonImport + OButtonImport::OButtonImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType) + :OURLReferenceImport(_rImport, _rEventManager, _rxParentContainer, _eType) + { + enableTrackAttributes(); + } + + void OButtonImport::startFastElement(sal_Int32 nElement, const Reference< XFastAttributeList >& _rxAttrList) + { + OURLReferenceImport::startFastElement(nElement, _rxAttrList); + + // handle the target-frame attribute + simulateDefaultedAttribute(OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::TargetFrame), PROPERTY_TARGETFRAME, "_blank"); + } + + //= OValueRangeImport + OValueRangeImport::OValueRangeImport( OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer, OControlElement::ElementType _eType ) + :OControlImport( _rImport, _rEventManager, _rxParentContainer, _eType ) + ,m_nStepSizeValue( 1 ) + { + + } + + bool OValueRangeImport::handleAttribute( sal_Int32 nElement, const OUString& _rValue ) + { + if ( (nElement & TOKEN_MASK) == OAttributeMetaData::getSpecialAttributeToken( SCAFlags::StepSize ) ) + { + ::sax::Converter::convertNumber( m_nStepSizeValue, _rValue ); + return true; + } + return OControlImport::handleAttribute( nElement, _rValue ); + } + + void OValueRangeImport::startFastElement( sal_Int32 nElement, const Reference< XFastAttributeList >& _rxAttrList ) + { + OControlImport::startFastElement( nElement, _rxAttrList ); + + if ( m_xInfo.is() ) + { + if ( m_xInfo->hasPropertyByName( PROPERTY_SPIN_INCREMENT ) ) + m_xElement->setPropertyValue( PROPERTY_SPIN_INCREMENT, Any( m_nStepSizeValue ) ); + else if ( m_xInfo->hasPropertyByName( PROPERTY_LINE_INCREMENT ) ) + m_xElement->setPropertyValue( PROPERTY_LINE_INCREMENT, Any( m_nStepSizeValue ) ); + } + } + + //= OTextLikeImport + OTextLikeImport::OTextLikeImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType) + :OControlImport(_rImport, _rEventManager, _rxParentContainer, _eType) + ,m_bEncounteredTextPara( false ) + { + enableTrackAttributes(); + } + + css::uno::Reference< css::xml::sax::XFastContextHandler > OTextLikeImport::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) + { + if ( nElement == XML_ELEMENT(TEXT, XML_P) ) + { + OSL_ENSURE( m_eElementType == OControlElement::TEXT_AREA, + "OTextLikeImport::CreateChildContext: text paragraphs in a non-text-area?" ); + + if ( m_eElementType == OControlElement::TEXT_AREA ) + { + Reference< XText > xTextElement( m_xElement, UNO_QUERY ); + if ( xTextElement.is() ) + { + rtl::Reference < XMLTextImportHelper > xTextImportHelper( m_rContext.getGlobalContext().GetTextImport() ); + + if ( !m_xCursor.is() ) + { + m_xOldCursor = xTextImportHelper->GetCursor(); + m_xCursor = xTextElement->createTextCursor(); + + if ( m_xCursor.is() ) + xTextImportHelper->SetCursor( m_xCursor ); + } + if ( m_xCursor.is() ) + { + m_bEncounteredTextPara = true; + return xTextImportHelper->CreateTextChildContext( m_rContext.getGlobalContext(), nElement, xAttrList ); + } + } + else + { + // in theory, we could accumulate all the text portions (without formatting), + // and set it as Text property at the model ... + } + } + } + + return OControlImport::createFastChildContext( nElement, xAttrList ); + } + + void OTextLikeImport::startFastElement(sal_Int32 nElement, const Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) + { + OControlImport::startFastElement(nElement, _rxAttrList); + + // handle the convert-empty-to-null attribute, whose default is different from the property default + // unfortunately, different classes are imported by this class ('cause they're represented by the + // same XML element), though not all of them know this property. + // So we have to do a check ... + if (m_xElement.is() && m_xInfo.is() && m_xInfo->hasPropertyByName(PROPERTY_EMPTY_IS_NULL) ) + simulateDefaultedAttribute(OAttributeMetaData::getDatabaseAttributeToken(DAFlags::ConvertEmpty), PROPERTY_EMPTY_IS_NULL, "false"); + } + + namespace { + + struct EqualHandle + { + const sal_Int32 m_nHandle; + explicit EqualHandle( sal_Int32 _nHandle ) : m_nHandle( _nHandle ) { } + + bool operator()( const PropertyValue& _rProp ) + { + return _rProp.Handle == m_nHandle; + } + }; + + } + + void OTextLikeImport::removeRedundantCurrentValue() + { + if ( !m_bEncounteredTextPara ) + return; + + // In case the text is written in the text:p elements, we need to ignore what we read as + // current-value attribute, since it's redundant. + // fortunately, OElementImport tagged the value property with the PROPID_CURRENT_VALUE + // handle, so we do not need to determine the name of our value property here + // (normally, it should be "Text", since no other controls than the edit field should + // have the text:p elements) + PropertyValueArray::iterator aValuePropertyPos = ::std::find_if( + m_aValues.begin(), + m_aValues.end(), + EqualHandle( PROPID_CURRENT_VALUE ) + ); + if ( aValuePropertyPos != m_aValues.end() ) + { + OSL_ENSURE( aValuePropertyPos->Name == PROPERTY_TEXT, "OTextLikeImport::EndElement: text:p was present, but our value property is *not* 'Text'!" ); + if ( aValuePropertyPos->Name == PROPERTY_TEXT ) + { + m_aValues.erase(aValuePropertyPos); + } + } + + // additionally, we need to set the "RichText" property of our element to sal_True + // (the presence of the text:p is used as indicator for the value of the RichText property) + bool bHasRichTextProperty = false; + if ( m_xInfo.is() ) + bHasRichTextProperty = m_xInfo->hasPropertyByName( PROPERTY_RICH_TEXT ); + OSL_ENSURE( bHasRichTextProperty, "OTextLikeImport::EndElement: text:p, but no rich text control?" ); + if ( bHasRichTextProperty ) + m_xElement->setPropertyValue( PROPERTY_RICH_TEXT, Any( true ) ); + // Note that we do *not* set the RichText property (in case our element has one) to sal_False here + // since this is the default of this property, anyway. + } + + namespace { + + struct EqualName + { + const OUString & m_sName; + explicit EqualName( const OUString& _rName ) : m_sName( _rName ) { } + + bool operator()( const PropertyValue& _rProp ) + { + return _rProp.Name == m_sName; + } + }; + + } + + void OTextLikeImport::adjustDefaultControlProperty() + { + // In OpenOffice.org 2.0, we changed the implementation of the css.form.component.TextField (the model of a text field control), + // so that it now uses another default control. So if we encounter a text field where the *old* default + // control property is writing, we are not allowed to use it + PropertyValueArray::iterator aDefaultControlPropertyPos = ::std::find_if( + m_aValues.begin(), + m_aValues.end(), + EqualName( "DefaultControl" ) + ); + if ( aDefaultControlPropertyPos != m_aValues.end() ) + { + OUString sDefaultControl; + OSL_VERIFY( aDefaultControlPropertyPos->Value >>= sDefaultControl ); + if ( sDefaultControl == "stardiv.one.form.control.Edit" ) + { + // complete remove this property value from the array. Today's "default value" of the "DefaultControl" + // property is sufficient + m_aValues.erase(aDefaultControlPropertyPos); + } + } + } + + void OTextLikeImport::endFastElement(sal_Int32 nElement) + { + removeRedundantCurrentValue(); + adjustDefaultControlProperty(); + + // let the base class do the stuff + OControlImport::endFastElement(nElement); + + // some cleanups + rtl::Reference < XMLTextImportHelper > xTextImportHelper( m_rContext.getGlobalContext().GetTextImport() ); + if ( m_xCursor.is() ) + { + // delete the newline which has been imported erroneously + // TODO (fs): stole this code somewhere - why don't we fix the text import?? + m_xCursor->gotoEnd( false ); + m_xCursor->goLeft( 1, true ); + m_xCursor->setString( OUString() ); + + // reset cursor + xTextImportHelper->ResetCursor(); + } + + if ( m_xOldCursor.is() ) + xTextImportHelper->SetCursor( m_xOldCursor ); + + } + + //= OListAndComboImport + OListAndComboImport::OListAndComboImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType) + :OControlImport(_rImport, _rEventManager, _rxParentContainer, _eType) + ,m_nEmptyListItems( 0 ) + ,m_nEmptyValueItems( 0 ) + ,m_bEncounteredLSAttrib( false ) + ,m_bLinkWithIndexes( false ) + { + if (OControlElement::COMBOBOX == m_eElementType) + enableTrackAttributes(); + } + + css::uno::Reference< css::xml::sax::XFastContextHandler > OListAndComboImport::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList ) + { + // is it the "option" sub tag of a listbox ? + if ((nElement & TOKEN_MASK) == XML_OPTION) + return new OListOptionImport(GetImport(), this); + + // is it the "item" sub tag of a combobox ? + if ((nElement & TOKEN_MASK) == XML_ITEM) + return new OComboItemImport(GetImport(), this); + + // everything else + return OControlImport::createFastChildContext(nElement, _rxAttrList); + } + + void OListAndComboImport::startFastElement(sal_Int32 nElement, const Reference< XFastAttributeList >& _rxAttrList) + { + m_bLinkWithIndexes = false; + + OControlImport::startFastElement(nElement, _rxAttrList); + + if (OControlElement::COMBOBOX == m_eElementType) + { + // for the auto-completion + // the attribute default does not equal the property default, so in case we did not read this attribute, + // we have to simulate it + simulateDefaultedAttribute( OAttributeMetaData::getSpecialAttributeToken( SCAFlags::AutoCompletion ), PROPERTY_AUTOCOMPLETE, "false"); + + // same for the convert-empty-to-null attribute, which's default is different from the property default + simulateDefaultedAttribute( OAttributeMetaData::getDatabaseAttributeToken( DAFlags::ConvertEmpty ), PROPERTY_EMPTY_IS_NULL, "false"); + } + } + + void OListAndComboImport::endFastElement(sal_Int32 nElement) + { + // append the list source property the properties sequence of our importer + // the string item list + PropertyValue aItemList; + aItemList.Name = PROPERTY_STRING_ITEM_LIST; + aItemList.Value <<= comphelper::containerToSequence(m_aListSource); + implPushBackPropertyValue(aItemList); + + if (OControlElement::LISTBOX == m_eElementType) + { + OSL_ENSURE((m_aListSource.size() + m_nEmptyListItems) == (m_aValueList.size() + m_nEmptyValueItems), + "OListAndComboImport::EndElement: inconsistence between labels and values!"); + + if ( !m_bEncounteredLSAttrib ) + { + // the value sequence + PropertyValue aValueList; + aValueList.Name = PROPERTY_LISTSOURCE; + aValueList.Value <<= comphelper::containerToSequence(m_aValueList); + implPushBackPropertyValue(aValueList); + } + + // the select sequence + PropertyValue aSelected; + aSelected.Name = PROPERTY_SELECT_SEQ; + aSelected.Value <<= comphelper::containerToSequence(m_aSelectedSeq); + implPushBackPropertyValue(aSelected); + + // the default select sequence + PropertyValue aDefaultSelected; + aDefaultSelected.Name = PROPERTY_DEFAULT_SELECT_SEQ; + aDefaultSelected.Value <<= comphelper::containerToSequence(m_aDefaultSelectedSeq); + implPushBackPropertyValue(aDefaultSelected); + } + + OControlImport::endFastElement(nElement); + + // the external list source, if applicable + if ( m_xElement.is() && !m_sCellListSource.isEmpty() ) + m_rContext.registerCellRangeListSource( m_xElement, m_sCellListSource ); + } + + void OListAndComboImport::doRegisterCellValueBinding( const OUString& _rBoundCellAddress ) + { + OUString sBoundCellAddress( _rBoundCellAddress ); + if ( m_bLinkWithIndexes ) + { + // This is a HACK. We register a string which is no valid address, but allows + // (somewhere else) to determine that a non-standard binding should be created. + // This hack is acceptable for OOo 1.1.1, since the file format for value + // bindings of form controls is to be changed afterwards, anyway. + sBoundCellAddress += ":index"; + } + + OControlImport::doRegisterCellValueBinding( sBoundCellAddress ); + } + + bool OListAndComboImport::handleAttribute(sal_Int32 nElement, const OUString& _rValue) + { + static const sal_Int32 nListSourceAttributeName = OAttributeMetaData::getDatabaseAttributeToken(DAFlags::ListSource); + if ( (nElement & TOKEN_MASK) == nListSourceAttributeName ) + { + PropertyValue aListSource; + aListSource.Name = PROPERTY_LISTSOURCE; + + // it's the ListSource attribute + m_bEncounteredLSAttrib = true; + if ( OControlElement::COMBOBOX == m_eElementType ) + { + aListSource.Value <<= _rValue; + } + else + { + // a listbox which has a list-source attribute must have a list-source-type of something + // not equal to ValueList. + // In this case, the list-source value is simply the one and only element of the ListSource property. + Sequence<OUString> aListSourcePropValue { _rValue }; + aListSource.Value <<= aListSourcePropValue; + } + + implPushBackPropertyValue( aListSource ); + return true; + } + + if ( (nElement & TOKEN_MASK) == OAttributeMetaData::getBindingAttributeToken( BAFlags::ListCellRange ) ) + { + m_sCellListSource = _rValue; + return true; + } + + if ( (nElement & TOKEN_MASK) == OAttributeMetaData::getBindingAttributeToken( BAFlags::ListLinkingType ) ) + { + sal_Int16 nLinkageType = 0; + PropertyConversion::convertString( + ::cppu::UnoType<sal_Int16>::get(), + _rValue, + aListLinkageMap + ) >>= nLinkageType; + + m_bLinkWithIndexes = ( nLinkageType != 0 ); + return true; + } + + return OControlImport::handleAttribute(nElement, _rValue); + } + + void OListAndComboImport::implPushBackLabel(const OUString& _rLabel) + { + OSL_ENSURE(!m_nEmptyListItems, "OListAndComboImport::implPushBackValue: label list is already done!"); + if (!m_nEmptyListItems) + m_aListSource.push_back(_rLabel); + } + + void OListAndComboImport::implPushBackValue(const OUString& _rValue) + { + OSL_ENSURE(!m_nEmptyValueItems, "OListAndComboImport::implPushBackValue: value list is already done!"); + if (!m_nEmptyValueItems) + { + OSL_ENSURE( !m_bEncounteredLSAttrib, "OListAndComboImport::implPushBackValue: invalid structure! Did you save this document with a version prior SRC641 m?" ); + // We already had the list-source attribute, which means that the ListSourceType is + // not ValueList, which means that the ListSource should contain only one string in + // the first element of the sequence + // All other values in the file are invalid + + m_aValueList.push_back( _rValue ); + } + } + + void OListAndComboImport::implEmptyLabelFound() + { + ++m_nEmptyListItems; + } + + void OListAndComboImport::implEmptyValueFound() + { + ++m_nEmptyValueItems; + } + + void OListAndComboImport::implSelectCurrentItem() + { + OSL_ENSURE((m_aListSource.size() + m_nEmptyListItems) == (m_aValueList.size() + m_nEmptyValueItems), + "OListAndComboImport::implSelectCurrentItem: inconsistence between labels and values!"); + + sal_Int16 nItemNumber = static_cast<sal_Int16>(m_aListSource.size() - 1 + m_nEmptyListItems); + m_aSelectedSeq.push_back(nItemNumber); + } + + void OListAndComboImport::implDefaultSelectCurrentItem() + { + OSL_ENSURE((m_aListSource.size() + m_nEmptyListItems) == (m_aValueList.size() + m_nEmptyValueItems), + "OListAndComboImport::implDefaultSelectCurrentItem: inconsistence between labels and values!"); + + sal_Int16 nItemNumber = static_cast<sal_Int16>(m_aListSource.size() - 1 + m_nEmptyListItems); + m_aDefaultSelectedSeq.push_back(nItemNumber); + } + + //= OListOptionImport + OListOptionImport::OListOptionImport(SvXMLImport& _rImport, + const OListAndComboImportRef& _rListBox) + :SvXMLImportContext(_rImport) + ,m_xListBoxImport(_rListBox) + { + } + + void OListOptionImport::startFastElement(sal_Int32 nElement, const Reference< XFastAttributeList >& _rxAttrList) + { + // the label and the value + const sal_Int32 nLabelAttribute = (nElement & ~TOKEN_MASK) | XML_LABEL; + const sal_Int32 nValueAttribute = (nElement & ~TOKEN_MASK) | XML_VALUE; + + // the label attribute + OUString sValue = _rxAttrList->getOptionalValue(nLabelAttribute); + bool bNonexistentAttribute = !_rxAttrList->hasAttribute(nLabelAttribute); + + if (bNonexistentAttribute) + m_xListBoxImport->implEmptyLabelFound(); + else + m_xListBoxImport->implPushBackLabel( sValue ); + + // the value attribute + sValue = _rxAttrList->getOptionalValue(nValueAttribute); + bNonexistentAttribute = !_rxAttrList->hasAttribute(nValueAttribute); + + if (bNonexistentAttribute) + m_xListBoxImport->implEmptyValueFound(); + else + m_xListBoxImport->implPushBackValue( sValue ); + + // the current-selected and selected + const sal_Int32 nSelectedAttribute = (nElement & ~TOKEN_MASK) | OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::CurrentSelected); + const sal_Int32 nDefaultSelectedAttribute = (nElement & ~TOKEN_MASK) | OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Selected); + + // propagate the selected flag + bool bSelected(false); + (void)::sax::Converter::convertBool(bSelected, + _rxAttrList->getOptionalValue(nSelectedAttribute)); + if (bSelected) + m_xListBoxImport->implSelectCurrentItem(); + + // same for the default selected + bool bDefaultSelected(false); + (void)::sax::Converter::convertBool(bDefaultSelected, + _rxAttrList->getOptionalValue(nDefaultSelectedAttribute)); + if (bDefaultSelected) + m_xListBoxImport->implDefaultSelectCurrentItem(); + } + + //= OComboItemImport + OComboItemImport::OComboItemImport(SvXMLImport& _rImport, + const OListAndComboImportRef& _rListBox) + :SvXMLImportContext(_rImport) + ,m_xListBoxImport(_rListBox) + { + } + + void OComboItemImport::startFastElement(sal_Int32 nElement, const Reference< XFastAttributeList >& _rxAttrList) + { + const sal_Int32 nLabelAttributeName = (nElement & ~TOKEN_MASK) | + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Label); + m_xListBoxImport->implPushBackLabel(_rxAttrList->getOptionalValue(nLabelAttributeName)); + } + + //= OColumnWrapperImport + OColumnWrapperImport::OColumnWrapperImport(OFormLayerXMLImport_Impl& _rImport, + IEventAttacherManager& _rEventManager, sal_Int32 /*nElement*/, + const Reference< XNameContainer >& _rxParentContainer) + :SvXMLImportContext(_rImport.getGlobalContext()) + ,m_xParentContainer(_rxParentContainer) + ,m_rFormImport(_rImport) + ,m_rEventManager(_rEventManager) + { + } + css::uno::Reference< css::xml::sax::XFastContextHandler > OColumnWrapperImport::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) + { + OControlImport* pReturn = implCreateChildContext(nElement, OElementNameMap::getElementType(nElement & TOKEN_MASK)); + if (pReturn) + { + OSL_ENSURE(m_xOwnAttributes.is(), "OColumnWrapperImport::CreateChildContext: had no form:column element!"); + pReturn->addOuterAttributes(m_xOwnAttributes); + } + return pReturn; + } + void OColumnWrapperImport::startFastElement(sal_Int32 /*nElement*/, const Reference< XFastAttributeList >& _rxAttrList) + { + OSL_ENSURE(!m_xOwnAttributes.is(), "OColumnWrapperImport::StartElement: already have the cloned list!"); + + // clone the attributes + Reference< XCloneable > xCloneList(_rxAttrList, UNO_QUERY_THROW); + m_xOwnAttributes.set(xCloneList->createClone(), UNO_QUERY_THROW); + } + + OControlImport* OColumnWrapperImport::implCreateChildContext( + sal_Int32 /*nElement*/, + OControlElement::ElementType _eType) + { + OSL_ENSURE( (OControlElement::TEXT == _eType) + || (OControlElement::TEXT_AREA == _eType) + || (OControlElement::FORMATTED_TEXT == _eType) + || (OControlElement::CHECKBOX == _eType) + || (OControlElement::LISTBOX == _eType) + || (OControlElement::COMBOBOX == _eType) + || (OControlElement::TIME == _eType) + || (OControlElement::DATE == _eType), + "OColumnWrapperImport::implCreateChildContext: invalid or unrecognized sub element!"); + + switch (_eType) + { + case OControlElement::COMBOBOX: + case OControlElement::LISTBOX: + return new OColumnImport<OListAndComboImport>(m_rFormImport, m_rEventManager, m_xParentContainer, _eType ); + + case OControlElement::PASSWORD: + return new OColumnImport<OPasswordImport>(m_rFormImport, m_rEventManager, m_xParentContainer, _eType ); + + case OControlElement::TEXT: + case OControlElement::TEXT_AREA: + case OControlElement::FORMATTED_TEXT: + return new OColumnImport< OTextLikeImport >( m_rFormImport, m_rEventManager, m_xParentContainer, _eType ); + + default: + return new OColumnImport<OControlImport>(m_rFormImport, m_rEventManager, m_xParentContainer, _eType ); + } + } + + //= OGridImport + OGridImport::OGridImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType) + :OControlImport(_rImport, _rEventManager, _rxParentContainer) + { + setElementType(_eType); + } + + css::uno::Reference< css::xml::sax::XFastContextHandler > OGridImport::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) + { + // maybe it's a sub control + if ((nElement & TOKEN_MASK) == XML_COLUMN) + { + if (m_xMeAsContainer.is()) + return new OColumnWrapperImport(m_rFormImport, *this, nElement, m_xMeAsContainer); + else + { + OSL_FAIL("OGridImport::CreateChildContext: don't have an element!"); + return nullptr; + } + } + + return OControlImport::createFastChildContext(nElement, xAttrList); + } + + void OGridImport::endFastElement(sal_Int32 nElement) + { + OControlImport::endFastElement(nElement); + + // now that we have all children, attach the events + css::uno::Reference< css::container::XIndexAccess > xIndexContainer(m_xMeAsContainer, css::uno::UNO_QUERY); + if (xIndexContainer.is()) + ODefaultEventAttacherManager::setEvents(xIndexContainer); + } + + css::uno::Reference< css::beans::XPropertySet > OGridImport::createElement() + { + // let the base class create the object + css::uno::Reference< css::beans::XPropertySet > xReturn = OControlImport::createElement(); + if (!xReturn.is()) + return xReturn; + + // ensure that the object is a XNameContainer (we strongly need this for inserting child elements) + m_xMeAsContainer.set(xReturn, css::uno::UNO_QUERY); + if (!m_xMeAsContainer.is()) + { + OSL_FAIL("OContainerImport::createElement: invalid element (no XNameContainer) created!"); + xReturn.clear(); + } + + return xReturn; + } + + //= OFormImport + OFormImport::OFormImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const Reference< XNameContainer >& _rxParentContainer) + :OElementImport(_rImport, _rEventManager, _rxParentContainer) + { + enableTrackAttributes(); + } + + css::uno::Reference< css::xml::sax::XFastContextHandler > OFormImport::createFastChildContext( + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList>& _rxAttrList ) + { + auto nToken = (nElement & TOKEN_MASK); + if( nToken == XML_FORM ) + return new OFormImport( m_rFormImport, *this, m_xMeAsContainer); + else if ( nToken == XML_CONNECTION_RESOURCE ) + return new OXMLDataSourceImport(GetImport(), _rxAttrList, m_xElement); + else if( nElement == XML_ELEMENT(OFFICE, XML_EVENT_LISTENERS) || + nToken == XML_PROPERTIES ) + return OElementImport::createFastChildContext( nElement, _rxAttrList ); + else + { + OControlElement::ElementType eType = OElementNameMap::getElementType(nToken); + switch (eType) + { + case OControlElement::TEXT: + case OControlElement::TEXT_AREA: + case OControlElement::FORMATTED_TEXT: + return new OTextLikeImport(m_rFormImport, *this, m_xMeAsContainer, eType); + case OControlElement::GRID: + return new OGridImport(m_rFormImport, *this, m_xMeAsContainer, eType); + case OControlElement::COMBOBOX: + case OControlElement::LISTBOX: + return new OListAndComboImport(m_rFormImport, *this, m_xMeAsContainer, eType); + case OControlElement::PASSWORD: + return new OPasswordImport(m_rFormImport, *this, m_xMeAsContainer, eType); + case OControlElement::BUTTON: + case OControlElement::IMAGE: + case OControlElement::IMAGE_FRAME: + return new OButtonImport( m_rFormImport, *this, m_xMeAsContainer, eType ); + case OControlElement::RADIO: + return new ORadioImport(m_rFormImport, *this, m_xMeAsContainer, eType); + case OControlElement::CHECKBOX: + return new OImagePositionImport(m_rFormImport, *this, m_xMeAsContainer, eType); + case OControlElement::FRAME: + case OControlElement::FIXED_TEXT: + return new OReferredControlImport(m_rFormImport, *this, m_xMeAsContainer); + case OControlElement::VALUERANGE: + return new OValueRangeImport( m_rFormImport, *this, m_xMeAsContainer, eType ); + default: + return new OControlImport(m_rFormImport, *this, m_xMeAsContainer, eType); + } + } + } + + void OFormImport::startFastElement(sal_Int32 nElement, const Reference< XFastAttributeList >& _rxAttrList) + { + m_rFormImport.enterEventContext(); + OElementImport::startFastElement(nElement, _rxAttrList); + + // handle the target-frame attribute + simulateDefaultedAttribute(OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::TargetFrame), PROPERTY_TARGETFRAME, "_blank"); + } + + void OFormImport::endFastElement(sal_Int32 nElement) + { + OElementImport::endFastElement(nElement); + + // now that we have all children, attach the events + css::uno::Reference< css::container::XIndexAccess > xIndexContainer(m_xMeAsContainer, css::uno::UNO_QUERY); + if (xIndexContainer.is()) + ODefaultEventAttacherManager::setEvents(xIndexContainer); + + m_rFormImport.leaveEventContext(); + } + + css::uno::Reference< css::beans::XPropertySet > OFormImport::createElement() + { + // let the base class create the object + css::uno::Reference< css::beans::XPropertySet > xReturn = OElementImport::createElement(); + if (!xReturn.is()) + return xReturn; + + // ensure that the object is a XNameContainer (we strongly need this for inserting child elements) + m_xMeAsContainer.set(xReturn, css::uno::UNO_QUERY); + if (!m_xMeAsContainer.is()) + { + OSL_FAIL("OContainerImport::createElement: invalid element (no XNameContainer) created!"); + xReturn.clear(); + } + + return xReturn; + } + + bool OFormImport::handleAttribute(sal_Int32 nElement, const OUString& _rValue) + { + // handle the master/details field attributes (they're way too special to let the OPropertyImport handle them) + static const sal_Int32 s_nMasterFieldsAttributeName = OAttributeMetaData::getFormAttributeToken(faMasterFields); + static const sal_Int32 s_nDetailFieldsAttributeName = OAttributeMetaData::getFormAttributeToken(faDetailFields); + + if ( (nElement & TOKEN_MASK) == s_nMasterFieldsAttributeName) + { + implTranslateStringListProperty(PROPERTY_MASTERFIELDS, _rValue); + return true; + } + + if ( (nElement & TOKEN_MASK) == s_nDetailFieldsAttributeName) + { + implTranslateStringListProperty(PROPERTY_DETAILFIELDS, _rValue); + return true; + } + + return OElementImport::handleAttribute(nElement, _rValue); + } + + void OFormImport::implTranslateStringListProperty(const OUString& _rPropertyName, const OUString& _rValue) + { + PropertyValue aProp; + aProp.Name = _rPropertyName; + + Sequence< OUString > aList; + + // split up the value string + if (!_rValue.isEmpty()) + { + // For the moment, we build a vector instead of a Sequence. It's easier to handle because of its + // push_back method + ::std::vector< OUString > aElements; + // estimate the number of tokens + sal_Int32 nEstimate = 0, nLength = _rValue.getLength(); + const sal_Unicode* pChars = _rValue.getStr(); + for (sal_Int32 i=0; i<nLength; ++i, ++pChars) + if (*pChars == ',') + ++nEstimate; + aElements.reserve(nEstimate + 1); + // that's the worst case. If the string contains the separator character _quoted_, we reserved too much... + + sal_Int32 nElementStart = 0; + sal_Int32 nNextSep = 0; + do + { + // extract the current element + nNextSep = ::sax::Converter::indexOfComma( + _rValue, nElementStart); + if (-1 == nNextSep) + nNextSep = nLength; + std::u16string_view sElement = _rValue.subView(nElementStart, nNextSep - nElementStart); + + size_t nElementLength = sElement.size(); + // when writing the sequence, we quoted the single elements with " characters + OSL_ENSURE( o3tl::starts_with(sElement, u"\"") && o3tl::ends_with(sElement, u"\""), + "OFormImport::implTranslateStringListProperty: invalid quoted element name."); + sElement = sElement.substr(1, nElementLength - 2); + + aElements.push_back(OUString(sElement)); + + // switch to the next element + nElementStart = 1 + nNextSep; + } + while (nElementStart < nLength); + + aList = Sequence< OUString >(aElements.data(), aElements.size()); + } + else + { + OSL_FAIL("OFormImport::implTranslateStringListProperty: invalid value (empty)!"); + } + + aProp.Value <<= aList; + + // add the property to the base class' array + implPushBackPropertyValue(aProp); + } + //= OXMLDataSourceImport + OXMLDataSourceImport::OXMLDataSourceImport( + SvXMLImport& _rImport + ,const Reference< css::xml::sax::XFastAttributeList > & _xAttrList + ,const css::uno::Reference< css::beans::XPropertySet >& _xElement) : + SvXMLImportContext( _rImport) + { + for( auto& aIter : sax_fastparser::castToFastAttributeList(_xAttrList) ) + { + if ( aIter.getToken() == + XML_ELEMENT(XLINK, OAttributeMetaData::getCommonControlAttributeToken( CCAFlags::TargetLocation ) ) ) + { + OUString sValue = aIter.toString(); + sValue = _rImport.GetAbsoluteReference(sValue); + INetURLObject aURL(sValue); + if ( aURL.GetProtocol() == INetProtocol::File ) + _xElement->setPropertyValue(PROPERTY_DATASOURCENAME,Any(sValue)); + else + _xElement->setPropertyValue(PROPERTY_URL,Any(sValue)); // the url is the "sdbc:" string + break; + } + else + SAL_WARN("xmloff", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString()); + } + } + + OUString OFormImport::determineDefaultServiceName() const + { + return "com.sun.star.form.component.Form"; + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/elementimport.hxx b/xmloff/source/forms/elementimport.hxx new file mode 100644 index 000000000..dcf4a6043 --- /dev/null +++ b/xmloff/source/forms/elementimport.hxx @@ -0,0 +1,674 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include "propertyimport.hxx" +#include "controlelement.hxx" +#include "valueproperties.hxx" +#include "eventimport.hxx" +#include "logging.hxx" +#include "property_description.hxx" + +#include <com/sun/star/text/XTextCursor.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/form/XGridColumnFactory.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <osl/diagnose.h> + +#include <map> +#include <vector> + +class XMLTextStyleContext; +namespace xmloff +{ + + class OFormLayerXMLImport_Impl; + + //= OElementNameMap + const OControlElement::ElementType& operator ++(OControlElement::ElementType& _e); + + /** helper class which allows fast translation of xml tag names into element types. + */ + class OElementNameMap : public OControlElement + { + typedef std::map<OUString, ElementType> MapString2Element; + static std::map<sal_Int32, ElementType> s_sElementTranslations2; + + OElementNameMap() = delete; + + public: + static ElementType getElementType(sal_Int32 nToken); + }; + + //= OElementImport + /** implements common behaviour for importing forms, controls and columns + */ + class OElementImport + :public OPropertyImport + ,public IEventAttacher + ,public OStackedLogging + { + protected: + OUString m_sServiceName; // the service name as extracted from the service-name attribute + OUString m_sName; // the name of the object (redundant, already contained in the base class' array) + OFormLayerXMLImport_Impl& m_rFormImport; // the form import context + IEventAttacherManager& m_rEventManager; // the event attacher manager + + const XMLTextStyleContext* m_pStyleElement; // the XML element which describes the style we encountered + // while reading our element + + /// the parent container to insert the new element into + css::uno::Reference< css::container::XNameContainer > + m_xParentContainer; + + /// the element we're creating. Valid after StartElement + css::uno::Reference< css::beans::XPropertySet > + m_xElement; + css::uno::Reference< css::beans::XPropertySetInfo > + m_xInfo; + + bool m_bImplicitGenericAttributeHandling; + + public: + /** ctor + @param _rImport + the importer + @param _rEventManager + the event attacher manager for the control being imported + @param _rAttributeMap + the attribute map to be used for translating attributes into properties + @param _rxParentContainer + the container in which the new element should be inserted + */ + OElementImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer + ); + virtual ~OElementImport() override; + + protected: + // SvXMLImportContext overridables + virtual void SAL_CALL startFastElement( sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + // OPropertyImport overridables + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override; + + // IEventAttacher + virtual void registerEvents( + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rEvents + ) override; + + /** create the (uninitialized) element which is to represent the read data + + <p>The default implementation uses <member>m_xORB</member> to create an object with <member>m_sServiceName</member>. + */ + virtual css::uno::Reference< css::beans::XPropertySet > + createElement(); + + protected: + /** can be used to handle properties where the attribute default and the property default differ. + <p>In such case, if the property had the attribute default upon writing, nothing is read, so upon reading, + the property is still at its own default (which is not the attribute default).<p/> + <p>This method, if told the attribute and the property, and the (implied) attribute default, sets the + property value as if the attribute was encountered.</p> + @see encounteredAttribute + */ + void simulateDefaultedAttribute(sal_Int32 nElement, const OUString& _rPropertyName, const char* _pAttributeDefault); + + /** to be called from within handleAttribute, checks whether the given attribute is covered by our generic + attribute handler mechanisms + */ + bool tryGenericAttribute( sal_Int32 nElement, const OUString& _rValue ); + + /** controls whether |handleAttribute| implicitly calls |tryGenericAttribute|, or whether the derived class + must do this explicitly at a suitable place in its own |handleAttribute| + */ + void disableImplicitGenericAttributeHandling() { m_bImplicitGenericAttributeHandling = false; } + + private: + OUString implGetDefaultName() const; + void implApplyGenericProperties(); + void implApplySpecificProperties(); + + PropertyGroups::const_iterator impl_matchPropertyGroup( const PropertyGroups& i_propertyGroups ) const; + + virtual OUString determineDefaultServiceName() const; + }; + + //= OControlImport + /** helper class for importing the description of a single control + */ + class OControlImport + :public OElementImport + ,public OValuePropertiesMetaData + { + protected: + OUString m_sControlId; + OControlElement::ElementType m_eElementType; + + PropertyValueArray m_aValueProperties; + // the value properties (value, current-value, min-value, max-value) require some special + // handling + + // we fake the attributes our base class gets: we add the attributes of the outer wrapper + // element which encloses us + css::uno::Reference< css::xml::sax::XFastAttributeList > + m_xOuterAttributes; + + /** the address of the calc cell which the control model should be bound to, + if applicable + */ + OUString m_sBoundCellAddress; + + /** name of a value binding (xforms:bind attribute) */ + OUString m_sBindingID; + + /** name of a list binding (form:xforms-list-source attribute) */ + OUString m_sListBindingID; + + /** name of a submission (xforms:submission attribute) */ + OUString m_sSubmissionID; + + protected: + // for use by derived classes only + OControlImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer + ); + + public: + OControlImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType + ); + + // SvXMLImportContext overridables + virtual void SAL_CALL startFastElement( sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + // OPropertyImport overridables + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override; + + void addOuterAttributes(const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxOuterAttribs); + + protected: + void setElementType(OControlElement::ElementType _eType) { m_eElementType = _eType; } + + protected: + static void implTranslateValueProperty( + const css::uno::Reference< css::beans::XPropertySetInfo >& _rxPropInfo, + css::beans::PropertyValue& /* [in/out] */ _rPropValue); + + virtual OUString determineDefaultServiceName() const override; + + /** registers the given cell address as value binding address for our element + + <p>The default implementation simply calls registerCellValueBinding at our import + context, but you may want to override this behaviour.</p> + + @param _rBoundCellAddress + the cell address to register for our element. Must not be <NULL/>. + @precond + we have a valid element (m_xElement) + */ + virtual void doRegisterCellValueBinding( const OUString& _rBoundCellAddress ); + + /** register the given XForms binding */ + void doRegisterXFormsValueBinding( const OUString& ); + + /** register the given XForms list binding */ + void doRegisterXFormsListBinding( const OUString& ); + + /** register the given XForms submission */ + void doRegisterXFormsSubmission( const OUString& ); + + protected: + + // OElementImport overridables + virtual css::uno::Reference< css::beans::XPropertySet > + createElement() override; + }; + + // TODO: + // this whole mechanism doesn't scale. Instead of deriving even more classes for every new attribute, + // we should have dedicated attribute handlers + // The rest of xmloff implements it this way - why don't we do, too? + + //= OImagePositionImport + class OImagePositionImport : public OControlImport + { + css::uno::Reference<css::graphic::XGraphic> m_xGraphic; + sal_Int16 m_nImagePosition; + sal_Int16 m_nImageAlign; + bool m_bHaveImagePosition; + + public: + OImagePositionImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType + ); + + protected: + // SvXMLImportContext overridables + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override; + + // OPropertyImport overridables + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override; + }; + + //= OReferredControlImport + class OReferredControlImport : public OControlImport + { + OUString m_sReferringControls; // the list of ids of controls referring to the one being imported + + public: + OReferredControlImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer + ); + + // SvXMLImportContext overridables + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override; + + // OPropertyImport overridables + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override; + }; + + //= OPasswordImport + class OPasswordImport : public OControlImport + { + public: + OPasswordImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType + ); + + // OPropertyImport overridables + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override; + }; + + //= ORadioImport + class ORadioImport : public OImagePositionImport + { + public: + ORadioImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType + ); + + protected: + // OPropertyImport overridables + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override; + }; + + //= OURLReferenceImport + /** a specialized version of the <type>OControlImport</type> class, which is able + to handle attributes which denote URLs (and stored relative) + */ + class OURLReferenceImport : public OImagePositionImport + { + public: + OURLReferenceImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType + ); + + protected: + // OPropertyImport overridables + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override; + }; + + //= OButtonImport + /** A specialized version of the <type>OControlImport</type> class, which handles + the target frame for image and command buttons + */ + class OButtonImport : public OURLReferenceImport + { + public: + OButtonImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType + ); + + protected: + // SvXMLImportContext overridables + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override; + }; + + //= OValueRangeImport + /** A specialized version of the <type>OControlImport</type> class, which imports + the value-range elements + */ + class OValueRangeImport : public OControlImport + { + private: + sal_Int32 m_nStepSizeValue; + + public: + OValueRangeImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType + ); + + protected: + // SvXMLImportContext overridables + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList ) override; + + // OPropertyImport overridables + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override; + }; + + //= OTextLikeImport + /** A specialized version of the <type>OControlImport</type> class, which handles + text like controls which have the convert-empty-to-null attribute</p> + */ + class OTextLikeImport : public OControlImport + { + private: + css::uno::Reference< css::text::XTextCursor > m_xCursor; + css::uno::Reference< css::text::XTextCursor > m_xOldCursor; + bool m_bEncounteredTextPara; + + public: + OTextLikeImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType + ); + + // SvXMLImportContext overridables + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override; + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + private: + void adjustDefaultControlProperty(); + void removeRedundantCurrentValue(); + }; + + //= OListAndComboImport + /** A specialized version of the <type>OControlImport</type> class, which handles + attributes / sub elements which are special to list and combo boxes + */ + class OListAndComboImport : public OControlImport + { + friend class OListOptionImport; + friend class OComboItemImport; + + protected: + std::vector<OUString > + m_aListSource; + std::vector< OUString > + m_aValueList; + + std::vector< sal_Int16 > + m_aSelectedSeq; + std::vector< sal_Int16 > + m_aDefaultSelectedSeq; + + OUString m_sCellListSource; /// the cell range which acts as list source for the control + + sal_Int32 m_nEmptyListItems; /// number of empty list items encountered during reading + sal_Int32 m_nEmptyValueItems; /// number of empty value items encountered during reading + + bool m_bEncounteredLSAttrib; + bool m_bLinkWithIndexes; /** <TRUE/> if and only if we should use a cell value binding + which exchanges the selection index (instead of the selection text + */ + + public: + OListAndComboImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType + ); + + // SvXMLImportContext overridables + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + // OPropertyImport overridables + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override; + + // OControlImport overridables + virtual void doRegisterCellValueBinding( const OUString& _rBoundCellAddress ) override; + + protected: + void implPushBackLabel(const OUString& _rLabel); + void implPushBackValue(const OUString& _rValue); + + void implEmptyLabelFound(); + void implEmptyValueFound(); + + void implSelectCurrentItem(); + void implDefaultSelectCurrentItem(); + }; + typedef rtl::Reference<OListAndComboImport> OListAndComboImportRef; + + //= OListOptionImport + /** helper class for importing a single <form:option> element. + */ + class OListOptionImport + :public SvXMLImportContext + { + OListAndComboImportRef m_xListBoxImport; + + public: + OListOptionImport(SvXMLImport& _rImport, + const OListAndComboImportRef& _rListBox); + + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override; + }; + + //= OComboItemImport + /** helper class for importing a single <form:item> element. + */ + class OComboItemImport + :public SvXMLImportContext + { + OListAndComboImportRef m_xListBoxImport; + + public: + OComboItemImport(SvXMLImport& _rImport, + const OListAndComboImportRef& _rListBox); + + protected: + // SvXMLImportContext overridables + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override; + }; + + + //= OColumnImport + /** helper class importing a single grid column (without the <form:column> element wrapping + the column). + + <p>BASE (the template argument) must be a derivee of OControlImport</p> + */ + template <class BASE> + class OColumnImport : public BASE + { + css::uno::Reference< css::form::XGridColumnFactory > + m_xColumnFactory; + + public: + OColumnImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType); + + protected: + // OElementImport overridables + virtual css::uno::Reference< css::beans::XPropertySet > + createElement() override; + }; + + //= OColumnWrapperImport + class OColumnWrapperImport : public SvXMLImportContext + { + css::uno::Reference< css::xml::sax::XFastAttributeList > + m_xOwnAttributes; + css::uno::Reference< css::container::XNameContainer > + m_xParentContainer; + OFormLayerXMLImport_Impl& m_rFormImport; + IEventAttacherManager& m_rEventManager; + + public: + OColumnWrapperImport(OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + sal_Int32 nElement, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer); + + // SvXMLImportContext overridables + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override; + private: + OControlImport* implCreateChildContext( + sal_Int32 nElement, + OControlElement::ElementType _eType); + }; + + /** helper class importing a single <form:grid> element + */ + class OGridImport : public OControlImport, public ODefaultEventAttacherManager + { + public: + OGridImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType); + + // SvXMLImportContext overridables + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + private: + // OElementImport overridables + virtual css::uno::Reference< css::beans::XPropertySet > createElement() override; + + css::uno::Reference< css::container::XNameContainer > m_xMeAsContainer; + }; + + /** helper class importing a single <form:form> element + */ + class OFormImport : public OElementImport, public ODefaultEventAttacherManager + { + public: + OFormImport( + OFormLayerXMLImport_Impl& _rImport, IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer + ); + + private: + // SvXMLImportContext overridables + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + // OPropertyImport overridables + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue) override; + + // OElementImport overridables + virtual css::uno::Reference< css::beans::XPropertySet > + createElement() override; + + virtual OUString determineDefaultServiceName() const override; + void implTranslateStringListProperty(const OUString& _rPropertyName, const OUString& _rValue); + + css::uno::Reference< css::container::XNameContainer > m_xMeAsContainer; + }; + + //= OXMLDataSourceImport + class OXMLDataSourceImport : public SvXMLImportContext + { + public: + OXMLDataSourceImport( SvXMLImport& _rImport + ,const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList + ,const css::uno::Reference< css::beans::XPropertySet >& _xElement); + }; + + //= OColumnImport + template <class BASE> + OColumnImport< BASE >::OColumnImport(OFormLayerXMLImport_Impl& _rImport, + IEventAttacherManager& _rEventManager, + const css::uno::Reference< css::container::XNameContainer >& _rxParentContainer, + OControlElement::ElementType _eType) + :BASE(_rImport, _rEventManager, _rxParentContainer, _eType) + ,m_xColumnFactory(_rxParentContainer, css::uno::UNO_QUERY) + { + OSL_ENSURE(m_xColumnFactory.is(), "OColumnImport::OColumnImport: invalid parent container (no factory)!"); + } + + // OElementImport overridables + template <class BASE> + css::uno::Reference< css::beans::XPropertySet > OColumnImport< BASE >::createElement() + { + css::uno::Reference< css::beans::XPropertySet > xReturn; + // no call to the base class' method. We have to use the grid column factory + if (m_xColumnFactory.is()) + { + // create the column + xReturn = m_xColumnFactory->createColumn(this->m_sServiceName); + OSL_ENSURE(xReturn.is(), "OColumnImport::createElement: the factory returned an invalid object!"); + } + return xReturn; + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/eventexport.cxx b/xmloff/source/forms/eventexport.cxx new file mode 100644 index 000000000..25548fb0a --- /dev/null +++ b/xmloff/source/forms/eventexport.cxx @@ -0,0 +1,126 @@ +/* -*- 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 "eventexport.hxx" +#include "strings.hxx" +#include <comphelper/sequence.hxx> +#include <sal/log.hxx> + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::lang; + + //= OEventDescriptorMapper + OEventDescriptorMapper::OEventDescriptorMapper(const Sequence< ScriptEventDescriptor >& _rEvents) + { + // translate the events + OUString sLibrary, sLocalMacroName; + for (const auto& rEvent : _rEvents) + { + // the name of the event is build from listener interface and listener method name + OUString sName = rEvent.ListenerType + + EVENT_NAME_SEPARATOR + + rEvent.EventMethod; + + Sequence< PropertyValue >& rMappedEvent = m_aMappedEvents[sName]; + + sLocalMacroName = rEvent.ScriptCode; + sLibrary.clear(); + if (rEvent.ScriptType == EVENT_STARBASIC) + { // for StarBasic, the library name is part of the ScriptCode + sal_Int32 nPrefixLen = sLocalMacroName.indexOf( ':' ); + SAL_WARN_IF( 0 > nPrefixLen, "xmloff", "OEventDescriptorMapper::OEventDescriptorMapper: invalid script code prefix!" ); + if ( 0 <= nPrefixLen ) + { + // the export handler for StarBasic expects "StarOffice", not "application" for application modules ... + sLibrary = sLocalMacroName.copy( 0, nPrefixLen ); + if (sLibrary == EVENT_APPLICATION) + sLibrary = EVENT_STAROFFICE; + + sLocalMacroName = sLocalMacroName.copy( nPrefixLen + 1 ); + } + // tree property values to describe one event ... + rMappedEvent.realloc( sLibrary.isEmpty() ? 2 : 3 ); + auto pMappedEvent = rMappedEvent.getArray(); + + // ... the type + pMappedEvent[0] = PropertyValue(EVENT_TYPE, -1, Any(rEvent.ScriptType), PropertyState_DIRECT_VALUE); + + // and the macro name + pMappedEvent[1] = PropertyValue(EVENT_LOCALMACRONAME, -1, Any(sLocalMacroName), PropertyState_DIRECT_VALUE); + + // the library + if ( !sLibrary.isEmpty() ) + pMappedEvent[2] = PropertyValue(EVENT_LIBRARY, -1, Any(sLibrary), PropertyState_DIRECT_VALUE); + } + else + { + rMappedEvent = { PropertyValue(EVENT_TYPE, -1, Any(rEvent.ScriptType), PropertyState_DIRECT_VALUE), + // and the macro name + PropertyValue(EVENT_SCRIPTURL, -1, Any(rEvent.ScriptCode), PropertyState_DIRECT_VALUE) }; + } + } + } + + void SAL_CALL OEventDescriptorMapper::replaceByName( const OUString&, const Any& ) + { + throw IllegalArgumentException( + "replacing is not implemented for this wrapper class.", static_cast< ::cppu::OWeakObject* >(this), 1); + } + + Any SAL_CALL OEventDescriptorMapper::getByName( const OUString& _rName ) + { + MapString2PropertyValueSequence::const_iterator aPos = m_aMappedEvents.find(_rName); + if (m_aMappedEvents.end() == aPos) + throw NoSuchElementException( + "There is no element named " + _rName, + static_cast< ::cppu::OWeakObject* >(this)); + + return Any(aPos->second); + } + + Sequence< OUString > SAL_CALL OEventDescriptorMapper::getElementNames( ) + { + return comphelper::mapKeysToSequence(m_aMappedEvents); + } + + sal_Bool SAL_CALL OEventDescriptorMapper::hasByName( const OUString& _rName ) + { + MapString2PropertyValueSequence::const_iterator aPos = m_aMappedEvents.find(_rName); + return m_aMappedEvents.end() != aPos; + } + + Type SAL_CALL OEventDescriptorMapper::getElementType( ) + { + return ::cppu::UnoType<PropertyValue>::get(); + } + + sal_Bool SAL_CALL OEventDescriptorMapper::hasElements( ) + { + return !m_aMappedEvents.empty(); + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/eventexport.hxx b/xmloff/source/forms/eventexport.hxx new file mode 100644 index 000000000..c4bb752c4 --- /dev/null +++ b/xmloff/source/forms/eventexport.hxx @@ -0,0 +1,71 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <map> + +#include <com/sun/star/container/XNameReplace.hpp> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <cppuhelper/implbase.hxx> + +namespace xmloff +{ + + //= OEventDescriptorMapper + typedef ::cppu::WeakImplHelper < css::container::XNameReplace + > OEventDescriptorMapper_Base; + /** helper class wrapping different script event representations + + <p>In the form layer, the script events are represented by <type scope="com.sun.star.script">ScriptEventDescriptor</type> + instances. The office applications, on the other hand, represent their a single script event as sequence + of <type scope="com.sun.star.beans">PropertyValue</type>s, where all events of a given object are + accessible through a <type scope="com.sun.star.container">XNameReplace</type> interface.</p> + <p>This class maps the first representation of events of a single object to the second one.</p> + <p>This way, we can use the helper classes here in the project.</p> + */ + class OEventDescriptorMapper : public OEventDescriptorMapper_Base + { + typedef std::map< OUString, css::uno::Sequence < css::beans::PropertyValue > > MapString2PropertyValueSequence; + MapString2PropertyValueSequence m_aMappedEvents; + + public: + explicit OEventDescriptorMapper( + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rEvents); + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/eventimport.cxx b/xmloff/source/forms/eventimport.cxx new file mode 100644 index 000000000..02bfe7764 --- /dev/null +++ b/xmloff/source/forms/eventimport.cxx @@ -0,0 +1,135 @@ +/* -*- 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 "eventimport.hxx" +#include <com/sun/star/script/XEventAttacherManager.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <osl/diagnose.h> +#include "strings.hxx" + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::container; + + //= OFormEventsImportContext + OFormEventsImportContext::OFormEventsImportContext(SvXMLImport& _rImport, IEventAttacher& _rEventAttacher) + :XMLEventsImportContext(_rImport) + ,m_rEventAttacher(_rEventAttacher) + { + } + + void OFormEventsImportContext::endFastElement(sal_Int32 ) + { + Sequence< ScriptEventDescriptor > aTranslated(aCollectEvents.size()); + ScriptEventDescriptor* pTranslated = aTranslated.getArray(); + + // loop through the collected events and translate them + sal_Int32 nSeparatorPos = -1; + for ( const auto& rEvent : aCollectEvents ) + { + // the name of the event is built from ListenerType::EventMethod + nSeparatorPos = rEvent.first.indexOf(EVENT_NAME_SEPARATOR); + OSL_ENSURE(-1 != nSeparatorPos, "OFormEventsImportContext::EndElement: invalid (unrecognized) event name!"); + pTranslated->ListenerType = rEvent.first.copy(0, nSeparatorPos); + pTranslated->EventMethod = rEvent.first.copy(nSeparatorPos + sizeof(EVENT_NAME_SEPARATOR) - 1); + + OUString sLibrary; + + // the local macro name and the event type are specified as properties + const PropertyValue* pEventDescription = rEvent.second.getConstArray(); + const PropertyValue* pEventDescriptionEnd = pEventDescription + rEvent.second.getLength(); + for (;pEventDescription != pEventDescriptionEnd; ++pEventDescription) + { + if (pEventDescription->Name == EVENT_LOCALMACRONAME || + pEventDescription->Name == EVENT_SCRIPTURL) + pEventDescription->Value >>= pTranslated->ScriptCode; + else if (pEventDescription->Name == EVENT_TYPE) + pEventDescription->Value >>= pTranslated->ScriptType; + else if (pEventDescription->Name == EVENT_LIBRARY) + pEventDescription->Value >>= sLibrary; + } + + if (pTranslated->ScriptType == EVENT_STARBASIC) + { + if (sLibrary == EVENT_STAROFFICE) + sLibrary = EVENT_APPLICATION; + + if ( !sLibrary.isEmpty() ) + { + // for StarBasic, the library is prepended + sLibrary += ":"; + } + sLibrary += pTranslated->ScriptCode; + pTranslated->ScriptCode = sLibrary; + } + + ++pTranslated; + } + + // register the events + m_rEventAttacher.registerEvents(aTranslated); + } + + //= ODefaultEventAttacherManager + + ODefaultEventAttacherManager::~ODefaultEventAttacherManager() + { + } + + void ODefaultEventAttacherManager::registerEvents(const Reference< XPropertySet >& _rxElement, + const Sequence< ScriptEventDescriptor >& _rEvents) + { + OSL_ENSURE(m_aEvents.end() == m_aEvents.find(_rxElement), + "ODefaultEventAttacherManager::registerEvents: already have events for this object!"); + // for the moment, only remember the script events + m_aEvents[_rxElement] = _rEvents; + } + + void ODefaultEventAttacherManager::setEvents(const Reference< XIndexAccess >& _rxContainer) + { + Reference< XEventAttacherManager > xEventManager(_rxContainer, UNO_QUERY); + if (!xEventManager.is()) + { + OSL_FAIL("ODefaultEventAttacherManager::setEvents: invalid argument!"); + return; + } + + // loop through all elements + sal_Int32 nCount = _rxContainer->getCount(); + Reference< XPropertySet > xCurrent; + MapPropertySet2ScriptSequence::const_iterator aRegisteredEventsPos; + for (sal_Int32 i=0; i<nCount; ++i) + { + xCurrent.set(_rxContainer->getByIndex(i), css::uno::UNO_QUERY); + if (xCurrent.is()) + { + aRegisteredEventsPos = m_aEvents.find(xCurrent); + if (m_aEvents.end() != aRegisteredEventsPos) + xEventManager->registerScriptEvents(i, aRegisteredEventsPos->second); + } + } + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/eventimport.hxx b/xmloff/source/forms/eventimport.hxx new file mode 100644 index 000000000..6e3f686df --- /dev/null +++ b/xmloff/source/forms/eventimport.hxx @@ -0,0 +1,77 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <map> + +#include <xmloff/XMLEventsImportContext.hxx> +#include "callbacks.hxx" +#include <com/sun/star/container/XIndexAccess.hpp> + +class SvXMLImport; +namespace xmloff +{ + + //= OFormEventsImportContext + class OFormEventsImportContext : public XMLEventsImportContext + { + IEventAttacher& m_rEventAttacher; + + public: + OFormEventsImportContext(SvXMLImport& _rImport, + IEventAttacher& _rEventAttacher); + + protected: + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + }; + + //= ODefaultEventAttacherManager + class ODefaultEventAttacherManager : public IEventAttacherManager + { + typedef std::map< + css::uno::Reference< css::beans::XPropertySet >, + css::uno::Sequence< css::script::ScriptEventDescriptor >> + MapPropertySet2ScriptSequence; + // usually an event attacher manager will need to collect all script events registered, 'cause + // the _real_ XEventAttacherManager handles it's events by index, but out indices are not fixed + // until _all_ controls have been inserted. + + MapPropertySet2ScriptSequence m_aEvents; + + public: + // IEventAttacherManager + virtual void registerEvents( + const css::uno::Reference< css::beans::XPropertySet >& _rxElement, + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rEvents + ) override; + + protected: + void setEvents( + const css::uno::Reference< css::container::XIndexAccess >& _rxContainer + ); + + virtual ~ODefaultEventAttacherManager(); + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/formattributes.cxx b/xmloff/source/forms/formattributes.cxx new file mode 100644 index 000000000..6b7b3fb9f --- /dev/null +++ b/xmloff/source/forms/formattributes.cxx @@ -0,0 +1,405 @@ +/* -*- 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 "formattributes.hxx" + +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/xmlimp.hxx> +#include <osl/diagnose.h> + +using namespace xmloff::token; + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + + //= OAttributeMetaData + OUString OAttributeMetaData::getCommonControlAttributeName(CCAFlags _nId) + { + switch (_nId) + { + case CCAFlags::Name: return "name"; + case CCAFlags::ServiceName: return "control-implementation"; + case CCAFlags::ButtonType: return "button-type"; +// disabled(AddAttributeIdLegacy) case CCAFlags::ControlId: return "id"; + case CCAFlags::CurrentSelected: return "current-selected"; + case CCAFlags::CurrentValue: return "current-value"; + case CCAFlags::Disabled: return "disabled"; + case CCAFlags::EnableVisible: return "visible"; + case CCAFlags::Dropdown: return "dropdown"; + case CCAFlags::For: return "for"; + case CCAFlags::ImageData: return "image-data"; + case CCAFlags::Label: return "label"; + case CCAFlags::MaxLength: return "max-length"; + case CCAFlags::Printable: return "printable"; + case CCAFlags::ReadOnly: return "readonly"; + case CCAFlags::Selected: return "selected"; + case CCAFlags::Size: return "size"; + case CCAFlags::TabIndex: return "tab-index"; + case CCAFlags::TargetFrame: return "target-frame"; + case CCAFlags::TargetLocation: return "href"; // the only special thing here: TargetLocation is represented by an xlink:href attribute + case CCAFlags::TabStop: return "tab-stop"; + case CCAFlags::Title: return "title"; + case CCAFlags::Value: return "value"; + case CCAFlags::Orientation: return "orientation"; + case CCAFlags::VisualEffect: return "visual-effect"; + default: + OSL_FAIL("OAttributeMetaData::getCommonControlAttributeName: invalid id (maybe you or-ed two flags?)!"); + } + return ""; + } + + sal_Int32 OAttributeMetaData::getCommonControlAttributeToken(CCAFlags _nId) + { + switch (_nId) + { + case CCAFlags::Name: return XML_NAME; + case CCAFlags::ServiceName: return XML_CONTROL_IMPLEMENTATION; + case CCAFlags::ButtonType: return XML_BUTTON_TYPE; +// disabled(AddAttributeIdLegacy) case CCAFlags::ControlId: return "id"; + case CCAFlags::CurrentSelected: return XML_CURRENT_SELECTED; + case CCAFlags::CurrentValue: return XML_CURRENT_VALUE; + case CCAFlags::Disabled: return XML_DISABLED; + case CCAFlags::EnableVisible: return XML_VISIBLE; + case CCAFlags::Dropdown: return XML_DROPDOWN; + case CCAFlags::For: return XML_FOR; + case CCAFlags::ImageData: return XML_IMAGE_DATA; + case CCAFlags::Label: return XML_LABEL; + case CCAFlags::MaxLength: return XML_MAX_LENGTH; + case CCAFlags::Printable: return XML_PRINTABLE; + case CCAFlags::ReadOnly: return XML_READONLY; + case CCAFlags::Selected: return XML_SELECTED; + case CCAFlags::Size: return XML_SIZE; + case CCAFlags::TabIndex: return XML_TAB_INDEX; + case CCAFlags::TargetFrame: return XML_TARGET_FRAME; + case CCAFlags::TargetLocation: return XML_HREF; // the only special thing here: TargetLocation is represented by an xlink:href attribute + case CCAFlags::TabStop: return XML_TAB_STOP; + case CCAFlags::Title: return XML_TITLE; + case CCAFlags::Value: return XML_VALUE; + case CCAFlags::Orientation: return XML_ORIENTATION; + case CCAFlags::VisualEffect: return XML_VISUAL_EFFECT; + default: + assert(false && "OAttributeMetaData::getCommonControlAttributeName: invalid id (maybe you or-ed two flags?)!"); + } + return XML_UNKNOWN; + } + + sal_uInt16 OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags _nId) + { + if (CCAFlags::TargetLocation == _nId) + return XML_NAMESPACE_XLINK; + + if (CCAFlags::TargetFrame == _nId) + return XML_NAMESPACE_OFFICE; + + return XML_NAMESPACE_FORM; + } + + OUString OAttributeMetaData::getFormAttributeName(FormAttributes _eAttrib) + { + switch (_eAttrib) + { + case faName: return "name"; + case faAction: return "href"; // the only special thing here: Action is represented by an xlink:href attribute + case faEnctype: return "enctype"; + case faMethod: return "method"; + case faAllowDeletes: return "allow-deletes"; + case faAllowInserts: return "allow-inserts"; + case faAllowUpdates: return "allow-updates"; + case faApplyFilter: return "apply-filter"; + case faCommand: return "command"; + case faCommandType: return "command-type"; + case faEscapeProcessing: return "escape-processing"; + case faDatasource: return "datasource"; + case faDetailFields: return "detail-fields"; + case faFilter: return "filter"; + case faIgnoreResult: return "ignore-result"; + case faMasterFields: return "master-fields"; + case faNavigationMode: return "navigation-mode"; + case faOrder: return "order"; + case faTabbingCycle: return "tab-cycle"; + default: + OSL_FAIL("OAttributeMetaData::getFormAttributeName: invalid id!"); + } + return ""; + } + + sal_Int32 OAttributeMetaData::getFormAttributeToken(FormAttributes _eAttrib) + { + switch (_eAttrib) + { + case faName: return XML_NAME; + case faAction: return XML_HREF; // the only special thing here: Action is represented by an xlink:href attribute + case faEnctype: return XML_ENCTYPE; + case faMethod: return XML_METHOD; + case faAllowDeletes: return XML_ALLOW_DELETES; + case faAllowInserts: return XML_ALLOW_INSERTS; + case faAllowUpdates: return XML_ALLOW_UPDATES; + case faApplyFilter: return XML_APPLY_FILTER; + case faCommand: return XML_COMMAND; + case faCommandType: return XML_COMMAND_TYPE; + case faEscapeProcessing: return XML_ESCAPE_PROCESSING; + case faDatasource: return XML_DATASOURCE; + case faDetailFields: return XML_DETAIL_FIELDS; + case faFilter: return XML_FILTER; + case faIgnoreResult: return XML_IGNORE_RESULT; + case faMasterFields: return XML_MASTER_FIELDS; + case faNavigationMode: return XML_NAVIGATION_MODE; + case faOrder: return XML_ORDER; + case faTabbingCycle: return XML_TAB_CYCLE; + default: + assert(false && "OAttributeMetaData::getFormAttributeName: invalid id!"); + } + return XML_NONE; + } + + sal_uInt16 OAttributeMetaData::getFormAttributeNamespace(FormAttributes _eAttrib) + { + if (faAction == _eAttrib) + return XML_NAMESPACE_XLINK; + + return XML_NAMESPACE_FORM; + } + + OUString OAttributeMetaData::getDatabaseAttributeName(DAFlags _nId) + { + switch (_nId) + { + case DAFlags::BoundColumn: return "bound-column"; + case DAFlags::ConvertEmpty: return "convert-empty-to-null"; + case DAFlags::DataField: return "data-field"; + case DAFlags::ListSource: return "list-source"; + case DAFlags::ListSource_TYPE: return "list-source-type"; + case DAFlags::InputRequired: return "input-required"; + default: + OSL_FAIL("OAttributeMetaData::getDatabaseAttributeName: invalid id (maybe you or-ed two flags?)!"); + } + return ""; + } + + sal_Int32 OAttributeMetaData::getDatabaseAttributeToken(DAFlags _nId) + { + switch (_nId) + { + case DAFlags::BoundColumn: return XML_BOUND_COLUMN; + case DAFlags::ConvertEmpty: return XML_CONVERT_EMPTY_TO_NULL; + case DAFlags::DataField: return XML_DATA_FIELD; + case DAFlags::ListSource: return XML_LIST_SOURCE; + case DAFlags::ListSource_TYPE: return XML_LIST_SOURCE_TYPE; + case DAFlags::InputRequired: return XML_INPUT_REQUIRED; + default: + assert(false && "OAttributeMetaData::getDatabaseAttributeName: invalid id (maybe you or-ed two flags?)!"); + } + return XML_NONE; + } + + OUString OAttributeMetaData::getBindingAttributeName(BAFlags _nId) + { + switch (_nId) + { + case BAFlags::LinkedCell: return "linked-cell"; + case BAFlags::ListLinkingType: return "list-linkage-type"; + case BAFlags::ListCellRange: return "source-cell-range"; + default: + OSL_FAIL("OAttributeMetaData::getBindingAttributeName: invalid id (maybe you or-ed two flags?)!"); + } + return ""; + } + + sal_Int32 OAttributeMetaData::getBindingAttributeToken(BAFlags _nId) + { + switch (_nId) + { + case BAFlags::LinkedCell: return XML_LINKED_CELL; + case BAFlags::ListLinkingType: return XML_LIST_LINKAGE_TYPE; + case BAFlags::ListCellRange: return XML_SOURCE_CELL_RANGE; + default: + assert(false && "OAttributeMetaData::getBindingAttributeName: invalid id (maybe you or-ed two flags?)!"); + } + return XML_UNKNOWN; + } + + OUString OAttributeMetaData::getSpecialAttributeName(SCAFlags _nId) + { + switch (_nId) + { + case SCAFlags::EchoChar: return "echo-char"; + case SCAFlags::MaxValue: return "max-value"; + case SCAFlags::MinValue: return "min-value"; + case SCAFlags::Validation: return "validation"; + case SCAFlags::GroupName: return "group-name"; + case SCAFlags::MultiLine: return "multi-line"; + case SCAFlags::AutoCompletion: return "auto-complete"; + case SCAFlags::Multiple: return "multiple"; + case SCAFlags::DefaultButton: return "default-button"; + case SCAFlags::CurrentState: return "current-state"; + case SCAFlags::IsTristate: return "is-tristate"; + case SCAFlags::State: return "state"; + case SCAFlags::ColumnStyleName: return "text-style-name"; + case SCAFlags::StepSize: return "step-size"; + case SCAFlags::PageStepSize: return "page-step-size"; + case SCAFlags::RepeatDelay: return "delay-for-repeat"; + case SCAFlags::Toggle: return "toggle"; + case SCAFlags::FocusOnClick: return "focus-on-click"; + default: + OSL_FAIL("OAttributeMetaData::getSpecialAttributeName: invalid id (maybe you or-ed two flags?)!"); + } + return ""; + } + + sal_Int32 OAttributeMetaData::getSpecialAttributeToken(SCAFlags _nId) + { + switch (_nId) + { + case SCAFlags::EchoChar: return XML_ECHO_CHAR; + case SCAFlags::MaxValue: return XML_MAX_VALUE; + case SCAFlags::MinValue: return XML_MIN_VALUE; + case SCAFlags::Validation: return XML_VALIDATION; + case SCAFlags::GroupName: return XML_GROUP_NAME; + case SCAFlags::MultiLine: return XML_MULTI_LINE; + case SCAFlags::AutoCompletion: return XML_AUTO_COMPLETE; + case SCAFlags::Multiple: return XML_MULTIPLE; + case SCAFlags::DefaultButton: return XML_DEFAULT_BUTTON; + case SCAFlags::CurrentState: return XML_CURRENT_STATE; + case SCAFlags::IsTristate: return XML_IS_TRISTATE; + case SCAFlags::State: return XML_STATE; + case SCAFlags::ColumnStyleName: return XML_TEXT_STYLE_NAME; + case SCAFlags::StepSize: return XML_STEP_SIZE; + case SCAFlags::PageStepSize: return XML_PAGE_STEP_SIZE; + case SCAFlags::RepeatDelay: return XML_DELAY_FOR_REPEAT; + case SCAFlags::Toggle: return XML_TOGGLE; + case SCAFlags::FocusOnClick: return XML_FOCUS_ON_CLICK; + default: + assert(false && "OAttributeMetaData::getSpecialAttributeName: invalid id (maybe you or-ed two flags?)!"); + } + return XML_UNKNOWN; + } + + sal_uInt16 OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags _nId) + { + switch( _nId ) + { + case SCAFlags::GroupName: return XML_NAMESPACE_FORMX; + default: break; + } + return XML_NAMESPACE_FORM; + } + + OUString OAttributeMetaData::getOfficeFormsAttributeName(OfficeFormsAttributes _eAttrib) + { + switch (_eAttrib) + { + case ofaAutomaticFocus: return "automatic-focus"; + case ofaApplyDesignMode: return "apply-design-mode"; + default: + OSL_FAIL("OAttributeMetaData::getOfficeFormsAttributeName: invalid id!"); + } + return ""; + } + + xmloff::token::XMLTokenEnum OAttributeMetaData::getOfficeFormsAttributeToken(OfficeFormsAttributes _eAttrib) + { + switch (_eAttrib) + { + case ofaAutomaticFocus: return token::XML_AUTOMATIC_FOCUS; + case ofaApplyDesignMode: return token::XML_APPLY_DESIGN_MODE; + default: + assert(false && "OAttributeMetaData::getOfficeFormsAttributeName: invalid id!"); + } + return token::XML_NONE; + } + + //= OAttribute2Property + OAttribute2Property::OAttribute2Property() + { + } + + OAttribute2Property::~OAttribute2Property() + { + } + + const OAttribute2Property::AttributeAssignment* OAttribute2Property::getAttributeTranslation( + sal_Int32 nAttributeToken) + { + auto aPos = m_aKnownProperties.find(nAttributeToken & TOKEN_MASK); + if (m_aKnownProperties.end() != aPos) + return &aPos->second; + return nullptr; + } + + void OAttribute2Property::addStringProperty( + sal_Int32 nAttributeToken, const OUString& _rPropertyName) + { + implAdd(nAttributeToken, _rPropertyName, ::cppu::UnoType<OUString>::get()); + } + + void OAttribute2Property::addBooleanProperty( + sal_Int32 nAttributeToken, const OUString& _rPropertyName, + const bool /*_bAttributeDefault*/, const bool _bInverseSemantics) + { + AttributeAssignment& aAssignment = implAdd(nAttributeToken, _rPropertyName, cppu::UnoType<bool>::get()); + aAssignment.bInverseSemantics = _bInverseSemantics; + } + + void OAttribute2Property::addInt16Property( + sal_Int32 nAttributeToken, const OUString& _rPropertyName) + { + implAdd(nAttributeToken, _rPropertyName, ::cppu::UnoType<sal_Int16>::get()); + } + + void OAttribute2Property::addInt32Property( + sal_Int32 nAttributeToken, const OUString& _rPropertyName) + { + implAdd( nAttributeToken, _rPropertyName, ::cppu::UnoType<sal_Int32>::get() ); + } + + void OAttribute2Property::addEnumPropertyImpl( + sal_Int32 nAttributeToken, const OUString& _rPropertyName, + const SvXMLEnumMapEntry<sal_uInt16>* _pValueMap, + const css::uno::Type* _pType) + { + AttributeAssignment& aAssignment = implAdd(nAttributeToken, _rPropertyName, + _pType ? *_pType : ::cppu::UnoType<sal_Int32>::get()); + aAssignment.pEnumMap = _pValueMap; + } + + OAttribute2Property::AttributeAssignment& OAttribute2Property::implAdd( + sal_Int32 nAttributeToken, const OUString& _rPropertyName, + const css::uno::Type& _rType) + { + nAttributeToken &= TOKEN_MASK; + OSL_ENSURE(m_aKnownProperties.end() == m_aKnownProperties.find(nAttributeToken), + "OAttribute2Property::implAdd: already have this attribute!"); + + AttributeAssignment aAssignment; + aAssignment.sPropertyName = _rPropertyName; + aAssignment.aPropertyType = _rType; + + // redundance, the accessor is stored in aAssignment.sAttributeName, too + m_aKnownProperties[nAttributeToken] = aAssignment; + return m_aKnownProperties[nAttributeToken]; + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/formattributes.hxx b/xmloff/source/forms/formattributes.hxx new file mode 100644 index 000000000..ae36fd65d --- /dev/null +++ b/xmloff/source/forms/formattributes.hxx @@ -0,0 +1,414 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <map> + +#include <com/sun/star/uno/Type.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <o3tl/typed_flags_set.hxx> + +template<typename EnumT> +struct SvXMLEnumMapEntry; + + // flags for common control attributes +enum class CCAFlags { + NONE = 0x00000000, + Name = 0x00000001, + ServiceName = 0x00000002, + ButtonType = 0x00000004, + ControlId = 0x00000008, + CurrentSelected = 0x00000010, + CurrentValue = 0x00000020, + Disabled = 0x00000040, + Dropdown = 0x00000080, + For = 0x00000100, + ImageData = 0x00000200, + Label = 0x00000400, + MaxLength = 0x00000800, + Printable = 0x00001000, + ReadOnly = 0x00002000, + Selected = 0x00004000, + Size = 0x00008000, + TabIndex = 0x00010000, + TargetFrame = 0x00020000, + TargetLocation = 0x00040000, + TabStop = 0x00080000, + Title = 0x00100000, + Value = 0x00200000, + Orientation = 0x00400000, + VisualEffect = 0x00800000, + EnableVisible = 0x01000000, +}; +namespace o3tl { + template<> struct typed_flags<CCAFlags> : is_typed_flags<CCAFlags, 0x01ffffff> {}; +} + + // flags for database control attributes +enum class DAFlags { + NONE = 0x0000, + BoundColumn = 0x0001, + ConvertEmpty = 0x0002, + DataField = 0x0004, + ListSource = 0x0008, + ListSource_TYPE = 0x0010, + InputRequired = 0x0020, +}; +namespace o3tl { + template<> struct typed_flags<DAFlags> : is_typed_flags<DAFlags, 0x003f> {}; +} + + // flags for binding related control attributes +enum class BAFlags { + NONE = 0x0000, + LinkedCell = 0x0001, + ListLinkingType = 0x0002, + ListCellRange = 0x0004, + XFormsBind = 0x0008, + XFormsListBind = 0x0010, + XFormsSubmission = 0x0020 +}; +namespace o3tl { + template<> struct typed_flags<BAFlags> : is_typed_flags<BAFlags, 0x003f> {}; +} + + // flags for event attributes +enum class EAFlags { + NONE = 0x0000, + ControlEvents = 0x0001, + OnChange = 0x0002, + OnClick = 0x0004, + OnDoubleClick = 0x0008, + OnSelect = 0x0010 +}; +namespace o3tl { + template<> struct typed_flags<EAFlags> : is_typed_flags<EAFlags, 0x001f> {}; +} + + // any other attributes, which are special to some control types +enum class SCAFlags { + NONE = 0x000000, + EchoChar = 0x000001, + MaxValue = 0x000002, + MinValue = 0x000004, + Validation = 0x000008, + GroupName = 0x000010, + MultiLine = 0x000020, + AutoCompletion = 0x000080, + Multiple = 0x000100, + DefaultButton = 0x000200, + CurrentState = 0x000400, + IsTristate = 0x000800, + State = 0x001000, + ColumnStyleName = 0x002000, + StepSize = 0x004000, + PageStepSize = 0x008000, + RepeatDelay = 0x010000, + Toggle = 0x020000, + FocusOnClick = 0x040000, + ImagePosition = 0x080000 +}; +namespace o3tl { + template<> struct typed_flags<SCAFlags> : is_typed_flags<SCAFlags, 0x0fffbf> {}; +} + + +namespace xmloff +{ + + /// attributes in the xml tag representing a form + enum FormAttributes + { + faName, + faAction, + faEnctype, + faMethod, + faAllowDeletes, + faAllowInserts, + faAllowUpdates, + faApplyFilter, + faCommand, + faCommandType, + faEscapeProcessing, + faDatasource, + faDetailFields, + faFilter, + faIgnoreResult, + faMasterFields, + faNavigationMode, + faOrder, + faTabbingCycle + }; + + // attributes of the office:forms element + enum OfficeFormsAttributes + { + ofaAutomaticFocus, + ofaApplyDesignMode + }; + + //= OAttributeMetaData + /** allows the translation of attribute ids into strings. + + <p>This class does not allow to connect xml attributes to property names or + something like that, it only deals with the xml side</p> + */ + class OAttributeMetaData + { + public: + /** calculates the xml attribute representation of a common control attribute. + @param _nId + the id of the attribute. Has to be one of the CCA_* constants. + */ + static OUString getCommonControlAttributeName(CCAFlags _nId); + + /** calculates the xml attribute representation of a common control attribute. + @param _nId + the id of the attribute. Has to be one of the CCA_* constants. + */ + static sal_Int32 getCommonControlAttributeToken(CCAFlags _nId); + + /** calculates the xml namespace key to use for a common control attribute + @param _nId + the id of the attribute. Has to be one of the CCA_* constants. + */ + static sal_uInt16 getCommonControlAttributeNamespace(CCAFlags _nId); + + /** retrieves the name of an attribute of a form xml representation + @param _eAttrib + enum value specifying the attribute + */ + static OUString getFormAttributeName(FormAttributes _eAttrib); + + /** retrieves the name of an attribute of a form xml representation + @param _eAttrib + enum value specifying the attribute + */ + static sal_Int32 getFormAttributeToken(FormAttributes _eAttrib); + + /** calculates the xml namespace key to use for an attribute of a form xml representation + @param _eAttrib + enum value specifying the attribute + */ + static sal_uInt16 getFormAttributeNamespace(FormAttributes _eAttrib); + + /** calculates the xml attribute representation of a database attribute. + @param _nId + the id of the attribute. Has to be one of the DA_* constants. + */ + static OUString getDatabaseAttributeName(DAFlags _nId); + + /** calculates the xml attribute representation of a database attribute. + @param _nId + the id of the attribute. Has to be one of the DA_* constants. + */ + static sal_Int32 getDatabaseAttributeToken(DAFlags _nId); + + /** calculates the xml namespace key to use for a database attribute. + @param _nId + the id of the attribute. Has to be one of the DA_* constants. + */ + static sal_uInt16 getDatabaseAttributeNamespace() + { + // nothing special here + return XML_NAMESPACE_FORM; + } + + /** calculates the xml attribute representation of a special attribute. + @param _nId + the id of the attribute. Has to be one of the SCA_* constants. + */ + static OUString getSpecialAttributeName(SCAFlags _nId); + + /** calculates the xml attribute representation of a special attribute. + @param _nId + the id of the attribute. Has to be one of the SCA_* constants. + */ + static sal_Int32 getSpecialAttributeToken(SCAFlags _nId); + + /** calculates the xml attribute representation of a binding attribute. + @param _nId + the id of the attribute. Has to be one of the BA_* constants. + */ + static OUString getBindingAttributeName(BAFlags _nId); + + /** calculates the xml attribute representation of a binding attribute. + @param _nId + the id of the attribute. Has to be one of the BA_* constants. + */ + static sal_Int32 getBindingAttributeToken(BAFlags _nId); + + /** calculates the xml namespace key to use for a binding attribute. + @param _nId + the id of the attribute. Has to be one of the BA_* constants. + */ + static sal_uInt16 getBindingAttributeNamespace() + { + // nothing special here + return XML_NAMESPACE_FORM; + } + + /** calculates the xml namespace key to use for a special attribute. + @param _nId + the id of the attribute. Has to be one of the SCA_* constants. + */ + static sal_uInt16 getSpecialAttributeNamespace(SCAFlags _nId); + + /** calculates the xml attribute representation of an attribute of the office:forms element + @param _nId + the id of the attribute + */ + static OUString getOfficeFormsAttributeName(OfficeFormsAttributes _eAttrib); + static xmloff::token::XMLTokenEnum getOfficeFormsAttributeToken(OfficeFormsAttributes _eAttrib); + + /** calculates the xml namedspace key of an attribute of the office:forms element + @param _nId + the id of the attribute + */ + static sal_uInt16 getOfficeFormsAttributeNamespace() + { // nothing special here + return XML_NAMESPACE_FORM; + } + }; + + //= OAttribute2Property + /** some kind of opposite to the OAttributeMetaData class. Able to translate + attributes into property names/types + + <p>The construction of this class is rather expensive (or at least it's initialization from outside), + so it should be shared</p> + */ + class OAttribute2Property final + { + public: + // TODO: maybe the following struct should be used for exports, too. In this case we would not need to + // store it's instances in a map, but in a vector for faster access. + struct AttributeAssignment + { + OUString sPropertyName; // the property name + css::uno::Type aPropertyType; // the property type + + // entries which are special to some value types + const SvXMLEnumMapEntry<sal_uInt16>* + pEnumMap; // the enum map, if applicable + bool bInverseSemantics; // for booleans: attribute and property value have the same or an inverse semantics? + + AttributeAssignment() : pEnumMap(nullptr), bInverseSemantics(false) { } + }; + + private: + std::map<sal_Int32, AttributeAssignment> m_aKnownProperties; + + public: + OAttribute2Property(); + ~OAttribute2Property(); + + /** return the AttributeAssignment which corresponds to the given attribute + + @return + a pointer to the <type>AttributeAssignment</type> structure as requested, NULL if the attribute + does not represent a property. + */ + const AttributeAssignment* getAttributeTranslation(sal_Int32 nAttributeToken); + + /** add an attribute assignment referring to a string property to the map + @param _pAttributeName + the name of the attribute + @param _rPropertyName + the name of the property assigned to the attribute + */ + void addStringProperty( + sal_Int32 nAttributeToken, const OUString& _rPropertyName); + + /** add an attribute assignment referring to a boolean property to the map + + @param _pAttributeName + the name of the attribute + @param _rPropertyName + the name of the property assigned to the attribute + @param _bAttributeDefault + the default value for the attribute. + @param _bInverseSemantics + if <TRUE/>, an attribute value of <TRUE/> means a property value of <FALSE/> and vice verse.<br/> + if <FALSE/>, the attribute value is used as property value directly + */ + void addBooleanProperty( + sal_Int32 nAttributeToken, const OUString& _rPropertyName, + const bool _bAttributeDefault, const bool _bInverseSemantics = false); + + /** add an attribute assignment referring to an int16 property to the map + + @param _pAttributeName + the name of the attribute + @param _rPropertyName + the name of the property assigned to the attribute + */ + void addInt16Property( + sal_Int32 nAttributeToken, const OUString& _rPropertyName); + + /** add an attribute assignment referring to an int32 property to the map + + @param _pAttributeName + the name of the attribute + @param _rPropertyName + the name of the property assigned to the attribute + */ + void addInt32Property( + sal_Int32 nAttributeToken, const OUString& _rPropertyName ); + + /** add an attribute assignment referring to an enum property to the map + + @param _pAttributeName + the name of the attribute + @param _rPropertyName + the name of the property assigned to the attribute + @param _pValueMap + the map to translate strings into enum values + @param _pType + the type of the property. May be NULL, in this case 32bit integer is assumed. + */ + template<typename EnumT> + void addEnumProperty( + sal_Int32 nAttributeToken, const OUString& _rPropertyName, + const SvXMLEnumMapEntry<EnumT>* _pValueMap, + const css::uno::Type* _pType = nullptr) + { + addEnumPropertyImpl(nAttributeToken, _rPropertyName, + reinterpret_cast<const SvXMLEnumMapEntry<sal_uInt16>*>(_pValueMap), _pType); + } + + private: + void addEnumPropertyImpl( + sal_Int32 nAttributeToken, const OUString& _rPropertyName, + const SvXMLEnumMapEntry<sal_uInt16>* _pValueMap, + const css::uno::Type* _pType); + /// some common code for the various add*Property methods + AttributeAssignment& implAdd( + sal_Int32 nAttributeToken, const OUString& _rPropertyName, + const css::uno::Type& _rType); + }; +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/formcellbinding.cxx b/xmloff/source/forms/formcellbinding.cxx new file mode 100644 index 000000000..e9220fd79 --- /dev/null +++ b/xmloff/source/forms/formcellbinding.cxx @@ -0,0 +1,433 @@ +/* -*- 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 "formcellbinding.hxx" +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include "strings.hxx" +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> + +#include <algorithm> + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::sheet; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::drawing; + using namespace ::com::sun::star::table; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::form::binding; + +namespace +{ + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::container::XChild; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::uno::UNO_QUERY; + + template< class TYPE > + Reference< TYPE > getTypedModelNode( const Reference< XInterface >& _rxModelNode ) + { + Reference< TYPE > xTypedNode( _rxModelNode, UNO_QUERY ); + if ( xTypedNode.is() ) + return xTypedNode; + else + { + Reference< XChild > xChild( _rxModelNode, UNO_QUERY ); + if ( xChild.is() ) + return getTypedModelNode< TYPE >( xChild->getParent() ); + else + return nullptr; + } + } + + Reference< XModel > getDocument( const Reference< XInterface >& _rxModelNode ) + { + return getTypedModelNode< XModel >( _rxModelNode ); + } + + struct StringCompare + { + private: + const OUString & m_sReference; + + public: + explicit StringCompare( const OUString& _rReference ) : m_sReference( _rReference ) { } + + bool operator()( std::u16string_view _rCompare ) + { + return ( _rCompare == m_sReference ); + } + }; +} + +//= FormCellBindingHelper +FormCellBindingHelper::FormCellBindingHelper( const Reference< XPropertySet >& _rxControlModel, const Reference< XModel >& _rxDocument ) + :m_xControlModel( _rxControlModel ) + ,m_xDocument( _rxDocument, UNO_QUERY ) +{ + OSL_ENSURE( m_xControlModel.is(), "FormCellBindingHelper::FormCellBindingHelper: invalid control model!" ); + + if ( !m_xDocument.is() ) + m_xDocument.set(getDocument( m_xControlModel ), css::uno::UNO_QUERY); + OSL_ENSURE( m_xDocument.is(), "FormCellBindingHelper::FormCellBindingHelper: Did not find the spreadsheet document!" ); +} + +bool FormCellBindingHelper::livesInSpreadsheetDocument( const Reference< XPropertySet >& _rxControlModel ) +{ + Reference< XSpreadsheetDocument > xDocument( getDocument( _rxControlModel ), UNO_QUERY ); + return xDocument.is(); +} + +bool FormCellBindingHelper::convertStringAddress( const OUString& _rAddressDescription, CellAddress& /* [out] */ _rAddress ) const +{ + Any aAddress; + return doConvertAddressRepresentations( + PROPERTY_FILE_REPRESENTATION, + Any( _rAddressDescription ), + PROPERTY_ADDRESS, + aAddress, + false + ) + && ( aAddress >>= _rAddress ); +} + +bool FormCellBindingHelper::convertStringAddress( const OUString& _rAddressDescription, + CellRangeAddress& /* [out] */ _rAddress ) const +{ + Any aAddress; + return doConvertAddressRepresentations( + PROPERTY_FILE_REPRESENTATION, + Any( _rAddressDescription ), + PROPERTY_ADDRESS, + aAddress, + true + ) + && ( aAddress >>= _rAddress ); +} + +Reference< XValueBinding > FormCellBindingHelper::createCellBindingFromStringAddress( const OUString& _rAddress, bool _bUseIntegerBinding ) const +{ + Reference< XValueBinding > xBinding; + if ( !m_xDocument.is() ) + // very bad ... + return xBinding; + + // get the UNO representation of the address + CellAddress aAddress; + if ( _rAddress.isEmpty() || !convertStringAddress( _rAddress, aAddress ) ) + return xBinding; + + xBinding.set(createDocumentDependentInstance( + _bUseIntegerBinding ? OUString(SERVICE_LISTINDEXCELLBINDING) : OUString(SERVICE_CELLVALUEBINDING), + PROPERTY_BOUND_CELL, + Any( aAddress ) + ), css::uno::UNO_QUERY); + + return xBinding; +} + +Reference< XListEntrySource > FormCellBindingHelper::createCellListSourceFromStringAddress( const OUString& _rAddress ) const +{ + Reference< XListEntrySource > xSource; + + CellRangeAddress aRangeAddress; + if ( !convertStringAddress( _rAddress, aRangeAddress ) ) + return xSource; + + // create a range object for this address + xSource.set(createDocumentDependentInstance( + SERVICE_CELLRANGELISTSOURCE, + PROPERTY_LIST_CELL_RANGE, + Any( aRangeAddress ) + ), css::uno::UNO_QUERY); + + return xSource; +} + +OUString FormCellBindingHelper::getStringAddressFromCellBinding( const Reference< XValueBinding >& _rxBinding ) const +{ + OSL_PRECOND( !_rxBinding.is() || isCellBinding( _rxBinding ), "FormCellBindingHelper::getStringAddressFromCellBinding: this is no cell binding!" ); + + OUString sAddress; + try + { + Reference< XPropertySet > xBindingProps( _rxBinding, UNO_QUERY ); + OSL_ENSURE( xBindingProps.is() || !_rxBinding.is(), "FormCellBindingHelper::getStringAddressFromCellBinding: no property set for the binding!" ); + if ( xBindingProps.is() ) + { + CellAddress aAddress; + xBindingProps->getPropertyValue( PROPERTY_BOUND_CELL ) >>= aAddress; + + Any aStringAddress; + doConvertAddressRepresentations( PROPERTY_ADDRESS, Any( aAddress ), + PROPERTY_FILE_REPRESENTATION, aStringAddress, false ); + + aStringAddress >>= sAddress; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "xmloff", "FormCellBindingHelper::getStringAddressFromCellBinding" ); + } + + return sAddress; +} + +OUString FormCellBindingHelper::getStringAddressFromCellListSource( const Reference< XListEntrySource >& _rxSource ) const +{ + OSL_PRECOND( !_rxSource.is() || isCellRangeListSource( _rxSource ), "FormCellBindingHelper::getStringAddressFromCellListSource: this is no cell list source!" ); + + OUString sAddress; + try + { + Reference< XPropertySet > xSourceProps( _rxSource, UNO_QUERY ); + OSL_ENSURE( xSourceProps.is() || !_rxSource.is(), "FormCellBindingHelper::getStringAddressFromCellListSource: no property set for the list source!" ); + if ( xSourceProps.is() ) + { + CellRangeAddress aRangeAddress; + xSourceProps->getPropertyValue( PROPERTY_LIST_CELL_RANGE ) >>= aRangeAddress; + + Any aStringAddress; + doConvertAddressRepresentations( PROPERTY_ADDRESS, Any( aRangeAddress ), + PROPERTY_FILE_REPRESENTATION, aStringAddress, true ); + aStringAddress >>= sAddress; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "xmloff", "FormCellBindingHelper::getStringAddressFromCellListSource" ); + } + + return sAddress; +} + +bool FormCellBindingHelper::isSpreadsheetDocumentWhichSupplies( const Reference< XSpreadsheetDocument >& _rxDocument, const OUString& _rService ) +{ + bool bYesItIs = false; + + try + { + Reference< XServiceInfo > xSI( _rxDocument, UNO_QUERY ); + if ( xSI.is() && xSI->supportsService( SERVICE_SPREADSHEET_DOCUMENT ) ) + { + Reference< XMultiServiceFactory > xDocumentFactory( _rxDocument, UNO_QUERY ); + OSL_ENSURE( xDocumentFactory.is(), "FormCellBindingHelper::isSpreadsheetDocumentWhichSupplies: spreadsheet document, but no factory?" ); + + if ( xDocumentFactory.is() ) + { + const Sequence<OUString> aAvailableServices = xDocumentFactory->getAvailableServiceNames( ); + + bYesItIs = std::any_of( aAvailableServices.begin(), aAvailableServices.end(), StringCompare( _rService ) ); + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "xmloff", "FormCellBindingHelper::isSpreadsheetDocumentWhichSupplies" ); + } + + return bYesItIs; +} + +bool FormCellBindingHelper::isSpreadsheetDocumentWhichSupplies( const OUString& _rService ) const +{ + return isSpreadsheetDocumentWhichSupplies( m_xDocument, _rService ); +} + +bool FormCellBindingHelper::isListCellRangeAllowed( const Reference< XModel >& _rxDocument ) +{ + return isSpreadsheetDocumentWhichSupplies( + Reference< XSpreadsheetDocument >( _rxDocument, UNO_QUERY ), + SERVICE_CELLRANGELISTSOURCE + ); +} + +bool FormCellBindingHelper::isListCellRangeAllowed( ) const +{ + bool bAllow( false ); + + Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY ); + if ( xSink.is() ) + { + bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_CELLRANGELISTSOURCE ); + } + + return bAllow; +} + +bool FormCellBindingHelper::isCellBindingAllowed( ) const +{ + bool bAllow( false ); + + Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY ); + if ( xBindable.is() ) + { + // the control can potentially be bound to an external value + // Does it live within a Calc document, and is able to supply CellBindings? + bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_CELLVALUEBINDING ); + } + + return bAllow; +} + +bool FormCellBindingHelper::isCellBindingAllowed( const Reference< XModel >& _rxDocument ) +{ + return isSpreadsheetDocumentWhichSupplies( + Reference< XSpreadsheetDocument >( _rxDocument, UNO_QUERY ), + SERVICE_CELLVALUEBINDING + ); +} + +bool FormCellBindingHelper::isCellBinding( const Reference< XValueBinding >& _rxBinding ) +{ + return doesComponentSupport( _rxBinding, SERVICE_CELLVALUEBINDING ); +} + +bool FormCellBindingHelper::isCellIntegerBinding( const Reference< XValueBinding >& _rxBinding ) +{ + return doesComponentSupport( _rxBinding, SERVICE_LISTINDEXCELLBINDING ); +} + +bool FormCellBindingHelper::isCellRangeListSource( const Reference< XListEntrySource >& _rxSource ) +{ + return doesComponentSupport( _rxSource, SERVICE_CELLRANGELISTSOURCE ); +} + +bool FormCellBindingHelper::doesComponentSupport( const Reference< XInterface >& _rxComponent, const OUString& _rService ) +{ + Reference< XServiceInfo > xSI( _rxComponent, UNO_QUERY ); + bool bDoes = xSI.is() && xSI->supportsService( _rService ); + return bDoes; +} + +Reference< XValueBinding > FormCellBindingHelper::getCurrentBinding( ) const +{ + Reference< XValueBinding > xBinding; + Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY ); + if ( xBindable.is() ) + xBinding = xBindable->getValueBinding(); + return xBinding; +} + +Reference< XListEntrySource > FormCellBindingHelper::getCurrentListSource( ) const +{ + Reference< XListEntrySource > xSource; + Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY ); + if ( xSink.is() ) + xSource = xSink->getListEntrySource(); + return xSource; +} + +void FormCellBindingHelper::setBinding( const Reference< XValueBinding >& _rxBinding ) +{ + Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY ); + OSL_PRECOND( xBindable.is(), "FormCellBindingHelper::setBinding: the object is not bindable!" ); + if ( xBindable.is() ) + xBindable->setValueBinding( _rxBinding ); +} + +void FormCellBindingHelper::setListSource( const Reference< XListEntrySource >& _rxSource ) +{ + Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY ); + OSL_PRECOND( xSink.is(), "FormCellBindingHelper::setListSource: the object is no list entry sink!" ); + if ( xSink.is() ) + xSink->setListEntrySource( _rxSource ); +} + +Reference< XInterface > FormCellBindingHelper::createDocumentDependentInstance( const OUString& _rService, const OUString& _rArgumentName, + const Any& _rArgumentValue ) const +{ + Reference< XInterface > xReturn; + + Reference< XMultiServiceFactory > xDocumentFactory( m_xDocument, UNO_QUERY ); + OSL_ENSURE( xDocumentFactory.is(), "FormCellBindingHelper::createDocumentDependentInstance: no document service factory!" ); + if ( xDocumentFactory.is() ) + { + try + { + if ( !_rArgumentName.isEmpty() ) + { + NamedValue aArg; + aArg.Name = _rArgumentName; + aArg.Value = _rArgumentValue; + + Sequence< Any > aArgs{ Any(aArg) }; + xReturn = xDocumentFactory->createInstanceWithArguments( _rService, aArgs ); + } + else + { + xReturn = xDocumentFactory->createInstance( _rService ); + } + } + catch ( const Exception& ) + { + OSL_FAIL( "FormCellBindingHelper::createDocumentDependentInstance: could not create the binding at the document!" ); + } + } + return xReturn; +} + +bool FormCellBindingHelper::doConvertAddressRepresentations( const OUString& _rInputProperty, const Any& _rInputValue, + const OUString& _rOutputProperty, Any& _rOutputValue, bool _bIsRange ) const +{ + bool bSuccess = false; + + Reference< XPropertySet > xConverter( + createDocumentDependentInstance( + _bIsRange ? OUString(SERVICE_RANGEADDRESS_CONVERSION) : OUString(SERVICE_ADDRESS_CONVERSION), + OUString(), + Any() + ), + UNO_QUERY + ); + OSL_ENSURE( xConverter.is(), "FormCellBindingHelper::doConvertAddressRepresentations: could not get a converter service!" ); + if ( xConverter.is() ) + { + try + { + xConverter->setPropertyValue( _rInputProperty, _rInputValue ); + _rOutputValue = xConverter->getPropertyValue( _rOutputProperty ); + bSuccess = true; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "xmloff", "FormCellBindingHelper::doConvertAddressRepresentations" ); + } + } + + return bSuccess; +} + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/formcellbinding.hxx b/xmloff/source/forms/formcellbinding.hxx new file mode 100644 index 000000000..a05413181 --- /dev/null +++ b/xmloff/source/forms/formcellbinding.hxx @@ -0,0 +1,260 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/table/CellAddress.hpp> +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <com/sun/star/form/binding/XValueBinding.hpp> +#include <com/sun/star/form/binding/XListEntrySource.hpp> +#include <com/sun/star/frame/XModel.hpp> + +namespace xmloff +{ + + //= FormCellBindingHelper + /** encapsulates functionality related to binding a form control to a spreadsheet cell + */ + class FormCellBindingHelper + { + css::uno::Reference< css::beans::XPropertySet > + m_xControlModel; // the model we work for + css::uno::Reference< css::sheet::XSpreadsheetDocument > + m_xDocument; // the document where the model lives + + public: + /** determines whether the given control model lives in a spreadsheet document + <p>If this method returns <FALSE/>, you cannot instantiate a CellBindingHelper with + this model, since then no of its functionality will be available.</p> + */ + static bool livesInSpreadsheetDocument( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel + ); + + /** ctor + @param _rxControlModel + the control model which is or will be bound + @param _rxDocument + the document. If this is <NULL/>, the document will be obtained from the model + itself by walk on up the chain of its ancestors.<br/> + This parameter can be used if the control model is not (yet) part of a document + model. + */ + FormCellBindingHelper( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel, + const css::uno::Reference< css::frame::XModel >& _rxDocument + ); + + /** gets a cell binding for the given address + @precond + isCellBindingAllowed returns <TRUE/> + */ + css::uno::Reference< css::form::binding::XValueBinding > + createCellBindingFromStringAddress( + const OUString& _rAddress, + bool _bUseIntegerBinding + ) const; + + /** gets a cell range list source binding for the given address + */ + css::uno::Reference< css::form::binding::XListEntrySource > + createCellListSourceFromStringAddress( const OUString& _rAddress ) const; + + /** creates a string representation for the given value binding's address + + <p>If the sheet of the bound cell is the same as the sheet which our control belongs + to, then the sheet name is omitted in the resulting string representation.</p> + + @precond + The binding is a valid cell binding, or <NULL/> + @see isCellBinding + */ + OUString getStringAddressFromCellBinding( + const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding + ) const; + + /** creates a string representation for the given list source's range address + + <p>If the sheet of the cell range which acts as list source is the same as the + sheet which our control belongs to, then the sheet name is omitted in the + resulting string representation.</p> + + @precond + The object is a valid cell range list source, or <NULL/> + @see isCellRangeListSource + */ + OUString getStringAddressFromCellListSource( + const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource + ) const; + + /** returns the current binding of our control model, if any. + */ + css::uno::Reference< css::form::binding::XValueBinding > + getCurrentBinding( ) const; + + /** returns the current external list source of the control model, if any + */ + css::uno::Reference< css::form::binding::XListEntrySource > + getCurrentListSource( ) const; + + /** sets a new binding for our control model + @precond + the control model is bindable (which is implied by <member>isCellBindingAllowed</member> + returning <TRUE/>) + */ + void setBinding( + const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding + ); + + /** sets a list source for our control model + @precond + the control model is a list sink (which is implied by <member>isListCellRangeAllowed</member> + returning <TRUE/>) + */ + void setListSource( + const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource + ); + + /** checks whether it's possible to bind the control model to a spreadsheet cell + */ + bool isCellBindingAllowed( ) const; + + /** checks whether within the given document, it's possible to bind control models to spreadsheet cells + */ + static bool isCellBindingAllowed( + const css::uno::Reference< css::frame::XModel >& _rxDocument + ); + + /** checks whether it's possible to bind the control model to a range of spreadsheet cells + supplying the list entries + */ + bool isListCellRangeAllowed( ) const; + + /** checks whether within the given document, it's possible to bind the control model to a range of + spreadsheet cells supplying the list entries + */ + static bool isListCellRangeAllowed( + const css::uno::Reference< css::frame::XModel >& _rxDocument + ); + + /** checks whether a given binding is a spreadsheet cell binding + */ + static bool isCellBinding( + const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding + ); + + /** checks whether a given binding is a spreadsheet cell binding, exchanging + integer values + */ + static bool isCellIntegerBinding( + const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding + ); + + /** checks whether a given list source is a spreadsheet cell list source + */ + static bool isCellRangeListSource( + const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource + ); + + private: + /** creates an address object from a string representation of a cell address + */ + bool convertStringAddress( + const OUString& _rAddressDescription, + css::table::CellAddress& /* [out] */ _rAddress + ) const; + + /** creates an address range object from a string representation of a cell range address + */ + bool convertStringAddress( + const OUString& _rAddressDescription, + css::table::CellRangeAddress& /* [out] */ _rAddress + ) const; + + /** determines if our document is a spreadsheet document, *and* can supply + the given service + */ + bool isSpreadsheetDocumentWhichSupplies( const OUString& _rService ) const; + + /** determines if our document is a spreadsheet document, *and* can supply + the given service + */ + static bool isSpreadsheetDocumentWhichSupplies( + const css::uno::Reference< css::sheet::XSpreadsheetDocument >& _rxDocument, + const OUString& _rService + ); + + /** checks whether a given component supports a given service + */ + static bool doesComponentSupport( + const css::uno::Reference< css::uno::XInterface >& _rxComponent, + const OUString& _rService + ); + + /** uses the document (it's factory interface, respectively) to create a component instance + @param _rService + the service name + @param _rArgumentName + the name of the single argument to pass during creation. May be empty, in this case + no arguments are passed + @param _rArgumentValue + the value of the instantiation argument. Not evaluated if <arg>_rArgumentName</arg> + is empty. + */ + css::uno::Reference< css::uno::XInterface > + createDocumentDependentInstance( + const OUString& _rService, + const OUString& _rArgumentName, + const css::uno::Any& _rArgumentValue + ) const; + + /** converts an address representation into another one + + @param _rInputProperty + the input property name for the conversion service + @param _rInputValue + the input property value for the conversion service + @param _rOutputProperty + the output property name for the conversion service + @param _rOutputValue + the output property value for the conversion service + @param _bIsRange + if <TRUE/>, the RangeAddressConversion service will be used, else + the AddressConversion service + + @return + <TRUE/> if any only if the conversion was successful + + @see css::table::CellAddressConversion + @see css::table::CellRangeAddressConversion + */ + bool doConvertAddressRepresentations( + const OUString& _rInputProperty, + const css::uno::Any& _rInputValue, + const OUString& _rOutputProperty, + css::uno::Any& _rOutputValue, + bool _bIsRange + ) const; + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/formenums.cxx b/xmloff/source/forms/formenums.cxx new file mode 100644 index 000000000..31567ddf7 --- /dev/null +++ b/xmloff/source/forms/formenums.cxx @@ -0,0 +1,191 @@ +/* -*- 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 "formenums.hxx" +#include <com/sun/star/form/FormSubmitEncoding.hpp> +#include <com/sun/star/form/FormSubmitMethod.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/form/NavigationBarMode.hpp> +#include <com/sun/star/form/TabulatorCycle.hpp> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/form/ListSourceType.hpp> +#include <com/sun/star/awt/TextAlign.hpp> +#include <com/sun/star/awt/FontEmphasisMark.hpp> +#include <com/sun/star/awt/FontRelief.hpp> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/awt/ImageScaleMode.hpp> +#include <tools/gen.hxx> +#include <xmloff/xmltoken.hxx> + +namespace xmloff +{ + +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star; +using namespace ::xmloff::token; + +const SvXMLEnumMapEntry<FormSubmitEncoding> aSubmitEncodingMap[] = +{ + { XML_APPLICATION_X_WWW_FORM_URLENCODED, FormSubmitEncoding_URL }, + { XML_MULTIPART_FORMDATA, FormSubmitEncoding_MULTIPART }, + { XML_APPLICATION_TEXT, FormSubmitEncoding_TEXT }, + { XML_TOKEN_INVALID, FormSubmitEncoding(0) } +}; +const SvXMLEnumMapEntry<FormSubmitMethod> aSubmitMethodMap[] = +{ + { XML_GET, FormSubmitMethod_GET }, + { XML_POST, FormSubmitMethod_POST }, + { XML_TOKEN_INVALID, FormSubmitMethod(0) } +}; +const SvXMLEnumMapEntry<sal_Int32> aCommandTypeMap[] = +{ + { XML_TABLE, CommandType::TABLE }, + { XML_QUERY, CommandType::QUERY }, + { XML_COMMAND, CommandType::COMMAND }, + { XML_TOKEN_INVALID, 0 } +}; +const SvXMLEnumMapEntry<NavigationBarMode> aNavigationTypeMap[] = +{ + { XML_NONE, NavigationBarMode_NONE }, + { XML_CURRENT, NavigationBarMode_CURRENT }, + { XML_PARENT, NavigationBarMode_PARENT }, + { XML_TOKEN_INVALID, NavigationBarMode(0) } +}; +const SvXMLEnumMapEntry<TabulatorCycle> aTabulatorCycleMap[] = +{ + { XML_RECORDS, TabulatorCycle_RECORDS }, + { XML_CURRENT, TabulatorCycle_CURRENT }, + { XML_PAGE, TabulatorCycle_PAGE }, + { XML_TOKEN_INVALID, TabulatorCycle(0) } +}; +const SvXMLEnumMapEntry<FormButtonType> aFormButtonTypeMap[] = +{ + { XML_PUSH, FormButtonType_PUSH }, + { XML_SUBMIT, FormButtonType_SUBMIT }, + { XML_RESET, FormButtonType_RESET }, + { XML_URL, FormButtonType_URL }, + { XML_TOKEN_INVALID, FormButtonType(0) } +}; +const SvXMLEnumMapEntry<ListSourceType> aListSourceTypeMap[] = +{ + { XML_VALUE_LIST, ListSourceType_VALUELIST }, + { XML_TABLE, ListSourceType_TABLE }, + { XML_QUERY, ListSourceType_QUERY }, + { XML_SQL, ListSourceType_SQL }, + { XML_SQL_PASS_THROUGH, ListSourceType_SQLPASSTHROUGH }, + { XML_TABLE_FIELDS, ListSourceType_TABLEFIELDS }, + { XML_TOKEN_INVALID, ListSourceType(0) } +}; +// check state of a checkbox +const SvXMLEnumMapEntry<TriState> aCheckStateMap[] = +{ + { XML_UNCHECKED, TRISTATE_FALSE }, + { XML_CHECKED, TRISTATE_TRUE }, + { XML_UNKNOWN, TRISTATE_INDET }, + { XML_TOKEN_INVALID, TriState(0) } +}; +const SvXMLEnumMapEntry<sal_Int16> aTextAlignMap[] = +{ + { XML_START, sal_uInt16(awt::TextAlign::LEFT) }, + { XML_CENTER, sal_uInt16(awt::TextAlign::CENTER) }, + { XML_END, sal_uInt16(awt::TextAlign::RIGHT) }, + { XML_JUSTIFY, -1 }, + { XML_JUSTIFIED, -1 }, + { XML_TOKEN_INVALID, 0 } +}; +const SvXMLEnumMapEntry<sal_uInt16> aBorderTypeMap[] = +{ + { XML_NONE, 0 }, + { XML_HIDDEN, 0 }, + { XML_SOLID, 2 }, + { XML_DOUBLE, 2 }, + { XML_DOTTED, 2 }, + { XML_DASHED, 2 }, + { XML_GROOVE, 1 }, + { XML_RIDGE, 1 }, + { XML_INSET, 1 }, + { XML_OUTSET, 1 }, + { XML_TOKEN_INVALID, 0 } +}; +const SvXMLEnumMapEntry<sal_uInt16> aFontEmphasisMap[] = +{ + { XML_NONE, awt::FontEmphasisMark::NONE }, + { XML_DOT, awt::FontEmphasisMark::DOT }, + { XML_CIRCLE, awt::FontEmphasisMark::CIRCLE }, + { XML_DISC, awt::FontEmphasisMark::DISC }, + { XML_ACCENT, awt::FontEmphasisMark::ACCENT }, + { XML_TOKEN_INVALID, 0 } +}; +const SvXMLEnumMapEntry<sal_uInt16> aFontReliefMap[] = +{ + { XML_NONE, FontRelief::NONE }, + { XML_ENGRAVED, FontRelief::ENGRAVED }, + { XML_EMBOSSED, FontRelief::EMBOSSED }, + { XML_TOKEN_INVALID, 0 } +}; +const SvXMLEnumMapEntry<sal_Int16> aListLinkageMap[] = +{ + { XML_SELECTION, 0 }, + { XML_SELECTION_INDEXES, 1 }, + { XML_TOKEN_INVALID, 0 } +}; +const SvXMLEnumMapEntry<sal_Int32> aOrientationMap[] = +{ + { XML_HORIZONTAL, ScrollBarOrientation::HORIZONTAL }, + { XML_VERTICAL, ScrollBarOrientation::VERTICAL }, + { XML_TOKEN_INVALID, 0 } +}; +const SvXMLEnumMapEntry<sal_Int16> aVisualEffectMap[] = +{ + { XML_NONE, VisualEffect::NONE }, + { XML_3D, VisualEffect::LOOK3D }, + { XML_FLAT, VisualEffect::FLAT }, + { XML_TOKEN_INVALID, 0 } +}; +const SvXMLEnumMapEntry<sal_Int16> aImagePositionMap[] = +{ + { XML_START, 0 }, + { XML_END, 1 }, + { XML_TOP, 2 }, + { XML_BOTTOM, 3 }, + { XML_CENTER, -1 }, + { XML_TOKEN_INVALID, 0 } +}; +const SvXMLEnumMapEntry<sal_uInt16> aImageAlignMap[] = +{ + { XML_START, 0 }, + { XML_CENTER, 1 }, + { XML_END, 2 }, + { XML_TOKEN_INVALID, 0 } +}; +const SvXMLEnumMapEntry<sal_uInt16> aScaleModeMap[] = +{ + { XML_BACKGROUND_NO_REPEAT, ImageScaleMode::NONE }, + { XML_REPEAT, ImageScaleMode::NONE }, // repeating the image is not supported + { XML_STRETCH, ImageScaleMode::ANISOTROPIC }, + { XML_SCALE, ImageScaleMode::ISOTROPIC }, + { XML_TOKEN_INVALID, 0 } +}; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/formenums.hxx b/xmloff/source/forms/formenums.hxx new file mode 100644 index 000000000..d0d20ff8d --- /dev/null +++ b/xmloff/source/forms/formenums.hxx @@ -0,0 +1,54 @@ +/* -*- 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 . + */ + +#pragma once + +#include <xmloff/xmlement.hxx> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/form/FormSubmitEncoding.hpp> +#include <com/sun/star/form/FormSubmitMethod.hpp> +#include <com/sun/star/form/ListSourceType.hpp> +#include <com/sun/star/form/NavigationBarMode.hpp> +#include <com/sun/star/form/TabulatorCycle.hpp> +#include <tools/gen.hxx> + +namespace xmloff +{ +extern const SvXMLEnumMapEntry<css::form::FormSubmitEncoding> aSubmitEncodingMap[]; +extern const SvXMLEnumMapEntry<css::form::FormSubmitMethod> aSubmitMethodMap[]; +extern const SvXMLEnumMapEntry<sal_Int32> aCommandTypeMap[]; +extern const SvXMLEnumMapEntry<css::form::NavigationBarMode> aNavigationTypeMap[]; +extern const SvXMLEnumMapEntry<css::form::TabulatorCycle> aTabulatorCycleMap[]; +extern const SvXMLEnumMapEntry<css::form::FormButtonType> aFormButtonTypeMap[]; +extern const SvXMLEnumMapEntry<css::form::ListSourceType> aListSourceTypeMap[]; +extern const SvXMLEnumMapEntry<TriState> aCheckStateMap[]; +extern const SvXMLEnumMapEntry<sal_Int16> aTextAlignMap[]; +extern const SvXMLEnumMapEntry<sal_uInt16> aBorderTypeMap[]; +extern const SvXMLEnumMapEntry<sal_uInt16> aFontEmphasisMap[]; +extern const SvXMLEnumMapEntry<sal_uInt16> aFontReliefMap[]; +extern const SvXMLEnumMapEntry<sal_Int16> aListLinkageMap[]; +extern const SvXMLEnumMapEntry<sal_Int32> aOrientationMap[]; +extern const SvXMLEnumMapEntry<sal_Int16> aVisualEffectMap[]; +extern const SvXMLEnumMapEntry<sal_Int16> aImagePositionMap[]; +extern const SvXMLEnumMapEntry<sal_uInt16> aImageAlignMap[]; +extern const SvXMLEnumMapEntry<sal_uInt16> aScaleModeMap[]; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/formevents.cxx b/xmloff/source/forms/formevents.cxx new file mode 100644 index 000000000..85e3137cb --- /dev/null +++ b/xmloff/source/forms/formevents.cxx @@ -0,0 +1,70 @@ +/* -*- 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 "formevents.hxx" +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlevent.hxx> + +namespace xmloff +{ + + //= event translation table + const XMLEventNameTranslation aEventTranslations[] = + { + { "XApproveActionListener::approveAction", XML_NAMESPACE_FORM, "approveaction" }, // "on-approveaction" + { "XActionListener::actionPerformed", XML_NAMESPACE_FORM, "performaction" }, // "on-performaction" + { "XChangeListener::changed", XML_NAMESPACE_DOM, "change" }, // "on-change" + { "XTextListener::textChanged", XML_NAMESPACE_FORM, "textchange" }, // "on-textchange" + { "XItemListener::itemStateChanged", XML_NAMESPACE_FORM, "itemstatechange" }, // "on-itemstatechange" + { "XFocusListener::focusGained", XML_NAMESPACE_DOM, "DOMFocusIn" }, // "on-focus" + { "XFocusListener::focusLost", XML_NAMESPACE_DOM, "DOMFocusOut" }, // "on-blur" + { "XKeyListener::keyPressed", XML_NAMESPACE_DOM, "keydown" }, // "on-keydown" + { "XKeyListener::keyReleased", XML_NAMESPACE_DOM, "keyup" }, // "on-keyup" + { "XMouseListener::mouseEntered", XML_NAMESPACE_DOM, "mouseover" }, // "on-mouseover" + { "XMouseMotionListener::mouseDragged", XML_NAMESPACE_FORM, "mousedrag" }, // "on-mousedrag" + { "XMouseMotionListener::mouseMoved", XML_NAMESPACE_DOM, "mousemove" }, // "on-mousemove" + { "XMouseListener::mousePressed", XML_NAMESPACE_DOM, "mousedown" }, // "on-mousedown" + { "XMouseListener::mouseReleased", XML_NAMESPACE_DOM, "mouseup" }, // "on-mouseup" + { "XMouseListener::mouseExited", XML_NAMESPACE_DOM, "mouseout" }, // "on-mouseout" + { "XResetListener::approveReset", XML_NAMESPACE_FORM, "approvereset" }, // "on-approvereset" + { "XResetListener::resetted", XML_NAMESPACE_DOM, "reset" }, // "on-reset" + { "XSubmitListener::approveSubmit", XML_NAMESPACE_DOM, "submit" }, // "on-submit" + { "XUpdateListener::approveUpdate", XML_NAMESPACE_FORM, "approveupdate" }, // "on-approveupdate" + { "XUpdateListener::updated", XML_NAMESPACE_FORM, "update" }, // "on-update" + { "XLoadListener::loaded", XML_NAMESPACE_DOM, "load" }, // "on-load" + { "XLoadListener::reloading", XML_NAMESPACE_FORM, "startreload" }, // "on-startreload" + { "XLoadListener::reloaded", XML_NAMESPACE_FORM, "reload" }, // "on-reload" + { "XLoadListener::unloading", XML_NAMESPACE_FORM, "startunload" }, // "on-startunload" + { "XLoadListener::unloaded", XML_NAMESPACE_DOM, "unload" }, // "on-unload" + { "XConfirmDeleteListener::confirmDelete", XML_NAMESPACE_FORM, "confirmdelete" }, // "on-confirmdelete" + { "XRowSetApproveListener::approveRowChange", XML_NAMESPACE_FORM, "approverowchange" }, // "on-approverowchange" + { "XRowSetListener::rowChanged", XML_NAMESPACE_FORM, "rowchange" }, // "on-rowchange" + { "XRowSetApproveListener::approveCursorMove", XML_NAMESPACE_FORM, "approvecursormove" }, // "on-approvecursormove" + { "XRowSetListener::cursorMoved", XML_NAMESPACE_FORM, "cursormove" }, // "on-cursormove" + { "XDatabaseParameterListener::approveParameter",XML_NAMESPACE_FORM, "supplyparameter" }, // "on-supplyparameter" + { "XSQLErrorListener::errorOccured", XML_NAMESPACE_DOM, "error" }, // "on-error" + { "XAdjustmentListener::adjustmentValueChanged",XML_NAMESPACE_FORM, "adjust" }, // "on-adjust" + { nullptr, 0, nullptr } + }; + + const XMLEventNameTranslation* g_pFormsEventTranslation = aEventTranslations; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/formevents.hxx b/xmloff/source/forms/formevents.hxx new file mode 100644 index 000000000..7077f7522 --- /dev/null +++ b/xmloff/source/forms/formevents.hxx @@ -0,0 +1,30 @@ +/* -*- 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 . + */ + +#pragma once + +struct XMLEventNameTranslation; +namespace xmloff +{ +//= event translation table +extern const XMLEventNameTranslation* g_pFormsEventTranslation; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/formlayerexport.cxx b/xmloff/source/forms/formlayerexport.cxx new file mode 100644 index 000000000..661e83630 --- /dev/null +++ b/xmloff/source/forms/formlayerexport.cxx @@ -0,0 +1,121 @@ +/* -*- 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 <xmloff/formlayerexport.hxx> +#include <xmloff/xmlexp.hxx> +#include "layerexport.hxx" +#include <osl/diagnose.h> +#include "officeforms.hxx" + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::drawing; + + //= OFormLayerXMLExport + + OFormLayerXMLExport::OFormLayerXMLExport(SvXMLExport& _rContext) + :m_pImpl(new OFormLayerXMLExport_Impl(_rContext)) + { + } + + OFormLayerXMLExport::~OFormLayerXMLExport() + { + } + + bool OFormLayerXMLExport::seekPage(const Reference< XDrawPage >& _rxDrawPage) + { + return m_pImpl->seekPage(_rxDrawPage); + } + + OUString OFormLayerXMLExport::getControlId(const Reference< XPropertySet >& _rxControl) + { + return m_pImpl->getControlId(_rxControl); + } + + OUString OFormLayerXMLExport::getControlNumberStyle( const Reference< XPropertySet >& _rxControl ) + { + return m_pImpl->getControlNumberStyle(_rxControl); + } + + void OFormLayerXMLExport::examineForms(const Reference< XDrawPage >& _rxDrawPage) + { + try + { + m_pImpl->examineForms(_rxDrawPage); + } + catch(Exception&) + { + OSL_FAIL("OFormLayerXMLExport::examine: could not examine the draw page!"); + } + } + + void OFormLayerXMLExport::exportForms(const Reference< XDrawPage >& _rxDrawPage) + { + m_pImpl->exportForms(_rxDrawPage); + } + + void OFormLayerXMLExport::exportXForms() const + { + m_pImpl->exportXForms(); + } + + bool OFormLayerXMLExport::pageContainsForms( const Reference< XDrawPage >& _rxDrawPage ) + { + return OFormLayerXMLExport_Impl::pageContainsForms( _rxDrawPage ); + } + + bool OFormLayerXMLExport::documentContainsXForms() const + { + return m_pImpl->documentContainsXForms(); + } + + void OFormLayerXMLExport::exportAutoControlNumberStyles() + { + m_pImpl->exportAutoControlNumberStyles(); + } + + void OFormLayerXMLExport::exportAutoStyles() + { + m_pImpl->exportAutoStyles(); + } + + void OFormLayerXMLExport::excludeFromExport( const Reference< XControlModel >& _rxControl ) + { + m_pImpl->excludeFromExport( _rxControl ); + } + + //= OOfficeFormsExport + OOfficeFormsExport::OOfficeFormsExport( SvXMLExport& _rExp ) + :m_pImpl( new OFormsRootExport(_rExp) ) + { + } + + OOfficeFormsExport::~OOfficeFormsExport() + { + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/formlayerimport.cxx b/xmloff/source/forms/formlayerimport.cxx new file mode 100644 index 000000000..400466bfd --- /dev/null +++ b/xmloff/source/forms/formlayerimport.cxx @@ -0,0 +1,90 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <xmloff/formlayerimport.hxx> +#include "layerimport.hxx" + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::drawing; + using namespace ::com::sun::star; + + OFormLayerXMLImport::OFormLayerXMLImport(SvXMLImport& _rImporter) + : m_pImpl( new OFormLayerXMLImport_Impl(_rImporter) ) + { + } + + OFormLayerXMLImport::~OFormLayerXMLImport() + { + } + + void OFormLayerXMLImport::setAutoStyleContext(SvXMLStylesContext* _pNewContext) + { + m_pImpl->setAutoStyleContext(_pNewContext); + } + + void OFormLayerXMLImport::startPage(const Reference< XDrawPage >& _rxDrawPage) + { + m_pImpl->startPage(_rxDrawPage); + } + + void OFormLayerXMLImport::endPage() + { + m_pImpl->endPage(); + } + + Reference< XPropertySet > OFormLayerXMLImport::lookupControl(const OUString& _rId) + { + return m_pImpl->lookupControlId(_rId); + } + + SvXMLImportContext* OFormLayerXMLImport::createOfficeFormsContext( + SvXMLImport& _rImport) + { + return OFormLayerXMLImport_Impl::createOfficeFormsContext(_rImport); + } + + SvXMLImportContext* OFormLayerXMLImport::createContext(sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList >& _rxAttribs) + { + return m_pImpl->createContext(nElement, _rxAttribs); + } + + void OFormLayerXMLImport::applyControlNumberStyle(const Reference< XPropertySet >& _rxControlModel, const OUString& _rControlNumberStyleName) + { + m_pImpl->applyControlNumberStyle(_rxControlModel, _rControlNumberStyleName); + } + + void OFormLayerXMLImport::documentDone( ) + { + m_pImpl->documentDone( ); + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/gridcolumnproptranslator.cxx b/xmloff/source/forms/gridcolumnproptranslator.cxx new file mode 100644 index 000000000..aeb1de438 --- /dev/null +++ b/xmloff/source/forms/gridcolumnproptranslator.cxx @@ -0,0 +1,299 @@ +/* -*- 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 "gridcolumnproptranslator.hxx" + +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/awt/TextAlign.hpp> +#include <com/sun/star/style/ParagraphAdjust.hpp> +#include <osl/diagnose.h> +#include <cppuhelper/implbase1.hxx> + +#include <algorithm> + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::style; + + namespace + { + OUString getParaAlignProperty() + { + return "ParaAdjust"; + } + + OUString getAlignProperty() + { + return "Align"; + } + + sal_Int32 findStringElement( const Sequence< OUString >& _rNames, const OUString& _rName ) + { + const OUString* pPos = ::std::find( _rNames.begin(), _rNames.end(), _rName ); + if ( pPos != _rNames.end() ) + return pPos - _rNames.begin(); + return -1; + } + + struct AlignmentTranslationEntry + { + ParagraphAdjust nParagraphValue; + sal_Int16 nControlValue; + } + const AlignmentTranslations[] = + { + // note that order matters: + // valueAlignToParaAdjust and valueParaAdjustToAlign search this map from the _beginning_ + // and use the first matching entry + { ParagraphAdjust_LEFT, awt::TextAlign::LEFT }, + { ParagraphAdjust_CENTER, awt::TextAlign::CENTER }, + { ParagraphAdjust_RIGHT, awt::TextAlign::RIGHT }, + { ParagraphAdjust_BLOCK, awt::TextAlign::RIGHT }, + { ParagraphAdjust_STRETCH, awt::TextAlign::LEFT }, + { ParagraphAdjust::ParagraphAdjust_MAKE_FIXED_SIZE, awt::TextAlign::LEFT }, + { ParagraphAdjust::ParagraphAdjust_MAKE_FIXED_SIZE, -1 } + }; + + void valueAlignToParaAdjust(Any& rValue) + { + sal_Int16 nValue = 0; + rValue >>= nValue; + const AlignmentTranslationEntry* pTranslation = AlignmentTranslations; + while (-1 != pTranslation->nControlValue) + { + if ( nValue == pTranslation->nControlValue ) + { + rValue <<= pTranslation->nParagraphValue; + return; + } + ++pTranslation; + } + OSL_FAIL( "valueAlignToParaAdjust: unreachable!" ); + } + + void valueParaAdjustToAlign(Any& rValue) + { + sal_Int32 nValue = 0; + rValue >>= nValue; + const AlignmentTranslationEntry* pTranslation = AlignmentTranslations; + while ( ParagraphAdjust::ParagraphAdjust_MAKE_FIXED_SIZE != pTranslation->nParagraphValue) + { + if ( static_cast<ParagraphAdjust>(nValue) == pTranslation->nParagraphValue) + { + rValue <<= pTranslation->nControlValue; + return; + } + ++pTranslation; + } + OSL_FAIL( "valueParaAdjustToAlign: unreachable!" ); + } + + //= OMergedPropertySetInfo + typedef ::cppu::WeakAggImplHelper1 < XPropertySetInfo + > OMergedPropertySetInfo_Base; + class OMergedPropertySetInfo : public OMergedPropertySetInfo_Base + { + private: + Reference< XPropertySetInfo > m_xMasterInfo; + + public: + explicit OMergedPropertySetInfo( const Reference< XPropertySetInfo >& _rxMasterInfo ); + + protected: + virtual ~OMergedPropertySetInfo() override; + + // XPropertySetInfo + virtual css::uno::Sequence< css::beans::Property > SAL_CALL getProperties( ) override; + virtual css::beans::Property SAL_CALL getPropertyByName( const OUString& aName ) override; + virtual sal_Bool SAL_CALL hasPropertyByName( const OUString& Name ) override; + }; + + OMergedPropertySetInfo::OMergedPropertySetInfo( const Reference< XPropertySetInfo >& _rxMasterInfo ) + :m_xMasterInfo( _rxMasterInfo ) + { + OSL_ENSURE( m_xMasterInfo.is(), "OMergedPropertySetInfo::OMergedPropertySetInfo: hmm?" ); + } + + OMergedPropertySetInfo::~OMergedPropertySetInfo() + { + } + + Sequence< Property > SAL_CALL OMergedPropertySetInfo::getProperties( ) + { + // add a "ParaAdjust" property to the master properties + Sequence< Property > aProperties; + if ( m_xMasterInfo.is() ) + aProperties = m_xMasterInfo->getProperties(); + + sal_Int32 nOldLength = aProperties.getLength(); + aProperties.realloc( nOldLength + 1 ); + aProperties.getArray()[ nOldLength ] = getPropertyByName( getParaAlignProperty() ); + + return aProperties; + } + + Property SAL_CALL OMergedPropertySetInfo::getPropertyByName( const OUString& aName ) + { + if ( aName == getParaAlignProperty() ) + return Property( getParaAlignProperty(), -1, + ::cppu::UnoType<ParagraphAdjust>::get(), 0 ); + + if ( !m_xMasterInfo.is() ) + return Property(); + + return m_xMasterInfo->getPropertyByName( aName ); + } + + sal_Bool SAL_CALL OMergedPropertySetInfo::hasPropertyByName( const OUString& Name ) + { + if ( Name == getParaAlignProperty() ) + return true; + + if ( !m_xMasterInfo.is() ) + return false; + + return m_xMasterInfo->hasPropertyByName( Name ); + } + } + + //= OGridColumnPropertyTranslator + OGridColumnPropertyTranslator::OGridColumnPropertyTranslator( const Reference< XMultiPropertySet >& _rxGridColumn ) + :m_xGridColumn( _rxGridColumn ) + { + OSL_ENSURE( m_xGridColumn.is(), "OGridColumnPropertyTranslator: invalid grid column!" ); + } + + OGridColumnPropertyTranslator::~OGridColumnPropertyTranslator() + { + } + + Reference< XPropertySetInfo > SAL_CALL OGridColumnPropertyTranslator::getPropertySetInfo( ) + { + Reference< XPropertySetInfo > xColumnPropInfo; + if ( m_xGridColumn.is() ) + xColumnPropInfo = m_xGridColumn->getPropertySetInfo(); + return new OMergedPropertySetInfo( xColumnPropInfo ); + } + + void SAL_CALL OGridColumnPropertyTranslator::setPropertyValue( const OUString& _rPropertyName, const Any& aValue ) + { + // we implement this by delegating it to setPropertyValues, which is to ignore unknown properties. On the other hand, our + // contract requires us to throw a UnknownPropertyException for unknown properties, so check this first. + + if ( !getPropertySetInfo()->hasPropertyByName( _rPropertyName ) ) + throw UnknownPropertyException( _rPropertyName, *this ); + + Sequence< OUString > aNames( &_rPropertyName, 1 ); + Sequence< Any > aValues( &aValue, 1 ); + setPropertyValues( aNames, aValues ); + } + + Any SAL_CALL OGridColumnPropertyTranslator::getPropertyValue( const OUString& PropertyName ) + { + Sequence< OUString > aNames( &PropertyName, 1 ); + Sequence< Any > aValues = getPropertyValues( aNames ); + OSL_ENSURE( aValues.getLength() == 1, "OGridColumnPropertyTranslator::getPropertyValue: nonsense!" ); + if ( aValues.getLength() == 1 ) + return aValues[0]; + return Any(); + } + + void SAL_CALL OGridColumnPropertyTranslator::addPropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& ) + { + OSL_FAIL( "OGridColumnPropertyTranslator::addPropertyChangeListener: not implemented - this should not be needed!" ); + } + + void SAL_CALL OGridColumnPropertyTranslator::removePropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& ) + { + OSL_FAIL( "OGridColumnPropertyTranslator::removePropertyChangeListener: not implemented - this should not be needed!" ); + } + + void SAL_CALL OGridColumnPropertyTranslator::addVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& ) + { + OSL_FAIL( "OGridColumnPropertyTranslator::addVetoableChangeListener: not implemented - this should not be needed!" ); + } + + void SAL_CALL OGridColumnPropertyTranslator::removeVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& ) + { + OSL_FAIL( "OGridColumnPropertyTranslator::removeVetoableChangeListener: not implemented - this should not be needed!" ); + } + + void SAL_CALL OGridColumnPropertyTranslator::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues ) + { + if ( !m_xGridColumn.is() ) + return; + + // if there's ever the need for more than one property being translated, then we should + // certainly have a more clever implementation than this ... + + Sequence< OUString > aTranslatedNames( aPropertyNames ); + Sequence< Any > aTranslatedValues( aValues ); + + sal_Int32 nParaAlignPos = findStringElement( aTranslatedNames, getParaAlignProperty() ); + if ( nParaAlignPos != -1 ) + { + aTranslatedNames.getArray()[ nParaAlignPos ] = getAlignProperty(); + valueParaAdjustToAlign( aTranslatedValues.getArray()[ nParaAlignPos ] ); + } + + m_xGridColumn->setPropertyValues( aTranslatedNames, aTranslatedValues ); + } + + Sequence< Any > SAL_CALL OGridColumnPropertyTranslator::getPropertyValues( const Sequence< OUString >& aPropertyNames ) + { + Sequence< Any > aValues( aPropertyNames.getLength() ); + if ( !m_xGridColumn.is() ) + return aValues; + + Sequence< OUString > aTranslatedNames( aPropertyNames ); + sal_Int32 nAlignPos = findStringElement( aTranslatedNames, getParaAlignProperty() ); + if ( nAlignPos != -1 ) + aTranslatedNames.getArray()[ nAlignPos ] = getAlignProperty(); + + aValues = m_xGridColumn->getPropertyValues( aPropertyNames ); + if ( nAlignPos != -1 ) + valueAlignToParaAdjust( aValues.getArray()[ nAlignPos ] ); + + return aValues; + } + + void SAL_CALL OGridColumnPropertyTranslator::addPropertiesChangeListener( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& ) + { + OSL_FAIL( "OGridColumnPropertyTranslator::addPropertiesChangeListener: not implemented - this should not be needed!" ); + } + + void SAL_CALL OGridColumnPropertyTranslator::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& ) + { + OSL_FAIL( "OGridColumnPropertyTranslator::removePropertiesChangeListener: not implemented - this should not be needed!" ); + } + + void SAL_CALL OGridColumnPropertyTranslator::firePropertiesChangeEvent( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& ) + { + OSL_FAIL( "OGridColumnPropertyTranslator::firePropertiesChangeEvent: not implemented - this should not be needed!" ); + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/gridcolumnproptranslator.hxx b/xmloff/source/forms/gridcolumnproptranslator.hxx new file mode 100644 index 000000000..9a49cd9c3 --- /dev/null +++ b/xmloff/source/forms/gridcolumnproptranslator.hxx @@ -0,0 +1,66 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/beans/XMultiPropertySet.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <cppuhelper/implbase.hxx> + +namespace xmloff +{ + + //= OGridColumnPropertyTranslator + typedef ::cppu::WeakImplHelper < css::beans::XPropertySet + , css::beans::XMultiPropertySet + > OGridColumnPropertyTranslator_Base; + class OGridColumnPropertyTranslator : public OGridColumnPropertyTranslator_Base + { + private: + css::uno::Reference< css::beans::XMultiPropertySet > + m_xGridColumn; + + public: + explicit OGridColumnPropertyTranslator( + const css::uno::Reference< css::beans::XMultiPropertySet >& _rxGridColumn + ); + + protected: + virtual ~OGridColumnPropertyTranslator() override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XMultiPropertySet + virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override; + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames ) override; + virtual void SAL_CALL addPropertiesChangeListener( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertiesChangeListener( const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + virtual void SAL_CALL firePropertiesChangeEvent( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/handler/form_handler_factory.cxx b/xmloff/source/forms/handler/form_handler_factory.cxx new file mode 100644 index 000000000..2a943aa82 --- /dev/null +++ b/xmloff/source/forms/handler/form_handler_factory.cxx @@ -0,0 +1,65 @@ +/* -*- 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 <forms/form_handler_factory.hxx> +#include "vcl_date_handler.hxx" +#include "vcl_time_handler.hxx" +#include <osl/diagnose.h> + +namespace xmloff +{ + + //= FormHandlerFactory + PPropertyHandler FormHandlerFactory::getFormPropertyHandler( const PropertyId i_propertyId ) + { + PPropertyHandler pHandler; + + switch ( i_propertyId ) + { + case PID_DATE_MIN: + case PID_DATE_MAX: + case PID_DEFAULT_DATE: + case PID_DATE: + { + static PPropertyHandler s_pVCLDateHandler = new VCLDateHandler(); + pHandler = s_pVCLDateHandler; + } + break; + + case PID_TIME_MIN: + case PID_TIME_MAX: + case PID_DEFAULT_TIME: + case PID_TIME: + { + static PPropertyHandler s_pVCLTimeHandler = new VCLTimeHandler(); + pHandler = s_pVCLTimeHandler; + } + break; + + default: + OSL_ENSURE( false, "FormHandlerFactory::getFormPropertyHandler: unknown property ID!" ); + break; + } + + return pHandler; + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/handler/vcl_date_handler.cxx b/xmloff/source/forms/handler/vcl_date_handler.cxx new file mode 100644 index 000000000..1dfaadd61 --- /dev/null +++ b/xmloff/source/forms/handler/vcl_date_handler.cxx @@ -0,0 +1,93 @@ +/* -*- 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 "vcl_date_handler.hxx" + +#include <rtl/ustrbuf.hxx> + +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/Date.hpp> + +#include <sax/tools/converter.hxx> + +#include <osl/diagnose.h> +#include <tools/date.hxx> + +namespace xmloff +{ + + using ::com::sun::star::uno::Any; + using ::com::sun::star::util::DateTime; + using ::com::sun::star::util::Date; + + //= VCLDateHandler + VCLDateHandler::VCLDateHandler() + { + } + + OUString VCLDateHandler::getAttributeValue( const Any& i_propertyValue ) const + { + Date aDate; + OSL_VERIFY( i_propertyValue >>= aDate ); + + DateTime aDateTime; // default-inited to 0 + aDateTime.Day = aDate.Day; + aDateTime.Month = aDate.Month; + aDateTime.Year = aDate.Year; + + OUStringBuffer aBuffer; + ::sax::Converter::convertDateTime( aBuffer, aDateTime, nullptr ); + return aBuffer.makeStringAndClear(); + } + + bool VCLDateHandler::getPropertyValues( const OUString& i_attributeValue, PropertyValues& o_propertyValues ) const + { + DateTime aDateTime; + Date aDate; + if (::sax::Converter::parseDateTime( aDateTime, i_attributeValue )) + { + aDate.Day = aDateTime.Day; + aDate.Month = aDateTime.Month; + aDate.Year = aDateTime.Year; + } + else + { + // compatibility format, before we wrote those values in XML-schema compatible form + sal_Int32 nVCLDate(0); + if (!::sax::Converter::convertNumber(nVCLDate, i_attributeValue)) + { + OSL_ENSURE( false, "VCLDateHandler::getPropertyValues: unknown date format (no XML-schema date, no legacy integer)!" ); + return false; + } + aDate = ::Date(nVCLDate).GetUNODate(); + } + + const Any aPropertyValue( aDate ); + + OSL_ENSURE( o_propertyValues.size() == 1, "VCLDateHandler::getPropertyValues: date strings represent exactly one property - not more, not less!" ); + for ( auto& prop : o_propertyValues ) + { + prop.second = aPropertyValue; + } + return true; + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/handler/vcl_date_handler.hxx b/xmloff/source/forms/handler/vcl_date_handler.hxx new file mode 100644 index 000000000..626ca4c77 --- /dev/null +++ b/xmloff/source/forms/handler/vcl_date_handler.hxx @@ -0,0 +1,40 @@ +/* -*- 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 . + */ + +#pragma once + +#include <forms/property_handler.hxx> + +namespace xmloff +{ + + //= VCLDateHandler + class VCLDateHandler : public PropertyHandlerBase + { + public: + VCLDateHandler(); + + // IPropertyHandler + virtual OUString getAttributeValue( const css::uno::Any& i_propertyValue ) const override; + virtual bool getPropertyValues( const OUString& i_attributeValue, PropertyValues& o_propertyValues ) const override; + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/handler/vcl_time_handler.cxx b/xmloff/source/forms/handler/vcl_time_handler.cxx new file mode 100644 index 000000000..6a8c2cba1 --- /dev/null +++ b/xmloff/source/forms/handler/vcl_time_handler.cxx @@ -0,0 +1,96 @@ +/* -*- 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 "vcl_time_handler.hxx" + +#include <rtl/ustrbuf.hxx> + +#include <com/sun/star/util/Duration.hpp> +#include <com/sun/star/util/Time.hpp> + +#include <sax/tools/converter.hxx> + +#include <osl/diagnose.h> +#include <tools/time.hxx> + +namespace xmloff +{ + + using ::com::sun::star::uno::Any; + using ::com::sun::star::util::Duration; + using ::com::sun::star::util::Time; + + //= VCLTimeHandler + VCLTimeHandler::VCLTimeHandler() + { + } + + OUString VCLTimeHandler::getAttributeValue( const Any& i_propertyValue ) const + { + css::util::Time aTime; + OSL_VERIFY( i_propertyValue >>= aTime ); + + Duration aDuration; // default-inited to 0 + aDuration.Hours = aTime.Hours; + aDuration.Minutes = aTime.Minutes; + aDuration.Seconds = aTime.Seconds; + aDuration.NanoSeconds = aTime.NanoSeconds; + + OUStringBuffer aBuffer; + ::sax::Converter::convertDuration( aBuffer, aDuration ); + return aBuffer.makeStringAndClear(); + } + + bool VCLTimeHandler::getPropertyValues( const OUString& i_attributeValue, PropertyValues& o_propertyValues ) const + { + Duration aDuration; + css::util::Time aTime; + if (::sax::Converter::convertDuration( aDuration, i_attributeValue )) + { + aTime = Time(aDuration.NanoSeconds, aDuration.Seconds, + aDuration.Minutes, aDuration.Hours, + false); + } + else + { + // compatibility format, before we wrote those values in XML-schema compatible form + sal_Int64 nVCLTime(0); + if (!::sax::Converter::convertNumber64(nVCLTime, i_attributeValue)) + { + OSL_ENSURE( false, "VCLTimeHandler::getPropertyValues: unknown time format (no XML-schema time, no legacy integer)!" ); + return false; + } + // legacy integer was in centiseconds + nVCLTime *= ::tools::Time::nanoPerCenti; + aTime = ::tools::Time(nVCLTime).GetUNOTime(); + } + + const Any aPropertyValue( aTime ); + + OSL_ENSURE( o_propertyValues.size() == 1, "VCLTimeHandler::getPropertyValues: time strings represent exactly one property - not more, not less!" ); + for ( auto& prop : o_propertyValues ) + { + prop.second = aPropertyValue; + } + return true; + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/handler/vcl_time_handler.hxx b/xmloff/source/forms/handler/vcl_time_handler.hxx new file mode 100644 index 000000000..a5b3b14a9 --- /dev/null +++ b/xmloff/source/forms/handler/vcl_time_handler.hxx @@ -0,0 +1,40 @@ +/* -*- 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 . + */ + +#pragma once + +#include <forms/property_handler.hxx> + +namespace xmloff +{ + + //= VCLTimeHandler + class VCLTimeHandler : public PropertyHandlerBase + { + public: + VCLTimeHandler(); + + // IPropertyHandler + virtual OUString getAttributeValue( const css::uno::Any& i_propertyValue ) const override; + virtual bool getPropertyValues( const OUString& i_attributeValue, PropertyValues& o_propertyValues ) const override; + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/layerexport.cxx b/xmloff/source/forms/layerexport.cxx new file mode 100644 index 000000000..87c3b5b40 --- /dev/null +++ b/xmloff/source/forms/layerexport.cxx @@ -0,0 +1,727 @@ +/* -*- 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 "layerexport.hxx" +#include "strings.hxx" +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmlprmap.hxx> +#include <xmloff/prhdlfac.hxx> +#include "elementexport.hxx" +#include <xmloff/families.hxx> +#include <xmloff/contextid.hxx> +#include <xmloff/controlpropertyhdl.hxx> +#include <xmloff/maptype.hxx> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include "controlpropertymap.hxx" +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/form/XFormsSupplier2.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/xforms/XFormsSupplier.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/script/XEventAttacherManager.hpp> +#include <com/sun/star/util/NumberFormatsSupplier.hpp> +#include <xmloff/XMLEventExport.hxx> +#include "formevents.hxx" +#include <xmloff/xmlnumfe.hxx> +#include <xmloff/xformsexport.hxx> + +#include <com/sun/star/text/XText.hpp> + +#include <stack> +#include <numeric> + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::drawing; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::script; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::text; + + //= OFormLayerXMLExport_Impl + const OUString& OFormLayerXMLExport_Impl::getControlNumberStyleNamePrefix() + { + static const OUString s_sControlNumberStyleNamePrefix("C"); + return s_sControlNumberStyleNamePrefix; + } + + OFormLayerXMLExport_Impl::OFormLayerXMLExport_Impl(SvXMLExport& _rContext) + :m_rContext(_rContext) + ,m_pControlNumberStyles(nullptr) + { + initializePropertyMaps(); + + // add our style family to the export context's style pool + m_xPropertyHandlerFactory = new OControlPropertyHandlerFactory(); + ::rtl::Reference< XMLPropertySetMapper > xStylePropertiesMapper = new XMLPropertySetMapper( getControlStylePropertyMap(), m_xPropertyHandlerFactory, true ); + m_xStyleExportMapper = new OFormComponentStyleExportMapper( xStylePropertiesMapper ); + + // our style family + m_rContext.GetAutoStylePool()->AddFamily( + XmlStyleFamily::CONTROL_ID, token::GetXMLToken(token::XML_PARAGRAPH), + m_xStyleExportMapper.get(), + OUString( XML_STYLE_FAMILY_CONTROL_PREFIX ) + ); + + // add our event translation table + m_rContext.GetEventExport().AddTranslationTable(g_pFormsEventTranslation); + + clear(); + } + + OFormLayerXMLExport_Impl::~OFormLayerXMLExport_Impl() + { + } + + bool OFormLayerXMLExport_Impl::impl_isFormPageContainingForms(const Reference< XDrawPage >& _rxDrawPage, Reference< XIndexAccess >& _rxForms) + { + Reference< XFormsSupplier2 > xFormsSupp(_rxDrawPage, UNO_QUERY); + OSL_ENSURE(xFormsSupp.is(), "OFormLayerXMLExport_Impl::impl_isFormPageContainingForms: invalid draw page (no XFormsSupplier)! Doin' nothing!"); + if (!xFormsSupp.is()) + return false; + + if ( !xFormsSupp->hasForms() ) + // nothing to do at all + return false; + + _rxForms.set(xFormsSupp->getForms(), UNO_QUERY); + Reference< XServiceInfo > xSI(_rxForms, UNO_QUERY); // order is important! + OSL_ENSURE(xSI.is(), "OFormLayerXMLExport_Impl::impl_isFormPageContainingForms: invalid collection (must not be NULL and must have a ServiceInfo)!"); + if (!xSI.is()) + return false; + + if (!xSI->supportsService("com.sun.star.form.Forms")) + { + OSL_FAIL("OFormLayerXMLExport_Impl::impl_isFormPageContainingForms: invalid collection (is no com.sun.star.form.Forms)!"); + // nothing to do + return false; + } + return true; + } + + void OFormLayerXMLExport_Impl::exportGridColumn(const Reference< XPropertySet >& _rxColumn, + const Sequence< ScriptEventDescriptor >& _rEvents) + { + // do the exporting + OColumnExport aExportImpl(*this, _rxColumn, getControlId( _rxColumn ), _rEvents); + aExportImpl.doExport(); + } + + void OFormLayerXMLExport_Impl::exportControl(const Reference< XPropertySet >& _rxControl, + const Sequence< ScriptEventDescriptor >& _rEvents) + { + // the list of the referring controls + OUString sReferringControls; + MapPropertySet2String::const_iterator aReferring = m_aCurrentPageReferring->second.find(_rxControl); + if (aReferring != m_aCurrentPageReferring->second.end()) + sReferringControls = aReferring->second; + + // the control id (should already have been created in examineForms) + OUString sControlId( getControlId( _rxControl ) ); + + // do the exporting + OControlExport aExportImpl(*this, _rxControl, sControlId, sReferringControls, _rEvents); + aExportImpl.doExport(); + } + + void OFormLayerXMLExport_Impl::exportForm(const Reference< XPropertySet >& _rxProps, + const Sequence< ScriptEventDescriptor >& _rEvents) + { + OSL_ENSURE(_rxProps.is(), "OFormLayerXMLExport_Impl::exportForm: invalid property set!"); + OFormExport aAttributeHandler(*this, _rxProps, _rEvents); + aAttributeHandler.doExport(); + } + + ::rtl::Reference< SvXMLExportPropertyMapper > OFormLayerXMLExport_Impl::getStylePropertyMapper() + { + return m_xStyleExportMapper; + } + + SvXMLExport& OFormLayerXMLExport_Impl::getGlobalContext() + { + return m_rContext; + } + + void OFormLayerXMLExport_Impl::exportCollectionElements(const Reference< XIndexAccess >& _rxCollection) + { + // step through all the elements of the collection + sal_Int32 nElements = _rxCollection->getCount(); + + Reference< XEventAttacherManager > xElementEventManager(_rxCollection, UNO_QUERY); + Sequence< ScriptEventDescriptor > aElementEvents; + + Reference< XPropertySetInfo > xPropsInfo; + for (sal_Int32 i=0; i<nElements; ++i) + { + try + { + // extract the current element + Reference< XPropertySet > xCurrentProps( _rxCollection->getByIndex(i), UNO_QUERY ); + OSL_ENSURE(xCurrentProps.is(), "OFormLayerXMLExport_Impl::exportCollectionElements: invalid child element, skipping!"); + if (!xCurrentProps.is()) + continue; + + // check if there is a ClassId property on the current element. If so, we assume it to be a control + xPropsInfo = xCurrentProps->getPropertySetInfo(); + OSL_ENSURE(xPropsInfo.is(), "OFormLayerXMLExport_Impl::exportCollectionElements: no property set info!"); + if (!xPropsInfo.is()) + // without this, a lot of stuff in the export routines may fail + continue; + + // if the element is part of an ignore list, we are not allowed to export it + if ( m_aIgnoreList.end() != m_aIgnoreList.find( xCurrentProps ) ) + continue; + + if (xElementEventManager.is()) + aElementEvents = xElementEventManager->getScriptEvents(i); + + if (xPropsInfo->hasPropertyByName(PROPERTY_COLUMNSERVICENAME)) + { + exportGridColumn(xCurrentProps, aElementEvents); + } + else if (xPropsInfo->hasPropertyByName(PROPERTY_CLASSID)) + { + exportControl(xCurrentProps, aElementEvents); + } + else + { + exportForm(xCurrentProps, aElementEvents); + } + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.forms", + "caught an exception ... skipping the current element!"); + continue; + } + } + } + + OUString OFormLayerXMLExport_Impl::getObjectStyleName( const Reference< XPropertySet >& _rxObject ) + { + OUString aObjectStyle; + + MapPropertySet2String::const_iterator aObjectStylePos = m_aGridColumnStyles.find( _rxObject ); + if ( m_aGridColumnStyles.end() != aObjectStylePos ) + aObjectStyle = aObjectStylePos->second; + return aObjectStyle; + } + + void OFormLayerXMLExport_Impl::clear() + { + m_aControlIds.clear(); + m_aReferringControls.clear(); + m_aCurrentPageIds = m_aControlIds.end(); + m_aCurrentPageReferring = m_aReferringControls.end(); + + m_aControlNumberFormats.clear(); + m_aGridColumnStyles.clear(); + + m_aIgnoreList.clear(); + } + + void OFormLayerXMLExport_Impl::exportAutoControlNumberStyles() + { + if ( m_pControlNumberStyles ) + m_pControlNumberStyles->Export( true ); + } + + void OFormLayerXMLExport_Impl::exportAutoStyles() + { + m_rContext.GetAutoStylePool()->exportXML( XmlStyleFamily::CONTROL_ID ); + } + + void OFormLayerXMLExport_Impl::exportForms(const Reference< XDrawPage >& _rxDrawPage) + { + // get the forms collection of the page + Reference< XIndexAccess > xCollectionIndex; + if (!impl_isFormPageContainingForms(_rxDrawPage, xCollectionIndex)) + { + return; + } + + bool bPageIsKnown = implMoveIterators(_rxDrawPage, false); + OSL_ENSURE(bPageIsKnown, "OFormLayerXMLExport_Impl::exportForms: exporting a page which has not been examined!"); + + // export forms collection + exportCollectionElements(xCollectionIndex); + } + + void OFormLayerXMLExport_Impl::exportXForms() const + { + // export XForms models + ::exportXForms( m_rContext ); + } + + bool OFormLayerXMLExport_Impl::pageContainsForms( const Reference< XDrawPage >& _rxDrawPage ) + { + Reference< XFormsSupplier2 > xFormsSupp( _rxDrawPage, UNO_QUERY ); + SAL_WARN_IF( !xFormsSupp.is(), "xmloff", "OFormLayerXMLExport_Impl::pageContainsForms: no XFormsSupplier2!" ); + return xFormsSupp.is() && xFormsSupp->hasForms(); + } + + bool OFormLayerXMLExport_Impl::documentContainsXForms() const + { + Reference< css::xforms::XFormsSupplier > xXFormSupp( m_rContext.GetModel(), UNO_QUERY ); + Reference< XNameContainer > xForms; + if ( xXFormSupp.is() ) + xForms = xXFormSupp->getXForms(); + return xForms.is() && xForms->hasElements(); + } + + bool OFormLayerXMLExport_Impl::implMoveIterators(const Reference< XDrawPage >& _rxDrawPage, bool _bClear) + { + if (!_rxDrawPage.is()) + return false; + + bool bKnownPage = false; + + // the one for the ids + m_aCurrentPageIds = m_aControlIds.find(_rxDrawPage); + if (m_aControlIds.end() == m_aCurrentPageIds) + { + m_aControlIds[_rxDrawPage] = MapPropertySet2String(); + m_aCurrentPageIds = m_aControlIds.find(_rxDrawPage); + } + else + { + bKnownPage = true; + if (_bClear && !m_aCurrentPageIds->second.empty() ) + m_aCurrentPageIds->second.clear(); + } + + // the one for the ids of the referring controls + m_aCurrentPageReferring = m_aReferringControls.find(_rxDrawPage); + if (m_aReferringControls.end() == m_aCurrentPageReferring) + { + m_aReferringControls[_rxDrawPage] = MapPropertySet2String(); + m_aCurrentPageReferring = m_aReferringControls.find(_rxDrawPage); + } + else + { + bKnownPage = true; + if (_bClear && !m_aCurrentPageReferring->second.empty() ) + m_aCurrentPageReferring->second.clear(); + } + return bKnownPage; + } + + bool OFormLayerXMLExport_Impl::seekPage(const Reference< XDrawPage >& _rxDrawPage) + { + bool bKnownPage = implMoveIterators( _rxDrawPage, false ); + if ( bKnownPage ) + return true; + + // if the page is not yet known, this does not automatically mean that it has + // not been examined. Instead, examineForms returns silently and successfully + // if a page is a XFormsPageSupplier2, but does not have a forms collection + // (This behaviour of examineForms is a performance optimization, to not force + // the page to create a forms container just to see that it's empty.) + + // So, in such a case, seekPage is considered to be successful, too, though the + // page was not yet known + Reference< XFormsSupplier2 > xFormsSupp( _rxDrawPage, UNO_QUERY ); + if ( xFormsSupp.is() && !xFormsSupp->hasForms() ) + return true; + + // anything else means that the page has not been examined before, or it's no + // valid form page. Both cases are Bad (TM). + return false; + } + + OUString OFormLayerXMLExport_Impl::getControlId(const Reference< XPropertySet >& _rxControl) + { + if (m_aCurrentPageIds == m_aControlIds.end()) + return OUString(); + + OSL_ENSURE(m_aCurrentPageIds->second.end() != m_aCurrentPageIds->second.find(_rxControl), + "OFormLayerXMLExport_Impl::getControlId: can not find the control!"); + return m_aCurrentPageIds->second[_rxControl]; + } + + OUString OFormLayerXMLExport_Impl::getImmediateNumberStyle( const Reference< XPropertySet >& _rxObject ) + { + OUString sNumberStyle; + + sal_Int32 nOwnFormatKey = implExamineControlNumberFormat( _rxObject ); + if ( -1 != nOwnFormatKey ) + sNumberStyle = getControlNumberStyleExport()->GetStyleName( nOwnFormatKey ); + + return sNumberStyle; + } + + OUString OFormLayerXMLExport_Impl::getControlNumberStyle( const Reference< XPropertySet >& _rxControl ) + { + OUString sNumberStyle; + + MapPropertySet2Int::const_iterator aControlFormatPos = m_aControlNumberFormats.find(_rxControl); + if (m_aControlNumberFormats.end() != aControlFormatPos) + { + OSL_ENSURE(m_pControlNumberStyles, "OFormLayerXMLExport_Impl::getControlNumberStyle: have a control which has a format style, but no style exporter!"); + sNumberStyle = getControlNumberStyleExport()->GetStyleName(aControlFormatPos->second); + } + // it's allowed to ask for a control which does not have format information. + // (This is for performance reasons) + + return sNumberStyle; + } + + void OFormLayerXMLExport_Impl::examineForms(const Reference< XDrawPage >& _rxDrawPage) + { + // get the forms collection of the page + Reference< XIndexAccess > xCollectionIndex; + if (!impl_isFormPageContainingForms(_rxDrawPage, xCollectionIndex)) + { + return; + } + + // move the iterator which specify the currently handled page + bool bPageIsKnown = implMoveIterators(_rxDrawPage, true); + OSL_ENSURE(!bPageIsKnown, "OFormLayerXMLExport_Impl::examineForms: examining a page twice!"); + + ::std::stack< Reference< XIndexAccess > > aContainerHistory; + ::std::stack< sal_Int32 > aIndexHistory; + + Reference< XIndexAccess > xLoop = xCollectionIndex; + sal_Int32 nChildPos = 0; + do + { + if (nChildPos < xLoop->getCount()) + { + Reference< XPropertySet > xCurrent( xLoop->getByIndex( nChildPos ), UNO_QUERY ); + OSL_ENSURE(xCurrent.is(), "OFormLayerXMLExport_Impl::examineForms: invalid child object"); + if (!xCurrent.is()) + continue; + + if (!checkExamineControl(xCurrent)) + { + // step down + Reference< XIndexAccess > xNextContainer(xCurrent, UNO_QUERY); + OSL_ENSURE(xNextContainer.is(), "OFormLayerXMLExport_Impl::examineForms: what the heck is this ... no control, no container?"); + aContainerHistory.push(xLoop); + aIndexHistory.push(nChildPos); + + xLoop = xNextContainer; + nChildPos = -1; // will be incremented below + } + ++nChildPos; + } + else + { + // step up + while ((nChildPos >= xLoop->getCount()) && !aContainerHistory.empty() ) + { + xLoop = aContainerHistory.top(); + aContainerHistory.pop(); + nChildPos = aIndexHistory.top(); + aIndexHistory.pop(); + + ++nChildPos; + } + if (nChildPos >= xLoop->getCount()) + // exited the loop above because we have no history anymore (0 == aContainerHistory.size()), + // and on the current level there are no more children + // -> leave + break; + } + } + while (xLoop.is()); + } + + namespace + { + struct AccumulateSize + { + size_t operator()( size_t _size, const MapPropertySet2Map::value_type& _map ) const + { + return _size + _map.second.size(); + } + }; + + OUString lcl_findFreeControlId( const MapPropertySet2Map& _rAllPagesControlIds ) + { + OUString sControlId = "control"; + + size_t nKnownControlCount = ::std::accumulate( _rAllPagesControlIds.begin(), _rAllPagesControlIds.end(), size_t(0), AccumulateSize() ); + sControlId += OUString::number( static_cast<sal_Int32>(nKnownControlCount) + 1 ); + + #ifdef DBG_UTIL + // Check if the id is already used. It shouldn't, as we currently have no mechanism for removing entries + // from the map, so the approach used above (take the accumulated map size) should be sufficient. But if + // somebody changes this (e.g. allows removing entries from the map), the assertion below probably will fail. + for ( const auto& outer : _rAllPagesControlIds ) + for ( const auto& inner : outer.second ) + { + OSL_ENSURE( inner.second != sControlId, + "lcl_findFreeControlId: auto-generated control ID is already used!" ); + } + #endif + return sControlId; + } + } + + bool OFormLayerXMLExport_Impl::checkExamineControl(const Reference< XPropertySet >& _rxObject) + { + Reference< XPropertySetInfo > xCurrentInfo = _rxObject->getPropertySetInfo(); + OSL_ENSURE(xCurrentInfo.is(), "OFormLayerXMLExport_Impl::checkExamineControl: no property set info"); + + bool bIsControl = xCurrentInfo->hasPropertyByName( PROPERTY_CLASSID ); + if (bIsControl) + { + // generate a new control id + + // find a free id + OUString sCurrentId = lcl_findFreeControlId( m_aControlIds ); + // add it to the map + m_aCurrentPageIds->second[_rxObject] = sCurrentId; + + // check if this control has a "LabelControl" property referring another control + if ( xCurrentInfo->hasPropertyByName( PROPERTY_CONTROLLABEL ) ) + { + Reference< XPropertySet > xCurrentReference( _rxObject->getPropertyValue( PROPERTY_CONTROLLABEL ), UNO_QUERY ); + if (xCurrentReference.is()) + { + OUString& sReferencedBy = m_aCurrentPageReferring->second[xCurrentReference]; + if (!sReferencedBy.isEmpty()) + // it's not the first _rxObject referring to the xCurrentReference + // -> separate the id + sReferencedBy += ","; + sReferencedBy += sCurrentId; + } + } + + // check if the control needs a number format style + if ( xCurrentInfo->hasPropertyByName( PROPERTY_FORMATKEY ) ) + { + examineControlNumberFormat(_rxObject); + } + + // check if it's a control providing text + Reference< XText > xControlText( _rxObject, UNO_QUERY ); + if ( xControlText.is() ) + { + m_rContext.GetTextParagraphExport()->collectTextAutoStyles( xControlText ); + } + + // check if it is a grid control - in this case, we need special handling for the columns + sal_Int16 nControlType = FormComponentType::CONTROL; + _rxObject->getPropertyValue( PROPERTY_CLASSID ) >>= nControlType; + if ( FormComponentType::GRIDCONTROL == nControlType ) + { + collectGridColumnStylesAndIds( _rxObject ); + } + } + + return bIsControl; + } + + void OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds( const Reference< XPropertySet >& _rxControl ) + { + // loop through all columns of the grid + try + { + Reference< XIndexAccess > xContainer( _rxControl, UNO_QUERY ); + OSL_ENSURE( xContainer.is(), "OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: grid control not being a container?!" ); + if ( !xContainer.is() ) + return; + + Reference< XPropertySetInfo > xColumnPropertiesMeta; + + sal_Int32 nCount = xContainer->getCount(); + for ( sal_Int32 i=0; i<nCount; ++i ) + { + Reference< XPropertySet > xColumnProperties( xContainer->getByIndex( i ), UNO_QUERY ); + OSL_ENSURE( xColumnProperties.is(), "OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: invalid grid column encountered!" ); + if ( !xColumnProperties.is() ) + continue; + + // generate a new control id + + // find a free id + OUString sCurrentId = lcl_findFreeControlId( m_aControlIds ); + // add it to the map + m_aCurrentPageIds->second[ xColumnProperties ] = sCurrentId; + + // determine a number style, if needed + xColumnPropertiesMeta = xColumnProperties->getPropertySetInfo(); + // get the styles of the column + ::std::vector<XMLPropertyState> aPropertyStates = m_xStyleExportMapper->Filter(m_rContext, xColumnProperties); + + // care for the number format, additionally + OUString sColumnNumberStyle; + if ( xColumnPropertiesMeta.is() && xColumnPropertiesMeta->hasPropertyByName( PROPERTY_FORMATKEY ) ) + sColumnNumberStyle = getImmediateNumberStyle( xColumnProperties ); + + if ( !sColumnNumberStyle.isEmpty() ) + { // the column indeed has a formatting + sal_Int32 nStyleMapIndex = m_xStyleExportMapper->getPropertySetMapper()->FindEntryIndex( CTF_FORMS_DATA_STYLE ); + // TODO: move this to the ctor + OSL_ENSURE ( -1 != nStyleMapIndex, "OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: could not obtain the index for our context id!"); + + XMLPropertyState aNumberStyleState( nStyleMapIndex, Any( sColumnNumberStyle ) ); + aPropertyStates.push_back( aNumberStyleState ); + } + + // determine the column style + + if ( !aPropertyStates.empty() ) + { // add to the style pool + OUString sColumnStyleName = m_rContext.GetAutoStylePool()->Add( XmlStyleFamily::CONTROL_ID, std::move(aPropertyStates) ); + + OSL_ENSURE( m_aGridColumnStyles.end() == m_aGridColumnStyles.find( xColumnProperties ), + "OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: already have a style for this column!" ); + + m_aGridColumnStyles.emplace( xColumnProperties, sColumnStyleName ); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.forms"); + } + } + + sal_Int32 OFormLayerXMLExport_Impl::implExamineControlNumberFormat( const Reference< XPropertySet >& _rxObject ) + { + // get the format key relative to our own formats supplier + sal_Int32 nOwnFormatKey = ensureTranslateFormat( _rxObject ); + + if ( -1 != nOwnFormatKey ) + // tell the exporter that we used this format + getControlNumberStyleExport()->SetUsed( nOwnFormatKey ); + + return nOwnFormatKey; + } + + void OFormLayerXMLExport_Impl::examineControlNumberFormat( const Reference< XPropertySet >& _rxControl ) + { + sal_Int32 nOwnFormatKey = implExamineControlNumberFormat( _rxControl ); + + if ( -1 == nOwnFormatKey ) + // nothing to do, the number format of this control is void + return; + + // remember the format key for this control (we'll be asked in getControlNumberStyle for this) + OSL_ENSURE(m_aControlNumberFormats.end() == m_aControlNumberFormats.find(_rxControl), + "OFormLayerXMLExport_Impl::examineControlNumberFormat: already handled this control!"); + m_aControlNumberFormats[_rxControl] = nOwnFormatKey; + } + + sal_Int32 OFormLayerXMLExport_Impl::ensureTranslateFormat(const Reference< XPropertySet >& _rxFormattedControl) + { + ensureControlNumberStyleExport(); + OSL_ENSURE(m_xControlNumberFormats.is(), "OFormLayerXMLExport_Impl::ensureTranslateFormat: no own formats supplier!"); + // (should have been created in ensureControlNumberStyleExport) + + sal_Int32 nOwnFormatKey = -1; + + // the format key (relative to the control's supplier) + sal_Int32 nControlFormatKey = -1; + Any aControlFormatKey = _rxFormattedControl->getPropertyValue(PROPERTY_FORMATKEY); + if (aControlFormatKey >>= nControlFormatKey) + { + // the control's number format + Reference< XNumberFormatsSupplier > xControlFormatsSupplier; + _rxFormattedControl->getPropertyValue(PROPERTY_FORMATSSUPPLIER) >>= xControlFormatsSupplier; + Reference< XNumberFormats > xControlFormats; + if (xControlFormatsSupplier.is()) + xControlFormats = xControlFormatsSupplier->getNumberFormats(); + OSL_ENSURE(xControlFormats.is(), "OFormLayerXMLExport_Impl::ensureTranslateFormat: formatted control without supplier!"); + + // obtain the persistent (does not depend on the formats supplier) representation of the control's format + Locale aFormatLocale; + OUString sFormatDescription; + if (xControlFormats.is()) + { + Reference< XPropertySet > xControlFormat = xControlFormats->getByKey(nControlFormatKey); + + xControlFormat->getPropertyValue(PROPERTY_LOCALE) >>= aFormatLocale; + xControlFormat->getPropertyValue(PROPERTY_FORMATSTRING) >>= sFormatDescription; + } + + // check if our own formats collection already knows the format + nOwnFormatKey = m_xControlNumberFormats->queryKey(sFormatDescription, aFormatLocale, false); + if (-1 == nOwnFormatKey) + { // no, we don't + // -> create a new format + nOwnFormatKey = m_xControlNumberFormats->addNew(sFormatDescription, aFormatLocale); + } + OSL_ENSURE(-1 != nOwnFormatKey, "OFormLayerXMLExport_Impl::ensureTranslateFormat: could not translate the controls format key!"); + } + else + OSL_ENSURE(!aControlFormatKey.hasValue(), "OFormLayerXMLExport_Impl::ensureTranslateFormat: invalid number format property value!"); + + return nOwnFormatKey; + } + + void OFormLayerXMLExport_Impl::ensureControlNumberStyleExport() + { + if (m_pControlNumberStyles) + return; + + // create our number formats supplier (if necessary) + Reference< XNumberFormatsSupplier > xFormatsSupplier; + + OSL_ENSURE(!m_xControlNumberFormats.is(), "OFormLayerXMLExport_Impl::getControlNumberStyleExport: inconsistence!"); + // the m_xControlNumberFormats and m_pControlNumberStyles should be maintained together + + try + { + // create it for en-US (does not really matter, as we will specify a locale for every + // concrete language to use) + Locale aLocale ( "en", "US", OUString() ); + xFormatsSupplier = NumberFormatsSupplier::createWithLocale( m_rContext.getComponentContext(), aLocale ); + m_xControlNumberFormats = xFormatsSupplier->getNumberFormats(); + } + catch(const Exception&) + { + } + + OSL_ENSURE(m_xControlNumberFormats.is(), "OFormLayerXMLExport_Impl::getControlNumberStyleExport: could not obtain my default number formats!"); + + // create the exporter + m_pControlNumberStyles = new SvXMLNumFmtExport(m_rContext, xFormatsSupplier, getControlNumberStyleNamePrefix()); + } + + SvXMLNumFmtExport* OFormLayerXMLExport_Impl::getControlNumberStyleExport() + { + ensureControlNumberStyleExport(); + return m_pControlNumberStyles; + } + + void OFormLayerXMLExport_Impl::excludeFromExport( const Reference< XControlModel >& _rxControl ) + { + Reference< XPropertySet > xProps( _rxControl, UNO_QUERY ); + OSL_ENSURE( xProps.is(), "OFormLayerXMLExport_Impl::excludeFromExport: invalid control model!" ); + ::std::pair< PropertySetBag::const_iterator, bool > aPos = + m_aIgnoreList.insert( xProps ); + OSL_ENSURE( aPos.second, "OFormLayerXMLExport_Impl::excludeFromExport: element already exists in the ignore list!" ); + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/layerexport.hxx b/xmloff/source/forms/layerexport.hxx new file mode 100644 index 000000000..4d19386ef --- /dev/null +++ b/xmloff/source/forms/layerexport.hxx @@ -0,0 +1,299 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <unordered_map> + +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <com/sun/star/util/XNumberFormats.hpp> +#include <com/sun/star/awt/XControlModel.hpp> +#include "callbacks.hxx" +#include <rtl/ref.hxx> +#include <o3tl/sorted_vector.hxx> + +class SvXMLExport; +class SvXMLNumFmtExport; +class XMLPropertyHandlerFactory; +class SvXMLExportPropertyMapper; + +namespace xmloff +{ + + typedef o3tl::sorted_vector< css::uno::Reference< css::beans::XPropertySet > > PropertySetBag; + + // maps objects (property sets) to strings, e.g. control ids. + typedef ::std::unordered_map < css::uno::Reference< css::beans::XPropertySet > + , OUString + > MapPropertySet2String; + + // map pages to maps (of property sets to strings) + typedef ::std::unordered_map < css::uno::Reference< css::drawing::XDrawPage > + , MapPropertySet2String + > MapPropertySet2Map; + + //= OFormLayerXMLExport_Impl + /** the implementation class for OFormLayerXMLExport + */ + class OFormLayerXMLExport_Impl + :public IFormsExportContext + { + friend class OFormLayerXMLExport; + + SvXMLExport& m_rContext; + SvXMLNumFmtExport* m_pControlNumberStyles; + + // ignore list for control models + PropertySetBag m_aIgnoreList; + + // style handling + ::rtl::Reference< XMLPropertyHandlerFactory > m_xPropertyHandlerFactory; + ::rtl::Reference< SvXMLExportPropertyMapper > m_xStyleExportMapper; + + // we need our own number formats supplier: + // Controls which have a number formats do not work with the formats supplier of the document they reside + // in, instead they use the formats of the data source their form is associated with. If there is no + // such form or no such data source, they work with an own formatter. + // Even more, time and date fields do not work with a central formatter at all, they have their own one + // (which is shared internally, but this is a (hidden) implementation detail.) + + // To not contaminate the global (document) number formats supplier (which could be obtained from the context), + // we have an own one. + // (Contaminate means: If a user adds a user-defined format to a formatted field, this format is stored in + // in the data source's formats supplier. To export this _and_ reuse existing structures, we would need to + // add this format to the global (document) formats supplier. + // In case of an export we could do some cleanup afterwards, but in case of an import, there is no such + // chance, as (if other user-defined formats exist in the document as well) we can't distinguish + // between user-defined formats really needed for the doc (i.e. in a calc cell) and formats only added + // to the supplier because the controls needed it. + css::uno::Reference< css::util::XNumberFormats > + m_xControlNumberFormats; + + MapPropertySet2Map m_aControlIds; + // the control ids of all controls on all pages we ever examined + + MapPropertySet2Map m_aReferringControls; + // for a given page (iter->first), and a given control (iter->second->first), this is the comma-separated + // lists of ids of the controls referring to the control given. + + MapPropertySet2Map::iterator + m_aCurrentPageIds; + // the iterator for the control id map for the page being handled + MapPropertySet2Map::iterator + m_aCurrentPageReferring; + // the same for the map of referring controls + + // TODO: To avoid this construct above, and to have a cleaner implementation, a class encapsulating the + // export of a single page should be introduced. + + typedef std::unordered_map<css::uno::Reference<css::beans::XPropertySet>, sal_Int32> MapPropertySet2Int; + MapPropertySet2Int m_aControlNumberFormats; + // maps controls to format keys, which are relative to our own formats supplier + + MapPropertySet2String m_aGridColumnStyles; + // style names of grid columns + + public: + explicit OFormLayerXMLExport_Impl(SvXMLExport& _rContext); + virtual ~OFormLayerXMLExport_Impl(); + + private: + /** exports one single grid column + */ + void exportGridColumn( + const css::uno::Reference< css::beans::XPropertySet >& _rxColumn, + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rEvents + ); + + /** exports one single control + */ + void exportControl( + const css::uno::Reference< css::beans::XPropertySet >& _rxControl, + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rEvents + ); + + /** exports one single form + */ + void exportForm(const css::uno::Reference< css::beans::XPropertySet >& _rxProps, + const css::uno::Sequence< css::script::ScriptEventDescriptor >& _rEvents + ); + + /** seek to the page given. + + <p>This must be called before you can retrieve any ids for controls on the page.</p> + + @see + getControlId + */ + bool seekPage( + const css::uno::Reference< css::drawing::XDrawPage >& _rxDrawPage); + + /** get the id of the given control. + + <p>You must have sought to the page of the control before calling this.</p> + */ + OUString + getControlId(const css::uno::Reference< css::beans::XPropertySet >& _rxControl); + + /** retrieves the style name for the control's number style. + + <p>For performance reasons, this method is allowed to be called for any controls, even those which + do not have a number style. In this case, an empty string is returned.</p> + */ + OUString + getControlNumberStyle( const css::uno::Reference< css::beans::XPropertySet >& _rxControl ); + + // IFormsExportContext + virtual void exportCollectionElements(const css::uno::Reference< css::container::XIndexAccess >& _rxCollection) override; + virtual SvXMLExport& getGlobalContext() override; + virtual OUString getObjectStyleName( + const css::uno::Reference< css::beans::XPropertySet >& _rxObject ) override; + virtual ::rtl::Reference< SvXMLExportPropertyMapper > getStylePropertyMapper() override; + + /** clear any structures which have been build in the recent <method>examine</method> calls. + */ + void clear(); + + /** examine a forms collection. + + <p>The method will collect control ids and add styles to the export context as necessary.</p> + + <p>Every control in the object hierarchy given will be assigned to a unique id, which is stored for later + use.</p> + + <p>In addition, any references the controls may have between each other, are collected and stored for + later use.</p> + + <p>Upon calling this method, the id map will be cleared before collecting the new ids, so any ids + you collected previously will be lost</p> + + @param _rxDrawPage + the draw page which's forms collection should be examined + + @see getControlId + @see exportControl + @see exportForms + */ + void examineForms( + const css::uno::Reference< css::drawing::XDrawPage >& _rxDrawPage); + + /** export a forms collection of a draw page + + <p>The method will obtain the forms collection of the page and call + <method>exportCollectionElements</method>.</p> + */ + void exportForms( + const css::uno::Reference< css::drawing::XDrawPage >& _rxDrawPage); + + /** exports the XForms model data + */ + void exportXForms() const; + + /** determines whether the given page contains logical forms + */ + static bool pageContainsForms( const css::uno::Reference< css::drawing::XDrawPage >& _rxDrawPage ); + + /** determines whether the given page contains XForm instances + */ + bool documentContainsXForms() const; + + /** exports the automatic control number styles + */ + void exportAutoControlNumberStyles(); + + /** exports the auto-styles collected during the examineForms calls + */ + void exportAutoStyles(); + + static bool impl_isFormPageContainingForms( + const css::uno::Reference< css::drawing::XDrawPage >& _rxDrawPage, + css::uno::Reference< css::container::XIndexAccess >& _rxForms); + + /** moves the m_aCurrentPage* members to the positions specifying the given page. + + @return <TRUE/> if there already were structures for the given page + */ + bool implMoveIterators( + const css::uno::Reference< css::drawing::XDrawPage >& _rxDrawPage, + bool _bClear); + + /** check the object given if it's a control, if so, examine it. + @return <TRUE/> if the object has been handled + */ + bool checkExamineControl(const css::uno::Reference< css::beans::XPropertySet >& _rxObject); + + /** examines the control's number format, so later the format style can be referred + + <p>remembers the format key for the control, so it can later be asked for in getControlNumberStyle</p> + */ + void examineControlNumberFormat(const css::uno::Reference< css::beans::XPropertySet >& _rxControl); + + /** examines the control's number format, so later the format style can be referred + + <p>does not remember the information returned in any way</p> + */ + sal_Int32 implExamineControlNumberFormat( const css::uno::Reference< css::beans::XPropertySet >& _rxObject ); + + /** collects AutoStyles for grid columns + */ + void collectGridColumnStylesAndIds( const css::uno::Reference< css::beans::XPropertySet >& _rxControl ); + + /** ensures that the number format of the given control exist in our own formats supplier. + + <p>The given control is examined for its format (i.e. it's FormatKey/FormatsSupplier properties), + and the format is added (if necessary) to m_xControlNumberFormats</p>. + + @return + the format key of the control's format relative to our own formats supplier + + */ + sal_Int32 ensureTranslateFormat(const css::uno::Reference< css::beans::XPropertySet >& _rxFormattedControl); + + /// returns the instance exporting our control's number styles + SvXMLNumFmtExport* getControlNumberStyleExport(); + + /// ensures that the instance exporting our control's number styles exists + void ensureControlNumberStyleExport(); + + /** determines the number format style for the given object without remembering it + */ + OUString + getImmediateNumberStyle( const css::uno::Reference< css::beans::XPropertySet >& _rxObject ); + + /** returns the prefix to be used for control number styles + */ + static const OUString& getControlNumberStyleNamePrefix(); + + /** exclude the given control (model) from export. + + <p>If your document contains form controls which are not to be exported for whatever reason, + you need to announce the models of these controls (can be retrieved from XControlShape::getControl) + to the form layer exporter.<br/> + Of course you have to do this before calling <member>exportForms</member></p> + */ + void excludeFromExport( const css::uno::Reference< css::awt::XControlModel >& _rxControl ); + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/layerimport.cxx b/xmloff/source/forms/layerimport.cxx new file mode 100644 index 000000000..e5d2c62af --- /dev/null +++ b/xmloff/source/forms/layerimport.cxx @@ -0,0 +1,553 @@ +/* -*- 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 "layerimport.hxx" +#include "formenums.hxx" +#include "elementimport.hxx" +#include "officeforms.hxx" +#include "strings.hxx" +#include <xmloff/xmlictxt.hxx> +#include <xmloff/xmlstyle.hxx> +#include <xmloff/families.hxx> +#include <xmloff/xmlimp.hxx> +#include <XMLEventImportHelper.hxx> +#include <xmloff/xmlnumfi.hxx> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/form/FormSubmitEncoding.hpp> +#include <com/sun/star/form/FormSubmitMethod.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/form/NavigationBarMode.hpp> +#include <com/sun/star/form/TabulatorCycle.hpp> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/form/ListSourceType.hpp> +#include "formevents.hxx" +#include "formcellbinding.hxx" +#include <xmloff/xformsimport.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <rtl/strbuf.hxx> +#include <tools/diagnose_ex.h> +#include <algorithm> + +namespace xmloff +{ + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::sdb; +using namespace token; + +//= OFormLayerXMLImport_Impl +OFormLayerXMLImport_Impl::OFormLayerXMLImport_Impl(SvXMLImport& _rImporter) + :m_rImporter(_rImporter) +{ + // build the attribute2property map + // string properties which are exported as attributes + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Name), PROPERTY_NAME); + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getSpecialAttributeToken(SCAFlags::GroupName), PROPERTY_GROUP_NAME); + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Label), PROPERTY_LABEL); + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::TargetLocation), PROPERTY_TARGETURL); + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Title), PROPERTY_TITLE); + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::TargetFrame), PROPERTY_TARGETFRAME); + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getDatabaseAttributeToken(DAFlags::DataField), PROPERTY_DATAFIELD); + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getFormAttributeToken(faCommand), PROPERTY_COMMAND); + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getFormAttributeToken(faDatasource), PROPERTY_DATASOURCENAME); + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getFormAttributeToken(faFilter), PROPERTY_FILTER); + m_aAttributeMetaData.addStringProperty( + OAttributeMetaData::getFormAttributeToken(faOrder), PROPERTY_ORDER); + + // properties not added because they're already present in another form + OSL_ENSURE( + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::TargetLocation).equals( + OAttributeMetaData::getFormAttributeName(faAction)), + "OFormLayerXMLImport_Impl::OFormLayerXMLImport_Impl: invalid attribute names (1)!"); + // if this fails, we would have to add a translation from faAction->PROPERTY_TARGETURL + // We did not because we already have one CCAFlags::TargetLocation->PROPERTY_TARGETURL, + // and CCAFlags::TargetLocation and faAction should be represented by the same attribute + + OSL_ENSURE( + OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Name).equals( + OAttributeMetaData::getFormAttributeName(faName)), + "OFormLayerXMLImport_Impl::OFormLayerXMLImport_Impl: invalid attribute names (2)!"); + // the same for faName, CCAFlags::Name and PROPERTY_NAME + + // boolean properties which are exported as attributes + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::CurrentSelected), PROPERTY_STATE, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Disabled), PROPERTY_ENABLED, false, true); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Dropdown), PROPERTY_DROPDOWN, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Printable), PROPERTY_PRINTABLE, true); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::ReadOnly), PROPERTY_READONLY, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Selected), PROPERTY_DEFAULT_STATE, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::TabStop), PROPERTY_TABSTOP, true); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getDatabaseAttributeToken(DAFlags::ConvertEmpty), PROPERTY_EMPTY_IS_NULL, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getSpecialAttributeToken(SCAFlags::Validation), PROPERTY_STRICTFORMAT, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getSpecialAttributeToken(SCAFlags::MultiLine), PROPERTY_MULTILINE, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getSpecialAttributeToken(SCAFlags::AutoCompletion), PROPERTY_AUTOCOMPLETE, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getSpecialAttributeToken(SCAFlags::Multiple), PROPERTY_MULTISELECTION, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getSpecialAttributeToken(SCAFlags::DefaultButton), PROPERTY_DEFAULTBUTTON, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getSpecialAttributeToken(SCAFlags::IsTristate), PROPERTY_TRISTATE, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getFormAttributeToken(faAllowDeletes), PROPERTY_ALLOWDELETES, true); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getFormAttributeToken(faAllowInserts), PROPERTY_ALLOWINSERTS, true); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getFormAttributeToken(faAllowUpdates), PROPERTY_ALLOWUPDATES, true); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getFormAttributeToken(faApplyFilter), PROPERTY_APPLYFILTER, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getFormAttributeToken(faEscapeProcessing), PROPERTY_ESCAPEPROCESSING, true); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getFormAttributeToken(faIgnoreResult), PROPERTY_IGNORERESULT, false); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getSpecialAttributeToken( SCAFlags::Toggle ), PROPERTY_TOGGLE, false ); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getSpecialAttributeToken( SCAFlags::FocusOnClick ), PROPERTY_FOCUS_ON_CLICK, true ); + m_aAttributeMetaData.addBooleanProperty( + OAttributeMetaData::getDatabaseAttributeToken( DAFlags::InputRequired ), PROPERTY_INPUT_REQUIRED, false ); + + // the int16 attributes + m_aAttributeMetaData.addInt16Property( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::MaxLength), PROPERTY_MAXTEXTLENGTH); + m_aAttributeMetaData.addInt16Property( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::Size), PROPERTY_LINECOUNT); + m_aAttributeMetaData.addInt16Property( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::TabIndex), PROPERTY_TABINDEX); + m_aAttributeMetaData.addInt16Property( + OAttributeMetaData::getDatabaseAttributeToken(DAFlags::BoundColumn), PROPERTY_BOUNDCOLUMN); + + // the int32 attributes + m_aAttributeMetaData.addInt32Property( + OAttributeMetaData::getSpecialAttributeToken( SCAFlags::PageStepSize ), PROPERTY_BLOCK_INCREMENT ); + + // the enum attributes + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getCommonControlAttributeToken( CCAFlags::VisualEffect ), PROPERTY_VISUAL_EFFECT, + aVisualEffectMap, + &::cppu::UnoType<sal_Int16>::get() ); + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getCommonControlAttributeToken( CCAFlags::Orientation ), PROPERTY_ORIENTATION, + aOrientationMap, + &::cppu::UnoType<sal_Int32>::get() ); + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getCommonControlAttributeToken(CCAFlags::ButtonType), PROPERTY_BUTTONTYPE, + aFormButtonTypeMap, + &::cppu::UnoType<FormButtonType>::get()); + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getDatabaseAttributeToken(DAFlags::ListSource_TYPE), PROPERTY_LISTSOURCETYPE, + aListSourceTypeMap, + &::cppu::UnoType<ListSourceType>::get()); + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getSpecialAttributeToken(SCAFlags::State), PROPERTY_DEFAULT_STATE, + aCheckStateMap, + &::cppu::UnoType<sal_Int16>::get()); + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getSpecialAttributeToken(SCAFlags::CurrentState), PROPERTY_STATE, + aCheckStateMap, + &::cppu::UnoType<sal_Int16>::get()); + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getFormAttributeToken(faEnctype), PROPERTY_SUBMIT_ENCODING, + aSubmitEncodingMap, + &::cppu::UnoType<FormSubmitEncoding>::get()); + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getFormAttributeToken(faMethod), PROPERTY_SUBMIT_METHOD, + aSubmitMethodMap, + &::cppu::UnoType<FormSubmitMethod>::get()); + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getFormAttributeToken(faCommandType), PROPERTY_COMMAND_TYPE, + aCommandTypeMap); + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getFormAttributeToken(faNavigationMode), PROPERTY_NAVIGATION, + aNavigationTypeMap, + &::cppu::UnoType<NavigationBarMode>::get()); + m_aAttributeMetaData.addEnumProperty( + OAttributeMetaData::getFormAttributeToken(faTabbingCycle), PROPERTY_CYCLE, + aTabulatorCycleMap, + &::cppu::UnoType<TabulatorCycle>::get()); + + // 'initialize' + m_aCurrentPageIds = m_aControlIds.end(); +} + +OFormLayerXMLImport_Impl::~OFormLayerXMLImport_Impl() +{} + +void OFormLayerXMLImport_Impl::setAutoStyleContext(SvXMLStylesContext* _pNewContext) +{ + OSL_ENSURE(!m_xAutoStyles.is(), "OFormLayerXMLImport_Impl::setAutoStyleContext: not to be called twice!"); + m_xAutoStyles.set(_pNewContext); +} + +void OFormLayerXMLImport_Impl::applyControlNumberStyle(const Reference< XPropertySet >& _rxControlModel, const OUString& _rControlNumberStyleName) +{ + OSL_ENSURE(_rxControlModel.is() && (!_rControlNumberStyleName.isEmpty()), + "OFormLayerXMLImport_Impl::applyControlNumberStyle: invalid arguments (this will crash)!"); + + OSL_ENSURE(m_xAutoStyles.is(), "OFormLayerXMLImport_Impl::applyControlNumberStyle: have no auto style context!"); + if (!m_xAutoStyles.is()) + { + m_xAutoStyles.set(m_rImporter.GetShapeImport()->GetAutoStylesContext()); + } + + if (!m_xAutoStyles.is()) + return; + + const SvXMLStyleContext* pStyle = m_xAutoStyles->FindStyleChildContext(XmlStyleFamily::DATA_STYLE, _rControlNumberStyleName); + if (pStyle) + { + const SvXMLNumFormatContext* pDataStyle = static_cast<const SvXMLNumFormatContext*>(pStyle); + + // set this format at the control model + try + { + // the models number format supplier and formats + Reference< XNumberFormatsSupplier > xFormatsSupplier; + _rxControlModel->getPropertyValue(PROPERTY_FORMATSSUPPLIER) >>= xFormatsSupplier; + Reference< XNumberFormats > xFormats; + if (xFormatsSupplier.is()) + xFormats = xFormatsSupplier->getNumberFormats(); + OSL_ENSURE(xFormats.is(), "OFormLayerXMLImport_Impl::applyControlNumberStyle: could not obtain the controls number formats!"); + + // obtain a key + if (xFormats.is()) + { + sal_Int32 nFormatKey = const_cast<SvXMLNumFormatContext*>(pDataStyle)->CreateAndInsert( xFormatsSupplier ); + OSL_ENSURE(-1 != nFormatKey, "OFormLayerXMLImport_Impl::applyControlNumberStyle: could not obtain a format key!"); + + // set the format on the control model + _rxControlModel->setPropertyValue(PROPERTY_FORMATKEY, Any(nFormatKey)); + } + } + catch(const Exception&) + { + OSL_FAIL("OFormLayerXMLImport_Impl::applyControlNumberStyle: couldn't set the format!"); + } + } + else + OSL_FAIL("OFormLayerXMLImport_Impl::applyControlNumberStyle: did not find the style with the given name!"); +} + +void OFormLayerXMLImport_Impl::registerCellValueBinding( const Reference< XPropertySet >& _rxControlModel, const OUString& _rCellAddress ) +{ + OSL_ENSURE( _rxControlModel.is() && !_rCellAddress.isEmpty(), + "OFormLayerXMLImport_Impl::registerCellValueBinding: invalid arguments!" ); + m_aCellValueBindings.emplace_back( _rxControlModel, _rCellAddress ); +} + +void OFormLayerXMLImport_Impl::registerXFormsValueBinding( + const Reference< XPropertySet >& _rxControlModel, + const OUString& _rBindingID ) +{ + // TODO: is an empty binding name allowed? + OSL_ENSURE( _rxControlModel.is(), "need model" ); + + m_aXFormsValueBindings.emplace_back( _rxControlModel, _rBindingID ); +} + +void OFormLayerXMLImport_Impl::registerXFormsListBinding( + const Reference< XPropertySet >& _rxControlModel, + const OUString& _rBindingID ) +{ + // TODO: is an empty binding name allowed? + OSL_ENSURE( _rxControlModel.is(), "need model" ); + + m_aXFormsListBindings.emplace_back( _rxControlModel, _rBindingID ); +} + +void OFormLayerXMLImport_Impl::registerXFormsSubmission( + const Reference< XPropertySet >& _rxControlModel, + const OUString& _rSubmissionID ) +{ + // TODO: is an empty binding name allowed? + OSL_ENSURE( _rxControlModel.is(), "need model" ); + + m_aXFormsSubmissions.emplace_back( _rxControlModel, _rSubmissionID ); +} + +void OFormLayerXMLImport_Impl::registerCellRangeListSource( const Reference< XPropertySet >& _rxControlModel, const OUString& _rCellRangeAddress ) +{ + OSL_ENSURE( _rxControlModel.is() && !_rCellRangeAddress.isEmpty(), + "OFormLayerXMLImport_Impl::registerCellRangeListSource: invalid arguments!" ); + m_aCellRangeListSources.emplace_back( _rxControlModel, _rCellRangeAddress ); +} +const SvXMLStyleContext* OFormLayerXMLImport_Impl::getStyleElement(const OUString& _rStyleName) const +{ + OSL_ENSURE( m_xAutoStyles.is(), "OFormLayerXMLImport_Impl::getStyleElement: have no auto style context!" ); + // did you use setAutoStyleContext? + + const SvXMLStyleContext* pControlStyle = + m_xAutoStyles.is() ? m_xAutoStyles->FindStyleChildContext( XmlStyleFamily::TEXT_PARAGRAPH, _rStyleName ) : nullptr; + OSL_ENSURE( pControlStyle || !m_xAutoStyles.is(), + OStringBuffer("OFormLayerXMLImport_Impl::getStyleElement: did not find the style named \"" + + OUStringToOString(_rStyleName, RTL_TEXTENCODING_ASCII_US) + + "\"!").getStr() ); + return pControlStyle; +} + +void OFormLayerXMLImport_Impl::enterEventContext() +{ + // install our own translation table. We need to disable the other tables because of name conflicts. + m_rImporter.GetEventImport().PushTranslationTable(); + m_rImporter.GetEventImport().AddTranslationTable(g_pFormsEventTranslation); +} + +void OFormLayerXMLImport_Impl::leaveEventContext() +{ + // install the original event tables. + m_rImporter.GetEventImport().PopTranslationTable(); +} + +void OFormLayerXMLImport_Impl::registerControlId(const Reference< XPropertySet >& _rxControl, const OUString& _rId) +{ + OSL_ENSURE(m_aCurrentPageIds != m_aControlIds.end(), "OFormLayerXMLImport_Impl::registerControlId: no current page!"); + OSL_ENSURE(!_rId.isEmpty(), "OFormLayerXMLImport_Impl::registerControlId: invalid (empty) control id!"); + + OSL_ENSURE(m_aCurrentPageIds->second.end() == m_aCurrentPageIds->second.find(_rId), "OFormLayerXMLImport_Impl::registerControlId: control id already used!"); + m_aCurrentPageIds->second[_rId] = _rxControl; +} + +void OFormLayerXMLImport_Impl::registerControlReferences(const Reference< XPropertySet >& _rxControl, const OUString& _rReferringControls) +{ + OSL_ENSURE(!_rReferringControls.isEmpty(), "OFormLayerXMLImport_Impl::registerControlReferences: invalid (empty) control id list!"); + OSL_ENSURE(_rxControl.is(), "OFormLayerXMLImport_Impl::registerControlReferences: invalid (NULL) control!"); + m_aControlReferences.emplace_back( _rxControl, _rReferringControls ); +} + +void OFormLayerXMLImport_Impl::startPage(const Reference< XDrawPage >& _rxDrawPage) +{ + m_xCurrentPageFormsSupp.clear(); + + OSL_ENSURE(_rxDrawPage.is(), "OFormLayerXMLImport_Impl::startPage: NULL page!"); + m_xCurrentPageFormsSupp.set(_rxDrawPage, css::uno::UNO_QUERY); + OSL_ENSURE( m_xCurrentPageFormsSupp.is(), "OFormLayerXMLImport_Impl::startPage: invalid draw page (no XFormsSupplier)!" ); + if ( !m_xCurrentPageFormsSupp.is() ) + return; + + // add a new entry to our page map + ::std::pair< MapDrawPage2Map::iterator, bool > aPagePosition = m_aControlIds.emplace(_rxDrawPage, MapString2PropertySet()); + OSL_ENSURE(aPagePosition.second, "OFormLayerXMLImport_Impl::startPage: already imported this page!"); + m_aCurrentPageIds = aPagePosition.first; +} + +void OFormLayerXMLImport_Impl::endPage() +{ + OSL_ENSURE( m_xCurrentPageFormsSupp.is(), "OFormLayerXMLImport_Impl::endPage: sure you called startPage before?" ); + + // do some knittings for the controls which are referring to each other + try + { + static const sal_Unicode s_nSeparator = ','; + OUString sCurrentReferring; + OUString sSeparator(&s_nSeparator, 1); + Reference< XPropertySet > xCurrentReferring; + sal_Int32 nSeparator, nPrevSep; + for ( const auto& rReferences : m_aControlReferences ) + { + // the list of control ids is comma separated + + // in a list of n ids there are only n-1 separators ... have to catch this last id + // -> normalize the list + OUString sReferring = rReferences.second + sSeparator; + + nPrevSep = -1; + while (-1 != (nSeparator = sReferring.indexOf(s_nSeparator, nPrevSep + 1))) + { + sCurrentReferring = sReferring.copy(nPrevSep + 1, nSeparator - nPrevSep - 1); + xCurrentReferring = lookupControlId(sCurrentReferring); + if (xCurrentReferring.is()) + // if this condition fails, this is an error, but lookupControlId should have asserted this ... + xCurrentReferring->setPropertyValue( PROPERTY_CONTROLLABEL, Any( rReferences.first ) ); + + nPrevSep = nSeparator; + } + } + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.forms", + "unable to knit the control references (caught an exception)!"); + } + + // now that we have all children of the forms collection, attach the events + Reference< XIndexAccess > xIndexContainer; + if ( m_xCurrentPageFormsSupp.is() && m_xCurrentPageFormsSupp->hasForms() ) + xIndexContainer.set(m_xCurrentPageFormsSupp->getForms(), css::uno::UNO_QUERY); + if ( xIndexContainer.is() ) + ODefaultEventAttacherManager::setEvents( xIndexContainer ); + + // clear the structures for the control references. + m_aControlReferences.clear(); + + // and no we have no current page anymore + m_aCurrentPageIds = m_aControlIds.end(); +} + +Reference< XPropertySet > OFormLayerXMLImport_Impl::lookupControlId(const OUString& _rControlId) +{ + OSL_ENSURE(m_aCurrentPageIds != m_aControlIds.end(), "OFormLayerXMLImport_Impl::lookupControlId: no current page!"); + Reference< XPropertySet > xReturn; + if (m_aCurrentPageIds != m_aControlIds.end()) + { + MapString2PropertySet::const_iterator aPos = m_aCurrentPageIds->second.find(_rControlId); + if (m_aCurrentPageIds->second.end() != aPos) + xReturn = aPos->second; + else + SAL_WARN("xmloff", "unknown control id " << _rControlId); + } + return xReturn; +} + +SvXMLImportContext* OFormLayerXMLImport_Impl::createOfficeFormsContext( + SvXMLImport& _rImport) +{ + return new OFormsRootImport( _rImport ); +} + +SvXMLImportContext* OFormLayerXMLImport_Impl::createContext( + sal_Int32 nElement, + const Reference< xml::sax::XFastAttributeList >&) +{ + SvXMLImportContext* pContext = nullptr; + if ( (nElement & TOKEN_MASK) == XML_FORM ) + { + if ( m_xCurrentPageFormsSupp.is() ) + pContext = new OFormImport(*this, *this, m_xCurrentPageFormsSupp->getForms() ); + } + else if ( nElement == XML_ELEMENT(XFORMS, XML_MODEL) ) + { + pContext = createXFormsModelContext( m_rImporter ); + } + else + SAL_WARN("xmloff", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement)); + + return pContext; +} + +void OFormLayerXMLImport_Impl::documentDone( ) +{ + SvXMLImport& rImport = getGlobalContext(); + if ( !( rImport.getImportFlags() & SvXMLImportFlags::CONTENT ) ) + return; + + // create (and bind) the spreadsheet cell bindings + if ( !m_aCellValueBindings.empty() + && FormCellBindingHelper::isCellBindingAllowed( rImport.GetModel() ) + ) + { + for ( const auto& rCellBindings : m_aCellValueBindings ) + { + try + { + FormCellBindingHelper aHelper( rCellBindings.first, rImport.GetModel() ); + OSL_ENSURE( aHelper.isCellBindingAllowed(), "OFormLayerXMLImport_Impl::documentDone: can't bind this control model!" ); + if ( aHelper.isCellBindingAllowed() ) + { + // There are special bindings for listboxes. See + // OListAndComboImport::doRegisterCellValueBinding for a comment on this HACK. + OUString sBoundCellAddress( rCellBindings.second ); + sal_Int32 nIndicator = sBoundCellAddress.lastIndexOf( ":index" ); + + bool bUseIndexBinding = false; + if ( nIndicator != -1 ) + { + sBoundCellAddress = sBoundCellAddress.copy( 0, nIndicator ); + bUseIndexBinding = true; + } + + aHelper.setBinding( aHelper.createCellBindingFromStringAddress( sBoundCellAddress, bUseIndexBinding ) ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.forms", + "caught an exception while binding to a cell!"); + } + } + m_aCellValueBindings.clear(); + } + + // the same for the spreadsheet cell range list sources + if ( !m_aCellRangeListSources.empty() + && FormCellBindingHelper::isListCellRangeAllowed( rImport.GetModel() ) + ) + { + for ( const auto& rRangeBindings : m_aCellRangeListSources ) + { + try + { + FormCellBindingHelper aHelper( rRangeBindings.first, rImport.GetModel() ); + OSL_ENSURE( aHelper.isListCellRangeAllowed(), "OFormLayerXMLImport_Impl::documentDone: can't bind this control model!" ); + if ( aHelper.isListCellRangeAllowed() ) + { + aHelper.setListSource( aHelper.createCellListSourceFromStringAddress( rRangeBindings.second ) ); + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.forms", + "caught an exception while binding to a cell range!"); + } + } + m_aCellRangeListSources.clear(); + } + + // process XForms-bindings; call registerXFormsValueBinding for each + for (const auto& aXFormsValueBinding : m_aXFormsValueBindings) + bindXFormsValueBinding(rImport.GetModel(), aXFormsValueBinding); + // same for list bindings + for (const auto& aXFormsListBindings : m_aXFormsListBindings) + bindXFormsListBinding(rImport.GetModel(), aXFormsListBindings); + // same for submissions + for (const auto& aXFormsSubmission : m_aXFormsSubmissions) + bindXFormsSubmission(rImport.GetModel(), aXFormsSubmission); +} + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/layerimport.hxx b/xmloff/source/forms/layerimport.hxx new file mode 100644 index 000000000..21e3a79c7 --- /dev/null +++ b/xmloff/source/forms/layerimport.hxx @@ -0,0 +1,175 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <map> +#include <unordered_map> + +#include <com/sun/star/xml/sax/XAttributeList.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/form/XFormsSupplier2.hpp> +#include <rtl/ref.hxx> +#include "formattributes.hxx" +#include "eventimport.hxx" + +class SvXMLImport; +class SvXMLImportContext; +class SvXMLStyleContext; +class SvXMLStylesContext; + + // unfortunately, we can't put this into our namespace, as the macro expands to (amongst others) a forward + // declaration of the class name, which then would be in the namespace, too + +namespace xmloff +{ + + class OAttribute2Property; + + //= OFormLayerXMLImport_Impl + class OFormLayerXMLImport_Impl + : public ODefaultEventAttacherManager + { + friend class OFormLayerXMLImport; + + SvXMLImport& m_rImporter; + OAttribute2Property m_aAttributeMetaData; + + /// the supplier for the forms of the currently imported page + css::uno::Reference< css::form::XFormsSupplier2 > + m_xCurrentPageFormsSupp; + rtl::Reference<SvXMLStylesContext> m_xAutoStyles; + + typedef std::map< OUString, css::uno::Reference< css::beans::XPropertySet > > MapString2PropertySet; + typedef std::unordered_map<css::uno::Reference<css::drawing::XDrawPage>, MapString2PropertySet> MapDrawPage2Map; + + MapDrawPage2Map m_aControlIds; // ids of the controls on all known page + MapDrawPage2Map::iterator m_aCurrentPageIds; // ifs of the controls on the current page + + typedef ::std::pair< css::uno::Reference< css::beans::XPropertySet >, OUString > + ModelStringPair; + ::std::vector< ModelStringPair > + m_aControlReferences; // control reference descriptions for current page + ::std::vector< ModelStringPair > + m_aCellValueBindings; // information about controls bound to spreadsheet cells + ::std::vector< ModelStringPair > + m_aCellRangeListSources;// information about controls bound to spreadsheet cell range list sources + + ::std::vector< ModelStringPair > + m_aXFormsValueBindings; // collect xforms:bind attributes to be resolved + + ::std::vector< ModelStringPair > + m_aXFormsListBindings; // collect forms:xforms-list-source attributes to be resolved + + ::std::vector< ModelStringPair > + m_aXFormsSubmissions; // collect xforms:submission attributes to be resolved + + public: + // IControlIdMap + void registerControlId( + const css::uno::Reference< css::beans::XPropertySet >& _rxControl, + const OUString& _rId); + void registerControlReferences( + const css::uno::Reference< css::beans::XPropertySet >& _rxControl, + const OUString& _rReferringControls); + + // OFormLayerXMLImport_Impl + OAttribute2Property& getAttributeMap() { return m_aAttributeMetaData; } + SvXMLImport& getGlobalContext() { return m_rImporter; } + const SvXMLStyleContext* getStyleElement(const OUString& _rStyleName) const; + void enterEventContext(); + void leaveEventContext(); + void applyControlNumberStyle( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel, + const OUString& _rControlNumberStyleName + ); + void registerCellValueBinding( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel, + const OUString& _rCellAddress + ); + + void registerCellRangeListSource( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel, + const OUString& _rCellRangeAddress + ); + + void registerXFormsValueBinding( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel, + const OUString& _rBindingID + ); + + void registerXFormsListBinding( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel, + const OUString& _rBindingID + ); + + void registerXFormsSubmission( + const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel, + const OUString& _rSubmissionID + ); + + ~OFormLayerXMLImport_Impl() override; + + private: + explicit OFormLayerXMLImport_Impl(SvXMLImport& _rImporter); + + /** start importing the forms of the given page + */ + void startPage( + const css::uno::Reference< css::drawing::XDrawPage >& _rxDrawPage); + + /** end importing the forms of the current page + */ + void endPage(); + + /** creates an import context for the office:forms element + */ + static SvXMLImportContext* createOfficeFormsContext( + SvXMLImport& _rImport); + + /** create an <type>SvXMLImportContext</type> instance which is able to import the <form:form> + element. + */ + SvXMLImportContext* createContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttribs); + + /** get the control with the given id + */ + css::uno::Reference< css::beans::XPropertySet > + lookupControlId(const OUString& _rControlId); + + /** announces the auto-style context to the form importer + */ + void setAutoStyleContext(SvXMLStylesContext* _pNewContext); + + /** to be called when the document has been completely imported + + <p>For some documents (currently: only some spreadsheet documents) it's necessary + do to a post processing, since not all information from the file can be processed + if the document is not completed, yet.</p> + */ + void documentDone( ); + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/logging.cxx b/xmloff/source/forms/logging.cxx new file mode 100644 index 000000000..6c2bb5922 --- /dev/null +++ b/xmloff/source/forms/logging.cxx @@ -0,0 +1,41 @@ +/* -*- 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 "logging.hxx" + +namespace xmloff +{ + +#ifdef TIMELOG + //= OStackedLogging + void OStackedLogging::enterContext( const char* _pContextName ) + { + m_aLogger.push( new ::rtl::Logfile( _pContextName ) ); + } + + void OStackedLogging::leaveTopContext( ) + { + delete m_aLogger.top(); + m_aLogger.pop(); + } +#endif + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/logging.hxx b/xmloff/source/forms/logging.hxx new file mode 100644 index 000000000..501b6c14e --- /dev/null +++ b/xmloff/source/forms/logging.hxx @@ -0,0 +1,59 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <stack> + +namespace rtl { class Logfile; } + +namespace xmloff +{ + +#ifdef TIMELOG + + //= OStackedLogging + class OStackedLogging + { + private: + ::std::stack< ::rtl::Logfile* > m_aLogger; + + protected: + OStackedLogging() { } + + protected: + void enterContext( const char* _pContextName ); + void leaveTopContext( ); + }; + +#define ENTER_LOG_CONTEXT( name ) enterContext( name ) +#define LEAVE_LOG_CONTEXT( ) leaveTopContext( ) + +#else + struct OStackedLogging { }; + +#define ENTER_LOG_CONTEXT( name ) +#define LEAVE_LOG_CONTEXT( ) + +#endif + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/officeforms.cxx b/xmloff/source/forms/officeforms.cxx new file mode 100644 index 000000000..07f2682d2 --- /dev/null +++ b/xmloff/source/forms/officeforms.cxx @@ -0,0 +1,177 @@ +/* -*- 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 "officeforms.hxx" + +#include <sax/tools/converter.hxx> + +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/namespacemap.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <comphelper/extract.hxx> +#include <tools/diagnose_ex.h> +#include "strings.hxx" + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::xml; + using ::xmloff::token::XML_FORMS; + using ::com::sun::star::xml::sax::XAttributeList; + using ::com::sun::star::xml::sax::XFastAttributeList; + + //= OFormsRootImport + OFormsRootImport::OFormsRootImport( SvXMLImport& rImport ) + :SvXMLImportContext(rImport) + { + } + + OFormsRootImport::~OFormsRootImport() + { + } + + css::uno::Reference< css::xml::sax::XFastContextHandler > OFormsRootImport::createFastChildContext( + sal_Int32 _nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList ) + { + SvXMLImportContext* pRet = nullptr; + try + { + pRet = GetImport().GetFormImport()->createContext( _nElement, xAttrList ); + } catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("xmloff.forms"); + } + return pRet; + } + + void OFormsRootImport::implImportBool(const Reference< XFastAttributeList >& _rxAttributes, OfficeFormsAttributes _eAttribute, + const Reference< XPropertySet >& _rxProps, const Reference< XPropertySetInfo >& _rxPropInfo, + const OUString& _rPropName, bool _bDefault) + { + // the complete attribute name to look for + sal_Int32 nCompleteAttributeName = XML_ELEMENT( + FORM, + OAttributeMetaData::getOfficeFormsAttributeToken(_eAttribute)); + + // get and convert the value + OUString sAttributeValue = _rxAttributes->getOptionalValue(nCompleteAttributeName); + bool bValue = _bDefault; + (void)::sax::Converter::convertBool(bValue, sAttributeValue); + + // set the property + if (_rxPropInfo->hasPropertyByName(_rPropName)) + { + _rxProps->setPropertyValue(_rPropName, Any(bValue)); + } + } + + void OFormsRootImport::startFastElement( sal_Int32 /*nElement*/, const Reference< XFastAttributeList >& _rxAttrList ) + { + ENTER_LOG_CONTEXT( "xmloff::OFormsRootImport - importing the complete tree" ); + + try + { + Reference< XPropertySet > xDocProperties(GetImport().GetModel(), UNO_QUERY); + if ( xDocProperties.is() ) + { // an empty model is allowed: when doing a copy'n'paste from e.g. Writer to Calc, + // this is done via streaming the controls as XML. + Reference< XPropertySetInfo > xDocPropInfo; + if (xDocProperties.is()) + xDocPropInfo = xDocProperties->getPropertySetInfo(); + + implImportBool(_rxAttrList, ofaAutomaticFocus, xDocProperties, xDocPropInfo, PROPERTY_AUTOCONTROLFOCUS, false); + implImportBool(_rxAttrList, ofaApplyDesignMode, xDocProperties, xDocPropInfo, PROPERTY_APPLYDESIGNMODE, true); + } + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.forms", + "caught an exception while setting the document properties!"); + } + } + + void OFormsRootImport::endFastElement(sal_Int32 ) + { + LEAVE_LOG_CONTEXT( ); + } + + //= OFormsRootExport + OFormsRootExport::OFormsRootExport( SvXMLExport& _rExp ) + { + addModelAttributes(_rExp); + + m_pImplElement.reset( new SvXMLElementExport(_rExp, XML_NAMESPACE_OFFICE, XML_FORMS, true, true) ); + } + + OFormsRootExport::~OFormsRootExport( ) + { + } + + void OFormsRootExport::implExportBool(SvXMLExport& _rExp, OfficeFormsAttributes _eAttribute, + const Reference< XPropertySet >& _rxProps, const Reference< XPropertySetInfo >& _rxPropInfo, + const OUString& _rPropName, bool _bDefault) + { + // retrieve the property value + bool bValue = _bDefault; + if (_rxPropInfo->hasPropertyByName(_rPropName)) + bValue = ::cppu::any2bool(_rxProps->getPropertyValue(_rPropName)); + + // convert into a string + OUStringBuffer aValue; + ::sax::Converter::convertBool(aValue, bValue); + + // add the attribute + _rExp.AddAttribute( + OAttributeMetaData::getOfficeFormsAttributeNamespace(), + OAttributeMetaData::getOfficeFormsAttributeName(_eAttribute), + aValue.makeStringAndClear()); + } + + void OFormsRootExport::addModelAttributes(SvXMLExport& _rExp) + { + try + { + Reference< XPropertySet > xDocProperties(_rExp.GetModel(), UNO_QUERY); + if ( xDocProperties.is() ) + { // an empty model is allowed: when doing a copy'n'paste from e.g. Writer to Calc, + // this is done via streaming the controls as XML. + Reference< XPropertySetInfo > xDocPropInfo; + if (xDocProperties.is()) + xDocPropInfo = xDocProperties->getPropertySetInfo(); + + implExportBool(_rExp, ofaAutomaticFocus, xDocProperties, xDocPropInfo, PROPERTY_AUTOCONTROLFOCUS, false); + implExportBool(_rExp, ofaApplyDesignMode, xDocProperties, xDocPropInfo, PROPERTY_APPLYDESIGNMODE, true); + } + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.forms", + "caught an exception while retrieving the document properties!"); + } + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/officeforms.hxx b/xmloff/source/forms/officeforms.hxx new file mode 100644 index 000000000..56694835c --- /dev/null +++ b/xmloff/source/forms/officeforms.hxx @@ -0,0 +1,92 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/beans/XPropertySet.hpp> + +#include "formattributes.hxx" +#include <xmloff/xmlictxt.hxx> +#include <memory> +#include "logging.hxx" + +class SvXMLElementExport; +class SvXMLExport; + +namespace xmloff +{ + + //= OFormsRootImport + class OFormsRootImport + :public SvXMLImportContext + ,public OStackedLogging + { + public: + + OFormsRootImport( SvXMLImport& _rImport); + virtual ~OFormsRootImport() override; + + // SvXMLImportContext overridable + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + private: + static void implImportBool( + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttributes, + OfficeFormsAttributes _eAttribute, + const css::uno::Reference< css::beans::XPropertySet >& _rxProps, + const css::uno::Reference< css::beans::XPropertySetInfo >& _rxPropInfo, + const OUString& _rPropName, + bool _bDefault + ); + }; + + //= OFormsRootExport + class OFormsRootExport + { + private: + std::unique_ptr<SvXMLElementExport> m_pImplElement; + + public: + explicit OFormsRootExport( SvXMLExport& _rExp ); + ~OFormsRootExport(); + + private: + static void addModelAttributes(SvXMLExport& _rExp); + + static void implExportBool( + SvXMLExport& _rExp, + OfficeFormsAttributes _eAttribute, + const css::uno::Reference< css::beans::XPropertySet >& _rxProps, + const css::uno::Reference< css::beans::XPropertySetInfo >& _rxPropInfo, + const OUString& _rPropName, + bool _bDefault + ); + + OFormsRootExport(const OFormsRootExport&) = delete; + OFormsRootExport& operator=(const OFormsRootExport&) = delete; + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/property_description.hxx b/xmloff/source/forms/property_description.hxx new file mode 100644 index 000000000..d30afcd29 --- /dev/null +++ b/xmloff/source/forms/property_description.hxx @@ -0,0 +1,103 @@ +/* -*- 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 . + */ + +#pragma once + +#include <forms/property_handler.hxx> +#include <xmloff/xmltoken.hxx> + +#include <vector> + +namespace xmloff +{ + + //= PropertyDescription + struct AttributeDescription + { + sal_uInt16 namespacePrefix; // usually XML_NAMESPACE_FORM + ::xmloff::token::XMLTokenEnum attributeToken; + + AttributeDescription() + :namespacePrefix( 0 ) + ,attributeToken( ::xmloff::token::XML_TOKEN_INVALID ) + { + } + + AttributeDescription( + const sal_uInt16 i_namespacePrefix, + const ::xmloff::token::XMLTokenEnum i_attributeToken + ) + :namespacePrefix( i_namespacePrefix ) + ,attributeToken( i_attributeToken ) + { + } + }; + + inline bool operator==( const AttributeDescription& i_lhs, const AttributeDescription& i_rhs ) + { + return ( i_lhs.namespacePrefix == i_rhs.namespacePrefix ) + && ( i_lhs.attributeToken == i_rhs.attributeToken ); + } + + //= PropertyDescription + struct PropertyDescription + { + /// is the name of the property + const OUString propertyName; + /** denotes the attribute which represents the property. Note that multiple properties might comprise a single + attribute value. + */ + /// is the factory for creating a handler for reading and writing the property + const PropertyHandlerFactory factory; + /// the unique ID of the property. The property meta data table must not contain two entries with the same property ID + const PropertyId propertyId; + const AttributeDescription attribute; + + PropertyDescription() + :propertyName() + ,factory( nullptr ) + ,propertyId( PID_INVALID ) + ,attribute() + { + } + + PropertyDescription( + const OUString& i_propertyName, + const sal_uInt16 i_namespacePrefix, + const ::xmloff::token::XMLTokenEnum i_attributeToken, + const PropertyHandlerFactory i_factory, + const PropertyId i_propertyId + ) + :propertyName( i_propertyName ) + ,factory( i_factory ) + ,propertyId( i_propertyId ) + ,attribute( i_namespacePrefix, i_attributeToken ) + { + } + }; + + //= PropertyDescriptionList + typedef ::std::vector< const PropertyDescription* > PropertyDescriptionList; + + //= PropertyGroups + typedef ::std::vector< PropertyDescriptionList > PropertyGroups; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/property_meta_data.cxx b/xmloff/source/forms/property_meta_data.cxx new file mode 100644 index 000000000..ce5760856 --- /dev/null +++ b/xmloff/source/forms/property_meta_data.cxx @@ -0,0 +1,156 @@ +/* -*- 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 "property_description.hxx" +#include "property_meta_data.hxx" +#include <forms/form_handler_factory.hxx> +#include "strings.hxx" +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlnamespace.hxx> + +#include <o3tl/hash_combine.hxx> +#include <tools/debug.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include <unordered_map> + +namespace xmloff::metadata +{ + + using namespace ::xmloff::token; + +#define FORM_SINGLE_PROPERTY( id, att ) \ + PropertyDescription( PROPERTY_##id, XML_NAMESPACE_FORM, att, &FormHandlerFactory::getFormPropertyHandler, PID_##id ) + + //= property meta data + namespace + { + const PropertyDescription* lcl_getPropertyMetaData() + { + static const PropertyDescription s_propertyMetaData[] = + { + FORM_SINGLE_PROPERTY( DATE_MIN, XML_MIN_VALUE ), + FORM_SINGLE_PROPERTY( DATE_MAX, XML_MAX_VALUE ), + FORM_SINGLE_PROPERTY( DEFAULT_DATE, XML_VALUE ), + FORM_SINGLE_PROPERTY( DATE, XML_CURRENT_VALUE ), + FORM_SINGLE_PROPERTY( TIME_MIN, XML_MIN_VALUE ), + FORM_SINGLE_PROPERTY( TIME_MAX, XML_MAX_VALUE ), + FORM_SINGLE_PROPERTY( DEFAULT_TIME, XML_VALUE ), + FORM_SINGLE_PROPERTY( TIME, XML_CURRENT_VALUE ), + + PropertyDescription() + }; + return s_propertyMetaData; + } + } + + namespace + { + // TODO: instead of having all of the below static, it should be some per-instance data. This way, the + // approach used here would scale much better. + // That is, if you have multiple "meta data instances", which manage a small, but closed set of properties, + // then looking through those multiple instances would probably be faster than searching within + // one big instance, since in this case, every instance can quickly decide whether it is responsible + // for some attribute or property, and otherwise delegate to the next instance. + + typedef std::unordered_map< OUString, const PropertyDescription* > DescriptionsByName; + + const DescriptionsByName& lcl_getPropertyDescriptions() + { + DBG_TESTSOLARMUTEX(); + static DescriptionsByName s_propertyDescriptionsByName; + if ( s_propertyDescriptionsByName.empty() ) + { + const PropertyDescription* desc = lcl_getPropertyMetaData(); + while ( !desc->propertyName.isEmpty() ) + { + s_propertyDescriptionsByName[ desc->propertyName ] = desc; + ++desc; + } + } + return s_propertyDescriptionsByName; + } + + typedef std::unordered_map< OUString, XMLTokenEnum > ReverseTokenLookup; + + struct AttributeHash + { + size_t operator()( const AttributeDescription& i_attribute ) const + { + std::size_t seed = 0; + o3tl::hash_combine(seed, i_attribute.attributeToken); + o3tl::hash_combine(seed, i_attribute.namespacePrefix); + return seed; + } + }; + + typedef std::unordered_map< AttributeDescription, PropertyGroups, AttributeHash > AttributesWithoutGroup; + + const AttributesWithoutGroup& lcl_getAttributesWithoutGroups() + { + DBG_TESTSOLARMUTEX(); + static AttributesWithoutGroup s_attributesWithoutGroup; + if ( s_attributesWithoutGroup.empty() ) + { + const PropertyDescription* desc = lcl_getPropertyMetaData(); + while ( !desc->propertyName.isEmpty() ) + { + PropertyDescriptionList singleElementList; + singleElementList.push_back( desc ); + + s_attributesWithoutGroup[ desc->attribute ].push_back( singleElementList ); + ++desc; + } + } + return s_attributesWithoutGroup; + } + } + + const PropertyDescription* getPropertyDescription( const OUString& i_propertyName ) + { + const DescriptionsByName& rAllDescriptions( lcl_getPropertyDescriptions() ); + DescriptionsByName::const_iterator pos = rAllDescriptions.find( i_propertyName ); + if ( pos != rAllDescriptions.end() ) + return pos->second; + return nullptr; + } + + void getPropertyGroupList( const AttributeDescription& i_attribute, PropertyGroups& o_propertyGroups ) + { + // the attribute is not used for any non-trivial group, which means it is mapped directly to + // a single property + const AttributesWithoutGroup& attributesWithoutGroups( lcl_getAttributesWithoutGroups() ); + const AttributesWithoutGroup::const_iterator pos = attributesWithoutGroups.find( i_attribute ); + if ( pos != attributesWithoutGroups.end() ) + o_propertyGroups = pos->second; + } + + AttributeDescription getAttributeDescription( sal_Int32 nAttributeToken ) + { + AttributeDescription attribute; + attribute.namespacePrefix = (nAttributeToken >> NMSP_SHIFT) - 1; + attribute.attributeToken = static_cast<XMLTokenEnum>(nAttributeToken & TOKEN_MASK); + return attribute; + } + +} // namespace xmloff::metadata + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/property_meta_data.hxx b/xmloff/source/forms/property_meta_data.hxx new file mode 100644 index 000000000..0d012fbc2 --- /dev/null +++ b/xmloff/source/forms/property_meta_data.hxx @@ -0,0 +1,42 @@ +/* -*- 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 . + */ + +#pragma once + +#include "property_description.hxx" + +namespace xmloff::metadata +{ + + const PropertyDescription* getPropertyDescription( const OUString& i_propertyName ); + + /** retrieves all known property groups which are mapped to the given attribute + */ + void getPropertyGroupList( + const AttributeDescription& i_attribute, + PropertyGroups& o_propertyGroups + ); + + /** retrieves the attribute descriptor for the attribute given by namespace prefix and attribute name + */ + AttributeDescription getAttributeDescription( sal_Int32 nElement ); + +} // namespace xmloff::metadata + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/propertyexport.cxx b/xmloff/source/forms/propertyexport.cxx new file mode 100644 index 000000000..79637d743 --- /dev/null +++ b/xmloff/source/forms/propertyexport.cxx @@ -0,0 +1,683 @@ +/* -*- 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 "propertyexport.hxx" + +#include <memory> + +#include <xmloff/xmlexp.hxx> +#include "strings.hxx" +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/xmlexppr.hxx> +#include <xmloff/xmlprmap.hxx> +#include <sax/tools/converter.hxx> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <comphelper/extract.hxx> +#include <comphelper/types.hxx> +#include "callbacks.hxx" +#include <unotools/datetime.hxx> +#include <tools/date.hxx> +#include <tools/datetime.hxx> + +namespace xmloff +{ + + using namespace css; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + + // NO using namespace ...util !!! + // need a tools Date/Time/DateTime below, which would conflict with the uno types then + + using namespace ::comphelper; + + //= OPropertyExport + OPropertyExport::OPropertyExport(IFormsExportContext& _rContext, const Reference< XPropertySet >& _rxProps) + :m_rContext(_rContext) + ,m_xProps(_rxProps) + ,m_xPropertyInfo( m_xProps->getPropertySetInfo() ) + ,m_xPropertyState( _rxProps, UNO_QUERY ) + { + // caching + OUStringBuffer aBuffer; + ::sax::Converter::convertBool(aBuffer, true); + m_sValueTrue = aBuffer.makeStringAndClear(); + ::sax::Converter::convertBool(aBuffer, false); + m_sValueFalse = aBuffer.makeStringAndClear(); + + OSL_ENSURE(m_xPropertyInfo.is(), "OPropertyExport::OPropertyExport: need an XPropertySetInfo!"); + + // collect the properties which need to be exported + examinePersistence(); + } + + bool OPropertyExport::shouldExportProperty( const OUString& i_propertyName ) const + { + // if the property state is DEFAULT, it does not need to be written - at least + // if it's a built-in property, and not a dynamically-added one. + bool bIsDefaultValue = m_xPropertyState.is() + && ( PropertyState_DEFAULT_VALUE == m_xPropertyState->getPropertyState( i_propertyName ) ); + bool bIsDynamicProperty = m_xPropertyInfo.is() + && ( ( m_xPropertyInfo->getPropertyByName( i_propertyName ).Attributes & PropertyAttribute::REMOVABLE ) != 0 ); + return ( !bIsDefaultValue || bIsDynamicProperty ); + } + + template< typename T > void + OPropertyExport::exportRemainingPropertiesSequence( + Any const & value, token::XMLTokenEnum eValueAttName) + { + css::uno::Sequence<T> anySeq; + bool bSuccess = value >>= anySeq; + assert(bSuccess); (void)bSuccess; + for (T const & i : std::as_const(anySeq)) + { + OUString sValue(implConvertAny(Any(i))); + AddAttribute(XML_NAMESPACE_OFFICE, eValueAttName, sValue ); + SvXMLElementExport aValueTag( + m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, + token::XML_LIST_VALUE, true, false); + } + } + + void OPropertyExport::exportRemainingProperties() + { + // the properties tag (will be created if we have at least one no-default property) + std::unique_ptr<SvXMLElementExport> pPropertiesTag; + + Any aValue; + OUString sValue; + + // loop through all the properties which are yet to be exported + for ( const auto& rProperty : m_aRemainingProps ) + { + DBG_CHECK_PROPERTY_NO_TYPE(rProperty); + + if ( !shouldExportProperty( rProperty ) ) + continue; + + // now that we have the first sub-tag we need the form:properties element + if (!pPropertiesTag) + pPropertiesTag = std::make_unique<SvXMLElementExport>(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, token::XML_PROPERTIES, true, true); + + // add the name attribute + AddAttribute(XML_NAMESPACE_FORM, token::XML_PROPERTY_NAME, rProperty); + + // get the value + aValue = m_xProps->getPropertyValue(rProperty); + + // the type to export + Type aExportType; + + // is it a sequence + bool bIsSequence = TypeClass_SEQUENCE == aValue.getValueTypeClass(); + // the type of the property, maybe reduced to the element type of a sequence + if (bIsSequence) + aExportType = getSequenceElementType( aValue.getValueType() ); + else + aExportType = aValue.getValueType(); + + // the type attribute + + bool bIsEmptyValue = TypeClass_VOID == aValue.getValueType().getTypeClass(); + if ( bIsEmptyValue ) + { + css::beans::Property aPropDesc = m_xPropertyInfo->getPropertyByName( rProperty ); + aExportType = aPropDesc.Type; + } + token::XMLTokenEnum eValueType = implGetPropertyXMLType( aExportType ); + + if ( bIsEmptyValue ) + AddAttribute( XML_NAMESPACE_OFFICE, token::XML_VALUE_TYPE, token::XML_VOID ); + else + AddAttribute( XML_NAMESPACE_OFFICE, token::XML_VALUE_TYPE, eValueType ); + + token::XMLTokenEnum eValueAttName( token::XML_VALUE ); + switch ( eValueType ) + { + case token::XML_BOOLEAN: eValueAttName = token::XML_BOOLEAN_VALUE; break; + case token::XML_STRING: eValueAttName = token::XML_STRING_VALUE; break; + default: break; + } + + if( !bIsSequence && !bIsEmptyValue ) + { // the simple case + + sValue = implConvertAny(aValue); + AddAttribute(XML_NAMESPACE_OFFICE, eValueAttName, sValue ); + } + + // start the property tag + SvXMLElementExport aValueTag1(m_rContext.getGlobalContext(), + XML_NAMESPACE_FORM, + bIsSequence ? token::XML_LIST_PROPERTY + : token::XML_PROPERTY, true, true); + + if (!bIsSequence) + continue; + + // the not-that-simple case, we need to iterate through the sequence elements + switch ( aExportType.getTypeClass() ) + { + case TypeClass_STRING: + exportRemainingPropertiesSequence< OUString >( + aValue, eValueAttName); + break; + case TypeClass_DOUBLE: + exportRemainingPropertiesSequence< double >( + aValue, eValueAttName); + break; + case TypeClass_BOOLEAN: + exportRemainingPropertiesSequence< sal_Bool >( + aValue, eValueAttName); + break; + case TypeClass_BYTE: + exportRemainingPropertiesSequence< sal_Int8 >( + aValue, eValueAttName); + break; + case TypeClass_SHORT: + exportRemainingPropertiesSequence< sal_Int16 >( + aValue, eValueAttName); + break; + case TypeClass_LONG: + exportRemainingPropertiesSequence< sal_Int32 >( + aValue, eValueAttName); + break; + case TypeClass_HYPER: + exportRemainingPropertiesSequence< sal_Int64 >( + aValue, eValueAttName); + break; + default: + OSL_FAIL("OPropertyExport::exportRemainingProperties: unsupported sequence type !"); + break; + } + } + } + + void OPropertyExport::examinePersistence() + { + m_aRemainingProps.clear(); + const Sequence< Property > aProperties = m_xPropertyInfo->getProperties(); + for (const auto& rProp : aProperties) + { + // no transient props + if ( rProp.Attributes & PropertyAttribute::TRANSIENT ) + continue; + // no read-only props + if ( ( rProp.Attributes & PropertyAttribute::READONLY ) != 0 ) + // except they're dynamically added + if ( ( rProp.Attributes & PropertyAttribute::REMOVABLE ) == 0 ) + continue; + m_aRemainingProps.insert(rProp.Name); + } + } + + void OPropertyExport::exportStringPropertyAttribute( const sal_uInt16 _nNamespaceKey, const OUString& _pAttributeName, + const OUString& _rPropertyName ) + { + DBG_CHECK_PROPERTY( _rPropertyName, OUString ); + + // no try-catch here, this would be too expensive. The outer scope has to handle exceptions (which should not + // happen if we're used correctly :) + + // this is way simple, as we don't need to convert anything (the property already is a string) + + // get the string + OUString sPropValue; + m_xProps->getPropertyValue( _rPropertyName ) >>= sPropValue; + + // add the attribute + if ( !sPropValue.isEmpty() ) + AddAttribute( _nNamespaceKey, _pAttributeName, sPropValue ); + + // the property does not need to be handled anymore + exportedProperty( _rPropertyName ); + } + + void OPropertyExport::exportBooleanPropertyAttribute(const sal_uInt16 _nNamespaceKey, const OUString& _pAttributeName, + const OUString& _rPropertyName, const BoolAttrFlags _nBooleanAttributeFlags) + { + DBG_CHECK_PROPERTY_NO_TYPE( _rPropertyName ); + // no check of the property value type: this method is allowed to be called with any integer properties + // (e.g. sal_Int32, sal_uInt16 etc) + + bool bDefault(BoolAttrFlags::DefaultTrue & _nBooleanAttributeFlags); + bool bDefaultVoid(BoolAttrFlags::DefaultVoid & _nBooleanAttributeFlags); + + // get the value + bool bCurrentValue = bDefault; + Any aCurrentValue = m_xProps->getPropertyValue( _rPropertyName ); + if (aCurrentValue.hasValue()) + { + bCurrentValue = ::cppu::any2bool(aCurrentValue); + // this will extract a boolean value even if the Any contains a int or short or something like that ... + + if (_nBooleanAttributeFlags & BoolAttrFlags::InverseSemantics) + bCurrentValue = !bCurrentValue; + + // we have a non-void current value + if (bDefaultVoid || (bDefault != bCurrentValue)) + // and (the default is void, or the non-void default does not equal the current value) + // -> write the attribute + AddAttribute(_nNamespaceKey, _pAttributeName, bCurrentValue ? m_sValueTrue : m_sValueFalse); + } + else + // we have a void current value + if (!bDefaultVoid) + // and we have a non-void default + // -> write the attribute + AddAttribute(_nNamespaceKey, _pAttributeName, bCurrentValue ? m_sValueTrue : m_sValueFalse); + + // the property does not need to be handled anymore + exportedProperty( _rPropertyName ); + } + + void OPropertyExport::exportInt16PropertyAttribute(const sal_uInt16 _nNamespaceKey, const OUString& _pAttributeName, + const OUString& _rPropertyName, const sal_Int16 _nDefault, bool force) + { + DBG_CHECK_PROPERTY( _rPropertyName, sal_Int16 ); + + // get the value + sal_Int16 nCurrentValue(_nDefault); + m_xProps->getPropertyValue( _rPropertyName ) >>= nCurrentValue; + + // add the attribute + if (force || _nDefault != nCurrentValue) + { + // let the formatter of the export context build a string + AddAttribute(_nNamespaceKey, _pAttributeName, OUString::number(nCurrentValue)); + } + + // the property does not need to be handled anymore + exportedProperty( _rPropertyName ); + } + + void OPropertyExport::exportInt32PropertyAttribute( const sal_uInt16 _nNamespaceKey, const OUString& _pAttributeName, + const OUString& _rPropertyName, const sal_Int32 _nDefault ) + { + DBG_CHECK_PROPERTY( _rPropertyName, sal_Int32 ); + + // get the value + sal_Int32 nCurrentValue( _nDefault ); + m_xProps->getPropertyValue( _rPropertyName ) >>= nCurrentValue; + + // add the attribute + if ( _nDefault != nCurrentValue ) + { + // let the formatter of the export context build a string + AddAttribute( _nNamespaceKey, _pAttributeName, OUString::number(nCurrentValue) ); + } + + // the property does not need to be handled anymore + exportedProperty( _rPropertyName ); + } + + void OPropertyExport::exportEnumPropertyAttributeImpl( + const sal_uInt16 _nNamespaceKey, const OUString& _pAttributeName, + const OUString &rPropertyName, const SvXMLEnumMapEntry<sal_uInt16>* _pValueMap, + const sal_uInt16 _nDefault, const bool _bVoidDefault) + { + // get the value + Any aValue = m_xProps->getPropertyValue(rPropertyName); + + if (aValue.hasValue()) + { // we have a non-void current value + sal_Int32 nCurrentValue(_nDefault); + ::cppu::enum2int(nCurrentValue, aValue); + + // add the attribute + if ((_nDefault != nCurrentValue) || _bVoidDefault) + { // the default does not equal the value, or the default is void and the value isn't + + // let the formatter of the export context build a string + OUStringBuffer sBuffer; + SvXMLUnitConverter::convertEnum(sBuffer, static_cast<sal_uInt16>(nCurrentValue), _pValueMap); + + AddAttribute(_nNamespaceKey, _pAttributeName, sBuffer.makeStringAndClear()); + } + } + else + { + if (!_bVoidDefault) + AddAttribute(_nNamespaceKey, _pAttributeName, OUString()); + } + + // the property does not need to be handled anymore + exportedProperty(rPropertyName); + } + + void OPropertyExport::exportTargetFrameAttribute() + { + DBG_CHECK_PROPERTY( PROPERTY_TARGETFRAME, OUString ); + + OUString sTargetFrame = comphelper::getString(m_xProps->getPropertyValue(PROPERTY_TARGETFRAME)); + if( sTargetFrame != "_blank" ) + { // an empty string and "_blank" have the same meaning and don't have to be written + AddAttribute(OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::TargetFrame) + ,OAttributeMetaData::getCommonControlAttributeName(CCAFlags::TargetFrame) + ,sTargetFrame); + } + + exportedProperty(PROPERTY_TARGETFRAME); + } + + void OPropertyExport::exportRelativeTargetLocation(const OUString& _sPropertyName,CCAFlags _nProperty,bool _bAddType) + { + Any aAny = m_xProps->getPropertyValue(_sPropertyName); + + OUString sTargetLocation; + if (aAny.has<uno::Reference<graphic::XGraphic>>()) + { + auto xGraphic = aAny.get<uno::Reference<graphic::XGraphic>>(); + OUString sOutMimeType; + sTargetLocation = m_rContext.getGlobalContext().AddEmbeddedXGraphic(xGraphic, sOutMimeType); + } + else if (aAny.has<OUString>()) + { + auto sURL = aAny.get<OUString>(); + sTargetLocation = m_rContext.getGlobalContext().AddEmbeddedObject(sURL); + } + else + { + SAL_WARN("xmloff.forms", "OPropertyExport::exportRelativeTargetLocation: " + "Value of " << _sPropertyName << " not found!"); + } + + if (!sTargetLocation.isEmpty()) + { + AddAttribute(OAttributeMetaData::getCommonControlAttributeNamespace(_nProperty) + ,OAttributeMetaData::getCommonControlAttributeName(_nProperty) + , sTargetLocation); + + // #i110911# add xlink:type="simple" if required + if (_bAddType) + AddAttribute(XML_NAMESPACE_XLINK, token::XML_TYPE, token::XML_SIMPLE); + + exportedProperty(_sPropertyName); + } + } + void OPropertyExport::flagStyleProperties() + { + // flag all the properties which are part of the style as "handled" + rtl::Reference< XMLPropertySetMapper > xStylePropertiesSupplier = m_rContext.getStylePropertyMapper()->getPropertySetMapper(); + for (sal_Int32 i=0; i<xStylePropertiesSupplier->GetEntryCount(); ++i) + exportedProperty(xStylePropertiesSupplier->GetEntryAPIName(i)); + + // the font properties are exported as single properties, but there is a FontDescriptor property which + // collects them all-in-one, this has been exported implicitly + exportedProperty(PROPERTY_FONT); + + // for the DateFormat and TimeFormat, there exist wrapper properties which has been exported as + // style, too + exportedProperty(PROPERTY_DATEFORMAT); + exportedProperty(PROPERTY_TIMEFORMAT); + + // the following properties should have been exported at the shape already: + exportedProperty( "VerticalAlign" ); + exportedProperty( "WritingMode" ); + exportedProperty( "ScaleMode" ); + // ditto the TextWritingMode + exportedProperty( "WritingMode" ); + } + + void OPropertyExport::exportGenericPropertyAttribute( + const sal_uInt16 _nAttributeNamespaceKey, const OUString& _pAttributeName, const char* _pPropertyName) + { + DBG_CHECK_PROPERTY_ASCII_NO_TYPE( _pPropertyName ); + + OUString sPropertyName = OUString::createFromAscii(_pPropertyName); + exportedProperty(sPropertyName); + + Any aCurrentValue = m_xProps->getPropertyValue(sPropertyName); + if (!aCurrentValue.hasValue()) + // nothing to do without a concrete value + return; + + OUString sValue = implConvertAny(aCurrentValue); + if (sValue.isEmpty() && (TypeClass_STRING == aCurrentValue.getValueTypeClass())) + { + // check whether or not the property is allowed to be VOID + Property aProperty = m_xPropertyInfo->getPropertyByName(sPropertyName); + if ((aProperty.Attributes & PropertyAttribute::MAYBEVOID) == 0) + // the string is empty, and the property is not allowed to be void + // -> don't need to write the attribute, 'cause missing it is unambiguous + return; + } + + // finally add the attribute to the context + AddAttribute(_nAttributeNamespaceKey, _pAttributeName, sValue); + } + + void OPropertyExport::exportStringSequenceAttribute(const sal_uInt16 _nAttributeNamespaceKey, const OUString& _pAttributeName, + const OUString& _rPropertyName) + { + const sal_Unicode _aListSeparator = ','; + const sal_Unicode _aQuoteCharacter = '"'; + DBG_CHECK_PROPERTY( _rPropertyName, Sequence< OUString > ); + + Sequence< OUString > aItems; + m_xProps->getPropertyValue( _rPropertyName ) >>= aItems; + + OUStringBuffer sFinalList; + + // unfortunately the OUString can't append single sal_Unicode characters ... + const OUString sQuote(&_aQuoteCharacter, 1); + const OUString sSeparator(&_aListSeparator, 1); + const bool bQuote = !sQuote.isEmpty(); + + // concatenate the string items + const OUString* pItems = aItems.getConstArray(); + const OUString* pEnd = pItems + aItems.getLength(); + const OUString* pLastElement = pEnd - 1; + for ( ; + pItems != pEnd; + ++pItems + ) + { + OSL_ENSURE(-1 == pItems->indexOf(_aQuoteCharacter), + "OPropertyExport::exportStringSequenceAttribute: there is an item which contains the quote character!"); + + if (bQuote) + sFinalList.append(sQuote); + sFinalList.append(*pItems); + if (bQuote) + sFinalList.append(sQuote); + + if (pItems != pLastElement) + sFinalList.append(sSeparator); + } + + if (!sFinalList.isEmpty()) + AddAttribute(_nAttributeNamespaceKey, _pAttributeName, sFinalList.makeStringAndClear()); + + exportedProperty( _rPropertyName ); + } + + OUString OPropertyExport::implConvertAny(const Any& _rValue) + { + OUStringBuffer aBuffer; + switch (_rValue.getValueTypeClass()) + { + case TypeClass_STRING: + { // extract the string + OUString sCurrentValue; + _rValue >>= sCurrentValue; + aBuffer.append(sCurrentValue); + } + break; + case TypeClass_DOUBLE: + // let the unit converter format is as string + ::sax::Converter::convertDouble(aBuffer, getDouble(_rValue)); + break; + case TypeClass_BOOLEAN: + aBuffer = getBOOL(_rValue) ? m_sValueTrue : m_sValueFalse; + break; + case TypeClass_BYTE: + case TypeClass_UNSIGNED_SHORT: + case TypeClass_SHORT: + case TypeClass_LONG: + // let the unit converter format is as string + aBuffer.append(getINT32(_rValue)); + break; + case TypeClass_UNSIGNED_LONG: + case TypeClass_HYPER: + aBuffer.append(getINT64(_rValue)); + break; + case TypeClass_UNSIGNED_HYPER: + aBuffer.append(static_cast<sal_Int64>(_rValue.get<sal_uInt64>())); + break; + case TypeClass_ENUM: + { + // convert it into an int32 + sal_Int32 nValue = 0; + ::cppu::enum2int(nValue, _rValue); + aBuffer.append(nValue); + } + break; + default: + { // hmmm... what else do we know? + double fValue = 0; + css::util::Date aDate; + css::util::Time aTime; + css::util::DateTime aDateTime; + if (_rValue >>= aDate) + { + Date aToolsDate( Date::EMPTY ); + ::utl::typeConvert(aDate, aToolsDate); + fValue = aToolsDate.GetDate(); + } + else if (_rValue >>= aTime) + { + fValue = aTime.Hours / static_cast<double>(::tools::Time::hourPerDay) + + aTime.Minutes / static_cast<double>(::tools::Time::minutePerDay) + + aTime.Seconds / static_cast<double>(::tools::Time::secondPerDay) + + aTime.NanoSeconds / static_cast<double>(::tools::Time::nanoSecPerDay); + } + else if (_rValue >>= aDateTime) + { + DateTime aToolsDateTime( DateTime::EMPTY ); + ::utl::typeConvert(aDateTime, aToolsDateTime); + // the time part (the digits behind the comma) + fValue = aTime.Hours / static_cast<double>(::tools::Time::hourPerDay) + + aTime.Minutes / static_cast<double>(::tools::Time::minutePerDay) + + aTime.Seconds / static_cast<double>(::tools::Time::secondPerDay) + + aTime.NanoSeconds / static_cast<double>(::tools::Time::nanoSecPerDay); + // plus the data part (the digits in front of the comma) + fValue += aToolsDateTime.GetDate(); + } + else + { + // if any other types are added here, please remember to adjust implGetPropertyXMLType accordingly + + // no more options ... + OSL_FAIL("OPropertyExport::implConvertAny: unsupported value type!"); + break; + } + // let the unit converter format is as string + ::sax::Converter::convertDouble(aBuffer, fValue); + } + break; + } + + return aBuffer.makeStringAndClear(); + } + + token::XMLTokenEnum OPropertyExport::implGetPropertyXMLType(const css::uno::Type& _rType) + { + // handle the type description + switch (_rType.getTypeClass()) + { + case TypeClass_STRING: + return token::XML_STRING; + case TypeClass_DOUBLE: + case TypeClass_BYTE: + case TypeClass_SHORT: + case TypeClass_LONG: + case TypeClass_HYPER: + case TypeClass_ENUM: + return token::XML_FLOAT; + case TypeClass_BOOLEAN: + return token::XML_BOOLEAN; + + default: + return token::XML_FLOAT; + } + } + +#ifdef DBG_UTIL + void OPropertyExport::AddAttribute( sal_uInt16 _nPrefix, const OUString& _rName, const OUString& _rValue ) + { + OSL_ENSURE(m_rContext.getGlobalContext().GetXAttrList()->getValueByName( _rName ).isEmpty(), + "OPropertyExport::AddAttribute: already have such an attribute"); + + m_rContext.getGlobalContext().AddAttribute( _nPrefix, _rName, _rValue ); + } + + void OPropertyExport::AddAttribute(sal_uInt16 _nPrefix, ::xmloff::token::XMLTokenEnum _eName, const OUString& _rValue) + { + OSL_ENSURE(m_rContext.getGlobalContext().GetXAttrList()->getValueByName(::xmloff::token::GetXMLToken(_eName)).isEmpty(), + "OPropertyExport::AddAttribute: already have such an attribute"); + + m_rContext.getGlobalContext().AddAttribute(_nPrefix, _eName, _rValue); + } + + void OPropertyExport::AddAttribute(sal_uInt16 _nPrefix, ::xmloff::token::XMLTokenEnum _eName, ::xmloff::token::XMLTokenEnum _eValue ) + { + OSL_ENSURE(m_rContext.getGlobalContext().GetXAttrList()->getValueByName(::xmloff::token::GetXMLToken(_eName)).isEmpty(), + "OPropertyExport::AddAttribute: already have such an attribute"); + + m_rContext.getGlobalContext().AddAttribute(_nPrefix, _eName, _eValue); + } + + void OPropertyExport::dbg_implCheckProperty(const OUString& _rPropertyName, const Type* _pType) + { + try + { + // the property must exist + if (!m_xPropertyInfo->hasPropertyByName(_rPropertyName)) + { + SAL_WARN("xmloff.forms", "OPropertyExport: " + "no property with the name " + _rPropertyName + "!"); + return; + } + + if (_pType) + { + // and it must have the correct type + Property aPropertyDescription = m_xPropertyInfo->getPropertyByName(_rPropertyName); + OSL_ENSURE(aPropertyDescription.Type.equals(*_pType), "OPropertyExport::dbg_implCheckProperty: invalid property type!"); + } + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.forms", "could not check the property!"); + } + } +#endif // DBG_UTIL - dbg_implCheckProperty + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/propertyexport.hxx b/xmloff/source/forms/propertyexport.hxx new file mode 100644 index 000000000..b848ab6e4 --- /dev/null +++ b/xmloff/source/forms/propertyexport.hxx @@ -0,0 +1,413 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <set> + +#include "formattributes.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlexp.hxx> +#include "callbacks.hxx" +#include "strings.hxx" + +enum class BoolAttrFlags { + DefaultFalse = 0x00, + DefaultTrue = 0x01, + DefaultVoid = 0x02, + InverseSemantics = 0x04, +}; +namespace o3tl { + template<> struct typed_flags<BoolAttrFlags> : is_typed_flags<BoolAttrFlags, 0x07> {}; +} + +namespace xmloff +{ + + // if sal_True, indicates that the semantic of the property referred by <arg>_pPropertyName</arg> + // is inverse to the semantic of the XML attribute.<br/> + // I.e. if the property value is <TRUE/>, <FALSE/> has to be written and vice versa. + // <p>Be careful with <arg>_bDefault</arg> and <arg>_bInverseSemantics</arg>: if <arg>_bInverseSemantics</arg> + // is <TRUE/>, the current property value is inverted <em>before</em> comparing it to the default.</p> + + class IFormsExportContext; + //= OPropertyExport + /** provides export related tools for attribute handling + + <p>(The name is somewhat misleading. It's not only a PropertyExport, but in real an ElementExport. + Anyway.)</p> + */ + class OPropertyExport + { + private: + std::set<OUString> m_aRemainingProps; + // see examinePersistence + + void exportRelativeTargetLocation(const OUString& _sPropertyName, CCAFlags _nProperty,bool _bAddType); + + protected: + IFormsExportContext& m_rContext; + + const css::uno::Reference< css::beans::XPropertySet > + m_xProps; + const css::uno::Reference< css::beans::XPropertySetInfo > + m_xPropertyInfo; + const css::uno::Reference< css::beans::XPropertyState > + m_xPropertyState; + + // caching + OUString m_sValueTrue; + OUString m_sValueFalse; + + public: + /** constructs an object capable of handling attributes for export + @param _rContext + the export context to which's attribute list the property translation should be added + @param m_xControl + the property set to be exported + */ + OPropertyExport(IFormsExportContext& _rContext, + const css::uno::Reference< css::beans::XPropertySet >& _rxProps); + + protected: + /** examines a property set given for all properties which's value are to made persistent + + <p>upon return the <method>m_aRemainingProps</method> will be filled with the names of all properties + which need to be stored</p> + */ + void examinePersistence(); + + template< typename T > void exportRemainingPropertiesSequence( + css::uno::Any const & value, + token::XMLTokenEnum eValueAttName); + + void exportRemainingProperties(); + + /** indicates that a property has been handled by a derived class, without using the helper methods of this + class. + + <p>Calling this method is necessary in case you use the suggested mechanism for the generic export of + properties. This means that you want to use <method>exportRemainingProperties</method>, which exports + all properties which need to ('cause they haven't been exported with one of the other type-specific + methods).</p> + + <p>In this case you should call exportedProperty for every property you export yourself, so the property + will be flagged as <em>already handled</em></p> + */ + void exportedProperty(const OUString& _rPropertyName) + { m_aRemainingProps.erase(_rPropertyName); } + + /** add an attribute which is represented by a string property to the export context + + @param _nNamespaceKey + the key of the namespace to use for the attribute name. Is used with the namespace map + provided by the export context. + @param _pAttributeName + the name of the attribute to add. Must not contain any namespace + @param _pPropertyName + the name of the property to ask the control for + */ + void exportStringPropertyAttribute( + const sal_uInt16 _nNamespaceKey, + const OUString& _pAttributeName, + const OUString& _rPropertyName + ); + + /** add an attribute which is represented by a boolean property to the export context + + @param _nNamespaceKey + the key of the namespace to use for the attribute name. Is used with the namespace map + provided by the export context. + @param _pAttributeName + the name of the attribute to add. Must not contain any namespace (it's added automatically) + @param _pPropertyName + the name of the property to ask the control for + @param _nBooleanAttributeFlags + specifies the default and the "alignment" (inverse semantics) of the boolean property + */ + void exportBooleanPropertyAttribute( + const sal_uInt16 _nNamespaceKey, + const OUString& _pAttributeName, + const OUString& _rPropertyName, + const BoolAttrFlags _nBooleanAttributeFlags); + + /** add an attribute which is represented by a sal_Int16 property to the export context + + @param _nNamespaceKey + the key of the namespace to use for the attribute name. Is used with the namespace map + provided by the export context. + @param _pAttributeName + the name of the attribute to add. Must not contain any namespace (it's added automatically) + @param _pPropertyName + the name of the property to ask the control for + @param _nDefault + the default of the attribute. See force parameter. + @param force + if true and the property is not set or does not contain a sal_Int16, + then _nDefault is written out. + if false and the current property value equals _nDefault, + then no attribute is added. + */ + void exportInt16PropertyAttribute( + const sal_uInt16 _nNamespaceKey, + const OUString& _pAttributeName, + const OUString& _rPropertyName, + const sal_Int16 _nDefault, + const bool force = false); + + /** add an attribute which is represented by a sal_Int32 property to the export context + + @param _nNamespaceKey + the key of the namespace to use for the attribute name. Is used with the namespace map + provided by the export context. + @param _pAttributeName + the name of the attribute to add. Must not contain any namespace (it's added automatically) + @param _pPropertyName + the name of the property to ask the control for + @param _nDefault + the default of the attribute. If the current property value equals this default, no + attribute is added. + */ + void exportInt32PropertyAttribute( + const sal_uInt16 _nNamespaceKey, + const OUString& _pAttributeName, + const OUString& _rPropertyName, + const sal_Int32 _nDefault); + + /** add an attribute which is represented by an enum property to the export context + + @param _nNamespaceKey + the key of the namespace to use for the attribute name. Is used with the namespace map + provided by the export context. + @param _pAttributeName + the name of the attribute to add. Must not contain any namespace (it's added automatically) + @param _pPropertyName + the name of the property to ask the control for + @param _pValueMap + the map to use when converting the property value to an attribute value + @param _nDefault + the default of the attribute. If the current property value equals this default, no + attribute is added. + */ + template<typename EnumT> + void exportEnumPropertyAttribute( + const sal_uInt16 _nNamespaceKey, + const OUString& _pAttributeName, + const OUString& _rPropertyName, + const SvXMLEnumMapEntry<EnumT>* _pValueMap, + const EnumT _nDefault, + const bool _bVoidDefault = false) + { + exportEnumPropertyAttributeImpl(_nNamespaceKey, _pAttributeName, _rPropertyName, + reinterpret_cast<const SvXMLEnumMapEntry<sal_uInt16>*>(_pValueMap), + static_cast<sal_Int16>(_nDefault), _bVoidDefault); + } + void exportEnumPropertyAttributeImpl( + const sal_uInt16 _nNamespaceKey, + const OUString& _pAttributeName, + const OUString& _rPropertyName, + const SvXMLEnumMapEntry<sal_uInt16>* _pValueMap, + const sal_uInt16 _nDefault, + const bool _bVoidDefault); + + // some very special methods for some very special attribute/property pairs + + /** add the hlink:target-frame attribute to the export context. + + <p>The value of this attribute is extracted from the TargetFrame property of the object given.</p> + + <p>The property needs a special handling because conflicts between the default values for the attribute + and the property.</p> + */ + void exportTargetFrameAttribute(); + + /** add the form:href attribute to the export context. + + <p>The value of this attribute is extracted from the TargetURL property of the object given.</p> + + <p>The property needs a special handling because the URL's need to be made relative</p> + + <p>If _bAddType is set, an additional xlink:type="simple" attribute is also added.</p> + */ + void exportTargetLocationAttribute(bool _bAddType) { exportRelativeTargetLocation(PROPERTY_TARGETURL,CCAFlags::TargetLocation,_bAddType); } + + /** add the form:image attribute to the export context. + + <p>The value of this attribute is extracted from the ImageURL property of the object given.</p> + + <p>The property needs a special handling because the URL's need to be made relative</p> + */ + void exportImageDataAttribute() { exportRelativeTargetLocation(PROPERTY_GRAPHIC, CCAFlags::ImageData, false); } + + /** flag the style properties as 'already exported' + + <p>We don't have style support right now, so the only thing the method does is removing the style-relevant + properties from the list of yet-to-be-exported properties (<member>m_aRemainingProps</member>)</p> + */ + void flagStyleProperties(); + + /** add an arbitrary attribute extracted from an arbitrary property to the export context + + <p>The current value of the property specified with <arg>_pPropertyName</arg> is taken and converted + into a string, no matter what type it has. (Okay, there are the usual limitations: We know Date, Datetime, + double, integer ... to name just a few).</p> + + <p>In case the property value is <NULL/> (void), no attribute is added</p> + + <p>In case the property value is an empty string, and the property is a not allowed to be <NULL/> (void), + no attribute is added</p> + + <p>In case the property value is a sequence of any type, no attribute is added, 'cause sequences can't be + transported as attribute. In the debug version, an additional assertion will occur if you nonetheless try + to do this.</p> + + @param _nNamespaceKey + the key of the namespace to use for the attribute name. Is used with the namespace map + provided by the export context. + @param _pAttributeName + the name of the attribute to add. Must not contain any namespace (it's added automatically) + @param _pPropertyName + the name of the property to ask the object for + */ + void exportGenericPropertyAttribute( + const sal_uInt16 _nAttributeNamespaceKey, + const OUString& _pAttributeName, + const char* _pPropertyName); + + /** exports a property value, which is a string sequence, as attribute + + <p>The elements of the string sequence given are quoted and concatenated, with the characters used for + this to be chosen by the caller</p> + + <p>If you use the quote character, no check (except assertions) is made if one of the list items + contains the quote character</p> + + <p>If you don't use the quote character, no check (except assertions) is made if one of the list items + contains the separator character (which would be deadly when reimporting the string)</p> + + @param _nNamespaceKey + the key of the namespace to use for the attribute name. Is used with the namespace map + provided by the export context. + @param _pAttributeName + the name of the attribute to add. Must not contain any namespace (it's added automatically) + @param _pPropertyName + the name of the property to ask the object for + */ + void exportStringSequenceAttribute( + const sal_uInt16 _nAttributeNamespaceKey, + const OUString& _pAttributeName, + const OUString& _rPropertyName); + + /** determines whether the given property is to be exported + + <p>Currently, the method simply checks whether the property's state is <em>not</em> PropertyState.DEFAULT, + or whether the property is a dynamic property (i.e. added via an <code>XPropertyContainer</code>). + So, take care when using the method - the heuristics is not applicable for all properties.</p> + */ + bool shouldExportProperty( const OUString& i_propertyName ) const; + + /** tries to convert an arbitrary <type scope="com.sun:star.uno">Any</type> into an string + + <p>If the type contained in the Any is not supported, the returned string will be empty. In the + debug version, an additional assertion occurs.</p> + + @param _rValue + the value to convert + */ + OUString implConvertAny( + const css::uno::Any& _rValue); + + /** + @return + token which can be used in the <code>form:property</code> element's <code>type</code> attribute + to describe the type of a value.<br/> + Possible types returned are + <ul> + <li><b>boolean</b>: <arg>_rValue</arg> was interpreted as boolean value before converting + it into a string</li> + <li><b>float</b>: <arg>_rValue</arg> was interpreted as 64 bit floating point 16bit integer, 32bit integer or 64 bit integer value before + converting it into a string</li> + <li><b>string</b>: <arg>_rValue</arg> did not need any conversion as it already was a string</li> + </ul> + If the type is not convertible, float is returned + */ + static ::xmloff::token::XMLTokenEnum implGetPropertyXMLType(const css::uno::Type& _rType); + +#ifdef DBG_UTIL + void AddAttribute( sal_uInt16 _nPrefix, const OUString& _rName, const OUString& _rValue ); + void AddAttribute(sal_uInt16 _nPrefix, ::xmloff::token::XMLTokenEnum _eName, const OUString& _rValue); + void AddAttribute(sal_uInt16 _nPrefix, ::xmloff::token::XMLTokenEnum _eName, ::xmloff::token::XMLTokenEnum _eValue ); +#else + // in the product version, inline this, so it does not cost us extra time calling into our method + void AddAttribute( sal_uInt16 _nPrefix, const OUString& _rName, const OUString& _rValue ) + { m_rContext.getGlobalContext().AddAttribute( _nPrefix, _rName, _rValue ); } + void AddAttribute(sal_uInt16 _nPrefix, ::xmloff::token::XMLTokenEnum _eName, const OUString& _rValue) + { m_rContext.getGlobalContext().AddAttribute(_nPrefix, _eName, _rValue); } + void AddAttribute(sal_uInt16 _nPrefix, ::xmloff::token::XMLTokenEnum _eName, ::xmloff::token::XMLTokenEnum _eValue ) + { m_rContext.getGlobalContext().AddAttribute(_nPrefix, _eName, _eValue); } +#endif + +#ifdef DBG_UTIL + protected: + /** check a given property set for the existence and type correctness of a given property + + <p>This method is available in the non-product version only.</p> + + @param _rPropertyName + the name of the property to ask the control model for + @param _pType + the expected type of the property. May be NULL, in this case no type check is made. + @return sal_True, if the property exists and is of the correct type + */ + void dbg_implCheckProperty( + const OUString& _rPropertyName, + const css::uno::Type* _pType); + +// void dbg_implCheckProperty( +// const char* _rPropertyName, +// const css::uno::Type* _pType) +// { +// dbg_implCheckProperty(OUString::createFromAscii(_rPropertyName), _pType); +// } +#endif + }; + + //= helper +#ifdef DBG_UTIL + #define DBG_CHECK_PROPERTY(name, type) \ + dbg_implCheckProperty(name, &cppu::UnoType<type>::get()) + + #define DBG_CHECK_PROPERTY_NO_TYPE(name) \ + dbg_implCheckProperty(name, nullptr) + + #define DBG_CHECK_PROPERTY_ASCII_NO_TYPE( name ) \ + dbg_implCheckProperty( OUString::createFromAscii( name ), nullptr ) +#else + #define DBG_CHECK_PROPERTY(name, type) + #define DBG_CHECK_PROPERTY_NO_TYPE(name) + #define DBG_CHECK_PROPERTY_ASCII_NO_TYPE( name ) +#endif + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/propertyimport.cxx b/xmloff/source/forms/propertyimport.cxx new file mode 100644 index 000000000..ded9fa856 --- /dev/null +++ b/xmloff/source/forms/propertyimport.cxx @@ -0,0 +1,524 @@ +/* -*- 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 <sal/config.h> + +#include <cmath> + +#include "propertyimport.hxx" + +#include <sax/tools/converter.hxx> + +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/namespacemap.hxx> +#include <o3tl/temporary.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <comphelper/extract.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <tools/date.hxx> +#include <tools/time.hxx> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <unotools/datetime.hxx> +#include <rtl/strbuf.hxx> + +using namespace ::xmloff::token; + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::xml; + using ::com::sun::star::xml::sax::XAttributeList; + using ::com::sun::star::xml::sax::XFastAttributeList; + + // NO using namespace ...util !!! + // need a tools Date/Time/DateTime below, which would conflict with the uno types then + +#define TYPE_DATE 1 +#define TYPE_TIME 2 +#define TYPE_DATETIME 3 + +//= PropertyConversion +namespace +{ + css::util::Time lcl_getTime(double _nValue) + { + css::util::Time aTime; + sal_uInt64 nIntValue = static_cast<sal_uInt64>(_nValue * ::tools::Time::nanoSecPerDay); + aTime.NanoSeconds = nIntValue % ::tools::Time::nanoSecPerSec; + nIntValue /= ::tools::Time::nanoSecPerSec; + aTime.Seconds = nIntValue % ::tools::Time::secondPerMinute; + nIntValue /= ::tools::Time::secondPerMinute; + aTime.Minutes = nIntValue % ::tools::Time::minutePerHour; + nIntValue /= ::tools::Time::minutePerHour; + OSL_ENSURE(nIntValue < 24, "lcl_getTime: more than a day?"); + aTime.Hours = nIntValue; + + return aTime; + } + + css::util::Date lcl_getDate( double _nValue ) + { + Date aToolsDate(static_cast<sal_uInt32>(_nValue)); + css::util::Date aDate; + ::utl::typeConvert(aToolsDate, aDate); + return aDate; + } +} + +Any PropertyConversion::convertString( const css::uno::Type& _rExpectedType, + const OUString& _rReadCharacters, const SvXMLEnumMapEntry<sal_uInt16>* _pEnumMap, const bool _bInvertBoolean ) +{ + Any aReturn; + bool bEnumAsInt = false; + switch (_rExpectedType.getTypeClass()) + { + case TypeClass_BOOLEAN: // sal_Bool + { + bool bValue; + bool bSuccess = + ::sax::Converter::convertBool(bValue, _rReadCharacters); + OSL_ENSURE(bSuccess, + OStringBuffer("PropertyConversion::convertString: could not convert \"" + + OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) + + "\" into a boolean!").getStr()); + aReturn <<= (_bInvertBoolean ? !bValue : bValue); + } + break; + case TypeClass_SHORT: // sal_Int16 + case TypeClass_LONG: // sal_Int32 + if (!_pEnumMap) + { // it's a real int32/16 property + sal_Int32 nValue(0); + bool bSuccess = + ::sax::Converter::convertNumber(nValue, _rReadCharacters); + OSL_ENSURE(bSuccess, + OStringBuffer("PropertyConversion::convertString: could not convert \"" + + OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) + + "\" into an integer!").getStr()); + if (TypeClass_SHORT == _rExpectedType.getTypeClass()) + aReturn <<= static_cast<sal_Int16>(nValue); + else + aReturn <<= nValue; + break; + } + bEnumAsInt = true; + [[fallthrough]]; + case TypeClass_ENUM: + { + sal_uInt16 nEnumValue(0); + bool bSuccess = SvXMLUnitConverter::convertEnum(nEnumValue, _rReadCharacters, _pEnumMap); + OSL_ENSURE(bSuccess, "PropertyConversion::convertString: could not convert to an enum value!"); + + if (bEnumAsInt) + if (TypeClass_SHORT == _rExpectedType.getTypeClass()) + aReturn <<= static_cast<sal_Int16>(nEnumValue); + else + aReturn <<= static_cast<sal_Int32>(nEnumValue); + else + aReturn = ::cppu::int2enum(static_cast<sal_Int32>(nEnumValue), _rExpectedType); + } + break; + case TypeClass_HYPER: + { + OSL_FAIL("PropertyConversion::convertString: 64-bit integers not implemented yet!"); + } + break; + case TypeClass_DOUBLE: + { + double nValue; + bool bSuccess = + ::sax::Converter::convertDouble(nValue, _rReadCharacters); + OSL_ENSURE(bSuccess, + OStringBuffer(OString::Concat("PropertyConversion::convertString: could not convert \"") + + OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) + + "\" into a double!").getStr()); + aReturn <<= nValue; + } + break; + case TypeClass_STRING: + aReturn <<= _rReadCharacters; + break; + case TypeClass_STRUCT: + { + sal_Int32 nType = 0; + if ( _rExpectedType.equals( ::cppu::UnoType< css::util::Date >::get() ) ) + nType = TYPE_DATE; + else if ( _rExpectedType.equals( ::cppu::UnoType< css::util::Time >::get() ) ) + nType = TYPE_TIME; + else if ( _rExpectedType.equals( ::cppu::UnoType< css::util::DateTime >::get() ) ) + nType = TYPE_DATETIME; + + if ( nType ) + { + // first extract the double + double nValue = 0; + bool bSuccess = + ::sax::Converter::convertDouble(nValue, _rReadCharacters); + OSL_ENSURE(bSuccess, + OStringBuffer("PropertyConversion::convertString: could not convert \"" + + OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) + + "\" into a double!").getStr()); + + // then convert it into the target type + switch (nType) + { + case TYPE_DATE: + { + OSL_ENSURE(std::modf(nValue, &o3tl::temporary(double())) == 0, + "PropertyConversion::convertString: a Date value with a fractional part?"); + aReturn <<= lcl_getDate(nValue); + } + break; + case TYPE_TIME: + { + OSL_ENSURE((static_cast<sal_uInt32>(nValue)) == 0, + "PropertyConversion::convertString: a tools::Time value with more than a fractional part?"); + aReturn <<= lcl_getTime(nValue); + } + break; + case TYPE_DATETIME: + { + css::util::Time aTime = lcl_getTime(nValue); + css::util::Date aDate = lcl_getDate(nValue); + + css::util::DateTime aDateTime; + aDateTime.NanoSeconds = aTime.NanoSeconds; + aDateTime.Seconds = aTime.Seconds; + aDateTime.Minutes = aTime.Minutes; + aDateTime.Hours = aTime.Hours; + aDateTime.Day = aDate.Day; + aDateTime.Month = aDate.Month; + aDateTime.Year = aDate.Year; + aReturn <<= aDateTime; + } + break; + } + } + else + OSL_FAIL("PropertyConversion::convertString: unsupported property type!"); + } + break; + default: + OSL_FAIL("PropertyConversion::convertString: invalid type class!"); + } + + return aReturn; +} + +Type PropertyConversion::xmlTypeToUnoType( const OUString& _rType ) +{ + Type aUnoType( cppu::UnoType<void>::get() ); + + static std::map< OUString, css::uno::Type > s_aTypeNameMap + { + { token::GetXMLToken( token::XML_BOOLEAN ) , cppu::UnoType<bool>::get()}, + // Not a copy paste error, quotation from: + // http://nabble.documentfoundation.org/Question-unoType-for-getXmlToken-dbaccess-reportdesign-module-tp4109071p4109116.html + // all numeric types (including the UNO double) + // consistently map to XML_FLOAT, so taking the extra precision from the + // C++ type "float" to "double" makes absolute sense + { token::GetXMLToken( token::XML_FLOAT ) , ::cppu::UnoType<double>::get()}, + { token::GetXMLToken( token::XML_STRING ) , ::cppu::UnoType<OUString>::get()}, + { token::GetXMLToken( token::XML_VOID ) , cppu::UnoType<void>::get() }, + }; + + const std::map< OUString, css::uno::Type >::iterator aTypePos = s_aTypeNameMap.find( _rType ); + OSL_ENSURE( s_aTypeNameMap.end() != aTypePos, "PropertyConversion::xmlTypeToUnoType: invalid property name!" ); + if ( s_aTypeNameMap.end() != aTypePos ) + aUnoType = aTypePos->second; + + return aUnoType; +} + +//= OPropertyImport +OPropertyImport::OPropertyImport(OFormLayerXMLImport_Impl& _rImport) + :SvXMLImportContext(_rImport.getGlobalContext()) + ,m_rContext(_rImport) + ,m_bTrackAttributes(false) +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > OPropertyImport::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + if( (nElement & TOKEN_MASK) == token::XML_PROPERTIES ) + { + return new OPropertyElementsContext( m_rContext.getGlobalContext(), this); + } + else + SAL_WARN("xmloff", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement)); + return nullptr; +} + +void OPropertyImport::startFastElement(sal_Int32 /*nElement*/, const Reference< XFastAttributeList >& xAttrList) +{ + + // assume the 'worst' case: all attributes describe properties. This should save our property array + // some reallocs + m_aValues.reserve(sax_fastparser::castToFastAttributeList(xAttrList).size()); + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + handleAttribute(aIter.getToken(), aIter.toString()); + + if (m_bTrackAttributes) + m_aEncounteredAttributes.insert(aIter.getToken() & TOKEN_MASK); + } + + // TODO: create PropertyValues for all the attributes which were not present, because they were implied + // this is necessary as soon as we have properties where the XML default is different from the property + // default +} + +bool OPropertyImport::encounteredAttribute(sal_Int32 nAttributeToken) const +{ + OSL_ENSURE(m_bTrackAttributes, "OPropertyImport::encounteredAttribute: attribute tracking not enabled!"); + return m_aEncounteredAttributes.end() != m_aEncounteredAttributes.find(nAttributeToken & TOKEN_MASK); +} + +void OPropertyImport::characters(const OUString& _rChars ) +{ + // ignore them (should be whitespace only) + OSL_ENSURE(_rChars.trim().isEmpty(), "OPropertyImport::Characters: non-whitespace characters!"); +} + +bool OPropertyImport::handleAttribute(sal_Int32 nAttributeToken, const OUString& _rValue) +{ + const OAttribute2Property::AttributeAssignment* pProperty = m_rContext.getAttributeMap().getAttributeTranslation(nAttributeToken & TOKEN_MASK); + if (pProperty) + { + // create and store a new PropertyValue + PropertyValue aNewValue; + aNewValue.Name = pProperty->sPropertyName; + + // convert the value string into the target type + if ((nAttributeToken & TOKEN_MASK) == token::XML_HREF) + { + aNewValue.Value <<= m_rContext.getGlobalContext().GetAbsoluteReference(_rValue); + } + else + { + aNewValue.Value = PropertyConversion::convertString( + pProperty->aPropertyType, _rValue, pProperty->pEnumMap, + pProperty->bInverseSemantics); + } + implPushBackPropertyValue( aNewValue ); + return true; + } + if ((nAttributeToken & TOKEN_MASK) != token::XML_TYPE) // xlink:type is valid but ignored for <form:form> + { + SAL_WARN( "xmloff", "OPropertyImport::handleAttribute: Can't handle " + << SvXMLImport::getPrefixAndNameFromToken(nAttributeToken) << "=" << _rValue ); + return false; + } + return true; +} + +//= OPropertyElementsContext +OPropertyElementsContext::OPropertyElementsContext(SvXMLImport& _rImport, + const OPropertyImportRef& _rPropertyImporter) + :SvXMLImportContext(_rImport) + ,m_xPropertyImporter(_rPropertyImporter) +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > OPropertyElementsContext::createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + if( (nElement & TOKEN_MASK) == XML_PROPERTY ) + { + return new OSinglePropertyContext(GetImport(), m_xPropertyImporter); + } + else if( (nElement & TOKEN_MASK) == XML_LIST_PROPERTY ) + { + return new OListPropertyContext( GetImport(), m_xPropertyImporter ); + } + return nullptr; +} + +#if OSL_DEBUG_LEVEL > 0 + void OPropertyElementsContext::startFastElement( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) + { + OSL_ENSURE(0 == xAttrList->getFastAttributes().getLength(), "OPropertyElementsContext::StartElement: the form:properties element should not have attributes!"); + } + + void OPropertyElementsContext::characters(const OUString& _rChars) + { + OSL_ENSURE(_rChars.trim().isEmpty(), "OPropertyElementsContext::Characters: non-whitespace characters detected!"); + } +#endif + +//= OSinglePropertyContext +OSinglePropertyContext::OSinglePropertyContext(SvXMLImport& _rImport, + const OPropertyImportRef& _rPropertyImporter) + :SvXMLImportContext(_rImport) + ,m_xPropertyImporter(_rPropertyImporter) +{ +} + +void OSinglePropertyContext::startFastElement( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + css::beans::PropertyValue aPropValue; // the property the instance imports currently + css::uno::Type aPropType; // the type of the property the instance imports currently + + OUString sType, sValue; + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(FORM, XML_PROPERTY_NAME): + aPropValue.Name = aIter.toString(); + break; + case XML_ELEMENT(OFFICE, XML_VALUE_TYPE): + sType = aIter.toString(); + break; + case XML_ELEMENT(OFFICE, XML_VALUE): + case XML_ELEMENT(OFFICE, XML_BOOLEAN_VALUE): + case XML_ELEMENT(OFFICE, XML_STRING_VALUE): + sValue = aIter.toString(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + // the name of the property + OSL_ENSURE(!aPropValue.Name.isEmpty(), "OSinglePropertyContext::StartElement: invalid property name!"); + + // needs to be translated into a css::uno::Type + aPropType = PropertyConversion::xmlTypeToUnoType( sType ); + if( TypeClass_VOID == aPropType.getTypeClass() ) + { + aPropValue.Value = Any(); + } + else + { + aPropValue.Value = + PropertyConversion::convertString(aPropType, + sValue); + } + + // now that we finally have our property value, add it to our parent object + if( !aPropValue.Name.isEmpty() ) + m_xPropertyImporter->implPushBackGenericPropertyValue(aPropValue); +} + +//= OListPropertyContext +OListPropertyContext::OListPropertyContext( SvXMLImport& _rImport, + const OPropertyImportRef& _rPropertyImporter ) + :SvXMLImportContext( _rImport ) + ,m_xPropertyImporter( _rPropertyImporter ) +{ +} + +void OListPropertyContext::startFastElement( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(FORM, XML_PROPERTY_NAME): + m_sPropertyName = aIter.toString(); + break; + case XML_ELEMENT(OFFICE, XML_VALUE_TYPE): + m_sPropertyType = aIter.toString(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } +} + +void OListPropertyContext::endFastElement(sal_Int32 ) +{ + OSL_ENSURE( !m_sPropertyName.isEmpty() && !m_sPropertyType.isEmpty(), + "OListPropertyContext::EndElement: no property name or type!" ); + + if ( m_sPropertyName.isEmpty() || m_sPropertyType.isEmpty() ) + return; + + Sequence< Any > aListElements( m_aListValues.size() ); + Any* pListElement = aListElements.getArray(); + css::uno::Type aType = PropertyConversion::xmlTypeToUnoType( m_sPropertyType ); + for ( const auto& rListValue : m_aListValues ) + { + *pListElement = PropertyConversion::convertString( aType, rListValue ); + ++pListElement; + } + + PropertyValue aSequenceValue; + aSequenceValue.Name = m_sPropertyName; + aSequenceValue.Value <<= aListElements; + + m_xPropertyImporter->implPushBackGenericPropertyValue( aSequenceValue ); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > OListPropertyContext::createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + if ( (nElement & TOKEN_MASK) == XML_LIST_VALUE ) + { + m_aListValues.emplace_back(); + return new OListValueContext( GetImport(), *m_aListValues.rbegin() ); + } + return nullptr; +} + +//= OListValueContext +OListValueContext::OListValueContext( SvXMLImport& _rImport, OUString& _rListValueHolder ) + :SvXMLImportContext( _rImport ) + ,m_rListValueHolder( _rListValueHolder ) +{ +} + +void OListValueContext::startFastElement( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(OFFICE, XML_VALUE): + case XML_ELEMENT(OFFICE, XML_STRING_VALUE): + case XML_ELEMENT(OFFICE, XML_BOOLEAN_VALUE): + m_rListValueHolder = aIter.toString(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } +} + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/propertyimport.hxx b/xmloff/source/forms/propertyimport.hxx new file mode 100644 index 000000000..b4b660d5b --- /dev/null +++ b/xmloff/source/forms/propertyimport.hxx @@ -0,0 +1,226 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <o3tl/sorted_vector.hxx> + +#include <xmloff/xmlictxt.hxx> +#include "formattributes.hxx" +#include <rtl/ref.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include "layerimport.hxx" + +namespace com::sun::star::util { + struct Time; + struct Date; +} + +namespace xmloff +{ + + //= PropertyConversion + class PropertyConversion + { + public: + template<typename EnumT> + static css::uno::Any convertString( + const css::uno::Type& _rExpectedType, + const OUString& _rReadCharacters, + const SvXMLEnumMapEntry<EnumT>* _pEnumMap = nullptr + ) + { + return convertString(_rExpectedType, _rReadCharacters, + reinterpret_cast<const SvXMLEnumMapEntry<sal_uInt16>*>(_pEnumMap), /*_bInvertBoolean*/false); + } + static css::uno::Any convertString( + const css::uno::Type& _rExpectedType, + const OUString& _rReadCharacters, + const SvXMLEnumMapEntry<sal_uInt16>* _pEnumMap = nullptr, + const bool _bInvertBoolean = false + ); + + static css::uno::Type xmlTypeToUnoType( const OUString& _rType ); + }; + + class OFormLayerXMLImport_Impl; + //= OPropertyImport + /** Helper class for importing property values + + <p>This class imports properties which are stored as attributes as well as properties which + are stored in </em><form:properties></em> elements.</p> + */ + class OPropertyImport : public SvXMLImportContext + { + friend class OSinglePropertyContext; + friend class OListPropertyContext; + + protected: + typedef ::std::vector< css::beans::PropertyValue > PropertyValueArray; + PropertyValueArray m_aValues; + PropertyValueArray m_aGenericValues; + // the values which the instance collects between StartElement and EndElement + + o3tl::sorted_vector<sal_Int32> m_aEncounteredAttributes; + + OFormLayerXMLImport_Impl& m_rContext; + + bool m_bTrackAttributes; + + // TODO: think about the restriction that the class does not know anything about the object it is importing. + // Perhaps this object should be known to the class, so setting the properties ('normal' ones as well as + // style properties) can be done in our own EndElement instead of letting derived classes do this. + + public: + OPropertyImport(OFormLayerXMLImport_Impl& _rImport); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& _rxAttrList) override; + virtual void SAL_CALL characters(const OUString& _rChars) override; + + protected: + /** handle one single attribute. + + <p>This is called for every attribute of the element. This class' implementation checks if the attribute + describes a property, if so, it is added to <member>m_aValues</member>.</p> + + <p>All non-property attributes should be handled in derived classes.</p> + + @param _nNamespaceKey + key of the namespace used in the attribute + @param _rLocalName + local (relative to the namespace) attribute name + @param _rValue + attribute value + */ + virtual bool handleAttribute(sal_Int32 nElement, const OUString& _rValue); + + /** determine if the element imported by the object had a given attribute. + <p>Please be aware of the fact that the name given must be a local name, i.e. not contain a namespace. + All form relevant attributes are in the same namespace, so this would be a redundant information.</p> + */ + bool encounteredAttribute(sal_Int32 nElement) const; + + /** enables the tracking of the encountered attributes + <p>The tracking will raise the import costs a little bit, but it's cheaper than + derived classes tracking this themself.</p> + */ + void enableTrackAttributes() { m_bTrackAttributes = true; } + + void implPushBackPropertyValue(const css::beans::PropertyValue& _rProp) + { + m_aValues.push_back(_rProp); + } + + void implPushBackPropertyValue( const OUString& _rName, const css::uno::Any& _rValue ) + { + m_aValues.push_back( css::beans::PropertyValue( + _rName, -1, _rValue, css::beans::PropertyState_DIRECT_VALUE ) ); + } + + void implPushBackGenericPropertyValue(const css::beans::PropertyValue& _rProp) + { + m_aGenericValues.push_back(_rProp); + } + }; + typedef rtl::Reference<OPropertyImport> OPropertyImportRef; + + //= OPropertyElementsContext + /** helper class for importing the <form:properties> element + */ + class OPropertyElementsContext : public SvXMLImportContext + { + OPropertyImportRef m_xPropertyImporter; // to add the properties + + public: + OPropertyElementsContext(SvXMLImport& _rImport, + const OPropertyImportRef& _rPropertyImporter); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + +#if OSL_DEBUG_LEVEL > 0 + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL characters(const OUString& _rChars) override; +#endif + }; + + //= OSinglePropertyContext + /** helper class for importing a single <form:property> element + */ + class OSinglePropertyContext : public SvXMLImportContext + { + OPropertyImportRef m_xPropertyImporter; // to add the properties + + public: + OSinglePropertyContext(SvXMLImport& _rImport, + const OPropertyImportRef& _rPropertyImporter); + + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + }; + + //= OListPropertyContext + class OListPropertyContext : public SvXMLImportContext + { + OPropertyImportRef m_xPropertyImporter; + OUString m_sPropertyName; + OUString m_sPropertyType; + ::std::vector< OUString > m_aListValues; + + public: + OListPropertyContext( SvXMLImport& _rImport, + const OPropertyImportRef& _rPropertyImporter ); + + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + }; + + //= OListValueContext + class OListValueContext : public SvXMLImportContext + { + OUString& m_rListValueHolder; + + public: + OListValueContext( SvXMLImport& _rImport, OUString& _rListValueHolder ); + + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/strings.hxx b/xmloff/source/forms/strings.hxx new file mode 100644 index 000000000..f12067504 --- /dev/null +++ b/xmloff/source/forms/strings.hxx @@ -0,0 +1,209 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> + +namespace xmloff +{ + + // properties + #define PROPERTY_CLASSID "ClassId" + #define PROPERTY_ECHOCHAR "EchoChar" + #define PROPERTY_MULTILINE "MultiLine" + #define PROPERTY_NAME "Name" + #define PROPERTY_GRAPHIC "Graphic" + #define PROPERTY_LABEL "Label" + #define PROPERTY_TARGETFRAME "TargetFrame" + #define PROPERTY_TARGETURL "TargetURL" + #define PROPERTY_TITLE "Tag" + #define PROPERTY_DROPDOWN "Dropdown" + #define PROPERTY_PRINTABLE "Printable" + #define PROPERTY_READONLY "ReadOnly" + #define PROPERTY_DEFAULT_STATE "DefaultState" + #define PROPERTY_TABSTOP "Tabstop" + #define PROPERTY_STATE "State" + #define PROPERTY_ENABLED "Enabled" + #define PROPERTY_ENABLEVISIBLE "EnableVisible" + #define PROPERTY_MAXTEXTLENGTH "MaxTextLen" + #define PROPERTY_LINECOUNT "LineCount" + #define PROPERTY_TABINDEX "TabIndex" + #define PROPERTY_COMMAND "Command" + #define PROPERTY_DATASOURCENAME "DataSourceName" + #define PROPERTY_FILTER "Filter" + #define PROPERTY_ORDER "Order" + #define PROPERTY_ALLOWDELETES "AllowDeletes" + #define PROPERTY_ALLOWINSERTS "AllowInserts" + #define PROPERTY_ALLOWUPDATES "AllowUpdates" + #define PROPERTY_APPLYFILTER "ApplyFilter" + #define PROPERTY_ESCAPEPROCESSING "EscapeProcessing" + #define PROPERTY_IGNORERESULT "IgnoreResult" + #define PROPERTY_SUBMIT_ENCODING "SubmitEncoding" + #define PROPERTY_SUBMIT_METHOD "SubmitMethod" + #define PROPERTY_COMMAND_TYPE "CommandType" + #define PROPERTY_NAVIGATION "NavigationBarMode" + #define PROPERTY_CYCLE "Cycle" + #define PROPERTY_BUTTONTYPE "ButtonType" + #define PROPERTY_DATAFIELD "DataField" + #define PROPERTY_BOUNDCOLUMN "BoundColumn" + #define PROPERTY_EMPTY_IS_NULL "ConvertEmptyToNull" + #define PROPERTY_INPUT_REQUIRED "InputRequired" + #define PROPERTY_LISTSOURCE "ListSource" + #define PROPERTY_LISTSOURCETYPE "ListSourceType" + #define PROPERTY_ECHO_CHAR "EchoChar" + #define PROPERTY_STRICTFORMAT "StrictFormat" + #define PROPERTY_AUTOCOMPLETE "Autocomplete" + #define PROPERTY_MULTISELECTION "MultiSelection" + #define PROPERTY_DEFAULTBUTTON "DefaultButton" + #define PROPERTY_TRISTATE "TriState" + #define PROPERTY_CONTROLLABEL "LabelControl" + #define PROPERTY_STRING_ITEM_LIST "StringItemList" + #define PROPERTY_VALUE_SEQ "ValueItemList" + #define PROPERTY_DEFAULT_SELECT_SEQ "DefaultSelection" + #define PROPERTY_SELECT_SEQ "SelectedItems" + #define PROPERTY_DATE_MIN "DateMin" + #define PROPERTY_DATE_MAX "DateMax" + #define PROPERTY_TIME_MIN "TimeMin" + #define PROPERTY_TIME_MAX "TimeMax" + #define PROPERTY_VALUE_MIN "ValueMin" + #define PROPERTY_VALUE_MAX "ValueMax" + #define PROPERTY_EFFECTIVE_MIN "EffectiveMin" + #define PROPERTY_EFFECTIVE_MAX "EffectiveMax" + #define PROPERTY_DEFAULT_DATE "DefaultDate" + #define PROPERTY_DATE "Date" + #define PROPERTY_DEFAULT_TIME "DefaultTime" + #define PROPERTY_TIME "Time" + #define PROPERTY_DEFAULT_VALUE "DefaultValue" + #define PROPERTY_VALUE "Value" + #define PROPERTY_HIDDEN_VALUE "HiddenValue" + #define PROPERTY_DEFAULT_TEXT "DefaultText" + #define PROPERTY_TEXT "Text" + #define PROPERTY_EFFECTIVE_VALUE "EffectiveValue" + #define PROPERTY_EFFECTIVE_DEFAULT "EffectiveDefault" + #define PROPERTY_REFVALUE "RefValue" + #define PROPERTY_URL "URL" + #define PROPERTY_FONT "FontDescriptor" + #define PROPERTY_BACKGROUNDCOLOR "BackgroundColor" + #define PROPERTY_MASTERFIELDS "MasterFields" + #define PROPERTY_DETAILFIELDS "DetailFields" + #define PROPERTY_COLUMNSERVICENAME "ColumnServiceName" + #define PROPERTY_FORMATKEY "FormatKey" + #define PROPERTY_ALIGN "Align" + #define PROPERTY_BORDER "Border" + #define PROPERTY_AUTOCONTROLFOCUS "AutomaticControlFocus" + #define PROPERTY_APPLYDESIGNMODE "ApplyFormDesignMode" + #define PROPERTY_FORMATSSUPPLIER "FormatsSupplier" + #define PROPERTY_LOCALE "Locale" + #define PROPERTY_FORMATSTRING "FormatString" + #define PROPERTY_DATEFORMAT "DateFormat" + #define PROPERTY_TIMEFORMAT "TimeFormat" + #define PROPERTY_PERSISTENCE_MAXTEXTLENGTH "PersistenceMaxTextLength" + #define PROPERTY_SCROLLVALUE_MIN "ScrollValueMin" + #define PROPERTY_SCROLLVALUE_MAX "ScrollValueMax" + #define PROPERTY_SCROLLVALUE "ScrollValue" + #define PROPERTY_SCROLLVALUE_DEFAULT "DefaultScrollValue" + #define PROPERTY_LINE_INCREMENT "LineIncrement" + #define PROPERTY_BLOCK_INCREMENT "BlockIncrement" + #define PROPERTY_REPEAT_DELAY "RepeatDelay" + #define PROPERTY_SPINVALUE "SpinValue" + #define PROPERTY_SPINVALUE_MIN "SpinValueMin" + #define PROPERTY_SPINVALUE_MAX "SpinValueMax" + #define PROPERTY_DEFAULT_SPINVALUE "DefaultSpinValue" + #define PROPERTY_SPIN_INCREMENT "SpinIncrement" + #define PROPERTY_ORIENTATION "Orientation" + #define PROPERTY_TOGGLE "Toggle" + #define PROPERTY_FOCUS_ON_CLICK "FocusOnClick" + #define PROPERTY_VISUAL_EFFECT "VisualEffect" + #define PROPERTY_IMAGE_POSITION "ImagePosition" + #define PROPERTY_IMAGE_ALIGN "ImageAlign" + #define PROPERTY_SCALE_IMAGE "ScaleImage" + #define PROPERTY_GROUP_NAME "GroupName" + + #define PROPERTY_BOUND_CELL "BoundCell" + #define PROPERTY_LIST_CELL_RANGE "CellRange" + #define PROPERTY_ADDRESS "Address" + #define PROPERTY_FILE_REPRESENTATION "PersistentRepresentation" + #define PROPERTY_RICH_TEXT "RichText" + + // services + inline constexpr OUStringLiteral SERVICE_SPREADSHEET_DOCUMENT = u"com.sun.star.sheet.SpreadsheetDocument"; + inline constexpr OUStringLiteral SERVICE_CELLVALUEBINDING = u"com.sun.star.table.CellValueBinding"; + inline constexpr OUStringLiteral SERVICE_LISTINDEXCELLBINDING = u"com.sun.star.table.ListPositionCellBinding"; + inline constexpr OUStringLiteral SERVICE_CELLRANGELISTSOURCE = u"com.sun.star.table.CellRangeListSource"; + inline constexpr OUStringLiteral SERVICE_ADDRESS_CONVERSION = u"com.sun.star.table.CellAddressConversion"; + inline constexpr OUStringLiteral SERVICE_RANGEADDRESS_CONVERSION = u"com.sun.star.table.CellRangeAddressConversion"; + + // old service names (compatibility) + #define SERVICE_PERSISTENT_COMPONENT_FORM "stardiv.one.form.component.Form" + #define SERVICE_PERSISTENT_COMPONENT_EDIT "stardiv.one.form.component.Edit" + #define SERVICE_PERSISTENT_COMPONENT_LISTBOX "stardiv.one.form.component.ListBox" + #define SERVICE_PERSISTENT_COMPONENT_COMBOBOX "stardiv.one.form.component.ComboBox" + #define SERVICE_PERSISTENT_COMPONENT_RADIOBUTTON "stardiv.one.form.component.RadioButton" + #define SERVICE_PERSISTENT_COMPONENT_GROUPBOX "stardiv.one.form.component.GroupBox" + #define SERVICE_PERSISTENT_COMPONENT_FIXEDTEXT "stardiv.one.form.component.FixedText" + #define SERVICE_PERSISTENT_COMPONENT_COMMANDBUTTON "stardiv.one.form.component.CommandButton" + #define SERVICE_PERSISTENT_COMPONENT_CHECKBOX "stardiv.one.form.component.CheckBox" + #define SERVICE_PERSISTENT_COMPONENT_GRID "stardiv.one.form.component.Grid" + #define SERVICE_PERSISTENT_COMPONENT_IMAGEBUTTON "stardiv.one.form.component.ImageButton" + #define SERVICE_PERSISTENT_COMPONENT_FILECONTROL "stardiv.one.form.component.FileControl" + #define SERVICE_PERSISTENT_COMPONENT_TIMEFIELD "stardiv.one.form.component.TimeField" + #define SERVICE_PERSISTENT_COMPONENT_DATEFIELD "stardiv.one.form.component.DateField" + #define SERVICE_PERSISTENT_COMPONENT_NUMERICFIELD "stardiv.one.form.component.NumericField" + #define SERVICE_PERSISTENT_COMPONENT_CURRENCYFIELD "stardiv.one.form.component.CurrencyField" + #define SERVICE_PERSISTENT_COMPONENT_PATTERNFIELD "stardiv.one.form.component.PatternField" + #define SERVICE_PERSISTENT_COMPONENT_HIDDENCONTROL "stardiv.one.form.component.Hidden" + #define SERVICE_PERSISTENT_COMPONENT_IMAGECONTROL "stardiv.one.form.component.ImageControl" + #define SERVICE_PERSISTENT_COMPONENT_FORMATTEDFIELD "stardiv.one.form.component.FormattedField" + + // new service names, the old ones are translated into this new ones + inline constexpr OUStringLiteral SERVICE_FORM = u"com.sun.star.form.component.Form"; + inline constexpr OUStringLiteral SERVICE_EDIT = u"com.sun.star.form.component.TextField"; + inline constexpr OUStringLiteral SERVICE_LISTBOX = u"com.sun.star.form.component.ListBox"; + inline constexpr OUStringLiteral SERVICE_COMBOBOX = u"com.sun.star.form.component.ComboBox"; + inline constexpr OUStringLiteral SERVICE_RADIOBUTTON = u"com.sun.star.form.component.RadioButton"; + inline constexpr OUStringLiteral SERVICE_GROUPBOX = u"com.sun.star.form.component.GroupBox"; + inline constexpr OUStringLiteral SERVICE_FIXEDTEXT = u"com.sun.star.form.component.FixedText"; + inline constexpr OUStringLiteral SERVICE_COMMANDBUTTON = u"com.sun.star.form.component.CommandButton"; + inline constexpr OUStringLiteral SERVICE_CHECKBOX = u"com.sun.star.form.component.CheckBox"; + inline constexpr OUStringLiteral SERVICE_GRID = u"com.sun.star.form.component.GridControl"; + inline constexpr OUStringLiteral SERVICE_IMAGEBUTTON = u"com.sun.star.form.component.ImageButton"; + inline constexpr OUStringLiteral SERVICE_FILECONTROL = u"com.sun.star.form.component.FileControl"; + inline constexpr OUStringLiteral SERVICE_TIMEFIELD = u"com.sun.star.form.component.TimeField"; + inline constexpr OUStringLiteral SERVICE_DATEFIELD = u"com.sun.star.form.component.DateField"; + inline constexpr OUStringLiteral SERVICE_NUMERICFIELD = u"com.sun.star.form.component.NumericField"; + inline constexpr OUStringLiteral SERVICE_CURRENCYFIELD = u"com.sun.star.form.component.CurrencyField"; + inline constexpr OUStringLiteral SERVICE_PATTERNFIELD = u"com.sun.star.form.component.PatternField"; + inline constexpr OUStringLiteral SERVICE_HIDDENCONTROL = u"com.sun.star.form.component.HiddenControl"; + inline constexpr OUStringLiteral SERVICE_IMAGECONTROL = u"com.sun.star.form.component.DatabaseImageControl"; + inline constexpr OUStringLiteral SERVICE_FORMATTEDFIELD = u"com.sun.star.form.component.FormattedField"; + + // various strings + #define EVENT_NAME_SEPARATOR "::" + inline constexpr OUStringLiteral EVENT_TYPE = u"EventType"; + inline constexpr OUStringLiteral EVENT_LIBRARY = u"Library"; + inline constexpr OUStringLiteral EVENT_LOCALMACRONAME = u"MacroName"; + inline constexpr OUStringLiteral EVENT_SCRIPTURL = u"Script"; + inline constexpr OUStringLiteral EVENT_STAROFFICE = u"StarOffice"; + #define EVENT_STARBASIC "StarBasic" + inline constexpr OUStringLiteral EVENT_APPLICATION = u"application"; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/valueproperties.cxx b/xmloff/source/forms/valueproperties.cxx new file mode 100644 index 000000000..c2f60e5a2 --- /dev/null +++ b/xmloff/source/forms/valueproperties.cxx @@ -0,0 +1,180 @@ +/* -*- 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 "valueproperties.hxx" +#include "strings.hxx" +#include <com/sun/star/form/FormComponentType.hpp> +#include <sal/log.hxx> + +namespace xmloff +{ + + using namespace ::com::sun::star::form; + + //= OValuePropertiesMetaData + void OValuePropertiesMetaData::getValuePropertyNames( + OControlElement::ElementType _eType, sal_Int16 _nFormComponentType, + char const * & _rpCurrentValuePropertyName, char const * & _rpValuePropertyName) + { + // reset the pointers in case we can't determine the property names + _rpCurrentValuePropertyName = _rpValuePropertyName = nullptr; + switch (_nFormComponentType) + { + case FormComponentType::TEXTFIELD: + if (OControlElement::FORMATTED_TEXT == _eType) + { + _rpCurrentValuePropertyName = PROPERTY_EFFECTIVE_VALUE; + _rpValuePropertyName = PROPERTY_EFFECTIVE_DEFAULT; + } + else + { + if (OControlElement::PASSWORD != _eType) + // no CurrentValue" for passwords + _rpCurrentValuePropertyName = PROPERTY_TEXT; + _rpValuePropertyName = PROPERTY_DEFAULT_TEXT; + } + break; + + case FormComponentType::NUMERICFIELD: + case FormComponentType::CURRENCYFIELD: + _rpCurrentValuePropertyName = PROPERTY_VALUE; + _rpValuePropertyName = PROPERTY_DEFAULT_VALUE; + break; + + case FormComponentType::PATTERNFIELD: + case FormComponentType::FILECONTROL: + case FormComponentType::COMBOBOX: + _rpValuePropertyName = PROPERTY_DEFAULT_TEXT; + [[fallthrough]]; + case FormComponentType::COMMANDBUTTON: + _rpCurrentValuePropertyName = PROPERTY_TEXT; + break; + + case FormComponentType::CHECKBOX: + case FormComponentType::RADIOBUTTON: + _rpValuePropertyName = PROPERTY_REFVALUE; + break; + + case FormComponentType::HIDDENCONTROL: + _rpValuePropertyName = PROPERTY_HIDDEN_VALUE; + break; + + case FormComponentType::SCROLLBAR: + _rpCurrentValuePropertyName = PROPERTY_SCROLLVALUE; + _rpValuePropertyName = PROPERTY_SCROLLVALUE_DEFAULT; + break; + + case FormComponentType::SPINBUTTON: + _rpCurrentValuePropertyName = PROPERTY_SPINVALUE; + _rpValuePropertyName = PROPERTY_DEFAULT_SPINVALUE; + break; + + default: + SAL_WARN( "xmloff", "OValuePropertiesMetaData::getValuePropertyNames: unsupported component type!" ); + break; + } + } + + void OValuePropertiesMetaData::getValueLimitPropertyNames(sal_Int16 _nFormComponentType, + char const * & _rpMinValuePropertyName, char const * & _rpMaxValuePropertyName) + { + _rpMinValuePropertyName = _rpMaxValuePropertyName = nullptr; + switch (_nFormComponentType) + { + case FormComponentType::NUMERICFIELD: + case FormComponentType::CURRENCYFIELD: + _rpMinValuePropertyName = PROPERTY_VALUE_MIN; + _rpMaxValuePropertyName = PROPERTY_VALUE_MAX; + break; + + case FormComponentType::TEXTFIELD: + _rpMinValuePropertyName = PROPERTY_EFFECTIVE_MIN; + _rpMaxValuePropertyName = PROPERTY_EFFECTIVE_MAX; + break; + + case FormComponentType::SCROLLBAR: + _rpMinValuePropertyName = PROPERTY_SCROLLVALUE_MIN; + _rpMaxValuePropertyName = PROPERTY_SCROLLVALUE_MAX; + break; + + case FormComponentType::SPINBUTTON: + _rpMinValuePropertyName = PROPERTY_SPINVALUE_MIN; + _rpMaxValuePropertyName = PROPERTY_SPINVALUE_MAX; + break; + + default: + SAL_WARN("xmloff", "OValuePropertiesMetaData::getValueLimitPropertyNames: unsupported component type!" ); + break; + } + } + + void OValuePropertiesMetaData::getRuntimeValuePropertyNames( + OControlElement::ElementType _eType, sal_Int16 _nFormComponentType, + char const * & _rpValuePropertyName, char const * & _rpDefaultValuePropertyName ) + { + // reset the pointers in case we can't determine the property names + _rpValuePropertyName = _rpDefaultValuePropertyName = nullptr; + switch (_nFormComponentType) + { + case FormComponentType::TEXTFIELD: + if (OControlElement::FORMATTED_TEXT == _eType) + { + _rpValuePropertyName = PROPERTY_EFFECTIVE_VALUE; + _rpDefaultValuePropertyName = PROPERTY_EFFECTIVE_DEFAULT; + } + else + { + _rpValuePropertyName = PROPERTY_TEXT; + _rpDefaultValuePropertyName = PROPERTY_DEFAULT_TEXT; + } + break; + + case FormComponentType::DATEFIELD: + _rpValuePropertyName = PROPERTY_DATE; + _rpDefaultValuePropertyName = PROPERTY_DEFAULT_DATE; + break; + + case FormComponentType::TIMEFIELD: + _rpValuePropertyName = PROPERTY_TIME; + _rpDefaultValuePropertyName = PROPERTY_DEFAULT_TIME; + break; + + case FormComponentType::NUMERICFIELD: + case FormComponentType::CURRENCYFIELD: + case FormComponentType::PATTERNFIELD: + case FormComponentType::FILECONTROL: + case FormComponentType::COMBOBOX: + case FormComponentType::SCROLLBAR: + case FormComponentType::SPINBUTTON: + // For these types, the runtime properties are the same as the ones which in the XML + // stream are named "value properties" + getValuePropertyNames( _eType, _nFormComponentType, _rpValuePropertyName, _rpDefaultValuePropertyName ); + break; + + case FormComponentType::CHECKBOX: + case FormComponentType::RADIOBUTTON: + _rpValuePropertyName = PROPERTY_STATE; + _rpDefaultValuePropertyName = PROPERTY_DEFAULT_STATE; + break; + } + } + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/valueproperties.hxx b/xmloff/source/forms/valueproperties.hxx new file mode 100644 index 000000000..b22b57bb0 --- /dev/null +++ b/xmloff/source/forms/valueproperties.hxx @@ -0,0 +1,67 @@ +/* -*- 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 . + */ + +#pragma once + +#include "controlelement.hxx" + +namespace xmloff +{ + + //= OValuePropertiesMetaData + class OValuePropertiesMetaData + { + protected: + OValuePropertiesMetaData() { } + + public: + /** calculate the property names for the <em>current-value</em> and the <em>value</em> attribute. + + <p>If controls of the given FormComponentType do not have any of the properties requested, + the respective out parameter will be set to NULL.</p> + */ + static void getValuePropertyNames( + OControlElement::ElementType _eType, + sal_Int16 _nFormComponentType, + char const * & _rpCurrentValuePropertyName, + char const * & _rpValuePropertyName); + + /** calculate the property names for the <em>min-value</em> and the <em>max-value</em> attribute. + + <p>If controls of the given FormComponentType do not have any of the properties requested, + the respective out parameter will be set to NULL.</p> + */ + static void getValueLimitPropertyNames( + sal_Int16 _nFormComponentType, + char const * & _rpMinValuePropertyName, + char const * & _rpMaxValuePropertyName); + + /** calculate the names of the properties which, at runtime, are used for <em>value</em> and + <em>default value</em>. + */ + static void getRuntimeValuePropertyNames( + OControlElement::ElementType _eType, + sal_Int16 _nFormComponentType, + char const * & _rpValuePropertyName, + char const * & _rpDefaultValuePropertyName); + }; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |