Warning, file /frameworks/kactivities/src/lib/activitiesmodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2012-2016 Ivan Cukic <ivan.cukic(at)kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 // Self 0008 #include "activitiesmodel.h" 0009 #include "activitiesmodel_p.h" 0010 0011 // Qt 0012 #include <QByteArray> 0013 #include <QDBusPendingCall> 0014 #include <QDBusPendingCallWatcher> 0015 #include <QDebug> 0016 #include <QFutureWatcher> 0017 #include <QHash> 0018 #include <QModelIndex> 0019 0020 // Local 0021 #include "utils/remove_if.h" 0022 0023 namespace KActivities 0024 { 0025 namespace Private 0026 { 0027 template<typename _Container> 0028 struct ActivityPosition { 0029 ActivityPosition() 0030 : isValid(false) 0031 , index(0) 0032 , iterator() 0033 { 0034 } 0035 0036 ActivityPosition(unsigned int index, typename _Container::const_iterator iterator) 0037 : isValid(true) 0038 , index(index) 0039 , iterator(iterator) 0040 { 0041 } 0042 0043 operator bool() const 0044 { 0045 return isValid; 0046 } 0047 0048 const bool isValid; 0049 const unsigned int index; 0050 const typename _Container::const_iterator iterator; 0051 0052 typedef typename _Container::value_type ContainerElement; 0053 }; 0054 0055 /** 0056 * Returns whether the activity has a desired state. 0057 * If the state is 0, returns true 0058 */ 0059 template<typename T> 0060 inline bool matchingState(ActivitiesModelPrivate::InfoPtr activity, const T &states) 0061 { 0062 return states.empty() || states.contains(activity->state()); 0063 } 0064 0065 /** 0066 * Searches for the activity. 0067 * Returns an option(index, iterator) for the found activity. 0068 */ 0069 template<typename _Container> 0070 inline ActivityPosition<_Container> activityPosition(const _Container &container, const QString &activityId) 0071 { 0072 auto position = std::find_if(container.begin(), container.end(), [&](const typename ActivityPosition<_Container>::ContainerElement &activity) { 0073 return activity->id() == activityId; 0074 }); 0075 0076 return (position != container.end()) ? ActivityPosition<_Container>(position - container.begin(), position) : ActivityPosition<_Container>(); 0077 } 0078 0079 /** 0080 * Notifies the model that an activity was updated 0081 */ 0082 template<typename _Model, typename _Container> 0083 inline void emitActivityUpdated(_Model *model, const _Container &container, const QString &activity, int role) 0084 { 0085 auto position = Private::activityPosition(container, activity); 0086 0087 if (position) { 0088 Q_EMIT model->q->dataChanged(model->q->index(position.index), 0089 model->q->index(position.index), 0090 role == Qt::DecorationRole ? QVector<int>{role, ActivitiesModel::ActivityIconSource} : QVector<int>{role}); 0091 } 0092 } 0093 0094 /** 0095 * Notifies the model that an activity was updated 0096 */ 0097 template<typename _Model, typename _Container> 0098 inline void emitActivityUpdated(_Model *model, const _Container &container, QObject *activityInfo, int role) 0099 { 0100 const auto activity = static_cast<Info *>(activityInfo); 0101 emitActivityUpdated(model, container, activity->id(), role); 0102 } 0103 0104 } 0105 0106 ActivitiesModelPrivate::ActivitiesModelPrivate(ActivitiesModel *parent) 0107 : q(parent) 0108 { 0109 } 0110 0111 ActivitiesModel::ActivitiesModel(QObject *parent) 0112 : QAbstractListModel(parent) 0113 , d(new ActivitiesModelPrivate(this)) 0114 { 0115 // Initializing role names for qml 0116 connect(&d->activities, &Consumer::serviceStatusChanged, this, [this](Consumer::ServiceStatus status) { 0117 d->setServiceStatus(status); 0118 }); 0119 0120 connect(&d->activities, &Consumer::activityAdded, this, [this](const QString &activity) { 0121 d->onActivityAdded(activity); 0122 }); 0123 connect(&d->activities, &Consumer::activityRemoved, this, [this](const QString &activity) { 0124 d->onActivityRemoved(activity); 0125 }); 0126 connect(&d->activities, &Consumer::currentActivityChanged, this, [this](const QString &activity) { 0127 d->onCurrentActivityChanged(activity); 0128 }); 0129 0130 d->setServiceStatus(d->activities.serviceStatus()); 0131 } 0132 0133 ActivitiesModel::ActivitiesModel(QVector<Info::State> shownStates, QObject *parent) 0134 : QAbstractListModel(parent) 0135 , d(new ActivitiesModelPrivate(this)) 0136 { 0137 d->shownStates = shownStates; 0138 0139 // Initializing role names for qml 0140 connect(&d->activities, &Consumer::serviceStatusChanged, this, [this](Consumer::ServiceStatus status) { 0141 d->setServiceStatus(status); 0142 }); 0143 0144 connect(&d->activities, &Consumer::activityAdded, this, [this](const QString &activity) { 0145 d->onActivityAdded(activity); 0146 }); 0147 connect(&d->activities, &Consumer::activityRemoved, this, [this](const QString &activity) { 0148 d->onActivityRemoved(activity); 0149 }); 0150 connect(&d->activities, &Consumer::currentActivityChanged, this, [this](const QString &activity) { 0151 d->onCurrentActivityChanged(activity); 0152 }); 0153 0154 d->setServiceStatus(d->activities.serviceStatus()); 0155 } 0156 0157 ActivitiesModel::~ActivitiesModel() 0158 { 0159 delete d; 0160 } 0161 0162 QHash<int, QByteArray> ActivitiesModel::roleNames() const 0163 { 0164 return {{ActivityName, "name"}, 0165 {ActivityState, "state"}, 0166 {ActivityId, "id"}, 0167 {ActivityIconSource, "iconSource"}, 0168 {ActivityDescription, "description"}, 0169 {ActivityBackground, "background"}, 0170 {ActivityIsCurrent, "isCurrent"}}; 0171 } 0172 0173 void ActivitiesModelPrivate::setServiceStatus(Consumer::ServiceStatus) 0174 { 0175 replaceActivities(activities.activities()); 0176 } 0177 0178 void ActivitiesModelPrivate::replaceActivities(const QStringList &activities) 0179 { 0180 q->beginResetModel(); 0181 0182 knownActivities.clear(); 0183 shownActivities.clear(); 0184 0185 for (const QString &activity : activities) { 0186 onActivityAdded(activity, false); 0187 } 0188 0189 q->endResetModel(); 0190 } 0191 0192 void ActivitiesModelPrivate::onActivityAdded(const QString &id, bool notifyClients) 0193 { 0194 auto info = registerActivity(id); 0195 0196 showActivity(info, notifyClients); 0197 } 0198 0199 void ActivitiesModelPrivate::onActivityRemoved(const QString &id) 0200 { 0201 hideActivity(id); 0202 unregisterActivity(id); 0203 } 0204 0205 void ActivitiesModelPrivate::onCurrentActivityChanged(const QString &id) 0206 { 0207 Q_UNUSED(id); 0208 0209 for (const auto &activity : shownActivities) { 0210 Private::emitActivityUpdated(this, shownActivities, activity->id(), ActivitiesModel::ActivityIsCurrent); 0211 } 0212 } 0213 0214 ActivitiesModelPrivate::InfoPtr ActivitiesModelPrivate::registerActivity(const QString &id) 0215 { 0216 auto position = Private::activityPosition(knownActivities, id); 0217 0218 if (position) { 0219 return *(position.iterator); 0220 0221 } else { 0222 auto activityInfo = std::make_shared<Info>(id); 0223 0224 auto ptr = activityInfo.get(); 0225 0226 connect(ptr, &Info::nameChanged, this, &ActivitiesModelPrivate::onActivityNameChanged); 0227 connect(ptr, &Info::descriptionChanged, this, &ActivitiesModelPrivate::onActivityDescriptionChanged); 0228 connect(ptr, &Info::iconChanged, this, &ActivitiesModelPrivate::onActivityIconChanged); 0229 connect(ptr, &Info::stateChanged, this, &ActivitiesModelPrivate::onActivityStateChanged); 0230 0231 knownActivities.insert(InfoPtr(activityInfo)); 0232 0233 return activityInfo; 0234 } 0235 } 0236 0237 void ActivitiesModelPrivate::unregisterActivity(const QString &id) 0238 { 0239 auto position = Private::activityPosition(knownActivities, id); 0240 0241 if (position) { 0242 if (auto shown = Private::activityPosition(shownActivities, id)) { 0243 q->beginRemoveRows(QModelIndex(), shown.index, shown.index); 0244 shownActivities.removeAt(shown.index); 0245 q->endRemoveRows(); 0246 } 0247 0248 knownActivities.removeAt(position.index); 0249 } 0250 } 0251 0252 void ActivitiesModelPrivate::showActivity(InfoPtr activityInfo, bool notifyClients) 0253 { 0254 // Should it really be shown? 0255 if (!Private::matchingState(activityInfo, shownStates)) { 0256 return; 0257 } 0258 0259 // Is it already shown? 0260 if (std::binary_search(shownActivities.cbegin(), shownActivities.cend(), activityInfo, InfoPtrComparator())) { 0261 return; 0262 } 0263 0264 auto registeredPosition = Private::activityPosition(knownActivities, activityInfo->id()); 0265 0266 if (!registeredPosition) { 0267 qDebug() << "Got a request to show an unknown activity, ignoring"; 0268 return; 0269 } 0270 0271 const auto activityInfoPtr = *(registeredPosition.iterator); 0272 0273 // In C++17, this would be: 0274 // const auto [iterator, index, found] = shownActivities.insert(...); 0275 const auto _result = shownActivities.insert(activityInfoPtr); 0276 // const auto iterator = std::get<0>(_result); 0277 const auto index = std::get<1>(_result); 0278 0279 if (notifyClients) { 0280 q->beginInsertRows(QModelIndex(), index, index); 0281 q->endInsertRows(); 0282 } 0283 } 0284 0285 void ActivitiesModelPrivate::hideActivity(const QString &id) 0286 { 0287 auto position = Private::activityPosition(shownActivities, id); 0288 0289 if (position) { 0290 q->beginRemoveRows(QModelIndex(), position.index, position.index); 0291 shownActivities.removeAt(position.index); 0292 q->endRemoveRows(); 0293 } 0294 } 0295 0296 // clang-format off 0297 #define CREATE_SIGNAL_EMITTER(What,Role) \ 0298 void ActivitiesModelPrivate::onActivity##What##Changed(const QString &) \ 0299 { \ 0300 Private::emitActivityUpdated(this, shownActivities, sender(), Role); \ 0301 } 0302 // clang-format on 0303 0304 CREATE_SIGNAL_EMITTER(Name, Qt::DisplayRole) 0305 CREATE_SIGNAL_EMITTER(Description, ActivitiesModel::ActivityDescription) 0306 CREATE_SIGNAL_EMITTER(Icon, Qt::DecorationRole) 0307 0308 #undef CREATE_SIGNAL_EMITTER 0309 0310 void ActivitiesModelPrivate::onActivityStateChanged(Info::State state) 0311 { 0312 if (shownStates.empty()) { 0313 Private::emitActivityUpdated(this, shownActivities, sender(), ActivitiesModel::ActivityState); 0314 0315 } else { 0316 auto info = findActivity(sender()); 0317 0318 if (!info) { 0319 return; 0320 } 0321 0322 if (shownStates.contains(state)) { 0323 showActivity(info, true); 0324 } else { 0325 hideActivity(info->id()); 0326 } 0327 } 0328 } 0329 0330 void ActivitiesModel::setShownStates(const QVector<Info::State> &states) 0331 { 0332 d->shownStates = states; 0333 0334 d->replaceActivities(d->activities.activities()); 0335 0336 Q_EMIT shownStatesChanged(states); 0337 } 0338 0339 QVector<Info::State> ActivitiesModel::shownStates() const 0340 { 0341 return d->shownStates; 0342 } 0343 0344 int ActivitiesModel::rowCount(const QModelIndex &parent) const 0345 { 0346 if (parent.isValid()) { 0347 return 0; 0348 } 0349 0350 return d->shownActivities.size(); 0351 } 0352 0353 QVariant ActivitiesModel::data(const QModelIndex &index, int role) const 0354 { 0355 const int row = index.row(); 0356 const auto &item = d->shownActivities.at(row); 0357 0358 switch (role) { 0359 case Qt::DisplayRole: 0360 case ActivityName: 0361 return item->name(); 0362 0363 case ActivityId: 0364 return item->id(); 0365 0366 case ActivityState: 0367 return item->state(); 0368 0369 case Qt::DecorationRole: 0370 case ActivityIconSource: { 0371 const QString &icon = item->icon(); 0372 0373 // We need a default icon for activities 0374 return icon.isEmpty() ? QStringLiteral("activities") : icon; 0375 } 0376 0377 case ActivityDescription: 0378 return item->description(); 0379 0380 case ActivityIsCurrent: 0381 return d->activities.currentActivity() == item->id(); 0382 0383 default: 0384 return QVariant(); 0385 } 0386 } 0387 0388 QVariant ActivitiesModel::headerData(int section, Qt::Orientation orientation, int role) const 0389 { 0390 Q_UNUSED(section); 0391 Q_UNUSED(orientation); 0392 Q_UNUSED(role); 0393 0394 return QVariant(); 0395 } 0396 0397 ActivitiesModelPrivate::InfoPtr ActivitiesModelPrivate::findActivity(QObject *ptr) const 0398 { 0399 auto info = std::find_if(knownActivities.cbegin(), knownActivities.cend(), [ptr](const InfoPtr &info) { 0400 return ptr == info.get(); 0401 }); 0402 0403 if (info == knownActivities.end()) { 0404 return nullptr; 0405 } else { 0406 return *info; 0407 } 0408 } 0409 0410 } // namespace KActivities 0411 0412 // #include "activitiesmodel.moc"