// xlsxchart.cpp

#include <QtGlobal>
#include <QString>
#include <QIODevice>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QDebug>

#include "xlsxchart_p.h"
#include "xlsxworksheet.h"
#include "xlsxcellrange.h"
#include "xlsxutility_p.h"

QT_BEGIN_NAMESPACE_XLSX

ChartPrivate::ChartPrivate(Chart *q, Chart::CreateFlag flag)
    : AbstractOOXmlFilePrivate(q, flag), chartType(static_cast<Chart::ChartType>(0))
{

}

ChartPrivate::~ChartPrivate()
{
}



/*!
 * \internal
 */
Chart::Chart(AbstractSheet *parent, CreateFlag flag)
    : AbstractOOXmlFile(new ChartPrivate(this, flag))
{
    Q_D(Chart);

    d_func()->sheet = parent;

    // d->legendPos = Chart::ChartAxisPos::None;
    d->legendPos = Chart::None;
    d->legendOverlay = false;
    d->majorGridlinesEnabled = false;
    d->minorGridlinesEnabled = false;
}

/*!
 * Destroys the chart.
 */
Chart::~Chart()
{
}

/*!
 * Add the data series which is in the range \a range of the \a sheet.
 */
void Chart::addSeries(const CellRange &range, AbstractSheet *sheet, bool headerH, bool headerV, bool swapHeaders)
{
    Q_D(Chart);

    if (!range.isValid())
        return;
    if (sheet && sheet->sheetType() != AbstractSheet::ST_WorkSheet)
        return;
    if (!sheet && d->sheet->sheetType() != AbstractSheet::ST_WorkSheet)
        return;

    QString sheetName = sheet ? sheet->sheetName() : d->sheet->sheetName();
    //In case sheetName contains space or '
    sheetName = escapeSheetName(sheetName);

    if (range.columnCount() == 1 || range.rowCount() == 1)
    {
        auto series = std::make_shared<XlsxSeries>();
        series->numberDataSource_numRef = sheetName + QLatin1String("!") + range.toString(true, true);
        d->seriesList.append(series);
    }
    else if ((range.columnCount() < range.rowCount()) || swapHeaders )
    {
        //Column based series
        int firstDataRow = range.firstRow();
        int firstDataColumn = range.firstColumn();

        QString axDataSouruce_numRef;
        if (d->chartType == CT_ScatterChart || d->chartType == CT_BubbleChart)
        {
            firstDataColumn += 1;
            CellRange subRange(range.firstRow(), range.firstColumn(), range.lastRow(), range.firstColumn());
            axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
        }

        if( headerH )
        {
            firstDataRow += 1;
        }
        if( headerV )
        {
            firstDataColumn += 1;
        }

        for (int col=firstDataColumn; col<=range.lastColumn(); ++col)
        {
            CellRange subRange(firstDataRow, col, range.lastRow(), col);
            auto series = std::make_shared<XlsxSeries>();
            series->axDataSource_numRef = axDataSouruce_numRef;
            series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);

            if( headerH )
            {
                CellRange subRange(range.firstRow(), col, range.firstRow(), col);
                series->headerH_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
            }
            else
            {
                series->headerH_numRef = QString();
            }
            if( headerV )
            {
                CellRange subRange(firstDataRow, range.firstColumn(), range.lastRow(), range.firstColumn());
                series->headerV_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
            }
            else
            {
                series->headerV_numRef = QString();
            }
            series->swapHeader = swapHeaders;

            d->seriesList.append(series);
        }

    }
    else
    {
        //Row based series
        int firstDataRow = range.firstRow();
        int firstDataColumn = range.firstColumn();

        QString axDataSouruce_numRef;
        if (d->chartType == CT_ScatterChart || d->chartType == CT_BubbleChart)
        {
            firstDataRow += 1;
            CellRange subRange(range.firstRow(), range.firstColumn(), range.firstRow(), range.lastColumn());
            axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
        }

        if( headerH )
        {
            firstDataRow += 1;
        }
        if( headerV )
        {
            firstDataColumn += 1;
        }

        for (int row=firstDataRow; row<=range.lastRow(); ++row)
        {
            CellRange subRange(row, firstDataColumn, row, range.lastColumn());
            auto series = std::make_shared<XlsxSeries>();
            series->axDataSource_numRef = axDataSouruce_numRef;
            series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);

            if( headerH )
            {
                CellRange subRange(range.firstRow(), firstDataColumn, range.firstRow(), range.lastColumn());
                series->headerH_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
            }
            else
            {
                series->headerH_numRef = QString();
            }

            if( headerV )
            {
                CellRange subRange(row, range.firstColumn(), row, range.firstColumn());
                series->headerV_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
            }
            else
            {
                series->headerV_numRef = QString();
            }
            series->swapHeader = swapHeaders;

            d->seriesList.append(series);
        }
    }
}

/*!
 * Set the type of the chart to \a type
 */
void Chart::setChartType(ChartType type)
{
    Q_D(Chart);

    d->chartType = type;
}

/*!
 * \internal
 *
 */
void Chart::setChartStyle(int id)
{
    Q_UNUSED(id)
    //!Todo
}

void Chart::setAxisTitle(Chart::ChartAxisPos pos, QString axisTitle)
{
    Q_D(Chart);

    if ( axisTitle.isEmpty() )
        return;

    // dev24 : fixed for old compiler
    if ( pos == Chart::Left )
    {
        d->axisNames[ XlsxAxis::Left ] = axisTitle;
    }
    else if ( pos == Chart::Top )
    {
        d->axisNames[ XlsxAxis::Top ] = axisTitle;
    }
    else if ( pos == Chart::Right )
    {
        d->axisNames[ XlsxAxis::Right ] = axisTitle;
    }
    else if ( pos == Chart::Bottom )
    {
        d->axisNames[ XlsxAxis::Bottom ] = axisTitle;
    }

}

// dev25
void Chart::setChartTitle(QString strchartTitle)
{
    Q_D(Chart);

    d->chartTitle = strchartTitle;
}


void Chart::setChartLegend(Chart::ChartAxisPos legendPos, bool overlay)
{
    Q_D(Chart);

    d->legendPos = legendPos;
    d->legendOverlay = overlay;
}


void Chart::setGridlinesEnable(bool majorGridlinesEnable, bool minorGridlinesEnable)
{
    Q_D(Chart);

    d->majorGridlinesEnabled = majorGridlinesEnable;
    d->minorGridlinesEnabled = minorGridlinesEnable;
}


/*!
 * \internal
 */
void Chart::saveToXmlFile(QIODevice *device) const
{
    Q_D(const Chart);

    /*
        <chartSpace>
            <chart>
                <view3D>
                    <perspective val="30"/>
                </view3D>
                <plotArea>
                    <layout/>
                    <barChart>
                    ...
                    </barChart>
                    <catAx/>
                    <valAx/>
                </plotArea>
                <legend>
                ...
                </legend>
            </chart>
            <printSettings>
            </printSettings>
        </chartSpace>
    */

    QXmlStreamWriter writer(device);

    writer.writeStartDocument(QStringLiteral("1.0"), true);

    // L.4.13.2.2 Chart
    //
    //  chartSpace is the root node, which contains an element defining the chart,
    // and an element defining the print settings for the chart.

    writer.writeStartElement(QStringLiteral("c:chartSpace"));

    writer.writeAttribute(QStringLiteral("xmlns:c"),
                          QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/chart"));
    writer.writeAttribute(QStringLiteral("xmlns:a"),
                          QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/main"));
    writer.writeAttribute(QStringLiteral("xmlns:r"),
                          QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));

    /*
    * chart is the root element for the chart. If the chart is a 3D chart,
    * then a view3D element is contained, which specifies the 3D view.
    * It then has a plot area, which defines a layout and contains an element
    * that corresponds to, and defines, the type of chart.
    */

    d->saveXmlChart(writer);

    writer.writeEndElement();// c:chartSpace
    writer.writeEndDocument();
}

/*!
 * \internal
 */
bool Chart::loadFromXmlFile(QIODevice *device)
{
    Q_D(Chart);

    QXmlStreamReader reader(device);
    while (!reader.atEnd())
    {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement)
        {
            if (reader.name() == QLatin1String("chart"))
            {
                if (!d->loadXmlChart(reader))
                {
                    return false;
                }
            }
        }
    }

    return true;
}


bool ChartPrivate::loadXmlChart(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("chart"));

