changeset 21889:a3f0c30eb0bf

propagate from branch 'im.pidgin.pidgin' (head 1716154a473ab645c7477f4285579df56d955fde) to branch 'im.pidgin.pidgin.next.minor' (head 457a3a60abf75010d7855c1f25254e8118d38466)
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Tue, 18 Dec 2007 05:50:43 +0000
parents c117352a6088 (current diff) c88a3f2dbb52 (diff)
children 5814ae85e756
files ChangeLog.API libpurple/protocols/bonjour/mdns_howl.c pidgin/gtkconv.c pidgin/gtkimhtml.c pidgin/gtkprefs.c pidgin/gtkroomlist.c pidgin/gtkutils.c
diffstat 18 files changed, 416 insertions(+), 383 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Tue Dec 18 03:35:20 2007 +0000
+++ b/ChangeLog.API	Tue Dec 18 05:50:43 2007 +0000
@@ -1,5 +1,23 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.4.0 (??/??/????):
+	libpurple:
+		Added:
+		* purple_certificate_add_ca_search_path. (Florian Quèze)
+
+	Pidgin:
+		Added:
+		* pidgin_create_dialog to create a window that closes on escape. Also
+		  added utility functions pidgin_dialog_get_vbox_with_properties,
+		  pidgin_dialog_get_vbox, pidgin_dialog_get_action_area to access the
+		  contents in the created dialog. (Peter 'fmoo' Ruibal)
+		* pidgin_dialog_add_button to add buttons to a dialog created by
+		  pidgin_create_dialog.
+		* GTK_IMHTML_NO_SMILEY for GtkIMHtmlOptions means not to look for
+		  smileys in the text. (Florian 'goutnet' Delizy)
+		* pidgin_auto_parent_window to make a window transient for a suitable
+		  parent window.
+
 version 2.3.2 (??/??/????):
 	Finch:
 		libgnt:
--- a/libpurple/certificate.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/libpurple/certificate.c	Tue Dec 18 05:50:43 2007 +0000
@@ -627,7 +627,7 @@
 
 /** System directory to probe for CA certificates */
 /* This is set in the lazy_init function */
-static const gchar *x509_ca_syspath = NULL;
+static GList *x509_ca_paths = NULL;
 
 /** A list of loaded CAs, populated from the above path whenever the lazy_init
     happens. Contains pointers to x509_ca_elements */
@@ -674,6 +674,7 @@
 	GDir *certdir;
 	const gchar *entry;
 	GPatternSpec *pempat;
+	GList *iter = NULL;
 	
 	if (x509_ca_initialized) return TRUE;
 
@@ -687,54 +688,48 @@
 		return FALSE;
 	}
 
-	/* Attempt to point at the appropriate system path */
-	if (NULL == x509_ca_syspath) {
-#ifdef _WIN32
-		x509_ca_syspath = g_build_filename(DATADIR,
-						   "ca-certs", NULL);
-#else
-		x509_ca_syspath = g_build_filename(DATADIR,
-						   "purple", "ca-certs", NULL);
-#endif
-	}
-
-	/* Populate the certificates pool from the system path */
-	certdir = g_dir_open(x509_ca_syspath, 0, NULL);
-	g_return_val_if_fail(certdir, FALSE);
-
 	/* Use a glob to only read .pem files */
 	pempat = g_pattern_spec_new("*.pem");
-	
-	while ( (entry = g_dir_read_name(certdir)) ) {
-		gchar *fullpath;
-		PurpleCertificate *crt;
 
-		if ( !g_pattern_match_string(pempat, entry) ) {
+	/* Populate the certificates pool from the search path(s) */
+	for (iter = x509_ca_paths; iter; iter = iter->next) {
+		certdir = g_dir_open(iter->data, 0, NULL);
+		if (!certdir) {
+			purple_debug_error("certificate/x509/ca", "Couldn't open location '%s'\n", iter->data);
 			continue;
 		}
 
-		fullpath = g_build_filename(x509_ca_syspath, entry, NULL);
-		
-		/* TODO: Respond to a failure in the following? */
-		crt = purple_certificate_import(x509, fullpath);
+		while ( (entry = g_dir_read_name(certdir)) ) {
+			gchar *fullpath;
+			PurpleCertificate *crt;
+
+			if ( !g_pattern_match_string(pempat, entry) ) {
+				continue;
+			}
+
+			fullpath = g_build_filename(iter->data, entry, NULL);
+
+			/* TODO: Respond to a failure in the following? */
+			crt = purple_certificate_import(x509, fullpath);
 
-		if (x509_ca_quiet_put_cert(crt)) {
-			purple_debug_info("certificate/x509/ca",
-					  "Loaded %s\n",
-					  fullpath);
-		} else {
-			purple_debug_error("certificate/x509/ca",
-					  "Failed to load %s\n",
-					  fullpath);
+			if (x509_ca_quiet_put_cert(crt)) {
+				purple_debug_info("certificate/x509/ca",
+						  "Loaded %s\n",
+						  fullpath);
+			} else {
+				purple_debug_error("certificate/x509/ca",
+						  "Failed to load %s\n",
+						  fullpath);
+			}
+
+			purple_certificate_destroy(crt);
+			g_free(fullpath);
 		}
-
-		purple_certificate_destroy(crt);
-		g_free(fullpath);
+		g_dir_close(certdir);
 	}
 
 	g_pattern_spec_free(pempat);
-	g_dir_close(certdir);
-	
+
 	purple_debug_info("certificate/x509/ca",
 			  "Lazy init completed.\n");
 	x509_ca_initialized = TRUE;
@@ -744,6 +739,17 @@
 static gboolean
 x509_ca_init(void)
 {
+	/* Attempt to point at the appropriate system path */
+	if (NULL == x509_ca_paths) {
+#ifdef _WIN32
+		x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
+						   "ca-certs", NULL));
+#else
+		x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
+						   "purple", "ca-certs", NULL));
+#endif
+	}
+
 	/* Attempt to initialize now, but if it doesn't work, that's OK;
 	   it will get done later */
 	if ( ! x509_ca_lazy_init()) {
@@ -752,7 +758,7 @@
 				  "dependency is not yet registered. "
 				  "It has been deferred to later.\n");
 	}
