changeset 18019:1e674c0886b1

propagate from branch 'im.pidgin.pidgin' (head b917cc2cf9c46c7163ad3345b312dac1eea896cf) to branch 'im.pidgin.rlaager.merging.for_2_1_0' (head 6d4701b6ce24327540b11d93b4e203334a042c6e)
author Richard Laager <rlaager@wiktel.com>
date Sun, 20 May 2007 18:40:58 +0000
parents 2c10f77c2378 (diff) ddf759c28d0b (current diff)
children 3c4811489e30
files ChangeLog pidgin/gtkconv.c
diffstat 41 files changed, 295 insertions(+), 131 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun May 20 18:32:40 2007 +0000
+++ b/ChangeLog	Sun May 20 18:40:58 2007 +0000
@@ -1,5 +1,18 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.1.0 (??/??/????):
+	libpurple:
+	* Core changes to allow UIs to use second-granularity for scheduling.
+	  Pidgin and Finch, which use the glib event loop, were changed to use
+	  g_timeout_add_seconds() on glib >= 2.14 when possible.  This allows
+	  glib to properly group our longer timers to increase power efficiency.
+	  (Arjan van de Ven with Intel Corporation)
+
+	Pidgin:
+	* Ensure only one copy of Pidgin is running with a given configuration
+	  directory.  The net effect of this is that trying to start Pidgin a
+	  second time will raise the buddy list.  (Gabriel Schulhof)
+
 version 2.0.1 (??/??/????):
 	* Buddy list update speedups when buddy icons are not being
 	  displayed.  (Scott Wolchok)
--- a/ChangeLog.API	Sun May 20 18:32:40 2007 +0000
+++ b/ChangeLog.API	Sun May 20 18:40:58 2007 +0000
@@ -1,5 +1,19 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.1.0 (??/??/????):
+	Added:
+	* PurpleEventLoopUiOps.timeout_add_seconds
+	    UIs can now use better scheduling for whole-second timers.  For
+	    example, clients based on the glib event loop can now use
+	    g_timeout_add_seconds().
+	* pidgin_create_window()
+	* purple_core_ensure_single_instance()
+	    This is for UIs to use to ensure only one copy is running.
+	* purple_dbus_is_owner()
+	* purple_timeout_add_seconds()
+	    Callers should prefer this to purple_timeout_add() for timers
+	    longer than 1 second away.  Be aware of the rounding, though.
+
 version 2.0.0 (5/3/2007):
 	Please note all functions, defines, and data structures have been
 	re-namespaced to match the new names of Pidgin, Finch, and libpurple.
--- a/configure.ac	Sun May 20 18:32:40 2007 +0000
+++ b/configure.ac	Sun May 20 18:40:58 2007 +0000
@@ -43,10 +43,10 @@
 #
 # Make sure to update finch/libgnt/configure.ac with libgnt version changes.
 #
-m4_define([purple_lt_current], [0])
+m4_define([purple_lt_current], [1])
 m4_define([purple_major_version], [2])
-m4_define([purple_minor_version], [0])
-m4_define([purple_micro_version], [1])
+m4_define([purple_minor_version], [1])
+m4_define([purple_micro_version], [0])
 m4_define([purple_version_suffix], [devel])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
--- a/finch/finch.c	Sun May 20 18:32:40 2007 +0000
+++ b/finch/finch.c	Sun May 20 18:40:58 2007 +0000
@@ -156,11 +156,15 @@
 	gnt_input_add,
 	g_source_remove,
 	NULL, /* input_get_error */
+#if GLIB_CHECK_VERSION(2,14,0)
+	g_timeout_add_seconds,
+#else
+	NULL,
+#endif
 
 	/* padding */
 	NULL,
 	NULL,
-	NULL,
 	NULL
 };
 
--- a/libpurple/account.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/account.c	Sun May 20 18:40:58 2007 +0000
@@ -417,7 +417,7 @@
 schedule_accounts_save()
 {
 	if (save_timer == 0)
-		save_timer = purple_timeout_add(5000, save_cb, NULL);
+		save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
 }
 
 
--- a/libpurple/blist.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/blist.c	Sun May 20 18:40:58 2007 +0000
@@ -365,7 +365,7 @@
 purple_blist_schedule_save()
 {
 	if (save_timer == 0)
-		save_timer = purple_timeout_add(5000, save_cb, NULL);
+		save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
 }
 
 
