PlasmaActivities

activitiesmodel.cpp
1/*
2 SPDX-FileCopyrightText: 2012-2016 Ivan Cukic <ivan.cukic(at)kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7// Self
8#include "activitiesmodel.h"
9#include "activitiesmodel_p.h"
10
11// Qt
12#include <QByteArray>
13#include <QDBusPendingCall>
14#include <QDBusPendingCallWatcher>
15#include <QDebug>
16#include <QFutureWatcher>
17#include <QHash>
18#include <QModelIndex>
19
20// Local
21#include "utils/remove_if.h"
22
23namespace KActivities
24{
25namespace Private
26{
27template<typename _Container>
28struct ActivityPosition {
29 ActivityPosition()
30 : isValid(false)
31 , index(0)
32 , iterator()
33 {
34 }
35
36 ActivityPosition(unsigned int index, typename _Container::const_iterator iterator)
37 : isValid(true)
38 , index(index)
39 , iterator(iterator)
40 {
41 }
42
43 operator bool() const
44 {
45 return isValid;
46 }
47
48 const bool isValid;
49 const unsigned int index;
50 const typename _Container::const_iterator iterator;
51
52 typedef typename _Container::value_type ContainerElement;
53};
54
55/**
56 * Returns whether the activity has a desired state.
57 * If the state is 0, returns true
58 */
59template<typename T>
60inline bool matchingState(ActivitiesModelPrivate::InfoPtr activity, const T &states)
61{
62 return states.empty() || states.contains(activity->state());
63}
64
65/**
66 * Searches for the activity.
67 * Returns an option(index, iterator) for the found activity.
68 */
69template<typename _Container>
70inline ActivityPosition<_Container> activityPosition(const _Container &container, const QString &activityId)
71{
72 auto position = std::find_if(container.begin(), container.end(), [&](const typename ActivityPosition<_Container>::ContainerElement &activity) {
73 return activity->id() == activityId;
74 });
75
76 return (position != container.end()) ? ActivityPosition<_Container>(position - container.begin(), position) : ActivityPosition<_Container>();
77}
78
79/**
80 * Notifies the model that an activity was updated
81 */
82template<typename _Model, typename _Container>
83inline void emitActivityUpdated(_Model *model, const _Container &container, const QString &activity, int role)
84{
85 auto position = Private::activityPosition(container, activity);
86
87 if (position) {
88 Q_EMIT model->q->dataChanged(model->q->index(position.index),
89 model->q->index(position.index),
90 role == Qt::DecorationRole ? QList<int>{role, ActivitiesModel::ActivityIconSource} : QList<int>{role});
91 }
92}
93
94/**
95 * Notifies the model that an activity was updated
96 */
97template<typename _Model, typename _Container>
98inline void emitActivityUpdated(_Model *model, const _Container &container, QObject *activityInfo, int role)
99{
100 const auto activity = static_cast<Info *>(activityInfo);
101 emitActivityUpdated(model, container, activity->id(), role);
102}
103
104}
105
106ActivitiesModelPrivate::ActivitiesModelPrivate(ActivitiesModel *parent)
107 : q(parent)
108{
109}
110
111ActivitiesModel::ActivitiesModel(QObject *parent)
112 : QAbstractListModel(parent)
113 , d(new ActivitiesModelPrivate(this))
114{
115 // Initializing role names for qml
116 connect(&d->activities, &Consumer::serviceStatusChanged, this, [this](Consumer::ServiceStatus status) {
117 d->setServiceStatus(status);
118 });
119
120 connect(&d->activities, &Consumer::activityAdded, this, [this](const QString &activity) {
121 d->onActivityAdded(activity);
122 });
123 connect(&d->activities, &Consumer::activityRemoved, this, [this](const QString &activity) {
124 d->onActivityRemoved(activity);
125 });
126 connect(&d->activities, &Consumer::currentActivityChanged, this, [this](const QString &activity) {
127 d->onCurrentActivityChanged(activity);
128 });
129
130 d->setServiceStatus(d->activities.serviceStatus());
131}
132
133ActivitiesModel::ActivitiesModel(QList<Info::State> shownStates, QObject *parent)
135 , d(new ActivitiesModelPrivate(this))
136{
137 d->shownStates = shownStates;
138
139 // Initializing role names for qml
141 d->setServiceStatus(status);
142 });
143
144 connect(&d->activities, &Consumer::activityAdded, this, [this](const QString &activity) {
145 d->onActivityAdded(activity);
146 });
147 connect(&d->activities, &Consumer::activityRemoved, this, [this](const QString &activity) {
148 d->onActivityRemoved(activity);
149 });
150 connect(&d->activities, &Consumer::currentActivityChanged, this, [this](const QString &activity) {
151 d->onCurrentActivityChanged(activity);
152 });
153
154 d->setServiceStatus(d->activities.serviceStatus());
155}
156
157ActivitiesModel::~ActivitiesModel() = default;
158
159QHash<int, QByteArray> ActivitiesModel::roleNames() const
160{
161 return {{ActivityName, "name"},
162 {ActivityState, "state"},
163 {ActivityId, "id"},
164 {ActivityIconSource, "iconSource"},
165 {ActivityDescription, "description"},
166 {ActivityBackground, "background"},
167 {ActivityIsCurrent, "isCurrent"}};
168}
169
170void ActivitiesModelPrivate::setServiceStatus(Consumer::ServiceStatus)
171{
172 replaceActivities(activities.activities());
173}
174
175void ActivitiesModelPrivate::replaceActivities(const QStringList &activities)
176{
177 q->beginResetModel();
178
179 knownActivities.clear();
180 shownActivities.clear();
181
182 for (const QString &activity : activities) {
183 onActivityAdded(activity, false);
184 }
185
186 q->endResetModel();
187}
188
189void ActivitiesModelPrivate::onActivityAdded(const QString &id, bool notifyClients)
190{
191 auto info = registerActivity(id);
192
193 showActivity(info, notifyClients);
194}
195
196void ActivitiesModelPrivate::onActivityRemoved(const QString &id)
197{
198 hideActivity(id);
199 unregisterActivity(id);
200}
201
202void ActivitiesModelPrivate::onCurrentActivityChanged(const QString &id)
203{
204 Q_UNUSED(id);
205
206 for (const auto &activity : shownActivities) {
207 Private::emitActivityUpdated(this, shownActivities, activity->id(), ActivitiesModel::ActivityIsCurrent);
208 }
209}
210
211ActivitiesModelPrivate::InfoPtr ActivitiesModelPrivate::registerActivity(const QString &id)
212{
213 auto position = Private::activityPosition(knownActivities, id);
214
215 if (position) {
216 return *(position.iterator);
217
218 } else {
219 auto activityInfo = std::make_shared<Info>(id);
220
221 auto ptr = activityInfo.get();
222
223 connect(ptr, &Info::nameChanged, this, &ActivitiesModelPrivate::onActivityNameChanged);
224 connect(ptr, &Info::descriptionChanged, this, &ActivitiesModelPrivate::onActivityDescriptionChanged);
225 connect(ptr, &Info::iconChanged, this, &ActivitiesModelPrivate::onActivityIconChanged);
226 connect(ptr, &Info::stateChanged, this, &ActivitiesModelPrivate::onActivityStateChanged);
227
228 knownActivities.insert(InfoPtr(activityInfo));
229
230 return activityInfo;
231 }
232}
233
234void ActivitiesModelPrivate::unregisterActivity(const QString &id)
235{
236 auto position = Private::activityPosition(knownActivities, id);
237
238 if (position) {
239 if (auto shown = Private::activityPosition(shownActivities, id)) {
240 q->beginRemoveRows(QModelIndex(), shown.index, shown.index);
241 shownActivities.removeAt(shown.index);
242 q->endRemoveRows();
243 }
244
245 knownActivities.removeAt(position.index);
246 }
247}
248
249void ActivitiesModelPrivate::showActivity(InfoPtr activityInfo, bool notifyClients)
250{
251 // Should it really be shown?
252 if (!Private::matchingState(activityInfo, shownStates)) {
253 return;
254 }
255
256 // Is it already shown?
257 if (std::binary_search(shownActivities.cbegin(), shownActivities.cend(), activityInfo, InfoPtrComparator())) {
258 return;
259 }
260
261 auto registeredPosition = Private::activityPosition(knownActivities, activityInfo->id());
262
263 if (!registeredPosition) {
264 qDebug() << "Got a request to show an unknown activity, ignoring";
265 return;
266 }
267
268 const auto activityInfoPtr = *(registeredPosition.iterator);
269
270 // In C++17, this would be:
271 // const auto [iterator, index, found] = shownActivities.insert(...);
272 const auto _result = shownActivities.insert(activityInfoPtr);
273 // const auto iterator = std::get<0>(_result);
274 const auto index = std::get<1>(_result);
275
276 if (notifyClients) {
277 q->beginInsertRows(QModelIndex(), index, index);
278 q->endInsertRows();
279 }
280}
281
282void ActivitiesModelPrivate::hideActivity(const QString &id)
283{
284 auto position = Private::activityPosition(shownActivities, id);
285
286 if (position) {
287 q->beginRemoveRows(QModelIndex(), position.index, position.index);
288 shownActivities.removeAt(position.index);
289 q->endRemoveRows();
290 }
291}
292
293// clang-format off
294#define CREATE_SIGNAL_EMITTER(What,Role) \
295 void ActivitiesModelPrivate::onActivity##What##Changed(const QString &) \
296 { \
297 Private::emitActivityUpdated(this, shownActivities, sender(), Role); \
298 }
299// clang-format on
300
301CREATE_SIGNAL_EMITTER(Name, Qt::DisplayRole)
302CREATE_SIGNAL_EMITTER(Description, ActivitiesModel::ActivityDescription)
303CREATE_SIGNAL_EMITTER(Icon, Qt::DecorationRole)
304
305#undef CREATE_SIGNAL_EMITTER
306
307void ActivitiesModelPrivate::onActivityStateChanged(Info::State state)
308{
309 if (shownStates.empty()) {
310 Private::emitActivityUpdated(this, shownActivities, sender(), ActivitiesModel::ActivityState);
311
312 } else {
313 auto info = findActivity(sender());
314
315 if (!info) {
316 return;
317 }
318
319 if (shownStates.contains(state)) {
320 showActivity(info, true);
321 } else {
322 hideActivity(info->id());
323 }
324 }
325}
326
328{
329 d->shownStates = states;
330
331 d->replaceActivities(d->activities.activities());
332
333 Q_EMIT shownStatesChanged(states);
334}
335
336QList<Info::State> ActivitiesModel::shownStates() const
337{
338 return d->shownStates;
339}
340
341int ActivitiesModel::rowCount(const QModelIndex &parent) const
342{
343 if (parent.isValid()) {
344 return 0;
345 }
346
347 return d->shownActivities.size();
348}
349
350QVariant ActivitiesModel::data(const QModelIndex &index, int role) const
351{
352 const int row = index.row();
353 const auto &item = d->shownActivities.at(row);
354
355 switch (role) {
356 case Qt::DisplayRole:
357 case ActivityName:
358 return item->name();
359
360 case ActivityId:
361 return item->id();
362
363 case ActivityState:
364 return item->state();
365
367 case ActivityIconSource: {
368 const QString &icon = item->icon();
369
370 // We need a default icon for activities
371 return icon.isEmpty() ? QStringLiteral("activities") : icon;
372 }
373
375 return item->description();
376
378 return d->activities.currentActivity() == item->id();
379
380 default:
381 return QVariant();
382 }
383}
384
385QVariant ActivitiesModel::headerData(int section, Qt::Orientation orientation, int role) const
386{
387 Q_UNUSED(section);
388 Q_UNUSED(orientation);
389 Q_UNUSED(role);
390
391 return QVariant();
392}
393
394ActivitiesModelPrivate::InfoPtr ActivitiesModelPrivate::findActivity(QObject *ptr) const
395{
396 auto info = std::find_if(knownActivities.cbegin(), knownActivities.cend(), [ptr](const InfoPtr &info) {
397 return ptr == info.get();
398 });
399
400 if (info == knownActivities.end()) {
401 return nullptr;
402 } else {
403 return *info;
404 }
405}
406
407} // namespace KActivities
408
409#include "moc_activitiesmodel.cpp"
410#include "moc_activitiesmodel_p.cpp"
Data model that shows existing activities.
void setShownStates(const QList< Info::State > &shownStates)
The model can filter the list of activities based on their state.
@ ActivityState
The current state of the activity.
@ ActivityDescription
Activity description.
@ ActivityIsCurrent
Is this activity the current one current.
@ ActivityId
UUID of the activity.
@ ActivityIconSource
Activity icon source name.
@ ActivityBackground
Activity wallpaper (currently unsupported)
ServiceStatus
Different states of the activities service.
Definition consumer.h:76
void activityAdded(const QString &id)
This signal is emitted when a new activity is added.
void currentActivityChanged(const QString &id)
This signal is emitted when the current activity is changed.
void activityRemoved(const QString &id)
This signal is emitted when an activity has been removed.
void serviceStatusChanged(Consumer::ServiceStatus status)
This signal is emitted when the activity service goes online or offline, or when the class manages to...
State
State of the activity.
Definition info.h:90
void descriptionChanged(const QString &description)
Emitted when the description is changed.
void nameChanged(const QString &name)
Emitted when the name is changed.
void stateChanged(KActivities::Info::State state)
Emitted when the activity changes state.
void iconChanged(const QString &icon)
Emitted when the icon was changed.
Q_SCRIPTABLE CaptureState status()
Namespace for everything in libkactivities.
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual QModelIndex parent(const QModelIndex &index) const const=0
QAbstractListModel(QObject *parent)
QObject(QObject *parent)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
bool isEmpty() const const
DecorationRole
Orientation
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 28 2025 12:01:06 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.