changeset 23136:6c4f47b9d201

Make PurpleSmiley a GObject. Removed some functions from the API that are not really necessary, including purple_smileys_add and purple_smileys_remove. They are called automatically when a new smiley is created or removed. PurpleSmiley has a 'destroy' signal, and 'shortcut' and 'image' properties. These might be of interest to UIs.
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sat, 10 May 2008 16:31:06 +0000
parents cb0759d9fe30
children dfc99f5dec43
files libpurple/smiley.c libpurple/smiley.h pidgin/gtksmiley.c
diffstat 3 files changed, 259 insertions(+), 138 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/smiley.c	Wed May 07 18:53:13 2008 +0000
+++ b/libpurple/smiley.c	Sat May 10 16:31:06 2008 +0000
@@ -31,19 +31,24 @@
 #include "smiley.h"
 #include "util.h"
 
-
 /**************************************************************************/
 /* Main structures, members and constants                                 */
 /**************************************************************************/
 
 struct _PurpleSmiley
 {
-        PurpleStoredImage *img;        /**< The id of the stored image with the
-                                        the smiley data.        */
-        char *shortcut;                /**< Shortcut associated with the custom
-                                        smiley. This field will work as a
-                                        unique key by this API. */
-        char *checksum;                /**< The smiley checksum.        */
+	GObject parent;
+	PurpleStoredImage *img;        /**< The id of the stored image with the
+	                                    the smiley data.        */
+	char *shortcut;                /**< Shortcut associated with the custom
+	                                    smiley. This field will work as a
+	                                    unique key by this API. */
+	char *checksum;                /**< The smiley checksum.        */
+};
+
+struct _PurpleSmileyClass
+{
+	GObjectClass parent_class;
 };
 
 static GHashTable *smiley_shortcut_index = NULL; /* shortcut (char *) => smiley (PurpleSmiley*) */
@@ -115,6 +120,11 @@
 purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data,
 		size_t smiley_data_len, const char *filename);
 
