changeset 23694:2d419a0fc0e4

partially working status icon theme stuff
author Justin Rodriguez <ffdragon@soc.pidgin.im>
date Fri, 08 Aug 2008 01:42:46 +0000
parents d93578eb7244
children f29e173320a7
files libpurple/xmlnode.c pidgin/Makefile.am pidgin/gtkblist.c pidgin/gtkprefs.c pidgin/pidginstock.c pidgin/pidginstock.h
diffstat 6 files changed, 336 insertions(+), 162 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/xmlnode.c	Wed Aug 06 02:54:39 2008 +0000
+++ b/libpurple/xmlnode.c	Fri Aug 08 01:42:46 2008 +0000
@@ -728,6 +728,78 @@
 	return ret;
 }
 
+xmlnode *
+xmlnode_from_file(const char *dir, const char *filename, const char *description, const char *process)
+{
+	gchar *filename_full;
+	GError *error = NULL;
+	gchar *contents = NULL;
+	gsize length;
+	xmlnode *node = NULL;
+
+	g_return_val_if_fail(dir != NULL, NULL);
+
+	purple_debug_info(process, "Reading file %s from directory %s\n",
+					filename, dir);
+
+	filename_full = g_build_filename(dir, filename, NULL);
+
+	if (!g_file_test(filename_full, G_FILE_TEST_EXISTS))
+	{
+		purple_debug_info(process, "File %s does not exist (this is not "
+						"necessarily an error)\n", filename_full);
+		g_free(filename_full);
+		return NULL;
+	}
+
+	if (!g_file_get_contents(filename_full, &contents, &length, &error))
+	{
+		purple_debug_error(process, "Error reading file %s: %s\n",
+						 filename_full, error->message);
+		g_error_free(error);
+	}
+
+	if ((contents != NULL) && (length > 0))
+	{
+		node = xmlnode_from_str(contents, length);
+
+		/* If we were unable to parse the file then save its contents to a backup file */
+		if (node == NULL)
+		{
+			gchar *filename_temp, *filename_temp_full;
+
+			filename_temp = g_strdup_printf("%s~", filename);
+			filename_temp_full = g_build_filename(dir, filename_temp, NULL);
+
+			purple_debug_error("util", "Error parsing file %s.  Renaming old "
+							 "file to %s\n", filename_full, filename_temp);
+			purple_util_write_data_to_file_absolute(filename_temp_full, contents, length);
+
+			g_free(filename_temp_full);
+			g_free(filename_temp);
+		}
+
+		g_free(contents);
+	}
+
+	/* If we could not parse the file then show the user an error message */
+	if (node == NULL)
+	{
+		gchar *title, *msg;
+		title = g_strdup_printf(_("Error Reading %s"), filename);
+		msg = g_strdup_printf(_("An error was encountered reading your "
+					"%s.  The file has not been loaded, and the old file "
+					"has been renamed to %s~."), description, filename_full);
+		purple_notify_error(NULL, NULL, title, msg);
+		g_free(title);
+		g_free(msg);
+	}
+
+	g_free(filename_full);
+
+	return node;
+}
+
 static void
 xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
 {
--- a/pidgin/Makefile.am	Wed Aug 06 02:54:39 2008 +0000
+++ b/pidgin/Makefile.am	Fri Aug 08 01:42:46 2008 +0000
@@ -96,6 +96,8 @@
 	gtkeventloop.c \
 	gtkexpander.c \
 	gtkft.c \
+	gtkicon-theme.c \
+	gtkicon-loader.c \
 	gtkidle.c \
 	gtkimhtml.c \
 	gtkimhtmltoolbar.c \
@@ -150,6 +152,8 @@
 	gtkeventloop.h \
 	gtkexpander.h \
 	gtkft.h \
+	gtkicon-theme.h \
+	gtkicon-loader.h \
 	gtkidle.h \
 	gtkgaim-compat.h \
 	gtkimhtml.h \
--- a/pidgin/gtkblist.c	Wed Aug 06 02:54:39 2008 +0000
+++ b/pidgin/gtkblist.c	Fri Aug 08 01:42:46 2008 +0000
@@ -50,6 +50,7 @@
 #include "gtkdebug.h"
 #include "gtkdialogs.h"
 #include "gtkft.h"
+#include "gtkicon-theme.h"
 #include "gtklog.h"
 #include "gtkmenutray.h"
 #include "gtkpounce.h"
@@ -5385,6 +5386,8 @@
 
 	priv->current_theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"), "blist"));
 
+	pidgin_stock_load_status_icon_theme(PIDGIN_ICON_THEME(purple_theme_manager_find_theme(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/icon/status/theme"), "status_icon")));
+
 	gtkblist->empty_avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
 	gdk_pixbuf_fill(gtkblist->empty_avatar, 0x00000000);
 
--- a/pidgin/gtkprefs.c	Wed Aug 06 02:54:39 2008 +0000
+++ b/pidgin/gtkprefs.c	Fri Aug 08 01:42:46 2008 +0000
@@ -44,6 +44,7 @@
 #include "gtkconv.h"
 #include "gtkdebug.h"
 #include "gtkdialogs.h"
+#include "gtkicon-theme.h"
 #include "gtkimhtml.h"
 #include "gtkimhtmltoolbar.h"
 #include "gtkprefs.h"
@@ -76,6 +77,7 @@
 static gboolean prefs_themes_unsorted = TRUE;
 static GtkListStore *prefs_sound_themes;
 static GtkListStore *prefs_blist_themes;
+static GtkListStore *prefs_status_icon_themes;
  
 
 /*
@@ -618,7 +620,12 @@
 		if (pixbuf != NULL)
 			gdk_pixbuf_unref(pixbuf);
 
-	} else if (PIDGIN_IS_BLIST_THEME(theme)){
+	} else if (PIDGIN_IS_BLIST_THEME(theme) || PIDGIN_IS_ICON_THEME(theme)){
+		GtkListStore *store;
+
+		if (PIDGIN_IS_BLIST_THEME(theme)) 
+			store = prefs_blist_themes;
+		else store = prefs_status_icon_themes;
 
 		image_full = purple_theme_get_image_full(theme);
 		if (image_full != NULL){
@@ -633,13 +640,14 @@
 		markup = g_strdup_printf("<b>%s</b>%s%s\n<span foreground='dim grey'>%s</span>", name, author != NULL ? " - " : "",
 					 author != NULL ? author : "", description != NULL ? description : "");
 
-		gtk_list_store_append (prefs_blist_themes, &iter);
-		gtk_list_store_set (prefs_blist_themes, &iter, 0, pixbuf, 1, markup, 2, name, -1);
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter, 0, pixbuf, 1, markup, 2, name, -1);
 
 		g_free(markup);
 		if (pixbuf != NULL)
 			gdk_pixbuf_unref(pixbuf);
-	}
+	} 
+
 }
 
 /* init all the theme variables so that the themes can be sorted later and used by pref pages */
@@ -667,9 +675,52 @@
 	gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>"
 								    "The default Pidgin buddy list theme</span>", 2, "", -1);
 
+	/* status icon themes */
+	prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+
+	gtk_list_store_append(prefs_status_icon_themes, &iter);
+	gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>"
+								    "The default Pidgin status icon theme</span>", 2, "", -1);
+
 	gdk_pixbuf_unref(pixbuf);
 }
 
+/* builds a theme combo box from a list store with colums: icon preview, markup, theme name */
+static GtkWidget *
+prefs_build_theme_combo_box(GtkListStore *store, const gchar *current_theme)
+{
+	GtkWidget *combo_box;
+	GtkCellRenderer *cell_rend;
+	GtkTreeIter iter;
+	gchar *theme = NULL;
+
+	g_return_val_if_fail(store != NULL && current_theme != NULL, NULL);
+
+	combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
+
+	cell_rend = gtk_cell_renderer_pixbuf_new();
+	gtk_cell_renderer_set_fixed_size(cell_rend, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE);
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "pixbuf", 0, NULL);
+	
+	cell_rend = gtk_cell_renderer_text_new();
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "markup", 1, NULL);
+	
+	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
+		do {
+			gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 2, &theme, -1);
+
+			if (g_str_equal(current_theme, theme))
+				gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter);
+			
+			g_free(theme);
+		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
+	}
+
+	return combo_box;
+}
+
 /* sets the current sound theme */
 static void
 prefs_set_sound_theme_cb(GtkComboBox *combo_box, gpointer user_data)
