changeset 14133:31d33e7bc0e6

[gaim-migrate @ 16775] A global buddy icon selector in the statusbox. This is done totally in the GTK+ UI; the core still sees a buddy icon as belonging to an account. Per-account icons can override the global one in Modify Account. There are some caching issues to work out, still. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Tue, 15 Aug 2006 23:25:29 +0000
parents 2c85b0f97dd0
children 4006d3dc2871
files src/gtkaccount.c src/gtkblist.c src/gtkstatusbox.c src/gtkstatusbox.h src/gtkutils.c src/gtkutils.h
diffstat 6 files changed, 685 insertions(+), 416 deletions(-) [+]
line wrap: on
line diff
--- a/src/gtkaccount.c	Tue Aug 15 20:23:58 2006 +0000
+++ b/src/gtkaccount.c	Tue Aug 15 23:25:29 2006 +0000
@@ -115,6 +115,7 @@
 	GtkWidget *user_frame;
 	GtkWidget *new_mail_check;
 	GtkWidget *icon_hbox;
+	GtkWidget *icon_check;
 	GtkWidget *icon_entry;
 	char *icon_path;
 	GtkWidget *icon_filesel;
@@ -153,8 +154,6 @@
 static void add_account_to_liststore(GaimAccount *account, gpointer user_data);
 static void set_account(GtkListStore *store, GtkTreeIter *iter,
 						  GaimAccount *account);
-static char*
-convert_buddy_icon(GaimPlugin *plugin, const char *path);
 
 /**************************************************************************
  * Add/Modify Account dialog
@@ -271,210 +270,24 @@
 	}
 }
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 static void
-icon_filesel_choose_cb(GtkWidget *widget, gint response, AccountPrefsDialog *dialog)
-{
-	char *filename, *current_folder;
-
-	if (response != GTK_RESPONSE_ACCEPT) {
-		if (response == GTK_RESPONSE_CANCEL)
-			gtk_widget_destroy(dialog->icon_filesel);
-		dialog->icon_filesel = NULL;
-		return;
-	}
-
-	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog->icon_filesel));
-	current_folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel));
-	if (current_folder != NULL) {
-		gaim_prefs_set_string("/gaim/gtk/filelocations/last_icon_folder", current_folder);
-		g_free(current_folder);
-	}
-
-#else /* FILECHOOSER */
-static void
-icon_filesel_choose_cb(GtkWidget *w, AccountPrefsDialog *dialog)
-{
-	char *filename, *current_folder;
-
-	filename = g_strdup(gtk_file_selection_get_filename(
-				GTK_FILE_SELECTION(dialog->icon_filesel)));
-
-	/* If they typed in a directory, change there */
-	if (gaim_gtk_check_if_dir(filename,
-				GTK_FILE_SELECTION(dialog->icon_filesel)))
-	{
-		g_free(filename);
-		return;
-	}
-
-	current_folder = g_path_get_dirname(filename);
-	if (current_folder != NULL) {
-		gaim_prefs_set_string("/gaim/gtk/filelocations/last_icon_folder", current_folder);
-		g_free(current_folder);
-	}
-
-#endif /* FILECHOOSER */
-
-	g_free(dialog->icon_path);
-	dialog->icon_path = convert_buddy_icon(dialog->plugin, filename);
-	set_dialog_icon(dialog);
-	gtk_widget_show(dialog->icon_entry);
-
-	gtk_widget_destroy(dialog->icon_filesel);
-	dialog->icon_filesel = NULL;
-	g_free(filename);
- }
-
-static void
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-icon_preview_change_cb(GtkFileChooser *widget, AccountPrefsDialog *dialog)
-#else /* FILECHOOSER */
-icon_preview_change_cb(GtkTreeSelection *sel, AccountPrefsDialog *dialog)
-#endif /* FILECHOOSER */
+icon_filesel_choose_cb(const char *filename, AccountPrefsDialog *dialog)
 {
-	GdkPixbuf *pixbuf, *scale;
-	int height, width;
-	char *basename, *markup, *size;
-	struct stat st;
-	char *filename;
-
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-	filename = gtk_file_chooser_get_preview_filename(
-					GTK_FILE_CHOOSER(dialog->icon_filesel));
-#else /* FILECHOOSER */
-	filename = g_strdup(gtk_file_selection_get_filename(
-		GTK_FILE_SELECTION(dialog->icon_filesel)));
-#endif /* FILECHOOSER */
-
-	if (!filename || g_stat(filename, &st))
-	{
-		g_free(filename);
-		return;
-	}
-
-	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
-	if (!pixbuf) {
-		gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), NULL);
-		gtk_label_set_markup(GTK_LABEL(dialog->icon_text), "");
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-		gtk_file_chooser_set_preview_widget_active(
-					GTK_FILE_CHOOSER(dialog->icon_filesel), FALSE);
-#endif /* FILECHOOSER */
-		g_free(filename);
-		return;
+	if (filename) {
+		g_free(dialog->icon_path);
+		dialog->icon_path = gaim_gtk_convert_buddy_icon(dialog->plugin, filename);
+		set_dialog_icon(dialog);
+		gtk_widget_show(dialog->icon_entry);
 	}
-
-	width = gdk_pixbuf_get_width(pixbuf);
-	height = gdk_pixbuf_get_height(pixbuf);
-	basename = g_path_get_basename(filename);
-	size = gaim_str_size_to_units(st.st_size);
-	markup = g_strdup_printf(_("<b>File:</b> %s\n"
-							   "<b>File size:</b> %s\n"
-							   "<b>Image size:</b> %dx%d"),
-							 basename, size, width, height);
-
-	scale = gdk_pixbuf_scale_simple(pixbuf, width * 50 / height,
-									50, GDK_INTERP_BILINEAR);
-	gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), scale);
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-	gtk_file_chooser_set_preview_widget_active(
-					GTK_FILE_CHOOSER(dialog->icon_filesel), TRUE);
-#endif /* FILECHOOSER */
-	gtk_label_set_markup(GTK_LABEL(dialog->icon_text), markup);
-
-	g_object_unref(G_OBJECT(pixbuf));
-	g_object_unref(G_OBJECT(scale));
-	g_free(filename);
-	g_free(basename);
-	g_free(size);
-	g_free(markup);
-}
-
-#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-static void
-icon_filesel_delete_cb(GtkWidget *w, AccountPrefsDialog *dialog)
-{
-	if (dialog->icon_filesel != NULL)
-		gtk_widget_destroy(dialog->icon_filesel);
-
+	
 	dialog->icon_filesel = NULL;
 }