//    qDebug() << "-------------- loadXmlChart";

    while (!reader.atEnd())
    {
        reader.readNextStartElement();

//        qDebug() << "-------------1- " << reader.name();

        if (reader.tokenType() == QXmlStreamReader::StartElement)
        {

            if (reader.name() == QLatin1String("plotArea"))
            {
                if (!loadXmlPlotArea(reader))
                {
                    return false;
                }
            }
            else if (reader.name() == QLatin1String("title"))
            {
                //!Todo

                if ( loadXmlChartTitle(reader) )
                {
                }

            }
//            else if (reader.name() == QLatin1String("legend"))
//            {
//                loadXmlChartLegend(reader);
//                qDebug() << "-------------- loadXmlChartLegend";
//            }
        }
        else if (reader.tokenType() == QXmlStreamReader::EndElement &&
                 reader.name() == QLatin1String("chart") )
        {
            break;
        }
    }
    return true;
}

// TO DEBUG: loop is not work, when i looping second element.
/*
dchrt_CT_PlotArea =
    element layout { dchrt_CT_Layout }?,
    (element areaChart { dchrt_CT_AreaChart }
        | element area3DChart { dchrt_ CT_Area3DChart }
        | element lineChart { dchrt_CT_LineChart }
        | element line3DChart { dchrt_CT_Line3DChart }
        | element stockChart { dchrt_CT_StockChart }
        | element radarChart { dchrt_CT_RadarChart }
        | element scatterChart { dchrt_CT_ScatterChart }
        | element pieChart { dchrt_CT_PieChart }
        | element pie3DChart { dchrt_CT_Pie3DChart }
        | element doughnutChart { dchrt_CT_DoughnutChart }
        | element barChart { dchrt_CT_BarChart }
        | element bar3DChart { dchrt_CT_Bar3DChart }
        | element ofPieChart { dchrt_CT_OfPieChart }
        | element surfaceChart { dchrt_CT_SurfaceChart }
        | element surface3DChart { dchrt_CT_Surface3DChart }
        | element bubbleChart { dchrt_CT_BubbleChart })+,
    (element valAx { dchrt_CT_ValAx }
        | element catAx { dchrt_CT_CatAx }
        | element dateAx { dchrt_CT_DateAx }
        | element serAx { dchrt_CT_SerAx })*,
    element dTable { dchrt_CT_DTable }?,
    element spPr { a_CT_ShapeProperties }?,
    element extLst { dchrt_CT_ExtensionList }?
 */
bool ChartPrivate::loadXmlPlotArea(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("plotArea"));

    // TO DEBUG:

    reader.readNext();

    while (!reader.atEnd())
    {
//        qDebug() << "-------------2- " << reader.name();

        if (reader.isStartElement())
        {
            if (!loadXmlPlotAreaElement(reader))
            {
                qDebug() << "[debug] failed to load plotarea element.";
                return false;
            }
            else if (reader.name() == QLatin1String("legend"))  // Why here?
            {
                loadXmlChartLegend(reader);
//                qDebug() << "-------------- loadXmlChartLegend";
            }

            reader.readNext();
        }
        else
        {
            reader.readNext();
        }
    }

    return true;
}

bool ChartPrivate::loadXmlPlotAreaElement(QXmlStreamReader &reader)
{
    if (reader.name() == QLatin1String("layout"))
    {
        //!ToDo extract attributes
        layout = readSubTree(reader);
    }
    else if (reader.name().endsWith(QLatin1String("Chart")))
    {
        // for pieChart, barChart, ... (choose one)
        if ( !loadXmlXxxChart(reader) )
        {
            qDebug() << "[debug] failed to load chart";
            return false;
        }
    }
    else if (reader.name() == QLatin1String("catAx")) // choose one : catAx, dateAx, serAx, valAx
    {
        // qDebug() << "loadXmlAxisCatAx()";
        loadXmlAxisCatAx(reader);
    }
    else if (reader.name() == QLatin1String("dateAx")) // choose one : catAx, dateAx, serAx, valAx
    {
        // qDebug() << "loadXmlAxisDateAx()";
        loadXmlAxisDateAx(reader);
    }
    else if (reader.name() == QLatin1String("serAx")) // choose one : catAx, dateAx, serAx, valAx
    {
        // qDebug() << "loadXmlAxisSerAx()";
        loadXmlAxisSerAx(reader);
    }
    else if (reader.name() == QLatin1String("valAx")) // choose one : catAx, dateAx, serAx, valAx
    {
        // qDebug() << "loadXmlAxisValAx()";
        loadXmlAxisValAx(reader);
    }
    else if (reader.name() == QLatin1String("dTable"))
    {
        //!ToDo
        // dTable "CT_DTable"
        // reader.skipCurrentElement();
    }
    else if (reader.name() == QLatin1String("spPr"))
    {
        //!ToDo
        // spPr "a:CT_ShapeProperties"
        // reader.skipCurrentElement();
    }
    else if (reader.name() == QLatin1String("extLst"))
    {
        //!ToDo
        // extLst "CT_ExtensionList"
        // reader.skipCurrentElement();
    }

    return true;
}

bool ChartPrivate::loadXmlXxxChart(QXmlStreamReader &reader)
{
    const auto& name = reader.name();

    if (name == QLatin1String("areaChart"))
    {
        chartType = Chart::CT_AreaChart;
    }
    else if (name == QLatin1String("area3DChart"))
    {
        chartType = Chart::CT_Area3DChart;
    }
    else if (name == QLatin1String("lineChart"))
    {
        chartType = Chart::CT_LineChart;
    }
    else if (name == QLatin1String("line3DChart"))
    {
        chartType = Chart::CT_Line3DChart;
    }
    else if (name == QLatin1String("stockChart"))
    {
        chartType = Chart::CT_StockChart;
    }
    else if (name == QLatin1String("radarChart"))
    {
        chartType = Chart::CT_RadarChart;
    }
    else if (name == QLatin1String("scatterChart"))
    {
        chartType = Chart::CT_ScatterChart;
    }
    else if (name == QLatin1String("pieChart"))
    {
        chartType = Chart::CT_PieChart;
    }
    else if (name == QLatin1String("pie3DChart"))
    {
        chartType = Chart::CT_Pie3DChart;
    }
    else if (name == QLatin1String("doughnutChart"))
    {
        chartType = Chart::CT_DoughnutChart;
    }
    else if (name == QLatin1String("barChart"))
    {
        chartType = Chart::CT_BarChart;
    }
    else if (name == QLatin1String("bar3DChart"))
    {
        chartType = Chart::CT_Bar3DChart;
    }
    else if (name == QLatin1String("ofPieChart"))
    {
        chartType = Chart::CT_OfPieChart;
    }
    else if (name == QLatin1String("surfaceChart"))
    {
        chartType = Chart::CT_SurfaceChart;
    }
    else if (name == QLatin1String("surface3DChart"))
    {
        chartType = Chart::CT_Surface3DChart;
    }
    else if (name == QLatin1String("bubbleChart"))
    {
        chartType = Chart::CT_BubbleChart;
    }
    else
    {
        qDebug() << "[undefined chart type] " << name;
        chartType = Chart::CT_NoStatementChart;
        return false;
    }

    while( !reader.atEnd() )
    {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement)
        {
            // dev57

            if ( reader.name() == QLatin1String("ser") )
            {
                loadXmlSer(reader);
            }
            else if (reader.name() == QLatin1String("varyColors"))
            {
            }
            else if (reader.name() == QLatin1String("barDir"))
            {
            }
            else if (reader.name() == QLatin1String("axId"))
            {
                //

            }
            else if (reader.name() == QLatin1String("scatterStyle"))
            {
            }
            else if (reader.name() == QLatin1String("holeSize"))
            {
            }
            else
            {
            }

        }
        else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
                  reader.name() == name )
        {
            break;
        }
    }

    return true;
}

bool ChartPrivate::loadXmlSer(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("ser"));

    auto series = std::make_shared<XlsxSeries>();
    seriesList.append(series);

    while ( !reader.atEnd() &&
            !(reader.tokenType() == QXmlStreamReader::EndElement &&
             reader.name() == QLatin1String("ser")) )
    {
        if (reader.readNextStartElement())
        {
            //TODO beide Header noch auswerten RTR 2019.11
            const auto& name = reader.name();
            if ( name == QLatin1String("tx") )
            {
                while ( !reader.atEnd() &&
                        !(reader.tokenType() == QXmlStreamReader::EndElement &&
                         reader.name() == name))
                {
                    if (reader.readNextStartElement())
                    {
                        if (reader.name() == QLatin1String("strRef"))
                            series->headerV_numRef = loadXmlStrRef(reader);
                    }
                }
            }
            else if ( name == QLatin1String("cat") ||
                 name == QLatin1String("xVal") )
            {
                while ( !reader.atEnd() &&
                        !(reader.tokenType() == QXmlStreamReader::EndElement &&
                         reader.name() == name))
                {
                    if (reader.readNextStartElement())
                    {
                        if (reader.name() == QLatin1String("numRef"))
                            series->axDataSource_numRef = loadXmlNumRef(reader);
                        else
                        if (reader.name() == QLatin1String("strRef"))
                            series->headerH_numRef = loadXmlStrRef(reader);
                    }
                }
            }
            else if (name == QLatin1String("val") || name == QLatin1String("yVal"))
            {
                while ( !reader.atEnd() &&
                        !(reader.tokenType() == QXmlStreamReader::EndElement &&
                        reader.name() == name))
                {
                    if (reader.readNextStartElement())
                    {
                        if (reader.name() == QLatin1String("numRef"))
                            series->numberDataSource_numRef = loadXmlNumRef(reader);
                    }
                }
            }
            else if (name == QLatin1String("extLst"))
            {
                while ( !reader.atEnd() &&
                        !(reader.tokenType() == QXmlStreamReader::EndElement &&
                         reader.name() == name))
                {
                    reader.readNextStartElement();
                }
            }
        }
    }

    return true;
}