-	
+
 	return TRUE;
 }
 
@@ -768,6 +774,9 @@
 	g_list_free(x509_ca_certs);
 	x509_ca_certs = NULL;
 	x509_ca_initialized = FALSE;
+	g_list_foreach(x509_ca_paths, (GFunc)g_free, NULL);
+	g_list_free(x509_ca_paths);
+	x509_ca_paths = NULL;
 }
 
 /** Look up a ca_element by dn */
@@ -1901,3 +1910,10 @@
 	g_byte_array_free(sha_bin, TRUE);
 }
 
+void purple_certificate_add_ca_search_path(const char *path)
+{
+	if (g_list_find_custom(x509_ca_paths, path, (GCompareFunc)strcmp))
+		return;
+	x509_ca_paths = g_list_append(x509_ca_paths, g_strdup(path));
+}
+
--- a/libpurple/certificate.h	Tue Dec 18 03:35:20 2007 +0000
+++ b/libpurple/certificate.h	Tue Dec 18 05:50:43 2007 +0000
@@ -786,6 +786,12 @@
 void
 purple_certificate_display_x509(PurpleCertificate *crt);
 
+/**
+ * Add a search path for certificates.
+ *
+ * @param path   Path to search for certificates.
+ */
+void purple_certificate_add_ca_search_path(const char *path);
 
 #ifdef __cplusplus
 }
--- a/pidgin/gtkaccount.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkaccount.c	Tue Dec 18 05:50:43 2007 +0000
@@ -1437,7 +1437,6 @@
 	GtkWidget *win;
 	GtkWidget *main_vbox;
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 	GtkWidget *dbox;
 	GtkWidget *notebook;
 	GtkWidget *button;
@@ -1475,16 +1474,14 @@
 	if ((dialog->plugin = purple_find_prpl(dialog->protocol_id)) != NULL)
 		dialog->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin);
 
-	dialog->window = win = pidgin_create_window((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"),
+	dialog->window = win = pidgin_create_dialog((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"),
 		PIDGIN_HIG_BORDER, "account", FALSE);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(account_win_destroy_cb), dialog);
 
 	/* Setup the vbox */
-	main_vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), main_vbox);
-	gtk_widget_show(main_vbox);
+	main_vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	notebook = gtk_notebook_new();
 	gtk_box_pack_start(GTK_BOX(main_vbox), notebook, FALSE, FALSE, 0);
@@ -1511,8 +1508,6 @@
 	if (!dialog->prpl_info || !dialog->prpl_info->register_user)
 		gtk_widget_hide(button);
 
-
-
 	/* Setup the page with 'Advanced'. */
 	dialog->bottom_vbox = dbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_set_border_width(GTK_CONTAINER(dbox), PIDGIN_HIG_BORDER);
@@ -1524,30 +1519,13 @@
 	add_protocol_options(dialog, dbox);
 	add_proxy_options(dialog, dbox);
 
-	/* Setup the button box */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(main_vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Cancel button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(cancel_account_prefs_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL, G_CALLBACK(cancel_account_prefs_cb), dialog);
 
 	/* Save button */
-	button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_SAVE, G_CALLBACK(ok_account_prefs_cb), dialog);
 	if (dialog->account == NULL)
 		gtk_widget_set_sensitive(button, FALSE);
-
-	gtk_widget_show(button);
-
 	dialog->ok_button = button;
 
 	/* Set up DND */
@@ -1561,9 +1539,6 @@
 	g_signal_connect(G_OBJECT(dialog->window), "drag_data_received",
 			 G_CALLBACK(account_dnd_recv), dialog);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(ok_account_prefs_cb), dialog);
-
 	/* Show the window. */
 	gtk_widget_show(win);
 }
@@ -2313,7 +2288,6 @@
 	AccountsWindow *dialog;
 	GtkWidget *win;
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 	GtkWidget *sw;
 	GtkWidget *button;
 	int width, height;
@@ -2328,7 +2302,7 @@
 	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/width");
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/height");
 
-	dialog->window = win = pidgin_create_window(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -2337,57 +2311,28 @@
 					 G_CALLBACK(configure_cb), accounts_window);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Setup the scrolled window that will contain the list of accounts. */
 	sw = create_accounts_list(dialog);
 	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
 	gtk_widget_show(sw);
 
-	/* Button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(add_account_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD, G_CALLBACK(add_account_cb), dialog);
 
 	/* Modify button */
-	button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY, G_CALLBACK(modify_account_cb), dialog);
 	dialog->modify_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(modify_account_cb), dialog);
 
 	/* Delete button */
