pidgin/gtkblist.c

changeset 20752
22ffec5874b4
parent 20747
c7494ef1530d
child 20879
157cd6ca3ab6
child 21203
182f920f98a0
--- a/pidgin/gtkblist.c	Fri Sep 28 17:18:13 2007 +0000
+++ b/pidgin/gtkblist.c	Fri Sep 28 17:20:33 2007 +0000
@@ -88,6 +88,8 @@
 	GtkWidget *group_combo;
 	GtkWidget *entries_box;
 	GtkSizeGroup *sg;
+	GtkWidget *autojoin;
+	GtkWidget *persistent;
 
 	GList *entries;
 
@@ -138,13 +140,23 @@
 static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender);
 static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node);
 static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded);
-
-struct _pidgin_blist_node {
+static void pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node);
+
+typedef enum {
+	PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE    =  1 << 0,  /* Whether there's pending message in a conversation */
+} PidginBlistNodeFlags;
+
+typedef struct _pidgin_blist_node {
 	GtkTreeRowReference *row;
 	gboolean contact_expanded;
 	gboolean recent_signonoff;
 	gint recent_signonoff_timer;
-};
+	struct {
+		PurpleConversation *conv;
+		time_t last_message;          /* timestamp for last displayed message */
+		PidginBlistNodeFlags flags;
+	} conv;
+} PidginBlistNode;
 
 static char dim_grey_string[8] = "";
 static char *dim_grey()
@@ -306,12 +318,36 @@
 	serv_send_file(b->account->gc, b->name, NULL);
 }
 
+static void gtk_blist_menu_move_to_cb(GtkWidget *w, PurpleBlistNode *node)
+{
+	PurpleGroup *group = g_object_get_data(G_OBJECT(w), "groupnode");
+	purple_blist_add_contact((PurpleContact *)node, group, NULL);
+
+}
+
 static void gtk_blist_menu_autojoin_cb(GtkWidget *w, PurpleChat *chat)
 {
 	purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-autojoin",
 			gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)));
 }
 
+static void gtk_blist_menu_persistent_cb(GtkWidget *w, PurpleChat *chat)
+{
+	purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent",
+			gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)));
+}
+
+static PurpleConversation *
+find_conversation_with_buddy(PurpleBuddy *buddy)
+{
+	PidginBlistNode *ui = buddy->node.ui_data;
+	if (ui)
+		return ui->conv.conv;
+	return purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+									     purple_buddy_get_name(buddy),
+									     purple_buddy_get_account(buddy));
+}
+
 static void gtk_blist_join_chat(PurpleChat *chat)
 {
 	PurpleConversation *conv;
@@ -395,6 +431,105 @@
 }
 #endif
 
