Implemented PostCaptureModel using QtSparql:
authorMohammed Sameer <msameer@foolab.org>
Sat, 29 Dec 2012 01:46:35 +0000 (03:46 +0200)
committerMohammed Sameer <msameer@foolab.org>
Sat, 29 Dec 2012 01:46:35 +0000 (03:46 +0200)
- Ability to remove rows
- Partial live update support

qml/PostCaptureItem.qml
qml/PostCapturePage.qml
src/main.cpp
src/postcapturemodel.cpp [new file with mode: 0644]
src/postcapturemodel.h [new file with mode: 0644]
src/src.pro

index a9d83df..e32b738 100644 (file)
@@ -25,18 +25,16 @@ import com.nokia.meego 1.1
 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");
@@ -58,7 +56,7 @@ Item {
                 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
index 74a1ecb..d105975 100644 (file)
 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
@@ -39,15 +38,9 @@ CameraPage {
         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()) {
@@ -78,7 +71,7 @@ CameraPage {
                         return;
                 }
 
-                deleteDialog.message = currentItem.fileName;
+                deleteDialog.message = currentItem.itemData.fileName;
                 deleteDialog.open();
         }
 
@@ -88,10 +81,12 @@ CameraPage {
                 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 {
@@ -104,7 +99,7 @@ CameraPage {
                         return;
                 }
 
-                if (!share.share(currentItem.itemUrl)) {
+                if (!share.share(currentItem.itemData.url)) {
                                 showError(qsTr("Failed to launch share service"));
                 }
         }
@@ -138,23 +133,24 @@ CameraPage {
                 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
@@ -182,7 +178,7 @@ CameraPage {
 
                 tools: ToolBarLayout {
                         Label {
-                                text: currentItem ? currentItem.itemTitle : ""
+                                text: currentItem ? currentItem.itemData.title : ""
                                 anchors.top: parent.top
                                 anchors.bottom: parent.bottom
                                 anchors.left: parent.left
@@ -191,7 +187,7 @@ CameraPage {
                         }
 
                         Label {
-                                text: currentItem ? Qt.formatDateTime(parseDate(currentItem.creationDate)) : ""
+                                text: currentItem ? currentItem.itemData.created : ""
                                 font.bold: true
                                 anchors.top: parent.top
                                 anchors.bottom: parent.bottom
index f49dab1..d204466 100644 (file)
@@ -43,6 +43,7 @@
 #include "sharehelper.h"
 #include "deletehelper.h"
 #include "galleryhelper.h"
+#include "postcapturemodel.h"
 
 static void initQuill() {
   // TODO: All these are hardcoded.
@@ -88,6 +89,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[]) {
   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);
diff --git a/src/postcapturemodel.cpp b/src/postcapturemodel.cpp
new file mode 100644 (file)
index 0000000..f84f827
--- /dev/null
@@ -0,0 +1,345 @@
+/*!
+ * 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;
+}
diff --git a/src/postcapturemodel.h b/src/postcapturemodel.h
new file mode 100644 (file)
index 0000000..be3eb8c
--- /dev/null
@@ -0,0 +1,143 @@
+// -*- 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 */
index e6808b9..1a69cdf 100644 (file)
@@ -17,8 +17,10 @@ LIBS +=  -L../imports/ -limports -L../lib/ -lqtcamera
 
 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