import CameraPlus 1.0
Item {
- id: item
- property bool isVideo: type.search("nmm#Video") > 0
+ id: postCaptureItem
+ property bool isVideo: itemData.type.search("nmm#Video") > 0
property bool error: false
-
+ property variant itemData: item
property bool isCurrentItem: PathView.isCurrentItem
- onIsCurrentItemChanged: page.currentItem = item;
-
- property string fileName: filename
- property string creationDate: created
- property string itemTitle: title
- property bool itemAvailable: available
- property url itemUrl: url
+ onIsCurrentItemChanged: {
+ if (isCurrentItem) {
+ page.currentItem = postCaptureItem;
+ }
+ }
function startPlayback() {
openFileNow("VideoPlayerPage.qml");
anchors.fill: parent
visible: page.status == PageStatus.Activating || page.status == PageStatus.Active
- Component.onCompleted: initialize(url, mimetype);
+ Component.onCompleted: initialize(itemData.url, itemData.mimetype);
MouseArea {
id: mouse
import QtQuick 1.1
import com.nokia.meego 1.1
import QtCamera 1.0
-import QtSparql 1.0
import CameraPlus 1.0
// TODO: losing resources while playback won't show an error
-// TODO: show something if we have no files.
// TODO: favorites
+// TODO: mass storage mode interaction
CameraPage {
id: page
standbyVisible: !Qt.application.active
property Item currentItem: null
- property bool available: currentItem ? currentItem.itemAvailable : false
-
- function parseDate(str) {
- var parts = str.split('T');
- var dates = parts[0].split('-');
- var times = parts[1].split(':');
- return new Date(dates[0], dates[1], dates[2], times[0], times[1], times[2]);
- }
+ property bool available: currentItem ? currentItem.itemData.available : false
+ Component.onCompleted: postCaptureModel.reload();
function launchGallery() {
if (!gallery.launch()) {
return;
}
- deleteDialog.message = currentItem.fileName;
+ deleteDialog.message = currentItem.itemData.fileName;
deleteDialog.open();
}
acceptButtonText: qsTr("Yes");
rejectButtonText: qsTr("No");
onAccepted: {
- // TODO: Remove from model and move to next item
- if (!remove.remove(currentItem.itemUrl)) {
+ if (!remove.remove(currentItem.itemData.url)) {
showError(qsTr("Failed to delete item"));
}
+ else {
+ postCaptureModel.remove(currentItem.itemData);
+ }
}
DeleteHelper {
return;
}
- if (!share.share(currentItem.itemUrl)) {
+ if (!share.share(currentItem.itemData.url)) {
showError(qsTr("Failed to launch share service"));
}
}
highlightRangeMode: PathView.StrictlyEnforceRange
pathItemCount: 3
- model: SparqlListModel {
- 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 "urn:equipment:' + deviceInfo.manufacturer + ':' + deviceInfo.model + ':" . {?urn a nfo:Video} UNION {?urn a nfo:Image}} ORDER BY DESC(?created)'
-
- connection: SparqlConnection {
- id: connection
- driver: "QTRACKER_DIRECT"
- onStatusChanged: checkStatus(status)
-
- function checkStatus(status) {
- if (status == SparqlConnection.Error) {
- // TODO: report error
- console.log("Error = " + connection.errorString());
- }
- }
+ model: PostCaptureModel {
+ id: postCaptureModel
+ manufacturer: deviceInfo.manufacturer
+ model: deviceInfo.model
+ onError: {
+ console.log("Error populating model " + msg);
+ showError(qsTr("Failed to load captures"));
}
}
+ Label {
+ // TODO: Hide this when we have no items
+ text: qsTr("No captures available");
+ anchors.centerIn: parent
+ font.pixelSize: 36
+ visible: currentItem == null
+ }
+
delegate: PostCaptureItem {
width: view.width - 10
height: view.height
tools: ToolBarLayout {
Label {
- text: currentItem ? currentItem.itemTitle : ""
+ text: currentItem ? currentItem.itemData.title : ""
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
}
Label {
- text: currentItem ? Qt.formatDateTime(parseDate(currentItem.creationDate)) : ""
+ text: currentItem ? currentItem.itemData.created : ""
font.bold: true
anchors.top: parent.top
anchors.bottom: parent.bottom
#include "sharehelper.h"
#include "deletehelper.h"
#include "galleryhelper.h"
+#include "postcapturemodel.h"
static void initQuill() {
// TODO: All these are hardcoded.
qmlRegisterType<ShareHelper>("CameraPlus", 1, 0, "ShareHelper");
qmlRegisterType<DeleteHelper>("CameraPlus", 1, 0, "DeleteHelper");
qmlRegisterType<GalleryHelper>("CameraPlus", 1, 0, "GalleryHelper");
+ qmlRegisterType<PostCaptureModel>("CameraPlus", 1, 0, "PostCaptureModel");
QUrl sourceUrl = QUrl::fromLocalFile(QDir::currentPath() + "/main.qml");
view.setSource(sourceUrl);
--- /dev/null
+/*!
+ * This file is part of CameraPlus.
+ *
+ * Copyright (C) 2012 Mohammed Sameer <msameer@foolab.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "postcapturemodel.h"
+#include <QSparqlConnection>
+#include <QSparqlQuery>
+#include <QSparqlResult>
+#include <QSparqlError>
+#include <QDeclarativeInfo>
+#include <QDateTime>
+#include <QDBusConnection>
+#include <QStringList>
+#include <QDBusMetaType>
+#include <QDBusArgument>
+
+#define DRIVER "QTRACKER_DIRECT"
+#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)"
+
+#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)) }"
+
+#define TRACKER_SERVICE "org.freedesktop.Tracker1"
+#define TRACKER_RESOURCE_PATH "/org/freedesktop/Tracker1/Resources"
+#define TRACKER_RESOURCE_INTERFACE "org.freedesktop.Tracker1.Resources"
+#define TRACKER_RESOURCE_SIGNAL "GraphUpdated"
+#define PHOTO_CLASS "http://www.tracker-project.org/temp/nmm#Photo"
+#define VIDEO_CLASS "http://www.tracker-project.org/temp/nmm#Video"
+#define TRACKER_RESOURCE_SIGNAL_SIGNATURE "sa(iiii)a(iiii)"
+
+class Quad {
+public:
+ int graph;
+ int subject;
+ int predicate;
+ int object;
+};
+
+Q_DECLARE_METATYPE(Quad);
+Q_DECLARE_METATYPE(QList<Quad>);
+
+QDBusArgument& operator<<(QDBusArgument& argument, const Quad& t) {
+ argument.beginStructure();
+ argument << t.graph << t.subject << t.predicate << t.object;
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument& operator>>(const QDBusArgument& argument, Quad& t) {
+ argument.beginStructure();
+ argument >> t.graph >> t.subject >> t.predicate >> t.object;
+ argument.endStructure();
+ return argument;
+}
+
+PostCaptureModel::PostCaptureModel(QObject *parent) :
+ QAbstractListModel(parent),
+ m_connection(0) {
+
+ QHash<int, QByteArray> roles;
+ roles.insert(Item, "item");
+ setRoleNames(roles);
+
+ qDBusRegisterMetaType<Quad>();
+ qDBusRegisterMetaType<QList<Quad> >();
+}
+
+PostCaptureModel::~PostCaptureModel() {
+ qDeleteAll(m_items);
+ m_items.clear();
+
+ delete m_connection; m_connection = 0;
+}
+
+void PostCaptureModel::classBegin() {
+
+}
+
+void PostCaptureModel::componentComplete() {
+
+}
+
+void PostCaptureModel::reload() {
+ delete m_connection; m_connection = 0;
+
+ if (!m_items.isEmpty()) {
+ beginRemoveRows(QModelIndex(), 0, m_items.size() - 1);
+ qDeleteAll(m_items);
+ m_items.clear();
+ endRemoveRows();
+ }
+
+ m_connection = new QSparqlConnection(DRIVER, QSparqlConnectionOptions(), this);
+ if (!m_connection->isValid()) {
+ emit error(tr("Failed to create SPARQL connection"));
+ return;
+ }
+
+ QString equipment = QString("urn:equipment:%1:%2:").arg(m_manufacturer).arg(m_model);
+
+ QSparqlQuery q(QUERY, QSparqlQuery::SelectStatement);
+ q.bindValue("equipment", equipment);
+ exec(q);
+
+ QDBusConnection::sessionBus().connect(TRACKER_SERVICE, TRACKER_RESOURCE_PATH,
+ TRACKER_RESOURCE_INTERFACE, TRACKER_RESOURCE_SIGNAL,
+ TRACKER_RESOURCE_SIGNAL_SIGNATURE,
+ this, SLOT(graphUpdated(const QString&,
+ const QList<Quad>&,
+ const QList<Quad>&)));
+}
+
+void PostCaptureModel::exec(QSparqlQuery& query) {
+ if (!m_connection) {
+ qWarning() << "No connection to query";
+ return;
+ }
+
+ QSparqlResult *result = m_connection->exec(query);
+
+ if (result->hasError()) {
+ QSparqlError error = result->lastError();
+ qmlInfo(this) << "Error executing SPARQL query" << error.message();
+
+ delete result;
+
+ emit PostCaptureModel::error(tr("Failed to query tracker"));
+ }
+
+ if (result) {
+ QObject::connect(result, SIGNAL(dataReady(int)), this, SLOT(dataReady(int)));
+ QObject::connect(result, SIGNAL(finished()), result, SLOT(deleteLater()));
+ }
+}
+
+int PostCaptureModel::rowCount(const QModelIndex& parent) const {
+ if (parent.isValid()) {
+ return 0;
+ }
+
+ return m_items.size();
+}
+
+QVariant PostCaptureModel::data(const QModelIndex& index, int role) const {
+ if (!index.isValid() || index.row() < 0 || index.row() >= m_items.size()) {
+ return QVariant();
+ }
+
+ if (role == Item) {
+ return QVariant::fromValue(dynamic_cast<QObject *>(m_items[index.row()]));
+ }
+
+ return QVariant();
+}
+
+QString PostCaptureModel::manufacturer() const {
+ return m_manufacturer;
+}
+
+void PostCaptureModel::setManufacturer(const QString& manufacturer) {
+ if (m_manufacturer != manufacturer) {
+ m_manufacturer = manufacturer;
+ emit manufacturerChanged();
+ }
+}
+
+QString PostCaptureModel::model() const {
+ return m_model;
+}
+
+void PostCaptureModel::setModel(const QString& model) {
+ if (m_model != model) {
+ m_model = model;
+ emit modelChanged();
+ }
+}
+
+void PostCaptureModel::dataReady(int totalCount) {
+ Q_UNUSED(totalCount);
+
+ QSparqlResult *result = dynamic_cast<QSparqlResult *>(sender());
+ if (!result) {
+ return;
+ }
+
+ while (result->next()) {
+ addRow(new PostCaptureModelItem(result->current(), this));
+ }
+
+ result->previous();
+}
+
+void PostCaptureModel::addRow(PostCaptureModelItem *item) {
+ if (m_hash.contains(item->trackerId())) {
+ m_hash[item->trackerId()]->update(item);
+ delete item;
+ return;
+ }
+
+ beginInsertRows(QModelIndex(), m_items.size(), m_items.size());
+ m_items << item;
+ m_hash.insert(item->trackerId(), item);
+
+ endInsertRows();
+}
+
+void PostCaptureModel::remove(QObject *item) {
+ PostCaptureModelItem *i = dynamic_cast<PostCaptureModelItem *>(item);
+ if (!i) {
+ qmlInfo(this) << "Invalid item to remove";
+ return;
+ }
+
+ int index = m_items.indexOf(i);
+ if (index == -1) {
+ qmlInfo(this) << "Item" << i->trackerId() << "not found in model";
+ return;
+ }
+
+ beginRemoveRows(QModelIndex(), index, index);
+ m_items.takeAt(index);
+ m_hash.remove(i->trackerId());
+ endRemoveRows();
+
+ i->deleteLater();
+}
+
+void PostCaptureModel::graphUpdated(const QString& className, const QList<Quad>& deleted,
+ const QList<Quad>& inserted) {
+
+ // We will just assume tracker:available has changed and that's it.
+ // We are not really interested in the rest of properties.
+
+ Q_UNUSED(deleted);
+
+ if (!(className == QLatin1String(PHOTO_CLASS) || className == QLatin1String(VIDEO_CLASS))) {
+ return;
+ }
+
+ QList<int> items;
+
+ for (int x = 0; x < inserted.size(); x++) {
+ Quad q = inserted[x];
+ if (m_hash.contains(q.subject) && items.indexOf(q.subject) == -1) {
+ items << q.subject;
+ }
+ }
+
+ for (int x = 0; x < items.size(); x++) {
+ QString query = QString(UPDATE_QUERY).arg(items[x]);
+ QSparqlQuery q(query, QSparqlQuery::SelectStatement);
+
+ exec(q);
+ }
+}
+
+PostCaptureModelItem::PostCaptureModelItem(const QSparqlResultRow& row, QObject *parent) :
+ QObject(parent) {
+
+ for (int x = 0; x < row.count(); x++) {
+ QSparqlBinding b = row.binding(x);
+ m_data.insert(b.name(), b.value());
+ }
+}
+
+QString PostCaptureModelItem::type() const {
+ return value("type").toString();
+}
+
+QUrl PostCaptureModelItem::url() const {
+ return value("url").toUrl();
+}
+
+QString PostCaptureModelItem::created() const {
+ return formatDateTime(value("created").toDateTime());
+}
+
+QString PostCaptureModelItem::title() const {
+ return value("title").toString();
+}
+
+QString PostCaptureModelItem::fileName() const {
+ return value("filename").toString();
+}
+
+QString PostCaptureModelItem::mimeType() const {
+ return value("mimetype").toString();
+}
+
+bool PostCaptureModelItem::available() const {
+ return value("available", false).toBool();
+}
+
+QString PostCaptureModelItem::lastModified() const {
+ return formatDateTime(value("lastmod").toDateTime());
+}
+
+unsigned PostCaptureModelItem::trackerId() const {
+ return value("trackerid").toUInt();
+}
+
+bool PostCaptureModelItem::favorite() const {
+ return value("favorite").toBool();
+}
+
+QString PostCaptureModelItem::formatDateTime(const QDateTime& dt) const {
+ return dt.toString();
+}
+
+void PostCaptureModelItem::update(PostCaptureModelItem *other) {
+ // We will only update available, favorite and title:
+ if (available() != other->available()) {
+ m_data["available"] = other->available();
+ emit availableChanged();
+ }
+
+ if (title() != other->title()) {
+ m_data["title"] = other->title();
+ emit titleChanged();
+ }
+
+ if (favorite() != other->favorite()) {
+ m_data["favorite"] = other->favorite();
+ emit favoriteChanged();
+ }
+}
+
+QVariant PostCaptureModelItem::value(const QString& id, const QVariant& def) const {
+ return m_data.contains(id) ? m_data[id] : def;
+}
--- /dev/null
+// -*- c++ -*-
+
+/*!
+ * This file is part of CameraPlus.
+ *
+ * Copyright (C) 2012 Mohammed Sameer <msameer@foolab.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POST_CAPTURE_MODEL_H
+#define POST_CAPTURE_MODEL_H
+
+#include <QAbstractListModel>
+#include <QDeclarativeParserStatus>
+#include <QUrl>
+
+class QSparqlConnection;
+class PostCaptureModelItem;
+class QDateTime;
+class Quad;
+class QSparqlQuery;
+class QSparqlResultRow;
+
+class PostCaptureModel : public QAbstractListModel, public QDeclarativeParserStatus {
+ Q_OBJECT
+ Q_INTERFACES(QDeclarativeParserStatus);
+
+ Q_PROPERTY(QString manufacturer READ manufacturer WRITE setManufacturer NOTIFY manufacturerChanged);
+ Q_PROPERTY(QString model READ model WRITE setModel NOTIFY modelChanged);
+
+ typedef enum {
+ Item = Qt::UserRole + 1
+ } Roles;
+
+public:
+ PostCaptureModel(QObject *parent = 0);
+ ~PostCaptureModel();
+
+ virtual void classBegin();
+ virtual void componentComplete();
+
+ virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
+ virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
+
+ QString manufacturer() const;
+ void setManufacturer(const QString& manufacturer);
+
+ QString model() const;
+ void setModel(const QString& model);
+
+signals:
+ void error(const QString& msg);
+
+ void manufacturerChanged();
+ void modelChanged();
+
+public slots:
+ void reload();
+ void remove(QObject *item);
+
+private slots:
+ void dataReady(int totalCount);
+ void graphUpdated(const QString& className, const QList<Quad>& deleted,
+ const QList<Quad>& inserted);
+
+private:
+ void addRow(PostCaptureModelItem *item);
+ void exec(QSparqlQuery& query);
+
+ QSparqlConnection *m_connection;
+ QString m_manufacturer;
+ QString m_model;
+
+ QList<PostCaptureModelItem *> m_items;
+ QHash<int, PostCaptureModelItem *> m_hash;
+};
+
+class PostCaptureModelItem : public QObject {
+ Q_OBJECT
+
+ Q_PROPERTY(QString type READ type NOTIFY typeChanged);
+ Q_PROPERTY(QUrl url READ url NOTIFY urlChanged);
+ Q_PROPERTY(QString created READ created NOTIFY createdChanged);
+ Q_PROPERTY(QString title READ title NOTIFY titleChanged);
+ Q_PROPERTY(QString fileName READ fileName NOTIFY fileNameChanged);
+ Q_PROPERTY(QString mimeType READ mimeType NOTIFY mimeTypeChanged);
+ Q_PROPERTY(bool available READ available NOTIFY availableChanged);
+ Q_PROPERTY(QString lastModified READ lastModified NOTIFY lastModifiedChanged);
+ Q_PROPERTY(unsigned trackerId READ trackerId CONSTANT);
+ Q_PROPERTY(bool favorite READ favorite NOTIFY favoriteChanged);
+
+public:
+ PostCaptureModelItem(const QSparqlResultRow& row, QObject *parent = 0);
+
+ ~PostCaptureModelItem() {
+
+ }
+
+ void update(PostCaptureModelItem *other);
+
+ QString type() const;
+ QUrl url() const;
+ QString created() const;
+ QString title() const;
+ QString fileName() const;
+ QString mimeType() const;
+ bool available() const;
+ QString lastModified() const;
+ unsigned trackerId() const;
+ bool favorite() const;
+
+signals:
+ void typeChanged();
+ void urlChanged();
+ void createdChanged();
+ void titleChanged();
+ void fileNameChanged();
+ void mimeTypeChanged();
+ void availableChanged();
+ void lastModifiedChanged();
+ void favoriteChanged();
+
+private:
+ QString formatDateTime(const QDateTime& dt) const;
+ QVariant value(const QString& id, const QVariant& def = QVariant()) const;
+
+ QVariantMap m_data;
+};
+
+#endif /* POST_CAPTURE_MODEL_H */
SOURCES += main.cpp settings.cpp filenaming.cpp quillitem.cpp displaystate.cpp fsmonitor.cpp \
cameraresources.cpp compass.cpp orientation.cpp geocode.cpp mountprotector.cpp \
- trackerstore.cpp focusrectangle.cpp sharehelper.cpp deletehelper.cpp galleryhelper.cpp
+ trackerstore.cpp focusrectangle.cpp sharehelper.cpp deletehelper.cpp galleryhelper.cpp \
+ postcapturemodel.cpp
HEADERS += settings.h filenaming.h quillitem.h displaystate.h fsmonitor.h \
cameraresources.h compass.h orientation.h geocode.h mountprotector.h \
- trackerstore.h focusrectangle.h sharehelper.h deletehelper.h galleryhelper.h
+ trackerstore.h focusrectangle.h sharehelper.h deletehelper.h galleryhelper.h \
+ postcapturemodel.h