+static void
+gtk_blist_do_personize(GList *merges)
+{
+	PurpleBlistNode *contact = NULL;
+	int max = 0;
+	GList *tmp;
+
+	/* First, we find the contact to merge the rest of the buddies into.
+ 	 * This will be the contact with the most buddies in it; ties are broken
+ 	 * by which contact is higher in the list
+ 	 */
+	for (tmp = merges; tmp; tmp = tmp->next) {
+		PurpleBlistNode *node = tmp->data;
+		PurpleBlistNode *b;
+		int i = 0;
+
+		if (node->type == PURPLE_BLIST_BUDDY_NODE)
+			node = node->parent;
+
+		if (node->type != PURPLE_BLIST_CONTACT_NODE)
+			continue;
+		
+
+		for (b = node->child; b; b = b->next)
+			i++;
+		if (i > max) {
+			contact = node;
+			max = i;
+		}
+	}
+
+	if (contact == NULL)
+		return;
+
+	/* Merge all those buddies into this contact */
+	for (tmp = merges; tmp; tmp = tmp->next) {
+		PurpleBlistNode *node = tmp->data;
+		if (node->type == PURPLE_BLIST_BUDDY_NODE)
+			node = node->parent;
+
+		if (node == contact)
+			continue;
+
+		purple_blist_merge_contact((PurpleContact *)node, contact);
+	}
+
+	/* And show the expanded contact, so the people know what's going on */
+	pidgin_blist_expand_contact_cb(NULL, contact);
+	g_list_free(merges);
+}
+
+static void
+gtk_blist_auto_personize(PurpleBlistNode *group, const char *alias)
+{
+	PurpleBlistNode *contact;
+	PurpleBlistNode *buddy;
+	GList *merges = NULL;
+	int i = 0;
+	char *a = g_utf8_casefold(alias, -1);
+
+	for (contact = group->child; contact; contact = contact->next) {
+		char *node_alias;
+		if (contact->type != PURPLE_BLIST_CONTACT_NODE)
+			continue;
+
+		node_alias = g_utf8_casefold(purple_contact_get_alias((PurpleContact *)contact), -1);
+		if (node_alias && !g_utf8_collate(node_alias, a)) {
+			merges = g_list_append(merges, contact);
+			i++;
+			g_free(node_alias);
+			continue;
+		}
+		g_free(node_alias);
+
+		for (buddy = contact->child; buddy; buddy = buddy->next) {
+			if (buddy->type != PURPLE_BLIST_BUDDY_NODE)
+				continue;
+
+			node_alias = g_utf8_casefold(purple_buddy_get_alias((PurpleBuddy *)buddy), -1);
+			if (node_alias && !g_utf8_collate(node_alias, a)) {
+				merges = g_list_append(merges, buddy);
+				i++;
+			}
+			g_free(node_alias);
+		}
+	}
+	g_free(a);
+	
+	if (i > 1)
+	{
+		char *msg = g_strdup_printf(ngettext("You have %d contact named %s. Would you like to merge them?", "You currently have %d contacts named %s. Would you like to merge them?", i), i, alias);
+		purple_request_action(NULL, NULL, msg, _("Merging these contacts will cause them to share a single entry on the buddy list and use a single conversation window. "
+							 "You can separate them again by choosing 'Expand' from the contact's context menu"), 0, NULL, NULL, NULL,
+				      merges, 2, _("_Merge"), PURPLE_CALLBACK(gtk_blist_do_personize), _("_Cancel"), PURPLE_CALLBACK(g_list_free));
+		g_free(msg);
+	} else
+		g_list_free(merges);
+}
+
 static void gtk_blist_renderer_edited_cb(GtkCellRendererText *text_rend, char *arg1,
 					 char *arg2, PurpleBuddyList *list)
 {
@@ -421,13 +556,14 @@
 				PurpleContact *contact = (PurpleContact *)node;
 				struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data;
 
-				if (contact->alias || gtknode->contact_expanded)
+				if (contact->alias || gtknode->contact_expanded) {
 					purple_blist_alias_contact(contact, arg2);
-				else
-				{
+					gtk_blist_auto_personize(node->parent, arg2);
+				} else {
 					PurpleBuddy *buddy = purple_contact_get_priority_buddy(contact);
 					purple_blist_alias_buddy(buddy, arg2);
 					serv_alias_buddy(buddy);
+					gtk_blist_auto_personize(node->parent, arg2);
 				}
 			}
 			break;
@@ -435,6 +571,7 @@
 		case PURPLE_BLIST_BUDDY_NODE:
 			purple_blist_alias_buddy((PurpleBuddy*)node, arg2);
 			serv_alias_buddy((PurpleBuddy *)node);
+			gtk_blist_auto_personize(node->parent->parent, arg2);
 			break;
 		case PURPLE_BLIST_GROUP_NODE:
 			dest = purple_find_group(arg2);
@@ -525,6 +662,26 @@
 	}
 }
 
