File indexing completed on 2024-05-26 03:50:49

0001 // xlsxchart.cpp
0002 
0003 #include <QtGlobal>
0004 #include <QString>
0005 #include <QIODevice>
0006 #include <QXmlStreamReader>
0007 #include <QXmlStreamWriter>
0008 #include <QDebug>
0009 
0010 #include "xlsxchart_p.h"
0011 #include "xlsxworksheet.h"
0012 #include "xlsxcellrange.h"
0013 #include "xlsxutility_p.h"
0014 
0015 QT_BEGIN_NAMESPACE_XLSX
0016 
0017 ChartPrivate::ChartPrivate(Chart *q, Chart::CreateFlag flag)
0018     : AbstractOOXmlFilePrivate(q, flag), chartType(static_cast<Chart::ChartType>(0))
0019 {
0020 
0021 }
0022 
0023 ChartPrivate::~ChartPrivate()
0024 {
0025 }
0026 
0027 
0028 
0029 /*!
0030  * \internal
0031  */
0032 Chart::Chart(AbstractSheet *parent, CreateFlag flag)
0033     : AbstractOOXmlFile(new ChartPrivate(this, flag))
0034 {
0035     Q_D(Chart);
0036 
0037     d_func()->sheet = parent;
0038 
0039     // d->legendPos = Chart::ChartAxisPos::None;
0040     d->legendPos = Chart::None;
0041     d->legendOverlay = false;
0042     d->majorGridlinesEnabled = false;
0043     d->minorGridlinesEnabled = false;
0044 }
0045 
0046 /*!
0047  * Destroys the chart.
0048  */
0049 Chart::~Chart()
0050 {
0051 }
0052 
0053 /*!
0054  * Add the data series which is in the range \a range of the \a sheet.
0055  */
0056 void Chart::addSeries(const CellRange &range, AbstractSheet *sheet, bool headerH, bool headerV, bool swapHeaders)
0057 {
0058     Q_D(Chart);
0059 
0060     if (!range.isValid())
0061         return;
0062     if (sheet && sheet->sheetType() != AbstractSheet::ST_WorkSheet)
0063         return;
0064     if (!sheet && d->sheet->sheetType() != AbstractSheet::ST_WorkSheet)
0065         return;
0066 
0067     QString sheetName = sheet ? sheet->sheetName() : d->sheet->sheetName();
0068     //In case sheetName contains space or '
0069     sheetName = escapeSheetName(sheetName);
0070 
0071     if (range.columnCount() == 1 || range.rowCount() == 1)
0072     {
0073         auto series = std::make_shared<XlsxSeries>();
0074         series->numberDataSource_numRef = sheetName + QLatin1String("!") + range.toString(true, true);
0075         d->seriesList.append(series);
0076     }
0077     else if ((range.columnCount() < range.rowCount()) || swapHeaders )
0078     {
0079         //Column based series
0080         int firstDataRow = range.firstRow();
0081         int firstDataColumn = range.firstColumn();
0082 
0083         QString axDataSouruce_numRef;
0084         if (d->chartType == CT_ScatterChart || d->chartType == CT_BubbleChart)
0085         {
0086             firstDataColumn += 1;
0087             CellRange subRange(range.firstRow(), range.firstColumn(), range.lastRow(), range.firstColumn());
0088             axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
0089         }
0090 
0091         if( headerH )
0092         {
0093             firstDataRow += 1;
0094         }
0095         if( headerV )
0096         {
0097             firstDataColumn += 1;
0098         }
0099 
0100         for (int col=firstDataColumn; col<=range.lastColumn(); ++col)
0101         {
0102             CellRange subRange(firstDataRow, col, range.lastRow(), col);
0103             auto series = std::make_shared<XlsxSeries>();
0104             series->axDataSource_numRef = axDataSouruce_numRef;
0105             series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
0106 
0107             if( headerH )
0108             {
0109                 CellRange subRange(range.firstRow(), col, range.firstRow(), col);
0110                 series->headerH_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
0111             }
0112             else
0113             {
0114                 series->headerH_numRef = QString();
0115             }
0116             if( headerV )
0117             {
0118                 CellRange subRange(firstDataRow, range.firstColumn(), range.lastRow(), range.firstColumn());
0119                 series->headerV_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
0120             }
0121             else
0122             {
0123                 series->headerV_numRef = QString();
0124             }
0125             series->swapHeader = swapHeaders;
0126 
0127             d->seriesList.append(series);
0128         }
0129 
0130     }
0131     else
0132     {
0133         //Row based series
0134         int firstDataRow = range.firstRow();
0135         int firstDataColumn = range.firstColumn();
0136 
0137         QString axDataSouruce_numRef;
0138         if (d->chartType == CT_ScatterChart || d->chartType == CT_BubbleChart)
0139         {
0140             firstDataRow += 1;
0141             CellRange subRange(range.firstRow(), range.firstColumn(), range.firstRow(), range.lastColumn());
0142             axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
0143         }
0144 
0145         if( headerH )
0146         {
0147             firstDataRow += 1;
0148         }
0149         if( headerV )
0150         {
0151             firstDataColumn += 1;
0152         }
0153 
0154         for (int row=firstDataRow; row<=range.lastRow(); ++row)
0155         {
0156             CellRange subRange(row, firstDataColumn, row, range.lastColumn());
0157             auto series = std::make_shared<XlsxSeries>();
0158             series->axDataSource_numRef = axDataSouruce_numRef;
0159             series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
0160 
0161             if( headerH )
0162             {
0163                 CellRange subRange(range.firstRow(), firstDataColumn, range.firstRow(), range.lastColumn());
0164                 series->headerH_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
0165             }
0166             else
0167             {
0168                 series->headerH_numRef = QString();
0169             }
0170 
0171             if( headerV )
0172             {
0173                 CellRange subRange(row, range.firstColumn(), row, range.firstColumn());
0174                 series->headerV_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
0175             }
0176             else
0177             {
0178                 series->headerV_numRef = QString();
0179             }
0180             series->swapHeader = swapHeaders;
0181 
0182             d->seriesList.append(series);
0183         }
0184     }
0185 }
0186 
0187 /*!
0188  * Set the type of the chart to \a type
0189  */
0190 void Chart::setChartType(ChartType type)
0191 {
0192     Q_D(Chart);
0193 
0194     d->chartType = type;
0195 }
0196 
0197 /*!
0198  * \internal
0199  *
0200  */
0201 void Chart::setChartStyle(int id)
0202 {
0203     Q_UNUSED(id)
0204     //!Todo
0205 }
0206 
0207 void Chart::setAxisTitle(Chart::ChartAxisPos pos, QString axisTitle)
0208 {
0209     Q_D(Chart);
0210 
0211     if ( axisTitle.isEmpty() )
0212         return;
0213 
0214     // dev24 : fixed for old compiler
0215     if ( pos == Chart::Left )
0216     {
0217         d->axisNames[ XlsxAxis::Left ] = axisTitle;
0218     }
0219     else if ( pos == Chart::Top )
0220     {
0221         d->axisNames[ XlsxAxis::Top ] = axisTitle;
0222     }
0223     else if ( pos == Chart::Right )
0224     {
0225         d->axisNames[ XlsxAxis::Right ] = axisTitle;
0226     }
0227     else if ( pos == Chart::Bottom )
0228     {
0229         d->axisNames[ XlsxAxis::Bottom ] = axisTitle;
0230     }
0231 
0232 }
0233 
0234 // dev25
0235 void Chart::setChartTitle(QString strchartTitle)
0236 {
0237     Q_D(Chart);
0238 
0239     d->chartTitle = strchartTitle;
0240 }
0241 
0242 
0243 void Chart::setChartLegend(Chart::ChartAxisPos legendPos, bool overlay)
0244 {
0245     Q_D(Chart);
0246 
0247     d->legendPos = legendPos;
0248     d->legendOverlay = overlay;
0249 }
0250 
0251 
0252 void Chart::setGridlinesEnable(bool majorGridlinesEnable, bool minorGridlinesEnable)
0253 {
0254     Q_D(Chart);
0255 
0256     d->majorGridlinesEnabled = majorGridlinesEnable;
0257     d->minorGridlinesEnabled = minorGridlinesEnable;
0258 }
0259 
0260 
0261 /*!
0262  * \internal
0263  */
0264 void Chart::saveToXmlFile(QIODevice *device) const
0265 {
0266     Q_D(const Chart);
0267 
0268     /*
0269         <chartSpace>
0270             <chart>
0271                 <view3D>
0272                     <perspective val="30"/>
0273                 </view3D>
0274                 <plotArea>
0275                     <layout/>
0276                     <barChart>
0277                     ...
0278                     </barChart>
0279                     <catAx/>
0280                     <valAx/>
0281                 </plotArea>
0282                 <legend>
0283                 ...
0284                 </legend>
0285             </chart>
0286             <printSettings>
0287             </printSettings>
0288         </chartSpace>
0289     */
0290 
0291     QXmlStreamWriter writer(device);
0292 
0293     writer.writeStartDocument(QStringLiteral("1.0"), true);
0294 
0295     // L.4.13.2.2 Chart
0296     //
0297     //  chartSpace is the root node, which contains an element defining the chart,
0298     // and an element defining the print settings for the chart.
0299 
0300     writer.writeStartElement(QStringLiteral("c:chartSpace"));
0301 
0302     writer.writeAttribute(QStringLiteral("xmlns:c"),
0303                           QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/chart"));
0304     writer.writeAttribute(QStringLiteral("xmlns:a"),
0305                           QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/main"));
0306     writer.writeAttribute(QStringLiteral("xmlns:r"),
0307                           QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
0308 
0309     /*
0310     * chart is the root element for the chart. If the chart is a 3D chart,
0311     * then a view3D element is contained, which specifies the 3D view.
0312     * It then has a plot area, which defines a layout and contains an element
0313     * that corresponds to, and defines, the type of chart.
0314     */
0315 
0316     d->saveXmlChart(writer);
0317 
0318     writer.writeEndElement();// c:chartSpace
0319     writer.writeEndDocument();
0320 }
0321 
0322 /*!
0323  * \internal
0324  */
0325 bool Chart::loadFromXmlFile(QIODevice *device)
0326 {
0327     Q_D(Chart);
0328 
0329     QXmlStreamReader reader(device);
0330     while (!reader.atEnd())
0331     {
0332         reader.readNextStartElement();
0333         if (reader.tokenType() == QXmlStreamReader::StartElement)
0334         {
0335             if (reader.name() == QLatin1String("chart"))
0336             {
0337                 if (!d->loadXmlChart(reader))
0338                 {
0339                     return false;
0340                 }
0341             }
0342         }
0343     }
0344 
0345     return true;
0346 }
0347 
0348 
0349 bool ChartPrivate::loadXmlChart(QXmlStreamReader &reader)
0350 {
0351     Q_ASSERT(reader.name() == QLatin1String("chart"));
0352 
0353 //    qDebug() << "-------------- loadXmlChart";
0354 
0355     while (!reader.atEnd())
0356     {
0357         reader.readNextStartElement();
0358 
0359 //        qDebug() << "-------------1- " << reader.name();
0360 
0361         if (reader.tokenType() == QXmlStreamReader::StartElement)
0362         {
0363 
0364             if (reader.name() == QLatin1String("plotArea"))
0365             {
0366                 if (!loadXmlPlotArea(reader))
0367                 {
0368                     return false;
0369                 }
0370             }
0371             else if (reader.name() == QLatin1String("title"))
0372             {
0373                 //!Todo
0374 
0375                 if ( loadXmlChartTitle(reader) )
0376                 {
0377                 }
0378 
0379             }
0380 //            else if (reader.name() == QLatin1String("legend"))
0381 //            {
0382 //                loadXmlChartLegend(reader);
0383 //                qDebug() << "-------------- loadXmlChartLegend";
0384 //            }
0385         }
0386         else if (reader.tokenType() == QXmlStreamReader::EndElement &&
0387                  reader.name() == QLatin1String("chart") )
0388         {
0389             break;
0390         }
0391     }
0392     return true;
0393 }
0394 
0395 // TO DEBUG: loop is not work, when i looping second element.
0396 /*
0397 dchrt_CT_PlotArea =
0398     element layout { dchrt_CT_Layout }?,
0399     (element areaChart { dchrt_CT_AreaChart }
0400         | element area3DChart { dchrt_ CT_Area3DChart }
0401         | element lineChart { dchrt_CT_LineChart }
0402         | element line3DChart { dchrt_CT_Line3DChart }
0403         | element stockChart { dchrt_CT_StockChart }
0404         | element radarChart { dchrt_CT_RadarChart }
0405         | element scatterChart { dchrt_CT_ScatterChart }
0406         | element pieChart { dchrt_CT_PieChart }
0407         | element pie3DChart { dchrt_CT_Pie3DChart }
0408         | element doughnutChart { dchrt_CT_DoughnutChart }
0409         | element barChart { dchrt_CT_BarChart }
0410         | element bar3DChart { dchrt_CT_Bar3DChart }
0411         | element ofPieChart { dchrt_CT_OfPieChart }
0412         | element surfaceChart { dchrt_CT_SurfaceChart }
0413         | element surface3DChart { dchrt_CT_Surface3DChart }
0414         | element bubbleChart { dchrt_CT_BubbleChart })+,
0415     (element valAx { dchrt_CT_ValAx }
0416         | element catAx { dchrt_CT_CatAx }
0417         | element dateAx { dchrt_CT_DateAx }
0418         | element serAx { dchrt_CT_SerAx })*,
0419     element dTable { dchrt_CT_DTable }?,
0420     element spPr { a_CT_ShapeProperties }?,
0421     element extLst { dchrt_CT_ExtensionList }?
0422  */
0423 bool ChartPrivate::loadXmlPlotArea(QXmlStreamReader &reader)
0424 {
0425     Q_ASSERT(reader.name() == QLatin1String("plotArea"));
0426 
0427     // TO DEBUG:
0428 
0429     reader.readNext();
0430 
0431     while (!reader.atEnd())
0432     {
0433 //        qDebug() << "-------------2- " << reader.name();
0434 
0435         if (reader.isStartElement())
0436         {
0437             if (!loadXmlPlotAreaElement(reader))
0438             {
0439                 qDebug() << "[debug] failed to load plotarea element.";
0440                 return false;
0441             }
0442             else if (reader.name() == QLatin1String("legend"))  // Why here?
0443             {
0444                 loadXmlChartLegend(reader);
0445 //                qDebug() << "-------------- loadXmlChartLegend";
0446             }
0447 
0448             reader.readNext();
0449         }
0450         else
0451         {
0452             reader.readNext();
0453         }
0454     }
0455 
0456     return true;
0457 }
0458 
0459 bool ChartPrivate::loadXmlPlotAreaElement(QXmlStreamReader &reader)
0460 {
0461     if (reader.name() == QLatin1String("layout"))
0462     {
0463         //!ToDo extract attributes
0464         layout = readSubTree(reader);
0465     }
0466     else if (reader.name().endsWith(QLatin1String("Chart")))
0467     {
0468         // for pieChart, barChart, ... (choose one)
0469         if ( !loadXmlXxxChart(reader) )
0470         {
0471             qDebug() << "[debug] failed to load chart";
0472             return false;
0473         }
0474     }
0475     else if (reader.name() == QLatin1String("catAx")) // choose one : catAx, dateAx, serAx, valAx
0476     {
0477         // qDebug() << "loadXmlAxisCatAx()";
0478         loadXmlAxisCatAx(reader);
0479     }
0480     else if (reader.name() == QLatin1String("dateAx")) // choose one : catAx, dateAx, serAx, valAx
0481     {
0482         // qDebug() << "loadXmlAxisDateAx()";
0483         loadXmlAxisDateAx(reader);
0484     }
0485     else if (reader.name() == QLatin1String("serAx")) // choose one : catAx, dateAx, serAx, valAx
0486     {
0487         // qDebug() << "loadXmlAxisSerAx()";
0488         loadXmlAxisSerAx(reader);
0489     }
0490     else if (reader.name() == QLatin1String("valAx")) // choose one : catAx, dateAx, serAx, valAx
0491     {
0492         // qDebug() << "loadXmlAxisValAx()";
0493         loadXmlAxisValAx(reader);
0494     }
0495     else if (reader.name() == QLatin1String("dTable"))
0496     {
0497         //!ToDo
0498         // dTable "CT_DTable"
0499         // reader.skipCurrentElement();
0500     }
0501     else if (reader.name() == QLatin1String("spPr"))
0502     {
0503         //!ToDo
0504         // spPr "a:CT_ShapeProperties"
0505         // reader.skipCurrentElement();
0506     }
0507     else if (reader.name() == QLatin1String("extLst"))
0508     {
0509         //!ToDo
0510         // extLst "CT_ExtensionList"
0511         // reader.skipCurrentElement();
0512     }
0513 
0514     return true;
0515 }
0516 
0517 bool ChartPrivate::loadXmlXxxChart(QXmlStreamReader &reader)
0518 {
0519     const auto& name = reader.name();
0520 
0521     if (name == QLatin1String("areaChart"))
0522     {
0523         chartType = Chart::CT_AreaChart;
0524     }
0525     else if (name == QLatin1String("area3DChart"))
0526     {
0527         chartType = Chart::CT_Area3DChart;
0528     }
0529     else if (name == QLatin1String("lineChart"))
0530     {
0531         chartType = Chart::CT_LineChart;
0532     }
0533     else if (name == QLatin1String("line3DChart"))
0534     {
0535         chartType = Chart::CT_Line3DChart;
0536     }
0537     else if (name == QLatin1String("stockChart"))
0538     {
0539         chartType = Chart::CT_StockChart;
0540     }
0541     else if (name == QLatin1String("radarChart"))
0542     {
0543         chartType = Chart::CT_RadarChart;
0544     }
0545     else if (name == QLatin1String("scatterChart"))
0546     {
0547         chartType = Chart::CT_ScatterChart;
0548     }
0549     else if (name == QLatin1String("pieChart"))
0550     {
0551         chartType = Chart::CT_PieChart;
0552     }
0553     else if (name == QLatin1String("pie3DChart"))
0554     {
0555         chartType = Chart::CT_Pie3DChart;
0556     }
0557     else if (name == QLatin1String("doughnutChart"))
0558     {
0559         chartType = Chart::CT_DoughnutChart;
0560     }
0561     else if (name == QLatin1String("barChart"))
0562     {
0563         chartType = Chart::CT_BarChart;
0564     }
0565     else if (name == QLatin1String("bar3DChart"))
0566     {
0567         chartType = Chart::CT_Bar3DChart;
0568     }
0569     else if (name == QLatin1String("ofPieChart"))
0570     {
0571         chartType = Chart::CT_OfPieChart;
0572     }
0573     else if (name == QLatin1String("surfaceChart"))
0574     {
0575         chartType = Chart::CT_SurfaceChart;
0576     }
0577     else if (name == QLatin1String("surface3DChart"))
0578     {
0579         chartType = Chart::CT_Surface3DChart;
0580     }
0581     else if (name == QLatin1String("bubbleChart"))
0582     {
0583         chartType = Chart::CT_BubbleChart;
0584     }
0585     else
0586     {
0587         qDebug() << "[undefined chart type] " << name;
0588         chartType = Chart::CT_NoStatementChart;
0589         return false;
0590     }
0591 
0592     while( !reader.atEnd() )
0593     {
0594         reader.readNextStartElement();
0595         if (reader.tokenType() == QXmlStreamReader::StartElement)
0596         {
0597             // dev57
0598 
0599             if ( reader.name() == QLatin1String("ser") )
0600             {
0601                 loadXmlSer(reader);
0602             }
0603             else if (reader.name() == QLatin1String("varyColors"))
0604             {
0605             }
0606             else if (reader.name() == QLatin1String("barDir"))
0607             {
0608             }
0609             else if (reader.name() == QLatin1String("axId"))
0610             {
0611                 //
0612 
0613             }
0614             else if (reader.name() == QLatin1String("scatterStyle"))
0615             {
0616             }
0617             else if (reader.name() == QLatin1String("holeSize"))
0618             {
0619             }
0620             else
0621             {
0622             }
0623 
0624         }
0625         else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
0626                   reader.name() == name )
0627         {
0628             break;
0629         }
0630     }
0631 
0632     return true;
0633 }
0634 
0635 bool ChartPrivate::loadXmlSer(QXmlStreamReader &reader)
0636 {
0637     Q_ASSERT(reader.name() == QLatin1String("ser"));
0638 
0639     auto series = std::make_shared<XlsxSeries>();
0640     seriesList.append(series);
0641 
0642     while ( !reader.atEnd() &&
0643             !(reader.tokenType() == QXmlStreamReader::EndElement &&
0644              reader.name() == QLatin1String("ser")) )
0645     {
0646         if (reader.readNextStartElement())
0647         {
0648             //TODO beide Header noch auswerten RTR 2019.11
0649             const auto& name = reader.name();
0650             if ( name == QLatin1String("tx") )
0651             {
0652                 while ( !reader.atEnd() &&
0653                         !(reader.tokenType() == QXmlStreamReader::EndElement &&
0654                          reader.name() == name))
0655                 {
0656                     if (reader.readNextStartElement())
0657                     {
0658                         if (reader.name() == QLatin1String("strRef"))
0659                             series->headerV_numRef = loadXmlStrRef(reader);
0660                     }
0661                 }
0662             }
0663             else if ( name == QLatin1String("cat") ||
0664                  name == QLatin1String("xVal") )
0665             {
0666                 while ( !reader.atEnd() &&
0667                         !(reader.tokenType() == QXmlStreamReader::EndElement &&
0668                          reader.name() == name))
0669                 {
0670                     if (reader.readNextStartElement())
0671                     {
0672                         if (reader.name() == QLatin1String("numRef"))
0673                             series->axDataSource_numRef = loadXmlNumRef(reader);
0674                         else
0675                         if (reader.name() == QLatin1String("strRef"))
0676                             series->headerH_numRef = loadXmlStrRef(reader);
0677                     }
0678                 }
0679             }
0680             else if (name == QLatin1String("val") || name == QLatin1String("yVal"))
0681             {
0682                 while ( !reader.atEnd() &&
0683                         !(reader.tokenType() == QXmlStreamReader::EndElement &&
0684                         reader.name() == name))
0685                 {
0686                     if (reader.readNextStartElement())
0687                     {
0688                         if (reader.name() == QLatin1String("numRef"))
0689                             series->numberDataSource_numRef = loadXmlNumRef(reader);
0690                     }
0691                 }
0692             }
0693             else if (name == QLatin1String("extLst"))
0694             {
0695                 while ( !reader.atEnd() &&
0696                         !(reader.tokenType() == QXmlStreamReader::EndElement &&
0697                          reader.name() == name))
0698                 {
0699                     reader.readNextStartElement();
0700                 }
0701             }
0702         }
0703     }
0704 
0705     return true;
0706 }
0707 
0708 
0709 QString ChartPrivate::loadXmlNumRef(QXmlStreamReader &reader)
0710 {
0711     Q_ASSERT(reader.name() == QLatin1String("numRef"));
0712 
0713     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0714                                 && reader.name() == QLatin1String("numRef")))
0715     {
0716         if (reader.readNextStartElement())
0717         {
0718             if (reader.name() == QLatin1String("f"))
0719                 return reader.readElementText();
0720         }
0721     }
0722 
0723     return QString();
0724 }
0725 
0726 
0727 QString ChartPrivate::loadXmlStrRef(QXmlStreamReader &reader)
0728 {
0729     Q_ASSERT(reader.name() == QLatin1String("strRef"));
0730 
0731     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0732                                 && reader.name() == QLatin1String("strRef")))
0733     {
0734         if (reader.readNextStartElement())
0735         {
0736             if (reader.name() == QLatin1String("f"))
0737                 return reader.readElementText();
0738         }
0739     }
0740 
0741     return QString();
0742 }
0743 
0744 
0745 void ChartPrivate::saveXmlChart(QXmlStreamWriter &writer) const
0746 {
0747     //----------------------------------------------------
0748     // c:chart
0749     writer.writeStartElement(QStringLiteral("c:chart"));
0750 
0751     //----------------------------------------------------
0752     // c:title
0753 
0754     saveXmlChartTitle(writer); // write 'chart title'
0755 
0756     //----------------------------------------------------
0757     // c:plotArea
0758 
0759     writer.writeStartElement(QStringLiteral("c:plotArea"));
0760 
0761     // a little workaround for Start- and EndElement with starting ">" and ending without ">"
0762     writer.device()->write("><c:layout>"); //layout
0763     writer.device()->write(layout.toUtf8());
0764     writer.device()->write("</c:layout"); //layout
0765 
0766     // dev35
0767     switch (chartType)
0768     {
0769         case Chart::CT_AreaChart:       saveXmlAreaChart(writer); break;
0770         case Chart::CT_Area3DChart:     saveXmlAreaChart(writer); break;
0771         case Chart::CT_LineChart:       saveXmlLineChart(writer); break;
0772         case Chart::CT_Line3DChart:     saveXmlLineChart(writer); break;
0773         case Chart::CT_StockChart: break;
0774         case Chart::CT_RadarChart: break;
0775         case Chart::CT_ScatterChart:    saveXmlScatterChart(writer); break;
0776         case Chart::CT_PieChart:        saveXmlPieChart(writer); break;
0777         case Chart::CT_Pie3DChart:      saveXmlPieChart(writer); break;
0778         case Chart::CT_DoughnutChart:   saveXmlDoughnutChart(writer); break;
0779         case Chart::CT_BarChart:        saveXmlBarChart(writer); break;
0780         case Chart::CT_Bar3DChart:      saveXmlBarChart(writer); break;
0781         case Chart::CT_OfPieChart: break;
0782         case Chart::CT_SurfaceChart:  break;
0783         case Chart::CT_Surface3DChart: break;
0784         case Chart::CT_BubbleChart:  break;
0785         default:  break;
0786     }
0787 
0788     saveXmlAxis(writer); // c:catAx, c:valAx, c:serAx, c:dateAx (choose one)
0789 
0790     //!TODO: write element
0791     // c:dTable CT_DTable
0792     // c:spPr   CT_ShapeProperties
0793     // c:extLst CT_ExtensionList
0794 
0795     writer.writeEndElement(); // c:plotArea
0796 
0797     // c:legend
0798     saveXmlChartLegend(writer); // c:legend
0799 
0800     writer.writeEndElement(); // c:chart
0801 }
0802 
0803 bool ChartPrivate::loadXmlChartTitle(QXmlStreamReader &reader)
0804 {
0805     //!TODO : load chart title
0806 
0807     Q_ASSERT(reader.name() == QLatin1String("title"));
0808 
0809     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0810                                 && reader.name() == QLatin1String("title")))
0811     {
0812         if (reader.readNextStartElement())
0813         {
0814             if (reader.name() == QLatin1String("tx")) // c:tx
0815                 return loadXmlChartTitleTx(reader);
0816         }
0817     }
0818 
0819     return false;
0820 }
0821 
0822 bool ChartPrivate::loadXmlChartTitleTx(QXmlStreamReader &reader)
0823 {
0824     Q_ASSERT(reader.name() == QLatin1String("tx"));
0825 
0826     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0827                                 && reader.name() == QLatin1String("tx")))
0828     {
0829         if (reader.readNextStartElement())
0830         {
0831             if (reader.name() == QLatin1String("rich")) // c:rich
0832                 return loadXmlChartTitleTxRich(reader);
0833         }
0834     }
0835 
0836     return false;
0837 }
0838 
0839 bool ChartPrivate::loadXmlChartTitleTxRich(QXmlStreamReader &reader)
0840 {
0841     Q_ASSERT(reader.name() == QLatin1String("rich"));
0842 
0843     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0844                                 && reader.name() == QLatin1String("rich")))
0845     {
0846         if (reader.readNextStartElement())
0847         {
0848             if (reader.name() == QLatin1String("p")) // a:p
0849                 return loadXmlChartTitleTxRichP(reader);
0850         }
0851     }
0852 
0853     return false;
0854 }
0855 
0856 bool ChartPrivate::loadXmlChartTitleTxRichP(QXmlStreamReader &reader)
0857 {
0858     Q_ASSERT(reader.name() == QLatin1String("p"));
0859 
0860     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0861                                 && reader.name() == QLatin1String("p")))
0862     {
0863         if (reader.readNextStartElement())
0864         {
0865             if (reader.name() == QLatin1String("r")) // a:r
0866                 return loadXmlChartTitleTxRichP_R(reader);
0867         }
0868     }
0869 
0870     return false;
0871 }
0872 
0873 bool ChartPrivate::loadXmlChartTitleTxRichP_R(QXmlStreamReader &reader)
0874 {
0875     Q_ASSERT(reader.name() == QLatin1String("r"));
0876 
0877     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0878                                 && reader.name() == QLatin1String("r")))
0879     {
0880         if (reader.readNextStartElement())
0881         {
0882             if (reader.name() == QLatin1String("t")) // a:t
0883             {
0884                 QString textValue = reader.readElementText();
0885                 this->chartTitle = textValue;
0886                 return true;
0887             }
0888         }
0889     }
0890 
0891     return false;
0892 }
0893 
0894 
0895 // write 'chart title'
0896 void ChartPrivate::saveXmlChartTitle(QXmlStreamWriter &writer) const
0897 {
0898     if ( chartTitle.isEmpty() )
0899         return;
0900 
0901     writer.writeStartElement(QStringLiteral("c:title"));
0902     /*
0903     <xsd:complexType name="CT_Title">
0904         <xsd:sequence>
0905             <xsd:element name="tx"      type="CT_Tx"                minOccurs="0" maxOccurs="1"/>
0906             <xsd:element name="layout"  type="CT_Layout"            minOccurs="0" maxOccurs="1"/>
0907             <xsd:element name="overlay" type="CT_Boolean"           minOccurs="0" maxOccurs="1"/>
0908             <xsd:element name="spPr"    type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
0909             <xsd:element name="txPr"    type="a:CT_TextBody"        minOccurs="0" maxOccurs="1"/>
0910             <xsd:element name="extLst"  type="CT_ExtensionList"     minOccurs="0" maxOccurs="1"/>
0911         </xsd:sequence>
0912     </xsd:complexType>
0913     */
0914 
0915         writer.writeStartElement(QStringLiteral("c:tx"));
0916         /*
0917         <xsd:complexType name="CT_Tx">
0918             <xsd:sequence>
0919                 <xsd:choice     minOccurs="1"   maxOccurs="1">
0920                 <xsd:element    name="strRef"   type="CT_StrRef"        minOccurs="1" maxOccurs="1"/>
0921                 <xsd:element    name="rich"     type="a:CT_TextBody"    minOccurs="1" maxOccurs="1"/>
0922                 </xsd:choice>
0923             </xsd:sequence>
0924         </xsd:complexType>
0925         */
0926 
0927             writer.writeStartElement(QStringLiteral("c:rich"));
0928             /*
0929             <xsd:complexType name="CT_TextBody">
0930                 <xsd:sequence>
0931                     <xsd:element name="bodyPr"      type="CT_TextBodyProperties"    minOccurs=" 1"  maxOccurs="1"/>
0932                     <xsd:element name="lstStyle"    type="CT_TextListStyle"         minOccurs="0"   maxOccurs="1"/>
0933                     <xsd:element name="p"           type="CT_TextParagraph"         minOccurs="1"   maxOccurs="unbounded"/>
0934                 </xsd:sequence>
0935             </xsd:complexType>
0936             */
0937 
0938                 writer.writeEmptyElement(QStringLiteral("a:bodyPr")); // <a:bodyPr/>
0939                 /*
0940                 <xsd:complexType name="CT_TextBodyProperties">
0941                     <xsd:sequence>
0942                         <xsd:element name="prstTxWarp" type="CT_PresetTextShape" minOccurs="0" maxOccurs="1"/>
0943                         <xsd:group ref="EG_TextAutofit" minOccurs="0" maxOccurs="1"/>
0944                         <xsd:element name="scene3d" type="CT_Scene3D" minOccurs="0" maxOccurs="1"/>
0945                         <xsd:group ref="EG_Text3D" minOccurs="0" maxOccurs="1"/>
0946                         <xsd:element name="extLst" type="CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
0947                     </xsd:sequence>
0948                     <xsd:attribute name="rot" type="ST_Angle" use="optional"/>
0949                     <xsd:attribute name="spcFirstLastPara" type="xsd:boolean" use="optional"/>
0950                     <xsd:attribute name="vertOverflow" type="ST_TextVertOverflowType" use="optional"/>
0951                     <xsd:attribute name="horzOverflow" type="ST_TextHorzOverflowType" use="optional"/>
0952                     <xsd:attribute name="vert" type="ST_TextVerticalType" use="optional"/>
0953                     <xsd:attribute name="wrap" type="ST_TextWrappingType" use="optional"/>
0954                     <xsd:attribute name="lIns" type="ST_Coordinate32" use="optional"/>
0955                     <xsd:attribute name="tIns" type="ST_Coordinate32" use="optional"/>
0956                     <xsd:attribute name="rIns" type="ST_Coordinate32" use="optional"/>
0957                     <xsd:attribute name="bIns" type="ST_Coordinate32" use="optional"/>
0958                     <xsd:attribute name="numCol" type="ST_TextColumnCount" use="optional"/>
0959                     <xsd:attribute name="spcCol" type="ST_PositiveCoordinate32" use="optional"/>
0960                     <xsd:attribute name="rtlCol" type="xsd:boolean" use="optional"/>
0961                     <xsd:attribute name="fromWordArt" type="xsd:boolean" use="optional"/>
0962                     <xsd:attribute name="anchor" type="ST_TextAnchoringType" use="optional"/>
0963                     <xsd:attribute name="anchorCtr" type="xsd:boolean" use="optional"/>
0964                     <xsd:attribute name="forceAA" type="xsd:boolean" use="optional"/>
0965                     <xsd:attribute name="upright" type="xsd:boolean" use="optional" default="false"/>
0966                     <xsd:attribute name="compatLnSpc" type="xsd:boolean" use="optional"/>
0967                 </xsd:complexType>
0968                  */
0969 
0970                 writer.writeEmptyElement(QStringLiteral("a:lstStyle")); // <a:lstStyle/>
0971 
0972                 writer.writeStartElement(QStringLiteral("a:p"));
0973                 /*
0974                 <xsd:complexType name="CT_TextParagraph">
0975                     <xsd:sequence>
0976                         <xsd:element    name="pPr"          type="CT_TextParagraphProperties" minOccurs="0" maxOccurs="1"/>
0977                         <xsd:group      ref="EG_TextRun"    minOccurs="0" maxOccurs="unbounded"/>
0978                         <xsd:element    name="endParaRPr"   type="CT_TextCharacterProperties" minOccurs="0"
0979                         maxOccurs="1"/>
0980                     </xsd:sequence>
0981                 </xsd:complexType>
0982                  */
0983 
0984                     // <a:pPr lvl="0">
0985                     writer.writeStartElement(QStringLiteral("a:pPr"));
0986 
0987                         writer.writeAttribute(QStringLiteral("lvl"), QStringLiteral("0"));
0988 
0989                         // <a:defRPr b="0"/>
0990                         writer.writeStartElement(QStringLiteral("a:defRPr"));
0991 
0992                             writer.writeAttribute(QStringLiteral("b"), QStringLiteral("0"));
0993 
0994                         writer.writeEndElement();  // a:defRPr
0995 
0996                     writer.writeEndElement();  // a:pPr
0997 
0998                 /*
0999                 <xsd:group name="EG_TextRun">
1000                     <xsd:choice>
1001                         <xsd:element name="r"   type="CT_RegularTextRun"/>
1002                         <xsd:element name="br"  type="CT_TextLineBreak"/>
1003                         <xsd:element name="fld" type="CT_TextField"/>
1004                     </xsd:choice>
1005                 </xsd:group>
1006                 */
1007 
1008                 writer.writeStartElement(QStringLiteral("a:r"));
1009                 /*
1010                 <xsd:complexType name="CT_RegularTextRun">
1011                     <xsd:sequence>
1012                         <xsd:element name="rPr" type="CT_TextCharacterProperties" minOccurs="0" maxOccurs="1"/>
1013                         <xsd:element name="t"   type="xsd:string" minOccurs="1" maxOccurs="1"/>
1014                     </xsd:sequence>
1015                 </xsd:complexType>
1016                  */
1017 
1018                     // <a:t>chart name</a:t>
1019                     writer.writeTextElement(QStringLiteral("a:t"), chartTitle);
1020 
1021                 writer.writeEndElement();  // a:r
1022 
1023                 writer.writeEndElement();  // a:p
1024 
1025             writer.writeEndElement();  // c:rich
1026 
1027         writer.writeEndElement();  // c:tx
1028 
1029         // <c:overlay val="0"/>
1030         writer.writeStartElement(QStringLiteral("c:overlay"));
1031             writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0"));
1032         writer.writeEndElement();  // c:overlay
1033 
1034     writer.writeEndElement();  // c:title
1035 }
1036 // }}
1037 
1038 
1039 // write 'chart legend'
1040 void ChartPrivate::saveXmlChartLegend(QXmlStreamWriter &writer) const
1041 {
1042     if ( legendPos == Chart::None )
1043         return;
1044 
1045 //    <c:legend>
1046 //    <c:legendPos val="r"/>
1047 //    <c:overlay val="0"/>
1048 //    </c:legend>
1049 
1050     writer.writeStartElement(QStringLiteral("c:legend"));
1051 
1052         writer.writeStartElement(QStringLiteral("c:legendPos"));
1053 
1054             QString pos;
1055             switch( legendPos )
1056             {
1057                 //case Chart::ChartAxisPos::Right:
1058                 case Chart::Right :
1059                     pos = QStringLiteral("r");
1060                     break;
1061 
1062                 // case Chart::ChartAxisPos::Left:
1063                 case Chart::Left :
1064                     pos = QStringLiteral("l");
1065                     break;
1066 
1067                 // case Chart::ChartAxisPos::Top:
1068                 case Chart::Top :
1069                     pos = QStringLiteral("t");
1070                     break;
1071 
1072                 // case Chart::ChartAxisPos::Bottom:
1073                 case Chart::Bottom :
1074                     pos = QStringLiteral("b");
1075                     break;
1076 
1077                 default:
1078                     pos = QStringLiteral("r");
1079                     break;
1080             }
1081 
1082             writer.writeAttribute(QStringLiteral("val"), pos);
1083 
1084         writer.writeEndElement();  // c:legendPos
1085 
1086         writer.writeStartElement(QStringLiteral("c:overlay"));
1087 
1088             if( legendOverlay )
1089             {
1090                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));
1091             }
1092             else
1093             {
1094                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0"));
1095             }
1096 
1097         writer.writeEndElement();  // c:overlay
1098 
1099     writer.writeEndElement(); // c:legend
1100 }
1101 
1102 
1103 void ChartPrivate::saveXmlPieChart(QXmlStreamWriter &writer) const
1104 {
1105     QString name = chartType == Chart::CT_PieChart ? QStringLiteral("c:pieChart") : QStringLiteral("c:pie3DChart");
1106 
1107     writer.writeStartElement(name);
1108 
1109     //Do the same behavior as Excel, Pie prefer varyColors
1110     writer.writeEmptyElement(QStringLiteral("c:varyColors"));
1111     writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));
1112 
1113     for (int i=0; i<seriesList.size(); ++i)
1114         saveXmlSer(writer, seriesList[i].get(), i);
1115 
1116     writer.writeEndElement(); //pieChart, pie3DChart
1117 }
1118 
1119 void ChartPrivate::saveXmlBarChart(QXmlStreamWriter &writer) const
1120 {
1121     QString name = chartType == Chart::CT_BarChart ? QStringLiteral("c:barChart") : QStringLiteral("c:bar3DChart");
1122 
1123     writer.writeStartElement(name);
1124 
1125     writer.writeEmptyElement(QStringLiteral("c:barDir"));
1126     writer.writeAttribute(QStringLiteral("val"), QStringLiteral("col"));
1127 
1128     for ( int i = 0 ; i < seriesList.size() ; ++i )
1129     {
1130         saveXmlSer(writer, seriesList[i].get(), i);
1131     }
1132 
1133     if ( axisList.isEmpty() )
1134     {
1135         const_cast<ChartPrivate*>(this)->axisList.append(
1136                     std::make_shared<XlsxAxis>( XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1, axisNames[XlsxAxis::Bottom] ));
1137 
1138         const_cast<ChartPrivate*>(this)->axisList.append(
1139                     std::make_shared<XlsxAxis>( XlsxAxis::T_Val, XlsxAxis::Left, 1, 0, axisNames[XlsxAxis::Left] ));
1140     }
1141 
1142 
1143     // Note: Bar3D have 2~3 axes
1144     // int axisListSize = axisList.size();
1145     // [dev62]
1146     // Q_ASSERT( axisListSize == 2 ||
1147     //          ( axisListSize == 3 && chartType == Chart::CT_Bar3DChart ) );
1148 
1149     for ( int i = 0 ; i < axisList.size() ; ++i )
1150     {
1151         writer.writeEmptyElement(QStringLiteral("c:axId"));
1152         writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
1153     }
1154 
1155     writer.writeEndElement(); //barChart, bar3DChart
1156 }
1157 
1158 void ChartPrivate::saveXmlLineChart(QXmlStreamWriter &writer) const
1159 {
1160     QString name = chartType==Chart::CT_LineChart ? QStringLiteral("c:lineChart") : QStringLiteral("c:line3DChart");
1161 
1162     writer.writeStartElement(name);
1163 
1164     // writer.writeEmptyElement(QStringLiteral("grouping")); // dev22
1165 
1166     for (int i=0; i<seriesList.size(); ++i)
1167         saveXmlSer(writer, seriesList[i].get(), i);
1168 
1169     if (axisList.isEmpty())
1170     {
1171         const_cast<ChartPrivate*>(this)->axisList.append(std::make_shared<XlsxAxis>(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1, axisNames[XlsxAxis::Bottom] ));
1172         const_cast<ChartPrivate*>(this)->axisList.append(std::make_shared<XlsxAxis>(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0, axisNames[XlsxAxis::Left] ));
1173         if (chartType==Chart::CT_Line3DChart)
1174             const_cast<ChartPrivate*>(this)->axisList.append(std::make_shared<XlsxAxis>(XlsxAxis::T_Ser, XlsxAxis::Bottom, 2, 0));
1175     }
1176 
1177     Q_ASSERT((axisList.size()==2||chartType==Chart::CT_LineChart)|| (axisList.size()==3 && chartType==Chart::CT_Line3DChart));
1178 
1179     for (int i=0; i<axisList.size(); ++i)
1180     {
1181         writer.writeEmptyElement(QStringLiteral("c:axId"));
1182         writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
1183     }
1184 
1185     writer.writeEndElement(); //lineChart, line3DChart
1186 }
1187 
1188 void ChartPrivate::saveXmlScatterChart(QXmlStreamWriter &writer) const
1189 {
1190     const QString name = QStringLiteral("c:scatterChart");
1191 
1192     writer.writeStartElement(name);
1193 
1194     writer.writeEmptyElement(QStringLiteral("c:scatterStyle"));
1195 
1196     for (int i=0; i<seriesList.size(); ++i)
1197         saveXmlSer(writer, seriesList[i].get(), i);
1198 
1199     if (axisList.isEmpty())
1200     {
1201         const_cast<ChartPrivate*>(this)->axisList.append(
1202                     std::make_shared<XlsxAxis>(XlsxAxis::T_Val, XlsxAxis::Bottom, 0, 1, axisNames[XlsxAxis::Bottom] ));
1203         const_cast<ChartPrivate*>(this)->axisList.append(
1204                     std::make_shared<XlsxAxis>(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0, axisNames[XlsxAxis::Left] ));
1205     }
1206 
1207     int axisListSize = axisList.size();
1208     Q_ASSERT(axisListSize == 2);
1209 
1210     for (int i=0; i<axisList.size(); ++i)
1211     {
1212         writer.writeEmptyElement(QStringLiteral("c:axId"));
1213         writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
1214     }
1215 
1216     writer.writeEndElement(); //c:scatterChart
1217 }
1218 
1219 void ChartPrivate::saveXmlAreaChart(QXmlStreamWriter &writer) const
1220 {
1221     QString name = chartType==Chart::CT_AreaChart ? QStringLiteral("c:areaChart") : QStringLiteral("c:area3DChart");
1222 
1223     writer.writeStartElement(name);
1224 
1225     // writer.writeEmptyElement(QStringLiteral("grouping")); // dev22
1226 
1227     for (int i=0; i<seriesList.size(); ++i)
1228         saveXmlSer(writer, seriesList[i].get(), i);
1229 
1230     if (axisList.isEmpty())
1231     {
1232         const_cast<ChartPrivate*>(this)->axisList.append(std::make_shared<XlsxAxis>(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1));
1233         const_cast<ChartPrivate*>(this)->axisList.append(std::make_shared<XlsxAxis>(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0));
1234     }
1235 
1236     //Note: Area3D have 2~3 axes
1237     Q_ASSERT(axisList.size()==2 || (axisList.size()==3 && chartType==Chart::CT_Area3DChart));
1238 
1239     for (int i=0; i<axisList.size(); ++i)
1240     {
1241         writer.writeEmptyElement(QStringLiteral("c:axId"));
1242         writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
1243     }
1244 
1245     writer.writeEndElement(); //lineChart, line3DChart
1246 }
1247 
1248 void ChartPrivate::saveXmlDoughnutChart(QXmlStreamWriter &writer) const
1249 {
1250     QString name = QStringLiteral("c:doughnutChart");
1251 
1252     writer.writeStartElement(name);
1253 
1254     writer.writeEmptyElement(QStringLiteral("c:varyColors"));
1255     writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));
1256 
1257     for (int i=0; i<seriesList.size(); ++i)
1258         saveXmlSer(writer, seriesList[i].get(), i);
1259 
1260     writer.writeStartElement(QStringLiteral("c:holeSize"));
1261     writer.writeAttribute(QStringLiteral("val"), QString::number(50));
1262 
1263     writer.writeEndElement();
1264 }
1265 
1266 void ChartPrivate::saveXmlSer(QXmlStreamWriter &writer, XlsxSeries *ser, int id) const
1267 {
1268 
1269     writer.writeStartElement(QStringLiteral("c:ser"));
1270     writer.writeEmptyElement(QStringLiteral("c:idx"));
1271     writer.writeAttribute(QStringLiteral("val"), QString::number(id));
1272     writer.writeEmptyElement(QStringLiteral("c:order"));
1273     writer.writeAttribute(QStringLiteral("val"), QString::number(id));
1274 
1275     QString header1;
1276     QString header2;
1277     if( ser->swapHeader )
1278     {
1279         header1 = ser->headerH_numRef;
1280         header2 = ser->headerV_numRef;
1281     }
1282     else
1283     {
1284         header1 = ser->headerV_numRef;
1285         header2 = ser->headerH_numRef;
1286     }
1287 
1288     if( !header1.isEmpty() )
1289     {
1290         writer.writeStartElement(QStringLiteral("c:tx"));
1291         writer.writeStartElement(QStringLiteral("c:strRef"));
1292         writer.writeTextElement(QStringLiteral("c:f"), header1);
1293         writer.writeEndElement();
1294         writer.writeEndElement();
1295     }
1296     if( !header2.isEmpty() )
1297     {
1298         writer.writeStartElement(QStringLiteral("c:cat"));
1299         writer.writeStartElement(QStringLiteral("c:strRef"));
1300         writer.writeTextElement(QStringLiteral("c:f"), header2);
1301         writer.writeEndElement();
1302         writer.writeEndElement();
1303     }
1304 
1305 #if 0
1306     if (!ser->axDataSource_numRef.isEmpty())
1307     {
1308         if (chartType == Chart::CT_ScatterChart || chartType == Chart::CT_BubbleChart)
1309         {
1310             writer.writeStartElement(QStringLiteral("c:xVal"));
1311         }
1312         else
1313         {
1314             writer.writeStartElement(QStringLiteral("c:cat"));
1315         }
1316 
1317         writer.writeStartElement(QStringLiteral("c:numRef"));
1318         writer.writeTextElement(QStringLiteral("c:f"), ser->axDataSource_numRef);
1319         writer.writeEndElement();//c:numRef
1320         writer.writeEndElement();//c:cat or c:xVal
1321     }
1322 #endif
1323 
1324     if (!ser->numberDataSource_numRef.isEmpty())
1325     {
1326         if (chartType == Chart::CT_ScatterChart || chartType == Chart::CT_BubbleChart)
1327             writer.writeStartElement(QStringLiteral("c:yVal"));
1328         else
1329             writer.writeStartElement(QStringLiteral("c:val"));
1330         writer.writeStartElement(QStringLiteral("c:numRef"));
1331         writer.writeTextElement(QStringLiteral("c:f"), ser->numberDataSource_numRef);
1332         writer.writeEndElement();//c:numRef
1333         writer.writeEndElement();//c:val or c:yVal
1334     }
1335 
1336     writer.writeEndElement();//c:ser
1337 }
1338 
1339 bool ChartPrivate::loadXmlAxisCatAx(QXmlStreamReader &reader)
1340 {
1341 
1342     auto axis = std::make_shared<XlsxAxis>();
1343     axis->type = XlsxAxis::T_Cat;
1344     axisList.append(axis);
1345 
1346     // load EG_AxShared
1347     if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) )
1348     {
1349         qDebug() << "failed to load EG_AxShared";
1350         return false;
1351     }
1352 
1353     //!TODO: load element
1354     // auto
1355     // lblAlgn
1356     // lblOffset
1357     // tickLblSkip
1358     // tickMarkSkip
1359     // noMultiLvlLbl
1360     // extLst
1361 
1362     return true;
1363 }
1364 
1365 bool ChartPrivate::loadXmlAxisDateAx(QXmlStreamReader &reader)
1366 {
1367 
1368     auto axis = std::make_shared<XlsxAxis>();
1369     axis->type = XlsxAxis::T_Date;
1370     axisList.append(axis);
1371 
1372     // load EG_AxShared
1373     if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) )
1374     {
1375         qDebug() << "failed to load EG_AxShared";
1376         return false;
1377     }
1378 
1379     //!TODO: load element
1380     // auto
1381     // lblOffset
1382     // baseTimeUnit
1383     // majorUnit
1384     // majorTimeUnit
1385     // minorUnit
1386     // minorTimeUnit
1387     // extLst
1388 
1389     return true;
1390 }
1391 
1392 bool ChartPrivate::loadXmlAxisSerAx(QXmlStreamReader &reader)
1393 {
1394 
1395     auto axis = std::make_shared<XlsxAxis>();
1396     axis->type = XlsxAxis::T_Ser;
1397     axisList.append(axis);
1398 
1399     // load EG_AxShared
1400     if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) )
1401     {
1402         qDebug() << "failed to load EG_AxShared";
1403         return false;
1404     }
1405 
1406     //!TODO: load element
1407     // tickLblSkip
1408     // tickMarkSkip
1409     // extLst
1410 
1411     return true;
1412 }
1413 
1414 bool ChartPrivate::loadXmlAxisValAx(QXmlStreamReader &reader)
1415 {
1416     Q_ASSERT(reader.name() == QLatin1String("valAx"));
1417 
1418     auto axis = std::make_shared<XlsxAxis>();
1419     axis->type = XlsxAxis::T_Val;
1420     axisList.append(axis);
1421 
1422     if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) )
1423     {
1424         qDebug() << "failed to load EG_AxShared";
1425         return false;
1426     }
1427 
1428     //!TODO: load element
1429     // crossBetween
1430     // majorUnit
1431     // minorUnit
1432     // dispUnits
1433     // extLst
1434 
1435     return true;
1436 }
1437 
1438 /*
1439 <xsd:group name="EG_AxShared">
1440     <xsd:sequence>
1441         <xsd:element name="axId" type="CT_UnsignedInt" minOccurs="1" maxOccurs="1"/> (*)(M)
1442         <xsd:element name="scaling" type="CT_Scaling" minOccurs="1" maxOccurs="1"/> (*)(M)
1443         <xsd:element name="delete" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
1444         <xsd:element name="axPos" type="CT_AxPos" minOccurs="1" maxOccurs="1"/> (*)(M)
1445         <xsd:element name="majorGridlines" type="CT_ChartLines" minOccurs="0" maxOccurs="1"/>
1446         <xsd:element name="minorGridlines" type="CT_ChartLines" minOccurs="0" maxOccurs="1"/>
1447         <xsd:element name="title" type="CT_Title" minOccurs="0" maxOccurs="1"/> (*)
1448         <xsd:element name="numFmt" type="CT_NumFmt" minOccurs="0" maxOccurs="1"/>
1449         <xsd:element name="majorTickMark" type="CT_TickMark" minOccurs="0" maxOccurs="1"/>
1450         <xsd:element name="minorTickMark" type="CT_TickMark" minOccurs="0" maxOccurs="1"/>
1451         <xsd:element name="tickLblPos" type="CT_TickLblPos" minOccurs="0" maxOccurs="1"/>
1452         <xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
1453         <xsd:element name="txPr" type="a:CT_TextBody" minOccurs="0" maxOccurs="1"/>
1454         <xsd:element name="crossAx" type="CT_UnsignedInt" minOccurs="1" maxOccurs="1"/> (*)(M)
1455         <xsd:choice minOccurs="0" maxOccurs="1">
1456             <xsd:element name="crosses" type="CT_Crosses" minOccurs="1" maxOccurs="1"/>
1457             <xsd:element name="crossesAt" type="CT_Double" minOccurs="1" maxOccurs="1"/>
1458         </xsd:choice>
1459     </xsd:sequence>
1460 </xsd:group>
1461 */
1462 bool ChartPrivate::loadXmlAxisEG_AxShared(QXmlStreamReader &reader, XlsxAxis* axis)
1463 {
1464     Q_ASSERT( nullptr != axis );
1465     Q_ASSERT( reader.name().endsWith(QLatin1String("Ax")) );
1466     QString name = reader.name().toString(); //
1467 
1468     while ( !reader.atEnd() )
1469     {
1470         reader.readNextStartElement();
1471         if ( reader.tokenType() == QXmlStreamReader::StartElement )
1472         {
1473             // qDebug() << "[debug]" << QTime::currentTime() << reader.name().toString();
1474 
1475             if ( reader.name() == QLatin1String("axId") ) // mandatory element
1476             {
1477                 // dev57
1478                 uint axId = reader.attributes().value(QStringLiteral("val")).toUInt(); // for Qt5.1
1479                 axis->axisId = axId;
1480             }
1481             else if ( reader.name() == QLatin1String("scaling") )
1482             {
1483                 // mandatory element
1484 
1485                 loadXmlAxisEG_AxShared_Scaling(reader, axis);
1486             }
1487             else if ( reader.name() == QLatin1String("delete") )
1488             {
1489                 //!TODO
1490             }
1491             else if ( reader.name() == QLatin1String("axPos") )
1492             {
1493                 // mandatory element
1494 
1495                 QString axPosVal = reader.attributes().value(QLatin1String("val")).toString();
1496 
1497                 if ( axPosVal == QLatin1String("l") ) { axis->axisPos = XlsxAxis::Left; }
1498                 else if ( axPosVal == QLatin1String("r") ) { axis->axisPos = XlsxAxis::Right; }
1499                 else if ( axPosVal == QLatin1String("t") ) { axis->axisPos = XlsxAxis::Top; }
1500                 else if ( axPosVal == QLatin1String("b") ) { axis->axisPos = XlsxAxis::Bottom; }
1501             }
1502             else if ( reader.name() == QLatin1String("majorGridlines") )
1503             {
1504                 //!TODO anything else?
1505                 majorGridlinesEnabled = true;
1506             }
1507             else if ( reader.name() == QLatin1String("minorGridlines") )
1508             {
1509                 //!TODO anything else?
1510                 minorGridlinesEnabled = true;
1511             }
1512             else if ( reader.name() == QLatin1String("title") )
1513             {
1514                 // title
1515                 if ( !loadXmlAxisEG_AxShared_Title(reader, axis) )
1516                 {
1517                     qDebug() << "failed to load EG_AxShared title.";
1518                     Q_ASSERT(false);
1519                     return false;
1520                 }
1521             }
1522             else if ( reader.name() == QLatin1String("numFmt") )
1523             {
1524                 //!TODO
1525             }
1526             else if ( reader.name() == QLatin1String("majorTickMark") )
1527             {
1528                 //!TODO
1529             }
1530             else if ( reader.name() == QLatin1String("minorTickMark") )
1531             {
1532                 //!TODO
1533             }
1534             else if ( reader.name() == QLatin1String("tickLblPos") )
1535             {
1536                 //!TODO
1537             }
1538             else if ( reader.name() == QLatin1String("spPr") )
1539             {
1540                 //!TODO
1541             }
1542             else if ( reader.name() == QLatin1String("txPr") )
1543             {
1544                 //!TODO
1545             }
1546             else if ( reader.name() == QLatin1String("crossAx") ) // mandatory element
1547             {
1548                 // dev57
1549                 uint crossAx = reader.attributes().value(QLatin1String("val")).toUInt(); // for Qt5.1
1550                 axis->crossAx = crossAx;
1551             }
1552             else if ( reader.name() == QLatin1String("crosses") )
1553             {
1554                 //!TODO
1555             }
1556             else if ( reader.name() == QLatin1String("crossesAt") )
1557             {
1558                 //!TODO
1559             }
1560 
1561             // reader.readNext();
1562         }
1563         else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
1564                   reader.name().toString() == name )
1565         {
1566             break;
1567         }
1568     }
1569 
1570     return true;
1571 }
1572 
1573 bool ChartPrivate::loadXmlAxisEG_AxShared_Scaling(QXmlStreamReader &reader, XlsxAxis* axis)
1574 {
1575     Q_UNUSED(axis);
1576     Q_ASSERT(reader.name() == QLatin1String("scaling"));
1577 
1578     while ( !reader.atEnd() )
1579     {
1580         reader.readNextStartElement();
1581         if ( reader.tokenType() == QXmlStreamReader::StartElement )
1582         {
1583             if ( reader.name() == QLatin1String("orientation") )
1584             {
1585             }
1586             else
1587             {
1588             }
1589         }
1590         else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
1591                   reader.name() == QLatin1String("scaling") )
1592         {
1593             break;
1594         }
1595     }
1596 
1597     return true;
1598 }
1599 
1600 /*
1601   <xsd:complexType name="CT_Title">
1602       <xsd:sequence>
1603           <xsd:element name="tx" type="CT_Tx" minOccurs="0" maxOccurs="1"/>
1604           <xsd:element name="layout" type="CT_Layout" minOccurs="0" maxOccurs="1"/>
1605           <xsd:element name="overlay" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
1606           <xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
1607           <xsd:element name="txPr" type="a:CT_TextBody" minOccurs="0" maxOccurs="1"/>
1608           <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
1609       </xsd:sequence>
1610   </xsd:complexType>
1611 
1612 <xsd:complexType name="CT_Tx">
1613     <xsd:sequence>
1614         <xsd:choice minOccurs="1" maxOccurs="1">
1615             <xsd:element name="strRef" type="CT_StrRef" minOccurs="1" maxOccurs="1"/>
1616             <xsd:element name="rich" type="a:CT_TextBody" minOccurs="1" maxOccurs="1"/>
1617         </xsd:choice>
1618     </xsd:sequence>
1619 </xsd:complexType>
1620 
1621 <xsd:complexType name="CT_StrRef">
1622     <xsd:sequence>
1623         <xsd:element name="f" type="xsd:string" minOccurs="1" maxOccurs="1"/>
1624         <xsd:element name="strCache" type="CT_StrData" minOccurs="0" maxOccurs="1"/>
1625         <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
1626     </xsd:sequence>
1627 </xsd:complexType>
1628 
1629 <xsd:complexType name="CT_TextBody">
1630     <xsd:sequence>
1631         <xsd:element name="bodyPr" type="CT_TextBodyProperties" minOccurs="1" maxOccurs="1"/>
1632         <xsd:element name="lstStyle" type="CT_TextListStyle" minOccurs="0" maxOccurs="1"/>
1633         <xsd:element name="p" type="CT_TextParagraph" minOccurs="1" maxOccurs="unbounded"/>
1634     </xsd:sequence>
1635 </xsd:complexType>
1636   */
1637 bool ChartPrivate::loadXmlAxisEG_AxShared_Title(QXmlStreamReader &reader, XlsxAxis* axis)
1638 {
1639     Q_ASSERT(reader.name() == QLatin1String("title"));
1640 
1641     while ( !reader.atEnd() )
1642     {
1643         reader.readNextStartElement();
1644         if ( reader.tokenType() == QXmlStreamReader::StartElement )
1645         {
1646             if ( reader.name() == QLatin1String("tx") )
1647             {
1648                 loadXmlAxisEG_AxShared_Title_Tx(reader, axis);
1649             }
1650             else if ( reader.name() == QLatin1String("overlay") )
1651             {
1652                 //!TODO: load overlay
1653                 loadXmlAxisEG_AxShared_Title_Overlay(reader, axis);
1654             }
1655             else
1656             {
1657             }
1658         }
1659         else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
1660                   reader.name() == QLatin1String("title") )
1661         {
1662             break;
1663         }
1664     }
1665 
1666     return true;
1667 }
1668 
1669 bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Overlay(QXmlStreamReader &reader, XlsxAxis* axis)
1670 {
1671     Q_UNUSED(axis);
1672     Q_ASSERT(reader.name() == QLatin1String("overlay"));
1673 
1674     while ( !reader.atEnd() )
1675     {
1676         reader.readNextStartElement();
1677         if ( reader.tokenType() == QXmlStreamReader::StartElement )
1678         {
1679         }
1680         else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
1681                   reader.name() == QLatin1String("overlay") )
1682         {
1683             break;
1684         }
1685     }
1686 
1687     return true;
1688 }
1689 
1690 bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx(QXmlStreamReader &reader, XlsxAxis* axis)
1691 {
1692     Q_ASSERT(reader.name() == QLatin1String("tx"));
1693 
1694     while ( !reader.atEnd() )
1695     {
1696         reader.readNextStartElement();
1697         if ( reader.tokenType() == QXmlStreamReader::StartElement )
1698         {
1699             if ( reader.name() == QLatin1String("rich") )
1700             {
1701                 loadXmlAxisEG_AxShared_Title_Tx_Rich(reader, axis);
1702             }
1703             else
1704             {
1705             }
1706         }
1707         else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
1708                   reader.name() == QLatin1String("tx") )
1709         {
1710             break;
1711         }
1712     }
1713 
1714     return true;
1715 }
1716 
1717 bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich(QXmlStreamReader &reader, XlsxAxis* axis)
1718 {
1719     Q_ASSERT(reader.name() == QLatin1String("rich"));
1720 
1721     while ( !reader.atEnd() )
1722     {
1723         reader.readNextStartElement();
1724         if ( reader.tokenType() == QXmlStreamReader::StartElement )
1725         {
1726             if ( reader.name() == QLatin1String("p") )
1727             {
1728                 loadXmlAxisEG_AxShared_Title_Tx_Rich_P(reader, axis);
1729             }
1730             else
1731             {
1732             }
1733         }
1734         else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
1735                   reader.name() == QLatin1String("rich") )
1736         {
1737             break;
1738         }
1739     }
1740 
1741     return true;
1742 }
1743 
1744 bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich_P(QXmlStreamReader &reader, XlsxAxis* axis)
1745 {
1746     Q_ASSERT(reader.name() == QLatin1String("p"));
1747 
1748     while ( !reader.atEnd() )
1749     {
1750         reader.readNextStartElement();
1751         if ( reader.tokenType() == QXmlStreamReader::StartElement )
1752         {
1753             if ( reader.name() == QLatin1String("r") )
1754             {
1755                 loadXmlAxisEG_AxShared_Title_Tx_Rich_P_R(reader, axis);
1756             }
1757             else if ( reader.name() == QLatin1String("pPr") )
1758             {
1759                 loadXmlAxisEG_AxShared_Title_Tx_Rich_P_pPr(reader, axis);
1760             }
1761             else
1762             {
1763 
1764             }
1765         }
1766         else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
1767                   reader.name() == QLatin1String("p") )
1768         {
1769             break;
1770         }
1771     }
1772 
1773     return true;
1774 }
1775 
1776 bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich_P_pPr(QXmlStreamReader &reader, XlsxAxis* axis)
1777 {
1778     Q_UNUSED(axis);
1779     Q_ASSERT(reader.name() == QLatin1String("pPr"));
1780 
1781     while ( !reader.atEnd() )
1782     {
1783         reader.readNextStartElement();
1784         if ( reader.tokenType() == QXmlStreamReader::StartElement )
1785         {
1786             if ( reader.name() == QLatin1String("defRPr") )
1787             {
1788                 reader.readElementText();
1789             }
1790             else
1791             {
1792             }
1793         }
1794         else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
1795                   reader.name() == QLatin1String("pPr") )
1796         {
1797             break;
1798         }
1799     }
1800 
1801     return true;
1802 }
1803 
1804 bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich_P_R(QXmlStreamReader &reader, XlsxAxis* axis)
1805 {
1806     Q_ASSERT(reader.name() == QLatin1String("r"));
1807 
1808     while ( !reader.atEnd() )
1809     {
1810         reader.readNextStartElement();
1811         if ( reader.tokenType() == QXmlStreamReader::StartElement )
1812         {
1813             if ( reader.name() == QLatin1String("t") )
1814             {
1815                 QString strAxisName = reader.readElementText();
1816                 XlsxAxis::AxisPos axisPos = axis->axisPos;
1817                 axis->axisNames[ axisPos ] = strAxisName;
1818             }
1819             else
1820             {
1821             }
1822         }
1823         else if ( reader.tokenType() == QXmlStreamReader::EndElement &&
1824                   reader.name() == QLatin1String("r") )
1825         {
1826             break;
1827         }
1828     }
1829 
1830     return true;
1831 }
1832 
1833 /*
1834 <xsd:complexType name="CT_PlotArea">
1835     <xsd:sequence>
1836         <xsd:element name="layout" type="CT_Layout" minOccurs="0" maxOccurs="1"/>
1837         <xsd:choice minOccurs="1" maxOccurs="unbounded">
1838             <xsd:element name="areaChart" type="CT_AreaChart" minOccurs="1" maxOccurs="1"/>
1839             <xsd:element name="area3DChart" type="CT_Area3DChart" minOccurs="1" maxOccurs="1"/>
1840             <xsd:element name="lineChart" type="CT_LineChart" minOccurs="1" maxOccurs="1"/>
1841             <xsd:element name="line3DChart" type="CT_Line3DChart" minOccurs="1" maxOccurs="1"/>
1842             <xsd:element name="stockChart" type="CT_StockChart" minOccurs="1" maxOccurs="1"/>
1843             <xsd:element name="radarChart" type="CT_RadarChart" minOccurs="1" maxOccurs="1"/>
1844             <xsd:element name="scatterChart" type="CT_ScatterChart" minOccurs="1" maxOccurs="1"/>
1845             <xsd:element name="pieChart" type="CT_PieChart" minOccurs="1" maxOccurs="1"/>
1846             <xsd:element name="pie3DChart" type="CT_Pie3DChart" minOccurs="1" maxOccurs="1"/>
1847             <xsd:element name="doughnutChart" type="CT_DoughnutChart" minOccurs="1" maxOccurs="1"/>
1848             <xsd:element name="barChart" type="CT_BarChart" minOccurs="1" maxOccurs="1"/>
1849             <xsd:element name="bar3DChart" type="CT_Bar3DChart" minOccurs="1" maxOccurs="1"/>
1850             <xsd:element name="ofPieChart" type="CT_OfPieChart" minOccurs="1" maxOccurs="1"/>
1851             <xsd:element name="surfaceChart" type="CT_SurfaceChart" minOccurs="1" maxOccurs="1"/>
1852             <xsd:element name="surface3DChart" type="CT_Surface3DChart" minOccurs="1" maxOccurs="1"/>
1853             <xsd:element name="bubbleChart" type="CT_BubbleChart" minOccurs="1" maxOccurs="1"/>
1854         </xsd:choice>
1855         <xsd:choice minOccurs="0" maxOccurs="unbounded">
1856             <xsd:element name="valAx" type="CT_ValAx" minOccurs="1" maxOccurs="1"/>
1857             <xsd:element name="catAx" type="CT_CatAx" minOccurs="1" maxOccurs="1"/>
1858             <xsd:element name="dateAx" type="CT_DateAx" minOccurs="1" maxOccurs="1"/>
1859             <xsd:element name="serAx" type="CT_SerAx" minOccurs="1" maxOccurs="1"/>
1860         </xsd:choice>
1861         <xsd:element name="dTable" type="CT_DTable" minOccurs="0" maxOccurs="1"/>
1862         <xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
1863         <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
1864     </xsd:sequence>
1865 </xsd:complexType>
1866 */
1867 
1868 /*
1869 <xsd:complexType name="CT_CatAx">
1870     <xsd:sequence>
1871         <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
1872         <xsd:element name="auto" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
1873         <xsd:element name="lblAlgn" type="CT_LblAlgn" minOccurs="0" maxOccurs="1"/>
1874         <xsd:element name="lblOffset" type="CT_LblOffset" minOccurs="0" maxOccurs="1"/>
1875         <xsd:element name="tickLblSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
1876         <xsd:element name="tickMarkSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
1877         <xsd:element name="noMultiLvlLbl" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
1878         <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
1879     </xsd:sequence>
1880 </xsd:complexType>
1881 <!----------------------------------------------------------------------------->
1882 <xsd:complexType name="CT_DateAx">
1883     <xsd:sequence>
1884         <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
1885         <xsd:element name="auto" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
1886         <xsd:element name="lblOffset" type="CT_LblOffset" minOccurs="0" maxOccurs="1"/>
1887         <xsd:element name="baseTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
1888         <xsd:element name="majorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
1889         <xsd:element name="majorTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
1890         <xsd:element name="minorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
1891         <xsd:element name="minorTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
1892         <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
1893     </xsd:sequence>
1894 </xsd:complexType>
1895 <!----------------------------------------------------------------------------->
1896 <xsd:complexType name="CT_SerAx">
1897     <xsd:sequence>
1898     <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
1899         <xsd:element name="tickLblSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
1900         <xsd:element name="tickMarkSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
1901         <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
1902     </xsd:sequence>
1903 </xsd:complexType>
1904 <!----------------------------------------------------------------------------->
1905 <xsd:complexType name="CT_ValAx">
1906     <xsd:sequence>
1907         <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
1908         <xsd:element name="crossBetween" type="CT_CrossBetween" minOccurs="0" maxOccurs="1"/>
1909         <xsd:element name="majorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
1910         <xsd:element name="minorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
1911         <xsd:element name="dispUnits" type="CT_DispUnits" minOccurs="0" maxOccurs="1"/>
1912         <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
1913     </xsd:sequence>
1914 </xsd:complexType>
1915 */
1916 
1917 void ChartPrivate::saveXmlAxis(QXmlStreamWriter &writer) const
1918 {
1919     for ( int i = 0 ; i < axisList.size() ; ++i )
1920     {
1921         XlsxAxis* axis = axisList[i].get();
1922         if ( nullptr == axis )
1923             continue;
1924 
1925         if ( axis->type == XlsxAxis::T_Cat  ) { saveXmlAxisCatAx( writer, axis ); }
1926         if ( axis->type == XlsxAxis::T_Val  ) { saveXmlAxisValAx( writer, axis ); }
1927         if ( axis->type == XlsxAxis::T_Ser  ) { saveXmlAxisSerAx( writer, axis ); }
1928         if ( axis->type == XlsxAxis::T_Date ) { saveXmlAxisDateAx( writer, axis ); }
1929     }
1930 
1931 }
1932 
1933 void ChartPrivate::saveXmlAxisCatAx(QXmlStreamWriter &writer, XlsxAxis* axis) const
1934 {
1935 /*
1936 <xsd:complexType name="CT_CatAx">
1937     <xsd:sequence>
1938         <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
1939         <xsd:element name="auto" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
1940         <xsd:element name="lblAlgn" type="CT_LblAlgn" minOccurs="0" maxOccurs="1"/>
1941         <xsd:element name="lblOffset" type="CT_LblOffset" minOccurs="0" maxOccurs="1"/>
1942         <xsd:element name="tickLblSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
1943         <xsd:element name="tickMarkSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
1944         <xsd:element name="noMultiLvlLbl" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
1945         <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
1946     </xsd:sequence>
1947 </xsd:complexType>
1948 */
1949 
1950     writer.writeStartElement(QStringLiteral("c:catAx"));
1951 
1952     saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared
1953 
1954     //!TODO: write element
1955     // auto
1956     // lblAlgn
1957     // lblOffset
1958     // tickLblSkip
1959     // tickMarkSkip
1960     // noMultiLvlLbl
1961     // extLst
1962 
1963     writer.writeEndElement(); // c:catAx
1964 }
1965 
1966 void ChartPrivate::saveXmlAxisDateAx(QXmlStreamWriter &writer, XlsxAxis* axis) const
1967 {
1968 /*
1969 <xsd:complexType name="CT_DateAx">
1970     <xsd:sequence>
1971         <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
1972         <xsd:element name="auto" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
1973         <xsd:element name="lblOffset" type="CT_LblOffset" minOccurs="0" maxOccurs="1"/>
1974         <xsd:element name="baseTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
1975         <xsd:element name="majorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
1976         <xsd:element name="majorTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
1977         <xsd:element name="minorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
1978         <xsd:element name="minorTimeUnit" type="CT_TimeUnit" minOccurs="0" maxOccurs="1"/>
1979         <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
1980     </xsd:sequence>
1981 </xsd:complexType>
1982 */
1983 
1984     writer.writeStartElement(QStringLiteral("c:dateAx"));
1985 
1986     saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared
1987 
1988     //!TODO: write element
1989     // auto
1990     // lblOffset
1991     // baseTimeUnit
1992     // majorUnit
1993     // majorTimeUnit
1994     // minorUnit
1995     // minorTimeUnit
1996     // extLst
1997 
1998     writer.writeEndElement(); // c:dateAx
1999 }
2000 
2001 void ChartPrivate::saveXmlAxisSerAx(QXmlStreamWriter &writer, XlsxAxis* axis) const
2002 {
2003 /*
2004 <xsd:complexType name="CT_SerAx">
2005     <xsd:sequence>
2006     <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
2007         <xsd:element name="tickLblSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
2008         <xsd:element name="tickMarkSkip" type="CT_Skip" minOccurs="0" maxOccurs="1"/>
2009         <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
2010     </xsd:sequence>
2011 </xsd:complexType>
2012 */
2013 
2014     writer.writeStartElement(QStringLiteral("c:serAx"));
2015 
2016     saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared
2017 
2018     //!TODO: write element
2019     // tickLblSkip
2020     // tickMarkSkip
2021     // extLst
2022 
2023     writer.writeEndElement(); // c:serAx
2024 }
2025 
2026 void ChartPrivate::saveXmlAxisValAx(QXmlStreamWriter &writer, XlsxAxis* axis) const
2027 {
2028 /*
2029 <xsd:complexType name="CT_ValAx">
2030     <xsd:sequence>
2031         <xsd:group ref="EG_AxShared" minOccurs="1" maxOccurs="1"/>
2032         <xsd:element name="crossBetween" type="CT_CrossBetween" minOccurs="0" maxOccurs="1"/>
2033         <xsd:element name="majorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
2034         <xsd:element name="minorUnit" type="CT_AxisUnit" minOccurs="0" maxOccurs="1"/>
2035         <xsd:element name="dispUnits" type="CT_DispUnits" minOccurs="0" maxOccurs="1"/>
2036         <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
2037     </xsd:sequence>
2038 </xsd:complexType>
2039 */
2040 
2041     writer.writeStartElement(QStringLiteral("c:valAx"));
2042 
2043     saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared
2044 
2045     //!TODO: write element
2046     // crossBetween
2047     // majorUnit
2048     // minorUnit
2049     // dispUnits
2050     // extLst
2051 
2052     writer.writeEndElement(); // c:valAx
2053 }
2054 
2055 void ChartPrivate::saveXmlAxisEG_AxShared(QXmlStreamWriter &writer, XlsxAxis* axis) const
2056 {
2057     /*
2058     <xsd:group name="EG_AxShared">
2059         <xsd:sequence>
2060             <xsd:element name="axId" type="CT_UnsignedInt" minOccurs="1" maxOccurs="1"/> (*)
2061             <xsd:element name="scaling" type="CT_Scaling" minOccurs="1" maxOccurs="1"/> (*)
2062             <xsd:element name="delete" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
2063             <xsd:element name="axPos" type="CT_AxPos" minOccurs="1" maxOccurs="1"/> (*)
2064             <xsd:element name="majorGridlines" type="CT_ChartLines" minOccurs="0" maxOccurs="1"/>
2065             <xsd:element name="minorGridlines" type="CT_ChartLines" minOccurs="0" maxOccurs="1"/>
2066             <xsd:element name="title" type="CT_Title" minOccurs="0" maxOccurs="1"/> (***********************)
2067             <xsd:element name="numFmt" type="CT_NumFmt" minOccurs="0" maxOccurs="1"/>
2068             <xsd:element name="majorTickMark" type="CT_TickMark" minOccurs="0" maxOccurs="1"/>
2069             <xsd:element name="minorTickMark" type="CT_TickMark" minOccurs="0" maxOccurs="1"/>
2070             <xsd:element name="tickLblPos" type="CT_TickLblPos" minOccurs="0" maxOccurs="1"/>
2071             <xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
2072             <xsd:element name="txPr" type="a:CT_TextBody" minOccurs="0" maxOccurs="1"/>
2073             <xsd:element name="crossAx" type="CT_UnsignedInt" minOccurs="1" maxOccurs="1"/> (*)
2074             <xsd:choice minOccurs="0" maxOccurs="1">
2075                 <xsd:element name="crosses" type="CT_Crosses" minOccurs="1" maxOccurs="1"/>
2076                 <xsd:element name="crossesAt" type="CT_Double" minOccurs="1" maxOccurs="1"/>
2077             </xsd:choice>
2078         </xsd:sequence>
2079     </xsd:group>
2080     */
2081 
2082     writer.writeEmptyElement(QStringLiteral("c:axId")); // 21.2.2.9. axId (Axis ID) (mandatory value)
2083         writer.writeAttribute(QStringLiteral("val"), QString::number(axis->axisId));
2084 
2085     writer.writeStartElement(QStringLiteral("c:scaling")); // CT_Scaling (mandatory value)
2086         writer.writeEmptyElement(QStringLiteral("c:orientation")); // CT_Orientation
2087             writer.writeAttribute(QStringLiteral("val"), QStringLiteral("minMax")); // ST_Orientation
2088     writer.writeEndElement(); // c:scaling
2089 
2090     writer.writeEmptyElement(QStringLiteral("c:axPos")); // axPos CT_AxPos (mandatory value)
2091         QString pos = GetAxisPosString( axis->axisPos );
2092         if ( !pos.isEmpty() )
2093         {
2094             writer.writeAttribute(QStringLiteral("val"), pos); // ST_AxPos
2095         }
2096 
2097     if( majorGridlinesEnabled )
2098     {
2099         writer.writeEmptyElement(QStringLiteral("c:majorGridlines"));
2100     }
2101     if( minorGridlinesEnabled )
2102     {
2103         writer.writeEmptyElement(QStringLiteral("c:minorGridlines"));
2104     }
2105 
2106     saveXmlAxisEG_AxShared_Title(writer, axis); // "c:title" CT_Title
2107 
2108     writer.writeEmptyElement(QStringLiteral("c:crossAx")); // crossAx (mandatory value)
2109         writer.writeAttribute(QStringLiteral("val"), QString::number(axis->crossAx));
2110 
2111 }
2112 
2113 void ChartPrivate::saveXmlAxisEG_AxShared_Title(QXmlStreamWriter &writer, XlsxAxis* axis) const
2114 {
2115     // CT_Title
2116 
2117     /*
2118     <xsd:complexType name="CT_Title">
2119         <xsd:sequence>
2120             <xsd:element name="tx" type="CT_Tx" minOccurs="0" maxOccurs="1"/>
2121             <xsd:element name="layout" type="CT_Layout" minOccurs="0" maxOccurs="1"/>
2122             <xsd:element name="overlay" type="CT_Boolean" minOccurs="0" maxOccurs="1"/>
2123             <xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="0" maxOccurs="1"/>
2124             <xsd:element name="txPr" type="a:CT_TextBody" minOccurs="0" maxOccurs="1"/>
2125             <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
2126         </xsd:sequence>
2127     </xsd:complexType>
2128     */
2129     /*
2130     <xsd:complexType name="CT_Tx">
2131         <xsd:sequence>
2132             <xsd:choice minOccurs="1" maxOccurs="1">
2133                 <xsd:element name="strRef" type="CT_StrRef" minOccurs="1" maxOccurs="1"/>
2134                 <xsd:element name="rich" type="a:CT_TextBody" minOccurs="1" maxOccurs="1"/>
2135             </xsd:choice>
2136         </xsd:sequence>
2137     </xsd:complexType>
2138     */
2139     /*
2140     <xsd:complexType name="CT_StrRef">
2141         <xsd:sequence>
2142             <xsd:element name="f" type="xsd:string" minOccurs="1" maxOccurs="1"/>
2143             <xsd:element name="strCache" type="CT_StrData" minOccurs="0" maxOccurs="1"/>
2144             <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/>
2145         </xsd:sequence>
2146     </xsd:complexType>
2147     */
2148     /*
2149     <xsd:complexType name="CT_TextBody">
2150         <xsd:sequence>
2151             <xsd:element name="bodyPr" type="CT_TextBodyProperties" minOccurs="1" maxOccurs="1"/>
2152             <xsd:element name="lstStyle" type="CT_TextListStyle" minOccurs="0" maxOccurs="1"/>
2153             <xsd:element name="p" type="CT_TextParagraph" minOccurs="1" maxOccurs="unbounded"/>
2154         </xsd:sequence>
2155     </xsd:complexType>
2156     */
2157 
2158     writer.writeStartElement(QStringLiteral("c:title"));
2159 
2160     // CT_Tx {{
2161      writer.writeStartElement(QStringLiteral("c:tx"));
2162 
2163       writer.writeStartElement(QStringLiteral("c:rich")); // CT_TextBody
2164 
2165        writer.writeEmptyElement(QStringLiteral("a:bodyPr")); // CT_TextBodyProperties
2166 
2167        writer.writeEmptyElement(QStringLiteral("a:lstStyle")); // CT_TextListStyle
2168 
2169        writer.writeStartElement(QStringLiteral("a:p"));
2170 
2171         writer.writeStartElement(QStringLiteral("a:pPr"));
2172             writer.writeAttribute(QStringLiteral("lvl"), QString::number(0));
2173 
2174             writer.writeStartElement(QStringLiteral("a:defRPr"));
2175             writer.writeAttribute(QStringLiteral("b"), QString::number(0));
2176             writer.writeEndElement(); // a:defRPr
2177         writer.writeEndElement(); // a:pPr
2178 
2179         writer.writeStartElement(QStringLiteral("a:r"));
2180         QString strAxisName = GetAxisName(axis);
2181         writer.writeTextElement( QStringLiteral("a:t"), strAxisName );
2182         writer.writeEndElement(); // a:r
2183 
2184        writer.writeEndElement(); // a:p
2185 
2186       writer.writeEndElement(); // c:rich
2187 
2188      writer.writeEndElement(); // c:tx
2189      // CT_Tx }}
2190 
2191      writer.writeStartElement(QStringLiteral("c:overlay"));
2192         writer.writeAttribute(QStringLiteral("val"), QString::number(0)); // CT_Boolean
2193      writer.writeEndElement(); // c:overlay
2194 
2195     writer.writeEndElement(); // c:title
2196 
2197 }
2198 
2199 QString ChartPrivate::GetAxisPosString( XlsxAxis::AxisPos axisPos ) const
2200 {
2201     QString pos;
2202     switch ( axisPos )
2203     {
2204         case XlsxAxis::Top    : pos = QStringLiteral("t"); break;
2205         case XlsxAxis::Bottom : pos = QStringLiteral("b"); break;
2206         case XlsxAxis::Left   : pos = QStringLiteral("l"); break;
2207         case XlsxAxis::Right  : pos = QStringLiteral("r"); break;
2208         default: break; // ??
2209     }
2210 
2211     return pos;
2212 }
2213 
2214 QString ChartPrivate::GetAxisName(XlsxAxis* axis) const
2215 {
2216     QString strAxisName;
2217     if ( nullptr == axis )
2218         return strAxisName;
2219 
2220     QString pos = GetAxisPosString( axis->axisPos ); // l, t, r, b
2221     if ( pos.isEmpty() )
2222         return strAxisName;
2223 
2224     strAxisName = axis->axisNames[ axis->axisPos ];
2225     return strAxisName;
2226 }
2227 
2228 
2229 ///
2230 /// \brief ChartPrivate::readSubTree
2231 /// \param reader
2232 /// \return
2233 ///
2234 QString ChartPrivate::readSubTree(QXmlStreamReader &reader)
2235 {
2236     QString treeString;
2237     QString prefix;
2238     const auto& treeName = reader.name();
2239 
2240     while (!reader.atEnd())
2241     {
2242         reader.readNextStartElement();
2243         if (reader.tokenType() == QXmlStreamReader::StartElement)
2244         {
2245             prefix = reader.prefix().toString();
2246 
2247             treeString += QLatin1String("<") + reader.qualifiedName().toString();
2248 
2249             const QXmlStreamAttributes attributes = reader.attributes();
2250             for (const QXmlStreamAttribute &attr : attributes) {
2251                 treeString += QLatin1String(" ") + attr.name().toString() + QLatin1String("=\"") + attr.value().toString() + QLatin1String("\"");
2252             }
2253             treeString += QStringLiteral(">");
2254         }
2255         else if (reader.tokenType() == QXmlStreamReader::EndElement )
2256         {
2257             if( reader.name() == treeName)
2258             {
2259                 break;
2260             }
2261             treeString += QLatin1String("</") + reader.qualifiedName().toString() + QLatin1String(">");
2262         }
2263     }
2264 
2265     return treeString;
2266 }
2267 
2268 
2269 ///
2270 /// \brief ChartPrivate::loadXmlChartLegend
2271 /// \param reader
2272 /// \return
2273 ///
2274 bool ChartPrivate::loadXmlChartLegend(QXmlStreamReader &reader)
2275 {
2276 
2277     Q_ASSERT(reader.name() == QLatin1String("legend"));
2278 
2279     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
2280                                 && reader.name() == QLatin1String("legend")))
2281     {
2282         if (reader.readNextStartElement())
2283         {
2284             if (reader.name() == QLatin1String("legendPos")) // c:legendPos
2285             {
2286                 QString pos = reader.attributes().value(QLatin1String("val")).toString();
2287                 if( pos.compare(QLatin1String("r"), Qt::CaseInsensitive) == 0)
2288                 {
2289                     // legendPos = Chart::ChartAxisPos::Right;
2290                     legendPos = Chart::Right;
2291                 }
2292                 else
2293                 if( pos.compare(QLatin1String("l"), Qt::CaseInsensitive) == 0)
2294                 {
2295                     // legendPos = Chart::ChartAxisPos::Left;
2296                     legendPos = Chart::Left;
2297                 }
2298                 else
2299                 if( pos.compare(QLatin1String("t"), Qt::CaseInsensitive) == 0)
2300                 {
2301                     // legendPos = Chart::ChartAxisPos::Top;
2302                     legendPos = Chart::Top;
2303                 }
2304                 else
2305                 if( pos.compare(QLatin1String("b"), Qt::CaseInsensitive) == 0)
2306                 {
2307                     // legendPos = Chart::ChartAxisPos::Bottom;
2308                     legendPos = Chart::Bottom;
2309                 }
2310                 else
2311                 {
2312                     // legendPos = Chart::ChartAxisPos::None;
2313                     legendPos = Chart::None;
2314                 }
2315             }
2316             else
2317             if (reader.name() == QLatin1String("overlay")) // c:legendPos
2318             {
2319                 QString pos = reader.attributes().value(QLatin1String("val")).toString();
2320                 if( pos.compare(QLatin1String("1"), Qt::CaseInsensitive) == 0 )
2321                 {
2322                     legendOverlay = true;
2323                 }
2324                 else
2325                 {
2326                     legendOverlay = false;
2327                 }
2328             }
2329         }
2330     }
2331 
2332     return false;
2333 }
2334 
2335 QT_END_NAMESPACE_XLSX