@@ -1097,6 +1148,22 @@
 	pidgin_blist_set_theme(theme);
 }
 
+/* sets the current icon theme */
+static void
+prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data)
+{
+	PidginIconTheme *theme;
+	GtkTreeIter iter;
+	gchar *name = NULL;
+	
+	g_return_if_fail(gtk_combo_box_get_active_iter(combo_box, &iter));
+	gtk_tree_model_get(GTK_TREE_MODEL(prefs_status_icon_themes), &iter, 2, &name, -1);
+
+	theme = PIDGIN_ICON_THEME(purple_theme_manager_find_theme(name, "icon"));
+	g_free(name);
+
+	pidgin_stock_load_status_icon_theme(theme);
+}
 
 static GtkWidget *
 interface_page(void)
@@ -1106,12 +1173,8 @@
 	GtkWidget *vbox2;
 	GtkWidget *label;
 	GtkWidget *combo_box;
-	GtkCellRenderer *cell_rend;
 	GtkSizeGroup *sg;
-	GtkTreeIter iter;
 	GList *names = NULL;
-	gchar *theme;
-	const gchar *current_theme;
 
 	ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
 	gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
@@ -1120,33 +1183,16 @@
 
 	/* Buddy List Themes */
 	vbox = pidgin_make_frame(ret, _("Buddy List Theme"));
