changeset 14138:7f276f375789

[gaim-migrate @ 16780] Merges r16472-16568 from blist-efficiency into trunk. committer: Tailor Script <tailor@pidgin.im>
author Aaron Sheldon <aaronsheldon>
date Wed, 16 Aug 2006 04:50:27 +0000
parents 7a763d1d1df6
children 44ec6c7cbc76
files src/blist.c src/gtkblist.c src/gtkblist.h src/gtkconv.c src/privacy.c
diffstat 5 files changed, 340 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/src/blist.c	Wed Aug 16 03:18:43 2006 +0000
+++ b/src/blist.c	Wed Aug 16 04:50:27 2006 +0000
@@ -2636,6 +2636,11 @@
 										GAIM_SUBTYPE_STATUS),
 	                     gaim_value_new(GAIM_TYPE_SUBTYPE,
 										GAIM_SUBTYPE_STATUS));
+	gaim_signal_register(handle, "buddy-privacy-changed",
+	                     gaim_marshal_VOID__POINTER, NULL,
+	                     1,
+	                     gaim_value_new(GAIM_TYPE_SUBTYPE,
+	                                    GAIM_SUBTYPE_BLIST_BUDDY));
 
 	gaim_signal_register(handle, "buddy-idle-changed",
 	                     gaim_marshal_VOID__POINTER_INT_INT, NULL,
--- a/src/gtkblist.c	Wed Aug 16 03:18:43 2006 +0000
+++ b/src/gtkblist.c	Wed Aug 16 04:50:27 2006 +0000
@@ -104,6 +104,7 @@
 
 static guint visibility_manager_count = 0;
 static gboolean gtk_blist_obscured = FALSE;
+GHashTable* status_icon_hash_table = NULL;
 
 static GList *gaim_gtk_blist_sort_methods = NULL;
 static struct gaim_gtk_blist_sort_method *current_sort_method = NULL;
@@ -119,14 +120,15 @@
 static GaimGtkBuddyList *gtkblist = NULL;
 
 static gboolean gaim_gtk_blist_refresh_timer(GaimBuddyList *list);
-static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node);
+static void gaim_gtk_blist_update_buddy_status_icon_key(GaimBlistNode *node, GaimStatusIconSize size);
+static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node, gboolean statusChange);
 static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data);
 static void gaim_gtk_blist_update(GaimBuddyList *list, GaimBlistNode *node);
 static void gaim_gtk_blist_update_contact(GaimBuddyList *list, GaimBlistNode *node);
 static char *gaim_get_tooltip_text(GaimBlistNode *node, gboolean full);
 static const char *item_factory_translate_func (const char *path, gpointer func_data);
 static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter);
-static void redo_buddy_list(GaimBuddyList *list, gboolean remove);
+static void redo_buddy_list(GaimBuddyList *list, gboolean remove, gboolean rerender);
 static void gaim_gtk_blist_collapse_contact_cb(GtkWidget *w, GaimBlistNode *node);
 
 static void gaim_gtk_blist_tooltip_destroy(void);
@@ -136,6 +138,7 @@
 	gboolean contact_expanded;
 	gboolean recent_signonoff;
 	gint recent_signonoff_timer;
+	GString *status_icon_key;
 };
 
 
@@ -535,6 +538,12 @@
 }
 
 static void
+gaim_gtk_blist_update_privacy_cb(GaimBuddy *buddy)
+{
+	gaim_gtk_blist_update_buddy(gaim_get_blist(), (GaimBlistNode*)(buddy), TRUE);
+}
+
+static void
 rebuild_joinchat_entries(GaimGtkJoinChatData *data)
 {
 	GaimConnection *gc;
@@ -2692,12 +2701,112 @@
 	int y;
 };
 
