grim/guifications2

This was compatibility code for Pidgin 1.x.y; because it was written as a check
for "not 2.0.0" this code would be compiled in for 3.0.0. It looks like the
supporting code elsewhere was removed, causing this to leave an unresolved
symbol in guifications.so, thus causing Pidgin to refuse to load. Now we load
in Pidgin 3.0.0. Whether it works or not, I have no clue.
/*
* Guifications - The end all, be all, toaster popup plugin
* Copyright (C) 2003-2008 Gary Kramlich
*
* 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 <glib.h>
#include <gdk/gdk.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_CONFIG_H
# include "../gf_config.h"
#endif
#include "gf_internal.h"
#include <debug.h>
#include <util.h>
#include <xmlnode.h>
#include "gf_item.h"
#include "gf_preferences.h"
#include "gf_theme_info.h"
#include "gf_theme_ops.h"
#include "gf_utils.h"
struct _GfTheme {
gint api_version;
gchar *file;
gchar *path;
GfThemeInfo *info;
GfThemeOptions *ops;
GList *notifications;
GfNotification *master;
};
#include "gf_theme.h"
static GList *probed_themes = NULL;
static GList *loaded_themes = NULL;
/***********************************************************************
* Loading, Unloading, Probing, and Finding ...
**********************************************************************/
GfTheme *
gf_theme_new() {
GfTheme *theme;
theme = g_new0(GfTheme, 1);
return theme;
}
GfTheme *
gf_theme_new_from_file(const gchar *filename) {
GfTheme *theme;
gchar *contents;
gint api_version;
gsize length;
xmlnode *root, *parent, *child;
g_return_val_if_fail(filename, NULL);
if(!g_file_get_contents(filename, &contents, &length, NULL)) {
purple_debug_info("Guifications", "** Error: failed to get file contents\n");
return NULL;
}
if(!(root = xmlnode_from_str(contents, length))) {
purple_debug_info("Guifications", "** Error: Could not parse file\n");
g_free(contents);
return NULL;
}
g_free(contents);
if(!(parent = xmlnode_get_child(root, "theme"))) {
purple_debug_info("Guifications", "** Error: No theme element found\n");
xmlnode_free(root);
return NULL;
}
api_version = atoi(xmlnode_get_attrib(parent, "api"));
if(api_version != GF_THEME_API_VERSION) {
purple_debug_info("Guifications", "** Error: Theme API version mismatch\n");
xmlnode_free(root);
return NULL;
}
/* allocate the theme */
theme = gf_theme_new();
/* info to know */
theme->api_version = api_version;
theme->file = g_strdup(filename);
theme->path = g_path_get_dirname(filename);
/* get the themes info */
if(!(child = xmlnode_get_child(parent, "info"))) {
purple_debug_info("Guifications", "** Error: No info element found\n");
gf_theme_unload(theme);
xmlnode_free(root);
return NULL;
}
if(!(theme->info = gf_theme_info_new_from_xmlnode(child))) {
purple_debug_info("Guifications", "** Error: could not load theme info\n");
gf_theme_unload(theme);
xmlnode_free(root);
return NULL;
}
/* get the themes options */
if(!(child = xmlnode_get_child(parent, "options"))) {
gf_theme_unload(theme);
xmlnode_free(root);
return NULL;
}
theme->ops = gf_theme_options_new_from_xmlnode(child);
/* get all the notifications */
child = xmlnode_get_child(parent, "notification");
while(child) {
GfNotification *notification;
notification = gf_notification_new_from_xmlnode(theme, child);
if(notification)
theme->notifications = g_list_append(theme->notifications,
notification);
child = xmlnode_get_next_twin(child);
}
/* loading was successful free the xmlnode */
xmlnode_free(root);
return theme;
}
GfTheme *
gf_theme_find_theme_by_name(const gchar *name) {
GfTheme *theme;
GList *l;
g_return_val_if_fail(name, NULL);
for(l = loaded_themes; l; l = l->next) {
theme = GF_THEME(l->data);
if(!g_utf8_collate(gf_theme_info_get_name(theme->info), name))
return theme;
}
return NULL;
}
GfTheme *
gf_theme_find_theme_by_filename(const gchar *filename) {
GfTheme *theme;
GList *l;
g_return_val_if_fail(filename, NULL);
for(l = loaded_themes; l; l = l->next) {
theme = GF_THEME(l->data);
if(!g_ascii_strcasecmp(gf_theme_get_filename(theme), filename))
return theme;
}
return NULL;
}
gchar *
gf_theme_strip_name(GfTheme *theme) {
g_return_val_if_fail(theme, NULL);
return gf_theme_info_strip_name(theme->info);
}
gboolean
gf_theme_is_loaded(const gchar *filename) {
GfTheme *theme;
GList *l;
g_return_val_if_fail(filename, FALSE);
for(l = loaded_themes; l; l = l->next) {
theme = GF_THEME(l->data);
if(!g_ascii_strcasecmp(filename, theme->file))
return TRUE;
}
return FALSE;
}
gboolean
gf_theme_is_probed(const gchar *filename) {
g_return_val_if_fail(filename, FALSE);
if(g_list_find_custom(probed_themes, filename, gf_utils_compare_strings))
return TRUE;
else
return FALSE;
}
gboolean
gf_theme_save_to_file(GfTheme *theme, const gchar *filename) {
GList *l;
gchar *api, *data;
FILE *fp = NULL;
xmlnode *root, *parent, *child;
g_return_val_if_fail(theme, FALSE);
g_return_val_if_fail(filename, FALSE);
root = xmlnode_new("guifications");
parent = xmlnode_new_child(root, "theme");
api = g_strdup_printf("%d", GF_THEME_API_VERSION);
xmlnode_set_attrib(parent, "api", api);
g_free(api);
if((child = gf_theme_info_to_xmlnode(theme->info)))
xmlnode_insert_child(parent, child);
if((child = gf_theme_options_to_xmlnode(theme->ops)))
xmlnode_insert_child(parent, child);
for(l = theme->notifications; l; l = l->next) {
if((child = gf_notification_to_xmlnode(GF_NOTIFICATION(l->data))))
xmlnode_insert_child(parent, child);
}
data = xmlnode_to_formatted_str(root, NULL);
fp = g_fopen(filename, "wb");
if(!fp) {
purple_debug_info("guifications", "Error trying to save theme %s\n", filename);
} else {
if(data)
fprintf(fp, "%s", data);
fclose(fp);
}
g_free(data);
xmlnode_free(root);
return TRUE;
}
void
gf_theme_destory(GfTheme *theme) {
GList *l;
g_return_if_fail(theme);
theme->api_version = 0;
if(theme->file)
g_free(theme->file);
if(theme->path)
g_free(theme->path);
if(theme->info)
gf_theme_info_destroy(theme->info);
if(theme->ops)
gf_theme_options_destroy(theme->ops);
for(l = theme->notifications; l; l = l->next)
gf_notification_destroy(GF_NOTIFICATION(l->data));
g_list_free(theme->notifications);
theme->notifications = NULL;
g_free(theme);
theme = NULL;
}
void
gf_theme_load(const gchar *filename) {
GfTheme *theme = NULL;
if((theme = gf_theme_new_from_file(filename)))
loaded_themes = g_list_append(loaded_themes, theme);
}
void
gf_theme_unload(GfTheme *theme) {
g_return_if_fail(theme);
loaded_themes = g_list_remove(loaded_themes, theme);
gf_theme_destory(theme);
}
void
gf_theme_probe(const gchar *filename) {
GfTheme *theme;
gboolean loaded;
g_return_if_fail(filename);
loaded = gf_theme_is_loaded(filename);
if(gf_theme_is_probed(filename))
gf_theme_unprobe(filename);
if(loaded)
gf_theme_unload(gf_theme_find_theme_by_filename(filename));
theme = gf_theme_new_from_file(filename);
if(theme) {
probed_themes = g_list_append(probed_themes, g_strdup(filename));
if(loaded)
loaded_themes = g_list_append(loaded_themes, theme);
else
gf_theme_destory(theme);
}
}
void
gf_themes_probe() {
GDir *dir;
gchar *path = NULL, *probe_dirs[3];
const gchar *file;
gint i;
probe_dirs[0] = g_build_filename(DATADIR, "pixmaps", "pidgin",
"guifications", "themes", NULL);
probe_dirs[1] = g_build_filename(purple_user_dir(), "guifications", "themes",
NULL);
probe_dirs[2] = NULL;
for(i = 0; probe_dirs[i]; i++) {
dir = g_dir_open(probe_dirs[i], 0, NULL);
if(dir) {
while((file = g_dir_read_name(dir))) {
/* disallow themes in hidden dirs */
if(file[0] == '.')
continue;
path = g_build_filename(probe_dirs[i], file, "theme.xml", NULL);
if(path) {
if(g_file_test(path, G_FILE_TEST_EXISTS)) {
purple_debug_info("Guifications", "Probing %s\n", path);
gf_theme_probe(path);
}
g_free(path);
}
}
g_dir_close(dir);
} else if(i == 1) {
/* if the user theme dir doesn't exist, create it */
purple_build_dir(probe_dirs[i], S_IRUSR | S_IWUSR | S_IXUSR);
}
g_free(probe_dirs[i]);
}
}
void
gf_theme_unprobe(const gchar *filename) {
GList *l, *ll;
gchar *file;
g_return_if_fail(filename);
for(l = probed_themes; l; l = ll) {
ll = l->next;
file = (gchar*)l->data;
if(!g_ascii_strcasecmp(file, filename)) {
probed_themes = g_list_remove(probed_themes, file);
g_free(file);
}
}
}
void
gf_themes_unprobe() {
GList *l;
gchar *file;
for(l = probed_themes; l; l = l->next) {
if((file = (gchar*)l->data)) {
purple_debug_info("Guifications", "unprobing %s\n", file);
g_free(file);
}
}
if(probed_themes)
g_list_free(probed_themes);
probed_themes = NULL;
}
/*******************************************************************************
* Theme API
******************************************************************************/
gint
gf_theme_get_api_version(GfTheme *theme) {
g_return_val_if_fail(theme, -1);
return theme->api_version;
}
const gchar *
gf_theme_get_filename(GfTheme *theme) {
g_return_val_if_fail(theme, NULL);
return theme->file;
}
const gchar *
gf_theme_get_path(GfTheme *theme) {
g_return_val_if_fail(theme, NULL);
return theme->path;
}
GfNotification *
gf_theme_get_master(GfTheme *theme) {
g_return_val_if_fail(theme, NULL);
return theme->master;
}
void
gf_theme_set_master(GfTheme *theme, GfNotification *notification) {
g_return_if_fail(theme);
g_return_if_fail(notification);
theme->master = notification;
}
static void
gf_theme_get_supported_func(gpointer key, gpointer val, gpointer data) {
GString *str = data;
gchar *type = key;
gint value = GPOINTER_TO_INT(val);
if(strlen(str->str) != 0)
str = g_string_append(str, ", ");
str = g_string_append(str, type);
if(value > 1)
g_string_append_printf(str, " (%d)", value);
}
gchar *
gf_theme_get_supported_notifications(GfTheme *theme) {
GfNotification *notification;
GHashTable *table;
GList *l;
GString *str;
const gchar *type;
gchar *ret;
gint value;
gpointer pvalue;
g_return_val_if_fail(theme, NULL);
table = g_hash_table_new(g_str_hash, g_str_equal);
for(l = theme->notifications; l; l = l->next) {
notification = GF_NOTIFICATION(l->data);
type = gf_notification_get_type(notification);
if(type && type[0] == '!')
continue;
pvalue = g_hash_table_lookup(table, type);
if(pvalue)
value = GPOINTER_TO_INT(pvalue) + 1;
else
value = 1;
g_hash_table_replace(table, (gpointer)type, GINT_TO_POINTER(value));
}
str = g_string_new("");
g_hash_table_foreach(table, gf_theme_get_supported_func, str);
g_hash_table_destroy(table);
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
void
gf_theme_set_theme_info(GfTheme *theme, GfThemeInfo *info) {
g_return_if_fail(theme);
g_return_if_fail(info);
if(theme->info)
gf_theme_info_destroy(theme->info);
theme->info = info;
}
GfThemeInfo *
gf_theme_get_theme_info(GfTheme *theme) {
g_return_val_if_fail(theme, NULL);
return theme->info;
}
void
gf_theme_set_theme_options(GfTheme *theme, GfThemeOptions *ops) {
g_return_if_fail(theme);
g_return_if_fail(ops);
if(theme->ops)
gf_theme_options_destroy(theme->ops);
theme->ops = ops;
}
GfThemeOptions *
gf_theme_get_theme_options(GfTheme *theme) {
g_return_val_if_fail(theme, NULL);
return theme->ops;
}
void
gf_theme_add_notification(GfTheme *theme, GfNotification *notification) {
const gchar *type = NULL;
g_return_if_fail(theme);
g_return_if_fail(notification);
type = gf_notification_get_type(notification);
if(!g_utf8_collate(GF_NOTIFICATION_MASTER, type)) {
if(theme->master) {
const gchar *name = NULL;
name = gf_theme_info_get_name(theme->info);
purple_debug_info("Guifications",
"Theme %s already has a master notification\n",
name ? name : "(NULL)");
return;
} else {
theme->master = notification;
}
}
theme->notifications = g_list_append(theme->notifications, notification);
}
void
gf_theme_remove_notification(GfTheme *theme, GfNotification *notification) {
const gchar *type = NULL;
g_return_if_fail(theme);
g_return_if_fail(notification);
type = gf_notification_get_type(notification);
if(!g_utf8_collate(GF_NOTIFICATION_MASTER, type)) {
purple_debug_info("Guifications",
"Master notifications can not be removed\n");
return;
}
theme->notifications = g_list_remove(theme->notifications, notification);
}
GList *
gf_theme_get_notifications(GfTheme *theme) {
g_return_val_if_fail(theme, NULL);
return theme->notifications;
}
GList *
gf_themes_get_all() {
return probed_themes;
}
GList *
gf_themes_get_loaded() {
return loaded_themes;
}
void
gf_themes_unload() {
GfTheme *theme;
GList *l = NULL, *ll = NULL;
for(l = loaded_themes; l; l = ll) {
ll = l->next;
theme = GF_THEME(l->data);
if(theme) {
gf_theme_unload(theme);
theme = NULL;
}
}
g_list_free(loaded_themes);
loaded_themes = NULL;
}
void
gf_themes_save_loaded() {
GfTheme *theme;
GList *l = NULL, *s = NULL;
for(l = loaded_themes; l; l = l->next) {
theme = GF_THEME(l->data);
if(theme)
s = g_list_append(s, theme->file);
}
purple_prefs_set_string_list(GF_PREF_LOADED_THEMES, s);
g_list_free(s);
}
void
gf_themes_load_saved(){
GList *s, *l;
gchar *filename = NULL;
for(l = s = purple_prefs_get_string_list(GF_PREF_LOADED_THEMES); s; s = s->next) {
filename = (gchar*)s->data;
if(gf_theme_is_probed(filename))
gf_theme_load(filename);
g_free(filename);
}
g_list_free(l);
}