+static void gtk_blist_menu_showoffline_cb(GtkWidget *w, PurpleBlistNode *node)
+{
+	if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+	{
+		purple_blist_node_set_bool(node, "show_offline",
+		                           !purple_blist_node_get_bool(node, "show_offline"));
+	}
+	else if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+	{
+		PurpleBlistNode *bnode;
+		gboolean setting = !purple_blist_node_get_bool(node, "show_offline");
+
+		purple_blist_node_set_bool(node, "show_offline", setting);
+		for (bnode = node->child; bnode != NULL; bnode = bnode->next) {
+			purple_blist_node_set_bool(bnode, "show_offline", setting);
+		}
+	}
+	pidgin_blist_update(purple_get_blist(), node);
+}
+
 static void gtk_blist_show_systemlog_cb()
 {
 	pidgin_syslog_show();
@@ -1119,17 +1276,49 @@
 	g_list_free(ll);
 }
 
+
+
+static void
+pidgin_append_blist_node_move_to_menu(GtkWidget *menu, PurpleBlistNode *node)
+{
+	GtkWidget *submenu;
+	GtkWidget *menuitem;
+	PurpleBlistNode *group;
+
+	menuitem = gtk_menu_item_new_with_label(_("Move to"));
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+	gtk_widget_show(menuitem);
+
+	submenu = gtk_menu_new();
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+
+	for (group = purple_blist_get_root(); group; group = group->next) {
+		if (group->type != PURPLE_BLIST_GROUP_NODE)
+			continue;
+		if (group == node->parent)
+			continue;
+		menuitem = pidgin_new_item_from_stock(submenu, purple_group_get_name((PurpleGroup *)group), NULL,
+						      G_CALLBACK(gtk_blist_menu_move_to_cb), node, 0, 0, NULL);
+		g_object_set_data(G_OBJECT(menuitem), "groupnode", group);
+	}
+	gtk_widget_show_all(submenu);
+}
+
 void
 pidgin_blist_make_buddy_menu(GtkWidget *menu, PurpleBuddy *buddy, gboolean sub) {
 	PurplePluginProtocolInfo *prpl_info;
 	PurpleContact *contact;
+	PurpleBlistNode *node;
 	gboolean contact_expanded = FALSE;
+	gboolean show_offline = FALSE;
 
 	g_return_if_fail(menu);
 	g_return_if_fail(buddy);
 
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(buddy->account->gc->prpl);
 
+	node = (PurpleBlistNode*)buddy;
+
 	contact = purple_buddy_get_contact(buddy);
 	if (contact) {
 		contact_expanded = ((struct _pidgin_blist_node *)(((PurpleBlistNode*)contact)->ui_data))->contact_expanded;
@@ -1145,17 +1334,17 @@
 		if (!prpl_info->can_receive_file ||
 			prpl_info->can_receive_file(buddy->account->gc, buddy->name))
 		{
-			pidgin_new_item_from_stock(menu, _("_Send File"),
+			pidgin_new_item_from_stock(menu, _("_Send File..."),
 									 PIDGIN_STOCK_TOOLBAR_SEND_FILE,
 									 G_CALLBACK(gtk_blist_menu_send_file_cb),
 									 buddy, 0, 0, NULL);
 		}
 	}
 
-	pidgin_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL,
+	pidgin_new_item_from_stock(menu, _("Add Buddy _Pounce..."), NULL,
 			G_CALLBACK(gtk_blist_menu_bp_cb), buddy, 0, 0, NULL);
 
-	if (((PurpleBlistNode*)buddy)->parent && ((PurpleBlistNode*)buddy)->parent->child->next && 
+	if (node->parent && node->parent->child->next && 
 	      !sub && !contact_expanded) {
 		pidgin_new_item_from_stock(menu, _("View _Log"), NULL,
 				G_CALLBACK(gtk_blist_menu_showlog_cb),
@@ -1165,15 +1354,22 @@
 				G_CALLBACK(gtk_blist_menu_showlog_cb), buddy, 0, 0, NULL);
 	}
 
-
-	pidgin_append_blist_node_proto_menu(menu, buddy->account->gc,
-										  (PurpleBlistNode *)buddy);
-	pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)buddy);
-
-	if (((PurpleBlistNode*)buddy)->parent && ((PurpleBlistNode*)buddy)->parent->child->next && 
+	if (!(purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE)) {
+		show_offline = purple_blist_node_get_bool(node, "show_offline");
+		pidgin_new_item_from_stock(menu, show_offline ? _("Hide when offline") : _("Show when offline"),
+				NULL, G_CALLBACK(gtk_blist_menu_showoffline_cb), node, 0, 0, NULL);
+	}
+
+	pidgin_append_blist_node_proto_menu(menu, buddy->account->gc, node);
+	pidgin_append_blist_node_extended_menu(menu, node);
+
+	if (!contact_expanded)
+		pidgin_append_blist_node_move_to_menu(menu, (PurpleBlistNode *)contact);
+
+	if (node->parent && node->parent->child->next && 
               !sub && !contact_expanded) {
 		pidgin_separator(menu);
-		pidgin_append_blist_node_privacy_menu(menu, (PurpleBlistNode *)buddy);
+		pidgin_append_blist_node_privacy_menu(menu, node);
 		pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS,
 				G_CALLBACK(gtk_blist_menu_alias_cb),
 				contact, 0, 0, NULL);
@@ -1182,7 +1378,7 @@
 				contact, 0, 0, NULL);
 	} else if (!sub || contact_expanded) {
 		pidgin_separator(menu);
-		pidgin_append_blist_node_privacy_menu(menu, (PurpleBlistNode *)buddy);
+		pidgin_append_blist_node_privacy_menu(menu, node);
 		pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS,
 				G_CALLBACK(gtk_blist_menu_alias_cb), buddy, 0, 0, NULL);
 		pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE,