--- a/libpurple/connection.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/connection.c	Sun May 20 18:40:58 2007 +0000
@@ -72,7 +72,7 @@
 	if (on && !gc->keepalive)
 	{
 		purple_debug_info("connection", "Activating keepalive.\n");
-		gc->keepalive = purple_timeout_add(30000, send_keepalive, gc);
+		gc->keepalive = purple_timeout_add_seconds(30, send_keepalive, gc);
 	}
 	else if (!on && gc->keepalive > 0)
 	{
--- a/libpurple/conversation.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/conversation.c	Sun May 20 18:40:58 2007 +0000
@@ -1010,7 +1010,7 @@
 	conv = purple_conv_im_get_conversation(im);
 	name = purple_conversation_get_name(conv);
 
-	im->typing_timeout = purple_timeout_add(timeout * 1000, reset_typing_cb, conv);
+	im->typing_timeout = purple_timeout_add_seconds(timeout, reset_typing_cb, conv);
 }
 
 void
--- a/libpurple/core.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/core.c	Sun May 20 18:40:58 2007 +0000
@@ -48,7 +48,11 @@
 #include "util.h"
 
 #ifdef HAVE_DBUS
+#  define DBUS_API_SUBJECT_TO_CHANGE
+#  include <dbus/dbus.h>
+#  include "dbus-purple.h"
 #  include "dbus-server.h"
+#  include "dbus-bindings.h"
 #endif
 
 struct PurpleCore
@@ -276,6 +280,91 @@
 	return _ops;
 }
 
+#ifdef HAVE_DBUS
+static char *purple_dbus_owner_user_dir(void)
+{
+	DBusMessage *msg = NULL, *reply = NULL;
+	DBusConnection *dbus_connection = NULL;
+	DBusError dbus_error;
+	char *remote_user_dir = NULL;
+
+	if ((dbus_connection = purple_dbus_get_connection()) == NULL)
+		return NULL;
+
+	if ((msg = dbus_message_new_method_call(DBUS_SERVICE_PURPLE, DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, "PurpleUserDir")) == NULL)
+		return NULL;
+
+	dbus_error_init(&dbus_error);
+	reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, 5000, &dbus_error);
+	dbus_message_unref(msg);
+	dbus_error_free(&dbus_error);
+
+	if (reply)
+	{
+		dbus_error_init(&dbus_error);
+		dbus_message_get_args(reply, &dbus_error, DBUS_TYPE_STRING, &remote_user_dir, DBUS_TYPE_INVALID);
+		remote_user_dir = g_strdup(remote_user_dir);
+		dbus_error_free(&dbus_error);
+		dbus_message_unref(reply);
+	}
+
+	return remote_user_dir;
+}
+
+static void purple_dbus_owner_show_buddy_list(void)
+{
+	DBusError dbus_error;
+	DBusMessage *msg = NULL, *reply = NULL;
+	DBusConnection *dbus_connection = NULL;
+
+	if ((dbus_connection = purple_dbus_get_connection()) == NULL)
+		return;
+
+	if ((msg = dbus_message_new_method_call(DBUS_SERVICE_PURPLE, DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, "PurpleBlistShow")) == NULL)
+		return;
+
+	dbus_error_init(&dbus_error);
+	if ((reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, 5000, &dbus_error)) != NULL)
+	{
+		dbus_message_unref(msg);
+	}
+	dbus_error_free(&dbus_error);
+}
+#endif /* HAVE_DBUS */
+
+gboolean
+purple_core_ensure_single_instance()
+{
+	gboolean is_single_instance = TRUE;
+#ifdef HAVE_DBUS
+	/* in the future, other mechanisms might have already set this to FALSE */
+	if (is_single_instance)
+	{
+		if (!purple_dbus_is_owner())
+		{
+			const char *user_dir = purple_user_dir();
+			char *dbus_owner_user_dir = purple_dbus_owner_user_dir();
+
+			if (NULL == user_dir && NULL != dbus_owner_user_dir)
+				is_single_instance = TRUE;
+			else if (NULL != user_dir && NULL == dbus_owner_user_dir)
+				is_single_instance = TRUE;
+			else if (NULL == user_dir && NULL == dbus_owner_user_dir)
+				is_single_instance = FALSE;
+			else
+				is_single_instance = strcmp(dbus_owner_user_dir, user_dir);
+
+			if (!is_single_instance)
+				purple_dbus_owner_show_buddy_list();
+
+			g_free(dbus_owner_user_dir);
+		}
+	}
+#endif /* HAVE_DBUS */
+
+	return is_single_instance;
+}
+
 static gboolean
 move_and_symlink_dir(const char *path, const char *basename, const char *old_base, const char *new_base, const char *relative)
 {
--- a/libpurple/core.h	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/core.h	Sun May 20 18:40:58 2007 +0000
@@ -121,6 +121,16 @@
  */
 gboolean purple_core_migrate(void);
 
+/**
+ * Ensures that only one instance is running.
+ *
+ * @return A boolean such that @c TRUE indicates that this is the first instance,
+ *         whereas @c FALSE indicates that there is another instance running.
+ *
+ * @since 2.1.0
+ */
+gboolean purple_core_ensure_single_instance(void);
+
 #ifdef __cplusplus
 }
 #endif
