1 /* This file is part of the KDE project
2 
3    Copyright 2007 Johannes Simon <johannes.simon@gmail.com>
4    Copyright 2009 Inge Wallin    <inge@lysator.liu.se>
5    Copyright 2017 Dag Andersen   <danders@get2net.dk>
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public
9    License as published by the Free Software Foundation; either
10    version 2 of the License, or (at your option) any later version.
11 
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16 
17    You should have received a copy of the GNU Library General Public License
18    along with this library; see the file COPYING.LIB.  If not, write to
19    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20    Boston, MA 02110-1301, USA.
21 */
22 
23 // Own
24 #include "Axis.h"
25 
26 // Qt
27 #include <QList>
28 #include <QString>
29 #include <QTextDocument>
30 #include <QPointer>
31 
32 // Calligra
33 #include <KoShapeLoadingContext.h>
34 #include <KoShapeSavingContext.h>
35 #include <KoShapeRegistry.h>
36 #include <KoXmlReader.h>
37 #include <KoXmlWriter.h>
38 #include <KoGenStyles.h>
39 #include <KoXmlNS.h>
40 #include <KoTextShapeData.h>
41 #include <KoOdfStylesReader.h>
42 #include <KoUnit.h>
43 #include <KoStyleStack.h>
44 #include <KoOdfLoadingContext.h>
45 #include <KoCharacterStyle.h>
46 #include <KoOdfGraphicStyles.h>
47 #include <KoOdfWorkaround.h>
48 #include <KoTextDocumentLayout.h>
49 #include <KoOdfNumberStyles.h>
50 
51 // KChart
52 #include <KChartChart>
53 #include <KChartLegend>
54 #include <KChartCartesianAxis>
55 #include <KChartCartesianCoordinatePlane>
56 #include <KChartRadarCoordinatePlane>
57 #include <KChartGridAttributes>
58 #include <KChartBarDiagram>
59 #include <KChartLineDiagram>
60 #include <KChartPieDiagram>
61 #include <KChartPlotter>
62 #include <KChartStockDiagram>
63 #include <KChartRingDiagram>
64 #include <KChartRadarDiagram>
65 #include <KChartBarAttributes>
66 #include <KChartPieAttributes>
67 #include <KChartThreeDBarAttributes>
68 #include <KChartThreeDPieAttributes>
69 #include <KChartThreeDLineAttributes>
70 #include <KChartBackgroundAttributes>
71 #include <KChartRulerAttributes>
72 #include <kchart_version.h>
73 
74 // KoChart
75 #include "PlotArea.h"
76 #include "KChartModel.h"
77 #include "DataSet.h"
78 #include "Legend.h"
79 #include "KChartConvertions.h"
80 #include "ChartProxyModel.h"
81 #include "TextLabelDummy.h"
82 #include "ChartLayout.h"
83 #include "OdfLoadingHelper.h"
84 #include "OdfHelper.h"
85 #include "ChartDebug.h"
86 
87 
88 using namespace KoChart;
89 
90 class Axis::Private
91 {
92 public:
93     Private(Axis *axis, AxisDimension dim);
94     ~Private();
95 
96     void adjustAllDiagrams();
97     /// Updates the axis position in the chart's layout
98     /// FIXME: We should instead implement a generic layout position method
99     /// and have the layout find out about our position when it changes.
100     void updatePosition();
101 
102     void registerDiagram(KChart::AbstractDiagram *diagram);
103 
104     KChart::AbstractDiagram *getDiagramAndCreateIfNeeded(ChartType chartType);
105     KChart::AbstractDiagram *getDiagram(ChartType chartType);
106     void deleteDiagram(ChartType chartType);
107     void deleteDiagram(KChart::AbstractDiagram *diagram);
108 
109     void restoreDiagrams();
110 
111     void createBarDiagram();
112     void createLineDiagram();
113     void createAreaDiagram();
114     void createCircleDiagram();
115     void createRingDiagram();
116     void createRadarDiagram(bool filled);
117     void createScatterDiagram();
118     void createStockDiagram();
119     void createBubbleDiagram();
120     void createSurfaceDiagram();
121     void createGanttDiagram();
122     void applyAttributesToDataSet(DataSet* set, ChartType newCharttype);
123 
124     // Pointer to Axis that owns this Private instance
125     Axis * const q;
126 
127     PlotArea *plotArea;
128 
129     const AxisDimension dimension;
130 
131     KoShape *title;
132     TextLabelData *titleData;
133 
134     /// FIXME: Unused variable 'id', including id() getter
135     QString id;
136     QList<DataSet*> dataSets;
137     qreal majorInterval;
138     int minorIntervalDivisor;
139     bool showInnerMinorTicks;
140     bool showOuterMinorTicks;
141     bool showInnerMajorTicks;
142     bool showOuterMajorTicks;
143     bool logarithmicScaling;
144     bool showMajorGrid;
145     bool showMinorGrid;
146     bool useAutomaticMajorInterval;
147     bool useAutomaticMinorInterval;
148     bool useAutomaticMinimumRange;
149     bool useAutomaticMaximumRange;
150 
151     KChart::CartesianAxis            *const kdAxis;
152     KChart::CartesianCoordinatePlane *kdPlane;
153     KChart::PolarCoordinatePlane     *kdPolarPlane;
154     KChart::RadarCoordinatePlane     *kdRadarPlane;
155     KoOdfNumberStyles::NumericStyleFormat *numericStyleFormat;
156 
157     QList<QPointer<KChart::AbstractCartesianDiagram> > diagrams;
158 
159     QPointer<KChart::BarDiagram> kdBarDiagram;
160     QPointer<KChart::LineDiagram> kdLineDiagram;
161     QPointer<KChart::LineDiagram> kdAreaDiagram;
162     QPointer<KChart::PieDiagram>  kdCircleDiagram;
163     QPointer<KChart::RingDiagram>  kdRingDiagram;
164     QPointer<KChart::RadarDiagram> kdRadarDiagram;
165     QPointer<KChart::Plotter>     kdScatterDiagram;
166     QPointer<KChart::StockDiagram> kdStockDiagram;
167     QPointer<KChart::Plotter>  kdBubbleDiagram;
168     // FIXME BUG: Somehow we need to visualize something for these
169     //            missing chart types.  We have some alternatives:
170     //            1. Show an empty area
171     //            2. Show a text "unsupported chart type"
172     //            3. Exchange for something else, e.g. a bar chart.
173     //            ... More?
174     //
175     // NOTE: Whatever we do, we should always store the data so that
176     //       it can be saved back into the file for a perfect
177     //       roundtrip.
178     QPointer<KChart::BarDiagram> kdSurfaceDiagram;
179     QPointer<KChart::BarDiagram> kdGanttDiagram;
180 
181     ChartType     plotAreaChartType;
182     ChartSubtype  plotAreaChartSubType;
183 
184     // If KChart::LineDiagram::centerDataPoints() property is set to true,
185     // the data points drawn in a line (i.e., also an area) diagram start at
186     // an offset of 0.5, that is, in the middle of a column in the diagram.
187     // Set flag to true if at least one dataset is attached to this axis
188     // that belongs to a horizontal bar chart
189     bool centerDataPoints;
190 
191     int gapBetweenBars;
192     int gapBetweenSets;
193 
194     // TODO: Save
195     // See ODF v1.2 $19.12 (chart:display-label)
196     bool showLabels;
197     bool showOverlappingDataLabels;
198 
199     bool isVisible;
200     QString name;
201 
202     QString axisPosition;
203     QString axisLabelsPosition;
204 
205 };
206 
207 class CartesianAxis : public KChart::CartesianAxis
208 {
209 public:
CartesianAxis(KoChart::Axis * _axis)210     CartesianAxis(KoChart::Axis *_axis) : KChart::CartesianAxis(), axis(_axis) {}
~CartesianAxis()211     virtual ~CartesianAxis() {}
customizedLabel(const QString & label) const212     const QString customizedLabel(const QString& label) const override {
213         if (KoOdfNumberStyles::NumericStyleFormat *n = axis->numericStyleFormat())
214             return KoOdfNumberStyles::format(label, *n);
215         return label;
216     }
217 private:
218     KoChart::Axis *axis;
219 };
220 
Private(Axis * axis,AxisDimension dim)221 Axis::Private::Private(Axis *axis, AxisDimension dim)
222     : q(axis)
223     , dimension(dim)
224     , kdAxis(new CartesianAxis(axis))
225     , kdPlane(0)
226     , kdPolarPlane(0)
227     , kdRadarPlane(0)
228     , numericStyleFormat(0)
229 {
230     centerDataPoints = false;
231 
232     gapBetweenBars = 0;
233     gapBetweenSets = 100;
234 
235     isVisible = true;
236 
237     useAutomaticMajorInterval = true;
238     useAutomaticMinorInterval = true;
239     useAutomaticMinimumRange = true;
240     useAutomaticMaximumRange = true;
241 
242     majorInterval = 2;
243     minorIntervalDivisor = 1;
244 
245     showMajorGrid = false;
246     showMinorGrid = false;
247 
248     logarithmicScaling = false;
249 
250     showInnerMinorTicks = false;
251     showOuterMinorTicks = false;
252     showInnerMajorTicks = false;
253     showOuterMajorTicks = true;
254 
255     showOverlappingDataLabels = false;
256     showLabels = true;
257 
258     title = 0;
259     titleData = 0;
260 
261     KChart::RulerAttributes attr = kdAxis->rulerAttributes();
262     attr.setShowRulerLine(true);
263     attr.setRulerLinePen(QPen());
264     kdAxis->setRulerAttributes(attr);
265 }
266 
~Private()267 Axis::Private::~Private()
268 {
269     Q_ASSERT(plotArea);
270 
271     q->removeAxisFromDiagrams();
272 
273     delete kdBarDiagram;
274     delete kdLineDiagram;
275     delete kdAreaDiagram;
276     delete kdCircleDiagram;
277     delete kdRingDiagram;
278     delete kdRadarDiagram;
279     delete kdScatterDiagram;
280     delete kdStockDiagram;
281     delete kdBubbleDiagram;
282     delete kdSurfaceDiagram;
283     delete kdGanttDiagram;
284 
285     delete numericStyleFormat;
286 
287     delete kdAxis;
288 
289     foreach(DataSet *dataSet, dataSets)
290         dataSet->setAttachedAxis(0);
291 }
292 
registerDiagram(KChart::AbstractDiagram * diagram)293 void Axis::Private::registerDiagram(KChart::AbstractDiagram *diagram)
294 {
295     QObject::connect(plotArea->proxyModel(), SIGNAL(columnsInserted(QModelIndex,int,int)),
296                      diagram->model(), SLOT(slotColumnsInserted(QModelIndex,int,int)));
297 
298     QObject::connect(diagram, SIGNAL(propertiesChanged()),
299                      plotArea, SLOT(plotAreaUpdate()));
300     QObject::connect(diagram, SIGNAL(layoutChanged(AbstractDiagram*)),
301                      plotArea, SLOT(plotAreaUpdate()));
302     QObject::connect(diagram, SIGNAL(modelsChanged()),
303                      plotArea, SLOT(plotAreaUpdate()));
304     QObject::connect(diagram, SIGNAL(dataHidden()),
305                      plotArea, SLOT(plotAreaUpdate()));
306 }
307 
getDiagramAndCreateIfNeeded(ChartType chartType)308 KChart::AbstractDiagram *Axis::Private::getDiagramAndCreateIfNeeded(ChartType chartType)
309 {
310     KChart::AbstractDiagram *diagram = 0;
311 
312     switch (chartType) {
313     case BarChartType:
314         if (!kdBarDiagram)
315             createBarDiagram();
316         diagram = kdBarDiagram;
317         break;
318     case LineChartType:
319         if (!kdLineDiagram)
320             createLineDiagram();
321         diagram = kdLineDiagram;
322         break;
323     case AreaChartType:
324         if (!kdAreaDiagram)
325             createAreaDiagram();
326         diagram = kdAreaDiagram;
327         break;
328     case CircleChartType:
329         if (!kdCircleDiagram)
330             createCircleDiagram();
331         diagram = kdCircleDiagram;
332         break;
333     case RingChartType:
334         if (!kdRingDiagram)
335             createRingDiagram();
336         diagram = kdRingDiagram;
337         break;
338     case RadarChartType:
339     case FilledRadarChartType:
340         if (!kdRadarDiagram)
341             createRadarDiagram(chartType == FilledRadarChartType);
342         diagram = kdRadarDiagram;
343         break;
344     case ScatterChartType:
345         if (!kdScatterDiagram)
346             createScatterDiagram();
347         diagram = kdScatterDiagram;
348         break;
349     case StockChartType:
350         if (!kdStockDiagram)
351             createStockDiagram();
352         diagram = kdStockDiagram;
353         break;
354     case BubbleChartType:
355         if (!kdBubbleDiagram)
356             createBubbleDiagram();
357         diagram = kdBubbleDiagram;
358         break;
359     case SurfaceChartType:
360         if (!kdSurfaceDiagram)
361             createSurfaceDiagram();
362         diagram = kdSurfaceDiagram;
363         break;
364     case GanttChartType:
365         if (!kdGanttDiagram)
366             createGanttDiagram();
367         diagram = kdGanttDiagram;
368         break;
369     default:
370         ;
371     }
372     diagram->setObjectName(q->name()); // for debug
373     adjustAllDiagrams();
374     debugChartAxis<<q->name()<<"diagram"<<diagram<<"for"<<chartType;
375     return diagram;
376 }
377 
378 /**
379  * Returns currently used internal KChart diagram for the specified chart type
380  */
getDiagram(ChartType chartType)381 KChart::AbstractDiagram *Axis::Private::getDiagram(ChartType chartType)
382 {
383     switch (chartType) {
384         case BarChartType:
385             return kdBarDiagram;
386         case LineChartType:
387             return kdLineDiagram;
388         case AreaChartType:
389             return kdAreaDiagram;
390         case CircleChartType:
391             return kdCircleDiagram;
392         case RingChartType:
393             return kdRingDiagram;
394         case RadarChartType:
395         case FilledRadarChartType:
396             return kdRadarDiagram;
397         case ScatterChartType:
398             return kdScatterDiagram;
399         case StockChartType:
400             return kdStockDiagram;
401         case BubbleChartType:
402             return kdBubbleDiagram;
403         case SurfaceChartType:
404             return kdSurfaceDiagram;
405         case GanttChartType:
406             return kdGanttDiagram;
407         case LastChartType:
408             return 0;
409         // Compiler warning for unhandled chart type is intentional.
410     }
411     Q_ASSERT(!"Unhandled chart type");
412     return 0;
413 }
414 
deleteDiagram(KChart::AbstractDiagram * diagram)415 void Axis::Private::deleteDiagram(KChart::AbstractDiagram *diagram)
416 {
417     Q_ASSERT(diagram);
418     if (diagram->coordinatePlane()) {
419         diagram->coordinatePlane()->takeDiagram(diagram);
420     }
421     delete diagram;
422     adjustAllDiagrams();
423 }
424 
deleteDiagram(ChartType chartType)425 void Axis::Private::deleteDiagram(ChartType chartType)
426 {
427     KChart::AbstractDiagram *diagram = getDiagram(chartType);
428     if (diagram) {
429         deleteDiagram(diagram);
430     }
431 }
432 
433 
createBarDiagram()434 void Axis::Private::createBarDiagram()
435 {
436     Q_ASSERT(kdBarDiagram == 0);
437 
438     kdBarDiagram = new KChart::BarDiagram(plotArea->kdChart(), kdPlane);
439     KChartModel *model = new KChartModel(plotArea, kdBarDiagram);
440     kdBarDiagram->setModel(model);
441     registerDiagram(kdBarDiagram);
442 
443     // By 'vertical', KChart means the orientation of a chart's bars,
444     // not the orientation of the x axis.
445     kdBarDiagram->setOrientation(plotArea->isVertical() ? Qt::Horizontal : Qt::Vertical);
446     kdBarDiagram->setPen(QPen(Qt::black, 0.0));
447 
448     kdBarDiagram->setAllowOverlappingDataValueTexts(showOverlappingDataLabels);
449 
450     if (plotAreaChartSubType == StackedChartSubtype)
451         kdBarDiagram->setType(KChart::BarDiagram::Stacked);
452     else if (plotAreaChartSubType == PercentChartSubtype) {
453         kdBarDiagram->setType(KChart::BarDiagram::Percent);
454         kdBarDiagram->setUnitSuffix("%", kdBarDiagram->orientation());
455     }
456 
457     if (isVisible) {
458         kdBarDiagram->addAxis(kdAxis);
459         q->registerDiagram(kdBarDiagram);
460     }
461     kdPlane->addDiagram(kdBarDiagram);
462 
463     Q_ASSERT(plotArea);
464     foreach (Axis *axis, plotArea->axes()) {
465         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
466             kdBarDiagram->addAxis(axis->kdAxis());
467             axis->registerDiagram(kdBarDiagram);
468         }
469     }
470 
471     // Set default bar diagram attributes
472     q->setGapBetweenBars(gapBetweenBars);
473     q->setGapBetweenSets(gapBetweenSets);
474 
475     // Propagate existing settings
476     KChart::ThreeDBarAttributes attributes(kdBarDiagram->threeDBarAttributes());
477     attributes.setEnabled(plotArea->isThreeD());
478     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
479     kdBarDiagram->setThreeDBarAttributes(attributes);
480 
481     q->plotAreaIsVerticalChanged();
482 
483     plotArea->parent()->legend()->kdLegend()->addDiagram(kdBarDiagram);
484 }
485 
createLineDiagram()486 void Axis::Private::createLineDiagram()
487 {
488     Q_ASSERT(kdLineDiagram == 0);
489 
490     kdLineDiagram = new KChart::LineDiagram(plotArea->kdChart(), kdPlane);
491     KChartModel *model = new KChartModel(plotArea, kdLineDiagram);
492     kdLineDiagram->setModel(model);
493     registerDiagram(kdLineDiagram);
494 
495     kdLineDiagram->setAllowOverlappingDataValueTexts(showOverlappingDataLabels);
496 
497     if (plotAreaChartSubType == StackedChartSubtype)
498         kdLineDiagram->setType(KChart::LineDiagram::Stacked);
499     else if (plotAreaChartSubType == PercentChartSubtype)
500         kdLineDiagram->setType(KChart::LineDiagram::Percent);
501 
502     if (isVisible) {
503         kdLineDiagram->addAxis(kdAxis);
504         q->registerDiagram(kdLineDiagram);
505     }
506     kdPlane->addDiagram(kdLineDiagram);
507 
508     Q_ASSERT(plotArea);
509     foreach (Axis *axis, plotArea->axes()) {
510         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
511             kdLineDiagram->addAxis(axis->kdAxis());
512             axis->registerDiagram(kdLineDiagram);
513         }
514     }
515 
516     // Propagate existing settings
517     KChart::ThreeDLineAttributes attributes(kdLineDiagram->threeDLineAttributes());
518     attributes.setEnabled(plotArea->isThreeD());
519     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
520     kdLineDiagram->setThreeDLineAttributes(attributes);
521 
522     KChart::LineAttributes lineAttr = kdLineDiagram->lineAttributes();
523     lineAttr.setMissingValuesPolicy(KChart::LineAttributes::MissingValuesHideSegments);
524     kdLineDiagram->setLineAttributes(lineAttr);
525 
526     plotArea->parent()->legend()->kdLegend()->addDiagram(kdLineDiagram);
527 }
528 
createAreaDiagram()529 void Axis::Private::createAreaDiagram()
530 {
531     Q_ASSERT(kdAreaDiagram == 0);
532 
533     kdAreaDiagram = new KChart::LineDiagram(plotArea->kdChart(), kdPlane);
534     KChartModel *model = new KChartModel(plotArea, kdAreaDiagram);
535     kdAreaDiagram->setModel(model);
536     registerDiagram(kdAreaDiagram);
537     KChart::LineAttributes attr = kdAreaDiagram->lineAttributes();
538     // Draw the area under the lines. This makes this diagram an area chart.
539     attr.setDisplayArea(true);
540     kdAreaDiagram->setLineAttributes(attr);
541     kdAreaDiagram->setPen(QPen(Qt::black, 0.0));
542     // KD Chart by default draws the first data set as last line in a normal
543     // line diagram, we however want the first series to appear in front.
544     kdAreaDiagram->setReverseDatasetOrder(true);
545 
546     kdAreaDiagram->setAllowOverlappingDataValueTexts(showOverlappingDataLabels);
547 
548     if (plotAreaChartSubType == StackedChartSubtype)
549         kdAreaDiagram->setType(KChart::LineDiagram::Stacked);
550     else if (plotAreaChartSubType == PercentChartSubtype)
551     {
552         kdAreaDiagram->setType(KChart::LineDiagram::Percent);
553         kdAreaDiagram->setUnitSuffix("%", Qt::Vertical);
554     }
555 
556     if (isVisible) {
557         kdAreaDiagram->addAxis(kdAxis);
558         q->registerDiagram(kdAreaDiagram);
559     }
560     kdPlane->addDiagram(kdAreaDiagram);
561 
562     Q_ASSERT(plotArea);
563     foreach (Axis *axis, plotArea->axes()) {
564         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
565             kdAreaDiagram->addAxis(axis->kdAxis());
566             axis->registerDiagram(kdAreaDiagram);
567         }
568     }
569 
570     // Propagate existing settings
571     KChart::ThreeDLineAttributes attributes(kdAreaDiagram->threeDLineAttributes());
572     attributes.setEnabled(plotArea->isThreeD());
573     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
574     kdAreaDiagram->setThreeDLineAttributes(attributes);
575 
576     plotArea->parent()->legend()->kdLegend()->addDiagram(kdAreaDiagram);
577 }
578 
createCircleDiagram()579 void Axis::Private::createCircleDiagram()
580 {
581     Q_ASSERT(kdCircleDiagram == 0);
582 
583     kdCircleDiagram = new KChart::PieDiagram(plotArea->kdChart(), kdPolarPlane);
584     KChartModel *model = new KChartModel(plotArea, kdCircleDiagram);
585     kdCircleDiagram->setModel(model);
586     registerDiagram(kdCircleDiagram);
587 
588     model->setDataDirection(Qt::Horizontal);
589 
590     plotArea->parent()->legend()->kdLegend()->addDiagram(kdCircleDiagram);
591     kdPolarPlane->addDiagram(kdCircleDiagram);
592 
593     // Propagate existing settings
594     KChart::ThreeDPieAttributes attributes(kdCircleDiagram->threeDPieAttributes());
595     attributes.setEnabled(plotArea->isThreeD());
596     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
597     kdCircleDiagram->setThreeDPieAttributes(attributes);
598 
599     // Initialize with default values that are specified in PlotArea
600     // Note: KChart takes an int here, though ODF defines the offset to be a double.
601     kdPolarPlane->setStartPosition((int)plotArea->angleOffset());
602 }
603 
createRingDiagram()604 void Axis::Private::createRingDiagram()
605 {
606     Q_ASSERT(kdRingDiagram == 0);
607 
608     kdRingDiagram = new KChart::RingDiagram(plotArea->kdChart(), kdPolarPlane);
609     KChartModel *model = new KChartModel(plotArea, kdRingDiagram);
610     kdRingDiagram->setModel(model);
611     registerDiagram(kdRingDiagram);
612 
613     model->setDataDirection(Qt::Horizontal);
614 
615     plotArea->parent()->legend()->kdLegend()->addDiagram(kdRingDiagram);
616     kdPolarPlane->addDiagram(kdRingDiagram);
617 
618     // Propagate existing settings
619     KChart::ThreeDPieAttributes attributes(kdRingDiagram->threeDPieAttributes());
620     attributes.setEnabled(plotArea->isThreeD());
621     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
622     kdRingDiagram->setThreeDPieAttributes(attributes);
623 
624     // Initialize with default values that are specified in PlotArea
625     // Note: KChart takes an int here, though ODF defines the offset to be a double.
626     kdPolarPlane->setStartPosition((int)plotArea->angleOffset());
627 }
628 
createRadarDiagram(bool filled)629 void Axis::Private::createRadarDiagram(bool filled)
630 {
631     Q_ASSERT(kdRadarDiagram == 0);
632 
633     //kdRadarDiagramModel->setDataDimensions(2);
634     //kdRadarDiagramModel->setDataDirection(Qt::Horizontal);
635 
636     kdRadarDiagram = new KChart::RadarDiagram(plotArea->kdChart(), kdRadarPlane);
637     KChartModel *model = new KChartModel(plotArea, kdRadarDiagram);
638     kdRadarDiagram->setModel(model);
639     registerDiagram(kdRadarDiagram);
640 
641     kdRadarDiagram->setCloseDatasets(true);
642 
643     if (filled) {
644         // Don't use a solid fill of 1.0 but a more transparent one so the
645         // grid and the data-value-labels are still visible plus it provides
646         // a better look (other areas can still be seen) even if it's slightly
647         // different from what OO.org does.
648         kdRadarDiagram->setFillAlpha(0.4);
649     }
650 
651 #if 0  // Stacked and Percent not supported by KChart.
652     if (plotAreaChartSubType == StackedChartSubtype)
653         kdRadarDiagram->setType(KChart::PolarDiagram::Stacked);
654     else if (plotAreaChartSubType == PercentChartSubtype)
655         kdRadarDiagram->setType(KChart::PolarDiagram::Percent);
656 #endif
657     plotArea->parent()->legend()->kdLegend()->addDiagram(kdRadarDiagram);
658     kdRadarPlane->addDiagram(kdRadarDiagram);
659 }
660 
createScatterDiagram()661 void Axis::Private::createScatterDiagram()
662 {
663     Q_ASSERT(kdScatterDiagram == 0);
664     Q_ASSERT(plotArea);
665 
666     kdScatterDiagram = new KChart::Plotter(plotArea->kdChart(), kdPlane);
667     KChartModel *model = new KChartModel(plotArea, kdScatterDiagram);
668     kdScatterDiagram->setModel(model);
669     registerDiagram(kdScatterDiagram);
670 
671     model->setDataDimensions(2);
672 
673     kdScatterDiagram->setPen(Qt::NoPen);
674 
675     if (isVisible) {
676         kdScatterDiagram->addAxis(kdAxis);
677         q->registerDiagram(kdScatterDiagram);
678     }
679     kdPlane->addDiagram(kdScatterDiagram);
680 
681 
682     foreach (Axis *axis, plotArea->axes()) {
683         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
684             kdScatterDiagram->addAxis(axis->kdAxis());
685             axis->registerDiagram(kdScatterDiagram);
686         }
687     }
688 
689     // Propagate existing settings
690     KChart::ThreeDLineAttributes attributes(kdScatterDiagram->threeDLineAttributes());
691     attributes.setEnabled(plotArea->isThreeD());
692     attributes.setThreeDBrushEnabled(plotArea->isThreeD());
693     kdScatterDiagram->setThreeDLineAttributes(attributes);
694 
695     plotArea->parent()->legend()->kdLegend()->addDiagram(kdScatterDiagram);
696 }
697 
createStockDiagram()698 void Axis::Private::createStockDiagram()
699 {
700     Q_ASSERT(kdStockDiagram == 0);
701 
702     kdStockDiagram = new KChart::StockDiagram(plotArea->kdChart(), kdPlane);
703     KChartModel *model = new KChartModel(plotArea, kdStockDiagram);
704     kdStockDiagram->setModel(model);
705     switch (plotAreaChartSubType) {
706         case HighLowCloseChartSubtype:
707             kdStockDiagram->setType(KChart::StockDiagram::HighLowClose);
708             break;
709         case OpenHighLowCloseChartSubtype:
710             kdStockDiagram->setType(KChart::StockDiagram::OpenHighLowClose);
711             break;
712         case CandlestickChartSubtype:
713             kdStockDiagram->setType(KChart::StockDiagram::Candlestick);
714             break;
715     }
716     registerDiagram(kdStockDiagram);
717 
718     model->setDataDimensions(numDimensions(StockChartType));
719 
720 #if 0  // Stacked and Percent not supported by KChart.
721     if (plotAreaChartSubType == StackedChartSubtype)
722         kdStockDiagram->setType(KChart::StockDiagram::Stacked);
723     else if (plotAreaChartSubType == PercentChartSubtype)
724         kdStockDiagram->setType(KChart::StockDiagram::Percent);
725 #endif
726 
727     if (isVisible) {
728         kdStockDiagram->addAxis(kdAxis);
729         q->registerDiagram(kdStockDiagram);
730     }
731     kdPlane->addDiagram(kdStockDiagram);
732 
733     Q_ASSERT(plotArea);
734     foreach (Axis *axis, plotArea->axes()) {
735         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
736             kdStockDiagram->addAxis(axis->kdAxis());
737             axis->registerDiagram(kdStockDiagram);
738         }
739     }
740 
741     plotArea->parent()->legend()->kdLegend()->addDiagram(kdStockDiagram);
742     q->updateKChartStockAttributes();
743 }
744 
createBubbleDiagram()745 void Axis::Private::createBubbleDiagram()
746 {
747     Q_ASSERT(kdBubbleDiagram == 0);
748     Q_ASSERT(plotArea);
749 
750     kdBubbleDiagram = new KChart::Plotter(plotArea->kdChart(), kdPlane);
751     KChartModel *model = new KChartModel(plotArea, kdBubbleDiagram);
752     kdBubbleDiagram->setModel(model);
753     registerDiagram(kdBubbleDiagram);
754 
755     model->setDataDimensions(2);
756 
757     kdPlane->addDiagram(kdBubbleDiagram);
758 
759     foreach (Axis *axis, plotArea->axes()) {
760         //if (axis->dimension() == XAxisDimension)
761         if (axis->isVisible() && axis->dimension() == XAxisDimension) {
762             kdBubbleDiagram->addAxis(axis->kdAxis());
763             q->registerDiagram(kdBubbleDiagram);
764         }
765     }
766 
767      // disable the connecting line
768     KChart::LineAttributes la = kdBubbleDiagram->lineAttributes();
769     la.setVisible(false);
770     kdBubbleDiagram->setLineAttributes(la);
771 
772     plotArea->parent()->legend()->kdLegend()->addDiagram(kdBubbleDiagram);
773 }
774 
createSurfaceDiagram()775 void Axis::Private::createSurfaceDiagram()
776 {
777     Q_ASSERT(!kdSurfaceDiagram);
778 
779     // This is a so far a by KChart unsupported chart type.
780     // Fall back to bar diagram for now.
781     kdSurfaceDiagram = new KChart::BarDiagram(plotArea->kdChart(), kdPlane);
782     KChartModel *model = new KChartModel(plotArea, kdSurfaceDiagram);
783     kdSurfaceDiagram->setModel(model);
784     registerDiagram(kdSurfaceDiagram);
785     plotArea->parent()->legend()->kdLegend()->addDiagram(kdSurfaceDiagram);
786     kdPlane->addDiagram(kdSurfaceDiagram);
787 }
788 
createGanttDiagram()789 void Axis::Private::createGanttDiagram()
790 {
791     // This is a so far a by KChart unsupported chart type (through KDGantt got merged into KChart with 2.3)
792     Q_ASSERT(!kdGanttDiagram);
793 
794     // This is a so far a by KChart unsupported chart type.
795     // Fall back to bar diagram for now.
796     kdGanttDiagram = new KChart::BarDiagram(plotArea->kdChart(), kdPlane);
797     KChartModel *model = new KChartModel(plotArea, kdGanttDiagram);
798     kdGanttDiagram->setModel(model);
799     registerDiagram(kdGanttDiagram);
800     plotArea->parent()->legend()->kdLegend()->addDiagram(kdGanttDiagram);
801     kdPlane->addDiagram(kdGanttDiagram);
802 }
803 
804 /**
805  * Automatically adjusts the diagram so that all currently displayed
806  * diagram types fit together.
807  */
adjustAllDiagrams()808 void Axis::Private::adjustAllDiagrams()
809 {
810     // If at least one dataset is attached that belongs to a
811     // horizontal bar chart, set centerDataPoints to true.
812     centerDataPoints = kdBarDiagram != 0;
813     if (kdLineDiagram)
814         kdLineDiagram->setCenterDataPoints(centerDataPoints);
815     if (kdAreaDiagram)
816         kdAreaDiagram->setCenterDataPoints(centerDataPoints);
817 }
818 
819 
820 // ================================================================
821 //                             class Axis
822 
823 // FIXME: make it possible to create an axis without parent, and
824 //        when it is removed, actually remove it from parent (signals and all)
825 
Axis(PlotArea * parent,AxisDimension dimension)826 Axis::Axis(PlotArea *parent, AxisDimension dimension)
827     : d(new Private(this, dimension))
828 {
829     Q_ASSERT(parent);
830 
831     parent->addAxis(this);
832 
833     d->plotArea = parent;
834     d->kdAxis->setObjectName(name());
835     KChart::BackgroundAttributes batt(d->kdAxis->backgroundAttributes());
836     batt.setBrush(QBrush(Qt::white));
837     d->kdAxis->setBackgroundAttributes(batt);
838     setFontSize(8.0); // also sets MeasureCalculationModeAbsolute
839     d->kdPlane = parent->kdCartesianPlane(this);
840     d->kdPolarPlane = parent->kdPolarPlane();
841     d->kdRadarPlane = parent->kdRadarPlane();
842 
843     d->plotAreaChartType    = d->plotArea->chartType();
844     d->plotAreaChartSubType = d->plotArea->chartSubType();
845 
846     setOdfAxisPosition("start");
847     setOdfAxisLabelsPosition("near-axis");
848 
849     KoShapeFactoryBase *textShapeFactory = KoShapeRegistry::instance()->value(TextShapeId);
850     if (textShapeFactory)
851         d->title = textShapeFactory->createDefaultShape(parent->parent()->resourceManager());
852     if (d->title) {
853         d->titleData = qobject_cast<TextLabelData*>(d->title->userData());
854         if (d->titleData == 0) {
855             d->titleData = new TextLabelData;
856             d->title->setUserData(d->titleData);
857         }
858 
859         QFont font = d->titleData->document()->defaultFont();
860         font.setPointSizeF(9);
861         d->titleData->document()->setDefaultFont(font);
862     }
863     else {
864         d->title = new TextLabelDummy;
865         d->titleData = new TextLabelData;
866         KoTextDocumentLayout *documentLayout = new KoTextDocumentLayout(d->titleData->document());
867         d->titleData->document()->setDocumentLayout(documentLayout);
868         d->title->setUserData(d->titleData);
869     }
870     d->title->setSize(QSizeF(CM_TO_POINT(3), CM_TO_POINT(0.75)));
871 
872     d->plotArea->parent()->addShape(d->title);
873     d->plotArea->parent()->setClipped(d->title, true);
874     d->plotArea->parent()->setInheritsTransform(d->title, true);
875     d->title->setDeletable(false);
876     d->title->setZIndex(5);
877     d->title->setToolDelegates(QSet<KoShape*>()<<parent->parent()<<d->title); // Enable chart tool
878     d->titleData->setResizeMethod(KoTextShapeDataBase::AutoResize);
879     d->title->setAdditionalStyleAttribute("chart:auto-position", "true");
880     d->title->setAllowedInteraction(KoShape::ShearingAllowed, false);
881     d->title->setAllowedInteraction(KoShape::RotationAllowed, false);
882     d->title->setAllowedInteraction(KoShape::ResizeAllowed, false);
883     d->title->setVisible(false); // Needed to avoid problems when creating secondary axes (Axis creation needs review/refactoring)
884 
885     connect(d->plotArea, SIGNAL(angleOffsetChanged(qreal)), this, SLOT(setAngleOffset(qreal)));
886     connect(d->plotArea, SIGNAL(holeSizeChanged(qreal)), this, SLOT(setHoleSize(qreal)));
887 
888     d->updatePosition();
889 }
890 
~Axis()891 Axis::~Axis()
892 {
893     Q_ASSERT(d->plotArea);
894     d->plotArea->parent()->KoShapeContainer::removeShape(d->title);
895 
896     Q_ASSERT(d->title);
897     delete d->title;
898 
899     delete d;
900 }
901 
plotArea() const902 PlotArea* Axis::plotArea() const
903 {
904     return d->plotArea;
905 }
906 
title() const907 KoShape *Axis::title() const
908 {
909     return d->title;
910 }
911 
titleText() const912 QString Axis::titleText() const
913 {
914     return d->titleData->document()->toPlainText();
915 }
916 
showLabels() const917 bool Axis::showLabels() const
918 {
919     return d->showLabels;
920 }
921 
showOverlappingDataLabels() const922 bool Axis::showOverlappingDataLabels() const
923 {
924     return d->showOverlappingDataLabels;
925 }
926 
id() const927 QString Axis::id() const
928 {
929     return d->id;
930 }
931 
dimension() const932 AxisDimension Axis::dimension() const
933 {
934     return d->dimension;
935 }
936 
dataSets() const937 QList<DataSet*> Axis::dataSets() const
938 {
939     return d->dataSets;
940 }
941 
attachDataSet(DataSet * dataSet)942 bool Axis::attachDataSet(DataSet *dataSet)
943 {
944     Q_ASSERT(!d->dataSets.contains(dataSet));
945     if (d->dataSets.contains(dataSet))
946         return false;
947 
948     d->dataSets.append(dataSet);
949 
950     if (dimension() == YAxisDimension) {
951         dataSet->setAttachedAxis(this);
952 
953         ChartType chartType = dataSet->chartType();
954         if (chartType == LastChartType)
955             chartType = d->plotAreaChartType;
956 
957         KChart::AbstractDiagram *diagram = d->getDiagramAndCreateIfNeeded(chartType);
958         Q_ASSERT(diagram);
959         KChartModel *model = dynamic_cast<KChartModel*>(diagram->model());
960         Q_ASSERT(model);
961 
962         model->addDataSet(dataSet);
963 
964         layoutPlanes();
965         requestRepaint();
966     }
967 
968     return true;
969 }
970 
detachDataSet(DataSet * dataSet,bool silent)971 bool Axis::detachDataSet(DataSet *dataSet, bool silent)
972 {
973     Q_ASSERT(d->dataSets.contains(dataSet));
974     if (!d->dataSets.contains(dataSet))
975         return false;
976     d->dataSets.removeAll(dataSet);
977 
978     if (dimension() == YAxisDimension) {
979         ChartType chartType = dataSet->chartType();
980         if (chartType == LastChartType)
981             chartType = d->plotAreaChartType;
982 
983         KChart::AbstractDiagram *oldDiagram = d->getDiagram(chartType);
984         Q_ASSERT(oldDiagram);
985         KChartModel *oldModel = dynamic_cast<KChartModel*>(oldDiagram->model());
986         Q_ASSERT(oldModel);
987 
988         const int rowCount = oldModel->dataDirection() == Qt::Vertical
989                                  ? oldModel->columnCount() : oldModel->rowCount();
990         // If there's only as many rows as needed for *one*
991         // dataset, that means that the dataset we're removing is
992         // the last one in the model --> delete model
993         if (rowCount == oldModel->dataDimensions())
994             d->deleteDiagram(chartType);
995         else
996             oldModel->removeDataSet(dataSet, silent);
997 
998         dataSet->setKdChartModel(0);
999         dataSet->setAttachedAxis(0);
1000 
1001         if (!silent) {
1002             layoutPlanes();
1003             requestRepaint();
1004         }
1005     }
1006 
1007     return true;
1008 }
1009 
clearDataSets()1010 void Axis::clearDataSets()
1011 {
1012     QList<DataSet*> list = d->dataSets;
1013     foreach(DataSet *dataSet, list)
1014         detachDataSet(dataSet, true);
1015 }
1016 
showRuler() const1017 bool Axis::showRuler() const
1018 {
1019     return d->kdAxis->rulerAttributes().showRulerLine();
1020 }
1021 
setShowRuler(bool show)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 
majorInterval() const1029 qreal Axis::majorInterval() const
1030 {
1031     return d->majorInterval;
1032 }
1033 
setMajorInterval(qreal interval)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 
minorInterval() const1060 qreal Axis::minorInterval() const
1061 {
1062     return (d->majorInterval / (qreal)d->minorIntervalDivisor);
1063 }
1064 
setMinorInterval(qreal interval)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 
minorIntervalDivisor() const1073 int Axis::minorIntervalDivisor() const
1074 {
1075     return d->minorIntervalDivisor;
1076 }
1077 
setMinorIntervalDivisor(int divisor)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 
useAutomaticMajorInterval() const1099 bool Axis::useAutomaticMajorInterval() const
1100 {
1101     return d->useAutomaticMajorInterval;
1102 }
1103 
useAutomaticMinorInterval() const1104 bool Axis::useAutomaticMinorInterval() const
1105 {
1106     return d->useAutomaticMinorInterval;
1107 }
1108 
setUseAutomaticMajorInterval(bool automatic)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 
setUseAutomaticMinorInterval(bool automatic)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 
showInnerMinorTicks() const1125 bool Axis::showInnerMinorTicks() const
1126 {
1127     return d->showInnerMinorTicks;
1128 }
1129 
showOuterMinorTicks() const1130 bool Axis::showOuterMinorTicks() const
1131 {
1132     return d->showOuterMinorTicks;
1133 }
1134 
showInnerMajorTicks() const1135 bool Axis::showInnerMajorTicks() const
1136 {
1137     return d->showInnerMinorTicks;
1138 }
1139 
showOuterMajorTicks() const1140 bool Axis::showOuterMajorTicks() const
1141 {
1142     return d->showOuterMajorTicks;
1143 }
1144 
setShowInnerMinorTicks(bool showTicks)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 
setShowOuterMinorTicks(bool showTicks)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 
setShowInnerMajorTicks(bool showTicks)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 
setShowOuterMajorTicks(bool showTicks)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 
setScalingLogarithmic(bool logarithmicScaling)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 
scalingIsLogarithmic() const1192 bool Axis::scalingIsLogarithmic() const
1193 {
1194     return d->logarithmicScaling;
1195 }
1196 
showMajorGrid() const1197 bool Axis::showMajorGrid() const
1198 {
1199     return d->showMajorGrid;
1200 }
1201 
setShowMajorGrid(bool showGrid)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 
showMinorGrid() const1218 bool Axis::showMinorGrid() const
1219 {
1220     return d->showMinorGrid;
1221 }
1222 
setShowMinorGrid(bool showGrid)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 
setTitleText(const QString & text)1239 void Axis::setTitleText(const QString &text)
1240 {
1241     d->titleData->document()->setPlainText(text);
1242 }
1243 
setShowLabels(bool show)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 
setShowOverlappingDataLabels(bool show)1253 void Axis::setShowOverlappingDataLabels(bool show)
1254 {
1255     d->showOverlappingDataLabels = show;
1256 }
1257 
orientation() const1258 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 
loadOdf(const KoXmlElement & axisElement,KoShapeLoadingContext & context)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 
loadOdfChartSubtypeProperties(const KoXmlElement & axisElement,KoShapeLoadingContext & context)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 
setName(const QString & name)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
name() const1573 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 
saveOdf(KoShapeSavingContext & context)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 
saveOdfGrid(KoShapeSavingContext & context,OdfGridClass gridClass)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 
update() const1716 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 
kdAxis() const1736 KChart::CartesianAxis *Axis::kdAxis() const
1737 {
1738     return d->kdAxis;
1739 }
1740 
kdPlane() const1741 KChart::AbstractCoordinatePlane *Axis::kdPlane() const
1742 {
1743     return d->kdPlane;
1744 }
1745 
plotAreaChartTypeChanged(ChartType newChartType)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 
plotAreaChartSubTypeChanged(ChartSubtype subType)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 
plotAreaIsVerticalChanged()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 
updatePosition()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 
registerAxis(Axis * axis)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 
registerDiagram(KChart::AbstractCartesianDiagram * diagram)1982 void Axis::registerDiagram(KChart::AbstractCartesianDiagram *diagram)
1983 {
1984     if (!d->diagrams.contains(diagram)) {
1985         d->diagrams << diagram;
1986     }
1987 }
1988 
restoreDiagrams()1989 void Axis::Private::restoreDiagrams()
1990 {
1991     diagrams.removeAll(nullptr);
1992     for (KChart::AbstractCartesianDiagram *diag : diagrams) {
1993         diag->addAxis(kdAxis);
1994     }
1995 }
1996 
removeAxisFromDiagrams(bool clear)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 
setThreeD(bool threeD)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 
requestRepaint() const2060 void Axis::requestRepaint() const
2061 {
2062     d->plotArea->requestRepaint();
2063 }
2064 
layoutPlanes()2065 void Axis::layoutPlanes()
2066 {
2067     d->kdPlane->layoutPlanes();
2068     d->kdPolarPlane->layoutPlanes();
2069     d->kdRadarPlane->layoutPlanes();
2070 }
2071 
gapBetweenBars() const2072 int Axis::gapBetweenBars() const
2073 {
2074     return d->gapBetweenBars;
2075 }
2076 
setGapBetweenBars(int percent)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 
gapBetweenSets() const2092 int Axis::gapBetweenSets() const
2093 {
2094     return d->gapBetweenSets;
2095 }
2096 
setGapBetweenSets(int percent)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 
setAngleOffset(qreal angle)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 
setHoleSize(qreal value)2122 void Axis::setHoleSize(qreal value)
2123 {
2124     //TODO KChart does not support
2125 }
2126 
font() const2127 QFont Axis::font() const
2128 {
2129     return d->kdAxis->textAttributes().font();
2130 }
2131 
setFont(const QFont & font)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 
fontSize() const2140 qreal Axis::fontSize() const
2141 {
2142     return d->kdAxis->textAttributes().fontSize().value();
2143 }
2144 
setFontSize(qreal size)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 
isVisible() const2158 bool Axis::isVisible() const
2159 {
2160     return d->isVisible;
2161 }
2162 
setVisible(bool visible)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 
numericStyleFormat() const2174 KoOdfNumberStyles::NumericStyleFormat *Axis::numericStyleFormat() const
2175 {
2176     return d->numericStyleFormat;
2177 }
2178 
SetNumericStyleFormat(KoOdfNumberStyles::NumericStyleFormat * numericStyleFormat) const2179 void Axis::SetNumericStyleFormat(KoOdfNumberStyles::NumericStyleFormat *numericStyleFormat) const
2180 {
2181     delete d->numericStyleFormat;
2182     d->numericStyleFormat = numericStyleFormat;
2183 }
2184 
setOdfAxisPosition(const QString & odfpos)2185 void Axis::setOdfAxisPosition(const QString &odfpos)
2186 {
2187     d->axisPosition = odfpos;
2188 }
2189 
odfAxisPosition() const2190 QString Axis::odfAxisPosition() const
2191 {
2192     return d->axisPosition;
2193 }
2194 
updateKChartAxisPosition()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 
kchartAxisPosition() const2291 CartesianAxis::Position Axis::kchartAxisPosition() const
2292 {
2293     return d->kdAxis->position();
2294 }
2295 
actualAxisPosition() const2296 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 
axisDirectionReversed() const2318 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 
setOdfAxisLabelsPosition(const QString & odfpos)2331 void Axis::setOdfAxisLabelsPosition(const QString &odfpos)
2332 {
2333     d->axisLabelsPosition = odfpos;
2334 }
2335 
odfAxisLabelsPosition() const2336 QString Axis::odfAxisLabelsPosition() const
2337 {
2338     return d->axisLabelsPosition;
2339 }
2340 
updateKChartStockAttributes()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 
operator <<(QDebug dbg,KoChart::Axis * a)2352 QDebug operator<<(QDebug dbg, KoChart::Axis *a)
2353 {
2354     dbg.nospace().noquote() <<"Axis["<<a->name()<<']';
2355     return dbg.space().quote();
2356 }
2357