File indexing completed on 2025-01-26 05:09:00

0001 /*
0002     SPDX-FileCopyrightText: 2010 Marco Martin <mart@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef DATAMODEL_H
0008 #define DATAMODEL_H
0009 
0010 #include <QAbstractItemModel>
0011 #include <QJSValue>
0012 #include <QList>
0013 #include <QRegularExpression>
0014 #include <QSortFilterProxyModel>
0015 
0016 #include <Plasma5Support/DataEngine>
0017 
0018 class QTimer;
0019 
0020 // All classes here will hopefully be removed in KF6 (along with DataEngines in general)
0021 
0022 namespace Plasma5Support
0023 {
0024 class DataSource;
0025 class DataModel;
0026 
0027 /**
0028  * @class SortFilterModel
0029  * @short Filter and sort an existing QAbstractItemModel
0030  */
0031 class SortFilterModel : public QSortFilterProxyModel
0032 {
0033     Q_OBJECT
0034     /**
0035      * The source model of this sorting proxy model. It has to inherit QAbstractItemModel (ListModel is not supported)
0036      */
0037     Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setModel NOTIFY sourceModelChanged)
0038 
0039     /**
0040      * The regular expression for the filter, only items with their filterRole matching filterRegExp will be displayed
0041      */
0042     Q_PROPERTY(QString filterRegExp READ filterRegExp WRITE setFilterRegExp NOTIFY filterRegExpChanged)
0043 
0044     /**
0045      * The string for the filter, only items with their filterRole matching filterString will be displayed
0046      */
0047     Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged REVISION 1)
0048 
0049     /**
0050      * A JavaScript callable that is passed the source model row index as first argument and the value
0051      * of filterRole as second argument. 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 filterCallable 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     Q_PROPERTY(QJSValue filterCallback READ filterCallback WRITE setFilterCallback NOTIFY filterCallbackChanged REVISION 1)
0058 
0059     /**
0060      * The role of the sourceModel on which filterRegExp must be applied.
0061      */
0062     Q_PROPERTY(QString filterRole READ filterRole WRITE setFilterRole)
0063 
0064     /**
0065      * The role of the sourceModel that will be used for sorting. if empty the order will be left unaltered
0066      */
0067     Q_PROPERTY(QString sortRole READ sortRole WRITE setSortRole)
0068 
0069     /**
0070      * One of Qt.Ascending or Qt.Descending
0071      */
0072     Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder)
0073 
0074     /**
0075      * Specify which column should be used for sorting
0076      */
0077     Q_PROPERTY(int sortColumn READ sortColumn WRITE setSortColumn NOTIFY sortColumnChanged)
0078 
0079     /**
0080      * How many items are in this model
0081      */
0082     Q_PROPERTY(int count READ count NOTIFY countChanged)
0083 
0084     friend class DataModel;
0085 
0086 public:
0087     explicit SortFilterModel(QObject *parent = nullptr);
0088     ~SortFilterModel() override;
0089 
0090     void setModel(QAbstractItemModel *source);
0091 
0092     void setFilterRegExp(const QString &exp);
0093     QString filterRegExp() const;
0094 
0095     void setFilterString(const QString &filterString);
0096     QString filterString() const;
0097 
0098     void setFilterCallback(const QJSValue &callback);
0099     QJSValue filterCallback() const;
0100 
0101     void setFilterRole(const QString &role);
0102     QString filterRole() const;
0103 
0104     void setSortRole(const QString &role);
0105     QString sortRole() const;
0106 
0107     void setSortOrder(const Qt::SortOrder order);
0108 
0109     void setSortColumn(int column);
0110 
0111     int count() const
0112     {
0113         return QSortFilterProxyModel::rowCount();
0114     }
0115 
0116     /**
0117      * Returns the item at index in the list model.
0118      * This allows the item data to be accessed (but not modified) from JavaScript.
0119      * It returns an Object with a property for each role.
0120      *
0121      * @param i the row we want
0122      */
0123     Q_INVOKABLE QVariantMap get(int i) const;
0124 
0125     Q_INVOKABLE int mapRowToSource(int i) const;
0126 
0127     Q_INVOKABLE int mapRowFromSource(int i) const;
0128 
0129 Q_SIGNALS:
0130     void countChanged();
0131     void sortColumnChanged();
0132     void sourceModelChanged(QObject *);
0133     void filterRegExpChanged(const QString &);
0134     Q_REVISION(1) void filterStringChanged(const QString &);
0135     Q_REVISION(1) void filterCallbackChanged(const QJSValue &);
0136 
0137 protected:
0138     int roleNameToId(const QString &name) const;
0139     bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
0140     QHash<int, QByteArray> roleNames() const override;
0141 
0142 protected Q_SLOTS:
0143     void syncRoleNames();
0144 
0145 private:
0146     QString m_filterRole;
0147     QString m_sortRole;
0148     QString m_filterString;
0149     QJSValue m_filterCallback;
0150     QHash<QString, int> m_roleIds;
0151 };
0152 
0153 /**
0154  * @class DataModel
0155  * @short DataSource data as a model
0156  */
0157 class DataModel : public QAbstractItemModel
0158 {
0159     Q_OBJECT
0160 
0161     /**
0162      * The instance of DataSource to construct this model on
0163      */
0164     Q_PROPERTY(QObject *dataSource READ dataSource WRITE setDataSource)
0165 
0166     /**
0167      * It's a regular expression. Only data with keys that match this filter
0168      * expression will be inserted in the model
0169      */
0170     Q_PROPERTY(QString keyRoleFilter READ keyRoleFilter WRITE setKeyRoleFilter)
0171 
0172     /**
0173      * It's a regular expression. If the DataSource is connected to more than one source,
0174      * only inserts data from sources matching this filter expression in the model. If we
0175      * want to have a source watch all sources beginning with say "name:", the required
0176      * regexp would be sourceFilter: "name:.*"
0177      */
0178     Q_PROPERTY(QString sourceFilter READ sourceFilter WRITE setSourceFilter)
0179 
0180     /**
0181      * How many items are in this model
0182      */
0183     Q_PROPERTY(int count READ count NOTIFY countChanged)
0184 
0185 public:
0186     DataModel(QObject *parent = nullptr);
0187     ~DataModel() override;
0188 
0189     void setDataSource(QObject *source);
0190     QObject *dataSource() const;
0191 
0192     /**
0193      * Include only items with a key that matches this regexp in the model
0194      */
0195     void setKeyRoleFilter(const QString &key);
0196     QString keyRoleFilter() const;
0197 
0198     /**
0199      * Include only sources that matches this regexp in the model
0200      */
0201     void setSourceFilter(const QString &key);
0202     QString sourceFilter() const;
0203 
0204     // Reimplemented
0205     QVariant data(const QModelIndex &index, int role) const override;
0206     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
0207     QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
0208     QModelIndex parent(const QModelIndex &child) const override;
0209     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
0210     int columnCount(const QModelIndex &parent = QModelIndex()) const override;
0211 
0212     int count() const
0213     {
0214         return countItems();
0215     }
0216 
0217     /**
0218      * Returns the item at index in the list model.
0219      * This allows the item data to be accessed (but not modified) from JavaScript.
0220      * It returns an Object with a property for each role.
0221      *
0222      * @param i the row we want
0223      */
0224     Q_INVOKABLE QVariantMap get(int i) const;
0225 
0226 protected:
0227     void setItems(const QString &sourceName, const QVariantList &list);
0228     inline int countItems() const;
0229     QHash<int, QByteArray> roleNames() const override;
0230 Q_SIGNALS:
0231     void countChanged();
0232     void sourceModelChanged(QObject *);
0233     void filterRegExpChanged(const QString &);
0234 
0235 private Q_SLOTS:
0236     void dataUpdated(const QString &sourceName, const QVariantMap &data);
0237     void removeSource(const QString &sourceName);
0238 
0239 private:
0240     DataSource *m_dataSource;
0241     QString m_keyRoleFilter;
0242     QRegularExpression m_keyRoleFilterRE;
0243     QString m_sourceFilter;
0244     QRegularExpression m_sourceFilterRE;
0245     QMap<QString, QList<QVariant>> m_items;
0246     QHash<int, QByteArray> m_roleNames;
0247     QHash<QString, int> m_roleIds;
0248     int m_maxRoleId;
0249 };
0250 
0251 int DataModel::countItems() const
0252 {
0253     int count = 0;
0254     for (const QList<QVariant> &v : std::as_const(m_items)) {
0255         count += v.count();
0256     }
0257     return count;
0258 }
0259 
0260 }
0261 #endif