kastatsfavoritesmodel.cpp 22.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/***************************************************************************
 *   Copyright (C) 2014-2015 by Eike Hein <hein@kde.org>                   *
 *   Copyright (C) 2016-2017 by Ivan Cukic <ivan.cukic@kde.org>            *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 ***************************************************************************/

#include "kastatsfavoritesmodel.h"
#include "appentry.h"
#include "contactentry.h"
#include "fileentry.h"
#include "actionlist.h"
David Edmundson's avatar
David Edmundson committed
26
#include "debug.h"
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

#include <QFileInfo>
#include <QTimer>
#include <QSortFilterProxyModel>

#include <KLocalizedString>
#include <KSharedConfig>
#include <KConfigGroup>

#include <KActivities/Consumer>
#include <KActivities/Stats/Terms>
#include <KActivities/Stats/Query>
#include <KActivities/Stats/ResultSet>
#include <KActivities/Stats/ResultWatcher>

namespace KAStats = KActivities::Stats;

using namespace KAStats;
using namespace KAStats::Terms;

47
48
49
#define AGENT_APPLICATIONS QStringLiteral("org.kde.plasma.favorites.applications")
#define AGENT_CONTACTS     QStringLiteral("org.kde.plasma.favorites.contacts")
#define AGENT_DOCUMENTS    QStringLiteral("org.kde.plasma.favorites.documents")
50
51
52

QString agentForUrl(const QString &url)
{
David Edmundson's avatar
David Edmundson committed
53
    return url.startsWith(QLatin1String("ktp:"))
54
                ? AGENT_CONTACTS
David Edmundson's avatar
David Edmundson committed
55
         : url.startsWith(QLatin1String("preferred:"))
56
                ? AGENT_APPLICATIONS
David Edmundson's avatar
David Edmundson committed
57
         : url.startsWith(QLatin1String("applications:"))
58
                ? AGENT_APPLICATIONS
David Edmundson's avatar
David Edmundson committed
59
         : (url.startsWith(QLatin1Char('/')) && !url.endsWith(QLatin1String(".desktop")))
60
                ? AGENT_DOCUMENTS
David Edmundson's avatar
David Edmundson committed
61
         : (url.startsWith(QLatin1String("file:/")) && !url.endsWith(QLatin1String(".desktop")))
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
                ? AGENT_DOCUMENTS
         // use applications as the default
                : AGENT_APPLICATIONS;
}

class KAStatsFavoritesModel::Private: public QAbstractListModel {
public:
    class NormalizedId {
    public:
        NormalizedId()
        {
        }

        NormalizedId(const Private *parent, const QString &id)
        {
            if (id.isEmpty()) return;

79
            QSharedPointer<AbstractEntry> entry = nullptr;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

            if (parent->m_itemEntries.contains(id)) {
                entry = parent->m_itemEntries[id];
            } else {
                // This entry is not cached - it is temporary,
                // so let's clean up when we exit this function
                entry = parent->entryForResource(id);
            }

            if (!entry || !entry->isValid()) {
                qWarning() << "Entry is not valid" << id << entry;
                m_id = id;
                return;
            }

            const auto url = entry->url();

David Edmundson's avatar
David Edmundson committed
97
            qCDebug(KICKER_DEBUG) << "Original id is: " << id << ", and the url is" << url;
98
99

            // Preferred applications need special handling
100
            if (entry->id().startsWith(QLatin1String("preferred:"))) {
101
102
103
104
105
                m_id = entry->id();
                return;
            }

            // If this is an application, use the applications:-format url
106
            auto appEntry = dynamic_cast<AppEntry*>(entry.data());
107
            if (appEntry && !appEntry->menuId().isEmpty()) {
108
                m_id = QLatin1String("applications:") + appEntry->menuId();
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
                return;
            }

            // We want to resolve symbolic links not to have two paths
            // refer to the same .desktop file
            if (url.isLocalFile()) {
                QFileInfo file(url.toLocalFile());

                if (file.exists()) {
                    m_id = QUrl::fromLocalFile(file.canonicalFilePath()).toString();
                    return;
                }
            }

            // If this is a file, we should have already covered it
124
            if (url.scheme() == QLatin1String("file")) {
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
                return;
            }

            m_id = url.toString();
        }

