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#ifndef KSORTFILTERPROXYMODEL_H
9#define KSORTFILTERPROXYMODEL_H
10
11#include <QAbstractItemModel>
12#include <QJSValue>
13#include <QList>
14#include <QQmlParserStatus>
15#include <QSortFilterProxyModel>
16#include <qqmlregistration.h>
17
18#include <array>
19
20/*!
21 * \qmltype KSortFilterProxyModel
22 * \inqmlmodule org.kde.kitemmodels
23 * \brief Filter and sort an existing QAbstractItemModel.
24 *
25 * \since 5.67
26 */
27class KSortFilterProxyModel : public QSortFilterProxyModel, public QQmlParserStatus
28{
29 Q_OBJECT
30 QML_ELEMENT
31 Q_INTERFACES(QQmlParserStatus)
32
33 /*!
34 * \qmlproperty string KSortFilterProxyModel::filterString
35 * The string for the filter, only rows with their filterRole matching filterString will be displayed
36 */
37 Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged)
38 /*!
39 * \qmlproperty var KSortFilterProxyModel::filterRowCallback
40 *
41 * A JavaScript callable that can be used to perform advanced filters on a given row.
42 * The callback is passed the source row, and source parent for a given row as arguments
43 *
44 * The callable's return value is evaluated as boolean to determine
45 * whether the row is accepted (true) or filtered out (false). It overrides the default implementation
46 * that uses filterRegExp or filterString; while filterCallback is set those two properties are
47 * ignored. Attempts to write a non-callable to this property are silently ignored, but you can set
48 * it to null.
49 *
50 * \code
51 * filterRowCallback: function(source_row, source_parent) {
52 * return sourceModel.data(sourceModel.index(source_row, 0, source_parent), Qt.DisplayRole) == "...";
53 * };
54 * \endcode
55 */
56 Q_PROPERTY(QJSValue filterRowCallback READ filterRowCallback WRITE setFilterRowCallback NOTIFY filterRowCallbackChanged)
57
58 /*!
59 * \qmlproperty var KSortFilterProxyModel::filterColumnCallback
60 *
61 * A JavaScript callable that can be used to perform advanced filters on a given column.
62 * The callback is passed the source column, and source parent for a given column as arguments.
63 *
64 * \see filterRowCallback
65 */
66 Q_PROPERTY(QJSValue filterColumnCallback READ filterColumnCallback WRITE setFilterColumnCallback NOTIFY filterColumnCallbackChanged)
67
68 /*!
69 * \qmlproperty string KSortFilterProxyModel::filterRoleName
70 *
71 * The role of the sourceModel on which the filter will be applied.
72 * This can either be the numerical role value or the role name as a string.
73 */
74 Q_PROPERTY(QString filterRoleName READ filterRoleName WRITE setFilterRoleName NOTIFY filterRoleNameChanged)
75
76 /*!
77 * \qmlproperty string KSortFilterProxyModel::sortRoleName
78 *
79 * The role of the sourceModel that will be used for sorting. if empty the order will be left unaltered
80 * This can either be the numerical role value or the role name as a string.
81 */
82 Q_PROPERTY(QString sortRoleName READ sortRoleName WRITE setSortRoleName NOTIFY sortRoleNameChanged)
83
84 /*!
85 * \qmlproperty Qt::SortOrder KSortFilterProxyModel::sortOrder
86 *
87 * One of Qt.AscendingOrder or Qt.DescendingOrder
88 */
89 Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged)
90
91 /*!
92 * \qmlproperty int KSortFilterProxyModel::sortColumn
93 *
94 * Specify which column should be used for sorting
95 * The default value is -1.
96 * If \a sortRole is set, the default value is 0.
97 */
98 Q_PROPERTY(int sortColumn READ sortColumn WRITE setSortColumn NOTIFY sortColumnChanged)
99
100 /*!
101 * \qmlproperty int KSortFilterProxyModel::count
102 *
103 * The number of top level rows.
104 */
105 Q_PROPERTY(int count READ rowCount NOTIFY rowCountChanged)
106
107public:
108 explicit KSortFilterProxyModel(QObject *parent = nullptr);
109 ~KSortFilterProxyModel() override;
110
111 void setSourceModel(QAbstractItemModel *sourceModel) override;
112
113 void setFilterRowCallback(const QJSValue &callback);
114 QJSValue filterRowCallback() const;
115
116 void setFilterString(const QString &filterString);
117 QString filterString() const;
118
119 void setFilterColumnCallback(const QJSValue &callback);
120 QJSValue filterColumnCallback() const;
121
122 void setFilterRoleName(const QString &roleName);
123 QString filterRoleName() const;
124
125 void setSortRoleName(const QString &roleName);
126 QString sortRoleName() const;
127
128 void setSortOrder(const Qt::SortOrder order);
129 void setSortColumn(int column);
130
131 void classBegin() override;
132 void componentComplete() override;
133
134public Q_SLOTS:
135 /*!
136 * \qmlmethod KSortFilterProxyModel::invalidateFilter
137 * Invalidates the current filtering.
138 *
139 * This function should be called if you are implementing custom filtering through
140 * filterRowCallback or filterColumnCallback, and your filter parameters have changed.
141 *
142 * \since 5.70
143 */
144 void invalidateFilter();
145
146Q_SIGNALS:
147 void filterStringChanged();
148 void filterRoleNameChanged();
149 void sortRoleNameChanged();
150 void sortOrderChanged();
151 void sortColumnChanged();
152 void filterRowCallbackChanged(const QJSValue &);
153 void filterColumnCallbackChanged(const QJSValue &);
154 void rowCountChanged();
155
156protected:
157 int roleNameToId(const QString &name) const;
158 bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
159 bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override;
160
161protected Q_SLOTS:
162 // This method is called whenever we suspect that role names mapping might have gone stale.
163 // It must not alter the source of truth for sort/filter properties.
164 void syncRoleNames();
165 // These methods are dealing with individual pairs of properties. They are
166 // called on various occasions, and need to check whether the invocation
167 // has been caused by a standalone base type's property change
168 // (switching source of truth to role ID) or as a side-effect of a sync.
169 void syncSortRoleProperties();
170 void syncFilterRoleProperties();
171
172private:
173 // conveniently, role ID is the default source of truth, turning it into
174 // zero-initialization.
175 enum SourceOfTruthForRoleProperty : bool {
176 SourceOfTruthIsRoleID = false,
177 SourceOfTruthIsRoleName = true,
178 };
179
180 bool m_componentCompleted : 1;
181 SourceOfTruthForRoleProperty m_sortRoleSourceOfTruth : 1;
182 SourceOfTruthForRoleProperty m_filterRoleSourceOfTruth : 1;
183 bool m_sortRoleGuard : 1;
184 bool m_filterRoleGuard : 1;
185 // default role name corresponds to the standard mapping of the default Qt::DisplayRole in QAbstractItemModel::roleNames
186 QString m_sortRoleName{QStringLiteral("display")};
187 QString m_filterRoleName{QStringLiteral("display")};
188 QString m_filterString;
189 QJSValue m_filterRowCallback;
190 QJSValue m_filterColumnCallback;
191 QHash<QString, int> m_roleIds;
192 std::array<QMetaObject::Connection, 3> m_sourceModelConnections;
193};
194
195#endif
196

source code of kitemmodels/src/qml/ksortfilterproxymodel.h