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