From 13d4e74e4fc415e92cdcdb804b0a658714b4474c Mon Sep 17 00:00:00 2001 From: Jonathan Clark Date: Wed, 17 Jun 2026 13:39:21 -0600 Subject: [PATCH] tdf#172442 xmloff: Fix Base ParagraphAdjust to awt::TextAlign conversion When Base loads forms, it flattens ParagraphAdjust values into GUI-specific awt::TextAlign values. The code that performed this remapping was missing some recently-added values, causing apparent loss of column alignment after save. This change adds the missing values. In addition, this change adds unit tests for the conversion code, and fixes a long-standing typo bug that made values fail to round-trip. Regression due to the following commit: commit 5d1b2baaa96fce2cc283e4d383bcfca70651b2da Author: Jonathan Clark Date: Tue Oct 21 02:14:26 2025 -0600 tdf#118350 [API CHANGE] Implement start and end paragraph alignment Change-Id: Ica27870a952c6ec77360f416e112724b4943a016 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/206816 Reviewed-by: Jonathan Clark Tested-by: Jenkins --- solenv/clang-format/excludelist | 1 - xmloff/CppunitTest_xmloff_forms.mk | 60 +++++ xmloff/Module_xmloff.mk | 3 +- xmloff/inc/forms/gridcolumnproptranslator.hxx | 80 ++++++ xmloff/qa/unit/forms.cxx | 239 ++++++++++++++++++ xmloff/source/forms/elementimport.cxx | 2 +- .../source/forms/gridcolumnproptranslator.cxx | 6 +- .../source/forms/gridcolumnproptranslator.hxx | 66 ----- 8 files changed, 386 insertions(+), 71 deletions(-) create mode 100644 xmloff/CppunitTest_xmloff_forms.mk create mode 100644 xmloff/inc/forms/gridcolumnproptranslator.hxx create mode 100644 xmloff/qa/unit/forms.cxx delete mode 100644 xmloff/source/forms/gridcolumnproptranslator.hxx diff --git a/solenv/clang-format/excludelist b/solenv/clang-format/excludelist index 7ca8bddaaa5d..625c3ccfedab 100644 --- a/solenv/clang-format/excludelist +++ b/solenv/clang-format/excludelist @@ -15136,7 +15136,6 @@ xmloff/source/forms/formevents.cxx xmloff/source/forms/formlayerexport.cxx xmloff/source/forms/formlayerimport.cxx xmloff/source/forms/gridcolumnproptranslator.cxx -xmloff/source/forms/gridcolumnproptranslator.hxx xmloff/source/forms/handler/form_handler_factory.cxx xmloff/source/forms/handler/vcl_date_handler.cxx xmloff/source/forms/handler/vcl_date_handler.hxx diff --git a/xmloff/CppunitTest_xmloff_forms.mk b/xmloff/CppunitTest_xmloff_forms.mk new file mode 100644 index 000000000000..1bc2926d6d35 --- /dev/null +++ b/xmloff/CppunitTest_xmloff_forms.mk @@ -0,0 +1,60 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# 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/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitTest,xmloff_forms)) + +$(eval $(call gb_CppunitTest_use_externals,xmloff_forms,\ + boost_headers \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,xmloff_forms, \ + xmloff/qa/unit/forms \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,xmloff_forms, \ + comphelper \ + cppu \ + cppuhelper \ + docmodel \ + embobj \ + sal \ + subsequenttest \ + test \ + unotest \ + utl \ + tl \ + xo \ +)) + +$(eval $(call gb_CppunitTest_set_include,xmloff_forms,\ + -I$(SRCDIR)/xmloff/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,xmloff_forms)) + +$(eval $(call gb_CppunitTest_use_ure,xmloff_forms)) +$(eval $(call gb_CppunitTest_use_vcl,xmloff_forms)) + +$(eval $(call gb_CppunitTest_use_rdb,xmloff_forms,services)) + +$(eval $(call gb_CppunitTest_use_custom_headers,xmloff_forms,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,xmloff_forms)) + +$(eval $(call gb_CppunitTest_add_arguments,xmloff_forms, \ + -env:arg-env=$(gb_Helper_LIBRARY_PATH_VAR)"$$$${$(gb_Helper_LIBRARY_PATH_VAR)+=$$$$$(gb_Helper_LIBRARY_PATH_VAR)}" \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/xmloff/Module_xmloff.mk b/xmloff/Module_xmloff.mk index 9aa54fa42a5c..9342f4899817 100644 --- a/xmloff/Module_xmloff.mk +++ b/xmloff/Module_xmloff.mk @@ -34,9 +34,10 @@ endif $(eval $(call gb_Module_add_check_targets,xmloff,\ $(if $(MERGELIBS),, \ CppunitTest_xmloff_uxmloff) \ + CppunitTest_xmloff_draw \ + CppunitTest_xmloff_forms \ CppunitTest_xmloff_style \ CppunitTest_xmloff_text \ - CppunitTest_xmloff_draw \ )) $(eval $(call gb_Module_add_subsequentcheck_targets,xmloff,\ diff --git a/xmloff/inc/forms/gridcolumnproptranslator.hxx b/xmloff/inc/forms/gridcolumnproptranslator.hxx new file mode 100644 index 000000000000..ea88766f8980 --- /dev/null +++ b/xmloff/inc/forms/gridcolumnproptranslator.hxx @@ -0,0 +1,80 @@ +/* -*- 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 +#include +#include + +namespace xmloff +{ +//= OGridColumnPropertyTranslator +typedef ::cppu::WeakImplHelper + OGridColumnPropertyTranslator_Base; +class SAL_DLLPUBLIC_EXPORT OGridColumnPropertyTranslator : public OGridColumnPropertyTranslator_Base +{ +private: + css::uno::Reference m_xGridColumn; + +public: + explicit OGridColumnPropertyTranslator( + const css::uno::Reference& _rxGridColumn); + +protected: + virtual ~OGridColumnPropertyTranslator() override; + + // XPropertySet + virtual css::uno::Reference + 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& xListener) override; + virtual void SAL_CALL removePropertyChangeListener( + const OUString& aPropertyName, + const css::uno::Reference& aListener) override; + virtual void SAL_CALL addVetoableChangeListener( + const OUString& PropertyName, + const css::uno::Reference& aListener) override; + virtual void SAL_CALL removeVetoableChangeListener( + const OUString& PropertyName, + const css::uno::Reference& aListener) override; + + // XMultiPropertySet + virtual void SAL_CALL + setPropertyValues(const css::uno::Sequence& aPropertyNames, + const css::uno::Sequence& aValues) override; + virtual css::uno::Sequence + SAL_CALL getPropertyValues(const css::uno::Sequence& aPropertyNames) override; + virtual void SAL_CALL addPropertiesChangeListener( + const css::uno::Sequence& aPropertyNames, + const css::uno::Reference& xListener) override; + virtual void SAL_CALL removePropertiesChangeListener( + const css::uno::Reference& xListener) override; + virtual void SAL_CALL firePropertiesChangeEvent( + const css::uno::Sequence& aPropertyNames, + const css::uno::Reference& xListener) override; +}; + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/qa/unit/forms.cxx b/xmloff/qa/unit/forms.cxx new file mode 100644 index 000000000000..60443a65cc8b --- /dev/null +++ b/xmloff/qa/unit/forms.cxx @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; + +namespace +{ +class Test : public UnoApiTest +{ +public: + Test() + : UnoApiTest(u"/xmloff/qa/unit/data/"_ustr) + { + } +}; + +class MockColumnAlignmentPropertySetInfo : public XPropertySetInfo +{ +public: + // XPropertySetInfo + Sequence SAL_CALL getProperties() override + { + return { getPropertyByName(u"Align"_ustr) }; + } + + Property SAL_CALL getPropertyByName(const OUString& aName) override + { + return Property{ aName, /*handle*/ 0, /*type*/ cppu::UnoType::get(), + /*attributes*/ 0 }; + } + + sal_Bool SAL_CALL hasPropertyByName(const OUString& aName) override + { + return aName == u"Align"_ustr; + } + + // XInterface + Any SAL_CALL queryInterface(const Type& aType) override + { + Any aRet; + if (aType == cppu::UnoType::get()) + { + aRet <<= Reference(this); + } + + return aRet; + } + + void SAL_CALL acquire() override {} + + void SAL_CALL release() override {} +}; + +class MockColumnAlignmentMultiPropertySet : public XMultiPropertySet +{ +private: + sal_Int16 nCurrTextAlign = awt::TextAlign::LEFT; + +public: + // XMultiPropertySet + Reference SAL_CALL getPropertySetInfo() override + { + return { new MockColumnAlignmentPropertySetInfo }; + } + + void SAL_CALL setPropertyValues(const Sequence& aPropertyNames, + const Sequence& aValues) override + { + for (sal_Int32 nIndex = 0; nIndex < aPropertyNames.getLength(); ++nIndex) + { + if (aPropertyNames[nIndex] == u"Align"_ustr) + { + aValues[nIndex] >>= nCurrTextAlign; + } + } + } + + Sequence SAL_CALL getPropertyValues(const Sequence& aPropertyNames) override + { + Sequence aRet{ aPropertyNames.getLength() }; + Any* pOut = aRet.getArray(); + + for (sal_Int32 nIndex = 0; nIndex < aPropertyNames.getLength(); ++nIndex) + { + if (aPropertyNames[nIndex] == u"Align"_ustr) + { + *pOut <<= nCurrTextAlign; + } + + ++pOut; + } + + return aRet; + } + + void SAL_CALL + addPropertiesChangeListener(const Sequence& /*aPropertyNames*/, + const Reference& /*xListener*/) override + { + } + + void SAL_CALL removePropertiesChangeListener( + const Reference& /*xListener*/) override + { + } + + void SAL_CALL + firePropertiesChangeEvent(const Sequence& /*aPropertyNames*/, + const Reference& /*xListener*/) override + { + } + + // XInterface + Any SAL_CALL queryInterface(const Type& aType) override + { + Any aRet; + if (aType == cppu::UnoType::get()) + { + aRet <<= Reference(this); + } + + return aRet; + } + + void SAL_CALL acquire() override {} + + void SAL_CALL release() override {} +}; +} + +CPPUNIT_TEST_FIXTURE(Test, testColumnAlignmentConversion) +{ + uno::Reference xProps = new MockColumnAlignmentMultiPropertySet; + CPPUNIT_ASSERT(xProps.is()); + Reference xTranslator = new xmloff::OGridColumnPropertyTranslator{ xProps }; + CPPUNIT_ASSERT(xTranslator.is()); + + auto fnSetParaAdjust = [&](style::ParagraphAdjust nValue) { + // Intentionally set the property as sal_Int32, not ParagraphAdjust. + // OGridColumnPropertyTranslator cannot translate the enum value. + xTranslator->setPropertyValue(u"ParaAdjust"_ustr, Any{ static_cast(nValue) }); + }; + + auto fnGetParaAdjust = [&] { + // However, get the property as ParagraphAdjust, because this is + // what OGridColumnPropertyTranslator returns. + Any aSource = xTranslator->getPropertyValue(u"ParaAdjust"_ustr); + + style::ParagraphAdjust nValue = style::ParagraphAdjust_LEFT; + aSource >>= nValue; + + return nValue; + }; + + auto fnSetTextAlign + = [&](sal_Int16 nValue) { xTranslator->setPropertyValue(u"Align"_ustr, Any{ nValue }); }; + + auto fnGetTextAlign = [&] { + Any aSource = xTranslator->getPropertyValue(u"Align"_ustr); + + sal_Int32 nValue = 0; + aSource >>= nValue; + + return nValue; + }; + + // Sanity check: should be initialized to left adjusted + CPPUNIT_ASSERT_EQUAL(style::ParagraphAdjust_LEFT, fnGetParaAdjust()); + CPPUNIT_ASSERT_EQUAL(awt::TextAlign::LEFT, fnGetTextAlign()); + + // Sanity check: paragraph adjust and AWT text align should round-trip + fnSetTextAlign(awt::TextAlign::RIGHT); + CPPUNIT_ASSERT_EQUAL(awt::TextAlign::RIGHT, fnGetTextAlign()); + + fnSetTextAlign(awt::TextAlign::LEFT); + CPPUNIT_ASSERT_EQUAL(awt::TextAlign::LEFT, fnGetTextAlign()); + + fnSetParaAdjust(style::ParagraphAdjust_RIGHT); + CPPUNIT_ASSERT_EQUAL(style::ParagraphAdjust_RIGHT, fnGetParaAdjust()); + + fnSetParaAdjust(style::ParagraphAdjust_LEFT); + CPPUNIT_ASSERT_EQUAL(style::ParagraphAdjust_LEFT, fnGetParaAdjust()); + + // Check conversion from AWT text align to paragraph adjust + fnSetTextAlign(awt::TextAlign::LEFT); + CPPUNIT_ASSERT_EQUAL(style::ParagraphAdjust_LEFT, fnGetParaAdjust()); + + fnSetTextAlign(awt::TextAlign::CENTER); + CPPUNIT_ASSERT_EQUAL(style::ParagraphAdjust_CENTER, fnGetParaAdjust()); + + fnSetTextAlign(awt::TextAlign::RIGHT); + CPPUNIT_ASSERT_EQUAL(style::ParagraphAdjust_RIGHT, fnGetParaAdjust()); + + // Check conversion from paragraph adjust to AWT text align + fnSetParaAdjust(style::ParagraphAdjust_LEFT); + CPPUNIT_ASSERT_EQUAL(awt::TextAlign::LEFT, fnGetTextAlign()); + + fnSetParaAdjust(style::ParagraphAdjust_CENTER); + CPPUNIT_ASSERT_EQUAL(awt::TextAlign::CENTER, fnGetTextAlign()); + + fnSetParaAdjust(style::ParagraphAdjust_RIGHT); + CPPUNIT_ASSERT_EQUAL(awt::TextAlign::RIGHT, fnGetTextAlign()); + + fnSetParaAdjust(style::ParagraphAdjust_BLOCK); + CPPUNIT_ASSERT_EQUAL(awt::TextAlign::RIGHT, fnGetTextAlign()); + + fnSetParaAdjust(style::ParagraphAdjust_STRETCH); + CPPUNIT_ASSERT_EQUAL(awt::TextAlign::LEFT, fnGetTextAlign()); + + // Like Calc, Base treats left/right align as start/end. RTL support + // is provided through RTL UI, which mirrors paragraph direction and + // alignment. Just check that these values are remapped correctly. + fnSetParaAdjust(style::ParagraphAdjust_START); + CPPUNIT_ASSERT_EQUAL(awt::TextAlign::LEFT, fnGetTextAlign()); + + fnSetParaAdjust(style::ParagraphAdjust_END); + CPPUNIT_ASSERT_EQUAL(awt::TextAlign::RIGHT, fnGetTextAlign()); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/forms/elementimport.cxx b/xmloff/source/forms/elementimport.cxx index cff6a4d40d8f..99ccdf30fc84 100644 --- a/xmloff/source/forms/elementimport.cxx +++ b/xmloff/source/forms/elementimport.cxx @@ -28,7 +28,7 @@ #include #include "formenums.hxx" #include -#include "gridcolumnproptranslator.hxx" +#include #include "property_description.hxx" #include "property_meta_data.hxx" diff --git a/xmloff/source/forms/gridcolumnproptranslator.cxx b/xmloff/source/forms/gridcolumnproptranslator.cxx index 6e11bf5bc4f7..79540f459386 100644 --- a/xmloff/source/forms/gridcolumnproptranslator.cxx +++ b/xmloff/source/forms/gridcolumnproptranslator.cxx @@ -17,7 +17,7 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#include "gridcolumnproptranslator.hxx" +#include #include #include @@ -66,6 +66,8 @@ namespace xmloff { ParagraphAdjust_RIGHT, awt::TextAlign::RIGHT }, { ParagraphAdjust_BLOCK, awt::TextAlign::RIGHT }, { ParagraphAdjust_STRETCH, awt::TextAlign::LEFT }, + { ParagraphAdjust_START, awt::TextAlign::LEFT }, + { ParagraphAdjust_END, awt::TextAlign::RIGHT }, { ParagraphAdjust::ParagraphAdjust_MAKE_FIXED_SIZE, awt::TextAlign::LEFT }, { ParagraphAdjust::ParagraphAdjust_MAKE_FIXED_SIZE, -1 } }; @@ -269,7 +271,7 @@ namespace xmloff if ( nAlignPos != -1 ) aTranslatedNames.getArray()[ nAlignPos ] = ALIGN; - aValues = m_xGridColumn->getPropertyValues( aPropertyNames ); + aValues = m_xGridColumn->getPropertyValues( aTranslatedNames ); if ( nAlignPos != -1 ) valueAlignToParaAdjust( aValues.getArray()[ nAlignPos ] ); diff --git a/xmloff/source/forms/gridcolumnproptranslator.hxx b/xmloff/source/forms/gridcolumnproptranslator.hxx deleted file mode 100644 index 9a49cd9c3dea..000000000000 --- a/xmloff/source/forms/gridcolumnproptranslator.hxx +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- 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 -#include -#include - -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: */