changeset 15033:ca46f41aa433

[gaim-migrate @ 17815] buddy-list alerts. I've made the two existing account ui_ops ('so-and-so has added you' and 'would you like to add so-and-so') use this. I'll follow up with a new 'so-and-so needs you to authorize him' ui_op, and replace the gaim_request_action() calls in the appropriate prpls committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Thu, 23 Nov 2006 08:37:07 +0000
parents ea6186360b68
children b28f5caf9445
files gtk/Makefile.am gtk/Makefile.mingw gtk/gtkaccount.c gtk/gtkblist.c gtk/gtkblist.h gtk/gtkscrollbook.c gtk/gtkscrollbook.h gtk/gtkutils.c gtk/gtkutils.h
diffstat 9 files changed, 441 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/gtk/Makefile.am	Wed Nov 22 21:33:51 2006 +0000
+++ b/gtk/Makefile.am	Thu Nov 23 08:37:07 2006 +0000
@@ -97,6 +97,7 @@
 	gtkrequest.c \
 	gtkroomlist.c \
 	gtksavedstatuses.c \
+	gtkscrollbook.c \
 	gtksession.c \
 	gtksound.c \
 	gtksourceiter.c \
@@ -143,6 +144,7 @@
 	gtkrequest.h \
 	gtkroomlist.h \
 	gtksavedstatuses.h \
+	gtkscrollbook.h \
 	gtksession.h \
 	gtksound.h \
 	gtksourceiter.h \
--- a/gtk/Makefile.mingw	Wed Nov 22 21:33:51 2006 +0000
+++ b/gtk/Makefile.mingw	Thu Nov 23 08:37:07 2006 +0000
@@ -82,6 +82,7 @@
 			gtkrequest.c \
 			gtkroomlist.c \
 			gtksavedstatuses.c \
+			gtkscrollbook.c \
 			gtksound.c \
 			gtksourceiter.c \
 			gtkstatusbox.c \
--- a/gtk/gtkaccount.c	Wed Nov 22 21:33:51 2006 +0000
+++ b/gtk/gtkaccount.c	Thu Nov 23 08:37:07 2006 +0000
@@ -2176,6 +2176,7 @@
 	GtkWidget *button;
 	int width, height;
 
+
 	if (accounts_window != NULL) {
 		gtk_window_present(GTK_WINDOW(accounts_window->window));
 		return;
@@ -2315,7 +2316,7 @@
 	                        : (gaim_connection_get_display_name(gc) != NULL
 	                           ? gaim_connection_get_display_name(gc)
 	                           : gaim_account_get_username(account))),
-	                       (msg != NULL ? ": " : "."),
+	                       (msg != NULL ? ":\n" : "."),
 	                       (msg != NULL ? msg  : ""));
 }
 
@@ -2326,12 +2327,14 @@
 {
 	char *buffer;
 	GaimConnection *gc;
+	GtkWidget *alert;
 
 	gc = gaim_account_get_connection(account);
 
 	buffer = make_info(account, gc, remote_user, id, alias, msg);
-
-	gaim_notify_info(NULL, NULL, buffer, NULL);
+	alert = gaim_gtk_make_mini_dialog(gc, GAIM_STOCK_DIALOG_INFO, buffer,
+					  NULL, NULL, _("Close"), NULL, NULL);
+	gaim_gtk_blist_add_alert(alert);
 
 	g_free(buffer);
 }
@@ -2344,6 +2347,7 @@
 	char *buffer;
 	GaimConnection *gc;
 	GaimGtkAccountAddUserData *data;
+	GtkWidget *alert;
 
 	gc = gaim_account_get_connection(account);
 
@@ -2351,14 +2355,14 @@
 	data->account  = account;
 	data->username = g_strdup(remote_user);
 	data->alias    = g_strdup(alias);
-
+	
 	buffer = make_info(account, gc, remote_user, id, alias, msg);
-
-	gaim_request_action(NULL, NULL, _("Add buddy to your list?"),
-	                    buffer, GAIM_DEFAULT_ACTION_NONE, data, 2,
-	                    _("Add"),    G_CALLBACK(add_user_cb),
-	                    _("Cancel"), G_CALLBACK(free_add_user_data));
-
+	alert = gaim_gtk_make_mini_dialog(gc, GAIM_STOCK_DIALOG_INFO,
+					  _("Add buddy to your list?"), buffer, data, 
+					  _("Cancel"), G_CALLBACK(free_add_user_data),
+					  _("Add"), G_CALLBACK(add_user_cb), NULL);
+	gaim_gtk_blist_add_alert(alert);
+        
 	g_free(buffer);
 }
 
