cpw.malu.xmlsmileytheme: a68d64d8: A rough first stab at implementing an XM...

malu at pidgin.im malu at pidgin.im
Mon Nov 24 17:11:18 EST 2008


-----------------------------------------------------------------
Revision: a68d64d86a9680427c8a94a9ca9dac98a06b2cb4
Ancestor: f3891f3348abfe90fbe60a054833c12075aa8df4
Author: malu at pidgin.im
Date: 2008-11-24T22:07:33
Branch: im.pidgin.cpw.malu.xmlsmileytheme
URL: http://d.pidgin.im/viewmtn/revision/info/a68d64d86a9680427c8a94a9ca9dac98a06b2cb4

Modified files:
        libpurple/util.c libpurple/util.h pidgin/gtkprefs.c
        pidgin/gtkthemes.c pidgin/gtkthemes.h

ChangeLog: 

A rough first stab at implementing an XML-based smiley theme format based
on XEP-0038
Currently it only adds smileys from the themes to the "default" set.
Unpacking of .jisp packages is not yet supported.
The plan is to support an extended format using protocol-specific extensions
and localization.
Also the interface in gtkthemes.[c|h] might get cleaned up, the extra _xml
function is probably not needed.


-------------- next part --------------
============================================================
--- libpurple/util.c	5251086d0a65e13ddfb21774e4717b68a97bd677
+++ libpurple/util.c	2c96f77fc9be43b3a01a6ad2c5190efaed156fe7
@@ -2772,10 +2772,10 @@ xmlnode *
 }
 
 xmlnode *
-purple_util_read_xml_from_file(const char *filename, const char *description)
+purple_util_read_xml_from_file_absolute(const char *filename, 
+	const char *description)
 {
 	const char *user_dir = purple_user_dir();
-	gchar *filename_full;
 	GError *error = NULL;
 	gchar *contents = NULL;
 	gsize length;
@@ -2786,20 +2786,17 @@ purple_util_read_xml_from_file(const cha
 	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))
+	if (!g_file_test(filename, 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);
+						"necessarily an error)\n", filename);
 		return NULL;
 	}
 
-	if (!g_file_get_contents(filename_full, &contents, &length, &error))
+	if (!g_file_get_contents(filename, &contents, &length, &error))
 	{
 		purple_debug_error("util", "Error reading file %s: %s\n",
-						 filename_full, error->message);
+						 filename, error->message);
 		g_error_free(error);
 	}
 
@@ -2814,7 +2811,7 @@ purple_util_read_xml_from_file(const cha
 
 			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);
+							 "file to %s\n", filename, filename_temp);
 			purple_util_write_data_to_file(filename_temp, contents, length);
 			g_free(filename_temp);
 		}
@@ -2829,14 +2826,23 @@ purple_util_read_xml_from_file(const cha
 		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);
+					"has been renamed to %s~."), description, filename);
 		purple_notify_error(NULL, NULL, title, msg);
 		g_free(title);
 		g_free(msg);
 	}
 
-	g_free(filename_full);
+	return node;
+	
+}
 
+xmlnode *
+purple_util_read_xml_from_file(const char *filename, const char *description)
+{
+	char *filename_full = g_build_filename(purple_user_dir(), filename, NULL);
+	xmlnode *node = 
+		purple_util_read_xml_from_file_absolute(filename_full, description);
+	g_free(filename_full);
 	return node;
 }
 
