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 */
39class 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
104public:
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
131public 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
142Q_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
152protected:
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
157protected 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
168private:
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

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