From 205c3163ad207b7a95d8a80799c4e538851f0ce7 Mon Sep 17 00:00:00 2001
From: Florian Zeitz <florob@babelmonkeys.de>
Date: Tue, 29 Jan 2013 19:25:22 +0100
Subject: [PATCH] Adapt to use XMPPContactManager

---
 data/gtk/chat.ui             |   1 +
 src/core/JubChatClient.h     |   2 +
 src/core/JubChatClient.m     |   6 ++
 src/gui/gtk/JubGtkChatUI.m   |  12 ++-
 src/gui/gtk/JubGtkRosterUI.h |   4 +-
 src/gui/gtk/JubGtkRosterUI.m | 163 ++++++++++++++++++-----------------
 6 files changed, 103 insertions(+), 85 deletions(-)

diff --git a/data/gtk/chat.ui b/data/gtk/chat.ui
index 8c352cd..bc23241 100644
--- a/data/gtk/chat.ui
+++ b/data/gtk/chat.ui
@@ -193,6 +193,7 @@
           <object class="GtkEntry" id="ChatEntry">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
+            <property name="has_focus">True</property>
             <property name="invisible_char">●</property>
           </object>
           <packing>
diff --git a/src/core/JubChatClient.h b/src/core/JubChatClient.h
index a73082d..4f6205b 100644
--- a/src/core/JubChatClient.h
+++ b/src/core/JubChatClient.h
@@ -9,11 +9,13 @@
 	XMPPConnection *connection;
 	XMPPRoster *roster;
 	XMPPStreamManagement *streamManagement;
+	XMPPContactManager *contactManager;
 	XMPPPresence *presence;
 	id<JubUI> ui;
 }
 @property (readonly) XMPPConnection *connection;
 @property (readonly) XMPPRoster *roster;
+@property (readonly) XMPPContactManager *contactManager;
 @property (readonly) XMPPPresence *presence;
 @property (assign) id<JubUI> ui;
 
diff --git a/src/core/JubChatClient.m b/src/core/JubChatClient.m
index 4efa894..b4a1481 100644
--- a/src/core/JubChatClient.m
+++ b/src/core/JubChatClient.m
@@ -3,6 +3,7 @@
 @implementation JubChatClient
 @synthesize connection;
 @synthesize roster;
+@synthesize contactManager;
 @synthesize presence;
 @synthesize ui;
 
@@ -22,6 +23,10 @@
 		roster = [[XMPPRoster alloc] initWithConnection: connection];
 		[roster addDelegate: self];
 
+		contactManager = [[XMPPContactManager alloc]
+				      initWithConnection: connection
+						  roster: roster];
+
 		streamManagement = [[XMPPStreamManagement alloc]
 		    initWithConnection: connection];
 