-#endif /* FILECHOOSER */
 
 static void
 icon_select_cb(GtkWidget *button, AccountPrefsDialog *dialog)
 {
-#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-	GtkWidget *hbox;
-	GtkWidget *tv;
-	GtkTreeSelection *sel;
-#endif /* FILECHOOSER */
-	const char *current_folder;
-
-	if (dialog->icon_filesel != NULL) {
-		gtk_window_present(GTK_WINDOW(dialog->icon_filesel));
-		return;
-	}
-
-	current_folder = gaim_prefs_get_string("/gaim/gtk/filelocations/last_icon_folder");
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-	dialog->icon_filesel = gtk_file_chooser_dialog_new(_("Buddy Icon"),
-									GTK_WINDOW(dialog->window),
-									GTK_FILE_CHOOSER_ACTION_OPEN,
-									GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-									GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-									NULL);
-	gtk_dialog_set_default_response(GTK_DIALOG(dialog->icon_filesel), GTK_RESPONSE_ACCEPT);
-	if ((current_folder != NULL) && (*current_folder != '\0'))
-		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel),
-											current_folder);
-
-	dialog->icon_preview = gtk_image_new();
-	dialog->icon_text = gtk_label_new(NULL);
-	gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview), -1, 50);
-	gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog->icon_filesel),
-					    GTK_WIDGET(dialog->icon_preview));
-	g_signal_connect(G_OBJECT(dialog->icon_filesel), "update-preview",
-					 G_CALLBACK(icon_preview_change_cb), dialog);
-	g_signal_connect(G_OBJECT(dialog->icon_filesel), "response",
-					 G_CALLBACK(icon_filesel_choose_cb), dialog);
-	icon_preview_change_cb(NULL, dialog);
-#else /* FILECHOOSER */
-	dialog->icon_filesel = gtk_file_selection_new(_("Buddy Icon"));
-	dialog->icon_preview = gtk_image_new();
-	dialog->icon_text = gtk_label_new(NULL);
-	if ((current_folder != NULL) && (*current_folder != '\0'))
-		gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog->icon_filesel),
-										current_folder);
-
-	gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview), -1, 50);
-	hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE);
-	gtk_box_pack_start(
-		GTK_BOX(GTK_FILE_SELECTION(dialog->icon_filesel)->main_vbox),
-		hbox, FALSE, FALSE, 0);
-	gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_preview,
-					 FALSE, FALSE, 0);
-	gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_text, FALSE, FALSE, 0);
-
-	tv = GTK_FILE_SELECTION(dialog->icon_filesel)->file_list;
-	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
-
-	g_signal_connect(G_OBJECT(sel), "changed",
-					 G_CALLBACK(icon_preview_change_cb), dialog);
-	g_signal_connect(
-		G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->ok_button),
-		"clicked",
-		G_CALLBACK(icon_filesel_choose_cb), dialog);
-	g_signal_connect(
-		G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->cancel_button),
-		"clicked",
-		G_CALLBACK(icon_filesel_delete_cb), dialog);
-	g_signal_connect(G_OBJECT(dialog->icon_filesel), "destroy",
-					 G_CALLBACK(icon_filesel_delete_cb), dialog);
-#endif /* FILECHOOSER */
-
-	gtk_widget_show_all(GTK_WIDGET(dialog->icon_filesel));
+	dialog->icon_filesel = gaim_gtk_buddy_icon_chooser_new(dialog->window, icon_filesel_choose_cb, dialog);
+	gtk_widget_show_all(dialog->icon_filesel);
 }
 
 static void
@@ -510,7 +323,8 @@
 			if ((rtmp = strchr(tmp, '\r')) || (rtmp = strchr(tmp, '\n')))
 				*rtmp = '\0';
 			g_free(dialog->icon_path);
-			dialog->icon_path = convert_buddy_icon(dialog->plugin, tmp);
+
+			dialog->icon_path = gaim_gtk_convert_buddy_icon(dialog->plugin, tmp);
 			set_dialog_icon(dialog);
 			gtk_widget_show(dialog->icon_entry);
 			g_free(tmp);
@@ -520,207 +334,6 @@
 	gtk_drag_finish(dc, FALSE, FALSE, t);
 }
 