QString ChartPrivate::loadXmlNumRef(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("numRef"));

    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                                && reader.name() == QLatin1String("numRef")))
    {
        if (reader.readNextStartElement())
        {
            if (reader.name() == QLatin1String("f"))
                return reader.readElementText();
        }
    }

    return QString();
}


QString ChartPrivate::loadXmlStrRef(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("strRef"));

    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                                && reader.name() == QLatin1String("strRef")))
    {
        if (reader.readNextStartElement())
        {
            if (reader.name() == QLatin1String("f"))
                return reader.readElementText();
        }
    }

    return QString();
}


void ChartPrivate::saveXmlChart(QXmlStreamWriter &writer) const
{
    //----------------------------------------------------
    // c:chart
    writer.writeStartElement(QStringLiteral("c:chart"));

    //----------------------------------------------------
    // c:title

    saveXmlChartTitle(writer); // write 'chart title'

    //----------------------------------------------------
    // c:plotArea

    writer.writeStartElement(QStringLiteral("c:plotArea"));

    // a little workaround for Start- and EndElement with starting ">" and ending without ">"
    writer.device()->write("><c:layout>"); //layout
    writer.device()->write(layout.toUtf8());
    writer.device()->write("</c:layout"); //layout

    // dev35
    switch (chartType)
    {
        case Chart::CT_AreaChart:       saveXmlAreaChart(writer); break;
        case Chart::CT_Area3DChart:     saveXmlAreaChart(writer); break;
        case Chart::CT_LineChart:       saveXmlLineChart(writer); break;
        case Chart::CT_Line3DChart:     saveXmlLineChart(writer); break;
        case Chart::CT_StockChart: break;
        case Chart::CT_RadarChart: break;
        case Chart::CT_ScatterChart:    saveXmlScatterChart(writer); break;
        case Chart::CT_PieChart:        saveXmlPieChart(writer); break;
        case Chart::CT_Pie3DChart:      saveXmlPieChart(writer); break;
        case Chart::CT_DoughnutChart:   saveXmlDoughnutChart(writer); break;
        case Chart::CT_BarChart:        saveXmlBarChart(writer); break;
        case Chart::CT_Bar3DChart:      saveXmlBarChart(writer); break;
        case Chart::CT_OfPieChart: break;
        case Chart::CT_SurfaceChart:  break;
        case Chart::CT_Surface3DChart: break;
        case Chart::CT_BubbleChart:  break;
        default:  break;
    }

    saveXmlAxis(writer); // c:catAx, c:valAx, c:serAx, c:dateAx (choose one)

    //!TODO: write element
    // c:dTable CT_DTable
    // c:spPr   CT_ShapeProperties
    // c:extLst CT_ExtensionList

    writer.writeEndElement(); // c:plotArea

    // c:legend
    saveXmlChartLegend(writer); // c:legend

    writer.writeEndElement(); // c:chart
}

bool ChartPrivate::loadXmlChartTitle(QXmlStreamReader &reader)
{
    //!TODO : load chart title

    Q_ASSERT(reader.name() == QLatin1String("title"));

    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                                && reader.name() == QLatin1String("title")))
    {
        if (reader.readNextStartElement())
        {
            if (reader.name() == QLatin1String("tx")) // c:tx
                return loadXmlChartTitleTx(reader);
        }
    }

    return false;
}

bool ChartPrivate::loadXmlChartTitleTx(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("tx"));

    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                                && reader.name() == QLatin1String("tx")))
    {
        if (reader.readNextStartElement())
        {
            if (reader.name() == QLatin1String("rich")) // c:rich
                return loadXmlChartTitleTxRich(reader);
        }
    }

    return false;
}

bool ChartPrivate::loadXmlChartTitleTxRich(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("rich"));

    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                                && reader.name() == QLatin1String("rich")))
    {
        if (reader.readNextStartElement())
        {
            if (reader.name() == QLatin1String("p")) // a:p
                return loadXmlChartTitleTxRichP(reader);
        }
    }

    return false;
}

bool ChartPrivate::loadXmlChartTitleTxRichP(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("p"));

    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                                && reader.name() == QLatin1String("p")))
    {
        if (reader.readNextStartElement())
        {
            if (reader.name() == QLatin1String("r")) // a:r
                return loadXmlChartTitleTxRichP_R(reader);
        }
    }

    return false;
}

bool ChartPrivate::loadXmlChartTitleTxRichP_R(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("r"));

    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                                && reader.name() == QLatin1String("r")))
    {
        if (reader.readNextStartElement())
        {
            if (reader.name() == QLatin1String("t")) // a:t
            {
                QString textValue = reader.readElementText();
                this->chartTitle = textValue;
                return true;
            }
        }
    }

    return false;
}