        const QString& value() const
        {
            return m_id;
        }

        bool operator==(const NormalizedId &other) const
        {
            return m_id == other.m_id;
        }

    private:
        QString m_id;
    };

    NormalizedId normalizedId(const QString &id) const
    {
        return NormalizedId(this, id);
    }

150
    QSharedPointer<AbstractEntry> entryForResource(const QString &resource) const
151
    {
152
153
        using SP = QSharedPointer<AbstractEntry>;

154
155
156
157
        const auto agent =
            agentForUrl(resource);

        if (agent == AGENT_CONTACTS) {
158
            return SP(new ContactEntry(q, resource));
159
160

        } else if (agent == AGENT_DOCUMENTS) {
161
            if (resource.startsWith(QLatin1String("/"))) {
162
                return SP(new FileEntry(q, QUrl::fromLocalFile(resource)));
163
            } else {
164
                return SP(new FileEntry(q, QUrl(resource)));
165
166
167
            }

        } else if (agent == AGENT_APPLICATIONS) {
168
            if (resource.startsWith(QLatin1String("applications:"))) {
169
                return SP(new AppEntry(q, resource.mid(13)));
170
            } else {
171
                return SP(new AppEntry(q, resource));
172
173
174
            }

        } else {
175
            return {};
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
        }
    }

    Private(KAStatsFavoritesModel *parent, QString clientId)
        : q(parent)
        , m_query(
              LinkedResources
                  | Agent {
                      AGENT_APPLICATIONS,
                      AGENT_CONTACTS,
                      AGENT_DOCUMENTS
                  }
                  | Type::any()
                  | Activity::current()
                  | Activity::global()
                  | Limit::all()
              )
        , m_watcher(m_query)
        , m_clientId(clientId)
    {
        // Connecting the watcher
        connect(&m_watcher, &ResultWatcher::resultLinked,
                [this] (const QString &resource) {
                    addResult(resource, -1);
                });

        connect(&m_watcher, &ResultWatcher::resultUnlinked,
                [this] (const QString &resource) {
                    removeResult(resource);
                });

        // Loading the items order
208
        const auto cfg = KSharedConfig::openConfig(QStringLiteral("kactivitymanagerd-statsrc"));
209
210
211
212
213

        // We want first to check whether we have an ordering for this activity.
        // If not, we will try to get a global one for this applet

        const QString thisGroupName =
214
            QStringLiteral("Favorites-") + clientId + QStringLiteral("-") + m_activities.currentActivity();
215
        const QString globalGroupName =
216
            QStringLiteral("Favorites-") + clientId + QStringLiteral("-global");
217
218
219
220
221
222
223
224

        KConfigGroup thisCfgGroup(cfg, thisGroupName);
        KConfigGroup globalCfgGroup(cfg, globalGroupName);

        QStringList ordering =
            thisCfgGroup.readEntry("ordering", QStringList()) +
            globalCfgGroup.readEntry("ordering", QStringList());

David Edmundson's avatar
David Edmundson committed
225
        qCDebug(KICKER_DEBUG) << "Loading the ordering " << ordering;
226
227

        // Loading the results without emitting any model signals
David Edmundson's avatar
David Edmundson committed
228
        qCDebug(KICKER_DEBUG) << "Query is" << m_query;
229
230
231
        ResultSet results(m_query);

        for (const auto& result: results) {
David Edmundson's avatar
David Edmundson committed
232
            qCDebug(KICKER_DEBUG) << "Got " << result.resource() << " -->";
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
            addResult(result.resource(), -1, false);
        }

        // Normalizing all the ids
        std::transform(ordering.begin(), ordering.end(), ordering.begin(),
                       [&] (const QString &item) {
                          return normalizedId(item).value();
                       });

        // Sorting the items in the cache
        std::sort(m_items.begin(), m_items.end(),
                [&] (const NormalizedId &left, const NormalizedId &right) {
                    auto leftIndex = ordering.indexOf(left.value());
                    auto rightIndex = ordering.indexOf(right.value());

                    return (leftIndex == -1 && rightIndex == -1) ?
                               left.value() < right.value() :

                           (leftIndex == -1) ?
                               false :

                           (rightIndex == -1) ?
                               true :

                           // otherwise
                               leftIndex < rightIndex;
                });

        // Debugging:
        QVector<QString> itemStrings(m_items.size());
        std::transform(m_items.cbegin(), m_items.cend(), itemStrings.begin(),
                [] (const NormalizedId &item) {
                    return item.value();
                });
David Edmundson's avatar
David Edmundson committed
267
        qCDebug(KICKER_DEBUG) << "After ordering: " << itemStrings;
268
269
270
271
272
273
    }