@@ -1235,10 +1431,10 @@
 	GtkWidget *item;
 
 	menu = gtk_menu_new();
-	item = pidgin_new_item_from_stock(menu, _("Add a _Buddy"), GTK_STOCK_ADD,
+	item = pidgin_new_item_from_stock(menu, _("Add _Buddy..."), GTK_STOCK_ADD,
 				 G_CALLBACK(pidgin_blist_add_buddy_cb), node, 0, 0, NULL);
 	gtk_widget_set_sensitive(item, purple_connections_get_all() != NULL);
-	item = pidgin_new_item_from_stock(menu, _("Add a C_hat"), GTK_STOCK_ADD,
+	item = pidgin_new_item_from_stock(menu, _("Add C_hat..."), GTK_STOCK_ADD,
 				 G_CALLBACK(pidgin_blist_add_chat_cb), node, 0, 0, NULL);
 	gtk_widget_set_sensitive(item, pidgin_blist_joinchat_is_showable());
 	pidgin_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE,
@@ -1256,16 +1452,19 @@
 create_chat_menu(PurpleBlistNode *node, PurpleChat *c)
 {
 	GtkWidget *menu;
-	gboolean autojoin;
+	gboolean autojoin, persistent;
 
 	menu = gtk_menu_new();
 	autojoin = (purple_blist_node_get_bool(node, "gtk-autojoin") ||
 			(purple_blist_node_get_string(node, "gtk-autojoin") != NULL));
+	persistent = purple_blist_node_get_bool(node, "gtk-persistent");
 
 	pidgin_new_item_from_stock(menu, _("_Join"), PIDGIN_STOCK_CHAT,
 			G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL);
 	pidgin_new_check_item(menu, _("Auto-Join"),
 			G_CALLBACK(gtk_blist_menu_autojoin_cb), node, autojoin);
+	pidgin_new_check_item(menu, _("Persistent"),
+			G_CALLBACK(gtk_blist_menu_persistent_cb), node, persistent);
 	pidgin_new_item_from_stock(menu, _("View _Log"), NULL,
 			G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL);
 
@@ -1307,7 +1506,6 @@
 				 node, 0, 0, NULL);
 
 	pidgin_append_blist_node_extended_menu(menu, node);
-
 	return menu;
 }
 
@@ -2888,7 +3086,7 @@
 
 	/* Accounts menu */
 	{ N_("/_Accounts"), NULL, NULL, 0, "<Branch>", NULL },
-	{ N_("/Accounts/Add\\/Edit"), "<CTL>A", pidgin_accounts_window_show, 0, "<Item>", NULL },
+	{ N_("/Accounts/Manage"), "<CTL>A", pidgin_accounts_window_show, 0, "<Item>", NULL },
 
 	/* Tools */
 	{ N_("/_Tools"), NULL, NULL, 0, "<Branch>", NULL },
@@ -3145,8 +3343,6 @@
 	GdkPixbuf *ret;
 	PurplePresence *p;
 
-
-
 	if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		if(!gtknode->contact_expanded) {
 			buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
@@ -3188,6 +3384,13 @@
 		return ret;
 	}
 
+	if (purple_status_get_attr_string(purple_presence_get_active_status(p), PURPLE_TUNE_TITLE)) {
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL);
+		ret = gdk_pixbuf_new_from_file(path, NULL);
+		g_free(path);
+		return ret;
+	}
+
 	prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
 	if (!prpl)
 		return NULL;
