grim/guifications2

flow: Merged 'autotools_mingw_cleanup' to ('develop').
/*
* 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);
}