-
-#if GTK_CHECK_VERSION(2,2,0)
-static gboolean
-str_array_match(char **a, char **b)
-{
-	int i, j;
-
-	if (!a || !b)
-		return FALSE;
-	for (i = 0; a[i] != NULL; i++)
-		for (j = 0; b[j] != NULL; j++)
-			if (!g_ascii_strcasecmp(a[i], b[j]))
-				return TRUE;
-	return FALSE;
-}
-#endif
-
-static char*
-convert_buddy_icon(GaimPlugin *plugin, const char *path)
-{
-#if GTK_CHECK_VERSION(2,2,0)
-	int width, height;
-	char **pixbuf_formats = NULL;
-	GdkPixbufFormat *format;
-	GdkPixbuf *pixbuf;
-	GaimPluginProtocolInfo *prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
-	char **prpl_formats =  g_strsplit (prpl_info->icon_spec.format,",",0);
-#if !GTK_CHECK_VERSION(2,4,0)
-	GdkPixbufLoader *loader;
-	FILE *file;
-	struct stat st;
-	void *data = NULL;
-#endif
-#endif
-	const char *dirname = gaim_buddy_icons_get_cache_dir();
-	char *random   = g_strdup_printf("%x", g_random_int());
-	char *filename = g_build_filename(dirname, random, NULL);
-
-	if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
-		gaim_debug_info("buddyicon", "Creating icon cache directory.\n");
-
-		if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
-			gaim_debug_error("buddyicon",
-							 "Unable to create directory %s: %s\n",
-							 dirname, strerror(errno));
-#if GTK_CHECK_VERSION(2,2,0)
-			g_strfreev(prpl_formats);
-#endif
-			g_free(random);
-			g_free(filename);
-			return NULL;
-		}
-	}
-
-#if GTK_CHECK_VERSION(2,2,0)
-#if GTK_CHECK_VERSION(2,4,0)
-	format = gdk_pixbuf_get_file_info (path, &width, &height);
-#else
-	loader = gdk_pixbuf_loader_new();
-	if (!g_stat(path, &st) && (file = g_fopen(path, "rb")) != NULL) {
-		data = g_malloc(st.st_size);
-		fread(data, 1, st.st_size, file);
-		fclose(file);
-		gdk_pixbuf_loader_write(loader, data, st.st_size, NULL);
-		g_free(data);
-	}
-	gdk_pixbuf_loader_close(loader, NULL);
-	pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
-	width = gdk_pixbuf_get_width(pixbuf);
-	height = gdk_pixbuf_get_height(pixbuf);
-	format = gdk_pixbuf_loader_get_format(loader);
-	g_object_unref(G_OBJECT(loader));
-#endif
-	pixbuf_formats =  gdk_pixbuf_format_get_extensions(format);
-
-	if (str_array_match(pixbuf_formats, prpl_formats) &&                  /* This is an acceptable format AND */
-		 (!(prpl_info->icon_spec.scale_rules & GAIM_ICON_SCALE_SEND) ||   /* The prpl doesn't scale before it sends OR */
-		  (prpl_info->icon_spec.min_width <= width &&
-		   prpl_info->icon_spec.max_width >= width &&
-		   prpl_info->icon_spec.min_height <= height &&
-		   prpl_info->icon_spec.max_height >= height)))                   /* The icon is the correct size */
-#endif
-	{
-		gchar *contents;
-		gsize length;
-		FILE *image;
-
-#if GTK_CHECK_VERSION(2,2,0)
-		g_strfreev(prpl_formats);
-		g_strfreev(pixbuf_formats);
-#endif
-
-		/* Copy the image to the cache folder as "filename". */
-
-		if (!g_file_get_contents(path, &contents, &length, NULL) ||
-		    (image = g_fopen(filename, "wb")) == NULL)
-		{
-			g_free(random);
-			g_free(filename);
-#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
-			g_object_unref(G_OBJECT(pixbuf));
-#endif
-			return NULL;
-		}
-
-		if (fwrite(contents, 1, length, image) != length)
-		{
-			fclose(image);
-			g_unlink(filename);
-
-			g_free(random);
-			g_free(filename);
-#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
-			g_object_unref(G_OBJECT(pixbuf));
-#endif
-			return NULL;
-		}
-		fclose(image);
-
-#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
-		g_object_unref(G_OBJECT(pixbuf));
-#endif
-
-		g_free(filename);
-		return random;
-	}
-#if GTK_CHECK_VERSION(2,2,0)
-	else
-	{
-		int i;
-		GError *error = NULL;
-		GdkPixbuf *scale;
-		pixbuf = gdk_pixbuf_new_from_file(path, &error);
-		g_strfreev(pixbuf_formats);
-		if (!error && (prpl_info->icon_spec.scale_rules & GAIM_ICON_SCALE_SEND) &&
-			(width < prpl_info->icon_spec.min_width ||
-			 width > prpl_info->icon_spec.max_width ||
-			 height < prpl_info->icon_spec.min_height ||
-			 height > prpl_info->icon_spec.max_height))
-		{
-			int new_width = width;
-			int new_height = height;
-
-			if(new_width > prpl_info->icon_spec.max_width)
-				new_width = prpl_info->icon_spec.max_width;
-			else if(new_width < prpl_info->icon_spec.min_width)
-				new_width = prpl_info->icon_spec.min_width;
-			if(new_height > prpl_info->icon_spec.max_height)
-				new_height = prpl_info->icon_spec.max_height;
-			else if(new_height < prpl_info->icon_spec.min_height)
-				new_height = prpl_info->icon_spec.min_height;
-
-			/* preserve aspect ratio */
-			if ((double)height * (double)new_width >
-				(double)width * (double)new_height) {
-					new_width = 0.5 + (double)width * (double)new_height / (double)height;
-			} else {
-					new_height = 0.5 + (double)height * (double)new_width / (double)width;
-			}
-
-			scale = gdk_pixbuf_scale_simple (pixbuf, new_width, new_height,
-					GDK_INTERP_HYPER);
-			g_object_unref(G_OBJECT(pixbuf));
-			pixbuf = scale;
-		}
-		if (error) {
-			g_free(random);
-			g_free(filename);
-			gaim_debug_error("buddyicon", "Could not open icon for conversion: %s\n", error->message);
-			g_error_free(error);
-			g_strfreev(prpl_formats);
-			return NULL;
-		}
-
-		for (i = 0; prpl_formats[i]; i++) {
-			gaim_debug_info("buddyicon", "Converting buddy icon to %s as %s\n", prpl_formats[i], filename);
-			/* The gdk-pixbuf documentation is wrong. gdk_pixbuf_save returns TRUE if it was successful,
-			 * FALSE if an error was set. */
-			if (gdk_pixbuf_save (pixbuf, filename, prpl_formats[i], &error, NULL) == TRUE)
-					break;
-			gaim_debug_warning("buddyicon", "Could not convert to %s: %s\n", prpl_formats[i], error->message);
-			g_error_free(error);
-			error = NULL;
-		}
-		g_strfreev(prpl_formats);
-		if (!error) {
-			g_object_unref(G_OBJECT(pixbuf));
-			g_free(filename);
-			return random;
-		} else {
-			gaim_debug_error("buddyicon", "Could not convert icon to usable format: %s\n", error->message);
-			g_error_free(error);
-		}
-		g_free(random);
-		g_free(filename);
-		g_object_unref(G_OBJECT(pixbuf));
-	}
-	return NULL;
-#endif
-}
-
 static void
 update_editable(GaimConnection *gc, AccountPrefsDialog *dialog)
 {
@@ -898,6 +511,12 @@
 }
 
 static void
+icon_check_cb(GtkWidget *checkbox, AccountPrefsDialog *dialog)
+{
+	gtk_widget_set_sensitive(dialog->icon_hbox, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)));
+}
+
+static void
 add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent)
 {
 	GtkWidget *frame;
@@ -930,12 +549,17 @@
 	gtk_widget_show(dialog->new_mail_check);
 
 	/* Buddy icon */
+	dialog->icon_check = gtk_check_button_new_with_label(_("Use this buddy icon for this account:"));
+	g_signal_connect(G_OBJECT(dialog->icon_check), "toggled", G_CALLBACK(icon_check_cb), dialog);
+	gtk_widget_show(dialog->icon_check);
+	gtk_box_pack_start(GTK_BOX(vbox), dialog->icon_check, FALSE, FALSE, 0);
+
 	dialog->icon_hbox = hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE);
+	gtk_widget_set_sensitive(hbox, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)));
 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
 	gtk_widget_show(hbox);
 
