[SCM] ktp-contact-list packaging branch, master, updated. debian/15.12.1-2-1070-g6c56f91

Maximiliano Curia maxy at moszumanska.debian.org
Sat May 28 00:06:28 UTC 2016


Gitweb-URL: http://git.debian.org/?p=pkg-kde/applications/ktp-contact-list.git;a=commitdiff;h=8f0624f

The following commit has been merged in the master branch:
commit 8f0624fa92f34fde82a6ea89c80e195edf50cc8b
Author: Martin Klapetek <martin.klapetek at gmail.com>
Date:   Sat May 21 09:58:05 2011 +0200

    Add groups support
    Reviewed-by: David Edmundson
    REVIEW: 101380
---
 CMakeLists.txt                              |   4 +
 account-filter-model.cpp                    |  92 +++++---
 account-filter-model.h                      |   6 +
 contact-delegate.cpp                        |  48 ++--
 groups-model-item.cpp                       | 116 ++++++++++
 contact-model-item.h => groups-model-item.h |  41 ++--
 groups-model.cpp                            | 345 ++++++++++++++++++++++++++++
 groups-model.h                              |  94 ++++++++
 main-widget.cpp                             |  63 ++++-
 main-widget.h                               |   3 +
 proxy-tree-node.cpp                         |  84 +++++++
 tree-node.h => proxy-tree-node.h            |  41 ++--
 tree-node.h                                 |   2 +
 13 files changed, 838 insertions(+), 101 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 919df77..f2247eb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,6 +36,10 @@ set (contactlist_SRCS
      add-contact-dialog.cpp
      remove-contact-dialog.cpp
      fetch-avatar-job.cpp
+
+     groups-model-item.cpp
+     groups-model.cpp
+     proxy-tree-node.cpp
 )
 
 
diff --git a/account-filter-model.cpp b/account-filter-model.cpp
index 75f2126..4a8df8c 100644
--- a/account-filter-model.cpp
+++ b/account-filter-model.cpp
@@ -21,6 +21,7 @@
 
 #include "account-filter-model.h"
 #include "accounts-model.h"
+#include "groups-model.h"
 
 #include <KDebug>
 
@@ -29,7 +30,9 @@ AccountFilterModel::AccountFilterModel(QObject *parent)
       m_filterOfflineUsers(false),
       m_filterByName(false)
 {
-
+    //FIXME FIXME FIXME -- this is an ugly workaround for some filter-misbehaving issues, need to investigate
+    connect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+            this, SLOT(invalidate()));
 }
 
 void AccountFilterModel::filterOfflineUsers(bool filterOfflineUsers)
@@ -59,20 +62,27 @@ bool AccountFilterModel::filterAcceptsRow(int source_row, const QModelIndex &sou
         //filter offline users out
         if (m_filterOfflineUsers &&
                 ((source_parent.child(source_row, 0).data(AccountsModel::PresenceTypeRole).toUInt()
-                 == Tp::ConnectionPresenceTypeOffline) ||
+                == Tp::ConnectionPresenceTypeOffline) ||
                 (source_parent.child(source_row, 0).data(AccountsModel::PresenceTypeRole).toUInt()
-                 == Tp::ConnectionPresenceTypeUnknown))) {
+                == Tp::ConnectionPresenceTypeUnknown))) {
 
             rowAccepted = false;
         }
     } else {
-        if (!sourceModel()->index(source_row, 0).data(AccountsModel::EnabledRole).toBool()) {
-            rowAccepted = false;
-        }
-        if (sourceModel()->index(source_row, 0).data(AccountsModel::ConnectionStatusRole).toUInt()
-            != Tp::ConnectionStatusConnected) {
-
-            rowAccepted = false;
+        QModelIndex index = sourceModel()->index(source_row, 0);
+        if (index.isValid()) {
+            if (m_groupsActive) {
+                rowAccepted = true;
+            } else {
+                if (!index.data(AccountsModel::EnabledRole).toBool()) {
+                    rowAccepted = false;
+                }
+                if (index.data(AccountsModel::ConnectionStatusRole).toUInt()
+                    != Tp::ConnectionStatusConnected) {
+
+                    rowAccepted = false;
+                }
+            }
         }
     }
 
@@ -95,34 +105,34 @@ void AccountFilterModel::clearFilterString()
 
 bool AccountFilterModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
 {
-    uint leftPresence;
-    uint rightPresence;
-
-    QString leftDisplayedName = sourceModel()->data(left).toString();
-    QString rightDisplayedName = sourceModel()->data(right).toString();
-
-    if (sortRole() == AccountsModel::PresenceTypeRole) {
-        leftPresence = sourceModel()->data(left, AccountsModel::PresenceTypeRole).toUInt();
-        rightPresence = sourceModel()->data(right, AccountsModel::PresenceTypeRole).toUInt();
-
-        if (leftPresence == rightPresence) {
-            return QString::localeAwareCompare(leftDisplayedName, rightDisplayedName) < 0;
-        } else {
-            if (leftPresence == Tp::ConnectionPresenceTypeAvailable) {
-                return true;
-            }
-            if (leftPresence == Tp::ConnectionPresenceTypeUnset ||
-                    leftPresence == Tp::ConnectionPresenceTypeOffline ||
-                    leftPresence == Tp::ConnectionPresenceTypeUnknown ||
-                    leftPresence == Tp::ConnectionPresenceTypeError) {
-                return false;
+        uint leftPresence;
+        uint rightPresence;
+
+        QString leftDisplayedName = sourceModel()->data(left).toString();
+        QString rightDisplayedName = sourceModel()->data(right).toString();
+
+        if (sortRole() == AccountsModel::PresenceTypeRole) {
+            leftPresence = sourceModel()->data(left, AccountsModel::PresenceTypeRole).toUInt();
+            rightPresence = sourceModel()->data(right, AccountsModel::PresenceTypeRole).toUInt();
+
+            if (leftPresence == rightPresence) {
+                return QString::localeAwareCompare(leftDisplayedName, rightDisplayedName) < 0;
+            } else {
+                if (leftPresence == Tp::ConnectionPresenceTypeAvailable) {
+                    return true;
+                }
+                if (leftPresence == Tp::ConnectionPresenceTypeUnset ||
+                        leftPresence == Tp::ConnectionPresenceTypeOffline ||
+                        leftPresence == Tp::ConnectionPresenceTypeUnknown ||
+                        leftPresence == Tp::ConnectionPresenceTypeError) {
+                    return false;
+                }
+
+                return leftPresence < rightPresence;
             }
-
-            return leftPresence < rightPresence;
+        } else {
+            return QString::localeAwareCompare(leftDisplayedName, rightDisplayedName) < 0;
         }
-    } else {
-        return QString::localeAwareCompare(leftDisplayedName, rightDisplayedName) < 0;
-    }
 }
 
 void AccountFilterModel::setSortByPresence(bool enabled)
@@ -139,4 +149,14 @@ bool AccountFilterModel::isSortedByPresence() const
     return sortRole() == AccountsModel::PresenceTypeRole;
 }
 
+bool AccountFilterModel::groupsActive() const
+{
+    return m_groupsActive;
+}
+
+void AccountFilterModel::setGroupsActive(bool active)
+{
+    m_groupsActive = active;
+}
+
 #include "account-filter-model.moc"
diff --git a/account-filter-model.h b/account-filter-model.h
index 6591355..007c2a9 100644
--- a/account-filter-model.h
+++ b/account-filter-model.h
@@ -48,6 +48,9 @@ public:
      */
     bool isSortedByPresence() const;
 
+    bool groupsActive() const;
+    void setGroupsActive(bool active);
+
 
 public slots:
     void filterOfflineUsers(bool filterOfflineUsers);
@@ -73,6 +76,9 @@ private:
 
     /// Holds the string which is searched in the model
     QString m_filterString;
+
+    /// True if the source is groups model
+    bool m_groupsActive;
 };
 
 #endif // ACCOUNTFILTERMODEL_H
diff --git a/contact-delegate.cpp b/contact-delegate.cpp
index 209e715..e1e444a 100644
--- a/contact-delegate.cpp
+++ b/contact-delegate.cpp
@@ -24,9 +24,10 @@
 
 #include <QtGui/QPainter>
 #include <QtGui/QPainterPath>
+#include <QtGui/QToolTip>
 #include <QApplication>
 #include <QStyle>
-#include <QtGui/QToolTip>
+#include <QHelpEvent>
 
 #include <KIconLoader>
 #include <KIcon>
@@ -36,7 +37,9 @@
 
 #include "accounts-model.h"
 #include "contact-model-item.h"
-#include <QHelpEvent>
+#include "proxy-tree-node.h"
+#include "groups-model-item.h"
+#include "groups-model.h"
 
 const int SPACING = 4;
 const int AVATAR_SIZE = 32;
@@ -67,7 +70,7 @@ void ContactDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opt
     QStyle *style = QApplication::style();
     style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter);
 
