Revision control

Copy as Markdown

// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include <QtQuickTemplates2/private/qquickheaderview_p_p.h>
#include <algorithm>
/*!
\qmltype HorizontalHeaderView
\inqmlmodule QtQuick.Controls
\ingroup qtquickcontrols-containers
\inherits TableView
\brief Provides a horizontal header view to accompany a \l TableView.
\include qquickheaderview.qdocinc {detailed-description} {HorizontalHeaderView}
\sa VerticalHeaderView
*/
/*!
\qmltype VerticalHeaderView
\inqmlmodule QtQuick.Controls
\ingroup qtquickcontrols-containers
\inherits TableView
\brief Offers a vertical header view to accompany a \l TableView.
\include qquickheaderview.qdocinc {detailed-description} {VerticalHeaderView}
\sa HorizontalHeaderView
*/
/*!
\qmlproperty TableView QtQuick.Controls::HorizontalHeaderView::syncView
\include qquickheaderview.qdocinc {syncView} {horizontally}
*/
/*!
\qmlproperty TableView QtQuick.Controls::VerticalHeaderView::syncView
\include qquickheaderview.qdocinc {syncView} {vertically}
*/
/*!
\qmlproperty QVariant QtQuick.Controls::HorizontalHeaderView::model
\include qquickheaderview.qdocinc {model} {horizontal}
*/
/*!
\qmlproperty QVariant QtQuick.Controls::VerticalHeaderView::model
\include qquickheaderview.qdocinc {model} {vertical}
*/
/*!
\qmlproperty string QtQuick.Controls::HorizontalHeaderView::textRole
\include qquickheaderview.qdocinc {textRole}
*/
/*!
\qmlproperty string QtQuick.Controls::VerticalHeaderView::textRole
\include qquickheaderview.qdocinc {textRole}
*/
/*!
\qmlproperty bool QtQuick.Controls::HorizontalHeaderView::movableColumns
\since 6.8
\include qquickheaderview.qdocinc {movableColumns}
*/
/*!
\qmlproperty bool QtQuick.Controls::VerticalHeaderView::movableRows
\since 6.8
\include qquickheaderview.qdocinc {movableRows}
*/
/*!
\qmlproperty bool QtQuick.Controls::HorizontalHeaderView::showSortIndicator
\since 6.13
\include qquickheaderview.qdocinc {showSortIndicator}
*/
/*!
\qmlproperty bool QtQuick.Controls::HorizontalHeaderView::sortIndicatorClearable
\since 6.13
\include qquickheaderview.qdocinc {sortIndicatorClearable}
*/
/*!
\qmlsignal void QtQuick.Controls::HorizontalHeaderView::headerClicked(int logicalColumn)
\since 6.13
\include qquickheaderview.qdocinc {headerClicked}
*/
QT_BEGIN_NAMESPACE
static const char *kRequiredProperty_headerView = "headerView";
static const char *kRequiredProperty_model = "model";
QQuickHeaderViewBasePrivate::QQuickHeaderViewBasePrivate()
: QQuickTableViewPrivate()
{
}
QQuickHeaderViewBasePrivate::~QQuickHeaderViewBasePrivate()
{
}
void QQuickHeaderViewBasePrivate::init()
{
Q_Q(QQuickHeaderViewBase);
m_headerDataProxyModel.m_headerView = q;
setSizePolicy(orientation() == Qt::Horizontal ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed,
orientation() == Qt::Horizontal ? QLayoutPolicy::Fixed : QLayoutPolicy::Preferred);
q->setSyncDirection(orientation());
}
const QPointer<QQuickItem> QQuickHeaderViewBasePrivate::delegateItemAt(int row, int col) const
{
return loadedTableItem(QPoint(col, row))->item;
}
QVariant QQuickHeaderViewBasePrivate::modelImpl() const
{
// modelImpl is supposed to return the actual model that is
// is used (that is, the proxy model), and not the source model.
if (m_headerDataProxyModel.sourceModel())
return QVariant::fromValue(&m_headerDataProxyModel);
// If we're not using the proxy model, use the assigned model
return QQuickTableViewPrivate::modelImpl();
}
void QQuickHeaderViewBasePrivate::setModelImpl(const QVariant &newModel)
{
m_modelExplicitlySet = newModel.isValid();
auto asTableModel = [](QAbstractItemModel *model) -> QAbstractItemModel* {
if (qobject_cast<QAbstractTableModel *>(model))
return model;
// Since QQmlTableModel is not derived from QAbstractTableModel (QATM),
// we have to check for this model separately. We want it to behave like
// QATM to keep existing code working. This model is in the Labs module,
// and we cannot link to it to make a qobject_cast. Therefore, we are checking
// the class name.
// TODO: When QQmlTableModel leaves Labs, change to qobject_cast?
if (model && QByteArrayView(model->metaObject()->className()) == "QQmlTableModel")
return model;
return nullptr;
};
if (auto qabstracttablemodel = asTableModel(qaim(newModel))) {
if (qabstracttablemodel != m_headerDataProxyModel.sourceModel()) {
m_headerDataProxyModel.setSourceModel(qabstracttablemodel);
assignedModel = QVariant::fromValue(std::addressof(m_headerDataProxyModel));
scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
emit q_func()->modelChanged();
}
return;
}
m_headerDataProxyModel.setSourceModel(nullptr);
QQuickTableViewPrivate::setModelImpl(newModel);
}
void QQuickHeaderViewBasePrivate::syncModel()
{
if (!m_modelExplicitlySet && assignedSyncView) {
// When no model has been explicitly set, but we have a syncView, we
// should use the headerData() from the syncView's model as model.
// If the model is not a QAbstractItemModel, or if it doesn't contain
// any header data, header view will be empty.
auto syncView_d = QQuickTableViewPrivate::get(assignedSyncView);
auto syncViewModelAsQaim = qaim(syncView_d->assignedModel);
if (syncViewModelAsQaim != m_headerDataProxyModel.sourceModel()) {
m_headerDataProxyModel.setSourceModel(syncViewModelAsQaim); // can be nullptr
assignedModel = QVariant::fromValue(std::addressof(m_headerDataProxyModel));
emit q_func()->modelChanged();
}
}
QQuickTableViewPrivate::syncModel();
// For models that are just a list or a number, and especially not a
// table, we transpose the view when the orientation is horizontal.
// The model (list) will then be laid out horizontally rather than
// vertically, which is otherwise the default.
isTransposed = false;
const auto aim = model->abstractItemModel();
if (orientation() == Qt::Horizontal)
isTransposed = !aim || aim->columnCount() == 1;
if (m_textRole.isEmpty() && aim)
m_textRole = QLatin1String("display");
}
void QQuickHeaderViewBasePrivate::syncSyncView()
{
if (assignedSyncDirection != orientation()) {
qmlWarning(q_func()) << "Setting syncDirection other than Qt::"
<< QVariant::fromValue(orientation()).toString()
<< " is invalid.";
assignedSyncDirection = orientation();
}
QQuickTableViewPrivate::syncSyncView();
}
QAbstractItemModel *QQuickHeaderViewBasePrivate::selectionSourceModel()
{
// Our proxy model shares no common model items with HeaderView.model. So
// selections done in HeaderView cannot be represented in an ItemSelectionModel
// that is shared with the syncView (and for the same reason, the mapping functions
// modelIndex(cell) and cellAtIndex(index) have not been overridden either).
// Instead, we set the internal proxy model as selection source model.
return &m_headerDataProxyModel;
}
void QQuickHeaderViewBasePrivate::initItemCallback(int modelIndex, QObject *object)
{
Q_Q(QQuickHeaderViewBase);
QQuickTableViewPrivate::initItemCallback(modelIndex, object);
auto item = qobject_cast<QQuickItem *>(object);
if (!item)
return;
QQmlDelegateModelItem *modelItem = tableModel->getModelItem(modelIndex);
setRequiredProperty(kRequiredProperty_headerView, QVariant::fromValue(q),
modelIndex, item, true);
setRequiredProperty(kRequiredProperty_model, QVariant::fromValue(modelItem->modelObject()),
modelIndex, item, true);
}
int QQuickHeaderViewBasePrivate::logicalRowIndex(const int visualIndex) const
{
return (m_headerDataProxyModel.orientation() == Qt::Horizontal) ? visualIndex : QQuickTableViewPrivate::logicalRowIndex(visualIndex);
}
int QQuickHeaderViewBasePrivate::logicalColumnIndex(const int visualIndex) const
{
return (m_headerDataProxyModel.orientation() == Qt::Vertical) ? visualIndex : QQuickTableViewPrivate::logicalColumnIndex(visualIndex);
}
int QQuickHeaderViewBasePrivate::visualRowIndex(const int logicalIndex) const
{
return (m_headerDataProxyModel.orientation() == Qt::Horizontal) ? logicalIndex : QQuickTableViewPrivate::visualRowIndex(logicalIndex);
}
int QQuickHeaderViewBasePrivate::visualColumnIndex(const int logicalIndex) const
{
return (m_headerDataProxyModel.orientation() == Qt::Vertical) ? logicalIndex : QQuickTableViewPrivate::visualColumnIndex(logicalIndex);
}
QQuickHeaderViewBase::QQuickHeaderViewBase(Qt::Orientation orient, QQuickItem *parent)
: QQuickTableView(*(new QQuickHeaderViewBasePrivate), parent)
{
Q_D(QQuickHeaderViewBase);
d->setOrientation(orient);
d->init();
}
QQuickHeaderViewBase::QQuickHeaderViewBase(QQuickHeaderViewBasePrivate &dd, QQuickItem *parent)
: QQuickTableView(dd, parent)
{
Q_D(QQuickHeaderViewBase);
d->init();
}
QQuickHeaderViewBase::~QQuickHeaderViewBase()
{
}
QString QQuickHeaderViewBase::textRole() const
{
Q_D(const QQuickHeaderViewBase);
return d->m_textRole;
}
void QQuickHeaderViewBase::setTextRole(const QString &role)
{
Q_D(QQuickHeaderViewBase);
if (d->m_textRole == role)
return;
d->m_textRole = role;
emit textRoleChanged();
}
Qt::Orientation QQuickHeaderViewBasePrivate::orientation() const
{
return m_headerDataProxyModel.orientation();
}
void QQuickHeaderViewBasePrivate::setOrientation(Qt::Orientation orientation)
{
if (QQuickHeaderViewBasePrivate::orientation() == orientation)
return;
m_headerDataProxyModel.setOrientation(orientation);
}
QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickVerticalHeaderViewPrivate &dd, QQuickItem *parent)
: QQuickHeaderViewBase(dd, parent)
{
}
/*! \internal
\class QHeaderDataProxyModel
\brief
QHeaderDataProxyModel is a proxy AbstractItemModel type that maps
source model's headerData() to correspondent data()
*/
QHeaderDataProxyModel::QHeaderDataProxyModel(QObject *parent)
: QAbstractItemModel(parent)
{
}
QHeaderDataProxyModel::~QHeaderDataProxyModel() = default;
void QHeaderDataProxyModel::setSourceModel(QAbstractItemModel *newSourceModel)
{
if (m_model == newSourceModel)
return;
beginResetModel();
disconnectFromModel();
m_model = newSourceModel;
connectToModel();
endResetModel();
}
QModelIndex QHeaderDataProxyModel::index(int row, int column, const QModelIndex &parent) const
{
return hasIndex(row, column, parent) ? createIndex(row, column) : QModelIndex();
}
QModelIndex QHeaderDataProxyModel::parent(const QModelIndex &child) const
{
Q_UNUSED(child);
return QModelIndex();
}
QModelIndex QHeaderDataProxyModel::sibling(int row, int column, const QModelIndex &) const
{
return index(row, column);
}
int QHeaderDataProxyModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_model.isNull() ? -1 : (m_orientation == Qt::Horizontal ? 1 : m_model->rowCount(parent));
}
int QHeaderDataProxyModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_model.isNull() ? -1 : (m_orientation == Qt::Vertical ? 1 : m_model->columnCount(parent));
}
QVariant QHeaderDataProxyModel::data(const QModelIndex &index, int role) const
{
if (m_model.isNull() || !hasIndex(index.row(), index.column()))
return QVariant();
auto section = m_orientation == Qt::Vertical ? index.row() : index.column();
return m_model->headerData(section, m_orientation, role);
}
bool QHeaderDataProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!hasIndex(index.row(), index.column()))
return false;
auto section = m_orientation == Qt::Vertical ? index.row() : index.column();
auto ret = m_model->setHeaderData(section, m_orientation, value, role);
return ret;
}
bool QHeaderDataProxyModel::hasChildren(const QModelIndex &parent) const
{
if (!parent.isValid())
return rowCount(parent) > 0 && columnCount(parent) > 0;
return false;
}
QHash<int, QByteArray> QHeaderDataProxyModel::roleNames() const
{
using namespace Qt::Literals::StringLiterals;
auto names = m_model ? m_model->roleNames() : QAbstractItemModel::roleNames();
if (m_headerView) {
QString textRole = m_headerView->textRole();
if (textRole.isEmpty())
textRole = u"display"_s;
if (!names.values().contains(textRole.toUtf8().constData())) {
qmlWarning(m_headerView).nospace() << "The 'textRole' property contains a role that doesn't exist in the model: "
<< textRole << ". Check your model's roleNames() implementation";
}
}
return names;
}
QVariant QHeaderDataProxyModel::variantValue() const
{
return QVariant::fromValue(static_cast<QObject *>(const_cast<QHeaderDataProxyModel *>(this)));
}
void QHeaderDataProxyModel::setOrientation(Qt::Orientation o)
{
if (o == m_orientation)
return;
beginResetModel();
m_orientation = o;
endResetModel();
}
Qt::Orientation QHeaderDataProxyModel::orientation() const
{
return m_orientation;
}
QPointer<QAbstractItemModel> QHeaderDataProxyModel::sourceModel() const
{
return m_model;
}
void QHeaderDataProxyModel::connectToModel()
{
if (m_model.isNull())
return;
connect(m_model, &QAbstractItemModel::headerDataChanged,
this, [this](Qt::Orientation orient, int first, int last) {
if (orient != orientation())
return;
if (orient == Qt::Horizontal) {
emit dataChanged(createIndex(0, first), createIndex(0, last));
} else {
emit dataChanged(createIndex(first, 0), createIndex(last, 0));
}
});
connect(m_model, &QAbstractItemModel::modelAboutToBeReset,
this, &QHeaderDataProxyModel::modelAboutToBeReset, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::modelReset,
this, &QHeaderDataProxyModel::modelReset, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::rowsAboutToBeMoved,
this, &QHeaderDataProxyModel::rowsAboutToBeMoved, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::rowsMoved,
this, &QHeaderDataProxyModel::rowsMoved, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::rowsAboutToBeInserted,
this, &QHeaderDataProxyModel::rowsAboutToBeInserted, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::rowsInserted,
this, &QHeaderDataProxyModel::rowsInserted, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved,
this, &QHeaderDataProxyModel::rowsAboutToBeRemoved, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::rowsRemoved,
this, &QHeaderDataProxyModel::rowsRemoved, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::columnsAboutToBeMoved,
this, &QHeaderDataProxyModel::columnsAboutToBeMoved, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::columnsMoved,
this, &QHeaderDataProxyModel::columnsMoved, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::columnsAboutToBeInserted,
this, &QHeaderDataProxyModel::columnsAboutToBeInserted, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::columnsInserted,
this, &QHeaderDataProxyModel::columnsInserted, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::columnsAboutToBeRemoved,
this, &QHeaderDataProxyModel::columnsAboutToBeRemoved, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::columnsRemoved,
this, &QHeaderDataProxyModel::columnsRemoved, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::layoutAboutToBeChanged,
this, &QHeaderDataProxyModel::layoutAboutToBeChanged, Qt::UniqueConnection);
connect(m_model, &QAbstractItemModel::layoutChanged,
this, &QHeaderDataProxyModel::layoutChanged, Qt::UniqueConnection);
}
void QHeaderDataProxyModel::disconnectFromModel()
{
if (m_model.isNull())
return;
m_model->disconnect(this);
}
QQuickHorizontalHeaderView::QQuickHorizontalHeaderView(QQuickItem *parent)
: QQuickHeaderViewBase(*(new QQuickHorizontalHeaderViewPrivate), parent)
{
setFlickableDirection(FlickableDirection::HorizontalFlick);
setResizableColumns(true);
}
QQuickHorizontalHeaderView::~QQuickHorizontalHeaderView()
{
#if QT_CONFIG(quick_draganddrop)
Q_D(QQuickHorizontalHeaderView);
d->destroySectionDragHandler();
#endif
}
bool QQuickHorizontalHeaderView::movableColumns() const
{
Q_D(const QQuickHorizontalHeaderView);
return d->m_movableColumns;
}
void QQuickHorizontalHeaderView::setMovableColumns(bool movableColumns)
{
Q_D(QQuickHorizontalHeaderView);
if (d->m_movableColumns == movableColumns)
return;
d->m_movableColumns = movableColumns;
#if QT_CONFIG(quick_draganddrop)
if (d->m_movableColumns)
d->initSectionDragHandler(Qt::Horizontal);
else
d->destroySectionDragHandler();
#endif
emit movableColumnsChanged();
}
bool QQuickHorizontalHeaderView::showSortIndicator() const
{
Q_D(const QQuickHorizontalHeaderView);
return d->showSortIndicator;
}
void QQuickHorizontalHeaderView::setShowSortIndicator(bool show)
{
Q_D(QQuickHorizontalHeaderView);
if (d->showSortIndicator == show)
return;
d->showSortIndicator = show;
emit showSortIndicatorChanged();
}
bool QQuickHorizontalHeaderView::sortIndicatorClearable() const
{
Q_D(const QQuickHorizontalHeaderView);
return d->sortIndicatorClearable;
}
void QQuickHorizontalHeaderView::setSortIndicatorClearable(bool clearable)
{
Q_D(QQuickHorizontalHeaderView);
if (d->sortIndicatorClearable == clearable)
return;
d->sortIndicatorClearable = clearable;
emit sortIndicatorClearableChanged();
}
QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickItem *parent)
: QQuickHeaderViewBase(*(new QQuickVerticalHeaderViewPrivate), parent)
{
setFlickableDirection(FlickableDirection::VerticalFlick);
setResizableRows(true);
}
QQuickVerticalHeaderView::~QQuickVerticalHeaderView()
{
#if QT_CONFIG(quick_draganddrop)
Q_D(QQuickVerticalHeaderView);
d->destroySectionDragHandler();
#endif
}
bool QQuickVerticalHeaderView::movableRows() const
{
Q_D(const QQuickVerticalHeaderView);
return d->m_movableRows ;
}
void QQuickVerticalHeaderView::setMovableRows(bool movableRows)
{
Q_D(QQuickVerticalHeaderView);
if (d->m_movableRows == movableRows)
return;
d->m_movableRows = movableRows;
#if QT_CONFIG(quick_draganddrop)
if (d->m_movableRows)
d->initSectionDragHandler(Qt::Vertical);
else
d->destroySectionDragHandler();
#endif
emit movableRowsChanged();
}
QQuickHorizontalHeaderViewPrivate::QQuickHorizontalHeaderViewPrivate()
{
setOrientation(Qt::Horizontal);
};
QQuickHorizontalHeaderViewPrivate::~QQuickHorizontalHeaderViewPrivate() = default;
void QQuickHorizontalHeaderViewPrivate::executeTap(const QQuickHandlerPoint &point)
{
Q_Q(QQuickHorizontalHeaderView);
QQuickTableViewPrivate::executeTap(point);
const QPoint cell = q->cellAtPosition(point.position());
const int visualColumn = cell.x();
const int logicalColumn = logicalColumnIndex(visualColumn);
if (logicalColumn < 0)
return;
emit q->headerClicked(logicalColumn);
if (!assignedSyncView)
return;
QQuickTableViewPrivate::get(assignedSyncView)->requestSort(logicalColumn, sortIndicatorClearable);
}
QQuickVerticalHeaderViewPrivate::QQuickVerticalHeaderViewPrivate()
{
setOrientation(Qt::Vertical);
};
QQuickVerticalHeaderViewPrivate::~QQuickVerticalHeaderViewPrivate() = default;
QT_END_NAMESPACE
#include "moc_qquickheaderview_p_p.cpp"
#include "moc_qquickheaderview_p.cpp"