2
* Copyright (C) 2012 Ivan Cukic <ivan.cukic(at)kde.org>
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
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
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.
20
#include "resourcemodel.h"
23
#include <QDBusPendingCall>
24
#include <QDBusPendingCallWatcher>
27
#include <QModelIndex>
31
#include <KLocalizedString>
34
#include <Nepomuk2/Query/Query>
35
#include <Nepomuk2/Query/QueryServiceClient>
36
#include <Nepomuk2/Query/Result>
37
#include <Nepomuk2/Resource>
39
#include <Soprano/Vocabulary/NAO>
40
#include <Nepomuk2/Vocabulary/NIE>
42
#include <common/dbus/org.kde.ActivityManager.Activities.h>
44
#include <utils/merge_into.h>
46
// from libkactivities (core)
47
#include "../core/manager_p.h"
48
#include "../core/utils_p.h"
52
namespace Nepomuk = Nepomuk2;
53
namespace NQuery = Nepomuk2::Query;
55
using namespace Soprano::Vocabulary;
56
using namespace Nepomuk::Vocabulary;
58
namespace KActivities {
62
inline short sign(T value)
64
return value >= 0 ? 1 : -1;
70
return value >= 0 ? value : -value;
80
bool operator>=(const ResourceInfo &other) const
82
short this_sign = sign(score);
83
short other_sign = sign(other.score);
85
if (this_sign != other_sign) {
86
return (other_sign == -1);
89
double this_abs = abs(score);
90
double other_abs = abs(other.score);
92
if (this_abs < other_abs) {
96
return title < other.title;
99
bool operator<(const ResourceInfo &other) const
101
return !(*this >= other);
105
typedef QList<ResourceInfo> ResourceInfoList;
107
class ResourceModel::Private {
109
DECLARE_RAII_MODEL_UPDATERS(ResourceModel)
111
Private(ResourceModel *parent)
116
, showCurrentActivity(true)
118
servicePresenceChanged(Manager::isServicePresent());
120
connect(Manager::self(), SIGNAL(servicePresenceChanged(bool)),
121
q, SLOT(servicePresenceChanged(bool)));
125
void servicePresenceChanged(bool present);
126
void resourceScoreUpdated(const QString &activity, const QString &client, const QString &resource, double score);
128
void newEntries(const QList<NQuery::Result> &entries);
129
void entriesRemoved(const QList<QUrl> &entries);
130
void error(const QString &errorMessage);
132
static ResourceInfo infoFromResult(const NQuery::Result &result);
134
void loadFromQuery(const QString &query);
139
QString activityToShowN3() const;
140
void setCurrentActivity(const QString &activity);
143
QString currentActivity;
146
ResourceModel::ContentMode contentMode;
148
QSet<QString> resourceSet;
149
ResourceInfoList resources;
150
QList<NQuery::QueryServiceClient *> queries;
152
QDBusInterface *service;
154
ResourceModel *const q;
156
bool showCurrentActivity : 1;
159
void ResourceModel::Private::reload()
161
servicePresenceChanged(Manager::isServicePresent());
164
void ResourceModel::Private::resourceScoreUpdated(const QString &activity, const QString &client, const QString &resource, double score)
166
// qDebug() << activity << client << resource << score;
169
void ResourceModel::Private::servicePresenceChanged(bool present)
171
// qDebug() << present;
188
if (showCurrentActivity && currentActivity.isEmpty()) {
189
// we need to show the current activity, but don't know which it is
191
bool res = Manager::activities()->callWithCallback("CurrentActivity", QVariantList(), q, SLOT(setCurrentActivity(QString)));
192
// qDebug() << "CALLING" << res;
194
connect(Manager::activities(), SIGNAL(CurrentActivityChanged(QString)),
195
q, SLOT(setCurrentActivity(QString)));
200
service = new QDBusInterface(
201
"org.kde.ActivityManager",
202
"/ActivityManager/Resources/Scoring",
203
"org.kde.ActivityManager.Resources.Scoring");
205
// connect(service, SIGNAL(resourceScoreUpdated(QString, QString, QString, double)),
206
// q, SLOT(resourceScoreUpdated(QString, QString, QString, double)));
211
contentMode = Recent;
213
switch (contentMode) {
233
void ResourceModel::Private::loadFromQuery(const QString &query)
235
// qDebug() << query;
237
NQuery::QueryServiceClient *queryClient = new NQuery::QueryServiceClient(q);
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());
245
queryClient->sparqlQuery(query, requestPropertyMap);
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)));
254
queries << queryClient;
257
void ResourceModel::Private::loadLinked()
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. "
264
"OPTIONAL { ?r nao:prefLabel ?title } . "
265
"OPTIONAL { ?r nao:iconName ?icon } . "
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 .");
276
loadFromQuery(_query.arg(
278
(application.isEmpty() ? QString() : _applicationFilter.arg(Soprano::Node::literalToN3(application)))));
281
void ResourceModel::Private::loadRecent()
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 . "
294
"OPTIONAL { ?r nao:prefLabel ?title } . "
295
"OPTIONAL { ?r nao:iconName ?icon } . "
297
"} order by desc(?score) limit %3");
299
static const QString &_applicationFilter = QStringLiteral(
300
"?scoreCache kao:initiatingAgent ?agent . "
301
"?agent nao:identifier %1 .");
303
// qDebug() << Soprano::Node::literalToN3(QDate(1970, 1, 1));
305
loadFromQuery(_query.arg(
307
(application.isEmpty() ? QString() : _applicationFilter.arg(Soprano::Node::literalToN3(application))),
308
QString::number(limit)));
311
void ResourceModel::Private::loadTopRated()
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 . "
321
"OPTIONAL { ?r nao:prefLabel ?title } . "
322
"OPTIONAL { ?r nao:iconName ?icon } . "
324
"} order by desc(?score) limit %3");
326
static const QString &_applicationFilter = QStringLiteral(
327
"?scoreCache kao:initiatingAgent ?agent . "
328
"?agent nao:identifier %1 .");
330
loadFromQuery(_query.arg(
332
(application.isEmpty() ? QString() : _applicationFilter.arg(Soprano::Node::literalToN3(application))),
333
QString::number(limit)));
336
QString ResourceModel::Private::activityToShowN3() const
338
return Soprano::Node::literalToN3(
339
activity.isEmpty() ? currentActivity : activity);
342
ResourceInfo ResourceModel::Private::infoFromResult(const NQuery::Result &result)
345
info.resource = result.resource().uri();
347
QHash<Nepomuk::Types::Property, Soprano::Node> props = result.requestProperties();
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();
360
if (info.title.isEmpty() /*&& info.url.startsWith("file://")*/) {
361
KFileItem fileItem(KFileItem::Unknown, KFileItem::Unknown, info.url);
363
if (fileItem.isFile() || fileItem.isDir()) {
364
info.title = fileItem.text();
365
info.icon = fileItem.iconName();
367
// qDebug() << "## 1 ##" << info.title << info.icon;
370
info.title = info.url;
377
void ResourceModel::Private::newEntries(const QList<NQuery::Result> &entries)
379
// model_insert m(q, QModelIndex(), 0, entries.size());
382
ResourceInfoList newEntries;
384
foreach(const NQuery::Result & result, entries)
386
const ResourceInfo entry = infoFromResult(result);
388
if (entry.title.isEmpty()
389
|| resourceSet.contains(entry.url)
390
|| entry.url.startsWith(QLatin1String("filex://"))) {
394
resourceSet << entry.url;
398
kamd::utils::merge_into(resources, newEntries);
403
void ResourceModel::Private::entriesRemoved(const QList<QUrl> &entries)
407
foreach(const QUrl & entry, entries)
409
// qDebug() << "Removing: " << entry;
411
ResourceInfoList::iterator start = resources.begin();
412
ResourceInfoList::iterator end = resources.end();
414
while (start != end) {
415
if (start->resource == entry) {
416
start = resources.erase(start);
417
end = resources.end();
426
void ResourceModel::Private::setCurrentActivity(const QString &activity)
428
if (currentActivity == activity) {
432
currentActivity = activity;
437
void ResourceModel::Private::error(const QString &errorMessage)
439
// qDebug() << errorMessage;
442
ResourceModel::ResourceModel(QObject *parent)
443
: QAbstractListModel(parent)
444
, d(new Private(this))
448
QHash<int, QByteArray> roles;
450
roles[Qt::DisplayRole] = "name";
451
roles[Qt::DecorationRole] = "icon";
456
ResourceModel::~ResourceModel()
461
int ResourceModel::rowCount(const QModelIndex &parent) const
469
return qMin(d->limit, d->resources.size());
472
QVariant ResourceModel::data(const QModelIndex &index, int role) const
478
const int row = index.row();
480
if (row >= d->resources.size()) {
484
const ResourceInfo &info = d->resources[row];
487
case Qt::DisplayRole:
489
// return QString(info.title + " " + QString::number(info.score));
491
case Qt::DecorationRole:
492
return KIcon(info.icon);
497
case ResourceIconName:
508
QVariant ResourceModel::headerData(int section, Qt::Orientation orientation, int role) const
510
Q_UNUSED(orientation)
512
if (section == 0 && role == Qt::DisplayRole) {
513
return i18nc("Header title for resource data model", "Resource");
519
void ResourceModel::setActivity(const QString &activity)
521
if (d->activity == activity) {
525
d->activity = activity;
527
d->showCurrentActivity = d->activity.isEmpty();
529
emit activityChanged(activity);
534
QString ResourceModel::activity() const
539
void ResourceModel::setApplication(const QString &application)
541
if (d->application == application) {
545
// qDebug() << "Setting the application to:" << application;
547
d->application = application;
549
emit applicationChanged(application);
554
QString ResourceModel::application() const
556
return d->application;
559
void ResourceModel::setLimit(int count)
561
if (d->limit == count) {
567
emit limitChanged(count);
572
int ResourceModel::limit() const
577
void ResourceModel::setContentMode(ResourceModel::ContentMode mode)
579
if (d->contentMode == mode) {
583
d->contentMode = mode;
587
ResourceModel::ContentMode ResourceModel::contentMode() const
589
return d->contentMode;
592
} // namespace Models
593
} // namespace KActivities
595
#include "resourcemodel.moc"