--- a/gtk/gtkblist.c	Wed Nov 22 21:33:51 2006 +0000
+++ b/gtk/gtkblist.c	Thu Nov 23 08:37:07 2006 +0000
@@ -54,6 +54,7 @@
 #include "gtkprivacy.h"
 #include "gtkroomlist.h"
 #include "gtkstatusbox.h"
+#include "gtkscrollbook.h"
 #include "gtkutils.h"
 
 #include <gdk/gdkkeysyms.h>
@@ -4030,7 +4031,7 @@
 	gtkblist->vbox = gtk_vbox_new(FALSE, 0);
 	gtk_notebook_append_page(GTK_NOTEBOOK(gtkblist->notebook), gtkblist->vbox, NULL);
 	gtk_widget_show_all(gtkblist->notebook);
-	if (accounts = gaim_accounts_get_all_active()) {
+	if ((accounts = gaim_accounts_get_all_active())) {
 		g_list_free(accounts);
 		gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkblist->notebook), 1);
 	}
@@ -4178,6 +4179,9 @@
 
 	sep = gtk_hseparator_new();
 	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sep, FALSE, FALSE, 0);
+
+	gtkblist->scrollbook = gtk_gaim_scroll_book_new();
+	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->scrollbook, FALSE, FALSE, 0);
 	
 	/* Create an empty vbox used for showing connection errors */
 	gtkblist->error_buttons = gtk_vbox_new(FALSE, 0);
@@ -4278,6 +4282,8 @@
 						gtkblist, GAIM_CALLBACK(conversation_deleting_cb),
 						gtkblist);
 
+	gtk_widget_hide(gtkblist->scrollbook);
+	
 	/* emit our created signal */
 	gaim_signal_emit(handle, "gtkblist-created", list);
 }
@@ -5497,6 +5503,11 @@
 	gaim_debug_info("gtkblist", "removed visibility manager: %d\n", visibility_manager_count);
 }
 
+void gaim_gtk_blist_add_alert(GtkWidget *widget)
+{
+	gtk_container_add(GTK_CONTAINER(gtkblist->scrollbook), widget);
+}
+
 
 static GaimBlistUiOps blist_ui_ops =
 {
--- a/gtk/gtkblist.h	Wed Nov 22 21:33:51 2006 +0000
+++ b/gtk/gtkblist.h	Thu Nov 23 08:37:07 2006 +0000
@@ -95,6 +95,9 @@
 	GList *tooltipdata;              /**< The data for each "chunk" of the tooltip */
 
 	GaimBlistNode *selected_node;    /**< The currently selected node */
+	
+	GtkWidget *scrollbook;          /**< Scrollbook for alerts */
+
 	GtkWidget *error_buttons;        /**< Box containing the connection error buttons */
 	GtkWidget *statusbox;            /**< The status selector dropdown */
 };
@@ -202,6 +205,13 @@
  */
 void gaim_gtk_blist_visibility_manager_remove(void);
 