    void addResult(const QString &_resource, int index, bool notifyModel = true)
    {
        // We want even files to have a proper URL
        const auto resource =
274
            _resource.startsWith(QLatin1Char('/')) ? QUrl::fromLocalFile(_resource).toString() : _resource;
275

David Edmundson's avatar
David Edmundson committed
276
        qCDebug(KICKER_DEBUG) << "Adding result" << resource << "already present?" << m_itemEntries.contains(resource);
277
278
279
280
281
282

        if (m_itemEntries.contains(resource)) return;

        auto entry = entryForResource(resource);

        if (!entry || !entry->isValid()) {
David Edmundson's avatar
David Edmundson committed
283
            qCDebug(KICKER_DEBUG) << "Entry is not valid!";
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
            return;
        }

        if (index == -1) {
            index = m_items.count();
        }

        if (notifyModel) {
            beginInsertRows(QModelIndex(), index, index);
        }

        auto url = entry->url();

        m_itemEntries[resource]
            = m_itemEntries[entry->id()]
            = m_itemEntries[url.toString()]
            = m_itemEntries[url.toLocalFile()]
            = entry;

        auto normalized = normalizedId(resource);
        m_items.insert(index, normalized);
        m_itemEntries[normalized.value()] = entry;

        if (notifyModel) {
            endInsertRows();
            saveOrdering();
        }
    }

    void removeResult(const QString &resource)
    {
        auto normalized = normalizedId(resource);

        // If we know this item will not really be removed,
        // but only that activities it is on have changed,
        // lets leave it
        if (m_ignoredItems.contains(normalized.value())) {
            m_ignoredItems.removeAll(normalized.value());
            return;
        }

David Edmundson's avatar
David Edmundson committed
325
        qCDebug(KICKER_DEBUG) << "Removing result" << resource;
326
327
328
329
330
331
332
333
334
335

        auto index = m_items.indexOf(normalizedId(resource));

        if (index == -1) return;

        beginRemoveRows(QModelIndex(), index, index);
        auto entry = m_itemEntries[resource];
        m_items.removeAt(index);

        // Removing the entry from the cache
336
        QMutableHashIterator<QString, QSharedPointer<AbstractEntry>> i(m_itemEntries);
337
        while (i.hasNext()) {
Marco Martin's avatar
Marco Martin committed
338
            i.next();
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
            if (i.value() == entry) {
                i.remove();
            }
        }

        endRemoveRows();
    }


    int rowCount(const QModelIndex &parent = QModelIndex()) const override
    {
        if (parent.isValid()) return 0;

        return m_items.count();
    }

    QVariant data(const QModelIndex &item,
                  int role = Qt::DisplayRole) const override
    {
        if (item.parent().isValid()) return QVariant();

        const auto index = item.row();

        const auto entry = m_itemEntries[m_items[index].value()];

        return entry == nullptr ? QVariant()
             : role == Qt::DisplayRole ? entry->name()
             : role == Qt::DecorationRole ? entry->icon()
             : role == Kicker::DescriptionRole ? entry->description()
             : role == Kicker::FavoriteIdRole ? entry->id()
             : role == Kicker::UrlRole ? entry->url()
             : role == Kicker::HasActionListRole ? entry->hasActions()
             : role == Kicker::ActionListRole ? entry->actions()
             : QVariant();
    }

