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