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