// write 'chart title'
void ChartPrivate::saveXmlChartTitle(QXmlStreamWriter &writer) const
{
    if ( chartTitle.isEmpty() )
        return;

    writer.writeStartElement(QStringLiteral("c:title"));
    /*
    <xsd:complexType name="CT_Title">
        <xsd:sequence>
            <xsd:element name="tx"      type="CT_Tx"                minOccurs="0" maxOccurs="1"/>
            <xsd:element name="layout"  type="CT_Layout"            minOccurs="0" maxOccurs="1"/>
            <xsd:element name="overlay" type="CT_Boolean"           minOccurs="0" maxOccurs="1"/>
            <xsd:element name="spPr"    type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="txPr"    type="a:CT_TextBody"        minOccurs="0" maxOccurs="1"/>
            <xsd:element name="extLst"  type="CT_ExtensionList"     minOccurs="0" maxOccurs="1"/>
        </xsd:sequence>
    </xsd:complexType>
    */

        writer.writeStartElement(QStringLiteral("c:tx"));
        /*
        <xsd:complexType name="CT_Tx">
            <xsd:sequence>
                <xsd:choice     minOccurs="1"   maxOccurs="1">
                <xsd:element    name="strRef"   type="CT_StrRef"        minOccurs="1" maxOccurs="1"/>
                <xsd:element    name="rich"     type="a:CT_TextBody"    minOccurs="1" maxOccurs="1"/>
                </xsd:choice>
            </xsd:sequence>
        </xsd:complexType>
        */

            writer.writeStartElement(QStringLiteral("c:rich"));
            /*
            <xsd:complexType name="CT_TextBody">
                <xsd:sequence>
                    <xsd:element name="bodyPr"      type="CT_TextBodyProperties"    minOccurs=" 1"  maxOccurs="1"/>
                    <xsd:element name="lstStyle"    type="CT_TextListStyle"         minOccurs="0"   maxOccurs="1"/>
                    <xsd:element name="p"           type="CT_TextParagraph"         minOccurs="1"   maxOccurs="unbounded"/>
                </xsd:sequence>
            </xsd:complexType>
            */

                writer.writeEmptyElement(QStringLiteral("a:bodyPr")); // <a:bodyPr/>
                /*
                <xsd:complexType name="CT_TextBodyProperties">
                    <xsd:sequence>
                        <xsd:element name="prstTxWarp" type="CT_PresetTextShape" minOccurs="0" maxOccurs="1"/>
                        <xsd:group ref="EG_TextAutofit" minOccurs="0" maxOccurs="1"/>
                        <xsd:element name="scene3d" type="CT_Scene3D" minOccurs="0" maxOccurs="1"/>
                        <xsd:group ref="EG_Text3D" minOccurs="0" maxOccurs="1"/>
                        <xsd:element name="extLst" type="CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
                    </xsd:sequence>
                    <xsd:attribute name="rot" type="ST_Angle" use="optional"/>
                    <xsd:attribute name="spcFirstLastPara" type="xsd:boolean" use="optional"/>
                    <xsd:attribute name="vertOverflow" type="ST_TextVertOverflowType" use="optional"/>
                    <xsd:attribute name="horzOverflow" type="ST_TextHorzOverflowType" use="optional"/>
                    <xsd:attribute name="vert" type="ST_TextVerticalType" use="optional"/>
                    <xsd:attribute name="wrap" type="ST_TextWrappingType" use="optional"/>
                    <xsd:attribute name="lIns" type="ST_Coordinate32" use="optional"/>
                    <xsd:attribute name="tIns" type="ST_Coordinate32" use="optional"/>
                    <xsd:attribute name="rIns" type="ST_Coordinate32" use="optional"/>
                    <xsd:attribute name="bIns" type="ST_Coordinate32" use="optional"/>
                    <xsd:attribute name="numCol" type="ST_TextColumnCount" use="optional"/>
                    <xsd:attribute name="spcCol" type="ST_PositiveCoordinate32" use="optional"/>
                    <xsd:attribute name="rtlCol" type="xsd:boolean" use="optional"/>
                    <xsd:attribute name="fromWordArt" type="xsd:boolean" use="optional"/>
                    <xsd:attribute name="anchor" type="ST_TextAnchoringType" use="optional"/>
                    <xsd:attribute name="anchorCtr" type="xsd:boolean" use="optional"/>
                    <xsd:attribute name="forceAA" type="xsd:boolean" use="optional"/>
                    <xsd:attribute name="upright" type="xsd:boolean" use="optional" default="false"/>
                    <xsd:attribute name="compatLnSpc" type="xsd:boolean" use="optional"/>
                </xsd:complexType>
                 */

                writer.writeEmptyElement(QStringLiteral("a:lstStyle")); // <a:lstStyle/>

                writer.writeStartElement(QStringLiteral("a:p"));
                /*
                <xsd:complexType name="CT_TextParagraph">
                    <xsd:sequence>
                        <xsd:element    name="pPr"          type="CT_TextParagraphProperties" minOccurs="0" maxOccurs="1"/>
                        <xsd:group      ref="EG_TextRun"    minOccurs="0" maxOccurs="unbounded"/>
                        <xsd:element    name="endParaRPr"   type="CT_TextCharacterProperties" minOccurs="0"
                        maxOccurs="1"/>
                    </xsd:sequence>
                </xsd:complexType>
                 */

                    // <a:pPr lvl="0">
                    writer.writeStartElement(QStringLiteral("a:pPr"));

                        writer.writeAttribute(QStringLiteral("lvl"), QStringLiteral("0"));

                        // <a:defRPr b="0"/>
                        writer.writeStartElement(QStringLiteral("a:defRPr"));

                            writer.writeAttribute(QStringLiteral("b"), QStringLiteral("0"));

                        writer.writeEndElement();  // a:defRPr

                    writer.writeEndElement();  // a:pPr

                /*
                <xsd:group name="EG_TextRun">
                    <xsd:choice>
                        <xsd:element name="r"   type="CT_RegularTextRun"/>
                        <xsd:element name="br"  type="CT_TextLineBreak"/>
                        <xsd:element name="fld" type="CT_TextField"/>
                    </xsd:choice>
                </xsd:group>
                */

                writer.writeStartElement(QStringLiteral("a:r"));
                /*
                <xsd:complexType name="CT_RegularTextRun">
                    <xsd:sequence>
                        <xsd:element name="rPr" type="CT_TextCharacterProperties" minOccurs="0" maxOccurs="1"/>
                        <xsd:element name="t"   type="xsd:string" minOccurs="1" maxOccurs="1"/>
                    </xsd:sequence>
                </xsd:complexType>
                 */

                    // <a:t>chart name</a:t>
                    writer.writeTextElement(QStringLiteral("a:t"), chartTitle);

                writer.writeEndElement();  // a:r

                writer.writeEndElement();  // a:p

            writer.writeEndElement();  // c:rich

        writer.writeEndElement();  // c:tx

        // <c:overlay val="0"/>
        writer.writeStartElement(QStringLiteral("c:overlay"));
            writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0"));
        writer.writeEndElement();  // c:overlay

    writer.writeEndElement();  // c:title
}
// }}


// write 'chart legend'
void ChartPrivate::saveXmlChartLegend(QXmlStreamWriter &writer) const
{
    if ( legendPos == Chart::None )
        return;

//    <c:legend>
//    <c:legendPos val="r"/>
//    <c:overlay val="0"/>
//    </c:legend>

    writer.writeStartElement(QStringLiteral("c:legend"));

        writer.writeStartElement(QStringLiteral("c:legendPos"));

            QString pos;
            switch( legendPos )
            {
                //case Chart::ChartAxisPos::Right:
                case Chart::Right :
                    pos = QStringLiteral("r");
                    break;

                // case Chart::ChartAxisPos::Left:
                case Chart::Left :
                    pos = QStringLiteral("l");
                    break;

                // case Chart::ChartAxisPos::Top:
                case Chart::Top :
                    pos = QStringLiteral("t");
                    break;

                // case Chart::ChartAxisPos::Bottom:
                case Chart::Bottom :
                    pos = QStringLiteral("b");
                    break;

                default:
                    pos = QStringLiteral("r");
                    break;
            }

            writer.writeAttribute(QStringLiteral("val"), pos);

        writer.writeEndElement();  // c:legendPos

        writer.writeStartElement(QStringLiteral("c:overlay"));

            if( legendOverlay )
            {
                writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));
            }
            else
            {
                writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0"));
            }

        writer.writeEndElement();  // c:overlay

    writer.writeEndElement(); // c:legend
}


void ChartPrivate::saveXmlPieChart(QXmlStreamWriter &writer) const
{
    QString name = chartType == Chart::CT_PieChart ? QStringLiteral("c:pieChart") : QStringLiteral("c:pie3DChart");

    writer.writeStartElement(name);

    //Do the same behavior as Excel, Pie prefer varyColors
    writer.writeEmptyElement(QStringLiteral("c:varyColors"));
    writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));

    for (int i=0; i<seriesList.size(); ++i)
        saveXmlSer(writer, seriesList[i].get(), i);

    writer.writeEndElement(); //pieChart, pie3DChart
}

void ChartPrivate::saveXmlBarChart(QXmlStreamWriter &writer) const
{
    QString name = chartType == Chart::CT_BarChart ? QStringLiteral("c:barChart") : QStringLiteral("c:bar3DChart");

    writer.writeStartElement(name);

    writer.writeEmptyElement(QStringLiteral("c:barDir"));
    writer.writeAttribute(QStringLiteral("val"), QStringLiteral("col"));

    for ( int i = 0 ; i < seriesList.size() ; ++i )
    {
        saveXmlSer(writer, seriesList[i].get(), i);
    }

    if ( axisList.isEmpty() )
    {
        const_cast<ChartPrivate*>(this)->axisList.append(
                    std::make_shared<XlsxAxis>( XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1, axisNames[XlsxAxis::Bottom] ));

        const_cast<ChartPrivate*>(this)->axisList.append(
                    std::make_shared<XlsxAxis>( XlsxAxis::T_Val, XlsxAxis::Left, 1, 0, axisNames[XlsxAxis::Left] ));
    }


    // Note: Bar3D have 2~3 axes
    // int axisListSize = axisList.size();
    // [dev62]
    // Q_ASSERT( axisListSize == 2 ||
    //          ( axisListSize == 3 && chartType == Chart::CT_Bar3DChart ) );

    for ( int i = 0 ; i < axisList.size() ; ++i )
    {
        writer.writeEmptyElement(QStringLiteral("c:axId"));
        writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
    }

    writer.writeEndElement(); //barChart, bar3DChart
}

void ChartPrivate::saveXmlLineChart(QXmlStreamWriter &writer) const
{
    QString name = chartType==Chart::CT_LineChart ? QStringLiteral("c:lineChart") : QStringLiteral("c:line3DChart");

    writer.writeStartElement(name);

    // writer.writeEmptyElement(QStringLiteral("grouping")); // dev22

    for (int i=0; i<seriesList.size(); ++i)
        saveXmlSer(writer, seriesList[i].get(), i);

    if (axisList.isEmpty())
    {
        const_cast<ChartPrivate*>(this)->axisList.append(std::make_shared<XlsxAxis>(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1, axisNames[XlsxAxis::Bottom] ));
        const_cast<ChartPrivate*>(this)->axisList.append(std::make_shared<XlsxAxis>(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0, axisNames[XlsxAxis::Left] ));
        if (chartType==Chart::CT_Line3DChart)
            const_cast<ChartPrivate*>(this)->axisList.append(std::make_shared<XlsxAxis>(XlsxAxis::T_Ser, XlsxAxis::Bottom, 2, 0));
    }

    Q_ASSERT((axisList.size()==2||chartType==Chart::CT_LineChart)|| (axisList.size()==3 && chartType==Chart::CT_Line3DChart));

    for (int i=0; i<axisList.size(); ++i)
    {
        writer.writeEmptyElement(QStringLiteral("c:axId"));
        writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
    }

    writer.writeEndElement(); //lineChart, line3DChart
}