@@ -3261,17 +3464,17 @@
 	}
 
 	if(buddy) {
-	  	PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-									     purple_buddy_get_name(buddy),
-									     purple_buddy_get_account(buddy));
+	  	PurpleConversation *conv = find_conversation_with_buddy(buddy);
 		PurplePresence *p;
 		gboolean trans;
 
 		if(conv != NULL) {
 			PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
-			if((gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) && size == PIDGIN_STATUS_ICON_SMALL) {
-				return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_MESSAGE,
-							       icon_size, "GtkTreeView");
+			if (gtkconv == NULL && size == PIDGIN_STATUS_ICON_SMALL) {
+				PidginBlistNode *ui = buddy->node.ui_data;
+				if (ui == NULL || (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE))
+					return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview),
+							PIDGIN_STOCK_STATUS_MESSAGE, icon_size, "GtkTreeView");
 			}
 		}
 
@@ -3327,16 +3530,17 @@
 	struct _pidgin_blist_node *gtkcontactnode = NULL;
 	char *idletime = NULL, *statustext = NULL;
 	time_t t;
-	PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-								     purple_buddy_get_name(b),
-								     purple_buddy_get_account(b));
-	PidginConversation *gtkconv;
+	PurpleConversation *conv = find_conversation_with_buddy(b);
 	gboolean hidden_conv = FALSE;
 
-	if(conv != NULL) {
-		gtkconv = PIDGIN_CONVERSATION(conv);
-		if(gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) {
-			hidden_conv = TRUE;
+	if (conv != NULL) {
+		PidginBlistNode *ui = b->node.ui_data;
+		if (ui) {
+			if (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE)
+				hidden_conv = TRUE;
+		} else {
+			if (PIDGIN_CONVERSATION(conv) == NULL)
+				hidden_conv = TRUE;
 		}
 	}
 
@@ -3671,7 +3875,7 @@
 		menu = NULL;
 	}
 
-	convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0);
+	convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0);
 	if (!convs)
 		/* no conversations added, don't show the menu */
 		return;
@@ -3727,7 +3931,7 @@
 		gtkblist->menutrayicon = NULL;
 	}
 
