From 071f1844a8f47e4d67ad3c752441a3d71d9e18e2 Mon Sep 17 00:00:00 2001 From: Patryk Zawadzki Date: Sun, 21 Jun 2009 01:46:36 +0200 Subject: Add a plist parser and set font family, size and default variant. Fixes bug #586387. --- libempathy-gtk/Makefile.am | 4 + libempathy-gtk/empathy-plist.c | 404 +++++++++++++++++++++++++++++++++++ libempathy-gtk/empathy-plist.h | 35 +++ libempathy-gtk/empathy-theme-adium.c | 37 +++- 4 files changed, 478 insertions(+), 2 deletions(-) create mode 100644 libempathy-gtk/empathy-plist.c create mode 100644 libempathy-gtk/empathy-plist.h (limited to 'libempathy-gtk') diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am index 2a4438e57..c38a96f23 100644 --- a/libempathy-gtk/Makefile.am +++ b/libempathy-gtk/Makefile.am @@ -226,11 +226,15 @@ endif if HAVE_WEBKIT libempathy_gtk_handwritten_source += \ + empathy-plist.c \ empathy-theme-adium.c libempathy_gtk_headers += \ + empathy-plist.h \ empathy-theme-adium.h else EXTRA_DIST += \ + empathy-plist.c \ + empathy-plist.h \ empathy-theme-adium.c \ empathy-theme-adium.h endif diff --git a/libempathy-gtk/empathy-plist.c b/libempathy-gtk/empathy-plist.c new file mode 100644 index 000000000..e025d98bc --- /dev/null +++ b/libempathy-gtk/empathy-plist.c @@ -0,0 +1,404 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Christophe Fergeau + * Based on itdb_plist parser from the gtkpod project. + * + * The code contained in this file is free software; you can redistribute + * it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either version + * 2.1 of the License, or (at your option) any later version. + * + * This file 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this code; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "empathy-plist.h" + +static GValue *empathy_plist_parse_node (xmlNode *a_node); + +static void +empathy_plist_value_free (GValue *val) +{ + g_value_unset (val); + g_free (val); +} + +static GValue * +empathy_plist_parse_integer (xmlNode *a_node) +{ + char *str_val; + char *end_ptr; + gint int_val; + GValue *value; + + str_val = (char *) xmlNodeGetContent (a_node); + int_val = strtol (str_val, &end_ptr, 0); + if (*end_ptr != '\0') { + xmlFree (str_val); + return NULL; + } + xmlFree (str_val); + + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_INT); + g_value_set_int (value, int_val); + + return value; +} + +static GValue * +empathy_plist_parse_string (xmlNode *a_node) +{ + char *str_val; + GValue *value; + + str_val = (char *) xmlNodeGetContent (a_node); + + value = g_new0 (GValue, 1); + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, str_val); + + xmlFree (str_val); + + return value; +} + +static GValue * +empathy_plist_parse_real (xmlNode *a_node) +{ + char *str_val; + char *end_ptr; + gfloat double_val; + GValue *value; + + str_val = (char *) xmlNodeGetContent (a_node); + double_val = g_ascii_strtod (str_val, &end_ptr); + if (*end_ptr != '\0') { + xmlFree (str_val); + return NULL; + } + xmlFree (str_val); + + value = g_new0 (GValue, 1); + g_value_init (value, G_TYPE_DOUBLE); + g_value_set_double (value, double_val); + + return value; +} + +static GValue * +empathy_plist_parse_boolean (xmlNode *a_node) +{ + gboolean bool_val; + GValue *value; + + if (strcmp ((char *) a_node->name, "true") == 0) { + bool_val = TRUE; + } else if (strcmp ((char *) a_node->name, "false") == 0) { + bool_val = FALSE; + } else { + return NULL; + } + + value = g_new0 (GValue, 1); + g_value_init (value, G_TYPE_BOOLEAN); + g_value_set_boolean (value, bool_val); + + return value; +} + +static GValue * +empathy_plist_parse_data (xmlNode *a_node) +{ + char *str_val; + guchar *raw_data; + gsize len; + GString *data_val; + GValue *value; + + str_val = (char *) xmlNodeGetContent (a_node); + raw_data = g_base64_decode (str_val, &len); + xmlFree (str_val); + data_val = g_string_new_len ((char *) raw_data, len); + g_free (raw_data); + + value = g_new0 (GValue, 1); + g_value_init(value, G_TYPE_GSTRING); + g_value_take_boxed (value, data_val); + + return value; +} + +static xmlNode * +empathy_plist_parse_one_dict_entry (xmlNode *a_node, GHashTable *dict) +{ + xmlNode *cur_node = a_node; + xmlChar *key_name; + GValue *value; + + while (cur_node && + (xmlStrcmp(cur_node->name, (xmlChar *) "key") != 0)) { + cur_node = cur_node->next; + } + if (!cur_node) { + return NULL; + } + key_name = xmlNodeGetContent (cur_node); + cur_node = cur_node->next; + while (cur_node && xmlIsBlankNode (cur_node)) { + cur_node = cur_node->next; + } + if (!cur_node) { + xmlFree (key_name); + return NULL; + } + + value = empathy_plist_parse_node (cur_node); + if (value) { + g_hash_table_insert (dict, g_strdup ((char *) key_name), value); + } + xmlFree (key_name); + + return cur_node->next; +} + +static GValue * +empathy_plist_parse_dict (xmlNode *a_node) +{ + xmlNode *cur_node = a_node->children; + GValue *value; + GHashTable *dict; + + dict = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) empathy_plist_value_free); + + while (cur_node) { + if (xmlIsBlankNode (cur_node)) { + cur_node = cur_node->next; + } else { + cur_node = empathy_plist_parse_one_dict_entry (cur_node, dict); + } + } + value = g_new0 (GValue, 1); + value = g_value_init (value, G_TYPE_HASH_TABLE); + g_value_take_boxed (value, dict); + + return value; +} + +typedef GValue *(*ParseCallback) (xmlNode *); + +static ParseCallback empathy_plist_get_parser_for_type (const xmlChar *type); + +static GValue * +empathy_plist_parse_array (xmlNode *a_node) +{ + xmlNode *cur_node = a_node->children; + GValue *value; + GValueArray *array; + + array = g_value_array_new (4); + + while (cur_node) { + if (empathy_plist_get_parser_for_type (cur_node->name)) { + GValue *cur_value; + cur_value = empathy_plist_parse_node (cur_node); + if (cur_value) { + array = g_value_array_append (array, cur_value); + g_value_unset (cur_value); + g_free (cur_value); + } + } + /* When an array contains an element enclosed in "unknown" tags (ie + * non-type ones), we silently skip them since early + * SysInfoExtended files used to have values enclosed within + * tags. + */ + cur_node = cur_node->next; + } + + value = g_new0 (GValue, 1); + value = g_value_init (value, G_TYPE_VALUE_ARRAY); + g_value_take_boxed (value, array); + + return value; +} + +struct Parser { + const char * const type_name; + ParseCallback parser; +}; + +static const struct Parser parsers[] = { {"integer", empathy_plist_parse_integer}, + {"real", empathy_plist_parse_real}, + {"string", empathy_plist_parse_string}, + {"true", empathy_plist_parse_boolean}, + {"false", empathy_plist_parse_boolean}, + {"data", empathy_plist_parse_data}, + {"dict", empathy_plist_parse_dict}, + {"array", empathy_plist_parse_array}, + {NULL, NULL} }; + +static ParseCallback +empathy_plist_get_parser_for_type (const xmlChar *type) +{ + guint i = 0; + + while (parsers[i].type_name) { + if (xmlStrcmp (type, (xmlChar *) parsers[i].type_name) == 0) { + if (parsers[i].parser) { + return parsers[i].parser; + } + } + i++; + } + return NULL; +} + +static GValue * +empathy_plist_parse_node (xmlNode *a_node) +{ + ParseCallback parser; + + g_return_val_if_fail (a_node != NULL, NULL); + parser = empathy_plist_get_parser_for_type (a_node->name); + if (parser) { + return parser (a_node); + } else { + return NULL; + } +} + +static GValue * +empathy_plist_parse (xmlNode * a_node) +{ + xmlNode *cur_node; + if (!a_node) { + return NULL; + } + if (xmlStrcmp (a_node->name, (xmlChar *) "plist") != 0) { + return NULL; + } + cur_node = a_node->xmlChildrenNode; + while (cur_node && (xmlIsBlankNode (cur_node))) { + cur_node = cur_node->next; + } + if (cur_node) { + return empathy_plist_parse_node (cur_node); + } + return NULL; +} + +GValue * +empathy_plist_parse_from_file (const char *filename) +{ + xmlDoc *doc = NULL; + xmlNode *root_element = NULL; + GValue *parsed_doc; + + doc = xmlReadFile (filename, NULL, 0); + + if (!doc) { + return NULL; + } + + root_element = xmlDocGetRootElement (doc); + + parsed_doc = empathy_plist_parse (root_element); + + xmlFreeDoc(doc); + xmlCleanupParser(); + + return parsed_doc; +} + +/** + * empathy_plist_parse_from_memory: + * @data: memory location containing XML plist data to parse + * @len: length in bytes of the string to parse + * + * Parses the XML plist file stored in @data which length is @len + * bytes. If an error occurs during the parsing, + * empathy_plist_parse_from_memory() will return NULL. + * + * Returns: NULL on error, a newly allocated + * #GValue containing a #GHashTable otherwise. + */ +GValue * +empathy_plist_parse_from_memory (const char *data, gsize len) +{ + xmlDoc *doc = NULL; + xmlNode *root_element = NULL; + GValue *parsed_doc; + + doc = xmlReadMemory (data, len, "noname.xml", NULL, 0); + + if (doc == NULL) { + return NULL; + } + + root_element = xmlDocGetRootElement (doc); + + parsed_doc = empathy_plist_parse (root_element); + + xmlFreeDoc(doc); + xmlCleanupParser(); + + return parsed_doc; +} + +gboolean +empathy_plist_get_int (GValue *data, const gchar *key, gint *value) +{ + GHashTable *hash; + GValue *entry; + + if (!data || !G_VALUE_HOLDS (data, G_TYPE_HASH_TABLE)) { + return FALSE; + } + + hash = g_value_get_boxed (data); + entry = g_hash_table_lookup (hash, key); + + if (!entry || !G_VALUE_HOLDS (entry, G_TYPE_INT)) { + return FALSE; + } + + *value = g_value_get_int (entry); + return TRUE; +} + +gboolean +empathy_plist_get_string (GValue *data, const gchar *key, gchar **value) +{ + GHashTable *hash; + GValue *entry; + + if (!data || !G_VALUE_HOLDS (data, G_TYPE_HASH_TABLE)) { + return FALSE; + } + + hash = g_value_get_boxed (data); + entry = g_hash_table_lookup (hash, key); + + if (!entry || !G_VALUE_HOLDS (entry, G_TYPE_STRING)) { + return FALSE; + } + + *value = g_value_dup_string (entry); + return TRUE; +} diff --git a/libempathy-gtk/empathy-plist.h b/libempathy-gtk/empathy-plist.h new file mode 100644 index 000000000..0f7dfe928 --- /dev/null +++ b/libempathy-gtk/empathy-plist.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Christophe Fergeau + * Based on itdb_plist parser from the gtkpod project. + * + * The code contained in this file is free software; you can redistribute + * it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either version + * 2.1 of the License, or (at your option) any later version. + * + * This file 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this code; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA + */ + +#ifndef __EMPATHY_PLIST_H__ +#define __EMPATHY_PLIST_H__ + +#include + +G_BEGIN_DECLS + +GValue * empathy_plist_parse_from_file (const char *filename); +GValue * empathy_plist_parse_from_memory (const char *data, gsize len); +gboolean empathy_plist_get_int (GValue *data, const gchar *key, gint *value); +gboolean empathy_plist_get_string (GValue *data, const gchar *key, gchar **value); + +G_END_DECLS + +#endif diff --git a/libempathy-gtk/empathy-theme-adium.c b/libempathy-gtk/empathy-theme-adium.c index 971594674..ed9086e4c 100644 --- a/libempathy-gtk/empathy-theme-adium.c +++ b/libempathy-gtk/empathy-theme-adium.c @@ -34,6 +34,7 @@ #include "empathy-smiley-manager.h" #include "empathy-conf.h" #include "empathy-ui-utils.h" +#include "empathy-plist.h" #define DEBUG_FLAG EMPATHY_DEBUG_CHAT #include @@ -94,6 +95,11 @@ theme_adium_load (EmpathyThemeAdium *theme) guint len = 0; guint i = 0; gchar *basedir_uri; + GValue *theme_info = NULL; + gchar *variant = NULL; + gchar *font_family = NULL; + gint font_size; + WebKitWebSettings *webkit_settings; priv->basedir = g_strconcat (priv->path, G_DIR_SEPARATOR_S "Contents" G_DIR_SEPARATOR_S "Resources" G_DIR_SEPARATOR_S, NULL); basedir_uri = g_strconcat ("file://", priv->basedir, NULL); @@ -161,6 +167,18 @@ theme_adium_load (EmpathyThemeAdium *theme) len = g_strv_length (strv); } + file = g_build_filename (priv->path, "Contents", "Info.plist", NULL); + theme_info = empathy_plist_parse_from_file (file); + g_free (file); + + if (theme_info) { + empathy_plist_get_string (theme_info, "DefaultVariant", &variant); + empathy_plist_get_string (theme_info, "DefaultFontFamily", &font_family); + empathy_plist_get_int (theme_info, "DefaultFontSize", &font_size); + g_value_unset (theme_info); + g_free (theme_info); + } + /* Replace %@ with the needed information in the template html. */ string = g_string_sized_new (template_len); g_string_append (string, strv[i++]); @@ -170,8 +188,11 @@ theme_adium_load (EmpathyThemeAdium *theme) /* We include main.css by default */ g_string_append_printf (string, "@import url(\"%s\");", css_path); g_string_append (string, strv[i++]); - /* FIXME: We should set the variant css here */ - g_string_append (string, ""); + if (variant) { + g_string_append (string, "Variants/"); + g_string_append (string, variant); + g_string_append (string, ".css"); + } } else { /* FIXME: We should set main.css OR the variant css */ g_string_append (string, css_path); @@ -185,9 +206,21 @@ theme_adium_load (EmpathyThemeAdium *theme) priv->template_html = g_string_free (string, FALSE); /* Load the template */ + webkit_settings = webkit_web_settings_new (); + if (font_family) { + g_object_set (G_OBJECT (webkit_settings), "default-font-family", font_family, NULL); + } + if (font_size) { + g_object_set (G_OBJECT (webkit_settings), "default-font-size", font_size, NULL); + } + + webkit_web_view_set_settings (WEBKIT_WEB_VIEW (theme), webkit_settings); webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (theme), priv->template_html, basedir_uri); + g_object_unref (webkit_settings); + g_free (variant); + g_free (font_family); g_free (basedir_uri); g_free (footer_html); g_free (template_html); -- cgit v1.2.3