From 9009663deb8f0862f419fd99bf0b761c7f923eff Mon Sep 17 00:00:00 2001 From: Tomaž Vajngerl Date: Sun, 26 Feb 2017 22:48:06 +0100 Subject: tdf#83257 [API-CHANGE] Pivot chart implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a squashed commit of the pivot chart implementation. Some of the changes: - Add pivot chart specific (pivot table) data provider which provides the data from a pivot table to the associated chart. - When inserting a chart and the cursor is in a pivot table, in that case insert a pivot chart - Modify the pivot chart when the pivot table changes - Collect and set the number format for the values - isDataFromSpreadsheet check for the creation wizard - In ChartView (and VLegend) check if the data provider is a pivot chart data provider and get the pivot table field names to create the buttons on the UI. - Adds the functionallity to show a filter pop-up (from calc) when clicking on row / column / page field buttons. - Remove (X)PopupRequest as we won't need it. - Add ODF import/export for pivot charts: + Added loext:data-pilot-source attribute on chart:chart which is the internal name of the pivot table with which the pivot chart is associated with. If the element is present, then the it means the chart is a pivot chart, else it is a normal chart + Added service to create pivot chart data provider through UNO + Add new methods to XPivotChartDataProvider to create value and label data sequences separately from the data source, which is needed for pivot chart import + When importing defer setting the data provider until a later time when we know if we are creating a chart od a pivot chart - Pivot chart ODF round-trip test - Add table pivot chart supplier API: This adds the XTablePivotChartSupplier and related interfaces so we can access, create, delete pivot charts from UNO in a sheet document. With this we now distinguish between normal charts and pivot charts. This was mainly needed because we can't extend the "published" interfaces of TableChartSupplier. - Added an extensive test, which uses the API to create a new pivot chart when there was none, and checks that the pivot chart updates when the pivot table updates. Change-Id: Ia9ed96fd6b1d342e61c2f7f9fa33a5e03dda21af Reviewed-on: https://gerrit.libreoffice.org/36023 Reviewed-by: Tomaž Vajngerl Tested-by: Tomaž Vajngerl --- chart2/qa/extras/PivotChartTest.cxx | 298 ++++++++++++++++++++++ chart2/qa/extras/charttest.hxx | 77 +++++- chart2/qa/extras/data/ods/PivotChartRoundTrip.ods | Bin 0 -> 21988 bytes chart2/qa/extras/data/ods/PivotTableExample.ods | Bin 0 -> 17183 bytes 4 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 chart2/qa/extras/PivotChartTest.cxx create mode 100644 chart2/qa/extras/data/ods/PivotChartRoundTrip.ods create mode 100644 chart2/qa/extras/data/ods/PivotTableExample.ods (limited to 'chart2/qa') diff --git a/chart2/qa/extras/PivotChartTest.cxx b/chart2/qa/extras/PivotChartTest.cxx new file mode 100644 index 000000000000..8d1c11d0ce5d --- /dev/null +++ b/chart2/qa/extras/PivotChartTest.cxx @@ -0,0 +1,298 @@ +/* -*- 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 "charttest.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class PivotChartTest : public ChartTest +{ +public: + PivotChartTest() : ChartTest() + {} + + void testRoundtrip(); + void testChangePivotTable(); + + CPPUNIT_TEST_SUITE(PivotChartTest); + CPPUNIT_TEST(testRoundtrip); + CPPUNIT_TEST(testChangePivotTable); + CPPUNIT_TEST_SUITE_END(); +}; + +namespace +{ + +void lclModifyOrientation(uno::Reference const & xDescriptor, + OUString const & sFieldName, + sheet::DataPilotFieldOrientation eOrientation) +{ + uno::Reference xPilotIndexAccess(xDescriptor->getDataPilotFields(), UNO_QUERY_THROW); + sal_Int32 nCount = xPilotIndexAccess->getCount(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + uno::Reference xNamed(xPilotIndexAccess->getByIndex(i), UNO_QUERY_THROW); + OUString aName = xNamed->getName(); + uno::Reference xPropSet(xNamed, UNO_QUERY_THROW); + if (aName == sFieldName) + xPropSet->setPropertyValue("Orientation", uno::makeAny(eOrientation)); + } +} + +bool lclCheckSequence(std::vector const & reference, + uno::Sequence const & values, + double delta) +{ + if (reference.size() != size_t(values.getLength())) + { + printf ("Sequence size differs - reference is %ld but actual is %ld\n", + reference.size(), size_t(values.getLength())); + return false; + } + + for (size_t i = 0; i < reference.size(); ++i) + { + double value = values[i].get(); + + if (std::fabs(reference[i] - value) > delta) + { + printf ("Value %f is not the same as reference %f (delta %f)\n", value, reference[i], delta); + return false; + } + } + return true; +} + +OUString lclGetLabel(Reference const & xChartDoc, sal_Int32 nSeriesIndex) +{ + Reference xLabelDataSequence = getLabelDataSequenceFromDoc(xChartDoc, nSeriesIndex); + return xLabelDataSequence->getData()[0].get(); +} + +uno::Reference lclGetPivotTableByName(sal_Int32 nIndex, OUString const & sPivotTableName, + uno::Reference const & xComponent) +{ + uno::Reference xDoc(xComponent, UNO_QUERY_THROW); + uno::Reference xSheetIndexAccess(xDoc->getSheets(), UNO_QUERY_THROW); + uno::Any aAny = xSheetIndexAccess->getByIndex(nIndex); + uno::Reference xSheet; + CPPUNIT_ASSERT(aAny >>= xSheet); + uno::Reference xDataPilotTablesSupplier(xSheet, uno::UNO_QUERY_THROW); + uno::Reference xDataPilotTables = xDataPilotTablesSupplier->getDataPilotTables(); + return uno::Reference(xDataPilotTables->getByName(sPivotTableName), UNO_QUERY_THROW); +} + +} // end anonymous namespace + +void PivotChartTest::testRoundtrip() +{ + uno::Sequence xSequence; + Reference xChartDoc; + + std::vector aReference1 { 10162.033139, 16614.523063, 27944.146101 }; + OUString aExpectedLabel1("Exp."); + + std::vector aReference2 { 101879.458079, 178636.929704, 314626.484864 }; + OUString aExpectedLabel2("Rev."); + + load("/chart2/qa/extras/data/ods/", "PivotChartRoundTrip.ods"); + + xChartDoc = getPivotChartDocFromSheet(1, mxComponent); + CPPUNIT_ASSERT(xChartDoc.is()); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), getNumberOfDataSeries(xChartDoc)); + + // Check the data series + { + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 0)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference1, xSequence, 1E-4)); + CPPUNIT_ASSERT_EQUAL(aExpectedLabel1, lclGetLabel(xChartDoc, 0)); + } + { + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 1)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference2, xSequence, 1E-4)); + CPPUNIT_ASSERT_EQUAL(aExpectedLabel2, lclGetLabel(xChartDoc, 1)); + } + + // Modify the pivot table + { + uno::Reference xDataPilotTable = lclGetPivotTableByName(1, "DataPilot1", mxComponent); + uno::Reference xDataPilotDescriptor(xDataPilotTable, UNO_QUERY_THROW); + + lclModifyOrientation(xDataPilotDescriptor, "Exp.", sheet::DataPilotFieldOrientation_HIDDEN); + } + + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), getNumberOfDataSeries(xChartDoc)); + + // Check again the data series + { + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 0)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference2, xSequence, 1E-4)); + CPPUNIT_ASSERT_EQUAL(OUString(""), lclGetLabel(xChartDoc, 0)); + } + + reload("calc8"); + + xChartDoc = getPivotChartDocFromSheet(1, mxComponent); + CPPUNIT_ASSERT(xChartDoc.is()); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), getNumberOfDataSeries(xChartDoc)); + + // Check again the data series + { + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 0)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference2, xSequence, 1E-4)); + CPPUNIT_ASSERT_EQUAL(OUString(""), lclGetLabel(xChartDoc, 0)); + } +} + +void PivotChartTest::testChangePivotTable() +{ + uno::Sequence xSequence; + Reference xChartDoc; + + load("/chart2/qa/extras/data/ods/", "PivotTableExample.ods"); + + // Check that we don't have any pivot chart in the document + uno::Reference xTablePivotCharts = getTablePivotChartsFromSheet(1, mxComponent); + uno::Reference xIndexAccess(xTablePivotCharts, UNO_QUERY_THROW); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xIndexAccess->getCount()); + + // Create a new pivot chart + xTablePivotCharts->addNewByName("Chart", awt::Rectangle{0, 0, 9000, 9000}, "DataPilot1"); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xIndexAccess->getCount()); + + // Get the pivot chart document so we ca access its data + xChartDoc.set(getPivotChartDocFromSheet(xTablePivotCharts, 0)); + + CPPUNIT_ASSERT(xChartDoc.is()); + + // Check first data series + { + std::vector aReference { 10162.033139, 16614.523063, 27944.146101 }; + OUString aExpectedLabel("Exp."); + + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 0)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference, xSequence, 1E-4)); + + CPPUNIT_ASSERT_EQUAL(aExpectedLabel, lclGetLabel(xChartDoc, 0)); + } + + // Check second data series + { + std::vector aReference { 101879.458079, 178636.929704, 314626.484864 }; + OUString aExpectedLabel("Rev."); + + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 1)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference, xSequence, 1E-4)); + + CPPUNIT_ASSERT_EQUAL(aExpectedLabel, lclGetLabel(xChartDoc, 1)); + } + + // Modify the pivot table + { + uno::Reference xDataPilotTable = lclGetPivotTableByName(1, "DataPilot1", mxComponent); + uno::Reference xDataPilotDescriptor(xDataPilotTable, UNO_QUERY_THROW); + + lclModifyOrientation(xDataPilotDescriptor, "Service Month", sheet::DataPilotFieldOrientation_ROW); + lclModifyOrientation(xDataPilotDescriptor, "Group Segment", sheet::DataPilotFieldOrientation_COLUMN); + lclModifyOrientation(xDataPilotDescriptor, "Rev.", sheet::DataPilotFieldOrientation_HIDDEN); + } + + // Check the pivot chart again as we expect it has been updated when we updated the pivot table + + CPPUNIT_ASSERT(xChartDoc.is()); + + // Check the first data series + { + std::vector aReference { 2855.559, 1780.326, 2208.713, 2130.064, 1187.371 }; + OUString aExpectedLabel("Big"); + + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 0)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference, xSequence, 1E-3)); + + CPPUNIT_ASSERT_EQUAL(aExpectedLabel, lclGetLabel(xChartDoc, 0)); + } + + // Check the second data series + { + std::vector aReference { 4098.908, 2527.286, 4299.716, 2362.225, 3326.389 }; + OUString aExpectedLabel("Medium"); + + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 1)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference, xSequence, 1E-3)); + + CPPUNIT_ASSERT_EQUAL(aExpectedLabel, lclGetLabel(xChartDoc, 1)); + } + + // Check the third data series + { + std::vector aReference { 4926.303, 5684.060, 4201.398, 7290.795, 5841.591 }; + OUString aExpectedLabel("Small"); + + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 2)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference, xSequence, 1E-3)); + + CPPUNIT_ASSERT_EQUAL(aExpectedLabel, lclGetLabel(xChartDoc, 2)); + } + + // Modify the pivot table + { + uno::Reference xDataPilotTable = lclGetPivotTableByName(1, "DataPilot1", mxComponent); + uno::Reference xDataPilotDescriptor(xDataPilotTable, UNO_QUERY_THROW); + + lclModifyOrientation(xDataPilotDescriptor, "Service Month", sheet::DataPilotFieldOrientation_HIDDEN); + } + + // Check the pivot chart again as we expect it has been updated when we updated the pivot table + + CPPUNIT_ASSERT(xChartDoc.is()); + + // Check the first data series + { + std::vector aReference { 10162.033139 }; + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 0)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference, xSequence, 1E-3)); + CPPUNIT_ASSERT_EQUAL(OUString("Big"), lclGetLabel(xChartDoc, 0)); + } + // Check the second data series + { + std::vector aReference { 16614.523063 }; + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 1)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference, xSequence, 1E-3)); + CPPUNIT_ASSERT_EQUAL(OUString("Medium"), lclGetLabel(xChartDoc, 1)); + } + // Check the third data series + { + std::vector aReference { 27944.146101 }; + xSequence = getDataSequenceFromDocByRole(xChartDoc, "values-y", 2)->getData(); + CPPUNIT_ASSERT(lclCheckSequence(aReference, xSequence, 1E-3)); + CPPUNIT_ASSERT_EQUAL(OUString("Small"), lclGetLabel(xChartDoc, 2)); + } +} + +CPPUNIT_TEST_SUITE_REGISTRATION(PivotChartTest); + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/qa/extras/charttest.hxx b/chart2/qa/extras/charttest.hxx index 1d2f4afe9150..b75dac6d25b5 100644 --- a/chart2/qa/extras/charttest.hxx +++ b/chart2/qa/extras/charttest.hxx @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -213,6 +216,64 @@ Reference< chart2::XChartDocument > getChartDocFromSheet( sal_Int32 nSheet, uno: return xChartDoc; } +uno::Reference getTablePivotChartsFromSheet(sal_Int32 nSheet, uno::Reference const & xComponent) +{ + uno::Reference xDoc(xComponent, UNO_QUERY_THROW); + CPPUNIT_ASSERT(xDoc.is()); + + uno::Reference xIA(xDoc->getSheets(), UNO_QUERY_THROW); + CPPUNIT_ASSERT(xIA.is()); + + uno::Reference xChartSupplier(xIA->getByIndex(nSheet), UNO_QUERY_THROW); + CPPUNIT_ASSERT(xChartSupplier.is()); + + uno::Reference xTablePivotCharts = xChartSupplier->getPivotCharts(); + CPPUNIT_ASSERT(xTablePivotCharts.is()); + + return xTablePivotCharts; +} + +Reference getPivotChartCompFromSheet(sal_Int32 nSheet, uno::Reference const & xComponent) +{ + uno::Reference xTablePivotCharts = getTablePivotChartsFromSheet(nSheet, xComponent); + + uno::Reference xIACharts(xTablePivotCharts, UNO_QUERY_THROW); + uno::Reference xTablePivotChart(xIACharts->getByIndex(0), UNO_QUERY_THROW); + CPPUNIT_ASSERT(xTablePivotChart.is()); + + uno::Reference xEmbObjectSupplier(xTablePivotChart, UNO_QUERY_THROW); + CPPUNIT_ASSERT(xEmbObjectSupplier.is()); + + uno::Reference xChartComp(xEmbObjectSupplier->getEmbeddedObject(), UNO_QUERY_THROW); + CPPUNIT_ASSERT(xChartComp.is()); + + return xChartComp; +} + +Reference getPivotChartDocFromSheet(sal_Int32 nSheet, uno::Reference const & xComponent) +{ + uno::Reference xChartDoc(getPivotChartCompFromSheet(nSheet, xComponent), UNO_QUERY_THROW); + CPPUNIT_ASSERT(xChartDoc.is()); + return xChartDoc; +} + +Reference getPivotChartDocFromSheet(uno::Reference const & xTablePivotCharts, sal_Int32 nIndex) +{ + uno::Reference xIACharts(xTablePivotCharts, UNO_QUERY_THROW); + uno::Reference xTablePivotChart(xIACharts->getByIndex(nIndex), UNO_QUERY_THROW); + CPPUNIT_ASSERT(xTablePivotChart.is()); + + uno::Reference xEmbObjectSupplier(xTablePivotChart, UNO_QUERY_THROW); + CPPUNIT_ASSERT(xEmbObjectSupplier.is()); + + uno::Reference xChartComp(xEmbObjectSupplier->getEmbeddedObject(), UNO_QUERY_THROW); + CPPUNIT_ASSERT(xChartComp.is()); + + uno::Reference xChartDoc(xChartComp, UNO_QUERY_THROW); + CPPUNIT_ASSERT(xChartDoc.is()); + return xChartDoc; +} + Reference< chart2::XChartType > getChartTypeFromDoc( Reference< chart2::XChartDocument > const & xChartDoc, sal_Int32 nChartType, sal_Int32 nCooSys = 0 ) { @@ -257,8 +318,20 @@ Reference getAxisFromDoc( return xAxis; } -Reference< chart2::XDataSeries > getDataSeriesFromDoc( uno::Reference< chart2::XChartDocument > const & xChartDoc, - sal_Int32 nDataSeries, sal_Int32 nChartType = 0, sal_Int32 nCooSys = 0 ) +sal_Int32 getNumberOfDataSeries(uno::Reference const & xChartDoc, + sal_Int32 nChartType = 0, sal_Int32 nCooSys = 0) +{ + Reference xChartType = getChartTypeFromDoc(xChartDoc, nChartType, nCooSys); + Reference xDataSeriesContainer(xChartType, UNO_QUERY_THROW); + CPPUNIT_ASSERT(xDataSeriesContainer.is()); + + uno::Sequence> xSeriesSequence(xDataSeriesContainer->getDataSeries()); + return xSeriesSequence.getLength(); +} + +Reference< chart2::XDataSeries > getDataSeriesFromDoc(uno::Reference const & xChartDoc, + sal_Int32 nDataSeries, sal_Int32 nChartType = 0, + sal_Int32 nCooSys = 0) { Reference< chart2::XChartType > xChartType = getChartTypeFromDoc( xChartDoc, nChartType, nCooSys ); Reference< chart2::XDataSeriesContainer > xDataSeriesContainer( xChartType, UNO_QUERY_THROW ); diff --git a/chart2/qa/extras/data/ods/PivotChartRoundTrip.ods b/chart2/qa/extras/data/ods/PivotChartRoundTrip.ods new file mode 100644 index 000000000000..c34521e0bc52 Binary files /dev/null and b/chart2/qa/extras/data/ods/PivotChartRoundTrip.ods differ diff --git a/chart2/qa/extras/data/ods/PivotTableExample.ods b/chart2/qa/extras/data/ods/PivotTableExample.ods new file mode 100644 index 000000000000..bc8df8170208 Binary files /dev/null and b/chart2/qa/extras/data/ods/PivotTableExample.ods differ -- cgit