summaryrefslogtreecommitdiffstats
path: root/src/charts/chartdataset.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/charts/chartdataset.cpp')
-rw-r--r--src/charts/chartdataset.cpp639
1 files changed, 639 insertions, 0 deletions
diff --git a/src/charts/chartdataset.cpp b/src/charts/chartdataset.cpp
new file mode 100644
index 00000000..b28e6be0
--- /dev/null
+++ b/src/charts/chartdataset.cpp
@@ -0,0 +1,639 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the Qt Enterprise Charts Add-on.
+**
+** $QT_BEGIN_LICENSE$
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "chartdataset_p.h"
+#include "chartpresenter_p.h"
+#include "qchart.h"
+#include "qchart_p.h"
+#include "qvalueaxis.h"
+#include "qbarcategoryaxis.h"
+#include "qvalueaxis_p.h"
+#include "qcategoryaxis.h"
+#include "qabstractseries_p.h"
+#include "qabstractbarseries.h"
+#include "qstackedbarseries.h"
+#include "qpercentbarseries.h"
+#include "qpieseries.h"
+#include "chartitem_p.h"
+#include "xydomain_p.h"
+#include "xypolardomain_p.h"
+#include "xlogydomain_p.h"
+#include "logxydomain_p.h"
+#include "logxlogydomain_p.h"
+#include "xlogypolardomain_p.h"
+#include "logxypolardomain_p.h"
+#include "logxlogypolardomain_p.h"
+
+#ifndef QT_ON_ARM
+#include "qdatetimeaxis.h"
+#endif
+
+QT_CHARTS_BEGIN_NAMESPACE
+
+ChartDataSet::ChartDataSet(QChart *chart)
+ : QObject(chart),
+ m_chart(chart)
+{
+
+}
+
+ChartDataSet::~ChartDataSet()
+{
+ deleteAllSeries();
+ deleteAllAxes();
+}
+
+/*
+ * This method adds series to chartdataset, series ownership is taken from caller.
+ */
+void ChartDataSet::addSeries(QAbstractSeries *series)
+{
+ if (m_seriesList.contains(series)) {
+ qWarning() << QObject::tr("Can not add series. Series already on the chart.");
+ return;
+ }
+
+ // Ignore unsupported series added to polar chart
+ if (m_chart && m_chart->chartType() == QChart::ChartTypePolar) {
+ if (!(series->type() == QAbstractSeries::SeriesTypeArea
+ || series->type() == QAbstractSeries::SeriesTypeLine
+ || series->type() == QAbstractSeries::SeriesTypeScatter
+ || series->type() == QAbstractSeries::SeriesTypeSpline)) {
+ qWarning() << QObject::tr("Can not add series. Series type is not supported by a polar chart.");
+ return;
+ }
+ series->d_ptr->setDomain(new XYPolarDomain());
+ } else {
+ series->d_ptr->setDomain(new XYDomain());
+ }
+
+ series->d_ptr->initializeDomain();
+ m_seriesList.append(series);
+
+ series->setParent(this); // take ownership
+ series->d_ptr->m_chart = m_chart;
+
+ emit seriesAdded(series);
+}
+
+/*
+ * This method adds axis to chartdataset, axis ownership is taken from caller.
+ */
+void ChartDataSet::addAxis(QAbstractAxis *axis, Qt::Alignment aligment)
+{
+ if (m_axisList.contains(axis)) {
+ qWarning() << QObject::tr("Can not add axis. Axis already on the chart.");
+ return;
+ }
+
+ axis->d_ptr->setAlignment(aligment);
+
+ if (!axis->alignment()) {
+ qWarning() << QObject::tr("No alignment specified !");
+ return;
+ };
+
+ AbstractDomain *newDomain;
+ if (m_chart && m_chart->chartType() == QChart::ChartTypePolar)
+ newDomain = new XYPolarDomain();
+ else
+ newDomain = new XYDomain();
+
+ QSharedPointer<AbstractDomain> domain(newDomain);
+ axis->d_ptr->initializeDomain(domain.data());
+
+ axis->setParent(this);
+ axis->d_ptr->m_chart = m_chart;
+ m_axisList.append(axis);
+
+ emit axisAdded(axis);
+}
+
+/*
+ * This method removes series form chartdataset, series ownership is passed back to caller.
+ */
+void ChartDataSet::removeSeries(QAbstractSeries *series)
+{
+
+ if (! m_seriesList.contains(series)) {
+ qWarning() << QObject::tr("Can not remove series. Series not found on the chart.");
+ return;
+ }
+
+ QList<QAbstractAxis*> axes = series->d_ptr->m_axes;
+
+ foreach(QAbstractAxis* axis, axes) {
+ detachAxis(series,axis);
+ }
+
+ emit seriesRemoved(series);
+ m_seriesList.removeAll(series);
+
+ // Reset domain to default
+ series->d_ptr->setDomain(new XYDomain());
+ series->setParent(0);
+ series->d_ptr->m_chart = 0;
+}
+
+/*
+ * This method removes axis form chartdataset, series ownership is passed back to caller.
+ */
+void ChartDataSet::removeAxis(QAbstractAxis *axis)
+{
+ if (! m_axisList.contains(axis)) {
+ qWarning() << QObject::tr("Can not remove axis. Axis not found on the chart.");
+ return;
+ }
+
+ QList<QAbstractSeries*> series = axis->d_ptr->m_series;
+
+ foreach(QAbstractSeries* s, series) {
+ detachAxis(s,axis);
+ }
+
+ emit axisRemoved(axis);
+ m_axisList.removeAll(axis);
+
+ axis->setParent(0);
+ axis->d_ptr->m_chart = 0;
+}
+
+/*
+ * This method attaches axis to series, return true if success.
+ */
+bool ChartDataSet::attachAxis(QAbstractSeries *series,QAbstractAxis *axis)
+{
+ Q_ASSERT(axis);
+
+ if (!series)
+ return false;
+
+ QList<QAbstractSeries *> attachedSeriesList = axis->d_ptr->m_series;
+ QList<QAbstractAxis *> attachedAxisList = series->d_ptr->m_axes;
+
+ if (!m_seriesList.contains(series)) {
+ qWarning() << QObject::tr("Can not find series on the chart.");
+ return false;
+ }
+
+ if (axis && !m_axisList.contains(axis)) {
+ qWarning() << QObject::tr("Can not find axis on the chart.");
+ return false;
+ }
+
+ if (attachedAxisList.contains(axis)) {
+ qWarning() << QObject::tr("Axis already attached to series.");
+ return false;
+ }
+
+ if (attachedSeriesList.contains(series)) {
+ qWarning() << QObject::tr("Axis already attached to series.");
+ return false;
+ }
+
+ AbstractDomain *domain = series->d_ptr->domain();
+ AbstractDomain::DomainType type = selectDomain(attachedAxisList<<axis);
+
+ if (type == AbstractDomain::UndefinedDomain) return false;
+
+ if (domain->type() != type) {
+ AbstractDomain *old = domain;
+ domain = createDomain(type);
+ domain->setRange(old->minX(), old->maxX(), old->minY(), old->maxY());
+ // Initialize domain size to old domain size, as it won't get updated
+ // unless geometry changes.
+ domain->setSize(old->size());
+ }
+
+ if (!domain)
+ return false;
+
+ if (!domain->attachAxis(axis))
+ return false;
+
+ QList<AbstractDomain *> blockedDomains;
+ domain->blockRangeSignals(true);
+ blockedDomains << domain;
+
+ if (domain != series->d_ptr->domain()) {
+ foreach (QAbstractAxis *axis, series->d_ptr->m_axes) {
+ series->d_ptr->domain()->detachAxis(axis);
+ domain->attachAxis(axis);
+ foreach (QAbstractSeries *otherSeries, axis->d_ptr->m_series) {
+ if (otherSeries != series && otherSeries->d_ptr->domain()) {
+ if (!otherSeries->d_ptr->domain()->rangeSignalsBlocked()) {
+ otherSeries->d_ptr->domain()->blockRangeSignals(true);
+ blockedDomains << otherSeries->d_ptr->domain();
+ }
+ }
+ }
+ }
+ series->d_ptr->setDomain(domain);
+ series->d_ptr->initializeDomain();
+ }
+
+ series->d_ptr->m_axes<<axis;
+ axis->d_ptr->m_series<<series;
+
+ series->d_ptr->initializeAxes();
+ axis->d_ptr->initializeDomain(domain);
+
+ foreach (AbstractDomain *blockedDomain, blockedDomains)
+ blockedDomain->blockRangeSignals(false);
+
+ return true;
+}
+
+/*
+ * This method detaches axis to series, return true if success.
+ */
+bool ChartDataSet::detachAxis(QAbstractSeries* series,QAbstractAxis *axis)
+{
+ Q_ASSERT(series);
+ Q_ASSERT(axis);
+
+ QList<QAbstractSeries* > attachedSeriesList = axis->d_ptr->m_series;
+ QList<QAbstractAxis* > attachedAxisList = series->d_ptr->m_axes;
+ AbstractDomain* domain = series->d_ptr->domain();
+
+ if (!m_seriesList.contains(series)) {
+ qWarning() << QObject::tr("Can not find series on the chart.");
+ return false;
+ }
+
+ if (axis && !m_axisList.contains(axis)) {
+ qWarning() << QObject::tr("Can not find axis on the chart.");
+ return false;
+ }
+
+ if (!attachedAxisList.contains(axis)) {
+ qWarning() << QObject::tr("Axis not attached to series.");
+ return false;
+ }
+
+ Q_ASSERT(axis->d_ptr->m_series.contains(series));
+
+ domain->detachAxis(axis);
+ series->d_ptr->m_axes.removeAll(axis);
+ axis->d_ptr->m_series.removeAll(series);
+
+ return true;
+}
+
+void ChartDataSet::createDefaultAxes()
+{
+ if (m_seriesList.isEmpty())
+ return;
+
+ QAbstractAxis::AxisTypes typeX(0);
+ QAbstractAxis::AxisTypes typeY(0);
+
+ // Remove possibly existing axes
+ deleteAllAxes();
+
+ Q_ASSERT(m_axisList.isEmpty());
+
+ // Select the required axis x and axis y types based on the types of the current series
+ foreach(QAbstractSeries* s, m_seriesList) {
+ typeX |= s->d_ptr->defaultAxisType(Qt::Horizontal);
+ typeY |= s->d_ptr->defaultAxisType(Qt::Vertical);
+ }
+
+ // Create the axes of the types selected
+ // As long as AxisType enum balues are sequential a check to see if there are series of
+ // different types is needed. In such cases AxisTypeNoAxis is used to create separate axes
+ // for the types.
+ if (typeX != QAbstractAxis::AxisTypeNoAxis) {
+ if (typeX != m_seriesList.first()->d_ptr->defaultAxisType(Qt::Horizontal))
+ typeX = QAbstractAxis::AxisTypeNoAxis;
+ createAxes(typeX, Qt::Horizontal);
+ }
+
+ if (typeY != QAbstractAxis::AxisTypeNoAxis) {
+ if (typeY != m_seriesList.first()->d_ptr->defaultAxisType(Qt::Vertical))
+ typeY = QAbstractAxis::AxisTypeNoAxis;
+ createAxes(typeY, Qt::Vertical);
+ }
+
+}
+
+void ChartDataSet::createAxes(QAbstractAxis::AxisTypes type, Qt::Orientation orientation)
+{
+ QAbstractAxis *axis = 0;
+ //decide what axis should be created
+
+ switch (type) {
+ case QAbstractAxis::AxisTypeValue:
+ axis = new QValueAxis(this);
+ break;
+ case QAbstractAxis::AxisTypeBarCategory:
+ axis = new QBarCategoryAxis(this);
+ break;
+ case QAbstractAxis::AxisTypeCategory:
+ axis = new QCategoryAxis(this);
+ break;
+#ifndef Q_WS_QWS
+ case QAbstractAxis::AxisTypeDateTime:
+ axis = new QDateTimeAxis(this);
+ break;
+#endif
+ default:
+ axis = 0;
+ break;
+ }
+
+ if (axis) {
+ //create one axis for all
+
+ addAxis(axis,orientation==Qt::Horizontal?Qt::AlignBottom:Qt::AlignLeft);
+ qreal min = 0;
+ qreal max = 0;
+ findMinMaxForSeries(m_seriesList,orientation,min,max);
+ foreach(QAbstractSeries *s, m_seriesList) {
+ attachAxis(s,axis);
+ }
+ axis->setRange(min,max);
+ }
+ else if (type.testFlag(QAbstractAxis::AxisTypeNoAxis)) {
+ //create separate axis
+ foreach(QAbstractSeries *s, m_seriesList) {
+ QAbstractAxis *axis = s->d_ptr->createDefaultAxis(orientation);
+ if(axis) {
+ addAxis(axis,orientation==Qt::Horizontal?Qt::AlignBottom:Qt::AlignLeft);
+ attachAxis(s,axis);
+ }
+ }
+ }
+}
+
+void ChartDataSet::findMinMaxForSeries(QList<QAbstractSeries *> series,Qt::Orientations orientation, qreal &min, qreal &max)
+{
+ Q_ASSERT(!series.isEmpty());
+
+ AbstractDomain *domain = series.first()->d_ptr->domain();
+ min = (orientation == Qt::Vertical) ? domain->minY() : domain->minX();
+ max = (orientation == Qt::Vertical) ? domain->maxY() : domain->maxX();
+
+ for (int i = 1; i< series.size(); i++) {
+ AbstractDomain *domain = series[i]->d_ptr->domain();
+ min = qMin((orientation == Qt::Vertical) ? domain->minY() : domain->minX(), min);
+ max = qMax((orientation == Qt::Vertical) ? domain->maxY() : domain->maxX(), max);
+ }
+ if (min == max) {
+ min -= 0.5;
+ max += 0.5;
+ }
+}
+
+void ChartDataSet::deleteAllSeries()
+{
+ foreach (QAbstractSeries *s , m_seriesList){
+ removeSeries(s);
+ s->deleteLater();
+ }
+ Q_ASSERT(m_seriesList.count() == 0);
+}
+
+void ChartDataSet::deleteAllAxes()
+{
+ foreach (QAbstractAxis *a , m_axisList){
+ removeAxis(a);
+ a->deleteLater();
+ }
+ Q_ASSERT(m_axisList.count() == 0);
+}
+
+void ChartDataSet::zoomInDomain(const QRectF &rect)
+{
+ QList<AbstractDomain*> domains;
+ foreach(QAbstractSeries *s, m_seriesList) {
+ AbstractDomain* domain = s->d_ptr->domain();
+ s->d_ptr->m_domain->blockRangeSignals(true);
+ domains<<domain;
+ }
+
+ foreach(AbstractDomain *domain, domains)
+ domain->zoomIn(rect);
+
+ foreach(AbstractDomain *domain, domains)
+ domain->blockRangeSignals(false);
+}
+
+void ChartDataSet::zoomOutDomain(const QRectF &rect)
+{
+ QList<AbstractDomain*> domains;
+ foreach(QAbstractSeries *s, m_seriesList) {
+ AbstractDomain* domain = s->d_ptr->domain();
+ s->d_ptr->m_domain->blockRangeSignals(true);
+ domains<<domain;
+ }
+
+ foreach(AbstractDomain *domain, domains)
+ domain->zoomOut(rect);
+
+ foreach(AbstractDomain *domain, domains)
+ domain->blockRangeSignals(false);
+}
+
+void ChartDataSet::zoomResetDomain()
+{
+ QList<AbstractDomain*> domains;
+ foreach (QAbstractSeries *s, m_seriesList) {
+ AbstractDomain *domain = s->d_ptr->domain();
+ s->d_ptr->m_domain->blockRangeSignals(true);
+ domains << domain;
+ }
+
+ foreach (AbstractDomain *domain, domains)
+ domain->zoomReset();
+
+ foreach (AbstractDomain *domain, domains)
+ domain->blockRangeSignals(false);
+}
+
+bool ChartDataSet::isZoomedDomain()
+{
+ foreach (QAbstractSeries *s, m_seriesList) {
+ if (s->d_ptr->domain()->isZoomed())
+ return true;
+ }
+ return false;
+}
+
+void ChartDataSet::scrollDomain(qreal dx, qreal dy)
+{
+ QList<AbstractDomain*> domains;
+ foreach(QAbstractSeries *s, m_seriesList) {
+ AbstractDomain* domain = s->d_ptr->domain();
+ s->d_ptr->m_domain->blockRangeSignals(true);
+ domains<<domain;
+ }
+
+ foreach(AbstractDomain *domain, domains)
+ domain->move(dx, dy);
+
+ foreach(AbstractDomain *domain, domains)
+ domain->blockRangeSignals(false);
+}
+
+QPointF ChartDataSet::mapToValue(const QPointF &position, QAbstractSeries *series)
+{
+ QPointF point;
+ if (series == 0 && !m_seriesList.isEmpty())
+ series = m_seriesList.first();
+
+ if (series && series->type() == QAbstractSeries::SeriesTypePie)
+ return point;
+
+ if (series && m_seriesList.contains(series))
+ point = series->d_ptr->m_domain->calculateDomainPoint(position - m_chart->plotArea().topLeft());
+ return point;
+}
+
+QPointF ChartDataSet::mapToPosition(const QPointF &value, QAbstractSeries *series)
+{
+ QPointF point = m_chart->plotArea().topLeft();
+ if (series == 0 && !m_seriesList.isEmpty())
+ series = m_seriesList.first();
+
+ if (series && series->type() == QAbstractSeries::SeriesTypePie)
+ return QPoint(0, 0);
+
+ bool ok;
+ if (series && m_seriesList.contains(series))
+ point += series->d_ptr->m_domain->calculateGeometryPoint(value, ok);
+ return point;
+}
+
+QList<QAbstractAxis *> ChartDataSet::axes() const
+{
+ return m_axisList;
+}
+
+QList<QAbstractSeries *> ChartDataSet::series() const
+{
+ return m_seriesList;
+}
+
+AbstractDomain::DomainType ChartDataSet::selectDomain(QList<QAbstractAxis *> axes)
+{
+ enum Type {
+ Undefined = 0,
+ LogType = 0x1,
+ ValueType = 0x2
+ };
+
+ int horizontal(Undefined);
+ int vertical(Undefined);
+
+ // Assume cartesian chart type, unless chart is set
+ QChart::ChartType chartType(QChart::ChartTypeCartesian);
+ if (m_chart)
+ chartType = m_chart->chartType();
+
+ foreach (QAbstractAxis *axis, axes)
+ {
+ switch (axis->type()) {
+ case QAbstractAxis::AxisTypeLogValue:
+ if (axis->orientation() == Qt::Horizontal)
+ horizontal |= LogType;
+ if (axis->orientation() == Qt::Vertical)
+ vertical |= LogType;
+ break;
+ case QAbstractAxis::AxisTypeValue:
+ case QAbstractAxis::AxisTypeBarCategory:
+ case QAbstractAxis::AxisTypeCategory:
+ case QAbstractAxis::AxisTypeDateTime:
+ if (axis->orientation() == Qt::Horizontal)
+ horizontal |= ValueType;
+ if (axis->orientation() == Qt::Vertical)
+ vertical |= ValueType;
+ break;
+ default:
+ qWarning() << "Undefined type";
+ break;
+ }
+ }
+
+ if (vertical == Undefined)
+ vertical = ValueType;
+ if (horizontal == Undefined)
+ horizontal = ValueType;
+
+ if (vertical == ValueType && horizontal == ValueType) {
+ if (chartType == QChart::ChartTypeCartesian)
+ return AbstractDomain::XYDomain;
+ else if (chartType == QChart::ChartTypePolar)
+ return AbstractDomain::XYPolarDomain;
+ }
+
+ if (vertical == LogType && horizontal == ValueType) {
+ if (chartType == QChart::ChartTypeCartesian)
+ return AbstractDomain::XLogYDomain;
+ if (chartType == QChart::ChartTypePolar)
+ return AbstractDomain::XLogYPolarDomain;
+ }
+
+ if (vertical == ValueType && horizontal == LogType) {
+ if (chartType == QChart::ChartTypeCartesian)
+ return AbstractDomain::LogXYDomain;
+ else if (chartType == QChart::ChartTypePolar)
+ return AbstractDomain::LogXYPolarDomain;
+ }
+
+ if (vertical == LogType && horizontal == LogType) {
+ if (chartType == QChart::ChartTypeCartesian)
+ return AbstractDomain::LogXLogYDomain;
+ else if (chartType == QChart::ChartTypePolar)
+ return AbstractDomain::LogXLogYPolarDomain;
+ }
+
+ return AbstractDomain::UndefinedDomain;
+}
+
+//refactor create factory
+AbstractDomain* ChartDataSet::createDomain(AbstractDomain::DomainType type)
+{
+ switch (type)
+ {
+ case AbstractDomain::LogXLogYDomain:
+ return new LogXLogYDomain();
+ case AbstractDomain::XYDomain:
+ return new XYDomain();
+ case AbstractDomain::XLogYDomain:
+ return new XLogYDomain();
+ case AbstractDomain::LogXYDomain:
+ return new LogXYDomain();
+ case AbstractDomain::XYPolarDomain:
+ return new XYPolarDomain();
+ case AbstractDomain::XLogYPolarDomain:
+ return new XLogYPolarDomain();
+ case AbstractDomain::LogXYPolarDomain:
+ return new LogXYPolarDomain();
+ case AbstractDomain::LogXLogYPolarDomain:
+ return new LogXLogYPolarDomain();
+ default:
+ return 0;
+ }
+}
+
+#include "moc_chartdataset_p.cpp"
+
+QT_CHARTS_END_NAMESPACE