1 /*
2  *  resourcemodel.h  -  models containing flat list of resources
3  *  Program:  kalarm
4  *  SPDX-FileCopyrightText: 2010-2021 David Jarvie <djarvie@kde.org>
5  *
6  *  SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #pragma once
10 
11 #include "resource.h"
12 
13 #include <KAlarmCal/KACalendar>
14 
15 #include <KDescendantsProxyModel>
16 #include <KCheckableProxyModel>
17 
18 #include <QSortFilterProxyModel>
19 #include <QListView>
20 
21 using namespace KAlarmCal;
22 
23 /*=============================================================================
24 = Class: ResourceFilterModel
25 = Proxy model to filter a resource data model to restrict its contents to
26 = resources, not events, containing specified alarm types.
27 = It can optionally be restricted to writable and/or enabled resources.
28 =============================================================================*/
29 class ResourceFilterModel : public QSortFilterProxyModel
30 {
31     Q_OBJECT
32 public:
33     /** Constructs a new instance.
34      *  @tparam DataModel  The data model class to use as the source model. It must
35      *                     have the following methods:
36      *                     static Model* instance(); - returns the unique instance.
37      *                     QModelIndex resourceIndex(const Resource&) const;
38      */
39     template <class DataModel>
40     static ResourceFilterModel* create(QObject* parent = nullptr);
41 
42     /** Set the alarm type to include in the model.
43      *  @param type  If EMPTY, include all alarm types;
44      *               otherwise, include only resources with the specified alarm type.
45      */
46     void setEventTypeFilter(CalEvent::Type);
47 
48     /** Filter on resources' writable status.
49      *  @param enabled  If true, only include writable resources;
50      *                  if false, ignore writable status.
51      */
52     void setFilterWritable(bool writable);
53 
54     /** Filter on resources' enabled status.
55      *  @param enabled  If true, only include enabled resources;
56      *                  if false, ignore enabled status.
57      */
58     void setFilterEnabled(bool enabled);
59 
60     /** Filter on resources' display names, using a simple text search. */
61     void setFilterText(const QString& text);
62 
63     QModelIndex resourceIndex(const Resource&) const;
64 
65     bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
66     bool canFetchMore(const QModelIndex &parent) const override;
67     QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override;
68     int columnCount(const QModelIndex &parent = QModelIndex()) const override;
69 
70 protected:
71     bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
72     bool filterAcceptsColumn(int sourceColumn, const QModelIndex &sourceParent) const override;
73 
74 private:
75     explicit ResourceFilterModel(QObject* parent);
76 
77     QModelIndex  (*mResourceIndexFunction)(const Resource&) {nullptr};  // function to fetch resource index from data model
78     QString        mFilterText;           // only include resources whose display names include this
79     CalEvent::Type mAlarmType {CalEvent::EMPTY};  // only include resources with this alarm type
80     bool           mWritableOnly {false}; // only include writable resources
81     bool           mEnabledOnly {false};  // only include enabled resources
82 };
83 
84 /*=============================================================================
85 = Class: ResourceListModel
86 = Proxy model converting the resource tree into a flat list.
87 = The model may be restricted to specified alarm types.
88 = It can optionally be restricted to writable and/or enabled resources.
89 =============================================================================*/
90 class ResourceListModel : public KDescendantsProxyModel
91 {
92     Q_OBJECT
93 public:
94     /** Constructs a new instance.
95      *  @tparam DataModel  The data model class to use as the source model. It must
96      *                     have the following methods:
97      *                     static DataModel* instance(); - returns the unique instance.
98      *                     QModelIndex resourceIndex(const Resource&) const;
99      */
100     template <class DataModel>
101     static ResourceListModel* create(QObject* parent = nullptr);
102 
103     void         setEventTypeFilter(CalEvent::Type);
104     void         setFilterWritable(bool writable);
105     void         setFilterEnabled(bool enabled);
106     void         setFilterText(const QString& text);
useResourceColour(bool use)107     void         useResourceColour(bool use)   { mUseResourceColour = use; }
108     Resource     resource(int row) const;
109     Resource     resource(const QModelIndex&) const;
110     QModelIndex  resourceIndex(const Resource&) const;
111     virtual bool isDescendantOf(const QModelIndex& ancestor, const QModelIndex& descendant) const;
112     QVariant     data(const QModelIndex&, int role = Qt::DisplayRole) const override;
113 
114 private:
115     explicit ResourceListModel(QObject* parent);
116 
117     bool mUseResourceColour {true};
118 };
119 
120 
121 /*=============================================================================
122 = Class: ResourceCheckListModel
123 = Proxy model providing a checkable list of all Resources.
124 = An alarm type is specified, whereby Resources which are enabled for that
125 = alarm type are checked; Resources which do not contain that alarm type, or
126 = which are disabled for that alarm type, are unchecked.
127 =============================================================================*/
128 class ResourceCheckListModel : public KCheckableProxyModel
129 {
130     Q_OBJECT
131 public:
132     /** Constructs a new instance.
133      *  @tparam DataModel  The data model class to use as the source model. It must
134      *                     have the following methods:
135      *                     static DataModel* instance(); - returns the unique instance.
136      *                     QModelIndex resourceIndex(const Resource&) const;
137      */
138     template <class DataModel>
139     static ResourceCheckListModel* create(CalEvent::Type, QObject* parent = nullptr);
140 
141     ~ResourceCheckListModel() override;
142     Resource resource(int row) const;
143     Resource resource(const QModelIndex&) const;
disable()144     void disable()    { mDisabled = true; }
145     QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override;
146     bool setData(const QModelIndex&, const QVariant& value, int role) override;
147 
148 Q_SIGNALS:
149     void resourceTypeChange(ResourceCheckListModel*);
150 
151 private Q_SLOTS:
152     void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
153     void slotRowsInsertedRemoved();
154     void resourceSettingsChanged(Resource&, ResourceType::Changes);
155 
156 private:
157     ResourceCheckListModel(CalEvent::Type, QObject* parent);
158     void init();
159     void setSelectionStatus(const Resource&, const QModelIndex&);
160     QByteArray debugType(const char* func) const;
161 
162     static ResourceListModel* mModel;
163     static int                mInstanceCount;
164     CalEvent::Type            mAlarmType;     // alarm type contained in this model
165     QItemSelectionModel*      mSelectionModel;
166     bool                      mResetting {false};   // currently handling rows inserted/removed
167     bool                      mDisabled {false};    // resources are being deleted on program exit
168 };
169 
170 
171 /*=============================================================================
172 = Class: ResourceFilterCheckListModel
173 = Proxy model providing a checkable resource list, filtered to contain only one
174 = alarm type. The selected alarm type may be changed as desired.
175 =============================================================================*/
176 class ResourceFilterCheckListModel : public QSortFilterProxyModel
177 {
178     Q_OBJECT
179 public:
180     /** Constructs an instance.
181      *  @tparam DataModel  The data model class to use as the source model. It must
182      *                     have the following methods:
183      *                     static DataModel* instance(); - returns the unique instance.
184      *                     QModelIndex resourceIndex(const Resource&) const;
185      *                     QString tooltip(const Resource&, CalEvent::Types) const;
186      */
187     template <class DataModel>
188     static ResourceFilterCheckListModel* create(QObject* parent = nullptr);
189 
190     ~ResourceFilterCheckListModel() override;
191     void setEventTypeFilter(CalEvent::Type);
192     Resource resource(int row) const;
193     Resource resource(const QModelIndex&) const;
194     static void disable();
195     QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override;
196 
197 protected:
198     bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
199 
200 private Q_SLOTS:
201     void resourceTypeChanged(ResourceCheckListModel*);
202     void slotRowsAboutToBeInserted(const QModelIndex& parent, int start, int end);
203     void slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end);
204     void slotRowsInserted();
205     void slotRowsRemoved();
206 
207 private:
208     explicit ResourceFilterCheckListModel(QObject* parent);
209     void init();
210 
211     static QVector<ResourceFilterCheckListModel*> mInstances;
212     ResourceCheckListModel* mActiveModel {nullptr};
213     ResourceCheckListModel* mArchivedModel {nullptr};
214     ResourceCheckListModel* mTemplateModel {nullptr};
215     CalEvent::Type          mAlarmType {CalEvent::EMPTY};  // alarm type contained in this model
216     QString               (*mTooltipFunction)(const Resource&, CalEvent::Types) {nullptr};  // function to fetch tooltip from data model
217 };
218 
219 
220 /*=============================================================================
221 = Class: ResourceView
222 = View for a ResourceFilterCheckListModel.
223 =============================================================================*/
224 class ResourceView : public QListView
225 {
226     Q_OBJECT
227 public:
228     explicit ResourceView(ResourceFilterCheckListModel*, QWidget* parent = nullptr);
229     ResourceFilterCheckListModel* resourceModel() const;
230     Resource  resource(int row) const;
231     Resource  resource(const QModelIndex&) const;
232 
233 protected:
234     void mouseReleaseEvent(QMouseEvent*) override;
235     bool viewportEvent(QEvent*) override;
236 };
237 
238 
239 /*=============================================================================
240 * Template definitions.
241 *============================================================================*/
242 
243 template <class DataModel>
create(QObject * parent)244 ResourceFilterModel* ResourceFilterModel::create(QObject* parent)
245 {
246     auto model = new ResourceFilterModel(parent);
247     model->setSourceModel(DataModel::instance());
248     model->mResourceIndexFunction = [](const Resource& r) { return DataModel::instance()->resourceIndex(r); };
249     return model;
250 }
251 
252 template <class DataModel>
create(QObject * parent)253 ResourceListModel* ResourceListModel::create(QObject* parent)
254 {
255     auto model = new ResourceListModel(parent);
256     model->setSourceModel(ResourceFilterModel::create<DataModel>(model));
257     return model;
258 }
259 
260 template <class DataModel>
create(CalEvent::Type type,QObject * parent)261 ResourceCheckListModel* ResourceCheckListModel::create(CalEvent::Type type, QObject* parent)
262 {
263     auto model = new ResourceCheckListModel(type, parent);
264     if (!mModel)
265         mModel = ResourceListModel::create<DataModel>(nullptr);
266     model->init();
267     return model;
268 }
269 
270 template <class DataModel>
create(QObject * parent)271 ResourceFilterCheckListModel* ResourceFilterCheckListModel::create(QObject* parent)
272 {
273     ResourceFilterCheckListModel* instance = new ResourceFilterCheckListModel(parent);
274     mInstances.append(instance);
275     instance->mActiveModel   = ResourceCheckListModel::create<DataModel>(CalEvent::ACTIVE, instance);
276     instance->mArchivedModel = ResourceCheckListModel::create<DataModel>(CalEvent::ARCHIVED, instance);
277     instance->mTemplateModel = ResourceCheckListModel::create<DataModel>(CalEvent::TEMPLATE, instance);
278     instance->mTooltipFunction = [](const Resource& r, CalEvent::Types t) { return DataModel::instance()->tooltip(r, t); };
279     instance->init();
280     return instance;
281 }
282 
283 
284 // vim: et sw=4:
285