Revision control
++ // Copyright (C) 2023 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++
++ #include <QtCore/qmath.h>
++ #include "qabstract3daxis_p.h"
++ #include "qbar3dseries_p.h"
++ #include "qcategory3daxis_p.h"
++ #include "qquickgraphsbars_p.h"
++ #include "qvalue3daxis_p.h"
++ #include "qgraphs3dlogging_p.h"
++
++ #include <QtGui/qquaternion.h>
++
++ QT_BEGIN_NAMESPACE
++
++ /*!
++ * \class QBar3DSeries
++ * \inmodule QtGraphs
++ * \ingroup graphs_3D
++ * \brief The QBar3DSeries class represents a data series in a 3D bar graph.
++ *
++ * This class manages the series specific visual elements, as well as the series
++ * data (via a data proxy).
++ *
++ * Regarding the proxy-series relationship, it is crucial to highlight
++ * a couple of key points. In this context, data is stored in series and
++ * users can access the dataset through the series. This series is controlled
++ * or represented by a proxy object. Thus, the proxy can be used to manage various
++ * operations on the data and update the actual dataset. However, it is necessary
++ * to create a series associated with this proxy to edit the dataset.
++ *
++ * If no data proxy is set explicitly for the series, the series creates a
++ * default proxy. Setting another proxy will destroy the existing proxy and all
++ * data added to the series.
++ *
++ * QBar3DSeries supports the following format tags for QAbstract3DSeries::setItemLabelFormat():
++ * \table
++ * \row
++ * \li @rowTitle \li Title from row axis
++ * \row
++ * \li @colTitle \li Title from column axis
++ * \row
++ * \li @valueTitle \li Title from value axis
++ * \row
++ * \li @rowIdx \li Visible row index. Localized using the graph locale.
++ * \row
++ * \li @colIdx \li Visible column index. Localized using the graph locale.
++ * \row
++ * \li @rowLabel \li Label from row axis
++ * \row
++ * \li @colLabel \li Label from column axis
++ * \row
++ * \li @valueLabel \li Item value formatted using the format of the value
++ * axis attached to the graph. For more information,
++ * see \l{QValue3DAxis::labelFormat}.
++ * \row
++ * \li @seriesName \li Name of the series
++ * \row
++ * \li %<format spec> \li Item value in the specified format. Formatted
++ * using the same rules as \l{QValue3DAxis::labelFormat}.
++ * \endtable
++ *
++ * For example:
++ * \snippet doc_src_qtgraphs.cpp labelformat
++ *
++ * \sa {Qt Graphs Data Handling with 3D}, Q3DGraphsWidgetItem::locale
++ */
++
++ /*!
++ * \qmltype Bar3DSeries
++ * \inqmlmodule QtGraphs
++ * \ingroup graphs_qml_3D
++ * \nativetype QBar3DSeries
++ * \inherits Abstract3DSeries
++ * \brief Represents a data series in a 3D bar graph.
++ *
++ * This type manages the series specific visual elements, as well as the series
++ * data (via a data proxy).
++ *
++ * Bar3DSeries supports the following format tags for itemLabelFormat:
++ * \table
++ * \row
++ * \li @rowTitle \li Title from row axis
++ * \row
++ * \li @colTitle \li Title from column axis
++ * \row
++ * \li @valueTitle \li Title from value axis
++ * \row
++ * \li @rowIdx \li Visible row index. Localized using the graph locale.
++ * \row
++ * \li @colIdx \li Visible column index. Localized using the graph locale.
++ * \row
++ * \li @rowLabel \li Label from row axis
++ * \row
++ * \li @colLabel \li Label from column axis
++ * \row
++ * \li @valueLabel \li Item value formatted using the format of the value
++ * axis attached to the graph. For more information,
++ * see \l{QValue3DAxis::labelFormat}{labelFormat}.
++ * \row
++ * \li @seriesName \li Name of the series
++ * \row
++ * \li %<format spec> \li Item value in the specified format. Formatted
++ * using the same rules as \l{QValue3DAxis::labelFormat}{labelFormat}.
++ * \endtable
++ *
++ * For a more complete description, see QBar3DSeries.
++ *
++ * \sa {Qt Graphs Data Handling with 3D}
++ */
++
++ /*!
++ * \qmlproperty BarDataProxy Bar3DSeries::dataProxy
++ *
++ * The active data proxy. The series assumes ownership of any proxy set to
++ * it and deletes any previously set proxy when a new one is added. The proxy
++ * cannot be null or set to another series.
++ */
++
++ /*!
++ * \qmlproperty point Bar3DSeries::selectedBar
++ *
++ * The bar in the series that is selected.
++ *
++ * The position of the selected bar is specified as a row and column in the
++ * data array of the series.
++ *
++ * Only one bar can be selected at a time.
++ *
++ * To clear the selection from this series, assign invalidSelectionPosition as the
++ * position.
++ *
++ * If this series is added to a graph, the graph can adjust the selection
++ * according to user interaction or if it becomes invalid. Selecting a bar on
++ * another added series will also clear the selection.
++ *
++ * Removing rows from or inserting rows into the series before the row of the
++ * selected bar will adjust the selection so that the same bar will stay
++ * selected.
++ *
++ * \sa {GraphsItem3D::clearSelection()}{GraphsItem3D.clearSelection()}
++ */
++
++ /*!
++ * \qmlproperty point Bar3DSeries::invalidSelectionPosition
++ * \readonly
++ *
++ * A constant property providing an invalid position for selection. This
++ * position is assigned to the selectedBar property to clear the selection from this
++ * series.
++ *
++ * \sa {GraphsItem3D::clearSelection()}{GraphsItem3D.clearSelection()}
++ */
++
++ /*!
++ * \qmlproperty real Bar3DSeries::meshAngle
++ *
++ * A convenience property for defining the series rotation angle in degrees.
++ *
++ * \note When reading this property, it is calculated from the
++ * \l{Abstract3DSeries::meshRotation}{Abstract3DSeries.meshRotation} value
++ * using floating point precision and always returns a value from zero to 360
++ * degrees.
++ *
++ * \sa {Abstract3DSeries::meshRotation}{Abstract3DSeries.meshRotation}
++ */
++
++ /*!
++ * \qmlproperty list<Color> Bar3DSeries::rowColors
++ * This property can be used to draw the rows of the series in different colors.
++ * The \l{QGraphsTheme::colorStyle}{GraphsTheme.colorStyle} must be set to
++ * \c Uniform to use this property.
++ * \note If the property is set and the theme is changed,
++ * the rowColors list is not cleared automatically.
++ *
++ * \sa QGraphsTheme::ColorStyle::Uniform
++ */
++
++ /*!
++ * \qmlproperty list Bar3DSeries::rowLabels
++ *
++ * The optional row labels for the array. Indexes in this array match the row
++ * indexes in the data array.
++ * If the list is shorter than the number of rows, all rows will not get labels.
++ */
++
++ /*!
++ * \qmlproperty list Bar3DSeries::columnLabels
++ *
++ * The optional column labels for the array. Indexes in this array match column
++ * indexes in rows. If the list is shorter than the longest row, all columns
++ * will not get labels.
++ */
++
++ /*!
++ * \qmlproperty BarDataArray Bar3DSeries::dataArray
++ *
++ * Holds the reference of the data array.
++ *
++ * dataArrayChanged signal is emitted when data array is set, unless \a newDataArray
++ * is identical to the previous one.
++ *
++ * \note Before doing anything regarding the dataArray, a series must be created for
++ * the relevant proxy.
++ */
++
++ /*!
++ \qmlsignal Bar3DSeries::dataProxyChanged(BarDataProxy proxy)
++
++ This signal is emitted when dataProxy changes to \a proxy.
++ */
++
++ /*!
++ \qmlsignal Bar3DSeries::selectedBarChanged(point position)
++
++ This signal is emitted when selectedBar changes to \a position.
++ */
++
++ /*!
++ \qmlsignal Bar3DSeries::meshAngleChanged(real angle)
++
++ This signal is emitted when meshAngle changes to \a angle.
++ */
++
++ /*!
++ \qmlsignal Bar3DSeries::rowColorsChanged(list<color> rowcolors)
++
++ This signal is emitted when rowColors changes to \a rowcolors.
++ */
++
++ /*!
++ \qmlsignal Bar3DSeries::rowLabelsChanged()
++
++ This signal is emitted when row labels change.
++ */
++
++ /*!
++ \qmlsignal Bar3DSeries::columnLabelsChanged()
++
++ This signal is emitted when column labels change.
++ */
++
++ /*!
++ \qmlsignal Bar3DSeries::dataArrayChanged(BarDataArray array)
++
++ This signal is emitted when dataArray changes to \a array.
++ */
++
++ /*!
++ * Constructs a bar 3D series with the parent \a parent.
++ */
++ QBar3DSeries::QBar3DSeries(QObject *parent)
++ : QAbstract3DSeries(*(new QBar3DSeriesPrivate()), parent)
++ {
++ Q_D(QBar3DSeries);
++ // Default proxy
++ d->setDataProxy(new QBarDataProxy);
++ connectSignals();
++ }
++
++ /*!
++ * Constructs a bar 3D series with the data proxy \a dataProxy and the parent
++ * \a parent.
++ */
++ QBar3DSeries::QBar3DSeries(QBarDataProxy *dataProxy, QObject *parent)
++ : QAbstract3DSeries(*(new QBar3DSeriesPrivate()), parent)
++ {
++ Q_D(QBar3DSeries);
++ d->setDataProxy(dataProxy);
++ connectSignals();
++ }
++
++ /*!
++ * Deletes a bar 3D series.
++ */
++ QBar3DSeries::~QBar3DSeries()
++ {
++ clearArray();
++ }
++
++ /*!
++ * \property QBar3DSeries::dataProxy
++ *
++ * \brief The active data proxy.
++ *
++ * The series assumes ownership of any proxy set to it and deletes any
++ * previously set proxy when a new one is added. The proxy cannot be null or
++ * set to another series.
++ */
++ void QBar3DSeries::setDataProxy(QBarDataProxy *proxy)
++ {
++ Q_D(QBar3DSeries);
++ d->setDataProxy(proxy);
++ }
++
++ QBarDataProxy *QBar3DSeries::dataProxy() const
++ {
++ Q_D(const QBar3DSeries);
++ return static_cast<QBarDataProxy *>(d->dataProxy());
++ }
++
++ /*!
++ * \property QBar3DSeries::selectedBar
++ *
++ * \brief The bar in the series that is selected.
++ *
++ */
++
++ /*!
++ * Selects the bar at the \a position position, specified as a row and column in
++ * the data array of the series.
++ *
++ * Only one bar can be selected at a time.
++ *
++ * To clear the selection from this series, invalidSelectionPosition() is set as
++ * \a position.
++ *
++ * If this series is added to a graph, the graph can adjust the selection
++ * according to user interaction or if it becomes invalid. Selecting a bar on
++ * another added series will also clear the selection.
++ *
++ * Removing rows from or inserting rows into the series before the row of the
++ * selected bar will adjust the selection so that the same bar will stay
++ * selected.
++ *
++ * \sa Q3DGraphsWidgetItem::clearSelection()
++ */
++ void QBar3DSeries::setSelectedBar(QPoint position)
++ {
++ Q_D(QBar3DSeries);
++ // Don't do this in private to avoid loops, as that is used for callback from
++ // graph.
++ if (d->m_graph)
++ static_cast<QQuickGraphsBars *>(d->m_graph)->setSelectedBar(position, this, true);
++ else
++ d->setSelectedBar(position);
++ }
++
++ QPoint QBar3DSeries::selectedBar() const
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_selectedBar;
++ }
++
++ /*!
++ * Returns an invalid position for selection. This position is set to the
++ * selectedBar property to clear the selection from this series.
++ *
++ * \sa Q3DGraphsWidgetItem::clearSelection()
++ */
++ QPoint QBar3DSeries::invalidSelectionPosition()
++ {
++ return QQuickGraphsBars::invalidSelectionPosition();
++ }
++
++ static inline float quaternionAngle(const QQuaternion &rotation)
++ {
++ return qRadiansToDegrees(qAcos(rotation.scalar())) * 2.f;
++ }
++
++ /*!
++ \property QBar3DSeries::meshAngle
++
++ \brief The series rotation angle in degrees.
++
++ Setting this property is equivalent to the following call:
++
++ \code
++ setMeshRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, angle))
++ \endcode
++
++ \note When reading this property, it is calculated from the
++ QAbstract3DSeries::meshRotation value using floating point precision
++ and always returns a value from zero to 360 degrees.
++
++ \sa QAbstract3DSeries::meshRotation
++ */
++ void QBar3DSeries::setMeshAngle(float angle)
++ {
++ setMeshRotation(QQuaternion::fromAxisAndAngle(upVector, angle));
++ }
++
++ float QBar3DSeries::meshAngle() const
++ {
++ QQuaternion rotation = meshRotation();
++
++ if (rotation.isIdentity() || rotation.x() != 0.0f || rotation.z() != 0.0f)
++ return 0.0f;
++ else
++ return quaternionAngle(rotation);
++ }
++
++ /*!
++ * \property QBar3DSeries::rowColors
++ *
++ * \brief The list of row colors in the series.
++ *
++ * This property can be used to color
++ * the rows of the series in different colors.
++ * The QGraphsTheme::ColorStyle must be set to
++ * QGraphsTheme::ColorStyle::Uniform to use this property.
++ *
++ * \sa QGraphsTheme::ColorStyle::Uniform
++ */
++ void QBar3DSeries::setRowColors(const QList<QColor> &colors)
++ {
++ Q_D(QBar3DSeries);
++ d->setRowColors(colors);
++ }
++
++ /*!
++ * \property QBar3DSeries::valueColoringEnabled
++ * \since 6.9
++ *
++ * \brief Use the given value to color the whole bar based on the range gradient
++ *
++ * This property can be used to color each
++ * bar separately based on its value and given range gradient.
++ * The QGraphsTheme::ColorStyle must be set to
++ * QGraphsTheme::ColorStyle::RangeGradient to use this property.
++ *
++ * \sa QGraphsTheme::ColorStyle::RangeGradient
++ */
++ void QBar3DSeries::setValueColoringEnabled(bool enabled)
++ {
++ Q_D(QBar3DSeries);
++ d->setValueColoringEnabled(enabled);
++ }
++
++ bool QBar3DSeries::isValueColoringEnabled() const
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_valueColoring;
++ }
++
++ /*!
++ * \property QBar3DSeries::dataArray
++ *
++ * \brief Data array for the series.
++ *
++ * Holds the reference of the data array.
++ *
++ * dataArrayChanged signal is emitted when data array is set, unless \a newDataArray
++ * is identical to the previous one.
++ *
++ * \note Before doing anything regarding the dataArray, a series must be created for
++ * the relevant proxy.
++ *
++ *\sa clearRow(qsizetype rowIndex)
++ *
++ *\sa clearArray()
++ */
++ void QBar3DSeries::setDataArray(const QBarDataArray &newDataArray)
++ {
++ Q_D(QBar3DSeries);
++ if (d->m_dataArray.isSharedWith(newDataArray)) {
++ qCDebug(lcProperties3D, "%s newDataArray is the same than the old one",
++ qUtf8Printable(QLatin1String(__FUNCTION__)));
++ return;
++ }
++ d->m_dataArray = newDataArray;
++ }
++
++ /*!
++ * Clears the existing row in the array according to given \a rowIndex.
++ */
++ void QBar3DSeries::clearRow(qsizetype rowIndex)
++ {
++ Q_D(QBar3DSeries);
++ d->clearRow(rowIndex);
++ }
++
++ /*!
++ * Clears the existing array.
++ */
++ void QBar3DSeries::clearArray()
++ {
++ Q_D(QBar3DSeries);
++ d->clearArray();
++ }
++
++ const QBarDataArray &QBar3DSeries::dataArray() const &
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_dataArray;
++ }
++
++ QBarDataArray QBar3DSeries::dataArray() &&
++ {
++ Q_D(QBar3DSeries);
++ return std::move(d->m_dataArray);
++ }
++
++ /*!
++ * \property QBar3DSeries::rowLabels
++ *
++ * \brief The optional row labels for the array.
++ *
++ * Indexes in this array match the row indexes in the data array.
++ * If the list is shorter than the number of rows, all rows will not get labels.
++ */
++ QStringList QBar3DSeries::rowLabels() const
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_rowLabels;
++ }
++
++ void QBar3DSeries::setRowLabels(const QStringList &labels)
++ {
++ Q_D(QBar3DSeries);
++ if (rowLabels() == labels) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << labels;
++ return;
++ }
++ d->setRowLabels(labels);
++ emit rowLabelsChanged();
++ }
++
++ /*!
++ * \property QBar3DSeries::columnLabels
++ *
++ * \brief The optional column labels for the array.
++ *
++ * Indexes in this array match column indexes in rows.
++ * If the list is shorter than the longest row, all columns will not get labels.
++ */
++ QStringList QBar3DSeries::columnLabels() const
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_columnLabels;
++ }
++
++ void QBar3DSeries::setColumnLabels(const QStringList &labels)
++ {
++ Q_D(QBar3DSeries);
++ if (columnLabels() == labels) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << labels;
++ return;
++ }
++ d->setColumnLabels(labels);
++ emit columnLabelsChanged();
++ }
++
++ /*!
++ * \property QBar3DSeries::customRowLabels
++ * \since 6.11
++ *
++ * \brief The optional custom row labels for the array.
++ *
++ * These labels will override any labels generated by a proxy.
++ * Indexes in this array match the row indexes in the data array.
++ * If the list is shorter than the number of rows, all rows will not get labels.
++ */
++ QStringList QBar3DSeries::customRowLabels() const
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_customRowLabels;
++ }
++
++ void QBar3DSeries::setCustomRowLabels(const QStringList &labels)
++ {
++ Q_D(QBar3DSeries);
++ if (customRowLabels() == labels) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << labels;
++ return;
++ }
++ d->setCustomRowLabels(labels);
++ emit customRowLabelsChanged(labels);
++ }
++
++ /*!
++ * \property QBar3DSeries::customColumnLabels
++ * \since 6.11
++ *
++ * \brief The optional custom column labels for the array.
++ *
++ * These labels will override any labels generated by a proxy.
++ * Indexes in this array match column indexes in rows.
++ * If the list is shorter than the longest row, all columns will not get labels.
++ */
++ QStringList QBar3DSeries::customColumnLabels() const
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_customColumnLabels;
++ }
++
++ void QBar3DSeries::setCustomColumnLabels(const QStringList &labels)
++ {
++ Q_D(QBar3DSeries);
++ if (customColumnLabels() == labels) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << labels;
++ return;
++ }
++ d->setCustomColumnLabels(labels);
++ emit customColumnLabelsChanged(labels);
++ }
++
++ QList<QColor> QBar3DSeries::rowColors() const
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_rowColors;
++ }
++
++ /*!
++ * \property QBar3DSeries::rowAxis
++ * \since 6.11
++ *
++ * \brief Holds an additional row axis for the series
++ * \note The additional axis does not adjust the
++ * fitting of the graph, but allows for an extra set of
++ * labels and an axis grid.
++ */
++ void QBar3DSeries::setRowAxis(QCategory3DAxis *axis)
++ {
++ Q_D(QBar3DSeries);
++ if (rowAxis() == axis) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << axis;
++ return;
++ }
++ d->setRowAxis(axis);
++ emit rowAxisChanged(axis);
++ }
++
++ QCategory3DAxis *QBar3DSeries::rowAxis() const
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_rowAxis;
++ }
++
++ void QBar3DSeries::resetRowAxis()
++ {
++ Q_D(QBar3DSeries);
++ if (!d->m_rowAxis) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "row axis not set";
++ return;
++ }
++ d->resetRowAxis();
++ emit rowAxisChanged(nullptr);
++ }
++
++
++ /*!
++ * \property QBar3DSeries::valueAxis
++ * \since 6.11
++ *
++ * \brief Holds an additional value axis for the series
++ * If an axis is given, the series will be fitted
++ * to the minimum and maximum values of the axis.
++ */
++ void QBar3DSeries::setValueAxis(QValue3DAxis *axis)
++ {
++ Q_D(QBar3DSeries);
++ if (valueAxis() == axis) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << axis;
++ return;
++ }
++ d->setValueAxis(axis);
++ emit valueAxisChanged(axis);
++ }
++
++ QValue3DAxis *QBar3DSeries::valueAxis() const
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_valueAxis;
++ }
++
++ void QBar3DSeries::resetValueAxis()
++ {
++ Q_D(QBar3DSeries);
++ if (!d->m_valueAxis) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value axis not set";
++ return;
++ }
++ d->resetValueAxis();
++ emit valueAxisChanged(nullptr);
++ }
++
++ /*!
++ * \property QBar3DSeries::columnAxis
++ * \since 6.11
++ *
++ * \brief Holds an additional column axis for the series
++ * \note The additional axis does not adjust the
++ * fitting of the graph, but allows for an extra set of
++ * labels and an axis grid.
++ */
++ void QBar3DSeries::setColumnAxis(QCategory3DAxis *axis)
++ {
++ Q_D(QBar3DSeries);
++ if (columnAxis() == axis) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << axis;
++ return;
++ }
++ d->setColumnAxis(axis);
++ emit columnAxisChanged(axis);
++ }
++
++ QCategory3DAxis *QBar3DSeries::columnAxis() const
++ {
++ Q_D(const QBar3DSeries);
++ return d->m_columnAxis;
++ }
++
++ void QBar3DSeries::resetColumnAxis()
++ {
++ Q_D(QBar3DSeries);
++ if (!d->m_columnAxis) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "column axis not set";
++ return;
++ }
++ d->resetColumnAxis();
++ emit columnAxisChanged(nullptr);
++ }
++
++ /*!
++ * \internal
++ */
++ void QBar3DSeries::connectSignals()
++ {
++ QObject::connect(this,
++ &QAbstract3DSeries::meshRotationChanged,
++ this,
++ &QBar3DSeries::handleMeshRotationChanged);
++ }
++
++ /*!
++ * \internal
++ */
++ void QBar3DSeries::handleMeshRotationChanged(const QQuaternion &rotation)
++ {
++ emit meshAngleChanged(quaternionAngle(rotation));
++ }
++
++ // QBar3DSeriesPrivate
++
++ QBar3DSeriesPrivate::QBar3DSeriesPrivate()
++ : QAbstract3DSeriesPrivate(QAbstract3DSeries::SeriesType::Bar)
++ , m_selectedBar(QQuickGraphsBars::invalidSelectionPosition())
++ {
++ m_itemLabelFormat = QStringLiteral("@valueLabel");
++ m_mesh = QAbstract3DSeries::Mesh::BevelBar;
++ m_valueColoring = false;
++ }
++
++ QBar3DSeriesPrivate::~QBar3DSeriesPrivate() {}
++
++ void QBar3DSeriesPrivate::fixRowLabels(qsizetype startIndex,
++ qsizetype count,
++ const QStringList &newLabels,
++ bool isInsert)
++ {
++ bool changed = false;
++ qsizetype currentSize = m_rowLabels.size();
++
++ qsizetype newSize = newLabels.size();
++ if (startIndex >= currentSize) {
++ // Adding labels past old label array, create empty strings to fill
++ // intervening space
++ if (newSize) {
++ for (qsizetype i = currentSize; i < startIndex; i++)
++ m_rowLabels << QString();
++ // Doesn't matter if insert, append, or just change when there were no
++ // existing strings, just append new strings.
++ m_rowLabels << newLabels;
++ changed = true;
++ }
++ } else {
++ if (isInsert) {
++ qsizetype insertIndex = startIndex;
++ if (count)
++ changed = true;
++ for (qsizetype i = 0; i < count; i++) {
++ if (i < newSize)
++ m_rowLabels.insert(insertIndex++, newLabels.at(i));
++ else
++ m_rowLabels.insert(insertIndex++, QString());
++ }
++ } else {
++ // Either append or change, replace labels up to array end and then add
++ // new ones
++ qsizetype lastChangeIndex = count + startIndex;
++ qsizetype newIndex = 0;
++ for (qsizetype i = startIndex; i < lastChangeIndex; i++) {
++ if (i >= currentSize) {
++ // Label past the current size, so just append the new label
++ if (newSize < newIndex) {
++ changed = true;
++ m_rowLabels << newLabels.at(newIndex);
++ } else {
++ break; // No point appending empty strings, so just exit
++ }
++ } else if (newSize > newIndex) {
++ // Replace existing label
++ if (m_rowLabels.at(i) != newLabels.at(newIndex)) {
++ changed = true;
++ m_rowLabels[i] = newLabels.at(newIndex);
++ }
++ } else {
++ // No more new labels, so clear existing label
++ if (!m_rowLabels.at(i).isEmpty()) {
++ changed = true;
++ m_rowLabels[i] = QString();
++ }
++ }
++ newIndex++;
++ }
++ }
++ }
++
++ if (changed) {
++ Q_Q(QBar3DSeries);
++ emit q->rowLabelsChanged();
++ }
++ }
++
++ void QBar3DSeriesPrivate::setDataProxy(QAbstractDataProxy *proxy)
++ {
++ Q_ASSERT(proxy->type() == QAbstractDataProxy::DataType::Bar);
++ Q_Q(QBar3DSeries);
++
++ QAbstract3DSeriesPrivate::setDataProxy(proxy);
++
++ emit q->dataProxyChanged(static_cast<QBarDataProxy *>(proxy));
++ }
++
++ void QBar3DSeriesPrivate::connectGraphAndProxy(QQuickGraphsItem *newGraph)
++ {
++ Q_Q(QBar3DSeries);
++ QBarDataProxy *barDataProxy = static_cast<QBarDataProxy *>(m_dataProxy);
++
++ if (m_graph && barDataProxy) {
++ // Disconnect old graph/old proxy
++ QObject::disconnect(barDataProxy, 0, m_graph, 0);
++ QObject::disconnect(q, 0, m_graph, 0);
++ }
++
++ if (newGraph && barDataProxy) {
++ QQuickGraphsBars *graph = static_cast<QQuickGraphsBars *>(newGraph);
++ QObject::connect(barDataProxy,
++ &QBarDataProxy::arrayReset,
++ graph,
++ &QQuickGraphsBars::handleArrayReset);
++ QObject::connect(barDataProxy,
++ &QBarDataProxy::rowsAdded,
++ graph,
++ &QQuickGraphsBars::handleRowsAdded);
++ QObject::connect(barDataProxy,
++ &QBarDataProxy::rowsChanged,
++ graph,
++ &QQuickGraphsBars::handleRowsChanged);
++ QObject::connect(barDataProxy,
++ &QBarDataProxy::rowsRemoved,
++ graph,
++ &QQuickGraphsBars::handleRowsRemoved);
++ QObject::connect(barDataProxy,
++ &QBarDataProxy::rowsInserted,
++ graph,
++ &QQuickGraphsBars::handleRowsInserted);
++ QObject::connect(barDataProxy,
++ &QBarDataProxy::itemChanged,
++ graph,
++ &QQuickGraphsBars::handleItemChanged);
++ QObject::connect(q,
++ &QBar3DSeries::rowLabelsChanged,
++ graph,
++ &QQuickGraphsBars::handleDataRowLabelsChanged);
++ QObject::connect(q,
++ &QBar3DSeries::columnLabelsChanged,
++ graph,
++ &QQuickGraphsBars::handleDataColumnLabelsChanged);
++ QObject::connect(q,
++ &QBar3DSeries::dataProxyChanged,
++ graph,
++ &QQuickGraphsBars::handleArrayReset);
++ QObject::connect(q,
++ &QBar3DSeries::rowColorsChanged,
++ graph,
++ &QQuickGraphsBars::handleRowColorsChanged);
++ }
++ }
++
++ void QBar3DSeriesPrivate::createItemLabel()
++ {
++ Q_Q(QBar3DSeries);
++ static const QString rowIndexTag(QStringLiteral("@rowIdx"));
++ static const QString rowLabelTag(QStringLiteral("@rowLabel"));
++ static const QString rowTitleTag(QStringLiteral("@rowTitle"));
++ static const QString colIndexTag(QStringLiteral("@colIdx"));
++ static const QString colLabelTag(QStringLiteral("@colLabel"));
++ static const QString colTitleTag(QStringLiteral("@colTitle"));
++ static const QString valueTitleTag(QStringLiteral("@valueTitle"));
++ static const QString valueLabelTag(QStringLiteral("@valueLabel"));
++ static const QString seriesNameTag(QStringLiteral("@seriesName"));
++
++ if (m_selectedBar == QBar3DSeries::invalidSelectionPosition()) {
++ m_itemLabel = QString(hiddenLabelTag);
++ return;
++ }
++
++ QLocale locale(QLocale::c());
++ if (m_graph)
++ locale = m_graph->locale();
++ else
++ return;
++
++ QCategory3DAxis *categoryAxisZ = static_cast<QCategory3DAxis *>(m_graph->axisZ());
++ QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_graph->axisX());
++ QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(m_graph->axisY());
++ qreal selectedBarValue = qreal(q->dataProxy()->itemAt(m_selectedBar).value());
++
++ // Custom format expects printf format specifier. There is no tag for it.
++ m_itemLabel = valueAxis->formatter()->stringForValue(selectedBarValue, m_itemLabelFormat);
++
++ int selBarPosRow = m_selectedBar.x();
++ int selBarPosCol = m_selectedBar.y();
++ m_itemLabel.replace(rowIndexTag, locale.toString(selBarPosRow));
++ if (categoryAxisZ->labels().size() > selBarPosRow)
++ m_itemLabel.replace(rowLabelTag, categoryAxisZ->labels().at(selBarPosRow));
++ else
++ m_itemLabel.replace(rowLabelTag, QString());
++ m_itemLabel.replace(rowTitleTag, categoryAxisZ->title());
++ m_itemLabel.replace(colIndexTag, locale.toString(selBarPosCol));
++ if (categoryAxisX->labels().size() > selBarPosCol)
++ m_itemLabel.replace(colLabelTag, categoryAxisX->labels().at(selBarPosCol));
++ else
++ m_itemLabel.replace(colLabelTag, QString());
++ m_itemLabel.replace(colTitleTag, categoryAxisX->title());
++ m_itemLabel.replace(valueTitleTag, valueAxis->title());
++
++ if (m_itemLabel.contains(valueLabelTag)) {
++ QString valueLabelText = valueAxis->formatter()->stringForValue(selectedBarValue,
++ valueAxis->labelFormat());
++ m_itemLabel.replace(valueLabelTag, valueLabelText);
++ }
++
++ m_itemLabel.replace(seriesNameTag, m_name);
++ }
++
++ void QBar3DSeriesPrivate::setSelectedBar(QPoint position)
++ {
++ Q_Q(QBar3DSeries);
++ if (position == m_selectedBar) {
++ qCDebug(lcProperties3D, "%s value is already set to: %d %d",
++ qUtf8Printable(QLatin1String(__FUNCTION__)), position.x(), position.y());
++ return;
++ }
++
++ markItemLabelDirty();
++ m_selectedBar = position;
++ emit q->selectedBarChanged(m_selectedBar);
++ }
++
++ void QBar3DSeriesPrivate::setRowColors(const QList<QColor> &colors)
++ {
++ Q_Q(QBar3DSeries);
++ if (m_rowColors == colors) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << colors;
++ return;
++ }
++
++ m_rowColors = colors;
++ emit q->rowColorsChanged(m_rowColors);
++ }
++
++ void QBar3DSeriesPrivate::setValueColoringEnabled(bool enabled)
++ {
++ Q_Q(QBar3DSeries);
++ if (m_valueColoring == enabled) {
++ qCDebug(lcProperties3D) << __FUNCTION__
++ << "value is already set to:" << enabled;
++ return;
++ }
++ m_valueColoring = enabled;
++ emit q->valueColoringEnabledChanged(enabled);
++ }
++
++ void QBar3DSeriesPrivate::setDataArray(const QBarDataArray &newDataArray)
++ {
++ m_dataArray = newDataArray;
++ }
++
++ void QBar3DSeriesPrivate::clearRow(qsizetype rowIndex)
++ {
++ m_dataArray[rowIndex].clear();
++ }
++
++ void QBar3DSeriesPrivate::clearArray()
++ {
++ m_dataArray.clear();
++ }
++
++ void QBar3DSeriesPrivate::setRowLabels(const QStringList &labels)
++ {
++ m_rowLabels = labels;
++ }
++
++ void QBar3DSeriesPrivate::setColumnLabels(const QStringList &labels)
++ {
++ m_columnLabels = labels;
++ }
++
++ void QBar3DSeriesPrivate::setCustomRowLabels(const QStringList &labels)
++ {
++ m_customRowLabels = labels;
++ }
++
++ void QBar3DSeriesPrivate::setCustomColumnLabels(const QStringList &labels)
++ {
++ m_customColumnLabels = labels;
++ }
++
++ void QBar3DSeriesPrivate::setRowAxis(QCategory3DAxis *axis)
++ {
++ m_rowAxis = axis;
++ }
++
++ void QBar3DSeriesPrivate::resetRowAxis()
++ {
++ m_rowAxis = nullptr;
++ }
++
++ void QBar3DSeriesPrivate::setValueAxis(QValue3DAxis *axis)
++ {
++ m_valueAxis = axis;
++ }
++
++ void QBar3DSeriesPrivate::resetValueAxis()
++ {
++ m_valueAxis = nullptr;
++ }
++
++ void QBar3DSeriesPrivate::setColumnAxis(QCategory3DAxis *axis)
++ {
++ m_columnAxis = axis;
++ }
++
++ void QBar3DSeriesPrivate::resetColumnAxis()
++ {
++ m_columnAxis = nullptr;
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qbar3dseries.cpp"