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 "graphs2d/qabstractseries_p.h"
++ #include <QtGraphs/qabstractseries.h>
++ #include <QtGraphs/qbarseries.h>
++ #include <QtGraphs/qbarset.h>
++ #include <private/qabstractseries_p.h>
++ #include <private/qbarseries_p.h>
++ #include <private/qbarset_p.h>
++ #include <private/qgraphsview_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ /*!
++ \class QBarSeries
++ \inmodule QtGraphs
++ \ingroup graphs_2D
++ \brief The QBarSeries class presents data in bar graphs.
++
++ This class draws data by default as a series of bars grouped by category,
++ with one bar per category from each bar set added to the series.
++ It also supports horizontal bars and grouping bars as stacked.
++
++ A bar series needs the GraphsView x-axis to be set to a BarCategoryAxis and
++ the y-axis set to ValueAxis.
++
++ \sa QBarSet, QAbstractSeries
++ */
++ /*!
++ \qmltype BarSeries
++ \nativetype QBarSeries
++ \inqmlmodule QtGraphs
++ \ingroup graphs_qml_2D
++ \inherits AbstractSeries
++
++ \brief Presents data in bar graphs.
++
++ Draws data by default as a series of bars grouped by category,
++ with one bar per category from each bar set added to the series.
++ It also supports horizontal bars and grouping bars as stacked.
++ */
++
++ /*!
++ \property QBarSeries::barWidth
++ \brief The width of the bars of the series.
++
++ The unit of width is the unit of the x-axis. The minimum width for bars is zero, and negative
++ values are treated as zero. Setting the width to zero means that the width of the bar on the
++ screen is one pixel regardless of the scale of the x-axis. Bars wider than zero are scaled
++ using the x-axis scale.
++
++ By default, the barWidth is 0.5 (bars will take 50% of the available width).
++ The valid values range from 0.0 (0%) to 1.0 (100%).
++
++ \note When used with QBarSeries, this value specifies the width of a group of bars instead of
++ that of a single bar.
++ \sa QBarSeries
++ */
++
++ /*!
++ \enum QBarSeries::BarsType
++
++ This enum value describes the type of the bar series:
++
++ \value Groups Bar sets are grouped by category.
++ \value Stacked Bar sets are stacked after each other by category.
++ \value StackedPercent Bar sets are stacked after each other by category.
++ The segment size corresponds to the percentage of the segment value
++ compared with the total value of all segments in the stack.
++ */
++ /*!
++ \property QBarSeries::barsType
++ \brief The type of the bar series.
++ */
++ /*!
++ \qmlproperty enumeration BarSeries::barsType
++
++ The type of the bar series:
++
++ \value BarSeries.BarsType.Groups
++ Bar sets are grouped by category. This is the default value.
++ \value BarSeries.BarsType.Stacked
++ Bar sets are stacked after each other by category.
++ \value BarSeries.BarsType.StackedPercent
++ Bar sets are stacked after each other by category. The segment size corresponds
++ to the percentage of the segment value compared with the total value of all
++ segments in the stack.
++ */
++
++ /*!
++ \qmlproperty real BarSeries::barWidth
++ The unit of width is the unit of the x-axis. The minimum width for bars is zero, and negative
++ values are treated as zero. Setting the width to zero means that the width of the bar on the
++ screen is one pixel regardless of the scale of the x-axis. Bars wider than zero are scaled
++ using the x-axis scale.
++
++ By default, the barWidth is 0.5 (bars will take 50% of the available width).
++ The valid values range from 0.0 (0%) to 1.0 (100%).
++
++ \note When used with the BarSeries type, this value specifies the width of a group of bars
++ instead of that of a single bar.
++ */
++
++ /*!
++ \property QBarSeries::count
++ \brief The number of bar sets in a bar series.
++ */
++ /*!
++ \qmlproperty int BarSeries::count
++ The number of bar sets in a bar series.
++ */
++
++ /*!
++ \property QBarSeries::labelsVisible
++ \brief The visibility of the labels in a bar series.
++ The default label visibility is \c false.
++
++ \note The default label only displays the value for the set.
++ You can create custom labels with a \l barDelegate.
++ \sa barDelegate
++ */
++ /*!
++ \qmlproperty bool BarSeries::labelsVisible
++ The visibility of the labels in a bar series.
++ The default label visibility is \c false.
++
++ \note The default label only displays the value for the set.
++ You can create custom labels with a \l barDelegate.
++
++ \sa barDelegate
++ */
++
++ /*!
++ \property QBarSeries::labelsFormat
++ \brief The format used for showing labels in a bar series.
++
++ QBarSeries supports the following format tag:
++ \table
++ \row
++ \li @value \li The value of the bar
++ \endtable
++
++ For example, the following usage of the format tags would produce labels that show the value
++ followed by the unit (u):
++ \code
++ series->setLabelsFormat("@value u");
++ \endcode
++
++ By default, the labels show the value of the bar. For the percent bar series, \e % is added
++ after the value. The labels are shown on the plot area, if the bars are close to each other,
++ the labels may overlap.
++
++ \sa labelsVisible, labelsPosition, labelsPrecision
++ */
++ /*!
++ \qmlproperty string BarSeries::labelsFormat
++ The format used for showing labels in a bar series.
++
++ \sa QBarSeries::labelsFormat, labelsVisible, labelsPosition
++ */
++
++ /*!
++ \enum QBarSeries::LabelsPosition
++
++ This enum value describes the position of the data value labels:
++
++ \value Center Label is located in the center of the bar.
++ \value InsideEnd Label is located inside the bar at the top.
++ \value InsideBase Label is located inside the bar at the bottom.
++ \value OutsideEnd Label is located outside the bar at the top.
++ */
++
++ /*!
++ \property QBarSeries::labelsPosition
++ \brief The position of value labels.
++
++ \sa labelsVisible, labelsFormat
++ */
++ /*!
++ \qmlproperty enumeration BarSeries::labelsPosition
++
++ The position of the data value labels:
++
++ \value BarSeries.LabelsPosition.Center
++ Label is located in the center of the bar.
++ \value BarSeries.LabelsPosition.InsideEnd
++ Label is located inside the bar at the top.
++ \value BarSeries.LabelsPosition.InsideBase
++ Label is located inside the bar at the bottom.
++ \value BarSeries.LabelsPosition.OutsideEnd
++ Label is located outside the bar at the top.
++
++ \sa labelsVisible, labelsFormat
++ */
++
++ /*!
++ \property QBarSeries::labelsMargin
++ \brief The margin of the value labels in pixels.
++
++ This margin from side is used when \l labelsPosition is set to something else
++ than \c LabelsPosition.Center. The default value is \c 0.
++ */
++ /*!
++ \qmlproperty real BarSeries::labelsMargin
++ The margin of the value labels in pixels.
++
++ This margin from side is used when \l labelsPosition is set to something else
++ than \c LabelsPosition.Center. The default value is \c 0.
++ */
++
++ /*!
++ \property QBarSeries::labelsAngle
++ \brief The angle of the value labels in degrees.
++ */
++ /*!
++ \qmlproperty real BarSeries::labelsAngle
++ The angle of the value labels in degrees.
++ */
++
++ /*!
++ \property QBarSeries::labelsPrecision
++ \brief The maximum amount of significant digits shown in value labels.
++
++ Default value is 6.
++ */
++ /*!
++ \qmlproperty real BarSeries::labelsPrecision
++ The maximum amount of significant digits shown in value labels.
++
++ Default value is 6.
++ */
++
++ /*!
++ \property QBarSeries::barDelegate
++ \brief A custom QML component used for visualizing each of the bars.
++ Instance of this component is created for each of the bar.
++ When this is not defined, a default rectangle visualization for bars is used.
++
++ The dynamic properties available for this component are:
++
++ \table
++ \header
++ \li Type
++ \li Name
++ \li Description
++ \row
++ \li QColor
++ \li barColor
++ \li The fill color of the bar. This value comes either from the \l QGraphsTheme
++ or from \l{QBarSet::color} if the \l QBarSet overrides the color.
++ \row
++ \li QColor
++ \li barBorderColor
++ \li The border color of the bar. This value comes either from the \l QGraphsTheme
++ or from \l{QBarSet::borderColor} if the \l QBarSet overrides the color.
++ \row
++ \li qreal
++ \li barBorderWidth
++ \li The width of the bar border. This value comes either from the \l QGraphsTheme
++ or from \l{QBarSet::borderWidth} if the \l QBarSet overrides the width.
++ \row
++ \li qreal
++ \li barValue
++ \li The value of the bar. This value comes from the \l{QBarSet::values}.
++ \row
++ \li QString
++ \li barLabel
++ \li The label of the bar. This value comes from the \l{QBarSet::label}.
++ \row
++ \li bool
++ \li barSelected
++ \li This value is true when the bar is selected, meaning that the bar index
++ is in \l{QBarSet::selectedBars}.
++ \row
++ \li int
++ \li barIndex
++ \li Index of the bar, from 0 to the amount of bars - 1. [since 6.9]
++ \endtable
++
++ To use any of these, add property with the defined name into your custom component.
++ For example \c{"property color barColor"} and \c{"property real barValue"}.
++ */
++ /*!
++ \qmlproperty Component BarSeries::barDelegate
++ A custom QML component used for visualizing each of the bars.
++ Instance of this component is created for each of the bar.
++ When this is not defined, a default rectangle visualization for bars is used.
++
++ The dynamic properties available for this component are:
++
++ \table
++ \header
++ \li Type
++ \li Name
++ \li Description
++ \row
++ \li color
++ \li barColor
++ \li The fill color of the bar. This value comes either from the \l GraphsTheme
++ or from \l{BarSet::color} if the \l BarSet overrides the color.
++ \row
++ \li color
++ \li barBorderColor
++ \li The border color of the bar. This value comes either from the \l GraphsTheme
++ or from \l{BarSet::borderColor} if the \l BarSet overrides the color.
++ \row
++ \li real
++ \li barBorderWidth
++ \li The width of the bar border. This value comes either from the \l GraphsTheme
++ or from \l{BarSet::borderWidth} if the \l BarSet overrides the width.
++ \row
++ \li real
++ \li barValue
++ \li The value of the bar. This value comes from the \l{BarSet::values}.
++ \row
++ \li string
++ \li barLabel
++ \li The label of the bar. This value comes from the \l{BarSet::label}.
++ \row
++ \li bool
++ \li barSelected
++ \li This value is true when the bar is selected, meaning that the bar index
++ is in \l{BarSet::selectedBars}.
++ \row
++ \li int
++ \li barIndex
++ \li Index of the bar, from 0 to the amount of bars - 1. [since 6.9]
++ \endtable
++
++ To use any of these, add property with the defined name into your custom component.
++ For example \c{"property color barColor"} and \c{"property real barValue"}.
++ */
++
++ /*!
++ \property QBarSeries::barSets
++ \brief A list of sets added to the series.
++ */
++ /*!
++ \qmlproperty list<BarSet> BarSeries::barSets
++ A list of sets added to the series.
++ */
++
++ /*!
++ \fn void QBarSeries::updatedBars()
++ This signal is emitted when bars are updated.
++ */
++
++ /*!
++ \fn void QBarSeries::labelsVisibleChanged(bool visible)
++ This signal is emitted when the labels' visibility changes to \a visible.
++ \sa labelsVisible(), setLabelsVisible()
++ */
++
++ /*!
++ \fn void QBarSeries::barsetsAdded(const QList<QBarSet *> &sets)
++ This signal is emitted when the bar sets specified by \a sets are added to the series.
++ \sa append(), insert()
++ */
++ /*!
++ \qmlsignal BarSeries::barsetsAdded()
++ This signal is emitted when bar sets are added to the series.
++ */
++
++ /*!
++ \fn void QBarSeries::barsetsRemoved(const QList<QBarSet *> &sets)
++ This signal is emitted when the bar sets specified by \a sets are removed from the series.
++ \sa remove()
++ */
++ /*!
++ \qmlsignal BarSeries::barsetsRemoved()
++ This signal is emitted when bar sets are removed from the series.
++ */
++
++ /*!
++ \fn void QBarSeries::setValueChanged(qsizetype index, QBarSet *barset)
++ This signal is emitted when a barset's value is changed. \a index is the index of
++ the barset in the series. The \a barset is a pointer to the changed set.
++ */
++ /*!
++ \fn void QBarSeries::setValueAdded(qsizetype index, qsizetype count, QBarSet *barset)
++ This signal is emitted when a barset's value is changed. \a index is the index of
++ the barset in the series. The number of the added values is indicated \a count.
++ The \a barset is a pointer to the changed set.
++ */
++ /*!
++ \fn void QBarSeries::setValueRemoved(qsizetype index, qsizetype count, QBarSet *barset)
++ This signal is emitted when a barset's value is changed. \a index is the index of
++ the barset in the series. The number of the removed values is indicated \a count.
++ The \a barset is a pointer to the changed set.
++ */
++
++ /*!
++ \qmlmethod BarSet BarSeries::at(int index)
++ Returns the bar set at \a index. Returns null if the index is not valid.
++ */
++
++ /*!
++ \qmlmethod BarSet BarSeries::insert(int index, BarSet barset)
++ Inserts a bar set specified by \a barset to a series at the position specified by \a index.
++ If the set is \c null or already belongs to the series, the set is not
++ appended. Returns \c true if inserting succeeds.
++
++ \sa append()
++ */
++
++ /*!
++ \qmlmethod bool BarSeries::append(BarSet barset)
++
++ Adds a set of bars specified by \a barset to the bar series and takes
++ ownership of it. If the set is null or it already belongs to the series,
++ it will not be appended.
++
++ Returns \c true if appending succeeded.
++
++ \sa insert()
++ */
++
++ /*!
++ \qmlmethod bool BarSeries::remove(BarSet barset)
++ Removes the bar set specified by \a barset from the series. Returns \c true if successful,
++ \c false otherwise.
++ */
++
++ /*!
++ \qmlmethod void BarSeries::clear()
++ Removes all bar sets from the series.
++ */
++
++ /*!
++ \qmlmethod bool BarSeries::replace(int index, BarSet barset)
++ Replaces the bar set at the position specified by \a index from the series and replaces it
++ with \a barset. Returns \c true if successful, \c false otherwise.
++ */
++
++ /*!
++ \qmlmethod Barset BarSeries::at(int index)
++ Returns the bar set specified by \a index from the series. Returns \c null otherwise.
++ */
++
++ /*!
++ \qmlmethod int BarSeries::find(BarSet barset)
++ Returns the index of the bar set specified by \a barset from the series. Returns \c -1 if
++ not found.
++ */
++
++ /*!
++ \qmlmethod void BarSeries::removeMultiple(int index, int count)
++ Removes a range of bar sets as specified by the \a index and \a count. The call
++ traverses over all sets even if removal of one fails.
++ */
++
++ /*!
++ \qmlmethod bool BarSeries::remove(int index)
++ Removes the bar set specified by \a index from the series. Returns \c true if the
++ removal was successful, \c false otherwise.
++ */
++
++ /*!
++ \qmlmethod bool BarSeries::replace(BarSet oldSet, BarSet newSet)
++ Replaces the bar set specified by \a oldSet with newSet. Returns \c true if the
++ removal was successful, \c false otherwise. \a oldSet is destroyed if this
++ is successful.
++ */
++
++ /*!
++ \qmlmethod bool BarSeries::replace(list<BarSet> sets)
++ Completely replaces all current bar set with \a sets. The size does not need
++ to match. Returns false if any of the bar set in \a sets are invalid.
++ */
++
++ /*!
++ \qmlsignal BarSeries::clicked(int index, BarSet barset)
++ This signal is emitted when the user clicks or taps the bar specified by \a index
++ in the bar set specified by \a barset.
++ */
++
++ /*!
++ \qmlsignal BarSeries::doubleClicked(int index, BarSet barset)
++ This signal is emitted when the user double-clicks or double-taps the bar specified
++ by \a index in the bar set specified by \a barset.
++ This signal always occurs after \l clicked.
++ */
++
++ /*!
++ \qmlsignal BarSeries::pressed(int index, BarSet barset)
++ This signal is emitted when the user clicks or taps the bar specified by \a index
++ in the bar set specified by \a barset and holds down the mouse button or gesture.
++ */
++
++ /*!
++ \qmlsignal BarSeries::released(int index, BarSet barset);
++ This signal is emitted when the user releases a previously pressed mouse button
++ or gesture on the bar specified by \a index in the bar set specified by \a barset.
++ */
++
++ /*!
++ \qmlsignal BarSeries::hovered(bool state, int index, BarSet barset)
++ This signal is emitted when a mouse or touch hovers over the bar specified by \a index
++ in the bar set specified by \a barset. \a state is \c true when the cursor enters the
++ bar and \c false when it leaves it.
++ */
++
++ /*!
++ \internal
++ */
++
++ /*!
++ Constructs an empty bar series that is a QObject and a child of \a parent.
++ */
++ QBarSeries::QBarSeries(QObject *parent)
++ : QAbstractSeries(*(new QBarSeriesPrivate()), parent)
++ {}
++
++ QBarSeries::~QBarSeries() {}
++
++ QBarSeries::QBarSeries(QBarSeriesPrivate &dd, QObject *parent)
++ : QAbstractSeries(dd, parent)
++ {}
++
++ /*!
++ Returns the bar series.
++ */
++ QAbstractSeries::SeriesType QBarSeries::type() const
++ {
++ return QAbstractSeries::SeriesType::Bar;
++ }
++
++ /*!
++ \property QBarSeries::seriesColors
++ \brief The list of base colors to be used for all the objects in the series.
++
++ If there are more series than colors, the color list wraps and starts again
++ with the first color in the list. If this is not set (default), colors
++ from the \l{QGraphsTheme::seriesColors} will be used.
++ */
++ /*!
++ \qmlproperty list<color> BarSeries::seriesColors
++ The list of base colors to be used for all the objects in the series.
++
++ If there are more series than colors, the color list wraps and starts again
++ with the first color in the list. If this is not set (default), colors
++ from the \l{GraphsTheme::seriesColors} will be used.
++ */
++ QList<QColor> QBarSeries::seriesColors() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_seriesColors;
++ }
++
++ void QBarSeries::setSeriesColors(const QList<QColor> &newSeriesColors)
++ {
++ Q_D(QBarSeries);
++ for (const QColor &color : newSeriesColors) {
++ if (!color.isValid()) {
++ qCWarning(lcProperties2D, "QBarSeries::setSeriesColors. Tried to use invalid color value.");
++ break;
++ }
++ }
++
++ if (d->m_seriesColors == newSeriesColors) {
++ qCDebug(lcProperties2D) << "QBarSeries::setSeriesColors. Set value of" << newSeriesColors << "is the same as it already was.";
++ return;
++ }
++
++ d->m_seriesColors = newSeriesColors;
++ emit seriesColorsChanged();
++ emit update();
++ }
++
++ /*!
++ \property QBarSeries::borderColors
++ \brief The list of border colors to be used for all the objects in the series.
++
++ If there are more series than colors, the color list wraps and starts again
++ with the first color in the list. If this is not set (default), colors
++ from the \l{QGraphsTheme::borderColors} will be used.
++ */
++ /*!
++ \qmlproperty list<color> BarSeries::borderColors
++ The list of border colors to be used for all the objects in the series.
++
++ If there are more series than colors, the color list wraps and starts again
++ with the first color in the list. If this is not set (default), colors
++ from the \l{GraphsTheme::borderColors} will be used.
++ */
++ QList<QColor> QBarSeries::borderColors() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_borderColors;
++ }
++
++ void QBarSeries::setBorderColors(const QList<QColor> &newBorderColors)
++ {
++ Q_D(QBarSeries);
++ for (const QColor &color : newBorderColors) {
++ if (!color.isValid()) {
++ qCWarning(lcProperties2D, "QBarSeries::setBorderColors. Tried to use invalid color value.");
++ break;
++ }
++ }
++
++ if (d->m_borderColors == newBorderColors) {
++ qCDebug(lcProperties2D) << "QBarSeries::setBorderColors. Set value of:" << newBorderColors << "is the same than it already was.";
++ return;
++ }
++
++ d->m_borderColors = newBorderColors;
++ emit borderColorsChanged();
++ emit update();
++ }
++
++ void QBarSeries::setBarsType(QBarSeries::BarsType type)
++ {
++ Q_D(QBarSeries);
++ if (d->m_barsType != type) {
++ d->m_barsType = type;
++ emit barsTypeChanged(type);
++ emit update();
++ } else {
++ qCDebug(lcProperties2D) << "QBarSeries::setBarsType. Set value of:" << type << "is the same than it already was.";
++ }
++ }
++
++ QBarSeries::BarsType QBarSeries::barsType() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_barsType;
++ }
++
++ /*!
++ Sets the width of the bars of the series to \a width.
++ */
++ void QBarSeries::setBarWidth(qreal width)
++ {
++ Q_D(QBarSeries);
++ if (d->barWidth() != width) {
++ d->setBarWidth(width);
++ emit barWidthChanged();
++ } else {
++ qCDebug(lcProperties2D, "QBarSeries::setBarWidth. Set value of: %f is the same than it already was.",
++ width);
++ }
++ }
++
++ /*!
++ Returns the width of the bars of the series.
++ \sa setBarWidth()
++ */
++ qreal QBarSeries::barWidth() const
++ {
++ Q_D(const QBarSeries);
++ return d->barWidth();
++ }
++
++ /*!
++ Adds a set of bars specified by \a set to the bar series and takes ownership of it. If the set
++ is null or it already belongs to the series, it will not be appended.
++ Returns \c true if appending succeeded.
++ */
++ bool QBarSeries::append(QBarSet *set)
++ {
++ Q_D(QBarSeries);
++ bool success = d->append(set);
++ if (success) {
++ QList<QBarSet *> sets;
++ sets.append(set);
++ set->setParent(this);
++ QObject::connect(set, &QBarSet::update, this, &QBarSeries::update);
++ emit barsetsAdded(sets);
++ emit barSetsChanged();
++ emit countChanged();
++ emit update();
++ }
++ return success;
++ }
++
++ /*!
++ Removes the bar set specified by \a set from the series and permanently deletes it if
++ the removal succeeds. Returns \c true if the set was removed.
++ */
++ bool QBarSeries::remove(QBarSet *set)
++ {
++ Q_D(QBarSeries);
++ bool success = d->remove(set);
++ if (success) {
++ QList<QBarSet *> sets;
++ sets.append(set);
++ set->setParent(0);
++ QObject::disconnect(set, &QBarSet::update, this, &QBarSeries::update);
++ emit barsetsRemoved(sets);
++ emit barSetsChanged();
++ emit countChanged();
++ emit update();
++ delete set;
++ set = 0;
++ }
++ return success;
++ }
++
++ /*!
++ Takes a single \a set from the series. Does not delete the bar set object.
++ \note The series remains the barset's parent object. You must set the
++ parent object to take full ownership.
++
++ Returns \c true if the take operation succeeds.
++ */
++ bool QBarSeries::take(QBarSet *set)
++ {
++ Q_D(QBarSeries);
++ bool success = d->remove(set);
++ if (success) {
++ QList<QBarSet *> sets;
++ sets.append(set);
++ QObject::disconnect(set, &QBarSet::update, this, &QBarSeries::update);
++ emit barsetsRemoved(sets);
++ emit barSetsChanged();
++ emit countChanged();
++ emit update();
++ }
++ return success;
++ }
++
++ /*!
++ Adds a list of bar sets specified by \a sets to a bar series and takes ownership of the sets.
++ Returns \c true if all sets were appended successfully. If any of the sets is null or was
++ previously appended to the series, nothing is appended and this function returns \c false.
++ If any of the sets appears in the list more than once, nothing is appended and this function
++ returns \c false.
++ */
++ bool QBarSeries::append(const QList<QBarSet *> &sets)
++ {
++ Q_D(QBarSeries);
++ if (!d->append(sets))
++ return false;
++
++ for (auto *set : sets) {
++ set->setParent(this);
++ QObject::connect(set, &QBarSet::update, this, &QBarSeries::update);
++ }
++
++ emit barsetsAdded(sets);
++ emit barSetsChanged();
++ emit countChanged();
++ emit update();
++ return true;
++ }
++
++ /*!
++ Inserts a bar set specified by \a set to a series at the position specified by \a index
++ and takes ownership of the set. If the set is null or already belongs to the series, it will
++ not be appended. Returns \c true if inserting succeeds.
++ */
++ bool QBarSeries::insert(qsizetype index, QBarSet *set)
++ {
++ Q_D(QBarSeries);
++ bool success = d->insert(index, set);
++ if (success) {
++ QList<QBarSet *> sets;
++ sets.append(set);
++ QObject::connect(set, &QBarSet::update, this, &QBarSeries::update);
++ emit barsetsAdded(sets);
++ emit barSetsChanged();
++ emit countChanged();
++ emit update();
++ }
++ return success;
++ }
++
++ /*!
++ Removes all bar sets from the series and permanently deletes them.
++ */
++ void QBarSeries::clear()
++ {
++ Q_D(QBarSeries);
++ const QList<QBarSet *> sets = barSets();
++ bool success = d->remove(sets);
++ if (success) {
++ emit barsetsRemoved(sets);
++ emit barSetsChanged();
++ emit countChanged();
++ for (QBarSet *set : sets) {
++ QObject::disconnect(set, &QBarSet::update, this, &QBarSeries::update);
++ // Detaching here prevents a crash in componentComplete if the QBarModelMapper has
++ // already destroyed "set".
++ set->setParent(nullptr);
++ set->deleteLater();
++ }
++ emit update();
++ }
++ }
++
++ /*!
++ Replaces the BarSet which is present at \a index with \a set.
++ The original BarSet will be permanently deleted.
++ */
++ void QBarSeries::replace(qsizetype index, QBarSet *set)
++ {
++ Q_D(QBarSeries);
++
++ if (d->m_barSets.size() <= index)
++ return;
++ if (!set)
++ return;
++ if (index < 0)
++ index = 0;
++
++ remove(d->m_barSets[index]);
++ d->insert(index, set);
++
++ QList<QBarSet *> sets;
++ sets.append(set);
++ QObject::connect(set, &QBarSet::update, this, &QBarSeries::update);
++ emit barsetsReplaced(sets);
++ }
++
++ /*!
++ Retrieves the BarSet specified at the location \a index.
++ Returns null if no BarSet was found.
++ */
++ QBarSet *QBarSeries::at(qsizetype index)
++ {
++ Q_D(QBarSeries);
++
++ if (d->m_barSets.size() <= index)
++ return nullptr;
++ if (index < 0)
++ return nullptr;
++
++ return d->m_barSets[index];
++ }
++
++ /*!
++ Returns the index of the first BarSet found as defined by \a set. Returns -1 if no BarSet was found.
++ */
++ qsizetype QBarSeries::find(QBarSet *set) const
++ {
++ Q_D(const QBarSeries);
++
++ for (qsizetype i = 0; i < d->m_barSets.size(); ++i) {
++ if (set == d->m_barSets[i])
++ return i;
++ }
++
++ return -1;
++ }
++
++ /*!
++ Removes multiple BarSets from the series starting from \a index to a number of \a count.
++ The BarSets will be permanently deleted.
++ */
++ void QBarSeries::removeMultiple(qsizetype index, qsizetype count)
++ {
++ Q_D(QBarSeries);
++
++ if (index < 0 || count < 1 || index + count > d->m_barSets.size())
++ return;
++
++ for (qsizetype i = index; i < index + count; ++i)
++ remove(d->m_barSets[index]);
++ }
++
++ /*!
++ Removes the BarSet at the location \a index. The BarSet will be permanently deleted.
++ */
++ bool QBarSeries::remove(qsizetype index)
++ {
++ Q_D(QBarSeries);
++
++ if (index >= d->m_barSets.size())
++ return false;
++ if (index < 0)
++ return false;
++
++ return remove(d->m_barSets[index]);
++ }
++
++ /*!
++ Replaces the BarSet specified by \a oldValue with the one in \a newValue. The BarSet
++ in \a oldValue will be permanently deleted if found inside the series. Returns \c true if
++ the replace is successful.
++ */
++ bool QBarSeries::replace(QBarSet *oldValue, QBarSet *newValue)
++ {
++ Q_D(QBarSeries);
++
++ if (!oldValue || !newValue)
++ return false;
++ if (oldValue == newValue)
++ return false;
++
++ for (qsizetype i = 0; i < d->m_barSets.size(); ++i) {
++ if (d->m_barSets[i] == oldValue) {
++ remove(d->m_barSets[i]);
++ d->insert(i, newValue);
++
++ QList<QBarSet *> sets;
++ sets.append(newValue);
++ QObject::connect(newValue, &QBarSet::update, this, &QBarSeries::update);
++ emit barsetsReplaced(sets);
++ return true;
++ }
++ }
++
++ return false;
++ }
++
++ /*!
++ Replaces the entire BarSet list inside of this BarSeries with the BarSets specified by \a sets.
++ All of the original BarSets will be permanently deleted. Returns \c true if all BarSets are replaced.
++ */
++ bool QBarSeries::replace(const QList<QBarSet *> &sets)
++ {
++ Q_D(QBarSeries);
++
++ for (const auto set : sets) {
++ if (!set)
++ return false;
++ }
++
++ for (const auto set : std::as_const(d->m_barSets)) {
++ remove(set);
++ }
++
++ for (const auto set : sets) {
++ QObject::connect(set, &QBarSet::update, this, &QBarSeries::update);
++ }
++
++ d->append(sets);
++ emit barsetsReplaced(sets);
++
++ return true;
++ }
++
++ /*!
++ Returns the number of bar sets in a bar series.
++ */
++ qsizetype QBarSeries::count() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_barSets.size();
++ }
++
++ /*!
++ Returns a list of bar sets in a bar series. Keeps the ownership of the bar sets.
++ */
++ QList<QBarSet *> QBarSeries::barSets() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_barSets;
++ }
++
++ /*!
++ Sets the visibility of labels in a bar series to \a visible.
++ */
++ void QBarSeries::setLabelsVisible(bool visible)
++ {
++ Q_D(QBarSeries);
++ if (d->m_labelsVisible != visible) {
++ d->setLabelsVisible(visible);
++ emit labelsVisibleChanged(visible);
++ emit update();
++ } else {
++ qCDebug(lcProperties2D) << "QBarSeries::setLabelsVisible. Label visibility is already set to:" << visible;
++ }
++ }
++
++ /*!
++ Returns the visibility of labels.
++ */
++ bool QBarSeries::labelsVisible() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_labelsVisible;
++ }
++
++ void QBarSeries::setLabelsFormat(const QString &format)
++ {
++ Q_D(QBarSeries);
++ if (d->m_labelsFormat != format) {
++ d->m_labelsFormat = format;
++ d->setLabelsDirty(true);
++ emit labelsFormatChanged(format);
++ emit update();
++ } else {
++ qCDebug(lcProperties2D) << "QBarSeries::setLabelsFormat. Format is already set to value:"
++ << format;
++ }
++ }
++
++ QString QBarSeries::labelsFormat() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_labelsFormat;
++ }
++
++ void QBarSeries::setLabelsMargin(qreal margin)
++ {
++ Q_D(QBarSeries);
++ if (d->m_labelsMargin != margin) {
++ d->m_labelsMargin = margin;
++ d->setLabelsDirty(true);
++ emit labelsMarginChanged(margin);
++ emit update();
++ } else {
++ qCDebug(lcProperties2D, "QBarSeries::setLabelsMargin. Margin is already set to value: %f",
++ margin);
++ }
++ }
++
++ qreal QBarSeries::labelsMargin() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_labelsMargin;
++ }
++
++ void QBarSeries::setLabelsAngle(qreal angle)
++ {
++ Q_D(QBarSeries);
++ if (d->m_labelsAngle != angle) {
++ d->m_labelsAngle = angle;
++ d->setLabelsDirty(true);
++ emit labelsAngleChanged(angle);
++ emit update();
++ } else {
++ qCDebug(lcProperties2D, "QBarSeries::setLabelsAngle. Label angle is already set to value: %f",
++ angle);
++ }
++ }
++
++ qreal QBarSeries::labelsAngle() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_labelsAngle;
++ }
++
++ void QBarSeries::setLabelsPosition(QBarSeries::LabelsPosition position)
++ {
++ Q_D(QBarSeries);
++ if (d->m_labelsPosition != position) {
++ d->m_labelsPosition = position;
++ emit labelsPositionChanged(position);
++ emit update();
++ } else {
++ qCDebug(lcProperties2D) << "QBarSeries::setLabelsPosition. Position is already set to:"
++ << position;
++ }
++ }
++
++ QBarSeries::LabelsPosition QBarSeries::labelsPosition() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_labelsPosition;
++ }
++
++ void QBarSeries::setLabelsPrecision(int precision)
++ {
++ Q_D(QBarSeries);
++ if (d->m_labelsPrecision != precision) {
++ d->m_labelsPrecision = precision;
++ d->setLabelsDirty(true);
++ emit labelsPrecisionChanged(precision);
++ emit update();
++ } else {
++ qCDebug(lcProperties2D, "QBarSeries::setLabelsPrecision. Precision is already set to: %d",
++ precision);
++ }
++ }
++
++ int QBarSeries::labelsPrecision() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_labelsPrecision;
++ }
++
++ QQmlComponent *QBarSeries::barDelegate() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_barDelegate;
++ }
++
++ void QBarSeries::setBarDelegate(QQmlComponent *newBarDelegate)
++ {
++ Q_D(QBarSeries);
++ if (d->m_barDelegate == newBarDelegate) {
++ qCDebug(lcProperties2D) << "QBarSeries::setBarDelegate. BarDelegate is already set to:"
++ << newBarDelegate;
++ return;
++ }
++
++ d->m_barDelegate = newBarDelegate;
++ d->m_barDelegateDirty = true;
++ emit barDelegateChanged();
++ emit update();
++ }
++
++ // Select all the elements in the series
++ void QBarSeries::selectAll()
++ {
++ Q_D(QBarSeries);
++ for (auto s : std::as_const(d->m_barSets)) {
++ s->selectAllBars();
++ }
++ }
++
++ // Deselect all the elements in the series
++ void QBarSeries::deselectAll()
++ {
++ Q_D(QBarSeries);
++ for (auto s : std::as_const(d->m_barSets)) {
++ s->deselectAllBars();
++ }
++ }
++
++ void QBarSeries::componentComplete()
++ {
++ const auto kids = children();
++ for (auto *child : kids) {
++ if (auto bs = qobject_cast<QBarSet *>(child)) {
++ append(bs);
++ qCDebug(lcSeries2D) << "append barset" << bs << "to barseries. barset values:" << bs->values();
++ }
++ }
++
++ qCDebug(lcEvents2D) << "QBarSeries::componentComplete.";
++
++ QAbstractSeries::componentComplete();
++ }
++
++ void QBarSeries::handleSetValueChange(qsizetype index)
++ {
++ QBarSet *set = qobject_cast<QBarSet *>(sender());
++ if (set)
++ emit setValueChanged(index, set);
++ emit update();
++ }
++
++ void QBarSeries::handleSetValueAdd(qsizetype index, qsizetype count)
++ {
++ QBarSet *set = qobject_cast<QBarSet *>(sender());
++ if (set)
++ emit setValueAdded(index, count, set);
++ emit update();
++ }
++
++ void QBarSeries::handleSetValueRemove(qsizetype index, qsizetype count)
++ {
++ QBarSet *set = qobject_cast<QBarSet *>(sender());
++ if (set)
++ emit setValueRemoved(index, count, set);
++ emit update();
++ }
++
++ bool QBarSeries::barDelegateDirty() const
++ {
++ Q_D(const QBarSeries);
++ return d->m_barDelegateDirty;
++ }
++
++ void QBarSeries::setBarDelegateDirty(bool dirty)
++ {
++ Q_D(QBarSeries);
++ d->m_barDelegateDirty = dirty;
++ }
++
++ QBarSeriesPrivate::QBarSeriesPrivate()
++ : QAbstractSeriesPrivate(QAbstractSeries::SeriesType::Bar)
++ , m_barWidth(0.5) // Default value is 50% of category width
++ , m_labelsVisible(false)
++ , m_visible(true)
++ , m_blockBarUpdate(false)
++ , m_labelsFormat()
++ , m_labelsMargin(0)
++ , m_labelsAngle(0)
++ , m_labelsPrecision(6)
++ , m_labelsDirty(true)
++ , m_barDelegateDirty(false)
++ {
++ }
++
++ qsizetype QBarSeriesPrivate::categoryCount() const
++ {
++ // No categories defined. return count of longest set.
++ qsizetype count = 0;
++ for (qsizetype i = 0; i < m_barSets.size(); i++) {
++ if (m_barSets.at(i)->count() > count)
++ count = m_barSets.at(i)->count();
++ }
++
++ return count;
++ }
++
++ void QBarSeriesPrivate::setBarWidth(qreal width)
++ {
++ Q_Q(QBarSeries);
++ width = std::clamp<qreal>(width, 0.0, 1.0);
++ if (!qFuzzyCompare(width + 1, m_barWidth + 1)) {
++ m_barWidth = width;
++ emit q->update();
++ }
++ }
++
++ qreal QBarSeriesPrivate::barWidth() const
++ {
++ return m_barWidth;
++ }
++
++ QBarSet *QBarSeriesPrivate::barsetAt(qsizetype index)
++ {
++ return m_barSets.at(index);
++ }
++
++ void QBarSeriesPrivate::setVisible(bool visible)
++ {
++ Q_Q(QBarSeries);
++ m_visible = visible;
++ emit q->visibleChanged();
++ }
++
++ void QBarSeriesPrivate::setLabelsVisible(bool visible)
++ {
++ m_labelsVisible = visible;
++ }
++
++ qreal QBarSeriesPrivate::min()
++ {
++ if (m_barSets.size() <= 0)
++ return 0;
++
++ qreal min = INT_MAX;
++
++ for (int i = 0; i < m_barSets.size(); i++) {
++ qsizetype categoryCount = m_barSets.at(i)->count();
++ for (qsizetype j = 0; j < categoryCount; j++) {
++ qreal temp = m_barSets.at(i)->at(j);
++ if (temp < min)
++ min = temp;
++ }
++ }
++ return min;
++ }
++
++ qreal QBarSeriesPrivate::max()
++ {
++ if (m_barSets.size() <= 0)
++ return 0;
++
++ qreal max = INT_MIN;
++
++ for (int i = 0; i < m_barSets.size(); i++) {
++ qsizetype categoryCount = m_barSets.at(i)->count();
++ for (qsizetype j = 0; j < categoryCount; j++) {
++ qreal temp = m_barSets.at(i)->at(j);
++ if (temp > max)
++ max = temp;
++ }
++ }
++
++ return max;
++ }
++
++ qreal QBarSeriesPrivate::valueAt(int set, int category)
++ {
++ if ((set < 0) || (set >= m_barSets.size()))
++ return 0; // No set, no value.
++ else if ((category < 0) || (category >= m_barSets.at(set)->count()))
++ return 0; // No category, no value.
++
++ return m_barSets.at(set)->at(category);
++ }
++
++ qreal QBarSeriesPrivate::percentageAt(int set, int category)
++ {
++ if ((set < 0) || (set >= m_barSets.size()))
++ return 0; // No set, no value.
++ else if ((category < 0) || (category >= m_barSets.at(set)->count()))
++ return 0; // No category, no value.
++
++ qreal value = m_barSets.at(set)->at(category);
++ qreal sum = categorySum(category);
++ if (qFuzzyIsNull(sum))
++ return 0;
++
++ return value / sum;
++ }
++
++ qreal QBarSeriesPrivate::categorySum(qsizetype category)
++ {
++ qreal sum(0);
++ qsizetype count = m_barSets.size(); // Count sets
++ for (qsizetype set = 0; set < count; set++) {
++ if (category < m_barSets.at(set)->count())
++ sum += m_barSets.at(set)->at(category);
++ }
++ return sum;
++ }
++
++ qreal QBarSeriesPrivate::absoluteCategorySum(int category)
++ {
++ qreal sum(0);
++ qsizetype count = m_barSets.size(); // Count sets
++ for (qsizetype set = 0; set < count; set++) {
++ if (category < m_barSets.at(set)->count())
++ sum += qAbs(m_barSets.at(set)->at(category));
++ }
++ return sum;
++ }
++
++ qreal QBarSeriesPrivate::maxCategorySum()
++ {
++ qreal max = INT_MIN;
++ qsizetype count = categoryCount();
++ for (qsizetype i = 0; i < count; i++) {
++ qreal sum = categorySum(i);
++ if (sum > max)
++ max = sum;
++ }
++ return max;
++ }
++
++ qreal QBarSeriesPrivate::minX()
++ {
++ if (m_barSets.size() <= 0)
++ return 0;
++
++ qreal min = INT_MAX;
++
++ for (int i = 0; i < m_barSets.size(); i++) {
++ qsizetype categoryCount = m_barSets.at(i)->count();
++ for (qsizetype j = 0; j < categoryCount; j++) {
++ qreal temp = m_barSets.at(i)->d_func()->m_values.at(j).x();
++ if (temp < min)
++ min = temp;
++ }
++ }
++ return min;
++ }
++
++ qreal QBarSeriesPrivate::maxX()
++ {
++ if (m_barSets.size() <= 0)
++ return 0;
++
++ qreal max = INT_MIN;
++
++ for (int i = 0; i < m_barSets.size(); i++) {
++ qsizetype categoryCount = m_barSets.at(i)->count();
++ for (qsizetype j = 0; j < categoryCount; j++) {
++ qreal temp = m_barSets.at(i)->d_func()->m_values.at(j).x();
++ if (temp > max)
++ max = temp;
++ }
++ }
++
++ return max;
++ }
++
++ qreal QBarSeriesPrivate::categoryTop(qsizetype category)
++ {
++ // Returns top (sum of all positive values) of category.
++ // Returns 0, if all values are negative
++ qreal top(0);
++ qsizetype count = m_barSets.size();
++ for (qsizetype set = 0; set < count; set++) {
++ if (category < m_barSets.at(set)->count()) {
++ qreal temp = m_barSets.at(set)->at(category);
++ if (temp > 0) {
++ top += temp;
++ }
++ }
++ }
++ return top;
++ }
++
++ qreal QBarSeriesPrivate::categoryBottom(qsizetype category)
++ {
++ // Returns bottom (sum of all negative values) of category
++ // Returns 0, if all values are positive
++ qreal bottom(0);
++ qsizetype count = m_barSets.size();
++ for (qsizetype set = 0; set < count; set++) {
++ if (category < m_barSets.at(set)->count()) {
++ qreal temp = m_barSets.at(set)->at(category);
++ if (temp < 0) {
++ bottom += temp;
++ }
++ }
++ }
++ return bottom;
++ }
++ qreal QBarSeriesPrivate::top()
++ {
++ // Returns top of all categories
++ qreal top(0);
++ qsizetype count = categoryCount();
++ for (qsizetype i = 0; i < count; i++) {
++ qreal temp = categoryTop(i);
++ if (temp > top)
++ top = temp;
++ }
++ return top;
++ }
++
++ qreal QBarSeriesPrivate::bottom()
++ {
++ // Returns bottom of all categories
++ qreal bottom(0);
++ qsizetype count = categoryCount();
++ for (qsizetype i = 0; i < count; i++) {
++ qreal temp = categoryBottom(i);
++ if (temp < bottom)
++ bottom = temp;
++ }
++ return bottom;
++ }
++
++ bool QBarSeriesPrivate::blockBarUpdate()
++ {
++ return m_blockBarUpdate;
++ }
++
++ bool QBarSeriesPrivate::append(QBarSet *set)
++ {
++ if ((m_barSets.contains(set)) || (set == 0))
++ return false; // Fail if set is already in list or set is null.
++
++ m_barSets.append(set);
++ Q_Q(QBarSeries);
++ QObject::connect(set, &QBarSet::updatedBars, q, &QBarSeries::updatedBars);
++ QObject::connect(set, &QBarSet::valueChanged, q, &QBarSeries::handleSetValueChange);
++ QObject::connect(set, &QBarSet::valueAdded, q, &QBarSeries::handleSetValueAdd);
++ QObject::connect(set, &QBarSet::valueRemoved, q, &QBarSeries::handleSetValueRemove);
++ QObject::connect(set, &QBarSet::selectedBarsChanged, q, &QBarSeries::updatedBars);
++
++ return true;
++ }
++
++ bool QBarSeriesPrivate::remove(QBarSet *set)
++ {
++ if (!m_barSets.contains(set))
++ return false; // Fail if set is not in list
++
++ m_barSets.removeOne(set);
++ Q_Q(QBarSeries);
++ QObject::disconnect(set, &QBarSet::updatedBars, q, &QBarSeries::updatedBars);
++ QObject::disconnect(set, &QBarSet::valueChanged, q, &QBarSeries::handleSetValueChange);
++ QObject::disconnect(set, &QBarSet::valueAdded, q, &QBarSeries::handleSetValueAdd);
++ QObject::disconnect(set, &QBarSet::valueRemoved, q, &QBarSeries::handleSetValueRemove);
++ QObject::disconnect(set, &QBarSet::selectedBarsChanged, q, &QBarSeries::updatedBars);
++
++ return true;
++ }
++
++ bool QBarSeriesPrivate::append(const QList<QBarSet *> &sets)
++ {
++ for (auto *set : sets) {
++ if ((set == 0) || (m_barSets.contains(set)))
++ return false; // Fail if any of the sets is null or is already appended.
++ if (sets.count(set) != 1)
++ return false; // Also fail if same set is more than once in given list.
++ }
++
++ Q_Q(QBarSeries);
++ for (auto *set : sets) {
++ m_barSets.append(set);
++ QObject::connect(set, &QBarSet::updatedBars, q, &QBarSeries::updatedBars);
++ QObject::connect(set, &QBarSet::valueChanged, q, &QBarSeries::handleSetValueChange);
++ QObject::connect(set, &QBarSet::valueAdded, q, &QBarSeries::handleSetValueAdd);
++ QObject::connect(set, &QBarSet::valueRemoved, q, &QBarSeries::handleSetValueRemove);
++ QObject::connect(set, &QBarSet::selectedBarsChanged, q, &QBarSeries::updatedBars);
++ }
++
++ return true;
++ }
++
++ bool QBarSeriesPrivate::remove(const QList<QBarSet *> &sets)
++ {
++ if (sets.size() == 0)
++ return false;
++
++ for (QBarSet *set : sets) {
++ if ((set == 0) || (!m_barSets.contains(set)))
++ return false; // Fail if any of the sets is null or is not in series
++ if (sets.count(set) != 1)
++ return false; // Also fail if same set is more than once in given list.
++ }
++
++ Q_Q(QBarSeries);
++ for (QBarSet *set : sets) {
++ m_barSets.removeOne(set);
++ QObject::disconnect(set, &QBarSet::updatedBars, q, &QBarSeries::updatedBars);
++ QObject::disconnect(set,
++ &QBarSet::valueChanged,
++ q,
++ &QBarSeries::handleSetValueChange);
++ QObject::disconnect(set, &QBarSet::valueAdded, q, &QBarSeries::handleSetValueAdd);
++ QObject::disconnect(set,
++ &QBarSet::valueRemoved,
++ q,
++ &QBarSeries::handleSetValueRemove);
++ QObject::disconnect(set, &QBarSet::selectedBarsChanged, q, &QBarSeries::updatedBars);
++ }
++
++ return true;
++ }
++
++ bool QBarSeriesPrivate::insert(qsizetype index, QBarSet *set)
++ {
++ if ((m_barSets.contains(set)) || (set == 0))
++ return false; // Fail if set is already in list or set is null.
++
++ m_barSets.insert(index, set);
++ Q_Q(QBarSeries);
++ QObject::connect(set, &QBarSet::updatedBars, q, &QBarSeries::updatedBars);
++ QObject::connect(set, &QBarSet::valueChanged, q, &QBarSeries::handleSetValueChange);
++ QObject::connect(set, &QBarSet::valueAdded, q, &QBarSeries::handleSetValueAdd);
++ QObject::connect(set, &QBarSet::valueRemoved, q, &QBarSeries::handleSetValueRemove);
++ QObject::disconnect(set, &QBarSet::selectedBarsChanged, q, &QBarSeries::updatedBars);
++
++ return true;
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qbarseries.cpp"