~neon/kactivities/master

« back to all changes in this revision

Viewing changes to src/imports/resourcemodel.cpp

  • Committer: Ivan Čukić
  • Date: 2013-12-04 15:50:33 UTC
  • mto: (381.1.76)
  • mto: This revision was merged to the branch mainline in revision 388.
  • Revision ID: git-v1:4eda380bbdf451b71ea96b1b81b61c1b8d588b4a
Activities model for QML

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *   Copyright (C) 2012 Ivan Cukic <ivan.cukic(at)kde.org>
 
3
 *
 
4
 *   This program is free software; you can redistribute it and/or modify
 
5
 *   it under the terms of the GNU General Public License version 2,
 
6
 *   or (at your option) any later version, as published by the Free
 
7
 *   Software Foundation
 
8
 *
 
9
 *   This program is distributed in the hope that it will be useful,
 
10
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 *   GNU General Public License for more details
 
13
 *
 
14
 *   You should have received a copy of the GNU General Public
 
15
 *   License along with this program; if not, write to the
 
16
 *   Free Software Foundation, Inc.,
 
17
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
18
 */
 
19
 
 
20
#include "resourcemodel.h"
 
21
 
 
22
#include <QByteArray>
 
23
#include <QDBusPendingCall>
 
24
#include <QDBusPendingCallWatcher>
 
25
#include <QHash>
 
26
#include <QList>
 
27
#include <QModelIndex>
 
28
#include <QDebug>
 
29
 
 
30
#include <KIcon>
 
31
#include <KLocalizedString>
 
32
#include <KFileItem>
 
33
 
 
34
#include <Nepomuk2/Query/Query>
 
35
#include <Nepomuk2/Query/QueryServiceClient>
 
36
#include <Nepomuk2/Query/Result>
 
37
#include <Nepomuk2/Resource>
 
38
 
 
39
#include <Soprano/Vocabulary/NAO>
 
40
#include <Nepomuk2/Vocabulary/NIE>
 
41
 
 
42
#include <common/dbus/org.kde.ActivityManager.Activities.h>
 
43
 
 
44
#include <utils/merge_into.h>
 
45
 
 
46
// from libkactivities (core)
 
47
#include "../core/manager_p.h"
 
48
#include "../core/utils_p.h"
 
49
 
 
50
#include "utils_p.h"
 
51
 
 
52
namespace Nepomuk = Nepomuk2;
 
53
namespace NQuery = Nepomuk2::Query;
 
54
 
 
55
using namespace Soprano::Vocabulary;
 
56
using namespace Nepomuk::Vocabulary;
 