-	combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL (prefs_blist_themes));
-	gtk_box_pack_start(GTK_BOX (vbox), combo_box, TRUE, TRUE, 0);
-
-	cell_rend = gtk_cell_renderer_pixbuf_new();
-	gtk_cell_renderer_set_fixed_size(cell_rend, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE);
-	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
-	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "pixbuf", 0, NULL);
-	
-	cell_rend = gtk_cell_renderer_text_new();
-	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
-	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "markup", 1, NULL);
-
-	current_theme = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme");
-	
-	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(prefs_blist_themes), &iter)) {
-		do {
-			gtk_tree_model_get(GTK_TREE_MODEL(prefs_blist_themes), &iter, 2, &theme, -1);
-
-			if (g_str_equal(current_theme, theme))
-				gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter);
-			
-			g_free(theme);
-		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(prefs_blist_themes), &iter));
-	}
-
+
+	combo_box = prefs_build_theme_combo_box(prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"));
+	gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
 	g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_blist_theme_cb, NULL);
 
+	/* Status Icon Themes */
+	combo_box = prefs_build_theme_combo_box(prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/icon/theme"));
+	gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_status_icon_theme_cb, NULL);
+
 	/* System Tray */	
 	vbox = pidgin_make_frame(ret, _("System Tray Icon"));
 	label = pidgin_prefs_dropdown(vbox, _("_Show system tray icon:"), PURPLE_PREF_STRING,
@@ -2059,12 +2105,9 @@
 	GtkTreeSelection *sel;
 	GtkTreePath *path;
 	GtkWidget *hbox;
-	PurpleSoundTheme *theme;
-	gboolean equal, customized, print_custom;
 	int j;
-	const char *file, *current_theme, *name, *author, *description;
-	char *pref, *markup;
-	GtkCellRenderer *cell_rend;
+	const char *file;
+	char *pref;
 #ifndef _WIN32
 	GtkWidget *dd;
 	GtkWidget *entry;
@@ -2154,52 +2197,10 @@
 			vbox->parent->parent, TRUE, TRUE, 0, GTK_PACK_START);
 
 	/* SOUND THEMES */
