File indexing completed on 2024-04-28 03:56:15

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"