File indexing completed on 2025-03-09 06:47:47
0001 /* 0002 * Copyright 2010 by Marco Martin <mart@kde.org> 0003 * Copyright 2019 by David Edmundson <davidedmundson@kde.org> 0004 * 0005 * This program is free software; you can redistribute it and/or modify 0006 * it under the terms of the GNU Library General Public License as 0007 * published by the Free Software Foundation; either version 2, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details 0014 * 0015 * You should have received a copy of the GNU Library General Public 0016 * License along with this program; if not, write to the 0017 * Free Software Foundation, Inc., 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "ksortfilterproxymodel.h" 0022 0023 #include <QQmlContext> 0024 #include <QQmlEngine> 0025 0026 #include "kitemmodels_debug.h" 0027 0028 KSortFilterProxyModel::KSortFilterProxyModel(QObject *parent) 0029 : QSortFilterProxyModel(parent) 0030 , m_componentCompleted(false) 0031 , m_sortRoleSourceOfTruth(SourceOfTruthIsRoleID) 0032 , m_filterRoleSourceOfTruth(SourceOfTruthIsRoleID) 0033 , m_sortRoleGuard(false) 0034 , m_filterRoleGuard(false) 0035 { 0036 setDynamicSortFilter(true); 0037 connect(this, &KSortFilterProxyModel::modelReset, this, &KSortFilterProxyModel::rowCountChanged); 0038 connect(this, &KSortFilterProxyModel::rowsInserted, this, &KSortFilterProxyModel::rowCountChanged); 0039 connect(this, &KSortFilterProxyModel::rowsRemoved, this, &KSortFilterProxyModel::rowCountChanged); 0040 0041 connect(this, &KSortFilterProxyModel::sortRoleChanged, this, &KSortFilterProxyModel::syncSortRoleProperties); 0042 connect(this, &KSortFilterProxyModel::filterRoleChanged, this, &KSortFilterProxyModel::syncFilterRoleProperties); 0043 } 0044 0045 KSortFilterProxyModel::~KSortFilterProxyModel() 0046 { 0047 } 0048 0049 static void reverseStringIntHash(QHash<QString, int> &dst, const QHash<int, QByteArray> &src) 0050 { 0051 dst.clear(); 0052 dst.reserve(src.count()); 0053 for (auto i = src.constBegin(); i != src.constEnd(); ++i) { 0054 dst[QString::fromUtf8(i.value())] = i.key(); 0055 } 0056 } 0057 0058 void KSortFilterProxyModel::syncRoleNames() 0059 { 0060 if (!sourceModel()) { 0061 return; 0062 } 0063 0064 reverseStringIntHash(m_roleIds, roleNames()); 0065 0066 m_sortRoleGuard = true; 0067 syncSortRoleProperties(); 0068 m_sortRoleGuard = false; 0069 0070 m_filterRoleGuard = true; 0071 syncFilterRoleProperties(); 0072 m_filterRoleGuard = false; 0073 } 0074 0075 int KSortFilterProxyModel::roleNameToId(const QString &name) const 0076 { 0077 return m_roleIds.value(name, Qt::DisplayRole); 0078 } 0079 0080 void KSortFilterProxyModel::setSourceModel(QAbstractItemModel *model) 0081 { 0082 const auto oldModel = sourceModel(); 0083 0084 if (model == oldModel) { 0085 return; 0086 } 0087 0088 if (oldModel) { 0089 for (const auto &connection : std::as_const(m_sourceModelConnections)) { 0090 disconnect(connection); 0091 } 0092 } 0093 0094 QSortFilterProxyModel::setSourceModel(model); 0095 0096 // NOTE: some models actually fill their roleNames() only when they get some actual data, this works around the bad behavior 0097 if (model) { 0098 m_sourceModelConnections = {{ 0099 connect(model, &QAbstractItemModel::modelReset, this, &KSortFilterProxyModel::syncRoleNames), 0100 connect(model, &QAbstractItemModel::rowsInserted, this, &KSortFilterProxyModel::syncRoleNames), 0101 connect(model, &QAbstractItemModel::rowsRemoved, this, &KSortFilterProxyModel::syncRoleNames), 0102 }}; 0103 } 0104 0105 if (m_componentCompleted) { 0106 syncRoleNames(); 0107 } 0108 } 0109 0110 bool KSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const 0111 { 0112 if (m_filterRowCallback.isCallable()) { 0113 QJSEngine *engine = qjsEngine(this); 0114 QJSValueList args = {QJSValue(source_row), engine->toScriptValue(source_parent)}; 0115 0116 QJSValue result = const_cast<KSortFilterProxyModel *>(this)->m_filterRowCallback.call(args); 0117 if (result.isError()) { 0118 qCWarning(KITEMMODELS_LOG) << "Row filter callback produced an error:"; 0119 qCWarning(KITEMMODELS_LOG) << result.toString(); 0120 return true; 0121 } else { 0122 return result.toBool(); 0123 } 0124 } 0125 0126 return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); 0127 } 0128 0129 bool KSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const 0130 { 0131 if (m_filterColumnCallback.isCallable()) { 0132 QJSEngine *engine = qjsEngine(this); 0133 QJSValueList args = {QJSValue(source_column), engine->toScriptValue(source_parent)}; 0134 0135 QJSValue result = const_cast<KSortFilterProxyModel *>(this)->m_filterColumnCallback.call(args); 0136 if (result.isError()) { 0137 qCWarning(KITEMMODELS_LOG) << "Row filter callback produced an error:"; 0138 qCWarning(KITEMMODELS_LOG) << result.toString(); 0139 return true; 0140 } else { 0141 return result.toBool(); 0142 } 0143 } 0144 0145 return QSortFilterProxyModel::filterAcceptsColumn(source_column, source_parent); 0146 } 0147 0148 void KSortFilterProxyModel::setFilterString(const QString &filterString) 0149 { 0150 if (filterString == m_filterString) { 0151 return; 0152 } 0153 m_filterString = filterString; 0154 QSortFilterProxyModel::setFilterFixedString(filterString); 0155 Q_EMIT filterStringChanged(); 0156 } 0157 0158 QString KSortFilterProxyModel::filterString() const 0159 { 0160 return m_filterString; 0161 } 0162 0163 QJSValue KSortFilterProxyModel::filterRowCallback() const 0164 { 0165 return m_filterRowCallback; 0166 } 0167 0168 void KSortFilterProxyModel::setFilterRowCallback(const QJSValue &callback) 0169 { 0170 if (m_filterRowCallback.strictlyEquals(callback)) { 0171 return; 0172 } 0173 0174 if (!callback.isNull() && !callback.isCallable()) { 0175 return; 0176 } 0177 0178 m_filterRowCallback = callback; 0179 invalidateFilter(); 0180 0181 Q_EMIT filterRowCallbackChanged(callback); 0182 } 0183 0184 void KSortFilterProxyModel::setFilterColumnCallback(const QJSValue &callback) 0185 { 0186 if (m_filterColumnCallback.strictlyEquals(callback)) { 0187 return; 0188 } 0189 0190 if (!callback.isNull() && !callback.isCallable()) { 0191 return; 0192 } 0193 0194 m_filterColumnCallback = callback; 0195 invalidateFilter(); 0196 0197 Q_EMIT filterColumnCallbackChanged(callback); 0198 } 0199 0200 QJSValue KSortFilterProxyModel::filterColumnCallback() const 0201 { 0202 return m_filterColumnCallback; 0203 } 0204 0205 void KSortFilterProxyModel::syncSortRoleProperties() 0206 { 0207 if (!sourceModel()) { 0208 return; 0209 } 0210 0211 if (!m_sortRoleGuard) { 0212 m_sortRoleSourceOfTruth = SourceOfTruthIsRoleID; 0213 } 0214 0215 if (m_sortRoleSourceOfTruth == SourceOfTruthIsRoleName) { 0216 if (m_sortRoleName.isEmpty()) { 0217 QSortFilterProxyModel::setSortRole(Qt::DisplayRole); 0218 sort(-1, Qt::AscendingOrder); 0219 } else { 0220 const auto role = roleNameToId(m_sortRoleName); 0221 QSortFilterProxyModel::setSortRole(role); 0222 sort(std::max(sortColumn(), 0), sortOrder()); 0223 } 0224 } else { 0225 const QString roleName = QString::fromUtf8(roleNames().value(sortRole())); 0226 if (m_sortRoleName != roleName) { 0227 m_sortRoleName = roleName; 0228 Q_EMIT sortRoleNameChanged(); 0229 } 0230 } 0231 } 0232 0233 void KSortFilterProxyModel::syncFilterRoleProperties() 0234 { 0235 if (!sourceModel()) { 0236 return; 0237 } 0238 0239 if (!m_filterRoleGuard) { 0240 m_filterRoleSourceOfTruth = SourceOfTruthIsRoleID; 0241 } 0242 0243 if (m_filterRoleSourceOfTruth == SourceOfTruthIsRoleName) { 0244 const auto role = roleNameToId(m_filterRoleName); 0245 QSortFilterProxyModel::setFilterRole(role); 0246 } else { 0247 const QString roleName = QString::fromUtf8(roleNames().value(filterRole())); 0248 if (m_filterRoleName != roleName) { 0249 m_filterRoleName = roleName; 0250 Q_EMIT filterRoleNameChanged(); 0251 } 0252 } 0253 } 0254 0255 void KSortFilterProxyModel::setFilterRoleName(const QString &roleName) 0256 { 0257 if (m_filterRoleSourceOfTruth == SourceOfTruthIsRoleName && m_filterRoleName == roleName) { 0258 return; 0259 } 0260 0261 m_filterRoleSourceOfTruth = SourceOfTruthIsRoleName; 0262 m_filterRoleName = roleName; 0263 0264 m_filterRoleGuard = true; 0265 syncFilterRoleProperties(); 0266 m_filterRoleGuard = false; 0267 0268 Q_EMIT filterRoleNameChanged(); 0269 } 0270 0271 QString KSortFilterProxyModel::filterRoleName() const 0272 { 0273 return m_filterRoleName; 0274 } 0275 0276 void KSortFilterProxyModel::setSortRoleName(const QString &roleName) 0277 { 0278 if (m_sortRoleSourceOfTruth == SourceOfTruthIsRoleName && m_sortRoleName == roleName) { 0279 return; 0280 } 0281 0282 m_sortRoleSourceOfTruth = SourceOfTruthIsRoleName; 0283 m_sortRoleName = roleName; 0284 0285 m_sortRoleGuard = true; 0286 syncSortRoleProperties(); 0287 m_sortRoleGuard = false; 0288 0289 Q_EMIT sortRoleNameChanged(); 0290 } 0291 0292 QString KSortFilterProxyModel::sortRoleName() const 0293 { 0294 return m_sortRoleName; 0295 } 0296 0297 void KSortFilterProxyModel::setSortOrder(const Qt::SortOrder order) 0298 { 0299 sort(std::max(sortColumn(), 0), order); 0300 Q_EMIT sortOrderChanged(); 0301 } 0302 0303 void KSortFilterProxyModel::setSortColumn(int column) 0304 { 0305 if (column == sortColumn()) { 0306 return; 0307 } 0308 sort(column, sortOrder()); 0309 Q_EMIT sortColumnChanged(); 0310 } 0311 0312 void KSortFilterProxyModel::classBegin() 0313 { 0314 } 0315 0316 void KSortFilterProxyModel::componentComplete() 0317 { 0318 m_componentCompleted = true; 0319 syncRoleNames(); 0320 } 0321 0322 void KSortFilterProxyModel::invalidateFilter() 0323 { 0324 QSortFilterProxyModel::invalidateFilter(); 0325 } 0326 0327 #include "moc_ksortfilterproxymodel.cpp"