KItemModels

ksortfilterproxymodel.cpp
1/*
2 * SPDX-FileCopyrightText: 2010 Marco Martin <mart@kde.org>
3 * SPDX-FileCopyrightText: 2019 David Edmundson <davidedmundson@kde.org>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#include "ksortfilterproxymodel.h"
9
10#include <QQmlContext>
11#include <QQmlEngine>
12
13#include "kitemmodels_debug.h"
14
15KSortFilterProxyModel::KSortFilterProxyModel(QObject *parent)
16 : QSortFilterProxyModel(parent)
17 , m_componentCompleted(false)
18 , m_sortRoleSourceOfTruth(SourceOfTruthIsRoleID)
19 , m_filterRoleSourceOfTruth(SourceOfTruthIsRoleID)
20 , m_sortRoleGuard(false)
21 , m_filterRoleGuard(false)
22{
23 setDynamicSortFilter(true);
24 connect(this, &KSortFilterProxyModel::modelReset, this, &KSortFilterProxyModel::rowCountChanged);
25 connect(this, &KSortFilterProxyModel::rowsInserted, this, &KSortFilterProxyModel::rowCountChanged);
26 connect(this, &KSortFilterProxyModel::rowsRemoved, this, &KSortFilterProxyModel::rowCountChanged);
27
28 connect(this, &KSortFilterProxyModel::sortRoleChanged, this, &KSortFilterProxyModel::syncSortRoleProperties);
29 connect(this, &KSortFilterProxyModel::filterRoleChanged, this, &KSortFilterProxyModel::syncFilterRoleProperties);
30}
31
32KSortFilterProxyModel::~KSortFilterProxyModel()
33{
34}
35
36static void reverseStringIntHash(QHash<QString, int> &dst, const QHash<int, QByteArray> &src)
37{
38 dst.clear();
39 dst.reserve(src.count());
40 for (auto i = src.constBegin(); i != src.constEnd(); ++i) {
41 dst[QString::fromUtf8(i.value())] = i.key();
42 }
43}
44
45void KSortFilterProxyModel::syncRoleNames()
46{
47 if (!sourceModel()) {
48 return;
49 }
50
51 reverseStringIntHash(m_roleIds, roleNames());
52
53 m_sortRoleGuard = true;
54 syncSortRoleProperties();
55 m_sortRoleGuard = false;
56
57 m_filterRoleGuard = true;
58 syncFilterRoleProperties();
59 m_filterRoleGuard = false;
60}
61
62int KSortFilterProxyModel::roleNameToId(const QString &name) const
63{
64 return m_roleIds.value(name, Qt::DisplayRole);
65}
66
67void KSortFilterProxyModel::setSourceModel(QAbstractItemModel *model)
68{
69 const auto oldModel = sourceModel();
70
71 if (model == oldModel) {
72 return;
73 }
74
75 if (oldModel) {
76 for (const auto &connection : std::as_const(m_sourceModelConnections)) {
77 disconnect(connection);
78 }
79 }
80
82
83 // NOTE: some models actually fill their roleNames() only when they get some actual data, this works around the bad behavior
84 if (model) {
85 m_sourceModelConnections = {{
86 connect(model, &QAbstractItemModel::modelReset, this, &KSortFilterProxyModel::syncRoleNames),
87 connect(model, &QAbstractItemModel::rowsInserted, this, &KSortFilterProxyModel::syncRoleNames),
88 connect(model, &QAbstractItemModel::rowsRemoved, this, &KSortFilterProxyModel::syncRoleNames),
89 }};
90 }
91
92 if (m_componentCompleted) {
93 syncRoleNames();
94 }
95}
96
97bool KSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
98{
99 if (m_filterRowCallback.isCallable()) {
100 QJSEngine *engine = qjsEngine(this);
101 QJSValueList args = {QJSValue(source_row), engine->toScriptValue(source_parent)};
102
103 QJSValue result = const_cast<KSortFilterProxyModel *>(this)->m_filterRowCallback.call(args);
104 if (result.isError()) {
105 qCWarning(KITEMMODELS_LOG) << "Row filter callback produced an error:";
106 qCWarning(KITEMMODELS_LOG) << result.toString();
107 return true;
108 } else {
109 return result.toBool();
110 }
111 }
112
113 return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
114}
115
116bool KSortFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
117{
118 if (m_filterColumnCallback.isCallable()) {
119 QJSEngine *engine = qjsEngine(this);
120 QJSValueList args = {QJSValue(source_column), engine->toScriptValue(source_parent)};
121
122 QJSValue result = const_cast<KSortFilterProxyModel *>(this)->m_filterColumnCallback.call(args);
123 if (result.isError()) {
124 qCWarning(KITEMMODELS_LOG) << "Row filter callback produced an error:";
125 qCWarning(KITEMMODELS_LOG) << result.toString();
126 return true;
127 } else {
128 return result.toBool();
129 }
130 }
131
132 return QSortFilterProxyModel::filterAcceptsColumn(source_column, source_parent);
133}
134
135void KSortFilterProxyModel::setFilterString(const QString &filterString)
136{
137 if (filterString == m_filterString) {
138 return;
139 }
140 m_filterString = filterString;
142 Q_EMIT filterStringChanged();
143}
144
146{
147 return m_filterString;
148}
149
151{
152 return m_filterRowCallback;
153}
154
155void KSortFilterProxyModel::setFilterRowCallback(const QJSValue &callback)
156{
157 if (m_filterRowCallback.strictlyEquals(callback)) {
158 return;
159 }
160
161 if (!callback.isNull() && !callback.isCallable()) {
162 return;
163 }
164
165 m_filterRowCallback = callback;
167
168 Q_EMIT filterRowCallbackChanged(callback);
169}
170
171void KSortFilterProxyModel::setFilterColumnCallback(const QJSValue &callback)
172{
173 if (m_filterColumnCallback.strictlyEquals(callback)) {
174 return;
175 }
176
177 if (!callback.isNull() && !callback.isCallable()) {
178 return;
179 }
180
181 m_filterColumnCallback = callback;
183
184 Q_EMIT filterColumnCallbackChanged(callback);
185}
186
188{
189 return m_filterColumnCallback;
190}
191
192void KSortFilterProxyModel::syncSortRoleProperties()
193{
194 if (!sourceModel()) {
195 return;
196 }
197
198 if (!m_sortRoleGuard) {
199 m_sortRoleSourceOfTruth = SourceOfTruthIsRoleID;
200 }
201
202 if (m_sortRoleSourceOfTruth == SourceOfTruthIsRoleName) {
203 if (m_sortRoleName.isEmpty()) {
206 } else {
207 const auto role = roleNameToId(m_sortRoleName);
209 sort(std::max(sortColumn(), 0), sortOrder());
210 }
211 } else {
212 const QString roleName = QString::fromUtf8(roleNames().value(sortRole()));
213 if (m_sortRoleName != roleName) {
214 m_sortRoleName = roleName;
215 Q_EMIT sortRoleNameChanged();
216 }
217 }
218}
219
220void KSortFilterProxyModel::syncFilterRoleProperties()
221{
222 if (!sourceModel()) {
223 return;
224 }
225
226 if (!m_filterRoleGuard) {
227 m_filterRoleSourceOfTruth = SourceOfTruthIsRoleID;
228 }
229
230 if (m_filterRoleSourceOfTruth == SourceOfTruthIsRoleName) {
231 const auto role = roleNameToId(m_filterRoleName);
233 } else {
234 const QString roleName = QString::fromUtf8(roleNames().value(filterRole()));
235 if (m_filterRoleName != roleName) {
236 m_filterRoleName = roleName;
237 Q_EMIT filterRoleNameChanged();
238 }
239 }
240}
241
242void KSortFilterProxyModel::setFilterRoleName(const QString &roleName)
243{
244 if (m_filterRoleSourceOfTruth == SourceOfTruthIsRoleName && m_filterRoleName == roleName) {
245 return;
246 }
247
248 m_filterRoleSourceOfTruth = SourceOfTruthIsRoleName;
249 m_filterRoleName = roleName;
250
251 m_filterRoleGuard = true;
252 syncFilterRoleProperties();
253 m_filterRoleGuard = false;
254
255 Q_EMIT filterRoleNameChanged();
256}
257
259{
260 return m_filterRoleName;
261}
262
263void KSortFilterProxyModel::setSortRoleName(const QString &roleName)
264{
265 if (m_sortRoleSourceOfTruth == SourceOfTruthIsRoleName && m_sortRoleName == roleName) {
266 return;
267 }
268
269 m_sortRoleSourceOfTruth = SourceOfTruthIsRoleName;
270 m_sortRoleName = roleName;
271
272 m_sortRoleGuard = true;
273 syncSortRoleProperties();
274 m_sortRoleGuard = false;
275
276 Q_EMIT sortRoleNameChanged();
277}
278
280{
281 return m_sortRoleName;
282}
283
284void KSortFilterProxyModel::setSortOrder(const Qt::SortOrder order)
285{
286 sort(std::max(sortColumn(), 0), order);
287 Q_EMIT sortOrderChanged();
288}
289
290void KSortFilterProxyModel::setSortColumn(int column)
291{
292 if (column == sortColumn()) {
293 return;
294 }
295 sort(column, sortOrder());
296 Q_EMIT sortColumnChanged();
297}
298
299void KSortFilterProxyModel::classBegin()
300{
301}
302
303void KSortFilterProxyModel::componentComplete()
304{
305 m_componentCompleted = true;
306 syncRoleNames();
307}
308
313
314#include "moc_ksortfilterproxymodel.cpp"
QJSValue filterRowCallback
A JavaScript callable that can be used to perform advanced filters on a given row.
QString sortRoleName
The role of the sourceModel that will be used for sorting.
Qt::SortOrder sortOrder
One of Qt.AscendingOrder or Qt.DescendingOrder.
QString filterRoleName
The role of the sourceModel on which the filter will be applied.
int sortColumn
Specify which column should be used for sorting The default value is -1.
QJSValue filterColumnCallback
A JavaScript callable that can be used to perform advanced filters on a given column.
QML_ELEMENTQString filterString
The string for the filter, only rows with their filterRole matching filterString will be displayed.
void invalidateFilter()
Invalidates the current filtering.
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
virtual QHash< int, QByteArray > roleNames() const const override
void clear()
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
Key key(const T &value) const const
void reserve(qsizetype size)
QJSValue toScriptValue(const T &value)
QJSValue call(const QJSValueList &args) const const
bool isCallable() const const
bool isError() const const
bool isNull() const const
bool toBool() const const
QString toString() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const const
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const const
void setFilterRole(int role)
void filterRoleChanged(int filterRole)
void setFilterFixedString(const QString &pattern)
virtual void setSourceModel(QAbstractItemModel *sourceModel) override
virtual void sort(int column, Qt::SortOrder order) override
void setSortRole(int role)
void sortRoleChanged(int sortRole)
QString fromUtf8(QByteArrayView str)
DisplayRole
AscendingOrder
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri May 2 2025 12:04:43 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.