changeset 23473:60d3ba8c9047

propagate from branch 'im.pidgin.pidgin' (head 1d533cebad7c0dbda8ec8ebee1334d27dcae5f9c) to branch 'im.pidgin.soc.2008.themes' (head 6108b8f480f1589e219641d557022940902871cf)
author Gary Kramlich <grim@reaperworld.com>
date Mon, 30 Jun 2008 23:12:54 +0000
parents 47b709962aab (current diff) fbf72bbd1084 (diff)
children 331a7a69d955
files finch/libgnt/gntkeys.c libpurple/Makefile.am libpurple/protocols/qq/qq_proxy.c libpurple/protocols/qq/qq_proxy.h libpurple/protocols/qq/recv_core.c libpurple/protocols/qq/recv_core.h libpurple/protocols/qq/send_core.c libpurple/protocols/qq/send_core.h libpurple/protocols/qq/sendqueue.c libpurple/protocols/qq/sendqueue.h libpurple/protocols/qq/udp_proxy_s5.c libpurple/protocols/qq/udp_proxy_s5.h libpurple/util.c
diffstat 18 files changed, 2011 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/Makefile.am	Mon Jun 30 22:58:18 2008 +0000
+++ b/libpurple/Makefile.am	Mon Jun 30 23:12:54 2008 +0000
@@ -75,8 +75,13 @@
 	stringref.c \
 	stun.c \
 	sound.c \
+	sound-loader.c \
+	sound-theme.c \
 	sslconn.c \
 	upnp.c \
+	theme.c \
+	theme-loader.c \
+	theme-manager.c \
 	util.c \
 	value.c \
 	version.c \
@@ -128,8 +133,13 @@
 	stringref.h \
 	stun.h \
 	sound.h \
+	sound-loader.h \
+	sound-theme.h \
 	sslconn.h \
 	upnp.h \
+	theme.h \
+	theme-loader.h \
+	theme-manager.h \
 	util.h \
 	value.h \
 	xmlnode.h \
--- a/libpurple/core.c	Mon Jun 30 22:58:18 2008 +0000
+++ b/libpurple/core.c	Mon Jun 30 23:12:54 2008 +0000
@@ -45,10 +45,12 @@
 #include "signals.h"
 #include "smiley.h"
 #include "sound.h"
+#include "sound-loader.h"
 #include "sslconn.h"
 #include "status.h"
 #include "stun.h"
 #include "util.h"
+#include "theme-manager.h"
 
 #ifdef HAVE_DBUS
 #  ifndef DBUS_API_SUBJECT_TO_CHANGE
@@ -147,6 +149,7 @@
 	/* Accounts use status, buddy icons and connection signals, so
 	 * initialize these before accounts
 	 */
+	purple_theme_manager_init(g_object_new(PURPLE_TYPE_SOUND_THEME_LOADER, "type", "sound", NULL), NULL);
 	purple_status_init();
 	purple_buddy_icons_init();
 	purple_connections_init();
@@ -169,7 +172,7 @@
 	purple_xfers_init();
 	purple_idle_init();
 	purple_smileys_init();