+static void
+purple_smiley_data_store(PurpleStoredImage *stored_img);
+
+static void
+purple_smiley_data_unstore(const char *filename);
 
 /*********************************************************************
  * Writing to disk                                                   *
@@ -264,17 +274,174 @@
 			PurpleSmiley *smiley;
 
 			smiley = parse_smiley(smiley_node);
-
-			purple_smileys_add(smiley);
 		}
 	}
 
 	xmlnode_free(root_node);
 }
 
+/*********************************************************************
+ * GObject Stuff                                                     *
+ *********************************************************************/
+enum
+{
+	PROP_0,
+	PROP_SHORTCUT,
+	PROP_IMGSTORE,
+};
+
+#define PROP_SHORTCUT_S "shortcut"
+#define PROP_IMGSTORE_S "image"
+
+enum
+{
+	SIG_DESTROY,
+	SIG_LAST
+};
+
+static guint signals[SIG_LAST];
+
+static void
+purple_smiley_init(GTypeInstance *instance, gpointer klass)
+{
+}
+
+static void
+purple_smiley_get_property(GObject *object, guint param_id, GValue *value,
+		GParamSpec *spec)
+{
+	PurpleSmiley *smiley = PURPLE_SMILEY(object);
+	switch (param_id) {
+		case PROP_SHORTCUT:
+			g_value_set_string(value, smiley->shortcut);
+			break;
+		case PROP_IMGSTORE:
+			g_value_set_pointer(value, smiley->img);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, spec);
+			break;
+	}
+}
+
+static void
+purple_smiley_set_property(GObject *object, guint param_id, const GValue *value,
+		GParamSpec *spec)
+{
+	PurpleSmiley *smiley = PURPLE_SMILEY(object);
+	switch (param_id) {
+		case PROP_SHORTCUT:
+			{
+				const char *shortcut = g_value_get_string(value);
+				purple_smiley_set_shortcut(smiley, shortcut);
+			}
+			break;
+		case PROP_IMGSTORE:
+			{
+				PurpleStoredImage *img = g_value_get_pointer(value);
+
+				purple_imgstore_unref(smiley->img);
+				g_free(smiley->checksum);
+
+				smiley->img = img;
+				if (img) {
+					smiley->checksum = purple_util_get_image_checksum(
+							purple_imgstore_get_data(img),
+							purple_imgstore_get_size(img));
+					purple_smiley_data_store(img);
+				} else {
+					smiley->checksum = NULL;
+				}
+
+				g_object_notify(object, PROP_IMGSTORE_S);
+			}
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, spec);
+			break;
+	}
+}
+
+static void
+purple_smiley_finalize(GObject *obj)
+{
+	PurpleSmiley *smiley = PURPLE_SMILEY(obj);
+	g_signal_emit(obj, signals[SIG_DESTROY], 0);
+
+	if (g_hash_table_lookup(smiley_shortcut_index, smiley->shortcut)) {
+		g_hash_table_remove(smiley_shortcut_index, smiley->shortcut);
+		g_hash_table_remove(smiley_checksum_index, smiley->checksum);
+	}
+
+	g_free(smiley->shortcut);
+	g_free(smiley->checksum);
+	if (smiley->img)
+		purple_smiley_data_unstore(purple_imgstore_get_filename(smiley->img));
+	purple_imgstore_unref(smiley->img);
+
+	purple_smileys_save();
+}
+
+static void
+purple_smiley_class_init(PurpleSmileyClass *klass)
+{
+	GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
+	GParamSpec *pspec;
+
+	gobj_class->get_property = purple_smiley_get_property;
+	gobj_class->set_property = purple_smiley_set_property;
+	gobj_class->finalize = purple_smiley_finalize;
+
+	/* Shortcut */
+	pspec = g_param_spec_string(PROP_SHORTCUT_S, _("Shortcut"),
+			_("The text-shortcut for the smiley"),
+			NULL,
+			G_PARAM_READWRITE);
+	g_object_class_install_property(gobj_class, PROP_SHORTCUT, pspec);
+
+	/* Stored Image */
+	pspec = g_param_spec_pointer(PROP_IMGSTORE_S, _("Stored Image"),
+			_("Stored Image. (that'll have to do for now)"),
+			G_PARAM_READWRITE);
+	g_object_class_install_property(gobj_class, PROP_IMGSTORE, pspec);
+
+	signals[SIG_DESTROY] = g_signal_new("destroy",
+			G_OBJECT_CLASS_TYPE(klass),
+			G_SIGNAL_RUN_LAST,
+			0, NULL, NULL,
+			g_cclosure_marshal_VOID__VOID,
+			G_TYPE_NONE, 0);
+}
+
+GType
+purple_smiley_get_type(void)
+{
+	static GType type = 0;
+
+	if(type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PurpleSmileyClass),
+			NULL,
+			NULL,
+			(GClassInitFunc)purple_smiley_class_init,
+			NULL,
+			NULL,
+			sizeof(PurpleSmiley),
+			0,
+			purple_smiley_init,
+			NULL,
+		};
+
+		type = g_type_register_static(G_TYPE_OBJECT,
+				"PurpleSmiley",
+				&info, 0);
+	}
+
+	return type;
+}
 
 /*********************************************************************
- * Stuff                                                             *
+ * Other Stuff                                                             *
  *********************************************************************/
 
 static char *get_file_full_path(const char *filename)
