File indexing completed on 2024-05-12 16:33:26

0001 /* This file is part of the KDE project
0002 
0003    Copyright 2007 Johannes Simon <johannes.simon@gmail.com>
0004    Copyright 2009 Inge Wallin    <inge@lysator.liu.se>
0005    Copyright 2017 Dag Andersen   <danders@get2net.dk>
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020    Boston, MA 02110-1301, USA.
0021 */
0022 
0023 // Own
0024 #include "Axis.h"
0025 
0026 // Qt
0027 #include <QList>
0028 #include <QString>
0029 #include <QTextDocument>
0030 #include <QPointer>
0031 
0032 // Calligra
0033 #include <KoShapeLoadingContext.h>
0034 #include <KoShapeSavingContext.h>
0035 #include <KoShapeRegistry.h>
0036 #include <KoXmlReader.h>
0037 #include <KoXmlWriter.h>
0038 #include <KoGenStyles.h>
0039 #include <KoXmlNS.h>
0040 #include <KoTextShapeData.h>
0041 #include <KoOdfStylesReader.h>
0042 #include <KoUnit.h>
0043 #include <KoStyleStack.h>
0044 #include <KoOdfLoadingContext.h>
0045 #include <KoCharacterStyle.h>
0046 #include <KoOdfGraphicStyles.h>
0047 #include <KoOdfWorkaround.h>
0048 #include <KoTextDocumentLayout.h>
0049 #include <KoOdfNumberStyles.h>
0050 
0051 // KChart
0052 #include <KChartChart>
0053 #include <KChartLegend>
0054 #include <KChartCartesianAxis>
0055 #include <KChartCartesianCoordinatePlane>
0056 #include <KChartRadarCoordinatePlane>
0057 #include <KChartGridAttributes>
0058 #include <KChartBarDiagram>
0059 #include <KChartLineDiagram>
0060 #include <KChartPieDiagram>
0061 #include <KChartPlotter>
0062 #include <KChartStockDiagram>
0063 #include <KChartRingDiagram>
0064 #include <KChartRadarDiagram>
0065 #include <KChartBarAttributes>
0066 #include <KChartPieAttributes>
0067 #include <KChartThreeDBarAttributes>
0068 #include <KChartThreeDPieAttributes>
0069 #include <KChartThreeDLineAttributes>
0070 #include <KChartBackgroundAttributes>
0071 #include <KChartRulerAttributes>
0072 #include <kchart_version.h>
0073 
0074 // KoChart
0075 #include "PlotArea.h"
0076 #include "KChartModel.h"
0077 #include "DataSet.h"
0078 #include "Legend.h"
0079 #include "KChartConvertions.h"
0080 #include "ChartProxyModel.h"
0081 #include "TextLabelDummy.h"
0082 #include "ChartLayout.h"
0083 #include "OdfLoadingHelper.h"
0084 #include "OdfHelper.h"
0085 #include "ChartDebug.h"
0086 
0087 
0088 using namespace KoChart;
0089 
0090 class Axis::Private
0091 {
0092 public:
0093     Private(Axis *axis, AxisDimension dim);
0094     ~Private();
0095 
0096     void adjustAllDiagrams();
0097     /// Updates the axis position in the chart's layout
0098     /// FIXME: We should instead implement a generic layout position method
0099     /// and have the layout find out about our position when it changes.
0100     void updatePosition();
0101 
0102     void registerDiagram(KChart::AbstractDiagram *diagram);
0103 
0104     KChart::AbstractDiagram *getDiagramAndCreateIfNeeded(ChartType chartType);
0105     KChart::AbstractDiagram *getDiagram(ChartType chartType);
0106     void deleteDiagram(ChartType chartType);
0107     void deleteDiagram(KChart::AbstractDiagram *diagram);
0108 
0109     void restoreDiagrams();
0110 
0111     void createBarDiagram();
0112     void createLineDiagram();
0113     void createAreaDiagram();
0114     void createCircleDiagram();
0115     void createRingDiagram();
0116     void createRadarDiagram(bool filled);
0117     void createScatterDiagram();
0118     void createStockDiagram();
0119     void createBubbleDiagram();
0120     void createSurfaceDiagram();
0121     void createGanttDiagram();
0122     void applyAttributesToDataSet(DataSet* set, ChartType newCharttype);
0123 
0124     // Pointer to Axis that owns this Private instance
0125     Axis * const q;
0126 
0127     PlotArea *plotArea;
0128 
0129     const AxisDimension dimension;
0130 
0131     KoShape *title;
0132     TextLabelData *titleData;
0133 
0134     /// FIXME: Unused variable 'id', including id() getter
0135     QString id;
0136     QList<DataSet*> dataSets;
0137     qreal majorInterval;
0138     int minorIntervalDivisor;
0139     bool showInnerMinorTicks;
0140     bool showOuterMinorTicks;
0141     bool showInnerMajorTicks;
0142     bool showOuterMajorTicks;
0143     bool logarithmicScaling;
0144     bool showMajorGrid;
0145     bool showMinorGrid;
0146     bool useAutomaticMajorInterval;
0147     bool useAutomaticMinorInterval;
0148     bool useAutomaticMinimumRange;
0149     bool useAutomaticMaximumRange;
0150 
0151     KChart::CartesianAxis            *const kdAxis;
0152     KChart::CartesianCoordinatePlane *kdPlane;
0153     KChart::PolarCoordinatePlane     *kdPolarPlane;
0154     KChart::RadarCoordinatePlane     *kdRadarPlane;
0155     KoOdfNumberStyles::NumericStyleFormat *numericStyleFormat;
0156 
0157     QList<QPointer<KChart::AbstractCartesianDiagram> > diagrams;
0158 
0159     QPointer<KChart::BarDiagram> kdBarDiagram;
0160     QPointer<KChart::LineDiagram> kdLineDiagram;
0161     QPointer<KChart::LineDiagram> kdAreaDiagram;
0162     QPointer<KChart::PieDiagram>  kdCircleDiagram;
0163     QPointer<KChart::RingDiagram>  kdRingDiagram;
0164     QPointer<KChart::RadarDiagram> kdRadarDiagram;
0165     QPointer<KChart::Plotter>     kdScatterDiagram;
0166     QPointer<KChart::StockDiagram> kdStockDiagram;
0167     QPointer<KChart::Plotter>  kdBubbleDiagram;
0168     // FIXME BUG: Somehow we need to visualize something for these
0169     //            missing chart types.  We have some alternatives:
0170     //            1. Show an empty area
0171     //            2. Show a text "unsupported chart type"
0172     //            3. Exchange for something else, e.g. a bar chart.
0173     //            ... More?
0174     //
0175     // NOTE: Whatever we do, we should always store the data so that
0176     //       it can be saved back into the file for a perfect
0177     //       roundtrip.
0178     QPointer<KChart::BarDiagram> kdSurfaceDiagram;
0179     QPointer<KChart::BarDiagram> kdGanttDiagram;
0180 
0181     ChartType     plotAreaChartType;
0182     ChartSubtype  plotAreaChartSubType;
0183 
0184     // If KChart::LineDiagram::centerDataPoints() property is set to true,
0185     // the data points drawn in a line (i.e., also an area) diagram start at
0186     // an offset of 0.5, that is, in the middle of a column in the diagram.
0187     // Set flag to true if at least one dataset is attached to this axis
0188     // that belongs to a horizontal bar chart
0189     bool centerDataPoints;
0190 
0191     int gapBetweenBars;
0192     int gapBetweenSets;
0193 
0194     // TODO: Save
0195     // See ODF v1.2 $19.12 (chart:display-label)
0196     bool showLabels;
0197     bool showOverlappingDataLabels;
0198 
0199     bool isVisible;
0200     QString name;
0201 
0202     QString axisPosition;
0203     QString axisLabelsPosition;
0204 
0205 };
0206 
0207 class CartesianAxis : public KChart::CartesianAxis
0208 {
0209 public:
0210     CartesianAxis(KoChart::Axis *_axis) : KChart::CartesianAxis(), axis(_axis) {}
0211     virtual ~CartesianAxis() {}
0212     const QString customizedLabel(const QString& label) const override {
0213         if (KoOdfNumberStyles::NumericStyleFormat *n = axis->numericStyleFormat())
0214             return KoOdfNumberStyles::format(label, *n);
0215         return label;
0216     }
0217 private:
0218     KoChart::Axis *axis;
0219 };
0220 
0221 Axis::Private::Private(Axis *axis, AxisDimension dim)
0222     : q(axis)
0223     , dimension(dim)
0224     , kdAxis(new CartesianAxis(axis))
0225     , kdPlane(0)
0226     , kdPolarPlane(0)
0227     , kdRadarPlane(0)
0228     , numericStyleFormat(0)
0229 {
0230     centerDataPoints = false;
0231 
0232     gapBetweenBars = 0;
0233     gapBetweenSets = 100;
0234 
0235     isVisible = true;
0236 
0237     useAutomaticMajorInterval = true;
0238     useAutomaticMinorInterval = true;
0239     useAutomaticMinimumRange = true;
0240     useAutomaticMaximumRange = true;
0241 
0242     majorInterval = 2;
0243     minorIntervalDivisor = 1;
0244 
0245     showMajorGrid = false;
0246     showMinorGrid = false;
0247 
0248     logarithmicScaling = false;
0249 
0250     showInnerMinorTicks = false;
0251     showOuterMinorTicks = false;
0252     showInnerMajorTicks = false;
0253     showOuterMajorTicks = true;
0254 
0255     showOverlappingDataLabels = false;
0256     showLabels = true;
0257 
0258     title = 0;
0259     titleData = 0;
0260 
0261     KChart::RulerAttributes attr = kdAxis->rulerAttributes();
0262     attr.setShowRulerLine(true);
0263     attr.setRulerLinePen(QPen());
0264     kdAxis->setRulerAttributes(attr);
0265 }
0266 
0267 Axis::Private::~Private()
0268 {
0269     Q_ASSERT(plotArea);
0270 
0271     q->removeAxisFromDiagrams();
0272 
0273     delete kdBarDiagram;
0274     delete kdLineDiagram;
0275     delete kdAreaDiagram;
0276     delete kdCircleDiagram;
0277     delete kdRingDiagram;
0278     delete kdRadarDiagram;
0279     delete kdScatterDiagram;
0280     delete kdStockDiagram;
0281     delete kdBubbleDiagram;
0282     delete kdSurfaceDiagram;
0283     delete kdGanttDiagram;
0284 
0285     delete numericStyleFormat;
0286 
0287     delete kdAxis;
0288 
0289     foreach(DataSet *dataSet, dataSets)
0290         dataSet->setAttachedAxis(0);
0291 }
0292 
0293 void Axis::Private::registerDiagram(KChart::AbstractDiagram *diagram)
0294 {
0295     QObject::connect(plotArea->proxyModel(), SIGNAL(columnsInserted(QModelIndex,int,int)),
0296                      diagram->model(), SLOT(slotColumnsInserted(QModelIndex,int,int)));
0297 
0298     QObject::connect(diagram, SIGNAL(propertiesChanged()),
0299                      plotArea, SLOT(plotAreaUpdate()));
0300     QObject::connect(diagram, SIGNAL(layoutChanged(AbstractDiagram*)),
0301                      plotArea, SLOT(plotAreaUpdate()));
0302     QObject::connect(diagram, SIGNAL(modelsChanged()),
0303                      plotArea, SLOT(plotAreaUpdate()));
0304     QObject::connect(diagram, SIGNAL(dataHidden()),
0305                      plotArea, SLOT(plotAreaUpdate()));
0306 }
0307 
0308 KChart::AbstractDiagram *Axis::Private::getDiagramAndCreateIfNeeded(ChartType chartType)
0309 {
0310     KChart::AbstractDiagram *diagram = 0;
0311 
0312     switch (chartType) {
0313     case BarChartType:
0314         if (!kdBarDiagram)
0315             createBarDiagram();
0316         diagram = kdBarDiagram;
0317         break;
0318     case LineChartType:
0319         if (!kdLineDiagram)
0320             createLineDiagram();
0321         diagram = kdLineDiagram;
0322         break;
0323     case AreaChartType:
0324         if (!kdAreaDiagram)
0325             createAreaDiagram();
0326         diagram = kdAreaDiagram;
0327         break;
0328     case CircleChartType:
0329         if (!kdCircleDiagram)
0330             createCircleDiagram();
0331         diagram = kdCircleDiagram;
0332         break;
0333     case RingChartType:
0334         if (!kdRingDiagram)
0335             createRingDiagram();
0336         diagram = kdRingDiagram;
0337         break;
0338     case RadarChartType:
0339     case FilledRadarChartType:
0340         if (!kdRadarDiagram)
0341             createRadarDiagram(chartType == FilledRadarChartType);
0342         diagram = kdRadarDiagram;
0343         break;
0344     case ScatterChartType:
0345         if (!kdScatterDiagram)
0346             createScatterDiagram();
0347         diagram = kdScatterDiagram;
0348         break;
0349     case StockChartType:
0350         if (!kdStockDiagram)
0351             createStockDiagram();
0352         diagram = kdStockDiagram;
0353         break;
0354     case BubbleChartType:
0355         if (!kdBubbleDiagram)
0356             createBubbleDiagram();
0357         diagram = kdBubbleDiagram;
0358         break;
0359     case SurfaceChartType:
0360         if (!kdSurfaceDiagram)
0361             createSurfaceDiagram();
0362         diagram = kdSurfaceDiagram;
0363         break;
0364     case GanttChartType:
0365         if (!kdGanttDiagram)
0366             createGanttDiagram();
0367         diagram = kdGanttDiagram;
0368         break;
0369     default:
0370         ;
0371     }
0372     diagram->setObjectName(q->name()); // for debug
0373     adjustAllDiagrams();
0374     debugChartAxis<<q->name()<<"diagram"<<diagram<<"for"<<chartType;
0375     return diagram;
0376 }
0377 
0378 /**
0379  * Returns currently used internal KChart diagram for the specified chart type
0380  */
0381 KChart::AbstractDiagram *Axis::Private::getDiagram(ChartType chartType)
0382 {
0383     switch (chartType) {
0384         case BarChartType:
0385             return kdBarDiagram;
0386         case LineChartType:
0387             return kdLineDiagram;
0388         case AreaChartType:
0389             return kdAreaDiagram;
0390         case CircleChartType:
0391             return kdCircleDiagram;
0392         case RingChartType:
0393             return kdRingDiagram;
0394         case RadarChartType:
0395         case FilledRadarChartType:
0396             return kdRadarDiagram;
0397         case ScatterChartType:
0398             return kdScatterDiagram;
0399         case StockChartType:
0400             return kdStockDiagram;
0401         case BubbleChartType:
0402             return kdBubbleDiagram;
0403         case SurfaceChartType:
0404             return kdSurfaceDiagram;
0405         case GanttChartType:
0406             return kdGanttDiagram;
0407         case LastChartType:
0408             return 0;
0409         // Compiler warning for unhandled chart type is intentional.
0410     }
0411     Q_ASSERT(!"Unhandled chart type");
0412     return 0;
0413 }
0414 
0415 void Axis::Private::deleteDiagram(KChart::AbstractDiagram *diagram)
0416 {
0417     Q_ASSERT(diagram);
0418     if (diagram->coordinatePlane()) {
0419         diagram->coordinatePlane()->takeDiagram(diagram);
0420     }
0421     delete diagram;
0422     adjustAllDiagrams();
0423 }
0424 
0425 void Axis::Private::deleteDiagram(ChartType chartType)
0426 {
0427     KChart::AbstractDiagram *diagram = getDiagram(chartType);
0428     if (diagram) {
0429         deleteDiagram(diagram);
0430     }
0431 }
0432 
0433 
0434 void Axis::Private::createBarDiagram()
0435 {
0436     Q_ASSERT(kdBarDiagram == 0);
0437 
0438     kdBarDiagram = new KChart::BarDiagram(plotArea->kdChart(), kdPlane);
0439     KChartModel *model = new KChartModel(plotArea, kdBarDiagram);
0440     kdBarDiagram->setModel(model);
0441     registerDiagram(kdBarDiagram);
0442 
0443     // By 'vertical', KChart means the orientation of a chart's bars,
0444     // not the orientation of the x axis.
0445     kdBarDiagram->setOrientation(plotArea->isVertical() ? Qt::Horizontal : Qt::Vertical);
0446     kdBarDiagram->setPen(QPen(Qt::black, 0.0));
0447 
0448     kdBarDiagram->setAllowOverlappingDataValueTexts(showOverlappingDataLabels);
0449 
0450     if (plotAreaChartSubType == StackedChartSubtype)
0451         kdBarDiagram->setType(KChart::BarDiagram::Stacked);
0452     else if (plotAreaChartSubType == PercentChartSubtype) {
0453         kdBarDiagram->setType(KChart::BarDiagram::Percent);
0454         kdBarDiagram->setUnitSuffix("%", kdBarDiagram->orientation());
0455     }
0456 
0457     if (isVisible) {
0458         kdBarDiagram->addAxis(kdAxis);
0459         q->registerDiagram(kdBarDiagram);
0460     }
0461     kdPlane->addDiagram(kdBarDiagram);
0462 
0463     Q_ASSERT(plotArea);
0464     foreach (Axis *axis, plotArea->axes()) {
0465         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
0466             kdBarDiagram->addAxis(axis->kdAxis());
0467             axis->registerDiagram(kdBarDiagram);
0468         }
0469     }
0470 
0471     // Set default bar diagram attributes
0472     q->setGapBetweenBars(gapBetweenBars);
0473     q->setGapBetweenSets(gapBetweenSets);
0474 
0475     // Propagate existing settings
0476     KChart::ThreeDBarAttributes attributes(kdBarDiagram->threeDBarAttributes());
0477     attributes.setEnabled(plotArea->isThreeD());
0478     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
0479     kdBarDiagram->setThreeDBarAttributes(attributes);
0480 
0481     q->plotAreaIsVerticalChanged();
0482 
0483     plotArea->parent()->legend()->kdLegend()->addDiagram(kdBarDiagram);
0484 }
0485 
0486 void Axis::Private::createLineDiagram()
0487 {
0488     Q_ASSERT(kdLineDiagram == 0);
0489 
0490     kdLineDiagram = new KChart::LineDiagram(plotArea->kdChart(), kdPlane);
0491     KChartModel *model = new KChartModel(plotArea, kdLineDiagram);
0492     kdLineDiagram->setModel(model);
0493     registerDiagram(kdLineDiagram);
0494 
0495     kdLineDiagram->setAllowOverlappingDataValueTexts(showOverlappingDataLabels);
0496 
0497     if (plotAreaChartSubType == StackedChartSubtype)
0498         kdLineDiagram->setType(KChart::LineDiagram::Stacked);
0499     else if (plotAreaChartSubType == PercentChartSubtype)
0500         kdLineDiagram->setType(KChart::LineDiagram::Percent);
0501 
0502     if (isVisible) {
0503         kdLineDiagram->addAxis(kdAxis);
0504         q->registerDiagram(kdLineDiagram);
0505     }
0506     kdPlane->addDiagram(kdLineDiagram);
0507 
0508     Q_ASSERT(plotArea);
0509     foreach (Axis *axis, plotArea->axes()) {
0510         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
0511             kdLineDiagram->addAxis(axis->kdAxis());
0512             axis->registerDiagram(kdLineDiagram);
0513         }
0514     }
0515 
0516     // Propagate existing settings
0517     KChart::ThreeDLineAttributes attributes(kdLineDiagram->threeDLineAttributes());
0518     attributes.setEnabled(plotArea->isThreeD());
0519     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
0520     kdLineDiagram->setThreeDLineAttributes(attributes);
0521 
0522     KChart::LineAttributes lineAttr = kdLineDiagram->lineAttributes();
0523     lineAttr.setMissingValuesPolicy(KChart::LineAttributes::MissingValuesHideSegments);
0524     kdLineDiagram->setLineAttributes(lineAttr);
0525 
0526     plotArea->parent()->legend()->kdLegend()->addDiagram(kdLineDiagram);
0527 }
0528 
0529 void Axis::Private::createAreaDiagram()
0530 {
0531     Q_ASSERT(kdAreaDiagram == 0);
0532 
0533     kdAreaDiagram = new KChart::LineDiagram(plotArea->kdChart(), kdPlane);
0534     KChartModel *model = new KChartModel(plotArea, kdAreaDiagram);
0535     kdAreaDiagram->setModel(model);
0536     registerDiagram(kdAreaDiagram);
0537     KChart::LineAttributes attr = kdAreaDiagram->lineAttributes();
0538     // Draw the area under the lines. This makes this diagram an area chart.
0539     attr.setDisplayArea(true);
0540     kdAreaDiagram->setLineAttributes(attr);
0541     kdAreaDiagram->setPen(QPen(Qt::black, 0.0));
0542     // KD Chart by default draws the first data set as last line in a normal
0543     // line diagram, we however want the first series to appear in front.
0544     kdAreaDiagram->setReverseDatasetOrder(true);
0545 
0546     kdAreaDiagram->setAllowOverlappingDataValueTexts(showOverlappingDataLabels);
0547 
0548     if (plotAreaChartSubType == StackedChartSubtype)
0549         kdAreaDiagram->setType(KChart::LineDiagram::Stacked);
0550     else if (plotAreaChartSubType == PercentChartSubtype)
0551     {
0552         kdAreaDiagram->setType(KChart::LineDiagram::Percent);
0553         kdAreaDiagram->setUnitSuffix("%", Qt::Vertical);
0554     }
0555 
0556     if (isVisible) {
0557         kdAreaDiagram->addAxis(kdAxis);
0558         q->registerDiagram(kdAreaDiagram);
0559     }
0560     kdPlane->addDiagram(kdAreaDiagram);
0561 
0562     Q_ASSERT(plotArea);
0563     foreach (Axis *axis, plotArea->axes()) {
0564         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
0565             kdAreaDiagram->addAxis(axis->kdAxis());
0566             axis->registerDiagram(kdAreaDiagram);
0567         }
0568     }
0569 
0570     // Propagate existing settings
0571     KChart::ThreeDLineAttributes attributes(kdAreaDiagram->threeDLineAttributes());
0572     attributes.setEnabled(plotArea->isThreeD());
0573     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
0574     kdAreaDiagram->setThreeDLineAttributes(attributes);
0575 
0576     plotArea->parent()->legend()->kdLegend()->addDiagram(kdAreaDiagram);
0577 }
0578 
0579 void Axis::Private::createCircleDiagram()
0580 {
0581     Q_ASSERT(kdCircleDiagram == 0);
0582 
0583     kdCircleDiagram = new KChart::PieDiagram(plotArea->kdChart(), kdPolarPlane);
0584     KChartModel *model = new KChartModel(plotArea, kdCircleDiagram);
0585     kdCircleDiagram->setModel(model);
0586     registerDiagram(kdCircleDiagram);
0587 
0588     model->setDataDirection(Qt::Horizontal);
0589 
0590     plotArea->parent()->legend()->kdLegend()->addDiagram(kdCircleDiagram);
0591     kdPolarPlane->addDiagram(kdCircleDiagram);
0592 
0593     // Propagate existing settings
0594     KChart::ThreeDPieAttributes attributes(kdCircleDiagram->threeDPieAttributes());
0595     attributes.setEnabled(plotArea->isThreeD());
0596     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
0597     kdCircleDiagram->setThreeDPieAttributes(attributes);
0598 
0599     // Initialize with default values that are specified in PlotArea
0600     // Note: KChart takes an int here, though ODF defines the offset to be a double.
0601     kdPolarPlane->setStartPosition((int)plotArea->angleOffset());
0602 }
0603 
0604 void Axis::Private::createRingDiagram()
0605 {
0606     Q_ASSERT(kdRingDiagram == 0);
0607 
0608     kdRingDiagram = new KChart::RingDiagram(plotArea->kdChart(), kdPolarPlane);
0609     KChartModel *model = new KChartModel(plotArea, kdRingDiagram);
0610     kdRingDiagram->setModel(model);
0611     registerDiagram(kdRingDiagram);
0612 
0613     model->setDataDirection(Qt::Horizontal);
0614 
0615     plotArea->parent()->legend()->kdLegend()->addDiagram(kdRingDiagram);
0616     kdPolarPlane->addDiagram(kdRingDiagram);
0617 
0618     // Propagate existing settings
0619     KChart::ThreeDPieAttributes attributes(kdRingDiagram->threeDPieAttributes());
0620     attributes.setEnabled(plotArea->isThreeD());
0621     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
0622     kdRingDiagram->setThreeDPieAttributes(attributes);
0623 
0624     // Initialize with default values that are specified in PlotArea
0625     // Note: KChart takes an int here, though ODF defines the offset to be a double.
0626     kdPolarPlane->setStartPosition((int)plotArea->angleOffset());
0627 }
0628 
0629 void Axis::Private::createRadarDiagram(bool filled)
0630 {
0631     Q_ASSERT(kdRadarDiagram == 0);
0632 
0633     //kdRadarDiagramModel->setDataDimensions(2);
0634     //kdRadarDiagramModel->setDataDirection(Qt::Horizontal);
0635 
0636     kdRadarDiagram = new KChart::RadarDiagram(plotArea->kdChart(), kdRadarPlane);
0637     KChartModel *model = new KChartModel(plotArea, kdRadarDiagram);
0638     kdRadarDiagram->setModel(model);
0639     registerDiagram(kdRadarDiagram);
0640 
0641     kdRadarDiagram->setCloseDatasets(true);
0642 
0643     if (filled) {
0644         // Don't use a solid fill of 1.0 but a more transparent one so the
0645         // grid and the data-value-labels are still visible plus it provides
0646         // a better look (other areas can still be seen) even if it's slightly
0647         // different from what OO.org does.
0648         kdRadarDiagram->setFillAlpha(0.4);
0649     }
0650 
0651 #if 0  // Stacked and Percent not supported by KChart.
0652     if (plotAreaChartSubType == StackedChartSubtype)
0653         kdRadarDiagram->setType(KChart::PolarDiagram::Stacked);
0654     else if (plotAreaChartSubType == PercentChartSubtype)
0655         kdRadarDiagram->setType(KChart::PolarDiagram::Percent);
0656 #endif
0657     plotArea->parent()->legend()->kdLegend()->addDiagram(kdRadarDiagram);
0658     kdRadarPlane->addDiagram(kdRadarDiagram);
0659 }
0660 
0661 void Axis::Private::createScatterDiagram()
0662 {
0663     Q_ASSERT(kdScatterDiagram == 0);
0664     Q_ASSERT(plotArea);
0665 
0666     kdScatterDiagram = new KChart::Plotter(plotArea->kdChart(), kdPlane);
0667     KChartModel *model = new KChartModel(plotArea, kdScatterDiagram);
0668     kdScatterDiagram->setModel(model);
0669     registerDiagram(kdScatterDiagram);
0670 
0671     model->setDataDimensions(2);
0672 
0673     kdScatterDiagram->setPen(Qt::NoPen);
0674 
0675     if (isVisible) {
0676         kdScatterDiagram->addAxis(kdAxis);
0677         q->registerDiagram(kdScatterDiagram);
0678     }
0679     kdPlane->addDiagram(kdScatterDiagram);
0680 
0681 
0682     foreach (Axis *axis, plotArea->axes()) {
0683         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
0684             kdScatterDiagram->addAxis(axis->kdAxis());
0685             axis->registerDiagram(kdScatterDiagram);
0686         }
0687     }
0688 
0689     // Propagate existing settings
0690     KChart::ThreeDLineAttributes attributes(kdScatterDiagram->threeDLineAttributes());
0691     attributes.setEnabled(plotArea->isThreeD());
0692     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
0693     kdScatterDiagram->setThreeDLineAttributes(attributes);
0694 
0695     plotArea->parent()->legend()->kdLegend()->addDiagram(kdScatterDiagram);
0696 }
0697 
0698 void Axis::Private::createStockDiagram()
0699 {
0700     Q_ASSERT(kdStockDiagram == 0);
0701 
0702     kdStockDiagram = new KChart::StockDiagram(plotArea->kdChart(), kdPlane);
0703     KChartModel *model = new KChartModel(plotArea, kdStockDiagram);
0704     kdStockDiagram->setModel(model);
0705     switch (plotAreaChartSubType) {
0706         case HighLowCloseChartSubtype:
0707             kdStockDiagram->setType(KChart::StockDiagram::HighLowClose);
0708             break;
0709         case OpenHighLowCloseChartSubtype:
0710             kdStockDiagram->setType(KChart::StockDiagram::OpenHighLowClose);
0711             break;
0712         case CandlestickChartSubtype:
0713             kdStockDiagram->setType(KChart::StockDiagram::Candlestick);
0714             break;
0715     }
0716     registerDiagram(kdStockDiagram);
0717 
0718     model->setDataDimensions(numDimensions(StockChartType));
0719 
0720 #if 0  // Stacked and Percent not supported by KChart.
0721     if (plotAreaChartSubType == StackedChartSubtype)
0722         kdStockDiagram->setType(KChart::StockDiagram::Stacked);
0723     else if (plotAreaChartSubType == PercentChartSubtype)
0724         kdStockDiagram->setType(KChart::StockDiagram::Percent);
0725 #endif
0726 
0727     if (isVisible) {
0728         kdStockDiagram->addAxis(kdAxis);
0729         q->registerDiagram(kdStockDiagram);
0730     }
0731     kdPlane->addDiagram(kdStockDiagram);
0732 
0733     Q_ASSERT(plotArea);
0734     foreach (Axis *axis, plotArea->axes()) {
0735         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
0736             kdStockDiagram->addAxis(axis->kdAxis());
0737             axis->registerDiagram(kdStockDiagram);
0738         }
0739     }
0740 
0741     plotArea->parent()->legend()->kdLegend()->addDiagram(kdStockDiagram);
0742     q->updateKChartStockAttributes();
0743 }
0744 
0745 void Axis::Private::createBubbleDiagram()
0746 {
0747     Q_ASSERT(kdBubbleDiagram == 0);
0748     Q_ASSERT(plotArea);
0749 
0750     kdBubbleDiagram = new KChart::Plotter(plotArea->kdChart(), kdPlane);
0751     KChartModel *model = new KChartModel(plotArea, kdBubbleDiagram);
0752     kdBubbleDiagram->setModel(model);
0753     registerDiagram(kdBubbleDiagram);
0754 
0755     model->setDataDimensions(2);
0756 
0757     kdPlane->addDiagram(kdBubbleDiagram);
0758 
0759     foreach (Axis *axis, plotArea->axes()) {
0760         //if (axis->dimension() == XAxisDimension)
0761         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
0762             kdBubbleDiagram->addAxis(axis->kdAxis());
0763             q->registerDiagram(kdBubbleDiagram);
0764         }
0765     }
0766 
0767      // disable the connecting line
0768     KChart::LineAttributes la = kdBubbleDiagram->lineAttributes();
0769     la.setVisible(false);
0770     kdBubbleDiagram->setLineAttributes(la);
0771 
0772     plotArea->parent()->legend()->kdLegend()->addDiagram(kdBubbleDiagram);
0773 }
0774 
0775 void Axis::Private::createSurfaceDiagram()
0776 {
0777     Q_ASSERT(!kdSurfaceDiagram);
0778 
0779     // This is a so far a by KChart unsupported chart type.
0780     // Fall back to bar diagram for now.
0781     kdSurfaceDiagram = new KChart::BarDiagram(plotArea->kdChart(), kdPlane);
0782     KChartModel *model = new KChartModel(plotArea, kdSurfaceDiagram);
0783     kdSurfaceDiagram->setModel(model);
0784     registerDiagram(kdSurfaceDiagram);
0785     plotArea->parent()->legend()->kdLegend()->addDiagram(kdSurfaceDiagram);
0786     kdPlane->addDiagram(kdSurfaceDiagram);
0787 }
0788 
0789 void Axis::Private::createGanttDiagram()
0790 {
0791     // This is a so far a by KChart unsupported chart type (through KDGantt got merged into KChart with 2.3)
0792     Q_ASSERT(!kdGanttDiagram);
0793 
0794     // This is a so far a by KChart unsupported chart type.
0795     // Fall back to bar diagram for now.
0796     kdGanttDiagram = new KChart::BarDiagram(plotArea->kdChart(), kdPlane);
0797     KChartModel *model = new KChartModel(plotArea, kdGanttDiagram);
0798     kdGanttDiagram->setModel(model);
0799     registerDiagram(kdGanttDiagram);
0800     plotArea->parent()->legend()->kdLegend()->addDiagram(kdGanttDiagram);
0801     kdPlane->addDiagram(kdGanttDiagram);
0802 }
0803 
0804 /**
0805  * Automatically adjusts the diagram so that all currently displayed
0806  * diagram types fit together.
0807  */
0808 void Axis::Private::adjustAllDiagrams()
0809 {
0810     // If at least one dataset is attached that belongs to a
0811     // horizontal bar chart, set centerDataPoints to true.
0812     centerDataPoints = kdBarDiagram != 0;
0813     if (kdLineDiagram)
0814         kdLineDiagram->setCenterDataPoints(centerDataPoints);
0815     if (kdAreaDiagram)
0816         kdAreaDiagram->setCenterDataPoints(centerDataPoints);
0817 }
0818 
0819 
0820 // ================================================================
0821 //                             class Axis
0822 
0823 // FIXME: make it possible to create an axis without parent, and
0824 //        when it is removed, actually remove it from parent (signals and all)
0825 
0826 Axis::Axis(PlotArea *parent, AxisDimension dimension)
0827     : d(new Private(this, dimension))
0828 {
0829     Q_ASSERT(parent);
0830 
0831     parent->addAxis(this);
0832 
0833     d->plotArea = parent;
0834     d->kdAxis->setObjectName(name());
0835     KChart::BackgroundAttributes batt(d->kdAxis->backgroundAttributes());
0836     batt.setBrush(QBrush(Qt::white));
0837     d->kdAxis->setBackgroundAttributes(batt);
0838     setFontSize(8.0); // also sets MeasureCalculationModeAbsolute
0839     d->kdPlane = parent->kdCartesianPlane(this);
0840     d->kdPolarPlane = parent->kdPolarPlane();
0841     d->kdRadarPlane = parent->kdRadarPlane();
0842 
0843     d->plotAreaChartType    = d->plotArea->chartType();
0844     d->plotAreaChartSubType = d->plotArea->chartSubType();
0845 
0846     setOdfAxisPosition("start");
0847     setOdfAxisLabelsPosition("near-axis");
0848 
0849     KoShapeFactoryBase *textShapeFactory = KoShapeRegistry::instance()->value(TextShapeId);
0850     if (textShapeFactory)
0851         d->title = textShapeFactory->createDefaultShape(parent->parent()->resourceManager());
0852     if (d->title) {
0853         d->titleData = qobject_cast<TextLabelData*>(d->title->userData());
0854         if (d->titleData == 0) {
0855             d->titleData = new TextLabelData;
0856             d->title->setUserData(d->titleData);
0857         }
0858 
0859         QFont font = d->titleData->document()->defaultFont();
0860         font.setPointSizeF(9);
0861         d->titleData->document()->setDefaultFont(font);
0862     }
0863     else {
0864         d->title = new TextLabelDummy;
0865         d->titleData = new TextLabelData;
0866         KoTextDocumentLayout *documentLayout = new KoTextDocumentLayout(d->titleData->document());
0867         d->titleData->document()->setDocumentLayout(documentLayout);
0868         d->title->setUserData(d->titleData);
0869     }
0870     d->title->setSize(QSizeF(CM_TO_POINT(3), CM_TO_POINT(0.75)));
0871 
0872     d->plotArea->parent()->addShape(d->title);
0873     d->plotArea->parent()->setClipped(d->title, true);
0874     d->plotArea->parent()->setInheritsTransform(d->title, true);
0875     d->title->setDeletable(false);
0876     d->title->setZIndex(5);
0877     d->title->setToolDelegates(QSet<KoShape*>()<<parent->parent()<<d->title); // Enable chart tool
0878     d->titleData->setResizeMethod(KoTextShapeDataBase::AutoResize);
0879     d->title->setAdditionalStyleAttribute("chart:auto-position", "true");
0880     d->title->setAllowedInteraction(KoShape::ShearingAllowed, false);
0881     d->title->setAllowedInteraction(KoShape::RotationAllowed, false);
0882     d->title->setAllowedInteraction(KoShape::ResizeAllowed, false);
0883     d->title->setVisible(false); // Needed to avoid problems when creating secondary axes (Axis creation needs review/refactoring)
0884 
0885     connect(d->plotArea, SIGNAL(angleOffsetChanged(qreal)), this, SLOT(setAngleOffset(qreal)));
0886     connect(d->plotArea, SIGNAL(holeSizeChanged(qreal)), this, SLOT(setHoleSize(qreal)));
0887 
0888     d->updatePosition();
0889 }
0890 
0891 Axis::~Axis()
0892 {
0893     Q_ASSERT(d->plotArea);
0894     d->plotArea->parent()->KoShapeContainer::removeShape(d->title);
0895 
0896     Q_ASSERT(d->title);
0897     delete d->title;
0898 
0899     delete d;
0900 }
0901 
0902 PlotArea* Axis::plotArea() const
0903 {
0904     return d->plotArea;
0905 }
0906 
0907 KoShape *Axis::title() const
0908 {
0909     return d->title;
0910 }
0911 
0912 QString Axis::titleText() const
0913 {
0914     return d->titleData->document()->toPlainText();
0915 }
0916 
0917 bool Axis::showLabels() const
0918 {
0919     return d->showLabels;
0920 }
0921 
0922 bool Axis::showOverlappingDataLabels() const
0923 {
0924     return d->showOverlappingDataLabels;
0925 }
0926 
0927 QString Axis::id() const
0928 {
0929     return d->id;
0930 }
0931 
0932 AxisDimension Axis::dimension() const
0933 {
0934     return d->dimension;
0935 }
0936 
0937 QList<DataSet*> Axis::dataSets() const
0938 {
0939     return d->dataSets;
0940 }
0941 
0942 bool Axis::attachDataSet(DataSet *dataSet)
0943 {
0944     Q_ASSERT(!d->dataSets.contains(dataSet));
0945     if (d->dataSets.contains(dataSet))
0946         return false;
0947 
0948     d->dataSets.append(dataSet);
0949 
0950     if (dimension() == YAxisDimension) {
0951         dataSet->setAttachedAxis(this);
0952 
0953         ChartType chartType = dataSet->chartType();
0954         if (chartType == LastChartType)
0955             chartType = d->plotAreaChartType;
0956 
0957         KChart::AbstractDiagram *diagram = d->getDiagramAndCreateIfNeeded(chartType);
0958         Q_ASSERT(diagram);
0959         KChartModel *model = dynamic_cast<KChartModel*>(diagram->model());
0960         Q_ASSERT(model);
0961 
0962         model->addDataSet(dataSet);
0963 
0964         layoutPlanes();
0965         requestRepaint();
0966     }
0967 
0968     return true;
0969 }
0970 
0971 bool Axis::detachDataSet(DataSet *dataSet, bool silent)
0972 {
0973     Q_ASSERT(d->dataSets.contains(dataSet));
0974     if (!d->dataSets.contains(dataSet))
0975         return false;
0976     d->dataSets.removeAll(dataSet);
0977 
0978     if (dimension() == YAxisDimension) {
0979         ChartType chartType = dataSet->chartType();
0980         if (chartType == LastChartType)
0981             chartType = d->plotAreaChartType;
0982 
0983         KChart::AbstractDiagram *oldDiagram = d->getDiagram(chartType);
0984         Q_ASSERT(oldDiagram);
0985         KChartModel *oldModel = dynamic_cast<KChartModel*>(oldDiagram->model());
0986         Q_ASSERT(oldModel);
0987 
0988         const int rowCount = oldModel->dataDirection() == Qt::Vertical
0989                                  ? oldModel->columnCount() : oldModel->rowCount();
0990         // If there's only as many rows as needed for *one*
0991         // dataset, that means that the dataset we're removing is
0992         // the last one in the model --> delete model
0993         if (rowCount == oldModel->dataDimensions())
0994             d->deleteDiagram(chartType);
0995         else
0996             oldModel->removeDataSet(dataSet, silent);
0997 
0998         dataSet->setKdChartModel(0);
0999         dataSet->setAttachedAxis(0);
1000 
1001         if (!silent) {
1002             layoutPlanes();
1003             requestRepaint();
1004         }
1005     }
1006 
1007     return true;
1008 }
1009 
1010 void Axis::clearDataSets()
1011 {
1012     QList<DataSet*> list = d->dataSets;
1013     foreach(DataSet *dataSet, list)
1014         detachDataSet(dataSet, true);
1015 }
1016 
1017 bool Axis::showRuler() const
1018 {
1019     return d->kdAxis->rulerAttributes().showRulerLine();
1020 }
1021 
1022 void Axis::setShowRuler(bool show)
1023 {
1024     KChart::RulerAttributes attr = d->kdAxis->rulerAttributes();
1025     attr.setShowRulerLine(!attr.showRulerLine());
1026     d->kdAxis->setRulerAttributes(attr);
1027 }
1028 
1029 qreal Axis::majorInterval() const
1030 {
1031     return d->majorInterval;
1032 }
1033 
1034 void Axis::setMajorInterval(qreal interval)
1035 {
1036     // Don't overwrite if automatic interval is being requested (for
1037     // interval = 0)
1038     if (interval != 0.0) {
1039         d->majorInterval = interval;
1040         d->useAutomaticMajorInterval = false;
1041     } else
1042         d->useAutomaticMajorInterval = true;
1043 
1044     // KChart
1045     KChart::GridAttributes attributes = d->kdPlane->gridAttributes(orientation());
1046     attributes.setGridStepWidth(interval);
1047     d->kdPlane->setGridAttributes(orientation(), attributes);
1048 
1049     attributes = d->kdPolarPlane->gridAttributes(true);
1050     attributes.setGridStepWidth(interval);
1051     d->kdPolarPlane->setGridAttributes(true, attributes);
1052 
1053     // FIXME: Hide minor tick marks more appropriately
1054     if (!d->showMinorGrid && interval != 0.0)
1055         setMinorInterval(interval);
1056 
1057     requestRepaint();
1058 }
1059 
1060 qreal Axis::minorInterval() const
1061 {
1062     return (d->majorInterval / (qreal)d->minorIntervalDivisor);
1063 }
1064 
1065 void Axis::setMinorInterval(qreal interval)
1066 {
1067     if (interval == 0.0)
1068         setMinorIntervalDivisor(0);
1069     else
1070         setMinorIntervalDivisor(int(qRound(d->majorInterval / interval)));
1071 }
1072 
1073 int Axis::minorIntervalDivisor() const
1074 {
1075     return d->minorIntervalDivisor;
1076 }
1077 
1078 void Axis::setMinorIntervalDivisor(int divisor)
1079 {
1080     // A divisor of 0.0 means automatic minor interval calculation
1081     if (divisor != 0) {
1082         d->minorIntervalDivisor = divisor;
1083         d->useAutomaticMinorInterval = false;
1084     } else
1085         d->useAutomaticMinorInterval = true;
1086 
1087     // KChart
1088     KChart::GridAttributes attributes = d->kdPlane->gridAttributes(orientation());
1089     attributes.setGridSubStepWidth((divisor != 0) ? (d->majorInterval / divisor) : 0.0);
1090     d->kdPlane->setGridAttributes(orientation(), attributes);
1091 
1092     attributes = d->kdPolarPlane->gridAttributes(true);
1093     attributes.setGridSubStepWidth((divisor != 0) ? (d->majorInterval / divisor) : 0.0);
1094     d->kdPolarPlane->setGridAttributes(true, attributes);
1095 
1096     requestRepaint();
1097 }
1098 
1099 bool Axis::useAutomaticMajorInterval() const
1100 {
1101     return d->useAutomaticMajorInterval;
1102 }
1103 
1104 bool Axis::useAutomaticMinorInterval() const
1105 {
1106     return d->useAutomaticMinorInterval;
1107 }
1108 
1109 void Axis::setUseAutomaticMajorInterval(bool automatic)
1110 {
1111     d->useAutomaticMajorInterval = automatic;
1112     // A value of 0.0 will activate automatic intervals,
1113     // but not change d->majorInterval
1114     setMajorInterval(automatic ? 0.0 : majorInterval());
1115 }
1116 
1117 void Axis::setUseAutomaticMinorInterval(bool automatic)
1118 {
1119     d->useAutomaticMinorInterval = automatic;
1120     // A value of 0.0 will activate automatic intervals,
1121     // but not change d->minorIntervalDivisor
1122     setMinorInterval(automatic ? 0.0 : minorInterval());
1123 }
1124 
1125 bool Axis::showInnerMinorTicks() const
1126 {
1127     return d->showInnerMinorTicks;
1128 }
1129 
1130 bool Axis::showOuterMinorTicks() const
1131 {
1132     return d->showOuterMinorTicks;
1133 }
1134 
1135 bool Axis::showInnerMajorTicks() const
1136 {
1137     return d->showInnerMinorTicks;
1138 }
1139 
1140 bool Axis::showOuterMajorTicks() const
1141 {
1142     return d->showOuterMajorTicks;
1143 }
1144 
1145 void Axis::setShowInnerMinorTicks(bool showTicks)
1146 {
1147     d->showInnerMinorTicks = showTicks;
1148     KChart::RulerAttributes attr = kdAxis()->rulerAttributes();
1149     attr.setShowMinorTickMarks(d->showInnerMinorTicks || d->showOuterMinorTicks);
1150     kdAxis()->setRulerAttributes(attr);
1151 }
1152 
1153 void Axis::setShowOuterMinorTicks(bool showTicks)
1154 {
1155     d->showOuterMinorTicks = showTicks;
1156     KChart::RulerAttributes attr = kdAxis()->rulerAttributes();
1157     attr.setShowMinorTickMarks(d->showInnerMinorTicks || d->showOuterMinorTicks);
1158     kdAxis()->setRulerAttributes(attr);
1159 }
1160 
1161 void Axis::setShowInnerMajorTicks(bool showTicks)
1162 {
1163     d->showInnerMajorTicks = showTicks;
1164     KChart::RulerAttributes attr = kdAxis()->rulerAttributes();
1165     attr.setShowMajorTickMarks(d->showInnerMajorTicks || d->showOuterMajorTicks);
1166     kdAxis()->setRulerAttributes(attr);
1167 }
1168 
1169 void Axis::setShowOuterMajorTicks(bool showTicks)
1170 {
1171     d->showOuterMajorTicks = showTicks;
1172     KChart::RulerAttributes attr = kdAxis()->rulerAttributes();
1173     attr.setShowMajorTickMarks(d->showInnerMajorTicks || d->showOuterMajorTicks);
1174     kdAxis()->setRulerAttributes(attr);
1175 }
1176 
1177 void Axis::setScalingLogarithmic(bool logarithmicScaling)
1178 {
1179     d->logarithmicScaling = logarithmicScaling;
1180 
1181     if (dimension() != YAxisDimension)
1182         return;
1183 
1184     d->kdPlane->setAxesCalcModeY(d->logarithmicScaling
1185                                   ? KChart::AbstractCoordinatePlane::Logarithmic
1186                                   : KChart::AbstractCoordinatePlane::Linear);
1187     d->kdPlane->layoutPlanes();
1188 
1189     requestRepaint();
1190 }
1191 
1192 bool Axis::scalingIsLogarithmic() const
1193 {
1194     return d->logarithmicScaling;
1195 }
1196 
1197 bool Axis::showMajorGrid() const
1198 {
1199     return d->showMajorGrid;
1200 }
1201 
1202 void Axis::setShowMajorGrid(bool showGrid)
1203 {
1204     d->showMajorGrid = showGrid;
1205 
1206     // KChart
1207     KChart::GridAttributes  attributes = d->kdPlane->gridAttributes(orientation());
1208     attributes.setGridVisible(d->showMajorGrid);
1209     d->kdPlane->setGridAttributes(orientation(), attributes);
1210 
1211     attributes = d->kdPolarPlane->gridAttributes(true);
1212     attributes.setGridVisible(d->showMajorGrid);
1213     d->kdPolarPlane->setGridAttributes(true, attributes);
1214 
1215     requestRepaint();
1216 }
1217 
1218 bool Axis::showMinorGrid() const
1219 {
1220     return d->showMinorGrid;
1221 }
1222 
1223 void Axis::setShowMinorGrid(bool showGrid)
1224 {
1225     d->showMinorGrid = showGrid;
1226 
1227     // KChart
1228     KChart::GridAttributes  attributes = d->kdPlane->gridAttributes(orientation());
1229     attributes.setSubGridVisible(d->showMinorGrid);
1230     d->kdPlane->setGridAttributes(orientation(), attributes);
1231 
1232     attributes = d->kdPolarPlane->gridAttributes(true);
1233     attributes.setSubGridVisible(d->showMinorGrid);
1234     d->kdPolarPlane->setGridAttributes(true, attributes);
1235 
1236     requestRepaint();
1237 }
1238 
1239 void Axis::setTitleText(const QString &text)
1240 {
1241     d->titleData->document()->setPlainText(text);
1242 }
1243 
1244 void Axis::setShowLabels(bool show)
1245 {
1246     d->showLabels = show;
1247 
1248     KChart::TextAttributes textAttr = d->kdAxis->textAttributes();
1249     textAttr.setVisible(show);
1250     d->kdAxis->setTextAttributes(textAttr);
1251 }
1252 
1253 void Axis::setShowOverlappingDataLabels(bool show)
1254 {
1255     d->showOverlappingDataLabels = show;
1256 }
1257 
1258 Qt::Orientation Axis::orientation() const
1259 {
1260     bool chartIsVertical = d->plotArea->isVertical();
1261     bool horizontal = d->dimension == (chartIsVertical ? YAxisDimension
1262                                                        : XAxisDimension);
1263     return horizontal ? Qt::Horizontal : Qt::Vertical;
1264 }
1265 
1266 bool Axis::loadOdf(const KoXmlElement &axisElement, KoShapeLoadingContext &context)
1267 {
1268     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
1269     KoOdfStylesReader &stylesReader = context.odfLoadingContext().stylesReader();
1270     OdfLoadingHelper *helper = (OdfLoadingHelper*)context.sharedData(OdfLoadingHelperId);
1271     bool reverseAxis = false;
1272 
1273     d->title->setVisible(false);
1274 
1275     QPen gridPen(Qt::NoPen);
1276     QPen subGridPen(Qt::NoPen);
1277 
1278     d->showMajorGrid = false;
1279     d->showMinorGrid = false;
1280 
1281     d->showInnerMinorTicks = false;
1282     d->showOuterMinorTicks = false;
1283     d->showInnerMajorTicks = false;
1284     d->showOuterMajorTicks = true;
1285 
1286     // Use automatic interval calculation by default
1287     setMajorInterval(0.0);
1288     setMinorInterval(0.0);
1289 
1290     if (!axisElement.isNull()) {
1291 
1292         QString styleName = axisElement.attributeNS(KoXmlNS::chart, "style-name", QString());
1293         const KoXmlElement *stylElement = stylesReader.findStyle(styleName, "chart");
1294         if (stylElement) {
1295             const QString dataStyleName = stylElement->attributeNS(KoXmlNS::style, "data-style-name", QString());
1296             if (!dataStyleName.isEmpty() && stylesReader.dataFormats().contains(dataStyleName)) {
1297                 delete d->numericStyleFormat;
1298                 d->numericStyleFormat = new KoOdfNumberStyles::NumericStyleFormat(stylesReader.dataFormats()[dataStyleName].first);
1299             }
1300         }
1301 
1302         KoXmlElement n;
1303         forEachElement (n, axisElement) {
1304             if (n.namespaceURI() != KoXmlNS::chart)
1305                 continue;
1306             if (n.localName() == "title") {
1307                 OdfHelper::loadOdfTitle(d->title, n, context);
1308                 // title shall *always* have AutoResize
1309                 d->titleData->setResizeMethod(KoTextShapeDataBase::AutoResize);
1310             }
1311             else if (n.localName() == "grid") {
1312                 bool major = false;
1313                 if (n.hasAttributeNS(KoXmlNS::chart, "class")) {
1314                     const QString className = n.attributeNS(KoXmlNS::chart, "class");
1315                     if (className == "major")
1316                         major = true;
1317                 } else {
1318                     warnChart << "Error: Axis' <chart:grid> element contains no valid class. It must be either \"major\" or \"minor\".";
1319                     continue;
1320                 }
1321 
1322                 if (major) {
1323                     d->showMajorGrid = true;
1324                 } else {
1325                     d->showMinorGrid = true;
1326                 }
1327 
1328                 if (n.hasAttributeNS(KoXmlNS::chart, "style-name")) {
1329                     styleStack.clear();
1330                     context.odfLoadingContext().fillStyleStack(n, KoXmlNS::style, "style-name", "chart");
1331                     styleStack.setTypeProperties("graphic");
1332                     if (styleStack.hasProperty(KoXmlNS::svg, "stroke-color")) {
1333                         const QString strokeColor = styleStack.property(KoXmlNS::svg, "stroke-color");
1334                         //d->showMajorGrid = true;
1335                         if (major)
1336                             gridPen = QPen(QColor(strokeColor));
1337                         else
1338                             subGridPen = QPen(QColor(strokeColor));
1339                     }
1340                 }
1341             }
1342             else if (n.localName() == "categories") {
1343                 if (n.hasAttributeNS(KoXmlNS::table, "cell-range-address")) {
1344                     const CellRegion region = CellRegion(helper->tableSource, n.attributeNS(KoXmlNS::table, "cell-range-address"));
1345                     helper->categoryRegionSpecifiedInXAxis = true;
1346                     plotArea()->proxyModel()->setCategoryDataRegion(region);
1347                 }
1348             }
1349         }
1350 
1351         if (axisElement.hasAttributeNS(KoXmlNS::chart, "axis-name")) {
1352             const QString name = axisElement.attributeNS(KoXmlNS::chart, "name", QString());
1353             setName(name);
1354         }
1355 
1356         // NOTE: chart:dimension already handled by PlotArea before and passed
1357         // explicitly in the constructor.
1358     } else {
1359         warnChartOdf<<"No axis element";
1360     }
1361 
1362     if (axisElement.hasAttributeNS(KoXmlNS::chart, "style-name")) {
1363         styleStack.clear();
1364         context.odfLoadingContext().fillStyleStack(axisElement, KoXmlNS::chart, "style-name", "chart");
1365 
1366         KoCharacterStyle charStyle;
1367         charStyle.loadOdf(&axisElement, context);
1368         setFont(charStyle.font());
1369 
1370         styleStack.setTypeProperties("chart");
1371 
1372         if (styleStack.hasProperty(KoXmlNS::chart, "logarithmic")
1373             && styleStack.property(KoXmlNS::chart, "logarithmic") == "true")
1374         {
1375             setScalingLogarithmic(true);
1376         }
1377 
1378         if (styleStack.hasProperty(KoXmlNS::chart, "reverse-direction")
1379             && styleStack.property(KoXmlNS::chart, "reverse-direction") == "true")
1380         {
1381             reverseAxis = true;
1382         }
1383 
1384         if (styleStack.hasProperty(KoXmlNS::chart, "interval-major"))
1385             setMajorInterval(KoUnit::parseValue(styleStack.property(KoXmlNS::chart, "interval-major")));
1386         if (styleStack.hasProperty(KoXmlNS::chart, "interval-minor-divisor"))
1387             setMinorIntervalDivisor(KoUnit::parseValue(styleStack.property(KoXmlNS::chart, "interval-minor-divisor")));
1388         else if (styleStack.hasProperty(KoXmlNS::chart, "interval-minor"))
1389             setMinorInterval(KoUnit::parseValue(styleStack.property(KoXmlNS::chart, "interval-minor")));
1390 
1391         if (styleStack.hasProperty(KoXmlNS::chart, "tick-marks-minor-inner"))
1392             setShowInnerMinorTicks(styleStack.property(KoXmlNS::chart, "tick-marks-minor-inner") == "true");
1393         if (styleStack.hasProperty(KoXmlNS::chart, "tick-marks-minor-outer"))
1394             setShowOuterMinorTicks(styleStack.property(KoXmlNS::chart, "tick-marks-minor-outer") == "true");
1395         if (styleStack.hasProperty(KoXmlNS::chart, "tick-marks-major-inner"))
1396             setShowInnerMajorTicks(styleStack.property(KoXmlNS::chart, "tick-marks-major-inner") == "true");
1397         if (styleStack.hasProperty(KoXmlNS::chart, "tick-marks-major-outer"))
1398             setShowOuterMajorTicks(styleStack.property(KoXmlNS::chart, "tick-marks-major-outer") == "true");
1399 
1400         if (styleStack.hasProperty(KoXmlNS::chart, "display-label"))
1401             setShowLabels(styleStack.property(KoXmlNS::chart, "display-label") != "false");
1402         if (styleStack.hasProperty(KoXmlNS::chart, "text-overlap"))
1403             setShowOverlappingDataLabels(styleStack.property(KoXmlNS::chart, "text-overlap") != "false");
1404         if (styleStack.hasProperty(KoXmlNS::chart, "visible"))
1405             setVisible(styleStack.property(KoXmlNS::chart, "visible")  != "false");
1406         if (styleStack.hasProperty(KoXmlNS::chart, "minimum")) {
1407             const qreal minimum = styleStack.property(KoXmlNS::chart, "minimum").toDouble();
1408             const qreal maximum = orientation() == Qt::Vertical
1409                                     ? d->kdPlane->verticalRange().second
1410                                     : d->kdPlane->horizontalRange().second;
1411             if (orientation() == Qt::Vertical)
1412                 d->kdPlane->setVerticalRange(qMakePair(minimum, maximum));
1413             else
1414                 d->kdPlane->setHorizontalRange(qMakePair(minimum, maximum));
1415             d->useAutomaticMinimumRange = false;
1416         }
1417         if (styleStack.hasProperty(KoXmlNS::chart, "maximum")) {
1418             const qreal minimum = orientation() == Qt::Vertical
1419                                     ? d->kdPlane->verticalRange().first
1420                                     : d->kdPlane->horizontalRange().first;
1421             const qreal maximum = styleStack.property(KoXmlNS::chart, "maximum").toDouble();
1422             if (orientation() == Qt::Vertical)
1423                 d->kdPlane->setVerticalRange(qMakePair(minimum, maximum));
1424             else
1425                 d->kdPlane->setHorizontalRange(qMakePair(minimum, maximum));
1426             d->useAutomaticMaximumRange = false;
1427         }
1428         /*if (styleStack.hasProperty(KoXmlNS::chart, "origin")) {
1429             const qreal origin = KoUnit::parseValue(styleStack.property(KoXmlNS::chart, "origin"));
1430         }*/
1431         styleStack.setTypeProperties("chart");
1432         if (styleStack.hasProperty(KoXmlNS::chart, "axis-position")) {
1433             d->axisPosition = styleStack.property(KoXmlNS::chart, "axis-position");
1434         }
1435         if (styleStack.hasProperty(KoXmlNS::chart, "axis-label-position")) {
1436             d->axisLabelsPosition = styleStack.property(KoXmlNS::chart, "axis-label-position");
1437         }
1438 
1439         styleStack.setTypeProperties("text");
1440         if (styleStack.hasProperty(KoXmlNS::fo, "font-size")) {
1441             const qreal fontSize = KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "font-size"));
1442             setFontSize(fontSize);
1443         }
1444         if (styleStack.hasProperty(KoXmlNS::fo, "font-color")) {
1445             QString fontColorString =  styleStack.property(KoXmlNS::fo, "font-color");
1446             QColor color(fontColorString);
1447             if (color.isValid()) {
1448                 KChart::TextAttributes tatt =  kdAxis()->textAttributes();
1449                 QPen pen = tatt.pen();
1450                 pen.setColor(color);
1451                 tatt.setPen(pen);
1452                 kdAxis()->setTextAttributes(tatt);
1453             }
1454         }
1455         if (styleStack.hasProperty(KoXmlNS::fo, "font-family")) {
1456             QString fontFamilyString = styleStack.property(KoXmlNS::fo, "font-family");
1457             if (!fontFamilyString.isEmpty()) {
1458                 QFont f = this->font();
1459                 f.setFamily(fontFamilyString);
1460                 setFont(f);
1461             }
1462         }
1463         if (styleStack.hasProperty(KoXmlNS::fo, "font-style")) {
1464             QString fontStyle = styleStack.property(KoXmlNS::fo, "font-style");
1465             if (fontStyle == "italic") {
1466                 QFont f = this->font();
1467                 f.setItalic(true);
1468                 setFont(f);
1469             } else if (fontStyle == "oblique") {
1470                 // TODO
1471             }
1472         }
1473         if (styleStack.hasProperty(KoXmlNS::fo, "font-weight")) {
1474             QString fontWeight = styleStack.property(KoXmlNS::fo, "font-weight");
1475             //fo:font-weight attribute are normal, bold, 100, 200, 300, 400, 500, 600, 700, 800 or 900.
1476             if (fontWeight == "bold") {
1477                 QFont f = this->font();
1478                 f.setBold(true);
1479                 setFont(f);
1480             } else {
1481                 // TODO
1482             }
1483         }
1484     } else {
1485         warnChartOdf<<"Axis element has no style information";
1486         setShowLabels(KoOdfWorkaround::fixMissingStyle_DisplayLabel(axisElement, context));
1487     }
1488 
1489     KChart::GridAttributes gridAttr = d->kdPlane->gridAttributes(orientation());
1490     gridAttr.setGridVisible(d->showMajorGrid);
1491     gridAttr.setSubGridVisible(d->showMinorGrid);
1492     if (gridPen.style() != Qt::NoPen)
1493         gridAttr.setGridPen(gridPen);
1494     if (subGridPen.style() != Qt::NoPen)
1495         gridAttr.setSubGridPen(subGridPen);
1496     d->kdPlane->setGridAttributes(orientation(), gridAttr);
1497 
1498     gridAttr = d->kdPolarPlane->gridAttributes(orientation());
1499     gridAttr.setGridVisible(d->showMajorGrid);
1500     gridAttr.setSubGridVisible(d->showMinorGrid);
1501     if (gridPen.style() != Qt::NoPen)
1502         gridAttr.setGridPen(gridPen);
1503     if (subGridPen.style() != Qt::NoPen)
1504         gridAttr.setSubGridPen(subGridPen);
1505 //     if (plotArea()->chartType() == RadarChartType || plotArea()->chartType() == FilledRadarChartType)
1506 //         d->kdPolarPlane->setGridAttributes(false, gridAttr);
1507 //     else
1508     d->kdPolarPlane->setGridAttributes(true, gridAttr);
1509 
1510     gridAttr = d->kdRadarPlane->globalGridAttributes();
1511     gridAttr.setGridVisible(d->showMajorGrid);
1512     gridAttr.setSubGridVisible(d->showMinorGrid);
1513     if (gridPen.style() != Qt::NoPen)
1514         gridAttr.setGridPen(gridPen);
1515     if (subGridPen.style() != Qt::NoPen)
1516         gridAttr.setSubGridPen(subGridPen);
1517     d->kdRadarPlane->setGlobalGridAttributes(gridAttr);
1518     KChart::TextAttributes ta(d->kdRadarPlane->textAttributes());
1519     ta.setVisible(helper->categoryRegionSpecifiedInXAxis);
1520     ta.setFont(font());
1521     ta.setFontSize(50);
1522     d->kdRadarPlane->setTextAttributes(ta);
1523 
1524     if (reverseAxis) {
1525         KChart::CartesianCoordinatePlane *plane = dynamic_cast<KChart::CartesianCoordinatePlane*>(kdPlane());
1526         if (plane) {
1527             if (orientation() == Qt::Horizontal)
1528                 plane->setHorizontalRangeReversed(reverseAxis);
1529             else // Qt::Vertical
1530                 plane->setVerticalRangeReversed(reverseAxis);
1531         }
1532     }
1533 
1534     // Style of axis is still in styleStack
1535     if (!loadOdfChartSubtypeProperties(axisElement, context)) {
1536         return false;
1537     }
1538     if (titleText().isEmpty()) {
1539         // do not allow visible empty text
1540         d->title->setVisible(false);
1541     }
1542     requestRepaint();
1543 
1544     debugChartOdf<<"Loaded axis:"<<name()<<"dimension:"<<dimension();
1545     return true;
1546 }
1547 
1548 bool Axis::loadOdfChartSubtypeProperties(const KoXmlElement &axisElement,
1549                                           KoShapeLoadingContext &context)
1550 {
1551     Q_UNUSED(axisElement);
1552     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
1553     styleStack.setTypeProperties("chart");
1554 
1555     // Load these attributes regardless of the actual chart type. They'll have
1556     // no effect if their respective chart type is not in use.
1557     // However, they'll be saved back to ODF that way.
1558     if (styleStack.hasProperty(KoXmlNS::chart, "gap-width"))
1559         setGapBetweenSets(KoUnit::parseValue(styleStack.property(KoXmlNS::chart, "gap-width")));
1560     if (styleStack.hasProperty(KoXmlNS::chart, "overlap"))
1561         // The minus is intended!
1562         setGapBetweenBars(-KoUnit::parseValue(styleStack.property(KoXmlNS::chart, "overlap")));
1563 
1564     return true;
1565 }
1566 
1567 void Axis::setName(const QString &name)
1568 {
1569     d->name = name;
1570 }
1571 
1572 // NOTE: only used during save/load to enable attaching axis to datasets
1573 QString Axis::name() const
1574 {
1575     if (!d->name.isEmpty()) {
1576         return d->name;
1577     }
1578     QString name;
1579     switch(dimension()) {
1580         case XAxisDimension:
1581             name = QLatin1Char('x');
1582             break;
1583         case YAxisDimension:
1584             name = QLatin1Char('y');
1585             break;
1586         case ZAxisDimension:
1587             name = QLatin1Char('z');
1588             break;
1589     }
1590     int i = 1;
1591     foreach (Axis *axis, d->plotArea->axes()) {
1592         if (axis == this)
1593             break;
1594         if (axis->dimension() == dimension())
1595             i++;
1596     }
1597     if (i == 1)
1598         name = "primary-" + name;
1599     else if (i == 2)
1600         name = "secondary-" + name;
1601     // Usually, there's not more than two axes of the same dimension.
1602     // But use a fallback name here nevertheless.
1603     else
1604         name = QString::number(i) + '-' + name;
1605 
1606     return name;
1607 }
1608 
1609 void Axis::saveOdf(KoShapeSavingContext &context)
1610 {
1611     KoXmlWriter &bodyWriter = context.xmlWriter();
1612     KoGenStyles &mainStyles = context.mainStyles();
1613     bodyWriter.startElement("chart:axis");
1614 
1615     KoGenStyle axisStyle(KoGenStyle::ChartAutoStyle, "chart");
1616     axisStyle.addProperty("chart:logarithmic", scalingIsLogarithmic());
1617 
1618 
1619     axisStyle.addProperty("chart:reverse-direction", axisDirectionReversed());
1620     if (!d->axisPosition.isEmpty()) {
1621         axisStyle.addProperty("chart:axis-position", d->axisPosition);
1622     }
1623     if (!d->axisLabelsPosition.isEmpty()) {
1624         axisStyle.addProperty("chart:axis-label-position", d->axisLabelsPosition);
1625     }
1626 
1627     axisStyle.addProperty("chart:tick-marks-minor-inner", showInnerMinorTicks());
1628     axisStyle.addProperty("chart:tick-marks-minor-outer", showOuterMinorTicks());
1629     axisStyle.addProperty("chart:tick-marks-major-inner", showInnerMajorTicks());
1630     axisStyle.addProperty("chart:tick-marks-major-outer", showOuterMajorTicks());
1631 
1632     axisStyle.addProperty("chart:display-label", showLabels());
1633     axisStyle.addProperty("chart:text-overlap", showOverlappingDataLabels());
1634     axisStyle.addProperty("chart:visible", isVisible());
1635     if (dimension() == YAxisDimension) {
1636         axisStyle.addProperty("chart:gap-width", d->gapBetweenSets);
1637         axisStyle.addProperty("chart:overlap", -d->gapBetweenBars);
1638     }
1639 
1640     if (!d->useAutomaticMinimumRange) {
1641         const qreal minimum = orientation() == Qt::Vertical
1642                             ? d->kdPlane->verticalRange().first
1643                             : d->kdPlane->horizontalRange().first;
1644         axisStyle.addProperty("chart:minimum", (int)minimum);
1645     }
1646     if (!d->useAutomaticMaximumRange) {
1647         const qreal maximum = orientation() == Qt::Vertical
1648                             ? d->kdPlane->verticalRange().second
1649                             : d->kdPlane->horizontalRange().second;
1650         axisStyle.addProperty("chart:maximum", (int)maximum);
1651     }
1652 
1653     //axisStyle.addPropertyPt("chart:origin", origin);
1654 
1655     KChart::TextAttributes tatt =  kdAxis()->textAttributes();
1656     QPen pen = tatt.pen();
1657     axisStyle.addProperty("fo:font-color", pen.color().name(), KoGenStyle::TextType);
1658     axisStyle.addProperty("fo:font-family", tatt.font().family(), KoGenStyle::TextType);
1659     axisStyle.addPropertyPt("fo:font-size", fontSize(), KoGenStyle::TextType);
1660     if (font().bold()) {
1661         axisStyle.addProperty("fo:font-weight", "bold" , KoGenStyle::TextType);
1662         // TODO support other weights
1663     }
1664     if (font().italic()) {
1665         axisStyle.addProperty("fo:font-style", "italic" , KoGenStyle::TextType);
1666         // TODO oblique
1667     }
1668 
1669     const QString styleName = mainStyles.insert(axisStyle, "ch");
1670     bodyWriter.addAttribute("chart:style-name", styleName);
1671 
1672     // TODO scale: logarithmic/linear
1673     // TODO visibility
1674 
1675     if (dimension() == XAxisDimension)
1676         bodyWriter.addAttribute("chart:dimension", "x");
1677     else if (dimension() == YAxisDimension)
1678         bodyWriter.addAttribute("chart:dimension", "y");
1679 
1680     bodyWriter.addAttribute("chart:name", name());
1681 
1682     OdfHelper::saveOdfTitle(d->title, bodyWriter, "chart:title", context);
1683 
1684     if (plotArea()->proxyModel()->categoryDataRegion().isValid()) {
1685         bodyWriter.startElement("chart:categories");
1686         bodyWriter.addAttribute("table:cell-range-address", plotArea()->proxyModel()->categoryDataRegion().toString());
1687         bodyWriter.endElement();
1688     }
1689 
1690     if (showMajorGrid())
1691         saveOdfGrid(context, OdfMajorGrid);
1692     if (showMinorGrid())
1693         saveOdfGrid(context, OdfMinorGrid);
1694 
1695     bodyWriter.endElement(); // chart:axis
1696 }
1697 
1698 void Axis::saveOdfGrid(KoShapeSavingContext &context, OdfGridClass gridClass)
1699 {
1700     KoXmlWriter &bodyWriter = context.xmlWriter();
1701     KoGenStyles &mainStyles = context.mainStyles();
1702 
1703     KoGenStyle gridStyle(KoGenStyle::GraphicAutoStyle, "chart");
1704 
1705     KChart::GridAttributes attributes = d->kdPlane->gridAttributes(orientation());
1706     QPen gridPen = (gridClass == OdfMinorGrid ? attributes.subGridPen() : attributes.gridPen());
1707     KoOdfGraphicStyles::saveOdfStrokeStyle(gridStyle, mainStyles, gridPen);
1708 
1709     bodyWriter.startElement("chart:grid");
1710     bodyWriter.addAttribute("chart:class", gridClass == OdfMinorGrid ? "minor" : "major");
1711 
1712     bodyWriter.addAttribute("chart:style-name", mainStyles.insert(gridStyle, "ch"));
1713     bodyWriter.endElement(); // chart:grid
1714 }
1715 
1716 void Axis::update() const
1717 {
1718     if (d->kdBarDiagram) {
1719         d->kdBarDiagram->doItemsLayout();
1720         d->kdBarDiagram->update();
1721     }
1722 
1723     if (d->kdLineDiagram) {
1724         d->kdLineDiagram->doItemsLayout();
1725         d->kdLineDiagram->update();
1726     }
1727 
1728     if (d->kdStockDiagram) {
1729         d->kdStockDiagram->doItemsLayout();
1730         d->kdStockDiagram->update();
1731     }
1732 
1733     d->plotArea->parent()->requestRepaint();
1734 }
1735 
1736 KChart::CartesianAxis *Axis::kdAxis() const
1737 {
1738     return d->kdAxis;
1739 }
1740 
1741 KChart::AbstractCoordinatePlane *Axis::kdPlane() const
1742 {
1743     return d->kdPlane;
1744 }
1745 
1746 void Axis::plotAreaChartTypeChanged(ChartType newChartType)
1747 {
1748     if (dimension() != YAxisDimension)
1749         return;
1750 
1751     // Return if there's nothing to do
1752     if (newChartType == d->plotAreaChartType)
1753         return;
1754 
1755     if (d->dataSets.isEmpty()) {
1756         d->plotAreaChartType = newChartType;
1757         return;
1758     }
1759 
1760     //qDebug() << "changed ChartType";
1761 
1762     ChartType oldChartType = d->plotAreaChartType;
1763 
1764     debugChartAxis<<oldChartType<<"->"<<newChartType;
1765     // Change only the fill in case of type change from RadarChartType to FilledRadarChartType
1766     // or viceversa as rest of the properties remain same
1767     if (newChartType == RadarChartType && oldChartType == FilledRadarChartType) {
1768         d->kdRadarDiagram->setFillAlpha(0);
1769     } else if (newChartType == FilledRadarChartType && oldChartType == RadarChartType) {
1770         d->kdRadarDiagram->setFillAlpha(0.4);
1771     } else {
1772         KChart::AbstractDiagram *newDiagram = d->getDiagram(newChartType);
1773         if (newDiagram) {
1774             debugChartAxis<<"already exists:"<<newDiagram;
1775             // Some dataset(s) have been attached to this diagram,
1776             // we delete it to get a fresh start
1777             d->deleteDiagram(newDiagram);
1778         }
1779         newDiagram = d->getDiagramAndCreateIfNeeded(newChartType);
1780 
1781         KChartModel *newModel = dynamic_cast<KChartModel*>(newDiagram->model());
1782         // FIXME: This causes a crash on unimplemented types. We should
1783         //        handle that in some other way.
1784         Q_ASSERT(newModel);
1785 
1786         foreach (DataSet *dataSet, d->dataSets) {
1787             //if (dataSet->chartType() != LastChartType) {
1788                 dataSet->setChartType(LastChartType);
1789                 dataSet->setChartSubType(NoChartSubtype);
1790             //}
1791         }
1792 
1793         KChart::AbstractDiagram *oldDiagram = d->getDiagram(oldChartType);
1794         Q_ASSERT(oldDiagram);
1795         // We need to know the old model so that we can remove the data sets
1796         // from the old model that we added to the new model.
1797         KChartModel *oldModel = dynamic_cast<KChartModel*>(oldDiagram->model());
1798         Q_ASSERT(oldModel);
1799 
1800         foreach (DataSet *dataSet, d->dataSets) {
1801             if (dataSet->chartType() != LastChartType) {
1802                 continue;
1803             }
1804             newModel->addDataSet(dataSet);
1805             const int dataSetCount = oldModel->dataDirection() == Qt::Vertical
1806                                      ? oldModel->columnCount() : oldModel->rowCount();
1807             if (dataSetCount == oldModel->dataDimensions()) {
1808                 d->deleteDiagram(oldChartType);
1809             } else {
1810                 oldModel->removeDataSet(dataSet);
1811             }
1812         }
1813     }
1814 
1815     d->plotAreaChartType = newChartType;
1816 
1817     layoutPlanes();
1818 
1819     requestRepaint();
1820 }
1821 
1822 void Axis::plotAreaChartSubTypeChanged(ChartSubtype subType)
1823 {
1824     d->plotAreaChartSubType = subType;
1825     if (d->kdBarDiagram) {
1826         d->kdBarDiagram->setUnitSuffix("", d->kdBarDiagram->orientation());
1827     }
1828     switch (d->plotAreaChartType) {
1829     case BarChartType:
1830         if (d->kdBarDiagram) {
1831             KChart::BarDiagram::BarType type;
1832             switch (subType) {
1833             case StackedChartSubtype:
1834                 type = KChart::BarDiagram::Stacked; break;
1835             case PercentChartSubtype:
1836                 type = KChart::BarDiagram::Percent;
1837                 d->kdBarDiagram->setUnitSuffix("%", d->kdBarDiagram->orientation());
1838                 break;
1839             default:
1840                 type = KChart::BarDiagram::Normal;
1841             }
1842             d->kdBarDiagram->setType(type);
1843         }
1844         break;
1845     case LineChartType:
1846         if (d->kdLineDiagram) {
1847             KChart::LineDiagram::LineType type;
1848             switch (subType) {
1849             case StackedChartSubtype:
1850                 type = KChart::LineDiagram::Stacked; break;
1851             case PercentChartSubtype:
1852                 type = KChart::LineDiagram::Percent;
1853                 d->kdLineDiagram->setUnitSuffix("%", Qt::Vertical);
1854                 break;
1855             default:
1856                 type = KChart::LineDiagram::Normal;
1857             }
1858             d->kdLineDiagram->setType(type);
1859         }
1860         break;
1861     case AreaChartType:
1862         if (d->kdAreaDiagram) {
1863             KChart::LineDiagram::LineType type;
1864             switch (subType) {
1865             case StackedChartSubtype:
1866                 type = KChart::LineDiagram::Stacked; break;
1867             case PercentChartSubtype:
1868                 type = KChart::LineDiagram::Percent;
1869                 d->kdAreaDiagram->setUnitSuffix("%", Qt::Vertical);
1870                 break;
1871             default:
1872                 type = KChart::LineDiagram::Normal;
1873             }
1874             d->kdAreaDiagram->setType(type);
1875         }
1876         break;
1877     case RadarChartType:
1878     case FilledRadarChartType:
1879 #if 0 // FIXME: Stacked and Percent not supported by KChart
1880         if (d->kdRadarDiagram) {
1881             KChart::PolarDiagram::PolarType type;
1882             switch (subType) {
1883             case StackedChartSubtype:
1884                 type = KChart::PolarDiagram::Stacked; break;
1885             case PercentChartSubtype:
1886                 type = KChart::PolarDiagram::Percent; break;
1887             default:
1888                 type = KChart::PolarDiagram::Normal;
1889             }
1890             d->kdRadarDiagram->setType(type);
1891         }
1892 #endif
1893         break;
1894     case StockChartType:
1895         if (d->kdStockDiagram) {
1896             KChart::StockDiagram::Type type;
1897             switch (subType) {
1898             case CandlestickChartSubtype:
1899                 type = KChart::StockDiagram::Candlestick;
1900                 break;
1901             case OpenHighLowCloseChartSubtype:
1902                 type = KChart::StockDiagram::OpenHighLowClose;
1903                 break;
1904             default:
1905                 type = KChart::StockDiagram::HighLowClose;
1906                 break;
1907             }
1908             d->kdStockDiagram->setType(type);
1909         }
1910         break;
1911     default:;
1912         // FIXME: Implement more chart types
1913     }
1914     Q_FOREACH(DataSet* set,  d->dataSets) {
1915         set->setChartType(d->plotAreaChartType);
1916         set->setChartSubType(subType);
1917     }
1918 }
1919 
1920 void Axis::plotAreaIsVerticalChanged()
1921 {
1922     if (d->kdBarDiagram) {
1923         d->kdBarDiagram->setOrientation(d->plotArea->isVertical() ? Qt::Horizontal : Qt::Vertical);
1924     }
1925     updateKChartAxisPosition();
1926 }
1927 
1928 void Axis::Private::updatePosition()
1929 {
1930 //     // Is the first x or y axis?
1931 //     bool first = (dimension == XAxisDimension) ? plotArea->xAxis() == q
1932 //                                                : plotArea->yAxis() == q;
1933 //
1934 //     Position position;
1935 //     ItemType type = GenericItemType;
1936 //     if (q->orientation() == Qt::Horizontal) {
1937 //         position = first ? BottomPosition : TopPosition;
1938 //         type = first ? XAxisTitleType : SecondaryXAxisTitleType;
1939 //     } else {
1940 //         position = first ? StartPosition : EndPosition;
1941 //         type = first ? YAxisTitleType : SecondaryYAxisTitleType;
1942 //     }
1943 //     // KChart
1944 //     kdAxis->setPosition(PositionToKChartAxisPosition(position));
1945 //     ChartLayout *layout = plotArea->parent()->layout();
1946 //     layout->setPosition(title, position, type);
1947 //     layout->layout();
1948 //
1949 //     q->requestRepaint();
1950 }
1951 
1952 void Axis::registerAxis(Axis *axis)
1953 {
1954     if (d->kdBarDiagram) {
1955         d->kdBarDiagram->addAxis(axis->kdAxis());
1956         axis->registerDiagram(d->kdBarDiagram);
1957     }
1958     if (d->kdLineDiagram) {
1959         d->kdLineDiagram->addAxis(axis->kdAxis());
1960         axis->registerDiagram(d->kdLineDiagram);
1961     }
1962     if (d->kdAreaDiagram) {
1963         d->kdAreaDiagram->addAxis(axis->kdAxis());
1964         axis->registerDiagram(d->kdAreaDiagram);
1965     }
1966     if (d->kdScatterDiagram) {
1967         d->kdScatterDiagram->addAxis(axis->kdAxis());
1968         axis->registerDiagram(d->kdScatterDiagram);
1969     }
1970     if (d->kdStockDiagram) {
1971         d->kdStockDiagram->addAxis(axis->kdAxis());
1972         axis->registerDiagram(d->kdStockDiagram);
1973     }
1974     if (d->kdBubbleDiagram) {
1975         d->kdBubbleDiagram->addAxis(axis->kdAxis());
1976         axis->registerDiagram(d->kdBubbleDiagram);
1977     }
1978     // FIXME: Add all diagrams here
1979 
1980 }
1981 
1982 void Axis::registerDiagram(KChart::AbstractCartesianDiagram *diagram)
1983 {
1984     if (!d->diagrams.contains(diagram)) {
1985         d->diagrams << diagram;
1986     }
1987 }
1988 
1989 void Axis::Private::restoreDiagrams()
1990 {
1991     diagrams.removeAll(nullptr);
1992     for (KChart::AbstractCartesianDiagram *diag : diagrams) {
1993         diag->addAxis(kdAxis);
1994     }
1995 }
1996 
1997 void Axis::removeAxisFromDiagrams(bool clear)
1998 {
1999     // HACK to remove an x-axis from a y-axis diagram
2000     d->diagrams.removeAll(nullptr);
2001     for (KChart::AbstractCartesianDiagram *diag : d->diagrams) {
2002         diag->takeAxis(d->kdAxis);
2003     }
2004     if (clear) {
2005         d->diagrams.clear();
2006     }
2007 }
2008 
2009 void Axis::setThreeD(bool threeD)
2010 {
2011     // FIXME: Setting KD Chart attributes does not belong here. They should be
2012     // determined dynamically somehow.
2013     // KChart
2014     if (d->kdBarDiagram) {
2015         KChart::ThreeDBarAttributes attributes(d->kdBarDiagram->threeDBarAttributes());
2016         attributes.setEnabled(threeD);
2017         attributes.setDepth(15.0);
2018         attributes.setThreeDBrushEnabled(threeD);
2019         d->kdBarDiagram->setThreeDBarAttributes(attributes);
2020     }
2021 
2022     if (d->kdLineDiagram) {
2023         KChart::ThreeDLineAttributes attributes(d->kdLineDiagram->threeDLineAttributes());
2024         attributes.setEnabled(threeD);
2025         attributes.setDepth(15.0);
2026         attributes.setThreeDBrushEnabled(threeD);
2027         d->kdLineDiagram->setThreeDLineAttributes(attributes);
2028     }
2029 
2030     if (d->kdAreaDiagram) {
2031         KChart::ThreeDLineAttributes attributes(d->kdAreaDiagram->threeDLineAttributes());
2032         attributes.setEnabled(threeD);
2033         attributes.setDepth(15.0);
2034         attributes.setThreeDBrushEnabled(threeD);
2035         d->kdAreaDiagram->setThreeDLineAttributes(attributes);
2036     }
2037 
2038     if (d->kdCircleDiagram) {
2039         KChart::ThreeDPieAttributes attributes(d->kdCircleDiagram->threeDPieAttributes());
2040         attributes.setEnabled(threeD);
2041         attributes.setDepth(15.0);
2042         attributes.setThreeDBrushEnabled(threeD);
2043         d->kdCircleDiagram->setThreeDPieAttributes(attributes);
2044     }
2045 
2046     if (d->kdRingDiagram) {
2047         KChart::ThreeDPieAttributes attributes(d->kdRingDiagram->threeDPieAttributes());
2048         attributes.setEnabled(threeD);
2049         attributes.setDepth(15.0);
2050         attributes.setThreeDBrushEnabled(threeD);
2051         d->kdRingDiagram->setThreeDPieAttributes(attributes);
2052     }
2053 
2054     // The following types don't support 3D, at least not in KChart:
2055     // scatter, radar, stock, bubble, surface, gantt
2056 
2057     requestRepaint();
2058 }
2059 
2060 void Axis::requestRepaint() const
2061 {
2062     d->plotArea->requestRepaint();
2063 }
2064 
2065 void Axis::layoutPlanes()
2066 {
2067     d->kdPlane->layoutPlanes();
2068     d->kdPolarPlane->layoutPlanes();
2069     d->kdRadarPlane->layoutPlanes();
2070 }
2071 
2072 int Axis::gapBetweenBars() const
2073 {
2074     return d->gapBetweenBars;
2075 }
2076 
2077 void Axis::setGapBetweenBars(int percent)
2078 {
2079     // This method is also used to override KChart's default attributes.
2080     // Do not just return and do nothing if value doesn't differ from stored one.
2081     d->gapBetweenBars = percent;
2082 
2083     if (d->kdBarDiagram) {
2084         KChart::BarAttributes attributes = d->kdBarDiagram->barAttributes();
2085         attributes.setBarGapFactor((float)percent / 100.0);
2086         d->kdBarDiagram->setBarAttributes(attributes);
2087     }
2088 
2089     requestRepaint();
2090 }
2091 
2092 int Axis::gapBetweenSets() const
2093 {
2094     return d->gapBetweenSets;
2095 }
2096 
2097 void Axis::setGapBetweenSets(int percent)
2098 {
2099     // This method is also used to override KChart's default attributes.
2100     // Do not just return and do nothing if value doesn't differ from stored one.
2101     d->gapBetweenSets = percent;
2102 
2103     if (d->kdBarDiagram) {
2104         KChart::BarAttributes attributes = d->kdBarDiagram->barAttributes();
2105         attributes.setGroupGapFactor((float)percent / 100.0);
2106         d->kdBarDiagram->setBarAttributes(attributes);
2107     }
2108 
2109     requestRepaint();
2110 }
2111 
2112 void Axis::setAngleOffset(qreal angle)
2113 {
2114     // only set if we already have a diagram else the value will be picked up on creating the diagram
2115     if (d->kdPolarPlane->diagram()) {
2116         d->kdPolarPlane->setStartPosition(angle);
2117 
2118         requestRepaint();
2119     }
2120 }
2121 
2122 void Axis::setHoleSize(qreal value)
2123 {
2124     //TODO KChart does not support
2125 }
2126 
2127 QFont Axis::font() const
2128 {
2129     return d->kdAxis->textAttributes().font();
2130 }
2131 
2132 void Axis::setFont(const QFont &font)
2133 {
2134     // Set the KChart axis to use this font
2135     KChart::TextAttributes attr = d->kdAxis->textAttributes();
2136     attr.setFont(font);
2137     d->kdAxis->setTextAttributes(attr);
2138 }
2139 
2140 qreal Axis::fontSize() const
2141 {
2142     return d->kdAxis->textAttributes().fontSize().value();
2143 }
2144 
2145 void Axis::setFontSize(qreal size)
2146 {
2147     // KChart has its own fontsize storage, it does not use QFont
2148     KChart::TextAttributes attributes = d->kdAxis->textAttributes();
2149     attributes.setFontSize(KChart::Measure(size, KChartEnums::MeasureCalculationModeAbsolute));
2150     d->kdAxis->setTextAttributes(attributes);
2151 
2152     // Keep font in sync
2153     QFont f = font();
2154     f.setPointSizeF(size);
2155     setFont(f);
2156 }
2157 
2158 bool Axis::isVisible() const
2159 {
2160     return d->isVisible;
2161 }
2162 
2163 void Axis::setVisible(bool visible)
2164 {
2165     debugChartAxis<<d->isVisible<<"->"<<visible<<d->kdBarDiagram;
2166     d->isVisible = visible;
2167     if (visible) {
2168         d->restoreDiagrams();
2169     } else {
2170         removeAxisFromDiagrams();
2171     }
2172 }
2173 
2174 KoOdfNumberStyles::NumericStyleFormat *Axis::numericStyleFormat() const
2175 {
2176     return d->numericStyleFormat;
2177 }
2178 
2179 void Axis::SetNumericStyleFormat(KoOdfNumberStyles::NumericStyleFormat *numericStyleFormat) const
2180 {
2181     delete d->numericStyleFormat;
2182     d->numericStyleFormat = numericStyleFormat;
2183 }
2184 
2185 void Axis::setOdfAxisPosition(const QString &odfpos)
2186 {
2187     d->axisPosition = odfpos;
2188 }
2189 
2190 QString Axis::odfAxisPosition() const
2191 {
2192     return d->axisPosition;
2193 }
2194 
2195 void Axis::updateKChartAxisPosition()
2196 {
2197     if (!isCartesian(d->plotArea->chartType())) {
2198         debugChartAxis<<name()<<"Not a cartesian chart"<<d->plotArea->chartType();
2199         return;
2200     }
2201     KChart::CartesianAxis::Position pos;
2202     if (d->plotArea->xAxis() == this) {
2203         if (d->plotArea->isVertical()) {
2204             pos = KChart::CartesianAxis::Left;
2205             if (d->axisPosition == "end") {
2206                 pos = KChart::CartesianAxis::Right;
2207             }
2208             Axis *yAxis = d->plotArea->yAxis();
2209             if (yAxis && yAxis->axisDirectionReversed()) {
2210                 pos = pos == KChart::CartesianAxis::Left ? KChart::CartesianAxis::Right : KChart::CartesianAxis::Left;
2211             }
2212         } else {
2213             pos = KChart::CartesianAxis::Bottom;
2214             if (d->axisPosition == "end") {
2215                 pos = KChart::CartesianAxis::Top;
2216             }
2217             Axis *yAxis = d->plotArea->yAxis();
2218             if (yAxis && yAxis->axisDirectionReversed()) {
2219                 pos = pos == KChart::CartesianAxis::Bottom ? KChart::CartesianAxis::Top : KChart::CartesianAxis::Bottom;
2220             }
2221         }
2222         d->kdAxis->setPosition(pos);
2223     } else if (d->plotArea->yAxis() == this) {
2224         if (d->plotArea->isVertical()) {
2225             pos = KChart::CartesianAxis::Bottom;
2226             if (d->axisPosition == "end") {
2227                 pos = KChart::CartesianAxis::Top;
2228             }
2229             Axis *xAxis = d->plotArea->xAxis();
2230             if (xAxis && xAxis->axisDirectionReversed()) {
2231                 pos = pos == KChart::CartesianAxis::Bottom ? KChart::CartesianAxis::Top : KChart::CartesianAxis::Bottom;
2232             }
2233         } else {
2234             pos = KChart::CartesianAxis::Left;
2235             if (d->axisPosition == "end") {
2236                 pos = KChart::CartesianAxis::Right;
2237             }
2238             Axis *xAxis = d->plotArea->xAxis();
2239             if (xAxis && xAxis->axisDirectionReversed()) {
2240                 pos = pos == KChart::CartesianAxis::Left ? KChart::CartesianAxis::Right : KChart::CartesianAxis::Left;
2241             }
2242         }
2243         d->kdAxis->setPosition(pos);
2244     } else if (d->plotArea->secondaryXAxis() == this) {
2245         if (d->plotArea->isVertical()) {
2246             pos = KChart::CartesianAxis::Right;
2247             if (d->axisPosition == "start") {
2248                 pos = KChart::CartesianAxis::Left;
2249             }
2250             Axis *yAxis = d->plotArea->yAxis();
2251             if (yAxis && yAxis->axisDirectionReversed()) {
2252                 pos = pos == KChart::CartesianAxis::Left ? KChart::CartesianAxis::Right : KChart::CartesianAxis::Left;
2253             }
2254         } else {
2255             pos = KChart::CartesianAxis::Top;
2256             if (d->axisPosition == "start") {
2257                 pos = KChart::CartesianAxis::Bottom;
2258             }
2259             Axis *yAxis = d->plotArea->yAxis();
2260             if (yAxis && yAxis->axisDirectionReversed()) {
2261                 pos = pos == KChart::CartesianAxis::Top ? KChart::CartesianAxis::Bottom : KChart::CartesianAxis::Top;
2262             }
2263         }
2264         d->kdAxis->setPosition(pos);
2265     } else if (d->plotArea->secondaryYAxis() == this) {
2266         if (d->plotArea->isVertical()) {
2267             pos = KChart::CartesianAxis::Top;
2268             if (d->axisPosition == "start") {
2269                 pos = KChart::CartesianAxis::Bottom;
2270             }
2271             Axis *xAxis = d->plotArea->xAxis();
2272             if (xAxis && xAxis->axisDirectionReversed()) {
2273                 pos = pos == KChart::CartesianAxis::Bottom ? KChart::CartesianAxis::Top : KChart::CartesianAxis::Bottom;
2274             }
2275         } else {
2276             pos = KChart::CartesianAxis::Right;
2277             if (d->axisPosition == "start") {
2278                 pos = KChart::CartesianAxis::Left;
2279             }
2280             Axis *xAxis = d->plotArea->xAxis();
2281             if (xAxis && xAxis->axisDirectionReversed()) {
2282                 pos = pos == KChart::CartesianAxis::Right ? KChart::CartesianAxis::Left : KChart::CartesianAxis::Right;
2283             }
2284         }
2285         d->kdAxis->setPosition(pos);
2286     }
2287     debugChartAxis<<name()<<d->kdAxis<<pos<<d->kdAxis->isAbscissa();
2288     d->plotArea->plotAreaUpdate();
2289 }
2290 
2291 CartesianAxis::Position Axis::kchartAxisPosition() const
2292 {
2293     return d->kdAxis->position();
2294 }
2295 
2296 CartesianAxis::Position Axis::actualAxisPosition() const
2297 {
2298     CartesianAxis::Position pos = d->kdAxis->position();
2299     if (d->plotArea->isVertical()) {
2300         switch (pos) {
2301             case KChart::CartesianAxis::Bottom:
2302                 pos = KChart::CartesianAxis::Left;
2303                 break;
2304             case KChart::CartesianAxis::Top:
2305                 pos = KChart::CartesianAxis::Right;
2306                 break;
2307             case KChart::CartesianAxis::Left:
2308                 pos = KChart::CartesianAxis::Bottom;
2309                 break;
2310             case KChart::CartesianAxis::Right:
2311                 pos = KChart::CartesianAxis::Top;
2312                 break;
2313         }
2314     }
2315     return pos;
2316 }
2317 
2318 bool Axis::axisDirectionReversed() const
2319 {
2320     bool reversed = false;
2321     KChart::CartesianCoordinatePlane *plane = dynamic_cast<KChart::CartesianCoordinatePlane*>(kdPlane());
2322     if (plane) {
2323         if (orientation() == Qt::Horizontal)
2324             reversed = plane->isHorizontalRangeReversed();
2325         else // Qt::Vertical
2326             reversed = plane->isVerticalRangeReversed();
2327     }
2328     return reversed;
2329 }
2330 
2331 void Axis::setOdfAxisLabelsPosition(const QString &odfpos)
2332 {
2333     d->axisLabelsPosition = odfpos;
2334 }
2335 
2336 QString Axis::odfAxisLabelsPosition() const
2337 {
2338     return d->axisLabelsPosition;
2339 }
2340 
2341 void Axis::updateKChartStockAttributes()
2342 {
2343     if (d->kdStockDiagram) {
2344         d->kdStockDiagram->setLowHighLinePen(d->plotArea->stockRangeLinePen());
2345         d->kdStockDiagram->setUpTrendCandlestickBrush(d->plotArea->stockGainBrush());
2346         d->kdStockDiagram->setDownTrendCandlestickBrush(d->plotArea->stockLossBrush());
2347         d->kdStockDiagram->setUpTrendCandlestickPen(d->plotArea->stockRangeLinePen());
2348         d->kdStockDiagram->setDownTrendCandlestickPen(d->plotArea->stockRangeLinePen());
2349     }
2350 }
2351 
2352 QDebug operator<<(QDebug dbg, KoChart::Axis *a)
2353 {
2354     dbg.nospace().noquote() <<"Axis["<<a->name()<<']';
2355     return dbg.space().quote();
2356 }