File indexing completed on 2024-05-12 15:42:59

0001 /*
0002     SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0003     SPDX-FileContributor: David Faure <david.faure@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "krearrangecolumnsproxymodel.h"
0009 
0010 class KRearrangeColumnsProxyModelPrivate
0011 {
0012 public:
0013     QVector<int> m_sourceColumns;
0014 };
0015 
0016 KRearrangeColumnsProxyModel::KRearrangeColumnsProxyModel(QObject *parent)
0017     : QIdentityProxyModel(parent)
0018     , d_ptr(new KRearrangeColumnsProxyModelPrivate)
0019 {
0020 }
0021 
0022 KRearrangeColumnsProxyModel::~KRearrangeColumnsProxyModel()
0023 {
0024 }
0025 
0026 void KRearrangeColumnsProxyModel::setSourceColumns(const QVector<int> &columns)
0027 {
0028     // We could use layoutChanged() here, but we would have to map persistent
0029     // indexes from the old to the new location...
0030     beginResetModel();
0031     d_ptr->m_sourceColumns = columns;
0032     endResetModel();
0033 }
0034 
0035 int KRearrangeColumnsProxyModel::columnCount(const QModelIndex &parent) const
0036 {
0037     Q_UNUSED(parent);
0038     if (!sourceModel()) {
0039         return 0;
0040     }
0041     return d_ptr->m_sourceColumns.count();
0042 }
0043 
0044 int KRearrangeColumnsProxyModel::rowCount(const QModelIndex &parent) const
0045 {
0046     Q_ASSERT(parent.isValid() ? parent.model() == this : true);
0047     if (!sourceModel()) {
0048         return 0;
0049     }
0050     if (parent.column() > 0) {
0051         return 0;
0052     }
0053     // The parent in the source model is on column 0, whatever swapping we are doing
0054     const QModelIndex sourceParent = mapToSource(parent).sibling(parent.row(), 0);
0055     return sourceModel()->rowCount(sourceParent);
0056 }
0057 
0058 bool KRearrangeColumnsProxyModel::hasChildren(const QModelIndex &parent) const
0059 {
0060     Q_ASSERT(parent.isValid() ? parent.model() == this : true);
0061     if (!sourceModel()) {
0062         return false;
0063     }
0064     if (d_ptr->m_sourceColumns.isEmpty()) { // no columns configured yet
0065         return false;
0066     }
0067     if (parent.column() > 0) {
0068         return false;
0069     }
0070     const QModelIndex sourceParent = mapToSource(parent).sibling(parent.row(), 0);
0071     return sourceModel()->rowCount(sourceParent) > 0;
0072 }
0073 
0074 // We derive from QIdentityProxyModel simply to be able to use
0075 // its mapFromSource method which has friend access to createIndex() in the source model.
0076 
0077 QModelIndex KRearrangeColumnsProxyModel::index(int row, int column, const QModelIndex &parent) const
0078 {
0079     Q_ASSERT(parent.isValid() ? parent.model() == this : true);
0080 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0081     Q_ASSERT(row >= 0);
0082 #else
0083     // workaround for QTreeView bug, fixed in https://codereview.qt-project.org/c/qt/qtbase/+/293092
0084     if (row < 0) {
0085         return {};
0086     }
0087 #endif
0088     Q_ASSERT(column >= 0);
0089 
0090     // Only first column has children
0091     if (parent.column() > 0) {
0092         return {};
0093     }
0094 
0095     if (!sourceModel()) {
0096         return {};
0097     }
0098     if (d_ptr->m_sourceColumns.isEmpty()) {
0099         return {};
0100     }
0101 
0102     // The parent in the source model is on column 0, whatever swapping we are doing
0103     const QModelIndex sourceParent = mapToSource(parent).sibling(parent.row(), 0);
0104 
0105     // Find the child in the source model, we need its internal pointer
0106     const QModelIndex sourceIndex = sourceModel()->index(row, sourceColumnForProxyColumn(column), sourceParent);
0107     if (!sourceIndex.isValid()) {
0108         return QModelIndex();
0109     }
0110 
0111     return createIndex(row, column, sourceIndex.internalPointer());
0112 }
0113 
0114 QModelIndex KRearrangeColumnsProxyModel::parent(const QModelIndex &child) const
0115 {
0116     Q_ASSERT(child.isValid() ? child.model() == this : true);
0117     const QModelIndex sourceIndex = mapToSource(child);
0118     const QModelIndex sourceParent = sourceIndex.parent();
0119     if (!sourceParent.isValid()) {
0120         return QModelIndex();
0121     }
0122     return createIndex(sourceParent.row(), 0, sourceParent.internalPointer());
0123 }
0124 
0125 QVariant KRearrangeColumnsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
0126 {
0127     if (orientation == Qt::Horizontal) {
0128         if (!sourceModel() || section >= d_ptr->m_sourceColumns.count()) {
0129             return QVariant();
0130         }
0131         const int sourceCol = sourceColumnForProxyColumn(section);
0132         return sourceModel()->headerData(sourceCol, orientation, role);
0133     } else {
0134         return QIdentityProxyModel::headerData(section, orientation, role);
0135     }
0136 }
0137 
0138 QModelIndex KRearrangeColumnsProxyModel::sibling(int row, int column, const QModelIndex &idx) const
0139 {
0140     if (column >= d_ptr->m_sourceColumns.count()) {
0141         return QModelIndex();
0142     }
0143     return index(row, column, idx.parent());
0144 }
0145 
0146 QModelIndex KRearrangeColumnsProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
0147 {
0148     if (!sourceIndex.isValid()) {
0149         return QModelIndex();
0150     }
0151     Q_ASSERT(sourceIndex.model() == sourceModel());
0152     const int proxyColumn = proxyColumnForSourceColumn(sourceIndex.column());
0153     return createIndex(sourceIndex.row(), proxyColumn, sourceIndex.internalPointer());
0154 }
0155 
0156 QModelIndex KRearrangeColumnsProxyModel::mapToSource(const QModelIndex &proxyIndex) const
0157 {
0158     if (!proxyIndex.isValid()) {
0159         return QModelIndex();
0160     }
0161     // This is just an indirect way to call sourceModel->createIndex(row, sourceColumn, pointer)
0162     const QModelIndex fakeIndex = createIndex(proxyIndex.row(), sourceColumnForProxyColumn(proxyIndex.column()), proxyIndex.internalPointer());
0163     return QIdentityProxyModel::mapToSource(fakeIndex);
0164 }
0165 
0166 int KRearrangeColumnsProxyModel::proxyColumnForSourceColumn(int sourceColumn) const
0167 {
0168     // If this is too slow, we could add a second QVector with index=logical_source_column value=desired_pos_in_proxy.
0169     return d_ptr->m_sourceColumns.indexOf(sourceColumn);
0170 }
0171 
0172 int KRearrangeColumnsProxyModel::sourceColumnForProxyColumn(int proxyColumn) const
0173 {
0174     Q_ASSERT(proxyColumn >= 0);
0175     Q_ASSERT(proxyColumn < d_ptr->m_sourceColumns.size());
0176     return d_ptr->m_sourceColumns.at(proxyColumn);
0177 }
0178 
0179 #include "moc_krearrangecolumnsproxymodel.cpp"