File indexing completed on 2024-05-19 04:56:28
0001 /** 0002 * \file checkablelistmodel.h 0003 * Proxy model to use QAbstractItemModel with QML. 0004 * 0005 * \b Project: Kid3 0006 * \author Urs Fleisch 0007 * \date 23 Sep 2014 0008 * 0009 * Copyright (C) 2014-2018 Urs Fleisch 0010 * 0011 * This file is part of Kid3. 0012 * 0013 * Kid3 is free software; you can redistribute it and/or modify 0014 * it under the terms of the GNU General Public License as published by 0015 * the Free Software Foundation; either version 2 of the License, or 0016 * (at your option) any later version. 0017 * 0018 * Kid3 is distributed in the hope that it will be useful, 0019 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0021 * GNU General Public License for more details. 0022 * 0023 * You should have received a copy of the GNU General Public License 0024 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0025 */ 0026 0027 #include "checkablelistmodel.h" 0028 #include <QItemSelectionModel> 0029 0030 CheckableListModel::CheckableListModel(QObject* parent) 0031 : QAbstractProxyModel(parent), m_selModel(nullptr) 0032 { 0033 } 0034 0035 QItemSelectionModel* CheckableListModel::selectionModel() const 0036 { 0037 return m_selModel; 0038 } 0039 0040 void CheckableListModel::setSelectionModel(QItemSelectionModel* selModel) 0041 { 0042 if (m_selModel != selModel) { 0043 if (m_selModel) { 0044 disconnect(m_selModel, nullptr, this, nullptr); 0045 } 0046 m_selModel = selModel; 0047 if (m_selModel) { 0048 connect(m_selModel, 0049 &QItemSelectionModel::selectionChanged, 0050 this, &CheckableListModel::onSelectionChanged); 0051 connect(m_selModel, &QItemSelectionModel::currentChanged, 0052 this, &CheckableListModel::onCurrentChanged); 0053 } 0054 emit selectionModelChanged(); 0055 } 0056 } 0057 0058 void CheckableListModel::setSelectionModelObject(QObject *obj) 0059 { 0060 if (auto selModel = qobject_cast<QItemSelectionModel*>(obj)) { 0061 setSelectionModel(selModel); 0062 } 0063 } 0064 0065 QModelIndex CheckableListModel::rootIndex() const 0066 { 0067 return m_rootIndex; 0068 } 0069 0070 void CheckableListModel::setRootIndex(const QModelIndex& rootIndex) 0071 { 0072 if (m_rootIndex != rootIndex) { 0073 beginResetModel(); 0074 m_rootIndex = rootIndex; 0075 endResetModel(); 0076 emit rootIndexChanged(); 0077 } 0078 } 0079 0080 QModelIndex CheckableListModel::modelIndex(int row) const 0081 { 0082 QAbstractItemModel* srcModel = sourceModel(); 0083 return srcModel ? srcModel->index(row, 0, m_rootIndex) : QModelIndex(); 0084 } 0085 0086 QModelIndex CheckableListModel::parentModelIndex() const 0087 { 0088 return m_rootIndex.parent(); 0089 } 0090 0091 bool CheckableListModel::setDataValue(int row, const QByteArray& roleName, 0092 const QVariant& value) 0093 { 0094 QHash<int,QByteArray> roleHash = roleNames(); 0095 for (auto it = roleHash.constBegin(); it != roleHash.constEnd(); ++it) { 0096 if (it.value() == roleName) { 0097 return setData(index(row, 0), value, it.key()); 0098 } 0099 } 0100 return false; 0101 } 0102 0103 QVariant CheckableListModel::getDataValue(int row, 0104 const QByteArray& roleName) const 0105 { 0106 QHash<int,QByteArray> roleHash = roleNames(); 0107 for (auto it = roleHash.constBegin(); it != roleHash.constEnd(); ++it) { 0108 if (it.value() == roleName) { 0109 return data(index(row, 0), it.key()); 0110 } 0111 } 0112 return QVariant(); 0113 } 0114 0115 bool CheckableListModel::hasModelChildren(int row) const 0116 { 0117 QAbstractItemModel* srcModel = sourceModel(); 0118 return srcModel && srcModel->hasChildren(mapToSource(index(row, 0))); 0119 } 0120 0121 int CheckableListModel::currentRow() const 0122 { 0123 return m_selModel ? mapFromSource(m_selModel->currentIndex()).row() : -1; 0124 } 0125 0126 void CheckableListModel::setCurrentRow(int row) 0127 { 0128 if (m_selModel) { 0129 m_selModel->setCurrentIndex( 0130 mapToSource(index(row, 0)), 0131 QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 0132 } 0133 } 0134 0135 Qt::ItemFlags CheckableListModel::flags(const QModelIndex& index) const 0136 { 0137 Qt::ItemFlags itemFlags = QAbstractProxyModel::flags(index); 0138 if (index.isValid() && index.column() == 0 && m_selModel) { 0139 itemFlags |= Qt::ItemIsUserCheckable; 0140 } 0141 return itemFlags; 0142 } 0143 0144 QVariant CheckableListModel::data(const QModelIndex& index, int role) const 0145 { 0146 if (role == Qt::CheckStateRole) { 0147 if (index.column() != 0) 0148 return QVariant(); 0149 if (!m_selModel) 0150 return Qt::Unchecked; 0151 return m_selModel->selection().contains(mapToSource(index)) 0152 ? Qt::Checked : Qt::Unchecked; 0153 } 0154 return QAbstractProxyModel::data(index, role); 0155 } 0156 0157 bool CheckableListModel::setData(const QModelIndex& index, 0158 const QVariant& value, int role) 0159 { 0160 if (role == Qt::CheckStateRole) { 0161 if (index.column() != 0) 0162 return false; 0163 if (!m_selModel) 0164 return false; 0165 0166 auto state = static_cast<Qt::CheckState>(value.toInt()); 0167 const QModelIndex srcIndex = mapToSource(index); 0168 m_selModel->setCurrentIndex(srcIndex, state == Qt::Checked 0169 ? QItemSelectionModel::Select | QItemSelectionModel::Rows 0170 : QItemSelectionModel::Deselect | QItemSelectionModel::Rows); 0171 emit dataChanged(index, index); 0172 return true; 0173 } 0174 return QAbstractProxyModel::setData(index, value, role); 0175 } 0176 0177 void CheckableListModel::setSourceModel(QAbstractItemModel* srcModel) 0178 { 0179 if (sourceModel() != srcModel) { 0180 QAbstractProxyModel::setSourceModel(srcModel); 0181 emit sourceModelChanged(); 0182 0183 if (sourceModel()) { 0184 disconnect(sourceModel(), nullptr, this, nullptr); 0185 } 0186 QAbstractProxyModel::setSourceModel(srcModel); 0187 if (srcModel) { 0188 connect(srcModel, &QAbstractItemModel::modelAboutToBeReset, 0189 this, &CheckableListModel::onModelAboutToBeReset); 0190 connect(srcModel, &QAbstractItemModel::modelReset, 0191 this, &CheckableListModel::onModelReset); 0192 connect(srcModel, &QAbstractItemModel::layoutAboutToBeChanged, 0193 this, &QAbstractItemModel::layoutAboutToBeChanged); 0194 connect(srcModel, &QAbstractItemModel::layoutChanged, 0195 this, &QAbstractItemModel::layoutChanged); 0196 connect(srcModel, &QAbstractItemModel::dataChanged, 0197 this, &CheckableListModel::onDataChanged); 0198 connect(srcModel, &QAbstractItemModel::rowsAboutToBeRemoved, 0199 this, &CheckableListModel::onRowsAboutToBeRemoved); 0200 connect(srcModel, &QAbstractItemModel::rowsRemoved, 0201 this, &CheckableListModel::onRowsRemoved); 0202 connect(srcModel, &QAbstractItemModel::rowsAboutToBeInserted, 0203 this, &CheckableListModel::onRowsAboutToBeInserted); 0204 connect(srcModel, &QAbstractItemModel::rowsInserted, 0205 this, &CheckableListModel::onRowsInserted); 0206 } 0207 } 0208 } 0209 0210 void CheckableListModel::setSourceModelObject(QObject* obj) 0211 { 0212 if (auto srcModel = qobject_cast<QAbstractItemModel*>(obj)) { 0213 setSourceModel(srcModel); 0214 } 0215 } 0216 0217 void CheckableListModel::onModelAboutToBeReset() 0218 { 0219 beginResetModel(); 0220 } 0221 0222 void CheckableListModel::onModelReset() 0223 { 0224 endResetModel(); 0225 } 0226 0227 void CheckableListModel::onDataChanged(const QModelIndex& topLeft, 0228 const QModelIndex& bottomRight) 0229 { 0230 QModelIndex first = mapFromSource(topLeft); 0231 QModelIndex last = mapFromSource(bottomRight); 0232 if (first.isValid() && last.isValid() && 0233 first.parent() == last.parent() && first.column() == last.column()) { 0234 emit dataChanged(first, last); 0235 } 0236 } 0237 0238 void CheckableListModel::onRowsAboutToBeRemoved(const QModelIndex& parent, 0239 int first, int last) 0240 { 0241 if (parent == m_rootIndex) { 0242 beginRemoveRows(mapFromSource(parent), first, last); 0243 } 0244 } 0245 0246 void CheckableListModel::onRowsRemoved(const QModelIndex &parent, 0247 int first, int last) 0248 { 0249 Q_UNUSED(first); 0250 Q_UNUSED(last); 0251 if (parent == m_rootIndex) { 0252 endRemoveRows(); 0253 } 0254 } 0255 0256 void CheckableListModel::onRowsAboutToBeInserted(const QModelIndex& parent, 0257 int first, int last) 0258 { 0259 if (parent == m_rootIndex) { 0260 beginInsertRows(mapFromSource(parent), first, last); 0261 } 0262 } 0263 0264 void CheckableListModel::onRowsInserted(const QModelIndex& parent, 0265 int first, int last) 0266 { 0267 Q_UNUSED(first); 0268 Q_UNUSED(last); 0269 if (parent == m_rootIndex) { 0270 endInsertRows(); 0271 } 0272 } 0273 0274 void CheckableListModel::onSelectionChanged(const QItemSelection& selected, 0275 const QItemSelection& deselected) 0276 { 0277 const auto selectedRanges = mapSelectionFromSource(selected); 0278 for (const QItemSelectionRange& range : selectedRanges) 0279 emit dataChanged(range.topLeft(), range.bottomRight()); 0280 const auto deselectedRanges = mapSelectionFromSource(deselected); 0281 for (const QItemSelectionRange& range : deselectedRanges) 0282 emit dataChanged(range.topLeft(), range.bottomRight()); 0283 } 0284 0285 void CheckableListModel::onCurrentChanged(const QModelIndex& current, 0286 const QModelIndex& previous) 0287 { 0288 QModelIndex idx = mapFromSource(current); 0289 emit currentRowChanged(idx.row()); 0290 emit dataChanged(idx, idx); 0291 idx = mapFromSource(previous); 0292 emit dataChanged(idx, idx); 0293 } 0294 0295 QModelIndex CheckableListModel::index(int row, int column, 0296 const QModelIndex& parent) const 0297 { 0298 return parent.isValid() ? QModelIndex() : createIndex(row, column); 0299 } 0300 0301 QModelIndex CheckableListModel::parent(const QModelIndex&) const 0302 { 0303 return QModelIndex(); 0304 } 0305 0306 int CheckableListModel::rowCount(const QModelIndex& parent) const 0307 { 0308 QAbstractItemModel* srcModel = sourceModel(); 0309 return !parent.isValid() && srcModel ? srcModel->rowCount(m_rootIndex) : 0; 0310 } 0311 0312 int CheckableListModel::columnCount(const QModelIndex& parent) const 0313 { 0314 QAbstractItemModel* srcModel = sourceModel(); 0315 return !parent.isValid() && srcModel ?srcModel->columnCount(m_rootIndex) : 0; 0316 } 0317 0318 QModelIndex CheckableListModel::mapToSource(const QModelIndex& proxyIndex) const 0319 { 0320 QAbstractItemModel* srcModel = sourceModel(); 0321 return proxyIndex.isValid() && srcModel 0322 ? srcModel->index(proxyIndex.row(), proxyIndex.column(), m_rootIndex) 0323 : QModelIndex(); 0324 } 0325 0326 QModelIndex CheckableListModel::mapFromSource(const QModelIndex& srcIndex) const 0327 { 0328 return srcIndex.parent() == m_rootIndex 0329 ? createIndex(srcIndex.row(), srcIndex.column()) : QModelIndex(); 0330 }