-
+		
 	/*
 	 * Call this early on to try to auto-detect our IP address and
 	 * hopefully save some time later.
@@ -178,6 +181,7 @@
 
 	if (ops != NULL && ops->ui_init != NULL)
 		ops->ui_init();
+	
 
 	return TRUE;
 }
@@ -213,6 +217,7 @@
 	purple_status_uninit();
 	purple_prefs_uninit();
 	purple_sound_uninit();
+	purple_theme_manager_uninit();
 	purple_xfers_uninit();
 	purple_proxy_uninit();
 	purple_dnsquery_uninit();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/sound-loader.c	Mon Jun 30 23:12:54 2008 +0000
@@ -0,0 +1,124 @@
+/*
+ * SoundThemeLoader for LibPurple
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ *
+ */
+
+#include "sound-loader.h"
+#include "sound-theme.h"
+#include "util.h"
+#include "xmlnode.h"
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+/*****************************************************************************
+ * Sound Theme Builder                                                      
+ *****************************************************************************/
+
+static gpointer
+purple_sound_loader_build(const gchar *dir)
+{
+	xmlnode *root_node, *sub_node;
+	gchar *filename, *filename_full, *data;
+	GDir *gdir;
+	PurpleSoundTheme *theme;
+
+	/* Find the theme file */
+	gdir = g_dir_open(dir, 0, NULL);
+	g_return_val_if_fail(gdir != NULL, NULL);
+
+	while ((filename = g_strdup(g_dir_read_name(gdir))) != NULL && ! g_str_has_suffix(filename, ".xml"))
+		g_free(filename);
+	
+	g_return_val_if_fail(filename != NULL, NULL);
+	
+	/* Build the xml tree */
+	filename_full = g_build_filename(dir, filename, NULL);
+
+	root_node = xmlnode_from_file(dir, filename, "sound themes", "sound-loader");
+	g_return_val_if_fail(root_node != NULL, NULL);
+
+	/* Parse the tree */	
+	sub_node = xmlnode_get_child(root_node, "description");
+	data = xmlnode_get_data(sub_node);
+
+	theme = g_object_new(PURPLE_TYPE_SOUND_THEME,
+			    "type", "sound",
+			    "name", xmlnode_get_attrib(root_node, "name"),
+			    "author", xmlnode_get_attrib(root_node, "author"),
+			    "image", xmlnode_get_attrib(root_node, "image"),
+			    "directory", dir,
+			    "description", data, NULL);
+	
+	xmlnode_free(sub_node);
+
+	while ((sub_node = xmlnode_get_child(root_node, "event")) != NULL){
+		purple_sound_theme_set_file(theme,
+					    xmlnode_get_attrib(sub_node, "name"),
+					    xmlnode_get_attrib(sub_node, "file"));
+		xmlnode_free(sub_node);
+	}
+
+	xmlnode_free(root_node);	
+	g_dir_close(gdir);
+	g_free(filename_full);
+	g_free(data);
+	return theme;
+}
+
+/******************************************************************************
+ * GObject Stuff                                                              
+ *****************************************************************************/
+
+static void
+purple_sound_theme_loader_class_init (PurpleSoundThemeLoaderClass *klass)
+{
+	PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass);
+
+	loader_klass->purple_theme_loader_build = purple_sound_loader_build;
+}
+
+
+GType 
+purple_sound_theme_loader_get_type (void)
+{
+  static GType type = 0;
+  if (type == 0) {
+    static const GTypeInfo info = {
+      sizeof (PurpleSoundThemeLoaderClass),
+      NULL,   /* base_init */
+      NULL,   /* base_finalize */
+      (GClassInitFunc)purple_sound_theme_loader_class_init,   /* class_init */
+      NULL,   /* class_finalize */
+      NULL,   /* class_data */
+      sizeof (PurpleSoundThemeLoader),
+      0,      /* n_preallocs */
+      NULL,    /* instance_init */
+      NULL,   /* value table */
+    };
+    type = g_type_register_static (PURPLE_TYPE_THEME_LOADER,
+                                   "PurpleSoundThemeLoader",
+                                   &info, 0);
+  }
+  return type;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/sound-loader.h	Mon Jun 30 23:12:54 2008 +0000
@@ -0,0 +1,71 @@
+/**
+ * @file sound-loader.h  Purple Sound Theme Loader Class API
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _PURPLE_SOUND_THEME_LOADER_H_
+#define _PURPLE_SOUND_THEME_LOADER_H_
+
+#include <glib.h>
+#include <glib-object.h>
+#include "theme-loader.h"
+
+/**
+ * A purple sound theme loader. extends PurpleThemeLoader (theme-loader.h)
+ * This is a class designed to build sound themes
+ *
+ * PurpleSoundThemeLoader is a GObject.
+ */
+typedef struct _PurpleSoundThemeLoader        PurpleSoundThemeLoader;
+typedef struct _PurpleSoundThemeLoaderClass   PurpleSoundThemeLoaderClass;
+
+#define PURPLE_TYPE_SOUND_THEME_LOADER			  (purple_sound_theme_loader_get_type ())
+#define PURPLE_SOUND_THEME_LOADER(obj)			  (G_TYPE_CHECK_INSTANCE_CAST ((obj), PURPLE_TYPE_SOUND_THEME_LOADER, PurpleSoundThemeLoader))
+#define PURPLE_SOUND_THEME_LOADER_CLASS(klass)		  (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_SOUND_THEME_LOADER, PurpleSoundThemeLoaderClass))
+#define PURPLE_IS_SOUND_THEME_LOADER(obj)	  	  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_SOUND_THEME_LOADER))
+#define PURPLE_IS_SOUND_THEME_LOADER_CLASS(klass) 	  (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_SOUND_THEME_LOADER))
+#define PURPLE_SOUND_THEME_LOADER_GET_CLASS(obj)  	  (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_SOUND_THEME_LOADER, PurpleSoundThemeLoaderClass))
+
+struct _PurpleSoundThemeLoader
+{
+	PurpleThemeLoader parent;
+};
+
+struct _PurpleSoundThemeLoaderClass
+{
+	PurpleThemeLoaderClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Purple Theme-Loader API                                         */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType purple_sound_theme_loader_get_type(void);
+
+G_END_DECLS
+#endif /* _PURPLE_SOUND_THEME_LOADER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/sound-theme.c	Mon Jun 30 23:12:54 2008 +0000
@@ -0,0 +1,163 @@
+/*
+ * Sound Themes for LibPurple
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ *
+ */
+
+#include "sound-theme.h"
+
+#define PURPLE_SOUND_THEME_GET_PRIVATE(Gobject) \
+	((PurpleSoundThemePrivate *) ((PURPLE_SOUND_THEME(Gobject))->priv))
+
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+typedef struct {
+	/* used to store filenames of diffrent sounds */
+	GHashTable *sound_files;
+} PurpleSoundThemePrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+
+/******************************************************************************
+ * GObject Stuff                                                              
+ *****************************************************************************/
+
+static void
+purple_sound_theme_init(GTypeInstance *instance,
+			gpointer klass)
+{
+	PurpleSoundThemePrivate *priv;
+
+	(PURPLE_SOUND_THEME(instance))->priv = g_new0(PurpleSoundThemePrivate, 1);
+
+	priv = PURPLE_SOUND_THEME_GET_PRIVATE(instance);
+
+	priv->sound_files = g_hash_table_new_full (g_str_hash,
+						   g_str_equal,
+						   g_free,
+						   g_free);
+}
+
+static void 
+purple_sound_theme_finalize (GObject *obj)
+{
+	PurpleSoundThemePrivate *priv;
+
+	priv = PURPLE_SOUND_THEME_GET_PRIVATE(obj);
+	
+	g_hash_table_destroy(priv->sound_files);
+
+	parent_class->finalize (obj);
+}
+
+static void
+purple_sound_theme_class_init (PurpleSoundThemeClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+        obj_class->finalize = purple_sound_theme_finalize;
+}
+
+GType 
+purple_sound_theme_get_type (void)
+{
+  static GType type = 0;
+  if (type == 0) {
+    static const GTypeInfo info = {
+      sizeof (PurpleSoundThemeClass),
+      NULL,   /* base_init */
+      NULL,   /* base_finalize */
+      (GClassInitFunc)purple_sound_theme_class_init,   /* class_init */
+      NULL,   /* class_finalize */
+      NULL,   /* class_data */
+      sizeof (PurpleSoundTheme),
+      0,      /* n_preallocs */
+      purple_sound_theme_init,    /* instance_init */
+      NULL,   /* value table */
+    };
+    type = g_type_register_static (PURPLE_TYPE_THEME,
+                                   "PurpleSoundTheme",
+                                   &info, 0);
+  }
+  return type;
+}
+
+
+/*****************************************************************************
+ * Public API functions                                                      
+ *****************************************************************************/
+
+const gchar *
+purple_sound_theme_get_file(PurpleSoundTheme *theme,
+			    const gchar *event)
+{
+	PurpleSoundThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_SOUND_THEME(theme), NULL);
+
+	priv = PURPLE_SOUND_THEME_GET_PRIVATE(theme);
+	
+	return g_hash_table_lookup(priv->sound_files, event);
+}
+
+gchar *
+purple_sound_theme_get_file_full(PurpleSoundTheme *theme,
+				 const gchar *event)
+{
+	gchar *filename;
+
+	g_return_val_if_fail(PURPLE_IS_SOUND_THEME(theme), NULL);
+
+	filename = purple_sound_theme_get_file(theme, event);
+	
+	g_return_val_if_fail(filename, NULL);
+
+	return g_build_filename(purple_theme_get_dir(PURPLE_THEME(theme)), filename, NULL);
+}
+
+void 
+purple_sound_theme_set_file(PurpleSoundTheme *theme,
+			    const gchar *event, 
+			    const gchar *filename)
+{
+	PurpleSoundThemePrivate *priv;
+	g_return_if_fail(PURPLE_IS_SOUND_THEME(theme));
+	
+	priv = PURPLE_SOUND_THEME_GET_PRIVATE(theme);
+
+	if (filename != NULL)g_hash_table_replace(priv->sound_files,
+                 	             g_strdup(event),
+                        	     g_strdup(filename));
+	else g_hash_table_remove(priv->sound_files, event);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/sound-theme.h	Mon Jun 30 23:12:54 2008 +0000
@@ -0,0 +1,102 @@
+/**
+ * @file sound-theme.h  Purple Sound Theme Abstact Class API
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _PURPLE_SOUND_THEME_H_
+#define _PURPLE_SOUND_THEME_H_
+
+#include <glib.h>
+#include <glib-object.h>
+#include "theme.h"
+#include "sound.h"
+
+/**
+ * extends PurpleTheme (theme.h)
+ * A purple sound theme.
+ * This is an object for Purple to represent a sound theme.
+ *
+ * PurpleSoundTheme is a PurpleTheme Object.
+ */
+typedef struct _PurpleSoundTheme        PurpleSoundTheme;
+typedef struct _PurpleSoundThemeClass   PurpleSoundThemeClass;
+
+#define PURPLE_TYPE_SOUND_THEME		  	(purple_sound_theme_get_type ())
+#define PURPLE_SOUND_THEME(obj)		  	(G_TYPE_CHECK_INSTANCE_CAST ((obj), PURPLE_TYPE_SOUND_THEME, PurpleSoundTheme))
+#define PURPLE_SOUND_THEME_CLASS(klass)	  	(G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_SOUND_THEME, PurpleSoundThemeClass))
+#define PURPLE_IS_SOUND_THEME(obj)	  	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_SOUND_THEME))
+#define PURPLE_IS_SOUND_THEME_CLASS(klass) 	(G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_SOUND_THEME))
+#define PURPLE_SOUND_THEME_GET_CLASS(obj)  	(G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_SOUND_THEME, PurpleSoundThemeClass))
+
+struct _PurpleSoundTheme
+{
+	PurpleTheme parent;
+	gpointer priv;
+};
+
+struct _PurpleSoundThemeClass
+{
+	PurpleThemeClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Purple Sound Theme API                                          */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType purple_sound_theme_get_type(void);
+
+/**
+ * Returns a copy of the filename for the sound event
+ *
+ * @param event		the purple sound event to look up
+ *
+ * @returns the filename of the sound event
+ */
+const gchar *purple_sound_theme_get_file(PurpleSoundTheme *theme,
+				   const gchar *event);
+/**
+ * Returns a copy of the directory and filename for the sound event
+ *
+ * @param event		the purple sound event to look up
+ *
+ * @returns the directory + '/' + filename of the sound event
+ */
+gchar *purple_sound_theme_get_file_full(PurpleSoundTheme *theme,
+					const gchar *event);
+/**
+ * Sets the filename for a given sound event
+ *
+ * @param event		the purple sound event to look up
+ * @param filename		the name of the file to be used for the event
+ */
+void purple_sound_theme_set_file(PurpleSoundTheme *theme,
+				const gchar *event, 
+			    	const gchar *filename);
+
+G_END_DECLS
+#endif /* _PURPLE_SOUND_THEME_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme-loader.c	Mon Jun 30 23:12:54 2008 +0000
@@ -0,0 +1,186 @@
+/*
+ * ThemeLoaders for LibPurple
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ *
+ */
+
+#include "theme-loader.h"
+
+#define PURPLE_THEME_LOADER_GET_PRIVATE(PurpleThemeLoader) \
+	((PurpleThemeLoaderPrivate *) ((PurpleThemeLoader)->priv))
+
+void purple_theme_loader_set_type_string(PurpleThemeLoader *loader, const gchar *type);
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+typedef struct {
+	gchar *type;
+} PurpleThemeLoaderPrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+
+enum {
+	PROP_ZERO = 0,
+	PROP_TYPE,
+};
+
+/******************************************************************************
+ * GObject Stuff                                                              *
+ *****************************************************************************/
+
+static void
+purple_theme_loader_get_property(GObject *obj, guint param_id, GValue *value,
+						 GParamSpec *psec)
+{
+	PurpleThemeLoader *theme_loader = PURPLE_THEME_LOADER(obj);
+
+	switch(param_id) {
+		case PROP_TYPE:
+			g_value_set_string(value, purple_theme_loader_get_type_string(theme_loader));
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+purple_theme_loader_set_property(GObject *obj, guint param_id, const GValue *value,
+						 GParamSpec *psec)
+{
+	PurpleThemeLoader *loader = PURPLE_THEME_LOADER(obj);
+
+	switch(param_id) {
+		case PROP_TYPE:
+			purple_theme_loader_set_type_string(loader, g_value_get_string(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+purple_theme_loader_init(GTypeInstance *instance,
+			gpointer klass)
+{
+	PurpleThemeLoader *loader = PURPLE_THEME_LOADER(instance);
+	loader->priv = g_new0(PurpleThemeLoaderPrivate, 1);
+}
+
+static void
+purple_theme_loader_finalize(GObject *obj)
+{
+	PurpleThemeLoader *loader = PURPLE_THEME_LOADER(obj);	
+	PurpleThemeLoaderPrivate *priv = PURPLE_THEME_LOADER_GET_PRIVATE(loader);
+
+	g_free(priv->type);
+
+	parent_class->finalize (obj);
+}
+
+static void
+purple_theme_loader_class_init (PurpleThemeLoaderClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GParamSpec *pspec;
+	
+	parent_class = g_type_class_peek_parent (klass);
+
+	obj_class->get_property = purple_theme_loader_get_property;
+	obj_class->set_property = purple_theme_loader_set_property;
+	obj_class->finalize = purple_theme_loader_finalize;
+
+	/* TYPE STRING (read only) */
+	pspec = g_param_spec_string("type", "Type",
+				    "The string represtenting the type of the theme",
+				    NULL,
+				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+	g_object_class_install_property(obj_class, PROP_TYPE, pspec);
+}
+
+
+GType 
+purple_theme_loader_get_type (void)
+{
+  static GType type = 0;
+  if (type == 0) {
+    static const GTypeInfo info = {
+      sizeof (PurpleThemeLoaderClass),
+      NULL,   /* base_init */
+      NULL,   /* base_finalize */
+      (GClassInitFunc)purple_theme_loader_class_init,   /* class_init */
+      NULL,   /* class_finalize */
+      NULL,   /* class_data */
+      sizeof (PurpleThemeLoader),
+      0,      /* n_preallocs */
+      purple_theme_loader_init,    /* instance_init */
+      NULL,   /* value table */
+    };
+    type = g_type_register_static (G_TYPE_OBJECT,
+                                   "PurpleThemeLoader",
+                                   &info, G_TYPE_FLAG_ABSTRACT);
+  }
+  return type;
+}
+
+
+/*****************************************************************************
+ * Public API functions                                                      
+ *****************************************************************************/
+
+
+const gchar *
+purple_theme_loader_get_type_string (PurpleThemeLoader *theme_loader)
+{
+	PurpleThemeLoaderPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_THEME_LOADER(theme_loader), NULL);
+
+	priv = PURPLE_THEME_LOADER_GET_PRIVATE(theme_loader);
+	return priv->type;
+}
+
+/* < private > */
+void
+purple_theme_loader_set_type_string(PurpleThemeLoader *loader, const gchar *type)
+{
+	PurpleThemeLoaderPrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME_LOADER(loader));
+
+	priv = PURPLE_THEME_LOADER_GET_PRIVATE(loader);
+
+	g_free(priv->type);
+	priv->type = g_strdup(type);
+}
+
+gpointer
+purple_theme_loader_build (PurpleThemeLoader *loader, const gchar *dir)
+{
+	return PURPLE_THEME_LOADER_GET_CLASS(loader)->purple_theme_loader_build(dir);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme-loader.h	Mon Jun 30 23:12:54 2008 +0000
@@ -0,0 +1,92 @@
+/**
+ * @file theme-loader.h  Purple Theme Loader Abstact Class API
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _PURPLE_THEME_LOADER_H_
+#define _PURPLE_THEME_LOADER_H_
+
+#include <glib.h>
+#include <glib-object.h>
+#include "theme.h"
+
+/**
+ * A purple theme loader.
+ * This is an abstract class for Purple to use with the Purple theme manager.
+ * The loader is responsible for building each type of theme
+ *
+ * PurpleThemeLoader is a GObject.
+ */
+typedef struct _PurpleThemeLoader        PurpleThemeLoader;
+typedef struct _PurpleThemeLoaderClass   PurpleThemeLoaderClass;
+
+#define PURPLE_TYPE_THEME_LOADER		  (purple_theme_loader_get_type ())
+#define PURPLE_THEME_LOADER(obj)	 	  (G_TYPE_CHECK_INSTANCE_CAST ((obj), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoader))
+#define PURPLE_THEME_LOADER_CLASS(klass)	  (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoaderClass))
+#define PURPLE_IS_THEME_LOADER(obj)	  	  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_THEME_LOADER))
+#define PURPLE_IS_THEME_LOADER_CLASS(klass) 	  (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_THEME_LOADER))
+#define PURPLE_THEME_LOADER_GET_CLASS(obj)  	  (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoaderClass))
+
+struct _PurpleThemeLoader
+{
+	GObject parent;
+	gpointer priv;
+};
+
+struct _PurpleThemeLoaderClass
+{
+	GObjectClass parent_class;
+	gpointer (*purple_theme_loader_build)(const gchar*);
+};
+
+/**************************************************************************/
+/** @name Purple Theme-Loader API                                         */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType purple_theme_loader_get_type(void);
+
+/**
+ * Returns the string represtenting the type of the theme loader
+ *
+ * @param self 		the theme loader
+ *
+ * @returns 		the string represting this type
+ */
+const gchar *purple_theme_loader_get_type_string(PurpleThemeLoader *self);
+
+/**
+ * Creates a new PurpleTheme
+ *
+ * @param dir 		the directory containing the theme
+ *
+ * @returns 		PurpleTheme containing the information from the directory
+ */
+gpointer purple_theme_loader_build(PurpleThemeLoader *loader, const gchar *dir);
+
+G_END_DECLS
+#endif /* _PURPLE_THEME_LOADER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme-manager.c	Mon Jun 30 23:12:54 2008 +0000
@@ -0,0 +1,284 @@
+/*
+ * Themes for LibPurple
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ *
+ */
+
+#include <stdarg.h>
+#include "theme-manager.h"
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GHashTable *theme_table = NULL;
+
+/*****************************************************************************
+ * GObject Stuff                                                     
+ ****************************************************************************/
+
+GType 
+purple_theme_manager_get_type (void)
+{
+  static GType type = 0;
+  if (type == 0) {
+    static const GTypeInfo info = {
+      sizeof (PurpleThemeManagerClass),
+      NULL,   /* base_init */
+      NULL,   /* base_finalize */
+      NULL,   /* class_init */
+      NULL,   /* class_finalize */
+      NULL,   /* class_data */
+      sizeof (PurpleThemeManager),
+      0,      /* n_preallocs */
+      NULL,    /* instance_init */
+      NULL,   /* Value Table */
+    };
+    type = g_type_register_static (G_TYPE_OBJECT,
+                                   "PurpleThemeManager",
+                                   &info, 0);
+  }
+  return type;
+}
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+/* makes a key of <type> + '/' + <name> */
+static gchar *
+purple_theme_manager_make_key(const gchar *name, const gchar *type)
+{
+	g_return_val_if_fail(name, NULL);
+	g_return_val_if_fail(type, NULL);		
+	return g_strconcat(type, "/", name, NULL);
+}
+
+/* returns TRUE if theme is of type "user_data" */ 
+static gboolean
+purple_theme_manager_is_theme_type(gchar *key,
+                  gpointer value,
+                  gchar *user_data)
+{
+	return g_str_has_prefix(key, g_strconcat(user_data, "/", NULL));
+}
+
+static gboolean
+purple_theme_manager_is_theme(gchar *key,
+                  gpointer value,
+                  gchar *user_data)
+{
+	return PURPLE_IS_THEME(value);
+}
+
+static void
+purple_theme_manager_function_wrapper(gchar *key,
+                  		      gpointer value,
+                		      PTFunc user_data)
+{
+	if (PURPLE_IS_THEME(value))
+		(* user_data) (value);
+}
+
+static void
+purple_theme_manager_build_dir(const gchar *root)
+{
+
+	GDir *rdir;
+	gchar *name, *type, *purple_dir, *theme_dir;
+	GDir *dir;
+	PurpleThemeLoader *loader;
+
+	rdir =  g_dir_open(root, 0, NULL);
+
+	g_return_if_fail(rdir);
+
+	/* Parses directory by root/name/purple/type */
+	while ((name = g_strdup(g_dir_read_name (rdir)))){
+		
+		purple_dir = g_build_filename(root, name, "purple", NULL);
+		dir =  g_dir_open(purple_dir, 0, NULL);	
+	
+		if (dir) {
+			while ((type = g_strdup(g_dir_read_name (dir)))) {
+				if ((loader = g_hash_table_lookup (theme_table, type))){
+
+					theme_dir = g_build_filename(purple_dir, type, NULL);
+					purple_theme_manager_add_theme(purple_theme_loader_build(loader, theme_dir));
+
+				}
+				g_free(type);
+
+			}
+			g_dir_close(dir);
+
+		}
+		g_free(purple_dir);
+		g_free(name);	
+	
+	}
+	g_dir_close(rdir);
+	
+}
+
+/*****************************************************************************
+ * Public API functions                                                      *
+ *****************************************************************************/
+
+void
+purple_theme_manager_init (PurpleThemeLoader *loader1, ...)
+{
+	va_list args;
+	PurpleThemeLoader *loader;
+
+	/*g_return_if_fail(theme_table != NULL);*/
+
+	theme_table = g_hash_table_new_full (g_str_hash,
+               	                             g_str_equal,
+               	                             g_free,
+               	                             g_object_unref);
+
+	va_start(args, loader1);
+	for (loader = loader1; loader != NULL; loader = va_arg(args, PurpleThemeLoader *))
+		purple_theme_manager_register_type(loader);
+	va_end(args);
+
+	/* TODO: add themes properly */
+	purple_theme_manager_build_dir("/usr/share/themes");
+}
+
+void 
+purple_theme_manager_refresh()
+{
+	g_hash_table_foreach_remove (theme_table,
+                	             (GHRFunc) purple_theme_manager_is_theme,
+                	             NULL);	
+	
+	/* TODO: this also needs to be fixed the same as new */
+	purple_theme_manager_build_dir("/usr/share/themes");
+
+}
+
+void 
+purple_theme_manager_uninit ()
+{
+	g_hash_table_destroy(theme_table);
+}
+
+
+void
+purple_theme_manager_register_type(PurpleThemeLoader *loader)
+{
+	gchar *type;
+
+	g_return_if_fail(PURPLE_IS_THEME_LOADER(loader));
+
+	type = g_strdup(purple_theme_loader_get_type_string(loader));
+	g_return_if_fail(type);
+
+	/* if something is already there do nothing */
+	if (! g_hash_table_lookup (theme_table, type)) 
+		g_hash_table_insert(theme_table, type, loader);
+}
+
+void
+purple_theme_manager_unregister_type(PurpleThemeLoader *loader)
+{
+	gchar *type;
+
+	g_return_if_fail(PURPLE_IS_THEME_LOADER(loader));
+
+	type = purple_theme_loader_get_type_string(loader);
+	g_return_if_fail(type);
+
+	if (g_hash_table_lookup (theme_table, type) == loader){
+
+		g_hash_table_remove (theme_table, type);
+
+		g_hash_table_foreach_remove (theme_table,
+                	                     (GHRFunc) purple_theme_manager_is_theme_type,
+                	                     type);		
+	}/* only free if given registered loader */
+
+	g_free(type);
+}
+
+PurpleTheme *
+purple_theme_manager_find_theme(const gchar *name,
+				const gchar *type)
+{
+	gchar *key;
+	PurpleTheme *theme;
+
+	key = purple_theme_manager_make_key(name, type);
+
+	g_return_val_if_fail(key, NULL);
+
+	theme = g_hash_table_lookup(theme_table, key);
+
+	g_free(key);
+
+	return theme;
+}
+
+
+void 
+purple_theme_manager_add_theme(PurpleTheme *theme)
+{
+	gchar *key;
+	
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	key = purple_theme_manager_make_key(purple_theme_get_name(theme),
+			  		    purple_theme_get_type_string(theme));
+
+	g_return_if_fail(key);
+
+	/* if something is already there do nothing */
+	if (g_hash_table_lookup(theme_table, key) == NULL) 
+		g_hash_table_insert(theme_table, key, theme);
+}
+
+void
+purple_theme_manager_remove_theme(PurpleTheme *theme)
+{
+	gchar *key;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	key = purple_theme_manager_make_key(purple_theme_get_name(theme),
+				  	    purple_theme_get_type_string(theme));
+
+	g_return_if_fail(key);
+
+	g_hash_table_remove(theme_table, key);	
+
+	g_free(key);	
+}
+
+void 
+purple_theme_manager_for_each_theme(PTFunc func)
+{
+	g_return_if_fail(func);
+
+	g_hash_table_foreach(theme_table,
+			     (GHFunc) purple_theme_manager_function_wrapper,
+			     func);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme-manager.h	Mon Jun 30 23:12:54 2008 +0000
@@ -0,0 +1,127 @@
+/**
+ * @file thememanager.h  Theme Manager API
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef __PURPLE_THEME_MANAGER_H__
+#define __PURPLE_THEME_MANAGER_H__
+
+#include <glib-object.h>
+#include <glib.h>
+#include "theme.h"
+#include "theme-loader.h"
+
+typedef void (*PTFunc) (PurpleTheme *);
+
+typedef struct _PurpleThemeManager PurpleThemeManager;
+typedef struct _PurpleThemeManagerClass PurpleThemeManagerClass;
+
+#define PURPLE_TYPE_THEME_MANAGER            (purple_theme_manager_get_type ())
+#define PURPLE_THEME_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_THEME_MANAGER, PurpleThemeManager))
+#define PURPLE_THEME_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_THEME_MANAGER, PurpleThemeManagerClass))
+#define PURPLE_IS_THEME_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_THEME_MANAGER))
+#define PURPLE_IS_THEME_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_THEME_MANAGER))
+#define PURPLE_GET_THEME_MANAGER_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_THEME_MANAGER, PurpleThemeManagerClass))
+
+struct _PurpleThemeManager {
+	GObject parent;
+};
+
+struct _PurpleThemeManagerClass {
+	GObjectClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Purple Theme Manager API                                        */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType purple_theme_manager_get_type (void);
+
+/**
+ * Initalizes the manager then add the loaders to the theme manager 
+ * and builds with the given loaders 
+ * @param 	the loaders to build with 
+ */
+void purple_theme_manager_init (PurpleThemeLoader *loader1, ...);
+
+/**
+ * Rebuilds all the themes in the theme manager
+ * (removes all current themes but keeps the added loaders)
+ */
+void purple_theme_manager_refresh(void);
+
+/**
+ * Uninitalizes the manager then frees all the themes an loaders it is responsible for 
+ */
+void purple_theme_manager_uninit (void);
+
+/**
+ * Finds the PurpleTheme object stored by the theme manager
+ * 
+ * @param name		the name of the PurpleTheme
+ * @param type 		the type of the PurpleTheme
+ *
+ * @returns 	The PurpleTheme or NULL if it wasn't found
+ */
+PurpleTheme *purple_theme_manager_find_theme(const gchar *name, const gchar *type);
+
+/**
+ * Adds a PurpleTheme to the theme manager, if the theme already exits it does nothing
+ *
+ * @param theme 	the PurpleTheme to add to the manager
+ */
+void purple_theme_manager_add_theme(PurpleTheme *theme);
+
+/**
+ * Removes a PurpleTheme from the theme manager, and frees the theme
+ * @param theme 	the PurpleTheme to remove from the manager
+ */
+void purple_theme_manager_remove_theme(PurpleTheme *theme);
+
+/**
+ * Addes a Loader to the theme manager so it knows how to build themes
+ * @param loader 	the PurpleThemeLoader to add
+ */
+void purple_theme_manager_register_type(PurpleThemeLoader *loader);
+
+/**
+ * Removes the loader and all themes of the same type from the loader
+ * @param loader 	the PurpleThemeLoader to be removed
+ */
+void purple_theme_manager_unregister_type(PurpleThemeLoader *loader);
+
+/**
+ * Calles the given function on each purple theme
+ *
+ * @param func 		the PTFunc to be applied to each theme
+ */
+void purple_theme_manager_for_each_theme(PTFunc func); 
+
+G_END_DECLS
+#endif /* __PURPLE_THEME_MANAGER_H__ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme.c	Mon Jun 30 23:12:54 2008 +0000
@@ -0,0 +1,387 @@
+/*
+ * Themes for LibPurple
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ *
+ */
+
+#include "theme.h"
+
+#define PURPLE_THEME_GET_PRIVATE(PurpleTheme) \
+	((PurpleThemePrivate *) ((PurpleTheme)->priv))
+
+void purple_theme_set_type_string(PurpleTheme *theme, const gchar *type);
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+typedef struct {
+	gchar *name;
+	gchar *description;
+	gchar *author;
+	gchar *type;
+	gchar *dir;
+	gchar *img;
+} PurpleThemePrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+
+enum {
+	PROP_ZERO = 0,
+	PROP_NAME,
+	PROP_DESCRIPION,
+	PROP_AUTHOR,
+	PROP_TYPE,
+	PROP_DIR,
+	PROP_IMAGE
+};
+
+/******************************************************************************
+ * GObject Stuff                                                              *
+ *****************************************************************************/
+
+static void
+purple_theme_get_property(GObject *obj, guint param_id, GValue *value,
+						 GParamSpec *psec)
+{
+	PurpleTheme *theme = PURPLE_THEME(obj);
+
+	switch(param_id) {
+		case PROP_NAME:
+			g_value_set_string(value, purple_theme_get_name(theme));
+			break;
+		case PROP_DESCRIPION:
+			g_value_set_string(value, purple_theme_get_description(theme));
+			break;
+		case PROP_AUTHOR:
+			g_value_set_string(value, purple_theme_get_author(theme));
+			break;
+		case PROP_TYPE:
+			g_value_set_string(value, purple_theme_get_type_string(theme));
+			break;
+		case PROP_DIR:
+			g_value_set_string(value, purple_theme_get_dir(theme));
+			break;
+		case PROP_IMAGE:
+			g_value_set_string(value, purple_theme_get_image(theme));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+purple_theme_set_property(GObject *obj, guint param_id, const GValue *value,
+						 GParamSpec *psec)
+{
+	PurpleTheme *theme = PURPLE_THEME(obj);
+
+	switch(param_id) {
+		case PROP_NAME:
+			purple_theme_set_name(theme, g_value_get_string(value));
+			break;
+		case PROP_DESCRIPION:
+			purple_theme_set_description(theme, g_value_get_string(value));
+			break;
+		case PROP_AUTHOR:
+			purple_theme_set_author(theme, g_value_get_string(value));
+			break;
+		case PROP_TYPE:
+			purple_theme_set_type_string(theme, g_value_get_string(value));
+			break;
+		case PROP_DIR:
+			purple_theme_set_dir(theme, g_value_get_string(value));
+			break;
+		case PROP_IMAGE:
+			purple_theme_set_image(theme, g_value_get_string(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+purple_theme_init(GTypeInstance *instance,
+			gpointer klass)
+{
+	PurpleTheme *theme = PURPLE_THEME(instance);
+	theme->priv = g_new0(PurpleThemePrivate, 1);
+}
+
+static void
+purple_theme_finalize(GObject *obj)
+{
+	PurpleTheme *theme = PURPLE_THEME(obj);	
+	PurpleThemePrivate *priv = PURPLE_THEME_GET_PRIVATE(theme);
+	
+	g_free(priv->name);
+	g_free(priv->description);
+	g_free(priv->author);
+	g_free(priv->type);
+	g_free(priv->dir);
+	g_free(priv->img);
+
+	G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+purple_theme_class_init (PurpleThemeClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GParamSpec *pspec;
+
+	parent_class = g_type_class_peek_parent(klass);
+
+	obj_class->get_property = purple_theme_get_property;
+	obj_class->set_property = purple_theme_set_property;
+	obj_class->finalize = purple_theme_finalize;
+	
+	/* NAME */
+	pspec = g_param_spec_string("name", "Name",
+				    "The name of the theme",
+				    NULL,
+				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property(obj_class, PROP_NAME, pspec);
+	/* DESCRIPION */
+	pspec = g_param_spec_string("description", "Description",
+				    "The description of the theme",
+				    NULL,
+				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property(obj_class, PROP_DESCRIPION, pspec);
+	/* AUTHOR */
+	pspec = g_param_spec_string("author", "Author",
+				    "The author of the theme",
+				    NULL,
+				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property(obj_class, PROP_AUTHOR, pspec);
+	/* TYPE STRING (read only) */
+	pspec = g_param_spec_string("type", "Type",
+				    "The string represtenting the type of the theme",
+				    NULL,
+				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+	g_object_class_install_property(obj_class, PROP_TYPE, pspec);
+	/* DIRECTORY */
+	pspec = g_param_spec_string("directory", "Directory",
+				    "The directory that contains the theme and all its files",
+				    NULL,
+				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property(obj_class, PROP_DIR, pspec);
+	/* PREVIEW IMAGE */
+	pspec = g_param_spec_string("image", "Image",
+				    "A preview image of the theme",
+				    NULL,
+				    G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_IMAGE, pspec);
+}
+
+
+GType 
+purple_theme_get_type (void)
+{
+  static GType type = 0;
+  if (type == 0) {
+    static const GTypeInfo info = {
+      sizeof (PurpleThemeClass),
+      NULL,   /* base_init */
+      NULL,   /* base_finalize */
+      (GClassInitFunc)purple_theme_class_init,   /* class_init */
+      NULL,   /* class_finalize */
+      NULL,   /* class_data */
+      sizeof (PurpleTheme),
+      0,      /* n_preallocs */
+      purple_theme_init,    /* instance_init */
+      NULL,   /* value table */
+    };
+    type = g_type_register_static (G_TYPE_OBJECT,
+                                   "PurpleTheme",
+                                   &info, G_TYPE_FLAG_ABSTRACT);
+  }
+  return type;
+}
+
+
+/*****************************************************************************
+ * Public API functions                                                      *
+ *****************************************************************************/
+
+const gchar *
+purple_theme_get_name(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+	return priv->name;
+}
+
+void
+purple_theme_set_name(PurpleTheme *theme, const gchar *name)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->name);
+	priv->name = g_strdup(name);
+}
+
+const gchar *
+purple_theme_get_description(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+	return priv->description;
+}
+
+void
+purple_theme_set_description(PurpleTheme *theme, const gchar *description)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->description);
+	priv->description = g_strdup(description);
+}
+
+const gchar *
+purple_theme_get_author(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+	return priv->author;
+}
+
+void
+purple_theme_set_author(PurpleTheme *theme, const gchar *author)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->author);
+	priv->author = g_strdup(author);
+}
+
+const gchar *
+purple_theme_get_type_string(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+	return priv->type;
+}
+
+/* < private > */
+void
+purple_theme_set_type_string(PurpleTheme *theme, const gchar *type)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->type);
+	priv->type = g_strdup(type);
+}
+
+const gchar *
+purple_theme_get_dir(PurpleTheme *theme) 
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+	return priv->dir;
+}
+
+void
+purple_theme_set_dir(PurpleTheme *theme, const gchar *dir)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->dir);
+	priv->dir = g_strdup(dir);
+}
+
+const gchar *
+purple_theme_get_image(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	return priv->img;
+}
+
+gchar *
+purple_theme_get_image_full(PurpleTheme *theme)
+{
+	const gchar *filename = purple_theme_get_image(theme);
+	
+	g_return_val_if_fail(filename, NULL);
+
+	return g_build_filename(purple_theme_get_dir(PURPLE_THEME(theme)), filename, NULL);
+}
+
+void 
+purple_theme_set_image(PurpleTheme *theme, const gchar *img)
+{	
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->img);
+	priv->img = g_strdup(img);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme.h	Mon Jun 30 23:12:54 2008 +0000
@@ -0,0 +1,175 @@
+/**
+ * @file theme.h  Purple Theme Abstact Class API
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _PURPLE_THEME_H_
+#define _PURPLE_THEME_H_
+
+#include <glib.h>
+#include <glib-object.h>
+#include "imgstore.h"
+
+/**
+ * A purple theme.
+ * This is an abstract class for Purple to use with the Purple theme manager.
+ *
+ * PurpleTheme is a GObject.
+ */
+typedef struct _PurpleTheme        PurpleTheme;
+typedef struct _PurpleThemeClass   PurpleThemeClass;
+
+#define PURPLE_TYPE_THEME		  (purple_theme_get_type ())
+#define PURPLE_THEME(obj)		  (G_TYPE_CHECK_INSTANCE_CAST ((obj), PURPLE_TYPE_THEME, PurpleTheme))
+#define PURPLE_THEME_CLASS(klass)	  (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_THEME, PurpleThemeClass))
+#define PURPLE_IS_THEME(obj)	  	  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_THEME))
+#define PURPLE_IS_THEME_CLASS(klass) 	  (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_THEME))
+#define PURPLE_THEME_GET_CLASS(obj)  	  (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_THEME, PurpleThemeClass))
+
+struct _PurpleTheme
+{
+	GObject parent;
+	gpointer priv;
+};
+
+struct _PurpleThemeClass
+{
+	GObjectClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Purple Theme API                                                */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType purple_theme_get_type(void);
+
+/**
+ * Returns the name of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ *
+ * @return The string representating the name of the theme
+ */
+const gchar *purple_theme_get_name(PurpleTheme *theme);
+
+/**
+ * Sets the name of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ * @param name 		the name of the PurpleTheme object
+ */
+void purple_theme_set_name(PurpleTheme *theme, const gchar *name);
+
+/**
+ * Returns the description of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ *
+ * @return A short description of the theme
+ */
+const gchar *purple_theme_get_description(PurpleTheme *theme);
+
+/**
+ * Sets the description of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ * @param description	the description of the PurpleTheme object
+ */
+void purple_theme_set_description(PurpleTheme *theme, const gchar *description);
+
+/**
+ * Returns the author of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ *
+ * @return The author of the theme
+ */
+const gchar *purple_theme_get_author(PurpleTheme *theme);
+
+/**
+ * Sets the author of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ * @param author	the author of the PurpleTheme object
+ */
+void purple_theme_set_author(PurpleTheme *theme, const gchar *author);
+
+/**
+ * Returns the type (string) of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ *
+ * @return The string represtenting the type
+ */
+const gchar *purple_theme_get_type_string(PurpleTheme *theme);
+
+/**
+ * Returns the directory of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ *
+ * @return The string represtenting the theme directory 
+ */
+const gchar *purple_theme_get_dir(PurpleTheme *theme);
+
+/**
+ * Sets the directory of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ * @param dir		the directory of the PurpleTheme object
+ */
+void purple_theme_set_dir(PurpleTheme *theme, const gchar *dir);
+
+/**
+ * Returns the image preview of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ *
+ * @return The image preview of the PurpleTheme object
+ */
+const gchar *purple_theme_get_image(PurpleTheme *theme);
+
+/**
+ * Returns the image preview and directory of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ *
+ * @return The image preview of the PurpleTheme object
+ */
+gchar *purple_theme_get_image_full(PurpleTheme *theme);
+
+/**
+ * Sets the directory of the PurpleTheme object
+ * 
+ * @param theme 	the purple theme
+ * @param img		the image preview of the PurpleTheme object
+ */
+void purple_theme_set_image(PurpleTheme *theme, const gchar *img);
+
+G_END_DECLS
+#endif /* _PURPLE_THEME_H_ */
--- a/libpurple/util.c	Mon Jun 30 22:58:18 2008 +0000
+++ b/libpurple/util.c	Mon Jun 30 23:12:54 2008 +0000
@@ -2757,70 +2757,7 @@
 xmlnode *
 purple_util_read_xml_from_file(const char *filename, const char *description)
 {
-	const char *user_dir = purple_user_dir();
-	gchar *filename_full;
-	GError *error = NULL;
-	gchar *contents = NULL;
-	gsize length;
-	xmlnode *node = NULL;
-
-	g_return_val_if_fail(user_dir != NULL, NULL);
-
-	purple_debug_info("util", "Reading file %s from directory %s\n",
-					filename, user_dir);
-
-	filename_full = g_build_filename(user_dir, filename, NULL);
-
-	if (!g_file_test(filename_full, G_FILE_TEST_EXISTS))
-	{
-		purple_debug_info("util", "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("util", "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 = g_strdup_printf("%s~", filename);
-			purple_debug_error("util", "Error parsing file %s.  Renaming old "
-							 "file to %s\n", filename_full, filename_temp);
-			purple_util_write_data_to_file(filename_temp, contents, length);
-			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.  They have 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;
+	return xmlnode_from_file(purple_user_dir(), filename, description, "util");
 }
 
 /*
--- a/libpurple/xmlnode.c	Mon Jun 30 22:58:18 2008 +0000
+++ b/libpurple/xmlnode.c	Mon Jun 30 23:12:54 2008 +0000
@@ -729,6 +729,78 @@
 }
 
 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;
+}
+
+xmlnode *
 xmlnode_copy(const xmlnode *src)
 {
 	xmlnode *ret;
--- a/libpurple/xmlnode.h	Mon Jun 30 22:58:18 2008 +0000
+++ b/libpurple/xmlnode.h	Mon Jun 30 23:12:54 2008 +0000
@@ -297,6 +297,20 @@
  */
 void xmlnode_free(xmlnode *node);
 
+/**
+ * Creates a node from a XML File.  Calling this on the
+ * root node of an XML document will parse the entire document
+ * into a tree of nodes, and return the xmlnode of the root.
+ *
+ * @param str  The string of xml.
+ * @param description  The description of the file being parsed
+ * @process  The utility that is calling xmlnode_from_file
+ *
+ * @return The new node.
+ */
+xmlnode *xmlnode_from_file(const char *dir, const char *filename, 
+			   const char *description, const char *process);
+
 #ifdef __cplusplus
 }
 #endif
--- a/pidgin/gtkprefs.c	Mon Jun 30 22:58:18 2008 +0000
+++ b/pidgin/gtkprefs.c	Mon Jun 30 23:12:54 2008 +0000
@@ -35,6 +35,8 @@
 #include "request.h"
 #include "savedstatuses.h"
 #include "sound.h"
+#include "sound-theme.h"
+#include "theme-manager.h"
 #include "util.h"
 #include "network.h"
 
@@ -69,6 +71,10 @@
 static int notebook_page = 0;
 static GtkTreeRowReference *previous_smiley_row = NULL;
 
+static gboolean prefs_themes_unsorted = TRUE;
+static GtkListStore *prefs_sound_themes;
+static GtkTreeIter prefs_sound_iter;
+
 /*
  * PROTOTYPES
  */
@@ -546,6 +552,101 @@
 	gtk_drag_finish(dc, FALSE, FALSE, t);
 }
 
+/* sets the current sound theme to customized or NULL */
+static void
+pref_set_sound_customized()
+{
+	if (pidgin_sound_is_customized())
+		gtk_list_store_set(prefs_sound_themes, &prefs_sound_iter, 2, "(Custom)", -1);
+	else gtk_list_store_set(prefs_sound_themes, &prefs_sound_iter, 2, NULL, -1);
+}
+
+/* adds the themes to the theme list from the manager so they can be sisplayed in prefs */
+static void
+prefs_themes_sort(PurpleTheme *theme)
+{
+	GdkPixbuf *pixbuf = NULL;
+	GtkTreeIter iter;
+	gchar *image_full;
+	
+	if (PURPLE_IS_SOUND_THEME(theme)){
+		/* TODO: string leaks? */
+		const gchar *pref = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme");
+		
+		image_full = purple_theme_get_image_full(theme);
+		if (image_full != NULL){
+			pixbuf = gdk_pixbuf_new_from_file(image_full, NULL);
+			g_free(image_full);
+		}
+		else pixbuf = NULL; 
+
+		gtk_list_store_append (prefs_sound_themes, &iter);
+		gtk_list_store_set (prefs_sound_themes, &iter, 0, pixbuf, 1, purple_theme_get_name(theme), 2, NULL, -1);
+
+		if (pixbuf != NULL)
+			gdk_pixbuf_unref(pixbuf);
+
+		if (pref && strlen(pref) && !strcmp(purple_theme_get_name(theme), pref))
+			prefs_sound_iter = iter;
+
+	}
+}
+
+/* init all the theme variables so that the themes can be sorted later and used by pref pages */
+static void
+prefs_themes_init(void)
+{
+	GdkPixbuf *pixbuf = NULL;
+	gchar *filename;
+
+	/* sound themes */
+	prefs_sound_themes = gtk_list_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+
+	filename = g_build_filename(DATADIR, "icons", "hicolor", "16x16", "apps", "pidgin.png", NULL);
+	pixbuf= gdk_pixbuf_new_from_file(filename, NULL);
+	g_free(filename);
+
+	gtk_list_store_append (prefs_sound_themes, &prefs_sound_iter);
+
+	gtk_list_store_set (prefs_sound_themes, &prefs_sound_iter, 0, pixbuf, 1, _("(Default)"), 2, NULL, -1);
+
+	gdk_pixbuf_unref (pixbuf);
+}
+
+/* sets the current sound theme */
+static void
+prefs_set_sound_theme(GtkComboBox *combo_box, gpointer user_data)
+{
+	gint i;
+	gchar *pref;
+	gchar *theme_name; 
+	
+	/* remove "(custom)" */
+	gtk_list_store_set(prefs_sound_themes, &prefs_sound_iter, 2, NULL, -1);
+
+	g_return_if_fail(gtk_combo_box_get_active_iter(combo_box, &prefs_sound_iter));
+
+	gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &prefs_sound_iter, 1, &theme_name, -1);
+
+	if (strcmp(theme_name, "(Default)") == 0){
+		g_free(theme_name);
+		theme_name = g_strdup("");
+	}
+
+	purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/theme", theme_name);
+
+	for(i=0; i <  PURPLE_NUM_SOUNDS; i++){
+		pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
+					pidgin_sound_get_event_option(i));
+		purple_prefs_set_path(pref, "");
+		g_free(pref);
+	}
+
+	gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
+
+	g_free(theme_name);
+}
+
 /* Does same as normal sort, except "none" is sorted first */
 static gint pidgin_sort_smileys (GtkTreeModel	*model,
 						GtkTreeIter		*a,
@@ -1736,6 +1837,8 @@
 	g_free(pref);
 
 	gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
+
+	pref_set_sound_customized();
 }
 
 static void
@@ -1758,6 +1861,8 @@
 	 */
 	if (sound == sound_row_sel)
 		gtk_entry_set_text(GTK_ENTRY(sound_entry), filename);
+
+	pref_set_sound_customized();	
 }
 
 static void select_sound(GtkWidget *button, gpointer being_NULL_is_fun)
@@ -1826,13 +1931,15 @@
 	if (sound_entry)
 		gtk_entry_set_text(GTK_ENTRY(sound_entry), (file && *file != '\0') ? file : _("(default)"));
 	g_value_unset (&val);
+
+	pref_set_sound_customized();
 }
 
 static GtkWidget *
 sound_page(void)
 {
 	GtkWidget *ret;
-	GtkWidget *vbox, *sw, *button;
+	GtkWidget *vbox, *sw, *button, *combo_box;
 	GtkSizeGroup *sg;
 	GtkTreeIter iter;
 	GtkWidget *event_view;
@@ -1845,6 +1952,7 @@
 	int j;
 	const char *file;
 	char *pref;
+	GtkCellRenderer *cell_rend;
 #ifndef _WIN32
 	GtkWidget *dd;
 	GtkWidget *entry;
@@ -1922,7 +2030,6 @@
 	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
 								sound_changed2_cb, vbox);
 #endif
-
 	vbox = pidgin_make_frame(ret, _("Sound Events"));
 
 	/* The following is an ugly hack to make the frame expand so the
@@ -1934,6 +2041,28 @@
 	gtk_box_set_child_packing(GTK_BOX(vbox->parent->parent->parent),
 			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));
+	gtk_box_pack_start (GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
+
+	cell_rend = gtk_cell_renderer_pixbuf_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, "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, "text", 1, 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, "text", 2, NULL);
+
+	gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &prefs_sound_iter);
+	g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_sound_theme, NULL);
+
+	pref_set_sound_customized();
+
+	/* SOUND SELECTION */
 	sw = gtk_scrolled_window_new(NULL,NULL);
 	gtk_widget_set_size_request(sw, -1, 100);
 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
@@ -2166,6 +2295,12 @@
 		gtk_window_present(GTK_WINDOW(prefs));
 		return;
 	}
+	
+	/* add everthing in the thmeme manager before the window is loaded */
+	if (prefs_themes_unsorted){
+		purple_theme_manager_for_each_theme(prefs_themes_sort);
+		prefs_themes_unsorted = FALSE;
+	}
 
 	/* copy the preferences to tmp values...
 	 * I liked "take affect immediately" Oh well :-( */
@@ -2261,6 +2396,9 @@
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder", "");
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_icon_folder", "");
 
+	/* Themes */
+	prefs_themes_init();
+
 	/* Smiley Themes */
 	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/smileys");
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default");
--- a/pidgin/gtksound.c	Mon Jun 30 22:58:18 2008 +0000
+++ b/pidgin/gtksound.c	Mon Jun 30 23:12:54 2008 +0000
@@ -40,6 +40,8 @@
 #include "notify.h"
 #include "prefs.h"
 #include "sound.h"
+#include "sound-theme.h"
+#include "theme-manager.h"
 #include "util.h"
 
 #include "gtkconv.h"
@@ -294,6 +296,7 @@
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/nick_said", "");
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/pounce_default", TRUE);
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/pounce_default", "");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/sound/theme", "");
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/conv_focus", TRUE);
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/mute", FALSE);
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/command", "");
@@ -557,6 +560,8 @@
 {
 	char *enable_pref;
 	char *file_pref;
+	char *theme_name;
+	PurpleSoundTheme *theme;
 
 	if ((event == PURPLE_SOUND_BUDDY_ARRIVE) && mute_login_sounds)
 		return;
@@ -570,23 +575,67 @@
 			sounds[event].pref);
 	file_pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", sounds[event].pref);
 
+
+
 	/* check NULL for sounds that don't have an option, ie buddy pounce */
 	if (purple_prefs_get_bool(enable_pref)) {
 		char *filename = g_strdup(purple_prefs_get_path(file_pref));
-		if(!filename || !strlen(filename)) {
+		theme_name = g_strdup(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"));
+		
+		if (theme_name && strlen(theme_name) && (!filename || !strlen(filename))){ /* Use theme */
 			g_free(filename);
+
+			theme = PURPLE_SOUND_THEME(purple_theme_manager_find_theme(theme_name, "sound"));
+			filename = purple_sound_theme_get_file_full(theme, sounds[event].pref);
+
+			if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)){ /* Use Default sound in this case */
+				purple_debug_error("sound", "The file: (%s) %s\n from theme: %s, was not found or wasn't readable\n", 
+							sounds[event].pref, filename, theme_name);
+				g_free(filename);
+			}
+		}
+		
+		if (!filename || !strlen(filename)) {			    /* Use Default sounds */
+			g_free(filename);
+
 			/* XXX Consider creating a constant for "sounds/purple" to be shared with Finch */
 			filename = g_build_filename(DATADIR, "sounds", "purple", sounds[event].def, NULL);
 		}
 
 		purple_sound_play_file(filename, NULL);
+
+		g_free(theme_name);
 		g_free(filename);
 	}
 
+
 	g_free(enable_pref);
 	g_free(file_pref);
 }
 
+gboolean 
+pidgin_sound_is_customized(void)
+{
+	gint i;	
+	gchar *path, *file;
+
+	for (i=0; i < PURPLE_NUM_SOUNDS; i++){
+		path = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", sounds[i].pref);
+		file = g_strdup(purple_prefs_get_path(path));
+		g_free(path);
+
+		if (file && strlen(file)){
+			g_free(file);
+			return TRUE;
+		}
+
+		g_free(file);
+	}
+
+	return FALSE;
+
+}
+
 static PurpleSoundUiOps sound_ui_ops =
 {
 	pidgin_sound_init,
--- a/pidgin/gtksound.h	Mon Jun 30 22:58:18 2008 +0000
+++ b/pidgin/gtksound.h	Mon Jun 30 23:12:54 2008 +0000
@@ -63,6 +63,13 @@
  */
 void *pidgin_sound_get_handle(void);
 
+/**
+ * Returns true Pidgin is using customized sounds
+ *
+ * @return TRUE if non default sounds are used 
+ */
+gboolean pidgin_sound_is_customized(void);
+
 /*@}*/
 
 #endif /* _PIDGINSOUND_H_ */