-	convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0);
+	convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0);
 	if (convs) {
 		GtkWidget *img = NULL;
 		GString *tooltip_text = NULL;
@@ -3735,14 +3939,10 @@
 		tooltip_text = g_string_new("");
 		l = convs;
 		while (l != NULL) {
-			if (PIDGIN_IS_PIDGIN_CONVERSATION(l->data)) {
-				PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)l->data);
-
-				g_string_append_printf(tooltip_text,
-						ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count),
-						gtkconv->unseen_count,
-						gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)));
-			}
+			int count = GPOINTER_TO_INT(purple_conversation_get_data(l->data, "unseen-count"));
+			g_string_append_printf(tooltip_text,
+					ngettext("%d unread message from %s\n", "%d unread messages from %s\n", count),
+					count, purple_conversation_get_name(l->data));
 			l = l->next;
 		}
 		if(tooltip_text->len > 0) {
@@ -3770,6 +3970,88 @@
 	conversation_updated_cb(conv, PURPLE_CONV_UPDATE_UNSEEN, gtkblist);
 }
 
+static void
+conversation_deleted_update_ui_cb(PurpleConversation *conv, struct _pidgin_blist_node *ui)
+{
+	if (ui->conv.conv != conv)
+		return;
+	ui->conv.conv = NULL;
+	ui->conv.flags = 0;
+	ui->conv.last_message = 0;
+}
+
+static void
+written_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message,
+		PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node)
+{
+	PidginBlistNode *ui = node->ui_data;
+	if (ui->conv.conv != conv || PIDGIN_CONVERSATION(conv) ||
+			!(flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)))
+		return;
+	ui->conv.flags |= PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE;
+	ui->conv.last_message = time(NULL);    /* XXX: for lack of better data */
+	pidgin_blist_update(purple_get_blist(), node);
+}
+
+static void
+displayed_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message,
+		PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node)
+{
+	PidginBlistNode *ui = node->ui_data;
+	if (ui->conv.conv != conv)
+		return;
+	ui->conv.flags &= ~PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE;
+	pidgin_blist_update(purple_get_blist(), node);
+}
+
+static void
+conversation_created_cb(PurpleConversation *conv, PidginBuddyList *gtkblist)
+{
+	switch (conv->type) {
+		case PURPLE_CONV_TYPE_IM:
+			{
+				GSList *buddies = purple_find_buddies(conv->account, conv->name);
+				while (buddies) {
+					PurpleBlistNode *buddy = buddies->data;
+					struct _pidgin_blist_node *ui = buddy->ui_data;
+					buddies = g_slist_delete_link(buddies, buddies);
+					if (!ui)
+						continue;
+					ui->conv.conv = conv;
+					ui->conv.flags = 0;
+					ui->conv.last_message = 0;
+					purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
+							ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui);
+					purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg",
+							ui, PURPLE_CALLBACK(written_msg_update_ui_cb), buddy);
+					purple_signal_connect(pidgin_conversations_get_handle(), "displayed-im-msg",
+							ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), buddy);
+				}
+			}
+		case PURPLE_CONV_TYPE_CHAT:
+			{
+				PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name);
+				struct _pidgin_blist_node *ui;
+				if (!chat)
+					break;
+				ui = chat->node.ui_data;
+				if (!ui)
+					break;
+				ui->conv.conv = conv;
+				ui->conv.flags = 0;
+				ui->conv.last_message = 0;
+				purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
+						ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui);
+				purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg",
+						ui, PURPLE_CALLBACK(written_msg_update_ui_cb), chat);
+				purple_signal_connect(pidgin_conversations_get_handle(), "displayed-chat-msg",
+						ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), chat);
+			}
+		default:
+			break;
+	}
+}
+
 /**********************************************************************************
  * Public API Functions                                                           *
  **********************************************************************************/
@@ -4334,7 +4616,7 @@
 	tmp = g_strdup_printf(_("<span weight='bold' size='larger'>Welcome to %s!</span>\n\n"
 
 					       "You have no accounts enabled. Enable your IM accounts from the "
-					       "<b>Accounts</b> window at <b>Accounts->Add/Edit</b>. Once you "
+					       "<b>Accounts</b> window at <b>Accounts->Manage</b>. Once you "
 					       "enable accounts, you'll be able to sign on, set your status, "
 					       "and talk to your friends."), PIDGIN_NAME);
 	pretty = pidgin_make_pretty_arrows(tmp);