void ChartPrivate::saveXmlScatterChart(QXmlStreamWriter &writer) const
{
    const QString name = QStringLiteral("c:scatterChart");

    writer.writeStartElement(name);

    writer.writeEmptyElement(QStringLiteral("c:scatterStyle"));

    for (int i=0; i<seriesList.size(); ++i)
        saveXmlSer(writer, seriesList[i].get(), i);

    if (axisList.isEmpty())
    {
        const_cast<ChartPrivate*>(this)->axisList.append(
                    std::make_shared<XlsxAxis>(XlsxAxis::T_Val, XlsxAxis::Bottom, 0, 1, axisNames[XlsxAxis::Bottom] ));
        const_cast<ChartPrivate*>(this)->axisList.append(
                    std::make_shared<XlsxAxis>(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0, axisNames[XlsxAxis::Left] ));
    }

    int axisListSize = axisList.size();
    Q_ASSERT(axisListSize == 2);

    for (int i=0; i<axisList.size(); ++i)
    {
        writer.writeEmptyElement(QStringLiteral("c:axId"));
        writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
    }

    writer.writeEndElement(); //c:scatterChart
}

void ChartPrivate::saveXmlAreaChart(QXmlStreamWriter &writer) const
{
    QString name = chartType==Chart::CT_AreaChart ? QStringLiteral("c:areaChart") : QStringLiteral("c:area3DChart");

    writer.writeStartElement(name);

    // writer.writeEmptyElement(QStringLiteral("grouping")); // dev22

    for (int i=0; i<seriesList.size(); ++i)
        saveXmlSer(writer, seriesList[i].get(), i);

    if (axisList.isEmpty())
    {
        const_cast<ChartPrivate*>(this)->axisList.append(std::make_shared<XlsxAxis>(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1));
        const_cast<ChartPrivate*>(this)->axisList.append(std::make_shared<XlsxAxis>(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0));
    }

    //Note: Area3D have 2~3 axes
    Q_ASSERT(axisList.size()==2 || (axisList.size()==3 && chartType==Chart::CT_Area3DChart));

    for (int i=0; i<axisList.size(); ++i)
    {
        writer.writeEmptyElement(QStringLiteral("c:axId"));
        writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
    }

    writer.writeEndElement(); //lineChart, line3DChart
}

void ChartPrivate::saveXmlDoughnutChart(QXmlStreamWriter &writer) const
{
    QString name = QStringLiteral("c:doughnutChart");

    writer.writeStartElement(name);

    writer.writeEmptyElement(QStringLiteral("c:varyColors"));
    writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));

    for (int i=0; i<seriesList.size(); ++i)
        saveXmlSer(writer, seriesList[i].get(), i);

    writer.writeStartElement(QStringLiteral("c:holeSize"));
    writer.writeAttribute(QStringLiteral("val"), QString::number(50));

    writer.writeEndElement();
}

void ChartPrivate::saveXmlSer(QXmlStreamWriter &writer, XlsxSeries *ser, int id) const
{

    writer.writeStartElement(QStringLiteral("c:ser"));
    writer.writeEmptyElement(QStringLiteral("c:idx"));
    writer.writeAttribute(QStringLiteral("val"), QString::number(id));
    writer.writeEmptyElement(QStringLiteral("c:order"));
    writer.writeAttribute(QStringLiteral("val"), QString::number(id));

    QString header1;
    QString header2;
    if( ser->swapHeader )
    {
        header1 = ser->headerH_numRef;
        header2 = ser->headerV_numRef;
    }
    else
    {
        header1 = ser->headerV_numRef;
        header2 = ser->headerH_numRef;
    }

    if( !header1.isEmpty() )
    {
        writer.writeStartElement(QStringLiteral("c:tx"));
        writer.writeStartElement(QStringLiteral("c:strRef"));
        writer.writeTextElement(QStringLiteral("c:f"), header1);
        writer.writeEndElement();
        writer.writeEndElement();
    }
    if( !header2.isEmpty() )
    {
        writer.writeStartElement(QStringLiteral("c:cat"));
        writer.writeStartElement(QStringLiteral("c:strRef"));
        writer.writeTextElement(QStringLiteral("c:f"), header2);
        writer.writeEndElement();
        writer.writeEndElement();
    }

#if 0
    if (!ser->axDataSource_numRef.isEmpty())
    {
        if (chartType == Chart::CT_ScatterChart || chartType == Chart::CT_BubbleChart)
        {
            writer.writeStartElement(QStringLiteral("c:xVal"));
        }
        else
        {
            writer.writeStartElement(QStringLiteral("c:cat"));
        }

        writer.writeStartElement(QStringLiteral("c:numRef"));
        writer.writeTextElement(QStringLiteral("c:f"), ser->axDataSource_numRef);
        writer.writeEndElement();//c:numRef
        writer.writeEndElement();//c:cat or c:xVal
    }
#endif

    if (!ser->numberDataSource_numRef.isEmpty())
    {
        if (chartType == Chart::CT_ScatterChart || chartType == Chart::CT_BubbleChart)
            writer.writeStartElement(QStringLiteral("c:yVal"));
        else
            writer.writeStartElement(QStringLiteral("c:val"));
        writer.writeStartElement(QStringLiteral("c:numRef"));
        writer.writeTextElement(QStringLiteral("c:f"), ser->numberDataSource_numRef);
        writer.writeEndElement();//c:numRef
        writer.writeEndElement();//c:val or c:yVal
    }

    writer.writeEndElement();//c:ser
}

bool ChartPrivate::loadXmlAxisCatAx(QXmlStreamReader &reader)
{

    auto axis = std::make_shared<XlsxAxis>();
    axis->type = XlsxAxis::T_Cat;
    axisList.append(axis);

    // load EG_AxShared
    if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) )
    {
        qDebug() << "failed to load EG_AxShared";
        return false;
    }

    //!TODO: load element
    // auto
    // lblAlgn
    // lblOffset
    // tickLblSkip
    // tickMarkSkip
    // noMultiLvlLbl
    // extLst

    return true;
}

bool ChartPrivate::loadXmlAxisDateAx(QXmlStreamReader &reader)
{

    auto axis = std::make_shared<XlsxAxis>();
    axis->type = XlsxAxis::T_Date;
    axisList.append(axis);

    // load EG_AxShared
    if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) )
    {
        qDebug() << "failed to load EG_AxShared";
        return false;
    }

    //!TODO: load element
    // auto
    // lblOffset
    // baseTimeUnit
    // majorUnit
    // majorTimeUnit
    // minorUnit
    // minorTimeUnit
    // extLst

    return true;
}

bool ChartPrivate::loadXmlAxisSerAx(QXmlStreamReader &reader)
{

    auto axis = std::make_shared<XlsxAxis>();
    axis->type = XlsxAxis::T_Ser;
    axisList.append(axis);

    // load EG_AxShared
    if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) )
    {
        qDebug() << "failed to load EG_AxShared";
        return false;
    }

    //!TODO: load element
    // tickLblSkip
    // tickMarkSkip
    // extLst

    return true;
}

bool ChartPrivate::loadXmlAxisValAx(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("valAx"));

    auto axis = std::make_shared<XlsxAxis>();
    axis->type = XlsxAxis::T_Val;
    axisList.append(axis);

    if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) )
    {
        qDebug() << "failed to load EG_AxShared";
        return false;
    }

    //!TODO: load element
    // crossBetween
    // majorUnit
    // minorUnit
    // dispUnits
    // extLst

    return true;
}