-    bool isContact = !index.data(AccountsModel::AliasRole).toString().isEmpty();
+    bool isContact = index.data(AccountsModel::ItemRole).userType() == qMetaTypeId<ContactModelItem*>();
 
     if (isContact) {
         QRect iconRect = optV4.rect;
@@ -182,25 +185,35 @@ void ContactDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opt
         QString counts;// = QString(" (%1/%2)").arg(index.data(AccountsModel::).toString(),
                         //               index.data(ModelRoles::AccountAllContactsCountRole).toString());
 
-        painter->fillRect(groupRect, m_palette->color(QPalette::AlternateBase));
-
-        painter->drawPixmap(accountGroupRect, KIcon(index.data(AccountsModel::IconRole).toString())
-                                                   .pixmap(ACCOUNT_ICON_SIZE, ACCOUNT_ICON_SIZE));
+        if (index.data(AccountsModel::ItemRole).userType() == qMetaTypeId<AccountsModelItem*>()) {
+            painter->drawPixmap(accountGroupRect, KIcon(index.data(AccountsModel::IconRole).toString())
+                                                        .pixmap(ACCOUNT_ICON_SIZE, ACCOUNT_ICON_SIZE));
+        } else {
+            painter->drawPixmap(accountGroupRect, KIconLoader::global()->loadIcon(QString("system-users"),
+                                                                                          KIconLoader::Desktop));
+        }
 
         painter->setPen(m_palette->color(QPalette::WindowText));
         painter->setFont(groupFont);
         painter->drawText(groupLabelRect, Qt::AlignVCenter | Qt::AlignRight,
-                          index.data(AccountsModel::DisplayNameRole).toString().append(counts));
+                          index.data(GroupsModel::GroupNameRole).toString().append(counts));
 
         QPen thinLinePen;
         thinLinePen.setWidth(0);
-        thinLinePen.setCosmetic(true);
-        thinLinePen.setColor(m_palette->color(QPalette::ButtonText));
+        thinLinePen.setColor(m_palette->color(QPalette::Disabled, QPalette::Button));
 
         painter->setPen(thinLinePen);
+        painter->setRenderHint(QPainter::Antialiasing, false);
 
-        painter->drawLine(groupRect.x(), groupRect.y(), groupRect.width(), groupRect.y());
-        painter->drawLine(groupRect.x(), groupRect.bottom(), groupRect.width(), groupRect.bottom());
+        QFontMetrics fm = painter->fontMetrics();
+        int groupNameWidth = fm.width(index.data(GroupsModel::GroupNameRole).toString());
+
+        painter->drawLine(expandSignRect.right() + SPACING * 2,
+                          groupRect.y() + groupRect.height() / 2,
+                          groupRect.width() - groupNameWidth - SPACING * 2,
+                          groupRect.y() + groupRect.height() / 2);
+
+        painter->setRenderHint(QPainter::Antialiasing, true);
 
         QStyleOption expandSignOption = option;
         expandSignOption.rect = expandSignRect;