@@ -37,6 +42,7 @@
 - (void)dealloc
 {
 	[roster release];
+	[contactManager release];
 	[streamManagement release];
 	[connection release];
 	[presence release];
diff --git a/src/gui/gtk/JubGtkChatUI.m b/src/gui/gtk/JubGtkChatUI.m
index 8fc633e..7db4aef 100644
--- a/src/gui/gtk/JubGtkChatUI.m
+++ b/src/gui/gtk/JubGtkChatUI.m
@@ -15,10 +15,14 @@ static gboolean call_send_block(GtkEntry *entry, GdkEventKey *event,
 
 	OFString *text =
 	    [[OFString alloc] initWithUTF8String: gtk_entry_get_text(entry)];
-	gtk_entry_set_text(entry, "");
-	params->block(text);
-	[params->chat addMessage: text
-			  sender: @"You"];
+
+	if ([text length] > 0) {
+		gtk_entry_set_text(entry, "");
+		params->block(text);
+		[params->chat addMessage: text
+				  sender: @"You"];
+	}
+
 	[text release];
 
 	return TRUE;
diff --git a/src/gui/gtk/JubGtkRosterUI.h b/src/gui/gtk/JubGtkRosterUI.h
index ec94383..4b2e50a 100644
--- a/src/gui/gtk/JubGtkRosterUI.h
+++ b/src/gui/gtk/JubGtkRosterUI.h
@@ -6,7 +6,7 @@
 
 @class JubGtkChatUI;
 
-@interface JubGtkRosterUI: OFObject <XMPPRosterDelegate, XMPPConnectionDelegate>
+@interface JubGtkRosterUI: OFObject <XMPPContactManagerDelegate>
 {
 	GtkWidget *roster_window;
 	GtkTreeStore *roster_model;
@@ -17,7 +17,7 @@
 	OFMutableDictionary *contactMap;
 	OFMutableDictionary *chatMap;
 	OFCountedSet *presences;
-	XMPPConnection *connection;
+	JubChatClient *client;
 }
 
 - initWithClient: (JubChatClient*)client;
diff --git a/src/gui/gtk/JubGtkRosterUI.m b/src/gui/gtk/JubGtkRosterUI.m
index ad54fd6..5826aa1 100644
--- a/src/gui/gtk/JubGtkRosterUI.m
+++ b/src/gui/gtk/JubGtkRosterUI.m
@@ -81,7 +81,7 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 }
 
 @implementation JubGtkRosterUI
-- initWithClient: (JubChatClient*)client;
+- initWithClient: (JubChatClient*)client_
 {
 	self = [super init];
 
@@ -95,10 +95,9 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 		contactMap = [[OFMutableDictionary alloc] init];
 		chatMap = [[OFMutableDictionary alloc] init];
 		presences = [[OFCountedSet alloc] init];
-		connection = [client.connection retain];
+		client = [client_ retain];
 
-		[connection addDelegate: self];
-		[client.roster addDelegate: self];
+		[client.contactManager addDelegate: self];
 
 		builder = gtk_builder_new();
 		gtk_builder_add_from_file(builder, "data/gtk/roster.ui", NULL);
@@ -129,7 +128,7 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 
 		presence_combo_changed_handler_id =
 		    g_signal_connect(presence_combo, "changed",
-			G_CALLBACK(presence_changed), connection);
+			G_CALLBACK(presence_changed), client.connection);
 
 		g_object_unref(G_OBJECT(builder));
 	} @catch (id e) {
@@ -142,31 +141,18 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 
 - (void)dealloc
 {
+	[client.contactManager removeDelegate: self];
 	[groupMap release];
 	[contactMap release];
 	[chatMap release];
 	[presences release];
-	[connection release];
+	[client release];
 
 	gtk_widget_destroy(roster_window);
 
 	[super dealloc];
 }
 
-/* Presence handling */
--   (void)connection: (XMPPConnection*)connection
-  didReceivePresence: (XMPPPresence*)presence
-{
-	if ([presence.type isEqual: @"available"])
-		[presences addObject: [presence.from bareJID]];
-	else if ([presence.type isEqual: @"unavailable"])
-		[presences removeObject: [presence.from bareJID]];
-
-	g_idle_add_block(^{
-		gtk_tree_model_filter_refilter(roster_filter);
-	});
-}
-
 // FIXME: This needs to move somewhere else
 - (JubGtkChatUI*)chatForJID: (XMPPJID*)jid
 {
@@ -187,7 +173,7 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 				    [XMPPMessage messageWithType: @"chat"];
 				msg.to = jid;
 				msg.body = text;
-				[connection sendStanza: msg];
+				[client.connection sendStanza: msg];
 			}
 		] autorelease];
 
@@ -200,9 +186,12 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 	return chat;
 }
 
--  (void)connection: (XMPPConnection*)connection
-  didReceiveMessage: (XMPPMessage*)message
+-  (void)contact: (XMPPContact*)contact
+  didSendMessage: (XMPPMessage*)message
 {
+	if (message.body == nil || ![message.type isEqual: @"chat"])
+		return;
+
 	JubGtkChatUI *chat = [self chatForJID: message.from];
 	[chat addMessage: message.body
 		  sender: [message.from bareJID]];
@@ -216,8 +205,17 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 		GtkTreeIter group_iter, contact_iter;
 		GtkTreeRowReference *group_ref, *contact_ref;
 		GtkTreePath *group_path, *contact_path;
-		OFMapTable *contactRows =
-		    [contactMap objectForKey: [item.JID bareJID]];
+		OFString *bareJID = [item.JID bareJID];
+		OFMapTable *contactRows;
+
+		if (!(contactRows = [contactMap objectForKey: bareJID])) {
+			contactRows = [OFMapTable
+			    mapTableWithKeyFunctions: keyFunctions
+				      valueFunctions: rowRefFunctions];
+
+			[contactMap setObject: contactRows
+				       forKey: bareJID];
+		}
 
 		group_ref = [groupMap valueForKey: group];
 
@@ -249,11 +247,11 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 		if (item.name)
 			gtk_tree_store_set(roster_model, &contact_iter,
 			    0, [item.name UTF8String],
-			    1, [[item.JID bareJID] UTF8String], -1);
+			    1, [bareJID UTF8String], -1);
 		else
 			gtk_tree_store_set(roster_model, &contact_iter,
 			    0, [item.JID.node UTF8String],
-			    1, [[item.JID bareJID] UTF8String], -1);
+			    1, [bareJID UTF8String], -1);
 
 		contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
 			roster_model), &contact_iter);
@@ -275,8 +273,8 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 		GtkTreeIter contact_iter, group_iter;
 		GtkTreePath *contact_path, *group_path;
 		GtkTreeRowReference *contact_ref, *group_ref;
