7#include "launchertasksmodel.h"
10#include <KDesktopFile>
11#include <KNotificationJobUiDelegate>
14#include <KWindowSystem>
16#include <PlasmaActivities/Consumer>
17#include <PlasmaActivities/ResourceInstance>
19#include <KIO/ApplicationLauncherJob>
27#include "launchertasksmodel_p.h"
30using namespace std::chrono_literals;
36template<
typename ActivitiesCollection>
37inline bool isOnAllActivities(
const ActivitiesCollection &activities)
39 return activities.isEmpty() || activities.contains(NULL_UUID);
42class Q_DECL_HIDDEN LauncherTasksModel::Private
45 Private(LauncherTasksModel *q);
47 KActivities::Consumer activitiesConsumer;
52 inline void setActivitiesForLauncher(
const QUrl &url,
const ActivitiesSet &activities)
54 if (activities.size() == activitiesConsumer.activities().size()) {
55 activitiesForLauncher[url] = {NULL_UUID};
57 activitiesForLauncher[url] = activities;
65 AppData appData(
const QUrl &url);
67 bool requestAddLauncherToActivities(
const QUrl &_url,
const QStringList &activities);
68 bool requestRemoveLauncherFromActivities(
const QUrl &_url,
const QStringList &activities);
71 LauncherTasksModel *q;
74LauncherTasksModel::Private::Private(LauncherTasksModel *q)
79void LauncherTasksModel::Private::init()
81 sycocaChangeTimer.setSingleShot(
true);
82 sycocaChangeTimer.setInterval(100ms);
85 if (!launchersOrder.
count()) {
92 Q_EMIT q->dataChanged(q->index(0, 0),
93 q->index(launchersOrder.
count() - 1, 0),
96 AbstractTasksModel::AppId,
97 AbstractTasksModel::AppName,
98 AbstractTasksModel::GenericName,
99 AbstractTasksModel::LauncherUrl,
100 AbstractTasksModel::LauncherUrlWithoutIcon});
104 sycocaChangeTimer.start();
108AppData LauncherTasksModel::Private::appData(
const QUrl &url)
110 const auto &it = appDataCache.constFind(url);
112 if (it != appDataCache.constEnd()) {
118 appDataCache.insert(url, data);
123bool LauncherTasksModel::Private::requestAddLauncherToActivities(
const QUrl &_url,
const QStringList &_activities)
126 if (!isValidLauncherUrl(url)) {
130 const auto activities = ActivitiesSet(_activities.
cbegin(), _activities.
cend());
139 const QString &menuId = service->menuId();
149 for (
const QUrl &launcher : std::as_const(launchersOrder)) {
152 if (launcherUrlsMatch(url, launcher, IgnoreQueryItems)) {
153 ActivitiesSet newActivities;
156 if (!activitiesForLauncher.
contains(launcher)) {
159 newActivities = activities;
162 if (isOnAllActivities(activities)) {
165 newActivities = ActivitiesSet{NULL_UUID};
167 }
else if (isOnAllActivities(activitiesForLauncher[launcher])) {
172 newActivities = activities;
175 newActivities += activities;
176 newActivities += activitiesForLauncher[launcher];
180 if (newActivities != activitiesForLauncher[launcher]) {
181 setActivitiesForLauncher(launcher, newActivities);
183 Q_EMIT q->dataChanged(q->index(row, 0), q->index(row, 0));
185 Q_EMIT q->launcherListChanged();
194 const auto count = launchersOrder.
count();
196 setActivitiesForLauncher(url, activities);
197 launchersOrder.
append(url);
200 Q_EMIT q->launcherListChanged();
205bool LauncherTasksModel::Private::requestRemoveLauncherFromActivities(
const QUrl &url,
const QStringList &activities)
207 for (
int row = 0; row < launchersOrder.
count(); ++row) {
208 const QUrl launcher = launchersOrder.
at(row);
210 if (launcherUrlsMatch(url, launcher, IgnoreQueryItems) || launcherUrlsMatch(url, appData(launcher).url, IgnoreQueryItems)) {
211 const auto currentActivities = activitiesForLauncher[url];
212 ActivitiesSet newActivities;
217 if (isOnAllActivities(currentActivities)) {
221 if (isOnAllActivities(activities)) {
225 const auto _activities = activitiesConsumer.activities();
226 for (
const auto &activity : _activities) {
227 if (!activities.
contains(activity)) {
228 newActivities << activity;
235 }
else if (isOnAllActivities(activities)) {
242 for (
const auto &activity : currentActivities) {
243 if (!activities.
contains(activity)) {
244 newActivities << activity;
248 if (newActivities.isEmpty()) {
257 appDataCache.remove(launcher);
259 activitiesForLauncher.
remove(url);
263 setActivitiesForLauncher(url, newActivities);
265 Q_EMIT q->dataChanged(q->index(row, 0), q->index(row, 0));
268 if (remove || update) {
269 Q_EMIT q->launcherListChanged();
278LauncherTasksModel::LauncherTasksModel(
QObject *parent)
279 : AbstractTasksModel(parent)
280 , d(new Private(this))
285LauncherTasksModel::~LauncherTasksModel()
291 if (!index.
isValid() || index.
row() >= d->launchersOrder.count()) {
295 const QUrl &url = d->launchersOrder.at(index.
row());
296 const AppData &data = d->appData(url);
301 }
else if (role == AppId) {
303 }
else if (role == AppName) {
305 }
else if (role == GenericName) {
306 return data.genericName;
307 }
else if (role == LauncherUrl) {
310 }
else if (role == LauncherUrlWithoutIcon) {
321 }
else if (role == IsLauncher) {
323 }
else if (role == IsVirtualDesktopsChangeable) {
325 }
else if (role == IsOnAllVirtualDesktops) {
327 }
else if (role == Activities) {
328 return QStringList(d->activitiesForLauncher[url].values());
329 }
else if (role == CanLaunchNewInstance) {
333 return AbstractTasksModel::data(index, role);
336int LauncherTasksModel::rowCount(
const QModelIndex &parent)
const
338 return parent.
isValid() ? 0 : d->launchersOrder.count();
341int LauncherTasksModel::rowCountForActivity(
const QString &activity)
const
343 if (activity == NULL_UUID || activity.
isEmpty()) {
347 return std::count_if(d->launchersOrder.cbegin(), d->launchersOrder.cend(), [
this, &activity](
const QUrl &url) {
348 const auto &set = d->activitiesForLauncher[url];
349 return set.contains(NULL_UUID) || set.contains(activity);
358 for (
const auto &launcher : std::as_const(d->launchersOrder)) {
359 const auto &activities = d->activitiesForLauncher[launcher];
362 if (isOnAllActivities(activities)) {
363 serializedLauncher = launcher.
toString();
366 serializedLauncher = u
'[' + d->activitiesForLauncher[launcher].values().join(u
',') + u
"]\n" + launcher.
toString();
369 result << serializedLauncher;
375void LauncherTasksModel::setLauncherList(
const QStringList &serializedLaunchers)
382 for (
const auto &serializedLauncher : serializedLaunchers) {
386 std::tie(url, _activities) = deserializeLauncher(serializedLauncher);
389 if (!isValidLauncherUrl(url)) {
396 if (isOnAllActivities(_activities)) {
397 activities = {NULL_UUID};
401 const auto allActivities = d->activitiesConsumer.activities();
403 for (
const QStringView activity : std::as_const(_activities)) {
404 if (allActivities.contains(activity)) {
405 validActivities << activity.toString();
409 if (validActivities.
isEmpty()) {
415 activities = std::move(validActivities);
419 const auto location = std::find_if(newLaunchersOrder.
begin(), newLaunchersOrder.
end(), [&url](
const QUrl &item) {
420 return launcherUrlsMatch(url, item, IgnoreQueryItems);
423 if (location != newLaunchersOrder.
end()) {
430 newLaunchersOrder << url;
433 if (!newActivitiesForLauncher.
contains(url)) {
435 newActivitiesForLauncher[url] = activities;
437 }
else if (newActivitiesForLauncher[url].contains(NULL_UUID)) {
440 }
else if (activities.
contains(NULL_UUID)) {
441 newActivitiesForLauncher[url] = {NULL_UUID};
445 newActivitiesForLauncher[url] += activities;
449 if (newLaunchersOrder != d->launchersOrder) {
450 const bool isOrderChanged = std::all_of(newLaunchersOrder.
cbegin(),
451 newLaunchersOrder.
cend(),
452 [
this](
const QUrl &url) {
453 return d->launchersOrder.contains(url);
455 && newLaunchersOrder.
size() == d->launchersOrder.size();
457 if (isOrderChanged) {
458 for (
int i = 0; i < newLaunchersOrder.
size(); i++) {
459 int oldRow = d->launchersOrder.indexOf(newLaunchersOrder.
at(i));
463 d->launchersOrder.move(oldRow, i);
469 if (!d->launchersOrder.empty()) {
470 beginRemoveRows(
QModelIndex(), 0, d->launchersOrder.size() - 1);
472 d->launchersOrder.clear();
473 d->activitiesForLauncher.clear();
478 if (!newLaunchersOrder.
empty()) {
481 d->launchersOrder = newLaunchersOrder;
482 d->activitiesForLauncher = newActivitiesForLauncher;
488 Q_EMIT launcherListChanged();
490 }
else if (newActivitiesForLauncher != d->activitiesForLauncher) {
491 for (
int i = 0; i < d->launchersOrder.size(); i++) {
492 const QUrl &url = d->launchersOrder.at(i);
494 if (d->activitiesForLauncher[url] != newActivitiesForLauncher[url]) {
495 d->activitiesForLauncher[url] = newActivitiesForLauncher[url];
496 Q_EMIT dataChanged(index(i, 0), index(i, 0), {Activities});
502bool LauncherTasksModel::requestAddLauncher(
const QUrl &url)
504 return d->requestAddLauncherToActivities(url, {NULL_UUID});
507bool LauncherTasksModel::requestRemoveLauncher(
const QUrl &url)
509 return d->requestRemoveLauncherFromActivities(url, {NULL_UUID});
512bool LauncherTasksModel::requestAddLauncherToActivity(
const QUrl &url,
const QString &activity)
514 return d->requestAddLauncherToActivities(url, {activity});
517bool LauncherTasksModel::requestRemoveLauncherFromActivity(
const QUrl &url,
const QString &activity)
519 return d->requestRemoveLauncherFromActivities(url, {activity});
524 const auto position = launcherPosition(_url);
526 if (position == -1) {
531 const auto url = d->launchersOrder.at(position);
534 return d->activitiesForLauncher.contains(url) ? d->activitiesForLauncher[url].values() :
QStringList{NULL_UUID};
538int LauncherTasksModel::launcherPosition(
const QUrl &url)
const
540 for (
int i = 0; i < d->launchersOrder.count(); ++i) {
541 if (launcherUrlsMatch(url, d->appData(d->launchersOrder.at(i)).
url, IgnoreQueryItems)) {
551 requestNewInstance(index);
554void LauncherTasksModel::requestNewInstance(
const QModelIndex &index)
556 if (!index.
isValid() || index.
model() !=
this || index.
row() < 0 || index.
row() >= d->launchersOrder.count()) {
560 runApp(d->appData(d->launchersOrder.at(index.
row())));
565 if (!index.
isValid() || index.
model() !=
this || index.
row() < 0 || index.
row() >= d->launchersOrder.count() || urls.
isEmpty()) {
569 const QUrl &url = d->launchersOrder.at(index.
row());
581 if (!service || !service->isApplication()) {
591 KActivities::ResourceInstance::notifyAccessed(
QUrl(
QString(u
"applications:" + service->storageId())), QStringLiteral(
"org.kde.libtaskmanager"));
static bool isDesktopFile(const QString &path)
static Ptr serviceByStorageId(const QString &_storageId)
static Ptr serviceByMenuId(const QString &_menuId)
static Ptr serviceByDesktopPath(const QString &_path)
Q_SIGNAL void databaseChanged()
void update(Part *part, const QByteArray &data, qint64 dataSize)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
bool contains(const Key &key) const const
bool remove(const Key &key)
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
const_iterator cbegin() const const
const_iterator cend() const const
qsizetype count() const const
bool isEmpty() const const
void removeAt(qsizetype i)
qsizetype size() const const
bool isValid() const const
const QAbstractItemModel * model() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool contains(const QSet< T > &other) const const
bool isEmpty() const const
bool isEmpty() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
bool hasQuery() const const
bool isLocalFile() const const
QString path(ComponentFormattingOptions options) const const
QString scheme() const const
void setQuery(const QString &query, ParsingMode mode)
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
QString url(FormattingOptions options) const const