-	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE, G_CALLBACK(ask_delete_account_cb), dialog);
 	dialog->delete_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(ask_delete_account_cb), dialog);
 
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(close_accounts_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, G_CALLBACK(close_accounts_cb), dialog);
 
 	purple_signal_connect(pidgin_account_get_handle(), "account-modified",
 	                    accounts_window,
--- a/pidgin/gtkcertmgr.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkcertmgr.c	Tue Dec 18 05:50:43 2007 +0000
@@ -559,7 +559,6 @@
 	CertMgrDialog *dlg;
 	GtkWidget *win;
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 
 	/* Enumerate all the certificates on file */
 	{
@@ -599,7 +598,7 @@
 	dlg = certmgr_dialog = g_new0(CertMgrDialog, 1);
 
 	win = dlg->window =
-		pidgin_create_window(_("Certificate Manager"),/* Title */
+		pidgin_create_dialog(_("Certificate Manager"),/* Title */
 				     PIDGIN_HIG_BORDER, /*Window border*/
 				     "certmgr",         /* Role */
 				     TRUE); /* Allow resizing */
@@ -611,9 +610,7 @@
 	gtk_window_set_default_size(GTK_WINDOW(win), 400, 400);
 
 	/* Main vbox */
-	vbox = gtk_vbox_new( FALSE, PIDGIN_HIG_BORDER );
-	gtk_container_add(GTK_CONTAINER(win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Notebook of various certificate managers */
 	dlg->notebook = gtk_notebook_new();
@@ -622,19 +619,9 @@
 			   0);
 	gtk_widget_show(dlg->notebook);
 
-	/* Box for the close button */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Close button */
-	dlg->closebutton = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), dlg->closebutton, FALSE, FALSE, 0);
-	gtk_widget_show(dlg->closebutton);
-	g_signal_connect(G_OBJECT(dlg->closebutton), "clicked",
-			 G_CALLBACK(certmgr_close_cb), dlg);
+	dlg->closebutton = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
+			G_CALLBACK(certmgr_close_cb), dlg);
 
 	/* Add the defined certificate managers */
 	/* TODO: Find a way of determining whether each is shown or not */
--- a/pidgin/gtkconv.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkconv.c	Tue Dec 18 05:50:43 2007 +0000
@@ -8318,7 +8318,7 @@
 	}
 	
 	if (e->button == 3) {
-		/* Right click was pressed. Popup the Send To menu. */
+		/* Right click was pressed. Popup the context menu. */
 		GtkWidget *menu = gtk_menu_new(), *sub;
 		gboolean populated = populate_menu_with_options(menu, gtkconv, TRUE);
 		sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtkconv->win->menu.send_to));
--- a/pidgin/gtkft.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkft.c	Tue Dec 18 05:50:43 2007 +0000
@@ -745,7 +745,6 @@
 	PidginXferDialog *dialog;
 	GtkWidget *window;
 	GtkWidget *vbox1, *vbox2;
-	GtkWidget *bbox;
 	GtkWidget *sw;
 	GtkWidget *button;
 	GtkWidget *expander;
@@ -759,15 +758,13 @@
 		purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished");
 
 	/* Create the window. */
-	dialog->window = window = pidgin_create_window(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE);
+	dialog->window = window = pidgin_create_dialog(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
 
 	/* Create the parent vbox for everything. */
-	vbox1 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(window), vbox1);
-	gtk_widget_show(vbox1);
+	vbox1 = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Create the main vbox for top half of the window. */
 	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -812,71 +809,35 @@
 	gtk_container_add(GTK_CONTAINER(expander), table);
 	gtk_widget_show(table);
 
-	/* Now the button box for the buttons */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Open button */
-	button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_OPEN, G_CALLBACK(open_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 	dialog->open_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(open_button_cb), dialog);
-
 	/* Pause button */
-	button = gtk_button_new_with_mnemonic(_("_Pause"));
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), _("_Pause"), G_CALLBACK(pause_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 	dialog->pause_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pause_button_cb), dialog);
-
 	/* Resume button */
-	button = gtk_button_new_with_mnemonic(_("_Resume"));
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), _("_Resume"), G_CALLBACK(resume_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 	dialog->resume_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(resume_button_cb), dialog);
-
 	/* Remove button */
-	button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_REMOVE, G_CALLBACK(remove_button_cb), dialog);
 	gtk_widget_hide(button);
 	dialog->remove_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(remove_button_cb), dialog);
-
 	/* Stop button */
-	button = gtk_button_new_from_stock(GTK_STOCK_STOP);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP, G_CALLBACK(stop_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
 	dialog->stop_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(stop_button_cb), dialog);
-
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, G_CALLBACK(close_button_cb), dialog);
 	dialog->close_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(close_button_cb), dialog);
-
 #ifdef _WIN32
 	g_signal_connect(G_OBJECT(dialog->window), "show",
 		G_CALLBACK(winpidgin_ensure_onscreen), dialog->window);
--- a/pidgin/gtkimhtml.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkimhtml.c	Tue Dec 18 05:50:43 2007 +0000
@@ -1012,7 +1012,7 @@
 static void imhtml_paste_insert(GtkIMHtml *imhtml, const char *text, gboolean plaintext)
 {
 	GtkTextIter iter;
-	GtkIMHtmlOptions flags = plaintext ? 0 : (GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS);
+	GtkIMHtmlOptions flags = plaintext ? GTK_IMHTML_NO_SMILEY : (GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS);
 
 	if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, NULL, NULL))
 		gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
