comparison libgaim/buddyicon.c @ 14192:60b1bc8dbf37

[gaim-migrate @ 16863] Renamed 'core' to 'libgaim' committer: Tailor Script <tailor@pidgin.im>
author Evan Schoenberg <evan.s@dreskin.net>
date Sat, 19 Aug 2006 01:50:10 +0000
parents
children 9b4268984921
comparison
equal deleted inserted replaced
14191:009db0b357b5 14192:60b1bc8dbf37
1 /**
2 * @file icon.c Buddy Icon API
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25 #include "internal.h"
26 #include "buddyicon.h"
27 #include "conversation.h"
28 #include "dbus-maybe.h"
29 #include "debug.h"
30 #include "util.h"
31
32 static GHashTable *account_cache = NULL;
33 static char *cache_dir = NULL;
34 static gboolean icon_caching = TRUE;
35
36 static GaimBuddyIcon *
37 gaim_buddy_icon_create(GaimAccount *account, const char *username)
38 {
39 GaimBuddyIcon *icon;
40 GHashTable *icon_cache;
41
42 icon = g_new0(GaimBuddyIcon, 1);
43 GAIM_DBUS_REGISTER_POINTER(icon, GaimBuddyIcon);
44
45 gaim_buddy_icon_set_account(icon, account);
46 gaim_buddy_icon_set_username(icon, username);
47
48 icon_cache = g_hash_table_lookup(account_cache, account);
49
50 if (icon_cache == NULL)
51 {
52 icon_cache = g_hash_table_new(g_str_hash, g_str_equal);
53
54 g_hash_table_insert(account_cache, account, icon_cache);
55 }
56
57 g_hash_table_insert(icon_cache,
58 (char *)gaim_buddy_icon_get_username(icon), icon);
59 return icon;
60 }
61
62 GaimBuddyIcon *
63 gaim_buddy_icon_new(GaimAccount *account, const char *username,
64 void *icon_data, size_t icon_len)
65 {
66 GaimBuddyIcon *icon;
67
68 g_return_val_if_fail(account != NULL, NULL);
69 g_return_val_if_fail(username != NULL, NULL);
70 g_return_val_if_fail(icon_data != NULL, NULL);
71 g_return_val_if_fail(icon_len > 0, NULL);
72
73 icon = gaim_buddy_icons_find(account, username);
74
75 if (icon == NULL)
76 icon = gaim_buddy_icon_create(account, username);
77
78 gaim_buddy_icon_ref(icon);
79 gaim_buddy_icon_set_data(icon, icon_data, icon_len);
80
81 /* gaim_buddy_icon_set_data() makes blist.c or
82 * conversation.c, or both, take a reference.
83 *
84 * Plus, we leave one for the caller of this function.
85 */
86
87 return icon;
88 }
89
90 void
91 gaim_buddy_icon_destroy(GaimBuddyIcon *icon)
92 {
93 GaimConversation *conv;
94 GaimAccount *account;
95 GHashTable *icon_cache;
96 const char *username;
97 GSList *sl, *list;
98
99 g_return_if_fail(icon != NULL);
100
101 if (icon->ref_count > 0)
102 {
103 /* If the ref count is greater than 0, then we weren't called from
104 * gaim_buddy_icon_unref(). So we go through and ask everyone to
105 * unref us. Then we return, since we know somewhere along the
106 * line we got called recursively by one of the unrefs, and the
107 * icon is already destroyed.
108 */
109 account = gaim_buddy_icon_get_account(icon);
110 username = gaim_buddy_icon_get_username(icon);
111
112 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, username, account);
113 if (conv != NULL)
114 gaim_conv_im_set_icon(GAIM_CONV_IM(conv), NULL);
115
116 for (list = sl = gaim_find_buddies(account, username); sl != NULL;
117 sl = sl->next)
118 {
119 GaimBuddy *buddy = (GaimBuddy *)sl->data;
120
121 gaim_buddy_set_icon(buddy, NULL);
122 }
123
124 g_slist_free(list);
125
126 return;
127 }
128
129 icon_cache = g_hash_table_lookup(account_cache,
130 gaim_buddy_icon_get_account(icon));
131
132 if (icon_cache != NULL)
133 g_hash_table_remove(icon_cache, gaim_buddy_icon_get_username(icon));
134
135 g_free(icon->username);
136 g_free(icon->data);
137 GAIM_DBUS_UNREGISTER_POINTER(icon);
138 g_free(icon);
139 }
140
141 GaimBuddyIcon *
142 gaim_buddy_icon_ref(GaimBuddyIcon *icon)
143 {
144 g_return_val_if_fail(icon != NULL, NULL);
145
146 icon->ref_count++;
147
148 return icon;
149 }
150
151 GaimBuddyIcon *
152 gaim_buddy_icon_unref(GaimBuddyIcon *icon)
153 {
154 g_return_val_if_fail(icon != NULL, NULL);
155 g_return_val_if_fail(icon->ref_count > 0, NULL);
156
157 icon->ref_count--;
158
159 if (icon->ref_count == 0)
160 {
161 gaim_buddy_icon_destroy(icon);
162
163 return NULL;
164 }
165
166 return icon;
167 }
168
169 void
170 gaim_buddy_icon_update(GaimBuddyIcon *icon)
171 {
172 GaimConversation *conv;
173 GaimAccount *account;
174 const char *username;
175 GSList *sl, *list;
176
177 g_return_if_fail(icon != NULL);
178
179 account = gaim_buddy_icon_get_account(icon);
180 username = gaim_buddy_icon_get_username(icon);
181
182 for (list = sl = gaim_find_buddies(account, username); sl != NULL;
183 sl = sl->next)
184 {
185 GaimBuddy *buddy = (GaimBuddy *)sl->data;
186
187 gaim_buddy_set_icon(buddy, icon);
188 }
189
190 g_slist_free(list);
191
192 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, username, account);
193
194 if (conv != NULL)
195 gaim_conv_im_set_icon(GAIM_CONV_IM(conv), icon);
196 }
197
198 static void
199 delete_icon_cache_file(const char *dirname, const char *old_icon)
200 {
201 struct stat st;
202
203 g_return_if_fail(dirname != NULL);
204 g_return_if_fail(old_icon != NULL);
205
206 if (g_stat(old_icon, &st) == 0)
207 g_unlink(old_icon);
208 else
209 {
210 char *filename = g_build_filename(dirname, old_icon, NULL);
211 if (g_stat(filename, &st) == 0)
212 g_unlink(filename);
213 g_free(filename);
214 }
215 gaim_debug_info("buddyicon", "Uncached file %s\n", old_icon);
216 }
217
218 void
219 gaim_buddy_icon_cache(GaimBuddyIcon *icon, GaimBuddy *buddy)
220 {
221 const guchar *data;
222 const char *dirname;
223 char *random;
224 char *filename;
225 const char *old_icon;
226 size_t len = 0;
227 FILE *file = NULL;
228
229 g_return_if_fail(icon != NULL);
230 g_return_if_fail(buddy != NULL);
231
232 if (!gaim_buddy_icons_is_caching())
233 return;
234
235 data = gaim_buddy_icon_get_data(icon, &len);
236
237 random = g_strdup_printf("%x", g_random_int());
238 dirname = gaim_buddy_icons_get_cache_dir();
239 filename = g_build_filename(dirname, random, NULL);
240 old_icon = gaim_blist_node_get_string((GaimBlistNode*)buddy, "buddy_icon");
241
242 if (!g_file_test(dirname, G_FILE_TEST_IS_DIR))
243 {
244 gaim_debug_info("buddyicon", "Creating icon cache directory.\n");
245
246 if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
247 {
248 gaim_debug_error("buddyicon",
249 "Unable to create directory %s: %s\n",
250 dirname, strerror(errno));
251 }
252 }
253
254 if ((file = g_fopen(filename, "wb")) != NULL)
255 {
256 fwrite(data, 1, len, file);
257 fclose(file);
258 gaim_debug_info("buddyicon", "Wrote file %s\n", filename);
259 }
260 else
261 {
262 gaim_debug_error("buddyicon", "Unable to create file %s: %s\n",
263 filename, strerror(errno));
264 }
265
266 g_free(filename);
267
268 if (old_icon != NULL)
269 delete_icon_cache_file(dirname, old_icon);
270
271 gaim_blist_node_set_string((GaimBlistNode *)buddy, "buddy_icon", random);
272
273 g_free(random);
274 }
275
276 void
277 gaim_buddy_icon_uncache(GaimBuddy *buddy)
278 {
279 const char *old_icon;
280
281 g_return_if_fail(buddy != NULL);
282
283 old_icon = gaim_blist_node_get_string((GaimBlistNode *)buddy, "buddy_icon");
284
285 if (old_icon != NULL)
286 delete_icon_cache_file(gaim_buddy_icons_get_cache_dir(), old_icon);
287
288 gaim_blist_node_remove_setting((GaimBlistNode *)buddy, "buddy_icon");
289
290 /* Unset the icon in case this function is called from
291 * something other than gaim_buddy_set_icon(). */
292 if (buddy->icon != NULL)
293 {
294 gaim_buddy_icon_unref(buddy->icon);
295 buddy->icon = NULL;
296 }
297 }
298
299 void
300 gaim_buddy_icon_set_account(GaimBuddyIcon *icon, GaimAccount *account)
301 {
302 g_return_if_fail(icon != NULL);
303 g_return_if_fail(account != NULL);
304
305 icon->account = account;
306 }
307
308 void
309 gaim_buddy_icon_set_username(GaimBuddyIcon *icon, const char *username)
310 {
311 g_return_if_fail(icon != NULL);
312 g_return_if_fail(username != NULL);
313
314 g_free(icon->username);
315 icon->username = g_strdup(username);
316 }
317
318 void
319 gaim_buddy_icon_set_data(GaimBuddyIcon *icon, void *data, size_t len)
320 {
321 g_return_if_fail(icon != NULL);
322
323 g_free(icon->data);
324
325 if (data != NULL && len > 0)
326 {
327 icon->data = g_memdup(data, len);
328 icon->len = len;
329 }
330 else
331 {
332 icon->data = NULL;
333 icon->len = 0;
334 }
335
336 gaim_buddy_icon_update(icon);
337 }
338
339 GaimAccount *
340 gaim_buddy_icon_get_account(const GaimBuddyIcon *icon)
341 {
342 g_return_val_if_fail(icon != NULL, NULL);
343
344 return icon->account;
345 }
346
347 const char *
348 gaim_buddy_icon_get_username(const GaimBuddyIcon *icon)
349 {
350 g_return_val_if_fail(icon != NULL, NULL);
351
352 return icon->username;
353 }
354
355 const guchar *
356 gaim_buddy_icon_get_data(const GaimBuddyIcon *icon, size_t *len)
357 {
358 g_return_val_if_fail(icon != NULL, NULL);
359
360 if (len != NULL)
361 *len = icon->len;
362
363 return icon->data;
364 }
365
366 const char *
367 gaim_buddy_icon_get_type(const GaimBuddyIcon *icon)
368 {
369 const void *data;
370 size_t len;
371
372 g_return_val_if_fail(icon != NULL, NULL);
373
374 data = gaim_buddy_icon_get_data(icon, &len);
375
376 /* TODO: Find a way to do this with GDK */
377 if (len >= 4)
378 {
379 if (!strncmp(data, "BM", 2))
380 return "bmp";
381 else if (!strncmp(data, "GIF8", 4))
382 return "gif";
383 else if (!strncmp(data, "\xff\xd8\xff\xe0", 4))
384 return "jpg";
385 else if (!strncmp(data, "\x89PNG", 4))
386 return "png";
387 }
388
389 return NULL;
390 }
391
392 void
393 gaim_buddy_icons_set_for_user(GaimAccount *account, const char *username,
394 void *icon_data, size_t icon_len)
395 {
396 g_return_if_fail(account != NULL);
397 g_return_if_fail(username != NULL);
398
399 if (icon_data == NULL || icon_len == 0)
400 {
401 GaimBuddyIcon *buddy_icon;
402
403 buddy_icon = gaim_buddy_icons_find(account, username);
404
405 if (buddy_icon != NULL)
406 gaim_buddy_icon_destroy(buddy_icon);
407 }
408 else
409 {
410 GaimBuddyIcon *icon = gaim_buddy_icon_new(account, username, icon_data, icon_len);
411 gaim_buddy_icon_unref(icon);
412 }
413 }
414
415 GaimBuddyIcon *
416 gaim_buddy_icons_find(GaimAccount *account, const char *username)
417 {
418 GHashTable *icon_cache;
419 GaimBuddyIcon *ret = NULL;
420 char *filename = NULL;
421
422 g_return_val_if_fail(account != NULL, NULL);
423 g_return_val_if_fail(username != NULL, NULL);
424
425 icon_cache = g_hash_table_lookup(account_cache, account);
426
427 if ((icon_cache == NULL) || ((ret = g_hash_table_lookup(icon_cache, username)) == NULL)) {
428 const char *file;
429 struct stat st;
430 GaimBuddy *b = gaim_find_buddy(account, username);
431
432 if (!b)
433 return NULL;
434
435 if ((file = gaim_blist_node_get_string((GaimBlistNode*)b, "buddy_icon")) == NULL)
436 return NULL;
437
438 if (!g_stat(file, &st))
439 filename = g_strdup(file);
440 else
441 filename = g_build_filename(gaim_buddy_icons_get_cache_dir(), file, NULL);
442
443 if (!g_stat(filename, &st)) {
444 FILE *f = g_fopen(filename, "rb");
445 if (f) {
446 char *data = g_malloc(st.st_size);
447 fread(data, 1, st.st_size, f);
448 fclose(f);
449 ret = gaim_buddy_icon_create(account, username);
450 gaim_buddy_icon_ref(ret);
451 gaim_buddy_icon_set_data(ret, data, st.st_size);
452 gaim_buddy_icon_unref(ret);
453 g_free(data);
454 g_free(filename);
455 return ret;
456 }
457 }
458 g_free(filename);
459 }
460
461 return ret;
462 }
463
464 void
465 gaim_buddy_icons_set_caching(gboolean caching)
466 {
467 icon_caching = caching;
468 }
469
470 gboolean
471 gaim_buddy_icons_is_caching(void)
472 {
473 return icon_caching;
474 }
475
476 void
477 gaim_buddy_icons_set_cache_dir(const char *dir)
478 {
479 g_return_if_fail(dir != NULL);
480
481 g_free(cache_dir);
482 cache_dir = g_strdup(dir);
483 }
484
485 const char *
486 gaim_buddy_icons_get_cache_dir(void)
487 {
488 return cache_dir;
489 }
490
491 char *gaim_buddy_icons_get_full_path(const char *icon) {
492 struct stat st;
493
494 if (icon == NULL)
495 return NULL;
496
497 if (g_stat(icon, &st) == 0)
498 return g_strdup(icon);
499 else
500 return g_build_filename(gaim_buddy_icons_get_cache_dir(), icon, NULL);
501 }
502
503 void *
504 gaim_buddy_icons_get_handle()
505 {
506 static int handle;
507
508 return &handle;
509 }
510
511 void
512 gaim_buddy_icons_init()
513 {
514 account_cache = g_hash_table_new_full(
515 g_direct_hash, g_direct_equal,
516 NULL, (GFreeFunc)g_hash_table_destroy);
517
518 cache_dir = g_build_filename(gaim_user_dir(), "icons", NULL);
519 }
520
521 void
522 gaim_buddy_icons_uninit()
523 {
524 g_hash_table_destroy(account_cache);
525 }
526
527 void gaim_buddy_icon_get_scale_size(GaimBuddyIconSpec *spec, int *width, int *height)
528 {
529 if(spec && spec->scale_rules & GAIM_ICON_SCALE_DISPLAY) {
530 int new_width, new_height;
531
532 new_width = *width;
533 new_height = *height;
534
535 if(*width < spec->min_width)
536 new_width = spec->min_width;
537 else if(*width > spec->max_width)
538 new_width = spec->max_width;
539
540 if(*height < spec->min_height)
541 new_height = spec->min_height;
542 else if(*height > spec->max_height)
543 new_height = spec->max_height;
544
545 /* preserve aspect ratio */
546 if ((double)*height * (double)new_width >
547 (double)*width * (double)new_height) {
548 new_width = 0.5 + (double)*width * (double)new_height / (double)*height;
549 } else {
550 new_height = 0.5 + (double)*height * (double)new_width / (double)*width;
551 }
552
553 *width = new_width;
554 *height = new_height;
555 }
556 }
557