im.pidgin.cpw.resiak.disconnectreason: 5fc66358cde858aca7728721cb177c53739f524b

resiak at soc.pidgin.im resiak at soc.pidgin.im
Wed Oct 31 11:41:03 EDT 2007


-----------------------------------------------------------------
Revision: 5fc66358cde858aca7728721cb177c53739f524b
Ancestor: 47fda7b123eae2b717ca991138186a53e6336d44
Author: resiak at soc.pidgin.im
Date: 2007-10-31T15:19:21
Branch: im.pidgin.cpw.resiak.disconnectreason

Modified files:
        pidgin/gtkblist.c pidgin/gtkblist.h

ChangeLog: 

Group all NAME_IN_USE errors together into one neat mini-dialog on the buddy
list, with a Re-enable button to re-enable all the accounts that were knocked
offline, and an Ignore button to dismiss the mini-dialog.

References #168

-------------- next part --------------
============================================================
--- pidgin/gtkblist.c	3ddbf234754f273de4a067f29bf39d81ebebbd5e
+++ pidgin/gtkblist.c	2cb6c8965df6f080b59abd936619edecfd138884
@@ -107,7 +107,24 @@ typedef struct
 	GList *entries;
 } PidginJoinChatData;
 
+typedef struct
+{
+	/** List of #PurpleAccount that were disconnected with
+	 * #PURPLE_CONNECTION_ERROR_NAME_IN_USE that have not been dealt with
+	 * by the user.
+	 */
+	GList *accounts_signed_on_elsewhere;
+	/** Pointer to the mini-dialog about having signed on elsewhere, if one
+	 * is showing; @c NULL otherwise.
+	 */
+	GtkWidget *signed_on_elsewhere_minidialog;
+	GtkLabel *signed_on_elsewhere_minidialog_title;
+	GtkVBox *signed_on_elsewhere_minidialog_accounts;
+} PidginBuddyListPrivate;
 
+#define PIDGIN_BUDDY_LIST_GET_PRIVATE(list) \
+	((PidginBuddyListPrivate *)((list)->priv))
+
 static GtkWidget *accountmenu = NULL;
 
 static guint visibility_manager_count = 0;
