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 | */ |
27 | class 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 | |
107 | public: |
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 | |
134 | public 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 | |
146 | Q_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 | |
156 | protected: |
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 | |
161 | protected 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 | |
172 | private: |
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 | |