im.pidgin.pidgin.custom_smiley: af58ab071808e2633ff0c7ca5c9b45c4c90b3974
sadrul at pidgin.im
sadrul at pidgin.im
Tue Feb 19 15:06:08 EST 2008
-----------------------------------------------------------------
Revision: af58ab071808e2633ff0c7ca5c9b45c4c90b3974
Ancestor: 22f15d3d569d5d812f284fecac1defaf2d725248
Author: sadrul at pidgin.im
Date: 2008-02-19T19:41:56
Branch: im.pidgin.pidgin.custom_smiley
URL: http://d.pidgin.im/viewmtn/revision/info/af58ab071808e2633ff0c7ca5c9b45c4c90b3974
Added files:
libpurple/smiley.c libpurple/smiley.h pidgin/gtksmiley.c
pidgin/gtksmiley.h
Modified files:
libpurple/Makefile.am libpurple/Makefile.mingw
libpurple/core.c libpurple/util.c libpurple/util.h
pidgin/Makefile.am pidgin/Makefile.mingw pidgin/gtkblist.c
pidgin/gtkconv.c pidgin/gtkimhtmltoolbar.c pidgin/gtkmain.c
pidgin/gtkthemes.c pidgin/gtkthemes.h pidgin/gtkutils.c
ChangeLog:
Apply the custom smiley patches from #1187, from Jorge Villaseño (Masca) and
Mauro Sérgio Ferreira Brasil.
I have not applied the bits on MSN yet. I will have to look at it later, but
I would rather someone else more familiar with the MSN code look at it first.
I changed some bits of the applied patch (whitespacing, camelcasing etc.), and
the bit that required a major version bump (in gtkthemes.h). There are a few
more things that need to be done for this to be merged back to i.p.p.
-------------- next part --------------
============================================================
--- libpurple/smiley.c b1c6b19b4bc8d50847fadf9aa39e5535208c6527
+++ libpurple/smiley.c b1c6b19b4bc8d50847fadf9aa39e5535208c6527
@@ -0,0 +1,858 @@
+/**
+ * @file smiley.c Simley API
+ * @ingroup core
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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 "internal.h"
+#include "xmlnode.h"
+#include "debug.h"
+#include "imgstore.h"
+#include "smiley.h"
+#include "util.h"
+
+
+/**************************************************************************/
+/* Main structures, members and constants */
+/**************************************************************************/
+
+static GHashTable *smiley_data_index = NULL;
+static GHashTable *smiley_shortcut_index = NULL;
+static GHashTable *smiley_checksum_index = NULL;
+
+static guint save_timer = 0;
+static gboolean smileys_loaded = FALSE;
+static char *smileys_dir = NULL;
+
+#define SMILEYS_DEFAULT_FOLDER "custom_smiley"
+#define SMILEYS_LOG_ID "smileys"
+
+#define XML_FILE_NAME "smileys.xml"
+
+#define XML_ROOT_TAG "smileys"
+#define XML_PROFILE_TAG "profile"
+#define XML_PROFILE_NAME_ATTRIB_TAG "name"
+#define XML_ACCOUNT_TAG "account"
+#define XML_ACCOUNT_USERID_ATTRIB_TAG "userid"
+#define XML_SMILEY_SET_TAG "smiley_set"
+#define XML_SMILEY_TAG "smiley"
+#define XML_SHORTCUT_ATTRIB_TAG "shortcut"
+#define XML_CHECKSUM_ATRIB_TAG "checksum"
+#define XML_FILENAME_ATRIB_TAG "filename"
+
+
+/******************************************************************************
+ * XML descriptor file layout *
+ ******************************************************************************
+ *
+ * Althought we are creating the profile XML structure here, now we
+ * won't handle it.
+ * So, we just add one profile named "default" that has no associated
+ * account elements, and have only the smiley_set that will contain
+ * all existent custom smiley.
+ *
+ * It's our "Highlander Profile" :-)
+ *
+ ******************************************************************************
+ *
+ * <smileys>
+ * <profile name="john.doe">
+ * <account userid="john.doe at jabber.org">
+ * <account userid="john.doe at gmail.com">
+ * <smiley_set>
+ * <smiley shortcut="aaa" checksum="xxxxxxxx" filename="file_name1.gif"/>
+ * <smiley shortcut="bbb" checksum="yyyyyyy" filename="file_name2.gif"/>
+ * </smiley_set>
+ * </profile>
+ * </smiley>
+ *
+ *****************************************************************************/
+
+
+/*********************************************************************
+ * Forward declarations *
+ *********************************************************************/
+
+static gboolean read_smiley_file(const char *path, guchar **data, size_t *len);
+
+static char *get_file_full_path(const char *filename);
+
+static PurpleSmiley *purple_smiley_create(const char *shortcut);
+
+PurpleSmiley *purple_smiley_load_file(const char *shortcut, const char *checksum,
+ const char *filename);
+
+void
+purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data,
+ size_t smiley_data_len, const char *filename);
+
+
+/*********************************************************************
+ * Writing to disk *
+ *********************************************************************/
+
+static xmlnode *
+smiley_to_xmlnode(PurpleSmiley *smiley)
+{
+ xmlnode *smiley_node = NULL;
+
+ smiley_node = xmlnode_new(XML_SMILEY_TAG);
+
+ if (!smiley_node)
+ return NULL;
+
+ xmlnode_set_attrib(smiley_node, XML_SHORTCUT_ATTRIB_TAG,
+ smiley->shortcut);
+
+ xmlnode_set_attrib(smiley_node, XML_CHECKSUM_ATRIB_TAG,
+ smiley->checksum);
+
+ xmlnode_set_attrib(smiley_node, XML_FILENAME_ATRIB_TAG,
+ purple_imgstore_get_filename(smiley->img));
+
+ return smiley_node;
+}
+
+static void
+add_smiley_to_main_node(gpointer key, gpointer value, gpointer user_data)
+{
+ xmlnode *child_node;
+
+ child_node = smiley_to_xmlnode(value);
+ xmlnode_insert_child((xmlnode*)user_data, child_node);
+}
+
+static xmlnode *
+smileys_to_xmlnode()
+{
+ xmlnode *root_node, *profile_node, *smileyset_node;
+
+ root_node = xmlnode_new(XML_ROOT_TAG);
+ xmlnode_set_attrib(root_node, "version", "1.0");
+
+ /* See the top comment's above to understand why initial tag elements
+ * are not being considered by now. */
+ profile_node = xmlnode_new(XML_PROFILE_TAG);
+ if (profile_node) {
+ xmlnode_set_attrib(profile_node, XML_PROFILE_NAME_ATTRIB_TAG, "Default");
+ xmlnode_insert_child(root_node, profile_node);
+
+ smileyset_node = xmlnode_new(XML_SMILEY_SET_TAG);
+ if (smileyset_node) {
+ xmlnode_insert_child(profile_node, smileyset_node);
+ g_hash_table_foreach(smiley_data_index, add_smiley_to_main_node, smileyset_node);
+ }
+ }
+
+ return root_node;
+}
+
+static void
+sync_smileys()
+{
+ xmlnode *root_node;
+ char *data;
+
+ if (!smileys_loaded) {
+ purple_debug_error(SMILEYS_LOG_ID, "Attempted to save smileys before it "
+ "was read!\n");
+ return;
+ }
+
+ root_node = smileys_to_xmlnode();
+ data = xmlnode_to_formatted_str(root_node, NULL);
+ purple_util_write_data_to_file(XML_FILE_NAME, data, -1);
+
+ g_free(data);
+ xmlnode_free(root_node);
+}
+
+static gboolean
+save_smileys_cb(gpointer data)
+{
+ sync_smileys();
+ save_timer = 0;
+ return FALSE;
+}
+
+static void
+purple_smileys_save()
+{
+ if (save_timer == 0)
+ save_timer = purple_timeout_add_seconds(5, save_smileys_cb, NULL);
+}
+
+
+/*********************************************************************
+ * Reading from disk *
+ *********************************************************************/
+
+static PurpleSmiley *
+parse_smiley(xmlnode *smiley_node)
+{
+ PurpleSmiley *smiley;
+ const char *shortcut = NULL;
+ const char *checksum = NULL;
+ const char *filename = NULL;
+
+ shortcut = xmlnode_get_attrib(smiley_node, XML_SHORTCUT_ATTRIB_TAG);
+ checksum = xmlnode_get_attrib(smiley_node, XML_CHECKSUM_ATRIB_TAG);
+ filename = xmlnode_get_attrib(smiley_node, XML_FILENAME_ATRIB_TAG);
+
+ if ((shortcut == NULL) || (checksum == NULL) || (filename == NULL))
+ return NULL;
+
+ smiley = purple_smiley_load_file(shortcut, checksum, filename);
+
+ return smiley;
+}
+
+static void
+purple_smileys_load()
+{
+ xmlnode *root_node, *profile_node;
+ xmlnode *smileyset_node = NULL;
+ xmlnode *smiley_node;
+
+ smileys_loaded = TRUE;
+
+ root_node = purple_util_read_xml_from_file(XML_FILE_NAME,
+ _(SMILEYS_LOG_ID));
+
+ if (root_node == NULL)
+ return;
+
+ /* See the top comment's above to understand why initial tag elements
+ * are not being considered by now. */
+ profile_node = xmlnode_get_child(root_node, XML_PROFILE_TAG);
+ if (profile_node)
+ smileyset_node = xmlnode_get_child(profile_node, XML_SMILEY_SET_TAG);
+
+ if (smileyset_node) {
+ smiley_node = xmlnode_get_child(smileyset_node, XML_SMILEY_TAG);
+ for (; smiley_node != NULL;
+ smiley_node = xmlnode_get_next_twin(smiley_node)) {
+ PurpleSmiley *smiley;
+
+ smiley = parse_smiley(smiley_node);
+
+ purple_smileys_add(smiley);
+ }
+ }
+
+ xmlnode_free(root_node);
+}
+
+
+/*********************************************************************
+ * Stuff *
+ *********************************************************************/
+
+static char *get_file_full_path(const char *filename)
+{
+ char *path;
+
+ path = g_build_filename(purple_smileys_get_storing_dir(), filename, NULL);
+
+ if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
+ g_free(path);
+ return NULL;
+ }
+
+ return path;
+}
+
+PurpleSmiley *
+purple_smiley_load_file(const char *shortcut, const char *checksum, const char *filename)
+{
+ PurpleSmiley *smiley = NULL;
+ guchar *smiley_data;
+ size_t smiley_data_len;
+ char *fullpath = NULL;
+
+ g_return_val_if_fail(shortcut != NULL, NULL);
+ g_return_val_if_fail(checksum != NULL, NULL);
+ g_return_val_if_fail(filename != NULL, NULL);
+
+ fullpath = get_file_full_path(filename);
+ if (!fullpath)
+ return NULL;
+
+ smiley = purple_smiley_create(shortcut);
+ if (!smiley)
+ return NULL;
+
+ smiley->checksum = g_strdup(checksum);
+
+ if (read_smiley_file(fullpath, &smiley_data, &smiley_data_len))
+ purple_smiley_set_data_impl(smiley, smiley_data,
+ smiley_data_len, filename);
+ else
+ purple_smiley_delete(smiley);
+
+ return smiley;
+}
+
+static void
+purple_smiley_data_store(PurpleStoredImage *stored_img)
+{
+ const char *dirname;
+ char *path;
+ FILE *file = NULL;
+
+ g_return_if_fail(stored_img != NULL);
+
+ if (!smileys_loaded)
+ return;
+
+ dirname = purple_smileys_get_storing_dir();
+ path = g_build_filename(dirname, purple_imgstore_get_filename(stored_img), NULL);
+
+ if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
+ purple_debug_info(SMILEYS_LOG_ID, "Creating smileys directory.\n");
+
+ if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
+ purple_debug_error(SMILEYS_LOG_ID,
+ "Unable to create directory %s: %s\n",
+ dirname, g_strerror(errno));
+ }
+ }
+
+ if ((file = g_fopen(path, "wb")) != NULL) {
+ if (!fwrite(purple_imgstore_get_data(stored_img),
+ purple_imgstore_get_size(stored_img), 1, file)) {
+ purple_debug_error(SMILEYS_LOG_ID, "Error writing %s: %s\n",
+ path, g_strerror(errno));
+ } else {
+ purple_debug_info(SMILEYS_LOG_ID, "Wrote cache file: %s\n", path);
+ }
+
+ fclose(file);
+ } else {
+ purple_debug_error(SMILEYS_LOG_ID, "Unable to create file %s: %s\n",
+ path, g_strerror(errno));
+ g_free(path);
+
+ return;
+ }
+
+ g_free(path);
+}
+
+static void
+purple_smiley_data_unstore(const char *filename)
+{
+ const char *dirname;
+ char *path;
+
+ g_return_if_fail(filename != NULL);
+
+ dirname = purple_smileys_get_storing_dir();
+ path = g_build_filename(dirname, filename, NULL);
+
+ if (g_file_test(path, G_FILE_TEST_EXISTS)) {
+ if (g_unlink(path))
+ purple_debug_error(SMILEYS_LOG_ID, "Failed to delete %s: %s\n",
+ path, g_strerror(errno));
+ else
+ purple_debug_info(SMILEYS_LOG_ID, "Deleted cache file: %s\n", path);
+ }
+
+ g_free(path);
+}
+
+static gboolean
+read_smiley_file(const char *path, guchar **data, size_t *len)
+{
+ GError *err = NULL;
+
+ if (!g_file_get_contents(path, (gchar **)data, len, &err)) {
+ purple_debug_error(SMILEYS_LOG_ID, "Error reading %s: %s\n",
+ path, err->message);
+ g_error_free(err);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static PurpleStoredImage *
+purple_smiley_data_new(guchar *smiley_data, size_t smiley_data_len)
+{
+ char *filename;
+ PurpleStoredImage *stored_img;
+
+ g_return_val_if_fail(smiley_data != NULL, NULL);
+ g_return_val_if_fail(smiley_data_len > 0, NULL);
+
+ filename = purple_util_get_image_filename(smiley_data, smiley_data_len);
+
+ if (filename == NULL) {
+ g_free(smiley_data);
+ return NULL;
+ }
+
+ stored_img = purple_imgstore_add(smiley_data, smiley_data_len, filename);
+
+ g_free(filename);
+
+ return stored_img;
+}
+
+void
+purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data,
+ size_t smiley_data_len, const char *filename)
+{
+ PurpleStoredImage *old_img;
+ const char *old_filename = NULL;
+ const char *new_filename = NULL;
+
+ g_return_if_fail(smiley != NULL);
+ g_return_if_fail(smiley_data != NULL);
+ 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);
+ else
+ smiley->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);
+
+ 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);
+
+ if (g_ascii_strcasecmp(old_filename, new_filename)) {
+ purple_smiley_data_unstore(old_filename);
+ purple_imgstore_unref(old_img);
+ }
+}
+
+
+/*****************************************************************************
+ * Public API functions *
+ *****************************************************************************/
+
+static PurpleSmiley *
+purple_smiley_create(const char *shortcut)
+{
+ PurpleSmiley *smiley;
+
+ smiley = g_slice_new0(PurpleSmiley);
+ if (!smiley)
+ return NULL;
+
+ smiley->shortcut = g_strdup(shortcut);
+
+ 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)
+{
+ PurpleSmiley *smiley = NULL;
+
+ g_return_val_if_fail(shortcut != NULL, NULL);
+ g_return_val_if_fail(img != NULL, NULL);
+
+ smiley = purple_smileys_find_by_shortcut(shortcut);
+ if (smiley)
+ return smiley;
+
+ smiley = purple_smiley_create(shortcut);
+ 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);
+
+ return smiley;
+}
+
+PurpleSmiley *
+purple_smiley_new_from_stream(const char *shortcut, guchar *smiley_data,
+ size_t smiley_data_len, const char *filename)
+{
+ PurpleSmiley *smiley;
+
+ g_return_val_if_fail(shortcut != NULL, NULL);
+ g_return_val_if_fail(smiley_data != NULL, NULL);
+ g_return_val_if_fail(smiley_data_len > 0, NULL);
+
+ smiley = purple_smileys_find_by_shortcut(shortcut);
+ if (smiley)
+ return smiley;
+
+ /* purple_smiley_create() sets shortcut */
+ smiley = purple_smiley_create(shortcut);
+ if (!smiley)
+ return NULL;
+
+ purple_smiley_set_data_impl(smiley, smiley_data, smiley_data_len, filename);
+
+ purple_smiley_data_store(smiley->img);
+
+ return smiley;
+}
+
+PurpleSmiley *
+purple_smiley_new_from_file(const char *shortcut, const char *filepath, const char *filename)
+{
+ PurpleSmiley *smiley = NULL;
+ guchar *smiley_data;
+ size_t smiley_data_len;
+
+ g_return_val_if_fail(shortcut != NULL, NULL);
+ g_return_val_if_fail(filepath != NULL, NULL);
+
+ if (read_smiley_file(filepath, &smiley_data, &smiley_data_len))
+ smiley = purple_smiley_new_from_stream(shortcut, smiley_data,
+ smiley_data_len, filename);
+
+ return smiley;
+}
+
+void
+purple_smiley_delete(PurpleSmiley *smiley)
+{
+ guchar *filename = NULL;
+
+ g_return_if_fail(smiley != NULL);
+
+ filename = g_hash_table_lookup(smiley_shortcut_index, smiley->shortcut);
+
+ if (filename != NULL)
+ purple_smileys_remove(smiley);
+
+ if (smiley->img)
+ purple_smiley_data_unstore(purple_imgstore_get_filename(smiley->img));
+
+ purple_smiley_destroy(smiley);
+}
+
+gboolean
+purple_smiley_set_shortcut(PurpleSmiley *smiley, const char *shortcut)
+{
+ char *filename = NULL;
+
+ g_return_val_if_fail(smiley != NULL, FALSE);
+ g_return_val_if_fail(shortcut != NULL, FALSE);
+
+ /* Check out whether the shortcut is already being used. */
+ filename = g_hash_table_lookup(smiley_shortcut_index, shortcut);
+
+ if (filename)
+ return FALSE;
+
+ /* Save filename associated with the current shortcut. */
+ filename = g_strdup(g_hash_table_lookup(
+ smiley_shortcut_index, smiley->shortcut));
+
+ /* If the updating smiley was already inserted to internal indexes, the
+ * shortcut index will need update.
+ * So we remove the old element first, and add the new one right after.
+ */
+ if (filename) {
+ g_hash_table_remove(smiley_shortcut_index, smiley->shortcut);
+ g_hash_table_insert(smiley_shortcut_index,
+ g_strdup(shortcut), filename);
+ }
+
+ g_free(smiley->shortcut);
+ smiley->shortcut = g_strdup(shortcut);
+
+ purple_smileys_save();
+
+ return TRUE;
+}
+
+void
+purple_smiley_set_data(PurpleSmiley *smiley, guchar *smiley_data,
+ size_t smiley_data_len, gboolean keepfilename)
+{
+ gboolean updateindex = FALSE;
+ const char *filename = NULL;
+
+ g_return_if_fail(smiley != NULL);
+ g_return_if_fail(smiley_data != NULL);
+ g_return_if_fail(smiley_data_len > 0);
+
+ /* If the updating smiley was already inserted to internal indexes, the
+ * checksum index will need update. And, considering that the filename
+ * could be regenerated using the file's checksum, the filename index
+ * will be updated too.
+ * So we remove it here, and add it again after the information is
+ * updated by "purple_smiley_set_data_impl" method. */
+ filename = g_hash_table_lookup(smiley_checksum_index, smiley->checksum);
+ if (filename) {
+ g_hash_table_remove(smiley_checksum_index, smiley->checksum);
+ g_hash_table_remove(smiley_data_index, filename);
+
+ updateindex = TRUE;
+ }
+
+ /* Updates the file data. */
+ if ((keepfilename) && (smiley->img) &&
+ (purple_imgstore_get_filename(smiley->img)))
+ purple_smiley_set_data_impl(smiley, smiley_data,
+ smiley_data_len,
+ purple_imgstore_get_filename(smiley->img));
+ else
+ purple_smiley_set_data_impl(smiley, smiley_data,
+ smiley_data_len, NULL);
+
+ /* Reinserts the index item. */
+ filename = purple_imgstore_get_filename(smiley->img);
+ if ((updateindex) && (filename)) {
+ g_hash_table_insert(smiley_checksum_index,
+ g_strdup(smiley->checksum), g_strdup(filename));
+ g_hash_table_insert(smiley_data_index, g_strdup(filename), smiley);
+ }
+
+ purple_smileys_save();
+}
+
+PurpleStoredImage *
+purple_smiley_get_stored_image(const PurpleSmiley *smiley)
+{
+ return purple_imgstore_ref(smiley->img);
+}
+
+const char *purple_smiley_get_shortcut(const PurpleSmiley *smiley)
+{
+ g_return_val_if_fail(smiley != NULL, NULL);
+
+ return smiley->shortcut;
+}
+
+const char *
+purple_smiley_get_checksum(const PurpleSmiley *smiley)
+{
+ g_return_val_if_fail(smiley != NULL, NULL);
+
+ return smiley->checksum;
+}
+
+gconstpointer
+purple_smiley_get_data(const PurpleSmiley *smiley, size_t *len)
+{
+ g_return_val_if_fail(smiley != NULL, NULL);
+
+ if (smiley->img) {
+ if (len != NULL)
+ *len = purple_imgstore_get_size(smiley->img);
+
+ return purple_imgstore_get_data(smiley->img);
+ }
+
+ return NULL;
+}
+
+const char *
+purple_smiley_get_extension(const PurpleSmiley *smiley)
+{
+ if (smiley->img != NULL)
+ return purple_imgstore_get_extension(smiley->img);
+
+ return NULL;
+}
+
+char *purple_smiley_get_full_path(PurpleSmiley *smiley)
+{
+ g_return_val_if_fail(smiley != NULL, NULL);
+
+ if (smiley->img == NULL)
+ return NULL;
+
+ return get_file_full_path(purple_imgstore_get_filename(smiley->img));
+}
+
+static void add_smiley_to_list(gpointer key, gpointer value, gpointer user_data)
+{
+ GList** preturninglist = (GList**)user_data;
+ GList *returninglist = *preturninglist;
+
+ returninglist = g_list_append(returninglist, value);
+
+ *preturninglist = returninglist;
+}
+
+GList *
+purple_smileys_get_all(void)
+{
+ GList *returninglist = NULL;
+
+ g_hash_table_foreach(smiley_data_index, add_smiley_to_list, &returninglist);
+
+ return returninglist;
+}
+
+void
+purple_smileys_add(PurpleSmiley *smiley)
+{
+ const char *filename = NULL;
+
+ g_return_if_fail(smiley != NULL);
+
+ if (!g_hash_table_lookup(smiley_shortcut_index, smiley->shortcut)) {
+ filename = purple_imgstore_get_filename(smiley->img);
+
+ g_hash_table_insert(smiley_data_index, g_strdup(filename), smiley);
+ g_hash_table_insert(smiley_shortcut_index, g_strdup(smiley->shortcut), g_strdup(filename));
+ g_hash_table_insert(smiley_checksum_index, g_strdup(smiley->checksum), g_strdup(filename));
+
+ purple_smileys_save();
+ }
+}
+
+void
+purple_smileys_remove(PurpleSmiley *smiley)
+{
+ const char *filename = NULL;
+
+ g_return_if_fail(smiley != NULL);
+
+ filename = purple_imgstore_get_filename(smiley->img);
+
+ if (g_hash_table_lookup(smiley_data_index, filename)) {
+ g_hash_table_remove(smiley_data_index, filename);
+ 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)
+{
+ PurpleSmiley *smiley = NULL;
+ guchar *filename = NULL;
+
+ g_return_val_if_fail(shortcut != NULL, NULL);
+
+ filename = g_hash_table_lookup(smiley_shortcut_index, shortcut);
+
+ if (filename == NULL)
+ return NULL;
+
+ smiley = g_hash_table_lookup(smiley_data_index, filename);
+
+ if (!smiley)
+ return NULL;
+
+ return smiley;
+}
+
+PurpleSmiley *
+purple_smileys_find_by_checksum(const char *checksum)
+{
+ PurpleSmiley *smiley = NULL;
+ guchar *filename = NULL;
+
+ g_return_val_if_fail(checksum != NULL, NULL);
+
+ filename = g_hash_table_lookup(smiley_checksum_index, checksum);
+
+ if (filename == NULL)
+ return NULL;
+
+ smiley = g_hash_table_lookup(smiley_data_index, filename);
+
+ if (!smiley)
+ return NULL;
+
+ return smiley;
+}
+
+const char *
+purple_smileys_get_storing_dir(void)
+{
+ return smileys_dir;
+}
+
+void
+purple_smileys_init()
+{
+ smiley_data_index = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
+ smiley_shortcut_index = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ smiley_checksum_index = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ smileys_dir = g_build_filename(purple_user_dir(), SMILEYS_DEFAULT_FOLDER, NULL);
+
+ purple_smileys_load();
+}
+
+static gboolean
+free_smiley_data(gpointer key, gpointer value, gpointer user_data)
+{
+ purple_smiley_destroy(value);
+
+ return TRUE;
+}
+
+void
+purple_smileys_uninit()
+{
+ if (save_timer != 0) {
+ purple_timeout_remove(save_timer);
+ save_timer = 0;
+ sync_smileys();
+ }
+
+ g_hash_table_foreach_remove(smiley_data_index, free_smiley_data, NULL);
+ g_hash_table_destroy(smiley_shortcut_index);
+ g_hash_table_destroy(smiley_checksum_index);
+ g_free(smileys_dir);
+}
+
============================================================
--- libpurple/smiley.h 1cbc22d49a09e1f51ef330a81b8da2cc1a32dc70
+++ libpurple/smiley.h 1cbc22d49a09e1f51ef330a81b8da2cc1a32dc70
@@ -0,0 +1,294 @@
+/**
+ * @file Smiley.h Smiley API
+ * @ingroup core
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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
+ *
+ */
+
+#ifndef _PURPLE_SMILEY_H_
+#define _PURPLE_SMILEY_H_
+
+#include "imgstore.h"
+#include "util.h"
+
+/**
+ * A custom smiley.
+ * This contains everything Purple will ever need to know about a custom smiley.
+ * Everything.
+ */
+typedef 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. */
+} PurpleSmiley;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**************************************************************************/
+/** @name Custom Smiley API */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * 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 img The image associated with the smiley.
+ * @param shortcut The custom smiley associated shortcut.
+ *
+ * @return The custom smiley structure filled up.
+ */
+PurpleSmiley *
+purple_smiley_new(PurpleStoredImage *img, const char *shortcut);
+
+/**
+ * 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
+ * will be automaticaly returned.
+ *
+ * @param shortcut The custom smiley associated shortcut.
+ * @param filepath The image file to be imported to a
+ * new custom smiley.
+ *
+ * @return The custom smiley structure filled up.
+ */
+PurpleSmiley *
+purple_smiley_new_from_file(const char *shortcut, const char *filepath,
+ const char *filename);
+
+/**
+ * Destroy the custom smiley and release the associated resources.
+ *
+ * @param smiley The custom smiley.
+ */
+void
+purple_smiley_delete(PurpleSmiley *smiley);
+
+/**
+ * Changes the custom smiley's shortcut.
+ *
+ * @param smiley The custom smiley.
+ * @param shortcut The custom smiley associated shortcut.
+ *
+ * @return TRUE whether the shortcut is not associated with another
+ * custom smiley and the parameters are valid. FALSE otherwise.
+ */
+gboolean
+purple_smiley_set_shortcut(PurpleSmiley *smiley, const char *shortcut);
+
+/**
+ * Changes the custom smiley's data.
+ *
+ * When the filename controling is made outside this API, the param
+ * @keepfilename must be TRUE.
+ * Otherwise, the file and filename will be regenerated, and the
+ * old one will be removed.
+ *
+ * @param smiley The custom smiley.
+ * @param smiley_data The custom smiley data.
+ * @param smiley_data_len The custom smiley data length.
+ * @param keepfilename The current custom smiley's filename must be
+ * kept.
+ */
+void
+purple_smiley_set_data(PurpleSmiley *smiley, guchar *smiley_data,
+ size_t smiley_data_len, gboolean keepfilename);
+
+/**
+ * Returns the custom smiley's associated shortcut.
+ *
+ * @param smiley The custom smiley.
+ *
+ * @return The shortcut.
+ */
+const char *purple_smiley_get_shortcut(const PurpleSmiley *smiley);
+
+/**
+ * Returns the custom smiley data's checksum.
+ *
+ * @param smiley The custom smiley.
+ *
+ * @return The checksum.
+ */
+const char *purple_smiley_get_checksum(const PurpleSmiley *smiley);
+
+/**
+ * Returns the PurpleStoredImage with the reference counter incremented.
+ *
+ * The returned PurpleStoredImage reference counter must be decremented
+ * after use.
+ *
+ * @param smiley The custom smiley.
+ *
+ * @return A PurpleStoredImage reference.
+ */
+PurpleStoredImage *purple_smiley_get_stored_image(const PurpleSmiley *smiley);
+
+/**
+ * Returns the custom smiley's data.
+ *
+ * @param smiley The custom smiley.
+ * @param len If not @c NULL, the length of the icon data returned
+ * will be set in the location pointed to by this.
+ *
+ * @return A pointer to the custom smiley data.
+ */
+gconstpointer purple_smiley_get_data(const PurpleSmiley *smiley, size_t *len);
+
+/**
+ * Returns an extension corresponding to the custom smiley's file type.
+ *
+ * @param smiley The custom smiley.
+ *
+ * @return The custom smiley's extension, "icon" if unknown, or @c NULL if
+ * the image data has disappeared.
+ */
+const char *purple_smiley_get_extension(const PurpleSmiley *smiley);
+
+/**
+ * Returns a full path to an custom smiley.
+ *
+ * If the custom smiley has data and the file exists in the cache, this
+ * will return a full path to the cached file.
+ *
+ * In general, it is not appropriate to be poking in the file cached
+ * directly. If you find yourself wanting to use this function, think
+ * very long and hard about it, and then don't.
+ *
+ * @param smiley The custom smiley.
+ *
+ * @return A full path to the file, or @c NULL under various conditions.
+ * The caller should use #g_free to free the returned string.
+ */
+char *purple_smiley_get_full_path(PurpleSmiley *smiley);
+
+/*@}*/
+
+
+/**************************************************************************/
+/** @name Custom Smiley Subsistem API */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Returns a list of all custom smileys.
+ *
+ * @constreturn A list of all custom smileys.
+ */
+GList *
+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.
+ *
+ * @return The custom smiley (with a reference for the caller) if found,
+ * or @c NULL if not found.
+ */
+PurpleSmiley *
+purple_smileys_find_by_shortcut(const char *shortcut);
+
+/**
+ * Returns the custom smiley given it's checksum.
+ *
+ * @param checksum The custom smiley's checksum.
+ *
+ * @return The custom smiley (with a reference for the caller) if found,
+ * or @c NULL if not found.
+ */
+PurpleSmiley *
+purple_smileys_find_by_checksum(const char *checksum);
+
+/**
+ * Returns the directory used to store custom smiley cached files.
+ *
+ * The default directory is PURPLEDIR/smileys, unless otherwise specified
+ * by purple_buddy_icons_set_cache_dir().
+ *
+ * @return The directory to store custom smyles cached files to.
+ */
+const char *purple_smileys_get_storing_dir(void);
+
+/**
+ * Initializes the custom smiley subsystem.
+ */
+void purple_smileys_init(void);
+
+/**
+ * Uninitializes the custom smiley subsystem.
+ */
+void purple_smileys_uninit(void);
+
+/*@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PURPLE_SMILEY_H_ */
+
============================================================
--- pidgin/gtksmiley.c 4874e411310eb534afe724bae929508dc9314b69
+++ pidgin/gtksmiley.c 4874e411310eb534afe724bae929508dc9314b69
@@ -0,0 +1,562 @@
+/**
+ * @file gtksmiley.c GTK+ Smiley Manager API
+ * @ingroup pidgin
+ */
+
+/*
+ * pidgin
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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 "internal.h"
+#include "pidgin.h"
+
+#include "debug.h"
+#include "notify.h"
+#include "smiley.h"
+
+#include "gtkimhtml.h"
+#include "gtksmiley.h"
+
+typedef struct
+{
+ GtkWidget *parent;
+ GtkWidget *smile;
+ GtkWidget *file_chooser;
+ gchar *filename;
+} PidginSmiley;
+
+typedef struct
+{
+ GtkWidget *window;
+
+ GtkWidget *treeview;
+ GtkListStore *model;
+} SmileyManager;
+
+enum
+{
+ ICON,
+ SHORTCUT,
+ DATA,
+ N_COL
+};
+
+static SmileyManager *smiley_manager = NULL;
+static GSList *gtk_smileys = NULL;
+
+static void
+pidgin_smiley_destroy(PidginSmiley *smiley)
+{
+ gtk_widget_destroy(smiley->parent);
+ g_free(smiley->filename);
+ g_free(smiley);
+}
+
+/******************************************************************************
+ * GtkIMHtmlSmileys stuff
+ *****************************************************************************/
+static void add_gtkimhtml_to_list(GtkIMHtmlSmiley *gtksmiley)
+{
+ gtk_smileys = g_slist_prepend(gtk_smileys, gtksmiley);
+
+ purple_debug_info("gtksmiley", "adding %s to gtk_smileys\n", gtksmiley->smile);
+}
+
+static GtkIMHtmlSmiley *smiley_purple_to_gtkimhtml(PurpleSmiley *smiley)
+{
+ GtkIMHtmlSmiley *gtksmiley;
+ gchar *filename;
+ const gchar *file;
+
+ file = purple_imgstore_get_filename(smiley->img);
+
+ filename = g_build_filename(purple_smileys_get_storing_dir(),file, NULL);
+
+ gtksmiley = g_new0(GtkIMHtmlSmiley,1);
+ gtksmiley->smile = g_strdup(smiley->shortcut);
+ gtksmiley->hidden = FALSE;
+ gtksmiley->file = filename;
+ gtksmiley->flags = GTK_IMHTML_SMILEY_CUSTOM;
+
+ return gtksmiley;
+}
+
+void pidgin_smiley_add_to_list(PurpleSmiley *smiley)
+{
+ GtkIMHtmlSmiley *gtksmiley;
+
+ gtksmiley = smiley_purple_to_gtkimhtml(smiley);
+ add_gtkimhtml_to_list(gtksmiley);
+}
+
+static void destroy_gtksmiley(GtkIMHtmlSmiley *gtksmiley)
+{
+ purple_debug_info("gtksmiley", "destroying %s\n", gtksmiley->smile);
+ g_free(gtksmiley->smile);
+ g_free(gtksmiley->file);
+ g_free(gtksmiley);
+}
+
+void pidgin_smiley_del_from_list(PurpleSmiley *smiley)
+{
+ GSList *list = NULL;
+ GtkIMHtmlSmiley *gtksmiley;
+
+ if (gtk_smileys == NULL)
+ return;
+
+ list = gtk_smileys;
+
+ for (; list; list = list->next) {
+ gtksmiley = (GtkIMHtmlSmiley*)list->data;
+
+ if (strcmp(gtksmiley->smile, smiley->shortcut))
+ continue;
+
+ destroy_gtksmiley(gtksmiley);
+ list = g_slist_delete_link(list, list);
+ break;
+ }
+
+ gtk_smileys = list;
+}
+
+void pidgin_smileys_init(void)
+{
+ GList *purple_smileys;
+ PurpleSmiley *smiley;
+
+ if (gtk_smileys != NULL)
+ return;
+
+ purple_smileys = purple_smileys_get_all();
+
+ for (; purple_smileys; purple_smileys = purple_smileys->next) {
+ smiley = (PurpleSmiley*)purple_smileys->data;
+
+ pidgin_smiley_add_to_list(smiley);
+ }
+}
+
+void pidgin_smileys_uninit(void)
+{
+ GSList *list;
+ GtkIMHtmlSmiley *gtksmiley;
+
+ list = gtk_smileys;
+
+ if (list == NULL)
+ return;
+
+ for (; list; list = g_slist_delete_link(list, list)) {
+ gtksmiley = (GtkIMHtmlSmiley*)list->data;
+ destroy_gtksmiley(gtksmiley);
+ }
+
+ gtk_smileys = NULL;
+}
+
+GSList *pidgin_smileys_get_all(void)
+{
+ return gtk_smileys;
+}
+
+/******************************************************************************
+ * Manager stuff
+ *****************************************************************************/
+
+static void refresh_list(void);
+
+/******************************************************************************
+ * The Add dialog
+ ******************************************************************************/
+
+static void do_add(GtkWidget *widget, PidginSmiley *s)
+{
+ const gchar *entry;
+ PurpleSmiley *emoticon;
+ gchar *file;
+
+ entry = gtk_entry_get_text(GTK_ENTRY(s->smile));
+
+ if (s->filename == NULL || *entry == 0) {
+ purple_notify_error(s->parent, _("Custom Smiley"),
+ _("More Data needed"), NULL);
+ return;
+ }
+
+ purple_debug_info("gtksmiley", "adding a new smiley\n");
+ file = g_path_get_basename(s->filename);
+ emoticon = purple_smiley_new_from_file(entry, s->filename, file);
+ purple_smileys_add(emoticon);
+ g_free(file);
+
+ if (gtk_smileys != NULL)
+ pidgin_smiley_add_to_list(emoticon);
+
+ if (smiley_manager != NULL)
+ refresh_list();
+
+ gtk_widget_destroy(s->parent);
+}
+
+static void do_add_select_cb(GtkWidget *widget, gint resp, PidginSmiley *s)
+{
+ switch (resp) {
+ case GTK_RESPONSE_ACCEPT:
+ do_add(widget, s);
+ break;
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ gtk_widget_destroy(s->parent);
+ break;
+ default:
+ purple_debug_error("gtksmiley", "no valid response\n");
+ break;
+ }
+}
+
+static void do_add_file_cb(GtkWidget *widget, gint resp, PidginSmiley *s)
+{
+ if (resp == GTK_RESPONSE_ACCEPT)
+ s->filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(s->file_chooser));
+}
+
+void pidgin_smiley_add(GtkWidget *widget)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *filech;
+ GtkWidget *window;
+
+ PidginSmiley *s = g_new0(PidginSmiley, 1);
+
+ window = gtk_dialog_new_with_buttons(_("Add Smiley"),
+ GTK_WINDOW(widget),
+ GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
+ GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NULL);
+ s->parent = window;
+
+ gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
+
+ g_signal_connect(window, "response", G_CALLBACK(do_add_select_cb), s);
+
+ /* The vbox */
+ vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), vbox);
+ gtk_widget_show(vbox);
+
+ /* The hbox */
+ hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)), hbox);
+
+ label = gtk_label_new(_("Select the file:"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ s->file_chooser = gtk_file_chooser_dialog_new(_("Custom Smiley"),
+ GTK_WINDOW(window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ g_signal_connect(s->file_chooser, "response", G_CALLBACK(do_add_file_cb), s);
+
+ filech = gtk_file_chooser_button_new_with_dialog(GTK_WIDGET(s->file_chooser));
+
+ gtk_box_pack_end(GTK_BOX(hbox), filech, TRUE, TRUE, 0);
+ gtk_widget_show(filech);
+
+ gtk_widget_show(hbox);
+
+ /* info */
+ hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)),hbox);
+
+ /* Smiley shortcut */
+ label = gtk_label_new(_("Define Smiley's shortcut"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ s->smile = gtk_entry_new();
+ gtk_entry_set_activates_default(GTK_ENTRY(s->smile), TRUE);
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(s->smile));
+
+ g_signal_connect(s->smile, "activate", G_CALLBACK(do_add), s);
+
+ gtk_box_pack_end(GTK_BOX(hbox), s->smile, FALSE, FALSE, 0);
+ gtk_widget_show(s->smile);
+
+ gtk_widget_show(hbox);
+
+ gtk_widget_show(GTK_WIDGET(window));
+ g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(pidgin_smiley_destroy), s);
+ g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(purple_notify_close_with_handle), s);
+}
+
+/******************************************************************************
+ * Delete smiley
+ *****************************************************************************/
+static void delete_foreach(GtkTreeModel *model, GtkTreePath *path,
+ GtkTreeIter *iter, gpointer data)
+{
+ PurpleSmiley *smiley;
+ char *shortcut;
+ SmileyManager *dialog;
+
+ dialog = (SmileyManager*)data;
+
+ gtk_tree_model_get(model, iter,
+ SHORTCUT, &shortcut,
+ -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 {
+ pidgin_smiley_del_from_list(smiley);
+ purple_smiley_delete(smiley);
+ }
+
+ g_free(shortcut);
+}
+
+static void smiley_delete(SmileyManager *dialog)
+{
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
+ gtk_tree_selection_selected_foreach(selection, delete_foreach, dialog);
+}
+/******************************************************************************
+ * The Smiley Manager
+ *****************************************************************************/
+static void add_columns(GtkWidget *treeview, SmileyManager *dialog)
+{
+ GtkCellRenderer *rend;
+ GtkTreeViewColumn *column;
+
+ /* Icon */
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_title(column, _("Smiley"));
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+
+ rend = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, rend, FALSE);
+ gtk_tree_view_column_add_attribute(column, rend, "pixbuf", ICON);
+
+ /* Shortcut */
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_title(column, _("Shortcut"));
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+
+ rend = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, rend, TRUE);
+ gtk_tree_view_column_add_attribute(column, rend, "text", SHORTCUT);
+}
+
+static void store_smiley_add(PurpleSmiley *smiley)
+{
+ GtkTreeIter iter;
+ PurpleStoredImage *img;
+ GdkPixbuf *sized_smiley = NULL;
+
+ if (smiley_manager == NULL)
+ return;
+
+ img = purple_smiley_get_stored_image(smiley);
+
+ if (img != NULL) {
+ GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+ GdkPixbuf *smiley_image = NULL;
+
+ gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(img),
+ purple_imgstore_get_size(img), NULL);
+ gdk_pixbuf_loader_close(loader, NULL);
+ smiley_image = gdk_pixbuf_loader_get_pixbuf(loader);
+
+ purple_imgstore_unref(img);
+
+ if (smiley_image != NULL)
+ sized_smiley = gdk_pixbuf_scale_simple(smiley_image,
+ 22, 22, GDK_INTERP_HYPER);
+
+ g_object_unref(loader);
+ }
+
+
+ gtk_list_store_append(smiley_manager->model, &iter);
+
+ gtk_list_store_set(smiley_manager->model, &iter,
+ ICON, sized_smiley,
+ SHORTCUT, smiley->shortcut,
+ DATA, NULL,
+ -1);
+
+ if (sized_smiley != NULL)
+ g_object_unref(G_OBJECT(sized_smiley));
+}
+
+static void populate_smiley_list(SmileyManager *dialog)
+{
+ GList *list;
+ PurpleSmiley *emoticon;
+
+ gtk_list_store_clear(dialog->model);
+
+ for(list = purple_smileys_get_all(); list != NULL; list = list->next) {
+ emoticon = (PurpleSmiley*)list->data;
+
+ store_smiley_add(emoticon);
+ }
+}
+
+static void smile_selected_cb(GtkTreeSelection *sel, SmileyManager *dialog)
+{
+ gint selected;
+
+ selected = gtk_tree_selection_count_selected_rows(sel);
+
+ gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog->window),
+ GTK_RESPONSE_NO, selected > 0);
+}
+
+static GtkWidget *smiley_list_create(SmileyManager *dialog)
+{
+ GtkWidget *sw;
+ GtkWidget *treeview;
+ GtkTreeSelection *sel;
+
+ sw = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
+ GTK_SHADOW_NONE);
+ gtk_widget_show(sw);
+
+ /* Create the list model */
+ dialog->model = gtk_list_store_new(N_COL,
+ GDK_TYPE_PIXBUF, /* ICON */
+ G_TYPE_STRING, /* SHORTCUT */
+ G_TYPE_POINTER /* DATA */
+ );
+
+ /* the actual treeview */
+ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
+ dialog->treeview = treeview;
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
+ gtk_container_add(GTK_CONTAINER(sw), treeview);
+
+ g_signal_connect(G_OBJECT(sel), "changed",
+ G_CALLBACK(smile_selected_cb), dialog);
+
+ gtk_widget_show(treeview);
+
+ add_columns(treeview, dialog);
+ populate_smiley_list(dialog);
+
+ return sw;
+}
+
+static void refresh_list()
+{
+ populate_smiley_list(smiley_manager);
+}
+
+static void smiley_manager_select_cb(GtkWidget *widget, gint resp, SmileyManager *dialog)
+{
+ switch (resp) {
+ case GTK_RESPONSE_YES:
+ pidgin_smiley_add(dialog->window);
+ break;
+ case GTK_RESPONSE_NO:
+ smiley_delete(dialog);
+ refresh_list();
+ break;
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CLOSE:
+ gtk_widget_destroy(dialog->window);
+ g_free(smiley_manager);
+ smiley_manager = NULL;
+ break;
+ default:
+ purple_debug_info("gtksmiley", "No valid selection\n");
+ break;
+ }
+}
+
+void pidgin_smiley_manager_show(void)
+{
+ SmileyManager *dialog;
+ GtkWidget *win;
+ GtkWidget *sw;
+ GtkWidget *vbox;
+
+ if (smiley_manager) {
+ gtk_window_present(GTK_WINDOW(smiley_manager->window));
+ return;
+ }
+
+ dialog = g_new0(SmileyManager, 1);
+ smiley_manager = dialog;
+
+ dialog->window = win = gtk_dialog_new_with_buttons(
+ _("Custom Smiley Manager"),
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_ADD, GTK_RESPONSE_YES,
+ GTK_STOCK_DELETE, GTK_RESPONSE_NO,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ NULL);
+
+ gtk_window_set_default_size(GTK_WINDOW(win), 50, 400);
+ gtk_container_set_border_width(GTK_CONTAINER(win),PIDGIN_HIG_BORDER);
+
+ g_signal_connect(win, "response", G_CALLBACK(smiley_manager_select_cb),
+ dialog);
+
+ /* The vbox */
+ vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(win)->vbox), vbox);
+ gtk_widget_show(vbox);
+
+ /* get the scrolled window with all stuff */
+ sw = smiley_list_create(dialog);
+ gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
+ gtk_widget_show(sw);
+
+ gtk_widget_show(win);
+}
+
============================================================
--- pidgin/gtksmiley.h 7d6b7f90fc88eb76ada117952a8a478ed5e216a5
+++ pidgin/gtksmiley.h 7d6b7f90fc88eb76ada117952a8a478ed5e216a5
@@ -0,0 +1,79 @@
+/**
+ * @file gtksmiley.h GTK+ Custom Smiley API
+ * @ingroup pidgin
+ */
+
+/* pidgin
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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
+ */
+
+#ifndef _PIDGIN_GTKSMILEY_H_
+#define _PIDGIN_GTKSMILEY_H_
+
+#include "smiley.h"
+
+/**
+ * Add a PurpleSmiley to the GtkIMHtmlSmiley's list to be able to use it
+ * in pidgin
+ *
+ * @param smiley The smiley to be added.
+ */
+void pidgin_smiley_add_to_list(PurpleSmiley *smiley);
+
+/**
+ * Delete a PurpleSmiley from the GtkIMHtmlSmiley's list
+ *
+ * @param smiley The smiley to be deleted.
+ */
+void pidgin_smiley_del_from_list(PurpleSmiley *smiley);
+
+/**
+ * Load the GtkIMHtml list
+ */
+void pidgin_smileys_init(void);
+
+/**
+ * Uninit the GtkIMHtml list
+ */
+void pidgin_smileys_uninit(void);
+
+/**
+ * Returns a GSList with the GtkIMHtmlSmiley of each custom smiley
+ *
+ * @constreturn A GtkIMHmlSmiley list
+ */
+GSList* pidgin_smileys_get_all(void);
+
+/******************************************************************************
+ * Smiley Manager
+ *****************************************************************************/
+/**
+ * Displays the Smiley Manager Window
+ */
+void pidgin_smiley_manager_show(void);
+
+/**
+ * Displays the "Add smiley" Dialog Box
+ *
+ * @param widget The parent widget to be linked
+ */
+void pidgin_smiley_add(GtkWidget *widget);
+
+#endif /* _PIDGIN_GTKSMILEY_H_*/
============================================================
--- libpurple/Makefile.am 970eb388b52914c9e0287f4010996ba5ce7524c9
+++ libpurple/Makefile.am 3988488ad4e4e5530fdcfa3fbf72df65f513c913
@@ -68,6 +68,7 @@ purple_coresources = \
savedstatuses.c \
server.c \
signals.c \
+ smiley.c \
dnsquery.c \
dnssrv.c\
status.c \
@@ -120,6 +121,7 @@ purple_coreheaders = \
savedstatuses.h \
server.h \
signals.h \
+ smiley.h \
dnsquery.h \
dnssrv.h \
status.h \
============================================================
--- libpurple/Makefile.mingw c0ffea1585abe54d11e8598926e5d515f2a14df3
+++ libpurple/Makefile.mingw b2ff98bfc1a3f857f03e3b3c6f337ed251d07635
@@ -65,6 +65,7 @@ C_SRC = \
savedstatuses.c \
server.c \
signals.c \
+ smiley.c \
sound.c \
sslconn.c \
status.c \
============================================================
--- libpurple/core.c be64471588034c0111105f55521e0bd7ea5c3f0a
+++ libpurple/core.c 2ff5b2540ab64eeeb06ce98fc862a2dfb190abd5
@@ -43,6 +43,7 @@
#include "proxy.h"
#include "savedstatuses.h"
#include "signals.h"
+#include "smiley.h"
#include "sound.h"
#include "sslconn.h"
#include "status.h"
@@ -162,6 +163,7 @@ purple_core_init(const char *ui)
purple_stun_init();
purple_xfers_init();
purple_idle_init();
+ purple_smileys_init();
/*
* Call this early on to try to auto-detect our IP address and
@@ -190,6 +192,7 @@ purple_core_quit(void)
purple_connections_disconnect_all();
/* Save .xml files, remove signals, etc. */
+ purple_smileys_uninit();
purple_idle_uninit();
purple_ssl_uninit();
purple_pounces_uninit();
============================================================
--- libpurple/util.c 1b362c0534b084dde914314ee50a1e392bd3e763
+++ libpurple/util.c 0b66daf2305037d8f954a8abecdda663267a881c
@@ -921,7 +921,6 @@ purple_markup_unescape_entity(const char
{
const char *pln;
int len, pound;
- char temp[2];
if (!text || *text != '&')
return NULL;
@@ -944,9 +943,8 @@ purple_markup_unescape_entity(const char
pln = "\302\256"; /* or use g_unichar_to_utf8(0xae); */
else if(IS_ENTITY("'"))
pln = "\'";
- else if(*(text+1) == '#' &&
- (sscanf(text, "&#%u%1[;]", £, temp) == 2 || sscanf(text, "&#x%x%1[;]", £, temp) == 2) &&
- pound != 0) {
+ else if(*(text+1) == '#' && (sscanf(text, "&#%u;", £) == 1) &&
+ pound != 0 && *(text+3+(gint)log10(pound)) == ';') {
static char buf[7];
int buflen = g_unichar_to_utf8((gunichar)pound, buf);
buf[buflen] = '\0';
@@ -2889,7 +2887,7 @@ char *
}
char *
-purple_util_get_image_filename(gconstpointer image_data, size_t image_len)
+purple_util_get_image_checksum(gconstpointer image_data, size_t image_len)
{
PurpleCipherContext *context;
gchar digest[41];
@@ -2910,8 +2908,15 @@ purple_util_get_image_filename(gconstpoi
}
purple_cipher_context_destroy(context);
+ return g_strdup(digest);
+}
+
+char *
+purple_util_get_image_filename(gconstpointer image_data, size_t image_len)
+{
/* Return the filename */
- return g_strdup_printf("%s.%s", digest,
+ return g_strdup_printf("%s.%s",
+ purple_util_get_image_checksum(image_data, image_len),
purple_util_get_image_extension(image_data, image_len));
}
============================================================
--- libpurple/util.h 55564322be9c4ef39a3dce010516ee0bc1b3668c
+++ libpurple/util.h f659080e639fc34b8335d375830eedc0c210b9b7
@@ -702,6 +702,11 @@ purple_util_get_image_extension(gconstpo
purple_util_get_image_extension(gconstpointer data, size_t len);
/**
+ * Returns a SHA-1 hash string of the data passed in.
+ */
+char *purple_util_get_image_checksum(gconstpointer image_data, size_t image_len);
+
+/**
* Returns a SHA-1 hash string of the data passed in with the correct file
* extention appended.
*/
============================================================
--- pidgin/Makefile.am 510e3537b50a6f6f3da3cb4cc110d44d3e183beb
+++ pidgin/Makefile.am dcb4b4e3b78d20b8574c563d56afd0f611594255
@@ -111,6 +111,7 @@ pidgin_SOURCES = \
gtksavedstatuses.c \
gtkscrollbook.c \
gtksession.c \
+ gtksmiley.c \
gtksound.c \
gtksourceiter.c \
gtksourceundomanager.c \
@@ -163,6 +164,7 @@ pidgin_headers = \
gtksavedstatuses.h \
gtkscrollbook.h \
gtksession.h \
+ gtksmiley.h \
gtksound.h \
gtksourceiter.h \
gtksourceundomanager.h \
============================================================
--- pidgin/Makefile.mingw 1ebbd8f7340dc68421109c46cc196236120867b2
+++ pidgin/Makefile.mingw 3627076c259e48c624e401b008588c7691b4c8c8
@@ -86,6 +86,7 @@ PIDGIN_C_SRC = \
gtkroomlist.c \
gtksavedstatuses.c \
gtkscrollbook.c \
+ gtksmiley.c \
gtksound.c \
gtksourceiter.c \
gtksourceundomanager.c \
============================================================
--- pidgin/gtkblist.c e1f5d7320ccc714f48b64e180c8b94fe157706fb
+++ pidgin/gtkblist.c 3f10f8fecc9476a86e3599c38b696f71ea9b37ec
@@ -57,6 +57,7 @@
#include "gtkroomlist.h"
#include "gtkstatusbox.h"
#include "gtkscrollbook.h"
+#include "gtksmiley.h"
#include "gtkutils.h"
#include "pidgin/minidialog.h"
#include "pidgin/pidgintooltip.h"
@@ -3078,6 +3079,7 @@ static GtkItemFactoryEntry blist_menu[]
{ N_("/_Tools"), NULL, NULL, 0, "<Branch>", NULL },
{ N_("/Tools/Buddy _Pounces"), NULL, pidgin_pounces_manager_show, 1, "<Item>", NULL },
{ N_("/Tools/_Certificates"), NULL, pidgin_certmgr_show, 0, "<Item>", NULL },
+ { N_("/Tools/Smile_y"), "<CTL>Y", pidgin_smiley_manager_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SMILEY },
{ N_("/Tools/Plu_gins"), "<CTL>U", pidgin_plugin_dialog_show, 2, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS },
{ N_("/Tools/Pr_eferences"), "<CTL>P", pidgin_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
{ N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "<Item>", NULL },
============================================================
--- pidgin/gtkconv.c 99510514ab71d30ff225dcd08bbc64e4ad7c31cb
+++ pidgin/gtkconv.c f9bde3ba022110b3bef5c9484c8a7ab9f77e50a9
@@ -5044,6 +5044,12 @@ private_gtkconv_new(PurpleConversation *
nbr_nick_colors = NUM_NICK_COLORS;
nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->imhtml)->base[GTK_STATE_NORMAL]);
}
+
+ /* We don't want to see the custom smileys if our buddy send us the
+ * defined shortcut. */
+ pidgin_themes_smiley_themeize(gtkconv->imhtml);
+ /* We want to see our smileys in the entry */
+ pidgin_themes_smiley_themeize_custom(gtkconv->entry);
}
static void
@@ -5350,8 +5356,6 @@ pidgin_conv_write_conv(PurpleConversatio
char *bracket;
int tag_count = 0;
gboolean is_rtl_message = FALSE;
- GtkSmileyTree *tree = NULL;
- GHashTable *smiley_data = NULL;
g_return_if_fail(conv != NULL);
gtkconv = PIDGIN_CONVERSATION(conv);
@@ -5510,14 +5514,8 @@ pidgin_conv_write_conv(PurpleConversatio
if (!(flags & PURPLE_MESSAGE_RECV))
{
- /* Temporarily revert to the original smiley-data to avoid showing up
- * custom smileys of the buddy when sending message
- */
- tree = GTK_IMHTML(gtkconv->imhtml)->default_smilies;
- GTK_IMHTML(gtkconv->imhtml)->default_smilies =
- GTK_IMHTML(gtkconv->entry)->default_smilies;
- smiley_data = GTK_IMHTML(gtkconv->imhtml)->smiley_data;
- GTK_IMHTML(gtkconv->imhtml)->smiley_data = GTK_IMHTML(gtkconv->entry)->smiley_data;
+ /* We want to see our own smileys. Need to revert it after send*/
+ pidgin_themes_smiley_themeize_custom(gtkconv->imhtml);
}
/* TODO: These colors should not be hardcoded so log.c can use them */
@@ -5763,8 +5761,7 @@ pidgin_conv_write_conv(PurpleConversatio
if (!(flags & PURPLE_MESSAGE_RECV))
{
/* Restore the smiley-data */
- GTK_IMHTML(gtkconv->imhtml)->default_smilies = tree;
- GTK_IMHTML(gtkconv->imhtml)->smiley_data = smiley_data;
+ pidgin_themes_smiley_themeize(gtkconv->imhtml);
}
purple_signal_emit(pidgin_conversations_get_handle(),
============================================================
--- pidgin/gtkimhtmltoolbar.c e21be01eb28ab2c96012516d71ad3558dab32185
+++ pidgin/gtkimhtmltoolbar.c 040b48544e6fd9aeef583eb8fab648122017903f
@@ -36,6 +36,7 @@
#include "gtkdialogs.h"
#include "gtkimhtmltoolbar.h"
+#include "gtksmiley.h"
#include "gtkthemes.h"
#include "gtkutils.h"
@@ -626,6 +627,34 @@ sort_smileys(struct smiley_button_list *
image = gtk_image_new_from_file(filename);
gtk_widget_size_request(image, &size);
+
+ if (size.width > 24) { /* This is a custom smiley, let's scale it */
+ GdkPixbuf *pixbuf = NULL;
+ GdkPixbuf *resized;
+ GtkImageType type;
+
+ type = gtk_image_get_storage_type(GTK_IMAGE(image));
+
+ if (type == GTK_IMAGE_PIXBUF) {
+ pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image));
+ }
+ else if (type == GTK_IMAGE_ANIMATION) {
+ GdkPixbufAnimation *animation;
+
+ animation = gtk_image_get_animation(GTK_IMAGE(image));
+
+ pixbuf = gdk_pixbuf_animation_get_static_image(animation);
+ }
+
+ if (pixbuf != NULL) {
+ resized = gdk_pixbuf_scale_simple(pixbuf, 24, 24,
+ GDK_INTERP_HYPER);
+ image = gtk_image_new_from_pixbuf(resized);
+
+ gtk_widget_size_request(image, &size);
+ }
+ }
+
(*width) += size.width;
button = gtk_button_new();
@@ -688,6 +717,7 @@ insert_smiley_cb(GtkWidget *smiley, GtkI
GtkWidget *dialog;
GtkWidget *smiley_table = NULL;
GSList *smileys, *unique_smileys = NULL;
+ GSList *custom_smileys = NULL;
if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smiley))) {
destroy_smiley_dialog(toolbar);
@@ -709,6 +739,15 @@ insert_smiley_cb(GtkWidget *smiley, GtkI
smileys = smileys->next;
}
+ custom_smileys = pidgin_smileys_get_all();
+
+ while (custom_smileys) {
+ GtkIMHtmlSmiley *smiley = custom_smileys->data;
+ unique_smileys = g_slist_append(unique_smileys, smiley);
+
+ custom_smileys = custom_smileys->next;
+ }
+
dialog = pidgin_create_dialog(_("Smile!"), 0, "smiley_dialog", FALSE);
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
============================================================
--- pidgin/gtkmain.c e68e49ba37082ccfe6da1a8e78e470c4ef15dac4
+++ pidgin/gtkmain.c 4a0af045f8c7b39c32222acc91bd0a95814c5fca
@@ -61,6 +61,7 @@
#include "gtkroomlist.h"
#include "gtksavedstatuses.h"
#include "gtksession.h"
+#include "gtksmiley.h"
#include "gtksound.h"
#include "gtkthemes.h"
#include "gtkutils.h"
@@ -327,6 +328,7 @@ pidgin_ui_init(void)
pidgin_roomlist_init();
pidgin_log_init();
pidgin_docklet_init();
+ pidgin_smileys_init();
}
static GHashTable *ui_info = NULL;
@@ -343,6 +345,7 @@ pidgin_quit(void)
pidgin_plugins_save();
/* Uninit */
+ pidgin_smileys_uninit();
pidgin_conversations_uninit();
pidgin_status_uninit();
pidgin_docklet_uninit();
============================================================
--- pidgin/gtkthemes.c b99c6f0a562c6a26d2b96c15afad6d8ea41c9d58
+++ pidgin/gtkthemes.c 7a2ed108d00a89a1b809cee04e60d53338ccea93
@@ -31,6 +31,7 @@
#include "gtkconv.h"
#include "gtkdialogs.h"
#include "gtkimhtml.h"
+#include "gtksmiley.h"
#include "gtkthemes.h"
GSList *smiley_themes = NULL;
@@ -119,7 +120,7 @@ void pidgin_themes_remove_smiley_theme(c
g_free(theme_dir);
}
-void pidgin_themes_smiley_themeize(GtkWidget *imhtml)
+static void _pidgin_themes_smiley_themeize(GtkWidget *imhtml, gboolean custom)
{
struct smiley_list *list;
if (!current_smiley_theme)
@@ -134,10 +135,30 @@ void pidgin_themes_smiley_themeize(GtkWi
gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml), sml, icons->data);
icons = icons->next;
}
+
+ if (custom == TRUE) {
+ icons = pidgin_smileys_get_all();
+
+ while (icons) {
+ gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml), sml, icons->data);
+ icons = icons->next;
+ }
+ }
+
list = list->next;
}
}
+void pidgin_themes_smiley_themeize(GtkWidget *imhtml)
+{
+ _pidgin_themes_smiley_themeize(imhtml, FALSE);
+}
+
+void pidgin_themes_smiley_themeize_custom(GtkWidget *imhtml)
+{
+ _pidgin_themes_smiley_themeize(imhtml, TRUE);
+}
+
static void
pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme *theme)
{
@@ -340,8 +361,9 @@ void pidgin_themes_load_smiley_theme(con
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(PIDGIN_CONVERSATION(conv)->entry);
+ pidgin_themes_smiley_themeize_custom(PIDGIN_CONVERSATION(conv)->entry);
}
}
}
============================================================
--- pidgin/gtkthemes.h af5b0d12647090de57148cf520133760e903e8b3
+++ pidgin/gtkthemes.h 7d5ad9d4b680224609bb343cd28b5e6b86622607
@@ -48,6 +48,7 @@ void pidgin_themes_smiley_themeize(GtkWi
void pidgin_themes_init(void);
gboolean pidgin_themes_smileys_disabled(void);
void pidgin_themes_smiley_themeize(GtkWidget *);
+void pidgin_themes_smiley_themeize_custom(GtkWidget *);
void pidgin_themes_smiley_theme_probe(void);
void pidgin_themes_load_smiley_theme(const char *file, gboolean load);
void pidgin_themes_remove_smiley_theme(const char *file);
============================================================
--- pidgin/gtkutils.c f97856e347a3079e0e816589d6bd3e1827caeb82
+++ pidgin/gtkutils.c 6c279088a0bbb992886e68f6e5fa4918f763db7a
@@ -103,7 +103,7 @@ pidgin_setup_imhtml(GtkWidget *imhtml)
g_signal_connect(G_OBJECT(imhtml), "url_clicked",
G_CALLBACK(url_clicked_cb), NULL);
- pidgin_themes_smiley_themeize(imhtml);
+ pidgin_themes_smiley_themeize_custom(imhtml);
gtk_imhtml_set_funcs(GTK_IMHTML(imhtml), >kimhtml_cbs);
More information about the Commits
mailing list