-	label = gtk_label_new(_("Buddy icon:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	label = gtk_label_new("    ");
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 	gtk_widget_show(label);
 
@@ -976,12 +600,14 @@
 
 	if (dialog->account != NULL) {
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->new_mail_check),
-				gaim_account_get_check_mail(dialog->account));
-
-		if (gaim_account_get_buddy_icon(dialog->account) != NULL) {
-			dialog->icon_path = g_strdup(gaim_account_get_buddy_icon(dialog->account));
-			set_dialog_icon(dialog);
-		}
+					     gaim_account_get_check_mail(dialog->account));
+		
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->icon_check),
+					     !gaim_account_get_ui_bool(dialog->account, GAIM_GTK_UI, "use-global-buddyicon",
+								       TRUE));
+
+		dialog->icon_path = g_strdup(gaim_account_get_ui_string(dialog->account, GAIM_GTK_UI, "non-global-buddyicon", NULL));
+		set_dialog_icon(dialog);
 	}
 
 	if (!dialog->prpl_info ||
@@ -1425,12 +1051,13 @@
 
 	if (dialog->icon_path != NULL)
 	{
-		const char *icon = gaim_account_get_buddy_icon(dialog->account);
+		const char *icon = gaim_account_get_ui_string(dialog->account, GAIM_GTK_UI, "non-global-buddyicon", NULL);
 		if (dialog->icon_path != NULL && (icon == NULL || strcmp(dialog->icon_path, icon)))
 		{
 			/* The user set an icon, which would've been cached by convert_buddy_icon,
 			 * but didn't save the changes. Delete the cache file. */
 			char *filename = g_build_filename(gaim_buddy_icons_get_cache_dir(), dialog->icon_path, NULL);
+			printf("Deleting\n");
 			g_unlink(filename);
 			g_free(filename);
 		}
@@ -1460,7 +1087,7 @@
 	const char *value;
 	char *username;
 	char *tmp;
-	gboolean new = FALSE;
+	gboolean new = FALSE, icon_change = FALSE;
 	GaimAccount *account;
 
 	if (dialog->account == NULL)
@@ -1488,7 +1115,20 @@
 		gaim_account_set_alias(account, NULL);
 
 	/* Buddy Icon */
-	gaim_account_set_buddy_icon(account, dialog->icon_path);
+	if (gaim_account_get_ui_bool(account, GAIM_GTK_UI, "use-global-buddyicon", TRUE) == 
+	    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check))) {
+		icon_change = TRUE;
+	}
+	gaim_account_set_ui_bool(account, GAIM_GTK_UI, "use-global-buddyicon", !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check)));
+	gaim_account_set_ui_string(account, GAIM_GTK_UI, "non-global-buddyicon", dialog->icon_path);
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check))) {
+		gaim_account_set_buddy_icon(account, dialog->icon_path);
+	} else if (gaim_prefs_get_string("/gaim/gtk/accounts/buddyicon") && icon_change) {
+		char *icon = gaim_gtk_convert_buddy_icon(dialog->plugin, gaim_prefs_get_string("/gaim/gtk/accounts/buddyicon"));
+		gaim_account_set_buddy_icon(account, icon);
+		g_free(icon);
+	}
+		
 
 	/* Remember Password */
 	gaim_account_set_remember_password(account,
@@ -2672,6 +2312,7 @@
 	gaim_prefs_add_none("/gaim/gtk/accounts/dialog");
 	gaim_prefs_add_int("/gaim/gtk/accounts/dialog/width",  520);
 	gaim_prefs_add_int("/gaim/gtk/accounts/dialog/height", 321);
+	gaim_prefs_add_string("/gaim/gtk/accounts/buddyicon", NULL);
 
 	gaim_signal_register(gaim_gtk_account_get_handle(), "account-modified",
 						 gaim_marshal_VOID__POINTER, NULL, 1,
--- a/src/gtkblist.c	Tue Aug 15 20:23:58 2006 +0000
+++ b/src/gtkblist.c	Tue Aug 15 23:25:29 2006 +0000
@@ -3814,12 +3814,11 @@
 
 	/* Add the statusbox */
 	gtkblist->statusbox = gtk_gaim_status_box_new();
+	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->statusbox, FALSE, TRUE, 0);
 	gtk_widget_set_name(gtkblist->statusbox, "gaim_gtkblist_statusbox");
-
+	gtk_gaim_status_box_set_buddy_icon(gtkblist->statusbox, gaim_prefs_get_string("/gaim/gtk/accounts/buddyicon"));
 	gtk_widget_show(gtkblist->statusbox);
-	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->statusbox, FALSE, TRUE, 0);
-
-
+	
 	/* set the Show Offline Buddies option. must be done
 	 * after the treeview or faceprint gets mad. -Robot101
 	 */
--- a/src/gtkstatusbox.c	Tue Aug 15 20:23:58 2006 +0000
+++ b/src/gtkstatusbox.c	Tue Aug 15 23:25:29 2006 +0000
@@ -77,6 +77,8 @@
 static gboolean gtk_gaim_status_box_expose_event (GtkWidget *widget, GdkEventExpose *event);
 static void gtk_gaim_status_box_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data);
 
+static void do_colorshift (GdkPixbuf *dest, GdkPixbuf *src, int shift);
+
 static void (*combo_box_size_request)(GtkWidget *widget, GtkRequisition *requisition);
 static void (*combo_box_size_allocate)(GtkWidget *widget, GtkAllocation *allocation);
 static void (*combo_box_forall) (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data);
@@ -260,6 +262,17 @@
 	gaim_signals_disconnect_by_handle(statusbox);
 	gaim_prefs_disconnect_by_handle(statusbox);
 
+	gdk_cursor_unref(statusbox->hand_cursor);
+	gdk_cursor_unref(statusbox->arrow_cursor);
+
+	g_object_unref(G_OBJECT(statusbox->buddy_icon));
+	g_object_unref(G_OBJECT(statusbox->buddy_icon_hover));
+	
+	if (statusbox->buddy_icon_sel)
+		gtk_widget_destroy(statusbox->buddy_icon_sel);
+	
+	g_free(statusbox->buddy_icon_path);
+
 	G_OBJECT_CLASS(parent_class)->finalize(obj);
 }
 
@@ -907,6 +920,55 @@
 }
 
 static void