+static void g_string_destroy(GString *destroyable)
+{
+	g_string_free(destroyable, TRUE);
+	return;
+}
+
+static void
+gaim_gtk_blist_update_buddy_status_icon_key(GaimBlistNode *node, GaimStatusIconSize size)
+{
+	int i;
+	GaimAccount *account;
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info;
+	GString *key = g_string_sized_new(16);
+	GaimBuddy *buddy;
+	const char *protoname = NULL;
+	struct _gaim_gtk_blist_node *gtknode = node->ui_data;
+	struct _gaim_gtk_blist_node *gtkbuddynode = NULL;
+	struct _emblem_data emblems[4] = {{NULL, 15, 15}, {NULL, 0, 15},
+		{NULL, 0, 0}, {NULL, 15, 0}};
+
+	buddy = (GaimBuddy*)node;
+	gtkbuddynode = node->ui_data;
+
+	if (gtkbuddynode && gtkbuddynode->recent_signonoff) {
+		if (GAIM_BUDDY_IS_ONLINE(buddy))
+			g_string_printf(key, "login");
+		else
+			g_string_printf(key, "logout");
+	} else {
+		GaimConversation *conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM,
+															 gaim_buddy_get_name(buddy),
+															 gaim_buddy_get_account(buddy));
+
+		account = buddy->account;
+		prpl = gaim_find_prpl(gaim_account_get_protocol_id(account));
+
+		if(!prpl)
+			return;
+
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+		if(prpl_info && prpl_info->list_icon) {
+			protoname = prpl_info->list_icon(account, buddy);
+		}
+		if(prpl_info && prpl_info->list_emblems) {
+			if(gtknode)
+				prpl_info->list_emblems(buddy, &emblems[0].filename,
+						&emblems[1].filename, &emblems[2].filename,
+						&emblems[3].filename);
+		}
+
+		g_string_assign(key, protoname);
+
+		if(conv != NULL) {
+			GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv);
+			if(gtkconv != NULL && gaim_gtkconv_is_hidden(gtkconv)) {
+				/* add pending emblem */
+				if(size == GAIM_STATUS_ICON_SMALL) {
+					emblems[0].filename="pending";
+				}
+				else {
+					emblems[3].filename=emblems[2].filename;
+					emblems[2].filename="pending";
+				}
+			}
+		}
+
+		if(size == GAIM_STATUS_ICON_SMALL) {
+			/* So that only the se icon will composite */
+			emblems[1].filename = emblems[2].filename = emblems[3].filename = NULL;
+		}
+
+		for(i=0; i<4; i++) {
+			if(emblems[i].filename) {
+				g_string_append_printf(key, "/%s", emblems[i].filename);
+			}
+		}
+	}
+
+	if (!GAIM_BUDDY_IS_ONLINE(buddy)) {
+		key = g_string_append(key, "/off");
+	} else if (gaim_presence_is_idle(gaim_buddy_get_presence(buddy))) {
+		key = g_string_append(key, "/idle");
+	}
+	if (!gaim_privacy_check(buddy->account, gaim_buddy_get_name(buddy))) {
+		key = g_string_append(key, "/priv");
+	}
+
+	if (gtkbuddynode) {
+		if (gtkbuddynode->status_icon_key)
+			g_string_free(gtkbuddynode->status_icon_key, TRUE);
+		gtkbuddynode->status_icon_key = g_string_new(key->str);		
+	}
+
+	g_string_free(key, TRUE);
+	return;
+}
+
 GdkPixbuf *
 gaim_gtk_blist_get_status_icon(GaimBlistNode *node, GaimStatusIconSize size)
 {
 	GdkPixbuf *scale, *status = NULL;
 	int i, scalesize = 30;
 	char *filename;
+	GString *key = g_string_sized_new(16);
 	const char *protoname = NULL;
 	struct _gaim_gtk_blist_node *gtknode = node->ui_data;
 	struct _gaim_gtk_blist_node *gtkbuddynode = NULL;
@@ -2721,6 +2830,28 @@
 		return NULL;
 	}
 
+	if (!status_icon_hash_table) {
+		status_icon_hash_table = g_hash_table_new_full((GHashFunc)g_string_hash,
+								(GEqualFunc)g_string_equal,
+								(GDestroyNotify)g_string_destroy,
+								(GDestroyNotify)gdk_pixbuf_unref);
+	
+	} else if (buddy && gtkbuddynode->status_icon_key && gtkbuddynode->status_icon_key->str) {
+		key = g_string_new(gtkbuddynode->status_icon_key->str);
+
+		/* Respect the size request given */
+		if (size == GAIM_STATUS_ICON_SMALL) {
+			key = g_string_append(key, "/tiny");
+		}
+
+		scale = g_hash_table_lookup(status_icon_hash_table, key);
+		if (scale) {
+			gdk_pixbuf_ref(scale);
+			g_string_free(key, TRUE);
+			return scale;
+		}
+	}
+
 	if(buddy || chat) {
 		GaimAccount *account;
 		GaimPlugin *prpl;
@@ -2748,24 +2879,93 @@
 		}
 	}
 