+/**
+ * Adds a mini-alert to the blist scrollbook
+ *
+ * @param widget   The widget to add
+ */
+void gaim_gtk_blist_add_alert(GtkWidget *widget);
+
 
 /**************************************************************************
  * @name GTK+ Buddy List sorting functions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gtk/gtkscrollbook.c	Thu Nov 23 08:37:07 2006 +0000
@@ -0,0 +1,198 @@
+/*
+ * @file gtkscrollbook.c GTK+ Status Selection Widget
+ * @ingroup gtkui
+ *
+ * gaim
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "gtkscrollbook.h"
+
+
+static void gtk_gaim_scroll_book_init (GtkGaimScrollBook *scroll_book);
+static void gtk_gaim_scroll_book_class_init (GtkGaimScrollBookClass *klass);
+
+GType
+gtk_gaim_scroll_book_get_type (void)
+{
+	static GType scroll_book_type = 0;
+
+	if (!scroll_book_type)
+	{
+		static const GTypeInfo scroll_book_info =
+		{
+			sizeof (GtkGaimScrollBookClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc) gtk_gaim_scroll_book_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GtkGaimScrollBook),
+			0,
+			(GInstanceInitFunc) gtk_gaim_scroll_book_init,
+			NULL  /* value_table */
+		};
+
+		scroll_book_type = g_type_register_static(GTK_TYPE_VBOX,
+							 "GtkGaimScrollBook",
+							 &scroll_book_info,
+							 0);
+	}
+
+	return scroll_book_type;
+}
+
+static void
+scroll_left_cb(GtkGaimScrollBook *scroll_book)
+{
+	int index;
+	index = gtk_notebook_get_current_page(GTK_NOTEBOOK(scroll_book->notebook));
+
+	if (index > 0)
+		gtk_notebook_set_current_page(GTK_NOTEBOOK(scroll_book->notebook), index - 1);
+}
+
+static void
+scroll_right_cb(GtkGaimScrollBook *scroll_book)
+{
+	int index, count;
+	index = gtk_notebook_get_current_page(GTK_NOTEBOOK(scroll_book->notebook));
+#if GTK_CHECK_VERSION(2,2,0)
+	count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(scroll_book->notebook));
+#else
+	count = g_list_length(GTK_NOTEBOOK(scroll_book->notebook)->children);
+#endif	
+	
+	if (index + 1 < count)
+		gtk_notebook_set_current_page(GTK_NOTEBOOK(scroll_book->notebook), index + 1);
+}
+
+static void
+refresh_scroll_box(GtkGaimScrollBook *scroll_book, int index, int count)
+{
+	char *label;
+
+	gtk_widget_show_all(scroll_book);
+	if (count <= 1)
+		gtk_widget_hide(GTK_WIDGET(scroll_book->hbox));
+	else
+		gtk_widget_show_all(GTK_WIDGET(scroll_book->hbox));
+		
+	
+	label = g_strdup_printf("<span size='smaller' weight='bold'>(%d/%d)</span>", index+1, count);
+	gtk_label_set_markup(GTK_LABEL(scroll_book->label), label);
+	g_free(label);				      
+
+	if (index == 0)
+		gtk_widget_set_sensitive(scroll_book->left_arrow, FALSE);
+	else
+		gtk_widget_set_sensitive(scroll_book->left_arrow, TRUE);
+
+	
+	if (index +1== count)
+		gtk_widget_set_sensitive(scroll_book->right_arrow, FALSE);
+	else
+		gtk_widget_set_sensitive(scroll_book->right_arrow, TRUE);
+}
+
+
+static void
+page_count_change_cb(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, GtkGaimScrollBook *scroll_book)
+{
+	int index = gtk_notebook_get_current_page(GTK_NOTEBOOK(scroll_book->notebook));
+#if GTK_CHECK_VERSION(2,2,0)
+	int count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(scroll_book->notebook));
+#else
+	count = g_list_length(GTK_NOTEBOOK(scroll_book->notebook)->children);
+#endif
+	refresh_scroll_box(scroll_book, index, count);
+	
+}
+
+static void
+switch_page_cb(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, GtkGaimScrollBook *scroll_book)
+{
+#if GTK_CHECK_VERSION(2,2,0)
+	int count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(scroll_book->notebook));
+#else
+	count = g_list_length(GTK_NOTEBOOK(scroll_book->notebook)->children);
+#endif
+	refresh_scroll_box(scroll_book, page_num, count);
+}
+
+static void
+gtk_gaim_scroll_book_add(GtkContainer *container, GtkWidget *widget)
+{
+	gtk_widget_show(widget);
+	gtk_notebook_append_page(GTK_NOTEBOOK(GTK_GAIM_SCROLL_BOOK(container)->notebook), widget, NULL);
+}
+
+static void
+gtk_gaim_scroll_book_class_init (GtkGaimScrollBookClass *klass)
+{
+	GtkContainerClass *container_class = (GtkContainerClass*)klass;
+
+	container_class->add = gtk_gaim_scroll_book_add;
+	
+}
+
+static void
+gtk_gaim_scroll_book_init (GtkGaimScrollBook *scroll_book)
+{
+	GtkWidget *eb;
+
+	scroll_book->hbox = gtk_hbox_new(FALSE, 0);
+
+	eb = gtk_event_box_new();
+	gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0);
+	scroll_book->right_arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+	gtk_container_add(GTK_CONTAINER(eb), scroll_book->right_arrow);
+	g_signal_connect_swapped(G_OBJECT(eb), "button-press-event", G_CALLBACK(scroll_right_cb), scroll_book);
+
+	scroll_book->label = gtk_label_new(NULL);
+	gtk_box_pack_end(GTK_BOX(scroll_book->hbox), scroll_book->label, FALSE, FALSE, 0);
+
+	eb = gtk_event_box_new();
+	gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0);
+	scroll_book->left_arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE);
+	gtk_container_add(GTK_CONTAINER(eb), scroll_book->left_arrow);
+	g_signal_connect_swapped(G_OBJECT(eb), "button-press-event", G_CALLBACK(scroll_left_cb), scroll_book);
+
+	gtk_box_pack_start(GTK_BOX(scroll_book), scroll_book->hbox, FALSE, FALSE, 0);
+	
+	scroll_book->notebook = gtk_notebook_new();
+	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(scroll_book->notebook), FALSE);
+	gtk_notebook_set_show_border(GTK_NOTEBOOK(scroll_book->notebook), FALSE);
+	
+	gtk_box_pack_start(GTK_BOX(scroll_book), scroll_book->notebook, TRUE, TRUE, 0);
+	
+	g_signal_connect(G_OBJECT(scroll_book->notebook), "page-added", G_CALLBACK(page_count_change_cb), scroll_book);
+	g_signal_connect(G_OBJECT(scroll_book->notebook), "page-removed", G_CALLBACK(page_count_change_cb), scroll_book);
+	g_signal_connect(G_OBJECT(scroll_book->notebook), "switch-page", G_CALLBACK(switch_page_cb), scroll_book);
+	gtk_widget_hide(scroll_book->hbox);
+}
+
+
+
+GtkWidget *
+gtk_gaim_scroll_book_new()
+{
+	return g_object_new(GTK_GAIM_TYPE_SCROLL_BOOK, NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gtk/gtkscrollbook.h	Thu Nov 23 08:37:07 2006 +0000
@@ -0,0 +1,80 @@
+/*
+ * @file gtkscrollbook  GTK+ Scrolling notebook Widget
+ * @ingroup gtkui
+ *
+ * gaim
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __GTK_GAIM_SCROLL_BOOK_H__
+#define __GTK_GAIM_SCROLL_BOOK_H__
+
+#include <gtk/gtk.h>
+
+
+G_BEGIN_DECLS
+
+#define GTK_GAIM_TYPE_SCROLL_BOOK             (gtk_gaim_scroll_book_get_type ())
+#define GTK_GAIM_SCROLL_BOOK(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_GAIM_TYPE_SCROLL_BOOK, GtkGaimScrollBook))
+#define GTK_GAIM_SCROLL_BOOK_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_GAIM_TYPE_SCROLL_BOOK, GtkGaimScrollBookClass))
+#define GTK_GAIM_IS_SCROLL_BOOK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_GAIM_TYPE_SCROLL_BOOK))
+#define GTK_GAIM_IS_SCROLL_BOOK_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_GAIM_TYPE_SCROLL_BOOK))
+#define GTK_GAIM_SCROLL_BOOK_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), GTK_GAIM_TYPE_SCROLL_BOOK, GtkGaimScrollBookClass))
+
+typedef struct _GtkGaimScrollBook      GtkGaimScrollBook;
+typedef struct _GtkGaimScrollBookClass GtkGaimScrollBookClass;
+
+struct _GtkGaimScrollBook
+{
+	GtkVBox parent_instance;
+
+	GtkWidget *notebook;
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *left_arrow;
+	GtkWidget *right_arrow;
+	
+	/* Padding for future expansion */
+	void (*_gtk_reserved0) (void);
+	void (*_gtk_reserved1) (void);
+	void (*_gtk_reserved2) (void);
+	void (*_gtk_reserved3) (void);
+
+};
+
+
+struct _GtkGaimScrollBookClass
+{
+	GtkComboBoxClass parent_class;
+
+	/* Padding for future expansion */
+	void (*_gtk_reserved0) (void);
+	void (*_gtk_reserved1) (void);
+	void (*_gtk_reserved2) (void);
+	void (*_gtk_reserved3) (void);
+};
+
+
+GType         gtk_gaim_scroll_book_get_type         (void) G_GNUC_CONST;
+GtkWidget    *gtk_gaim_scroll_book_new              (void);
+
+G_END_DECLS
+
+#endif  /* __GTK_GAIM_SCROLL_BOOK_H__ */
--- a/gtk/gtkutils.c	Wed Nov 22 21:33:51 2006 +0000
+++ b/gtk/gtkutils.c	Thu Nov 23 08:37:07 2006 +0000
@@ -2851,3 +2851,114 @@
 
 	return ret;
 }