+icon_choose_cb(const char *filename, GtkGaimStatusBox *box)
+{
+	if (filename) {
+		GList *accounts;
+		for (accounts = gaim_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
+			GaimAccount *account = accounts->data;
+			if (gaim_account_get_ui_bool(account, GAIM_GTK_UI, "use-global-buddy-icon", TRUE)) {
+				char *icon = gaim_gtk_convert_buddy_icon(gaim_find_prpl(gaim_account_get_protocol_id(account)),
+									 filename);
+				gaim_account_set_buddy_icon(account, icon);
+				g_free(icon);
+			}
+		}
+		gtk_gaim_status_box_set_buddy_icon(box, filename);
+	}
+	
+	box->buddy_icon_sel = NULL;
+}
+
+static gboolean
+icon_box_press_cb(GtkWidget *widget, GdkEventButton *event, GtkGaimStatusBox *box)
+{
+	if (box->buddy_icon_sel) {
+		gtk_window_present(GTK_WINDOW(box->buddy_icon_sel));
+		return FALSE;
+	}
+
+	GtkWidget *filesel = gaim_gtk_buddy_icon_chooser_new(NULL, icon_choose_cb, box);
+	gtk_widget_show_all(filesel);
+	return FALSE;
+}
+
+static gboolean
+icon_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, GtkGaimStatusBox *box)
+{
+	gdk_window_set_cursor(widget->window, box->hand_cursor);
+	gtk_image_set_from_pixbuf(box->icon, box->buddy_icon_hover);
+	return FALSE;
+}
+
+static gboolean
+icon_box_leave_cb(GtkWidget *widget, GdkEventCrossing *event, GtkGaimStatusBox *box)
+{
+	gdk_window_set_cursor(widget->window, box->arrow_cursor);
+	gtk_image_set_from_pixbuf(box->icon, box->buddy_icon) ;
+	return FALSE;
+}
+
+static void
 gtk_gaim_status_box_init (GtkGaimStatusBox *status_box)
 {
 	GtkCellRenderer *text_rend;
@@ -922,6 +984,18 @@
 	status_box->vsep = gtk_vseparator_new();
 	status_box->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
 
+	status_box->buddy_icon = gdk_pixbuf_new_from_file("/home/seanegan/p1120233.jpg", NULL);
+	status_box->icon = gtk_image_new_from_pixbuf(status_box->buddy_icon);
+	status_box->icon_box = gtk_event_box_new();
+	status_box->hand_cursor = gdk_cursor_new (GDK_HAND2);
+	status_box->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
+
+	g_signal_connect(G_OBJECT(status_box->icon_box), "enter-notify-event", G_CALLBACK(icon_box_enter_cb), status_box);
+	g_signal_connect(G_OBJECT(status_box->icon_box), "leave-notify-event", G_CALLBACK(icon_box_leave_cb), status_box);
+	g_signal_connect(G_OBJECT(status_box->icon_box), "button-press-event", G_CALLBACK(icon_box_press_cb), status_box);
+
+	gtk_container_add(GTK_CONTAINER(status_box->icon_box), status_box->icon);
+
 	status_box->store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
 	status_box->dropdown_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
 	gtk_combo_box_set_model(GTK_COMBO_BOX(status_box), GTK_TREE_MODEL(status_box->dropdown_store));
@@ -934,6 +1008,7 @@
 	gtk_box_pack_start(GTK_BOX(status_box->hbox), status_box->vsep, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(status_box->hbox), status_box->arrow, FALSE, FALSE, 0);
 	gtk_widget_show_all(status_box->toggle_button);
+	gtk_widget_show_all(status_box->icon_box);
 #if GTK_CHECK_VERSION(2,4,0)
 	gtk_button_set_focus_on_click(GTK_BUTTON(status_box->toggle_button), FALSE);
 #endif
@@ -984,6 +1059,7 @@
 #endif
 	gtk_widget_set_parent(status_box->vbox, GTK_WIDGET(status_box));
 	gtk_widget_set_parent(status_box->toggle_button, GTK_WIDGET(status_box));
+	gtk_widget_set_parent(status_box->icon_box, GTK_WIDGET(status_box));
 	GTK_BIN(status_box)->child = status_box->toggle_button;
 
 	gtk_box_pack_start(GTK_BOX(status_box->vbox), status_box->sw, TRUE, TRUE, 0);
@@ -1024,14 +1100,58 @@
 		requisition->height += box_req.height + 3;
 
 	requisition->width = 1;
+
+
+}
+
+/* From gnome-panel */
+static void
+do_colorshift (GdkPixbuf *dest, GdkPixbuf *src, int shift)
+{
+	gint i, j;
+	gint width, height, has_alpha, srcrowstride, destrowstride;
+	guchar *target_pixels;
+	guchar *original_pixels;
+	guchar *pixsrc;
+	guchar *pixdest;
+	int val;
+	guchar r,g,b;
+
+	has_alpha = gdk_pixbuf_get_has_alpha (src);
+	width = gdk_pixbuf_get_width (src);
+	height = gdk_pixbuf_get_height (src);
+	srcrowstride = gdk_pixbuf_get_rowstride (src);
+	destrowstride = gdk_pixbuf_get_rowstride (dest);
+	target_pixels = gdk_pixbuf_get_pixels (dest);
+	original_pixels = gdk_pixbuf_get_pixels (src);
+
+	for (i = 0; i < height; i++) {
+		pixdest = target_pixels + i*destrowstride;
+		pixsrc = original_pixels + i*srcrowstride;
+		for (j = 0; j < width; j++) {
+			r = *(pixsrc++);
+			g = *(pixsrc++);
+			b = *(pixsrc++);
+			val = r + shift;
+			*(pixdest++) = CLAMP(val, 0, 255);
+			val = g + shift;
+			*(pixdest++) = CLAMP(val, 0, 255);
+			val = b + shift;
+			*(pixdest++) = CLAMP(val, 0, 255);
+			if (has_alpha)
+				*(pixdest++) = *(pixsrc++);
+		}
+	}
 }
 
 static void
 gtk_gaim_status_box_size_allocate(GtkWidget *widget,
-								  GtkAllocation *allocation)
+				  GtkAllocation *allocation)
 {
+	GtkGaimStatusBox *status_box = GTK_GAIM_STATUS_BOX(widget);
 	GtkRequisition req = {0,0};
-	GtkAllocation parent_alc, box_alc;
+	GtkAllocation parent_alc, box_alc, icon_alc;
+	GdkPixbuf *scaled;
 
 	combo_box_size_request(widget, &req);
 
@@ -1043,7 +1163,28 @@
 	parent_alc = *allocation;
 	parent_alc.height = MAX(1,req.height);
 	parent_alc.y += 3;
+	parent_alc.width -= (parent_alc.height + 3);
 	combo_box_size_allocate(widget, &parent_alc);
+
+	icon_alc = *allocation;
+	icon_alc.height = MAX(1,req.height);
+	icon_alc.width = icon_alc.height;
+	icon_alc.x = allocation->width - icon_alc.width;
+	icon_alc.y += 3;
+	
+	if (status_box->icon_size != icon_alc.height) {
+		scaled = gdk_pixbuf_new_from_file_at_scale(status_box->buddy_icon_path, 
+							   icon_alc.height, icon_alc.width, FALSE, NULL);
+		status_box->buddy_icon_hover = gdk_pixbuf_copy(scaled);
+		do_colorshift(status_box->buddy_icon_hover, status_box->buddy_icon_hover, 30);
+		g_object_unref(status_box->buddy_icon);
+		status_box->buddy_icon = scaled;
+		gtk_image_set_from_pixbuf(status_box->icon, status_box->buddy_icon);
+		status_box->icon_size = icon_alc.height;
+	}
+	gtk_widget_size_allocate((GTK_GAIM_STATUS_BOX(widget))->icon_box, &icon_alc);
+
+
 	gtk_widget_size_allocate((GTK_GAIM_STATUS_BOX(widget))->toggle_button, &parent_alc);
 	widget->allocation = *allocation;
 }