-	if(buddy) {
-		GaimConversation *conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM,
+/* Begin Generating Lookup Key */
+	if (buddy) {
+		gaim_gtk_blist_update_buddy_status_icon_key(node, size);
+		g_string_printf(key, "%s", strdup(gtkbuddynode->status_icon_key->str));
+	}
+	/* There are only two options for chat or gaimdude - big or small */
+	else if (chat) {
+		GaimAccount *account;
+		GaimPlugin *prpl;
+		GaimPluginProtocolInfo *prpl_info;
+
+		account = chat->account;
+
+		prpl = gaim_find_prpl(gaim_account_get_protocol_id(account));
+		if(!prpl)
+			return NULL;
+
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+		if(prpl_info && prpl_info->list_icon) {
+			protoname = prpl_info->list_icon(account, NULL);
+		}
+		g_string_append_printf(key, "%s-chat", protoname);
+	}
+	else
+		key = g_string_append(key, "gaimdude");
+
+	/* If the icon is small, we do not store this into the status_icon_key
+	 * in the gtkbuddynode.  This way we can respect the size value on cache 
+	 * lookup. Otherwise, different sized icons could not be stored easily.
+	 */
+	if (size == GAIM_STATUS_ICON_SMALL) {
+		key = g_string_append(key, "/tiny");
+	}
+
+/* End Generating Lookup Key */
+
+/* If we already know this icon, just return it */
+	scale = g_hash_table_lookup(status_icon_hash_table, key);
+	if (scale) {
+		gdk_pixbuf_ref(scale);
+		g_string_free(key, TRUE);
+		return scale;
+	}
+
+/* Create a new composite icon */
+
+		if(buddy) {
+			GaimAccount *account;
+			GaimPlugin *prpl;
+			GaimPluginProtocolInfo *prpl_info;
+			GaimConversation *conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM,
 																	 gaim_buddy_get_name(buddy),
 																	 gaim_buddy_get_account(buddy));
-		if(conv != NULL) {
-			GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv);
-			if(gtkconv != NULL && gaim_gtkconv_is_hidden(gtkconv)) {
-				/* add pending emblem */
-				if(size == GAIM_STATUS_ICON_SMALL) {
-					emblems[0].filename="pending";
-				}
-				else {
-					emblems[3].filename=emblems[2].filename;
-					emblems[2].filename="pending";
+
+			account = buddy->account;
+
+			prpl = gaim_find_prpl(gaim_account_get_protocol_id(account));
+			if(!prpl)
+				return NULL;
+
+			prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+			if(prpl_info && prpl_info->list_icon) {
+				protoname = prpl_info->list_icon(account, buddy);
+			}
+			if(prpl_info && prpl_info->list_emblems) {
+				if(gtknode && !gtknode->recent_signonoff)
+					prpl_info->list_emblems(buddy, &emblems[0].filename,
+							&emblems[1].filename, &emblems[2].filename,
+							&emblems[3].filename);
+			}
+			
+			if(conv != NULL) {
+				GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv);
+				if(gtkconv != NULL && gaim_gtkconv_is_hidden(gtkconv)) {
+					/* add pending emblem */
+					if(size == GAIM_STATUS_ICON_SMALL) {
+						emblems[0].filename="pending";
+					}
+					else {
+						emblems[3].filename=emblems[2].filename;
+						emblems[2].filename="pending";
+					}
 				}
 			}
 		}
-	}
 
 	if(size == GAIM_STATUS_ICON_SMALL) {
 		scalesize = 15;
@@ -2773,6 +2973,8 @@
 		emblems[1].filename = emblems[2].filename = emblems[3].filename = NULL;
 	}
 
