tdf#50934: Add a pie-with-remainder-as-another-pie chart type

Implement ODF import/export for bar-of-pie and pie-of-pie types,
and add simple tests for this capability. The associated ODF tags
are implemented in the loext namespace. This also required changing
the schema.

Change-Id: Ib55ae1c5818ad810f7b962d807a9163a3d02ba17
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164436
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/chart2/qa/extras/chart2export.cxx b/chart2/qa/extras/chart2export.cxx
index b46bc77..86f2dee 100644
--- a/chart2/qa/extras/chart2export.cxx
+++ b/chart2/qa/extras/chart2export.cxx
@@ -18,6 +18,7 @@
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/chart2/DataPointLabel.hpp>
#include <com/sun/star/chart/DataLabelPlacement.hpp>
#include <com/sun/star/chart2/PieChartSubType.hpp>

using uno::Reference;
using beans::XPropertySet;
@@ -1132,6 +1133,58 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest, testErrorBarDataRangeODS)
    CPPUNIT_ASSERT_EQUAL(OUString("$Sheet1.$C$1:$C$3"), aNegRange);
}

CPPUNIT_TEST_FIXTURE(Chart2ExportTest, tdf50934_barOfPie)
{
    loadFromFile(u"ods/tdf50934_barOfPie.ods");
    saveAndReload("calc8");

    uno::Reference< chart2::XChartDocument > xChartDoc = getChartDocFromSheet( 0, mxComponent );
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference< chart2::XChartType > xChartType = getChartTypeFromDoc( xChartDoc, 0 );
    CPPUNIT_ASSERT(xChartType.is());

    CPPUNIT_ASSERT_EQUAL(u"com.sun.star.chart2.PieChartType"_ustr,
            xChartType->getChartType());

    // Verify that it saves and loads as bar-of-pie
    Reference< chart2::XDiagram> xDia(xChartDoc->getFirstDiagram());
    CPPUNIT_ASSERT(xDia.is());
    uno::Reference< beans::XPropertySet > xDiaProp( xDia, uno::UNO_QUERY );
    CPPUNIT_ASSERT(xDiaProp.is());
    uno::Any aAny = xDiaProp->getPropertyValue("SubPieType");
    CPPUNIT_ASSERT(aAny.hasValue());
    chart2::PieChartSubType subPieType;
    aAny >>= subPieType;
    CPPUNIT_ASSERT_EQUAL(chart2::PieChartSubType_BAR, subPieType);
}

CPPUNIT_TEST_FIXTURE(Chart2ExportTest, tdf50934_pieOfPie)
{
    loadFromFile(u"ods/tdf50934_pieOfPie.ods");
    saveAndReload("calc8");

    uno::Reference< chart2::XChartDocument > xChartDoc = getChartDocFromSheet( 0, mxComponent );
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference< chart2::XChartType > xChartType = getChartTypeFromDoc( xChartDoc, 0 );
    CPPUNIT_ASSERT(xChartType.is());

    CPPUNIT_ASSERT_EQUAL(u"com.sun.star.chart2.PieChartType"_ustr,
            xChartType->getChartType());

    // Verify that it saves and loads as pie-of-pie
    Reference< chart2::XDiagram> xDia(xChartDoc->getFirstDiagram());
    CPPUNIT_ASSERT(xDia.is());
    uno::Reference< beans::XPropertySet > xDiaProp( xDia, uno::UNO_QUERY );
    CPPUNIT_ASSERT(xDiaProp.is());
    uno::Any aAny = xDiaProp->getPropertyValue("SubPieType");
    CPPUNIT_ASSERT(aAny.hasValue());
    chart2::PieChartSubType subPieType;
    aAny >>= subPieType;
    CPPUNIT_ASSERT_EQUAL(chart2::PieChartSubType_PIE, subPieType);
}

