From fa05a80aae42d11bcbf8f8b5effc2d1f0145807e Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 3 Jun 2026 13:02:19 +0200 Subject: [PATCH] [API CHANGE] tdf#171950 Add API to create 2- and 3- color scales The existing API allows creating a color scale conditional format but it has no entries and there is no API to create an instance of XColorScaleEntry in order to add one. For data bars the existing API instead creates a conditional format with two existing entries and then you can use the XDataBarEntry interface to modify them. This patch makes the color scale API work like the databar one. It adds constants to ConditionEntryType to create a 2-color or 3-color scale which have the entries pre-created. This is an API change because it adds constants to an unpublished constant group. It also makes it so that the getType method on the conditional format will now return either COLORSCALE2 or COLORSCALE3 depending on the number of entries. In practice I think this is very unlikely to break any existing code. Change-Id: I28faa6c66ac3963220786e8da45a2d550058f095 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/205944 Tested-by: Jenkins Reviewed-by: Neil Roberts --- .../com/sun/star/sheet/ConditionEntryType.idl | 8 + sc/CppunitTest_sc_color_scale_api.mk | 77 +++++++ sc/Module_sc.mk | 1 + sc/qa/extras/color_scale_api.cxx | 200 ++++++++++++++++++ sc/source/ui/unoobj/condformatuno.cxx | 34 ++- 5 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 sc/CppunitTest_sc_color_scale_api.mk create mode 100644 sc/qa/extras/color_scale_api.cxx diff --git a/offapi/com/sun/star/sheet/ConditionEntryType.idl b/offapi/com/sun/star/sheet/ConditionEntryType.idl index 0266e6721474..5b717850c87b 100644 --- a/offapi/com/sun/star/sheet/ConditionEntryType.idl +++ b/offapi/com/sun/star/sheet/ConditionEntryType.idl @@ -20,6 +20,14 @@ constants ConditionEntryType const long ICONSET = 3; const long DATE = 4; + + /** A color scale conditional format with a min and a max + */ + const long COLORSCALE2 = 5; + + /** A color scale conditional format with a min, a middle and a max + */ + const long COLORSCALE3 = 6; }; diff --git a/sc/CppunitTest_sc_color_scale_api.mk b/sc/CppunitTest_sc_color_scale_api.mk new file mode 100644 index 000000000000..fcfbbe0e67b0 --- /dev/null +++ b/sc/CppunitTest_sc_color_scale_api.mk @@ -0,0 +1,77 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*- +# +# 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,sc_color_scale_api)) + +$(eval $(call gb_CppunitTest_use_common_precompiled_header,sc_color_scale_api)) + +$(eval $(call gb_CppunitTest_add_exception_objects,sc_color_scale_api, \ + sc/qa/extras/color_scale_api \ +)) + +$(eval $(call gb_CppunitTest_use_externals,sc_color_scale_api, \ + boost_headers \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,sc_color_scale_api, \ + basegfx \ + comphelper \ + cppu \ + cppuhelper \ + drawinglayer \ + drawinglayercore \ + editeng \ + for \ + forui \ + i18nlangtag \ + msfilter \ + oox \ + sal \ + salhelper \ + sax \ + sb \ + sc \ + scqahelper \ + sfx \ + sot \ + subsequenttest \ + svl \ + svt \ + svx \ + svxcore \ + test \ + tk \ + tl \ + ucbhelper \ + unotest \ + utl \ + $(call gb_Helper_optional,SCRIPTING, \ + vbahelper) \ + vcl \ + xo \ +)) + +$(eval $(call gb_CppunitTest_set_include,sc_color_scale_api,\ + -I$(SRCDIR)/sc/inc \ + -I$(SRCDIR)/sc/qa/unit \ + -I$(SRCDIR)/sc/source/ui/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,sc_color_scale_api)) + +$(eval $(call gb_CppunitTest_use_ure,sc_color_scale_api)) +$(eval $(call gb_CppunitTest_use_vcl,sc_color_scale_api)) + +$(eval $(call gb_CppunitTest_use_rdb,sc_color_scale_api,services)) + +$(eval $(call gb_CppunitTest_use_configuration,sc_color_scale_api)) + +# vim: set noet sw=4 ts=4: diff --git a/sc/Module_sc.mk b/sc/Module_sc.mk index 2a58bca9e8ea..75cc2075686e 100644 --- a/sc/Module_sc.mk +++ b/sc/Module_sc.mk @@ -81,6 +81,7 @@ endif $(eval $(call gb_Module_add_slowcheck_targets,sc, \ CppunitTest_sc_anchor_test \ + CppunitTest_sc_color_scale_api \ CppunitTest_sc_cond_format \ CppunitTest_sc_cond_format_merge \ CppunitTest_sc_copypaste \ diff --git a/sc/qa/extras/color_scale_api.cxx b/sc/qa/extras/color_scale_api.cxx new file mode 100644 index 000000000000..cf2a08ae1063 --- /dev/null +++ b/sc/qa/extras/color_scale_api.cxx @@ -0,0 +1,200 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 +#include +#include +#include +#include +#include + +using namespace css; + +namespace sc_apitest +{ +class ScColorScaleTest : public ScModelTestBase +{ +public: + ScColorScaleTest(); + + void setUp() override; + void tearDown() override; + + void testCreateColorScale2(); + void testCreateColorScale3(); + + CPPUNIT_TEST_SUITE(ScColorScaleTest); + CPPUNIT_TEST(testCreateColorScale2); + CPPUNIT_TEST(testCreateColorScale3); + CPPUNIT_TEST_SUITE_END(); + +private: + uno::Reference m_xMSF; + uno::Reference m_xDoc; + uno::Reference m_xSheets; + uno::Reference m_xSheet; + uno::Reference m_xSheetProperties; + uno::Reference m_xConditionalFormats; +}; + +ScColorScaleTest::ScColorScaleTest() + : ScModelTestBase(u"sc/qa/extras/testdocuments/"_ustr) +{ +} + +void ScColorScaleTest::setUp() +{ + ScModelTestBase::setUp(); + + createScDoc(); + + m_xMSF.set(mxComponent, uno::UNO_QUERY_THROW); + m_xDoc.set(mxComponent, uno::UNO_QUERY_THROW); + m_xSheets.set(m_xDoc->getSheets(), uno::UNO_QUERY_THROW); + m_xSheet.set(m_xSheets->getByIndex(0), uno::UNO_QUERY_THROW); + m_xSheetProperties.set(m_xSheet, uno::UNO_QUERY_THROW); + m_xConditionalFormats.set(m_xSheetProperties->getPropertyValue("ConditionalFormats"), + uno::UNO_QUERY_THROW); +} + +void ScColorScaleTest::tearDown() +{ + m_xConditionalFormats.clear(); + m_xSheetProperties.clear(); + m_xSheet.clear(); + m_xSheets.clear(); + m_xDoc.clear(); + m_xMSF.clear(); + + ScModelTestBase::tearDown(); +} + +void ScColorScaleTest::testCreateColorScale2() +{ + // Tests creating a COLORSCALE2. This should allocate two entries that we can modify. See + // tdf#171950. This also tests tdf#172333 as a side effect. + + // Set color scale conditional formatting on A1:A2 + uno::Reference xRanges( + m_xMSF->createInstance("com.sun.star.sheet.SheetCellRanges"), uno::UNO_QUERY_THROW); + table::CellRangeAddress a1a2 = { 0, 0, 0, 0, 1 }; + xRanges->addRangeAddress(a1a2, false); + m_xConditionalFormats->createByRange(xRanges); + uno::Reference xConditionalFormat + = m_xConditionalFormats->getConditionalFormats()[0]; + xConditionalFormat->createEntry(sheet::ConditionEntryType::COLORSCALE2, 0); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xConditionalFormat->getCount()); + + uno::Reference xEntry(xConditionalFormat->getByIndex(0), + uno::UNO_QUERY_THROW); + uno::Sequence> aColorScales; + CPPUNIT_ASSERT(xEntry->getPropertyValue("ColorScaleEntries") >>= aColorScales); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aColorScales.getLength()); + + aColorScales[0]->setColor(sal_Int32(COL_MAGENTA)); + aColorScales[0]->setType(sheet::ColorScaleEntryType::COLORSCALE_MIN); + + aColorScales[1]->setColor(sal_Int32(COL_BROWN)); + aColorScales[1]->setType(sheet::ColorScaleEntryType::COLORSCALE_MAX); + + // Put 50 in A1 to make a minimum value + insertStringToCell(u"A1"_ustr, u"50"); + // Put 100 in A2 to make a maximum value + insertStringToCell(u"A2"_ustr, u"100"); + + // A1 should be magenta + { + ScConditionalFormat* pCondFormat = getScDoc()->GetCondFormat(0, 0, 0); + ScRefCellValue aCell(*getScDoc(), ScAddress(0, 0, 0)); + Color aColor = pCondFormat->GetData(aCell, ScAddress(0, 0, 0)).mxColorScale.value(); + CPPUNIT_ASSERT_EQUAL(COL_MAGENTA, aColor); + } + // A2 should be brown + { + ScConditionalFormat* pCondFormat = getScDoc()->GetCondFormat(0, 1, 0); + ScRefCellValue aCell(*getScDoc(), ScAddress(0, 1, 0)); + Color aColor = pCondFormat->GetData(aCell, ScAddress(0, 1, 0)).mxColorScale.value(); + CPPUNIT_ASSERT_EQUAL(COL_BROWN, aColor); + } +} + +void ScColorScaleTest::testCreateColorScale3() +{ + // Tests creating a COLORSCALE3. This should allocate three entries that we can modify. See + // tdf#171950. This also tests tdf#172333 as a side effect. + + // Set color scale conditional formatting on A1:A3 + uno::Reference xRanges( + m_xMSF->createInstance("com.sun.star.sheet.SheetCellRanges"), uno::UNO_QUERY_THROW); + table::CellRangeAddress a1a3 = { 0, 0, 0, 0, 2 }; + xRanges->addRangeAddress(a1a3, false); + m_xConditionalFormats->createByRange(xRanges); + uno::Reference xConditionalFormat + = m_xConditionalFormats->getConditionalFormats()[0]; + xConditionalFormat->createEntry(sheet::ConditionEntryType::COLORSCALE3, 0); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xConditionalFormat->getCount()); + + uno::Reference xEntry(xConditionalFormat->getByIndex(0), + uno::UNO_QUERY_THROW); + uno::Sequence> aColorScales; + CPPUNIT_ASSERT(xEntry->getPropertyValue("ColorScaleEntries") >>= aColorScales); + CPPUNIT_ASSERT_EQUAL(sal_Int32(3), aColorScales.getLength()); + + aColorScales[0]->setColor(sal_Int32(COL_MAGENTA)); + aColorScales[0]->setType(sheet::ColorScaleEntryType::COLORSCALE_MIN); + + aColorScales[1]->setColor(sal_Int32(COL_BROWN)); + aColorScales[1]->setType(sheet::ColorScaleEntryType::COLORSCALE_VALUE); + aColorScales[1]->setFormula("75"); + + aColorScales[2]->setColor(sal_Int32(COL_RED)); + aColorScales[2]->setType(sheet::ColorScaleEntryType::COLORSCALE_MAX); + + // Put 50 in A1 to make a minimum value + insertStringToCell(u"A1"_ustr, u"50"); + // Put 75 in A2 to make a middle value + insertStringToCell(u"A2"_ustr, u"75"); + // Put 100 in A3 to make a maximum value + insertStringToCell(u"A3"_ustr, u"100"); + + // A1 should be magenta + { + ScConditionalFormat* pCondFormat = getScDoc()->GetCondFormat(0, 0, 0); + ScRefCellValue aCell(*getScDoc(), ScAddress(0, 0, 0)); + Color aColor = pCondFormat->GetData(aCell, ScAddress(0, 0, 0)).mxColorScale.value(); + CPPUNIT_ASSERT_EQUAL(COL_MAGENTA, aColor); + } + // A2 should be brown + { + ScConditionalFormat* pCondFormat = getScDoc()->GetCondFormat(0, 1, 0); + ScRefCellValue aCell(*getScDoc(), ScAddress(0, 1, 0)); + Color aColor = pCondFormat->GetData(aCell, ScAddress(0, 1, 0)).mxColorScale.value(); + CPPUNIT_ASSERT_EQUAL(COL_BROWN, aColor); + } + // A3 should be red + { + ScConditionalFormat* pCondFormat = getScDoc()->GetCondFormat(0, 2, 0); + ScRefCellValue aCell(*getScDoc(), ScAddress(0, 2, 0)); + Color aColor = pCondFormat->GetData(aCell, ScAddress(0, 2, 0)).mxColorScale.value(); + CPPUNIT_ASSERT_EQUAL(COL_RED, aColor); + } +} + +CPPUNIT_TEST_SUITE_REGISTRATION(ScColorScaleTest); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sc/source/ui/unoobj/condformatuno.cxx b/sc/source/ui/unoobj/condformatuno.cxx index d897797a675a..d481db9b588f 100644 --- a/sc/source/ui/unoobj/condformatuno.cxx +++ b/sc/source/ui/unoobj/condformatuno.cxx @@ -469,6 +469,21 @@ ScDocShell* ScCondFormatObj::getDocShell() return mpDocShell; } +namespace +{ +ScColorScaleFormat* createColorScaleFormat(ScDocument& rDoc, size_t nEntries) +{ + ScColorScaleFormat* pNewEntry = new ScColorScaleFormat(rDoc); + + for (size_t i = 0; i < nEntries; ++i) + pNewEntry->AddEntry(new ScColorScaleEntry); + + pNewEntry->EnsureSize(); + + return pNewEntry; +} +} + void ScCondFormatObj::createEntry(const sal_Int32 nType, const sal_Int32 nPos) { SolarMutexGuard aGuard; @@ -485,8 +500,13 @@ void ScCondFormatObj::createEntry(const sal_Int32 nType, const sal_Int32 nPos) rDoc, pFormat->GetRange().GetTopLeftCorner(), u""_ustr); break; case sheet::ConditionEntryType::COLORSCALE: - pNewEntry = new ScColorScaleFormat(rDoc); - static_cast(pNewEntry)->EnsureSize(); + pNewEntry = createColorScaleFormat(rDoc, 0); + break; + case sheet::ConditionEntryType::COLORSCALE2: + pNewEntry = createColorScaleFormat(rDoc, 2); + break; + case sheet::ConditionEntryType::COLORSCALE3: + pNewEntry = createColorScaleFormat(rDoc, 3); break; case sheet::ConditionEntryType::DATABAR: pNewEntry = new ScDataBarFormat(rDoc); @@ -855,7 +875,15 @@ ScColorScaleFormat* ScColorScaleFormatObj::getCoreObject() sal_Int32 ScColorScaleFormatObj::getType() { - return sheet::ConditionEntryType::COLORSCALE; + switch (getCoreObject()->size()) + { + case 2: + return sheet::ConditionEntryType::COLORSCALE2; + case 3: + return sheet::ConditionEntryType::COLORSCALE3; + default: + return sheet::ConditionEntryType::COLORSCALE; + } } uno::Reference SAL_CALL ScColorScaleFormatObj::getPropertySetInfo()