    bool trigger(int row, const QString &actionId, const QVariant &argument)
    {
        if (row < 0 || row >= rowCount()) {
            return false;
        }

        const QString id = data(index(row, 0), Kicker::UrlRole).toString();
382
383
384
385
386
387
388
389
390
391
        if (m_itemEntries.contains(id)) {
            return m_itemEntries[id]->run(actionId, argument);
        }
        // Entries with preferred:// can be changed by the user, BUG: 416161
        // then the list of entries could be out of sync
        const auto entry = m_itemEntries[m_items[row].value()];
        if (QUrl(entry->id()).scheme() == QLatin1String("preferred")) {
           return entry->run(actionId, argument);
        }
        return false;
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
    }

    void move(int from, int to)
    {
        if (from < 0) return;
        if (from >= m_items.count()) return;
        if (to < 0) return;
        if (to >= m_items.count()) return;

        if (from == to) return;

        const int modelTo = to + (to > from ? 1 : 0);

        if (q->beginMoveRows(QModelIndex(), from, from,
                             QModelIndex(), modelTo)) {
            m_items.move(from, to);
            q->endMoveRows();

David Edmundson's avatar
David Edmundson committed
410
            qCDebug(KICKER_DEBUG) << "Save ordering (from Private::move) -->";
411
412
413
414
415
416
417
418
419
420
421
422
            saveOrdering();
        }
    }

    void saveOrdering()
    {
        QStringList ids;

        for (const auto& item: m_items) {
            ids << item.value();
        }

David Edmundson's avatar
David Edmundson committed
423
        qCDebug(KICKER_DEBUG) << "Save ordering (from Private::saveOrdering) -->";
424
425
426
427
428
        saveOrdering(ids, m_clientId, m_activities.currentActivity());
    }

    static void saveOrdering(const QStringList &ids, const QString &clientId, const QString &currentActivity)
    {
429
        const auto cfg = KSharedConfig::openConfig(QStringLiteral("kactivitymanagerd-statsrc"));
430
431
432

        QStringList activities {
            currentActivity,
433
            QStringLiteral("global")
434
435
        };

David Edmundson's avatar
David Edmundson committed
436
        qCDebug(KICKER_DEBUG) << "Saving ordering for" << currentActivity << "and global" << ids;
437
438
439

        for (const auto& activity: activities) {
            const QString groupName =
440
                QStringLiteral("Favorites-") + clientId + QStringLiteral("-") + activity;
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456

            KConfigGroup cfgGroup(cfg, groupName);

            cfgGroup.writeEntry("ordering", ids);
        }

        cfg->sync();
    }

    KAStatsFavoritesModel *const q;
    KActivities::Consumer m_activities;
    Query m_query;
    ResultWatcher m_watcher;
    QString m_clientId;

    QVector<NormalizedId> m_items;
457
    QHash<QString, QSharedPointer<AbstractEntry>> m_itemEntries;
458
459
460
461
462
463
464
465
466
467
468
469
    QStringList m_ignoredItems;
};

KAStatsFavoritesModel::KAStatsFavoritesModel(QObject *parent)
: PlaceholderModel(parent)
, d(nullptr) // we have no client id yet
, m_enabled(true)
, m_maxFavorites(-1)
, m_activities(new KActivities::Consumer(this))
{
    connect(m_activities, &KActivities::Consumer::currentActivityChanged,
            this, [&] (const QString &currentActivity) {
David Edmundson's avatar
David Edmundson committed
470
                qCDebug(KICKER_DEBUG) << "Activity just got changed to" << currentActivity;
471
                Q_UNUSED(currentActivity);
472
473
474
475
                if (d) {
                    auto clientId = d->m_clientId;
                    initForClient(clientId);
                }
476
477
478
479
480
481
482
483
484
485
            });
}

KAStatsFavoritesModel::~KAStatsFavoritesModel()
{
    delete d;
}

void KAStatsFavoritesModel::initForClient(const QString &clientId)
{
David Edmundson's avatar
David Edmundson committed
486
    qCDebug(KICKER_DEBUG) << "initForClient" << clientId;
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504

    setSourceModel(nullptr);
    delete d;
    d = new Private(
        this,
        clientId
        );

    setSourceModel(d);
}

