2 * This file is part of CameraPlus.
4 * Copyright (C) 2012-2013 Mohammed Sameer <msameer@foolab.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "postcapturemodel.h"
22 #include <QSparqlConnection>
23 #include <QSparqlQuery>
24 #include <QSparqlResult>
25 #include <QSparqlError>
27 #include <QDeclarativeInfo>
32 #include <QDBusConnection>
33 #include <QStringList>
34 #include <QDBusMetaType>
35 #include <QDBusArgument>
37 #define DRIVER "QTRACKER_DIRECT"
38 #define QUERY "SELECT rdf:type(?urn) AS ?type nie:url(?urn) AS ?url nie:contentCreated(?urn) AS ?created nie:title(?urn) AS ?title nfo:fileName(?urn) AS ?filename nie:mimeType(?urn) AS ?mimetype tracker:available(?urn) AS ?available nfo:fileLastModified(?urn) as ?lastmod tracker:id(?urn) AS ?trackerid (EXISTS { ?urn nao:hasTag nao:predefined-tag-favorite }) AS ?favorite WHERE { ?urn nfo:equipment ?:equipment . {?urn a nfo:Video} UNION {?urn a nfo:Image}} ORDER BY DESC(?created)"
40 #define UPDATE_QUERY "SELECT rdf:type(?urn) AS ?type nie:url(?urn) AS ?url nie:contentCreated(?urn) AS ?created nie:title(?urn) AS ?title nfo:fileName(?urn) AS ?filename nie:mimeType(?urn) AS ?mimetype tracker:available(?urn) AS ?available nfo:fileLastModified(?urn) as ?lastmod tracker:id(?urn) AS ?trackerid (EXISTS { ?urn nao:hasTag nao:predefined-tag-favorite }) AS ?favorite WHERE {?urn a nfo:Visual . FILTER (tracker:id(?urn) IN (%1)) }"
42 #define TRACKER_SERVICE "org.freedesktop.Tracker1"
43 #define TRACKER_RESOURCE_PATH "/org/freedesktop/Tracker1/Resources"
44 #define TRACKER_RESOURCE_INTERFACE "org.freedesktop.Tracker1.Resources"
45 #define TRACKER_RESOURCE_SIGNAL "GraphUpdated"
46 #define PHOTO_CLASS "http://www.tracker-project.org/temp/nmm#Photo"
47 #define VIDEO_CLASS "http://www.tracker-project.org/temp/nmm#Video"
48 #define TRACKER_RESOURCE_SIGNAL_SIGNATURE "sa(iiii)a(iiii)"
58 Q_DECLARE_METATYPE(Quad);
59 Q_DECLARE_METATYPE(QList<Quad>);
61 QDBusArgument& operator<<(QDBusArgument& argument, const Quad& t) {
62 argument.beginStructure();
63 argument << t.graph << t.subject << t.predicate << t.object;
64 argument.endStructure();
68 const QDBusArgument& operator>>(const QDBusArgument& argument, Quad& t) {
69 argument.beginStructure();
70 argument >> t.graph >> t.subject >> t.predicate >> t.object;
71 argument.endStructure();
75 PostCaptureModel::PostCaptureModel(QObject *parent) :
76 QAbstractListModel(parent),
80 qDBusRegisterMetaType<Quad>();
81 qDBusRegisterMetaType<QList<Quad> >();
83 QHash<int, QByteArray> roles;
84 roles.insert(Item, "item");
89 PostCaptureModel::~PostCaptureModel() {
93 delete m_connection; m_connection = 0;
96 void PostCaptureModel::reload() {
97 delete m_connection; m_connection = 0;
99 if (!m_items.isEmpty()) {
100 beginRemoveRows(QModelIndex(), 0, m_items.size() - 1);
106 m_connection = new QSparqlConnection(DRIVER, QSparqlConnectionOptions(), this);
107 if (!m_connection->isValid()) {
108 emit error(tr("Failed to create SPARQL connection"));
112 QString equipment = QString("urn:equipment:%1:%2:").arg(m_manufacturer).arg(m_model);
114 QSparqlQuery q(QUERY, QSparqlQuery::SelectStatement);
115 q.bindValue("equipment", equipment);
119 const char *slot = SLOT(graphUpdated(const QString&,
121 const QList<Quad>&));
122 m_connected = QDBusConnection::sessionBus().connect(TRACKER_SERVICE, TRACKER_RESOURCE_PATH,
123 TRACKER_RESOURCE_INTERFACE,
124 TRACKER_RESOURCE_SIGNAL,
125 TRACKER_RESOURCE_SIGNAL_SIGNATURE,
130 qmlInfo(this) << "Failed to connect to tracker " << TRACKER_RESOURCE_SIGNAL;
134 void PostCaptureModel::exec(QSparqlQuery& query) {
136 qWarning() << "No connection to query";
140 QSparqlResult *result = m_connection->exec(query);
142 if (result->hasError()) {
143 QSparqlError error = result->lastError();
144 qmlInfo(this) << "Error executing SPARQL query" << error.message();
148 emit PostCaptureModel::error(tr("Failed to query tracker"));
152 QObject::connect(result, SIGNAL(dataReady(int)), this, SLOT(dataReady(int)));
153 QObject::connect(result, SIGNAL(finished()), result, SLOT(deleteLater()));
157 int PostCaptureModel::rowCount(const QModelIndex& parent) const {
158 if (parent.isValid()) {
162 return m_items.size();
165 QVariant PostCaptureModel::data(const QModelIndex& index, int role) const {
166 if (!index.isValid() || index.row() < 0 || index.row() >= m_items.size()) {
171 return QVariant::fromValue(dynamic_cast<QObject *>(m_items[index.row()]));
177 QString PostCaptureModel::manufacturer() const {
178 return m_manufacturer;
181 void PostCaptureModel::setManufacturer(const QString& manufacturer) {
182 if (m_manufacturer != manufacturer) {
183 m_manufacturer = manufacturer;
184 emit manufacturerChanged();
188 QString PostCaptureModel::model() const {
192 void PostCaptureModel::setModel(const QString& model) {
193 if (m_model != model) {
199 void PostCaptureModel::dataReady(int totalCount) {
200 Q_UNUSED(totalCount);
202 QSparqlResult *result = dynamic_cast<QSparqlResult *>(sender());
207 while (result->next()) {
208 addRow(new PostCaptureModelItem(result->current(), this));
214 void PostCaptureModel::addRow(PostCaptureModelItem *item) {
215 if (m_hash.contains(item->trackerId())) {
216 m_hash[item->trackerId()]->update(item);
221 beginInsertRows(QModelIndex(), m_items.size(), m_items.size());
223 m_hash.insert(item->trackerId(), item);
228 void PostCaptureModel::remove(QObject *item) {
229 PostCaptureModelItem *i = dynamic_cast<PostCaptureModelItem *>(item);
231 qmlInfo(this) << "Invalid item to remove";
235 int index = m_items.indexOf(i);
237 qmlInfo(this) << "Item" << i->trackerId() << "not found in model";
241 beginRemoveRows(QModelIndex(), index, index);
242 m_items.takeAt(index);
243 m_hash.remove(i->trackerId());
249 void PostCaptureModel::graphUpdated(const QString& className, const QList<Quad>& deleted,
250 const QList<Quad>& inserted) {
252 // We will just assume tracker:available has changed and that's it.
253 // We are not really interested in the rest of properties.
255 if (!(className == QLatin1String(PHOTO_CLASS) || className == QLatin1String(VIDEO_CLASS))) {
261 for (int x = 0; x < deleted.size(); x++) {
263 if (m_hash.contains(q.subject) && items.indexOf(q.subject) == -1) {
268 for (int x = 0; x < inserted.size(); x++) {
269 Quad q = inserted[x];
270 if (m_hash.contains(q.subject) && items.indexOf(q.subject) == -1) {
275 for (int x = 0; x < items.size(); x++) {
276 QString query = QString(UPDATE_QUERY).arg(items[x]);
277 QSparqlQuery q(query, QSparqlQuery::SelectStatement);
284 QHash<int, QByteArray> PostCaptureModel::roleNames() const {
288 void PostCaptureModel::setRoleNames(const QHash<int, QByteArray>& roles) {
293 PostCaptureModelItem::PostCaptureModelItem(const QSparqlResultRow& row, QObject *parent) :
296 for (int x = 0; x < row.count(); x++) {
297 QSparqlBinding b = row.binding(x);
298 m_data.insert(b.name(), b.value());
302 QString PostCaptureModelItem::type() const {
303 return value("type").toString();
306 QUrl PostCaptureModelItem::url() const {
307 return value("url").toUrl();
310 QString PostCaptureModelItem::created() const {
311 return formatDateTime(value("created").toDateTime());
314 QString PostCaptureModelItem::title() const {
315 return value("title").toString();
318 QString PostCaptureModelItem::fileName() const {
319 return value("filename").toString();
322 QString PostCaptureModelItem::mimeType() const {
323 return value("mimetype").toString();
326 bool PostCaptureModelItem::available() const {
327 return value("available", false).toBool();
330 QString PostCaptureModelItem::lastModified() const {
331 return formatDateTime(value("lastmod").toDateTime());
334 unsigned PostCaptureModelItem::trackerId() const {
335 return value("trackerid").toUInt();
338 bool PostCaptureModelItem::favorite() const {
339 return value("favorite", false).toBool();
342 void PostCaptureModelItem::setFavorite(bool add) {
343 if (favorite() != add) {
344 m_data["favorite"] = add;
346 emit favoriteChanged();
350 QString PostCaptureModelItem::formatDateTime(const QDateTime& dt) const {
351 return dt.toString();
354 void PostCaptureModelItem::update(PostCaptureModelItem *other) {
355 // We will only update available, favorite and title:
357 qDebug() << "i" << trackerId() << other->trackerId() << "\n"
358 << "a" << available() << other->available() << "\n"
359 << "t" << title() << other->title() << "\n"
360 << "f" << favorite() << other->favorite();
363 if (available() != other->available()) {
364 m_data["available"] = other->available();
365 emit availableChanged();
368 if (title() != other->title()) {
369 m_data["title"] = other->title();
373 if (favorite() != other->favorite()) {
374 m_data["favorite"] = other->favorite();
375 emit favoriteChanged();
379 QVariant PostCaptureModelItem::value(const QString& id, const QVariant& def) const {
380 return m_data.contains(id) ? m_data[id] : def;