Mercurial > pidgin.yaz
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 |