QString KAStatsFavoritesModel::description() const
{
    return i18n("Favorites");
}

bool KAStatsFavoritesModel::trigger(int row, const QString &actionId, const QVariant &argument)
{
505
    return d && d->trigger(row, actionId, argument);
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
}

bool KAStatsFavoritesModel::enabled() const
{
    return m_enabled;
}

int KAStatsFavoritesModel::maxFavorites() const
{
    return m_maxFavorites;
}

void KAStatsFavoritesModel::setMaxFavorites(int max)
{
    Q_UNUSED(max);
}

void KAStatsFavoritesModel::setEnabled(bool enable)
{
    if (m_enabled != enable) {
        m_enabled = enable;

        emit enabledChanged();
    }
}

QStringList KAStatsFavoritesModel::favorites() const
{
    qWarning() << "KAStatsFavoritesModel::favorites returns nothing, it is here just to keep the API backwards-compatible";
    return QStringList();
}

void KAStatsFavoritesModel::setFavorites(const QStringList& favorites)
{
    Q_UNUSED(favorites);
    qWarning() << "KAStatsFavoritesModel::setFavorites is ignored";
}

bool KAStatsFavoritesModel::isFavorite(const QString &id) const
{
    return d && d->m_itemEntries.contains(id);
}

void KAStatsFavoritesModel::portOldFavorites(const QStringList &ids)
{
551
    if (!d) return;
David Edmundson's avatar
David Edmundson committed
552
    qCDebug(KICKER_DEBUG) << "portOldFavorites" << ids;
553

554
    const QString activityId = QStringLiteral(":global");
555
556
557
558
559
560
561
562
563
564
    std::for_each(ids.begin(), ids.end(), [&] (const QString &id) {
                addFavoriteTo(id, activityId);
            });

    // Resetting the model
    auto clientId = d->m_clientId;
    setSourceModel(nullptr);
    delete d;
    d = nullptr;

David Edmundson's avatar
David Edmundson committed
565
    qCDebug(KICKER_DEBUG) << "Save ordering (from portOldFavorites) -->";
566
567
568
569
570
571
572
573
    Private::saveOrdering(ids, clientId, m_activities->currentActivity());

    QTimer::singleShot(500,
        std::bind(&KAStatsFavoritesModel::initForClient, this, clientId));
}

void KAStatsFavoritesModel::addFavorite(const QString &id, int index)
{
David Edmundson's avatar
David Edmundson committed
574
    qCDebug(KICKER_DEBUG) << "addFavorite" << id << index << " -->";
575
    addFavoriteTo(id, QStringLiteral(":global"), index);
576
577
578
579
}

void KAStatsFavoritesModel::removeFavorite(const QString &id)
{
David Edmundson's avatar
David Edmundson committed
580
    qCDebug(KICKER_DEBUG) << "removeFavorite" << id << " -->";
581
    removeFavoriteFrom(id, QStringLiteral(":any"));
582
583
584
585
}

void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const QString &activityId, int index)
{
David Edmundson's avatar
David Edmundson committed
586
    qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activityId << index << " -->";
587
588
589
590
591
    addFavoriteTo(id, Activity(activityId), index);
}

void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const QString &activityId)
{
David Edmundson's avatar
David Edmundson committed
592
    qCDebug(KICKER_DEBUG) << "removeFavoriteFrom" << id << activityId << " -->";
593
594
595
596
597
598
599
600
601
602
603
    removeFavoriteFrom(id, Activity(activityId));
}

void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const Activity &activity, int index)
{
    if (!d || id.isEmpty()) return;

    Q_ASSERT(!activity.values.isEmpty());

    setDropPlaceholderIndex(-1);

604
    QStringList matchers { d->m_activities.currentActivity(), QStringLiteral(":global"), QStringLiteral(":current") };
605
606
607
608
609
610
611
    if (std::find_first_of(activity.values.cbegin(), activity.values.cend(),
                           matchers.cbegin(), matchers.cend()) != activity.values.cend()) {
        d->addResult(id, index);
    }

    const auto url = d->normalizedId(id).value();

David Edmundson's avatar
David Edmundson committed
612
    qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activity << index << url << " (actual)";
613
614
615
616
617
618
619
620
621

    if (url.isEmpty()) return;

    d->m_watcher.linkToActivity(QUrl(url), activity,
                                Agent(agentForUrl(url)));
}

void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const Activity &activity)
{
622
623
    if (!d || id.isEmpty()) return;

624
625
626
627
    const auto url = d->normalizedId(id).value();

    Q_ASSERT(!activity.values.isEmpty());

David Edmundson's avatar
David Edmundson committed
628
    qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activity << url << " (actual)";
629
630
631
632
633
634
635
636
637

    if (url.isEmpty()) return;

    d->m_watcher.unlinkFromActivity(QUrl(url), activity,
                                    Agent(agentForUrl(url)));
}

void KAStatsFavoritesModel::setFavoriteOn(const QString &id, const QString &activityId)
{
638
639
    if (!d || id.isEmpty()) return;

640
641
    const auto url = d->normalizedId(id).value();

David Edmundson's avatar
David Edmundson committed
642
    qCDebug(KICKER_DEBUG) << "setFavoriteOn" << id << activityId << url << " (actual)";
643

David Edmundson's avatar
David Edmundson committed
644
    qCDebug(KICKER_DEBUG) << "%%%%%%%%%%% Activity is" << activityId;
645
    if (activityId.isEmpty() || activityId == QLatin1String(":any") ||
646
            activityId == QLatin1String(":global") ||
647
648
649
650
651
652
653
654
655
656
657
658
            activityId == m_activities->currentActivity()) {
        d->m_ignoredItems << url;
    }

    d->m_watcher.unlinkFromActivity(QUrl(url), Activity::any(),
                                    Agent(agentForUrl(url)));
    d->m_watcher.linkToActivity(QUrl(url), activityId,
                                Agent(agentForUrl(url)));
}

void KAStatsFavoritesModel::moveRow(int from, int to)
{
659
660
    if (!d) return;

661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
    d->move(from, to);
}

AbstractModel *KAStatsFavoritesModel::favoritesModel()
{
    return this;
}

void KAStatsFavoritesModel::refresh()
{
}

QObject *KAStatsFavoritesModel::activities() const
{
    return m_activities;
}

QString KAStatsFavoritesModel::activityNameForId(const QString &activityId) const
{
    // It is safe to use a short-lived object here,
    // we are always synced with KAMD in plasma
    KActivities::Info info(activityId);
    return info.name();
}

QStringList KAStatsFavoritesModel::linkedActivitiesFor(const QString &id) const
{
    if (!d) {
David Edmundson's avatar
David Edmundson committed
689
        qCDebug(KICKER_DEBUG) << "Linked for" << id << "is empty, no Private instance";
690
691
692
693
694
        return {};
    }

    auto url = d->normalizedId(id).value();

695
    if (url.startsWith(QLatin1String("file:"))) {
696
697
698
699
        url = QUrl(url).toLocalFile();
    }

    if (url.isEmpty()) {
David Edmundson's avatar
David Edmundson committed
700
        qCDebug(KICKER_DEBUG) << "The url for" << id << "is empty";
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
        return {};
    }

    auto query = LinkedResources
                    | Agent {
                        AGENT_APPLICATIONS,
                        AGENT_CONTACTS,
                        AGENT_DOCUMENTS
                      }
                    | Type::any()
                    | Activity::any()
                    | Url(url)
                    | Limit::all();

    ResultSet results(query);

    for (const auto &result: results) {
David Edmundson's avatar
David Edmundson committed
718
        qCDebug(KICKER_DEBUG) << "Returning" << result.linkedActivities() << "for" << id << url;
719
720
721
        return result.linkedActivities();
    }

David Edmundson's avatar
David Edmundson committed
722
    qCDebug(KICKER_DEBUG) << "Returning empty list of activities for" << id << url;
723
724
725
    return {};
}