+
+
 	if(buddy && GAIM_BUDDY_IS_ONLINE(buddy) &&  gtkbuddynode && gtkbuddynode->recent_signonoff) {
 			filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL);
 	} else if(buddy && !GAIM_BUDDY_IS_ONLINE(buddy) && gtkbuddynode && gtkbuddynode->recent_signonoff) {
@@ -2829,7 +3031,6 @@
 
 	if(buddy) {
 		presence = gaim_buddy_get_presence(buddy);
-
 		if (!GAIM_BUDDY_IS_ONLINE(buddy))
 			gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE);
 		else if (gaim_presence_is_idle(presence))
@@ -2859,6 +3060,10 @@
 		}
 	}
 
+	/* Insert the new icon into the status icon hash table */	
+	g_hash_table_insert (status_icon_hash_table, key, scale);
+	gdk_pixbuf_ref(scale);
+
 	return scale;
 }
 
@@ -3082,7 +3287,7 @@
 	return TRUE;
 }
 
-static void gaim_gtk_blist_hide_node(GaimBuddyList *list, GaimBlistNode *node)
+static void gaim_gtk_blist_hide_node(GaimBuddyList *list, GaimBlistNode *node, gboolean update)
 {
 	struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data;
 	GtkTreeIter iter;
@@ -3092,11 +3297,10 @@
 
 	if(gtkblist->selected_node == node)
 		gtkblist->selected_node = NULL;
-
 	if (get_iter_from_node(node, &iter)) {
 		gtk_tree_store_remove(gtkblist->treemodel, &iter);
-		if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_BUDDY(node)
-				|| GAIM_BLIST_NODE_IS_CHAT(node)) {
+		if(update && (GAIM_BLIST_NODE_IS_CONTACT(node) || 
+			GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CHAT(node))) {
 			gaim_gtk_blist_update(list, node->parent);
 		}
 	}
@@ -3228,7 +3432,7 @@
 	if(conv->account != NULL && conv->name != NULL) {
 		GaimBuddy *buddy = gaim_find_buddy(conv->account, conv->name);
 		if(buddy != NULL)
-			gaim_gtk_blist_update_buddy(NULL, (GaimBlistNode *)buddy);
+			gaim_gtk_blist_update_buddy(NULL, (GaimBlistNode *)buddy, TRUE);
 	}
 
 	if (gtkblist->menutrayicon) {
@@ -3367,7 +3571,7 @@
 		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
 	}
 
-	redo_buddy_list(gaim_get_blist(), TRUE);
+	redo_buddy_list(gaim_get_blist(), FALSE, FALSE);
 #if GTK_CHECK_VERSION(2,6,0)
 	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview));
 #endif
@@ -3914,23 +4118,36 @@
 	gaim_signal_emit(handle, "gtkblist-created", list);
 }
 
-static void redo_buddy_list(GaimBuddyList *list, gboolean remove)
+static void redo_buddy_list(GaimBuddyList *list, gboolean remove, gboolean rerender)
 {
 	GaimBlistNode *node = list->root;
 
 	while (node)
 	{
+		/* This is only needed when we're reverting to a non-GTK+ sorted
+		 * status.  We shouldn't need to remove otherwise.
+		 */
 		if (remove && !GAIM_BLIST_NODE_IS_GROUP(node))
-			gaim_gtk_blist_hide_node(list, node);
-
-		gaim_gtk_blist_update(list, node);
+			gaim_gtk_blist_hide_node(list, node, FALSE);
+
+		if (GAIM_BLIST_NODE_IS_BUDDY(node))
+			gaim_gtk_blist_update_buddy(list, node, rerender);
+		else if (GAIM_BLIST_NODE_IS_CHAT(node))
+			gaim_gtk_blist_update(list, node);
 		node = gaim_blist_node_next(node, FALSE);
 	}
+
+	/* There is no hash table if there is nothing in the buddy list to update */
+	if (status_icon_hash_table) {
+		g_hash_table_destroy(status_icon_hash_table);
+		status_icon_hash_table = NULL;
+	}
+
 }
 
 void gaim_gtk_blist_refresh(GaimBuddyList *list)
 {
-	redo_buddy_list(list, FALSE);
+	redo_buddy_list(list, FALSE, TRUE);
 }
 
 void
@@ -3979,7 +4196,7 @@
 
 	gaim_request_close_with_handle(node);
 