57
 
 
58
namespace KActivities {
 
59
namespace Models {
 
60
 
 
61
template <typename T>
 
62
inline short sign(T value)
 
63
{
 
64
    return value >= 0 ? 1 : -1;
 
65
}
 
66
 
 
67
template <typename T>
 
68
inline T abs(T value)
 
69
{
 
70
    return value >= 0 ? value : -value;
 
71
}
 
72
 
 
73
struct ResourceInfo {
 
74
    QUrl resource;
 
75
    QString url;
 
76
    QString title;
 
77
    QString icon;
 
78
    double score;
 
79
 
 
80
    bool operator>=(const ResourceInfo &other) const
 
81
    {
 
82
        short this_sign = sign(score);
 
83
        short other_sign = sign(other.score);
 
84
 
 
85
        if (this_sign != other_sign) {
 
86
            return (other_sign == -1);
 
87
        }
 
88
 
 
89
        double this_abs = abs(score);
 
90
        double other_abs = abs(other.score);
 
91
 
 
92
        if (this_abs < other_abs) {
 
93
            return true;
 
94
        }
 
95
 
 
96
        return title < other.title;
 
97
    }
 
98
 
 
99
    bool operator<(const ResourceInfo &other) const
 
100
    {
 
101
        return !(*this >= other);
 
102
    }
 
103
};
 
104
 
 
105
typedef QList<ResourceInfo> ResourceInfoList;
 
106
 
 
107
class ResourceModel::Private {
 
108
public:
 
109
    DECLARE_RAII_MODEL_UPDATERS(ResourceModel)
 
110
 
 
111
    Private(ResourceModel *parent)
 
112
        : limit(10)
 
113
        , service(0)
 
114
        , q(parent)
 
115
        , valid(false)
 
116
        , showCurrentActivity(true)
 
117
    {
 
118
        servicePresenceChanged(Manager::isServicePresent());
 
119
 
 
120
        connect(Manager::self(), SIGNAL(servicePresenceChanged(bool)),
 
121
                q, SLOT(servicePresenceChanged(bool)));
 
122
    }
 
123
 
 
124
    void reload();
 
125
    void servicePresenceChanged(bool present);
 
126
    void resourceScoreUpdated(const QString &activity, const QString &client, const QString &resource, double score);
 
127
 
 
128
    void newEntries(const QList<NQuery::Result> &entries);
 
129
    void entriesRemoved(const QList<QUrl> &entries);
 
130
    void error(const QString &errorMessage);
 
131
 
 
132
    static ResourceInfo infoFromResult(const NQuery::Result &result);
 
133
 
 
134
    void loadFromQuery(const QString &query);
 
135
    void loadLinked();
 
136
    void loadTopRated();
 
137
    void loadRecent();
 
138
 
 
139
    QString activityToShowN3() const;
 
140
    void setCurrentActivity(const QString &activity);
 
141
 
 
142
    QString activity;
 
143
    QString currentActivity;
 
144
    QString application;
 
145
    int limit;
 
146
    ResourceModel::ContentMode contentMode;
 
147
 
 
148
    QSet<QString> resourceSet;
 
149
    ResourceInfoList resources;
 
150
    QList<NQuery::QueryServiceClient *> queries;
 
151
 
 
152
    QDBusInterface *service;
 
153
 
 
154
    ResourceModel *const q;
 
155
    bool valid : 1;
 
156
    bool showCurrentActivity : 1;
 
157
};
 
158
 
 
159
void ResourceModel::Private::reload()
 
160
{
 
161
    servicePresenceChanged(Manager::isServicePresent());
 
162
}
 
163
 
 
164
void ResourceModel::Private::resourceScoreUpdated(const QString &activity, const QString &client, const QString &resource, double score)
 
165
{
 
166
    // qDebug() << activity << client << resource << score;
 
167
}
 
168
 
 
169
void ResourceModel::Private::servicePresenceChanged(bool present)
 
170
{
 
171
    // qDebug() << present;
 
172
    model_reset m(q);
 
173
 
 
174
    resources.clear();
 
175
    resourceSet.clear();
 
176
 
 
177
    valid = present;
 
178
 
 
179
    if (service) {
 
180
        delete service;
 
181
        service = 0;
 
182
    }
 
183
 
 
184
    if (!valid) {
 
185
        return;
 
186
    }
 
187
 
 
188
    if (showCurrentActivity && currentActivity.isEmpty()) {
 
189
        // we need to show the current activity, but don't know which it is
 
190
 
 
191
        bool res = Manager::activities()->callWithCallback("CurrentActivity", QVariantList(), q, SLOT(setCurrentActivity(QString)));
 
192
        // qDebug() << "CALLING" << res;
 
193
 
 
194
        connect(Manager::activities(), SIGNAL(CurrentActivityChanged(QString)),
 
195
                q, SLOT(setCurrentActivity(QString)));
 
196
 
 
197
        return;
 
198
    }
 
199
 
 
200
    service = new QDBusInterface(
 
201
        "org.kde.ActivityManager",
 
202
        "/ActivityManager/Resources/Scoring",
 
203
        "org.kde.ActivityManager.Resources.Scoring");
 
204
 
 
205
    // connect(service, SIGNAL(resourceScoreUpdated(QString, QString, QString, double)),
 
206
    //         q, SLOT(resourceScoreUpdated(QString, QString, QString, double)));
 
207
 
 
208
    qDeleteAll(queries);
 
209
    queries.clear();
 
210
 
 
211
    contentMode = Recent;
 
212
 
 
213
    switch (contentMode) {
 
214
    case Favorites:
 
215
        loadTopRated();
 
216
        loadLinked();
 
217
        break;
 
218
 
 
219
    case Linked:
 
220
        loadLinked();
 
221
        break;
 
222
 
 
223
    case TopRated:
 
224
        loadTopRated();
 
225
        break;
 
226
 
 
227
    case Recent:
 
228
        loadRecent();
 
229
        break;
 
230
    }
 
231
}
 
232
 
 
233
void ResourceModel::Private::loadFromQuery(const QString &query)
 
234
{
 
235
    // qDebug() << query;
 
236
 
 
237
    NQuery::QueryServiceClient *queryClient = new NQuery::QueryServiceClient(q);
 
238
 
 
239
    NQuery::RequestPropertyMap requestPropertyMap;
 
240
    requestPropertyMap.insert("url", NIE::url());
 
241
    requestPropertyMap.insert("title", NAO::prefLabel());
 
242
    requestPropertyMap.insert("score", NAO::numericRating());
 
243
    requestPropertyMap.insert("icon", NAO::iconName());
 
244
 
 
245
    queryClient->sparqlQuery(query, requestPropertyMap);
 
246
 
 
247
    connect(queryClient, SIGNAL(newEntries(QList<Nepomuk2::Query::Result>)),
 
248
            q, SLOT(newEntries(QList<Nepomuk2::Query::Result>)));
 
249
    connect(queryClient, SIGNAL(entriesRemoved(QList<QUrl>)),
 
250
            q, SLOT(entriesRemoved(QList<QUrl>)));
 
251
    connect(queryClient, SIGNAL(error(QString)),
 
252
            q, SLOT(error(QString)));
 
253
 
 
254
    queries << queryClient;
 
255
}
 
256
 
 
257
void ResourceModel::Private::loadLinked()
 
258
{
 
259
    static const QString &_query = QStringLiteral(
 
260
        "select distinct ?r, ?url, -1 as ?score, ?title, ?icon where { "
 
261
        "?activity nao:isRelated ?r . "
 
262
        "?activity kao:activityIdentifier %1. "
 
263
        "?r nie:url ?url . "
 
264
        "OPTIONAL { ?r nao:prefLabel ?title } . "
 
265
        "OPTIONAL { ?r nao:iconName ?icon } . "
 
266
        "%2 "
 
267
        "}");
 
268
 
 
269
    static const QString &_applicationFilter = QStringLiteral(
 
270
        "?scoreCache a kao:ResourceScoreCache . "
 
271
        "?scoreCache kao:usedActivity ?activity . "
 
272
        "?scoreCache kao:targettedResource ?r . "
 
273
        "?scoreCache kao:initiatingAgent ?agent . "
 
274
        "?agent nao:identifier %1 .");
 
275
 
 
276
    loadFromQuery(_query.arg(
 
277
        activityToShowN3(),
 
278
        (application.isEmpty() ? QString() : _applicationFilter.arg(Soprano::Node::literalToN3(application)))));
 
279
}
 
280
 
 
281
void ResourceModel::Private::loadRecent()
 
282
{
 
283
    static const QString &_query = QStringLiteral(
 
284
        "select distinct ?r, ?url, "
 
285
        // "(bif:datediff ('second', \"1970-01-01\"^^<http://www.w3.org/2001/XMLSchema#date>, ?lastModified)) as ?score, "
 
286
        "(bif:datediff ('second', \"1970-01-01\"^^<http://www.w3.org/2001/XMLSchema#date>, ?lastModified)) as ?score, "
 
287
        "?title, ?icon where { "
 
288
        "?scoreCache a kao:ResourceScoreCache . "
 
289
        "?scoreCache kao:usedActivity ?activity . "
 
290
        "?activity kao:activityIdentifier %1. "
 
291
        "?scoreCache kao:targettedResource ?r . "
 
292
        "?scoreCache nao:lastModified ?lastModified . "
 
293
        "?r nie:url ?url . "
 
294
        "OPTIONAL { ?r nao:prefLabel ?title } . "
 
295
        "OPTIONAL { ?r nao:iconName ?icon } . "
 
296
        "%2 "
 
297
        "} order by desc(?score) limit %3");
 
298
 
 
299
    static const QString &_applicationFilter = QStringLiteral(
 
300
        "?scoreCache kao:initiatingAgent ?agent . "
 
301
        "?agent nao:identifier %1 .");
 
302
 
 
303
    // qDebug() << Soprano::Node::literalToN3(QDate(1970, 1, 1));
 
304
 
 
305
    loadFromQuery(_query.arg(
 
306
        activityToShowN3(),
 
307
        (application.isEmpty() ? QString() : _applicationFilter.arg(Soprano::Node::literalToN3(application))),
 
308
        QString::number(limit)));
 
309
}
 
310
 
 
311
void ResourceModel::Private::loadTopRated()
 
312
{
 
313
    static const QString &_query = QStringLiteral(
 
314
        "select distinct ?r, ?url, ?score, ?title, ?icon where { "
 
315
        "?scoreCache a kao:ResourceScoreCache . "
 
316
        "?scoreCache kao:usedActivity ?activity . "
 
317
        "?activity kao:activityIdentifier %1. "
 
318
        "?scoreCache kao:targettedResource ?r . "
 
319
        "?scoreCache kao:cachedScore ?score . "
 
320
        "?r nie:url ?url . "
 
321
        "OPTIONAL { ?r nao:prefLabel ?title } . "
 
322
        "OPTIONAL { ?r nao:iconName ?icon } . "
 
323
        "%2 "
 
324
        "} order by desc(?score) limit %3");
 
325
 
 
326
    static const QString &_applicationFilter = QStringLiteral(
 
327
        "?scoreCache kao:initiatingAgent ?agent . "
 
328
        "?agent nao:identifier %1 .");
 
329
 
 
330
    loadFromQuery(_query.arg(
 
331
        activityToShowN3(),
 
332
        (application.isEmpty() ? QString() : _applicationFilter.arg(Soprano::Node::literalToN3(application))),
 
333
        QString::number(limit)));
 
334
}
 
335
 
 
336
QString ResourceModel::Private::activityToShowN3() const
 
337
{
 
338
    return Soprano::Node::literalToN3(
 
339
        activity.isEmpty() ? currentActivity : activity);
 
340
}
 
341
 
 
342
ResourceInfo ResourceModel::Private::infoFromResult(const NQuery::Result &result)
 
343
{
 
344
    ResourceInfo info;
 
345
    info.resource = result.resource().uri();
 
346
 
 
347
    QHash<Nepomuk::Types::Property, Soprano::Node> props = result.requestProperties();
 
348
 
 
349
    info.url = props[NIE::url()].toString();
 
350
    info.title = props[NAO::prefLabel()].toString();
 
351
    info.icon = props[NAO::iconName()].toString();
 
352
    info.score = props[NAO::numericRating()].toString().toDouble();
 
353
 
 
354
    // qDebug()
 
355
        // << info.url
 
356
        // << info.title
 
357
        // << info.icon
 
358
        // << info.score;
 
359
 
 
360
    if (info.title.isEmpty() /*&& info.url.startsWith("file://")*/) {
 
361
        KFileItem fileItem(KFileItem::Unknown, KFileItem::Unknown, info.url);
 
362
 
 
363
        if (fileItem.isFile() || fileItem.isDir()) {
 
364
            info.title = fileItem.text();
 
365
            info.icon = fileItem.iconName();
 
366
 
 
367
            // qDebug() << "## 1 ##" << info.title << info.icon;
 
368
 
 
369
        } else {
 
370
            info.title = info.url;
 
371
        }
 
372
    }
 
373
 
 
374
    return info;
 
375
}
 
376
 
 
377
void ResourceModel::Private::newEntries(const QList<NQuery::Result> &entries)
 
378
{
 
379
    // model_insert m(q, QModelIndex(), 0, entries.size());
 
380
    model_reset m(q);
 
381
 
 
382
    ResourceInfoList newEntries;
 
383
 
 
384
    foreach(const NQuery::Result & result, entries)
 
385
    {
 
386
        const ResourceInfo entry = infoFromResult(result);
 
387
 
 
388
        if (entry.title.isEmpty()
 
389
            || resourceSet.contains(entry.url)
 
390
            || entry.url.startsWith(QLatin1String("filex://"))) {
 
391
            continue;
 
392
        }
 
393
 
 
394
        resourceSet << entry.url;
 
395
        newEntries << entry;
 
396
    }
 
397
 
 
398
    kamd::utils::merge_into(resources, newEntries);
 
399
 
 
400
    valid = 1;
 
401
}
 
402
 
 
403
void ResourceModel::Private::entriesRemoved(const QList<QUrl> &entries)
 
404
{
 
405
    model_reset m(q);
 
406
 
 
407
    foreach(const QUrl & entry, entries)
 
408
    {
 
409
        // qDebug() << "Removing: " << entry;
 
410
 
 
411
        ResourceInfoList::iterator start = resources.begin();
 
412
        ResourceInfoList::iterator end = resources.end();
 
413
 
 
414
        while (start != end) {
 
415
            if (start->resource == entry) {
 
416
                start = resources.erase(start);
 
417
                end = resources.end();
 
418
 
 
419
            } else {
 
420
                ++start;
 
421
            }
 
422
        }
 
423
    }
 
424
}
 
425
 
 
426
void ResourceModel::Private::setCurrentActivity(const QString &activity)
 
427
{
 
428
    if (currentActivity == activity) {
 
429
        return;
 
430
    }
 
431
 
 
432
    currentActivity = activity;
 
433
 
 
434
    reload();
 
435
}
 
436
 
 
437
void ResourceModel::Private::error(const QString &errorMessage)
 
438
{
 
439
    // qDebug() << errorMessage;
 
440
}
 
441
 
 
442
ResourceModel::ResourceModel(QObject *parent)
 
443
    : QAbstractListModel(parent)
 
444
    , d(new Private(this))
 
445
{
 
446
    d->valid = false;
 
447
 
 
448
    QHash<int, QByteArray> roles;
 
449
 
 
450
    roles[Qt::DisplayRole] = "name";
 
451
    roles[Qt::DecorationRole] = "icon";
 
452
 
 
453
    setRoleNames(roles);
 
454
}
 
455
 
 
456
ResourceModel::~ResourceModel()
 
457
{
 
458
    delete d;
 
459
}
 
460
 
 
461
int ResourceModel::rowCount(const QModelIndex &parent) const
 
462
{
 
463
    Q_UNUSED(parent);
 
464
 
 
465
    if (!d->valid) {
 
466
        return 0;
 
467
    }
 
468
 
 
469
    return qMin(d->limit, d->resources.size());
 
470
}
 
471
 
 
472
QVariant ResourceModel::data(const QModelIndex &index, int role) const
 
473
{
 
474
    if (!d->valid) {
 
475
        return QVariant();
 
476
    }
 
477
 
 
478
    const int row = index.row();
 
479
 
 
480
    if (row >= d->resources.size()) {
 
481
        return QVariant();
 
482
    }
 
483
 
 
484
    const ResourceInfo &info = d->resources[row];
 
485
 
 
486
    switch (role) {
 
487
    case Qt::DisplayRole:
 
488
        return info.title;
 
489
    // return QString(info.title + " " + QString::number(info.score));
 
490
 
 
491
    case Qt::DecorationRole:
 
492
        return KIcon(info.icon);
 
493
 
 
494
    case ResourceUrl:
 
495
        return info.url;
 
496
 
 
497
    case ResourceIconName:
 
498
        return info.icon;
 
499
 
 
500
    case ResourceScore:
 
501
        return info.score;
 
502
 
 
503
    default:
 
504
        return QVariant();
 
505
    }
 
506
}
 
507
 
 
508
QVariant ResourceModel::headerData(int section, Qt::Orientation orientation, int role) const
 
509
{
 
510
    Q_UNUSED(orientation)
 
511
 
 
512
    if (section == 0 && role == Qt::DisplayRole) {
 
513
        return i18nc("Header title for resource data model", "Resource");
 
514
    }
 
515
 
 
516
    return QVariant();
 
517
}
 
518
 
 
519
void ResourceModel::setActivity(const QString &activity)
 
520
{
 
521
    if (d->activity == activity) {
 
522
        return;
 
523
    }
 
524
 
 
525
    d->activity = activity;
 
526
 
 
527
    d->showCurrentActivity = d->activity.isEmpty();
 
528
 
 
529
    emit activityChanged(activity);
 
530
 
 
531
    d->reload();
 
532
}
 
533
 
 
534
QString ResourceModel::activity() const
 
535
{
 
536
    return d->activity;
 
537
}
 
538
 
 
539
void ResourceModel::setApplication(const QString &application)
 
540
{
 
541
    if (d->application == application) {
 
542
        return;
 
543
    }
 
544
 
 
545
    // qDebug() << "Setting the application to:" << application;
 
546
 
 
547
    d->application = application;
 
548
 
 
549
    emit applicationChanged(application);
 
550
 
 
551
    d->reload();
 
552
}
 
553
 
 
554
QString ResourceModel::application() const
 
555
{
 
556
    return d->application;
 
557
}
 
558
 
 
559
void ResourceModel::setLimit(int count)
 
560
{
 
561
    if (d->limit == count) {
 
562
        return;
 
563
    }
 
564
 
 
565
    d->limit = count;
 
566
 
 
567
    emit limitChanged(count);
 
568
 
 
569
    d->reload();
 
570
}
 
571
 
 
572
int ResourceModel::limit() const
 
573
{
 
574
    return d->limit;
 
575
}
 
576
 
 
577
void ResourceModel::setContentMode(ResourceModel::ContentMode mode)
 
578
{
 
579
    if (d->contentMode == mode) {
 
580
        return;
 
581
    }
 
582
 
 
583
    d->contentMode = mode;
 
584
    d->reload();
 
585
}
 
586
 
 
587
ResourceModel::ContentMode ResourceModel::contentMode() const
 
588
{
 
589
    return d->contentMode;
 
590
}
 
591
 
 
592
} // namespace Models
 
593
} // namespace KActivities
 
594
 
 
595
#include "resourcemodel.moc"