@@ -218,12 +231,13 @@ void ContactDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opt
 QSize ContactDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
 {
     Q_UNUSED(option);
-//     if(option.state & QStyle::State_Selected)
-//         kDebug() << index.data(ModelRoles::UserNameRole).toString();
+    bool isContact = index.data(AccountsModel::ItemRole).userType() == qMetaTypeId<ContactModelItem*>();
 
-    if (!index.data(AccountsModel::AliasRole).toString().isEmpty()) {
+    if (isContact) {
         return QSize(0, 32 + 4 * SPACING);
-    } else return QSize(0, 20);
+    } else {
+        return QSize(0, 20);
+    }
 }
 
 void ContactDelegate::hideStatusMessageSlot(const QModelIndex& index)
@@ -330,8 +344,6 @@ bool ContactDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view, cons
      * * Contact is blocked will only show if the contact is blocked, else no display.
      */
 
-    kDebug() << contactAvatar;
-
     QString table;
     table += QString("<table><th colspan='2' align='center'><img src='%2' height='16' width='16' /> %3</th>").arg(cmIconPath, displayName);
     if (contactAvatar.isEmpty() || QPixmap(contactAvatar).isNull()) {
diff --git a/groups-model-item.cpp b/groups-model-item.cpp
new file mode 100644
index 0000000..6eec952
--- /dev/null
+++ b/groups-model-item.cpp
@@ -0,0 +1,116 @@
+/*
+ * Contact groups model item, represents a group in the contactlist tree
+ * This file is based on TelepathyQt4Yell Models
+ *
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2011 Martin Klapetek <martin dot klapetek at gmail dot com>
+ *
+ * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <TelepathyQt4/Account>
+#include <TelepathyQt4/ContactManager>
+
+#include "groups-model-item.h"
+#include "groups-model.h"
+#include "accounts-model.h"
+#include "proxy-tree-node.h"
+#include "contact-model-item.h"
+
+struct GroupsModelItem::Private
+{
+    Private(const QString &groupName)
+        : mGroupName(groupName)
+    {
+    }
+
+    void setGroupName(const QString &value);
+    QString groupName();
+
+    QString mGroupName;
+};
+
+void GroupsModelItem::Private::setGroupName(const QString& value)
+{
+    mGroupName = value;
+}
+
+QString GroupsModelItem::Private::groupName()
+{
+    return mGroupName;
+}
+
+GroupsModelItem::GroupsModelItem(const QString &groupName)
+    : mPriv(new Private(groupName))
+{
+}
+
+GroupsModelItem::~GroupsModelItem()
+{
+    delete mPriv;
+}
+
+QVariant GroupsModelItem::data(int role) const
+{
+    switch (role) {
+    case AccountsModel::ItemRole:
+        return QVariant::fromValue((GroupsModelItem*)this);
+    case GroupsModel::GroupNameRole:
+        return mPriv->mGroupName;
+    default:
+        return QVariant();
+    }
+}
+
+bool GroupsModelItem::setData(int role, const QVariant &value)
+{
+    switch (role) {
+    case GroupsModel::GroupNameRole:
+        setGroupName(value.toString());
+        return true;
+    default:
+        return false;
+    }
+}
+
+void GroupsModelItem::setGroupName(const QString& value)
+{
+    mPriv->setGroupName(value);
+}
+
+QString GroupsModelItem::groupName()
+{
+    return mPriv->groupName();
+}
+
+void GroupsModelItem::addProxyContact(ProxyTreeNode *proxyNode)
+{
+    emit childrenAdded(this, QList<TreeNode*>() << proxyNode);
+}
+
+void GroupsModelItem::removeProxyContact(ProxyTreeNode *proxyNode)
+{
+    emit childrenRemoved(this, indexOf(proxyNode), indexOf(proxyNode));
+}
+
+void GroupsModelItem::removeContact(ContactModelItem* contact)
+{
+    for (int i = 0; i < children().size(); i++) {
+        ProxyTreeNode* proxyNode = qobject_cast<ProxyTreeNode*>(childAt(i));
+        if (proxyNode->data(AccountsModel::ItemRole).value<ContactModelItem*>() == contact) {
+            proxyNode->remove();
+        }
+    }
+}
diff --git a/contact-model-item.h b/groups-model-item.h
similarity index 56%
copy from contact-model-item.h
copy to groups-model-item.h
index 772becd..63f3973 100644
--- a/contact-model-item.h
+++ b/groups-model-item.h
@@ -1,8 +1,8 @@
 /*
- * Contacts model item, represents a contact in the contactlist tree
+ * Contact groups model item, represents a group in the contactlist tree
  * This file is based on TelepathyQt4Yell Models
  *
- * Copyright (C) 2010 Collabora Ltd. <info at collabora.co.uk>
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
  * Copyright (C) 2011 Martin Klapetek <martin dot klapetek at gmail dot com>
  *
  * This library is free software; you can redistribute it and/or
@@ -20,31 +20,43 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#ifndef TELEPATHY_CONTACT_MODEL_ITEM_H
-#define TELEPATHY_CONTACT_MODEL_ITEM_H
+#ifndef TELEPATHY_GROUPS_MODEL_ITEM_H
+#define TELEPATHY_GROUPS_MODEL_ITEM_H
 
+#include <TelepathyQt4/Constants>
+#include <TelepathyQt4/Contact>
 #include <TelepathyQt4/Types>
 
 #include <QtCore/QVariant> //needed for declare metatype
 
 #include "tree-node.h"
 
-class ContactModelItem : public TreeNode
+class ContactModelItem;
+class ProxyTreeNode;
+
+class GroupsModelItem : public TreeNode
 {
     Q_OBJECT
-    Q_DISABLE_COPY(ContactModelItem)
+    Q_DISABLE_COPY(GroupsModelItem)
 
 public:
-    ContactModelItem(const Tp::ContactPtr &contact);
-    virtual ~ContactModelItem();
+    GroupsModelItem(const QString &groupName);
+    virtual ~GroupsModelItem();
 
     Q_INVOKABLE virtual QVariant data(int role) const;
-    Q_INVOKABLE virtual bool setData(int role, const QVariant &value);
+    virtual bool setData(int role, const QVariant &value);
+
+    Q_INVOKABLE void setGroupName(const QString &value);
+    QString groupName();
+
+    void addProxyContact(ProxyTreeNode* proxyNode);
+    void removeProxyContact(ProxyTreeNode* proxyNode);
+    void removeContact(ContactModelItem* contact);
 
-    Tp::ContactPtr contact() const;
+//     void clearContacts();
+
+private Q_SLOTS:
 
-public Q_SLOTS:
-    void onChanged();
 
 private:
     struct Private;
@@ -52,7 +64,6 @@ private:
     Private *mPriv;
 };
 
-Q_DECLARE_METATYPE(ContactModelItem*);
-
+Q_DECLARE_METATYPE(GroupsModelItem*);
 
-#endif // TELEPATHY_CONTACT_MODEL_ITEM_H
+#endif // TELEPATHY_GROUPS_MODEL_ITEM_H
diff --git a/groups-model.cpp b/groups-model.cpp
new file mode 100644
index 0000000..2b7fec9
--- /dev/null
+++ b/groups-model.cpp
@@ -0,0 +1,345 @@
+/*
+ * Contact groups model
+ * This file is based on TelepathyQt4Yell Models
+ *
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2011 Martin Klapetek <martin dot klapetek at gmail dot com>
+ *
+ * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <TelepathyQt4/ContactManager>
+#include <TelepathyQt4/Contact>
+#include <TelepathyQt4/PendingReady>
+
+#include "groups-model.h"
+#include "groups-model-item.h"
+#include "proxy-tree-node.h"
+#include "accounts-model.h"
+#include "contact-model-item.h"
+
+#include <KDebug>
+
+struct GroupsModel::Private
+{
+    Private(AccountsModel *am)
+        : mAM(am)
+    {
+    }
+
+    TreeNode *node(const QModelIndex &index) const;
+
+    AccountsModel *mAM;
+    TreeNode *mTree;
+};
+
+TreeNode *GroupsModel::Private::node(const QModelIndex &index) const
+{
+    TreeNode *node = reinterpret_cast<TreeNode *>(index.internalPointer());
+    return node ? node : mTree;
+}
+
+GroupsModel::GroupsModel(AccountsModel *am, QObject *parent)
+    : QAbstractItemModel(parent),
+      mPriv(new GroupsModel::Private(am))
+{
+    mPriv->mTree = new TreeNode;
+
+    connect(mPriv->mTree,
+            SIGNAL(changed(TreeNode*)),
+            SLOT(onItemChanged(TreeNode*)));
+
+    connect(mPriv->mTree,
+            SIGNAL(childrenAdded(TreeNode*,QList<TreeNode*>)),
+            SLOT(onItemsAdded(TreeNode*,QList<TreeNode*>)));
+
+    connect(mPriv->mTree,
+            SIGNAL(childrenRemoved(TreeNode*,int,int)),
+            SLOT(onItemsRemoved(TreeNode*,int,int)));
+
+    loadAccountsModel();
+    QHash<int, QByteArray> roles;
+    roles[GroupNameRole] = "groupName";
+    setRoleNames(roles);
+}
+
+GroupsModel::~GroupsModel()
+{
+    delete mPriv->mTree;
+    delete mPriv;
+}
+
+int GroupsModel::columnCount(const QModelIndex &parent) const
+{
+    return 1;
+}
+
+int GroupsModel::rowCount(const QModelIndex &parent) const
+{
+    return mPriv->node(parent)->size();
+}
+
+QVariant GroupsModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid()) {
+        return QVariant();
+    }
+
+    return mPriv->node(index)->data(role);
+}
+
+Qt::ItemFlags GroupsModel::flags(const QModelIndex &index) const
+{
+    if (index.isValid()) {
+        return Qt::ItemIsEnabled;
+    }
+
+    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
+}
+
+bool GroupsModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+    if (index.isValid()) {
+        mPriv->node(index)->setData(role, value);
+    }
+
+    return false;
+}
+
+QModelIndex GroupsModel::index(int row, int column, const QModelIndex &parent) const
+{
+    TreeNode *parentNode = mPriv->node(parent);
+    if (row < parentNode->size()) {
+        return createIndex(row, column, parentNode->childAt(row));
+    }
+
+    return QModelIndex();
+}
+
+QModelIndex GroupsModel::index(TreeNode *node) const
+{
+    if (node->parent()) {
+        return createIndex(node->parent()->indexOf(node), 0, node);
+    } else {
+        return QModelIndex();
+    }
+}
+
+QModelIndex GroupsModel::parent(const QModelIndex &index) const
+{
+    if (!index.isValid()) {
+        return QModelIndex();
+    }
+
+    TreeNode *currentNode = mPriv->node(index);
+    if (currentNode->parent()) {
+        return GroupsModel::index(currentNode->parent());
+    } else {
+        // no parent: return root node
+        return QModelIndex();
+    }
+}
+
+void GroupsModel::onItemChanged(TreeNode* node)
+{
+    emit dataChanged(index(node), index(node));
+}
+
+void GroupsModel::onItemsAdded(TreeNode *parent, const QList<TreeNode *> &nodes)
+{
+    QModelIndex parentIndex = index(parent);
+    int currentSize = rowCount(parentIndex);
+    beginInsertRows(parentIndex, currentSize, currentSize + nodes.size() - 1);
+    foreach (TreeNode *node, nodes) {
+        parent->addChild(node);
+    }
+    endInsertRows();
+}
+
+void GroupsModel::onItemsRemoved(TreeNode *parent, int first, int last)
+{
+    kDebug();
+    QModelIndex parentIndex = index(parent);
+    QList<TreeNode *> removedItems;
+    beginRemoveRows(parentIndex, first, last);
+    for (int i = last; i >= first; i--) {
+        parent->childAt(i)->remove();
+    }
+    endRemoveRows();
+}
+
+
+void GroupsModel::onSourceItemsAdded(TreeNode *parent, const QList<TreeNode *> &nodes)
+{
+    kDebug() << "Adding" << nodes.size() << "nodes...";
+    QModelIndex parentIndex = index(parent);
+    int currentSize = rowCount(parentIndex);
+    foreach (TreeNode *node, nodes) {
+        ContactModelItem *contactItem = qobject_cast<ContactModelItem*>(node);
+        QStringList groups = contactItem->contact()->groups();
+        addContactToGroups(contactItem, groups);
+    }
+}
+
+void GroupsModel::onSourceItemsRemoved(TreeNode* parent, int first, int last)
+{
+
+}
+
+void GroupsModel::loadAccountsModel()
+{
+    for (int x = 0; x < mPriv->mAM->rowCount(); x++) {
+        QModelIndex parent = mPriv->mAM->index(x, 0);
+        for (int i = 0; i < mPriv->mAM->rowCount(parent); i++) {
+            if (mPriv->mAM->data(mPriv->mAM->index(i, 0, parent),
+                                 AccountsModel::ItemRole).userType() == qMetaTypeId<ContactModelItem*>()) {
+
+                QStringList groups = mPriv->mAM->data(mPriv->mAM->index(i, 0, parent),
+                                                      AccountsModel::GroupsRole).toStringList();
+
+                ContactModelItem *contactItem = mPriv->mAM->data(mPriv->mAM->index(i, 0, parent),
+                                                                 AccountsModel::ItemRole).value<ContactModelItem*>();
+
+                addContactToGroups(contactItem, groups);
+            }
+        }
+
+        //we need to connect accounts onItemsAdded/onItemsRemoved to watch for changes
+        //and process them directly (directly add/remove the nodes)
+        AccountsModelItem *accountItem = mPriv->mAM->data(parent, AccountsModel::ItemRole).value<AccountsModelItem*>();
+        connect(accountItem, SIGNAL(childrenAdded(TreeNode*,QList<TreeNode*>)),
+                this, SLOT(onSourceItemsAdded(TreeNode*,QList<TreeNode*>)));
+
+        connect(accountItem, SIGNAL(childrenRemoved(TreeNode*,int,int)),
+                this, SLOT(onSourceItemsRemoved(TreeNode*,int,int)));
+
+        kDebug() << "Connecting" << accountItem->account()->displayName() << "to groups model";
+
+    }
+}
+
+void GroupsModel::onContactAddedToGroup(const QString& group)
+{
+    addContactToGroups(qobject_cast<ProxyTreeNode*>(sender()), group);
+}
+
+void GroupsModel::onContactRemovedFromGroup(const QString& group)
+{
+    removeContactFromGroup(qobject_cast<ProxyTreeNode*>(sender()), group);
+}
+
+void GroupsModel::removeContactFromGroup(ProxyTreeNode* proxyNode, const QString& group)
+{
+    kDebug() << "Removing contact from" << group;
+
+    QStringList contactGroups = proxyNode->data(AccountsModel::ItemRole).value<ContactModelItem*>()->contact()->groups();
+    kDebug() << "Left groups are:" << contactGroups;
+
+    contactGroups.removeOne(group);
+
+    //if the contact really is in that group, remove it
+    if (qobject_cast<GroupsModelItem*>(proxyNode->parent())->groupName() == group) {
+
+        disconnect(proxyNode, SIGNAL(contactAddedToGroup(QString)), 0, 0);
+        disconnect(proxyNode, SIGNAL(contactRemovedFromGroup(QString)), 0, 0);
+
+        //if the the contact has no groups left, then put it in Ungroupped group
+        if (contactGroups.isEmpty()) {
+            addContactToGroups(proxyNode->data(AccountsModel::ItemRole).value<ContactModelItem*>(), contactGroups);
+        }
+
+//         beginRemoveRows(index(proxyNode->parent()), proxyNode->parent()->indexOf(proxyNode), proxyNode->parent()->indexOf(proxyNode));
+        qobject_cast<GroupsModelItem*>(proxyNode->parent())->removeProxyContact(proxyNode);
+//         endRemoveRows();
+    }
+}
+
+void GroupsModel::addContactToGroups(ProxyTreeNode* proxyNode, const QString& group)
+{
+    addContactToGroups(proxyNode->data(AccountsModel::ItemRole).value<ContactModelItem*>(), group);
+}
+
+void GroupsModel::addContactToGroups(ContactModelItem* contactItem, const QString& group)
+{
+    addContactToGroups(contactItem, QStringList(group));
+}
+
+void GroupsModel::addContactToGroups(ContactModelItem* contactItem, QStringList groups)
+{
+    kDebug() << "Contact groups:" << groups;
+
+    //check if the contact is in Ungroupped group, if it is, it needs to be removed from there
+    bool checkUngroupped = false;
+    //if the contact has no groups, create an 'Ungroupped' group for it
+    if (groups.isEmpty()) {
+        groups.append("Ungroupped"); //FIXME i18n
+    } else {
+        checkUngroupped = true;
+    }
+
+    groups.removeDuplicates();
+
+    foreach (QString group, groups) {
+        bool groupExists = false;
+        GroupsModelItem *groupItem;
+
+        kDebug() << "Adding" << contactItem->contact()->alias() << "to" << group;
+
+        //check if the group already exists first
+        for (int i = 0; i < mPriv->mTree->children().size(); i++) {
+//         foreach (GroupsModelItem *savedGroupItem, mPriv->mTree->children()) {
+            GroupsModelItem *savedGroupItem = qobject_cast<GroupsModelItem*>(mPriv->mTree->childAt(i));
+            if (savedGroupItem->groupName() == group) {
+                groupExists = true;
+                kDebug() << "Existing group found for" << group;
+                groupItem = savedGroupItem;
+
+                if (!checkUngroupped) {
+                    break;
+                }
+            }
+            if (checkUngroupped) {
+                if (savedGroupItem->groupName() == "Ungroupped") {
+                    for (int i = 0; i < savedGroupItem->size(); i++) {
+                        ProxyTreeNode *tmpNode = qobject_cast<ProxyTreeNode*>(savedGroupItem->childAt(i));
+                        if (tmpNode->data(AccountsModel::ItemRole).value<ContactModelItem*>()->contact()->id() == contactItem->contact()->id()) {
+                            removeContactFromGroup(tmpNode, QString("Ungroupped"));
+                            if (groupExists) {
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!groupExists) {
+            kDebug() << "Creating new group for" << group;
+            groupItem = new GroupsModelItem(group);
+            onItemsAdded(mPriv->mTree, QList<TreeNode *>() << groupItem);
+        }
+
+        ProxyTreeNode *proxyNode = new ProxyTreeNode(contactItem);
+        groupItem->addProxyContact(proxyNode);
+
+        connect(proxyNode, SIGNAL(contactAddedToGroup(QString)),
+                this, SLOT(onContactAddedToGroup(QString)));
+
+        connect(proxyNode, SIGNAL(contactRemovedFromGroup(QString)),
+                this, SLOT(onContactRemovedFromGroup(QString)));
+
+    }
+}
+
diff --git a/groups-model.h b/groups-model.h
new file mode 100644
index 0000000..f838897
--- /dev/null
+++ b/groups-model.h
@@ -0,0 +1,94 @@
+/*
+ * Contact groups model
+ * This file is based on TelepathyQt4Yell Models
+ *
+ * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2011 Martin Klapetek <martin dot klapetek at gmail dot com>
+ *
+ * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef TELEPATHY_GROUPS_MODEL_H
+#define TELEPATHY_GROUPS_MODEL_H
+
+#include <QAbstractListModel>
+
+#include <TelepathyQt4/Account>
+#include <TelepathyQt4/AccountManager>
+#include <TelepathyQt4/TextChannel>
+#include <TelepathyQt4/Types>
+
+class ContactModelItem;
+class GroupsModelItem;
+class AccountsModel;
+class ProxyTreeNode;
+class TreeNode;
+
+class GroupsModel : public QAbstractItemModel
+{
+    Q_OBJECT
+//    Q_DISABLE_COPY(GroupsModel)const AccountsModel& am, QObject* parentconst AccontsModel& am, QObject* parent
+//     Q_PROPERTY(int accountCount READ accountCount NOTIFY accountCountChanged)
+    Q_ENUMS(Role)
+
+public:
+    enum Role {
+        // general roles
+        GroupNameRole = Qt::DisplayRole
+    };
+
+    explicit GroupsModel(AccountsModel* am, QObject* parent = 0);
+    virtual ~GroupsModel();
+
+    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+    virtual QVariant data(const QModelIndex &index, int role) const;
+
+    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+    virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+    virtual QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
+    virtual QModelIndex index(TreeNode *node) const;
+    virtual QModelIndex parent(const QModelIndex &index) const;
+
+    void addContactToGroups(ContactModelItem* contactItem, QStringList groups = QStringList());
+    ///Convenience classes for addContactToGroups
+    void addContactToGroups(ProxyTreeNode* proxyNode, const QString& group);
+    void addContactToGroups(ContactModelItem* contactItem, const QString& group);
+
+    void removeContactFromGroup(ProxyTreeNode* proxyNode, const QString& group);
+
+// Q_SIGNALS:
+//     void accountCountChanged();
+//     void accountConnectionStatusChanged(const QString &accountId, int status);
+
+private Q_SLOTS:
+//     void onNewAccount(const Tp::AccountPtr &account);
+    void onItemChanged(TreeNode *node);
+    void loadAccountsModel();
+    void onItemsAdded(TreeNode *parent, const QList<TreeNode *> &nodes);
+    void onItemsRemoved(TreeNode *parent, int first, int last);
+
+    void onSourceItemsAdded(TreeNode *parent, const QList<TreeNode *> &nodes);
+    void onSourceItemsRemoved(TreeNode *parent, int first, int last);
+    void onContactAddedToGroup(const QString &group);
+    void onContactRemovedFromGroup(const QString &group);
+
+private:
+    struct Private;
+    friend struct Private;
+    Private *mPriv;
+};
+
+#endif // TELEPATHY_GROUPS_MODEL_H
diff --git a/main-widget.cpp b/main-widget.cpp
index 41f3be1..98f7fd7 100644
--- a/main-widget.cpp
+++ b/main-widget.cpp
@@ -62,6 +62,7 @@
 #include "add-contact-dialog.h"
 #include "remove-contact-dialog.h"
 #include "fetch-avatar-job.h"
+#include "groups-model.h"
 
 #define PREFERRED_TEXTCHAT_HANDLER "org.freedesktop.Telepathy.Client.KDE.TextUi"
 #define PREFERRED_FILETRANSFER_HANDLER "org.freedesktop.Telepathy.Client.KDE.FileTransfer"
@@ -252,8 +253,15 @@ void MainWidget::onAccountManagerReady(Tp::PendingOperation* op)
     }
 
     m_model = new AccountsModel(m_accountManager, this);
+    m_groupsModel = new GroupsModel(m_model, this);
     m_modelFilter = new AccountFilterModel(this);
-    m_modelFilter->setSourceModel(m_model);
+    if (m_groupContactsAction->isChecked()) {
+        m_modelFilter->setGroupsActive(true);
+        m_modelFilter->setSourceModel(m_groupsModel);
+    } else {
+        m_modelFilter->setGroupsActive(false);
+        m_modelFilter->setSourceModel(m_model);
+    }
     m_modelFilter->setDynamicSortFilter(true);
     m_modelFilter->filterOfflineUsers(m_hideOfflineAction->isChecked());
     m_modelFilter->clearFilterString();
@@ -306,7 +314,7 @@ void MainWidget::onAccountManagerReady(Tp::PendingOperation* op)
     foreach (const Tp::AccountPtr account, accounts) {
         onNewAccountAdded(account);
     }
-    m_contactsListView->expandAll();
+//     m_contactsListView->expandAll();
 }
 
 void MainWidget::onAccountConnectionStatusChanged(Tp::ConnectionStatus status)
@@ -464,11 +472,22 @@ void MainWidget::startTextChannel(const QModelIndex &index)
     }
 
     QModelIndex realIndex = m_modelFilter->mapToSource(index);
-    Tp::ContactPtr contact = m_model->data(realIndex, AccountsModel::ItemRole).value<ContactModelItem*>()->contact();
+    Tp::ContactPtr contact;
+
+    if (m_groupContactsAction->isChecked()) {
+        contact = m_groupsModel->data(realIndex, AccountsModel::ItemRole).value<ContactModelItem*>()->contact();
+    } else {
+        contact = m_model->data(realIndex, AccountsModel::ItemRole).value<ContactModelItem*>()->contact();
+    }
 
     kDebug() << "Requesting chat for contact" << contact->alias();
 
-    Tp::AccountPtr account = m_model->accountForContactIndex(realIndex);
+    Tp::AccountPtr account;
+    if (m_groupContactsAction->isChecked()) {
+        account = qobject_cast<AccountsModelItem*>(m_groupsModel->data(realIndex, AccountsModel::ItemRole).value<ContactModelItem*>()->parent())->account();
+    } else {
+        account = m_model->accountForContactIndex(realIndex);
+    }
 
     Tp::PendingChannelRequest* channelRequest = account->ensureTextChat(contact,
                                                                         QDateTime::currentDateTime(),
@@ -656,13 +675,25 @@ void MainWidget::onCustomContextMenuRequested(const QPoint &)
 {
     QModelIndex index = m_contactsListView->currentIndex();
 
-    Tp::ContactPtr contact = m_model->contactForIndex(m_modelFilter->mapToSource(index));
+    Tp::ContactPtr contact;
+    if (m_groupContactsAction->isChecked()) {
+        contact = m_groupsModel->data(m_modelFilter->mapToSource(index), AccountsModel::ItemRole).value<ContactModelItem*>()->contact();
+    } else {
+        contact = m_model->contactForIndex(m_modelFilter->mapToSource(index));
+    }
+
     if (contact.isNull()) {
         kDebug() << "Contact is nulled";
         return;
     }
 
-    Tp::AccountPtr account = m_model->accountForContactIndex(m_modelFilter->mapToSource(index));
+    Tp::AccountPtr account;
+    if (m_groupContactsAction->isChecked()) {
+        account = qobject_cast<AccountsModelItem*>(m_groupsModel->data(m_modelFilter->mapToSource(index), AccountsModel::ItemRole).value<ContactModelItem*>()->parent())->account();
+    } else {
+        account = m_model->accountForContactIndex(m_modelFilter->mapToSource(index));
+    }
+
     if (account.isNull()) {
         kDebug() << "Account is nulled";
         return;
@@ -772,7 +803,12 @@ void MainWidget::onCustomContextMenuRequested(const QPoint &)
 void MainWidget::slotAddContactToGroupTriggered()
 {
     QModelIndex index = m_contactsListView->currentIndex();
-    Tp::ContactPtr contact = m_model->contactForIndex(m_modelFilter->mapToSource(index));
+    Tp::ContactPtr contact;
+    if (m_groupContactsAction->isChecked()) {
+        contact = m_groupsModel->data(m_modelFilter->mapToSource(index), AccountsModel::ItemRole).value<ContactModelItem*>()->contact();
+    } else {
+        contact = m_model->contactForIndex(m_modelFilter->mapToSource(index));
+    }
     if (contact.isNull()) {
         kDebug() << "Contact is nulled";
         return;
@@ -1123,5 +1159,18 @@ void MainWidget::handleConnectionError(const Tp::AccountPtr& account)
     }
 }
 
+void MainWidget::onGroupContacts(bool enabled)
+{
+    if (enabled) {
+        m_modelFilter->setSourceModel(0);   //this prevents some crashes
+        m_modelFilter->setGroupsActive(true);
+        m_modelFilter->setSourceModel(m_groupsModel);
+    } else {
+        m_modelFilter->setSourceModel(0);
+        m_modelFilter->setGroupsActive(false);
+        m_modelFilter->setSourceModel(m_model);
+    }
+
+}
 
 #include "main-widget.moc"
diff --git a/main-widget.h b/main-widget.h
index 4ec5357..555f1f1 100644
--- a/main-widget.h
+++ b/main-widget.h
@@ -33,6 +33,7 @@
 #include <KAction>
 #include "ui_main-widget.h"
 
+class GroupsModel;
 class KMenu;
 class KSelectAction;
 class AccountsModel;
@@ -88,6 +89,7 @@ public Q_SLOTS:
     void startAudioChannel(const QModelIndex &index);
     void startVideoChannel(const QModelIndex &index);
     void onCustomContextMenuRequested(const QPoint &point);
+    void onGroupContacts(bool enabled);
 
 private Q_SLOTS:
     void slotAddContactToGroupTriggered();
@@ -112,6 +114,7 @@ private:
     void handleConnectionError(const Tp::AccountPtr &account);
 
     AccountsModel          *m_model;
+    GroupsModel            *m_groupsModel;
     AccountFilterModel     *m_modelFilter;
     Tp::AccountManagerPtr   m_accountManager;
     KMenu                  *m_accountMenu;
diff --git a/proxy-tree-node.cpp b/proxy-tree-node.cpp
new file mode 100644
index 0000000..0dfa373
--- /dev/null
+++ b/proxy-tree-node.cpp
@@ -0,0 +1,84 @@
+/*
+ * Proxy tree model node
+ *
+ * Copyright (C) 2011 Martin Klapetek <martin dot klapetek at gmail dot com>
+ *
+ * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <TelepathyQt4/Contact>
+
+#include "proxy-tree-node.h"
+#include "tree-node.h"
+#include "contact-model-item.h"
+
+struct ProxyTreeNode::Private
+{
+    Private(ContactModelItem *sourceNode) :
+        mSource(sourceNode)
+    {
+    }
+
+    ~Private()
+    {
+    }
+
+    ContactModelItem *mSource;
+};
+
+ProxyTreeNode::ProxyTreeNode(ContactModelItem *sourceNode)
+    : mPriv(new Private(sourceNode))
+{
+    connect(sourceNode->contact().data(),
+            SIGNAL(addedToGroup(QString)),
+            SIGNAL(contactAddedToGroup(QString)));
+
+    connect(sourceNode->contact().data(),
+            SIGNAL(removedFromGroup(QString)),
+            SIGNAL(contactRemovedFromGroup(QString)));
+
+    connect(sourceNode, SIGNAL(destroyed(QObject*)),
+            this, SLOT(onSourceNodeRemoved()));
+
+    connect(sourceNode,
+            SIGNAL(changed(TreeNode*)),
+            SIGNAL(changed(TreeNode*)));
+}
+
+ProxyTreeNode::~ProxyTreeNode()
+{
+    delete mPriv;
+}
+
+QVariant ProxyTreeNode::data(int role) const
+{
+    if (!mPriv->mSource) {
+        return QVariant();
+    }
+    return mPriv->mSource->data(role);
+}
+
+bool ProxyTreeNode::setData(int role, const QVariant &value)
+{
+    return false;
+}
+
+void ProxyTreeNode::onSourceNodeRemoved()
+{
+    int index = parent()->indexOf(this);
+    emit childrenRemoved(parent(), index, index);
+
+    remove();
+}
diff --git a/tree-node.h b/proxy-tree-node.h
similarity index 58%
copy from tree-node.h
copy to proxy-tree-node.h
index adeefa4..347210a 100644
--- a/tree-node.h
+++ b/proxy-tree-node.h
@@ -1,8 +1,6 @@
-/*                                                                         *
- * Tree model node
- * This file is based on TelepathyQt4Yell Models
+/*
+ * Proxy tree model node
  *
- * Copyright (C) 2010 Collabora Ltd. <info at collabora.co.uk>
  * Copyright (C) 2011 Martin Klapetek <martin dot klapetek at gmail dot com>
  *
  * This library is free software; you can redistribute it and/or
@@ -20,40 +18,33 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#ifndef TELEPATHY_TREE_NODE_H
-#define TELEPATHY_TREE_NODE_H
+#ifndef TELEPATHY_PROXY_TREE_NODE_H
+#define TELEPATHY_PROXY_TREE_NODE_H
 
 #include <QObject>
 #include <QVariant>
+#include "tree-node.h"
 
-class TreeNode : public QObject
+class ContactModelItem;
+class ProxyTreeNode : public TreeNode
 {
     Q_OBJECT
-    Q_DISABLE_COPY(TreeNode)
+    Q_DISABLE_COPY(ProxyTreeNode)
 
 public:
-    TreeNode();
+    ProxyTreeNode(ContactModelItem* sourceNode);
 
-    virtual ~TreeNode();
-
-    TreeNode *childAt(int index) const;
-
-    void addChild(TreeNode *node);
-
-    int indexOf(TreeNode *node) const;
-
-    int size() const;
-
-    TreeNode *parent() const;
+    virtual ~ProxyTreeNode();
 
     virtual QVariant data(int role) const;
     virtual bool setData(int role, const QVariant &value);
-    virtual void remove();
+
+public Q_SLOTS:
+    void onSourceNodeRemoved();
 
 Q_SIGNALS:
-    void changed(TreeNode *);
-    void childrenAdded(TreeNode *parent, const QList<TreeNode *> &nodes);
-    void childrenRemoved(TreeNode *parent, int first, int last);
+    void contactAddedToGroup(const QString& group);
+    void contactRemovedFromGroup(const QString& group);
 
 private:
     struct Private;
@@ -61,4 +52,4 @@ private:
     Private *mPriv;
 };
 
-#endif // TELEPATHY_TREE_NODE_H
+#endif // TELEPATHY_PROXY_TREE_NODE_H
diff --git a/tree-node.h b/tree-node.h
index adeefa4..9f675ab 100644
--- a/tree-node.h
+++ b/tree-node.h
@@ -48,6 +48,8 @@ public:
 
     virtual QVariant data(int role) const;
     virtual bool setData(int role, const QVariant &value);
+
+public Q_SLOTS:
     virtual void remove();
 
 Q_SIGNALS:

-- 
ktp-contact-list packaging



More information about the pkg-kde-commits mailing list