@@ -2998,6 +2998,7 @@
 			pos += tlen;
 			g_free(tag); /* This was allocated back in VALID_TAG() */
 		} else if (imhtml->edit.link == NULL &&
+				!(options & GTK_IMHTML_NO_SMILEY) &&
 				gtk_imhtml_is_smiley(imhtml, fonts, c, &smilelen)) {
 			GtkIMHtmlFontDetail *fd;
 			gchar *sml = NULL;
--- a/pidgin/gtkimhtml.h	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkimhtml.h	Tue Dec 18 05:50:43 2007 +0000
@@ -225,7 +225,8 @@
 	GTK_IMHTML_RETURN_LOG          = 1 << 7,
 	GTK_IMHTML_USE_POINTSIZE       = 1 << 8,
 	GTK_IMHTML_NO_FORMATTING       = 1 << 9,
-	GTK_IMHTML_USE_SMOOTHSCROLLING = 1 << 10
+	GTK_IMHTML_USE_SMOOTHSCROLLING = 1 << 10,
+	GTK_IMHTML_NO_SMILEY           = 1 << 11,
 } GtkIMHtmlOptions;
 
 enum {
--- a/pidgin/gtknotify.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtknotify.c	Tue Dec 18 05:50:43 2007 +0000
@@ -284,6 +284,8 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 
+	pidgin_auto_parent_window(dialog);
+
 	gtk_widget_show_all(dialog);
 
 	return dialog;
@@ -684,6 +686,8 @@
 	g_object_set_data(G_OBJECT(window), "info-widget", imhtml);
 
 	/* Show the window */
+	pidgin_auto_parent_window(window);
+
 	gtk_widget_show(window);
 
 	return window;
@@ -894,6 +898,8 @@
 	pidgin_notify_searchresults_new_rows(gc, results, data);
 
 	/* Show the window */
+	pidgin_auto_parent_window(window);
+
 	gtk_widget_show(window);
 	return data;
 }
--- a/pidgin/gtkpounce.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkpounce.c	Tue Dec 18 05:50:43 2007 +0000
@@ -1317,7 +1317,6 @@
 pidgin_pounces_manager_show(void)
 {
 	PouncesManager *dialog;
-	GtkWidget *bbox;
 	GtkWidget *button;
 	GtkWidget *list;
 	GtkWidget *vbox;
@@ -1334,7 +1333,7 @@
 	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/pounces/dialog/width");
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/pounces/dialog/height");
 
-	dialog->window = win = pidgin_create_window(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -1343,61 +1342,33 @@
 					 G_CALLBACK(pounces_manager_configure_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* List of saved buddy pounces */
 	list = create_pounces_list(dialog);
 	gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0);
 
-	/* Button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
+	/* Add button */
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD, G_CALLBACK(pounces_manager_add_cb), dialog);
+	gtk_widget_set_sensitive(button, (purple_accounts_get_all() != NULL));
 
-	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_set_sensitive(button, (purple_accounts_get_all() != NULL));
 	purple_signal_connect(purple_connections_get_handle(), "signed-on",
 						pounces_manager, PURPLE_CALLBACK(pounces_manager_connection_cb), button);
 	purple_signal_connect(purple_connections_get_handle(), "signed-off",
 						pounces_manager, PURPLE_CALLBACK(pounces_manager_connection_cb), button);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_add_cb), dialog);
 
 	/* Modify button */
-	button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY);
-	dialog->modify_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY, G_CALLBACK(pounces_manager_modify_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_modify_cb), dialog);
+	dialog->modify_button = button;
 
 	/* Delete button */
-	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
-	dialog->delete_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE, G_CALLBACK(pounces_manager_delete_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_delete_cb), dialog);
+	dialog->delete_button = button;
 
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_close_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, G_CALLBACK(pounces_manager_close_cb), dialog);
 
 	gtk_widget_show(win);
 }
--- a/pidgin/gtkprefs.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkprefs.c	Tue Dec 18 05:50:43 2007 +0000
@@ -2150,7 +2150,6 @@
 void pidgin_prefs_show(void)
 {
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 	GtkWidget *notebook;
 	GtkWidget *button;
 
@@ -2166,31 +2165,20 @@
 	/* Back to instant-apply! I win!  BU-HAHAHA! */
 
 	/* Create the window */
-	prefs = pidgin_create_window(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE);
+	prefs = pidgin_create_dialog(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE);
 	g_signal_connect(G_OBJECT(prefs), "destroy",
 					 G_CALLBACK(delete_prefs), NULL);
 
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(prefs), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(prefs), FALSE, PIDGIN_HIG_BORDER);
 
 	/* The notebook */
 	prefsnotebook = notebook = gtk_notebook_new ();
 	gtk_box_pack_start (GTK_BOX (vbox), notebook, FALSE, FALSE, 0);
 	gtk_widget_show(prefsnotebook);
 
-	/* The buttons to press! */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
-	gtk_widget_show (bbox);
-
-	button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
+	button = pidgin_dialog_add_button(GTK_DIALOG(prefs), GTK_STOCK_CLOSE, NULL, NULL);
 	g_signal_connect_swapped(G_OBJECT(button), "clicked",
 							 G_CALLBACK(gtk_widget_destroy), prefs);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 
 	prefs_notebook_init();
 