============================================================
--- libpurple/util.h	62c0624b2e3267b01a6bd6c85f825d393d1859fc
+++ libpurple/util.h	e7c8ab516204cc69c9be2275da298bc69d164001
@@ -676,7 +676,26 @@ xmlnode *purple_util_read_xml_from_file(
 xmlnode *purple_util_read_xml_from_file(const char *filename,
 									  const char *description);
 
+
+
 /**
+ * Read the contents of a given file and parse the results into an
+ * xmlnode tree structure. Using an absoule path
+ * @since 2.6.0
+ *
+ * @param filename    The basename of the file to open in the purple_user_dir.
+ * @param description A very short description of the contents of this
+ *                    file.  This is used in error messages shown to the
+ *                    user when the file can not be opened.  For example,
+ *                    "preferences," or "buddy pounces."
+ *
+ * @return An xmlnode tree of the contents of the given file.  Or NULL, if
+ *         the file does not exist or there was an error reading the file.
+ */
+xmlnode *purple_util_read_xml_from_file_absolute(const char *filename,
+	const char *description);
+
+/**
  * Creates a temporary file and returns a file pointer to it.
  *
  * This is like mkstemp(), but returns a file pointer and uses a
============================================================
--- pidgin/gtkprefs.c	a83069e6a0019e2f44bec6932a7c79aaada767e5
+++ pidgin/gtkprefs.c	d3b2d91580453af4ece4daa5c942a207ed329989
@@ -2254,7 +2254,11 @@ smiley_theme_pref_cb(const char *name, P
 	for (themes = smiley_themes; themes; themes = themes->next) {
 		struct smiley_theme *smile = themes->data;
 		if (smile->name && strcmp(themename, smile->name) == 0) {
-			pidgin_themes_load_smiley_theme(smile->path, TRUE);
+			if (pidgin_themes_is_xml(smile->path)) {
+				pidgin_themes_load_smiley_theme_xml(smile->path, TRUE);
+			} else {
+				pidgin_themes_load_smiley_theme(smile->path, TRUE);
+			}
 			break;
 		}
 	}
============================================================
--- pidgin/gtkthemes.c	7fbff18d2f55de2dd1262f95f2d89e776ff4c7d9
+++ pidgin/gtkthemes.c	db96ccf3ec274d9e199cf86c1e0e7118d31fba86
@@ -216,34 +216,209 @@ pidgin_smiley_themes_remove_non_existing
 
 	if (!current_smiley_theme && smiley_themes) {
 		struct smiley_theme *smile = g_slist_last(smiley_themes)->data;
-		pidgin_themes_load_smiley_theme(smile->path, TRUE);
+		if (pidgin_themes_is_xml(smile->path)) {
+			pidgin_themes_load_smiley_theme_xml(smile->path, TRUE);
+		} else {
+			pidgin_themes_load_smiley_theme(smile->path, TRUE);
+		}
 	}
 }
 
+static struct smiley_theme *
+pidgin_themes_lookup_smiley_theme(const char *file)
+{
+	GSList *lst = smiley_themes;
+	
+	while (lst) {
+		struct smiley_theme *thm = lst->data;
+		if (!strcmp(thm->path, file)) {
+			return thm;
+		}
+		lst = lst->next;
+	}
+	
+	return NULL;
+}
+
+static void
+pidgin_themes_reload_smiley_theme(struct smiley_theme *theme)
+{
+	/* reload smiley theme in GtkIMHtmls */
+	GList *cnv;
+
+	if (current_smiley_theme)
+		pidgin_themes_destroy_smiley_theme_smileys(current_smiley_theme);
+	current_smiley_theme = theme;
+
+	for (cnv = purple_get_conversations(); cnv != NULL; cnv = cnv->next) {
+		PurpleConversation *conv = cnv->data;
+
+		if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) {
+			/* We want to see our custom smileys on our entry if we write the shortcut */
+			pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->imhtml);
+			pidgin_themes_smiley_themeize_custom(PIDGIN_CONVERSATION(conv)->entry);
+		}
+	}
+}
+
+gboolean pidgin_themes_is_xml(const char *file)
+{
+	if (strcmp(g_path_get_basename(file), "icondef.xml") == 0) {
+		return TRUE;
+	} else if (strcmp(g_path_get_basename(file), "theme") == 0) {
+		return FALSE;
+	} else {
+		purple_debug_error("gtkthemes", 
+			"pidgin_themes_is_xml called with invalid path\n");
+		return FALSE;
+	}
+}
+
+void pidgin_themes_load_smiley_theme_xml(const char *file, gboolean load)
+{
+	xmlnode *icondef = 
+		purple_util_read_xml_from_file_absolute(file, _("Smiley Themes"));
+	struct smiley_theme *theme = pidgin_themes_lookup_smiley_theme(file);
+	const gchar *dirname = g_path_get_dirname(file);
+	gboolean new_theme = FALSE;
+	
+	if (icondef) {
+		xmlnode *meta = NULL;
+		
+		if (strcmp(icondef->name, "icondef") != 0) {
+			purple_debug_error("gtkthemes", 
+				"icondefs.xml must contain a <icondef/> top level element\n");
+			return;
+		}
+		
+		meta = xmlnode_get_child(icondef, "meta");
+		
+		if (!theme) {
+			 new_theme = TRUE;
+			 theme = g_new0(struct smiley_theme, 1);
+			 theme->path = g_strdup(file);
+		} else if (theme == current_smiley_theme) {
+			/* don't reload the current theme */
+			return;
+		}
+		
+		if (!meta) {
+			purple_debug_error("gtkthemes", 
+				"icondef.xml contains no meta data\n");
+			if (theme) {
+				g_free(theme);
+			}
+			return;
+		} else {
+			/* parse out meta data */
+			xmlnode *name = xmlnode_get_child(meta, "name");
+			xmlnode *description = xmlnode_get_child(meta, "description");
+			xmlnode *author = xmlnode_get_child(meta, "author");
+			
+			if (name) {
+				theme->name = xmlnode_get_data(name);
+			} else {
+				/* maybe should do something better here... */
+				theme->name = g_strdup(file);
+			}
+			
+			if (description) {
+				theme->desc = xmlnode_get_data(description);
+			}
+			
+			if (author) {
+				theme->author = xmlnode_get_data(author);
+			}
+			
+			/* skip other meta elements for now (version, etc.) */
+			/* should also set the icon for the theme, from somewhere */
+		}
+		
+		if (load) {
+			/* create the "Default" list of smileys */
+			struct smiley_list *child = g_new0(struct smiley_list, 1);
+			xmlnode *icon = NULL;
+			
+			child->sml = g_strdup("default");
+			
+			for (icon = xmlnode_get_child(icondef, "icon") ; icon ;
+				 icon = xmlnode_get_next_twin(icon)) {
+				xmlnode *object = NULL;
+				xmlnode *text = NULL;
+				gboolean found_object = FALSE;
+				gchar *path = NULL;
+				gchar *filename = NULL;
+					 
+				for (object = xmlnode_get_child(icon, "object") ; object ;
+					object = xmlnode_get_next_twin(object)) {
+					const gchar *mime = xmlnode_get_attrib(object, "mime");
+						
+					if (mime && strncmp(mime, "image/", 6) == 0) {
+						found_object = TRUE;
+						break;
+					}
+				}
+					 
+				if (!found_object) {
+					purple_debug_error("gtkthemes", 
+						"icon in theme without an image object\n");
+					continue;
+				}
+				
+				filename = xmlnode_get_data_unescaped(object);
+				path = g_build_filename(dirname, filename, NULL);
+					 
+				for (text = xmlnode_get_child(icon, "text") ; text ;
+					text = xmlnode_get_next_twin(text)) {
+					gchar *smile = xmlnode_get_data_unescaped(text);
+					GtkIMHtmlSmiley *smiley =
+						gtk_imhtml_smiley_create(path, smile, FALSE, 0);
+						
+					purple_debug_info("gtkthemes",
+						"adding smiley with shortcut %s\n", smile);
+						
+					child->smileys = 
+						g_slist_append(child->smileys, (gpointer) smiley);
+					g_free(smile);
+				}
+					 
+				g_free(filename);
+				g_free(path);
+			}
+			
+			theme->list = child;
+		}
+		
+		if (new_theme) {
+			smiley_themes = g_slist_append(smiley_themes, theme);
+		}
+		
+		if (load) {
+			pidgin_themes_reload_smiley_theme(theme);
+		}
+		
+	} else {
+		purple_debug_error("gtkthemes", "parse error in icondefs.xml");
+	}
+}
+
 void pidgin_themes_load_smiley_theme(const char *file, gboolean load)
 {
 	FILE *f = g_fopen(file, "r");
 	char buf[256];
 	char *i;
-	struct smiley_theme *theme=NULL;
 	struct smiley_list *list = NULL;
-	GSList *lst = smiley_themes;
 	char *dirname;
 	gboolean new_theme = FALSE;
-
+	struct smiley_theme *theme = pidgin_themes_lookup_smiley_theme(file);
+	
+	purple_debug_info("gtkthemes", "lookup theme %p\n", theme);
+	
 	if (!f)
 		return;
-
-	while (lst) {
-		struct smiley_theme *thm = lst->data;
-		if (!strcmp(thm->path, file)) {
-			theme = thm;
-			break;
-		}
-		lst = lst->next;
-	}
-
+	
 	if (!theme) {
+		purple_debug_info("gtkthemes", "creating new theme structure\n");
 		new_theme = TRUE;
 		theme = g_new0(struct smiley_theme, 1);
 		theme->path = g_strdup(file);
@@ -346,10 +521,14 @@ void pidgin_themes_load_smiley_theme(con
 	}
 
 	if (new_theme) {
+		purple_debug_info("gtkthemes", "adding theme %s to list of themes\n",
+			theme->name);
 		smiley_themes = g_slist_prepend(smiley_themes, theme);
 	}
 
 	if (load) {
+		pidgin_themes_reload_smiley_theme(theme);
+#if 0
 		GList *cnv;
 
 		if (current_smiley_theme)
@@ -365,6 +544,7 @@ void pidgin_themes_load_smiley_theme(con
 				pidgin_themes_smiley_themeize_custom(PIDGIN_CONVERSATION(conv)->entry);
 			}
 		}
+#endif
 	}
 }
 