--- a/libpurple/dbus-server.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/dbus-server.c	Sun May 20 18:40:58 2007 +0000
@@ -65,6 +65,12 @@
 static GHashTable *map_id_type;
 
 static gchar *init_error;
+static int dbus_request_name_reply = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+
+gboolean purple_dbus_is_owner(void)
+{
+	return(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER == dbus_request_name_reply);
+}
 
 /**
  * This function initializes the pointer-id traslation system.  It
@@ -592,6 +598,7 @@
 		return;
 	}
 
+	dbus_request_name_reply =
 	result = dbus_bus_request_name(purple_dbus_connection,
 			DBUS_SERVICE_PURPLE, 0, &error);
 
--- a/libpurple/dbus-server.h	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/dbus-server.h	Sun May 20 18:40:58 2007 +0000
@@ -169,6 +169,13 @@
 void *purple_dbus_get_handle(void);
 
 /**
+ * Determines whether this instance owns the DBus service name
+ * 
+ * @since 2.1.0
+ */
+gboolean purple_dbus_is_owner(void);
+
+/**
  * Starts Purple's D-BUS server.  It is responsible for handling DBUS
  * requests from other applications.
  */
--- a/libpurple/eventloop.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/eventloop.c	Sun May 20 18:40:58 2007 +0000
@@ -35,6 +35,17 @@
 	return ops->timeout_add(interval, function, data);
 }
 
+guint
+purple_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data)
+{
+	PurpleEventLoopUiOps *ops = purple_eventloop_get_ui_ops();
+
+	if (ops->timeout_add_seconds)
+		return ops->timeout_add_seconds(interval, function, data);
+	else
+		return ops->timeout_add(1000 * interval, function, data);
+}
+
 gboolean
 purple_timeout_remove(guint tag)
 {
--- a/libpurple/eventloop.h	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/eventloop.h	Sun May 20 18:40:58 2007 +0000
@@ -48,7 +48,7 @@
 struct _PurpleEventLoopUiOps
 {
 	/**
-	 * Creates a callback timer.
+	 * Creates a callback timer with an interval measured in milliseconds.
 	 * @see g_timeout_add, purple_timeout_add
 	 **/
 	guint (*timeout_add)(guint interval, GSourceFunc function, gpointer data);
@@ -81,7 +81,20 @@
 	 */
 	int (*input_get_error)(int fd, int *error);
 
-	void (*_purple_reserved1)(void);
+	/**
+	 * Creates a callback timer with an interval measured in seconds.
+	 *
+	 * This allows UIs to group timers for better power efficiency.  For
+	 * this reason, @a interval may be rounded by up to a second.
+	 *
+	 * Implementation of this UI op is optional.  If it's not implemented,
+	 * calls to purple_timeout_add_seconds() will be serviced by the
+	 * timeout_add UI op.
+	 *
+	 * @see g_timeout_add_seconds, purple_timeout_add_seconds()
+	 **/
+	guint (*timeout_add_seconds)(guint interval, GSourceFunc function, gpointer data);
+
 	void (*_purple_reserved2)(void);
 	void (*_purple_reserved3)(void);
 	void (*_purple_reserved4)(void);
@@ -93,10 +106,15 @@
 /*@{*/
 /**
  * Creates a callback timer.
+ * 
  * The timer will repeat until the function returns @c FALSE. The
  * first call will be at the end of the first interval.
+ *
+ * If the timer is in a multiple of seconds, use purple_timeout_add_seconds()
+ * instead as it allows UIs to group timers for power efficiency.
+ *
  * @param interval	The time between calls of the function, in
- *					milliseconds.
+ *                      milliseconds.
  * @param function	The function to call.
  * @param data		data to pass to @a function.
  * @return A handle to the timer which can be passed to 
@@ -105,6 +123,24 @@
 guint purple_timeout_add(guint interval, GSourceFunc function, gpointer data);
 
 /**
+ * Creates a callback timer.
+ *
+ * The timer will repeat until the function returns @c FALSE. The
+ * first call will be at the end of the first interval.
+ *
+ * This function allows UIs to group timers for better power efficiency.  For
+ * this reason, @a interval may be rounded by up to a second.
+ * 
+ * @param interval	The time between calls of the function, in
+ *                      seconds.
+ * @param function	The function to call.
+ * @param data		data to pass to @a function.
+ * @return A handle to the timer which can be passed to 
+ *         purple_timeout_remove to remove the timer.
+ */
+guint purple_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data);
+
+/**
  * Removes a timeout handler.
  *
  * @param handle The handle, as returned by purple_timeout_add.
--- a/libpurple/example/nullclient.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/example/nullclient.c	Sun May 20 18:40:58 2007 +0000
@@ -108,11 +108,15 @@
 	glib_input_add,
 	g_source_remove,
 	NULL,
+#if GLIB_CHECK_VERSION(2,14,0)
+	g_timeout_add_seconds,
+#else
+	NULL,
+#endif
 
 	/* padding */
 	NULL,
 	NULL,
-	NULL,
 	NULL
 };
 /*** End of the eventloop functions. ***/
--- a/libpurple/idle.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/idle.c	Sun May 20 18:40:58 2007 +0000
@@ -230,7 +230,11 @@
 	if (time_until_next_idle_event == 0)
 		idle_timer = 0;
 	else
-		idle_timer = purple_timeout_add(1000 * (time_until_next_idle_event + 1), check_idleness_timer, NULL);
+	{
+		/* +1 for the boundary,
+		 * +1 more for g_timeout_add_seconds rounding. */
+		idle_timer = purple_timeout_add_seconds(time_until_next_idle_event + 2, check_idleness_timer, NULL);
+	}
 	return FALSE;
 }
 
@@ -309,8 +313,10 @@
 void
 purple_idle_init()
 {
-	/* Add the timer to check if we're idle */
-	idle_timer = purple_timeout_add(1000 * (IDLEMARK + 1), check_idleness_timer, NULL);
+	/* Add the timer to check if we're idle.
+	 * IDLEMARK + 1 as the boundary,
+	 * +1 more for g_timeout_add_seconds rounding. */
+	idle_timer = purple_timeout_add_seconds((IDLEMARK + 2), check_idleness_timer, NULL);
 
 	purple_signal_connect(purple_conversations_get_handle(), "sent-im-msg",
 						purple_idle_get_handle(),
--- a/libpurple/pounce.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/pounce.c	Sun May 20 18:40:58 2007 +0000
@@ -273,7 +273,7 @@
 schedule_pounces_save(void)
 {
 	if (save_timer == 0)
-		save_timer = purple_timeout_add(5000, save_cb, NULL);
+		save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
 }
 
 
--- a/libpurple/prefs.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/prefs.c	Sun May 20 18:40:58 2007 +0000
@@ -226,7 +226,7 @@
 schedule_prefs_save(void)
 {
 	if (save_timer == 0)
-		save_timer = purple_timeout_add(5000, save_cb, NULL);
+		save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
 }
 
 
--- a/libpurple/savedstatuses.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/savedstatuses.c	Sun May 20 18:40:58 2007 +0000
@@ -357,7 +357,7 @@
 schedule_save(void)
 {
 	if (save_timer == 0)
-		save_timer = purple_timeout_add(5000, save_cb, NULL);
+		save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
 }
 
 
--- a/libpurple/server.c	Sun May 20 18:32:40 2007 +0000
+++ b/libpurple/server.c	Sun May 20 18:40:58 2007 +0000
@@ -92,7 +92,7 @@
 
 	/* because we're modifying or creating a lar, schedule the
 	 * function to expire them as the pref dictates */
-	purple_timeout_add((SECS_BEFORE_RESENDING_AUTORESPONSE + 1) * 1000, expire_last_auto_responses, NULL);
+	purple_timeout_add_seconds((SECS_BEFORE_RESENDING_AUTORESPONSE + 1), expire_last_auto_responses, NULL);
 
 	tmp = last_auto_responses;
 
--- a/pidgin/gtkaccount.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkaccount.c	Sun May 20 18:40:58 2007 +0000
@@ -1447,17 +1447,8 @@
 		dialog->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin);
 
 
-	dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(win), "account");
-
-	if (type == PIDGIN_ADD_ACCOUNT_DIALOG)
-		gtk_window_set_title(GTK_WINDOW(win), _("Add Account"));
-	else
-		gtk_window_set_title(GTK_WINDOW(win), _("Modify Account"));
-
-	gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
-
-	gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER);
+	dialog->window = win = pidgin_create_window((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);
@@ -2294,7 +2285,6 @@
 	GtkWidget *button;
 	int width, height;
 
-
 	if (accounts_window != NULL) {
 		gtk_window_present(GTK_WINDOW(accounts_window->window));
 		return;
@@ -2305,11 +2295,8 @@
 	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 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	dialog->window = win = pidgin_create_window(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
-	gtk_window_set_role(GTK_WINDOW(win), "accounts");
-	gtk_window_set_title(GTK_WINDOW(win), _("Accounts"));
-	gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(accedit_win_destroy_cb), accounts_window);
--- a/pidgin/gtkblist.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkblist.c	Sun May 20 18:40:58 2007 +0000
@@ -4195,7 +4195,7 @@
 				{"application/x-im-contact", 0, DRAG_BUDDY},
 				{"text/x-vcard", 0, DRAG_VCARD }};
 	if (gtkblist && gtkblist->window) {
-		purple_blist_set_visible(purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/list_visible"));
+		purple_blist_set_visible(TRUE);
 		return;
 	}
 
@@ -4204,9 +4204,7 @@
 	gtkblist->empty_avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
 	gdk_pixbuf_fill(gtkblist->empty_avatar, 0x00000000);
 
-	gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(gtkblist->window), "buddy_list");
-	gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List"));
+	gtkblist->window = pidgin_create_window(_("Buddy List"), 0, "buddy_list", TRUE);
 	g_signal_connect(G_OBJECT(gtkblist->window), "focus-in-event",
 			 G_CALLBACK(blist_focus_cb), gtkblist);
 	GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE;
--- a/pidgin/gtkconv.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkconv.c	Sun May 20 18:40:58 2007 +0000
@@ -8042,10 +8042,7 @@
 	window_list = g_list_append(window_list, win);
 
 	/* Create the window. */
-	win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(win->window), "conversation");
-	gtk_window_set_resizable(GTK_WINDOW(win->window), TRUE);
-	gtk_container_set_border_width(GTK_CONTAINER(win->window), 0);
+	win->window = pidgin_create_window(NULL, 0, "conversation", TRUE);
 	GTK_WINDOW(win->window)->allow_shrink = TRUE;
 
 	if (available_list == NULL) {
--- a/pidgin/gtkeventloop.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkeventloop.c	Sun May 20 18:40:58 2007 +0000
@@ -120,7 +120,11 @@
 	pidgin_input_add,
 	g_source_remove,
 	NULL, /* input_get_error */
+#if GLIB_CHECK_VERSION(2,14,0)
+	g_timeout_add_seconds,
+#else
 	NULL,
+#endif
 	NULL,
 	NULL,
 	NULL
--- a/pidgin/gtkft.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkft.c	Sun May 20 18:40:58 2007 +0000
@@ -758,10 +758,7 @@
 		purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished");
 
 	/* Create the window. */
-	dialog->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(window), "file transfer");
-	gtk_window_set_title(GTK_WINDOW(window), _("File Transfers"));
-	gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
+	dialog->window = window = pidgin_create_window(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
--- a/pidgin/gtkmain.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkmain.c	Sun May 20 18:40:58 2007 +0000
@@ -730,6 +730,15 @@
 		abort();
 	}
 
+	if (!purple_core_ensure_single_instance()) {
+		purple_core_quit();
+#ifdef HAVE_SIGNAL_H
+		g_free(segfault_message);
+#endif
+		return 0;
+	}
+		
+
 	/* TODO: Move blist loading into purple_blist_init() */
 	purple_set_blist(purple_blist_new());
 	purple_blist_load();
--- a/pidgin/gtknotify.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtknotify.c	Sun May 20 18:40:58 2007 +0000
@@ -577,10 +577,8 @@
 	char label_text[2048];
 	char *linked_text, *primary_esc, *secondary_esc;
 
-	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_title(GTK_WINDOW(window), title);
+	window = pidgin_create_window(title, PIDGIN_HIG_BORDER, NULL, TRUE);
 	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
-	gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(formatted_close_cb), NULL);
@@ -712,10 +710,8 @@
 	data->results = results;
 
 	/* Create the window */
-	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_title(GTK_WINDOW(window), (title ? title :_("Search Results")));
+	pidgin_create_window(title ? title :_("Search Results"), PIDGIN_HIG_BORDER, NULL, TRUE);
 	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
-	gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
 
 	g_signal_connect_swapped(G_OBJECT(window), "delete_event",
 							 G_CALLBACK(searchresults_close_cb), data);
--- a/pidgin/gtkpounce.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkpounce.c	Sun May 20 18:40:58 2007 +0000
@@ -498,15 +498,9 @@
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
 	/* Create the window. */