--- a/pidgin/gtkprivacy.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkprivacy.c	Tue Dec 18 05:50:43 2007 +0000
@@ -45,6 +45,7 @@
 	GtkWidget *add_button;
 	GtkWidget *remove_button;
 	GtkWidget *clear_button;
+	GtkWidget *close_button;
 
 	GtkWidget *button_box;
 	GtkWidget *allow_widget;
@@ -259,19 +260,22 @@
 
 	gtk_widget_hide(dialog->allow_widget);
 	gtk_widget_hide(dialog->block_widget);
-	gtk_widget_hide(dialog->button_box);
+	gtk_widget_hide_all(dialog->button_box);
 
 	if (new_type == PURPLE_PRIVACY_ALLOW_USERS) {
 		gtk_widget_show(dialog->allow_widget);
-		gtk_widget_show(dialog->button_box);
+		gtk_widget_show_all(dialog->button_box);
 		dialog->in_allow_list = TRUE;
 	}
 	else if (new_type == PURPLE_PRIVACY_DENY_USERS) {
 		gtk_widget_show(dialog->block_widget);
-		gtk_widget_show(dialog->button_box);
+		gtk_widget_show_all(dialog->button_box);
 		dialog->in_allow_list = FALSE;
 	}
 
+	gtk_widget_show_all(dialog->close_button);
+	gtk_widget_show(dialog->button_box);
+
 	purple_blist_schedule_save();
 	pidgin_blist_refresh(purple_get_blist());
 }
@@ -355,7 +359,6 @@
 privacy_dialog_new(void)
 {
 	PidginPrivacyDialog *dialog;
-	GtkWidget *bbox;
 	GtkWidget *hbox;
 	GtkWidget *vbox;
 	GtkWidget *button;
@@ -367,15 +370,13 @@
 
 	dialog = g_new0(PidginPrivacyDialog, 1);
 
-	dialog->win = pidgin_create_window(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", TRUE);
+	dialog->win = pidgin_create_dialog(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", TRUE);
 
 	g_signal_connect(G_OBJECT(dialog->win), "delete_event",
 					 G_CALLBACK(destroy_cb), dialog);
 
 	/* Main vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(dialog->win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(dialog->win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Description label */
 	label = gtk_label_new(
@@ -433,52 +434,27 @@
 	gtk_box_pack_start(GTK_BOX(vbox), dialog->block_widget, TRUE, TRUE, 0);
 
 	/* Add the button box for Add, Remove, Clear */
-	dialog->button_box = bbox = gtk_hbutton_box_new();
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+	dialog->button_box = pidgin_dialog_get_action_area(GTK_DIALOG(dialog->win));
 
 	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_ADD, G_CALLBACK(add_cb), dialog);
 	dialog->add_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(add_cb), dialog);
 
 	/* Remove button */
-	button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_REMOVE, G_CALLBACK(remove_cb), dialog);
 	dialog->remove_button = button;
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(remove_cb), dialog);
 
 	/* Clear button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_CLEAR, G_CALLBACK(clear_cb), dialog);
 	dialog->clear_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(clear_cb), dialog);
-
-	/* Another button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
-	gtk_widget_show(bbox);
 
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_CLOSE, G_CALLBACK(close_cb), dialog);
+	dialog->close_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(close_cb), dialog);
-
+	type_changed_cb(GTK_OPTION_MENU(dialog->type_menu), dialog);
+#if 0
 	if (dialog->account->perm_deny == PURPLE_PRIVACY_ALLOW_USERS) {
 		gtk_widget_show(dialog->allow_widget);
 		gtk_widget_show(dialog->button_box);
@@ -489,7 +465,7 @@
 		gtk_widget_show(dialog->button_box);
 		dialog->in_allow_list = FALSE;
 	}
-
+#endif
 	return dialog;
 }
 
--- a/pidgin/gtkrequest.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkrequest.c	Tue Dec 18 05:50:43 2007 +0000
@@ -439,6 +439,8 @@
 	pidgin_set_accessible_label (entry, label);
 	data->u.input.entry = entry;
 
+	pidgin_auto_parent_window(dialog);
+
 	/* Show everything. */
 	gtk_widget_show(dialog);
 
@@ -546,6 +548,8 @@
 	g_object_set_data(G_OBJECT(dialog), "radio", radio);
 
 	/* Show everything. */
+	pidgin_auto_parent_window(dialog);
+
 	gtk_widget_show_all(dialog);
 
 	return data;
@@ -661,6 +665,8 @@
 		gtk_dialog_set_default_response(GTK_DIALOG(dialog), default_action);
 
 	/* Show everything. */
+	pidgin_auto_parent_window(dialog);
+
 	gtk_widget_show_all(dialog);
 
 	return data;
@@ -1059,7 +1065,6 @@
 	GtkWidget *vbox;
 	GtkWidget *vbox2;
 	GtkWidget *hbox;
-	GtkWidget *bbox;
 	GtkWidget *frame;
 	GtkWidget *label;
 	GtkWidget *table;
@@ -1089,9 +1094,9 @@
 
 
 #ifdef _WIN32