@@ -372,7 +552,7 @@ void pidgin_themes_smiley_theme_probe()
 {
 	GDir *dir;
 	const gchar *file;
-	gchar *path, *test_path;
+	gchar *path, *old_path, *test_path;
 	int l;
 	char* probedirs[3];
 
@@ -387,14 +567,31 @@ void pidgin_themes_smiley_theme_probe()
 			while ((file = g_dir_read_name(dir))) {
 				test_path = g_build_filename(probedirs[l], file, NULL);
 				if (g_file_test(test_path, G_FILE_TEST_IS_DIR)) {
-					path = g_build_filename(probedirs[l], file, "theme", NULL);
-
+					old_path = g_build_filename(probedirs[l], file, "theme", 
+						NULL);
+					path = g_build_filename(probedirs[l], file, "icondef.xml",
+						NULL);
+					
 					/* Here we check to see that the theme has proper syntax.
 					 * We set the second argument to FALSE so that it doesn't load
 					 * the theme yet.
 					 */
-					pidgin_themes_load_smiley_theme(path, FALSE);
+					purple_debug_info("gtkthemes",
+						"trying to load %s or %s\n", path, old_path);
+					if (g_file_test(path, G_FILE_TEST_IS_REGULAR)) {
+						/* load as XML format theme XEP-0038 */
+						purple_debug_info("gtkthemes",
+							"loading theme using XML format from %s\n",
+							path);
+						pidgin_themes_load_smiley_theme_xml(path, FALSE);
+					} else if (g_file_test(old_path, G_FILE_TEST_IS_REGULAR)) {
+						purple_debug_info("gtkthemes",
+							"loading theme using old format from %s\n",
+							old_path);
+						pidgin_themes_load_smiley_theme(old_path, FALSE);
+					}
 					g_free(path);
+					g_free(old_path);
 				}
 				g_free(test_path);
 			}
@@ -407,7 +604,11 @@ void pidgin_themes_smiley_theme_probe()
 
 	if (!current_smiley_theme && smiley_themes) {
 		struct smiley_theme *smile = smiley_themes->data;
-		pidgin_themes_load_smiley_theme(smile->path, TRUE);
+		if (pidgin_themes_is_xml(smile->path)) {
+			pidgin_themes_load_smiley_theme_xml(smile->path, TRUE);
+		} else {
+			pidgin_themes_load_smiley_theme(smile->path, TRUE);
+		}
 	}
 }
 
============================================================
--- pidgin/gtkthemes.h	50544ee7490c1c79c030e02198b42905a232a706
+++ pidgin/gtkthemes.h	1e72e45de774339319a1d3024ce7964db3b8e64d
@@ -61,6 +61,13 @@ void pidgin_themes_load_smiley_theme(con
 void pidgin_themes_load_smiley_theme(const char *file, gboolean load);
 
 /**
+ * @since 2.6.0
+ */
+void pidgin_themes_load_smiley_theme_xml(const char *file, gboolean load);
+
+gboolean pidgin_themes_is_xml(const char *file);
+
+/**
  * @since 2.1.0
  */
 void pidgin_themes_remove_smiley_theme(const char *file);


More information about the Commits mailing list