[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 <nroberts@libreoffice.org>
This commit is contained in:
Neil Roberts
2026-06-09 13:11:16 +02:00
parent 599d616d3e
commit fa05a80aae
5 changed files with 317 additions and 3 deletions
@@ -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;
};
+77
View File
@@ -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:
+1
View File
@@ -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 \
+200
View File
@@ -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 <conditio.hxx>
#include <helper/qahelper.hxx>
#include <tools/color.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/sheet/ColorScaleEntryType.hpp>
#include <com/sun/star/sheet/ConditionEntryType.hpp>
#include <com/sun/star/sheet/XColorScaleEntry.hpp>
#include <com/sun/star/sheet/XConditionalFormats.hpp>
#include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
#include <com/sun/star/sheet/XSpreadsheet.hpp>
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<lang::XMultiServiceFactory> m_xMSF;
uno::Reference<sheet::XSpreadsheetDocument> m_xDoc;
uno::Reference<container::XIndexAccess> m_xSheets;
uno::Reference<sheet::XSpreadsheet> m_xSheet;
uno::Reference<beans::XPropertySet> m_xSheetProperties;
uno::Reference<sheet::XConditionalFormats> 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<sheet::XSheetCellRangeContainer> 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<sheet::XConditionalFormat> xConditionalFormat
= m_xConditionalFormats->getConditionalFormats()[0];
xConditionalFormat->createEntry(sheet::ConditionEntryType::COLORSCALE2, 0);
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xConditionalFormat->getCount());
uno::Reference<beans::XPropertySet> xEntry(xConditionalFormat->getByIndex(0),
uno::UNO_QUERY_THROW);
uno::Sequence<uno::Reference<sheet::XColorScaleEntry>> 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<sheet::XSheetCellRangeContainer> 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<sheet::XConditionalFormat> xConditionalFormat
= m_xConditionalFormats->getConditionalFormats()[0];
xConditionalFormat->createEntry(sheet::ConditionEntryType::COLORSCALE3, 0);
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xConditionalFormat->getCount());
uno::Reference<beans::XPropertySet> xEntry(xConditionalFormat->getByIndex(0),
uno::UNO_QUERY_THROW);
uno::Sequence<uno::Reference<sheet::XColorScaleEntry>> 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: */
+31 -3
View File
@@ -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<ScColorScaleFormat*>(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<beans::XPropertySetInfo> SAL_CALL ScColorScaleFormatObj::getPropertySetInfo()