-	gaim_gtk_blist_hide_node(list, node);
+	gaim_gtk_blist_hide_node(list, node, TRUE);
 
 	if(node->parent)
 		gaim_gtk_blist_update(list, node->parent);
@@ -4147,7 +4364,7 @@
 				-1);
 		g_free(mark);
 	} else {
-		gaim_gtk_blist_hide_node(list, gnode);
+		gaim_gtk_blist_hide_node(list, gnode, TRUE);
 	}
 }
 
@@ -4269,13 +4486,13 @@
 			buddy_node(buddy, &iter, cnode);
 		}
 	} else {
-		gaim_gtk_blist_hide_node(list, cnode);
+		gaim_gtk_blist_hide_node(list, cnode, TRUE);
 	}
 }
 
 
 
-static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node)
+static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node, gboolean statusChange)
 {
 	GaimBuddy *buddy;
 	struct _gaim_gtk_blist_node *gtkparentnode;
@@ -4287,6 +4504,10 @@
 
 	buddy = (GaimBuddy*)node;
 
+	if (statusChange)
+		gaim_gtk_blist_update_buddy_status_icon_key(node, (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")
+				 ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL));
+
 	/* First things first, update the contact */
 	gaim_gtk_blist_update_contact(list, node);
 
@@ -4302,7 +4523,7 @@
 		buddy_node(buddy, &iter, node);
 
 	} else {
-		gaim_gtk_blist_hide_node(list, node);
+		gaim_gtk_blist_hide_node(list, node, TRUE);
 	}
 
 }
@@ -4342,7 +4563,7 @@
 		if(status)
 			g_object_unref(status);
 	} else {
-		gaim_gtk_blist_hide_node(list, node);
+		gaim_gtk_blist_hide_node(list, node, TRUE);
 	}
 }
 
@@ -4362,7 +4583,7 @@
 			gaim_gtk_blist_update_contact(list, node);
 			break;
 		case GAIM_BLIST_BUDDY_NODE:
-			gaim_gtk_blist_update_buddy(list, node);
+			gaim_gtk_blist_update_buddy(list, node, TRUE);
 			break;
 		case GAIM_BLIST_CHAT_NODE:
 			gaim_gtk_blist_update_chat(list, node);
@@ -5214,6 +5435,7 @@
 
 	gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", gtk_blist_handle, GAIM_CALLBACK(buddy_signonoff_cb), NULL);
 	gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-off", gtk_blist_handle, GAIM_CALLBACK(buddy_signonoff_cb), NULL);
+	gaim_signal_connect(gaim_blist_get_handle(), "buddy-privacy-changed", gtk_blist_handle, GAIM_CALLBACK(gaim_gtk_blist_update_privacy_cb), NULL);
 }
 
 void
@@ -5271,8 +5493,11 @@
 		gaim_gtk_blist_sort_method_set("none");
 		return;
 	}
-	redo_buddy_list(gaim_get_blist(), TRUE);
-
+	if (!strcmp(id, "none")) {
+		redo_buddy_list(gaim_get_blist(), TRUE, FALSE);
+	} else {
+		redo_buddy_list(gaim_get_blist(), FALSE, FALSE);
+	}
 }
 
 /******************************************
@@ -5837,8 +6062,8 @@
 	if (gtk_check_menu_item_get_active(checkmenuitem))
 	{
 		gaim_gtk_set_cursor(gtkblist->window, GDK_WATCH);
-
-		gaim_gtk_blist_sort_method_set(id);
+		/* This is redundant. I think. */
+		/* gaim_gtk_blist_sort_method_set(id); */
 		gaim_prefs_set_string("/gaim/gtk/blist/sort_type", id);
 
 		gaim_gtk_clear_cursor(gtkblist->window);
--- a/src/gtkblist.h	Wed Aug 16 03:18:43 2006 +0000
+++ b/src/gtkblist.h	Wed Aug 16 04:50:27 2006 +0000
@@ -143,7 +143,7 @@
  * Populates a menu with the items shown on the buddy list for a buddy.
  *
  * @param menu  The menu to populate
- * @param buddy The buddy who's menu to get
+ * @param buddy The buddy whose menu to get
  * @param sub   TRUE if this is a sub-menu, FALSE otherwise
  */
 void gaim_gtk_blist_make_buddy_menu(GtkWidget *menu, GaimBuddy *buddy, gboolean sub);
