Mercurial > pidgin
diff libpurple/buddyicon.c @ 15373:5fe8042783c1
Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Sat, 20 Jan 2007 02:32:10 +0000 |
parents | |
children | 32c366eeeb99 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/buddyicon.c Sat Jan 20 02:32:10 2007 +0000 @@ -0,0 +1,575 @@ +/** + * @file icon.c Buddy Icon API + * @ingroup core + * + * gaim + * + * Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "internal.h" +#include "buddyicon.h" +#include "conversation.h" +#include "dbus-maybe.h" +#include "debug.h" +#include "util.h" + +static GHashTable *account_cache = NULL; +static char *cache_dir = NULL; +static gboolean icon_caching = TRUE; + +static GaimBuddyIcon * +gaim_buddy_icon_create(GaimAccount *account, const char *username) +{ + GaimBuddyIcon *icon; + GHashTable *icon_cache; + + icon = g_new0(GaimBuddyIcon, 1); + GAIM_DBUS_REGISTER_POINTER(icon, GaimBuddyIcon); + + gaim_buddy_icon_set_account(icon, account); + gaim_buddy_icon_set_username(icon, username); + + icon_cache = g_hash_table_lookup(account_cache, account); + + if (icon_cache == NULL) + { + icon_cache = g_hash_table_new(g_str_hash, g_str_equal); + + g_hash_table_insert(account_cache, account, icon_cache); + } + + g_hash_table_insert(icon_cache, + (char *)gaim_buddy_icon_get_username(icon), icon); + return icon; +} + +GaimBuddyIcon * +gaim_buddy_icon_new(GaimAccount *account, const char *username, + void *icon_data, size_t icon_len) +{ + GaimBuddyIcon *icon; + + g_return_val_if_fail(account != NULL, NULL); + g_return_val_if_fail(username != NULL, NULL); + g_return_val_if_fail(icon_data != NULL, NULL); + g_return_val_if_fail(icon_len > 0, NULL); + + icon = gaim_buddy_icons_find(account, username); + + if (icon == NULL) + icon = gaim_buddy_icon_create(account, username); + + gaim_buddy_icon_ref(icon); + gaim_buddy_icon_set_data(icon, icon_data, icon_len); + gaim_buddy_icon_set_path(icon, NULL); + + /* gaim_buddy_icon_set_data() makes blist.c or + * conversation.c, or both, take a reference. + * + * Plus, we leave one for the caller of this function. + */ + + return icon; +} + +void +gaim_buddy_icon_destroy(GaimBuddyIcon *icon) +{ + GaimConversation *conv; + GaimAccount *account; + GHashTable *icon_cache; + const char *username; + GSList *sl, *list; + + g_return_if_fail(icon != NULL); + + if (icon->ref_count > 0) + { + /* If the ref count is greater than 0, then we weren't called from + * gaim_buddy_icon_unref(). So we go through and ask everyone to + * unref us. Then we return, since we know somewhere along the + * line we got called recursively by one of the unrefs, and the + * icon is already destroyed. + */ + account = gaim_buddy_icon_get_account(icon); + username = gaim_buddy_icon_get_username(icon); + + conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, username, account); + if (conv != NULL) + gaim_conv_im_set_icon(GAIM_CONV_IM(conv), NULL); + + for (list = sl = gaim_find_buddies(account, username); sl != NULL; + sl = sl->next) + { + GaimBuddy *buddy = (GaimBuddy *)sl->data; + + gaim_buddy_set_icon(buddy, NULL); + } + + g_slist_free(list); + + return; + } + + icon_cache = g_hash_table_lookup(account_cache, + gaim_buddy_icon_get_account(icon)); + + if (icon_cache != NULL) + g_hash_table_remove(icon_cache, gaim_buddy_icon_get_username(icon)); + + g_free(icon->username); + g_free(icon->data); + g_free(icon->path); + GAIM_DBUS_UNREGISTER_POINTER(icon); + g_free(icon); +} + +GaimBuddyIcon * +gaim_buddy_icon_ref(GaimBuddyIcon *icon) +{ + g_return_val_if_fail(icon != NULL, NULL); + + icon->ref_count++; + + return icon; +} + +GaimBuddyIcon * +gaim_buddy_icon_unref(GaimBuddyIcon *icon) +{ + g_return_val_if_fail(icon != NULL, NULL); + g_return_val_if_fail(icon->ref_count > 0, NULL); + + icon->ref_count--; + + if (icon->ref_count == 0) + { + gaim_buddy_icon_destroy(icon); + + return NULL; + } + + return icon; +} + +void +gaim_buddy_icon_update(GaimBuddyIcon *icon) +{ + GaimConversation *conv; + GaimAccount *account; + const char *username; + GSList *sl, *list; + + g_return_if_fail(icon != NULL); + + account = gaim_buddy_icon_get_account(icon); + username = gaim_buddy_icon_get_username(icon); + + for (list = sl = gaim_find_buddies(account, username); sl != NULL; + sl = sl->next) + { + GaimBuddy *buddy = (GaimBuddy *)sl->data; + + gaim_buddy_set_icon(buddy, icon); + } + + g_slist_free(list); + + conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, username, account); + + if (conv != NULL) + gaim_conv_im_set_icon(GAIM_CONV_IM(conv), icon); +} + +static void +delete_icon_cache_file(const char *dirname, const char *old_icon) +{ + struct stat st; + + g_return_if_fail(dirname != NULL); + g_return_if_fail(old_icon != NULL); + + if (g_stat(old_icon, &st) == 0) + g_unlink(old_icon); + else + { + char *filename = g_build_filename(dirname, old_icon, NULL); + if (g_stat(filename, &st) == 0) + g_unlink(filename); + g_free(filename); + } + gaim_debug_info("buddyicon", "Uncached file %s\n", old_icon); +} + +void +gaim_buddy_icon_cache(GaimBuddyIcon *icon, GaimBuddy *buddy) +{ + const guchar *data; + const char *dirname; + char *random; + char *filename; + const char *old_icon; + size_t len = 0; + FILE *file = NULL; + + g_return_if_fail(icon != NULL); + g_return_if_fail(buddy != NULL); + + if (!gaim_buddy_icons_is_caching()) + return; + + data = gaim_buddy_icon_get_data(icon, &len); + + random = g_strdup_printf("%x", g_random_int()); + dirname = gaim_buddy_icons_get_cache_dir(); + filename = g_build_filename(dirname, random, NULL); + old_icon = gaim_blist_node_get_string((GaimBlistNode*)buddy, "buddy_icon"); + + if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) + { + gaim_debug_info("buddyicon", "Creating icon cache directory.\n"); + + if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) + { + gaim_debug_error("buddyicon", + "Unable to create directory %s: %s\n", + dirname, strerror(errno)); + } + } + + if ((file = g_fopen(filename, "wb")) != NULL) + { + fwrite(data, 1, len, file); + fclose(file); + gaim_debug_info("buddyicon", "Wrote file %s\n", filename); + } + else + { + gaim_debug_error("buddyicon", "Unable to create file %s: %s\n", + filename, strerror(errno)); + g_free(filename); + g_free(random); + return; + } + + g_free(filename); + + if (old_icon != NULL) + delete_icon_cache_file(dirname, old_icon); + + gaim_blist_node_set_string((GaimBlistNode *)buddy, "buddy_icon", random); + + g_free(random); +} + +void +gaim_buddy_icon_uncache(GaimBuddy *buddy) +{ + const char *old_icon; + + g_return_if_fail(buddy != NULL); + + old_icon = gaim_blist_node_get_string((GaimBlistNode *)buddy, "buddy_icon"); + + if (old_icon != NULL) + delete_icon_cache_file(gaim_buddy_icons_get_cache_dir(), old_icon); + + gaim_blist_node_remove_setting((GaimBlistNode *)buddy, "buddy_icon"); + + /* Unset the icon in case this function is called from + * something other than gaim_buddy_set_icon(). */ + if (buddy->icon != NULL) + { + gaim_buddy_icon_unref(buddy->icon); + buddy->icon = NULL; + } +} + +void +gaim_buddy_icon_set_account(GaimBuddyIcon *icon, GaimAccount *account) +{ + g_return_if_fail(icon != NULL); + g_return_if_fail(account != NULL); + + icon->account = account; +} + +void +gaim_buddy_icon_set_username(GaimBuddyIcon *icon, const char *username) +{ + g_return_if_fail(icon != NULL); + g_return_if_fail(username != NULL); + + g_free(icon->username); + icon->username = g_strdup(username); +} + +void +gaim_buddy_icon_set_data(GaimBuddyIcon *icon, void *data, size_t len) +{ + g_return_if_fail(icon != NULL); + + g_free(icon->data); + + if (data != NULL && len > 0) + { + icon->data = g_memdup(data, len); + icon->len = len; + } + else + { + icon->data = NULL; + icon->len = 0; + } + + gaim_buddy_icon_update(icon); +} + +void +gaim_buddy_icon_set_path(GaimBuddyIcon *icon, const gchar *path) +{ + g_return_if_fail(icon != NULL); + + g_free(icon->path); + icon->path = g_strdup(path); +} + +GaimAccount * +gaim_buddy_icon_get_account(const GaimBuddyIcon *icon) +{ + g_return_val_if_fail(icon != NULL, NULL); + + return icon->account; +} + +const char * +gaim_buddy_icon_get_username(const GaimBuddyIcon *icon) +{ + g_return_val_if_fail(icon != NULL, NULL); + + return icon->username; +} + +const guchar * +gaim_buddy_icon_get_data(const GaimBuddyIcon *icon, size_t *len) +{ + g_return_val_if_fail(icon != NULL, NULL); + + if (len != NULL) + *len = icon->len; + + return icon->data; +} + +const char * +gaim_buddy_icon_get_path(GaimBuddyIcon *icon) +{ + g_return_val_if_fail(icon != NULL, NULL); + + return icon->path; +} + +const char * +gaim_buddy_icon_get_type(const GaimBuddyIcon *icon) +{ + const void *data; + size_t len; + + g_return_val_if_fail(icon != NULL, NULL); + + data = gaim_buddy_icon_get_data(icon, &len); + + /* TODO: Find a way to do this with GDK */ + if (len >= 4) + { + if (!strncmp(data, "BM", 2)) + return "bmp"; + else if (!strncmp(data, "GIF8", 4)) + return "gif"; + else if (!strncmp(data, "\xff\xd8\xff\xe0", 4)) + return "jpg"; + else if (!strncmp(data, "\x89PNG", 4)) + return "png"; + } + + return NULL; +} + +void +gaim_buddy_icons_set_for_user(GaimAccount *account, const char *username, + void *icon_data, size_t icon_len) +{ + g_return_if_fail(account != NULL); + g_return_if_fail(username != NULL); + + if (icon_data == NULL || icon_len == 0) + { + GaimBuddyIcon *buddy_icon; + + buddy_icon = gaim_buddy_icons_find(account, username); + + if (buddy_icon != NULL) + gaim_buddy_icon_destroy(buddy_icon); + } + else + { + GaimBuddyIcon *icon = gaim_buddy_icon_new(account, username, icon_data, icon_len); + gaim_buddy_icon_unref(icon); + } +} + +GaimBuddyIcon * +gaim_buddy_icons_find(GaimAccount *account, const char *username) +{ + GHashTable *icon_cache; + GaimBuddyIcon *ret = NULL; + char *filename = NULL; + + g_return_val_if_fail(account != NULL, NULL); + g_return_val_if_fail(username != NULL, NULL); + + icon_cache = g_hash_table_lookup(account_cache, account); + + if ((icon_cache == NULL) || ((ret = g_hash_table_lookup(icon_cache, username)) == NULL)) { + const char *file; + struct stat st; + GaimBuddy *b = gaim_find_buddy(account, username); + + if (!b) + return NULL; + + if ((file = gaim_blist_node_get_string((GaimBlistNode*)b, "buddy_icon")) == NULL) + return NULL; + + if (!g_stat(file, &st)) + filename = g_strdup(file); + else + filename = g_build_filename(gaim_buddy_icons_get_cache_dir(), file, NULL); + + if (!g_stat(filename, &st)) { + FILE *f = g_fopen(filename, "rb"); + if (f) { + char *data = g_malloc(st.st_size); + fread(data, 1, st.st_size, f); + fclose(f); + ret = gaim_buddy_icon_create(account, username); + gaim_buddy_icon_ref(ret); + gaim_buddy_icon_set_data(ret, data, st.st_size); + gaim_buddy_icon_unref(ret); + g_free(data); + g_free(filename); + return ret; + } + } + g_free(filename); + } + + return ret; +} + +void +gaim_buddy_icons_set_caching(gboolean caching) +{ + icon_caching = caching; +} + +gboolean +gaim_buddy_icons_is_caching(void) +{ + return icon_caching; +} + +void +gaim_buddy_icons_set_cache_dir(const char *dir) +{ + g_return_if_fail(dir != NULL); + + g_free(cache_dir); + cache_dir = g_strdup(dir); +} + +const char * +gaim_buddy_icons_get_cache_dir(void) +{ + return cache_dir; +} + +char *gaim_buddy_icons_get_full_path(const char *icon) { + if (icon == NULL) + return NULL; + + if (g_file_test(icon, G_FILE_TEST_IS_REGULAR)) + return g_strdup(icon); + else + return g_build_filename(gaim_buddy_icons_get_cache_dir(), icon, NULL); +} + +void * +gaim_buddy_icons_get_handle() +{ + static int handle; + + return &handle; +} + +void +gaim_buddy_icons_init() +{ + account_cache = g_hash_table_new_full( + g_direct_hash, g_direct_equal, + NULL, (GFreeFunc)g_hash_table_destroy); + + cache_dir = g_build_filename(gaim_user_dir(), "icons", NULL); +} + +void +gaim_buddy_icons_uninit() +{ + g_hash_table_destroy(account_cache); +} + +void gaim_buddy_icon_get_scale_size(GaimBuddyIconSpec *spec, int *width, int *height) +{ + int new_width, new_height; + + new_width = *width; + new_height = *height; + + if (*width < spec->min_width) + new_width = spec->min_width; + else if (*width > spec->max_width) + new_width = spec->max_width; + + if (*height < spec->min_height) + new_height = spec->min_height; + else if (*height > spec->max_height) + new_height = spec->max_height; + + /* preserve aspect ratio */ + if ((double)*height * (double)new_width > + (double)*width * (double)new_height) { + new_width = 0.5 + (double)*width * (double)new_height / (double)*height; + } else { + new_height = 0.5 + (double)*height * (double)new_width / (double)*width; + } + + *width = new_width; + *height = new_height; +} +