-	data->dialog = win = pidgin_create_window(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
+	data->dialog = win = pidgin_create_dialog(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
 #else /* !_WIN32 */
-	data->dialog = win = pidgin_create_window(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
+	data->dialog = win = pidgin_create_dialog(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
 #endif /* _WIN32 */
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -1099,7 +1104,7 @@
 
 	/* Setup the main horizontal box */
 	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), hbox);
+	gtk_container_add(GTK_CONTAINER(pidgin_dialog_get_vbox(GTK_DIALOG(win))), hbox);
 	gtk_widget_show(hbox);
 
 	/* Dialog icon. */
@@ -1382,39 +1387,21 @@
 
 	g_object_unref(sg);
 
-	/* Button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Cancel button */
-	button = gtk_button_new_from_stock(text_to_stock(cancel_text));
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(multifield_cancel_cb), data);
-
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(cancel_text), G_CALLBACK(multifield_cancel_cb), data);
 	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 
 	/* OK button */
-	button = gtk_button_new_from_stock(text_to_stock(ok_text));
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(ok_text), G_CALLBACK(multifield_ok_cb), data);
 	data->ok_button = button;
-
 	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 	gtk_window_set_default(GTK_WINDOW(win), button);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(multifield_ok_cb), data);
-
 	if (!purple_request_fields_all_required_filled(fields))
 		gtk_widget_set_sensitive(button, FALSE);
 
+	pidgin_auto_parent_window(win);
+
 	gtk_widget_show(win);
 
 	return data;
@@ -1622,6 +1609,8 @@
 					 G_CALLBACK(file_ok_check_if_exists_cb), data);
 #endif /* FILECHOOSER */
 
+	pidgin_auto_parent_window(filesel);
+
 	data->dialog = filesel;
 	gtk_widget_show(filesel);
 
@@ -1673,6 +1662,8 @@
 #endif
 
 	data->dialog = dirsel;
+	pidgin_auto_parent_window(dirsel);
+
 	gtk_widget_show(dirsel);
 
 	return (void *)data;
--- a/pidgin/gtkroomlist.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkroomlist.c	Tue Dec 18 05:50:43 2007 +0000
@@ -685,15 +685,13 @@
 	dialog->account = account;
 
 	/* Create the window. */
-	dialog->window = window = pidgin_create_window(_("Room List"), PIDGIN_HIG_BORDER, "room list", TRUE);
+	dialog->window = window = pidgin_create_dialog(_("Room List"), PIDGIN_HIG_BORDER, "room list", TRUE);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
 
 	/* Create the parent vbox for everything. */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(window), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
 
 	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_add(GTK_CONTAINER(vbox), vbox2);
@@ -738,19 +736,14 @@
 	gtk_widget_show(dialog->progress);
 
 	/* button box */
-	bbox = gtk_hbutton_box_new();
+	bbox = pidgin_dialog_get_action_area(GTK_DIALOG(window));
 	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
 	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
 
 	/* stop button */
-	dialog->stop_button = gtk_button_new_from_stock(GTK_STOCK_STOP);
-	gtk_box_pack_start(GTK_BOX(bbox), dialog->stop_button, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(dialog->stop_button), "clicked",
+	dialog->stop_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP,
 	                 G_CALLBACK(stop_button_cb), dialog);
 	gtk_widget_set_sensitive(dialog->stop_button, FALSE);
-	gtk_widget_show(dialog->stop_button);
 
 	/* list button */
 	dialog->list_button = pidgin_pixbuf_button_from_stock(_("_Get List"), GTK_STOCK_REFRESH,
@@ -779,11 +772,8 @@
 	gtk_widget_show(dialog->join_button);
 
 	/* close button */
-	dialog->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), dialog->close_button, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(dialog->close_button), "clicked",
+	dialog->close_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE,
 					 G_CALLBACK(close_button_cb), dialog);
-	gtk_widget_show(dialog->close_button);
 
 	/* show the dialog window and return the dialog */
 	gtk_widget_show(dialog->window);
--- a/pidgin/gtksavedstatuses.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtksavedstatuses.c	Tue Dec 18 05:50:43 2007 +0000
@@ -594,7 +594,7 @@
 	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/status/dialog/width");
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/status/dialog/height");
 
-	dialog->window = win = pidgin_create_window(_("Saved Statuses"), PIDGIN_HIG_BORDER, "statuses", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Saved Statuses"), PIDGIN_HIG_BORDER, "statuses", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -603,18 +603,14 @@
 					 G_CALLBACK(configure_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* List of saved status states */
 	list = create_saved_status_list(dialog);
 	gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0);
 
 	/* Button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
+	bbox = pidgin_dialog_get_action_area(GTK_DIALOG(win));
 
 	/* Use button */
 	button = pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE,
@@ -627,36 +623,23 @@
 					 G_CALLBACK(status_window_use_cb), dialog);
 
 	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_add_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD,
+			G_CALLBACK(status_window_add_cb), dialog);
 
 	/* Modify button */
-	button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY,
+			G_CALLBACK(status_window_modify_cb), dialog);
 	dialog->modify_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+
+	/* Delete button */
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE,
+			G_CALLBACK(status_window_delete_cb), dialog);
+	dialog->delete_button = button;
 	gtk_widget_set_sensitive(button, FALSE);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_modify_cb), dialog);
