From 6fb6f6832c07508bc1988761bf5f3e2c20242441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Sat, 28 Jan 2012 15:54:17 +0100 Subject: [PATCH] Add libkdchart --- libkdchart/Doxyfile | 1241 +++++++++++ libkdchart/LICENSE.GPL | 1027 +++++++++ libkdchart/README.txt | 24 + libkdchart/include/KDChartAbstractAxis | 1 + .../include/KDChartAbstractCartesianDiagram | 1 + .../include/KDChartAbstractCoordinatePlane | 1 + libkdchart/include/KDChartAbstractDiagram | 1 + libkdchart/include/KDChartAbstractPieDiagram | 1 + .../include/KDChartAbstractPolarDiagram | 1 + libkdchart/include/KDChartAbstractProxyModel | 1 + .../include/KDChartAbstractTernaryDiagram | 1 + .../include/KDChartAbstractThreeDAttributes | 1 + libkdchart/include/KDChartAttributesModel | 1 + .../include/KDChartBackgroundAttributes | 1 + libkdchart/include/KDChartBarAttributes | 1 + libkdchart/include/KDChartBarDiagram | 1 + libkdchart/include/KDChartCartesianAxis | 1 + .../include/KDChartCartesianCoordinatePlane | 1 + libkdchart/include/KDChartChart | 1 + libkdchart/include/KDChartDataValueAttributes | 1 + libkdchart/include/KDChartDatasetProxyModel | 1 + libkdchart/include/KDChartDatasetSelector | 1 + libkdchart/include/KDChartDiagramObserver | 1 + libkdchart/include/KDChartEnums | 1 + libkdchart/include/KDChartFrameAttributes | 1 + libkdchart/include/KDChartGlobal | 1 + libkdchart/include/KDChartGridAttributes | 1 + libkdchart/include/KDChartHeaderFooter | 1 + libkdchart/include/KDChartLegend | 1 + libkdchart/include/KDChartLeveyJenningsAxis | 1 + .../KDChartLeveyJenningsCoordinatePlane | 1 + .../include/KDChartLeveyJenningsDiagram | 1 + libkdchart/include/KDChartLeveyJenningsGrid | 1 + .../KDChartLeveyJenningsGridAttributes | 1 + libkdchart/include/KDChartLineAttributes | 1 + libkdchart/include/KDChartLineDiagram | 1 + libkdchart/include/KDChartMarkerAttributes | 1 + libkdchart/include/KDChartMeasure | 1 + libkdchart/include/KDChartPaintContext | 1 + libkdchart/include/KDChartPalette | 1 + libkdchart/include/KDChartPieAttributes | 1 + libkdchart/include/KDChartPieDiagram | 1 + libkdchart/include/KDChartPlotter | 1 + .../include/KDChartPolarCoordinatePlane | 1 + libkdchart/include/KDChartPolarDiagram | 1 + libkdchart/include/KDChartPosition | 1 + libkdchart/include/KDChartRelativePosition | 1 + libkdchart/include/KDChartRingDiagram | 1 + libkdchart/include/KDChartRulerAttributes | 1 + libkdchart/include/KDChartStockBarAttributes | 1 + libkdchart/include/KDChartStockDiagram | 1 + libkdchart/include/KDChartTernaryAxis | 1 + .../include/KDChartTernaryCoordinatePlane | 1 + libkdchart/include/KDChartTernaryLineDiagram | 1 + libkdchart/include/KDChartTernaryPointDiagram | 1 + libkdchart/include/KDChartTextAttributes | 1 + libkdchart/include/KDChartThreeDBarAttributes | 1 + .../include/KDChartThreeDLineAttributes | 1 + libkdchart/include/KDChartThreeDPieAttributes | 1 + .../include/KDChartValueTrackerAttributes | 1 + libkdchart/include/KDChartWidget | 1 + libkdchart/include/KDChartZoomParameters | 1 + libkdchart/include/KDTextDocument | 1 + .../include/ui_KDChartDatasetSelector.h | 181 ++ libkdchart/kdablibfakes/include/KDABLibFakes | 1 + libkdchart/kdablibfakes/src/KDABLibFakes.h | 80 + .../src/CartesianCoordinateTransformation.h | 182 ++ libkdchart/src/KDChartAbstractArea.cpp | 156 ++ libkdchart/src/KDChartAbstractArea.h | 140 ++ libkdchart/src/KDChartAbstractAreaBase.cpp | 242 +++ libkdchart/src/KDChartAbstractAreaBase.h | 129 ++ libkdchart/src/KDChartAbstractAreaBase_p.h | 94 + libkdchart/src/KDChartAbstractAreaWidget.cpp | 201 ++ libkdchart/src/KDChartAbstractAreaWidget.h | 119 ++ libkdchart/src/KDChartAbstractAreaWidget_p.h | 90 + libkdchart/src/KDChartAbstractArea_p.h | 78 + libkdchart/src/KDChartAbstractAxis.cpp | 259 +++ libkdchart/src/KDChartAbstractAxis.h | 245 +++ libkdchart/src/KDChartAbstractAxis_p.h | 99 + .../src/KDChartAbstractCartesianDiagram.cpp | 193 ++ .../src/KDChartAbstractCartesianDiagram.h | 132 ++ .../src/KDChartAbstractCartesianDiagram_p.h | 105 + .../src/KDChartAbstractCoordinatePlane.cpp | 453 ++++ .../src/KDChartAbstractCoordinatePlane.h | 437 ++++ .../src/KDChartAbstractCoordinatePlane_p.h | 113 + libkdchart/src/KDChartAbstractDiagram.cpp | 1083 ++++++++++ libkdchart/src/KDChartAbstractDiagram.h | 731 +++++++ libkdchart/src/KDChartAbstractDiagram_p.h | 546 +++++ libkdchart/src/KDChartAbstractGrid.cpp | 120 ++ libkdchart/src/KDChartAbstractGrid.h | 159 ++ libkdchart/src/KDChartAbstractPieDiagram.cpp | 201 ++ libkdchart/src/KDChartAbstractPieDiagram.h | 88 + libkdchart/src/KDChartAbstractPieDiagram_p.h | 71 + .../src/KDChartAbstractPolarDiagram.cpp | 65 + libkdchart/src/KDChartAbstractPolarDiagram.h | 60 + .../src/KDChartAbstractPolarDiagram_p.h | 94 + libkdchart/src/KDChartAbstractProxyModel.cpp | 89 + libkdchart/src/KDChartAbstractProxyModel.h | 54 + .../src/KDChartAbstractThreeDAttributes.cpp | 119 ++ .../src/KDChartAbstractThreeDAttributes.h | 69 + .../src/KDChartAbstractThreeDAttributes_p.h | 63 + libkdchart/src/KDChartAttributesModel.cpp | 723 +++++++ libkdchart/src/KDChartAttributesModel.h | 161 ++ .../src/KDChartBackgroundAttributes.cpp | 157 ++ libkdchart/src/KDChartBackgroundAttributes.h | 81 + libkdchart/src/KDChartBarAttributes.cpp | 199 ++ libkdchart/src/KDChartBarAttributes.h | 84 + libkdchart/src/KDChartBarDiagram.cpp | 427 ++++ libkdchart/src/KDChartBarDiagram.h | 149 ++ libkdchart/src/KDChartBarDiagram_p.cpp | 236 ++ libkdchart/src/KDChartBarDiagram_p.h | 152 ++ libkdchart/src/KDChartCartesianAxis.cpp | 1900 +++++++++++++++++ libkdchart/src/KDChartCartesianAxis.h | 170 ++ libkdchart/src/KDChartCartesianAxis_p.h | 104 + .../src/KDChartCartesianCoordinatePlane.cpp | 924 ++++++++ .../src/KDChartCartesianCoordinatePlane.h | 481 +++++ .../src/KDChartCartesianCoordinatePlane_p.h | 138 ++ ...DChartCartesianDiagramDataCompressor_p.cpp | 828 +++++++ .../KDChartCartesianDiagramDataCompressor_p.h | 207 ++ libkdchart/src/KDChartCartesianGrid.cpp | 697 ++++++ libkdchart/src/KDChartCartesianGrid.h | 122 ++ libkdchart/src/KDChartChart.cpp | 1624 ++++++++++++++ libkdchart/src/KDChartChart.h | 518 +++++ libkdchart/src/KDChartChart_p.h | 168 ++ libkdchart/src/KDChartDataValueAttributes.cpp | 368 ++++ libkdchart/src/KDChartDataValueAttributes.h | 304 +++ libkdchart/src/KDChartDatasetProxyModel.cpp | 300 +++ libkdchart/src/KDChartDatasetProxyModel.h | 188 ++ libkdchart/src/KDChartDatasetSelector.cpp | 186 ++ libkdchart/src/KDChartDatasetSelector.h | 85 + libkdchart/src/KDChartDatasetSelector.ui | 164 ++ libkdchart/src/KDChartDiagramObserver.cpp | 152 ++ libkdchart/src/KDChartDiagramObserver.h | 88 + libkdchart/src/KDChartEnums.h | 349 +++ libkdchart/src/KDChartFrameAttributes.cpp | 130 ++ libkdchart/src/KDChartFrameAttributes.h | 72 + libkdchart/src/KDChartGlobal.h | 260 +++ libkdchart/src/KDChartGridAttributes.cpp | 291 +++ libkdchart/src/KDChartGridAttributes.h | 117 + libkdchart/src/KDChartHeaderFooter.cpp | 133 ++ libkdchart/src/KDChartHeaderFooter.h | 72 + libkdchart/src/KDChartHeaderFooter_p.h | 93 + libkdchart/src/KDChartLayoutItems.cpp | 1156 ++++++++++ libkdchart/src/KDChartLayoutItems.h | 473 ++++ libkdchart/src/KDChartLegend.cpp | 1148 ++++++++++ libkdchart/src/KDChartLegend.h | 398 ++++ libkdchart/src/KDChartLegend_p.h | 152 ++ libkdchart/src/KDChartLineAttributes.cpp | 144 ++ libkdchart/src/KDChartLineAttributes.h | 105 + libkdchart/src/KDChartLineDiagram.cpp | 478 +++++ libkdchart/src/KDChartLineDiagram.h | 160 ++ libkdchart/src/KDChartLineDiagram_p.cpp | 348 +++ libkdchart/src/KDChartLineDiagram_p.h | 159 ++ libkdchart/src/KDChartMarkerAttributes.cpp | 179 ++ libkdchart/src/KDChartMarkerAttributes.h | 103 + libkdchart/src/KDChartMeasure.cpp | 247 +++ libkdchart/src/KDChartMeasure.h | 191 ++ libkdchart/src/KDChartModelDataCache_p.cpp | 101 + libkdchart/src/KDChartModelDataCache_p.h | 341 +++ libkdchart/src/KDChartNormalBarDiagram_p.cpp | 206 ++ libkdchart/src/KDChartNormalBarDiagram_p.h | 59 + libkdchart/src/KDChartNormalLineDiagram_p.cpp | 196 ++ libkdchart/src/KDChartNormalLineDiagram_p.h | 52 + .../src/KDChartNormalLyingBarDiagram_p.cpp | 188 ++ .../src/KDChartNormalLyingBarDiagram_p.h | 59 + libkdchart/src/KDChartNormalPlotter_p.cpp | 129 ++ libkdchart/src/KDChartNormalPlotter_p.h | 52 + libkdchart/src/KDChartNullPaintDevice.h | 102 + libkdchart/src/KDChartPaintContext.cpp | 86 + libkdchart/src/KDChartPaintContext.h | 64 + libkdchart/src/KDChartPainterSaver_p.h | 111 + libkdchart/src/KDChartPalette.cpp | 184 ++ libkdchart/src/KDChartPalette.h | 97 + libkdchart/src/KDChartPercentBarDiagram_p.cpp | 210 ++ libkdchart/src/KDChartPercentBarDiagram_p.h | 43 + .../src/KDChartPercentLineDiagram_p.cpp | 235 ++ libkdchart/src/KDChartPercentLineDiagram_p.h | 42 + .../src/KDChartPercentLyingBarDiagram_p.cpp | 218 ++ .../src/KDChartPercentLyingBarDiagram_p.h | 42 + libkdchart/src/KDChartPercentPlotter_p.cpp | 269 +++ libkdchart/src/KDChartPercentPlotter_p.h | 41 + libkdchart/src/KDChartPieAttributes.cpp | 125 ++ libkdchart/src/KDChartPieAttributes.h | 92 + libkdchart/src/KDChartPieAttributes_p.h | 60 + libkdchart/src/KDChartPieDiagram.cpp | 1116 ++++++++++ libkdchart/src/KDChartPieDiagram.h | 116 + libkdchart/src/KDChartPieDiagram_p.h | 72 + libkdchart/src/KDChartPlotter.cpp | 392 ++++ libkdchart/src/KDChartPlotter.h | 126 ++ libkdchart/src/KDChartPlotter_p.cpp | 320 +++ libkdchart/src/KDChartPlotter_p.h | 156 ++ .../src/KDChartPolarCoordinatePlane.cpp | 435 ++++ libkdchart/src/KDChartPolarCoordinatePlane.h | 165 ++ .../src/KDChartPolarCoordinatePlane_p.h | 139 ++ libkdchart/src/KDChartPolarDiagram.cpp | 312 +++ libkdchart/src/KDChartPolarDiagram.h | 109 + libkdchart/src/KDChartPolarDiagram_p.h | 75 + libkdchart/src/KDChartPolarGrid.cpp | 115 + libkdchart/src/KDChartPolarGrid.h | 59 + libkdchart/src/KDChartPosition.cpp | 261 +++ libkdchart/src/KDChartPosition.h | 288 +++ libkdchart/src/KDChartPrintingParameters.cpp | 59 + libkdchart/src/KDChartPrintingParameters.h | 60 + libkdchart/src/KDChartRelativePosition.cpp | 232 ++ libkdchart/src/KDChartRelativePosition.h | 216 ++ libkdchart/src/KDChartRingDiagram.cpp | 522 +++++ libkdchart/src/KDChartRingDiagram.h | 97 + libkdchart/src/KDChartRingDiagram_p.h | 77 + libkdchart/src/KDChartRulerAttributes.cpp | 265 +++ libkdchart/src/KDChartRulerAttributes.h | 130 ++ libkdchart/src/KDChartSignalCompressor.cpp | 49 + libkdchart/src/KDChartSignalCompressor.h | 74 + libkdchart/src/KDChartStackedBarDiagram_p.cpp | 211 ++ libkdchart/src/KDChartStackedBarDiagram_p.h | 42 + .../src/KDChartStackedLineDiagram_p.cpp | 241 +++ libkdchart/src/KDChartStackedLineDiagram_p.h | 42 + .../src/KDChartStackedLyingBarDiagram_p.cpp | 205 ++ .../src/KDChartStackedLyingBarDiagram_p.h | 42 + libkdchart/src/KDChartStockBarAttributes.cpp | 107 + libkdchart/src/KDChartStockBarAttributes.h | 63 + libkdchart/src/KDChartStockDiagram.cpp | 372 ++++ libkdchart/src/KDChartStockDiagram.h | 119 ++ libkdchart/src/KDChartStockDiagram_p.cpp | 535 +++++ libkdchart/src/KDChartStockDiagram_p.h | 91 + libkdchart/src/KDChartTextArea.cpp | 111 + libkdchart/src/KDChartTextArea.h | 85 + libkdchart/src/KDChartTextArea_p.h | 84 + libkdchart/src/KDChartTextAttributes.cpp | 265 +++ libkdchart/src/KDChartTextAttributes.h | 210 ++ libkdchart/src/KDChartThreeDBarAttributes.cpp | 112 + libkdchart/src/KDChartThreeDBarAttributes.h | 71 + libkdchart/src/KDChartThreeDBarAttributes_p.h | 63 + .../src/KDChartThreeDLineAttributes.cpp | 113 + libkdchart/src/KDChartThreeDLineAttributes.h | 70 + .../src/KDChartThreeDLineAttributes_p.h | 64 + libkdchart/src/KDChartThreeDPieAttributes.cpp | 97 + libkdchart/src/KDChartThreeDPieAttributes.h | 69 + libkdchart/src/KDChartThreeDPieAttributes_p.h | 62 + .../src/KDChartValueTrackerAttributes.cpp | 139 ++ .../src/KDChartValueTrackerAttributes.h | 112 + libkdchart/src/KDChartWidget.cpp | 633 ++++++ libkdchart/src/KDChartWidget.h | 240 +++ libkdchart/src/KDChartWidget_p.h | 69 + libkdchart/src/KDChartZoomParameters.h | 78 + libkdchart/src/KDTextDocument.cpp | 101 + libkdchart/src/KDTextDocument.h | 67 + libkdchart/src/Makefile | 5 + .../KDChartTextLabelCache.cpp | 321 +++ .../KDChartTextLabelCache.h | 148 ++ libkdchart/src/Scenery/ChartGraphicsItem.cpp | 39 + libkdchart/src/Scenery/ChartGraphicsItem.h | 54 + libkdchart/src/Scenery/ReverseMapper.cpp | 176 ++ libkdchart/src/Scenery/ReverseMapper.h | 77 + .../Ternary/KDChartAbstractTernaryDiagram.cpp | 79 + .../Ternary/KDChartAbstractTernaryDiagram.h | 63 + .../Ternary/KDChartAbstractTernaryDiagram_p.h | 120 ++ libkdchart/src/Ternary/KDChartTernaryAxis.cpp | 284 +++ libkdchart/src/Ternary/KDChartTernaryAxis.h | 96 + .../Ternary/KDChartTernaryCoordinatePlane.cpp | 205 ++ .../Ternary/KDChartTernaryCoordinatePlane.h | 66 + .../Ternary/KDChartTernaryCoordinatePlane_p.h | 90 + libkdchart/src/Ternary/KDChartTernaryGrid.cpp | 216 ++ libkdchart/src/Ternary/KDChartTernaryGrid.h | 76 + .../src/Ternary/KDChartTernaryLineDiagram.cpp | 160 ++ .../src/Ternary/KDChartTernaryLineDiagram.h | 54 + .../src/Ternary/KDChartTernaryLineDiagram_p.h | 73 + .../Ternary/KDChartTernaryPointDiagram.cpp | 142 ++ .../src/Ternary/KDChartTernaryPointDiagram.h | 53 + .../Ternary/KDChartTernaryPointDiagram_p.h | 75 + libkdchart/src/Ternary/TernaryConstants.cpp | 46 + libkdchart/src/Ternary/TernaryConstants.h | 45 + libkdchart/src/Ternary/TernaryPoint.cpp | 104 + libkdchart/src/Ternary/TernaryPoint.h | 56 + libkdchart/src/kdchart_export.h | 57 + libkdchart/src/ui_KDChartDatasetSelector.h | 181 ++ 275 files changed, 46323 insertions(+) create mode 100644 libkdchart/Doxyfile create mode 100644 libkdchart/LICENSE.GPL create mode 100644 libkdchart/README.txt create mode 100644 libkdchart/include/KDChartAbstractAxis create mode 100644 libkdchart/include/KDChartAbstractCartesianDiagram create mode 100644 libkdchart/include/KDChartAbstractCoordinatePlane create mode 100644 libkdchart/include/KDChartAbstractDiagram create mode 100644 libkdchart/include/KDChartAbstractPieDiagram create mode 100644 libkdchart/include/KDChartAbstractPolarDiagram create mode 100644 libkdchart/include/KDChartAbstractProxyModel create mode 100644 libkdchart/include/KDChartAbstractTernaryDiagram create mode 100644 libkdchart/include/KDChartAbstractThreeDAttributes create mode 100644 libkdchart/include/KDChartAttributesModel create mode 100644 libkdchart/include/KDChartBackgroundAttributes create mode 100644 libkdchart/include/KDChartBarAttributes create mode 100644 libkdchart/include/KDChartBarDiagram create mode 100644 libkdchart/include/KDChartCartesianAxis create mode 100644 libkdchart/include/KDChartCartesianCoordinatePlane create mode 100644 libkdchart/include/KDChartChart create mode 100644 libkdchart/include/KDChartDataValueAttributes create mode 100644 libkdchart/include/KDChartDatasetProxyModel create mode 100644 libkdchart/include/KDChartDatasetSelector create mode 100644 libkdchart/include/KDChartDiagramObserver create mode 100644 libkdchart/include/KDChartEnums create mode 100644 libkdchart/include/KDChartFrameAttributes create mode 100644 libkdchart/include/KDChartGlobal create mode 100644 libkdchart/include/KDChartGridAttributes create mode 100644 libkdchart/include/KDChartHeaderFooter create mode 100644 libkdchart/include/KDChartLegend create mode 100644 libkdchart/include/KDChartLeveyJenningsAxis create mode 100644 libkdchart/include/KDChartLeveyJenningsCoordinatePlane create mode 100644 libkdchart/include/KDChartLeveyJenningsDiagram create mode 100644 libkdchart/include/KDChartLeveyJenningsGrid create mode 100644 libkdchart/include/KDChartLeveyJenningsGridAttributes create mode 100644 libkdchart/include/KDChartLineAttributes create mode 100644 libkdchart/include/KDChartLineDiagram create mode 100644 libkdchart/include/KDChartMarkerAttributes create mode 100644 libkdchart/include/KDChartMeasure create mode 100644 libkdchart/include/KDChartPaintContext create mode 100644 libkdchart/include/KDChartPalette create mode 100644 libkdchart/include/KDChartPieAttributes create mode 100644 libkdchart/include/KDChartPieDiagram create mode 100644 libkdchart/include/KDChartPlotter create mode 100644 libkdchart/include/KDChartPolarCoordinatePlane create mode 100644 libkdchart/include/KDChartPolarDiagram create mode 100644 libkdchart/include/KDChartPosition create mode 100644 libkdchart/include/KDChartRelativePosition create mode 100644 libkdchart/include/KDChartRingDiagram create mode 100644 libkdchart/include/KDChartRulerAttributes create mode 100644 libkdchart/include/KDChartStockBarAttributes create mode 100644 libkdchart/include/KDChartStockDiagram create mode 100644 libkdchart/include/KDChartTernaryAxis create mode 100644 libkdchart/include/KDChartTernaryCoordinatePlane create mode 100644 libkdchart/include/KDChartTernaryLineDiagram create mode 100644 libkdchart/include/KDChartTernaryPointDiagram create mode 100644 libkdchart/include/KDChartTextAttributes create mode 100644 libkdchart/include/KDChartThreeDBarAttributes create mode 100644 libkdchart/include/KDChartThreeDLineAttributes create mode 100644 libkdchart/include/KDChartThreeDPieAttributes create mode 100644 libkdchart/include/KDChartValueTrackerAttributes create mode 100644 libkdchart/include/KDChartWidget create mode 100644 libkdchart/include/KDChartZoomParameters create mode 100644 libkdchart/include/KDTextDocument create mode 100644 libkdchart/include/ui_KDChartDatasetSelector.h create mode 100644 libkdchart/kdablibfakes/include/KDABLibFakes create mode 100644 libkdchart/kdablibfakes/src/KDABLibFakes.h create mode 100644 libkdchart/src/CartesianCoordinateTransformation.h create mode 100644 libkdchart/src/KDChartAbstractArea.cpp create mode 100644 libkdchart/src/KDChartAbstractArea.h create mode 100644 libkdchart/src/KDChartAbstractAreaBase.cpp create mode 100644 libkdchart/src/KDChartAbstractAreaBase.h create mode 100644 libkdchart/src/KDChartAbstractAreaBase_p.h create mode 100644 libkdchart/src/KDChartAbstractAreaWidget.cpp create mode 100644 libkdchart/src/KDChartAbstractAreaWidget.h create mode 100644 libkdchart/src/KDChartAbstractAreaWidget_p.h create mode 100644 libkdchart/src/KDChartAbstractArea_p.h create mode 100644 libkdchart/src/KDChartAbstractAxis.cpp create mode 100644 libkdchart/src/KDChartAbstractAxis.h create mode 100644 libkdchart/src/KDChartAbstractAxis_p.h create mode 100644 libkdchart/src/KDChartAbstractCartesianDiagram.cpp create mode 100644 libkdchart/src/KDChartAbstractCartesianDiagram.h create mode 100644 libkdchart/src/KDChartAbstractCartesianDiagram_p.h create mode 100644 libkdchart/src/KDChartAbstractCoordinatePlane.cpp create mode 100644 libkdchart/src/KDChartAbstractCoordinatePlane.h create mode 100644 libkdchart/src/KDChartAbstractCoordinatePlane_p.h create mode 100644 libkdchart/src/KDChartAbstractDiagram.cpp create mode 100644 libkdchart/src/KDChartAbstractDiagram.h create mode 100644 libkdchart/src/KDChartAbstractDiagram_p.h create mode 100644 libkdchart/src/KDChartAbstractGrid.cpp create mode 100644 libkdchart/src/KDChartAbstractGrid.h create mode 100644 libkdchart/src/KDChartAbstractPieDiagram.cpp create mode 100644 libkdchart/src/KDChartAbstractPieDiagram.h create mode 100644 libkdchart/src/KDChartAbstractPieDiagram_p.h create mode 100644 libkdchart/src/KDChartAbstractPolarDiagram.cpp create mode 100644 libkdchart/src/KDChartAbstractPolarDiagram.h create mode 100644 libkdchart/src/KDChartAbstractPolarDiagram_p.h create mode 100644 libkdchart/src/KDChartAbstractProxyModel.cpp create mode 100644 libkdchart/src/KDChartAbstractProxyModel.h create mode 100644 libkdchart/src/KDChartAbstractThreeDAttributes.cpp create mode 100644 libkdchart/src/KDChartAbstractThreeDAttributes.h create mode 100644 libkdchart/src/KDChartAbstractThreeDAttributes_p.h create mode 100644 libkdchart/src/KDChartAttributesModel.cpp create mode 100644 libkdchart/src/KDChartAttributesModel.h create mode 100644 libkdchart/src/KDChartBackgroundAttributes.cpp create mode 100644 libkdchart/src/KDChartBackgroundAttributes.h create mode 100644 libkdchart/src/KDChartBarAttributes.cpp create mode 100644 libkdchart/src/KDChartBarAttributes.h create mode 100644 libkdchart/src/KDChartBarDiagram.cpp create mode 100644 libkdchart/src/KDChartBarDiagram.h create mode 100644 libkdchart/src/KDChartBarDiagram_p.cpp create mode 100644 libkdchart/src/KDChartBarDiagram_p.h create mode 100644 libkdchart/src/KDChartCartesianAxis.cpp create mode 100644 libkdchart/src/KDChartCartesianAxis.h create mode 100644 libkdchart/src/KDChartCartesianAxis_p.h create mode 100644 libkdchart/src/KDChartCartesianCoordinatePlane.cpp create mode 100644 libkdchart/src/KDChartCartesianCoordinatePlane.h create mode 100644 libkdchart/src/KDChartCartesianCoordinatePlane_p.h create mode 100644 libkdchart/src/KDChartCartesianDiagramDataCompressor_p.cpp create mode 100644 libkdchart/src/KDChartCartesianDiagramDataCompressor_p.h create mode 100644 libkdchart/src/KDChartCartesianGrid.cpp create mode 100644 libkdchart/src/KDChartCartesianGrid.h create mode 100644 libkdchart/src/KDChartChart.cpp create mode 100644 libkdchart/src/KDChartChart.h create mode 100644 libkdchart/src/KDChartChart_p.h create mode 100644 libkdchart/src/KDChartDataValueAttributes.cpp create mode 100644 libkdchart/src/KDChartDataValueAttributes.h create mode 100644 libkdchart/src/KDChartDatasetProxyModel.cpp create mode 100644 libkdchart/src/KDChartDatasetProxyModel.h create mode 100644 libkdchart/src/KDChartDatasetSelector.cpp create mode 100644 libkdchart/src/KDChartDatasetSelector.h create mode 100644 libkdchart/src/KDChartDatasetSelector.ui create mode 100644 libkdchart/src/KDChartDiagramObserver.cpp create mode 100644 libkdchart/src/KDChartDiagramObserver.h create mode 100644 libkdchart/src/KDChartEnums.h create mode 100644 libkdchart/src/KDChartFrameAttributes.cpp create mode 100644 libkdchart/src/KDChartFrameAttributes.h create mode 100644 libkdchart/src/KDChartGlobal.h create mode 100644 libkdchart/src/KDChartGridAttributes.cpp create mode 100644 libkdchart/src/KDChartGridAttributes.h create mode 100644 libkdchart/src/KDChartHeaderFooter.cpp create mode 100644 libkdchart/src/KDChartHeaderFooter.h create mode 100644 libkdchart/src/KDChartHeaderFooter_p.h create mode 100644 libkdchart/src/KDChartLayoutItems.cpp create mode 100644 libkdchart/src/KDChartLayoutItems.h create mode 100644 libkdchart/src/KDChartLegend.cpp create mode 100644 libkdchart/src/KDChartLegend.h create mode 100644 libkdchart/src/KDChartLegend_p.h create mode 100644 libkdchart/src/KDChartLineAttributes.cpp create mode 100644 libkdchart/src/KDChartLineAttributes.h create mode 100644 libkdchart/src/KDChartLineDiagram.cpp create mode 100644 libkdchart/src/KDChartLineDiagram.h create mode 100644 libkdchart/src/KDChartLineDiagram_p.cpp create mode 100644 libkdchart/src/KDChartLineDiagram_p.h create mode 100644 libkdchart/src/KDChartMarkerAttributes.cpp create mode 100644 libkdchart/src/KDChartMarkerAttributes.h create mode 100644 libkdchart/src/KDChartMeasure.cpp create mode 100644 libkdchart/src/KDChartMeasure.h create mode 100644 libkdchart/src/KDChartModelDataCache_p.cpp create mode 100644 libkdchart/src/KDChartModelDataCache_p.h create mode 100644 libkdchart/src/KDChartNormalBarDiagram_p.cpp create mode 100644 libkdchart/src/KDChartNormalBarDiagram_p.h create mode 100644 libkdchart/src/KDChartNormalLineDiagram_p.cpp create mode 100644 libkdchart/src/KDChartNormalLineDiagram_p.h create mode 100644 libkdchart/src/KDChartNormalLyingBarDiagram_p.cpp create mode 100644 libkdchart/src/KDChartNormalLyingBarDiagram_p.h create mode 100644 libkdchart/src/KDChartNormalPlotter_p.cpp create mode 100644 libkdchart/src/KDChartNormalPlotter_p.h create mode 100644 libkdchart/src/KDChartNullPaintDevice.h create mode 100644 libkdchart/src/KDChartPaintContext.cpp create mode 100644 libkdchart/src/KDChartPaintContext.h create mode 100644 libkdchart/src/KDChartPainterSaver_p.h create mode 100644 libkdchart/src/KDChartPalette.cpp create mode 100644 libkdchart/src/KDChartPalette.h create mode 100644 libkdchart/src/KDChartPercentBarDiagram_p.cpp create mode 100644 libkdchart/src/KDChartPercentBarDiagram_p.h create mode 100644 libkdchart/src/KDChartPercentLineDiagram_p.cpp create mode 100644 libkdchart/src/KDChartPercentLineDiagram_p.h create mode 100644 libkdchart/src/KDChartPercentLyingBarDiagram_p.cpp create mode 100644 libkdchart/src/KDChartPercentLyingBarDiagram_p.h create mode 100644 libkdchart/src/KDChartPercentPlotter_p.cpp create mode 100644 libkdchart/src/KDChartPercentPlotter_p.h create mode 100644 libkdchart/src/KDChartPieAttributes.cpp create mode 100644 libkdchart/src/KDChartPieAttributes.h create mode 100644 libkdchart/src/KDChartPieAttributes_p.h create mode 100644 libkdchart/src/KDChartPieDiagram.cpp create mode 100644 libkdchart/src/KDChartPieDiagram.h create mode 100644 libkdchart/src/KDChartPieDiagram_p.h create mode 100644 libkdchart/src/KDChartPlotter.cpp create mode 100644 libkdchart/src/KDChartPlotter.h create mode 100644 libkdchart/src/KDChartPlotter_p.cpp create mode 100644 libkdchart/src/KDChartPlotter_p.h create mode 100644 libkdchart/src/KDChartPolarCoordinatePlane.cpp create mode 100644 libkdchart/src/KDChartPolarCoordinatePlane.h create mode 100644 libkdchart/src/KDChartPolarCoordinatePlane_p.h create mode 100644 libkdchart/src/KDChartPolarDiagram.cpp create mode 100644 libkdchart/src/KDChartPolarDiagram.h create mode 100644 libkdchart/src/KDChartPolarDiagram_p.h create mode 100644 libkdchart/src/KDChartPolarGrid.cpp create mode 100644 libkdchart/src/KDChartPolarGrid.h create mode 100644 libkdchart/src/KDChartPosition.cpp create mode 100644 libkdchart/src/KDChartPosition.h create mode 100644 libkdchart/src/KDChartPrintingParameters.cpp create mode 100644 libkdchart/src/KDChartPrintingParameters.h create mode 100644 libkdchart/src/KDChartRelativePosition.cpp create mode 100644 libkdchart/src/KDChartRelativePosition.h create mode 100644 libkdchart/src/KDChartRingDiagram.cpp create mode 100644 libkdchart/src/KDChartRingDiagram.h create mode 100644 libkdchart/src/KDChartRingDiagram_p.h create mode 100644 libkdchart/src/KDChartRulerAttributes.cpp create mode 100644 libkdchart/src/KDChartRulerAttributes.h create mode 100644 libkdchart/src/KDChartSignalCompressor.cpp create mode 100644 libkdchart/src/KDChartSignalCompressor.h create mode 100644 libkdchart/src/KDChartStackedBarDiagram_p.cpp create mode 100644 libkdchart/src/KDChartStackedBarDiagram_p.h create mode 100644 libkdchart/src/KDChartStackedLineDiagram_p.cpp create mode 100644 libkdchart/src/KDChartStackedLineDiagram_p.h create mode 100644 libkdchart/src/KDChartStackedLyingBarDiagram_p.cpp create mode 100644 libkdchart/src/KDChartStackedLyingBarDiagram_p.h create mode 100644 libkdchart/src/KDChartStockBarAttributes.cpp create mode 100644 libkdchart/src/KDChartStockBarAttributes.h create mode 100644 libkdchart/src/KDChartStockDiagram.cpp create mode 100644 libkdchart/src/KDChartStockDiagram.h create mode 100644 libkdchart/src/KDChartStockDiagram_p.cpp create mode 100644 libkdchart/src/KDChartStockDiagram_p.h create mode 100644 libkdchart/src/KDChartTextArea.cpp create mode 100644 libkdchart/src/KDChartTextArea.h create mode 100644 libkdchart/src/KDChartTextArea_p.h create mode 100644 libkdchart/src/KDChartTextAttributes.cpp create mode 100644 libkdchart/src/KDChartTextAttributes.h create mode 100644 libkdchart/src/KDChartThreeDBarAttributes.cpp create mode 100644 libkdchart/src/KDChartThreeDBarAttributes.h create mode 100644 libkdchart/src/KDChartThreeDBarAttributes_p.h create mode 100644 libkdchart/src/KDChartThreeDLineAttributes.cpp create mode 100644 libkdchart/src/KDChartThreeDLineAttributes.h create mode 100644 libkdchart/src/KDChartThreeDLineAttributes_p.h create mode 100644 libkdchart/src/KDChartThreeDPieAttributes.cpp create mode 100644 libkdchart/src/KDChartThreeDPieAttributes.h create mode 100644 libkdchart/src/KDChartThreeDPieAttributes_p.h create mode 100644 libkdchart/src/KDChartValueTrackerAttributes.cpp create mode 100644 libkdchart/src/KDChartValueTrackerAttributes.h create mode 100644 libkdchart/src/KDChartWidget.cpp create mode 100644 libkdchart/src/KDChartWidget.h create mode 100644 libkdchart/src/KDChartWidget_p.h create mode 100644 libkdchart/src/KDChartZoomParameters.h create mode 100644 libkdchart/src/KDTextDocument.cpp create mode 100644 libkdchart/src/KDTextDocument.h create mode 100644 libkdchart/src/Makefile create mode 100644 libkdchart/src/PrerenderedElements/KDChartTextLabelCache.cpp create mode 100644 libkdchart/src/PrerenderedElements/KDChartTextLabelCache.h create mode 100644 libkdchart/src/Scenery/ChartGraphicsItem.cpp create mode 100644 libkdchart/src/Scenery/ChartGraphicsItem.h create mode 100644 libkdchart/src/Scenery/ReverseMapper.cpp create mode 100644 libkdchart/src/Scenery/ReverseMapper.h create mode 100644 libkdchart/src/Ternary/KDChartAbstractTernaryDiagram.cpp create mode 100644 libkdchart/src/Ternary/KDChartAbstractTernaryDiagram.h create mode 100644 libkdchart/src/Ternary/KDChartAbstractTernaryDiagram_p.h create mode 100644 libkdchart/src/Ternary/KDChartTernaryAxis.cpp create mode 100644 libkdchart/src/Ternary/KDChartTernaryAxis.h create mode 100644 libkdchart/src/Ternary/KDChartTernaryCoordinatePlane.cpp create mode 100644 libkdchart/src/Ternary/KDChartTernaryCoordinatePlane.h create mode 100644 libkdchart/src/Ternary/KDChartTernaryCoordinatePlane_p.h create mode 100644 libkdchart/src/Ternary/KDChartTernaryGrid.cpp create mode 100644 libkdchart/src/Ternary/KDChartTernaryGrid.h create mode 100644 libkdchart/src/Ternary/KDChartTernaryLineDiagram.cpp create mode 100644 libkdchart/src/Ternary/KDChartTernaryLineDiagram.h create mode 100644 libkdchart/src/Ternary/KDChartTernaryLineDiagram_p.h create mode 100644 libkdchart/src/Ternary/KDChartTernaryPointDiagram.cpp create mode 100644 libkdchart/src/Ternary/KDChartTernaryPointDiagram.h create mode 100644 libkdchart/src/Ternary/KDChartTernaryPointDiagram_p.h create mode 100644 libkdchart/src/Ternary/TernaryConstants.cpp create mode 100644 libkdchart/src/Ternary/TernaryConstants.h create mode 100644 libkdchart/src/Ternary/TernaryPoint.cpp create mode 100644 libkdchart/src/Ternary/TernaryPoint.h create mode 100644 libkdchart/src/kdchart_export.h create mode 100644 libkdchart/src/ui_KDChartDatasetSelector.h diff --git a/libkdchart/Doxyfile b/libkdchart/Doxyfile new file mode 100644 index 0000000..6f95fa0 --- /dev/null +++ b/libkdchart/Doxyfile @@ -0,0 +1,1241 @@ +# Doxyfile 1.4.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "KD Chart 2" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = [rev. 2.4] + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = reimpl=\n[reimplemented]\n + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES +# Mirko: I have enabled this to be able to browse through the doxygen docs to +# find some hidden relationships between sources. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = YES + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = NO + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = YES + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = YES + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +# Mirko: I did not find any use of sectionname in our sources. +# ENABLED_SECTIONS = v100 + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is YES. + +SHOW_DIRECTORIES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the progam writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. +# +# Note: We don not include the include directory, but at the beginning of each +# class documentation we have Qt-like comments showing the +# #include statement to be used. + +INPUT = src + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS = *.h *.cpp + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# be excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = .moc .obj .ui + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* +# +# khz: Note that all *Grid.cpp/*Grid.h classes are internal, since users +# are supposed to use the coordinate plane's accessor methods only. + +EXCLUDE_PATTERNS = moc_* *.moc *_p.h ui_*.h *Grid.* CartesianCoordinateTransformation.* kdchart_export.* + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = images + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = KDChart + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = refman + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = images/footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 1 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = YES + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = kdchart.tag + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = NO + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +# Mirko: this is very useful, please install graphviz if you do not have it. +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 800 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 600 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = YES + +SERVER_BASED_SEARCH = NO diff --git a/libkdchart/LICENSE.GPL b/libkdchart/LICENSE.GPL new file mode 100644 index 0000000..7f506fb --- /dev/null +++ b/libkdchart/LICENSE.GPL @@ -0,0 +1,1027 @@ + + The KD Chart Library is Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. + + You may use, distribute and copy the KD Chart Library under the terms of + the GNU General Public License version 2 or under the terms of GNU General + Public License version 3 both of which are displayed below. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for + software and other kinds of works. + + The licenses for most software and other practical works are designed + to take away your freedom to share and change the works. By contrast, + the GNU General Public License is intended to guarantee your freedom to + share and change all versions of a program--to make sure it remains free + software for all its users. We, the Free Software Foundation, use the + GNU General Public License for most of our software; it applies also to + any other work released this way by its authors. You can apply it to + your programs, too. + + When we speak of free software, we are referring to freedom, not + price. Our General Public Licenses are designed to make sure that you + have the freedom to distribute copies of free software (and charge for + them if you wish), that you receive source code or can get it if you + want it, that you can change the software or use pieces of it in new + free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you + these rights or asking you to surrender the rights. Therefore, you have + certain responsibilities if you distribute copies of the software, or if + you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether + gratis or for a fee, you must pass on to the recipients the same + freedoms that you received. You must make sure that they, too, receive + or can get the source code. And you must show them these terms so they + know their rights. + + Developers that use the GNU GPL protect your rights with two steps: + (1) assert copyright on the software, and (2) offer you this License + giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains + that there is no warranty for this free software. For both users' and + authors' sake, the GPL requires that modified versions be marked as + changed, so that their problems will not be attributed erroneously to + authors of previous versions. + + Some devices are designed to deny users access to install or run + modified versions of the software inside them, although the manufacturer + can do so. This is fundamentally incompatible with the aim of + protecting users' freedom to change the software. The systematic + pattern of such abuse occurs in the area of products for individuals to + use, which is precisely where it is most unacceptable. Therefore, we + have designed this version of the GPL to prohibit the practice for those + products. If such problems arise substantially in other domains, we + stand ready to extend this provision to those domains in future versions + of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. + States should not allow patents to restrict development and use of + software on general-purpose computers, but in those that do, we wish to + avoid the special danger that patents applied to a free program could + make it effectively proprietary. To prevent this, the GPL assures that + patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and + modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of + works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this + License. Each licensee is addressed as "you". "Licensees" and + "recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work + in a fashion requiring copyright permission, other than the making of an + exact copy. The resulting work is called a "modified version" of the + earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based + on the Program. + + To "propagate" a work means to do anything with it that, without + permission, would make you directly or secondarily liable for + infringement under applicable copyright law, except executing it on a + computer or modifying a private copy. Propagation includes copying, + distribution (with or without modification), making available to the + public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other + parties to make or receive copies. Mere interaction with a user through + a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" + to the extent that it includes a convenient and prominently visible + feature that (1) displays an appropriate copyright notice, and (2) + tells the user that there is no warranty for the work (except to the + extent that warranties are provided), that licensees may convey the + work under this License, and how to view a copy of this License. If + the interface presents a list of user commands or options, such as a + menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work + for making modifications to it. "Object code" means any non-source + form of a work. + + A "Standard Interface" means an interface that either is an official + standard defined by a recognized standards body, or, in the case of + interfaces specified for a particular programming language, one that + is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other + than the work as a whole, that (a) is included in the normal form of + packaging a Major Component, but which is not part of that Major + Component, and (b) serves only to enable use of the work with that + Major Component, or to implement a Standard Interface for which an + implementation is available to the public in source code form. A + "Major Component", in this context, means a major essential component + (kernel, window system, and so on) of the specific operating system + (if any) on which the executable work runs, or a compiler used to + produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all + the source code needed to generate, install, and (for an executable + work) run the object code and to modify the work, including scripts to + control those activities. However, it does not include the work's + System Libraries, or general-purpose tools or generally available free + programs which are used unmodified in performing those activities but + which are not part of the work. For example, Corresponding Source + includes interface definition files associated with source files for + the work, and the source code for shared libraries and dynamically + linked subprograms that the work is specifically designed to require, + such as by intimate data communication or control flow between those + subprograms and other parts of the work. + + The Corresponding Source need not include anything that users + can regenerate automatically from other parts of the Corresponding + Source. + + The Corresponding Source for a work in source code form is that + same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of + copyright on the Program, and are irrevocable provided the stated + conditions are met. This License explicitly affirms your unlimited + permission to run the unmodified Program. The output from running a + covered work is covered by this License only if the output, given its + content, constitutes a covered work. This License acknowledges your + rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not + convey, without conditions so long as your license otherwise remains + in force. You may convey covered works to others for the sole purpose + of having them make modifications exclusively for you, or provide you + with facilities for running those works, provided that you comply with + the terms of this License in conveying all material for which you do + not control copyright. Those thus making or running the covered works + for you must do so exclusively on your behalf, under your direction + and control, on terms that prohibit them from making any copies of + your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under + the conditions stated below. Sublicensing is not allowed; section 10 + makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological + measure under any applicable law fulfilling obligations under article + 11 of the WIPO copyright treaty adopted on 20 December 1996, or + similar laws prohibiting or restricting circumvention of such + measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such circumvention + is effected by exercising rights under this License with respect to + the covered work, and you disclaim any intention to limit operation or + modification of the work as a means of enforcing, against the work's + users, your or third parties' legal rights to forbid circumvention of + technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you + receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice; + keep intact all notices stating that this License and any + non-permissive terms added in accord with section 7 apply to the code; + keep intact all notices of the absence of any warranty; and give all + recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, + and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to + produce it from the Program, in the form of source code under the + terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent + works, which are not by their nature extensions of the covered work, + and which are not combined with it such as to form a larger program, + in or on a volume of a storage or distribution medium, is called an + "aggregate" if the compilation and its resulting copyright are not + used to limit the access or legal rights of the compilation's users + beyond what the individual works permit. Inclusion of a covered work + in an aggregate does not cause this License to apply to the other + parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms + of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this License, + in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded + from the Corresponding Source as a System Library, need not be + included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any + tangible personal property which is normally used for personal, family, + or household purposes, or (2) anything designed or sold for incorporation + into a dwelling. In determining whether a product is a consumer product, + doubtful cases shall be resolved in favor of coverage. For a particular + product received by a particular user, "normally used" refers to a + typical or common use of that class of product, regardless of the status + of the particular user or of the way in which the particular user + actually uses, or expects or is expected to use, the product. A product + is a consumer product regardless of whether the product has substantial + commercial, industrial or non-consumer uses, unless such uses represent + the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, + procedures, authorization keys, or other information required to install + and execute modified versions of a covered work in that User Product from + a modified version of its Corresponding Source. The information must + suffice to ensure that the continued functioning of the modified object + code is in no case prevented or interfered with solely because + modification has been made. + + If you convey an object code work under this section in, or with, or + specifically for use in, a User Product, and the conveying occurs as + part of a transaction in which the right of possession and use of the + User Product is transferred to the recipient in perpetuity or for a + fixed term (regardless of how the transaction is characterized), the + Corresponding Source conveyed under this section must be accompanied + by the Installation Information. But this requirement does not apply + if neither you nor any third party retains the ability to install + modified object code on the User Product (for example, the work has + been installed in ROM). + + The requirement to provide Installation Information does not include a + requirement to continue to provide support service, warranty, or updates + for a work that has been modified or installed by the recipient, or for + the User Product in which it has been modified or installed. Access to a + network may be denied when the modification itself materially and + adversely affects the operation of the network or violates the rules and + protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, + in accord with this section must be in a format that is publicly + documented (and with an implementation available to the public in + source code form), and must require no special password or key for + unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this + License by making exceptions from one or more of its conditions. + Additional permissions that are applicable to the entire Program shall + be treated as though they were included in this License, to the extent + that they are valid under applicable law. If additional permissions + apply only to part of the Program, that part may be used separately + under those permissions, but the entire Program remains governed by + this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option + remove any additional permissions from that copy, or from any part of + it. (Additional permissions may be written to require their own + removal in certain cases when you modify the work.) You may place + additional permissions on material, added by you to a covered work, + for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you + add to a covered work, you may (if authorized by the copyright holders of + that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further + restrictions" within the meaning of section 10. If the Program as you + received it, or any part of it, contains a notice stating that it is + governed by this License along with a term that is a further + restriction, you may remove that term. If a license document contains + a further restriction but permits relicensing or conveying under this + License, you may add to a covered work material governed by the terms + of that license document, provided that the further restriction does + not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you + must place, in the relevant source files, a statement of the + additional terms that apply to those files, or a notice indicating + where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the + form of a separately written license, or stated as exceptions; + the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly + provided under this License. Any attempt otherwise to propagate or + modify it is void, and will automatically terminate your rights under + this License (including any patent licenses granted under the third + paragraph of section 11). + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the copyright + holder fails to notify you of the violation by some reasonable means + prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from that + copyright holder, and you cure the violation prior to 30 days after + your receipt of the notice. + + Termination of your rights under this section does not terminate the + licenses of parties who have received copies or rights from you under + this License. If your rights have been terminated and not permanently + reinstated, you do not qualify to receive new licenses for the same + material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or + run a copy of the Program. Ancillary propagation of a covered work + occurring solely as a consequence of using peer-to-peer transmission + to receive a copy likewise does not require acceptance. However, + nothing other than this License grants you permission to propagate or + modify any covered work. These actions infringe copyright if you do + not accept this License. Therefore, by modifying or propagating a + covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically + receives a license from the original licensors, to run, modify and + propagate that work, subject to this License. You are not responsible + for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a covered + work results from an entity transaction, each party to that + transaction who receives a copy of the work also receives whatever + licenses to the work the party's predecessor in interest had or could + give under the previous paragraph, plus a right to possession of the + Corresponding Source of the work from the predecessor in interest, if + the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the + rights granted or affirmed under this License. For example, you may + not impose a license fee, royalty, or other charge for exercise of + rights granted under this License, and you may not initiate litigation + (including a cross-claim or counterclaim in a lawsuit) alleging that + any patent claim is infringed by making, using, selling, offering for + sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this + License of the Program or a work on which the Program is based. The + work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims + owned or controlled by the contributor, whether already acquired or + hereafter acquired, that would be infringed by some manner, permitted + by this License, of making, using, or selling its contributor version, + but do not include claims that would be infringed only as a + consequence of further modification of the contributor version. For + purposes of this definition, "control" includes the right to grant + patent sublicenses in a manner consistent with the requirements of + this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free + patent license under the contributor's essential patent claims, to + make, use, sell, offer for sale, import and otherwise run, modify and + propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express + agreement or commitment, however denominated, not to enforce a patent + (such as an express permission to practice a patent or covenant not to + sue for patent infringement). To "grant" such a patent license to a + party means to make such an agreement or commitment not to enforce a + patent against the party. + + If you convey a covered work, knowingly relying on a patent license, + and the Corresponding Source of the work is not available for anyone + to copy, free of charge and under the terms of this License, through a + publicly available network server or other readily accessible means, + then you must either (1) cause the Corresponding Source to be so + available, or (2) arrange to deprive yourself of the benefit of the + patent license for this particular work, or (3) arrange, in a manner + consistent with the requirements of this License, to extend the patent + license to downstream recipients. "Knowingly relying" means you have + actual knowledge that, but for the patent license, your conveying the + covered work in a country, or your recipient's use of the covered work + in a country, would infringe one or more identifiable patents in that + country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or + arrangement, you convey, or propagate by procuring conveyance of, a + covered work, and grant a patent license to some of the parties + receiving the covered work authorizing them to use, propagate, modify + or convey a specific copy of the covered work, then the patent license + you grant is automatically extended to all recipients of the covered + work and works based on it. + + A patent license is "discriminatory" if it does not include within + the scope of its coverage, prohibits the exercise of, or is + conditioned on the non-exercise of one or more of the rights that are + specifically granted under this License. You may not convey a covered + work if you are a party to an arrangement with a third party that is + in the business of distributing software, under which you make payment + to the third party based on the extent of your activity of conveying + the work, and under which the third party grants, to any of the + parties who would receive the covered work from you, a discriminatory + patent license (a) in connection with copies of the covered work + conveyed by you (or copies made from those copies), or (b) primarily + for and in connection with specific products or compilations that + contain the covered work, unless you entered into that arrangement, + or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting + any implied license or other defenses to infringement that may + otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot convey a + covered work so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you may + not convey it at all. For example, if you agree to terms that obligate you + to collect a royalty for further conveying from those to whom you convey + the Program, the only way you could satisfy both those terms and this + License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have + permission to link or combine any covered work with a work licensed + under version 3 of the GNU Affero General Public License into a single + combined work, and to convey the resulting work. The terms of this + License will continue to apply to the part which is the covered work, + but the special requirements of the GNU Affero General Public License, + section 13, concerning interaction through a network will apply to the + combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of + the GNU General Public License from time to time. Such new versions will + be similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies that a certain numbered version of the GNU General + Public License "or any later version" applies to it, you have the + option of following the terms and conditions either of that numbered + version or of any later version published by the Free Software + Foundation. If the Program does not specify a version number of the + GNU General Public License, you may choose any version ever published + by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future + versions of the GNU General Public License can be used, that proxy's + public statement of acceptance of a version permanently authorizes you + to choose that version for the Program. + + Later license versions may give you additional or different + permissions. However, no additional obligations are imposed on any + author or copyright holder as a result of your choosing to follow a + later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT + HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY + OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM + IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF + ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS + THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY + GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE + USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), + EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF + SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided + above cannot be given local legal effect according to their terms, + reviewing courts shall apply local law that most closely approximates + an absolute waiver of all civil liability in connection with the + Program, unless a warranty or assumption of liability accompanies a + copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it + free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest + to attach them to the start of each source file to most effectively + state the exclusion of warranty; and each file should have at least + the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short + notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + + The hypothetical commands `show w' and `show c' should show the appropriate + parts of the General Public License. Of course, your program's commands + might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, + if any, to sign a "copyright disclaimer" for the program, if necessary. + For more information on this, and how to apply and follow the GNU GPL, see + . + + The GNU General Public License does not permit incorporating your program + into proprietary programs. If your program is a subroutine library, you + may consider it more useful to permit linking proprietary applications with + the library. If this is what you want to do, use the GNU Lesser General + Public License instead of this License. But first, please read + . + +------------------------------------------------------------------------- diff --git a/libkdchart/README.txt b/libkdchart/README.txt new file mode 100644 index 0000000..1b0963d --- /dev/null +++ b/libkdchart/README.txt @@ -0,0 +1,24 @@ +Welcome to KD Chart 2, Klaralvdalens Datakonsult's charting engine for Qt! + +Please refer to the license file for conditions of use. + +After reading the introductory overview files in doc/ +you will find more information at three places: + +detailed browsable API reference: doc/refman/index.html + or: http://docs.kdab.com/kdchart/2.4/ +programmers manual with examples: doc/manual/kdchart.pdf +our sorted example programs: examples/ + +In case of additional questions during evaluation or use of +KD Chart please contact our technical support by mail: + + kdchart-support@kdab.com + +We thank you for your interest in KD Chart and we are here +to assist you if the documentation leaves open questions or +if you just need some help with finding the best way in which +to realize your charting ideas. + +The KDAB KD Chart Support Team. + diff --git a/libkdchart/include/KDChartAbstractAxis b/libkdchart/include/KDChartAbstractAxis new file mode 100644 index 0000000..f2edb6e --- /dev/null +++ b/libkdchart/include/KDChartAbstractAxis @@ -0,0 +1 @@ +#include "../src/KDChartAbstractAxis.h" diff --git a/libkdchart/include/KDChartAbstractCartesianDiagram b/libkdchart/include/KDChartAbstractCartesianDiagram new file mode 100644 index 0000000..bb16f0f --- /dev/null +++ b/libkdchart/include/KDChartAbstractCartesianDiagram @@ -0,0 +1 @@ +#include "../src/KDChartAbstractCartesianDiagram.h" diff --git a/libkdchart/include/KDChartAbstractCoordinatePlane b/libkdchart/include/KDChartAbstractCoordinatePlane new file mode 100644 index 0000000..0e4a579 --- /dev/null +++ b/libkdchart/include/KDChartAbstractCoordinatePlane @@ -0,0 +1 @@ +#include "../src/KDChartAbstractCoordinatePlane.h" diff --git a/libkdchart/include/KDChartAbstractDiagram b/libkdchart/include/KDChartAbstractDiagram new file mode 100644 index 0000000..b6ab18e --- /dev/null +++ b/libkdchart/include/KDChartAbstractDiagram @@ -0,0 +1 @@ +#include "../src/KDChartAbstractDiagram.h" diff --git a/libkdchart/include/KDChartAbstractPieDiagram b/libkdchart/include/KDChartAbstractPieDiagram new file mode 100644 index 0000000..a2e70ba --- /dev/null +++ b/libkdchart/include/KDChartAbstractPieDiagram @@ -0,0 +1 @@ +#include "../src/KDChartAbstractPieDiagram.h" diff --git a/libkdchart/include/KDChartAbstractPolarDiagram b/libkdchart/include/KDChartAbstractPolarDiagram new file mode 100644 index 0000000..5df6668 --- /dev/null +++ b/libkdchart/include/KDChartAbstractPolarDiagram @@ -0,0 +1 @@ +#include "../src/KDChartAbstractPolarDiagram.h" diff --git a/libkdchart/include/KDChartAbstractProxyModel b/libkdchart/include/KDChartAbstractProxyModel new file mode 100644 index 0000000..d2d315d --- /dev/null +++ b/libkdchart/include/KDChartAbstractProxyModel @@ -0,0 +1 @@ +#include "../src/KDChartAbstractProxyModel.h" diff --git a/libkdchart/include/KDChartAbstractTernaryDiagram b/libkdchart/include/KDChartAbstractTernaryDiagram new file mode 100644 index 0000000..1a2ceb8 --- /dev/null +++ b/libkdchart/include/KDChartAbstractTernaryDiagram @@ -0,0 +1 @@ +#include "../src/KDChartAbstractTernaryDiagram.h" diff --git a/libkdchart/include/KDChartAbstractThreeDAttributes b/libkdchart/include/KDChartAbstractThreeDAttributes new file mode 100644 index 0000000..4b85895 --- /dev/null +++ b/libkdchart/include/KDChartAbstractThreeDAttributes @@ -0,0 +1 @@ +#include "../src/KDChartAbstractThreeDAttributes.h" diff --git a/libkdchart/include/KDChartAttributesModel b/libkdchart/include/KDChartAttributesModel new file mode 100644 index 0000000..4107ffb --- /dev/null +++ b/libkdchart/include/KDChartAttributesModel @@ -0,0 +1 @@ +#include "../src/KDChartAttributesModel.h" diff --git a/libkdchart/include/KDChartBackgroundAttributes b/libkdchart/include/KDChartBackgroundAttributes new file mode 100644 index 0000000..f225d5d --- /dev/null +++ b/libkdchart/include/KDChartBackgroundAttributes @@ -0,0 +1 @@ +#include "../src/KDChartBackgroundAttributes.h" diff --git a/libkdchart/include/KDChartBarAttributes b/libkdchart/include/KDChartBarAttributes new file mode 100644 index 0000000..ebfabcc --- /dev/null +++ b/libkdchart/include/KDChartBarAttributes @@ -0,0 +1 @@ +#include "../src/KDChartBarAttributes.h" diff --git a/libkdchart/include/KDChartBarDiagram b/libkdchart/include/KDChartBarDiagram new file mode 100644 index 0000000..81a851e --- /dev/null +++ b/libkdchart/include/KDChartBarDiagram @@ -0,0 +1 @@ +#include "../src/KDChartBarDiagram.h" diff --git a/libkdchart/include/KDChartCartesianAxis b/libkdchart/include/KDChartCartesianAxis new file mode 100644 index 0000000..ff1b11d --- /dev/null +++ b/libkdchart/include/KDChartCartesianAxis @@ -0,0 +1 @@ +#include "../src/KDChartCartesianAxis.h" diff --git a/libkdchart/include/KDChartCartesianCoordinatePlane b/libkdchart/include/KDChartCartesianCoordinatePlane new file mode 100644 index 0000000..9f9949d --- /dev/null +++ b/libkdchart/include/KDChartCartesianCoordinatePlane @@ -0,0 +1 @@ +#include "../src/KDChartCartesianCoordinatePlane.h" diff --git a/libkdchart/include/KDChartChart b/libkdchart/include/KDChartChart new file mode 100644 index 0000000..e25cb55 --- /dev/null +++ b/libkdchart/include/KDChartChart @@ -0,0 +1 @@ +#include "../src/KDChartChart.h" diff --git a/libkdchart/include/KDChartDataValueAttributes b/libkdchart/include/KDChartDataValueAttributes new file mode 100644 index 0000000..84b0bfb --- /dev/null +++ b/libkdchart/include/KDChartDataValueAttributes @@ -0,0 +1 @@ +#include "../src/KDChartDataValueAttributes.h" diff --git a/libkdchart/include/KDChartDatasetProxyModel b/libkdchart/include/KDChartDatasetProxyModel new file mode 100644 index 0000000..b7ffe3f --- /dev/null +++ b/libkdchart/include/KDChartDatasetProxyModel @@ -0,0 +1 @@ +#include "../src/KDChartDatasetProxyModel.h" diff --git a/libkdchart/include/KDChartDatasetSelector b/libkdchart/include/KDChartDatasetSelector new file mode 100644 index 0000000..1567d38 --- /dev/null +++ b/libkdchart/include/KDChartDatasetSelector @@ -0,0 +1 @@ +#include "../src/KDChartDatasetSelector.h" diff --git a/libkdchart/include/KDChartDiagramObserver b/libkdchart/include/KDChartDiagramObserver new file mode 100644 index 0000000..a4c7f6c --- /dev/null +++ b/libkdchart/include/KDChartDiagramObserver @@ -0,0 +1 @@ +#include "../src/KDChartDiagramObserver.h" diff --git a/libkdchart/include/KDChartEnums b/libkdchart/include/KDChartEnums new file mode 100644 index 0000000..30c46a8 --- /dev/null +++ b/libkdchart/include/KDChartEnums @@ -0,0 +1 @@ +#include "../src/KDChartEnums.h" diff --git a/libkdchart/include/KDChartFrameAttributes b/libkdchart/include/KDChartFrameAttributes new file mode 100644 index 0000000..7c40ffb --- /dev/null +++ b/libkdchart/include/KDChartFrameAttributes @@ -0,0 +1 @@ +#include "../src/KDChartFrameAttributes.h" diff --git a/libkdchart/include/KDChartGlobal b/libkdchart/include/KDChartGlobal new file mode 100644 index 0000000..26019c8 --- /dev/null +++ b/libkdchart/include/KDChartGlobal @@ -0,0 +1 @@ +#include "../src/KDChartGlobal.h" diff --git a/libkdchart/include/KDChartGridAttributes b/libkdchart/include/KDChartGridAttributes new file mode 100644 index 0000000..cbe8f48 --- /dev/null +++ b/libkdchart/include/KDChartGridAttributes @@ -0,0 +1 @@ +#include "../src/KDChartGridAttributes.h" diff --git a/libkdchart/include/KDChartHeaderFooter b/libkdchart/include/KDChartHeaderFooter new file mode 100644 index 0000000..51d2883 --- /dev/null +++ b/libkdchart/include/KDChartHeaderFooter @@ -0,0 +1 @@ +#include "../src/KDChartHeaderFooter.h" diff --git a/libkdchart/include/KDChartLegend b/libkdchart/include/KDChartLegend new file mode 100644 index 0000000..40fe204 --- /dev/null +++ b/libkdchart/include/KDChartLegend @@ -0,0 +1 @@ +#include "../src/KDChartLegend.h" diff --git a/libkdchart/include/KDChartLeveyJenningsAxis b/libkdchart/include/KDChartLeveyJenningsAxis new file mode 100644 index 0000000..364159f --- /dev/null +++ b/libkdchart/include/KDChartLeveyJenningsAxis @@ -0,0 +1 @@ +#include "../src/LeveyJennings/KDChartLeveyJenningsAxis.h" diff --git a/libkdchart/include/KDChartLeveyJenningsCoordinatePlane b/libkdchart/include/KDChartLeveyJenningsCoordinatePlane new file mode 100644 index 0000000..1dba9ca --- /dev/null +++ b/libkdchart/include/KDChartLeveyJenningsCoordinatePlane @@ -0,0 +1 @@ +#include "../src/LeveyJennings/KDChartLeveyJenningsCoordinatePlane.h" diff --git a/libkdchart/include/KDChartLeveyJenningsDiagram b/libkdchart/include/KDChartLeveyJenningsDiagram new file mode 100644 index 0000000..0f1e2d2 --- /dev/null +++ b/libkdchart/include/KDChartLeveyJenningsDiagram @@ -0,0 +1 @@ +#include "../src/LeveyJennings/KDChartLeveyJenningsDiagram.h" diff --git a/libkdchart/include/KDChartLeveyJenningsGrid b/libkdchart/include/KDChartLeveyJenningsGrid new file mode 100644 index 0000000..87f6917 --- /dev/null +++ b/libkdchart/include/KDChartLeveyJenningsGrid @@ -0,0 +1 @@ +#include "../src/LeveyJennings/KDChartLeveyJenningsGrid.h" diff --git a/libkdchart/include/KDChartLeveyJenningsGridAttributes b/libkdchart/include/KDChartLeveyJenningsGridAttributes new file mode 100644 index 0000000..32dd56d --- /dev/null +++ b/libkdchart/include/KDChartLeveyJenningsGridAttributes @@ -0,0 +1 @@ +#include "../src/LeveyJennings/KDChartLeveyJenningsGridAttributes.h" diff --git a/libkdchart/include/KDChartLineAttributes b/libkdchart/include/KDChartLineAttributes new file mode 100644 index 0000000..003899d --- /dev/null +++ b/libkdchart/include/KDChartLineAttributes @@ -0,0 +1 @@ +#include "../src/KDChartLineAttributes.h" diff --git a/libkdchart/include/KDChartLineDiagram b/libkdchart/include/KDChartLineDiagram new file mode 100644 index 0000000..e8bbe31 --- /dev/null +++ b/libkdchart/include/KDChartLineDiagram @@ -0,0 +1 @@ +#include "../src/KDChartLineDiagram.h" diff --git a/libkdchart/include/KDChartMarkerAttributes b/libkdchart/include/KDChartMarkerAttributes new file mode 100644 index 0000000..cc043e6 --- /dev/null +++ b/libkdchart/include/KDChartMarkerAttributes @@ -0,0 +1 @@ +#include "../src/KDChartMarkerAttributes.h" diff --git a/libkdchart/include/KDChartMeasure b/libkdchart/include/KDChartMeasure new file mode 100644 index 0000000..e9bd30a --- /dev/null +++ b/libkdchart/include/KDChartMeasure @@ -0,0 +1 @@ +#include "../src/KDChartMeasure.h" diff --git a/libkdchart/include/KDChartPaintContext b/libkdchart/include/KDChartPaintContext new file mode 100644 index 0000000..0dd1d0c --- /dev/null +++ b/libkdchart/include/KDChartPaintContext @@ -0,0 +1 @@ +#include "../src/KDChartPaintContext.h" diff --git a/libkdchart/include/KDChartPalette b/libkdchart/include/KDChartPalette new file mode 100644 index 0000000..85f0abe --- /dev/null +++ b/libkdchart/include/KDChartPalette @@ -0,0 +1 @@ +#include "../src/KDChartPalette.h" diff --git a/libkdchart/include/KDChartPieAttributes b/libkdchart/include/KDChartPieAttributes new file mode 100644 index 0000000..8f4c7ab --- /dev/null +++ b/libkdchart/include/KDChartPieAttributes @@ -0,0 +1 @@ +#include "../src/KDChartPieAttributes.h" diff --git a/libkdchart/include/KDChartPieDiagram b/libkdchart/include/KDChartPieDiagram new file mode 100644 index 0000000..9f48e78 --- /dev/null +++ b/libkdchart/include/KDChartPieDiagram @@ -0,0 +1 @@ +#include "../src/KDChartPieDiagram.h" diff --git a/libkdchart/include/KDChartPlotter b/libkdchart/include/KDChartPlotter new file mode 100644 index 0000000..f418eff --- /dev/null +++ b/libkdchart/include/KDChartPlotter @@ -0,0 +1 @@ +#include "../src/KDChartPlotter.h" diff --git a/libkdchart/include/KDChartPolarCoordinatePlane b/libkdchart/include/KDChartPolarCoordinatePlane new file mode 100644 index 0000000..5fe756c --- /dev/null +++ b/libkdchart/include/KDChartPolarCoordinatePlane @@ -0,0 +1 @@ +#include "../src/KDChartPolarCoordinatePlane.h" diff --git a/libkdchart/include/KDChartPolarDiagram b/libkdchart/include/KDChartPolarDiagram new file mode 100644 index 0000000..f85a6a2 --- /dev/null +++ b/libkdchart/include/KDChartPolarDiagram @@ -0,0 +1 @@ +#include "../src/KDChartPolarDiagram.h" diff --git a/libkdchart/include/KDChartPosition b/libkdchart/include/KDChartPosition new file mode 100644 index 0000000..10b53ea --- /dev/null +++ b/libkdchart/include/KDChartPosition @@ -0,0 +1 @@ +#include "../src/KDChartPosition.h" diff --git a/libkdchart/include/KDChartRelativePosition b/libkdchart/include/KDChartRelativePosition new file mode 100644 index 0000000..cb1acb5 --- /dev/null +++ b/libkdchart/include/KDChartRelativePosition @@ -0,0 +1 @@ +#include "../src/KDChartRelativePosition.h" diff --git a/libkdchart/include/KDChartRingDiagram b/libkdchart/include/KDChartRingDiagram new file mode 100644 index 0000000..346fd56 --- /dev/null +++ b/libkdchart/include/KDChartRingDiagram @@ -0,0 +1 @@ +#include "../src/KDChartRingDiagram.h" diff --git a/libkdchart/include/KDChartRulerAttributes b/libkdchart/include/KDChartRulerAttributes new file mode 100644 index 0000000..25b9d77 --- /dev/null +++ b/libkdchart/include/KDChartRulerAttributes @@ -0,0 +1 @@ +#include "../src/KDChartRulerAttributes.h" diff --git a/libkdchart/include/KDChartStockBarAttributes b/libkdchart/include/KDChartStockBarAttributes new file mode 100644 index 0000000..fc3040d --- /dev/null +++ b/libkdchart/include/KDChartStockBarAttributes @@ -0,0 +1 @@ +#include "../src/KDChartStockBarAttributes.h" diff --git a/libkdchart/include/KDChartStockDiagram b/libkdchart/include/KDChartStockDiagram new file mode 100644 index 0000000..8c38dcb --- /dev/null +++ b/libkdchart/include/KDChartStockDiagram @@ -0,0 +1 @@ +#include "../src/KDChartStockDiagram.h" diff --git a/libkdchart/include/KDChartTernaryAxis b/libkdchart/include/KDChartTernaryAxis new file mode 100644 index 0000000..3aa19cb --- /dev/null +++ b/libkdchart/include/KDChartTernaryAxis @@ -0,0 +1 @@ +#include "../src/Ternary/KDChartTernaryAxis.h" diff --git a/libkdchart/include/KDChartTernaryCoordinatePlane b/libkdchart/include/KDChartTernaryCoordinatePlane new file mode 100644 index 0000000..a5075ca --- /dev/null +++ b/libkdchart/include/KDChartTernaryCoordinatePlane @@ -0,0 +1 @@ +#include "../src/Ternary/KDChartTernaryCoordinatePlane.h" diff --git a/libkdchart/include/KDChartTernaryLineDiagram b/libkdchart/include/KDChartTernaryLineDiagram new file mode 100644 index 0000000..9b40bf1 --- /dev/null +++ b/libkdchart/include/KDChartTernaryLineDiagram @@ -0,0 +1 @@ +#include "../src/Ternary/KDChartTernaryLineDiagram.h" diff --git a/libkdchart/include/KDChartTernaryPointDiagram b/libkdchart/include/KDChartTernaryPointDiagram new file mode 100644 index 0000000..89b6d97 --- /dev/null +++ b/libkdchart/include/KDChartTernaryPointDiagram @@ -0,0 +1 @@ +#include "../src/Ternary/KDChartTernaryPointDiagram.h" diff --git a/libkdchart/include/KDChartTextAttributes b/libkdchart/include/KDChartTextAttributes new file mode 100644 index 0000000..872f46d --- /dev/null +++ b/libkdchart/include/KDChartTextAttributes @@ -0,0 +1 @@ +#include "../src/KDChartTextAttributes.h" diff --git a/libkdchart/include/KDChartThreeDBarAttributes b/libkdchart/include/KDChartThreeDBarAttributes new file mode 100644 index 0000000..b18d577 --- /dev/null +++ b/libkdchart/include/KDChartThreeDBarAttributes @@ -0,0 +1 @@ +#include "../src/KDChartThreeDBarAttributes.h" diff --git a/libkdchart/include/KDChartThreeDLineAttributes b/libkdchart/include/KDChartThreeDLineAttributes new file mode 100644 index 0000000..8451986 --- /dev/null +++ b/libkdchart/include/KDChartThreeDLineAttributes @@ -0,0 +1 @@ +#include "../src/KDChartThreeDLineAttributes.h" diff --git a/libkdchart/include/KDChartThreeDPieAttributes b/libkdchart/include/KDChartThreeDPieAttributes new file mode 100644 index 0000000..6a5d8cc --- /dev/null +++ b/libkdchart/include/KDChartThreeDPieAttributes @@ -0,0 +1 @@ +#include "../src/KDChartThreeDPieAttributes.h" diff --git a/libkdchart/include/KDChartValueTrackerAttributes b/libkdchart/include/KDChartValueTrackerAttributes new file mode 100644 index 0000000..6976e0a --- /dev/null +++ b/libkdchart/include/KDChartValueTrackerAttributes @@ -0,0 +1 @@ +#include "../src/KDChartValueTrackerAttributes.h" diff --git a/libkdchart/include/KDChartWidget b/libkdchart/include/KDChartWidget new file mode 100644 index 0000000..f64131f --- /dev/null +++ b/libkdchart/include/KDChartWidget @@ -0,0 +1 @@ +#include "../src/KDChartWidget.h" diff --git a/libkdchart/include/KDChartZoomParameters b/libkdchart/include/KDChartZoomParameters new file mode 100644 index 0000000..9a98aab --- /dev/null +++ b/libkdchart/include/KDChartZoomParameters @@ -0,0 +1 @@ +#include "../src/KDChartZoomParameters.h" diff --git a/libkdchart/include/KDTextDocument b/libkdchart/include/KDTextDocument new file mode 100644 index 0000000..513c8f0 --- /dev/null +++ b/libkdchart/include/KDTextDocument @@ -0,0 +1 @@ +#include "../src/KDTextDocument.h" diff --git a/libkdchart/include/ui_KDChartDatasetSelector.h b/libkdchart/include/ui_KDChartDatasetSelector.h new file mode 100644 index 0000000..4d8b0d2 --- /dev/null +++ b/libkdchart/include/ui_KDChartDatasetSelector.h @@ -0,0 +1,181 @@ +/******************************************************************************** +** Form generated from reading UI file 'KDChartDatasetSelector.ui' +** +** Created: Sat Jan 7 12:36:09 2012 +** by: Qt User Interface Compiler version 4.7.3 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef KDCHARTDATASETSELECTOR_H +#define KDCHARTDATASETSELECTOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Ui_DatasetSelector +{ +public: + QHBoxLayout *hboxLayout; + QGroupBox *groupBox; + QGridLayout *gridLayout; + QCheckBox *cbReverseColumns; + QLabel *label_5; + QSpinBox *sbStartColumn; + QLabel *label_2; + QSpinBox *sbColumnCount; + QLabel *label; + QLabel *label_6; + QSpinBox *sbStartRow; + QLabel *label_4; + QCheckBox *cbReverseRows; + QLabel *label_3; + QSpinBox *sbRowCount; + QSpacerItem *spacerItem; + + void setupUi(QWidget *DatasetSelector) + { + if (DatasetSelector->objectName().isEmpty()) + DatasetSelector->setObjectName(QString::fromUtf8("DatasetSelector")); + DatasetSelector->resize(728, 344); + QSizePolicy sizePolicy(static_cast(3), static_cast(3)); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(DatasetSelector->sizePolicy().hasHeightForWidth()); + DatasetSelector->setSizePolicy(sizePolicy); + DatasetSelector->setMinimumSize(QSize(0, 0)); + hboxLayout = new QHBoxLayout(DatasetSelector); +#ifndef Q_OS_MAC + hboxLayout->setSpacing(6); +#endif +#ifndef Q_OS_MAC + hboxLayout->setContentsMargins(9, 9, 9, 9); +#endif + hboxLayout->setObjectName(QString::fromUtf8("hboxLayout")); + groupBox = new QGroupBox(DatasetSelector); + groupBox->setObjectName(QString::fromUtf8("groupBox")); + groupBox->setCheckable(true); + groupBox->setChecked(false); + gridLayout = new QGridLayout(groupBox); +#ifndef Q_OS_MAC + gridLayout->setSpacing(6); +#endif +#ifndef Q_OS_MAC + gridLayout->setContentsMargins(9, 9, 9, 9); +#endif + gridLayout->setObjectName(QString::fromUtf8("gridLayout")); + cbReverseColumns = new QCheckBox(groupBox); + cbReverseColumns->setObjectName(QString::fromUtf8("cbReverseColumns")); + + gridLayout->addWidget(cbReverseColumns, 3, 1, 1, 3); + + label_5 = new QLabel(groupBox); + label_5->setObjectName(QString::fromUtf8("label_5")); + label_5->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout->addWidget(label_5, 3, 0, 1, 1); + + sbStartColumn = new QSpinBox(groupBox); + sbStartColumn->setObjectName(QString::fromUtf8("sbStartColumn")); + + gridLayout->addWidget(sbStartColumn, 2, 3, 1, 1); + + label_2 = new QLabel(groupBox); + label_2->setObjectName(QString::fromUtf8("label_2")); + label_2->setAlignment(Qt::AlignCenter); + + gridLayout->addWidget(label_2, 2, 2, 1, 1); + + sbColumnCount = new QSpinBox(groupBox); + sbColumnCount->setObjectName(QString::fromUtf8("sbColumnCount")); + + gridLayout->addWidget(sbColumnCount, 2, 1, 1, 1); + + label = new QLabel(groupBox); + label->setObjectName(QString::fromUtf8("label")); + label->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout->addWidget(label, 2, 0, 1, 1); + + label_6 = new QLabel(groupBox); + label_6->setObjectName(QString::fromUtf8("label_6")); + label_6->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout->addWidget(label_6, 1, 0, 1, 1); + + sbStartRow = new QSpinBox(groupBox); + sbStartRow->setObjectName(QString::fromUtf8("sbStartRow")); + + gridLayout->addWidget(sbStartRow, 0, 3, 1, 1); + + label_4 = new QLabel(groupBox); + label_4->setObjectName(QString::fromUtf8("label_4")); + label_4->setAlignment(Qt::AlignCenter); + + gridLayout->addWidget(label_4, 0, 2, 1, 1); + + cbReverseRows = new QCheckBox(groupBox); + cbReverseRows->setObjectName(QString::fromUtf8("cbReverseRows")); + + gridLayout->addWidget(cbReverseRows, 1, 1, 1, 3); + + label_3 = new QLabel(groupBox); + label_3->setObjectName(QString::fromUtf8("label_3")); + label_3->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout->addWidget(label_3, 0, 0, 1, 1); + + sbRowCount = new QSpinBox(groupBox); + sbRowCount->setObjectName(QString::fromUtf8("sbRowCount")); + + gridLayout->addWidget(sbRowCount, 0, 1, 1, 1); + + spacerItem = new QSpacerItem(169, 31, QSizePolicy::Minimum, QSizePolicy::Expanding); + + gridLayout->addItem(spacerItem, 4, 2, 1, 1); + + + hboxLayout->addWidget(groupBox); + + + retranslateUi(DatasetSelector); + + QMetaObject::connectSlotsByName(DatasetSelector); + } // setupUi + + void retranslateUi(QWidget *DatasetSelector) + { + DatasetSelector->setWindowTitle(QApplication::translate("DatasetSelector", "Data Selector", 0, QApplication::UnicodeUTF8)); + groupBox->setTitle(QApplication::translate("DatasetSelector", "Only display a subset of the model in the chart:", 0, QApplication::UnicodeUTF8)); + cbReverseColumns->setText(QApplication::translate("DatasetSelector", "in reverse order.", 0, QApplication::UnicodeUTF8)); + label_5->setText(QApplication::translate("DatasetSelector", "...", 0, QApplication::UnicodeUTF8)); + label_2->setText(QApplication::translate("DatasetSelector", "columns starting at column", 0, QApplication::UnicodeUTF8)); + label->setText(QApplication::translate("DatasetSelector", "Display", 0, QApplication::UnicodeUTF8)); + label_6->setText(QApplication::translate("DatasetSelector", "...", 0, QApplication::UnicodeUTF8)); + label_4->setText(QApplication::translate("DatasetSelector", "rows starting at row", 0, QApplication::UnicodeUTF8)); + cbReverseRows->setText(QApplication::translate("DatasetSelector", "in reverse order.", 0, QApplication::UnicodeUTF8)); + label_3->setText(QApplication::translate("DatasetSelector", "Display", 0, QApplication::UnicodeUTF8)); + } // retranslateUi + +}; + +namespace Ui { + class DatasetSelector: public Ui_DatasetSelector {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // KDCHARTDATASETSELECTOR_H diff --git a/libkdchart/kdablibfakes/include/KDABLibFakes b/libkdchart/kdablibfakes/include/KDABLibFakes new file mode 100644 index 0000000..b96bd3e --- /dev/null +++ b/libkdchart/kdablibfakes/include/KDABLibFakes @@ -0,0 +1 @@ +#include "../src/KDABLibFakes.h" diff --git a/libkdchart/kdablibfakes/src/KDABLibFakes.h b/libkdchart/kdablibfakes/src/KDABLibFakes.h new file mode 100644 index 0000000..fcd1822 --- /dev/null +++ b/libkdchart/kdablibfakes/src/KDABLibFakes.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDAB_LIB_FAKES_H +#define KDAB_LIB_FAKES_H + +#if defined Q_OS_DARWIN +/* On Mac OS X, ensure that will define std::isnan */ +#define _GLIBCPP_USE_C99 1 +#endif + +#include + +#ifdef Q_OS_SOLARIS +#include +#include +#endif + +#include + + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define DEGTORAD(d) (d)*M_PI/180 + +// Smybian's math.h doesn't define a trunc function +#if defined(Q_OS_SYMBIAN) || defined(QT_SIMULATOR) +#define trunc(x) (double) ((int) (x + (x >= 0.0 ? -0.5 : 0.5))) +#endif + +// We use our own ISNAN / ISINF in the code to detect +// that we defined them. +// reason: Windows / MacOS do not have isnan() / isinf() +#if defined (Q_OS_WIN) && defined(_MSC_VER) +#include +#define ISNAN(x ) _isnan(x ) +#define ISINF(x ) (!(_finite(x ) + _isnan(x ) ) ) +#elif defined (Q_OS_DARWIN) || defined (Q_OS_CYGWIN) +#define ISNAN(x) std::isnan(x) +#define ISINF(x) std::isinf(x) +#else +#define ISNAN(x) isnan(x) +#define ISINF(x) isinf(x) +#endif + + +// We wrap every for() by extra { } to work around +// the scope bug for loop counters in MS Visual C++ v6 +#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) +/* This is done in Qt41 qglobal.h but not Qt42*/ +#if QT_VERSION < 0x040200 +#define for if (0) {} else for +#endif +#define KDAB_FOREACH( v, c ) if (0) {} else Q_FOREACH( v, c ) +#else +#define KDAB_FOREACH( v, c ) Q_FOREACH( v, c ) +#endif + +#endif diff --git a/libkdchart/src/CartesianCoordinateTransformation.h b/libkdchart/src/CartesianCoordinateTransformation.h new file mode 100644 index 0000000..5f3cef0 --- /dev/null +++ b/libkdchart/src/CartesianCoordinateTransformation.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef CARTESIANCOORDINATETRANSFORMATION_H +#define CARTESIANCOORDINATETRANSFORMATION_H + +#include +#include +#include + +#include "KDChartZoomParameters.h" + +#include +#include + +namespace KDChart { + + // FIXME: if this struct is used more often, we need to make it a class + // with proper accessor methods: + + /** + * \internal + */ + struct CoordinateTransformation { + QRectF diagramRect; + // represents the distance of the diagram coordinate origin to the + // origin of the coordinate plane space: + QPointF originTranslation; + // make a vector base for R2: + double unitVectorX; + double unitVectorY; + // implement isometric scaling: + double isoScaleX; + double isoScaleY; + + CartesianCoordinatePlane::AxesCalcMode axesCalcModeY; + CartesianCoordinatePlane::AxesCalcMode axesCalcModeX; + + ZoomParameters zoom; + + typedef QPair< qreal, qreal > qrealPair; + + inline qreal makeLogarithmic( qrealPair reference, qreal value ) const + { + qreal result = value; + + qreal relation; + if( reference.second == -1.0 ) + relation = 1.0; + else if( reference.second == 1.0 ) + relation = 1.0; + else if( reference.second > 0.0 ) + relation = reference.second / log10( reference.second ); + else if( result < 0.0 ) + relation = reference.second / log10( -reference.second ); + else + relation = 10.0; + + if( value == 0.0 ) + result = 0.0;//std::numeric_limits< qreal >::quiet_NaN(); + else if( value > 0.0 ) + result = log10( result ) * relation; + else if( value < 0.0 ) + result = -log10( -result ) * relation; + + if( value == 0.0 ) + return result; + + result -= log10( qAbs( reference.first ) ) * relation; + result *= ( reference.second - reference.first ) / relation / (log10(qAbs(reference.second))-log10(qAbs(reference.first))); + result += reference.first; + + if( reference.first < 0.0 ) + { + result += reference.first; + result -= reference.second; + result = reference.first - result + reference.second; + + } + + return result; + } + + inline QPointF translate( const QPointF& diagramPoint ) const + { + // ### de-inline me + QPointF result = originTranslation; + QPointF tempPoint = diagramPoint; + + const QRectF& diagRect = diagramRect; + + if( axesCalcModeY == CartesianCoordinatePlane::Logarithmic ) + { + tempPoint.setY( makeLogarithmic( qrealPair( diagRect.bottom(), diagRect.y() ), tempPoint.y() ) ); + } + if( axesCalcModeX == CartesianCoordinatePlane::Logarithmic ) + { + tempPoint.setX( makeLogarithmic( qrealPair( diagRect.x(), diagRect.right() ), tempPoint.x() ) ); + } + + tempPoint.rx() += diagRect.width() / (2.0 * zoom.xFactor); + tempPoint.ry() += diagRect.height() / (2.0 * zoom.yFactor); + + tempPoint.rx() -= diagRect.width() * zoom.xCenter; + tempPoint.ry() -= diagRect.height() * zoom.yCenter; + + // translate: xNew = (xOld - diaX) * zoomX + diaX + tempPoint.setX( ( tempPoint.x() - diagRect.x() ) * zoom.xFactor + diagRect.x() ); + tempPoint.setY( ( tempPoint.y() - diagRect.y() ) * zoom.yFactor + diagRect.y() ); + + result.rx() += isoScaleX * unitVectorX * tempPoint.x(); + result.ry() += isoScaleY * unitVectorY * tempPoint.y(); + + return result; + } + + // convert screen points to value space points + inline const QPointF translateBack( const QPointF& screenPoint ) const + { + qreal x, y; + + x = screenPoint.x() - originTranslation.x(); + y = screenPoint.y() - originTranslation.y(); + + x /= isoScaleX * unitVectorX; + y /= isoScaleY * unitVectorY; + + // translate back: xOld = DiaX + (xNew - DiaX) / zoomX + x = diagramRect.x() + (x - diagramRect.x()) / zoom.xFactor; + y = diagramRect.y() + (y - diagramRect.y()) / zoom.yFactor; + + x += diagramRect.width() * zoom.xCenter; + y += diagramRect.height() * zoom.yCenter; + + x -= diagramRect.width() / (2.0 * zoom.xFactor); + y -= diagramRect.height() / (2.0 * zoom.yFactor); + + /* + if ( axesCalcModeY == CartesianCoordinatePlane::Logarithmic ){ + tempPoint.setY( makeLogarithmic( diagramRect.y(), tempPoint.y() ) ); + //qDebug() << "Y: " << tempPoint.y(); + } + if ( axesCalcModeX == CartesianCoordinatePlane::Logarithmic ){ + //qDebug() << "X diagramRect.x(): " << diagramRect.x(); + //qDebug() << "X tempPoint old: " << tempPoint; + tempPoint.setX( makeLogarithmic( diagramRect.width(), tempPoint.x() ) ); + //qDebug() << "X tempPoint new: " << tempPoint; + } +// qDebug() << "CoordinateTransformation::translate() using diagramRect: " +// << diagramRect.x() << diagramRect.y() << diagramRect.width() << diagramRect.height(); + */ + + return QPointF(x, y); + } + + }; + + typedef QList CoordinateTransformationList; + +} + +#endif + diff --git a/libkdchart/src/KDChartAbstractArea.cpp b/libkdchart/src/KDChartAbstractArea.cpp new file mode 100644 index 0000000..95732a6 --- /dev/null +++ b/libkdchart/src/KDChartAbstractArea.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractArea.h" +#include "KDChartAbstractArea_p.h" + +#include + +#include +#include + +#include + + +using namespace KDChart; + +#define d (d_func()) + +AbstractArea::Private::Private() : + AbstractAreaBase::Private() +{ + // this bloc left empty intentionally +} + + +AbstractArea::Private::~Private() +{ + // this bloc left empty intentionally +} + + +AbstractArea::AbstractArea() + : QObject() + , KDChart::AbstractAreaBase() + , KDChart::AbstractLayoutItem() +{ + init(); +} + +AbstractArea::~AbstractArea() +{ + // this bloc left empty intentionally +} + + +void AbstractArea::init() +{ + d->amountOfLeftOverlap = 0; + d->amountOfRightOverlap = 0; + d->amountOfTopOverlap = 0; + d->amountOfBottomOverlap = 0; +} + + +int AbstractArea::leftOverlap( bool doNotRecalculate ) const +{ + // Re-calculate the sizes, + // so we also get the amountOf..Overlap members set newly: + if( ! doNotRecalculate ) + sizeHint(); + return d->amountOfLeftOverlap; +} +int AbstractArea::rightOverlap( bool doNotRecalculate ) const +{ + // Re-calculate the sizes, + // so we also get the amountOf..Overlap members set newly: + if( ! doNotRecalculate ) + sizeHint(); + return d->amountOfRightOverlap; +} +int AbstractArea::topOverlap( bool doNotRecalculate ) const +{ + // Re-calculate the sizes, + // so we also get the amountOf..Overlap members set newly: + if( ! doNotRecalculate ) + sizeHint(); + return d->amountOfTopOverlap; +} +int AbstractArea::bottomOverlap( bool doNotRecalculate ) const +{ + // Re-calculate the sizes, + // so we also get the amountOf..Overlap members set newly: + if( ! doNotRecalculate ) + sizeHint(); + return d->amountOfBottomOverlap; +} + + +void AbstractArea::paintIntoRect( QPainter& painter, const QRect& rect ) +{ + const QRect oldGeometry( geometry() ); + if( oldGeometry != rect ) + setGeometry( rect ); + painter.translate( rect.left(), rect.top() ); + paintAll( painter ); + painter.translate( -rect.left(), -rect.top() ); + if( oldGeometry != rect ) + setGeometry( oldGeometry ); +} + +void AbstractArea::paintAll( QPainter& painter ) +{ + // Paint the background and frame + const QRect overlappingArea( geometry().adjusted( + -d->amountOfLeftOverlap, + -d->amountOfTopOverlap, + d->amountOfRightOverlap, + d->amountOfBottomOverlap ) ); + paintBackground( painter, overlappingArea ); + paintFrame( painter, overlappingArea ); + + // temporarily adjust the widget size, to be sure all content gets calculated + // to fit into the inner rectangle + const QRect oldGeometry( areaGeometry() ); + QRect inner( innerRect() ); + inner.moveTo( + oldGeometry.left() + inner.left(), + oldGeometry.top() + inner.top() ); + const bool needAdjustGeometry = oldGeometry != inner; + if( needAdjustGeometry ) + setGeometry( inner ); + paint( &painter ); + if( needAdjustGeometry ) + setGeometry( oldGeometry ); + //qDebug() << "AbstractAreaWidget::paintAll() done."; +} + +QRect AbstractArea::areaGeometry() const +{ + return geometry(); +} + +void AbstractArea::positionHasChanged() +{ + emit positionChanged( this ); +} + diff --git a/libkdchart/src/KDChartAbstractArea.h b/libkdchart/src/KDChartAbstractArea.h new file mode 100644 index 0000000..7c1cb90 --- /dev/null +++ b/libkdchart/src/KDChartAbstractArea.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTAREA_H +#define KDCHARTABSTRACTAREA_H + +#include + +#include "KDChartGlobal.h" +#include "KDChartAbstractAreaBase.h" +#include "KDChartLayoutItems.h" + +namespace KDChart { + + +/** + * @class AbstractArea KDChartAbstractArea.h + * @brief An area in the chart with a background, a frame, etc. + * + * AbstractArea is the base class for all non-widget chart elements that have + * a set of background attributes and frame attributes, such as + * coordinate planes or axes. + * + * @note This class inherits from AbstractAreaBase, AbstractLayoutItem, QObject. + * The reason for this tripple inheritance is that neither AbstractAreaBase nor + * AbstractLayoutItem are QObject. + */ +class KDCHART_EXPORT AbstractArea : public QObject, + public AbstractAreaBase, + public AbstractLayoutItem +{ + Q_OBJECT + + Q_DISABLE_COPY( AbstractArea ) + KDCHART_DECLARE_PRIVATE_DERIVED( AbstractArea ) + + +public: + virtual ~AbstractArea() ; + +// virtual AbstractArea * clone() const = 0; + /** + * @brief Draws the background and frame, then calls paint(). + * + * In most cases there is no need to overwrite this method in a derived + * class, but you would overwrite AbstractLayoutItem::paint() instead. + */ + virtual void paintIntoRect( QPainter& painter, const QRect& rect ); + + /** + * Call paintAll, if you want the background and the frame to be drawn + * before the normal paint() is invoked automatically. + */ + virtual void paintAll( QPainter& painter ); + + /** + * This is called at layout time by KDChart::AutoSpacerLayoutItem::sizeHint(). + * + * The method triggers AbstractArea::sizeHint() to find out the + * amount of overlap at the left edge of the area. + * + * \note The default implementation is not using any caching, + * it might make sense to implement a more sophisticated solution + * for derived classes that have complex work to do in sizeHint(). + * All we have here is a primitive flag to be set by the caller + * if it is sure that no sizeHint() needs to be called. + */ + virtual int leftOverlap( bool doNotRecalculate=false ) const; + /** + * This is called at layout time by KDChart::AutoSpacerLayoutItem::sizeHint(). + * + * The method triggers AbstractArea::sizeHint() to find out the + * amount of overlap at the right edge of the area. + * + * \note The default implementation is not using any caching, + * it might make sense to implement a more sophisticated solution + * for derived classes that have complex work to do in sizeHint(). + * All we have here is a primitive flag to be set by the caller + * if it is sure that no sizeHint() needs to be called. + */ + virtual int rightOverlap( bool doNotRecalculate=false ) const; + /** + * This is called at layout time by KDChart::AutoSpacerLayoutItem::sizeHint(). + * + * The method triggers AbstractArea::sizeHint() to find out the + * amount of overlap at the top edge of the area. + * + * \note The default implementation is not using any caching, + * it might make sense to implement a more sophisticated solution + * for derived classes that have complex work to do in sizeHint(). + * All we have here is a primitive flag to be set by the caller + * if it is sure that no sizeHint() needs to be called. + */ + virtual int topOverlap( bool doNotRecalculate=false ) const; + /** + * This is called at layout time by KDChart:AutoSpacerLayoutItem::sizeHint(). + * + * The method triggers AbstractArea::sizeHint() to find out the + * amount of overlap at the bottom edge of the area. + * + * \note The default implementation is not using any caching, + * it might make sense to implement a more sophisticated solution + * for derived classes that have complex work to do in sizeHint(). + * All we have here is a primitive flag to be set by the caller + * if it is sure that no sizeHint() needs to be called. + */ + virtual int bottomOverlap( bool doNotRecalculate=false ) const; + +protected: + AbstractArea(); + virtual QRect areaGeometry() const; + virtual void positionHasChanged(); + +Q_SIGNALS: + void positionChanged( AbstractArea * ); + + //KDCHART_DECLARE_PRIVATE_DERIVED(AbstractArea) +}; // End of class AbstractArea + +} +#endif // KDCHARTABSTRACTAREA_H diff --git a/libkdchart/src/KDChartAbstractAreaBase.cpp b/libkdchart/src/KDChartAbstractAreaBase.cpp new file mode 100644 index 0000000..eb602fc --- /dev/null +++ b/libkdchart/src/KDChartAbstractAreaBase.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractAreaBase.h" +#include "KDChartAbstractAreaBase_p.h" +#include +#include +#include +#include "KDChartPainterSaver_p.h" +#include "KDChartPrintingParameters.h" +#include + +#include + + +using namespace KDChart; + +AbstractAreaBase::Private::Private() : + visible( true ) + // PENDING(khz) dockingPointToPadding?, alignToDockingPoint? +{ + init(); +} + + +AbstractAreaBase::Private::~Private() {} + + +void AbstractAreaBase::Private::init() +{ +} + + + + +AbstractAreaBase::AbstractAreaBase() : + _d( new Private() ) +{ +} + +AbstractAreaBase::~AbstractAreaBase() +{ + delete _d; _d = 0; +} + + +void AbstractAreaBase::init() +{ +} + + +#define d d_func() + +bool AbstractAreaBase::compare( const AbstractAreaBase* other )const +{ + if( other == this ) return true; + if( ! other ){ + //qDebug() << "CartesianAxis::compare() cannot compare to Null pointer"; + return false; + } + /* + qDebug() << "AbstractAreaBase:" << (frameAttributes() == other->frameAttributes()) + << (backgroundAttributes() == other->backgroundAttributes()) << "\n"; + */ + return (frameAttributes() == other->frameAttributes()) && + (backgroundAttributes() == other->backgroundAttributes()); +} + +void AbstractAreaBase::alignToReferencePoint( const RelativePosition& position ) +{ + Q_UNUSED( position ); + // PENDING(kalle) FIXME + qWarning( "Sorry, not implemented: void AbstractAreaBase::alignToReferencePoint( const RelativePosition& position )" ); +} + +void AbstractAreaBase::setFrameAttributes( const FrameAttributes &a ) +{ + if( d->frameAttributes == a ) + return; + + d->frameAttributes = a; + positionHasChanged(); +} + +FrameAttributes AbstractAreaBase::frameAttributes() const +{ + return d->frameAttributes; +} + +void AbstractAreaBase::setBackgroundAttributes( const BackgroundAttributes &a ) +{ + if( d->backgroundAttributes == a ) + return; + + d->backgroundAttributes = a; + positionHasChanged(); +} + +BackgroundAttributes AbstractAreaBase::backgroundAttributes() const +{ + return d->backgroundAttributes; +} + + +/* static */ +void AbstractAreaBase::paintBackgroundAttributes( QPainter& painter, const QRect& rect, + const KDChart::BackgroundAttributes& attributes ) +{ + if( !attributes.isVisible() ) return; + + /* first draw the brush (may contain a pixmap)*/ + if( Qt::NoBrush != attributes.brush().style() ) { + KDChart::PainterSaver painterSaver( &painter ); + painter.setPen( Qt::NoPen ); + const QPointF newTopLeft( painter.deviceMatrix().map( rect.topLeft() ) ); + painter.setBrushOrigin( newTopLeft ); + painter.setBrush( attributes.brush() ); + painter.drawRect( rect.adjusted( 0, 0, -1, -1 ) ); + } + /* next draw the backPixmap over the brush */ + if( !attributes.pixmap().isNull() && + attributes.pixmapMode() != BackgroundAttributes::BackgroundPixmapModeNone ) { + QPointF ol = rect.topLeft(); + if( BackgroundAttributes::BackgroundPixmapModeCentered == attributes.pixmapMode() ) + { + ol.setX( rect.center().x() - attributes.pixmap().width() / 2 ); + ol.setY( rect.center().y() - attributes.pixmap().height()/ 2 ); + painter.drawPixmap( ol, attributes.pixmap() ); + } else { + QMatrix m; + double zW = (double)rect.width() / (double)attributes.pixmap().width(); + double zH = (double)rect.height() / (double)attributes.pixmap().height(); + switch( attributes.pixmapMode() ) { + case BackgroundAttributes::BackgroundPixmapModeScaled: + { + double z; + z = qMin( zW, zH ); + m.scale( z, z ); + } + break; + case BackgroundAttributes::BackgroundPixmapModeStretched: + m.scale( zW, zH ); + break; + default: + ; // Cannot happen, previously checked + } + QPixmap pm = attributes.pixmap().transformed( m ); + ol.setX( rect.center().x() - pm.width() / 2 ); + ol.setY( rect.center().y() - pm.height()/ 2 ); + painter.drawPixmap( ol, pm ); + } + } +} + +/* static */ +void AbstractAreaBase::paintFrameAttributes( QPainter& painter, const QRect& rect, + const KDChart::FrameAttributes& attributes ) +{ + + if( !attributes.isVisible() ) return; + + // Note: We set the brush to NoBrush explicitly here. + // Otherwise we might get a filled rectangle, so any + // previously drawn background would be overwritten by that area. + + const QPen oldPen( painter.pen() ); + const QBrush oldBrush( painter.brush() ); + painter.setPen( PrintingParameters::scalePen( attributes.pen() ) ); + painter.setBrush( Qt::NoBrush ); + painter.drawRect( rect.adjusted( 0, 0, -1, -1 ) ); + painter.setBrush( oldBrush ); + painter.setPen( oldPen ); +} + +void AbstractAreaBase::paintBackground( QPainter& painter, const QRect& rect ) +{ + Q_ASSERT_X ( d != 0, "AbstractAreaBase::paintBackground()", + "Private class was not initialized!" ); + paintBackgroundAttributes( painter, rect, d->backgroundAttributes ); +} + + +void AbstractAreaBase::paintFrame( QPainter& painter, const QRect& rect ) +{ + Q_ASSERT_X ( d != 0, "AbstractAreaBase::paintFrame()", + "Private class was not initialized!" ); + paintFrameAttributes( painter, rect, d->frameAttributes ); +} + + +void AbstractAreaBase::getFrameLeadings(int& left, int& top, int& right, int& bottom ) const +{ + if( d && d->frameAttributes.isVisible() ){ + const int padding = qMax( d->frameAttributes.padding(), 0 ); + left = padding; + top = padding; + right = padding; + bottom = padding; + }else{ + left = 0; + top = 0; + right = 0; + bottom = 0; + } +} + +QRect AbstractAreaBase::innerRect() const +{ + int left; + int top; + int right; + int bottom; + getFrameLeadings( left, top, right, bottom ); + return + QRect( QPoint(0,0), areaGeometry().size() ) + .adjusted( left, top, -right, -bottom ); +} + +void AbstractAreaBase::positionHasChanged() +{ + // this bloc left empty intentionally +} + diff --git a/libkdchart/src/KDChartAbstractAreaBase.h b/libkdchart/src/KDChartAbstractAreaBase.h new file mode 100644 index 0000000..49a3957 --- /dev/null +++ b/libkdchart/src/KDChartAbstractAreaBase.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTAREABASE_H +#define KDCHARTABSTRACTAREABASE_H + +#include +#include +#include + +#include "KDChartGlobal.h" +#include "KDChartLayoutItems.h" +#include "KDChartRelativePosition.h" +#include "KDChartAbstractAreaBase.h" + + +class QPainter; +class QString; +namespace KDChart { + class TextAttributes; + class BackgroundAttributes; + class FrameAttributes; + class PaintContext; + + +/** + * @class AbstractAreaBase KDChartAbstractAreaBase.h + * @brief Base class for AbstractArea and AbstractAreaWidget: An area + * in the chart with a background, a frame, etc. + * + * AbstractAreaBase is the base class for all chart elements that have + * a set of background attributes and frame attributes, such as + * legends or axes. + * + * @note Normally you should not use AbstractAreaBase directly, but + * derive your classes from AbstractArea or AbstractAreaWidget. + * + * @note This classis not a QObject, so it is easier to inherit from + * it, if your are inheriting from a QObject too like AbstractAreaWidget does it. + * + * @sa AbstractArea, AbstractAreaWidget + */ +class KDCHART_EXPORT AbstractAreaBase +{ + Q_DISABLE_COPY( AbstractAreaBase ) + KDCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC( AbstractAreaBase ) + +protected: + AbstractAreaBase(); + virtual ~AbstractAreaBase() ; + +public: +// virtual AbstractAreaBase * clone() const = 0; + + /** + * Returns true if both areas have the same settings. + */ + bool compare( const AbstractAreaBase* other )const; + + void alignToReferencePoint( const RelativePosition& position ); + + void setFrameAttributes( const FrameAttributes &a ); + FrameAttributes frameAttributes() const; + + void setBackgroundAttributes( const BackgroundAttributes &a ); + BackgroundAttributes backgroundAttributes() const; + + virtual void paintBackground( QPainter& painter, const QRect& rectangle ); + virtual void paintFrame( QPainter& painter, const QRect& rectangle ); + + static void paintBackgroundAttributes( QPainter& painter, const QRect& rectangle, + const KDChart::BackgroundAttributes& attributes ); + static void paintFrameAttributes( QPainter& painter, const QRect& rectangle, + const KDChart::FrameAttributes& attributes ); + + /** \internal + * \note Normally you should not call this method, but derive your classes + * from AbstractArea or AbstractAreaWidget. + * \sa AbstractArea, AbstractAreaWidget + */ + void getFrameLeadings(int& left, int& top, int& right, int& bottom ) const; + + +protected: + /** \internal + * \note Normally you should not call this method, but derive your classes + * from AbstractArea or AbstractAreaWidget. + * \sa AbstractArea, AbstractAreaWidget + */ + QRect innerRect() const; + + /** \internal + * This internal method is used by AbstractArea and AbstractAreaWidget + * to find out the real widget size. + * \sa AbstractArea, AbstractAreaWidget + */ + virtual QRect areaGeometry() const = 0; + + /** \internal + * This internal method can be overwritten by derived classes, + * if they want to emit a signal (or perform other actions, resp.) + * when the Position of the area has been changed. + * The default implementation does nothing. + */ + virtual void positionHasChanged(); + +}; // End of class AbstractAreaBase + +} +#endif // KDCHARTABSTRACTAREABASE_H diff --git a/libkdchart/src/KDChartAbstractAreaBase_p.h b/libkdchart/src/KDChartAbstractAreaBase_p.h new file mode 100644 index 0000000..1caaf51 --- /dev/null +++ b/libkdchart/src/KDChartAbstractAreaBase_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTAREABASE_P_H +#define KDCHARTABSTRACTAREABASE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +/** \file KDChartAbstractAreaBase_p.h + * \internal + */ + +#include "KDChartAbstractAreaBase.h" +#include "KDChartTextAttributes.h" +#include "KDChartFrameAttributes.h" +#include "KDChartBackgroundAttributes.h" + +#include + + +namespace KDChart { + +/** + * \internal + */ + class AbstractAreaBase::Private + { + friend class AbstractAreaBase; + public: + explicit Private(); + virtual ~Private(); + + Private( const Private& rhs ) : + visible( rhs.visible ), + frameAttributes( rhs.frameAttributes ), + backgroundAttributes( rhs.backgroundAttributes ) + { + } + + protected: + void init(); + + // These are set each time the area's sizeHint() + // (or the maximumSize(), resp.) is calculated: + // They store additional layout-information about + // space needed around the area. + // Other classes (e.g. KDChart::AutoSpacer) can use + // these data to determine how much space has to + // be added additionally ... + mutable int amountOfLeftOverlap; + mutable int amountOfRightOverlap; + mutable int amountOfTopOverlap; + mutable int amountOfBottomOverlap; + + private: + bool visible; + KDChart::FrameAttributes frameAttributes; + KDChart::BackgroundAttributes backgroundAttributes; + }; + + inline AbstractAreaBase::AbstractAreaBase( AbstractAreaBase::Private * p ) : + _d( p ) { init(); } + +} +#endif /* KDCHARTABSTRACTAREABASE_P_H */ + diff --git a/libkdchart/src/KDChartAbstractAreaWidget.cpp b/libkdchart/src/KDChartAbstractAreaWidget.cpp new file mode 100644 index 0000000..537e30c --- /dev/null +++ b/libkdchart/src/KDChartAbstractAreaWidget.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractAreaWidget.h" +#include "KDChartAbstractAreaWidget_p.h" + +#include + + +using namespace KDChart; + + +AbstractAreaWidget::Private::Private() +{ + // this block left empty intentionally +} + +AbstractAreaWidget::Private::~Private() +{ + // this block left empty intentionally +} + + +void AbstractAreaWidget::Private::resizeLayout( + AbstractAreaWidget* widget, const QSize& size ) +{ + if( size == currentLayoutSize ) return; + + currentLayoutSize = size; + + // Now we call adjust the size, for the inner parts of the widget. + int left; + int top; + int right; + int bottom; + widget->getFrameLeadings( left, top, right, bottom ); + const QSize innerSize( size.width() - left - right, + size.height() - top - bottom ); + // With this adjusted size we call the real resizeLayout method, + // which normally will call resizeLayout( size ) in the derived class + // - which in turn is the place to resize the layout member variable + // of that class. + widget->resizeLayout( innerSize ); +} + + +AbstractAreaWidget::AbstractAreaWidget( QWidget* parent ) + : QWidget( parent ) + , AbstractAreaBase( new Private() ) +{ + init(); +} + +AbstractAreaWidget::~AbstractAreaWidget() +{ + // this block left empty intentionally +} + +void AbstractAreaWidget::init() +{ + // this block left empty intentionally +} + +void AbstractAreaWidget::needSizeHint() +{ + // this block left empty intentionally +} + +#define d d_func() + +void AbstractAreaWidget::resizeLayout( const QSize& size ) +{ + Q_UNUSED( size ); + // this block left empty intentionally +} + +void AbstractAreaWidget::paintEvent( QPaintEvent* event ) +{ + Q_UNUSED( event ); + QPainter painter( this ); + if( size() != d->currentLayoutSize ){ + d->resizeLayout( this, size() ); + } + paintAll( painter ); +} + +void AbstractAreaWidget::paintIntoRect( QPainter& painter, const QRect& rect ) +{ + //qDebug() << "AbstractAreaWidget::paintIntoRect() called rect=" << rect; + + if( rect.isEmpty() ) return; + + d->resizeLayout( this, rect.size() ); + + const QPoint translation( rect.topLeft() ); + painter.translate( translation ); + paintAll( painter ); + painter.translate( -translation.x(), -translation.y() ); + +/* + // make sure, the contents of the widget have been set up, + // so we get a useful geometry: + needSizeHint(); + + const QRect oldGeometry( layout()->geometry() ); + const QRect newGeo( QPoint(0,0), rect.size() ); + const bool mustChangeGeo = layout() && oldGeometry != newGeo; + if( mustChangeGeo ) + layout()->setGeometry( newGeo ); + painter.translate( rect.left(), rect.top() ); + paintAll( painter ); + painter.translate( -rect.left(), -rect.top() ); + if( mustChangeGeo ) + layout()->setGeometry( oldGeometry ); +*/ +} + +void AbstractAreaWidget::forceRebuild() +{ + //bloc left empty intentionally +} + +void AbstractAreaWidget::paintAll( QPainter& painter ) +{ + //qDebug() << "AbstractAreaWidget::paintAll() called"; + + // Paint the background and frame + paintBackground( painter, QRect(QPoint(0, 0), size() ) ); + paintFrame( painter, QRect(QPoint(0, 0), size() ) ); + +/* + we do not call setContentsMargins() now, + but we call resizeLayout() whenever the size or the frame has changed + + // adjust the widget's content margins, + // to be sure all content gets calculated + // to fit into the inner rectangle + const QRect oldGeometry( areaGeometry() ); + const QRect inner( innerRect() ); + //qDebug() << "areaGeometry():" << oldGeometry + // << " contentsRect():" << contentsRect() << " inner:" << inner; + if( contentsRect() != inner ){ + //qDebug() << "old contentsRect():" << contentsRect() << " new innerRect:" << inner; + setContentsMargins( + inner.left(), + inner.top(), + oldGeometry.width() -inner.width()-1, + oldGeometry.height()-inner.height()-1 ); + //forceRebuild(); + } +*/ + int left; + int top; + int right; + int bottom; + getFrameLeadings( left, top, right, bottom ); + const QPoint translation( left, top ); + painter.translate( translation ); + paint( &painter ); + painter.translate( -translation.x(), -translation.y() ); + //qDebug() << "AbstractAreaWidget::paintAll() done."; +} + +QRect AbstractAreaWidget::areaGeometry() const +{ + return geometry(); +} + +void AbstractAreaWidget::positionHasChanged() +{ + emit positionChanged( this ); +} +/* +void AbstractAreaWidget::setGeometry( const QRect & rect ) +{ + qDebug() << "AbstractAreaWidget::setGeometry("<< rect << ") called"; + const bool bChanged = rect != geometry(); + QWidget::setGeometry( rect ); + if( bChanged ) + forceRebuild(); +} +*/ diff --git a/libkdchart/src/KDChartAbstractAreaWidget.h b/libkdchart/src/KDChartAbstractAreaWidget.h new file mode 100644 index 0000000..554b786 --- /dev/null +++ b/libkdchart/src/KDChartAbstractAreaWidget.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTAREAWIDGET_H +#define KDCHARTABSTRACTAREAWIDGET_H + +#include +#include +#include +#include + +#include "KDChartAbstractAreaBase.h" + +namespace KDChart { + + +/** + * @class AbstractAreaWidget KDChartAbstractArea.h + * @brief An area in the chart with a background, a frame, etc. + * + * AbstractAreaWidget is the base for all widget classes that have + * a set of background attributes and frame attributes, such as + * KDChart::Chart and KDChart::Legend. + */ +class KDCHART_EXPORT AbstractAreaWidget : public QWidget, public AbstractAreaBase +{ + Q_OBJECT + + Q_DISABLE_COPY( AbstractAreaWidget ) + KDCHART_DECLARE_PRIVATE_DERIVED_QWIDGET( AbstractAreaWidget ) + +public: + explicit AbstractAreaWidget( QWidget* parent = 0 ); + + /** + * @brief Draws the background and frame, then calls paint(). + * + * In most cases there is no need to overwrite this method in a derived + * class, but you would overwrite paint() instead. + * @sa paint + */ + virtual void paintEvent( QPaintEvent* event ); + + /** + * @brief Draws the background and frame, then calls paint(). + * + * In most cases there is no need to overwrite this method in a derived + * class, but you would overwrite paint() instead. + */ + virtual void paintIntoRect( QPainter& painter, const QRect& rect ); + + /** + * Overwrite this to paint the inner contents of your widget. + * + * @note When overriding this method, please let your widget draw + * itself at the top/left corner of the painter. You should call rect() + * (or width(), height(), resp.) to find the drawable area's size: + * While the paint() method is being executed the frame of the widget + * is outside of its rect(), so you can use all of rect() for + * your custom drawing! + * @sa paint, paintIntoRect + */ + virtual void paint( QPainter* painter ) = 0; + + /** + * Call paintAll, if you want the background and the frame to be drawn + * before the normal paint() is invoked automatically. + */ + void paintAll( QPainter& painter ); + + /** + * Call this to trigger an unconditional re-building of the widget's internals. + */ + virtual void forceRebuild(); + + /** + * Call this to trigger an conditional re-building of the widget's internals. + * + * e.g. AbstractAreaWidget call this, before calling layout()->setGeometry() + */ + virtual void needSizeHint(); + //virtual void setGeometry( const QRect & rect ); + virtual void resizeLayout( const QSize& ); + +protected: + virtual ~AbstractAreaWidget() ; + virtual QRect areaGeometry() const; + virtual void positionHasChanged(); + + +public: +// virtual AbstractAreaWidget * clone() const = 0; + +Q_SIGNALS: + void positionChanged( AbstractAreaWidget * ); + +}; // End of class AbstractAreaWidget + +} +#endif // KDCHARTABSTRACTAREAWIDGET_H diff --git a/libkdchart/src/KDChartAbstractAreaWidget_p.h b/libkdchart/src/KDChartAbstractAreaWidget_p.h new file mode 100644 index 0000000..a0a0ce5 --- /dev/null +++ b/libkdchart/src/KDChartAbstractAreaWidget_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTAREAWIDGET_P_H +#define KDCHARTABSTRACTAREAWIDGET_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +/** \file KDChartAbstractAreaWidget_p.h + * \internal + */ + +#include "KDChartAbstractAreaWidget.h" +#include "KDChartAbstractAreaBase_p.h" + +#include + + +namespace KDChart { + +/** + * \internal + */ +class AbstractAreaWidget::Private : public AbstractAreaBase::Private +{ + friend class AbstractAreaWidget; +public: + explicit Private(); + virtual ~Private(); + + Private( const Private& rhs ) : + AbstractAreaBase::Private( rhs ) + { + // Just for consistency + } + + QSize currentLayoutSize; + + // non-virtual method, calling widget->resizeLayout( size ) + void resizeLayout( AbstractAreaWidget* widget, const QSize& sz ); +}; + + +inline AbstractAreaWidget::AbstractAreaWidget( AbstractAreaWidget::Private * p, QWidget* parent ) + : QWidget( parent ), AbstractAreaBase( p ) +{ + init(); +} +inline AbstractAreaWidget::Private * AbstractAreaWidget::d_func() +{ + return static_cast( AbstractAreaBase::d_func() ); +} +inline const AbstractAreaWidget::Private * AbstractAreaWidget::d_func() const +{ + return static_cast( AbstractAreaBase::d_func() ); +} + + +} + +#endif /* KDCHARTABSTRACTAREAWIDGET_P_H */ + diff --git a/libkdchart/src/KDChartAbstractArea_p.h b/libkdchart/src/KDChartAbstractArea_p.h new file mode 100644 index 0000000..9504e09 --- /dev/null +++ b/libkdchart/src/KDChartAbstractArea_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTAREA_P_H +#define KDCHARTABSTRACTAREA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +/** \file KDChartAbstractArea_p.h + * \internal + */ + +#include "KDChartAbstractArea.h" +#include "KDChartAbstractAreaBase_p.h" + +#include + + +namespace KDChart { + +/** + * \internal + */ +class AbstractArea::Private : public AbstractAreaBase::Private +{ + friend class AbstractArea; +public: + explicit Private(); + virtual ~Private(); +}; + + +inline AbstractArea::AbstractArea( Private * p ) + : QObject(), AbstractAreaBase( p ), AbstractLayoutItem() +{ + init(); +} +inline AbstractArea::Private * AbstractArea::d_func() +{ + return static_cast( AbstractAreaBase::d_func() ); +} +inline const AbstractArea::Private * AbstractArea::d_func() const +{ + return static_cast( AbstractAreaBase::d_func() ); +} + +} + +#endif /* KDCHARTABSTRACTAREA_P_H */ + diff --git a/libkdchart/src/KDChartAbstractAxis.cpp b/libkdchart/src/KDChartAbstractAxis.cpp new file mode 100644 index 0000000..857fb31 --- /dev/null +++ b/libkdchart/src/KDChartAbstractAxis.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractAxis.h" +#include "KDChartAbstractAxis_p.h" +#include "KDChartAbstractDiagram.h" +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartEnums.h" +#include "KDChartMeasure.h" + +#include + +#include + +using namespace KDChart; + +#define d d_func() + +AbstractAxis::Private::Private( AbstractDiagram* diagram, AbstractAxis* axis ) + : observer( 0 ) + , mDiagram( diagram ) + , mAxis( axis ) +{ + // Note: We do NOT call setDiagram( diagram, axis ); + // but it is called in AbstractAxis::delayedInit() instead! +} + +AbstractAxis::Private::~Private() +{ + delete observer; + observer = 0; +} + +bool AbstractAxis::Private::setDiagram( + AbstractDiagram* diagram_, + bool delayedInit ) +{ + AbstractDiagram* diagram = delayedInit ? mDiagram : diagram_; + if( delayedInit ){ + mDiagram = 0; + } + + // do not set a diagram again that was already set + if ( diagram && + ((diagram == mDiagram) || secondaryDiagrams.contains( diagram )) ) + return false; + + bool bNewDiagramStored = false; + if ( ! mDiagram ) { + mDiagram = diagram; + delete observer; + if ( mDiagram ) { +//qDebug() << "axis" << (axis != 0); + observer = new DiagramObserver( mDiagram, mAxis ); + bNewDiagramStored = true; + }else{ + observer = 0; + } + } else { + if ( diagram ) + secondaryDiagrams.enqueue( diagram ); + } + return bNewDiagramStored; +} + +void AbstractAxis::Private::unsetDiagram( AbstractDiagram* diagram ) +{ + if ( diagram == mDiagram ) { + mDiagram = 0; + delete observer; + observer = 0; + } else { + secondaryDiagrams.removeAll( diagram ); + } + if( !secondaryDiagrams.isEmpty() ) { + AbstractDiagram *nextDiagram = secondaryDiagrams.dequeue(); + setDiagram( nextDiagram ); + } +} + +bool AbstractAxis::Private::hasDiagram( AbstractDiagram* diagram ) const +{ + return diagram == mDiagram || secondaryDiagrams.contains( diagram ); +} + +AbstractAxis::AbstractAxis ( AbstractDiagram* diagram ) + : AbstractArea( new Private( diagram, this ) ) +{ + init(); + QTimer::singleShot(0, this, SLOT(delayedInit())); +} + +AbstractAxis::~AbstractAxis() +{ + d->mDiagram = 0; + d->secondaryDiagrams.clear(); +} + + +void AbstractAxis::init() +{ + Measure m( + 12.5, + KDChartEnums::MeasureCalculationModeAuto, + KDChartEnums::MeasureOrientationAuto ); + d->textAttributes.setFontSize( m ); + m.setValue( 5 ); + m.setCalculationMode( KDChartEnums::MeasureCalculationModeAbsolute ); + d->textAttributes.setMinimalFontSize( m ); +} + +void AbstractAxis::delayedInit() +{ + // We call setDiagram() here, because the c'tor of Private + // only has stored the pointers, but it did not call setDiagram(). + if( d ) + d->setDiagram( 0, true /* delayedInit */ ); +} + +bool AbstractAxis::compare( const AbstractAxis* other )const +{ + if( other == this ) return true; + if( ! other ){ + //qDebug() << "CartesianAxis::compare() cannot compare to Null pointer"; + return false; + } + /* + qDebug() << (textAttributes() == other->textAttributes()); + qDebug() << (labels() == other->labels()); + qDebug() << (shortLabels() == other->shortLabels()); + */ + return ( static_cast(this)->compare( other ) ) && + (textAttributes() == other->textAttributes()) && + (labels() == other->labels()) && + (shortLabels() == other->shortLabels()); +} + + +const QString AbstractAxis::customizedLabel( const QString& label )const +{ + return label; +} + + +void AbstractAxis::createObserver( AbstractDiagram* diagram ) +{ + if( d->setDiagram( diagram ) ) + connectSignals(); +} + +void AbstractAxis::deleteObserver( AbstractDiagram* diagram ) +{ + d->unsetDiagram( diagram ); +} + +void AbstractAxis::connectSignals() +{ + if( d->observer ){ + connect( d->observer, SIGNAL( diagramDataChanged( AbstractDiagram *) ), + this, SLOT( update() ) ); + } +} + + +void AbstractAxis::setTextAttributes( const TextAttributes &a ) +{ + if( d->textAttributes == a ) + return; + + d->textAttributes = a; + update(); +} + +TextAttributes AbstractAxis::textAttributes() const +{ + return d->textAttributes; +} + + +void AbstractAxis::setRulerAttributes( const RulerAttributes &a ) +{ + d->rulerAttributes = a; + update(); +} + +RulerAttributes AbstractAxis::rulerAttributes() const +{ + return d->rulerAttributes; +} + +void AbstractAxis::setLabels( const QStringList& list ) +{ + if( d->hardLabels == list ) + return; + + d->hardLabels = list; + update(); +} + +QStringList AbstractAxis::labels() const +{ + return d->hardLabels; +} + +void AbstractAxis::setShortLabels( const QStringList& list ) +{ + if( d->hardShortLabels == list ) + return; + + d->hardShortLabels = list; + update(); +} + +QStringList AbstractAxis::shortLabels() const +{ + return d->hardShortLabels; +} + +const AbstractCoordinatePlane* AbstractAxis::coordinatePlane() const +{ + if( d->diagram() ) + return d->diagram()->coordinatePlane(); + return 0; +} + +const AbstractDiagram * KDChart::AbstractAxis::diagram() const +{ + return d->diagram(); +} + +bool KDChart::AbstractAxis::observedBy( AbstractDiagram * diagram ) const +{ + return d->hasDiagram( diagram ); +} + +void KDChart::AbstractAxis::update() +{ + if( d->diagram() ) + d->diagram()->update(); +} diff --git a/libkdchart/src/KDChartAbstractAxis.h b/libkdchart/src/KDChartAbstractAxis.h new file mode 100644 index 0000000..44cb677 --- /dev/null +++ b/libkdchart/src/KDChartAbstractAxis.h @@ -0,0 +1,245 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTAXIS_H +#define KDCHARTABSTRACTAXIS_H + +// #include +// #include +// #include + +#include "kdchart_export.h" +#include "KDChartGlobal.h" +#include "KDChartAbstractArea.h" +#include "KDChartTextAttributes.h" +#include "KDChartRulerAttributes.h" + + +class QPainter; +class QSizeF; +// class QRectF; + + +namespace KDChart { + + class Area; + class AbstractCoordinatePlane; + class PaintContext; + class AbstractDiagram; + + /** + * The base class for axes. + * + * For being useful, axes need to be assigned to a diagram, see + * AbstractCartesianDiagram::addAxis and AbstractCartesianDiagram::takeAxis. + * + * \sa PolarAxis, AbstractCartesianDiagram + */ + class KDCHART_EXPORT AbstractAxis : public AbstractArea + { + Q_OBJECT + + Q_DISABLE_COPY( AbstractAxis ) + KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( AbstractAxis, AbstractDiagram* ) + + public: + explicit AbstractAxis( AbstractDiagram* diagram = 0 ); + virtual ~AbstractAxis(); + + // FIXME implement when code os ready for it: + // virtual Area* clone() const = 0; + + // FIXME (Mirko) readd when needed + // void copyRelevantDetailsFrom( const KDChartAxis* axis ); + + /* virtual void paint( PaintContext* ) const = 0; + virtual QSize sizeHint() const = 0;*/ + //virtual void paintEvent( QPaintEvent* event) = 0; + + /** + * \brief Implement this method if you want to adjust axis labels + * before they are printed. + * + * KD Chart is calling this method immediately before drawing the + * text, this means: What you return here will be drawn without + * further modifications. + * + * \param label The text of the label as KD Chart has calculated it + * automatically (or as it was taken from a QStringList provided + * by you, resp.) + * + * \return The text to be drawn. By default this is the same as \c label. + */ + virtual const QString customizedLabel( const QString& label )const; + + /** + * Returns true if both axes have the same settings. + */ + bool compare( const AbstractAxis* other )const; + + /** + * \internal + * + * Method invoked by AbstractCartesianDiagram::addAxis(). + * + * You should not call this function, unless you know exactly, + * what you are doing. + * + * \sa connectSignals(), AbstractCartesianDiagram::addAxis() + */ + void createObserver( AbstractDiagram* diagram ); + + /** + * \internal + * + * Method invoked by AbstractCartesianDiagram::takeAxis(). + * + * You should not call this function, unless you know exactly, + * what you are doing. + * + * \sa AbstractCartesianDiagram::takeAxis() + */ + void deleteObserver( AbstractDiagram* diagram ); + const AbstractDiagram* diagram() const; + bool observedBy( AbstractDiagram* diagram ) const; + + /** + * Wireing the signal/slot connections. + * + * This method gets called automatically, each time, when you assign + * the axis to a diagram, either by passing a diagram* to the c'tor, + * or by calling the diagram's setAxis method, resp. + * + * If overwriting this method in derived classes, make sure to call + * this base method AbstractAxis::connectSignals(), so your axis + * gets connected to the diagram's built-in signals. + * + * \sa AbstractCartesianDiagram::addAxis() + */ + virtual void connectSignals(); + + /** + \brief Use this to specify the text attributes to be used for axis labels. + + By default, the reference area will be set at painting time. + It will be the then-valid coordinate plane's parent widget, + so normally, it will be the KDChart::Chart. + Thus the labels of all of your axes in all of your diagrams + within that Chart will be drawn in same font size, by default. + + \sa textAttributes, setLabels + */ + void setTextAttributes( const TextAttributes &a ); + + /** + \brief Returns the text attributes to be used for axis labels. + + \sa setTextAttributes + */ + TextAttributes textAttributes() const; + + /** + \brief Use this to specify the attributes used to paint the axis ruler + + Every axis has a default set of ruler attributes that is exactly the + same among them. Use this method to specify your own attributes. + + \sa rulerAttributes + */ + void setRulerAttributes( const RulerAttributes &a ); + + /** + \brief Returns the attributes to be used for painting the rulers + + \sa setRulerAttributes + */ + RulerAttributes rulerAttributes() const; + + /** + \brief Use this to specify your own set of strings, to be used as axis labels. + + Labels specified via setLabels take precedence: + If a non-empty list is passed, KD Chart will use these strings as axis labels, + instead of calculating them. + + If you a smaller number of strings than the number of labels drawn at this + axis, KD Chart will iterate over the list, repeating the strings, until all + labels are drawn. + As an example you could specify the seven days of the week as abscissa labels, + which would be repeatedly used then. + + By passing an empty QStringList you can reset the default behaviour. + + \sa labels, setShortLabels + */ + void setLabels( const QStringList& list ); + + /** + Returns a list of strings, that are used as axis labels, as set via setLabels. + + \sa setLabels + */ + QStringList labels() const; + + /** + \brief Use this to specify your own set of strings, to be used as axis labels, + in case the normal labels are too long. + + \note Setting done via setShortLabels will be ignored, if you did not pass + a non-empty string list via setLabels too! + + By passing an empty QStringList you can reset the default behaviour. + + \sa shortLabels, setLabels + */ + void setShortLabels( const QStringList& list ); + + /** + Returns a list of strings, that are used as axis labels, as set via setShortLabels. + + \note Setting done via setShortLabels will be ignored, if you did not pass + a non-empty string list via setLabels too! + + \sa setShortLabels + */ + QStringList shortLabels() const; + + virtual void setGeometry( const QRect& rect ) = 0; + virtual QRect geometry() const = 0; + + /** + \brief Convenience function, returns the coordinate plane, in which this axis is used. + + If the axis is not used in a coordinate plane, the return value is Zero. + */ + const AbstractCoordinatePlane* coordinatePlane() const; + + protected Q_SLOTS: + /** called for initializing after the c'tor has completed */ + virtual void delayedInit(); + + public Q_SLOTS: + void update(); + }; +} + +#endif // KDCHARTABSTRACTAXIS_H diff --git a/libkdchart/src/KDChartAbstractAxis_p.h b/libkdchart/src/KDChartAbstractAxis_p.h new file mode 100644 index 0000000..ee00b5e --- /dev/null +++ b/libkdchart/src/KDChartAbstractAxis_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTAXIS_P_H +#define KDCHARTAXIS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include "KDChartAbstractArea_p.h" +#include "KDChartAbstractDiagram.h" +#include +#include +#include + +#include + + +namespace KDChart { + +/** + * \internal + */ +class AbstractAxis::Private : public AbstractArea::Private +{ + friend class AbstractAxis; + +public: + Private( AbstractDiagram* diagram, AbstractAxis* axis ); + ~Private(); + + bool setDiagram( AbstractDiagram* diagram, bool delayedInit = false ); + void unsetDiagram( AbstractDiagram* diagram ); + const AbstractDiagram* diagram() const + { + return mDiagram; + } + bool hasDiagram( AbstractDiagram* diagram ) const; + + DiagramObserver* observer; + + TextAttributes textAttributes; + RulerAttributes rulerAttributes; + QStringList hardLabels; + QStringList hardShortLabels; + QQueue secondaryDiagrams; + +protected: + AbstractDiagram* mDiagram; + AbstractAxis* mAxis; +}; + + +inline AbstractAxis::AbstractAxis( Private * p, AbstractDiagram* diagram ) + : AbstractArea( p ) +{ + Q_UNUSED( diagram ); + init(); + QTimer::singleShot(0, this, SLOT(delayedInit())); +} + +inline AbstractAxis::Private * AbstractAxis::d_func() +{ return static_cast( AbstractArea::d_func() ); } +inline const AbstractAxis::Private * AbstractAxis::d_func() const +{ return static_cast( AbstractArea::d_func() ); } + +} +#endif /* KDCHARTAREA_P_H */ + diff --git a/libkdchart/src/KDChartAbstractCartesianDiagram.cpp b/libkdchart/src/KDChartAbstractCartesianDiagram.cpp new file mode 100644 index 0000000..7f2ef90 --- /dev/null +++ b/libkdchart/src/KDChartAbstractCartesianDiagram.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartAbstractCartesianDiagram_p.h" +#include "KDChartPaintContext.h" +#include +#include + +#include + + +using namespace KDChart; + +AbstractCartesianDiagram::Private::Private() + : referenceDiagram( 0 ) +{ + qRegisterMetaType< QModelIndex >( "QModelIndex" ); +} + +AbstractCartesianDiagram::Private::~Private() +{ +} + +bool AbstractCartesianDiagram::compare( const AbstractCartesianDiagram* other )const +{ + if( other == this ) return true; + if( ! other ){ + //qDebug() << "AbstractCartesianDiagram::compare() cannot compare to Null pointer"; + return false; + } + /* + qDebug() << "\n AbstractCartesianDiagram::compare():"; + // compare own properties + qDebug() << + ((referenceDiagram() == other->referenceDiagram()) && + ((! referenceDiagram()) || (referenceDiagramOffset() == other->referenceDiagramOffset()))); + */ + return // compare the base class + ( static_cast(this)->compare( other ) ) && + // compare own properties + (referenceDiagram() == other->referenceDiagram()) && + ((! referenceDiagram()) || (referenceDiagramOffset() == other->referenceDiagramOffset())); +} + + +#define d d_func() + +AbstractCartesianDiagram::AbstractCartesianDiagram ( QWidget* parent, CartesianCoordinatePlane* plane ) + : AbstractDiagram ( new Private(), parent, plane ) +{ + init(); +} + +KDChart::AbstractCartesianDiagram::~AbstractCartesianDiagram() +{ + Q_FOREACH( CartesianAxis* axis, d->axesList ) { + axis->deleteObserver( this ); + } + d->axesList.clear(); +} + +void AbstractCartesianDiagram::init() +{ + d->compressor.setModel( attributesModel() ); + connect( this, SIGNAL( layoutChanged( AbstractDiagram* ) ), + &( d->compressor ), SLOT( slotDiagramLayoutChanged( AbstractDiagram* ) ) ); +} + +void AbstractCartesianDiagram::addAxis( CartesianAxis *axis ) +{ + if ( !d->axesList.contains( axis ) ) { + d->axesList.append( axis ); + axis->createObserver( this ); + layoutPlanes(); + } +} + +void AbstractCartesianDiagram::takeAxis( CartesianAxis *axis ) +{ + const int idx = d->axesList.indexOf( axis ); + if( idx != -1 ) + d->axesList.takeAt( idx ); + axis->deleteObserver( this ); + axis->setParentWidget( 0 ); + layoutPlanes(); +} + +KDChart::CartesianAxisList AbstractCartesianDiagram::axes( ) const +{ + return d->axesList; +} + +void KDChart::AbstractCartesianDiagram::layoutPlanes() +{ + //qDebug() << "KDChart::AbstractCartesianDiagram::layoutPlanes()"; + AbstractCoordinatePlane* plane = coordinatePlane(); + if( plane ){ + plane->layoutPlanes(); + //qDebug() << "KDChart::AbstractCartesianDiagram::layoutPlanes() OK"; + } +} + +void KDChart::AbstractCartesianDiagram::setCoordinatePlane( AbstractCoordinatePlane* plane ) +{ + if( coordinatePlane() ) { + disconnect( attributesModel(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), + coordinatePlane(), SLOT( relayout() ) ); + disconnect( attributesModel(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), + coordinatePlane(), SLOT( relayout() ) ); + disconnect( attributesModel(), SIGNAL( columnsRemoved( const QModelIndex&, int, int ) ), + coordinatePlane(), SLOT( relayout() ) ); + disconnect( attributesModel(), SIGNAL( columnsInserted( const QModelIndex&, int, int ) ), + coordinatePlane(), SLOT( relayout() ) ); + disconnect( coordinatePlane() ); + } + + AbstractDiagram::setCoordinatePlane(plane); + if ( plane ) { + // Readjust the layout when the dataset count changes + connect( attributesModel(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), + plane, SLOT( relayout() ), Qt::QueuedConnection ); + connect( attributesModel(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), + plane, SLOT( relayout() ), Qt::QueuedConnection ); + connect( attributesModel(), SIGNAL( columnsRemoved( const QModelIndex&, int, int ) ), + plane, SLOT( relayout() ), Qt::QueuedConnection ); + connect( attributesModel(), SIGNAL( columnsInserted( const QModelIndex&, int, int ) ), + plane, SLOT( relayout() ), Qt::QueuedConnection ); + } + // show the axes, after all have been layoutPlanes + // (because they might depend on each other) + /* + if( plane ) + Q_FOREACH( CartesianAxis* axis, d->axesList ) + axis->show(); + else + Q_FOREACH( CartesianAxis* axis, d->axesList ) + axis->hide(); + */ +} + +void AbstractCartesianDiagram::setReferenceDiagram( AbstractCartesianDiagram* diagram, const QPointF& offset ) +{ + d->referenceDiagram = diagram; + d->referenceDiagramOffset = offset; +} + +AbstractCartesianDiagram* AbstractCartesianDiagram::referenceDiagram() const +{ + return d->referenceDiagram; +} + +QPointF AbstractCartesianDiagram::referenceDiagramOffset() const +{ + return d->referenceDiagramOffset; +} + +void AbstractCartesianDiagram::setRootIndex( const QModelIndex& index ) +{ + AbstractDiagram::setRootIndex( index ); + d->compressor.setRootIndex( attributesModel()->mapFromSource( index ) ); +} + +void AbstractCartesianDiagram::setModel( QAbstractItemModel* model ) +{ + AbstractDiagram::setModel( model ); + d->compressor.setModel( attributesModel() ); +} + +void AbstractCartesianDiagram::setAttributesModel( AttributesModel* model ) +{ + AbstractDiagram::setAttributesModel( model ); + d->compressor.setModel( attributesModel() ); +} diff --git a/libkdchart/src/KDChartAbstractCartesianDiagram.h b/libkdchart/src/KDChartAbstractCartesianDiagram.h new file mode 100644 index 0000000..36983ba --- /dev/null +++ b/libkdchart/src/KDChartAbstractCartesianDiagram.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTCARTESIANDIAGRAM_H +#define KDCHARTABSTRACTCARTESIANDIAGRAM_H + +#include "KDChartCartesianCoordinatePlane.h" +#include "KDChartAbstractDiagram.h" +#include "KDChartCartesianAxis.h" + +namespace KDChart { + + class GridAttributes; +// class PaintContext; + + /** + * @brief Base class for diagrams based on a cartesian coordianate system. + * + * The AbstractCartesianDiagram interface adds those elements that are + * specific to diagrams based on a cartesian coordinate system to the + * basic AbstractDiagram interface. + */ + class KDCHART_EXPORT AbstractCartesianDiagram : public AbstractDiagram + { + Q_OBJECT + Q_DISABLE_COPY( AbstractCartesianDiagram ) +// KDCHART_DECLARE_PRIVATE_DERIVED( AbstractCartesianDiagram ) + KDCHART_DECLARE_DERIVED_DIAGRAM( AbstractCartesianDiagram, CartesianCoordinatePlane ) + + public: + explicit AbstractCartesianDiagram ( QWidget* parent = 0, CartesianCoordinatePlane* plane = 0 ); + virtual ~AbstractCartesianDiagram(); + + /** + * Returns true if both diagrams have the same settings. + */ + bool compare( const AbstractCartesianDiagram* other )const; + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + virtual const int numberOfAbscissaSegments () const = 0; + virtual const int numberOfOrdinateSegments () const = 0; +#else + virtual int numberOfAbscissaSegments () const = 0; + virtual int numberOfOrdinateSegments () const = 0; +#endif + /** + * Add the axis to the diagram. The diagram takes ownership of the axis + * and will delete it. + * + * To gain back ownership (e.g. for assigning the axis to another diagram) + * use the takeAxis method, before calling addAxis on the other diagram. + * + * \sa takeAxis + */ + virtual void addAxis( CartesianAxis * axis ); + /** + * Removes the axis from the diagram, without deleting it. + * + * The diagram no longer owns the axis, so it is + * the caller's responsibility to delete the axis. + * + * \sa addAxis + */ + virtual void takeAxis( CartesianAxis * axis ); + /** + * @return a list of all axes added to the diagram + */ + virtual KDChart::CartesianAxisList axes () const; + + /** + * Triggers layouting of all coordinate planes on the current chart. + * Normally you don't need to call this method. It's handled automatically for you. + */ + virtual void layoutPlanes(); + /** \reimpl */ + virtual void setCoordinatePlane( AbstractCoordinatePlane* plane ); + + /** + * Makes this diagram use another diagram \a diagram as reference diagram with relative offset + * \a offset. + * To share cartesian axes between different diagrams there might be cases when you need that. + * Normally you don't. + * \sa examples/SharedAbscissa + */ + virtual void setReferenceDiagram( AbstractCartesianDiagram* diagram, const QPointF& offset = QPointF() ); + /** + * @return this diagram's reference diagram + * \sa setReferenceDiagram + */ + virtual AbstractCartesianDiagram* referenceDiagram() const; + /** + * @return the relative offset of this diagram's reference diagram + * \sa setReferenceDiagram + */ + virtual QPointF referenceDiagramOffset() const; + + /* reimpl */ + void setModel( QAbstractItemModel* model ); + /* reimpl */ + void setRootIndex( const QModelIndex& index ); + /* reimpl */ + void setAttributesModel( AttributesModel* model ); + + protected: + /** @return the 3D item depth of the model index \a index */ + virtual double threeDItemDepth( const QModelIndex& index ) const = 0; + /** @return the 3D item depth of the data set \a column */ + virtual double threeDItemDepth( int column ) const = 0; + }; + +} + +#endif diff --git a/libkdchart/src/KDChartAbstractCartesianDiagram_p.h b/libkdchart/src/KDChartAbstractCartesianDiagram_p.h new file mode 100644 index 0000000..526ff9c --- /dev/null +++ b/libkdchart/src/KDChartAbstractCartesianDiagram_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTCARTESIANDIAGRAM_P_H +#define KDCHARTABSTRACTCARTESIANDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractCartesianDiagram.h" + +#include +#include +#include + +#include + + +namespace KDChart { + + class CartesianCoordinatePlane; + class AbstractCartesianDiagram; + +/** + * \internal + */ +class AbstractCartesianDiagram::Private : public AbstractDiagram::Private +{ + friend class AbstractCartesianDiagram; +public: + Private(); + virtual ~Private(); + + Private( const Private& rhs ) : + AbstractDiagram::Private( rhs ), + // Do not copy axes and reference diagrams. + axesList(), + referenceDiagram( 0 ) + { + } + + /** \reimpl */ + virtual CartesianDiagramDataCompressor::DataValueAttributesList aggregatedAttrs( + AbstractDiagram * diagram, + const QModelIndex & index, + const CartesianDiagramDataCompressor::CachePosition * position ) const + { + if( position ) + return compressor.aggregatedAttrs( diagram, index, *position ); + CartesianDiagramDataCompressor::DataValueAttributesList allAttrs; + allAttrs[index] = diagram->dataValueAttributes( index ); + return allAttrs; + } + + CartesianAxisList axesList; + + AbstractCartesianDiagram* referenceDiagram; + QPointF referenceDiagramOffset; + + mutable CartesianDiagramDataCompressor compressor; +}; + +KDCHART_IMPL_DERIVED_DIAGRAM( AbstractCartesianDiagram, AbstractDiagram, CartesianCoordinatePlane ) +/* +inline AbstractCartesianDiagram::AbstractCartesianDiagram( Private * p ) + : AbstractDiagram( p ) { init(); } +inline AbstractCartesianDiagram::AbstractCartesianDiagram( + Private * p, QWidget* parent, CartesianCoordinatePlane* plane ) + : AbstractDiagram( p, parent, plane ) { init(); } +inline AbstractCartesianDiagram::Private * AbstractCartesianDiagram::d_func() +{ return static_cast( AbstractDiagram::d_func() ); } +inline const AbstractCartesianDiagram::Private * AbstractCartesianDiagram::d_func() const +{ return static_cast( AbstractDiagram::d_func() ); } +*/ + +} +#endif /* KDCHARTABSTRACTCARTESIANDIAGRAM_P_H */ + diff --git a/libkdchart/src/KDChartAbstractCoordinatePlane.cpp b/libkdchart/src/KDChartAbstractCoordinatePlane.cpp new file mode 100644 index 0000000..55ea2a1 --- /dev/null +++ b/libkdchart/src/KDChartAbstractCoordinatePlane.cpp @@ -0,0 +1,453 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractCoordinatePlane.h" +#include "KDChartAbstractCoordinatePlane_p.h" + +#include +#include +#include + +#include "KDChartChart.h" +#include "KDChartGridAttributes.h" + +#include + + +using namespace KDChart; + +#define d d_func() + +AbstractCoordinatePlane::Private::Private() + : AbstractArea::Private() + , parent( 0 ) + , grid( 0 ) + , referenceCoordinatePlane( 0 ) + , enableRubberBandZooming( false ) + , rubberBand( 0 ) +{ + // this bloc left empty intentionally +} + + +AbstractCoordinatePlane::AbstractCoordinatePlane ( KDChart::Chart* parent ) + : AbstractArea ( new Private() ) +{ + d->parent = parent; + d->init(); +} + +AbstractCoordinatePlane::~AbstractCoordinatePlane() +{ + emit destroyedCoordinatePlane( this ); +} + +void AbstractCoordinatePlane::init() +{ + d->initialize(); // virtual method to init the correct grid: cartesian, polar, ... + connect( this, SIGNAL(internal_geometryChanged( QRect, QRect )), + this, SIGNAL(geometryChanged( QRect, QRect )), + Qt::QueuedConnection ); +} + +void AbstractCoordinatePlane::addDiagram ( AbstractDiagram* diagram ) +{ + // diagrams are invisible and paint through their paint() method + diagram->hide(); + + d->diagrams.append( diagram ); + diagram->setParent( d->parent ); + diagram->setCoordinatePlane( this ); + layoutDiagrams(); + layoutPlanes(); // there might be new axes, etc + connect( diagram, SIGNAL( modelsChanged() ), this, SLOT( layoutPlanes() ) ); + connect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( update()) ); + connect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( relayout()) ); + + update(); +} + +/*virtual*/ +void AbstractCoordinatePlane::replaceDiagram ( AbstractDiagram* diagram, AbstractDiagram* oldDiagram_ ) +{ + if( diagram && oldDiagram_ != diagram ){ + AbstractDiagram* oldDiagram = oldDiagram_; + if( d->diagrams.count() ){ + if( ! oldDiagram ){ + oldDiagram = d->diagrams.first(); + if( oldDiagram == diagram ) + return; + } + takeDiagram( oldDiagram ); + } + delete oldDiagram; + addDiagram( diagram ); + layoutDiagrams(); + layoutPlanes(); // there might be new axes, etc + update(); + } +} + +/*virtual*/ +void AbstractCoordinatePlane::takeDiagram ( AbstractDiagram* diagram ) +{ + const int idx = d->diagrams.indexOf( diagram ); + if( idx != -1 ){ + d->diagrams.removeAt( idx ); + diagram->setParent( 0 ); + diagram->setCoordinatePlane( 0 ); + disconnect( diagram, SIGNAL( modelsChanged() ), this, SLOT( layoutPlanes() ) ); + disconnect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( update()) ); + disconnect( diagram, SIGNAL( modelDataChanged() ), this, SLOT( relayout()) ); + layoutDiagrams(); + update(); + } +} + + +AbstractDiagram* AbstractCoordinatePlane::diagram() +{ + if ( d->diagrams.isEmpty() ) + { + return 0; + } else { + return d->diagrams.first(); + } +} + +AbstractDiagramList AbstractCoordinatePlane::diagrams() +{ + return d->diagrams; +} + +ConstAbstractDiagramList AbstractCoordinatePlane::diagrams() const +{ + ConstAbstractDiagramList list; +#ifndef QT_NO_STL + qCopy( d->diagrams.begin(), d->diagrams.end(), std::back_inserter( list ) ); +#else + Q_FOREACH( AbstractDiagram * a, d->diagrams ) + list.push_back( a ); +#endif + return list; +} + +QSize KDChart::AbstractCoordinatePlane::minimumSizeHint() const +{ + return QSize( 200, 200 ); +} + + +QSizePolicy KDChart::AbstractCoordinatePlane::sizePolicy() const +{ + return QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); +} + +void KDChart::AbstractCoordinatePlane::setGlobalGridAttributes( const GridAttributes& a ) +{ + d->gridAttributes = a; + update(); +} + +GridAttributes KDChart::AbstractCoordinatePlane::globalGridAttributes() const +{ + return d->gridAttributes; +} + +KDChart::DataDimensionsList KDChart::AbstractCoordinatePlane::gridDimensionsList() +{ + //KDChart::DataDimensionsList l( d->grid->updateData( this ) ); + //qDebug() << "AbstractCoordinatePlane::gridDimensionsList() Y-range:" << l.last().end - l.last().start << " step width:" << l.last().stepWidth; + //qDebug() << "AbstractCoordinatePlane::gridDimensionsList() X-range:" << l.first().end - l.first().start << " step width:" << l.first().stepWidth; + return d->grid->updateData( this ); +} + +void KDChart::AbstractCoordinatePlane::setGridNeedsRecalculate() +{ + d->grid->setNeedRecalculate(); +} + +void KDChart::AbstractCoordinatePlane::setReferenceCoordinatePlane( AbstractCoordinatePlane * plane ) +{ + d->referenceCoordinatePlane = plane; +} + +AbstractCoordinatePlane * KDChart::AbstractCoordinatePlane::referenceCoordinatePlane( ) const +{ + return d->referenceCoordinatePlane; +} + +void KDChart::AbstractCoordinatePlane::setParent( KDChart::Chart* parent ) +{ + d->parent = parent; +} + +const KDChart::Chart* KDChart::AbstractCoordinatePlane::parent() const +{ + return d->parent; +} + +KDChart::Chart* KDChart::AbstractCoordinatePlane::parent() +{ + return d->parent; +} + +/* pure virtual in QLayoutItem */ +bool KDChart::AbstractCoordinatePlane::isEmpty() const +{ + return false; // never empty! + // coordinate planes with no associated diagrams + // are showing a default grid of ()1..10, 1..10) stepWidth 1 +} +/* pure virtual in QLayoutItem */ +Qt::Orientations KDChart::AbstractCoordinatePlane::expandingDirections() const +{ + return Qt::Vertical | Qt::Horizontal; +} +/* pure virtual in QLayoutItem */ +QSize KDChart::AbstractCoordinatePlane::maximumSize() const +{ + // No maximum size set. Especially not parent()->size(), we are not layouting + // to the parent widget's size when using Chart::paint()! + return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX); +} +/* pure virtual in QLayoutItem */ +QSize KDChart::AbstractCoordinatePlane::minimumSize() const +{ + return QSize(60, 60); // this default can be overwritten by derived classes +} +/* pure virtual in QLayoutItem */ +QSize KDChart::AbstractCoordinatePlane::sizeHint() const +{ + // we return our maxiumu (which is the full size of the Chart) + // even if we know the plane will be smaller + return maximumSize(); +} +/* pure virtual in QLayoutItem */ +void KDChart::AbstractCoordinatePlane::setGeometry( const QRect& r ) +{ +// qDebug() << "KDChart::AbstractCoordinatePlane::setGeometry(" << r << ") called"; + if( d->geometry != r ){ + //qDebug() << "entering KDChart::AbstractCoordinatePlane::setGeometry(" << r << ")"; + // inform the outside word by Signal geometryChanged() + // via a queued connection to internal_geometryChanged() + emit internal_geometryChanged( d->geometry, r ); + + d->geometry = r; + // Note: We do *not* call update() here + // because it would invoke KDChart::update() recursively. + //qDebug() << "leaving KDChart::AbstractCoordinatePlane::setGeometry(" << r << ")"; + } +} +/* pure virtual in QLayoutItem */ +QRect KDChart::AbstractCoordinatePlane::geometry() const +{ + return d->geometry; +} + +void KDChart::AbstractCoordinatePlane::update() +{ + //qDebug("KDChart::AbstractCoordinatePlane::update() called"); + emit needUpdate(); +} + +void KDChart::AbstractCoordinatePlane::relayout() +{ + //qDebug("KDChart::AbstractCoordinatePlane::relayout() called"); + emit needRelayout(); +} + +void KDChart::AbstractCoordinatePlane::layoutPlanes() +{ + //qDebug("KDChart::AbstractCoordinatePlane::relayout() called"); + emit needLayoutPlanes(); +} + +void KDChart::AbstractCoordinatePlane::setRubberBandZoomingEnabled( bool enable ) +{ + d->enableRubberBandZooming = enable; + + if( !enable && d->rubberBand != 0 ) + { + delete d->rubberBand; + d->rubberBand = 0; + } +} + +bool KDChart::AbstractCoordinatePlane::isRubberBandZoomingEnabled() const +{ + return d->enableRubberBandZooming; +} + +void KDChart::AbstractCoordinatePlane::mousePressEvent( QMouseEvent* event ) +{ + if( event->button() == Qt::LeftButton ) + { + if( d->enableRubberBandZooming && d->rubberBand == 0 ) + d->rubberBand = new QRubberBand( QRubberBand::Rectangle, qobject_cast< QWidget* >( parent() ) ); + + if( d->rubberBand != 0 ) + { + d->rubberBandOrigin = event->pos(); + d->rubberBand->setGeometry( QRect( event->pos(), QSize() ) ); + d->rubberBand->show(); + + event->accept(); + } + } + else if( event->button() == Qt::RightButton ) + { + if( d->enableRubberBandZooming && !d->rubberBandZoomConfigHistory.isEmpty() ) + { + // restore the last config from the stack + ZoomParameters config = d->rubberBandZoomConfigHistory.pop(); + setZoomFactorX( config.xFactor ); + setZoomFactorY( config.yFactor ); + setZoomCenter( config.center() ); + + QWidget* const p = qobject_cast< QWidget* >( parent() ); + if( p != 0 ) + p->update(); + + event->accept(); + } + } + + KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) + { + a->mousePressEvent( event ); + } +} + +void KDChart::AbstractCoordinatePlane::mouseDoubleClickEvent( QMouseEvent* event ) +{ + if( event->button() == Qt::RightButton ) + { + // othewise the second click gets lost + // which is pretty annoying when zooming out fast + mousePressEvent( event ); + } + KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) + { + a->mouseDoubleClickEvent( event ); + } +} + +void KDChart::AbstractCoordinatePlane::mouseReleaseEvent( QMouseEvent* event ) +{ + if( d->rubberBand != 0 ) + { + // save the old config on the stack + d->rubberBandZoomConfigHistory.push( ZoomParameters( zoomFactorX(), zoomFactorY(), zoomCenter() ) ); + + // this is the height/width of the rubber band in pixel space + const double rubberWidth = static_cast< double >( d->rubberBand->width() ); + const double rubberHeight = static_cast< double >( d->rubberBand->height() ); + + if( rubberWidth > 0.0 && rubberHeight > 0.0 ) + { + // this is the center of the rubber band in pixel space + const double rubberCenterX = static_cast< double >( d->rubberBand->geometry().center().x() - geometry().x() ); + const double rubberCenterY = static_cast< double >( d->rubberBand->geometry().center().y() - geometry().y() ); + + // this is the height/width of the plane in pixel space + const double myWidth = static_cast< double >( geometry().width() ); + const double myHeight = static_cast< double >( geometry().height() ); + + // this describes the new center of zooming, relative to the plane pixel space + const double newCenterX = rubberCenterX / myWidth / zoomFactorX() + zoomCenter().x() - 0.5 / zoomFactorX(); + const double newCenterY = rubberCenterY / myHeight / zoomFactorY() + zoomCenter().y() - 0.5 / zoomFactorY(); + + // this will be the new zoom factor + const double newZoomFactorX = zoomFactorX() * myWidth / rubberWidth; + const double newZoomFactorY = zoomFactorY() * myHeight / rubberHeight; + + // and this the new center + const QPointF newZoomCenter( newCenterX, newCenterY ); + + setZoomFactorX( newZoomFactorX ); + setZoomFactorY( newZoomFactorY ); + setZoomCenter( newZoomCenter ); + } + + d->rubberBand->parentWidget()->update(); + delete d->rubberBand; + d->rubberBand = 0; + + event->accept(); + } + + KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) + { + a->mouseReleaseEvent( event ); + } +} + +void KDChart::AbstractCoordinatePlane::mouseMoveEvent( QMouseEvent* event ) +{ + if( d->rubberBand != 0 ) + { + const QRect normalized = QRect( d->rubberBandOrigin, event->pos() ).normalized(); + d->rubberBand->setGeometry( normalized & geometry() ); + + event->accept(); + } + + KDAB_FOREACH( AbstractDiagram * a, d->diagrams ) + { + a->mouseMoveEvent( event ); + } +} + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +bool KDChart::AbstractCoordinatePlane::isVisiblePoint( const QPointF& point ) const +{ + return d->isVisiblePoint( this, point ); +} + +AbstractCoordinatePlane* KDChart::AbstractCoordinatePlane::sharedAxisMasterPlane( QPainter* p ) +{ + Q_UNUSED( p ); + return this; +} + +#if !defined(QT_NO_DEBUG_STREAM) +#include "KDChartEnums.h" + +QDebug KDChart::operator<<( QDebug stream, const DataDimension& r ) +{ + stream << "DataDimension(" + << " start=" << r.start + << " end=" << r.end + << " sequence=" << KDChartEnums::granularitySequenceToString( r.sequence ) + << " isCalculated=" << r.isCalculated + << " calcMode=" << ( r.calcMode == AbstractCoordinatePlane::Logarithmic ? "Logarithmic" : "Linear" ) + << " stepWidth=" << r.stepWidth + << " subStepWidth=" << r.subStepWidth + << " )"; + return stream; +} +#endif + +#undef d diff --git a/libkdchart/src/KDChartAbstractCoordinatePlane.h b/libkdchart/src/KDChartAbstractCoordinatePlane.h new file mode 100644 index 0000000..481e72b --- /dev/null +++ b/libkdchart/src/KDChartAbstractCoordinatePlane.h @@ -0,0 +1,437 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTCOORDINATEPLANE_H +#define KDCHARTABSTRACTCOORDINATEPLANE_H + +#include +#include + +#include "KDChartAbstractArea.h" +#include "KDChartAbstractDiagram.h" +#include "KDChartEnums.h" + +namespace KDChart { + + class Chart; + class GridAttributes; + class DataDimension; + + typedef QList DataDimensionsList; + + /** + * @brief Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane, TernaryCoordinatePlane + */ + class KDCHART_EXPORT AbstractCoordinatePlane : public AbstractArea + { + Q_OBJECT + + Q_DISABLE_COPY( AbstractCoordinatePlane ) + KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( AbstractCoordinatePlane, Chart* ) + + friend class AbstractGrid; + + public: + enum AxesCalcMode { Linear, Logarithmic }; + + protected: + explicit AbstractCoordinatePlane ( Chart* parent = 0 ); + + public: + virtual ~AbstractCoordinatePlane(); + + /** + * Adds a diagram to this coordinate plane. + * @param diagram The diagram to add. + * + * \sa replaceDiagram, takeDiagram + */ + virtual void addDiagram ( AbstractDiagram* diagram ); + + /** + * Replaces the old diagram, or appends the + * diagram, it there is none yet. + * + * @param diagram The diagram to be used instead of the old diagram. + * This parameter must not be zero, or the method will do nothing. + * + * @param oldDiagram The diagram to be removed by the new diagram. This + * diagram will be deleted automatically. If the parameter is omitted, + * the very first diagram will be replaced. In case, there was no + * diagram yet, the new diagram will just be added. + * + * \note If you want to re-use the old diagram, call takeDiagram and + * addDiagram, instead of using replaceDiagram. + * + * \sa addDiagram, takeDiagram + */ + virtual void replaceDiagram ( AbstractDiagram* diagram, AbstractDiagram* oldDiagram = 0 ); + + /** + * Removes the diagram from the plane, without deleting it. + * + * The plane no longer owns the diagram, so it is + * the caller's responsibility to delete the diagram. + * + * \sa addDiagram, replaceDiagram + */ + virtual void takeDiagram( AbstractDiagram* diagram ); + + /** + * @return The first diagram associated with this coordinate plane. + */ + AbstractDiagram* diagram(); + + /** + * @return The list of diagrams associated with this coordinate plane. + */ + AbstractDiagramList diagrams(); + + /** + * @return The list of diagrams associated with this coordinate plane. + */ + ConstAbstractDiagramList diagrams() const; + + /** + * Distribute the available space among the diagrams and axes. + */ + virtual void layoutDiagrams() = 0; + + /** + * Translate the given point in value space coordinates to a position + * in pixel space. + * @param diagramPoint The point in value coordinates. + * @returns The translated point. + */ + virtual const QPointF translate ( const QPointF& diagramPoint ) const = 0; + + /** \reimpl */ + virtual QSize minimumSizeHint() const; + /** \reimpl */ + virtual QSizePolicy sizePolicy() const; + + /** + * @return Whether zooming with a rubber band using the mouse is enabled. + */ + bool isRubberBandZoomingEnabled() const; + + /** + * Enables or disables zooming with a rubber band using the mouse. + */ + void setRubberBandZoomingEnabled( bool enable ); + + /** + * @return The zoom factor in horizontal direction, that is applied + * to all coordinate transformations. + */ + virtual double zoomFactorX() const { return 1.0; } + + /** + * @return The zoom factor in vertical direction, that is applied + * to all coordinate transformations. + */ + virtual double zoomFactorY() const { return 1.0; } + + /** + * Sets both zoom factors in one go. + * \sa setZoomFactorX,setZoomFactorY + */ + virtual void setZoomFactors( double factorX, double factorY ) { Q_UNUSED( factorX ); Q_UNUSED( factorY ); } + + /** + * Sets the zoom factor in horizontal direction, that is applied + * to all coordinate transformations. + * @param factor The new zoom factor + */ + virtual void setZoomFactorX( double factor ) { Q_UNUSED( factor ); } + + /** + * Sets the zoom factor in vertical direction, that is applied + * to all coordinate transformations. + * @param factor The new zoom factor + */ + virtual void setZoomFactorY( double factor ) { Q_UNUSED( factor ); } + + /** + * @return The center point (in value coordinates) of the + * coordinate plane, that is used for zoom operations. + */ + virtual QPointF zoomCenter() const { return QPointF(0.0, 0.0); } + + /** + * Set the point (in value coordinates) to be used as the + * center point in zoom operations. + * @param center The point to use. + */ + virtual void setZoomCenter( const QPointF& center ) { Q_UNUSED( center ); } + + /** + * Set the grid attributes to be used by this coordinate plane. + * To disable grid painting, for example, your code should like this: + * \code + * GridAttributes ga = plane->globalGridAttributes(); + * ga.setGlobalGridVisible( false ); + * plane->setGlobalGridAttributes( ga ); + * \endcode + * \sa globalGridAttributes + * \sa CartesianCoordinatePlane::setGridAttributes + */ + void setGlobalGridAttributes( const GridAttributes & ); + + /** + * @return The grid attributes used by this coordinate plane. + * \sa setGlobalGridAttributes + * \sa CartesianCoordinatePlane::gridAttributes + */ + GridAttributes globalGridAttributes() const; + + /** + * Returns the dimensions used for drawing the grid lines. + * + * Returned data is the result of (cached) grid calculations, + * so - if you need that information for your own tasks - make sure to + * call again this function after every data modification that has changed + * the data range, since grid calculation is based upon the data range, + * thus the grid start/end might have changed if the data was changed. + * + * @note Returned list will contain different numbers of DataDimension, + * depending on the kind of coordinate plane used. + * For CartesianCoordinatePlane two DataDimension are returned: the first + * representing grid lines in X direction (matching the Abscissa axes) + * and the second indicating vertical grid lines (or Ordinate axes, resp.). + * + * @return The dimensions used for drawing the grid lines. + * @sa DataDimension + */ + DataDimensionsList gridDimensionsList(); + + /** + * Set another coordinate plane to be used as the reference plane + * for this one. + * @param plane The coordinate plane to be used the reference plane + * for this one. + * @see referenceCoordinatePlane + */ + void setReferenceCoordinatePlane( AbstractCoordinatePlane * plane ); + + /** + * There are two ways, in which planes can be caused to interact, in + * where they are put layouting wise: The first is the reference plane. If + * such a reference plane is set, on a plane, it will use the same cell in the + * layout as that one. In addition to this, planes can share an axis. In that case + * they will be laid out in relation to each other as suggested by the position + * of the axis. If, for example Plane1 and Plane2 share an axis at position Left, + * that will result in the layout: Axis Plane1 Plane 2, vertically. If Plane1 + * also happens to be Plane2's reference plane, both planes are drawn over each + * other. The reference plane concept allows two planes to share the same space + * even if neither has any axis, and in case there are shared axis, it is used + * to decided, whether the planes should be painted on top of each other or + * laid out vertically or horizontally next to each other. + * @return The reference coordinate plane associated with this one. + */ + AbstractCoordinatePlane * referenceCoordinatePlane() const; + + virtual AbstractCoordinatePlane* sharedAxisMasterPlane( QPainter* p = 0 ); + + + /** pure virtual in QLayoutItem */ + virtual bool isEmpty() const; + /** pure virtual in QLayoutItem */ + virtual Qt::Orientations expandingDirections() const; + /** pure virtual in QLayoutItem */ + virtual QSize maximumSize() const; + /** pure virtual in QLayoutItem */ + virtual QSize minimumSize() const; + /** pure virtual in QLayoutItem */ + virtual QSize sizeHint() const; + /** pure virtual in QLayoutItem + * + * \note Do not call this function directly, unless you know + * exactly what you are doing. Geometry management is done + * by KD Chart's internal layouting measures. + */ + virtual void setGeometry( const QRect& r ); + /** pure virtual in QLayoutItem */ + virtual QRect geometry() const; + + virtual void mousePressEvent( QMouseEvent* event ); + virtual void mouseDoubleClickEvent( QMouseEvent* event ); + virtual void mouseMoveEvent( QMouseEvent* event ); + virtual void mouseReleaseEvent( QMouseEvent* event ); + + /** + * Called internally by KDChart::Chart + */ + void setParent( Chart* parent ); + Chart* parent(); + const Chart* parent() const; + + /** + * Tests, if a point is visible on the coordinate plane. + * + * \note Before calling this function the point must have been translated into coordinate plane space. + */ +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + const bool isVisiblePoint( const QPointF& point ) const; +#else + bool isVisiblePoint( const QPointF& point ) const; +#endif + + public Q_SLOTS: + /** + * Calling update() on the plane triggers the global KDChart::Chart::update() + */ + void update(); + /** + * Calling relayout() on the plane triggers the global KDChart::Chart::slotRelayout() + */ + void relayout(); + /** + * Calling layoutPlanes() on the plane triggers the global KDChart::Chart::slotLayoutPlanes() + */ + void layoutPlanes(); + /** + * Used by the chart to clear the cached grid data. + */ + void setGridNeedsRecalculate(); + + Q_SIGNALS: + /** Emitted when this coordinate plane is destroyed. */ + void destroyedCoordinatePlane( AbstractCoordinatePlane* ); + + /** Emitted when plane needs to update its drawings. */ + void needUpdate(); + + /** Emitted when plane needs to trigger the Chart's layouting. */ + void needRelayout(); + + /** Emitted when plane needs to trigger the Chart's layouting of the coord. planes. */ + void needLayoutPlanes(); + + /** Emitted upon change of a property of the Coordinate Plane or any of its components. */ + void propertiesChanged(); + + /** Emitted after the geometry of the Coordinate Plane has been changed. + * and control has returned to the event loop. + * + * Parameters are the the old geometry, the new geometry. + */ + void geometryChanged( QRect, QRect ); + + private: + Q_SIGNALS: + // Emitted from inside the setGeometry() + // This is connected via QueuedConnection to the geometryChanged() Signal + // that users can connect to safely then. + void internal_geometryChanged( QRect, QRect ); + + protected: + virtual DataDimensionsList getDataDimensionsList() const = 0; + + //KDCHART_DECLARE_PRIVATE_DERIVED( AbstractCoordinatePlane ) + }; + + /** + * \brief Helper class for one dimension of data, e.g. for the rows in a data model, + * or for the labels of an axis, or for the vertical lines in a grid. + * + * isCalculated specifies whether this dimension's values are calculated or counted. + * (counted == "Item 1", "Item 2", "Item 3" ...) + * + * sequence is the GranularitySequence, as specified at for the respective + * coordinate plane. + * + * Step width is an optional parameter, to be omitted (or set to Zero, resp.) + * if the step width is unknown. + * + * The default c'tor just gets you counted values from 1..10, using step width 1, + * used by the CartesianGrid, when showing an empty plane without any diagrams. + */ + class DataDimension{ + public: + DataDimension() + : start( 1.0 ) + , end( 10.0 ) + , isCalculated( false ) + , calcMode( AbstractCoordinatePlane::Linear ) + , sequence( KDChartEnums::GranularitySequence_10_20 ) + , stepWidth( 1.0 ) + , subStepWidth( 0.0 ) + {} + DataDimension( qreal start_, + qreal end_, + bool isCalculated_, + AbstractCoordinatePlane::AxesCalcMode calcMode_, + KDChartEnums::GranularitySequence sequence_, + qreal stepWidth_=0.0, + qreal subStepWidth_=0.0 ) + : start( start_ ) + , end( end_ ) + , isCalculated( isCalculated_ ) + , calcMode( calcMode_ ) + , sequence( sequence_ ) + , stepWidth( stepWidth_ ) + , subStepWidth( subStepWidth_ ) + {} + /** + * Returns the size of the distance, + * equivalent to the width() (or height(), resp.) of a QRectF. + * + * Note that this value can be negative, e.g. indicating axis labels + * going in reversed direction. + */ + qreal distance() const + { + return end-start; + } + + bool operator==( const DataDimension& r ) const + { + return + (start == r.start) && + (end == r.end) && + (sequence == r.sequence) && + (isCalculated == r.isCalculated) && + (calcMode == r.calcMode) && + (stepWidth == r.stepWidth) && + (subStepWidth == r.subStepWidth); + } + + bool operator!=( const DataDimension& other ) const + { return !operator==( other ); } + + + qreal start; + qreal end; + bool isCalculated; + AbstractCoordinatePlane::AxesCalcMode calcMode; + KDChartEnums::GranularitySequence sequence; + qreal stepWidth; + qreal subStepWidth; + }; + +#if !defined(QT_NO_DEBUG_STREAM) + QDebug operator<<( QDebug stream, const DataDimension& r ); +#endif + +} +#endif diff --git a/libkdchart/src/KDChartAbstractCoordinatePlane_p.h b/libkdchart/src/KDChartAbstractCoordinatePlane_p.h new file mode 100644 index 0000000..8500a80 --- /dev/null +++ b/libkdchart/src/KDChartAbstractCoordinatePlane_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRCOORDINATEPLANE_P_H +#define KDCHARTABSTRCOORDINATEPLANE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractArea_p.h" +#include +#include +#include +#include + +#include + +#include + +class QRubberBand; + +namespace KDChart { + + +/** + * \internal + */ +class AbstractCoordinatePlane::Private : public AbstractArea::Private +{ + friend class AbstractCoordinatePlane; +protected: + explicit Private(); + virtual ~Private(){ + delete grid; + }; + + virtual void initialize() + { + qDebug("ERROR: Calling AbstractCoordinatePlane::Private::initialize()"); + // can not call the base class: grid = new AbstractGrid(); + } + + virtual bool isVisiblePoint( + const AbstractCoordinatePlane * plane, + const QPointF& point ) const + { + Q_UNUSED( plane ); + Q_UNUSED( point ); + return true; + } + + KDChart::Chart* parent; + AbstractGrid* grid; + QRect geometry; + AbstractDiagramList diagrams; + GridAttributes gridAttributes; + AbstractCoordinatePlane *referenceCoordinatePlane; + + bool enableRubberBandZooming; + QRubberBand* rubberBand; + QPoint rubberBandOrigin; + + QStack< ZoomParameters > rubberBandZoomConfigHistory; +}; + + +inline AbstractCoordinatePlane::AbstractCoordinatePlane( Private * p, KDChart::Chart* parent ) + : AbstractArea( p ) +{ + if( p ) + p->parent = parent; + init(); +} +inline AbstractCoordinatePlane::Private * AbstractCoordinatePlane::d_func() +{ + return static_cast( AbstractArea::d_func() ); +} +inline const AbstractCoordinatePlane::Private * AbstractCoordinatePlane::d_func() const +{ + return static_cast( AbstractArea::d_func() ); +} + + +} + +#endif /* KDCHARTABSTRACTCOORDINATEPLANE_P_H*/ diff --git a/libkdchart/src/KDChartAbstractDiagram.cpp b/libkdchart/src/KDChartAbstractDiagram.cpp new file mode 100644 index 0000000..74ede14 --- /dev/null +++ b/libkdchart/src/KDChartAbstractDiagram.cpp @@ -0,0 +1,1083 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractDiagram.h" +#include "KDChartAbstractDiagram_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "KDChartAbstractCoordinatePlane.h" +#include "KDChartChart.h" +#include "KDChartDataValueAttributes.h" +#include "KDChartTextAttributes.h" +#include "KDChartMarkerAttributes.h" +#include "KDChartAbstractThreeDAttributes.h" +#include "KDChartThreeDLineAttributes.h" + +#include + +#define PI 3.141592653589793 + +using namespace KDChart; + +AbstractDiagram::Private::Private() + : plane( 0 ) + , attributesModel( new PrivateAttributesModel(0,0) ) + , allowOverlappingDataValueTexts( false ) + , antiAliasing( true ) + , percent( false ) + , datasetDimension( 1 ) + , databoundariesDirty(true) + , lastRoundedValue() + , lastX( 0 ) + , mCachedFontMetrics( QFontMetrics( qApp->font() ) ) +{ +} + +AbstractDiagram::Private::~Private() +{ + if( attributesModel && qobject_cast(attributesModel) ) + delete attributesModel; +} + +void AbstractDiagram::Private::init() +{ +} + +void AbstractDiagram::Private::init( AbstractCoordinatePlane* newPlane ) +{ + plane = newPlane; +} + +bool AbstractDiagram::Private::usesExternalAttributesModel()const +{ + return ( ! attributesModel.isNull() ) && + ( ! qobject_cast(attributesModel) ); +} + +void AbstractDiagram::Private::setAttributesModel( AttributesModel* amodel ) +{ + if( !attributesModel.isNull() && + qobject_cast(attributesModel) ) { + delete attributesModel; + } + attributesModel = amodel; +} + +AbstractDiagram::Private::Private( const AbstractDiagram::Private& rhs ) : + // Do not copy the plane + plane( 0 ), + attributesModelRootIndex( QModelIndex() ), + attributesModel( rhs.attributesModel ), + allowOverlappingDataValueTexts( rhs.allowOverlappingDataValueTexts ), + antiAliasing( rhs.antiAliasing ), + percent( rhs.percent ), + datasetDimension( rhs.datasetDimension ), + mCachedFontMetrics( rhs.cachedFontMetrics() ) +{ + attributesModel = new PrivateAttributesModel( 0, 0); + attributesModel->initFrom( rhs.attributesModel ); +} + +#define d d_func() + +AbstractDiagram::AbstractDiagram ( QWidget* parent, AbstractCoordinatePlane* plane ) + : QAbstractItemView ( parent ), _d( new Private() ) +{ + _d->init( plane ); + init(); +} + +AbstractDiagram::~AbstractDiagram() +{ + emit aboutToBeDestroyed(); + delete _d; +} + +void AbstractDiagram::init() +{ + d->reverseMapper.setDiagram( this ); +} + + +bool AbstractDiagram::compare( const AbstractDiagram* other )const +{ + if( other == this ) return true; + if( ! other ){ + //qDebug() << "AbstractDiagram::compare() cannot compare to Null pointer"; + return false; + } + /* + qDebug() << "\n AbstractDiagram::compare() QAbstractScrollArea:"; + // compare QAbstractScrollArea properties + qDebug() << + ((horizontalScrollBarPolicy() == other->horizontalScrollBarPolicy()) && + (verticalScrollBarPolicy() == other->verticalScrollBarPolicy())); + qDebug() << "AbstractDiagram::compare() QFrame:"; + // compare QFrame properties + qDebug() << + ((frameShadow() == other->frameShadow()) && + (frameShape() == other->frameShape()) && + (frameWidth() == other->frameWidth()) && + (lineWidth() == other->lineWidth()) && + (midLineWidth() == other->midLineWidth())); + qDebug() << "AbstractDiagram::compare() QAbstractItemView:"; + // compare QAbstractItemView properties + qDebug() << + ((alternatingRowColors() == other->alternatingRowColors()) && + (hasAutoScroll() == other->hasAutoScroll()) && +#if QT_VERSION > 0x040199 + (dragDropMode() == other->dragDropMode()) && + (dragDropOverwriteMode() == other->dragDropOverwriteMode()) && + (horizontalScrollMode() == other->horizontalScrollMode ()) && + (verticalScrollMode() == other->verticalScrollMode()) && +#endif + (dragEnabled() == other->dragEnabled()) && + (editTriggers() == other->editTriggers()) && + (iconSize() == other->iconSize()) && + (selectionBehavior() == other->selectionBehavior()) && + (selectionMode() == other->selectionMode()) && + (showDropIndicator() == other->showDropIndicator()) && + (tabKeyNavigation() == other->tabKeyNavigation()) && + (textElideMode() == other->textElideMode())); + qDebug() << "AbstractDiagram::compare() AttributesModel: "; + // compare all of the properties stored in the attributes model + qDebug() << attributesModel()->compare( other->attributesModel() ); + qDebug() << "AbstractDiagram::compare() own:"; + // compare own properties + qDebug() << + ((rootIndex().column() == other->rootIndex().column()) && + (rootIndex().row() == other->rootIndex().row()) && + (allowOverlappingDataValueTexts() == other->allowOverlappingDataValueTexts()) && + (antiAliasing() == other->antiAliasing()) && + (percentMode() == other->percentMode()) && + (datasetDimension() == other->datasetDimension())); + */ + return // compare QAbstractScrollArea properties + (horizontalScrollBarPolicy() == other->horizontalScrollBarPolicy()) && + (verticalScrollBarPolicy() == other->verticalScrollBarPolicy()) && + // compare QFrame properties + (frameShadow() == other->frameShadow()) && + (frameShape() == other->frameShape()) && +// frameWidth is a read-only property defined by the style, it should not be in here: + // (frameWidth() == other->frameWidth()) && + (lineWidth() == other->lineWidth()) && + (midLineWidth() == other->midLineWidth()) && + // compare QAbstractItemView properties + (alternatingRowColors() == other->alternatingRowColors()) && + (hasAutoScroll() == other->hasAutoScroll()) && +#if QT_VERSION > 0x040199 + (dragDropMode() == other->dragDropMode()) && + (dragDropOverwriteMode() == other->dragDropOverwriteMode()) && + (horizontalScrollMode() == other->horizontalScrollMode ()) && + (verticalScrollMode() == other->verticalScrollMode()) && +#endif + (dragEnabled() == other->dragEnabled()) && + (editTriggers() == other->editTriggers()) && + (iconSize() == other->iconSize()) && + (selectionBehavior() == other->selectionBehavior()) && + (selectionMode() == other->selectionMode()) && + (showDropIndicator() == other->showDropIndicator()) && + (tabKeyNavigation() == other->tabKeyNavigation()) && + (textElideMode() == other->textElideMode()) && + // compare all of the properties stored in the attributes model + attributesModel()->compare( other->attributesModel() ) && + // compare own properties + (rootIndex().column() == other->rootIndex().column()) && + (rootIndex().row() == other->rootIndex().row()) && + (allowOverlappingDataValueTexts() == other->allowOverlappingDataValueTexts()) && + (antiAliasing() == other->antiAliasing()) && + (percentMode() == other->percentMode()) && + (datasetDimension() == other->datasetDimension()); +} + +AbstractCoordinatePlane* AbstractDiagram::coordinatePlane() const +{ + return d->plane; +} + +const QPair AbstractDiagram::dataBoundaries () const +{ + if( d->databoundariesDirty ){ + d->databoundaries = calculateDataBoundaries (); + d->databoundariesDirty = false; + } + return d->databoundaries; +} + +void AbstractDiagram::setDataBoundariesDirty() const +{ + d->databoundariesDirty = true; +} + +void AbstractDiagram::setModel( QAbstractItemModel * newModel ) +{ + if( model() ) + { + disconnect( model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); + disconnect( model(), SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); + disconnect( model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); + disconnect( model(), SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); + disconnect( model(), SIGNAL( modelReset() ), this, SLOT( setDataBoundariesDirty() ) ); + disconnect( model(), SIGNAL( layoutChanged() ), this, SLOT( setDataBoundariesDirty() ) ); + disconnect( model(), SIGNAL( dataChanged(QModelIndex,QModelIndex) ), this, SIGNAL( modelDataChanged() )); + } + QAbstractItemView::setModel( newModel ); + AttributesModel* amodel = new PrivateAttributesModel( newModel, this ); + amodel->initFrom( d->attributesModel ); + d->setAttributesModel(amodel); + scheduleDelayedItemsLayout(); + setDataBoundariesDirty(); + if( model() ) + { + connect( model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); + connect( model(), SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); + connect( model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); + connect( model(), SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); + connect( model(), SIGNAL( modelReset() ), this, SLOT( setDataBoundariesDirty() ) ); + connect( model(), SIGNAL( layoutChanged() ), this, SLOT( setDataBoundariesDirty() ) ); + connect( model(), SIGNAL( dataChanged(QModelIndex,QModelIndex) ), this, SIGNAL( modelDataChanged() )); + } + emit modelsChanged(); +} + +void AbstractDiagram::setSelectionModel( QItemSelectionModel* newSelectionModel ) +{ + if( selectionModel() ) + { + disconnect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( modelsChanged() ) ); + disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SIGNAL( modelsChanged() ) ); + } + QAbstractItemView::setSelectionModel( newSelectionModel ); + if( selectionModel() ) + { + connect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( modelsChanged() ) ); + connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SIGNAL( modelsChanged() ) ); + } + emit modelsChanged(); +} + +/*! Sets an external AttributesModel on this diagram. By default, a diagram has it's + own internal set of attributes, but an external one can be set. This can be used to + share attributes between several diagrams. The diagram does not take ownership of the + attributesmodel. +*/ +void AbstractDiagram::setAttributesModel( AttributesModel* amodel ) +{ + if( amodel->sourceModel() != model() ) { + qWarning("KDChart::AbstractDiagram::setAttributesModel() failed: " + "Trying to set an attributesmodel which works on a different " + "model than the diagram."); + return; + } + if( qobject_cast(amodel) ) { + qWarning("KDChart::AbstractDiagram::setAttributesModel() failed: " + "Trying to set an attributesmodel that is private to another diagram."); + return; + } + d->setAttributesModel(amodel); + scheduleDelayedItemsLayout(); + setDataBoundariesDirty(); + emit modelsChanged(); +} + +bool AbstractDiagram::usesExternalAttributesModel()const +{ + return d->usesExternalAttributesModel(); +} + +/*! \returns a pointer to the AttributesModel currently used by this diagram. */ +AttributesModel* AbstractDiagram::attributesModel() const +{ + return d->attributesModel; +} + +QModelIndex AbstractDiagram::conditionallyMapFromSource( const QModelIndex & index ) const +{ + Q_ASSERT( !index.isValid() || index.model() == attributesModel() || index.model() == attributesModel()->sourceModel() ); + return index.model() == attributesModel() + ? index + : attributesModel()->mapFromSource( index ); +} + +/*! \reimpl */ +void AbstractDiagram::setRootIndex ( const QModelIndex& idx ) +{ + QAbstractItemView::setRootIndex(idx); + setAttributesModelRootIndex( d->attributesModel->mapFromSource(idx) ); +} + +/*! \internal */ +void AbstractDiagram::setAttributesModelRootIndex( const QModelIndex& idx ) +{ + d->attributesModelRootIndex=idx; + setDataBoundariesDirty(); + scheduleDelayedItemsLayout(); +} + +/*! returns a QModelIndex pointing into the AttributesModel that corresponds to the + root index of the diagram. */ +QModelIndex AbstractDiagram::attributesModelRootIndex() const +{ + if ( !d->attributesModelRootIndex.isValid() ) + d->attributesModelRootIndex = d->attributesModel->mapFromSource( rootIndex() ); + return d->attributesModelRootIndex; +} + +void AbstractDiagram::setCoordinatePlane( AbstractCoordinatePlane* parent ) +{ + d->plane = parent; +} + +void AbstractDiagram::doItemsLayout() +{ + if ( d->plane ) { + d->plane->layoutDiagrams(); + update(); + } + QAbstractItemView::doItemsLayout(); +} + +void AbstractDiagram::dataChanged( const QModelIndex &topLeft, + const QModelIndex &bottomRight ) +{ + Q_UNUSED( topLeft ); + Q_UNUSED( bottomRight ); + // We are still too dumb to do intelligent updates... + setDataBoundariesDirty(); + scheduleDelayedItemsLayout(); +} + + +void AbstractDiagram::setHidden( const QModelIndex & index, bool hidden ) +{ + d->attributesModel->setData( + conditionallyMapFromSource( index ), + qVariantFromValue( hidden ), + DataHiddenRole ); + emit dataHidden(); +} + +void AbstractDiagram::setHidden( int dataset, bool hidden ) +{ + // To store the flag for a dataset, we use the first column + // that's associated with it. (i.e., with a dataset dimension + // of two, the column of the keys) + d->setDatasetAttrs( dataset, qVariantFromValue( hidden ), DataHiddenRole ); + emit dataHidden(); +} + +void AbstractDiagram::setHidden( bool hidden ) +{ + d->attributesModel->setModelData( + qVariantFromValue( hidden ), + DataHiddenRole ); + emit dataHidden(); +} + +bool AbstractDiagram::isHidden() const +{ + return qVariantValue( + attributesModel()->modelData( DataHiddenRole ) ); +} + +bool AbstractDiagram::isHidden( int dataset ) const +{ + const QVariant boolFlag( d->datasetAttrs( dataset, DataHiddenRole ) ); + if( boolFlag.isValid() ) + return qVariantValue< bool >( boolFlag ); + return isHidden(); +} + +bool AbstractDiagram::isHidden( const QModelIndex & index ) const +{ + return qVariantValue( + attributesModel()->data( + conditionallyMapFromSource(index), + DataHiddenRole ) ); +} + + +void AbstractDiagram::setDataValueAttributes( const QModelIndex & index, + const DataValueAttributes & a ) +{ + d->attributesModel->setData( + conditionallyMapFromSource( index ), + qVariantFromValue( a ), + DataValueLabelAttributesRole ); + emit propertiesChanged(); +} + + +void AbstractDiagram::setDataValueAttributes( int dataset, const DataValueAttributes & a ) +{ + d->setDatasetAttrs( dataset, qVariantFromValue( a ), DataValueLabelAttributesRole ); + emit propertiesChanged(); +} + +DataValueAttributes AbstractDiagram::dataValueAttributes() const +{ + return qVariantValue( + attributesModel()->modelData( KDChart::DataValueLabelAttributesRole ) ); +} + +DataValueAttributes AbstractDiagram::dataValueAttributes( int dataset ) const +{ + /* + The following did not work! + (khz, 2008-01-25) + If there was some attrs specified for the 0-th cells of a dataset, + then this logic would return the cell's settings instead of the header settings: + + return qVariantValue( + attributesModel()->data( attributesModel()->mapFromSource(columnToIndex( column )), + KDChart::DataValueLabelAttributesRole ) ); + */ + + const QVariant headerAttrs( + d->datasetAttrs( dataset, KDChart::DataValueLabelAttributesRole ) ); + if( headerAttrs.isValid() ) + return qVariantValue< DataValueAttributes >( headerAttrs ); + return dataValueAttributes(); +} + +DataValueAttributes AbstractDiagram::dataValueAttributes( const QModelIndex & index ) const +{ + return qVariantValue( + attributesModel()->data( + conditionallyMapFromSource( index ), + KDChart::DataValueLabelAttributesRole ) ); +} + +void AbstractDiagram::setDataValueAttributes( const DataValueAttributes & a ) +{ + d->attributesModel->setModelData( qVariantFromValue( a ), DataValueLabelAttributesRole ); + emit propertiesChanged(); +} + +void AbstractDiagram::setAllowOverlappingDataValueTexts( bool allow ) +{ + d->allowOverlappingDataValueTexts = allow; + emit propertiesChanged(); +} + +bool AbstractDiagram::allowOverlappingDataValueTexts() const +{ + return d->allowOverlappingDataValueTexts; +} + +void AbstractDiagram::setAntiAliasing( bool enabled ) +{ + d->antiAliasing = enabled; + emit propertiesChanged(); +} + +bool AbstractDiagram::antiAliasing() const +{ + return d->antiAliasing; +} + +void AbstractDiagram::setPercentMode ( bool percent ) +{ + d->percent = percent; + emit propertiesChanged(); +} + +bool AbstractDiagram::percentMode() const +{ + return d->percent; +} + + +void AbstractDiagram::paintDataValueText( QPainter* painter, + const QModelIndex& index, + const QPointF& pos, + double value ) +{ + d->paintDataValueText( this, painter, index, pos, value ); +} + + +QString AbstractDiagram::roundValues( double value, + const int decimalPos, + const int decimalDigits ) const +{ + return d->roundValues( value, decimalPos, decimalDigits ); +} + +void AbstractDiagram::paintDataValueTexts( QPainter* painter ) +{ + if ( !checkInvariants() ) return; + const int rowCount = model()->rowCount(rootIndex()); + const int columnCount = model()->columnCount(rootIndex()); + d->clearListOfAlreadyDrawnDataValueTexts(); + for ( int i=datasetDimension()-1; iindex( j, i, rootIndex() ); + double value = model()->data( index ).toDouble(); + const QPointF pos = coordinatePlane()->translate( QPointF( j, value ) ); + paintDataValueText( painter, index, pos, value ); + } + } +} + + +void AbstractDiagram::paintMarker( QPainter* painter, + const DataValueAttributes& a, + const QModelIndex& index, + const QPointF& pos ) +{ + if ( !checkInvariants() || !a.isVisible() ) return; + const MarkerAttributes ma = a.markerAttributes(); + if ( !ma.isVisible() ) return; + + const PainterSaver painterSaver( painter ); + // the size of the marker - unscaled + const QSizeF maSize( ma.markerSize().width() / painter->matrix().m11(), + ma.markerSize().height() / painter->matrix().m22() ); + QBrush indexBrush( brush( index ) ); + QPen indexPen( ma.pen() ); + if ( ma.markerColor().isValid() ) + indexBrush.setColor( ma.markerColor() ); + + paintMarker( painter, ma, indexBrush, indexPen, pos, maSize ); + + // workaround: BC cannot be changed, otherwise we would pass the + // index down to next-lower paintMarker function. So far, we + // basically save a circle of radius maSize at pos in the + // reverseMapper. This means that ^^^ this version of paintMarker + // needs to be called to reverse-map the marker. + d->reverseMapper.addCircle( index.row(), index.column(), pos, 2 * maSize ); +} + +void AbstractDiagram::paintMarker( QPainter* painter, + const QModelIndex& index, + const QPointF& pos ) +{ + if ( !checkInvariants() ) return; + paintMarker( painter, dataValueAttributes( index ), index, pos ); +} + +void AbstractDiagram::paintMarker( QPainter* painter, + const MarkerAttributes& markerAttributes, + const QBrush& brush, + const QPen& pen, + const QPointF& pos, + const QSizeF& maSize ) +{ + const QPen oldPen( painter->pen() ); + // Pen is used to paint 4Pixels - 1 Pixel - Ring and FastCross types. + // make sure to use the brush color - see above in those cases. + const bool isFourPixels = (markerAttributes.markerStyle() == MarkerAttributes::Marker4Pixels); + if( isFourPixels || (markerAttributes.markerStyle() == MarkerAttributes::Marker1Pixel) ){ + // for high-performance point charts with tiny point markers: + painter->setPen( PrintingParameters::scalePen( QPen( brush.color().light() ) ) ); + if( isFourPixels ){ + const qreal x = pos.x(); + const qreal y = pos.y(); + painter->drawLine( QPointF(x-1.0,y-1.0), + QPointF(x+1.0,y-1.0) ); + painter->drawLine( QPointF(x-1.0,y), + QPointF(x+1.0,y) ); + painter->drawLine( QPointF(x-1.0,y+1.0), + QPointF(x+1.0,y+1.0) ); + } + painter->drawPoint( pos ); + }else{ + const PainterSaver painterSaver( painter ); + // we only a solid line surrounding the markers + QPen painterPen( pen ); + painterPen.setStyle( Qt::SolidLine ); + painter->setPen( PrintingParameters::scalePen( painterPen ) ); + painter->setBrush( brush ); + painter->setRenderHint ( QPainter::Antialiasing ); + painter->translate( pos ); + switch ( markerAttributes.markerStyle() ) { + case MarkerAttributes::MarkerCircle: + painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2, + maSize.height(), maSize.width()) ); + break; + case MarkerAttributes::MarkerSquare: + { + QRectF rect( 0 - maSize.width()/2, 0 - maSize.height()/2, + maSize.width(), maSize.height() ); + painter->drawRect( rect ); + break; + } + case MarkerAttributes::MarkerDiamond: + { + QVector diamondPoints; + QPointF top, left, bottom, right; + top = QPointF( 0, 0 - maSize.height()/2 ); + left = QPointF( 0 - maSize.width()/2, 0 ); + bottom = QPointF( 0, maSize.height()/2 ); + right = QPointF( maSize.width()/2, 0 ); + diamondPoints << top << left << bottom << right; + painter->drawPolygon( diamondPoints ); + break; + } + // both handled on top of the method: + case MarkerAttributes::Marker1Pixel: + case MarkerAttributes::Marker4Pixels: + break; + case MarkerAttributes::MarkerRing: + { + painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) ); + painter->setBrush( Qt::NoBrush ); + painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2, + maSize.height(), maSize.width()) ); + break; + } + case MarkerAttributes::MarkerCross: + { + // Note: Markers can have outline, + // so just drawing two rects is NOT the solution here! + const qreal w02 = maSize.width() * 0.2; + const qreal w05 = maSize.width() * 0.5; + const qreal h02 = maSize.height()* 0.2; + const qreal h05 = maSize.height()* 0.5; + QVector crossPoints; + QPointF p[12]; + p[ 0] = QPointF( -w02, -h05 ); + p[ 1] = QPointF( w02, -h05 ); + p[ 2] = QPointF( w02, -h02 ); + p[ 3] = QPointF( w05, -h02 ); + p[ 4] = QPointF( w05, h02 ); + p[ 5] = QPointF( w02, h02 ); + p[ 6] = QPointF( w02, h05 ); + p[ 7] = QPointF( -w02, h05 ); + p[ 8] = QPointF( -w02, h02 ); + p[ 9] = QPointF( -w05, h02 ); + p[10] = QPointF( -w05, -h02 ); + p[11] = QPointF( -w02, -h02 ); + for( int i=0; i<12; ++i ) + crossPoints << p[i]; + crossPoints << p[0]; + painter->drawPolygon( crossPoints ); + break; + } + case MarkerAttributes::MarkerFastCross: + { + QPointF left, right, top, bottom; + left = QPointF( -maSize.width()/2, 0 ); + right = QPointF( maSize.width()/2, 0 ); + top = QPointF( 0, -maSize.height()/2 ); + bottom= QPointF( 0, maSize.height()/2 ); + painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) ); + painter->drawLine( left, right ); + painter->drawLine( top, bottom ); + break; + } + case MarkerAttributes::NoMarker: + break; + default: + Q_ASSERT_X ( false, "paintMarkers()", + "Type item does not match a defined Marker Type." ); + } + } + painter->setPen( oldPen ); +} + +void AbstractDiagram::paintMarkers( QPainter* painter ) +{ + if ( !checkInvariants() ) return; + const int rowCount = model()->rowCount(rootIndex()); + const int columnCount = model()->columnCount(rootIndex()); + for ( int i=datasetDimension()-1; iindex( j, i, rootIndex() ); + double value = model()->data( index ).toDouble(); + const QPointF pos = coordinatePlane()->translate( QPointF( j, value ) ); + paintMarker( painter, index, pos ); + } + } +} + + +void AbstractDiagram::setPen( const QModelIndex& index, const QPen& pen ) +{ + attributesModel()->setData( + conditionallyMapFromSource( index ), + qVariantFromValue( pen ), DatasetPenRole ); + emit propertiesChanged(); +} + +void AbstractDiagram::setPen( const QPen& pen ) +{ + attributesModel()->setModelData( + qVariantFromValue( pen ), DatasetPenRole ); + emit propertiesChanged(); +} + +void AbstractDiagram::setPen( int dataset, const QPen& pen ) +{ + d->setDatasetAttrs( dataset, qVariantFromValue( pen ), DatasetPenRole ); + emit propertiesChanged(); +} + +QPen AbstractDiagram::pen() const +{ + return qVariantValue( + attributesModel()->data( DatasetPenRole ) ); +} + +QPen AbstractDiagram::pen( int dataset ) const +{ + const QVariant penSettings( d->datasetAttrs( dataset, DatasetPenRole ) ); + if( penSettings.isValid() ) + return qVariantValue< QPen >( penSettings ); + return pen(); +} + +QPen AbstractDiagram::pen( const QModelIndex& index ) const +{ + return qVariantValue( + attributesModel()->data( + conditionallyMapFromSource( index ), + DatasetPenRole ) ); +} + +void AbstractDiagram::setBrush( const QModelIndex& index, const QBrush& brush ) +{ + attributesModel()->setData( + conditionallyMapFromSource( index ), + qVariantFromValue( brush ), DatasetBrushRole ); + emit propertiesChanged(); +} + +void AbstractDiagram::setBrush( const QBrush& brush ) +{ + attributesModel()->setModelData( + qVariantFromValue( brush ), DatasetBrushRole ); + emit propertiesChanged(); +} + +void AbstractDiagram::setBrush( int dataset, const QBrush& brush ) +{ + d->setDatasetAttrs( dataset, qVariantFromValue( brush ), DatasetBrushRole ); + emit propertiesChanged(); +} + +QBrush AbstractDiagram::brush() const +{ + return qVariantValue( + attributesModel()->data( DatasetBrushRole ) ); +} + +QBrush AbstractDiagram::brush( int dataset ) const +{ + const QVariant brushSettings( d->datasetAttrs( dataset, DatasetBrushRole ) ); + if( brushSettings.isValid() ) + return qVariantValue< QBrush >( brushSettings ); + return brush(); +} + +QBrush AbstractDiagram::brush( const QModelIndex& index ) const +{ + return qVariantValue( + attributesModel()->data( conditionallyMapFromSource( index ), DatasetBrushRole ) ); +} + +/** + * Sets the unit prefix for one value + * @param prefix the prefix to be set + * @param column the value using that prefix + * @param orientation the orientantion of the axis to set + */ +void AbstractDiagram::setUnitPrefix( const QString& prefix, int column, Qt::Orientation orientation ) +{ + d->unitPrefixMap[ column ][ orientation ]= prefix; +} + +/** + * Sets the unit prefix for all values + * @param prefix the prefix to be set + * @param orientation the orientantion of the axis to set + */ +void AbstractDiagram::setUnitPrefix( const QString& prefix, Qt::Orientation orientation ) +{ + d->unitPrefix[ orientation ] = prefix; +} + +/** + * Sets the unit suffix for one value + * @param suffix the suffix to be set + * @param column the value using that suffix + * @param orientation the orientantion of the axis to set + */ +void AbstractDiagram::setUnitSuffix( const QString& suffix, int column, Qt::Orientation orientation ) +{ + d->unitSuffixMap[ column ][ orientation ]= suffix; +} + +/** + * Sets the unit suffix for all values + * @param suffix the suffix to be set + * @param orientation the orientantion of the axis to set + */ +void AbstractDiagram::setUnitSuffix( const QString& suffix, Qt::Orientation orientation ) +{ + d->unitSuffix[ orientation ] = suffix; +} + +/** + * Returns the unit prefix for a special value + * @param column the value which's prefix is requested + * @param orientation the orientation of the axis + * @param fallback if true, the global prefix is return when no specific one is set for that value + * @return the unit prefix + */ +QString AbstractDiagram::unitPrefix( int column, Qt::Orientation orientation, bool fallback ) const +{ + if( !fallback || d->unitPrefixMap[ column ].contains( orientation ) ) + return d->unitPrefixMap[ column ][ orientation ]; + return d->unitPrefix[ orientation ]; +} + +/** Returns the global unit prefix + * @param orientation the orientation of the axis + * @return the unit prefix + */ +QString AbstractDiagram::unitPrefix( Qt::Orientation orientation ) const +{ + return d->unitPrefix[ orientation ]; +} + +/** + * Returns the unit suffix for a special value + * @param column the value which's suffix is requested + * @param orientation the orientation of the axis + * @param fallback if true, the global suffix is return when no specific one is set for that value + * @return the unit suffix + */ +QString AbstractDiagram::unitSuffix( int column, Qt::Orientation orientation, bool fallback ) const +{ + if( !fallback || d->unitSuffixMap[ column ].contains( orientation ) ) + return d->unitSuffixMap[ column ][ orientation ]; + return d->unitSuffix[ orientation ]; +} + +/** Returns the global unit suffix + * @param orientation the orientation of the axis + * @return the unit siffix + */ +QString AbstractDiagram::unitSuffix( Qt::Orientation orientation ) const +{ + return d->unitSuffix[ orientation ]; +} + +// implement QAbstractItemView: +QRect AbstractDiagram::visualRect( const QModelIndex &index ) const +{ + return d->reverseMapper.boundingRect( index.row(), index.column() ).toRect(); +} + +void AbstractDiagram::scrollTo(const QModelIndex &, ScrollHint ) +{} + +// indexAt ... down below + +QModelIndex AbstractDiagram::moveCursor(CursorAction, Qt::KeyboardModifiers ) +{ return QModelIndex(); } + +int AbstractDiagram::horizontalOffset() const +{ return 0; } + +int AbstractDiagram::verticalOffset() const +{ return 0; } + +bool AbstractDiagram::isIndexHidden(const QModelIndex &) const +{ return true; } + +void AbstractDiagram::setSelection(const QRect& rect , QItemSelectionModel::SelectionFlags command ) +{ + const QModelIndexList indexes = d->indexesIn( rect ); + QItemSelection selection; + KDAB_FOREACH( const QModelIndex& index, indexes ) + { + selection.append( QItemSelectionRange( index ) ); + } + selectionModel()->select( selection, command ); +} + +QRegion AbstractDiagram::visualRegionForSelection(const QItemSelection &selection) const +{ + QPolygonF polygon; + KDAB_FOREACH( const QModelIndex& index, selection.indexes() ) + { + polygon << d->reverseMapper.polygon(index.row(), index.column()); + } + return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() ); +} + +QRegion AbstractDiagram::visualRegion(const QModelIndex &index) const +{ + QPolygonF polygon = d->reverseMapper.polygon(index.row(), index.column()); + return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() ); +} + +void KDChart::AbstractDiagram::useDefaultColors( ) +{ + d->attributesModel->setPaletteType( AttributesModel::PaletteTypeDefault ); +} + +void KDChart::AbstractDiagram::useSubduedColors( ) +{ + d->attributesModel->setPaletteType( AttributesModel::PaletteTypeSubdued ); +} + +void KDChart::AbstractDiagram::useRainbowColors( ) +{ + d->attributesModel->setPaletteType( AttributesModel::PaletteTypeRainbow ); +} + +QStringList AbstractDiagram::itemRowLabels() const +{ + QStringList ret; + if( model() ){ + //qDebug() << "AbstractDiagram::itemRowLabels(): " << attributesModel()->rowCount(attributesModelRootIndex()) << "entries"; + const int rowCount = attributesModel()->rowCount(attributesModelRootIndex()); + for( int i = 0; i < rowCount; ++i ){ + //qDebug() << "item row label: " << attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString(); + ret << unitPrefix( i, Qt::Horizontal, true ) + + attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString() + + unitSuffix( i, Qt::Horizontal, true ); + } + } + return ret; +} + +QStringList AbstractDiagram::datasetLabels() const +{ + QStringList ret; + if( model() == 0 ) + return ret; + + const int columnCount = attributesModel()->columnCount(attributesModelRootIndex()); + for( int i = 0; i < columnCount; i += datasetDimension() ) + ret << attributesModel()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString(); + + return ret; +} + +QList AbstractDiagram::datasetBrushes() const +{ + QList ret; + if( model() == 0 ) + return ret; + + const int datasetCount = attributesModel()->columnCount(attributesModelRootIndex()) / datasetDimension(); + for ( int dataset = 0; dataset < datasetCount; dataset++ ) + ret << brush( dataset ); + + return ret; +} + +QList AbstractDiagram::datasetPens() const +{ + QList ret; + if( model() == 0 ) + return ret; + + const int datasetCount = attributesModel()->columnCount(attributesModelRootIndex()) / datasetDimension(); + for ( int dataset = 0; dataset < datasetCount; dataset++ ) + ret << pen( dataset ); + + return ret; +} + +QList AbstractDiagram::datasetMarkers() const +{ + QList ret; + if( model() == 0 ) + return ret; + + const int datasetCount = attributesModel()->columnCount(attributesModelRootIndex()) / datasetDimension(); + for ( int dataset = 0; dataset < datasetCount; dataset++ ) + ret << dataValueAttributes( dataset ).markerAttributes(); + + return ret; +} + +bool AbstractDiagram::checkInvariants( bool justReturnTheStatus ) const +{ + if( ! justReturnTheStatus ){ + Q_ASSERT_X ( model(), "AbstractDiagram::checkInvariants()", + "There is no usable model set, for the diagram." ); + + Q_ASSERT_X ( coordinatePlane(), "AbstractDiagram::checkInvariants()", + "There is no usable coordinate plane set, for the diagram." ); + } + return model() && coordinatePlane(); +} + +int AbstractDiagram::datasetDimension( ) const +{ + return d->datasetDimension; +} + +void AbstractDiagram::setDatasetDimension( int dimension ) +{ + Q_UNUSED( dimension ); + qDebug() << "Setting the dataset dimension using AbstractDiagram::setDatasetDimension is obsolete. Use the specific diagram types instead."; +} + +void AbstractDiagram::setDatasetDimensionInternal( int dimension ) +{ + Q_ASSERT( dimension != 0 ); + + if ( d->datasetDimension == dimension ) return; + d->datasetDimension = dimension; + setDataBoundariesDirty(); + emit layoutChanged( this ); +} + +double AbstractDiagram::valueForCell( int row, int column ) const +{ + return d->attributesModel->data( + d->attributesModel->index( row, column, attributesModelRootIndex() ) ).toDouble(); +} + +void AbstractDiagram::update() const +{ + //qDebug("KDChart::AbstractDiagram::update() called"); + if( d->plane ) + d->plane->update(); +} + +QModelIndex AbstractDiagram::indexAt( const QPoint& point ) const +{ + return d->indexAt( point ); +} + +QModelIndexList AbstractDiagram::indexesAt( const QPoint& point ) const +{ + return d->indexesAt( point ); +} + diff --git a/libkdchart/src/KDChartAbstractDiagram.h b/libkdchart/src/KDChartAbstractDiagram.h new file mode 100644 index 0000000..2e21cf6 --- /dev/null +++ b/libkdchart/src/KDChartAbstractDiagram.h @@ -0,0 +1,731 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTDIAGRAM_H +#define KDCHARTABSTRACTDIAGRAM_H + +#include +#include +#include + +#include "KDChartGlobal.h" +#include "KDChartMarkerAttributes.h" +#include "KDChartAttributesModel.h" + +namespace KDChart { + + class AbstractCoordinatePlane; + class AttributesModel; + class DataValueAttributes; + class PaintContext; + + /** + * @brief AbstractDiagram defines the interface for diagram classes + * + * AbstractDiagram is the base class for diagram classes ("chart types"). + * + * It defines the interface, that needs to be implemented for the diagram, + * to function within the KDChart framework. It extends Interview's + * QAbstractItemView. + */ + class KDCHART_EXPORT AbstractDiagram : public QAbstractItemView + { + Q_OBJECT + Q_DISABLE_COPY( AbstractDiagram ) + KDCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC( AbstractDiagram ) + + friend class AbstractCoordinatePlane; + friend class CartesianCoordinatePlane; + friend class PolarCoordinatePlane; + + protected: + explicit inline AbstractDiagram( + Private *p, QWidget* parent, AbstractCoordinatePlane* plane ); + explicit AbstractDiagram ( + QWidget* parent = 0, AbstractCoordinatePlane* plane = 0 ); + public: + virtual ~AbstractDiagram(); + + + /** + * Returns true if both diagrams have the same settings. + */ + bool compare( const AbstractDiagram* other )const; + + + /** + * @brief Return the bottom left and top right data point, that the + * diagram will display (unless the grid adjusts these values). + * + * This method returns a chached result of calculations done by + * calculateDataBoundaries. + * Classes derived from AbstractDiagram must implement the + * calculateDataBoundaries function, to specify their own + * way of calculating the data boundaries. + * If derived classes want to force recalculation of the + * data boundaries, they can call setDataBoundariesDirty() + * + * Returned value is in diagram coordinates. + */ + const QPair dataBoundaries() const; + + // protected: // FIXME: why should that be private? (Mirko) + /** + * Draw the diagram contents to the rectangle and painter, that are + * passed in as part of the paint context. + * + * @param paintContext All information needed for painting. + */ + virtual void paint ( PaintContext* paintContext ) = 0; + + /** + * Called by the widget's sizeEvent. Adjust all internal structures, + * that are calculated, dependending on the size of the widget. + * + * @param area + */ + virtual void resize ( const QSizeF& area ) = 0; + + /** Associate a model with the diagram. */ + virtual void setModel ( QAbstractItemModel * model ); + + /** Associate a seleection model with the diagrom. */ + virtual void setSelectionModel( QItemSelectionModel* selectionModel ); + + /** + * Associate an AttributesModel with this diagram. Note that + * the diagram does _not_ take ownership of the AttributesModel. + * This should thus only be used with AttributesModels that + * have been explicitly created by the user, and are owned + * by her. Setting an AttributesModel that is internal to + * another diagram is an error. + * + * Correct: + * + * \code + * AttributesModel *am = new AttributesModel( model, 0 ); + * diagram1->setAttributesModel( am ); + * diagram2->setAttributesModel( am ); + * + * \endcode + * + * Wrong: + * + * \code + * + * diagram1->setAttributesModel( diagram2->attributesModel() ); + * + * \endcode + * + * @param model The AttributesModel to use for this diagram. + * @see AttributesModel, usesExternalAttributesModel + */ + virtual void setAttributesModel( AttributesModel* model ); + + /** + * Returns whether the diagram is using its own built-in attributes model + * or an attributes model that was set via setAttributesModel. + * + * @see setAttributesModel + */ + virtual bool usesExternalAttributesModel()const; + + /** + * Returns the AttributesModel, that is used by this diagram. + * By default each diagram owns its own AttributesModel, which + * should never be deleted. Only if a user-supplied AttributesModel + * has been set does the pointer returned here not belong to the + * diagram. + * + * @return The AttributesModel associated with the diagram. + * @see setAttributesModel + */ + virtual AttributesModel* attributesModel() const; + + /** Set the root index in the model, where the diagram starts + * referencing data for display. */ + virtual void setRootIndex ( const QModelIndex& idx ); + + /** \reimpl */ + virtual QRect visualRect(const QModelIndex &index) const; + /** \reimpl */ + virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible); + /** \reimpl */ + virtual QModelIndex indexAt(const QPoint &point) const; + /** \reimpl */ + virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers); + /** \reimpl */ + virtual int horizontalOffset() const; + /** \reimpl */ + virtual int verticalOffset() const; + /** \reimpl */ + virtual bool isIndexHidden(const QModelIndex &index) const; + /** \reimpl */ + virtual void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command); + /** \reimpl */ + virtual QRegion visualRegionForSelection(const QItemSelection &selection) const; + virtual QRegion visualRegion(const QModelIndex &index) const; + /** \reimpl */ + virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + /** \reimpl */ + virtual void doItemsLayout(); + + /** + * The coordinate plane associated with the diagram. This determines + * how coordinates in value space are mapped into pixel space. By default + * this is a CartesianCoordinatePlane. + * @return The coordinate plane associated with the diagram. + */ + AbstractCoordinatePlane* coordinatePlane() const; + + /** + * Set the coordinate plane associated with the diagram. This determines + * how coordinates in value space are mapped into pixel space. The chart + * takes ownership. + * @return The coordinate plane associated with the diagram. + */ + virtual void setCoordinatePlane( AbstractCoordinatePlane* plane ); + + + /** + * Hide (or unhide, resp.) a data cell. + * + * \note Hidden data are still taken into account by the coordinate plane, + * so neither the grid nor your axes' ranges will change, when you hide data. + * For totally removing data from KD Chart's view you can use another approach: + * e.g. you could define a proxy model on top of your data model, and register + * the proxy model calling setModel() instead of registering your real data model. + * + * @param index The datapoint to set the hidden status for. With a dataset dimension + * of two, this is the index of the key of each key/value pair. + * @param hidden The hidden status to set. + */ + void setHidden( const QModelIndex & index, bool hidden ); + + /** + * Hide (or unhide, resp.) a dataset. + * + * \note Hidden data are still taken into account by the coordinate plane, + * so neither the grid nor your axes' ranges will change, when you hide data. + * For totally removing data from KD Chart's view you can use another approach: + * e.g. you could define a proxy model on top of your data model, and register + * the proxy model calling setModel() instead of registering your real data model. + * + * @param dataset The dataset to set the hidden status for. + * @param hidden The hidden status to set. + */ + void setHidden( int dataset, bool hidden ); + + /** + * Hide (or unhide, resp.) all datapoints in the model. + * + * \note Hidden data are still taken into account by the coordinate plane, + * so neither the grid nor your axes' ranges will change, when you hide data. + * For totally removing data from KD Chart's view you can use another approach: + * e.g. you could define a proxy model on top of your data model, and register + * the proxy model calling setModel() instead of registering your real data model. + * + * @param hidden The hidden status to set. + */ + void setHidden( bool hidden ); + + /** + * Retrieve the hidden status specified globally. This will fall + * back automatically to the default settings ( = not hidden), if there + * are no specific settings. + * @return The global hidden status. + */ + bool isHidden() const; + + /** + * Retrieve the hidden status for the given dataset. This will fall + * back automatically to what was set at diagram level, if there + * are no dataset specific settings. + * @param dataset The dataset to retrieve the hidden status for. + * @return The hidden status for the given dataset. + */ + bool isHidden( int dataset ) const; + + /** + * Retrieve the hidden status for the given index. This will fall + * back automatically to what was set at dataset or diagram level, if there + * are no datapoint specific settings. + * @param index The datapoint to retrieve the hidden status for. + * @return The hidden status for the given index. + */ + bool isHidden( const QModelIndex & index ) const; + + + /** + * Set the DataValueAttributes for the given index. + * @param index The datapoint to set the attributes for. With a dataset dimension + * of two, this is the index of the key of each key/value pair. + * @param a The attributes to set. + */ + void setDataValueAttributes( const QModelIndex & index, + const DataValueAttributes & a ); + + /** + * Set the DataValueAttributes for the given dataset. + * @param dataset The dataset to set the attributes for. + * @param a The attributes to set. + */ + void setDataValueAttributes( int dataset, const DataValueAttributes & a ); + + /** + * Set the DataValueAttributes for all datapoints in the model. + * @param a The attributes to set. + */ + void setDataValueAttributes( const DataValueAttributes & a ); + + /** + * Retrieve the DataValueAttributes specified globally. This will fall + * back automatically to the default settings, if there + * are no specific settings. + * @return The global DataValueAttributes. + */ + DataValueAttributes dataValueAttributes() const; + + /** + * Retrieve the DataValueAttributes for the given dataset. This will fall + * back automatically to what was set at model level, if there + * are no dataset specific settings. + * @param dataset The dataset to retrieve the attributes for. + * @return The DataValueAttributes for the given dataset. + */ + DataValueAttributes dataValueAttributes( int dataset ) const; + + /** + * Retrieve the DataValueAttributes for the given index. This will fall + * back automatically to what was set at dataset or model level, if there + * are no datapoint specific settings. + * @param index The datapoint to retrieve the attributes for. With a dataset dimension + * of two, this is the index of the key of each key/value pair. + * @return The DataValueAttributes for the given index. + */ + DataValueAttributes dataValueAttributes( const QModelIndex & index ) const; + + /** + * Set the pen to be used, for painting the datapoint at the given index. + * @param index The datapoint's index in the model. With a dataset dimension + * of two, this is the index of the key of each key/value pair. + * @param pen The pen to use. + */ + void setPen( const QModelIndex& index, const QPen& pen ); + + /** + * Set the pen to be used, for painting the given dataset. + * @param dataset The dataset to set the pen for. + * @param pen The pen to use. + */ + void setPen( int dataset, const QPen& pen ); + + /** + * Set the pen to be used, for painting all datasets in the model. + * @param pen The pen to use. + */ + void setPen( const QPen& pen ); + + /** + * Retrieve the pen to be used for painting datapoints globally. This will fall + * back automatically to the default settings, if there + * are no specific settings. + * @return The pen to use for painting. + */ + QPen pen() const; + /** + * Retrieve the pen to be used for the given dataset. This will fall + * back automatically to what was set at model level, if there + * are no dataset specific settings. + * @param dataset The dataset to retrieve the pen for. + * @return The pen to use for painting. + */ + QPen pen( int dataset ) const; + /** + * Retrieve the pen to be used, for painting the datapoint at the given + * index in the model. + * @param index The index of the datapoint in the model. With a dataset dimension + * of two, this is the index of the key of each key/value pair. + * @return The pen to use for painting. + */ + QPen pen( const QModelIndex& index ) const; + + /** + * Set the brush to be used, for painting the datapoint at the given index. + * @param index The datapoint's index in the model. With a dataset dimension + * of two, this is the index of the key of each key/value pair. + * @param brush The brush to use. + */ + void setBrush( const QModelIndex& index, const QBrush& brush); + + /** + * Set the brush to be used, for painting the given dataset. + * @param dataset The dataset to set the brush for. + * @param brush The brush to use. + */ + void setBrush( int dataset, const QBrush& brush ); + + /** + * Set the brush to be used, for painting all datasets in the model. + * @param brush The brush to use. + */ + void setBrush( const QBrush& brush); + + /** + * Retrieve the brush to be used for painting datapoints globally. This will fall + * back automatically to the default settings, if there + * are no specific settings. + * @return The brush to use for painting. + */ + QBrush brush() const; + /** + * Retrieve the brush to be used for the given dataset. This will fall + * back automatically to what was set at model level, if there + * are no dataset specific settings. + * @param dataset The dataset to retrieve the brush for. + * @return The brush to use for painting. + */ + QBrush brush( int dataset ) const; + /** + * Retrieve the brush to be used, for painting the datapoint at the given + * index in the model. + * @param index The index of the datapoint in the model. With a dataset dimension + * of two, this is the index of the key of each key/value pair. + * @return The brush to use for painting. + */ + QBrush brush( const QModelIndex& index ) const; + + /** + * Set the unit prefix to be used on axes for one specific column. + * @param prefix The prefix to be used. + * @param column The column which should be set. + * @param orientation The orientation of the axis to use. + */ + void setUnitPrefix( const QString& prefix, int column, Qt::Orientation orientation ); + /** + * Set the unit prefix to be used on axes for all columns. + * @param prefix The prefix to be used. + * @param orientation The orientation of the axis to use. + */ + void setUnitPrefix( const QString& prefix, Qt::Orientation orientation ); + + /** + * Set the unit prefix to be used on axes for one specific column. + * @param suffix The suffix to be used. + * @param column The column which should be set. + * @param orientation The orientation of the axis to use. + */ + void setUnitSuffix( const QString& suffix, int column, Qt::Orientation orientation ); + /** + * Set the unit prefix to be used on axes for all columns. + * @param suffix The suffix to be used. + * @param orientation The orientation of the axis to use. + */ + void setUnitSuffix( const QString& suffix, Qt::Orientation orientation ); + + /** + * Retrieves the axis unit prefix for a specific column. + * @param column The column whose prefix should be retrieved. + * @param orientation The orientation of the axis. + * @param fallback If true, the prefix for all columns is returned, when + * none is set for the selected column. + * @return The axis unit prefix. + */ + QString unitPrefix( int column, Qt::Orientation orientation, bool fallback = false ) const; + /** + * Retrieves the axis unit prefix. + * @param orientation The orientation of the axis. + * @return The axis unit prefix. + */ + QString unitPrefix( Qt::Orientation orientation ) const; + + /** + * Retrieves the axis unit suffix for a specific column. + * @param column The column whose prefix should be retrieved. + * @param orientation The orientation of the axis. + * @param fallback If true, the suffix for all columns is returned, when + * none is set for the selected column. + * @return The axis unit suffix. + */ + QString unitSuffix( int column, Qt::Orientation orientation, bool fallback = false ) const; + /** + * Retrieves the axis unit suffix. + * @param orientation The orientation of the axis. + * @return The axis unit suffix. + */ + QString unitSuffix( Qt::Orientation orientation ) const; + + /** + * Set whether data value labels are allowed to overlap. + * @param allow True means that overlapping labels are allowed. + */ + void setAllowOverlappingDataValueTexts( bool allow ); + + /** + * @return Whether data value labels are allowed to overlap. + */ + bool allowOverlappingDataValueTexts() const; + + /** + * Set whether anti-aliasing is to be used while rendering + * this diagram. + * @param enabled True means that AA is enabled. + */ + void setAntiAliasing( bool enabled ); + + /** + * @return Whether anti-aliasing is to be used for rendering + * this diagram. + */ + bool antiAliasing() const; + + /** + * Set the palette to be used, for painting datasets to the default + * palette. + * @see KDChart::Palette. + * FIXME: fold into one usePalette (KDChart::Palette&) method + */ + void useDefaultColors(); + + /** + * Set the palette to be used, for painting datasets to the rainbow + * palette. + * @see KDChart::Palette. + */ + void useRainbowColors(); + + /** + * Set the palette to be used, for painting datasets to the subdued + * palette. + * @see KDChart::Palette. + */ + void useSubduedColors(); + + /** + * The set of item row labels currently displayed, for use in Abscissa axes, etc. + * @return The set of item row labels currently displayed. + */ + QStringList itemRowLabels() const; + + /** + * The set of dataset labels currently displayed, for use in legends, etc. + * @return The set of dataset labels currently displayed. + */ + QStringList datasetLabels() const; + + /** + * The set of dataset brushes currently used, for use in legends, etc. + * + * @note Cell-level override brushes, if set, take precedence over the + * dataset values, so you might need to check these too, in order to find + * the brush, that is used for a single cell. + * + * @return The current set of dataset brushes. + */ + QList datasetBrushes() const; + + /** + * The set of dataset pens currently used, for use in legends, etc. + * + * @note Cell-level override pens, if set, take precedence over the + * dataset values, so you might need to check these too, in order to find + * the pens, that is used for a single cell. + * + * @return The current set of dataset pens. + */ + QList datasetPens() const; + + /** + * The set of dataset markers currently used, for use in legends, etc. + * + * @note Cell-level override markers, if set, take precedence over the + * dataset values, so you might need to check these too, in order to find + * the marker, that is shown for a single cell. + * + * @return The current set of dataset brushes. + */ + QList datasetMarkers() const; + + + /** + * \deprecated + * + * \brief Deprecated method that turns the percent mode of this diagram on or off. + * + * This method is deprecated. Use the setType() method of a supporting diagram implementation + * instead, e.g. BarDiagram::setType(). + * + * \see percentMode + */ + void setPercentMode( bool percent ); + + + /** + * \brief Returns whether this diagram is drawn in percent mode. + * + * If true, all data points in the same column of a diagram will + * be be drawn at the same X coordinate and stacked up so that the distance from the + * last data point (or the zero line) to a data point P is always the ratio of (Y-Value of P)/ + * (sum of all Y-Values in same column as P) relative to the diagrams height + * (or width, if abscissa and ordinate are swapped). + * + * Note that this property is not applicable to all diagram types. + */ + bool percentMode() const; + + virtual void paintMarker( QPainter* painter, + const MarkerAttributes& markerAttributes, + const QBrush& brush, const QPen&, + const QPointF& point, const QSizeF& size ); + + /** + * The dataset dimension of a diagram determines, how many value dimensions + * it expects each datapoint to have. + * For each dimension it will expect one column of values in the model. + * If the dimensionality is 1, automatic values will be used for the abscissa. + * + * For example a diagram with the default dimension of 1, will have one column + * per datapoint (the y values) and will use automatic values for the x axis + * (1, 2, 3, ... n). + * If the dimension is 2, the diagram will use the first, (and the third, + * fifth, etc) columns as X values, and the second, (and the fourth, sixth, + * etc) column as Y values. + * @return The dataset dimension of the diagram. + */ + int datasetDimension() const; + + /** + * \deprecated + * + * Sets the dataset dimension of the diagram. Using this method + * is deprecated. Use the specific diagram types instead. + */ + void setDatasetDimension( int dimension ); + + protected: + void setDatasetDimensionInternal( int dimension ); + + public: + void update() const; + + void paintMarker( QPainter* painter, const DataValueAttributes& a, + const QModelIndex& index, + const QPointF& pos ); + void paintMarker( QPainter* painter, + const QModelIndex& index, + const QPointF& pos ); + void paintDataValueText( QPainter* painter, const QModelIndex& index, + const QPointF& pos, double value ); + + // reverse mapping: + /** This method is added alongside with indexAt from QAIM, + since in kdchart multiple indexes can be displayed at the same + spot. */ + QModelIndexList indexesAt( const QPoint& point ) const; + + protected: + virtual bool checkInvariants( bool justReturnTheStatus=false ) const; + virtual const QPair calculateDataBoundaries() const = 0; + + protected Q_SLOTS: + void setDataBoundariesDirty() const; + + protected: + /** + * \deprecated + * This method is deprecated and provided for backward-compatibility only. + * Your own diagram classes should call + * d->paintDataValueTextsAndMarkers() instead + * which also is taking care for showing your cell-specific comments, if any, + */ + virtual void paintDataValueTexts( QPainter* painter ); + /** + * \deprecated + * This method is deprecated and provided for backward-compatibility only. + * Your own diagram classes should call + * d->paintDataValueTextsAndMarkers() instead + * which also is taking care for showing your cell-specific comments, if any, + */ + virtual void paintMarkers( QPainter* painter ); + void setAttributesModelRootIndex( const QModelIndex& ); + QModelIndex attributesModelRootIndex() const; + + /** + * Helper method, retrieving the data value (DisplayRole) for a given row and column + * @param row The row to query. + * @param column The column to query. + * @return The value of the display role at the given row and column as a double. + * @deprecated + */ + double valueForCell( int row, int column ) const; + + Q_SIGNALS: + /** Diagrams are supposed to emit this signal, when the layout of one + of their element changes. Layouts can change, for example, when + axes are added or removed, or when the configuration was changed + in a way that the axes or the diagram itself are displayed in a + different geometry. + Changes in the diagrams coordinate system also result + in the layoutChanged() signal being emitted. + */ + void layoutChanged( AbstractDiagram* ); + + /** + * This signal is emitted when this diagram is being destroyed, but before all the + * data, i.e. the attributes model, is invalidated. + */ + void aboutToBeDestroyed(); + + /** This signal is emitted, when either the model or the AttributesModel is replaced. */ + void modelsChanged(); + + /** This signal is emitted, when the model data is changed. */ + void modelDataChanged(); + + /** This signal is emitted, when the hidden status of at least one data cell was (un)set. */ + void dataHidden(); + + /** Emitted upon change of a property of the Diagram. */ + void propertiesChanged(); + + private: + QModelIndex conditionallyMapFromSource( const QModelIndex & sourceIndex ) const; + QString roundValues( double value, const int decimalPos, + const int decimalDigits ) const; + + }; + + typedef QList AbstractDiagramList; + typedef QList ConstAbstractDiagramList; + + /** + * @brief Internally used class just adding a special constructor used by AbstractDiagram + */ + class PrivateAttributesModel : public AttributesModel { + Q_OBJECT + public: + explicit PrivateAttributesModel( QAbstractItemModel* model, QObject * parent = 0 ) + : AttributesModel(model,parent) {} + }; +} + +#endif diff --git a/libkdchart/src/KDChartAbstractDiagram_p.h b/libkdchart/src/KDChartAbstractDiagram_p.h new file mode 100644 index 0000000..49158ce --- /dev/null +++ b/libkdchart/src/KDChartAbstractDiagram_p.h @@ -0,0 +1,546 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTDIAGRAM_P_H +#define KDCHARTABSTRACTDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractDiagram.h" +#include "KDChartAbstractCoordinatePlane.h" +#include "KDChartDataValueAttributes.h" +#include "KDChartBackgroundAttributes" +#include "KDChartRelativePosition.h" +#include "KDChartPosition.h" +#include "KDChartPainterSaver_p.h" +#include "KDChartPaintContext.h" +#include "KDChartPrintingParameters.h" +#include "KDChartChart.h" +#include +#include "Scenery/ReverseMapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace KDChart { + class AttributesModel; + + class DataValueTextInfo { + public: + DataValueTextInfo(){} + DataValueTextInfo( const QModelIndex& _index, const DataValueAttributes& _attrs, const QPointF& _pos, const QPointF& _markerPos, double _value ) + :index( _index), attrs( _attrs ), pos( _pos ), markerPos( _markerPos ), value( _value ) + {} + DataValueTextInfo( const DataValueTextInfo& other ) + :index( other.index ), attrs( other.attrs ), pos( other.pos ), markerPos( other.markerPos ), value( other.value ) {} + QModelIndex index; + DataValueAttributes attrs; + QPointF pos; + QPointF markerPos; + double value; + }; + + typedef QVector DataValueTextInfoList; + typedef QVectorIterator DataValueTextInfoListIterator; + + +/** + * \internal + */ + class KDChart::AbstractDiagram::Private + { + friend class AbstractDiagram; + public: + explicit Private(); + virtual ~Private(); + + Private( const Private& rhs ); + + void setAttributesModel( AttributesModel* ); + + bool usesExternalAttributesModel()const; + + // FIXME: Optimize if necessary + virtual qreal calcPercentValue( const QModelIndex & index ) + { + qreal sum = 0.0; + for ( int col = 0; col < attributesModel->columnCount( QModelIndex() ); col++ ) + sum += attributesModel->data( attributesModel->index( index.row(), col, QModelIndex() ) ).toDouble(); + if ( sum == 0.0 ) + return 0.0; + return attributesModel->data( attributesModel->mapFromSource( index ) ).toDouble() / sum * 100.0; + } + + void appendDataValueTextInfoToList( + AbstractDiagram * diagram, + DataValueTextInfoList & list, + const QModelIndex & index, + const CartesianDiagramDataCompressor::CachePosition * position, + const PositionPoints& points, + const Position& autoPositionPositive, + const Position& autoPositionNegative, + const qreal value ) + { + CartesianDiagramDataCompressor::DataValueAttributesList allAttrs( aggregatedAttrs( diagram, index, position ) ); + QMap::const_iterator i; + for (i = allAttrs.constBegin(); i != allAttrs.constEnd(); ++i){ + if( i.value().isVisible() ){ + const bool bValueIsPositive = (value >= 0.0); + RelativePosition relPos( i.value().position( bValueIsPositive ) ); + relPos.setReferencePoints( points ); + if( relPos.referencePosition().isUnknown() ) + relPos.setReferencePosition( bValueIsPositive ? autoPositionPositive : autoPositionNegative ); + + const QPointF referencePoint = relPos.referencePoint(); + if( diagram->coordinatePlane()->isVisiblePoint( referencePoint ) ){ + const qreal fontHeight = cachedFontMetrics( i.value().textAttributes(). + calculatedFont( plane, KDChartEnums::MeasureOrientationMinimum ), diagram )->height(); + // Note: When printing data value texts the font height is used as reference size for both, + // horizontal and vertical padding, if the respective padding's Measure is using + // automatic reference area detection. + QSizeF relativeMeasureSize( fontHeight, fontHeight ); + //qDebug()<<"fontHeight"<= decimalDigits ; --i ) { + count += 1; + int lastval = QString( digits.data() [i] ).toInt(); + int val = QString( digits.data() [i-1] ) .toInt(); + if ( lastval >= 5 ) { + val += 1; + digits.replace( digits.length() - count,1 , QString::number( val ) ); + } + } + + digits.truncate( decimalDigits ); + num.append( QLatin1Char( '.' ) + digits ); + + return num; + + } + + void clearListOfAlreadyDrawnDataValueTexts() + { + alreadyDrawnDataValueTexts.clear(); + } + + void paintDataValueTextsAndMarkers( AbstractDiagram* diag, + PaintContext* ctx, + const DataValueTextInfoList & list, + bool paintMarkers, + bool justCalculateRect=false, + QRectF* cumulatedBoundingRect=0 ) + { + const PainterSaver painterSaver( ctx->painter() ); + ctx->painter()->setClipping( false ); + if( paintMarkers && ! justCalculateRect ) + { + DataValueTextInfoListIterator it( list ); + while ( it.hasNext() ) { + const DataValueTextInfo& info = it.next(); + diag->paintMarker( ctx->painter(), info.index, info.markerPos ); + } + } + DataValueTextInfoListIterator it( list ); + + Measure m( 18.0, KDChartEnums::MeasureCalculationModeRelative, + KDChartEnums::MeasureOrientationMinimum ); + m.setReferenceArea( ctx->coordinatePlane() ); + TextAttributes ta; + ta.setFontSize( m ); + m.setAbsoluteValue( 6.0 ); + ta.setMinimalFontSize( m ); + clearListOfAlreadyDrawnDataValueTexts(); + while ( it.hasNext() ) { + const DataValueTextInfo& info = it.next(); + paintDataValueText( diag, ctx->painter(), info.index, info.pos, info.value, + justCalculateRect, + cumulatedBoundingRect ); + + const QString comment = info.index.data( KDChart::CommentRole ).toString(); + if( comment.isEmpty() ) + continue; + TextBubbleLayoutItem item( comment, + ta, + ctx->coordinatePlane()->parent(), + KDChartEnums::MeasureOrientationMinimum, + Qt::AlignHCenter|Qt::AlignVCenter ); + const QRect rect( info.pos.toPoint(), item.sizeHint() ); + if( justCalculateRect && cumulatedBoundingRect ){ + (*cumulatedBoundingRect) |= rect; + }else{ + item.setGeometry( rect ); + item.paint( ctx->painter() ); + + // Return the cumulatedBoundingRect if asked for + if(cumulatedBoundingRect) + (*cumulatedBoundingRect) |= rect; + } + } + } + + + void paintDataValueText( const AbstractDiagram* diag, + QPainter* painter, + const QModelIndex& index, + const QPointF& pos, + double value, + bool justCalculateRect=false, + QRectF* cumulatedBoundingRect=0 ) + { + const DataValueAttributes a( diag->dataValueAttributes( index ) ); + + if ( !a.isVisible() ) return; + + if ( a.usePercentage() ) + value = calcPercentValue( index ); + + // handle decimal digits + int decimalDigits = a.decimalDigits(); + int decimalPos = QString::number( value ).indexOf( QLatin1Char( '.' ) ); + QString roundedValue; + if ( a.dataLabel().isNull() ){ + if ( decimalPos > 0 && value != 0 ) + roundedValue = roundValues ( value, decimalPos, decimalDigits ); + else + roundedValue = QString::number( value ); + }else{ + roundedValue = a.dataLabel(); + } + + // handle prefix and suffix + if ( !a.prefix().isNull() ) + roundedValue.prepend( a.prefix() ); + + if ( !a.suffix().isNull() ) + roundedValue.append( a.suffix() ); + + paintDataValueText( diag, painter, a, pos, roundedValue, value >= 0.0, + justCalculateRect, cumulatedBoundingRect ); + } + + + void paintDataValueText( const AbstractDiagram* diag, + QPainter* painter, + const DataValueAttributes& attrs, + const QPointF& pos, + QString text, + bool valueIsPositive, + bool justCalculateRect=false, + QRectF* cumulatedBoundingRect=0 ) + { + if ( !attrs.isVisible() ) return; + + const TextAttributes ta( attrs.textAttributes() ); + if ( ta.isVisible() ) { + /* for debugging: + PainterSaver painterSaver( painter ); + painter->setPen( Qt::black ); + painter->drawLine( pos - QPointF( 2,2), pos + QPointF( 2,2) ); + painter->drawLine( pos - QPointF(-2,2), pos + QPointF(-2,2) ); + */ + + QTextDocument doc; + if( Qt::mightBeRichText( text ) ) + doc.setHtml( text ); + else + doc.setPlainText( text ); + + const RelativePosition relPos( attrs.position( valueIsPositive ) ); + //const Qt::Alignment alignBottomLeft = Qt::AlignBottom | Qt::AlignLeft; + const QFont calculatedFont( ta.calculatedFont( plane, KDChartEnums::MeasureOrientationMinimum ) ); + + // note: We can not use boundingRect() to retrieve the width, as that returnes a too small value + const QSizeF plainSize( + cachedFontMetrics( calculatedFont, painter->device() )->width( doc.toPlainText() ), + cachedFontMetrics( calculatedFont, painter->device() )->boundingRect( doc.toPlainText() ).height() ); + + // FIXME draw the non-text bits, background, etc + + if ( attrs.showRepetitiveDataLabels() || pos.x() <= lastX || lastRoundedValue != text ) { + //qDebug() << text; + + //Check if there is only one and only one pie. + //If not then update lastRoundedValue for further checking. + if(!(diag->model()->rowCount() == 1)) + lastRoundedValue = text; + + lastX = pos.x(); + const PainterSaver painterSaver( painter ); + painter->setPen( PrintingParameters::scalePen( ta.pen() ) ); + + doc.setDefaultFont( calculatedFont ); + QAbstractTextDocumentLayout::PaintContext context; + context.palette = diag->palette(); + context.palette.setColor(QPalette::Text, ta.pen().color() ); + + BackgroundAttributes back(attrs.backgroundAttributes()); + if(back.isVisible()) + { + QTextBlockFormat fmt; + fmt.setBackground(back.brush()); + QTextCursor cursor(&doc); + cursor.setPosition(0); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor, 1); + cursor.mergeBlockFormat(fmt); + } + + QAbstractTextDocumentLayout* const layout = doc.documentLayout(); + + painter->translate( pos ); + painter->rotate( ta.rotation() ); + qreal dx = 0.0; + qreal dy = 0.0; + const Qt::Alignment alignTopLeft = (Qt::AlignLeft | Qt::AlignTop); + if( (relPos.alignment() & alignTopLeft) != alignTopLeft ){ + if( relPos.alignment() & Qt::AlignRight ) + dx = - plainSize.width(); + else if( relPos.alignment() & Qt::AlignHCenter ) + dx = - 0.5 * plainSize.width(); + + if( relPos.alignment() & Qt::AlignBottom ) + dy = - plainSize.height(); + else if( relPos.alignment() & Qt::AlignVCenter ) + dy = - 0.5 * plainSize.height(); + } + + bool drawIt = true; + // note: This flag can be set differently for every label text! + // In theory a user could e.g. have some small red text on one of the + // values that she wants to have written in any case - so we just + // do not test if such texts would cover some of the others. + if( ! attrs.showOverlappingDataLabels() ){ + const QRectF br( layout->blockBoundingRect( doc.begin() ) ); + qreal radRot = DEGTORAD( - ((ta.rotation() < 0) ? ta.rotation()+360 : ta.rotation()) ); + //qDebug() << radRot; + qreal cosRot = cos( radRot ); + qreal sinRot = sin( radRot ); + QPolygon pr( br.toRect(), true ); + // YES, people, the following stuff NEEDS to be done that way! + // Otherwise we will not get the texts' individual rotation + // and/or the shifting of the texts correctly. + // Just believe me - I did tests .. :-) (khz, 2008-02-19) + for( int i=0; i(pos.x() + x*cosRot + y*sinRot), + static_cast(pos.y() - x*sinRot + y*cosRot)); + } + KDAB_FOREACH( QPolygon oldPoly, alreadyDrawnDataValueTexts ) { + if( ! oldPoly.intersected( pr ).isEmpty() ) + drawIt = false; + } + if( drawIt ) + alreadyDrawnDataValueTexts << pr; + } + if( drawIt ){ + QRectF rect = layout->frameBoundingRect(doc.rootFrame()); + rect.moveTo(pos.x()+dx, pos.y()+dy); + + if( justCalculateRect && cumulatedBoundingRect ){ + (*cumulatedBoundingRect) |= rect; + }else{ + painter->translate( QPointF( dx, dy ) ); + layout->draw( painter, context ); + + // Return the cumulatedBoundingRect if asked for + if(cumulatedBoundingRect) + (*cumulatedBoundingRect) |= rect; + } + } + } + + } + } + + virtual QModelIndex indexAt( const QPoint& point ) const + { + QModelIndexList l = indexesAt( point ); + qSort( l ); + if ( !l.isEmpty() ) + return l.first(); + else + return QModelIndex(); + } + + QModelIndexList indexesAt( const QPoint& point ) const + { + return reverseMapper.indexesAt( point ); // which could be empty + } + + QModelIndexList indexesIn( const QRect& rect ) const + { + return reverseMapper.indexesIn( rect ); + } + + virtual CartesianDiagramDataCompressor::DataValueAttributesList aggregatedAttrs( + AbstractDiagram * diagram, + const QModelIndex & index, + const CartesianDiagramDataCompressor::CachePosition * position ) const + { + Q_UNUSED( position ); // used by cartesian diagrams only + CartesianDiagramDataCompressor::DataValueAttributesList allAttrs; + allAttrs[index] = diagram->dataValueAttributes( index ); + return allAttrs; + } + + /** + * Sets arbitrary attributes of a data set. + */ + void setDatasetAttrs( int dataset, QVariant data, DisplayRoles role ) + { + // To store attributes for a dataset, we use the first column + // that's associated with it. (i.e., with a dataset dimension + // of two, the column of the keys). In most cases however, there's + // only one data dimension, and thus also only one column per data set. + int column = dataset * datasetDimension; + attributesModel->setHeaderData( column, Qt::Horizontal, data, role ); + } + + /** + * Retrieves arbitrary attributes of a data set. + */ + QVariant datasetAttrs( int dataset, DisplayRoles role ) const + { + // See setDataSetAttrs for explanation of column + int column = dataset * datasetDimension; + return attributesModel->headerData( column, Qt::Horizontal, role ); + } + + /** + * Resets an attribute of a dataset back to its default. + */ + void resetDatasetAttrs( int dataset, DisplayRoles role ) + { + // See setDataSetAttrs for explanation of column + int column = dataset * datasetDimension; + attributesModel->resetHeaderData( column, Qt::Horizontal, role ); + } + + protected: + void init(); + void init( AbstractCoordinatePlane* plane ); + QPointer plane; + mutable QModelIndex attributesModelRootIndex; + QPointer attributesModel; + bool allowOverlappingDataValueTexts; + bool antiAliasing; + bool percent; + int datasetDimension; + mutable QPair databoundaries; + mutable bool databoundariesDirty; + ReverseMapper reverseMapper; + + QMap< Qt::Orientation, QString > unitSuffix; + QMap< Qt::Orientation, QString > unitPrefix; + QMap< int, QMap< Qt::Orientation, QString > > unitSuffixMap; + QMap< int, QMap< Qt::Orientation, QString > > unitPrefixMap; + QList< QPolygon > alreadyDrawnDataValueTexts; + + private: + QString lastRoundedValue; + qreal lastX; + QFontMetrics mCachedFontMetrics; + QFont mCachedFont; + QPaintDevice * mCachedPaintDevice; + }; + + inline AbstractDiagram::AbstractDiagram( Private * p ) : _d( p ) + { + init(); + } + inline AbstractDiagram::AbstractDiagram( + Private * p, QWidget* parent, AbstractCoordinatePlane* plane ) + : QAbstractItemView( parent ), _d( p ) + { + _d->init( plane ); + init(); + } + + + class LineAttributesInfo { + public : + LineAttributesInfo() {} + LineAttributesInfo( const QModelIndex _index, const QPointF& _value, const QPointF& _nextValue ) + :index( _index ), value ( _value ), nextValue ( _nextValue ) {} + + QModelIndex index; + QPointF value; + QPointF nextValue; + }; + + typedef QVector LineAttributesInfoList; + typedef QVectorIterator LineAttributesInfoListIterator; + +} +#endif /* KDCHARTDIAGRAM_P_H */ diff --git a/libkdchart/src/KDChartAbstractGrid.cpp b/libkdchart/src/KDChartAbstractGrid.cpp new file mode 100644 index 0000000..0b52b4e --- /dev/null +++ b/libkdchart/src/KDChartAbstractGrid.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractGrid.h" +#include "KDChartPaintContext.h" + +#include + +#include + + +using namespace KDChart; +using namespace std; + + +static qreal _trunc( qreal v ) +{ + return (( v > 0.0 ) ? floor( v ) : ceil( v )); +} + + +AbstractGrid::AbstractGrid () + : mPlane( 0 ) +{ + //this bloc left empty intentionally +} + +AbstractGrid::~AbstractGrid() +{ + //this bloc left empty intentionally +} + +void AbstractGrid::setNeedRecalculate() +{ + mCachedRawDataDimensions.clear(); +} + +DataDimensionsList AbstractGrid::updateData( AbstractCoordinatePlane* plane ) +{ + + if( plane ){ + const DataDimensionsList rawDataDimensions( plane->getDataDimensionsList() ); + if( mCachedRawDataDimensions.empty() || (rawDataDimensions != mCachedRawDataDimensions) ){ + mCachedRawDataDimensions = rawDataDimensions; + mPlane = plane; + mData = calculateGrid( rawDataDimensions ); + //qDebug("AbstractGrid::updateData() returns mData.first().start: %f mData.first().end: %f mData.first().stepWidth: %f", mData.first().start, mData.first().end, mData.first().stepWidth); + } + } + return mData; +} + +bool AbstractGrid::isBoundariesValid(const QRectF& r ) +{ + return isBoundariesValid( + QPair(r.topLeft(), r.bottomRight()) ); +} + +bool AbstractGrid::isBoundariesValid(const QPair& b ) +{ + return isValueValid( b.first.x() ) && isValueValid( b.first.y() ) && + isValueValid( b.second.x() ) && isValueValid( b.second.y() ); +} + +bool AbstractGrid::isBoundariesValid(const DataDimensionsList& l ) +{ + for (int i = 0; i < l.size(); ++i) + if ( ! isValueValid( l.at(i).start ) || ! isValueValid( l.at(i).end ) ) + return false; + return true; +} + +bool AbstractGrid::isValueValid(const qreal& r ) +{ + return !(ISNAN(r) || ISINF(r)); +} + +void AbstractGrid::adjustLowerUpperRange( + qreal& start, qreal& end, + qreal stepWidth, + bool adjustLower, bool adjustUpper ) +{ + const qreal startAdjust = ( start >= 0.0 ) ? 0.0 : -1.0; + const qreal endAdjust = ( end >= 0.0 ) ? 1.0 : 0.0; + if ( adjustLower && (fmod( start, stepWidth ) != 0.0) ) + start = stepWidth * (_trunc( start / stepWidth ) + startAdjust); + if ( adjustUpper && (fmod( end, stepWidth ) != 0.0) ) + end = stepWidth * (_trunc( end / stepWidth ) + endAdjust); +} + +const DataDimension AbstractGrid::adjustedLowerUpperRange( + const DataDimension& dim, + bool adjustLower, bool adjustUpper ) +{ + DataDimension result( dim ); + adjustLowerUpperRange( + result.start, result.end, + result.stepWidth, + adjustLower, adjustUpper ); + return result; +} diff --git a/libkdchart/src/KDChartAbstractGrid.h b/libkdchart/src/KDChartAbstractGrid.h new file mode 100644 index 0000000..5ec2685 --- /dev/null +++ b/libkdchart/src/KDChartAbstractGrid.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTGRID_H +#define KDCHARTABSTRACTGRID_H + +#include + +#include "KDChartAbstractCoordinatePlane.h" +#include "KDChartGridAttributes.h" +#include "KDChartAbstractDiagram.h" +#include "KDChartCartesianAxis.h" + +namespace KDChart { + + class PaintContext; + + + /** + * \internal + * + * \brief Abstract base class for grid classes: cartesian, polar, ... + * + * The AbstractGrid interface is the base class used by + * AbstractCoordinatePlane, for calculating and for drawing + * the grid lines of the plane. + */ + class AbstractGrid + { + public: + virtual ~AbstractGrid(); + protected: + AbstractGrid (); + + + public: + /** \brief Returns the cached result of data calculation. + * + * For this, all derived classes need to implement the + * pure-virtual calculateGrid() method. + */ + DataDimensionsList updateData( AbstractCoordinatePlane* plane ); + + /** + * Doing the actual drawing. + * + * Every derived class must implement this. + * + * \note When implementing drawGrid(): Before you start drawing, + * make sure to call updateData(), to get the data boundaries + * recalculated. + * For an example, see the implementation of CartesianGrid:drawGrid(). + */ + virtual void drawGrid( PaintContext* context ) = 0; + + /** + * Causes grid to be recalculated upon the next call + * of updateData(). + * + * \see calculateGrid + */ + void setNeedRecalculate(); + + /** + * Checks whether both coordinates of r are valid according + * to isValueValid + * + * \see isValueValid + */ + static bool isBoundariesValid(const QRectF& r ); + + /** + * Checks whether both coordinates of both points are valid + * according to isValueValid + * + * \see isValueValid + */ + static bool isBoundariesValid(const QPair& b ); + + /** + * Checks whether all start and end properties of every + * DataDimension in the list l are valid according to + * isValueValid(). + * + * \see isValueValid + */ + static bool isBoundariesValid(const DataDimensionsList& l ); + + /** + * Checks if r is neither NaN nor infinity. + */ + static bool isValueValid(const qreal& r ); + + /** + * Adjusts \a start and/or \a end so that they are a multiple of + * \a stepWidth + */ + static void adjustLowerUpperRange( + qreal& start, qreal& end, + qreal stepWidth, + bool adjustLower, bool adjustUpper ); + + /** + * Adjusts \a dim so that \c dim.start and/or \c dim.end are a multiple + * of \c dim.stepWidth. + * + * \see adjustLowerUpperRange + */ + static const DataDimension adjustedLowerUpperRange( + const DataDimension& dim, + bool adjustLower, bool adjustUpper ); + + GridAttributes gridAttributes; + + protected: + DataDimensionsList mData; + AbstractCoordinatePlane* mPlane; + + private: + /** + * \brief Calculates the grid start/end/step width values. + * + * Gets the raw data dimensions - e.g. the data model's boundaries, + * together with their isCalculated flags. + * + * Returns the calculated start/end values for the grid, and their + * respective step widths. + * If at least one of the step widths is Zero, all dimensions of + * the returned list are considered invalid! + * + * \note This function needs to be implemented by all derived classes, + * like CartesianGrid, PolarGrid, ... + */ + virtual DataDimensionsList calculateGrid( const DataDimensionsList& rawDataDimensions ) const = 0; + DataDimensionsList mCachedRawDataDimensions; + }; + +} + +#endif diff --git a/libkdchart/src/KDChartAbstractPieDiagram.cpp b/libkdchart/src/KDChartAbstractPieDiagram.cpp new file mode 100644 index 0000000..434eef2 --- /dev/null +++ b/libkdchart/src/KDChartAbstractPieDiagram.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractPieDiagram.h" +#include "KDChartAbstractPieDiagram_p.h" + +#include "KDChartAttributesModel.h" +#include "KDChartPieAttributes.h" +#include "KDChartThreeDPieAttributes.h" + +#include + +#include + + +using namespace KDChart; + +AbstractPieDiagram::Private::Private() : + granularity( 1.0 ) +{ +} + +AbstractPieDiagram::Private::~Private() {} + +AbstractPieDiagram::AbstractPieDiagram( QWidget* parent, PolarCoordinatePlane *plane ) : + AbstractPolarDiagram( new Private(), parent, plane ) +{ + init(); +} + +AbstractPieDiagram::~AbstractPieDiagram() +{ +} + + +void AbstractPieDiagram::init() +{ +} + + +bool AbstractPieDiagram::compare( const AbstractPieDiagram* other )const +{ + if( other == this ) return true; + if( ! other ){ + //qDebug() << "AbstractPieDiagram::compare() cannot compare to Null pointer"; + return false; + } + /* + qDebug() << "\n AbstractPieDiagram::compare():"; + // compare own properties + qDebug() << + (granularity() == other->granularity()) && + (startPosition() == other->startPosition()); + */ + return // compare the base class + ( static_cast(this)->compare( other ) ) && + // compare own properties + (granularity() == other->granularity()) && + (startPosition() == other->startPosition()); +} + + +#define d d_func() + +void AbstractPieDiagram::setGranularity( qreal value ) +{ + d->granularity = value; +} + +qreal AbstractPieDiagram::granularity() const +{ + return (d->granularity < 0.05 || d->granularity > 36.0) + ? 1.0 + : d->granularity; +} + + +void AbstractPieDiagram::setStartPosition( int degrees ) +{ + Q_UNUSED( degrees ); + qWarning() << "Deprecated AbstractPieDiagram::setStartPosition() called, setting ignored."; +} + +int AbstractPieDiagram::startPosition() const +{ + qWarning() << "Deprecated AbstractPieDiagram::startPosition() called."; + return 0; +} + +void AbstractPieDiagram::setPieAttributes( const PieAttributes & attrs ) +{ + d->attributesModel->setModelData( qVariantFromValue( attrs ), PieAttributesRole ); + emit layoutChanged( this ); +} + +void AbstractPieDiagram::setPieAttributes( int column, const PieAttributes & attrs ) +{ + d->setDatasetAttrs( column, qVariantFromValue( attrs ), PieAttributesRole ); + emit layoutChanged( this ); +} + +void AbstractPieDiagram::setPieAttributes( const QModelIndex & index, const PieAttributes & attrs ) +{ + d->attributesModel->setData( index, qVariantFromValue( attrs), PieAttributesRole ); + emit layoutChanged( this ); +} + +// Note: Our users NEED this method - even if +// we do not need it at drawing time! +// (khz, 2006-07-28) +PieAttributes AbstractPieDiagram::pieAttributes() const +{ + return qVariantValue( + d->attributesModel->data( PieAttributesRole ) ); +} + +// Note: Our users NEED this method - even if +// we do not need it at drawing time! +// (khz, 2006-07-28) +PieAttributes AbstractPieDiagram::pieAttributes( int column ) const +{ + const QVariant attrs( d->datasetAttrs( column, PieAttributesRole ) ); + if( attrs.isValid() ) + return qVariantValue< PieAttributes >( attrs ); + return pieAttributes(); +} + +PieAttributes AbstractPieDiagram::pieAttributes( const QModelIndex & index ) const +{ + return qVariantValue( + d->attributesModel->data( + d->attributesModel->mapFromSource( index ), + PieAttributesRole ) ); +} + + +void AbstractPieDiagram::setThreeDPieAttributes( const ThreeDPieAttributes & tda ) +{ + d->attributesModel->setModelData( qVariantFromValue( tda ), ThreeDPieAttributesRole ); + emit layoutChanged( this ); +} + +void AbstractPieDiagram::setThreeDPieAttributes( int column, const ThreeDPieAttributes & tda ) +{ + d->setDatasetAttrs( column, qVariantFromValue( tda ), ThreeDPieAttributesRole ); + emit layoutChanged( this ); +} + +void AbstractPieDiagram::setThreeDPieAttributes( const QModelIndex & index, const ThreeDPieAttributes & tda ) +{ + model()->setData( index, qVariantFromValue( tda ), ThreeDPieAttributesRole ); + emit layoutChanged( this ); +} + +// Note: Our users NEED this method - even if +// we do not need it at drawing time! +// (khz, 2006-07-28) +ThreeDPieAttributes AbstractPieDiagram::threeDPieAttributes() const +{ + return qVariantValue( + d->attributesModel->data( ThreeDPieAttributesRole ) ); +} + +// Note: Our users NEED this method - even if +// we do not need it at drawing time! +// (khz, 2006-07-28) +ThreeDPieAttributes AbstractPieDiagram::threeDPieAttributes( int column ) const +{ + const QVariant attrs( d->datasetAttrs( column, ThreeDPieAttributesRole ) ); + if( attrs.isValid() ) + return qVariantValue< ThreeDPieAttributes >( attrs ); + return threeDPieAttributes(); +} + +ThreeDPieAttributes AbstractPieDiagram::threeDPieAttributes( const QModelIndex & index ) const +{ + return qVariantValue( + d->attributesModel->data( + d->attributesModel->mapFromSource( index ), + ThreeDPieAttributesRole ) ); +} + diff --git a/libkdchart/src/KDChartAbstractPieDiagram.h b/libkdchart/src/KDChartAbstractPieDiagram.h new file mode 100644 index 0000000..d380d7c --- /dev/null +++ b/libkdchart/src/KDChartAbstractPieDiagram.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTPIEDIAGRAM_H +#define KDCHARTABSTRACTPIEDIAGRAM_H + +#include "KDChartAbstractPolarDiagram.h" + +namespace KDChart { + class PieAttributes; + class ThreeDPieAttributes; + +/** + * @brief Base class for any diagram type + */ +class KDCHART_EXPORT AbstractPieDiagram : public AbstractPolarDiagram +{ + Q_OBJECT + + Q_DISABLE_COPY( AbstractPieDiagram ) + KDCHART_DECLARE_DERIVED_DIAGRAM( AbstractPieDiagram, PolarCoordinatePlane ) + +public: + explicit AbstractPieDiagram( + QWidget* parent = 0, PolarCoordinatePlane* plane = 0 ); + virtual ~AbstractPieDiagram(); + + /** + * Returns true if both diagrams have the same settings. + */ + bool compare( const AbstractPieDiagram* other )const; + + /** Set the granularity: the smaller the granularity the more your diagram + * segments will show facettes instead of rounded segments. + * \param value the granularity value between 0.05 (one twentieth of a degree) + * and 36.0 (one tenth of a full circle), other values will be interpreted as 1.0. + */ + void setGranularity( qreal value ); + + /** @return the granularity. */ + qreal granularity() const; + + /** \deprecated Use PolarCoordinatePlane::setStartPosition( qreal degrees ) instead. */ + void setStartPosition( int degrees ); + /** \deprecated Use qreal PolarCoordinatePlane::startPosition instead. */ + int startPosition() const; + + void setPieAttributes( const PieAttributes & a ); + void setPieAttributes( int column, + const PieAttributes & a ); + void setPieAttributes( const QModelIndex & index, + const PieAttributes & a ); + PieAttributes pieAttributes() const; + PieAttributes pieAttributes( int column ) const; + PieAttributes pieAttributes( const QModelIndex & index ) const; + + void setThreeDPieAttributes( const ThreeDPieAttributes & a ); + void setThreeDPieAttributes( int column, + const ThreeDPieAttributes & a ); + void setThreeDPieAttributes( const QModelIndex & index, + const ThreeDPieAttributes & a ); + ThreeDPieAttributes threeDPieAttributes() const; + ThreeDPieAttributes threeDPieAttributes( int column ) const; + ThreeDPieAttributes threeDPieAttributes( const QModelIndex & index ) const; +}; // End of class KDChartAbstractPieDiagram + +} + +#endif // KDCHARTABSTACTPIEDIAGRAM_H diff --git a/libkdchart/src/KDChartAbstractPieDiagram_p.h b/libkdchart/src/KDChartAbstractPieDiagram_p.h new file mode 100644 index 0000000..05adf93 --- /dev/null +++ b/libkdchart/src/KDChartAbstractPieDiagram_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTPIEDIAGRAM_P_H +#define KDCHARTABSTRACTPIEDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractPolarDiagram_p.h" +#include + +#include + + +namespace KDChart { + +class PolarCoordinatePlane; + +/** + * \internal + */ +class AbstractPieDiagram::Private : public AbstractPolarDiagram::Private +{ + friend class AbstractPieDiagram; +public: + Private(); + ~Private(); + + Private( const Private& rhs ) : + AbstractPolarDiagram::Private( rhs ), + granularity( rhs.granularity ) + { + } + +private: + double granularity; +}; + +KDCHART_IMPL_DERIVED_DIAGRAM( AbstractPieDiagram, AbstractPolarDiagram, PolarCoordinatePlane ) + +} +#endif /* KDCHARTABSTRACTPIEDIAGRAM_P_H */ + diff --git a/libkdchart/src/KDChartAbstractPolarDiagram.cpp b/libkdchart/src/KDChartAbstractPolarDiagram.cpp new file mode 100644 index 0000000..0e30e50 --- /dev/null +++ b/libkdchart/src/KDChartAbstractPolarDiagram.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractPolarDiagram.h" +#include "KDChartAbstractPolarDiagram_p.h" + +#include + + +using namespace KDChart; + +AbstractPolarDiagram::Private::Private() +{ +} + +AbstractPolarDiagram::Private::~Private() +{ +} + +void AbstractPolarDiagram::init() +{ +} + +#define d d_func() + +AbstractPolarDiagram::AbstractPolarDiagram ( + QWidget* parent, PolarCoordinatePlane* plane ) + : AbstractDiagram ( new Private(), parent, plane ) +{ +} + + +const PolarCoordinatePlane * AbstractPolarDiagram::polarCoordinatePlane() const +{ + return dynamic_cast( coordinatePlane() ); +} + +int AbstractPolarDiagram::columnCount() const +{ + return static_cast( numberOfValuesPerDataset() ); +} + +int AbstractPolarDiagram::rowCount() const +{ + return static_cast( numberOfDatasets() ); +} diff --git a/libkdchart/src/KDChartAbstractPolarDiagram.h b/libkdchart/src/KDChartAbstractPolarDiagram.h new file mode 100644 index 0000000..3ecb12d --- /dev/null +++ b/libkdchart/src/KDChartAbstractPolarDiagram.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTPOLARDIAGRAM_H +#define KDCHARTABSTRACTPOLARDIAGRAM_H + +#include "KDChartPolarCoordinatePlane.h" +#include "KDChartAbstractDiagram.h" + +namespace KDChart { + + class GridAttributes; + + /** + * @brief Base class for diagrams based on a polar coordinate system. + */ + class KDCHART_EXPORT AbstractPolarDiagram : public AbstractDiagram + { + Q_OBJECT + Q_DISABLE_COPY( AbstractPolarDiagram ) + KDCHART_DECLARE_DERIVED_DIAGRAM( AbstractPolarDiagram, PolarCoordinatePlane ) + + public: + explicit AbstractPolarDiagram ( + QWidget* parent = 0, PolarCoordinatePlane* plane = 0 ); + virtual ~AbstractPolarDiagram() {} + + virtual double valueTotals () const = 0; + virtual double numberOfValuesPerDataset() const = 0; + virtual double numberOfDatasets() const { return 1; }; + virtual double numberOfGridRings() const = 0; + + const PolarCoordinatePlane * polarCoordinatePlane() const; + + int columnCount() const; + int rowCount() const; + }; + +} + +#endif diff --git a/libkdchart/src/KDChartAbstractPolarDiagram_p.h b/libkdchart/src/KDChartAbstractPolarDiagram_p.h new file mode 100644 index 0000000..2484141 --- /dev/null +++ b/libkdchart/src/KDChartAbstractPolarDiagram_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTPOLARDIAGRAM_P_H +#define KDCHARTABSTRACTPOLARDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractDiagram_p.h" +#include + +#include + + +namespace KDChart { + + class PolarCoordinatePlane; + +/** + * \internal + */ +class AbstractPolarDiagram::Private : public AbstractDiagram::Private +{ + friend class AbstractPolarDiagram; +public: + Private(); + ~Private(); + + Private( const Private& rhs ) : + AbstractDiagram::Private( rhs ) + { + // just for consistency + } + + /** \reimpl */ + // FIXME: Optimize when needed + virtual qreal calcPercentValue( const QModelIndex & index ) + { + qreal sum = 0.0; + for ( int row = 0; row < attributesModel->rowCount( QModelIndex() ); row++ ) + sum += attributesModel->data( attributesModel->index( row, index.column(), QModelIndex() ) ).toDouble(); + if ( sum == 0.0 ) + return 0.0; + return attributesModel->data( attributesModel->mapFromSource( index ) ).toDouble() / sum * 100.0; + } + +private: + double granularity; +}; + +KDCHART_IMPL_DERIVED_DIAGRAM( AbstractPolarDiagram, AbstractDiagram, PolarCoordinatePlane ) +/* +inline AbstractPolarDiagram::AbstractPolarDiagram( Private * p ) + : AbstractDiagram( p ) { init(); } +inline AbstractPolarDiagram::AbstractPolarDiagram( + Private *p, QWidget* parent, PolarCoordinatePlane* plane ) + : AbstractDiagram( p, parent, plane ) { init(); } +inline AbstractPolarDiagram::Private * AbstractPolarDiagram::d_func() +{ return static_cast( AbstractDiagram::d_func() ); } +inline const AbstractPolarDiagram::Private * AbstractPolarDiagram::d_func() const +{ return static_cast( AbstractDiagram::d_func() ); } +*/ + +} +#endif /* KDCHARTABSTRACTCARTESIANDIAGRAM_P_H */ + diff --git a/libkdchart/src/KDChartAbstractProxyModel.cpp b/libkdchart/src/KDChartAbstractProxyModel.cpp new file mode 100644 index 0000000..6f9594b --- /dev/null +++ b/libkdchart/src/KDChartAbstractProxyModel.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractProxyModel.h" + +#include + +#include + + +namespace KDChart { + + /** This is basically KDAbstractProxyModel, but only the + bits that we really need from it */ +AbstractProxyModel::AbstractProxyModel(QObject* parent) + : QAbstractProxyModel(parent) {} + +// Think this is ugly? Well, it's not from me, it comes from QProxyModel +struct KDPrivateModelIndex +{ + int r, c; + void *p; + const QAbstractItemModel *m; +}; + +QModelIndex AbstractProxyModel::mapFromSource( const QModelIndex & sourceIndex ) const +{ + if ( !sourceIndex.isValid() ) + return QModelIndex(); + //qDebug() << "sourceIndex.model()="< + +#include + +#define d d_func() + + +using namespace KDChart; + + +AbstractThreeDAttributes::Private::Private() + : enabled( false ), + depth( 20 ) +{ +} + + +AbstractThreeDAttributes::AbstractThreeDAttributes() + : _d( new Private() ) +{ +} + +AbstractThreeDAttributes::AbstractThreeDAttributes( const AbstractThreeDAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +AbstractThreeDAttributes& AbstractThreeDAttributes::operator= ( const AbstractThreeDAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +AbstractThreeDAttributes::~AbstractThreeDAttributes() +{ + delete _d; _d = 0; +} + + +bool AbstractThreeDAttributes::operator==( const AbstractThreeDAttributes& r ) const +{ + if( isEnabled() == r.isEnabled() && + depth() == r.depth() ) + return true; + else + return false; +} + + +void AbstractThreeDAttributes::init( ) +{ + +} + +void AbstractThreeDAttributes::setEnabled( bool enabled ) +{ + d->enabled = enabled; +} + +bool AbstractThreeDAttributes::isEnabled() const +{ + return d->enabled; +} + +void AbstractThreeDAttributes::setDepth( double depth ) +{ + d->depth = depth; +} + + +double AbstractThreeDAttributes::depth() const +{ + return d->depth; +} + + +double AbstractThreeDAttributes::validDepth() const +{ + return isEnabled() ? d->depth : 0.0; +} + + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::AbstractThreeDAttributes& a) +{ + dbg << "enabled="< + +#include + + +namespace KDChart { + +/** + * \internal + */ +class AbstractThreeDAttributes::Private +{ + friend class AbstractThreeDAttributes; +public: + Private(); + +private: + bool enabled; + int height; + double depth; +}; + +inline AbstractThreeDAttributes::AbstractThreeDAttributes( Private * p ) : _d( p ) { init(); } + +} + +#endif // KDCHARTABSTRACTTHREEDATTRIBUTES_P_H diff --git a/libkdchart/src/KDChartAttributesModel.cpp b/libkdchart/src/KDChartAttributesModel.cpp new file mode 100644 index 0000000..eea74b9 --- /dev/null +++ b/libkdchart/src/KDChartAttributesModel.cpp @@ -0,0 +1,723 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAttributesModel.h" +#include "KDChartPalette.h" +#include "KDChartGlobal.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +using namespace KDChart; + +AttributesModel::AttributesModel( QAbstractItemModel* model, QObject * parent/* = 0 */ ) + : AbstractProxyModel( parent ), + mPaletteType( PaletteTypeDefault ) +{ + setSourceModel(model); + setDefaultForRole( KDChart::DataValueLabelAttributesRole, + DataValueAttributes::defaultAttributesAsVariant() ); +} + +AttributesModel::~AttributesModel() +{ +} + +void AttributesModel::initFrom( const AttributesModel* other ) +{ + if( other == this || ! other ) return; + + mDataMap = other->mDataMap; + mHorizontalHeaderDataMap = other->mHorizontalHeaderDataMap; + mVerticalHeaderDataMap = other->mVerticalHeaderDataMap; + mModelDataMap = other->mModelDataMap; + mDefaultsMap = other->mDefaultsMap; + + setPaletteType( other->paletteType() ); +} + +bool AttributesModel::compare( const AttributesModel* other )const +{ + if( other == this ) return true; + if( ! other ){ + //qDebug() << "AttributesModel::compare() cannot compare to Null pointer"; + return false; + } + + { + if (mDataMap.count() != other->mDataMap.count()){ + //qDebug() << "AttributesModel::compare() dataMap have different sizes"; + return false; + } + QMap > >::const_iterator itA = mDataMap.constBegin(); + QMap > >::const_iterator itB = other->mDataMap.constBegin(); + while (itA != mDataMap.constEnd()) { + if ((*itA).count() != (*itB).count()){ + //qDebug() << "AttributesModel::compare() dataMap/map have different sizes"; + return false; + } + QMap >::const_iterator it2A = (*itA).constBegin(); + QMap >::const_iterator it2B = (*itB).constBegin(); + while (it2A != itA->constEnd()) { + if ((*it2A).count() != (*it2B).count()){ + //qDebug() << "AttributesModel::compare() dataMap/map/map have different sizes:" + // << (*it2A).count() << (*it2B).count(); + return false; + } + QMap::const_iterator it3A = (*it2A).constBegin(); + QMap::const_iterator it3B = (*it2B).constBegin(); + while (it3A != it2A->constEnd()) { + if ( it3A.key() != it3B.key() ){ + //qDebug( "AttributesModel::compare()\n" + // " dataMap[%i, %i] values have different types. A: %x B: %x", + // itA.key(), it2A.key(), it3A.key(), it3B.key()); + return false; + } + if ( ! compareAttributes( it3A.key(), it3A.value(), it3B.value() ) ){ + //qDebug( "AttributesModel::compare()\n" + // " dataMap[%i, %i] values are different. Role: %x", itA.key(), it2A.key(), it3A.key()); + return false; + } + ++it3A; + ++it3B; + } + ++it2A; + ++it2B; + } + ++itA; + ++itB; + } + } + { + if (mHorizontalHeaderDataMap.count() != other->mHorizontalHeaderDataMap.count()){ + //qDebug() << "AttributesModel::compare() horizontalHeaderDataMap have different sizes"; + return false; + } + QMap >::const_iterator itA = mHorizontalHeaderDataMap.constBegin(); + QMap >::const_iterator itB = other->mHorizontalHeaderDataMap.constBegin(); + while (itA != mHorizontalHeaderDataMap.constEnd()) { + if ((*itA).count() != (*itB).count()){ + //qDebug() << "AttributesModel::compare() horizontalHeaderDataMap/map have different sizes"; + return false; + } + QMap::const_iterator it2A = (*itA).constBegin(); + QMap::const_iterator it2B = (*itB).constBegin(); + while (it2A != itA->constEnd()) { + if ( it2A.key() != it2B.key() ){ + //qDebug( "AttributesModel::compare()\n" + // " horizontalHeaderDataMap[ %i ] values have different types. A: %x B: %x", + // itA.key(), it2A.key(), it2B.key()); + return false; + } + if ( ! compareAttributes( it2A.key(), it2A.value(), it2B.value() ) ){ + //qDebug( "AttributesModel::compare()\n" + // " horizontalHeaderDataMap[ %i ] values are different. Role: %x", itA.key(), it2A.key() ); + return false; + } + ++it2A; + ++it2B; + } + ++itA; + ++itB; + } + } + { + if (mVerticalHeaderDataMap.count() != other->mVerticalHeaderDataMap.count()){ + //qDebug() << "AttributesModel::compare() verticalHeaderDataMap have different sizes"; + return false; + } + QMap >::const_iterator itA = mVerticalHeaderDataMap.constBegin(); + QMap >::const_iterator itB = other->mVerticalHeaderDataMap.constBegin(); + while (itA != mVerticalHeaderDataMap.constEnd()) { + if ((*itA).count() != (*itB).count()){ + //qDebug() << "AttributesModel::compare() verticalHeaderDataMap/map have different sizes"; + return false; + } + QMap::const_iterator it2A = (*itA).constBegin(); + QMap::const_iterator it2B = (*itB).constBegin(); + while (it2A != itA->constEnd()) { + if ( it2A.key() != it2B.key() ){ + //qDebug( "AttributesModel::compare()\n" + // " verticalHeaderDataMap[ %i ] values have different types. A: %x B: %x", + // itA.key(), it2A.key(), it2B.key()); + return false; + } + if ( ! compareAttributes( it2A.key(), it2A.value(), it2B.value() ) ){ + //qDebug( "AttributesModel::compare()\n" + // " verticalHeaderDataMap[ %i ] values are different. Role: %x", itA.key(), it2A.key() ); + return false; + } + ++it2A; + ++it2B; + } + ++itA; + ++itB; + } + } + { + if (mModelDataMap.count() != other->mModelDataMap.count()){ + //qDebug() << "AttributesModel::compare() modelDataMap have different sizes:" << mModelDataMap.count() << other->mModelDataMap.count(); + return false; + } + QMap::const_iterator itA = mModelDataMap.constBegin(); + QMap::const_iterator itB = other->mModelDataMap.constBegin(); + while (itA != mModelDataMap.constEnd()) { + if ( itA.key() != itB.key() ){ + //qDebug( "AttributesModel::compare()\n" + // " modelDataMap values have different types. A: %x B: %x", + // itA.key(), itB.key()); + return false; + } + if ( ! compareAttributes( itA.key(), itA.value(), itB.value() ) ){ + //qDebug( "AttributesModel::compare()\n" + // " modelDataMap values are different. Role: %x", itA.key() ); + return false; + } + ++itA; + ++itB; + } + } + if (paletteType() != other->paletteType()){ + //qDebug() << "AttributesModel::compare() palette types are different"; + return false; + } + return true; +} + +bool AttributesModel::compareAttributes( + int role, const QVariant& a, const QVariant& b )const +{ + if( isKnownAttributesRole( role ) ){ + switch( role ) { + case DataValueLabelAttributesRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case DatasetBrushRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case DatasetPenRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case ThreeDAttributesRole: + // As of yet there is no ThreeDAttributes class, + // and the AbstractThreeDAttributes class is pure virtual, + // so we ignore this role for now. + // (khz, 04.04.2007) + /* + return (qVariantValue( a ) == + qVariantValue( b )); + */ + break; + case LineAttributesRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case ThreeDLineAttributesRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case BarAttributesRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case StockBarAttributesRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case ThreeDBarAttributesRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case PieAttributesRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case ThreeDPieAttributesRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case ValueTrackerAttributesRole: + return (qVariantValue( a ) == + qVariantValue( b )); + case DataHiddenRole: + return (qVariantValue( a ) == + qVariantValue( b )); + default: + Q_ASSERT( false ); // all of our own roles need to be handled + break; + } + }else{ + return (a == b); + } + return true; +} + + +QVariant AttributesModel::headerData ( int section, + Qt::Orientation orientation, + int role/* = Qt::DisplayRole */ ) const +{ + if( sourceModel() ) { + const QVariant sourceData = sourceModel()->headerData( section, orientation, role ); + if ( sourceData.isValid() ) return sourceData; + } + + if( orientation == Qt::Horizontal && role == ColumnDataRole ) + { + // it seems the source model doesn't like the idea of handing out all the column data at once... + // so we have to do it manually. + QVariantList result; + const int rows = sourceModel()->rowCount(); + for( int row = 0; row < rows; ++row ) + result.push_back( sourceModel()->index( row, section ).data() ); + + return result; + } + + + // the source model didn't have data set, let's use our stored values + const QMap >& map = orientation == Qt::Horizontal ? mHorizontalHeaderDataMap : mVerticalHeaderDataMap; + if ( map.contains( section ) ) { + const QMap &dataMap = map[ section ]; + if ( dataMap.contains( role ) ) { + return dataMap[ role ]; + } + } + + return defaultHeaderData( section, orientation, role ); +} + + +QVariant AttributesModel::defaultHeaderData ( int section, Qt::Orientation orientation, int role ) const +{ + // Default values if nothing else matches + switch ( role ) { + case Qt::DisplayRole: + //TODO for KDChart 3.0: Change to "return QString::number( section+1 );" + return QString(QLatin1String( orientation == Qt::Vertical ? "Series " : "Item " ) + QString::number( section ) ); + + case KDChart::DatasetBrushRole: { + if ( paletteType() == PaletteTypeSubdued ) + return Palette::subduedPalette().getBrush( section ); + else if ( paletteType() == PaletteTypeRainbow ) + return Palette::rainbowPalette().getBrush( section ); + else if ( paletteType() == PaletteTypeDefault ) + return Palette::defaultPalette().getBrush( section ); + else + qWarning("Unknown type of fallback palette!"); + } break; + case KDChart::DatasetPenRole: { + // default to the color set for the brush (or it's defaults) + // but only if no per model override was set + if ( !modelData( role ).isValid() ) { + QBrush brush = qVariantValue( headerData( section, orientation, DatasetBrushRole ) ); + return QPen( brush.color() ); + } + } break; + default: + break; + } + + return QVariant(); +} + +// Note: Our users NEED this method - even if +// we do not need it at drawing time! +// (khz, 2006-07-28) +QVariant AttributesModel::data( int role ) const +{ + if ( isKnownAttributesRole( role ) ) { + // check if there is something set at global level + QVariant v = modelData( role ); + + // else return the default setting, if any + if ( !v.isValid() ) + v = defaultsForRole( role ); + return v; + } + return QVariant(); +} + + +// Note: Our users NEED this method - even if +// we do not need it at drawing time! +// (khz, 2006-07-28) +QVariant AttributesModel::data( int column, int role ) const +{ + if ( isKnownAttributesRole( role ) ) { + // check if there is something set for the column (dataset) + QVariant v; + v = headerData( column, Qt::Horizontal, role ); + + // check if there is something set at global level + if ( !v.isValid() ) + v = data( role ); // includes automatic fallback to default + return v; + } + return QVariant(); +} + + +QVariant AttributesModel::data( const QModelIndex& index, int role ) const +{ + //qDebug() << "AttributesModel::data(" << index << role << ")"; + if( index.isValid() ) { + Q_ASSERT( index.model() == this ); + } + + if( sourceModel() == 0 ) + return QVariant(); + + if( index.isValid() ) + { + const QVariant sourceData = sourceModel()->data( mapToSource(index), role ); + if( sourceData.isValid() ) + return sourceData; + } + + // check if we are storing a value for this role at this cell index + if( mDataMap.contains( index.column() ) ) + { + const QMap< int, QMap< int, QVariant > >& colDataMap = mDataMap[ index.column() ]; + if( colDataMap.contains( index.row() ) ) + { + const QMap< int, QVariant >& dataMap = colDataMap[ index.row() ]; + if( dataMap.contains( role ) ) + { + const QVariant v = dataMap[ role ]; + if( v.isValid() ) + return v; + } + } + } + // check if there is something set for the column (dataset), or at global level + if( index.isValid() ) + return data( index.column(), role ); // includes automatic fallback to default + + return QVariant(); +} + + +bool AttributesModel::isKnownAttributesRole( int role ) const +{ + bool oneOfOurs = false; + switch( role ) { + // fallthrough intended + case DataValueLabelAttributesRole: + case DatasetBrushRole: + case DatasetPenRole: + case ThreeDAttributesRole: + case LineAttributesRole: + case ThreeDLineAttributesRole: + case BarAttributesRole: + case StockBarAttributesRole: + case ThreeDBarAttributesRole: + case PieAttributesRole: + case ThreeDPieAttributesRole: + case ValueTrackerAttributesRole: + case DataHiddenRole: + oneOfOurs = true; + default: + break; + } + return oneOfOurs; +} + +QVariant AttributesModel::defaultsForRole( int role ) const +{ + // returns default-constructed QVariant if not found + return mDefaultsMap.value( role ); +} + +bool AttributesModel::setData ( const QModelIndex & index, const QVariant & value, int role ) +{ + if ( !isKnownAttributesRole( role ) ) { + return sourceModel()->setData( mapToSource(index), value, role ); + } else { + QMap< int, QMap< int, QVariant> > &colDataMap = mDataMap[ index.column() ]; + QMap &dataMap = colDataMap[ index.row() ]; + //qDebug() << "AttributesModel::setData" <<"role" << role << "value" << value; + dataMap.insert( role, value ); + emit attributesChanged( index, index ); + return true; + } +} + +bool AttributesModel::resetData ( const QModelIndex & index, int role ) +{ + return setData ( index, QVariant(), role ); +} + +bool AttributesModel::setHeaderData ( int section, Qt::Orientation orientation, + const QVariant & value, int role ) +{ + if( sourceModel() != 0 && headerData( section, orientation, role ) == value ) + return true; + if ( !isKnownAttributesRole( role ) ) { + return sourceModel()->setHeaderData( section, orientation, value, role ); + } else { + QMap > §ionDataMap + = orientation == Qt::Horizontal ? mHorizontalHeaderDataMap : mVerticalHeaderDataMap; + QMap &dataMap = sectionDataMap[ section ]; + dataMap.insert( role, value ); + if( sourceModel() ){ + emit attributesChanged( index( 0, section, QModelIndex() ), + index( rowCount( QModelIndex() ), section, QModelIndex() ) ); + emit headerDataChanged( orientation, section, section ); + if ( section != -1 ) + emit dataChanged( index( 0, section, QModelIndex() ), + index( rowCount( QModelIndex() ) - 1, section, QModelIndex() ) ); + } + return true; + } +} + +bool AttributesModel::resetHeaderData ( int section, Qt::Orientation orientation, int role ) +{ + return setHeaderData ( section, orientation, QVariant(), role ); +} + +void AttributesModel::setPaletteType( AttributesModel::PaletteType type ) +{ + mPaletteType = type; +} + +AttributesModel::PaletteType AttributesModel::paletteType() const +{ + return mPaletteType; +} + +bool KDChart::AttributesModel::setModelData( const QVariant value, int role ) +{ + mModelDataMap.insert( role, value ); + if( sourceModel() ){ + emit attributesChanged( index( 0, 0, QModelIndex() ), + index( rowCount( QModelIndex() ), + columnCount( QModelIndex() ), QModelIndex() ) ); + } + return true; +} + +QVariant KDChart::AttributesModel::modelData( int role ) const +{ + return mModelDataMap.value( role, QVariant() ); +} + +int AttributesModel::rowCount( const QModelIndex& index ) const +{ + if ( sourceModel() ) { + return sourceModel()->rowCount( mapToSource(index) ); + } else { + return 0; + } +} + +int AttributesModel::columnCount( const QModelIndex& index ) const +{ + if ( sourceModel() ) { + return sourceModel()->columnCount( mapToSource(index) ); + } else { + return 0; + } +} + +void AttributesModel::setSourceModel( QAbstractItemModel* sourceModel ) +{ + if( this->sourceModel() != 0 ) + { + disconnect( this->sourceModel(), SIGNAL( dataChanged( const QModelIndex&, const QModelIndex&)), + this, SLOT( slotDataChanged( const QModelIndex&, const QModelIndex&))); + disconnect( this->sourceModel(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), + this, SLOT( slotRowsInserted( const QModelIndex&, int, int ) ) ); + disconnect( this->sourceModel(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), + this, SLOT( slotRowsRemoved( const QModelIndex&, int, int ) ) ); + disconnect( this->sourceModel(), SIGNAL( rowsAboutToBeInserted( const QModelIndex&, int, int ) ), + this, SLOT( slotRowsAboutToBeInserted( const QModelIndex&, int, int ) ) ); + disconnect( this->sourceModel(), SIGNAL( rowsAboutToBeRemoved( const QModelIndex&, int, int ) ), + this, SLOT( slotRowsAboutToBeRemoved( const QModelIndex&, int, int ) ) ); + disconnect( this->sourceModel(), SIGNAL( columnsInserted( const QModelIndex&, int, int ) ), + this, SLOT( slotColumnsInserted( const QModelIndex&, int, int ) ) ); + disconnect( this->sourceModel(), SIGNAL( columnsRemoved( const QModelIndex&, int, int ) ), + this, SLOT( slotColumnsRemoved( const QModelIndex&, int, int ) ) ); + disconnect( this->sourceModel(), SIGNAL( columnsAboutToBeInserted( const QModelIndex&, int, int ) ), + this, SLOT( slotColumnsAboutToBeInserted( const QModelIndex&, int, int ) ) ); + disconnect( this->sourceModel(), SIGNAL( columnsAboutToBeRemoved( const QModelIndex&, int, int ) ), + this, SLOT( slotColumnsAboutToBeRemoved( const QModelIndex&, int, int ) ) ); + disconnect( this->sourceModel(), SIGNAL( modelReset() ), + this, SIGNAL( modelReset() ) ); + disconnect( this->sourceModel(), SIGNAL( layoutChanged() ), + this, SIGNAL( layoutChanged() ) ); + } + QAbstractProxyModel::setSourceModel( sourceModel ); + if( this->sourceModel() != NULL ) + { + connect( this->sourceModel(), SIGNAL( dataChanged( const QModelIndex&, const QModelIndex&)), + this, SLOT( slotDataChanged( const QModelIndex&, const QModelIndex&))); + connect( this->sourceModel(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), + this, SLOT( slotRowsInserted( const QModelIndex&, int, int ) ) ); + connect( this->sourceModel(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), + this, SLOT( slotRowsRemoved( const QModelIndex&, int, int ) ) ); + connect( this->sourceModel(), SIGNAL( rowsAboutToBeInserted( const QModelIndex&, int, int ) ), + this, SLOT( slotRowsAboutToBeInserted( const QModelIndex&, int, int ) ) ); + connect( this->sourceModel(), SIGNAL( rowsAboutToBeRemoved( const QModelIndex&, int, int ) ), + this, SLOT( slotRowsAboutToBeRemoved( const QModelIndex&, int, int ) ) ); + connect( this->sourceModel(), SIGNAL( columnsInserted( const QModelIndex&, int, int ) ), + this, SLOT( slotColumnsInserted( const QModelIndex&, int, int ) ) ); + connect( this->sourceModel(), SIGNAL( columnsRemoved( const QModelIndex&, int, int ) ), + this, SLOT( slotColumnsRemoved( const QModelIndex&, int, int ) ) ); + connect( this->sourceModel(), SIGNAL( columnsAboutToBeInserted( const QModelIndex&, int, int ) ), + this, SLOT( slotColumnsAboutToBeInserted( const QModelIndex&, int, int ) ) ); + connect( this->sourceModel(), SIGNAL( columnsAboutToBeRemoved( const QModelIndex&, int, int ) ), + this, SLOT( slotColumnsAboutToBeRemoved( const QModelIndex&, int, int ) ) ); + connect( this->sourceModel(), SIGNAL( modelReset() ), + this, SIGNAL( modelReset() ) ); + connect( this->sourceModel(), SIGNAL( layoutChanged() ), + this, SIGNAL( layoutChanged() ) ); + } +} + +void AttributesModel::slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end ) +{ + beginInsertRows( mapFromSource( parent ), start, end ); +} + +void AttributesModel::slotColumnsAboutToBeInserted( const QModelIndex& parent, int start, int end ) +{ + beginInsertColumns( mapFromSource( parent ), start, end ); +} + +void AttributesModel::slotRowsInserted( const QModelIndex& parent, int start, int end ) +{ + Q_UNUSED( parent ); + Q_UNUSED( start ); + Q_UNUSED( end ); + endInsertRows(); +} + +void AttributesModel::slotColumnsInserted( const QModelIndex& parent, int start, int end ) +{ + Q_UNUSED( parent ); + Q_UNUSED( start ); + Q_UNUSED( end ); + endInsertColumns(); +} + +void AttributesModel::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) +{ + beginRemoveRows( mapFromSource( parent ), start, end ); +} + +void AttributesModel::slotColumnsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) +{ + beginRemoveColumns( mapFromSource( parent ), start, end ); +} + +void AttributesModel::slotRowsRemoved( const QModelIndex& parent, int start, int end ) +{ + Q_UNUSED( parent ); + Q_UNUSED( start ); + Q_UNUSED( end ); + endRemoveRows(); +} + +void AttributesModel::slotColumnsRemoved( const QModelIndex& parent, int start, int end ) +{ + Q_UNUSED( parent ); + Q_UNUSED( start ); + Q_UNUSED( end ); + endRemoveColumns(); +} + +void AttributesModel::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) +{ + emit dataChanged( mapFromSource( topLeft ), mapFromSource( bottomRight ) ); +} + +/** needed for serialization */ +const QMap > > AttributesModel::dataMap()const +{ + return mDataMap; +} +/** needed for serialization */ +const QMap > AttributesModel::horizontalHeaderDataMap()const +{ + return mHorizontalHeaderDataMap; +} +/** needed for serialization */ +const QMap > AttributesModel::verticalHeaderDataMap()const +{ + return mVerticalHeaderDataMap; +} +/** needed for serialization */ +const QMap AttributesModel::modelDataMap()const +{ + return mModelDataMap; +} + +/** needed for serialization */ +void AttributesModel::setDataMap( const QMap > > map ) +{ + mDataMap = map; +} +/** needed for serialization */ +void AttributesModel::setHorizontalHeaderDataMap( const QMap > map ) +{ + mHorizontalHeaderDataMap = map; +} +/** needed for serialization */ +void AttributesModel::setVerticalHeaderDataMap( const QMap > map ) +{ + mVerticalHeaderDataMap = map; +} +/** needed for serialization */ +void AttributesModel::setModelDataMap( const QMap map ) +{ + mModelDataMap = map; +} + +void AttributesModel::setDefaultForRole( int role, const QVariant& value ) +{ + if ( value.isValid() ) { + mDefaultsMap.insert( role, value ); + } else { + // erase the possibily existing value to not let the map grow: + QMap::iterator it = mDefaultsMap.find( role ); + if ( it != mDefaultsMap.end() ) { + mDefaultsMap.erase( it ); + } + } + + Q_ASSERT( defaultsForRole( role ) == value ); +} diff --git a/libkdchart/src/KDChartAttributesModel.h b/libkdchart/src/KDChartAttributesModel.h new file mode 100644 index 0000000..b5c0ce2 --- /dev/null +++ b/libkdchart/src/KDChartAttributesModel.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef __KDCHART_ATTRIBUTES_MODEL_H__ +#define __KDCHART_ATTRIBUTES_MODEL_H__ + +#include "KDChartAbstractProxyModel.h" +#include +#include + +#include "KDChartGlobal.h" + +namespace KDChart { + +/** + * @brief A proxy model used for storing attributes + */ +class KDCHART_EXPORT AttributesModel : public AbstractProxyModel +{ + Q_OBJECT + + friend class AttributesModelSerializer; + +public: + enum PaletteType { + PaletteTypeDefault = 0, + PaletteTypeRainbow = 1, + PaletteTypeSubdued = 2 + }; + + explicit AttributesModel( QAbstractItemModel* model, QObject * parent = 0 ); + ~AttributesModel(); + + /** Copies the internal data (maps and palette) of another + * AttributesModel* into this one. + */ + void initFrom( const AttributesModel* other ); + + /** Returns true if both, all of the attributes set, and + * the palette set is equal in both of the AttributeModels. + */ + bool compare( const AttributesModel* other )const; + + bool compareAttributes( int role, const QVariant& a, const QVariant& b )const; + + /* Attributes Model specific API */ + bool setModelData( const QVariant value, int role ); + QVariant modelData( int role ) const; + + /** Returns whether the given role corresponds to one of the known + * internally used ones. */ + bool isKnownAttributesRole( int role ) const; + + /** Sets the palettetype used by this attributesmodel */ + void setPaletteType( PaletteType type ); + PaletteType paletteType() const; + + /** Returns the data that were specified at global level, + * or the default data, or QVariant(). + */ + QVariant data(int role) const; + + /** Returns the data that were specified at per column level, + * or the globally set data, or the default data, or QVariant(). + */ + QVariant data(int column, int role) const; + + /** \reimpl */ + QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + /** \reimpl */ + int rowCount(const QModelIndex& ) const; + /** \reimpl */ + int columnCount(const QModelIndex& ) const; + /** \reimpl */ + QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const; + /** \reimpl */ + bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::DisplayRole); + /** Remove any explicit attributes settings that might have been specified before. */ + bool resetData ( const QModelIndex & index, int role = Qt::DisplayRole); + /** \reimpl */ + bool setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, + int role = Qt::DisplayRole); + /** Returns default values for the header data. */ + virtual QVariant defaultHeaderData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + /** Remove any explicit attributes settings that might have been specified before. */ + bool resetHeaderData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole); + /** \reimpl */ + void setSourceModel ( QAbstractItemModel* sourceModel ); + + /** Define the default value for a certain role. + Passing a default-constructed QVariant is equivalent to removing the default. */ + void setDefaultForRole( int role, const QVariant& value ); + +Q_SIGNALS: + void attributesChanged( const QModelIndex&, const QModelIndex& ); + +protected: + /** needed for serialization */ + const QMap > > dataMap()const; + /** needed for serialization */ + const QMap > horizontalHeaderDataMap()const; + /** needed for serialization */ + const QMap > verticalHeaderDataMap()const; + /** needed for serialization */ + const QMap modelDataMap()const; + /** needed for serialization */ + void setDataMap( const QMap > > map ); + /** needed for serialization */ + void setHorizontalHeaderDataMap( const QMap > map ); + /** needed for serialization */ + void setVerticalHeaderDataMap( const QMap > map ); + /** needed for serialization */ + void setModelDataMap( const QMap map ); + +private Q_SLOTS: + void slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end ); + void slotColumnsAboutToBeInserted( const QModelIndex& parent, int start, int end ); + void slotRowsInserted( const QModelIndex& parent, int start, int end ); + void slotColumnsInserted( const QModelIndex& parent, int start, int end ); + + void slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ); + void slotColumnsAboutToBeRemoved( const QModelIndex& parent, int start, int end ); + void slotRowsRemoved( const QModelIndex& parent, int start, int end ); + void slotColumnsRemoved( const QModelIndex& parent, int start, int end ); + + void slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ); + +private: + // helper + QVariant defaultsForRole( int role ) const; + + QMap > > mDataMap; + QMap > mHorizontalHeaderDataMap; + QMap > mVerticalHeaderDataMap; + QMap mModelDataMap; + QMap mDefaultsMap; + PaletteType mPaletteType; +}; + +} + +#endif diff --git a/libkdchart/src/KDChartBackgroundAttributes.cpp b/libkdchart/src/KDChartBackgroundAttributes.cpp new file mode 100644 index 0000000..f9f1520 --- /dev/null +++ b/libkdchart/src/KDChartBackgroundAttributes.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartBackgroundAttributes.h" +#include + +#include + +#define d d_func() + + +using namespace KDChart; + +class BackgroundAttributes::Private +{ + friend class KDChart::BackgroundAttributes; +public: + Private(); +private: + bool visible; + QBrush brush; + BackgroundPixmapMode pixmapMode; + QPixmap pixmap; +}; + +BackgroundAttributes::Private::Private() : + visible( false ), + pixmapMode( BackgroundAttributes::BackgroundPixmapModeNone ) +{ +} + + +BackgroundAttributes::BackgroundAttributes() + : _d( new Private() ) +{ +} + +BackgroundAttributes::BackgroundAttributes( const BackgroundAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +BackgroundAttributes & BackgroundAttributes::operator=( const BackgroundAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +bool BackgroundAttributes::operator==( const BackgroundAttributes& r ) const +{ + return isEqualTo( r ); +} + + +bool BackgroundAttributes::isEqualTo( + const BackgroundAttributes& other, bool ignorePixmap ) const +{ + /* + qDebug() << "BackgroundAttributes::operator=="; + qDebug() << "isVisible" << (isVisible() == other.isVisible()); + qDebug() << "brush" << (brush() == other.brush()); + qDebug() << "pixmapMode"<< (pixmapMode() == other.pixmapMode()); + qDebug() << "pixmap" << (pixmap().serialNumber() == other.pixmap().serialNumber()); + */ + return ( + isVisible() == other.isVisible() && + brush() == other.brush() && + pixmapMode() == other.pixmapMode() && + (ignorePixmap || + pixmap().serialNumber() == other.pixmap().serialNumber()) ); +} + + +BackgroundAttributes::~BackgroundAttributes() +{ + delete _d; _d = 0; +} + + + + +void BackgroundAttributes::setVisible( bool visible ) +{ + d->visible = visible; +} + + +bool BackgroundAttributes::isVisible() const +{ + return d->visible; +} + +void BackgroundAttributes::setBrush( const QBrush &brush ) +{ + d->brush = brush; +} + +QBrush BackgroundAttributes::brush() const +{ + return d->brush; +} + +void BackgroundAttributes::setPixmapMode( BackgroundPixmapMode mode ) +{ + d->pixmapMode = mode; +} + +BackgroundAttributes::BackgroundPixmapMode BackgroundAttributes::pixmapMode() const +{ + return d->pixmapMode; +} + +void BackgroundAttributes::setPixmap( const QPixmap &backPixmap ) +{ + d->pixmap = backPixmap; +} + +QPixmap BackgroundAttributes::pixmap() const +{ + return d->pixmap; +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::BackgroundAttributes& ba) +{ + dbg << "KDChart::BackgroundAttributes(" + << "visible="< +#include +#include +#include "KDChartGlobal.h" + +namespace KDChart { + +/** + * Set of attributes usable for background pixmaps + */ +class KDCHART_EXPORT BackgroundAttributes +{ +public: + BackgroundAttributes(); + BackgroundAttributes( const BackgroundAttributes& ); + BackgroundAttributes &operator= ( const BackgroundAttributes& ); + + ~BackgroundAttributes(); + + enum BackgroundPixmapMode { BackgroundPixmapModeNone, + BackgroundPixmapModeCentered, + BackgroundPixmapModeScaled, + BackgroundPixmapModeStretched }; + + void setVisible( bool visible ); + bool isVisible() const; + + void setBrush( const QBrush &brush ); + QBrush brush() const; + + void setPixmapMode( BackgroundPixmapMode mode ); + BackgroundPixmapMode pixmapMode() const; + + void setPixmap( const QPixmap &backPixmap ); + QPixmap pixmap() const; + + bool operator==( const BackgroundAttributes& ) const; + inline bool operator!=( const BackgroundAttributes& other ) const { return !operator==(other); } + + bool isEqualTo( const BackgroundAttributes& other, bool ignorePixmap=false ) const; + +private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( BackgroundAttributes ) +}; // End of class BackgroundAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::BackgroundAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +Q_DECLARE_METATYPE( KDChart::BackgroundAttributes ) +Q_DECLARE_TYPEINFO( KDChart::BackgroundAttributes, Q_MOVABLE_TYPE ); +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::BackgroundAttributes ) + +#endif // KDCHARTBACKGROUNDATTRIBUTES_H diff --git a/libkdchart/src/KDChartBarAttributes.cpp b/libkdchart/src/KDChartBarAttributes.cpp new file mode 100644 index 0000000..ca1d214 --- /dev/null +++ b/libkdchart/src/KDChartBarAttributes.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartBarAttributes.h" +#include + +#include + +#define d d_func() + + +using namespace KDChart; + +class BarAttributes::Private +{ + friend class BarAttributes; +public: + Private(); + +private: + qreal datasetGap; + bool useFixedDatasetGap; + qreal valueBlockGap; + bool useFixedValueBlockGap; + qreal barWidth; + bool useFixedBarWidth; + bool drawSolidExcessArrows; + qreal groupGapFactor; + qreal barGapFactor; +}; + + +BarAttributes::Private::Private() + :datasetGap( 6 ), + useFixedDatasetGap( false ), + valueBlockGap( 24 ), + useFixedValueBlockGap( false ), + barWidth( -1 ), + useFixedBarWidth( false ), + drawSolidExcessArrows( false ), + groupGapFactor( 1.0 ), + barGapFactor( 0.5 ) +{ +} + + +BarAttributes::BarAttributes() + : _d( new Private() ) +{ +} + +BarAttributes::BarAttributes( const BarAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +BarAttributes& BarAttributes::operator= ( const BarAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +BarAttributes::~BarAttributes() +{ + delete _d; _d = 0; +} + + +bool BarAttributes::operator==( const BarAttributes& r ) const +{ + if( fixedDataValueGap() == r.fixedDataValueGap() && + useFixedDataValueGap() == r.useFixedDataValueGap() && + fixedValueBlockGap() == r.fixedValueBlockGap() && + useFixedValueBlockGap() == r.useFixedValueBlockGap() && + fixedBarWidth() == r.fixedBarWidth() && + useFixedBarWidth() == r.useFixedBarWidth() && + groupGapFactor() == r.groupGapFactor() && + barGapFactor() == r.barGapFactor() && + drawSolidExcessArrows() == r.drawSolidExcessArrows() ) + return true; + else + return false; +} + + +void BarAttributes::setFixedDataValueGap( qreal gap ) +{ + d->datasetGap = gap; +} + +qreal BarAttributes::fixedDataValueGap() const +{ + return d->datasetGap; +} + +void BarAttributes::setUseFixedDataValueGap( bool gapIsFixed ) +{ + d->useFixedDatasetGap = gapIsFixed; +} + +bool BarAttributes::useFixedDataValueGap() const +{ + return d->useFixedDatasetGap; +} + +void BarAttributes::setFixedValueBlockGap( qreal gap ) +{ + d->valueBlockGap = gap; +} + +qreal BarAttributes::fixedValueBlockGap() const +{ + return d->valueBlockGap; +} + +void BarAttributes::setUseFixedValueBlockGap( bool gapIsFixed ) +{ + d->useFixedValueBlockGap = gapIsFixed; +} + +bool BarAttributes::useFixedValueBlockGap() const +{ + return d->useFixedValueBlockGap; +} + +void BarAttributes::setFixedBarWidth( qreal width ) +{ + d->barWidth = width; +} + +qreal BarAttributes::fixedBarWidth() const +{ + + return d->barWidth; +} + +void BarAttributes::setUseFixedBarWidth( bool useFixedBarWidth ) +{ + d->useFixedBarWidth = useFixedBarWidth; +} + +bool BarAttributes::useFixedBarWidth() const +{ + return d->useFixedBarWidth; +} + +void BarAttributes::setGroupGapFactor( qreal gapFactor ) +{ + d->groupGapFactor = gapFactor; +} + +qreal BarAttributes::groupGapFactor() const +{ + return d->groupGapFactor; +} + +void BarAttributes::setBarGapFactor( qreal gapFactor ) +{ + d->barGapFactor = gapFactor; +} + +qreal BarAttributes::barGapFactor() const +{ + return d->barGapFactor; +} + +void BarAttributes::setDrawSolidExcessArrows( bool solidArrows ) +{ + d->drawSolidExcessArrows = solidArrows; +} + +bool BarAttributes::drawSolidExcessArrows() const +{ + return d->drawSolidExcessArrows; +} + diff --git a/libkdchart/src/KDChartBarAttributes.h b/libkdchart/src/KDChartBarAttributes.h new file mode 100644 index 0000000..a359eea --- /dev/null +++ b/libkdchart/src/KDChartBarAttributes.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTBARATTRIBUTES_H +#define KDCHARTBARATTRIBUTES_H + +#include +#include "KDChartGlobal.h" + +namespace KDChart { + +/** + * @brief Set of attributes for changing the appearance of bar charts + */ +class KDCHART_EXPORT BarAttributes +{ +public: + BarAttributes(); + BarAttributes( const BarAttributes& ); + BarAttributes &operator= ( const BarAttributes& ); + + ~BarAttributes(); + + void setFixedDataValueGap( qreal gap ); + qreal fixedDataValueGap() const; + + void setUseFixedDataValueGap( bool gapIsFixed ); + bool useFixedDataValueGap() const; + + void setFixedValueBlockGap( qreal gap ); + qreal fixedValueBlockGap() const; + + void setUseFixedValueBlockGap( bool gapIsFixed ); + bool useFixedValueBlockGap() const; + + void setFixedBarWidth( qreal width ); + qreal fixedBarWidth() const; + + void setUseFixedBarWidth( bool useFixedBarWidth ); + bool useFixedBarWidth() const; + + void setGroupGapFactor ( qreal gapFactor ); + qreal groupGapFactor() const; + + void setBarGapFactor( qreal gapFactor ); + qreal barGapFactor() const; + + void setDrawSolidExcessArrows( bool solidArrows ); + bool drawSolidExcessArrows() const; + + bool operator==( const BarAttributes& ) const; + inline bool operator!=( const BarAttributes& other ) const { return !operator==(other); } + +private: + class Private; + Private * _d; + Private * d_func() { return _d; } + const Private * d_func() const { return _d; } +}; // End of class BarAttributes + +} + +Q_DECLARE_METATYPE( KDChart::BarAttributes ) + +#endif // KDCHARTBARATTRIBUTES_H diff --git a/libkdchart/src/KDChartBarDiagram.cpp b/libkdchart/src/KDChartBarDiagram.cpp new file mode 100644 index 0000000..9ecc510 --- /dev/null +++ b/libkdchart/src/KDChartBarDiagram.cpp @@ -0,0 +1,427 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartBarDiagram.h" +#include "KDChartBarDiagram_p.h" + +#include "KDChartThreeDBarAttributes.h" +#include "KDChartPosition.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractGrid.h" + +#include +#include + +#include + +#include "KDChartNormalBarDiagram_p.h" +#include "KDChartStackedBarDiagram_p.h" +#include "KDChartPercentBarDiagram_p.h" +#include "KDChartNormalLyingBarDiagram_p.h" +#include "KDChartStackedLyingBarDiagram_p.h" +#include "KDChartPercentLyingBarDiagram_p.h" + + +using namespace KDChart; + +BarDiagram::Private::Private() + : orientation( Qt::Vertical ) +{ +} + +BarDiagram::Private::~Private() {} + +#define d d_func() + + +BarDiagram::BarDiagram( QWidget* parent, CartesianCoordinatePlane* plane ) : + AbstractCartesianDiagram( new Private(), parent, plane ) +{ + init(); +} + +void BarDiagram::init() +{ + d->diagram = this; + d->normalDiagram = new NormalBarDiagram( this ); + d->stackedDiagram = new StackedBarDiagram( this ); + d->percentDiagram = new PercentBarDiagram( this ); + d->normalLyingDiagram = new NormalLyingBarDiagram( this ); + d->stackedLyingDiagram = new StackedLyingBarDiagram( this ); + d->percentLyingDiagram = new PercentLyingBarDiagram( this ); + d->implementor = d->normalDiagram; + d->compressor.setModel( attributesModel() ); +} + +BarDiagram::~BarDiagram() +{ +} + +/** + * Creates an exact copy of this diagram. + */ +BarDiagram * BarDiagram::clone() const +{ + + BarDiagram* newDiagram = new BarDiagram( new Private( *d ) ); + newDiagram->setType( type() ); + return newDiagram; +} + +bool BarDiagram::compare( const BarDiagram* other )const +{ + if( other == this ) return true; + if( ! other ){ + return false; + } + + return // compare the base class + ( static_cast(this)->compare( other ) ) && + // compare own properties + (type() == other->type()); +} + +/** + * Sets the bar diagram's type to \a type + * \sa BarDiagram::BarType + */ +void BarDiagram::setType( const BarType type ) +{ + //if ( type == d->barType ) return; + if ( d->implementor->type() == type ) return; + + if ( d->orientation == Qt::Vertical ) { + switch( type ) { + case Normal: + d->implementor = d->normalDiagram; + break; + case Stacked: + d->implementor = d->stackedDiagram; + break; + case Percent: + d->implementor = d->percentDiagram; + break; + default: + Q_ASSERT_X( false, "BarDiagram::setType", "unknown diagram subtype" ); + } + } else { + switch( type ) { + case Normal: + d->implementor = d->normalLyingDiagram; + break; + case Stacked: + d->implementor = d->stackedLyingDiagram; + break; + case Percent: + d->implementor = d->percentLyingDiagram; + break; + default: + Q_ASSERT_X( false, "BarDiagram::setType", "unknown diagram subtype" ); + } + } + + Q_ASSERT( d->implementor->type() == type ); + + //d->barType = type; + // AbstractAxis settings - see AbstractDiagram and CartesianAxis + setPercentMode( type == BarDiagram::Percent ); + setDataBoundariesDirty(); + emit layoutChanged( this ); + emit propertiesChanged(); +} + +/** + * @return the type of the bar diagram + */ +BarDiagram::BarType BarDiagram::type() const +{ + return d->implementor->type(); +} + +/** + * Sets the orientation of the bar diagram + */ +void BarDiagram::setOrientation( Qt::Orientation orientation ) +{ + if ( d->orientation == orientation ) + return; + d->orientation = orientation; + + if ( d->orientation == Qt::Vertical ) { + switch( type() ) { + case Normal: + d->implementor = d->normalDiagram; + break; + case Stacked: + d->implementor = d->stackedDiagram; + break; + case Percent: + d->implementor = d->percentDiagram; + break; + default: + Q_ASSERT_X( false, "BarDiagram::setType", "unknown diagram subtype" ); + } + } else { + switch( type() ) { + case Normal: + d->implementor = d->normalLyingDiagram; + break; + case Stacked: + d->implementor = d->stackedLyingDiagram; + break; + case Percent: + d->implementor = d->percentLyingDiagram; + break; + default: + Q_ASSERT_X( false, "BarDiagram::setType", "unknown diagram subtype" ); + } + } + + // AbstractAxis settings - see AbstractDiagram and CartesianAxis + setPercentMode( type() == BarDiagram::Percent ); + setDataBoundariesDirty(); + emit layoutChanged( this ); + emit propertiesChanged(); +} + +/** + * @return the orientation of the bar diagram + */ +Qt::Orientation BarDiagram::orientation() const +{ + return d->orientation; +} + +/** + * Sets the global bar attributes to \a ba + */ +void BarDiagram::setBarAttributes( const BarAttributes& ba ) +{ + d->attributesModel->setModelData( qVariantFromValue( ba ), BarAttributesRole ); + emit propertiesChanged(); +} + +/** + * Sets the bar attributes of data set \a column to \a ba + */ +void BarDiagram::setBarAttributes( int column, const BarAttributes& ba ) +{ + d->setDatasetAttrs( column, qVariantFromValue( ba ), BarAttributesRole ); + emit propertiesChanged(); +} + +/** + * Sets the line attributes for the model index \a index to \a ba + */ +void BarDiagram::setBarAttributes( const QModelIndex& index, const BarAttributes& ba ) +{ + attributesModel()->setData( + d->attributesModel->mapFromSource( index ), + qVariantFromValue( ba ), + BarAttributesRole ); + emit propertiesChanged(); +} + +/** + * @return the global bar attribute set + */ +BarAttributes BarDiagram::barAttributes() const +{ + return qVariantValue( + d->attributesModel->data( KDChart::BarAttributesRole ) ); +} + +/** + * @return the bar attribute set of data set \a column + */ +BarAttributes BarDiagram::barAttributes( int column ) const +{ + const QVariant attrs( d->datasetAttrs( column, KDChart::BarAttributesRole ) ); + if( attrs.isValid() ) + return qVariantValue< BarAttributes >( attrs ); + return barAttributes(); +} + +/** + * @return the bar attribute set of the model index \a index + */ +BarAttributes BarDiagram::barAttributes( const QModelIndex& index ) const +{ + return qVariantValue( + d->attributesModel->data( + d->attributesModel->mapFromSource( index ), + KDChart::BarAttributesRole ) ); +} + +/** + * Sets the global 3D bar attributes to \a threeDAttrs + */ +void BarDiagram::setThreeDBarAttributes( const ThreeDBarAttributes& threeDAttrs ) +{ + setDataBoundariesDirty(); + d->attributesModel->setModelData( qVariantFromValue( threeDAttrs ), ThreeDBarAttributesRole ); + emit layoutChanged( this ); + emit propertiesChanged(); +} + +/** + * Sets the 3D bar attributes of dataset \a column to \a threeDAttrs + */ +void BarDiagram::setThreeDBarAttributes( int column, const ThreeDBarAttributes& threeDAttrs ) +{ + setDataBoundariesDirty(); + d->setDatasetAttrs( column, qVariantFromValue( threeDAttrs ), ThreeDBarAttributesRole ); + //emit layoutChanged( this ); + emit propertiesChanged(); + +} + +/** + * Sets the 3D line attributes of model index \a index to \a threeDAttrs + */ +void BarDiagram::setThreeDBarAttributes( const QModelIndex& index, const ThreeDBarAttributes& threeDAttrs ) +{ + setDataBoundariesDirty(); + d->attributesModel->setData( + d->attributesModel->mapFromSource(index), + qVariantFromValue( threeDAttrs ), + ThreeDBarAttributesRole ); + //emit layoutChanged( this ); + emit propertiesChanged(); +} + +/** + * @return the global 3D bar attributes + */ +ThreeDBarAttributes BarDiagram::threeDBarAttributes() const +{ + return qVariantValue( + d->attributesModel->data( KDChart::ThreeDBarAttributesRole ) ); +} + +/** + * @return the 3D bar attributes of data set \a column + */ +ThreeDBarAttributes BarDiagram::threeDBarAttributes( int column ) const +{ + const QVariant attrs( d->datasetAttrs( column, KDChart::ThreeDBarAttributesRole ) ); + if( attrs.isValid() ) + return qVariantValue< ThreeDBarAttributes >( attrs ); + return threeDBarAttributes(); +} + +/** + * @return the 3D bar attributes of the model index \a index + */ +ThreeDBarAttributes BarDiagram::threeDBarAttributes( const QModelIndex& index ) const +{ + return qVariantValue( + d->attributesModel->data( + d->attributesModel->mapFromSource(index), + KDChart::ThreeDBarAttributesRole ) ); +} + +double BarDiagram::threeDItemDepth( const QModelIndex& index ) const +{ + return threeDBarAttributes( index ).validDepth(); +} + +double BarDiagram::threeDItemDepth( int column ) const +{ + return qVariantValue( + d->datasetAttrs( column, KDChart::ThreeDBarAttributesRole ) ).validDepth(); +} + +void BarDiagram::resizeEvent ( QResizeEvent*) +{ + +} + +const QPair BarDiagram::calculateDataBoundaries() const +{ + d->compressor.setResolution( static_cast( this->size().width() * coordinatePlane()->zoomFactorX() ), + static_cast( this->size().height() * coordinatePlane()->zoomFactorY() ) ); + + if ( !checkInvariants(true) ) return QPair( QPointF( 0, 0 ), QPointF( 0, 0 ) ); + + // note: calculateDataBoundaries() is ignoring the hidden flags. + // That's not a bug but a feature: Hiding data does not mean removing them. + // For totally removing data from KD Chart's view people can use e.g. a proxy model + // calculate boundaries for different line types Normal - Stacked - Percent - Default Normal + return d->implementor->calculateDataBoundaries(); +} + +void BarDiagram::paintEvent ( QPaintEvent*) +{ + QPainter painter ( viewport() ); + PaintContext ctx; + ctx.setPainter ( &painter ); + ctx.setRectangle( QRectF ( 0, 0, width(), height() ) ); + paint ( &ctx ); +} + +void BarDiagram::paint( PaintContext* ctx ) +{ + if ( !checkInvariants( true ) ) return; + if ( !AbstractGrid::isBoundariesValid(dataBoundaries()) ) return; + const PainterSaver p( ctx->painter() ); + if( model()->rowCount( rootIndex() ) == 0 || model()->columnCount( rootIndex() ) == 0 ) + return; // nothing to paint for us + + AbstractCoordinatePlane* const plane = ctx->coordinatePlane(); + ctx->setCoordinatePlane( plane->sharedAxisMasterPlane( ctx->painter() ) ); + + // Only paint elements that are in the paint context's rectangle + // (in this case boundaries of the diagram, see paintEvent()) + ctx->painter()->setClipping( true ); + ctx->painter()->setClipRect( ctx->rectangle() ); + + // paint different bar types Normal - Stacked - Percent - Default Normal + d->implementor->paint( ctx ); + + ctx->setCoordinatePlane( plane ); +} + +void BarDiagram::resize( const QSizeF& size ) +{ + d->compressor.setResolution( static_cast< int >( size.width() * coordinatePlane()->zoomFactorX() ), + static_cast< int >( size.height() * coordinatePlane()->zoomFactorY() ) ); + setDataBoundariesDirty(); +} + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +int BarDiagram::numberOfAbscissaSegments () const +{ + return d->attributesModel->rowCount(attributesModelRootIndex()); +} + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +int BarDiagram::numberOfOrdinateSegments () const +{ + return d->attributesModel->columnCount(attributesModelRootIndex()); +} + +//#undef d diff --git a/libkdchart/src/KDChartBarDiagram.h b/libkdchart/src/KDChartBarDiagram.h new file mode 100644 index 0000000..3b7e89a --- /dev/null +++ b/libkdchart/src/KDChartBarDiagram.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTBARDIAGRAM_H +#define KDCHARTBARDIAGRAM_H + +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartBarAttributes.h" +//#include "KDChartThreeDBarAttributes.h" + +class QPainter; + +namespace KDChart { + + class ThreeDBarAttributes; + +/** + * @brief BarDiagram defines a common bar diagram. + * + * It provides different subtypes which are set using \a setType. + */ +class KDCHART_EXPORT BarDiagram : public AbstractCartesianDiagram +{ + Q_OBJECT + + Q_DISABLE_COPY( BarDiagram ) + + KDCHART_DECLARE_DERIVED_DIAGRAM( BarDiagram, CartesianCoordinatePlane ) + +public: + class BarDiagramType; + friend class BarDiagramType; + + explicit BarDiagram( + QWidget* parent = 0, CartesianCoordinatePlane* plane = 0 ); + virtual ~BarDiagram(); + + virtual BarDiagram * clone() const; + /** + * Returns true if both diagrams have the same settings. + */ + bool compare( const BarDiagram* other )const; + + enum BarType { Normal, + Stacked, + Percent, + Rows ///< @deprecated Use BarDiagram::setOrientation() instead + }; + + void setType( const BarType type ); + BarType type() const; + + void setOrientation( Qt::Orientation orientation ); + Qt::Orientation orientation() const; + + void setBarAttributes( const BarAttributes & a ); + void setBarAttributes( int column, const BarAttributes & a ); + void setBarAttributes( const QModelIndex & index, const BarAttributes & a ); + + BarAttributes barAttributes() const; + BarAttributes barAttributes( int column ) const; + BarAttributes barAttributes( const QModelIndex & index ) const; + + void setThreeDBarAttributes( const ThreeDBarAttributes & a ); + void setThreeDBarAttributes( int column, const ThreeDBarAttributes & a ); + void setThreeDBarAttributes( const QModelIndex & index, + const ThreeDBarAttributes & a ); + ThreeDBarAttributes threeDBarAttributes() const; + ThreeDBarAttributes threeDBarAttributes( int column ) const; + ThreeDBarAttributes threeDBarAttributes( const QModelIndex & index ) const; + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + // implement AbstractCartesianDiagram + /** \reimpl */ + const int numberOfAbscissaSegments () const; + /** \reimpl */ + const int numberOfOrdinateSegments () const; +#else + // implement AbstractCartesianDiagram + /** \reimpl */ + int numberOfAbscissaSegments () const; + /** \reimpl */ + int numberOfOrdinateSegments () const; +#endif + +protected: + void paint ( PaintContext* paintContext ); + +public: + void resize ( const QSizeF& area ); + +#if 0 + // FIXME merge with 3DAttributes? + void setThreeDimensionalBars( bool threeDBars ); + bool threeDimensionalBars() const; + + void setThreeDimensionalBarsShadowColors( bool shadow ); + bool threeDimensionalBarsShadowColors() const; + + void setThreeDimensionalBarAngle( uint angle ); + uint threeDimensionalBarAngle() const; + + void setThreeDimensionalBarDepth( double depth ); + double threeDimensionalBarDepth() const; + +#endif + + +protected: + virtual double threeDItemDepth( const QModelIndex & index ) const; + virtual double threeDItemDepth( int column ) const; + /** \reimpl */ + const QPair calculateDataBoundaries() const; + void paintEvent ( QPaintEvent* ); + void resizeEvent ( QResizeEvent* ); +private: + + /* + void paintBars( PaintContext* ctx, const QModelIndex& index, const QRectF& bar, double& maxDepth ); + */ + void calculateValueAndGapWidths( int rowCount, int colCount, + double groupWidth, + double& barWidth, + double& spaceBetweenBars, + double& spaceBetweenGroups ); +}; // End of class BarDiagram + +} + +#endif // KDCHARTBARDIAGRAM_H diff --git a/libkdchart/src/KDChartBarDiagram_p.cpp b/libkdchart/src/KDChartBarDiagram_p.cpp new file mode 100644 index 0000000..28391f8 --- /dev/null +++ b/libkdchart/src/KDChartBarDiagram_p.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartBarDiagram.h" +#include "KDChartDataValueAttributes.h" + +#include "KDChartBarDiagram_p.h" + +using namespace KDChart; + +BarDiagram::Private::Private( const Private& rhs ) + : AbstractCartesianDiagram::Private( rhs ) +{ +} + +void BarDiagram::BarDiagramType::paintBars( PaintContext* ctx, const QModelIndex& index, const QRectF& bar, double& maxDepth ) +{ + QRectF isoRect; + QPolygonF topPoints, sidePoints; + ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( index ); + double usedDepth = 0; + + //Pending Michel: configure threeDBrush settings - shadowColor etc... + QBrush indexBrush ( diagram()->brush( index ) ); + QPen indexPen( diagram()->pen( index ) ); + PainterSaver painterSaver( ctx->painter() ); + if ( diagram()->antiAliasing() ) + ctx->painter()->setRenderHint ( QPainter::Antialiasing ); + ctx->painter()->setBrush( indexBrush ); + ctx->painter()->setPen( PrintingParameters::scalePen( indexPen ) ); + if ( threeDAttrs.isEnabled() ) { + bool stackedMode = false; + bool percentMode = false; + bool paintTop = true; + if ( maxDepth ) + threeDAttrs.setDepth( -maxDepth ); + QPointF boundRight = ctx->coordinatePlane()->translate( diagram()->dataBoundaries().second ); + //fixme adjust the painting to reasonable depth value + switch ( type() ) + { + case BarDiagram::Normal: + usedDepth = threeDAttrs.depth()/4; + stackedMode = false; + percentMode = false; + break; + case BarDiagram::Stacked: + usedDepth = threeDAttrs.depth(); + stackedMode = true; + percentMode = false; + break; + case BarDiagram::Percent: + usedDepth = threeDAttrs.depth(); + stackedMode = false; + percentMode = true; + break; + default: + Q_ASSERT_X ( false, "dataBoundaries()", + "Type item does not match a defined bar chart Type." ); + } + isoRect = bar.translated( usedDepth, -usedDepth ); + // we need to find out if the height is negative + // and in this case paint it up and down + //qDebug() << isoRect.height(); + if ( isoRect.height() < 0 ) { + topPoints << isoRect.bottomLeft() << isoRect.bottomRight() + << bar.bottomRight() << bar.bottomLeft(); + if ( stackedMode ) { + // fix it when several negative stacked values + if ( index.column() == 0 ) { + paintTop = true; + } + else + paintTop = false; + } + + } else { + reverseMapper().addRect( index.row(), index.column(), isoRect ); + ctx->painter()->drawRect( isoRect ); + topPoints << bar.topLeft() << bar.topRight() << isoRect.topRight() << isoRect.topLeft(); + } + + if ( percentMode && isoRect.height() == 0 ) + paintTop = false; + + bool needToSetClippingOffForTop = false; + if ( paintTop ){ + // Draw the top, if at least one of the top's points is + // either inside or near at the edge of the coordinate plane: + bool drawIt = false; + bool hasPointOutside = false; + const QRectF r( ctx->rectangle().adjusted(0,-1,1,0) ); + KDAB_FOREACH( QPointF pt, topPoints ) { + if( r.contains( pt ) ) + drawIt = true; + else + hasPointOutside = true; + } + if( drawIt ){ + const PainterSaver p( ctx->painter() ); + needToSetClippingOffForTop = hasPointOutside && ctx->painter()->hasClipping(); + if( needToSetClippingOffForTop ) + ctx->painter()->setClipping( false ); + reverseMapper().addPolygon( index.row(), index.column(), topPoints ); + ctx->painter()->drawPolygon( topPoints ); + } + } + + + + sidePoints << bar.topRight() << isoRect.topRight() << isoRect.bottomRight() << bar.bottomRight(); + if ( bar.height() != 0 ){ + const PainterSaver p( ctx->painter() ); + if( needToSetClippingOffForTop ) + ctx->painter()->setClipping( false ); + reverseMapper().addPolygon( index.row(), index.column(), sidePoints ); + ctx->painter()->drawPolygon( sidePoints ); + } + } + + if( bar.height() != 0 ) + { + reverseMapper().addRect( index.row(), index.column(), bar ); + ctx->painter()->drawRect( bar ); + } + // reset + //diagram()->maxDepth = threeDAttrs.depth(); +} + +AttributesModel* BarDiagram::BarDiagramType::attributesModel() const +{ + return m_private->attributesModel; +} + +QModelIndex BarDiagram::BarDiagramType::attributesModelRootIndex() const +{ + return m_private->diagram->attributesModelRootIndex(); +} + +BarDiagram* BarDiagram::BarDiagramType::diagram() const +{ + return m_private->diagram; +} + +void BarDiagram::BarDiagramType::appendDataValueTextInfoToList( + AbstractDiagram * diagram, + DataValueTextInfoList & list, + const QModelIndex & index, + const PositionPoints& points, + const Position& autoPositionPositive, + const Position& autoPositionNegative, + const qreal value ) +{ + m_private->appendDataValueTextInfoToList( diagram, list, index, 0, + points, + autoPositionPositive, autoPositionNegative, value ); +} + +void BarDiagram::BarDiagramType::paintDataValueTextsAndMarkers( + AbstractDiagram* diagram, + PaintContext* ctx, + const DataValueTextInfoList & list, + bool paintMarkers ) +{ + m_private->paintDataValueTextsAndMarkers( diagram, ctx, list, paintMarkers ); +} + + +void BarDiagram::BarDiagramType::calculateValueAndGapWidths( int rowCount,int colCount, + double groupWidth, + double& outBarWidth, + double& outSpaceBetweenBars, + double& outSpaceBetweenGroups ) +{ + + Q_UNUSED( rowCount ); + + BarAttributes ba = diagram()->barAttributes( diagram()->model()->index( 0, 0, diagram()->rootIndex() ) ); + + // Pending Michel Fixme + /* We are colCount groups to paint. Each group is centered around the + * horizontal point position on the grid. The full area covers the + * values -1 to colCount + 1. A bar has a relative width of one unit, + * the gaps between bars are 0.5 wide, and the gap between groups is + * also one unit, by default. */ + + double units; + if( type() == Normal ) + units = colCount // number of bars in group * 1.0 + + (colCount-1) * ba.barGapFactor() // number of bar gaps + + 1 * ba.groupGapFactor(); // number of group gaps + else + units = 1 + 1 * ba.groupGapFactor(); + + double unitWidth = groupWidth / units; + outBarWidth = unitWidth; + outSpaceBetweenBars += unitWidth * ba.barGapFactor(); + + // Pending Michel - minLimit: allow space between bars to be reduced until the bars are displayed next to each other. + // is that what we want? + // sebsauer; in the case e.g. CartesianCoordinatePlane::setHorizontalRangeReversed(true) was + // used to reverse the values, we deal with negative outSpaceBetweenBars and unitWidth here + // and since that's correct we don't like to lose e.g. the spacing here. + //if ( outSpaceBetweenBars < 0 ) + // outSpaceBetweenBars = 0; + + outSpaceBetweenGroups += unitWidth * ba.groupGapFactor(); +} + +ReverseMapper& BarDiagram::BarDiagramType::reverseMapper() +{ + return m_private->reverseMapper; +} + +CartesianDiagramDataCompressor& BarDiagram::BarDiagramType::compressor() const +{ + return m_private->compressor; +} diff --git a/libkdchart/src/KDChartBarDiagram_p.h b/libkdchart/src/KDChartBarDiagram_p.h new file mode 100644 index 0000000..97566af --- /dev/null +++ b/libkdchart/src/KDChartBarDiagram_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTBARDIAGRAM_P_H +#define KDCHARTBARDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartBarDiagram.h" + +#include + +#include "KDChartAbstractCartesianDiagram_p.h" +#include "KDChartThreeDBarAttributes.h" + +#include + + +namespace KDChart { + +class PaintContext; + +/** + * \internal + */ +class BarDiagram::Private : public AbstractCartesianDiagram::Private +{ + friend class BarDiagram; + friend class BarDiagramType; + +public: + Private(); + Private( const Private& rhs ); + ~Private(); + +/* refactoring */ +/* + Private( const Private& rhs ) : + AbstractCartesianDiagram::Private( rhs ), + barType( rhs.barType ), + maxDepth( rhs.maxDepth ) + { + } + + void calculateValueAndGapWidths( int rowCount, int colCount, + double groupWidth, + double& barWidth, + double& spaceBetweenBars, + double& spaceBetweenGroups ); +*/ + + Qt::Orientation orientation; + + BarDiagram* diagram; + BarDiagramType* implementor; // the current type + BarDiagramType* normalDiagram; + BarDiagramType* stackedDiagram; + BarDiagramType* percentDiagram; + BarDiagramType* normalLyingDiagram; + BarDiagramType* stackedLyingDiagram; + BarDiagramType* percentLyingDiagram; + +/* refactoring */ +/* + BarType barType; +private: + double maxDepth; +*/ +}; + +KDCHART_IMPL_DERIVED_DIAGRAM( BarDiagram, AbstractCartesianDiagram, CartesianCoordinatePlane ) + + // we inherit privately, so that derived classes cannot call the + // base class functions - those reference the wrong (unattached to + // a diagram) d + class BarDiagram::BarDiagramType : private BarDiagram::Private + { + public: + explicit BarDiagramType( BarDiagram* d ) + : BarDiagram::Private() + , m_private( d->d_func() ) + { + } + virtual ~BarDiagramType() {} + virtual BarDiagram::BarType type() const = 0; + virtual const QPair calculateDataBoundaries() const = 0; + virtual void paint( PaintContext* ctx ) = 0; + BarDiagram* diagram() const; + + protected: + // method that make elements of m_private available to derived + // classes: + AttributesModel* attributesModel() const; + QModelIndex attributesModelRootIndex() const; + ReverseMapper& reverseMapper(); + CartesianDiagramDataCompressor& compressor() const; + + void appendDataValueTextInfoToList( + AbstractDiagram * diagram, + DataValueTextInfoList & list, + const QModelIndex & index, + const PositionPoints& points, + const Position& autoPositionPositive, + const Position& autoPositionNegative, + const qreal value ); + void paintDataValueTextsAndMarkers( + AbstractDiagram* diag, + PaintContext* ctx, + const DataValueTextInfoList & list, + bool paintMarkers ); + + void paintBars( PaintContext* ctx, const QModelIndex& index, + const QRectF& bar, double& maxDepth ); + void calculateValueAndGapWidths( int rowCount, int colCount, + double groupWidth, + double& barWidth, + double& spaceBetweenBars, + double& spaceBetweenGroups ); + + BarDiagram::Private* m_private; + }; +} + +#endif /* KDCHARTBARDIAGRAM_P_H */ diff --git a/libkdchart/src/KDChartCartesianAxis.cpp b/libkdchart/src/KDChartCartesianAxis.cpp new file mode 100644 index 0000000..6379d95 --- /dev/null +++ b/libkdchart/src/KDChartCartesianAxis.cpp @@ -0,0 +1,1900 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartCartesianAxis.h" +#include "KDChartCartesianAxis_p.h" + +#include + +#include +#include +#include +#include +#include + +#include "KDChartPaintContext.h" +#include "KDChartChart.h" +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartAbstractGrid.h" +#include "KDChartPainterSaver_p.h" +#include "KDChartLayoutItems.h" +#include "KDChartBarDiagram.h" +#include "KDChartStockDiagram.h" +#include "KDChartLineDiagram.h" +#include "KDChartPrintingParameters.h" + +#include + +#include + +using namespace KDChart; + +#define d (d_func()) + +CartesianAxis::CartesianAxis ( AbstractCartesianDiagram* diagram ) + : AbstractAxis ( new Private( diagram, this ), diagram ) +{ + init(); +} + +CartesianAxis::~CartesianAxis () +{ + // when we remove the first axis it will unregister itself and + // propagate the next one to the primary, thus the while loop + while ( d->mDiagram ) { + AbstractCartesianDiagram *cd = qobject_cast( d->mDiagram ); + cd->takeAxis( this ); + } + Q_FOREACH( AbstractDiagram *diagram, d->secondaryDiagrams ) { + AbstractCartesianDiagram *cd = qobject_cast( diagram ); + cd->takeAxis( this ); + } +} + +void CartesianAxis::init () +{ + d->position = Bottom; + setCachedSizeDirty(); +} + + +bool CartesianAxis::compare( const CartesianAxis* other )const +{ + if( other == this ) return true; + if( ! other ){ + //qDebug() << "CartesianAxis::compare() cannot compare to Null pointer"; + return false; + } + /* + qDebug() << (position() == other->position()); + qDebug() << (titleText() == other->titleText()); + qDebug() << (titleTextAttributes() == other->titleTextAttributes()); + */ + return ( static_cast(this)->compare( other ) ) && + ( position() == other->position() ) && + ( titleText() == other->titleText() ) && + ( titleTextAttributes() == other->titleTextAttributes() ); +} + + +void CartesianAxis::setTitleText( const QString& text ) +{ + d->titleText = text; + layoutPlanes(); +} + +QString CartesianAxis::titleText() const +{ + return d->titleText; +} + +void CartesianAxis::setTitleTextAttributes( const TextAttributes &a ) +{ + d->titleTextAttributes = a; + d->useDefaultTextAttributes = false; + layoutPlanes(); +} + +TextAttributes CartesianAxis::titleTextAttributes() const +{ + if( hasDefaultTitleTextAttributes() ){ + TextAttributes ta( textAttributes() ); + Measure me( ta.fontSize() ); + me.setValue( me.value() * 1.5 ); + ta.setFontSize( me ); + return ta; + } + return d->titleTextAttributes; +} + +void CartesianAxis::resetTitleTextAttributes() +{ + d->useDefaultTextAttributes = true; + layoutPlanes(); +} + +bool CartesianAxis::hasDefaultTitleTextAttributes() const +{ + return d->useDefaultTextAttributes; +} + + +void CartesianAxis::setPosition ( Position p ) +{ + d->position = p; + layoutPlanes(); +} + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +CartesianAxis::Position CartesianAxis::position() const +{ + return d->position; +} + +void CartesianAxis::layoutPlanes() +{ + //qDebug() << "CartesianAxis::layoutPlanes()"; + if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) { + //qDebug() << "CartesianAxis::layoutPlanes(): Sorry, found no plane."; + return; + } + AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane(); + if( plane ){ + plane->layoutPlanes(); + //qDebug() << "CartesianAxis::layoutPlanes() OK"; + } +} + +/* + void CartesianAxis::paintEvent( QPaintEvent* event ) + { + Q_UNUSED( event ); + + if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) return; + + PaintContext context; + QPainter painter( this ); + context.setPainter( &painter ); + AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane(); + context.setCoordinatePlane( plane ); + QRectF rect = QRectF ( 1, 1, plane->width() - 3, plane->height() - 3 ); + context.setRectangle( rect ); + d->geometry.setSize( size() ); + paintCtx( &context ); + } +*/ + + +static bool referenceDiagramIsBarDiagram( const AbstractDiagram * diagram ) +{ + const AbstractCartesianDiagram * dia = + qobject_cast< const AbstractCartesianDiagram * >( diagram ); + if( dia && dia->referenceDiagram() ) + dia = dia->referenceDiagram(); + return qobject_cast< const BarDiagram* >( dia ) != 0; +} + +static bool referenceDiagramNeedsCenteredAbscissaTicks( const AbstractDiagram *diagram ) +{ + const AbstractCartesianDiagram * dia = + qobject_cast< const AbstractCartesianDiagram * >( diagram ); + if( dia && dia->referenceDiagram() ) + dia = dia->referenceDiagram(); + if ( qobject_cast< const BarDiagram* >( dia ) ) + return true; + if ( qobject_cast< const StockDiagram* >( dia ) ) + return true; + + const LineDiagram * lineDiagram = qobject_cast< const LineDiagram* >( dia ); + return lineDiagram && lineDiagram->centerDataPoints(); +} + +bool CartesianAxis::isAbscissa() const +{ + const Qt::Orientation diagramOrientation = referenceDiagramIsBarDiagram( d->diagram() ) ? ((BarDiagram*)(d->diagram()))->orientation() + : Qt::Vertical; + return diagramOrientation == Qt::Vertical ? position() == Bottom || position() == Top + : position() == Left || position() == Right; +} + +bool CartesianAxis::isOrdinate() const +{ + const Qt::Orientation diagramOrientation = referenceDiagramIsBarDiagram( d->diagram() ) ? ((BarDiagram*)(d->diagram()))->orientation() + : Qt::Vertical; + return diagramOrientation == Qt::Vertical ? position() == Left || position() == Right + : position() == Bottom || position() == Top; +} + +void CartesianAxis::paint( QPainter* painter ) +{ + if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) return; + PaintContext ctx; + ctx.setPainter ( painter ); + ctx.setCoordinatePlane( d->diagram()->coordinatePlane() ); + const QRect rect( areaGeometry() ); + + //qDebug() << "CartesianAxis::paint( QPainter* painter ) " << " areaGeometry()():" << rect << " sizeHint():" << sizeHint(); + + ctx.setRectangle( + QRectF ( + //QPointF(0, 0), + QPointF(rect.left(), rect.top()), + QSizeF(rect.width(), rect.height() ) ) ); + // enabling clipping so that we're not drawing outside + QRegion clipRegion( rect.adjusted( -1, -1, 1, 1 ) ); + painter->save(); + painter->setClipRegion( clipRegion ); + paintCtx( &ctx ); + painter->restore(); + //qDebug() << "KDChart::CartesianAxis::paint() done."; +} + +void CartesianAxis::Private::drawSubUnitRulers( QPainter* painter, CartesianCoordinatePlane* plane, const DataDimension& dim, + const QPointF& rulerRef, const QVector& drawnTicks, const bool diagramIsVertical, + const RulerAttributes& rulerAttr ) const +{ + const QRect geoRect( axis()->geometry() ); + int nextMayBeTick = 0; + int mayBeTick = 0; + int logSubstep = 0; + qreal f = dim.start; + qreal fLogSubstep = f; + const bool isAbscissa = axis()->isAbscissa(); + const bool isLogarithmic = (dim.calcMode == AbstractCoordinatePlane::Logarithmic ); + const int subUnitTickLength = axis()->tickLength( true ); + + // Use negative limit to ensure that also the last tick is painted, + // which is needed if major tick marks are disabled + while ( dim.end - f > -std::numeric_limits< float >::epsilon() ) { + const qreal quotient = f / dim.stepWidth; + const bool isMinorTickMark = qAbs(qRound(quotient) - quotient) > std::numeric_limits< float >::epsilon(); + // 'Drawn' ticks isn't quite the right naming here, it also counts major tick marks, which are not drawn. + if( drawnTicks.count() > nextMayBeTick ) + mayBeTick = drawnTicks[ nextMayBeTick ]; + // Paint minor tick mark only if there is no major tick mark drawn at this point + if ( isMinorTickMark || !rulerAttr.showMajorTickMarks() ) { + if ( isAbscissa ) { + // for the x-axis + QPointF topPoint = diagramIsVertical ? QPointF( f, 0 ) : QPointF( 0, f ); + QPointF bottomPoint( topPoint ); + // we don't draw the sub ticks, if we are at the same position as a normal tick + topPoint = plane->translate( topPoint ); + bottomPoint = plane->translate( bottomPoint ); + if ( diagramIsVertical ) { + topPoint.setY( rulerRef.y() + subUnitTickLength ); + bottomPoint.setY( rulerRef.y() ); + } else { + topPoint.setX( rulerRef.x() + subUnitTickLength ); + bottomPoint.setX( rulerRef.x() ); + } + if( qAbs( mayBeTick - topPoint.x() ) > 1 ) + { + if ( rulerAttr.hasTickMarkPenAt( topPoint.x() ) ) + painter->setPen( rulerAttr.tickMarkPen( topPoint.x() ) ); + else + painter->setPen( rulerAttr.minorTickMarkPen() ); + painter->drawLine( topPoint, bottomPoint ); + } + else { + ++nextMayBeTick; + } + } else { + // for the y-axis + + QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, f ) : QPointF( f, 0 ) ); + //qDebug() << "geoRect:" << geoRect << " geoRect.top()" << geoRect.top() << "geoRect.bottom()" << geoRect.bottom() << " translatedValue:" << translatedValue; + // we don't draw the sub ticks, if we are at the same position as a normal tick + if( qAbs( mayBeTick - diagramIsVertical ? leftPoint.y() : leftPoint.x() ) > 1 ){ + const qreal translatedValue = leftPoint.y(); + bool translatedValueIsWithinBoundaries; + if ( diagramIsVertical ) { + translatedValueIsWithinBoundaries = translatedValue > geoRect.top() && translatedValue <= geoRect.bottom(); + } else { + translatedValueIsWithinBoundaries = translatedValue > geoRect.left() && translatedValue <= geoRect.right(); + } + if( translatedValueIsWithinBoundaries ){ + QPointF rightPoint = diagramIsVertical ? QPointF( 0, f ) : QPointF( f, 0 ); + rightPoint = plane->translate( rightPoint ); + if ( diagramIsVertical ) { + leftPoint.setX( rulerRef.x() + subUnitTickLength ); + rightPoint.setX( rulerRef.x() ); + } else { + leftPoint.setY( rulerRef.y() + (position == Bottom ? subUnitTickLength : -subUnitTickLength) ); + rightPoint.setY( rulerRef.y() ); + } + if ( rulerAttr.hasTickMarkPenAt( f ) ) + painter->setPen( rulerAttr.tickMarkPen( f ) ); + else + painter->setPen( rulerAttr.minorTickMarkPen() ); + painter->drawLine( leftPoint, rightPoint ); + } + } else { + ++nextMayBeTick; + } + } + } + if ( isLogarithmic ){ + if( logSubstep == 9 ){ + fLogSubstep *= ( fLogSubstep > 0.0 ) ? 10.0 : 0.1; + if( fLogSubstep == 0 ) + fLogSubstep = 0.01; + logSubstep = 0; + f = fLogSubstep; + } + else + { + f += fLogSubstep; + } + ++logSubstep; + }else{ + f += dim.subStepWidth; + } + } +} + + +const TextAttributes CartesianAxis::Private::titleTextAttributesWithAdjustedRotation() const +{ + TextAttributes titleTA( titleTextAttributes ); + if( (position == Left || position == Right) ){ + int rotation = titleTA.rotation() + 270; + if( rotation >= 360 ) + rotation -= 360; + + // limit the allowed values to 0, 90, 180, 270: + if( rotation < 90 ) + rotation = 0; + else if( rotation < 180 ) + rotation = 90; + else if( rotation < 270 ) + rotation = 180; + else if( rotation < 360 ) + rotation = 270; + else + rotation = 0; + + titleTA.setRotation( rotation ); + } + return titleTA; +} + +void CartesianAxis::setTitleSpace( qreal axisTitleSpace ) +{ + d->axisTitleSpace = axisTitleSpace; +} + +qreal CartesianAxis::titleSpace() const +{ + return d->axisTitleSpace; +} + +void CartesianAxis::Private::drawTitleText( QPainter* painter, CartesianCoordinatePlane* plane, const QRect& areaGeoRect ) const +{ + const TextAttributes titleTA( titleTextAttributesWithAdjustedRotation() ); + if( titleTA.isVisible() ) { + TextLayoutItem titleItem( titleText, + titleTA, + plane->parent(), + KDChartEnums::MeasureOrientationMinimum, + Qt::AlignHCenter|Qt::AlignVCenter ); + QPointF point; + QSize size( titleItem.sizeHint() ); + //FIXME(khz): We definitely need to provide a way that users can decide + // the position of an axis title. + switch( position ) + { + case Top: + point.setX( areaGeoRect.left() + areaGeoRect.width() / 2.0); + point.setY( areaGeoRect.top() + (size.height() / 2)*1/axisTitleSpace ); + size.setWidth( qMin( size.width(), axis()->geometry().width() ) ); + break; + case Bottom: + point.setX( areaGeoRect.left() + areaGeoRect.width() / 2.0); + point.setY( areaGeoRect.bottom() - (size.height()/2)*1/axisTitleSpace); + size.setWidth( qMin( size.width(), axis()->geometry().width() ) ); + break; + case Left: + point.setX( areaGeoRect.left() + (size.width() / 2)*1/axisTitleSpace); + point.setY( areaGeoRect.top() + areaGeoRect.height() / 2.0); + size.setHeight( qMin( size.height(), axis()->geometry().height() ) ); + break; + case Right: + point.setX( areaGeoRect.right() - (size.width() / 2)*1/axisTitleSpace); + point.setY( areaGeoRect.top() + areaGeoRect.height() / 2.0); + size.setHeight( qMin( size.height(), axis()->geometry().height() ) ); + break; + } + const PainterSaver painterSaver( painter ); + painter->translate( point ); + //if( axis()->isOrdinate() ) + // painter->rotate( 270.0 ); + titleItem.setGeometry( QRect( QPoint(-size.width() / 2, -size.height() / 2), size ) ); + //painter->drawRect(titleItem.geometry().adjusted(0,0,-1,-1)); + titleItem.paint( painter ); + } +} + + +static void calculateNextLabel( qreal& labelValue, qreal step, bool isLogarithmic, qreal min ) +{ + if ( isLogarithmic ){ + if( step > 0.0 ) + labelValue *= 10.0; + else + labelValue /= 10.0; + if( labelValue == 0.0 ) + labelValue = pow( 10.0, floor( log10( min ) ) ); + }else{ + //qDebug() << "new axis label:" << labelValue << "+" << step << "=" << labelValue+step; + labelValue += step; + if( qAbs(labelValue) < 1.0e-15 ) + labelValue = 0.0; + } +} + + +void CartesianAxis::paintCtx( PaintContext* context ) +{ + + Q_ASSERT_X ( d->diagram(), "CartesianAxis::paint", + "Function call not allowed: The axis is not assigned to any diagram." ); + + CartesianCoordinatePlane* plane = dynamic_cast(context->coordinatePlane()); + Q_ASSERT_X ( plane, "CartesianAxis::paint", + "Bad function call: PaintContext::coodinatePlane() NOT a cartesian plane." ); + + // note: Not having any data model assigned is no bug + // but we can not draw an axis then either. + if( ! d->diagram()->model() ) + return; + + // Determine the diagram that specifies the orientation of the diagram we're painting here + // That diagram is the reference diagram, if it exists, or otherwise the diagram itself. + // Note: In KDChart 2.3 or earlier, only a bar diagram can be vertical instead of horizontal. + const AbstractCartesianDiagram * refDiagram = qobject_cast< const AbstractCartesianDiagram * >( d->diagram() ); + if( refDiagram && refDiagram->referenceDiagram() ) + refDiagram = refDiagram->referenceDiagram(); + const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( refDiagram ); + const Qt::Orientation diagramOrientation = barDiagram ? barDiagram->orientation() : Qt::Vertical; + const bool diagramIsVertical = diagramOrientation == Qt::Vertical; + + /* + * let us paint the labels at a + * smaller resolution + * Same mini pixel value as for + * Cartesian Grid + */ + //const qreal MinimumPixelsBetweenRulers = 1.0; + DataDimensionsList dimensions( plane->gridDimensionsList() ); + //qDebug("CartesianAxis::paintCtx() gets DataDimensionsList.first(): start: %f end: %f stepWidth: %f", dimensions.first().start, dimensions.first().end, dimensions.first().stepWidth); + + // test for programming errors: critical + Q_ASSERT_X ( dimensions.count() == 2, "CartesianAxis::paint", + "Error: plane->gridDimensionsList() did not return exactly two dimensions." ); + DataDimension dimX, dimY; + DataDimension dim; + // If the diagram is horizontal, we need to inverse the x/y ranges + if ( diagramIsVertical ) { + /*double yStart = dimY.start; + double yEnd = dimY.end; + dimY.start = dimX.start; + dimY.end = dimX.end; + dimX.start = yStart; + dimX.end = yEnd;*/ + dimX = AbstractGrid::adjustedLowerUpperRange( dimensions.first(), true, true ); + dimY = AbstractGrid::adjustedLowerUpperRange( dimensions.last(), true, true ); + + // FIXME + // Ugly workaround for dimensions being bound to both, the x coordinate direction and the abscissa + //if ( referenceDiagramIsPercentLyingBarDiagram ) { + // dimY.stepWidth = 10.0; + // dimY.subStepWidth = 2.0; + //} + } else { + dimX = AbstractGrid::adjustedLowerUpperRange( dimensions.last(), true, true ); + dimY = AbstractGrid::adjustedLowerUpperRange( dimensions.first(), true, true ); + } + dim = (isAbscissa() ? dimX : dimY); + + /* + if(isAbscissa()) + qDebug() << " " << "Abscissa:" << dimX.start <<".."<diagram()->model()->rowCount(d->diagram()->rootIndex()) - 1.0; + }else{ + numberOfUnitRulers = absRange / qAbs( dimY.stepWidth ) + 1.0; + } + + // qDebug() << "absRange" << absRange << "dimY.stepWidth:" << dimY.stepWidth << "numberOfUnitRulers:" << numberOfUnitRulers; + + qreal numberOfSubUnitRulers; + if ( isAbscissa() ){ + if( dimX.isCalculated ) + numberOfSubUnitRulers = absRange / qAbs( dimX.subStepWidth ) + 1.0; + else + numberOfSubUnitRulers = dimX.subStepWidth>0 ? absRange / qAbs( dimX.subStepWidth ) + 1.0 : 0.0; + }else{ + numberOfSubUnitRulers = absRange / qAbs( dimY.subStepWidth ) + 1.0; + } + + // - calculate the absolute range in screen pixels: + const QPointF p1 = plane->translate( diagramIsVertical ? QPointF(dimX.start, dimY.start) : QPointF(dimY.start, dimX.start) ); + const QPointF p2 = plane->translate( diagramIsVertical ? QPointF(dimX.end, dimY.end) : QPointF(dimY.end, dimX.end ) ); + + double screenRange; + if ( isAbscissa() ) + screenRange = qAbs ( p1.x() - p2.x() ); + else + screenRange = qAbs ( p1.y() - p2.y() ); + + const bool useItemCountLabels = isAbscissa() && ! dimX.isCalculated; + + // attributes used to customize ruler appearance + const RulerAttributes rulerAttr = rulerAttributes(); + + const bool drawUnitRulers = rulerAttr.showMajorTickMarks() && (screenRange / ( numberOfUnitRulers / dimX.stepWidth ) > MinimumPixelsBetweenRulers); + const bool drawSubUnitRulers = rulerAttr.showMinorTickMarks() && + (numberOfSubUnitRulers != 0.0) && + (screenRange / numberOfSubUnitRulers > MinimumPixelsBetweenRulers); + + const TextAttributes labelTA = textAttributes(); + const bool drawLabels = labelTA.isVisible(); + + // - find the reference point at which to start drawing and the increment (line distance); + QPointF rulerRef; + const QRect areaGeoRect( areaGeometry() ); + const QRect geoRect( geometry() ); + QRectF rulerRect; + + QPainter* const ptr = context->painter(); + + //for debugging: if( isAbscissa() )ptr->drawRect(areaGeoRect.adjusted(0,0,-1,-1)); + //qDebug() << " " << (isAbscissa() ? "Abscissa":"Ordinate") << "axis painting with geometry" << areaGeoRect; + + // FIXME references are of course different for all locations: + switch( position() ) + { + case Top: + rulerRef.setX( areaGeoRect.topLeft().x() ); + rulerRef.setY( areaGeoRect.topLeft().y() + areaGeoRect.height() ); + break; + case Bottom: + rulerRef.setX( areaGeoRect.bottomLeft().x() ); + rulerRef.setY( areaGeoRect.bottomLeft().y() - areaGeoRect.height() ); + break; + case Right: + rulerRef.setX( areaGeoRect.bottomRight().x() - areaGeoRect.width() ); + rulerRef.setY( areaGeoRect.bottomRight().y() ); + break; + case Left: + rulerRef.setX( areaGeoRect.bottomLeft().x() + areaGeoRect.width() ); + rulerRef.setY( areaGeoRect.bottomLeft().y() ); + break; + } + + // set up the lines to paint: + + // set up a map of integer positions, + + // - starting with the fourth + // - the the halfs + // - then the tens + // this will override all halfs and fourth that hit a higher-order ruler + // MAKE SURE TO START AT (0, 0)! + + // set up a reference point, a step vector and a unit vector for the drawing: + + const qreal minValueY = dimY.start; + const qreal maxValueY = dimY.end; + const qreal minValueX = dimX.start; + const qreal maxValueX = dimX.end; + const bool isLogarithmicX = (dimX.calcMode == AbstractCoordinatePlane::Logarithmic ); + const bool isLogarithmicY = (dimY.calcMode == AbstractCoordinatePlane::Logarithmic ); +//#define AXES_PAINTING_DEBUG 1 +#ifdef AXES_PAINTING_DEBUG + qDebug() << "CartesianAxis::paint: reference values:" << endl + << "-- range x/y: " << dimX.distance() << "/" << dimY.distance() << endl + << "-- absRange: " << absRange << endl + << "-- numberOfUnitRulers: " << numberOfUnitRulers << endl + << "-- screenRange: " << screenRange << endl + << "-- drawUnitRulers: " << drawUnitRulers << endl + << "-- drawLabels: " << drawLabels << endl + << "-- ruler reference point:: " << rulerRef << endl + << "-- minValueX: " << minValueX << " maxValueX: " << maxValueX << endl + << "-- minValueY: " << minValueY << " maxValueY: " << maxValueY << endl + ; +#endif + + // solving issue #4075 in a quick way: + ptr->setPen ( PrintingParameters::scalePen( labelTA.pen() ) ); // perhaps we want to add a setter method later? + + //ptr->setPen ( Qt::black ); + + const QObject* referenceArea = plane->parent(); + + // that QVector contains all drawn x-ticks (so no subticks are drawn there also) + QVector< int > drawnAbscissaTicks; + // and that does the same for the y-ticks + QVector< int > drawnYTicks; + + /* + * Find out if it is a bar diagram + * bar diagrams display their data per column + * we need to handle the last label another way + * 1 - Last label == QString null ( Header Labels ) + * 2 - Display labels and ticks in the middle of the column + */ + + const bool centerAbscissaTicks = referenceDiagramNeedsCenteredAbscissaTicks( d->diagram() ); + + // this draws the unit rulers + if ( drawUnitRulers ) { + const QStringList labelsList( labels() ); + const QStringList shortLabelsList( shortLabels() ); + const int hardLabelsCount = labelsList.count(); + const int shortLabelsCount = shortLabelsList.count(); + bool useShortLabels = false; + + bool useConfiguredStepsLabels = false; + QStringList headerLabels; + if( useItemCountLabels ){ + //qDebug() << (isOrdinate() ? "is Ordinate" : "is Abscissa"); + headerLabels = + isOrdinate() + ? d->diagram()->datasetLabels() + : d->diagram()->itemRowLabels(); + //qDebug() << numberOfUnitRulers; + // check if configured stepWidth + useConfiguredStepsLabels = isAbscissa() && + dimX.stepWidth && + (( (headerLabels.count() - 1)/ dimX.stepWidth ) != numberOfUnitRulers); + if( useConfiguredStepsLabels ) { + numberOfUnitRulers = ( headerLabels.count() - 1 )/ dimX.stepWidth; + // we need to register data values for the steps + // in case it is configured by the user + QStringList configuredStepsLabels; + double value = dimX.start;// headerLabels.isEmpty() ? 0.0 : headerLabels.first().toDouble(); + configuredStepsLabels << QString::number( value ); + + for( int i = 0; i < numberOfUnitRulers; i++ ) + { + //qDebug() << value; + value += dimX.stepWidth; + configuredStepsLabels.append( d->diagram()->unitPrefix( i, diagramIsVertical ? Qt::Horizontal : Qt::Vertical, true ) + + QString::number( value ) + + d->diagram()->unitSuffix( i, diagramIsVertical ? Qt::Horizontal : Qt::Vertical, true ) ); + } + headerLabels = configuredStepsLabels; + } + + if ( centerAbscissaTicks ) + headerLabels.append( QString() ); + } + RulerAttributes rulerAttr = rulerAttributes(); + if ( rulerAttr.showRulerLine() ) + { + QPointF start; + QPointF end; + switch( position() ) + { + case( CartesianAxis::Bottom ): + start = QPointF( dimX.start, dimY.start ); + end = QPointF( dimX.end, dimY.start ); + break; + case( CartesianAxis::Top ): + start = QPointF( dimX.start, dimY.end ); + end = QPointF( dimX.end, dimY.end ); + break; + case( CartesianAxis::Left ): + start = QPointF( dimX.start, dimY.start ); + end = QPointF( dimX.start, dimY.end ); + break; + case( CartesianAxis::Right ): + start = QPointF( dimX.end, dimY.start ); + end = QPointF( dimX.end, dimY.end ); + break; + } + start = plane->translate( start ); + end = plane->translate( end ); + bool clip = context->painter()->hasClipping(); + context->painter()->setClipping( false ); + context->painter()->drawLine( start, end ); + context->painter()->setClipping( clip ); + + } + + const int headerLabelsCount = headerLabels.count(); + //qDebug() << "headerLabelsCount" << headerLabelsCount; + + TextLayoutItem* labelItem = + drawLabels + ? new TextLayoutItem( QString::number( minValueY ), + labelTA, + referenceArea, + KDChartEnums::MeasureOrientationMinimum, + Qt::AlignLeft ) + : 0; + labelItem->setTextAttributes( textAttributes() ); + TextLayoutItem* labelItem2 = + drawLabels + ? new TextLayoutItem( QString::number( minValueY ), + labelTA, + referenceArea, + KDChartEnums::MeasureOrientationMinimum, + Qt::AlignLeft ) + : 0; + labelItem2->setTextAttributes( textAttributes() ); + const QFontMetricsF met( + drawLabels + ? labelItem->realFont() + : QFontMetricsF( QApplication::font(), GlobalMeasureScaling::paintDevice() ) ); + const qreal halfFontHeight = rulerAttr.labelMargin() >= 0 ? rulerAttr.labelMargin() : met.height() * 0.5; + const qreal halfFontWidth = rulerAttr.labelMargin() >= 0 ? rulerAttr.labelMargin() : met.averageCharWidth() * 0.5; + + if ( isAbscissa() ) { + //Draw ticks at custom postions on x-axis + if( !d->customTicksPositions.isEmpty() ) + { + const QList< double > values = d->customTicksPositions; + KDAB_FOREACH( const double v, values ) + { + QPointF topPoint = diagramIsVertical ? QPointF( v, 0.0 ) : QPointF( 0.0, v ); + QPointF bottomPoint = topPoint; + topPoint = plane->translate( topPoint ); + bottomPoint = plane->translate( bottomPoint ); + if ( diagramIsVertical ) { + topPoint.setY( rulerRef.y() + tickLength() ); + bottomPoint.setY( rulerRef.y() ); + } else { + topPoint.setX( rulerRef.x() + tickLength() ); + bottomPoint.setX( rulerRef.x() ); + } + + context->painter()->drawLine(topPoint, bottomPoint); + } + } + + if( !d->annotations.isEmpty() ) + { + const QList< double > values = d->annotations.keys(); + KDAB_FOREACH( const double v, values ) + { + QPointF topPoint = diagramIsVertical ? QPointF( v, 0.0 ) : QPointF( 0.0, v ); + QPointF bottomPoint = topPoint; + topPoint = plane->translate( topPoint ); + bottomPoint = plane->translate( bottomPoint ); + if ( diagramIsVertical ) { + topPoint.setY( rulerRef.y() + tickLength() ); + bottomPoint.setY( rulerRef.y() ); + } else { + topPoint.setX( rulerRef.x() + tickLength() ); + bottomPoint.setX( rulerRef.x() ); + } + + labelItem->setText( d->annotations[ v ] ); + const QSize size( labelItem->sizeHint() ); + if ( diagramIsVertical ) { + labelItem->setGeometry( + QRect( + QPoint( + static_cast( topPoint.x() - size.width() / 2.0 ), + static_cast( topPoint.y() + + ( position() == Bottom + ? halfFontHeight + : ((halfFontHeight + size.height()) * -1.0) ) ) ), + size ) ); + } else { + labelItem->setGeometry( + QRect( + QPoint( + static_cast( bottomPoint.x() + + ( position() == Right + ? halfFontWidth + : (-halfFontWidth - size.width()) ) ), + + static_cast( topPoint.y() - ( size.height() ) * 0.5 ) ), + size ) ); + } + + QRect labelGeo = labelItem->geometry(); + // if our item would only half fit, we disable clipping for that one + if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() ) + ptr->setClipping( false ); + else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() ) + ptr->setClipping( false ); + + labelItem->paint( ptr ); + } + } + + qreal labelDiff = dimX.stepWidth; + const int precision = ( QString::number( labelDiff ).section( QLatin1Char('.'), 1, 2 ) ).length(); + + // If we have a labels list AND a short labels list, we first find out, + // if there is enough space for showing ALL of the long labels: + // If not, use the short labels. + if( drawLabels && hardLabelsCount > 0 && shortLabelsCount > 0 && d->annotations.isEmpty() ){ + bool labelsAreOverlapping = false; + int iLabel = 0; + qreal i = minValueX; + while ( i < maxValueX-1 && !labelsAreOverlapping ) + { + const int idx = (iLabel < hardLabelsCount ) ? iLabel : 0; + const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0; + if ( dimX.stepWidth != 1.0 && ! dim.isCalculated ) + { + // Check intersects for the header label - we need to pass the full string + // here and not only the i value. + if( useConfiguredStepsLabels ){ + labelItem->setText( customizedLabel(headerLabels[ idx ]) ); + labelItem2->setText( customizedLabel(headerLabels[ idx2 ]) ); + }else{ + //qDebug() << "i + labelDiff " << i + labelDiff; + labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ? + headerLabels[static_cast(i)] : + QString::number( i, 'f', precision )) ); + // qDebug() << "1 - labelItem->text() " << labelItem->text(); + //qDebug() << "labelDiff" << labelDiff + // << " index" << i+labelDiff << " count" << headerLabelsCount; + labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ? + headerLabels[static_cast(i+labelDiff)] : + QString::number( i + labelDiff, 'f', precision )) ); + //qDebug() << "2 - labelItem->text() " << labelItem->text(); + } + } else { + //qDebug() << iLabel << i << "("<setText( customizedLabel( + useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) ); + labelItem2->setText( customizedLabel( + useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) ); + } + + QPointF firstPos = diagramIsVertical ? QPointF( i, 0.0 ) : QPointF( 0.0, i ); + firstPos = plane->translate( firstPos ); + + QPointF secondPos = diagramIsVertical ? QPointF( i + labelDiff, 0.0 ) : QPointF( 0.0, i + labelDiff ); + secondPos = plane->translate( secondPos ); + + labelsAreOverlapping = labelItem->intersects( *labelItem2, firstPos, secondPos ); + + if ( ++iLabel > hardLabelsCount - 1 ) + iLabel = 0; + if ( isLogarithmicX ) + i *= 10.0; + else + i += dimX.stepWidth; + //qDebug() << labelsAreOverlapping << iLabel << i << labelsAreOverlapping << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text(); + } + + useShortLabels = labelsAreOverlapping; + } + + // qDebug() << "initial labelDiff " << labelDiff; + if ( drawLabels && d->annotations.isEmpty() ) + { + qreal i = minValueX; + int iLabel = 0; + + while ( i + labelDiff < maxValueX ) + { + const int idx = (iLabel < hardLabelsCount ) ? iLabel : 0; + const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0; + //qDebug() << "drawLabels" << drawLabels << " hardLabelsCount" << hardLabelsCount + // << " dimX.stepWidth" << dimX.stepWidth << " dim.isCalculated" << dim.isCalculated; + if ( !drawLabels || hardLabelsCount < 1 || ( dimX.stepWidth != 1.0 && ! dim.isCalculated ) ) + { + // Check intersects for the header label - we need to pass the full string + // here and not only the i value. + if( useConfiguredStepsLabels ){ + labelItem->setText( customizedLabel(headerLabels[ idx ]) ); + labelItem2->setText( customizedLabel(headerLabels[ idx2 ]) ); + }else{ + //qDebug() << "i + labelDiff " << i + labelDiff; + labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ? + headerLabels[static_cast(i)] : + QString::number( i, 'f', precision )) ); + // qDebug() << "1 - labelItem->text() " << labelItem->text(); + //qDebug() << "labelDiff" << labelDiff + // << " index" << i+labelDiff << " count" << headerLabelsCount; + labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ? + headerLabels[static_cast(i+labelDiff)] : + QString::number( i + labelDiff, 'f', precision )) ); + //qDebug() << "2 - labelItem->text() " << labelItem->text(); + } + } else { + const int shortIdx = (iLabel < shortLabelsCount ) ? iLabel : 0; + const int shortIdx2 = (iLabel < shortLabelsCount - 1) ? iLabel + 1 : 0; + labelItem->setText( customizedLabel( + useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) ); + labelItem2->setText( customizedLabel( + useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) ); + } + + QPointF firstPos = diagramIsVertical ? QPointF( i, 0.0 ) : QPointF( 0.0, i ); + firstPos = plane->translate( firstPos ); + + QPointF secondPos = diagramIsVertical ? QPointF( i + labelDiff, 0.0 ) : QPointF( 0.0, i + labelDiff ); + secondPos = plane->translate( secondPos ); + + if ( labelItem->intersects( *labelItem2, firstPos, secondPos ) ) + { + i = minValueX; + + // fix for issue #4179: + labelDiff *= 10.0; + // old code: labelDiff += labelDiff; + + iLabel = 0; + //qDebug() << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text() << labelDiff; + } + else + { + i += labelDiff; + //qDebug() << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text(); + } + + if ( (++iLabel > hardLabelsCount - 1) && !useConfiguredStepsLabels ) + { + iLabel = 0; + } + } + // fixing bugz issue #5018 without breaking issue #4179: + if( minValueX + labelDiff > maxValueX ) + labelDiff = maxValueX - minValueX; + // This makes sure the first and the last X label are drawn + // if there is not enouth place to draw some more of them + // according to labelDiff calculation performed above. + } + + int idxLabel = 0; + qreal iLabelF = minValueX; + //qDebug() << iLabelF; + qreal i = minValueX; + qreal labelStep = 0.0; + // qDebug() << "dimX.stepWidth:" << dimX.stepWidth << "labelDiff:" << labelDiff; + //dimX.stepWidth = 0.5; + while( i <= maxValueX && d->annotations.isEmpty() ) + { + // Line charts: we want the first tick to begin at 0.0 not at 0.5 otherwise labels and + // values does not fit each others + QPointF topPoint = diagramIsVertical ? QPointF( i + ( centerAbscissaTicks ? 0.5 : 0.0 ), 0.0 ) : QPointF( 0.0, i + ( centerAbscissaTicks ? 0.5 : 0.0 ) ); + QPointF bottomPoint ( topPoint ); + topPoint = plane->translate( topPoint ); + bottomPoint = plane->translate( bottomPoint ); + if ( diagramIsVertical ) { + topPoint.setY( rulerRef.y() + tickLength() ); + bottomPoint.setY( rulerRef.y() ); + } else { + bottomPoint.setX( rulerRef.x() - (position() == Left ? tickLength() : -tickLength()) ); + topPoint.setX( rulerRef.x() ); + } + + const qreal translatedValue = diagramIsVertical ? topPoint.x() : topPoint.y(); + bool bIsVisibleLabel; + if ( diagramIsVertical ) + bIsVisibleLabel = ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicX) || i != 0.0 ); + else + bIsVisibleLabel = ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicX) || i != 0.0 ); + + // fix for issue #4179: + bool painttick = bIsVisibleLabel && labelStep <= 0; + // old code: bool painttick = true; + + //Dont paint more ticks than we need + //when diagram type is Bar + if ( centerAbscissaTicks && i == maxValueX ) + painttick = false; + + if ( bIsVisibleLabel && painttick ) { + ptr->save(); + if ( rulerAttr.hasTickMarkPenAt( i ) ) + ptr->setPen( rulerAttr.tickMarkPen( i ) ); + else + ptr->setPen( rulerAttr.majorTickMarkPen() ); + ptr->drawLine( topPoint, bottomPoint ); + ptr->restore(); + } + + drawnAbscissaTicks.append( static_cast( diagramIsVertical ? topPoint.x() : topPoint.y() ) ); + if( drawLabels ) { + if( bIsVisibleLabel ){ + if ( isLogarithmicX ) + labelItem->setText( customizedLabel(QString::number( i ) ) ); + /* We don't need that + * it causes header labels to be skipped even if there is enough + * space for them to displayed. + * Commenting for now - I need to test more in details - Let me know if I am wrong here. + */ + /* + else if( (dimX.stepWidth != 1.0) && ! dimX.isCalculated ) { + labelItem->setText( customizedLabel(QString::number( i, 'f', 0 )) ); + } + */ + else { + int idx = idxLabel + static_cast(minValueX); + if( hardLabelsCount ){ + if( useShortLabels ){ + if( idx >= shortLabelsList.count() ) + idx = 0; + }else{ + if( idx >= labelsList.count() ) + idx = 0; + } + } + labelItem->setText( + customizedLabel( + hardLabelsCount + ? ( useShortLabels ? shortLabelsList[ idx ] : labelsList[ idx ] ) + : ( headerLabelsCount ? headerLabels[ idx ] : QString::number( iLabelF )))); + //qDebug() << "x - labelItem->text() " << labelItem->text() << headerLabelsCount; + } + // No need to call labelItem->setParentWidget(), since we are using + // the layout item temporarily only. + if( labelStep <= 0 ) { + const PainterSaver p( ptr ); + //const QSize size( labelItem->sizeHint() ); + QPoint topLeft, topRight, bottomRight, bottomLeft; + const QSize size( + labelItem->sizeHintAndRotatedCorners( + topLeft, topRight, bottomRight, bottomLeft) ); + const QSize sizeUnrotated( labelItem->sizeHintUnrotated() ); + const int rotation = labelTA.rotation(); + const bool rotPositive = (rotation > 0 && rotation < 180); + QPoint midOfSide(0,0); + int dX = 0; + int dY = 0; + if( rotation ){ + if( rotPositive ){ + midOfSide = (topLeft + bottomLeft) / 2; + dX = topLeft.x() - midOfSide.x(); + dY = bottomLeft.y() - midOfSide.y(); + }else{ + midOfSide = (topRight + bottomRight) / 2; + dX = midOfSide.x() - topLeft.x(); + dY = midOfSide.y() - topRight.y(); + } + } + /* + if( i == 2 ){ + qDebug()<<"------"<setPen( Qt::black ); + QRectF rect(topPoint, QSizeF(sizeUnrotated)); + ptr->drawRect( rect ); + ptr->drawRect( QRectF(topPoint, QSizeF(2,2)) ); + ptr->drawRect( QRectF(topPoint+topLeft, QSizeF(2,2)) ); + ptr->drawRect( QRectF(topPoint+bottomLeft, QSizeF(2,2)) ); + ptr->drawRect( QRectF(topPoint+bottomRight, QSizeF(2,2)) ); + ptr->drawRect( QRectF(topPoint+topRight, QSizeF(2,2)) ); + ptr->drawRect( QRectF(topPoint+midOfSide, QSizeF(2,2)) ); + ptr->setPen( Qt::green ); + rect = QRectF(topPoint, QSizeF(size)); + ptr->drawRect( rect ); + ptr->drawRect( QRectF(QPointF((rect.topLeft() + rect.bottomLeft()) / 2.0 - QPointF(2.0,2.0)), QSizeF(3.0,3.0)) ); + //ptr->drawRect( QRectF(QPointF((rect.topRight() + rect.bottomRight()) / 2.0 - QPointF(2.0,2.0)), QSizeF(3.0,3.0)) ); + } + */ + QPoint topLeftPt; + if( diagramIsVertical ){ + if( rotation ){ + topLeftPt = QPoint( + static_cast( topPoint.x() ) - dX, + static_cast( topPoint.y() - dY + + ( position() == Bottom + ? halfFontHeight + : ((halfFontHeight + size.height()) * -1.0) ) ) ); + }else{ + topLeftPt = QPoint( + static_cast( topPoint.x() - size.width() / 2.0 ), + static_cast( topPoint.y() + + ( position() == Bottom + ? halfFontHeight + : ((halfFontHeight + size.height()) * -1.0) ) ) ); + } + }else{ + if( rotation ){ + topLeftPt = QPoint( + static_cast( topPoint.x() ) + dX, + static_cast( topPoint.y() - dY + + ( position() == Bottom + ? halfFontHeight + : ((halfFontHeight + size.height()) * -1.0) ) ) ); + }else{ + topLeftPt = QPoint( + static_cast( bottomPoint.x() + + ( position() == Right + ? halfFontWidth + : (-halfFontWidth - size.width()) ) ), + static_cast( topPoint.y() - ( size.height() ) * 0.5 ) ); + } + } + labelItem->setGeometry( QRect(topLeftPt, size) ); + + QRect labelGeo = labelItem->geometry(); + //ptr->drawRect(labelGeo); + // if our item would only half fit, we disable clipping for that one + if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() ) + ptr->setClipping( false ); + else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() ) + ptr->setClipping( false ); + + if( !isLogarithmicX ) + labelStep = labelDiff - dimX.stepWidth; + + // if our item would only half fit, we disable clipping for that one + if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() ) + ptr->setClipping( false ); + else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() ) + ptr->setClipping( false ); + + labelItem->paint( ptr ); + + // do not call customizedLabel() again: + labelItem2->setText( labelItem->text() ); + + } else { + labelStep -= dimX.stepWidth; + } + } + + if( hardLabelsCount ) { + if( useShortLabels && idxLabel >= shortLabelsCount - 1 ) + idxLabel = 0; + else if( !useShortLabels && idxLabel >= hardLabelsCount - 1 ) + idxLabel = 0; + else{ + idxLabel += static_cast(dimX.stepWidth); + //qDebug() << "dimX.stepWidth:" << dimX.stepWidth << " idxLabel:" << idxLabel; + } + } else if( headerLabelsCount ) { + if( ++idxLabel > headerLabelsCount - 1 ) { + idxLabel = 0; + } + } else { + iLabelF += dimX.stepWidth; + } + } + if ( isLogarithmicX ) + { + i *= 10.0; + if( i == 0.0 ) + { + const qreal j = dimensions.first().start; + i = j == 0.0 ? 1.0 : pow( 10.0, floor( log10( j ) ) ); + } + } + else + { + i += dimX.stepWidth; + } + } + } else { + const PainterSaver p( ptr ); + const double maxLimit = maxValueY; + const double steg = dimY.stepWidth; + int maxLabelsWidth = 0; + qreal labelValue; + + if( drawLabels && position() == Right ){ + // Find the widest label, so we to know how much we need to right-shift + // our labels, to get them drawn right aligned: + labelValue = minValueY; + while ( labelValue <= maxLimit ) { + const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) + + QString::number( labelValue ) + + diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true ); + labelItem->setText( customizedLabel( labelText ) ); + maxLabelsWidth = qMax( maxLabelsWidth, diagramIsVertical ? labelItem->sizeHint().width() : labelItem->sizeHint().height() ); + calculateNextLabel( labelValue, steg, isLogarithmicY, dimensions.last().start ); + + if(maxValueY == 0 && minValueY == 0) + break; + } + } + + labelValue = minValueY; + qreal step = steg; + bool nextLabel = false; + //qDebug("minValueY: %f maxLimit: %f steg: %f", minValueY, maxLimit, steg); + + //Draws custom tick marks in the y-axis + if( !d->customTicksPositions.isEmpty() ) + { + const QList< double > values = d->customTicksPositions; + KDAB_FOREACH( const double value, values ) + { + QPointF annoPoint = (diagramIsVertical ? QPointF( 0.0, value ) : QPointF( value, 0.0 )); + QPointF leftPoint = plane->translate( annoPoint ); + QPointF rightPoint = plane->translate( annoPoint ); + + if ( diagramIsVertical ) { + leftPoint.setX( rulerRef.x() + tickLength() ); + rightPoint.setX( rulerRef.x() ); + } else { + leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) ); + rightPoint.setY( rulerRef.y() ); + } + + context->painter()->drawLine(leftPoint, rightPoint); + } + } + + if( drawLabels ) + { + // first calculate the steps depending on labels colision + while( labelValue <= maxLimit ) { + QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue ) : QPointF( labelValue, 0 ) ); + const qreal translatedValue = diagramIsVertical ? leftPoint.y() : leftPoint.x(); + //qDebug() << "geoRect:" << geoRect << " geoRect.top()" << geoRect.top() << "geoRect.bottom()" << geoRect.bottom() << " translatedValue:" << translatedValue; + const bool bTranslatedValueIsWithinRange = diagramIsVertical ? translatedValue > geoRect.top() && translatedValue <= geoRect.bottom() + : translatedValue > geoRect.left() && translatedValue <= geoRect.right(); + if( bTranslatedValueIsWithinRange ){ + const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) + + QString::number( labelValue ) + + diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true ); + const QString label2Text = diagram()->unitPrefix( static_cast< int >( labelValue + step ), diagramOrientation, true ) + + QString::number( labelValue + step ) + + diagram()->unitSuffix( static_cast< int >( labelValue + step ), diagramOrientation, true ); + labelItem->setText( customizedLabel( labelText ) ); + labelItem2->setText( customizedLabel( QString::number( labelValue + step ) ) ); + QPointF nextPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue + step ) : QPointF( labelValue + step, 0 ) ); + if ( labelItem->intersects( *labelItem2, leftPoint, nextPoint ) ) + { + step += steg; + nextLabel = false; + }else{ + nextLabel = true; + } + }else{ + nextLabel = true; + } + + if ( nextLabel || isLogarithmicY ) + calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start ); + else + labelValue = minValueY; + } + + // Second - Paint the labels + labelValue = minValueY; + //qDebug() << "axis labels starting at" << labelValue << "step width" << step; + if( !d->annotations.isEmpty() ) + { + const QList< double > annotations = d->annotations.keys(); + KDAB_FOREACH( const double annotation, annotations ) + { + QPointF annoPoint = (diagramIsVertical ? QPointF( 0.0, annotation ) : QPointF( annotation, 0.0 )); + QPointF leftPoint = plane->translate( annoPoint ); + QPointF rightPoint = plane->translate( annoPoint ); + + if ( diagramIsVertical ) { + leftPoint.setX( rulerRef.x() + tickLength() ); + rightPoint.setX( rulerRef.x() ); + } else { + leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) ); + rightPoint.setY( rulerRef.y() ); + } + + const qreal translatedValue = diagramIsVertical ? rightPoint.y() : rightPoint.x(); + const bool bIsVisibleLabel = diagramIsVertical ? + ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicY) || labelValue != 0.0 ) + : ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicY) || labelValue != 0.0 ); + + if( bIsVisibleLabel ) + { + ptr->save(); + if ( rulerAttr.hasTickMarkPenAt( annotation ) ) + ptr->setPen( rulerAttr.tickMarkPen( annotation ) ); + else + ptr->setPen( rulerAttr.majorTickMarkPen() ); + ptr->drawLine( leftPoint, rightPoint ); + ptr->restore(); + + labelItem->setText( d->annotations[ annotation ] ); + const QSize labelSize( labelItem->sizeHint() ); + int x, y; + if ( diagramIsVertical ) { + x = static_cast( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5) + - ( position() == Left ? labelSize.width() : 0.0 ) ); + y = static_cast( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 ); + } else { + const qreal halfFontHeight = met.height() * 0.5; + x = static_cast( leftPoint.x() - labelSize.width() * 0.5 ); + y = static_cast( (position() == Bottom ? leftPoint.y() : rightPoint.y()) + + + ( position() == Bottom ? halfFontHeight : -(halfFontHeight + labelSize.height()) ) ); + } + labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) ); + + QRect labelGeo = labelItem->geometry(); + // if our item would only half fit, we disable clipping for that one + if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() ) + ptr->setClipping( false ); + else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() ) + ptr->setClipping( false ); + + labelItem->paint( ptr ); + } + } + } + else + { + while( labelValue <= maxLimit ) { + //qDebug() << "value now" << labelValue; + const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) + + QString::number( labelValue ) + + diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true ); + labelItem->setText( customizedLabel( labelText ) ); + QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue ) : QPointF( labelValue, 0 ) ); + QPointF rightPoint = leftPoint; + + if ( diagramIsVertical ) { + leftPoint.setX( rulerRef.x() + tickLength() ); + rightPoint.setX( rulerRef.x() ); + } else { + leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) ); + rightPoint.setY( rulerRef.y() ); + } + + bool bIsVisibleLabel; + const qreal translatedValue = diagramIsVertical ? rightPoint.y() : rightPoint.x(); + if ( diagramIsVertical) + bIsVisibleLabel = ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicX) || labelValue != 0.0 ); + else + bIsVisibleLabel = ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicX) || labelValue != 0.0 ); + + if( bIsVisibleLabel ){ + ptr->save(); + if ( rulerAttr.hasTickMarkPenAt( labelValue ) ) + ptr->setPen( rulerAttr.tickMarkPen( labelValue ) ); + else + ptr->setPen( rulerAttr.majorTickMarkPen() ); + ptr->drawLine( leftPoint, rightPoint ); + ptr->restore(); + + drawnYTicks.append( static_cast( diagramIsVertical ? leftPoint.y() : leftPoint.x() ) ); + const QSize labelSize( labelItem->sizeHint() ); + + int x, y; + if ( diagramIsVertical ) { + x = static_cast( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5) ) + - ( position() == Left ? labelSize.width() : (labelSize.width() - maxLabelsWidth) ); + y = static_cast( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 ); + } else { + const qreal halfFontHeight = met.height() * 0.5; + x = static_cast( leftPoint.x() - labelSize.width() * 0.5 ); + y = static_cast( (position() == Bottom ? leftPoint.y() : rightPoint.y()) + + + ( position() == Bottom ? halfFontHeight : -(halfFontHeight + labelSize.height()) ) ); + } + + labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) ); + const QRect labelGeo = labelItem->geometry(); + const bool hadClipping = ptr->hasClipping(); + if( labelGeo.top() < geoRect.top() && labelGeo.bottom() > geoRect.top() ) + ptr->setClipping( false ); + else if( labelGeo.top() < geoRect.bottom() && labelGeo.bottom() > geoRect.bottom() ) + ptr->setClipping( false ); + + // if our item would only half fit, we disable clipping for that one + if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() ) + ptr->setClipping( false ); + else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() ) + ptr->setClipping( false ); + + labelItem->paint( ptr ); + ptr->setClipping( hadClipping ); + } + + //qDebug() << step; + calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start ); + } + } + } + } + delete labelItem; + delete labelItem2; + } + + // this draws the subunit rulers + if ( drawSubUnitRulers && d->annotations.isEmpty() ) { + ptr->save(); + + d->drawSubUnitRulers( ptr, plane, dim, rulerRef, isAbscissa() ? drawnAbscissaTicks : drawnYTicks, diagramIsVertical, rulerAttr ); + + ptr->restore(); + } + + if( ! titleText().isEmpty() ) { + d->drawTitleText( ptr, plane, areaGeoRect ); + } + + //qDebug() << "KDChart::CartesianAxis::paintCtx() done."; +} + + +/* pure virtual in QLayoutItem */ +bool CartesianAxis::isEmpty() const +{ + return false; // if the axis exists, it has some (perhaps default) content +} +/* pure virtual in QLayoutItem */ +Qt::Orientations CartesianAxis::expandingDirections() const +{ + Qt::Orientations ret; + switch ( position() ) + { + case Bottom: + case Top: + ret = Qt::Horizontal; + break; + case Left: + case Right: + ret = Qt::Vertical; + break; + default: + Q_ASSERT( false ); // all positions need to be handeld + break; + }; + return ret; +} + + +static void calculateOverlap( int i, int first, int last, + int measure, + bool centerAbscissaTicks, + int& firstOverlap, int& lastOverlap ) +{ + if( i == first ){ + if( centerAbscissaTicks ){ + //TODO(khz): Calculate the amount of left overlap + // for bar diagrams. + }else{ + firstOverlap = measure / 2; + } + } + // we test both bounds in on go: first and last might be equal + if( i == last ){ + if( centerAbscissaTicks ){ + //TODO(khz): Calculate the amount of right overlap + // for bar diagrams. + }else{ + lastOverlap = measure / 2; + } + } +} + + +void CartesianAxis::setCachedSizeDirty() const +{ + d->cachedMaximumSize = QSize(); +} + +/* pure virtual in QLayoutItem */ +QSize CartesianAxis::maximumSize() const +{ + if( ! d->cachedMaximumSize.isValid() ) + d->cachedMaximumSize = d->calculateMaximumSize(); + return d->cachedMaximumSize; +} + +QSize CartesianAxis::Private::calculateMaximumSize() const +{ + QSize result; + if ( !diagram() ) + return result; + + const AbstractCartesianDiagram * dia = qobject_cast< const AbstractCartesianDiagram * >( diagram() ); + if( dia && dia->referenceDiagram() ) + dia = dia->referenceDiagram(); + const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( dia ); + const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->orientation() : Qt::Vertical; + const bool diagramIsVertical = diagramOrientation == Qt::Vertical; + + const TextAttributes labelTA = mAxis->textAttributes(); + const bool drawLabels = labelTA.isVisible(); + + const TextAttributes titleTA( titleTextAttributesWithAdjustedRotation() ); + const bool drawTitle = titleTA.isVisible() && ! axis()->titleText().isEmpty(); + + AbstractCoordinatePlane* plane = diagram()->coordinatePlane(); + //qDebug() << this<<"::maximumSize() uses plane geometry" << plane->geometry(); + QObject* refArea = plane->parent(); + TextLayoutItem labelItem( QString(), labelTA, refArea, + KDChartEnums::MeasureOrientationMinimum, Qt::AlignLeft ); + TextLayoutItem titleItem( axis()->titleText(), titleTA, refArea, + KDChartEnums::MeasureOrientationMinimum, Qt::AlignHCenter | Qt::AlignVCenter ); + + const QFontMetrics fm( labelItem.realFont(), GlobalMeasureScaling::paintDevice() ); + + const qreal labelGap = + drawLabels + ? ( (diagramIsVertical ? fm.height() : fm.averageCharWidth()) / 3.0) + : 0.0; + const QFontMetricsF titleFM = QFontMetricsF( titleItem.realFont(), GlobalMeasureScaling::paintDevice() ); + const qreal titleGap = + drawTitle + ? ( (diagramIsVertical ? titleFM.height() : titleFM.averageCharWidth()) / 3.0) + : 0.0; + + if ( axis()->isAbscissa() ) { + const bool centerAbscissaTicks = referenceDiagramNeedsCenteredAbscissaTicks(diagram()); + int leftOverlap = 0; + int rightOverlap = 0; + + qreal w = diagramIsVertical ? 10.0 : 0.0; + qreal h = diagramIsVertical ? 0.0 : 10.0; + if( drawLabels ){ + // if there're no label strings, we take the biggest needed number as height + if( !annotations.isEmpty() ) + { + const QStringList strings = annotations.values(); + KDAB_FOREACH( const QString& string, strings ) + { + labelItem.setText( string ); + const QSize siz = labelItem.sizeHint(); + if ( diagramIsVertical ) + h = qMax( h, static_cast< qreal >( siz.height() ) ); + else + w = qMax( w, static_cast< qreal >( siz.width() ) ); + } + } + else if ( !axis()->labels().isEmpty() ) + { + // find the longest label text: + const int first=0; + const int last=axis()->labels().count()-1; + const QStringList labelsList( axis()->labels() ); + for ( int i = first; i <= last; ++i ) + { + labelItem.setText( axis()->customizedLabel(labelsList[ i ]) ); + const QSize siz = labelItem.sizeHint(); + //qDebug()<(siz.height()) ); + else + w = qMax( w, static_cast(siz.width()) ); + calculateOverlap( i, first, last, diagramIsVertical ? siz.width() : siz.height(), centerAbscissaTicks, + leftOverlap, rightOverlap ); + + } + } + else + { + QStringList headerLabels = diagram()->itemRowLabels(); + const int headerLabelsCount = headerLabels.count(); + if( headerLabelsCount ){ + if( cachedHeaderLabels == headerLabels && ( diagramIsVertical ? cachedFontHeight == fm.height() : cachedFontWidth == fm.averageCharWidth() )) { + if ( diagramIsVertical ) + h = cachedLabelHeight; + else + w = cachedLabelWidth; + } else { + cachedHeaderLabels = headerLabels; + if ( diagramIsVertical ) + cachedFontWidth = fm.averageCharWidth(); + else + cachedFontHeight = fm.height(); + const bool useFastCalcAlgorithm + = (strcmp( axis()->metaObject()->className(), "KDChart::CartesianAxis" ) == 0); + const int first=0; + const int last=headerLabelsCount-1; + for ( int i = first; + i <= last; + i = (useFastCalcAlgorithm && i < last) ? last : (i+1) ) + { + labelItem.setText( axis()->customizedLabel(headerLabels[ i ]) ); + const QSize siz = labelItem.sizeHint(); + if ( diagramIsVertical ) { + h = qMax( h, static_cast(siz.height()) ); + cachedLabelHeight = h; + } else { + cachedLabelWidth = w; + w = qMax( w, static_cast(siz.width()) ); + } + calculateOverlap( i, first, last, diagramIsVertical ? siz.width() : siz.height(), centerAbscissaTicks, + leftOverlap, rightOverlap ); + } + } + }else{ + labelItem.setText( + axis()->customizedLabel( + QString::number( diagramIsVertical ? plane->gridDimensionsList().first().end + : plane->gridDimensionsList().last().end, 'f', 0 ))); + const QSize siz = labelItem.sizeHint(); + if ( diagramIsVertical ) + h = siz.height(); + else + w = siz.width(); + calculateOverlap( 0, 0, 0, siz.width(), centerAbscissaTicks, + leftOverlap, rightOverlap ); + } + } + // we leave a little gap between axis labels and bottom (or top, resp.) side of axis + h += labelGap; + } + // space for a possible title: + if ( drawTitle ) { + // we add the title height and leave a little gap between axis labels and axis title + if ( diagramIsVertical ) { + h += titleItem.sizeHint().height() + titleGap; + w = titleItem.sizeHint().width() + 2.0; + } else { + h = titleItem.sizeHint().height() + 2.0; + w += titleItem.sizeHint().width() + titleGap; + } + } + // space for the ticks + if ( diagramIsVertical ) + h += qAbs( axis()->tickLength() ) * 3.0; + else + w += qAbs( axis()->tickLength() ) * 3.0; + result = QSize ( static_cast( w ), static_cast( h ) ); + + //qDebug()<<"calculated size of x axis:"<(this), + SLOT(adjustLeftRightGridColumnWidths())); + } + */ + } else { + int topOverlap = 0; + int bottomOverlap = 0; + + qreal w = diagramIsVertical ? 0.0 : 10.0; + qreal h = diagramIsVertical ? 10.0 : 0.0; + if( drawLabels ){ + // if there're no label strings, we loop through the values + // taking the longest (not largest) number - e.g. 0.00001 is longer than 100 + if( !annotations.isEmpty() ) + { + const QStringList strings = annotations.values(); + KDAB_FOREACH( const QString& string, strings ) + { + labelItem.setText( string ); + const QSize siz = labelItem.sizeHint(); + if ( diagramIsVertical ) + w = qMax( w, static_cast< qreal >( siz.width() ) ); + else + h = qMax( h, static_cast< qreal >( siz.height() ) ); + } + } + else if( axis()->labels().isEmpty() ) + { + const DataDimension dimY = AbstractGrid::adjustedLowerUpperRange( + diagramIsVertical ? plane->gridDimensionsList().last() + : plane->gridDimensionsList().first(), true, true ); + const double step = dimY.stepWidth; + const qreal minValue = dimY.start; + const qreal maxValue = dimY.end; + const bool isLogarithmicY = (dimY.calcMode == AbstractCoordinatePlane::Logarithmic ); + qreal labelValue = minValue; + + while( labelValue <= maxValue ) { + const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) + + QString::number( labelValue ) + + diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true ); + labelItem.setText( axis()->customizedLabel( labelText ) ); + + const QSize siz = labelItem.sizeHint(); + if ( diagramIsVertical ) + w = qMax( w, (qreal)siz.width() ); + else + h = qMax( h, (qreal)siz.height() ); + calculateOverlap( 0, 0, 0, diagramIsVertical ? siz.height() : siz.width(), false,// bar diagram flag is ignored for Ordinates + topOverlap, bottomOverlap ); + calculateNextLabel( labelValue, step, isLogarithmicY, plane->gridDimensionsList().last().start ); + + if(maxValue == 0 && minValue == 0) + break; + } + }else{ + // find the longest label text: + const int first=0; + const int last=axis()->labels().count()-1; + const QStringList labelsList( axis()->labels() ); + for ( int i = first; i <= last; ++i ) + { + labelItem.setText( axis()->customizedLabel(labelsList[ i ]) ); + const QSize siz = labelItem.sizeHint(); + if ( diagramIsVertical ) + w = qMax( w, (qreal)siz.width() ); + else + h = qMax( h, (qreal)siz.height() ); + calculateOverlap( 0, 0, 0, diagramIsVertical ? siz.height() : siz.width(), false,// bar diagram flag is ignored for Ordinates + topOverlap, bottomOverlap ); + } + } + // we leave a little gap between axis labels and left (or right, resp.) side of axis + w += labelGap; + } + // space for a possible title: + if ( drawTitle ) { + // we add the title height and leave a little gap between axis labels and axis title + if ( diagramIsVertical ) { + w += titleItem.sizeHint().width() + titleGap; + h = titleItem.sizeHint().height() + 2.0; + } else { + w = titleItem.sizeHint().width() + 2.0; + h += titleItem.sizeHint().height() + titleGap; + } + //qDebug() << "left/right axis title item size-hint:" << titleItem.sizeHint(); + } + // space for the ticks + if ( diagramIsVertical ) + w += qAbs( axis()->tickLength() ) * 3.0; + else + h += qAbs( axis()->tickLength() ) * 3.0; + + result = QSize ( static_cast( w ), static_cast( h ) ); + //qDebug() << "left/right axis width:" << result << " w:" << w; + + + // If necessary adjust the heights + // of the top (or bottom, resp.) side neighboring rows: + amountOfTopOverlap = topOverlap; + amountOfBottomOverlap = bottomOverlap; + /* Unused code for a push-model: + if( topOverlap || bottomOverlap ){ + QTimer::singleShot(200, const_cast(this), + SLOT(adjustTopBottomGridRowHeights())); + } + */ + } +//qDebug() << "*******************" << result; + //result=QSize(0,0); + return result; +} +/* pure virtual in QLayoutItem */ +QSize CartesianAxis::minimumSize() const +{ + return maximumSize(); +} +/* pure virtual in QLayoutItem */ +QSize CartesianAxis::sizeHint() const +{ + return maximumSize(); +} +/* pure virtual in QLayoutItem */ +void CartesianAxis::setGeometry( const QRect& r ) +{ +// qDebug() << "KDChart::CartesianAxis::setGeometry(" << r << ") called" +// << (isAbscissa() ? "for Abscissa":"for Ordinate") << "axis"; + d->geometry = r; + setCachedSizeDirty(); +} +/* pure virtual in QLayoutItem */ +QRect CartesianAxis::geometry() const +{ + return d->geometry; +} + +int CartesianAxis::tickLength( bool subUnitTicks ) const +{ + int result = 0; + + if ( isAbscissa() ) { + result = position() == Top ? -4 : 3; + } else { + result = position() == Left ? -4 : 3; + } + + if ( subUnitTicks ) + result = result < 0 ? result + 1 : result - 1; + + return result; +} + +QMap< double, QString > CartesianAxis::annotations() const +{ + return d->annotations; +} + +void CartesianAxis::setAnnotations( const QMap< double, QString >& annotations ) +{ + if( d->annotations == annotations ) + return; + + d->annotations = annotations; + update(); +} + +QList< double > CartesianAxis::customTicks() const +{ + return d->customTicksPositions; +} + +void CartesianAxis::setCustomTicks( const QList< double >& customTicksPositions ) +{ + if( d->customTicksPositions == customTicksPositions ) + return; + + d->customTicksPositions = customTicksPositions; + update(); +} + + +/* unused code from KDChartCartesianAxis.h for using a push-model: +Q_SIGNALS: + void needAdjustLeftRightColumnsForOverlappingLabels( + CartesianAxis* axis, int left, int right ); + void needAdjustTopBottomRowsForOverlappingLabels( + CartesianAxis* axis, int top, int bottom ); +private Q_SLOTS: + void adjustLeftRightGridColumnWidths(); + void adjustTopBottomGridRowHeights(); +*/ + +/* +// Unused code trying to use a push-model: This did not work +// since we can not re-layout the planes each time when +// Qt layouting is calling sizeHint() +void CartesianAxis::adjustLeftRightGridColumnWidths() +{ + if( ! d->amountOfLeftOverlap && ! d->amountOfRightOverlap ) + return; + const int leftOverlap = d->amountOfLeftOverlap; + const int rightOverlap= d->amountOfRightOverlap; + d->amountOfLeftOverlap = 0; + d->amountOfRightOverlap = 0; + emit needAdjustLeftRightColumnsForOverlappingLabels( + this, leftOverlap, rightOverlap ); +} + +void CartesianAxis::adjustTopBottomGridRowHeights() +{ + if( ! d->amountOfTopOverlap && ! d->amountOfBottomOverlap ) + return; + const int topOverlap = d->amountOfTopOverlap; + const int bottomOverlap= d->amountOfBottomOverlap; + d->amountOfTopOverlap = 0; + d->amountOfBottomOverlap = 0; + emit needAdjustTopBottomRowsForOverlappingLabels( + this, topOverlap, bottomOverlap ); +} +*/ diff --git a/libkdchart/src/KDChartCartesianAxis.h b/libkdchart/src/KDChartCartesianAxis.h new file mode 100644 index 0000000..e8c059d --- /dev/null +++ b/libkdchart/src/KDChartCartesianAxis.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTCARTESIANAXIS_H +#define KDCHARTCARTESIANAXIS_H + +#include + +#include "KDChartAbstractAxis.h" + +namespace KDChart { + + class AbstractCartesianDiagram; + + /** + * The class for cartesian axes. + * + * For being useful, axes need to be assigned to a diagram, see + * AbstractCartesianDiagram::addAxis and AbstractCartesianDiagram::takeAxis. + * + * \sa PolarAxis, AbstractCartesianDiagram + */ + class KDCHART_EXPORT CartesianAxis : public AbstractAxis + { + Q_OBJECT + + Q_DISABLE_COPY( CartesianAxis ) + KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( CartesianAxis, AbstractDiagram* ) + + public: + enum Position { + Bottom, + Top, + Right, + Left + }; + + /** + * C'tor of the class for cartesian axes. + * + * \note If using a zero parent for the constructor, you need to call + * your diagram's addAxis function to add your axis to the diagram. + * Otherwise, there is no need to call addAxis, since the constructor + * does that automatically for you, if you pass a diagram as parameter. + * + * \sa AbstractCartesianDiagram::addAxis + */ + explicit CartesianAxis ( AbstractCartesianDiagram* diagram = 0 ); + ~CartesianAxis(); + + /** + * Returns true if both axes have the same settings. + */ + bool compare( const CartesianAxis* other )const; + + /** reimpl */ + virtual void paint( QPainter* ); + /** reimpl */ + virtual void paintCtx( PaintContext* ); + + /** + * Sets the optional text displayed as chart title. + */ + void setTitleText( const QString& text ); + QString titleText() const; + + /** + * Sets the spacing between the title and the diagram. + */ + void setTitleSpace( qreal value ); + qreal titleSpace() const; + + void setTitleTextAttributes( const TextAttributes &a ); + /** + * Returns the text attributes that will be used for displaying the + * title text. + * This is either the text attributes as specified by setTitleTextAttributes, + * or (if setTitleTextAttributes() was not called) the default text attributes. + * \sa resetTitleTextAttributes, hasDefaultTitleTextAttributes + */ + TextAttributes titleTextAttributes() const; + /** + * Reset the title text attributes to the built-in default: + * + * Same font and pen as AbstractAxis::textAttributes() + * and 1.5 times their size. + */ + void resetTitleTextAttributes(); + bool hasDefaultTitleTextAttributes() const; + + virtual void setPosition ( Position p ); +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + virtual const Position position () const; +#else + virtual Position position () const; +#endif + + virtual void layoutPlanes(); + + virtual bool isAbscissa() const; + virtual bool isOrdinate() const; + + /** + * Sets the axis annotations to \a annotations. + * Annotations are a QMap of doubles and QStrings defining special + * markers and their position. + * If you use annotations, the normal ticks and values will be invisible. + * To unset the annotations, pass an empty QMap. + */ + void setAnnotations( const QMap< double, QString >& annotations ); + /** + * Returns the currently set axis annotations. + */ + QMap< double, QString > annotations() const; + + /** + * Sets custom ticks on the axis. + * Ticks are a QList of doubles defining their special position. + */ + void setCustomTicks( const QList< double >& ticksPostions ); + /** + * Returns the currently set custom ticks on the axis. + */ + QList< double > customTicks() const; + + + /** pure virtual in QLayoutItem */ + virtual bool isEmpty() const; + /** pure virtual in QLayoutItem */ + virtual Qt::Orientations expandingDirections() const; + /** pure virtual in QLayoutItem */ + virtual QSize maximumSize() const; + /** pure virtual in QLayoutItem */ + virtual QSize minimumSize() const; + /** pure virtual in QLayoutItem */ + virtual QSize sizeHint() const; + /** pure virtual in QLayoutItem */ + virtual void setGeometry( const QRect& r ); + /** pure virtual in QLayoutItem */ + virtual QRect geometry() const; + + public Q_SLOTS: + void setCachedSizeDirty() const; + + virtual int tickLength( bool subUnitTicks = false ) const; + }; + + typedef QList CartesianAxisList; +} + +#endif diff --git a/libkdchart/src/KDChartCartesianAxis_p.h b/libkdchart/src/KDChartCartesianAxis_p.h new file mode 100644 index 0000000..18cfc13 --- /dev/null +++ b/libkdchart/src/KDChartCartesianAxis_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTCARTESIANAXIS_P_H +#define KDCHARTCARTESIANAXIS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartCartesianAxis.h" +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartAbstractAxis_p.h" + +#include + + +namespace KDChart { + +/** + * \internal + */ +class CartesianAxis::Private : public AbstractAxis::Private +{ + friend class CartesianAxis; + +public: + Private( AbstractCartesianDiagram* diagram, CartesianAxis* axis ) + : AbstractAxis::Private( diagram, axis ) + , useDefaultTextAttributes( true ) + , cachedHeaderLabels( QStringList() ) + , cachedLabelHeight( 0.0 ) + , cachedFontHeight( 0 ) + , axisTitleSpace( 1.0 ) + {} + ~Private() {} + + const CartesianAxis* axis() const { return static_cast( mAxis ); } + + void drawSubUnitRulers( QPainter*, CartesianCoordinatePlane* plane, const DataDimension& dim, + const QPointF& rulerRef, const QVector& drawnTicks, const bool diagramIsVertical, + const RulerAttributes& rulerAttr) const; + void drawTitleText( QPainter*, CartesianCoordinatePlane* plane, const QRect& areaGeoRect ) const; + + const TextAttributes titleTextAttributesWithAdjustedRotation() const; + + QSize calculateMaximumSize() const; + +private: + QString titleText; + TextAttributes titleTextAttributes; + bool useDefaultTextAttributes; + Position position; + QRect geometry; + QMap< double, QString > annotations; + QList< double > customTicksPositions; + mutable QStringList cachedHeaderLabels; + mutable qreal cachedLabelHeight; + mutable qreal cachedLabelWidth; + mutable int cachedFontHeight; + mutable int cachedFontWidth; + mutable QSize cachedMaximumSize; + qreal axisTitleSpace; +}; + +inline CartesianAxis::CartesianAxis( Private * p, AbstractDiagram* diagram ) + : AbstractAxis( p, diagram ) +{ + init(); +} +inline CartesianAxis::Private * CartesianAxis::d_func() +{ return static_cast( AbstractAxis::d_func() ); } +inline const CartesianAxis::Private * CartesianAxis::d_func() const +{ return static_cast( AbstractAxis::d_func() ); } + +} + +#endif diff --git a/libkdchart/src/KDChartCartesianCoordinatePlane.cpp b/libkdchart/src/KDChartCartesianCoordinatePlane.cpp new file mode 100644 index 0000000..33738b4 --- /dev/null +++ b/libkdchart/src/KDChartCartesianCoordinatePlane.cpp @@ -0,0 +1,924 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartCartesianCoordinatePlane.h" +#include "KDChartCartesianCoordinatePlane_p.h" + +#include +#include +#include +#include +#include + +#include "KDChartAbstractDiagram.h" +#include "KDChartAbstractCartesianDiagram.h" +#include "CartesianCoordinateTransformation.h" +#include "KDChartGridAttributes.h" +#include "KDChartPaintContext.h" +#include "KDChartPainterSaver_p.h" +#include "KDChartBarDiagram.h" + +#include + + +using namespace KDChart; + +#define d d_func() + +CartesianCoordinatePlane::Private::Private() + : AbstractCoordinatePlane::Private() + , bPaintIsRunning( false ) + , hasOwnGridAttributesHorizontal ( false ) + , hasOwnGridAttributesVertical ( false ) + // old: , initialResizeEventReceived ( false ) + , isometricScaling ( false ) + , horizontalMin(0) + , horizontalMax(0) + , verticalMin(0) + , verticalMax(0) + , autoAdjustHorizontalRangeToData(67) + , autoAdjustVerticalRangeToData( 67) + , autoAdjustGridToZoom( true ) + , fixedDataCoordinateSpaceRelation( false ) + , xAxisStartAtZero(true) + , reverseVerticalPlane( false ) + , reverseHorizontalPlane( false ) +{ +} + +CartesianCoordinatePlane::CartesianCoordinatePlane ( Chart* parent ) + : AbstractCoordinatePlane ( new Private(), parent ) +{ + // this bloc left empty intentionally +} + +CartesianCoordinatePlane::~CartesianCoordinatePlane() +{ + // this bloc left empty intentionally +} + +void CartesianCoordinatePlane::init() +{ + // this bloc left empty intentionally +} + + +void CartesianCoordinatePlane::addDiagram ( AbstractDiagram* diagram ) +{ + Q_ASSERT_X ( dynamic_cast ( diagram ), + "CartesianCoordinatePlane::addDiagram", "Only cartesian " + "diagrams can be added to a cartesian coordinate plane!" ); + AbstractCoordinatePlane::addDiagram ( diagram ); + connect ( diagram, SIGNAL ( layoutChanged ( AbstractDiagram* ) ), + SLOT ( slotLayoutChanged ( AbstractDiagram* ) ) ); + + connect( diagram, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) ); +} + + +void CartesianCoordinatePlane::paint ( QPainter* painter ) +{ + // prevent recursive call: + //qDebug("attempt plane::paint()"); + if( d->bPaintIsRunning ){ + return; + } + d->bPaintIsRunning = true; + + //qDebug() << "start plane::paint()"; + + AbstractDiagramList diags = diagrams(); + if ( !diags.isEmpty() ) + { + PaintContext ctx; + ctx.setPainter ( painter ); + ctx.setCoordinatePlane ( this ); + const QRectF drawArea( drawingArea() ); + ctx.setRectangle ( drawArea ); + + // enabling clipping so that we're not drawing outside + PainterSaver painterSaver( painter ); + QRect clipRect = drawArea.toRect().adjusted( -1, -1, 1, 1 ); + QRegion clipRegion( clipRect ); + painter->setClipRegion( clipRegion ); + + // paint the coordinate system rulers: + d->grid->drawGrid( &ctx ); + + // paint the diagrams: + for ( int i = 0; i < diags.size(); i++ ) + { + if ( diags[i]->isHidden() ) { + continue; + } +//qDebug(" start diags[i]->paint ( &ctx );"); + PainterSaver diagramPainterSaver( painter ); + diags[i]->paint ( &ctx ); +//qDebug(" done: diags[i]->paint ( &ctx );"); + } + + //for debugging: + // painter->drawRect( drawArea.adjusted(4,4,-4,-4) ); + // painter->drawRect( drawArea.adjusted(2,2,-2,-2) ); + // painter->drawRect( drawArea ); + } + d->bPaintIsRunning = false; + //qDebug("done: plane::paint()"); +} + + +void CartesianCoordinatePlane::slotLayoutChanged ( AbstractDiagram* ) +{ + // old: if ( d->initialResizeEventReceived ) + layoutDiagrams(); +} + +QRectF CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams() const +{ + // determine unit of the rectangles of all involved diagrams: + qreal minX = 0; + qreal maxX = 0; + qreal minY = 0; + qreal maxY = 0 + ; + bool bStarting = true; + Q_FOREACH( const AbstractDiagram* diagram, diagrams() ) + { + QPair dataBoundariesPair = diagram->dataBoundaries(); + //qDebug() << "CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams()\ngets diagram->dataBoundaries: " << dataBoundariesPair.first << dataBoundariesPair.second; + if ( bStarting || dataBoundariesPair.first.x() < minX ) minX = dataBoundariesPair.first.x(); + if ( bStarting || dataBoundariesPair.first.y() < minY ) minY = dataBoundariesPair.first.y(); + if ( bStarting || dataBoundariesPair.second.x() > maxX ) maxX = dataBoundariesPair.second.x(); + if ( bStarting || dataBoundariesPair.second.y() > maxY ) maxY = dataBoundariesPair.second.y(); + bStarting = false; + } + //qDebug() << "CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams()\nreturns data boundaries: " << QRectF( QPointF(minX, minY), QSizeF(maxX - minX, maxY - minY) ); + QRectF dataBoundingRect; + dataBoundingRect.setBottomLeft( QPointF(minX, minY) ); + dataBoundingRect.setTopRight( QPointF(maxX, maxY) ); + return dataBoundingRect; +} + + +QRectF CartesianCoordinatePlane::adjustedToMaxEmptyInnerPercentage( + const QRectF& r, unsigned int percentX, unsigned int percentY ) const +{ + QRectF erg( r ); + if( ( axesCalcModeX() != Logarithmic || r.left() < 0.0 ) && (percentX > 0) && (percentX != 100) ) { + const bool isPositive = (r.left() >= 0); + if( (r.right() >= 0) == isPositive ){ + const qreal innerBound = + isPositive ? qMin(r.left(), r.right()) : qMax(r.left(), r.right()); + const qreal outerBound = + isPositive ? qMax(r.left(), r.right()) : qMin(r.left(), r.right()); + if( innerBound / outerBound * 100 <= percentX ) + { + if(d->xAxisStartAtZero) + { + if( isPositive ) + erg.setLeft( 0.0 ); + else + erg.setRight( 0.0 ); + } + } + } + } + if( ( axesCalcModeY() != Logarithmic || r.bottom() < 0.0 ) && (percentY > 0) && (percentY != 100) ) { + //qDebug() << erg.bottom() << erg.top(); + const bool isPositive = (r.bottom() >= 0); + if( (r.top() >= 0) == isPositive ){ + const qreal innerBound = + isPositive ? qMin(r.top(), r.bottom()) : qMax(r.top(), r.bottom()); + const qreal outerBound = + isPositive ? qMax(r.top(), r.bottom()) : qMin(r.top(), r.bottom()); + //qDebug() << innerBound << outerBound; + if( innerBound / outerBound * 100 <= percentY ) + { + if( isPositive ) + erg.setBottom( 0.0 ); + else + erg.setTop( 0.0 ); + } + } + //qDebug() << erg.bottom() << erg.top() << "!!"; + } + return erg; +} + + +QRectF CartesianCoordinatePlane::calculateRawDataBoundingRect() const +{ + // are manually set ranges to be applied? + const bool bAutoAdjustHorizontalRange = (d->autoAdjustHorizontalRangeToData < 100); + const bool bAutoAdjustVerticalRange = (d->autoAdjustVerticalRangeToData < 100); + + const bool bHardHorizontalRange = (d->horizontalMin != d->horizontalMax) && ! bAutoAdjustHorizontalRange; + const bool bHardVerticalRange = (d->verticalMin != d->verticalMax) && ! bAutoAdjustVerticalRange; + QRectF dataBoundingRect; + + // if custom boundaries are set on the plane, use them + if ( bHardHorizontalRange && bHardVerticalRange ) { + dataBoundingRect.setLeft( d->horizontalMin ); + dataBoundingRect.setRight( d->horizontalMax ); + dataBoundingRect.setBottom( d->verticalMin ); + dataBoundingRect.setTop( d->verticalMax ); + }else{ + // determine unit of the rectangles of all involved diagrams: + dataBoundingRect = getRawDataBoundingRectFromDiagrams(); + if ( bHardHorizontalRange ) { + dataBoundingRect.setLeft( d->horizontalMin ); + dataBoundingRect.setRight( d->horizontalMax ); + } + if ( bHardVerticalRange ) { + dataBoundingRect.setBottom( d->verticalMin ); + dataBoundingRect.setTop( d->verticalMax ); + } + } + // recalculate the bounds, if automatic adjusting of ranges is desired AND + // both bounds are at the same side of the zero line + dataBoundingRect = adjustedToMaxEmptyInnerPercentage( + dataBoundingRect, d->autoAdjustHorizontalRangeToData, d->autoAdjustVerticalRangeToData ); + if( bAutoAdjustHorizontalRange ){ + const_cast(d)->horizontalMin = dataBoundingRect.left(); + const_cast(d)->horizontalMax = dataBoundingRect.right(); + } + if( bAutoAdjustVerticalRange ){ + const_cast(this)->d->verticalMin = dataBoundingRect.bottom(); + const_cast(this)->d->verticalMax = dataBoundingRect.top(); + } + //qDebug() << "CartesianCoordinatePlane::calculateRawDataBoundingRect()\nreturns data boundaries: " << dataBoundingRect; + return dataBoundingRect; +} + + +DataDimensionsList CartesianCoordinatePlane::getDataDimensionsList() const +{ + + DataDimensionsList l; + const AbstractCartesianDiagram* dgr + = diagrams().isEmpty() ? 0 : dynamic_cast (diagrams().first() ); + if( dgr && dgr->referenceDiagram() ) + dgr = dgr->referenceDiagram(); + const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( dgr ); + + // note: + // It does make sense to retrieve the orientation from the first diagram. This is because + // a coordinate plane can either be for horizontal *or* for vertical diagrams. Both at the + // same time won't work, and thus the orientation for all diagrams is the same as for the first one. + const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->orientation() : Qt::Vertical; + + const bool diagramIsVertical = diagramOrientation == Qt::Vertical; + + + if( dgr ){ + const QRectF r( calculateRawDataBoundingRect() ); + // note: + // We do *not* access d->gridAttributesHorizontal here, but + // we use the getter function, to get the global attrs, if no + // special ones have been set for the respective orientation. + const GridAttributes gaH( gridAttributes( Qt::Horizontal ) ); + const GridAttributes gaV( gridAttributes( Qt::Vertical ) ); + // append the first dimension: for Abscissa axes + l.append( + DataDimension( + r.left(), r.right(), + diagramIsVertical ? ( dgr->datasetDimension() > 1 ) : true, + axesCalcModeX(), + gaH.gridGranularitySequence(), + gaH.gridStepWidth(), + gaH.gridSubStepWidth() ) ); + // append the second dimension: for Ordinate axes + l.append( + DataDimension( + r.bottom(), r.top(), + diagramIsVertical ? true : ( dgr->datasetDimension() > 1 ), + axesCalcModeY(), + gaV.gridGranularitySequence(), + gaV.gridStepWidth(), + gaV.gridSubStepWidth() ) ); + }else{ + l.append( DataDimension() ); // This gets us the default 1..0 / 1..0 grid + l.append( DataDimension() ); // shown, if there is no diagram on this plane. + } + return l; +} + +QRectF CartesianCoordinatePlane::drawingArea() const +{ + // the rectangle the diagrams cover in the *plane*: + // (Why -3? We save 1px on each side for the antialiased drawing, and + // respect the way QPainter calculates the width of a painted rect (the + // size is the rectangle size plus the pen width). This way, most clipping + // for regular pens should be avoided. When pens with a penWidth or larger + // than 1 are used, this may not be sufficient. + const QRect rect( areaGeometry() ); + return QRectF ( rect.left()+1, rect.top()+1, rect.width() - 3, rect.height() - 3 ); +} + + +QRectF CartesianCoordinatePlane::logicalArea() const +{ + if ( d->dimensions.isEmpty() ) + return QRectF(); + + const DataDimension dimX = d->dimensions.first(); + const DataDimension dimY = d->dimensions.last(); + const QPointF pt( qMin( dimX.start, dimX.end ), qMax( dimY.start, dimY.end ) ); + const QSizeF siz( qAbs( dimX.distance() ), -qAbs( dimY.distance() ) ); + const QRectF dataBoundingRect( pt, siz ); + + // determine logical top left, taking the "reverse" option of + // horizontal and vertical dimension into account + QPointF topLeft; + if( !d->reverseVerticalPlane && !d->reverseHorizontalPlane ) + topLeft = dataBoundingRect.topLeft(); + else if( d->reverseVerticalPlane && !d->reverseHorizontalPlane ) + topLeft = dataBoundingRect.bottomLeft(); + else if( d->reverseVerticalPlane && d->reverseHorizontalPlane ) + topLeft = dataBoundingRect.bottomRight(); + else if( !d->reverseVerticalPlane && d->reverseHorizontalPlane ) + topLeft = dataBoundingRect.topRight(); + + const double width = dataBoundingRect.width() * ( d->reverseHorizontalPlane ? -1.0 : 1.0 ); + const double height = dataBoundingRect.height() * ( d->reverseVerticalPlane ? -1.0 : 1.0 ); + + return QRectF( topLeft, QSizeF( width, height ) ); +} + +QRectF CartesianCoordinatePlane::diagramArea() const +{ + const QRectF logArea( logicalArea() ); + QPointF physicalTopLeft = d->coordinateTransformation.translate( logArea.topLeft() ); + QPointF physicalBottomRight = d->coordinateTransformation.translate( logArea.bottomRight() ); + + return QRectF( physicalTopLeft, physicalBottomRight ).normalized(); +} + +QRectF CartesianCoordinatePlane::visibleDiagramArea() const +{ + return diagramArea().intersected( drawingArea() ); +} + +void CartesianCoordinatePlane::layoutDiagrams() +{ + if ( diagrams().isEmpty() ) + { // FIXME evaluate what can still be prepared + // FIXME decide default dimension if no diagrams are present (to make empty planes useable) + } + + d->dimensions = gridDimensionsList(); + // test for programming errors: critical + Q_ASSERT_X ( d->dimensions.count() == 2, "CartesianCoordinatePlane::layoutDiagrams", + "Error: gridDimensionsList() did not return exactly two dimensions." ); + + // physical area of the plane + const QRectF physicalArea( drawingArea() ); + // .. in contrast to the logical area + const QRectF logArea( logicalArea() ); + + d->coordinateTransformation.unitVectorX = logArea.width() != 0 ? physicalArea.width() / logArea.width() : 1.0; + d->coordinateTransformation.unitVectorY = logArea.height() != 0 ? physicalArea.height() / logArea.height() : 1.0; + + const double diagramXUnitInCoordinatePlane = d->coordinateTransformation.unitVectorX; + const double diagramYUnitInCoordinatePlane = d->coordinateTransformation.unitVectorY; + + double scaleX; + double scaleY; + + // calculate isometric scaling factor to maxscale the diagram into + // the coordinate system: + if ( d->isometricScaling ) + { + double scale = qMin ( qAbs ( diagramXUnitInCoordinatePlane ), + qAbs ( diagramYUnitInCoordinatePlane ) ); + + scaleX = qAbs( scale / diagramXUnitInCoordinatePlane ); + scaleY = qAbs( scale / diagramYUnitInCoordinatePlane ); + } else { + scaleX = 1.0; + scaleY = 1.0; + } + + const QPointF logicalTopLeft = logArea.topLeft(); + // calculate diagram origin in plane coordinates: + QPointF coordinateOrigin = QPointF ( logicalTopLeft.x() * -diagramXUnitInCoordinatePlane, + logicalTopLeft.y() * -diagramYUnitInCoordinatePlane ); + coordinateOrigin += physicalArea.topLeft(); + + d->coordinateTransformation.originTranslation = coordinateOrigin; + + // As in the first quadrant of the coordinate system, the origin is the bottom left, not top left. + // This origin is then the top left point of the resulting diagramRect for our coordinateTransformation. + const QRectF normalizedLogArea = logArea.normalized(); + d->coordinateTransformation.diagramRect = QRectF( normalizedLogArea.bottomLeft(), normalizedLogArea.topRight() ); + + d->coordinateTransformation.isoScaleX = scaleX; + d->coordinateTransformation.isoScaleY = scaleY; + + // the plane area might have changed, so the zoom values might also be changed + handleFixedDataCoordinateSpaceRelation( physicalArea ); + + update(); +} + +void CartesianCoordinatePlane::setFixedDataCoordinateSpaceRelation( bool fixed ) +{ + d->fixedDataCoordinateSpaceRelation = fixed; + d->fixedDataCoordinateSpaceRelationOldSize = QRectF(); + /* + //TODO(khz): We need to discuss if we want to do this: + if( ! fixed ){ + bool bChanged = false; + if( doneSetZoomFactorY( 1.0 ) ) + bChanged = true; + if( doneSetZoomFactorX( 1.0 ) ) + bChanged = true; + if( doneSetZoomCenter( QPointF(0.5, 0.5) ) ) + bChanged = true; + if( bChanged ){ + emit propertiesChanged(); + } + } + */ +} + +bool CartesianCoordinatePlane::hasFixedDataCoordinateSpaceRelation() const +{ + return d->fixedDataCoordinateSpaceRelation; +} + +void CartesianCoordinatePlane::setXAxisStartAtZero(bool fixedStart) +{ + if(d->xAxisStartAtZero == fixedStart) + return; + + d->xAxisStartAtZero = fixedStart; +} + +bool CartesianCoordinatePlane::xAxisStartAtZero() const +{ + return d->xAxisStartAtZero; +} + +void CartesianCoordinatePlane::handleFixedDataCoordinateSpaceRelation( const QRectF& geometry ) +{ + // is the feature enabled? + if( !d->fixedDataCoordinateSpaceRelation ) + return; + + // is the new geometry ok? + if( geometry.height() < 1 || geometry.width() < 1 ) + return; + + // if the size was changed, we calculate new zoom settings + if( d->fixedDataCoordinateSpaceRelationOldSize != geometry && !d->fixedDataCoordinateSpaceRelationOldSize.isNull() ) + { + const double newZoomX = zoomFactorX() * d->fixedDataCoordinateSpaceRelationOldSize.width() / geometry.width(); + const double newZoomY = zoomFactorY() * d->fixedDataCoordinateSpaceRelationOldSize.height() / geometry.height(); + + const QPointF oldCenter = zoomCenter(); + const QPointF newCenter = QPointF( oldCenter.x() * geometry.width() / d->fixedDataCoordinateSpaceRelationOldSize.width(), + oldCenter.y() * geometry.height() / d->fixedDataCoordinateSpaceRelationOldSize.height() ); + + // Use these internal methods to avoid sending + // the propertiesChanged signal three times: + bool bChanged = false; + if( doneSetZoomFactorY( newZoomY ) ) + bChanged = true; + if( doneSetZoomFactorX( newZoomX ) ) + bChanged = true; + if( doneSetZoomCenter( newCenter ) ) + bChanged = true; + if( bChanged ){ + emit propertiesChanged(); + } + } + + d->fixedDataCoordinateSpaceRelationOldSize = geometry; +} + +const QPointF CartesianCoordinatePlane::translate( const QPointF& diagramPoint ) const +{ + // Note: We do not test if the point lays inside of the data area, + // but we just apply the transformation calculations to the point. + // This allows for basic calculations done by the user, see e.g. + // the file examples/Lines/BubbleChart/mainwindow.cpp + return d->coordinateTransformation.translate ( diagramPoint ); +} + +const QPointF CartesianCoordinatePlane::translateBack( const QPointF& screenPoint ) const +{ + return d->coordinateTransformation.translateBack ( screenPoint ); +} + +void CartesianCoordinatePlane::setIsometricScaling ( bool onOff ) +{ + if ( d->isometricScaling != onOff ) + { + d->isometricScaling = onOff; + layoutDiagrams(); + emit propertiesChanged(); + } +} + +bool CartesianCoordinatePlane::doesIsometricScaling () const +{ + return d->isometricScaling; +} + +bool CartesianCoordinatePlane::doneSetZoomFactorX( double factor ) +{ + const bool done = ( d->coordinateTransformation.zoom.xFactor != factor ); + if( done ){ + d->coordinateTransformation.zoom.xFactor = factor; + if( d->autoAdjustGridToZoom ) + d->grid->setNeedRecalculate(); + } + return done; +} + +bool CartesianCoordinatePlane::doneSetZoomFactorY( double factor ) +{ + const bool done = ( d->coordinateTransformation.zoom.yFactor != factor ); + if( done ){ + d->coordinateTransformation.zoom.yFactor = factor; + if( d->autoAdjustGridToZoom ) + d->grid->setNeedRecalculate(); + } + return done; +} + +bool CartesianCoordinatePlane::doneSetZoomCenter( const QPointF& point ) +{ + const bool done = ( d->coordinateTransformation.zoom.center() != point ); + if( done ){ + d->coordinateTransformation.zoom.setCenter( point ); + if( d->autoAdjustGridToZoom ) + d->grid->setNeedRecalculate(); + } + return done; +} + +void CartesianCoordinatePlane::setZoomFactors( double factorX, double factorY ) +{ + if( doneSetZoomFactorX( factorX ) || doneSetZoomFactorY( factorY ) ){ + emit propertiesChanged(); + } +} + +void CartesianCoordinatePlane::setZoomFactorX( double factor ) +{ + if( doneSetZoomFactorX( factor ) ){ + emit propertiesChanged(); + } +} + +void CartesianCoordinatePlane::setZoomFactorY( double factor ) +{ + if( doneSetZoomFactorY( factor ) ){ + emit propertiesChanged(); + } +} + +void CartesianCoordinatePlane::setZoomCenter( const QPointF& point ) +{ + if( doneSetZoomCenter( point ) ){ + emit propertiesChanged(); + } +} + +QPointF CartesianCoordinatePlane::zoomCenter() const +{ + return d->coordinateTransformation.zoom.center(); +} + +double CartesianCoordinatePlane::zoomFactorX() const +{ + return d->coordinateTransformation.zoom.xFactor; +} + +double CartesianCoordinatePlane::zoomFactorY() const +{ + return d->coordinateTransformation.zoom.yFactor; +} + + +CartesianCoordinatePlane::AxesCalcMode CartesianCoordinatePlane::axesCalcModeY() const +{ + return d->coordinateTransformation.axesCalcModeY; +} + +CartesianCoordinatePlane::AxesCalcMode CartesianCoordinatePlane::axesCalcModeX() const +{ + return d->coordinateTransformation.axesCalcModeX; +} + +void CartesianCoordinatePlane::setAxesCalcModes( AxesCalcMode mode ) +{ + if( d->coordinateTransformation.axesCalcModeY != mode || + d->coordinateTransformation.axesCalcModeX != mode ){ + d->coordinateTransformation.axesCalcModeY = mode; + d->coordinateTransformation.axesCalcModeX = mode; + emit propertiesChanged(); + } +} + +void CartesianCoordinatePlane::setAxesCalcModeY( AxesCalcMode mode ) +{ + if( d->coordinateTransformation.axesCalcModeY != mode ){ + d->coordinateTransformation.axesCalcModeY = mode; + emit propertiesChanged(); + } +} + +void CartesianCoordinatePlane::setAxesCalcModeX( AxesCalcMode mode ) +{ + if( d->coordinateTransformation.axesCalcModeX != mode ){ + d->coordinateTransformation.axesCalcModeX = mode; + emit propertiesChanged(); + } +} + +void CartesianCoordinatePlane::setHorizontalRange( const QPair< qreal, qreal > & range ) +{ + if ( d->horizontalMin != range.first || d->horizontalMax != range.second ) { + d->autoAdjustHorizontalRangeToData = 100; + d->horizontalMin = range.first; + d->horizontalMax = range.second; + layoutDiagrams(); + emit propertiesChanged(); + } +} + +void CartesianCoordinatePlane::setVerticalRange( const QPair< qreal, qreal > & range ) +{ + + if ( d->verticalMin != range.first || d->verticalMax != range.second ) { + d->autoAdjustVerticalRangeToData = 100; + d->verticalMin = range.first; + d->verticalMax = range.second; + layoutDiagrams(); + emit propertiesChanged(); + } +} + +QPair< qreal, qreal > CartesianCoordinatePlane::horizontalRange( ) const +{ + return QPair( d->horizontalMin, d->horizontalMax ); +} + +QPair< qreal, qreal > CartesianCoordinatePlane::verticalRange( ) const +{ + return QPair( d->verticalMin, d->verticalMax ); +} + +void CartesianCoordinatePlane::adjustRangesToData() +{ + const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() ); + d->horizontalMin = dataBoundingRect.left(); + d->horizontalMax = dataBoundingRect.right(); + d->verticalMin = dataBoundingRect.top(); + d->verticalMax = dataBoundingRect.bottom(); + layoutDiagrams(); + emit propertiesChanged(); +} + +void CartesianCoordinatePlane::adjustHorizontalRangeToData() +{ + const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() ); + d->horizontalMin = dataBoundingRect.left(); + d->horizontalMax = dataBoundingRect.right(); + layoutDiagrams(); + emit propertiesChanged(); +} + +void CartesianCoordinatePlane::adjustVerticalRangeToData() +{ + const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() ); + d->verticalMin = dataBoundingRect.bottom(); + d->verticalMax = dataBoundingRect.top(); + layoutDiagrams(); + emit propertiesChanged(); +} + +void CartesianCoordinatePlane::setAutoAdjustHorizontalRangeToData( unsigned int percentEmpty ) +{ + if ( d->autoAdjustHorizontalRangeToData != percentEmpty ) + { + d->autoAdjustHorizontalRangeToData = percentEmpty; + d->horizontalMin = 0.0; + d->horizontalMax = 0.0; + layoutDiagrams(); + emit propertiesChanged(); + } +} + +void CartesianCoordinatePlane::setAutoAdjustVerticalRangeToData( unsigned int percentEmpty ) +{ + if ( d->autoAdjustVerticalRangeToData != percentEmpty ) + { + d->autoAdjustVerticalRangeToData = percentEmpty; + d->verticalMin = 0.0; + d->verticalMax = 0.0; + layoutDiagrams(); + emit propertiesChanged(); + } +} + +unsigned int CartesianCoordinatePlane::autoAdjustHorizontalRangeToData() const +{ + return d->autoAdjustHorizontalRangeToData; +} + +unsigned int CartesianCoordinatePlane::autoAdjustVerticalRangeToData() const +{ + return d->autoAdjustVerticalRangeToData; +} + +void CartesianCoordinatePlane::setGridAttributes( + Qt::Orientation orientation, + const GridAttributes& a ) +{ + if( orientation == Qt::Horizontal ) + d->gridAttributesHorizontal = a; + else + d->gridAttributesVertical = a; + setHasOwnGridAttributes( orientation, true ); + update(); + emit propertiesChanged(); +} + +void CartesianCoordinatePlane::resetGridAttributes( + Qt::Orientation orientation ) +{ + setHasOwnGridAttributes( orientation, false ); + update(); +} + +const GridAttributes CartesianCoordinatePlane::gridAttributes( + Qt::Orientation orientation ) const +{ + if( hasOwnGridAttributes( orientation ) ){ + if( orientation == Qt::Horizontal ) + return d->gridAttributesHorizontal; + else + return d->gridAttributesVertical; + }else{ + return globalGridAttributes(); + } +} + +void CartesianCoordinatePlane::setHasOwnGridAttributes( + Qt::Orientation orientation, bool on ) +{ + if( orientation == Qt::Horizontal ) + d->hasOwnGridAttributesHorizontal = on; + else + d->hasOwnGridAttributesVertical = on; + emit propertiesChanged(); +} + +bool CartesianCoordinatePlane::hasOwnGridAttributes( + Qt::Orientation orientation ) const +{ + return + ( orientation == Qt::Horizontal ) + ? d->hasOwnGridAttributesHorizontal + : d->hasOwnGridAttributesVertical; +} + +void CartesianCoordinatePlane::setAutoAdjustGridToZoom( bool autoAdjust ) +{ + if( d->autoAdjustGridToZoom != autoAdjust ){ + d->autoAdjustGridToZoom = autoAdjust; + d->grid->setNeedRecalculate(); + emit propertiesChanged(); + } +} + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +bool CartesianCoordinatePlane::autoAdjustGridToZoom() const +{ + return d->autoAdjustGridToZoom; +} + +AbstractCoordinatePlane* CartesianCoordinatePlane::sharedAxisMasterPlane( QPainter* painter ) +{ + CartesianCoordinatePlane* plane = this; + AbstractCartesianDiagram* diag = dynamic_cast< AbstractCartesianDiagram* >( plane->diagram() ); + const CartesianAxis* sharedAxis = 0; + if( diag != 0 ) + { + const CartesianAxisList axes = diag->axes(); + KDAB_FOREACH( const CartesianAxis* a, axes ) + { + CartesianCoordinatePlane* p = const_cast< CartesianCoordinatePlane* >( + dynamic_cast< const CartesianCoordinatePlane* >( a->coordinatePlane() ) ); + if( p != 0 && p != this ) + { + plane = p; + sharedAxis = a; + } + } + } + + if( plane == this || painter == 0 ) + return plane; + + const QPointF zero = QPointF( 0, 0 ); + const QPointF tenX = QPointF( 10, 0 ); + const QPointF tenY = QPointF( 0, 10 ); + + + if( sharedAxis->isOrdinate() ) + { + painter->translate( translate( zero ).x(), 0.0 ); + const qreal factor = (translate( tenX ) - translate( zero ) ).x() / ( plane->translate( tenX ) - plane->translate( zero ) ).x(); + painter->scale( factor, 1.0 ); + painter->translate( -plane->translate( zero ).x(), 0.0 ); + } + if( sharedAxis->isAbscissa() ) + { + painter->translate( 0.0, translate( zero ).y() ); + const qreal factor = (translate( tenY ) - translate( zero ) ).y() / ( plane->translate( tenY ) - plane->translate( zero ) ).y(); + painter->scale( 1.0, factor ); + painter->translate( 0.0, -plane->translate( zero ).y() ); + } + + + return plane; +} + +void CartesianCoordinatePlane::setHorizontalRangeReversed( bool reverse ) +{ + if( d->reverseHorizontalPlane == reverse ) + return; + + d->reverseHorizontalPlane = reverse; + layoutDiagrams(); + emit propertiesChanged(); +} + +bool CartesianCoordinatePlane::isHorizontalRangeReversed() const +{ + return d->reverseHorizontalPlane; +} + +void CartesianCoordinatePlane::setVerticalRangeReversed( bool reverse ) +{ + if( d->reverseVerticalPlane == reverse ) + return; + + d->reverseVerticalPlane = reverse; + layoutDiagrams(); + emit propertiesChanged(); +} + +bool CartesianCoordinatePlane::isVerticalRangeReversed() const +{ + return d->reverseVerticalPlane; +} + +QRectF CartesianCoordinatePlane::visibleDataRange() const +{ + QRectF result; + + const QRectF drawArea = drawingArea(); + + result.setTopLeft( translateBack( drawArea.topLeft() ) ); + result.setBottomRight( translateBack( drawArea.bottomRight() ) ); + + return result; +} + +void CartesianCoordinatePlane::setGeometry( const QRect& rectangle ) +{ + if( rectangle == geometry() ) + return; + + AbstractCoordinatePlane::setGeometry( rectangle ); + Q_FOREACH( AbstractDiagram* diagram, diagrams() ) { + diagram->resize( drawingArea().size() ); + } +} diff --git a/libkdchart/src/KDChartCartesianCoordinatePlane.h b/libkdchart/src/KDChartCartesianCoordinatePlane.h new file mode 100644 index 0000000..8678fc3 --- /dev/null +++ b/libkdchart/src/KDChartCartesianCoordinatePlane.h @@ -0,0 +1,481 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTCARTESIANCOORDINATEPLANE_H +#define KDCHARTCARTESIANCOORDINATEPLANE_H + +#include "KDChartAbstractCoordinatePlane.h" + +namespace KDChart { + + class Chart; + class PaintContext; + class AbstractDiagram; + class CartesianAxis; + class CartesianGrid; + + /** + * @brief Cartesian coordinate plane + */ + class KDCHART_EXPORT CartesianCoordinatePlane : public AbstractCoordinatePlane + { + Q_OBJECT + + Q_DISABLE_COPY( CartesianCoordinatePlane ) + KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( CartesianCoordinatePlane, Chart* ) + + friend class CartesianAxis; + friend class CartesianGrid; + + public: + explicit CartesianCoordinatePlane ( Chart* parent = 0 ); + ~CartesianCoordinatePlane(); + + void addDiagram ( AbstractDiagram* diagram ); + + void setIsometricScaling ( bool onOff ); + + bool doesIsometricScaling() const; + + const QPointF translate ( const QPointF& diagramPoint ) const; + + /** + * \sa setZoomFactorX, setZoomCenter + */ + virtual double zoomFactorX() const; + /** + * \sa setZoomFactorY, setZoomCenter + */ + virtual double zoomFactorY() const; + + /** + * \sa setZoomFactorX,setZoomFactorY + */ + virtual void setZoomFactors( double factorX, double factorY ); + /** + * \sa zoomFactorX, setZoomCenter + */ + virtual void setZoomFactorX( double factor ); + /** + * \sa zoomFactorY, setZoomCenter + */ + virtual void setZoomFactorY( double factor ); + + /** + * \sa setZoomCenter, setZoomFactorX, setZoomFactorY + */ + virtual QPointF zoomCenter() const; + + /** + * \sa zoomCenter, setZoomFactorX, setZoomFactorY + */ + virtual void setZoomCenter( const QPointF& center ); + + /** + * Allows to specify a fixed data-space / coordinate-space relation. If set + * to true then fixed bar widths are used, so you see more bars as the window + * is made wider. + * + * This allows to completely restrict the size of bars in a graph such that, + * upon resizing a window, the graphs coordinate plane will grow (add more + * ticks to x- and y-coordinates) rather than have the image grow. + */ + void setFixedDataCoordinateSpaceRelation( bool fixed ); + bool hasFixedDataCoordinateSpaceRelation() const; + + /** + * Allows to fix the lower bound of X axis to zero when diagram is in first quadrant. + * + * The default behavior is to lower x or y bound to be 0. If this behaviour is not wanted, + * either \a CartesianCoordinatePlane::setHorizontalRange could be used instead of letting + * KDChart auto-adjust the ranges, or this method can be used to disable this behavior. + */ + void setXAxisStartAtZero(bool fixedStart); + bool xAxisStartAtZero() const; + + /** + * \brief Set the boundaries of the visible value space displayed in horizontal direction. + * + * This is also known as the horizontal viewport. + * + * By default the horizontal range is adjusted to the range covered by the model's data, + * see setAutoAdjustHorizontalRangeToData for details. + * Calling setHorizontalRange with a valid range disables this default automatic adjusting, + * while on the other hand automatic adjusting will set these ranges. + * + * To disable use of this range you can either pass an empty pair by using the default + * constructor QPair() or you can set both values to the same which constitutes + * a null range. + * + * \note By default the visible data range often is larger than the + * range calculated from the data model (or set by setHoriz.|Vert.Range(), resp.). + * This is due to the built-in grid calculation feature: The visible start/end + * values get adjusted so that they match a main-grid line. + * You can turn this feature off for any of the four bounds by calling + * GridAttributes::setAdjustBoundsToGrid() for either the global grid-attributes + * or for the horizontal/vertical attrs separately. + * + * \note If you use user defined vertical ranges together with logarithmic scale, only + * positive values are supported. If you set it to negative values, the result is undefined. + * + * \param range a pair of values representing the smalles and the largest + * horizontal value space coordinate displayed. + * + * \sa setAutoAdjustHorizontalRangeToData, setVerticalRange + * \sa GridAttributes::setAdjustBoundsToGrid() + */ + void setHorizontalRange( const QPair & range ); + + /** + * \brief Set the boundaries of the visible value space displayed in vertical direction. + * + * This is also known as the vertical viewport. + * + * By default the vertical range is adjusted to the range covered by the model's data, + * see setAutoAdjustVerticalRangeToData for details. + * Calling setVerticalRange with a valid range disables this default automatic adjusting, + * while on the other hand automatic adjusting will set these ranges. + * + * To disable use of this range you can either pass an empty pair by using the default + * constructor QPair() or you can set setting both values to the same which constitutes + * a null range. + * + * \note By default the visible data range often is larger than the + * range calculated from the data model (or set by setHoriz.|Vert.Range(), resp.). + * This is due to the built-in grid calculation feature: The visible start/end + * values get adjusted so that they match a main-grid line. + * You can turn this feature off for any of the four bounds by calling + * GridAttributes::setAdjustBoundsToGrid() for either the global grid-attributes + * or for the horizontal/vertical attrs separately. + * + * \note If you use user defined vertical ranges together with logarithmic scale, only + * positive values are supported. If you set it to negative values, the result is undefined. + * + * \param range a pair of values representing the smalles and the largest + * vertical value space coordinate displayed. + * + * \sa setAutoAdjustVerticalRangeToData, setHorizontalRange + * \sa GridAttributes::setAdjustBoundsToGrid() + */ + void setVerticalRange( const QPair & range ); + + /** + * @return The largest and smallest visible horizontal value space + * value. If this is not explicitly set,or if both values are the same, + * the plane will use the union of the dataBoundaries of all + * associated diagrams. + * \see KDChart::AbstractDiagram::dataBoundaries + */ + QPair horizontalRange() const; + + /** + * @return The largest and smallest visible horizontal value space + * value. If this is not explicitly set, or if both values are the same, + * the plane will use the union of the dataBoundaries of all + * associated diagrams. + * \see KDChart::AbstractDiagram::dataBoundaries + */ + QPair verticalRange() const; + + /** + * \brief Automatically adjust horizontal range settings to the ranges covered by + * the model's values, when ever the data have changed, and then emit horizontalRangeAutomaticallyAdjusted. + * + * By default the horizontal range is adjusted automatically, if more than 67 percent of + * the available horizontal space would be empty otherwise. + * + * Range setting is adjusted if more than \c percentEmpty percent of the horizontal + * space covered by the coordinate plane would otherwise be empty. + * Automatic range adjusting can happen, when either all of the data are positive or all are negative. + * + * Set percentEmpty to 100 to disable automatic range adjusting. + * + * \param percentEmpty The maximal percentage of horizontal space that may be empty. + * + * \sa horizontalRangeAutomaticallyAdjusted + * \sa autoAdjustHorizontalRangeToData, adjustRangesToData + * \sa setHorizontalRange, setVerticalRange + * \sa setAutoAdjustVerticalRangeToData + */ + void setAutoAdjustHorizontalRangeToData( unsigned int percentEmpty = 67 ); + + /** + * \brief Automatically adjust vertical range settings to the ranges covered by + * the model's values, when ever the data have changed, and then emit verticalRangeAutomaticallyAdjusted. + * + * By default the vertical range is adjusted automatically, if more than 67 percent of + * the available vertical space would be empty otherwise. + * + * Range setting is adjusted if more than \c percentEmpty percent of the horizontal + * space covered by the coordinate plane would otherwise be empty. + * Automatic range adjusting can happen, when either all of the data are positive or all are negative. + * + * Set percentEmpty to 100 to disable automatic range adjusting. + * + * \param percentEmpty The maximal percentage of horizontal space that may be empty. + * + * \sa verticalRangeAutomaticallyAdjusted + * \sa autoAdjustVerticalRangeToData, adjustRangesToData + * \sa setHorizontalRange, setVerticalRange + * \sa setAutoAdjustHorizontalRangeToData + */ + void setAutoAdjustVerticalRangeToData( unsigned int percentEmpty = 67 ); + + /** + * \brief Returns the maximal allowed percent of the horizontal + * space covered by the coordinate plane that may be empty. + * + * \return A percent value indicating how much of the horizontal space may be empty. + * If more than this is empty, automatic range adjusting is applied. + * A return value of 100 indicates that no such automatic adjusting is done at all. + * + * \sa setAutoAdjustHorizontalRangeToData, adjustRangesToData + */ + unsigned int autoAdjustHorizontalRangeToData() const; + + /** + * \brief Returns the maximal allowed percent of the vertical + * space covered by the coordinate plane that may be empty. + * + * \return A percent value indicating how much of the vertical space may be empty. + * If more than this is empty, automatic range adjusting is applied. + * A return value of 100 indicates that no such automatic adjusting is done at all. + * + * \sa setAutoAdjustVerticalRangeToData, adjustRangesToData + */ + unsigned int autoAdjustVerticalRangeToData() const; + + + /** + * Set the attributes to be used for grid lines drawn in horizontal + * direction (or in vertical direction, resp.). + * + * To disable horizontal grid painting, for example, your code should like this: + * \code + * GridAttributes ga = plane->gridAttributes( Qt::Horizontal ); + * ga.setGridVisible( false ); + * plane-setGridAttributes( Qt::Horizontal, ga ); + * \endcode + * + * \note setGridAttributes overwrites the global attributes that + * were set by AbstractCoordinatePlane::setGlobalGridAttributes. + * To re-activate these global attributes you can call + * resetGridAttributes. + * + * \sa resetGridAttributes, gridAttributes + * \sa setAutoAdjustGridToZoom + * \sa AbstractCoordinatePlane::setGlobalGridAttributes + * \sa hasOwnGridAttributes + */ + void setGridAttributes( Qt::Orientation orientation, const GridAttributes & ); + + /** + * Reset the attributes to be used for grid lines drawn in horizontal + * direction (or in vertical direction, resp.). + * By calling this method you specify that the global attributes set by + * AbstractCoordinatePlane::setGlobalGridAttributes be used. + * + * \sa setGridAttributes, gridAttributes + * \sa setAutoAdjustGridToZoom + * \sa AbstractCoordinatePlane::globalGridAttributes + * \sa hasOwnGridAttributes + */ + void resetGridAttributes( Qt::Orientation orientation ); + + /** + * \return The attributes used for grid lines drawn in horizontal + * direction (or in vertical direction, resp.). + * + * \note This function always returns a valid set of grid attributes: + * If no special grid attributes were set foe this orientation + * the global attributes are returned, as returned by + * AbstractCoordinatePlane::globalGridAttributes. + * + * \sa setGridAttributes + * \sa resetGridAttributes + * \sa AbstractCoordinatePlane::globalGridAttributes + * \sa hasOwnGridAttributes + */ + const GridAttributes gridAttributes( Qt::Orientation orientation ) const; + + /** + * \return Returns whether the grid attributes have been set for the + * respective direction via setGridAttributes( orientation ). + * + * If false, the grid will use the global attributes set + * by AbstractCoordinatePlane::globalGridAttributes (or the default + * attributes, resp.) + * + * \sa setGridAttributes + * \sa resetGridAttributes + * \sa AbstractCoordinatePlane::globalGridAttributes + */ + bool hasOwnGridAttributes( Qt::Orientation orientation ) const; + + /** + * Disable / re-enable the built-in grid adjusting feature. + * + * By default additional lines will be drawn in a Linear grid when zooming in. + * + * \sa autoAdjustGridToZoom, setGridAttributes + */ + void setAutoAdjustGridToZoom( bool autoAdjust ); + + /** + * Return the status of the built-in grid adjusting feature. + * + * \sa setAutoAdjustGridToZoom + */ +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + const bool autoAdjustGridToZoom() const; +#else + bool autoAdjustGridToZoom() const; +#endif + + AxesCalcMode axesCalcModeY() const; + AxesCalcMode axesCalcModeX() const; + + /** Specifies the calculation modes for all axes */ + void setAxesCalcModes( AxesCalcMode mode ); + /** Specifies the calculation mode for all Ordinate axes */ + void setAxesCalcModeY( AxesCalcMode mode ); + /** Specifies the calculation mode for all Abscissa axes */ + void setAxesCalcModeX( AxesCalcMode mode ); + + /** reimpl */ + virtual void paint( QPainter* ); + + /** reimpl */ + AbstractCoordinatePlane* sharedAxisMasterPlane( QPainter* p = 0 ); + + /** + * Returns the currently visible data range. Might be greater than the + * range of the grid. + */ + QRectF visibleDataRange() const; + + /** + * Returns the logical area, i.e., the rectangle defined by the very top + * left and very bottom right coordinate. + */ + QRectF logicalArea() const; + + /** + * Returns the (physical) area occupied by the diagram. Unless zoom is applied + * (which is also true when a fixed data coordinate / space relation is used), + * \code diagramArea() == drawingArea() \endcode . + * \sa setFixedDataCoordinateSpaceRelation + * \sa drawingArea + */ + QRectF diagramArea() const; + + /** + * Returns the visible part of the diagram area, i.e. + * \code diagramArea().intersected( drawingArea() ) \endcode + * \sa diagramArea + */ + QRectF visibleDiagramArea() const; + + /** + * Sets whether the horizontal range should be reversed or not, i.e. + * small values to the left and large values to the right (the default) + * or vice versa. + * \param reverse Whether the horizontal range should be reversed or not + */ + void setHorizontalRangeReversed( bool reverse ); + + /** + * \return Whether the horizontal range is reversed or not + */ + bool isHorizontalRangeReversed() const; + + /** + * Sets whether the vertical range should be reversed or not, i.e. + * small values at the bottom and large values at the top (the default) + * or vice versa. + * \param reverse Whether the vertical range should be reversed or not + */ + void setVerticalRangeReversed( bool reverse ); + + /** + * \return Whether the vertical range is reversed or not + */ + bool isVerticalRangeReversed() const; + + /** + * reimplement from AbstractCoordinatePlane + */ + void setGeometry( const QRect& r ); + + public Q_SLOTS: + /** + * \brief Adjust both, horizontal and vertical range settings to the + * ranges covered by the model's data values. + * + * \sa setHorizontalRange, setVerticalRange + * \sa adjustHorizontalRangeToData, adjustVerticalRangeToData + * \sa setAutoAdjustHorizontalRangeToData, setAutoAdjustVerticalRangeToData + */ + void adjustRangesToData(); + + /** + * Adjust horizontal range settings to the ranges covered by the model's data values. + * \sa adjustRangesToData + */ + void adjustHorizontalRangeToData(); + + /** + * Adjust vertical range settings to the ranges covered by the model's data values. + * \sa adjustRangesToData + */ + void adjustVerticalRangeToData(); + + + protected: + QRectF getRawDataBoundingRectFromDiagrams() const; + QRectF adjustedToMaxEmptyInnerPercentage( + const QRectF& r, unsigned int percentX, unsigned int percentY ) const; + virtual QRectF calculateRawDataBoundingRect() const; + virtual DataDimensionsList getDataDimensionsList() const; + // the whole drawing area, includes diagrams and axes, but maybe smaller + // than (width, height): + virtual QRectF drawingArea() const; + const QPointF translateBack( const QPointF& screenPoint ) const; + void paintEvent ( QPaintEvent* ); + void layoutDiagrams(); + bool doneSetZoomFactorX( double factor ); + bool doneSetZoomFactorY( double factor ); + bool doneSetZoomCenter( const QPointF& center ); + + void handleFixedDataCoordinateSpaceRelation( const QRectF& geometry ); + + protected Q_SLOTS: + void slotLayoutChanged( AbstractDiagram* ); + + private: + void setHasOwnGridAttributes( + Qt::Orientation orientation, bool on ); + }; + +} + +#endif diff --git a/libkdchart/src/KDChartCartesianCoordinatePlane_p.h b/libkdchart/src/KDChartCartesianCoordinatePlane_p.h new file mode 100644 index 0000000..ec900d2 --- /dev/null +++ b/libkdchart/src/KDChartCartesianCoordinatePlane_p.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTCARTESIANCOORDINATEPLANE_P_H +#define KDCHARTCARTESIANCOORDINATEPLANE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractCoordinatePlane_p.h" +#include "CartesianCoordinateTransformation.h" +#include "KDChartCartesianGrid.h" + +#include + + +namespace KDChart { + +/** + * \internal + */ +class CartesianCoordinatePlane::Private : public AbstractCoordinatePlane::Private +{ + friend class CartesianCoordinatePlane; +public: + explicit Private(); + virtual ~Private() { } + + virtual void initialize() + { + bPaintIsRunning = false; + coordinateTransformation.axesCalcModeX = Linear; + coordinateTransformation.axesCalcModeY = Linear; + grid = new CartesianGrid(); + } + + virtual bool isVisiblePoint( + const AbstractCoordinatePlane * plane, + const QPointF& point ) const + { + QPointF p = point; + const CartesianCoordinatePlane* const ref = + dynamic_cast< const CartesianCoordinatePlane* >( const_cast< AbstractCoordinatePlane* >( plane )->sharedAxisMasterPlane() ); + const CartesianCoordinatePlane* const cartPlane = + dynamic_cast< const CartesianCoordinatePlane* >( plane ); + if( ref != 0 && ref != cartPlane ) + { + const QPointF logical = ref->translateBack( point ) - cartPlane->visibleDataRange().topLeft() + + ref->visibleDataRange().topLeft(); + p = ref->translate( logical ); + } + const QRectF geo( plane->geometry() ); + return geo.contains( p ); + } + + + // the coordinate plane will calculate the coordinate transformation: + CoordinateTransformation coordinateTransformation; + + bool bPaintIsRunning; + + // true after setGridAttributes( Qt::Orientation ) was used, + // false if resetGridAttributes( Qt::Orientation ) was called + bool hasOwnGridAttributesHorizontal; + bool hasOwnGridAttributesVertical; + + // true after the first resize event came in + // bool initialResizeEventReceived; + + // true if the coordinate plane scales isometrically + bool isometricScaling; + + GridAttributes gridAttributesHorizontal; + GridAttributes gridAttributesVertical; + + qreal horizontalMin; + qreal horizontalMax; + qreal verticalMin; + qreal verticalMax; + + // autoAdjustHorizontalRangeToData determines if and how much the horizontal range is adjusted. + // A value of 100 means that the fixed horizontal range will be used (e.g. set by the user), + // otherwise the value will be the percentage of the diagram's horizontal range that is to be + // left empty (i.e., it resembles the 'gap' between the horizontal extrema and the border of the + // diagram). + unsigned int autoAdjustHorizontalRangeToData; + + // autoAdjustVerticalRangeToData determines if and how much the vertical range is adjusted. + // A value of 100 means that the fixed vertical range will be used (e.g. set by the user), + // otherwise the value will be the percentage of the diagram's vertical range that is to be + // left empty (i.e., it resembles the 'gap' between the vertical extrema and the border of the + // diagram). + unsigned int autoAdjustVerticalRangeToData; + bool autoAdjustGridToZoom; + + bool fixedDataCoordinateSpaceRelation; + bool xAxisStartAtZero; + QRectF fixedDataCoordinateSpaceRelationOldSize; + + DataDimensionsList dimensions; + + bool reverseVerticalPlane; + bool reverseHorizontalPlane; +}; + + +KDCHART_IMPL_DERIVED_PLANE(CartesianCoordinatePlane, AbstractCoordinatePlane) + +} + +#endif /* KDCHARTBARDIAGRAM_P_H */ diff --git a/libkdchart/src/KDChartCartesianDiagramDataCompressor_p.cpp b/libkdchart/src/KDChartCartesianDiagramDataCompressor_p.cpp new file mode 100644 index 0000000..713e2fd --- /dev/null +++ b/libkdchart/src/KDChartCartesianDiagramDataCompressor_p.cpp @@ -0,0 +1,828 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartCartesianDiagramDataCompressor_p.h" + +#include +#include + +#include "KDChartAbstractCartesianDiagram.h" + +#include + +using namespace KDChart; +using namespace std; + +CartesianDiagramDataCompressor::CartesianDiagramDataCompressor( QObject* parent ) + : QObject( parent ) + , m_mode( Precise ) + , m_xResolution( 0 ) + , m_yResolution( 0 ) + , m_sampleStep( 0 ) + , m_datasetDimension( 1 ) +{ + calculateSampleStepWidth(); +} + +QModelIndexList CartesianDiagramDataCompressor::indexesAt( const CachePosition& position ) const +{ + if ( isValidCachePosition( position ) ) { + CachePosition posPrev( position ); + if( m_datasetDimension == 2 ){ + if(posPrev.second) + --posPrev.second; + }else{ + if(posPrev.first) + --posPrev.first; + } + const QModelIndexList indPrev = mapToModel( posPrev ); + const QModelIndexList indCur = mapToModel( position ); + + QModelIndexList indexes; + if( m_datasetDimension == 2 ) + { + const int iStart = (indPrev.empty() || indPrev==indCur) ? indCur.first().column() + : indPrev.first().column() + 1; + const int iEnd = indCur.last().column(); + for( int i=iStart; i<=iEnd; ++i){ + indexes << m_model->index( position.first, i, m_rootIndex ); + } + } + else + { + const int iStart = (indPrev.empty() || indPrev==indCur) ? indCur.first().row() + : indPrev.first().row() + 1; + const int iEnd = (indCur.isEmpty()) ? iStart : indCur.first().row(); + //qDebug()<index( i, position.second, m_rootIndex ); + } + } + return indexes; + } else { + return QModelIndexList(); + } +} + + +CartesianDiagramDataCompressor::DataValueAttributesList CartesianDiagramDataCompressor::aggregatedAttrs( + AbstractDiagram * diagram, + const QModelIndex & index, + const CachePosition& position ) const +{ + // return cached attrs, if any + DataValueAttributesCache::const_iterator i = m_dataValueAttributesCache.constFind(position); + if( i != m_dataValueAttributesCache.constEnd() ) + return i.value(); + // retrieve attrs from all cells between the prev. cell and the current one + CartesianDiagramDataCompressor::DataValueAttributesList allAttrs; + const QModelIndexList indexes( indexesAt( position ) ); + KDAB_FOREACH( QModelIndex idx, indexes ) { + DataValueAttributes attrs( diagram->dataValueAttributes( idx ) ); + if( attrs.isVisible() ){ + // make sure no duplicate attrs are stored + bool isDuplicate = false; + CartesianDiagramDataCompressor::DataValueAttributesList::const_iterator i = allAttrs.constBegin(); + while (i != allAttrs.constEnd()) { + if( i.value() == attrs ){ + isDuplicate = true; + break; + } + ++i; + } + if( !isDuplicate ){ + //qDebug()<dataValueAttributes( index ); + } + // cache the attrs + m_dataValueAttributesCache[position] = allAttrs; + return allAttrs; +} + + +void CartesianDiagramDataCompressor::slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end ) +{ + if ( parent != m_rootIndex ) + return; + Q_ASSERT( start <= end ); + + CachePosition startPos = mapToCache( start, 0 ); + CachePosition endPos = mapToCache( end, 0 ); + + static const CachePosition NullPosition( -1, -1 ); + if( startPos == NullPosition ) + { + rebuildCache(); + startPos = mapToCache( start, 0 ); + endPos = mapToCache( end, 0 ); + // The start position still isn't valid, + // means that no resolution was set yet or we're about to add the first rows + if( startPos == NullPosition ) { + return; + } + } + + start = startPos.first; + end = endPos.first; + + for( int i = 0; i < m_data.size(); ++i ) + { + Q_ASSERT( start >= 0 && start <= m_data[ i ].size() ); + m_data[ i ].insert( start, end - start + 1, DataPoint() ); + } +} + +void CartesianDiagramDataCompressor::slotRowsInserted( const QModelIndex& parent, int start, int end ) +{ + if ( parent != m_rootIndex ) + return; + Q_ASSERT( start <= end ); + + CachePosition startPos = mapToCache( start, 0 ); + CachePosition endPos = mapToCache( end, 0 ); + + static const CachePosition NullPosition( -1, -1 ); + if( startPos == NullPosition ) + { + // Rebuild the cache at this point if we have added the first rows + rebuildCache(); + startPos = mapToCache( start, 0 ); + endPos = mapToCache( end, 0 ); + // The start position still isn't valid, + // means that no resolution was set yet + if( startPos == NullPosition ) { + return; + } + } + + start = startPos.first; + end = endPos.first; + + for( int i = 0; i < m_data.size(); ++i ) + { + for( int j = start; j < m_data[i].size(); ++j ) { + retrieveModelData( CachePosition( j, i ) ); + } + } +} + +void CartesianDiagramDataCompressor::slotColumnsAboutToBeInserted( const QModelIndex& parent, int start, int end ) +{ + if ( parent != m_rootIndex ) + return; + Q_ASSERT( start <= end ); + + CachePosition startPos = mapToCache( 0, start ); + CachePosition endPos = mapToCache( 0, end ); + + static const CachePosition NullPosition( -1, -1 ); + if( startPos == NullPosition ) + { + rebuildCache(); + startPos = mapToCache( 0, start ); + endPos = mapToCache( 0, end ); + // The start position still isn't valid, + // means that no resolution was set yet or we're about to add the first columns + if( startPos == NullPosition ) { + return; + } + } + + start = startPos.second; + end = endPos.second; + + const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution ); + Q_ASSERT( start >= 0 && start <= m_data.size() ); + m_data.insert( start, end - start + 1, QVector< DataPoint >( rowCount ) ); +} + +void CartesianDiagramDataCompressor::slotColumnsInserted( const QModelIndex& parent, int start, int end ) +{ + if ( parent != m_rootIndex ) + return; + Q_ASSERT( start <= end ); + + CachePosition startPos = mapToCache( 0, start ); + CachePosition endPos = mapToCache( 0, end ); + + static const CachePosition NullPosition( -1, -1 ); + if( startPos == NullPosition ) + { + // Rebuild the cache at this point if we have added the first columns + rebuildCache(); + startPos = mapToCache( 0, start ); + endPos = mapToCache( 0, end ); + // The start position still isn't valid, + // means that no resolution was set yet + if( startPos == NullPosition ) { + return; + } + } + + start = startPos.second; + end = endPos.second; + + for( int i = start; i < m_data.size(); ++i ) + { + for(int j = 0; j < m_data[i].size(); ++j ) { + retrieveModelData( CachePosition( j, i ) ); + } + } +} + +void CartesianDiagramDataCompressor::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) +{ + if ( parent != m_rootIndex ) + return; + Q_ASSERT( start <= end ); + + CachePosition startPos = mapToCache( start, 0 ); + CachePosition endPos = mapToCache( end, 0 ); + + static const CachePosition NullPosition( -1, -1 ); + if( startPos == NullPosition ) + { + rebuildCache(); + startPos = mapToCache( start, 0 ); + endPos = mapToCache( end, 0 ); + // The start position still isn't valid, + // probably means that no resolution was set yet + if( startPos == NullPosition ) { + return; + } + } + + start = startPos.first; + end = endPos.first; + + for( int i = 0; i < m_data.size(); ++i ) + { + m_data[ i ].remove( start, end - start + 1 ); + } +} + +void CartesianDiagramDataCompressor::slotRowsRemoved( const QModelIndex& parent, int start, int end ) +{ + if ( parent != m_rootIndex ) + return; + Q_ASSERT( start <= end ); + + CachePosition startPos = mapToCache( start, 0 ); + CachePosition endPos = mapToCache( end, 0 ); + + start = startPos.first; + end = endPos.first; + + static const CachePosition NullPosition( -1, -1 ); + if( startPos == NullPosition ) + { + // Since we should already have rebuilt the cache, it won't help to rebuild it again. + // Do not Q_ASSERT() though, since the resolution might simply not be set or we might now have 0 rows + return; + } + + for( int i = 0; i < m_data.size(); ++i ) { + for(int j = start; j < m_data[i].size(); ++j ) { + retrieveModelData( CachePosition( j, i ) ); + } + } +} + +void CartesianDiagramDataCompressor::slotColumnsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) +{ + if ( parent != m_rootIndex ) + return; + Q_ASSERT( start <= end ); + + CachePosition startPos = mapToCache( 0, start ); + CachePosition endPos = mapToCache( 0, end ); + + static const CachePosition NullPosition( -1, -1 ); + if( startPos == NullPosition ) + { + rebuildCache(); + startPos = mapToCache( 0, start ); + endPos = mapToCache( 0, end ); + // The start position still isn't valid, + // probably means that no resolution was set yet + if( startPos == NullPosition ) { + return; + } + } + + start = startPos.second; + end = endPos.second; + + m_data.remove( start, end - start + 1 ); +} + +void CartesianDiagramDataCompressor::slotColumnsRemoved( const QModelIndex& parent, int start, int end ) +{ + if ( parent != m_rootIndex ) + return; + Q_ASSERT( start <= end ); + + const CachePosition startPos = mapToCache( 0, start ); + const CachePosition endPos = mapToCache( 0, end ); + + start = startPos.second; + end = endPos.second; + + static const CachePosition NullPosition( -1, -1 ); + if( startPos == NullPosition ) + { + // Since we should already have rebuilt the cache, it won't help to rebuild it again. + // Do not Q_ASSERT() though, since the resolution might simply not be set or we might now have 0 columns + return; + } + + for( int i = start; i < m_data.size(); ++i ) { + for( int j = 0; j < m_data[i].size(); ++j ) { + retrieveModelData( CachePosition( j, i ) ); + } + } +} + +void CartesianDiagramDataCompressor::slotModelHeaderDataChanged( Qt::Orientation orientation, int first, int last ) +{ + if( orientation != Qt::Vertical ) + return; + + const QModelIndex firstRow = m_model->index( 0, first, m_rootIndex ); + const QModelIndex lastRow = m_model->index( m_model->rowCount( m_rootIndex ) - 1, last, m_rootIndex ); + + slotModelDataChanged( firstRow, lastRow ); +} + +void CartesianDiagramDataCompressor::slotModelDataChanged( + const QModelIndex& topLeftIndex, + const QModelIndex& bottomRightIndex ) +{ + if ( topLeftIndex.parent() != m_rootIndex ) + return; + Q_ASSERT( topLeftIndex.parent() == bottomRightIndex.parent() ); + Q_ASSERT( topLeftIndex.row() <= bottomRightIndex.row() ); + Q_ASSERT( topLeftIndex.column() <= bottomRightIndex.column() ); + CachePosition topleft = mapToCache( topLeftIndex ); + CachePosition bottomright = mapToCache( bottomRightIndex ); + for ( int row = topleft.first; row <= bottomright.first; ++row ) + for ( int column = topleft.second; column <= bottomright.second; ++column ) + invalidate( CachePosition( row, column ) ); +} + +void CartesianDiagramDataCompressor::slotModelLayoutChanged() +{ + rebuildCache(); + calculateSampleStepWidth(); +} + +void CartesianDiagramDataCompressor::slotDiagramLayoutChanged( AbstractDiagram* diagramBase ) +{ + AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( diagramBase ); + Q_ASSERT( diagram ); + if ( diagram->datasetDimension() != m_datasetDimension ) { + setDatasetDimension( diagram->datasetDimension() ); + } +} + +int CartesianDiagramDataCompressor::modelDataColumns() const +{ + Q_ASSERT( m_datasetDimension != 0 ); + // only operational if there is a model and a resolution + if ( m_model ) { + const int columns = m_model->columnCount( m_rootIndex ) / m_datasetDimension; + + if( columns != m_data.size() ) + { + rebuildCache(); + } + + Q_ASSERT( columns == m_data.size() ); + return columns; + } else { + return 0; + } +} + +int CartesianDiagramDataCompressor::modelDataRows() const +{ + // only operational if there is a model, columns, and a resolution + if ( m_model && m_model->columnCount( m_rootIndex ) > 0 && m_xResolution > 0 ) { + return m_data.isEmpty() ? 0 : m_data.first().size(); + } else { + return 0; + } +} + +void CartesianDiagramDataCompressor::setModel( QAbstractItemModel* model ) +{ + if ( m_model != 0 && m_model != model ) { + disconnect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ), + this, SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) ); + disconnect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), + this, SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) ); + disconnect( m_model, SIGNAL( layoutChanged() ), + this, SLOT( slotModelLayoutChanged() ) ); + disconnect( m_model, SIGNAL( rowsAboutToBeInserted( QModelIndex, int, int ) ), + this, SLOT( slotRowsAboutToBeInserted( QModelIndex, int, int ) ) ); + disconnect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), + this, SLOT( slotRowsInserted( QModelIndex, int, int ) ) ); + disconnect( m_model, SIGNAL( rowsAboutToBeRemoved( QModelIndex, int, int ) ), + this, SLOT( slotRowsAboutToBeRemoved( QModelIndex, int, int ) ) ); + disconnect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), + this, SLOT( slotRowsRemoved( QModelIndex, int, int ) ) ); + disconnect( m_model, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ), + this, SLOT( slotColumnsAboutToBeInserted( QModelIndex, int, int ) ) ); + disconnect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ), + this, SLOT( slotColumnsInserted( QModelIndex, int, int ) ) ); + disconnect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ), + this, SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) ); + disconnect( m_model, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ), + this, SLOT( slotColumnsAboutToBeRemoved( QModelIndex, int, int ) ) ); + disconnect( m_model, SIGNAL( modelReset() ), + this, SLOT( rebuildCache() ) ); + m_model = 0; + } + + m_modelCache.setModel( model ); + + if ( model != 0 ) { + m_model = model; + connect( m_model, SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ), + SLOT( slotModelHeaderDataChanged( Qt::Orientation, int, int ) ) ); + connect( m_model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), + SLOT( slotModelDataChanged( QModelIndex, QModelIndex ) ) ); + connect( m_model, SIGNAL( layoutChanged() ), + SLOT( slotModelLayoutChanged() ) ); + connect( m_model, SIGNAL( rowsAboutToBeInserted( QModelIndex, int, int ) ), + SLOT( slotRowsAboutToBeInserted( QModelIndex, int, int ) ) ); + connect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), + SLOT( slotRowsInserted( QModelIndex, int, int ) ) ); + connect( m_model, SIGNAL( rowsAboutToBeRemoved( QModelIndex, int, int ) ), + SLOT( slotRowsAboutToBeRemoved( QModelIndex, int, int ) ) ); + connect( m_model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), + SLOT( slotRowsRemoved( QModelIndex, int, int ) ) ); + connect( m_model, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ), + SLOT( slotColumnsAboutToBeInserted( QModelIndex, int, int ) ) ); + connect( m_model, SIGNAL( columnsInserted( QModelIndex, int, int ) ), + SLOT( slotColumnsInserted( QModelIndex, int, int ) ) ); + connect( m_model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ), + SLOT( slotColumnsRemoved( QModelIndex, int, int ) ) ); + connect( m_model, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ), + SLOT( slotColumnsAboutToBeRemoved( QModelIndex, int, int ) ) ); + connect( m_model, SIGNAL( modelReset() ), + this, SLOT( rebuildCache() ) ); + } + rebuildCache(); + calculateSampleStepWidth(); +} + +void CartesianDiagramDataCompressor::setRootIndex( const QModelIndex& root ) +{ + if ( m_rootIndex != root ) { + Q_ASSERT( root.model() == m_model || !root.isValid() ); + m_rootIndex = root; + m_modelCache.setRootIndex( root ); + rebuildCache(); + calculateSampleStepWidth(); + } +} +void CartesianDiagramDataCompressor::setResolution( int x, int y ) +{ + const int oldX = m_xResolution; + const int oldY = m_yResolution; + + if( m_datasetDimension != 1 ) + { + // just ignore the resolution in that case + m_xResolution = m_model == 0 ? 0 : m_model->rowCount( m_rootIndex ); + m_yResolution = qMax( 0, y ); + } + else if ( x != m_xResolution || y != m_yResolution ) { + m_xResolution = qMax( 0, x ); + m_yResolution = qMax( 0, y ); + rebuildCache(); + calculateSampleStepWidth(); + } + + if( oldX != m_xResolution || ( oldY != m_yResolution && m_datasetDimension == 1 ) ) + { + rebuildCache(); + calculateSampleStepWidth(); + } +} + +void CartesianDiagramDataCompressor::clearCache() +{ + for ( int column = 0; column < m_data.size(); ++column ) + m_data[column].fill( DataPoint() ); +} + +void CartesianDiagramDataCompressor::rebuildCache() const +{ + Q_ASSERT( m_datasetDimension != 0 ); + + m_data.clear(); + const int columnCount = m_model ? m_model->columnCount( m_rootIndex ) / m_datasetDimension : 0; + const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution ); + m_data.resize( columnCount ); + for ( int i = 0; i < columnCount; ++i ) { + m_data[i].resize( rowCount ); + } + // also empty the attrs cache + m_dataValueAttributesCache.clear(); +} + +const CartesianDiagramDataCompressor::DataPoint& CartesianDiagramDataCompressor::data( const CachePosition& position ) const +{ + static DataPoint NullDataPoint; + if ( ! isValidCachePosition( position ) ) return NullDataPoint; + if ( ! isCached( position ) ) retrieveModelData( position ); + return m_data[ position.second ][ position.first ]; +} + +QPair< QPointF, QPointF > CartesianDiagramDataCompressor::dataBoundaries() const +{ + const int colCount = modelDataColumns(); + double xMin = std::numeric_limits< double >::quiet_NaN(); + double xMax = std::numeric_limits< double >::quiet_NaN(); + double yMin = std::numeric_limits< double >::quiet_NaN(); + double yMax = std::numeric_limits< double >::quiet_NaN(); + + for( int column = 0; column < colCount; ++column ) + { + const DataPointVector& data = m_data[ column ]; + int row = 0; + for( DataPointVector::const_iterator it = data.begin(); it != data.end(); ++it, ++row ) + { + const DataPoint& p = *it; + if( !p.index.isValid() ) + retrieveModelData( CachePosition( row, column ) ); + + const double valueX = ISNAN( p.key ) ? 0.0 : p.key; + const double valueY = ISNAN( p.value ) ? 0.0 : p.value; + if( ISNAN( xMin ) ) + { + xMin = valueX; + xMax = valueX; + yMin = valueY; + yMax = valueY; + } + else + { + xMin = qMin( xMin, valueX ); + xMax = qMax( xMax, valueX ); + yMin = qMin( yMin, valueY ); + yMax = qMax( yMax, valueY ); + } + } + } + + // NOTE: calculateDataBoundaries must return the *real* data boundaries! + // i.e. we may NOT fake yMin to be qMin( 0.0, yMin ) + // (khz, 2008-01-24) + const QPointF bottomLeft( QPointF( xMin, yMin ) ); + const QPointF topRight( QPointF( xMax, yMax ) ); + return QPair< QPointF, QPointF >( bottomLeft, topRight ); +} + +void CartesianDiagramDataCompressor::retrieveModelData( const CachePosition& position ) const +{ + Q_ASSERT( isValidCachePosition( position ) ); + DataPoint result; + + switch(m_mode ) { + case Precise: + { + bool forceHidden = false; + result.hidden = true; + const QModelIndexList indexes = mapToModel( position ); + if( m_datasetDimension != 1 ) + { + Q_ASSERT( indexes.count() == 2 ); + + // try the ColumnDataRole approach first + const int xColumn = indexes.first().column(); + const int yColumn = indexes.last().column(); + const QVariantList xValues = m_model->headerData( xColumn, Qt::Horizontal, ColumnDataRole ).toList(); + const QVariantList yValues = m_model->headerData( yColumn, Qt::Horizontal, ColumnDataRole ).toList(); + + if( !xValues.isEmpty() && !yValues.isEmpty() ) + { + Q_ASSERT( xValues.count() == yValues.count() ); + int row = 0; + QVariantList::const_iterator itX = xValues.begin(); + QVariantList::const_iterator itY = yValues.begin(); + for( ; itX != xValues.end(); ++itX, ++itY, ++row ) + { + DataPoint result; + result.index = m_model->index( row, xColumn, m_rootIndex ); + if( !itX->isNull() ) + { + result.key = itX->toDouble(); + result.value = itY->toDouble(); + } + m_data[ position.second ][ row ] = result; + } + return; + } + + const QModelIndex& xIndex = indexes.first(); + const QModelIndex& yIndex = indexes.last(); + const double xData = m_modelCache.data( xIndex ); + const double yData = m_modelCache.data( yIndex ); + result.index = xIndex; + result.key = xData; + result.value = yData; + } + else + { + if ( ! indexes.isEmpty() ) { + result.value = std::numeric_limits< double >::quiet_NaN(); + result.key = 0.0; + Q_FOREACH( const QModelIndex& index, indexes ) { + const double value = m_modelCache.data( index ); + if( !ISNAN( value ) ) + { + result.value = ISNAN( result.value ) ? value : result.value + value; + } + result.key += index.row(); + } + result.index = indexes.at( 0 ); + result.key /= indexes.size(); + result.value /= indexes.size(); + } + } + if( !forceHidden ) + { + Q_FOREACH( const QModelIndex& index, indexes ) + { + // the point is visible if any of the points at this pixel position is visible + if ( qVariantValue( m_model->data( index, DataHiddenRole ) ) == false ) { + result.hidden = false; + } + } + } + } + break; + case SamplingSeven: + default: + { + } + break; + }; + + m_data[position.second][position.first] = result; + Q_ASSERT( isCached( position ) ); +} + +CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache( + const QModelIndex& index ) const +{ + Q_ASSERT( m_datasetDimension != 0 ); + + static const CachePosition NullPosition( -1, -1 ); + if ( ! index.isValid() ) return NullPosition; + return mapToCache( index.row(), index.column() ); +} + +CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache( + int row, int column ) const +{ + Q_ASSERT( m_datasetDimension != 0 ); + + if ( m_data.size() == 0 || m_data[0].size() == 0 ) return mapToCache( QModelIndex() ); + // assumption: indexes per column == 1 + if ( indexesPerPixel() == 0 ) return mapToCache( QModelIndex() ); + return CachePosition( static_cast< int >( ( row ) / indexesPerPixel() ), column / m_datasetDimension ); +} + +QModelIndexList CartesianDiagramDataCompressor::mapToModel( const CachePosition& position ) const +{ + if ( isValidCachePosition( position ) ) { + QModelIndexList indexes; + if( m_datasetDimension == 2 ) + { + indexes << m_model->index( position.first, position.second * 2, m_rootIndex ); + indexes << m_model->index( position.first, position.second * 2 + 1, m_rootIndex ); + } + else + { + // assumption: indexes per column == 1 + const qreal ipp = indexesPerPixel(); + for ( int i = 0; i < ipp; ++i ) { + const QModelIndex index = m_model->index( qRound( position.first * ipp ) + i, position.second, m_rootIndex ); + if( index.isValid() ) + indexes << index; + } + } + return indexes; + } else { + return QModelIndexList(); + } +} + +qreal CartesianDiagramDataCompressor::indexesPerPixel() const +{ + if ( m_data.size() == 0 ) return 0; + if ( m_data[0].size() == 0 ) return 0; + if ( ! m_model ) return 0; + return static_cast< qreal >( m_model->rowCount( m_rootIndex ) ) / static_cast< qreal >( m_data[0].size() ); +} + +bool CartesianDiagramDataCompressor::isValidCachePosition( const CachePosition& position ) const +{ + if ( ! m_model ) return false; + if ( m_data.size() == 0 || m_data[0].size() == 0 ) return false; + if ( position.second < 0 || position.second >= m_data.size() ) return false; + if ( position.first < 0 || position.first >= m_data[position.second].size() ) return false; + return true; +} + +void CartesianDiagramDataCompressor::invalidate( const CachePosition& position ) +{ + if ( isValidCachePosition( position ) ) { + m_data[position.second][position.first] = DataPoint(); + // Also invalidate the data value attributes at "position". + // Otherwise the user overwrites the attributes without us noticing + // it because we keep reading what's in the cache. + m_dataValueAttributesCache.remove( position ); + } +} + +bool CartesianDiagramDataCompressor::isCached( const CachePosition& position ) const +{ + Q_ASSERT( isValidCachePosition( position ) ); + const DataPoint& p = m_data[position.second][position.first]; + return p.index.isValid(); +} + +void CartesianDiagramDataCompressor::calculateSampleStepWidth() +{ + if ( m_mode == Precise ) { + m_sampleStep = 1; + return; + } + + static unsigned int SomePrimes[] = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, + 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, + 151, 211, 313, 401, 503, 607, 701, 811, 911, 1009, + 10037, 12911, 16001, 20011, 50021, + 100003, 137867, 199999, 500009, 707753, 1000003, 0 + }; // ... after that, having a model at all becomes impractical + + // we want at least 17 samples per data point, using a prime step width + const double WantedSamples = 17; + if ( WantedSamples > indexesPerPixel() ) { + m_sampleStep = 1; + } else { + int i; + for ( i = 0; SomePrimes[i] != 0; ++i ) { + if ( WantedSamples * SomePrimes[i+1] > indexesPerPixel() ) { + break; + } + } + m_sampleStep = SomePrimes[i]; + if ( SomePrimes[i] == 0 ) { + m_sampleStep = SomePrimes[i-1]; + } else { + m_sampleStep = SomePrimes[i]; + } + } +} + +void CartesianDiagramDataCompressor::setDatasetDimension( int dimension ) +{ + if ( dimension != m_datasetDimension ) { + m_datasetDimension = dimension; + rebuildCache(); + calculateSampleStepWidth(); + } +} diff --git a/libkdchart/src/KDChartCartesianDiagramDataCompressor_p.h b/libkdchart/src/KDChartCartesianDiagramDataCompressor_p.h new file mode 100644 index 0000000..8ee8568 --- /dev/null +++ b/libkdchart/src/KDChartCartesianDiagramDataCompressor_p.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTCARTESIANDIAGRAMDATACOMPRESSOR_H +#define KDCHARTCARTESIANDIAGRAMDATACOMPRESSOR_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include +#include +#include + +#include "KDChartDataValueAttributes.h" +#include "KDChartModelDataCache_p.h" + +#include "kdchart_export.h" + +class CartesianDiagramDataCompressorTests; +class QAbstractItemModel; + +namespace KDChart { + + class AbstractDiagram; + + // - transparently compress table model data if the diagram widget + // size does not allow to display all data points in an acceptable way + // - the class acts much like a proxy model, but is not + // implemented as one, to avoid performance penalties by QVariant + // conversions + // - a wanted side effect is that the compressor will deliver + // more precise values for more precise media, like paper + // (a) this is absolutely strictly seriously private API of KDChart + // (b) if possible, this class is going to be templatized for + // different diagram types + + // KDCHART_EXPORT is needed as long there's a test using + // this class directly + class KDCHART_EXPORT CartesianDiagramDataCompressor : public QObject + { + Q_OBJECT + friend class ::CartesianDiagramDataCompressorTests; + + public: + class DataPoint { + public: + DataPoint() + : key( std::numeric_limits< qreal >::quiet_NaN() ), + value( std::numeric_limits< qreal >::quiet_NaN() ), + hidden( false ) + {} + qreal key; + qreal value; + bool hidden; + QModelIndex index; + }; + typedef QVector DataPointVector; + class CachePosition { + public: + CachePosition() + : first( -1 ), + second( -1 ) + {} + CachePosition( int first, int second ) + : first( first ), + second( second ) + {} + int first; + int second; + + bool operator==( const CachePosition& rhs ) const + { + return first == rhs.first && + second == rhs.second; + } + bool operator<( const CachePosition& rhs ) const + { + // This function is used to topologically sort all cache positions. + + // Think of them as entries in a matrix or table: + // An entry comes before another entry if it is either above the other + // entry, or in the same row and to the left of the other entry. + return first < rhs.first || ( first == rhs.first && second < rhs.second ); + } + }; + + typedef QMap DataValueAttributesList; + typedef QMap DataValueAttributesCache; + + enum ApproximationMode { + // do not approximate, interpolate by averaging all + // datapoints for a pixel + Precise, + // approximate by averaging out over prime number distances + SamplingSeven + }; + + explicit CartesianDiagramDataCompressor( QObject* parent = 0 ); + + // input: model, chart resolution, approximation mode + void setModel( QAbstractItemModel* ); + void setRootIndex( const QModelIndex& root ); + void setResolution( int x, int y ); + void setApproximationMode( ApproximationMode mode ); + void setDatasetDimension( int dimension ); + + // output: resulting model resolution, data points + // FIXME (Mirko) rather stupid naming, Mirko! + int modelDataColumns() const; + int modelDataRows() const; + const DataPoint& data( const CachePosition& ) const; + + QPair< QPointF, QPointF > dataBoundaries() const; + + QModelIndexList indexesAt( const CachePosition& position ) const; + DataValueAttributesList aggregatedAttrs( + AbstractDiagram * diagram, + const QModelIndex & index, + const CachePosition& position ) const; + + private Q_SLOTS: + void slotRowsAboutToBeInserted( const QModelIndex&, int, int ); + void slotRowsInserted( const QModelIndex&, int, int ); + void slotRowsAboutToBeRemoved( const QModelIndex&, int, int ); + void slotRowsRemoved( const QModelIndex&, int, int ); + + void slotColumnsAboutToBeInserted( const QModelIndex&, int, int ); + void slotColumnsInserted( const QModelIndex&, int, int ); + void slotColumnsAboutToBeRemoved( const QModelIndex&, int, int ); + void slotColumnsRemoved( const QModelIndex&, int, int ); + + void slotModelHeaderDataChanged( Qt::Orientation, int, int ); + void slotModelDataChanged( const QModelIndex&, const QModelIndex& ); + void slotModelLayoutChanged(); + // FIXME resolution changes and root index changes should all + // be catchable with this method: + void slotDiagramLayoutChanged( AbstractDiagram* ); + + // geometry has changed + void rebuildCache() const; + // reset all cached values, without changing the cache geometry + void clearCache(); + + private: + // mark a cache position as invalid + void invalidate( const CachePosition& ); + // verify it is within the range + bool isValidCachePosition( const CachePosition& ) const; + + CachePosition mapToCache( const QModelIndex& ) const; + CachePosition mapToCache( int row, int column ) const; + QModelIndexList mapToModel( const CachePosition& ) const; + qreal indexesPerPixel() const; + + // retrieve data from the model, put it into the cache + void retrieveModelData( const CachePosition& ) const; + // check if a data point is in the cache: + bool isCached( const CachePosition& ) const; + // set sample step width according to settings: + void calculateSampleStepWidth(); + + // one per dataset + mutable QVector m_data; + ApproximationMode m_mode; + int m_xResolution; + int m_yResolution; + QPointer m_model; + unsigned int m_sampleStep; + QModelIndex m_rootIndex; + ModelDataCache< qreal > m_modelCache; + mutable DataValueAttributesCache m_dataValueAttributesCache; + int m_datasetDimension; + }; +} + +#endif diff --git a/libkdchart/src/KDChartCartesianGrid.cpp b/libkdchart/src/KDChartCartesianGrid.cpp new file mode 100644 index 0000000..2479638 --- /dev/null +++ b/libkdchart/src/KDChartCartesianGrid.cpp @@ -0,0 +1,697 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartCartesianGrid.h" +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartPaintContext.h" +#include "KDChartPainterSaver_p.h" +#include "KDChartPrintingParameters.h" + +#include + +#include + +#include + +using namespace KDChart; + +CartesianGrid::CartesianGrid() + : AbstractGrid(), m_minsteps( 2 ), m_maxsteps( 12 ) +{ +} + +CartesianGrid::~CartesianGrid() +{ +} + +int CartesianGrid::minimalSteps() const +{ + return m_minsteps; +} + +void CartesianGrid::setMinimalSteps(int minsteps) +{ + m_minsteps = minsteps; +} + +int CartesianGrid::maximalSteps() const +{ + return m_maxsteps; +} + +void CartesianGrid::setMaximalSteps(int maxsteps) +{ + m_maxsteps = maxsteps; +} + +void CartesianGrid::drawGrid( PaintContext* context ) +{ + //qDebug() << "KDChart::CartesianGrid::drawGrid( PaintContext* context ) called"; + + CartesianCoordinatePlane* plane = dynamic_cast(context->coordinatePlane()); + + // This plane is used for tranlating the coordinates - not for the data boundaries + PainterSaver p( context->painter() ); + plane = dynamic_cast< CartesianCoordinatePlane* >( plane->sharedAxisMasterPlane( context->painter() ) ); + + Q_ASSERT_X ( plane, "CartesianGrid::drawGrid", + "Bad function call: PaintContext::coodinatePlane() NOT a cartesian plane." ); + + + const GridAttributes gridAttrsX( plane->gridAttributes( Qt::Horizontal ) ); + const GridAttributes gridAttrsY( plane->gridAttributes( Qt::Vertical ) ); + + //qDebug() << "OK:"; + if ( !gridAttrsX.isGridVisible() && !gridAttrsY.isGridVisible() ) return; + //qDebug() << "A"; + + // important: Need to update the calculated mData, + // before we may use it! + updateData( context->coordinatePlane() ); + + if( plane->axesCalcModeX() == KDChart::AbstractCoordinatePlane::Logarithmic && mData.first().stepWidth == 0.0 ) + mData.first().stepWidth = 1.0; + if( plane->axesCalcModeY() == KDChart::AbstractCoordinatePlane::Logarithmic && mData.last().stepWidth == 0.0 ) + mData.last().stepWidth = 1.0; + + // test for programming errors: critical + Q_ASSERT_X ( mData.count() == 2, "CartesianGrid::drawGrid", + "Error: updateData did not return exactly two dimensions." ); + + // test for invalid boundaries: non-critical + if( !isBoundariesValid( mData ) ) return; + //qDebug() << "B"; + + DataDimension dimX = mData.first(); + const DataDimension& dimY = mData.last(); + // test for other programming errors: critical + Q_ASSERT_X ( dimX.stepWidth, "CartesianGrid::drawGrid", + "Error: updateData returned a Zero step width for the X grid." ); + Q_ASSERT_X ( dimY.stepWidth, "CartesianGrid::drawGrid", + "Error: updateData returned a Zero step width for the Y grid." ); + + + qreal numberOfUnitLinesX = + qAbs( dimX.distance() / dimX.stepWidth ) + + (dimX.isCalculated ? 1.0 : 0.0); + qreal numberOfUnitLinesY = + qAbs( dimY.distance() / dimY.stepWidth ) + + (dimY.isCalculated ? 1.0 : 0.0); + //qDebug("numberOfUnitLinesX: %f numberOfUnitLinesY: %f",numberOfUnitLinesX,numberOfUnitLinesY); + + // do not draw a Zero size grid (and do not divide by Zero) + if( numberOfUnitLinesX <= 0.0 || numberOfUnitLinesY <= 0.0 ) return; + //qDebug() << "C"; + + const QPointF p1 = plane->translate( QPointF(dimX.start, dimY.start) ); + const QPointF p2 = plane->translate( QPointF(dimX.end, dimY.end) ); +//qDebug() << "dimX.isCalculated:" << dimX.isCalculated << "dimY.isCalculated:" << dimY.isCalculated; +//qDebug() << "dimX.start: " << dimX.start << "dimX.end: " << dimX.end; +//qDebug() << "dimY.start: " << dimY.start << "dimY.end: " << dimY.end; +//qDebug() << "p1:" << p1 << " p2:" << p2; + + const qreal screenRangeX = qAbs ( p1.x() - p2.x() ); + const qreal screenRangeY = qAbs ( p1.y() - p2.y() ); + + /* + * let us paint the grid at a smaller resolution + * the user can disable at any time + * by setting the grid attribute to false + * Same Value as for Cartesian Axis + */ + static const qreal GridLineDistanceTreshold = 4.0; // pixels between each grid line + const qreal MinimumPixelsBetweenLines = + GridLineDistanceTreshold; + //qDebug() << "x step " << dimX.stepWidth << " y step " << dimY.stepWidth; + + //qreal unitFactorX = 1.0; +// qreal unitFactorY = 1.0; + + //FIXME(khz): Remove this code, and do the calculation in the grid calc function + if( ! dimX.isCalculated ){ + + while( screenRangeX / numberOfUnitLinesX <= MinimumPixelsBetweenLines ){ + dimX.stepWidth *= 10.0; + dimX.subStepWidth *= 10.0; + numberOfUnitLinesX = qAbs( dimX.distance() / dimX.stepWidth ); + } + } + if( dimX.subStepWidth && (screenRangeX / (dimX.distance() / dimX.subStepWidth) <= MinimumPixelsBetweenLines) ){ + dimX.subStepWidth = 0.0; + //qDebug() << "de-activating grid sub steps: not enough space"; + } + + const bool drawUnitLinesX = gridAttrsX.isGridVisible() && + (screenRangeX / numberOfUnitLinesX > MinimumPixelsBetweenLines); + const bool drawUnitLinesY = gridAttrsY.isGridVisible() && + (screenRangeY / numberOfUnitLinesY > MinimumPixelsBetweenLines); + + const bool isLogarithmicX = dimX.isCalculated && (dimX.calcMode == AbstractCoordinatePlane::Logarithmic ); + const bool isLogarithmicY = (dimY.calcMode == AbstractCoordinatePlane::Logarithmic ); +/* + while ( !drawUnitLinesX ) { + unitFactorX *= 10.0; + drawUnitLinesX = screenRangeX / (numberOfUnitLinesX / unitFactorX) > MinimumPixelsBetweenLines; + } + while ( !drawUnitLinesY ) { + unitFactorY *= 10.0; + drawUnitLinesY = screenRangeY / (numberOfUnitLinesY / unitFactorY) > MinimumPixelsBetweenLines; + } +*/ + + const bool drawSubGridLinesX = isLogarithmicX || + ((dimX.subStepWidth != 0.0) && + (screenRangeX / (numberOfUnitLinesX / dimX.stepWidth * dimX.subStepWidth) > MinimumPixelsBetweenLines) && + gridAttrsX.isSubGridVisible()); + + const bool drawSubGridLinesY = isLogarithmicY || + ((dimY.subStepWidth != 0.0) && + (screenRangeY / (numberOfUnitLinesY / dimY.stepWidth * dimY.subStepWidth) > MinimumPixelsBetweenLines) && + gridAttrsY.isSubGridVisible()); + + qreal minValueX = qMin( dimX.start, dimX.end ); + qreal maxValueX = qMax( dimX.start, dimX.end ); + qreal minValueY = qMin( dimY.start, dimY.end ); + qreal maxValueY = qMax( dimY.start, dimY.end ); + AbstractGrid::adjustLowerUpperRange( minValueX, maxValueX, dimX.stepWidth, true, true ); + AbstractGrid::adjustLowerUpperRange( minValueY, maxValueY, dimY.stepWidth, true, true ); + + if ( drawSubGridLinesX ) { + context->painter()->setPen( PrintingParameters::scalePen( gridAttrsX.subGridPen() ) ); + qreal f = minValueX; + qreal fLogSubstep = minValueX; + + int logSubstep = 0; + while ( f <= maxValueX ) { + QPointF topPoint( f, maxValueY ); + QPointF bottomPoint( f, minValueY ); + topPoint = plane->translate( topPoint ); + bottomPoint = plane->translate( bottomPoint ); + context->painter()->drawLine( topPoint, bottomPoint ); + if ( isLogarithmicX ){ + if( logSubstep == 9 ){ + fLogSubstep *= ( fLogSubstep > 0.0 ) ? 10.0 : 0.1; + if( fLogSubstep == 0.0 ) + fLogSubstep = pow( 10.0, floor( log10( dimX.start ) ) ); + + logSubstep = 0; + f = fLogSubstep; + } + else + { + f += fLogSubstep; + } + ++logSubstep; + }else{ + f += dimX.subStepWidth; + } + + if(maxValueX == 0 && minValueX == 0) + break; + } + } + + if ( drawSubGridLinesY ) { + context->painter()->setPen( PrintingParameters::scalePen( gridAttrsY.subGridPen() ) ); + qreal f = minValueY; + qreal fLogSubstep = minValueY; + + int logSubstep = 0; + while ( f <= maxValueY ) { + //qDebug() << "sub grid line Y at" << f; + QPointF leftPoint( minValueX, f ); + QPointF rightPoint( maxValueX, f ); + leftPoint = plane->translate( leftPoint ); + rightPoint = plane->translate( rightPoint ); + context->painter()->drawLine( leftPoint, rightPoint ); + if ( isLogarithmicY ){ + if( logSubstep == 9 ){ + fLogSubstep *= ( fLogSubstep > 0.0 ) ? 10.0 : 0.1; + if( fLogSubstep == 0.0 ) + fLogSubstep = pow( 10.0, floor( log10( dimY.start ) ) ); + + logSubstep = 0; + f = fLogSubstep; + } + else + { + f += fLogSubstep; + } + ++logSubstep; + }else{ + f += dimY.subStepWidth; + } + + if(maxValueY == 0 && minValueY == 0) + break; + } + } + + const bool drawXZeroLineX + = dimX.isCalculated && + gridAttrsX.zeroLinePen().style() != Qt::NoPen; + + const bool drawZeroLineY + = gridAttrsY.zeroLinePen().style() != Qt::NoPen; + + if ( drawUnitLinesX || drawXZeroLineX ) { + //qDebug() << "E"; + if ( drawUnitLinesX ) + context->painter()->setPen( PrintingParameters::scalePen( gridAttrsX.gridPen() ) ); +// const qreal minX = dimX.start; + + qreal f = minValueX; + + while ( f <= maxValueX ) { + // PENDING(khz) FIXME: make draving/not drawing of Zero line more sophisticated?: + const bool zeroLineHere = drawXZeroLineX && (f == 0.0); + if ( drawUnitLinesX || zeroLineHere ){ + //qDebug("main grid line X at: %f --------------------------",f); + QPointF topPoint( f, maxValueY ); + QPointF bottomPoint( f, minValueY ); + topPoint = plane->translate( topPoint ); + bottomPoint = plane->translate( bottomPoint ); + if ( zeroLineHere ) + context->painter()->setPen( PrintingParameters::scalePen( gridAttrsX.zeroLinePen() ) ); + context->painter()->drawLine( topPoint, bottomPoint ); + if ( zeroLineHere ) + context->painter()->setPen( PrintingParameters::scalePen( gridAttrsX.gridPen() ) ); + } + if ( isLogarithmicX ) { + f *= ( f > 0.0 ) ? 10.0 : 0.1; + if( f == 0.0 ) + f = pow( 10.0, floor( log10( dimX.start ) ) ); + } + else + f += dimX.stepWidth; + + if(maxValueX == 0 && minValueX == 0) + break; + } + // draw the last line if not logarithmic calculation + // we need the in order to get the right grid line painted + // when f + dimX.stepWidth jump over maxValueX + if ( ! isLogarithmicX ) + context->painter()->drawLine( plane->translate( QPointF( maxValueX, maxValueY ) ), + plane->translate( QPointF( maxValueX, minValueY ) ) ); + + } + if ( drawUnitLinesY || drawZeroLineY ) { + //qDebug() << "F"; + if ( drawUnitLinesY ) + context->painter()->setPen( PrintingParameters::scalePen( gridAttrsY.gridPen() ) ); + //const qreal minY = dimY.start; + //qDebug("minY: %f maxValueY: %f dimY.stepWidth: %f",minY,maxValueY,dimY.stepWidth); + qreal f = minValueY; + + while ( f <= maxValueY ) { + // PENDING(khz) FIXME: make draving/not drawing of Zero line more sophisticated?: + //qDebug("main grid line Y at: %f",f); + const bool zeroLineHere = (f == 0.0); + if ( drawUnitLinesY || zeroLineHere ){ + QPointF leftPoint( minValueX, f ); + QPointF rightPoint( maxValueX, f ); + leftPoint = plane->translate( leftPoint ); + rightPoint = plane->translate( rightPoint ); + if ( zeroLineHere ) + context->painter()->setPen( PrintingParameters::scalePen( gridAttrsY.zeroLinePen() ) ); + context->painter()->drawLine( leftPoint, rightPoint ); + if ( zeroLineHere ) + context->painter()->setPen( PrintingParameters::scalePen( gridAttrsY.gridPen() ) ); + } + if ( isLogarithmicY ) { + f *= ( f > 0.0 ) ? 10.0 : 0.1; + if( f == 0.0 ) + f = pow( 10.0, floor( log10( dimY.start ) ) ); + } + else + f += dimY.stepWidth; + + if(maxValueY == 0 && minValueY == 0) + break; + } + } + //qDebug() << "Z"; +} + + +DataDimensionsList CartesianGrid::calculateGrid( + const DataDimensionsList& rawDataDimensions ) const +{ + Q_ASSERT_X ( rawDataDimensions.count() == 2, "CartesianGrid::calculateGrid", + "Error: calculateGrid() expects a list with exactly two entries." ); + + CartesianCoordinatePlane* plane = dynamic_cast( mPlane ); + Q_ASSERT_X ( plane, "CartesianGrid::calculateGrid", + "Error: PaintContext::calculatePlane() called, but no cartesian plane set." ); + + DataDimensionsList l( rawDataDimensions ); + // rule: Returned list is either empty, or it is providing two + // valid dimensions, complete with two non-Zero step widths. + if( isBoundariesValid( l ) ) { + const QPointF translatedBottomLeft( plane->translateBack( plane->geometry().bottomLeft() ) ); + const QPointF translatedTopRight( plane->translateBack( plane->geometry().topRight() ) ); + //qDebug() << "CartesianGrid::calculateGrid() first:" << l.first().start << l.first().end << " last:" << l.last().start << l.last().end; + //qDebug() << "CartesianGrid::calculateGrid() translated x:" << translatedBottomLeft.x() << translatedTopRight.x() << " y:" << translatedBottomLeft.y() << translatedTopRight.y(); + //qDebug() << "CartesianGrid::calculateGrid() raw data y-range :" << l.last().end - l.last().start; + //qDebug() << "CartesianGrid::calculateGrid() translated y-range:" << translatedTopRight.y() - translatedBottomLeft.y(); + + /* Code is obsolete. The dataset dimension of the diagram should *never* be > 1. + if( l.first().isCalculated + && plane->autoAdjustGridToZoom() + && plane->axesCalcModeX() == CartesianCoordinatePlane::Linear + && plane->zoomFactorX() > 1.0 ) + { + l.first().start = translatedBottomLeft.x(); + l.first().end = translatedTopRight.x(); + } + */ + + const GridAttributes gridAttrsX( plane->gridAttributes( Qt::Horizontal ) ); + const GridAttributes gridAttrsY( plane->gridAttributes( Qt::Vertical ) ); + + const DataDimension dimX + = calculateGridXY( l.first(), Qt::Horizontal, + gridAttrsX.adjustLowerBoundToGrid(), + gridAttrsX.adjustUpperBoundToGrid() ); + if( dimX.stepWidth ){ + //qDebug("CartesianGrid::calculateGrid() l.last().start: %f l.last().end: %f", l.last().start, l.last().end); + //qDebug(" l.first().start: %f l.first().end: %f", l.first().start, l.first().end); + + // one time for the min/max value + const DataDimension minMaxY + = calculateGridXY( l.last(), Qt::Vertical, + gridAttrsY.adjustLowerBoundToGrid(), + gridAttrsY.adjustUpperBoundToGrid() ); + + if( plane->autoAdjustGridToZoom() + && plane->axesCalcModeY() == CartesianCoordinatePlane::Linear + && plane->zoomFactorY() > 1.0 ) + { + l.last().start = translatedBottomLeft.y(); + l.last().end = translatedTopRight.y(); + } + // and one other time for the step width + const DataDimension dimY + = calculateGridXY( l.last(), Qt::Vertical, + gridAttrsY.adjustLowerBoundToGrid(), + gridAttrsY.adjustUpperBoundToGrid() ); + if( dimY.stepWidth ){ + l.first().start = dimX.start; + l.first().end = dimX.end; + l.first().stepWidth = dimX.stepWidth; + l.first().subStepWidth = dimX.subStepWidth; + l.last().start = minMaxY.start; + l.last().end = minMaxY.end; + l.last().stepWidth = dimY.stepWidth; + l.last().subStepWidth = dimY.subStepWidth; + //qDebug() << "CartesianGrid::calculateGrid() final grid y-range:" << l.last().end - l.last().start << " step width:" << l.last().stepWidth << endl; + // calculate some reasonable subSteps if the + // user did not set the sub grid but did set + // the stepWidth. + + // FIXME (Johannes) + // the last (y) dimension is not always the dimension for the ordinate! + // since there's no way to check for the orientation of this dimension here, + // we cannot automatically assume substep values + //if ( dimY.subStepWidth == 0 ) + // l.last().subStepWidth = dimY.stepWidth/2; + //else + // l.last().subStepWidth = dimY.subStepWidth; + } + } + } + //qDebug() << "CartesianGrid::calculateGrid() final grid Y-range:" << l.last().end - l.last().start << " substep width:" << l.last().subStepWidth; + //qDebug() << "CartesianGrid::calculateGrid() final grid X-range:" << l.first().end - l.first().start << " substep width:" << l.first().subStepWidth; + + return l; +} + + +qreal fastPow10( int x ) +{ + qreal res = 1.0; + if( 0 <= x ){ + for( int i = 1; i <= x; ++i ) + res *= 10.0; + }else{ + for( int i = -1; i >= x; --i ) + res /= 10.0; + } + return res; +} + +#if defined ( Q_WS_WIN) +#define trunc(x) ((int)(x)) +#endif + +DataDimension CartesianGrid::calculateGridXY( + const DataDimension& rawDataDimension, + Qt::Orientation orientation, + bool adjustLower, bool adjustUpper ) const +{ + CartesianCoordinatePlane* const plane = dynamic_cast( mPlane ); + if( ((orientation == Qt::Vertical) && (plane->autoAdjustVerticalRangeToData() >= 100)) + || ((orientation == Qt::Horizontal) && (plane->autoAdjustHorizontalRangeToData() >= 100)) ) + { + adjustLower = false; + adjustUpper = false; + } + + DataDimension dim( rawDataDimension ); + if( dim.isCalculated && dim.start != dim.end ){ + if( dim.calcMode == AbstractCoordinatePlane::Linear ){ + // linear ( == not-logarithmic) calculation + if( dim.stepWidth == 0.0 ){ + QList granularities; + switch( dim.sequence ){ + case KDChartEnums::GranularitySequence_10_20: + granularities << 1.0 << 2.0; + break; + case KDChartEnums::GranularitySequence_10_50: + granularities << 1.0 << 5.0; + break; + case KDChartEnums::GranularitySequence_25_50: + granularities << 2.5 << 5.0; + break; + case KDChartEnums::GranularitySequence_125_25: + granularities << 1.25 << 2.5; + break; + case KDChartEnums::GranularitySequenceIrregular: + granularities << 1.0 << 1.25 << 2.0 << 2.5 << 5.0; + break; + default: + break; + } + //qDebug("CartesianGrid::calculateGridXY() dim.start: %f dim.end: %f", dim.start, dim.end); + calculateStepWidth( + dim.start, dim.end, granularities, orientation, + dim.stepWidth, dim.subStepWidth, + adjustLower, adjustUpper ); + } + // if needed, adjust start/end to match the step width: + //qDebug() << "CartesianGrid::calculateGridXY() has 1st linear range: min " << dim.start << " and max" << dim.end; + + AbstractGrid::adjustLowerUpperRange( dim.start, dim.end, dim.stepWidth, + adjustLower, adjustUpper ); + //qDebug() << "CartesianGrid::calculateGridXY() returns linear range: min " << dim.start << " and max" << dim.end; + }else{ + // logarithmic calculation with negative values + if( dim.end <= 0 ) + { + qreal min; + const qreal minRaw = qMin( dim.start, dim.end ); + const int minLog = -static_cast(trunc( log10( -minRaw ) ) ); + if( minLog >= 0 ) + min = qMin( minRaw, -std::numeric_limits< qreal >::epsilon() ); + else + min = -fastPow10( -(minLog-1) ); + + qreal max; + const qreal maxRaw = qMin( -std::numeric_limits< qreal >::epsilon(), qMax( dim.start, dim.end ) ); + const int maxLog = -static_cast(ceil( log10( -maxRaw ) ) ); + if( maxLog >= 0 ) + max = -1; + else if( fastPow10( -maxLog ) < maxRaw ) + max = -fastPow10( -(maxLog+1) ); + else + max = -fastPow10( -maxLog ); + if( adjustLower ) + dim.start = min; + if( adjustUpper ) + dim.end = max; + dim.stepWidth = -pow( 10.0, ceil( log10( qAbs( max - min ) / 10.0 ) ) ); + } + // logarithmic calculation (ignoring all negative values) + else + { + qreal min; + const qreal minRaw = qMax( qMin( dim.start, dim.end ), qreal( 0.0 ) ); + const int minLog = static_cast(trunc( log10( minRaw ) ) ); + if( minLog <= 0 && dim.end < 1.0 ) + min = qMax( minRaw, std::numeric_limits< qreal >::epsilon() ); + else if( minLog <= 0 ) + min = qMax( qreal(0.00001), dim.start ); + else + min = fastPow10( minLog-1 ); + + // Uh oh. Logarithmic scaling doesn't work with a lower or upper + // bound being 0. + const bool zeroBound = dim.start == 0.0 || dim.end == 0.0; + + qreal max; + const qreal maxRaw = qMax( qMax( dim.start, dim.end ), qreal( 0.0 ) ); + const int maxLog = static_cast(ceil( log10( maxRaw ) ) ); + if( maxLog <= 0 ) + max = 1; + else if( fastPow10( maxLog ) < maxRaw ) + max = fastPow10( maxLog+1 ); + else + max = fastPow10( maxLog ); + if( adjustLower || zeroBound ) + dim.start = min; + if( adjustUpper || zeroBound ) + dim.end = max; + dim.stepWidth = pow( 10.0, ceil( log10( qAbs( max - min ) / 10.0 ) ) ); + } + } + }else{ + //qDebug() << "CartesianGrid::calculateGridXY() returns stepWidth 1.0 !!"; + // Do not ignore the user configuration + dim.stepWidth = dim.stepWidth ? dim.stepWidth : 1.0; + } + return dim; +} + + +static void calculateSteps( + qreal start_, qreal end_, const QList& list, + int minSteps, int maxSteps, + int power, + qreal& steps, qreal& stepWidth, + bool adjustLower, bool adjustUpper ) +{ + //qDebug("-----------------------------------\nstart: %f end: %f power-of-ten: %i", start_, end_, power); + + qreal distance; + steps = 0.0; + + const int lastIdx = list.count()-1; + for( int i = 0; i <= lastIdx; ++i ){ + const qreal testStepWidth = list.at(lastIdx - i) * fastPow10( power ); + //qDebug( "testing step width: %f", testStepWidth); + qreal start = qMin( start_, end_ ); + qreal end = qMax( start_, end_ ); + //qDebug("pre adjusting start: %f end: %f", start, end); + AbstractGrid::adjustLowerUpperRange( start, end, testStepWidth, adjustLower, adjustUpper ); + //qDebug("post adjusting start: %f end: %f", start, end); + + const qreal testDistance = qAbs(end - start); + const qreal testSteps = testDistance / testStepWidth; + + //qDebug() << "testDistance:" << testDistance << " distance:" << distance; + if( (minSteps <= testSteps) && (testSteps <= maxSteps) + && ( (steps == 0.0) || (testDistance <= distance) ) ){ + steps = testSteps; + stepWidth = testStepWidth; + distance = testDistance; + //qDebug( "start: %f end: %f step width: %f steps: %f distance: %f", start, end, stepWidth, steps, distance); + } + } +} + + +void CartesianGrid::calculateStepWidth( + qreal start_, qreal end_, + const QList& granularities, + Qt::Orientation orientation, + qreal& stepWidth, qreal& subStepWidth, + bool adjustLower, bool adjustUpper ) const +{ + Q_UNUSED( orientation ); + + Q_ASSERT_X ( granularities.count(), "CartesianGrid::calculateStepWidth", + "Error: The list of GranularitySequence values is empty." ); + QList list( granularities ); + qSort( list ); + + const qreal start = qMin( start_, end_); + const qreal end = qMax( start_, end_); + const qreal distance = end - start; + //qDebug( "raw data start: %f end: %f", start, end); + + qreal steps; + int power = 0; + while( list.last() * fastPow10( power ) < distance ){ + ++power; + }; + // We have the sequence *two* times in the calculation test list, + // so we will be sure to find the best match: + const int count = list.count(); + QList testList; + + for( int dec = -1; dec == -1 || fastPow10( dec + 1 ) >= distance; --dec ) + for( int i = 0; i < count; ++i ) + testList << list.at(i) * fastPow10( dec ); + + testList << list; + + do{ + //qDebug() << "list:" << testList; + //qDebug( "calculating steps: power: %i", power); + calculateSteps( start, end, testList, m_minsteps, m_maxsteps, power, + steps, stepWidth, + adjustLower, adjustUpper ); + --power; + }while( steps == 0.0 ); + ++power; + //qDebug( "steps calculated: stepWidth: %f steps: %f", stepWidth, steps); + + // find the matching sub-grid line width in case it is + // not set by the user + + if ( subStepWidth == 0.0 ) { + if( stepWidth == list.first() * fastPow10( power ) ){ + subStepWidth = list.last() * fastPow10( power-1 ); + //qDebug("A"); + }else if( stepWidth == list.first() * fastPow10( power-1 ) ){ + subStepWidth = list.last() * fastPow10( power-2 ); + //qDebug("B"); + }else{ + qreal smallerStepWidth = list.first(); + for( int i = 1; i < list.count(); ++i ){ + if( stepWidth == list.at( i ) * fastPow10( power ) ){ + subStepWidth = smallerStepWidth * fastPow10( power ); + break; + } + if( stepWidth == list.at( i ) * fastPow10( power-1 ) ){ + subStepWidth = smallerStepWidth * fastPow10( power-1 ); + break; + } + smallerStepWidth = list.at( i ); + } + + //qDebug("C"); + } + } + //qDebug("CartesianGrid::calculateStepWidth() found stepWidth %f (%f steps) and sub-stepWidth %f", stepWidth, steps, subStepWidth); +} diff --git a/libkdchart/src/KDChartCartesianGrid.h b/libkdchart/src/KDChartCartesianGrid.h new file mode 100644 index 0000000..d7cefcf --- /dev/null +++ b/libkdchart/src/KDChartCartesianGrid.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTCARTESIANGRID_H +#define KDCHARTCARTESIANGRID_H + +#include "KDChartCartesianCoordinatePlane.h" +#include "KDChartAbstractGrid.h" + +namespace KDChart { + + class PaintContext; + class CartesianCoordinatePlane; + + /** + * \internal + * + * \brief Class for the grid in a cartesian plane. + * + * The CartesianGrid interface is used + * for calculating and for drawing + * the horizonal grid lines, and the vertical grid lines + * of a cartesian coordinate plane. + */ + class CartesianGrid : public AbstractGrid + { + public: + CartesianGrid(); + virtual ~CartesianGrid(); + + int minimalSteps() const; + void setMinimalSteps(int minsteps); + + int maximalSteps() const; + void setMaximalSteps(int maxsteps); + + void drawGrid( PaintContext* context ); + + private: + int m_minsteps; + int m_maxsteps; + + DataDimensionsList calculateGrid( + const DataDimensionsList& rawDataDimensions ) const; + + /** + * Helper function called by calculateGrid(). + * + * Classes derived from CartesianGrid can overwrite calculateGridXY() if they need + * a way of calculating the start/end/step width of their horizontal grid + * lines (or of their vertical grid lines, resp.), that is different from the + * default implementation of this method. + * + * \param adjustLower If true, the function adjusts the start value + * so it matches the position of a grid line, if false the start value is + * the raw data dimension start value. + * \param adjustUpper If true, the function adjusts the end value + * so it matches the position of a grid line, if false the end value is + * the raw data dimension end value. + */ + virtual DataDimension calculateGridXY( + const DataDimension& rawDataDimension, + Qt::Orientation orientation, + bool adjustLower, bool adjustUpper ) const; + + /** + * Helper function called by calculateGridXY(). + * + * Classes derived from CartesianGrid can overwrite calculateStepWidth() if they need + * a way of calculating the step width, based upon given start/end values + * for their horizontal grid lines (or for their vertical grid lines, resp.), + * that is different from the default implementation of this method. + * + * \note The CartesianGrid class tries to keep the displayed range as near to + * the raw data range as possible, so in most cases there should be no reason + * to change the default implementation: Adjusting + * KDChart::GridAttributes::setGridGranularitySequence should be sufficient. + * + * \param start The raw start value of the data range. + * \param end The raw end value of the data range. + * \param granularities The list of allowed granularities. + * \param adjustLower If true, the function adjusts the start value + * so it matches the position of a grid line, if false the start value is + * left as it is, in any case the value is adjusted for internal calculation only. + * \param adjustUpper If true, the function adjusts the end value + * so it matches the position of a grid line, if false the end value is + * left as it is, in any case the value is adjusted for internal calculation only. + * + * \returns stepWidth: One of the values from the granularities + * list, optionally multiplied by a positive (or negative, resp.) + * power of ten. subStepWidth: The matching width for sub-grid lines. + */ + virtual void calculateStepWidth( + qreal start, qreal end, + const QList& granularities, + Qt::Orientation orientation, + qreal& stepWidth, qreal& subStepWidth, + bool adjustLower, bool adjustUpper ) const; + }; + +} + +#endif diff --git a/libkdchart/src/KDChartChart.cpp b/libkdchart/src/KDChartChart.cpp new file mode 100644 index 0000000..1ab1e34 --- /dev/null +++ b/libkdchart/src/KDChartChart.cpp @@ -0,0 +1,1624 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartChart.h" +#include "KDChartChart_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "KDChartCartesianCoordinatePlane.h" +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartHeaderFooter.h" +#include "KDChartEnums.h" +#include "KDChartLegend.h" +#include "KDChartLayoutItems.h" +#include +#include +#include "KDChartPainterSaver_p.h" +#include "KDChartPrintingParameters.h" + +#if defined KDAB_EVAL +#include "../evaldialog/evaldialog.h" +#endif + +#include + +#define SET_ALL_MARGINS_TO_ZERO + +// Layout widgets even if they are not visible +class MyWidgetItem : public QWidgetItem +{ +public: + explicit MyWidgetItem(QWidget *w, Qt::Alignment alignment = 0) + : QWidgetItem(w) { + setAlignment( alignment ); + } + /*reimp*/ bool isEmpty() const { + QWidget* w = const_cast(this)->widget(); + // legend->hide() should indeed hide the legend, + // but a legend in a chart that hasn't been shown yet isn't hidden + // (as can happen when using Chart::paint() without showing the chart) + return w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide); + } +}; + +using namespace KDChart; + +void Chart::Private::slotUnregisterDestroyedLegend( Legend *l ) +{ + legends.removeAll( l ); + slotRelayout(); +} + +void Chart::Private::slotUnregisterDestroyedHeaderFooter( HeaderFooter* hf ) +{ + headerFooters.removeAll( hf ); + hf->removeFromParentLayout(); + textLayoutItems.remove( textLayoutItems.indexOf( hf ) ); + slotRelayout(); +} + +void Chart::Private::slotUnregisterDestroyedPlane( AbstractCoordinatePlane* plane ) +{ + coordinatePlanes.removeAll( plane ); + Q_FOREACH ( AbstractCoordinatePlane* p, coordinatePlanes ) + { + if ( p->referenceCoordinatePlane() == plane) { + p->setReferenceCoordinatePlane(0); + } + } + plane->layoutPlanes(); +} + +Chart::Private::Private( Chart* chart_ ) + : chart( chart_ ) + , layout( 0 ) + , vLayout( 0 ) + , planesLayout( 0 ) + , headerLayout( 0 ) + , footerLayout( 0 ) + , dataAndLegendLayout( 0 ) + , globalLeadingLeft( 0 ) + , globalLeadingRight( 0 ) + , globalLeadingTop( 0 ) + , globalLeadingBottom( 0 ) +{ + for( int row = 0; row < 3; ++row ) + { + for( int column = 0; column < 3; ++column ) + { + dummyHeaders[ row ][ column ] = HorizontalLineLayoutItem(); + dummyFooters[ row ][ column ] = HorizontalLineLayoutItem(); + innerHdFtLayouts[0][row][column] = 0; + innerHdFtLayouts[1][row][column] = 0; + } + } +} + +Chart::Private::~Private() +{ + removeDummyHeaderFooters(); +} + +void Chart::Private::removeDummyHeaderFooters() +{ + for ( int row = 0; row < 3; ++row ) + { + for ( int column = 0; column < 3; ++ column ) + { + if( innerHdFtLayouts[0][row][column] ){ + innerHdFtLayouts[0][row][column]->removeItem( &(dummyHeaders[row][column]) ); + innerHdFtLayouts[1][row][column]->removeItem( &(dummyFooters[row][column]) ); + } + } + } +} + +void Chart::Private::layoutHeadersAndFooters() +{ + removeDummyHeaderFooters(); + + bool headersLineFilled[] = { false, false, false }; + bool footersLineFilled[] = { false, false, false }; + + Q_FOREACH( HeaderFooter *hf, headerFooters ) { + // for now, there are only two types of Header/Footer, + // we use a pointer to the right layout, depending on the type(): + int innerLayoutIdx = 0; + switch( hf->type() ){ + case HeaderFooter::Header: + innerLayoutIdx = 0; + break; + case HeaderFooter::Footer: + innerLayoutIdx = 1; + break; + default: + Q_ASSERT( false ); // all types need to be handled + break; + }; + + if( hf->position() != Position::Unknown ) { + int row, column; + Qt::Alignment hAlign, vAlign; + if( hf->position().isNorthSide() ){ + row = 0; + vAlign = Qt::AlignTop; + } + else if( hf->position().isSouthSide() ){ + row = 2; + vAlign = Qt::AlignBottom; + } + else{ + row = 1; + vAlign = Qt::AlignVCenter; + } + if( hf->position().isWestSide() ){ + column = 0; + hAlign = Qt::AlignLeft; + } + else if( hf->position().isEastSide() ){ + column = 2; + hAlign = Qt::AlignRight; + } + else{ + column = 1; + hAlign = Qt::AlignHCenter; + } + switch( hf->type() ){ + case HeaderFooter::Header: + if( !headersLineFilled[ row ] ) + { + for( int col = 0; col < 3; ++col ) + innerHdFtLayouts[0][row][col]->addItem( &(dummyHeaders[ row ][ col ]) ); + headersLineFilled[ row ] = true; + } + break; + case HeaderFooter::Footer: + if( !footersLineFilled[ row ] ) + { + for( int col = 0; col < 3; ++col ) + innerHdFtLayouts[1][row][col]->addItem( &(dummyFooters[ row ][ col ]) ); + footersLineFilled[ row ] = true; + } + break; + }; + textLayoutItems << hf; + QVBoxLayout* headerFooterLayout = innerHdFtLayouts[innerLayoutIdx][row][column]; + hf->setParentLayout( headerFooterLayout ); + hf->setAlignment( hAlign | vAlign ); + headerFooterLayout->addItem( hf ); + } + else{ + qDebug( "Unknown header/footer position" ); + } + } +} + +void Chart::Private::layoutLegends() +{ + //qDebug() << "starting Chart::Private::layoutLegends()"; + // To support more than one Legend, we first collect them all + // in little lists: one list per grid position. + // Since the dataAndLegendLayout is a 3x3 grid, we need 9 little lists. + QList infos[3][3]; + + Q_FOREACH( Legend *legend, legends ) { + + legend->needSizeHint(); // we'll lay it out soon + + bool bOK = true; + int row, column; + //qDebug() << legend->position().name(); + switch( legend->position().value() ) { + case KDChartEnums::PositionNorthWest: row = 0; column = 0; + break; + case KDChartEnums::PositionNorth: row = 0; column = 1; + break; + case KDChartEnums::PositionNorthEast: row = 0; column = 2; + break; + case KDChartEnums::PositionEast: row = 1; column = 2; + break; + case KDChartEnums::PositionSouthEast: row = 2; column = 2; + break; + case KDChartEnums::PositionSouth: row = 2; column = 1; + break; + case KDChartEnums::PositionSouthWest: row = 2; column = 0; + break; + case KDChartEnums::PositionWest: row = 1; column = 0; + break; + case KDChartEnums::PositionCenter: + qDebug( "Sorry: Legend not shown, because position Center is not supported." ); + bOK = false; + break; + case KDChartEnums::PositionFloating: + bOK = false; + break; + default: + qDebug( "Sorry: Legend not shown, because of unknown legend position." ); + bOK = false; + break; + } + if( bOK ) + infos[row][column] << legend; + } + // We have collected all legend information, + // so we can design their layout now. + for (int iR = 0; iR < 3; ++iR) { + for (int iC = 0; iC < 3; ++iC) { + QList& list = infos[iR][iC]; + const int count = list.size(); + switch( count ){ + case 0: + break; + case 1: { + Legend* legend = list.first(); + dataAndLegendLayout->addItem( new MyWidgetItem(legend), + iR, iC, 1, 1, legend->alignment() ); + } + break; + default: { + // We have more than one legend in the same cell + // of the big dataAndLegendLayout grid. + // + // So we need to find out, if they are aligned the + // same way: + // Those legends, that are aligned the same way, will be drawn + // leftbound, on top of each other, in a little VBoxLayout. + // + // If not al of the legends are aligned the same way, + // there will be a grid with 3 cells: for left/mid/right side + // (or top/mid/bottom side, resp.) legends + Legend* legend = list.first(); + Qt::Alignment alignment = legend->alignment(); + bool haveSameAlign = true; + for (int i = 1; i < count; ++i) { + legend = list.at(i); + if( alignment != legend->alignment() ){ + haveSameAlign = false; + break; + } + } + if( haveSameAlign ){ + QVBoxLayout* vLayout = new QVBoxLayout(); +#if defined SET_ALL_MARGINS_TO_ZERO + vLayout->setMargin(0); +#endif + for (int i = 0; i < count; ++i) { + vLayout->addItem( new MyWidgetItem(list.at(i), Qt::AlignLeft) ); + } + dataAndLegendLayout->addLayout( vLayout, iR, iC, 1, 1, alignment ); + }else{ + QGridLayout* gridLayout = new QGridLayout(); +#if defined SET_ALL_MARGINS_TO_ZERO + gridLayout->setMargin(0); +#endif + + +#define ADD_VBOX_WITH_LEGENDS(row, column, align) \ +{ \ + QVBoxLayout* innerLayout = new QVBoxLayout(); \ + for (int i = 0; i < count; ++i) { \ + legend = list.at(i); \ + if( legend->alignment() == ( align ) ) \ + innerLayout->addItem( new MyWidgetItem(legend, Qt::AlignLeft) ); \ + } \ + gridLayout->addLayout( innerLayout, row, column, ( align ) ); \ +} + ADD_VBOX_WITH_LEGENDS( 0, 0, Qt::AlignTop | Qt::AlignLeft ) + ADD_VBOX_WITH_LEGENDS( 0, 1, Qt::AlignTop | Qt::AlignHCenter ) + ADD_VBOX_WITH_LEGENDS( 0, 2, Qt::AlignTop | Qt::AlignRight ) + + ADD_VBOX_WITH_LEGENDS( 1, 0, Qt::AlignVCenter | Qt::AlignLeft ) + ADD_VBOX_WITH_LEGENDS( 1, 1, Qt::AlignCenter ) + ADD_VBOX_WITH_LEGENDS( 1, 2, Qt::AlignVCenter | Qt::AlignRight ) + + ADD_VBOX_WITH_LEGENDS( 2, 0, Qt::AlignBottom | Qt::AlignLeft ) + ADD_VBOX_WITH_LEGENDS( 2, 1, Qt::AlignBottom | Qt::AlignHCenter ) + ADD_VBOX_WITH_LEGENDS( 2, 2, Qt::AlignBottom | Qt::AlignRight ) + + dataAndLegendLayout->addLayout( gridLayout, iR, iC, 1, 1 ); + } + } + } + } + } + //qDebug() << "finished Chart::Private::layoutLegends()"; +} + + +QHash Chart::Private::buildPlaneLayoutInfos() +{ + /* There are two ways in which planes can be caused to interact in + * where they are put layouting wise: The first is the reference plane. If + * such a reference plane is set, on a plane, it will use the same cell in the + * layout as that one. In addition to this, planes can share an axis. In that case + * they will be laid out in relation to each other as suggested by the position + * of the axis. If, for example Plane1 and Plane2 share an axis at position Left, + * that will result in the layout: Axis Plane1 Plane 2, vertically. If Plane1 + * also happens to be Plane2's referece plane, both planes are drawn over each + * other. The reference plane concept allows two planes to share the same space + * even if neither has any axis, and in case there are shared axis, it is used + * to decided, whether the planes should be painted on top of each other or + * laid out vertically or horizontally next to each other. */ + QHash axisInfos; + QHash planeInfos; + Q_FOREACH(AbstractCoordinatePlane* plane, coordinatePlanes ) + { + PlaneInfo p; + // first check if we share space with another plane + p.referencePlane = plane->referenceCoordinatePlane(); + planeInfos.insert( plane, p ); + + Q_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) { + AbstractCartesianDiagram* diagram = + dynamic_cast ( abstractDiagram ); + if( !diagram ) continue; + + Q_FOREACH( CartesianAxis* axis, diagram->axes() ) { + if ( !axisInfos.contains( axis ) ) { + /* If this is the first time we see this axis, add it, with the + * current plane. The first plane added to the chart that has + * the axis associated with it thus "owns" it, and decides about + * layout. */ + AxisInfo i; + i.plane = plane; + axisInfos.insert( axis, i ); + } else { + AxisInfo i = axisInfos[axis]; + if ( i.plane == plane ) continue; // we don't want duplicates, only shared + + /* The user expects diagrams to be added on top, and to the right + * so that horizontally we need to move the new diagram, vertically + * the reference one. */ + PlaneInfo pi = planeInfos[plane]; + // plane-to-plane linking overrides linking via axes + if ( !pi.referencePlane ) { + // we're not the first plane to see this axis, mark us as a slave + pi.referencePlane = i.plane; + if ( axis->position() == CartesianAxis::Left + || axis->position() == CartesianAxis::Right ) + pi.horizontalOffset += 1; + planeInfos[plane] = pi; + + pi = planeInfos[i.plane]; + if ( axis->position() == CartesianAxis::Top + || axis->position() == CartesianAxis::Bottom ) + pi.verticalOffset += 1; + + planeInfos[i.plane] = pi; + } + } + } + } + // Create a new grid layout for each plane that has no reference. + p = planeInfos[plane]; + if ( p.referencePlane == 0 ) { + p.gridLayout = new QGridLayout(); + // TESTING(khz): set the margin of all of the layouts to Zero +#if defined SET_ALL_MARGINS_TO_ZERO + p.gridLayout->setMargin(0); +#endif + planeInfos[plane] = p; + } + } + return planeInfos; +} + + template +static T* findOrCreateLayoutByObjectName( QLayout * parentLayout, const char* name ) +{ + T *box = qFindChild( parentLayout, QString::fromLatin1( name ) ); + if ( !box ) { + box = new T(); + // TESTING(khz): set the margin of all of the layouts to Zero +#if defined SET_ALL_MARGINS_TO_ZERO + box->setMargin(0); +#endif + box->setObjectName( QString::fromLatin1( name ) ); + box->setSizeConstraint( QLayout::SetFixedSize ); + } + return box; +} + +#if 0 +static QVBoxLayout* findOrCreateVBoxLayoutByObjectName( QLayout* parentLayout, const char* name ) +{ + return findOrCreateLayoutByObjectName( parentLayout, name ); +} + +static QHBoxLayout* findOrCreateHBoxLayoutByObjectName( QLayout* parentLayout, const char* name ) +{ + return findOrCreateLayoutByObjectName( parentLayout, name ); +} +#endif + +void Chart::Private::slotLayoutPlanes() +{ + //qDebug() << "KDChart::Chart is layouting the planes"; + const QBoxLayout::Direction oldPlanesDirection = + planesLayout ? planesLayout->direction() : QBoxLayout::TopToBottom; + if ( planesLayout && dataAndLegendLayout ) + dataAndLegendLayout->removeItem( planesLayout ); + + const bool hadPlanesLayout = planesLayout != 0; + int left, top, right, bottom; + if(hadPlanesLayout) + planesLayout->getContentsMargins(&left, &top, &right, &bottom); + + KDAB_FOREACH( KDChart::AbstractLayoutItem* plane, planeLayoutItems ) { + plane->removeFromParentLayout(); + } + planeLayoutItems.clear(); + delete planesLayout; + //hint: The direction is configurable by the user now, as + // we are using a QBoxLayout rather than a QVBoxLayout. (khz, 2007/04/25) + planesLayout = new QBoxLayout( oldPlanesDirection ); + + if(hadPlanesLayout) + planesLayout->setContentsMargins(left, top, right, bottom); + + // TESTING(khz): set the margin of all of the layouts to Zero +#if defined SET_ALL_MARGINS_TO_ZERO + planesLayout->setMargin(0); + planesLayout->setSpacing(0); +#endif + planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) ); + + /* First go through all planes and all axes and figure out whether the planes + * need to coordinate. If they do, they share a grid layout, if not, each + * get their own. See buildPlaneLayoutInfos() for more details. */ + QHash planeInfos = buildPlaneLayoutInfos(); + QHash axisInfos; + KDAB_FOREACH( AbstractCoordinatePlane* plane, coordinatePlanes ) { + Q_ASSERT( planeInfos.contains(plane) ); + PlaneInfo& pi = planeInfos[ plane ]; + int column = pi.horizontalOffset; + int row = pi.verticalOffset; + //qDebug() << "processing plane at column" << column << "and row" << row; + QGridLayout *planeLayout = pi.gridLayout; + + if(!planeLayout){ + PlaneInfo& refPi = pi; + // if this plane is sharing an axis with another one, recursively check for the original plane and use + // the grid of that as planeLayout. + while ( !planeLayout && refPi.referencePlane) { + refPi = planeInfos[refPi.referencePlane]; + planeLayout = refPi.gridLayout; + } + Q_ASSERT_X(planeLayout, + "Chart::Private::slotLayoutPlanes()", + "Invalid reference plane. Please Check whether the reference plane is added to the Chart or not" ); + } else { + planesLayout->addLayout( planeLayout ); + } + + /* Put the plane in the center of the layout. If this is our own, that's + * the middle of the layout, if we are sharing, it's a cell in the center + * column of the shared grid. */ + planeLayoutItems << plane; + plane->setParentLayout( planeLayout ); + planeLayout->addItem( plane, row, column, 1, 1, 0 ); + //qDebug() << "Chart slotLayoutPlanes() calls planeLayout->addItem("<< row << column << ")"; + planeLayout->setRowStretch( row, 2 ); + planeLayout->setColumnStretch( column, 2 ); + + KDAB_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) + { + AbstractCartesianDiagram* diagram = + dynamic_cast ( abstractDiagram ); + //qDebug() << "--------------- diagram ???????????????????? -----------------"; + if( !diagram ) continue; // FIXME polar ? + //qDebug() << "--------------- diagram ! ! ! ! ! ! ! ! ! ! -----------------"; + + if( pi.referencePlane != 0 ) + { + pi.topAxesLayout = planeInfos[ pi.referencePlane ].topAxesLayout; + pi.bottomAxesLayout = planeInfos[ pi.referencePlane ].bottomAxesLayout; + pi.leftAxesLayout = planeInfos[ pi.referencePlane ].leftAxesLayout; + pi.rightAxesLayout = planeInfos[ pi.referencePlane ].rightAxesLayout; + } + + // collect all axes of a kind into sublayouts + if( pi.topAxesLayout == 0 ) + { + pi.topAxesLayout = new QVBoxLayout; +#if defined SET_ALL_MARGINS_TO_ZERO + pi.topAxesLayout->setMargin(0); +#endif + pi.topAxesLayout->setObjectName( QString::fromLatin1( "topAxesLayout" ) ); + } + if( pi.bottomAxesLayout == 0 ) + { + pi.bottomAxesLayout = new QVBoxLayout; +#if defined SET_ALL_MARGINS_TO_ZERO + pi.bottomAxesLayout->setMargin(0); +#endif + pi.bottomAxesLayout->setObjectName( QString::fromLatin1( "bottomAxesLayout" ) ); + } + if( pi.leftAxesLayout == 0 ) + { + pi.leftAxesLayout = new QHBoxLayout; +#if defined SET_ALL_MARGINS_TO_ZERO + pi.leftAxesLayout->setMargin(0); +#endif + pi.leftAxesLayout->setObjectName( QString::fromLatin1( "leftAxesLayout" ) ); + } + if( pi.rightAxesLayout == 0 ) + { + pi.rightAxesLayout = new QHBoxLayout; +#if defined SET_ALL_MARGINS_TO_ZERO + pi.rightAxesLayout->setMargin(0); +#endif + pi.rightAxesLayout->setObjectName( QString::fromLatin1( "rightAxesLayout" ) ); + } + + if( pi.referencePlane != 0 ) + { + planeInfos[ pi.referencePlane ].topAxesLayout = pi.topAxesLayout; + planeInfos[ pi.referencePlane ].bottomAxesLayout = pi.bottomAxesLayout; + planeInfos[ pi.referencePlane ].leftAxesLayout = pi.leftAxesLayout; + planeInfos[ pi.referencePlane ].rightAxesLayout = pi.rightAxesLayout; + } + + //pi.leftAxesLayout->setSizeConstraint( QLayout::SetFixedSize ); + + KDAB_FOREACH( CartesianAxis* axis, diagram->axes() ) { + if ( axisInfos.contains( axis ) ) continue; // already laid this one out + Q_ASSERT ( axis ); + axis->setCachedSizeDirty(); + //qDebug() << "--------------- axis added to planeLayoutItems -----------------"; + planeLayoutItems << axis; + /* + // Unused code trying to use a push-model: This did not work + // since we can not re-layout the planes each time when + // Qt layouting is calling sizeHint() + connect( axis, SIGNAL( needAdjustLeftRightColumnsForOverlappingLabels( + CartesianAxis*, int, int ) ), + this, SLOT( slotAdjustLeftRightColumnsForOverlappingLabels( + CartesianAxis*, int, int ) ) ); + connect( axis, SIGNAL( needAdjustTopBottomRowsForOverlappingLabels( + CartesianAxis*, int, int ) ), + this, SLOT( slotAdjustTopBottomRowsForOverlappingLabels( + CartesianAxis*, int, int ) ) ); + */ + switch ( axis->position() ) + { + case CartesianAxis::Top: + axis->setParentLayout( pi.topAxesLayout ); + pi.topAxesLayout->addItem( axis ); + break; + case CartesianAxis::Bottom: + axis->setParentLayout( pi.bottomAxesLayout ); + pi.bottomAxesLayout->addItem( axis ); + break; + case CartesianAxis::Left: + axis->setParentLayout( pi.leftAxesLayout ); + pi.leftAxesLayout->addItem( axis ); + break; + case CartesianAxis::Right: + axis->setParentLayout( pi.rightAxesLayout ); + pi.rightAxesLayout->addItem( axis ); + break; + default: + Q_ASSERT_X( false, "Chart::paintEvent", + "unknown axis position" ); + break; + }; + axisInfos.insert( axis, AxisInfo() ); + } + /* Put each stack of axes-layouts in the cells surrounding the + * associated plane. We are laying out in the oder the planes + * were added, and the first one gets to lay out shared axes. + * Private axes go here as well, of course. */ + if ( !pi.topAxesLayout->parent() ) + planeLayout->addLayout( pi.topAxesLayout, row - 1, column ); + if ( !pi.bottomAxesLayout->parent() ) + planeLayout->addLayout( pi.bottomAxesLayout, row + 1, column ); + if ( !pi.leftAxesLayout->parent() ){ + planeLayout->addLayout( pi.leftAxesLayout, row, column - 1); + //planeLayout->setRowStretch( row, 0 ); + //planeLayout->setColumnStretch( 0, 0 ); + } + if ( !pi.rightAxesLayout->parent() ) + planeLayout->addLayout( pi.rightAxesLayout, row, column + 1); + } + + // use up to four auto-spacer items in the corners around the diagrams: +#define ADD_AUTO_SPACER_IF_NEEDED( \ + spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ) \ + { \ + if( hLayout || vLayout ) { \ + AutoSpacerLayoutItem * spacer \ + = new AutoSpacerLayoutItem( hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ); \ + planeLayout->addItem( spacer, spacerRow, spacerColumn, 1, 1 ); \ + spacer->setParentLayout( planeLayout ); \ + planeLayoutItems << spacer; \ + } \ + } + ADD_AUTO_SPACER_IF_NEEDED( row-1, column-1, false, pi.leftAxesLayout, false, pi.topAxesLayout ) + ADD_AUTO_SPACER_IF_NEEDED( row+1, column-1, true, pi.leftAxesLayout, false, pi.bottomAxesLayout ) + ADD_AUTO_SPACER_IF_NEEDED( row-1, column+1, false, pi.rightAxesLayout, true, pi.topAxesLayout ) + ADD_AUTO_SPACER_IF_NEEDED( row+1, column+1, true, pi.rightAxesLayout, true, pi.bottomAxesLayout ) + } + // re-add our grid(s) to the chart's layout + if ( dataAndLegendLayout ){ + dataAndLegendLayout->addLayout( planesLayout, 1, 1 ); + dataAndLegendLayout->setRowStretch( 1, 1000 ); + dataAndLegendLayout->setColumnStretch( 1, 1000 ); + } + + slotRelayout(); + //qDebug() << "KDChart::Chart finished layouting the planes."; +} + +void Chart::Private::createLayouts( QWidget* w ) +{ + KDAB_FOREACH( KDChart::TextArea* textLayoutItem, textLayoutItems ) { + textLayoutItem->removeFromParentLayout(); + } + textLayoutItems.clear(); + + KDAB_FOREACH( KDChart::AbstractArea* layoutItem, layoutItems ) { + layoutItem->removeFromParentLayout(); + } + layoutItems.clear(); + + removeDummyHeaderFooters(); + + // layout for the planes is handled separately, so we don't want to delete it here + if ( dataAndLegendLayout) { + dataAndLegendLayout->removeItem( planesLayout ); + planesLayout->setParent( 0 ); + } + // nuke the old bunch + delete layout; + + // The HBox d->layout provides the left and right global leadings + layout = new QHBoxLayout( w ); + // TESTING(khz): set the margin of all of the layouts to Zero +#if defined SET_ALL_MARGINS_TO_ZERO + layout->setMargin(0); +#endif + layout->setObjectName( QString::fromLatin1( "Chart::Private::layout" ) ); + layout->addSpacing( globalLeadingLeft ); + + // The vLayout provides top and bottom global leadings and lays + // out headers/footers and the data area. + vLayout = new QVBoxLayout(); + // TESTING(khz): set the margin of all of the layouts to Zero +#if defined SET_ALL_MARGINS_TO_ZERO + vLayout->setMargin(0); +#endif + vLayout->setObjectName( QString::fromLatin1( "vLayout" ) ); + layout->addLayout( vLayout, 1000 ); + layout->addSpacing( globalLeadingRight ); + + + + // 1. the gap above the top edge of the headers area + vLayout->addSpacing( globalLeadingTop ); + // 2. the header(s) area + headerLayout = new QGridLayout(); + // TESTING(khz): set the margin of all of the layouts to Zero +#if defined SET_ALL_MARGINS_TO_ZERO + headerLayout->setMargin(0); +#endif + vLayout->addLayout( headerLayout ); + // 3. the area containing coordinate plane(s), axes, legend(s) + dataAndLegendLayout = new QGridLayout(); + // TESTING(khz): set the margin of all of the layouts to Zero +#if defined SET_ALL_MARGINS_TO_ZERO + dataAndLegendLayout->setMargin(0); +#endif + dataAndLegendLayout->setObjectName( QString::fromLatin1( "dataAndLegendLayout" ) ); + vLayout->addLayout( dataAndLegendLayout, 1000 ); + // 4. the footer(s) area + footerLayout = new QGridLayout(); + // TESTING(khz): set the margin of all of the layouts to Zero +#if defined SET_ALL_MARGINS_TO_ZERO + footerLayout->setMargin(0); +#endif + footerLayout->setObjectName( QString::fromLatin1( "footerLayout" ) ); + vLayout->addLayout( footerLayout ); + + // 5. Prepare the header / footer layout cells: + // Each of the 9 header cells (the 9 footer cells) + // contain their own QVBoxLayout + // since there can be more than one header (footer) per cell. + static const Qt::Alignment hdFtAlignments[3][3] = { + { Qt::AlignTop | Qt::AlignLeft, Qt::AlignTop | Qt::AlignHCenter, Qt::AlignTop | Qt::AlignRight }, + { Qt::AlignVCenter | Qt::AlignLeft, Qt::AlignVCenter | Qt::AlignHCenter, Qt::AlignVCenter | Qt::AlignRight }, + { Qt::AlignBottom | Qt::AlignLeft, Qt::AlignBottom | Qt::AlignHCenter, Qt::AlignBottom | Qt::AlignRight } + }; + for ( int row = 0; row < 3; ++row ) + { + for ( int column = 0; column < 3; ++ column ) + { + QVBoxLayout* innerHdLayout = new QVBoxLayout(); + QVBoxLayout* innerFtLayout = new QVBoxLayout(); + innerHdFtLayouts[0][row][column] = innerHdLayout; + innerHdFtLayouts[1][row][column] = innerFtLayout; +#if defined SET_ALL_MARGINS_TO_ZERO + innerHdLayout->setMargin(0); + innerFtLayout->setMargin(0); +#endif + const Qt::Alignment align = hdFtAlignments[row][column]; + innerHdLayout->setAlignment( align ); + innerFtLayout->setAlignment( align ); + headerLayout->addLayout( innerHdLayout, row, column, align ); + footerLayout->addLayout( innerFtLayout, row, column, align ); + } + } + + // 6. the gap below the bottom edge of the headers area + vLayout->addSpacing( globalLeadingBottom ); + + // the data+axes area + dataAndLegendLayout->addLayout( planesLayout, 1, 1 ); + dataAndLegendLayout->setRowStretch( 1, 1 ); + dataAndLegendLayout->setColumnStretch( 1, 1 ); + + //qDebug() << "w->rect()" << w->rect(); +} + +void Chart::Private::slotRelayout() +{ + //qDebug() << "Chart relayouting started."; + createLayouts( chart ); + + layoutHeadersAndFooters(); + layoutLegends(); + + // This triggers the qlayout, see QBoxLayout::setGeometry + // The geometry is not necessarily w->rect(), when using paint(), this is why + // we don't call layout->activate(). + const QRect geo( QRect( 0, 0, currentLayoutSize.width(), currentLayoutSize.height() ) ); + if( geo.isValid() && geo != layout->geometry() ){ + //qDebug() << "Chart slotRelayout() adjusting geometry to" << geo; + //if( coordinatePlanes.count() ) + // qDebug() << " plane geo before" << coordinatePlanes.first()->geometry(); + layout->setGeometry( geo ); + //if( coordinatePlanes.count() ) { + // qDebug() << " plane geo after " << coordinatePlanes.first()->geometry(); + //} + } + + // Adapt diagram drawing to the new size + KDAB_FOREACH (AbstractCoordinatePlane* plane, coordinatePlanes ) { + plane->layoutDiagrams(); + } + //qDebug() << "Chart relayouting done."; +} + +// Called when the size of the chart changes. +// So in theory, we only need to adjust geometries. +// But this also needs to make sure that everything is in place for the first painting. +void Chart::Private::resizeLayout( const QSize& size ) +{ + currentLayoutSize = size; + //qDebug() << "Chart::resizeLayout(" << currentLayoutSize << ")"; + + /* + // We need to make sure that the legend's layouts are populated, + // so that setGeometry gets proper sizeHints from them and resizes them properly. + KDAB_FOREACH( Legend *legend, legends ) { + // This forceRebuild will see a wrong areaGeometry, but I don't care about geometries yet, + // only about the fact that legends should have their contents populated. + // -> it would be better to dissociate "building contents" and "resizing" in Legend... + + // legend->forceRebuild(); + + legend->resizeLayout( size ); + } + */ + slotLayoutPlanes(); // includes slotRelayout + + //qDebug() << "Chart::resizeLayout done"; +} + + +void Chart::Private::paintAll( QPainter* painter ) +{ + QRect rect( QPoint(0, 0), currentLayoutSize ); + + //qDebug() << this<<"::paintAll() uses layout size" << currentLayoutSize; + + // Paint the background (if any) + KDChart::AbstractAreaBase::paintBackgroundAttributes( + *painter, rect, backgroundAttributes ); + // Paint the frame (if any) + KDChart::AbstractAreaBase::paintFrameAttributes( + *painter, rect, frameAttributes ); + + chart->reLayoutFloatingLegends(); + + KDAB_FOREACH( KDChart::AbstractArea* layoutItem, layoutItems ) { + layoutItem->paintAll( *painter ); + } + KDAB_FOREACH( KDChart::AbstractLayoutItem* planeLayoutItem, planeLayoutItems ) { + planeLayoutItem->paintAll( *painter ); + } + KDAB_FOREACH( KDChart::TextArea* textLayoutItem, textLayoutItems ) { + textLayoutItem->paintAll( *painter ); + } +} + +// ******** Chart interface implementation *********** + +Chart::Chart ( QWidget* parent ) + : QWidget ( parent ) + , _d( new Private( this ) ) +{ +#if defined KDAB_EVAL + EvalDialog::checkEvalLicense( "KD Chart" ); +#endif + + FrameAttributes frameAttrs; +// no frame per default... +// frameAttrs.setVisible( true ); + frameAttrs.setPen( QPen( Qt::black ) ); + frameAttrs.setPadding( 1 ); + setFrameAttributes( frameAttrs ); + + addCoordinatePlane( new CartesianCoordinatePlane ( this ) ); +} + +Chart::~Chart() +{ + delete _d; +} + +#define d d_func() + +void Chart::setFrameAttributes( const FrameAttributes &a ) +{ + d->frameAttributes = a; +} + +FrameAttributes Chart::frameAttributes() const +{ + return d->frameAttributes; +} + +void Chart::setBackgroundAttributes( const BackgroundAttributes &a ) +{ + d->backgroundAttributes = a; +} + +BackgroundAttributes Chart::backgroundAttributes() const +{ + return d->backgroundAttributes; +} + +//TODO KDChart 3.0; change QLayout into QBoxLayout::Direction +void Chart::setCoordinatePlaneLayout( QLayout * layout ) +{ + delete d->planesLayout; + d->planesLayout = dynamic_cast( layout ); + d->slotLayoutPlanes(); +} + +QLayout* Chart::coordinatePlaneLayout() +{ + return d->planesLayout; +} + +AbstractCoordinatePlane* Chart::coordinatePlane() +{ + if ( d->coordinatePlanes.isEmpty() ) + { + qWarning() << "Chart::coordinatePlane: warning: no coordinate plane defined."; + return 0; + } else { + return d->coordinatePlanes.first(); + } +} + +CoordinatePlaneList Chart::coordinatePlanes() +{ + return d->coordinatePlanes; +} + +void Chart::addCoordinatePlane( AbstractCoordinatePlane* plane ) +{ + connect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ), + d, SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) ); + connect( plane, SIGNAL( needUpdate() ), this, SLOT( update() ) ); + connect( plane, SIGNAL( needRelayout() ), d, SLOT( slotRelayout() ) ) ; + connect( plane, SIGNAL( needLayoutPlanes() ), d, SLOT( slotLayoutPlanes() ) ) ; + connect( plane, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) ); + d->coordinatePlanes.append( plane ); + plane->setParent( this ); + d->slotLayoutPlanes(); +} + +void Chart::replaceCoordinatePlane( AbstractCoordinatePlane* plane, + AbstractCoordinatePlane* oldPlane_ ) +{ + if( plane && oldPlane_ != plane ){ + AbstractCoordinatePlane* oldPlane = oldPlane_; + if( d->coordinatePlanes.count() ){ + if( ! oldPlane ){ + oldPlane = d->coordinatePlanes.first(); + if( oldPlane == plane ) + return; + } + takeCoordinatePlane( oldPlane ); + } + delete oldPlane; + addCoordinatePlane( plane ); + } +} + +void Chart::takeCoordinatePlane( AbstractCoordinatePlane* plane ) +{ + const int idx = d->coordinatePlanes.indexOf( plane ); + if( idx != -1 ){ + d->coordinatePlanes.takeAt( idx ); + disconnect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ), + d, SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) ); + plane->removeFromParentLayout(); + plane->setParent( 0 ); + d->mouseClickedPlanes.removeAll(plane); + } + d->slotLayoutPlanes(); + // Need to emit the signal: In case somebody has connected the signal + // to her own slot for e.g. calling update() on a widget containing the chart. + emit propertiesChanged(); +} + +void Chart::setGlobalLeading( int left, int top, int right, int bottom ) +{ + setGlobalLeadingLeft( left ); + setGlobalLeadingTop( top ); + setGlobalLeadingRight( right ); + setGlobalLeadingBottom( bottom ); + d->slotRelayout(); +} + +void Chart::setGlobalLeadingLeft( int leading ) +{ + d->globalLeadingLeft = leading; + d->slotRelayout(); +} + +int Chart::globalLeadingLeft() const +{ + return d->globalLeadingLeft; +} + +void Chart::setGlobalLeadingTop( int leading ) +{ + d->globalLeadingTop = leading; + d->slotRelayout(); +} + +int Chart::globalLeadingTop() const +{ + return d->globalLeadingTop; +} + +void Chart::setGlobalLeadingRight( int leading ) +{ + d->globalLeadingRight = leading; + d->slotRelayout(); +} + +int Chart::globalLeadingRight() const +{ + return d->globalLeadingRight; +} + +void Chart::setGlobalLeadingBottom( int leading ) +{ + d->globalLeadingBottom = leading; + d->slotRelayout(); +} + +int Chart::globalLeadingBottom() const +{ + return d->globalLeadingBottom; +} + +void Chart::paint( QPainter* painter, const QRect& target ) +{ + if( target.isEmpty() || !painter ) return; + //qDebug() << "Chart::paint( ..," << target << ")"; + + QPaintDevice* prevDevice = GlobalMeasureScaling::paintDevice(); + GlobalMeasureScaling::setPaintDevice( painter->device() ); + + // Output on a widget + if( dynamic_cast< QWidget* >( painter->device() ) != 0 ) + { + GlobalMeasureScaling::setFactors( + static_cast< qreal >( target.width() ) / + static_cast< qreal >( geometry().size().width() ), + static_cast< qreal >( target.height() ) / + static_cast< qreal >( geometry().size().height() ) ); + } + // Output onto a QPixmap + else + { + PrintingParameters::setScaleFactor( static_cast< qreal >( painter->device()->logicalDpiX() ) / static_cast< qreal >( logicalDpiX() ) ); + + const qreal resX = static_cast< qreal >( logicalDpiX() ) / static_cast< qreal >( painter->device()->logicalDpiX() ); + const qreal resY = static_cast< qreal >( logicalDpiY() ) / static_cast< qreal >( painter->device()->logicalDpiY() ); + + GlobalMeasureScaling::setFactors( + static_cast< qreal >( target.width() ) / + static_cast< qreal >( geometry().size().width() ) * resX, + static_cast< qreal >( target.height() ) / + static_cast< qreal >( geometry().size().height() ) * resY ); + } + + + if( target.size() != d->currentLayoutSize ){ + d->resizeLayout( target.size() ); + } + const QPoint translation = target.topLeft(); + painter->translate( translation ); + + d->paintAll( painter ); + + // for debugging: + //painter->setPen(QPen(Qt::blue, 8)); + //painter->drawRect(target.adjusted(12,12,-12,-12)); + + KDAB_FOREACH( Legend *legend, d->legends ) { + const bool hidden = legend->isHidden() && legend->testAttribute(Qt::WA_WState_ExplicitShowHide); + if ( !hidden ) { + //qDebug() << "painting legend at " << legend->geometry(); + legend->paintIntoRect( *painter, legend->geometry() ); + //testing: + //legend->paintIntoRect( *painter, legend->geometry().adjusted(-100,0,-100,0) ); + } + } + + painter->translate( -translation.x(), -translation.y() ); + + GlobalMeasureScaling::instance()->resetFactors(); + PrintingParameters::resetScaleFactor(); + GlobalMeasureScaling::setPaintDevice( prevDevice ); + + //qDebug() << "KDChart::Chart::paint() done.\n"; +} + +void Chart::resizeEvent ( QResizeEvent * ) +{ + d->resizeLayout( size() ); + KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ){ + plane->setGridNeedsRecalculate(); + } + reLayoutFloatingLegends(); +} + + +void Chart::reLayoutFloatingLegends() +{ + KDAB_FOREACH( Legend *legend, d->legends ) { + const bool hidden = legend->isHidden() && legend->testAttribute(Qt::WA_WState_ExplicitShowHide); + if ( legend->position().isFloating() && !hidden ){ + // resize the legend + const QSize legendSize( legend->sizeHint() ); + legend->setGeometry( QRect( legend->geometry().topLeft(), legendSize ) ); + // find the legends corner point (reference point plus any paddings) + const RelativePosition relPos( legend->floatingPosition() ); + QPointF pt( relPos.calculatedPoint( size() ) ); + //qDebug() << pt; + // calculate the legend's top left point + const Qt::Alignment alignTopLeft = Qt::AlignBottom | Qt::AlignLeft; + if( (relPos.alignment() & alignTopLeft) != alignTopLeft ){ + if( relPos.alignment() & Qt::AlignRight ) + pt.rx() -= legendSize.width(); + else if( relPos.alignment() & Qt::AlignHCenter ) + pt.rx() -= 0.5 * legendSize.width(); + + if( relPos.alignment() & Qt::AlignBottom ) + pt.ry() -= legendSize.height(); + else if( relPos.alignment() & Qt::AlignVCenter ) + pt.ry() -= 0.5 * legendSize.height(); + } + //qDebug() << pt << endl; + legend->move( static_cast(pt.x()), static_cast(pt.y()) ); + } + } +} + + +void Chart::paintEvent( QPaintEvent* ) +{ + QPainter painter( this ); + + if( size() != d->currentLayoutSize ){ + d->resizeLayout( size() ); + reLayoutFloatingLegends(); + } + + //FIXME(khz): Paint the background/frame too! + // (can we derive Chart from AreaWidget ??) + d->paintAll( &painter ); +} + +void Chart::addHeaderFooter( HeaderFooter* headerFooter ) +{ + d->headerFooters.append( headerFooter ); + headerFooter->setParent( this ); + connect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ), + d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) ); + connect( headerFooter, SIGNAL( positionChanged( HeaderFooter* ) ), + d, SLOT( slotRelayout() ) ); + d->slotRelayout(); +} + +void Chart::replaceHeaderFooter( HeaderFooter* headerFooter, + HeaderFooter* oldHeaderFooter_ ) +{ + if( headerFooter && oldHeaderFooter_ != headerFooter ){ + HeaderFooter* oldHeaderFooter = oldHeaderFooter_; + if( d->headerFooters.count() ){ + if( ! oldHeaderFooter ){ + oldHeaderFooter = d->headerFooters.first(); + if( oldHeaderFooter == headerFooter ) + return; + } + takeHeaderFooter( oldHeaderFooter ); + } + delete oldHeaderFooter; + addHeaderFooter( headerFooter ); + } +} + +void Chart::takeHeaderFooter( HeaderFooter* headerFooter ) +{ + const int idx = d->headerFooters.indexOf( headerFooter ); + if( idx != -1 ){ + d->headerFooters.takeAt( idx ); + disconnect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ), + d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) ); + headerFooter->setParent( 0 ); + } + d->slotRelayout(); + // Need to emit the signal: In case somebody has connected the signal + // to her own slot for e.g. calling update() on a widget containing the chart. + emit propertiesChanged(); +} + +HeaderFooter* Chart::headerFooter() +{ + if( d->headerFooters.isEmpty() ) { + return 0; + } else { + return d->headerFooters.first(); + } +} + +HeaderFooterList Chart::headerFooters() +{ + return d->headerFooters; +} + +void Chart::addLegend( Legend* legend ) +{ + if( ! legend ) return; + + //qDebug() << "adding the legend"; + d->legends.append( legend ); + legend->setParent( this ); + + TextAttributes textAttrs( legend->textAttributes() ); + + KDChart::Measure measure( textAttrs.fontSize() ); + measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum ); + measure.setValue( 20 ); + textAttrs.setFontSize( measure ); + legend->setTextAttributes( textAttrs ); + + textAttrs = legend->titleTextAttributes(); + measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum ); + measure.setValue( 24 ); + textAttrs.setFontSize( measure ); + + legend->setTitleTextAttributes( textAttrs ); + + legend->setReferenceArea( this ); + +/* + future: Use relative sizes for the markers too! + + const uint nMA = Legend::datasetCount(); + for( uint iMA = 0; iMA < nMA; ++iMA ){ + MarkerAttributes ma( legend->markerAttributes( iMA ) ); + ma.setMarkerSize( ... ) + legend->setMarkerAttributes( iMA, ma ) + } +*/ + + connect( legend, SIGNAL( destroyedLegend( Legend* ) ), + d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) ); + connect( legend, SIGNAL( positionChanged( AbstractAreaWidget* ) ), + d, SLOT( slotLayoutPlanes() ) ); //slotRelayout() ) ); + connect( legend, SIGNAL( propertiesChanged() ), + this, SIGNAL( propertiesChanged() ) ); + legend->setVisible( true ); + d->slotRelayout(); +} + + +void Chart::replaceLegend( Legend* legend, Legend* oldLegend_ ) +{ + if( legend && oldLegend_ != legend ){ + Legend* oldLegend = oldLegend_; + if( d->legends.count() ){ + if( ! oldLegend ){ + oldLegend = d->legends.first(); + if( oldLegend == legend ) + return; + } + takeLegend( oldLegend ); + } + delete oldLegend; + addLegend( legend ); + } +} + +void Chart::takeLegend( Legend* legend ) +{ + const int idx = d->legends.indexOf( legend ); + if( idx != -1 ){ + d->legends.takeAt( idx ); + disconnect( legend, SIGNAL( destroyedLegend( Legend* ) ), + d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) ); + disconnect( legend, SIGNAL( positionChanged( AbstractAreaWidget* ) ), + d, SLOT( slotLayoutPlanes() ) ); //slotRelayout() ) ); + disconnect( legend, SIGNAL( propertiesChanged() ), + this, SIGNAL( propertiesChanged() ) ); + legend->setParent( 0 ); + legend->setVisible( false ); + } + d->slotRelayout(); + + // Need to emit the signal: In case somebody has connected the signal + // to her own slot for e.g. calling update() on a widget containing the chart. + // Note: + // We do this ourselves in examples/DrawIntoPainter/mainwindow.cpp + emit propertiesChanged(); +} + +Legend* Chart::legend() +{ + if ( d->legends.isEmpty() ) + { + return 0; + } else { + return d->legends.first(); + } +} + +LegendList Chart::legends() +{ + return d->legends; +} + + +void Chart::mousePressEvent( QMouseEvent* event ) +{ + const QPoint pos = mapFromGlobal( event->globalPos() ); + + KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) + { + if ( plane->geometry().contains( event->pos() ) ) + { + if ( plane->diagrams().size() > 0 ) + { + QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(), + event->button(), event->buttons(), + event->modifiers() ); + + plane->mousePressEvent( &ev ); + d->mouseClickedPlanes.append( plane ); + } + } + } +} + +/* +// Unused code trying to use a push-model: This did not work +// since we can not re-layout the planes each time when +// Qt layouting is calling sizeHint() +void Chart::Private::slotAdjustLeftRightColumnsForOverlappingLabels( + CartesianAxis* axis, int leftOverlap, int rightOverlap) +{ + const QLayout* axisLayout = axis ? axis->parentLayout() : 0; + + if( (! leftOverlap && ! rightOverlap) || ! axis || ! axisLayout->parent() ) + return; + + bool needUpdate = false; + // access the planeLayout: + QGridLayout* grid = qobject_cast(axisLayout->parent()); + if( grid ){ + // find the index of the parent layout in the planeLayout: + int idx = -1; + for (int i = 0; i < grid->count(); ++i) + if( grid->itemAt(i) == axisLayout ) + idx = i; + // set the min widths of the neighboring column: + if( idx > -1 ){ + int row, column, rowSpan, columnSpan; + grid->getItemPosition( idx, &row, &column, &rowSpan, &columnSpan ); + const int leftColumn = column-1; + const int rightColumn = column+columnSpan; + // find the left/right axes layouts + QHBoxLayout* leftAxesLayout=0; + QHBoxLayout* rightAxesLayout=0; + for( int i = 0; + (!leftAxesLayout || !rightAxesLayout) && i < grid->count(); + ++i ) + { + int r, c, rs, cs; + grid->getItemPosition( i, &r, &c, &rs, &cs ); + if( c+cs-1 == leftColumn ) + leftAxesLayout = dynamic_cast(grid->itemAt(i)); + if( c == rightColumn ) + rightAxesLayout = dynamic_cast(grid->itemAt(i)); + } + if( leftAxesLayout ){ + const int leftColumnMinWidth = leftOverlap; + QLayoutItem* item = leftAxesLayout->count() + ? dynamic_cast(leftAxesLayout->itemAt(leftAxesLayout->count()-1)) + : 0; + QSpacerItem* spacer = dynamic_cast(item); + if( spacer ){ + if( spacer->sizeHint().width() < leftColumnMinWidth ){ + needUpdate = true; + spacer->changeSize(leftColumnMinWidth, 1); + qDebug() << "adjusted left spacer->sizeHint().width() to" << spacer->sizeHint().width(); + } + }else{ + AbstractAxis* axis = dynamic_cast(item); + if( !axis || axis->sizeHint().width() < leftColumnMinWidth ){ + needUpdate = true; + leftAxesLayout->insertSpacing( -1, leftColumnMinWidth ); + qDebug() << "adjusted column" << leftColumn << "min width to" << leftColumnMinWidth; + } + } + } + if( rightAxesLayout ){ + const int rightColumnMinWidth = rightOverlap; + QLayoutItem* item = rightAxesLayout->count() + ? dynamic_cast(rightAxesLayout->itemAt(0)) + : 0; + QSpacerItem* spacer = dynamic_cast(item); + if( spacer ){ + if( spacer->sizeHint().width() < rightColumnMinWidth ){ + needUpdate = true; + spacer->changeSize(rightColumnMinWidth, 1); + qDebug() << "adjusted right spacer->sizeHint().width() to" << spacer->sizeHint().width(); + } + }else{ + AbstractAxis* axis = dynamic_cast(item); + if( !axis || axis->sizeHint().width() < rightColumnMinWidth ){ + needUpdate = true; + rightAxesLayout->insertSpacing( 0, rightColumnMinWidth ); + qDebug() << "adjusted column" << rightColumn << "min width to" << rightColumnMinWidth; + } + } + } + } + } + if( needUpdate ){ + ;// do something ...? + } +} + + +void Chart::Private::slotAdjustTopBottomRowsForOverlappingLabels( + CartesianAxis* axis, int topOverlap, int bottomOverlap) +{ + const QLayout* axisLayout = axis ? axis->parentLayout() : 0; + + if( (! topOverlap && ! bottomOverlap) || ! axisLayout || ! axisLayout->parent() ) + return; + + // access the planeLayout: + QGridLayout* grid = qobject_cast(axisLayout->parent()); + if( grid ){ + // find the index of the parent layout in the planeLayout: + int idx = -1; + for (int i = 0; i < grid->count(); ++i) + if( grid->itemAt(i) == axisLayout ) + idx = i; + // set the min widths of the neighboring column: + if( idx > -1 ){ + int row, column, rowSpan, columnSpan; + grid->getItemPosition( idx, &row, &column, &rowSpan, &columnSpan ); + const int topRow = row-1; + const int bottomRow = row+rowSpan; + // find the left/right axes layouts + QVBoxLayout* topAxesLayout=0; + QVBoxLayout* bottomAxesLayout=0; + for( int i = 0; + (!topAxesLayout || !bottomAxesLayout) && i < grid->count(); + ++i ) + { + int r, c, rs, cs; + grid->getItemPosition( i, &r, &c, &rs, &cs ); + if( r+rs-1 == topRow ) + topAxesLayout = dynamic_cast(grid->itemAt(i)); + if( r == bottomRow ) + bottomAxesLayout = dynamic_cast(grid->itemAt(i)); + } + if( topAxesLayout ){ + const int topRowMinWidth = topOverlap; + QLayoutItem* item = topAxesLayout->count() + ? dynamic_cast(topAxesLayout->itemAt(topAxesLayout->count()-1)) + : 0; + QSpacerItem* spacer = dynamic_cast(item); + if( spacer ){ + if( spacer->sizeHint().height() < topRowMinWidth ){ + spacer->changeSize(1, topRowMinWidth); + qDebug() << "adjusted top spacer->sizeHint().height() to" << spacer->sizeHint().height(); + } + }else{ + AbstractAxis* axis = dynamic_cast(item); + if( !axis || axis->sizeHint().height() < topRowMinWidth ){ + topAxesLayout->insertSpacing( -1, topRowMinWidth ); + qDebug() << "adjusted row" << topRow << "min height to" << topRowMinWidth; + } + } + } + if( bottomAxesLayout ){ + const int bottomRowMinWidth = bottomOverlap; + QLayoutItem* item = bottomAxesLayout->count() + ? dynamic_cast(bottomAxesLayout->itemAt(0)) + : 0; + QSpacerItem* spacer = dynamic_cast(item); + if( spacer ){ + if( spacer->sizeHint().height() < bottomRowMinWidth ){ + spacer->changeSize(1, bottomRowMinWidth); + qDebug() << "adjusted bottom spacer->sizeHint().height() to" << spacer->sizeHint().height(); + } + }else{ + AbstractAxis* axis = dynamic_cast(item); + if( !axis || axis->sizeHint().height() < bottomRowMinWidth ){ + bottomAxesLayout->insertSpacing( 0, bottomRowMinWidth ); + qDebug() << "adjusted row" << bottomRow << "min height to" << bottomRowMinWidth; + } + } + } + } + } +} +*/ + +void Chart::mouseDoubleClickEvent( QMouseEvent* event ) +{ + const QPoint pos = mapFromGlobal( event->globalPos() ); + + KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) + { + if ( plane->geometry().contains( event->pos() ) ) + { + if ( plane->diagrams().size() > 0 ) + { + QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(), + event->button(), event->buttons(), + event->modifiers() ); + plane->mouseDoubleClickEvent( &ev ); + } + } + } +} + +void Chart::mouseMoveEvent( QMouseEvent* event ) +{ + QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes ); + + KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) + { + if( plane->geometry().contains( event->pos() ) ) + { + if( plane->diagrams().size() > 0 ) + { + eventReceivers.insert( plane ); + } + } + } + + const QPoint pos = mapFromGlobal( event->globalPos() ); + + KDAB_FOREACH( AbstractCoordinatePlane* plane, eventReceivers ) + { + QMouseEvent ev( QEvent::MouseMove, pos, event->globalPos(), + event->button(), event->buttons(), + event->modifiers() ); + plane->mouseMoveEvent( &ev ); + } +} + +void Chart::mouseReleaseEvent( QMouseEvent* event ) +{ + QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes ); + + KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) + { + if ( plane->geometry().contains( event->pos() ) ) + { + if( plane->diagrams().size() > 0 ) + { + eventReceivers.insert( plane ); + } + } + } + + const QPoint pos = mapFromGlobal( event->globalPos() ); + + KDAB_FOREACH( AbstractCoordinatePlane* plane, eventReceivers ) + { + QMouseEvent ev( QEvent::MouseButtonRelease, pos, event->globalPos(), + event->button(), event->buttons(), + event->modifiers() ); + plane->mouseReleaseEvent( &ev ); + } + + d->mouseClickedPlanes.clear(); +} + +bool Chart::event( QEvent* event ) +{ + switch( event->type() ) + { + case QEvent::ToolTip: + { + const QHelpEvent* const helpEvent = static_cast< QHelpEvent* >( event ); + KDAB_FOREACH( const AbstractCoordinatePlane* const plane, d->coordinatePlanes ) + { + for (int i = plane->diagrams().count() - 1; i >= 0; --i) { + const QModelIndex index = plane->diagrams().at(i)->indexAt( helpEvent->pos() ); + const QVariant toolTip = index.data( Qt::ToolTipRole ); + if( toolTip.isValid() ) + { + QPoint pos = mapFromGlobal(helpEvent->pos()); + QRect rect(pos-QPoint(1,1), QSize(3,3)); + QToolTip::showText( QCursor::pos(), toolTip.toString(), this, rect ); + return true; + } + } + } + // fall-through intended + } + default: + return QWidget::event( event ); + } +} diff --git a/libkdchart/src/KDChartChart.h b/libkdchart/src/KDChartChart.h new file mode 100644 index 0000000..d7e45b6 --- /dev/null +++ b/libkdchart/src/KDChartChart.h @@ -0,0 +1,518 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTCHART_H +#define KDCHARTCHART_H + +#include + +#include "kdchart_export.h" +#include "KDChartGlobal.h" + +/** \file KDChartChart.h + * \brief Declaring the class KDChart::Chart. + * + * + */ + + +namespace KDChart { + + class BackgroundAttributes; + class FrameAttributes; + class AbstractDiagram; + class AbstractCoordinatePlane; + class HeaderFooter; + class Legend; + + typedef QList CoordinatePlaneList; + typedef QList HeaderFooterList; + typedef QList LegendList; + + + /** + * @class Chart KDChartChart.h KDChartChart + * @brief A chart with one or more diagrams. + * + * The Chart class represents a drawing consisting of one or more diagrams + * and various optional elements such as legends, axes, text boxes, headers + * or footers. It takes ownership of all these elements when they are assigned + * to it. Each diagram is associated with a coordinate plane, of which the chart + * can have more than one. The coordinate planes (and thus the associated diagrams) + * can be laid out in various ways. + * + * The Chart class makes heavy use of the Qt Interview framework for model/view + * programming, and thus requires data to be presented to it in a QAbstractItemModel + * compatible way. For many simple charts, especially if the visualized data is + * static, KDChart::Widget provides an abstracted interface, that hides the complexity + * of Interview to a large extent. + */ + class KDCHART_EXPORT Chart : public QWidget + { + Q_OBJECT + Q_PROPERTY( int globalLeadingTop READ globalLeadingTop WRITE setGlobalLeadingTop ) + Q_PROPERTY( int globalLeadingBottom READ globalLeadingBottom WRITE setGlobalLeadingBottom ) + Q_PROPERTY( int globalLeadingLeft READ globalLeadingLeft WRITE setGlobalLeadingLeft ) + Q_PROPERTY( int globalLeadingRight READ globalLeadingRight WRITE setGlobalLeadingRight ) + + KDCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC_QWIDGET( Chart ) + + public: + explicit Chart ( QWidget* parent = 0 ); + ~Chart(); + + /** + \brief Specify the frame attributes to be used, by default is it a thin black line. + + To hide the frame line, you could do something like this: + \verbatim + KDChart::FrameAttributes frameAttrs( my_chart->frameAttributes() ); + frameAttrs.setVisible( false ); + my_chart->setFrameAttributes( frameAttrs ); + \endverbatim + + \sa setBackgroundAttributes + */ + void setFrameAttributes( const FrameAttributes &a ); + FrameAttributes frameAttributes() const; + + /** + \brief Specify the background attributes to be used, by default there is no background. + + To set a light blue background, you could do something like this: + \verbatim + KDChart::BackgroundAttributes backgroundAttrs( my_chart->backgroundAttributes() ); + backgroundAttrs.setVisible( true ); + backgroundAttrs.setBrush( QColor(0xd0,0xd0,0xff) ); + my_chart->setBackgroundAttributes( backgroundAttrs ); + \endverbatim + + \sa setFrameAttributes + */ + void setBackgroundAttributes( const BackgroundAttributes &a ); + BackgroundAttributes backgroundAttributes() const; + + /** + * Each chart must have at least one coordinate plane. + * Initially a default CartesianCoordinatePlane is created. + * Use replaceCoordinatePlane() to replace it with a different + * one, such as a PolarCoordinatePlane. + * @return The first coordinate plane of the chart. + */ + AbstractCoordinatePlane* coordinatePlane(); + + /** + * The list of coordinate planes. + * @return The list of coordinate planes. + */ + CoordinatePlaneList coordinatePlanes(); + + /** + * Adds a coordinate plane to the chart. The chart takes ownership. + * @param plane The coordinate plane to add. + * + * \sa replaceCoordinatePlane, takeCoordinatePlane + */ + void addCoordinatePlane( AbstractCoordinatePlane* plane ); + + /** + * Replaces the old coordinate plane, or appends the + * plane, it there is none yet. + * + * @param plane The coordinate plane to be used instead of the old plane. + * This parameter must not be zero, or the method will do nothing. + * + * @param oldPlane The coordinate plane to be removed by the new plane. This + * plane will be deleted automatically. If the parameter is omitted, + * the very first coordinate plane will be replaced. In case, there was no + * plane yet, the new plane will just be added. + * + * \note If you want to re-use the old coordinate plane, call takeCoordinatePlane and + * addCoordinatePlane, instead of using replaceCoordinatePlane. + * + * \sa addCoordinatePlane, takeCoordinatePlane + */ + void replaceCoordinatePlane( AbstractCoordinatePlane* plane, + AbstractCoordinatePlane* oldPlane = 0 ); + + /** + * Removes the coordinate plane from the chart, without deleting it. + * + * The chart no longer owns the plane, so it is + * the caller's responsibility to delete the plane. + * + * \sa addCoordinatePlane, takeCoordinatePlane + */ + void takeCoordinatePlane( AbstractCoordinatePlane* plane ); + + /** + * Set the coordinate plane layout that should be used as model for + * the internal used layout. The layout needs to be an instance of + * QHBoxLayout or QVBoxLayout. + */ + void setCoordinatePlaneLayout( QLayout * layout ); + QLayout* coordinatePlaneLayout(); + + /** + * The first header or footer of the chart. By default there is none. + * @return The first header or footer of the chart or 0 if there was none + * added to the chart. + */ + HeaderFooter* headerFooter(); + + /** + * The list of headers and footers associated with the chart. + * @return The list of headers and footers associated with the chart. + */ + HeaderFooterList headerFooters(); + + /** + * Adds a header or a footer to the chart. The chart takes ownership. + * @param headerFooter The header (or footer, resp.) to add. + * + * \sa replaceHeaderFooter, takeHeaderFooter + */ + void addHeaderFooter( HeaderFooter* headerFooter ); + + /** + * Replaces the old header (or footer, resp.), or appends the + * new header or footer, it there is none yet. + * + * @param headerFooter The header or footer to be used instead of the old one. + * This parameter must not be zero, or the method will do nothing. + * + * @param oldHeaderFooter The header or footer to be removed by the new one. This + * header or footer will be deleted automatically. If the parameter is omitted, + * the very first header or footer will be replaced. In case, there was no + * header and no footer yet, the new header or footer will just be added. + * + * \note If you want to re-use the old header or footer, call takeHeaderFooter and + * addHeaderFooter, instead of using replaceHeaderFooter. + * + * \sa addHeaderFooter, takeHeaderFooter + */ + void replaceHeaderFooter ( HeaderFooter* headerFooter, + HeaderFooter* oldHeaderFooter = 0 ); + + /** + * Removes the header (or footer, resp.) from the chart, without deleting it. + * + * The chart no longer owns the header or footer, so it is + * the caller's responsibility to delete the header or footer. + * + * \sa addHeaderFooter, replaceHeaderFooter + */ + void takeHeaderFooter( HeaderFooter* headerFooter ); + + /** + * The first legend of the chart or 0 if there was none added to the chart. + * @return The first legend of the chart or 0 if none exists. + */ + Legend* legend(); + + /** + * The list of all legends associated with the chart. + * @return The list of all legends associated with the chart. + */ + LegendList legends(); + + /** + * Add the given legend to the chart. The chart takes ownership. + * @param legend The legend to add. + * + * \sa replaceLegend, takeLegend + */ + void addLegend( Legend* legend ); + + /** + * Replaces the old legend, or appends the + * new legend, it there is none yet. + * + * @param legend The legend to be used instead of the old one. + * This parameter must not be zero, or the method will do nothing. + * + * @param oldLegend The legend to be removed by the new one. This + * legend will be deleted automatically. If the parameter is omitted, + * the very first legend will be replaced. In case, there was no + * legend yet, the new legend will just be added. + * + * If you want to re-use the old legend, call takeLegend and + * addLegend, instead of using replaceLegend. + * + * \note Whenever addLegend is called the font sizes used by the + * Legend are set to relative and they get coupled to the Chart's size, + * with their relative values being 20 for the item texts and 24 to the + * title text. So if you want to use custom font sizes for the Legend + * make sure to set them after calling addLegend. + * + * \sa addLegend, takeLegend + */ + void replaceLegend ( Legend* legend, Legend* oldLegend = 0 ); + + /** + * Removes the legend from the chart, without deleting it. + * + * The chart no longer owns the legend, so it is + * the caller's responsibility to delete the legend. + * + * \sa addLegend, takeLegend + */ + void takeLegend( Legend* legend ); + + /** + * Set the padding between the margin of the widget and the area that + * the contents are drawn into. + * @param left The padding on the left side. + * @param top The padding at the top. + * @param right The padding on the left hand side. + * @param bottom The padding on the bottom. + * + * \note Using previous versions of KD Chart you might have called + * setGlobalLeading() to make room for long Abscissa labels (or for an + * overlapping top label of an Ordinate axis, resp.) that would not fit + * into the normal axis area. This is \em no \em longer \em needed + * because KD Chart now is using hidden auto-spacer items reserving + * as much free space as is needed for axes with overlaping content + * at the respective sides. + * + * \sa setGlobalLeadingTop, setGlobalLeadingBottom, setGlobalLeadingLeft, setGlobalLeadingRight + * \sa globalLeadingTop, globalLeadingBottom, globalLeadingLeft, globalLeadingRight + */ + void setGlobalLeading( int left, int top, int right, int bottom ); + + /** + * Set the padding between the start of the widget and the start + * of the area that is used for drawing on the left. + * @param leading The padding value. + * + * \sa setGlobalLeading + */ + void setGlobalLeadingLeft( int leading ); + + /** + * The padding between the start of the widget and the start + * of the area that is used for drawing on the left. + * @return The padding between the start of the widget and the start + * of the area that is used for drawing on the left. + * + * \sa setGlobalLeading + */ + int globalLeadingLeft() const; + + /** + * Set the padding between the start of the widget and the start + * of the area that is used for drawing at the top. + * @param leading The padding value. + * + * \sa setGlobalLeading + */ + void setGlobalLeadingTop( int leading ); + + /** + * The padding between the start of the widget and the start + * of the area that is used for drawing at the top. + * @return The padding between the start of the widget and the start + * of the area that is used for drawing at the top. + * + * \sa setGlobalLeading + */ + int globalLeadingTop() const; + + /** + * Set the padding between the start of the widget and the start + * of the area that is used for drawing on the right. + * @param leading The padding value. + * + * \sa setGlobalLeading + */ + void setGlobalLeadingRight( int leading ); + + /** + * The padding between the start of the widget and the start + * of the area that is used for drawing on the right. + * @return The padding between the start of the widget and the start + * of the area that is used for drawing on the right. + * + * \sa setGlobalLeading + */ + int globalLeadingRight() const; + + /** + * Set the padding between the start of the widget and the start + * of the area that is used for drawing on the bottom. + * @param leading The padding value. + * + * \sa setGlobalLeading + */ + void setGlobalLeadingBottom( int leading ); + + /** + * The padding between the start of the widget and the start + * of the area that is used for drawing at the bottom. + * @return The padding between the start of the widget and the start + * of the area that is used for drawing at the bottom. + * + * \sa setGlobalLeading + */ + int globalLeadingBottom() const; + + /** + * Paints all the contents of the chart. Use this method, to make KDChart + * draw into your QPainter. + * + * \note Any global leading settings will be used by the paint method too, + * so make sure to set them to zero, if you want the drawing to have the exact + * size of the target rectangle. + * + * \param painter The painter to be drawn into. + * \param target The rectangle to be filled by the Chart's drawing. + * + * \sa setGlobalLeading + */ + void paint( QPainter* painter, const QRect& target ); + + void reLayoutFloatingLegends(); + + Q_SIGNALS: + /** Emitted upon change of a property of the Chart or any of its components. */ + void propertiesChanged(); + + protected: + /** + * Adjusts the internal layout when the chart is resized. + */ + /* reimp */ void resizeEvent ( QResizeEvent * event ); + + /** + * @brief Draws the background and frame, then calls paint(). + * + * In most cases there is no need to override this method in a derived + * class, but if you do, do not forget to call paint(). + * @sa paint + */ + /* reimp */ void paintEvent( QPaintEvent* event ); + + /** reimp */ + void mousePressEvent( QMouseEvent* event ); + /** reimp */ + void mouseDoubleClickEvent( QMouseEvent* event ); + /** reimp */ + void mouseMoveEvent( QMouseEvent* event ); + /** reimp */ + void mouseReleaseEvent( QMouseEvent* event ); + /** reimp */ + bool event( QEvent* event ); + }; + +// Here we have a few docu block to be included into the API documentation: +/** + * \dir src + * \brief Implementation directory of KDChart. + * + * This directory contains the header files and the source files of both, + * the private and the public classes. + * + * \note Only classes that have an include wrapper in the \c $KDCHARTDIR/include + * directory are part of the supported API. + * All other classes are to be considered as implemntation details, they + * could be changed in future versions of KDChart without notice. + * + * In other words: No class that is not mentioned in the \c $KDCHARTDIR/include + * directory may be directly used by your application. + * + * The recommended way to include classes of the KDChart API is including + * them by class name, so instead of including KDChartChart.h you would say: + * + \verbatim +#include + \endverbatim + * + * When following this there is no reason to include the \c $KDCHARTDIR/src + * directory, it is sufficient to include \c $KDCHARTDIR/include + */ +} +/** + * @class QAbstractItemView "(do not include)" + * @brief Class only listed here to document inheritance of some KDChart classes. + * + * Please consult the respective Qt documentation for details: + * http://doc.trolltech.com/ + */ +/** + * @class QAbstractProxyModel "(do not include)" + * @brief Class only listed here to document inheritance of some KDChart classes. + * + * Please consult the respective Qt documentation for details: + * http://doc.trolltech.com/ + */ +/** + * @class QFrame "(do not include)" + * @brief Class only listed here to document inheritance of some KDChart classes. + * + * Please consult the respective Qt documentation for details: + * http://doc.trolltech.com/ + */ +/** + * @class QObject "(do not include)" + * @brief Class only listed here to document inheritance of some KDChart classes. + * + * Please consult the respective Qt documentation for details: + * http://doc.trolltech.com/ + */ +/** + * @class QSortFilterProxyModel "(do not include)" + * @brief Class only listed here to document inheritance of some KDChart classes. + * + * Please consult the respective Qt documentation for details: + * http://doc.trolltech.com/ + */ +/** + * @class QWidget "(do not include)" + * @brief Class only listed here to document inheritance of some KDChart classes. + * + * Please consult the respective Qt documentation for details: + * http://doc.trolltech.com/ + */ +/** + * @class QTextDocument "(do not include)" + * @brief Class only listed here to document inheritance of some KDChart classes. + * + * Please consult the respective Qt documentation for details: + * http://doc.trolltech.com/ + */ +/** + * @class QLayoutItem "(do not include)" + * @brief Class only listed here to document inheritance of some KDChart classes. + * + * Please consult the respective Qt documentation for details: + * http://doc.trolltech.com/ + */ +/** + * @class QGraphicsPolygonItem "(do not include)" + * @brief Class only listed here to document inheritance of some KDChart classes. + * + * Please consult the respective Qt documentation for details: + * http://doc.trolltech.com/ + */ + + +#endif diff --git a/libkdchart/src/KDChartChart_p.h b/libkdchart/src/KDChartChart_p.h new file mode 100644 index 0000000..d12f9ad --- /dev/null +++ b/libkdchart/src/KDChartChart_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTCHART_P_H +#define KDCHARTCHART_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include "KDChartChart.h" +#include "KDChartAbstractArea.h" +#include "KDChartTextArea.h" +#include "KDChartFrameAttributes.h" +#include "KDChartBackgroundAttributes.h" +#include "KDChartLayoutItems.h" + +#include + + +namespace KDChart { + +/* + struct PlaneInfo can't be declared inside Chart::Private, otherwise MSVC.net says: + qhash.h(195) : error C2248: 'KDChart::Chart::Private' : cannot access protected class declared in class 'KDChart::Chart' + KDChartChart_p.h(58) : see declaration of 'KDChart::Chart::Private' + KDChartChart.h(61) : see declaration of 'KDChart::Chart' + KDChartChart.cpp(262) : see reference to class template instantiation 'QHash' being compiled, with + Key=KDChart::AbstractCoordinatePlane *, + T=KDChart::Chart::Private::PlaneInfo +*/ +/** + * \internal + */ +struct PlaneInfo { + PlaneInfo() + : referencePlane( 0 ), + horizontalOffset( 1 ), + verticalOffset( 1 ), + gridLayout( 0 ), + topAxesLayout( 0 ), + bottomAxesLayout( 0 ), + leftAxesLayout( 0 ), + rightAxesLayout( 0 ) + {} + AbstractCoordinatePlane *referencePlane; + int horizontalOffset; + int verticalOffset; + QGridLayout* gridLayout; + QVBoxLayout* topAxesLayout; + QVBoxLayout* bottomAxesLayout; + QHBoxLayout* leftAxesLayout; + QHBoxLayout* rightAxesLayout; +}; + + +/** + * \internal + */ +class Chart::Private : public QObject +{ + Q_OBJECT + public: + CoordinatePlaneList coordinatePlanes; + HeaderFooterList headerFooters; + LegendList legends; + + Chart* chart; + QHBoxLayout* layout; + QVBoxLayout* vLayout; + QBoxLayout* planesLayout; + QGridLayout* headerLayout; + QGridLayout* footerLayout; + QGridLayout* dataAndLegendLayout; + + QVBoxLayout* innerHdFtLayouts[2][3][3]; // auxiliary pointers + + QMap< int, QMap< int, HorizontalLineLayoutItem > > dummyHeaders; + QMap< int, QMap< int, HorizontalLineLayoutItem > > dummyFooters; + + QVector textLayoutItems; + QVector layoutItems; + QVector planeLayoutItems; + QVector legendLayoutItems; + + QSize currentLayoutSize; + + // since we do not want to derive Chart from AbstractAreaBase, + // we store the attributes here, and then we call two static painting + // methods to drawn the background (or frame, resp.). + KDChart::FrameAttributes frameAttributes; + KDChart::BackgroundAttributes backgroundAttributes; + + int globalLeadingLeft, globalLeadingRight, globalLeadingTop, globalLeadingBottom; + + QList< AbstractCoordinatePlane* > mouseClickedPlanes; + + Private ( Chart* ); + + virtual ~Private(); + + void removeDummyHeaderFooters(); + + void createLayouts( QWidget * parent ); + void layoutLegends(); + void layoutHeadersAndFooters(); + void resizeLayout( const QSize& sz ); + void paintAll( QPainter* painter ); + + struct AxisInfo { + AxisInfo() + :plane(0) + {} + AbstractCoordinatePlane *plane; + }; + + QHash buildPlaneLayoutInfos(); + + public Q_SLOTS: + void slotLayoutPlanes(); + void slotRelayout(); + void slotUnregisterDestroyedLegend( Legend * legend ); + void slotUnregisterDestroyedHeaderFooter( HeaderFooter* headerFooter ); + void slotUnregisterDestroyedPlane( AbstractCoordinatePlane* plane ); + /* + // Unused code trying to use a push-model: This did not work + // since we can not re-layout the planes each time when + // Qt layouting is calling sizeHint() + void slotAdjustLeftRightColumnsForOverlappingLabels( + CartesianAxis* axis, int left, int right); + void slotAdjustTopBottomRowsForOverlappingLabels( + CartesianAxis* axis, int top, int bottom); + */ +}; + +} + +#endif diff --git a/libkdchart/src/KDChartDataValueAttributes.cpp b/libkdchart/src/KDChartDataValueAttributes.cpp new file mode 100644 index 0000000..66a58ba --- /dev/null +++ b/libkdchart/src/KDChartDataValueAttributes.cpp @@ -0,0 +1,368 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartDataValueAttributes.h" + +#include +#include +#include "KDChartRelativePosition.h" +#include "KDChartPosition.h" +#include +#include +#include +#include + +#include + +// FIXME till +#define KDCHART_DATA_VALUE_AUTO_DIGITS 4 + + +#define d d_func() + +using namespace KDChart; + +class DataValueAttributes::Private +{ + friend class DataValueAttributes; +public: + Private(); +private: + bool visible; + TextAttributes textAttributes; + FrameAttributes frameAttributes; + BackgroundAttributes backgroundAttributes; + MarkerAttributes markerAttributes; + int decimalDigits; + QString prefix; + QString suffix; + QString dataLabel; + int powerOfTenDivisor; + bool showInfinite; + RelativePosition negativeRelPos; + RelativePosition positiveRelPos; + bool showRepetitiveDataLabels; + bool showOverlappingDataLabels; + bool usePercentage; +}; + +DataValueAttributes::Private::Private() : + visible( false ), + decimalDigits( KDCHART_DATA_VALUE_AUTO_DIGITS ), + powerOfTenDivisor( 0 ), + showInfinite( true ) +{ + Measure me( 25.0, + KDChartEnums::MeasureCalculationModeAuto, + KDChartEnums::MeasureOrientationAuto ); + textAttributes.setFontSize( me ); + me.setValue( 8.0 ); + me.setCalculationMode( KDChartEnums::MeasureCalculationModeAbsolute ); + textAttributes.setMinimalFontSize( me ); + textAttributes.setRotation( -45 ); + + // we set the Position to unknown: so the diagrams can take their own decisions + positiveRelPos.setReferencePosition( Position::Unknown ); // a bar diagram will use: Position::NorthWest + negativeRelPos.setReferencePosition( Position::Unknown ); // a bar diagram will use: Position::SouthEast + + positiveRelPos.setAlignment( Qt::AlignLeft | Qt::AlignBottom ); + negativeRelPos.setAlignment( Qt::AlignRight | Qt::AlignTop ); + + showRepetitiveDataLabels = false; + showOverlappingDataLabels = false; + + // By default use 0.4 (or 0.5, resp.) of the font height as horizontal distance between + // the data and their respective data value texts, + // and use 0.75 as the vertical distance. + const double posHoriPadding = 400.0; const double posVertPadding = -75.0; + const double negHoriPadding = -500.0; const double negVertPadding = 75.0; + Measure m( posHoriPadding, KDChartEnums::MeasureCalculationModeAuto ); + positiveRelPos.setHorizontalPadding( m ); + m.setValue( posVertPadding ); + positiveRelPos.setVerticalPadding( m ); + m.setValue( negHoriPadding ); + negativeRelPos.setHorizontalPadding( m ); + m.setValue( negVertPadding ); + negativeRelPos.setVerticalPadding( m ); + + usePercentage = false; +} + + +DataValueAttributes::DataValueAttributes() + : _d( new Private() ) +{ +} + +DataValueAttributes::DataValueAttributes( const DataValueAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +DataValueAttributes & DataValueAttributes::operator=( const DataValueAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +DataValueAttributes::~DataValueAttributes() +{ + delete _d; _d = 0; +} + + +bool DataValueAttributes::operator==( const DataValueAttributes& r ) const +{ + /* + qDebug() << "DataValueAttributes::operator== finds" + << "b" << (isVisible() == r.isVisible()) + << "c" << (textAttributes() == r.textAttributes()) + << "d" << (frameAttributes() == r.frameAttributes()) + << "e" << (backgroundAttributes() == r.backgroundAttributes()) + << "f" << (markerAttributes() == r.markerAttributes()) + << "g" << (decimalDigits() == r.decimalDigits()) + << "h" << (prefix() == r.prefix()) + << "i" << (suffix() == r.suffix()) + << "j" << (dataLabel() == r.dataLabel()) + << "k" << (powerOfTenDivisor() == r.powerOfTenDivisor()) + << "l" << (showInfinite() == r.showInfinite()) + << "m" << (negativePosition() == r.negativePosition()) + << "n" << (positivePosition() == r.positivePosition()) + << "o" << (showRepetitiveDataLabels() == r.showRepetitiveDataLabels()) + << "p" << (showOverlappingDataLabels() == r.showOverlappingDataLabels()); + */ + return ( isVisible() == r.isVisible() && + textAttributes() == r.textAttributes() && + frameAttributes() == r.frameAttributes() && + backgroundAttributes() == r.backgroundAttributes() && + markerAttributes() == r.markerAttributes() && + decimalDigits() == r.decimalDigits() && + prefix() == r.prefix() && + suffix() == r.suffix() && + dataLabel() == r.dataLabel() && + powerOfTenDivisor() == r.powerOfTenDivisor() && + showInfinite() == r.showInfinite() && + negativePosition() == r.negativePosition() && + positivePosition() == r.positivePosition() && + showRepetitiveDataLabels() == r.showRepetitiveDataLabels() && + showOverlappingDataLabels() == r.showOverlappingDataLabels() && + usePercentage() == r.usePercentage() ); +} + +/*static*/ +const DataValueAttributes& DataValueAttributes::defaultAttributes() +{ + static const DataValueAttributes theDefaultDataValueAttributes; + return theDefaultDataValueAttributes; +} + +/*static*/ +const QVariant& DataValueAttributes::defaultAttributesAsVariant() +{ + static const QVariant theDefaultDataValueAttributesVariant = qVariantFromValue(defaultAttributes()); + return theDefaultDataValueAttributesVariant; +} + + +void DataValueAttributes::setVisible( bool visible ) +{ + d->visible = visible; +} + +bool DataValueAttributes::isVisible() const +{ + return d->visible; +} + +void DataValueAttributes::setTextAttributes( const TextAttributes &a ) +{ + d->textAttributes = a; +} + +TextAttributes DataValueAttributes::textAttributes() const +{ + return d->textAttributes; +} + +void DataValueAttributes::setFrameAttributes( const FrameAttributes &a ) +{ + d->frameAttributes = a; +} + +FrameAttributes DataValueAttributes::frameAttributes() const +{ + return d->frameAttributes; +} + +void DataValueAttributes::setBackgroundAttributes( const BackgroundAttributes &a ) +{ + d->backgroundAttributes = a; +} + +BackgroundAttributes DataValueAttributes::backgroundAttributes() const +{ + return d->backgroundAttributes; +} + +void DataValueAttributes::setMarkerAttributes( const MarkerAttributes &a ) +{ + d->markerAttributes = a; +} + +MarkerAttributes DataValueAttributes::markerAttributes() const +{ + return d->markerAttributes; +} + +void DataValueAttributes::setUsePercentage( bool enable ) +{ + d->usePercentage = enable; +} + +bool DataValueAttributes::usePercentage() const +{ + return d->usePercentage; +} + +void DataValueAttributes::setDecimalDigits( int digits ) +{ + d->decimalDigits = digits; +} + +int DataValueAttributes::decimalDigits() const +{ + return d->decimalDigits; +} + +void DataValueAttributes::setPrefix( const QString prefixString ) +{ + d->prefix = prefixString; +} + +QString DataValueAttributes::prefix() const +{ + return d->prefix; +} + +void DataValueAttributes::setSuffix( const QString suffixString ) +{ + d->suffix = suffixString; +} + +QString DataValueAttributes::suffix() const +{ + return d->suffix; +} + +void DataValueAttributes::setDataLabel( const QString label ) +{ + d->dataLabel = label; +} + +QString DataValueAttributes::dataLabel() const +{ + return d->dataLabel; +} + +bool DataValueAttributes::showRepetitiveDataLabels() const +{ + return d->showRepetitiveDataLabels; +} + +void DataValueAttributes::setShowRepetitiveDataLabels( bool showRepetitiveDataLabels ) +{ + d->showRepetitiveDataLabels = showRepetitiveDataLabels; +} + +bool DataValueAttributes::showOverlappingDataLabels() const +{ + return d->showOverlappingDataLabels; +} + +void DataValueAttributes::setShowOverlappingDataLabels( bool showOverlappingDataLabels ) +{ + d->showOverlappingDataLabels = showOverlappingDataLabels; +} + +void DataValueAttributes::setPowerOfTenDivisor( int powerOfTenDivisor ) +{ + d->powerOfTenDivisor = powerOfTenDivisor; +} + +int DataValueAttributes::powerOfTenDivisor() const +{ + return d->powerOfTenDivisor; +} + +void DataValueAttributes::setShowInfinite( bool infinite ) +{ + d->showInfinite = infinite; +} + +bool DataValueAttributes::showInfinite() const +{ + return d->showInfinite; +} + +void DataValueAttributes::setNegativePosition( const RelativePosition& relPosition ) +{ + d->negativeRelPos = relPosition; +} + +const RelativePosition DataValueAttributes::negativePosition() const +{ + return d->negativeRelPos; +} + +void DataValueAttributes::setPositivePosition( const RelativePosition& relPosition ) +{ + d->positiveRelPos = relPosition; +} + +const RelativePosition DataValueAttributes::positivePosition() const +{ + return d->positiveRelPos; +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::DataValueAttributes& val ) +{ + dbg << "RelativePosition DataValueAttributes(" + << "visible="< +#include +#include "KDChartGlobal.h" +#include "KDChartEnums.h" +#include "KDChartRelativePosition.h" + +/** \file KDChartDataValueAttributes.h + * \brief Declaring the class KDChart::DataValueAttributes. + * + * + */ + + +namespace KDChart { + + class TextAttributes; + class BackgroundAttributes; + class FrameAttributes; + class MarkerAttributes; + + /** + * \class DataValueAttributes KDChartDataValueAttributes.h KDChartDataValueAttributes + * \brief Diagram attributes dealing with data value labels. + * + * The DataValueAttributes group all properties that can be set + * wrt data value labels and if and how they are displayed. This + * includes things like the text attributes (font, color), what + * markers are used, howmany decimal digits are displayed, etc. + */ +class KDCHART_EXPORT DataValueAttributes +{ +public: + DataValueAttributes(); + DataValueAttributes( const DataValueAttributes& ); + DataValueAttributes &operator= ( const DataValueAttributes& ); + bool operator==( const DataValueAttributes& ) const; + inline bool operator!=( const DataValueAttributes& other ) const { return !operator==(other); } + + ~DataValueAttributes(); + + static const DataValueAttributes& defaultAttributes(); + static const QVariant& defaultAttributesAsVariant(); + + /** Set whether data value labels should be displayed. + * \param visible Whether data value labels should be displayed. + */ + void setVisible( bool visible ); + + /** + * @return Whether data value labels should be displayed. + */ + bool isVisible() const; + + /** + * Set the text attributes to use for the data value labels. + * \param a The text attributes to set. + * \see TextAttributes + */ + void setTextAttributes( const TextAttributes &a ); + + /** + * \return The text attributes used for painting data value labels. + */ + TextAttributes textAttributes() const; + + /** + * Set the frame attributes to use for the data value labels area. + * \param a The frame attributes to set. + * \see FrameAttributes + */ + void setFrameAttributes( const FrameAttributes &a ); + + /** + * \return The frame attributes used for painting the data + * value labels area. + * \see FrameAttributes + */ + FrameAttributes frameAttributes() const; + + /** + * Set the background attributes to use for the data value labels area. + * \param a The background attributes to set. + * \see BackgroundAttributes + */ + void setBackgroundAttributes( const BackgroundAttributes &a ); + + /** + * \return The background attributes used for painting the data + * value labels area. + * \see BackgroundAttributes + */ + BackgroundAttributes backgroundAttributes() const; + + /** + * Set the marker attributes to use for the data values. This includes + * the marker type. + * \param a The marker attributes to set. + * \see MarkerAttributes + */ + void setMarkerAttributes( const MarkerAttributes &a ); + + /** + * \return The marker attributes used for decorating the data + * values. + * \see MarkerAttributes + */ + MarkerAttributes markerAttributes() const; + + /** + * Specify whether to use percentages instead of actual data point values when no + * specific label is set. In a bar or cartesian diagram, this means that the value + * will be shown in % in relation to the sum of all values in the same category, in + * a polar diagram in relation to the sum of all values in a data set. + * + * When this is turned on, the value will \b not automatically have the '%' postfix. + * \param enable Whether to enable percentage values + */ + void setUsePercentage( bool enable ); + + /** + * \return Whether to use percentage values + * \see setUsePercentage + */ + bool usePercentage() const; + + /** + * Set how many decimal digits to display when rendering the data value + * labels. If there are no decimal digits it will not be displayed. + * \param digits The number of decimal digits to use. + */ + void setDecimalDigits( int digits ); + + /** + * \return The number of decimal digits displayed. + */ + int decimalDigits() const; + + /** + * \brief Prepend a prefix string to the data value label + * \sa prefix + */ + void setPrefix( const QString prefix ); + + /** + * \brief Returns the string used as a prefix to the data value text. + * \sa setPrefix + */ + QString prefix() const; + + /** + * \brief Append a suffix string to the data value label + * \sa suffix + */ + void setSuffix( const QString suffix ); + + /** + * \brief Returns the string used as a suffix to the data value text. + * \sa setSuffix + */ + QString suffix() const; + + /** + * \brief display a string label instead of the original data value label + * Supports HTML code. + * \sa dataLabel + */ + void setDataLabel( const QString label ); + + /** + * \brief Returns the string displayed instead of the data value label + * \sa setDataLabel + */ + QString dataLabel() const; + + /** + * \return Whether data values not different from their predecessors are drawn. + */ + bool showRepetitiveDataLabels() const; + + /** + * + * Set whether data value labels not different from their predecessors should be drawn. + * \param showRepetitiveDataLabels Whether data value not different from their predecessors are drawn. + */ + void setShowRepetitiveDataLabels( bool showRepetitiveDataLabels ); + + /** + * \return Whether data value texts overlapping other data value texts of the same diagram are drawn. + */ + bool showOverlappingDataLabels() const; + + /** + * + * Set whether data value texts overlapping other data value texts of the same diagram should be drawn. + * \param showOverlappingDataLabels Whether data texts overlapping other data value texts of the same diagram are drawn. + */ + void setShowOverlappingDataLabels( bool showOverlappingDataLabels ); + + /** + * \cond PLANNED_FOR_FUTURE + * + * These method are planned for future versions of KD Chart, + * so they are not part of the documented API yet. + * + */ + void setPowerOfTenDivisor( int powerOfTenDivisor ); + int powerOfTenDivisor() const; + /** + * \endcond + */ + + + /** + * \cond PLANNED_FOR_FUTURE + * + * These method are planned for future versions of KD Chart, + * so they are not part of the documented API yet. + */ + void setShowInfinite( bool infinite ); + bool showInfinite() const; + /** + * \endcond + */ + + /** + * \brief Defines the relative positioning of the data value labels for negative values. + * + * The position is specified in relation to the respective data value point, or in + * releation to the respective data representation area, that's one area segment in + * a LineDiagram showing areas, or one bar in a BarDiagram, one pie slice ... + * + * \sa negativePosition + */ + void setNegativePosition( const RelativePosition& relPosition ); + + /** + * \brief Return the relative positioning of the data value labels + * \sa setNegativePosition + */ + const RelativePosition negativePosition() const; + + /** + * \brief Defines the relative position of the data value labels for positive values. + * + * The position is specified in relation to the respective data value point, or in + * releation to the respective data representation area, that's one area segment in + * a LineDiagram showing areas, or one bar in a BarDiagram, one pie slice ... + * + * \sa positivePosition + */ + void setPositivePosition( const RelativePosition& relPosition ); + + /** + * \brief Return the relative positioning of the data value labels + * \sa setPositivePosition + */ + const RelativePosition positivePosition() const; + + const RelativePosition position( bool positive ) const + { + return positive ? positivePosition() : negativePosition(); + } + +private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( DataValueAttributes ) + +}; // End of class DataValueAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::DataValueAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +Q_DECLARE_METATYPE( KDChart::DataValueAttributes ) +Q_DECLARE_TYPEINFO( KDChart::DataValueAttributes, Q_MOVABLE_TYPE ); +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::DataValueAttributes ) + +#endif // KDCHARTDATAVALUEATTRIBUTES_H diff --git a/libkdchart/src/KDChartDatasetProxyModel.cpp b/libkdchart/src/KDChartDatasetProxyModel.cpp new file mode 100644 index 0000000..cc4abe7 --- /dev/null +++ b/libkdchart/src/KDChartDatasetProxyModel.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartDatasetProxyModel.h" + +#include + +#include + + +using namespace KDChart; + +DatasetProxyModel::DatasetProxyModel (QObject* parent) + : QSortFilterProxyModel ( parent ) +{ +} + +QModelIndex DatasetProxyModel::buddy( const QModelIndex& index ) const +{ + return index; +} + +Qt::ItemFlags DatasetProxyModel::flags( const QModelIndex& index ) const +{ + return sourceModel()->flags( mapToSource( index ) ); +} + +void DatasetProxyModel::setDatasetRowDescriptionVector ( + const DatasetDescriptionVector& configuration ) +{ + Q_ASSERT_X ( sourceModel(), "DatasetProxyModel::setDatasetRowDescriptionVector", + "A source model must be set before the selection can be configured." ); + initializeDatasetDecriptors ( configuration, sourceModel()->rowCount(mRootIndex), + mRowSrcToProxyMap, mRowProxyToSrcMap ); + clear(); // clear emits layoutChanged() +} + +void DatasetProxyModel::setDatasetColumnDescriptionVector ( + const DatasetDescriptionVector& configuration ) +{ + Q_ASSERT_X ( sourceModel(), "DatasetProxyModel::setDatasetColumnDescriptionVector", + "A source model must be set before the selection can be configured." ); + initializeDatasetDecriptors ( configuration, sourceModel()->columnCount(mRootIndex), + mColSrcToProxyMap, mColProxyToSrcMap ); + clear(); // clear emits layoutChanged() +} + +void DatasetProxyModel::setDatasetDescriptionVectors ( + const DatasetDescriptionVector& rowConfig, + const DatasetDescriptionVector& columnConfig ) +{ + setDatasetRowDescriptionVector( rowConfig ); + setDatasetColumnDescriptionVector ( columnConfig ); +} + +QModelIndex DatasetProxyModel::index( int row, int column, + const QModelIndex &parent ) const +{ + return mapFromSource( sourceModel()->index( mapProxyRowToSource(row), + mapProxyColumnToSource(column), + parent ) ); +} + +QModelIndex DatasetProxyModel::parent( const QModelIndex& child ) const +{ +// return mapFromSource( sourceModel()->parent( child ) ); + return mapFromSource( sourceModel()->parent( mapToSource( child ) ) ); +} + +QModelIndex DatasetProxyModel::mapFromSource ( const QModelIndex & sourceIndex ) const +{ + Q_ASSERT_X ( sourceModel(), "DatasetProxyModel::mapFromSource", "A source " + "model must be set before the selection can be configured." ); + + if ( !sourceIndex.isValid() ) return sourceIndex; + + if ( mRowSrcToProxyMap.isEmpty() && mColSrcToProxyMap.isEmpty() ) + { + return createIndex ( sourceIndex.row(), sourceIndex.column(), + sourceIndex.internalPointer() ); + } else { + int row = mapSourceRowToProxy ( sourceIndex.row() ); + int column = mapSourceColumnToProxy ( sourceIndex.column() ); + return createIndex ( row, column, sourceIndex.internalPointer() ); + } +} + +QModelIndex DatasetProxyModel::mapToSource ( const QModelIndex& proxyIndex ) const +{ + Q_ASSERT_X ( sourceModel(), "DatasetProxyModel::mapToSource", "A source " + "model must be set before the selection can be configured." ); + + if ( !proxyIndex.isValid() ) return proxyIndex; + if ( mRowSrcToProxyMap.isEmpty() && mColSrcToProxyMap.isEmpty() ) + { + return sourceModel()->index( proxyIndex.row(), proxyIndex.column(), mRootIndex ); + } else { + int row = mapProxyRowToSource ( proxyIndex.row() ); + int column = mapProxyColumnToSource ( proxyIndex.column() ); + return sourceModel()->index( row, column, mRootIndex ); + } +} + +bool DatasetProxyModel::filterAcceptsRow ( int sourceRow, + const QModelIndex & ) const +{ + if ( mRowSrcToProxyMap.isEmpty() ) + { // no row mapping set, all rows are passed down: + return true; + } else { + Q_ASSERT ( sourceModel() ); + Q_ASSERT ( mRowSrcToProxyMap.size() == sourceModel()->rowCount(mRootIndex) ); + if ( mRowSrcToProxyMap[sourceRow] == -1 ) + { // this row is explicitly not accepted: + return false; + } else { + Q_ASSERT ( mRowSrcToProxyMap[sourceRow] >= 0 + && mRowSrcToProxyMap[sourceRow] < mRowSrcToProxyMap.size() ); + return true; + } + } +} + +bool DatasetProxyModel::filterAcceptsColumn ( int sourceColumn, + const QModelIndex & ) const +{ + if ( mColSrcToProxyMap.isEmpty() ) + { // no column mapping set up yet, all columns are passed down: + return true; + } else { + Q_ASSERT ( sourceModel() ); + Q_ASSERT ( mColSrcToProxyMap.size() == sourceModel()->columnCount(mRootIndex) ); + if ( mColSrcToProxyMap[sourceColumn] == -1 ) + { // this column is explicitly not accepted: + return false; + } else { + Q_ASSERT ( mColSrcToProxyMap[sourceColumn] >= 0 + && mColSrcToProxyMap[sourceColumn] < mColSrcToProxyMap.size() ); + return true; + } + } +} + +int DatasetProxyModel::mapProxyRowToSource ( const int& proxyRow ) const +{ + if ( mRowProxyToSrcMap.isEmpty() ) + { // if no row mapping is set, we pass down the row: + return proxyRow; + } else { + Q_ASSERT ( proxyRow >= 0 && proxyRow < mRowProxyToSrcMap.size() ); + return mRowProxyToSrcMap[ proxyRow ]; + } +} + +int DatasetProxyModel::mapProxyColumnToSource ( const int& proxyColumn ) const +{ + if ( mColProxyToSrcMap.isEmpty() ) + { // if no column mapping is set, we pass down the column: + return proxyColumn; + } else { + Q_ASSERT ( proxyColumn >= 0 && proxyColumn < mColProxyToSrcMap.size() ); + return mColProxyToSrcMap[ proxyColumn ]; + } +} + +int DatasetProxyModel::mapSourceRowToProxy ( const int& sourceRow ) const +{ + if ( mRowSrcToProxyMap.isEmpty() ) + { + return sourceRow; + } else { + Q_ASSERT ( sourceRow >= 0 && sourceRow < mRowSrcToProxyMap.size() ); + return mRowSrcToProxyMap[sourceRow]; + } +} + +int DatasetProxyModel::mapSourceColumnToProxy ( const int& sourceColumn ) const +{ + if ( mColSrcToProxyMap.isEmpty() ) + { + return sourceColumn; + } else { + Q_ASSERT ( sourceColumn >= 0 && sourceColumn < mColSrcToProxyMap.size() ); + return mColSrcToProxyMap.at( sourceColumn ) ; + } +} + +void DatasetProxyModel::resetDatasetDescriptions() +{ + mRowSrcToProxyMap.clear(); + mRowProxyToSrcMap.clear(); + mColSrcToProxyMap.clear(); + mColProxyToSrcMap.clear(); + clear(); +} + +QVariant DatasetProxyModel::data(const QModelIndex &index, int role) const +{ + return sourceModel()->data( mapToSource ( index ), role ); +} + +bool DatasetProxyModel::setData( const QModelIndex& index, const QVariant& value, int role ) +{ + return sourceModel()->setData( mapToSource( index ), value, role ); +} + +QVariant DatasetProxyModel::headerData ( int section, Qt::Orientation orientation, int role ) const +{ + if ( orientation == Qt::Horizontal ) + { + if ( mapProxyColumnToSource ( section ) == -1 ) + { + return QVariant(); + } else { + return sourceModel()->headerData ( mapProxyColumnToSource ( section ), + orientation, role ); + } + } else { + if ( mapProxyRowToSource ( section ) == -1 ) + { + return QVariant(); + } else { + return sourceModel()->headerData ( mapProxyRowToSource ( section ), + orientation, role ); + } + } +} + +void DatasetProxyModel::initializeDatasetDecriptors ( + const DatasetDescriptionVector& inConfiguration, + const int sourceCount, + DatasetDescriptionVector& outSourceToProxyMap, + DatasetDescriptionVector& outProxyToSourceMap ) +{ + // in the current mapping implementation, the proxy-to-source map is + // identical to the configuration vector: + outProxyToSourceMap = inConfiguration; + outSourceToProxyMap.fill ( -1, sourceCount ); + + for ( int index = 0; index < inConfiguration.size(); ++index ) + { + // make sure the values in inConfiguration point to columns in the + // source model: + + if (inConfiguration[index] == -1) + continue; + + Q_ASSERT_X ( inConfiguration[index] >= 0 + && inConfiguration[index] < sourceCount, + "DatasetProxyModel::initializeDatasetDecriptors", + "column index outside of source model" ); + Q_ASSERT_X ( outSourceToProxyMap[inConfiguration[index]] == -1 , + "DatasetProxyModel::initializeDatasetDecriptors", + "no duplicates allowed in mapping configuration, mapping has to be revertible" ); + + outSourceToProxyMap[inConfiguration[index]] = index; + + } +} + +void DatasetProxyModel::setSourceModel (QAbstractItemModel *m) +{ + if ( sourceModel() ) { + disconnect ( sourceModel(), SIGNAL ( layoutChanged() ), + this, SLOT( resetDatasetDescriptions() ) ); + } + QSortFilterProxyModel::setSourceModel ( m ); + mRootIndex = QModelIndex(); + if ( m ) { + connect ( m, SIGNAL ( layoutChanged() ), + this, SLOT( resetDatasetDescriptions() ) ); + } + resetDatasetDescriptions(); +} + +void DatasetProxyModel::setSourceRootIndex(const QModelIndex& rootIdx) +{ + mRootIndex = rootIdx; + resetDatasetDescriptions(); +} + diff --git a/libkdchart/src/KDChartDatasetProxyModel.h b/libkdchart/src/KDChartDatasetProxyModel.h new file mode 100644 index 0000000..a41b8eb --- /dev/null +++ b/libkdchart/src/KDChartDatasetProxyModel.h @@ -0,0 +1,188 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTDATASETPROXYMODEL_H +#define KDCHARTDATASETPROXYMODEL_H + +#include +#include + +#include "kdchart_export.h" + +namespace KDChart { + + class IndexOutOfBoundsException; + + typedef QVector DatasetDescriptionVector; + + /** DatasetProxyModel takes a KDChart dataset configuration and translates + it into a filtering proxy model. + + The resulting model will only contain the part of the model that is + selected by the dataset, and the according row and column header + data. + + Currently, this model is implemented for table models only. The way it + would work with models representing a tree is to be decided. + + The column selection is configured by passing a dataset description + vector to the model. This vector (of integers) is supposed to have one + value for each column of the original model. If the value at position + x is -1, column x of the original model is not included in the + dataset. If it is between 0 and (columnCount() -1), it is the column + the source column is mapped to in the resulting model. Any other value + is an error. + */ + class KDCHART_EXPORT DatasetProxyModel : public QSortFilterProxyModel + { + Q_OBJECT + public: + /** Create a DatasetProxyModel. + Without further configuration, this model is invalid. + @see setDatasetDescriptionVector + */ + explicit DatasetProxyModel ( QObject* parent = 0 ); + + QModelIndex buddy( const QModelIndex& index ) const; + + Qt::ItemFlags flags( const QModelIndex& index ) const; + + QModelIndex index( int row, int column, + const QModelIndex &parent = QModelIndex() ) const; + QModelIndex parent(const QModelIndex &child ) const; + + /** Implements the mapping from the source to the proxy indexes. */ + QModelIndex mapFromSource ( const QModelIndex & sourceIndex ) const; + + /** Implements the mapping from the proxy to the source indexes. */ + QModelIndex mapToSource ( const QModelIndex& proxyIndex ) const; + + /** Overloaded from base class. */ + QVariant data(const QModelIndex &index, int role) const; + + /** Overloaded from base class. */ + bool setData( const QModelIndex& index, const QVariant& value, int role ); + + /** Overloaded from base class. */ + QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + + /** Overloaded from base class. */ + void setSourceModel(QAbstractItemModel *sourceModel); + + /** Set the root index of the table in + the source model */ + void setSourceRootIndex(const QModelIndex& rootIdx); + + + public Q_SLOTS: + /** Reset all dataset description. + After that, the result of the proxying is an empty model (a new + dataset description needs to be set to achieve a non-empty result). + */ + void resetDatasetDescriptions(); + + /** Configure the dataset selection for the columns. + Every call to this method resets the previous dataset + description. + */ + void setDatasetColumnDescriptionVector ( const DatasetDescriptionVector& columnConfig ); + + /** Configure the dataset selection for the rows. + Every call to this method resets the previous dataset + description. + */ + void setDatasetRowDescriptionVector ( const DatasetDescriptionVector& rowConfig ); + + /** Convenience method to configure rows and columns in one step. */ + void setDatasetDescriptionVectors ( + const DatasetDescriptionVector& rowConfig, + const DatasetDescriptionVector& columnConfig ); + + // FIXME: add convenience methods to configure common dataset + // selections (like rectangular areas etc) + + protected: + /** Decide whether the column is accepted. */ + bool filterAcceptsColumn ( int sourceColumn, + const QModelIndex & ) const; + + + /** Decide whether the row is accepted. */ + bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const; + + private: + + /** Map a proxy column to a source column. */ + int mapProxyColumnToSource ( const int& proxyColumn ) const; + + /** Map a source column to a proxy column. */ + int mapSourceColumnToProxy ( const int& sourceColumn ) const; + + /** Map a proxy row to a source row. */ + int mapProxyRowToSource ( const int& proxyRow ) const; + + /** Map a source row to a proxy row. */ + int mapSourceRowToProxy ( const int& sourceRow ) const; + + /** Initialize the transformation vectors from the dataset + description. + + The input parameter "Configuration" is a vector that specifies + what srce column will be mapped to what proxy column. Example: + + position: [0][1][2] + value: [2][0][1] + + This will map the source column 2 to proxy column 0, source 0 to + proxy 1, and source 1 to proxy 2. Source needs to have at least 2 + column. The source-to-proxy mapping looks the same, except that it + may contain values of -1, which means this column is not part of + the resulting model. The values in the configuration vector must + be unique (otherwise, a 1-to-1 mapping in both directions is + impossible). + + sourceCount is the number of columns in the source model. The proxy-to-source map has + as many elements as the proxy has columns, the source-to-proxy map + has as many elements as the source has columns. Same goes for rows + (the mapping logic is the same). + + */ + void initializeDatasetDecriptors ( + const DatasetDescriptionVector& inConfiguration, + int sourceCount, + DatasetDescriptionVector& outSourceToProxyMap, + DatasetDescriptionVector& outProxyToSourceMap ); + + DatasetDescriptionVector mColSrcToProxyMap; + DatasetDescriptionVector mColProxyToSrcMap; + DatasetDescriptionVector mRowSrcToProxyMap; + DatasetDescriptionVector mRowProxyToSrcMap; + + int mProxyRowCount; + int mProxyColumnCount; + QModelIndex mRootIndex; + }; + +} + + +#endif diff --git a/libkdchart/src/KDChartDatasetSelector.cpp b/libkdchart/src/KDChartDatasetSelector.cpp new file mode 100644 index 0000000..92c7614 --- /dev/null +++ b/libkdchart/src/KDChartDatasetSelector.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include + +#include "KDChartDatasetSelector.h" + +#include "ui_KDChartDatasetSelector.h" + +#include + +using namespace KDChart; + +DatasetSelectorWidget::DatasetSelectorWidget ( QWidget* parent ) + : QFrame ( parent ) + , mUi ( new Ui::DatasetSelector () ) + , mSourceRowCount ( 0 ) + , mSourceColumnCount ( 0 ) +{ + qWarning("For DatasetSelectorWidget to become useful, it has to be connected to the proxy model it configures!"); + + mUi->setupUi ( this ); + setMinimumSize ( minimumSizeHint() ); +} + +void DatasetSelectorWidget::on_sbStartColumn_valueChanged ( int ) +{ + calculateMapping(); +} + +void DatasetSelectorWidget::on_sbStartRow_valueChanged( int ) +{ + calculateMapping(); +} + +void DatasetSelectorWidget::on_sbColumnCount_valueChanged ( int ) +{ + calculateMapping(); +} + +void DatasetSelectorWidget::on_sbRowCount_valueChanged ( int ) +{ + calculateMapping(); +} + +void DatasetSelectorWidget::on_cbReverseRows_stateChanged ( int ) +{ + calculateMapping(); +} + +void DatasetSelectorWidget::on_cbReverseColumns_stateChanged ( int ) +{ + calculateMapping(); +} + +void DatasetSelectorWidget::on_groupBox_toggled( bool state ) +{ + if ( state ) + { + calculateMapping(); + } else { + emit mappingDisabled(); + } +} + + +void DatasetSelectorWidget::setSourceRowCount ( const int& rowCount ) +{ + if ( rowCount != mSourceRowCount ) + { + mSourceRowCount = rowCount; + resetDisplayValues(); + } +} + +void DatasetSelectorWidget::setSourceColumnCount ( const int& columnCount ) +{ + if ( columnCount != mSourceColumnCount ) + { + mSourceColumnCount = columnCount; + resetDisplayValues(); + } +} + +void DatasetSelectorWidget::resetDisplayValues() +{ + mUi->sbStartRow->setValue( 0 ); + mUi->sbStartRow->setMinimum ( 0 ); + mUi->sbStartRow->setMaximum ( qMax ( mSourceRowCount - 1, 0 ) ); + mUi->sbStartColumn->setValue( 0 ); + mUi->sbStartColumn->setMinimum ( 0 ); + mUi->sbStartColumn->setMaximum ( qMax ( mSourceColumnCount - 1, 0 ) ); + mUi->sbRowCount->setMinimum ( 1 ); + mUi->sbRowCount->setMaximum ( mSourceRowCount ); + mUi->sbRowCount->setValue( mSourceRowCount ); + mUi->sbColumnCount->setMinimum ( 1 ); + mUi->sbColumnCount->setMaximum ( mSourceColumnCount ); + mUi->sbColumnCount->setValue( mSourceColumnCount ); + mUi->groupBox->setChecked ( false ); + emit mappingDisabled(); +} + +void DatasetSelectorWidget::calculateMapping() +{ + if ( mSourceColumnCount < 2 && mSourceRowCount < 2 ) + { + mUi->groupBox->setEnabled ( false ); + emit mappingDisabled(); + } else { + mUi->groupBox->setEnabled ( true ); + + if ( ! mUi->groupBox->isChecked() ) + { + emit mappingDisabled(); + return; + } + + // retrieve values: + int startRow = mUi->sbStartRow->value(); + int startColumn = mUi->sbStartColumn->value(); + int rowCount = mUi->sbRowCount->value(); + int columnCount = mUi->sbColumnCount->value(); + bool reverseColumns = mUi->cbReverseColumns->checkState() == Qt::Checked; + bool reverseRows = mUi->cbReverseRows->checkState() == Qt::Checked; + + // verify values: + startRow = qMin ( startRow, mSourceRowCount - 2 ); + startRow = qMax ( 0, startRow ); + startColumn = qMin ( startColumn, mSourceColumnCount - 2 ); + startColumn = qMax ( 0, startColumn ); + + rowCount = qMin ( rowCount, mSourceRowCount - startRow ); + rowCount = qMax ( 1, rowCount ); + columnCount = qMin ( columnCount, mSourceColumnCount - startColumn ); + columnCount = qMax ( 1, columnCount ); + + DatasetDescriptionVector rowConfig ( rowCount ); + Q_ASSERT ( rowConfig.size() > 0 ); + DatasetDescriptionVector columnConfig ( columnCount ); + Q_ASSERT ( columnConfig.size() > 0 ); + + // fill the dataset description vectors: + for ( int row = 0; row < rowCount; ++row ) + { + if ( reverseRows ) + { + rowConfig[row] = startRow + rowCount - row - 1; + } else { + rowConfig[row] = startRow + row; + } + } + + for ( int column = 0; column < columnCount; ++ column ) + { + if ( reverseColumns ) + { + columnConfig[column] = startColumn + columnCount - column -1; + } else { + columnConfig[column] = startColumn + column; + } + } + + // and tell the world: + emit configureDatasetProxyModel ( rowConfig, columnConfig ); + } +} + diff --git a/libkdchart/src/KDChartDatasetSelector.h b/libkdchart/src/KDChartDatasetSelector.h new file mode 100644 index 0000000..ebe3afe --- /dev/null +++ b/libkdchart/src/KDChartDatasetSelector.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTDATASETSELECTOR_H +#define KDCHARTDATASETSELECTOR_H + +#include + +#include "KDChartDatasetProxyModel.h" + + +/** + * \cond PRIVATE_API_DOCU + * + * ( This class is used internally by DatasetSelectorWidget. ) + */ + +namespace Ui { + class DatasetSelector; +} +/** + * \endcond + */ + +namespace KDChart { + + class KDCHART_EXPORT DatasetSelectorWidget : public QFrame + { + Q_OBJECT + + public: + explicit DatasetSelectorWidget ( QWidget* parent = 0 ); + + public Q_SLOTS: + void setSourceRowCount ( const int& rowCount ); + void setSourceColumnCount ( const int& columnCount ); + + Q_SIGNALS: + void configureDatasetProxyModel ( + const DatasetDescriptionVector& rowConfig, + const DatasetDescriptionVector& columnConfig ); + + void mappingDisabled (); + + private Q_SLOTS: + void on_sbStartColumn_valueChanged ( int ); + void on_sbStartRow_valueChanged ( int ); + void on_sbColumnCount_valueChanged( int ); + void on_sbRowCount_valueChanged( int ); + void on_cbReverseRows_stateChanged ( int ); + void on_cbReverseColumns_stateChanged ( int ); + void on_groupBox_toggled ( bool ); + + + private: + void resetDisplayValues (); + void calculateMapping(); + + Ui::DatasetSelector* mUi; + int mSourceRowCount; + int mSourceColumnCount; + }; + +} + +#endif diff --git a/libkdchart/src/KDChartDatasetSelector.ui b/libkdchart/src/KDChartDatasetSelector.ui new file mode 100644 index 0000000..5c55d3e --- /dev/null +++ b/libkdchart/src/KDChartDatasetSelector.ui @@ -0,0 +1,164 @@ + + Mirko Boehm <mirko@kdab.com> + + + DatasetSelector + + + + 0 + 0 + 728 + 344 + + + + + 3 + 3 + 0 + 0 + + + + + 0 + 0 + + + + Data Selector + + + + 9 + + + 6 + + + + + Only display a subset of the model in the chart: + + + true + + + false + + + + 9 + + + 6 + + + + + in reverse order. + + + + + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + columns starting at column + + + Qt::AlignCenter + + + + + + + + + + Display + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + ... + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + rows starting at row + + + Qt::AlignCenter + + + + + + + in reverse order. + + + + + + + Display + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 169 + 31 + + + + + + + + + + + + + diff --git a/libkdchart/src/KDChartDiagramObserver.cpp b/libkdchart/src/KDChartDiagramObserver.cpp new file mode 100644 index 0000000..e825a1a --- /dev/null +++ b/libkdchart/src/KDChartDiagramObserver.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include +#include +#include + +#include + +#include + +using namespace KDChart; + +DiagramObserver::DiagramObserver( AbstractDiagram * diagram, QObject* parent ) + : QObject( parent ), m_diagram( diagram ) +{ + if ( m_diagram ) { + connect( m_diagram, SIGNAL(destroyed(QObject*)), SLOT(slotDestroyed(QObject*))); + connect( m_diagram, SIGNAL(aboutToBeDestroyed()), SLOT(slotAboutToBeDestroyed())); + connect( m_diagram, SIGNAL(modelsChanged()), SLOT(slotModelsChanged())); + } + init(); +} + +DiagramObserver::~DiagramObserver() +{ +} + +const AbstractDiagram* DiagramObserver::diagram() const +{ + return m_diagram; +} + +AbstractDiagram* DiagramObserver::diagram() +{ + return m_diagram; +} + + +void DiagramObserver::init() +{ + if ( !m_diagram ) + return; + + if ( m_model ) + disconnect(m_model); + + if ( m_attributesmodel ) + disconnect(m_attributesmodel); + + connect( m_diagram, SIGNAL(dataHidden()), SLOT(slotDataHidden()) ); + + if( m_diagram->model() ){ + connect( m_diagram->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), + SLOT(slotDataChanged(QModelIndex,QModelIndex))); + connect( m_diagram->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), + SLOT(slotDataChanged())); + connect( m_diagram->model(), SIGNAL(columnsInserted(QModelIndex,int,int)), + SLOT(slotDataChanged())); + connect( m_diagram->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), + SLOT(slotDataChanged())); + connect( m_diagram->model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), + SLOT(slotDataChanged())); + connect( m_diagram->model(), SIGNAL(modelReset()), + SLOT(slotDataChanged())); + connect( m_diagram->model(), SIGNAL(headerDataChanged(Qt::Orientation,int,int)), + SLOT(slotHeaderDataChanged(Qt::Orientation,int,int))); + } + + if( m_diagram->attributesModel() ) + connect( m_diagram->attributesModel(), SIGNAL(attributesChanged(QModelIndex,QModelIndex)), + SLOT(slotAttributesChanged(QModelIndex,QModelIndex))); + m_model = m_diagram->model(); + m_attributesmodel = m_diagram->attributesModel(); +} + + +void DiagramObserver::slotDestroyed(QObject*) +{ + //qDebug() << this << "emits signal\n" + // " emit diagramDestroyed(" << m_diagram << ")"; + AbstractDiagram* diag = m_diagram; + disconnect( m_diagram, 0, this, 0); + m_diagram = 0; + emit diagramDestroyed( diag ); +} + +void DiagramObserver::slotAboutToBeDestroyed() +{ + emit diagramAboutToBeDestroyed( m_diagram ); +} + +void DiagramObserver::slotModelsChanged() +{ + init(); + slotDataChanged(); + slotAttributesChanged(); +} + +void DiagramObserver::slotHeaderDataChanged(Qt::Orientation,int,int) +{ + //qDebug() << "DiagramObserver::slotHeaderDataChanged()"; + emit diagramDataChanged( m_diagram ); +} + +void DiagramObserver::slotDataChanged(QModelIndex,QModelIndex) +{ + slotDataChanged(); +} + +void DiagramObserver::slotDataChanged() +{ + //qDebug() << "DiagramObserver::slotDataChanged()"; + emit diagramDataChanged( m_diagram ); +} + +void DiagramObserver::slotDataHidden() +{ + //qDebug() << "DiagramObserver::slotDataHidden()"; + emit diagramDataHidden( m_diagram ); +} + +void DiagramObserver::slotAttributesChanged(QModelIndex,QModelIndex) +{ + slotAttributesChanged(); +} + +void DiagramObserver::slotAttributesChanged() +{ + //qDebug() << "DiagramObserver::slotAttributesChanged()"; + emit diagramAttributesChanged( m_diagram ); +} + diff --git a/libkdchart/src/KDChartDiagramObserver.h b/libkdchart/src/KDChartDiagramObserver.h new file mode 100644 index 0000000..f15232c --- /dev/null +++ b/libkdchart/src/KDChartDiagramObserver.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef __KDCHARTDIAGRAMOBSERVER_H_ +#define __KDCHARTDIAGRAMOBSERVER_H_ + +#include "KDChartGlobal.h" + +#include +#include +#include + +class QAbstractItemModel; + +namespace KDChart { + + class AbstractDiagram; + + /** + * \brief A DiagramObserver watches the associated diagram for + * changes and deletion and emits corresponsing signals. + */ + class KDCHART_EXPORT DiagramObserver : public QObject + { + Q_OBJECT + public: + /** + * Constructs a new observer observing the given diagram. + */ + explicit DiagramObserver( AbstractDiagram * diagram, QObject* parent = 0 ); + ~DiagramObserver(); + + const AbstractDiagram* diagram() const; + AbstractDiagram* diagram(); + + Q_SIGNALS: + /** This signal is emitted immediately before the diagram is + * being destroyed. */ + void diagramDestroyed( AbstractDiagram* diagram ); + /** Emitted when a diagram is being destroyed, but before its data is invalidated **/ + void diagramAboutToBeDestroyed( AbstractDiagram* diagram ); + /** This signal is emitted whenever the data of the diagram changes. */ + void diagramDataChanged( AbstractDiagram* diagram ); + /** This signal is emitted whenever any of the data of the diagram was set (un)hidden. */ + void diagramDataHidden( AbstractDiagram* diagram ); + /** This signal is emitted whenever the attributes of the diagram change. */ + void diagramAttributesChanged( AbstractDiagram* diagram ); + + private Q_SLOTS: + void slotDestroyed(QObject*); + void slotAboutToBeDestroyed(); + void slotHeaderDataChanged(Qt::Orientation,int,int); + void slotDataChanged(QModelIndex,QModelIndex); + void slotDataChanged(); + void slotDataHidden(); + void slotAttributesChanged(); + void slotAttributesChanged(QModelIndex,QModelIndex); + void slotModelsChanged(); + + private: + void init(); + + AbstractDiagram* m_diagram; + QPointer m_model; + QPointer m_attributesmodel; + }; +} + +#endif // KDChartDiagramObserver_H diff --git a/libkdchart/src/KDChartEnums.h b/libkdchart/src/KDChartEnums.h new file mode 100644 index 0000000..8d5123e --- /dev/null +++ b/libkdchart/src/KDChartEnums.h @@ -0,0 +1,349 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef __KDCHARTENUMS_H__ +#define __KDCHARTENUMS_H__ + +#include "KDChartGlobal.h" + +#include +#include +#include + +/** \file KDChartEnums.h + \brief Definition of global enums. + */ + +/** + Project global class providing some enums needed both by KDChartParams + and by KDChartCustomBox. + */ +class KDCHART_EXPORT KDChartEnums :public QObject +{ + Q_OBJECT + Q_ENUMS( TextLayoutPolicy ) + Q_ENUMS( AreaName ) + Q_ENUMS( PositionFlag ) + +public: + /** + GranularitySequence specifies the values, that may be applied, + to determine a step width within a given data range. + + \note Granularity with can be set for Linear axis calculation mode only, + there is no way to specify a step width for Logarithmic axes. + + Value occurring in the GranularitySequence names only are showing + their respective relation ship. For real data they will most times not + be used directly, but be multiplied by positive (or negative, resp.) + powers of ten. + + A granularity sequence is a sequence of values from the following set: + 1, 1.25, 2, 2.5, 5. + + The reason for using one of the following three pre-defined granularity + sequences (instead of just using the best matching step width) is to + follow a simple rule: If scaling becomes finer (== smaller step width) + no value, that has been on a grid line before, shall loose its line + and be NOT on a grid line anymore! + + This means: Smaller step width may not remove any grid lines, but it + may add additional lines in between. + + \li \c GranularitySequence_10_20 Step widths can be 1, or 2, but they never can be 2.5 nor 5, nor 1.25. + \li \c GranularitySequence_10_50 Step widths can be 1, or 5, but they never can be 2, nor 2.5, nor 1.25. + \li \c GranularitySequence_25_50 Step widths can be 2.5, or 5, but they never can be 1, nor 2, nor 1.25. + \li \c GranularitySequence_125_25 Step widths can be 1.25 or 2.5 but they never can be 1, nor 2, nor 5. + \li \c GranularitySequenceIrregular Step widths can be all of these values: 1, or 1.25, or 2, or 2.5, or 5. + + \note When ever possible, try to avoid using GranularitySequenceIrregular! + Allowing all possible step values, using this granularity sequence involves a + serious risk: Your users might be irritated due to 'jumping' grid lines, when step size + is changed from 2.5 to 2 (or vice versa, resp.). + In case you still want to use GranularitySequenceIrregular just make sure to NOT draw + any sub-grid lines, because in most cases you will get not-matching step widths for + the sub-grid. + In short: GranularitySequenceIrregular can safely be used if your data range is not + changing at all AND (b) you will not allow the coordinate plane to be zoomed + AND (c) you are not displaying any sub-grid lines. + + Since you probably like having the value 1 as an allowed step width, + the granularity sequence decision boils down to a boolean question: + \li To get ten divided by five you use GranularitySequence_10_20, while + \li for having it divided by two GranularitySequence_10_50 is your choice. + + */ + enum GranularitySequence { + GranularitySequence_10_20, + GranularitySequence_10_50, + GranularitySequence_25_50, + GranularitySequence_125_25, + GranularitySequenceIrregular }; + + /** + Converts the specified granularity sequence enum to a + string representation. + + \param sequence the granularity sequence enum to convert + \return the string representation of the granularity sequence + */ + static QString granularitySequenceToString( GranularitySequence sequence ) { + switch( sequence ) { + case GranularitySequence_10_20: + return QString::fromLatin1("GranularitySequence_10_20"); + case GranularitySequence_10_50: + return QString::fromLatin1("GranularitySequence_10_50"); + case GranularitySequence_25_50: + return QString::fromLatin1("GranularitySequence_25_50"); + case GranularitySequence_125_25: + return QString::fromLatin1("GranularitySequence_125_25"); + case GranularitySequenceIrregular: + return QString::fromLatin1("GranularitySequenceIrregular"); + default: // should not happen + qDebug( "Unknown granularity sequence" ); + return QString::fromLatin1("GranularitySequence_10_20"); + } + } + + + /** + Converts the specified string to a granularity sequence enum value. + + \param string the string to convert + \return the granularity sequence enum value + */ + static GranularitySequence stringToGranularitySequence( const QString& string ) { + if( string == QString::fromLatin1("GranularitySequence_10_20") ) + return GranularitySequence_10_20; + if( string == QString::fromLatin1("GranularitySequence_10_50") ) + return GranularitySequence_10_50; + if( string == QString::fromLatin1("GranularitySequence_25_50") ) + return GranularitySequence_25_50; + if( string == QString::fromLatin1("GranularitySequence_125") ) + return GranularitySequence_125_25; + if( string == QString::fromLatin1("GranularitySequenceIrregular") ) + return GranularitySequenceIrregular; + // default, should not happen + return GranularitySequence_10_20; + } + + + /** + Text layout policy: what to do if text that is to be drawn would + cover neighboring text or neighboring areas. + + \li \c LayoutJustOverwrite Just ignore the layout collision and write the text nevertheless. + \li \c LayoutPolicyRotate Try counter-clockwise rotation to make the text fit into the space. + \li \c LayoutPolicyShiftVertically Shift the text baseline upwards (or downwards, resp.) and draw a connector line between the text and its anchor. + \li \c LayoutPolicyShiftHorizontally Shift the text baseline to the left (or to the right, resp.) and draw a connector line between the text and its anchor. + \li \c LayoutPolicyShrinkFontSize Reduce the text font size. + + \sa KDChartParams::setPrintDataValues + */ + enum TextLayoutPolicy { LayoutJustOverwrite, + LayoutPolicyRotate, + LayoutPolicyShiftVertically, + LayoutPolicyShiftHorizontally, + LayoutPolicyShrinkFontSize }; + + /** + Converts the specified text layout policy enum to a + string representation. + + \param type the text layout policy to convert + \return the string representation of the text layout policy enum + */ + static QString layoutPolicyToString( TextLayoutPolicy type ); + + + /** + Converts the specified string to a text layout policy enum value. + + \param string the string to convert + \return the text layout policy enum value + */ + static TextLayoutPolicy stringToLayoutPolicy( const QString& string ); + + + /** + Numerical values of the static KDChart::Position instances, + for using a Position::value() with a switch() statement. + + \sa Position + */ + enum PositionValue { + PositionUnknown = 0, + PositionCenter = 1, + PositionNorthWest = 2, + PositionNorth = 3, + PositionNorthEast = 4, + PositionEast = 5, + PositionSouthEast = 6, + PositionSouth = 7, + PositionSouthWest = 8, + PositionWest = 9, + PositionFloating =10 + }; + + + /** + Measure calculation mode: the way how the absolute value of a KDChart::Measure is determined during KD Chart's internal geometry calculation time. + + KDChart::Measure values either are relative (calculated in relation to a given AbstractArea), or they are absolute (used as fixed values). + + Values stored in relative measure always are interpreted as per-mille of a reference area's height (or width, resp.) depending on the orientation set for the KDChart::Measure. + + \li \c MeasureCalculationModeAbsolute Value set by setValue() is absolute, to be used unchanged. + \li \c MeasureCalculationModeRelative Value is relative, the reference area is specified by setReferenceArea(), and orientation specified by setOrientation(). + \li \c MeasureCalculationModeAuto Value is relative, KD Chart will automatically determine which reference area to use, and it will determine the orientation too. + \li \c MeasureCalculationModeAutoArea Value is relative, Orientation is specified by setOrientation(), and KD Chart will automatically determine which reference area to use. + \li \c MeasureCalculationModeAutoOrientation Value is relative, Area is specified by setReferenceArea(), and KD Chart will automatically determine which orientation to use. + + \sa KDChart::Measure::setCalculationMode + */ + enum MeasureCalculationMode { MeasureCalculationModeAbsolute, + MeasureCalculationModeRelative, + MeasureCalculationModeAuto, + MeasureCalculationModeAutoArea, + MeasureCalculationModeAutoOrientation }; + + /** + Converts the specified measure calculation mode enum to a + string representation. + + \param mode the measure calculation mode to convert + \return the string representation of the Measure calculation mode enum + */ + static QString measureCalculationModeToString( MeasureCalculationMode mode ) { + switch( mode ) { + case MeasureCalculationModeAbsolute: + return QString::fromLatin1("MeasureCalculationModeAbsolute"); + case MeasureCalculationModeAuto: + return QString::fromLatin1("MeasureCalculationModeAuto"); + case MeasureCalculationModeAutoArea: + return QString::fromLatin1("MeasureCalculationModeAutoArea"); + case MeasureCalculationModeAutoOrientation: + return QString::fromLatin1("MeasureCalculationModeAutoOrientation"); + case MeasureCalculationModeRelative: + return QString::fromLatin1("MeasureCalculationModeRelative"); + default: // should not happen + qDebug( "Unknown measure calculation mode" ); + return QString::fromLatin1("MeasureCalculationModeAuto"); + } + } + + + /** + Converts the specified string to a measure calculation mode enum value. + + \param string the string to convert + \return the measure calculation mode enum value + */ + static MeasureCalculationMode stringToMeasureCalculationMode( const QString& string ) { + if( string == QString::fromLatin1("MeasureCalculationModeAbsolute") ) + return MeasureCalculationModeAbsolute; + if( string == QString::fromLatin1("MeasureCalculationModeAuto") ) + return MeasureCalculationModeAuto; + if( string == QString::fromLatin1("MeasureCalculationModeAutoArea") ) + return MeasureCalculationModeAutoArea; + if( string == QString::fromLatin1("MeasureCalculationModeAutoOrientation") ) + return MeasureCalculationModeAutoOrientation; + if( string == QString::fromLatin1("MeasureCalculationModeRelative") ) + return MeasureCalculationModeRelative; + // default, should not happen + return MeasureCalculationModeAuto; + } + + /** + Measure orientation mode: the way how the absolute value of a KDChart::Measure is determined during KD Chart's internal geometry calculation time. + + KDChart::Measure values either are relative (calculated in relation to a given AbstractArea), or they are absolute (used as fixed values). + + Values stored in relative measure take into account the width (and/or the height, resp.) of a so-called reference area, + that is either specified by KDChart::Measure::setReferenceArea, or determined by KD Chart automatically, respectively. + + \li \c MeasureOrientationAuto Value is calculated, based upon the width (or on the height, resp.) of the reference area: KD Chart will automatically determie an appropriate way. + \li \c MeasureOrientationHorizontal Value is calculated, based upon the width of the reference area. + \li \c MeasureOrientationVertical Value is calculated, based upon the height of the reference area. + \li \c MeasureOrientationMinimum Value is calculated, based upon the width (or on the height, resp.) of the reference area - which ever is smaller. + \li \c MeasureOrientationMaximum Value is calculated, based upon the width (or on the height, resp.) of the reference area - which ever is smaller. + + \sa KDChart::Measure::setOrientationMode + */ + enum MeasureOrientation { MeasureOrientationAuto, + MeasureOrientationHorizontal, + MeasureOrientationVertical, + MeasureOrientationMinimum, + MeasureOrientationMaximum }; + + /** + Converts the specified measure orientation enum to a + string representation. + + \param mode the measure orientation to convert + \return the string representation of the measure orientation enum + */ + static QString measureOrientationToString( MeasureOrientation mode ) { + switch( mode ) { + case MeasureOrientationAuto: + return QString::fromLatin1("MeasureOrientationAuto"); + case MeasureOrientationHorizontal: + return QString::fromLatin1("MeasureOrientationHorizontal"); + case MeasureOrientationVertical: + return QString::fromLatin1("MeasureOrientationVertical"); + case MeasureOrientationMinimum: + return QString::fromLatin1("MeasureOrientationMinimum"); + case MeasureOrientationMaximum: + return QString::fromLatin1("MeasureOrientationMaximum"); + default: // should not happen + qDebug( "Unknown measure orientation mode" ); + return QString::fromLatin1("MeasureOrientationAuto"); + } + } + + + /** + Converts the specified string to a measure orientation enum value. + + \param string the string to convert + \return the measure orientation enum value + */ + static MeasureOrientation stringToMeasureOrientation( const QString& string ) { + if( string == QString::fromLatin1("MeasureOrientationAuto") ) + return MeasureOrientationAuto; + if( string == QString::fromLatin1("MeasureOrientationHorizontal") ) + return MeasureOrientationHorizontal; + if( string == QString::fromLatin1("MeasureOrientationVertical") ) + return MeasureOrientationVertical; + if( string == QString::fromLatin1("MeasureOrientationMinimum") ) + return MeasureOrientationMinimum; + if( string == QString::fromLatin1("MeasureOrientationMaximum") ) + return MeasureOrientationMaximum; + // default, should not happen + return MeasureOrientationAuto; + } + + +}; + + +#endif diff --git a/libkdchart/src/KDChartFrameAttributes.cpp b/libkdchart/src/KDChartFrameAttributes.cpp new file mode 100644 index 0000000..5c059d9 --- /dev/null +++ b/libkdchart/src/KDChartFrameAttributes.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartFrameAttributes.h" + +#include + +#define d d_func() + +using namespace KDChart; + +class FrameAttributes::Private +{ + friend class FrameAttributes; +public: + Private(); +private: + bool visible; + QPen pen; + int padding; +}; + +FrameAttributes::Private::Private() : + visible( false ), + padding( 0 ) +{ +} + + +FrameAttributes::FrameAttributes() + : _d( new Private() ) +{ +} + +FrameAttributes::FrameAttributes( const FrameAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +FrameAttributes & FrameAttributes::operator=( const FrameAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +FrameAttributes::~FrameAttributes() +{ + delete _d; _d = 0; +} + + +bool FrameAttributes::operator==( const FrameAttributes& r ) const +{ + /* + qDebug() << "FrameAttributes:" << (isVisible() == r.isVisible()) + << (pen() == r.pen()) + << (padding() == r.padding()) << "\n"; + */ + return ( isVisible() == r.isVisible() && + pen() == r.pen() && + padding() == r.padding() ); +} + + + + +void FrameAttributes::setVisible( bool visible ) +{ + d->visible = visible; +} + +bool FrameAttributes::isVisible() const +{ + return d->visible; +} + +void FrameAttributes::setPen( const QPen & pen ) +{ + d->pen = pen; +} + +QPen FrameAttributes::pen() const +{ + return d->pen; +} + +void FrameAttributes::setPadding( int padding ) +{ + d->padding = padding; +} + +int FrameAttributes::padding() const +{ + return d->padding; +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::FrameAttributes& fa) +{ + dbg << "KDChart::FrameAttributes(" + << "visible="< +#include +#include +#include "KDChartGlobal.h" + +namespace KDChart { + +/** + * @brief A set of attributes for frames around items + */ +class KDCHART_EXPORT FrameAttributes +{ +public: + FrameAttributes(); + FrameAttributes( const FrameAttributes& ); + FrameAttributes &operator= ( const FrameAttributes& ); + + ~FrameAttributes(); + + void setVisible( bool visible ); + bool isVisible() const; + + void setPen( const QPen & pen ); + QPen pen() const; + + void setPadding( int padding ); + int padding() const; + + bool operator==( const FrameAttributes& ) const; + inline bool operator!=( const FrameAttributes& other ) const { return !operator==(other); } + +private: + + KDCHART_DECLARE_PRIVATE_BASE_VALUE( FrameAttributes ) +}; // End of class FrameAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::FrameAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +Q_DECLARE_METATYPE( KDChart::FrameAttributes ) +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::FrameAttributes ) +Q_DECLARE_TYPEINFO( KDChart::FrameAttributes, Q_MOVABLE_TYPE ); + +#endif // KDCHARTFRAMEATTRIBUTES_H diff --git a/libkdchart/src/KDChartGlobal.h b/libkdchart/src/KDChartGlobal.h new file mode 100644 index 0000000..2796689 --- /dev/null +++ b/libkdchart/src/KDChartGlobal.h @@ -0,0 +1,260 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef __KDCHARTGLOBAL_H__ +#define __KDCHARTGLOBAL_H__ + +#include + +#include "kdchart_export.h" + +#ifndef KDAB_SET_OBJECT_NAME +template +inline T & __kdab__dereference_for_methodcall( T & o ) { + return o; +} + +template +inline T & __kdab__dereference_for_methodcall( T * o ) { + return *o; +} + +#define KDAB_SET_OBJECT_NAME( x ) __kdab__dereference_for_methodcall( x ).setObjectName( QLatin1String( #x ) ) +#endif + +/* vc.net2002 is 1300, vc.net2003 is 1310 */ +#if defined(_MSC_VER) && _MSC_VER <= 1300 +#define KDCHART_DECLARE_PRIVATE_DERIVED( X ) \ +public: \ + class Private; \ +protected: \ + inline Private * d_func(); \ + inline const Private * d_func() const; \ + explicit inline X( Private * ); \ +private: \ + void init(); +#else +#define KDCHART_DECLARE_PRIVATE_DERIVED( X ) \ +protected: \ + class Private; \ + inline Private * d_func(); \ + inline const Private * d_func() const; \ + explicit inline X( Private * ); \ +private: \ + void init(); +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1300 +#define KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( X, ParentType ) \ +public: \ + class Private; \ +protected: \ + inline Private * d_func(); \ + inline const Private * d_func() const; \ + explicit inline X( Private *, ParentType ); \ +private: \ + void init(); +#else +#define KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( X, ParentType ) \ +protected: \ + class Private; \ + inline Private * d_func(); \ + inline const Private * d_func() const; \ + explicit inline X( Private *, ParentType ); \ +private: \ + void init(); +#endif + +#define KDCHART_DECLARE_PRIVATE_DERIVED_QWIDGET( X ) \ + KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( X, QWidget* ) + +#define KDCHART_DECLARE_PRIVATE_BASE_VALUE( X ) \ +public: \ + inline void swap( X & other ) { qSwap( _d, other._d ); } \ +protected: \ + class Private; \ + Private * d_func() { return _d; } \ + const Private * d_func() const { return _d; } \ +private: \ + void init(); \ + Private * _d; + +#if defined(_MSC_VER) && _MSC_VER <= 1300 +#define KDCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC( X ) \ +public: \ + class Private; \ +protected: \ + Private * d_func() { return _d; } \ + const Private * d_func() const { return _d; } \ + explicit inline X( Private * ); \ +private: \ + void init(); \ + Private * _d; +#else +#define KDCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC( X ) \ +protected: \ + class Private; \ + Private * d_func() { return _d; } \ + const Private * d_func() const { return _d; } \ + explicit inline X( Private * ); \ +private: \ + void init(); \ + Private * _d; +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1300 +#define KDCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC_QWIDGET( X ) \ +public: \ + class Private; \ +protected: \ + Private * d_func() { return _d; } \ + const Private * d_func() const { return _d; } \ + explicit inline X( Private *, QWidget* ); \ +private: \ + void init(); \ + Private * _d; +#else +#define KDCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC_QWIDGET( X ) \ +protected: \ + class Private; \ + Private * d_func() { return _d; } \ + const Private * d_func() const { return _d; } \ + explicit inline X( Private *, QWidget* ); \ +private: \ + void init(); \ + Private * _d; +#endif + + +#define KDCHART_DERIVED_PRIVATE_FOOTER( CLASS, PARENT ) \ +inline CLASS::CLASS( Private * p ) \ + : PARENT( p ) { init(); } \ +inline CLASS::Private * CLASS::d_func() \ +{ return static_cast( PARENT::d_func() ); } \ +inline const CLASS::Private * CLASS::d_func() const \ +{ return static_cast( PARENT::d_func() ); } + + +#if defined(_MSC_VER) && _MSC_VER <= 1300 +#define KDCHART_DECLARE_DERIVED_DIAGRAM( X, PLANE ) \ +public: \ + class Private; \ +protected: \ + inline Private * d_func(); \ + inline const Private * d_func() const; \ + explicit inline X( Private * ); \ + explicit inline X( Private *, QWidget *, PLANE * ); \ +private: \ + void init(); +#else +#define KDCHART_DECLARE_DERIVED_DIAGRAM( X, PLANE ) \ +protected: \ + class Private; \ + inline Private * d_func(); \ + inline const Private * d_func() const; \ + explicit inline X( Private * ); \ + explicit inline X( Private *, QWidget *, PLANE * ); \ +private: \ + void init(); +#endif + +#define KDCHART_IMPL_DERIVED_DIAGRAM( CLASS, PARENT, PLANE ) \ +inline CLASS::CLASS( Private * p ) \ + : PARENT( p ) { init(); } \ +inline CLASS::CLASS( \ + Private * p, QWidget* parent, PLANE * plane ) \ + : PARENT( p, parent, plane ) { init(); } \ +inline CLASS::Private * CLASS::d_func() \ + { return static_cast( PARENT::d_func() ); } \ +inline const CLASS::Private * CLASS::d_func() const \ + { return static_cast( PARENT::d_func() ); } + + +#define KDCHART_IMPL_DERIVED_PLANE( CLASS, BASEPLANE ) \ +inline CLASS::CLASS( Private * p, Chart* parent ) \ + : BASEPLANE( p, parent ) { init(); } \ +inline CLASS::Private * CLASS::d_func() \ + { return static_cast( BASEPLANE::d_func() ); } \ +inline const CLASS::Private * CLASS::d_func() const \ + { return static_cast( BASEPLANE::d_func() ); } + + +#include // qSwap +#ifndef QT_NO_STL +#include +#define KDCHART_DECLARE_SWAP_SPECIALISATION( X ) \ + template <> inline void qSwap( X & lhs, X & rhs ) \ + { lhs.swap( rhs ); } \ + namespace std { \ + template <> inline void swap( X & lhs, X & rhs ) \ + { lhs.swap( rhs ); } \ + } +#else +#define KDCHART_DECLARE_SWAP_SPECIALISATION( X ) \ + template <> inline void qSwap( X & lhs, X & rhs ) \ + { lhs.swap( rhs ); } +#endif + +#define KDCHART_DECLARE_SWAP_SPECIALISATION_DERIVED( X ) \ + KDCHART_DECLARE_SWAP_SPECIALISATION( X ) + +#define KDCHART_DECLARE_SWAP_BASE( X ) \ +protected: \ + void doSwap( X& other ) \ + { qSwap( _d, other._d); } + +#define KDCHART_DECLARE_SWAP_DERIVED( X ) \ + void swap( X& other ) { doSwap( other ); } + +#if defined(Q_OS_WIN) && defined(QT_DLL) +#if defined(_MSC_VER) && _MSC_VER >= 1300 +// workaround http://support.microsoft.com/default.aspx?scid=kb;en-us;309801 +#include +#include +template class Q_DECL_IMPORT QVector; +#endif +#endif + +#include + +namespace KDChart { + +enum DisplayRoles { + DatasetPenRole = 0x0A79EF95, + DatasetBrushRole, + DataValueLabelAttributesRole, + ThreeDAttributesRole, + LineAttributesRole, + ThreeDLineAttributesRole, + BarAttributesRole, + StockBarAttributesRole, + ThreeDBarAttributesRole, + PieAttributesRole, + ThreeDPieAttributesRole, + DataHiddenRole, + ValueTrackerAttributesRole, + CommentRole, + ColumnDataRole +}; +} + +#endif // __KDCHARTGLOBAL_H__ diff --git a/libkdchart/src/KDChartGridAttributes.cpp b/libkdchart/src/KDChartGridAttributes.cpp new file mode 100644 index 0000000..54ec114 --- /dev/null +++ b/libkdchart/src/KDChartGridAttributes.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartGridAttributes.h" + +#include +#include + +#include + +#define d d_func() + +using namespace KDChart; + +class GridAttributes::Private +{ + friend class GridAttributes; +public: + Private(); +private: + bool visible; + KDChartEnums::GranularitySequence sequence; + qreal stepWidth; + qreal subStepWidth; + bool adjustLower; + bool adjustUpper; + QPen pen; + bool subVisible; + QPen subPen; + QPen zeroPen; +}; + +GridAttributes::Private::Private() + : visible( true ), + sequence( KDChartEnums::GranularitySequence_10_20 ), + stepWidth( 0.0 ), + subStepWidth( 0.0 ), + adjustLower( true ), + adjustUpper( true ), + pen( QColor(0xa0, 0xa0, 0xa0 ) ), + subVisible( true ), + subPen( QColor(0xd0, 0xd0, 0xd0 ) ), + zeroPen( QColor( 0x00, 0x00, 0x80 ) ) +{ + pen.setCapStyle( Qt::FlatCap ); + subPen.setCapStyle( Qt::FlatCap ); + zeroPen.setCapStyle( Qt::FlatCap ); +} + + +GridAttributes::GridAttributes() + : _d( new Private() ) +{ + // this bloc left empty intentionally +} + +GridAttributes::GridAttributes( const GridAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +GridAttributes & GridAttributes::operator=( const GridAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +GridAttributes::~GridAttributes() +{ + delete _d; _d = 0; +} + + +bool GridAttributes::operator==( const GridAttributes& r ) const +{ + return isGridVisible() == r.isGridVisible() && + gridGranularitySequence() == r.gridGranularitySequence() && + adjustLowerBoundToGrid() == r.adjustLowerBoundToGrid() && + adjustUpperBoundToGrid() == r.adjustUpperBoundToGrid() && + gridPen() == r.gridPen() && + isSubGridVisible() == r.isSubGridVisible() && + subGridPen() == r.subGridPen() && + zeroLinePen() == r.zeroLinePen(); +} + + +void GridAttributes::setGridVisible( bool visible ) +{ + d->visible = visible; +} + +bool GridAttributes::isGridVisible() const +{ + return d->visible; +} + +/** + * Specifies the step width to be used for calculating + * the grid lines. + * + * \note Step with can be set for Linear axis calculation mode only, + * there is no way to specify a step width for Logarithmic axes. + * + * By default the GridAttributes class does not use a fixed step width, + * but it uses KDChartEnums::GranularitySequence_10_20. + * + * \param stepWidth the step width to be used. + * If this parameter is omitted (or set to Zero, resp.) + * the automatic step width calculation will be done, + * using the granularity sequence specified. + * This is the default. + * + * \sa gridStepWidth, setGranularitySequence + */ +void GridAttributes::setGridStepWidth( qreal stepWidth ) +{ + d->stepWidth = stepWidth; +} + +/** + * Returns the step width to be used for calculating + * the grid lines. + * + * \sa setGridStepWidth + */ +qreal GridAttributes::gridStepWidth() const +{ + return d->stepWidth; +} + + +/** + * Specifies the sub-step width to be used for calculating + * the grid sub-lines. + * + * + * \param subStepWidth the sub-step width to be used. + * If this parameter is omitted (or set to Zero, resp.) + * the automatic calculation will be done, using the + * granularity sequence specified. + * This is the default. + * + * \sa gridSubStepWidth + */ + +void GridAttributes::setGridSubStepWidth( qreal subStepWidth ) +{ + d->subStepWidth = subStepWidth; +} + +/** + * Returns the sub-step width to be used for calculating + * the sub-grid lines. + * + * \sa setGridStepWidth + */ +qreal GridAttributes::gridSubStepWidth() const +{ + return d->subStepWidth; +} + +/** + * Specifies the granularity sequence to be used for calculating + * the grid lines. + * + * By default the GridAttributes class uses KDChartEnums::GranularitySequence_10_20. + * + * \note Granularity can be set for Linear axis calculation mode only, + * there is no way to specify a step width for Logarithmic axes. + * + * \note The sequence specified by this method is ignored, if + * a fixed step width was specified via setStepWidth. + * + * \param sequence one of the sequences declared in + * KDChartEnums::GranularitySequence. + * + * \sa gridGranularitySequence, setStepWidth + */ +void GridAttributes::setGridGranularitySequence( KDChartEnums::GranularitySequence sequence ) +{ + d->sequence = sequence; +} + +/** + * Returns the granularity sequence to be used for calculating + * the grid lines. + * + * \sa setGridGranularitySequence + */ +KDChartEnums::GranularitySequence GridAttributes::gridGranularitySequence() const +{ + return d->sequence; +} + +void GridAttributes::setAdjustBoundsToGrid( bool adjustLower, bool adjustUpper ) +{ + d->adjustLower = adjustLower; + d->adjustUpper = adjustUpper; +} +bool GridAttributes::adjustLowerBoundToGrid() const +{ + return d->adjustLower; +} +bool GridAttributes::adjustUpperBoundToGrid() const +{ + return d->adjustUpper; +} + +void GridAttributes::setGridPen( const QPen & pen ) +{ + d->pen = pen; + d->pen.setCapStyle( Qt::FlatCap ); +} + +QPen GridAttributes::gridPen() const +{ + return d->pen; +} + +void GridAttributes::setSubGridVisible( bool visible ) +{ + d->subVisible = visible; +} + +bool GridAttributes::isSubGridVisible() const +{ + return d->subVisible; +} + +void GridAttributes::setSubGridPen( const QPen & pen ) +{ + d->subPen = pen; + d->subPen.setCapStyle( Qt::FlatCap ); +} + +QPen GridAttributes::subGridPen() const +{ + return d->subPen; +} + +void GridAttributes::setZeroLinePen( const QPen & pen ) +{ + d->zeroPen = pen; + d->zeroPen.setCapStyle( Qt::FlatCap ); +} + +QPen GridAttributes::zeroLinePen() const +{ + return d->zeroPen; +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::GridAttributes& a) +{ + dbg << "KDChart::GridAttributes(" + << "visible="< +#include "KDChartGlobal.h" +#include "KDChartEnums.h" + +class QPen; + +namespace KDChart { + +/** + * @brief A set of attributes controlling the appearance of grids + */ +class KDCHART_EXPORT GridAttributes +{ +public: + GridAttributes(); + GridAttributes( const GridAttributes& ); + GridAttributes &operator= ( const GridAttributes& ); + + ~GridAttributes(); + + void setGridVisible( bool visible ); + bool isGridVisible() const; + + + void setGridStepWidth( qreal stepWidth=0.0 ); + qreal gridStepWidth() const; + + void setGridSubStepWidth( qreal subStepWidth=0.0 ); + qreal gridSubStepWidth() const; + + /** + * Specify which granularity sequence is to be used to find a matching + * grid granularity. + * + * See details explained at KDChartEnums::GranularitySequence. + * + * You might also want to use setAdjustBoundsToGrid for fine-tuning the + * start/end value. + * + * \sa setAdjustBoundsToGrid, GranularitySequence + */ + void setGridGranularitySequence( KDChartEnums::GranularitySequence sequence ); + KDChartEnums::GranularitySequence gridGranularitySequence() const; + + /** + * By default visible bounds of the data area are adjusted to match + * a main grid line. + * If you set the respective adjust flag to false the bound will + * not start at a grid line's value but it will be the exact value + * of the data range set. + * + * \sa CartesianCoordinatePlane::setHorizontalRange + * \sa CartesianCoordinatePlane::setVerticalRange + */ + void setAdjustBoundsToGrid( bool adjustLower, bool adjustUpper ); + bool adjustLowerBoundToGrid() const; + bool adjustUpperBoundToGrid() const; + + + void setGridPen( const QPen & pen ); + QPen gridPen() const; + + + void setSubGridVisible( bool visible ); + bool isSubGridVisible() const; + + void setSubGridPen( const QPen & pen ); + QPen subGridPen() const; + + + void setZeroLinePen( const QPen & pen ); + QPen zeroLinePen() const; + + bool operator==( const GridAttributes& ) const; + inline bool operator!=( const GridAttributes& other ) const { return !operator==(other); } + +private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( GridAttributes ) +}; // End of class GridAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::GridAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::GridAttributes ) +Q_DECLARE_METATYPE( KDChart::GridAttributes ) +Q_DECLARE_TYPEINFO( KDChart::GridAttributes, Q_MOVABLE_TYPE ); + + +#endif // KDCHARTGRIDATTRIBUTES_H diff --git a/libkdchart/src/KDChartHeaderFooter.cpp b/libkdchart/src/KDChartHeaderFooter.cpp new file mode 100644 index 0000000..011f8f6 --- /dev/null +++ b/libkdchart/src/KDChartHeaderFooter.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartHeaderFooter.h" +#include "KDChartHeaderFooter_p.h" + +#include "KDChartChart.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "KDTextDocument.h" + +#include + +using namespace KDChart; + +HeaderFooter::Private::Private() : + type( Header ), + position( Position::North ) +{ +} + +HeaderFooter::Private::~Private() +{ +} + +#define d d_func() + +HeaderFooter::HeaderFooter( Chart* parent ) : + TextArea( new Private() ) +{ + setParent( parent ); + init(); +} + +HeaderFooter::~HeaderFooter() +{ + emit destroyedHeaderFooter( this ); +} + +void HeaderFooter::setParent( QObject* parent ) +{ + QObject::setParent( parent ); + setParentWidget( qobject_cast( parent ) ); + if( parent && ! autoReferenceArea() ) + setAutoReferenceArea( parent ); +} + +void HeaderFooter::init() +{ + TextAttributes ta; + ta.setPen( QPen(Qt::black) ); + ta.setFont( QFont( QLatin1String( "helvetica" ), 10, QFont::Bold, false ) ); + + Measure m( 35.0 ); + m.setRelativeMode( autoReferenceArea(), KDChartEnums::MeasureOrientationMinimum ); + ta.setFontSize( m ); + + m.setValue( 8.0 ); + m.setCalculationMode( KDChartEnums::MeasureCalculationModeAbsolute ); + ta.setMinimalFontSize( m ); + + setTextAttributes( ta ); +} + +/** + * Creates an exact copy of this header/footer. + */ +HeaderFooter * HeaderFooter::clone() const +{ + HeaderFooter* headerFooter = new HeaderFooter( new Private( *d ), 0 ); + headerFooter->setType( type() ); + headerFooter->setPosition( position() ); + headerFooter->setText( text() ); + headerFooter->setTextAttributes( textAttributes() ); + return headerFooter; +} + +bool HeaderFooter::compare( const HeaderFooter& other )const +{ + return (type() == other.type()) && + (position() == other.position()) && + // also compare members inherited from the base class: + (autoReferenceArea() == other.autoReferenceArea()) && + (text() == other.text()) && + (textAttributes() == other.textAttributes()); +} + +void HeaderFooter::setType( HeaderFooterType type ) +{ + d->type = type; + emit positionChanged( this ); +} + +HeaderFooter::HeaderFooterType HeaderFooter::type() const +{ + return d->type; +} + +void HeaderFooter::setPosition( Position position ) +{ + d->position = position; + emit positionChanged( this ); +} + +Position HeaderFooter::position() const +{ + return d->position; +} diff --git a/libkdchart/src/KDChartHeaderFooter.h b/libkdchart/src/KDChartHeaderFooter.h new file mode 100644 index 0000000..2ede070 --- /dev/null +++ b/libkdchart/src/KDChartHeaderFooter.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTHEADERFOOTER_H +#define KDCHARTHEADERFOOTER_H + +#include "KDChartTextArea.h" +#include "KDChartPosition.h" + +namespace KDChart { + + class Chart; + class TextAttributes; + +/** + * @brief A header or even footer displaying text above or below charts + */ +class KDCHART_EXPORT HeaderFooter : public TextArea +{ + Q_OBJECT + + Q_DISABLE_COPY( HeaderFooter ) + KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( HeaderFooter, Chart* ) + +public: + HeaderFooter( Chart* parent = 0 ); + virtual ~HeaderFooter(); + + virtual HeaderFooter * clone() const; + + bool compare( const HeaderFooter& other )const; + + enum HeaderFooterType{ Header, + Footer }; + + void setType( HeaderFooterType type ); + HeaderFooterType type() const; + + void setPosition( Position position ); + Position position() const; + + void setParent( QObject* parent ); + +Q_SIGNALS: + void destroyedHeaderFooter( HeaderFooter* ); + void positionChanged( HeaderFooter* ); + +}; // End of class HeaderFooter + +} + + +#endif // KDCHARTHEADERFOOTER_H diff --git a/libkdchart/src/KDChartHeaderFooter_p.h b/libkdchart/src/KDChartHeaderFooter_p.h new file mode 100644 index 0000000..9d27bd8 --- /dev/null +++ b/libkdchart/src/KDChartHeaderFooter_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTHEADERFOOTER_P_H +#define KDCHARTHEADERFOOTER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartHeaderFooter.h" +#include "KDChartTextArea_p.h" +#include "KDChartChart.h" +#include + +#include + + +class KDTextDocument; + +namespace KDChart { + class Chart; + class TextAttributes; +} + +/** + * \internal + */ +class KDChart::HeaderFooter::Private : public KDChart::TextArea::Private +{ + friend class KDChart::HeaderFooter; +public: + explicit Private(); + ~Private(); + + Private( const Private& rhs ) : + TextArea::Private( rhs ), + type( rhs.type ), + position( rhs.position ) + { + } + + void updateTextDoc(); + +private: + // user-settable + HeaderFooterType type; + Position position; +}; + +inline KDChart::HeaderFooter::HeaderFooter( Private* d, KDChart::Chart* parent ) + : TextArea( d ) +{ + setParent( parent ); + init(); +} +inline KDChart::HeaderFooter::Private * KDChart::HeaderFooter::d_func() +{ + return static_cast( TextArea::d_func() ); +} +inline const KDChart::HeaderFooter::Private * KDChart::HeaderFooter::d_func() const +{ + return static_cast( TextArea::d_func() ); +} + + +#endif /* KDCHARTHEADERFOOTER_P_H */ diff --git a/libkdchart/src/KDChartLayoutItems.cpp b/libkdchart/src/KDChartLayoutItems.cpp new file mode 100644 index 0000000..b323a59 --- /dev/null +++ b/libkdchart/src/KDChartLayoutItems.cpp @@ -0,0 +1,1156 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartLayoutItems.h" +#include "KDTextDocument.h" +#include "KDChartAbstractArea.h" +#include "KDChartAbstractDiagram.h" +#include "KDChartBackgroundAttributes.h" +#include "KDChartFrameAttributes.h" +#include "KDChartPaintContext.h" +#include "KDChartPainterSaver_p.h" +#include "KDChartPrintingParameters.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define PI 3.141592653589793 + + + +//#define DEBUG_ITEMS_PAINT + +/** + Inform the item about its widget: This enables the item, + to trigger that widget's update, whenever the size of the item's + contents has changed. + + Thus, you need to call setParentWidget on every item, that + has a non-fixed size. + */ +void KDChart::AbstractLayoutItem::setParentWidget( QWidget* widget ) +{ + mParent = widget; +} + +void KDChart::AbstractLayoutItem::paintAll( QPainter& painter ) +{ + paint( &painter ); +} + +/** + * Default impl: Paint the complete item using its layouted position and size. + */ +void KDChart::AbstractLayoutItem::paintCtx( PaintContext* context ) +{ + if( context ) + paint( context->painter() ); +} + +/** + Report changed size hint: ask the parent widget to recalculate the layout. + */ +void KDChart::AbstractLayoutItem::sizeHintChanged()const +{ + // This is exactly like what QWidget::updateGeometry does. +// qDebug("KDChart::AbstractLayoutItem::sizeHintChanged() called"); + if( mParent ) { + if ( mParent->layout() ) + mParent->layout()->invalidate(); + else + QApplication::postEvent( mParent, new QEvent( QEvent::LayoutRequest ) ); + } +} + +KDChart::TextBubbleLayoutItem::TextBubbleLayoutItem( const QString& text, + const KDChart::TextAttributes& attributes, + const QObject* area, + KDChartEnums::MeasureOrientation orientation, + Qt::Alignment alignment ) + : AbstractLayoutItem( alignment ), + m_text( new TextLayoutItem( text, attributes, area, orientation, alignment ) ) +{ +} + +KDChart::TextBubbleLayoutItem::TextBubbleLayoutItem() + : AbstractLayoutItem( Qt::AlignLeft ), + m_text( new TextLayoutItem() ) +{ +} + +KDChart::TextBubbleLayoutItem::~TextBubbleLayoutItem() +{ + delete m_text; +} + +void KDChart::TextBubbleLayoutItem::setAutoReferenceArea( const QObject* area ) +{ + m_text->setAutoReferenceArea( area ); +} + +const QObject* KDChart::TextBubbleLayoutItem::autoReferenceArea() const +{ + return m_text->autoReferenceArea(); +} + +void KDChart::TextBubbleLayoutItem::setText( const QString& text ) +{ + m_text->setText( text ); +} + +QString KDChart::TextBubbleLayoutItem::text() const +{ + return m_text->text(); +} + +void KDChart::TextBubbleLayoutItem::setTextAttributes( const TextAttributes& a ) +{ + m_text->setTextAttributes( a ); +} + +KDChart::TextAttributes KDChart::TextBubbleLayoutItem::textAttributes() const +{ + return m_text->textAttributes(); +} + +bool KDChart::TextBubbleLayoutItem::isEmpty() const +{ + return m_text->isEmpty(); +} + +Qt::Orientations KDChart::TextBubbleLayoutItem::expandingDirections() const +{ + return m_text->expandingDirections(); +} + +QSize KDChart::TextBubbleLayoutItem::maximumSize() const +{ + const int border = borderWidth(); + return m_text->maximumSize() + QSize( 2 * border, 2 * border ); +} + +QSize KDChart::TextBubbleLayoutItem::minimumSize() const +{ + const int border = borderWidth(); + return m_text->minimumSize() + QSize( 2 * border, 2 * border ); +} + +QSize KDChart::TextBubbleLayoutItem::sizeHint() const +{ + const int border = borderWidth(); + return m_text->sizeHint() + QSize( 2 * border, 2 * border ); +} + +void KDChart::TextBubbleLayoutItem::setGeometry( const QRect& r ) +{ + const int border = borderWidth(); + m_text->setGeometry( r.adjusted( border, border, -border, -border ) ); +} + +QRect KDChart::TextBubbleLayoutItem::geometry() const +{ + const int border = borderWidth(); + return m_text->geometry().adjusted( -border, -border, border, border ); +} + +void KDChart::TextBubbleLayoutItem::paint( QPainter* painter ) +{ + const QPen oldPen = painter->pen(); + const QBrush oldBrush = painter->brush(); + painter->setPen( Qt::black ); + painter->setBrush( QColor( 255, 255, 220 ) ); + painter->drawRoundRect( geometry(), 10 ); + painter->setPen( oldPen ); + painter->setBrush( oldBrush ); + m_text->paint( painter ); +} + +int KDChart::TextBubbleLayoutItem::borderWidth() const +{ + return 1; +} + +KDChart::TextLayoutItem::TextLayoutItem( const QString& text, + const KDChart::TextAttributes& attributes, + const QObject* area, + KDChartEnums::MeasureOrientation orientation, + Qt::Alignment alignment ) + : AbstractLayoutItem( alignment ) + , mText( text ) + , mTextAlignment( alignment ) + , mAttributes( attributes ) + , mAutoReferenceArea( area ) + , mAutoReferenceOrientation( orientation ) + , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() + , cachedFontSize( 0.0 ) + , cachedFont( mAttributes.font() ) +{ +} + +KDChart::TextLayoutItem::TextLayoutItem() + : AbstractLayoutItem( Qt::AlignLeft ) + , mText() + , mTextAlignment( Qt::AlignLeft ) + , mAttributes() + , mAutoReferenceArea( 0 ) + , mAutoReferenceOrientation( KDChartEnums::MeasureOrientationHorizontal ) + , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() + , cachedFontSize( 0.0 ) + , cachedFont( mAttributes.font() ) +{ + +} + +void KDChart::TextLayoutItem::setAutoReferenceArea( const QObject* area ) +{ + mAutoReferenceArea = area; + cachedSizeHint = QSize(); + sizeHint(); +} + +const QObject* KDChart::TextLayoutItem::autoReferenceArea() const +{ + return mAutoReferenceArea; +} + +void KDChart::TextLayoutItem::setText(const QString & text) +{ + mText = text; + cachedSizeHint = QSize(); + sizeHint(); + if( mParent ) + mParent->update(); +} + +QString KDChart::TextLayoutItem::text() const +{ + return mText; +} + +void KDChart::TextLayoutItem::setTextAlignment( Qt::Alignment alignment) +{ + if( mTextAlignment == alignment ) + return; + mTextAlignment = alignment; + if( mParent ) + mParent->update(); +} + +Qt::Alignment KDChart::TextLayoutItem::textAlignment() const +{ + return mTextAlignment; +} + +/** + \brief Use this to specify the text attributes to be used for this item. + + \sa textAttributes +*/ +void KDChart::TextLayoutItem::setTextAttributes( const TextAttributes &a ) +{ + mAttributes = a; + cachedFont = a.font(); + cachedSizeHint = QSize(); // invalidate size hint + sizeHint(); + if( mParent ) + mParent->update(); +} + +/** + Returns the text attributes to be used for this item. + + \sa setTextAttributes +*/ +KDChart::TextAttributes KDChart::TextLayoutItem::textAttributes() const +{ + return mAttributes; +} + + +Qt::Orientations KDChart::TextLayoutItem::expandingDirections() const +{ + return 0; // Grow neither vertically nor horizontally +} + +QRect KDChart::TextLayoutItem::geometry() const +{ + return mRect; +} + +bool KDChart::TextLayoutItem::isEmpty() const +{ + return false; // never empty, otherwise the layout item would not exist +} + +QSize KDChart::TextLayoutItem::maximumSize() const +{ + return sizeHint(); // PENDING(kalle) Review, quite inflexible +} + +QSize KDChart::TextLayoutItem::minimumSize() const +{ + return sizeHint(); // PENDING(kalle) Review, quite inflexible +} + +void KDChart::TextLayoutItem::setGeometry( const QRect& r ) +{ + mRect = r; +} + +QPointF rotatedPoint( const QPointF& pt, qreal rotation, const QPointF& center ) +{ + const qreal angle = PI * rotation / 180.0; + const qreal cosAngle = cos( angle ); + const qreal sinAngle = -sin( angle ); + return QPointF( + (cosAngle * ( pt.x() - center.x() ) + sinAngle * ( pt.y() - center.y() ) ), + (cosAngle * ( pt.y() - center.y() ) - sinAngle * ( pt.x() - center.x() ) ) ) + center; +} + + +QRectF rotatedRect( const QRectF& oldRect, qreal angleInt, const QPointF& center ) +{ + const QRect rect( oldRect.translated( center ).toRect() ); + const qreal angle = PI * angleInt / 180.0; + const qreal cosAngle = cos( angle ); + const qreal sinAngle = sin( angle ); + QMatrix rotationMatrix(cosAngle, sinAngle, -sinAngle, cosAngle, 0, 0); + QPolygon rotPts; + rotPts << rotationMatrix.map(rect.topLeft()) //QPoint(0,0) + << rotationMatrix.map(rect.topRight()) + << rotationMatrix.map(rect.bottomRight()) + << rotationMatrix.map(rect.bottomLeft()); + //<< rotatedPoint(rect.topRight(), angleInt, center).toPoint() + //<< rotatedPoint(rect.bottomRight(), angleInt, center).toPoint() + //<< rotatedPoint(rect.bottomLeft(), angleInt, center).toPoint(); + return rotPts.boundingRect(); +/* + const QPointF topLeft( rotatedPoint( oldRect.topLeft(), angle, center ) ); + const QPointF topRight( rotatedPoint( oldRect.topRight(), angle, center ) ); + const QPointF bottomLeft( rotatedPoint( oldRect.bottomLeft(), angle, center ) ); + const QPointF bottomRight( rotatedPoint( oldRect.bottomRight(), angle, center ) ); + + const qreal x = qMin( qMin( topLeft.x(), topRight.x() ), qMin( bottomLeft.x(), topLeft.x() ) ); + const qreal y = qMin( qMin( topLeft.y(), topRight.y() ), qMin( bottomLeft.y(), topLeft.y() ) ); + const qreal width = qMax( qMax( topLeft.x(), topRight.x() ), qMax( bottomLeft.x(), topLeft.x() ) ) - x; + const qreal height = qMax( qMax( topLeft.y(), topRight.y() ), qMax( bottomLeft.y(), topLeft.y() ) ) - y; + + return QRectF( x, y, width, height ); +*/ +} + +/* +QRectF rotatedRect( const QRectF& rect, qreal angle ) +{ + const QPointF topLeft( rotatedPoint( rect.topLeft(), angle ) ); + //const QPointF topRight( rotatedPoint( rect.topRight(), angle ) ); + //const QPointF bottomLeft( rotatedPoint( rect.bottomLeft(), angle ) ); +#if 1 + const QPointF bottomRight( rotatedPoint( rect.bottomRight(), angle ) ); + const QRectF result( topLeft, QSizeF( bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y() ) ); +#else + const QPointF siz( rotatedPoint( QPointF( rect.size().width(), rect.size().height() ), angle ) ); + const QRectF result( + topLeft, + QSizeF( siz.x(), //bottomRight.x() - topLeft.x(), + siz.y() ) ); //bottomRight.y() - topLeft.y() ) ); + //qDebug() << "angle" << angle << "\nbefore:" << rect << "\n after:" << result; +#endif + return result; +}*/ + +qreal KDChart::TextLayoutItem::fitFontSizeToGeometry() const +{ + QFont f = realFont(); + const qreal origResult = f.pointSizeF(); + qreal result = origResult; + const qreal minSize = mAttributes.minimalFontSize().value(); + const QSize mySize = geometry().size(); + if( mySize.isNull() ) + return result; + + const QString t = text(); + QFontMetrics fm( f ); + while( true ) + { + const QSizeF textSize = rotatedRect( fm.boundingRect( t ), mAttributes.rotation() ).normalized().size(); + + if( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ) + return result; + + result -= 0.5; + if ( minSize > 0 && result < minSize ) + return result += 0.5; + if( result <= 0.0 ) + return origResult; + f.setPointSizeF( result ); + fm = QFontMetrics( f ); + } +} + +qreal KDChart::TextLayoutItem::realFontSize() const +{ + return mAttributes.calculatedFontSize( mAutoReferenceArea, mAutoReferenceOrientation ); +} + + +bool KDChart::TextLayoutItem::realFontWasRecalculated() const +{ + const qreal fntSiz = realFontSize(); + const bool bRecalcDone = + ( ( ! cachedSizeHint.isValid() ) || ( cachedFontSize != fntSiz ) ); + + if( bRecalcDone && fntSiz > 0.0 ){ + cachedFontSize = fntSiz; + cachedFont.setPointSizeF( fntSiz ); + } + return bRecalcDone; +} + + +QFont KDChart::TextLayoutItem::realFont() const +{ + realFontWasRecalculated(); // we can safely ignore the boolean return value + return cachedFont; +} + +QPolygon KDChart::TextLayoutItem::rotatedCorners() const +{ + // the angle in rad + const qreal angle = mAttributes.rotation() * PI / 180.0; + QSize size = unrotatedSizeHint(); + + // my P1 - P4 (the four points of the rotated area) + QPointF P1( size.height() * sin( angle ), 0 ); + QPointF P2( size.height() * sin( angle ) + size.width() * cos( angle ), size.width() * sin( angle ) ); + QPointF P3( size.width() * cos( angle ), size.width() * sin( angle ) + size.height() * cos( angle ) ); + QPointF P4( 0, size.height() * cos( angle ) ); + + QPolygon result; + result << P1.toPoint() << P2.toPoint() << P3.toPoint() << P4.toPoint(); + return result; +} + +bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const +{ + return intersects( other, myPos.toPoint(), otherPos.toPoint() ); +} + +bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const +{ + if ( mAttributes.rotation() != other.mAttributes.rotation() ) + { + // that's the code for the common case: the rotation angles don't need to match here + QPolygon myPolygon( rotatedCorners() ); + QPolygon otherPolygon( other.rotatedCorners() ); + + // move the polygons to their positions + myPolygon.translate( myPos ); + otherPolygon.translate( otherPos ); + + // create regions out of it + QRegion myRegion( myPolygon ); + QRegion otherRegion( otherPolygon ); + + // now the question - do they intersect or not? + return ! myRegion.intersect( otherRegion ).isEmpty(); + + } else { + // and that's the code for the special case: the rotation angles match, which is less time consuming in calculation + const qreal angle = mAttributes.rotation() * PI / 180.0; + // both sizes + const QSizeF mySize( unrotatedSizeHint() ); + const QSizeF otherSize( other.unrotatedSizeHint() ); + + // that's myP1 relative to myPos + QPointF myP1( mySize.height() * sin( angle ), 0.0 ); + // that's otherP1 to myPos + QPointF otherP1 = QPointF( otherSize.height() * sin( angle ), 0.0 ) + otherPos - myPos; + + // now rotate both points the negative angle around myPos + myP1 = QPointF( myP1.x() * cos( -angle ), myP1.x() * sin( -angle ) ); + qreal r = sqrt( otherP1.x() * otherP1.x() + otherP1.y() * otherP1.y() ); + otherP1 = QPointF( r * cos( -angle ), r * sin( -angle ) ); + + // finally we look, whether both rectangles intersect or even not + const bool res = QRectF( myP1, mySize ).intersects( QRectF( otherP1, otherSize ) ); + //qDebug() << res << QRectF( myP1, mySize ) << QRectF( otherP1, otherSize ); + return res; + } +} + +QSize KDChart::TextLayoutItem::sizeHint() const +{ + QPoint dummy; + return sizeHintAndRotatedCorners(dummy,dummy,dummy,dummy); +} + +QSize KDChart::TextLayoutItem::sizeHintAndRotatedCorners( + QPoint& topLeftPt, QPoint& topRightPt, QPoint& bottomRightPt, QPoint& bottomLeftPt) const +{ + if( realFontWasRecalculated() || mAttributes.rotation() ) + { + const QSize newSizeHint( calcSizeHint( cachedFont, + topLeftPt, topRightPt, bottomRightPt, bottomLeftPt ) ); + if( newSizeHint != cachedSizeHint ){ + cachedSizeHint = newSizeHint; + sizeHintChanged(); + } + cachedTopLeft = topLeftPt; + cachedTopRight = topRightPt; + cachedBottomRight = bottomRightPt; + cachedBottomLeft = bottomLeftPt; + }else{ + topLeftPt = cachedTopLeft; + topRightPt = cachedTopRight; + bottomRightPt = cachedBottomRight; + bottomLeftPt = cachedBottomLeft; + } + //qDebug() << "-------- KDChart::TextLayoutItem::sizeHint() returns:"<pixelMetric( QStyle::PM_ButtonMargin, 0, 0 ); + // fine-tuning for small font sizes: the frame must not be so big, if the font is tiny + frame = qMin( frame, ret.height() * 2 / 3 ); + //qDebug() << "frame:"<< frame; + ret += QSize( frame, frame ); + return ret; + //const QFontMetricsF met( fnt, GlobalMeasureScaling::paintDevice() ); + //const int frame = QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, 0, 0 ); + //return + // met.boundingRect( mText ).size().toSize() + QSize( frame, frame ); +} + + +QSize KDChart::TextLayoutItem::calcSizeHint( + QFont fnt, QPoint& topLeftPt, QPoint& topRightPt, QPoint& bottomRightPt, QPoint& bottomLeftPt ) const +{ + const QSize siz( unrotatedSizeHint( fnt )); + //qDebug() << "-------- siz: "<setFont( f ); + QRectF rect( geometry() ); + +// #ifdef DEBUG_ITEMS_PAINT +// painter->setPen( Qt::black ); +// painter->drawRect( rect ); +// #endif + painter->translate( rect.center() ); + rect.moveTopLeft( QPointF( - rect.width() / 2, - rect.height() / 2 ) ); +#ifdef DEBUG_ITEMS_PAINT + painter->setPen( Qt::blue ); + painter->drawRect( rect ); + painter->drawRect( QRect(QPoint((rect.topLeft().toPoint() + rect.bottomLeft().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); + //painter->drawRect( QRect(QPoint((rect.topRight().toPoint() + rect.bottomRight().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); +#endif + painter->rotate( mAttributes.rotation() ); + rect = rotatedRect( rect, mAttributes.rotation() ); +#ifdef DEBUG_ITEMS_PAINT + painter->setPen( Qt::red ); + painter->drawRect( rect ); + painter->drawRect( QRect(QPoint((rect.topLeft().toPoint() + rect.bottomLeft().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); + //painter->drawRect( QRect(QPoint((rect.topRight().toPoint() + rect.bottomRight().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) ); +#endif + painter->setPen( PrintingParameters::scalePen( mAttributes.pen() ) ); + QFontMetrics fontMetrics( f ); + const int AHight = fontMetrics.boundingRect( QChar::fromAscii( 'A' ) ).height(); + const qreal AVCenter = fontMetrics.ascent() - AHight / 2.0; + // Make sure that capital letters are vertically centered. This looks much + // better than just centering the text's bounding rect. + rect.translate( 0.0, rect.height() / 2.0 - AVCenter ); + //painter->drawText( rect, Qt::AlignHCenter | Qt::AlignTop, mText ); + painter->drawText( rect, mTextAlignment, mText ); + +// if ( calcSizeHint( realFont() ).width() > rect.width() ) +// qDebug() << "rect.width()" << rect.width() << "text.width()" << calcSizeHint( realFont() ).width(); +// +// //painter->drawText( rect, Qt::AlignHCenter | Qt::AlignVCenter, mText ); +} + +KDChart::HorizontalLineLayoutItem::HorizontalLineLayoutItem() + : AbstractLayoutItem( Qt::AlignCenter ) +{ +} + +Qt::Orientations KDChart::HorizontalLineLayoutItem::expandingDirections() const +{ + return Qt::Vertical|Qt::Horizontal; // Grow both vertically, and horizontally +} + +QRect KDChart::HorizontalLineLayoutItem::geometry() const +{ + return mRect; +} + +bool KDChart::HorizontalLineLayoutItem::isEmpty() const +{ + return false; // never empty, otherwise the layout item would not exist +} + +QSize KDChart::HorizontalLineLayoutItem::maximumSize() const +{ + return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); +} + +QSize KDChart::HorizontalLineLayoutItem::minimumSize() const +{ + return QSize( 0, 0 ); +} + +void KDChart::HorizontalLineLayoutItem::setGeometry( const QRect& r ) +{ + mRect = r; +} + +QSize KDChart::HorizontalLineLayoutItem::sizeHint() const +{ + return QSize( -1, 3 ); // see qframe.cpp +} + + +void KDChart::HorizontalLineLayoutItem::paint( QPainter* painter ) +{ + if( !mRect.isValid() ) + return; + + painter->drawLine( QPointF( mRect.left(), mRect.center().y() ), + QPointF( mRect.right(), mRect.center().y() ) ); +} + + +KDChart::VerticalLineLayoutItem::VerticalLineLayoutItem() + : AbstractLayoutItem( Qt::AlignCenter ) +{ +} + +Qt::Orientations KDChart::VerticalLineLayoutItem::expandingDirections() const +{ + return Qt::Vertical|Qt::Vertical; // Grow both vertically, and horizontally +} + +QRect KDChart::VerticalLineLayoutItem::geometry() const +{ + return mRect; +} + +bool KDChart::VerticalLineLayoutItem::isEmpty() const +{ + return false; // never empty, otherwise the layout item would not exist +} + +QSize KDChart::VerticalLineLayoutItem::maximumSize() const +{ + return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); +} + +QSize KDChart::VerticalLineLayoutItem::minimumSize() const +{ + return QSize( 0, 0 ); +} + +void KDChart::VerticalLineLayoutItem::setGeometry( const QRect& r ) +{ + mRect = r; +} + +QSize KDChart::VerticalLineLayoutItem::sizeHint() const +{ + return QSize( 3, -1 ); // see qframe.cpp +} + + +void KDChart::VerticalLineLayoutItem::paint( QPainter* painter ) +{ + if( !mRect.isValid() ) + return; + + painter->drawLine( QPointF( mRect.center().x(), mRect.top() ), + QPointF( mRect.center().x(), mRect.bottom() ) ); +} + + + +KDChart::MarkerLayoutItem::MarkerLayoutItem( KDChart::AbstractDiagram* diagram, + const MarkerAttributes& marker, + const QBrush& brush, const QPen& pen, + Qt::Alignment alignment ) + : AbstractLayoutItem( alignment ) + , mDiagram( diagram ) + , mMarker( marker ) + , mBrush( brush ) + , mPen( pen ) +{ +} + +Qt::Orientations KDChart::MarkerLayoutItem::expandingDirections() const +{ + return 0; // Grow neither vertically nor horizontally +} + +QRect KDChart::MarkerLayoutItem::geometry() const +{ + return mRect; +} + +bool KDChart::MarkerLayoutItem::isEmpty() const +{ + return false; // never empty, otherwise the layout item would not exist +} + +QSize KDChart::MarkerLayoutItem::maximumSize() const +{ + return sizeHint(); // PENDING(kalle) Review, quite inflexible +} + +QSize KDChart::MarkerLayoutItem::minimumSize() const +{ + return sizeHint(); // PENDING(kalle) Review, quite inflexible +} + +void KDChart::MarkerLayoutItem::setGeometry( const QRect& r ) +{ + mRect = r; +} + +QSize KDChart::MarkerLayoutItem::sizeHint() const +{ + //qDebug() << "KDChart::MarkerLayoutItem::sizeHint() returns:"<(( rect.width() - siz.width()) / 2.0 ), + static_cast(( rect.height() - siz.height()) / 2.0 ) ); + +#ifdef DEBUG_ITEMS_PAINT + QPointF oldPos = pos; +#endif + +// And finally, drawMarker() assumes the position to be the center + // of the marker, adjust again. + pos += QPointF( static_cast( siz.width() ) / 2.0, + static_cast( siz.height() )/ 2.0 ); + + diagram->paintMarker( painter, marker, brush, pen, pos.toPoint(), siz ); + +#ifdef DEBUG_ITEMS_PAINT + const QPen oldPen( painter->pen() ); + painter->setPen( Qt::red ); + painter->drawRect( QRect(oldPos.toPoint(), siz) ); + painter->setPen( oldPen ); +#endif +} + + +KDChart::LineLayoutItem::LineLayoutItem( KDChart::AbstractDiagram* diagram, + int length, + const QPen& pen, + Qt::Alignment alignment ) + : AbstractLayoutItem( alignment ) + , mDiagram( diagram ) + , mLength( length ) + , mPen( pen ) +{ + //have a mini pen width + if ( pen.width() < 2 ) + mPen.setWidth( 2 ); +} + +Qt::Orientations KDChart::LineLayoutItem::expandingDirections() const +{ + return 0; // Grow neither vertically nor horizontally +} + +QRect KDChart::LineLayoutItem::geometry() const +{ + return mRect; +} + +bool KDChart::LineLayoutItem::isEmpty() const +{ + return false; // never empty, otherwise the layout item would not exist +} + +QSize KDChart::LineLayoutItem::maximumSize() const +{ + return sizeHint(); // PENDING(kalle) Review, quite inflexible +} + +QSize KDChart::LineLayoutItem::minimumSize() const +{ + return sizeHint(); // PENDING(kalle) Review, quite inflexible +} + +void KDChart::LineLayoutItem::setGeometry( const QRect& r ) +{ + mRect = r; +} + +QSize KDChart::LineLayoutItem::sizeHint() const +{ + return QSize( mLength, mPen.width()+2 ); +} + +void KDChart::LineLayoutItem::paint( QPainter* painter ) +{ + paintIntoRect( painter, mRect, mPen ); +} + +void KDChart::LineLayoutItem::paintIntoRect( + QPainter* painter, + const QRect& rect, + const QPen& pen ) +{ + if( ! rect.isValid() ) + return; + + const QPen oldPen = painter->pen(); + painter->setPen( PrintingParameters::scalePen( pen ) ); + const qreal y = rect.center().y(); + painter->drawLine( QPointF( rect.left(), y ), + QPointF( rect.right(), y ) ); + painter->setPen( oldPen ); +} + + +KDChart::LineWithMarkerLayoutItem::LineWithMarkerLayoutItem( + KDChart::AbstractDiagram* diagram, + int lineLength, + const QPen& linePen, + int markerOffs, + const MarkerAttributes& marker, + const QBrush& markerBrush, + const QPen& markerPen, + Qt::Alignment alignment ) + : AbstractLayoutItem( alignment ) + , mDiagram( diagram ) + , mLineLength( lineLength ) + , mLinePen( linePen ) + , mMarkerOffs( markerOffs ) + , mMarker( marker ) + , mMarkerBrush( markerBrush ) + , mMarkerPen( markerPen ) +{ +} + +Qt::Orientations KDChart::LineWithMarkerLayoutItem::expandingDirections() const +{ + return 0; // Grow neither vertically nor horizontally +} + +QRect KDChart::LineWithMarkerLayoutItem::geometry() const +{ + return mRect; +} + +bool KDChart::LineWithMarkerLayoutItem::isEmpty() const +{ + return false; // never empty, otherwise the layout item would not exist +} + +QSize KDChart::LineWithMarkerLayoutItem::maximumSize() const +{ + return sizeHint(); // PENDING(kalle) Review, quite inflexible +} + +QSize KDChart::LineWithMarkerLayoutItem::minimumSize() const +{ + return sizeHint(); // PENDING(kalle) Review, quite inflexible +} + +void KDChart::LineWithMarkerLayoutItem::setGeometry( const QRect& r ) +{ + mRect = r; +} + +QSize KDChart::LineWithMarkerLayoutItem::sizeHint() const +{ + const QSize sizeM = mMarker.markerSize().toSize(); + const QSize sizeL = QSize( mLineLength, mLinePen.width()+2 ); + return QSize( qMax(sizeM.width(), sizeL.width()), + qMax(sizeM.height(), sizeL.height()) ); +} + +void KDChart::LineWithMarkerLayoutItem::paint( QPainter* painter ) +{ + // paint the line over the full width, into the vertical middle of the rect + LineLayoutItem::paintIntoRect( painter, mRect, mLinePen ); + + // paint the marker with the given offset from the left side of the line + const QRect r( + QPoint( mRect.x()+mMarkerOffs, mRect.y() ), + QSize( mMarker.markerSize().toSize().width(), mRect.height() ) ); + MarkerLayoutItem::paintIntoRect( + painter, r, mDiagram, mMarker, mMarkerBrush, mMarkerPen ); +} + + + +KDChart::AutoSpacerLayoutItem::AutoSpacerLayoutItem( + bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout, + bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout ) + : AbstractLayoutItem( Qt::AlignCenter ) + , mLayoutIsAtTopPosition( layoutIsAtTopPosition ) + , mRightLeftLayout( rightLeftLayout ) + , mLayoutIsAtLeftPosition( layoutIsAtLeftPosition ) + , mTopBottomLayout( topBottomLayout ) +{ +} + +Qt::Orientations KDChart::AutoSpacerLayoutItem::expandingDirections() const +{ + return 0; // Grow neither vertically nor horizontally +} + +QRect KDChart::AutoSpacerLayoutItem::geometry() const +{ + return mRect; +} + +bool KDChart::AutoSpacerLayoutItem::isEmpty() const +{ + return true; // never empty, otherwise the layout item would not exist +} + +QSize KDChart::AutoSpacerLayoutItem::maximumSize() const +{ + return sizeHint(); +} + +QSize KDChart::AutoSpacerLayoutItem::minimumSize() const +{ + return sizeHint(); +} + +void KDChart::AutoSpacerLayoutItem::setGeometry( const QRect& r ) +{ + mRect = r; +} + + +static void updateCommonBrush( QBrush& commonBrush, bool& bStart, const KDChart::AbstractArea& area ) +{ + const KDChart::BackgroundAttributes ba( area.backgroundAttributes() ); + const bool hasSimpleBrush = ( + ! area.frameAttributes().isVisible() && + ba.isVisible() && + ba.pixmapMode() == KDChart::BackgroundAttributes::BackgroundPixmapModeNone && + ba.brush().gradient() == 0 ); + if( bStart ){ + bStart = false; + commonBrush = hasSimpleBrush ? ba.brush() : QBrush(); + }else{ + if( ! hasSimpleBrush || ba.brush() != commonBrush ) + { + commonBrush = QBrush(); + } + } +} + +QSize KDChart::AutoSpacerLayoutItem::sizeHint() const +{ + QBrush commonBrush; + bool bStart=true; + // calculate the maximal overlap of the top/bottom axes: + int topBottomOverlap = 0; + if( mTopBottomLayout ){ + for (int i = 0; i < mTopBottomLayout->count(); ++i){ + AbstractArea* area = dynamic_cast(mTopBottomLayout->itemAt(i)); + if( area ){ + //qDebug() << "AutoSpacerLayoutItem testing" << area; + topBottomOverlap = + mLayoutIsAtLeftPosition + ? qMax( topBottomOverlap, area->rightOverlap() ) + : qMax( topBottomOverlap, area->leftOverlap() ); + updateCommonBrush( commonBrush, bStart, *area ); + } + } + } + // calculate the maximal overlap of the left/right axes: + int leftRightOverlap = 0; + if( mRightLeftLayout ){ + for (int i = 0; i < mRightLeftLayout->count(); ++i){ + AbstractArea* area = dynamic_cast(mRightLeftLayout->itemAt(i)); + if( area ){ + //qDebug() << "AutoSpacerLayoutItem testing" << area; + leftRightOverlap = + mLayoutIsAtTopPosition + ? qMax( leftRightOverlap, area->bottomOverlap() ) + : qMax( leftRightOverlap, area->topOverlap() ); + updateCommonBrush( commonBrush, bStart, *area ); + } + } + } + if( topBottomOverlap > 0 && leftRightOverlap > 0 ) + mCommonBrush = commonBrush; + else + mCommonBrush = QBrush(); + mCachedSize = QSize( topBottomOverlap, leftRightOverlap ); + //qDebug() << mCachedSize; + return mCachedSize; +} + + +void KDChart::AutoSpacerLayoutItem::paint( QPainter* painter ) +{ + if( mParentLayout && mRect.isValid() && mCachedSize.isValid() && + mCommonBrush.style() != Qt::NoBrush ) + { + QPoint p1( mRect.topLeft() ); + QPoint p2( mRect.bottomRight() ); + if( mLayoutIsAtLeftPosition ) + p1.rx() += mCachedSize.width() - mParentLayout->spacing(); + else + p2.rx() -= mCachedSize.width() - mParentLayout->spacing(); + if( mLayoutIsAtTopPosition ){ + p1.ry() += mCachedSize.height() - mParentLayout->spacing() - 1; + p2.ry() -= 1; + }else + p2.ry() -= mCachedSize.height() - mParentLayout->spacing() - 1; + //qDebug() << mLayoutIsAtTopPosition << mLayoutIsAtLeftPosition; + //qDebug() << mRect; + //qDebug() << mParentLayout->margin(); + //qDebug() << QRect( p1, p2 ); + const QPoint oldBrushOrigin( painter->brushOrigin() ); + const QBrush oldBrush( painter->brush() ); + const QPen oldPen( painter->pen() ); + const QPointF newTopLeft( painter->deviceMatrix().map( p1 ) ); + painter->setBrushOrigin( newTopLeft ); + painter->setBrush( mCommonBrush ); + painter->setPen( Qt::NoPen ); + painter->drawRect( QRect( p1, p2 ) ); + painter->setBrushOrigin( oldBrushOrigin ); + painter->setBrush( oldBrush ); + painter->setPen( oldPen ); + } + // debug code: +#if 0 + //qDebug() << "KDChart::AutoSpacerLayoutItem::paint()"; + if( !mRect.isValid() ) + return; + + painter->drawRect( mRect ); + painter->drawLine( QPointF( mRect.x(), mRect.top() ), + QPointF( mRect.right(), mRect.bottom() ) ); + painter->drawLine( QPointF( mRect.right(), mRect.top() ), + QPointF( mRect.x(), mRect.bottom() ) ); +#endif +} diff --git a/libkdchart/src/KDChartLayoutItems.h b/libkdchart/src/KDChartLayoutItems.h new file mode 100644 index 0000000..c1f0368 --- /dev/null +++ b/libkdchart/src/KDChartLayoutItems.h @@ -0,0 +1,473 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTLAYOUTITEMS_H +#define KDCHARTLAYOUTITEMS_H + +#include +#include +#include +#include +#include +#include + +#include "KDChartTextAttributes.h" +#include "KDChartMarkerAttributes.h" + +class QPainter; +class KDTextDocument; + +QPointF rotatedPoint( const QPointF& pt, qreal rotation, const QPointF& center = QPointF() ); +QRectF rotatedRect( const QRectF& pt, qreal rotation, const QPointF& center = QPointF() ); + +namespace KDChart { + class AbstractDiagram; + class PaintContext; + + /** + * Base class for all layout items of KD Chart + * \internal + */ + class KDCHART_EXPORT AbstractLayoutItem : public QLayoutItem + { + public: + AbstractLayoutItem( Qt::Alignment itemAlignment = 0 ) : + QLayoutItem( itemAlignment ), + mParent( 0 ), + mParentLayout( 0 ) {} + + /** + * Default impl: just call paint. + * + * Derived classes like KDChart::AbstractArea are providing + * additional action here. + */ + virtual void paintAll( QPainter& painter ); + + virtual void paint( QPainter* ) = 0; + + virtual void paintCtx( PaintContext* context ); + virtual void setParentWidget( QWidget* widget ); + virtual void sizeHintChanged()const; + + void setParentLayout( QLayout* lay ) + { + mParentLayout = lay; + } + QLayout* parentLayout() + { + return mParentLayout; + } + void removeFromParentLayout() + { + if( mParentLayout ){ + if( widget() ) + mParentLayout->removeWidget( widget() ); + else + mParentLayout->removeItem( this ); + } + } + protected: + QWidget* mParent; + QLayout* mParentLayout; + }; + + /** + * Layout item showing a text + *\internal + */ + class KDCHART_EXPORT TextLayoutItem : public AbstractLayoutItem + { + public: + TextLayoutItem(); + TextLayoutItem( const QString& text, + const TextAttributes& attributes, + const QObject* autoReferenceArea, + KDChartEnums::MeasureOrientation autoReferenceOrientation, + Qt::Alignment alignment = 0 ); + + void setAutoReferenceArea( const QObject* area ); + const QObject* autoReferenceArea() const; + + void setText(const QString & text); + QString text() const; + + void setTextAlignment( Qt::Alignment ); + Qt::Alignment textAlignment() const; + + void setTextAttributes( const TextAttributes& a ); + TextAttributes textAttributes() const; + + /** pure virtual in QLayoutItem */ + virtual bool isEmpty() const; + /** pure virtual in QLayoutItem */ + virtual Qt::Orientations expandingDirections() const; + /** pure virtual in QLayoutItem */ + virtual QSize maximumSize() const; + /** pure virtual in QLayoutItem */ + virtual QSize minimumSize() const; + /** pure virtual in QLayoutItem */ + virtual QSize sizeHint() const; + /** pure virtual in QLayoutItem */ + virtual void setGeometry( const QRect& r ); + /** pure virtual in QLayoutItem */ + virtual QRect geometry() const; + + virtual QSize sizeHintAndRotatedCorners( + QPoint& topLeftPt, QPoint& topRightPt, QPoint& bottomRightPt, QPoint& bottomLeftPt) const; + virtual QSize sizeHintUnrotated() const; + + virtual bool intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const; + virtual bool intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const; + + virtual qreal realFontSize() const; + virtual QFont realFont() const; + + virtual void paint( QPainter* ); + + private: + QPolygon rotatedCorners() const; + bool realFontWasRecalculated() const; + QSize unrotatedSizeHint( QFont fnt = QFont() ) const; + QSize calcSizeHint( QFont fnt, + QPoint& topLeftPt, QPoint& topRightPt, QPoint& bottomRightPt, QPoint& bottomLeftPt ) const; + + qreal fitFontSizeToGeometry() const; + + QRect mRect; + QString mText; + Qt::Alignment mTextAlignment; + TextAttributes mAttributes; + const QObject* mAutoReferenceArea; + KDChartEnums::MeasureOrientation mAutoReferenceOrientation; + mutable QSize cachedSizeHint; + mutable QPoint cachedTopLeft; + mutable QPoint cachedTopRight; + mutable QPoint cachedBottomRight; + mutable QPoint cachedBottomLeft; + mutable qreal cachedFontSize; + mutable QFont cachedFont; + }; + + class KDCHART_EXPORT TextBubbleLayoutItem : public AbstractLayoutItem + { + public: + TextBubbleLayoutItem(); + TextBubbleLayoutItem( const QString& text, + const TextAttributes& attributes, + const QObject* autoReferenceArea, + KDChartEnums::MeasureOrientation autoReferenceOrientation, + Qt::Alignment alignment = 0 ); + + ~TextBubbleLayoutItem(); + + void setAutoReferenceArea( const QObject* area ); + const QObject* autoReferenceArea() const; + + void setText(const QString & text); + QString text() const; + + void setTextAttributes( const TextAttributes& a ); + TextAttributes textAttributes() const; + + /** pure virtual in QLayoutItem */ + virtual bool isEmpty() const; + /** pure virtual in QLayoutItem */ + virtual Qt::Orientations expandingDirections() const; + /** pure virtual in QLayoutItem */ + virtual QSize maximumSize() const; + /** pure virtual in QLayoutItem */ + virtual QSize minimumSize() const; + /** pure virtual in QLayoutItem */ + virtual QSize sizeHint() const; + /** pure virtual in QLayoutItem */ + virtual void setGeometry( const QRect& r ); + /** pure virtual in QLayoutItem */ + virtual QRect geometry() const; + + virtual void paint( QPainter* painter ); + + protected: + int borderWidth() const; + + private: + TextLayoutItem* const m_text; + }; + + /** + * Layout item showing a data point marker + * \internal + */ + class KDCHART_EXPORT MarkerLayoutItem : public AbstractLayoutItem + { + public: + MarkerLayoutItem( AbstractDiagram* diagram, + const MarkerAttributes& marker, + const QBrush& brush, + const QPen& pen, + Qt::Alignment alignment = 0 ); + + virtual Qt::Orientations expandingDirections() const; + virtual QRect geometry() const; + virtual bool isEmpty() const; + virtual QSize maximumSize() const; + virtual QSize minimumSize() const; + virtual void setGeometry( const QRect& r ); + virtual QSize sizeHint() const; + + virtual void paint( QPainter* ); + + static void paintIntoRect( + QPainter* painter, + const QRect& rect, + AbstractDiagram* diagram, + const MarkerAttributes& marker, + const QBrush& brush, + const QPen& pen ); + + private: + AbstractDiagram* mDiagram; + QRect mRect; + MarkerAttributes mMarker; + QBrush mBrush; + QPen mPen; + }; + + /** + * Layout item showing a coloured line + * \internal + */ + class KDCHART_EXPORT LineLayoutItem : public AbstractLayoutItem + { + public: + LineLayoutItem( AbstractDiagram* diagram, + int length, + const QPen& pen, + Qt::Alignment alignment = 0 ); + + virtual Qt::Orientations expandingDirections() const; + virtual QRect geometry() const; + virtual bool isEmpty() const; + virtual QSize maximumSize() const; + virtual QSize minimumSize() const; + virtual void setGeometry( const QRect& r ); + virtual QSize sizeHint() const; + + virtual void paint( QPainter* ); + + static void paintIntoRect( + QPainter* painter, + const QRect& rect, + const QPen& pen ); + + private: + AbstractDiagram* mDiagram; + int mLength; + QPen mPen; + QRect mRect; + }; + + /** + * Layout item showing a coloured line and a data point marker + * \internal + */ + class KDCHART_EXPORT LineWithMarkerLayoutItem : public AbstractLayoutItem + { + public: + LineWithMarkerLayoutItem( AbstractDiagram* diagram, + int lineLength, + const QPen& linePen, + int markerOffs, + const MarkerAttributes& marker, + const QBrush& markerBrush, + const QPen& markerPen, + Qt::Alignment alignment = 0 ); + + virtual Qt::Orientations expandingDirections() const; + virtual QRect geometry() const; + virtual bool isEmpty() const; + virtual QSize maximumSize() const; + virtual QSize minimumSize() const; + virtual void setGeometry( const QRect& r ); + virtual QSize sizeHint() const; + + virtual void paint( QPainter* ); + + private: + AbstractDiagram* mDiagram; + QRect mRect; + int mLineLength; + QPen mLinePen; + int mMarkerOffs; + MarkerAttributes mMarker; + QBrush mMarkerBrush; + QPen mMarkerPen; + }; + + + /** + * Layout item showing a horizontal line + * \internal + */ + class KDCHART_EXPORT HorizontalLineLayoutItem : public AbstractLayoutItem + { + public: + HorizontalLineLayoutItem(); + + virtual Qt::Orientations expandingDirections() const; + virtual QRect geometry() const; + virtual bool isEmpty() const; + virtual QSize maximumSize() const; + virtual QSize minimumSize() const; + virtual void setGeometry( const QRect& r ); + virtual QSize sizeHint() const; + + virtual void paint( QPainter* ); + + private: + QRect mRect; + }; + + /** + * Layout item showing a vertial line + * \internal + */ + class KDCHART_EXPORT VerticalLineLayoutItem : public AbstractLayoutItem + { + public: + VerticalLineLayoutItem(); + + virtual Qt::Orientations expandingDirections() const; + virtual QRect geometry() const; + virtual bool isEmpty() const; + virtual QSize maximumSize() const; + virtual QSize minimumSize() const; + virtual void setGeometry( const QRect& r ); + virtual QSize sizeHint() const; + + virtual void paint( QPainter* ); + + private: + QRect mRect; + }; + + /** + * @brief An empty layout item + * \internal + * + * The AutoSpacerLayoutItem is automatically put into each corner cell of + * the planeLayout grid: one of its reference-layouts is a QVBoxLayout (for + * the top, or bottom axes resp.), the other one is a QHBoxLayout (for the + * left/right sided axes). + * + * The spacer reserves enough space so all of the AbstractAreas contained + * in the two reference-layouts can display not only their in-bounds + * content but also their overlapping content reaching out of their area. + * + * KD Chart's layouting is applying this schema: +\verbatim + +------------------+-------------------------+-----------------+ + | +--------------+ | +---------------------+ | +-------------+ | + | | | | | QVBoxLayout for | | | | | + | | AUTO | | | the top axis/axes | | | AUTO | | + | | SPACER | | +---------------------+ | | SPACER | | + | | ITEM | | | | | | ITEM | | + | | | | | | | | | | + | +--------------+ | +---------------------+ | +-------------+ | + +------------------+-------------------------+-----------------+ + | +--------+-----+ | +---------------------+ | +-------+-----+ | + | | | | | | | | | | | | + | | | | | | | | | | | | + | | QHBox- | | | | | | | Right | | | + | | Layout | | | | | | | | | | + | | | | | | | | | axes | | | + | | for | | | | | | | | | | + | | | | | | | | | layout| | | + | | the | | | | DIAGRAM(s) | | | | | | + | | | | | | | | | | | | + | | left | | | | | | | | | | + | | | | | | | | | | | | + | | axis | | | | | | | | | | + | | or | | | | | | | | | | + | | axes | | | | | | | | | | + | | | | | | | | | | | | + | +--------+-----+ | +---------------------+ | +-------+-----+ | + +------------------+-------------------------+-----------------+ + | +--------------+ | +---------------------+ | +-------------+ | + | | | | | QVBoxLayout for | | | | | + | | AUTO | | | the bottom axes | | | AUTO | | + | | SPACER | | +---------------------+ | | SPACER | | + | | ITEM | | | | | | ITEM | | + | | | | | | | | | | + | +--------------+ | +---------------------+ | +-------------+ | + +------------------+-------------------------+-----------------+ +\endverbatim + * + * A typical use case is an Abscissa axis with long labels: +\verbatim + 2 -| + | + 1 -| + | + 0 -+------------------------------------ + | | | | | + Monday Tuesday Wednesday Thursday Friday +\endverbatim + * The last letters of the word "Friday" would have been + * cut off in previous versions of KD Chart - that is + * if you did not call KDChart::Chart::setGlobalLeading(). + * + * Now the word will be shown completely because there + * is an auto-spacer-item taking care for the additional + * space needed in the lower/right corner. + */ + class KDCHART_EXPORT AutoSpacerLayoutItem : public AbstractLayoutItem + { + public: + AutoSpacerLayoutItem( + bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout, + bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout ); + + virtual Qt::Orientations expandingDirections() const; + virtual QRect geometry() const; + virtual bool isEmpty() const; + virtual QSize maximumSize() const; + virtual QSize minimumSize() const; + virtual void setGeometry( const QRect& r ); + virtual QSize sizeHint() const; + + virtual void paint( QPainter* ); + + private: + QRect mRect; + bool mLayoutIsAtTopPosition; + QHBoxLayout *mRightLeftLayout; + bool mLayoutIsAtLeftPosition; + QVBoxLayout *mTopBottomLayout; + + mutable QBrush mCommonBrush; + mutable QSize mCachedSize; + }; + +} + +#endif /* KDCHARTLAYOUTITEMS_H */ diff --git a/libkdchart/src/KDChartLegend.cpp b/libkdchart/src/KDChartLegend.cpp new file mode 100644 index 0000000..7efb10f --- /dev/null +++ b/libkdchart/src/KDChartLegend.cpp @@ -0,0 +1,1148 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartLegend.h" +#include "KDChartLegend_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "KDTextDocument.h" +#include +#include +#include "KDChartLayoutItems.h" + +#include + +using namespace KDChart; + +Legend::Private::Private() : + referenceArea(0), + position( Position::East ), + alignment( Qt::AlignCenter ), + textAlignment( Qt::AlignCenter ), + relativePosition( RelativePosition() ), + orientation( Qt::Vertical ), + order( Qt::AscendingOrder ), + showLines( false ), + texts(), + textAttributes(), + titleText( QObject::tr( "Legend" ) ), + titleTextAttributes(), + spacing( 1 ), + useAutomaticMarkerSize( true ), + legendStyle( MarkersOnly ) + //needRebuild( true ) +{ + // By default we specify a simple, hard point as the 'relative' position's ref. point, + // since we can not be sure that there will be any parent specified for the legend. + relativePosition.setReferencePoints( PositionPoints( QPointF( 0.0, 0.0 ) ) ); + relativePosition.setReferencePosition( Position::NorthWest ); + relativePosition.setAlignment( Qt::AlignTop | Qt::AlignLeft ); + relativePosition.setHorizontalPadding( KDChart::Measure( 4.0, KDChartEnums::MeasureCalculationModeAbsolute ) ); + relativePosition.setVerticalPadding( KDChart::Measure( 4.0, KDChartEnums::MeasureCalculationModeAbsolute ) ); +} + +Legend::Private::~Private() +{ + // this bloc left empty intentionally +} + + + +#define d d_func() + + +Legend::Legend( QWidget* parent ) : + AbstractAreaWidget( new Private(), parent ) +{ + d->referenceArea = parent; + init(); +} + +Legend::Legend( KDChart::AbstractDiagram* diagram, QWidget* parent ) : + AbstractAreaWidget( new Private(), parent ) +{ + d->referenceArea = parent; + init(); + setDiagram( diagram ); +} + +Legend::~Legend() +{ + emit destroyedLegend( this ); +} + +void Legend::init() +{ + setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + d->layout = new QGridLayout( this ); + d->layout->setMargin( 2 ); + d->layout->setSpacing( d->spacing ); + //setLayout( d->layout ); + + const Measure normalFontSizeTitle( 12, KDChartEnums::MeasureCalculationModeAbsolute ); + const Measure normalFontSizeLabels( 10, KDChartEnums::MeasureCalculationModeAbsolute ); + const Measure minimalFontSize( 4, KDChartEnums::MeasureCalculationModeAbsolute ); + + TextAttributes textAttrs; + textAttrs.setPen( QPen( Qt::black ) ); + textAttrs.setFont( QFont( QLatin1String( "helvetica" ), 10, QFont::Normal, false ) ); + textAttrs.setFontSize( normalFontSizeLabels ); + textAttrs.setMinimalFontSize( minimalFontSize ); + setTextAttributes( textAttrs ); + + TextAttributes titleTextAttrs; + titleTextAttrs.setPen( QPen( Qt::black ) ); + titleTextAttrs.setFont( QFont( QLatin1String( "helvetica" ), 12, QFont::Bold, false ) ); + titleTextAttrs.setFontSize( normalFontSizeTitle ); + titleTextAttrs.setMinimalFontSize( minimalFontSize ); + setTitleTextAttributes( titleTextAttrs ); + + FrameAttributes frameAttrs; + frameAttrs.setVisible( true ); + frameAttrs.setPen( QPen( Qt::black ) ); + frameAttrs.setPadding( 1 ); + setFrameAttributes( frameAttrs ); + + d->position = Position::NorthEast; + d->alignment = Qt::AlignCenter; +} + + +QSize Legend::minimumSizeHint() const +{ + return sizeHint(); +} + +//#define DEBUG_LEGEND_PAINT + +QSize Legend::sizeHint() const +{ +#ifdef DEBUG_LEGEND_PAINT + qDebug() << "Legend::sizeHint() started"; +#endif + Q_FOREACH( KDChart::AbstractLayoutItem* layoutItem, d->layoutItems ) { + layoutItem->sizeHint(); + } + return AbstractAreaWidget::sizeHint(); +} + +void Legend::needSizeHint() +{ + // Re-build the Legend's content, if it has not been build yet, + // or if the Legend's geometry has changed, resp. + buildLegend(); +} + +void Legend::resizeLayout( const QSize& size ) +{ +#ifdef DEBUG_LEGEND_PAINT + qDebug() << "Legend::resizeLayout started"; +#endif + if( d->layout ){ + d->layout->setGeometry( QRect(QPoint(0,0), size) ); + activateTheLayout(); + } +#ifdef DEBUG_LEGEND_PAINT + qDebug() << "Legend::resizeLayout done"; +#endif +} + +void Legend::activateTheLayout() +{ + if( d->layout && d->layout->parent() ) + d->layout->activate(); +} + + +void Legend::setLegendStyle( LegendStyle style ) +{ + if( d->legendStyle == style ) return; + d->legendStyle = style; + setNeedRebuild(); +} + +Legend::LegendStyle Legend::legendStyle() const +{ + return d->legendStyle; +} + +/** + * Creates an exact copy of this legend. + */ +Legend* Legend::clone() const +{ + Legend* legend = new Legend( new Private( *d ), 0 ); + legend->setTextAttributes( textAttributes() ); + legend->setTitleTextAttributes( titleTextAttributes() ); + legend->setFrameAttributes( frameAttributes() ); + legend->setUseAutomaticMarkerSize( useAutomaticMarkerSize() ); + legend->setPosition( position() ); + legend->setAlignment( alignment() ); + legend->setTextAlignment( textAlignment() ); + legend->setLegendStyle( legendStyle() ); + return legend; +} + + +bool Legend::compare( const Legend* other )const +{ + if( other == this ) return true; + if( ! other ){ + //qDebug() << "Legend::compare() cannot compare to Null pointer"; + return false; + } + + qDebug() << ( static_cast(this)->compare( other ) ); + qDebug() << (isVisible() == other->isVisible()); + qDebug() << (position() == other->position()); + qDebug() << (alignment() == other->alignment()); + qDebug() << (textAlignment() == other->textAlignment()); + qDebug() << (floatingPosition() == other->floatingPosition()); + qDebug() << (orientation() == other->orientation()); + qDebug() << (showLines() == other->showLines()); + qDebug() << (texts() == other->texts()); + qDebug() << (brushes() == other->brushes()); + qDebug() << (pens() == other->pens()); + qDebug() << (markerAttributes() == other->markerAttributes()); + qDebug() << (useAutomaticMarkerSize() == other->useAutomaticMarkerSize()); + qDebug() << (textAttributes() == other->textAttributes()); + qDebug() << (titleText() == other->titleText()); + qDebug() << (titleTextAttributes() == other->titleTextAttributes()); + qDebug() << (spacing() == other->spacing()); + qDebug() << (legendStyle() == other->legendStyle()); + + return ( static_cast(this)->compare( other ) ) && + (isVisible() == other->isVisible()) && + (position() == other->position()) && + (alignment() == other->alignment())&& + (textAlignment() == other->textAlignment())&& + (floatingPosition() == other->floatingPosition()) && + (orientation() == other->orientation())&& + (showLines() == other->showLines())&& + (texts() == other->texts())&& + (brushes() == other->brushes())&& + (pens() == other->pens())&& + (markerAttributes() == other->markerAttributes())&& + (useAutomaticMarkerSize() == other->useAutomaticMarkerSize()) && + (textAttributes() == other->textAttributes()) && + (titleText() == other->titleText())&& + (titleTextAttributes() == other->titleTextAttributes()) && + (spacing() == other->spacing()) && + (legendStyle() == other->legendStyle()); +} + + +void Legend::paint( QPainter* painter ) +{ +#ifdef DEBUG_LEGEND_PAINT + qDebug() << "entering Legend::paint( QPainter* painter )"; +#endif + // rule: We do not show a legend, if there is no diagram. + if( ! diagram() ) return; + + // re-calculate/adjust the Legend's internal layout and contents, if needed: + //buildLegend(); + + // PENDING(kalle) Support palette + + Q_FOREACH( KDChart::AbstractLayoutItem* layoutItem, d->layoutItems ) { + layoutItem->paint( painter ); + } +#ifdef DEBUG_LEGEND_PAINT + qDebug() << "leaving Legend::paint( QPainter* painter )"; +#endif +} + + +uint Legend::datasetCount() const +{ + int modelLabelsCount = 0; + int modelBrushesCount = 0; + for (int i = 0; i < d->observers.size(); ++i) { + DiagramObserver * obs = d->observers.at(i); + modelLabelsCount += obs->diagram()->datasetLabels().count(); + modelBrushesCount += obs->diagram()->datasetBrushes().count(); + } + Q_ASSERT( modelLabelsCount == modelBrushesCount ); + return modelLabelsCount; +} + + +void Legend::setReferenceArea( const QWidget* area ) +{ + if( area == d->referenceArea ) return; + d->referenceArea = area; + setNeedRebuild(); +} + +const QWidget* Legend::referenceArea() const +{ + //qDebug() << d->referenceArea; + return (d->referenceArea ? d->referenceArea : static_cast(parent())); +} + + +AbstractDiagram* Legend::diagram() const +{ + if( d->observers.isEmpty() ) + return 0; + return d->observers.first()->diagram(); +} + +DiagramList Legend::diagrams() const +{ + DiagramList list; + for (int i = 0; i < d->observers.size(); ++i) + list << d->observers.at(i)->diagram(); + return list; +} + +ConstDiagramList Legend::constDiagrams() const +{ + ConstDiagramList list; + for (int i = 0; i < d->observers.size(); ++i) + list << d->observers.at(i)->diagram(); + return list; +} + +void Legend::addDiagram( AbstractDiagram* newDiagram ) +{ + if ( newDiagram ) + { + DiagramObserver* observer = new DiagramObserver( newDiagram, this ); + + DiagramObserver* oldObs = d->findObserverForDiagram( newDiagram ); + if( oldObs ){ + delete oldObs; + d->observers[ d->observers.indexOf( oldObs ) ] = observer; + }else{ + d->observers.append( observer ); + } + connect( observer, SIGNAL( diagramAboutToBeDestroyed(AbstractDiagram*) ), + SLOT( resetDiagram(AbstractDiagram*) )); + connect( observer, SIGNAL( diagramDataChanged(AbstractDiagram*) ), + SLOT( setNeedRebuild() )); + connect( observer, SIGNAL( diagramDataHidden(AbstractDiagram*) ), + SLOT( setNeedRebuild() )); + connect( observer, SIGNAL( diagramAttributesChanged(AbstractDiagram*) ), + SLOT( setNeedRebuild() )); + setNeedRebuild(); + } +} + +void Legend::removeDiagram( AbstractDiagram* oldDiagram ) +{ + int datasetBrushOffset = 0; + QList diagrams = this->diagrams(); + for(int i =0; i datasetBrushes().count(); i++ ){ + d->brushes.remove(datasetBrushOffset + i); + d->texts.remove(datasetBrushOffset + i); + } + for( int i = 0; i < oldDiagram->datasetPens().count(); i++ ){ + d->pens.remove(datasetBrushOffset + i); + } + break; + } + datasetBrushOffset += diagrams.at(i)->datasetBrushes().count(); + } + + if( oldDiagram ){ + DiagramObserver* oldObs = d->findObserverForDiagram( oldDiagram ); + if( oldObs ){ + //qDebug() << "before delete oldObs;"; + delete oldObs; + //qDebug() << "after delete oldObs;"; + d->observers.removeAt( d->observers.indexOf( oldObs ) ); + //qDebug() << "after d->observers.removeAt()"; + } + setNeedRebuild(); + } +} + +void Legend::removeDiagrams() +{ + // removeDiagram() may change the d->observers list. So, build up the list of + // diagrams to remove first and then remove them one by one. + QList diagrams; + for (int i = 0; i < d->observers.size(); ++i) + diagrams.append( d->observers.at(i)->diagram() ); + for (int i = 0; i < diagrams.count(); ++i) + removeDiagram( diagrams[i] ); +} + +void Legend::replaceDiagram( AbstractDiagram* newDiagram, + AbstractDiagram* oldDiagram ) +{ + KDChart::AbstractDiagram* old = oldDiagram; + if( ! d->observers.isEmpty() && ! old ){ + old = d->observers.first()->diagram(); + if( ! old ) + d->observers.removeFirst(); // first entry had a 0 diagram + } + if( old ) + removeDiagram( old ); + if( newDiagram ) + addDiagram( newDiagram ); +} + +uint Legend::dataSetOffset(KDChart::AbstractDiagram* diagram) +{ + uint offset = 0; + + for (int i = 0; i < d->observers.count(); ++i) + { + if(d->observers.at(i)->diagram() == diagram) + return offset; + + KDChart::AbstractDiagram* diagram = d->observers.at(i)->diagram(); + if(!diagram->model()) + continue; + + offset = offset + diagram->model()->columnCount(); + } + + return offset; +} + +void Legend::setDiagram( KDChart::AbstractDiagram* newDiagram ) +{ + replaceDiagram( newDiagram ); +} + +void Legend::resetDiagram( AbstractDiagram* oldDiagram ) +{ + //qDebug() << oldDiagram; + removeDiagram( oldDiagram ); +} + +void Legend::setVisible( bool visible ) +{ + if( isVisible() == visible ) + return; + QWidget::setVisible( visible ); + emitPositionChanged(); +} + +void Legend::setNeedRebuild() +{ + //qDebug() << "setNeedRebuild()"; + buildLegend(); + sizeHint(); +} + +void Legend::setPosition( Position position ) +{ + if( d->position == position ) + return; + d->position = position; + emitPositionChanged(); +} + +void Legend::emitPositionChanged() +{ + emit positionChanged( this ); + emit propertiesChanged(); +} + + +Position Legend::position() const +{ + return d->position; +} + +void Legend::setAlignment( Qt::Alignment alignment ) +{ + if( d->alignment == alignment ) + return; + d->alignment = alignment; + emitPositionChanged(); +} + +Qt::Alignment Legend::alignment() const +{ + return d->alignment; +} + +void Legend::setTextAlignment( Qt::Alignment alignment ) +{ + if( d->textAlignment == alignment ) + return; + d->textAlignment = alignment; + emitPositionChanged(); +} + +Qt::Alignment Legend::textAlignment() const +{ + return d->textAlignment; +} + +void Legend::setFloatingPosition( const RelativePosition& relativePosition ) +{ + d->position = Position::Floating; + if( d->relativePosition != relativePosition ){ + d->relativePosition = relativePosition; + emitPositionChanged(); + } +} + +const RelativePosition Legend::floatingPosition() const +{ + return d->relativePosition; +} + +void Legend::setOrientation( Qt::Orientation orientation ) +{ + if( d->orientation == orientation ) return; + d->orientation = orientation; + setNeedRebuild(); + emitPositionChanged(); +} + +Qt::Orientation Legend::orientation() const +{ + return d->orientation; +} + +void Legend::setSortOrder( Qt::SortOrder order ) +{ + if( d->order == order ) + return; + d->order = order; + setNeedRebuild(); + emitPositionChanged(); +} + +Qt::SortOrder Legend::sortOrder() const +{ + return d->order; +} + +void Legend::setShowLines( bool legendShowLines ) +{ + if( d->showLines == legendShowLines ) return; + d->showLines = legendShowLines; + setNeedRebuild(); + emitPositionChanged(); +} + +bool Legend::showLines() const +{ + return d->showLines; +} + +void Legend::setUseAutomaticMarkerSize( bool useAutomaticMarkerSize ) +{ + d->useAutomaticMarkerSize = useAutomaticMarkerSize; + setNeedRebuild(); + emitPositionChanged(); +} + +bool Legend::useAutomaticMarkerSize() const +{ + return d->useAutomaticMarkerSize; +} + +/** + \brief Removes all legend texts that might have been set by setText. + + This resets the Legend to default behaviour: Texts are created automatically. +*/ +void Legend::resetTexts() +{ + if( ! d->texts.count() ) return; + d->texts.clear(); + setNeedRebuild(); +} + +void Legend::setText( uint dataset, const QString& text ) +{ + if( d->texts[ dataset ] == text ) return; + d->texts[ dataset ] = text; + setNeedRebuild(); +} + +QString Legend::text( uint dataset ) const +{ + if( d->texts.find( dataset ) != d->texts.end() ){ + return d->texts[ dataset ]; + }else{ + return d->modelLabels[ dataset ]; + } +} + +const QMap Legend::texts() const +{ + return d->texts; +} + +void Legend::setColor( uint dataset, const QColor& color ) +{ + if( d->brushes[ dataset ] == color ) return; + d->brushes[ dataset ] = color; + setNeedRebuild(); + update(); +} + +void Legend::setBrush( uint dataset, const QBrush& brush ) +{ + if( d->brushes[ dataset ] == brush ) return; + d->brushes[ dataset ] = brush; + setNeedRebuild(); + update(); +} + +QBrush Legend::brush( uint dataset ) const +{ + if( d->brushes.find( dataset ) != d->brushes.end() ) + return d->brushes[ dataset ]; + else + return d->modelBrushes[ dataset ]; +} + +const QMap Legend::brushes() const +{ + return d->brushes; +} + + +void Legend::setBrushesFromDiagram( KDChart::AbstractDiagram* diagram ) +{ + bool bChangesDone = false; + QList datasetBrushes = diagram->datasetBrushes(); + for( int i = 0; i < datasetBrushes.count(); i++ ){ + if( d->brushes[ i ] != datasetBrushes[ i ] ){ + d->brushes[ i ] = datasetBrushes[ i ]; + bChangesDone = true; + } + } + if( bChangesDone ) { + setNeedRebuild(); + update(); + } +} + + +void Legend::setPen( uint dataset, const QPen& pen ) +{ + if( d->pens[dataset] == pen ) return; + d->pens[dataset] = pen; + setNeedRebuild(); + update(); +} + +QPen Legend::pen( uint dataset ) const +{ + if( d->pens.find( dataset ) != d->pens.end() ) + return d->pens[dataset]; + else + return d->modelPens[ dataset ]; +} + +const QMap Legend::pens() const +{ + return d->pens; +} + + +void Legend::setMarkerAttributes( uint dataset, const MarkerAttributes& markerAttributes ) +{ + if( d->markerAttributes[dataset] == markerAttributes ) return; + d->markerAttributes[ dataset ] = markerAttributes; + setNeedRebuild(); + update(); +} + +MarkerAttributes Legend::markerAttributes( uint dataset ) const +{ + if( d->markerAttributes.find( dataset ) != d->markerAttributes.end() ) + return d->markerAttributes[ dataset ]; + else if ( static_cast( d->modelMarkers.count() ) > dataset ) + return d->modelMarkers[ dataset ]; + return MarkerAttributes(); +} + +const QMap Legend::markerAttributes() const +{ + return d->markerAttributes; +} + + +void Legend::setTextAttributes( const TextAttributes &a ) +{ + if( d->textAttributes == a ) return; + d->textAttributes = a; + setNeedRebuild(); +} + +TextAttributes Legend::textAttributes() const +{ + return d->textAttributes; +} + +void Legend::setTitleText( const QString& text ) +{ + if( d->titleText == text ) return; + d->titleText = text; + setNeedRebuild(); +} + +QString Legend::titleText() const +{ + return d->titleText; +} + +void Legend::setTitleTextAttributes( const TextAttributes &a ) +{ + if( d->titleTextAttributes == a ) return; + d->titleTextAttributes = a; + setNeedRebuild(); +} + +TextAttributes Legend::titleTextAttributes() const +{ + return d->titleTextAttributes; +} + +void Legend::forceRebuild() +{ +#ifdef DEBUG_LEGEND_PAINT + qDebug() << "entering Legend::forceRebuild()"; +#endif + //setSpacing(d->layout->spacing()); + buildLegend(); +#ifdef DEBUG_LEGEND_PAINT + qDebug() << "leaving Legend::forceRebuild()"; +#endif +} + +void Legend::setSpacing( uint space ) +{ + if( d->spacing == space && d->layout->spacing() == static_cast(space) ) return; + d->spacing = space; + d->layout->setSpacing( space ); + setNeedRebuild(); +} + +uint Legend::spacing() const +{ + return d->spacing; +} + +void Legend::setDefaultColors() +{ + setColor( 0, Qt::red ); + setColor( 1, Qt::green ); + setColor( 2, Qt::blue ); + setColor( 3, Qt::cyan ); + setColor( 4, Qt::magenta ); + setColor( 5, Qt::yellow ); + setColor( 6, Qt::darkRed ); + setColor( 7, Qt::darkGreen ); + setColor( 8, Qt::darkBlue ); + setColor( 9, Qt::darkCyan ); + setColor( 10, Qt::darkMagenta ); + setColor( 11, Qt::darkYellow ); +} + +void Legend::setRainbowColors() +{ + setColor( 0, QColor(255, 0,196) ); + setColor( 1, QColor(255, 0, 96) ); + setColor( 2, QColor(255, 128,64) ); + setColor( 3, Qt::yellow ); + setColor( 4, Qt::green ); + setColor( 5, Qt::cyan ); + setColor( 6, QColor( 96, 96,255) ); + setColor( 7, QColor(160, 0,255) ); + for( int i = 8; i < 16; ++i ) + setColor( i, brush( i - 8 ).color().light() ); +} + +void Legend::setSubduedColors( bool ordered ) +{ +static const int NUM_SUBDUEDCOLORS = 18; +static const QColor SUBDUEDCOLORS[ NUM_SUBDUEDCOLORS ] = { + QColor( 0xe0,0x7f,0x70 ), + QColor( 0xe2,0xa5,0x6f ), + QColor( 0xe0,0xc9,0x70 ), + QColor( 0xd1,0xe0,0x70 ), + QColor( 0xac,0xe0,0x70 ), + QColor( 0x86,0xe0,0x70 ), + QColor( 0x70,0xe0,0x7f ), + QColor( 0x70,0xe0,0xa4 ), + QColor( 0x70,0xe0,0xc9 ), + QColor( 0x70,0xd1,0xe0 ), + QColor( 0x70,0xac,0xe0 ), + QColor( 0x70,0x86,0xe0 ), + QColor( 0x7f,0x70,0xe0 ), + QColor( 0xa4,0x70,0xe0 ), + QColor( 0xc9,0x70,0xe0 ), + QColor( 0xe0,0x70,0xd1 ), + QColor( 0xe0,0x70,0xac ), + QColor( 0xe0,0x70,0x86 ), +}; + if( ordered ) + for(int i=0; ineedRebuild ) { +#ifdef DEBUG_LEGEND_PAINT + qDebug() << "leaving Legend::buildLegend() with NO action (was already build)"; +#endif + // Note: We do *not* need to send positionChanged here, + // because we send it in the resizeEvent, so layouting + // is done at the right time. + return; + } +#ifdef DEBUG_LEGEND_PAINT + qDebug() << "entering Legend::buildLegend() **********************************"; +#endif + d->needRebuild = false; + */ + + Q_FOREACH( QLayoutItem* layoutItem, d->layoutItems ) { + d->layout->removeItem( layoutItem ); + } + qDeleteAll( d->layoutItems ); + d->layoutItems.clear(); + + if( orientation() == Qt::Vertical ) { + d->layout->setColumnStretch( 6, 1 ); + } else { + d->layout->setColumnStretch( 6, 0 ); + } + + d->modelLabels.clear(); + d->modelBrushes.clear(); + d->modelPens.clear(); + d->modelMarkers.clear(); + // retrieve the diagrams' settings for all non-hidden datasets + for (int i = 0; i < d->observers.size(); ++i){ + const AbstractDiagram* diagram = d->observers.at(i)->diagram(); + if( diagram ){ + //qDebug() << "accessing" << diagram; + const QStringList diagramLabels( diagram->datasetLabels() ); + const QList diagramBrushes( diagram->datasetBrushes() ); + const QList diagramPens( diagram->datasetPens() ); + const QList diagramMarkers( diagram->datasetMarkers() ); + const int begin = sortOrder() == Qt::AscendingOrder ? 0 : diagramLabels.count() - 1; + const int end = sortOrder() == Qt::AscendingOrder ? diagramLabels.count() : -1; + for ( int dataset = begin; dataset != end; dataset += begin < end ? 1 : -1 ) + { + // only show the label if the diagrams is NOT having the dataset set to hidden + // and the dataset is not hidden in this legend either + if( !diagram->isHidden( dataset ) && !datasetIsHidden( dataset ) ){ + d->modelLabels += diagramLabels[ dataset ]; + d->modelBrushes += diagramBrushes[ dataset ]; + d->modelPens += diagramPens[ dataset ]; + d->modelMarkers += diagramMarkers[ dataset ]; + } + } + } + } + + Q_ASSERT( d->modelLabels.count() == d->modelBrushes.count() ); + + // legend caption + if( !titleText().isEmpty() && titleTextAttributes().isVisible() ) { + // PENDING(kalle) Other properties! + KDChart::TextLayoutItem* titleItem = + new KDChart::TextLayoutItem( titleText(), + titleTextAttributes(), + referenceArea(), + (orientation() == Qt::Vertical) + ? KDChartEnums::MeasureOrientationMinimum + : KDChartEnums::MeasureOrientationHorizontal, + d->textAlignment ); + titleItem->setParentWidget( this ); + + d->layoutItems << titleItem; + if( orientation() == Qt::Vertical ) + d->layout->addItem( titleItem, 0, 0, 1, 5, Qt::AlignCenter ); + else + d->layout->addItem( titleItem, 0, 0, 1, d->modelLabels.count() ? (d->modelLabels.count()*4) : 1, Qt::AlignCenter ); + + // The line between the title and the legend items, if any. + if( showLines() && d->modelLabels.count() ) { + KDChart::HorizontalLineLayoutItem* lineItem = new KDChart::HorizontalLineLayoutItem(); + d->layoutItems << lineItem; + if( orientation() == Qt::Vertical ){ + d->layout->addItem( lineItem, 1, 0, 1, 5, Qt::AlignCenter ); + }else{ + // we have 1+count*4 columns, because we have both, a leading and a trailing spacer + d->layout->addItem( lineItem, 1, 0, 1, 1+d->modelLabels.count()*4, Qt::AlignCenter ); + } + } + } + + const KDChartEnums::MeasureOrientation orient = + (orientation() == Qt::Vertical) + ? KDChartEnums::MeasureOrientationMinimum + : KDChartEnums::MeasureOrientationHorizontal; + const TextAttributes labelAttrs( textAttributes() ); + const qreal fontHeight = labelAttrs.calculatedFontSize( referenceArea(), orient ); + const LegendStyle style = legendStyle(); + //qDebug() << "fontHeight:" << fontHeight; + + const bool bShowMarkers = (style != LinesOnly); + + QSizeF maxMarkersSize(1.0, 1.0); + QVector markerAttrs( d->modelLabels.count() ); + if( bShowMarkers ){ + for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) { + markerAttrs[dataset] = markerAttributes( dataset ); + QSizeF siz; + if( useAutomaticMarkerSize() || + ! markerAttrs[dataset].markerSize().isValid() ) + { + siz = QSizeF(fontHeight, fontHeight); + markerAttrs[dataset].setMarkerSize( siz ); + }else{ + siz = markerAttrs[dataset].markerSize(); + } + maxMarkersSize = + QSizeF(qMax(maxMarkersSize.width(), siz.width()), + qMax(maxMarkersSize.height(), siz.height())); + } + } + + // If we show a marker on a line, we paint it after 8 pixels + // of the line have been painted. This allows to see the line style + // at the right side of the marker without the line needing to + // be too long. + // (having the marker in the middle of the line would require longer lines) + const int markerOffsOnLine = 8; + + int maxLineLength = 18; + { + bool hasComplexPenStyle = false; + for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ){ + const QPen pn = pen(dataset); + const Qt::PenStyle ps = pn.style(); + if( ps != Qt::NoPen ){ + maxLineLength = qMax( pn.width() * 18, maxLineLength ); + if( ps != Qt::SolidLine ) + hasComplexPenStyle = true; + } + } + if( hasComplexPenStyle && bShowMarkers ) + maxLineLength = + maxLineLength + markerOffsOnLine + + static_cast(maxMarkersSize.width()); + } + +#define ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( column ) \ +{ \ + if( orientation() != Qt::Vertical ) \ + d->layout->addItem( new QSpacerItem( spacing(), 1 ), \ + 2, \ + column ); \ +} + + // Horizontal needs a leading spacer + ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( 0 ) + + // for all datasets: add (line)marker items and text items to the layout + for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) { + KDChart::AbstractLayoutItem* markerLineItem = 0; + // It is possible to set the marker brush both through the MarkerAttributes, + // as well as through the dataset brush set in the diagram, whereas the + // MarkerAttributes are preferred. + const QBrush markerBrush = markerAttrs[dataset].markerColor().isValid() ? + QBrush(markerAttrs[dataset].markerColor()) : brush( dataset ); + switch( style ){ + case( MarkersOnly ): + markerLineItem = new KDChart::MarkerLayoutItem( + diagram(), + markerAttrs[dataset], + markerBrush, + markerAttrs[dataset].pen(), + Qt::AlignLeft ); + break; + case( LinesOnly ): + markerLineItem = new KDChart::LineLayoutItem( + diagram(), + maxLineLength, + pen( dataset ), + Qt::AlignCenter ); + break; + case( MarkersAndLines ): + markerLineItem = new KDChart::LineWithMarkerLayoutItem( + diagram(), + maxLineLength, + pen( dataset ), + markerOffsOnLine, + markerAttrs[dataset], + markerBrush, + markerAttrs[dataset].pen(), + Qt::AlignCenter ); + break; + default: + Q_ASSERT( false ); // all styles need to be handled + } + if( markerLineItem ){ + d->layoutItems << markerLineItem; + if( orientation() == Qt::Vertical ) + d->layout->addItem( markerLineItem, + dataset*2+2, // first row is title, second is line + 1, + 1, 1, Qt::AlignCenter ); + else + d->layout->addItem( markerLineItem, + 2, // all in row two + dataset*4+1 ); + } + + // PENDING(kalle) Other properties! + KDChart::TextLayoutItem* labelItem = + new KDChart::TextLayoutItem( text( dataset ), + labelAttrs, + referenceArea(), orient, + d->textAlignment ); + labelItem->setParentWidget( this ); + + d->layoutItems << labelItem; + if( orientation() == Qt::Vertical ) + d->layout->addItem( labelItem, + dataset*2+2, // first row is title, second is line + 3 ); + else + d->layout->addItem( labelItem, + 2, // all in row two + dataset*4+2 ); + + // horizontal lines (only in vertical mode, and not after the last item) + if( orientation() == Qt::Vertical && showLines() && dataset != d->modelLabels.count()-1 ) { + KDChart::HorizontalLineLayoutItem* lineItem = new KDChart::HorizontalLineLayoutItem(); + d->layoutItems << lineItem; + d->layout->addItem( lineItem, + dataset*2+1+2, + 0, + 1, 5, Qt::AlignCenter ); + } + + // vertical lines (only in horizontal mode, and not after the last item) + if( orientation() == Qt::Horizontal && showLines() && dataset != d->modelLabels.count()-1 ) { + KDChart::VerticalLineLayoutItem* lineItem = new KDChart::VerticalLineLayoutItem(); + d->layoutItems << lineItem; + d->layout->addItem( lineItem, + 2, // all in row two + style == MarkersAndLines ? dataset*4+4 : dataset*4+3 , + 1, 1, Qt::AlignCenter ); + } + + // Horizontal needs a spacer + ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( dataset*4+4 ) + } + + // vertical line (only in vertical mode) + if( orientation() == Qt::Vertical && showLines() && d->modelLabels.count() ) { + KDChart::VerticalLineLayoutItem* lineItem = new KDChart::VerticalLineLayoutItem(); + d->layoutItems << lineItem; + d->layout->addItem( lineItem, 2, 2, d->modelLabels.count()*2, 1 ); + } + + // This line is absolutely necessary, otherwise: #2516. + activateTheLayout(); + + emit propertiesChanged(); + //emit positionChanged( this ); + //emitPositionChanged(); +#ifdef DEBUG_LEGEND_PAINT + qDebug() << "leaving Legend::buildLegend()"; +#endif +} + +void Legend::setHiddenDatasets( const QList hiddenDatasets ) +{ + d->hiddenDatasets = hiddenDatasets; +} + +const QList Legend::hiddenDatasets() const +{ + return d->hiddenDatasets; +} + +void Legend::setDatasetHidden( uint dataset, bool hidden ) +{ + if( hidden && !d->hiddenDatasets.contains( dataset ) ) + d->hiddenDatasets.append( dataset ); + else if( !hidden && d->hiddenDatasets.contains( dataset ) ) + d->hiddenDatasets.removeAll( dataset ); +} + +bool Legend::datasetIsHidden( uint dataset ) const +{ + return d->hiddenDatasets.contains( dataset ); +} + diff --git a/libkdchart/src/KDChartLegend.h b/libkdchart/src/KDChartLegend.h new file mode 100644 index 0000000..c0834e1 --- /dev/null +++ b/libkdchart/src/KDChartLegend.h @@ -0,0 +1,398 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTLEGEND_H +#define KDCHARTLEGEND_H + +#include "KDChartAbstractAreaWidget.h" +#include "KDChartPosition.h" +#include "KDChartMarkerAttributes.h" + +class QTextTable; + +namespace KDChart { + + class AbstractDiagram; + typedef QList DiagramList; + typedef QList ConstDiagramList; + +/** + * @brief Legend defines the interface for the legend drawing class. + * + * Legend is the class for drawing legends for all kinds of diagrams ("chart types"). + * + * Legend is drawn on chart level, not per diagram, but you can have more than one + * legend per chart, using KDChart::Chart::addLegend(). + * + * \note Legend is different from all other classes ofd KD Chart, since it can be + * displayed outside of the Chart's area. If you want to, you can embedd the legend + * into your own widget, or into another part of a bigger grid, into which you might + * have inserted the Chart. + * + * On the other hand, please note that you MUST call Chart::addLegend to get your + * legend positioned into the correct place of your chart - if you want to have + * the legend shown inside of the chart (that's probably true for most cases). + */ +class KDCHART_EXPORT Legend : public AbstractAreaWidget +{ + Q_OBJECT + + Q_DISABLE_COPY( Legend ) + KDCHART_DECLARE_PRIVATE_DERIVED_QWIDGET( Legend ) + +public: + explicit Legend( QWidget* parent = 0 ); + explicit Legend( KDChart::AbstractDiagram* diagram, QWidget* parent ); + virtual ~Legend(); + + + enum LegendStyle { MarkersOnly = 0, + LinesOnly = 1, + MarkersAndLines = 2 }; + + + void setLegendStyle( LegendStyle style ); + LegendStyle legendStyle() const; + + + virtual Legend * clone() const; + + /** + * Returns true if both legends have the same settings. + */ + bool compare( const Legend* other )const; + + //QSize calcSizeHint() const; + virtual void resizeEvent( QResizeEvent * event ); // TODO: should be protected + + virtual void paint( QPainter* painter ); + virtual void setVisible( bool visible ); + + /** + Specifies the reference area for font size of title text, + and for font size of the item texts, IF automatic area + detection is set. + + \note This parameter is ignored, if the Measure given for + setTitleTextAttributes (or setTextAttributes, resp.) is + not specifying automatic area detection. + + If no reference area is specified, but automatic area + detection is set, then the size of the legend's parent + widget will be used. + + \sa KDChart::Measure, KDChartEnums::MeasureCalculationMode + */ + void setReferenceArea( const QWidget* area ); + /** + Returns the reference area, that is used for font size of title text, + and for font size of the item texts, IF automatic area + detection is set. + + \sa setReferenceArea + */ + const QWidget* referenceArea() const; + + /** + * The first diagram of the legend or 0 if there was none added to the legend. + * @return The first diagram of the legend or 0. + * + * \sa diagrams, addDiagram, removeDiagram, removeDiagrams, replaceDiagram, setDiagram + */ + KDChart::AbstractDiagram* diagram() const; + + /** + * The list of all diagrams associated with the legend. + * @return The list of all diagrams associated with the legend. + * + * \sa diagram, addDiagram, removeDiagram, removeDiagrams, replaceDiagram, setDiagram + */ + DiagramList diagrams() const; + + /** + * @return The list of diagrams associated with this coordinate plane. + */ + ConstDiagramList constDiagrams() const; + + /** + * Add the given diagram to the legend. + * @param newDiagram The diagram to add. + * + * \sa diagram, diagrams, removeDiagram, removeDiagrams, replaceDiagram, setDiagram + */ + void addDiagram( KDChart::AbstractDiagram* newDiagram ); + + /** + * Removes the diagram from the legend's list of diagrams. + * + * \sa diagram, diagrams, addDiagram, removeDiagrams, replaceDiagram, setDiagram + */ + void removeDiagram( KDChart::AbstractDiagram* oldDiagram ); + + /** + * Removes all of the diagram from the legend's list of diagrams. + * + * \sa diagram, diagrams, addDiagram, removeDiagram, replaceDiagram, setDiagram + */ + void removeDiagrams(); + + /** + * Replaces the old diagram, or appends the + * new diagram, it there is none yet. + * + * @param newDiagram The diagram to be used instead of the old one. + * If this parameter is zero, the first diagram will just be removed. + * + * @param oldDiagram The diagram to be removed by the new one. This + * diagram will be deleted automatically. If the parameter is omitted, + * the very first diagram will be replaced. In case, there was no + * diagram yet, the new diagram will just be added. + * + * \sa diagram, diagrams, addDiagram, removeDiagram, removeDiagrams, setDiagram + */ + void replaceDiagram( KDChart::AbstractDiagram* newDiagram, + KDChart::AbstractDiagram* oldDiagram = 0 ); + + /** + * Returns the offset of the first dataset of \c diagram. + * + */ + uint dataSetOffset( KDChart::AbstractDiagram* diagram ); + + /** + * @brief A convenience method doing the same as replaceDiagram( newDiagram, 0 ); + * + * Replaces the first diagram by the given diagram. + * If the legend's list of diagram is empty the given diagram is added to the list. + * + * \sa diagram, diagrams, addDiagram, removeDiagram, removeDiagrams, replaceDiagram + */ + void setDiagram( KDChart::AbstractDiagram* newDiagram ); + + /** + * \brief Specify the position of a non-floating legend. + * + * Use setFloatingPosition to set position and alignment + * if your legend is floating. + * + * \sa setAlignment, setFloatingPosition + */ + void setPosition( Position position ); + + /** + * Returns the position of a non-floating legend. + * \sa setPosition + */ + Position position() const; + + /** + * \brief Specify the alignment of a non-floating legend. + * + * Use setFloatingPosition to set position and alignment + * if your legend is floating. + * + * \sa alignment, setPosition, setFloatingPosition + */ + void setAlignment( Qt::Alignment ); + + /** + * Returns the alignment of a non-floating legend. + * \sa setAlignment + */ + Qt::Alignment alignment() const; + + /** + * \brief Specify the alignment of the text elements within the legend + * + * \sa textAlignment() + */ + void setTextAlignment( Qt::Alignment ); + + /** + * \brief Returns the alignment used while rendering text elements within the legend. + * + * \sa setTextAlignment() + */ + Qt::Alignment textAlignment() const; + + /** + * \brief Specify the position and alignment of a floating legend. + * + * Use setPosition and setAlignment to set position and alignment + * if your legend is non-floating. + * + * \note When setFloatingPosition is called, the Legend's position value is set to + * KDChart::Position::Floating automatically. + * + * If your Chart is pointed to by m_chart, your could have the floating legend + * aligned exactly to the chart's coordinate plane's top-right corner + * with the following commands: +\verbatim +KDChart::RelativePosition relativePosition; +relativePosition.setReferenceArea( m_chart->coordinatePlane() ); +relativePosition.setReferencePosition( Position::NorthEast ); +relativePosition.setAlignment( Qt::AlignTop | Qt::AlignRight ); +relativePosition.setHorizontalPadding( + KDChart::Measure( -1.0, KDChartEnums::MeasureCalculationModeAbsolute ) ); +relativePosition.setVerticalPadding( + KDChart::Measure( 0.0, KDChartEnums::MeasureCalculationModeAbsolute ) ); +m_legend->setFloatingPosition( relativePosition ); +\endverbatim + * + * To have the legend positioned at a fixed point, measured from the QPainter's top left corner, + * you could use the following code code: + * +\verbatim +KDChart::RelativePosition relativePosition; +relativePosition.setReferencePoints( PositionPoints( QPointF( 0.0, 0.0 ) ) ); +relativePosition.setReferencePosition( Position::NorthWest ); +relativePosition.setAlignment( Qt::AlignTop | Qt::AlignLeft ); +relativePosition.setHorizontalPadding( + KDChart::Measure( 4.0, KDChartEnums::MeasureCalculationModeAbsolute ) ); +relativePosition.setVerticalPadding( + KDChart::Measure( 4.0, KDChartEnums::MeasureCalculationModeAbsolute ) ); +m_legend->setFloatingPosition( relativePosition ); +\endverbatim + * Actually that's exactly the code KD Chart is using as default position for any floating legends, + * so if you just say setPosition( KDChart::Position::Floating ) without calling setFloatingPosition + * your legend will be positioned at point 4/4. + * + * \sa setPosition, setAlignment + */ + void setFloatingPosition( const RelativePosition& relativePosition ); + + /** + * Returns the position of a floating legend. + * \sa setFloatingPosition + */ + const RelativePosition floatingPosition() const; + + void setOrientation( Qt::Orientation orientation ); + Qt::Orientation orientation() const; + + + void setSortOrder( Qt::SortOrder order ); + Qt::SortOrder sortOrder() const; + + void setShowLines( bool legendShowLines ); + bool showLines() const; + + void resetTexts(); + void setText( uint dataset, const QString& text ); + QString text( uint dataset ) const; + const QMap texts() const; + + /** + * Sets a list of datasets that are to be hidden in the legend. + * + * By passing an empty list, you show all datasets. + * Note that by default, all datasets are shown, which means + * that hiddenDatasets() == QList() + */ + void setHiddenDatasets( const QList hiddenDatasets ); + const QList hiddenDatasets() const; + void setDatasetHidden( uint dataset, bool hidden ); + bool datasetIsHidden( uint dataset ) const; + + uint datasetCount() const; + + void setDefaultColors(); + void setRainbowColors(); + void setSubduedColors( bool ordered = false ); + + void setBrushesFromDiagram( KDChart::AbstractDiagram* diagram ); + + /** + * Note: there is no color() getter method, since setColor + * just sets a QBrush with the respective color, so the + * brush() getter method is sufficient. + */ + void setColor( uint dataset, const QColor& color ); + + void setBrush( uint dataset, const QBrush& brush ); + QBrush brush( uint dataset ) const; + const QMap brushes() const; + + void setPen( uint dataset, const QPen& pen ); + QPen pen( uint dataset ) const; + const QMap pens() const; + + /** + * Note that any sizes specified via setMarkerAttributes are ignored, + * unless you disable the automatic size calculation, by saying + * setUseAutomaticMarkerSize( false ) + */ + void setMarkerAttributes( uint dataset, const MarkerAttributes& ); + MarkerAttributes markerAttributes( uint dataset ) const; + const QMap markerAttributes() const; + + /** + * This option is on by default, it means that Marker sizes in the Legend + * will be the same as the font height used for their respective label texts. + * + * Set this to false, if you want to specify the marker sizes via setMarkerAttributes + * or if you want the Legend to use the same marker sizes as they are used in the Diagrams. + */ + void setUseAutomaticMarkerSize( bool useAutomaticMarkerSize ); + bool useAutomaticMarkerSize() const; + + void setTextAttributes( const TextAttributes &a ); + TextAttributes textAttributes() const; + + void setTitleText( const QString& text ); + QString titleText() const; + + void setTitleTextAttributes( const TextAttributes &a ); + TextAttributes titleTextAttributes() const; + + // FIXME same as frameSettings()->padding()? + void setSpacing( uint space ); + uint spacing() const; + + // called internally by KDChart::Chart, when painting into a custom QPainter + virtual void forceRebuild(); + + virtual QSize minimumSizeHint() const; + virtual QSize sizeHint() const; + virtual void needSizeHint(); + virtual void resizeLayout( const QSize& size ); + +/*public static*/ +// static LegendPosition stringToPosition( QString name, bool* ok=0 ); + +Q_SIGNALS: + void destroyedLegend( Legend* ); + /** Emitted upon change of a property of the Legend or any of its components. */ + void propertiesChanged(); + +private Q_SLOTS: + void emitPositionChanged(); + void resetDiagram( AbstractDiagram* ); + void activateTheLayout(); + void setNeedRebuild(); + void buildLegend(); +}; // End of class Legend + +} + + +#endif // KDCHARTLEGEND_H diff --git a/libkdchart/src/KDChartLegend_p.h b/libkdchart/src/KDChartLegend_p.h new file mode 100644 index 0000000..6247456 --- /dev/null +++ b/libkdchart/src/KDChartLegend_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTLEGEND_P_H +#define KDCHARTLEGEND_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartLegend.h" +#include +#include "KDChartAbstractAreaWidget_p.h" +#include +#include +#include +#include +#include +#include + +#include + + +class QGridLayout; +class KDTextDocument; +class QTextDocument; + +namespace KDChart { + class AbstractDiagram; + class DiagramObserver; + class AbstractLayoutItem; + + class DiagramsObserversList : public QList {}; +} + +/** + * \internal + */ +class KDChart::Legend::Private : public KDChart::AbstractAreaWidget::Private +{ + friend class KDChart::Legend; +public: + Private(); + ~Private(); + + Private( const Private& rhs ) : + AbstractAreaWidget::Private( rhs ), + referenceArea( 0 ), + position( rhs.position ), + alignment( rhs.alignment ), + textAlignment( rhs.textAlignment ), + relativePosition( rhs.relativePosition ), + orientation( rhs.orientation ), + order( rhs.order ), + showLines( rhs.showLines ), + texts( rhs.texts ), + brushes( rhs.brushes ), + pens( rhs.pens ), + markerAttributes( rhs.markerAttributes ), + textAttributes( rhs.textAttributes ), + titleText( rhs.titleText ), + titleTextAttributes( rhs.titleTextAttributes ), + spacing( rhs.spacing ), + useAutomaticMarkerSize( rhs.useAutomaticMarkerSize ), + legendStyle( MarkersOnly ) + //needRebuild( true ) + { + } + + DiagramObserver* findObserverForDiagram( AbstractDiagram* diagram ) + { + for (int i = 0; i < observers.size(); ++i) { + DiagramObserver * obs = observers.at(i); + if( obs->diagram() == diagram ) + return obs; + } + return 0; + } + +private: + // user-settable + const QWidget* referenceArea; + Position position; + Qt::Alignment alignment; + Qt::Alignment textAlignment; + RelativePosition relativePosition; + Qt::Orientation orientation; + Qt::SortOrder order; + bool showLines; + QMap texts; + QMap brushes; + QMap pens; + QMap markerAttributes; + QList hiddenDatasets; + TextAttributes textAttributes; + QString titleText; + TextAttributes titleTextAttributes; + uint spacing; + bool useAutomaticMarkerSize; + LegendStyle legendStyle; + + // internal +// bool needRebuild; + mutable QStringList modelLabels; + mutable QList modelBrushes; + mutable QList modelPens; + mutable QList modelMarkers; + mutable QSize cachedSizeHint; + //QVector layoutItems; + QVector layoutItems; + QGridLayout* layout; + DiagramsObserversList observers; +}; + +inline KDChart::Legend::Legend( Private* p, QWidget* parent ) + : AbstractAreaWidget( p, parent ) { init(); } +inline KDChart::Legend::Private * KDChart::Legend::d_func() +{ return static_cast( AbstractAreaWidget::d_func() ); } +inline const KDChart::Legend::Private * KDChart::Legend::d_func() const +{ return static_cast( AbstractAreaWidget::d_func() ); } + + + + +#endif /* KDCHARTLEGEND_P_H */ + diff --git a/libkdchart/src/KDChartLineAttributes.cpp b/libkdchart/src/KDChartLineAttributes.cpp new file mode 100644 index 0000000..4b653e2 --- /dev/null +++ b/libkdchart/src/KDChartLineAttributes.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartLineAttributes.h" +#include + +#include + +#define d d_func() + +using namespace KDChart; + +class LineAttributes::Private +{ + friend class LineAttributes; +public: + Private(); + +private: + //Areas + MissingValuesPolicy missingValuesPolicy; + bool displayArea; + uint transparency; + int areaBoundingDataset; +}; + + +LineAttributes::Private::Private() + : missingValuesPolicy( MissingValuesAreBridged ) + , displayArea( false ) + , transparency( 255 ) + , areaBoundingDataset( -1 ) +{ +} + + +LineAttributes::LineAttributes() + : _d( new Private() ) +{ +} + +LineAttributes::LineAttributes( const LineAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +LineAttributes& LineAttributes::operator= ( const LineAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +LineAttributes::~LineAttributes() +{ + delete _d; _d = 0; +} + +bool LineAttributes::operator==( const LineAttributes& r ) const +{ + return + missingValuesPolicy() == r.missingValuesPolicy() && + displayArea() == r.displayArea() && + transparency() == r.transparency() && + areaBoundingDataset() == r.areaBoundingDataset(); +} + +void LineAttributes::setMissingValuesPolicy( MissingValuesPolicy policy ) +{ + d->missingValuesPolicy = policy; +} + +LineAttributes::MissingValuesPolicy LineAttributes::missingValuesPolicy() const +{ + return d->missingValuesPolicy; +} + +void LineAttributes::setDisplayArea( bool display ) +{ + d->displayArea = display; +} + +bool LineAttributes::displayArea() const +{ + return d->displayArea; +} + +void LineAttributes::setTransparency( uint alpha ) +{ + if ( alpha > 255 ) + alpha = 255; + d->transparency = alpha; +} + +uint LineAttributes::transparency() const +{ + return d->transparency; +} + +void LineAttributes::setAreaBoundingDataset( int dataset ) +{ + d->areaBoundingDataset = dataset; +} + +int LineAttributes::areaBoundingDataset() const +{ + return d->areaBoundingDataset; +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::LineAttributes& a) +{ + dbg << "KDChart::LineAttributes(" + // MissingValuesPolicy missingValuesPolicy; + << "bool="< +#include "KDChartGlobal.h" + +namespace KDChart { + +/** + * @brief Set of attributes for changing the appearance of line charts + */ +class KDCHART_EXPORT LineAttributes +{ +public: + /** + \brief MissingValuesPolicy specifies how a missing value will be shown in a line diagram. + + Missing value is assumed if the data cell contains a QVariant that can not be + interpreted as a double, or if the data cell is hidden while its dataset is not hidden. + + \li \c MissingValuesAreBridged the default: No markers will be shown for missing values + but the line will be bridged if there is at least one valid cell before and after + the missing value(s), otherwise the segment will be hidden. + \li \c MissingValuesHideSegments Line segments starting with a missing value will + not be shown, and no markers will be shown for missing values, so this will look like + a piece of the line is missing. + \li \c MissingValuesShownAsZero Missing value(s) will be treated like normal zero values, + and markers will shown for them too, so there will be no visible difference between a + zero value and a missing value. + \li \c MissingValuesPolicyIgnored (internal value, do not use) + + */ + enum MissingValuesPolicy { + MissingValuesAreBridged, + MissingValuesHideSegments, + MissingValuesShownAsZero, + MissingValuesPolicyIgnored }; + + LineAttributes(); + LineAttributes( const LineAttributes& ); + LineAttributes &operator= ( const LineAttributes& ); + + ~LineAttributes(); + + /* line chart and area chart - all types */ + void setMissingValuesPolicy( MissingValuesPolicy policy ); + MissingValuesPolicy missingValuesPolicy() const; + + /* area chart - all types */ + /** + * Sets the lower or upper (depending on the displayed value being positive or + * negative, resp.) bounding line (i.e., the dataset with the line data). The area + * is then drawn between this line and the line of the specified dataset. + * Pass -1 to draw the area between this line and the zero line. + */ + void setAreaBoundingDataset( int dataset ); + int areaBoundingDataset() const; + + void setDisplayArea( bool display ); + bool displayArea() const; + /*allows viewing the covered areas*/ + void setTransparency( uint alpha ); + uint transparency() const; + + bool operator==( const LineAttributes& ) const; + inline bool operator!=( const LineAttributes& other ) const { return !operator==(other); } + +private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( LineAttributes ) +}; // End of class LineAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::LineAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::LineAttributes ) +Q_DECLARE_METATYPE( KDChart::LineAttributes ) +Q_DECLARE_TYPEINFO( KDChart::LineAttributes, Q_MOVABLE_TYPE ); + + +#endif // KDCHARTLINEATTRIBUTES_H diff --git a/libkdchart/src/KDChartLineDiagram.cpp b/libkdchart/src/KDChartLineDiagram.cpp new file mode 100644 index 0000000..bc29a70 --- /dev/null +++ b/libkdchart/src/KDChartLineDiagram.cpp @@ -0,0 +1,478 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartLineDiagram.h" +#include "KDChartLineDiagram_p.h" + +#include "KDChartBarDiagram.h" +#include "KDChartPalette.h" +#include "KDChartPosition.h" +#include "KDChartTextAttributes.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractGrid.h" +#include "KDChartDataValueAttributes.h" + +#include + +#include "KDChartNormalLineDiagram_p.h" +#include "KDChartStackedLineDiagram_p.h" +#include "KDChartPercentLineDiagram_p.h" + +#include +#include +#include +#include +#include +#include + +using namespace KDChart; + +LineDiagram::Private::Private() +{ +} + +LineDiagram::Private::~Private() {} + + +#define d d_func() + + +LineDiagram::LineDiagram( QWidget* parent, CartesianCoordinatePlane* plane ) : + AbstractCartesianDiagram( new Private(), parent, plane ) +{ + init(); +} + +void LineDiagram::init() +{ + d->diagram = this; + d->normalDiagram = new NormalLineDiagram( this ); + d->stackedDiagram = new StackedLineDiagram( this ); + d->percentDiagram = new PercentLineDiagram( this ); + d->implementor = d->normalDiagram; + d->centerDataPoints = false; + d->reverseDatasetOrder = false; +} + +LineDiagram::~LineDiagram() +{ +} + +/** + * Creates an exact copy of this diagram. + */ +LineDiagram * LineDiagram::clone() const +{ + LineDiagram* newDiagram = new LineDiagram( new Private( *d ) ); + newDiagram->setType( type() ); + return newDiagram; +} + + +bool LineDiagram::compare( const LineDiagram* other )const +{ + if( other == this ) return true; + if( ! other ){ + return false; + } + /* + qDebug() <<"\n LineDiagram::compare():"; + // compare own properties + qDebug() << (type() == other->type()); + */ + return // compare the base class + ( static_cast(this)->compare( other ) ) && + // compare own properties + (type() == other->type()) && + (centerDataPoints() == other->centerDataPoints()) && + (reverseDatasetOrder() == other->reverseDatasetOrder()); +} + +/** + * Sets the line diagram's type to \a type + * \sa LineDiagram::LineType + */ +void LineDiagram::setType( const LineType type ) +{ + if ( d->implementor->type() == type ) return; + if ( type != LineDiagram::Normal && datasetDimension() > 1 ) { + Q_ASSERT_X ( false, "setType()", + "This line chart type can't be used with multi-dimensional data." ); + return; + } + switch( type ) { + case Normal: + d->implementor = d->normalDiagram; + break; + case Stacked: + d->implementor = d->stackedDiagram; + break; + case Percent: + d->implementor = d->percentDiagram; + break; + default: + Q_ASSERT_X( false, "LineDiagram::setType", "unknown diagram subtype" ); + }; + + // d->lineType = type; + Q_ASSERT( d->implementor->type() == type ); + + // AbstractAxis settings - see AbstractDiagram and CartesianAxis + setPercentMode( type == LineDiagram::Percent ); + setDataBoundariesDirty(); + emit layoutChanged( this ); + emit propertiesChanged(); +} + +/** + * @return the type of the line diagram + */ +LineDiagram::LineType LineDiagram::type() const +{ + return d->implementor->type(); +} + +void LineDiagram::setCenterDataPoints( bool center ) +{ + d->centerDataPoints = center; + emit propertiesChanged(); +} + +bool LineDiagram::centerDataPoints() const +{ + return d->centerDataPoints; +} + +void LineDiagram::setReverseDatasetOrder( bool reverse ) +{ + d->reverseDatasetOrder = reverse; +} + +bool LineDiagram::reverseDatasetOrder() const +{ + return d->reverseDatasetOrder; +} + +/** + * Sets the global line attributes to \a la + */ +void LineDiagram::setLineAttributes( const LineAttributes& la ) +{ + d->attributesModel->setModelData( + qVariantFromValue( la ), + LineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Sets the line attributes of data set \a column to \a la + */ +void LineDiagram::setLineAttributes( + int column, + const LineAttributes& la ) +{ + d->setDatasetAttrs( column, qVariantFromValue( la ), LineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Resets the line attributes of data set \a column + */ +void LineDiagram::resetLineAttributes( int column ) +{ + d->resetDatasetAttrs( column, LineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Sets the line attributes for the model index \a index to \a la + */ +void LineDiagram::setLineAttributes( + const QModelIndex& index, + const LineAttributes& la ) +{ + d->attributesModel->setData( + d->attributesModel->mapFromSource(index), + qVariantFromValue( la ), + LineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Remove any explicit line attributes settings that might have been specified before. + */ +void LineDiagram::resetLineAttributes( const QModelIndex & index ) +{ + d->attributesModel->resetData( + d->attributesModel->mapFromSource(index), LineAttributesRole ); + emit propertiesChanged(); +} + +/** + * @return the global line attribute set + */ +LineAttributes LineDiagram::lineAttributes() const +{ + return qVariantValue( + d->attributesModel->data( KDChart::LineAttributesRole ) ); +} + +/** + * @return the line attribute set of data set \a column + */ +LineAttributes LineDiagram::lineAttributes( int column ) const +{ + const QVariant attrs( d->datasetAttrs( column, LineAttributesRole ) ); + if( attrs.isValid() ) + return qVariantValue< LineAttributes >( attrs ); + return lineAttributes(); +} + +/** + * @return the line attribute set of the model index \a index + */ +LineAttributes LineDiagram::lineAttributes( + const QModelIndex& index ) const +{ + return qVariantValue( + d->attributesModel->data( + d->attributesModel->mapFromSource(index), + KDChart::LineAttributesRole ) ); +} + +/** + * Sets the global 3D line attributes to \a la + */ +void LineDiagram::setThreeDLineAttributes( + const ThreeDLineAttributes& la ) +{ + setDataBoundariesDirty(); + d->attributesModel->setModelData( + qVariantFromValue( la ), + ThreeDLineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Sets the 3D line attributes of data set \a column to \a ta + */ +void LineDiagram::setThreeDLineAttributes( + int column, + const ThreeDLineAttributes& la ) +{ + setDataBoundariesDirty(); + d->setDatasetAttrs( column, qVariantFromValue( la ), ThreeDLineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Sets the 3D line attributes of model index \a index to \a la + */ +void LineDiagram::setThreeDLineAttributes( + const QModelIndex & index, + const ThreeDLineAttributes& la ) +{ + setDataBoundariesDirty(); + d->attributesModel->setData( + d->attributesModel->mapFromSource(index), + qVariantFromValue( la ), + ThreeDLineAttributesRole ); + emit propertiesChanged(); +} + +/** + * @return the global 3D line attributes + */ +ThreeDLineAttributes LineDiagram::threeDLineAttributes() const +{ + return qVariantValue( + d->attributesModel->data( KDChart::ThreeDLineAttributesRole ) ); +} + +/** + * @return the 3D line attributes of data set \a column + */ +ThreeDLineAttributes LineDiagram::threeDLineAttributes( int column ) const +{ + const QVariant attrs( d->datasetAttrs( column, ThreeDLineAttributesRole ) ); + if( attrs.isValid() ) + return qVariantValue< ThreeDLineAttributes >( attrs ); + return threeDLineAttributes(); +} + +/** + * @return the 3D line attributes of the model index \a index + */ +ThreeDLineAttributes LineDiagram::threeDLineAttributes( const QModelIndex& index ) const +{ + return qVariantValue( + d->attributesModel->data( + d->attributesModel->mapFromSource( index ), + KDChart::ThreeDLineAttributesRole ) ); +} + +double LineDiagram::threeDItemDepth( const QModelIndex& index ) const +{ + return threeDLineAttributes( index ).validDepth(); +} + +double LineDiagram::threeDItemDepth( int column ) const +{ + return qVariantValue( + d->datasetAttrs( column, KDChart::ThreeDLineAttributesRole ) ).validDepth(); +} + +/** + * Sets the value tracker attributes of the model index \a index to \a va + */ +void LineDiagram::setValueTrackerAttributes( const QModelIndex & index, + const ValueTrackerAttributes & va ) +{ + d->attributesModel->setData( d->attributesModel->mapFromSource(index), + qVariantFromValue( va ), + KDChart::ValueTrackerAttributesRole ); + emit propertiesChanged(); +} + +/** + * Returns the value tracker attributes of the model index \a index + */ +ValueTrackerAttributes LineDiagram::valueTrackerAttributes( + const QModelIndex & index ) const +{ + return qVariantValue( d->attributesModel->data( + d->attributesModel->mapFromSource( index ), + KDChart::ValueTrackerAttributesRole ) ); +} + +void LineDiagram::resizeEvent ( QResizeEvent* ) +{ +} + +const QPair LineDiagram::calculateDataBoundaries() const +{ + d->compressor.setResolution( static_cast( this->size().width() * coordinatePlane()->zoomFactorX() ), + static_cast( this->size().height() * coordinatePlane()->zoomFactorY() ) ); + + if ( !checkInvariants( true ) ) return QPair( QPointF( 0, 0 ), QPointF( 0, 0 ) ); + + // note: calculateDataBoundaries() is ignoring the hidden flags. + // That's not a bug but a feature: Hiding data does not mean removing them. + // For totally removing data from KD Chart's view people can use e.g. a proxy model ... + + // calculate boundaries for different line types Normal - Stacked - Percent - Default Normal + return d->implementor->calculateDataBoundaries(); +} + + +void LineDiagram::paintEvent ( QPaintEvent*) +{ +//qDebug() << "starting LineDiagram::paintEvent ( QPaintEvent*)"; + QPainter painter ( viewport() ); + PaintContext ctx; + ctx.setPainter ( &painter ); + ctx.setRectangle ( QRectF ( 0, 0, width(), height() ) ); + paint ( &ctx ); +//qDebug() << " LineDiagram::paintEvent ( QPaintEvent*) ended."; +} + + +double LineDiagram::valueForCellTesting( int row, int column, + bool& bOK, + bool showHiddenCellsAsInvalid ) const +{ + double value; + if( showHiddenCellsAsInvalid && isHidden( model()->index( row, column, rootIndex() ) ) ) + bOK = false; + else + value = d->attributesModel->data( + d->attributesModel->index( row, column, attributesModelRootIndex() ) + ).toDouble( &bOK ); + return bOK ? value : 0.0; +} + +LineAttributes::MissingValuesPolicy LineDiagram::getCellValues( + int row, int column, + bool shiftCountedXValuesByHalfSection, + double& valueX, double& valueY ) const +{ + LineAttributes::MissingValuesPolicy policy; + + bool bOK = true; + valueX = ( datasetDimension() > 1 && column > 0 ) + ? valueForCellTesting( row, column-1, bOK, true ) + : ((shiftCountedXValuesByHalfSection ? 0.5 : 0.0) + row); + if( bOK ) + valueY = valueForCellTesting( row, column, bOK, true ); + if( bOK ){ + policy = LineAttributes::MissingValuesPolicyIgnored; + }else{ + // missing value: find out the policy + QModelIndex index = model()->index( row, column, rootIndex() ); + LineAttributes la = lineAttributes( index ); + policy = la.missingValuesPolicy(); + } + return policy; +} + +void LineDiagram::paint( PaintContext* ctx ) +{ + // note: Not having any data model assigned is no bug + // but we can not draw a diagram then either. + if ( !checkInvariants( true ) ) return; + if ( !AbstractGrid::isBoundariesValid(dataBoundaries()) ) return; + const PainterSaver p( ctx->painter() ); + if( model()->rowCount( rootIndex() ) == 0 || model()->columnCount( rootIndex() ) == 0 ) + return; // nothing to paint for us + + AbstractCoordinatePlane* const plane = ctx->coordinatePlane(); + ctx->setCoordinatePlane( plane->sharedAxisMasterPlane( ctx->painter() ) ); + + + // paint different line types Normal - Stacked - Percent - Default Normal + d->implementor->paint( ctx ); + + ctx->setCoordinatePlane( plane ); +} + +void LineDiagram::resize ( const QSizeF& size ) +{ + d->compressor.setResolution( static_cast( size.width() * coordinatePlane()->zoomFactorX() ), + static_cast( size.height() * coordinatePlane()->zoomFactorY() ) ); + setDataBoundariesDirty(); +} + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +int LineDiagram::numberOfAbscissaSegments () const +{ + return d->attributesModel->rowCount(attributesModelRootIndex()); +} + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +int LineDiagram::numberOfOrdinateSegments () const +{ + return d->attributesModel->columnCount(attributesModelRootIndex()); +} diff --git a/libkdchart/src/KDChartLineDiagram.h b/libkdchart/src/KDChartLineDiagram.h new file mode 100644 index 0000000..59a4771 --- /dev/null +++ b/libkdchart/src/KDChartLineDiagram.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTLINEDIAGRAM_H +#define KDCHARTLINEDIAGRAM_H + +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartLineAttributes.h" +#include "KDChartValueTrackerAttributes.h" + +class QPainter; +class QPolygonF; + +namespace KDChart { + + class ThreeDLineAttributes; + +/** + * @brief LineDiagram defines a common line diagram. + * + * It provides different subtypes which are set using \a setType. + */ +class KDCHART_EXPORT LineDiagram : public AbstractCartesianDiagram +{ + Q_OBJECT + + Q_DISABLE_COPY( LineDiagram ) +// KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( LineDiagram, CartesianCoordinatePlane * ) + + KDCHART_DECLARE_DERIVED_DIAGRAM( LineDiagram, CartesianCoordinatePlane ) + + +public: + class LineDiagramType; + friend class LineDiagramType; + + explicit LineDiagram( QWidget* parent = 0, CartesianCoordinatePlane* plane = 0 ); + virtual ~LineDiagram(); + + virtual LineDiagram * clone() const; + + /** + * Returns true if both diagrams have the same settings. + */ + bool compare( const LineDiagram* other ) const; + + enum LineType { + Normal = 0, + Stacked = 1, + Percent = 2 + }; + + + void setType( const LineType type ); + LineType type() const; + + /** If centerDataPoints() is true, all data points are moved by an + * offset of 0.5 to the right. This is useful in conjunction with + * bar diagrams, since data points are then centered just like bars. + * + * \sa centerDataPoints() + */ + void setCenterDataPoints( bool center ); + /** @return option set by setCenterDataPoints() */ + bool centerDataPoints() const; + + /** With this property set to true, data sets in a normal line diagram + * are drawn in reversed order. More clearly, the first (top-most) data set + * in the source model will then appear in front. This is mostly due to + * historical reasons. + */ + void setReverseDatasetOrder( bool reverse ); + /** \see setReverseDatasetOrder */ + bool reverseDatasetOrder() const; + + void setLineAttributes( const LineAttributes & a ); + void setLineAttributes( int column, const LineAttributes & a ); + void setLineAttributes( const QModelIndex & index, const LineAttributes & a ); + void resetLineAttributes( int column ); + void resetLineAttributes( const QModelIndex & index ); + LineAttributes lineAttributes() const; + LineAttributes lineAttributes( int column ) const; + LineAttributes lineAttributes( const QModelIndex & index ) const; + + void setThreeDLineAttributes( const ThreeDLineAttributes & a ); + void setThreeDLineAttributes( int column, const ThreeDLineAttributes & a ); + void setThreeDLineAttributes( const QModelIndex & index, + const ThreeDLineAttributes & a ); + + //FIXME(khz): big TODO(khz): add a lot of reset...Attributes() methods to all + // appropriate places, for 2.1 (that is: after we have release 2.0.2) :-) + + ThreeDLineAttributes threeDLineAttributes() const; + ThreeDLineAttributes threeDLineAttributes( int column ) const; + ThreeDLineAttributes threeDLineAttributes( const QModelIndex & index ) const; + + void setValueTrackerAttributes( const QModelIndex & index, + const ValueTrackerAttributes & a ); + ValueTrackerAttributes valueTrackerAttributes( const QModelIndex & index ) const; + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + // implement AbstractCartesianDiagram + /* reimpl */ + const int numberOfAbscissaSegments () const; + /* reimpl */ + const int numberOfOrdinateSegments () const; +#else + // implement AbstractCartesianDiagram + /* reimpl */ + int numberOfAbscissaSegments () const; + /* reimpl */ + int numberOfOrdinateSegments () const; +#endif + +protected: + void paint ( PaintContext* paintContext ); + +public: + void resize ( const QSizeF& area ); + +protected: + // FIXME what does that mean? + double valueForCellTesting( int row, int column, + bool& bOK, + bool showHiddenCellsAsInvalid = false ) const; + LineAttributes::MissingValuesPolicy getCellValues( + int row, int column, + bool shiftCountedXValuesByHalfSection, + double& valueX, double& valueY ) const; + + virtual double threeDItemDepth( const QModelIndex & index ) const; + virtual double threeDItemDepth( int column ) const; + /** \reimpl */ + virtual const QPair calculateDataBoundaries() const; + void paintEvent ( QPaintEvent* ); + void resizeEvent ( QResizeEvent* ); +}; // End of class KDChartLineDiagram + +} + +#endif // KDCHARTLINEDIAGRAM_H diff --git a/libkdchart/src/KDChartLineDiagram_p.cpp b/libkdchart/src/KDChartLineDiagram_p.cpp new file mode 100644 index 0000000..fc97d5e --- /dev/null +++ b/libkdchart/src/KDChartLineDiagram_p.cpp @@ -0,0 +1,348 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartLineDiagram.h" +#include "KDChartDataValueAttributes.h" + +#include "KDChartLineDiagram_p.h" + +using namespace KDChart; +using namespace std; + +LineDiagram::Private::Private( const Private& rhs ) + : AbstractCartesianDiagram::Private( rhs ) +{ +} + +void LineDiagram::Private::paintPolyline( + PaintContext* ctx, + const QBrush& brush, const QPen& pen, + const QPolygonF& points ) const +{ + ctx->painter()->setBrush( brush ); + ctx->painter()->setPen( PrintingParameters::scalePen( + QPen( pen.color(), + pen.width(), + pen.style(), + Qt::FlatCap, + Qt::MiterJoin ) ) ); +#if QT_VERSION > 0x040299 + ctx->painter()->drawPolyline( points ); +#else + // FIXME (Mirko) verify, this sounds reverse-logical + // For Qt versions older than 4.3 drawPolyline is VERY slow + // so we use traditional line segments drawing instead then. + for (int i = 0; i < points.size()-1; ++i) + ctx->painter()->drawLine( points.at(i), points.at(i+1) ); +#endif +} + +/*! + Projects a point in a space defined by its x, y, and z coordinates + into a point onto a plane, given two rotation angles around the x + resp. y axis. +*/ +const QPointF LineDiagram::LineDiagramType::project( + QPointF point, QPointF maxLimits, + double z, const QModelIndex& index ) const +{ + Q_UNUSED( maxLimits ); + ThreeDLineAttributes td = diagram()->threeDLineAttributes( index ); + + //Pending Michel FIXME - the rotation does not work as expected atm + double xrad = DEGTORAD( td.lineXRotation() ); + double yrad = DEGTORAD( td.lineYRotation() ); + QPointF ret = QPointF(point.x()*cos( yrad ) + z * sin( yrad ) , point.y()*cos( xrad ) - z * sin( xrad ) ); + return ret; +} + +void LineDiagram::LineDiagramType::paintThreeDLines( + PaintContext* ctx, const QModelIndex& index, + const QPointF& from, const QPointF& to, const double depth ) +{ + // retrieve the boundaries + const QPair< QPointF, QPointF > boundaries = diagram()->dataBoundaries(); + const QPointF& maxLimits = boundaries.second; + const QPointF topLeft = project( from, maxLimits, depth, index ); + const QPointF topRight = project ( to, maxLimits, depth, index ); + + const QPolygonF segment = QPolygonF() << from << topLeft << topRight << to; + const QBrush indexBrush ( diagram()->brush( index ) ); + const PainterSaver painterSaver( ctx->painter() ); + + if( diagram()->antiAliasing() ) + ctx->painter()->setRenderHint( QPainter::Antialiasing ); + + ctx->painter()->setBrush( indexBrush ); + ctx->painter()->setPen( PrintingParameters::scalePen( diagram()->pen( index ) ) ); + + reverseMapper().addPolygon( index.row(), index.column(), segment ); + ctx->painter()->drawPolygon( segment ); +} + +// this method is factored out from LineDiagram::paint, and contains +// the common parts of the method that previously implemented all +// chart types in one +void LineDiagram::LineDiagramType::paintElements( + PaintContext* ctx, + DataValueTextInfoList& list, + LineAttributesInfoList& lineList, + LineAttributes::MissingValuesPolicy policy ) +{ + Q_UNUSED( policy ); + // paint all lines and their attributes + const PainterSaver painterSaver( ctx->painter() ); + if ( diagram()->antiAliasing() ) + ctx->painter()->setRenderHint ( QPainter::Antialiasing ); + LineAttributesInfoListIterator itline ( lineList ); + + QBrush curBrush; + QPen curPen; + QPolygonF points; + while ( itline.hasNext() ) { + const LineAttributesInfo& lineInfo = itline.next(); + const QModelIndex& index = lineInfo.index; + const ThreeDLineAttributes td = diagram()->threeDLineAttributes( index ); + const ValueTrackerAttributes vt = diagram()->valueTrackerAttributes( index ); + + if( td.isEnabled() ){ + paintThreeDLines( ctx, index, lineInfo.value, lineInfo.nextValue, td.depth() ); + } else { + const QBrush br( diagram()->brush( index ) ); + const QPen pn( diagram()->pen( index ) ); + if( points.count() && points.last() == lineInfo.value && curBrush == br && curPen == pn ) { + // line goes from last value in points to lineInfo.nextValue + reverseMapper().addLine( lineInfo.index.row(), lineInfo.index.column(), points.last(), lineInfo.nextValue ); + points << lineInfo.nextValue; + } else { + if( points.count() ) + paintPolyline( ctx, curBrush, curPen, points ); + curBrush = br; + curPen = pn; + points.clear(); + // line goes from lineInfo.value to lineInfo,nextValue + reverseMapper().addLine( lineInfo.index.row(), lineInfo.index.column(), lineInfo.value, lineInfo.nextValue ); + points << lineInfo.value << lineInfo.nextValue; + } + } + } + if( points.count() ) + paintPolyline( ctx, curBrush, curPen, points ); + + itline.toFront(); + while ( itline.hasNext() ) { + const LineAttributesInfo& lineInfo = itline.next(); + const QModelIndex& index = lineInfo.index; + const ValueTrackerAttributes vt = diagram()->valueTrackerAttributes( index ); + if( vt.isEnabled() ) + paintValueTracker( ctx, vt, lineInfo.value ); + } + + // paint all data value texts and the point markers + paintDataValueTextsAndMarkers( diagram(), ctx, list, true ); +} + +AttributesModel* LineDiagram::LineDiagramType::attributesModel() const +{ + return m_private->attributesModel; +} + +QModelIndex LineDiagram::LineDiagramType::attributesModelRootIndex() const +{ + return m_private->diagram->attributesModelRootIndex(); +} + +int LineDiagram::LineDiagramType::datasetDimension() const +{ + return m_private->datasetDimension; +} + +ReverseMapper& LineDiagram::LineDiagramType::reverseMapper() +{ + return m_private->reverseMapper; +} + +LineAttributes::MissingValuesPolicy LineDiagram::LineDiagramType::getCellValues( + int row, int column, + bool shiftCountedXValuesByHalfSection, + double& valueX, double& valueY ) const +{ + return m_private->diagram->getCellValues( row, column, shiftCountedXValuesByHalfSection, + valueX, valueY ); +} + +double LineDiagram::LineDiagramType::valueForCellTesting( + int row, int column, + bool& bOK, + bool showHiddenCellsAsInvalid) const +{ + return m_private->diagram->valueForCellTesting( row, column, bOK, showHiddenCellsAsInvalid ); +} + +LineDiagram* LineDiagram::LineDiagramType::diagram() const +{ + return m_private->diagram; +} + +void LineDiagram::LineDiagramType::paintAreas( + PaintContext* ctx, + const QModelIndex& index, const QList< QPolygonF >& areas, + const uint transparency ) +{ + QColor trans = diagram()->brush( index ).color(); + trans.setAlpha( transparency ); + QPen indexPen = diagram()->pen(index); + indexPen.setColor( trans ); + const PainterSaver painterSaver( ctx->painter() ); + + if( diagram()->antiAliasing() ) + ctx->painter()->setRenderHint( QPainter::Antialiasing ); + + ctx->painter()->setPen( PrintingParameters::scalePen( indexPen ) ); + ctx->painter()->setBrush( trans ); + + QPainterPath path; + for( int i = 0; i < areas.count(); ++i ) + { + const QPolygonF& p = areas[ i ]; + path.addPolygon( p ); + reverseMapper().addPolygon( index.row(), index.column(), p ); + path.closeSubpath(); + } + ctx->painter()->drawPath( path ); +} + +double LineDiagram::LineDiagramType::valueForCell( int row, int column ) +{ + return diagram()->valueForCell( row, column ); +} + +void LineDiagram::LineDiagramType::appendDataValueTextInfoToList( + AbstractDiagram * diagram, + DataValueTextInfoList & list, + const QModelIndex & index, + const CartesianDiagramDataCompressor::CachePosition * position, + const PositionPoints& points, + const Position& autoPositionPositive, + const Position& autoPositionNegative, + const qreal value ) +{ + Q_UNUSED( autoPositionNegative ); + m_private->appendDataValueTextInfoToList( diagram, list, index, position, points, + autoPositionPositive, autoPositionPositive, value ); +} + +void LineDiagram::LineDiagramType::paintValueTracker( PaintContext* ctx, const ValueTrackerAttributes& vt, const QPointF& at ) +{ + CartesianCoordinatePlane* plane = qobject_cast( ctx->coordinatePlane() ); + if( !plane ) + return; + + DataDimensionsList gridDimensions = ctx->coordinatePlane()->gridDimensionsList(); + const QPointF bottomLeft( ctx->coordinatePlane()->translate( + QPointF( plane->isHorizontalRangeReversed() ? + gridDimensions.at( 0 ).end : + gridDimensions.at( 0 ).start, + plane->isVerticalRangeReversed() ? + gridDimensions.at( 1 ).end : + gridDimensions.at( 1 ).start ) ) ); + const QPointF markerPoint = at; + const QPointF ordinatePoint( bottomLeft.x(), at.y() ); + const QPointF abscissaPoint( at.x(), bottomLeft.y() ); + + const QSizeF markerSize = vt.markerSize(); + const QRectF ellipseMarker = QRectF( at.x() - markerSize.width() / 2, + at.y() - markerSize.height() / 2, + markerSize.width(), markerSize.height() ); + + const QPointF ordinateMarker[3] = { + QPointF( ordinatePoint.x(), at.y() + markerSize.height() / 2 ), + QPointF( ordinatePoint.x() + markerSize.width() / 2, at.y() ), + QPointF( ordinatePoint.x(), at.y() - markerSize.height() / 2 ) + }; + + const QPointF abscissaMarker[3] = { + QPointF( at.x() + markerSize.width() / 2, abscissaPoint.y() ), + QPointF( at.x(), abscissaPoint.y() - markerSize.height() / 2 ), + QPointF( at.x() - markerSize.width() / 2, abscissaPoint.y() ) + }; + + QPointF topLeft = ordinatePoint; + QPointF bottomRightOffset = abscissaPoint - topLeft; + QSizeF size( bottomRightOffset.x(), bottomRightOffset.y() ); + QRectF area( topLeft, size ); + + PainterSaver painterSaver( ctx->painter() ); + ctx->painter()->setPen( PrintingParameters::scalePen( vt.pen() ) ); + ctx->painter()->setBrush( QBrush() ); + + ctx->painter()->drawLine( markerPoint, ordinatePoint ); + ctx->painter()->drawLine( markerPoint, abscissaPoint ); + + ctx->painter()->fillRect( area, vt.areaBrush() ); + + ctx->painter()->drawEllipse( ellipseMarker ); + + ctx->painter()->setBrush( vt.pen().color() ); + ctx->painter()->drawPolygon( ordinateMarker, 3 ); + ctx->painter()->drawPolygon( abscissaMarker, 3 ); +} + +CartesianDiagramDataCompressor& LineDiagram::LineDiagramType::compressor() const +{ + return m_private->compressor; +} + +double LineDiagram::LineDiagramType::interpolateMissingValue( const CartesianDiagramDataCompressor::CachePosition& pos ) const +{ + double leftValue = std::numeric_limits< double >::quiet_NaN(); + double rightValue = std::numeric_limits< double >::quiet_NaN(); + int missingCount = 1; + + const int column = pos.second; + const int row = pos.first; + const int rowCount = compressor().modelDataRows(); + + // iterate back and forth to find valid values + for( int r1 = row - 1; r1 > 0; --r1 ) + { + const CartesianDiagramDataCompressor::CachePosition position( r1, column ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + leftValue = point.value; + if( !ISNAN( point.value ) ) + break; + ++missingCount; + } + for( int r2 = row + 1; r2 < rowCount; ++r2 ) + { + const CartesianDiagramDataCompressor::CachePosition position( r2, column ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + rightValue = point.value; + if( !ISNAN( point.value ) ) + break; + ++missingCount; + } + if( !ISNAN( leftValue ) && !ISNAN( rightValue ) ) + return leftValue + ( rightValue - leftValue ) / ( missingCount + 1 ); + else + return std::numeric_limits< double >::quiet_NaN(); +} diff --git a/libkdchart/src/KDChartLineDiagram_p.h b/libkdchart/src/KDChartLineDiagram_p.h new file mode 100644 index 0000000..b77317f --- /dev/null +++ b/libkdchart/src/KDChartLineDiagram_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTLINEDIAGRAM_P_H +#define KDCHARTLINEDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartLineDiagram.h" + +#include + +#include "KDChartThreeDLineAttributes.h" +#include "KDChartAbstractCartesianDiagram_p.h" +#include "KDChartCartesianDiagramDataCompressor_p.h" + +#include + + +namespace KDChart { + + class PaintContext; + +/** + * \internal + */ + class LineDiagram::Private : public AbstractCartesianDiagram::Private + { + friend class LineDiagram; + friend class LineDiagramType; + + public: + Private(); + Private( const Private& rhs ); + ~Private(); + + void paintPolyline( + PaintContext* ctx, + const QBrush& brush, const QPen& pen, + const QPolygonF& points ) const; + + LineDiagram* diagram; + LineDiagramType* implementor; // the current type + LineDiagramType* normalDiagram; + LineDiagramType* stackedDiagram; + LineDiagramType* percentDiagram; + bool centerDataPoints; + bool reverseDatasetOrder; + }; + + KDCHART_IMPL_DERIVED_DIAGRAM( LineDiagram, AbstractCartesianDiagram, CartesianCoordinatePlane ) + + // we inherit privately, so that derived classes cannot call the + // base class functions - those reference the wrong (unattached to + // a diagram) d + class LineDiagram::LineDiagramType : private LineDiagram::Private + { + public: + explicit LineDiagramType( LineDiagram* d ) + : LineDiagram::Private() + , m_private( d->d_func() ) + { + } + virtual ~LineDiagramType() {} + virtual LineDiagram::LineType type() const = 0; + virtual const QPair calculateDataBoundaries() const = 0; + virtual void paint( PaintContext* ctx ) = 0; + LineDiagram* diagram() const; + + protected: + // method that make elements of m_private available to derived + // classes: + AttributesModel* attributesModel() const; + QModelIndex attributesModelRootIndex() const; + ReverseMapper& reverseMapper(); + CartesianDiagramDataCompressor& compressor() const; + + double interpolateMissingValue( const CartesianDiagramDataCompressor::CachePosition& pos ) const; + + int datasetDimension() const; + LineAttributes::MissingValuesPolicy getCellValues( + int row, int column, + bool shiftCountedXValuesByHalfSection, + double& valueX, double& valueY ) const; + double valueForCellTesting( int row, int column, + bool& bOK, + bool showHiddenCellsAsInvalid = false ) const; + void paintAreas( PaintContext* ctx, const QModelIndex& index, + const QList& areas, const uint transparency ); + double valueForCell( int row, int column ); + void appendDataValueTextInfoToList( + AbstractDiagram * diagram, + DataValueTextInfoList & list, + const QModelIndex & index, + const CartesianDiagramDataCompressor::CachePosition * position, + const PositionPoints& points, + const Position& autoPositionPositive, + const Position& autoPositionNegative, + const qreal value ); + + + const QPointF project( QPointF point, QPointF maxLimits, + double z, const QModelIndex& index ) const; + + void paintThreeDLines( + PaintContext* ctx, const QModelIndex& index, + const QPointF& from, const QPointF& to, const double depth ); + + void paintElements( PaintContext* ctx, + DataValueTextInfoList&, + LineAttributesInfoList&, + LineAttributes::MissingValuesPolicy ); + + void paintValueTracker( PaintContext* ctx, const ValueTrackerAttributes& vt, + const QPointF& at ); + + LineDiagram::Private* m_private; + }; + +/* + inline LineDiagram::LineDiagram( Private * p, CartesianCoordinatePlane* plane ) + : AbstractCartesianDiagram( p, plane ) { init(); } + inline LineDiagram::Private * LineDiagram::d_func() + { return static_cast( AbstractCartesianDiagram::d_func() ); } + inline const LineDiagram::Private * LineDiagram::d_func() const + { return static_cast( AbstractCartesianDiagram::d_func() ); } +*/ + +} + +#endif /* KDCHARTLINEDIAGRAM_P_H */ diff --git a/libkdchart/src/KDChartMarkerAttributes.cpp b/libkdchart/src/KDChartMarkerAttributes.cpp new file mode 100644 index 0000000..f8cab70 --- /dev/null +++ b/libkdchart/src/KDChartMarkerAttributes.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartMarkerAttributes.h" +#include +#include +#include +#include +#include +#include + +#include + +using namespace KDChart; + +class MarkerAttributes::Private +{ + friend class ::KDChart::MarkerAttributes; +public: + Private(); +private: + bool visible; + QMap markerStylesMap; + MarkerStyle markerStyle; + QSizeF markerSize; + QColor markerColor; + QPen markerPen; +}; + +MarkerAttributes::Private::Private() + : visible( false ), + markerStyle( MarkerSquare ), + markerSize( 10, 10 ), + markerPen( Qt::black ) +{ +} + + +MarkerAttributes::MarkerAttributes() + : _d( new Private ) +{ + +} + +MarkerAttributes::MarkerAttributes( const MarkerAttributes& r ) + : _d( new Private( *r._d ) ) +{ + +} + +MarkerAttributes & MarkerAttributes::operator=( const MarkerAttributes& r ) +{ + MarkerAttributes copy( r ); + copy.swap( *this ); + return *this; +} + +MarkerAttributes::~MarkerAttributes() +{ + delete _d; _d = 0; +} + +#define d d_func() + +bool MarkerAttributes::operator==( const MarkerAttributes& r ) const +{ + /* + qDebug() << "MarkerAttributes::operator== finds" + << "b" << (isVisible() == r.isVisible()) + << "c" << (markerStylesMap() == r.markerStylesMap()) + << "d" << (markerStyle() == r.markerStyle()) << markerStyle() <visible = visible; +} + +bool MarkerAttributes::isVisible() const +{ + return d->visible; +} + +void MarkerAttributes::setMarkerStylesMap( const MarkerStylesMap & map ) +{ + d->markerStylesMap = map; +} + +MarkerAttributes::MarkerStylesMap MarkerAttributes::markerStylesMap() const +{ + return d->markerStylesMap; +} + +void MarkerAttributes::setMarkerStyle( MarkerStyle style ) +{ + d->markerStyle = style; +} + +MarkerAttributes::MarkerStyle MarkerAttributes::markerStyle() const +{ + return d->markerStyle; +} + +void MarkerAttributes::setMarkerSize( const QSizeF& size ) +{ + d->markerSize = size; +} + +QSizeF MarkerAttributes::markerSize() const +{ + return d->markerSize; +} + +void MarkerAttributes::setMarkerColor( const QColor& color ) +{ + d->markerColor = color; +} + +QColor MarkerAttributes::markerColor() const +{ + return d->markerColor; +} + +void MarkerAttributes::setPen( const QPen& pen ) +{ + d->markerPen = pen; +} + +QPen MarkerAttributes::pen() const +{ + return d->markerPen; +} + +#undef d + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<( QDebug dbg, const MarkerAttributes & ma ) { + return dbg << "KDChart::MarkerAttributes(" + << "visible=" << ma.isVisible() + << "markerStylesMap=" << ma.markerStylesMap() + << "markerStyle=" << ma.markerStyle() + << "markerColor=" << ma.markerColor() + << "pen=" << ma.pen() + << ")"; +} +#endif + diff --git a/libkdchart/src/KDChartMarkerAttributes.h b/libkdchart/src/KDChartMarkerAttributes.h new file mode 100644 index 0000000..ec6529e --- /dev/null +++ b/libkdchart/src/KDChartMarkerAttributes.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTMARKERATTRIBUTES_H +#define KDCHARTMARKERATTRIBUTES_H + +#include +#include "KDChartGlobal.h" + +class QColor; +class QSizeF; +class QPen; +class QDebug; +template class QMap; + +namespace KDChart { + + /** + * @brief A set of ottributes controlling the appearance of data set markers + */ + class KDCHART_EXPORT MarkerAttributes + { + public: + MarkerAttributes(); + MarkerAttributes( const MarkerAttributes& ); + MarkerAttributes &operator= ( const MarkerAttributes& ); + + ~MarkerAttributes(); + + enum MarkerStyle { MarkerCircle = 0, + MarkerSquare = 1, + MarkerDiamond = 2, + Marker1Pixel = 3, + Marker4Pixels = 4, + MarkerRing = 5, + MarkerCross = 6, + MarkerFastCross = 7, + NoMarker = 8 }; + + void setVisible( bool visible ); + bool isVisible() const; + + typedef QMap MarkerStylesMap; + void setMarkerStylesMap( const MarkerStylesMap & map ); + MarkerStylesMap markerStylesMap() const; + + void setMarkerStyle( MarkerStyle style ); + MarkerStyle markerStyle() const; + + /** + * Normally you need to specify a valid QSizeF here, but for Legends you can + * use the invalid size QSizeF(), to enable automatic marker size calculation: + * + * For Markers shown in a Legend this means the marker size will be equal to + * the font height used for the labels that are shown next to the markers. + */ + void setMarkerSize( const QSizeF& size ); + QSizeF markerSize() const; + + void setMarkerColor( const QColor& color ); + QColor markerColor() const; + + void setPen( const QPen& pen ); + QPen pen() const; + + bool operator==( const MarkerAttributes& ) const; + bool operator!=( const MarkerAttributes& ) const; + + private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( MarkerAttributes ) + }; // End of class MarkerAttributes + + inline bool MarkerAttributes::operator!=( const MarkerAttributes & other ) const { return !operator==( other ); } +} + +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::MarkerAttributes ) +Q_DECLARE_TYPEINFO( KDChart::MarkerAttributes, Q_MOVABLE_TYPE ); +Q_DECLARE_METATYPE( KDChart::MarkerAttributes ) + +#ifndef QT_NO_DEBUG_STREAM +KDCHART_EXPORT QDebug operator<<( QDebug, const KDChart::MarkerAttributes & ); +#endif + +#endif // KDCHARTMARKERATTRIBUTES_H diff --git a/libkdchart/src/KDChartMeasure.cpp b/libkdchart/src/KDChartMeasure.cpp new file mode 100644 index 0000000..edbd544 --- /dev/null +++ b/libkdchart/src/KDChartMeasure.cpp @@ -0,0 +1,247 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartMeasure.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include + + +namespace KDChart { + + +Measure::Measure() + : mValue( 0.0 ), + mMode( KDChartEnums::MeasureCalculationModeAuto ), + mArea( 0 ), + mOrientation( KDChartEnums::MeasureOrientationAuto ) +{ + // this bloc left empty intentionally +} + +Measure::Measure( qreal value, + KDChartEnums::MeasureCalculationMode mode, + KDChartEnums::MeasureOrientation orientation ) + : mValue( value ), + mMode( mode ), + mArea( 0 ), + mOrientation( orientation ) +{ + // this bloc left empty intentionally +} + +Measure::Measure( const Measure& r ) + : mValue( r.value() ), + mMode( r.calculationMode() ), + mArea( r.referenceArea() ), + mOrientation( r.referenceOrientation() ) +{ + // this bloc left empty intentionally +} + +Measure & Measure::operator=( const Measure& r ) +{ + if( this != &r ){ + mValue = r.value(); + mMode = r.calculationMode(); + mArea = r.referenceArea(); + mOrientation = r.referenceOrientation(); + } + + return *this; +} + + +qreal Measure::calculatedValue( const QSizeF& autoSize, + KDChartEnums::MeasureOrientation autoOrientation) const +{ + if( mMode == KDChartEnums::MeasureCalculationModeAbsolute ){ + return mValue; + }else{ + qreal value = 0.0; + const QObject theAutoArea; + const QObject* autoArea = &theAutoArea; + const QObject* area = mArea ? mArea : autoArea; + KDChartEnums::MeasureOrientation orientation = mOrientation; + switch( mMode ){ + case KDChartEnums::MeasureCalculationModeAuto: + area = autoArea; + orientation = autoOrientation; + break; + case KDChartEnums::MeasureCalculationModeAutoArea: + area = autoArea; + break; + case KDChartEnums::MeasureCalculationModeAutoOrientation: + orientation = autoOrientation; + break; + case KDChartEnums::MeasureCalculationModeAbsolute: // fall through intended + case KDChartEnums::MeasureCalculationModeRelative: + break; + } + if( area ){ + QSizeF size; + if( area == autoArea ) + size = autoSize; + else + size = sizeOfArea( area ); + //qDebug() << ( area == autoArea ) << "size" << size; + qreal referenceValue = 0; + switch( orientation ){ + case KDChartEnums::MeasureOrientationAuto: // fall through intended + case KDChartEnums::MeasureOrientationMinimum: + referenceValue = qMin( size.width(), size.height() ); + break; + case KDChartEnums::MeasureOrientationMaximum: + referenceValue = qMax( size.width(), size.height() ); + break; + case KDChartEnums::MeasureOrientationHorizontal: + referenceValue = size.width(); + break; + case KDChartEnums::MeasureOrientationVertical: + referenceValue = size.height(); + break; + } + value = mValue / 1000.0 * referenceValue; + } + return value; + } +} + + +qreal Measure::calculatedValue( const QObject* autoArea, + KDChartEnums::MeasureOrientation autoOrientation) const +{ + return calculatedValue( sizeOfArea( autoArea ), autoOrientation); +} + + +const QSizeF Measure::sizeOfArea( const QObject* area ) const +{ + QSizeF size; + const CartesianCoordinatePlane* plane = dynamic_cast( area ); + if ( false ) { + size = plane->visibleDiagramArea().size(); + } else { + const AbstractArea* kdcArea = dynamic_cast(area); + if( kdcArea ){ + size = kdcArea->geometry().size(); + //qDebug() << "Measure::sizeOfArea() found kdcArea with size" << size; + }else{ + const QWidget* widget = dynamic_cast(area); + if( widget ){ + /* ATTENTION: Using the layout does not work: The Legend will never get the right size then! + const QLayout * layout = widget->layout(); + if( layout ){ + size = layout->geometry().size(); + //qDebug() << "Measure::sizeOfArea() found widget with layout size" << size; + }else*/ + { + size = widget->geometry().size(); + //qDebug() << "Measure::sizeOfArea() found widget with size" << size; + } + }else if( mMode != KDChartEnums::MeasureCalculationModeAbsolute ){ + size = QSizeF(1.0, 1.0); + //qDebug("Measure::sizeOfArea() got no valid area."); + } + } + } + const QPair< qreal, qreal > factors + = GlobalMeasureScaling::instance()->currentFactors(); + return QSizeF(size.width() * factors.first, size.height() * factors.second); +} + + +bool Measure::operator==( const Measure& r ) const +{ + return( mValue == r.value() && + mMode == r.calculationMode() && + mArea == r.referenceArea() && + mOrientation == r.referenceOrientation() ); +} + + + +GlobalMeasureScaling::GlobalMeasureScaling() +{ + mFactors.push( qMakePair(qreal(1.0), qreal(1.0)) ); +} + +GlobalMeasureScaling::~GlobalMeasureScaling() +{ + // this space left empty intentionally +} + +GlobalMeasureScaling* GlobalMeasureScaling::instance() +{ + static GlobalMeasureScaling instance; + return &instance; +} + +void GlobalMeasureScaling::setFactors(qreal factorX, qreal factorY) +{ + instance()->mFactors.push( qMakePair(factorX, factorY) ); +} + +void GlobalMeasureScaling::resetFactors() +{ + // never remove the initial (1.0. 1.0) setting + if( instance()->mFactors.count() > 1 ) + instance()->mFactors.pop(); +} + +const QPair< qreal, qreal > GlobalMeasureScaling::currentFactors() +{ + return instance()->mFactors.top(); +} + +void GlobalMeasureScaling::setPaintDevice( QPaintDevice* paintDevice ) +{ + instance()->m_paintDevice = paintDevice; +} + +QPaintDevice* GlobalMeasureScaling::paintDevice() +{ + return instance()->m_paintDevice; +} + +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::Measure& m) +{ + dbg << "KDChart::Measure(" + << "value="< +#include +#include +#include "KDChartGlobal.h" +#include "KDChartEnums.h" + +/** \file KDChartMeasure.h + * \brief Declaring the class KDChart::Measure. + * + * + */ + + +class QObject; +class QPaintDevice; + +namespace KDChart { + +/** + * \class Measure KDChartMeasure.h KDChartMeasure + * \brief Measure is used to specify all relative and/or absolute measures in KDChart, e.g. font sizes. + * + */ + +class KDCHART_EXPORT Measure +{ +public: + Measure(); + /*implicit*/ Measure( qreal value, + KDChartEnums::MeasureCalculationMode mode = KDChartEnums::MeasureCalculationModeAuto, + KDChartEnums::MeasureOrientation orientation = KDChartEnums::MeasureOrientationAuto ); + Measure( const Measure& ); + Measure &operator= ( const Measure& ); + + void setValue( qreal val ){ mValue = val; } + qreal value() const { return mValue; } + + void setCalculationMode( KDChartEnums::MeasureCalculationMode mode ){ mMode = mode; } + KDChartEnums::MeasureCalculationMode calculationMode() const { return mMode; } + + /** + * The reference area must either be derived from AbstractArea + * or be derived from QWidget, so e.g. it could be derived from + * AbstractAreaWidget too. + */ + void setRelativeMode( const QObject * area, + KDChartEnums::MeasureOrientation orientation ) + { + mMode = KDChartEnums::MeasureCalculationModeRelative; + mArea = area; + mOrientation = orientation; + } + + /** + * \brief This is a convenience method for specifying a value, + * with implicitely setting the calculation mode to MeasureCalculationModeAbsolute + * + * Calling setAbsoluteValue( value ) is the same as calling +\verbatim + setValue( value ); + setCalculationMode( KDChartEnums::MeasureCalculationModeAbsolute ); +\endverbatim + */ + void setAbsoluteValue( qreal val ) + { + mMode = KDChartEnums::MeasureCalculationModeAbsolute; + mValue = val; + } + + /** + * The reference area must either be derived from AbstractArea + * or be derived from QWidget, so e.g. it could be derived from + * AbstractAreaWidget too. + */ + void setReferenceArea( const QObject * area ){ mArea = area; } + /** + * The returned reference area will either be derived from AbstractArea + * or be derived from QWidget. + */ + const QObject * referenceArea() const { return mArea; } + + void setReferenceOrientation( KDChartEnums::MeasureOrientation orientation ){ mOrientation = orientation; } + KDChartEnums::MeasureOrientation referenceOrientation() const { return mOrientation; } + + /** + * The reference area must either be derived from AbstractArea + * or be derived from QWidget, so e.g. it could be derived from + * AbstractAreaWidget too. + */ + qreal calculatedValue( const QObject * autoArea, KDChartEnums::MeasureOrientation autoOrientation ) const; + qreal calculatedValue( const QSizeF& autoSize, KDChartEnums::MeasureOrientation autoOrientation ) const; + const QSizeF sizeOfArea( const QObject* area ) const; + + bool operator==( const Measure& ) const; + bool operator!=( const Measure& other ) const { return !operator==(other); } + +private: + qreal mValue; + KDChartEnums::MeasureCalculationMode mMode; + const QObject* mArea; + KDChartEnums::MeasureOrientation mOrientation; +}; // End of class Measure + + + +/** + * Auxiliary class used by the KDChart::Measure and KDChart::Chart class. + * + * Normally there should be no need to call any of these methods yourself. + * + * They are used by KDChart::Chart::paint( QPainter*, const QRect& ) + * to adjust all of the relative Measures according to the target + * rectangle's size. + * + * Default factors are (1.0, 1.0) + */ +class GlobalMeasureScaling +{ +public: + static GlobalMeasureScaling* instance(); + + GlobalMeasureScaling(); + virtual ~GlobalMeasureScaling(); + +public: + /** + * Set new factors to be used by all Measure objects from now on. + * Previous values will be stored. + */ + static void setFactors(qreal factorX, qreal factorY); + + /** + * Reset factors to the values active before the previous call of + * setFactors. + * This works on a stack, so recursive calls works fine, like: + * setFactors, setFactors, unserFactors, unsetFactors + */ + static void resetFactors(); + + /** + * Returns the currently active factors. + */ + static const QPair< qreal, qreal > currentFactors(); + + /** + * Sets the paint device usable for calculating fort metrics. + */ + static void setPaintDevice( QPaintDevice* paintDevice ); + + /** + * Returns the paint device usable for calculating fort metrics. + */ + static QPaintDevice* paintDevice(); + +private: + QStack< QPair< qreal, qreal > > mFactors; + QPaintDevice* m_paintDevice; +}; + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::Measure& ); +#endif /* QT_NO_DEBUG_STREAM */ + +#endif // KDCHARTMEASURE_H diff --git a/libkdchart/src/KDChartModelDataCache_p.cpp b/libkdchart/src/KDChartModelDataCache_p.cpp new file mode 100644 index 0000000..8e93483 --- /dev/null +++ b/libkdchart/src/KDChartModelDataCache_p.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartModelDataCache_p.h" + +#include + +using namespace KDChart::ModelDataCachePrivate; + +ModelSignalMapperConnector::ModelSignalMapperConnector( ModelSignalMapper& mapper ) + : QObject( 0 ), + m_mapper( mapper ) +{ +} + +ModelSignalMapperConnector::~ModelSignalMapperConnector() +{ +} + +void ModelSignalMapperConnector::connectSignals( QAbstractItemModel* model ) +{ + connect( model, SIGNAL( destroyed() ), this, SLOT( resetModel() ) ); + connect( model, SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( columnsInserted( QModelIndex, int, int ) ) ); + connect( model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( columnsRemoved( QModelIndex, int, int ) ) ); + connect( model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( dataChanged( QModelIndex, QModelIndex ) ) ); + connect( model, SIGNAL( layoutChanged() ), this, SLOT( layoutChanged() ) ); + connect( model, SIGNAL( modelReset() ), this, SLOT( modelReset() ) ); + connect( model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( rowsInserted( QModelIndex, int, int )) ); + connect( model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( rowsRemoved( QModelIndex, int, int ) ) ); +} + +void ModelSignalMapperConnector::disconnectSignals( QAbstractItemModel* model ) +{ + disconnect( model, SIGNAL( destroyed() ), this, SLOT( resetModel() ) ); + disconnect( model, SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( columnsInserted( QModelIndex, int, int ) ) ); + disconnect( model, SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( columnsRemoved( QModelIndex, int, int ) ) ); + disconnect( model, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( dataChanged( QModelIndex, QModelIndex ) ) ); + disconnect( model, SIGNAL( layoutChanged() ), this, SLOT( layoutChanged() ) ); + disconnect( model, SIGNAL( modelReset() ), this, SLOT( modelReset() ) ); + disconnect( model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( rowsInserted( QModelIndex, int, int )) ); + disconnect( model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( rowsRemoved( QModelIndex, int, int ) ) ); +} + +void ModelSignalMapperConnector::resetModel() +{ + m_mapper.resetModel(); +} + +void ModelSignalMapperConnector::columnsInserted( const QModelIndex& parent, int start, int end ) +{ + m_mapper.columnsInserted( parent, start, end ); +} + +void ModelSignalMapperConnector::columnsRemoved( const QModelIndex& parent, int start, int end ) +{ + m_mapper.columnsRemoved( parent, start, end ); +} + +void ModelSignalMapperConnector::dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) +{ + m_mapper.dataChanged( topLeft, bottomRight ); +} + +void ModelSignalMapperConnector::layoutChanged() +{ + m_mapper.layoutChanged(); +} + +void ModelSignalMapperConnector::modelReset() +{ + m_mapper.modelReset(); +} + +void ModelSignalMapperConnector::rowsInserted( const QModelIndex& parent, int start, int end ) +{ + m_mapper.rowsInserted( parent, start, end ); +} + +void ModelSignalMapperConnector::rowsRemoved( const QModelIndex& parent, int start, int end ) +{ + m_mapper.rowsRemoved( parent, start, end ); +} diff --git a/libkdchart/src/KDChartModelDataCache_p.h b/libkdchart/src/KDChartModelDataCache_p.h new file mode 100644 index 0000000..c9e349d --- /dev/null +++ b/libkdchart/src/KDChartModelDataCache_p.h @@ -0,0 +1,341 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTMODELDATACACHE_H +#define KDCHARTMODELDATACACHE_H + +#include + +#include +#include +#include + +#include "kdchart_export.h" + +class QAbstractItemModel; + +namespace KDChart +{ + namespace ModelDataCachePrivate + { + class KDCHART_EXPORT ModelSignalMapper + { + protected: + ModelSignalMapper() {} + public: + virtual ~ModelSignalMapper() {} + virtual void resetModel() = 0; + virtual void columnsInserted( const QModelIndex&, int, int ) = 0; + virtual void columnsRemoved( const QModelIndex&, int, int ) = 0; + virtual void dataChanged( const QModelIndex&, const QModelIndex& ) = 0; + virtual void layoutChanged() = 0; + virtual void modelReset() = 0; + virtual void rowsInserted( const QModelIndex&, int, int ) = 0; + virtual void rowsRemoved( const QModelIndex&, int, int ) = 0; + }; + + // this class maps slots to a non-QObject instantiating ModelSignalMapper + class KDCHART_EXPORT ModelSignalMapperConnector : public QObject + { + Q_OBJECT + public: + explicit ModelSignalMapperConnector( ModelSignalMapper& mapper ); + ~ModelSignalMapperConnector(); + + void connectSignals( QAbstractItemModel* model ); + void disconnectSignals( QAbstractItemModel* model ); + + protected Q_SLOTS: + void resetModel(); + void columnsInserted( const QModelIndex&, int, int ); + void columnsRemoved( const QModelIndex&, int, int ); + void dataChanged( const QModelIndex&, const QModelIndex& ); + void layoutChanged(); + void modelReset(); + void rowsInserted( const QModelIndex&, int, int ); + void rowsRemoved( const QModelIndex&, int, int ); + + private: + ModelSignalMapper& m_mapper; + }; + + template< class T> + T nan() + { + return T(); + } + + template<> + inline double nan< double >() + { + return std::numeric_limits< double >::quiet_NaN(); + } + } + + template< class T, int ROLE = Qt::DisplayRole > + class ModelDataCache : public ModelDataCachePrivate::ModelSignalMapper + { + public: + ModelDataCache() + : m_model( 0 ), + m_connector( *this ) + { + } + + virtual ~ModelDataCache() + { + } + + T data( const QModelIndex& index ) const + { + if( !index.isValid() || index.parent() != m_rootIndex ) + return ModelDataCachePrivate::nan< T >(); + + if( index.row() >= m_data.count() ) + { + qWarning( "KDChart didn't got signal rowsInserted, resetModel or layoutChanged, " + "but an index with a row outside of the known bounds." ); + // apparently, data were added behind our back (w/o signals) + const_cast< ModelDataCache< T, ROLE >* >( this )->rowsInserted( m_rootIndex, + m_data.count(), + qMax(m_data.count(), m_model->rowCount( m_rootIndex ) - 1) ); + Q_ASSERT( index.row() < m_data.count() ); + } + + if( index.column() >= m_data.first().count() ) + { + qWarning( "KDChart didn't got signal columnsInserted, resetModel or layoutChanged, " + "but an index with a column outside of the known bounds." ); + // apparently, data were added behind our back (w/o signals) + const_cast< ModelDataCache< T, ROLE >* >( this )->columnsInserted( m_rootIndex, + m_data.first().count(), + qMax(m_data.first().count(), m_model->columnCount( m_rootIndex ) - 1) ); + Q_ASSERT( index.column() < m_data.first().count() ); + } + + return data( index.row(), index.column() ); + } + + T data( int row, int column ) const + { + if( row < 0 || column < 0 ) + return ModelDataCachePrivate::nan< T >(); + + Q_ASSERT( row < m_data.count() ); + Q_ASSERT( column < m_data.first().count() ); + + if( isCached( row, column ) ) + return m_data.at( row ).at( column ); + + return fetchFromModel( row, column, ROLE ); + } + + void setModel( QAbstractItemModel* model ) + { + if( m_model != 0 ) + m_connector.disconnectSignals( m_model ); + + m_model = model; + + if( m_model != 0 ) + m_connector.connectSignals( m_model ); + + modelReset(); + } + + QAbstractItemModel* model() const + { + return m_model; + } + + void setRootIndex( const QModelIndex& rootIndex ) + { + Q_ASSERT( rootIndex.model() == m_model || !rootIndex.isValid() ); + m_rootIndex = rootIndex; + modelReset(); + } + + QModelIndex rootIndex() const + { + return m_rootIndex; + } + + protected: + bool isCached( int row, int column ) const + { + return m_cacheValid.at( row ).at( column ); + } + + T fetchFromModel( int row, int column, int role ) const + { + Q_ASSERT( m_model != 0 ); + + const QModelIndex index = m_model->index( row, column, m_rootIndex ); + const QVariant data = index.data( role ); + const T value = data.isNull() ? ModelDataCachePrivate::nan< T >() + : qVariantValue< T >( data ); + + m_data[ row ][ column ] = value; + m_cacheValid[ row ][ column ] = true; + + return value; + } + + protected: + void columnsInserted( const QModelIndex& parent, int start, int end ) + { + Q_ASSERT( m_model != 0 ); + + if( parent != m_rootIndex ) + return; + + Q_ASSERT( start <= end ); + + const int rowCount = m_data.count(); + for( int row = 0; row < rowCount; ++row ) + { + m_data[ row ].insert( start, end - start + 1, T() ); + m_cacheValid[ row ].insert( start, end - start + 1, false ); + Q_ASSERT( m_data.at( row ).count() == m_model->columnCount( m_rootIndex ) ); + Q_ASSERT( m_cacheValid.at( row ).count() == m_model->columnCount( m_rootIndex ) ); + } + } + + void columnsRemoved( const QModelIndex& parent, int start, int end ) + { + Q_ASSERT( m_model != 0 ); + + if( parent != m_rootIndex ) + return; + + Q_ASSERT( start <= end ); + + const int rowCount = m_data.count(); + for( int row = 0; row < rowCount; ++row ) + { + m_data[ row ].remove( start, end - start + 1 ); + m_cacheValid[ row ].remove( start, end - start + 1 ); + Q_ASSERT( m_data.at( row ).count() == m_model->columnCount( m_rootIndex ) ); + Q_ASSERT( m_cacheValid.at( row ).count() == m_model->columnCount( m_rootIndex ) ); + } + } + + void dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) + { + Q_ASSERT( m_model != 0 ); + Q_ASSERT( topLeft.parent() == bottomRight.parent() ); + Q_ASSERT( topLeft.model() == m_model && bottomRight.model() == m_model ); + + if( !topLeft.isValid() || !bottomRight.isValid() || topLeft.parent() != m_rootIndex ) + return; + + const int minRow = qMax( 0, topLeft.row() ); + const int maxRow = bottomRight.row(); + const int minCol = qMax( 0, topLeft.column() ); + const int maxCol = bottomRight.column(); + + Q_ASSERT( minRow <= maxRow ); + Q_ASSERT( minCol <= maxCol ); + Q_ASSERT( maxRow < m_model->rowCount( m_rootIndex ) ); + Q_ASSERT( maxCol < m_model->columnCount( m_rootIndex ) ); + + for( int row = minRow; row <= maxRow; ++row ) + { + for( int col = minCol; col <= maxCol; ++col ) + { + m_cacheValid[ row ][ col ] = false; + Q_ASSERT( !isCached( row, col ) ); + } + } + } + + void layoutChanged() + { + modelReset(); + } + + void modelReset() + { + m_data.clear(); + m_cacheValid.clear(); + + if( m_model == 0 ) + return; + + m_data.fill( QVector< T >( m_model->columnCount( m_rootIndex ) ), m_model->rowCount( m_rootIndex ) ); + m_cacheValid.fill( QVector< bool >( m_model->columnCount( m_rootIndex ), false ), m_model->rowCount( m_rootIndex ) ); + + Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) ); + Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) ); + } + + void rowsInserted( const QModelIndex& parent, int start, int end ) + { + Q_ASSERT( m_model != 0 ); + + if( parent != m_rootIndex ) + return; + + Q_ASSERT( start >= 0 && start <= m_data.size() ); + Q_ASSERT( start <= end ); + + m_data.insert( start, end - start + 1, QVector< T >( m_model->columnCount( m_rootIndex ) ) ); + m_cacheValid.insert( start, end - start + 1, QVector< bool >( m_model->columnCount( m_rootIndex ), false ) ); + + Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) ); + Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) ); + } + + void rowsRemoved( const QModelIndex& parent, int start, int end ) + { + Q_ASSERT( m_model != 0 ); + + if( parent != m_rootIndex ) + return; + + Q_ASSERT( start <= end ); + + m_data.remove( start, end - start + 1 ); + m_cacheValid.remove( start, end - start + 1 ); + + Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) ); + Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) ); + } + + void resetModel() + { + // no need to disconnect, this is a response to SIGNAL( destroyed() ) + m_model = 0; + modelReset(); + } + + + private: + QAbstractItemModel* m_model; + QModelIndex m_rootIndex; + ModelDataCachePrivate::ModelSignalMapperConnector m_connector; + mutable QVector< QVector< T > > m_data; + mutable QVector< QVector< bool > > m_cacheValid; + }; +} + +#endif diff --git a/libkdchart/src/KDChartNormalBarDiagram_p.cpp b/libkdchart/src/KDChartNormalBarDiagram_p.cpp new file mode 100644 index 0000000..2a247db --- /dev/null +++ b/libkdchart/src/KDChartNormalBarDiagram_p.cpp @@ -0,0 +1,206 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartNormalBarDiagram_p.h" + +#include + +#include "KDChartBarDiagram.h" +#include "KDChartTextAttributes.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractCartesianDiagram.h" + +using namespace KDChart; +using namespace std; + +NormalBarDiagram::NormalBarDiagram( BarDiagram* d ) + : BarDiagramType( d ) +{ +} + +BarDiagram::BarType NormalBarDiagram::type() const +{ + return BarDiagram::Normal; +} + +const QPair NormalBarDiagram::calculateDataBoundaries() const +{ + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + + double xMin = 0.0; + double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; + double yMin = 0.0, yMax = 0.0; + + double usedDepth = 0; + + bool bStarting = true; + for ( int column = 0; column < colCount; ++column ) + { + for ( int row = 0; row < rowCount; ++row ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, column ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + const double value = ISNAN( point.value ) ? 0.0 : point.value; + + QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( sourceIndex ); + + if ( threeDAttrs.isEnabled() ) + usedDepth = qMax( usedDepth, threeDAttrs.depth() ); + + // this is always true yMin can be 0 in case all values + // are the same + // same for yMax it can be zero if all values are negative + if( bStarting ){ + yMin = value; + yMax = value; + bStarting = false; + }else{ + yMin = qMin( yMin, value ); + yMax = qMax( yMax, value ); + } + } + } + + // special cases + if ( yMax == yMin ) { + if ( yMin == 0.0 ) + yMax = 0.1; //we need at least a range + else if( yMax < 0.0 ) + yMax = 0.0; // they are the same and negative + else if( yMin > 0.0 ) + yMin = 0.0; // they are the same but positive + } + const QPointF bottomLeft ( QPointF( xMin, yMin ) ); + const QPointF topRight ( QPointF( xMax, yMax ) ); + + //qDebug() << "KDChart::NormalBarDiagram::calculateDataBoundaries() returns " << bottomLeft << topRight; + + return QPair< QPointF, QPointF >( bottomLeft, topRight ); +} + +void NormalBarDiagram::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + + const QPair boundaries = diagram()->dataBoundaries(); // cached + + const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; + const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); + + const int rowCount = attributesModel()->rowCount(attributesModelRootIndex()); + const int colCount = attributesModel()->columnCount(attributesModelRootIndex()); + + BarAttributes ba = diagram()->barAttributes( diagram()->model()->index( 0, 0, diagram()->rootIndex() ) ); + ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( diagram()->model()->index( 0, 0, diagram()->rootIndex() ) ); + double barWidth = 0; + double maxDepth = 0; + double width = boundRight.x() - boundLeft.x(); + double groupWidth = width / (rowCount + 2); + double spaceBetweenBars = 0; + double spaceBetweenGroups = 0; + + if ( ba.useFixedBarWidth() ) { + + barWidth = ba.fixedBarWidth(); + groupWidth += barWidth; + + // Pending Michel set a min and max value for the groupWidth + // related to the area.width + if ( groupWidth < 0 ) + groupWidth = 0; + + if ( groupWidth * rowCount > width ) + groupWidth = width / rowCount; + } + + // maxLimit: allow the space between bars to be larger until area.width() + // is covered by the groups. + double maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); + + //Pending Michel: FixMe + if ( ba.useFixedDataValueGap() ) { + if ( width > maxLimit ) + spaceBetweenBars += ba.fixedDataValueGap(); + else + spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); + } + + if ( ba.useFixedValueBlockGap() ) { + spaceBetweenGroups += ba.fixedValueBlockGap(); + } + + calculateValueAndGapWidths( rowCount, colCount,groupWidth, + barWidth, spaceBetweenBars, spaceBetweenGroups ); + + DataValueTextInfoList list; + + for( int row = 0; row < rowCount; ++row ) + { + double offset = -groupWidth/2 + spaceBetweenGroups/2; + + if ( ba.useFixedDataValueGap() ) + { + if ( spaceBetweenBars > 0 ) + { + if ( width > maxLimit ) + offset -= ba.fixedDataValueGap(); + else + offset -= ((width/rowCount) - groupWidth)/(colCount-1); + + } + else + { + offset += barWidth/2; + } + } + + for( int column=0; column< colCount; ++column ) + { + // paint one group + const CartesianDiagramDataCompressor::CachePosition position( row, column ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + const qreal value = point.value;//attributesModel()->data( sourceIndex ).toDouble(); + if ( ! point.hidden && !ISNAN( value ) ) { + QPointF topPoint = ctx->coordinatePlane()->translate( QPointF( point.key + 0.5, value ) ); + QPointF bottomPoint = ctx->coordinatePlane()->translate( QPointF( point.key, 0 ) ); + + if ( threeDAttrs.isEnabled() ) { + const double usedDepth = threeDAttrs.depth()/4; + topPoint.setY( topPoint.y() + usedDepth + 1.0 ); + } + + const double barHeight = bottomPoint.y() - topPoint.y(); + topPoint.setX( topPoint.x() + offset ); + const QRectF rect( topPoint, QSizeF( barWidth, barHeight ) ); + appendDataValueTextInfoToList( diagram(), list, sourceIndex, PositionPoints( rect ), + Position::NorthWest, Position::SouthEast, + point.value ); + paintBars( ctx, sourceIndex, rect, maxDepth ); + } + offset += barWidth + spaceBetweenBars; + } + } + paintDataValueTextsAndMarkers( diagram(), ctx, list, false ); +} diff --git a/libkdchart/src/KDChartNormalBarDiagram_p.h b/libkdchart/src/KDChartNormalBarDiagram_p.h new file mode 100644 index 0000000..dab8c80 --- /dev/null +++ b/libkdchart/src/KDChartNormalBarDiagram_p.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTNORMALBARDIAGRAM_P_H +#define KDCHARTNORMALBARDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +#include "KDChartBarDiagram_p.h" + +namespace KDChart { + + class NormalBarDiagram : public BarDiagram::BarDiagramType + { + public: + explicit NormalBarDiagram( BarDiagram* ); + virtual ~NormalBarDiagram() {} + virtual BarDiagram::BarType type() const; + virtual const QPair calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); +/* + virtual void calculateValueAndGapWidths( int rowCount,int colCount, + double groupWidth, + double& outBarWidth, + double& outSpaceBetweenBars, + double& outSpaceBetweenGroups ); +*/ + }; + +} + +#endif diff --git a/libkdchart/src/KDChartNormalLineDiagram_p.cpp b/libkdchart/src/KDChartNormalLineDiagram_p.cpp new file mode 100644 index 0000000..0212976 --- /dev/null +++ b/libkdchart/src/KDChartNormalLineDiagram_p.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include + +#include + +#include "KDChartBarDiagram.h" +#include "KDChartLineDiagram.h" +#include "KDChartTextAttributes.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartNormalLineDiagram_p.h" + +using namespace KDChart; +using namespace std; + +NormalLineDiagram::NormalLineDiagram( LineDiagram* d ) + : LineDiagramType( d ) +{ +} + +LineDiagram::LineType NormalLineDiagram::type() const +{ + return LineDiagram::Normal; +} + +const QPair< QPointF, QPointF > NormalLineDiagram::calculateDataBoundaries() const +{ + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + const double xMin = 0.0; + double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; + if ( !diagram()->centerDataPoints() && diagram()->model() ) + xMax -= 1; + double yMin = std::numeric_limits< double >::quiet_NaN(); + double yMax = std::numeric_limits< double >::quiet_NaN(); + + for( int column = 0; column < colCount; ++column ) + { + for ( int row = 0; row < rowCount; ++row ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, column ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + const double value = ISNAN( point.value ) ? 0.0 : point.value; + + if ( ISNAN( yMin ) ) { + yMin = value; + yMax = value; + } else { + yMin = qMin( yMin, value ); + yMax = qMax( yMax, value ); + } + } + } + + // NOTE: calculateDataBoundaries must return the *real* data boundaries! + // i.e. we may NOT fake yMin to be qMin( 0.0, yMin ) + // (khz, 2008-01-24) + const QPointF bottomLeft( QPointF( xMin, yMin ) ); + const QPointF topRight( QPointF( xMax, yMax ) ); + return QPair< QPointF, QPointF >( bottomLeft, topRight ); +} + +void NormalLineDiagram::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + Q_ASSERT( dynamic_cast( ctx->coordinatePlane() ) ); + CartesianCoordinatePlane* plane = static_cast( ctx->coordinatePlane() ); + const int columnCount = compressor().modelDataColumns(); + const int rowCount = compressor().modelDataRows(); + if ( columnCount == 0 || rowCount == 0 ) return; // maybe blank out the area? + +// FIXME integrate column index retrieval to compressor: +// the compressor should only pass through visiblel columns + int maxFound = 0; +// { // find the last column number that is not hidden +// for( int column = datasetDimension() - 1; +// column < columnCount; +// column += datasetDimension() ) +// if( ! diagram()->isHidden( column ) ) +// maxFound = column; +// } + maxFound = columnCount; + // ^^^ temp + + // Reverse order of data sets? + bool rev = diagram()->reverseDatasetOrder(); + DataValueTextInfoList textInfoList; + LineAttributesInfoList lineList; + for( int column = rev ? columnCount - 1 : 0; + rev ? (column >= 0) : (column < columnCount); + rev ? --column : ++column ) { + LineAttributes laPreviousCell; + CartesianDiagramDataCompressor::DataPoint lastPoint; + qreal lastAreaBoundingValue = 0; + + // Get min. y value, used as lower or upper bounding for area highlighting + const qreal minYValue = qMin(plane->visibleDataRange().bottom(), plane->visibleDataRange().top()); + + CartesianDiagramDataCompressor::CachePosition previousCellPosition; + for ( int row = 0; row < rowCount; ++row ) { + const CartesianDiagramDataCompressor::CachePosition position( row, column ); + // get where to draw the line from: + CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + + const LineAttributes laCell = diagram()->lineAttributes( sourceIndex ); + const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy(); + + // lower or upper bounding for the highlighted area + qreal areaBoundingValue; + if ( laCell.areaBoundingDataset() != -1 ) { + const CartesianDiagramDataCompressor::CachePosition areaBoundingCachePosition( row, laCell.areaBoundingDataset() ); + areaBoundingValue = compressor().data( areaBoundingCachePosition ).value; + } else + // Use min. y value (i.e. zero line in most cases) if no bounding dataset is set + areaBoundingValue = minYValue; + + if( ISNAN( point.value ) ) + { + switch( policy ) + { + case LineAttributes::MissingValuesAreBridged: + // we just bridge both values + continue; + case LineAttributes::MissingValuesShownAsZero: + // set it to zero + point.value = 0.0; + break; + case LineAttributes::MissingValuesHideSegments: + // they're just hidden + break; + default: + break; + // hm.... + } + } + + // area corners, a + b are the line ends: + const QPointF a( plane->translate( QPointF( diagram()->centerDataPoints() ? lastPoint.key + 0.5 : lastPoint.key, lastPoint.value ) ) ); + const QPointF b( plane->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, point.value ) ) ); + const QPointF c( plane->translate( QPointF( diagram()->centerDataPoints() ? lastPoint.key + 0.5 : lastPoint.key, lastAreaBoundingValue ) ) ); + const QPointF d( plane->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, areaBoundingValue ) ) ); + // add the line to the list: + // add data point labels: + const PositionPoints pts = PositionPoints( b, a, d, c ); + // if necessary, add the area to the area list: + QList areas; + if ( !ISNAN( point.value ) && !ISNAN( lastPoint.value ) && laCell.displayArea() ) { + areas << ( QPolygonF() << a << b << d << c );//polygon; + } + // add the pieces to painting if this is not hidden: + if ( ! point.hidden && !ISNAN( point.value ) ) + { + appendDataValueTextInfoToList( diagram(), textInfoList, sourceIndex, &position, + pts, Position::NorthWest, Position::SouthWest, + point.value ); + paintAreas( ctx, attributesModel()->mapToSource( lastPoint.index ), areas, laCell.transparency() ); + // position 0 is not really painted, since it takes two points to make a line :-) + if( row > 0 && !ISNAN( lastPoint.value ) ) + lineList.append( LineAttributesInfo( sourceIndex, a, b ) ); + } + + // wrap it up: + previousCellPosition = position; + laPreviousCell = laCell; + lastAreaBoundingValue = areaBoundingValue; + lastPoint = point; + } + + } + + LineAttributes::MissingValuesPolicy policy = LineAttributes::MissingValuesAreBridged; //unused + paintElements( ctx, textInfoList, lineList, policy ); +} diff --git a/libkdchart/src/KDChartNormalLineDiagram_p.h b/libkdchart/src/KDChartNormalLineDiagram_p.h new file mode 100644 index 0000000..09dd3ea --- /dev/null +++ b/libkdchart/src/KDChartNormalLineDiagram_p.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTNORMALLINEDIAGRAM_P_H +#define KDCHARTNORMALLINEDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +#include "KDChartLineDiagram_p.h" + +namespace KDChart { + + class NormalLineDiagram : public LineDiagram::LineDiagramType + { + public: + explicit NormalLineDiagram( LineDiagram* ); + virtual ~NormalLineDiagram() {} + virtual LineDiagram::LineType type() const; + virtual const QPair calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); + }; + +} + +#endif diff --git a/libkdchart/src/KDChartNormalLyingBarDiagram_p.cpp b/libkdchart/src/KDChartNormalLyingBarDiagram_p.cpp new file mode 100644 index 0000000..a861c5d --- /dev/null +++ b/libkdchart/src/KDChartNormalLyingBarDiagram_p.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartNormalLyingBarDiagram_p.h" + +#include + +#include "KDChartBarDiagram.h" +#include "KDChartTextAttributes.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractCartesianDiagram.h" + +using namespace KDChart; +using namespace std; + +NormalLyingBarDiagram::NormalLyingBarDiagram( BarDiagram* d ) + : BarDiagramType( d ) +{ +} + +BarDiagram::BarType NormalLyingBarDiagram::type() const +{ + return BarDiagram::Normal; +} + +const QPair NormalLyingBarDiagram::calculateDataBoundaries() const +{ + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + + double xMin = 0.0; + double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; + double yMin = 0.0, yMax = 0.0; + + bool bStarting = true; + for ( int column = 0; column < colCount; ++column ) + { + for ( int row = 0; row < rowCount; ++row ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, column ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + const double value = ISNAN( point.value ) ? 0.0 : point.value; + // this is always true yMin can be 0 in case all values + // are the same + // same for yMax it can be zero if all values are negative + if( bStarting ){ + yMin = value; + yMax = value; + bStarting = false; + }else{ + yMin = qMin( yMin, value ); + yMax = qMax( yMax, value ); + } + } + } + + // special cases + if ( yMax == yMin ) { + if ( yMin == 0.0 ) + yMax = 0.1; //we need at least a range + else if( yMax < 0.0 ) + yMax = 0.0; // they are the same and negative + else if( yMin > 0.0 ) + yMin = 0.0; // they are the same but positive + } + const QPointF bottomLeft ( QPointF( yMin, xMin ) ); + const QPointF topRight ( QPointF( yMax, xMax ) ); + + return QPair< QPointF, QPointF >( bottomLeft, topRight ); +} + +void NormalLyingBarDiagram::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + + const QPair boundaries = diagram()->dataBoundaries(); // cached + + const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; + const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); + + const int rowCount = attributesModel()->rowCount(attributesModelRootIndex()); + const int colCount = attributesModel()->columnCount(attributesModelRootIndex()); + + BarAttributes ba = diagram()->barAttributes( diagram()->model()->index( 0, 0, diagram()->rootIndex() ) ); + double barWidth = 0; + double maxDepth = 0; + double width = boundLeft.y() - boundRight.y(); + double groupWidth = width / (rowCount + 2); + double spaceBetweenBars = 0; + double spaceBetweenGroups = 0; + + if ( ba.useFixedBarWidth() ) { + + barWidth = ba.fixedBarWidth(); + groupWidth += barWidth; + + // Pending Michel set a min and max value for the groupWidth + // related to the area.width + if ( groupWidth < 0 ) + groupWidth = 0; + + if ( groupWidth * rowCount > width ) + groupWidth = width / rowCount; + } + + // maxLimit: allow the space between bars to be larger until area.width() + // is covered by the groups. + double maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); + + //Pending Michel: FixMe + if ( ba.useFixedDataValueGap() ) { + if ( width > maxLimit ) + spaceBetweenBars += ba.fixedDataValueGap(); + else + spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); + } + + if ( ba.useFixedValueBlockGap() ) { + spaceBetweenGroups += ba.fixedValueBlockGap(); + } + + calculateValueAndGapWidths( rowCount, colCount,groupWidth, + barWidth, spaceBetweenBars, spaceBetweenGroups ); + + DataValueTextInfoList list; + + for( int row = rowCount - 1; row >= 0; --row ) + { + double offset = (groupWidth + spaceBetweenGroups) / 2.0 - spaceBetweenBars; + + if ( ba.useFixedDataValueGap() ) + { + if ( spaceBetweenBars > 0 ) + { + if ( width > maxLimit ) + offset -= ba.fixedDataValueGap(); + else + offset -= ((width/rowCount) - groupWidth)/(colCount-1); + + } + else + { + offset += barWidth/2; + } + } + + for( int column = colCount - 1; column >= 0; --column ) + { + offset -= barWidth + spaceBetweenBars; + + // paint one group + const CartesianDiagramDataCompressor::CachePosition position( row, column ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + const qreal value = point.value;//attributesModel()->data( sourceIndex ).toDouble(); + QPointF topPoint = ctx->coordinatePlane()->translate( QPointF( value, rowCount - (point.key + 0.5) ) ); + QPointF bottomPoint = ctx->coordinatePlane()->translate( QPointF( 0, rowCount - point.key ) ); + const double barHeight = topPoint.x() - bottomPoint.x(); + topPoint.ry() += offset; + topPoint.rx() -= barHeight; + const QRectF rect( topPoint, QSizeF( barHeight, barWidth ) ); + appendDataValueTextInfoToList( diagram(), list, sourceIndex, PositionPoints( rect ), + Position::NorthEast, Position::SouthWest, + point.value ); + paintBars( ctx, sourceIndex, rect, maxDepth ); + } + } + paintDataValueTextsAndMarkers( diagram(), ctx, list, false ); +} diff --git a/libkdchart/src/KDChartNormalLyingBarDiagram_p.h b/libkdchart/src/KDChartNormalLyingBarDiagram_p.h new file mode 100644 index 0000000..ce29f4d --- /dev/null +++ b/libkdchart/src/KDChartNormalLyingBarDiagram_p.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTNORMALLYINGBARDIAGRAM_P_H +#define KDCHARTNORMALLYINGBARDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +#include "KDChartBarDiagram_p.h" + +namespace KDChart { + + class NormalLyingBarDiagram : public BarDiagram::BarDiagramType + { + public: + explicit NormalLyingBarDiagram( BarDiagram* ); + virtual ~NormalLyingBarDiagram() {} + virtual BarDiagram::BarType type() const; + virtual const QPair calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); +/* + virtual void calculateValueAndGapWidths( int rowCount,int colCount, + double groupWidth, + double& outBarWidth, + double& outSpaceBetweenBars, + double& outSpaceBetweenGroups ); +*/ + }; + +} + +#endif diff --git a/libkdchart/src/KDChartNormalPlotter_p.cpp b/libkdchart/src/KDChartNormalPlotter_p.cpp new file mode 100644 index 0000000..a56ad95 --- /dev/null +++ b/libkdchart/src/KDChartNormalPlotter_p.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartNormalPlotter_p.h" +#include "KDChartPlotter.h" + +#include + +using namespace KDChart; +using namespace std; + +NormalPlotter::NormalPlotter( Plotter* d ) + : PlotterType( d ) +{ +} + +Plotter::PlotType NormalPlotter::type() const +{ + return Plotter::Normal; +} + +const QPair< QPointF, QPointF > NormalPlotter::calculateDataBoundaries() const +{ + return compressor().dataBoundaries(); +} + +void NormalPlotter::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + + Q_ASSERT( dynamic_cast< CartesianCoordinatePlane* >( ctx->coordinatePlane() ) ); + const CartesianCoordinatePlane* const plane = static_cast< CartesianCoordinatePlane* >( ctx->coordinatePlane() ); + const int colCount = compressor().modelDataColumns(); + const int rowCount = compressor().modelDataRows(); + + if( colCount == 0 || rowCount == 0 ) + return; + + DataValueTextInfoList textInfoList; + + for( int column = 0; column < colCount; ++column ) + { + LineAttributesInfoList lineList; + LineAttributes laPreviousCell; + CartesianDiagramDataCompressor::CachePosition previousCellPosition; + CartesianDiagramDataCompressor::DataPoint lastPoint; + + for( int row = 0; row < rowCount; ++row ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, column ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + LineAttributes laCell = diagram()->lineAttributes( sourceIndex ); + const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy(); + + if( ISNAN( point.key ) || ISNAN( point.value ) ) + { + switch( policy ) + { + case LineAttributes::MissingValuesAreBridged: // we just bridge both values + continue; + case LineAttributes::MissingValuesShownAsZero: // fall-through since that attribute makes no sense for the plotter + case LineAttributes::MissingValuesHideSegments: // fall-through since they're just hidden + default: + previousCellPosition = CartesianDiagramDataCompressor::CachePosition(); + lastPoint = CartesianDiagramDataCompressor::DataPoint(); + continue; + } + } + + // area corners, a + b are the line ends: + const QPointF a( plane->translate( QPointF( lastPoint.key, lastPoint.value ) ) ); + const QPointF b( plane->translate( QPointF( point.key, point.value ) ) ); + if( a.toPoint() == b.toPoint() ) + continue; + + const QPointF c( plane->translate( QPointF( lastPoint.key, 0.0 ) ) ); + const QPointF d( plane->translate( QPointF( point.key, 0.0 ) ) ); + + // add the pieces to painting if this is not hidden: + if ( !point.hidden /*&& !ISNAN( lastPoint.key ) && !ISNAN( lastPoint.value ) */) { + // add data point labels: + const PositionPoints pts = PositionPoints( b, a, d, c ); + // if necessary, add the area to the area list: + QList areas; + if ( laCell.displayArea() ) { + QPolygonF polygon; + polygon << a << b << d << c; + areas << polygon; + } + appendDataValueTextInfoToList( diagram(), textInfoList, sourceIndex, pts, + Position::NorthWest, Position::SouthWest, + point.value ); + if( !ISNAN( lastPoint.key ) && !ISNAN( lastPoint.value ) ) + { + paintAreas( ctx, attributesModel()->mapToSource( lastPoint.index ), areas, laCell.transparency() ); + lineList.append( LineAttributesInfo( sourceIndex, a, b ) ); + } + } + + // wrap it up: + previousCellPosition = position; + laPreviousCell = laCell; + lastPoint = point; + } + LineAttributes::MissingValuesPolicy policy = LineAttributes::MissingValuesAreBridged; //unused + paintElements( ctx, textInfoList, lineList, policy ); + } +} diff --git a/libkdchart/src/KDChartNormalPlotter_p.h b/libkdchart/src/KDChartNormalPlotter_p.h new file mode 100644 index 0000000..8f6b99e --- /dev/null +++ b/libkdchart/src/KDChartNormalPlotter_p.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTNORMALPLOTTER_P_H +#define KDCHARTNORMALPLOTTER_P_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +#include "KDChartPlotter_p.h" + +namespace KDChart { + + class NormalPlotter : public Plotter::PlotterType + { + public: + explicit NormalPlotter( Plotter* ); + virtual ~NormalPlotter() {} + virtual Plotter::PlotType type() const; + virtual const QPair< QPointF, QPointF > calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); + }; +} + +#endif diff --git a/libkdchart/src/KDChartNullPaintDevice.h b/libkdchart/src/KDChartNullPaintDevice.h new file mode 100644 index 0000000..9c09153 --- /dev/null +++ b/libkdchart/src/KDChartNullPaintDevice.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef NULL_PAINT_DEVICE_H +#define NULL_PAINT_DEVICE_H + +#include +#include + +namespace KDChart +{ + class NullPaintEngine : public QPaintEngine + { + public: + virtual bool begin(QPaintDevice * /*pdev*/) { return true; } + virtual void drawEllipse(const QRectF & /*rect*/) { } + virtual void drawEllipse(const QRect & /*rect*/) { } + virtual void drawImage(const QRectF & /*rectangle*/, const QImage & /*image*/, const QRectF & /*sr*/, Qt::ImageConversionFlags /*flags*/) { } + virtual void drawLines(const QLineF * /*lines*/, int /*lineCount*/) { } + virtual void drawLines(const QLine * /*lines*/, int /*lineCount*/) { } + virtual void drawPath(const QPainterPath & /*path*/) { } + virtual void drawPixmap(const QRectF & /*r*/, const QPixmap & /*pm*/, const QRectF & /*sr*/) { } + virtual void drawPoints(const QPointF * /*points*/, int /*pointCount*/) { } + virtual void drawPoints(const QPoint * /*points*/, int /*pointCount*/) { } + virtual void drawPolygon(const QPointF * /*points*/, int /*pointCount*/, PolygonDrawMode /*mode*/) { } + virtual void drawPolygon(const QPoint * /*points*/, int /*pointCount*/, PolygonDrawMode /*mode*/) { } + virtual void drawRects(const QRectF * /*rects*/, int /*rectCount*/) { } + virtual void drawRects(const QRect * /*rects*/, int /*rectCount*/) { } + virtual void drawTextItem(const QPointF & /*p*/, const QTextItem & /*textItem*/) { } + virtual void drawTiledPixmap(const QRectF & /*rect*/, const QPixmap & /*pixmap*/, const QPointF & /*p*/) { } + virtual bool end() { return true; } + + virtual Type type() const { return QPaintEngine::User; } + virtual void updateState(const QPaintEngineState & /*state*/) { } + }; + + class NullPaintDevice : public QPaintDevice + { + public: + NullPaintDevice(const QSize& size) : m_size(size) { } + ~NullPaintDevice() { } + + int metric(PaintDeviceMetric metric) const + { + switch(metric) + { + case QPaintDevice::PdmWidth: + return m_size.width(); + case QPaintDevice::PdmHeight: + return m_size.height(); + case QPaintDevice::PdmWidthMM: + return 1; + case QPaintDevice::PdmHeightMM: + return 1; + case QPaintDevice::PdmNumColors: + return int((uint)(-1)); + case QPaintDevice::PdmDepth: + return 1; + case QPaintDevice::PdmDpiX: + return 1; + case QPaintDevice::PdmDpiY: + return 1; + case QPaintDevice::PdmPhysicalDpiX: + return 1; + case QPaintDevice::PdmPhysicalDpiY: + return 1; + } + return 1; + } + + QPaintEngine* paintEngine() const + { + static NullPaintEngine nullPaintEngine; + return &nullPaintEngine; + } + + private: + QSize m_size; + }; + +} + +#endif diff --git a/libkdchart/src/KDChartPaintContext.cpp b/libkdchart/src/KDChartPaintContext.cpp new file mode 100644 index 0000000..58b645d --- /dev/null +++ b/libkdchart/src/KDChartPaintContext.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPaintContext.h" +#include "KDChartAbstractCoordinatePlane.h" + +#include +#include + +#include + +using namespace KDChart; + +#define d (d_func()) + +class PaintContext::Private { + +public: + QPainter* painter; + QRectF rect; + AbstractCoordinatePlane* plane; + + Private() + : painter( 0 ) + , plane ( 0 ) + {} +}; + +PaintContext::PaintContext() + : _d ( new Private() ) +{ +} + +PaintContext::~PaintContext() +{ + delete _d; +} + +const QRectF PaintContext::rectangle() const +{ + return d->rect; +} + +void PaintContext::setRectangle ( const QRectF& rect ) +{ + d->rect = rect; +} + +QPainter* PaintContext::painter() const +{ + return d->painter; +} + +void PaintContext::setPainter( QPainter* painter ) +{ + d->painter = painter; +} + +AbstractCoordinatePlane* PaintContext::coordinatePlane() const +{ + return d->plane; +} + +void PaintContext::setCoordinatePlane( AbstractCoordinatePlane* plane) +{ + d->plane = plane; +} diff --git a/libkdchart/src/KDChartPaintContext.h b/libkdchart/src/KDChartPaintContext.h new file mode 100644 index 0000000..7f000c6 --- /dev/null +++ b/libkdchart/src/KDChartPaintContext.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef PAINTCONTEXT_H +#define PAINTCONTEXT_H + +class QPainter; + +#include +#include "KDChartGlobal.h" + +namespace KDChart { + + class AbstractCoordinatePlane; + + /** + * @brief Stores information about painting diagrams + * \internal + */ + class KDCHART_EXPORT PaintContext + { + public: + PaintContext(); + ~PaintContext(); + + const QRectF rectangle () const; + void setRectangle( const QRectF& rect ); + + QPainter* painter() const; + void setPainter( QPainter* painter ); + + AbstractCoordinatePlane* coordinatePlane() const; + void setCoordinatePlane( AbstractCoordinatePlane* plane ); + + private: + class Private; + Private * _d; + Private * d_func() { return _d; } + const Private * d_func() const { return _d; } + }; + +} + +#endif /* PAINTCONTEXT_H */ + diff --git a/libkdchart/src/KDChartPainterSaver_p.h b/libkdchart/src/KDChartPainterSaver_p.h new file mode 100644 index 0000000..3766c54 --- /dev/null +++ b/libkdchart/src/KDChartPainterSaver_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPAINTERSAVER_P_H +#define KDCHARTPAINTERSAVER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include + + +namespace KDChart { + +/** + \internal + + @short Resource Allocation Is Initialization for QPainter::save and + restore + + Usage: + + Instead of + \code + painter.save(); + // ... + painter.restore(); + \endcode + + Use this: + + \code + const QPainterSaver saver( &painter ); + // ... + \endcode + + which makes sure that restore() is called when exiting from guard + clauses, or when exceptions are thrown. +*/ +class PainterSaver { + Q_DISABLE_COPY( PainterSaver ) +public: + explicit PainterSaver( QPainter* p ) + : painter( p ) + { +#if defined Q_OS_WIN + static bool initialized = false; + static bool isQt4_3_0; + if( !initialized ) + { + isQt4_3_0 = ( QString::fromLatin1( qVersion() ) == QString::fromLatin1( "4.3.0" ) ); + } + initialized = true; + m_isQt4_3_0 = isQt4_3_0; +#endif + p->save(); + } + + ~PainterSaver() + { +#if defined Q_OS_WIN + // the use of setClipRect is a workaround for a bug in Qt 4.3.0 which could + // lead to an assert on Windows + if( m_isQt4_3_0 ) + { + painter->setClipRect( 0, 0, 2, 2 ); + } +#endif + painter->restore(); + } + +private: +#if defined Q_OS_WIN + bool m_isQt4_3_0; +#endif + QPainter* const painter; +}; + +} + +#endif /* KDCHARTPAINTERSAVER_P_H */ + diff --git a/libkdchart/src/KDChartPalette.cpp b/libkdchart/src/KDChartPalette.cpp new file mode 100644 index 0000000..8f3bf65 --- /dev/null +++ b/libkdchart/src/KDChartPalette.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPalette.h" +#include +#include + +#include + +using namespace KDChart; + +namespace { + + static Palette makeDefaultPalette() { + Palette p; + + p.addBrush( Qt::red ); + p.addBrush( Qt::green ); + p.addBrush( Qt::blue ); + p.addBrush( Qt::cyan ); + p.addBrush( Qt::magenta ); + p.addBrush( Qt::yellow ); + p.addBrush( Qt::darkRed ); + p.addBrush( Qt::darkGreen ); + p.addBrush( Qt::darkBlue ); + p.addBrush( Qt::darkCyan ); + p.addBrush( Qt::darkMagenta ); + p.addBrush( Qt::darkYellow ); + + return p; + } + + static Palette makeSubduedPalette() { + Palette p; + + p.addBrush( QColor( 0xe0,0x7f,0x70 ) ); + p.addBrush( QColor( 0xe2,0xa5,0x6f ) ); + p.addBrush( QColor( 0xe0,0xc9,0x70 ) ); + p.addBrush( QColor( 0xd1,0xe0,0x70 ) ); + p.addBrush( QColor( 0xac,0xe0,0x70 ) ); + p.addBrush( QColor( 0x86,0xe0,0x70 ) ); + p.addBrush( QColor( 0x70,0xe0,0x7f ) ); + p.addBrush( QColor( 0x70,0xe0,0xa4 ) ); + p.addBrush( QColor( 0x70,0xe0,0xc9 ) ); + p.addBrush( QColor( 0x70,0xd1,0xe0 ) ); + p.addBrush( QColor( 0x70,0xac,0xe0 ) ); + p.addBrush( QColor( 0x70,0x86,0xe0 ) ); + p.addBrush( QColor( 0x7f,0x70,0xe0 ) ); + p.addBrush( QColor( 0xa4,0x70,0xe0 ) ); + p.addBrush( QColor( 0xc9,0x70,0xe0 ) ); + p.addBrush( QColor( 0xe0,0x70,0xd1 ) ); + p.addBrush( QColor( 0xe0,0x70,0xac ) ); + p.addBrush( QColor( 0xe0,0x70,0x86 ) ); + + return p; + } + + static Palette makeRainbowPalette() { + Palette p; + + p.addBrush( QColor(255, 0,196) ); + p.addBrush( QColor(255, 0, 96) ); + p.addBrush( QColor(255, 128,64) ); + p.addBrush( Qt::yellow ); + p.addBrush( Qt::green ); + p.addBrush( Qt::cyan ); + p.addBrush( QColor( 96, 96,255) ); + p.addBrush( QColor(160, 0,255) ); + for( int i = 8 ; i < 16 ; ++i ) + p.addBrush( p.getBrush(i-8).color().light(), i ); + + return p; + } + +} + +#define d d_func() + +class Palette::Private +{ +public: + explicit Private() {} + ~Private() {} + + QVector brushes; +}; + +const Palette& Palette::defaultPalette() +{ + static const Palette palette = makeDefaultPalette(); + return palette; +} + +const Palette& Palette::subduedPalette() +{ + static const Palette palette = makeSubduedPalette(); + return palette; +} + +const Palette& Palette::rainbowPalette() +{ + static const Palette palette = makeRainbowPalette(); + return palette; +} + +Palette::Palette( QObject *parent ) + : QObject( parent ), _d( new Private ) +{ + +} + +Palette::~Palette() +{ + delete _d; _d = 0; +} + + + +Palette::Palette( const Palette& r ) + : QObject(), _d( new Private( *r.d ) ) +{ +} + +Palette& Palette::operator=( const Palette& r ) +{ + Palette copy( r ); + copy.swap( *this ); + + // emit changed() ? + return *this; +} + +bool Palette::isValid() const +{ + return d->brushes.size() >= 1; +} + +int Palette::size() const +{ + return d->brushes.size(); +} + +void Palette::addBrush( const QBrush& brush, int position ) +{ + if ( position < 0 || position >= size() ) { + d->brushes.append( brush ); + } else { + d->brushes.insert( position, brush ); + } + emit changed(); +} + +QBrush Palette::getBrush( int position ) const +{ + if ( !isValid() ) return QBrush(); + return d->brushes.at( position % size() ); +} + +void Palette::removeBrush( int position ) +{ + if ( position < 0 || position >= size() ) return; + d->brushes.remove( position ); + emit changed(); +} + diff --git a/libkdchart/src/KDChartPalette.h b/libkdchart/src/KDChartPalette.h new file mode 100644 index 0000000..177a6a1 --- /dev/null +++ b/libkdchart/src/KDChartPalette.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef __KDCHART_PALETTE_H__ +#define __KDCHART_PALETTE_H__ + +#include +#include +#include "KDChartGlobal.h" + +namespace KDChart { + + /** + * \brief A Palette is a set of brushes (or colors) to be used + * for painting data sets. + * + * The palette class encapsulates a colletion of brushes, which in + * the simplest case are colors, to be used for painting a series of + * data sets. When asked for the m-th color, a palette of size n will + * wrap around and thus cycle through the available colors. + * + * Three builtin palettes are provided for convenience, one with a default + * set of colors, one with a subdued color selection, one with rainbow + * colors. + * + * When a palette changes, it emits a changed() signal. Hook up to it, if + * you want to repaint when the color selection changes. + */ + +class KDCHART_EXPORT Palette: public QObject +{ + Q_OBJECT +public: + explicit Palette( QObject *parent = 0 ); + Palette( const Palette& ); + Palette &operator= ( const Palette & ); + + ~Palette(); + + /** Provide access to the three builtin palettes, one with standard bright + * colors, one with more subdued colors, and one with rainbow colors. */ + static const Palette& defaultPalette(); + static const Palette& subduedPalette(); + static const Palette& rainbowPalette(); + + /** @return whether this represents a valid palette. For a palette to be + * valid it needs to have at least one brush associated. */ + bool isValid() const; + + /** @return the number of brushed in the palette. */ + int size() const; + + /** Adds \a brush to the palette. If no \a position is specified, the + * brush is appended. + */ + void addBrush( const QBrush & brush, int position = -1 ); + + /** + * Query the palette for a brush at the specified position. If the + * position exceeds the size of the palette, it wraps around. + */ + QBrush getBrush( int position ) const; + + /** Remove the brush at position \a position, if there is one. */ + void removeBrush( int position ); + +Q_SIGNALS: + /** Emitted whenever the palette changes. Views listen to this and + * repaing. */ + void changed(); + +private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( Palette ) +}; + +} +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::Palette ) +#endif diff --git a/libkdchart/src/KDChartPercentBarDiagram_p.cpp b/libkdchart/src/KDChartPercentBarDiagram_p.cpp new file mode 100644 index 0000000..581e98e --- /dev/null +++ b/libkdchart/src/KDChartPercentBarDiagram_p.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPercentBarDiagram_p.h" + +#include + +#include "KDChartBarDiagram.h" +#include "KDChartTextAttributes.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractCartesianDiagram.h" + +using namespace KDChart; + +PercentBarDiagram::PercentBarDiagram( BarDiagram* d ) + : BarDiagramType( d ) +{ +} + +BarDiagram::BarType PercentBarDiagram::type() const +{ + return BarDiagram::Percent; +} + +const QPair PercentBarDiagram::calculateDataBoundaries() const +{ + const double xMin = 0.0; + const double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; + const double yMin = 0.0; + const double yMax = 100.0; + + const int rowCount = xMax; + const int colCount = diagram()->model() ? diagram()->model()->columnCount( diagram()->rootIndex() ) : 0; + + double usedDepth = 0; + + for( int row = 0; row < rowCount ; ++row ) + { + for( int col = 0; col < colCount; ++col ) { + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); + QModelIndex sourceIndex = attributesModel()->mapToSource( p.index ); + ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( sourceIndex ); + + if( threeDAttrs.isEnabled() ) + if( threeDAttrs.depth() > usedDepth ) + usedDepth = threeDAttrs.depth(); + } + + } + + const QPointF bottomLeft( QPointF( xMin, yMin ) ); + const QPointF topRight( QPointF( xMax, yMax + usedDepth * 0.3 ) ); + + //qDebug() << "BarDiagram::calculateDataBoundaries () returns ( " << bottomLeft << topRight <<")"; + return QPair< QPointF, QPointF >( bottomLeft, topRight ); +} + +void PercentBarDiagram::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + + const QPair boundaries = diagram()->dataBoundaries(); // cached + + const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; + const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); + + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + + BarAttributes ba = diagram()->barAttributes( diagram()->model()->index( 0, 0, diagram()->rootIndex() ) ); + double barWidth = 0; + double maxDepth = 0; + double width = boundRight.x() - boundLeft.x(); + double groupWidth = width/ (rowCount + 2); + double spaceBetweenBars = 0; + double spaceBetweenGroups = 0; + + if ( ba.useFixedBarWidth() ) { + barWidth = ba.fixedBarWidth(); + groupWidth += barWidth; + + // Pending Michel set a min and max value for the groupWidth + // related to the area.width + if ( groupWidth < 0 ) + groupWidth = 0; + + if ( groupWidth * rowCount > width ) + groupWidth = width / rowCount; + } + + // maxLimit: allow the space between bars to be larger until area.width() + // is covered by the groups. + double maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); + + + //Pending Michel: FixMe + if ( ba.useFixedDataValueGap() ) { + if ( width > maxLimit ) + spaceBetweenBars += ba.fixedDataValueGap(); + else + spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); + } + + if ( ba.useFixedValueBlockGap() ) + spaceBetweenGroups += ba.fixedValueBlockGap(); + + calculateValueAndGapWidths( rowCount, colCount,groupWidth, + barWidth, spaceBetweenBars, spaceBetweenGroups ); + + DataValueTextInfoList list; + const double maxValue = 100; // always 100 % + double sumValues = 0; + QVector sumValuesVector; + + //calculate sum of values for each column and store + for( int row = 0; row < rowCount; ++row ) + { + for( int col = 0; col < colCount; ++col ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + //if ( point.value > 0 ) + sumValues += qMax( point.value, -point.value ); + if ( col == colCount - 1 ) { + sumValuesVector << sumValues ; + sumValues = 0; + } + } + } + + // calculate stacked percent value + for( int col = 0; col < colCount; ++col ) + { + double offset = spaceBetweenGroups; + if( ba.useFixedBarWidth() ) + offset -= ba.fixedBarWidth(); + + if( offset < 0 ) + offset = 0; + + for( int row = 0; row < rowCount ; ++row ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); + QModelIndex sourceIndex = attributesModel()->mapToSource( p.index ); + ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( sourceIndex ); + + if ( threeDAttrs.isEnabled() ){ + if ( barWidth > 0 ) + barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; + if ( barWidth <= 0 ) { + barWidth = 0; + maxDepth = offset - ( width/rowCount); + } + }else{ + barWidth = (width - (offset*rowCount))/ rowCount; + } + + const double value = qMax( p.value, -p.value ); + double stackedValues = 0.0; + double key = 0.0; + + // calculate stacked percent value + // we only take in account positives values for now. + for( int k = col; k >= 0 ; --k ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, k ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + stackedValues += qMax( point.value, -point.value ); + key = point.key; + } + + QPointF point, previousPoint; + if( sumValuesVector.at( row ) != 0 && value > 0 ) { + point = ctx->coordinatePlane()->translate( QPointF( key, stackedValues / sumValuesVector.at( row ) * maxValue ) ); + point.rx() += offset / 2; + + previousPoint = ctx->coordinatePlane()->translate( QPointF( key, ( stackedValues - value)/sumValuesVector.at(row)* maxValue ) ); + } + const double barHeight = previousPoint.y() - point.y(); + + const QRectF rect( point, QSizeF( barWidth, barHeight ) ); + appendDataValueTextInfoToList( diagram(), list, sourceIndex, PositionPoints( rect ), + Position::NorthWest, Position::SouthEast, + value ); + paintBars( ctx, sourceIndex, rect, maxDepth ); + } + } + paintDataValueTextsAndMarkers( diagram(), ctx, list, false ); +} diff --git a/libkdchart/src/KDChartPercentBarDiagram_p.h b/libkdchart/src/KDChartPercentBarDiagram_p.h new file mode 100644 index 0000000..5b3591e --- /dev/null +++ b/libkdchart/src/KDChartPercentBarDiagram_p.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPERCENTBARDIAGRAM_P_H +#define KDCHARTPERCENTBARDIAGRAM_P_H + + +#include "KDChartBarDiagram_p.h" + +namespace KDChart { + + class PercentBarDiagram : public BarDiagram::BarDiagramType + { + public: + explicit PercentBarDiagram( BarDiagram* ); + virtual ~PercentBarDiagram() {} + virtual BarDiagram::BarType type() const; + virtual const QPair calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); + }; + +} + +#endif diff --git a/libkdchart/src/KDChartPercentLineDiagram_p.cpp b/libkdchart/src/KDChartPercentLineDiagram_p.cpp new file mode 100644 index 0000000..c3b8992 --- /dev/null +++ b/libkdchart/src/KDChartPercentLineDiagram_p.cpp @@ -0,0 +1,235 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPercentLineDiagram_p.h" + +#include + +#include "KDChartBarDiagram.h" +#include "KDChartLineDiagram.h" +#include "KDChartTextAttributes.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractCartesianDiagram.h" + +using namespace KDChart; +using namespace std; + +PercentLineDiagram::PercentLineDiagram( LineDiagram* d ) + : LineDiagramType( d ) +{ +} + +LineDiagram::LineType PercentLineDiagram::type() const +{ + return LineDiagram::Percent; +} + +const QPair PercentLineDiagram::calculateDataBoundaries() const +{ + const double xMin = 0.0; + double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; + if ( !diagram()->centerDataPoints() && diagram()->model() ) + xMax -= 1; + const double yMin = 0.0; + const double yMax = 100.0; + + QPointF bottomLeft( QPointF( xMin, yMin ) ); + QPointF topRight( QPointF( xMax, yMax ) ); + return QPair ( bottomLeft, topRight ); +} + +void PercentLineDiagram::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + + const QPair boundaries = diagram()->dataBoundaries(); + const QPointF bottomLeft = boundaries.first; + const QPointF topRight = boundaries.second; + + const int columnCount = compressor().modelDataColumns(); + const int rowCount = compressor().modelDataRows(); + +// FIXME integrade column index retrieval to compressor: + int maxFound = 0; +// { // find the last column number that is not hidden +// for( int iColumn = datasetDimension() - 1; +// iColumn < columnCount; +// iColumn += datasetDimension() ) +// if( ! diagram()->isHidden( iColumn ) ) +// maxFound = iColumn; +// } + maxFound = columnCount; + // ^^^ temp + const int lastVisibleColumn = maxFound - 1; + + DataValueTextInfoList list; + LineAttributesInfoList lineList; + LineAttributes::MissingValuesPolicy policy = LineAttributes::MissingValuesAreBridged; + + //FIXME(khz): add LineAttributes::MissingValuesPolicy support for LineDiagram::Stacked and ::Percent + + double maxValue = 100; // always 100% + double sumValues = 0; + QVector percentSumValues; + + //calculate sum of values for each column and store + for ( int row = 0; row < rowCount; ++row ) + { + for( int col = 0; col < columnCount; ++col ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + const LineAttributes laCell = diagram()->lineAttributes( sourceIndex ); + const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy(); + if( ISNAN( point.value ) && policy == LineAttributes::MissingValuesAreBridged ) + point.value = interpolateMissingValue( position ); + if ( point.value > 0 ) + sumValues += point.value; + if ( col == lastVisibleColumn ) + { + percentSumValues << sumValues ; + sumValues = 0; + } + } + } + + QList bottomPoints; + bool bFirstDataset = true; + + for( int column = 0; column < columnCount; ++column ) + { + //display area can be set by dataset ( == column) and/or by cell + LineAttributes laPreviousCell; // by default no area is drawn + QModelIndex indexPreviousCell; + QList areas; + QList points; + + for( int row = 0; row < rowCount; ++row ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, column ); + CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + const LineAttributes laCell = diagram()->lineAttributes( sourceIndex ); + const bool bDisplayCellArea = laCell.displayArea(); + + double stackedValues = 0, nextValues = 0, nextKey = 0; + for ( int column2 = column; + column2 >= 0;//datasetDimension() - 1; + column2 -= 1 )//datasetDimension() ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, column2 ); + CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy(); + if( ISNAN( point.value ) && policy == LineAttributes::MissingValuesAreBridged ) + point.value = interpolateMissingValue( position ); + + const double val = point.value; + if( val > 0 ) + stackedValues += val; + //qDebug() << valueForCell( iRow, iColumn2 ); + if ( row + 1 < rowCount ){ + const CartesianDiagramDataCompressor::CachePosition position( row + 1, column2 ); + CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy(); + if( ISNAN( point.value ) && policy == LineAttributes::MissingValuesAreBridged ) + point.value = interpolateMissingValue( position ); + + const double val = point.value; + if( val > 0 ) + nextValues += val; + nextKey = point.key; + } + } + if ( percentSumValues.at( row ) != 0 ) + stackedValues = stackedValues / percentSumValues.at( row ) * maxValue; + else + stackedValues = 0.0; + //qDebug() << stackedValues << endl; + QPointF nextPoint = ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, stackedValues ) ); + points << nextPoint; + + const QPointF ptNorthWest( nextPoint ); + const QPointF ptSouthWest( + bDisplayCellArea + ? ( bFirstDataset + ? ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, 0.0 ) ) + : bottomPoints.at( row ) + ) + : nextPoint ); + QPointF ptNorthEast; + QPointF ptSouthEast; + + if ( row + 1 < rowCount ){ + if ( percentSumValues.at( row + 1 ) != 0 ) + nextValues = nextValues / percentSumValues.at( row + 1 ) * maxValue; + else + nextValues = 0.0; + QPointF toPoint = ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? nextKey + 0.5 : nextKey, nextValues ) ); + lineList.append( LineAttributesInfo( sourceIndex, nextPoint, toPoint ) ); + ptNorthEast = toPoint; + ptSouthEast = + bDisplayCellArea + ? ( bFirstDataset + ? ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? nextKey + 0.5 : nextKey, 0.0 ) ) + : bottomPoints.at( row + 1 ) + ) + : toPoint; + if( areas.count() && laCell != laPreviousCell ){ + paintAreas( ctx, indexPreviousCell, areas, laPreviousCell.transparency() ); + areas.clear(); + } + if( bDisplayCellArea ){ + QPolygonF poly; + poly << ptNorthWest << ptNorthEast << ptSouthEast << ptSouthWest; + areas << poly; + laPreviousCell = laCell; + indexPreviousCell = sourceIndex; + }else{ + //qDebug() << "no area shown for row"< calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); + }; + +} + +#endif diff --git a/libkdchart/src/KDChartPercentLyingBarDiagram_p.cpp b/libkdchart/src/KDChartPercentLyingBarDiagram_p.cpp new file mode 100644 index 0000000..e73af2e --- /dev/null +++ b/libkdchart/src/KDChartPercentLyingBarDiagram_p.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPercentLyingBarDiagram_p.h" + +#include + +#include "KDChartBarDiagram.h" +#include "KDChartTextAttributes.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractCartesianDiagram.h" + +using namespace KDChart; + +PercentLyingBarDiagram::PercentLyingBarDiagram( BarDiagram* d ) + : BarDiagramType( d ) +{ +} + +BarDiagram::BarType PercentLyingBarDiagram::type() const +{ + return BarDiagram::Percent; +} + +const QPair PercentLyingBarDiagram::calculateDataBoundaries() const +{ + //const int rowCount = compressor().modelDataRows(); + //const int colCount = compressor().modelDataColumns(); + + const double xMin = 0; + const double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; + double yMin = 0.0, yMax = 100.0; + /*for( int col = 0; col < colCount; ++col ) + { + for( int row = 0; row < rowCount; ++row ) + { + // Ordinate should begin at 0 the max value being the 100% pos + const QModelIndex idx = diagram()->model()->index( row, col, diagram()->rootIndex() ); + // only positive values are handled + double value = diagram()->model()->data( idx ).toDouble(); + if ( value > 0 ) + yMax = qMax( yMax, value ); + } + }*/ + // special cases + if ( yMax == yMin ) { + if ( yMin == 0.0 ) + yMax = 0.1; //we need at least a range + else + yMax = 0.0; // they are the same but negative + } + const QPointF bottomLeft( QPointF( yMin, xMin ) ); + const QPointF topRight( QPointF( yMax, xMax ) ); + + //qDebug() << "BarDiagram::calculateDataBoundaries () returns ( " << bottomLeft << topRight <<")"; + return QPair< QPointF, QPointF >( bottomLeft, topRight ); +} + +void PercentLyingBarDiagram::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + + const QPair boundaries = diagram()->dataBoundaries(); // cached + + const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; + const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); + + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + + BarAttributes ba = diagram()->barAttributes( diagram()->model()->index( 0, 0, diagram()->rootIndex() ) ); + double barWidth = 0; + double maxDepth = 0; + double width = boundLeft.y() - boundRight.y(); + QPointF testVector = boundRight - boundLeft; + double groupWidth = width/ (rowCount + 2); + double spaceBetweenBars = 0; + double spaceBetweenGroups = 0; + + if ( ba.useFixedBarWidth() ) { + barWidth = ba.fixedBarWidth(); + groupWidth += barWidth; + + // Pending Michel set a min and max value for the groupWidth + // related to the area.width + if ( groupWidth < 0 ) + groupWidth = 0; + + if ( groupWidth * rowCount > width ) + groupWidth = width / rowCount; + } + + // maxLimit: allow the space between bars to be larger until area.width() + // is covered by the groups. + double maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); + + + //Pending Michel: FixMe + if ( ba.useFixedDataValueGap() ) { + if ( width > maxLimit ) + spaceBetweenBars += ba.fixedDataValueGap(); + else + spaceBetweenBars = ((ctx->rectangle().width()/rowCount) - groupWidth)/(colCount-1); + } + + if ( ba.useFixedValueBlockGap() ) + spaceBetweenGroups += ba.fixedValueBlockGap(); + + calculateValueAndGapWidths( rowCount, colCount,groupWidth, + barWidth, spaceBetweenBars, spaceBetweenGroups ); + + DataValueTextInfoList list; + const double maxValue = 100.0; // always 100 % + double sumValues = 0; + QVector sumValuesVector; + + //calculate sum of values for each column and store + for( int row = 0; row < rowCount; ++row ) + { + for( int col = 0; col < colCount; ++col ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + //if ( point.value > 0 ) + sumValues += qMax( point.value, -point.value ); + if ( col == colCount - 1 ) { + sumValuesVector << sumValues ; + sumValues = 0; + } + } + } + + // calculate stacked percent value + for( int curRow = rowCount - 1; curRow >= 0; --curRow ) + { + double offset = spaceBetweenGroups; + if( ba.useFixedBarWidth() ) + offset -= ba.fixedBarWidth(); + + if( offset < 0 ) + offset = 0; + + for( int col = 0; col < colCount ; ++col ) + { + double threeDOffset = 0.0; + const CartesianDiagramDataCompressor::CachePosition position( curRow, col ); + const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); + QModelIndex sourceIndex = attributesModel()->mapToSource( p.index ); + ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( sourceIndex ); + + if ( threeDAttrs.isEnabled() ){ + if ( barWidth > 0 ) { + barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; + threeDOffset = threeDAttrs.depth(); + } + if ( barWidth <= 0 ) { + barWidth = 0.1; + threeDOffset = (width - (offset*rowCount))/ rowCount; + } + }else{ + barWidth = (width - (offset*rowCount))/ rowCount; + } + + const double value = qMax( p.value, -p.value ); + double stackedValues = 0.0; + double key = 0.0; + + // calculate stacked percent value + // we only take in account positives values for now. + for( int k = col; k >= 0 ; --k ) + { + const CartesianDiagramDataCompressor::CachePosition position( curRow, k ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + stackedValues += qMax( point.value, -point.value ); + key = point.key; + } + + QPointF point, previousPoint; + if( sumValuesVector.at( curRow ) != 0 && value > 0 ) { + QPointF dataPoint( ( stackedValues / sumValuesVector.at( curRow ) * maxValue ), rowCount - key ); + point = ctx->coordinatePlane()->translate( dataPoint ); + point.ry() += offset / 2 + threeDOffset; + + previousPoint = ctx->coordinatePlane()->translate( QPointF( ( ( stackedValues - value) / sumValuesVector.at( curRow ) * maxValue ), rowCount - key ) ); + } + + const double barHeight = point.x() - previousPoint.x(); + + point.setX ( point.x() - barHeight ); + + const QRectF rect( point, QSizeF( barHeight, barWidth ) ); + appendDataValueTextInfoToList( diagram(), list, sourceIndex, PositionPoints( rect ), + Position::NorthEast, Position::SouthWest, + value ); + paintBars( ctx, sourceIndex, rect, maxDepth ); + } + } + paintDataValueTextsAndMarkers( diagram(), ctx, list, false ); +} diff --git a/libkdchart/src/KDChartPercentLyingBarDiagram_p.h b/libkdchart/src/KDChartPercentLyingBarDiagram_p.h new file mode 100644 index 0000000..852b500 --- /dev/null +++ b/libkdchart/src/KDChartPercentLyingBarDiagram_p.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPERCENTLYINGBARDIAGRAM_P_H +#define KDCHARTPERCENTLYINGBARDIAGRAM_P_H + +#include "KDChartBarDiagram_p.h" + +namespace KDChart { + + class PercentLyingBarDiagram : public BarDiagram::BarDiagramType + { + public: + explicit PercentLyingBarDiagram( BarDiagram* ); + virtual ~PercentLyingBarDiagram() {} + virtual BarDiagram::BarType type() const; + virtual const QPair calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); + }; + +} + +#endif diff --git a/libkdchart/src/KDChartPercentPlotter_p.cpp b/libkdchart/src/KDChartPercentPlotter_p.cpp new file mode 100644 index 0000000..44e18d4 --- /dev/null +++ b/libkdchart/src/KDChartPercentPlotter_p.cpp @@ -0,0 +1,269 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPercentPlotter_p.h" +#include "KDChartPlotter.h" + +#include + +using namespace KDChart; +using namespace std; + +PercentPlotter::PercentPlotter( Plotter* d ) + : PlotterType( d ) +{ +} + +Plotter::PlotType PercentPlotter::type() const +{ + return Plotter::Percent; +} + +const QPair< QPointF, QPointF > PercentPlotter::calculateDataBoundaries() const +{ + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + double xMin = std::numeric_limits< double >::quiet_NaN(); + double xMax = std::numeric_limits< double >::quiet_NaN(); + const double yMin = 0.0; + const double yMax = 100.0; + + for( int column = 0; column < colCount; ++column ) + { + for ( int row = 0; row < rowCount; ++row ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, column ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + + const double valueX = ISNAN( point.key ) ? 0.0 : point.key; + + if( ISNAN( xMin ) ) + { + xMin = valueX; + xMax = valueX; + } + else + { + xMin = qMin( xMin, valueX ); + xMax = qMax( xMax, valueX ); + } + } + } + + // NOTE: calculateDataBoundaries must return the *real* data boundaries! + // i.e. we may NOT fake yMin to be qMin( 0.0, yMin ) + // (khz, 2008-01-24) + const QPointF bottomLeft( QPointF( xMin, yMin ) ); + const QPointF topRight( QPointF( xMax, yMax ) ); + return QPair< QPointF, QPointF >( bottomLeft, topRight ); +} + +class Value +{ +public: + Value() + : value( std::numeric_limits< double >::quiet_NaN() ) + { + } + // allow implicit conversion + Value( double value ) + : value( value ) + { + } + operator double() const + { + return value; + } + +private: + double value; +}; + +void PercentPlotter::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + + Q_ASSERT( dynamic_cast< CartesianCoordinatePlane* >( ctx->coordinatePlane() ) ); + const CartesianCoordinatePlane* const plane = static_cast< CartesianCoordinatePlane* >( ctx->coordinatePlane() ); + const int colCount = compressor().modelDataColumns(); + const int rowCount = compressor().modelDataRows(); + + if( colCount == 0 || rowCount == 0 ) + return; + + DataValueTextInfoList textInfoList; + LineAttributes::MissingValuesPolicy policy = LineAttributes::MissingValuesAreBridged; // ??? + + // this map contains the y-values to each x-value + QMap< double, QVector< QPair< Value, QModelIndex > > > diagramValues; + + for( int col = 0; col < colCount; ++col ) + { + for( int row = 0; row < rowCount; ++row ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + diagramValues[ point.key ].resize( colCount ); + diagramValues[ point.key ][ col ].first = point.value; + diagramValues[ point.key ][ col ].second = point.index; + } + } + + // the sums of the y-values per x-value + QMap< double, double > yValueSums; + // the x-values + QList< double > xValues = diagramValues.keys(); + // make sure it's sorted + qSort( xValues ); + Q_FOREACH( const double xValue, xValues ) + { + // the y-values to the current x-value + QVector< QPair< Value, QModelIndex > >& yValues = diagramValues[ xValue ]; + Q_ASSERT( yValues.count() == colCount ); + + for( int column = 0; column < colCount; ++column ) + { + QPair< Value, QModelIndex >& data = yValues[ column ]; + // if the index is invalid, there was no value. Let's interpolate. + if( !data.second.isValid() ) + { + QPair< QPair< double, Value >, QModelIndex > left; + QPair< QPair< double, Value >, QModelIndex > right; + int xIndex = 0; + // let's find the next lower value + for( xIndex = xValues.indexOf( xValue ); xIndex >= 0; --xIndex ) + { + if( diagramValues[ xValues[ xIndex ] ][ column ].second.isValid() ) + { + left.first.first = xValues[ xIndex ]; + left.first.second = diagramValues[ left.first.first ][ column ].first; + left.second = diagramValues[ xValues[ xIndex ] ][ column ].second; + break; + } + } + // let's find the next higher value + for( xIndex = xValues.indexOf( xValue ); xIndex < xValues.count(); ++xIndex ) + { + if( diagramValues[ xValues[ xIndex ] ][ column ].second.isValid() ) + { + right.first.first = xValues[ xIndex ]; + right.first.second = diagramValues[ right.first.first ][ column ].first; + right.second = diagramValues[ xValues[ xIndex ] ][ column ].second; + break; + } + } + + // interpolate out of them (left and/or right might be invalid, but this doesn't matter here) + const double leftX = left.first.first; + const double rightX = right.first.first; + const double leftY = left.first.second; + const double rightY = right.first.second; + + data.first = leftY + ( rightY - leftY ) * ( xValue - leftX ) / ( rightX - leftX ); + // if the result is a valid value, let's assign the index, too + if( !ISNAN( data.first.operator double() ) ) + data.second = left.second; + } + + // sum it up + if( !ISNAN( yValues[ column ].first.operator double() ) ) + yValueSums[ xValue ] += yValues[ column ].first; + } + } + + for( int column = 0; column < colCount; ++column ) + { + LineAttributesInfoList lineList; + LineAttributes laPreviousCell; + CartesianDiagramDataCompressor::CachePosition previousCellPosition; + + CartesianDiagramDataCompressor::DataPoint lastPoint; + + qreal lastExtraY = 0.0; + qreal lastValue = 0.0; + + QMapIterator< double, QVector< QPair< Value, QModelIndex > > > i( diagramValues ); + while( i.hasNext() ) + { + i.next(); + CartesianDiagramDataCompressor::DataPoint point; + point.key = i.key(); + const QPair< Value, QModelIndex >& data = i.value().at( column ); + point.value = data.first; + point.index = data.second; + + if( ISNAN( point.key ) || ISNAN( point.value ) ) + { + previousCellPosition = CartesianDiagramDataCompressor::CachePosition(); + continue; + } + + double extraY = 0.0; + for( int col = column - 1; col >= 0; --col ) + { + const double y = i.value().at( col ).first; + if( !ISNAN( y ) ) + extraY += y; + } + + LineAttributes laCell; + + const qreal value = ( point.value + extraY ) / yValueSums[ i.key() ] * 100; + + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + // area corners, a + b are the line ends: + const QPointF a( plane->translate( QPointF( lastPoint.key, lastValue ) ) ); + const QPointF b( plane->translate( QPointF( point.key, value ) ) ); + const QPointF c( plane->translate( QPointF( lastPoint.key, lastExtraY / yValueSums[ i.key() ] * 100 ) ) ); + const QPointF d( plane->translate( QPointF( point.key, extraY / yValueSums[ i.key() ] * 100 ) ) ); + // add the line to the list: + laCell = diagram()->lineAttributes( sourceIndex ); + // add data point labels: + const PositionPoints pts = PositionPoints( b, a, d, c ); + // if necessary, add the area to the area list: + QList areas; + if ( laCell.displayArea() ) { + QPolygonF polygon; + polygon << a << b << d << c; + areas << polygon; + } + // add the pieces to painting if this is not hidden: + if ( !point.hidden /*&& !ISNAN( lastPoint.key ) && !ISNAN( lastPoint.value ) */) { + appendDataValueTextInfoToList( diagram(), textInfoList, sourceIndex, pts, + Position::NorthWest, Position::SouthWest, + value ); + if( !ISNAN( lastPoint.key ) && !ISNAN( lastPoint.value ) ) + { + paintAreas( ctx, attributesModel()->mapToSource( lastPoint.index ), areas, laCell.transparency() ); + lineList.append( LineAttributesInfo( sourceIndex, a, b ) ); + } + } + + // wrap it up: + laPreviousCell = laCell; + lastPoint = point; + lastExtraY = extraY; + lastValue = value; + } + paintElements( ctx, textInfoList, lineList, policy ); + } +} diff --git a/libkdchart/src/KDChartPercentPlotter_p.h b/libkdchart/src/KDChartPercentPlotter_p.h new file mode 100644 index 0000000..ce658c5 --- /dev/null +++ b/libkdchart/src/KDChartPercentPlotter_p.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPERCENTPLOTTER_P_H +#define KDCHARTPRECENTPLOTTER_P_H + +#include "KDChartPlotter_p.h" + +namespace KDChart { + + class PercentPlotter : public Plotter::PlotterType + { + public: + explicit PercentPlotter( Plotter* ); + virtual ~PercentPlotter() {} + virtual Plotter::PlotType type() const; + virtual const QPair< QPointF, QPointF > calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); + }; +} + +#endif diff --git a/libkdchart/src/KDChartPieAttributes.cpp b/libkdchart/src/KDChartPieAttributes.cpp new file mode 100644 index 0000000..94c3a12 --- /dev/null +++ b/libkdchart/src/KDChartPieAttributes.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPieAttributes.h" +#include "KDChartPieAttributes_p.h" + +#include + +#include + +#define d d_func() + + +using namespace KDChart; + + +PieAttributes::Private::Private() + : explodeFactor( 0.0 ) + , tangentialGapFactor( 0.0 ) + , radialGapFactor( 0.0 ) +{ +} + + +PieAttributes::PieAttributes() + : _d( new Private() ) +{ +} + +PieAttributes::PieAttributes( const PieAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +PieAttributes& PieAttributes::operator= ( const PieAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +PieAttributes::~PieAttributes() +{ + delete _d; _d = 0; +} + + +bool PieAttributes::operator==( const PieAttributes& r ) const +{ + return + explodeFactor() == r.explodeFactor() && + gapFactor( true ) == r.gapFactor( true ) && + gapFactor( false) == r.gapFactor( false); +} + + +void PieAttributes::init( ) +{ + +} + +void PieAttributes::setExplode( bool enabled ) +{ + d->explodeFactor = (enabled ? 0.1 : 0.0); +} + +bool PieAttributes::explode() const +{ + return (d->explodeFactor != 0.0); +} + +void PieAttributes::setExplodeFactor( qreal factor ) +{ + d->explodeFactor = factor; +} + +qreal PieAttributes::explodeFactor() const +{ + return d->explodeFactor; +} + +void PieAttributes::setGapFactor( bool circular, qreal factor ) +{ + if ( circular ) + d->tangentialGapFactor = factor; + else + d->radialGapFactor = factor; +} + +qreal PieAttributes::gapFactor( bool circular ) const +{ + return circular ? d->tangentialGapFactor : d->radialGapFactor; +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::PieAttributes& a) +{ + dbg << "KDChart::PieAttributes("; + dbg << "explodeFactor="<< a.explodeFactor() << ")"; + return dbg; +} +#endif /* QT_NO_DEBUG_STREAM */ + diff --git a/libkdchart/src/KDChartPieAttributes.h b/libkdchart/src/KDChartPieAttributes.h new file mode 100644 index 0000000..011c278 --- /dev/null +++ b/libkdchart/src/KDChartPieAttributes.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHART_PIE_ATTRIBUTES_H +#define KDCHART_PIE_ATTRIBUTES_H + +#include +#include "KDChartAbstractThreeDAttributes.h" +#include "KDChartGlobal.h" + +namespace KDChart { + +/** + * @brief A set of attributes controlling the appearance of pie charts + */ +class KDCHART_EXPORT PieAttributes +{ +public: + PieAttributes(); + PieAttributes( const PieAttributes& ); + PieAttributes &operator= ( const PieAttributes& ); + + ~PieAttributes(); + + /** \brief Enable or disable exploding the respective pie piece(s). + * + * The default explode factor is 10 percent; use setExplodeFactor + * to specify a different factor. + * + * \note This is a convenience function: Calling setExplode( true ) + * does the same as calling setExplodeFactor( 0.1 ), and calling + * setExplode( false ) does the same as calling setExplodeFactor( 0.0 ). + * + * \sa setExplodeFactor + */ + void setExplode( bool explode ); + + /** @return whether the respective pie piece(s) will be exploded. */ + bool explode() const; + + /** Set the explode factor. + * The explode factor is a qreal between 0 and 1, and is interpreted + * as a percentage of the total available radius of the pie. + * + * \sa setExplode + */ + void setExplodeFactor( qreal factor ); + + /** @return the explode factor set by setExplode or by setExplodeFactor. */ + qreal explodeFactor() const; + + void setGapFactor( bool circular, qreal factor ); + qreal gapFactor( bool circular ) const; + + bool operator==( const PieAttributes& ) const; + inline bool operator!=( const PieAttributes& other ) const { return !operator==(other); } + +private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( PieAttributes ) +}; // End of class PieAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::PieAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + + +Q_DECLARE_METATYPE( KDChart::PieAttributes ) +Q_DECLARE_TYPEINFO( KDChart::PieAttributes, Q_MOVABLE_TYPE ); +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::PieAttributes ) + +#endif // KDCHART_PIE_ATTRIBUTES_H diff --git a/libkdchart/src/KDChartPieAttributes_p.h b/libkdchart/src/KDChartPieAttributes_p.h new file mode 100644 index 0000000..827f6bc --- /dev/null +++ b/libkdchart/src/KDChartPieAttributes_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHART_PIE_ATTRIBUTES_P_H +#define KDCHART_PIE_ATTRIBUTES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include + + +namespace KDChart { + +/** + * \internal + */ +class PieAttributes::Private +{ + friend class PieAttributes; +public: + Private(); + + qreal explodeFactor; + qreal tangentialGapFactor; + qreal radialGapFactor; +}; + +} + +#endif // KDCHART_PIE_ATTRIBUTES_P_H diff --git a/libkdchart/src/KDChartPieDiagram.cpp b/libkdchart/src/KDChartPieDiagram.cpp new file mode 100644 index 0000000..fcc5c17 --- /dev/null +++ b/libkdchart/src/KDChartPieDiagram.cpp @@ -0,0 +1,1116 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include +#include +#include + +#include "KDChartPieDiagram.h" +#include "KDChartPieDiagram_p.h" + +#include "KDChartAttributesModel.h" +#include "KDChartPaintContext.h" +#include "KDChartPieAttributes.h" +#include "KDChartThreeDPieAttributes.h" +#include "KDChartPainterSaver_p.h" +#include "KDChartDataValueAttributes.h" +#include "KDChartNullPaintDevice.h" + +#include + + +using namespace KDChart; + +PieDiagram::Private::Private() +{ +} + +PieDiagram::Private::~Private() {} + +#define d d_func() + +PieDiagram::PieDiagram( QWidget* parent, PolarCoordinatePlane* plane ) : + AbstractPieDiagram( new Private(), parent, plane ) +{ + init(); +} + +PieDiagram::~PieDiagram() +{ +} + +void PieDiagram::init() +{ +} + +/** + * Creates an exact copy of this diagram. + */ +PieDiagram * PieDiagram::clone() const +{ + return new PieDiagram( new Private( *d ) ); +} + +const QPair PieDiagram::calculateDataBoundaries () const +{ + if ( !checkInvariants( true ) ) return QPair( QPointF( 0, 0 ), QPointF( 0, 0 ) ); + + const PieAttributes attrs( pieAttributes( model()->index( 0, 0, rootIndex() ) ) ); + + QPointF bottomLeft ( QPointF( 0, 0 ) ); + QPointF topRight; + // If we explode, we need extra space for the pie slice that has + // the largest explosion distance. + if ( attrs.explode() ) { + const int colCount = columnCount(); + qreal maxExplode = 0.0; + for( int j = 0; j < colCount; ++j ){ + const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) ); + maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() ); + } + topRight = QPointF( 1.0+maxExplode, 1.0+maxExplode ); + }else{ + topRight = QPointF( 1.0, 1.0 ); + } + return QPair ( bottomLeft, topRight ); +} + + +void PieDiagram::paintEvent( QPaintEvent* ) +{ + QPainter painter ( viewport() ); + PaintContext ctx; + ctx.setPainter ( &painter ); + ctx.setRectangle( QRectF ( 0, 0, width(), height() ) ); + paint ( &ctx ); +} + +void PieDiagram::resizeEvent ( QResizeEvent*) +{ +} + +void PieDiagram::resize ( const QSizeF& ) +{ +} + +static QRectF buildReferenceRect( const PolarCoordinatePlane* plane ) +{ + QRectF contentsRect; +//qDebug() << ".........................................."; + QPointF referencePointAtTop = plane->translate( QPointF( 1, 0 ) ); + QPointF temp = plane->translate( QPointF( 0, 0 ) ) - referencePointAtTop; + const double offset = temp.y(); + referencePointAtTop.setX( referencePointAtTop.x() - offset ); + contentsRect.setTopLeft( referencePointAtTop ); + contentsRect.setBottomRight( referencePointAtTop + QPointF( 2*offset, 2*offset) ); +//qDebug() << contentsRect; + return contentsRect; +} +/* +void PieDiagram::paint( PaintContext* ctx ) +{ + if ( !checkInvariants(true) ) return; + const int colCount = model()->columnCount(rootIndex()); + QRectF contentsRect = buildReferenceRect( polarCoordinatePlane() ); + DataValueTextInfoList list; + double startAngle = startPosition(); + double startAngleValueSpace = valueTotals() / 360 * startAngle; + for ( int j=0; jdata( model()->index( 0, j,rootIndex() ) ).toDouble() ); + double spanAngle = polarCoordinatePlane()->translatePolar( QPointF( nextValue, 1 ) ).x(); + if ( spanAngle == 0 ) continue; + QBrush brush = qVariantValue( attributesModel()->headerData( j, Qt::Vertical, KDChart::DatasetBrushRole ) ); + QPen pen = qVariantValue( attributesModel()->headerData( j, Qt::Vertical, KDChart::DatasetPenRole ) ); + PainterSaver painterSaver( ctx->painter() ); + ctx->painter()->setRenderHint ( QPainter::Antialiasing ); + ctx->painter()->setBrush( brush ); + ctx->painter()->setPen( pen ); + + // Explosion support + QRectF pieRect = contentsRect; + if( explode() ) { + QPointF oldCenter = contentsRect.center(); + QPointF newCenter = polarCoordinatePlane()->translate( QPointF( explodeFactor( j ), + startAngleValueSpace + nextValue/2.0 ) ); + QPointF difference = newCenter - oldCenter; + pieRect.translate( difference ); + } + + ctx->painter()->drawPie( pieRect, ( int ) ((-startAngle + 90 )), ( int ) (-spanAngle) ); + startAngle += spanAngle; + startAngleValueSpace += nextValue; + } + d->clearListOfAlreadyDrawnDataValueTexts(); + DataValueTextInfoListIterator it( list ); + while ( it.hasNext() ) { + const DataValueTextInfo& info = it.next(); + paintDataValueText( ctx->painter(), info.index, info.pos, info.value ); + } +} +*/ + +void PieDiagram::paint(PaintContext* ctx) +{ + // Painting is a two stage process + // In the first stage we figure out how much space is needed + // for text labels. + // In the second stage, we make use of that information and + // perform the actual painting. + QPainter* actualPainter = ctx->painter(); + QRectF textBoundingRect; + + // Use a null paint device and perform the first painting. + KDChart::NullPaintDevice nullPd(ctx->rectangle().size().toSize()); + QPainter nullPainter(&nullPd); + ctx->setPainter(&nullPainter); + paintInternal(ctx, textBoundingRect); + + //edit start + // point from the text is getting printed + /*QPoint currentPosition = textBoundingRect.bottomLeft().toPoint(); + + QPoint textRectCenter = textBoundingRect.center().toPoint(); + + qreal newX = currentPosition.x() - textRectCenter.x(); + qreal newY = currentPosition.y() - textRectCenter.y(); + currentPosition.setX(newX); + currentPosition.setY(newY); + + textBoundingRect.translate(currentPosition);*/ + //edit end + // Now perform the real painting + ctx->setPainter(actualPainter); + paintInternal(ctx, textBoundingRect); +} + +void PieDiagram::paintInternal(PaintContext* ctx, QRectF& textBoundingRect) +{ + // note: Not having any data model assigned is no bug + // but we can not draw a diagram then either. + if ( !checkInvariants(true) ) + return; + + d->reverseMapper.clear(); + + const PieAttributes attrs( pieAttributes() ); + const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( model()->index( 0, 0, rootIndex() ) ) ); + + const int colCount = columnCount(); + + QRectF contentsRect( buildReferenceRect( polarCoordinatePlane() ) ); + contentsRect = ctx->rectangle(); +// contentsRect = geometry(); +//qDebug() << contentsRect; + if( contentsRect.isEmpty() ) + return; + + DataValueTextInfoList list; + const qreal sum = valueTotals(); + + if( sum == 0.0 ) //nothing to draw + return; + + d->startAngles.resize( colCount ); + d->angleLens.resize( colCount ); + + // compute position + d->size = qMin( contentsRect.width(), contentsRect.height() ); // initial size + + // if the pies explode, we need to give them additional space => + // make the basic size smaller + qreal maxExplode = 0.0; + for( int j = 0; j < colCount; ++j ){ + const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) ); + maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() ); + } + d->size /= ( 1.0 + 1.0 * maxExplode ); + + if(!textBoundingRect.isEmpty()) + { + // Find out the maximum distance from every corner of the rectangle with + // the center. + double maxDistance = 0, dist = 0; + + QPointF center = ctx->rectangle().center(); + + dist = qAbs(textBoundingRect.right() - center.x()); + if(dist > maxDistance) + maxDistance = dist; + + dist = qAbs(textBoundingRect.left() - center.x()); + if(dist > maxDistance) + maxDistance = dist; + + dist = qAbs(textBoundingRect.top() - center.y()); + if(dist > maxDistance) + maxDistance = dist; + + dist = qAbs(textBoundingRect.bottom() - center.y()); + if(dist > maxDistance) + maxDistance = dist; + + double size = d->size; + double diff = (2*maxDistance - d->size); + if(diff > 0) + d->size *= 1.0-(diff/size); + } + + if(d->size < 0) + d->size = 0; + + qreal sizeFor3DEffect = 0.0; + if ( ! threeDAttrs.isEnabled() ) { + + qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 ); + qreal y = ( contentsRect.height() == d->size ) ? 0.0 : ( ( contentsRect.height() - d->size ) / 2.0 ); + d->position = QRectF( x, y, d->size, d->size ); + d->position.translate( contentsRect.left(), contentsRect.top() ); + } else { + // threeD: width is the maximum possible width; height is 1/2 of that + qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 ); + qreal height = d->size; + // make sure that the height plus the threeDheight is not more than the + // available size + if ( threeDAttrs.depth() >= 0.0 ) { + // positive pie height: absolute value + sizeFor3DEffect = threeDAttrs.depth(); + height = d->size - sizeFor3DEffect; + } else { + // negative pie height: relative value + sizeFor3DEffect = - threeDAttrs.depth() / 100.0 * height; + height = d->size - sizeFor3DEffect; + } + qreal y = ( contentsRect.height() == height ) ? 0.0 : ( ( contentsRect.height() - height - sizeFor3DEffect ) / 2.0 ); + + d->position = QRectF( contentsRect.left() + x, contentsRect.top() + y, + d->size, height ); + // d->position.moveBy( contentsRect.left(), contentsRect.top() ); + } + + const PolarCoordinatePlane * plane = polarCoordinatePlane(); + const qreal sectorsPerValue = 360.0 / sum; + qreal currentValue = plane ? plane->startPosition() : 0.0; + + bool atLeastOneValue = false; // guard against completely empty tables + QVariant vValY; + for ( int iColumn = 0; iColumn < colCount; ++iColumn ) { + // is there anything at all at this column? + bool bOK; + const double cellValue = qAbs( model()->data( model()->index( 0, iColumn, rootIndex() ) ) + .toDouble( &bOK ) ); + + if( bOK ){ + d->startAngles[ iColumn ] = currentValue; + d->angleLens[ iColumn ] = cellValue * sectorsPerValue; + atLeastOneValue = true; + } else { // mark as non-existent + d->angleLens[ iColumn ] = 0.0; + if ( iColumn > 0.0 ) + d->startAngles[ iColumn ] = d->startAngles[ iColumn - 1 ]; + else + d->startAngles[ iColumn ] = currentValue; + } + //qDebug() << "d->startAngles["<angleLens["<angleLens[ iColumn ] + // << " = " << d->startAngles[ iColumn ]+d->angleLens[ iColumn ]; + + currentValue = d->startAngles[ iColumn ] + d->angleLens[ iColumn ]; + } + + // If there was no value at all, bail out, to avoid endless loops + // later on (e.g. in findPieAt()). + if( ! atLeastOneValue ) + return; + + + // Find the backmost pie which is at +90° and needs to be drawn + // first + int backmostpie = findPieAt( 90, colCount ); + // Find the frontmost pie (at -90°/+270°) that should be drawn last + int frontmostpie = findPieAt( 270, colCount ); + // the right- and the leftmost (only needed in some special cases...) + int rightmostpie = findPieAt( 0, colCount ); + int leftmostpie = findPieAt( 180, colCount ); + + + int currentLeftPie = backmostpie; + int currentRightPie = backmostpie; + + d->clearListOfAlreadyDrawnDataValueTexts(); + + drawOnePie( ctx->painter(), &list, 0, backmostpie, granularity(), sizeFor3DEffect ); + + if( backmostpie == frontmostpie ) + { + if( backmostpie == leftmostpie ) + currentLeftPie = findLeftPie( currentLeftPie, colCount ); + if( backmostpie == rightmostpie ) + currentRightPie = findRightPie( currentRightPie, colCount ); + } + while( currentLeftPie != frontmostpie ) + { + if( currentLeftPie != backmostpie ) + drawOnePie( ctx->painter(), &list, 0, currentLeftPie, granularity(), sizeFor3DEffect ); + currentLeftPie = findLeftPie( currentLeftPie, colCount ); + } + while( currentRightPie != frontmostpie ) + { + if( currentRightPie != backmostpie ) + drawOnePie( ctx->painter(), &list, 0, currentRightPie, granularity(), sizeFor3DEffect ); + currentRightPie = findRightPie( currentRightPie, colCount ); + } + + // if the backmost pie is not the frontmost pie, we draw the frontmost at last + if( backmostpie != frontmostpie || ! threeDPieAttributes().isEnabled() ) + { + drawOnePie( ctx->painter(), &list, 0, frontmostpie, granularity(), sizeFor3DEffect ); + // otherwise, this gets a bit more complicated... +/* } else if( threeDPieAttributes().isEnabled() ) { + //drawPieSurface( ctx->painter(), 0, frontmostpie, granularity() ); + const QModelIndex index = model()->index( 0, frontmostpie, rootIndex() ); + QPen pen = this->pen( index ); + ctx->painter()->setBrush( brush( index ) ); + if ( threeDAttrs.isEnabled() ) + pen.setColor( QColor( 0, 0, 0 ) ); + ctx->painter()->setPen( pen ); + + qreal startAngle = d->startAngles[ frontmostpie ]; + if( startAngle > 360 ) + startAngle -= 360; + + qreal endAngle = startAngle + d->angleLens[ frontmostpie ]; + startAngle = qMax( startAngle, 180.0 ); + + drawArcEffectSegment( ctx->painter(), piePosition( 0, frontmostpie), + sizeFor3DEffect, startAngle, endAngle, granularity() );*/ + } + + d->paintDataValueTextsAndMarkers( this, ctx, list, false, false, &textBoundingRect ); +} + +#if defined ( Q_WS_WIN) +#define trunc(x) ((int)(x)) +#endif + +QRectF PieDiagram::piePosition( uint dataset, uint pie ) const +{ + Q_UNUSED( dataset ); + qreal angleLen = d->angleLens[ pie ]; + qreal startAngle = d->startAngles[ pie ]; + QModelIndex index( model()->index( 0, pie, rootIndex() ) ); + const PieAttributes attrs( pieAttributes( index ) ); + const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) ); + + QRectF drawPosition( d->position ); + + if ( attrs.explode() ) { + qreal explodeAngle = ( startAngle + angleLen / 2.0 ); + qreal explodeAngleRad = DEGTORAD( explodeAngle ); + qreal cosAngle = cos( explodeAngleRad ); + qreal sinAngle = -sin( explodeAngleRad ); + qreal explodeX = attrs.explodeFactor() * d->size / 2.0 * cosAngle; + qreal explodeY = attrs.explodeFactor() * d->size / 2.0 * sinAngle; + drawPosition.translate( explodeX, explodeY ); + } + return drawPosition; + } + +/** + Internal method that draws one of the pies in a pie chart. + + \param painter the QPainter to draw in + \param dataset the dataset to draw the pie for + \param pie the pie to draw + \param threeDPieHeight the height of the three dimnensional effect + */ +void PieDiagram::drawOnePie( QPainter* painter, + DataValueTextInfoList* list, + uint dataset, uint pie, + qreal granularity, + qreal threeDPieHeight ) +{ + Q_UNUSED( threeDPieHeight ); + // Is there anything to draw at all? + const qreal angleLen = d->angleLens[ pie ]; + if ( angleLen ) { + const QModelIndex index( model()->index( 0, pie, rootIndex() ) ); + const PieAttributes attrs( pieAttributes( index ) ); + const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) ); + + const QRectF drawPosition = piePosition( dataset, pie ); + + draw3DEffect( painter, + drawPosition, dataset, pie, + granularity, + threeDAttrs, + attrs.explode() ); + + drawPieSurface( painter, list, dataset, pie, granularity ); + } +} + +/** + Internal method that draws the surface of one of the pies in a pie chart. + + \param painter the QPainter to draw in + \param dataset the dataset to draw the pie for + \param pie the pie to draw + */ +void PieDiagram::drawPieSurface( QPainter* painter, + DataValueTextInfoList* list, + uint dataset, uint pie, + qreal granularity ) +{ + // Is there anything to draw at all? + qreal angleLen = d->angleLens[ pie ]; + if ( angleLen ) { + qreal startAngle = d->startAngles[ pie ]; + + QModelIndex index( model()->index( 0, pie, rootIndex() ) ); + const PieAttributes attrs( pieAttributes( index ) ); + const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) ); + + QRectF drawPosition = piePosition( dataset, pie ); + + painter->setRenderHint ( QPainter::Antialiasing ); + painter->setBrush( brush( index ) ); + painter->setPen( pen( index ) ); +// if ( threeDAttrs.isEnabled() ) +// pen.setColor( QColor( 0, 0, 0 ) ); +// painter->setPen( pen ); + + if ( angleLen == 360 ) { + // full circle, avoid nasty line in the middle + painter->drawEllipse( drawPosition ); + + //Add polygon to Reverse mapper for showing tool tips. + QPolygonF poly( drawPosition ); + d->reverseMapper.addPolygon( index.row(), index.column(), poly ); + } else { + // draw the top of this piece + // Start with getting the points for the arc. + const int arcPoints = static_cast(trunc( angleLen / granularity )); + QPolygonF poly( arcPoints+2 ); + qreal degree=0.0; + int iPoint = 0; + bool perfectMatch = false; + + while ( degree <= angleLen ){ + poly[ iPoint ] = pointOnCircle( drawPosition, startAngle + degree ); + //qDebug() << degree << angleLen << poly[ iPoint ]; + perfectMatch = (degree == angleLen); + degree += granularity; + ++iPoint; + } + // if necessary add one more point to fill the last small gap + if( ! perfectMatch ){ + poly[ iPoint ] = pointOnCircle( drawPosition, startAngle + angleLen ); + + // add the center point of the piece + poly.append( drawPosition.center() ); + }else{ + poly[ iPoint ] = drawPosition.center(); + } + //find the value and paint it + //fix value position + d->reverseMapper.addPolygon( index.row(), index.column(), poly ); + + painter->drawPolygon( poly ); + } + // the new code is setting the needed position points according to the slice: + // all is calculated as if the slice were 'standing' on it's tip and the border + // were on top, so North is the middle of the curved outside line and South is the tip + // + const qreal sum = valueTotals(); + const QPointF south = drawPosition.center(); + const QPointF southEast = south; + const QPointF southWest = south; + const QPointF north = pointOnCircle( drawPosition, startAngle + angleLen/2.0 ); + + const QPointF northEast = pointOnCircle( drawPosition, startAngle ); + const QPointF northWest = pointOnCircle( drawPosition, startAngle + angleLen ); + QPointF center = (south + north) / 2.0; + const QPointF east = (south + northEast) / 2.0; + const QPointF west = (south + northWest) / 2.0; + + CartesianDiagramDataCompressor::DataValueAttributesList allAttrs( d->aggregatedAttrs( this, index, 0 ) ); + const QFontMetrics * fm = (d->cachedFontMetrics( allAttrs.value(index).textAttributes().calculatedFont(d->plane,KDChartEnums::MeasureOrientationMinimum ), this )); + if(!list->isEmpty()) + { + QRect textRect = fm->boundingRect(QString::number(list->last().value)); + textRect.translated(center.toPoint()); + QPoint textRectCenter = textRect.center(); + qreal newX = center.x() - textRectCenter.x(); + qreal newY = center.y() - textRectCenter.y(); + center.setX(newX); + center.setY(newY); + } + + PositionPoints points( center, northWest, north, northEast, east, southEast, south, southWest, west); + qreal topAngle = startAngle - 90; + if( topAngle < 0.0 ) + topAngle += 360; + points.setDegrees(KDChartEnums::PositionEast, topAngle); + points.setDegrees(KDChartEnums::PositionNorthEast, topAngle); + points.setDegrees(KDChartEnums::PositionWest, topAngle + angleLen); + points.setDegrees(KDChartEnums::PositionNorthWest, topAngle + angleLen); + points.setDegrees(KDChartEnums::PositionCenter, topAngle + angleLen/2.0); + points.setDegrees(KDChartEnums::PositionNorth, topAngle + angleLen/2.0); + + //painter->drawText(points.mPositionCenter,QLatin1String("P")); + + d->appendDataValueTextInfoToList( + this, *list, index, 0, + points, Position::Center, Position::Center, + angleLen*sum / 360 ); + + // The following, old code (since kdc 2.0.0) was not correct: + // Settings made for the position had been totally ignored, + // AND the center was NOT the center - except for pieces of 45 degrees size + // + // QLineF centerLine( drawPosition.center(), + // QPointF( (poly[ last - 2].x() + poly.first().x())/2, + // ( poly.first().y() + poly[last-2].y() )/2 ) ); + // QPointF valuePos( ( centerLine.x1() + centerLine.x2() )/2, + // ( centerLine.y1() + centerLine.y2() )/2 ) ; + // + // paintDataValueText( painter, index, valuePos, angleLen*sum / 360 ); + } +} + + +/** + Internal method that draws the shadow creating the 3D effect of a pie + + \param painter the QPainter to draw in + \param drawPosition the position to draw at + \param dataset the dataset to draw the pie for + \param pie the pie to draw the shadow for + \param threeDHeight the height of the shadow + */ +void PieDiagram::draw3DEffect( QPainter* painter, + const QRectF& drawPosition, + uint dataset, uint pie, + qreal granularity, + const ThreeDPieAttributes& threeDAttrs, + bool /*explode*/ ) +{ + Q_UNUSED( dataset ); + + if( ! threeDAttrs.isEnabled() ) + return; + + // NOTE: We cannot optimize away drawing some of the effects (even + // when not exploding), because some of the pies might be left out + // in future versions which would make some of the normally hidden + // pies visible. Complex hidden-line algorithms would be much more + // expensive than just drawing for nothing. + + // No need to save the brush, will be changed on return from this + // method anyway. + if( threeDAttrs.useShadowColors() ){ + const QPen pen = this->pen( model()->index( 0, pie, rootIndex() ) ); + painter->setBrush( QBrush( pen.color() ) ); + } + //painter->setBrush( QBrush( threeDAttrs.dataShadow1Color( pie ), + // params()->shadowPattern() ) ); + + qreal startAngle = d->startAngles[ pie ]; + qreal endAngle = startAngle + d->angleLens[ pie ]; + // Normalize angles + while ( startAngle >= 360 ) + startAngle -= 360; + while ( endAngle >= 360 ) + endAngle -= 360; + Q_ASSERT( startAngle >= 0 && startAngle <= 360 ); + Q_ASSERT( endAngle >= 0 && endAngle <= 360 ); + + //int centerY = drawPosition.center().y(); + + if ( startAngle == endAngle || + startAngle == endAngle - 360 ) { // full circle + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, 360, granularity ); + } else if ( startAngle <= 90 ) { + if ( endAngle <= 90 ) { + if ( startAngle <= endAngle ) { + /// starts and ends in first quadrant, less than 1/4 + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), startAngle ); + drawUpperBrinkEffect( painter, drawPosition, endAngle ); + } else { + /// starts and ends in first quadrant, more than 3/4 + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), startAngle ); + drawUpperBrinkEffect( painter, drawPosition, endAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, 360, granularity ); + } + } else if ( endAngle <= 180 ) { + /// starts in first quadrant, ends in second quadrant, + /// less than 1/2 + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), startAngle ); + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), endAngle ); + } else if ( endAngle <= 270 ) { + /// starts in first quadrant, ends in third quadrant + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), startAngle ); + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), endAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, endAngle, granularity ); + } else { // 270*16 < endAngle < 360*16 + /// starts in first quadrant, ends in fourth quadrant, + /// more than 3/4 + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), startAngle ); + drawUpperBrinkEffect( painter, drawPosition, endAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, endAngle, granularity ); + } + } else if ( startAngle <= 180 ) { + if ( endAngle <= 90 ) { + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, 360, granularity ); + drawUpperBrinkEffect( painter, drawPosition, startAngle ); + drawUpperBrinkEffect( painter, drawPosition, endAngle ); + } else if ( endAngle <= 180 ) { + if ( startAngle <= endAngle ) { + /// starts in second quadrant, ends in second + /// quadrant, less than 1/4 + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), endAngle ); + drawUpperBrinkEffect( painter, drawPosition, startAngle ); + } else { + /// starts in second quadrant, ends in second + /// quadrant, more than 1/4 + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), endAngle ); + drawUpperBrinkEffect( painter, drawPosition, startAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, 360, granularity ); + } + } else if ( endAngle <= 270 ) { + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), endAngle ); + drawUpperBrinkEffect( painter, drawPosition, startAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, endAngle, granularity ); + } else { // 270*16 < endAngle < 360*16 + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, endAngle, granularity ); + drawUpperBrinkEffect( painter, drawPosition, startAngle ); + drawUpperBrinkEffect( painter, drawPosition, endAngle ); + } + } else if ( startAngle <= 270 ) { + if ( endAngle <= 90 ) { + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + startAngle, 360, granularity ); + drawUpperBrinkEffect( painter, drawPosition, startAngle ); + drawUpperBrinkEffect( painter, drawPosition, endAngle ); + } else if ( endAngle <= 180 ) { + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), endAngle ); + drawUpperBrinkEffect( painter, drawPosition, startAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + startAngle, 360, granularity ); + } else if ( endAngle <= 270 ) { + if ( startAngle <= endAngle ) { + /// starts in third quadrant, ends in third quadrant, + /// less than 1/4 + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), endAngle ); + drawUpperBrinkEffect( painter, drawPosition, startAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + startAngle, endAngle, granularity ); + } else { + /// starts in third quadrant, ends in third quadrant, + /// more than 3/4 + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), endAngle ); + drawUpperBrinkEffect( painter, drawPosition, startAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, endAngle, granularity ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + startAngle, 360, granularity ); + } + } else { // 270*16 < endAngle < 360*16 + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + startAngle, endAngle, granularity ); + drawUpperBrinkEffect( painter, drawPosition, startAngle ); + drawUpperBrinkEffect( painter, drawPosition, endAngle ); + } + } else { // 270*16 < startAngle < 360*16 + if ( endAngle <= 90 ) { + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), startAngle ); + drawUpperBrinkEffect( painter, drawPosition, endAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + startAngle, 360, granularity ); + } else if ( endAngle <= 180 ) { + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), startAngle ); + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), endAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + startAngle, 360, granularity ); + } else if ( endAngle <= 270 ) { + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), startAngle ); + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), endAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, endAngle, granularity ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + startAngle, 360, granularity ); + } else { // 270*16 < endAngle < 360*16 + if ( startAngle <= endAngle ) { + /// starts in fourth quadrant, ends in fourth + /// quadrant, less than 1/4 + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), startAngle ); + drawUpperBrinkEffect( painter, drawPosition, endAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + startAngle, endAngle, granularity ); + } else { + /// starts in fourth quadrant, ends in fourth + /// quadrant, more than 3/4 + drawStraightEffectSegment( painter, drawPosition, + threeDAttrs.depth(), startAngle ); + drawUpperBrinkEffect( painter, drawPosition, endAngle ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + startAngle, 360, granularity ); + drawArcEffectSegment( painter, drawPosition, + threeDAttrs.depth(), + 180, endAngle, granularity ); + } + } + } + drawArcUpperBrinkEffectSegment( painter, drawPosition, startAngle, endAngle, granularity ); +} + + +/** + Internal method that draws a segment with a straight 3D effect + + \param painter the QPainter to draw in + \param rect the position to draw at + \param threeDHeight the height of the shadow + \param angle the angle of the segment + */ +void PieDiagram::drawStraightEffectSegment( QPainter* painter, + const QRectF& rect, + qreal threeDHeight, + qreal angle ) +{ + QPolygonF poly( 4 ); + const QPointF center = rect.center(); + const QPointF circlePoint = pointOnCircle( rect, angle ); + poly[0] = center; + poly[1] = circlePoint; + poly[2] = QPointF( circlePoint.x(), circlePoint.y() + threeDHeight ); + poly[3] = QPointF( center.x(), center.y() + threeDHeight ); + // TODO: add polygon to ReverseMapper + painter->drawPolygon( poly ); +// if ( region ) +// *region += QRegion( points ); +} + +/** + Internal method that draws the upper brink of a 3D pie piece + + \param painter the QPainter to draw in + \param rect the position to draw at + \param angle the angle of the segment + */ +void PieDiagram::drawUpperBrinkEffect( QPainter* painter, + const QRectF& rect, + qreal angle ) +{ + const QPointF center = rect.center(); + const QPointF circlePoint = pointOnCircle( rect, angle ); + painter->drawLine( center, circlePoint ); +} + +/** + Internal method that draws a segment with an arc 3D effect + + \param painter the QPainter to draw in + \param rect the position to draw at + \param threeDHeight the height of the shadow + \param startAngle the starting angle of the segment + \param endAngle the ending angle of the segment + */ +void PieDiagram::drawArcEffectSegment( QPainter* painter, + const QRectF& rect, + qreal threeDHeight, + qreal startAngle, + qreal endAngle, + qreal granularity ) +{ + // Start with getting the points for the inner arc. + qreal startA = qMin( startAngle, endAngle ); + qreal endA = qMax( startAngle, endAngle ); + + // sometimes we have to draw two segments, which are on different sides of the pie + if( endA > 540 ) + drawArcEffectSegment( painter, rect, threeDHeight, 180, endA - 360, granularity ); + if( endA > 360 ) + endA = qMin( endA, qreal( 360.0 ) ); + + int numHalfPoints = static_cast( trunc( ( endA - startA ) / granularity ) ) + 1; + + QPolygonF poly( numHalfPoints ); + + qreal degree = endA; + int iPoint = 0; + bool perfectMatch = false; + while ( degree >= startA ){ + poly[ numHalfPoints - iPoint - 1 ] = pointOnCircle( rect, degree ); + + perfectMatch = (degree == startA); + degree -= granularity; + ++iPoint; + } + // if necessary add one more point to fill the last small gap + if( ! perfectMatch ){ + poly.prepend( pointOnCircle( rect, startA ) ); + ++numHalfPoints; + } + + poly.resize( numHalfPoints * 2 ); + + // Now copy these arcs again into the final array, but in the + // opposite direction and moved down by the 3D height. + for ( int i = numHalfPoints - 1; i >= 0; --i ) { + QPointF pointOnFirstArc( poly[ i ] ); + pointOnFirstArc.setY( pointOnFirstArc.y() + threeDHeight ); + poly[ numHalfPoints * 2 - i - 1 ] = pointOnFirstArc; + } + + // TODO: Add polygon to ReverseMapper + painter->drawPolygon( poly ); +// if ( region ) +// *region += QRegion( collect ); +} + +/** + Internal method that draws the upper brink of a 3D pie segment + + \param painter the QPainter to draw in + \param rect the position to draw at + \param startAngle the starting angle of the segment + \param endAngle the ending angle of the segment + */ +void PieDiagram::drawArcUpperBrinkEffectSegment( QPainter* painter, + const QRectF& rect, + qreal startAngle, + qreal endAngle, + qreal granularity ) +{ + if ( endAngle < startAngle ) + endAngle += 360; + // Start with getting the poits for the inner arc. + const qreal startA = qMin( startAngle, endAngle ); + const qreal endA = qMax( startAngle, endAngle ); + + int numHalfPoints = static_cast( trunc( ( endA - startA ) / granularity ) ) + 1; + + QPolygonF poly( numHalfPoints ); + + qreal degree = endA; + int iPoint = 0; + bool perfectMatch = false; + while ( degree >= startA ){ + poly[ numHalfPoints - iPoint - 1 ] = pointOnCircle( rect, degree ); + + perfectMatch = (degree == startA); + degree -= granularity; + ++iPoint; + } + // if necessary add one more point to fill the last small gap + if( ! perfectMatch ){ + poly.prepend( pointOnCircle( rect, startA ) ); + ++numHalfPoints; + } + + painter->drawPolyline( poly ); +// if ( region ) +// *region += QRegion( collect ); +} + +/** + Internal method that finds the pie that is located at the position + specified by \c angle. + + \param angle the angle at which to search for a pie + \return the number of the pie found + */ +uint PieDiagram::findPieAt( qreal angle, int colCount ) +{ + for ( int i = 0; i < colCount; ++i ) { + qreal endseg = d->startAngles[ i ] + d->angleLens[ i ]; + if ( ( d->startAngles[ i ] <= angle ) && + ( endseg >= angle ) ) + // found! + return i; + } + + // If we have not found it, try wrap around + // but only if the current searched angle is < 360 degree + if ( angle < 360 ) + return findPieAt( angle + 360, colCount ); + // otherwise - what ever went wrong - we return 0 + return 0; +} + + +/** + Internal method that finds the pie that is located to the left of + the pie specified by \c pie. + + \param pie the pie to start the search from + \return the number of the pie to the left of \c pie + */ +uint PieDiagram::findLeftPie( uint pie, int colCount ) +{ + if ( pie == 0 ) + if ( colCount > 1 ) + return colCount - 1; + else + return 0; + else { + return pie - 1; + } +} + + +/** + Internal method that finds the pie that is located to the right of + the pie specified by \c pie. + + \param pie the pie to start the search from + \return the number of the pie to the right of \c pie + */ +uint PieDiagram::findRightPie( uint pie, int colCount ) +{ + int rightpie = pie + 1; + if ( rightpie == colCount ) + rightpie = 0; + return rightpie; +} + +/* +/ ** + This method is a specialization that returns a fallback legend text + appropriate for pies that do not have more than one dataset + + This method is only used when automatic legends are used, because + manual and first-column legends do not need fallback texts. + + \param uint dataset the dataset number for which to generate a + fallback text + \return the fallback text to use for describing the specified + dataset in the legend + * / +QString PieDiagram::fallbackLegendText( uint dataset ) const +{ + return QObject::tr( "Item " ) + QString::number( dataset + 1 ); +} + + +/ ** + This methods returns the number of elements to be shown in the + legend in case fallback texts are used. + + This method is only used when automatic legends are used, because + manual and first-column legends do not need fallback texts. + + \return the number of fallback texts to use + * / +uint PieDiagram::numLegendFallbackTexts( KDChartTableDataBase* data ) const +{ + return data->usedCols(); +} +*/ + +/** + * Auxiliary method returning a point to a given boundary + * rectangle of the enclosed ellipse and an angle. + */ +QPointF PieDiagram::pointOnCircle( const QRectF& rect, qreal angle ) +{ + qreal angleRad = DEGTORAD( angle ); + qreal cosAngle = cos( angleRad ); + qreal sinAngle = -sin( angleRad ); + qreal posX = cosAngle * rect.width() / 2.0; + qreal posY = sinAngle * rect.height() / 2.0; + return QPointF( posX + rect.center().x(), + posY + rect.center().y() ); + +} + +/*virtual*/ +double PieDiagram::valueTotals() const +{ + const int colCount = columnCount(); + double total = 0.0; + for ( int j = 0; j < colCount; ++j ) { + total += qAbs(model()->data( model()->index( 0, j, rootIndex() ) ).toDouble()); + //qDebug() << model()->data( model()->index( 0, j, rootIndex() ) ).toDouble(); + } + return total; +} + +/*virtual*/ +double PieDiagram::numberOfValuesPerDataset() const +{ + return model() ? model()->columnCount( rootIndex() ) : 0.0; +} + +/*virtual*/ +double PieDiagram::numberOfGridRings() const +{ + return 1; +} diff --git a/libkdchart/src/KDChartPieDiagram.h b/libkdchart/src/KDChartPieDiagram.h new file mode 100644 index 0000000..fa786f7 --- /dev/null +++ b/libkdchart/src/KDChartPieDiagram.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPIEDIAGRAM_H +#define KDCHARTPIEDIAGRAM_H + +#include "KDChartAbstractPieDiagram.h" + +namespace KDChart { + + class DataValueTextInfo; + typedef class QVector DataValueTextInfoList; + +/** + * @brief PieDiagram defines a common pie diagram + */ +class KDCHART_EXPORT PieDiagram : public AbstractPieDiagram +{ + Q_OBJECT + + Q_DISABLE_COPY( PieDiagram ) + KDCHART_DECLARE_DERIVED_DIAGRAM( PieDiagram, PolarCoordinatePlane ) + +public: + explicit PieDiagram( + QWidget* parent = 0, PolarCoordinatePlane* plane = 0 ); + virtual ~PieDiagram(); + +protected: + // Implement AbstractDiagram + /** \reimpl */ + virtual void paint ( PaintContext* paintContext ); + void paintInternal(PaintContext* paintContext, QRectF& textBoundingRect); + +public: + /** \reimpl */ + virtual void resize ( const QSizeF& area ); + + // Implement AbstractPolarDiagram + /** \reimpl */ + virtual double valueTotals () const; + /** \reimpl */ + virtual double numberOfValuesPerDataset() const; + /** \reimpl */ + virtual double numberOfGridRings() const; + + virtual PieDiagram * clone() const; + +protected: + /** \reimpl */ + virtual const QPair calculateDataBoundaries() const; + void paintEvent( QPaintEvent* ); + void resizeEvent( QResizeEvent* ); + +private: + QRectF piePosition( uint dataset, uint pie ) const; + void drawOnePie( QPainter* painter, + DataValueTextInfoList* list, + uint dataset, uint pie, + qreal granularity, + qreal threeDPieHeight ); + void drawPieSurface( QPainter* painter, + DataValueTextInfoList* list, + uint dataset, uint pie, + qreal granularity ); + void draw3DEffect( QPainter* painter, + const QRectF& drawPosition, + uint dataset, uint pie, + qreal granularity, + const ThreeDPieAttributes& threeDAttrs, + bool /*explode*/ ); + void drawStraightEffectSegment( QPainter* painter, + const QRectF& rect, + qreal threeDHeight, + qreal angle ); + void drawUpperBrinkEffect( QPainter* painter, + const QRectF& rect, + qreal angle ); + void drawArcEffectSegment( QPainter* painter, + const QRectF& rect, + qreal threeDHeight, + qreal startAngle, + qreal endAngle, + qreal granularity ); + void drawArcUpperBrinkEffectSegment( QPainter* painter, + const QRectF& rect, + qreal startAngle, + qreal endAngle, + qreal granularity ); + uint findPieAt( qreal angle, int columnCount ); + uint findLeftPie( uint pie, int columnCount ); + uint findRightPie( uint pie, int columnCount ); + QPointF pointOnCircle( const QRectF& rect, qreal angle ); +}; // End of class KDChartPieDiagram + +} +#endif // KDCHARTPIEDIAGRAM_H diff --git a/libkdchart/src/KDChartPieDiagram_p.h b/libkdchart/src/KDChartPieDiagram_p.h new file mode 100644 index 0000000..be3bf9d --- /dev/null +++ b/libkdchart/src/KDChartPieDiagram_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPIEDIAGRAM_P_H +#define KDCHARTPIEDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractPieDiagram_p.h" + +#include + + +namespace KDChart { + +/** + * \internal + */ +class PieDiagram::Private : public AbstractPieDiagram::Private +{ + friend class PieDiagram; +public: + Private(); + ~Private(); + + Private( const Private& rhs ) : + AbstractPieDiagram::Private( rhs ) + { + // just for consistency + } + +protected: + // this information needed temporarily at drawing time + QVector < qreal > startAngles; + QVector < qreal > angleLens; + QRectF position; + qreal size; +}; + +KDCHART_IMPL_DERIVED_DIAGRAM( PieDiagram, AbstractPieDiagram, PolarCoordinatePlane ) + +} + +#endif /* KDCHARTPIEDIAGRAM_P_H */ diff --git a/libkdchart/src/KDChartPlotter.cpp b/libkdchart/src/KDChartPlotter.cpp new file mode 100644 index 0000000..d8b4aa8 --- /dev/null +++ b/libkdchart/src/KDChartPlotter.cpp @@ -0,0 +1,392 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPlotter.h" +#include "KDChartPlotter_p.h" + +#include "KDChartAbstractGrid.h" + +#include + +#include "KDChartNormalPlotter_p.h" +#include "KDChartPercentPlotter_p.h" + +using namespace KDChart; + +Plotter::Private::Private() +{ +} + +Plotter::Private::~Private() {} + + +#define d d_func() + + +Plotter::Plotter( QWidget* parent, CartesianCoordinatePlane* plane ) : + AbstractCartesianDiagram( new Private(), parent, plane ) +{ + init(); +} + +void Plotter::init() +{ + d->diagram = this; + d->normalPlotter = new NormalPlotter( this ); + d->percentPlotter = new PercentPlotter( this ); + d->implementor = d->normalPlotter; + + setDatasetDimensionInternal( 2 ); +} + +Plotter::~Plotter() +{ +} + +/** + * Creates an exact copy of this diagram. + */ +Plotter* Plotter::clone() const +{ + Plotter* newDiagram = new Plotter( new Private( *d ) ); + newDiagram->setType( type() ); + return newDiagram; +} + +bool Plotter::compare( const Plotter* other )const +{ + if( other == this ) + return true; + if( other == 0 ) + return false; + return // compare the base class + ( static_cast< const AbstractCartesianDiagram* >( this )->compare( other ) ) && + // compare own properties + ( type() == other->type() ); +} + +/** + * Sets the plotter's type to \a type + */ +void Plotter::setType( const PlotType type ) +{ + if( d->implementor->type() == type ) + return; + if( datasetDimension() != 2 ) + { + Q_ASSERT_X ( false, "setType()", + "This line chart type can only be used with two-dimensional data." ); + return; + } + switch( type ) { + case Normal: + d->implementor = d->normalPlotter; + break; + case Percent: + d->implementor = d->percentPlotter; + break; + default: + Q_ASSERT_X( false, "Plotter::setType", "unknown plotter subtype" ); + }; + + // d->lineType = type; + Q_ASSERT( d->implementor->type() == type ); + + setDataBoundariesDirty(); + emit layoutChanged( this ); + emit propertiesChanged(); +} + +/** + * @return the type of the plotter + */ +Plotter::PlotType Plotter::type() const +{ + return d->implementor->type(); +} + +/** + * Sets the global line attributes to \a la + */ +void Plotter::setLineAttributes( const LineAttributes& la ) +{ + d->attributesModel->setModelData( + qVariantFromValue( la ), + LineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Sets the line attributes of data set \a column to \a la + */ +void Plotter::setLineAttributes( + int column, + const LineAttributes& la ) +{ + d->setDatasetAttrs( column, qVariantFromValue( la ), + LineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Resets the line attributes of data set \a column + */ +void Plotter::resetLineAttributes( int column ) +{ + d->resetDatasetAttrs( column, LineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Sets the line attributes for the model index \a index to \a la + */ +void Plotter::setLineAttributes( + const QModelIndex & index, + const LineAttributes& la ) +{ + d->attributesModel->setData( + d->attributesModel->mapFromSource(index), + qVariantFromValue( la ), + LineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Remove any explicit line attributes settings that might have been specified before. + */ +void Plotter::resetLineAttributes( const QModelIndex & index ) +{ + d->attributesModel->resetData( + d->attributesModel->mapFromSource(index), LineAttributesRole ); + emit propertiesChanged(); +} + +/** + * @return the global line attribute set + */ +LineAttributes Plotter::lineAttributes() const +{ + return qVariantValue( + d->attributesModel->data( KDChart::LineAttributesRole ) ); +} + +/** + * @return the line attribute set of data set \a column + */ +LineAttributes Plotter::lineAttributes( int column ) const +{ + const QVariant attrs( d->datasetAttrs( column, LineAttributesRole ) ); + if( attrs.isValid() ) + return qVariantValue< LineAttributes >( attrs ); + return lineAttributes(); +} + +/** + * @return the line attribute set of the model index \a index + */ +LineAttributes Plotter::lineAttributes( + const QModelIndex& index ) const +{ + return qVariantValue( + d->attributesModel->data( + d->attributesModel->mapFromSource(index), + KDChart::LineAttributesRole ) ); +} + +/** + * Sets the global 3D line attributes to \a la + */ +void Plotter::setThreeDLineAttributes( + const ThreeDLineAttributes& la ) +{ + setDataBoundariesDirty(); + d->attributesModel->setModelData( + qVariantFromValue( la ), + ThreeDLineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Sets the 3D line attributes of data set \a column to \a la + */ +void Plotter::setThreeDLineAttributes( + int column, + const ThreeDLineAttributes& la ) +{ + setDataBoundariesDirty(); + d->setDatasetAttrs( column, qVariantFromValue( la ), ThreeDLineAttributesRole ); + emit propertiesChanged(); +} + +/** + * Sets the 3D line attributes of model index \a index to \a la + */ +void Plotter::setThreeDLineAttributes( + const QModelIndex& index, + const ThreeDLineAttributes& la ) +{ + setDataBoundariesDirty(); + d->attributesModel->setData( + d->attributesModel->mapFromSource(index), + qVariantFromValue( la ), + ThreeDLineAttributesRole ); + emit propertiesChanged(); +} + +/** + * @return the global 3D line attributes + */ +ThreeDLineAttributes Plotter::threeDLineAttributes() const +{ + return qVariantValue( + d->attributesModel->data( KDChart::ThreeDLineAttributesRole ) ); +} + +/** + * @return the 3D line attributes of data set \a column + */ +ThreeDLineAttributes Plotter::threeDLineAttributes( int column ) const +{ + const QVariant attrs( d->datasetAttrs( column, ThreeDLineAttributesRole ) ); + if( attrs.isValid() ) + return qVariantValue< ThreeDLineAttributes >( attrs ); + return threeDLineAttributes(); +} + +/** + * @return the 3D line attributes of the model index \a index + */ +ThreeDLineAttributes Plotter::threeDLineAttributes( + const QModelIndex& index ) const +{ + return qVariantValue( + d->attributesModel->data( + d->attributesModel->mapFromSource( index ), + KDChart::ThreeDLineAttributesRole ) ); +} + +double Plotter::threeDItemDepth( const QModelIndex & index ) const +{ + return threeDLineAttributes( index ).validDepth(); +} + +double Plotter::threeDItemDepth( int column ) const +{ + return qVariantValue( + d->datasetAttrs( column, KDChart::ThreeDLineAttributesRole ) ).validDepth(); +} + +/** + * Sets the value tracker attributes of the model index \a index to \a va + */ +void Plotter::setValueTrackerAttributes( const QModelIndex & index, + const ValueTrackerAttributes & va ) +{ + d->attributesModel->setData( d->attributesModel->mapFromSource(index), + qVariantFromValue( va ), + KDChart::ValueTrackerAttributesRole ); + emit propertiesChanged(); +} + +/** + * Returns the value tracker attributes of the model index \a index + */ +ValueTrackerAttributes Plotter::valueTrackerAttributes( + const QModelIndex & index ) const +{ + return qVariantValue( d->attributesModel->data( + d->attributesModel->mapFromSource( index ), + KDChart::ValueTrackerAttributesRole ) ); +} + +void Plotter::resizeEvent ( QResizeEvent* ) +{ +} + +const QPair< QPointF, QPointF > Plotter::calculateDataBoundaries() const +{ + if ( !checkInvariants( true ) ) + return QPair< QPointF, QPointF >( QPointF( 0, 0 ), QPointF( 0, 0 ) ); + + // note: calculateDataBoundaries() is ignoring the hidden flags. + // That's not a bug but a feature: Hiding data does not mean removing them. + // For totally removing data from KD Chart's view people can use e.g. a proxy model ... + + // calculate boundaries for different line types Normal - Stacked - Percent - Default Normal + return d->implementor->calculateDataBoundaries(); +} + + +void Plotter::paintEvent ( QPaintEvent*) +{ + QPainter painter ( viewport() ); + PaintContext ctx; + ctx.setPainter ( &painter ); + ctx.setRectangle ( QRectF ( 0, 0, width(), height() ) ); + paint ( &ctx ); +} + +void Plotter::paint( PaintContext* ctx ) +{ + // note: Not having any data model assigned is no bug + // but we can not draw a diagram then either. + if ( !checkInvariants( true ) ) return; + + AbstractCoordinatePlane* const plane = ctx->coordinatePlane(); + if( ! plane ) return; + d->setCompressorResolution( size(), plane ); + + if ( !AbstractGrid::isBoundariesValid(dataBoundaries()) ) return; + + const PainterSaver p( ctx->painter() ); + if( model()->rowCount( rootIndex() ) == 0 || model()->columnCount( rootIndex() ) == 0 ) + return; // nothing to paint for us + + ctx->setCoordinatePlane( plane->sharedAxisMasterPlane( ctx->painter() ) ); + + // paint different line types Normal - Stacked - Percent - Default Normal + d->implementor->paint( ctx ); + + ctx->setCoordinatePlane( plane ); +} + +void Plotter::resize ( const QSizeF& size ) +{ + d->setCompressorResolution( size, coordinatePlane() ); + setDataBoundariesDirty(); +} + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +int Plotter::numberOfAbscissaSegments () const +{ + return d->attributesModel->rowCount( attributesModelRootIndex() ); +} + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +int Plotter::numberOfOrdinateSegments () const +{ + return d->attributesModel->columnCount( attributesModelRootIndex() ); +} diff --git a/libkdchart/src/KDChartPlotter.h b/libkdchart/src/KDChartPlotter.h new file mode 100644 index 0000000..8311d2b --- /dev/null +++ b/libkdchart/src/KDChartPlotter.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPLOTTER_H +#define KDCHARTPLOTTER_H + +#include "KDChartAbstractCartesianDiagram.h" + +#include "KDChartLineAttributes.h" +#include "KDChartValueTrackerAttributes.h" + +namespace KDChart { + + class ThreeDLineAttributes; + +/** + * @brief Plotter defines a diagram type plotting two-dimensional data. + */ +class KDCHART_EXPORT Plotter : public AbstractCartesianDiagram +{ + Q_OBJECT + + Q_DISABLE_COPY( Plotter ) + + KDCHART_DECLARE_DERIVED_DIAGRAM( Plotter, CartesianCoordinatePlane ) + + +public: + class PlotterType; + friend class PlotterType; + + explicit Plotter( QWidget* parent = 0, CartesianCoordinatePlane* plane = 0 ); + virtual ~Plotter(); + + virtual Plotter* clone() const; + + /** + * Returns true if both diagrams have the same settings. + */ + bool compare( const Plotter* other ) const; + + enum PlotType { + Normal = 0, + Percent + }; + + + void setType( const PlotType type ); + PlotType type() const; + + void setLineAttributes( const LineAttributes & a ); + void setLineAttributes( int column, const LineAttributes & a ); + void setLineAttributes( const QModelIndex & index, const LineAttributes & a ); + void resetLineAttributes( int column ); + void resetLineAttributes( const QModelIndex & index ); + LineAttributes lineAttributes() const; + LineAttributes lineAttributes( int column ) const; + LineAttributes lineAttributes( const QModelIndex & index ) const; + + void setThreeDLineAttributes( const ThreeDLineAttributes & a ); + void setThreeDLineAttributes( int column, const ThreeDLineAttributes & a ); + void setThreeDLineAttributes( const QModelIndex & index, + const ThreeDLineAttributes & a ); + + //FIXME(khz): big TODO(khz): add a lot of reset...Attributes() methods to all + // appropriate places, for 2.1 (that is: after we have release 2.0.2) :-) + + ThreeDLineAttributes threeDLineAttributes() const; + ThreeDLineAttributes threeDLineAttributes( int column ) const; + ThreeDLineAttributes threeDLineAttributes( const QModelIndex & index ) const; + + void setValueTrackerAttributes( const QModelIndex & index, + const ValueTrackerAttributes & a ); + ValueTrackerAttributes valueTrackerAttributes( const QModelIndex & index ) const; + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + // implement AbstractCartesianDiagram + /* reimpl */ + const int numberOfAbscissaSegments () const; + /* reimpl */ + const int numberOfOrdinateSegments () const; +#else + // implement AbstractCartesianDiagram + /* reimpl */ + int numberOfAbscissaSegments () const; + /* reimpl */ + int numberOfOrdinateSegments () const; +#endif + +protected: + void paint ( PaintContext* paintContext ); + +public: + void resize ( const QSizeF& area ); + +protected: + virtual double threeDItemDepth( const QModelIndex & index ) const; + virtual double threeDItemDepth( int column ) const; + /** \reimpl */ + virtual const QPair calculateDataBoundaries() const; + void paintEvent ( QPaintEvent* ); + void resizeEvent ( QResizeEvent* ); +}; // End of class KDChart::Plotter + +} + +#endif // KDCHARTLINEDIAGRAM_H diff --git a/libkdchart/src/KDChartPlotter_p.cpp b/libkdchart/src/KDChartPlotter_p.cpp new file mode 100644 index 0000000..1166186 --- /dev/null +++ b/libkdchart/src/KDChartPlotter_p.cpp @@ -0,0 +1,320 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPlotter_p.h" +#include "KDChartPlotter.h" + +#include "KDChartValueTrackerAttributes.h" + +using namespace KDChart; + +Plotter::Private::Private( const Private& rhs ) + : AbstractCartesianDiagram::Private( rhs ) +{ +} + +void Plotter::Private::setCompressorResolution( + const QSizeF& size, + const AbstractCoordinatePlane* plane ) +{ + compressor.setResolution( static_cast( size.width() * plane->zoomFactorX() ), + static_cast( size.height() * plane->zoomFactorY() ) ); +} + + +void Plotter::Private::paintPolyline( + PaintContext* ctx, + const QBrush& brush, const QPen& pen, + const QPolygonF& points ) const +{ + ctx->painter()->setBrush( brush ); + ctx->painter()->setPen( PrintingParameters::scalePen( + QPen( pen.color(), + pen.width(), + pen.style(), + Qt::FlatCap, + Qt::MiterJoin ) ) ); +#if QT_VERSION > 0x040299 + ctx->painter()->drawPolyline( points ); +#else + // FIXME (Mirko) verify, this sounds reverse-logical + // For Qt versions older than 4.3 drawPolyline is VERY slow + // so we use traditional line segments drawing instead then. + for (int i = 0; i < points.size()-1; ++i) + ctx->painter()->drawLine( points.at(i), points.at(i+1) ); +#endif +} + +/*! + Projects a point in a space defined by its x, y, and z coordinates + into a point onto a plane, given two rotation angles around the x + resp. y axis. +*/ +const QPointF Plotter::PlotterType::project( + QPointF point, QPointF maxLimits, + double z, const QModelIndex& index ) const +{ + Q_UNUSED( maxLimits ); + ThreeDLineAttributes td = diagram()->threeDLineAttributes( index ); + + //Pending Michel FIXME - the rotation does not work as expected atm + double xrad = DEGTORAD( td.lineXRotation() ); + double yrad = DEGTORAD( td.lineYRotation() ); + QPointF ret = QPointF(point.x()*cos( yrad ) + z * sin( yrad ) , point.y()*cos( xrad ) - z * sin( xrad ) ); + return ret; +} + +void Plotter::PlotterType::paintThreeDLines( + PaintContext* ctx, const QModelIndex& index, + const QPointF& from, const QPointF& to, const double depth ) +{ + // retrieve the boundaries + const QPair< QPointF, QPointF > boundaries = diagram()->dataBoundaries(); + const QPointF& maxLimits = boundaries.second; + const QPointF topLeft = project( from, maxLimits, depth, index ); + const QPointF topRight = project ( to, maxLimits, depth, index ); + + const QPolygonF segment = QPolygonF() << from << topLeft << topRight << to; + const QBrush indexBrush ( diagram()->brush( index ) ); + const PainterSaver painterSaver( ctx->painter() ); + + if( diagram()->antiAliasing() ) + ctx->painter()->setRenderHint( QPainter::Antialiasing ); + + ctx->painter()->setBrush( indexBrush ); + ctx->painter()->setPen( PrintingParameters::scalePen( diagram()->pen( index ) ) ); + + reverseMapper().addPolygon( index.row(), index.column(), segment ); + ctx->painter()->drawPolygon( segment ); +} + +// this method is factored out from LineDiagram::paint, and contains +// the common parts of the method that previously implemented all +// chart types in one +void Plotter::PlotterType::paintElements( + PaintContext* ctx, + DataValueTextInfoList& list, + LineAttributesInfoList& lineList, + LineAttributes::MissingValuesPolicy policy ) +{ + Q_UNUSED( policy ); + // paint all lines and their attributes + PainterSaver painterSaver( ctx->painter() ); + if ( diagram()->antiAliasing() ) + ctx->painter()->setRenderHint ( QPainter::Antialiasing ); + LineAttributesInfoListIterator itline ( lineList ); + + QBrush curBrush; + QPen curPen; + QPolygonF points; + while ( itline.hasNext() ) { + const LineAttributesInfo& lineInfo = itline.next(); + const QModelIndex& index = lineInfo.index; + const ThreeDLineAttributes td = diagram()->threeDLineAttributes( index ); + const ValueTrackerAttributes vt = diagram()->valueTrackerAttributes( index ); + + if( td.isEnabled() ){ + paintThreeDLines( ctx, index, lineInfo.value, lineInfo.nextValue, td.depth() ); + } else { + const QBrush br( diagram()->brush( index ) ); + const QPen pn( diagram()->pen( index ) ); + if( points.count() && points.last() == lineInfo.value && curBrush == br && curPen == pn ) { + // line goes from last value in points to lineInfo.nextValue + reverseMapper().addLine( lineInfo.index.row(), lineInfo.index.column(), points.last(), lineInfo.nextValue ); + points << lineInfo.nextValue; + } else { + if( points.count() ) + paintPolyline( ctx, curBrush, curPen, points ); + curBrush = br; + curPen = pn; + points.clear(); + // line goes from lineInfo.value to lineInfo,nextValue + reverseMapper().addLine( lineInfo.index.row(), lineInfo.index.column(), lineInfo.value, lineInfo.nextValue ); + points << lineInfo.value << lineInfo.nextValue; + } + } + + if( vt.isEnabled() ) + paintValueTracker( ctx, vt, lineInfo.value ); + } + if( points.count() ) + paintPolyline( ctx, curBrush, curPen, points ); + // paint all data value texts and the point markers + paintDataValueTextsAndMarkers( diagram(), ctx, list, true ); +} + +AttributesModel* Plotter::PlotterType::attributesModel() const +{ + return m_private->attributesModel; +} + +#if 0 +QModelIndex LineDiagram::LineDiagramType::attributesModelRootIndex() const +{ + return m_private->diagram->attributesModelRootIndex(); +} + +int LineDiagram::LineDiagramType::datasetDimension() const +{ + return m_private->datasetDimension; +} +#endif + +ReverseMapper& Plotter::PlotterType::reverseMapper() +{ + return m_private->reverseMapper; +} + +#if 0 +LineAttributes::MissingValuesPolicy LineDiagram::LineDiagramType::getCellValues( + int row, int column, + bool shiftCountedXValuesByHalfSection, + double& valueX, double& valueY ) const +{ + return m_private->diagram->getCellValues( row, column, shiftCountedXValuesByHalfSection, + valueX, valueY ); +} + +double LineDiagram::LineDiagramType::valueForCellTesting( + int row, int column, + bool& bOK, + bool showHiddenCellsAsInvalid) const +{ + return m_private->diagram->valueForCellTesting( row, column, bOK, showHiddenCellsAsInvalid ); +} +#endif + +Plotter* Plotter::PlotterType::diagram() const +{ + return m_private->diagram; +} + +void Plotter::PlotterType::paintAreas( + PaintContext* ctx, + const QModelIndex& index, const QList< QPolygonF >& areas, + const uint transparency ) +{ + QColor trans = diagram()->brush( index ).color(); + trans.setAlpha( transparency ); + QPen indexPen = diagram()->pen(index); + indexPen.setColor( trans ); + const PainterSaver painterSaver( ctx->painter() ); + + if( diagram()->antiAliasing() ) + ctx->painter()->setRenderHint( QPainter::Antialiasing ); + + ctx->painter()->setPen( PrintingParameters::scalePen( indexPen ) ); + ctx->painter()->setBrush( trans ); + + QPainterPath path; + for( int i = 0; i < areas.count(); ++i ) + { + const QPolygonF& p = areas[ i ]; + path.addPolygon( p ); + reverseMapper().addPolygon( index.row(), index.column(), p ); + path.closeSubpath(); + } + ctx->painter()->drawPath( path ); +} + +#if 0 +double LineDiagram::LineDiagramType::valueForCell( int row, int column ) +{ + return diagram()->valueForCell( row, column ); +} +#endif + +void Plotter::PlotterType::appendDataValueTextInfoToList( + AbstractDiagram * diagram, + DataValueTextInfoList & list, + const QModelIndex & index, + const PositionPoints& points, + const Position& autoPositionPositive, + const Position& autoPositionNegative, + const qreal value ) +{ + Q_UNUSED( autoPositionNegative ); + m_private->appendDataValueTextInfoToList( + diagram, list, index, 0, + points, autoPositionPositive, autoPositionPositive, value ); +} + +void Plotter::PlotterType::paintValueTracker( PaintContext* ctx, const ValueTrackerAttributes& vt, const QPointF& at ) +{ + CartesianCoordinatePlane* plane = qobject_cast( ctx->coordinatePlane() ); + if( !plane ) + return; + + DataDimensionsList gridDimensions = ctx->coordinatePlane()->gridDimensionsList(); + const QPointF bottomLeft( ctx->coordinatePlane()->translate( + QPointF( plane->isHorizontalRangeReversed() ? + gridDimensions.at( 0 ).end : + gridDimensions.at( 0 ).start, + plane->isVerticalRangeReversed() ? + gridDimensions.at( 1 ).end : + gridDimensions.at( 1 ).start ) ) ); + const QPointF markerPoint = at; + const QPointF ordinatePoint( bottomLeft.x(), at.y() ); + const QPointF abscissaPoint( at.x(), bottomLeft.y() ); + + const QSizeF markerSize = vt.markerSize(); + const QRectF ellipseMarker = QRectF( at.x() - markerSize.width() / 2, + at.y() - markerSize.height() / 2, + markerSize.width(), markerSize.height() ); + + const QPointF ordinateMarker[3] = { + QPointF( ordinatePoint.x(), at.y() + markerSize.height() / 2 ), + QPointF( ordinatePoint.x() + markerSize.width() / 2, at.y() ), + QPointF( ordinatePoint.x(), at.y() - markerSize.height() / 2 ) + }; + + const QPointF abscissaMarker[3] = { + QPointF( at.x() + markerSize.width() / 2, abscissaPoint.y() ), + QPointF( at.x(), abscissaPoint.y() - markerSize.height() / 2 ), + QPointF( at.x() - markerSize.width() / 2, abscissaPoint.y() ) + }; + + QPointF topLeft = ordinatePoint; + QPointF bottomRightOffset = abscissaPoint - topLeft; + QSizeF size( bottomRightOffset.x(), bottomRightOffset.y() ); + QRectF area( topLeft, size ); + + PainterSaver painterSaver( ctx->painter() ); + ctx->painter()->setPen( PrintingParameters::scalePen( vt.pen() ) ); + ctx->painter()->setBrush( QBrush() ); + + ctx->painter()->drawLine( markerPoint, ordinatePoint ); + ctx->painter()->drawLine( markerPoint, abscissaPoint ); + + ctx->painter()->fillRect( area, vt.areaBrush() ); + + ctx->painter()->drawEllipse( ellipseMarker ); + + ctx->painter()->setBrush( vt.pen().color() ); + ctx->painter()->drawPolygon( ordinateMarker, 3 ); + ctx->painter()->drawPolygon( abscissaMarker, 3 ); +} + +CartesianDiagramDataCompressor& Plotter::PlotterType::compressor() const +{ + return m_private->compressor; +} diff --git a/libkdchart/src/KDChartPlotter_p.h b/libkdchart/src/KDChartPlotter_p.h new file mode 100644 index 0000000..a9c0153 --- /dev/null +++ b/libkdchart/src/KDChartPlotter_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPLOTTER_P_H +#define KDCHARTPLOTTER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartPlotter.h" + +#include + +#include "KDChartThreeDLineAttributes.h" +#include "KDChartAbstractCartesianDiagram_p.h" +#include "KDChartCartesianDiagramDataCompressor_p.h" + +#include + + +namespace KDChart { + + class PaintContext; + +/** + * \internal + */ + class Plotter::Private : public AbstractCartesianDiagram::Private + { + friend class Plotter; + friend class PlotterType; + + public: + Private(); + Private( const Private& rhs ); + ~Private(); + + void setCompressorResolution( + const QSizeF& size, + const AbstractCoordinatePlane* plane ); + void paintPolyline( + PaintContext* ctx, + const QBrush& brush, const QPen& pen, + const QPolygonF& points ) const; + + Plotter* diagram; + PlotterType* implementor; // the current type + PlotterType* normalPlotter; + PlotterType* percentPlotter; + }; + + KDCHART_IMPL_DERIVED_DIAGRAM( Plotter, AbstractCartesianDiagram, CartesianCoordinatePlane ) + + // we inherit privately, so that derived classes cannot call the + // base class functions - those reference the wrong (unattached to + // a diagram) d + class Plotter::PlotterType : private Plotter::Private + { + public: + explicit PlotterType( Plotter* d ) + : Plotter::Private() + , m_private( d->d_func() ) + { + } + virtual ~PlotterType() {} + virtual Plotter::PlotType type() const = 0; + virtual const QPair calculateDataBoundaries() const = 0; + virtual void paint( PaintContext* ctx ) = 0; + Plotter* diagram() const; + + protected: + // method that make elements of m_private available to derived + // classes: + AttributesModel* attributesModel() const; + QModelIndex attributesModelRootIndex() const; + ReverseMapper& reverseMapper(); + CartesianDiagramDataCompressor& compressor() const; + + int datasetDimension() const; +/* LineAttributes::MissingValuesPolicy getCellValues( + int row, int column, + bool shiftCountedXValuesByHalfSection, + double& valueX, double& valueY ) const; + double valueForCellTesting( int row, int column, + bool& bOK, + bool showHiddenCellsAsInvalid = false ) const;*/ + void paintAreas( PaintContext* ctx, const QModelIndex& index, + const QList& areas, const uint transparency ); +/* double valueForCell( int row, int column );*/ + void appendDataValueTextInfoToList( + AbstractDiagram * diagram, + DataValueTextInfoList & list, + const QModelIndex & index, + const PositionPoints& points, + const Position& autoPositionPositive, + const Position& autoPositionNegative, + const qreal value ); + + + const QPointF project( QPointF point, QPointF maxLimits, + double z, const QModelIndex& index ) const; + + void paintThreeDLines( + PaintContext* ctx, const QModelIndex& index, + const QPointF& from, const QPointF& to, const double depth ); + + void paintElements( PaintContext* ctx, + DataValueTextInfoList&, + LineAttributesInfoList&, + LineAttributes::MissingValuesPolicy ); + + void paintValueTracker( PaintContext* ctx, const ValueTrackerAttributes& vt, + const QPointF& at ); + + Plotter::Private* m_private; + }; + +/* + inline LineDiagram::LineDiagram( Private * p, CartesianCoordinatePlane* plane ) + : AbstractCartesianDiagram( p, plane ) { init(); } + inline LineDiagram::Private * LineDiagram::d_func() + { return static_cast( AbstractCartesianDiagram::d_func() ); } + inline const LineDiagram::Private * LineDiagram::d_func() const + { return static_cast( AbstractCartesianDiagram::d_func() ); } +*/ + +} + +#endif /* KDCHARTLINEDIAGRAM_P_H */ diff --git a/libkdchart/src/KDChartPolarCoordinatePlane.cpp b/libkdchart/src/KDChartPolarCoordinatePlane.cpp new file mode 100644 index 0000000..011f30a --- /dev/null +++ b/libkdchart/src/KDChartPolarCoordinatePlane.cpp @@ -0,0 +1,435 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPolarCoordinatePlane.h" +#include "KDChartPolarCoordinatePlane_p.h" + +#include "KDChartPainterSaver_p.h" +#include "KDChartChart.h" +#include "KDChartPaintContext.h" +#include "KDChartAbstractDiagram.h" +#include "KDChartAbstractPolarDiagram.h" +#include "KDChartPolarDiagram.h" + +#include + +#include +#include +#include +#include +#include + +#include + +using namespace KDChart; + +#define d d_func() + + +/* +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#define DEGTORAD(d) (d)*M_PI/180 + +struct PolarCoordinatePlane::CoordinateTransformation +{ + // represents the distance of the diagram coordinate origin to the + // origin of the coordinate plane space: + QPointF originTranslation; + double radiusUnit; + double angleUnit; + + ZoomParameters zoom; + + static QPointF polarToCartesian( double R, double theta ) + { + return QPointF( R * cos( DEGTORAD( theta ) ), R * sin( DEGTORAD( theta ) ) ); + } + + inline const QPointF translate( const QPointF& diagramPoint ) const + { + // calculate the polar coordinates + const double x = diagramPoint.x() * radiusUnit; + const double y = ( diagramPoint.y() * angleUnit) - 90; + // convert to cartesian coordinates + QPointF cartesianPoint = polarToCartesian( x, y ); + cartesianPoint.setX( cartesianPoint.x() * zoom.xFactor ); + cartesianPoint.setY( cartesianPoint.y() * zoom.yFactor ); + + QPointF newOrigin = originTranslation; + double minOrigin = qMin( newOrigin.x(), newOrigin.y() ); + newOrigin.setX( newOrigin.x() + minOrigin * ( 1 - zoom.xCenter * 2 ) * zoom.xFactor ); + newOrigin.setY( newOrigin.y() + minOrigin * ( 1 - zoom.yCenter * 2 ) * zoom.yFactor ); + + return newOrigin + cartesianPoint; + } + + inline const QPointF translatePolar( const QPointF& diagramPoint ) const + { + return QPointF( diagramPoint.x() * angleUnit, diagramPoint.y() * radiusUnit ); + } +}; + +class PolarCoordinatePlane::Private +{ +public: + Private() + :currentTransformation(0), + initialResizeEventReceived(false ) + {} + + + // the coordinate plane will calculate coordinate transformations for all + // diagrams and store them here: + CoordinateTransformationList coordinateTransformations; + // when painting, this pointer selects the coordinate transformation for + // the current diagram: + CoordinateTransformation* currentTransformation; + // the reactangle occupied by the diagrams, in plane coordinates + QRectF contentRect; + // true after the first resize event came in + bool initialResizeEventReceived; +}; +*/ + +PolarCoordinatePlane::PolarCoordinatePlane ( Chart* parent ) + : AbstractCoordinatePlane ( new Private(), parent ) +{ + // this bloc left empty intentionally +} + +PolarCoordinatePlane::~PolarCoordinatePlane() +{ + // this bloc left empty intentionally +} + +void PolarCoordinatePlane::init() +{ + // this bloc left empty intentionally +} + +void PolarCoordinatePlane::addDiagram ( AbstractDiagram* diagram ) +{ + Q_ASSERT_X ( dynamic_cast ( diagram ), + "PolarCoordinatePlane::addDiagram", "Only polar" + "diagrams can be added to a polar coordinate plane!" ); + AbstractCoordinatePlane::addDiagram ( diagram ); + connect ( diagram, SIGNAL ( layoutChanged ( AbstractDiagram* ) ), + SLOT ( slotLayoutChanged ( AbstractDiagram* ) ) ); + +} + +void PolarCoordinatePlane::paint ( QPainter* painter ) +{ + AbstractDiagramList diags = diagrams(); + if ( d->coordinateTransformations.size() == diags.size() ) + { + PaintContext ctx; + ctx.setPainter ( painter ); + ctx.setCoordinatePlane ( this ); + ctx.setRectangle ( geometry() /*d->contentRect*/ ); + + // 1. ask the diagrams if they need additional space for data labels / data comments + const qreal oldZoomX = zoomFactorX(); + const qreal oldZoomY = zoomFactorY(); + d->newZoomX = oldZoomX; + d->newZoomY = oldZoomY; + for ( int i = 0; i < diags.size(); i++ ) + { + d->currentTransformation = & ( d->coordinateTransformations[i] ); + qreal zoomX; + qreal zoomY; + PolarDiagram* polarDia = dynamic_cast ( diags[i] ); + if( polarDia ){ + polarDia->paint ( &ctx, true, zoomX, zoomY ); + d->newZoomX = qMin(d->newZoomX, zoomX); + d->newZoomY = qMin(d->newZoomY, zoomY); + } + } + d->currentTransformation = 0; + + // if re-scaling is needed start the timer and bail out + if( d->newZoomX != oldZoomX || d->newZoomY != oldZoomY ){ + //qDebug()<<"new zoom:"<newZoomY<<" old zoom"<currentTransformation = & ( d->coordinateTransformations.first() ); + + d->grid->drawGrid( &ctx ); + + // paint the diagrams which will re-use their DataValueTextInfoList(s) filled in step 1: + for ( int i = 0; i < diags.size(); i++ ) + { + d->currentTransformation = & ( d->coordinateTransformations[i] ); + PainterSaver painterSaver( painter ); + PolarDiagram* polarDia = dynamic_cast ( diags[i] ); + if( polarDia ){ + qreal dummy1, dummy2; + polarDia->paint ( &ctx, false, dummy1, dummy2 ); + }else{ + diags[i]->paint ( &ctx ); + } + } + d->currentTransformation = 0; + } // else: diagrams have not been set up yet +} + + +void PolarCoordinatePlane::adjustZoomAndRepaint() +{ + const qreal newZoom = qMin(d->newZoomX, d->newZoomY); + setZoomFactors(newZoom, newZoom); + update(); +} + + +void PolarCoordinatePlane::resizeEvent ( QResizeEvent* ) +{ + d->initialResizeEventReceived = true; + layoutDiagrams(); +} + +void PolarCoordinatePlane::layoutDiagrams() +{ + // the rectangle the diagrams cover in the *plane*: + // (Why -3? We save 1px on each side for the antialiased drawing, and + // respect the way QPainter calculates the width of a painted rect (the + // size is the rectangle size plus the pen width). This way, most clipping + // for regular pens should be avoided. When pens with a penWidth or larger + // than 1 are used, this may not b sufficient. + const QRect rect( areaGeometry() ); + d->contentRect = QRectF ( 1, 1, rect.width() - 3, rect.height() - 3 ); + + const ZoomParameters zoom = d->coordinateTransformations.isEmpty() ? ZoomParameters() + : d->coordinateTransformations.front().zoom; + // FIXME distribute space according to options: + const qreal oldStartPosition = startPosition(); + d->coordinateTransformations.clear(); + Q_FOREACH( AbstractDiagram* diagram, diagrams() ) + { + AbstractPolarDiagram *polarDiagram = dynamic_cast( diagram ); + Q_ASSERT( polarDiagram ); + QPair dataBoundariesPair = polarDiagram->dataBoundaries(); + + const double angleUnit = 360 / polarDiagram->valueTotals(); +//qDebug() << "--------------------------------------------------------"; + const double radius = qAbs( dataBoundariesPair.first.y() ) + dataBoundariesPair.second.y(); +//qDebug() << radius <<"="<contentRect.width(); + const double planeHeight = d->contentRect.height(); + const double radiusUnit = qMin( planeWidth, planeHeight ) / diagramWidth; +//qDebug() << radiusUnit <<"=" << "qMin( "<contentRect.topLeft(); + + CoordinateTransformation diagramTransposition; + diagramTransposition.originTranslation = coordinateOrigin; + diagramTransposition.radiusUnit = radiusUnit; + diagramTransposition.angleUnit = angleUnit; + diagramTransposition.startPosition = oldStartPosition; + diagramTransposition.zoom = zoom; + diagramTransposition.minValue = dataBoundariesPair.first.y() < 0 ? dataBoundariesPair.first.y() : 0.0; + d->coordinateTransformations.append( diagramTransposition ); + } +} + +const QPointF PolarCoordinatePlane::translate( const QPointF& diagramPoint ) const +{ + Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::translate", + "Only call translate() from within paint()." ); + return d->currentTransformation->translate ( diagramPoint ); +} + +const QPointF PolarCoordinatePlane::translatePolar( const QPointF& diagramPoint ) const +{ + Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::translate", + "Only call translate() from within paint()." ); + return d->currentTransformation->translatePolar ( diagramPoint ); +} + +qreal PolarCoordinatePlane::angleUnit() const +{ + Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::angleUnit", + "Only call angleUnit() from within paint()." ); + return d->currentTransformation->angleUnit; +} + +qreal PolarCoordinatePlane::radiusUnit() const +{ + Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::radiusUnit", + "Only call radiusUnit() from within paint()." ); + return d->currentTransformation->radiusUnit; +} + +void PolarCoordinatePlane::slotLayoutChanged ( AbstractDiagram* ) +{ + if ( d->initialResizeEventReceived ) layoutDiagrams(); +} + +void PolarCoordinatePlane::setStartPosition( qreal degrees ) +{ + Q_ASSERT_X ( diagram(), "PolarCoordinatePlane::setStartPosition", + "setStartPosition() needs a diagram to be associated to the plane." ); + for( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin(); + it != d->coordinateTransformations.end(); + ++it ) + { + CoordinateTransformation& trans = *it; + trans.startPosition = degrees; + } +} + +qreal PolarCoordinatePlane::startPosition() const +{ + return d->coordinateTransformations.isEmpty() + ? 0.0 + : d->coordinateTransformations.first().startPosition; +} + +double PolarCoordinatePlane::zoomFactorX() const +{ + return d->coordinateTransformations.isEmpty() + ? 1.0 + : d->coordinateTransformations.first().zoom.xFactor; +} + +double PolarCoordinatePlane::zoomFactorY() const +{ + return d->coordinateTransformations.isEmpty() + ? 1.0 + : d->coordinateTransformations.first().zoom.yFactor; +} + +void PolarCoordinatePlane::setZoomFactors( double factorX, double factorY ) +{ + setZoomFactorX( factorX ); + setZoomFactorY( factorY ); +} + +void PolarCoordinatePlane::setZoomFactorX( double factor ) +{ + for( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin(); + it != d->coordinateTransformations.end(); + ++it ) + { + CoordinateTransformation& trans = *it; + trans.zoom.xFactor = factor; + } +} + +void PolarCoordinatePlane::setZoomFactorY( double factor ) +{ + for( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin(); + it != d->coordinateTransformations.end(); + ++it ) + { + CoordinateTransformation& trans = *it; + trans.zoom.yFactor = factor; + } +} + +QPointF PolarCoordinatePlane::zoomCenter() const +{ + return d->coordinateTransformations.isEmpty() + ? QPointF( 0.5, 0.5 ) + : QPointF( d->coordinateTransformations.first().zoom.xCenter, d->coordinateTransformations.first().zoom.yCenter ); +} + +void PolarCoordinatePlane::setZoomCenter( const QPointF& center ) +{ + for( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin(); + it != d->coordinateTransformations.end(); + ++it ) + { + CoordinateTransformation& trans = *it; + trans.zoom.xCenter = center.x(); + trans.zoom.yCenter = center.y(); + } +} + +DataDimensionsList PolarCoordinatePlane::getDataDimensionsList() const +{ + DataDimensionsList l; + + //FIXME(khz): do the real calculation + + return l; +} + +void KDChart::PolarCoordinatePlane::setGridAttributes( + bool circular, + const GridAttributes& a ) +{ + if( circular ) + d->gridAttributesCircular = a; + else + d->gridAttributesSagittal = a; + setHasOwnGridAttributes( circular, true ); + update(); + emit propertiesChanged(); +} + +void KDChart::PolarCoordinatePlane::resetGridAttributes( + bool circular ) +{ + setHasOwnGridAttributes( circular, false ); + update(); +} + +const GridAttributes KDChart::PolarCoordinatePlane::gridAttributes( + bool circular ) const +{ + if( hasOwnGridAttributes( circular ) ){ + if( circular ) + return d->gridAttributesCircular; + else + return d->gridAttributesSagittal; + }else{ + return globalGridAttributes(); + } +} + +void KDChart::PolarCoordinatePlane::setHasOwnGridAttributes( + bool circular, bool on ) +{ + if( circular ) + d->hasOwnGridAttributesCircular = on; + else + d->hasOwnGridAttributesSagittal = on; + emit propertiesChanged(); +} + +bool KDChart::PolarCoordinatePlane::hasOwnGridAttributes( + bool circular ) const +{ + return + ( circular ) + ? d->hasOwnGridAttributesCircular + : d->hasOwnGridAttributesSagittal; +} diff --git a/libkdchart/src/KDChartPolarCoordinatePlane.h b/libkdchart/src/KDChartPolarCoordinatePlane.h new file mode 100644 index 0000000..8f812f4 --- /dev/null +++ b/libkdchart/src/KDChartPolarCoordinatePlane.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHART_POLAR_COORDINATEPLANE_H +#define KDCHART_POLAR_COORDINATEPLANE_H + +#include "KDChartAbstractCoordinatePlane.h" + +namespace KDChart { + + class Chart; + class PaintContext; + + /** + * @brief Polar coordinate plane + */ + class KDCHART_EXPORT PolarCoordinatePlane : public AbstractCoordinatePlane + { + Q_OBJECT + + Q_DISABLE_COPY( PolarCoordinatePlane ) + KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( PolarCoordinatePlane, Chart* ) + + public: + struct CoordinateTransformation; + typedef QList CoordinateTransformationList; + + explicit PolarCoordinatePlane ( Chart* parent = 0 ); + ~PolarCoordinatePlane(); + + void addDiagram ( AbstractDiagram* diagram ); + + const QPointF translate ( const QPointF& diagramPoint ) const; + const QPointF translatePolar ( const QPointF& diagramPoint ) const; + + /** \brief Specify the rotation of the coordinate plane. + * + * In a Pie diagram this indicates the position where the first pie starts, + * in a Polar diagram it specifies the Zero position of the circular axis. + * \sa startPosition + */ + void setStartPosition( qreal degrees ); + /** Retrieve the rotation of the coordinate plane. + * \sa setStartPosition + */ + qreal startPosition() const; + + virtual double zoomFactorX() const; + virtual double zoomFactorY() const; + + virtual void setZoomFactors( double factorX, double factorY ); + virtual void setZoomFactorX( double factor ); + virtual void setZoomFactorY( double factor ); + + virtual QPointF zoomCenter() const; + + virtual void setZoomCenter( const QPointF& center ); + + /** + * Set the attributes to be used for grid lines drawn in circular + * direction (or in sagittal direction, resp.). + * + * To disable circular grid painting, for example, your code should like this: + * \code + * GridAttributes ga = plane->gridAttributes( bool ); + * ga.setGridVisible( false ); + * plane-setGridAttributes( bool, ga ); + * \endcode + * + * \note setGridAttributes overwrites the global attributes that + * were set by AbstractCoordinatePlane::setGlobalGridAttributes. + * To re-activate these global attributes you can call + * resetGridAttributes. + * + * \sa resetGridAttributes, gridAttributes + * \sa AbstractCoordinatePlane::setGlobalGridAttributes + * \sa hasOwnGridAttributes + */ + void setGridAttributes( bool circular, const GridAttributes & ); + + /** + * Reset the attributes to be used for grid lines drawn in circular + * direction (or in sagittal direction, resp.). + * By calling this method you specify that the global attributes set by + * AbstractCoordinatePlane::setGlobalGridAttributes be used. + * + * \sa setGridAttributes, gridAttributes + * \sa AbstractCoordinatePlane::globalGridAttributes + * \sa hasOwnGridAttributes + */ + void resetGridAttributes( bool circular ); + + /** + * \return The attributes used for grid lines drawn in circular + * direction (or in sagittal direction, resp.). + * + * \note This function always returns a valid set of grid attributes: + * If no special grid attributes were set for this direction + * the global attributes are returned, as returned by + * AbstractCoordinatePlane::globalGridAttributes. + * + * \sa setGridAttributes + * \sa resetGridAttributes + * \sa AbstractCoordinatePlane::globalGridAttributes + * \sa hasOwnGridAttributes + */ + const GridAttributes gridAttributes( bool circular ) const; + + /** + * \return Returns whether the grid attributes have been set for the + * respective direction via setGridAttributes( bool circular ). + * + * If false, the grid will use the global attributes set + * by AbstractCoordinatePlane::globalGridAttributes (or the default + * attributes, resp.) + * + * \sa setGridAttributes + * \sa resetGridAttributes + * \sa AbstractCoordinatePlane::globalGridAttributes + */ + bool hasOwnGridAttributes( bool circular ) const; + + qreal angleUnit() const; + qreal radiusUnit() const; + + /** reimpl */ + virtual void paint( QPainter* ); + + protected: + virtual DataDimensionsList getDataDimensionsList() const; + void paintEvent ( QPaintEvent* ); + void resizeEvent ( QResizeEvent* ); + + void layoutDiagrams(); + protected Q_SLOTS: + void slotLayoutChanged( AbstractDiagram* diagram ); + void adjustZoomAndRepaint(); + + private: + void setHasOwnGridAttributes( + bool circular, bool on ); + }; + +} + +#endif diff --git a/libkdchart/src/KDChartPolarCoordinatePlane_p.h b/libkdchart/src/KDChartPolarCoordinatePlane_p.h new file mode 100644 index 0000000..d07f670 --- /dev/null +++ b/libkdchart/src/KDChartPolarCoordinatePlane_p.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPOLARCOORDINATEPLANE_P_H +#define KDCHARTPOLARCOORDINATEPLANE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractCoordinatePlane_p.h" +#include "KDChartZoomParameters.h" +#include "KDChartPolarGrid.h" + +#include + + +namespace KDChart { + +/** + * \internal + */ +struct PolarCoordinatePlane::CoordinateTransformation +{ + // represents the distance of the diagram coordinate origin to the + // origin of the coordinate plane space: + QPointF originTranslation; + double radiusUnit; + double angleUnit; + double minValue; + + qreal startPosition; + ZoomParameters zoom; + + static QPointF polarToCartesian( double R, double theta ) + { + // de-inline me + return QPointF( R * cos( DEGTORAD( theta ) ), R * sin( DEGTORAD( theta ) ) ); + } + + inline const QPointF translate( const QPointF& diagramPoint ) const + { + // ### de-inline me + // calculate the polar coordinates + const double x = (diagramPoint.x() * radiusUnit) - (minValue * radiusUnit); +//qDebug() << x << "=" << diagramPoint.x() << "*" << radiusUnit << " startPosition: " << startPosition; + const double y = ( diagramPoint.y() * -angleUnit) - 90.0 - startPosition; + // convert to cartesian coordinates + QPointF cartesianPoint = polarToCartesian( x, y ); + cartesianPoint.setX( cartesianPoint.x() * zoom.xFactor ); + cartesianPoint.setY( cartesianPoint.y() * zoom.yFactor ); + + QPointF newOrigin = originTranslation; + double minOrigin = qMin( newOrigin.x(), newOrigin.y() ); + newOrigin.setX( newOrigin.x() + minOrigin * ( 1 - zoom.xCenter * 2 ) * zoom.xFactor ); + newOrigin.setY( newOrigin.y() + minOrigin * ( 1 - zoom.yCenter * 2 ) * zoom.yFactor ); + + return newOrigin + cartesianPoint; + } + + inline const QPointF translatePolar( const QPointF& diagramPoint ) const + { + // ### de-inline me + return QPointF( diagramPoint.x() * angleUnit, diagramPoint.y() * radiusUnit ); + } +}; + +class PolarCoordinatePlane::Private : public AbstractCoordinatePlane::Private +{ + friend class PolarCoordinatePlane; +public: + explicit Private() + : currentTransformation(0) + , initialResizeEventReceived(false ) + , hasOwnGridAttributesCircular ( false ) + , hasOwnGridAttributesSagittal ( false ) + {} + + virtual ~Private() { } + + virtual void initialize() + { + grid = new PolarGrid(); + } + + // the coordinate plane will calculate coordinate transformations for all + // diagrams and store them here: + CoordinateTransformationList coordinateTransformations; + // when painting, this pointer selects the coordinate transformation for + // the current diagram: + CoordinateTransformation* currentTransformation; + // the reactangle occupied by the diagrams, in plane coordinates + QRectF contentRect; + // true after the first resize event came in + bool initialResizeEventReceived; + + // true after setGridAttributes( Qt::Orientation ) was used, + // false if resetGridAttributes( Qt::Orientation ) was called + bool hasOwnGridAttributesCircular; + bool hasOwnGridAttributesSagittal; + + GridAttributes gridAttributesCircular; + GridAttributes gridAttributesSagittal; + + qreal newZoomX, newZoomY; +}; + + +KDCHART_IMPL_DERIVED_PLANE(PolarCoordinatePlane, AbstractCoordinatePlane) + +} + +#endif /* KDCHARTBARDIAGRAM_P_H */ diff --git a/libkdchart/src/KDChartPolarDiagram.cpp b/libkdchart/src/KDChartPolarDiagram.cpp new file mode 100644 index 0000000..537ea32 --- /dev/null +++ b/libkdchart/src/KDChartPolarDiagram.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPolarDiagram.h" +#include "KDChartPolarDiagram_p.h" + +#include +#include "KDChartAttributesModel.h" +#include "KDChartPaintContext.h" +#include "KDChartPainterSaver_p.h" +#include "KDChartDataValueAttributes.h" + +#include + +using namespace KDChart; + +PolarDiagram::Private::Private() : + rotateCircularLabels( false ), + closeDatasets( false ) +{ +} + +PolarDiagram::Private::~Private() {} + +#define d d_func() + +PolarDiagram::PolarDiagram( QWidget* parent, PolarCoordinatePlane* plane ) : + AbstractPolarDiagram( new Private( ), parent, plane ) +{ + //init(); +} + +PolarDiagram::~PolarDiagram() +{ +} + + +void PolarDiagram::init() +{ + setShowDelimitersAtPosition( Position::Unknown, false ); + setShowDelimitersAtPosition( Position::Center, false ); + setShowDelimitersAtPosition( Position::NorthWest, false ); + setShowDelimitersAtPosition( Position::North, true ); + setShowDelimitersAtPosition( Position::NorthEast, false ); + setShowDelimitersAtPosition( Position::West, false ); + setShowDelimitersAtPosition( Position::East, false ); + setShowDelimitersAtPosition( Position::SouthWest, false ); + setShowDelimitersAtPosition( Position::South, true ); + setShowDelimitersAtPosition( Position::SouthEast, false ); + setShowDelimitersAtPosition( Position::Floating, false ); + + setShowLabelsAtPosition( Position::Unknown, false ); + setShowLabelsAtPosition( Position::Center, false ); + setShowLabelsAtPosition( Position::NorthWest, false ); + setShowLabelsAtPosition( Position::North, true ); + setShowLabelsAtPosition( Position::NorthEast, false ); + setShowLabelsAtPosition( Position::West, false ); + setShowLabelsAtPosition( Position::East, false ); + setShowLabelsAtPosition( Position::SouthWest, false ); + setShowLabelsAtPosition( Position::South, true ); + setShowLabelsAtPosition( Position::SouthEast, false ); + setShowLabelsAtPosition( Position::Floating, false ); +} + +/** + * Creates an exact copy of this diagram. + */ +PolarDiagram * PolarDiagram::clone() const +{ + PolarDiagram* newDiagram = new PolarDiagram( new Private( *d ) ); + // This needs to be copied after the fact + newDiagram->d->showDelimitersAtPosition = d->showDelimitersAtPosition; + newDiagram->d->showLabelsAtPosition = d->showLabelsAtPosition; + newDiagram->d->rotateCircularLabels = d->rotateCircularLabels; + newDiagram->d->closeDatasets = d->closeDatasets; + return newDiagram; +} + +const QPair PolarDiagram::calculateDataBoundaries () const +{ + if ( !checkInvariants(true) ) return QPair( QPointF( 0, 0 ), QPointF( 0, 0 ) ); + const int rowCount = model()->rowCount(rootIndex()); + const int colCount = model()->columnCount(rootIndex()); + double xMin = 0.0; + double xMax = colCount; + double yMin = 0, yMax = 0; + for ( int iCol=0; iColdata( model()->index( iRow, iCol, rootIndex() ) ).toDouble(); + yMax = qMax( yMax, value ); + yMin = qMin( yMin, value ); + } + } + QPointF bottomLeft ( QPointF( xMin, yMin ) ); + QPointF topRight ( QPointF( xMax, yMax ) ); + return QPair ( bottomLeft, topRight ); +} + + + +void PolarDiagram::paintEvent ( QPaintEvent*) +{ + QPainter painter ( viewport() ); + PaintContext ctx; + ctx.setPainter ( &painter ); + ctx.setRectangle( QRectF ( 0, 0, width(), height() ) ); + paint ( &ctx ); +} + +void PolarDiagram::resizeEvent ( QResizeEvent*) +{ +} + +void PolarDiagram::paintPolarMarkers( PaintContext* ctx, const QPolygonF& polygon ) +{ + Q_UNUSED(ctx); + Q_UNUSED(polygon); + // obsolete, since we are using real markers now! +} + +void PolarDiagram::paint( PaintContext* ctx ) +{ + qreal dummy1, dummy2; + paint( ctx, true, dummy1, dummy2 ); + paint( ctx, false, dummy1, dummy2 ); +} + +void PolarDiagram::paint( PaintContext* ctx, + bool calculateListAndReturnScale, + qreal& newZoomX, qreal& newZoomY ) +{ + // note: Not having any data model assigned is no bug + // but we can not draw a diagram then either. + if ( !checkInvariants(true) ) + return; + d->reverseMapper.clear(); + + const int rowCount = model()->rowCount( rootIndex() ); + const int colCount = model()->columnCount( rootIndex() ); + + int iRow, iCol; + + if( calculateListAndReturnScale ){ + + // Check if all of the data value texts / data comments will fit + // into the available space: + d->dataValueInfoList.clear(); + for ( iCol=0; iCol < colCount; ++iCol ) { + for ( iRow=0; iRow < rowCount; ++iRow ) { + QModelIndex index = model()->index( iRow, iCol, rootIndex() ); + const double value = model()->data( index ).toDouble(); + QPointF point = coordinatePlane()->translate( + QPointF( value, iRow ) ) + ctx->rectangle().topLeft(); + //qDebug() << point; + d->appendDataValueTextInfoToList( + this, d->dataValueInfoList, index, 0, + PositionPoints( point ), Position::Center, Position::Center, + value ); + } + } + const qreal oldZoomX = coordinatePlane()->zoomFactorX(); + const qreal oldZoomY = coordinatePlane()->zoomFactorY(); + newZoomX = oldZoomX; + newZoomY = oldZoomY; + if( d->dataValueInfoList.count() ){ + QRectF txtRectF; + d->paintDataValueTextsAndMarkers( this, ctx, d->dataValueInfoList, true, true, &txtRectF ); + const QRect txtRect = txtRectF.toRect(); + const QRect curRect = coordinatePlane()->geometry(); + const qreal gapX = qMin( txtRect.left() - curRect.left(), curRect.right() - txtRect.right() ); + const qreal gapY = qMin( txtRect.top() - curRect.top(), curRect.bottom() - txtRect.bottom() ); + newZoomX = oldZoomX; + newZoomY = oldZoomY; + if( gapX < 0.0 ) + newZoomX *= 1.0 + (gapX-1.0) / curRect.width(); + if( gapY < 0.0 ) + newZoomY *= 1.0 + (gapY-1.0) / curRect.height(); + } + + }else{ + // Iterate through data sets + for ( iCol=0; iCol < colCount; ++iCol ) { + //TODO(khz): As of yet PolarDiagram can not show per-segment line attributes + // but it draws every polyline in one go - using one color. + // This needs to be enhanced to allow for cell-specific settings + // in the same way as LineDiagram does it. + QBrush brush = qVariantValue( d->datasetAttrs( iCol, KDChart::DatasetBrushRole ) ); + QPolygonF polygon; + QPointF point0; + for ( iRow=0; iRow < rowCount; ++iRow ) { + QModelIndex index = model()->index( iRow, iCol, rootIndex() ); + const double value = model()->data( index ).toDouble(); + QPointF point = coordinatePlane()->translate( + QPointF( value, iRow ) ) + ctx->rectangle().topLeft(); + polygon.append( point ); + //qDebug() << point; + if( ! iRow ) + point0= point; + } + if( closeDatasets() && rowCount ) + polygon.append( point0 ); + + PainterSaver painterSaver( ctx->painter() ); + ctx->painter()->setRenderHint ( QPainter::Antialiasing ); + ctx->painter()->setBrush( brush ); + QPen p( ctx->painter()->pen() ); + p.setColor( brush.color() ); // FIXME use DatasetPenRole + p.setWidth( 2 );// FIXME properties + ctx->painter()->setPen( PrintingParameters::scalePen( p ) ); + ctx->painter()->drawPolyline( polygon ); + } + d->paintDataValueTextsAndMarkers( this, ctx, d->dataValueInfoList, true ); + } +} + +void PolarDiagram::resize ( const QSizeF& ) +{ +} + +/*virtual*/ +double PolarDiagram::valueTotals () const +{ + return model()->rowCount(rootIndex()); +} + +/*virtual*/ +double PolarDiagram::numberOfValuesPerDataset() const +{ + return model() ? model()->rowCount(rootIndex()) : 0.0; +} + +/*virtual*/ +double PolarDiagram::numberOfGridRings() const +{ + return 5; // FIXME +} + +void PolarDiagram::setZeroDegreePosition( int degrees ) +{ + Q_UNUSED( degrees ); + qWarning() << "Deprecated PolarDiagram::setZeroDegreePosition() called, setting ignored."; +} + +int PolarDiagram::zeroDegreePosition() const +{ + qWarning() << "Deprecated PolarDiagram::zeroDegreePosition() called."; + return 0; +} + +void PolarDiagram::setRotateCircularLabels( bool rotateCircularLabels ) +{ + d->rotateCircularLabels = rotateCircularLabels; +} + +bool PolarDiagram::rotateCircularLabels() const +{ + return d->rotateCircularLabels; +} + +void PolarDiagram::setCloseDatasets( bool closeDatasets ) +{ + d->closeDatasets = closeDatasets; +} + +bool PolarDiagram::closeDatasets() const +{ + return d->closeDatasets; +} + +void PolarDiagram::setShowDelimitersAtPosition( Position position, + bool showDelimiters ) +{ + d->showDelimitersAtPosition[position.value()] = showDelimiters; +} + +void PolarDiagram::setShowLabelsAtPosition( Position position, + bool showLabels ) +{ + d->showLabelsAtPosition[position.value()] = showLabels; +} + +bool PolarDiagram::showDelimitersAtPosition( Position position ) const +{ + return d->showDelimitersAtPosition[position.value()]; +} + +bool PolarDiagram::showLabelsAtPosition( Position position ) const +{ + return d->showLabelsAtPosition[position.value()]; +} + + + diff --git a/libkdchart/src/KDChartPolarDiagram.h b/libkdchart/src/KDChartPolarDiagram.h new file mode 100644 index 0000000..e0abb21 --- /dev/null +++ b/libkdchart/src/KDChartPolarDiagram.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPOLARDIAGRAM_H +#define KDCHARTPOLARDIAGRAM_H + + +#include "KDChartPosition.h" +#include "KDChartAbstractPolarDiagram.h" + + +class QPolygonF; + + +namespace KDChart { + +/** + * @brief PolarDiagram defines a common polar diagram + */ +class KDCHART_EXPORT PolarDiagram : public AbstractPolarDiagram +{ + Q_OBJECT + + Q_DISABLE_COPY( PolarDiagram ) + KDCHART_DECLARE_DERIVED_DIAGRAM( PolarDiagram, PolarCoordinatePlane ) + +public: + explicit PolarDiagram( + QWidget* parent = 0, PolarCoordinatePlane* plane = 0 ); + virtual ~PolarDiagram(); + +protected: + // Implement AbstractDiagram + /** \reimpl */ + virtual void paint ( PaintContext* paintContext ); + +public: + /** \reimpl */ + virtual void resize ( const QSizeF& area ); + + // Implement AbstractPolarDiagram + /** \reimpl */ + virtual double valueTotals () const; + /** \reimpl */ + virtual double numberOfValuesPerDataset() const; + /** \reimpl */ + virtual double numberOfGridRings() const; + + virtual PolarDiagram * clone() const; + + /** \deprecated Use PolarCoordinatePlane::setStartPosition( qreal degrees ) instead. */ + void setZeroDegreePosition( int degrees ); + /** \deprecated Use qreal PolarCoordinatePlane::startPosition instead. */ + int zeroDegreePosition() const; + + void setRotateCircularLabels( bool rotateCircularLabels ); + bool rotateCircularLabels() const; + + /** Close each of the data series by connecting the last point to its + * respective start point + */ + void setCloseDatasets( bool closeDatasets ); + bool closeDatasets() const; + + void setShowDelimitersAtPosition( Position position, + bool showDelimiters ); + void setShowLabelsAtPosition( Position position, + bool showLabels ); + + bool showDelimitersAtPosition( Position position ) const; + + bool showLabelsAtPosition( Position position ) const; + + virtual void paint ( PaintContext* paintContext, + bool calculateListAndReturnScale, + qreal& newZoomX, qreal& newZoomY ); + +protected: + /** \reimpl */ + virtual const QPair calculateDataBoundaries() const; + void paintEvent ( QPaintEvent* ); + void resizeEvent ( QResizeEvent* ); + virtual void paintPolarMarkers( PaintContext* ctx, const QPolygonF& polygon ); + +}; // End of class PolarDiagram + +} + + +#endif // KDCHARTPOLARDIAGRAM_H diff --git a/libkdchart/src/KDChartPolarDiagram_p.h b/libkdchart/src/KDChartPolarDiagram_p.h new file mode 100644 index 0000000..299ce3a --- /dev/null +++ b/libkdchart/src/KDChartPolarDiagram_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPOLARDIAGRAM_P_H +#define KDCHARTPOLARDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractPolarDiagram_p.h" + +#include + + +namespace KDChart { + +/** + * \internal + */ +class PolarDiagram::Private : public AbstractPolarDiagram::Private +{ + friend class PolarDiagram; +public: + Private(); + ~Private(); + + Private( const Private& rhs ) : + AbstractPolarDiagram::Private( rhs ), + showDelimitersAtPosition( rhs.showDelimitersAtPosition ), + showLabelsAtPosition( rhs.showLabelsAtPosition ), + rotateCircularLabels( rhs.rotateCircularLabels ), + closeDatasets( rhs.closeDatasets ) + { + } + +private: + QMap showDelimitersAtPosition; + QMap showLabelsAtPosition; + bool rotateCircularLabels; + bool closeDatasets; + DataValueTextInfoList dataValueInfoList; +}; + +KDCHART_IMPL_DERIVED_DIAGRAM( PolarDiagram, AbstractPolarDiagram, PolarCoordinatePlane ) + +} + +#endif /* KDCHARTPOLARDIAGRAM_P_H */ diff --git a/libkdchart/src/KDChartPolarGrid.cpp b/libkdchart/src/KDChartPolarGrid.cpp new file mode 100644 index 0000000..e5b0f4c --- /dev/null +++ b/libkdchart/src/KDChartPolarGrid.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPolarGrid.h" +#include "KDChartPaintContext.h" +#include "KDChartPolarDiagram.h" +#include "KDChartPieDiagram.h" +#include "KDChartPrintingParameters.h" + +#include + +#include + +using namespace KDChart; + + +DataDimensionsList PolarGrid::calculateGrid( + const DataDimensionsList& rawDataDimensions ) const +{ + qDebug("Calling PolarGrid::calculateGrid()"); + DataDimensionsList l; + + //FIXME(khz): do the real calculation + + l = rawDataDimensions; + + return l; +} + + +void PolarGrid::drawGrid( PaintContext* context ) +{ +// if ( d->coordinateTransformations.size () <= 0 ) return; + + PolarCoordinatePlane* plane = dynamic_cast(context->coordinatePlane()); + Q_ASSERT_X ( plane, "PolarGrid::drawGrid", + "Bad function call: PaintContext::coodinatePlane() NOT a polar plane." ); + + const GridAttributes gridAttrsCircular( plane->gridAttributes( true ) ); + const GridAttributes gridAttrsSagittal( plane->gridAttributes( false ) ); + + //qDebug() << "OK:"; + if ( !gridAttrsCircular.isGridVisible() && !gridAttrsSagittal.isGridVisible() ) return; + //qDebug() << "A"; + + // FIXME: we paint the rulers to the settings of the first diagram for now: + AbstractPolarDiagram* dgr = dynamic_cast (plane->diagrams().first() ); + Q_ASSERT ( dgr ); // only polar diagrams are allowed here + + + // Do not draw a grid for pie diagrams + if( dynamic_cast (plane->diagrams().first() ) ) return; + + + context->painter()->setPen ( PrintingParameters::scalePen( QColor ( Qt::lightGray ) ) ); + const double min = dgr->dataBoundaries().first.y(); + QPointF origin = plane->translate( QPointF( min, 0 ) ) + context->rectangle().topLeft(); + //qDebug() << "origin" << origin; + + const double r = qAbs( min ) + dgr->dataBoundaries().second.y(); // use the full extents + + if ( gridAttrsSagittal.isGridVisible() ){ + const int numberOfSpokes = ( int ) ( 360 / plane->angleUnit() ); + for ( int i = 0; i < numberOfSpokes ; ++i ) { + context->painter()->drawLine( origin, plane->translate( QPointF( r - qAbs( min ), i ) ) + context->rectangle().topLeft() ); + } + } + + if ( gridAttrsCircular.isGridVisible() ) + { + const qreal startPos = plane->startPosition(); + plane->setStartPosition( 0.0 ); + const int numberOfGridRings = ( int )dgr->numberOfGridRings(); + for ( int j = 0; j < numberOfGridRings; ++j ) { + const double rad = min - ( ( j + 1) * r / numberOfGridRings ); + + if ( rad == 0 ) + continue; + + QRectF rect; + QPointF topLeftPoint; + QPointF bottomRightPoint; + + topLeftPoint = plane->translate( QPointF( rad, 0 ) ); + topLeftPoint.setX( plane->translate( QPointF( rad, 90 / plane->angleUnit() ) ).x() ); + bottomRightPoint = plane->translate( QPointF( rad, 180 / plane->angleUnit() ) ); + bottomRightPoint.setX( plane->translate( QPointF( rad, 270 / plane->angleUnit() ) ).x() ); + + rect.setTopLeft( topLeftPoint + context->rectangle().topLeft() ); + rect.setBottomRight( bottomRightPoint + context->rectangle().topLeft() ); + + context->painter()->drawEllipse( rect ); + } + plane->setStartPosition( startPos ); + } +} diff --git a/libkdchart/src/KDChartPolarGrid.h b/libkdchart/src/KDChartPolarGrid.h new file mode 100644 index 0000000..85ef57e --- /dev/null +++ b/libkdchart/src/KDChartPolarGrid.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPOLARGrid_H +#define KDCHARTPOLARGrid_H + +#include "KDChartPolarCoordinatePlane.h" +#include "KDChartAbstractGrid.h" + +namespace KDChart { + + class PaintContext; + class PolarCoordinatePlane; + + /** + * \internal + * + * \brief Class for the grid in a polar plane. + * + * The PolarGrid interface is used + * for calculating and for drawing + * the sagittal grid lines, and the circular grid lines + * of a polar coordinate plane. + */ + class PolarGrid : public AbstractGrid + { + public: + PolarGrid() : AbstractGrid(){} + virtual ~PolarGrid(){} + + virtual void drawGrid( PaintContext* context ); + + private: + virtual DataDimensionsList calculateGrid( + const DataDimensionsList& rawDataDimensions ) const; + }; + +} + +#endif diff --git a/libkdchart/src/KDChartPosition.cpp b/libkdchart/src/KDChartPosition.cpp new file mode 100644 index 0000000..03140d3 --- /dev/null +++ b/libkdchart/src/KDChartPosition.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +using namespace KDChart; + +namespace { +/** + * \internal + * Static strings, to be translated in printable() + */ +static const char * staticPositionNames[] = { + QT_TRANSLATE_NOOP("Position","Unknown Position"), + QT_TRANSLATE_NOOP("Position","Center"), + QT_TRANSLATE_NOOP("Position","NorthWest"), + QT_TRANSLATE_NOOP("Position","North"), + QT_TRANSLATE_NOOP("Position","NorthEast"), + QT_TRANSLATE_NOOP("Position","East"), + QT_TRANSLATE_NOOP("Position","SouthEast"), + QT_TRANSLATE_NOOP("Position","South"), + QT_TRANSLATE_NOOP("Position","SouthWest"), + QT_TRANSLATE_NOOP("Position","West"), + QT_TRANSLATE_NOOP("Position","Floating") +}; + + +/** + * \internal + * One value for unknown positions, and nine values for predefined positions. + */ +static Position staticPositionUnknown = Position( KDChartEnums::PositionUnknown ); +static Position staticPositionCenter = Position( KDChartEnums::PositionCenter ); +static Position staticPositionNorthWest = Position( KDChartEnums::PositionNorthWest ); +static Position staticPositionNorth = Position( KDChartEnums::PositionNorth ); +static Position staticPositionNorthEast = Position( KDChartEnums::PositionNorthEast ); +static Position staticPositionEast = Position( KDChartEnums::PositionEast ); +static Position staticPositionSouthEast = Position( KDChartEnums::PositionSouthEast ); +static Position staticPositionSouth = Position( KDChartEnums::PositionSouth ); +static Position staticPositionSouthWest = Position( KDChartEnums::PositionSouthWest ); +static Position staticPositionWest = Position( KDChartEnums::PositionWest ); +static Position staticPositionFloating = Position( KDChartEnums::PositionFloating ); + +static int maxPositionValue = 10; + +} // anon namespace + +const Position& Position::Unknown = staticPositionUnknown; +const Position& Position::Center = staticPositionCenter; +const Position& Position::NorthWest = staticPositionNorthWest; +const Position& Position::North = staticPositionNorth; +const Position& Position::NorthEast = staticPositionNorthEast; +const Position& Position::East = staticPositionEast; +const Position& Position::SouthEast = staticPositionSouthEast; +const Position& Position::South = staticPositionSouth; +const Position& Position::SouthWest = staticPositionSouthWest; +const Position& Position::West = staticPositionWest; +const Position& Position::Floating = staticPositionFloating; + + +/** + * Default constructor. Creates a new Position, defaulting it to Position::Unknown. + */ +Position::Position() + : m_value( KDChartEnums::PositionUnknown ) +{ + +} + +Position::Position( int value ) + : m_value( value ) +{ + assert( 0 <= value ); assert( value <= maxPositionValue ); +} + +/** + * Constructor. Creates a new Position, defaulting it to the respective value. + * + * Valid values ranging from zero (unknown value) to 10. + * If invalid value is passed, a Position::Unknown is created. + * + * \note Normally there is no need to call this constructor, but you would + * rather use one of the nine pre-defined, static values, e.g. like this: + * \verbatim + * const KDChart::Position myPosition = KDChart::Position::NorthEast; + * \endverbatim + */ +Position::Position( KDChartEnums::PositionValue value ) + : m_value( value ) +{ + +} + +/** + * Returns an integer value corresponding to this Position. + */ +KDChartEnums::PositionValue Position::value() const +{ + return static_cast( m_value ); +} + +bool Position::isUnknown() const +{ + return m_value == Position::Unknown.value(); +} + +bool Position::isWestSide() const +{ + return m_value == Position::SouthWest.value() || + m_value == Position::West.value() || + m_value == Position::NorthWest.value(); +} +bool Position::isNorthSide() const +{ + return m_value == Position::NorthWest.value() || + m_value == Position::North.value() || + m_value == Position::NorthEast.value(); +} +bool Position::isEastSide() const +{ + return m_value == Position::NorthEast.value() || + m_value == Position::East.value() || + m_value == Position::SouthEast.value(); +} +bool Position::isSouthSide() const +{ + return m_value == Position::SouthWest.value() || + m_value == Position::South.value() || + m_value == Position::SouthEast.value(); +} + +bool Position::isCorner() const +{ + return m_value == Position::NorthWest.value() || + m_value == Position::NorthEast.value() || + m_value == Position::SouthEast.value() || + m_value == Position::SouthWest.value(); +} +bool Position::isPole() const +{ + return m_value == Position::North.value() || + m_value == Position::South.value(); +} + +bool Position::isFloating() const +{ + return m_value == Position::Floating.value(); +} + +/** + * Returns a non-translated string in English language, corresponding to this Position. + */ +const char * Position::name() const +{ + return staticPositionNames[m_value]; +} + +/** + * Returns a translated string, corresponding to this Position. + */ +QString Position::printableName() const +{ + return tr(staticPositionNames[m_value]); +} + + +/** + * \brief Returns a list of all string, corresponding to + * the pre-defined positions. + * + * \param options if set to \c ExcludeCenter, the returned list + * does not contain the Center position. + */ +QList Position::names( Options options ) +{ + QList list; + const int start = ( options & IncludeCenter ) ? 1 : 2; + const int end = ( options & IncludeFloating ) ? maxPositionValue : maxPositionValue-1; + for( int i=start; i<=end; ++i) + list.append( staticPositionNames[i] ); + return list; +} + +/** + * \brief Returns a list of all translated string, corresponding to + * the pre-defined positions. + * + * \param options if set to \c ExcludeCenter, the returned list + * does not contain the Center position. + */ +QStringList Position::printableNames( Options options ) +{ + QStringList list; + const int start = ( options & IncludeCenter ) ? 1 : 2; + const int end = ( options & IncludeFloating ) ? maxPositionValue : maxPositionValue-1; + for( int i=start; i<=end; ++i) + list.append( Position(i).printableName() ); + return list; +} + +Position Position::fromName(const char * name) +{ + for( int i=1; i<=maxPositionValue; ++i) + if ( !qstricmp( name, staticPositionNames[i] ) ) + return Position(i); + return Position(0); +} + +Position Position::fromName( const QByteArray & name ) { + return fromName( name.data() ); +} + +bool Position::operator==( const Position& r ) const +{ + return ( value() == r.value() ); +} + + +bool Position::operator==( int value_ ) const +{ + return ( value() == value_ ); +} + + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::Position& p ) +{ + dbg << "KDChart::Position(" + << p.name() << ")"; + return dbg; +} +#endif /* QT_NO_DEBUG_STREAM */ diff --git a/libkdchart/src/KDChartPosition.h b/libkdchart/src/KDChartPosition.h new file mode 100644 index 0000000..e54b046 --- /dev/null +++ b/libkdchart/src/KDChartPosition.h @@ -0,0 +1,288 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTPOSITION_H +#define KDCHARTPOSITION_H + +#include +#include +#include +#include +#include "KDChartGlobal.h" +#include "KDChartEnums.h" + +class QStringList; +class QByteArray; +template class QList; + +namespace KDChart { + +/** + * \class Position KDChartPosition.h + * \brief Defines a position, using compass terminology. + * + * Using KDChartPosition you can specify one of nine + * pre-defined, logical points (see the \c static \c const getter + * methods below), in a similar way, as you would use a + * compass to navigate on a map. + * + * \note Often you will declare a \c Position together with the + * RelativePosition class, to specify a logical point, + * which then will be used to layout your chart at runtime, + * e.g. for specifying the location of a floating Legend box. + * + * For comparing a Position's value with a switch() statement, + * you can use numeric values defined in KDChartEnums, like this: +\verbatim +switch( yourPosition().value() ) { + case KDChartEnums::PositionNorthWest: + // your code ... + break; + case KDChartEnums::PositionNorth: + // your code ... + break; +} +\endverbatim + * \sa RelativePosition, KDChartEnums::PositionValue + */ +class KDCHART_EXPORT Position +{ + Q_DECLARE_TR_FUNCTIONS( Position ) + Position( int value ); +public: + Position(); + Position( KDChartEnums::PositionValue value ); // intentionally non-explicit + + KDChartEnums::PositionValue value() const; + + const char * name() const; + QString printableName() const; + + bool isUnknown() const; + + bool isWestSide() const; + bool isNorthSide() const; + bool isEastSide() const; + bool isSouthSide() const; + + bool isCorner() const; + bool isPole() const; + + bool isFloating() const; + + static const Position& Unknown; + static const Position& Center; + static const Position& NorthWest; + static const Position& North; + static const Position& NorthEast; + static const Position& East; + static const Position& SouthEast; + static const Position& South; + static const Position& SouthWest; + static const Position& West; + + static const Position& Floating; + + // boolean flags: 1, 2, 4, 8, ... + enum Option { + IncludeCenter = 0x1, + IncludeFloating = 0x2 }; + Q_DECLARE_FLAGS( Options, Option ) + + // Unfortunately the following typecast from int to Options is needed + // as the | operator is not defined yet, this will be done by + // the makro Q_DECLARE_OPERATORS_FOR_FLAGS( KDChart::Position::Options ) + // at the bottom of this file. + static QList names( Options options = Options(IncludeCenter | IncludeFloating) ); + static QStringList printableNames( Options options = Options(IncludeCenter | IncludeFloating) ); + + static Position fromName(const char * name); + static Position fromName(const QByteArray & name); + + bool operator==( const Position& ) const; + bool operator==( int ) const; + bool operator!=( const Position& ) const; + bool operator!=( int ) const; + +private: + int m_value; +}; // End of class Position + +inline bool Position::operator!=( const Position & other ) const { return !operator==( other ); } +inline bool Position::operator!=( int other ) const { return !operator==( other ); } + +/** + * @brief Stores the absolute target points of a Position + * \internal + */ +class KDCHART_EXPORT PositionPoints +{ + public: + PositionPoints(){} // all points get initialized with the default automatically + + PositionPoints( + QPointF center, + QPointF northWest, + QPointF north, + QPointF northEast, + QPointF east, + QPointF southEast, + QPointF south, + QPointF southWest, + QPointF west ) + : mPositionCenter( center ) + , mPositionNorthWest( northWest ) + , mPositionNorth( north ) + , mPositionNorthEast( northEast ) + , mPositionEast( east ) + , mPositionSouthEast( southEast ) + , mPositionSouth( south ) + , mPositionSouthWest( southWest ) + , mPositionWest( west ) + {} + PositionPoints( + const QPointF& onePointForAllPositions ) + : mPositionCenter( onePointForAllPositions ) + , mPositionNorthWest( onePointForAllPositions ) + , mPositionNorth( onePointForAllPositions ) + , mPositionNorthEast( onePointForAllPositions ) + , mPositionEast( onePointForAllPositions ) + , mPositionSouthEast( onePointForAllPositions ) + , mPositionSouth( onePointForAllPositions ) + , mPositionSouthWest( onePointForAllPositions ) + , mPositionWest( onePointForAllPositions ) + {} + PositionPoints( + const QRectF& rect ) + { + const QRectF r( rect.normalized() ); + mPositionCenter = r.center(); + mPositionNorthWest = r.topLeft(); + mPositionNorth = QPointF(r.center().x(), r.top()); + mPositionNorthEast = r.topRight(); + mPositionEast = QPointF(r.right(), r.center().y()); + mPositionSouthEast = r.bottomRight(); + mPositionSouth = QPointF(r.center().x(), r.bottom()); + mPositionSouthWest = r.bottomLeft(); + mPositionWest = QPointF(r.left(), r.center().y()); + } + PositionPoints( + QPointF northWest, + QPointF northEast, + QPointF southEast, + QPointF southWest ) + : mPositionCenter( (northWest + southEast) / 2.0 ) + , mPositionNorthWest( northWest ) + , mPositionNorth( (northWest + northEast) / 2.0 ) + , mPositionNorthEast( northEast ) + , mPositionEast( (northEast + southEast) / 2.0 ) + , mPositionSouthEast( southEast ) + , mPositionSouth( (southWest + southEast) / 2.0 ) + , mPositionSouthWest( southWest ) + , mPositionWest( (northWest + southWest) / 2.0 ) + {} + + void setDegrees( KDChartEnums::PositionValue pos, qreal degrees ) + { + mapOfDegrees[pos] = degrees; + } + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + const qreal degrees( KDChartEnums::PositionValue pos ) const +#else + qreal degrees( KDChartEnums::PositionValue pos ) const +#endif + { + if( mapOfDegrees.contains(pos) ) + return mapOfDegrees[pos]; + return 0.0; + } + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + const QPointF point( Position position ) const +#else + QPointF point( Position position ) const +#endif + { + //qDebug() << "point( " << position.name() << " )"; + if( position == Position::Center) + return mPositionCenter; + if( position == Position::NorthWest) + return mPositionNorthWest; + if( position == Position::North) + return mPositionNorth; + if( position == Position::NorthEast) + return mPositionNorthEast; + if( position == Position::East) + return mPositionEast; + if( position == Position::SouthEast) + return mPositionSouthEast; + if( position == Position::South) + return mPositionSouth; + if( position == Position::SouthWest) + return mPositionSouthWest; + if( position == Position::West) + return mPositionWest; + return mPositionUnknown; + } + + bool isNull() const + { + return + mPositionUnknown.isNull() && + mPositionCenter.isNull() && + mPositionNorthWest.isNull() && + mPositionNorth.isNull() && + mPositionNorthEast.isNull() && + mPositionEast.isNull() && + mPositionSouthEast.isNull() && + mPositionSouth.isNull() && + mPositionSouthWest.isNull() && + mPositionWest.isNull(); + } + + QPointF mPositionUnknown; + QPointF mPositionCenter; + QPointF mPositionNorthWest; + QPointF mPositionNorth; + QPointF mPositionNorthEast; + QPointF mPositionEast; + QPointF mPositionSouthEast; + QPointF mPositionSouth; + QPointF mPositionSouthWest; + QPointF mPositionWest; + QMap mapOfDegrees; + +}; // End of class PositionPoints + + +} + +Q_DECLARE_TYPEINFO( KDChart::Position, Q_MOVABLE_TYPE ); +Q_DECLARE_METATYPE( KDChart::Position ) +Q_DECLARE_OPERATORS_FOR_FLAGS( KDChart::Position::Options ) + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::Position& ); +#endif /* QT_NO_DEBUG_STREAM */ + +#endif // KDCHARTPOSITION_H diff --git a/libkdchart/src/KDChartPrintingParameters.cpp b/libkdchart/src/KDChartPrintingParameters.cpp new file mode 100644 index 0000000..c8cf7d6 --- /dev/null +++ b/libkdchart/src/KDChartPrintingParameters.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartPrintingParameters.h" + +using namespace KDChart; + +PrintingParameters::PrintingParameters() + : scaleFactor( 1.0 ) +{ +} + +PrintingParameters* PrintingParameters::instance() +{ + static PrintingParameters instance; + return &instance; +} + +void PrintingParameters::setScaleFactor( const qreal scaleFactor ) +{ + instance()->scaleFactor = scaleFactor; +} + +void PrintingParameters::resetScaleFactor() +{ + instance()->scaleFactor = 1.0; +} + +QPen PrintingParameters::scalePen( const QPen& pen ) +{ + if( instance()->scaleFactor == 1.0 ) + return pen; + + QPen resultPen = pen; + resultPen.setWidthF( resultPen.widthF() * instance()->scaleFactor ); + if( resultPen.widthF() == 0.0 ) + resultPen.setWidthF( instance()->scaleFactor ); + + return resultPen; +} diff --git a/libkdchart/src/KDChartPrintingParameters.h b/libkdchart/src/KDChartPrintingParameters.h new file mode 100644 index 0000000..dee6da1 --- /dev/null +++ b/libkdchart/src/KDChartPrintingParameters.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef PRINTINGPARAMETERS_H +#define PRINTINGPARAMETERS_H + +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +namespace KDChart { + /** + * PrintingParameters stores the scale factor which lines has to been scaled with when printing. + * It's essentially printer's logical DPI / widget's logical DPI + * \internal + */ + class PrintingParameters { + public: + static void setScaleFactor( const qreal scaleFactor ); + static void resetScaleFactor(); + static QPen scalePen( const QPen& pen ); + + private: + PrintingParameters(); + static PrintingParameters* instance(); + + qreal scaleFactor; + }; +} + +#endif diff --git a/libkdchart/src/KDChartRelativePosition.cpp b/libkdchart/src/KDChartRelativePosition.cpp new file mode 100644 index 0000000..66c70b8 --- /dev/null +++ b/libkdchart/src/KDChartRelativePosition.cpp @@ -0,0 +1,232 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartRelativePosition.h" + +#include "KDChartEnums.h" +#include "KDChartMeasure.h" +#include "KDChartPosition.h" +#include "KDChartAbstractArea.h" + +#include +#include + +#include + +using namespace KDChart; + +class RelativePosition::Private { + friend class ::KDChart::RelativePosition; +public: + Private(); + ~Private(); + +private: + QObject* area; + PositionPoints points; + Position position; + Qt::Alignment alignment; + Measure horizontalPadding; + Measure verticalPadding; + qreal rotation; +}; + + +RelativePosition::Private::Private() + : area( 0 ), + alignment( Qt::AlignCenter ), + rotation( 0 ) +{ + +} + +RelativePosition::Private::~Private() +{} + + + +RelativePosition::RelativePosition() + : _d( new Private ) +{ + +} + +RelativePosition::RelativePosition( const RelativePosition& r ) + : _d( new Private( *r._d ) ) +{ + +} + +RelativePosition & RelativePosition::operator=( const RelativePosition & other ) { + RelativePosition copy( other ); + copy.swap( *this ); + return *this; +} + +RelativePosition::~RelativePosition() +{ + delete _d; +} + +#define d d_func() + +void RelativePosition::setReferenceArea( QObject * area ) { + d->area = area; + if( area ) + setReferencePoints( PositionPoints() ); +} + +QObject * RelativePosition::referenceArea() const { + return d->area; +} + +void RelativePosition::setReferencePoints( const PositionPoints& points ){ + d->points = points; + if( !points.isNull() ) + setReferenceArea( 0 ); +} +const PositionPoints RelativePosition::referencePoints() const{ + return d->points; +} + +void RelativePosition::setReferencePosition( Position pos ) { + d->position = pos; +} + +void RelativePosition::resetReferencePosition() { + d->position = Position::Unknown; +} + +Position RelativePosition::referencePosition() const { + return d->position; +} + +void RelativePosition::setAlignment( Qt::Alignment align ) { + d->alignment = align; +} + +Qt::Alignment RelativePosition::alignment() const { + return d->alignment; +} + +void RelativePosition::setHorizontalPadding( const Measure & pad ) { + d->horizontalPadding = pad; +} + +Measure RelativePosition::horizontalPadding() const { + return d->horizontalPadding; +} + +void RelativePosition::setVerticalPadding( const Measure & pad ) { + d->verticalPadding = pad; +} + +Measure RelativePosition::verticalPadding() const { + return d->verticalPadding; +} + +void RelativePosition::setRotation( qreal rot ) { + d->rotation = rot; +} + +qreal RelativePosition::rotation() const { + return d->rotation; +} + + +const QPointF RelativePosition::referencePoint(qreal* polarDegrees) const +{ + bool useRect = (d->area != 0); + QRect rect; + if( useRect ){ + const QWidget* widget = dynamic_cast(d->area); + if( widget ){ + const QLayout * layout = widget->layout(); + rect = layout ? layout->geometry() : widget->geometry(); + }else{ + const AbstractArea* kdcArea = dynamic_cast(d->area); + if( kdcArea ) + rect = kdcArea->geometry(); + else + useRect = false; + } + } + QPointF pt; + if ( useRect ){ + pt = PositionPoints( rect ).point( d->position ); + if( polarDegrees ) + *polarDegrees = 0.0; + }else{ + pt = d->points.point( d->position ); + if( polarDegrees ) + *polarDegrees = d->points.degrees( d->position.value() ); + } + return pt; +} + + +const QPointF RelativePosition::calculatedPoint( const QSizeF& autoSize ) const +{ + const qreal dx = horizontalPadding().calculatedValue( autoSize, KDChartEnums::MeasureOrientationHorizontal ); + const qreal dy = verticalPadding() .calculatedValue( autoSize, KDChartEnums::MeasureOrientationVertical ); + qreal polarDegrees; + QPointF pt( referencePoint( &polarDegrees ) ); + if( polarDegrees == 0.0 ){ + pt += QPointF(dx, dy); + }else{ + const qreal rad = DEGTORAD( polarDegrees); + const qreal sinDeg = sin(rad); + const qreal cosDeg = cos(rad); + pt.setX( pt.x() + dx * cosDeg + dy * sinDeg ); + pt.setY( pt.y() - dx * sinDeg + dy * cosDeg ); + } + return pt; +} + + +bool RelativePosition::operator==( const RelativePosition& r ) const +{ + return d->area == r.referenceArea() && + d->position == r.referencePosition() && + d->alignment == r.alignment() && + d->horizontalPadding == r.horizontalPadding() && + d->verticalPadding == r.verticalPadding() && + d->rotation == r.rotation() ; +} + +#undef d + + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::RelativePosition& rp) +{ + dbg << "KDChart::RelativePosition(" + << "referencearea="< +#include +#include +#include +#include +#include "KDChartGlobal.h" + +namespace KDChart { + + class Position; + class PositionPoints; + class Measure; + +/** + \class RelativePosition KDChartRelativePosition.h + \brief Defines relative position information: reference area, position + in this area, horizontal / vertical padding, and rotating. + + Using RelativePosition you can specify the relative parts + of some position information, and you can specify the absolute parts: + the reference area, and the position in this area. + + \note To get an absolute position, you have three options: + \li either you declare both, the relative and the absolute parts, + using setReferenceArea for the later, + \li or you specify a set of points, using setReferencePoints, + \li or you refrein from using either, but leave it to KD Chart to find + a matching reference area for you. + */ +class KDCHART_EXPORT RelativePosition +{ +public: + RelativePosition(); + RelativePosition( const RelativePosition& ); + + RelativePosition & operator=( const RelativePosition & other ); + + ~RelativePosition(); + + /** + * \brief Specifies the reference area to be used to find the anchor point. + * + * The reference area's type can be either QWidget, or be derived from KDChart::AbstractArea. + * + * \note Usage of reference area and reference points works mutually exclusively: + * Only one setting can be valid, so any former specification of reference points is reset + * when you call setReferenceArea. + * + * Also note: In a few cases KD Chart will ignore your area (or points, resp.) settings! + * Relative positioning of data value texts is an example: For these + * the reference area is the respective data area taking precendence over your settings. + * + * \sa setReferencePosition, setAlignment, setHorizontalPadding, setVerticalPadding + */ + void setReferenceArea( QObject* area ); + QObject* referenceArea() const; + + /** + * \brief Specifies a set of points from which the anchor point will be selected. + * + * \note Usage of reference area and reference points works mutually exclusively: + * Only one setting can be valid, so any former specification of reference area is reset + * when you call setReferencePoints. + * + * Also note: In a few cases KD Chart will ignore your points (or area, resp.) settings! + * Relative positioning of data value texts is an example: For these + * the reference area is the respective data area taking precendence over your settings. + * + * \sa setReferenceArea, setReferencePosition, setAlignment, setHorizontalPadding, setVerticalPadding + */ + void setReferencePoints( const PositionPoints& points ); + const PositionPoints referencePoints() const; + + /** + * \brief Specifies the position of the anchor point. + * + * The anchor point of a RelativePosition may be one of the pre-defined + * points of it's reference area - for details see KDChart::Position. + * + * \sa resetReferencePosition, setReferenceArea, setAlignment, setHorizontalPadding, setVerticalPadding, KDChart::Position + */ + void setReferencePosition( Position position ); + + /** + * \brief Resets the position of the anchor point to the built-in default. + * + * If the anchor point of a RelativePosition is reset (or never changed from the + * default setting, resp.) KD Chart will choose an appropriate Position at run-time. + * + * e.g. BarDiagrams will use Position::NorthWest / Position::SouthEast for positive / negative values. + * + * \sa setReferencePosition, setReferenceArea, setAlignment, setHorizontalPadding, setVerticalPadding, KDChart::Position + */ + void resetReferencePosition(); + Position referencePosition() const; + + /** + * Specifies the location of the content, that is to be positioned by this RelativePosition. + * + * Aligning is applied, after horiz./vert. padding was retrieved to calculate the real + * reference point, so aligning is seen as relative to that point. + * + * \note When printing data value texts at a centered point you might want to call + * setAlignment( Qt::AlignCenter ) and also set the horizontal/vertical padding to Zero + * to have your texts centered more precisely. + * + * \sa setReferencePosition, setReferenceArea, setHorizontalPadding, setVerticalPadding + */ + void setAlignment( Qt::Alignment flags ); + Qt::Alignment alignment() const; + + /** + * Specifies the horizontal width of the gap between the anchor point and the content, + * that is to be positioned by this RelativePosition. + * + * \note When printing data value texts this Measure is used to find the alignment + * point to align the text to, then alignment() is looked at to determine the way how + * the text is to be aligned to that point. The font height is used as reference size + * for both, horizontal and vertical padding, if the respective padding's Measure is + * using automatic reference area detection. + * + * \sa setVerticalPadding, setReferencePosition, setReferenceArea + */ + void setHorizontalPadding( const Measure& padding ); + Measure horizontalPadding() const; + + /** + * Specifies the vertical width of the gap between the anchor point and the content, + * that is to be positioned by this RelativePosition. + * + * \note When printing data value texts this Measure is used to find the alignment + * point to align the text to, then alignment() is looked at to determine the way how + * the text is to be aligned to that point. The font height is used as reference size + * for both, horizontal and vertical padding, if the respective padding's Measure is + * using automatic reference area detection. + * + * \sa setHorizontalPadding, setReferencePosition, setReferenceArea + */ + void setVerticalPadding( const Measure& padding ); + Measure verticalPadding() const; + + void setRotation( qreal rot ); + qreal rotation() const; + + /** + * \brief Return the reference point, according to the reference area/position, but ignoring horiz/vert padding. + * + * This method is called at drawing time. + * The returned point is used to test if the label of a data value is to be printed: labels + * are printed only, if their reference points are either inside or touching the coordinate plane. + * + * If polarDegrees is set, the degree information will be returned that was stored for the + * respective point. This is used by the PieDiagram class to determin how vertical/horizontal + * padding settings should affect the position of the data value texts' reference points. + * + * \sa calculatedPoint, setReferenceArea, setReferencePosition, setHorizontalPadding, setVerticalPadding + */ + const QPointF referencePoint(qreal* polarDegrees=0) const; + + /** + * \brief Calculate a point, according to the reference area/position and horiz/vert padding. + * + * This method is called at drawing time: The returned point is used as anchor point. + * Note that calculatedPoint ignores the alignment setting, it just returns the point, + * so the calling code needs to take alignment into account explicitly. + * + * \sa referencePoint, setReferenceArea, setReferencePosition, setHorizontalPadding, setVerticalPadding + */ + const QPointF calculatedPoint( const QSizeF& autoSize ) const; + + bool operator==( const RelativePosition& ) const; + bool operator!=( const RelativePosition & other ) const; + +private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( RelativePosition ) +}; + +inline bool RelativePosition::operator!=( const RelativePosition & other ) const { return !operator==( other ); } +} + +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::RelativePosition ) + +Q_DECLARE_TYPEINFO( KDChart::RelativePosition, Q_MOVABLE_TYPE ); +Q_DECLARE_METATYPE( KDChart::RelativePosition ) + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::RelativePosition& ); +#endif /* QT_NO_DEBUG_STREAM */ + + +#endif // KDCHARTRELATIVEPOSITION_H diff --git a/libkdchart/src/KDChartRingDiagram.cpp b/libkdchart/src/KDChartRingDiagram.cpp new file mode 100644 index 0000000..b64d172 --- /dev/null +++ b/libkdchart/src/KDChartRingDiagram.cpp @@ -0,0 +1,522 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartRingDiagram.h" +#include "KDChartRingDiagram_p.h" + +#include "KDChartAttributesModel.h" +#include "KDChartPaintContext.h" +#include "KDChartPainterSaver_p.h" +#include "KDChartPieAttributes.h" +#include "KDChartDataValueAttributes.h" + +#include + +#include + +using namespace KDChart; + +RingDiagram::Private::Private() + : relativeThickness( false ) + , expandWhenExploded( false ) +{ +} + +RingDiagram::Private::~Private() {} + +#define d d_func() + +RingDiagram::RingDiagram( QWidget* parent, PolarCoordinatePlane* plane ) : + AbstractPieDiagram( new Private(), parent, plane ) +{ + init(); +} + +RingDiagram::~RingDiagram() +{ +} + +void RingDiagram::init() +{ +} + +/** + * Creates an exact copy of this diagram. + */ +RingDiagram * RingDiagram::clone() const +{ + return new RingDiagram( new Private( *d ) ); +} + +bool RingDiagram::compare( const RingDiagram* other )const +{ + if( other == this ) return true; + if( ! other ){ + return false; + } + /* + qDebug() <<"\n RingDiagram::compare():"; + // compare own properties + qDebug() << (type() == other->type()); + qDebug() << (relativeThickness() == other->relativeThickness()); + qDebug() << (expandWhenExploded() == other->expandWhenExploded()); + */ + return // compare the base class + ( static_cast(this)->compare( other ) ) && + // compare own properties + (relativeThickness() == other->relativeThickness()) && + (expandWhenExploded() == other->expandWhenExploded()); +} + +void RingDiagram::setRelativeThickness( bool relativeThickness ) +{ + d->relativeThickness = relativeThickness; +} + +bool RingDiagram::relativeThickness() const +{ + return d->relativeThickness; +} + +void RingDiagram::setExpandWhenExploded( bool expand ) +{ + d->expandWhenExploded = expand; +} + +bool RingDiagram::expandWhenExploded() const +{ + return d->expandWhenExploded; +} + +const QPair RingDiagram::calculateDataBoundaries () const +{ + if ( !checkInvariants( true ) ) return QPair( QPointF( 0, 0 ), QPointF( 0, 0 ) ); + + const PieAttributes attrs( pieAttributes( model()->index( 0, 0, rootIndex() ) ) ); + + QPointF bottomLeft ( QPointF( 0, 0 ) ); + QPointF topRight; + // If we explode, we need extra space for the pie slice that has + // the largest explosion distance. + if ( attrs.explode() ) { + const int rCount = rowCount(); + const int colCount = columnCount(); + qreal maxExplode = 0.0; + for( int i = 0; i < rCount; ++i ){ + qreal maxExplodeInThisRow = 0.0; + for( int j = 0; j < colCount; ++j ){ + const PieAttributes columnAttrs( pieAttributes( model()->index( i, j, rootIndex() ) ) ); + //qDebug() << columnAttrs.explodeFactor(); + maxExplodeInThisRow = qMax( maxExplodeInThisRow, columnAttrs.explodeFactor() ); + } + maxExplode += maxExplodeInThisRow; + + // FIXME: What if explode factor of inner ring is > 1.0 ? + if ( !d->expandWhenExploded ) + break; + } + // explode factor is relative to width (outer r - inner r) of one ring + maxExplode /= ( rCount + 1); + topRight = QPointF( 1.0+maxExplode, 1.0+maxExplode ); + }else{ + topRight = QPointF( 1.0, 1.0 ); + } + return QPair ( bottomLeft, topRight ); +} + +void RingDiagram::paintEvent( QPaintEvent* ) +{ + QPainter painter ( viewport() ); + PaintContext ctx; + ctx.setPainter ( &painter ); + ctx.setRectangle( QRectF ( 0, 0, width(), height() ) ); + paint ( &ctx ); +} + +void RingDiagram::resizeEvent( QResizeEvent* ) +{ +} + +static QRectF buildReferenceRect( const PolarCoordinatePlane* plane ) +{ + QRectF contentsRect; + QPointF referencePointAtTop = plane->translate( QPointF( 1, 0 ) ); + QPointF temp = plane->translate( QPointF( 0, 0 ) ) - referencePointAtTop; + const double offset = temp.y(); + referencePointAtTop.setX( referencePointAtTop.x() - offset ); + contentsRect.setTopLeft( referencePointAtTop ); + contentsRect.setBottomRight( referencePointAtTop + QPointF( 2*offset, 2*offset) ); + return contentsRect; +} +/* + +*/ + + +void RingDiagram::paint( PaintContext* ctx ) +{ + // note: Not having any data model assigned is no bug + // but we can not draw a diagram then either. + if ( !checkInvariants(true) ) + return; + + const PieAttributes attrs( pieAttributes() ); + + const int rCount = rowCount(); + const int colCount = columnCount(); + + QRectF contentsRect( buildReferenceRect( polarCoordinatePlane() ) ); + contentsRect = ctx->rectangle(); + if( contentsRect.isEmpty() ) + return; + + DataValueTextInfoList list; + + d->startAngles = QVector< QVector >( rCount, QVector( colCount ) ); + d->angleLens = QVector< QVector >( rCount, QVector( colCount ) ); + + // compute position + d->size = qMin( contentsRect.width(), contentsRect.height() ); // initial size + + // if the pies explode, we need to give them additional space => + // make the basic size smaller + qreal totalOffset = 0.0; + for( int i = 0; i < rCount; ++i ){ + qreal maxOffsetInThisRow = 0.0; + for( int j = 0; j < colCount; ++j ){ + const PieAttributes cellAttrs( pieAttributes( model()->index( i, j, rootIndex() ) ) ); + //qDebug() << cellAttrs.explodeFactor(); + const qreal explode = cellAttrs.explode() ? cellAttrs.explodeFactor() : 0.0; + maxOffsetInThisRow = qMax( maxOffsetInThisRow, cellAttrs.gapFactor( false ) + explode ); + } + if ( !d->expandWhenExploded ) + maxOffsetInThisRow -= (qreal)i; + if ( maxOffsetInThisRow > 0.0 ) + totalOffset += maxOffsetInThisRow; + + // FIXME: What if explode factor of inner ring is > 1.0 ? + //if ( !d->expandWhenExploded ) + // break; + } + + // explode factor is relative to width (outer r - inner r) of one ring + if ( rCount > 0 ) + totalOffset /= ( rCount + 1 ); + d->size /= ( 1.0 + totalOffset ); + + + qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 ); + qreal y = ( contentsRect.height() == d->size ) ? 0.0 : ( ( contentsRect.height() - d->size ) / 2.0 ); + d->position = QRectF( x, y, d->size, d->size ); + d->position.translate( contentsRect.left(), contentsRect.top() ); + + const PolarCoordinatePlane * plane = polarCoordinatePlane(); + + bool atLeastOneValue = false; // guard against completely empty tables + QVariant vValY; + + d->clearListOfAlreadyDrawnDataValueTexts(); + for ( int iRow = 0; iRow < rCount; ++iRow ) { + const qreal sum = valueTotals( iRow ); + if( sum == 0.0 ) //nothing to draw + continue; + qreal currentValue = plane ? plane->startPosition() : 0.0; + const qreal sectorsPerValue = 360.0 / sum; + + for ( int iColumn = 0; iColumn < colCount; ++iColumn ) { + // is there anything at all at this column? + bool bOK; + const double cellValue = qAbs( model()->data( model()->index( iRow, iColumn, rootIndex() ) ) + .toDouble( &bOK ) ); + + if( bOK ){ + d->startAngles[ iRow ][ iColumn ] = currentValue; + d->angleLens[ iRow ][ iColumn ] = cellValue * sectorsPerValue; + atLeastOneValue = true; + } else { // mark as non-existent + d->angleLens[ iRow ][ iColumn ] = 0.0; + if ( iColumn > 0.0 ) + d->startAngles[ iRow ][ iColumn ] = d->startAngles[ iRow ][ iColumn - 1 ]; + else + d->startAngles[ iRow ][ iColumn ] = currentValue; + } + //qDebug() << "d->startAngles["<angleLens["<angleLens[ iColumn ] + // << " = " << d->startAngles[ iColumn ]+d->angleLens[ iColumn ]; + + currentValue = d->startAngles[ iRow ][ iColumn ] + d->angleLens[ iRow ][ iColumn ]; + + drawOnePie( ctx->painter(), iRow, iColumn, granularity() ); + } + } +} + +#if defined ( Q_WS_WIN) +#define trunc(x) ((int)(x)) +#endif + +/** + Internal method that draws one of the pies in a pie chart. + + \param painter the QPainter to draw in + \param dataset the dataset to draw the pie for + \param pie the pie to draw + */ +void RingDiagram::drawOnePie( QPainter* painter, + uint dataset, uint pie, + qreal granularity ) +{ + // Is there anything to draw at all? + const qreal angleLen = d->angleLens[ dataset ][ pie ]; + if ( angleLen ) { + const QModelIndex index( model()->index( dataset, pie, rootIndex() ) ); + const PieAttributes attrs( pieAttributes( index ) ); + + drawPieSurface( painter, dataset, pie, granularity ); + } +} + +void RingDiagram::resize( const QSizeF& ) +{ +} + +/** + Internal method that draws the surface of one of the pies in a pie chart. + + \param painter the QPainter to draw in + \param dataset the dataset to draw the pie for + \param pie the pie to draw + */ +void RingDiagram::drawPieSurface( QPainter* painter, + uint dataset, uint pie, + qreal granularity ) +{ + // Is there anything to draw at all? + qreal angleLen = d->angleLens[ dataset ][ pie ]; + if ( angleLen ) { + qreal startAngle = d->startAngles[ dataset ][ pie ]; + + QModelIndex index( model()->index( dataset, pie, rootIndex() ) ); + const PieAttributes attrs( pieAttributes( index ) ); + + const int rCount = rowCount(); + const int colCount = columnCount(); + + int iPoint = 0; + + QRectF drawPosition = d->position;//piePosition( dataset, pie ); + + painter->setRenderHint ( QPainter::Antialiasing ); + painter->setBrush( brush( index ) ); + painter->setPen( pen( index ) ); +// painter->setPen( pen ); + //painter->setPen( Qt::red ); + if ( angleLen == 360 ) { + // full circle, avoid nasty line in the middle + // FIXME: Draw a complete ring here + //painter->drawEllipse( drawPosition ); + } else { + bool perfectMatch = false; + + qreal circularGap = 0.0; + + if ( attrs.gapFactor( true ) > 0.0 ) + { + // FIXME: Measure in degrees! + circularGap = attrs.gapFactor( true ); + //qDebug() << "gapFactor=" << attrs.gapFactor( false ); + } + + QPolygonF poly; + + qreal degree = 0; + + qreal actualStartAngle = startAngle + circularGap; + qreal actualAngleLen = angleLen - 2 * circularGap; + + qreal totalRadialExplode = 0.0; + qreal maxRadialExplode = 0.0; + + qreal totalRadialGap = 0.0; + qreal maxRadialGap = 0.0; + for( uint i = rCount - 1; i > dataset; --i ){ + qreal maxRadialExplodeInThisRow = 0.0; + qreal maxRadialGapInThisRow = 0.0; + for( int j = 0; j < colCount; ++j ){ + const PieAttributes cellAttrs( pieAttributes( model()->index( i, j, rootIndex() ) ) ); + //qDebug() << cellAttrs.explodeFactor(); + if ( d->expandWhenExploded ) + maxRadialGapInThisRow = qMax( maxRadialGapInThisRow, cellAttrs.gapFactor( false ) ); + if ( !cellAttrs.explode() ) + continue; + // Don't use a gap for the very inner circle + if ( d->expandWhenExploded ) + maxRadialExplodeInThisRow = qMax( maxRadialExplodeInThisRow, cellAttrs.explodeFactor() ); + } + maxRadialExplode += maxRadialExplodeInThisRow; + maxRadialGap += maxRadialGapInThisRow; + + // FIXME: What if explode factor of inner ring is > 1.0 ? + //if ( !d->expandWhenExploded ) + // break; + } + + totalRadialGap = maxRadialGap + attrs.gapFactor( false ); + totalRadialExplode = attrs.explode() ? maxRadialExplode + attrs.explodeFactor() : maxRadialExplode; + + while ( degree <= actualAngleLen ) { + const QPointF p = pointOnCircle( drawPosition, dataset, pie, false, actualStartAngle + degree, totalRadialGap, totalRadialExplode ); + poly.append( p ); + degree += granularity; + iPoint++; + } + if( ! perfectMatch ){ + poly.append( pointOnCircle( drawPosition, dataset, pie, false, actualStartAngle + actualAngleLen, totalRadialGap, totalRadialExplode ) ); + iPoint++; + } + + // The center point of the inner brink + const QPointF innerCenterPoint( poly[ int(iPoint / 2) ] ); + + actualStartAngle = startAngle + circularGap; + actualAngleLen = angleLen - 2 * circularGap; + + degree = actualAngleLen; + + const int lastInnerBrinkPoint = iPoint; + while ( degree >= 0 ){ + poly.append( pointOnCircle( drawPosition, dataset, pie, true, actualStartAngle + degree, totalRadialGap, totalRadialExplode ) ); + perfectMatch = (degree == 0); + degree -= granularity; + iPoint++; + } + // if necessary add one more point to fill the last small gap + if( ! perfectMatch ){ + poly.append( pointOnCircle( drawPosition, dataset, pie, true, actualStartAngle, totalRadialGap, totalRadialExplode ) ); + iPoint++; + } + + // The center point of the outer brink + const QPointF outerCenterPoint( poly[ lastInnerBrinkPoint + int((iPoint - lastInnerBrinkPoint) / 2) ] ); + //qDebug() << poly; + //find the value and paint it + //fix value position + const qreal sum = valueTotals( dataset ); + painter->drawPolygon( poly ); + + const QPointF centerPoint = (innerCenterPoint + outerCenterPoint) / 2.0; + + paintDataValueText( painter, index, centerPoint, angleLen*sum / 360 ); + + } + } +} + + +/** + * Auxiliary method returning a point to a given boundary + * rectangle of the enclosed ellipse and an angle. + */ +QPointF RingDiagram::pointOnCircle( const QRectF& rect, int dataset, int pie, bool outer, qreal angle, qreal totalGapFactor, qreal totalExplodeFactor ) +{ + qreal angleLen = d->angleLens[ dataset ][ pie ]; + qreal startAngle = d->startAngles[ dataset ][ pie ]; + QModelIndex index( model()->index( dataset, pie, rootIndex() ) ); + const PieAttributes attrs( pieAttributes( index ) ); + + const int rCount = rowCount(); + + //const qreal gapFactor = attrs.gapFactor( false ); + + //qDebug() << "##" << attrs.explode(); + //if ( attrs.explodeFactor() != 0.0 ) + // qDebug() << attrs.explodeFactor(); + + + qreal level = outer ? (rCount - dataset - 1) + 2 : (rCount - dataset - 1) + 1; + + + //maxExplode /= rCount; + + //qDebug() << "dataset=" << dataset << "maxExplode=" << maxExplode; + + //level += maxExplode; + + const qreal offsetX = rCount > 0 ? level * rect.width() / ( ( rCount + 1 ) * 2 ) : 0.0; + const qreal offsetY = rCount > 0 ? level * rect.height() / ( ( rCount + 1 ) * 2 ): 0.0; + const qreal centerOffsetX = rCount > 0 ? totalExplodeFactor * rect.width() / ( ( rCount + 1 ) * 2 ) : 0.0; + const qreal centerOffsetY = rCount > 0 ? totalExplodeFactor * rect.height() / ( ( rCount + 1 ) * 2 ): 0.0; + const qreal gapOffsetX = rCount > 0 ? totalGapFactor * rect.width() / ( ( rCount + 1 ) * 2 ) : 0.0; + const qreal gapOffsetY = rCount > 0 ? totalGapFactor * rect.height() / ( ( rCount + 1 ) * 2 ): 0.0; + + qreal explodeAngleRad = DEGTORAD( angle ); + qreal cosAngle = cos( explodeAngleRad ); + qreal sinAngle = -sin( explodeAngleRad ); + qreal explodeAngleCenterRad = DEGTORAD( startAngle + angleLen / 2.0 ); + qreal cosAngleCenter = cos( explodeAngleCenterRad ); + qreal sinAngleCenter = -sin( explodeAngleCenterRad ); + return QPointF( ( offsetX + gapOffsetX ) * cosAngle + centerOffsetX * cosAngleCenter + rect.center().x(), + ( offsetY + gapOffsetY ) * sinAngle + centerOffsetY * sinAngleCenter + rect.center().y() ); +} + +/*virtual*/ +double RingDiagram::valueTotals() const +{ + const int rCount = rowCount(); + const int colCount = columnCount(); + double total = 0.0; + for ( int i = 0; i < rCount; ++i ) { + for ( int j = 0; j < colCount; ++j ) { + total += qAbs(model()->data( model()->index( 0, j, rootIndex() ) ).toDouble()); + //qDebug() << model()->data( model()->index( 0, j, rootIndex() ) ).toDouble(); + } + } + return total; +} + +double RingDiagram::valueTotals( int dataset ) const +{ + const int colCount = columnCount(); + double total = 0.0; + for ( int j = 0; j < colCount; ++j ) { + total += qAbs(model()->data( model()->index( dataset, j, rootIndex() ) ).toDouble()); + //qDebug() << model()->data( model()->index( 0, j, rootIndex() ) ).toDouble(); + } + return total; +} + +/*virtual*/ +double RingDiagram::numberOfValuesPerDataset() const +{ + return model() ? model()->columnCount( rootIndex() ) : 0.0; +} + +double RingDiagram::numberOfDatasets() const +{ + return model() ? model()->rowCount( rootIndex() ) : 0.0; +} + +/*virtual*/ +double RingDiagram::numberOfGridRings() const +{ + return 1; +} diff --git a/libkdchart/src/KDChartRingDiagram.h b/libkdchart/src/KDChartRingDiagram.h new file mode 100644 index 0000000..90e6970 --- /dev/null +++ b/libkdchart/src/KDChartRingDiagram.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTRINGDIAGRAM_H +#define KDCHARTRINGDIAGRAM_H + +#include "KDChartAbstractPieDiagram.h" + +namespace KDChart { + +/** + * @brief RingDiagram defines a common ring diagram + */ +class KDCHART_EXPORT RingDiagram : public AbstractPieDiagram +{ + Q_OBJECT + + Q_DISABLE_COPY( RingDiagram ) + KDCHART_DECLARE_DERIVED_DIAGRAM( RingDiagram, PolarCoordinatePlane ) + +public: + explicit RingDiagram( + QWidget* parent = 0, PolarCoordinatePlane* plane = 0 ); + virtual ~RingDiagram(); + +protected: + // Implement AbstractDiagram + /** \reimpl */ + virtual void paint ( PaintContext* paintContext ); +public: + /** \reimpl */ + virtual void resize ( const QSizeF& area ); + + // Implement AbstractPolarDiagram + /** \reimpl */ + virtual double valueTotals () const; + /** \reimpl */ + virtual double numberOfValuesPerDataset() const; + virtual double numberOfDatasets() const; + /** \reimpl */ + virtual double numberOfGridRings() const; + + double valueTotals( int dataset ) const; + + virtual RingDiagram * clone() const; + + /** + * Returns true if both diagrams have the same settings. + */ + bool compare( const RingDiagram* other ) const; + + void setRelativeThickness( bool relativeThickness ); + bool relativeThickness() const; + + virtual void setExpandWhenExploded( bool expand ); + virtual bool expandWhenExploded() const; + +protected: + /** \reimpl */ + virtual const QPair calculateDataBoundaries() const; + void paintEvent( QPaintEvent* ); + void resizeEvent( QResizeEvent* ); + +private: + //QRectF piePosition( uint dataset, uint pie ) const; + void drawOnePie( QPainter* painter, + uint dataset, uint pie, + qreal granularity ); + void drawPieSurface( QPainter* painter, + uint dataset, uint pie, + qreal granularity ); + QPointF pointOnCircle( const QRectF& rect, int dataset, int pie, bool outer, qreal angle, qreal totalGapFactor, qreal totalExplodeFactor ); + //qreal radiusOf( const QRectF& rect, int dataset, int pie, bool outer ); +}; // End of class RingDiagram + +} + +#endif // KDCHARTRINGDIAGRAM_H diff --git a/libkdchart/src/KDChartRingDiagram_p.h b/libkdchart/src/KDChartRingDiagram_p.h new file mode 100644 index 0000000..fc9aa98 --- /dev/null +++ b/libkdchart/src/KDChartRingDiagram_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTRINGDIAGRAM_P_H +#define KDCHARTRINGDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractPieDiagram_p.h" + +#include + + +namespace KDChart { + +/** + * \internal + */ +class RingDiagram::Private : public AbstractPieDiagram::Private +{ + friend class RingDiagram; +public: + Private(); + ~Private(); + + Private( const Private& rhs ) : + AbstractPieDiagram::Private( rhs ) + { + relativeThickness = rhs.relativeThickness; + expandWhenExploded = rhs.expandWhenExploded; + } + +protected: + // this information needed temporarily at drawing time + QVector< QVector < qreal > > startAngles; + QVector< QVector < qreal > > angleLens; + QRectF position; + qreal size; + bool relativeThickness; + bool expandWhenExploded; + // polygons associated to their 3d depth + QMap polygonsToRender; +}; + +KDCHART_IMPL_DERIVED_DIAGRAM( RingDiagram, AbstractPieDiagram, PolarCoordinatePlane ) + +} + +#endif /* KDCHARTRINGDIAGRAM_P_H */ diff --git a/libkdchart/src/KDChartRulerAttributes.cpp b/libkdchart/src/KDChartRulerAttributes.cpp new file mode 100644 index 0000000..c9851db --- /dev/null +++ b/libkdchart/src/KDChartRulerAttributes.cpp @@ -0,0 +1,265 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartRulerAttributes.h" + +#include + +#include +#include + +#include + +#define d d_func() + +using namespace KDChart; + +class RulerAttributes::Private +{ + friend class RulerAttributes; +public: + Private(); +private: + QPen tickMarkPen; + QPen majorTickMarkPen; + QPen minorTickMarkPen; + + bool majorTickMarkPenIsSet; + bool minorTickMarkPenIsSet; + + bool showMajorTickMarks; + bool showMinorTickMarks; + + bool showRulerLine; + + int labelMargin; + + RulerAttributes::TickMarkerPensMap customTickMarkPens; +}; + +RulerAttributes::Private::Private() + : tickMarkPen( QColor( 0x00, 0x00, 0x00 ) ) + , majorTickMarkPen( QColor( 0x00, 0x00, 0x00 ) ) + , minorTickMarkPen( QColor( 0x00, 0x00, 0x00 ) ) + , showRulerLine( false ) +{ + tickMarkPen.setCapStyle( Qt::FlatCap ); + majorTickMarkPen.setCapStyle( Qt::FlatCap ); + minorTickMarkPen.setCapStyle( Qt::FlatCap ); + + majorTickMarkPenIsSet = false; + minorTickMarkPenIsSet = false; + + showMajorTickMarks = true; + showMinorTickMarks = true; + + labelMargin = -1; +} + +RulerAttributes::RulerAttributes() + : _d( new Private() ) +{ + // this bloc left empty intentionally +} + +RulerAttributes::RulerAttributes( const RulerAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +void RulerAttributes::setTickMarkPen( const QPen& pen ) +{ + d->tickMarkPen = pen; +} + +QPen RulerAttributes::tickMarkPen() const +{ + return d->tickMarkPen; +} + +void RulerAttributes::setMajorTickMarkPen( const QPen& pen ) +{ + d->majorTickMarkPen = pen; + d->majorTickMarkPenIsSet = true; +} + +bool RulerAttributes::majorTickMarkPenIsSet() const +{ + return d->majorTickMarkPenIsSet; +} + +QPen RulerAttributes::majorTickMarkPen() const +{ + return d->majorTickMarkPenIsSet ? d->majorTickMarkPen : d->tickMarkPen; +} + +void RulerAttributes::setMinorTickMarkPen( const QPen& pen ) +{ + d->minorTickMarkPen = pen; + d->minorTickMarkPenIsSet = true; +} + +bool RulerAttributes::minorTickMarkPenIsSet() const +{ + return d->minorTickMarkPenIsSet; +} + +QPen RulerAttributes::minorTickMarkPen() const +{ + return d->minorTickMarkPenIsSet ? d->minorTickMarkPen : d->tickMarkPen; +} + +void RulerAttributes::setTickMarkPen( qreal value, const QPen& pen ) +{ + if ( !d->customTickMarkPens.contains( value ) ) + d->customTickMarkPens.insert( value, pen ); +} + +QPen RulerAttributes::tickMarkPen( qreal value ) const +{ + QMapIterator it( d->customTickMarkPens ); + while( it.hasNext() ) { + it.next(); + if ( qAbs( value - it.key() ) < std::numeric_limits< float >::epsilon() ) + return it.value(); + } + return d->tickMarkPen; +} + +RulerAttributes::TickMarkerPensMap RulerAttributes::tickMarkPens() const +{ + return d->customTickMarkPens; +} + +bool RulerAttributes::hasTickMarkPenAt( qreal value ) const +{ + QMapIterator it( d->customTickMarkPens ); + while( it.hasNext() ) { + it.next(); + if ( qAbs( value - it.key() ) < std::numeric_limits< float >::epsilon() ) + return true; + } + return false; +} + +void RulerAttributes::setTickMarkColor( const QColor& color ) +{ + d->tickMarkPen.setColor( color ); +} + +QColor RulerAttributes::tickMarkColor() const +{ + return d->tickMarkPen.color(); +} + +void RulerAttributes::setShowMajorTickMarks( bool show ) +{ + d->showMajorTickMarks = show; +} + +bool RulerAttributes::showMajorTickMarks() const +{ + return d->showMajorTickMarks; +} + +void RulerAttributes::setShowMinorTickMarks( bool show ) +{ + d->showMinorTickMarks = show; +} + +bool RulerAttributes::showMinorTickMarks() const +{ + return d->showMinorTickMarks; +} + +void RulerAttributes::setLabelMargin(int margin) +{ + d->labelMargin = margin; +} + +int RulerAttributes::labelMargin() const +{ + return d->labelMargin; +} + +RulerAttributes & RulerAttributes::operator=( const RulerAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +RulerAttributes::~RulerAttributes() +{ + delete _d; _d = 0; +} + + +bool RulerAttributes::operator == ( const RulerAttributes& r ) const +{ + bool isEqual = + tickMarkPen() == r.tickMarkPen() && + majorTickMarkPen() == r.majorTickMarkPen() && + minorTickMarkPen() == r.minorTickMarkPen(); + if( isEqual ) { + QMapIterator it( d->customTickMarkPens ); + while( it.hasNext() ) { + it.next(); + if ( it.value() != r.tickMarkPen(it.key()) ) + return false; + } + } + return isEqual; +} + +void RulerAttributes::setShowRulerLine( bool show ) +{ + d->showRulerLine = show; +} + +bool RulerAttributes::showRulerLine() const +{ + return d->showRulerLine; +} + + +#if !defined( QT_NO_DEBUG_STREAM ) +QDebug operator << ( QDebug dbg, const KDChart::RulerAttributes& a ) +{ + dbg << "KDChart::RulerAttributes(" + << "tickMarkPen=" << a.tickMarkPen() + << "majorTickMarkPen=" << a.majorTickMarkPen() + << "minorTickMarkPen=" << a.minorTickMarkPen(); + const RulerAttributes::TickMarkerPensMap pens( a.tickMarkPens() ); + QMapIterator it( pens ); + while( it.hasNext() ) { + it.next(); + dbg << "customTickMarkPen=(" << it.value() << " : " << it.key() << ")"; + } + dbg << ")"; + return dbg; +} +#endif /* QT_NO_DEBUG_STREAM */ + diff --git a/libkdchart/src/KDChartRulerAttributes.h b/libkdchart/src/KDChartRulerAttributes.h new file mode 100644 index 0000000..f428daa --- /dev/null +++ b/libkdchart/src/KDChartRulerAttributes.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTRULERATTRIBUTES_H +#define KDCHARTRULERATTRIBUTES_H + +#include +#include "KDChartGlobal.h" +#include "KDChartEnums.h" + +class QPen; + +namespace KDChart { + +/** + * @brief A set of attributes controlling the appearance of axis rulers + */ +class KDCHART_EXPORT RulerAttributes +{ +public: + RulerAttributes(); + RulerAttributes( const RulerAttributes& ); + RulerAttributes &operator= ( const RulerAttributes& ); + + ~RulerAttributes(); + + /** + * Sets the pen used to draw the tick marks + */ + void setTickMarkPen( const QPen& pen ); + QPen tickMarkPen() const; + + /** + * Sets the pen used to draw major tick marks + */ + void setMajorTickMarkPen( const QPen& pen ); + bool majorTickMarkPenIsSet() const; + QPen majorTickMarkPen() const; + + /** + * Sets the pen used to draw minor tick marks + */ + void setMinorTickMarkPen( const QPen& pen ); + bool minorTickMarkPenIsSet() const; + QPen minorTickMarkPen() const; + + /** + * Sets the pen used to draw the tick mark at a specific value + * + * Note: This will not paint a tick mark at the specified value + * if it wasn't already drawn before. + */ + void setTickMarkPen( qreal value, const QPen& pen ); + QPen tickMarkPen( qreal value ) const; + typedef QMap TickMarkerPensMap; + TickMarkerPensMap tickMarkPens() const; + + bool hasTickMarkPenAt( qreal value) const; + + /** + * Color setter method provided for convenience + */ + void setTickMarkColor( const QColor& color ); + QColor tickMarkColor() const; + + /** + * Shows or hides minor tick marks + */ + void setShowMinorTickMarks( bool show ); + bool showMinorTickMarks() const; + + /** + * Shows or hides the ruler line + */ + void setShowRulerLine( bool show ); + bool showRulerLine() const; + + + /** + * Shows or hides major tick marks + */ + void setShowMajorTickMarks( bool show ); + bool showMajorTickMarks() const; + + /** + * Set margin that should be used between the labals and the ticks. Per + * default the value is -1 what means, that half of the label's font + * height/width should be used as margin. + */ + void setLabelMargin(int margin); + int labelMargin() const; + + bool operator==( const RulerAttributes& ) const; + inline bool operator!=( const RulerAttributes& other ) const { return !operator==(other); } + +private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( RulerAttributes ) +}; // End of class RulerAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::RulerAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::RulerAttributes ) +Q_DECLARE_METATYPE( KDChart::RulerAttributes ) +Q_DECLARE_TYPEINFO( KDChart::RulerAttributes, Q_MOVABLE_TYPE ); + + +#endif // KDCHARTRULERATTRIBUTES_H diff --git a/libkdchart/src/KDChartSignalCompressor.cpp b/libkdchart/src/KDChartSignalCompressor.cpp new file mode 100644 index 0000000..449320d --- /dev/null +++ b/libkdchart/src/KDChartSignalCompressor.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartSignalCompressor.h" + +using namespace KDChart; + +SignalCompressor::SignalCompressor( QObject* receiver, const char* signal, + QObject* parent ) + : QObject( parent ) +{ + connect( this, SIGNAL( finallyEmit() ), receiver, signal ); + connect( &m_timer, SIGNAL( timeout() ), SLOT( nowGoAlready() ) ); + m_timer.setSingleShot( true ); + // m_timer.setIntervall( 0 ); // default, just to know... +} + +void SignalCompressor::emitSignal() +{ + if ( !m_timer.isActive() ) m_timer.start(); +} + +void SignalCompressor::nowGoAlready() +{ + emit finallyEmit(); +} + + + + diff --git a/libkdchart/src/KDChartSignalCompressor.h b/libkdchart/src/KDChartSignalCompressor.h new file mode 100644 index 0000000..1ec4c88 --- /dev/null +++ b/libkdchart/src/KDChartSignalCompressor.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTSIGNALCOMPRESSOR_H +#define KDCHARTSIGNALCOMPRESSOR_H + +#include +#include + +namespace KDChart { + + /** SignalCompressor compresses signals where the same signal + needs to be emitted by several pieces of the code, but only + one of the signals should be received at the end. + Usage: + * create a object of SignalCompressor, and give it the name + and object of the signal it is supposed to manage + * instead of emitting the signal, call emitSignal() on the compressor + * the signal will only be emitted once, and that is after the + current call stack ends and returns to the event loop + + With the current implementation, the class changes the + sematics of signals to be a queued connection. If that is not + wanted, another compression algorithm needs to be + implemented. + Also, at the moment, only nullary signals are supported, as + parameters could not be compressed. + A typical use of the class is to compress update + notifications. + This class is not part of the published KDChart API. + */ + class SignalCompressor : public QObject + { + Q_OBJECT + + public: + SignalCompressor( QObject* receiver, const char* signal, + QObject* parent = 0 ); + + Q_SIGNALS: + void finallyEmit(); + + public Q_SLOTS: + void emitSignal(); // emit() won't work, because of stupid defines + + private Q_SLOTS: + void nowGoAlready(); + + private: + QTimer m_timer; + }; + +} + +#endif diff --git a/libkdchart/src/KDChartStackedBarDiagram_p.cpp b/libkdchart/src/KDChartStackedBarDiagram_p.cpp new file mode 100644 index 0000000..8196398 --- /dev/null +++ b/libkdchart/src/KDChartStackedBarDiagram_p.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include + +#include "KDChartBarDiagram.h" +#include "KDChartTextAttributes.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartStackedBarDiagram_p.h" + +using namespace KDChart; + +StackedBarDiagram::StackedBarDiagram( BarDiagram* d ) + : BarDiagramType( d ) +{ +} + +BarDiagram::BarType StackedBarDiagram::type() const +{ + return BarDiagram::Stacked; +} + +const QPair StackedBarDiagram::calculateDataBoundaries() const +{ + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + + double xMin = 0; + double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; + double yMin = 0, yMax = 0; + + bool bStarting = true; + for( int row = 0; row < rowCount; ++row ) + { + // calculate sum of values per column - Find out stacked Min/Max + double stackedValues = 0.0; + double negativeStackedValues = 0.0; + for ( int col = 0; col < colCount ; ++col ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + + if( point.value > 0.0 ) + stackedValues += point.value; + else + negativeStackedValues += point.value; + + // this is always true yMin can be 0 in case all values + // are the same + // same for yMax it can be zero if all values are negative + if( bStarting ){ + yMin = negativeStackedValues < 0.0 ? negativeStackedValues : stackedValues; + yMax = stackedValues > 0.0 ? stackedValues : negativeStackedValues; + bStarting = false; + }else{ + yMin = qMin( qMin( yMin, stackedValues ), negativeStackedValues ); + yMax = qMax( qMax( yMax, stackedValues ), negativeStackedValues ); + } + } + } + // special cases + if ( yMax == yMin ) { + if ( yMin == 0.0 ) + yMax = 0.1; //we need at least a range + else if( yMax < 0.0 ) + yMax = 0.0; // they are the same and negative + else if( yMin > 0.0 ) + yMin = 0.0; // they are the same but positive + } + const QPointF bottomLeft ( QPointF( xMin, yMin ) ); + const QPointF topRight ( QPointF( xMax, yMax ) ); + + return QPair< QPointF, QPointF >( bottomLeft, topRight ); +} + +void StackedBarDiagram::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + + const QPair boundaries = diagram()->dataBoundaries(); // cached + + const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; + const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); + + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + + BarAttributes ba = diagram()->barAttributes( diagram()->model()->index( 0, 0, diagram()->rootIndex() ) ); + double barWidth = 0; + double maxDepth = 0; + double width = boundRight.x() - boundLeft.x(); + double groupWidth = width/ (rowCount + 2); + double spaceBetweenBars = 0; + double spaceBetweenGroups = 0; + + if ( ba.useFixedBarWidth() ) { + barWidth = ba.fixedBarWidth(); + groupWidth += barWidth; + + // Pending Michel set a min and max value for the groupWidth + // related to the area.width + if ( groupWidth < 0 ) + groupWidth = 0; + + if ( groupWidth * rowCount > width ) + groupWidth = width / rowCount; + } + + // maxLimit: allow the space between bars to be larger until area.width() + // is covered by the groups. + double maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); + + + //Pending Michel: FixMe + if ( ba.useFixedDataValueGap() ) { + if ( width > maxLimit ) + spaceBetweenBars += ba.fixedDataValueGap(); + else + spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); + } + + if ( ba.useFixedValueBlockGap() ) + spaceBetweenGroups += ba.fixedValueBlockGap(); + + calculateValueAndGapWidths( rowCount, colCount,groupWidth, + barWidth, spaceBetweenBars, spaceBetweenGroups ); + + DataValueTextInfoList list; + for( int col = 0; col < colCount; ++col ) + { + double offset = spaceBetweenGroups; + if( ba.useFixedBarWidth() ) + offset -= ba.fixedBarWidth(); + + if( offset < 0 ) + offset = 0; + + for( int row = 0; row < rowCount; ++row ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); + + const QModelIndex index = attributesModel()->mapToSource( p.index ); + ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( index ); + const double value = p.value; + double stackedValues = 0.0; + double key = 0.0; + + if ( threeDAttrs.isEnabled() ) { + if ( barWidth > 0 ) + barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; + if ( barWidth <= 0 ) { + barWidth = 0; + maxDepth = offset - (width/rowCount); + } + } else { + barWidth = (width - (offset*rowCount))/ rowCount ; + } + + for ( int k = col; k >= 0; --k ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, k ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + if( ( p.value >= 0.0 && point.value >= 0.0 ) || ( p.value < 0.0 && point.value < 0.0 ) ) + stackedValues += point.value; + key = point.key; + } + + const double usedDepth = threeDAttrs.depth(); + + QPointF point = ctx->coordinatePlane()->translate( QPointF( key, stackedValues ) ); + + const double dy = point.y() - usedDepth; + if ( dy < 0 ) { + threeDAttrs.setDepth( point.y() - 1 ); + diagram()->setThreeDBarAttributes( threeDAttrs ); + } + + point.rx() += offset / 2; + const QPointF previousPoint = ctx->coordinatePlane()->translate( QPointF( key, stackedValues - value ) ); + const double barHeight = previousPoint.y() - point.y(); + + const QRectF rect( point, QSizeF( barWidth , barHeight ) ); + appendDataValueTextInfoToList( diagram(), list, index, PositionPoints( rect ), + Position::NorthWest, Position::SouthEast, + value ); + paintBars( ctx, index, rect, maxDepth ); + } + } + paintDataValueTextsAndMarkers( diagram(), ctx, list, false ); +} diff --git a/libkdchart/src/KDChartStackedBarDiagram_p.h b/libkdchart/src/KDChartStackedBarDiagram_p.h new file mode 100644 index 0000000..0c683b0 --- /dev/null +++ b/libkdchart/src/KDChartStackedBarDiagram_p.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTSTACKEDBARDIAGRAM_P_H +#define KDCHARTSTACKEDBARDIAGRAM_P_H + +#include "KDChartBarDiagram_p.h" + +namespace KDChart { + + class StackedBarDiagram : public BarDiagram::BarDiagramType + { + public: + explicit StackedBarDiagram( BarDiagram* ); + virtual ~StackedBarDiagram() {} + virtual BarDiagram::BarType type() const; + virtual const QPair calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); + }; + +} + +#endif diff --git a/libkdchart/src/KDChartStackedLineDiagram_p.cpp b/libkdchart/src/KDChartStackedLineDiagram_p.cpp new file mode 100644 index 0000000..4832cd3 --- /dev/null +++ b/libkdchart/src/KDChartStackedLineDiagram_p.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartStackedLineDiagram_p.h" + +#include + +#include "KDChartBarDiagram.h" +#include "KDChartLineDiagram.h" +#include "KDChartTextAttributes.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractCartesianDiagram.h" + +using namespace KDChart; +using namespace std; + +StackedLineDiagram::StackedLineDiagram( LineDiagram* d ) + : LineDiagramType( d ) +{ +} + +LineDiagram::LineType StackedLineDiagram::type() const +{ + return LineDiagram::Stacked; +} + +const QPair StackedLineDiagram::calculateDataBoundaries() const +{ + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + const double xMin = 0; + double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; + if ( !diagram()->centerDataPoints() && diagram()->model() ) + xMax -= 1; + double yMin = 0, yMax = 0; + + bool bStarting = true; + for( int row = 0; row < rowCount; ++row ) + { + // calculate sum of values per column - Find out stacked Min/Max + double stackedValues = 0.0; + double negativeStackedValues = 0.0; + for( int col = datasetDimension() - 1; col < colCount; col += datasetDimension() ) { + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + + if( ISNAN( point.value ) ) + continue; + + if( point.value >= 0.0 ) + stackedValues += point.value; + else + negativeStackedValues += point.value; + } + + if( bStarting ){ + yMin = stackedValues; + yMax = stackedValues; + bStarting = false; + }else{ + // take in account all stacked values + yMin = qMin( qMin( yMin, negativeStackedValues ), stackedValues ); + yMax = qMax( qMax( yMax, negativeStackedValues ), stackedValues ); + } + } + + const QPointF bottomLeft( xMin, yMin ); + const QPointF topRight( xMax, yMax ); + + return QPair ( bottomLeft, topRight ); +} + +void StackedLineDiagram::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + + const QPair boundaries = diagram()->dataBoundaries(); + const QPointF bottomLeft = boundaries.first; + const QPointF topRight = boundaries.second; + + const int columnCount = compressor().modelDataColumns(); + const int rowCount = compressor().modelDataRows(); + +// FIXME integrate column index retrieval to compressor: + int maxFound = 0; +// { // find the last column number that is not hidden +// for( int iColumn = datasetDimension() - 1; +// iColumn < columnCount; +// iColumn += datasetDimension() ) +// if( ! diagram()->isHidden( iColumn ) ) +// maxFound = iColumn; +// } + maxFound = columnCount; + // ^^^ temp + + DataValueTextInfoList list; + LineAttributesInfoList lineList; + LineAttributes::MissingValuesPolicy policy = LineAttributes::MissingValuesAreBridged; + + //FIXME(khz): add LineAttributes::MissingValuesPolicy support for LineDiagram::Stacked and ::Percent + + QVector percentSumValues; + + QList bottomPoints; + bool bFirstDataset = true; + + for( int column = 0; column < columnCount; ++column ) + { + CartesianDiagramDataCompressor::CachePosition previousCellPosition; + + //display area can be set by dataset ( == column) and/or by cell + LineAttributes laPreviousCell; // by default no area is drawn + QModelIndex indexPreviousCell; + QList areas; + QList points; + + for ( int row = 0; row < rowCount; ++row ) { + const CartesianDiagramDataCompressor::CachePosition position( row, column ); + CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + + const LineAttributes laCell = diagram()->lineAttributes( sourceIndex ); + const bool bDisplayCellArea = laCell.displayArea(); + + const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy(); + + if( ISNAN( point.value ) && policy == LineAttributes::MissingValuesShownAsZero ) + point.value = 0.0; + + double stackedValues = 0, nextValues = 0, nextKey = 0; + for ( int column2 = column; column2 >= 0; --column2 ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, column2 ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); + if( !ISNAN( point.value ) ) + { + stackedValues += point.value; + } + else if( policy == LineAttributes::MissingValuesAreBridged ) + { + const double interpolation = interpolateMissingValue( position ); + if( !ISNAN( interpolation ) ) + stackedValues += interpolation; + } + + //qDebug() << valueForCell( iRow, iColumn2 ); + if ( row + 1 < rowCount ){ + const CartesianDiagramDataCompressor::CachePosition position( row + 1, column2 ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + if( !ISNAN( point.value ) ) + { + nextValues += point.value; + } + else if( policy == LineAttributes::MissingValuesAreBridged ) + { + const double interpolation = interpolateMissingValue( position ); + if( !ISNAN( interpolation ) ) + nextValues += interpolation; + } + nextKey = point.key; + } + } + //qDebug() << stackedValues << endl; + const QPointF nextPoint = ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, stackedValues ) ); + points << nextPoint; + + const QPointF ptNorthWest( nextPoint ); + const QPointF ptSouthWest( + bDisplayCellArea + ? ( bFirstDataset + ? ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, 0.0 ) ) + : bottomPoints.at( row ) + ) + : nextPoint ); + QPointF ptNorthEast; + QPointF ptSouthEast; + + if ( row + 1 < rowCount ){ + QPointF toPoint = ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? nextKey + 0.5 : nextKey, nextValues ) ); + lineList.append( LineAttributesInfo( sourceIndex, nextPoint, toPoint ) ); + ptNorthEast = toPoint; + ptSouthEast = + bDisplayCellArea + ? ( bFirstDataset + ? ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? nextKey + 0.5 : nextKey, 0.0 ) ) + : bottomPoints.at( row + 1 ) + ) + : toPoint; + if( areas.count() && laCell != laPreviousCell ){ + paintAreas( ctx, indexPreviousCell, areas, laPreviousCell.transparency() ); + areas.clear(); + } + if( bDisplayCellArea ){ + QPolygonF poly; + poly << ptNorthWest << ptNorthEast << ptSouthEast << ptSouthWest; + areas << poly; + laPreviousCell = laCell; + indexPreviousCell = sourceIndex; + }else{ + //qDebug() << "no area shown for row"< calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); + }; + +} + +#endif diff --git a/libkdchart/src/KDChartStackedLyingBarDiagram_p.cpp b/libkdchart/src/KDChartStackedLyingBarDiagram_p.cpp new file mode 100644 index 0000000..22969a4 --- /dev/null +++ b/libkdchart/src/KDChartStackedLyingBarDiagram_p.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include + +#include "KDChartBarDiagram.h" +#include "KDChartTextAttributes.h" +#include "KDChartAttributesModel.h" +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartStackedLyingBarDiagram_p.h" + +using namespace KDChart; + +StackedLyingBarDiagram::StackedLyingBarDiagram( BarDiagram* d ) + : BarDiagramType( d ) +{ +} + +BarDiagram::BarType StackedLyingBarDiagram::type() const +{ + return BarDiagram::Stacked; +} + +const QPair StackedLyingBarDiagram::calculateDataBoundaries() const +{ + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + + double xMin = 0; + double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; + double yMin = 0, yMax = 0; + + bool bStarting = true; + for( int row = 0; row < rowCount; ++row ) + { + // calculate sum of values per column - Find out stacked Min/Max + double stackedValues = 0.0; + double negativeStackedValues = 0.0; + for ( int col = 0; col < colCount ; ++col ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + + if( point.value > 0.0 ) + stackedValues += point.value; + else + negativeStackedValues += point.value; + + // this is always true yMin can be 0 in case all values + // are the same + // same for yMax it can be zero if all values are negative + if( bStarting ){ + yMin = negativeStackedValues < 0.0 ? negativeStackedValues : stackedValues; + yMax = stackedValues > 0.0 ? stackedValues : negativeStackedValues; + bStarting = false; + }else{ + yMin = qMin( qMin( yMin, stackedValues ), negativeStackedValues ); + yMax = qMax( qMax( yMax, stackedValues ), negativeStackedValues ); + } + } + } + // special cases + if ( yMax == yMin ) { + if ( yMin == 0.0 ) + yMax = 0.1; //we need at least a range + else if( yMax < 0.0 ) + yMax = 0.0; // they are the same and negative + else if( yMin > 0.0 ) + yMin = 0.0; // they are the same but positive + } + const QPointF bottomLeft ( QPointF( yMin, xMin ) ); + const QPointF topRight ( QPointF( yMax, xMax ) ); + + return QPair< QPointF, QPointF >( bottomLeft, topRight ); +} + +void StackedLyingBarDiagram::paint( PaintContext* ctx ) +{ + reverseMapper().clear(); + + const QPair boundaries = diagram()->dataBoundaries(); // cached + + const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; + const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); + + const int rowCount = compressor().modelDataRows(); + const int colCount = compressor().modelDataColumns(); + + BarAttributes ba = diagram()->barAttributes( diagram()->model()->index( 0, 0, diagram()->rootIndex() ) ); + double barWidth = 0; + double maxDepth = 0; + double width = boundLeft.y() - boundRight.y(); + double groupWidth = width/ (rowCount + 2); + double spaceBetweenBars = 0; + double spaceBetweenGroups = 0; + + if ( ba.useFixedBarWidth() ) { + barWidth = ba.fixedBarWidth(); + groupWidth += barWidth; + + // Pending Michel set a min and max value for the groupWidth + // related to the area.width + if ( groupWidth < 0 ) + groupWidth = 0; + + if ( groupWidth * rowCount > width ) + groupWidth = width / rowCount; + } + + // maxLimit: allow the space between bars to be larger until area.width() + // is covered by the groups. + double maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); + + + //Pending Michel: FixMe + if ( ba.useFixedDataValueGap() ) { + if ( ctx->rectangle().width() > maxLimit ) + spaceBetweenBars += ba.fixedDataValueGap(); + else + spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); + } + + if ( ba.useFixedValueBlockGap() ) + spaceBetweenGroups += ba.fixedValueBlockGap(); + + calculateValueAndGapWidths( rowCount, colCount,groupWidth, + barWidth, spaceBetweenBars, spaceBetweenGroups ); + + DataValueTextInfoList list; + for( int row = rowCount - 1; row >= 0; --row ) + { + double offset = spaceBetweenGroups; + if( ba.useFixedBarWidth() ) + offset -= ba.fixedBarWidth(); + + if( offset < 0 ) + offset = 0; + + for( int col = 0; col < colCount; ++col ) + { + double threeDOffset = 0.0; + const CartesianDiagramDataCompressor::CachePosition position( row, col ); + const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); + + const QModelIndex index = attributesModel()->mapToSource( p.index ); + ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( index ); + const double value = p.value; + double stackedValues = 0.0; + double key = 0.0; + + if ( threeDAttrs.isEnabled() ){ + if ( barWidth > 0 ) { + barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; + threeDOffset = threeDAttrs.depth(); + } + if ( barWidth <= 0 ) { + barWidth = 0.1; + threeDOffset = (width - (offset*rowCount))/ rowCount; + } + }else{ + barWidth = (width - (offset*rowCount))/ rowCount; + } + + for ( int k = col; k >= 0; --k ) + { + const CartesianDiagramDataCompressor::CachePosition position( row, k ); + const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); + if( ( p.value >= 0.0 && point.value >= 0.0 ) || ( p.value < 0.0 && point.value < 0.0 ) ) + stackedValues += point.value; + key = point.key; + } + QPointF point = ctx->coordinatePlane()->translate( QPointF( stackedValues, rowCount - key ) ); + point.ry() += offset / 2 + threeDOffset; + const QPointF previousPoint = ctx->coordinatePlane()->translate( QPointF( stackedValues - value, rowCount - key ) ); + const double barHeight = point.x() - previousPoint.x(); + point.rx() -= barHeight; + + const QRectF rect( point, QSizeF( barHeight , barWidth ) ); + appendDataValueTextInfoToList( diagram(), list, index, PositionPoints( rect ), + Position::NorthEast, Position::SouthWest, + value ); + paintBars( ctx, index, rect, maxDepth ); + } + } + paintDataValueTextsAndMarkers( diagram(), ctx, list, false ); +} diff --git a/libkdchart/src/KDChartStackedLyingBarDiagram_p.h b/libkdchart/src/KDChartStackedLyingBarDiagram_p.h new file mode 100644 index 0000000..95f31b6 --- /dev/null +++ b/libkdchart/src/KDChartStackedLyingBarDiagram_p.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTSTACKEDLYINGBARDIAGRAM_P_H +#define KDCHARTSTACKEDLYINGBARDIAGRAM_P_H + +#include "KDChartBarDiagram_p.h" + +namespace KDChart { + + class StackedLyingBarDiagram : public BarDiagram::BarDiagramType + { + public: + explicit StackedLyingBarDiagram( BarDiagram* ); + virtual ~StackedLyingBarDiagram() {} + virtual BarDiagram::BarType type() const; + virtual const QPair calculateDataBoundaries() const; + virtual void paint( PaintContext* ctx ); + }; + +} + +#endif diff --git a/libkdchart/src/KDChartStockBarAttributes.cpp b/libkdchart/src/KDChartStockBarAttributes.cpp new file mode 100644 index 0000000..7768c5e --- /dev/null +++ b/libkdchart/src/KDChartStockBarAttributes.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartStockBarAttributes.h" + +#define d d_func() + +using namespace KDChart; + +class StockBarAttributes::Private { +public: + Private(); + + qreal candlestickWidth; + qreal tickLength; +}; + +StockBarAttributes::Private::Private() + : candlestickWidth( 0.3 ) + , tickLength( 0.15 ) +{ +} + +StockBarAttributes::StockBarAttributes() + : _d( new Private ) +{ +} + +StockBarAttributes::StockBarAttributes( const StockBarAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +StockBarAttributes &StockBarAttributes::operator= ( const StockBarAttributes& r ) +{ + if ( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +StockBarAttributes::~StockBarAttributes() +{ + delete _d; +} + +/** + * Sets the width of a candlestick + * + * @param width The width of a candlestick + */ +void StockBarAttributes::setCandlestickWidth( qreal width ) +{ + d->candlestickWidth = width; +} +/** + * @return the width of a candlestick + */ +qreal StockBarAttributes::candlestickWidth() const +{ + return d->candlestickWidth; +} + +/** + * Sets the tick length of both the open and close marker + * + * @param length the tick length + */ +void StockBarAttributes::setTickLength( qreal length ) +{ + d->tickLength = length; +} + +/** + * @return the tick length used for both the open and close marker + */ +qreal StockBarAttributes::tickLength() const +{ + return d->tickLength; +} + +bool StockBarAttributes::operator==( const StockBarAttributes& r ) const +{ + return candlestickWidth() == r.candlestickWidth() && + tickLength() == r.tickLength(); +} diff --git a/libkdchart/src/KDChartStockBarAttributes.h b/libkdchart/src/KDChartStockBarAttributes.h new file mode 100644 index 0000000..f3a6d3d --- /dev/null +++ b/libkdchart/src/KDChartStockBarAttributes.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTSTOCKBARATTRIBUTES_H +#define KDCHARTSTOCKBARATTRIBUTES_H + +#include +#include "KDChartGlobal.h" + +namespace KDChart { + +/** + * @brief Attributes to customize the appearance of a column in a stock chart + */ +class KDCHART_EXPORT StockBarAttributes +{ +public: + StockBarAttributes(); + StockBarAttributes( const StockBarAttributes& ); + StockBarAttributes &operator= ( const StockBarAttributes& ); + + ~StockBarAttributes(); + + void setCandlestickWidth( qreal width ); + qreal candlestickWidth() const; + + void setTickLength( qreal length ); + qreal tickLength() const; + + bool operator==( const StockBarAttributes& ) const; + inline bool operator!=( const StockBarAttributes& other ) const { return !operator==(other); } + +private: + class Private; + Private * _d; + Private * d_func() { return _d; } + const Private * d_func() const { return _d; } +}; + +} + +Q_DECLARE_METATYPE( KDChart::StockBarAttributes ) + +#endif // KDCHARTSTOCKBARATTRIBUTES_H diff --git a/libkdchart/src/KDChartStockDiagram.cpp b/libkdchart/src/KDChartStockDiagram.cpp new file mode 100644 index 0000000..0cfa030 --- /dev/null +++ b/libkdchart/src/KDChartStockDiagram.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartStockDiagram.h" +#include "KDChartStockDiagram_p.h" + +#include "KDChartPaintContext.h" + +using namespace KDChart; + +#define d d_func() + +StockDiagram::StockDiagram( QWidget *parent, CartesianCoordinatePlane *plane ) + : AbstractCartesianDiagram( new Private(), parent, plane ) +{ + init(); +} + +StockDiagram::~StockDiagram() +{ +} + +/** + * Initializes the diagram + */ +void StockDiagram::init() +{ + d->diagram = this; + d->compressor.setModel( attributesModel() ); + + // Set properties to defaults + d->type = HighLowClose; + d->upTrendCandlestickBrush = QBrush( Qt::white ); + d->downTrendCandlestickBrush = QBrush( Qt::black ); + d->upTrendCandlestickPen = QPen( Qt::black ); + d->downTrendCandlestickPen = QPen( Qt::black ); + + d->lowHighLinePen = QPen( Qt::black ); + + setPen( QPen( Qt::black ) ); +} + +/** + * Switches between the supported types of stock charts, + * depending on \a type + */ +void StockDiagram::setType( Type type ) +{ + d->type = type; + emit propertiesChanged(); +} + +/** + * @return the type of this diagram + */ +StockDiagram::Type StockDiagram::type() const +{ + return d->type; +} + +void StockDiagram::setStockBarAttributes( const StockBarAttributes &attr ) +{ + attributesModel()->setModelData( + qVariantFromValue( attr ), + StockBarAttributesRole ); + emit propertiesChanged(); +} + +StockBarAttributes StockDiagram::stockBarAttributes() const +{ + return qVariantValue( + attributesModel()->modelData( StockBarAttributesRole ) ); +} + +void StockDiagram::setStockBarAttributes( int column, const StockBarAttributes &attr ) +{ + d->setDatasetAttrs( column, qVariantFromValue( attr ), StockBarAttributesRole ); + emit propertiesChanged(); +} + +StockBarAttributes StockDiagram::stockBarAttributes( int column ) const +{ + const QVariant attr( d->datasetAttrs( column, StockBarAttributesRole ) ); + if ( attr.isValid() ) + return qVariantValue( attr ); + return stockBarAttributes(); +} + +/** + * Sets the 3D attributes for all bars (i.e. candlesticks) + * + * @param attr The 3D attributes to set + */ +void StockDiagram::setThreeDBarAttributes( const ThreeDBarAttributes &attr ) +{ + attributesModel()->setModelData( + qVariantFromValue( attr ), + ThreeDBarAttributesRole ); + emit propertiesChanged(); +} + +/** + * Returns the 3D attributes for all bars (i.e. candlesticks) + * + * @return the 3D bar attributes + */ +ThreeDBarAttributes StockDiagram::threeDBarAttributes() const +{ + return qVariantValue( + attributesModel()->modelData( ThreeDBarAttributesRole ) ); +} + +/** + * Sets the 3D attributes for the bar (i.e. candlestick) in certain column + * of the diagram + * + * Note: Every column in a StockDiagram is represented by a row in the model + * + * @param column The column to set the 3D bar attributes for + * @param attr The 3D attributes to set + */ +void StockDiagram::setThreeDBarAttributes( int column, const ThreeDBarAttributes &attr ) +{ + d->setDatasetAttrs( column, qVariantFromValue( attr ), StockBarAttributesRole ); + emit propertiesChanged(); +} + +/** + * Returns the 3D attributes for a bars (i.e. candlestick) in a certain column + * of the diagram + * + * Note: Every column in a StockDiagram is represented by a row in the model + * + * @param column The column to get the 3D bar attributes for + * @return The 3D attributes for the specified column + */ +ThreeDBarAttributes StockDiagram::threeDBarAttributes( int column ) const +{ + const QVariant attr( d->datasetAttrs( column, ThreeDBarAttributesRole ) ); + if ( attr.isValid() ) + return qVariantValue( attr ); + return threeDBarAttributes(); +} + + +void StockDiagram::setLowHighLinePen( const QPen &pen ) +{ + d->lowHighLinePen = pen; +} + +QPen StockDiagram::lowHighLinePen() const +{ + return d->lowHighLinePen; +} + +void StockDiagram::setLowHighLinePen( int column, const QPen &pen ) +{ + d->lowHighLinePens[column] = pen; +} + +QPen StockDiagram::lowHighLinePen( int column ) const +{ + if ( d->lowHighLinePens.contains( column ) ) + return d->lowHighLinePens[column]; + return d->lowHighLinePen; +} + +void StockDiagram::setUpTrendCandlestickBrush( const QBrush &brush ) +{ + d->upTrendCandlestickBrush = brush; +} + +QBrush StockDiagram::upTrendCandlestickBrush() const +{ + return d->upTrendCandlestickBrush; +} + +void StockDiagram::setDownTrendCandlestickBrush( const QBrush &brush ) +{ + d->downTrendCandlestickBrush = brush; +} + +QBrush StockDiagram::downTrendCandlestickBrush() const +{ + return d->downTrendCandlestickBrush; +} + +void StockDiagram::setUpTrendCandlestickBrush( int column, const QBrush &brush ) +{ + d->upTrendCandlestickBrushes[column] = brush; +} + +QBrush StockDiagram::upTrendCandlestickBrush( int column ) const +{ + if ( d->upTrendCandlestickBrushes.contains( column ) ) + return d->upTrendCandlestickBrushes[column]; + return d->upTrendCandlestickBrush; +} + +void StockDiagram::setDownTrendCandlestickBrush( int column, const QBrush &brush ) +{ + d->downTrendCandlestickBrushes[column] = brush; +} + +QBrush StockDiagram::downTrendCandlestickBrush( int column ) const +{ + if ( d->downTrendCandlestickBrushes.contains( column ) ) + return d->downTrendCandlestickBrushes[column]; + return d->downTrendCandlestickBrush; +} + + +void StockDiagram::setUpTrendCandlestickPen( const QPen &pen ) +{ + d->upTrendCandlestickPen = pen; +} + +QPen StockDiagram::upTrendCandlestickPen() const +{ + return d->upTrendCandlestickPen; +} + +void StockDiagram::setDownTrendCandlestickPen( const QPen &pen ) +{ + d->downTrendCandlestickPen = pen; +} + +QPen StockDiagram::downTrendCandlestickPen() const +{ + return d->downTrendCandlestickPen; +} + +void StockDiagram::setUpTrendCandlestickPen( int column, const QPen &pen ) +{ + d->upTrendCandlestickPens[column] = pen; +} + +QPen StockDiagram::upTrendCandlestickPen( int column ) const +{ + if ( d->upTrendCandlestickPens.contains( column ) ) + return d->upTrendCandlestickPens[column]; + return d->upTrendCandlestickPen; +} + +void StockDiagram::setDownTrendCandlestickPen( int column, const QPen &pen ) +{ + d->downTrendCandlestickPens[column] = pen; +} + +QPen StockDiagram::downTrendCandlestickPen( int column ) const +{ + if ( d->downTrendCandlestickPens.contains( column ) ) + return d->downTrendCandlestickPens[column]; + return d->downTrendCandlestickPen; +} + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +int StockDiagram::numberOfAbscissaSegments() const { return 1; } + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +int StockDiagram::numberOfOrdinateSegments() const { return 1; } + +void StockDiagram::paint( PaintContext *context ) +{ + // Clear old reverse mapping data and create new + // reverse mapping scene + d->reverseMapper.clear(); + + PainterSaver painterSaver( context->painter() ); + int rowCount = attributesModel()->rowCount( attributesModelRootIndex() ); + for ( int row = 0; row < rowCount; row++ ) { + CartesianDiagramDataCompressor::DataPoint low; + CartesianDiagramDataCompressor::DataPoint high; + CartesianDiagramDataCompressor::DataPoint open; + CartesianDiagramDataCompressor::DataPoint close; + CartesianDiagramDataCompressor::DataPoint volume; + + if ( d->type == HighLowClose ) { + const CartesianDiagramDataCompressor::CachePosition highPos( row, 0 ); + const CartesianDiagramDataCompressor::CachePosition lowPos( row, 1 ); + const CartesianDiagramDataCompressor::CachePosition closePos( row, 2 ); + low = d->compressor.data( lowPos ); + high = d->compressor.data( highPos ); + close = d->compressor.data( closePos ); + } else if ( d->type == OpenHighLowClose || d->type == Candlestick ) { + const CartesianDiagramDataCompressor::CachePosition openPos( row, 0 ); + const CartesianDiagramDataCompressor::CachePosition highPos( row, 1 ); + const CartesianDiagramDataCompressor::CachePosition lowPos( row, 2 ); + const CartesianDiagramDataCompressor::CachePosition closePos( row, 3 ); + open = d->compressor.data( openPos ); + low = d->compressor.data( lowPos ); + high = d->compressor.data( highPos ); + close = d->compressor.data( closePos ); + } + + switch( d->type ) { + case HighLowClose: + open.hidden = true; + // Fall-through intended! + case OpenHighLowClose: + d->drawOHLCBar( open, high, low, close, context ); + break; + case Candlestick: + d->drawCandlestick( open, high, low, close, context ); + break; + } + } +} + +void StockDiagram::resize( const QSizeF &size ) +{ + d->compressor.setResolution( static_cast< int >( size.width() * coordinatePlane()->zoomFactorX() ), + static_cast< int >( size.height() * coordinatePlane()->zoomFactorY() ) ); + setDataBoundariesDirty(); +} + +double StockDiagram::threeDItemDepth( int column ) const +{ + Q_UNUSED( column ); + //FIXME: Implement threeD functionality + return 1.0; +} + +double StockDiagram::threeDItemDepth( const QModelIndex &index ) const +{ + Q_UNUSED( index ); + //FIXME: Implement threeD functionality + return 1.0; +} + +const QPair StockDiagram::calculateDataBoundaries() const +{ + const int rowCount = attributesModel()->rowCount( attributesModelRootIndex() ); + const int colCount = attributesModel()->columnCount( attributesModelRootIndex() ); + qreal xMin = 0.0; + qreal xMax = rowCount; + qreal yMin = 0.0; + qreal yMax = 0.0; + for ( int row = 0; row < rowCount; row++ ) { + for ( int col = 0; col < colCount; col++ ) { + const CartesianDiagramDataCompressor::CachePosition pos( row, col ); + const CartesianDiagramDataCompressor::DataPoint point = d->compressor.data( pos ); + yMax = qMax( yMax, point.value ); + yMin = qMin( yMin, point.value ); // FIXME: Can stock charts really have negative values? + } + } + return QPair( QPointF( xMin, yMin ), QPointF( xMax, yMax ) ); +} + diff --git a/libkdchart/src/KDChartStockDiagram.h b/libkdchart/src/KDChartStockDiagram.h new file mode 100644 index 0000000..ec5c8dd --- /dev/null +++ b/libkdchart/src/KDChartStockDiagram.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHART_STOCK_DIAGRAM_H +#define KDCHART_STOCK_DIAGRAM_H + +#include "KDChartAbstractCartesianDiagram.h" +#include "KDChartCartesianCoordinatePlane.h" +#include "KDChartStockBarAttributes.h" +#include "KDChartThreeDBarAttributes.h" + +namespace KDChart { + class PaintContext; + +class KDCHART_EXPORT StockDiagram : public AbstractCartesianDiagram +{ + Q_OBJECT + + Q_DISABLE_COPY( StockDiagram ) + + KDCHART_DECLARE_DERIVED_DIAGRAM( StockDiagram, CartesianCoordinatePlane ) + +public: + enum Type { + HighLowClose, + OpenHighLowClose, + Candlestick + }; + + explicit StockDiagram( QWidget *parent = 0, CartesianCoordinatePlane *plane = 0 ); + ~StockDiagram(); + + void setType( Type type ); + Type type() const; + + void setStockBarAttributes( const StockBarAttributes &attr ); + StockBarAttributes stockBarAttributes() const; + + void setStockBarAttributes( int column, const StockBarAttributes &attr ); + StockBarAttributes stockBarAttributes( int column ) const; + + void setThreeDBarAttributes( const ThreeDBarAttributes &attr ); + ThreeDBarAttributes threeDBarAttributes() const; + + void setThreeDBarAttributes( int column, const ThreeDBarAttributes &attr ); + ThreeDBarAttributes threeDBarAttributes( int column ) const; + + void setLowHighLinePen( const QPen &pen ); + QPen lowHighLinePen() const; + + void setLowHighLinePen( int column, const QPen &pen ); + QPen lowHighLinePen( int column ) const; + + void setUpTrendCandlestickBrush( const QBrush &brush ); + QBrush upTrendCandlestickBrush() const; + + void setDownTrendCandlestickBrush( const QBrush &brush ); + QBrush downTrendCandlestickBrush() const; + + void setUpTrendCandlestickBrush( int column, const QBrush &brush ); + QBrush upTrendCandlestickBrush( int column ) const; + + void setDownTrendCandlestickBrush( int column, const QBrush &brush ); + QBrush downTrendCandlestickBrush( int column ) const; + + void setUpTrendCandlestickPen( const QPen &pen ); + QPen upTrendCandlestickPen() const; + + void setDownTrendCandlestickPen( const QPen &pen ); + QPen downTrendCandlestickPen() const; + + void setUpTrendCandlestickPen( int column, const QPen &pen ); + QPen upTrendCandlestickPen( int column ) const; + + void setDownTrendCandlestickPen( int column, const QPen &pen ); + QPen downTrendCandlestickPen( int column ) const; + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + virtual const int numberOfAbscissaSegments() const; + virtual const int numberOfOrdinateSegments() const; +#else + virtual int numberOfAbscissaSegments() const; + virtual int numberOfOrdinateSegments() const; +#endif + + virtual void paint( PaintContext *paintContext ); + + virtual void resize( const QSizeF &size ); + + virtual double threeDItemDepth( int column ) const; + virtual double threeDItemDepth( const QModelIndex &index ) const; + +protected: + virtual const QPair calculateDataBoundaries() const; +}; + +} // Namespace KDChart + +#endif // KDCHART_STOCK_DIAGRAM_H + diff --git a/libkdchart/src/KDChartStockDiagram_p.cpp b/libkdchart/src/KDChartStockDiagram_p.cpp new file mode 100644 index 0000000..b79762a --- /dev/null +++ b/libkdchart/src/KDChartStockDiagram_p.cpp @@ -0,0 +1,535 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartStockDiagram_p.h" + +using namespace KDChart; + + +class StockDiagram::Private::ThreeDPainter +{ +public: + struct ThreeDProperties { + qreal depth; + qreal angle; + bool useShadowColors; + }; + + ThreeDPainter( QPainter *p ) + : painter( p ) {}; + + QPolygonF drawTwoDLine( const QLineF &line, const QPen &pen, + const ThreeDProperties &props ); + QPolygonF drawThreeDLine( const QLineF &line, const QBrush &brush, + const QPen &pen, const ThreeDProperties &props ); + QPolygonF drawThreeDRect( const QRectF &rect, const QBrush &brush, + const QPen &pen, const ThreeDProperties &props ); + +private: + QPointF projectPoint( const QPointF &point, qreal depth, qreal angle ) const; + QColor calcShadowColor( const QColor &color, qreal angle ) const; + + QPainter *painter; +}; + +/** + * Projects a point in 3D space + * + * @param depth The distance from the point and the projected point + * @param angle The angle the projected point is rotated by around the original point + */ +QPointF StockDiagram::Private::ThreeDPainter::projectPoint( const QPointF &point, qreal depth, qreal angle ) const +{ + const qreal angleInRad = DEGTORAD( angle ); + const qreal distX = depth * cos( angleInRad ); + // Y coordinates are reversed on our coordinate plane + const qreal distY = depth * -sin( angleInRad ); + + return QPointF( point.x() + distX, point.y() + distY ); +} + +/** + * Returns the shadow color for a given color, depending on the angle of rotation + * + * @param color The color to calculate the shadow color for + * @param angle The angle that the colored area is rotated by + */ +QColor StockDiagram::Private::ThreeDPainter::calcShadowColor( const QColor &color, qreal angle ) const +{ + // The shadow factor determines to how many percent the brightness + // of the color can be reduced. That is, the darkest shadow color + // is color * shadowFactor. + const qreal shadowFactor = 0.5; + const qreal sinAngle = 1.0 - qAbs( sin( DEGTORAD( angle ) ) ) * shadowFactor; + return QColor( qRound( color.red() * sinAngle ), + qRound( color.green() * sinAngle ), + qRound( color.blue() * sinAngle ) ); +} + +/** + * Draws a 2D line in 3D space by painting it with a z-coordinate of props.depth / 2.0 + * + * @param line The line to draw + * @param pen The pen to use to draw the line + * @param props The 3D properties to draw the line with + * @return The drawn line, but with a width of 2px, as a polygon + */ +QPolygonF StockDiagram::Private::ThreeDPainter::drawTwoDLine( const QLineF &line, const QPen &pen, + const ThreeDProperties &props ) +{ + // Restores the painting properties when destroyed + PainterSaver painterSaver( painter ); + + // The z coordinate to use (i.e., at what depth to draw the line) + const qreal z = props.depth / 2.0; + + // Projec the 2D points of the line in 3D + const QPointF deepP1 = projectPoint( line.p1(), z, props.angle ); + const QPointF deepP2 = projectPoint( line.p2(), z, props.angle ); + + // The drawn line with a width of 2px + QPolygonF threeDArea; + // The offset of the line "borders" from the center to each side + const QPointF offset( 0.0, 1.0 ); + threeDArea << deepP1 - offset << deepP2 - offset + << deepP1 + offset << deepP2 + offset << deepP1 - offset; + + painter->setPen( pen ); + painter->drawLine( QLineF( deepP1, deepP2 ) ); + + return threeDArea; +} + +/** + * Draws an ordinary line in 3D by expanding it in the z-axis by the given depth. + * + * @param line The line to draw + * @param brush The brush to fill the resulting polygon with + * @param pen The pen to paint the borders of the resulting polygon with + * @param props The 3D properties to draw the line with + * @return The 3D shape drawn + */ +QPolygonF StockDiagram::Private::ThreeDPainter::drawThreeDLine( const QLineF &line, const QBrush &brush, + const QPen &pen, const ThreeDProperties &props ) +{ + // Restores the painting properties when destroyed + PainterSaver painterSaver( painter ); + + const QPointF p1 = line.p1(); + const QPointF p2 = line.p2(); + + // Project the 2D points of the line in 3D + const QPointF deepP1 = projectPoint( p1, props.depth, props.angle ); + const QPointF deepP2 = projectPoint( p2, props.depth, props.angle ); + + // The result is a 3D representation of the 2D line + QPolygonF threeDArea; + threeDArea << p1 << p2 << deepP2 << deepP1 << p1; + + // Use shadow colors if ThreeDProperties::useShadowColors is set + // Note: Setting a new color on a brush or pen does not effect gradients or textures + if ( props.useShadowColors ) { + QBrush shadowBrush( brush ); + QPen shadowPen( pen ); + shadowBrush.setColor( calcShadowColor( brush.color(), props.angle ) ); + shadowPen.setColor( calcShadowColor( pen.color(), props.angle ) ); + painter->setBrush( shadowBrush ); + painter->setPen( shadowPen ); + } else { + painter->setBrush( brush ); + painter->setPen( pen ); + } + + painter->drawPolygon( threeDArea ); + + return threeDArea; +} + +/** + * Draws a 3D cuboid by extending a 2D rectangle in the z-axis + * + * @param rect The rectangle to draw + * @param brush The brush fill the surfaces of the cuboid with + * @param pen The pen to draw the edges with + * @param props The 3D properties to use for drawing the cuboid + * @return The drawn cuboid as a polygon + */ +QPolygonF StockDiagram::Private::ThreeDPainter::drawThreeDRect( const QRectF &rect, const QBrush &brush, + const QPen &pen, const ThreeDProperties &props ) +{ + // Restores the painting properties when destroyed + PainterSaver painterSaver( painter ); + + // Make sure that the top really is the top + const QRectF normalizedRect = rect.normalized(); + + // Calculate all the four sides of the rectangle + const QLineF topSide = QLineF( normalizedRect.topLeft(), normalizedRect.topRight() ); + const QLineF bottomSide = QLineF( normalizedRect.bottomLeft(), normalizedRect.bottomRight() ); + const QLineF leftSide = QLineF( normalizedRect.topLeft(), normalizedRect.bottomLeft() ); + const QLineF rightSide = QLineF( normalizedRect.topRight(), normalizedRect.bottomRight() ); + + QPolygonF drawnPolygon; + + // Shorter names are easier on the eyes + const qreal angle = props.angle; + + // Only top and right side is visible + if ( angle >= 0.0 && angle < 90.0 ) { + drawnPolygon = drawnPolygon.united( drawThreeDLine( topSide, brush, pen, props ) ); + drawnPolygon = drawnPolygon.united( drawThreeDLine( rightSide, brush, pen, props ) ); + // Only top and left side is visible + } else if ( angle >= 90.0 && angle < 180.0 ) { + drawnPolygon = drawnPolygon.united( drawThreeDLine( topSide, brush, pen, props ) ); + drawnPolygon = drawnPolygon.united( drawThreeDLine( leftSide, brush, pen, props ) ); + // Only bottom and left side is visible + } else if ( angle >= 180.0 && angle < 270.0 ) { + drawnPolygon = drawnPolygon.united( drawThreeDLine( bottomSide, brush, pen, props ) ); + drawnPolygon = drawnPolygon.united( drawThreeDLine( leftSide, brush, pen, props ) ); + // Only bottom and right side is visible + } else if ( angle >= 270.0 && angle <= 360.0 ) { + drawnPolygon = drawnPolygon.united( drawThreeDLine( bottomSide, brush, pen, props ) ); + drawnPolygon = drawnPolygon.united( drawThreeDLine( rightSide, brush, pen, props ) ); + } + + // Draw the front side + painter->setPen( pen ); + painter->setBrush( brush ); + painter->drawRect( normalizedRect ); + + return drawnPolygon; +} + + +StockDiagram::Private::Private() + : AbstractCartesianDiagram::Private() +{ +} + +StockDiagram::Private::Private( const Private& r ) + : AbstractCartesianDiagram::Private( r ) +{ +} + +StockDiagram::Private::~Private() +{ +} + +/** + * Projects a point onto the coordinate plane + * + * @param context The context to paint the point in + * @point The point to project onto the coordinate plane + * @return The projected point + */ +QPointF StockDiagram::Private::projectPoint( PaintContext *context, const QPointF &point ) const +{ + return context->coordinatePlane()->translate( QPointF( point.x() + 0.5, point.y() ) ); +} + +/** + * Projects a candlestick onto the coordinate plane + * + * @param context The context to paint the candlestick in + * @param low The + */ +QRectF StockDiagram::Private::projectCandlestick( PaintContext *context, const QPointF &open, const QPointF &close, qreal width ) const +{ + const QPointF leftHighPoint = context->coordinatePlane()->translate( QPointF( close.x() + 0.5 - width / 2.0, close.y() ) ); + const QPointF rightLowPoint = context->coordinatePlane()->translate( QPointF( open.x() + 0.5 + width / 2.0, open.y() ) ); + const QPointF rightHighPoint = context->coordinatePlane()->translate( QPointF( close.x() + 0.5 + width / 2.0, close.y() ) ); + + return QRectF( leftHighPoint, QSizeF( rightHighPoint.x() - leftHighPoint.x(), + rightLowPoint.y() - leftHighPoint.y() ) ); +} + +void StockDiagram::Private::drawOHLCBar( const CartesianDiagramDataCompressor::DataPoint &open, + const CartesianDiagramDataCompressor::DataPoint &high, + const CartesianDiagramDataCompressor::DataPoint &low, + const CartesianDiagramDataCompressor::DataPoint &close, + PaintContext *context ) +{ + // Note: A row in the model is a column in a StockDiagram + const int col = low.index.row(); + + StockBarAttributes attr = diagram->stockBarAttributes( col ); + ThreeDBarAttributes threeDAttr = diagram->threeDBarAttributes( col ); + const qreal tickLength = attr.tickLength(); + + const QPointF leftOpenPoint( open.key + 0.5 - tickLength, open.value ); + const QPointF rightOpenPoint( open.key + 0.5, open.value ); + const QPointF highPoint( high.key + 0.5, high.value ); + const QPointF lowPoint( low.key + 0.5, low.value ); + const QPointF leftClosePoint( close.key + 0.5, close.value ); + const QPointF rightClosePoint( close.key + 0.5 + tickLength, close.value ); + + bool reversedOrder = false; + // If 3D mode is enabled, we have to make sure the z-order is right + if ( threeDAttr.isEnabled() ) { + const int angle = threeDAttr.angle(); + // Z-order is from right to left + if ( ( angle >= 0 && angle < 90 ) || ( angle >= 180 && angle < 270 ) ) + reversedOrder = true; + // Z-order is from left to right + if ( ( angle >= 90 && angle < 180 ) || ( angle >= 270 && angle < 0 ) ) + reversedOrder = false; + } + + if ( reversedOrder ) { + if ( !open.hidden ) + drawLine( col, leftOpenPoint, rightOpenPoint, context ); // Open marker + if ( !low.hidden && !high.hidden ) + drawLine( col, lowPoint, highPoint, context ); // Low-High line + if ( !close.hidden ) + drawLine( col, leftClosePoint, rightClosePoint, context ); // Close marker + } else { + if ( !close.hidden ) + drawLine( col, leftClosePoint, rightClosePoint, context ); // Close marker + if ( !low.hidden && !high.hidden ) + drawLine( col, lowPoint, highPoint, context ); // Low-High line + if ( !open.hidden ) + drawLine( col, leftOpenPoint, rightOpenPoint, context ); // Open marker + } + + DataValueTextInfoList list; + if ( !open.hidden ) + appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( open.index ), 0, + PositionPoints( leftOpenPoint ), Position::South, Position::South, open.value ); + if ( !high.hidden ) + appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( high.index ), 0, + PositionPoints( highPoint ), Position::South, Position::South, high.value ); + if ( !low.hidden ) + appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( low.index ), 0, + PositionPoints( lowPoint ), Position::South, Position::South, low.value ); + if ( !close.hidden ) + appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( close.index ), 0, + PositionPoints( rightClosePoint ), Position::South, Position::South, close.value ); + paintDataValueTextsAndMarkers( diagram, context, list, false ); +} + +/** + * Draws a line connecting the low and the high value of an OHLC chart + * + * @param low The low data point + * @param high The high data point + * @param context The context to draw the candlestick in + */ +void StockDiagram::Private::drawCandlestick( const CartesianDiagramDataCompressor::DataPoint &open, + const CartesianDiagramDataCompressor::DataPoint &high, + const CartesianDiagramDataCompressor::DataPoint &low, + const CartesianDiagramDataCompressor::DataPoint &close, + PaintContext *context ) +{ + PainterSaver painterSaver( context->painter() ); + + // Note: A row in the model is a column in a StockDiagram, and the other way around + const int row = low.index.row(); + const int col = low.index.column(); + + QPointF bottomCandlestickPoint; + QPointF topCandlestickPoint; + QBrush brush; + QPen pen; + bool drawLowerLine; + bool drawCandlestick = !open.hidden && !close.hidden; + bool drawUpperLine; + + // Find out if we need to paint a down-trend or up-trend candlestick + // and set brush and pen accordingly + // Also, determine what the top and bottom points of the candlestick are + if ( open.value <= close.value ) { + pen = diagram->upTrendCandlestickPen( row ); + brush = diagram->upTrendCandlestickBrush( row ); + bottomCandlestickPoint = QPointF( open.key, open.value ); + topCandlestickPoint = QPointF( close.key, close.value ); + drawLowerLine = !low.hidden && !open.hidden; + drawUpperLine = !low.hidden && !close.hidden; + } else { + pen = diagram->downTrendCandlestickPen( row ); + brush = diagram->downTrendCandlestickBrush( row ); + bottomCandlestickPoint = QPointF( close.key, close.value ); + topCandlestickPoint = QPointF( open.key, open.value ); + drawLowerLine = !low.hidden && !close.hidden; + drawUpperLine = !low.hidden && !open.hidden; + } + + StockBarAttributes attr = diagram->stockBarAttributes( col ); + ThreeDBarAttributes threeDAttr = diagram->threeDBarAttributes( col ); + + const QPointF lowPoint = projectPoint( context, QPointF( low.key, low.value ) ); + const QPointF highPoint = projectPoint( context, QPointF( high.key, high.value ) ); + const QLineF lowerLine = QLineF( lowPoint, projectPoint( context, bottomCandlestickPoint ) ); + const QLineF upperLine = QLineF( projectPoint( context, topCandlestickPoint ), highPoint ); + + // Convert the data point into coordinates on the coordinate plane + QRectF candlestick = projectCandlestick( context, bottomCandlestickPoint, + topCandlestickPoint, attr.candlestickWidth() ); + + // Remember the drawn polygon to add it to the ReverseMapper later + QPolygonF drawnPolygon; + + // Use the ThreeDPainter class to draw a 3D candlestick + if ( threeDAttr.isEnabled() ) { + ThreeDPainter threeDPainter( context->painter() ); + + ThreeDPainter::ThreeDProperties threeDProps; + threeDProps.depth = threeDAttr.depth(); + threeDProps.angle = threeDAttr.angle(); + threeDProps.useShadowColors = threeDAttr.useShadowColors(); + + // If the perspective angle is within [0,180], we paint from bottom to top, + // otherwise from top to bottom to ensure the correct z order + if ( threeDProps.angle > 0.0 && threeDProps.angle < 180.0 ) { + if ( drawLowerLine ) + drawnPolygon = threeDPainter.drawTwoDLine( lowerLine, pen, threeDProps ); + if ( drawCandlestick ) + drawnPolygon = threeDPainter.drawThreeDRect( candlestick, brush, pen, threeDProps ); + if ( drawUpperLine ) + drawnPolygon = threeDPainter.drawTwoDLine( upperLine, pen, threeDProps ); + } else { + if ( drawUpperLine ) + drawnPolygon = threeDPainter.drawTwoDLine( upperLine, pen, threeDProps ); + if ( drawCandlestick ) + drawnPolygon = threeDPainter.drawThreeDRect( candlestick, brush, pen, threeDProps ); + if ( drawLowerLine ) + drawnPolygon = threeDPainter.drawTwoDLine( lowerLine, pen, threeDProps ); + } + } else { + QPainter *const painter = context->painter(); + painter->setBrush( brush ); + painter->setPen( pen ); + if ( drawLowerLine ) + painter->drawLine( lowerLine ); + if ( drawUpperLine ) + painter->drawLine( upperLine ); + if ( drawCandlestick ) + painter->drawRect( candlestick ); + + // The 2D representation is the projected candlestick itself + drawnPolygon = candlestick; + + // FIXME: Add lower and upper line to reverse mapper + } + + DataValueTextInfoList list; + + if ( !low.hidden ) + appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( low.index ), 0, + PositionPoints( lowPoint ), Position::South, Position::South, low.value ); + if ( drawCandlestick ) { + // Both, the open as well as the close value are represented by this candlestick + reverseMapper.addPolygon( row, openValueColumn(), drawnPolygon ); + reverseMapper.addPolygon( row, closeValueColumn(), drawnPolygon ); + + appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( open.index ), 0, + PositionPoints( candlestick.bottomRight() ), Position::South, Position::South, open.value ); + appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( close.index ), 0, + PositionPoints( candlestick.topRight() ), Position::South, Position::South, close.value ); + } + if ( !high.hidden ) + appendDataValueTextInfoToList( diagram, list, diagram->attributesModel()->mapToSource( high.index ), 0, + PositionPoints( highPoint ), Position::South, Position::South, high.value ); + + paintDataValueTextsAndMarkers( diagram, context, list, false ); +} + +/** + * Draws a line connecting two points + * + * @param col The column of the diagram to paint the line in + * @param point1 The first point + * @param point2 The second point + * @param context The context to draw the low-high line in + */ +void StockDiagram::Private::drawLine( int col, const QPointF &point1, const QPointF &point2, PaintContext *context ) +{ + PainterSaver painterSaver( context->painter() ); + + // A row in the model is a column in the diagram + const int modelRow = col; + const int modelCol = 0; + + const QPen pen = diagram->pen( col ); + const QBrush brush = diagram->brush( col ); + const ThreeDBarAttributes threeDBarAttr = diagram->threeDBarAttributes( col ); + + QPointF transP1 = context->coordinatePlane()->translate( point1 ); + QPointF transP2 = context->coordinatePlane()->translate( point2 ); + QLineF line = QLineF( transP1, transP2 ); + + if ( threeDBarAttr.isEnabled() ) { + ThreeDPainter::ThreeDProperties threeDProps; + threeDProps.angle = threeDBarAttr.angle(); + threeDProps.depth = threeDBarAttr.depth(); + threeDProps.useShadowColors = threeDBarAttr.useShadowColors(); + + ThreeDPainter painter( context->painter() ); + reverseMapper.addPolygon( modelCol, modelRow, painter.drawThreeDLine( line, brush, pen, threeDProps ) ); + } else { + context->painter()->setPen( pen ); + //context->painter()->setBrush( brush ); + reverseMapper.addLine( modelCol, modelRow, transP1, transP2 ); + context->painter()->drawLine( line ); + } +} + +/** + * Returns the column of the open value in the model + * + * @return The column of the open value + */ +int StockDiagram::Private::openValueColumn() const +{ + // Return an invalid column if diagram has no open values + return type == HighLowClose ? -1 : 0; +} + +/** + * Returns the column of the high value in the model + * + * @return The column of the high value + */ +int StockDiagram::Private::highValueColumn() const +{ + return type == HighLowClose ? 0 : 1; +} + +/** + * Returns the column of the low value in the model + * + * @return The column of the low value + */ +int StockDiagram::Private::lowValueColumn() const +{ + return type == HighLowClose ? 1 : 2; +} + +/** + * Returns the column of the close value in the model + * + * @return The column of the close value + */ +int StockDiagram::Private::closeValueColumn() const +{ + return type == HighLowClose ? 2 : 3; +} + diff --git a/libkdchart/src/KDChartStockDiagram_p.h b/libkdchart/src/KDChartStockDiagram_p.h new file mode 100644 index 0000000..6e80566 --- /dev/null +++ b/libkdchart/src/KDChartStockDiagram_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHART_STOCK_DIAGRAM_P_H +#define KDCHART_STOCK_DIAGRAM_P_H + +#include "KDChartStockDiagram.h" +#include "KDChartAbstractCartesianDiagram_p.h" +#include "KDChartCartesianDiagramDataCompressor_p.h" +#include "KDChartPaintContext.h" + +namespace KDChart { + +class StockDiagram::Private : public AbstractCartesianDiagram::Private +{ + friend class StockDiagram; + +public: + Private(); + Private( const Private& r ); + ~Private(); + + Type type; + StockDiagram *diagram; + + QBrush upTrendCandlestickBrush; + QBrush downTrendCandlestickBrush; + QPen upTrendCandlestickPen; + QPen downTrendCandlestickPen; + + QMap upTrendCandlestickBrushes; + QMap downTrendCandlestickBrushes; + QMap upTrendCandlestickPens; + QMap downTrendCandlestickPens; + + QPen lowHighLinePen; + QMap lowHighLinePens; + + + void drawOHLCBar( const CartesianDiagramDataCompressor::DataPoint &open, + const CartesianDiagramDataCompressor::DataPoint &high, + const CartesianDiagramDataCompressor::DataPoint &low, + const CartesianDiagramDataCompressor::DataPoint &close, + PaintContext *context ); + void drawHLCBar( const CartesianDiagramDataCompressor::DataPoint &high, + const CartesianDiagramDataCompressor::DataPoint &low, + const CartesianDiagramDataCompressor::DataPoint &close, + PaintContext *context ); + void drawCandlestick( const CartesianDiagramDataCompressor::DataPoint &open, + const CartesianDiagramDataCompressor::DataPoint &high, + const CartesianDiagramDataCompressor::DataPoint &low, + const CartesianDiagramDataCompressor::DataPoint &close, + PaintContext *context ); + +private: + void drawLine( int col, const QPointF &point1, const QPointF &p2, PaintContext *context ); + QPointF projectPoint( PaintContext *context, const QPointF &point ) const; + QRectF projectCandlestick( PaintContext *context, const QPointF &open, const QPointF &close, qreal width ) const; + int openValueColumn() const; + int highValueColumn() const; + int lowValueColumn() const; + int closeValueColumn() const; + + class ThreeDPainter; +}; + +KDCHART_IMPL_DERIVED_DIAGRAM( StockDiagram, AbstractCartesianDiagram, CartesianCoordinatePlane ) + +} + +#endif // KDCHART_STOCK_DIAGRAM_P_H + diff --git a/libkdchart/src/KDChartTextArea.cpp b/libkdchart/src/KDChartTextArea.cpp new file mode 100644 index 0000000..9d76912 --- /dev/null +++ b/libkdchart/src/KDChartTextArea.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartTextArea.h" +#include "KDChartTextArea_p.h" + +#include + +#include +#include + +#include + + +using namespace KDChart; + +TextArea::Private::Private() : + AbstractAreaBase::Private() +{ + // this bloc left empty intentionally +} + + +TextArea::Private::~Private() +{ + // this bloc left empty intentionally +} + + +TextArea::TextArea() + : QObject() + , KDChart::AbstractAreaBase() + , KDChart::TextLayoutItem() +{ + // this bloc left empty intentionally +} + +TextArea::~TextArea() +{ + // this bloc left empty intentionally +} + + +void TextArea::init() +{ + // this bloc left empty intentionally +} + +void TextArea::paintIntoRect( QPainter& painter, const QRect& rect ) +{ + const QRect oldGeometry( geometry() ); + if( oldGeometry != rect ) + setGeometry( rect ); + painter.translate( rect.left(), rect.top() ); + paintAll( painter ); + painter.translate( -rect.left(), -rect.top() ); + if( oldGeometry != rect ) + setGeometry( oldGeometry ); +} + +void TextArea::paintAll( QPainter& painter ) +{ + // Paint the background and frame + paintBackground( painter, geometry() ); + paintFrame( painter, geometry() ); + + // temporarily adjust the widget size, to be sure all content gets calculated + // to fit into the inner rectangle + const QRect oldGeometry( areaGeometry() ); + QRect inner( innerRect() ); + inner.moveTo( + oldGeometry.left() + inner.left(), + oldGeometry.top() + inner.top() ); + const bool needAdjustGeometry = oldGeometry != inner; + if( needAdjustGeometry ) + setGeometry( inner ); + paint( &painter ); + if( needAdjustGeometry ) + setGeometry( oldGeometry ); + //qDebug() << "TextAreaWidget::paintAll() done."; +} + +QRect TextArea::areaGeometry() const +{ + return geometry(); +} + +void TextArea::positionHasChanged() +{ + emit positionChanged( this ); +} + diff --git a/libkdchart/src/KDChartTextArea.h b/libkdchart/src/KDChartTextArea.h new file mode 100644 index 0000000..7c74639 --- /dev/null +++ b/libkdchart/src/KDChartTextArea.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHART_TEXT_AREA_H +#define KDCHART_TEXT_AREA_H + +#include + +#include "KDChartGlobal.h" +#include "KDChartAbstractAreaBase.h" +#include "KDChartLayoutItems.h" + +namespace KDChart { + + +/** + * @class TextArea KDChartTextArea.h + * @brief A text area in the chart with a background, a frame, etc. + * + * TextArea is the base class for all text containing non-widget chart elements + * that have a set of background attributes and frame attributes, such as + * headers or footers. + * + * @note This class inherits from AbstractAreaBase, TextLayoutItem, QObject. + * The reason for this tripple inheritance is that neither AbstractAreaBase nor + * TextLayoutItem are QObject. + */ +class KDCHART_EXPORT TextArea : public QObject, public AbstractAreaBase, public TextLayoutItem +{ + Q_OBJECT + + Q_DISABLE_COPY( TextArea ) + KDCHART_DECLARE_PRIVATE_DERIVED( TextArea ) + + +public: + virtual ~TextArea() ; + +// virtual TextArea * clone() const = 0; + /** + * @brief Draws the background and frame, then calls paint(). + * + * In most cases there is no need to overwrite this method in a derived + * class, but you would overwrite TextLayoutItem::paint() instead. + */ + virtual void paintIntoRect( QPainter& painter, const QRect& rect ); + + /** + * Call paintAll, if you want the background and the frame to be drawn + * before the normal paint() is invoked automatically. + */ + void paintAll( QPainter& painter ); + +protected: + TextArea(); + virtual QRect areaGeometry() const; + virtual void positionHasChanged(); + +Q_SIGNALS: + void positionChanged( TextArea * ); + + //KDCHART_DECLARE_PRIVATE_DERIVED(TextArea) +}; // End of class TextArea + +} +#endif // KDCHART_TEXT_AREA_H diff --git a/libkdchart/src/KDChartTextArea_p.h b/libkdchart/src/KDChartTextArea_p.h new file mode 100644 index 0000000..b05a744 --- /dev/null +++ b/libkdchart/src/KDChartTextArea_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHART_TEXT_AREA_P_H +#define KDCHART_TEXT_AREA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +/** \file KDChartTextArea_p.h + * \internal + */ + +#include "KDChartTextArea.h" +#include "KDChartAbstractAreaBase_p.h" + +#include + + +namespace KDChart { + +/** + * \internal + */ + class TextArea::Private : public AbstractAreaBase::Private + { + friend class TextArea; + public: + explicit Private(); + virtual ~Private(); + + Private( const Private& rhs ) : + AbstractAreaBase::Private( rhs ) + { + // Just for consistency + } + }; + + + inline TextArea::TextArea( Private * p ) + : QObject(), AbstractAreaBase( p ), TextLayoutItem() + { + init(); + } + inline TextArea::Private * TextArea::d_func() + { + return static_cast( AbstractAreaBase::d_func() ); + } + inline const TextArea::Private * TextArea::d_func() const + { + return static_cast( AbstractAreaBase::d_func() ); + } + +} + +#endif /* KDCHART_TEXT_AREA_P_H */ + diff --git a/libkdchart/src/KDChartTextAttributes.cpp b/libkdchart/src/KDChartTextAttributes.cpp new file mode 100644 index 0000000..4d75ffd --- /dev/null +++ b/libkdchart/src/KDChartTextAttributes.cpp @@ -0,0 +1,265 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartTextAttributes.h" +#include +#include +#include +#include + +#include +#include + +#define d d_func() + +using namespace KDChart; + +class TextAttributes::Private +{ + friend class TextAttributes; +public: + Private(); +private: + bool visible; + QFont font; + mutable QFont cachedFont; + mutable qreal cachedFontSize; + Measure fontSize; + Measure minimalFontSize; + bool autoRotate; + bool autoShrink; + int rotation; + QPen pen; +}; + +TextAttributes::Private::Private() +{ + cachedFontSize = -1.0; +} + + +TextAttributes::TextAttributes() + : _d( new Private() ) +{ + setVisible( true ); + setFont( QApplication::font() ); + setAutoRotate( false ); + setAutoShrink( false ); + setRotation( 0 ); + setPen( QPen( Qt::black ) ); +} + +TextAttributes::TextAttributes( const TextAttributes& r ) + : _d( new Private( *r.d ) ) +{ + +} + +TextAttributes & TextAttributes::operator=( const TextAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +TextAttributes::~TextAttributes() +{ + delete _d; _d = 0; +} + + +bool TextAttributes::operator==( const TextAttributes& r ) const +{ + // the following works around a bug in gcc 4.3.2 + // causing StyleHint to be set to Zero when copying a QFont + const QFont myFont( font() ); + QFont r_font( r.font() ); + r_font.setStyleHint( myFont.styleHint(), myFont.styleStrategy() ); + /* + qDebug() << "\nTextAttributes::operator== :" << ( isVisible() == r.isVisible()) + << " font:"<<(myFont == r_font) + << " fontSize:"<<(fontSize() == r.fontSize()) + << " minimalFontSize:"<<(minimalFontSize() == r.minimalFontSize()) + << (autoRotate() == r.autoRotate()) + << (autoShrink() == r.autoShrink()) + << (rotation() == rotation()) + << (pen() == r.pen()); + */ + return ( isVisible() == r.isVisible() && + myFont == r_font && + fontSize() == r.fontSize() && + minimalFontSize() == r.minimalFontSize() && + autoRotate() == r.autoRotate() && + autoShrink() == r.autoShrink() && + rotation() == r.rotation() && + pen() == r.pen() ); +} + + +void TextAttributes::setVisible( bool visible ) +{ + d->visible = visible; +} + +bool TextAttributes::isVisible() const +{ + return d->visible; +} + +void TextAttributes::setFont( const QFont& font ) +{ + d->font = font; + d->cachedFont = font; // note: we do not set the font's size here, but in calculatedFont() + //qDebug() << "resetting cached font size"; + d->cachedFontSize = -1.0; +} + +QFont TextAttributes::font() const +{ + return d->font; +} + +void TextAttributes::setFontSize( const Measure & measure ) +{ + d->fontSize = measure; +} + +Measure TextAttributes::fontSize() const +{ + return d->fontSize; +} + +void TextAttributes::setMinimalFontSize( const Measure & measure ) +{ + d->minimalFontSize = measure; +} + +Measure TextAttributes::minimalFontSize() const +{ + return d->minimalFontSize; +} + +bool TextAttributes::hasAbsoluteFontSize() const +{ + return d->fontSize.calculationMode() == KDChartEnums::MeasureCalculationModeAbsolute + && d->minimalFontSize.calculationMode() == KDChartEnums::MeasureCalculationModeAbsolute; +} + + +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) +const +#endif +qreal TextAttributes::calculatedFontSize( + const QObject* autoReferenceArea, + KDChartEnums::MeasureOrientation autoReferenceOrientation ) const +{ + const qreal normalSize = fontSize().calculatedValue( autoReferenceArea, autoReferenceOrientation ); + const qreal minimalSize = minimalFontSize().calculatedValue( autoReferenceArea, autoReferenceOrientation ); + //qDebug() << "TextAttributes::calculatedFontSize() finds" << normalSize << "and" << minimalSize; + return qMax( normalSize, minimalSize ); +} + + +const QFont TextAttributes::calculatedFont( + const QObject* autoReferenceArea, + KDChartEnums::MeasureOrientation autoReferenceOrientation ) const +{ + const CartesianCoordinatePlane* plane = dynamic_cast( autoReferenceArea ); + + static qreal size = calculatedFontSize( autoReferenceArea, autoReferenceOrientation ); + if ( plane ) + { + if(!plane->hasFixedDataCoordinateSpaceRelation()) + size = calculatedFontSize( autoReferenceArea, autoReferenceOrientation ); + } + else + size = calculatedFontSize( autoReferenceArea, autoReferenceOrientation ); + + if( size > 0.0 && d->cachedFontSize != size ){ + //qDebug() << "new into the cache:" << size; + d->cachedFontSize = size; + d->cachedFont.setPointSizeF( d->cachedFontSize ); + } + + return d->cachedFont; +} + + +void TextAttributes::setAutoRotate( bool autoRotate ) +{ + d->autoRotate = autoRotate; +} + +bool TextAttributes::autoRotate() const +{ + return d->autoRotate; +} + +void TextAttributes::setAutoShrink( bool autoShrink ) +{ + d->autoShrink = autoShrink; +} + +bool TextAttributes::autoShrink() const +{ + return d->autoShrink; +} + +void TextAttributes::setRotation( int rotation ) +{ + d->rotation = rotation; +} + +int TextAttributes::rotation() const +{ + return d->rotation; +} + +void TextAttributes::setPen( const QPen& pen ) +{ + d->pen = pen; +} + +QPen TextAttributes::pen() const +{ + return d->pen; +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::TextAttributes& ta) +{ + dbg << "KDChart::TextAttributes(" + << "visible="< +#include +#include "KDChartGlobal.h" +#include "KDChartMeasure.h" + +class QPen; +class QFont; + +namespace KDChart { + + /** + * \brief A set of text attributes. + * + * TextAttributes encapsulates settings that have to do with + * text. This includes font, fontsize, color, whether the text + * is rotated, etc + */ +class KDCHART_EXPORT TextAttributes +{ +public: + TextAttributes(); + TextAttributes( const TextAttributes& ); + TextAttributes &operator= ( const TextAttributes& ); + bool operator==( const TextAttributes& ) const; + inline bool operator!=( const TextAttributes& other ) const + { return !operator==(other); } + + ~TextAttributes(); + + /** + * Set whether the text is to be rendered at all. + * \param visible Whether the text is visible. + */ + void setVisible( bool visible ); + + /** + * \return Whether the text is visible. + */ + bool isVisible() const; + + /** + * Set the font to be used for rendering the text. + * + * \note All of the font's attributes will be used - except of its size! + * To specify the size please use setFontSize (or setMinimalFontSize, resp.) + * + * \param font The font to use. + * + * \sa setFontSize, setMinimalFontSize + */ + void setFont( const QFont& font ); + + /** + * \return The font that is used for rendering text. + */ + QFont font() const; + + /** + * Set the size of the font used for rendering text. + * \param measure The measure to use. + * \see Measure + */ + void setFontSize( const Measure & measure ); + + /** + * \return The measure used for the font size. + */ + Measure fontSize() const; + + /** + * Set the minimal size of the font used for rendering text. + * \param measure The measure to use. + * \see Measure + */ + void setMinimalFontSize( const Measure & measure ); + + /** + * \return The measure used for the minimal font size. + */ + Measure minimalFontSize() const; + + /** + * \brief Returns the font size that is used at drawing time. + * + * This method is called at drawing time. It returns the + * font size as it is used for rendering text, taking into account + * any measures that were set via setFontSize and/or setMinimalFontSize. + */ +#if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE) + const qreal calculatedFontSize( +#else + qreal calculatedFontSize( +#endif + const QObject* autoReferenceArea, + KDChartEnums::MeasureOrientation autoReferenceOrientation ) const; + + /** + * \brief Returns the font in the size that is used at drawing time. + * + * This method is called at drawing time. It returns the + * font as it is used for rendering text, taking into account + * any measures that were set via setFontSize and/or setMinimalFontSize. + */ + const QFont calculatedFont( + const QObject* autoReferenceArea, + KDChartEnums::MeasureOrientation autoReferenceOrientation ) const; + + /** + * \return Whether the text has an absolute font size set. + */ + bool hasAbsoluteFontSize() const; + + /** + * Set whether the text should be automatically rotated as + * needed when space is constraint. + * \param autoRotate Whether text should be automatically rotated. + */ + void setAutoRotate( bool autoRotate ); + + /** + * \return Whether text is automatically rotated when space is + * constrained. + */ + bool autoRotate() const; + + /** + * Set whether the text should automatically be shrunk, if + * space is constraint. + * \param autoShrink Whether text should be auto-shrunk. + */ + void setAutoShrink( bool autoShrink ); + + /** + * \return Whether text is automatically shrunk if space is + * constraint. + */ + bool autoShrink() const; + + /** + * Set the rotation angle to use for the text. + * + * \note For axis titles the rotation angle can be set to one of the + * following angles: 0, 90, 180, 270 + * Any other values specified will be replaced by the next smaller + * one of the allowed values, so no matter what you set the rotation + * will always be one of these four values. + * + * \param rotation The rotation angle. + */ + void setRotation( int rotation ); + + /** + * \return The rotation angle used for rendering the text. + */ + int rotation() const; + + /** + * Set the pen to use for rendering the text. + * \param pen The pen to use. + */ + void setPen( const QPen& pen ); + + /** + * \return The pen used for rendering the text. + */ + QPen pen() const; + + // FIXME KDChartEnums::TextLayoutPolicy? + +private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( TextAttributes ) + +}; // End of class TextAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::TextAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::TextAttributes ) +Q_DECLARE_METATYPE( KDChart::TextAttributes ) +Q_DECLARE_TYPEINFO( KDChart::TextAttributes, Q_MOVABLE_TYPE ); + +#endif // KDCHARTTEXTATTRIBUTES_H diff --git a/libkdchart/src/KDChartThreeDBarAttributes.cpp b/libkdchart/src/KDChartThreeDBarAttributes.cpp new file mode 100644 index 0000000..ad33b53 --- /dev/null +++ b/libkdchart/src/KDChartThreeDBarAttributes.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartThreeDBarAttributes.h" +#include "KDChartThreeDBarAttributes_p.h" + +#include + +#include + +#define d d_func() + +using namespace KDChart; + +ThreeDBarAttributes::Private::Private() + : useShadowColors( true ), + angle( 45 ) +{ +} + + +ThreeDBarAttributes::ThreeDBarAttributes() + : AbstractThreeDAttributes( new Private() ) +{ + +} + +ThreeDBarAttributes::ThreeDBarAttributes( const ThreeDBarAttributes& r ) + : AbstractThreeDAttributes( new Private( *r.d) ) +{ +} + +ThreeDBarAttributes& ThreeDBarAttributes::operator= ( const ThreeDBarAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +ThreeDBarAttributes::~ThreeDBarAttributes() +{ +} + +void ThreeDBarAttributes::init() +{ +} + + +bool ThreeDBarAttributes::operator==( const ThreeDBarAttributes& r ) const +{ + return ( useShadowColors() == r.useShadowColors() && + angle() == r.angle() && + AbstractThreeDAttributes::operator==(r)); +} + + + +void ThreeDBarAttributes::setUseShadowColors( bool shadowColors ) +{ + d->useShadowColors = shadowColors; +} + +bool ThreeDBarAttributes::useShadowColors() const +{ + return d->useShadowColors; +} + +void ThreeDBarAttributes::setAngle( uint threeDAngle ) +{ + d->angle = threeDAngle; +} + +uint ThreeDBarAttributes::angle() const +{ + return d->angle; +} + + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::ThreeDBarAttributes& a) +{ + dbg << "KDChart::ThreeDBarAttributes("; + dbg = operator <<( dbg, static_cast(a) ); + dbg << "useShadowColors="<< a.useShadowColors() + << "angle=" << a.angle() << ")"; + return dbg; +} +#endif /* QT_NO_DEBUG_STREAM */ + + diff --git a/libkdchart/src/KDChartThreeDBarAttributes.h b/libkdchart/src/KDChartThreeDBarAttributes.h new file mode 100644 index 0000000..0c3a6f1 --- /dev/null +++ b/libkdchart/src/KDChartThreeDBarAttributes.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTHREEDBARATTRIBUTES_H +#define KDCHARTTHREEDBARATTRIBUTES_H + +#include +#include "KDChartAbstractThreeDAttributes.h" +#include "KDChartGlobal.h" + +namespace KDChart { + + /** + * @brief A set of 3D bar attributes + */ + class KDCHART_EXPORT ThreeDBarAttributes : public AbstractThreeDAttributes + { + public: + ThreeDBarAttributes(); + ThreeDBarAttributes( const ThreeDBarAttributes& ); + ThreeDBarAttributes &operator= ( const ThreeDBarAttributes& ); + + ~ThreeDBarAttributes(); + + /* threeD Bars specific */ + void setUseShadowColors( bool useShadowColors ); + bool useShadowColors() const; + + //Pending Michel I am not sure this will be used + void setAngle( uint threeDAngle ); + uint angle() const; + + bool operator==( const ThreeDBarAttributes& ) const; + inline bool operator!=( const ThreeDBarAttributes& other ) const { return !operator==(other); } + + KDCHART_DECLARE_SWAP_DERIVED(ThreeDBarAttributes) + + KDCHART_DECLARE_PRIVATE_DERIVED(ThreeDBarAttributes) + + }; // End of class ThreeDBarAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::ThreeDBarAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +Q_DECLARE_METATYPE( KDChart::ThreeDBarAttributes ) +Q_DECLARE_TYPEINFO( KDChart::ThreeDBarAttributes, Q_MOVABLE_TYPE ); +KDCHART_DECLARE_SWAP_SPECIALISATION_DERIVED( KDChart::ThreeDBarAttributes ) + +#endif // KDCHARTTHREEDBARATTRIBUTES_H diff --git a/libkdchart/src/KDChartThreeDBarAttributes_p.h b/libkdchart/src/KDChartThreeDBarAttributes_p.h new file mode 100644 index 0000000..285c9fb --- /dev/null +++ b/libkdchart/src/KDChartThreeDBarAttributes_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTHREEDBARATTRIBUTES_P_H +#define KDCHARTTHREEDBARATTRIBUTES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include + + +namespace KDChart { + +/** + * \internal + */ +class ThreeDBarAttributes::Private : public AbstractThreeDAttributes::Private +{ + friend class ThreeDBarAttributes; +public: + Private(); + +private: + bool useShadowColors; + uint angle; +}; + +KDCHART_DERIVED_PRIVATE_FOOTER(ThreeDBarAttributes, AbstractThreeDAttributes) + +} + + +#endif // KDCHARTTHREEDBARATTRIBUTES_P_H diff --git a/libkdchart/src/KDChartThreeDLineAttributes.cpp b/libkdchart/src/KDChartThreeDLineAttributes.cpp new file mode 100644 index 0000000..1f21da6 --- /dev/null +++ b/libkdchart/src/KDChartThreeDLineAttributes.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartThreeDLineAttributes.h" +#include "KDChartThreeDLineAttributes_p.h" + +#include + +#include + +#define d d_func() + +using namespace KDChart; + +ThreeDLineAttributes::Private::Private() + : lineXRotation( 15 ), + lineYRotation( 15 ) +{ +} + + +ThreeDLineAttributes::ThreeDLineAttributes() + : AbstractThreeDAttributes( new Private() ) +{ + +} + +ThreeDLineAttributes::ThreeDLineAttributes( const ThreeDLineAttributes& r ) + : AbstractThreeDAttributes( new Private( *r.d) ) +{ +} + +ThreeDLineAttributes& ThreeDLineAttributes::operator= ( const ThreeDLineAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +ThreeDLineAttributes::~ThreeDLineAttributes() +{ +} + +void ThreeDLineAttributes::init() +{ +} + + +bool ThreeDLineAttributes::operator==( const ThreeDLineAttributes& r ) const +{ + return ( lineXRotation() == r.lineXRotation() && + lineYRotation() == r.lineYRotation() && + AbstractThreeDAttributes::operator==(r)); +} + + + +void ThreeDLineAttributes::setLineXRotation( const uint degrees ) +{ + d->lineXRotation = degrees; +} + +uint ThreeDLineAttributes::lineXRotation() const +{ + return d->lineXRotation; +} + +void ThreeDLineAttributes::setLineYRotation( const uint degrees ) +{ + d->lineYRotation = degrees; +} + +uint ThreeDLineAttributes::lineYRotation() const +{ + return d->lineYRotation; +} + + +#if !defined(QT_NO_DEBUG_STREAM) + +QDebug operator<<(QDebug dbg, const KDChart::ThreeDLineAttributes& a) +{ + dbg << "KDChart::ThreeDLineAttributes("; + dbg = operator <<( dbg, static_cast(a) ); + dbg << " lineXRotation="<< a.lineXRotation() + << " lineYRotation="<< a.lineYRotation() + << ")"; + return dbg; +} +#endif /* QT_NO_DEBUG_STREAM */ + diff --git a/libkdchart/src/KDChartThreeDLineAttributes.h b/libkdchart/src/KDChartThreeDLineAttributes.h new file mode 100644 index 0000000..96dd41e --- /dev/null +++ b/libkdchart/src/KDChartThreeDLineAttributes.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTHREEDLINEATTRIBUTES_H +#define KDCHARTTHREEDLINEATTRIBUTES_H + +#include +#include "KDChartAbstractThreeDAttributes.h" +#include "KDChartGlobal.h" + +namespace KDChart { + + /** + * @brief A set of 3D line attributes + */ + class KDCHART_EXPORT ThreeDLineAttributes : public AbstractThreeDAttributes + { + public: + ThreeDLineAttributes(); + ThreeDLineAttributes( const ThreeDLineAttributes& ); + ThreeDLineAttributes &operator= ( const ThreeDLineAttributes& ); + + ~ThreeDLineAttributes(); + + /* threeD lines specific */ + void setLineXRotation( const uint degrees ); + uint lineXRotation() const; + void setLineYRotation( const uint degrees ); + uint lineYRotation() const; + + bool operator==( const ThreeDLineAttributes& ) const; + inline bool operator!=( const ThreeDLineAttributes& other ) const { return !operator==(other); } + + KDCHART_DECLARE_SWAP_DERIVED(ThreeDLineAttributes) + + KDCHART_DECLARE_PRIVATE_DERIVED(ThreeDLineAttributes) + + }; // End of class ThreeDLineAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::ThreeDLineAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +Q_DECLARE_METATYPE( KDChart::ThreeDLineAttributes ) +Q_DECLARE_TYPEINFO( KDChart::ThreeDLineAttributes, Q_MOVABLE_TYPE ); +KDCHART_DECLARE_SWAP_SPECIALISATION_DERIVED( KDChart::ThreeDLineAttributes ) + + +#endif // KDCHARTTHREEDLINEATTRIBUTES_H diff --git a/libkdchart/src/KDChartThreeDLineAttributes_p.h b/libkdchart/src/KDChartThreeDLineAttributes_p.h new file mode 100644 index 0000000..28cadea --- /dev/null +++ b/libkdchart/src/KDChartThreeDLineAttributes_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTHREEDLINESATTRIBUTES_P_H +#define KDCHARTTHREEDLINESATTRIBUTES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include + + +namespace KDChart { + +/** + * \internal + */ +class ThreeDLineAttributes::Private : public AbstractThreeDAttributes::Private +{ + friend class ThreeDLineAttributes; +public: + Private(); + +private: + //ThreeDLines + uint lineXRotation; + uint lineYRotation; +}; + +KDCHART_DERIVED_PRIVATE_FOOTER(ThreeDLineAttributes, AbstractThreeDAttributes) + +} + + +#endif // KDCHARTTHREEDLINESATTRIBUTES_P_H diff --git a/libkdchart/src/KDChartThreeDPieAttributes.cpp b/libkdchart/src/KDChartThreeDPieAttributes.cpp new file mode 100644 index 0000000..448bdbb --- /dev/null +++ b/libkdchart/src/KDChartThreeDPieAttributes.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartThreeDPieAttributes.h" +#include "KDChartThreeDPieAttributes_p.h" + +#include + +#include + +#define d d_func() + +using namespace KDChart; + +ThreeDPieAttributes::Private::Private() + : useShadowColors( true ) +{ +} + + +ThreeDPieAttributes::ThreeDPieAttributes() + : AbstractThreeDAttributes( new Private() ) +{ + +} + +ThreeDPieAttributes::ThreeDPieAttributes( const ThreeDPieAttributes& r ) + : AbstractThreeDAttributes( new Private( *r.d) ) +{ +} + +ThreeDPieAttributes& ThreeDPieAttributes::operator= ( const ThreeDPieAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +ThreeDPieAttributes::~ThreeDPieAttributes() +{ +} + +void ThreeDPieAttributes::init() +{ +} + + +bool ThreeDPieAttributes::operator==( const ThreeDPieAttributes& r ) const +{ + return ( useShadowColors() == r.useShadowColors() && + AbstractThreeDAttributes::operator==(r)); +} + + + +void ThreeDPieAttributes::setUseShadowColors( bool shadowColors ) +{ + d->useShadowColors = shadowColors; +} + +bool ThreeDPieAttributes::useShadowColors() const +{ + return d->useShadowColors; +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::ThreeDPieAttributes& a) +{ + dbg << "KDChart::ThreeDPieAttributes("; + dbg = operator <<( dbg, static_cast(a) ); + dbg << "useShadowColors="<< a.useShadowColors() << ")"; + return dbg; +} +#endif /* QT_NO_DEBUG_STREAM */ + diff --git a/libkdchart/src/KDChartThreeDPieAttributes.h b/libkdchart/src/KDChartThreeDPieAttributes.h new file mode 100644 index 0000000..d3b0841 --- /dev/null +++ b/libkdchart/src/KDChartThreeDPieAttributes.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTHREEDPIEATTRIBUTES_H +#define KDCHARTTHREEDPIEATTRIBUTES_H + +#include +#include "KDChartAbstractThreeDAttributes.h" +#include "KDChartGlobal.h" + +namespace KDChart { + + /** + * @brief A set of 3D pie attributes + */ + class KDCHART_EXPORT ThreeDPieAttributes : public AbstractThreeDAttributes + { + public: + ThreeDPieAttributes(); + ThreeDPieAttributes( const ThreeDPieAttributes& ); + ThreeDPieAttributes &operator= ( const ThreeDPieAttributes& ); + + ~ThreeDPieAttributes(); + + /* threeD Pies specific */ + void setUseShadowColors( bool useShadowColors ); + bool useShadowColors() const; + + bool operator==( const ThreeDPieAttributes& ) const; + inline bool operator!=( const ThreeDPieAttributes& other ) const { return !operator==(other); } + + + KDCHART_DECLARE_SWAP_DERIVED(ThreeDPieAttributes) + +private: + KDCHART_DECLARE_PRIVATE_DERIVED(ThreeDPieAttributes) + + }; // End of class ThreeDPieAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::ThreeDPieAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +Q_DECLARE_METATYPE( KDChart::ThreeDPieAttributes ) +Q_DECLARE_TYPEINFO( KDChart::ThreeDPieAttributes, Q_MOVABLE_TYPE ); +KDCHART_DECLARE_SWAP_SPECIALISATION_DERIVED( KDChart::ThreeDPieAttributes ) + +#endif // KDCHARTTHREEDPIEATTRIBUTES_H diff --git a/libkdchart/src/KDChartThreeDPieAttributes_p.h b/libkdchart/src/KDChartThreeDPieAttributes_p.h new file mode 100644 index 0000000..dbab04b --- /dev/null +++ b/libkdchart/src/KDChartThreeDPieAttributes_p.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTHREEDPIEATTRIBUTES_P_H +#define KDCHARTTHREEDPIEATTRIBUTES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include + + +namespace KDChart { + +/** +* \internal +*/ +class ThreeDPieAttributes::Private : public AbstractThreeDAttributes::Private +{ + friend class ThreeDPieAttributes; +public: + Private(); + +private: + bool useShadowColors; +}; + +KDCHART_DERIVED_PRIVATE_FOOTER(ThreeDPieAttributes, AbstractThreeDAttributes) + +} + + +#endif // KDCHARTTHREEDPIEATTRIBUTES_P_H diff --git a/libkdchart/src/KDChartValueTrackerAttributes.cpp b/libkdchart/src/KDChartValueTrackerAttributes.cpp new file mode 100644 index 0000000..57aca32 --- /dev/null +++ b/libkdchart/src/KDChartValueTrackerAttributes.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartValueTrackerAttributes.h" + +#include +#include +#include +#include + +#define d d_func() + +using namespace KDChart; + +class ValueTrackerAttributes::Private +{ + friend class ValueTrackerAttributes; + public: + Private(); + private: + QPen pen; + QSizeF markerSize; + bool enabled; + QBrush areaBrush; +}; + +ValueTrackerAttributes::Private::Private() + : pen( QPen( QColor( 80, 80, 80, 200 ) ) ), + markerSize( QSizeF( 6.0, 6.0 ) ), + enabled( false ), + areaBrush( QBrush() ) +{ +} + + +ValueTrackerAttributes::ValueTrackerAttributes() + : _d( new Private() ) +{ +} + +ValueTrackerAttributes::ValueTrackerAttributes( const ValueTrackerAttributes& r ) + : _d( new Private( *r.d ) ) +{ +} + +ValueTrackerAttributes & ValueTrackerAttributes::operator=( const ValueTrackerAttributes& r ) +{ + if( this == &r ) + return *this; + + *d = *r.d; + + return *this; +} + +ValueTrackerAttributes::~ValueTrackerAttributes() +{ + delete _d; _d = 0; +} + + +bool ValueTrackerAttributes::operator==( const ValueTrackerAttributes& r ) const +{ + return ( pen() == r.pen() && + areaBrush() == r.areaBrush() && + markerSize() == r.markerSize() && + isEnabled() == r.isEnabled() ); +} + +void ValueTrackerAttributes::setPen( const QPen& pen ) +{ + d->pen = pen; +} + +QPen ValueTrackerAttributes::pen() const +{ + return d->pen; +} + +void ValueTrackerAttributes::setAreaBrush( const QBrush& brush ) +{ + d->areaBrush = brush; +} + +QBrush ValueTrackerAttributes::areaBrush() const +{ + return d->areaBrush; +} + +void ValueTrackerAttributes::setMarkerSize( const QSizeF& size ) +{ + d->markerSize = size; +} + +QSizeF ValueTrackerAttributes::markerSize() const +{ + return d->markerSize; +} + +void ValueTrackerAttributes::setEnabled( bool enabled ) +{ + d->enabled = enabled; +} + +bool ValueTrackerAttributes::isEnabled() const +{ + return d->enabled; +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug dbg, const KDChart::ValueTrackerAttributes& va) +{ + dbg << "KDChart::ValueTrackerAttributes(" + << "pen="< +#include +#include "KDChartGlobal.h" + +namespace KDChart { + + /** + * \class ValueTrackerAttributes KDChartValueTrackerAttributes.h KDChartValueTrackerAttributes + * \brief Cell-specific attributes regarding value tracking + * + * ValueTrackerAttributes groups the properties regarding + * value tracking, and how it is displayed. + * Value tracking can be used to emphasize on one or several + * specific points in a line diagram. + */ + + class KDCHART_EXPORT ValueTrackerAttributes + { + public: + ValueTrackerAttributes(); + ValueTrackerAttributes( const ValueTrackerAttributes& ); + ValueTrackerAttributes &operator= ( const ValueTrackerAttributes& ); + + ~ValueTrackerAttributes(); + + /** Set the pen the value tracking lines and markers will be drawn with + * \param pen The pen the lines and markers will be drawn with + */ + void setPen( const QPen& pen ); + + /** + * @return The pen the lines and markers are drawn with + */ + QPen pen() const; + + /** Set the brush the area below the value tracking + * lines should be filled with. Default is a black brush + * with the style Qt::NoBrush. + * \param brush The brush the area should be filled with + */ + void setAreaBrush( const QBrush& brush ); + + /** + * @return The brush the area below the value tracking lines is filled with + */ + QBrush areaBrush() const; + + /** Set the size of the markers. This includes both the arrows at + * the axises and the circle at the data point. + * \param size The size of the markers + */ + void setMarkerSize( const QSizeF& size ); + + /** + * @return The size of the markers + */ + QSizeF markerSize() const; + + /** Set whether value tracking should be enabled for a specific + * index or not + * \param enabled Whether value tracking should be enabled or not + */ + void setEnabled( bool enabled ); + + /** + * @return Whether value tracking is enabled or not + */ + bool isEnabled() const; + + bool operator==( const ValueTrackerAttributes& ) const; + inline bool operator!=( const ValueTrackerAttributes& other ) const { return !operator==(other); } + + private: + KDCHART_DECLARE_PRIVATE_BASE_VALUE( ValueTrackerAttributes ) + }; // End of class ValueTrackerAttributes + +} + +#if !defined(QT_NO_DEBUG_STREAM) +KDCHART_EXPORT QDebug operator<<(QDebug, const KDChart::ValueTrackerAttributes& ); +#endif /* QT_NO_DEBUG_STREAM */ + +Q_DECLARE_METATYPE( KDChart::ValueTrackerAttributes ) +KDCHART_DECLARE_SWAP_SPECIALISATION( KDChart::ValueTrackerAttributes ) +Q_DECLARE_TYPEINFO( KDChart::ValueTrackerAttributes, Q_MOVABLE_TYPE ); + +#endif // KDCHARTVALUETRACKERATTRIBUTES_H diff --git a/libkdchart/src/KDChartWidget.cpp b/libkdchart/src/KDChartWidget.cpp new file mode 100644 index 0000000..193269a --- /dev/null +++ b/libkdchart/src/KDChartWidget.cpp @@ -0,0 +1,633 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define d d_func() + +using namespace KDChart; + +Widget::Private::Private( Widget * qq ) + : q( qq ), + layout( q ), + m_model( q ), + m_chart( q ), + m_cartPlane( &m_chart ), + m_polPlane( &m_chart ), + usedDatasetWidth( 0 ) +{ + KDAB_SET_OBJECT_NAME( layout ); + KDAB_SET_OBJECT_NAME( m_model ); + KDAB_SET_OBJECT_NAME( m_chart ); + + layout.addWidget( &m_chart ); +} + +Widget::Private::~Private() {} + + +/** +* \class Widget KDChartWidget.h +* \brief The KDChart widget for usage without Interwiev. +* +* If you want to use KDChart with Interview, use KDChart::Chart instead. +*/ + +/** + * Constructor. Creates a new widget with all data initialized empty. + * + * \param parent the widget parent; passed on to QWidget + */ +Widget::Widget( QWidget* parent ) : + QWidget(parent), _d( new Private( this ) ) +{ + // as default we have a cartesian coordinate plane ... + // ... and a line diagram + setType( Line ); +} + +/** + * Destructor. + */ +Widget::~Widget() +{ + delete _d; _d = 0; +} + +void Widget::init() +{ +} + +void Widget::setDataset( int column, const QVector< double > & data, const QString& title ) +{ + if ( ! checkDatasetWidth( 1 ) ) + return; + + QStandardItemModel & model = d->m_model; + + justifyModelSize( data.size(), column + 1 ); + + for( int i = 0; i < data.size(); ++i ) + { + const QModelIndex index = model.index( i, column ); + model.setData( index, QVariant( data[i] ), Qt::DisplayRole ); + } + if ( ! title.isEmpty() ) + model.setHeaderData( column, Qt::Horizontal, QVariant( title ) ); +} + +void Widget::setDataset( int column, const QVector< QPair< double, double > > & data, const QString& title ) +{ + if ( ! checkDatasetWidth( 2 )) + return; + + QStandardItemModel & model = d->m_model; + + justifyModelSize( data.size(), (column + 1) * 2 ); + + for( int i = 0; i < data.size(); ++i ) + { + QModelIndex index = model.index( i, column * 2 ); + model.setData( index, QVariant( data[i].first ), Qt::DisplayRole ); + + index = model.index( i, column * 2 + 1 ); + model.setData( index, QVariant( data[i].second ), Qt::DisplayRole ); + } + if ( ! title.isEmpty() ){ + model.setHeaderData( column, Qt::Horizontal, QVariant( title ) ); + } +} + +void Widget::setDataCell( int row, int column, double data ) +{ + if ( ! checkDatasetWidth( 1 ) ) + return; + + QStandardItemModel & model = d->m_model; + + justifyModelSize( row + 1, column + 1 ); + + const QModelIndex index = model.index( row, column ); + model.setData( index, QVariant( data ), Qt::DisplayRole ); +} + +void Widget::setDataCell( int row, int column, QPair< double, double > data ) +{ + if ( ! checkDatasetWidth( 2 )) + return; + + QStandardItemModel & model = d->m_model; + + justifyModelSize( row + 1, (column + 1) * 2 ); + + QModelIndex index = model.index( row, column * 2 ); + model.setData( index, QVariant( data.first ), Qt::DisplayRole ); + + index = model.index( row, column * 2 + 1 ); + model.setData( index, QVariant( data.second ), Qt::DisplayRole ); +} + +/* + * Resets all data. + */ +void Widget::resetData() +{ + d->m_model.clear(); + d->usedDatasetWidth = 0; +} + +/** + * Sets all global leadings (borders). + */ +void Widget::setGlobalLeading( int left, int top, int right, int bottom ) +{ + d->m_chart.setGlobalLeading( left, top, right, bottom ); +} + +/** + * Sets the left leading (border). + */ +void Widget::setGlobalLeadingLeft( int leading ) +{ + d->m_chart.setGlobalLeadingLeft( leading ); +} + +/** + * Returns the left leading (border). + */ +int Widget::globalLeadingLeft() const +{ + return d->m_chart.globalLeadingLeft(); +} + +/** + * Sets the top leading (border). + */ +void Widget::setGlobalLeadingTop( int leading ) +{ + d->m_chart.setGlobalLeadingTop( leading ); +} + +/** + * Returns the top leading (border). + */ +int Widget::globalLeadingTop() const +{ + return d->m_chart.globalLeadingTop(); +} + +/** + * Sets the right leading (border). + */ +void Widget::setGlobalLeadingRight( int leading ) +{ + d->m_chart.setGlobalLeadingRight( leading ); +} + +/** + * Returns the right leading (border). + */ +int Widget::globalLeadingRight() const +{ + return d->m_chart.globalLeadingRight(); +} + +/** + * Sets the bottom leading (border). + */ +void Widget::setGlobalLeadingBottom( int leading ) +{ + d->m_chart.setGlobalLeadingBottom( leading ); +} + +/** + * Returns the bottom leading (border). + */ +int Widget::globalLeadingBottom() const +{ + return d->m_chart.globalLeadingBottom(); +} + +/** + * Returns the first of all headers. + */ +KDChart::HeaderFooter* Widget::firstHeaderFooter() +{ + return d->m_chart.headerFooter(); +} + +/** + * Returns a list with all headers. + */ +QList Widget::allHeadersFooters() +{ + return d->m_chart.headerFooters(); +} + +/** + * Adds a new header/footer with the given text to the position. + */ +void Widget::addHeaderFooter( const QString& text, + HeaderFooter::HeaderFooterType type, + Position position) +{ + HeaderFooter* newHeader = new HeaderFooter( &d->m_chart ); + newHeader->setType( type ); + newHeader->setPosition( position ); + newHeader->setText( text ); + d->m_chart.addHeaderFooter( newHeader ); // we need this explicit call ! +} + +/** + * Adds an existing header / footer object. + */ +void Widget::addHeaderFooter( HeaderFooter* header ) +{ + header->setParent( &d->m_chart ); + d->m_chart.addHeaderFooter( header ); // we need this explicit call ! +} + +void Widget::replaceHeaderFooter( HeaderFooter* header, HeaderFooter* oldHeader ) +{ + header->setParent( &d->m_chart ); + d->m_chart.replaceHeaderFooter( header, oldHeader ); +} + +void Widget::takeHeaderFooter( HeaderFooter* header ) +{ + d->m_chart.takeHeaderFooter( header ); +} + +/** + * Returns the first of all legends. + */ +KDChart::Legend* Widget::legend() +{ + return d->m_chart.legend(); +} + +/** + * Returns a list with all legends. + */ +QList Widget::allLegends() +{ + return d->m_chart.legends(); +} + +/** + * Adds an empty legend on the given position. + */ +void Widget::addLegend( Position position ) +{ + Legend* legend = new Legend( diagram(), &d->m_chart ); + legend->setPosition( position ); + d->m_chart.addLegend( legend ); +} + +/** + * Adds a new, already existing, legend. + */ +void Widget::addLegend( Legend* legend ) +{ + legend->setDiagram( diagram() ); + legend->setParent( &d->m_chart ); + d->m_chart.addLegend( legend ); +} + +void Widget::replaceLegend( Legend* legend, Legend* oldLegend ) +{ + legend->setDiagram( diagram() ); + legend->setParent( &d->m_chart ); + d->m_chart.replaceLegend( legend, oldLegend ); +} + +void Widget::takeLegend( Legend* legend ) +{ + d->m_chart.takeLegend( legend ); +} + +AbstractDiagram* Widget::diagram() +{ + if ( coordinatePlane() == 0 ) + qDebug() << "diagram(): coordinatePlane() was NULL"; + + return coordinatePlane()->diagram(); +} + +BarDiagram* Widget::barDiagram() +{ + return dynamic_cast( diagram() ); +} +LineDiagram* Widget::lineDiagram() +{ + return dynamic_cast( diagram() ); +} +Plotter* Widget::plotter() +{ + return dynamic_cast( diagram() ); +} +PieDiagram* Widget::pieDiagram() +{ + return dynamic_cast( diagram() ); +} +RingDiagram* Widget::ringDiagram() +{ + return dynamic_cast( diagram() ); +} +PolarDiagram* Widget::polarDiagram() +{ + return dynamic_cast( diagram() ); +} + +AbstractCoordinatePlane* Widget::coordinatePlane() +{ + return d->m_chart.coordinatePlane(); +} + +static bool isCartesian( KDChart::Widget::ChartType type ) +{ + return (type == KDChart::Widget::Bar) || (type == KDChart::Widget::Line); +} + +static bool isPolar( KDChart::Widget::ChartType type ) +{ + return (type == KDChart::Widget::Pie) + || (type == KDChart::Widget::Ring) + || (type == KDChart::Widget::Polar); +} + +void Widget::setType( ChartType chartType, SubType chartSubType ) +{ + AbstractDiagram* diag = 0; + const ChartType oldType = type(); + + if ( chartType != oldType ){ + if( chartType != NoType ){ + if ( isCartesian( chartType ) && ! isCartesian( oldType ) ) + { + if( coordinatePlane() == &d->m_polPlane ){ + d->m_chart.takeCoordinatePlane( &d->m_polPlane ); + d->m_chart.addCoordinatePlane( &d->m_cartPlane ); + }else{ + d->m_chart.replaceCoordinatePlane( &d->m_cartPlane ); + } + } + else if ( isPolar( chartType ) && ! isPolar( oldType ) ) + { + if( coordinatePlane() == &d->m_cartPlane ){ + d->m_chart.takeCoordinatePlane( &d->m_cartPlane ); + d->m_chart.addCoordinatePlane( &d->m_polPlane ); + }else{ + d->m_chart.replaceCoordinatePlane( &d->m_polPlane ); + } + } + } + switch ( chartType ){ + case Bar: + diag = new BarDiagram( &d->m_chart, &d->m_cartPlane ); + break; + case Line: + diag = new LineDiagram( &d->m_chart, &d->m_cartPlane ); + break; + case Plot: + diag = new Plotter( &d->m_chart, &d->m_cartPlane ); + break; + case Pie: + diag = new PieDiagram( &d->m_chart, &d->m_polPlane ); + break; + case Polar: + diag = new PolarDiagram( &d->m_chart, &d->m_polPlane ); + break; + case Ring: + diag = new RingDiagram( &d->m_chart, &d->m_polPlane ); + break; + case NoType: + break; + } + if ( diag != NULL ){ + if ( isCartesian( oldType ) && isCartesian( chartType ) ){ + AbstractCartesianDiagram *oldDiag = + qobject_cast( coordinatePlane()->diagram() ); + AbstractCartesianDiagram *newDiag = + qobject_cast( diag ); + Q_FOREACH( CartesianAxis* axis, oldDiag->axes() ) { + oldDiag->takeAxis( axis ); + newDiag->addAxis ( axis ); + } + } + diag->setModel( &d->m_model ); + coordinatePlane()->replaceDiagram( diag ); + + LegendList legends = d->m_chart.legends(); + Q_FOREACH(Legend* l, legends) + l->setDiagram( diag ); + //checkDatasetWidth( d->usedDatasetWidth ); + } + //coordinatePlane()->setGridNeedsRecalculate(); + } + + if ( chartType != NoType ){ + if ( chartType != oldType || chartSubType != subType() ) + setSubType( chartSubType ); + d->m_chart.resize( size() ); // triggering immediate update + } +} + +void Widget::setSubType( SubType subType ) +{ + BarDiagram* barDia = qobject_cast< BarDiagram* >( diagram() ); + LineDiagram* lineDia = qobject_cast< LineDiagram* >( diagram() ); + Plotter* plotterDia = qobject_cast< Plotter* >( diagram() ); + +//FIXME(khz): Add the impl for these chart types - or remove them from here: +// PieDiagram* pieDia = qobject_cast< PieDiagram* >( diagram() ); +// PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() ); +// RingDiagram* ringDia = qobject_cast< RingDiagram* >( diagram() ); + +#define SET_SUB_TYPE(DIAGRAM, SUBTYPE) \ +{ \ + if( DIAGRAM ) \ + DIAGRAM->setType( SUBTYPE ); \ +} + switch ( subType ) + { + case Normal: + SET_SUB_TYPE( barDia, BarDiagram::Normal ); + SET_SUB_TYPE( lineDia, LineDiagram::Normal ); + SET_SUB_TYPE( plotterDia, Plotter::Normal ); + break; + case Stacked: + SET_SUB_TYPE( barDia, BarDiagram::Stacked ); + SET_SUB_TYPE( lineDia, LineDiagram::Stacked ); + //SET_SUB_TYPE( plotterDia, Plotter::Stacked ); + break; + case Percent: + SET_SUB_TYPE( barDia, BarDiagram::Percent ); + SET_SUB_TYPE( lineDia, LineDiagram::Percent ); + SET_SUB_TYPE( plotterDia, Plotter::Percent ); + break; + case Rows: + SET_SUB_TYPE( barDia, BarDiagram::Rows ); + break; + default: + Q_ASSERT_X ( false, + "Widget::setSubType", "Sub-type not supported!" ); + break; + } +// coordinatePlane()->show(); +} + +/** + * Returns the type of the chart. + */ +Widget::ChartType Widget::type() const +{ + // PENDING(christoph) save the type out-of-band: + AbstractDiagram * const dia = const_cast( this )->diagram(); + if ( qobject_cast< BarDiagram* >( dia ) ) + return Bar; + else if ( qobject_cast< LineDiagram* >( dia ) ) + return Line; + else if ( qobject_cast< Plotter* >( dia ) ) + return Plot; + else if( qobject_cast< PieDiagram* >( dia ) ) + return Pie; + else if( qobject_cast< PolarDiagram* >( dia ) ) + return Polar; + else if( qobject_cast< RingDiagram* >( dia ) ) + return Ring; + else + return NoType; +} + +Widget::SubType Widget::subType() const +{ + // PENDING(christoph) save the type out-of-band: + Widget::SubType retVal = Normal; + + AbstractDiagram * const dia = const_cast( this )->diagram(); + BarDiagram* barDia = qobject_cast< BarDiagram* >( dia ); + LineDiagram* lineDia = qobject_cast< LineDiagram* >( dia ); + Plotter* plotterDia = qobject_cast< Plotter* >( dia ); + +//FIXME(khz): Add the impl for these chart types - or remove them from here: +// PieDiagram* pieDia = qobject_cast< PieDiagram* >( diagram() ); +// PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() ); +// RingDiagram* ringDia = qobject_cast< RingDiagram* >( diagram() ); + +#define TEST_SUB_TYPE(DIAGRAM, INTERNALSUBTYPE, SUBTYPE) \ +{ \ + if( DIAGRAM && DIAGRAM->type() == INTERNALSUBTYPE ) \ + retVal = SUBTYPE; \ +} + const Widget::ChartType mainType = type(); + switch ( mainType ) + { + case Bar: + TEST_SUB_TYPE( barDia, BarDiagram::Normal, Normal ); + TEST_SUB_TYPE( barDia, BarDiagram::Stacked, Stacked ); + TEST_SUB_TYPE( barDia, BarDiagram::Percent, Percent ); + TEST_SUB_TYPE( barDia, BarDiagram::Rows, Rows ); + break; + case Line: + TEST_SUB_TYPE( lineDia, LineDiagram::Normal, Normal ); + TEST_SUB_TYPE( lineDia, LineDiagram::Stacked, Stacked ); + TEST_SUB_TYPE( lineDia, LineDiagram::Percent, Percent ); + break; + case Plot: + TEST_SUB_TYPE( plotterDia, Plotter::Normal, Normal ); + TEST_SUB_TYPE( plotterDia, Plotter::Percent, Percent ); + break; + case Pie: + // no impl. yet + break; + case Polar: + // no impl. yet + break; + case Ring: + // no impl. yet + break; + default: + Q_ASSERT_X ( false, + "Widget::subType", "Chart type not supported!" ); + break; + } + return retVal; +} + + +/** + * Checks whether the given width matches with the one used until now. + */ +bool Widget::checkDatasetWidth( int width ) +{ + if( width == diagram()->datasetDimension() ) + { + d->usedDatasetWidth = width; + return true; + } + qDebug() << "The current diagram type doesn't support this data dimension."; + return false; +/* if ( d->usedDatasetWidth == width || d->usedDatasetWidth == 0 ) { + d->usedDatasetWidth = width; + diagram()->setDatasetDimension( width ); + return true; + } + qDebug() << "It's impossible to mix up the different setDataset() methods on the same widget."; + return false;*/ +} + +/** + * Justifies the model, so that the given rows and columns fit into it. + */ +void Widget::justifyModelSize( int rows, int columns ) +{ + QAbstractItemModel & model = d->m_model; + const int currentRows = model.rowCount(); + const int currentCols = model.columnCount(); + + if ( currentCols < columns ) + if ( ! model.insertColumns( currentCols, columns - currentCols )) + qDebug() << "justifyModelSize: could not increase model size."; + if ( currentRows < rows ) + if ( ! model.insertRows( currentRows, rows - currentRows )) + qDebug() << "justifyModelSize: could not increase model size."; + + Q_ASSERT( model.rowCount() >= rows ); + Q_ASSERT( model.columnCount() >= columns ); +} diff --git a/libkdchart/src/KDChartWidget.h b/libkdchart/src/KDChartWidget.h new file mode 100644 index 0000000..9e0ddcb --- /dev/null +++ b/libkdchart/src/KDChartWidget.h @@ -0,0 +1,240 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef __KDCHARTWIDGET_H__ +#define __KDCHARTWIDGET_H__ + +#include "KDChartGlobal.h" + +#include + +#include "KDChartEnums.h" +#include "KDChartHeaderFooter.h" + +template class QVector; +template struct QPair; + +namespace KDChart { + + // some forward declarations + class AbstractDiagram; + class Chart; + class AbstractCoordinatePlane; + class TableModel; + class BarDiagram; + class LineDiagram; + class Plotter; + class PieDiagram; + class RingDiagram; + class PolarDiagram; + class Legend; + class Position; + + /** + * \class Widget KDChartWidget.h + * \brief The KD Chart widget for usage without Model/View. + * + * If you want to use KD Chart with Model/View, use KDChart::Chart instead. + */ + class KDCHART_EXPORT Widget : public QWidget + { + Q_OBJECT + + Q_DISABLE_COPY( Widget ) + KDCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC_QWIDGET( Widget ) + + public: + /** + * Standard Qt-style Constructor + * + * Creates a new widget with all data initialized empty. + * + * \param parent the widget parent; passed on to QWidget + */ + explicit Widget( QWidget* parent = 0 ); + + /** Destructor. */ + ~Widget(); + /** Sets the data in the given column using a QVector of double for the Y values. */ + void setDataset( int column, const QVector< double > & data, const QString& title = QString() ); + /** Sets the data in the given column using a QVector of QPairs + * of double for the (X, Y) values. */ + void setDataset( int column, const QVector< QPair< double, double > > & data, const QString& title = QString() ); + /** Sets the Y value data for a given cell. */ + void setDataCell( int row, int column, double data ); + /** Sets the data for a given column using an (X, Y) QPair of doubles. */ + void setDataCell( int row, int column, QPair< double, double > data ); + /** Resets all data. */ + void resetData(); + + public Q_SLOTS: + /** Sets all global leadings (borders). */ + void setGlobalLeading( int left, int top, int right, int bottom ); + /** Sets the left leading (border). */ + void setGlobalLeadingLeft( int leading ); + /** Sets the top leading (border). */ + void setGlobalLeadingTop( int leading ); + /** Sets the right leading (border). */ + void setGlobalLeadingRight( int leading ); + /** Sets the bottom leading (border). */ + void setGlobalLeadingBottom( int leading ); + + public: + /** Returns the left leading (border). */ + int globalLeadingLeft() const; + /** Returns the top leading (border). */ + int globalLeadingTop() const; + /** Returns the right leading (border). */ + int globalLeadingRight() const; + /** Returns the bottom leading (border). */ + int globalLeadingBottom() const; + + /** Returns the first of all headers. */ + HeaderFooter* firstHeaderFooter(); + /** Returns a list with all headers. */ + QList allHeadersFooters(); + + /** Adds a new header/footer with the given text to the position. */ + void addHeaderFooter( const QString& text, + HeaderFooter::HeaderFooterType type, + Position position ); + + /** + * Adds the existing header / footer object \a header. + * \sa replaceHeaderFooter, takeHeaderFooter + */ + void addHeaderFooter( HeaderFooter* header ); + + /** + * Replaces the old header (or footer, resp.), or appends the + * new header or footer, it there is none yet. + * + * @param header The header or footer to be used instead of the old one. + * This parameter must not be zero, or the method will do nothing. + * + * @param oldHeader The header or footer to be removed by the new one. This + * header or footer will be deleted automatically. If the parameter is omitted, + * the very first header or footer will be replaced. In case, there was no + * header and no footer yet, the new header or footer will just be added. + * + * \note If you want to re-use the old header or footer, call takeHeaderFooter and + * addHeaderFooter, instead of using replaceHeaderFooter. + * + * \sa addHeaderFooter, takeHeaderFooter + */ + void replaceHeaderFooter( HeaderFooter* header, + HeaderFooter* oldHeader = 0 ); + + /** Remove the header (or footer, resp.) from the widget, + * without deleting it. + * The chart no longer owns the header or footer, so it is + * the caller's responsibility to delete the header or footer. + * + * \sa addHeaderFooter, replaceHeaderFooter + */ + void takeHeaderFooter( HeaderFooter* header ); + + /** Returns the first of all legends. */ + Legend* legend(); + /** Returns a list with all legends. */ + QList allLegends(); + + /** Adds an empty legend on the given position. */ + void addLegend( Position position ); + /** Adds a new, already existing, legend. */ + void addLegend (Legend* legend ); + + void replaceLegend( Legend* legend, Legend* oldLegend = 0 ); + void takeLegend( Legend* legend ); + + + /** Returns a pointer to the current diagram. */ + AbstractDiagram* diagram(); + + /** If the current diagram is a BarDiagram, it is returnd; otherwise 0 is returned. + * This function provides type-safe casting. + */ + BarDiagram* barDiagram(); + /** If the current diagram is a LineDiagram, it is returnd; otherwise 0 is returned. + * This function provides type-safe casting. + */ + LineDiagram* lineDiagram(); + /** If the current diagram is a LineDiagram, it is returnd; otherwise 0 is returned. + * This function provides type-safe casting. + * + * \note Do not use lineDiagram for multi-dimensional diagrams, but use plotter instead + * + * \sa plotter + */ + Plotter* plotter(); + /** If the current diagram is a Plotter, it is returnd; otherwise 0 is returned. + * This function provides type-safe casting. + */ + PieDiagram* pieDiagram(); + /** If the current diagram is a RingDiagram, it is returnd; otherwise 0 is returned. + * This function provides type-safe casting. + */ + RingDiagram* ringDiagram(); + /** If the current diagram is a PolarDiagram, it is returnd; otherwise 0 is returned. + * This function provides type-safe casting. + */ + PolarDiagram* polarDiagram(); + + /** Returns a pointer to the current coordinate plane. */ + AbstractCoordinatePlane* coordinatePlane(); + + + enum ChartType { NoType, Bar, Line, Plot, Pie, Ring, Polar }; + + /** Returns the type of the chart. */ + ChartType type() const; + + /** Sub type values, matching the values defines for the respective Diagram classes. */ + enum SubType { Normal, Stacked, Percent, Rows }; + + /** Returns the sub-type of the chart. */ + SubType subType() const; + + public Q_SLOTS: + /** Sets the type of the chart. */ + void setType( ChartType chartType, SubType subType=Normal ); + /** \brief Sets the type of the chart without changing the main type. + * + * Make sure to use a sub-type that matches the main type, + * so e.g. setting sub-type Rows makes sense for Bar charts only, + * and it will be ignored for all other chart types. + * + * \sa KDChartBarDiagram::BarType, KDChartLineDiagram::LineType + * \sa KDChartPieDiagram::PieType, KDChartRingDiagram::RingType + * \sa KDChartPolarDiagram::PolarType + */ + void setSubType( SubType subType ); + + private: + /** Justifies the model, so that the given rows and columns fit into it. */ + void justifyModelSize( int rows, int columns ); + /** Checks wether the given width matches with the one used until now. */ + bool checkDatasetWidth( int width ); + }; +} + +#endif // KDChartWidget_H diff --git a/libkdchart/src/KDChartWidget_p.h b/libkdchart/src/KDChartWidget_p.h new file mode 100644 index 0000000..fab3c39 --- /dev/null +++ b/libkdchart/src/KDChartWidget_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef __KDCHARTWIDGET_P_H__ +#define __KDCHARTWIDGET_P_H__ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +#include + +#include +#include + +/** + * \internal + */ +class KDChart::Widget::Private +{ + friend class ::KDChart::Widget; + Widget * const q; +public: + explicit Private( Widget * qq ); + ~Private(); // non-virtual, since nothing inherits this + +protected: + QGridLayout layout; + QStandardItemModel m_model; + Chart m_chart; + CartesianCoordinatePlane m_cartPlane; + PolarCoordinatePlane m_polPlane; + + int usedDatasetWidth; +}; + + +#endif // KDChartWidget_p_H diff --git a/libkdchart/src/KDChartZoomParameters.h b/libkdchart/src/KDChartZoomParameters.h new file mode 100644 index 0000000..0429718 --- /dev/null +++ b/libkdchart/src/KDChartZoomParameters.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef ZOOMPARAMETERS_H +#define ZOOMPARAMETERS_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +namespace KDChart { + /** + * ZoomParameters stores the center and the factor of zooming internally + * \internal + */ + class ZoomParameters { + public: + ZoomParameters() + : xFactor( 1.0 ), + yFactor( 1.0 ), + xCenter( 0.5 ), + yCenter( 0.5) + { + } + + ZoomParameters( double xFactor, double yFactor, const QPointF& center ) + : xFactor( xFactor ), + yFactor( yFactor ), + xCenter( center.x() ), + yCenter( center.y() ) + { + } + + void setCenter( const QPointF& center ) + { + xCenter = center.x(); + yCenter = center.y(); + } + const QPointF center() const + { + return QPointF( xCenter, yCenter ); + } + + double xFactor; + double yFactor; + + double xCenter; + double yCenter; + }; +} + +#endif diff --git a/libkdchart/src/KDTextDocument.cpp b/libkdchart/src/KDTextDocument.cpp new file mode 100644 index 0000000..1297382 --- /dev/null +++ b/libkdchart/src/KDTextDocument.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDTextDocument.h" +#include +#include +#include +#include + +#include + +// This is an internal class that mimicks some of the behavior of a +// QLabel with rich text assigned, this is mostly a workaround around +// QTextDocumentLayout not being a public class. + +KDTextDocument::KDTextDocument( QObject * p ) + : QTextDocument( p ), + mHintValid( false ), + mSizeHint(), + mMinimumSizeHint() +{ + +} + +KDTextDocument::KDTextDocument( const QString & text, QObject * p ) + : QTextDocument( text, p ), + mHintValid( false ), + mSizeHint(), + mMinimumSizeHint() +{ + +} + +KDTextDocument::~KDTextDocument() {} + + +QSize KDTextDocument::sizeHint() +{ + if( !mHintValid ) + (void)minimumSizeHint(); + return mSizeHint; +} + +QSize KDTextDocument::minimumSizeHint() +{ + /* + QTextCursor cursor( this ); + if( ! cursor.atEnd() ) + cursor.movePosition( QTextCursor::NextBlock ); + qDebug() << "KDTextDocument::minimumSizeHint() found:" << cursor.block().text(); + QSizeF s( documentLayout()->blockBoundingRect( cursor.block() ).size() ); + qDebug() << "KDTextDocument::minimumSizeHint() found rect" << documentLayout()->blockBoundingRect( cursor.block()); + return QSize( static_cast(s.width()), + static_cast(s.height()) ); + */ + + if( mHintValid ) + return mMinimumSizeHint; + + mHintValid = true; + mSizeHint = sizeForWidth( -1 ); + QSize sz(-1, -1); + + // PENDING(kalle) Cache + sz.rwidth() = sizeForWidth( 0 ).width(); + sz.rheight() = sizeForWidth( 32000 ).height(); + if( mSizeHint.height() < sz.height()) + sz.rheight() = mSizeHint.height(); + + mMinimumSizeHint = sz; + return sz; +} + + +QSize KDTextDocument::sizeForWidth(int w) +{ + Q_UNUSED( w ); + + setPageSize(QSize(0, 100000)); + + return documentLayout()->documentSize().toSize(); +} diff --git a/libkdchart/src/KDTextDocument.h b/libkdchart/src/KDTextDocument.h new file mode 100644 index 0000000..4fe63ab --- /dev/null +++ b/libkdchart/src/KDTextDocument.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDTEXTDOCUMENT_H +#define KDTEXTDOCUMENT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +/** + * KDTextDocument is an internally used enhanced QTextDocument + * \internal + */ +class KDTextDocument : public QTextDocument +{ + Q_OBJECT + +public: + explicit KDTextDocument( QObject* parent = 0 ); + explicit KDTextDocument( const QString& text, QObject* parent = 0 ); + ~KDTextDocument(); + + QSize sizeHint(); + QSize minimumSizeHint(); + +private: + QSize sizeForWidth( int width ); + +private: + bool mHintValid; + QSize mSizeHint; + QSize mMinimumSizeHint; +}; + + +#endif /* KDTEXTDOCUMENT_H */ + diff --git a/libkdchart/src/Makefile b/libkdchart/src/Makefile new file mode 100644 index 0000000..c5cb627 --- /dev/null +++ b/libkdchart/src/Makefile @@ -0,0 +1,5 @@ +QT_PACKAGES="QtCore QtGui QtSql" +CXXFLAGS=`pkg-config --cflags $(QT_PACKAGES)` -Wall -Isrc + +LDFLAGS=`pkg-config --libs $(QT_PACKAGES)` + diff --git a/libkdchart/src/PrerenderedElements/KDChartTextLabelCache.cpp b/libkdchart/src/PrerenderedElements/KDChartTextLabelCache.cpp new file mode 100644 index 0000000..49893d4 --- /dev/null +++ b/libkdchart/src/PrerenderedElements/KDChartTextLabelCache.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartTextLabelCache.h" + +#include + +#include +#include +#include +#include +#include + +#ifndef NDEBUG +int HitCount = 0; +int MissCount = 0; +#define INC_HIT_COUNT { ++HitCount; } +#define INC_MISS_COUNT { ++MissCount; } +#define DUMP_CACHE_STATS \ + if ( HitCount != 0 && MissCount != 0 ) { \ + int total = HitCount + MissCount; \ + double hitQuote = ( 1.0 * HitCount ) / total; \ + qDebug() << "PrerenderedLabel dtor: hits/misses/total:" \ + << HitCount << "/" << MissCount << "/" << total \ + << "(" << 100 * hitQuote << "% hits)"; \ + } +#else +#define INC_HIT_COUNT +#define INC_MISS_COUNT +#define DUMP_CACHE_STATS +#endif + +PrerenderedElement::PrerenderedElement() + : m_referencePoint( KDChartEnums::PositionNorthWest ) +{ +} + +void PrerenderedElement::setPosition( const QPointF& position ) +{ // this does not invalidate the element + m_position = position; +} + +const QPointF& PrerenderedElement::position() const +{ + return m_position; +} + +void PrerenderedElement::setReferencePoint( KDChartEnums::PositionValue point ) +{ // this does not invalidate the element + m_referencePoint = point; +} + +KDChartEnums::PositionValue PrerenderedElement::referencePoint() const +{ + return m_referencePoint; +} + +PrerenderedLabel::PrerenderedLabel() + : PrerenderedElement() + , m_dirty( true ) + , m_font( qApp->font() ) + , m_brush( Qt::black ) + , m_pen( Qt::black ) // do not use anything invisible + , m_angle( 0.0 ) +{ +} + +PrerenderedLabel::~PrerenderedLabel() +{ + DUMP_CACHE_STATS; +} + +/** + * Invalidates the preredendered data, forces re-rendering. + */ +void PrerenderedLabel::invalidate() const +{ + m_dirty = true; +} + +/** + * Sets the label's font to \a font. + */ +void PrerenderedLabel::setFont( const QFont& font ) +{ + m_font = font; + invalidate(); +} + +/** + * @return the label's font. + */ +const QFont& PrerenderedLabel::font() const +{ + return m_font; +} + +/** + * Sets the label's text to \a text + */ +void PrerenderedLabel::setText( const QString& text ) +{ + m_text = text; + invalidate(); +} + +/** + * @return the label's text + */ +const QString& PrerenderedLabel::text() const +{ + return m_text; +} + +/** + * Sets the label's brush to \a brush + */ +void PrerenderedLabel::setBrush( const QBrush& brush ) +{ + m_brush = brush; + invalidate(); +} + +/** + * @return the label's brush + */ +const QBrush& PrerenderedLabel::brush() const +{ + return m_brush; +} + +/** + * Sets the angle of the label to \a angle degrees + */ +void PrerenderedLabel::setAngle( double angle ) +{ + m_angle = angle; + invalidate(); +} + +/** + * @return the label's angle in degrees + */ +double PrerenderedLabel::angle() const +{ + return m_angle; +} + +const QPixmap& PrerenderedLabel::pixmap() const +{ + if ( m_dirty ) { + INC_MISS_COUNT; + paint(); + } else { + INC_HIT_COUNT; + } + return m_pixmap; +} + +void PrerenderedLabel::paint() const +{ + // FIXME find a better value using font metrics of text (this + // requires finding the diameter of the circle formed by rotating + // the bounding rect around the center): + const int Width = 1000; + const int Height = Width; + + QRectF boundingRect; + const QColor FullTransparent( 255, 255, 255, 0 ); +#ifdef Q_WS_X11 + QImage pixmap( Width, Height, QImage::Format_ARGB32_Premultiplied ); + qWarning() << "PrerenderedLabel::paint: using QImage for prerendered labels " + << "to work around XRender/Qt4 bug."; +#else + QPixmap pixmap( Width, Height ); +#endif + // pixmap.fill( FullTransparent ); + { + static const QPointF Center ( 0.0, 0.0 ); + QPointF textBottomRight; + QPainter painter( &pixmap ); + painter.setRenderHint(QPainter::TextAntialiasing, true ); + painter.setRenderHint(QPainter::Antialiasing, true ); + + // QImage (X11 workaround) does not have fill(): + painter.setPen( FullTransparent ); + painter.setBrush( FullTransparent ); + painter.drawRect( 0, 0, Width, Height ); + + QMatrix matrix; + matrix.translate( 0.5 * Width, 0.5 * Height ); + matrix.rotate( m_angle ); +#if QT_VERSION > 0x040199 + painter.setWorldMatrix( matrix ); +#else + painter.setMatrix( matrix ); +#endif + + painter.setPen( m_pen ); + painter.setBrush( m_brush ); + painter.setFont( m_font ); + QRectF container( -0.5 * Width, -0.5 * Height, Width, 0.5 * Height ); + painter.drawText( container, Qt::AlignHCenter | Qt::AlignBottom, + m_text, &boundingRect ); + m_referenceBottomLeft = QPointF( boundingRect.bottomLeft().x(), 0.0 ); + textBottomRight = QPointF( boundingRect.bottomRight().x(), 0.0 ); + m_textAscendVector = boundingRect.topRight() - textBottomRight; + m_textBaseLineVector = textBottomRight - m_referenceBottomLeft; + + // FIXME translate topright by char height + boundingRect = matrix.mapRect( boundingRect ); + m_referenceBottomLeft = matrix.map( m_referenceBottomLeft ) + - boundingRect.topLeft(); + textBottomRight = matrix.map( textBottomRight ) + - boundingRect.topLeft(); + m_textAscendVector = matrix.map( m_textAscendVector ) + - matrix.map( Center ); + m_textBaseLineVector = matrix.map( m_textBaseLineVector ) + - matrix.map( Center ); + } + + m_dirty = false; // now all the calculation vectors are valid + + QPixmap temp( static_cast( boundingRect.width() ), + static_cast( boundingRect.height() ) ); + { + temp.fill( FullTransparent ); + QPainter painter( &temp ); +#ifdef Q_WS_X11 + painter.drawImage( QPointF( 0.0, 0.0 ), pixmap, boundingRect ); +#else + painter.drawPixmap( QPointF( 0.0, 0.0 ), pixmap, boundingRect ); +#endif +// #define PRERENDEREDLABEL_DEBUG +#ifdef PRERENDEREDLABEL_DEBUG + painter.setPen( QPen( Qt::red, 2 ) ); + painter.setBrush( Qt::red ); + // paint markers for the reference points + QList positions; + positions << KDChartEnums::PositionCenter + << KDChartEnums::PositionNorthWest + << KDChartEnums::PositionNorth + << KDChartEnums::PositionNorthEast + << KDChartEnums::PositionEast + << KDChartEnums::PositionSouthEast + << KDChartEnums::PositionSouth + << KDChartEnums::PositionSouthWest + << KDChartEnums::PositionWest; + Q_FOREACH( KDChartEnums::PositionValue position, positions ) { + static const double Radius = 0.5; + static const double Diameter = 2 * Radius; + + QPointF point ( referencePointLocation( position ) ); + painter.drawEllipse( QRectF( point - QPointF( Radius, Radius ), + QSizeF( Diameter, Diameter ) ) ); + } +#endif + } + + m_pixmap = temp; +} + +QPointF PrerenderedLabel::referencePointLocation() const +{ + return referencePointLocation( referencePoint() ); +} + +QPointF PrerenderedLabel::referencePointLocation( KDChartEnums::PositionValue position ) const +{ + if ( m_dirty ) { + INC_MISS_COUNT; + paint(); + } else { + INC_HIT_COUNT; + } + + switch( position ) { + case KDChartEnums::PositionCenter: + return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + 0.5 * m_textAscendVector; + case KDChartEnums::PositionNorthWest: + return m_referenceBottomLeft + m_textAscendVector; + case KDChartEnums::PositionNorth: + return m_referenceBottomLeft + 0.5 * m_textBaseLineVector + m_textAscendVector; + case KDChartEnums::PositionNorthEast: + return m_referenceBottomLeft + m_textBaseLineVector + m_textAscendVector; + case KDChartEnums::PositionEast: + return m_referenceBottomLeft + 0.5 * m_textAscendVector; + case KDChartEnums::PositionSouthEast: + return m_referenceBottomLeft + m_textBaseLineVector; + case KDChartEnums::PositionSouth: + return m_referenceBottomLeft + 0.5 * m_textBaseLineVector; + case KDChartEnums::PositionSouthWest: + return m_referenceBottomLeft; + case KDChartEnums::PositionWest: + return m_referenceBottomLeft + m_textBaseLineVector + 0.5 * m_textAscendVector; + + case KDChartEnums::PositionUnknown: // intentional fall-through + case KDChartEnums::PositionFloating: // intentional fall-through + default: + return QPointF(); + } +} diff --git a/libkdchart/src/PrerenderedElements/KDChartTextLabelCache.h b/libkdchart/src/PrerenderedElements/KDChartTextLabelCache.h new file mode 100644 index 0000000..9627da2 --- /dev/null +++ b/libkdchart/src/PrerenderedElements/KDChartTextLabelCache.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTEXTLABELCACHE_H +#define KDCHARTTEXTLABELCACHE_H + +#include +#include +#include +#include + +#include "KDChartEnums.h" + +/** + * @brief base class for prerendered elements like labels, pixmaps, markers, etc. + */ +class PrerenderedElement { +public: + PrerenderedElement(); + virtual ~PrerenderedElement() {} + + /** Returns the rendered element. + If any of the properties have change, the element will be + regenerated. */ + virtual const QPixmap& pixmap() const = 0; + + /** Return the location of the reference point relatively to the + pixmap's origin. */ + virtual QPointF referencePointLocation( KDChartEnums::PositionValue ) const = 0; + + /** Set the position of the element. */ + void setPosition( const QPointF& position ); + /** Get the position of the element. */ + const QPointF& position() const; + + /** Set the reference point of the element. + Every element has nine possible reference points (all compass + directions, plus the center. + */ + void setReferencePoint( KDChartEnums::PositionValue ); + /** Get the reference point of the element. */ + KDChartEnums::PositionValue referencePoint() const; + +protected: + /** invalidate() needs to be called if any of the properties that + determine the visual appearance of the prerendered element + change. + It can be called for a const object, as objects may need to + force recalculation of the pixmap. + */ + virtual void invalidate() const = 0; + +private: + QPointF m_position; + KDChartEnums::PositionValue m_referencePoint; +}; + +/** + @brief PrerenderedLabel is an internal KDChart class that simplifies creation + and caching of cached text labels. + + It provides referenze points to anchor the text to other + elements. Reference points use the positions defined in + KDChartEnums. + + Usage: +
+    double angle = 90.0;
+    CachedLabel label;
+    label.paint( font, tr("Label"), angle );
+    
+*/ + +// FIXME this is merely a prototype +// FIXME caching could be done by a second layer that can be used to, +// e.g., query for a prerendered element by id or name, or by changing +// the pixmap() method to do lazy evaluation. +class PrerenderedLabel : public PrerenderedElement +{ +public: + PrerenderedLabel(); + ~PrerenderedLabel(); + + void setFont( const QFont& font ); + const QFont& font() const; + + void setText( const QString& text ); + const QString& text() const; + + void setBrush( const QBrush& brush ); + const QBrush& brush() const; + + void setPen( const QPen& ); + const QPen& pen() const; + + void setAngle( double angle ); + double angle() const; + + // reimpl PrerenderedElement: + const QPixmap& pixmap() const; + QPointF referencePointLocation( KDChartEnums::PositionValue position ) const; + // overload: return location of referencePoint(): + QPointF referencePointLocation() const; + +protected: + void invalidate() const; + +private: + /** Create a label with the given text and the given rotation + angle. Needs to be const, otherwise the pixmap() method cannot + update when needed. */ + void paint() const; + + // store the settings (these are used for the painting): + mutable bool m_dirty; + QFont m_font; + QString m_text; + QBrush m_brush; + QPen m_pen; + double m_angle; + + // these are valid once the label has been rendered: + mutable QPixmap m_pixmap; + mutable QPointF m_referenceBottomLeft; + mutable QPointF m_textBaseLineVector; + mutable QPointF m_textAscendVector; +}; + +#endif diff --git a/libkdchart/src/Scenery/ChartGraphicsItem.cpp b/libkdchart/src/Scenery/ChartGraphicsItem.cpp new file mode 100644 index 0000000..71fb6d6 --- /dev/null +++ b/libkdchart/src/Scenery/ChartGraphicsItem.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "ChartGraphicsItem.h" + +using namespace KDChart; + +ChartGraphicsItem::ChartGraphicsItem() + : QGraphicsPolygonItem() + , m_row( -1 ) + , m_column( -1 ) +{ +} + +ChartGraphicsItem::ChartGraphicsItem( int row, int column ) + : QGraphicsPolygonItem() + , m_row( row ) + , m_column( column ) +{ +} diff --git a/libkdchart/src/Scenery/ChartGraphicsItem.h b/libkdchart/src/Scenery/ChartGraphicsItem.h new file mode 100644 index 0000000..d54751d --- /dev/null +++ b/libkdchart/src/Scenery/ChartGraphicsItem.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef CHARTGRAPHICSITEM_H +#define CHARTGRAPHICSITEM_H + +#include + +namespace KDChart { + + /** + * @brief Graphics item used inside of the ReverseMapper + * \internal + */ + class ChartGraphicsItem : public QGraphicsPolygonItem + { + public: + enum { Type = UserType + 1 }; + + ChartGraphicsItem(); + + ChartGraphicsItem( int row, int column ); + + int row() const { return m_row; } + int column() const { return m_column; } + int type() const { return Type; } + + private: + int m_row; + int m_column; + }; + +} + +#endif diff --git a/libkdchart/src/Scenery/ReverseMapper.cpp b/libkdchart/src/Scenery/ReverseMapper.cpp new file mode 100644 index 0000000..e7b5c10 --- /dev/null +++ b/libkdchart/src/Scenery/ReverseMapper.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "ReverseMapper.h" + +#include + +#include +#include +#include +#include +#include + +#include "../KDChartAbstractDiagram.h" +#include "ChartGraphicsItem.h" + +using namespace KDChart; + +ReverseMapper::ReverseMapper() + : m_scene( 0 ) + , m_diagram( 0 ) +{ +} + +ReverseMapper::ReverseMapper( AbstractDiagram* diagram ) + : m_scene( 0 ) + , m_diagram( diagram ) +{ +} + +ReverseMapper::~ReverseMapper() +{ + delete m_scene; m_scene = 0; +} + +void ReverseMapper::setDiagram( AbstractDiagram* diagram ) +{ + + m_diagram = diagram; +} + +void ReverseMapper::clear() +{ + delete m_scene; + m_scene = new QGraphicsScene(); +} + +QModelIndexList ReverseMapper::indexesIn( const QRect& rect ) const +{ + Q_ASSERT( m_diagram ); + if ( m_scene && m_scene->sceneRect().intersects( rect ) ) { + QList items = m_scene->items( rect ); + QModelIndexList indexes; + Q_FOREACH( QGraphicsItem* item, items ) { + ChartGraphicsItem* i = qgraphicsitem_cast( item ); + if ( i ) { + QModelIndex index ( m_diagram->model()->index( i->row(), i->column(), m_diagram->rootIndex() ) ); + indexes << index; + } + } + return indexes; + } else { + return QModelIndexList(); + } +} + +QModelIndexList ReverseMapper::indexesAt( const QPointF& point ) const +{ + Q_ASSERT( m_diagram ); + if ( m_scene && m_scene->sceneRect().contains( point ) ) { + QList items = m_scene->items( point ); + QModelIndexList indexes; + Q_FOREACH( QGraphicsItem* item, items ) { + ChartGraphicsItem* i = qgraphicsitem_cast( item ); + if ( i ) { + QModelIndex index ( m_diagram->model()->index( i->row(), i->column(), m_diagram->rootIndex() ) ); + if( !indexes.contains(index) ) + indexes << index; + } + } + return indexes; + } else { + return QModelIndexList(); + } +} + +QPolygonF ReverseMapper::polygon( int row, int column ) const +{ + const QModelIndex index = m_diagram->model()->index( row, column, m_diagram->rootIndex() ); + return m_itemMap.contains( index ) ? m_itemMap[ index ]->polygon() : QPolygon(); +} + +QRectF ReverseMapper::boundingRect( int row, int column ) const +{ + const QModelIndex index = m_diagram->model()->index( row, column, m_diagram->rootIndex() ); + return m_itemMap.contains( index ) ? m_itemMap[ index ]->polygon().boundingRect() : QRectF(); +} + +void ReverseMapper::addItem( ChartGraphicsItem* item ) +{ + Q_ASSERT( m_scene ); + m_scene->addItem( item ); + m_itemMap.insert( m_diagram->model()->index( item->row(), item->column(), m_diagram->rootIndex() ), item ); +} + +void ReverseMapper::addRect( int row, int column, const QRectF& rect ) +{ + addPolygon( row, column, QPolygonF( rect ) ); +} + +void ReverseMapper::addPolygon( int row, int column, const QPolygonF& polygon ) +{ + ChartGraphicsItem* item = new ChartGraphicsItem( row, column ); + item->setPolygon( polygon ); + addItem( item ); +} + +void ReverseMapper::addCircle( int row, int column, const QPointF& location, const QSizeF& diameter ) +{ + QPainterPath path; + QPointF ossfet( -0.5*diameter.width(), -0.5*diameter.height() ); + path.addEllipse( QRectF( location + ossfet, diameter ) ); + addPolygon( row, column, QPolygonF( path.toFillPolygon() ) ); +} + +void ReverseMapper::addLine( int row, int column, const QPointF& from, const QPointF& to ) +{ + // that's no line, dude... make a small circle around that point, instead + if( from == to ) + { + addCircle( row, column, from, QSizeF( 1.5, 1.5 ) ); + return; + } + // lines do not make good polygons to click on. we calculate a 2 + // pixel wide rectangle, where the original line is excatly + // centered in. + // make a 3 pixel wide polygon from the line: + static const QPointF pixel( 1.0, 1.0 ); + QPointF left, right; + if ( from.x() < to.x() ) { + left = from; + right = to; + } else { + right = from; + left = to; + } + const QPointF lineVector( right - left ); + const qreal lineVectorLength = sqrt( lineVector.x() * lineVector.x() + lineVector.y() * lineVector.y() ); + const QPointF lineVectorUnit( lineVector / lineVectorLength ); + const QPointF normOfLineVectorUnit( -lineVectorUnit.y(), lineVectorUnit.x() ); + // now the four polygon end points: + const QPointF one( left - lineVectorUnit + normOfLineVectorUnit ); + const QPointF two( left - lineVectorUnit - normOfLineVectorUnit ); + const QPointF three( right + lineVectorUnit - normOfLineVectorUnit ); + const QPointF four( right + lineVectorUnit + normOfLineVectorUnit ); + addPolygon( row, column, QPolygonF() << one << two << three << four ); +} diff --git a/libkdchart/src/Scenery/ReverseMapper.h b/libkdchart/src/Scenery/ReverseMapper.h new file mode 100644 index 0000000..b351654 --- /dev/null +++ b/libkdchart/src/Scenery/ReverseMapper.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef REVERSEMAPPER_H +#define REVERSEMAPPER_H + +#include +#include + +class QRectF; +class QGraphicsScene; +class QPolygonF; + +namespace KDChart { + + class AbstractDiagram; + class ChartGraphicsItem; + + /** + * @brief The ReverseMapper stores information about objects on a chart and their respective model indexes + * \internal + */ + class ReverseMapper + { + + public: + ReverseMapper(); + explicit ReverseMapper( AbstractDiagram* diagram ); + + ~ReverseMapper(); + + void setDiagram( AbstractDiagram* diagram ); + + void clear(); + + QModelIndexList indexesAt( const QPointF& point ) const; + QModelIndexList indexesIn( const QRect& rect ) const; + + QPolygonF polygon( int row, int column ) const; + QRectF boundingRect( int row, int column ) const; + + void addItem( ChartGraphicsItem* item ); + + // convenience methods: + void addPolygon( int row, int column, const QPolygonF& polygon ); + void addRect( int row, int column, const QRectF& rect ); + void addCircle( int row, int column, const QPointF& location, const QSizeF& diameter ); + void addLine( int row, int column, const QPointF& from, const QPointF& to ); + + private: + QGraphicsScene* m_scene; + AbstractDiagram* m_diagram; + QHash m_itemMap; + }; + +} + +#endif diff --git a/libkdchart/src/Ternary/KDChartAbstractTernaryDiagram.cpp b/libkdchart/src/Ternary/KDChartAbstractTernaryDiagram.cpp new file mode 100644 index 0000000..d917cf1 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartAbstractTernaryDiagram.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartAbstractTernaryDiagram.h" +#include "KDChartAbstractTernaryDiagram_p.h" + +#include "KDChartTernaryCoordinatePlane.h" + +using namespace KDChart; + +AbstractTernaryDiagram::Private::Private() + : AbstractDiagram::Private() +{ +} + +void AbstractTernaryDiagram::init() +{ +} + +#define d d_func() + +AbstractTernaryDiagram::AbstractTernaryDiagram( QWidget* parent, + TernaryCoordinatePlane* plane ) + : AbstractDiagram( parent, plane ) +{ +} + +AbstractTernaryDiagram::~AbstractTernaryDiagram() +{ + while ( ! d->axesList.isEmpty() ) { + TernaryAxis* axis = d->axesList.takeFirst(); + delete axis; + } +} + +void AbstractTernaryDiagram::addAxis( TernaryAxis* axis ) +{ + d->axesList.append( axis ); + // FIXME update +} + +void AbstractTernaryDiagram::takeAxis( TernaryAxis* axis ) +{ + + int index = d->axesList.indexOf( axis ); + if ( index != -1 ) + d->axesList.removeAt( index ); + // FIXME update +} + +TernaryAxisList AbstractTernaryDiagram::axes() const +{ + return d->axesList; +} + +void AbstractTernaryDiagram::paint (PaintContext *paintContext) +{ + d->paint( paintContext ); +} + diff --git a/libkdchart/src/Ternary/KDChartAbstractTernaryDiagram.h b/libkdchart/src/Ternary/KDChartAbstractTernaryDiagram.h new file mode 100644 index 0000000..5e71031 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartAbstractTernaryDiagram.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTTERNARYDIAGRAM_H +#define KDCHARTABSTRACTTERNARYDIAGRAM_H + +#include "../KDChartAbstractDiagram.h" +#include "KDChartTernaryAxis.h" + +namespace KDChart { + + class TernaryCoordinatePlane; + class TernaryAxis; + + /** + * @brief Base class for diagrams based on a ternary coordinate plane. + */ + class KDCHART_EXPORT AbstractTernaryDiagram : public AbstractDiagram + { + Q_OBJECT + Q_DISABLE_COPY( AbstractTernaryDiagram ) + KDCHART_DECLARE_DERIVED_DIAGRAM( AbstractTernaryDiagram, + TernaryCoordinatePlane ) + + public: + explicit AbstractTernaryDiagram ( QWidget* parent = 0, + TernaryCoordinatePlane* plane = 0 ); + virtual ~AbstractTernaryDiagram(); + + virtual void resize (const QSizeF &area) = 0; + virtual void paint (PaintContext *paintContext); + + virtual void addAxis( TernaryAxis* axis ); + virtual void takeAxis( TernaryAxis* axis ); + virtual TernaryAxisList axes () const; + + protected: + virtual const QPair< QPointF, QPointF > calculateDataBoundaries () const = 0; + + }; + +} + +#endif diff --git a/libkdchart/src/Ternary/KDChartAbstractTernaryDiagram_p.h b/libkdchart/src/Ternary/KDChartAbstractTernaryDiagram_p.h new file mode 100644 index 0000000..cbd93b3 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartAbstractTernaryDiagram_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTABSTRACTTERNARYDIAGRAM_P_H +#define KDCHARTABSTRACTTERNARYDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractTernaryDiagram.h" + +#include "KDChartTernaryCoordinatePlane.h" +#include +#include +#include + +#include "../Scenery/ReverseMapper.h" +#include "../Scenery/ChartGraphicsItem.h" +#include + + +namespace KDChart { + + class CartesianCoordinatePlane; + class AbstractTernaryDiagram; + +/** + * \internal + */ + class AbstractTernaryDiagram::Private : public AbstractDiagram::Private + { + friend class AbstractTernaryDiagram; + public: + Private(); + ~Private() {} + + Private( const Private& rhs ) : + AbstractDiagram::Private( rhs ), + // Do not copy axes and reference diagrams. + axesList(), + referenceDiagram( 0 ) + { + } + + TernaryAxisList axesList; + + AbstractTernaryDiagram* referenceDiagram; + QPointF referenceDiagramOffset; + + void drawPoint( QPainter* p, int row, int column, + const QPointF& widgetLocation ) + { + // Q_ASSERT( false ); // unused, to be removed + static const double Diameter = 5.0; + static const double Radius = Diameter / 2.0; + QRectF ellipseRect( widgetLocation - QPointF( Radius, Radius ), + QSizeF( Diameter, Diameter ) ); + p->drawEllipse( ellipseRect ); + + reverseMapper.addRect( row, column, ellipseRect ); + } + + virtual void paint( PaintContext* paintContext ) + { + paintContext->painter()->setRenderHint( QPainter::Antialiasing, + antiAliasing ); + if ( !axesList.isEmpty() ) { + + Q_FOREACH( TernaryAxis* axis, axesList ) { + PainterSaver s( paintContext->painter() ); + axis->paintCtx( paintContext ); + } + } + } + }; + + KDCHART_IMPL_DERIVED_DIAGRAM( AbstractTernaryDiagram, AbstractDiagram, TernaryCoordinatePlane ) +/* +inline AbstractTernaryDiagram::AbstractTernaryDiagram( Private * p ) + : AbstractDiagram( p ) { init(); } +inline AbstractTernaryDiagram::AbstractTernaryDiagram( + Private * p, QWidget* parent, CartesianCoordinatePlane* plane ) + : AbstractDiagram( p, parent, plane ) { init(); } +inline AbstractTernaryDiagram::Private * AbstractTernaryDiagram::d_func() +{ return static_cast( AbstractDiagram::d_func() ); } +inline const AbstractTernaryDiagram::Private * AbstractTernaryDiagram::d_func() const +{ return static_cast( AbstractDiagram::d_func() ); } +*/ + +} + +#endif /* KDCHARTABSTRACTTERNARYDIAGRAM_P_H */ + diff --git a/libkdchart/src/Ternary/KDChartTernaryAxis.cpp b/libkdchart/src/Ternary/KDChartTernaryAxis.cpp new file mode 100644 index 0000000..c54ec48 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryAxis.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartTernaryAxis.h" + +#include + +#include +#include + +#include "TernaryConstants.h" +#include "KDChartTernaryCoordinatePlane.h" +#include "KDChartAbstractTernaryDiagram.h" + + +#include "../src/KDChartLayoutItems.h" +#include "PrerenderedElements/KDChartTextLabelCache.h" + +using namespace KDChart; + +// m_label and m_fifty do not have to be pointers, once the class is +// pimpled (PrerenderedLabel is not published API) + +TernaryAxis::TernaryAxis ( AbstractTernaryDiagram* diagram) + : AbstractAxis( diagram ) + , m_position( KDChartEnums::PositionUnknown ) + , m_label( new PrerenderedLabel ) + , m_fifty( new PrerenderedLabel ) +{ + resetTitleTextAttributes(); + setPosition( KDChartEnums::PositionSouth ); // arbitrary + m_fifty->setText( QObject::tr( "50%" ) ); // const + // FIXME is this consistent with other diagram/axis/plane implementations? + diagram->addAxis( this ); +} + +TernaryAxis::~TernaryAxis() +{ + delete m_label; m_label = 0; + delete m_label; m_fifty = 0; +} + +void TernaryAxis::paintAll (QPainter &) +{ + // not used +} + +void TernaryAxis::paint (QPainter *) +{ + // not used +} + +void TernaryAxis::paintCtx (PaintContext * paintContext) +{ + QPainter* p = paintContext->painter(); + TernaryCoordinatePlane* plane = + (TernaryCoordinatePlane*) paintContext->coordinatePlane(); + // QObject* refArea = plane->parent(); + QRectF drawArea = paintContext->rectangle(); + QRectF titleArea; + + // paint the axis label (across the triangle, that one): + QList labels; + labels << m_label << m_fifty; + Q_FOREACH( PrerenderedLabel* label, labels ) { + const QPixmap& pixmap = label->pixmap(); + QPointF point = plane->translate( label->position() ) + - label->referencePointLocation(); + p->drawPixmap( point, pixmap ); + } +} + +bool TernaryAxis::isEmpty() const +{ + // todo: what's this method for? + return false; +} + +QRect TernaryAxis::geometry () const +{ + return m_geometry; +} + +void TernaryAxis::setGeometry (const QRect &rect) +{ + m_geometry = rect; +} + +QSize TernaryAxis::minimumSize () const +{ + // todo: return realistic sizes + return QSize( 100, 100 ); +} + +QSize TernaryAxis::maximumSize () const +{ + return QSize( 300, 200 ); +} + +QSize TernaryAxis::sizeHint () const +{ + return QSize( 150, 100 ); +} + +Qt::Orientations TernaryAxis::expandingDirections () const +{ + return Qt::Vertical | Qt::Horizontal; +} + +const Position TernaryAxis::position () const +{ + return m_position; +} + +void TernaryAxis::setPosition (Position p) +{ + if ( p == position() ) return; + + if ( p != KDChartEnums::PositionWest + && p != KDChartEnums::PositionEast + && p != KDChartEnums::PositionSouth ) + { + qDebug() << "TernaryAxis::setPosition: only south, east and west are supported " + "positions for ternary axes."; + return; + } + + if ( m_title.isEmpty() ) + switch( p.value() ) { + case KDChartEnums::PositionSouth: + m_label->setText( tr( "A" ) ); + break; + case KDChartEnums::PositionWest: + m_label->setText( tr( "C" ) ); + break; + case KDChartEnums::PositionEast: + m_label->setText( tr( "B" ) ); + break; + default: + break; + } + + m_position = p; + updatePrerenderedLabels(); // position has changed +} + +void TernaryAxis::setTitleText( const QString& text ) +{ + m_title = text; // do not remove + m_label->setText( text ); +} + +QString TernaryAxis::titleText() const +{ + return m_label->text(); +} + +void TernaryAxis::setTitleTextAttributes( const TextAttributes &a ) +{ + m_titleAttributes = a; + updatePrerenderedLabels(); +} + +TextAttributes TernaryAxis::titleTextAttributes() const +{ + return m_titleAttributes; +} + +void TernaryAxis::resetTitleTextAttributes() +{ + TextAttributes a; + m_titleAttributes = a; + updatePrerenderedLabels(); +} + +bool TernaryAxis::hasDefaultTitleTextAttributes() const +{ + TextAttributes a; + return m_titleAttributes == a; +} + +void TernaryAxis::updatePrerenderedLabels() +{ + TextAttributes attributes = titleTextAttributes(); + double axisLabelAngle = 0.0; + double fiftyMarkAngle = 0.0; + QPointF axisLabelPosition; + QPointF fiftyMarkPosition; + KDChartEnums::PositionValue fiftyMarkReferencePoint = KDChartEnums::PositionUnknown; + + switch( position().value() ) { + case KDChartEnums::PositionSouth: + // this is the axis on the other side of A + axisLabelAngle = 0.0; + fiftyMarkAngle = 0.0; + axisLabelPosition = TriangleTop; + fiftyMarkPosition = 0.5 * AxisVector_B_C - RelMarkerLength * Norm_B_C; + fiftyMarkReferencePoint = KDChartEnums::PositionNorth; + break; + case KDChartEnums::PositionEast: + // this is the axis on the other side of B + axisLabelAngle = 240.0; + fiftyMarkAngle = 60; + axisLabelPosition = TriangleBottomLeft; + fiftyMarkPosition = AxisVector_B_C + 0.5 * AxisVector_C_A - RelMarkerLength * Norm_C_A; + fiftyMarkReferencePoint = KDChartEnums::PositionSouth; + break; + case KDChartEnums::PositionWest: + // this is the axis on the other side of C + axisLabelAngle = 120.0; + fiftyMarkAngle = 300.0; + axisLabelPosition = TriangleBottomRight; + fiftyMarkPosition = 0.5 * AxisVector_B_A + RelMarkerLength * Norm_B_A; + fiftyMarkReferencePoint = KDChartEnums::PositionSouth; + break; + case KDChartEnums::PositionUnknown: + break; // initial value + default: + qDebug() << "TernaryAxis::updatePrerenderedLabel: unknown location"; + }; + + m_label->setFont( attributes.font() ); + // m_label->setText( titleText() ); // done by setTitleText() + m_label->setAngle( axisLabelAngle ); + m_label->setPosition( axisLabelPosition ); + m_label->setReferencePoint( KDChartEnums::PositionSouth ); + QFont font = attributes.font(); + font.setPointSizeF( 0.85 * font.pointSizeF() ); + m_fifty->setFont( font ); + m_fifty->setAngle( fiftyMarkAngle ); + m_fifty->setPosition( fiftyMarkPosition ); + m_fifty->setReferencePoint( fiftyMarkReferencePoint ); +} + +QPair TernaryAxis::requiredMargins() const +{ + QSizeF topleft( 0.0, 0.0 ); + QSizeF bottomRight( 0.0, 0.0 ); + + switch( position().value() ) { + case KDChartEnums::PositionSouth: + // the label of the south axis is, in fact, up north. + topleft.setHeight( m_label->pixmap().height() ); + bottomRight.setHeight( m_fifty->pixmap().height() ); + break; + case KDChartEnums::PositionWest: + bottomRight.setWidth( m_label->pixmap().width() + - m_label->referencePointLocation().x() ); + bottomRight.setHeight( m_label->pixmap().height() + - m_label->referencePointLocation().y() ); + break; + case KDChartEnums::PositionEast: + topleft.setWidth( m_label->pixmap().width() + - ( m_label->pixmap().width() + - m_label->referencePointLocation().x() ) ); + bottomRight.setHeight( m_label->pixmap().height() + - ( m_label->pixmap().height() + - m_label->referencePointLocation().y() ) ); + break; + default: + qDebug() << "TernaryAxis::requiredMargins: unknown location"; + } +// qDebug() << "TernaryAxis::requiredMargins:" << topleft << bottomRight; + return QPair( topleft, bottomRight ); +} diff --git a/libkdchart/src/Ternary/KDChartTernaryAxis.h b/libkdchart/src/Ternary/KDChartTernaryAxis.h new file mode 100644 index 0000000..8a7cbe4 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryAxis.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTERNARYAXIS_H +#define KDCHARTTERNARYAXIS_H + + +#include +#include +#include + +class PrerenderedLabel; + +namespace KDChart { + + class AbstractTernaryDiagram; + + /** + * The class for ternary axes + */ + class KDCHART_EXPORT TernaryAxis : public AbstractAxis + { + Q_OBJECT + + Q_DISABLE_COPY( TernaryAxis ) + KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( TernaryAxis, AbstractDiagram* ) + + public: + explicit TernaryAxis ( AbstractTernaryDiagram* diagram = 0 ); + ~TernaryAxis(); + + virtual void paintAll( QPainter &); + virtual void paint (QPainter *); + virtual void paintCtx (PaintContext *); + + virtual QRect geometry () const; + virtual void setGeometry (const QRect &rect); + + virtual bool isEmpty () const; + virtual QSize minimumSize () const; + virtual QSize maximumSize () const; + virtual QSize sizeHint () const; + virtual Qt::Orientations expandingDirections () const ; + + virtual const Position position () const; + virtual void setPosition (Position p); + + void setTitleText( const QString& text ); + QString titleText() const; + void setTitleTextAttributes( const TextAttributes &a ); + TextAttributes titleTextAttributes() const; + void resetTitleTextAttributes(); + bool hasDefaultTitleTextAttributes() const; + + QPair requiredMargins() const; + + private: + void updatePrerenderedLabels(); + // TODO, move class variables to private class + QRect m_geometry; + Position m_position; + + QString m_title; + TextAttributes m_titleAttributes; + + // FIXME (Mirko): Move axis labels from grid to here, do not + // expose them, just paint them. Use title text for text. Make + // a function to allow the coordinate plane to calculate the + // necessary margins, like this: + PrerenderedLabel* m_label; + PrerenderedLabel* m_fifty; + }; + + typedef QList TernaryAxisList; +} + +#endif diff --git a/libkdchart/src/Ternary/KDChartTernaryCoordinatePlane.cpp b/libkdchart/src/Ternary/KDChartTernaryCoordinatePlane.cpp new file mode 100644 index 0000000..0f3edab --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryCoordinatePlane.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartTernaryCoordinatePlane.h" +#include "KDChartTernaryCoordinatePlane_p.h" + +#include +#include + +#include "KDChartPaintContext.h" +#include "KDChartPainterSaver_p.h" +#include "KDChartTernaryAxis.h" +#include "KDChartAbstractTernaryDiagram.h" + +#include "TernaryConstants.h" + +using namespace KDChart; + +#define d d_func() + +TernaryCoordinatePlane::Private::Private() + : AbstractCoordinatePlane::Private() +{ +} + +TernaryCoordinatePlane::TernaryCoordinatePlane( Chart* parent ) + : AbstractCoordinatePlane( new Private(), parent ) +{ +} + +TernaryCoordinatePlane::~TernaryCoordinatePlane() +{ +} + +void TernaryCoordinatePlane::init() +{ +} + +void TernaryCoordinatePlane::addDiagram( AbstractDiagram* diagram ) +{ + Q_ASSERT_X ( dynamic_cast( diagram ), + "TernaryCoordinatePlane::addDiagram", "Only ternary " + "diagrams can be added to a ternary coordinate plane!" ); + AbstractCoordinatePlane::addDiagram ( diagram ); +// connect ( diagram, SIGNAL ( layoutChanged ( AbstractDiagram* ) ), +// SLOT ( slotLayoutChanged ( AbstractDiagram* ) ) ); +// connect( diagram, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) ); +} + +void TernaryCoordinatePlane::layoutDiagrams() +{ // this is our "resize event": + // all diagrams always take the same space, nothing to be done here + // the "inner" margin (adjustments to diagram coordinates) + QRectF diagramNativeRectangle ( QPointF( 0.0, 0.0 ), + QSizeF( TriangleWidth, TriangleHeight ) ); + QPair margins = grid()->requiredMargins(); + d->diagramRect = areaGeometry(); + diagramNativeRectangle.adjust + (-margins.first.width(), -margins.first.height(), + margins.second.width(), margins.second.height() ); + + // the "outer" margin (distance between diagram contents and area, + // determined by axis label overlap + { + QSizeF topleft( 0.0, 0.0 ); + QSizeF bottomRight( 0.0, 0.0 ); + Q_FOREACH( AbstractDiagram* abstractDiagram, diagrams() ) { + AbstractTernaryDiagram* diagram = + qobject_cast( abstractDiagram ); + Q_ASSERT( diagram ); + Q_FOREACH( TernaryAxis* axis, diagram->axes() ) { + QPair margin = axis->requiredMargins(); + topleft = topleft.expandedTo( margin.first ); + bottomRight = bottomRight.expandedTo( margin.second ); + } + } + d->diagramRectContainer = + d->diagramRect.adjusted( topleft.width(), + topleft.height(), + -bottomRight.width(), + -bottomRight.height() ); + } + + // now calculate isometric projection, x and y widget coordinate + // units, and location of (0.0, 0.0) in diagram coordinates + QPointF zeroZeroPoint = d->diagramRectContainer.bottomLeft(); + double w = d->diagramRectContainer.width(); + double h = d->diagramRectContainer.height(); + double usableWidth; + double usableHeight; + + if ( TriangleHeight * w > h ) { + // shorten width: + usableWidth = h / diagramNativeRectangle.height(); + usableHeight = h; + zeroZeroPoint.setX( zeroZeroPoint.x() + ( w - usableWidth ) / 2 ); + } else { + // reduce height: + usableWidth = w; + usableHeight = diagramNativeRectangle.height() * w; + zeroZeroPoint.setY( zeroZeroPoint.y() - ( h - usableHeight ) / 2 ); + } + // the rectangle has 1 as it's width, and TriangleHeight as it's + // height - so this is how we translate that to widget coordinates: + d->xUnit = usableWidth / diagramNativeRectangle.width(); // only because we normalize the values to [0..1] + d->yUnit = -usableHeight / diagramNativeRectangle.height(); + + // now move zeroZeroPoint so that it does not include the tick marks + { + double descent = diagramNativeRectangle.height() - TriangleHeight; + double rightShift = -diagramNativeRectangle.x(); + zeroZeroPoint += QPointF( rightShift * d->xUnit, descent * d->yUnit ); + } + + d->diagramRect.setBottomLeft( zeroZeroPoint ); + d->diagramRect.setTopRight( QPointF( usableWidth, -usableHeight ) + zeroZeroPoint ); +} + +const QPointF TernaryCoordinatePlane::translate( const QPointF& point ) const +{ + return QPointF( d->diagramRect.bottomLeft().x() + point.x() * d->xUnit, + d->diagramRect.bottomLeft().y() + point.y() * d->yUnit ); +} + +QSize TernaryCoordinatePlane::minimumSizeHint() const +{ + // FIXME temp + return QSize(); +} + +QSizePolicy TernaryCoordinatePlane::sizePolicy() const +{ + return QSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding ); +} + +void TernaryCoordinatePlane::paint( QPainter* painter ) +{ + PainterSaver s( painter ); + // FIXME: this is not a good location for that: + painter->setRenderHint(QPainter::Antialiasing, true ); + +// painter->setPen( QColor( "gold" ) ); +// painter->setBrush( QColor( "gold" ) ); +// painter->drawRect( d->diagramRectContainer ); + + AbstractDiagramList diags = diagrams(); + if ( !diags.isEmpty() ) + { + PaintContext ctx; + ctx.setPainter ( painter ); + ctx.setCoordinatePlane ( this ); + const QRectF drawArea( areaGeometry() ); + ctx.setRectangle ( drawArea ); + + // enabling clipping so that we're not drawing outside +// QRect clipRect = drawArea.toRect().adjusted( -1, -1, 1, 1 ); +// QRegion clipRegion( clipRect ); +// painter->setClipRegion( clipRegion ); + + // paint the coordinate system rulers: + Q_ASSERT( d->grid != 0 ); + d->grid->drawGrid( &ctx ); + + // paint the diagrams: + for ( int i = 0; i < diags.size(); i++ ) + { + PainterSaver diagramPainterSaver( painter ); + diags[i]->paint ( &ctx ); + } + } +} + +DataDimensionsList TernaryCoordinatePlane::getDataDimensionsList() const +{ // not needed + return DataDimensionsList(); +} + +TernaryGrid* TernaryCoordinatePlane::grid() const +{ + TernaryGrid* ternaryGrid = static_cast( d->grid ); + Q_ASSERT( dynamic_cast( d->grid ) ); + return ternaryGrid; +} + +#undef d diff --git a/libkdchart/src/Ternary/KDChartTernaryCoordinatePlane.h b/libkdchart/src/Ternary/KDChartTernaryCoordinatePlane.h new file mode 100644 index 0000000..048d079 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryCoordinatePlane.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTERNARYCOORDINATEPLANE_H +#define KDCHARTTERNARYCOORDINATEPLANE_H + +#include "../KDChartAbstractCoordinatePlane.h" + +namespace KDChart { + + class TernaryGrid; + + /** + * @brief Ternary coordinate plane + */ + class KDCHART_EXPORT TernaryCoordinatePlane + : public AbstractCoordinatePlane + { + Q_OBJECT + Q_DISABLE_COPY( TernaryCoordinatePlane ) + KDCHART_DECLARE_PRIVATE_DERIVED_PARENT( TernaryCoordinatePlane, Chart* ) + + public: + explicit TernaryCoordinatePlane( Chart* parent = 0 ); + ~TernaryCoordinatePlane(); + + void addDiagram( AbstractDiagram* diagram ); + + void layoutDiagrams(); + + const QPointF translate ( const QPointF& diagramPoint ) const; + + void paint( QPainter* ); + DataDimensionsList getDataDimensionsList() const; + + /** \reimpl */ + QSize minimumSizeHint() const; + /** \reimpl */ + QSizePolicy sizePolicy() const; + + private: + TernaryGrid* grid() const; + }; + +} + +#endif diff --git a/libkdchart/src/Ternary/KDChartTernaryCoordinatePlane_p.h b/libkdchart/src/Ternary/KDChartTernaryCoordinatePlane_p.h new file mode 100644 index 0000000..1b483a6 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryCoordinatePlane_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTERNARYCOORDINATEPLANE_P_H +#define KDCHARTTERNARYCOORDINATEPLANE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include "KDChartTernaryGrid.h" +#include "KDChartAbstractCoordinatePlane_p.h" + +#include + +namespace KDChart { + + class TernaryAxis; + + /** + * \internal + */ + + class TernaryCoordinatePlane::Private : public AbstractCoordinatePlane::Private + { + friend class TernaryCoordinatePlane; + + public: + explicit Private(); + + virtual ~Private() { + // grid is delete in base class dtor + } + + virtual void initialize() + { + grid = new TernaryGrid(); + xUnit = 0.0; + yUnit = 0.0; + } + + QList axes; + + TextAttributes labelAttributes; + + // the diagram is drawn within this rectangle, which is within + // this widget: + QRectF diagramRectContainer; + // this is the "frame" of the plot area + QRectF diagramRect; + // multiply m_xUnit with a [0..1] value to get an isometric + // widget coordinate + double xUnit; + // same for y: + double yUnit; + + }; + + KDCHART_IMPL_DERIVED_PLANE(TernaryCoordinatePlane, AbstractCoordinatePlane) +} + +#endif /* KDCHARTTERNARYCOORDINATEPLANE_P_H */ diff --git a/libkdchart/src/Ternary/KDChartTernaryGrid.cpp b/libkdchart/src/Ternary/KDChartTernaryGrid.cpp new file mode 100644 index 0000000..d5fa44c --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryGrid.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartTernaryGrid.h" + +#include + +#include +#include + +#include +#include + +#include "TernaryPoint.h" +#include "TernaryConstants.h" +#include "KDChartPaintContext.h" +#include "KDChartPainterSaver_p.h" +#include "KDChartTernaryCoordinatePlane.h" +#include "KDChartPrintingParameters.h" + +using namespace KDChart; + +TickInfo::TickInfo( double _percentage, int _depth ) + : percentage ( _percentage ) + , depth( _depth ) +{ +} + +bool KDChart::operator==(const TickInfo& left, const TickInfo& right) +{ + return fabs( left.percentage - right.percentage ) + <= std::numeric_limits::epsilon() + && left.depth == right.depth; +} + +TernaryGrid::TernaryGrid() + : AbstractGrid() +{ +} + +TernaryGrid::~TernaryGrid() +{ +} + +void TernaryGrid::drawGrid( PaintContext* context ) +{ + static const int GridLineDistanceTreshold = 20; // pixels between each grid line + + QPainter& painter = *context->painter(); // recover from pointer madness + PainterSaver s( &painter ); // can i have a reference based version of that? + TernaryCoordinatePlane* plane = dynamic_cast(context->coordinatePlane()); + Q_ASSERT_X ( plane, "TernaryGrid::drawGrid", + "Bad function call: PaintContext::coodinatePlane() NOT a ternary plane." ); + + // translate the points and see how many grid lines we can draw: + const int MaxDepth = 3; + double xPixels = plane->translate( TriangleBottomRight ).x() - + plane->translate( TriangleBottomLeft ).x(); + int granularity = 20; + if ( xPixels > 10 * GridLineDistanceTreshold ) granularity = 10; + if ( xPixels > 20 * GridLineDistanceTreshold ) granularity = 5; + + m_tickInfo.clear(); + for ( int i = granularity; i < 100; i+=granularity ) + { + TickInfo tick( ( 1.0 * i ) / 100.0, 2 ); + if ( i % 10 == 0 ) tick.depth = 1; + if ( i % 20 == 0 ) tick.depth = 0; + m_tickInfo.append( tick ); + } + + QVector lines[MaxDepth]; + {Q_FOREACH( const TickInfo& tick, m_tickInfo ) { + const double& percent = tick.percentage; + { // draw parallels to B + TernaryPoint ternaryStart( percent, 1.0 - percent ); + TernaryPoint ternaryEnd( 0.0, 1.0 - percent ); + QPointF start( translate( ternaryStart ) ); + QPointF end( translate( ternaryEnd ) ); + lines[tick.depth].append( QLineF( plane->translate( start ), + plane->translate( end ) ) ); + } + { // draw parallels to C + TernaryPoint ternaryStart( percent, 0.0 ); + TernaryPoint ternaryEnd( 0.0, percent ); + QPointF start( translate( ternaryStart ) ); + QPointF end( translate( ternaryEnd ) ); + lines[tick.depth].append( QLineF( plane->translate( start ), + plane->translate( end ) ) ); + } + { // draw parallels to A + TernaryPoint ternaryStart( percent, 1.0 - percent ); + TernaryPoint ternaryEnd( percent, 0.0 ); + QPointF start( translate( ternaryStart ) ); + QPointF end( translate( ternaryEnd ) ); + lines[tick.depth].append( QLineF( plane->translate( start ), + plane->translate( end ) ) ); + } + }} + + // now draw the lines: + painter.setPen( PrintingParameters::scalePen( QPen( QColor( "lightgray" ), 1 ) ) ); + painter.setBrush( QColor( "lightgray" ) ); + painter.drawLines( lines[2] ); + painter.setPen( PrintingParameters::scalePen( QPen( QColor( "gray" ), 1 ) ) ); + painter.setBrush( QColor( "gray" ) ); + painter.drawLines( lines[1] ); + painter.setPen( PrintingParameters::scalePen( QPen( QColor( "darkslategray" ), 1 ) ) ); + painter.setBrush( QColor( "darkslategray" ) ); + painter.drawLines( lines[0] ); + + // now draw the triangle (this could be part of the axis, in fact): + painter.setPen( PrintingParameters::scalePen( QPen( Qt::black, 1 ) ) ); + // make sure this does not fill, otherwise it wipes the contents + // of the triangle (doh!): + painter.setBrush( Qt::NoBrush ); + QPolygonF points; + points << plane->translate( TriangleBottomLeft ) + << plane->translate( TriangleBottomRight ) + << plane->translate( TriangleTop ); + painter.drawPolygon( points ); + + // now draw the ticks: + painter.setPen( PrintingParameters::scalePen( QPen( Qt::black ) ) ); + painter.setBrush( Qt::black ); + + QVector ticks; + // prepare list of percentages, then calculate lines: + QVector percentages( m_tickInfo ); + // I have commented those out, I think it looks ugly if they are + // enabled: + // percentages.prepend( 0.0 ); + // percentages.append( 1.0 ); + + // FIXME this may need a predicate that takes eplison into account + // (but it does not hurt, since it will not make the painter + // paint two lines): + percentages.erase( std::unique( percentages.begin(), percentages.end() ), + percentages.end() ); + + {Q_FOREACH( const TickInfo& tick, percentages ) { + const double& percent = tick.percentage; + { // BC axis markers: + const QPointF markerDistance( FullMarkerDistanceBC + / ( tick.depth + 1 ) ); + QPointF start( percent, 0.0 ); + ticks.append( QLineF( plane->translate( start ), + plane->translate( start - markerDistance ) ) ); + } + { // AC axis markers: + const QPointF markerDistance( FullMarkerDistanceAC + / ( tick.depth + 1 ) ); + const QPointF start( TriangleBottomRight + percent * AxisVector_C_A ); + const QPointF end( start + markerDistance ); + ticks.append( QLineF( plane->translate( start ), + plane->translate( end ) ) ); + } + { + // AB axis markers: + const QPointF markerDistance( FullMarkerDistanceBA + / ( tick.depth +1 ) ); + const QPointF start( percent * AxisVector_B_A ); + const QPointF end( start + markerDistance ); + ticks.append( QLineF( plane->translate( start ), + plane->translate( end ) ) ); + } + }} + painter.drawLines( ticks ); +} + +DataDimensionsList TernaryGrid::calculateGrid( const DataDimensionsList& ) const +{ + return DataDimensionsList(); +} + +QPair TernaryGrid::requiredMargins() const +{ +// double topMargin = ( FullMarkerDistanceBA * RelMarkerLength ).x(); + double topMargin = 0.0; // no markers on tip of triangle + double leftMargin = fabs( FullMarkerDistanceBA.x() ); + double bottomMargin = fabs( FullMarkerDistanceBC.y() ); +// qDebug() << "TernaryGrid::requiredMargins: leftMargin:" << leftMargin +// << ", bottomMargin:" << bottomMargin +// << ", topMargin:" << topMargin +// << ", FullMarkerDistanceBC:" << FullMarkerDistanceBC +// << ", FullMarkerDistanceBA:" << FullMarkerDistanceBA +// << ", FullMarkerDistanceAC:" << FullMarkerDistanceAC +// << ", RelMarkerLength:" << RelMarkerLength; + return QPair + ( QSizeF( leftMargin, topMargin ), + QSizeF( leftMargin, bottomMargin ) ); +} + +const QVector& TernaryGrid::tickInfo() const +{ + return m_tickInfo; +} diff --git a/libkdchart/src/Ternary/KDChartTernaryGrid.h b/libkdchart/src/Ternary/KDChartTernaryGrid.h new file mode 100644 index 0000000..f3de1da --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryGrid.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTERNARYGRID_H +#define KDCHARTTERNARYGRID_H + +#include + +#include "KDChartAbstractGrid.h" +#include "PrerenderedElements/KDChartTextLabelCache.h" + +namespace KDChart { + + struct TickInfo { + TickInfo( double percentage = 0, int depth = 0 ); + double percentage; + int depth; + }; + + bool operator==(const TickInfo&, const TickInfo& ); + + class PaintContext; + + // VERIFY: Grids are not public API, are they? + class TernaryGrid : public AbstractGrid + { + public: + TernaryGrid(); + + virtual ~TernaryGrid(); + + void drawGrid( PaintContext* context ); + DataDimensionsList calculateGrid( const DataDimensionsList& rawDataDimensions ) const; + + /** Returns two QSizeF objects specifying the dimension of the + margins needed between each corner of the diagram and the + border of the drawing area. Margins are required because + the tick marks are placed outside of the trianges + containing rectangle. + The margins are returned in diagram coordinates, + since the grid does not know about widget coordinates. + */ + QPair requiredMargins() const; + /** Return the locations of the grid lines, so that axes can + draw axis rulers at the correct positions. + This information is valid after the grid has been + painted (that is, the axes need to be painted after the + grid. */ + const QVector& tickInfo() const; + private: + QVector m_tickInfo; + // QList m_labels; + }; + +} + +#endif diff --git a/libkdchart/src/Ternary/KDChartTernaryLineDiagram.cpp b/libkdchart/src/Ternary/KDChartTernaryLineDiagram.cpp new file mode 100644 index 0000000..a478b25 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryLineDiagram.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartTernaryLineDiagram.h" +#include "KDChartTernaryLineDiagram_p.h" + +#include + +#include + +#include + +#include "KDChartLineAttributes.h" +#include "KDChartDataValueAttributes.h" +#include "KDChartMarkerAttributes.h" +#include "TernaryPoint.h" +#include "TernaryConstants.h" +#include "KDChartPainterSaver_p.h" + +using namespace KDChart; + +#define d d_func() + +TernaryLineDiagram::Private::Private() + : AbstractTernaryDiagram::Private() +{ +} + +TernaryLineDiagram::TernaryLineDiagram ( QWidget* parent, + TernaryCoordinatePlane* plane ) + : AbstractTernaryDiagram( new Private(), parent, plane ) +{ + init(); + setDatasetDimensionInternal( 3 ); // the third column is implicit + + DataValueAttributes dataValueAttributes; + dataValueAttributes.setVisible( true ); + MarkerAttributes markerAttributes; + markerAttributes.setMarkerStyle( MarkerAttributes::MarkerCircle ); + markerAttributes.setVisible( true ); + dataValueAttributes.setMarkerAttributes( markerAttributes ); + attributesModel()->setDefaultForRole( + KDChart::DataValueLabelAttributesRole, + qVariantFromValue( dataValueAttributes ) ); +} + +TernaryLineDiagram::~TernaryLineDiagram() +{ +} + +void TernaryLineDiagram::init() +{ +} + +void TernaryLineDiagram::resize (const QSizeF& area) +{ + Q_UNUSED( area ); +} + +void TernaryLineDiagram::paint (PaintContext *paintContext) +{ + d->reverseMapper.clear(); + + d->paint( paintContext ); + // sanity checks: + if ( model() == 0 ) return; + + QPainter* p = paintContext->painter(); + PainterSaver s( p ); + + TernaryCoordinatePlane* plane = + (TernaryCoordinatePlane*) paintContext->coordinatePlane(); + Q_ASSERT( plane ); + + double x, y, z; + + + // for some reason(?) TernaryPointDiagram is using per-diagram DVAs only: + const DataValueAttributes attrs( dataValueAttributes() ); + + + d->clearListOfAlreadyDrawnDataValueTexts(); + + int columnCount = model()->columnCount( rootIndex() ); + QPointF start; + for(int column=0; columnrowCount( rootIndex() ); + for( int row = 0; row < numrows; row++ ) + { + // see if there is data otherwise skip + QModelIndex base = model()->index( row, column ); + if( ! model()->data( base ).isNull() ) + { + p->setPen( PrintingParameters::scalePen( pen( base ) ) ); + p->setBrush( brush( base ) ); + + // retrieve data + x = qMax( model()->data( model()->index( row, column, rootIndex() ) ).toDouble(), + 0.0 ); + y = qMax( model()->data( model()->index( row, column+1, rootIndex() ) ).toDouble(), + 0.0 ); + z = qMax( model()->data( model()->index( row, column+2, rootIndex() ) ).toDouble(), + 0.0 ); + + double total = x + y + z; + if ( fabs( total ) > 3 * std::numeric_limits::epsilon() ) { + TernaryPoint tPunkt( x / total, y / total ); + QPointF diagramLocation = translate( tPunkt ); + QPointF widgetLocation = plane->translate( diagramLocation ); + + if ( row > 0 ) { + p->drawLine( start, widgetLocation ); + } + paintMarker( p, model()->index( row, column, rootIndex() ), widgetLocation ); + start = widgetLocation; + // retrieve text and data value attributes + // FIXME use data model DisplayRole text + QString text = tr( "(%1, %2, %3)" ) + .arg( x * 100, 0, 'f', 0 ) + .arg( y * 100, 0, 'f', 0 ) + .arg( z * 100, 0, 'f', 0 ); + d->paintDataValueText( this, p, attrs, widgetLocation, text, true ); + } else { + // ignore and do not paint this point, garbage data + qDebug() << "TernaryPointDiagram::paint: data point x/y/z:" + << x << "/" << y << "/" << z << "ignored, unusable."; + } + } + } + } +} + +const QPair< QPointF, QPointF > TernaryLineDiagram::calculateDataBoundaries () const +{ + // this is a constant, because we defined it to be one: + static QPair Boundaries( + TriangleBottomLeft, + QPointF( TriangleBottomRight.x(), TriangleHeight ) ); + return Boundaries; +} diff --git a/libkdchart/src/Ternary/KDChartTernaryLineDiagram.h b/libkdchart/src/Ternary/KDChartTernaryLineDiagram.h new file mode 100644 index 0000000..2c52da1 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryLineDiagram.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTERNARYLINEDIAGRAM_H +#define KDCHARTTERNARYLINEDIAGRAM_H + +#include "KDChartTernaryCoordinatePlane.h" +#include "KDChartAbstractTernaryDiagram.h" + + +namespace KDChart { + + /** + * @brief A TernaryLineDiagram is a line diagram with a ternary coordinate plane + */ + class KDCHART_EXPORT TernaryLineDiagram : public AbstractTernaryDiagram + { + Q_OBJECT + Q_DISABLE_COPY( TernaryLineDiagram ) + KDCHART_DECLARE_DERIVED_DIAGRAM( TernaryLineDiagram, TernaryCoordinatePlane ) + + public: + explicit TernaryLineDiagram ( QWidget* parent = 0, TernaryCoordinatePlane* plane = 0 ); + virtual ~TernaryLineDiagram(); + + void resize (const QSizeF &area); + void paint (PaintContext *paintContext); + + protected: + const QPair< QPointF, QPointF > calculateDataBoundaries () const; + + }; +} + +#endif diff --git a/libkdchart/src/Ternary/KDChartTernaryLineDiagram_p.h b/libkdchart/src/Ternary/KDChartTernaryLineDiagram_p.h new file mode 100644 index 0000000..1de73a8 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryLineDiagram_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTERNARYLINEDIAGRAM_P_H +#define KDCHARTTERNARYLINEDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "KDChartAbstractTernaryDiagram_p.h" + +#include + +namespace KDChart { + +/** + * \internal + */ + class TernaryLineDiagram::Private : public AbstractTernaryDiagram::Private + { + friend class TernaryLineDiagram; + public: + Private(); + ~Private() {} + + Private( const Private& rhs ) + : AbstractTernaryDiagram::Private( rhs ) + { + } + + }; + + KDCHART_IMPL_DERIVED_DIAGRAM( TernaryLineDiagram, AbstractTernaryDiagram, TernaryCoordinatePlane ) +/* + inline LineDiagram::LineDiagram( Private * p, TernaryCoordinatePlane* plane ) + : AbstractTernaryDiagram( p, plane ) { init(); } + inline LineDiagram::Private * LineDiagram::d_func() + { return static_cast( AbstractTernaryDiagram::d_func() ); } + inline const LineDiagram::Private * LineDiagram::d_func() const + { return static_cast( AbstractTernaryDiagram::d_func() ); } +*/ + +} + +#endif /* KDCHARTTERNARYLINEDIAGRAM_P_H */ + diff --git a/libkdchart/src/Ternary/KDChartTernaryPointDiagram.cpp b/libkdchart/src/Ternary/KDChartTernaryPointDiagram.cpp new file mode 100644 index 0000000..533a2bb --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryPointDiagram.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "KDChartTernaryPointDiagram.h" +#include "KDChartTernaryPointDiagram_p.h" + +#include + +#include + +#include + +#include "TernaryPoint.h" +#include "TernaryConstants.h" + +using namespace KDChart; + +#define d d_func() + +TernaryPointDiagram::Private::Private() + : AbstractTernaryDiagram::Private() +{ +} + +TernaryPointDiagram::TernaryPointDiagram ( QWidget* parent, + TernaryCoordinatePlane* plane ) + : AbstractTernaryDiagram( new Private(), parent, plane ) +{ + init(); + setDatasetDimensionInternal( 3 ); // the third column is implicit +} + +TernaryPointDiagram::~TernaryPointDiagram() +{ +} + +void TernaryPointDiagram::init() +{ + d->reverseMapper.setDiagram( this ); +} + +void TernaryPointDiagram::resize (const QSizeF& area) +{ + Q_UNUSED( area ); +} + +void TernaryPointDiagram::paint (PaintContext *paintContext) +{ + d->reverseMapper.clear(); + + d->paint( paintContext ); + + // sanity checks: + if ( model() == 0 ) return; + + QPainter* p = paintContext->painter(); + PainterSaver s( p ); + + TernaryCoordinatePlane* plane = + (TernaryCoordinatePlane*) paintContext->coordinatePlane(); + Q_ASSERT( plane ); + + double x, y, z; + + + // for some reason(?) TernaryPointDiagram is using per-diagram DVAs only: + const DataValueAttributes attrs( dataValueAttributes() ); + + d->clearListOfAlreadyDrawnDataValueTexts(); + + int columnCount = model()->columnCount( rootIndex() ); + for(int column=0; columnrowCount( rootIndex() ); + for( int row = 0; row < numrows; row++ ) + { + QModelIndex base = model()->index( row, column, rootIndex() ); + // see if there is data otherwise skip + if( ! model()->data( model()->index( row, column+0, rootIndex() ) ).isNull() ) + { + p->setPen( PrintingParameters::scalePen( pen( base ) ) ); + p->setBrush( brush( base ) ); + + // retrieve data + x = qMax( model()->data( model()->index( row, column+0, rootIndex() ) ).toDouble(), + 0.0 ); + y = qMax( model()->data( model()->index( row, column+1, rootIndex() ) ).toDouble(), + 0.0 ); + z = qMax( model()->data( model()->index( row, column+2, rootIndex() ) ).toDouble(), + 0.0 ); + + // fix messed up data values (paint as much as possible) + double total = x + y + z; + if ( fabs( total ) > 3 * std::numeric_limits::epsilon() ) { + TernaryPoint tPunkt( x / total, y / total ); + QPointF diagramLocation = translate( tPunkt ); + QPointF widgetLocation = plane->translate( diagramLocation ); + + paintMarker( p, model()->index( row, column, rootIndex() ), widgetLocation ); + QString text = tr( "(%1, %2, %3)" ) + .arg( x * 100, 0, 'f', 0 ) + .arg( y * 100, 0, 'f', 0 ) + .arg( z * 100, 0, 'f', 0 ); + d->paintDataValueText( this, p, attrs, widgetLocation, text, true ); + } else { + // ignore and do not paint this point, garbage data + qDebug() << "TernaryPointDiagram::paint: data point x/y/z:" + << x << "/" << y << "/" << z << "ignored, unusable."; + } + } + } + } +} + +const QPair< QPointF, QPointF > TernaryPointDiagram::calculateDataBoundaries () const +{ + // this is a constant, because we defined it to be one: + static QPair Boundaries( + TriangleBottomLeft, + QPointF( TriangleBottomRight.x(), TriangleHeight ) ); + return Boundaries; +} + diff --git a/libkdchart/src/Ternary/KDChartTernaryPointDiagram.h b/libkdchart/src/Ternary/KDChartTernaryPointDiagram.h new file mode 100644 index 0000000..a45d9b1 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryPointDiagram.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTERNARYPOINTDIAGRAM_H +#define KDCHARTTERNARYPOINTDIAGRAM_H + +#include "KDChartTernaryCoordinatePlane.h" +#include "KDChartAbstractTernaryDiagram.h" + +namespace KDChart { + + /** + * @brief A TernaryPointDiagram is a point diagram within a ternary coordinate plane + */ + class KDCHART_EXPORT TernaryPointDiagram : public AbstractTernaryDiagram + { + Q_OBJECT + Q_DISABLE_COPY( TernaryPointDiagram ) + KDCHART_DECLARE_DERIVED_DIAGRAM( TernaryPointDiagram, TernaryCoordinatePlane ) + + public: + explicit TernaryPointDiagram ( QWidget* parent = 0, TernaryCoordinatePlane* plane = 0 ); + virtual ~TernaryPointDiagram(); + + virtual void resize (const QSizeF &area); + virtual void paint (PaintContext *paintContext); + + protected: + virtual const QPair< QPointF, QPointF > calculateDataBoundaries () const; + }; + +} + +#endif diff --git a/libkdchart/src/Ternary/KDChartTernaryPointDiagram_p.h b/libkdchart/src/Ternary/KDChartTernaryPointDiagram_p.h new file mode 100644 index 0000000..ae3e0a1 --- /dev/null +++ b/libkdchart/src/Ternary/KDChartTernaryPointDiagram_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHARTTERNARYPOINTDIAGRAM_P_H +#define KDCHARTTERNARYPOINTDIAGRAM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the KD Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include "KDChartAbstractTernaryDiagram_p.h" + +#include + +namespace KDChart { + +/** + * \internal + */ + class TernaryPointDiagram::Private : public AbstractTernaryDiagram::Private + { + friend class TernaryPointDiagram; + public: + Private(); + ~Private() {} + + Private( const Private& rhs ) + : AbstractTernaryDiagram::Private( rhs ) + { + } + + }; + +KDCHART_IMPL_DERIVED_DIAGRAM( TernaryPointDiagram, AbstractTernaryDiagram, TernaryCoordinatePlane ) +/* +inline TernaryPointDiagram::TernaryPointDiagram( Private * p, TernaryCoordinatePlane* plane ) + : AbstractTernaryDiagram( p, plane ) { init(); } +inline TernaryPointDiagram::Private * TernaryPointDiagram::d_func() +{ return static_cast( AbstractTernaryDiagram::d_func() ); } +inline const TernaryPointDiagram::Private * TernaryPointDiagram::d_func() const +{ return static_cast( AbstractTernaryDiagram::d_func() ); } +*/ + +} + +#endif /* KDCHARTTERNARYPOINTDIAGRAM_P_H */ + diff --git a/libkdchart/src/Ternary/TernaryConstants.cpp b/libkdchart/src/Ternary/TernaryConstants.cpp new file mode 100644 index 0000000..086e5fd --- /dev/null +++ b/libkdchart/src/Ternary/TernaryConstants.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "TernaryConstants.h" + +#include + +using namespace std; + +extern const double Sqrt3 = sqrt( 3.0 ); +extern const double TriangleWidth = 1.0; +extern const double TriangleHeight = 0.5 * Sqrt3; +extern const QPointF TriangleTop( 0.5, TriangleHeight ); +extern const QPointF TriangleBottomLeft( 0.0, 0.0 ); +extern const QPointF TriangleBottomRight( 1.0, 0.0 ); +extern const QPointF AxisVector_C_A( TriangleTop - TriangleBottomRight ); +extern const QPointF Norm_C_A( -AxisVector_C_A.y(), AxisVector_C_A.x() ); +extern const QPointF AxisVector_B_A( TriangleTop ); +extern const QPointF Norm_B_A( -AxisVector_B_A.y(), AxisVector_B_A.x() ); +extern const QPointF AxisVector_B_C( TriangleBottomRight ); +extern const QPointF Norm_B_C( -AxisVector_B_C.y(), AxisVector_B_C.x() ); + +extern const double RelMarkerLength = 0.03 * TriangleWidth; +extern const QPointF FullMarkerDistanceBC( RelMarkerLength * Norm_B_C ); +extern const QPointF FullMarkerDistanceAC( -RelMarkerLength * Norm_C_A ); +extern const QPointF FullMarkerDistanceBA( RelMarkerLength * Norm_B_A ); + diff --git a/libkdchart/src/Ternary/TernaryConstants.h b/libkdchart/src/Ternary/TernaryConstants.h new file mode 100644 index 0000000..2577290 --- /dev/null +++ b/libkdchart/src/Ternary/TernaryConstants.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef TERNARYCONSTANTS_H +#define TERNARYCONSTANTS_H + +#include + +extern const double Sqrt3; +extern const double TriangleWidth; +extern const double TriangleHeight; +extern const QPointF TriangleTop; +extern const QPointF TriangleBottomLeft; +extern const QPointF TriangleBottomRight; +extern const QPointF AxisVector_C_A; +extern const QPointF Norm_C_A; +extern const QPointF AxisVector_B_A; +extern const QPointF Norm_B_A; +extern const QPointF AxisVector_B_C; +extern const QPointF Norm_B_C; +extern const double RelMarkerLength; +extern const QPointF FullMarkerDistanceBC; +extern const QPointF FullMarkerDistanceAC; +extern const QPointF FullMarkerDistanceBA; + +#endif diff --git a/libkdchart/src/Ternary/TernaryPoint.cpp b/libkdchart/src/Ternary/TernaryPoint.cpp new file mode 100644 index 0000000..20149de --- /dev/null +++ b/libkdchart/src/Ternary/TernaryPoint.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "TernaryPoint.h" +#include "TernaryConstants.h" + +#include + +#include +#include + +TernaryPoint::TernaryPoint() + : m_a( -1.0 ) + , m_b( -1.0 ) +{ + Q_ASSERT( !isValid() ); +} + +TernaryPoint::TernaryPoint( double a, double b ) + : m_a( -1.0 ) + , m_b( -1.0 ) +{ + set( a, b ); +} + +void TernaryPoint::set( double a, double b ) +{ + if ( a >= 0.0 && a <= 1.0 + && b >= 0.0 && b <= 1.0 + && 1.0 - a - b >= -2.0 * std::numeric_limits::epsilon() ) { + m_a = a; + m_b = b; + Q_ASSERT( isValid() ); // more a test for isValid + } else { + m_a = -1.0; + m_b = -1.0; + Q_ASSERT( ! isValid() ); + } +} + +bool TernaryPoint::isValid() const +{ + return + m_a >= 0.0 && m_a <= 1.0 + && m_b >= 0.0 && m_b <= 1.0 + && 1.0 - m_a + m_b >= - std::numeric_limits::epsilon(); +} + +QDebug operator<<( QDebug stream, const TernaryPoint& point ) +{ + QString string; + QTextStream text( &string ); + text << "[TernaryPoint: "; + if ( point.isValid() ) { + text.setFieldWidth( 2 ); + text.setPadChar( QLatin1Char( '0' ) ); + text << ( int ) ( point.a() * 100.0 ) << "%|" + << ( int ) ( point.b() * 100.0 ) << "%|" + << ( int ) ( point.c() * 100.0 ) << "%]"; + } else { + text << "a=" << point.a() << " - b=" << point.b() << " - INVALID]"; + } + stream << string; + return stream; +} + +QPointF translate( const TernaryPoint& point ) +{ + if ( point.isValid() ) { + // the position is calculated by + // - first moving along the B-C line to the function that b + // selects + // - then traversing the selected function until we meet with + // the function that A selects (which is a parallel of the B-C + // line) + QPointF bPosition( 1.0 - point.b(), 0.0 ); + QPointF aPosition( point.a() * AxisVector_C_A ); + QPointF result( bPosition + aPosition ); + return result; + } else { + qWarning() << "TernaryPoint::translate(TernaryPoint): cannot translate invalid ternary points:" + << point; + return QPointF(); + } +} diff --git a/libkdchart/src/Ternary/TernaryPoint.h b/libkdchart/src/Ternary/TernaryPoint.h new file mode 100644 index 0000000..8bb98f0 --- /dev/null +++ b/libkdchart/src/Ternary/TernaryPoint.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef TERNARYPOINT_H +#define TERNARYPOINT_H + +#include +#include + +/** + * @brief TernaryPoint defines a point within a ternary coordinate plane + * \internal + */ +class TernaryPoint +{ +public: + TernaryPoint(); + TernaryPoint( double a, double b ); + + double a() const { return m_a; } + double b() const { return m_b; } + double c() const { return 1.0 - m_a - m_b; } + + void set( double a, double b ); + + bool isValid() const; + +private: + double m_a; + double m_b; +}; + +QDebug operator<<( QDebug stream, const TernaryPoint& point ); + +QPointF translate( const TernaryPoint& ); + +#endif diff --git a/libkdchart/src/kdchart_export.h b/libkdchart/src/kdchart_export.h new file mode 100644 index 0000000..ddf88da --- /dev/null +++ b/libkdchart/src/kdchart_export.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Chart library. +** +** Licensees holding valid commercial KD Chart licenses may use this file in +** accordance with the KD Chart Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDCHART_EXPORT_H +#define KDCHART_EXPORT_H + +#include + +# ifdef KDCHART_STATICLIB +# undef KDCHART_SHAREDLIB +# define KDCHART_EXPORT +# define UITOOLS_EXPORT +# define KDCHART_COMPAT_EXPORT +# define KDCHART_PLUGIN_EXPORT +# else +# ifdef KDCHART_BUILD_KDCHART_LIB +# define KDCHART_EXPORT Q_DECL_EXPORT +# else +# define KDCHART_EXPORT Q_DECL_IMPORT +# endif +# ifdef UITOOLS_BUILD_UITOOLS_LIB +# define UITOOLS_EXPORT Q_DECL_EXPORT +# else +# define UITOOLS_EXPORT Q_DECL_IMPORT +# endif +# ifdef KDCHART_BUILD_KDCHART_COMPAT_LIB +# define KDCHART_COMPAT_EXPORT Q_DECL_EXPORT +# else +# define KDCHART_COMPAT_EXPORT Q_DECL_IMPORT +# endif +# ifdef KDCHART_BUILD_PLUGIN_LIB +# define KDCHART_PLUGIN_EXPORT Q_DECL_EXPORT +# else +# define KDCHART_PLUGIN_EXPORT Q_DECL_IMPORT +# endif +# endif + +#endif // KDCHART_EXPORT_H diff --git a/libkdchart/src/ui_KDChartDatasetSelector.h b/libkdchart/src/ui_KDChartDatasetSelector.h new file mode 100644 index 0000000..2b77052 --- /dev/null +++ b/libkdchart/src/ui_KDChartDatasetSelector.h @@ -0,0 +1,181 @@ +/******************************************************************************** +** Form generated from reading UI file 'KDChartDatasetSelector.ui' +** +** Created: Sat Jan 7 12:43:10 2012 +** by: Qt User Interface Compiler version 4.7.3 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef UI_KDCHARTDATASETSELECTOR_H +#define UI_KDCHARTDATASETSELECTOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Ui_DatasetSelector +{ +public: + QHBoxLayout *hboxLayout; + QGroupBox *groupBox; + QGridLayout *gridLayout; + QCheckBox *cbReverseColumns; + QLabel *label_5; + QSpinBox *sbStartColumn; + QLabel *label_2; + QSpinBox *sbColumnCount; + QLabel *label; + QLabel *label_6; + QSpinBox *sbStartRow; + QLabel *label_4; + QCheckBox *cbReverseRows; + QLabel *label_3; + QSpinBox *sbRowCount; + QSpacerItem *spacerItem; + + void setupUi(QWidget *DatasetSelector) + { + if (DatasetSelector->objectName().isEmpty()) + DatasetSelector->setObjectName(QString::fromUtf8("DatasetSelector")); + DatasetSelector->resize(728, 344); + QSizePolicy sizePolicy(static_cast(3), static_cast(3)); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(DatasetSelector->sizePolicy().hasHeightForWidth()); + DatasetSelector->setSizePolicy(sizePolicy); + DatasetSelector->setMinimumSize(QSize(0, 0)); + hboxLayout = new QHBoxLayout(DatasetSelector); +#ifndef Q_OS_MAC + hboxLayout->setSpacing(6); +#endif +#ifndef Q_OS_MAC + hboxLayout->setContentsMargins(9, 9, 9, 9); +#endif + hboxLayout->setObjectName(QString::fromUtf8("hboxLayout")); + groupBox = new QGroupBox(DatasetSelector); + groupBox->setObjectName(QString::fromUtf8("groupBox")); + groupBox->setCheckable(true); + groupBox->setChecked(false); + gridLayout = new QGridLayout(groupBox); +#ifndef Q_OS_MAC + gridLayout->setSpacing(6); +#endif +#ifndef Q_OS_MAC + gridLayout->setContentsMargins(9, 9, 9, 9); +#endif + gridLayout->setObjectName(QString::fromUtf8("gridLayout")); + cbReverseColumns = new QCheckBox(groupBox); + cbReverseColumns->setObjectName(QString::fromUtf8("cbReverseColumns")); + + gridLayout->addWidget(cbReverseColumns, 3, 1, 1, 3); + + label_5 = new QLabel(groupBox); + label_5->setObjectName(QString::fromUtf8("label_5")); + label_5->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout->addWidget(label_5, 3, 0, 1, 1); + + sbStartColumn = new QSpinBox(groupBox); + sbStartColumn->setObjectName(QString::fromUtf8("sbStartColumn")); + + gridLayout->addWidget(sbStartColumn, 2, 3, 1, 1); + + label_2 = new QLabel(groupBox); + label_2->setObjectName(QString::fromUtf8("label_2")); + label_2->setAlignment(Qt::AlignCenter); + + gridLayout->addWidget(label_2, 2, 2, 1, 1); + + sbColumnCount = new QSpinBox(groupBox); + sbColumnCount->setObjectName(QString::fromUtf8("sbColumnCount")); + + gridLayout->addWidget(sbColumnCount, 2, 1, 1, 1); + + label = new QLabel(groupBox); + label->setObjectName(QString::fromUtf8("label")); + label->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout->addWidget(label, 2, 0, 1, 1); + + label_6 = new QLabel(groupBox); + label_6->setObjectName(QString::fromUtf8("label_6")); + label_6->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout->addWidget(label_6, 1, 0, 1, 1); + + sbStartRow = new QSpinBox(groupBox); + sbStartRow->setObjectName(QString::fromUtf8("sbStartRow")); + + gridLayout->addWidget(sbStartRow, 0, 3, 1, 1); + + label_4 = new QLabel(groupBox); + label_4->setObjectName(QString::fromUtf8("label_4")); + label_4->setAlignment(Qt::AlignCenter); + + gridLayout->addWidget(label_4, 0, 2, 1, 1); + + cbReverseRows = new QCheckBox(groupBox); + cbReverseRows->setObjectName(QString::fromUtf8("cbReverseRows")); + + gridLayout->addWidget(cbReverseRows, 1, 1, 1, 3); + + label_3 = new QLabel(groupBox); + label_3->setObjectName(QString::fromUtf8("label_3")); + label_3->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout->addWidget(label_3, 0, 0, 1, 1); + + sbRowCount = new QSpinBox(groupBox); + sbRowCount->setObjectName(QString::fromUtf8("sbRowCount")); + + gridLayout->addWidget(sbRowCount, 0, 1, 1, 1); + + spacerItem = new QSpacerItem(169, 31, QSizePolicy::Minimum, QSizePolicy::Expanding); + + gridLayout->addItem(spacerItem, 4, 2, 1, 1); + + + hboxLayout->addWidget(groupBox); + + + retranslateUi(DatasetSelector); + + QMetaObject::connectSlotsByName(DatasetSelector); + } // setupUi + + void retranslateUi(QWidget *DatasetSelector) + { + DatasetSelector->setWindowTitle(QApplication::translate("DatasetSelector", "Data Selector", 0, QApplication::UnicodeUTF8)); + groupBox->setTitle(QApplication::translate("DatasetSelector", "Only display a subset of the model in the chart:", 0, QApplication::UnicodeUTF8)); + cbReverseColumns->setText(QApplication::translate("DatasetSelector", "in reverse order.", 0, QApplication::UnicodeUTF8)); + label_5->setText(QApplication::translate("DatasetSelector", "...", 0, QApplication::UnicodeUTF8)); + label_2->setText(QApplication::translate("DatasetSelector", "columns starting at column", 0, QApplication::UnicodeUTF8)); + label->setText(QApplication::translate("DatasetSelector", "Display", 0, QApplication::UnicodeUTF8)); + label_6->setText(QApplication::translate("DatasetSelector", "...", 0, QApplication::UnicodeUTF8)); + label_4->setText(QApplication::translate("DatasetSelector", "rows starting at row", 0, QApplication::UnicodeUTF8)); + cbReverseRows->setText(QApplication::translate("DatasetSelector", "in reverse order.", 0, QApplication::UnicodeUTF8)); + label_3->setText(QApplication::translate("DatasetSelector", "Display", 0, QApplication::UnicodeUTF8)); + } // retranslateUi + +}; + +namespace Ui { + class DatasetSelector: public Ui_DatasetSelector {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // UI_UI_KDCHARTDATASETSELECTOR_H