? edit_and_rename_groups.patch3
? edit_and_rename_groups.patch4
? subscription.patch2
? subscription.patch3
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gossip/ChangeLog,v
retrieving revision 1.455
diff -u -b -B -p -r1.455 ChangeLog
--- ChangeLog	1 May 2005 06:27:48 -0000	1.455
+++ ChangeLog	1 May 2005 13:58:25 -0000
@@ -148,7 +148,7 @@
 
 	* autogen.sh: Use automake 1.9
 
-2005-04-08  Martyn Russel  <mr@gnome.org>
+2005-04-08  Martyn Russell  <mr@gnome.org>
 
 	* src/gossip-cell-renderer-text.c:
 	(cell_renderer_text_update_text):
Index: libgossip/gossip-contact.c
===================================================================
RCS file: /cvs/gnome/gossip/libgossip/gossip-contact.c,v
retrieving revision 1.12
diff -u -b -B -p -r1.12 gossip-contact.c
--- libgossip/gossip-contact.c	24 Apr 2005 22:33:14 -0000	1.12
+++ libgossip/gossip-contact.c	1 May 2005 13:58:25 -0000
@@ -514,7 +514,6 @@ gossip_contact_set_groups (GossipContact
 	GList             *l;
 
 	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), FALSE);
-	g_return_val_if_fail (groups != NULL, FALSE);
 	
 	priv = GET_PRIV (contact);
 