CPPUNIT_TEST_FIXTURE(Chart2ExportTest, testChartCrash)
{
    loadFromFile(u"docx/FDO75975.docx");
diff --git a/chart2/qa/extras/data/ods/tdf50934_barOfPie.ods b/chart2/qa/extras/data/ods/tdf50934_barOfPie.ods
new file mode 100644
index 0000000..d9b577a
--- /dev/null
+++ b/chart2/qa/extras/data/ods/tdf50934_barOfPie.ods
Binary files differ
diff --git a/chart2/qa/extras/data/ods/tdf50934_pieOfPie.ods b/chart2/qa/extras/data/ods/tdf50934_pieOfPie.ods
new file mode 100644
index 0000000..74a9f85
--- /dev/null
+++ b/chart2/qa/extras/data/ods/tdf50934_pieOfPie.ods
Binary files differ
diff --git a/chart2/source/controller/chartapiwrapper/DiagramWrapper.cxx b/chart2/source/controller/chartapiwrapper/DiagramWrapper.cxx
index 949aaa9..2917b98 100644
--- a/chart2/source/controller/chartapiwrapper/DiagramWrapper.cxx
+++ b/chart2/source/controller/chartapiwrapper/DiagramWrapper.cxx
@@ -465,6 +465,15 @@ OUString lcl_getDiagramType( std::u16string_view rTemplateServiceName )
        if( aName.find( u"Area" ) != std::u16string_view::npos )
            return "com.sun.star.chart.AreaDiagram";

        // Handle bar-of-pie and pie-of-pie before simple pie
        // "BarOfPie"
        if( aName.find( u"BarOfPie" ) != std::u16string_view::npos )
            return "com.sun.star.chart.BarOfPieDiagram";

        // "PieOfPie"
        if( aName.find( u"PieOfPie" ) != std::u16string_view::npos )
            return "com.sun.star.chart.PieOfPieDiagram";

        // "Pie" "PieAllExploded" "ThreeDPie" "ThreeDPieAllExploded"
        if( aName.find( u"Pie" ) != std::u16string_view::npos )
            return "com.sun.star.chart.PieDiagram";
diff --git a/chart2/source/inc/ChartType.hxx b/chart2/source/inc/ChartType.hxx
index fccafdb..fa55cf0 100644
--- a/chart2/source/inc/ChartType.hxx
+++ b/chart2/source/inc/ChartType.hxx
@@ -75,12 +75,6 @@ public:
    // ____ XChartType ____
    // still abstract ! implement !
    virtual OUString SAL_CALL getChartType() override = 0;
#if 0
    virtual ::com::sun::star::chart2::PieChartSubType SAL_CALL getPieChartSubType() override
    {
        return ::com::sun::star::chart2::PieChartSubType_NONE;
    }
#endif
    virtual css::uno::Reference< css::chart2::XCoordinateSystem > SAL_CALL
        createCoordinateSystem( ::sal_Int32 DimensionCount ) final override;
    virtual css::uno::Sequence< OUString > SAL_CALL
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index b6fa0dc..ce732e3 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -1909,7 +1909,10 @@ namespace xmloff::token {
        XML_STYLES,
        XML_STYLESHEET,
        XML_SUB_TABLE,
        XML_SUB_BAR,
        XML_SUB_NONE,
        XML_SUBJECT,
        XML_SUB_PIE,
        XML_SUBSET,
        XML_SUBTITLE,
        XML_SUBTOTAL_FIELD,
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index a68033d..b14c02b 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -2717,6 +2717,20 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
    </rng:optional>
  </rng:define>

    <!-- TODO no proposal -->
  <rng:define name="chart-chart-attlist" combine="interleave">
    <rng:optional>
      <rng:attribute name="loext:sub-bar">
        <rng:ref name="boolean"/>
      </rng:attribute>
    </rng:optional>
    <rng:optional>
      <rng:attribute name="loext:sub-pie">
        <rng:ref name="boolean"/>
      </rng:attribute>
    </rng:optional>
  </rng:define>

  <!-- OFFICE-2112, TODO half of this missing in proposal -->
  <rng:define name="table-table-protection">
    <rng:element name="loext:table-protection">
diff --git a/xmloff/source/chart/PropertyMap.hxx b/xmloff/source/chart/PropertyMap.hxx
index cd6370e..ec59875 100644
--- a/xmloff/source/chart/PropertyMap.hxx
+++ b/xmloff/source/chart/PropertyMap.hxx
@@ -42,6 +42,7 @@
#define XML_SCH_TYPE_LABEL_BORDER_STYLE     ( XML_SCH_TYPES_START + 17 )
#define XML_SCH_TYPE_LABEL_BORDER_OPACITY   ( XML_SCH_TYPES_START + 18 )
#define XML_SCH_TYPE_LABEL_FILL_STYLE       ( XML_SCH_TYPES_START + 19 )
#define XML_SCH_TYPE_OF_PIE_TYPE            ( XML_SCH_TYPES_START + 20 )

// context ids
#define XML_SCH_CONTEXT_USER_SYMBOL                 ( XML_SCH_CTF_START + 0 )
diff --git a/xmloff/source/chart/SchXMLChartContext.cxx b/xmloff/source/chart/SchXMLChartContext.cxx
index b2def8e3..9d5522f 100644
--- a/xmloff/source/chart/SchXMLChartContext.cxx
+++ b/xmloff/source/chart/SchXMLChartContext.cxx
@@ -231,7 +231,8 @@ SchXMLChartContext::SchXMLChartContext( SchXMLImportHelper& rImpHelper,
        mbColHasLabels( false ),
        mbRowHasLabels( false ),
        meDataRowSource( chart::ChartDataRowSource_COLUMNS ),
        mbIsStockChart( false )
        mbIsStockChart( false ),
        mPieSubType(com::sun::star::chart2::PieChartSubType_NONE)
{
}

@@ -317,6 +318,7 @@ void SchXMLChartContext::startFastElement( sal_Int32 /*nElement*/,
    OUString sAutoStyleName;
    OUString aOldChartTypeName;
    bool bHasAddin = false;
    mPieSubType = com::sun::star::chart2::PieChartSubType_NONE;

    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
    {
@@ -385,6 +387,16 @@ void SchXMLChartContext::startFastElement( sal_Int32 /*nElement*/,
            case XML_ELEMENT(CHART,  XML_ROW_MAPPING):
                msRowTrans = aIter.toString();
                break;
            case XML_ELEMENT(LO_EXT, XML_SUB_BAR):
                if (aIter.toString().toBoolean()) {
                    mPieSubType = com::sun::star::chart2::PieChartSubType_BAR;
                }
                break;
            case XML_ELEMENT(LO_EXT, XML_SUB_PIE):
                if (aIter.toString().toBoolean()) {
                    mPieSubType = com::sun::star::chart2::PieChartSubType_PIE;
                }
                break;
            default:
                XMLOFF_WARN_UNKNOWN("xmloff", aIter);
        }
@@ -752,6 +764,15 @@ void SchXMLChartContext::endFastElement(sal_Int32 )
    // cleanup: remove empty chart type groups
    lcl_removeEmptyChartTypeGroups( xNewDoc );

    // Handle sub-pie type. Is this the right place to do this?
    if (maChartTypeServiceName == "com.sun.star.chart2.PieChartType") {
        Reference< chart2::XDiagram> xDia(xNewDoc->getFirstDiagram());
        uno::Reference< beans::XPropertySet > xDiaProp( xDia, uno::UNO_QUERY );
        if( xDiaProp.is()) {
            xDiaProp->setPropertyValue("SubPieType", uno::Any(mPieSubType));
        }
    }

    // set stack mode before a potential chart type detection (in case we have a rectangular range)
    uno::Reference< chart::XDiagram > xDiagram( xDoc->getDiagram() );
    uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY );
diff --git a/xmloff/source/chart/SchXMLChartContext.hxx b/xmloff/source/chart/SchXMLChartContext.hxx
index 40ba13e0..f79be55 100644
--- a/xmloff/source/chart/SchXMLChartContext.hxx
+++ b/xmloff/source/chart/SchXMLChartContext.hxx
@@ -23,6 +23,8 @@
#include <com/sun/star/chart/ChartDataRowSource.hpp>
#include <com/sun/star/awt/Size.hpp>

#include <com/sun/star/chart2/PieChartSubType.hpp>

#include "transporttypes.hxx"

#include <vector>
@@ -98,6 +100,7 @@ private:
    bool mbRowHasLabels;
    css::chart::ChartDataRowSource meDataRowSource;
    bool mbIsStockChart;
    com::sun::star::chart2::PieChartSubType mPieSubType;

    OUString msCategoriesAddress;
    OUString msChartAddress;
diff --git a/xmloff/source/chart/SchXMLExport.cxx b/xmloff/source/chart/SchXMLExport.cxx
index 4fb0227a..79238a5 100644
--- a/xmloff/source/chart/SchXMLExport.cxx
+++ b/xmloff/source/chart/SchXMLExport.cxx
@@ -1284,6 +1284,13 @@ void SchXMLExportHelper_Impl::parseDocument( Reference< chart::XChartDocument > 
                            XML_NAMESPACE_CHART, GetXMLToken(eXMLChartType )) );
            }

            // Handle subtype for of-pie charts
            if (sChartType == u"com.sun.star.chart.BarOfPieDiagram") {
                mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SUB_BAR, OUString::boolean(true));
            } else if (sChartType == u"com.sun.star.chart.PieOfPieDiagram") {
                mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SUB_PIE, OUString::boolean(true));
            }

            //column-mapping or row-mapping
            if( maSequenceMapping.hasElements() )
            {
diff --git a/xmloff/source/chart/SchXMLTools.cxx b/xmloff/source/chart/SchXMLTools.cxx
index 120e361..4287b73 100644
--- a/xmloff/source/chart/SchXMLTools.cxx
+++ b/xmloff/source/chart/SchXMLTools.cxx
@@ -167,6 +167,10 @@ static const tMakeStringStringMap& lcl_getChartTypeNameMap()
         "com.sun.star.chart2.ColumnChartType"},
        {"com.sun.star.chart.PieDiagram",
         "com.sun.star.chart2.PieChartType"},
        {"com.sun.star.chart.BarOfPieDiagram",
         "com.sun.star.chart2.BarOfPieChartType"},
        {"com.sun.star.chart.PieOfPieDiagram",
         "com.sun.star.chart2.PieOfPieChartType"},
        {"com.sun.star.chart.DonutDiagram",
         "com.sun.star.chart2.DonutChartType"},
        {"com.sun.star.chart.XYDiagram",
@@ -304,7 +308,8 @@ XMLTokenEnum getTokenByChartType(
            else if( aServiceName == u"Bar" ||
                     (!bUseOldNames && aServiceName == u"Column"))
                eResult = XML_BAR;
            else if ( aServiceName == u"Pie" )
            else if ( aServiceName == u"Pie" || aServiceName == u"BarOfPie" ||
                    aServiceName == u"PieOfPie" )
                eResult = XML_CIRCLE;
            else if ( aServiceName == u"Donut" )
                eResult = XML_RING;
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index 78ac150..3fb5874 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -1922,7 +1922,10 @@ namespace xmloff::token {
        TOKEN( "styles",                          XML_STYLES ),
        TOKEN( "stylesheet",                      XML_STYLESHEET ),
        TOKEN( "sub-table",                       XML_SUB_TABLE ),
        TOKEN( "sub-bar",                         XML_SUB_BAR ),
        TOKEN( "sub-none",                        XML_SUB_NONE ),
        TOKEN( "subject",                         XML_SUBJECT ),
        TOKEN( "sub-pie",                         XML_SUB_PIE ),
        TOKEN( "subset",                          XML_SUBSET ),
        TOKEN( "subtitle",                        XML_SUBTITLE ),
        TOKEN( "subtotal-field",                  XML_SUBTOTAL_FIELD ),
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index 882ad0e..5045d86 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -1822,7 +1822,10 @@ style-ref
styles
stylesheet
sub-table
sub-bar
sub-none
subject
sub-pie
subset
subtitle
subtotal-field