@@ -4683,6 +4965,9 @@
 	purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
 						gtkblist, PURPLE_CALLBACK(conversation_deleting_cb),
 						gtkblist);
+	purple_signal_connect(purple_conversations_get_handle(), "conversation-created",
+			gtkblist, PURPLE_CALLBACK(conversation_created_cb),
+			gtkblist);
 
 	gtk_widget_hide(gtkblist->headline_hbox);
 	gtk_widget_hide(gtkblist->error_buttons);
@@ -4789,6 +5074,7 @@
 		if(gtknode->recent_signonoff_timer > 0)
 			purple_timeout_remove(gtknode->recent_signonoff_timer);
 
+		purple_signals_disconnect_by_handle(node->ui_data);
 		g_free(node->ui_data);
 		node->ui_data = NULL;
 	}
@@ -4953,7 +5239,7 @@
 				   STATUS_ICON_COLUMN, NULL,
 				   NAME_COLUMN, title,
 				   NODE_COLUMN, gnode,
-				   BGCOLOR_COLUMN, &bgcolor,
+	/* 			   BGCOLOR_COLUMN, &bgcolor,     */
 				   GROUP_EXPANDER_COLUMN, TRUE,
 				   GROUP_EXPANDER_VISIBLE_COLUMN, TRUE,
 				   CONTACT_EXPANDER_VISIBLE_COLUMN, FALSE,
@@ -5213,14 +5499,17 @@
 		GdkPixbuf *emblem;
 		char *mark;
 		gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
-		const char *name = purple_chat_get_name(chat);
-		PurpleConversation *conv =
-				purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, chat->account);
-		gboolean hidden = (conv && !PIDGIN_CONVERSATION(conv));
-
-		if(!insert_node(list, node, &iter))
+		PidginBlistNode *ui;
+		PurpleConversation *conv;
+		gboolean hidden;
+
+		if (!insert_node(list, node, &iter))
 			return;
 
+		ui = node->ui_data;
+		conv = ui->conv.conv;
+		hidden = (conv && (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE));
+
 		status = pidgin_blist_get_status_icon(node,
 				 PIDGIN_STATUS_ICON_SMALL);
 		emblem = pidgin_blist_get_emblem(node);
@@ -5527,7 +5816,7 @@
 	gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
 
 	/* Set up stuff for the account box */
-	label = gtk_label_new_with_mnemonic(_("_Account:"));
+	label = gtk_label_new_with_mnemonic(_("A_ccount:"));
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
 
@@ -5644,6 +5933,12 @@
 		purple_blist_add_chat(chat, group, NULL);
 	}
 
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->autojoin)))
+		purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-autojoin", TRUE);
+
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->persistent)))
+		purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent", TRUE);
+
 	gtk_widget_destroy(data->window);
 	g_free(data->default_chat_name);
 	g_list_free(data->entries);
@@ -5938,6 +6233,11 @@
 	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_BIN(data->group_combo)->child);
 	pidgin_set_accessible_label (data->group_combo, label);
 	gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0);
+	
+	data->autojoin = gtk_check_button_new_with_mnemonic(_("Autojoin when account becomes online."));
+	data->persistent = gtk_check_button_new_with_mnemonic(_("Hide chat when the window is closed."));
+	gtk_box_pack_start(GTK_BOX(vbox), data->autojoin, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), data->persistent, FALSE, FALSE, 0);
 
 	g_signal_connect(G_OBJECT(data->window), "response",
 					 G_CALLBACK(add_chat_resp_cb), data);
@@ -6586,7 +6886,7 @@
 	for (l = gtk_container_get_children(GTK_CONTAINER(accountmenu)); l; l = g_list_delete_link(l, l)) {
 		menuitem = l->data;
 
-		if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Add\\/Edit")))
+		if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Manage")))
 			gtk_widget_destroy(menuitem);
 	}
 

mercurial