I have made the following changes intended for :
  CE:Apps / qmlmessages

Please review and accept or decline.
BOSS has already run some checks on this request.
See the "Messages from BOSS" section below.

https://build.pub.meego.com//request/show/6763

Thank You,
John Brooks

[This message was auto-generated]

---

Request # 6763:

Messages from BOSS:

State: review at 2012-09-21T13:28:39 by bossbot

Reviews:
       accepted by bossbot : Prechecks succeeded.
       new for CE-maintainers : Please replace this text with a review and 
approve/reject the review (not the SR). BOSS will take care of the rest

Changes:
  submit: home:special:branches:CE:Apps / qmlmessages -> CE:Apps / qmlmessages
  
changes files:
--------------
--- qmlmessages.changes
+++ qmlmessages.changes
@@ -0,0 +1,7 @@
+* Fri Sep 21 2012 John Brooks <[email protected]> - 0.0.4
+- Open the conversation window after tapping on a commhistory-daemon 
notification
+- Improve handling of incoming telepathy channels
+- Use libcommhistory-declarative for QML bindings
+- Fixes NEMO#378: Use a background thread for CommHistory models
+- Use streamed queries for ConversationModel to improve load time
+

old:
----
  0001-Use-pkgconfig-to-find-TelepathyQt4.patch
  qmlmessages-0.0.3.tar.bz2

new:
----
  qmlmessages-0.0.4.tar.bz2

spec files:
-----------
--- qmlmessages.spec
+++ qmlmessages.spec
@@ -9,15 +9,15 @@
 # << macros
 
 Summary:    Messaging application for nemo
-Version:    0.0.3
+Version:    0.0.4
 Release:    1
 Group:      Applications/System
 License:    BSD
 URL:        https://github.com/nemomobile/qmlmessages
 Source0:    %{name}-%{version}.tar.bz2
 Source100:  qmlmessages.yaml
-Patch0:     0001-Use-pkgconfig-to-find-TelepathyQt4.patch
 Requires:   qt-components
+Requires:   libcommhistory-declarative
 BuildRequires:  pkgconfig(QtCore) >= 4.7.0
 BuildRequires:  pkgconfig(QtDeclarative)
 BuildRequires:  pkgconfig(QtContacts)
@@ -47,8 +47,6 @@
 %prep
 %setup -q -n %{name}
 
-# 0001-Use-pkgconfig-to-find-TelepathyQt4.patch
-%patch0 -p1
 # >> setup
 # << setup
 

other changes:
--------------