-	combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL (prefs_sound_themes));
+	combo_box = prefs_build_theme_combo_box(prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"));
+	pref_sound_generate_markup();
 	gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
 
-	cell_rend = gtk_cell_renderer_pixbuf_new();
-	gtk_cell_renderer_set_fixed_size(cell_rend, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE);
-	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
-	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "pixbuf", 0, NULL);
-	
-	cell_rend = gtk_cell_renderer_text_new();
-	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
-	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "markup", 1, NULL);
-
-	/* Generate markup and set combo box to the active theme */
-	current_theme = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme");
-	customized = pidgin_sound_is_customized();
-	
-	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(prefs_sound_themes), &iter)) {
-		do {
-			gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &iter, 2, &name, -1);
-			
-			equal = g_str_equal(current_theme, name);
-
-			if (equal)
-				gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter);
-
-			print_custom = customized && equal;
-			
-			if (g_str_equal(name, ""))
-				markup = g_strdup_printf("<b>(Default)</b>%s%s - None\n<span foreground='dim grey'>The default Pidgin sound theme</span>",
-							 print_custom ? " " : "", print_custom ? "(Custom)" : ""); 
-			else {
-				theme = PURPLE_SOUND_THEME(purple_theme_manager_find_theme(name, "sound"));
-				author = purple_theme_get_author(PURPLE_THEME(theme));
-				description = purple_theme_get_description(PURPLE_THEME(theme));
-				
-				markup = g_strdup_printf("<b>%s</b>%s%s%s%s\n<span foreground='dim grey'>%s</span>",
-							 name, print_custom ? " " : "", print_custom ? "(Custom)" : "", 
-							 author != NULL ? " - " : "", author != NULL ? author : "", description != NULL ? description : ""); 
-			}
-
-			gtk_list_store_set(prefs_sound_themes, &iter, 1, markup, -1);
-			g_free(markup);
-
-		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(prefs_sound_themes), &iter));
-	}
-
 	g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_sound_theme_cb, NULL);
 
 	/* SOUND SELECTION */
--- a/pidgin/pidginstock.c	Wed Aug 06 02:54:39 2008 +0000
+++ b/pidgin/pidginstock.c	Fri Aug 08 01:42:46 2008 +0000
@@ -26,17 +26,32 @@
  */
 #include "internal.h"
 #include "pidgin.h"
+#include "prefs.h"
+
+#include "gtkicon-loader.h"
+#include "theme-manager.h"
 
 #include "pidginstock.h"
 
+/**************************************************************************
+ * Globals
+ **************************************************************************/
+
+static gboolean stock_initted = FALSE;
+static GtkIconSize microscopic, extra_small, small, medium, large, huge;
+
+/**************************************************************************
+ * Structures
+ **************************************************************************/
+
 static struct StockIcon
 {
 	const char *name;
 	const char *dir;
 	const char *filename;
 
-} const stock_icons[] =
-{
+} const stock_icons[] = {
+
 	{ PIDGIN_STOCK_ACTION,          NULL,      GTK_STOCK_EXECUTE          },
 #if GTK_CHECK_VERSION(2,6,0)
 	{ PIDGIN_STOCK_ALIAS,           NULL,      GTK_STOCK_EDIT             },
@@ -95,7 +110,7 @@
 	{ PIDGIN_STOCK_PAUSE,               N_("_Pause"),      0, 0, NULL },
 };
 
