summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Roster')
-rw-r--r--Swiften/Roster/AppearOffline.h26
-rw-r--r--Swiften/Roster/ContactRosterItem.cpp50
-rw-r--r--Swiften/Roster/ContactRosterItem.h41
-rw-r--r--Swiften/Roster/GroupRosterItem.h68
-rw-r--r--Swiften/Roster/OfflineRosterFilter.h24
-rw-r--r--Swiften/Roster/OpenChatRosterAction.h20
-rw-r--r--Swiften/Roster/Roster.cpp139
-rw-r--r--Swiften/Roster/Roster.h49
-rw-r--r--Swiften/Roster/RosterFilter.h17
-rw-r--r--Swiften/Roster/RosterItem.h24
-rw-r--r--Swiften/Roster/RosterItemOperation.h16
-rw-r--r--Swiften/Roster/SetAvatar.h33
-rw-r--r--Swiften/Roster/SetPresence.h38
-rw-r--r--Swiften/Roster/TreeWidget.h13
-rw-r--r--Swiften/Roster/TreeWidgetFactory.h20
-rw-r--r--Swiften/Roster/TreeWidgetItem.h34
-rw-r--r--Swiften/Roster/UnitTest/MockTreeWidget.h14
-rw-r--r--Swiften/Roster/UnitTest/MockTreeWidgetFactory.h50
-rw-r--r--Swiften/Roster/UnitTest/MockTreeWidgetItem.h30
-rw-r--r--Swiften/Roster/UnitTest/OfflineRosterFilterTest.cpp0
-rw-r--r--Swiften/Roster/UnitTest/RosterTest.cpp57
-rw-r--r--Swiften/Roster/UnitTest/XMPPRosterTest.cpp164
-rw-r--r--Swiften/Roster/UserRosterAction.h32
-rw-r--r--Swiften/Roster/XMPPRoster.cpp48
-rw-r--r--Swiften/Roster/XMPPRoster.h44
25 files changed, 1051 insertions, 0 deletions
diff --git a/Swiften/Roster/AppearOffline.h b/Swiften/Roster/AppearOffline.h
new file mode 100644
index 0000000..673e018
--- /dev/null
+++ b/Swiften/Roster/AppearOffline.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "Swiften/Roster/RosterItemOperation.h"
+#include "Swiften/Roster/ContactRosterItem.h"
+
+namespace Swift {
+
+class RosterItem;
+
+class AppearOffline : public RosterItemOperation {
+ public:
+ AppearOffline() {
+ }
+
+ virtual void operator() (RosterItem* item) const {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);
+ if (contact) {
+ contact->setStatusShow(StatusShow::None);
+ }
+ }
+
+};
+
+}
+
+
diff --git a/Swiften/Roster/ContactRosterItem.cpp b/Swiften/Roster/ContactRosterItem.cpp
new file mode 100644
index 0000000..968f7f1
--- /dev/null
+++ b/Swiften/Roster/ContactRosterItem.cpp
@@ -0,0 +1,50 @@
+#include "Swiften/Roster/ContactRosterItem.h"
+#include "Swiften/Roster/GroupRosterItem.h"
+
+namespace Swift {
+
+
+ContactRosterItem::ContactRosterItem(const JID& jid, const String& name, GroupRosterItem* parent, TreeWidgetFactory* factory) : jid_(jid), name_(name) {
+ parent->addChild(this);
+ widget_ = factory->createTreeWidgetItem(parent->getWidget());
+ widget_->setText(name.isEmpty() ? jid.toString() : name);
+ widget_->onUserAction.connect(boost::bind(&ContactRosterItem::handleUserAction, this, _1));
+ setStatusShow(StatusShow::None);
+}
+
+ContactRosterItem::~ContactRosterItem() {
+ delete widget_;
+}
+
+StatusShow::Type ContactRosterItem::getStatusShow() {
+ return statusShow_;
+}
+
+void ContactRosterItem::setStatusShow(StatusShow::Type show) {
+ statusShow_ = show;
+ widget_->setStatusShow(show);
+}
+
+void ContactRosterItem::setStatusText(const String& status) {
+ widget_->setStatusText(status);
+}
+
+void ContactRosterItem::setAvatarPath(const String& path) {
+ widget_->setAvatarPath(path);
+}
+
+const JID& ContactRosterItem::getJID() const {
+ return jid_;
+}
+
+void ContactRosterItem::show() {
+ widget_->show();
+}
+
+void ContactRosterItem::hide() {
+ widget_->hide();
+}
+
+}
+
+
diff --git a/Swiften/Roster/ContactRosterItem.h b/Swiften/Roster/ContactRosterItem.h
new file mode 100644
index 0000000..f1810aa
--- /dev/null
+++ b/Swiften/Roster/ContactRosterItem.h
@@ -0,0 +1,41 @@
+#ifndef SWIFTEN_ContactRosterItem_H
+#define SWIFTEN_ContactRosterItem_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+#include "Swiften/Roster/RosterItem.h"
+#include "Swiften/Roster/UserRosterAction.h"
+#include "Swiften/Elements/StatusShow.h"
+
+#include <boost/bind.hpp>
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class TreeWidgetItem;
+class GroupRosterItem;
+class ContactRosterItem : public RosterItem {
+ public:
+ ContactRosterItem(const JID& jid, const String& name, GroupRosterItem* parent, TreeWidgetFactory* factory);
+ ~ContactRosterItem();
+
+ StatusShow::Type getStatusShow();
+ void setStatusShow(StatusShow::Type show);
+ void setStatusText(const String& status);
+ void setAvatarPath(const String& path);
+ const JID& getJID() const;
+ void show();
+ void hide();
+
+ private:
+ JID jid_;
+ String name_;
+ TreeWidgetItem *widget_;
+ StatusShow::Type statusShow_;
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/GroupRosterItem.h b/Swiften/Roster/GroupRosterItem.h
new file mode 100644
index 0000000..2ab59ea
--- /dev/null
+++ b/Swiften/Roster/GroupRosterItem.h
@@ -0,0 +1,68 @@
+#ifndef SWIFTEN_GroupRosterItem_H
+#define SWIFTEN_GroupRosterItem_H
+
+#include "Swiften/Roster/RosterItem.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Roster/TreeWidget.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+#include "Swiften/Roster/TreeWidgetItem.h"
+#include "Swiften/Roster/ContactRosterItem.h"
+
+#include <list>
+
+namespace Swift {
+
+class GroupRosterItem : public RosterItem {
+ public:
+ GroupRosterItem(const String& name, TreeWidget* tree, TreeWidgetFactory* factory) : name_(name) {
+ widget_ = factory->createTreeWidgetItem(tree);
+ widget_->setExpanded(true);
+ widget_->setText(name);
+ }
+
+ ~GroupRosterItem() {
+ delete widget_;
+ }
+
+ const String& getName() const {
+ return name_;
+ }
+
+ TreeWidgetItem* getWidget() const {
+ return widget_;
+ }
+
+ const std::list<RosterItem*>& getChildren() const {
+ return children_;
+ }
+
+ void addChild(RosterItem* item) {
+ children_.push_back(item);
+ }
+
+ void removeChild(const JID& jid) {
+ std::list<RosterItem*>::iterator it = children_.begin();
+ while (it != children_.end()) {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(*it);
+ if (contact && contact->getJID() == jid) {
+ delete contact;
+ it = children_.erase(it);
+ continue;
+ }
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(*it);
+ if (group) {
+ group->removeChild(jid);
+ }
+ it++;
+ }
+ }
+
+ private:
+ String name_;
+ TreeWidgetItem* widget_;
+ std::list<RosterItem*> children_;
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/OfflineRosterFilter.h b/Swiften/Roster/OfflineRosterFilter.h
new file mode 100644
index 0000000..512d074
--- /dev/null
+++ b/Swiften/Roster/OfflineRosterFilter.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_OfflineRosterFilter_H
+#define SWIFTEN_OfflineRosterFilter_H
+
+#include "Swiften/Roster/ContactRosterItem.h"
+#include "Swiften/Roster/RosterItem.h"
+#include "Swiften/Roster/RosterFilter.h"
+#include "Swiften/Elements/StatusShow.h"
+
+namespace Swift {
+
+class OfflineRosterFilter : public RosterFilter {
+ public:
+ virtual ~OfflineRosterFilter() {}
+ virtual bool operator() (RosterItem *item) const {
+ ContactRosterItem *contactItem = dynamic_cast<ContactRosterItem*>(item);
+ return contactItem && contactItem->getStatusShow() == StatusShow::None;
+ }
+};
+
+}
+#endif
+
+
+
diff --git a/Swiften/Roster/OpenChatRosterAction.h b/Swiften/Roster/OpenChatRosterAction.h
new file mode 100644
index 0000000..03715a5
--- /dev/null
+++ b/Swiften/Roster/OpenChatRosterAction.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_OpenChatRosterAction_H
+#define SWIFTEN_OpenChatRosterAction_H
+
+#include "Swiften/Roster/UserRosterAction.h"
+
+namespace Swift {
+class RosterItem;
+class TreeWidgetItem;
+
+class OpenChatRosterAction : public UserRosterAction {
+ public:
+ virtual ~OpenChatRosterAction() {};
+
+};
+
+}
+#endif
+
+
+
diff --git a/Swiften/Roster/Roster.cpp b/Swiften/Roster/Roster.cpp
new file mode 100644
index 0000000..28245af
--- /dev/null
+++ b/Swiften/Roster/Roster.cpp
@@ -0,0 +1,139 @@
+#include "Swiften/Roster/Roster.h"
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Roster/ContactRosterItem.h"
+#include "Swiften/Roster/RosterItem.h"
+#include "Swiften/Roster/GroupRosterItem.h"
+#include "Swiften/Roster/RosterItemOperation.h"
+#include "Swiften/Roster/TreeWidget.h"
+#include "Swiften/Roster/TreeWidgetFactory.h"
+
+#include <boost/bind.hpp>
+
+#include <deque>
+
+namespace Swift {
+
+Roster::Roster(TreeWidget *treeWidget, TreeWidgetFactory *widgetFactory) : treeWidget_(treeWidget), widgetFactory_(widgetFactory) {
+}
+
+Roster::~Roster() {
+ foreach (RosterItem* item, items_) {
+ delete item;
+ }
+ delete treeWidget_;
+}
+
+TreeWidget* Roster::getWidget() {
+ return treeWidget_;
+}
+
+GroupRosterItem* Roster::getGroup(const String& groupName) {
+ foreach (RosterItem *item, children_) {
+ GroupRosterItem *group = dynamic_cast<GroupRosterItem*>(item);
+ if (group && group->getName() == groupName) {
+ return group;
+ }
+ }
+ GroupRosterItem* group = new GroupRosterItem(groupName, treeWidget_, widgetFactory_);
+ children_.push_back(group);
+ items_.push_back(group);
+ return group;
+}
+
+void Roster::handleUserAction(boost::shared_ptr<UserRosterAction> action) {
+ onUserAction(action);
+}
+
+void Roster::addContact(const JID& jid, const String& name, const String& group) {
+ ContactRosterItem *item = new ContactRosterItem(jid, name, getGroup(group), widgetFactory_);
+ items_.push_back(item);
+ item->onUserAction.connect(boost::bind(&Roster::handleUserAction, this, _1));
+ filterItem(item);
+
+}
+
+void Roster::removeContact(const JID& jid) {
+ std::vector<RosterItem*>::iterator it = children_.begin();
+ while (it != children_.end()) {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(*it);
+ if (contact && contact->getJID() == jid) {
+ delete contact;
+ it = children_.erase(it);
+ continue;
+ }
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(*it);
+ if (group) {
+ group->removeChild(jid);
+ }
+ it++;
+ }
+}
+
+void Roster::removeContactFromGroup(const JID& jid, const String& groupName) {
+ std::vector<RosterItem*>::iterator it = children_.begin();
+ while (it != children_.end()) {
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(*it);
+ if (group && group->getName() == groupName) {
+ group->removeChild(jid);
+ }
+ it++;
+ }
+}
+
+
+void Roster::applyOnItems(const RosterItemOperation& operation) {
+ std::deque<RosterItem*> queue(children_.begin(), children_.end());
+ while (!queue.empty()) {
+ RosterItem* item = *queue.begin();
+ queue.pop_front();
+ operation(item);
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item);
+ if (group) {
+ queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end());
+ }
+ }
+ filterAll();
+}
+
+void Roster::removeFilter(RosterFilter *filter) {
+ for (unsigned int i = 0; i < filters_.size(); i++) {
+ if (filters_[i] == filter) {
+ filters_.erase(filters_.begin() + i);
+ break;
+ }
+ }
+ filterAll();
+}
+
+
+void Roster::filterItem(RosterItem* rosterItem) {
+ ContactRosterItem *item = dynamic_cast<ContactRosterItem*>(rosterItem);
+ if (!item) {
+ return;
+ }
+ bool hide = true;
+ foreach (RosterFilter *filter, filters_) {
+ hide &= (*filter)(item);
+ }
+ filters_.size() > 0 && hide ? item->hide() : item->show();
+}
+
+void Roster::filterAll() {
+ std::deque<RosterItem*> queue(children_.begin(), children_.end());
+ while (!queue.empty()) {
+ RosterItem *item = *queue.begin();
+ queue.pop_front();
+ GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item);
+ if (group) {
+ queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end());
+ } else {
+ filterItem(item);
+ }
+ }
+}
+
+}
+
diff --git a/Swiften/Roster/Roster.h b/Swiften/Roster/Roster.h
new file mode 100644
index 0000000..6010832
--- /dev/null
+++ b/Swiften/Roster/Roster.h
@@ -0,0 +1,49 @@
+#ifndef SWIFTEN_Roster_H
+#define SWIFTEN_Roster_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Roster/RosterItemOperation.h"
+#include "Swiften/Roster/UserRosterAction.h"
+#include "Swiften/Roster/RosterFilter.h"
+
+#include <vector>
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class TreeWidgetFactory;
+class TreeWidget;
+class RosterItem;
+class GroupRosterItem;
+
+class Roster {
+ public:
+ Roster(TreeWidget *treeWidget, TreeWidgetFactory *widgetFactory);
+ ~Roster();
+
+ TreeWidget* getWidget();
+ GroupRosterItem* getGroup(const String& groupName);
+ void addContact(const JID& jid, const String& name, const String& group);
+ void removeContact(const JID& jid);
+ void removeContactFromGroup(const JID& jid, const String& group);
+ void applyOnItems(const RosterItemOperation& operation);
+ boost::signal<void (boost::shared_ptr<UserRosterAction>)> onUserAction;
+ void addFilter(RosterFilter *filter) {filters_.push_back(filter);filterAll();}
+ void removeFilter(RosterFilter *filter);
+ std::vector<RosterFilter*> getFilters() {return filters_;}
+
+ private:
+ void filterItem(RosterItem* item);
+ void filterAll();
+ void handleUserAction(boost::shared_ptr<UserRosterAction> action);
+ TreeWidget *treeWidget_;
+ TreeWidgetFactory *widgetFactory_;
+ std::vector<RosterItem*> children_;
+ std::vector<RosterItem*> items_;
+ std::vector<RosterFilter*> filters_;
+};
+}
+
+#endif
diff --git a/Swiften/Roster/RosterFilter.h b/Swiften/Roster/RosterFilter.h
new file mode 100644
index 0000000..a824304
--- /dev/null
+++ b/Swiften/Roster/RosterFilter.h
@@ -0,0 +1,17 @@
+#ifndef SWIFTEN_RosterFilter_H
+#define SWIFTEN_RosterFilter_H
+
+#include "Swiften/Roster/RosterItem.h"
+
+namespace Swift {
+
+class RosterFilter {
+ public:
+ virtual ~RosterFilter() {}
+ virtual bool operator() (RosterItem* item) const = 0;
+};
+
+}
+#endif
+
+
diff --git a/Swiften/Roster/RosterItem.h b/Swiften/Roster/RosterItem.h
new file mode 100644
index 0000000..2707920
--- /dev/null
+++ b/Swiften/Roster/RosterItem.h
@@ -0,0 +1,24 @@
+#ifndef SWIFTEN_RosterItem_H
+#define SWIFTEN_RosterItem_H
+
+#include "Swiften/Roster/UserRosterAction.h"
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class RosterItem {
+ public:
+ virtual ~RosterItem() {};
+ boost::signal<void (boost::shared_ptr<UserRosterAction>)> onUserAction;
+ protected:
+ void handleUserAction(boost::shared_ptr<UserRosterAction> action) {
+ action->setRosterItem(this);
+ onUserAction(action);
+ }
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/RosterItemOperation.h b/Swiften/Roster/RosterItemOperation.h
new file mode 100644
index 0000000..ea8e723
--- /dev/null
+++ b/Swiften/Roster/RosterItemOperation.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_RosterItemOperation_H
+#define SWIFTEN_RosterItemOperation_H
+
+#include "Swiften/Roster/RosterItem.h"
+
+namespace Swift {
+
+class RosterItemOperation {
+ public:
+ virtual ~RosterItemOperation() {}
+ virtual void operator() (RosterItem*) const = 0;
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/SetAvatar.h b/Swiften/Roster/SetAvatar.h
new file mode 100644
index 0000000..7bc9c37
--- /dev/null
+++ b/Swiften/Roster/SetAvatar.h
@@ -0,0 +1,33 @@
+#ifndef SWIFTEN_SetAvatar_H
+#define SWIFTEN_SetAvatar_H
+
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Roster/RosterItemOperation.h"
+#include "Swiften/Roster/ContactRosterItem.h"
+
+namespace Swift {
+
+class RosterItem;
+
+class SetAvatar : public RosterItemOperation {
+ public:
+ SetAvatar(const JID& jid, const String& path, JID::CompareType compareType = JID::WithoutResource) : jid_(jid), path_(path), compareType_(compareType) {
+ }
+
+ virtual void operator() (RosterItem* item) const {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);
+ if (contact && contact->getJID().equals(jid_, compareType_)) {
+ contact->setAvatarPath(path_);
+ }
+ }
+
+ private:
+ JID jid_;
+ String path_;
+ JID::CompareType compareType_;
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/SetPresence.h b/Swiften/Roster/SetPresence.h
new file mode 100644
index 0000000..a18ae6d
--- /dev/null
+++ b/Swiften/Roster/SetPresence.h
@@ -0,0 +1,38 @@
+#ifndef SWIFTEN_SetPresence_H
+#define SWIFTEN_SetPresence_H
+
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Roster/RosterItemOperation.h"
+#include "Swiften/Roster/ContactRosterItem.h"
+
+namespace Swift {
+
+class RosterItem;
+
+class SetPresence : public RosterItemOperation {
+ public:
+ SetPresence(boost::shared_ptr<Presence> presence, JID::CompareType compareType = JID::WithoutResource) : presence_(presence), compareType_(compareType) {
+ }
+
+ virtual void operator() (RosterItem* item) const {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);
+ if (contact && contact->getJID().equals(presence_->getFrom(), compareType_)) {
+ if (presence_->getType() != Presence::Available) {
+ contact->setStatusShow(StatusShow::None);
+ contact->setStatusText(presence_->getStatus());
+ } else {
+ contact->setStatusShow(presence_->getShow());
+ contact->setStatusText(presence_->getStatus());
+ }
+ }
+ }
+
+ private:
+ boost::shared_ptr<Presence> presence_;
+ JID::CompareType compareType_;
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/TreeWidget.h b/Swiften/Roster/TreeWidget.h
new file mode 100644
index 0000000..a26003e
--- /dev/null
+++ b/Swiften/Roster/TreeWidget.h
@@ -0,0 +1,13 @@
+#ifndef SWIFTEN_TreeWidget_H
+#define SWIFTEN_TreeWidget_H
+
+namespace Swift {
+
+class TreeWidget {
+ public:
+ virtual ~TreeWidget() {}
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/TreeWidgetFactory.h b/Swiften/Roster/TreeWidgetFactory.h
new file mode 100644
index 0000000..f4ba68d
--- /dev/null
+++ b/Swiften/Roster/TreeWidgetFactory.h
@@ -0,0 +1,20 @@
+#ifndef SWIFTEN_TreeWidgetFactory_H
+#define SWIFTEN_TreeWidgetFactory_H
+
+namespace Swift {
+
+class TreeWidgetItem;
+class TreeWidget;
+
+class TreeWidgetFactory {
+ public:
+ virtual ~TreeWidgetFactory() {}
+ virtual TreeWidget* createTreeWidget() = 0;
+ virtual TreeWidgetItem* createTreeWidgetItem(TreeWidgetItem* item) = 0;
+ virtual TreeWidgetItem* createTreeWidgetItem(TreeWidget* item) = 0;
+};
+
+}
+
+#endif
+
diff --git a/Swiften/Roster/TreeWidgetItem.h b/Swiften/Roster/TreeWidgetItem.h
new file mode 100644
index 0000000..4124546
--- /dev/null
+++ b/Swiften/Roster/TreeWidgetItem.h
@@ -0,0 +1,34 @@
+#ifndef SWIFTEN_TreeWidgetItem_H
+#define SWIFTEN_TreeWidgetItem_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Roster/UserRosterAction.h"
+#include "Swiften/Elements/StatusShow.h"
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class TreeWidgetItem {
+ public:
+ virtual ~TreeWidgetItem() {}
+ virtual void setText(const String& text) = 0;
+ virtual void setStatusText(const String& text) = 0;
+ virtual void setAvatarPath(const String& path) = 0;
+ virtual void setExpanded(bool b) = 0;
+ //virtual void setTextColor(unsigned long color) = 0;
+ virtual void setStatusShow(StatusShow::Type show) = 0;
+ //virtual void setBackgroundColor(unsigned long color) = 0;
+ boost::signal<void (boost::shared_ptr<UserRosterAction>)> onUserAction;
+ virtual void show() = 0;
+ virtual void hide() = 0;
+ void performUserAction(boost::shared_ptr<UserRosterAction> action) {
+ action->setTreeWidgetItem(this);
+ onUserAction(action);
+ }
+};
+
+}
+#endif
+
diff --git a/Swiften/Roster/UnitTest/MockTreeWidget.h b/Swiften/Roster/UnitTest/MockTreeWidget.h
new file mode 100644
index 0000000..e6f6def
--- /dev/null
+++ b/Swiften/Roster/UnitTest/MockTreeWidget.h
@@ -0,0 +1,14 @@
+#ifndef SWIFTEN_MockTreeWidget_H
+#define SWIFTEN_MockTreeWidget_H
+
+#include "Swiften/Roster/TreeWidget.h"
+
+namespace Swift {
+
+class MockTreeWidget : public TreeWidget {
+ public:
+ virtual ~MockTreeWidget() {}
+};
+
+}
+#endif
diff --git a/Swiften/Roster/UnitTest/MockTreeWidgetFactory.h b/Swiften/Roster/UnitTest/MockTreeWidgetFactory.h
new file mode 100644
index 0000000..b2b4f10
--- /dev/null
+++ b/Swiften/Roster/UnitTest/MockTreeWidgetFactory.h
@@ -0,0 +1,50 @@
+#ifndef SWIFTEN_MockTreeWidgetFactory_H
+#define SWIFTEN_MockTreeWidgetFactory_H
+
+#include "Swiften/Roster/TreeWidgetFactory.h"
+
+#include <vector>
+#include "Swiften/Base/foreach.h"
+#include "Swiften/Roster/UnitTest/MockTreeWidget.h"
+#include "Swiften/Roster/UnitTest/MockTreeWidgetItem.h"
+
+namespace Swift {
+
+class MockTreeWidgetItem;
+class MockTreeWidget;
+
+class MockTreeWidgetFactory : public TreeWidgetFactory {
+ public:
+ virtual ~MockTreeWidgetFactory() {}
+ virtual TreeWidget* createTreeWidget() {
+ root_ = new MockTreeWidget();
+ return root_;
+ };
+ virtual TreeWidgetItem* createTreeWidgetItem(TreeWidgetItem* group) {
+ MockTreeWidgetItem* entry = new MockTreeWidgetItem();
+ groupMembers_[group].push_back(entry);
+ return entry;
+ };
+ virtual TreeWidgetItem* createTreeWidgetItem(TreeWidget*) {
+ MockTreeWidgetItem* group = new MockTreeWidgetItem();
+ groups_.push_back(group);
+ return group;
+ };
+ virtual std::vector<String> getGroups() {
+ std::vector<String> groupNames;
+ foreach (MockTreeWidgetItem* group, groups_) {
+ groupNames.push_back(group->getText());
+ }
+ return groupNames;
+ };
+ private:
+ std::vector<MockTreeWidgetItem*> groups_;
+ std::map<TreeWidgetItem*, std::vector<MockTreeWidgetItem*> > groupMembers_;
+ MockTreeWidget* root_;
+};
+
+}
+
+#endif
+
+
diff --git a/Swiften/Roster/UnitTest/MockTreeWidgetItem.h b/Swiften/Roster/UnitTest/MockTreeWidgetItem.h
new file mode 100644
index 0000000..a40aca7
--- /dev/null
+++ b/Swiften/Roster/UnitTest/MockTreeWidgetItem.h
@@ -0,0 +1,30 @@
+#ifndef SWIFTEN_MockTreeWidgetItem_H
+#define SWIFTEN_MockTreeWidgetItem_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Roster/TreeWidgetItem.h"
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class MockTreeWidgetItem : public TreeWidgetItem {
+ public:
+ virtual ~MockTreeWidgetItem() {};
+ virtual void setText(const String& text) {text_ = text;};
+ String getText() {return text_;};
+ virtual void setStatusText(const String&) {};
+ virtual void setAvatarPath(const String&) {};
+ virtual void setExpanded(bool) {};
+ virtual void setStatusShow(StatusShow::Type /*show*/) {};
+ virtual void show() {};
+ virtual void hide() {};
+ private:
+ String text_;
+};
+
+}
+#endif
+
+
diff --git a/Swiften/Roster/UnitTest/OfflineRosterFilterTest.cpp b/Swiften/Roster/UnitTest/OfflineRosterFilterTest.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Swiften/Roster/UnitTest/OfflineRosterFilterTest.cpp
diff --git a/Swiften/Roster/UnitTest/RosterTest.cpp b/Swiften/Roster/UnitTest/RosterTest.cpp
new file mode 100644
index 0000000..b43a41c
--- /dev/null
+++ b/Swiften/Roster/UnitTest/RosterTest.cpp
@@ -0,0 +1,57 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Roster/Roster.h"
+#include "Swiften/Roster/UnitTest/MockTreeWidget.h"
+#include "Swiften/Roster/UnitTest/MockTreeWidgetFactory.h"
+#include "Swiften/Roster/UnitTest/MockTreeWidgetItem.h"
+
+using namespace Swift;
+
+class RosterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(RosterTest);
+ CPPUNIT_TEST(testGetGroup);
+ CPPUNIT_TEST_SUITE_END();
+
+ private:
+ Roster *roster_;
+ TreeWidget *widget_;
+ TreeWidgetFactory *factory_;
+ JID jid1_;
+ JID jid2_;
+ JID jid3_;
+
+ public:
+
+ RosterTest() : jid1_(JID("a@b.c")), jid2_(JID("b@c.d")), jid3_(JID("c@d.e")) {}
+
+ void setUp() {
+ factory_ = new MockTreeWidgetFactory();
+ widget_ = factory_->createTreeWidget();
+ roster_ = new Roster(widget_, factory_);
+ }
+
+ void tearDown() {
+ delete roster_;
+ //delete widget_;
+ delete factory_;
+ }
+
+ void testGetGroup() {
+ roster_->addContact(jid1_, "Bert", "group1");
+ roster_->addContact(jid2_, "Ernie", "group2");
+ roster_->addContact(jid3_, "Cookie", "group1");
+
+ CPPUNIT_ASSERT_EQUAL(roster_->getGroup("group1"), roster_->getGroup("group1"));
+ CPPUNIT_ASSERT_EQUAL(roster_->getGroup("group2"), roster_->getGroup("group2"));
+ CPPUNIT_ASSERT_EQUAL(roster_->getGroup("group3"), roster_->getGroup("group3"));
+ CPPUNIT_ASSERT(roster_->getGroup("group1") != roster_->getGroup("group2"));
+ CPPUNIT_ASSERT(roster_->getGroup("group2") != roster_->getGroup("group3"));
+ CPPUNIT_ASSERT(roster_->getGroup("group3") != roster_->getGroup("group1"));
+ }
+
+};
+CPPUNIT_TEST_SUITE_REGISTRATION(RosterTest);
+
diff --git a/Swiften/Roster/UnitTest/XMPPRosterTest.cpp b/Swiften/Roster/UnitTest/XMPPRosterTest.cpp
new file mode 100644
index 0000000..d03953e
--- /dev/null
+++ b/Swiften/Roster/UnitTest/XMPPRosterTest.cpp
@@ -0,0 +1,164 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include <vector>
+
+#include "Swiften/Roster/XMPPRoster.h"
+
+
+using namespace Swift;
+
+enum XMPPRosterEvents {None, Add, Remove, Update};
+
+class XMPPRosterSignalHandler {
+public:
+ XMPPRosterSignalHandler(XMPPRoster* roster) {
+ lastEvent_ = None;
+ roster->onJIDAdded.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDAdded, this, _1));
+ roster->onJIDRemoved.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDRemoved, this, _1));
+ roster->onJIDUpdated.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDUpdated, this, _1, _2, _3));
+ }
+
+ XMPPRosterEvents getLastEvent() {
+ return lastEvent_;
+ }
+
+ JID getLastJID() {
+ return lastJID_;
+ }
+
+ String getLastOldName() {
+ return lastOldName_;
+ }
+
+ std::vector<String> getLastOldGroups() {
+ return lastOldGroups_;
+ }
+
+ void reset() {
+ lastEvent_ = None;
+ }
+
+private:
+ void handleJIDAdded(const JID& jid) {
+ lastJID_ = jid;
+ lastEvent_ = Add;
+ }
+
+ void handleJIDRemoved(const JID& jid) {
+ lastJID_ = jid;
+ lastEvent_ = Remove;
+ }
+
+ void handleJIDUpdated(const JID& jid, const String& oldName, const std::vector<String>& oldGroups) {
+ CPPUNIT_ASSERT_EQUAL(None, lastEvent_);
+ lastJID_ = jid;
+ lastOldName_ = oldName;
+ lastOldGroups_ = oldGroups;
+ lastEvent_ = Update;
+ }
+
+ XMPPRosterEvents lastEvent_;
+ JID lastJID_;
+ String lastOldName_;
+ std::vector<String> lastOldGroups_;
+
+};
+
+class XMPPRosterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(XMPPRosterTest);
+ CPPUNIT_TEST(testJIDAdded);
+ CPPUNIT_TEST(testJIDRemoved);
+ CPPUNIT_TEST(testJIDUpdated);
+ CPPUNIT_TEST_SUITE_END();
+
+ private:
+ XMPPRoster* roster_;
+ XMPPRosterSignalHandler* handler_;
+ JID jid1_;
+ JID jid2_;
+ JID jid3_;
+ std::vector<String> groups1_;
+ std::vector<String> groups2_;
+
+
+ public:
+
+ XMPPRosterTest() : jid1_(JID("a@b.c")), jid2_(JID("b@c.d")), jid3_(JID("c@d.e")) {}
+
+ void setUp() {
+ roster_ = new XMPPRoster();
+ handler_ = new XMPPRosterSignalHandler(roster_);
+ groups1_.push_back("bobs");
+ groups1_.push_back("berts");
+ groups2_.push_back("ernies");
+ }
+
+ void tearDown() {
+ delete roster_;
+ }
+
+ void testJIDAdded() {
+ roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
+ CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
+ handler_->reset();
+ roster_->addContact(jid2_, "NameTwo", groups1_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NameTwo"), roster_->getNameForJID(jid2_));
+ CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
+ CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid2_));
+ CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
+ handler_->reset();
+ roster_->addContact(jid3_, "NewName", groups2_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid3_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid3_));
+ CPPUNIT_ASSERT(groups2_ == roster_->getGroupsForJID(jid3_));
+ }
+
+ void testJIDRemoved() {
+ roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
+ handler_->reset();
+ roster_->removeContact(jid1_);
+ CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ handler_->reset();
+ roster_->addContact(jid1_, "NewName2", groups1_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NewName2"), roster_->getNameForJID(jid1_));
+ roster_->addContact(jid2_, "NewName3", groups1_, RosterItemPayload::Both);
+ handler_->reset();
+ roster_->removeContact(jid2_);
+ CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID());
+ handler_->reset();
+ roster_->removeContact(jid1_);
+ CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ }
+
+ void testJIDUpdated() {
+ roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
+ CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
+ handler_->reset();
+ roster_->addContact(jid1_, "NameTwo", groups2_, RosterItemPayload::Both);
+ CPPUNIT_ASSERT_EQUAL(Update, handler_->getLastEvent());
+ CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+ CPPUNIT_ASSERT_EQUAL(String("NameTwo"), roster_->getNameForJID(jid1_));
+ CPPUNIT_ASSERT(groups2_ == roster_->getGroupsForJID(jid1_));
+ }
+
+};
+CPPUNIT_TEST_SUITE_REGISTRATION(XMPPRosterTest);
+
diff --git a/Swiften/Roster/UserRosterAction.h b/Swiften/Roster/UserRosterAction.h
new file mode 100644
index 0000000..80ace68
--- /dev/null
+++ b/Swiften/Roster/UserRosterAction.h
@@ -0,0 +1,32 @@
+#ifndef SWIFTEN_UserRosterAction_H
+#define SWIFTEN_UserRosterAction_H
+
+namespace Swift {
+class RosterItem;
+class TreeWidgetItem;
+
+class UserRosterAction {
+ public:
+ virtual ~UserRosterAction() {};
+ void setRosterItem(RosterItem *item) {
+ rosterItem_ = item;
+ };
+ void setTreeWidgetItem(TreeWidgetItem *item) {
+ treeWidgetItem_ = item;
+ }
+ RosterItem* getRosterItem() {
+ return rosterItem_;
+ }
+ TreeWidgetItem* getTreeWidgetItem() {
+ return treeWidgetItem_;
+ }
+
+ private:
+ RosterItem *rosterItem_;
+ TreeWidgetItem *treeWidgetItem_;
+};
+
+}
+#endif
+
+
diff --git a/Swiften/Roster/XMPPRoster.cpp b/Swiften/Roster/XMPPRoster.cpp
new file mode 100644
index 0000000..62edc45
--- /dev/null
+++ b/Swiften/Roster/XMPPRoster.cpp
@@ -0,0 +1,48 @@
+#include "Swiften/Roster/XMPPRoster.h"
+
+namespace Swift {
+
+void XMPPRoster::addContact(const JID& jid, const String& name, const std::vector<String>& groups, RosterItemPayload::Subscription subscription) {
+ JID bareJID(jid.toBare());
+ bool exists = containsJID(bareJID);
+ String oldName = getNameForJID(bareJID);
+ std::vector<String> oldGroups = entries_[bareJID].groups;
+ if (exists) {
+ entries_.erase(bareJID);
+ }
+ XMPPRosterItem item;
+ item.groups = groups;
+ item.name = name;
+ item.jid = jid;
+ item.subscription = subscription;
+ entries_[bareJID] = item;
+ if (exists) {
+ onJIDUpdated(bareJID, oldName, oldGroups);
+ } else {
+ onJIDAdded(bareJID);
+ }
+}
+
+void XMPPRoster::removeContact(const JID& jid) {
+ entries_.erase(JID(jid.toBare()));
+ onJIDRemoved(jid);
+}
+
+bool XMPPRoster::containsJID(const JID& jid) {
+ return entries_.find(JID(jid.toBare())) != entries_.end();
+}
+
+const String& XMPPRoster::getNameForJID(const JID& jid) {
+ return entries_[JID(jid.toBare())].name;
+}
+
+const std::vector<String>& XMPPRoster::getGroupsForJID(const JID& jid) {
+ return entries_[JID(jid.toBare())].groups;
+}
+
+RosterItemPayload::Subscription XMPPRoster::getSubscriptionStateForJID(const JID& jid) {
+ return entries_[JID(jid.toBare())].subscription;
+}
+
+}
+
diff --git a/Swiften/Roster/XMPPRoster.h b/Swiften/Roster/XMPPRoster.h
new file mode 100644
index 0000000..47326c3
--- /dev/null
+++ b/Swiften/Roster/XMPPRoster.h
@@ -0,0 +1,44 @@
+#ifndef SWIFTEN_XMPPRoster_H
+#define SWIFTEN_XMPPRoster_H
+
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/RosterItemPayload.h"
+
+#include <map>
+#include <vector>
+#include <boost/signal.hpp>
+
+namespace Swift {
+
+struct XMPPRosterItem {
+ JID jid;
+ String name;
+ std::vector<String> groups;
+ RosterItemPayload::Subscription subscription;
+};
+
+class XMPPRoster {
+ public:
+ XMPPRoster() {};
+ ~XMPPRoster() {};
+
+ void addContact(const JID& jid, const String& name, const std::vector<String>& groups, const RosterItemPayload::Subscription subscription);
+ bool containsJID(const JID& jid);
+ void removeContact(const JID& jid);
+ RosterItemPayload::Subscription getSubscriptionStateForJID(const JID& jid);
+ const String& getNameForJID(const JID& jid);
+ const std::vector<String>& getGroupsForJID(const JID& jid);
+
+ boost::signal<void (const JID&)> onJIDAdded;
+ boost::signal<void (const JID&)> onJIDRemoved;
+ boost::signal<void (const JID&, const String&, const std::vector<String>&)> onJIDUpdated;
+
+ private:
+ //std::map<JID, std::pair<String, std::vector<String> > > entries_;
+ std::map<JID, XMPPRosterItem> entries_;
+};
+}
+
+#endif
+