-	dialog->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	dialog->window = window = pidgin_create_window((cur_pounce == NULL ? _("New Buddy Pounce") : _("Edit Buddy Pounce")),
+		PIDGIN_HIG_BORDER, "buddy_pounce", FALSE) ;
 	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
-	gtk_window_set_role(GTK_WINDOW(window), "buddy_pounce");
-	gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
-	gtk_window_set_title(GTK_WINDOW(window),
-						 (cur_pounce == NULL
-						  ? _("New Buddy Pounce") : _("Edit Buddy Pounce")));
-
-	gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
@@ -1323,11 +1317,8 @@
 	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 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	dialog->window = win = pidgin_create_window(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
-	gtk_window_set_role(GTK_WINDOW(win), "pounces");
-	gtk_window_set_title(GTK_WINDOW(win), _("Buddy Pounces"));
-	gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(pounces_manager_destroy_cb), dialog);
--- a/pidgin/gtkprefs.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkprefs.c	Sun May 20 18:40:58 2007 +0000
@@ -1948,11 +1948,7 @@
 	/* Back to instant-apply! I win!  BU-HAHAHA! */
 
 	/* Create the window */
-	prefs = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(prefs), "preferences");
-	gtk_window_set_title(GTK_WINDOW(prefs), _("Preferences"));
-	gtk_window_set_resizable (GTK_WINDOW(prefs), FALSE);
-	gtk_container_set_border_width(GTK_CONTAINER(prefs), PIDGIN_HIG_BORDER);
+	prefs = pidgin_create_window(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE);
 	g_signal_connect(G_OBJECT(prefs), "destroy",
 					 G_CALLBACK(delete_prefs), NULL);
 
--- a/pidgin/gtkprivacy.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkprivacy.c	Sun May 20 18:40:58 2007 +0000
@@ -366,11 +366,7 @@
 
 	dialog = g_new0(PidginPrivacyDialog, 1);
 
-	dialog->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_resizable(GTK_WINDOW(dialog->win), FALSE);
-	gtk_window_set_role(GTK_WINDOW(dialog->win), "privacy");
-	gtk_window_set_title(GTK_WINDOW(dialog->win), _("Privacy"));
-	gtk_container_set_border_width(GTK_CONTAINER(dialog->win), PIDGIN_HIG_BORDER);
+	dialog->win = pidgin_create_window(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", FALSE);
 
 	g_signal_connect(G_OBJECT(dialog->win), "delete_event",
 					 G_CALLBACK(destroy_cb), dialog);
--- a/pidgin/gtkrequest.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkrequest.c	Sun May 20 18:40:58 2007 +0000
@@ -1069,16 +1069,12 @@
 	data->cbs[0] = ok_cb;
 	data->cbs[1] = cancel_cb;
 
-	data->dialog = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-
-	if (title != NULL)
-		gtk_window_set_title(GTK_WINDOW(win), title);
+	
 #ifdef _WIN32
-		gtk_window_set_title(GTK_WINDOW(win), PIDGIN_ALERT_TITLE);
-#endif
-
-	gtk_window_set_role(GTK_WINDOW(win), "multifield");
-	gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER);
+	data->dialog = win = pidgin_create_window(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
+#else /* !_WIN32 */
+	data->dialog = win = pidgin_create_window(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
+#endif /* _WIN32 */
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(destroy_multifield_cb), data);
--- a/pidgin/gtkroomlist.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkroomlist.c	Sun May 20 18:40:58 2007 +0000
@@ -371,11 +371,7 @@
 	dialog->account = account;
 
 	/* Create the window. */
-	dialog->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(window), "room list");
-	gtk_window_set_title(GTK_WINDOW(window), _("Room List"));
-
-	gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
+	dialog->window = window = pidgin_create_window(_("Room List"), PIDGIN_HIG_BORDER, "room list", TRUE);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
--- a/pidgin/gtksavedstatuses.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtksavedstatuses.c	Sun May 20 18:40:58 2007 +0000
@@ -551,11 +551,8 @@
 	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 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	dialog->window = win = pidgin_create_window(_("Saved Statuses"), PIDGIN_HIG_BORDER, "statuses", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
-	gtk_window_set_role(GTK_WINDOW(win), "statuses");
-	gtk_window_set_title(GTK_WINDOW(win), _("Saved Statuses"));
-	gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(status_window_destroy_cb), dialog);
@@ -1085,11 +1082,7 @@
 	if (edit)
 		dialog->original_title = g_strdup(purple_savedstatus_get_title(saved_status));
 
