File indexing completed on 2025-01-12 13:05:33
0001 /* 0002 * Copyright (c) 2010 Sebastian Sauer <sebsauer@kdab.com> 0003 * Copyright (c) 2010 Carlos Licea <carlos@kdab.com> 0004 * Copyright (c) 2014 Inge Wallin <inge@lysator.liu.se> 0005 * 0006 * This library is free software; you can redistribute it and/or modify 0007 * it under the terms of the GNU Lesser General Public License as published 0008 * by the Free Software Foundation; either version 2.1 of the License, or 0009 * (at your option) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU Lesser General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Lesser General Public License 0017 * along with this program; if not, write to the Free Software 0018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #ifndef CHARTING_H 0022 #define CHARTING_H 0023 0024 #include <QString> 0025 #include <QRect> 0026 #include <QMap> 0027 #include <QHash> 0028 #include <QVector> 0029 #include <QColor> 0030 0031 0032 namespace KoChart 0033 { 0034 0035 class Value 0036 { 0037 public: 0038 enum DataId { 0039 SeriesLegendOrTrendlineName = 0x00, ///< Referenced data specifies the series, legend entry, or trendline name. Error bars name MUST be empty. 0040 HorizontalValues = 0x01, ///< Referenced data specifies the values or horizontal values on bubble and scatter chart groups of the series and error bars. 0041 VerticalValues = 0x02, ///< Referenced data specifies the categories or vertical values on bubble and scatter chart groups of the series and error bars. 0042 BubbleSizeValues = 0x03 ///< Referenced data specifies the bubble size values of the series. 0043 }; 0044 DataId m_dataId; 0045 0046 enum DataType { 0047 AutoGeneratedName = 0x00, ///< The data source is a category (3) name, series name or bubble size that was automatically generated. 0048 TextOrValue = 0x01, ///< The data source is the text or value as specified by the formula field. 0049 CellRange = 0x02 ///< The data source is the value from a range of cells in a sheet specified by the formula field. 0050 }; 0051 DataType m_type; 0052 0053 bool m_isUnlinkedFormat; ///< false=data uses the number formatting of the referenced data, true=data uses the custom formatting specified via m_numberFormat. 0054 unsigned m_numberFormat; ///< specifies the numnber format to use for the data. 0055 QString m_formula; ///< the optional formula. could be for example "[Sheet1.$D$2:$F$2]" 0056 0057 Value(DataId dataId, DataType type, 0058 const QString& formula = QString(), 0059 bool isUnlinkedFormat = false, unsigned numberFormat = 0) 0060 : m_dataId(dataId) 0061 , m_type(type) 0062 , m_isUnlinkedFormat(isUnlinkedFormat) 0063 , m_numberFormat(numberFormat) 0064 , m_formula(formula) 0065 {} 0066 virtual ~Value() {} 0067 }; 0068 0069 0070 // ---------------------------------------------------------------- 0071 // Formats 0072 0073 0074 class Format 0075 { 0076 public: 0077 Format() {} 0078 virtual ~Format() {} 0079 }; 0080 0081 class LineFormat : public Format 0082 { 0083 public: 0084 enum Style { 0085 Solid = 0x0000, 0086 Dash = 0x0001, 0087 Dot = 0x0002, 0088 DashDot = 0x0003, 0089 DashDotDot = 0x0004, 0090 None = 0x0005, 0091 DarkGrayPattern = 0x0006, 0092 MediumGrayPattern = 0x0007, 0093 LightGrayPattern = 0x0008 0094 }; 0095 0096 enum Thickness { 0097 Hairline = 0xFFFF, 0098 NarrowSingle = 0x0000, 0099 MediumDouble = 0x0001, 0100 WideTriple = 0x0002 0101 }; 0102 0103 Style m_style; 0104 Thickness m_thickness; 0105 0106 explicit LineFormat(const Style& style = None, const Thickness& thickness = Hairline) 0107 : Format() 0108 , m_style(style) 0109 , m_thickness(thickness) 0110 {} 0111 }; 0112 0113 class PieFormat : public Format 0114 { 0115 public: 0116 int m_pcExplode; // from PieFormat 0117 0118 explicit PieFormat(int pcExplode = 0) 0119 : Format() 0120 , m_pcExplode(pcExplode) 0121 {} 0122 }; 0123 0124 class AreaFormat : public Format 0125 { 0126 public: 0127 QColor m_foreground; 0128 QColor m_background; 0129 bool m_fill; 0130 0131 explicit AreaFormat(const QColor &foreground = QColor(), const QColor &background = QColor(), 0132 bool fill = false) 0133 : Format() 0134 , m_foreground(foreground) 0135 , m_background(background) 0136 , m_fill(fill) 0137 {} 0138 }; 0139 0140 class Gradient 0141 { 0142 public: 0143 Gradient() { angle = 0.0; } 0144 0145 class GradientStop 0146 { 0147 public: 0148 void reset() 0149 { 0150 position = 1.0; 0151 knownColorValue = QColor(); 0152 tintVal = 0; 0153 satVal = 0; 0154 shadeVal = 0; 0155 referenceColor.clear(); 0156 } 0157 0158 qreal position; 0159 QColor knownColorValue; 0160 qreal tintVal; 0161 qreal satVal; 0162 qreal shadeVal; 0163 QString referenceColor; 0164 }; 0165 0166 QVector< GradientStop > gradientStops; 0167 qreal angle; 0168 }; 0169 0170 class Fill 0171 { 0172 public: 0173 enum FillType {Blip, Gradient, Group, None, Pattern, Solid}; 0174 0175 Fill() 0176 : type(None) 0177 , valid(false) 0178 {} 0179 0180 void setColor(const QColor& color){ solidColor = color; valid = true; type = Solid; } 0181 void setType(FillType type){ this->type = type; valid = true; } 0182 0183 QColor solidColor; 0184 QString pixmapFile; 0185 KoChart::Gradient gradient; 0186 FillType type; 0187 bool valid; 0188 }; 0189 0190 class ShapeProperties 0191 { 0192 public: 0193 int lineWidth; 0194 Fill lineFill; 0195 Fill areaFill; 0196 }; 0197 0198 0199 // ---------------------------------------------------------------- 0200 // Chart types 0201 0202 0203 class ChartImpl 0204 { 0205 public: 0206 ChartImpl() {} 0207 virtual ~ChartImpl() {} 0208 virtual QByteArray name() const = 0; 0209 }; 0210 0211 class PieImpl : public ChartImpl 0212 { 0213 public: 0214 /// Starting angle of the first data point clockwise from the top of the circle. 0215 int m_anStart; 0216 0217 explicit PieImpl(int anStart = 0) 0218 : ChartImpl() 0219 , m_anStart(anStart) 0220 {} 0221 0222 QByteArray name() const override { return "circle"; } 0223 }; 0224 0225 class RingImpl : public PieImpl 0226 { 0227 public: 0228 /// Size of the center hole in a doughnut chart group as a percentage of the plot area size. 0229 int m_pcDonut; 0230 0231 explicit RingImpl(int anStart = 0, int pcDonut = 0) 0232 : PieImpl(anStart) 0233 , m_pcDonut(pcDonut) 0234 {} 0235 0236 QByteArray name() const override { return "ring"; } 0237 }; 0238 0239 class BarImpl : public ChartImpl 0240 { 0241 public: 0242 QByteArray name() const override { return "bar"; } 0243 }; 0244 0245 class LineImpl : public ChartImpl 0246 { 0247 public: 0248 QByteArray name() const override { return "line"; } 0249 }; 0250 0251 class RadarImpl : public ChartImpl 0252 { 0253 public: 0254 /// If true then the radar-chart is filled (a RadarArea chart), else not. 0255 bool m_filled; 0256 0257 explicit RadarImpl(bool filled = false) 0258 : ChartImpl() 0259 , m_filled(filled) 0260 {} 0261 0262 QByteArray name() const override { return m_filled ? "filled-radar" : "radar"; } 0263 }; 0264 0265 class AreaImpl : public ChartImpl 0266 { 0267 public: 0268 AreaImpl() : ChartImpl() {} 0269 0270 QByteArray name() const override { return "area"; } 0271 }; 0272 0273 class StockImpl : public ChartImpl 0274 { 0275 public: 0276 StockImpl() : ChartImpl() {} 0277 0278 QByteArray name() const override { return "stock"; } 0279 }; 0280 0281 class ScatterImpl : public ChartImpl 0282 { 0283 public: 0284 enum ScatterStyle { None, Line, LineMarker, Marker, Smooth, SmoothMarker }; 0285 ScatterStyle style; 0286 0287 ScatterImpl() 0288 : ChartImpl() 0289 , style(LineMarker) 0290 {} 0291 0292 QByteArray name() const override { return "scatter"; } 0293 }; 0294 0295 class BubbleImpl : public ChartImpl 0296 { 0297 public: 0298 enum SizeType { 0299 Area = 0x0001, ///< The area of the data point represents the value. 0300 Width = 0x0002 ///< The width of the data point represents the value. 0301 }; 0302 /// Specifies how the default size of the data points represents the value. 0303 SizeType m_sizeType; 0304 0305 /// The size of the data points as a percentage of their default size. A 0306 /// value of 100 shows all the data points in their default size, as 0307 /// determined by the application. 0308 unsigned int m_sizeRatio; 0309 0310 /// Specifies whether data points with negative values are shown. 0311 bool m_showNegativeBubbles; 0312 0313 explicit BubbleImpl(SizeType sizeType = Area, unsigned int sizeRatio = 100, 0314 bool showNegativeBubbles = true) 0315 : ChartImpl() 0316 , m_sizeType(sizeType) 0317 , m_sizeRatio(sizeRatio) 0318 , m_showNegativeBubbles(showNegativeBubbles) 0319 {} 0320 0321 QByteArray name() const override { return "bubble"; } 0322 }; 0323 0324 class SurfaceImpl : public ChartImpl 0325 { 0326 public: 0327 /// Specifies that the surface is either filled or a wireframe. 0328 bool m_fill; 0329 0330 explicit SurfaceImpl(bool fill = false) : ChartImpl(), m_fill(fill) {} 0331 0332 QByteArray name() const override { return "surface"; } 0333 }; 0334 0335 0336 // ---------------------------------------------------------------- 0337 // Objects within the chart 0338 0339 0340 class Obj 0341 { 0342 public: 0343 unsigned int m_mdTopLt; 0344 unsigned int m_mdBotRt; 0345 unsigned int m_x1; 0346 unsigned int m_y1; 0347 unsigned int m_x2; 0348 unsigned int m_y2; 0349 KoChart::AreaFormat *m_areaFormat; 0350 0351 explicit Obj() 0352 : m_mdTopLt(0) 0353 , m_mdBotRt(0) 0354 , m_x1(0) 0355 , m_y1(0) 0356 , m_x2(0) 0357 , m_y2(0) 0358 , m_areaFormat(0) 0359 {} 0360 virtual ~Obj() { delete m_areaFormat; } 0361 }; 0362 0363 class Text : public Obj 0364 { 0365 public: 0366 QString m_text; 0367 0368 explicit Text(const QString &text = QString()) 0369 : Obj() 0370 , m_text(text) 0371 {} 0372 ~Text() override {} 0373 }; 0374 0375 class Axis : public Obj 0376 { 0377 public: 0378 enum Type { 0379 HorizontalValueAxis = 0x0000, 0380 VerticalValueAxis = 0x0001, 0381 SeriesAxis = 0x0002 0382 }; 0383 Type m_type; 0384 0385 class Gridline 0386 { 0387 public: 0388 LineFormat m_format; 0389 explicit Gridline(const LineFormat &format = LineFormat()) 0390 : m_format(format) 0391 {} 0392 }; 0393 0394 Gridline m_majorGridlines; 0395 Gridline m_minorGridlines; 0396 0397 LineFormat m_format; 0398 0399 QString m_numberFormat; 0400 0401 bool m_reversed; 0402 bool m_logarithmic; 0403 0404 bool m_autoMinimum; 0405 bool m_autoMaximum; 0406 qreal m_minimum; 0407 qreal m_maximum; 0408 0409 explicit Axis(Type type) 0410 : Obj() 0411 , m_type(type) 0412 , m_reversed(false) 0413 , m_logarithmic(false) 0414 , m_autoMinimum(true) 0415 , m_autoMaximum(true) 0416 , m_minimum(0) 0417 , m_maximum(0) 0418 {} 0419 ~Axis() override {} 0420 }; 0421 0422 class Cell 0423 { 0424 public: 0425 int m_column; 0426 int m_row; 0427 QString m_value; 0428 QString m_valueType; 0429 0430 Cell(int columnIndex, int rowIndex) 0431 : m_column(columnIndex) 0432 , m_row(rowIndex) 0433 , m_valueType("string") 0434 {} 0435 }; 0436 0437 0438 /// cell data representation of internal table 0439 class InternalTable 0440 { 0441 public: 0442 InternalTable() 0443 : m_maxRow(0) 0444 , m_maxColumn(0) 0445 {} 0446 ~InternalTable() 0447 { 0448 qDeleteAll(m_cells); 0449 } 0450 0451 Cell* cell(int columnIndex, int rowIndex, bool autoCreate) 0452 { 0453 const uint maximumSpreadsheetColumns = 0x7FFF; // MSOOXML::maximumSpreadsheetColumns() 0454 const unsigned hashed = (rowIndex + 1) * maximumSpreadsheetColumns + columnIndex + 1; 0455 0456 Cell* c = m_cells[hashed]; 0457 if (!c && autoCreate) { 0458 c = new Cell(columnIndex, rowIndex); 0459 m_cells[hashed] = c; 0460 0461 if (rowIndex > m_maxRow) 0462 m_maxRow = rowIndex; 0463 if (columnIndex > m_maxColumn) 0464 m_maxColumn = columnIndex; 0465 if (!m_maxCellsInRow.contains(rowIndex) || columnIndex > m_maxCellsInRow[rowIndex]) 0466 m_maxCellsInRow[rowIndex] = columnIndex; 0467 } 0468 0469 return c; 0470 } 0471 0472 int maxCellsInRow(int rowIndex) const { return m_maxCellsInRow[rowIndex]; } 0473 int maxRow() const { return m_maxRow; } 0474 int maxColumn() const { return m_maxColumn; } 0475 0476 private: 0477 int m_maxRow; 0478 int m_maxColumn; 0479 QHash<unsigned, Cell*> m_cells; 0480 QHash<int, int> m_maxCellsInRow; 0481 }; 0482 0483 0484 /// Different types of markers. 0485 enum MarkerType { 0486 NoMarker, 0487 AutoMarker, 0488 SquareMarker, 0489 DiamondMarker, 0490 StarMarker, 0491 DotMarker, 0492 DashMarker, 0493 PlusMarker, 0494 CircleMarker, 0495 SymbolXMarker, 0496 TriangleMarker 0497 // TODO fill the missing marker types in 0498 }; 0499 0500 class DataPoint : public Obj 0501 { 0502 public: 0503 }; 0504 0505 class Series : public Obj 0506 { 0507 public: 0508 /// the type of data in categories, or horizontal values on bubble and 0509 /// scatter chart groups, in the series. MUST be either 0x0001=numeric or 0510 /// 0x0003=text. 0511 int m_dataTypeX; 0512 0513 /// the count of categories (3), or horizontal values on bubble and 0514 /// scatter chart groups, in the series. 0515 int m_countXValues; 0516 0517 /// the count of values, or vertical values on bubble and scatter chart 0518 /// groups, in the series. 0519 int m_countYValues; 0520 0521 /// the count of bubble size values in the series. 0522 int m_countBubbleSizeValues; 0523 0524 /// determines if the data values are shown in raw values as labels 0525 bool m_showDataLabelValues; 0526 0527 /// determines if the data values are shown in percent as labels or not 0528 bool m_showDataLabelPercent; 0529 0530 /// determines if the category data is shown as labels or not 0531 bool m_showDataLabelCategory; 0532 0533 /// determines if the name of the series is shown as labels 0534 bool m_showDataLabelSeries; 0535 0536 /// range that contains the values that should be visualized by the dataSeries. 0537 QString m_valuesCellRangeAddress; 0538 0539 /// Ranges that contain the values that should be visualized by the dataSeries. 0540 QStringList m_domainValuesCellRangeAddress; 0541 0542 /// The referenced values used in the chart 0543 QMap<Value::DataId, Value*> m_datasetValue; 0544 0545 /// The data-points in the series. 0546 QList<DataPoint*> m_dataPoints; 0547 0548 /// The formatting for the referenced values 0549 QList<Format*> m_datasetFormat; 0550 0551 /// List of text records attached to the series. 0552 QList<Text*> m_texts; 0553 0554 /// range that contains label 0555 QString m_labelCell; 0556 0557 /// marker type 0558 MarkerType m_markerType; 0559 ShapeProperties* spPr; 0560 QString m_numberFormat; 0561 0562 explicit Series() 0563 : Obj() 0564 , m_dataTypeX(0) 0565 , m_countXValues(0) 0566 , m_countYValues(0) 0567 , m_countBubbleSizeValues(0) 0568 , m_showDataLabelValues(false) 0569 , m_showDataLabelPercent(false) 0570 , m_showDataLabelCategory(false) 0571 , m_showDataLabelSeries(false) 0572 , m_markerType(NoMarker) 0573 ,spPr(0) 0574 {} 0575 ~Series() override 0576 { 0577 qDeleteAll(m_datasetValue); 0578 qDeleteAll(m_dataPoints); 0579 qDeleteAll(m_datasetFormat); 0580 0581 delete spPr; 0582 } 0583 }; 0584 0585 class PlotArea : public Obj 0586 { 0587 public: 0588 explicit PlotArea() 0589 : Obj() 0590 {} 0591 ~PlotArea() override {} 0592 }; 0593 0594 class Legend : public Obj 0595 { 0596 public: 0597 explicit Legend() : Obj() {} 0598 ~Legend() override {} 0599 }; 0600 0601 /// The main charting class that represents a single chart. 0602 class Chart : public Obj 0603 { 0604 public: 0605 QString m_sheetName; 0606 0607 /// If true then the chart is a 3d chart else the chart is 2d. 0608 bool m_is3d; 0609 0610 /// Specifies a counter clockwise rotation of a polar coordinate in a 0611 /// circle, ring or polar chart. 0612 int m_angleOffset; 0613 0614 //int anRot, anElv, pcDist; 0615 0616 /// Margins around the chart object 0617 int m_leftMargin; 0618 int m_topMargin; 0619 int m_rightMargin; 0620 int m_bottomMargin; 0621 0622 /// List of series 0623 QList<Series*> m_series; 0624 0625 /// List of text records attached to the chart. 0626 QList<Text*> m_texts; 0627 0628 /// Range of all referenced cells. 0629 QRect m_cellRangeAddress; 0630 0631 /// Range that contains the vertical values (the categories) for the plot-area. 0632 QString m_verticalCellRangeAddress; 0633 0634 // The ChartTitle 0635 QString m_title; 0636 0637 /// The more concrete chart implementation like e.g. a PieImpl for a pie chart. 0638 ChartImpl *m_impl; 0639 0640 /// The plot-area. 0641 PlotArea *m_plotArea; 0642 0643 /// The legend. 0644 Legend *m_legend; 0645 0646 /// List of defined axes. 0647 QList<Axis*> m_axes; 0648 0649 /// Whether the chart is vertical or not. 0650 bool m_transpose; 0651 0652 /// Whether the chart is stacked or not. 0653 bool m_stacked; 0654 0655 /// Whether the chart is percentage or not. 0656 bool m_f100; 0657 0658 /// style for colors fills, line types, etc 0659 int m_style; 0660 0661 Gradient* m_fillGradient; 0662 Gradient* m_plotAreaFillGradient; 0663 MarkerType m_markerType; 0664 bool m_showLines; 0665 qreal m_textSize; 0666 0667 // the chart's internal table 0668 InternalTable m_internalTable; 0669 0670 explicit Chart() 0671 : Obj() 0672 , m_is3d(false) 0673 , m_angleOffset(0) 0674 , m_leftMargin(0) 0675 , m_topMargin(0) 0676 , m_rightMargin(0) 0677 , m_bottomMargin(0) 0678 , m_impl(0) 0679 , m_plotArea(0) 0680 , m_legend(0) 0681 , m_transpose(false) 0682 , m_stacked(false) 0683 , m_f100(false) 0684 , m_style(2) 0685 , m_fillGradient(0) 0686 , m_plotAreaFillGradient(0) 0687 , m_markerType(NoMarker) 0688 , m_showLines(false) 0689 , m_textSize(10) 0690 { 0691 m_x1 = m_y1 = m_x2 = m_y2 = -1; // -1 means autoposition/autosize 0692 } 0693 ~Chart() override 0694 { 0695 qDeleteAll(m_series); 0696 qDeleteAll(m_texts); 0697 delete m_impl; 0698 delete m_plotArea; 0699 delete m_legend; 0700 delete m_fillGradient; 0701 delete m_plotAreaFillGradient; 0702 } 0703 0704 void addRange(const QRect& range) 0705 { 0706 if (range.isValid()) { 0707 if (m_cellRangeAddress.isValid()) { 0708 if (range.left() < m_cellRangeAddress.left()) 0709 m_cellRangeAddress.setLeft(range.left()); 0710 if (range.top() < m_cellRangeAddress.top()) 0711 m_cellRangeAddress.setTop(range.top()); 0712 if (range.right() > m_cellRangeAddress.right()) 0713 m_cellRangeAddress.setRight(range.right()); 0714 if (range.bottom() > m_cellRangeAddress.bottom()) 0715 m_cellRangeAddress.setBottom(range.bottom()); 0716 } else { 0717 m_cellRangeAddress = range; 0718 } 0719 } 0720 } 0721 }; 0722 0723 } // namespace Charting 0724 0725 #endif