Index: libgossip/gossip-protocol.c
===================================================================
RCS file: /cvs/gnome/gossip/libgossip/gossip-protocol.c,v
retrieving revision 1.7
diff -u -b -B -p -r1.7 gossip-protocol.c
--- libgossip/gossip-protocol.c	30 Apr 2005 22:15:16 -0000	1.7
+++ libgossip/gossip-protocol.c	1 May 2005 13:58:26 -0000
@@ -59,7 +59,11 @@ gossip_protocol_class_init (GossipProtoc
 	klass->set_presence        = NULL;
         klass->add_contact         = NULL;
         klass->rename_contact      = NULL;
+        klass->remove_contact      = NULL;
+	klass->update_contact      = NULL;
+	klass->rename_group        = NULL;
 	klass->get_active_resource = NULL;
+	klass->get_groups          = NULL;
 	klass->async_get_vcard     = NULL;
 	klass->async_set_vcard     = NULL;
 
@@ -314,6 +318,35 @@ gossip_protocol_get_contacts (GossipProt
 	return NULL;
 }
 
+void
+gossip_protocol_update_contact (GossipProtocol *protocol,
+                                GossipContact  *contact)
+{
+	GossipProtocolClass *klass;
+
+	g_return_if_fail (GOSSIP_IS_PROTOCOL (protocol));
+
+	klass = GOSSIP_PROTOCOL_GET_CLASS (protocol);
+	if (klass->update_contact) {
+		klass->update_contact (protocol, contact);
+	}
+}
+
+void
+gossip_protocol_rename_group (GossipProtocol *protocol,
+			      const gchar    *group,
+			      const gchar    *new_name)
+{
+	GossipProtocolClass *klass;
+
+	g_return_if_fail (GOSSIP_IS_PROTOCOL (protocol));
+
+	klass = GOSSIP_PROTOCOL_GET_CLASS (protocol);
+	if (klass->rename_group) {
+		klass->rename_group (protocol, group, new_name);
+	}
+}
+
 const gchar * 
 gossip_protocol_get_active_resource (GossipProtocol *protocol,
 				     GossipContact  *contact)
@@ -325,6 +358,21 @@ gossip_protocol_get_active_resource (Gos
 	klass = GOSSIP_PROTOCOL_GET_CLASS (protocol);
 	if (klass->get_active_resource) {
 		return klass->get_active_resource (protocol, contact);
+	}
+
+	return NULL;
+}
+
+GList * 
+gossip_protocol_get_groups (GossipProtocol *protocol)
+{
+	GossipProtocolClass *klass;
+
+	g_return_val_if_fail (GOSSIP_IS_PROTOCOL (protocol), NULL);
+
+	klass = GOSSIP_PROTOCOL_GET_CLASS (protocol);
+	if (klass->get_groups) {
+		return klass->get_groups (protocol);
 	}
 
 	return NULL;
Index: libgossip/gossip-protocol.h
===================================================================
RCS file: /cvs/gnome/gossip/libgossip/gossip-protocol.h,v
retrieving revision 1.5
diff -u -b -B -p -r1.5 gossip-protocol.h
--- libgossip/gossip-protocol.h	30 Apr 2005 11:43:51 -0000	1.5
+++ libgossip/gossip-protocol.h	1 May 2005 13:58:26 -0000
@@ -73,9 +73,16 @@ struct _GossipProtocolClass {
                                                 const gchar    *new_name);
         void          (*remove_contact)        (GossipProtocol *protocol,
                                                 GossipContact  *contact);
+        void          (*update_contact)        (GossipProtocol *protocol,
+						GossipContact  *contact);
+        void          (*rename_group)          (GossipProtocol *protocol,
+						const gchar    *group,
+						const gchar    *new_name);
 	const GList * (*get_contacts)          (GossipProtocol *protocol);
 	const gchar * (*get_active_resource)  (GossipProtocol *protocol,
 					       GossipContact  *contact);
+ 	GList *       (*get_groups)            (GossipProtocol *protocol);
+
 
 	gboolean      (*async_register)       (GossipProtocol *protocol,
 					       GossipAccount  *account,
@@ -127,8 +134,16 @@ void          gossip_protocol_rename_con
 void          gossip_protocol_remove_contact (GossipProtocol *protocol,
                                               GossipContact  *contact);
 const GList * gossip_protocol_get_contacts   (GossipProtocol *protocol);
-const gchar *gossip_protocol_get_active_resource (GossipProtocol               *protocol,
+
+void          gossip_protocol_update_contact (GossipProtocol *protocol,
+  					      GossipContact  *contact);
+void          gossip_protocol_rename_group   (GossipProtocol *protocol,
+					      const gchar    *group,
+					      const gchar    *new_name);
+
+ const gchar *gossip_protocol_get_active_resource (GossipProtocol *protocol,
 					      GossipContact  *contact);
+ GList *      gossip_protocol_get_groups          (GossipProtocol *protocol);
 
 
 /* Async functions */
Index: libgossip/gossip-session.c
===================================================================
RCS file: /cvs/gnome/gossip/libgossip/gossip-session.c,v
retrieving revision 1.13
diff -u -b -B -p -r1.13 gossip-session.c
--- libgossip/gossip-session.c	30 Apr 2005 22:15:16 -0000	1.13
+++ libgossip/gossip-session.c	1 May 2005 13:58:27 -0000
@@ -655,7 +655,42 @@ void
 gossip_session_update_contact (GossipSession *session,
                                GossipContact *contact)
 {
-        /* FIXME: Implement */
+	GossipSessionPriv *priv;
+	GossipProtocol    *protocol;
+
+	g_return_if_fail (GOSSIP_IS_SESSION (session));
+        g_return_if_fail (contact != NULL);
+	
+	priv = GET_PRIV (session);
+
+	protocol = session_get_protocol (session, NULL);
+	if (!protocol) {
+		return;
+	}
+	
+        gossip_protocol_update_contact (protocol, contact);
+}
+
+void
+gossip_session_rename_group (GossipSession *session,
+			     const gchar   *group,
+			     const gchar   *new_name)
+{
+	GossipSessionPriv *priv;
+	GossipProtocol    *protocol;
+
+	g_return_if_fail (GOSSIP_IS_SESSION (session));
+        g_return_if_fail (group != NULL);
+        g_return_if_fail (new_name != NULL);
+	
+	priv = GET_PRIV (session);
+
+	protocol = session_get_protocol (session, NULL);
+	if (!protocol) {
+		return;
+	}
+	
+        gossip_protocol_rename_group (protocol, group, new_name);
 }
 
 const GList *
@@ -673,9 +708,23 @@ gossip_session_get_contacts (GossipSessi
 GList *
 gossip_session_get_groups (GossipSession *session)
 {
-	/* FIXME: Implement */
+	GossipSessionPriv *priv;
+	GossipProtocol    *protocol;
 	
+	g_return_val_if_fail (GOSSIP_IS_SESSION (session), NULL);
+
+	priv = GET_PRIV (session);
+
+	protocol = session_get_protocol (session, NULL);
+	if (!protocol) {
 	return NULL;
+	}
+
+	/* FIXME: currently this will only use the session protocol,
+	   but if there were 3 protocols running we should really call
+	   this to all each one and merge the list */
+
+	return gossip_protocol_get_groups (protocol);
 }
 
 const gchar *
Index: libgossip/gossip-session.h
===================================================================
RCS file: /cvs/gnome/gossip/libgossip/gossip-session.h,v
retrieving revision 1.6
diff -u -b -B -p -r1.6 gossip-session.h
--- libgossip/gossip-session.h	30 Apr 2005 15:06:42 -0000	1.6
+++ libgossip/gossip-session.h	1 May 2005 13:58:27 -0000
@@ -98,8 +98,10 @@ void             gossip_session_remove_c
                                                 GossipContact *contact);
 void             gossip_session_update_contact (GossipSession *session,
                                                 GossipContact *contact);
+void             gossip_session_rename_group   (GossipSession *session,
+						const gchar   *group,
+						const gchar   *new_name);
 
-/* Add, remove, move */
 const GList *   gossip_session_get_contacts   (GossipSession  *session);
 GList *         gossip_session_get_groups     (GossipSession  *session);
 
Index: protocols/jabber/gossip-jabber.c
===================================================================
RCS file: /cvs/gnome/gossip/protocols/jabber/gossip-jabber.c,v
retrieving revision 1.25
diff -u -b -B -p -r1.25 gossip-jabber.c
--- protocols/jabber/gossip-jabber.c	30 Apr 2005 22:15:17 -0000	1.25
+++ protocols/jabber/gossip-jabber.c	1 May 2005 13:58:29 -0000
@@ -81,6 +81,15 @@ typedef struct {
 	gpointer               user_data;
 } RegisterAccountData;
 
+
+typedef struct {
+	GossipJabber          *jabber;
+	gchar                 *group;
+	gchar                 *new_name;
+} RenameGroupData;
+
+
+
 static void            gossip_jabber_class_init              (GossipJabberClass            *klass);
 static void            gossip_jabber_init                    (GossipJabber                 *jabber);
 static void            jabber_finalize                       (GObject                      *obj);
@@ -117,9 +126,21 @@ static void            jabber_rename_con
 							      const gchar                  *new_name);
 static void            jabber_remove_contact                 (GossipProtocol               *protocol,
 							      GossipContact                *contact);
+static void            jabber_update_contact                 (GossipProtocol               *protocol,
+ 							      GossipContact                *contact);
+static void            jabber_rename_group                   (GossipProtocol               *protocol,
+ 							      const gchar                  *group,
+ 							      const gchar                  *new_name);
+static void            jabber_rename_group_foreach_cb        (const gchar                  *jid,
+ 							      GossipContact                *contact,
+ 							      RenameGroupData              *rg);
 static const GList *   jabber_get_contacts                   (GossipProtocol               *protocol);
 static const gchar *   jabber_get_active_resource            (GossipProtocol               *protocol,
 							      GossipContact                *contact);
+static GList *         jabber_get_groups                     (GossipProtocol               *protocol);
+static void            jabber_get_groups_foreach_cb          (const gchar                  *jid,
+ 							      GossipContact                *contact,
+ 							      GList                       **list);
 static gboolean        jabber_async_get_vcard                (GossipProtocol               *protocol,
 							      GossipContact                *contact,
 							      GossipAsyncVCardCallback      callback,
@@ -147,8 +168,6 @@ static LmSSLResponse   jabber_ssl_func  
 static void            jabber_disconnect_func                (LmConnection                 *connection,
 							      LmDisconnectReason            reason,
 							      GossipJabber                 *jabber);
-static gboolean        jabber_update_contact                 (GossipContact                *contact,
-							      LmMessageNode                *node);
 static LmHandlerResult jabber_message_handler                (LmMessageHandler             *handler,
 							      LmConnection                 *conn,
 							      LmMessage                    *message,
@@ -218,8 +237,11 @@ gossip_jabber_class_init (GossipJabberCl
         protocol_class->add_contact         = jabber_add_contact;
         protocol_class->rename_contact      = jabber_rename_contact;
         protocol_class->remove_contact      = jabber_remove_contact;
+	protocol_class->update_contact      = jabber_update_contact;
+ 	protocol_class->rename_group        = jabber_rename_group;
 	protocol_class->get_contacts        = jabber_get_contacts;
 	protocol_class->get_active_resource = jabber_get_active_resource;
+ 	protocol_class->get_groups          = jabber_get_groups;
 	protocol_class->async_get_vcard     = jabber_async_get_vcard;
 	protocol_class->async_set_vcard     = jabber_async_set_vcard;
 	protocol_class->async_get_version   = jabber_async_get_version;
@@ -810,7 +832,8 @@ jabber_rename_contact (GossipProtocol *p
 }
 
 static void
-jabber_remove_contact (GossipProtocol *protocol, GossipContact *contact)
+jabber_remove_contact (GossipProtocol *protocol, 
+		       GossipContact  *contact)
 {
 	GossipJabber     *jabber;
 	GossipJabberPriv *priv;
@@ -852,6 +875,110 @@ jabber_get_contacts (GossipProtocol *pro
 	return NULL;
 }
 
+static void
+jabber_update_contact (GossipProtocol *protocol, 
+		       GossipContact  *contact)
+{
+	/* we set the groups _and_ the name here, the rename function
+	   will do exactly what we want to do so just call that */
+	jabber_rename_contact (protocol, 
+			       contact, 
+			       gossip_contact_get_name (contact));
+}
+
+static void
+jabber_rename_group (GossipProtocol *protocol,
+		     const gchar    *group,
+		     const gchar    *new_name)
+{
+	GossipJabber     *jabber;
+	GossipJabberPriv *priv;
+
+	RenameGroupData  *rg;
+
+	jabber = GOSSIP_JABBER (protocol);
+	priv = jabber->priv;
+	
+	rg = g_new0 (RenameGroupData, 1);
+	
+	rg->jabber = jabber;
+	rg->group = g_strdup (group);
+	rg->new_name = g_strdup (new_name);
+
+	g_hash_table_foreach (priv->contacts, 
+			      (GHFunc)jabber_rename_group_foreach_cb, 
+			      rg);
+
+	g_free (rg->group);
+	g_free (rg->new_name);
+	g_free (rg);
+}
+
+static void
+jabber_rename_group_foreach_cb (const gchar     *jid,
+				GossipContact   *contact,
+				RenameGroupData *rg)
+{
+	GossipJabberPriv *priv;
+	LmMessage        *m;
+        LmMessageNode    *node;
+	gchar            *escaped;
+	GList            *l; 
+	gboolean          found = FALSE;
+
+	priv = rg->jabber->priv;
+
+        for (l = gossip_contact_get_groups (contact); l && !found; l = l->next) {
+		gchar *group = (gchar*)l->data;
+
+		if (group && strcmp (group, rg->group) == 0) {
+			found = TRUE;
+		}
+	}
+	
+	if (!found) {
+		return;
+	}
+
+	m = lm_message_new_with_sub_type (NULL, 
+					  LM_MESSAGE_TYPE_IQ,
+					  LM_MESSAGE_SUB_TYPE_SET);
+	node = lm_message_node_add_child (m->node, "query", NULL);
+	lm_message_node_set_attributes (node,
+					"xmlns", "jabber:iq:roster",
+					NULL);
+	
+	escaped = g_markup_escape_text (gossip_contact_get_name (contact), -1);
+	
+	node = lm_message_node_add_child (node, "item", NULL);
+	lm_message_node_set_attributes (node, 
+					"jid", gossip_contact_get_id (contact),
+					"name", escaped,
+					NULL);
+	g_free (escaped);
+	
+	for (l = gossip_contact_get_groups (contact); l; l = l->next) {
+		const gchar *group = (const gchar*) l->data;
+		
+		/* do not include the group we are renaming */
+		if (group && strcmp (group, rg->group) == 0) {
+			continue;
+		}
+
+		escaped = g_markup_escape_text (group, -1);
+		lm_message_node_add_child (node, "group", escaped);
+		g_free (escaped);
+	}	
+
+	/* add the new group name */
+	escaped = g_markup_escape_text (rg->new_name, -1);
+	lm_message_node_add_child (node, "group", escaped);
+	g_free (escaped);
+	
+	lm_connection_send (priv->connection, m, NULL);
+	lm_message_unref (m);
+}
+
 static const gchar *
 jabber_get_active_resource (GossipProtocol *protocol,
 			    GossipContact  *contact)
@@ -860,6 +987,51 @@ jabber_get_active_resource (GossipProtoc
 	return NULL;
 }
 
+static GList *
+jabber_get_groups (GossipProtocol *protocol)
+{
+	GossipJabber     *jabber;
+	GossipJabberPriv *priv;
+	GList            *list = NULL;
+
+	jabber = GOSSIP_JABBER (protocol);
+	priv = jabber->priv;
+	
+	g_hash_table_foreach (priv->contacts, 
+			      (GHFunc)jabber_get_groups_foreach_cb, 
+			      &list);
+
+	list = g_list_sort (list, (GCompareFunc)strcmp);
+
+	return list;
+}
+
+static void
+jabber_get_groups_foreach_cb (const gchar    *jid,
+			      GossipContact  *contact,
+			      GList         **list)
+{
+	GList *l;
+
+	if (!gossip_contact_get_groups (contact)) {
+		return;
+	}
+
+	for (l = gossip_contact_get_groups (contact); l; l = l->next) {
+		gchar *group;
+		GList *found;
+
+		group = (gchar*) l->data;
+		found = g_list_find_custom (*list, 
+					    group, 
+					    (GCompareFunc)strcmp);
+		
+		if (!found) {
+			*list = g_list_prepend (*list, g_strdup (group));
+		}
+	}
+}
+
 static gboolean
 jabber_async_get_vcard (GossipProtocol            *protocol,
 			GossipContact             *contact,
@@ -1016,54 +1188,6 @@ jabber_disconnect_func (LmConnection    
 	jabber_signal_logged_out (jabber);
 }
 
-static gboolean
-jabber_update_contact (GossipContact *contact, LmMessageNode *node)
-{
-	/*const gchar   *subscription;
-	const gchar   *ask; */
-	const gchar   *name;
-	LmMessageNode *child;
-	GList         *categories = NULL;
-	gboolean       updated, categories_updated;
-
-	updated = categories_updated = FALSE;
-
-	/* Update the item, can be name change, group change, etc.. */
-	/* FIXME: Do we really need this in the contact? 
-	 *
-	 subscription = lm_message_node_get_attribute (node, "subscription");
-	 if (subscription) {
-	 g_free (item->subscription);
-	 item->subscription = g_strdup (subscription);
-	 }
-	
-	ask = lm_message_node_get_attribute (node, "ask");
-	if (ask) {
-		g_free (item->ask);
-		item->ask = g_strdup (ask);
-	}
-	 */
-		
-	name = lm_message_node_get_attribute (node, "name");
-	if (name) {
-		updated = TRUE;
-		gossip_contact_set_name (contact, name);
-	}
-
-	for (child = node->children; child; child = child->next) {
-		if (strcmp (child->name, "group") == 0 && child->value) {
-			categories = g_list_append (categories, child->value);
-		}
-	}
-
-	if (categories) {
-		/* does not get free'd */
-	categories_updated = gossip_contact_set_groups (contact, categories);
-	}
-
-	return updated || categories_updated;
-}
-
 static LmHandlerResult
 jabber_message_handler (LmMessageHandler *handler,
 			LmConnection     *conn,
@@ -1219,6 +1343,7 @@ jabber_iq_handler (LmMessageHandler *han
 	priv = jabber->priv;
 	
 	if (lm_message_get_sub_type (m) != LM_MESSAGE_SUB_TYPE_GET &&
+	    lm_message_get_sub_type (m) != LM_MESSAGE_SUB_TYPE_SET &&
 	    lm_message_get_sub_type (m) != LM_MESSAGE_SUB_TYPE_RESULT) {
 		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
 	}
@@ -1246,8 +1371,14 @@ jabber_iq_handler (LmMessageHandler *han
 		gboolean       new_item = FALSE;
 		gboolean       updated;
 		LmMessageNode *subnode;
+		LmMessageNode *child;
 		GList         *groups = NULL;
 
+		const gchar   *name;
+		GList         *new_groups = NULL;
+		gboolean       name_updated, groups_updated;
+		
+
 		if (strcmp (node->name, "item") != 0) {
 			continue;
 		}
@@ -1268,6 +1399,7 @@ jabber_iq_handler (LmMessageHandler *han
 			groups = g_list_append (groups, subnode->value);
 		}
 		
+		/* FIXME: why is this here if we set the groups below */
 		if (groups) {
 			gossip_contact_set_groups (contact, groups);
 		}
@@ -1285,13 +1417,33 @@ jabber_iq_handler (LmMessageHandler *han
 			continue;
 		}
 		
-		updated = jabber_update_contact (contact, node);
+		/* find out if any thing has updated */
+		name_updated = groups_updated = FALSE;
+
+		name = lm_message_node_get_attribute (node, "name");
+		if (name) {
+			name_updated = TRUE;
+			gossip_contact_set_name (contact, name);
+		}
+
+		for (child = node->children; child; child = child->next) {
+			if (strcmp (child->name, "group") == 0 && child->value) {
+				new_groups = g_list_append (new_groups, child->value);
+			}
+		}
+
+		if (new_groups) {
+			/* does not get free'd */
+			groups_updated = gossip_contact_set_groups (contact, new_groups);
+		}
+
+		updated = (name_updated || groups_updated);
+
 		
 		if (new_item) {
 			g_signal_emit_by_name (jabber, 
 					       "contact-added", contact);
-		} 
-		else if (updated) {
+		} else if (updated) {
 			g_signal_emit_by_name (jabber, 
 					       "contact-updated", contact);
 		}
Index: src/gossip-contact-list.c
===================================================================
RCS file: /cvs/gnome/gossip/src/gossip-contact-list.c,v
retrieving revision 1.13
diff -u -b -B -p -r1.13 gossip-contact-list.c
--- src/gossip-contact-list.c	30 Apr 2005 22:15:17 -0000	1.13
+++ src/gossip-contact-list.c	1 May 2005 13:58:30 -0000
@@ -465,29 +465,27 @@ contact_list_contact_updated_cb (GossipS
 				 GossipContact     *contact,
 				 GossipContactList *list)
 {
-	GtkTreeModel   *model;
+	GossipContactListPriv *priv;
 	GossipPresence *presence;
-	GList          *iters, *l;
+	GtkTreeModel   *model;
+
+	priv = list->priv;
 
 	model    = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 	presence = gossip_contact_get_active_presence (contact);
 
-	iters = contact_list_find_contact (list, contact);
-	if (!iters) {
-		return;
-	}
-
-	for (l = iters; l; l = l->next) {
-		gtk_tree_store_set (GTK_TREE_STORE (model), l->data,
-			    MODEL_COL_NAME, gossip_contact_get_name (contact),
-			    -1);
-	}
-
 	d(g_print ("Contact List: Contact updated: %s\n",
 		   gossip_contact_get_name (contact)));
 	
-	g_list_foreach (iters, (GFunc)gtk_tree_iter_free, NULL);
-	g_list_free (iters);
+	if (!priv->show_offline && !gossip_contact_is_online (contact)) {
+		return;
+	}
+
+	/* we do this to make sure the groups are correct, if not, we
+	   would have to check the groups already set up for each
+	   contact and then see what has been updated */
+	contact_list_remove_contact (list, contact);
+	contact_list_add_contact (list, contact);
 }
 
 static void 
@@ -993,7 +991,9 @@ contact_list_find_contact_foreach (GtkTr
 		fc->iters = g_list_append (fc->iters, gtk_tree_iter_copy (iter));
 	}
 	
-	return fc->found;
+	/* we want to find ALL contacts that match, this means if we
+	   have the same contact in 3 groups, all iters should be returned. */
+	return FALSE;
 }
 
 static gchar *
@@ -1176,7 +1176,71 @@ contact_list_group_menu_rename_cb (gpoin
                                    guint      action,
                                    GtkWidget *widget)
 {
-	d(g_print ("FIXME: Implement group::rename\n"));
+	GossipContactList *list;
+	GtkWidget         *dialog;
+	GtkWidget         *entry;
+	GtkWidget         *hbox;
+	gchar             *str;
+	gchar             *group;
+
+        list = GOSSIP_CONTACT_LIST (data);
+
+        group = gossip_contact_list_get_selected_group (list);
+	if (!group) {
+		return;
+	}
+
+	str = g_strdup_printf ("<b>%s</b>", group);
+
+	/* Translator: %s denotes the contact ID */
+	dialog = gtk_message_dialog_new (GTK_WINDOW (gossip_app_get_window ()),
+					 0,
+					 GTK_MESSAGE_QUESTION,
+					 GTK_BUTTONS_OK_CANCEL,
+					 _("Please enter a new name for the group:\n%s"),
+					 str);
+	
+	g_free (str);
+
+	gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+	
+	g_object_set (GTK_MESSAGE_DIALOG (dialog)->label,
+		      "use-markup", TRUE,
+		      NULL);
+
+        entry = gtk_entry_new ();
+	gtk_widget_show (entry);
+
+	gtk_entry_set_text (GTK_ENTRY (entry), group);
+	gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
+
+	g_signal_connect (entry,
+			  "activate",
+			  G_CALLBACK (contact_list_rename_entry_activate_cb),
+			  dialog);
+	
+	hbox = gtk_hbox_new (FALSE, 0);
+	gtk_widget_show (hbox);
+	
+	gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 4);
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), 
+			    hbox, FALSE, TRUE, 4);
+	
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
+		str = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
+	} else {
+		str = NULL;
+	}
+	
+	gtk_widget_destroy (dialog);
+
+	if (!str || !str[0]) {
+		g_free (group);
+		return;
+	}
+	
+	gossip_session_rename_group (gossip_app_get_session (), group, str);
+	g_free (group);
 }
 
 typedef struct {
@@ -1376,6 +1440,37 @@ gossip_contact_list_get_selected (Gossip
 			    -1);
 
 	return contact;
+}
+
+char *
+gossip_contact_list_get_selected_group (GossipContactList *list)
+{
+        GossipContactListPriv *priv;
+	GtkTreeSelection      *selection;
+	GtkTreeIter            iter;
+	GtkTreeModel          *model;
+	gboolean               is_group;
+	gchar                 *name;
+
+	g_return_val_if_fail (GOSSIP_IS_CONTACT_LIST (list), NULL);
+	
+	priv = list->priv;
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
+        if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+                return NULL;
+        }
+
+        gtk_tree_model_get (model, &iter,
+                            MODEL_COL_IS_GROUP, &is_group,
+			    MODEL_COL_NAME, &name,
+			    -1);
+
+	if (!is_group) {
+		return NULL;
+	}
+
+	return name;
 }
 
 gboolean
Index: src/gossip-contact-list.h
===================================================================
RCS file: /cvs/gnome/gossip/src/gossip-contact-list.h,v
retrieving revision 1.5
diff -u -b -B -p -r1.5 gossip-contact-list.h
--- src/gossip-contact-list.h	30 Apr 2005 14:54:28 -0000	1.5
+++ src/gossip-contact-list.h	1 May 2005 13:58:30 -0000
@@ -50,6 +50,7 @@ GType               gossip_contact_list_
 GossipContactList * gossip_contact_list_new (void);
 
 GossipContact *  gossip_contact_list_get_selected  (GossipContactList *list);
+char *           gossip_contact_list_get_selected_group (GossipContactList *list);
 
 gboolean     gossip_contact_list_get_show_offline (GossipContactList *list);
 void         gossip_contact_list_set_show_offline (GossipContactList *list,
Index: src/gossip-edit-groups-dialog.c
===================================================================
RCS file: /cvs/gnome/gossip/src/gossip-edit-groups-dialog.c,v
retrieving revision 1.7
diff -u -b -B -p -r1.7 gossip-edit-groups-dialog.c
--- src/gossip-edit-groups-dialog.c	30 Apr 2005 14:54:28 -0000	1.7
+++ src/gossip-edit-groups-dialog.c	1 May 2005 13:58:31 -0000
@@ -37,15 +37,12 @@ struct _GossipEditGroups {
 	GtkWidget   *add_entry;
 	GtkWidget   *add_button;
 
-	GtkWidget   *apply_button;
 	GtkWidget   *ok_button;
 
 	GtkTreeView *treeview;
 
         GossipContact *contact;
 
-	GList       *restore_groups;
-	
 	gboolean     changes_made;
 };
 
@@ -85,7 +82,6 @@ static void     edit_groups_add_groups  
 static void     edit_groups_set                   (GossipEditGroups      *info,
 						   GList                 *groups);
 static void     edit_groups_save                  (GossipEditGroups      *info);
-static void     edit_groups_restore               (GossipEditGroups      *info);
 static gboolean edit_groups_find_name             (GossipEditGroups      *info,
 						   const gchar           *name,
 						   GtkTreeIter           *iter);
@@ -114,25 +110,14 @@ edit_groups_dialog_destroy_cb (GtkWidget
 {
         g_object_unref (info->contact);
 
-	g_list_foreach (info->restore_groups, (GFunc) g_free, NULL); 
-	g_list_free (info->restore_groups);
-	
  	g_free (info); 
 }
 
 static void
 edit_groups_dialog_response_cb (GtkWidget *widget, gint response, GossipEditGroups *info)
 {
-	if (response == GTK_RESPONSE_OK && info->changes_made) {
-		/* save groups if changes made */
-		edit_groups_save (info);
-	}
-	else if (response == GTK_RESPONSE_APPLY && info->changes_made) {
+	if (info->changes_made && response == GTK_RESPONSE_OK) {
 		edit_groups_save (info);
-		return;
-	}
-	else if (response == GTK_RESPONSE_CANCEL && info->changes_made) {
-		edit_groups_restore (info);
 	}
 	
 	gtk_widget_destroy (info->dialog);
@@ -160,7 +145,6 @@ gossip_edit_groups_new (GossipContact *c
 				     "add_entry", &info->add_entry,
 				     "add_button", &info->add_button,
 				     "ok_button", &info->ok_button,
-				     "apply_button", &info->apply_button,
 				     "groups_treeview", &info->treeview,
 				     NULL);
 
@@ -286,15 +270,6 @@ edit_groups_save (GossipEditGroups *info
 	
 	g_list_foreach (groups, (GFunc)g_free, NULL); 
 	g_list_free (groups);
-
-	gtk_widget_set_sensitive (GTK_WIDGET (info->apply_button), FALSE);
-}
-
-static void
-edit_groups_restore (GossipEditGroups *info)
-{
-	edit_groups_set (info, info->restore_groups);
-	gtk_widget_set_sensitive (GTK_WIDGET (info->apply_button), FALSE);
 }
 
 static void
@@ -313,8 +288,6 @@ edit_groups_add_groups (GossipEditGroups
 	for (l = groups; l; l = l->next) {
 		const gchar *group_str = (const gchar *) l->data;
 
-		/* This needs to be done better, could have group_is_unsorted().
-		 */
 		if (strcmp (group_str, _("Unsorted")) == 0) {
 			continue;
 		}
@@ -325,8 +298,6 @@ edit_groups_add_groups (GossipEditGroups
 	for (l = all_groups; l; l = l->next) {
 		const gchar *group_str = (const gchar *) l->data;
 
-		/* This needs to be done better, could have group_is_unsorted().
-		 */
 		if (strcmp (group_str, _("Unsorted")) == 0) {
 			continue;
 		}
@@ -348,7 +319,8 @@ edit_groups_add_groups (GossipEditGroups
 		}
 	}
 
-	info->restore_groups = my_groups;
+	g_list_foreach (my_groups, (GFunc) g_free, NULL); 
+	g_list_free (my_groups);
 }
 
 static gboolean 
@@ -485,8 +457,6 @@ edit_groups_cell_toggled (GtkCellRendere
 	gtk_tree_path_free (path);
 
 	info->changes_made = TRUE;
-
-	gtk_widget_set_sensitive (GTK_WIDGET (info->apply_button), info->changes_made);
 }
 
 static void 
@@ -538,7 +508,5 @@ edit_groups_add_button_clicked (GtkButto
 	gtk_entry_set_text (GTK_ENTRY (info->add_entry), "");
 	
 	info->changes_made = TRUE;
-
-	gtk_widget_set_sensitive (GTK_WIDGET (info->apply_button), info->changes_made);
 }
 
Index: ui/main.glade
===================================================================
RCS file: /cvs/gnome/gossip/ui/main.glade,v
retrieving revision 1.76
diff -u -b -B -p -r1.76 main.glade
--- ui/main.glade	30 Apr 2005 22:32:50 -0000	1.76
+++ ui/main.glade	1 May 2005 13:58:38 -0000
@@ -3669,14 +3669,14 @@ You can change your account settings lat
   <property name="visible">True</property>
   <property name="title" translatable="yes">Edit Groups</property>
   <property name="type">GTK_WINDOW_TOPLEVEL</property>
-  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
   <property name="modal">False</property>
   <property name="resizable">False</property>
   <property name="destroy_with_parent">False</property>
   <property name="decorated">True</property>
   <property name="skip_taskbar_hint">False</property>
   <property name="skip_pager_hint">False</property>
-  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
   <property name="has_separator">False</property>
 
@@ -3692,20 +3692,6 @@ You can change your account settings lat
 	  <property name="layout_style">GTK_BUTTONBOX_END</property>
 
 	  <child>
-	    <widget class="GtkButton" id="apply_button">
-	      <property name="visible">True</property>
-	      <property name="sensitive">False</property>
-	      <property name="can_default">True</property>
-	      <property name="can_focus">True</property>
-	      <property name="label">gtk-apply</property>
-	      <property name="use_stock">True</property>
-	      <property name="relief">GTK_RELIEF_NORMAL</property>
-	      <property name="focus_on_click">True</property>
-	      <property name="response_id">-10</property>
-	    </widget>
-	  </child>
-
-	  <child>
 	    <widget class="GtkButton" id="cancel_button">
 	      <property name="visible">True</property>
 	      <property name="can_default">True</property>
@@ -3861,7 +3847,7 @@ You can change your account settings lat
 
 	      <child>
 		<widget class="GtkScrolledWindow" id="scrolledwindow">
-		  <property name="height_request">175</property>
+		  <property name="height_request">125</property>
 		  <property name="visible">True</property>
 		  <property name="can_focus">True</property>
 		  <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