-		OFMapTable *contactRows =
-		    [contactMap objectForKey: [item.JID bareJID]];
+		OFString *bareJID = [item.JID bareJID];
+		OFMapTable *contactRows = [contactMap objectForKey: bareJID];
 
 		contact_ref = [contactRows valueForKey: group];
 		contact_path = gtk_tree_row_reference_get_path(contact_ref);
@@ -297,73 +295,80 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 		}
 
 		gtk_tree_path_free(group_path);
+
+		[contactRows removeValueForKey: group];
+		if([contactRows count] == 0)
+			[contactMap removeObjectForKey: bareJID];
 	});
 }
 
-- (void)rosterWasReceived: (XMPPRoster*)roster_
+- (void)contactManager: (XMPPContactManager*)manager
+	 didAddContact: (XMPPContact*)contact
 {
-	of_log(@"Handling roster");
-	[[roster_ rosterItems] enumerateKeysAndObjectsUsingBlock:
-	    ^(OFString *bareJID, XMPPRosterItem *item, BOOL *stop) {
-		OFArray *groups;
-		OFMapTable *contactRows = [OFMapTable
-		    mapTableWithKeyFunctions: keyFunctions
-			      valueFunctions: rowRefFunctions];
-
-		[contactMap setObject: contactRows
-			       forKey: bareJID];
-
-		if (item.groups != nil)
-			groups = item.groups;
-		else
-			groups = @[@"General"];
+	XMPPRosterItem *rosterItem = contact.rosterItem;
+	OFArray *groups = rosterItem.groups;;
+
+	if (groups == nil)
+		groups = @[ @"General" ];
 
-		for (OFString *group in groups)
-			[self Jub_addRosterItem: item
-					  group: group];
-	}];
+	for (OFString *group in groups)
+		[self Jub_addRosterItem: rosterItem
+				  group: group];
 }
 
--         (void)roster: (XMPPRoster*)roster_
-  didReceiveRosterItem: (XMPPRosterItem*)item
+- (void)contactManager: (XMPPContactManager*)manager
+      didRemoveContact: (XMPPContact*)contact
 {
-	OFArray *groups;
-	XMPPRosterItem *oldItem =
-	    [roster_.rosterItems objectForKey: [item.JID bareJID]];
+	XMPPRosterItem *rosterItem = contact.rosterItem;
+	OFArray *groups = rosterItem.groups;
 
-	if (oldItem) {
-		if (oldItem.groups != nil)
-			groups = oldItem.groups;
-		else
-			groups = @[@"General"];
+	if (groups == nil)
+		groups = @[ @"General" ];
 
-		for (OFString *group in groups)
-			[self Jub_removeRosterItem: oldItem
-					     group: group];
+	for (OFString *group in groups)
+		[self Jub_removeRosterItem: rosterItem
+				     group: group];
+}
 
-		[contactMap removeObjectForKey: [item.JID bareJID]];
-	}
+-	     (void)contact: (XMPPContact*)contact
+  willUpdateWithRosterItem: (XMPPRosterItem*)rosterItem;
+{
+	// Remove contact from old set of groups
+	XMPPRosterItem *oldItem = contact.rosterItem;
+	OFArray *groups = oldItem.groups;
 
-	if (![item.subscription isEqual: @"remove"]) {
-		OFMapTable *contactRows = [OFMapTable
-		    mapTableWithKeyFunctions: keyFunctions
-			      valueFunctions: rowRefFunctions];
+	if (groups == nil)
+		groups = @[ @"General" ];
 
-		[contactMap setObject: contactRows
-			       forKey: [item.JID bareJID]];
+	for (OFString *group in groups)
+		[self Jub_removeRosterItem: oldItem
+				     group: group];
 
-		if (item.groups != nil)
-			groups = item.groups;
-		else
-			groups = @[@"General"];
+	// Add contact to new set of groups
+	groups = rosterItem.groups;
 
-		for (OFString *group in groups)
-			[self Jub_addRosterItem: item
-					  group: group];
-	}
+	if (groups == nil)
+		groups = @[ @"General" ];
+
+	for (OFString *group in groups)
+		[self Jub_addRosterItem: rosterItem
+				  group: group];
+}
+
+-   (void)contact: (XMPPContact*)contact
+  didSendPresence: (XMPPPresence*)presence
+{
+	if ([presence.type isEqual: @"available"])
+		[presences addObject: [presence.from bareJID]];
+	else if ([presence.type isEqual: @"unavailable"])
+		[presences removeObject: [presence.from bareJID]];
+
+	g_idle_add_block(^{
+		gtk_tree_model_filter_refilter(roster_filter);
+	});
 }
 
--      (void)client: (JubChatClient*)client
+-      (void)client: (JubChatClient*)client_
   didChangePresence: (XMPPPresence*)presence
 {
 	OFString *tooltip = @"";
-- 
2.39.5