# HG changeset patch # User Gary Kramlich # Date 1219928423 0 # Node ID e0a596a0a02045026d50e5be3626f02e86e45fcb # Parent e9eb71dc21af403869635f851913fe856006e526# Parent 0de17531e9f21fd20167bca9a247074cb71e9a42 propagate from branch 'im.pidgin.pidgin' (head 69cdb2b9e20a69a98389b47c672718d5be222338) to branch 'im.pidgin.soc.2008.themes' (head 76c4c8e60f9192eff589f0416652aaab2ae9521d) diff -r e9eb71dc21af -r e0a596a0a020 libpurple/Makefile.am --- a/libpurple/Makefile.am Thu Aug 28 09:20:22 2008 +0000 +++ b/libpurple/Makefile.am Thu Aug 28 13:00:23 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 \ diff -r e9eb71dc21af -r e0a596a0a020 libpurple/core.c --- a/libpurple/core.c Thu Aug 28 09:20:22 2008 +0000 +++ b/libpurple/core.c Thu Aug 28 13:00:23 2008 +0000 @@ -46,10 +46,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 @@ -143,6 +145,7 @@ purple_plugins_probe(G_MODULE_SUFFIX); + purple_theme_manager_init(); /* The buddy icon code uses the imgstore, so init it early. */ purple_imgstore_init(); @@ -171,7 +174,6 @@ 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. @@ -180,6 +182,8 @@ if (ops != NULL && ops->ui_init != NULL) ops->ui_init(); + + purple_theme_manager_refresh(); return TRUE; } @@ -215,6 +219,7 @@ purple_status_uninit(); purple_prefs_uninit(); purple_sound_uninit(); + purple_theme_manager_uninit(); purple_xfers_uninit(); purple_proxy_uninit(); purple_dnsquery_uninit(); diff -r e9eb71dc21af -r e0a596a0a020 libpurple/sound-loader.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/sound-loader.c Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,115 @@ +/* + * 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" + +/***************************************************************************** + * Sound Theme Builder + *****************************************************************************/ + +static PurpleTheme * +purple_sound_loader_build(const gchar *dir) +{ + xmlnode *root_node = NULL, *sub_node; + gchar *filename_full, *data; + PurpleSoundTheme *theme = NULL; + + /* Find the theme file */ + g_return_val_if_fail(dir != NULL, NULL); + filename_full = g_build_filename(dir, "theme.xml", NULL); + + if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR)) + root_node = xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-loader"); + + g_free(filename_full); + 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); + + if (xmlnode_get_attrib(root_node, "name") != NULL) { + 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); + + sub_node = xmlnode_get_child(root_node, "event"); + + while (sub_node) { + purple_sound_theme_set_file(theme, + xmlnode_get_attrib(sub_node, "name"), + xmlnode_get_attrib(sub_node, "file")); + sub_node = xmlnode_get_next_twin(sub_node); + } + } + + xmlnode_free(root_node); + g_free(data); + return PURPLE_THEME(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; +} + + diff -r e9eb71dc21af -r e0a596a0a020 libpurple/sound-loader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/sound-loader.h Thu Aug 28 13:00:23 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 +#include +#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_ */ diff -r e9eb71dc21af -r e0a596a0a020 libpurple/sound-theme.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/sound-theme.c Thu Aug 28 13:00:23 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); +} + + diff -r e9eb71dc21af -r e0a596a0a020 libpurple/sound-theme.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/sound-theme.h Thu Aug 28 13:00:23 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 +#include +#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_ */ diff -r e9eb71dc21af -r e0a596a0a020 libpurple/sound.c --- a/libpurple/sound.c Thu Aug 28 09:20:22 2008 +0000 +++ b/libpurple/sound.c Thu Aug 28 13:00:23 2008 +0000 @@ -25,6 +25,8 @@ #include "blist.h" #include "prefs.h" #include "sound.h" +#include "sound-loader.h" +#include "theme-manager.h" static PurpleSoundUiOps *sound_ui_ops = NULL; @@ -134,6 +136,8 @@ purple_prefs_add_none("/purple/sound"); purple_prefs_add_int("/purple/sound/while_status", STATUS_AVAILABLE); memset(last_played, 0, sizeof(last_played)); + + purple_theme_manager_register_type(g_object_new(PURPLE_TYPE_SOUND_THEME_LOADER, "type", "sound", NULL)); } void diff -r e9eb71dc21af -r e0a596a0a020 libpurple/theme-loader.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme-loader.c Thu Aug 28 13:00:23 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); +} + +PurpleTheme * +purple_theme_loader_build(PurpleThemeLoader *loader, const gchar *dir) +{ + return PURPLE_THEME_LOADER_GET_CLASS(loader)->purple_theme_loader_build(dir); +} diff -r e9eb71dc21af -r e0a596a0a020 libpurple/theme-loader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme-loader.h Thu Aug 28 13:00:23 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 +#include +#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; + PurpleTheme *((*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 + */ +PurpleTheme *purple_theme_loader_build(PurpleThemeLoader *loader, const gchar *dir); + +G_END_DECLS +#endif /* _PURPLE_THEME_LOADER_H_ */ diff -r e9eb71dc21af -r e0a596a0a020 libpurple/theme-manager.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme-manager.c Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,302 @@ +/* + * 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 + +#include "theme-manager.h" +#include "util.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 + '/' + */ +static gchar * +purple_theme_manager_make_key(const gchar *name, const gchar *type) +{ + g_return_val_if_fail(name && strlen(name), NULL); + g_return_val_if_fail(type && strlen(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) +{ + + gchar *purple_dir, *theme_dir; + const gchar *name = NULL, *type = NULL; + GDir *rdir, *tdir; + PurpleThemeLoader *loader; + + rdir = g_dir_open(root, 0, NULL); + + g_return_if_fail(rdir); + + /* Parses directory by root/name/purple/type */ + while((name = g_dir_read_name(rdir))) { + purple_dir = g_build_filename(root, name, "purple", NULL); + tdir = g_dir_open(purple_dir, 0, NULL); + + if(!tdir) { + g_free(purple_dir); + + continue; + } + + while((type = g_dir_read_name(tdir))) { + if((loader = g_hash_table_lookup(theme_table, type))) { + PurpleTheme *theme = NULL; + + theme_dir = g_build_filename(purple_dir, type, NULL); + + theme = purple_theme_loader_build(loader, theme_dir); + + if(PURPLE_IS_THEME(theme)) + purple_theme_manager_add_theme(theme); + } + } + + g_dir_close(tdir); + g_free(purple_dir); + } + + g_dir_close(rdir); +} + +/***************************************************************************** + * Public API functions * + *****************************************************************************/ + +void +purple_theme_manager_init(void) +{ + theme_table = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + g_object_unref); +} + +void +purple_theme_manager_refresh() +{ + gchar *path = NULL; + const gchar *xdg = NULL; + gint i = 0; + + g_hash_table_foreach_remove(theme_table, + (GHRFunc) purple_theme_manager_is_theme, + NULL); + + /* Add themes from ~/.purple */ + path = g_build_filename(purple_user_dir(), "themes", NULL); + purple_theme_manager_build_dir(path); + g_free(path); + + /* look for XDG_DATA_HOME. If we don't have it use ~/.local, and add it */ + if((xdg = g_getenv("XDG_DATA_HOME")) != NULL) + path = g_build_filename(xdg, "themes", NULL); + else + path = g_build_filename(purple_home_dir(), ".local", "themes", NULL); + + purple_theme_manager_build_dir(path); + g_free(path); + + /* now dig through XDG_DATA_DIRS and add those too */ + xdg = g_getenv("XDG_DATA_DIRS"); + if(xdg) { + gchar **xdg_dirs = g_strsplit(xdg, G_SEARCHPATH_SEPARATOR_S, 0); + + for(i = 0; xdg_dirs[i]; i++) { + path = g_build_filename(xdg_dirs[i], "themes", NULL); + purple_theme_manager_build_dir(path); + g_free(path); + } + + g_strfreev(xdg_dirs); + } +} + +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) +{ + const 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, + (gpointer)type); + }/* only free if given registered loader */ +} + +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); +} + diff -r e9eb71dc21af -r e0a596a0a020 libpurple/theme-manager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme-manager.h Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,125 @@ +/** + * @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 +#include +#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 theme manager + */ +void purple_theme_manager_init (void); + +/** + * Uninitalizes the manager then frees all the themes an loaders it is responsible for + */ +void purple_theme_manager_uninit (void); + +/** + * Rebuilds all the themes in the theme manager + * (removes all current themes but keeps the added loaders) + */ +void purple_theme_manager_refresh(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__ */ + diff -r e9eb71dc21af -r e0a596a0a020 libpurple/theme.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme.c Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,407 @@ +/* + * 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" +#include "util.h" + +#include + +#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_DESCRIPTION, + 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_DESCRIPTION: + 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_DESCRIPTION: + 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); + + /* DESCRIPTION */ + 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_DESCRIPTION, 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; +} + +/****************************************************************************** + * Helper Functions + *****************************************************************************/ + +static gchar* +theme_clean_text(const gchar *text) +{ + gchar *clean_text = g_markup_escape_text(text, strlen(text)); + g_strdelimit(clean_text, "\n", ' '); + purple_str_strip_char(clean_text, '\r'); + return clean_text; +} + +/***************************************************************************** + * 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 = theme_clean_text(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 = theme_clean_text(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 = theme_clean_text(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); +} diff -r e9eb71dc21af -r e0a596a0a020 libpurple/theme.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme.h Thu Aug 28 13:00:23 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 +#include +#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_ */ diff -r e9eb71dc21af -r e0a596a0a020 libpurple/util.c --- a/libpurple/util.c Thu Aug 28 09:20:22 2008 +0000 +++ b/libpurple/util.c Thu Aug 28 13:00:23 2008 +0000 @@ -2771,70 +2771,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"); } /* diff -r e9eb71dc21af -r e0a596a0a020 libpurple/xmlnode.c --- a/libpurple/xmlnode.c Thu Aug 28 09:20:22 2008 +0000 +++ b/libpurple/xmlnode.c Thu Aug 28 13:00:23 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) { diff -r e9eb71dc21af -r e0a596a0a020 libpurple/xmlnode.h --- a/libpurple/xmlnode.h Thu Aug 28 09:20:22 2008 +0000 +++ b/libpurple/xmlnode.h Thu Aug 28 13:00:23 2008 +0000 @@ -26,6 +26,8 @@ #ifndef _PURPLE_XMLNODE_H_ #define _PURPLE_XMLNODE_H_ +#include + #ifdef __cplusplus extern "C" { #endif @@ -297,6 +299,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 diff -r e9eb71dc21af -r e0a596a0a020 pidgin/Makefile.am --- a/pidgin/Makefile.am Thu Aug 28 09:20:22 2008 +0000 +++ b/pidgin/Makefile.am Thu Aug 28 13:00:23 2008 +0000 @@ -78,6 +78,8 @@ pidginstock.c \ gtkaccount.c \ gtkblist.c \ + gtkblist-loader.c \ + gtkblist-theme.c \ gtkcelllayout.c \ gtkcellrendererexpander.c \ gtkcellrendererprogress.c \ @@ -94,6 +96,8 @@ gtkeventloop.c \ gtkexpander.c \ gtkft.c \ + gtkicon-theme.c \ + gtkicon-loader.c \ gtkidle.c \ gtkimhtml.c \ gtkimhtmltoolbar.c \ @@ -116,6 +120,7 @@ gtksourceiter.c \ gtksourceundomanager.c \ gtksourceview-marshal.c \ + gtkstatus-icon-theme.c \ gtkstatusbox.c \ gtkthemes.c \ gtkutils.c \ @@ -127,6 +132,8 @@ eggtrayicon.h \ gtkaccount.h \ gtkblist.h \ + gtkblist-loader.h \ + gtkblist-theme.h \ gtkcelllayout.h \ gtkcellrendererexpander.h \ gtkcellrendererprogress.h \ @@ -146,6 +153,8 @@ gtkeventloop.h \ gtkexpander.h \ gtkft.h \ + gtkicon-theme.h \ + gtkicon-loader.h \ gtkidle.h \ gtkgaim-compat.h \ gtkimhtml.h \ @@ -169,6 +178,7 @@ gtksourceiter.h \ gtksourceundomanager.h \ gtksourceview-marshal.h \ + gtkstatus-icon-theme.h \ gtkstatusbox.h \ pidginstock.h \ gtkthemes.h \ diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkblist-loader.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkblist-loader.c Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,274 @@ +/* + * GTKBlistThemeLoader for Pidgin + * + * 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 + +#include "xmlnode.h" + +#include "gtkblist-loader.h" +#include "gtkblist-theme.h" + +/****************************************************************************** + * Globals + *****************************************************************************/ +#define DEFAULT_TEXT_COLOR "black" +/***************************************************************************** + * Buddy List Theme Builder + *****************************************************************************/ + +static PurpleTheme * +pidgin_blist_loader_build(const gchar *dir) +{ + xmlnode *root_node = NULL, *sub_node, *sub_sub_node; + gchar *filename_full, *data; + const gchar *temp; + gboolean sucess = TRUE; + GdkColor *bgcolor, *expanded_bgcolor, *collapsed_bgcolor, *contact_color; + GdkColor color; + FontColorPair *expanded, *collapsed, *contact, *online, *away, *offline, *idle, *message, *status; + PidginBlistLayout *layout; + PidginBlistTheme *theme; + + /* Find the theme file */ + g_return_val_if_fail(dir != NULL, NULL); + filename_full = g_build_filename(dir, "theme.xml", NULL); + + if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR)) + root_node = xmlnode_from_file(dir, "theme.xml", "buddy list themes", "blist-loader"); + + g_free(filename_full); + g_return_val_if_fail(root_node != NULL, NULL); + + sub_node = xmlnode_get_child(root_node, "description"); + data = xmlnode_get_data(sub_node); + + /* init all structs and colors */ + bgcolor = g_new0(GdkColor, 1); + expanded_bgcolor = g_new0(GdkColor, 1); + collapsed_bgcolor = g_new0(GdkColor, 1); + + layout = g_new0(PidginBlistLayout, 1); + + contact_color = g_new0(GdkColor, 1); + + expanded = g_new0(FontColorPair, 1); + collapsed = g_new0(FontColorPair, 1); + contact = g_new0(FontColorPair, 1); + online = g_new0(FontColorPair, 1); + away = g_new0(FontColorPair, 1); + offline = g_new0(FontColorPair, 1); + idle = g_new0(FontColorPair, 1); + message = g_new0(FontColorPair, 1); + status = g_new0(FontColorPair, 1); + + /* */ + if ((sucess = (sub_node = xmlnode_get_child(root_node, "blist")) != NULL)) { + if ((temp = xmlnode_get_attrib(sub_node, "color")) != NULL && gdk_color_parse(temp, bgcolor)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), bgcolor, FALSE, TRUE); + else { + g_free(bgcolor); + bgcolor = NULL; + } + } + + /* */ + if ((sucess = sucess && (sub_node = xmlnode_get_child(root_node, "groups")) != NULL + && (sub_sub_node = xmlnode_get_child(sub_node, "expanded")) != NULL)) { + + expanded->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + + if ((temp = xmlnode_get_attrib(sub_sub_node, "text_color")) != NULL && gdk_color_parse(temp, &color)) + expanded->color = g_strdup(temp); + else expanded->color = g_strdup(DEFAULT_TEXT_COLOR); + + + if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, expanded_bgcolor)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), expanded_bgcolor, FALSE, TRUE); + else { + g_free(expanded_bgcolor); + expanded_bgcolor = NULL; + } + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "collapsed")) != NULL)) { + + collapsed->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + + if((temp = xmlnode_get_attrib(sub_sub_node, "text_color")) != NULL && gdk_color_parse(temp, &color)) + collapsed->color = g_strdup(temp); + else collapsed->color = g_strdup(DEFAULT_TEXT_COLOR); + + if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, collapsed_bgcolor)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), collapsed_bgcolor, FALSE, TRUE); + else { + g_free(collapsed_bgcolor); + collapsed_bgcolor = NULL; + } + } + + /* */ + if ((sucess = sucess && (sub_node = xmlnode_get_child(root_node, "buddys")) != NULL && + (sub_sub_node = xmlnode_get_child(sub_node, "placement")) != NULL)) { + + layout->status_icon = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) : 0; + layout->text = (temp = xmlnode_get_attrib(sub_sub_node, "name")) != NULL ? atoi(temp) : 1; + layout->emblem = (temp = xmlnode_get_attrib(sub_sub_node, "emblem")) != NULL ? atoi(temp) : 2; + layout->protocol_icon = (temp = xmlnode_get_attrib(sub_sub_node, "protocol_icon")) != NULL ? atoi(temp) : 3; + layout->buddy_icon = (temp = xmlnode_get_attrib(sub_sub_node, "buddy_icon")) != NULL ? atoi(temp) : 4; + layout->show_status = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) != 0 : 1; + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "background")) != NULL)) { + if(gdk_color_parse(xmlnode_get_attrib(sub_sub_node, "color"), contact_color)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), contact_color, FALSE, TRUE); + else { + g_free(contact_color); + contact_color = NULL; + } + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "contact_text")) != NULL)) { + contact->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + contact->color = g_strdup(temp); + else contact->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "online_text")) != NULL)) { + online->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + online->color = g_strdup(temp); + else online->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "away_text")) != NULL)) { + away->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + away->color = g_strdup(temp); + else away->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "offline_text")) != NULL)) { + offline->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + online->color = g_strdup(temp); + else online->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "idle_text")) != NULL)) { + idle->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + idle->color = g_strdup(temp); + else online->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "message_text")) != NULL)) { + message->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + message->color = g_strdup(temp); + else message->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "status_text")) != NULL)) { + status->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + status->color = g_strdup(temp); + else status->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + /* name is required for theme manager */ + sucess = sucess && xmlnode_get_attrib(root_node, "name") != NULL; + + /* the new theme */ + theme = g_object_new(PIDGIN_TYPE_BLIST_THEME, + "type", "blist", + "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, + "background-color", bgcolor, + "layout", layout, + "expanded-color", expanded_bgcolor, + "expanded-text", expanded, + "collapsed-color", collapsed_bgcolor, + "collapsed-text", collapsed, + "contact-color", contact_color, + "contact", contact, + "online", online, + "away", away, + "offline", offline, + "idle", idle, + "message", message, + "status", status, NULL); + + xmlnode_free(root_node); + g_free(data); + + /* malformed xml file - also frees all partial data*/ + if (!sucess) { + g_object_unref(theme); + theme = NULL; + } + + return PURPLE_THEME(theme); +} + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_blist_theme_loader_class_init(PidginBlistThemeLoaderClass *klass) +{ + PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass); + + loader_klass->purple_theme_loader_build = pidgin_blist_loader_build; +} + + +GType +pidgin_blist_theme_loader_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PidginBlistThemeLoaderClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_blist_theme_loader_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginBlistThemeLoader), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static(PURPLE_TYPE_THEME_LOADER, + "PidginBlistThemeLoader", + &info, 0); + } + return type; +} + + diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkblist-loader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkblist-loader.h Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,71 @@ +/** + * @file gtkblist-loader.h Pidgin Buddy List Theme Loader Class API + */ + +/* pidgin + * + * 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 + */ + +#ifndef _PIDGIN_BLIST_THEME_LOADER_H_ +#define _PIDGIN_BLIST_THEME_LOADER_H_ + +#include +#include +#include "theme-loader.h" + +/** + * A pidgin buddy list theme loader. extends PurpleThemeLoader (theme-loader.h) + * This is a class designed to build sound themes + * + * PidginBlistThemeLoader is a GObject. + */ +typedef struct _PidginBlistThemeLoader PidginBlistThemeLoader; +typedef struct _PidginBlistThemeLoaderClass PidginBlistThemeLoaderClass; + +#define PIDGIN_TYPE_BLIST_THEME_LOADER (pidgin_blist_theme_loader_get_type ()) +#define PIDGIN_BLIST_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_BLIST_THEME_LOADER, PidginBlistThemeLoader)) +#define PIDGIN_BLIST_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_BLIST_THEME_LOADER, PidginBlistThemeLoaderClass)) +#define PIDGIN_IS_BLIST_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_BLIST_THEME_LOADER)) +#define PIDGIN_IS_BLIST_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_BLIST_THEME_LOADER)) +#define PIDGIN_BLIST_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_BLIST_THEME_LOADER, PidginBlistThemeLoaderClass)) + +struct _PidginBlistThemeLoader +{ + PurpleThemeLoader parent; +}; + +struct _PidginBlistThemeLoaderClass +{ + PurpleThemeLoaderClass parent_class; +}; + +/**************************************************************************/ +/** @name Buddy List Theme-Loader API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_blist_theme_loader_get_type(void); + +G_END_DECLS +#endif /* _PIDGIN_BLIST_THEME_LOADER_H_ */ diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkblist-theme.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkblist-theme.c Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,743 @@ +/* + * Buddy List Themes for Pidgin + * + * 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 "gtkblist-theme.h" + +#define PIDGIN_BLIST_THEME_GET_PRIVATE(Gobject) \ + ((PidginBlistThemePrivate *) ((PIDGIN_BLIST_THEME(Gobject))->priv)) + +/****************************************************************************** + * Structs + *****************************************************************************/ +typedef struct { + /* Buddy list */ + gdouble opacity; + GdkColor *bgcolor; + PidginBlistLayout *layout; + + /* groups */ + GdkColor *expanded_color; + FontColorPair *expanded; + + GdkColor *collapsed_color; + FontColorPair *collapsed; + + /* buddy */ + GdkColor *contact_color; + + FontColorPair *contact; + + FontColorPair *online; + FontColorPair *away; + FontColorPair *offline; + FontColorPair *idle; + FontColorPair *message; + + FontColorPair *status; + +} PidginBlistThemePrivate; + +/****************************************************************************** + * Globals + *****************************************************************************/ + +static GObjectClass *parent_class = NULL; + +/****************************************************************************** + * Enums + *****************************************************************************/ +enum { + PROP_ZERO = 0, + PROP_BACKGROUND_COLOR, + PROP_OPACITY, + PROP_LAYOUT, + PROP_EXPANDED_COLOR, + PROP_EXPANDED_TEXT, + PROP_COLLAPSED_COLOR, + PROP_COLLAPSED_TEXT, + PROP_CONTACT_COLOR, + PROP_CONTACT, + PROP_ONLINE, + PROP_AWAY, + PROP_OFFLINE, + PROP_IDLE, + PROP_MESSAGE, + PROP_STATUS, +}; + +/****************************************************************************** + * Helpers + *****************************************************************************/ + +void +free_font_and_color(FontColorPair *pair) +{ + if(pair != NULL){ + if (pair->font) + g_free(pair->font); + if (pair->color) + g_free(pair->color); + g_free(pair); + } +} + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_blist_theme_init(GTypeInstance *instance, + gpointer klass) +{ + (PIDGIN_BLIST_THEME(instance))->priv = g_new0(PidginBlistThemePrivate, 1); +} + +static void +pidgin_blist_theme_get_property(GObject *obj, guint param_id, GValue *value, + GParamSpec *psec) +{ + PidginBlistTheme *theme = PIDGIN_BLIST_THEME(obj); + + switch(param_id) { + case PROP_BACKGROUND_COLOR: + g_value_set_pointer(value, pidgin_blist_theme_get_background_color(theme)); + break; + case PROP_OPACITY: + g_value_set_double(value, pidgin_blist_theme_get_opacity(theme)); + break; + case PROP_LAYOUT: + g_value_set_pointer(value, pidgin_blist_theme_get_layout(theme)); + break; + case PROP_EXPANDED_COLOR: + g_value_set_pointer(value, pidgin_blist_theme_get_expanded_background_color(theme)); + break; + case PROP_EXPANDED_TEXT: + g_value_set_pointer(value, pidgin_blist_theme_get_expanded_text_info(theme)); + break; + case PROP_COLLAPSED_COLOR: + g_value_set_pointer(value, pidgin_blist_theme_get_collapsed_background_color(theme)); + break; + case PROP_COLLAPSED_TEXT: + g_value_set_pointer(value, pidgin_blist_theme_get_collapsed_text_info(theme)); + break; + case PROP_CONTACT_COLOR: + g_value_set_pointer(value, pidgin_blist_theme_get_contact_color(theme)); + break; + case PROP_CONTACT: + g_value_set_pointer(value, pidgin_blist_theme_get_contact_text_info(theme)); + break; + case PROP_ONLINE: + g_value_set_pointer(value, pidgin_blist_theme_get_online_text_info(theme)); + break; + case PROP_AWAY: + g_value_set_pointer(value, pidgin_blist_theme_get_away_text_info(theme)); + break; + case PROP_OFFLINE: + g_value_set_pointer(value, pidgin_blist_theme_get_offline_text_info(theme)); + break; + case PROP_IDLE: + g_value_set_pointer(value, pidgin_blist_theme_get_idle_text_info(theme)); + break; + case PROP_MESSAGE: + g_value_set_pointer(value, pidgin_blist_theme_get_unread_message_text_info(theme)); + break; + case PROP_STATUS: + g_value_set_pointer(value, pidgin_blist_theme_get_status_text_info(theme)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec); + break; + } +} + +static void +pidgin_blist_theme_set_property(GObject *obj, guint param_id, const GValue *value, + GParamSpec *psec) +{ + PidginBlistTheme *theme = PIDGIN_BLIST_THEME(obj); + + switch(param_id) { + case PROP_BACKGROUND_COLOR: + pidgin_blist_theme_set_background_color(theme, g_value_get_pointer(value)); + break; + case PROP_OPACITY: + pidgin_blist_theme_set_opacity(theme, g_value_get_double(value)); + break; + case PROP_LAYOUT: + pidgin_blist_theme_set_layout(theme, g_value_get_pointer(value)); + break; + case PROP_EXPANDED_COLOR: + pidgin_blist_theme_set_expanded_background_color(theme, g_value_get_pointer(value)); + break; + case PROP_EXPANDED_TEXT: + pidgin_blist_theme_set_expanded_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_COLLAPSED_COLOR: + pidgin_blist_theme_set_collapsed_background_color(theme, g_value_get_pointer(value)); + break; + case PROP_COLLAPSED_TEXT: + pidgin_blist_theme_set_collapsed_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_CONTACT_COLOR: + pidgin_blist_theme_set_contact_color(theme, g_value_get_pointer(value)); + break; + case PROP_CONTACT: + pidgin_blist_theme_set_contact_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_ONLINE: + pidgin_blist_theme_set_online_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_AWAY: + pidgin_blist_theme_set_away_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_OFFLINE: + pidgin_blist_theme_set_offline_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_IDLE: + pidgin_blist_theme_set_idle_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_MESSAGE: + pidgin_blist_theme_set_unread_message_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_STATUS: + pidgin_blist_theme_set_status_text_info(theme, g_value_get_pointer(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec); + break; + } +} +static void +pidgin_blist_theme_finalize (GObject *obj) +{ + PidginBlistThemePrivate *priv; + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(obj); + + /* Buddy List */ + g_free(priv->layout); + + /* Group */ + free_font_and_color(priv->expanded); + free_font_and_color(priv->collapsed); + + /* Buddy */ + free_font_and_color(priv->contact); + free_font_and_color(priv->online); + free_font_and_color(priv->away); + free_font_and_color(priv->offline); + free_font_and_color(priv->message); + free_font_and_color(priv->status); + + g_free(priv); + + parent_class->finalize (obj); +} + +static void +pidgin_blist_theme_class_init (PidginBlistThemeClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + parent_class = g_type_class_peek_parent (klass); + + obj_class->get_property = pidgin_blist_theme_get_property; + obj_class->set_property = pidgin_blist_theme_set_property; + obj_class->finalize = pidgin_blist_theme_finalize; + + /* Buddy List */ + pspec = g_param_spec_pointer("background-color", "Background Color", + "The background color for the buddy list", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_BACKGROUND_COLOR, pspec); + + pspec = g_param_spec_pointer("layout", "Layout", + "The layout of icons, name, and status of the blist", + G_PARAM_READWRITE); + + g_object_class_install_property(obj_class, PROP_LAYOUT, pspec); + + /* Group */ + pspec = g_param_spec_pointer("expanded-color", "Expanded Background Color", + "The background color of an expanded group", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_EXPANDED_COLOR, pspec); + + pspec = g_param_spec_pointer("expanded-text", "Expanded Text", + "The text information for when a group is expanded", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_EXPANDED_TEXT, pspec); + + pspec = g_param_spec_pointer("collapsed-color", "Collapsed Background Color", + "The background color of a collapsed group", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_COLLAPSED_COLOR, pspec); + + pspec = g_param_spec_pointer("collapsed-text", "Collapsed Text", + "The text information for when a group is collapsed", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_COLLAPSED_TEXT, pspec); + + /* Buddy */ + pspec = g_param_spec_pointer("contact-color", "Contact/Chat Background Color", + "The background color of a contact or chat", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_CONTACT_COLOR, pspec); + + pspec = g_param_spec_pointer("contact", "Contact Text", + "The text information for when a contact is expanded", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_CONTACT, pspec); + + pspec = g_param_spec_pointer("online", "On-line Text", + "The text information for when a buddy is online", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_ONLINE, pspec); + + pspec = g_param_spec_pointer("away", "Away Text", + "The text information for when a buddy is away", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_AWAY, pspec); + + pspec = g_param_spec_pointer("offline", "Off-line Text", + "The text information for when a buddy is off-line", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_OFFLINE, pspec); + + pspec = g_param_spec_pointer("idle", "Idle Text", + "The text information for when a buddy is idle", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_IDLE, pspec); + + pspec = g_param_spec_pointer("message", "Message Text", + "The text information for when a buddy is has an unread message", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_MESSAGE, pspec); + + pspec = g_param_spec_pointer("status", "Status Text", + "The text information for a buddy's status", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_STATUS, pspec); +} + +GType +pidgin_blist_theme_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static GTypeInfo info = { + sizeof (PidginBlistThemeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_blist_theme_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginBlistTheme), + 0, /* n_preallocs */ + pidgin_blist_theme_init, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static (PURPLE_TYPE_THEME, + "PidginBlistTheme", + &info, 0); + } + return type; +} + + +/***************************************************************************** + * Public API functions + *****************************************************************************/ + +/* get methods */ +GdkColor * +pidgin_blist_theme_get_background_color(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->bgcolor; +} + +gdouble +pidgin_blist_theme_get_opacity(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), 1.0); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->opacity; +} + +PidginBlistLayout * +pidgin_blist_theme_get_layout(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->layout; +} + +GdkColor * +pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->expanded_color; +} + +FontColorPair * +pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->expanded; +} + +GdkColor * +pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->collapsed_color; +} + +FontColorPair * +pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->collapsed; +} + +GdkColor * +pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->contact_color; +} + +FontColorPair * +pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->contact; +} + +FontColorPair * +pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->online; +} + +FontColorPair * +pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->away; +} + +FontColorPair * +pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->offline; +} + +FontColorPair * +pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->idle; +} + +FontColorPair * +pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->message; +} + +FontColorPair * +pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->status; +} + +/* Set Methods */ +void +pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, GdkColor *color) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + priv->bgcolor = color; +} + +void +pidgin_blist_theme_set_opacity(PidginBlistTheme *theme, gdouble opacity) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme) || opacity < 0.0 || opacity > 1.0); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + priv->opacity = opacity; +} + +void +pidgin_blist_theme_set_layout(PidginBlistTheme *theme, PidginBlistLayout *layout) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + g_free(priv->layout); + priv->layout = layout; +} + +void +pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, GdkColor *color) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + priv->expanded_color = color; +} + +void +pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->expanded); + priv->expanded = pair; +} + +void +pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, GdkColor *color) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + priv->collapsed_color = color; +} + +void +pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->collapsed); + priv->collapsed = pair; +} + +void +pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, GdkColor *color) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + priv->contact_color = color; +} + +void +pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->contact); + priv->contact = pair; +} + +void +pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->online); + priv->online = pair; +} + +void +pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->away); + priv->away = pair; +} + +void +pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->offline); + priv->offline = pair; +} + +void +pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->idle); + priv->idle = pair; +} + +void +pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->message); + priv->message = pair; +} + +void +pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->status); + priv->status = pair; +} diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkblist-theme.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkblist-theme.h Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,317 @@ +/** + * @file gtkblist-theme.h GTK+ Buddy List Theme API + */ + +/* pidgin + * + * 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 + */ + +#ifndef _PIDGIN_BLIST_THEME_H_ +#define _PIDGIN_BLIST_THEME_H_ + +#include +#include +#include + +#include "theme.h" + +/** + * extends PurpleTheme (theme.h) + * A pidgin buddy list theme. + * This is an object for Purple to represent a buddy list theme. + * + * PidginBlistTheme is a PurpleTheme Object. + */ +typedef struct _PidginBlistTheme PidginBlistTheme; +typedef struct _PidginBlistThemeClass PidginBlistThemeClass; + +#define PIDGIN_TYPE_BLIST_THEME (pidgin_blist_theme_get_type ()) +#define PIDGIN_BLIST_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_BLIST_THEME, PidginBlistTheme)) +#define PIDGIN_BLIST_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_BLIST_THEME, PidginBlistThemeClass)) +#define PIDGIN_IS_BLIST_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_BLIST_THEME)) +#define PIDGIN_IS_BLIST_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_BLIST_THEME)) +#define PIDGIN_BLIST_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_BLIST_THEME, PidginBlistThemeClass)) + +struct _PidginBlistTheme +{ + PurpleTheme parent; + gpointer priv; +}; + +struct _PidginBlistThemeClass +{ + PurpleThemeClass parent_class; +}; + +typedef struct +{ + gchar *font; + gchar *color; + +} FontColorPair; + +typedef struct +{ + gint status_icon; + gint text; + gint emblem; + gint protocol_icon; + gint buddy_icon; + gboolean show_status; + +} PidginBlistLayout; + +/**************************************************************************/ +/** @name FontColorPair API */ +/**************************************************************************/ + +/** + * Frees a font and color pair + */ +void free_font_and_color(FontColorPair *pair); + +/**************************************************************************/ +/** @name Purple Buddy List Theme API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_blist_theme_get_type(void); + +/* get methods */ + +/** + * Returns the background color of the buddy list + * + * @returns a gdk color + */ + GdkColor *pidgin_blist_theme_get_background_color(PidginBlistTheme *theme); + +/** + * Returns the opacity of the buddy list window + * (0.0 or clear to 1.0 fully Opaque) + * + * @returns the opacity + */ +gdouble pidgin_blist_theme_get_opacity(PidginBlistTheme *theme); + +/** + * Returns the layout to be used with the buddy list + * + * @returns the buddy list layout + */ + PidginBlistLayout *pidgin_blist_theme_get_layout(PidginBlistTheme *theme); + +/** + * Returns the background color to be used with expanded groups + * + * @returns a gdk color + */ + GdkColor *pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used with expanded groups + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme); + +/** + * Returns the background color to be used with collapsed groups + * + * @returns a gdk color + */ + GdkColor *pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used with collapsed groups + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme); + +/** + * Returns the colors to be used for contacts and chats + * + * @returns a gdkcolor for contacts and chats + */ + GdkColor *pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for expanded contacts + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for online buddies + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for away and idle buddies + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for offline buddies + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for idle buddies + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for buddies with unread messages + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for a buddy's status message + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme); + +/* Set Methods */ + +/** + * Sets the background color to be used for this buddy list theme + * + * @param color the new background color + */ +void pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, GdkColor *color); + +/** + * Sets the opacity to be used for this buddy list theme + * + * @param opacity the new opacity setting + */ +void pidgin_blist_theme_set_opacity(PidginBlistTheme *theme, gdouble opacity); + +/** + * Sets the buddy list layout to be used for this buddy list theme + * + * @param layout the new layout + */ +void pidgin_blist_theme_set_layout(PidginBlistTheme *theme, PidginBlistLayout *layout); + +/** + * Sets the background color to be used for expanded groups + * + * @param color the new background color + */ +void pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, GdkColor *color); + +/** + * Sets the text color and font to be used for expanded groups + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the background color to be used for collapsed groups + * + * @param color the new background color + */ +void pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, GdkColor *color); + +/** + * Sets the text color and font to be used for expanded groups + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the background color to be used for contacts and chats + * + * @param color the color to use for contacts and chats + */ +void pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, GdkColor *color); + +/** + * Sets the text color and font to be used for expanded contacts + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for online buddies + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for away and idle buddies + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for offline buddies + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for idle buddies + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for buddies with an unread message + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for buddy status messages + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +G_END_DECLS +#endif /* _PIDGIN_BLIST_THEME_H_ */ diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Thu Aug 28 09:20:22 2008 +0000 +++ b/pidgin/gtkblist.c Thu Aug 28 13:00:23 2008 +0000 @@ -38,6 +38,8 @@ #include "request.h" #include "signals.h" #include "pidginstock.h" +#include "theme-loader.h" +#include "theme-manager.h" #include "util.h" #include "gtkaccount.h" @@ -58,6 +60,8 @@ #include "gtkstatusbox.h" #include "gtkscrollbook.h" #include "gtksmiley.h" +#include "gtkblist-loader.h" +#include "gtkblist-theme.h" #include "gtkutils.h" #include "pidgin/minidialog.h" #include "pidgin/pidgintooltip.h" @@ -121,6 +125,9 @@ * is showing; @c NULL otherwise. */ PidginMiniDialog *signed_on_elsewhere; + + PidginBlistTheme *current_theme; + } PidginBuddyListPrivate; #define PIDGIN_BUDDY_LIST_GET_PRIVATE(list) \ @@ -1790,7 +1797,8 @@ return handled; } -static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_data) +static gboolean +gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_data) { GtkTreePath *path; PurpleBlistNode *node; @@ -3776,19 +3784,22 @@ return ret; } -gchar *pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased) -{ - const char *name; - char *esc, *text = NULL; +gchar * +pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased) +{ + const char *name, *name_color, *name_font, *status_color, *status_font; + char *text = NULL; PurplePlugin *prpl; PurplePluginProtocolInfo *prpl_info = NULL; PurpleContact *contact; PurplePresence *presence; struct _pidgin_blist_node *gtkcontactnode = NULL; - char *idletime = NULL, *statustext = NULL; - time_t t; + char *idletime = NULL, *statustext = NULL, *nametext = NULL; PurpleConversation *conv = find_conversation_with_buddy(b); gboolean hidden_conv = FALSE; + gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); + FontColorPair *pair; + PidginBlistTheme *theme; if (conv != NULL) { PidginBlistNode *ui = b->node.ui_data; @@ -3806,174 +3817,164 @@ if(contact) gtkcontactnode = ((PurpleBlistNode*)contact)->ui_data; - if(gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias) + /* Name */ + if (gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias) name = contact->alias; else name = purple_buddy_get_alias(b); - esc = g_markup_escape_text(name, strlen(name)); + nametext = g_markup_escape_text(name, strlen(name)); presence = purple_buddy_get_presence(b); - if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons") && aliased) - { - if (!selected && purple_presence_is_idle(presence)) - { - text = g_strdup_printf("%s", - dim_grey(), esc); - g_free(esc); - if (hidden_conv) { - char *tmp = text; - text = g_strdup_printf("%s", text); + /* Name is all that is needed */ + if (aliased && biglist) { + + /* Status Info */ + prpl = purple_find_prpl(purple_account_get_protocol_id(b->account)); + + if (prpl != NULL) + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + + if (prpl_info && prpl_info->status_text && b->account->gc) { + char *tmp = prpl_info->status_text(b); + const char *end; + + if(tmp && !g_utf8_validate(tmp, -1, &end)) { + char *new = g_strndup(tmp, + g_utf8_pointer_to_offset(tmp, end)); + g_free(tmp); + tmp = new; + } + /* add ... to messages that are too long, GTK 2.6+ does it automatically */ +#if !GTK_CHECK_VERSION(2,6,0) + if(tmp) { + char buf[32]; + char *c = tmp; + int length = 0, vis=0; + gboolean inside = FALSE; + g_strdelimit(tmp, "\n", ' '); + purple_str_strip_char(tmp, '\r'); + + while(*c && vis < 20) { + if(*c == '&') + inside = TRUE; + else if(*c == ';') + inside = FALSE; + if(!inside) + vis++; + c = g_utf8_next_char(c); /* this is fun */ + } + + length = c - tmp; + + if(vis == 20) + g_snprintf(buf, sizeof(buf), "%%.%ds...", length); + else + g_snprintf(buf, sizeof(buf), "%%s "); + + statustext = g_strdup_printf(buf, tmp);purple_presence_is_idle(presence) + g_free(tmp); } - return text; - } - else if (hidden_conv) - { - char *tmp = esc; - esc = g_strdup_printf("%s", esc); - g_free(tmp); - } - return esc; - } - - prpl = purple_find_prpl(purple_account_get_protocol_id(b->account)); - - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && prpl_info->status_text && b->account->gc) { - char *tmp = prpl_info->status_text(b); - const char *end; - - if(tmp && !g_utf8_validate(tmp, -1, &end)) { - char *new = g_strndup(tmp, - g_utf8_pointer_to_offset(tmp, end)); - g_free(tmp); - tmp = new; +#else + if(tmp) { + g_strdelimit(tmp, "\n", ' '); + purple_str_strip_char(tmp, '\r'); + } + statustext = tmp; +#endif } - -#if !GTK_CHECK_VERSION(2,6,0) - if(tmp) { - char buf[32]; - char *c = tmp; - int length = 0, vis=0; - gboolean inside = FALSE; - g_strdelimit(tmp, "\n", ' '); - purple_str_strip_char(tmp, '\r'); - - while(*c && vis < 20) { - if(*c == '&') - inside = TRUE; - else if(*c == ';') - inside = FALSE; - if(!inside) - vis++; - c = g_utf8_next_char(c); /* this is fun */ - } - - length = c - tmp; - - if(vis == 20) - g_snprintf(buf, sizeof(buf), "%%.%ds...", length); - else - g_snprintf(buf, sizeof(buf), "%%s "); - - statustext = g_strdup_printf(buf, tmp); - - g_free(tmp); - } -#else - if(tmp) { - g_strdelimit(tmp, "\n", ' '); - purple_str_strip_char(tmp, '\r'); - } - statustext = tmp; -#endif - } - - if(!purple_presence_is_online(presence) && !statustext) - statustext = g_strdup(_("Offline")); - else if (!statustext) - text = g_strdup(esc); - - if (purple_presence_is_idle(presence)) { - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time")) { + + if(!purple_presence_is_online(presence) && !statustext) + statustext = g_strdup(_("Offline")); + + /* Idle Text */ + if (purple_presence_is_idle(presence) && purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time")) { time_t idle_secs = purple_presence_get_idle_time(presence); if (idle_secs > 0) { int iday, ihrs, imin; + time_t t; time(&t); iday = (t - idle_secs) / (24 * 60 * 60); ihrs = ((t - idle_secs) / 60 / 60) % 24; imin = ((t - idle_secs) / 60) % 60; - - if (iday) + + if (iday) idletime = g_strdup_printf(_("Idle %dd %dh %02dm"), iday, ihrs, imin); else if (ihrs) idletime = g_strdup_printf(_("Idle %dh %02dm"), ihrs, imin); else idletime = g_strdup_printf(_("Idle %dm"), imin); - } - else - idletime = g_strdup(_("Idle")); - - if (!selected) { - g_free(text); - text = g_strdup_printf("%s\n" - "%s%s%s", - dim_grey(), esc, dim_grey(), - idletime != NULL ? idletime : "", - (idletime != NULL && statustext != NULL) ? " - " : "", - statustext != NULL ? statustext : ""); - } - } - else if (!selected && !statustext) {/* We handle selected text later */ - g_free(text); - text = g_strdup_printf("%s", dim_grey(), esc); - } else if (!selected && !text) { - g_free(text); - text = g_strdup_printf("%s\n" - "%s", - dim_grey(), esc, dim_grey(), - statustext != NULL ? statustext : ""); + + } else idletime = g_strdup(_("Idle")); } - } else if (!PURPLE_BUDDY_IS_ONLINE(b)) { - if (!selected && !statustext) {/* We handle selected text later */ - g_free(text); - text = g_strdup_printf("%s", dim_grey(), esc); - } else if (!selected && !text) - text = g_strdup_printf("%s\n" - "%s", - dim_grey(), esc, dim_grey(), - statustext != NULL ? statustext : ""); - - } - /* Not idle and not selected */ - else if (!selected && !text) - { - text = g_strdup_printf("%s\n" - "%s", - esc, dim_grey(), - statustext != NULL ? statustext : ""); - } - - /* It is selected. */ - if ((selected && !text) || (selected && idletime)) { - g_free(text); - text = g_strdup_printf("%s\n" - "%s%s%s", - esc, - idletime != NULL ? idletime : "", - (idletime != NULL && statustext != NULL) ? " - " : "", - statustext != NULL ? statustext : ""); - } - - g_free(idletime); - g_free(statustext); - g_free(esc); + } + + /* choose the colors of the text */ + theme = pidgin_blist_get_theme(); + + if (theme == NULL) { + status_color = name_color = "dim grey"; + status_font = name_font = ""; + + } else if (purple_presence_is_idle(presence)) { + pair = pidgin_blist_theme_get_idle_text_info(theme); + status_color = name_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey"; + status_font = name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + } else if (!purple_presence_is_online(presence)) { + pair = pidgin_blist_theme_get_offline_text_info(theme); + name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black"; + name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + pair = pidgin_blist_theme_get_status_text_info(theme); + status_color = (pair != NULL && pair->color != NULL) ? g_strdup(pair->color) : "dim grey"; + status_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + } else if (purple_presence_is_available(presence)) { + pair = pidgin_blist_theme_get_online_text_info(theme); + name_color = (pair != NULL && pair->color != NULL) ? g_strdup(pair->color) : "black"; + name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + pair = pidgin_blist_theme_get_status_text_info(theme); + status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey"; + status_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + } else { + pair = pidgin_blist_theme_get_away_text_info(theme); + name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black"; + name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + pair = pidgin_blist_theme_get_status_text_info(theme); + status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey"; + status_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + } + + if (aliased && selected) { + name_color = "black"; + status_color = "black"; + } + + /* Put it all together */ + if (aliased && biglist && (statustext || idletime)) { + /* using breaks the status, so it must be seperated into */ + text = g_strdup_printf("%s\n" + "%s%s%s", + name_font, name_color, nametext, status_font, status_color, + idletime != NULL ? idletime : "", + (idletime != NULL && statustext != NULL) ? " - " : "", + statustext != NULL ? statustext : ""); + + } else text = g_strdup_printf("%s", name_font, name_color, nametext); + + g_free(nametext); + if (statustext) + g_free(statustext); + if (idletime) + g_free(idletime); if (hidden_conv) { char *tmp = text; @@ -5225,11 +5226,144 @@ } #endif +/* builds the blist layout according to to the current theme */ +static void +pidgin_blist_build_layout(PurpleBuddyList *list) +{ + GtkTreeViewColumn *column; + PidginBlistLayout *layout; + PidginBlistTheme *theme; + GtkCellRenderer *rend; + gint i, status_icon = 0, text = 1, emblem = 2, protocol_icon = 3, buddy_icon = 4; + + + column = gtkblist->text_column; + + if ((theme = pidgin_blist_get_theme()) != NULL && (layout = pidgin_blist_theme_get_layout(theme)) != NULL) { + status_icon = layout->status_icon ; + text = layout->text; + emblem = layout->emblem; + protocol_icon = layout->protocol_icon; + buddy_icon = layout->buddy_icon; + } + + gtk_tree_view_column_clear(column); + + /* group */ + rend = pidgin_cell_renderer_expander_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, + "visible", GROUP_EXPANDER_VISIBLE_COLUMN, + "expander-visible", GROUP_EXPANDER_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "sensitive", GROUP_EXPANDER_COLUMN, + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + NULL); + + /* contact */ + rend = pidgin_cell_renderer_expander_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, + "visible", CONTACT_EXPANDER_VISIBLE_COLUMN, + "expander-visible", CONTACT_EXPANDER_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "sensitive", CONTACT_EXPANDER_COLUMN, + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + NULL); + + for (i = 0; i < 5; i++) { + + if (status_icon == i) { + /* status icons */ + rend = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, + "pixbuf", STATUS_ICON_COLUMN, + "visible", STATUS_ICON_VISIBLE_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + NULL); + g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); + + } else if (text == i) { + /* name */ + gtkblist->text_rend = rend = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, rend, TRUE); + gtk_tree_view_column_set_attributes(column, rend, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + "markup", NAME_COLUMN, + NULL); +#if GTK_CHECK_VERSION(2,6,0) + g_signal_connect(G_OBJECT(rend), "editing-started", G_CALLBACK(gtk_blist_renderer_editing_started_cb), NULL); + g_signal_connect(G_OBJECT(rend), "editing-canceled", G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list); +#endif + g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), list); + g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); +#if GTK_CHECK_VERSION(2,6,0) + g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); +#endif + + /* idle */ + rend = gtk_cell_renderer_text_new(); + g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, + "markup", IDLE_COLUMN, + "visible", IDLE_VISIBLE_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + NULL); + } else if (emblem == i) { + /* emblem */ + rend = gtk_cell_renderer_pixbuf_new(); + g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, NULL); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, "pixbuf", EMBLEM_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + "visible", EMBLEM_VISIBLE_COLUMN, NULL); + + } else if (protocol_icon == i) { + /* protocol icon */ + rend = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, + "pixbuf", PROTOCOL_ICON_COLUMN, + "visible", PROTOCOL_ICON_VISIBLE_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + NULL); + g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL); + + } else if (buddy_icon == i) { + /* buddy icon */ + rend = gtk_cell_renderer_pixbuf_new(); + g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + "visible", BUDDY_ICON_VISIBLE_COLUMN, + NULL); + } + + }/* end for loop */ + +} + static void pidgin_blist_show(PurpleBuddyList *list) { PidginBuddyListPrivate *priv; void *handle; - GtkCellRenderer *rend; GtkTreeViewColumn *column; GtkWidget *menu; GtkWidget *ebox; @@ -5255,6 +5389,8 @@ gtkblist = PIDGIN_BLIST(list); priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist); + priv->current_theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"), "blist")); + gtkblist->empty_avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); gdk_pixbuf_fill(gtkblist->empty_avatar, 0x00000000); @@ -5287,8 +5423,8 @@ gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu), blist_menu, NULL); pidgin_load_accels(); - g_signal_connect(G_OBJECT(accel_group), "accel-changed", - G_CALLBACK(pidgin_save_accels_cb), NULL); + g_signal_connect(G_OBJECT(accel_group), "accel-changed", G_CALLBACK(pidgin_save_accels_cb), NULL); + menu = gtk_item_factory_get_widget(gtkblist->ift, ""); gtkblist->menutray = pidgin_menu_tray_new(); gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtkblist->menutray); @@ -5438,105 +5574,16 @@ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); + /* expander columns */ column = gtk_tree_view_column_new(); gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); gtk_tree_view_column_set_visible(column, FALSE); gtk_tree_view_set_expander_column(GTK_TREE_VIEW(gtkblist->treeview), column); - gtkblist->text_column = column = gtk_tree_view_column_new (); - rend = pidgin_cell_renderer_expander_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "visible", GROUP_EXPANDER_VISIBLE_COLUMN, - "expander-visible", GROUP_EXPANDER_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "sensitive", GROUP_EXPANDER_COLUMN, - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - NULL); - - rend = pidgin_cell_renderer_expander_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "expander-visible", CONTACT_EXPANDER_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "sensitive", CONTACT_EXPANDER_COLUMN, - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - "visible", CONTACT_EXPANDER_VISIBLE_COLUMN, - NULL); - - rend = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "pixbuf", STATUS_ICON_COLUMN, - "visible", STATUS_ICON_VISIBLE_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - NULL); - g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); - - gtkblist->text_rend = rend = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start (column, rend, TRUE); - gtk_tree_view_column_set_attributes(column, rend, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - "markup", NAME_COLUMN, - NULL); -#if GTK_CHECK_VERSION(2,6,0) - g_signal_connect(G_OBJECT(rend), "editing-started", G_CALLBACK(gtk_blist_renderer_editing_started_cb), NULL); - g_signal_connect(G_OBJECT(rend), "editing-canceled", G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list); -#endif - g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), list); - g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); -#if GTK_CHECK_VERSION(2,6,0) - g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); -#endif - gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); - - rend = gtk_cell_renderer_text_new(); - g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "markup", IDLE_COLUMN, - "visible", IDLE_VISIBLE_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - NULL); - - rend = gtk_cell_renderer_pixbuf_new(); - g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, NULL); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, "pixbuf", EMBLEM_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - "visible", EMBLEM_VISIBLE_COLUMN, NULL); - - rend = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "pixbuf", PROTOCOL_ICON_COLUMN, - "visible", PROTOCOL_ICON_VISIBLE_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - NULL); - g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL); - - rend = gtk_cell_renderer_pixbuf_new(); - g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - "visible", BUDDY_ICON_VISIBLE_COLUMN, - NULL); - + /* everything else column */ + gtkblist->text_column = gtk_tree_view_column_new (); + gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->text_column); + pidgin_blist_build_layout(list); g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL); g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL); @@ -5963,13 +6010,21 @@ GtkTreeIter iter; GtkTreePath *path; gboolean expanded; - GdkColor bgcolor; + GdkColor *bgcolor = NULL; GdkPixbuf *avatar = NULL; + PidginBlistTheme *theme = NULL; if(!insert_node(list, gnode, &iter)) return; - bgcolor = gtkblist->treeview->style->bg[GTK_STATE_ACTIVE]; + if ((theme = pidgin_blist_get_theme()) == NULL) + bgcolor = NULL; + else if (purple_blist_node_get_bool(gnode, "collapsed") || count <= 0) + bgcolor = pidgin_blist_theme_get_collapsed_background_color(theme); + else bgcolor = pidgin_blist_theme_get_expanded_background_color(theme); + + if (bgcolor == NULL) + bgcolor = &(gtkblist->treeview->style->bg[GTK_STATE_ACTIVE]); path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(gtkblist->treeview), path); @@ -5987,7 +6042,7 @@ STATUS_ICON_COLUMN, NULL, NAME_COLUMN, title, NODE_COLUMN, gnode, - /* BGCOLOR_COLUMN, &bgcolor, */ + BGCOLOR_COLUMN, bgcolor, GROUP_EXPANDER_COLUMN, TRUE, GROUP_EXPANDER_VISIBLE_COLUMN, TRUE, CONTACT_EXPANDER_VISIBLE_COLUMN, FALSE, @@ -6010,6 +6065,9 @@ char *mark, *esc; PurpleBlistNode *selected_node = NULL; GtkTreeIter iter; + FontColorPair *pair; + gchar *text_color, *text_font; + PidginBlistTheme *theme; group = (PurpleGroup*)gnode; @@ -6025,8 +6083,20 @@ purple_blist_get_group_size(group, FALSE)); } + theme = pidgin_blist_get_theme(); + if (theme == NULL) + pair = NULL; + else if + (expanded) pair = pidgin_blist_theme_get_expanded_text_info(theme); + else pair = pidgin_blist_theme_get_collapsed_text_info(theme); + + + text_color = (selected || pair == NULL || pair->color == NULL) ? "black" : pair->color; + text_font = (pair == NULL || pair->font == NULL) ? "" : pair->font; + esc = g_markup_escape_text(group->name, -1); - mark = g_strdup_printf("%s%s", esc ? esc : "", group_count); + mark = g_strdup_printf("%s%s", + text_color, text_font, esc ? esc : "", group_count); g_free(esc); return mark; @@ -6034,14 +6104,15 @@ static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *node) { - PurplePresence *presence; + PurplePresence *presence = purple_buddy_get_presence(buddy); GdkPixbuf *status, *avatar, *emblem, *prpl_icon; + GdkColor *color = NULL; char *mark; char *idle = NULL; gboolean expanded = ((struct _pidgin_blist_node *)(node->parent->ui_data))->contact_expanded; gboolean selected = (gtkblist->selected_node == node); gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); - presence = purple_buddy_get_presence(buddy); + PidginBlistTheme *theme; if (editing_blist) return; @@ -6065,35 +6136,38 @@ emblem = pidgin_blist_get_emblem((PurpleBlistNode*) buddy); mark = pidgin_blist_get_name_markup(buddy, selected, TRUE); + theme = pidgin_blist_get_theme(); + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time") && - purple_presence_is_idle(presence) && - !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons")) + purple_presence_is_idle(presence) && !biglist) { time_t idle_secs = purple_presence_get_idle_time(presence); if (idle_secs > 0) { + FontColorPair *pair = NULL; + const gchar *textcolor; time_t t; int ihrs, imin; time(&t); + ihrs = (t - idle_secs) / 3600; imin = ((t - idle_secs) / 60) % 60; - idle = g_strdup_printf("%d:%02d", ihrs, imin); - } - } - - if (purple_presence_is_idle(presence)) - { - if (idle && !selected) { - char *i2 = g_strdup_printf("%s", - dim_grey(), idle); - g_free(idle); - idle = i2; + + if (!selected && theme != NULL && (pair = pidgin_blist_theme_get_idle_text_info(theme)) != NULL && pair->color != NULL) + textcolor = pair->color; + else textcolor = "black"; + + idle = g_strdup_printf("%d:%02d", textcolor, + (pair == NULL || pair->font == NULL) ? "" : pair->font, ihrs, imin); } } prpl_icon = pidgin_create_prpl_icon(buddy->account, PIDGIN_PRPL_ICON_SMALL); + if (theme != NULL) + color = pidgin_blist_theme_get_contact_color(theme); + gtk_tree_store_set(gtkblist->treemodel, iter, STATUS_ICON_COLUMN, status, STATUS_ICON_VISIBLE_COLUMN, TRUE, @@ -6106,7 +6180,7 @@ EMBLEM_VISIBLE_COLUMN, (emblem != NULL), PROTOCOL_ICON_COLUMN, prpl_icon, PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"), - BGCOLOR_COLUMN, NULL, + BGCOLOR_COLUMN, color, CONTACT_EXPANDER_COLUMN, NULL, CONTACT_EXPANDER_VISIBLE_COLUMN, expanded, GROUP_EXPANDER_VISIBLE_COLUMN, FALSE, @@ -6164,19 +6238,37 @@ if(gtknode->contact_expanded) { GdkPixbuf *status; - char *mark; + gchar *mark; + GdkColor *color = NULL; + PidginBlistTheme *theme = pidgin_blist_get_theme(); + gboolean selected = (gtkblist->selected_node == cnode); + + mark = g_markup_escape_text(purple_contact_get_alias(contact), -1); + + if (theme != NULL) { + FontColorPair *pair = pidgin_blist_theme_get_contact_text_info(theme); + color = pidgin_blist_theme_get_contact_color(theme); + + if (pair != NULL) { + gchar *temp = g_strdup_printf("%s", + (selected || pair->color == NULL) ? "black" : pair->color, + (pair->font == NULL) ? "" : pair->font, mark); + + g_free(mark); + mark = temp; + } + } status = pidgin_blist_get_status_icon(cnode, biglist? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL); - - mark = g_markup_escape_text(purple_contact_get_alias(contact), -1); + gtk_tree_store_set(gtkblist->treemodel, &iter, STATUS_ICON_COLUMN, status, STATUS_ICON_VISIBLE_COLUMN, TRUE, NAME_COLUMN, mark, IDLE_COLUMN, NULL, IDLE_VISIBLE_COLUMN, FALSE, - BGCOLOR_COLUMN, NULL, + BGCOLOR_COLUMN, color, BUDDY_ICON_COLUMN, NULL, CONTACT_EXPANDER_COLUMN, TRUE, CONTACT_EXPANDER_VISIBLE_COLUMN, TRUE, @@ -6244,12 +6336,16 @@ if(purple_account_is_connected(chat->account)) { GtkTreeIter iter; GdkPixbuf *status, *avatar, *emblem, *prpl_icon; - char *mark; + gchar *mark, *color, *font, *tmp; gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); PidginBlistNode *ui; PurpleConversation *conv; gboolean hidden; + GdkColor *bgcolor = NULL; + FontColorPair *pair; + PidginBlistTheme *theme; + gboolean selected = (gtkblist->selected_node == node); if (!insert_node(list, node, &iter)) return; @@ -6269,14 +6365,29 @@ else avatar = NULL; - mark = g_markup_escape_text(purple_chat_get_name(chat), -1); - if (hidden) { - char *bold = g_strdup_printf("%s", mark); - g_free(mark); - mark = bold; - } + mark = g_markup_escape_text(purple_chat_get_name(chat), -1); + + theme = pidgin_blist_get_theme(); + + if (theme == NULL) + pair = NULL; + else if (hidden) + pair = pidgin_blist_theme_get_unread_message_text_info(theme); + else pair = pidgin_blist_theme_get_online_text_info(theme); + + font = (pair == NULL || pair->font == NULL) ? "" : g_strdup(pair->font); + color = (selected || pair == NULL || pair->color == NULL) ? "black" : g_strdup(pair->color); + + tmp = g_strdup_printf("%s", + font, color, hidden ? "bold" : "normal", mark); + + g_free(mark); + mark = tmp; prpl_icon = pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL); + + if (theme != NULL) + bgcolor = pidgin_blist_theme_get_contact_color(theme); gtk_tree_store_set(gtkblist->treemodel, &iter, STATUS_ICON_COLUMN, status, @@ -6288,6 +6399,7 @@ PROTOCOL_ICON_COLUMN, prpl_icon, PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"), NAME_COLUMN, mark, + BGCOLOR_COLUMN, bgcolor, GROUP_EXPANDER_VISIBLE_COLUMN, FALSE, -1); @@ -6300,6 +6412,7 @@ g_object_unref(avatar); if(prpl_icon) g_object_unref(prpl_icon); + } else { pidgin_blist_hide_node(list, node, TRUE); } @@ -7159,6 +7272,33 @@ (GSourceFunc)buddy_signonoff_timeout_cb, buddy); } +void +pidgin_blist_set_theme(PidginBlistTheme *theme) +{ + PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist); + PurpleBuddyList *list = purple_get_blist(); + + if (theme != NULL) + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/theme", + purple_theme_get_name(PURPLE_THEME(theme))); + else purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/theme", ""); + + priv->current_theme = theme; + + pidgin_blist_build_layout(list); + + pidgin_blist_refresh(list); +} + + +PidginBlistTheme * +pidgin_blist_get_theme() +{ + PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist); + + return priv->current_theme; +} + void pidgin_blist_init(void) { void *gtk_blist_handle = pidgin_blist_get_handle(); @@ -7184,6 +7324,9 @@ purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/width", 250); /* Golden ratio, baby */ purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/height", 405); /* Golden ratio, baby */ purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay", 500); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/theme", ""); + + purple_theme_manager_register_type(g_object_new(PIDGIN_TYPE_BLIST_THEME_LOADER, "type", "blist", NULL)); /* Register our signals */ purple_signal_register(gtk_blist_handle, "gtkblist-hiding", diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkblist.h --- a/pidgin/gtkblist.h Thu Aug 28 09:20:22 2008 +0000 +++ b/pidgin/gtkblist.h Thu Aug 28 13:00:23 2008 +0000 @@ -59,6 +59,7 @@ #include "pidgin.h" #include "blist.h" +#include "gtkblist-theme.h" /************************************************************************** * @name Structures @@ -250,6 +251,19 @@ */ void pidgin_blist_add_alert(GtkWidget *widget); +/** + * Sets the current theme for Pidgin to use + * + * @param theme the new theme to use + */ +void pidgin_blist_set_theme(PidginBlistTheme *theme); + +/** + * Gets Pidgin's current buddy list theme + * + * @returns the current theme + */ +PidginBlistTheme *pidgin_blist_get_theme(void); /************************************************************************** * @name GTK+ Buddy List sorting functions diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkicon-loader.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkicon-loader.c Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,115 @@ +/* + * PidginIconThemeLoader for Pidgin + * + * 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 "gtkicon-loader.h" +#include "gtkstatus-icon-theme.h" + +#include "xmlnode.h" + +/***************************************************************************** + * Icon Theme Builder + *****************************************************************************/ + +static PurpleTheme * +pidgin_icon_loader_build(const gchar *dir) +{ + xmlnode *root_node = NULL, *sub_node; + gchar *filename_full, *data; + PidginIconTheme *theme = NULL; + + /* Find the theme file */ + g_return_val_if_fail(dir != NULL, NULL); + filename_full = g_build_filename(dir, "theme.xml", NULL); + + if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR)) + root_node = xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-loader"); + + g_free(filename_full); + 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); + + if (xmlnode_get_attrib(root_node, "name") != NULL) { + theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME, + "type", "status-icon", + "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); + + sub_node = xmlnode_get_child(root_node, "icon"); + + while (sub_node){ + pidgin_icon_theme_set_icon(theme, + xmlnode_get_attrib(sub_node, "id"), + xmlnode_get_attrib(sub_node, "file")); + sub_node = xmlnode_get_next_twin(sub_node); + } + } + + xmlnode_free(root_node); + g_free(data); + return PURPLE_THEME(theme); +} + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_icon_theme_loader_class_init (PidginIconThemeLoaderClass *klass) +{ + PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass); + + loader_klass->purple_theme_loader_build = pidgin_icon_loader_build; +} + + +GType +pidgin_icon_theme_loader_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PidginIconThemeLoaderClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_icon_theme_loader_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginIconThemeLoader), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static (PURPLE_TYPE_THEME_LOADER, + "PidginIconThemeLoader", + &info, 0); + } + return type; +} + + diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkicon-loader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkicon-loader.h Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,71 @@ +/** + * @file gtkicon-loader.h Pidgin Icon 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 _PIDGIN_ICON_THEME_LOADER_H_ +#define _PIDGIN_ICON_THEME_LOADER_H_ + +#include +#include +#include "theme-loader.h" + +/** + * A pidgin icon theme loader. Extends PurpleThemeLoader (theme-loader.h) + * This is a class designed to build icon themes + * + * PidginIconThemeLoader is a GObject. + */ +typedef struct _PidginIconThemeLoader PidginIconThemeLoader; +typedef struct _PidginIconThemeLoaderClass PidginIconThemeLoaderClass; + +#define PIDGIN_TYPE_ICON_THEME_LOADER (pidgin_icon_theme_loader_get_type ()) +#define PIDGIN_ICON_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_ICON_THEME_LOADER, PidginIconThemeLoader)) +#define PIDGIN_ICON_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_ICON_THEME_LOADER, PidginIconThemeLoaderClass)) +#define PIDGIN_IS_ICON_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_ICON_THEME_LOADER)) +#define PIDGIN_IS_ICON_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_ICON_THEME_LOADER)) +#define PIDGIN_ICON_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_ICON_THEME_LOADER, PidginIconThemeLoaderClass)) + +struct _PidginIconThemeLoader +{ + PurpleThemeLoader parent; +}; + +struct _PidginIconThemeLoaderClass +{ + PurpleThemeLoaderClass parent_class; +}; + +/**************************************************************************/ +/** @name Pidgin Icon Theme-Loader API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_icon_theme_loader_get_type(void); + +G_END_DECLS +#endif /* _PIDGIN_ICON_THEME_LOADER_H_ */ diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkicon-theme.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkicon-theme.c Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,147 @@ +/* + * Icon Themes for Pidgin + * + * 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 "gtkicon-theme.h" +#include "pidginstock.h" + +#include + +#define PIDGIN_ICON_THEME_GET_PRIVATE(Gobject) \ + ((PidginIconThemePrivate *) ((PIDGIN_ICON_THEME(Gobject))->priv)) + + +/****************************************************************************** + * Structs + *****************************************************************************/ +typedef struct { + /* used to store filenames of diffrent icons */ + GHashTable *icon_files; +} PidginIconThemePrivate; + +/****************************************************************************** + * Globals + *****************************************************************************/ + +static GObjectClass *parent_class = NULL; + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_icon_theme_init(GTypeInstance *instance, + gpointer klass) +{ + PidginIconThemePrivate *priv; + + (PIDGIN_ICON_THEME(instance))->priv = g_new0(PidginIconThemePrivate, 1); + + priv = PIDGIN_ICON_THEME_GET_PRIVATE(instance); + + priv->icon_files = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + g_free); +} + +static void +pidgin_icon_theme_finalize(GObject *obj) +{ + PidginIconThemePrivate *priv; + + priv = PIDGIN_ICON_THEME_GET_PRIVATE(obj); + + g_hash_table_destroy(priv->icon_files); + g_free(priv); + + parent_class->finalize(obj); +} + +static void +pidgin_icon_theme_class_init(PidginIconThemeClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + obj_class->finalize = pidgin_icon_theme_finalize; +} + +GType +pidgin_icon_theme_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PidginIconThemeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_icon_theme_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginIconTheme), + 0, /* n_preallocs */ + pidgin_icon_theme_init, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static(PURPLE_TYPE_THEME, + "PidginIconTheme", + &info, G_TYPE_FLAG_ABSTRACT); + } + return type; +} + + +/***************************************************************************** + * Public API functions + *****************************************************************************/ + +const gchar * +pidgin_icon_theme_get_icon(PidginIconTheme *theme, + const gchar *id) +{ + PidginIconThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_ICON_THEME(theme), NULL); + + priv = PIDGIN_ICON_THEME_GET_PRIVATE(theme); + + return g_hash_table_lookup(priv->icon_files, id); +} + +void +pidgin_icon_theme_set_icon(PidginIconTheme *theme, + const gchar *id, + const gchar *filename) +{ + PidginIconThemePrivate *priv; + g_return_if_fail(PIDGIN_IS_ICON_THEME(theme)); + + priv = PIDGIN_ICON_THEME_GET_PRIVATE(theme); + + if (filename != NULL) + g_hash_table_replace(priv->icon_files, + g_strdup(id), + g_strdup(filename)); + else g_hash_table_remove(priv->icon_files, id); +} diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkicon-theme.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkicon-theme.h Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,92 @@ +/** + * @file icon-theme.h Pidgin Icon Theme Class API + */ + +/* pidgin + * + * 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 + */ + +#ifndef _PIDGIN_ICON_THEME_H_ +#define _PIDGIN_ICON_THEME_H_ + +#include +#include +#include "theme.h" + +/** + * extends PurpleTheme (theme.h) + * A pidgin icon theme. + * This object represents a Pidgin icon theme. + * + * PidginIconTheme is a PurpleTheme Object. + */ +typedef struct _PidginIconTheme PidginIconTheme; +typedef struct _PidginIconThemeClass PidginIconThemeClass; + +#define PIDGIN_TYPE_ICON_THEME (pidgin_icon_theme_get_type ()) +#define PIDGIN_ICON_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_ICON_THEME, PidginIconTheme)) +#define PIDGIN_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_ICON_THEME, PidginIconThemeClass)) +#define PIDGIN_IS_ICON_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_ICON_THEME)) +#define PIDGIN_IS_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_ICON_THEME)) +#define PIDGIN_ICON_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_ICON_THEME, PidginIconThemeClass)) + +struct _PidginIconTheme +{ + PurpleTheme parent; + gpointer priv; +}; + +struct _PidginIconThemeClass +{ + PurpleThemeClass parent_class; +}; + +/**************************************************************************/ +/** @name Pidgin Icon Theme API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_icon_theme_get_type(void); + +/** + * Returns a copy of the filename for the icon event or NULL if it is not set + * + * @param event the pidgin icon event to look up + * + * @returns the filename of the icon event + */ +const gchar *pidgin_icon_theme_get_icon(PidginIconTheme *theme, + const gchar *event); +/** + * Sets the filename for a given icon id, setting the icon to NULL will remove the icon from the theme + * + * @param icon_id a string representing what the icon is to be used for + * @param filename the name of the file to be used for the given id + */ +void pidgin_icon_theme_set_icon(PidginIconTheme *theme, + const gchar *icon_id, + const gchar *filename); + +G_END_DECLS +#endif /* _PIDGIN_ICON_THEME_H_ */ diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Thu Aug 28 09:20:22 2008 +0000 +++ b/pidgin/gtkprefs.c Thu Aug 28 13:00:23 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" @@ -47,6 +49,7 @@ #include "gtkprefs.h" #include "gtksavedstatuses.h" #include "gtksound.h" +#include "gtkstatus-icon-theme.h" #include "gtkthemes.h" #include "gtkutils.h" #include "pidginstock.h" @@ -56,6 +59,8 @@ #define PROXYUSER 2 #define PROXYPASS 3 +#define PREFS_OPTIMAL_ICON_SIZE 32 + static int sound_row_sel = 0; static GtkWidget *prefsnotebook; @@ -69,6 +74,12 @@ static int notebook_page = 0; static GtkTreeRowReference *previous_smiley_row = NULL; +static gboolean prefs_themes_unsorted = TRUE; +static GtkListStore *prefs_sound_themes; +static GtkListStore *prefs_blist_themes; +static GtkListStore *prefs_status_icon_themes; + + /* * PROTOTYPES */ @@ -546,6 +557,213 @@ gtk_drag_finish(dc, FALSE, FALSE, t); } +/* Rebuild the markup for the sound theme selection for "(Custom)" themes */ +static void +pref_sound_generate_markup() +{ + gboolean print_custom, customized; + const gchar *name, *author, *description, *current_theme; + gchar *markup; + PurpleSoundTheme *theme; + GtkTreeIter iter; + + customized = pidgin_sound_is_customized(); + current_theme = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"); + + 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); + + print_custom = customized && g_str_equal(current_theme, name); + + if (g_str_equal(name, "")) + markup = g_strdup_printf("(Default)%s%s - None\nThe default Pidgin sound theme", + 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("%s%s%s%s%s\n%s", + 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)); + } +} + +/* 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 = NULL, *markup; + const gchar *name, *author, *description; + + if (PURPLE_IS_SOUND_THEME(theme)){ + + image_full = purple_theme_get_image_full(theme); + if (image_full != NULL){ + pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, 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, 2, purple_theme_get_name(theme), -1); + + if (pixbuf != NULL) + gdk_pixbuf_unref(pixbuf); + + } else if (PIDGIN_IS_BLIST_THEME(theme) || PIDGIN_IS_STATUS_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){ + pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL); + g_free(image_full); + } else pixbuf = NULL; + + name = purple_theme_get_name(theme); + author = purple_theme_get_author(theme); + description = purple_theme_get_description(theme); + + markup = g_strdup_printf("%s%s%s\n%s", name, author != NULL ? " - " : "", + author != NULL ? author : "", description != NULL ? description : ""); + + 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 */ +static void +prefs_themes_init() +{ + GdkPixbuf *pixbuf = NULL; + gchar *filename; + GtkTreeIter iter; + + filename = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL); + pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL); + g_free(filename); + + /* sound themes */ + prefs_sound_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + gtk_list_store_append(prefs_sound_themes, &iter); + gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, "", -1); + + /* blist themes */ + prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + gtk_list_store_append(prefs_blist_themes, &iter); + gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, "(Default) - None\n" + "The default Pidgin buddy list theme", 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, "(Default) - None\n" + "The default Pidgin status icon theme", 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; + gboolean unset = TRUE; + + 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_CHECK_VERSION(2,6,0) + g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); +#endif*/ + + 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); + unset = FALSE; + } + + g_free(theme); + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); + } + + if (unset) + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0); + + return combo_box; +} + +/* sets the current sound theme */ +static void +prefs_set_sound_theme_cb(GtkComboBox *combo_box, gpointer user_data) +{ + gint i; + gchar *pref; + gchar *new_theme; + gboolean sucess; + GtkTreeIter new_iter; + + + sucess = gtk_combo_box_get_active_iter(combo_box, &new_iter); + g_return_if_fail(sucess); + + gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &new_iter, 2, &new_theme, -1); + + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/theme", new_theme); + + /* New theme removes all customization */ + 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); + } + + /* gets rid of the "(Custom)" from the last selection */ + pref_sound_generate_markup(); + + gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)")); + + g_free(new_theme); +} + /* Does same as normal sort, except "none" is sorted first */ static gint pidgin_sort_smileys (GtkTreeModel *model, GtkTreeIter *a, @@ -922,6 +1140,40 @@ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0); } +/* sets the current buddy list theme */ +static void +prefs_set_blist_theme_cb(GtkComboBox *combo_box, gpointer user_data) +{ + PidginBlistTheme *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_blist_themes), &iter, 2, &name, -1); + + theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(name, "blist")); + g_free(name); + + pidgin_blist_set_theme(theme); +} + +/* sets the current icon theme */ +static void +prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data) +{ + PidginStatusIconTheme *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_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon")); + g_free(name); + + pidgin_stock_load_status_icon_theme(theme); +} + static GtkWidget * interface_page(void) { @@ -929,6 +1181,7 @@ GtkWidget *vbox; GtkWidget *vbox2; GtkWidget *label; + GtkWidget *combo_box; GtkSizeGroup *sg; GList *names = NULL; @@ -937,6 +1190,19 @@ sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + /* Buddy List Themes */ + vbox = pidgin_make_frame(ret, _("Buddy List Theme")); + + 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 "/status/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, PIDGIN_PREFS_ROOT "/docklet/show", @@ -1736,6 +2002,8 @@ g_free(pref); gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)")); + + pref_sound_generate_markup(); } static void @@ -1758,6 +2026,8 @@ */ if (sound == sound_row_sel) gtk_entry_set_text(GTK_ENTRY(sound_entry), filename); + + pref_sound_generate_markup(); } static void select_sound(GtkWidget *button, gpointer being_NULL_is_fun) @@ -1826,13 +2096,15 @@ if (sound_entry) gtk_entry_set_text(GTK_ENTRY(sound_entry), (file && *file != '\0') ? file : _("(default)")); g_value_unset (&val); + + pref_sound_generate_markup(); } static GtkWidget * sound_page(void) { GtkWidget *ret; - GtkWidget *vbox, *sw, *button; + GtkWidget *vbox, *sw, *button, *combo_box; GtkSizeGroup *sg; GtkTreeIter iter; GtkWidget *event_view; @@ -1922,7 +2194,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 +2205,14 @@ gtk_box_set_child_packing(GTK_BOX(vbox->parent->parent->parent), vbox->parent->parent, TRUE, TRUE, 0, GTK_PACK_START); + /* 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); + + g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_sound_theme_cb, NULL); + + /* 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,7 +2445,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 :-( */ /* (that should have been "effect," right?) */ @@ -2261,6 +2545,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"); diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtksound.c --- a/pidgin/gtksound.c Thu Aug 28 09:20:22 2008 +0000 +++ b/pidgin/gtksound.c Thu Aug 28 13:00:23 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; + const char *theme_name; + PurpleSoundTheme *theme; if ((event == PURPLE_SOUND_BUDDY_ARRIVE) && mute_login_sounds) return; @@ -570,16 +575,35 @@ 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 = 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(filename); } @@ -587,6 +611,29 @@ 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, diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtksound.h --- a/pidgin/gtksound.h Thu Aug 28 09:20:22 2008 +0000 +++ b/pidgin/gtksound.h Thu Aug 28 13:00:23 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_ */ diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkstatus-icon-theme.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkstatus-icon-theme.c Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,73 @@ +/* + * Status Icon Themes for Pidgin + * + * 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 "gtkstatus-icon-theme.h" + +/****************************************************************************** + * Globals + *****************************************************************************/ +static GObjectClass *parent_class = NULL; + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_status_icon_theme_finalize(GObject *obj) +{ + parent_class->finalize(obj); +} + +static void +pidgin_status_icon_theme_class_init(PidginStatusIconThemeClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + obj_class->finalize = pidgin_status_icon_theme_finalize; +} + +GType +pidgin_status_icon_theme_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PidginStatusIconThemeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_status_icon_theme_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginStatusIconTheme), + 0, /* n_preallocs */ + NULL, + NULL, /* value table */ + }; + type = g_type_register_static(PIDGIN_TYPE_ICON_THEME, + "PidginStatusIconTheme", + &info, 0); + } + return type; +} diff -r e9eb71dc21af -r e0a596a0a020 pidgin/gtkstatus-icon-theme.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkstatus-icon-theme.h Thu Aug 28 13:00:23 2008 +0000 @@ -0,0 +1,71 @@ +/** + * @file status_icon-theme.h Pidgin Icon Theme Class API + */ + +/* pidgin + * + * 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 + */ + +#ifndef _PIDGIN_STATUS_ICON_THEME_H_ +#define _PIDGIN_STATUS_ICON_THEME_H_ + +#include +#include "gtkicon-theme.h" + +/** + * extends PidginIconTheme (gtkicon-theme.h) + * A pidgin status icon theme. + * This object represents a Pidgin status icon theme. + * + * PidginStatusIconTheme is a PidginIconTheme Object. + */ +typedef struct _PidginStatusIconTheme PidginStatusIconTheme; +typedef struct _PidginStatusIconThemeClass PidginStatusIconThemeClass; + +#define PIDGIN_TYPE_STATUS_ICON_THEME (pidgin_status_icon_theme_get_type ()) +#define PIDGIN_STATUS_ICON_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_STATUS_ICON_THEME, PidginStatusIconTheme)) +#define PIDGIN_STATUS_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_STATUS_ICON_THEME, PidginStatusIconThemeClass)) +#define PIDGIN_IS_STATUS_ICON_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_STATUS_ICON_THEME)) +#define PIDGIN_IS_STATUS_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_STATUS_ICON_THEME)) +#define PIDGIN_STATUS_ICON_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_STATUS_ICON_THEME, PidginStatusIconThemeClass)) + +struct _PidginStatusIconTheme +{ + PidginIconTheme parent; +}; + +struct _PidginStatusIconThemeClass +{ + PidginIconThemeClass parent_class; +}; + +/**************************************************************************/ +/** @name Pidgin Status Icon Theme API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_status_icon_theme_get_type(void); + +G_END_DECLS +#endif /* _PIDGIN_STATUS_ICON_THEME_H_ */ diff -r e9eb71dc21af -r e0a596a0a020 pidgin/pidginstock.c --- a/pidgin/pidginstock.c Thu Aug 28 09:20:22 2008 +0000 +++ b/pidgin/pidginstock.c Thu Aug 28 13:00:23 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 }, @@ -96,7 +111,7 @@ { PIDGIN_STOCK_EDIT, N_("_Edit"), 0, 0, NULL } }; -static struct SizedStockIcon { +typedef struct { const char *name; const char *dir; const char *filename; @@ -108,18 +123,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 }, @@ -173,37 +179,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, "tray", "tray-new-im.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) { @@ -223,55 +248,10 @@ return filename; } -static void -add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir, - gboolean rtl, const char *size, const char *file) -{ - char *filename; - GtkIconSource *source; - - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, file, NULL); - source = gtk_icon_source_new(); - gtk_icon_source_set_filename(source, filename); - gtk_icon_source_set_direction(source, GTK_TEXT_DIR_LTR); - gtk_icon_source_set_direction_wildcarded(source, !rtl); - gtk_icon_source_set_size(source, sizeid); - gtk_icon_source_set_size_wildcarded(source, FALSE); - gtk_icon_source_set_state_wildcarded(source, TRUE); - gtk_icon_set_add_source(iconset, source); - gtk_icon_source_free(source); - - if (sizeid == gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)) { - source = gtk_icon_source_new(); - gtk_icon_source_set_filename(source, filename); - gtk_icon_source_set_direction_wildcarded(source, TRUE); - gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU); - gtk_icon_source_set_size_wildcarded(source, FALSE); - gtk_icon_source_set_state_wildcarded(source, TRUE); - gtk_icon_set_add_source(iconset, source); - gtk_icon_source_free(source); - } - g_free(filename); - - if (rtl) { - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, "rtl", file, NULL); - source = gtk_icon_source_new(); - gtk_icon_source_set_filename(source, filename); - gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL); - gtk_icon_source_set_size(source, sizeid); - gtk_icon_source_set_size_wildcarded(source, FALSE); - gtk_icon_source_set_state_wildcarded(source, TRUE); - gtk_icon_set_add_source(iconset, source); - g_free(filename); - gtk_icon_source_free(source); - } - - -} /* Altered from do_colorshift in gnome-panel */ static void -do_alphashift (GdkPixbuf *dest, GdkPixbuf *src) +do_alphashift(GdkPixbuf *dest, GdkPixbuf *src) { gint i, j; gint width, height, has_alpha, srcrowstride, destrowstride; @@ -305,25 +285,48 @@ } } -/* TODO: This is almost certainly not the best way to do this, but it's late, I'm tired, - * we're a few hours from getting this thing out, and copy/paste is EASY. - */ +static gchar * +find_icon_file(PidginStatusIconTheme *theme, const gchar *size, SizedStockIcon sized_icon, gboolean rtl) +{ + const gchar *file, *dir; + gchar *file_full = NULL; + + if (theme != NULL) { + file = pidgin_icon_theme_get_icon(PIDGIN_ICON_THEME(theme), sized_icon.name); + dir = purple_theme_get_dir(PURPLE_THEME(theme)); + + if (rtl) + file_full = g_build_filename(dir, size, "rtl", file, NULL); + else file_full = g_build_filename(dir, size, file, NULL); + + if (g_file_test(file_full, G_FILE_TEST_IS_REGULAR)) + return file_full; + + g_free(file_full); + } + + if (rtl) + return g_build_filename(DATADIR, "pixmaps", "pidgin", sized_icon.dir, size, sized_icon.filename, NULL); + else return g_build_filename(DATADIR, "pixmaps", "pidgin", sized_icon.dir, size, sized_icon.filename, NULL); +} + static void -add_translucent_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir, - gboolean rtl, const char *size, const char *file) +add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, PidginStatusIconTheme *theme, + const char *size, SizedStockIcon sized_icon, gboolean translucent) { char *filename; - GtkIconSource *source; + GtkIconSource *source; GdkPixbuf *pixbuf; - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, file, NULL); + filename = find_icon_file(theme, size, sized_icon, FALSE); pixbuf = gdk_pixbuf_new_from_file(filename, NULL); - do_alphashift(pixbuf, pixbuf); + if (translucent) + do_alphashift(pixbuf, pixbuf); source = gtk_icon_source_new(); - gtk_icon_source_set_pixbuf(source, pixbuf); + gtk_icon_source_set_pixbuf(source, pixbuf); gtk_icon_source_set_direction(source, GTK_TEXT_DIR_LTR); - gtk_icon_source_set_direction_wildcarded(source, !rtl); + gtk_icon_source_set_direction_wildcarded(source, !sized_icon.rtl); gtk_icon_source_set_size(source, sizeid); gtk_icon_source_set_size_wildcarded(source, FALSE); gtk_icon_source_set_state_wildcarded(source, TRUE); @@ -343,12 +346,15 @@ g_free(filename); g_object_unref(pixbuf); - if (rtl) { - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, "rtl", file, NULL); - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); - do_alphashift(pixbuf, pixbuf); + if (sized_icon.rtl) { + filename = find_icon_file(theme, size, sized_icon, TRUE); + pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + if (translucent) + do_alphashift(pixbuf, pixbuf); + source = gtk_icon_source_new(); - gtk_icon_source_set_pixbuf(source, pixbuf); + gtk_icon_source_set_pixbuf(source, pixbuf); + gtk_icon_source_set_filename(source, filename); gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL); gtk_icon_source_set_size(source, sizeid); gtk_icon_source_set_size_wildcarded(source, FALSE); @@ -358,25 +364,92 @@ g_object_unref(pixbuf); gtk_icon_source_free(source); } +} + +/***************************************************************************** + * Public API functions + *****************************************************************************/ + +void +pidgin_stock_load_status_icon_theme(PidginStatusIconTheme *theme) +{ + GtkIconFactory *icon_factory; + gint i; + GtkIconSet *normal; + GtkIconSet *translucent = NULL; + GtkWidget *win; + + if (theme != NULL) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/status/icon-theme", + purple_theme_get_name(PURPLE_THEME(theme))); + purple_prefs_set_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", + purple_theme_get_dir(PURPLE_THEME(theme))); + } + else { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/status/icon-theme", ""); + purple_prefs_set_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", ""); + } + + 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(); + +#define ADD_SIZED_ICON(name, size) if (sized_status_icons[i].name) { \ + add_sized_icon(normal, name, theme, size, sized_status_icons[i], FALSE); \ + if (sized_status_icons[i].translucent_name) \ + add_sized_icon(translucent, name, theme, size, sized_status_icons[i], TRUE); \ + } + 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 + + 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; + PidginIconThemeLoader *loader; + const gchar *path = NULL; if (stock_initted) return; stock_initted = TRUE; + /* Setup the status icon theme */ + loader = g_object_new(PIDGIN_TYPE_ICON_THEME_LOADER, "type", "status-icon", NULL); + purple_theme_manager_register_type(PURPLE_THEME_LOADER(loader)); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/status/icon-theme", ""); + purple_prefs_add_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", ""); + /* Setup the icon factory. */ icon_factory = gtk_icon_factory_new(); @@ -386,6 +459,7 @@ win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_realize(win); + /* All non-sized icons */ for (i = 0; i < G_N_ELEMENTS(stock_icons); i++) { GtkIconSource *source; @@ -425,7 +499,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); @@ -433,18 +506,13 @@ large = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_LARGE, 48, 48); huge = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_HUGE, 64, 64); + /* All non-status sized icons */ for (i = 0; i < G_N_ELEMENTS(sized_stock_icons); i++) { - GtkIconSet *iconset; - - iconset = gtk_icon_set_new(); + GtkIconSet *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) +#define ADD_SIZED_ICON(name, size) if (sized_stock_icons[i].name) \ + add_sized_icon(iconset, name, NULL, size, sized_stock_icons[i], FALSE); ADD_SIZED_ICON(microscopic, "11"); ADD_SIZED_ICON(extra_small, "16"); ADD_SIZED_ICON(small, "22"); @@ -455,32 +523,21 @@ 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)); + /* Pre-load Status icon theme - this avoids a bug with displaying the correct icon in the tray, theme is destroyed after*/ + if (purple_prefs_get_string(PIDGIN_PREFS_ROOT "/icon/status/theme") && + (path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir"))) { + + PidginStatusIconTheme *theme = PIDGIN_STATUS_ICON_THEME(purple_theme_loader_build(PURPLE_THEME_LOADER(loader), path)); + pidgin_stock_load_status_icon_theme(theme); + g_object_unref(G_OBJECT(theme)); + + } else pidgin_stock_load_status_icon_theme(NULL); + /* Register the stock items. */ gtk_stock_add_static(stock_items, G_N_ELEMENTS(stock_items)); } diff -r e9eb71dc21af -r e0a596a0a020 pidgin/pidginstock.h --- a/pidgin/pidginstock.h Thu Aug 28 09:20:22 2008 +0000 +++ b/pidgin/pidginstock.h Thu Aug 28 13:00:23 2008 +0000 @@ -24,6 +24,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include +#include "gtkstatus-icon-theme.h" #ifndef _PIDGIN_STOCK_H_ #define _PIDGIN_STOCK_H_ @@ -176,6 +177,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(PidginStatusIconTheme *theme); + /** * Sets up the purple stock repository. */