-static struct SizedStockIcon {
+typedef struct {
   const char *name;
   const char *dir;
   const char *filename;
@@ -107,18 +122,9 @@
   gboolean huge;
   gboolean rtl;
   const char *translucent_name;
-} const sized_stock_icons [] = {
-	{ PIDGIN_STOCK_STATUS_AVAILABLE,   "status", "available.png", 	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AVAILABLE_I },
-	{ PIDGIN_STOCK_STATUS_AWAY, 	   "status", "away.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AWAY_I },
-	{ PIDGIN_STOCK_STATUS_BUSY, 	"status", "busy.png", 		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_BUSY_I },
-	{ PIDGIN_STOCK_STATUS_CHAT, 	"status", "chat.png",		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_STATUS_INVISIBLE,"status", "invisible.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_STATUS_XA, 	"status", "extended-away.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, PIDGIN_STOCK_STATUS_XA_I },
-	{ PIDGIN_STOCK_STATUS_LOGIN, 	"status", "log-in.png",		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
-	{ PIDGIN_STOCK_STATUS_LOGOUT, 	"status", "log-out.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
-	{ PIDGIN_STOCK_STATUS_OFFLINE, 	"status", "offline.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_OFFLINE_I  },
-	{ PIDGIN_STOCK_STATUS_PERSON, 	"status", "person.png",		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_STATUS_MESSAGE, 	"toolbar", "message-new.png",   TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+} SizedStockIcon;
+
+const SizedStockIcon sized_stock_icons [] = {
 	
 	{ PIDGIN_STOCK_STATUS_IGNORED,	"emblems", "blocked.png",	FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_STATUS_FOUNDER,	"emblems", "founder.png",	FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
@@ -150,37 +156,56 @@
 	{ PIDGIN_STOCK_ANIMATION_TYPING4,  "animations", "typing4.png",FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_ANIMATION_TYPING5,  "animations", "typing5.png",FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 
-	{ PIDGIN_STOCK_TOOLBAR_ACCOUNTS, "toolbar", "accounts.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_BGCOLOR, "toolbar", "change-bgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_BLOCK, "emblems", "blocked.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_FGCOLOR, "toolbar", "change-fgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_SMILEY, "toolbar", "emote-select.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_FONT_FACE, "toolbar", "font-face.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, "toolbar", "font-size-down.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_TEXT_LARGER, "toolbar", "font-size-up.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_INSERT, "toolbar", "insert.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, "toolbar", "insert-image.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_INSERT_LINK, "toolbar", "insert-link.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, "toolbar", "message-new.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_PENDING, "toolbar", "message-new.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_PLUGINS, "toolbar", "plugins.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_TYPING, "toolbar", "typing.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_UNBLOCK, "toolbar", "unblock.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR, "toolbar", "select-avatar.png", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_SEND_FILE, "toolbar", "send-file.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_TRANSFER, "toolbar", "transfer.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_ACCOUNTS,	"toolbar", "accounts.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_BGCOLOR,		"toolbar", "change-bgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_BLOCK,		"emblems", "blocked.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_FGCOLOR,		"toolbar", "change-fgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_SMILEY,		"toolbar", "emote-select.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_FONT_FACE,	"toolbar", "font-face.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER,	"toolbar", "font-size-down.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_TEXT_LARGER,	"toolbar", "font-size-up.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_INSERT,		"toolbar", "insert.png", 	 FALSE,	TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE,	"toolbar", "insert-image.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_INSERT_LINK,	"toolbar", "insert-link.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW,	"toolbar", "message-new.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_PENDING,		"toolbar", "message-new.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_PLUGINS,		"toolbar", "plugins.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_TYPING,		"toolbar", "typing.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_UNBLOCK,		"toolbar", "unblock.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR,	"toolbar", "select-avatar.png",	 FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_SEND_FILE,	"toolbar", "send-file.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_TRANSFER,	"toolbar", "transfer.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  }
+};
+
+const SizedStockIcon sized_status_icons [] = {
 
-	{ PIDGIN_STOCK_TRAY_AVAILABLE, "tray", "tray-online.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_INVISIBLE, "tray", "tray-invisible.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_AWAY, "tray", "tray-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_BUSY, "tray", "tray-busy.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_XA, "tray", "tray-extended-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_OFFLINE, "tray", "tray-offline.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_CONNECT, "tray", "tray-connecting.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_PENDING, "tray", "tray-new-im.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_EMAIL, "tray", "tray-message.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  }
+	{ PIDGIN_STOCK_STATUS_AVAILABLE, "status", "available.png", 	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AVAILABLE_I },
+	{ PIDGIN_STOCK_STATUS_AWAY, 	 "status", "away.png",		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AWAY_I },
+	{ PIDGIN_STOCK_STATUS_BUSY, 	 "status", "busy.png", 		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_BUSY_I },
+	{ PIDGIN_STOCK_STATUS_CHAT, 	 "status", "chat.png",		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
+	{ PIDGIN_STOCK_STATUS_INVISIBLE, "status", "invisible.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
+	{ PIDGIN_STOCK_STATUS_XA, 	 "status", "extended-away.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE,  PIDGIN_STOCK_STATUS_XA_I },
+	{ PIDGIN_STOCK_STATUS_LOGIN, 	 "status", "log-in.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE,  NULL },
+	{ PIDGIN_STOCK_STATUS_LOGOUT, 	 "status", "log-out.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE,  NULL },
+	{ PIDGIN_STOCK_STATUS_OFFLINE, 	 "status", "offline.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_OFFLINE_I  },
+	{ PIDGIN_STOCK_STATUS_PERSON, 	 "status", "person.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_STATUS_MESSAGE, 	 "toolbar", "message-new.png",  TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+
+	{ PIDGIN_STOCK_TRAY_AVAILABLE,	"tray", "tray-online.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_INVISIBLE,	"tray", "tray-invisible.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_AWAY,	"tray", "tray-away.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_BUSY,	"tray", "tray-busy.png", 	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_XA,		"tray", "tray-extended-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_OFFLINE,	"tray", "tray-offline.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_CONNECT,	"tray", "tray-connecting.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_PENDING,	"tray", "tray-new-im.png", 	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_EMAIL,	"tray", "tray-message.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  }
 };
 
+/*****************************************************************************
+ * Private functions                                                      
+ *****************************************************************************/
+
 static gchar *
 find_file(const char *dir, const char *base)
 {
@@ -335,25 +360,106 @@
 		g_object_unref(pixbuf);
 		gtk_icon_source_free(source);
         }
+}
 
+static void
+build_sized_icon_sets(GtkIconSet *normal, GtkIconSet *translucent, SizedStockIcon icon,
+		      GtkIconSize size, PidginIconTheme *theme, const gchar *pix)
+{
+	const gchar *file, *dir;
+	gchar *file_full;
+	gboolean use_theme;
 
+	if ((use_theme = theme != NULL)) {
+		file = pidgin_icon_theme_get_file(theme, icon.name);
+		dir = purple_theme_get_dir(PURPLE_THEME(theme));
+
+		file_full = g_build_filename(dir, pix, file, NULL);
+		use_theme = g_file_test(file_full, G_FILE_TEST_IS_REGULAR);
+		g_free(file_full);
+	} 
+
+	if (!use_theme) {
+		file = icon.filename;
+		dir = icon.dir;
+	}
+
+	add_sized_icon(normal, size, dir, icon.rtl, pix, file);
+	if (translucent != NULL)
+		add_translucent_sized_icon(translucent, size, dir, icon.rtl, pix, file);
 }
 
+/*****************************************************************************
+ * Public API functions                                                      
+ *****************************************************************************/
+
+void
+pidgin_stock_load_status_icon_theme(PidginIconTheme *theme)
+{
+	GtkIconFactory *icon_factory;
+	gint i;
+	GtkIconSet *normal;
+	GtkIconSet *translucent = NULL;
+	GtkWidget *win;
+
+	g_return_if_fail(stock_initted);
+	
+	icon_factory = gtk_icon_factory_new();
+
+	gtk_icon_factory_add_default(icon_factory);
+
+	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_widget_realize(win);
+
+	for (i = 0; i < G_N_ELEMENTS(sized_status_icons); i++)
+	{
+		normal = gtk_icon_set_new();
+		if (sized_status_icons[i].translucent_name)
+			translucent = gtk_icon_set_new();
+
+		if (sized_status_icons[i].microscopic)
+			build_sized_icon_sets(normal, translucent, sized_status_icons[i], microscopic, theme, "11");
+		if (sized_status_icons[i].extra_small)
+			build_sized_icon_sets(normal, translucent, sized_status_icons[i], extra_small, theme, "16");
+		if (sized_status_icons[i].small)
+			build_sized_icon_sets(normal, translucent, sized_status_icons[i], small, theme, "22");
+		if (sized_status_icons[i].medium)
+			build_sized_icon_sets(normal, translucent, sized_status_icons[i], medium, theme,  "32");
+		if (sized_status_icons[i].large)
+			build_sized_icon_sets(normal, translucent, sized_status_icons[i], large, theme, "48");
+		if (sized_status_icons[i].huge)
+			build_sized_icon_sets(normal, translucent, sized_status_icons[i], huge, theme, "64");
+
+		gtk_icon_factory_add(icon_factory, sized_status_icons[i].name, normal);
+		gtk_icon_set_unref(normal);
+
+		if (sized_status_icons[i].translucent_name) {
+			gtk_icon_factory_add(icon_factory, sized_status_icons[i].translucent_name, translucent);
+			gtk_icon_set_unref(translucent);
+		}
+	}
+
+	gtk_widget_destroy(win);
+	g_object_unref(G_OBJECT(icon_factory));
+}
 
 void
 pidgin_stock_init(void)
 {
-	static gboolean stock_initted = FALSE;
 	GtkIconFactory *icon_factory;
 	size_t i;
 	GtkWidget *win;
-	GtkIconSize microscopic, extra_small, small, medium, large, huge;
 
 	if (stock_initted)
 		return;
 
 	stock_initted = TRUE;
 
+	/* Setup the theme */
+	purple_theme_manager_register_type(g_object_new(PIDGIN_TYPE_ICON_THEME_LOADER, "type", "icon", NULL));
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/icon/status");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/icon/status/theme", "");
+
 	/* Setup the icon factory. */
 	icon_factory = gtk_icon_factory_new();
 
@@ -402,7 +508,6 @@
 	}
 
 	/* register custom icon sizes */
-
 	microscopic =  gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11);
 	extra_small =  gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16);
 	small =        gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22);
@@ -416,48 +521,28 @@
 
 		iconset = gtk_icon_set_new();
 
-#define ADD_SIZED_ICON(name, size) do { \
-		if (sized_stock_icons[i].name)  \
-			add_sized_icon(iconset, name,  \
-					sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \
-					size, sized_stock_icons[i].filename); \
-		} while (0)
-		ADD_SIZED_ICON(microscopic, "11");
-		ADD_SIZED_ICON(extra_small, "16");
-		ADD_SIZED_ICON(small, "22");
-		ADD_SIZED_ICON(medium, "32");
-		ADD_SIZED_ICON(large, "48");
-		ADD_SIZED_ICON(huge, "64");
-#undef ADD_SIZED_ICON
+		if (sized_stock_icons[i].microscopic)
+			build_sized_icon_sets(iconset, NULL, sized_stock_icons[i], microscopic, NULL, "11");
+		if (sized_stock_icons[i].extra_small)
+			build_sized_icon_sets(iconset, NULL, sized_stock_icons[i], extra_small, NULL, "16");
+		if (sized_stock_icons[i].small)
+			build_sized_icon_sets(iconset, NULL, sized_stock_icons[i], small, NULL, "22");
+		if (sized_stock_icons[i].medium)
+			build_sized_icon_sets(iconset, NULL, sized_stock_icons[i], medium, NULL, "32");
+		if (sized_stock_icons[i].large)
+			build_sized_icon_sets(iconset, NULL, sized_stock_icons[i], large, NULL, "48");
+		if (sized_stock_icons[i].huge)
+			build_sized_icon_sets(iconset, NULL, sized_stock_icons[i], huge, NULL, "64");
 
 		gtk_icon_factory_add(icon_factory, sized_stock_icons[i].name, iconset);
 		gtk_icon_set_unref(iconset);
-
-		if (sized_stock_icons[i].translucent_name) {
-			iconset = gtk_icon_set_new();
-
-#define ADD_TRANS_ICON(name, size) do { \
-			if (sized_stock_icons[i].name) \
-				add_translucent_sized_icon(iconset, name, \
-						sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \
-						size, sized_stock_icons[i].filename); \
-			} while (0)
-			ADD_TRANS_ICON(microscopic, "11");
-			ADD_TRANS_ICON(extra_small, "16");
-			ADD_TRANS_ICON(small, "22");
-			ADD_TRANS_ICON(medium, "32");
-			ADD_TRANS_ICON(large, "48");
-			ADD_TRANS_ICON(huge, "64");
-#undef ADD_TRANS_ICON
-
-			gtk_icon_factory_add(icon_factory, sized_stock_icons[i].translucent_name, iconset);
-			gtk_icon_set_unref(iconset);
-		}
 	}
 
 	gtk_widget_destroy(win);
 	g_object_unref(G_OBJECT(icon_factory));
 
+	pidgin_stock_load_status_icon_theme(NULL);
+
 	/* Register the stock items. */
 	gtk_stock_add_static(stock_items, G_N_ELEMENTS(stock_items));
 }
--- a/pidgin/pidginstock.h	Wed Aug 06 02:54:39 2008 +0000
+++ b/pidgin/pidginstock.h	Fri Aug 08 01:42:46 2008 +0000
@@ -24,6 +24,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 #include <gtk/gtkstock.h>
+#include "gtkicon-theme.h"
 
 #ifndef _PIDGIN_STOCK_H_
 #define _PIDGIN_STOCK_H_
@@ -154,6 +155,14 @@
 #define PIDGIN_ICON_SIZE_TANGO_MEDIUM         "pidgin-icon-size-tango-medium"
 #define PIDGIN_ICON_SIZE_TANGO_LARGE          "pidgin-icon-size-tango-large"
 #define PIDGIN_ICON_SIZE_TANGO_HUGE           "pidgin-icon-size-tango-huge"
+
+/**
+ * Loades all of the icons from the status icon theme into Pidgin stock
+ * 
+ * @param theme		the theme to load, or null to load all the default icons
+ */
+void pidgin_stock_load_status_icon_theme(PidginIconTheme *theme);
+
 /**
  * Sets up the purple stock repository.
  */