-
-	/* Delete button */
-	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
-	dialog->delete_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_set_sensitive(button, FALSE);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_delete_cb), dialog);
-
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_close_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
+			G_CALLBACK(status_window_close_cb), dialog);
 
 	purple_signal_connect(purple_savedstatuses_get_handle(),
 			"savedstatus-changed", status_window,
@@ -1147,14 +1130,13 @@
 	if (edit)
 		dialog->original_title = g_strdup(purple_savedstatus_get_title(saved_status));
 
-	dialog->window = win = pidgin_create_window(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(status_editor_destroy_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
@@ -1257,23 +1239,18 @@
 		(saved_status != NULL) && purple_savedstatus_has_substatuses(saved_status));
 
 	/* Button box */
-	bbox = gtk_hbutton_box_new();
+	bbox = pidgin_dialog_get_action_area(GTK_DIALOG(win));
 	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
 	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
 
 	/* Cancel button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_editor_cancel_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL,
+			G_CALLBACK(status_editor_cancel_cb), dialog);
 
 	/* Use button */
 	button = pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE,
 										   PIDGIN_BUTTON_HORIZONTAL);
 	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(status_editor_ok_cb), dialog);
 
@@ -1284,19 +1261,15 @@
 	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	if (dialog->original_title == NULL)
 		gtk_widget_set_sensitive(button, FALSE);
-
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(status_editor_ok_cb), dialog);
 
 	/* Save button */
-	button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
-	dialog->save_button = GTK_BUTTON(button);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_SAVE,
+			G_CALLBACK(status_editor_ok_cb), dialog);
 	if (dialog->original_title == NULL)
 		gtk_widget_set_sensitive(button, FALSE);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_editor_ok_cb), dialog);
+	dialog->save_button = GTK_BUTTON(button);
 
 	gtk_widget_show_all(win);
 	g_object_unref(sg);
@@ -1447,8 +1420,6 @@
 	char *tmp;
 	SubStatusEditor *dialog;
 	GtkSizeGroup *sg;
-	GtkWidget *bbox;
-	GtkWidget *button;
 	GtkWidget *combo;
 	GtkWidget *hbox;
 	GtkWidget *frame;
@@ -1486,15 +1457,14 @@
 	dialog->account = account;
 
 	tmp = g_strdup_printf(_("Status for %s"), purple_account_get_username(account));
-	dialog->window = win = pidgin_create_window(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE);
+	dialog->window = win = pidgin_create_dialog(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE);
 	g_free(tmp);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(substatus_editor_destroy_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
@@ -1543,25 +1513,13 @@
 	dialog->toolbar = GTK_IMHTMLTOOLBAR(toolbar);
 	gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
 
-	/* Button box */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-
 	/* Cancel button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(substatus_editor_cancel_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL,
+			G_CALLBACK(substatus_editor_cancel_cb), dialog);
 
 	/* OK button */
-	button = gtk_button_new_from_stock(GTK_STOCK_OK);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(substatus_editor_ok_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_OK,
+			G_CALLBACK(substatus_editor_ok_cb), dialog);
 
 	/* Seed the input widgets with the current values */
 
--- a/pidgin/gtkutils.c	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkutils.c	Tue Dec 18 05:50:43 2007 +0000
@@ -132,12 +132,9 @@
 	}
 }
 
-GtkWidget *
-pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable)
+static
+void pidgin_window_init(GtkWindow *wnd, const char *title, guint border_width, const char *role, gboolean resizable)
 {
-	GtkWindow *wnd = NULL;
-
-	wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
 	if (title)
 		gtk_window_set_title(wnd, title);
 #ifdef _WIN32
@@ -148,11 +145,63 @@
 	if (role)
 		gtk_window_set_role(wnd, role);
 	gtk_window_set_resizable(wnd, resizable);
+}
+
+GtkWidget *
+pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable)
+{
+	GtkWindow *wnd = NULL;
+
+	wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+	pidgin_window_init(wnd, title, border_width, role, resizable);
+
+	return GTK_WIDGET(wnd);
+}
+
+GtkWidget *
+pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable)
+{
+	GtkWindow *wnd = NULL;
+
+	wnd = GTK_WINDOW(gtk_dialog_new());
+	pidgin_window_init(wnd, title, border_width, role, resizable);
+	g_object_set(G_OBJECT(wnd), "has-separator", FALSE, NULL);
 
 	return GTK_WIDGET(wnd);
 }
 
 GtkWidget *
+pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing)
+{
+	GtkBox *vbox = GTK_BOX(GTK_DIALOG(dialog)->vbox);
+	gtk_box_set_homogeneous(vbox, homogeneous);
+	gtk_box_set_spacing(vbox, spacing);
+	return GTK_WIDGET(vbox);
+}
+
+GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog)
+{
+	return GTK_DIALOG(dialog)->vbox;
+}
+
+GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog)
+{
+	return GTK_DIALOG(dialog)->action_area;
+}
+
+GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label,
+		GCallback callback, gpointer callbackdata)
+{
+	GtkWidget *button = gtk_button_new_from_stock(label);
+	GtkWidget *bbox = pidgin_dialog_get_action_area(dialog);
+	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	if (callback)
+		g_signal_connect(G_OBJECT(button), "clicked", callback, callbackdata);
+	gtk_widget_show(button);
+	return button;
+}
+
+GtkWidget *
 pidgin_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret)
 {
 	GtkWidget *frame;
@@ -3269,3 +3318,107 @@
 	gtk_entry_set_text(GTK_ENTRY(GTK_BIN((widget))->child), (text));
 }
 