@@ -1055,6 +1196,7 @@
 	GtkGaimStatusBox *status_box = GTK_GAIM_STATUS_BOX(widget);
 	gtk_container_propagate_expose(GTK_CONTAINER(widget), status_box->vbox, event);
 	gtk_container_propagate_expose(GTK_CONTAINER(widget), status_box->toggle_button, event);
+	gtk_container_propagate_expose(GTK_CONTAINER(widget), status_box->icon_box, event);
 	return FALSE;
 }
 
@@ -1071,6 +1213,7 @@
 		(* callback) (status_box->vbox, callback_data);
 		(* callback) (status_box->toggle_button, callback_data);
 		(* callback) (status_box->arrow, callback_data);
+		(* callback) (status_box->icon_box, callback_data);
 	}
 
 	combo_box_forall(container, include_internals, callback, callback_data);
@@ -1179,6 +1322,30 @@
 }
 
 void
+gtk_gaim_status_box_set_buddy_icon(GtkGaimStatusBox *box, const char *filename)
+{
+	GdkPixbuf *scaled;
+	g_free(box->buddy_icon_path);
+	box->buddy_icon_path = g_strdup(filename);
+
+	scaled = gdk_pixbuf_new_from_file_at_scale(filename, 
+						   box->icon_size, box->icon_size, FALSE, NULL);
+	box->buddy_icon_hover = gdk_pixbuf_copy(scaled);
+	do_colorshift(box->buddy_icon_hover, box->buddy_icon_hover, 30);
+	g_object_unref(box->buddy_icon);
+	box->buddy_icon = scaled;
+	gtk_image_set_from_pixbuf(box->icon, box->buddy_icon);
+	
+	gaim_prefs_set_string("/gaim/gtk/accounts/buddyicon", filename);
+}
+
+const char*
+gtk_gaim_status_box_get_buddy_icon(GtkGaimStatusBox *box)
+{
+	return box->buddy_icon_path;
+}
+
+void
 gtk_gaim_status_box_pulse_connecting(GtkGaimStatusBox *status_box)
 {
 	if (!status_box)
--- a/src/gtkstatusbox.h	Tue Aug 15 20:23:58 2006 +0000
+++ b/src/gtkstatusbox.h	Tue Aug 15 23:25:29 2006 +0000
@@ -83,6 +83,17 @@
 
 	GtkWidget *vbox, *sw;
 	GtkWidget *imhtml;
+
+	char      *buddy_icon_path;
+        GdkPixbuf *buddy_icon;
+        GdkPixbuf *buddy_icon_hover;
+	GtkWidget *buddy_icon_sel;
+        GtkWidget *icon;
+	GtkWidget *icon_box;
+	GdkCursor *hand_cursor;
+	GdkCursor *arrow_cursor;
+        int icon_size;
+
 	gboolean imhtml_visible;
 
 	GtkWidget *cell_view;
@@ -146,6 +157,12 @@
 void
 gtk_gaim_status_box_pulse_connecting(GtkGaimStatusBox *status_box);
 
+void
+gtk_gaim_status_box_set_buddy_icon(GtkGaimStatusBox *status_box, const char *filename);
+
+const char *
+gtk_gaim_status_box_get_buddy_icon(GtkGaimStatusBox *status_box);
+
 char *gtk_gaim_status_box_get_message(GtkGaimStatusBox *status_box);
 
 G_END_DECLS
--- a/src/gtkutils.c	Tue Aug 15 20:23:58 2006 +0000
+++ b/src/gtkutils.c	Tue Aug 15 23:25:29 2006 +0000
@@ -2211,3 +2211,429 @@
 	gdk_window_set_cursor(widget->window, NULL);
 }
 
