File indexing completed on 2024-04-28 03:56:15

0001 /*
0002  *   Copyright 2010 by Marco Martin <mart@kde.org>
0003  *   Copyright 2019 by David Edmundson <davidedmundson@kde.org>
0004  *
0005  *   This program is free software; you can redistribute it and/or modify
0006  *   it under the terms of the GNU Library General Public License as
0007  *   published by the Free Software Foundation; either version 2, or
0008  *   (at your option) any later version.
0009  *
0010  *   This program is distributed in the hope that it will be useful,
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  *   GNU General Public License for more details
0014  *
0015  *   You should have received a copy of the GNU Library General Public
0016  *   License along with this program; if not, write to the
0017  *   Free Software Foundation, Inc.,
0018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
0019  */
0020 
0021 #ifndef KSORTFILTERPROXYMODEL_H
0022 #define KSORTFILTERPROXYMODEL_H
0023 
0024 #include <QAbstractItemModel>
0025 #include <QJSValue>
0026 #include <QList>
0027 #include <QQmlParserStatus>
0028 #include <QSortFilterProxyModel>
0029 
0030 #include <array>
0031 
0032 /**
0033  * @class SortFilterModel
0034  * @short Filter and sort an existing QAbstractItemModel
0035  *
0036  * @since 5.67
0037  */
0038 class KSortFilterProxyModel : public QSortFilterProxyModel, public QQmlParserStatus
0039 {
0040     Q_OBJECT
0041     Q_INTERFACES(QQmlParserStatus)
0042 
0043     /**
0044      * The string for the filter, only rows with their filterRole matching filterString will be displayed
0045      */
0046     Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged)
0047     /**
0048      * A JavaScript callable that can be used to perform advanced filters on a given row.
0049      * The callback is passed the source row, and source parent for a given row as arguments
0050      *
0051      * The callable's return value is evaluated as boolean to determine
0052      * whether the row is accepted (true) or filtered out (false). It overrides the default implementation
0053      * that uses filterRegExp or filterString; while filterCallback is set those two properties are
0054      * ignored. Attempts to write a non-callable to this property are silently ignored, but you can set
0055      * it to null.
0056      *
0057      * @code
0058      * filterRowCallback: function(source_row, source_parent) {
0059      *   return sourceModel.data(sourceModel.index(source_row, 0, source_parent), Qt.DisplayRole) == "...";
0060      * };
0061      * @endcode
0062      */
0063     Q_PROPERTY(QJSValue filterRowCallback READ filterRowCallback WRITE setFilterRowCallback NOTIFY filterRowCallbackChanged)
0064 
0065     /**
0066      * A JavaScript callable that can be used to perform advanced filters on a given column.
0067      * The callback is passed the source column, and source parent for a given column as arguments.
0068      *
0069      * @see filterRowCallback
0070      */
0071     Q_PROPERTY(QJSValue filterColumnCallback READ filterColumnCallback WRITE setFilterColumnCallback NOTIFY filterColumnCallbackChanged)
0072 
0073     /**
0074      * The role of the sourceModel on which the filter will be applied.
0075      * This can either be the numerical role value or the role name as a string.
0076      */
0077     Q_PROPERTY(QString filterRoleName READ filterRoleName WRITE setFilterRoleName NOTIFY filterRoleNameChanged)
0078 
0079     /**
0080      * The role of the sourceModel that will be used for sorting. if empty the order will be left unaltered
0081      * This can either be the numerical role value or the role name as a string.
0082      */
0083     Q_PROPERTY(QString sortRoleName READ sortRoleName WRITE setSortRoleName NOTIFY sortRoleNameChanged)
0084 
0085     /**
0086      * One of Qt.AscendingOrder or Qt.DescendingOrder
0087      */
0088     Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged)
0089 
0090     /**
0091      * Specify which column should be used for sorting
0092      * The default value is -1.
0093      * If \a sortRole is set, the default value is 0.
0094      */
0095     Q_PROPERTY(int sortColumn READ sortColumn WRITE setSortColumn NOTIFY sortColumnChanged)
0096 
0097     /**
0098      * The number of top level rows.
0099      */
0100     Q_PROPERTY(int count READ rowCount NOTIFY rowCountChanged)
0101 
0102 public:
0103     explicit KSortFilterProxyModel(QObject *parent = nullptr);
0104     ~KSortFilterProxyModel() override;
0105 
0106     void setSourceModel(QAbstractItemModel *sourceModel) override;
0107 
0108     void setFilterRowCallback(const QJSValue &callback);
0109     QJSValue filterRowCallback() const;
0110 
0111     void setFilterString(const QString &filterString);
0112     QString filterString() const;
0113 
0114     void setFilterColumnCallback(const QJSValue &callback);
0115     QJSValue filterColumnCallback() const;
0116 
0117     void setFilterRoleName(const QString &roleName);
0118     QString filterRoleName() const;
0119 
0120     void setSortRoleName(const QString &roleName);
0121     QString sortRoleName() const;
0122 
0123     void setSortOrder(const Qt::SortOrder order);
0124     void setSortColumn(int column);
0125 
0126     void classBegin() override;
0127     void componentComplete() override;
0128 
0129 public Q_SLOTS:
0130     /**
0131      * Invalidates the current filtering.
0132      *
0133      * This function should be called if you are implementing custom filtering through
0134      * filterRowCallback or filterColumnCallback, and your filter parameters have changed.
0135      *
0136      * @since 5.70
0137      */
0138     void invalidateFilter();
0139 
0140 Q_SIGNALS:
0141     void filterStringChanged();
0142     void filterRoleNameChanged();
0143     void sortRoleNameChanged();
0144     void sortOrderChanged();
0145     void sortColumnChanged();
0146     void filterRowCallbackChanged(const QJSValue &);
0147     void filterColumnCallbackChanged(const QJSValue &);
0148     void rowCountChanged();
0149 
0150 protected:
0151     int roleNameToId(const QString &name) const;
0152     bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
0153     bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override;
0154 
0155 protected Q_SLOTS:
0156     // This method is called whenever we suspect that role names mapping might have gone stale.
0157     // It must not alter the source of truth for sort/filter properties.
0158     void syncRoleNames();
0159     // These methods are dealing with individual pairs of properties. They are
0160     // called on various occasions, and need to check whether the invocation
0161     // has been caused by a standalone base type's property change
0162     // (switching source of truth to role ID) or as a side-effect of a sync.
0163     void syncSortRoleProperties();
0164     void syncFilterRoleProperties();
0165 
0166 private:
0167     // conveniently, role ID is the default source of truth, turning it into
0168     // zero-initialization.
0169     enum SourceOfTruthForRoleProperty : bool {
0170         SourceOfTruthIsRoleID = false,
0171         SourceOfTruthIsRoleName = true,
0172     };
0173 
0174     bool m_componentCompleted : 1;
0175     SourceOfTruthForRoleProperty m_sortRoleSourceOfTruth : 1;
0176     SourceOfTruthForRoleProperty m_filterRoleSourceOfTruth : 1;
0177     bool m_sortRoleGuard : 1;
0178     bool m_filterRoleGuard : 1;
0179     // default role name corresponds to the standard mapping of the default Qt::DisplayRole in QAbstractItemModel::roleNames
0180     QString m_sortRoleName{QStringLiteral("display")};
0181     QString m_filterRoleName{QStringLiteral("display")};
0182     QString m_filterString;
0183     QJSValue m_filterRowCallback;
0184     QJSValue m_filterColumnCallback;
0185     QHash<QString, int> m_roleIds;
0186     std::array<QMetaObject::Connection, 3> m_sourceModelConnections;
0187 };
0188 
0189 #endif