diff options
Diffstat (limited to 'src/datavisualization/data/qbardataproxy.cpp')
-rw-r--r-- | src/datavisualization/data/qbardataproxy.cpp | 707 |
1 files changed, 707 insertions, 0 deletions
diff --git a/src/datavisualization/data/qbardataproxy.cpp b/src/datavisualization/data/qbardataproxy.cpp new file mode 100644 index 00000000..2ec38980 --- /dev/null +++ b/src/datavisualization/data/qbardataproxy.cpp @@ -0,0 +1,707 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "qbardataproxy.h" +#include "qbardataproxy_p.h" + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +/*! + * \class QBarDataProxy + * \inmodule QtDataVisualization + * \brief Base proxy class for Q3DBars. + * \since 1.0.0 + * + * QBarDataProxy handles adding, inserting, changing and removing rows of data. + * + * The data array is a list of vectors (rows) of QBarDataItem instances. + * Each row can contain different amount of items or even be null. + * + * QBarDataProxy takes ownership of all QBarDataRows passed to it, whether directly or + * in a QBarDataArray container. + * If you use QBarDataRow pointers to directly modify data after adding the array to the proxy, + * you must also emit proper signal to make the graph update. + * + * QBarDataProxy optionally keeps track of row and column labels, which Q3DCategoryAxis can utilize + * to show axis labels. The row and column labels are stored in separate array from the data and + * row manipulation methods provide an alternate versions that don't affect the row labels. + * This enables the option of having row labels that relate to the position of the data in the + * array rather than the data itself. + * + * QBarDataProxy supports the following format tags for QAbstractDataProxy::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 + * \row + * \li @colIdx \li Visible Column index + * \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 same format the value axis attached to the graph uses, + * see \l{Q3DValueAxis::setLabelFormat()} for more information. + * \row + * \li %<format spec> \li Item value in specified format. + * \endtable + * + * For example: + * \snippet doc_src_qtdatavisualization.cpp 1 + * + * \sa {Qt Data Visualization Data Handling} + */ + +/*! + * \qmltype BarDataProxy + * \inqmlmodule com.digia.QtDataVisualization 1.0 + * \since com.digia.QtDataVisualization 1.0 + * \ingroup datavisualization_qml + * \instantiates QBarDataProxy + * \inherits AbstractDataProxy + * \brief Base proxy type for Bars3D. + * + * This type handles adding, inserting, changing and removing rows of data with Qt Quick 2. + * + * This type is uncreatable, but contains properties that are exposed via subtypes. + * + * For more complete description, see QBarDataProxy. + * + * \sa ItemModelBarDataProxy, {Qt Data Visualization Data Handling} + */ + +/*! + * \qmlproperty int BarDataProxy::rowCount + * Row count in the array. + */ + +/*! + * \qmlproperty list BarDataProxy::rowLabels + * + * Optional row labels for the array. Indexes in this array match row indexes in data array. + * If the list is shorter than row count, all rows will not get labels. + */ + +/*! + * \qmlproperty list BarDataProxy::columnLabels + * + * 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. + */ + +/*! + * Constructs QBarDataProxy with the given \a parent. + */ +QBarDataProxy::QBarDataProxy(QObject *parent) : + QAbstractDataProxy(new QBarDataProxyPrivate(this), parent) +{ +} + +/*! + * \internal + */ +QBarDataProxy::QBarDataProxy(QBarDataProxyPrivate *d, QObject *parent) : + QAbstractDataProxy(d, parent) +{ +} + +/*! + * Destroys QBarDataProxy. + */ +QBarDataProxy::~QBarDataProxy() +{ +} + +/*! + * Clears the existing array and row and column labels. + */ +void QBarDataProxy::resetArray() +{ + resetArray(0, QStringList(), QStringList()); +} + +/*! + * Takes ownership of the \a newArray. Clears the existing array and if the \a newArray is + * different than the existing array. If it's the same array, this just triggers arrayReset() + * signal. + * Passing null array deletes the old array and creates a new empty array. + * Row and column labels are not affected. + */ +void QBarDataProxy::resetArray(QBarDataArray *newArray) +{ + dptr()->resetArray(newArray, 0, 0); + emit arrayReset(); +} + +/*! + * Takes ownership of the \a newArray. Clears the existing array and if the \a newArray is + * different than the existing array. If it's the same array, this just triggers arrayReset() + * signal. + * Passing null array deletes the old array and creates a new empty array. + * The \a rowLabels and \a columnLabels lists specify the new labels for rows and columns. + */ +void QBarDataProxy::resetArray(QBarDataArray *newArray, const QStringList &rowLabels, + const QStringList &columnLabels) +{ + dptr()->resetArray(newArray, &rowLabels, &columnLabels); + emit arrayReset(); +} + +/*! + * Changes existing rows by replacing a row at \a rowIndex with \a row. The \a row can be + * the same as the existing row already stored at the \a rowIndex. + * Existing row labels are not affected. + */ +void QBarDataProxy::setRow(int rowIndex, QBarDataRow *row) +{ + dptr()->setRow(rowIndex, row, 0); + emit rowsChanged(rowIndex, 1); +} + +/*! + * Changes existing rows by replacing a row at \a rowIndex with \a row. The \a row can be + * the same as the existing row already stored at the \a rowIndex. + * Changes the row label to the \a label. + */ +void QBarDataProxy::setRow(int rowIndex, QBarDataRow *row, const QString &label) +{ + dptr()->setRow(rowIndex, row, &label); + emit rowsChanged(rowIndex, 1); +} + +/*! + * Changes existing rows by replacing a rows starting at \a rowIndex with \a rows. + * Existing row labels are not affected. The rows in the \a rows array can be + * the same as the existing rows already stored at the \a rowIndex. + */ +void QBarDataProxy::setRows(int rowIndex, const QBarDataArray &rows) +{ + dptr()->setRows(rowIndex, rows, 0); + emit rowsChanged(rowIndex, rows.size()); +} + +/*! + * Changes existing rows by replacing a rows starting at \a rowIndex with \a rows. + * The row labels are changed to \a labels. The rows in the \a rows array can be + * the same as the existing rows already stored at the \a rowIndex. + */ +void QBarDataProxy::setRows(int rowIndex, const QBarDataArray &rows, const QStringList &labels) +{ + dptr()->setRows(rowIndex, rows, &labels); + emit rowsChanged(rowIndex, rows.size()); +} + +/*! + * Changes a single item at \a rowIndex, \a columnIndex to the \a item. + */ +void QBarDataProxy::setItem(int rowIndex, int columnIndex, const QBarDataItem &item) +{ + dptr()->setItem(rowIndex, columnIndex, item); + emit itemChanged(rowIndex, columnIndex); +} + +/*! + * Adds a new \a row to the end of array. + * Existing row labels are not affected. + * + * \return index of the added row. + */ +int QBarDataProxy::addRow(QBarDataRow *row) +{ + int addIndex = dptr()->addRow(row, 0); + emit rowsAdded(addIndex, 1); + return addIndex; +} + +/*! + * Adds a new \a row with the \a label to the end of array. + * + * \return index of the added row. + */ +int QBarDataProxy::addRow(QBarDataRow *row, const QString &label) +{ + int addIndex = dptr()->addRow(row, &label); + emit rowsAdded(addIndex, 1); + return addIndex; +} + +/*! + * Adds new \a rows to the end of array. + * Existing row labels are not affected. + * + * \return index of the first added row. + */ +int QBarDataProxy::addRows(const QBarDataArray &rows) +{ + int addIndex = dptr()->addRows(rows, 0); + emit rowsAdded(addIndex, rows.size()); + return addIndex; +} + +/*! + * Adds new \a rows with \a labels to the end of array. + * + * \return index of the first added row. + */ +int QBarDataProxy::addRows(const QBarDataArray &rows, const QStringList &labels) +{ + int addIndex = dptr()->addRows(rows, &labels); + emit rowsAdded(addIndex, rows.size()); + return addIndex; +} + +/*! + * Inserts a new \a row into \a rowIndex. + * If rowIndex is equal to array size, rows are added to end of the array. + * Any existing row labels are not affected. + * \note Row labels array will be out of sync with row array after this call, + * if there were labeled rows beyond the inserted row. + */ +void QBarDataProxy::insertRow(int rowIndex, QBarDataRow *row) +{ + dptr()->insertRow(rowIndex, row, 0); + emit rowsInserted(rowIndex, 1); +} + +/*! + * Inserts a new \a row with the \a label into \a rowIndex. + * If rowIndex is equal to array size, rows are added to end of the array. + */ +void QBarDataProxy::insertRow(int rowIndex, QBarDataRow *row, const QString &label) +{ + dptr()->insertRow(rowIndex, row, &label); + emit rowsInserted(rowIndex, 1); +} + +/*! + * Inserts new \a rows into \a rowIndex. + * If rowIndex is equal to array size, rows are added to end of the array. + * Any existing row labels are not affected. + * \note Row labels array will be out of sync with row array after this call, + * if there were labeled rows beyond the inserted rows. + */ +void QBarDataProxy::insertRows(int rowIndex, const QBarDataArray &rows) +{ + dptr()->insertRows(rowIndex, rows, 0); + emit rowsInserted(rowIndex, rows.size()); +} + +/*! + * Inserts new \a rows with \a labels into \a rowIndex. + * If rowIndex is equal to array size, rows are added to end of the array. + */ +void QBarDataProxy::insertRows(int rowIndex, const QBarDataArray &rows, const QStringList &labels) +{ + dptr()->insertRows(rowIndex, rows, &labels); + emit rowsInserted(rowIndex, rows.size()); +} + +/*! + * Removes \a removeCount rows staring at \a rowIndex. Attempting to remove rows past the end of the + * array does nothing. If \a removeLabels is true, corresponding row labels are also removed. Otherwise + * the row labels are not affected. + * \note If \a removeLabels is false, the row labels array will be out of sync with the row array + * if there are labeled rows beyond the removed rows. + */ +void QBarDataProxy::removeRows(int rowIndex, int removeCount, bool removeLabels) +{ + if (rowIndex < rowCount() && removeCount >= 1) { + dptr()->removeRows(rowIndex, removeCount, removeLabels); + emit rowsRemoved(rowIndex, removeCount); + } +} + +/*! + * \property QBarDataProxy::rowCount + * + * Row count in the array. + */ +int QBarDataProxy::rowCount() const +{ + return dptrc()->m_dataArray->size(); +} + +/*! + * \property QBarDataProxy::rowLabels + * + * Optional row labels for the array. Indexes in this array match row indexes in data array. + * If the list is shorter than row count, all rows will not get labels. + */ +QStringList QBarDataProxy::rowLabels() const +{ + return dptrc()->m_rowLabels; +} + +void QBarDataProxy::setRowLabels(const QStringList &labels) +{ + if (dptr()->m_rowLabels != labels) { + dptr()->m_rowLabels = labels; + emit rowLabelsChanged(); + } +} + +/*! + * \property QBarDataProxy::columnLabels + * + * 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 QBarDataProxy::columnLabels() const +{ + return dptrc()->m_columnLabels; +} + +void QBarDataProxy::setColumnLabels(const QStringList &labels) +{ + if (dptr()->m_columnLabels != labels) { + dptr()->m_columnLabels = labels; + emit columnLabelsChanged(); + } +} + +/*! + * \return pointer to the data array. + */ +const QBarDataArray *QBarDataProxy::array() const +{ + return dptrc()->m_dataArray; +} + +/*! + * \return pointer to the row at \a rowIndex. It is guaranteed to be valid only until next call + * that modifies data. + */ +const QBarDataRow *QBarDataProxy::rowAt(int rowIndex) const +{ + const QBarDataArray &dataArray = *dptrc()->m_dataArray; + Q_ASSERT(rowIndex >= 0 && rowIndex < dataArray.size()); + return dataArray[rowIndex]; +} + +/*! + * \return pointer to the item at \a rowIndex, \a columnIndex. It is guaranteed to be valid only + * until next call that modifies data. + */ +const QBarDataItem *QBarDataProxy::itemAt(int rowIndex, int columnIndex) const +{ + const QBarDataArray &dataArray = *dptrc()->m_dataArray; + Q_ASSERT(rowIndex >= 0 && rowIndex < dataArray.size()); + const QBarDataRow &dataRow = *dataArray[rowIndex]; + Q_ASSERT(columnIndex >= 0 && columnIndex < dataRow.size()); + return &dataRow.at(columnIndex); +} + +/*! + * \internal + */ +QBarDataProxyPrivate *QBarDataProxy::dptr() +{ + return static_cast<QBarDataProxyPrivate *>(d_ptr.data()); +} + +/*! + * \internal + */ +const QBarDataProxyPrivate *QBarDataProxy::dptrc() const +{ + return static_cast<const QBarDataProxyPrivate *>(d_ptr.data()); +} + +/*! + * \fn void QBarDataProxy::arrayReset() + * + * Emitted when data array is reset. + * If you change the whole array contents without calling resetArray(), you need to + * emit this signal yourself or the graph won't get updated. + */ + +/*! + * \fn void QBarDataProxy::rowsAdded(int startIndex, int count) + * + * Emitted when rows have been added. Provides \a startIndex and \a count of rows added. + * If you add rows directly to the array without calling addRow() or addRows(), you + * need to emit this signal yourself or the graph won't get updated. + */ + +/*! + * \fn void QBarDataProxy::rowsChanged(int startIndex, int count) + * + * Emitted when rows have changed. Provides \a startIndex and \a count of changed rows. + * If you change rows directly in the array without calling setRow() or setRows(), you + * need to emit this signal yourself or the graph won't get updated. + */ + +/*! + * \fn void QBarDataProxy::rowsRemoved(int startIndex, int count) + * + * Emitted when rows have been removed. Provides \a startIndex and \a count of rows removed. + * Index is the current array size if rows were removed from the end of the array. + * If you remove rows directly from the array without calling removeRows(), you + * need to emit this signal yourself or the graph won't get updated. + */ + +/*! + * \fn void QBarDataProxy::rowsInserted(int startIndex, int count) + * + * Emitted when rows have been inserted. Provides \a startIndex and \a count of inserted rows. + * If you insert rows directly into the array without calling insertRow() or insertRows(), you + * need to emit this signal yourself or the graph won't get updated. + */ + +/*! + * \fn void QBarDataProxy::itemChanged(int rowIndex, int columnIndex) + * + * Emitted when an item has changed. Provides \a rowIndex and \a columnIndex of changed item. + * If you change an item directly in the array without calling setItem(), you + * need to emit this signal yourself or the graph won't get updated. + */ + +// QBarDataProxyPrivate + +QBarDataProxyPrivate::QBarDataProxyPrivate(QBarDataProxy *q) + : QAbstractDataProxyPrivate(q, QAbstractDataProxy::DataTypeBar), + m_dataArray(new QBarDataArray) +{ + m_itemLabelFormat = QStringLiteral("@valueTitle: @valueLabel"); +} + +QBarDataProxyPrivate::~QBarDataProxyPrivate() +{ + clearArray(); +} + +void QBarDataProxyPrivate::resetArray(QBarDataArray *newArray, const QStringList *rowLabels, + const QStringList *columnLabels) +{ + if (rowLabels) + qptr()->setRowLabels(*rowLabels); + if (columnLabels) + qptr()->setColumnLabels(*columnLabels); + + if (!newArray) + newArray = new QBarDataArray; + + if (newArray != m_dataArray) { + clearArray(); + m_dataArray = newArray; + } +} + +void QBarDataProxyPrivate::setRow(int rowIndex, QBarDataRow *row, const QString *label) +{ + Q_ASSERT(rowIndex >= 0 && rowIndex < m_dataArray->size()); + + if (label) + fixRowLabels(rowIndex, 1, QStringList(*label), false); + if (row != m_dataArray->at(rowIndex)) { + clearRow(rowIndex); + (*m_dataArray)[rowIndex] = row; + } +} + +void QBarDataProxyPrivate::setRows(int rowIndex, const QBarDataArray &rows, const QStringList *labels) +{ + QBarDataArray &dataArray = *m_dataArray; + Q_ASSERT(rowIndex >= 0 && (rowIndex + rows.size()) <= dataArray.size()); + if (labels) + fixRowLabels(rowIndex, rows.size(), *labels, false); + for (int i = 0; i < rows.size(); i++) { + if (rows.at(i) != dataArray.at(rowIndex)) { + clearRow(rowIndex); + dataArray[rowIndex] = rows.at(i); + } + rowIndex++; + } +} + +void QBarDataProxyPrivate::setItem(int rowIndex, int columnIndex, const QBarDataItem &item) +{ + Q_ASSERT(rowIndex >= 0 && rowIndex < m_dataArray->size()); + QBarDataRow &row = *(*m_dataArray)[rowIndex]; + Q_ASSERT(columnIndex < row.size()); + row[columnIndex] = item; +} + +int QBarDataProxyPrivate::addRow(QBarDataRow *row, const QString *label) +{ + int currentSize = m_dataArray->size(); + if (label) + fixRowLabels(currentSize, 1, QStringList(*label), false); + m_dataArray->append(row); + return currentSize; +} + +int QBarDataProxyPrivate::addRows(const QBarDataArray &rows, const QStringList *labels) +{ + int currentSize = m_dataArray->size(); + if (labels) + fixRowLabels(currentSize, rows.size(), *labels, false); + for (int i = 0; i < rows.size(); i++) + m_dataArray->append(rows.at(i)); + return currentSize; +} + +void QBarDataProxyPrivate::insertRow(int rowIndex, QBarDataRow *row, const QString *label) +{ + Q_ASSERT(rowIndex >= 0 && rowIndex <= m_dataArray->size()); + if (label) + fixRowLabels(rowIndex, 1, QStringList(*label), true); + m_dataArray->insert(rowIndex, row); +} + +void QBarDataProxyPrivate::insertRows(int rowIndex, const QBarDataArray &rows, const QStringList *labels) +{ + Q_ASSERT(rowIndex >= 0 && rowIndex <= m_dataArray->size()); + if (labels) + fixRowLabels(rowIndex, rows.size(), *labels, true); + for (int i = 0; i < rows.size(); i++) + m_dataArray->insert(rowIndex++, rows.at(i)); +} + +void QBarDataProxyPrivate::removeRows(int rowIndex, int removeCount, bool removeLabels) +{ + Q_ASSERT(rowIndex >= 0); + int maxRemoveCount = m_dataArray->size() - rowIndex; + removeCount = qMin(removeCount, maxRemoveCount); + bool labelsChanged = false; + for (int i = 0; i < removeCount; i++) { + clearRow(rowIndex); + m_dataArray->removeAt(rowIndex); + if (removeLabels && m_rowLabels.size() > rowIndex) { + m_rowLabels.removeAt(rowIndex); + labelsChanged = true; + } + } + if (labelsChanged) + emit qptr()->rowLabelsChanged(); +} + +QBarDataProxy *QBarDataProxyPrivate::qptr() +{ + return static_cast<QBarDataProxy *>(q_ptr); +} + +void QBarDataProxyPrivate::clearRow(int rowIndex) +{ + if (m_dataArray->at(rowIndex)) { + delete m_dataArray->at(rowIndex); + (*m_dataArray)[rowIndex] = 0; + } +} + +void QBarDataProxyPrivate::clearArray() +{ + for (int i = 0; i < m_dataArray->size(); i++) + clearRow(i); + m_dataArray->clear(); + delete m_dataArray; +} + +/*! + * \internal + * Fixes the row label array to include specified labels. + */ +void QBarDataProxyPrivate::fixRowLabels(int startIndex, int count, const QStringList &newLabels, bool isInsert) +{ + bool changed = false; + int currentSize = m_rowLabels.size(); + + int newSize = newLabels.size(); + if (startIndex >= currentSize) { + // Adding labels past old label array, create empty strings to fill intervening space + if (newSize) { + for (int 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) { + int insertIndex = startIndex; + if (count) + changed = true; + for (int 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 + int lastChangeIndex = count + startIndex; + int newIndex = 0; + for (int 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) + emit qptr()->rowLabelsChanged(); +} + +QPair<GLfloat, GLfloat> QBarDataProxyPrivate::limitValues(int startRow, int endRow, + int startColumn, int endColumn) const +{ + QPair<GLfloat, GLfloat> limits = qMakePair(0.0f, 0.0f); + endRow = qMin(endRow, m_dataArray->size() - 1); + for (int i = startRow; i <= endRow; i++) { + QBarDataRow *row = m_dataArray->at(i); + if (row) { + endColumn = qMin(endColumn, row->size() - 1); + for (int j = startColumn; j <= endColumn; j++) { + const QBarDataItem &item = m_dataArray->at(i)->at(j); + qreal itemValue = item.value(); + if (limits.second < itemValue) + limits.second = itemValue; + if (limits.first > itemValue) + limits.first = itemValue; + } + } + } + return limits; +} + +QT_DATAVISUALIZATION_END_NAMESPACE |