++++++ qmlmessages-0.0.3.tar.bz2 -> qmlmessages-0.0.4.tar.bz2
--- qml/qmlmessages/ConversationListDelegate.qml
+++ qml/qmlmessages/ConversationListDelegate.qml
@@ -66,7 +66,7 @@
 
             Label {
                 id: nameFirst
-                text: model.displayName
+                text: model.remoteUids[0]
 
                 platformStyle: LabelStyle {
                     fontFamily: "Droid Sans"
@@ -77,7 +77,7 @@
             Label {
                 id: messageDate
                 // XXX This should be something more natural/useful
-                text: Qt.formatDateTime(model.messageDate, "M/d")
+                text: Qt.formatDateTime(model.lastModified, "M/d")
                 anchors.right: parent.right
                 anchors.verticalCenter: parent.verticalCenter
 
@@ -91,7 +91,7 @@
 
         Label {
             id: messagePreview
-            text: model.messagePreview
+            text: model.lastMessageText
             elide: Text.ElideRight
             width: parent.width
 
--- qml/qmlmessages/ConversationListWidget.qml
+++ qml/qmlmessages/ConversationListWidget.qml
@@ -54,7 +54,8 @@
 
         delegate: ConversationListDelegate {
             onClicked: {
-                pageStack.push(Qt.resolvedUrl("ConversationPage.qml"), { 
group: model.groupId })
+                var group = groupManager.getConversationById(model.groupId)
+                pageStack.push(Qt.resolvedUrl("ConversationPage.qml"), { 
channel: group })
             }
         }
     }
--- qml/qmlmessages/ConversationPage.qml
+++ qml/qmlmessages/ConversationPage.qml
@@ -40,12 +40,8 @@
 Page {
     id: conversationPage
 
-    property int group: -1
-    onGroupChanged: if (group >= 0) channel.setGroup(group)
-
-    ConversationChannel {
-        id: channel
-    }
+    property QtObject channel: null
+    tools: null
 
     PageHeader {
         id: header
@@ -73,7 +69,7 @@
             elide: Text.ElideRight
             smooth: true
             color: "#111111"
-            text: channel.contactId
+            text: channel == null ? "" : channel.contactId
             style: Text.Raised
             styleColor: "white"
 
@@ -114,7 +110,7 @@
                 || accountSelector.selectedIndex < 0)
                 return
             console.log("startConversation", accountSelector.selectedUid, 
targetEditor.text);
-            channel.setGroup(accountSelector.selectedUid, targetEditor.text)
+            channel = 
groupManager.getConversation(accountSelector.selectedUid, targetEditor.text)
         }
     }
 
@@ -183,7 +179,7 @@
     states: [
         State {
             name: "active"
-            when: channel.state != ConversationChannel.Null
+            when: channel !== null
 
             PropertyChanges {
                 target: messagesView
@@ -192,7 +188,7 @@
         },
         State {
             name: "new"
-            when: channel.state == ConversationChannel.Null
+            when: channel == null
 
             PropertyChanges {
                 target: targetEditor
@@ -221,5 +217,10 @@
             }
         }
     ]
+
+    onChannelChanged: {
+        if (channel != null)
+            channel.ensureChannel()
+    }
 }
 
--- qml/qmlmessages/MessagesView.qml
+++ qml/qmlmessages/MessagesView.qml
@@ -31,6 +31,7 @@
 import QtQuick 1.1
 import com.nokia.meego 1.0
 import org.nemomobile.qmlmessages 1.0
+import org.nemomobile.commhistory 1.0
 
 Item {
     property alias model: view.model
@@ -45,10 +46,18 @@
         anchors.fill: parent
         cacheBuffer: parent.height
 
-        onCountChanged: view.positionViewAtBeginning()
         // Necessary when opening VKB, for example
         onHeightChanged: view.positionViewAtBeginning()
 
+        Connections {
+            target: model
+            onRowsInserted: {
+                if (first == 0)
+                    view.positionViewAtBeginning()
+            }
+            onModelReset: view.positionViewAtBeginning()
+        }
+
         delegate: BorderImage {
             id: messageBox
             height: childrenRect.height + 20
@@ -68,7 +77,7 @@
 
                 Text {
                     id: messageText
-                    text: model.text
+                    text: model.freeText
                     width: messageBox.parent.width * 0.7
                     wrapMode: Text.Wrap
                     style: Text.Raised
@@ -99,7 +108,7 @@
             states: [
                 State {
                     name: "incoming"
-                    when: model.direction == ChatModel.Incoming
+                    when: model.direction == CommHistory.Inbound
 
                     PropertyChanges {
                         target: messageBox
@@ -112,7 +121,7 @@
                 },
                 State {
                     name: "outgoing"
-                    when: model.direction == ChatModel.Outgoing
+                    when: model.direction == CommHistory.Outbound
 
                     PropertyChanges {
                         target: messageBox
--- qml/qmlmessages/main.qml
+++ qml/qmlmessages/main.qml
@@ -36,11 +36,46 @@
 PageStackWindow {
     id: window 
 
-    initialPage: ConversationListPage {}
-
     // Shared AccountsModel
     AccountsModel {
         id: accountsModel
     }
+
+    Connections {
+        target: pageStack
+        onCurrentPageChanged: {
+            var group
+            try {
+                group = pageStack.currentPage.channel
+            } catch (e) {
+            }
+            if (group == undefined)
+                group = null
+            windowManager.currentGroup = group
+        }
+    }
+
+    function showConversation(group) {
+        if (group == windowManager.currentGroup)
+            return
+
+        var pages = [ ]
+        if (!pageStack.currentPage) {
+            pages.push(Qt.resolvedUrl("ConversationListPage.qml"))
+        }
+        pages.push({ page: Qt.resolvedUrl("ConversationPage.qml"), properties: 
{ channel: group } })
+
+        if (pageStack.depth > 1)
+            pageStack.replace(pages)
+        else
+            pageStack.push(pages)
+    }
+
+    function showGroupsList() {
+        if (!pageStack.currentPage)
+            pageStack.push(Qt.resolvedUrl("ConversationListPage.qml"))
+        else if (pageStack.depth > 1)
+            pageStack.pop(null, true)
+    }
 }
 
--- qmlmessages.pro
+++ qmlmessages.pro
@@ -1,12 +1,8 @@
 PROJECT_NAME = qmlmessages
 QT += dbus declarative svg
 
-INCLUDEPATH += /usr/include/telepathy-1.0/
-LIBS += -ltelepathy-qt4
-CXXFLAGS += -fPIC
-
 CONFIG += link_pkgconfig
-PKGCONFIG += commhistory
+PKGCONFIG += commhistory TelepathyQt4
 
 target.path = $$INSTALL_ROOT/usr/bin
 INSTALLS += target
@@ -17,18 +13,16 @@
     src/accountsmodel.cpp \
     src/clienthandler.cpp \
     src/conversationchannel.cpp \
-    src/qmlgroupmodel.cpp \
-    src/qmlchatmodel.cpp \
     src/windowmanager.cpp \
-    src/dbusadaptor.cpp
+    src/dbusadaptor.cpp \
+    src/groupmanager.cpp
 
 HEADERS += src/accountsmodel.h \
     src/clienthandler.h \
     src/conversationchannel.h \
-    src/qmlgroupmodel.h \
-    src/qmlchatmodel.h \
     src/windowmanager.h \
-    src/dbusadaptor.h
+    src/dbusadaptor.h \
+    src/groupmanager.h
 
 RESOURCES += res/res.qrc qml/qml.qrc
 
--- src/clienthandler.cpp
+++ src/clienthandler.cpp
@@ -30,11 +30,13 @@
 
 #include "clienthandler.h"
 #include "conversationchannel.h"
+#include "groupmanager.h"
 
 #include <TelepathyQt4/ChannelClassSpec>
 #include <TelepathyQt4/ReceivedMessage>
 #include <TelepathyQt4/TextChannel>
 #include <TelepathyQt4/ChannelRequest>
+#include <TelepathyQt4/Account>
 
 using namespace Tp;
 
@@ -67,7 +69,24 @@
                                    const QList<ChannelRequestPtr> 
&requestsSatisfied, const QDateTime &userActionTime,
                                    const HandlerInfo &handlerInfo)
 {
-    // XXX what do we need to take care of here?
+    foreach (const ChannelPtr &channel, channels) {
+        QVariantMap properties = channel->immutableProperties();
+        QString targetId = properties.value(TP_QT_IFACE_CHANNEL + 
QLatin1String(".TargetID")).toString();
+
+        if (targetId.isEmpty()) {
+            qWarning() << "handleChannels cannot get TargetID for channel";
+            continue;
+        }
+
+        ConversationChannel *g = 
GroupManager::instance()->getConversation(account->objectPath(), targetId);
+        if (!g) {
+            qWarning() << "handleChannels cannot create ConversationChannel";
+            continue;
+        }
+
+        g->setChannel(channel);
+    }
+
     context->setFinished();
 }
 
--- src/conversationchannel.cpp
+++ src/conversationchannel.cpp
@@ -29,9 +29,8 @@
  */
 
 #include "conversationchannel.h"
+#include "groupmanager.h"
 #include "clienthandler.h"
-#include "qmlgroupmodel.h"
-#include "qmlchatmodel.h"
 
 #include <TelepathyQt4/ChannelRequest>
 #include <TelepathyQt4/TextChannel>
@@ -41,12 +40,14 @@
 #include <TelepathyQt4/Account>
 #include <TelepathyQt4/AccountManager>
 
+#include <CommHistory/ConversationModel>
+#include <CommHistory/GroupModel>
+
 // XXX
 extern Tp::AccountManagerPtr accountManager;
-extern QmlGroupModel *groupModel;
 
 ConversationChannel::ConversationChannel(QObject *parent)
-    : QObject(parent), mPendingRequest(0), mState(Null), mModel(0)
+    : QObject(parent), mPendingRequest(0), mState(Null), mModel(0), 
mGroupId(-1)
 {
 }
 
@@ -56,16 +57,7 @@
 
 void ConversationChannel::setGroup(int groupid)
 {
-    CommHistory::Group group;
-
-    for (int i = 0, c = groupModel->rowCount(); i < c; i++) {
-        int id = groupModel->index(i, 
0).data(QmlGroupModel::GroupIdRole).toInt();
-        if (id == groupid) {
-            group = groupModel->group(groupModel->index(i, 0));
-            break;
-        }
-    }
-
+    CommHistory::Group group = GroupManager::instance()->groupFromId(groupid);
     if (!group.isValid()) {
         qWarning() << Q_FUNC_INFO << "Cannot find group id" << groupid;
         return;
@@ -74,51 +66,36 @@
     setupGroup(group);
 }
 
-void ConversationChannel::setGroup(const QString &localUid, const QString 
&remoteUid)
-{
-    CommHistory::Group group;
-
-    for (int i = 0, c = groupModel->rowCount(); i < c; i++) {
-        const CommHistory::Group &g = groupModel->group(groupModel->index(i, 
0));
-        if (g.localUid() == localUid && g.remoteUids().size() == 1
-                && g.remoteUids().first() == remoteUid) {
-            group = g;
-            break;
-        }
-    }
-
-    if (!group.isValid()) {
-        qDebug() << "ConversationChannel::setGroup creating group for" << 
localUid << remoteUid;
-        group.setLocalUid(localUid);
-        group.setRemoteUids(QStringList() << remoteUid);
-        group.setChatType(CommHistory::Group::ChatTypeP2P);
-        if (!groupModel->addGroup(group)) {
-            qWarning() << "ConversationChannel::setGroup failed creating 
group" << localUid << remoteUid;
-            return;
-        }
-        Q_ASSERT(group.isValid());
-    }
-
-    setupGroup(group);
-}
-
 void ConversationChannel::setupGroup(const CommHistory::Group &group)
 {
+    mGroupId = group.id();
+
     mContactId = group.remoteUids().value(0);
+    mLocalUid = group.localUid();
     emit contactIdChanged();
 
     Q_ASSERT(!mModel);
-    mModel = new QmlChatModel(group.id(), this);
+    mModel = new CommHistory::ConversationModel(this);
+    
mModel->setBackgroundThread(GroupManager::instance()->groupModel()->backgroundThread());
+    mModel->setQueryMode(CommHistory::EventModel::StreamedAsyncQuery);
+    mModel->setFirstChunkSize(25);
+    mModel->setChunkSize(50);
+    mModel->setTreeMode(false);
+    mModel->getEvents(mGroupId);
     emit chatModelReady(mModel);
+}
 
-    qDebug() << Q_FUNC_INFO << group.localUid() << group.remoteUids().value(0);
+void ConversationChannel::ensureChannel()
+{
+    if (!mChannel.isNull() || mPendingRequest || !mRequest.isNull())
+        return;
 
     // XXX wait for account manager if necessary?
-    Tp::AccountPtr account = accountManager->accountForPath(group.localUid());
+    Tp::AccountPtr account = accountManager->accountForPath(mLocalUid);
     // XXX error check
     Q_ASSERT(account);
     Q_ASSERT(account->isReady());
-    Tp::PendingChannelRequest *req = 
account->ensureTextChat(group.remoteUids().value(0),
+    Tp::PendingChannelRequest *req = account->ensureTextChat(mContactId,
             QDateTime::currentDateTime(),
             QLatin1String("org.freedesktop.Telepathy.Client.qmlmessages"));
     start(req);
@@ -140,10 +117,12 @@
 
 void ConversationChannel::setChannel(const Tp::ChannelPtr &c)
 {
-    Q_ASSERT(mChannel.isNull());
-    Q_ASSERT(!c.isNull());
-    if (!mChannel.isNull() || c.isNull())
+    if (mChannel && c && mChannel->objectPath() == c->objectPath())
         return;
+    if (!mChannel.isNull() || c.isNull()) {
+        qWarning() << Q_FUNC_INFO << "called with existing channel set";
+        return;
+    }
 
     mChannel = c;
     connect(mChannel->becomeReady(Tp::TextChannel::FeatureMessageQueue),
@@ -151,6 +130,8 @@
             SLOT(channelReady()));
     connect(mChannel.data(), SIGNAL(messageReceived(Tp::ReceivedMessage)),
             SLOT(messageReceived(Tp::ReceivedMessage)));
+    connect(mChannel.data(), 
SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
+            SLOT(channelInvalidated(Tp::DBusProxy*,QString,QString)));
 
     setState(PendingReady);
 }
@@ -251,6 +232,7 @@
 {
     if (mChannel.isNull() || !mChannel->isReady()) {
         Q_ASSERT(state() != Ready);
+        ensureChannel();
         qDebug() << Q_FUNC_INFO << "Buffering:" << text;
         mPendingMessages.append(text);
         return;
@@ -268,3 +250,13 @@
     Tp::PendingSendMessage *msg = textChannel->send(text);
 }
 
+void ConversationChannel::channelInvalidated(Tp::DBusProxy *proxy,
+        const QString &errorName, const QString &errorMessage)
+{
+    Q_UNUSED(proxy);
+    qDebug() << "Channel invalidated:" << errorName << errorMessage;
+
+    mChannel.reset();
+    setState(Null);
+}
+
--- src/conversationchannel.h
+++ src/conversationchannel.h
@@ -38,8 +38,7 @@
 #include <TelepathyQt4/PendingSendMessage>
 #include <TelepathyQt4/ReceivedMessage>
 #include <CommHistory/Group>
-
-class QmlChatModel;
+#include <CommHistory/ConversationModel>
 
 /* ConversationChannel handles the relationship between a commhistory
  * group and the associated Telepathy channel.
@@ -57,8 +56,11 @@
    
     Q_PROPERTY(State state READ state NOTIFY stateChanged)
     Q_PROPERTY(QString contactId READ contactId NOTIFY contactIdChanged)
+    Q_PROPERTY(QString localUid READ localUid CONSTANT)
+
+    Q_PROPERTY(QObject* model READ model NOTIFY chatModelReady)
 
-    Q_PROPERTY(QmlChatModel* model READ model NOTIFY chatModelReady)
+    friend class GroupManager;
 
 public:
     enum State {
@@ -70,27 +72,22 @@
         Error
     };
 
-    ConversationChannel(QObject *parent = 0);
     virtual ~ConversationChannel();
 
-    /* Set commhistory group by ID. Group must exist in the GroupModel. */
-    Q_INVOKABLE void setGroup(int groupid);
-    /* Find commhistory group for the combination of local and remote UIDs.
-     * Does not support multi-target groups currently.
-     *
-     * If the group doesn't exist, it will be created locally. */
-    Q_INVOKABLE void setGroup(const QString &localUid, const QString 
&remoteUid);
-
     State state() const { return mState; }
     QString contactId() const { return mContactId; }
-    QmlChatModel *model() const { return mModel; }
+    QString localUid() const { return mLocalUid; }
+    QObject *model() const { return mModel; }
+
+    Q_INVOKABLE void ensureChannel();
+    void setChannel(const Tp::ChannelPtr &channel);
 
 public slots:
     void sendMessage(const QString &text);
 
 signals:
     void stateChanged(int newState);
-    void chatModelReady(QmlChatModel *model);
+    void chatModelReady(QObject *model);
     void contactIdChanged();
 
     void requestSucceeded();
@@ -104,21 +101,29 @@
 
     void messageReceived(const Tp::ReceivedMessage &message);
 
+    void channelInvalidated(Tp::DBusProxy *proxy, const QString &errorName, 
const QString &errorMessage);
+
 private:
     Tp::PendingChannelRequest *mPendingRequest;
     Tp::ChannelRequestPtr mRequest;
     Tp::ChannelPtr mChannel;
     State mState;
-    QmlChatModel *mModel;
+    CommHistory::ConversationModel *mModel;
 
+    int mGroupId;
     QString mContactId;
+    QString mLocalUid;
 
     QList<QString> mPendingMessages;
 
+    ConversationChannel(QObject *parent = 0);
+
     void setState(State newState);
-    void setupGroup(const CommHistory::Group &group);
     void start(Tp::PendingChannelRequest *request);
-    void setChannel(const Tp::ChannelPtr &channel);
+
+    /* Set commhistory group by ID. Group must exist in the GroupModel. */
+    void setGroup(int groupid);
+    void setupGroup(const CommHistory::Group &group);
 };
 
 #endif
--- src/dbusadaptor.cpp
+++ src/dbusadaptor.cpp
@@ -49,3 +49,8 @@
     wm->showGroupsWindow();
 }
 
+void DBusAdaptor::startConversation(const QString &localUid, const QString 
&remoteUid, unsigned type)
+{
+    wm->showConversation(localUid, remoteUid, type);
+}
+
--- src/dbusadaptor.h
+++ src/dbusadaptor.h
@@ -46,6 +46,8 @@
     void showGroupsWindow();
     void showGroupsWindow(const QStringList &a);
 
+    void startConversation(const QString &localUid, const QString &remoteUid, 
unsigned type);
+
 private:
     WindowManager * const wm;
 };
--- src/groupmanager.cpp
+++ src/groupmanager.cpp
@@ -0,0 +1,128 @@
+/* Copyright (C) 2012 John Brooks <[email protected]>
+ *
+ * You may use this file under the terms of the BSD license as follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Nemo Mobile nor the names of its contributors
+ *     may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "groupmanager.h"
+#include "conversationchannel.h"
+#include <QThread>
+#include <CommHistory/GroupModel>
+
+Q_GLOBAL_STATIC(GroupManager, gmInstance)
+
+GroupManager *GroupManager::instance()
+{
+    return gmInstance();
+}
+
+GroupManager::GroupManager(QObject *parent)
+    : QObject(parent)
+{
+    QThread *modelThread = new QThread(this);
+    modelThread->start();
+
+    mGroupModel = new CommHistory::GroupModel(this);
+    mGroupModel->setBackgroundThread(modelThread);
+    mGroupModel->getGroups();
+}
+
+ConversationChannel *GroupManager::getConversationById(int groupid)
+{
+    QHash<int,ConversationChannel*>::iterator it = groups.find(groupid);
+    if (it == groups.end()) {
+        ConversationChannel *c = new ConversationChannel(this);
+        c->setGroup(groupid);
+        addGroup(groupid, c);
+        return c;
+    }
+    return *it;
+}
+
+CommHistory::Group GroupManager::groupFromUid(const QString &localUid, const 
QString &remoteUid)
+{
+    for (int i = 0, c = mGroupModel->rowCount(); i < c; i++) {
+        const CommHistory::Group &g = mGroupModel->group(mGroupModel->index(i, 
0));
+        if (g.localUid() == localUid && g.remoteUids().size() == 1
+                && g.remoteUids().first() == remoteUid) {
+            return g;
+        }
+    }
+    return CommHistory::Group();
+}
+
+CommHistory::Group GroupManager::groupFromId(int groupid)
+{
+    for (int i = 0, c = mGroupModel->rowCount(); i < c; i++) {
+        const CommHistory::Group &g = mGroupModel->group(mGroupModel->index(i, 
0));
+        if (g.id() == groupid)
+            return g;
+    }
+
+    return CommHistory::Group();
+}
+
+ConversationChannel *GroupManager::getConversation(const QString &localUid, 
const QString &remoteUid, bool create)
+{
+    CommHistory::Group group = groupFromUid(localUid, remoteUid);
+    if (group.isValid())
+        return getConversationById(group.id());
+    if (!create)
+        return 0;
+
+    qDebug() << "ConversationChannel creating group for" << localUid << 
remoteUid;
+    group.setLocalUid(localUid);
+    group.setRemoteUids(QStringList() << remoteUid);
+    group.setChatType(CommHistory::Group::ChatTypeP2P);
+    if (!mGroupModel->addGroup(group)) {
+        qWarning() << "ConversationChannel failed creating group" << localUid 
<< remoteUid;
+        return 0;
+    }
+    Q_ASSERT(group.isValid());
+
+    ConversationChannel *c = new ConversationChannel(this);
+    c->setupGroup(group);
+    addGroup(group.id(), c);
+    return c;
+}
+
+void GroupManager::addGroup(int id, ConversationChannel *c)
+{
+    Q_ASSERT(!groups.contains(id));
+    groups.insert(id, c);
+    connect(c, SIGNAL(destroyed(QObject*)), SLOT(groupDestroyed(QObject*)));
+}
+
+void GroupManager::groupDestroyed(QObject *obj)
+{
+    for (QHash<int,ConversationChannel*>::iterator it = groups.begin(); it != 
groups.end(); it++) {
+        if (*it == obj) {
+            groups.erase(it);
+            break;
+        }
+    }
+}
--- src/groupmanager.h
+++ src/groupmanager.h
@@ -0,0 +1,79 @@
+/* Copyright (C) 2012 John Brooks <[email protected]>
+ *
+ * You may use this file under the terms of the BSD license as follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Nemo Mobile nor the names of its contributors
+ *     may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GROUPMANAGER_H
+#define GROUPMANAGER_H
+
+#include <QObject>
+#include <QHash>
+
+#include <TelepathyQt/Account>
+#include <TelepathyQt/Channel>
+
+class ConversationChannel;
+
+namespace CommHistory {
+    class Group;
+    class GroupModel;
+}
+
+class GroupManager : public QObject
+{
+    Q_OBJECT
+
+public:
+    static GroupManager *instance();
+
+    explicit GroupManager(QObject *parent = 0);
+
+    CommHistory::GroupModel *groupModel() const { return mGroupModel; }
+
+    /* Get a conversation by commhistory group ID, creating it if necessary.
+     * A telepathy channel will be established if none exists already. */
+    Q_INVOKABLE ConversationChannel *getConversationById(int groupid);
+    /* Find commhistory group for the combination of local and remote UIDs,
+     * and return the associated conversation. Does not support multi-target
+     * groups currently. If the group doesn't exist, it will be created 
immediately. */
+    Q_INVOKABLE ConversationChannel *getConversation(const QString &localUid, 
const QString &remoteUid, bool create = true);
+
+    CommHistory::Group groupFromUid(const QString &localUid, const QString 
&remoteUid);
+    CommHistory::Group groupFromId(int groupid);
+
+private slots:
+    void groupDestroyed(QObject *obj);
+
+private:
+    CommHistory::GroupModel *mGroupModel;
+    QHash<int,ConversationChannel*> groups;
+
+    void addGroup(int id, ConversationChannel *c);
+};
+
+#endif
--- src/main.cpp
+++ src/main.cpp
@@ -43,19 +43,17 @@
 #include <TelepathyQt4/Types>
 #include <TelepathyQt4/ClientRegistrar>
 
+#include <CommHistory/GroupModel>
+
 #include "src/windowmanager.h"
 #include "src/accountsmodel.h"
 #include "src/clienthandler.h"
 #include "src/conversationchannel.h"
-#include "src/qmlgroupmodel.h"
-#include "src/qmlchatmodel.h"
 
 using namespace Tp;
 
 Tp::AccountManagerPtr accountManager;
 
-QmlGroupModel *groupModel = 0;
-
 #ifdef HAS_BOOSTER
 Q_DECL_EXPORT
 #endif
@@ -93,11 +91,7 @@
 
     // Set up QML
     qmlRegisterType<AccountsModel>("org.nemomobile.qmlmessages", 1, 0, 
"AccountsModel");
-    qmlRegisterUncreatableType<QmlChatModel>("org.nemomobile.qmlmessages", 1, 
0, "ChatModel", "Cannot be created");
-    qmlRegisterType<ConversationChannel>("org.nemomobile.qmlmessages", 1, 0, 
"ConversationChannel");
-
-    QmlGroupModel gm;
-    groupModel = &gm;
+    
qmlRegisterUncreatableType<ConversationChannel>("org.nemomobile.qmlmessages", 
1, 0, "ConversationChannel", "");
 
     WindowManager *wm = WindowManager::instance();
     if (showWindow)
--- src/qmlchatmodel.cpp
+++ src/qmlchatmodel.cpp
@@ -1,60 +0,0 @@
-/* Copyright (C) 2012 John Brooks <[email protected]>
- *
- * You may use this file under the terms of the BSD license as follows:
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *   * Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *   * Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in
- *     the documentation and/or other materials provided with the
- *     distribution.
- *   * Neither the name of Nemo Mobile nor the names of its contributors
- *     may be used to endorse or promote products derived from this
- *     software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "qmlchatmodel.h"
-#include <QDebug>
-
-QmlChatModel::QmlChatModel(int groupid, QObject *parent)
-    : CommHistory::ConversationModel(parent)
-{
-    QHash<int,QByteArray> roles;
-    roles[Qt::DisplayRole] = "text";
-    roles[DirectionRole] = "direction";
-    roles[StatusRole] = "status";
-    roles[StartTimeRole] = "date";
-    setRoleNames(roles);
-
-    setTreeMode(false);
-    getEvents(groupid);
-}
-
-QVariant QmlChatModel::data(const QModelIndex &index, int role) const
-{
-    int column = index.column();
-    switch (role) {
-        case Qt::DisplayRole: column = FreeText; break;
-        case DirectionRole: column = 
CommHistory::ConversationModel::Direction; break;
-        case StartTimeRole: column = StartTime; break;
-        case StatusRole: column = Status; break;
-    }
-
-    return CommHistory::ConversationModel::data(this->index(index.row(), 
column, index.parent()), Qt::DisplayRole);
-}
-
--- src/qmlchatmodel.h
+++ src/qmlchatmodel.h
@@ -1,62 +0,0 @@
-/* Copyright (C) 2012 John Brooks <[email protected]>
- *
- * You may use this file under the terms of the BSD license as follows:
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *   * Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *   * Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in
- *     the documentation and/or other materials provided with the
- *     distribution.
- *   * Neither the name of Nemo Mobile nor the names of its contributors
- *     may be used to endorse or promote products derived from this
- *     software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef QMLCHATMODEL_H
-#define QMLCHATMODEL_H
-
-#include <CommHistory/ConversationModel>
-
-class QmlChatModel : public CommHistory::ConversationModel
-{
-    Q_OBJECT
-    Q_ENUMS(Direction)
-
-public:
-    enum {
-        DirectionRole = Qt::UserRole,
-        StartTimeRole,
-        StatusRole
-    };
-
-    enum Direction {
-        UnknownDirection = 0,
-        Incoming,
-        Outgoing
-    };
-
-    QmlChatModel(int groupid, QObject *parent = 0);
-
-    virtual QVariant data(const QModelIndex &index, int role = 
Qt::DisplayRole) const;
-};
-
-Q_DECLARE_METATYPE(QmlChatModel*);
-
-#endif
-
--- src/qmlgroupmodel.cpp
+++ src/qmlgroupmodel.cpp
@@ -1,71 +0,0 @@
-/* Copyright (C) 2012 John Brooks <[email protected]>
- *
- * You may use this file under the terms of the BSD license as follows:
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *   * Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *   * Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in
- *     the documentation and/or other materials provided with the
- *     distribution.
- *   * Neither the name of Nemo Mobile nor the names of its contributors
- *     may be used to endorse or promote products derived from this
- *     software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "qmlgroupmodel.h"
-#include "qmlchatmodel.h"
-
-QmlGroupModel::QmlGroupModel(QObject *parent)
-    : CommHistory::GroupModel(parent)
-{
-    QHash<int,QByteArray> roles;
-    roles[Qt::DisplayRole] = "displayName";
-    roles[LastMessageTextRole] = "messagePreview";
-    roles[LastModifiedRole] = "messageDate";
-    roles[GroupIdRole] = "groupId";
-    setRoleNames(roles);
-
-    getGroups();
-}
-
-QVariant QmlGroupModel::data(const QModelIndex &index, int role) const
-{
-    int column = index.column();
-
-    switch (role) {
-        case Qt::DisplayRole: {
-            QList<CommHistory::Event::Contact> contacts = 
group(index).contacts();
-            if (!contacts.isEmpty())
-                return contacts[0].second;
-            column = RemoteUids;
-            break;
-        }
-        case LastMessageTextRole: column = LastMessageText; break;
-        case LastModifiedRole: column = LastModified; break;
-        case GroupIdRole: column = GroupId; break;
-    }
-
-    QVariant re = CommHistory::GroupModel::data(this->index(index.row(), 
column, index.parent()), Qt::DisplayRole);
-
-    if (column == RemoteUids) {
-        re = re.value<QVariantList>()[0];
-    }
-    return re;
-}
-
--- src/qmlgroupmodel.h
+++ src/qmlgroupmodel.h
@@ -1,51 +0,0 @@
-/* Copyright (C) 2012 John Brooks <[email protected]>
- *
- * You may use this file under the terms of the BSD license as follows:
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *   * Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *   * Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in
- *     the documentation and/or other materials provided with the
- *     distribution.
- *   * Neither the name of Nemo Mobile nor the names of its contributors
- *     may be used to endorse or promote products derived from this
- *     software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef QMLGROUPMODEL_H
-#define QMLGROUPMODEL_H
-
-#include <CommHistory/GroupModel>
-
-class QmlGroupModel : public CommHistory::GroupModel
-{
-public:
-    enum {
-        LastMessageTextRole = Qt::UserRole,
-        LastModifiedRole,
-        GroupIdRole
-    };
-
-    QmlGroupModel(QObject *parent = 0);
-
-    virtual QVariant data(const QModelIndex &index, int role = 
Qt::DisplayRole) const;
-};
-
-#endif
-
--- src/windowmanager.cpp
+++ src/windowmanager.cpp
@@ -30,18 +30,20 @@
 
 #include "windowmanager.h"
 #include "clienthandler.h"
-#include "qmlgroupmodel.h"
+#include "groupmanager.h"
 #include "dbusadaptor.h"
+#include "conversationchannel.h"
 #include <QDeclarativeView>
 #include <QDeclarativeContext>
+#include <QDeclarativeItem>
 #include <QDBusConnection>
 #ifdef HAS_BOOSTER
 #include <applauncherd/MDeclarativeCache>
 #endif
 
-Q_GLOBAL_STATIC(WindowManager, wmInstance)
+#include <CommHistory/GroupModel>
 
-extern QmlGroupModel *groupModel;
+Q_GLOBAL_STATIC(WindowManager, wmInstance)
 
 WindowManager *WindowManager::instance()
 {
@@ -49,7 +51,7 @@
 }
 
 WindowManager::WindowManager(QObject *parent)
-    : QObject(parent)
+    : QObject(parent), mCurrentGroup(0)
 {
     new DBusAdaptor(this);
     
QDBusConnection::sessionBus().registerService("org.nemomobile.qmlmessages");
@@ -65,7 +67,7 @@
 #endif
 }
 
-void WindowManager::showGroupsWindow()
+void WindowManager::ensureWindow()
 {
     if (!mWindow) {
 #ifdef HAS_BOOSTER
@@ -75,22 +77,71 @@
 #endif
 
         QDeclarativeView *w = mWindow.data();
+        mCurrentGroup = 0;
 
         // mWindow is a QWeakPointer, so it'll be cleared on delete
         w->setAttribute(Qt::WA_DeleteOnClose);
         w->setWindowTitle(tr("Messages"));
-        w->rootContext()->setContextProperty("clientHandler", 
QVariant::fromValue<QObject*>(ClientHandler::instance()));
-        w->rootContext()->setContextProperty("groupModel", 
QVariant::fromValue<QObject*>(groupModel));
+        w->rootContext()->setContextProperty("windowManager",
+                QVariant::fromValue<QObject*>(this));
+        w->rootContext()->setContextProperty("groupManager",
+                QVariant::fromValue<QObject*>(GroupManager::instance()));
+        w->rootContext()->setContextProperty("groupModel",
+                
QVariant::fromValue<QObject*>(GroupManager::instance()->groupModel()));
         w->setSource(QUrl("qrc:qml/qmlmessages/main.qml"));
         w->setAttribute(Qt::WA_OpaquePaintEvent);
         w->setAttribute(Qt::WA_NoSystemBackground);
         w->viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
         w->viewport()->setAttribute(Qt::WA_NoSystemBackground);
     }
+}
+
+void WindowManager::showGroupsWindow()
+{
+    ensureWindow();
+
+    QGraphicsObject *root = mWindow.data()->rootObject();
+    if (!root)
+        qFatal("No root object in window");
+    bool ok = root->metaObject()->invokeMethod(root, "showGroupsList");
+    if (!ok)
+        qWarning() << Q_FUNC_INFO << "showGroupsList call failed";
+
+    mWindow.data()->showFullScreen();
+    mWindow.data()->activateWindow();
+    mWindow.data()->raise();
+}
+
+void WindowManager::showConversation(const QString &localUid, const QString 
&remoteUid, unsigned type)
+{
+    Q_UNUSED(type);
+    ensureWindow();
+
+    qDebug() << Q_FUNC_INFO << localUid << remoteUid << type;
+    ConversationChannel *group = 
GroupManager::instance()->getConversation(localUid, remoteUid);
+    if (!group) {
+        qWarning() << Q_FUNC_INFO << "could not create group";
+        return;
+    }
 
-    // XXX set page for existing windows?
+    QGraphicsObject *root = mWindow.data()->rootObject();
+    if (!root)
+        qFatal("No root object in window");
+    bool ok = root->metaObject()->invokeMethod(root, "showConversation", 
Q_ARG(QVariant, QVariant::fromValue<QObject*>(group)));
+    if (!ok)
+        qWarning() << Q_FUNC_INFO << "showConversation call failed";
 
     mWindow.data()->showFullScreen();
     mWindow.data()->activateWindow();
+    mWindow.data()->raise();
+}
+
+void WindowManager::updateCurrentGroup(ConversationChannel *g)
+{
+    if (g == mCurrentGroup)
+        return;
+
+    mCurrentGroup = g;
+    emit currentGroupChanged(g);
 }
 
--- src/windowmanager.h
+++ src/windowmanager.h
@@ -35,6 +35,7 @@
 #include <QWeakPointer>
 
 class QDeclarativeView;
+class ConversationChannel;
 
 /* Right now, WindowManager is just responsible for creating/showing the
  * single window we manage when requested via DBus or application launch.
@@ -51,11 +52,24 @@
     explicit WindowManager(QObject *parent = 0);
     virtual ~WindowManager();
 
+    Q_PROPERTY(ConversationChannel* currentGroup READ currentGroup WRITE 
updateCurrentGroup NOTIFY currentGroupChanged)
+    ConversationChannel *currentGroup() const { return mCurrentGroup; }
+
 public slots:
     void showGroupsWindow();
+    void showConversation(const QString &localUid, const QString &remoteUid, 
unsigned type);
+
+private slots:
+    void updateCurrentGroup(ConversationChannel *group);
+
+signals:
+    void currentGroupChanged(ConversationChannel *currentGroup);
 
 private:
     QWeakPointer<QDeclarativeView> mWindow;
+    ConversationChannel *mCurrentGroup;
+
+    void ensureWindow();
 };
 
 #endif

++++++ qmlmessages.yaml
--- qmlmessages.yaml
+++ qmlmessages.yaml
@@ -1,14 +1,12 @@
 Name:  qmlmessages
 Summary: Messaging application for nemo
-Version: 0.0.3
+Version: 0.0.4
 Release: 1
 Group: Applications/System
 License: BSD
 URL: https://github.com/nemomobile/qmlmessages
 Sources:
     - "%{name}-%{version}.tar.bz2"
-Patches:
-    - 0001-Use-pkgconfig-to-find-TelepathyQt4.patch
 
 Description: Messaging application using Qt Quick for Nemo Mobile.
 Configure: none
@@ -45,6 +43,7 @@
 
 Requires:
     - qt-components
+    - libcommhistory-declarative
  
 Files:
     - "%{_bindir}/qmlmessages"

++++++ deleted files:
--- 0001-Use-pkgconfig-to-find-TelepathyQt4.patch



Reply via email to