@@ -4067,10 +4084,13 @@ static void pidgin_blist_new_list(Purple
 static void pidgin_blist_new_list(PurpleBuddyList *blist)
 {
 	PidginBuddyList *gtkblist;
+	PidginBuddyListPrivate *priv;
 
 	gtkblist = g_new0(PidginBuddyList, 1);
 	gtkblist->connection_errors = g_hash_table_new_full(g_direct_hash,
 												g_direct_equal, NULL, g_free);
+	gtkblist->priv = priv = g_new0(PidginBuddyListPrivate, 1);
+
 	blist->ui_data = gtkblist;
 }
 
@@ -4296,7 +4316,37 @@ headline_box_press_cb(GtkWidget *widget,
 /* Connection error handling stuff */
 /***********************************/
 
+#define OBJECT_DATA_KEY_ACCOUNT "account"
+
+static gboolean
+find_account_widget(GObject *widget,
+                    PurpleAccount *account)
+{
+	if (g_object_get_data(widget, OBJECT_DATA_KEY_ACCOUNT) == account)
+		return 0; /* found */
+	else
+		return 1;
+}
+
 static void
+pack_prpl_icon_start(GtkWidget *box,
+                     PurpleAccount *account)
+{
+	GdkPixbuf *pixbuf;
+	GtkWidget *image;
+
+	pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+	if (pixbuf != NULL) {
+		image = gtk_image_new_from_pixbuf(pixbuf);
+		g_object_unref(pixbuf);
+
+		gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
+	}
+}
+
+/* Generic error buttons */
+
+static void
 ce_modify_account_cb(PurpleAccount *account)
 {
 	pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account);
@@ -4356,16 +4406,13 @@ connection_error_button_clicked_cb(GtkBu
 	remove_error_button(button);
 }
 
-#define BUTTON_DATA_KEY_ACCOUNT "account"
-
 /* Add a button showing the connection error message */
 static void
 add_generic_connection_error_button(PurpleAccount *account,
                                     const PurpleConnectionErrorInfo *err)
 {
 	gchar *escaped, *text;
-	GtkWidget *button, *label, *image, *hbox;
-	GdkPixbuf *pixbuf;
+	GtkWidget *button, *label, *hbox;
 
 	escaped = g_markup_escape_text(err->description, -1);
 	text = g_strdup_printf(_("<span color=\"red\">%s disconnected: %s</span>"),
@@ -4375,15 +4422,8 @@ add_generic_connection_error_button(Purp
 
 	hbox = gtk_hbox_new(FALSE, 6);
 
-	/* Create the icon */
-	pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
-	if (pixbuf != NULL) {
-		image = gtk_image_new_from_pixbuf(pixbuf);
-		g_object_unref(pixbuf);
+	pack_prpl_icon_start(hbox, account);
 
-		gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
-	}
-
 	/* Create the text */
 	label = gtk_label_new(NULL);
 	gtk_label_set_markup(GTK_LABEL(label), text);
@@ -4395,7 +4435,7 @@ add_generic_connection_error_button(Purp
 
 	/* Create the actual button and put the icon and text on it */
 	button = gtk_button_new();
-	g_object_set_data(G_OBJECT(button), BUTTON_DATA_KEY_ACCOUNT, account);
+	g_object_set_data(G_OBJECT(button), OBJECT_DATA_KEY_ACCOUNT, account);
 	gtk_container_add(GTK_CONTAINER(button), hbox);
 	g_signal_connect(G_OBJECT(button), "clicked",
 	                 G_CALLBACK(connection_error_button_clicked_cb),
@@ -4407,23 +4447,13 @@ add_generic_connection_error_button(Purp
 	gtk_widget_show_all(gtkblist->error_buttons);
 }
 
-static gboolean
-find_account_button(GObject *button,
-                    PurpleAccount *account)
-{
-	if (g_object_get_data(button, BUTTON_DATA_KEY_ACCOUNT) == account)
-		return 0; /* found */
-	else
-		return 1;
-}
-
 static void
 remove_generic_connection_error_button(PurpleAccount *account)
 {
 	GtkButton *button;
 	GList *l = NULL;
 	GList *children = gtk_container_get_children(GTK_CONTAINER(gtkblist->error_buttons));
-	l = g_list_find_custom(children, account, (GCompareFunc) find_account_button);
+	l = g_list_find_custom(children, account, (GCompareFunc) find_account_widget);
 	if (l) { /* it may have already been removed by being clicked on */
 		button = GTK_BUTTON(l->data);
 		remove_error_button(button);
@@ -4431,7 +4461,232 @@ remove_generic_connection_error_button(P
 	g_list_free(children);
 }
 
+
+/* Notifications about accounts which were disconnected with
+ * PURPLE_CONNECTION_ERROR_NAME_IN_USE
+ */
+
 static void
+destroy_signed_on_elsewhere_minidialog(PidginBuddyListPrivate *priv)
+{
+	if(priv->signed_on_elsewhere_minidialog == NULL)
+		return;
+
+	gtk_widget_destroy(priv->signed_on_elsewhere_minidialog);
+	priv->signed_on_elsewhere_minidialog = NULL;
+	priv->signed_on_elsewhere_minidialog_accounts = NULL;
+	priv->signed_on_elsewhere_minidialog_title = NULL;
+}
+
+static void
+reconnect_elsewhere_accounts(PidginBuddyList *gtkblist)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	GList *l;
+	PurpleAccount *account;
+	for (l = priv->accounts_signed_on_elsewhere; l; l = l->next) {
+		account = l->data;
+		purple_account_set_enabled(account, purple_core_get_ui(), TRUE);
+	}
+	g_list_free(priv->accounts_signed_on_elsewhere);
+	priv->accounts_signed_on_elsewhere = NULL;
+
+	destroy_signed_on_elsewhere_minidialog(priv);
+}
+
+static void
+ignore_elsewhere_accounts(PidginBuddyList *gtkblist)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+
+	g_list_free(priv->accounts_signed_on_elsewhere);
+	priv->accounts_signed_on_elsewhere = NULL;
+
+	destroy_signed_on_elsewhere_minidialog(priv);
+}
+
+static GtkWidget *
+make_elsewhere_minidialog_button(const char *text,
+                                 GCallback callback)
+{
+	GtkWidget *button = gtk_button_new();
+	GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
+	GtkWidget *label = gtk_label_new(NULL);
+	char *button_text =
+		g_strdup_printf("<span size=\"smaller\">%s</span>", text);
+
+	gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+
+	g_signal_connect_swapped(G_OBJECT(button), "clicked", callback,
+		gtkblist);
+	gtk_container_add(GTK_CONTAINER(button), hbox);
+
+	gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), button_text);
+	g_free(button_text);
+
+	gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
+	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+
+	return button;
+}
+
+static void
+create_signed_on_elsewhere_minidialog(PidginBuddyList *gtkblist)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	GtkWidget *dialog, *vbox, *hbox, *label, *img, *button;
+	GtkSizeGroup *sg;
+
+	if(priv->signed_on_elsewhere_minidialog)
+		return;
+
+	dialog = gtk_vbox_new(FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BOX_SPACE);
+	priv->signed_on_elsewhere_minidialog = dialog;
+
+	/* HBox to hold error icon and title */
+	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+
+	img = gtk_image_new_from_stock(PIDGIN_STOCK_DISCONNECT,
+		gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
+	gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
+	if (img)
+		gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
+
+	label = gtk_label_new(NULL);
+	priv->signed_on_elsewhere_minidialog_title = GTK_LABEL(label);
+	gtk_widget_set_size_request(label,
+		purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width")-25, -1);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(dialog), hbox, FALSE, FALSE, 0);
+
+	/* VBox to hold "[prplicon] account at server.com" widgets */
+	vbox = gtk_vbox_new(FALSE, 0);
+	priv->signed_on_elsewhere_minidialog_accounts = GTK_VBOX(vbox);
+	gtk_box_pack_start(GTK_BOX(dialog), vbox, FALSE, FALSE, 0);
+
+	/* HBox to hold buttons */
+	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
+
+	button = make_elsewhere_minidialog_button(_("Re-enable"),
+		(GCallback) reconnect_elsewhere_accounts);
+	gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_size_group_add_widget(sg, button);
+
+	button = make_elsewhere_minidialog_button(_("Ignore"),
+		(GCallback) ignore_elsewhere_accounts);
+	gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_size_group_add_widget(sg, button);
+
+	gtk_box_pack_start(GTK_BOX(dialog), hbox, FALSE, FALSE, 0);
+	pidgin_blist_add_alert(dialog);
+}
+
+static void
+update_signed_on_elsewhere_minidialog_title(void)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	guint length;
+	char *title, *title_markup;
+
+	length = g_list_length(priv->accounts_signed_on_elsewhere);
+	if (length == 0)
+	{
+		destroy_signed_on_elsewhere_minidialog(priv);
+		return;
+	}
+
+	create_signed_on_elsewhere_minidialog(gtkblist);
+
+	title = g_strdup_printf(
+		ngettext("%d account was disabled because you signed on from another location.",
+			 "%d accounts were disabled because you signed on from another location.",
+			 length),
+		length);
+	title_markup = g_strdup_printf("<span weight=\"bold\" size=\"smaller\">%s</span>", title);
+
+	gtk_label_set_markup(priv->signed_on_elsewhere_minidialog_title, title_markup);
+
+	g_free(title_markup);
+	g_free(title);
+
+	gtk_widget_show_all(priv->signed_on_elsewhere_minidialog);
+}
+
+static GtkWidget *
+create_account_label(PurpleAccount *account)
+{
+	GtkWidget *hbox, *label;
+	const char *username = purple_account_get_username(account);
+	char *markup;
+
+	hbox = gtk_hbox_new(FALSE, 6);
+	g_object_set_data(G_OBJECT(hbox), OBJECT_DATA_KEY_ACCOUNT, account);
+
+	pack_prpl_icon_start(hbox, account);
+
+	label = gtk_label_new(NULL);
+	markup = g_strdup_printf("<span size=\"smaller\">%s</span>", username);
+	gtk_label_set_markup(GTK_LABEL(label), markup);
+	g_free(markup);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+#if GTK_CHECK_VERSION(2,6,0)
+	g_object_set(label, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+#endif
+	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+
+	return hbox;
+}
+
+static void
+add_to_signed_on_elsewhere(PurpleAccount *account)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	GtkWidget *account_label;
+	if(g_list_find(priv->accounts_signed_on_elsewhere, account) != NULL)
+		return;
+
+	priv->accounts_signed_on_elsewhere =
+		g_list_prepend(priv->accounts_signed_on_elsewhere, account);
+
+	update_signed_on_elsewhere_minidialog_title();
+
+	account_label = create_account_label(account);
+	gtk_box_pack_start(GTK_BOX(priv->signed_on_elsewhere_minidialog_accounts),
+		account_label, FALSE, FALSE, 0);
+	gtk_widget_show_all(account_label);
+}
+
+static void
+remove_from_signed_on_elsewhere(PurpleAccount *account)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	if(g_list_find(priv->accounts_signed_on_elsewhere, account) == NULL)
+		return;
+
+	priv->accounts_signed_on_elsewhere =
+		g_list_remove(priv->accounts_signed_on_elsewhere, account);
+
+	if(priv->signed_on_elsewhere_minidialog_accounts) {
+		GList *children = gtk_container_get_children(
+			GTK_CONTAINER(priv->signed_on_elsewhere_minidialog_accounts));
+		GList *l = g_list_find_custom(children, account,
+			(GCompareFunc) find_account_widget);
+		if (l)
+			gtk_widget_destroy(GTK_WIDGET(l->data));
+
+		g_list_free(children);
+	}
+
+	update_signed_on_elsewhere_minidialog_title();
+}
+
+/* Call appropriate error notification code based on error types */
+
+static void
 update_account_error_state(PurpleAccount *account,
                            const PurpleConnectionErrorInfo *old,
                            const PurpleConnectionErrorInfo *new,
@@ -4444,11 +4699,17 @@ update_account_error_state(PurpleAccount
 		pidgin_blist_update_account_error_state(account, NULL);
 
 	if (old) {
-		remove_generic_connection_error_button(account);
+		if(old->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE)
+			remove_from_signed_on_elsewhere(account);
+		else
+			remove_generic_connection_error_button(account);
 	}
 
 	if (new) {
-		add_generic_connection_error_button(account, new);
+		if(new->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE)
+			add_to_signed_on_elsewhere(account);
+		else
+			add_generic_connection_error_button(account, new);
 	}
 }
 
@@ -5662,6 +5923,8 @@ static void pidgin_blist_destroy(PurpleB
 
 static void pidgin_blist_destroy(PurpleBuddyList *list)
 {
+	PidginBuddyListPrivate *priv;
+
 	if (!gtkblist)
 		return;
 
@@ -5695,6 +5958,9 @@ static void pidgin_blist_destroy(PurpleB
 	gtkblist->hand_cursor = NULL;
 	gtkblist->arrow_cursor = NULL;
 
+	priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	g_free(priv);
+
 	g_free(gtkblist);
 	accountmenu = NULL;
 	gtkblist = NULL;
============================================================
--- pidgin/gtkblist.h	f688dd28ed3f798d1e3d3de35f588963685a29f8
+++ pidgin/gtkblist.h	1432074dfa3f5aab3563e5db7c1ee41e7e759fa9
@@ -126,6 +126,8 @@ struct _PidginBuddyList {
 	GtkWidget *error_buttons;        /**< Box containing the connection error buttons */
 	GtkWidget *statusbox;            /**< The status selector dropdown */
 	GdkPixbuf *empty_avatar;         /**< A 32x32 transparent pixbuf */
+
+	gpointer priv;                   /**< Pointer to opaque private data */
 };
 
 #define PIDGIN_BLIST(list) ((PidginBuddyList *)(list)->ui_data)


More information about the Commits mailing list