/**************************************************************************** ** ** Copyright (C) 2013 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 Commercial Charts Add-on. ** ** $QT_BEGIN_LICENSE$ ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial 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 "xlogydomain_p.h" #include "logxydomain_p.h" #include "logxlogydomain_p.h" #ifndef QT_ON_ARM #include "qdatetimeaxis.h" #endif QTCOMMERCIALCHART_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; } 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; }; QSharedPointer domain(new XYDomain()); 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 axes = series->d_ptr->m_axes; foreach(QAbstractAxis* axis, axes) { detachAxis(series,axis); } emit seriesRemoved(series); m_seriesList.removeAll(series); 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 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(series); Q_ASSERT(axis); QList attachedSeriesList = axis->d_ptr->m_series; QList 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<type()!=type){ AbstractDomain *old = domain; domain = createDomain(type); domain->setRange(old->minX(), old->maxX(), old->minY(), old->maxY()); } if(!domain) return false; if(!domain->attachAxis(axis)) return false; if(domain!=series->d_ptr->domain()){ foreach(QAbstractAxis* axis,series->d_ptr->m_axes){ series->d_ptr->domain()->detachAxis(axis); domain->attachAxis(axis); } series->d_ptr->setDomain(domain); series->d_ptr->initializeDomain(); } series->d_ptr->m_axes<d_ptr->m_series<d_ptr->initializeAxes(); axis->d_ptr->initializeDomain(domain); 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 attachedSeriesList = axis->d_ptr->m_series; QList 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 createAxes(typeX, Qt::Horizontal); 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 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 domains; foreach(QAbstractSeries *s, m_seriesList) { AbstractDomain* domain = s->d_ptr->domain(); s->d_ptr->m_domain->blockRangeSignals(true); domains<zoomIn(rect); foreach(AbstractDomain *domain, domains) domain->blockRangeSignals(false); } void ChartDataSet::zoomOutDomain(const QRectF &rect) { QList domains; foreach(QAbstractSeries *s, m_seriesList) { AbstractDomain* domain = s->d_ptr->domain(); s->d_ptr->m_domain->blockRangeSignals(true); domains<zoomOut(rect); foreach(AbstractDomain *domain, domains) domain->blockRangeSignals(false); } void ChartDataSet::scrollDomain(qreal dx, qreal dy) { QList domains; foreach(QAbstractSeries *s, m_seriesList) { AbstractDomain* domain = s->d_ptr->domain(); s->d_ptr->m_domain->blockRangeSignals(true); domains<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 ChartDataSet::axes() const { return m_axisList; } QList ChartDataSet::series() const { return m_seriesList; } AbstractDomain::DomainType ChartDataSet::selectDomain(QList axes) { enum Type { Undefined = 0, LogType = 0x1, ValueType = 0x2 }; int horizontal(Undefined); int vertical(Undefined); 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) { return AbstractDomain::XYDomain; } if(vertical==LogType && horizontal== ValueType) { return AbstractDomain::XLogYDomain; } if(vertical==ValueType && horizontal== LogType) { return AbstractDomain::LogXYDomain; } if(vertical==LogType && horizontal== LogType) { return AbstractDomain::LogXLogYDomain; } 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(); default: return 0; } } #include "moc_chartdataset_p.cpp" QTCOMMERCIALCHART_END_NAMESPACE