+gboolean pidgin_auto_parent_window(GtkWidget *widget)
+{
+#if 0
+	/* This looks at the most recent window that received focus, and makes
+	 * that the parent window. */
+#ifndef _WIN32
+	static GdkAtom _WindowTime = GDK_NONE;
+	static GdkAtom _Cardinal = GDK_NONE;
+	GList *windows = NULL;
+	GtkWidget *parent = NULL;
+	time_t window_time = 0;
+
+	windows = gtk_window_list_toplevels();
+
+	if (_WindowTime == GDK_NONE) {
+		_WindowTime = gdk_x11_xatom_to_atom(gdk_x11_get_xatom_by_name("_NET_WM_USER_TIME"));
+	}
+	if (_Cardinal == GDK_NONE) {
+		_Cardinal = gdk_atom_intern("CARDINAL", FALSE);
+	}
+
+	while (windows) {
+		GtkWidget *window = windows->data;
+		guchar *data = NULL;
+		int al = 0;
+		time_t value;
+
+		windows = g_list_delete_link(windows, windows);
+
+		if (window == widget ||
+				!GTK_WIDGET_VISIBLE(window))
+			continue;
+
+		if (!gdk_property_get(window->window, _WindowTime, _Cardinal, 0, sizeof(time_t), FALSE,
+				NULL, NULL, &al, &data))
+			continue;
+		value = *(time_t *)data;
+		if (window_time < value) {
+			window_time = value;
+			parent = window;
+		}
+		g_free(data);
+	}
+	if (windows)
+		g_list_free(windows);
+	if (parent) {
+		if (!gtk_get_current_event() && gtk_window_has_toplevel_focus(GTK_WINDOW(parent))) {
+			/* The window is in focus, and the new window was not triggered by a keypress/click
+			 * event. So do not set it transient, to avoid focus stealing and all that.
+			 */
+			return FALSE;
+		}
+		gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(parent));
+		return TRUE;
+	}
+	return FALSE;
+#endif
+#else
+	/* This finds the currently active window and makes that the parent window. */
+	GList *windows = NULL;
+	GtkWidget *parent = NULL;
+	GdkEvent *event = gtk_get_current_event();
+	GdkWindow *menu = NULL;
+
+	if (event == NULL)
+		/* The window was not triggered by a user action. */
+		return FALSE;
+
+	/* We need to special case events from a popup menu. */
+	if (event->type == GDK_BUTTON_RELEASE) {
+		/* XXX: Neither of the following works:
+			menu = event->button.window;
+			menu = gdk_window_get_parent(event->button.window);
+			menu = gdk_window_get_toplevel(event->button.window);
+		*/
+	} else if (event->type == GDK_KEY_PRESS)
+		menu = event->key.window;
+
+	windows = gtk_window_list_toplevels();
+	while (windows) {
+		GtkWidget *window = windows->data;
+		windows = g_list_delete_link(windows, windows);
+
+		if (window == widget ||
+				!GTK_WIDGET_VISIBLE(window)) {
+			continue;
+		}
+
+		if (gtk_window_has_toplevel_focus(GTK_WINDOW(window)) ||
+				(menu && menu == window->window)) {
+			parent = window;
+			break;
+		}
+	}
+	if (windows)
+		g_list_free(windows);
+	if (parent) {
+		gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(parent));
+		return TRUE;
+	}
+	return FALSE;
+#endif
+}
+
--- a/pidgin/gtkutils.h	Tue Dec 18 03:35:20 2007 +0000
+++ b/pidgin/gtkutils.h	Tue Dec 18 05:50:43 2007 +0000
@@ -121,6 +121,61 @@
 GtkWidget *pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable);
 
 /**
+ * Creates a new dialog window
+ *
+ * @param title        The window title, or @c NULL
+ * @param border_width The window's desired border width
+ * @param role         A string indicating what the window is responsible for doing, or @c NULL
+ * @param resizable    Whether the window should be resizable (@c TRUE) or not (@c FALSE)
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable);
+
+/**
+ * Retrieves the main content box (vbox) from a pidgin dialog window
+ *
+ * @param dialog       The dialog window
+ * @param homogeneous  TRUE if all children are to be given equal space allotments. 
+ * @param spacing      the number of pixels to place by default between children
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing);
+
+/**
+ * Retrieves the main content box (vbox) from a pidgin dialog window
+ *
+ * @param dialog       The dialog window
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog);
+
+/**
+ * Add a button to a dialog created by #pidgin_create_dialog.
+ *
+ * @param dialog         The dialog window
+ * @param label          The stock-id or the label for the button
+ * @param callback       The callback function for the button
+ * @param callbackdata   The user data for the callback function
+ *
+ * @return The created button.
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label,
+		GCallback callback, gpointer callbackdata);
+
+/**
+ * Retrieves the action area (button box) from a pidgin dialog window
+ *
+ * @param dialog       The dialog window
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog);
+
+/**
  * Toggles the sensitivity of a widget.
  *
  * @param widget    @c NULL. Used for signal handlers.
@@ -725,5 +780,15 @@
  */
 void pidgin_text_combo_box_entry_set_text(GtkWidget *widget, const char *text);
 
+/**
+ * Automatically make a window transient to a suitable parent window.
+ *
+ * @param window    The window to make transient.
+ *
+ * @return  Whether the window was made transient or not.
+ * @since 2.4.0
+ */
+gboolean pidgin_auto_parent_window(GtkWidget *window);
+
 #endif /* _PIDGINUTILS_H_ */