--- a/src/gtkconv.c	Wed Aug 16 03:18:43 2006 +0000
+++ b/src/gtkconv.c	Wed Aug 16 04:50:27 2006 +0000
@@ -2185,6 +2185,7 @@
 	GaimAccount *account = NULL;
 	const char *name = NULL;
 	GdkPixbuf *status = NULL;
+	GaimBlistUiOps *ops = gaim_blist_get_ui_ops();
 
 	g_return_val_if_fail(conv != NULL, NULL);
 
@@ -2198,6 +2199,12 @@
 	if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) {
 		GaimBuddy *b = gaim_find_buddy(account, name);
 		if (b != NULL) {
+			/* I hate this hack.  It fixes a bug where the pending message icon
+			 * displays in the conv tab even though it shouldn't.
+			 * A better solution would be great. */
+			if (ops && ops->update)
+				ops->update(NULL, (GaimBlistNode*)b);
+
 			status = gaim_gtk_blist_get_status_icon((GaimBlistNode*)b,
 				(small_icon ? GAIM_STATUS_ICON_SMALL : GAIM_STATUS_ICON_LARGE));
 		}
@@ -6341,6 +6348,20 @@
 }
 
 static void
+update_buddy_privacy_changed(GaimBuddy *buddy)
+{
+	GaimGtkConversation *gtkconv;
+	GaimConversation *conv;
+
+	gtkconv = get_gtkconv_with_contact(gaim_buddy_get_contact(buddy));
+	if (gtkconv)
+	{
+		conv = gtkconv->active_conv;
+		gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_TAB_ICON);
+	}
+}
+
+static void
 update_buddy_idle_changed(GaimBuddy *buddy, gboolean old, gboolean newidle)
 {
 	GaimConversation *conv;
@@ -6628,6 +6649,8 @@
 						handle, GAIM_CALLBACK(update_buddy_sign), "off");
 	gaim_signal_connect(blist_handle, "buddy-status-changed",
 						handle, GAIM_CALLBACK(update_buddy_status_changed), NULL);
+	gaim_signal_connect(blist_handle, "buddy-privacy-changed",
+						handle, GAIM_CALLBACK(update_buddy_privacy_changed), NULL);
 	gaim_signal_connect(blist_handle, "buddy-idle-changed",
 						handle, GAIM_CALLBACK(update_buddy_idle_changed), NULL);
 	gaim_signal_connect(blist_handle, "buddy-icon-changed",
--- a/src/privacy.c	Wed Aug 16 03:18:43 2006 +0000
+++ b/src/privacy.c	Wed Aug 16 04:50:27 2006 +0000
@@ -34,6 +34,7 @@
 {
 	GSList *l;
 	char *name;
+	GaimBuddy *buddy;
 
 	g_return_val_if_fail(account != NULL, FALSE);
 	g_return_val_if_fail(who     != NULL, FALSE);
@@ -41,16 +42,17 @@
 	name = g_strdup(gaim_normalize(account, who));
 
 	for (l = account->permit; l != NULL; l = l->next) {
-		if (!gaim_utf8_strcasecmp(name, gaim_normalize(account, (char *)l->data)))
+		if (!gaim_utf8_strcasecmp(name, (char *)l->data))
 			break;
 	}
 
-	g_free(name);
+ 	if (l != NULL)
+	{
+		g_free(name);
+ 		return FALSE;
+	}
 
-	if (l != NULL)
-		return FALSE;
-
-	account->permit = g_slist_append(account->permit, g_strdup(who));
+	account->permit = g_slist_append(account->permit, name);
 
 	if (!local_only && gaim_account_is_connected(account))
 		serv_add_permit(gaim_account_get_connection(account), who);
@@ -60,6 +62,12 @@
 
 	gaim_blist_schedule_save();
 
+	/* This lets the UI know a buddy has had its privacy setting changed */
+	buddy = gaim_find_buddy(account, name);
+	if (buddy != NULL) {
+		gaim_signal_emit(gaim_blist_get_handle(),
+                "buddy-privacy-changed", buddy);
+	}
 	return TRUE;
 }
 
@@ -69,19 +77,18 @@
 {
 	GSList *l;
 	char *name;
+	GaimBuddy *buddy;
 
 	g_return_val_if_fail(account != NULL, FALSE);
 	g_return_val_if_fail(who     != NULL, FALSE);
 
-	name = g_strdup(gaim_normalize(account, who));
+	name = gaim_normalize(account, who);
 
 	for (l = account->permit; l != NULL; l = l->next) {
-		if (!gaim_utf8_strcasecmp(name, gaim_normalize(account, (char *)l->data)))
+		if (!gaim_utf8_strcasecmp(name, (char *)l->data))
 			break;
 	}
 
-	g_free(name);
-
 	if (l == NULL)
 		return FALSE;
 
@@ -96,6 +103,11 @@
 
 	gaim_blist_schedule_save();
 
+	buddy = gaim_find_buddy(account, name);
+	if (buddy != NULL) {
+		gaim_signal_emit(gaim_blist_get_handle(),
+                "buddy-privacy-changed", buddy);
+	}
 	return TRUE;
 }
 
@@ -105,6 +117,7 @@
 {
 	GSList *l;
 	char *name;
+	GaimBuddy *buddy;
 
 	g_return_val_if_fail(account != NULL, FALSE);
 	g_return_val_if_fail(who     != NULL, FALSE);
@@ -116,12 +129,13 @@
 			break;
 	}
 
-	g_free(name);
+	if (l != NULL)
+	{
+		g_free(name);
+		return FALSE;
+	}
 
-	if (l != NULL)
-		return FALSE;
-
-	account->deny = g_slist_append(account->deny, g_strdup(who));
+	account->deny = g_slist_append(account->deny, name);
 
 	if (!local_only && gaim_account_is_connected(account))
 		serv_add_deny(gaim_account_get_connection(account), who);
@@ -131,6 +145,11 @@
 
 	gaim_blist_schedule_save();
 
+	buddy = gaim_find_buddy(account, name);
+	if (buddy != NULL) {
+		gaim_signal_emit(gaim_blist_get_handle(),
+                "buddy-privacy-changed", buddy);
+	}
 	return TRUE;
 }
 