+
+GSList *minidialogs = NULL;
+
+static void *
+gaim_gtk_utils_get_handle()
+{
+	static int handle;
+
+	return &handle;
+}
+
+static void connection_signed_off_cb(GaimConnection *gc) 
+{
+	GSList *list;
+	for (list = minidialogs; list; list = list->next) {
+		if (g_object_get_data(G_OBJECT(list->data), "gc") == gc) {
+				gtk_widget_destroy(GTK_WIDGET(list->data));
+		}
+	}
+}
+
+static void alert_killed_cb(GtkWidget *widget)
+{
+	minidialogs = g_slist_remove(minidialogs, widget);
+}
+
+void *gaim_gtk_make_mini_dialog(GaimConnection *gc, const char *icon_name,
+				const char *primary, const char *secondary,
+				void *user_data,  ...)
+{
+	GtkWidget *vbox;
+        GtkWidget *hbox;
+        GtkWidget *bbox;
+        GtkWidget *label;
+        GtkWidget *button;
+        GtkWidget *img = NULL;
+	char label_text[2048];
+	const char *button_text;
+	GCallback callback;
+	char *primary_esc, *secondary_esc;
+	va_list args;
+	static gboolean first_call = TRUE;
+
+	img = gtk_image_new_from_stock(icon_name, GTK_ICON_SIZE_BUTTON);
+	gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
+
+	vbox = gtk_vbox_new(FALSE,0);
+        gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+
+	g_object_set_data(G_OBJECT(vbox), "gc" ,gc);
+	minidialogs = g_slist_prepend(minidialogs, vbox);
+	g_signal_connect(G_OBJECT(vbox), "destroy", G_CALLBACK(alert_killed_cb), NULL);
+
+	if (first_call) {
+		first_call = FALSE;
+		gaim_signal_connect(gaim_connections_get_handle(), "signed-off", 
+				    gaim_gtk_utils_get_handle(),
+				    GAIM_CALLBACK(connection_signed_off_cb), NULL);
+	}
+	
+      	hbox = gtk_hbox_new(FALSE, 0);
+        gtk_container_add(GTK_CONTAINER(vbox), hbox);
+	
+        if (img != NULL)
+		gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
+	
+	primary_esc = g_markup_escape_text(primary, -1);
+
+	if (secondary)
+		secondary_esc = g_markup_escape_text(secondary, -1);
+	g_snprintf(label_text, sizeof(label_text),
+		   "<span weight=\"bold\" size=\"smaller\">%s</span>%s<span size=\"smaller\">%s</span>",
+		   primary_esc, secondary ? "\n" : "", secondary?secondary_esc:"");
+	g_free(primary_esc);
+	label = gtk_label_new(NULL);
+        gtk_widget_modify_text(vbox, GTK_STATE_NORMAL, &(label->style->white));
+        gtk_label_set_markup(GTK_LABEL(label), label_text);
+        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);
+#if GTK_CHECK_VERSION(2,6,0)
+	 g_object_set(label, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+#endif
+
+        bbox = gtk_hbutton_box_new();
+        gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+        gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+	gtk_box_set_spacing(GTK_BOX(bbox), 6);
+	
+	va_start(args, user_data);
+	while ((button_text = va_arg(args, char*))) {
+		callback = va_arg(args, GCallback);
+		button = gtk_button_new();
+		gtk_container_set_border_width(GTK_CONTAINER(button), 0);
+		if (callback)
+			g_signal_connect_swapped(G_OBJECT(button), "clicked", callback, user_data);
+		g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_destroy), vbox);
+		hbox = gtk_hbox_new(FALSE, 0);
+		gtk_container_add(GTK_CONTAINER(button), hbox);
+		g_snprintf(label_text, sizeof(label_text),
+			   "<span size=\"smaller\">%s</span>", button_text);
+		label = gtk_label_new(NULL);
+		gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), label_text);
+		gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
+		gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+		gtk_container_add(GTK_CONTAINER(bbox), button);
+	}
+	va_end(args);
+
+	return vbox;
+}
--- a/gtk/gtkutils.h	Wed Nov 22 21:33:51 2006 +0000
+++ b/gtk/gtkutils.h	Thu Nov 23 08:37:07 2006 +0000
@@ -510,3 +510,16 @@
  * @return         A newly allocated string with unicode arrow characters
  */
 char *gaim_gtk_make_pretty_arrows(const char *str);
+
+/**
+ * Creates a "mini-dialog" suitable for embedding in the buddy list scrollbook
+ *
+ * @param handle         A handle
+ * @param primary        The primary text
+ * @param secondary      The secondary text
+ * @param user_data      Data to pass to the callbacks
+ * @param ...            a NULL-terminated list of button labels and callbacks
+ */
+void *gaim_gtk_make_mini_dialog(GaimConnection *handle, const char* stock_id, 
+				const char *primary, const char *secondary,
+				void *user_data,  ...);