/*
<xsd:group name="EG_AxShared">
    <xsd:sequence>
        <xsd:element name="axId" type="CT_UnsignedInt" minOccurs="1" maxOccurs="1"/> (*)(M)
        <xsd:element name="scaling" type="CT_Scaling" minOccurs="1" maxOccurs="1"/> (*)(M)
        <xsd:element name="delete" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="axPos" type="CT_AxPos" minOccurs="1" maxOccurs="1"/> (*)(M)
        <xsd:element name="majorGridlines" type="CT_ChartLines" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="minorGridlines" type="CT_ChartLines" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="title" type="CT_Title" minOccurs="0" maxOccurs="1"/> (*)
        <xsd:element name="numFmt" type="CT_NumFmt" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="majorTickMark" type="CT_TickMark" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="minorTickMark" type="CT_TickMark" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="tickLblPos" type="CT_TickLblPos" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="txPr" type="a:CT_TextBody" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="crossAx" type="CT_UnsignedInt" minOccurs="1" maxOccurs="1"/> (*)(M)
        <xsd:choice minOccurs="0" maxOccurs="1">
            <xsd:element name="crosses" type="CT_Crosses" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="crossesAt" type="CT_Double" minOccurs="1" maxOccurs="1"/>
        </xsd:choice>
    </xsd:sequence>
</xsd:group>
*/
bool ChartPrivate::loadXmlAxisEG_AxShared(QXmlStreamReader &reader, XlsxAxis* axis)
{
    Q_ASSERT( NULL != axis );
    Q_ASSERT( reader.name().endsWith(QLatin1String("Ax")) );
    QString name = reader.name().toString(); //

    while ( !reader.atEnd() )
    {
        reader.readNextStartElement();
        if ( reader.tokenType() == QXmlStreamReader::StartElement )
        {
            // qDebug() << "[debug]" << QTime::currentTime() << reader.name().toString();

            if ( reader.name() == QLatin1String("axId") ) // mandatory element
            {
                // dev57
                uint axId = reader.attributes().value(QStringLiteral("val")).toUInt(); // for Qt5.1
                axis->axisId = axId;
            }
            else if ( reader.name() == QLatin1String("scaling") )
            {
                // mandatory element

                loadXmlAxisEG_AxShared_Scaling(reader, axis);
            }
            else if ( reader.name() == QLatin1String("delete") )
            {
                //!TODO
            }
            else if ( reader.name() == QLatin1String("axPos") )
            {
                // mandatory element

                QString axPosVal = reader.attributes().value(QLatin1String("val")).toString();

                if ( axPosVal == QLatin1String("l") ) { axis->axisPos = XlsxAxis::Left; }
                else if ( axPosVal == QLatin1String("r") ) { axis->axisPos = XlsxAxis::Right; }
                else if ( axPosVal == QLatin1String("t") ) { axis->axisPos = XlsxAxis::Top; }
                else if ( axPosVal == QLatin1String("b") ) { axis->axisPos = XlsxAxis::Bottom; }
            }
            else if ( reader.name() == QLatin1String("majorGridlines") )
            {
                //!TODO anything else?
                majorGridlinesEnabled = true;
            }
            else if ( reader.name() == QLatin1String("minorGridlines") )
            {
                //!TODO anything else?
                minorGridlinesEnabled = true;
            }
            else if ( reader.name() == QLatin1String("title") )
            {
                // title
                if ( !loadXmlAxisEG_AxShared_Title(reader, axis) )
                {
                    qDebug() << "failed to load EG_AxShared title.";
                    Q_ASSERT(false);
                    return false;
                }
            }
            else if ( reader.name() == QLatin1String("numFmt") )
            {
                //!TODO
            }
            else if ( reader.name() == QLatin1String("majorTickMark") )
            {
                //!TODO
            }
            else if ( reader.name() == QLatin1String("minorTickMark") )
            {
                //!TODO
            }
            else if ( reader.name() == QLatin1String("tickLblPos") )
            {
                //!TODO
            }
            else if ( reader.name() == QLatin1String("spPr") )
            {
                //!TODO
            }
            else if ( reader.name() == QLatin1String("txPr") )
            {
                //!TODO
            }
            else if ( reader.name() == QLatin1String("crossAx") ) // mandatory element
            {
                // dev57
                uint crossAx = reader.attributes().value(QLatin1String("val")).toUInt(); // for Qt5.1
                axis->crossAx = crossAx;
            }
            else if ( reader.name() == QLatin1String("crosses") )
            {
                //!TODO
            }
            else if ( reader.name() == QLatin1String("crossesAt") )
            {
                //!TODO
            }

            // reader.readNext();
        }
        else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
                  reader.name().toString() == name )
        {
            break;
        }
    }

    return true;
}

bool ChartPrivate::loadXmlAxisEG_AxShared_Scaling(QXmlStreamReader &reader, XlsxAxis* axis)
{
    Q_UNUSED(axis);
    Q_ASSERT(reader.name() == QLatin1String("scaling"));

    while ( !reader.atEnd() )
    {
        reader.readNextStartElement();
        if ( reader.tokenType() == QXmlStreamReader::StartElement )
        {
            if ( reader.name() == QLatin1String("orientation") )
            {
            }
            else
            {
            }
        }
        else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
                  reader.name() == QLatin1String("scaling") )
        {
            break;
        }
    }

    return true;
}

/*
  <xsd:complexType name="CT_Title">
      <xsd:sequence>
          <xsd:element name="tx" type="CT_Tx" minOccurs="0" maxOccurs="1"/>
          <xsd:element name="layout" type="CT_Layout" minOccurs="0" maxOccurs="1"/>
          <xsd:element name="overlay" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
          <xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
          <xsd:element name="txPr" type="a:CT_TextBody" minOccurs="0" maxOccurs="1"/>
          <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
      </xsd:sequence>
  </xsd:complexType>

<xsd:complexType name="CT_Tx">
    <xsd:sequence>
        <xsd:choice minOccurs="1" maxOccurs="1">
            <xsd:element name="strRef" type="CT_StrRef" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="rich" type="a:CT_TextBody" minOccurs="1" maxOccurs="1"/>
        </xsd:choice>
    </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="CT_StrRef">
    <xsd:sequence>
        <xsd:element name="f" type="xsd:string" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="strCache" type="CT_StrData" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="CT_TextBody">
    <xsd:sequence>
        <xsd:element name="bodyPr" type="CT_TextBodyProperties" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="lstStyle" type="CT_TextListStyle" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="p" type="CT_TextParagraph" minOccurs="1" maxOccurs="unbounded"/>
    </xsd:sequence>
</xsd:complexType>
  */
bool ChartPrivate::loadXmlAxisEG_AxShared_Title(QXmlStreamReader &reader, XlsxAxis* axis)
{
    Q_ASSERT(reader.name() == QLatin1String("title"));

    while ( !reader.atEnd() )
    {
        reader.readNextStartElement();
        if ( reader.tokenType() == QXmlStreamReader::StartElement )
        {
            if ( reader.name() == QLatin1String("tx") )
            {
                loadXmlAxisEG_AxShared_Title_Tx(reader, axis);
            }
            else if ( reader.name() == QLatin1String("overlay") )
            {
                //!TODO: load overlay
                loadXmlAxisEG_AxShared_Title_Overlay(reader, axis);
            }
            else
            {
            }
        }
        else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
                  reader.name() == QLatin1String("title") )
        {
            break;
        }
    }

    return true;
}

bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Overlay(QXmlStreamReader &reader, XlsxAxis* axis)
{
    Q_UNUSED(axis);
    Q_ASSERT(reader.name() == QLatin1String("overlay"));

    while ( !reader.atEnd() )
    {
        reader.readNextStartElement();
        if ( reader.tokenType() == QXmlStreamReader::StartElement )
        {
        }
        else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
                  reader.name() == QLatin1String("overlay") )
        {
            break;
        }
    }

    return true;
}

bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx(QXmlStreamReader &reader, XlsxAxis* axis)
{
    Q_ASSERT(reader.name() == QLatin1String("tx"));

    while ( !reader.atEnd() )
    {
        reader.readNextStartElement();
        if ( reader.tokenType() == QXmlStreamReader::StartElement )
        {
            if ( reader.name() == QLatin1String("rich") )
            {
                loadXmlAxisEG_AxShared_Title_Tx_Rich(reader, axis);
            }
            else
            {
            }
        }
        else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
                  reader.name() == QLatin1String("tx") )
        {
            break;
        }
    }

    return true;
}

bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich(QXmlStreamReader &reader, XlsxAxis* axis)
{
    Q_ASSERT(reader.name() == QLatin1String("rich"));

    while ( !reader.atEnd() )
    {
        reader.readNextStartElement();
        if ( reader.tokenType() == QXmlStreamReader::StartElement )
        {
            if ( reader.name() == QLatin1String("p") )
            {
                loadXmlAxisEG_AxShared_Title_Tx_Rich_P(reader, axis);
            }
            else
            {
            }
        }
        else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
                  reader.name() == QLatin1String("rich") )
        {
            break;
        }
    }

    return true;
}

bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich_P(QXmlStreamReader &reader, XlsxAxis* axis)
{
    Q_ASSERT(reader.name() == QLatin1String("p"));

    while ( !reader.atEnd() )
    {
        reader.readNextStartElement();
        if ( reader.tokenType() == QXmlStreamReader::StartElement )
        {
            if ( reader.name() == QLatin1String("r") )
            {
                loadXmlAxisEG_AxShared_Title_Tx_Rich_P_R(reader, axis);
            }
            else if ( reader.name() == QLatin1String("pPr") )
            {
                loadXmlAxisEG_AxShared_Title_Tx_Rich_P_pPr(reader, axis);
            }
            else
            {

            }
        }
        else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
                  reader.name() == QLatin1String("p") )
        {
            break;
        }
    }

    return true;
}

bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich_P_pPr(QXmlStreamReader &reader, XlsxAxis* axis)
{
    Q_UNUSED(axis);
    Q_ASSERT(reader.name() == QLatin1String("pPr"));

    while ( !reader.atEnd() )
    {
        reader.readNextStartElement();
        if ( reader.tokenType() == QXmlStreamReader::StartElement )
        {
            if ( reader.name() == QLatin1String("defRPr") )
            {
                reader.readElementText();
            }
            else
            {
            }
        }
        else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
                  reader.name() == QLatin1String("pPr") )
        {
            break;
        }
    }

    return true;
}

bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich_P_R(QXmlStreamReader &reader, XlsxAxis* axis)
{
    Q_ASSERT(reader.name() == QLatin1String("r"));

    while ( !reader.atEnd() )
    {
        reader.readNextStartElement();
        if ( reader.tokenType() == QXmlStreamReader::StartElement )
        {
            if ( reader.name() == QLatin1String("t") )
            {
                QString strAxisName = reader.readElementText();
                XlsxAxis::AxisPos axisPos = axis->axisPos;
                axis->axisNames[ axisPos ] = strAxisName;
            }
            else
            {
            }
        }
        else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
                  reader.name() == QLatin1String("r") )
        {
            break;
        }
    }

    return true;
}

/*
<xsd:complexType name="CT_PlotArea">
    <xsd:sequence>
        <xsd:element name="layout" type="CT_Layout" minOccurs="0" maxOccurs="1"/>
        <xsd:choice minOccurs="1" maxOccurs="unbounded">
            <xsd:element name="areaChart" type="CT_AreaChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="area3DChart" type="CT_Area3DChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="lineChart" type="CT_LineChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="line3DChart" type="CT_Line3DChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="stockChart" type="CT_StockChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="radarChart" type="CT_RadarChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="scatterChart" type="CT_ScatterChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="pieChart" type="CT_PieChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="pie3DChart" type="CT_Pie3DChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="doughnutChart" type="CT_DoughnutChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="barChart" type="CT_BarChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="bar3DChart" type="CT_Bar3DChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="ofPieChart" type="CT_OfPieChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="surfaceChart" type="CT_SurfaceChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="surface3DChart" type="CT_Surface3DChart" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="bubbleChart" type="CT_BubbleChart" minOccurs="1" maxOccurs="1"/>
        </xsd:choice>
        <xsd:choice minOccurs="0" maxOccurs="unbounded">
            <xsd:element name="valAx" type="CT_ValAx" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="catAx" type="CT_CatAx" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="dateAx" type="CT_DateAx" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="serAx" type="CT_SerAx" minOccurs="1" maxOccurs="1"/>
        </xsd:choice>
        <xsd:element name="dTable" type="CT_DTable" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>
*/

/*
<xsd:complexType name="CT_CatAx">
    <xsd:sequence>
        <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="auto" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="lblAlgn" type="CT_LblAlgn" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="lblOffset" type="CT_LblOffset" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="tickLblSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="tickMarkSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="noMultiLvlLbl" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>
<!----------------------------------------------------------------------------->
<xsd:complexType name="CT_DateAx">
    <xsd:sequence>
        <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="auto" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="lblOffset" type="CT_LblOffset" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="baseTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="majorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="majorTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="minorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="minorTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>
<!----------------------------------------------------------------------------->
<xsd:complexType name="CT_SerAx">
    <xsd:sequence>
    <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="tickLblSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="tickMarkSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>
<!----------------------------------------------------------------------------->
<xsd:complexType name="CT_ValAx">
    <xsd:sequence>
        <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="crossBetween" type="CT_CrossBetween" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="majorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="minorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="dispUnits" type="CT_DispUnits" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>
*/

void ChartPrivate::saveXmlAxis(QXmlStreamWriter &writer) const
{
    for ( int i = 0 ; i < axisList.size() ; ++i )
    {
        XlsxAxis* axis = axisList[i].get();
        if ( nullptr == axis )
            continue;

        if ( axis->type == XlsxAxis::T_Cat  ) { saveXmlAxisCatAx( writer, axis ); }
        if ( axis->type == XlsxAxis::T_Val  ) { saveXmlAxisValAx( writer, axis ); }
        if ( axis->type == XlsxAxis::T_Ser  ) { saveXmlAxisSerAx( writer, axis ); }
        if ( axis->type == XlsxAxis::T_Date ) { saveXmlAxisDateAx( writer, axis ); }
    }

}

void ChartPrivate::saveXmlAxisCatAx(QXmlStreamWriter &writer, XlsxAxis* axis) const
{
/*
<xsd:complexType name="CT_CatAx">
    <xsd:sequence>
        <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="auto" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="lblAlgn" type="CT_LblAlgn" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="lblOffset" type="CT_LblOffset" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="tickLblSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="tickMarkSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="noMultiLvlLbl" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>
*/

    writer.writeStartElement(QStringLiteral("c:catAx"));

    saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared

    //!TODO: write element
    // auto
    // lblAlgn
    // lblOffset
    // tickLblSkip
    // tickMarkSkip
    // noMultiLvlLbl
    // extLst

    writer.writeEndElement(); // c:catAx
}

void ChartPrivate::saveXmlAxisDateAx(QXmlStreamWriter &writer, XlsxAxis* axis) const
{
/*
<xsd:complexType name="CT_DateAx">
    <xsd:sequence>
        <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="auto" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="lblOffset" type="CT_LblOffset" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="baseTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="majorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="majorTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="minorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="minorTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>
*/

    writer.writeStartElement(QStringLiteral("c:dateAx"));

    saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared

    //!TODO: write element
    // auto
    // lblOffset
    // baseTimeUnit
    // majorUnit
    // majorTimeUnit
    // minorUnit
    // minorTimeUnit
    // extLst

    writer.writeEndElement(); // c:dateAx
}

void ChartPrivate::saveXmlAxisSerAx(QXmlStreamWriter &writer, XlsxAxis* axis) const
{
/*
<xsd:complexType name="CT_SerAx">
    <xsd:sequence>
    <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="tickLblSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="tickMarkSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>
*/

    writer.writeStartElement(QStringLiteral("c:serAx"));

    saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared

    //!TODO: write element
    // tickLblSkip
    // tickMarkSkip
    // extLst

    writer.writeEndElement(); // c:serAx
}

void ChartPrivate::saveXmlAxisValAx(QXmlStreamWriter &writer, XlsxAxis* axis) const
{
/*
<xsd:complexType name="CT_ValAx">
    <xsd:sequence>
        <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
        <xsd:element name="crossBetween" type="CT_CrossBetween" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="majorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="minorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="dispUnits" type="CT_DispUnits" minOccurs="0" maxOccurs="1"/>
        <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
    </xsd:sequence>
</xsd:complexType>
*/

    writer.writeStartElement(QStringLiteral("c:valAx"));

    saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared

    //!TODO: write element
    // crossBetween
    // majorUnit
    // minorUnit
    // dispUnits
    // extLst

    writer.writeEndElement(); // c:valAx
}