-	dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(win), "status");
-	gtk_window_set_title(GTK_WINDOW(win), _("Status"));
-	gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
-	gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER);
+	dialog->window = win = pidgin_create_window (_("Status"), PIDGIN_HIG_BORDER, "status", FALSE) ;
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(status_editor_destroy_cb), dialog);
@@ -1423,13 +1416,9 @@
 	dialog->status_editor = status_editor;
 	dialog->account = account;
 
-	dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(win), "substatus");
 	tmp = g_strdup_printf(_("Status for %s"), purple_account_get_username(account));
-	gtk_window_set_title(GTK_WINDOW(win), tmp);
+	dialog->window = win = pidgin_create_window(tmp, PIDGIN_HIG_BORDER, "substatus", FALSE) ;
 	g_free(tmp);
-	gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
-	gtk_container_set_border_width(GTK_CONTAINER(win), PIDGIN_HIG_BORDER);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(substatus_editor_destroy_cb), dialog);
--- a/pidgin/gtkutils.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkutils.c	Sun May 20 18:40:58 2007 +0000
@@ -122,6 +122,22 @@
 }
 
 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));
+	if (title)
+		gtk_window_set_title(wnd, title);
+	gtk_container_set_border_width(GTK_CONTAINER(wnd), border_width);
+	if (role)
+		gtk_window_set_role(wnd, role);
+	gtk_window_set_resizable(wnd, resizable);
+
+	return GTK_WIDGET(wnd);
+}
+
+GtkWidget *
 pidgin_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret)
 {
 	GtkWidget *frame;
--- a/pidgin/gtkutils.h	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkutils.h	Sun May 20 18:40:58 2007 +0000
@@ -93,6 +93,18 @@
 GtkWidget *pidgin_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret);
 
 /**
+ * Creates a new 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.1.0
+ */
+GtkWidget *pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable);
+
+/**
  * Toggles the sensitivity of a widget.
  *
  * @param widget    @c NULL. Used for signal handlers.
--- a/pidgin/gtkwhiteboard.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/gtkwhiteboard.c	Sun May 20 18:40:58 2007 +0000
@@ -28,6 +28,7 @@
 #include "debug.h"
 
 #include "gtkwhiteboard.h"
+#include "gtkutils.h"
 
 /******************************************************************************
  * Prototypes
@@ -143,21 +144,14 @@
 		gtkwb->brush_color = 0xff0000;
 	}
 
-	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtkwb->window = window;
-	gtk_widget_set_name(window, wb->who);
-
 	/* Try and set window title as the name of the buddy, else just use their
 	 * username
 	 */
 	buddy = purple_find_buddy(wb->account, wb->who);
 
-	if (buddy != NULL)
-		gtk_window_set_title((GtkWindow*)(window), purple_buddy_get_contact_alias(buddy));
-	else
-		gtk_window_set_title((GtkWindow*)(window), wb->who);
-
-	gtk_window_set_resizable((GtkWindow*)(window), FALSE);
+	window = pidgin_create_window(buddy != NULL ? purple_buddy_get_contact_alias(buddy) : wb->who, 0, NULL, FALSE);
+	gtkwb->window = window;
+	gtk_widget_set_name(window, wb->who);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(whiteboard_close_cb), gtkwb);
--- a/pidgin/plugins/gevolution/add_buddy_dialog.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/plugins/gevolution/add_buddy_dialog.c	Sun May 20 18:40:58 2007 +0000
@@ -442,10 +442,7 @@
 	if (username != NULL)
 		dialog->username = g_strdup(username);
 
-	dialog->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(dialog->win), "add_buddy");
-	gtk_window_set_title(GTK_WINDOW(dialog->win), _("Add Buddy"));
-	gtk_container_set_border_width(GTK_CONTAINER(dialog->win), 12);
+	dialog->win = pidgin_create_window(_("Add Buddy"), PIDGIN_HIG_BORDER, "add_buddy", TRUE);
 	gtk_widget_set_size_request(dialog->win, -1, 400);
 
 	g_signal_connect(G_OBJECT(dialog->win), "delete_event",
--- a/pidgin/plugins/gevolution/assoc-buddy.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/plugins/gevolution/assoc-buddy.c	Sun May 20 18:40:58 2007 +0000
@@ -329,9 +329,7 @@
 
 	dialog->buddy = buddy;
 
-	dialog->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(dialog->win), "assoc_buddy");
-	gtk_container_set_border_width(GTK_CONTAINER(dialog->win), 12);
+	dialog->win = pidgin_create_window(NULL, PIDGIN_HIG_BORDER, "assoc_buddy", TRUE);
 
 	g_signal_connect(G_OBJECT(dialog->win), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
--- a/pidgin/plugins/gevolution/new_person_dialog.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/plugins/gevolution/new_person_dialog.c	Sun May 20 18:40:58 2007 +0000
@@ -246,11 +246,7 @@
 	dialog->book = book;
 	g_object_ref(book);
 
-	dialog->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_role(GTK_WINDOW(dialog->win), "new_person");
-	gtk_window_set_title(GTK_WINDOW(dialog->win), _("New Person"));	
-	gtk_window_set_resizable(GTK_WINDOW(dialog->win), FALSE);
-	gtk_container_set_border_width(GTK_CONTAINER(dialog->win), 12);
+	dialog->win = pidgin_create_window(_("New Person"), PIDGIN_HIG_BORDER, "new_person", FALSE);
 
 	g_signal_connect(G_OBJECT(dialog->win), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
--- a/pidgin/plugins/ticker/ticker.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/plugins/ticker/ticker.c	Sun May 20 18:40:58 2007 +0000
@@ -36,6 +36,7 @@
 
 #include "gtkblist.h"
 #include "gtkplugin.h"
+#include "gtkutils.h"
 
 #include "gtkticker.h"
 
@@ -70,12 +71,10 @@
 		return;
 	}
 
-	tickerwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	tickerwindow = pidgin_create_window(_("Buddy Ticker"), 0, "ticker", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(tickerwindow), 500, -1);
 	g_signal_connect(G_OBJECT(tickerwindow), "delete_event",
 			G_CALLBACK (buddy_ticker_destroy_window), NULL);
-	gtk_window_set_title (GTK_WINDOW(tickerwindow), _("Buddy Ticker"));
-	gtk_window_set_role (GTK_WINDOW(tickerwindow), "ticker");
 
 	ticker = gtk_ticker_new();
 	gtk_ticker_set_spacing(GTK_TICKER(ticker), 20);
--- a/pidgin/plugins/xmppconsole.c	Sun May 20 18:32:40 2007 +0000
+++ b/pidgin/plugins/xmppconsole.c	Sun May 20 18:40:58 2007 +0000
@@ -29,6 +29,7 @@
 #if !GTK_CHECK_VERSION(2,4,0)
 #include "pidgincombobox.h"
 #endif
+#include "gtkutils.h"
 
 typedef struct {
 	PurpleConnection *gc;
@@ -741,10 +742,8 @@
 	
 	console = g_new0(XmppConsole, 1);
 
-	console->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_window_set_title(GTK_WINDOW(console->window), _("XMPP Console"));
+	console->window = pidgin_create_window(_("XMPP Console"), PIDGIN_HIG_BORDER, NULL, TRUE);
 	g_signal_connect(G_OBJECT(console->window), "destroy", G_CALLBACK(console_destroy), NULL);
-	gtk_container_set_border_width(GTK_CONTAINER(console->window), 12);
 	gtk_window_set_default_size(GTK_WINDOW(console->window), 580, 400);
 	gtk_container_add(GTK_CONTAINER(console->window), vbox);