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 5d1b2baaa9
 Author: Jonathan Clark <jonathan@libreoffice.org>
 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 <jonathan@libreoffice.org>
Tested-by: Jenkins
This commit is contained in:
Jonathan Clark
2026-06-18 13:57:18 +02:00
parent cd857e3183
commit 13d4e74e4f
8 changed files with 386 additions and 71 deletions
-1
View File
@@ -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
+60
View File
@@ -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:
+2 -1
View File
@@ -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,\
@@ -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 <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 SAL_DLLPUBLIC_EXPORT 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: */
+239
View File
@@ -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 <test/unoapi_test.hxx>
#include <com/sun/star/awt/TextAlign.hpp>
#include <com/sun/star/beans/XMultiPropertySet.hdl>
#include <com/sun/star/style/ParagraphAdjust.hpp>
#include <forms/gridcolumnproptranslator.hxx>
#include <comphelper/propertysethelper.hxx>
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<Property> 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<sal_Int16>::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<XPropertySetInfo>::get())
{
aRet <<= Reference<XPropertySetInfo>(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<XPropertySetInfo> SAL_CALL getPropertySetInfo() override
{
return { new MockColumnAlignmentPropertySetInfo };
}
void SAL_CALL setPropertyValues(const Sequence<OUString>& aPropertyNames,
const Sequence<Any>& aValues) override
{
for (sal_Int32 nIndex = 0; nIndex < aPropertyNames.getLength(); ++nIndex)
{
if (aPropertyNames[nIndex] == u"Align"_ustr)
{
aValues[nIndex] >>= nCurrTextAlign;
}
}
}
Sequence<Any> SAL_CALL getPropertyValues(const Sequence<OUString>& aPropertyNames) override
{
Sequence<Any> 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<OUString>& /*aPropertyNames*/,
const Reference<XPropertiesChangeListener>& /*xListener*/) override
{
}
void SAL_CALL removePropertiesChangeListener(
const Reference<XPropertiesChangeListener>& /*xListener*/) override
{
}
void SAL_CALL
firePropertiesChangeEvent(const Sequence<OUString>& /*aPropertyNames*/,
const Reference<XPropertiesChangeListener>& /*xListener*/) override
{
}
// XInterface
Any SAL_CALL queryInterface(const Type& aType) override
{
Any aRet;
if (aType == cppu::UnoType<XMultiPropertySet>::get())
{
aRet <<= Reference<XMultiPropertySet>(this);
}
return aRet;
}
void SAL_CALL acquire() override {}
void SAL_CALL release() override {}
};
}
CPPUNIT_TEST_FIXTURE(Test, testColumnAlignmentConversion)
{
uno::Reference<XMultiPropertySet> xProps = new MockColumnAlignmentMultiPropertySet;
CPPUNIT_ASSERT(xProps.is());
Reference<XPropertySet> 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<sal_Int32>(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: */
+1 -1
View File
@@ -28,7 +28,7 @@
#include <xmloff/txtstyli.hxx>
#include "formenums.hxx"
#include <xmloff/xmltoken.hxx>
#include "gridcolumnproptranslator.hxx"
#include <forms/gridcolumnproptranslator.hxx>
#include "property_description.hxx"
#include "property_meta_data.hxx"
@@ -17,7 +17,7 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include "gridcolumnproptranslator.hxx"
#include <forms/gridcolumnproptranslator.hxx>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/awt/TextAlign.hpp>
@@ -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 ] );
@@ -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 <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: */