void ChartPrivate::saveXmlAxisEG_AxShared(QXmlStreamWriter &writer, XlsxAxis* axis) const
{
    /*
    <xsd:group name="EG_AxShared">
        <xsd:sequence>
            <xsd:element name="axId" type="CT_UnsignedInt" minOccurs="1" maxOccurs="1"/> (*)
            <xsd:element name="scaling" type="CT_Scaling" minOccurs="1" maxOccurs="1"/> (*)
            <xsd:element name="delete" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="axPos" type="CT_AxPos" minOccurs="1" maxOccurs="1"/> (*)
            <xsd:element name="majorGridlines" type="CT_ChartLines" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="minorGridlines" type="CT_ChartLines" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="title" type="CT_Title" minOccurs="0" maxOccurs="1"/> (***********************)
            <xsd:element name="numFmt" type="CT_NumFmt" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="majorTickMark" type="CT_TickMark" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="minorTickMark" type="CT_TickMark" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="tickLblPos" type="CT_TickLblPos" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="txPr" type="a:CT_TextBody" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="crossAx" type="CT_UnsignedInt" minOccurs="1" maxOccurs="1"/> (*)
            <xsd:choice minOccurs="0" maxOccurs="1">
                <xsd:element name="crosses" type="CT_Crosses" minOccurs="1" maxOccurs="1"/>
                <xsd:element name="crossesAt" type="CT_Double" minOccurs="1" maxOccurs="1"/>
            </xsd:choice>
        </xsd:sequence>
    </xsd:group>
    */

    writer.writeEmptyElement(QStringLiteral("c:axId")); // 21.2.2.9. axId (Axis ID) (mandatory value)
        writer.writeAttribute(QStringLiteral("val"), QString::number(axis->axisId));

    writer.writeStartElement(QStringLiteral("c:scaling")); // CT_Scaling (mandatory value)
        writer.writeEmptyElement(QStringLiteral("c:orientation")); // CT_Orientation
            writer.writeAttribute(QStringLiteral("val"), QStringLiteral("minMax")); // ST_Orientation
    writer.writeEndElement(); // c:scaling

    writer.writeEmptyElement(QStringLiteral("c:axPos")); // axPos CT_AxPos (mandatory value)
        QString pos = GetAxisPosString( axis->axisPos );
        if ( !pos.isEmpty() )
        {
            writer.writeAttribute(QStringLiteral("val"), pos); // ST_AxPos
        }

    if( majorGridlinesEnabled )
    {
        writer.writeEmptyElement(QStringLiteral("c:majorGridlines"));
    }
    if( minorGridlinesEnabled )
    {
        writer.writeEmptyElement(QStringLiteral("c:minorGridlines"));
    }

    saveXmlAxisEG_AxShared_Title(writer, axis); // "c:title" CT_Title

    writer.writeEmptyElement(QStringLiteral("c:crossAx")); // crossAx (mandatory value)
        writer.writeAttribute(QStringLiteral("val"), QString::number(axis->crossAx));

}

void ChartPrivate::saveXmlAxisEG_AxShared_Title(QXmlStreamWriter &writer, XlsxAxis* axis) const
{
    // CT_Title

    /*
    <xsd:complexType name="CT_Title">
        <xsd:sequence>
            <xsd:element name="tx" type="CT_Tx" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="layout" type="CT_Layout" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="overlay" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="txPr" type="a:CT_TextBody" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
        </xsd:sequence>
    </xsd:complexType>
    */
    /*
    <xsd:complexType name="CT_Tx">
        <xsd:sequence>
            <xsd:choice minOccurs="1" maxOccurs="1">
                <xsd:element name="strRef" type="CT_StrRef" minOccurs="1" maxOccurs="1"/>
                <xsd:element name="rich" type="a:CT_TextBody" minOccurs="1" maxOccurs="1"/>
            </xsd:choice>
        </xsd:sequence>
    </xsd:complexType>
    */
    /*
    <xsd:complexType name="CT_StrRef">
        <xsd:sequence>
            <xsd:element name="f" type="xsd:string" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="strCache" type="CT_StrData" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
        </xsd:sequence>
    </xsd:complexType>
    */
    /*
    <xsd:complexType name="CT_TextBody">
        <xsd:sequence>
            <xsd:element name="bodyPr" type="CT_TextBodyProperties" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="lstStyle" type="CT_TextListStyle" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="p" type="CT_TextParagraph" minOccurs="1" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>
    */

    writer.writeStartElement(QStringLiteral("c:title"));

    // CT_Tx {{
     writer.writeStartElement(QStringLiteral("c:tx"));

      writer.writeStartElement(QStringLiteral("c:rich")); // CT_TextBody

       writer.writeEmptyElement(QStringLiteral("a:bodyPr")); // CT_TextBodyProperties

       writer.writeEmptyElement(QStringLiteral("a:lstStyle")); // CT_TextListStyle

       writer.writeStartElement(QStringLiteral("a:p"));

        writer.writeStartElement(QStringLiteral("a:pPr"));
            writer.writeAttribute(QStringLiteral("lvl"), QString::number(0));

            writer.writeStartElement(QStringLiteral("a:defRPr"));
            writer.writeAttribute(QStringLiteral("b"), QString::number(0));
            writer.writeEndElement(); // a:defRPr
        writer.writeEndElement(); // a:pPr

        writer.writeStartElement(QStringLiteral("a:r"));
        QString strAxisName = GetAxisName(axis);
        writer.writeTextElement( QStringLiteral("a:t"), strAxisName );
        writer.writeEndElement(); // a:r

       writer.writeEndElement(); // a:p

      writer.writeEndElement(); // c:rich

     writer.writeEndElement(); // c:tx
     // CT_Tx }}

     writer.writeStartElement(QStringLiteral("c:overlay"));
        writer.writeAttribute(QStringLiteral("val"), QString::number(0)); // CT_Boolean
     writer.writeEndElement(); // c:overlay

    writer.writeEndElement(); // c:title

}

QString ChartPrivate::GetAxisPosString( XlsxAxis::AxisPos axisPos ) const
{
    QString pos;
    switch ( axisPos )
    {
        case XlsxAxis::Top    : pos = QStringLiteral("t"); break;
        case XlsxAxis::Bottom : pos = QStringLiteral("b"); break;
        case XlsxAxis::Left   : pos = QStringLiteral("l"); break;
        case XlsxAxis::Right  : pos = QStringLiteral("r"); break;
        default: break; // ??
    }

    return pos;
}

QString ChartPrivate::GetAxisName(XlsxAxis* axis) const
{
    QString strAxisName;
    if ( NULL == axis )
        return strAxisName;

    QString pos = GetAxisPosString( axis->axisPos ); // l, t, r, b
    if ( pos.isEmpty() )
        return strAxisName;

    strAxisName = axis->axisNames[ axis->axisPos ];
    return strAxisName;
}


///
/// \brief ChartPrivate::readSubTree
/// \param reader
/// \return
///
QString ChartPrivate::readSubTree(QXmlStreamReader &reader)
{
    QString treeString;
    QString prefix;
    const auto& treeName = reader.name();

    while (!reader.atEnd())
    {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement)
        {
            prefix = reader.prefix().toString();

            treeString += QLatin1String("<") + reader.qualifiedName().toString();

            const QXmlStreamAttributes attributes = reader.attributes();
            for (const QXmlStreamAttribute &attr : attributes) {
                treeString += QLatin1String(" ") + attr.name().toString() + QLatin1String("=\"") + attr.value().toString() + QLatin1String("\"");
            }
            treeString += QStringLiteral(">");
        }
        else if (reader.tokenType() == QXmlStreamReader::EndElement )
        {
            if( reader.name() == treeName)
            {
                break;
            }
            treeString += QLatin1String("</") + reader.qualifiedName().toString() + QLatin1String(">");
        }
    }

    return treeString;
}


///
/// \brief ChartPrivate::loadXmlChartLegend
/// \param reader
/// \return
///
bool ChartPrivate::loadXmlChartLegend(QXmlStreamReader &reader)
{

    Q_ASSERT(reader.name() == QLatin1String("legend"));

    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                                && reader.name() == QLatin1String("legend")))
    {
        if (reader.readNextStartElement())
        {
            if (reader.name() == QLatin1String("legendPos")) // c:legendPos
            {
                QString pos = reader.attributes().value(QLatin1String("val")).toString();
                if( pos.compare(QLatin1String("r"), Qt::CaseInsensitive) == 0)
                {
                    // legendPos = Chart::ChartAxisPos::Right;
                    legendPos = Chart::Right;
                }
                else
                if( pos.compare(QLatin1String("l"), Qt::CaseInsensitive) == 0)
                {
                    // legendPos = Chart::ChartAxisPos::Left;
                    legendPos = Chart::Left;
                }
                else
                if( pos.compare(QLatin1String("t"), Qt::CaseInsensitive) == 0)
                {
                    // legendPos = Chart::ChartAxisPos::Top;
                    legendPos = Chart::Top;
                }
                else
                if( pos.compare(QLatin1String("b"), Qt::CaseInsensitive) == 0)
                {
                    // legendPos = Chart::ChartAxisPos::Bottom;
                    legendPos = Chart::Bottom;
                }
                else
                {
                    // legendPos = Chart::ChartAxisPos::None;
                    legendPos = Chart::None;
                }
            }
            else
            if (reader.name() == QLatin1String("overlay")) // c:legendPos
            {
                QString pos = reader.attributes().value(QLatin1String("val")).toString();
                if( pos.compare(QLatin1String("1"), Qt::CaseInsensitive) == 0 )
                {
                    legendOverlay = true;
                }
                else
                {
                    legendOverlay = false;
                }
            }
        }
    }

    return false;
}

QT_END_NAMESPACE_XLSX