@@ -140,18 +159,19 @@
 {
 	GSList *l;
 	char *name;
+	GaimBuddy *buddy;
 
 	g_return_val_if_fail(account != NULL, FALSE);
 	g_return_val_if_fail(who     != NULL, FALSE);
 
-	name = g_strdup(gaim_normalize(account, who));
+	name = gaim_normalize(account, who);
 
 	for (l = account->deny; l != NULL; l = l->next) {
-		if (!gaim_utf8_strcasecmp(name, gaim_normalize(account, (char *)l->data)))
+		if (!gaim_utf8_strcasecmp(name, (char *)l->data))
 			break;
 	}
 
-	g_free(name);
+	buddy = gaim_find_buddy(account, name);
 
 	if (l == NULL)
 		return FALSE;
@@ -163,7 +183,12 @@
 		serv_rem_deny(gaim_account_get_connection(account), name);
 
 	if (privacy_ops != NULL && privacy_ops->deny_removed != NULL)
-		privacy_ops->deny_removed(account, name);
+		privacy_ops->deny_removed(account, who);
+
+	if (buddy != NULL) {
+		gaim_signal_emit(gaim_blist_get_handle(),
+                "buddy-privacy-changed", buddy);
+	}
 
 	g_free(name);
 	gaim_blist_schedule_save();
@@ -184,15 +209,17 @@
 			return FALSE;
 
 		case GAIM_PRIVACY_ALLOW_USERS:
+			who = gaim_normalize(account, who);
 			for (list=account->permit; list!=NULL; list=list->next) {
-				if (!gaim_utf8_strcasecmp(who, gaim_normalize(account, (char *)list->data)))
+				if (!gaim_utf8_strcasecmp(who, (char *)list->data))
 					return TRUE;
 			}
 			return FALSE;
 
 		case GAIM_PRIVACY_DENY_USERS:
+			who = gaim_normalize(account, who);
 			for (list=account->deny; list!=NULL; list=list->next) {
-				if (!gaim_utf8_strcasecmp(who, gaim_normalize( account, (char *)list->data )))
+				if (!gaim_utf8_strcasecmp(who, (char *)list->data ))
 					return FALSE;
 			}
 			return TRUE;