+struct _icon_chooser {
+	GtkWidget *icon_filesel;
+	GtkWidget *icon_preview;
+	GtkWidget *icon_text;
+	
+	void (*callback)(const char*,gpointer);
+	gpointer data;
+};
+
+#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+static void
+icon_filesel_delete_cb(GtkWidget *w, struct _icon_chooser *dialog)
+{
+	if (dialog->icon_filesel != NULL)
+		gtk_widget_destroy(dialog->icon_filesel);
+
+	if (dialog->callback)
+		dialog->callback(NULL, data);
+
+	g_free(dialog);
+}
+#endif /* FILECHOOSER */
+
+
+
+#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+static void
+icon_filesel_choose_cb(GtkWidget *widget, gint response, struct _icon_chooser *dialog)
+{
+	char *filename, *current_folder;
+
+	if (response != GTK_RESPONSE_ACCEPT) {
+		if (response == GTK_RESPONSE_CANCEL) {
+			gtk_widget_destroy(dialog->icon_filesel);
+		}
+		dialog->icon_filesel = NULL;
+		if (dialog->callback)
+			dialog->callback(NULL, dialog->data);
+		g_free(dialog);
+		return;
+	}
+
+	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog->icon_filesel));
+	current_folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel));
+	if (current_folder != NULL) {
+		gaim_prefs_set_string("/gaim/gtk/filelocations/last_icon_folder", current_folder);
+		g_free(current_folder);
+	}
+
+#else /* FILECHOOSER */
+static void
+icon_filesel_choose_cb(GtkWidget *w, AccountPrefsDialog *dialog)
+{
+	char *filename, *current_folder;
+
+	filename = g_strdup(gtk_file_selection_get_filename(
+				GTK_FILE_SELECTION(dialog->icon_filesel)));
+
+	/* If they typed in a directory, change there */
+	if (gaim_gtk_check_if_dir(filename,
+				GTK_FILE_SELECTION(dialog->icon_filesel)))
+	{
+		g_free(filename);
+		return;
+	}
+
+	current_folder = g_path_get_dirname(filename);
+	if (current_folder != NULL) {
+		gaim_prefs_set_string("/gaim/gtk/filelocations/last_icon_folder", current_folder);
+		g_free(current_folder);
+	}
+
+#endif /* FILECHOOSER */
+	if (dialog->callback)
+		dialog->callback(filename, dialog->data);
+	gtk_widget_destroy(dialog->icon_filesel);
+	g_free(filename);
+	g_free(dialog);
+ }
+
+
+static void
+#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+icon_preview_change_cb(GtkFileChooser *widget, struct _icon_chooser *dialog)
+#else /* FILECHOOSER */
+icon_preview_change_cb(GtkTreeSelection *sel, struct _icon_chooser *dialog)
+#endif /* FILECHOOSER */
+{
+	GdkPixbuf *pixbuf, *scale;
+	int height, width;
+	char *basename, *markup, *size;
+	struct stat st;
+	char *filename;
+
+#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+	filename = gtk_file_chooser_get_preview_filename(
+					GTK_FILE_CHOOSER(dialog->icon_filesel));
+#else /* FILECHOOSER */
+	filename = g_strdup(gtk_file_selection_get_filename(
+		GTK_FILE_SELECTION(dialog->icon_filesel)));
+#endif /* FILECHOOSER */
+
+	if (!filename || g_stat(filename, &st))
+	{
+		g_free(filename);
+		return;
+	}
+
+	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+	if (!pixbuf) {
+		gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), NULL);
+		gtk_label_set_markup(GTK_LABEL(dialog->icon_text), "");
+#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+		gtk_file_chooser_set_preview_widget_active(
+					GTK_FILE_CHOOSER(dialog->icon_filesel), FALSE);
+#endif /* FILECHOOSER */
+		g_free(filename);
+		return;
+	}
+
+	width = gdk_pixbuf_get_width(pixbuf);
+	height = gdk_pixbuf_get_height(pixbuf);
+	basename = g_path_get_basename(filename);
+	size = gaim_str_size_to_units(st.st_size);
+	markup = g_strdup_printf(_("<b>File:</b> %s\n"
+							   "<b>File size:</b> %s\n"
+							   "<b>Image size:</b> %dx%d"),
+							 basename, size, width, height);
+
+	scale = gdk_pixbuf_scale_simple(pixbuf, width * 50 / height,
+									50, GDK_INTERP_BILINEAR);
+	gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), scale);
+#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+	gtk_file_chooser_set_preview_widget_active(
+					GTK_FILE_CHOOSER(dialog->icon_filesel), TRUE);
+#endif /* FILECHOOSER */
+	gtk_label_set_markup(GTK_LABEL(dialog->icon_text), markup);
+
+	g_object_unref(G_OBJECT(pixbuf));
+	g_object_unref(G_OBJECT(scale));
+	g_free(filename);
+	g_free(basename);
+	g_free(size);
+	g_free(markup);
+}
+
+
+GtkWidget *gaim_gtk_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char*,gpointer), gpointer data) {
+	struct _icon_chooser *dialog = g_new0(struct _icon_chooser, 1);
+
+#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+	GtkWidget *hbox;
+	GtkWidget *tv;
+	GtkTreeSelection *sel;
+	
+#endif /* FILECHOOSER */
+	const char *current_folder;
+
+	dialog->callback = callback;
+	dialog->data = data;
+
+	if (dialog->icon_filesel != NULL) {
+		gtk_window_present(GTK_WINDOW(dialog->icon_filesel));
+		return NULL;
+	}
+
+	current_folder = gaim_prefs_get_string("/gaim/gtk/filelocations/last_icon_folder");
+#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+
+	dialog->icon_filesel = gtk_file_chooser_dialog_new(_("Buddy Icon"),
+							   parent,
+							   GTK_FILE_CHOOSER_ACTION_OPEN,
+							   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+							   GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+							   NULL);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog->icon_filesel), GTK_RESPONSE_ACCEPT);
+	if ((current_folder != NULL) && (*current_folder != '\0'))
+		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel),
+						    current_folder);
+
+	dialog->icon_preview = gtk_image_new();
+	dialog->icon_text = gtk_label_new(NULL);
+	gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview), -1, 50);
+	gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog->icon_filesel),
+					    GTK_WIDGET(dialog->icon_preview));
+	g_signal_connect(G_OBJECT(dialog->icon_filesel), "update-preview",
+					 G_CALLBACK(icon_preview_change_cb), dialog);
+	g_signal_connect(G_OBJECT(dialog->icon_filesel), "response",
+					 G_CALLBACK(icon_filesel_choose_cb), dialog);
+	icon_preview_change_cb(NULL, dialog);
+#else /* FILECHOOSER */
+	dialog->icon_filesel = gtk_file_selection_new(_("Buddy Icon"));
+	dialog->icon_preview = gtk_image_new();
+	dialog->icon_text = gtk_label_new(NULL);
+	if ((current_folder != NULL) && (*current_folder != '\0'))
+		gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog->icon_filesel),
+										current_folder);
+
+	gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview), -1, 50);
+	hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE);
+	gtk_box_pack_start(
+		GTK_BOX(GTK_FILE_SELECTION(dialog->icon_filesel)->main_vbox),
+		hbox, FALSE, FALSE, 0);
+	gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_preview,
+			 FALSE, FALSE, 0);
+	gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_text, FALSE, FALSE, 0);
+
+	tv = GTK_FILE_SELECTION(dialog->icon_filesel)->file_list;
+	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
+
+	g_signal_connect(G_OBJECT(sel), "changed",
+					 G_CALLBACK(icon_preview_change_cb), dialog);
+	g_signal_connect(
+		G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->ok_button),
+		"clicked",
+		G_CALLBACK(icon_filesel_choose_cb), dialog);
+	g_signal_connect(
+		G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->cancel_button),
+		"clicked",
+		G_CALLBACK(icon_filesel_delete_cb), dialog);
+	g_signal_connect(G_OBJECT(dialog->icon_filesel), "destroy",
+					 G_CALLBACK(icon_filesel_delete_cb), dialog);
+#endif /* FILECHOOSER */
+	return 	dialog->icon_filesel;
+}
+
+
+#if GTK_CHECK_VERSION(2,2,0)
+static gboolean
+str_array_match(char **a, char **b)
+{
+	int i, j;
+
+	if (!a || !b)
+		return FALSE;
+	for (i = 0; a[i] != NULL; i++)
+		for (j = 0; b[j] != NULL; j++)
+			if (!g_ascii_strcasecmp(a[i], b[j]))
+				return TRUE;
+	return FALSE;
+}
+#endif
+
+char*
+gaim_gtk_convert_buddy_icon(GaimPlugin *plugin, const char *path)
+{
+#if GTK_CHECK_VERSION(2,2,0)
+	int width, height;
+	char **pixbuf_formats = NULL;
+	GdkPixbufFormat *format;
+	GdkPixbuf *pixbuf;
+	GaimPluginProtocolInfo *prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
+	char **prpl_formats =  g_strsplit (prpl_info->icon_spec.format,",",0);
+#if !GTK_CHECK_VERSION(2,4,0)
+	GdkPixbufLoader *loader;
+	FILE *file;
+	struct stat st;
+	void *data = NULL;
+#endif
+#endif
+	const char *dirname = gaim_buddy_icons_get_cache_dir();
+	char *random   = g_strdup_printf("%x", g_random_int());
+	char *filename = g_build_filename(dirname, random, NULL);
+
+	if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
+		gaim_debug_info("buddyicon", "Creating icon cache directory.\n");
+
+		if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
+			gaim_debug_error("buddyicon",
+							 "Unable to create directory %s: %s\n",
+							 dirname, strerror(errno));
+#if GTK_CHECK_VERSION(2,2,0)
+			g_strfreev(prpl_formats);
+#endif
+			g_free(random);
+			g_free(filename);
+			return NULL;
+		}
+	}
+
+#if GTK_CHECK_VERSION(2,2,0)
+#if GTK_CHECK_VERSION(2,4,0)
+	format = gdk_pixbuf_get_file_info (path, &width, &height);
+#else
+	loader = gdk_pixbuf_loader_new();
+	if (!g_stat(path, &st) && (file = g_fopen(path, "rb")) != NULL) {
+		data = g_malloc(st.st_size);
+		fread(data, 1, st.st_size, file);
+		fclose(file);
+		gdk_pixbuf_loader_write(loader, data, st.st_size, NULL);
+		g_free(data);
+	}
+	gdk_pixbuf_loader_close(loader, NULL);
+	pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
+	width = gdk_pixbuf_get_width(pixbuf);
+	height = gdk_pixbuf_get_height(pixbuf);
+	format = gdk_pixbuf_loader_get_format(loader);
+	g_object_unref(G_OBJECT(loader));
+#endif
+	pixbuf_formats =  gdk_pixbuf_format_get_extensions(format);
+
+	if (str_array_match(pixbuf_formats, prpl_formats) &&                  /* This is an acceptable format AND */
+		 (!(prpl_info->icon_spec.scale_rules & GAIM_ICON_SCALE_SEND) ||   /* The prpl doesn't scale before it sends OR */
+		  (prpl_info->icon_spec.min_width <= width &&
+		   prpl_info->icon_spec.max_width >= width &&
+		   prpl_info->icon_spec.min_height <= height &&
+		   prpl_info->icon_spec.max_height >= height)))                   /* The icon is the correct size */
+#endif
+	{
+		gchar *contents;
+		gsize length;
+		FILE *image;
+
+#if GTK_CHECK_VERSION(2,2,0)
+		g_strfreev(prpl_formats);
+		g_strfreev(pixbuf_formats);
+#endif
+
+		/* Copy the image to the cache folder as "filename". */
+
+		if (!g_file_get_contents(path, &contents, &length, NULL) ||
+		    (image = g_fopen(filename, "wb")) == NULL)
+		{
+			g_free(random);
+			g_free(filename);
+#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
+			g_object_unref(G_OBJECT(pixbuf));
+#endif
+			return NULL;
+		}
+
+		if (fwrite(contents, 1, length, image) != length)
+		{
+			fclose(image);
+			g_unlink(filename);
+
+			g_free(random);
+			g_free(filename);
+#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
+			g_object_unref(G_OBJECT(pixbuf));
+#endif
+			return NULL;
+		}
+		fclose(image);
+
+#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
+		g_object_unref(G_OBJECT(pixbuf));
+#endif
+
+		g_free(filename);
+		return random;
+	}
+#if GTK_CHECK_VERSION(2,2,0)
+	else
+	{
+		int i;
+		GError *error = NULL;
+		GdkPixbuf *scale;
+		pixbuf = gdk_pixbuf_new_from_file(path, &error);
+		g_strfreev(pixbuf_formats);
+		if (!error && (prpl_info->icon_spec.scale_rules & GAIM_ICON_SCALE_SEND) &&
+			(width < prpl_info->icon_spec.min_width ||
+			 width > prpl_info->icon_spec.max_width ||
+			 height < prpl_info->icon_spec.min_height ||
+			 height > prpl_info->icon_spec.max_height))
+		{
+			int new_width = width;
+			int new_height = height;
+
+			if(new_width > prpl_info->icon_spec.max_width)
+				new_width = prpl_info->icon_spec.max_width;
+			else if(new_width < prpl_info->icon_spec.min_width)
+				new_width = prpl_info->icon_spec.min_width;
+			if(new_height > prpl_info->icon_spec.max_height)
+				new_height = prpl_info->icon_spec.max_height;
+			else if(new_height < prpl_info->icon_spec.min_height)
+				new_height = prpl_info->icon_spec.min_height;
+
+			/* preserve aspect ratio */
+			if ((double)height * (double)new_width >
+				(double)width * (double)new_height) {
+					new_width = 0.5 + (double)width * (double)new_height / (double)height;
+			} else {
+					new_height = 0.5 + (double)height * (double)new_width / (double)width;
+			}
+
+			scale = gdk_pixbuf_scale_simple (pixbuf, new_width, new_height,
+					GDK_INTERP_HYPER);
+			g_object_unref(G_OBJECT(pixbuf));
+			pixbuf = scale;
+		}
+		if (error) {
+			g_free(random);
+			g_free(filename);
+			gaim_debug_error("buddyicon", "Could not open icon for conversion: %s\n", error->message);
+			g_error_free(error);
+			g_strfreev(prpl_formats);
+			return NULL;
+		}
+
+		for (i = 0; prpl_formats[i]; i++) {
+			gaim_debug_info("buddyicon", "Converting buddy icon to %s as %s\n", prpl_formats[i], filename);
+			/* The gdk-pixbuf documentation is wrong. gdk_pixbuf_save returns TRUE if it was successful,
+			 * FALSE if an error was set. */
+			if (gdk_pixbuf_save (pixbuf, filename, prpl_formats[i], &error, NULL) == TRUE)
+					break;
+			gaim_debug_warning("buddyicon", "Could not convert to %s: %s\n", prpl_formats[i], error->message);
+			g_error_free(error);
+			error = NULL;
+		}
+		g_strfreev(prpl_formats);
+		if (!error) {
+			g_object_unref(G_OBJECT(pixbuf));
+			g_free(filename);
+			return random;
+		} else {
+			gaim_debug_error("buddyicon", "Could not convert icon to usable format: %s\n", error->message);
+			g_error_free(error);
+		}
+		g_free(random);
+		g_free(filename);
+		g_object_unref(G_OBJECT(pixbuf));
+	}
+	return NULL;
+#endif
+}
--- a/src/gtkutils.h	Tue Aug 15 20:23:58 2006 +0000
+++ b/src/gtkutils.h	Tue Aug 15 23:25:29 2006 +0000
@@ -460,4 +460,23 @@
  */
 void gaim_gtk_clear_cursor(GtkWidget *widget);
 
+/**
+ * Creates a File Selection widget for choosing a buddy icon
+ *
+ * @param parent      The parent window
+ * @param callback    The callback to call when the window is closed. If the user chose an icon, the char* argument will point to its path
+ * @param data        Data to pass to @callback
+ * @return            The file dialog 
+ */
+GtkWidget *gaim_gtk_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char*,gpointer), gpointer data);
+
+/**
+ * Converts a buddy icon to the required size and format
+ * 
+ * @param plugin     The prpl to conver the icon
+ * @param path       The path of a buddy icon to convert
+ * @return           The path of a new buddy icon
+ */
+char* gaim_gtk_convert_buddy_icon(GaimPlugin *plugin, const char *path);
+
 #endif /* _GAIM_GTKUTILS_H_ */