Revision control
++ // Copyright (C) 2017 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
++
++ #include "declarativechart_p.h"
++ #include <QtGui/QPainter>
++ #if QT_CONFIG(charts_line_chart)
++ #include "declarativelineseries_p.h"
++ #endif
++ #if QT_CONFIG(charts_area_chart)
++ #include "declarativeareaseries_p.h"
++ #endif
++ #include "declarativebarseries_p.h"
++ #if QT_CONFIG(charts_pie_chart)
++ #include "declarativepieseries_p.h"
++ #endif
++ #if QT_CONFIG(charts_spline_chart)
++ #include "declarativesplineseries_p.h"
++ #endif
++ #if QT_CONFIG(charts_boxplot_chart)
++ #include "declarativeboxplotseries_p.h"
++ #endif
++ #if QT_CONFIG(charts_candlestick_chart)
++ #include "declarativecandlestickseries_p.h"
++ #endif
++ #if QT_CONFIG(charts_scatter_chart)
++ #include "declarativescatterseries_p.h"
++ #endif
++ #include "declarativechartnode_p.h"
++ #include "declarativeabstractrendernode_p.h"
++ #include "declarativemargins_p.h"
++ #include "declarativeaxes_p.h"
++ #include <QtCharts/private/qabstractseries_p.h>
++ #include <QtCharts/private/chartdataset_p.h>
++ #include <QtCharts/private/qchart_p.h>
++ #include <QtCharts/private/chartpresenter_p.h>
++ #include <QtCharts/QBarCategoryAxis>
++ #include <QtCharts/QValueAxis>
++ #include <QtCharts/QLogValueAxis>
++ #include <QtCharts/QCategoryAxis>
++ #include <QtCharts/QPolarChart>
++ #include <QtWidgets/QGraphicsSceneMouseEvent>
++ #include <QtWidgets/QGraphicsSceneHoverEvent>
++ #include <QtWidgets/QApplication>
++ #include <QtCore/QTimer>
++ #include <QtCore/QThread>
++ #include <QtQuick/QQuickWindow>
++ #include <QtWidgets/QGraphicsLayout>
++
++ #if QT_CONFIG(charts_datetime_axis)
++ #include <QtCharts/QDateTimeAxis>
++ #endif
++
++ QT_BEGIN_NAMESPACE
++
++ /*!
++ \qmltype ChartView
++ \inqmlmodule QtCharts
++
++ \brief Manages the graphical representation of the chart's series, legends,
++ and axes.
++
++ The ChartView type displays different series types as charts.
++
++ This example shows how to create a simple line chart:
++
++ \image examples_qmlchart2.png {Line chart with a single rising line that
++ peaks and then levels off}
++ \snippet qmlchartsgallery/qml/LineSeries.qml 1
++ */
++
++ /*!
++ \qmlproperty enumeration ChartView::theme
++
++ The theme used by the chart.
++
++ A theme is a built-in collection of UI style related settings applied to all
++ the visual elements of a chart, such as colors, pens, brushes, and fonts of
++ series, as well as axes, title, and legend. The \l {Qml Oscilloscope}
++ example illustrates how to set a theme.
++
++ \note Changing the theme will overwrite all customizations previously
++ applied to the series.
++
++ The following values are supported:
++
++ \value ChartView.ChartThemeLight
++ The light theme, which is the default theme.
++ \value ChartView.ChartThemeBlueCerulean
++ The cerulean blue theme.
++ \value ChartView.ChartThemeDark
++ The dark theme.
++ \value ChartView.ChartThemeBrownSand
++ The sand brown theme.
++ \value ChartView.ChartThemeBlueNcs
++ The natural color system (NCS) blue theme.
++ \value ChartView.ChartThemeHighContrast
++ The high contrast theme.
++ \value ChartView.ChartThemeBlueIcy
++ The icy blue theme.
++ \value ChartView.ChartThemeQt
++ The Qt theme.
++ */
++
++ /*!
++ \qmlproperty enumeration ChartView::animationOptions
++
++ The animations enabled in the chart:
++
++ \value ChartView.NoAnimation
++ Animation is disabled in the chart. This is the default value.
++ \value ChartView.GridAxisAnimations
++ Grid axis animation is enabled in the chart.
++ \value ChartView.SeriesAnimations
++ Series animation is enabled in the chart.
++ \value ChartView.AllAnimations
++ All animation types are enabled in the chart.
++ */
++
++ /*!
++ \qmlproperty int ChartView::animationDuration
++ The duration of the animation for the chart.
++ */
++
++ /*!
++ \qmlproperty easing ChartView::animationEasingCurve
++ The easing curve of the animation for the chart.
++ */
++
++ /*!
++ \qmlproperty font ChartView::titleFont
++ The title font of the chart.
++
++ For more information, see \l [QML]{font}.
++ */
++
++ /*!
++ \qmlproperty string ChartView::title
++ The title is shown as a headline on top of the chart. Chart titles support
++ HTML formatting.
++
++ \sa titleColor
++ */
++
++ /*!
++ \qmlproperty color ChartView::titleColor
++ The color of the title text.
++ */
++
++ /*!
++ \qmlproperty Legend ChartView::legend
++ The legend of the chart. The legend lists all the series, pie slices, and bar
++ sets added to the chart.
++ */
++
++ /*!
++ \qmlproperty int ChartView::count
++ The number of series added to the chart.
++ */
++
++ /*!
++ \qmlproperty color ChartView::backgroundColor
++ The color of the chart's background. By default, the background color is
++ specified by the chart theme.
++
++ \sa theme
++ */
++
++ /*!
++ \qmlproperty real ChartView::backgroundRoundness
++ The diameter of the rounding circle at the corners of the chart background.
++ */
++
++ /*!
++ \qmlproperty color ChartView::plotAreaColor
++ The color of the background of the chart's plot area. By default, the plot
++ area background uses the chart's background color, which is specified by the
++ chart theme.
++
++ \sa backgroundColor, theme
++ */
++
++ /*!
++ \qmlproperty list<AbstractAxis> ChartView::axes
++ The axes of the chart.
++ */
++
++ /*!
++ \qmlproperty bool ChartView::dropShadowEnabled
++ Whether the background drop shadow effect is enabled.
++
++ If set to \c true, the background drop shadow effect is enabled. If set to
++ \c false, it is disabled.
++ */
++
++ /*!
++ \qmlproperty rect ChartView::plotArea
++ The rectangle within which the chart is drawn.
++
++ The plot area does not include the area defined by margins. By default this will resize if inside
++ a ChartView. If an explicit rectangle is set for the plot area then it will respect this, to revert
++ back to the default behavior, then setting it to \c{Qt.rect(0, 0, 0, 0)} will achieve this.
++
++ \sa margins
++ */
++
++ /*!
++ \qmlproperty Margins ChartView::margins
++ The minimum margins allowed between the edge of the chart rectangle and the
++ plot area. The margins are used for drawing the title, axes, and legend.
++ */
++
++ /*!
++ \qmlproperty bool ChartView::localizeNumbers
++ \since QtCharts 2.0
++
++ Whether numbers are localized.
++
++ When \c true, all generated numbers appearing in various series and axis
++ labels will be localized using the QLocale set with the \l locale property.
++ When \c{false}, the \e C locale is always used. Defaults to \c{false}.
++
++ \note This property does not affect DateTimeAxis labels, which always use the
++ QLocale set with the locale property.
++
++ \sa locale
++ */
++
++ /*!
++ \qmlproperty locale ChartView::locale
++ \since QtCharts 2.0
++
++ The locale used to format various chart labels.
++
++ Labels are localized only when \l localizeNumbers is \c true, except for
++ DateTimeAxis labels, which always use the QLocale set with this property.
++
++ Defaults to the application default locale at the time when the chart is
++ constructed.
++
++ \sa localizeNumbers
++ */
++
++ /*!
++ \qmlmethod AbstractSeries ChartView::series(int index)
++ Returns the series with the index \a index on the chart. Together with the
++ \l count property of the chart, this enables looping through the series of a
++ chart.
++
++ \sa count
++ */
++
++ /*!
++ \qmlmethod AbstractSeries ChartView::series(string name)
++ Returns the first series in the chart with the name \a name. If there is no
++ series with that name, returns null.
++ */
++
++ /*!
++ \qmlmethod AbstractSeries ChartView::createSeries(enumeration type, string name, AbstractAxis axisX, AbstractAxis axisY)
++ Adds a series of the type \a type to the chart with the name \a name
++ and, optionally, the axes \a axisX and \a axisY. For example:
++ \code
++ // lineSeries is a LineSeries object that has already been added to the ChartView; re-use its axes
++ var myAxisX = chartView.axisX(lineSeries);
++ var myAxisY = chartView.axisY(lineSeries);
++ var scatter = chartView.createSeries(ChartView.SeriesTypeScatter, "scatter series", myAxisX, myAxisY);
++ \endcode
++
++ The following enumeration values can be used as values of \a type:
++
++ \value ChartView.SeriesTypeLine
++ A line series.
++ \value ChartView.SeriesTypeArea
++ An area series.
++ \value ChartView.SeriesTypeBar
++ A bar series.
++ \value ChartView.SeriesTypeStackedBar
++ A stacked bar series.
++ \value ChartView.SeriesTypePercentBar
++ A percent bar series.
++ \value ChartView.SeriesTypeBoxPlot
++ A box plot series.
++ \value ChartView.SeriesTypeCandlestick
++ A candlestick series.
++ \value ChartView.SeriesTypePie
++ A pie series.
++ \value ChartView.SeriesTypeScatter
++ A scatter series.
++ \value ChartView.SeriesTypeSpline
++ A spline series.
++ \value ChartView.SeriesTypeHorizontalBar
++ A horizontal bar series.
++ \value ChartView.SeriesTypeHorizontalStackedBar
++ A horizontal stacked bar series.
++ \value ChartView.SeriesTypeHorizontalPercentBar
++ A horizontal percent bar series.
++ */
++
++ /*!
++ \qmlmethod void ChartView::removeSeries(AbstractSeries series)
++ Removes the series \a series from the chart and permanently deletes the series
++ object.
++ */
++
++ /*!
++ \qmlmethod void ChartView::removeAllSeries()
++ Removes all series from the chart and permanently deletes all the series
++ objects.
++ */
++
++ /*!
++ \qmlmethod Axis ChartView::axisX(AbstractSeries series)
++ The x-axis of the \a series.
++ */
++
++ /*!
++ \qmlmethod void ChartView::setAxisX(AbstractAxis axis, AbstractSeries series)
++ Sets the x-axis of the \a series to \a axis.
++ */
++
++ /*!
++ \qmlmethod Axis ChartView::axisY(AbstractSeries series)
++ The y-axis of the \a series.
++ */
++
++ /*!
++ \qmlmethod void ChartView::setAxisY(AbstractAxis axis, AbstractSeries series)
++ Sets the y-axis of the \a series to \a axis.
++ */
++
++ /*!
++ \qmlmethod void ChartView::zoom(real factor)
++ Zooms into the chart by the custom factor \a factor.
++
++ A factor over 1.0 zooms into the view and a factor between 0.0 and 1.0
++ zooms out of it.
++ */
++
++ /*!
++ \qmlmethod void ChartView::zoomIn()
++ Zooms into the view by a factor of two.
++ */
++
++ /*!
++ \qmlmethod void ChartView::zoomIn(rect rectangle)
++ Zooms into the view to a maximum level at which the rectangle \a rectangle is
++ still fully visible.
++ \note This is not supported for polar charts.
++ */
++
++ /*!
++ \qmlmethod void ChartView::zoomOut()
++ Zooms out of the view by a factor of two.
++ \note This will do nothing if the result would contain an invalid logarithmic axis range.
++ */
++
++ /*!
++ \qmlmethod void ChartView::zoomReset()
++ Resets the series domains to what they were before any zoom method was called.
++
++ \note This will also reset scrolling and explicit axis range settings
++ specified between the first zoom operation and calling this method. If no zoom
++ operation has been performed, this method does nothing.
++ */
++
++ /*!
++ \qmlmethod bool ChartView::isZoomed()
++
++ Returns \c true if any series has a zoomed domain.
++ */
++
++ /*!
++ \qmlmethod void ChartView::scrollLeft(real pixels)
++ Scrolls to left by the number of pixels specified by \a pixels. This is a
++ convenience method suitable for key navigation, for example.
++ */
++
++ /*!
++ \qmlmethod void ChartView::scrollRight(real pixels)
++ Scrolls to right by by the number of pixels specified by \a pixels. This is a
++ convenience method suitable for key navigation, for example.
++ */
++
++ /*!
++ \qmlmethod void ChartView::scrollUp(real pixels)
++ Scrolls up by the number of pixels specified by \a pixels. This is a
++ convenience method suitable for key navigation, for example.
++ */
++
++ /*!
++ \qmlmethod void ChartView::scrollDown(real pixels)
++ Scrolls down by the number of pixels specified by \a pixels. This is a
++ convenience method suitable for key navigation, for example.
++ */
++
++ /*!
++ \qmlmethod point ChartView::mapToValue(point position, AbstractSeries series)
++ Returns the value in the series \a series located at the position \a position
++ in the chart.
++ */
++
++ /*!
++ \qmlmethod point ChartView::mapToPosition(point value, AbstractSeries series)
++ Returns the position in the chart of the value \a value in the series
++ \a series.
++ */
++
++ /*!
++ \qmlsignal ChartView::seriesAdded(AbstractSeries series)
++ This signal is emitted when the series \a series is added to the chart.
++ */
++
++ /*!
++ \qmlsignal ChartView::seriesRemoved(AbstractSeries series)
++ This signal is emitted when the series \a series is removed from the chart.
++ The series object becomes invalid when the signal handler completes.
++ */
++
++ DeclarativeChart::DeclarativeChart(QQuickItem *parent)
++ : QQuickItem(parent)
++ {
++ initChart(QChart::ChartTypeCartesian);
++ }
++
++ DeclarativeChart::DeclarativeChart(QChart::ChartType type, QQuickItem *parent)
++ : QQuickItem(parent)
++ {
++ initChart(type);
++ }
++
++ #if QT_CONFIG(charts_bar_chart)
++ // QTBUG-71013
++ // The symbol resides in qbarmodelmapper.cpp#548 in the C++ module.
++ // Here, it gets imported and reset to the DeclarativeBarSet allocator
++ #if defined(Q_OS_WIN) && !defined(QT_STATIC)
++ Q_CHARTS_EXPORT
++ #else
++ extern
++ #endif
++ QBarSet *(*qt_allocate_bar_set)(const QString &label);
++
++ QBarSet *qt_allocate_bar_set_qml(const QString &label)
++ {
++ auto bar = new DeclarativeBarSet();
++ bar->setLabel(label);
++ return bar;
++ }
++ #endif
++
++ void DeclarativeChart::initChart(QChart::ChartType type)
++ {
++ m_sceneImage = 0;
++ m_sceneImageDirty = false;
++ m_sceneImageNeedsClear = false;
++ m_guiThreadId = QThread::currentThreadId();
++ m_paintThreadId = 0;
++ m_updatePending = false;
++
++ setFlag(ItemHasContents, true);
++
++ #if QT_CONFIG(charts_bar_chart)
++ // Reset allocator for QBarSet to create
++ // Declarative BarSets by default
++ qt_allocate_bar_set = &qt_allocate_bar_set_qml;
++ #endif
++
++ if (type == QChart::ChartTypePolar)
++ m_chart = new QPolarChart();
++ else
++ m_chart = new QChart();
++
++ m_chart->d_ptr->m_presenter->glSetUseWidget(false);
++ m_glXYDataManager = m_chart->d_ptr->m_dataset->glXYSeriesDataManager();
++
++ m_scene = new QGraphicsScene(this);
++ m_scene->addItem(m_chart);
++
++ setAntialiasing(QQuickItem::antialiasing());
++ connect(m_scene, &QGraphicsScene::changed, this, &DeclarativeChart::sceneChanged);
++ connect(this, &DeclarativeChart::needRender, this, &DeclarativeChart::renderScene,
++ Qt::QueuedConnection);
++ connect(this, SIGNAL(antialiasingChanged(bool)), this, SLOT(handleAntialiasingChanged(bool)));
++ connect(this, &DeclarativeChart::pendingRenderNodeMouseEventResponses,
++ this, &DeclarativeChart::handlePendingRenderNodeMouseEventResponses,
++ Qt::QueuedConnection);
++
++ setAcceptedMouseButtons(Qt::AllButtons);
++ setAcceptHoverEvents(true);
++
++ m_margins = new DeclarativeMargins(this);
++ m_margins->setTop(m_chart->margins().top());
++ m_margins->setLeft(m_chart->margins().left());
++ m_margins->setRight(m_chart->margins().right());
++ m_margins->setBottom(m_chart->margins().bottom());
++ connect(m_margins, SIGNAL(topChanged(int,int,int,int)),
++ this, SLOT(changeMargins(int,int,int,int)));
++ connect(m_margins, SIGNAL(bottomChanged(int,int,int,int)),
++ this, SLOT(changeMargins(int,int,int,int)));
++ connect(m_margins, SIGNAL(leftChanged(int,int,int,int)),
++ this, SLOT(changeMargins(int,int,int,int)));
++ connect(m_margins, SIGNAL(rightChanged(int,int,int,int)),
++ this, SLOT(changeMargins(int,int,int,int)));
++ connect(m_chart->d_ptr->m_dataset, SIGNAL(seriesAdded(QAbstractSeries*)), this, SLOT(handleSeriesAdded(QAbstractSeries*)));
++ connect(m_chart->d_ptr->m_dataset, SIGNAL(seriesRemoved(QAbstractSeries*)), this, SIGNAL(seriesRemoved(QAbstractSeries*)));
++ connect(m_chart, SIGNAL(plotAreaChanged(QRectF)), this, SIGNAL(plotAreaChanged(QRectF)));
++ }
++
++ void DeclarativeChart::handleSeriesAdded(QAbstractSeries *series)
++ {
++ emit seriesAdded(series);
++ }
++
++ void DeclarativeChart::handlePendingRenderNodeMouseEventResponses()
++ {
++ const int count = m_pendingRenderNodeMouseEventResponses.size();
++ if (count) {
++ QXYSeries *lastSeries = nullptr; // Small optimization; events are likely for same series
++ QList<QAbstractSeries *> seriesList = m_chart->series();
++ for (int i = 0; i < count; i++) {
++ const MouseEventResponse &response = m_pendingRenderNodeMouseEventResponses.at(i);
++ QXYSeries *series = nullptr;
++ if (lastSeries == response.series) {
++ series = lastSeries;
++ } else {
++ for (int j = 0; j < seriesList.size(); j++) {
++ QAbstractSeries *chartSeries = seriesList.at(j);
++ if (response.series == chartSeries) {
++ series = qobject_cast<QXYSeries *>(chartSeries);
++ break;
++ }
++ }
++ }
++ if (series) {
++ lastSeries = series;
++ QSizeF normalizedPlotSize(
++ m_chart->plotArea().size().width()
++ / m_adjustedPlotArea.size().width(),
++ m_chart->plotArea().size().height()
++ / m_adjustedPlotArea.size().height());
++
++ QPoint adjustedPoint(
++ response.point.x() * normalizedPlotSize.width(),
++ response.point.y() * normalizedPlotSize.height());
++
++ QPointF domPoint = series->d_ptr->domain()->calculateDomainPoint(adjustedPoint);
++ switch (response.type) {
++ case MouseEventResponse::Pressed:
++ emit series->pressed(domPoint);
++ break;
++ case MouseEventResponse::Released:
++ emit series->released(domPoint);
++ break;
++ case MouseEventResponse::Clicked:
++ emit series->clicked(domPoint);
++ break;
++ case MouseEventResponse::DoubleClicked:
++ emit series->doubleClicked(domPoint);
++ break;
++ case MouseEventResponse::HoverEnter:
++ emit series->hovered(domPoint, true);
++ break;
++ case MouseEventResponse::HoverLeave:
++ emit series->hovered(domPoint, false);
++ break;
++ default:
++ // No action
++ break;
++ }
++ }
++ }
++ m_pendingRenderNodeMouseEventResponses.clear();
++ }
++ }
++
++ void DeclarativeChart::changeMargins(int top, int bottom, int left, int right)
++ {
++ m_chart->setMargins(QMargins(left, top, right, bottom));
++ emit marginsChanged();
++ }
++
++ DeclarativeChart::~DeclarativeChart()
++ {
++ delete m_chart;
++ delete m_sceneImage;
++ }
++
++ void DeclarativeChart::componentComplete()
++ {
++ const auto childlist = children();
++ for (QObject *child : childlist) {
++ if (qobject_cast<QAbstractSeries *>(child)) {
++ // Add series to the chart
++ QAbstractSeries *series = qobject_cast<QAbstractSeries *>(child);
++ m_chart->addSeries(series);
++
++ // Connect to axis changed signals (unless this is a pie series)
++ #if QT_CONFIG(charts_pie_chart)
++ if (!qobject_cast<DeclarativePieSeries *>(series))
++ #endif
++ {
++ connect(series, SIGNAL(axisXChanged(QAbstractAxis*)), this, SLOT(handleAxisXSet(QAbstractAxis*)));
++ connect(series, SIGNAL(axisXTopChanged(QAbstractAxis*)), this, SLOT(handleAxisXTopSet(QAbstractAxis*)));
++ connect(series, SIGNAL(axisYChanged(QAbstractAxis*)), this, SLOT(handleAxisYSet(QAbstractAxis*)));
++ connect(series, SIGNAL(axisYRightChanged(QAbstractAxis*)), this, SLOT(handleAxisYRightSet(QAbstractAxis*)));
++ }
++
++ initializeAxes(series);
++ }
++ }
++
++ QQuickItem::componentComplete();
++ }
++
++ void DeclarativeChart::seriesAxisAttachHelper(QAbstractSeries *series, QAbstractAxis *axis,
++ Qt::Orientations orientation,
++ Qt::Alignment alignment)
++ {
++ if (!series->attachedAxes().contains(axis)) {
++ // Remove & delete old axes that are not attached to any other series
++ // Detach old axis from series so that if it is shared with other series
++ // It can be deleted.
++ const auto axes = m_chart->axes(orientation, series);
++ for (QAbstractAxis *oldAxis : axes) {
++ bool otherAttachments = false;
++ if (oldAxis != axis) {
++ series->detachAxis(oldAxis);
++ const auto allSeries = m_chart->series();
++ for (QAbstractSeries *oldSeries : allSeries) {
++ if (oldSeries != series && oldSeries->attachedAxes().contains(oldAxis)) {
++ otherAttachments = true;
++ break;
++ }
++ }
++ if (!otherAttachments) {
++ m_chart->removeAxis(oldAxis);
++ delete oldAxis;
++ }
++ }
++ }
++ if (!m_chart->axes(orientation).contains(axis))
++ m_chart->addAxis(axis, alignment);
++
++ series->attachAxis(axis);
++ }
++ }
++
++ void DeclarativeChart::handleAxisXSet(QAbstractAxis *axis)
++ {
++ QAbstractSeries *s = qobject_cast<QAbstractSeries *>(sender());
++ if (axis && s) {
++ seriesAxisAttachHelper(s, axis, Qt::Horizontal, Qt::AlignBottom);
++ } else {
++ qWarning() << "Trying to set axisX to null.";
++ }
++ }
++
++ void DeclarativeChart::handleAxisXTopSet(QAbstractAxis *axis)
++ {
++ QAbstractSeries *s = qobject_cast<QAbstractSeries *>(sender());
++ if (axis && s) {
++ seriesAxisAttachHelper(s, axis, Qt::Horizontal, Qt::AlignTop);
++ } else {
++ qWarning() << "Trying to set axisXTop to null.";
++ }
++ }
++
++ void DeclarativeChart::handleAxisYSet(QAbstractAxis *axis)
++ {
++ QAbstractSeries *s = qobject_cast<QAbstractSeries *>(sender());
++ if (axis && s) {
++ seriesAxisAttachHelper(s, axis, Qt::Vertical, Qt::AlignLeft);
++ } else {
++ qWarning() << "Trying to set axisY to null.";
++ }
++ }
++
++ void DeclarativeChart::handleAxisYRightSet(QAbstractAxis *axis)
++ {
++ QAbstractSeries *s = qobject_cast<QAbstractSeries *>(sender());
++ if (axis && s) {
++ seriesAxisAttachHelper(s, axis, Qt::Vertical, Qt::AlignRight);
++ } else {
++ qWarning() << "Trying to set axisYRight to null.";
++ }
++ }
++
++ void DeclarativeChart::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
++ {
++ if (newGeometry.isValid()) {
++ if (newGeometry.width() > 0 && newGeometry.height() > 0) {
++ m_chart->resize(newGeometry.width(), newGeometry.height());
++ }
++ }
++ QQuickItem::geometryChange(newGeometry, oldGeometry);
++ }
++
++ QSGNode *DeclarativeChart::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
++ {
++ DeclarativeChartNode *node = static_cast<DeclarativeChartNode *>(oldNode);
++
++ if (!node) {
++ node = new DeclarativeChartNode(window());
++ // Ensure that chart is rendered whenever node is recreated
++ if (m_sceneImage)
++ m_sceneImageDirty = true;
++ }
++
++ const QRectF &bRect = boundingRect();
++ // Update renderNode data
++ if (node->renderNode()) {
++ if (m_glXYDataManager->dataMap().size() || m_glXYDataManager->mapDirty()) {
++ // Convert plotArea to QRect and back to QRectF to get rid of sub-pixel widths/heights
++ // which can cause unwanted partial antialising of the graph.
++ const QRectF plotArea = QRectF(m_chart->plotArea().toRect());
++ const QSizeF &chartAreaSize = m_chart->size();
++
++ // We can't use chart's plot area directly, as chart enforces a minimum size
++ // internally, so that axes and labels always fit the chart area.
++ const qreal normalizedX = plotArea.x() / chartAreaSize.width();
++ const qreal normalizedY = plotArea.y() / chartAreaSize.height();
++ const qreal normalizedWidth = plotArea.width() / chartAreaSize.width();
++ const qreal normalizedHeight = plotArea.height() / chartAreaSize.height();
++
++ m_adjustedPlotArea = QRectF(normalizedX * bRect.width(),
++ normalizedY * bRect.height(),
++ normalizedWidth * bRect.width(),
++ normalizedHeight * bRect.height());
++
++ const QSize &adjustedPlotSize = m_adjustedPlotArea.size().toSize();
++ if (adjustedPlotSize != node->renderNode()->textureSize())
++ node->renderNode()->setTextureSize(adjustedPlotSize);
++
++ node->renderNode()->setRect(m_adjustedPlotArea);
++ node->renderNode()->setSeriesData(m_glXYDataManager->mapDirty(),
++ m_glXYDataManager->dataMap());
++ node->renderNode()->setAntialiasing(antialiasing());
++
++ // Clear dirty flags from original xy data
++ m_glXYDataManager->clearAllDirty();
++ }
++
++ node->renderNode()->takeMouseEventResponses(m_pendingRenderNodeMouseEventResponses);
++ if (m_pendingRenderNodeMouseEventResponses.size())
++ emit pendingRenderNodeMouseEventResponses();
++ if (m_pendingRenderNodeMouseEvents.size()) {
++ node->renderNode()->addMouseEvents(m_pendingRenderNodeMouseEvents);
++ // Queue another update to receive responses
++ update();
++ }
++ }
++
++ m_pendingRenderNodeMouseEvents.clear();
++
++ // Copy chart (if dirty) to chart node
++ if (m_sceneImageDirty) {
++ node->createTextureFromImage(*m_sceneImage);
++ m_sceneImageDirty = false;
++ }
++
++ node->setRect(bRect);
++
++ return node;
++ }
++
++ void DeclarativeChart::sceneChanged(const QList<QRectF> ®ion)
++ {
++ const int count = region.size();
++ const qreal limitSize = 0.01;
++ if (count && !m_updatePending) {
++ qreal totalSize = 0.0;
++ for (int i = 0; i < count; i++) {
++ const QRectF ® = region.at(i);
++ totalSize += (reg.height() * reg.width());
++ if (totalSize >= limitSize)
++ break;
++ }
++ // Ignore region updates that change less than small fraction of a pixel, as there is
++ // little point regenerating the image in these cases. These are typically cases
++ // where OpenGL series are drawn to otherwise static chart.
++ if (totalSize >= limitSize) {
++ m_updatePending = true;
++ // Do async render to avoid some unnecessary renders.
++ emit needRender();
++ } else {
++ // We do want to call update to trigger possible gl series updates.
++ update();
++ }
++ }
++ }
++
++ void DeclarativeChart::renderScene()
++ {
++ m_updatePending = false;
++ m_sceneImageDirty = true;
++ QSize chartSize = m_chart->size().toSize();
++ if (!m_sceneImage || chartSize != m_sceneImage->size()) {
++ delete m_sceneImage;
++ qreal dpr = window() ? window()->devicePixelRatio() : 1.0;
++ m_sceneImage = new QImage(chartSize * dpr, QImage::Format_ARGB32);
++ m_sceneImage->setDevicePixelRatio(dpr);
++ m_sceneImageNeedsClear = true;
++ }
++
++ if (m_sceneImageNeedsClear) {
++ m_sceneImage->fill(Qt::transparent);
++ // Don't clear the flag if chart background has any transparent element to it
++ if (m_chart->backgroundBrush().color().alpha() == 0xff && !m_chart->isDropShadowEnabled())
++ m_sceneImageNeedsClear = false;
++ }
++ QPainter painter(m_sceneImage);
++ if (antialiasing()) {
++ painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing
++ | QPainter::SmoothPixmapTransform);
++ }
++ QRect renderRect(QPoint(0, 0), chartSize);
++ m_scene->render(&painter, renderRect, renderRect);
++ update();
++ }
++
++ void DeclarativeChart::mousePressEvent(QMouseEvent *event)
++ {
++ m_mousePressScenePoint = event->position();
++ m_mousePressScreenPoint = event->globalPosition().toPoint();
++ m_lastMouseMoveScenePoint = m_mousePressScenePoint;
++ m_lastMouseMoveScreenPoint = m_mousePressScreenPoint;
++ m_mousePressButton = event->button();
++ m_mousePressButtons = event->buttons();
++
++ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress);
++ mouseEvent.setWidget(0);
++ mouseEvent.setButtonDownScenePos(m_mousePressButton, m_mousePressScenePoint);
++ mouseEvent.setButtonDownScreenPos(m_mousePressButton, m_mousePressScreenPoint);
++ mouseEvent.setScenePos(m_mousePressScenePoint);
++ mouseEvent.setScreenPos(m_mousePressScreenPoint);
++ mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint);
++ mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint);
++ mouseEvent.setButtons(m_mousePressButtons);
++ mouseEvent.setButton(m_mousePressButton);
++ mouseEvent.setModifiers(event->modifiers());
++ mouseEvent.setAccepted(false);
++
++ QApplication::sendEvent(m_scene, &mouseEvent);
++
++ queueRendererMouseEvent(event);
++ }
++
++ void DeclarativeChart::mouseReleaseEvent(QMouseEvent *event)
++ {
++ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease);
++ mouseEvent.setWidget(0);
++ mouseEvent.setButtonDownScenePos(m_mousePressButton, m_mousePressScenePoint);
++ mouseEvent.setButtonDownScreenPos(m_mousePressButton, m_mousePressScreenPoint);
++ mouseEvent.setScenePos(event->position());
++ mouseEvent.setScreenPos(event->globalPosition().toPoint());
++ mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint);
++ mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint);
++ mouseEvent.setButtons(event->buttons());
++ mouseEvent.setButton(event->button());
++ mouseEvent.setModifiers(event->modifiers());
++ mouseEvent.setAccepted(false);
++
++ QApplication::sendEvent(m_scene, &mouseEvent);
++
++ m_mousePressButtons = event->buttons();
++ m_mousePressButton = Qt::NoButton;
++
++ queueRendererMouseEvent(event);
++ }
++
++ void DeclarativeChart::hoverMoveEvent(QHoverEvent *event)
++ {
++ QPointF previousLastScenePoint = m_lastMouseMoveScenePoint;
++
++ // Convert hover move to mouse move, since we don't seem to get actual mouse move events.
++ // QGraphicsScene generates hover events from mouse move events, so we don't need
++ // to pass hover events there.
++ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
++ mouseEvent.setWidget(0);
++ mouseEvent.setButtonDownScenePos(m_mousePressButton, m_mousePressScenePoint);
++ mouseEvent.setButtonDownScreenPos(m_mousePressButton, m_mousePressScreenPoint);
++ mouseEvent.setScenePos(event->position());
++ // Hover events do not have global pos in them, and the screen position doesn't seem to
++ // matter anyway in this use case, so just pass event pos instead of trying to
++ // calculate the real screen position.
++ mouseEvent.setScreenPos(event->position().toPoint());
++ mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint);
++ mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint);
++ mouseEvent.setButtons(m_mousePressButtons);
++ mouseEvent.setButton(m_mousePressButton);
++ mouseEvent.setModifiers(event->modifiers());
++ m_lastMouseMoveScenePoint = mouseEvent.scenePos();
++ m_lastMouseMoveScreenPoint = mouseEvent.screenPos();
++ mouseEvent.setAccepted(false);
++
++ QApplication::sendEvent(m_scene, &mouseEvent);
++
++ // Update triggers another hover event, so let's not handle successive hovers at same
++ // position to avoid infinite loop.
++ if (m_glXYDataManager->dataMap().size() && previousLastScenePoint != m_lastMouseMoveScenePoint) {
++ QMouseEvent *newEvent = new QMouseEvent(QEvent::MouseMove,
++ event->position() - m_adjustedPlotArea.topLeft(),
++ event->globalPosition() - m_adjustedPlotArea.topLeft(),
++ m_mousePressButton,
++ m_mousePressButtons,
++ event->modifiers());
++ m_pendingRenderNodeMouseEvents.append(newEvent);
++ update();
++ }
++ }
++
++ void DeclarativeChart::mouseDoubleClickEvent(QMouseEvent *event)
++ {
++ m_mousePressScenePoint = event->position();
++ m_mousePressScreenPoint = event->globalPosition().toPoint();
++ m_lastMouseMoveScenePoint = m_mousePressScenePoint;
++ m_lastMouseMoveScreenPoint = m_mousePressScreenPoint;
++ m_mousePressButton = event->button();
++ m_mousePressButtons = event->buttons();
++
++ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseDoubleClick);
++ mouseEvent.setWidget(0);
++ mouseEvent.setButtonDownScenePos(m_mousePressButton, m_mousePressScenePoint);
++ mouseEvent.setButtonDownScreenPos(m_mousePressButton, m_mousePressScreenPoint);
++ mouseEvent.setScenePos(m_mousePressScenePoint);
++ mouseEvent.setScreenPos(m_mousePressScreenPoint);
++ mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint);
++ mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint);
++ mouseEvent.setButtons(m_mousePressButtons);
++ mouseEvent.setButton(m_mousePressButton);
++ mouseEvent.setModifiers(event->modifiers());
++ mouseEvent.setAccepted(false);
++
++ QApplication::sendEvent(m_scene, &mouseEvent);
++
++ queueRendererMouseEvent(event);
++ }
++
++ void DeclarativeChart::handleAntialiasingChanged(bool enable)
++ {
++ setAntialiasing(enable);
++ emit needRender();
++ }
++
++ void DeclarativeChart::setTheme(DeclarativeChart::Theme theme)
++ {
++ QChart::ChartTheme chartTheme = (QChart::ChartTheme) theme;
++ if (chartTheme != m_chart->theme())
++ m_chart->setTheme(chartTheme);
++ }
++
++ DeclarativeChart::Theme DeclarativeChart::theme()
++ {
++ return (DeclarativeChart::Theme) m_chart->theme();
++ }
++
++ void DeclarativeChart::setAnimationOptions(DeclarativeChart::Animation animations)
++ {
++ QChart::AnimationOption animationOptions = (QChart::AnimationOption) animations;
++ if (animationOptions != m_chart->animationOptions())
++ m_chart->setAnimationOptions(animationOptions);
++ }
++
++ DeclarativeChart::Animation DeclarativeChart::animationOptions()
++ {
++ if (m_chart->animationOptions().testFlag(QChart::AllAnimations))
++ return DeclarativeChart::AllAnimations;
++ else if (m_chart->animationOptions().testFlag(QChart::GridAxisAnimations))
++ return DeclarativeChart::GridAxisAnimations;
++ else if (m_chart->animationOptions().testFlag(QChart::SeriesAnimations))
++ return DeclarativeChart::SeriesAnimations;
++ else
++ return DeclarativeChart::NoAnimation;
++ }
++
++ void DeclarativeChart::setAnimationDuration(int msecs)
++ {
++ if (msecs != m_chart->animationDuration()) {
++ m_chart->setAnimationDuration(msecs);
++ emit animationDurationChanged(msecs);
++ }
++ }
++
++ int DeclarativeChart::animationDuration() const
++ {
++ return m_chart->animationDuration();
++ }
++
++ void DeclarativeChart::setAnimationEasingCurve(const QEasingCurve &curve)
++ {
++ if (curve != m_chart->animationEasingCurve()) {
++ m_chart->setAnimationEasingCurve(curve);
++ emit animationEasingCurveChanged(curve);
++ }
++ }
++
++ QEasingCurve DeclarativeChart::animationEasingCurve() const
++ {
++ return m_chart->animationEasingCurve();
++ }
++
++ void DeclarativeChart::setTitle(QString title)
++ {
++ if (title != m_chart->title())
++ m_chart->setTitle(title);
++ }
++ QString DeclarativeChart::title()
++ {
++ return m_chart->title();
++ }
++
++ QAbstractAxis *DeclarativeChart::axisX(QAbstractSeries *series)
++ {
++ QList<QAbstractAxis *> axes = m_chart->axes(Qt::Horizontal, series);
++ if (axes.size())
++ return axes[0];
++ return 0;
++ }
++
++ QAbstractAxis *DeclarativeChart::axisY(QAbstractSeries *series)
++ {
++ QList<QAbstractAxis *> axes = m_chart->axes(Qt::Vertical, series);
++ if (axes.size())
++ return axes[0];
++ return 0;
++ }
++
++ QLegend *DeclarativeChart::legend()
++ {
++ return m_chart->legend();
++ }
++
++ void DeclarativeChart::setTitleColor(QColor color)
++ {
++ QBrush b = m_chart->titleBrush();
++ if (color != b.color()) {
++ b.setColor(color);
++ m_chart->setTitleBrush(b);
++ emit titleColorChanged(color);
++ }
++ }
++
++ QFont DeclarativeChart::titleFont() const
++ {
++ return m_chart->titleFont();
++ }
++
++ void DeclarativeChart::setTitleFont(const QFont &font)
++ {
++ m_chart->setTitleFont(font);
++ }
++
++ QColor DeclarativeChart::titleColor()
++ {
++ return m_chart->titleBrush().color();
++ }
++
++ void DeclarativeChart::setBackgroundColor(QColor color)
++ {
++ QBrush b = m_chart->backgroundBrush();
++ if (b.style() != Qt::SolidPattern || color != b.color()) {
++ if (color.alpha() < 0xff)
++ m_sceneImageNeedsClear = true;
++ b.setStyle(Qt::SolidPattern);
++ b.setColor(color);
++ m_chart->setBackgroundBrush(b);
++ emit backgroundColorChanged();
++ }
++ }
++
++ QColor DeclarativeChart::backgroundColor()
++ {
++ return m_chart->backgroundBrush().color();
++ }
++
++ void DeclarativeChart::setPlotAreaColor(QColor color)
++ {
++ QBrush b = m_chart->plotAreaBackgroundBrush();
++ if (b.style() != Qt::SolidPattern || color != b.color()) {
++ b.setStyle(Qt::SolidPattern);
++ b.setColor(color);
++ m_chart->setPlotAreaBackgroundBrush(b);
++ m_chart->setPlotAreaBackgroundVisible(true);
++ emit plotAreaColorChanged();
++ }
++ }
++
++ QColor DeclarativeChart::plotAreaColor()
++ {
++ return m_chart->plotAreaBackgroundBrush().color();
++ }
++
++ void DeclarativeChart::setLocalizeNumbers(bool localize)
++ {
++ if (m_chart->localizeNumbers() != localize) {
++ m_chart->setLocalizeNumbers(localize);
++ emit localizeNumbersChanged();
++ }
++ }
++
++ bool DeclarativeChart::localizeNumbers() const
++ {
++ return m_chart->localizeNumbers();
++ }
++
++ void DeclarativeChart::setLocale(const QLocale &locale)
++ {
++ if (m_chart->locale() != locale) {
++ m_chart->setLocale(locale);
++ emit localeChanged();
++ }
++ }
++
++ QLocale DeclarativeChart::locale() const
++ {
++ return m_chart->locale();
++ }
++
++ int DeclarativeChart::count()
++ {
++ return m_chart->series().size();
++ }
++
++ void DeclarativeChart::setDropShadowEnabled(bool enabled)
++ {
++ if (enabled != m_chart->isDropShadowEnabled()) {
++ m_sceneImageNeedsClear = true;
++ m_chart->setDropShadowEnabled(enabled);
++ dropShadowEnabledChanged(enabled);
++ }
++ }
++
++ bool DeclarativeChart::dropShadowEnabled()
++ {
++ return m_chart->isDropShadowEnabled();
++ }
++
++ qreal DeclarativeChart::backgroundRoundness() const
++ {
++ return m_chart->backgroundRoundness();
++ }
++
++ void DeclarativeChart::setBackgroundRoundness(qreal diameter)
++ {
++ if (m_chart->backgroundRoundness() != diameter) {
++ m_sceneImageNeedsClear = true;
++ m_chart->setBackgroundRoundness(diameter);
++ emit backgroundRoundnessChanged(diameter);
++ }
++ }
++
++ void DeclarativeChart::zoom(qreal factor)
++ {
++ m_chart->zoom(factor);
++ }
++
++ void DeclarativeChart::zoomIn()
++ {
++ m_chart->zoomIn();
++ }
++
++ void DeclarativeChart::zoomIn(const QRectF &rectangle)
++ {
++ m_chart->zoomIn(rectangle);
++ }
++
++ void DeclarativeChart::zoomOut()
++ {
++ m_chart->zoomOut();
++ }
++
++ void DeclarativeChart::zoomReset()
++ {
++ m_chart->zoomReset();
++ }
++
++ bool DeclarativeChart::isZoomed()
++ {
++ return m_chart->isZoomed();
++ }
++
++ void DeclarativeChart::scrollLeft(qreal pixels)
++ {
++ m_chart->scroll(-pixels, 0);
++ }
++
++ void DeclarativeChart::scrollRight(qreal pixels)
++ {
++ m_chart->scroll(pixels, 0);
++ }
++
++ void DeclarativeChart::scrollUp(qreal pixels)
++ {
++ m_chart->scroll(0, pixels);
++ }
++
++ void DeclarativeChart::scrollDown(qreal pixels)
++ {
++ m_chart->scroll(0, -pixels);
++ }
++
++ QQmlListProperty<QAbstractAxis> DeclarativeChart::axes()
++ {
++ return QQmlListProperty<QAbstractAxis>(this, 0,
++ &DeclarativeChart::axesAppendFunc,
++ &DeclarativeChart::axesCountFunc,
++ &DeclarativeChart::axesAtFunc,
++ &DeclarativeChart::axesClearFunc);
++ }
++
++ void DeclarativeChart::axesAppendFunc(QQmlListProperty<QAbstractAxis> *list, QAbstractAxis *element)
++ {
++ // Empty implementation
++ Q_UNUSED(list);
++ Q_UNUSED(element);
++ }
++
++ qsizetype DeclarativeChart::axesCountFunc(QQmlListProperty<QAbstractAxis> *list)
++ {
++ if (qobject_cast<DeclarativeChart *>(list->object)) {
++ DeclarativeChart *chart = qobject_cast<DeclarativeChart *>(list->object);
++ return chart->m_chart->axes(Qt::Horizontal | Qt::Vertical).size();
++ }
++ return 0;
++ }
++
++ QAbstractAxis *DeclarativeChart::axesAtFunc(QQmlListProperty<QAbstractAxis> *list, qsizetype index)
++ {
++ if (qobject_cast<DeclarativeChart *>(list->object)) {
++ DeclarativeChart *chart = qobject_cast<DeclarativeChart *>(list->object);
++ QList<QAbstractAxis *> axes = chart->m_chart->axes(Qt::Horizontal | Qt::Vertical);
++ return axes.at(index);
++ }
++ return 0;
++ }
++
++ void DeclarativeChart::axesClearFunc(QQmlListProperty<QAbstractAxis> *list)
++ {
++ // Empty implementation
++ Q_UNUSED(list);
++ }
++
++
++ QAbstractSeries *DeclarativeChart::series(int index)
++ {
++ if (index < m_chart->series().size()) {
++ return m_chart->series().at(index);
++ }
++ return 0;
++ }
++
++ QAbstractSeries *DeclarativeChart::series(QString seriesName)
++ {
++ const auto allSeries = m_chart->series();
++ for (QAbstractSeries *series : allSeries) {
++ if (series->name() == seriesName)
++ return series;
++ }
++ return 0;
++ }
++
++ QAbstractSeries *DeclarativeChart::createSeries(int type, QString name, QAbstractAxis *axisX, QAbstractAxis *axisY)
++ {
++ QAbstractSeries *series = 0;
++
++ switch (type) {
++ #if QT_CONFIG(charts_line_chart)
++ case DeclarativeChart::SeriesTypeLine:
++ series = new DeclarativeLineSeries();
++ break;
++ #endif
++ #if QT_CONFIG(charts_area_chart)
++ case DeclarativeChart::SeriesTypeArea: {
++ DeclarativeAreaSeries *area = new DeclarativeAreaSeries();
++ DeclarativeLineSeries *line = new DeclarativeLineSeries();
++ line->setParent(area);
++ area->setUpperSeries(line);
++ series = area;
++ break;
++ }
++ #endif
++ #if QT_CONFIG(charts_bar_chart)
++ case DeclarativeChart::SeriesTypeStackedBar:
++ series = new DeclarativeStackedBarSeries();
++ break;
++ case DeclarativeChart::SeriesTypePercentBar:
++ series = new DeclarativePercentBarSeries();
++ break;
++ case DeclarativeChart::SeriesTypeBar:
++ series = new DeclarativeBarSeries();
++ break;
++ case DeclarativeChart::SeriesTypeHorizontalBar:
++ series = new DeclarativeHorizontalBarSeries();
++ break;
++ case DeclarativeChart::SeriesTypeHorizontalPercentBar:
++ series = new DeclarativeHorizontalPercentBarSeries();
++ break;
++ case DeclarativeChart::SeriesTypeHorizontalStackedBar:
++ series = new DeclarativeHorizontalStackedBarSeries();
++ break;
++ #endif
++ #if QT_CONFIG(charts_boxplot_chart)
++ case DeclarativeChart::SeriesTypeBoxPlot:
++ series = new DeclarativeBoxPlotSeries();
++ break;
++ #endif
++ #if QT_CONFIG(charts_candlestick_chart)
++ case DeclarativeChart::SeriesTypeCandlestick:
++ series = new DeclarativeCandlestickSeries();
++ break;
++ #endif
++ #if QT_CONFIG(charts_pie_chart)
++ case DeclarativeChart::SeriesTypePie:
++ series = new DeclarativePieSeries();
++ break;
++ #endif
++ #if QT_CONFIG(charts_scatter_chart)
++ case DeclarativeChart::SeriesTypeScatter:
++ series = new DeclarativeScatterSeries();
++ break;
++ #endif
++ #if QT_CONFIG(charts_spline_chart)
++ case DeclarativeChart::SeriesTypeSpline:
++ series = new DeclarativeSplineSeries();
++ break;
++ #endif
++ default:
++ qWarning() << "Illegal series type";
++ }
++
++ if (series) {
++ // Connect to axis changed signals (unless this is a pie series)
++ #if QT_CONFIG(charts_pie_chart)
++ if (!qobject_cast<DeclarativePieSeries *>(series))
++ #endif
++ {
++ connect(series, SIGNAL(axisXChanged(QAbstractAxis*)), this, SLOT(handleAxisXSet(QAbstractAxis*)));
++ connect(series, SIGNAL(axisXTopChanged(QAbstractAxis*)), this, SLOT(handleAxisXSet(QAbstractAxis*)));
++ connect(series, SIGNAL(axisYChanged(QAbstractAxis*)), this, SLOT(handleAxisYSet(QAbstractAxis*)));
++ connect(series, SIGNAL(axisYRightChanged(QAbstractAxis*)), this, SLOT(handleAxisYRightSet(QAbstractAxis*)));
++ }
++
++ series->setName(name);
++ m_chart->addSeries(series);
++
++ if (!axisX || !axisY)
++ initializeAxes(series);
++
++ if (axisX)
++ setAxisX(axisX, series);
++ if (axisY)
++ setAxisY(axisY, series);
++ }
++
++ return series;
++ }
++
++ void DeclarativeChart::removeSeries(QAbstractSeries *series)
++ {
++ if (series)
++ m_chart->removeSeries(series);
++ else
++ qWarning("removeSeries: cannot remove null");
++ }
++
++ void DeclarativeChart::setAxisX(QAbstractAxis *axis, QAbstractSeries *series)
++ {
++ if (axis && series)
++ seriesAxisAttachHelper(series, axis, Qt::Horizontal, Qt::AlignBottom);
++ }
++
++ void DeclarativeChart::setAxisY(QAbstractAxis *axis, QAbstractSeries *series)
++ {
++ if (axis && series)
++ seriesAxisAttachHelper(series, axis, Qt::Vertical, Qt::AlignLeft);
++ }
++
++ QAbstractAxis *DeclarativeChart::defaultAxis(Qt::Orientation orientation, QAbstractSeries *series)
++ {
++ if (!series) {
++ qWarning() << "No axis type defined for null series";
++ return 0;
++ }
++
++ const auto axes = m_chart->axes(orientation);
++ for (QAbstractAxis *existingAxis : axes) {
++ if (existingAxis->type() == series->d_ptr->defaultAxisType(orientation))
++ return existingAxis;
++ }
++
++ switch (series->d_ptr->defaultAxisType(orientation)) {
++ case QAbstractAxis::AxisTypeValue:
++ return new QValueAxis(this);
++ case QAbstractAxis::AxisTypeBarCategory:
++ return new QBarCategoryAxis(this);
++ case QAbstractAxis::AxisTypeCategory:
++ return new QCategoryAxis(this);
++ #if QT_CONFIG(charts_datetime_axis)
++ case QAbstractAxis::AxisTypeDateTime:
++ return new QDateTimeAxis(this);
++ #endif
++ case QAbstractAxis::AxisTypeLogValue:
++ return new QLogValueAxis(this);
++ default:
++ // assume AxisTypeNoAxis
++ return 0;
++ }
++ }
++
++ void DeclarativeChart::initializeAxes(QAbstractSeries *series)
++ {
++ if (false) {
++ }
++ #if QT_CONFIG(charts_line_chart)
++ else if (qobject_cast<DeclarativeLineSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeLineSeries *>(series)->m_axes);
++ #endif
++ #if QT_CONFIG(charts_scatter_chart)
++ else if (qobject_cast<DeclarativeScatterSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeScatterSeries *>(series)->m_axes);
++ #endif
++ #if QT_CONFIG(charts_spline_chart)
++ else if (qobject_cast<DeclarativeSplineSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeSplineSeries *>(series)->m_axes);
++ #endif
++ #if QT_CONFIG(charts_area_chart)
++ else if (qobject_cast<DeclarativeAreaSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeAreaSeries *>(series)->m_axes);
++ #endif
++ #if QT_CONFIG(charts_bar_chart)
++ else if (qobject_cast<DeclarativeBarSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeBarSeries *>(series)->m_axes);
++ else if (qobject_cast<DeclarativeStackedBarSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeStackedBarSeries *>(series)->m_axes);
++ else if (qobject_cast<DeclarativePercentBarSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativePercentBarSeries *>(series)->m_axes);
++ else if (qobject_cast<DeclarativeHorizontalBarSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeHorizontalBarSeries *>(series)->m_axes);
++ else if (qobject_cast<DeclarativeHorizontalStackedBarSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeHorizontalStackedBarSeries *>(series)->m_axes);
++ else if (qobject_cast<DeclarativeHorizontalPercentBarSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeHorizontalPercentBarSeries *>(series)->m_axes);
++ #endif
++ #if QT_CONFIG(charts_boxplot_chart)
++ else if (qobject_cast<DeclarativeBoxPlotSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeBoxPlotSeries *>(series)->m_axes);
++ #endif
++ #if QT_CONFIG(charts_candlestick_chart)
++ else if (qobject_cast<DeclarativeCandlestickSeries *>(series))
++ doInitializeAxes(series, qobject_cast<DeclarativeCandlestickSeries *>(series)->m_axes);
++ #endif
++ // else: do nothing
++ }
++
++ void DeclarativeChart::doInitializeAxes(QAbstractSeries *series, DeclarativeAxes *axes)
++ {
++ qreal min;
++ qreal max;
++ // Initialize axis X
++ if (axes->axisX()) {
++ axes->emitAxisXChanged();
++ } else if (axes->axisXTop()) {
++ axes->emitAxisXTopChanged();
++ } else {
++ axes->setAxisX(defaultAxis(Qt::Horizontal, series));
++ findMinMaxForSeries(series, Qt::Horizontal, min, max);
++ axes->axisX()->setRange(min, max);
++ }
++
++ // Initialize axis Y
++ if (axes->axisY()) {
++ axes->emitAxisYChanged();
++ } else if (axes->axisYRight()) {
++ axes->emitAxisYRightChanged();
++ } else {
++ axes->setAxisY(defaultAxis(Qt::Vertical, series));
++ findMinMaxForSeries(series, Qt::Vertical, min, max);
++ axes->axisY()->setRange(min, max);
++ }
++ }
++
++ void DeclarativeChart::findMinMaxForSeries(QAbstractSeries *series, Qt::Orientations orientation,
++ qreal &min, qreal &max)
++ {
++ if (!series) {
++ min = 0.5;
++ max = 0.5;
++ } else {
++ AbstractDomain *domain = series->d_ptr->domain();
++ min = (orientation == Qt::Vertical) ? domain->minY() : domain->minX();
++ max = (orientation == Qt::Vertical) ? domain->maxY() : domain->maxX();
++
++ if (min == max) {
++ min -= 0.5;
++ max += 0.5;
++ }
++ }
++ }
++
++ void DeclarativeChart::queueRendererMouseEvent(QMouseEvent *event)
++ {
++ if (m_glXYDataManager->dataMap().size()) {
++ QMouseEvent *newEvent = new QMouseEvent(event->type(),
++ event->position() - m_adjustedPlotArea.topLeft(),
++ event->globalPosition() - m_adjustedPlotArea.topLeft(),
++ event->button(),
++ event->buttons(),
++ event->modifiers());
++
++ m_pendingRenderNodeMouseEvents.append(newEvent);
++
++ update();
++ }
++ }
++
++ QPointF DeclarativeChart::mapToValue(const QPointF &position, QAbstractSeries *series)
++ {
++ return m_chart->mapToValue(position, series);
++ }
++
++ QPointF DeclarativeChart::mapToPosition(const QPointF &value, QAbstractSeries *series)
++ {
++ return m_chart->mapToPosition(value, series);
++ }
++
++ void DeclarativeChart::setPlotArea(const QRectF &rect)
++ {
++ m_chart->setPlotArea(rect);
++
++ // If plotArea is set inside ChartView, contentGeometry is updated too early and we end up
++ // with incorrect plotArea. Invalidate the layout to correct the geometry.
++ m_chart->layout()->invalidate();
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_declarativechart_p.cpp"