@@ -437,7 +604,7 @@
 purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data,
 				size_t smiley_data_len, const char *filename)
 {
-	PurpleStoredImage *old_img;
+	PurpleStoredImage *old_img, *new_img;
 	const char *old_filename = NULL;
 	const char *new_filename = NULL;
 
@@ -446,23 +613,19 @@
 	g_return_if_fail(smiley_data_len > 0);
 
 	old_img = smiley->img;
-	smiley->img = NULL;
 
 	if (filename)
-		smiley->img = purple_imgstore_add(smiley_data,
-				smiley_data_len, filename);
+		new_img = purple_imgstore_add(smiley_data, smiley_data_len, filename);
 	else
-		smiley->img = purple_smiley_data_new(smiley_data, smiley_data_len);
+		new_img = purple_smiley_data_new(smiley_data, smiley_data_len);
 
-	g_free(smiley->checksum);
-	smiley->checksum = purple_util_get_image_checksum(
-			smiley_data, smiley_data_len);
+	g_object_set(G_OBJECT(smiley), PROP_IMGSTORE_S, new_img, NULL);
 
+	/* If the old and new image files have different names we need
+	 * to unstore old image file. */
 	if (!old_img)
 		return;
 
-	/* If the old and new image files have different names we need
-	 * to unstore old image file. */
 	old_filename = purple_imgstore_get_filename(old_img);
 	new_filename = purple_imgstore_get_filename(smiley->img);
 
@@ -482,27 +645,11 @@
 {
 	PurpleSmiley *smiley;
 
-	smiley = g_slice_new0(PurpleSmiley);
-	if (!smiley)
-		return NULL;
-
-	smiley->shortcut = g_strdup(shortcut);
+	smiley = PURPLE_SMILEY(g_object_new(PURPLE_TYPE_SMILEY, PROP_SHORTCUT_S, shortcut, NULL));
 
 	return smiley;
 }
 
-static void
-purple_smiley_destroy(PurpleSmiley *smiley)
-{
-	g_return_if_fail(smiley != NULL);
-
-	g_free(smiley->shortcut);
-	g_free(smiley->checksum);
-	purple_imgstore_unref(smiley->img);
-
-	g_slice_free(PurpleSmiley, smiley);
-}
-
 PurpleSmiley *
 purple_smiley_new(PurpleStoredImage *img, const char *shortcut)
 {
@@ -519,17 +666,12 @@
 	if (!smiley)
 		return NULL;
 
-	smiley->checksum = purple_util_get_image_checksum(
-				purple_imgstore_get_data(img),
-				purple_imgstore_get_size(img));
-
-	smiley->img = img;
-	purple_smiley_data_store(img);
+	g_object_set(G_OBJECT(smiley), PROP_IMGSTORE_S, img, NULL);
 
 	return smiley;
 }
 
-PurpleSmiley *
+static PurpleSmiley *
 purple_smiley_new_from_stream(const char *shortcut, guchar *smiley_data,
 			size_t smiley_data_len, const char *filename)
 {
@@ -580,11 +722,7 @@
 {
 	g_return_if_fail(smiley != NULL);
 
-	purple_smileys_remove(smiley);
-	if (smiley->img)
-		purple_smiley_data_unstore(purple_imgstore_get_filename(smiley->img));
-
-	purple_smiley_destroy(smiley);
+	g_object_unref(smiley);
 }
 
 gboolean
@@ -598,7 +736,8 @@
 		return FALSE;
 
 	/* Remove the old shortcut. */
-	g_hash_table_remove(smiley_shortcut_index, smiley->shortcut);
+	if (smiley->shortcut)
+		g_hash_table_remove(smiley_shortcut_index, smiley->shortcut);
 
 	/* Insert the new shortcut. */
 	g_hash_table_insert(smiley_shortcut_index, g_strdup(shortcut), smiley);
@@ -606,6 +745,8 @@
 	g_free(smiley->shortcut);
 	smiley->shortcut = g_strdup(shortcut);
 
+	g_object_notify(G_OBJECT(smiley), PROP_SHORTCUT_S);
+
 	purple_smileys_save();
 
 	return TRUE;
@@ -710,32 +851,6 @@
 	return returninglist;
 }
 
-void
-purple_smileys_add(PurpleSmiley *smiley)
-{
-	g_return_if_fail(smiley != NULL);
-
-	if (!g_hash_table_lookup(smiley_shortcut_index, smiley->shortcut)) {
-		g_hash_table_insert(smiley_shortcut_index, g_strdup(smiley->shortcut), smiley);
-		g_hash_table_insert(smiley_checksum_index, g_strdup(smiley->checksum), smiley);
-
-		purple_smileys_save();
-	}
-}
-
-void
-purple_smileys_remove(PurpleSmiley *smiley)
-{
-	g_return_if_fail(smiley != NULL);
-
-	if (g_hash_table_lookup(smiley_shortcut_index, smiley->shortcut)) {
-		g_hash_table_remove(smiley_shortcut_index, smiley->shortcut);
-		g_hash_table_remove(smiley_checksum_index, smiley->checksum);
-
-		purple_smileys_save();
-	}
-}
-
 PurpleSmiley *
 purple_smileys_find_by_shortcut(const char *shortcut)
 {
--- a/libpurple/smiley.h	Wed May 07 18:53:13 2008 +0000
+++ b/libpurple/smiley.h	Sat May 10 16:31:06 2008 +0000
@@ -1,5 +1,5 @@
 /**
- * @file Smiley.h Smiley API
+ * @file smiley.h Smiley API
  * @ingroup core
  */
 
@@ -28,6 +28,8 @@
 #ifndef _PURPLE_SMILEY_H_
 #define _PURPLE_SMILEY_H_
 
+#include <glib-object.h>
+
 #include "imgstore.h"
 #include "util.h"
 
@@ -35,8 +37,18 @@
  * A custom smiley.
  * This contains everything Purple will ever need to know about a custom smiley.
  * Everything.
+ *
+ * PurpleSmiley is a GObject.
  */
-typedef struct _PurpleSmiley PurpleSmiley;
+typedef struct _PurpleSmiley        PurpleSmiley;
+typedef struct _PurpleSmileyClass   PurpleSmileyClass;
+
+#define PURPLE_TYPE_SMILEY             (purple_smiley_get_type ())
+#define PURPLE_SMILEY(smiley)          (G_TYPE_CHECK_INSTANCE_CAST ((smiley), PURPLE_TYPE_SMILEY, PurpleSmiley))
+#define PURPLE_SMILEY_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_SMILEY, PurpleSmileyClass))
+#define PURPLE_IS_SMILEY(smiley)       (G_TYPE_CHECK_INSTANCE_TYPE ((smiley), PURPLE_TYPE_SMILEY))
+#define PURPLE_IS_SMILEY_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_SMILEY))
+#define PURPLE_SMILEY_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_SMILEY, PurpleSmileyClass))
 
 #ifdef __cplusplus
 extern "C" {
@@ -48,6 +60,12 @@
 /*@{*/
 
 /**
+ * GObject foo.
+ * @internal.
+ */
+GType purple_smiley_get_type(void);
+
+/**
  * Creates a new custom smiley structure and populates it.
  *
  * If a custom smiley with the informed shortcut already exist, it
@@ -64,22 +82,6 @@
 /**
  * Creates a new custom smiley structure and populates it.
  *
- * If a custom smiley with the informed shortcut already exist, it
- * will be automaticaly returned.
- *
- * @param shortcut           The custom smiley associated shortcut.
- * @param smiley_data        The custom smiley data.
- * @param smiley_data_len    The custom smiley data length.
- *
- * @return The custom smiley structure filled up.
- */
-PurpleSmiley *
-purple_smiley_new_from_stream(const char *shortcut, guchar *smiley_data,
-                        size_t smiley_data_len, const char *filename);
-
-/**
- * Creates a new custom smiley structure and populates it.
- *
  * The data is retrieved from an already existent file.
  *
  * If a custom smiley with the informed shortcut already exist, it
@@ -118,7 +120,7 @@
  * Changes the custom smiley's data.
  *
  * When the filename controling is made outside this API, the param
- * @keepfilename must be TRUE.
+ * #keepfilename must be TRUE.
  * Otherwise, the file and filename will be regenerated, and the
  * old one will be removed.
  *
@@ -204,7 +206,7 @@
 
 
 /**************************************************************************/
-/** @name Custom Smiley Subsistem API                                     */
+/** @name Custom Smiley Subsystem API                                     */
 /**************************************************************************/
 /*@{*/
 
@@ -217,24 +219,6 @@
 purple_smileys_get_all(void);
 
 /**
- * Adds the custom smiley to the library persistence.
- *
- * @param smiley   The custom smiley.
- *
- */
-void
-purple_smileys_add(PurpleSmiley *smiley);
-
-/**
- * Remove the custom smiley from persistence.
- *
- * @param smiley   The custom smiley.
- *
- */
-void
-purple_smileys_remove(PurpleSmiley *smiley);
-
-/**
  * Returns the custom smiley given it's shortcut.
  *
  * @param shortcut The custom smiley's shortcut.
--- a/pidgin/gtksmiley.c	Wed May 07 18:53:13 2008 +0000
+++ b/pidgin/gtksmiley.c	Sat May 10 16:31:06 2008 +0000
@@ -76,6 +76,7 @@
 /******************************************************************************
  * GtkIMHtmlSmileys stuff
  *****************************************************************************/
+/* Perhaps these should be in gtkimhtml.c instead. -- sadrul */
 static void add_gtkimhtml_to_list(GtkIMHtmlSmiley *gtksmiley)
 {
 	gtk_smileys = g_slist_prepend(gtk_smileys, gtksmiley);
@@ -83,7 +84,13 @@
 	purple_debug_info("gtksmiley", "adding %s to gtk_smileys\n", gtksmiley->smile);
 }
 
-/* Perhaps this should be in gtkimhtml.c instead. -- sad */
+static void
+shortcut_changed_cb(PurpleSmiley *smiley, gpointer dontcare, GtkIMHtmlSmiley *gtksmiley)
+{
+	g_free(gtksmiley->smile);
+	gtksmiley->smile = g_strdup(purple_smiley_get_shortcut(smiley));
+}
+
 static GtkIMHtmlSmiley *smiley_purple_to_gtkimhtml(PurpleSmiley *smiley)
 {
 	GtkIMHtmlSmiley *gtksmiley;
@@ -92,21 +99,17 @@
 
 	file = purple_imgstore_get_filename(purple_smiley_get_stored_image(smiley));
 
-	filename = g_build_filename(purple_smileys_get_storing_dir(),file, NULL);
+	filename = g_build_filename(purple_smileys_get_storing_dir(), file, NULL);
 
 	gtksmiley = gtk_imhtml_smiley_create(filename, purple_smiley_get_shortcut(smiley),
 			FALSE, GTK_IMHTML_SMILEY_CUSTOM);
 	g_free(filename);
 
-	return gtksmiley;
-}
+	/* Make sure the shortcut for the GtkIMHtmlSmiley is updated with the PurpleSmiley */
+	g_signal_connect(G_OBJECT(smiley), "notify::shortcut",
+			G_CALLBACK(shortcut_changed_cb), gtksmiley);
 
-void pidgin_smiley_add_to_list(PurpleSmiley *smiley)
-{
-	GtkIMHtmlSmiley *gtksmiley;
-
-	gtksmiley = smiley_purple_to_gtkimhtml(smiley);
-	add_gtkimhtml_to_list(gtksmiley);
+	return gtksmiley;
 }
 
 void pidgin_smiley_del_from_list(PurpleSmiley *smiley)
@@ -126,6 +129,8 @@
 			continue;
 
 		gtk_imhtml_smiley_destroy(gtksmiley);
+		g_signal_handlers_disconnect_matched(G_OBJECT(smiley), G_SIGNAL_MATCH_DATA,
+				0, 0, NULL, NULL, gtksmiley);
 		break;
 	}
 
@@ -133,6 +138,15 @@
 		gtk_smileys = g_slist_delete_link(gtk_smileys, list);
 }
 
+void pidgin_smiley_add_to_list(PurpleSmiley *smiley)
+{
+	GtkIMHtmlSmiley *gtksmiley;
+
+	gtksmiley = smiley_purple_to_gtkimhtml(smiley);
+	add_gtkimhtml_to_list(gtksmiley);
+	g_signal_connect(G_OBJECT(smiley), "destroy", G_CALLBACK(pidgin_smiley_del_from_list), NULL);
+}
+
 void pidgin_smileys_init(void)
 {
 	GList *smileys;
@@ -224,7 +238,6 @@
 
 		purple_debug_info("gtksmiley", "adding a new smiley\n");
 		emoticon = purple_smiley_new_from_file(entry, s->filename);
-		purple_smileys_add(emoticon);
 		if (gtk_smileys != NULL)
 			pidgin_smiley_add_to_list(emoticon);
 	}
@@ -371,36 +384,45 @@
 static void delete_foreach(GtkTreeModel *model, GtkTreePath *path,
 		GtkTreeIter *iter, gpointer data)
 {
-	PurpleSmiley *smiley;
-	char *shortcut;
+	PurpleSmiley *smiley = NULL;
 	SmileyManager *dialog;
 
 	dialog = (SmileyManager*)data;
 
 	gtk_tree_model_get(model, iter,
-			SHORTCUT, &shortcut,
+			SMILEY, &smiley,
 			-1);
 
-	purple_debug_info("gtksmiley", "delete_foreach shortcut = %s\n", shortcut);
-
-	smiley = purple_smileys_find_by_shortcut(shortcut);
-
-	if(smiley == NULL)
-		purple_debug_error("gtksmiley", "%s not found\n", shortcut);
-	else {
+	if(smiley != NULL) {
+		g_object_unref(G_OBJECT(smiley));
 		pidgin_smiley_del_from_list(smiley);
 		purple_smiley_delete(smiley);
 	}
+}
 
-	g_free(shortcut);
+static void append_to_list(GtkTreeModel *model, GtkTreePath *path,
+		GtkTreeIter *iter, gpointer data)
+{
+	GList **list = data;
+	*list = g_list_prepend(*list, gtk_tree_path_copy(path));
 }
 
 static void smiley_delete(SmileyManager *dialog)
 {
 	GtkTreeSelection *selection;
+	GList *list = NULL;
 
 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
 	gtk_tree_selection_selected_foreach(selection, delete_foreach, dialog);
+	gtk_tree_selection_selected_foreach(selection, append_to_list, &list);
+
+	while (list) {
+		GtkTreeIter iter;
+		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, list->data))
+			gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &iter);
+		gtk_tree_path_free(list->data);
+		list = g_list_delete_link(list, list);
+	}
 }
 /******************************************************************************
  * The Smiley Manager
@@ -499,6 +521,7 @@
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
 	gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, SMILEY, &smiley, -1);
 	pidgin_smiley_edit(gtk_widget_get_toplevel(GTK_WIDGET(treeview)), smiley);
+	g_object_unref(G_OBJECT(smiley));
 }
 
 static GtkWidget *smiley_list_create(SmileyManager *dialog)
@@ -519,7 +542,7 @@
 	dialog->model = gtk_list_store_new(N_COL,
 			GDK_TYPE_PIXBUF,	/* ICON */
 			G_TYPE_STRING,		/* SHORTCUT */
-			G_TYPE_POINTER		/* SMILEY */
+			G_TYPE_OBJECT		/* SMILEY */
 			);
 
 	/* the actual treeview */
@@ -557,7 +580,6 @@
 			break;
 		case GTK_RESPONSE_NO:
 			smiley_delete(dialog);
-			refresh_list();
 			break;
 		case GTK_RESPONSE_DELETE_EVENT:
 		case GTK_RESPONSE_CLOSE: