Mercurial > pidgin
comparison 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 |
comparison
equal
deleted
inserted
replaced
15372:f79e0f4df793 | 15373:5fe8042783c1 |
---|---|
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 gaim_buddy_icon_set_path(icon, NULL); | |
81 | |
82 /* gaim_buddy_icon_set_data() makes blist.c or | |
83 * conversation.c, or both, take a reference. | |
84 * | |
85 * Plus, we leave one for the caller of this function. | |
86 */ | |
87 | |
88 return icon; | |
89 } | |
90 | |
91 void | |
92 gaim_buddy_icon_destroy(GaimBuddyIcon *icon) | |
93 { | |
94 GaimConversation *conv; | |
95 GaimAccount *account; | |
96 GHashTable *icon_cache; | |
97 const char *username; | |
98 GSList *sl, *list; | |
99 | |
100 g_return_if_fail(icon != NULL); | |
101 | |
102 if (icon->ref_count > 0) | |
103 { | |
104 /* If the ref count is greater than 0, then we weren't called from | |
105 * gaim_buddy_icon_unref(). So we go through and ask everyone to | |
106 * unref us. Then we return, since we know somewhere along the | |
107 * line we got called recursively by one of the unrefs, and the | |
108 * icon is already destroyed. | |
109 */ | |
110 account = gaim_buddy_icon_get_account(icon); | |
111 username = gaim_buddy_icon_get_username(icon); | |
112 | |
113 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, username, account); | |
114 if (conv != NULL) | |
115 gaim_conv_im_set_icon(GAIM_CONV_IM(conv), NULL); | |
116 | |
117 for (list = sl = gaim_find_buddies(account, username); sl != NULL; | |
118 sl = sl->next) | |
119 { | |
120 GaimBuddy *buddy = (GaimBuddy *)sl->data; | |
121 | |
122 gaim_buddy_set_icon(buddy, NULL); | |
123 } | |
124 | |
125 g_slist_free(list); | |
126 | |
127 return; | |
128 } | |
129 | |
130 icon_cache = g_hash_table_lookup(account_cache, | |
131 gaim_buddy_icon_get_account(icon)); | |
132 | |
133 if (icon_cache != NULL) | |
134 g_hash_table_remove(icon_cache, gaim_buddy_icon_get_username(icon)); | |
135 | |
136 g_free(icon->username); | |
137 g_free(icon->data); | |
138 g_free(icon->path); | |
139 GAIM_DBUS_UNREGISTER_POINTER(icon); | |
140 g_free(icon); | |
141 } | |
142 | |
143 GaimBuddyIcon * | |
144 gaim_buddy_icon_ref(GaimBuddyIcon *icon) | |
145 { | |
146 g_return_val_if_fail(icon != NULL, NULL); | |
147 | |
148 icon->ref_count++; | |
149 | |
150 return icon; | |
151 } | |
152 | |
153 GaimBuddyIcon * | |
154 gaim_buddy_icon_unref(GaimBuddyIcon *icon) | |
155 { | |
156 g_return_val_if_fail(icon != NULL, NULL); | |
157 g_return_val_if_fail(icon->ref_count > 0, NULL); | |
158 | |
159 icon->ref_count--; | |
160 | |
161 if (icon->ref_count == 0) | |
162 { | |
163 gaim_buddy_icon_destroy(icon); | |
164 | |
165 return NULL; | |
166 } | |
167 | |
168 return icon; | |
169 } | |
170 | |
171 void | |
172 gaim_buddy_icon_update(GaimBuddyIcon *icon) | |
173 { | |
174 GaimConversation *conv; | |
175 GaimAccount *account; | |
176 const char *username; | |
177 GSList *sl, *list; | |
178 | |
179 g_return_if_fail(icon != NULL); | |
180 | |
181 account = gaim_buddy_icon_get_account(icon); | |
182 username = gaim_buddy_icon_get_username(icon); | |
183 | |
184 for (list = sl = gaim_find_buddies(account, username); sl != NULL; | |
185 sl = sl->next) | |
186 { | |
187 GaimBuddy *buddy = (GaimBuddy *)sl->data; | |
188 | |
189 gaim_buddy_set_icon(buddy, icon); | |
190 } | |
191 | |
192 g_slist_free(list); | |
193 | |
194 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, username, account); | |
195 | |
196 if (conv != NULL) | |
197 gaim_conv_im_set_icon(GAIM_CONV_IM(conv), icon); | |
198 } | |
199 | |
200 static void | |
201 delete_icon_cache_file(const char *dirname, const char *old_icon) | |
202 { | |
203 struct stat st; | |
204 | |
205 g_return_if_fail(dirname != NULL); | |
206 g_return_if_fail(old_icon != NULL); | |
207 | |
208 if (g_stat(old_icon, &st) == 0) | |
209 g_unlink(old_icon); | |
210 else | |
211 { | |
212 char *filename = g_build_filename(dirname, old_icon, NULL); | |
213 if (g_stat(filename, &st) == 0) | |
214 g_unlink(filename); | |
215 g_free(filename); | |
216 } | |
217 gaim_debug_info("buddyicon", "Uncached file %s\n", old_icon); | |
218 } | |
219 | |
220 void | |
221 gaim_buddy_icon_cache(GaimBuddyIcon *icon, GaimBuddy *buddy) | |
222 { | |
223 const guchar *data; | |
224 const char *dirname; | |
225 char *random; | |
226 char *filename; | |
227 const char *old_icon; | |
228 size_t len = 0; | |
229 FILE *file = NULL; | |
230 | |
231 g_return_if_fail(icon != NULL); | |
232 g_return_if_fail(buddy != NULL); | |
233 | |
234 if (!gaim_buddy_icons_is_caching()) | |
235 return; | |
236 | |
237 data = gaim_buddy_icon_get_data(icon, &len); | |
238 | |
239 random = g_strdup_printf("%x", g_random_int()); | |
240 dirname = gaim_buddy_icons_get_cache_dir(); | |
241 filename = g_build_filename(dirname, random, NULL); | |
242 old_icon = gaim_blist_node_get_string((GaimBlistNode*)buddy, "buddy_icon"); | |
243 | |
244 if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) | |
245 { | |
246 gaim_debug_info("buddyicon", "Creating icon cache directory.\n"); | |
247 | |
248 if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) | |
249 { | |
250 gaim_debug_error("buddyicon", | |
251 "Unable to create directory %s: %s\n", | |
252 dirname, strerror(errno)); | |
253 } | |
254 } | |
255 | |
256 if ((file = g_fopen(filename, "wb")) != NULL) | |
257 { | |
258 fwrite(data, 1, len, file); | |
259 fclose(file); | |
260 gaim_debug_info("buddyicon", "Wrote file %s\n", filename); | |
261 } | |
262 else | |
263 { | |
264 gaim_debug_error("buddyicon", "Unable to create file %s: %s\n", | |
265 filename, strerror(errno)); | |
266 g_free(filename); | |
267 g_free(random); | |
268 return; | |
269 } | |
270 | |
271 g_free(filename); | |
272 | |
273 if (old_icon != NULL) | |
274 delete_icon_cache_file(dirname, old_icon); | |
275 | |
276 gaim_blist_node_set_string((GaimBlistNode *)buddy, "buddy_icon", random); | |
277 | |
278 g_free(random); | |
279 } | |
280 | |
281 void | |
282 gaim_buddy_icon_uncache(GaimBuddy *buddy) | |
283 { | |
284 const char *old_icon; | |
285 | |
286 g_return_if_fail(buddy != NULL); | |
287 | |
288 old_icon = gaim_blist_node_get_string((GaimBlistNode *)buddy, "buddy_icon"); | |
289 | |
290 if (old_icon != NULL) | |
291 delete_icon_cache_file(gaim_buddy_icons_get_cache_dir(), old_icon); | |
292 | |
293 gaim_blist_node_remove_setting((GaimBlistNode *)buddy, "buddy_icon"); | |
294 | |
295 /* Unset the icon in case this function is called from | |
296 * something other than gaim_buddy_set_icon(). */ | |
297 if (buddy->icon != NULL) | |
298 { | |
299 gaim_buddy_icon_unref(buddy->icon); | |
300 buddy->icon = NULL; | |
301 } | |
302 } | |
303 | |
304 void | |
305 gaim_buddy_icon_set_account(GaimBuddyIcon *icon, GaimAccount *account) | |
306 { | |
307 g_return_if_fail(icon != NULL); | |
308 g_return_if_fail(account != NULL); | |
309 | |
310 icon->account = account; | |
311 } | |
312 | |
313 void | |
314 gaim_buddy_icon_set_username(GaimBuddyIcon *icon, const char *username) | |
315 { | |
316 g_return_if_fail(icon != NULL); | |
317 g_return_if_fail(username != NULL); | |
318 | |
319 g_free(icon->username); | |
320 icon->username = g_strdup(username); | |
321 } | |
322 | |
323 void | |
324 gaim_buddy_icon_set_data(GaimBuddyIcon *icon, void *data, size_t len) | |
325 { | |
326 g_return_if_fail(icon != NULL); | |
327 | |
328 g_free(icon->data); | |
329 | |
330 if (data != NULL && len > 0) | |
331 { | |
332 icon->data = g_memdup(data, len); | |
333 icon->len = len; | |
334 } | |
335 else | |
336 { | |
337 icon->data = NULL; | |
338 icon->len = 0; | |
339 } | |
340 | |
341 gaim_buddy_icon_update(icon); | |
342 } | |
343 | |
344 void | |
345 gaim_buddy_icon_set_path(GaimBuddyIcon *icon, const gchar *path) | |
346 { | |
347 g_return_if_fail(icon != NULL); | |
348 | |
349 g_free(icon->path); | |
350 icon->path = g_strdup(path); | |
351 } | |
352 | |
353 GaimAccount * | |
354 gaim_buddy_icon_get_account(const GaimBuddyIcon *icon) | |
355 { | |
356 g_return_val_if_fail(icon != NULL, NULL); | |
357 | |
358 return icon->account; | |
359 } | |
360 | |
361 const char * | |
362 gaim_buddy_icon_get_username(const GaimBuddyIcon *icon) | |
363 { | |
364 g_return_val_if_fail(icon != NULL, NULL); | |
365 | |
366 return icon->username; | |
367 } | |
368 | |
369 const guchar * | |
370 gaim_buddy_icon_get_data(const GaimBuddyIcon *icon, size_t *len) | |
371 { | |
372 g_return_val_if_fail(icon != NULL, NULL); | |
373 | |
374 if (len != NULL) | |
375 *len = icon->len; | |
376 | |
377 return icon->data; | |
378 } | |
379 | |
380 const char * | |
381 gaim_buddy_icon_get_path(GaimBuddyIcon *icon) | |
382 { | |
383 g_return_val_if_fail(icon != NULL, NULL); | |
384 | |
385 return icon->path; | |
386 } | |
387 | |
388 const char * | |
389 gaim_buddy_icon_get_type(const GaimBuddyIcon *icon) | |
390 { | |
391 const void *data; | |
392 size_t len; | |
393 | |
394 g_return_val_if_fail(icon != NULL, NULL); | |
395 | |
396 data = gaim_buddy_icon_get_data(icon, &len); | |
397 | |
398 /* TODO: Find a way to do this with GDK */ | |
399 if (len >= 4) | |
400 { | |
401 if (!strncmp(data, "BM", 2)) | |
402 return "bmp"; | |
403 else if (!strncmp(data, "GIF8", 4)) | |
404 return "gif"; | |
405 else if (!strncmp(data, "\xff\xd8\xff\xe0", 4)) | |
406 return "jpg"; | |
407 else if (!strncmp(data, "\x89PNG", 4)) | |
408 return "png"; | |
409 } | |
410 | |
411 return NULL; | |
412 } | |
413 | |
414 void | |
415 gaim_buddy_icons_set_for_user(GaimAccount *account, const char *username, | |
416 void *icon_data, size_t icon_len) | |
417 { | |
418 g_return_if_fail(account != NULL); | |
419 g_return_if_fail(username != NULL); | |
420 | |
421 if (icon_data == NULL || icon_len == 0) | |
422 { | |
423 GaimBuddyIcon *buddy_icon; | |
424 | |
425 buddy_icon = gaim_buddy_icons_find(account, username); | |
426 | |
427 if (buddy_icon != NULL) | |
428 gaim_buddy_icon_destroy(buddy_icon); | |
429 } | |
430 else | |
431 { | |
432 GaimBuddyIcon *icon = gaim_buddy_icon_new(account, username, icon_data, icon_len); | |
433 gaim_buddy_icon_unref(icon); | |
434 } | |
435 } | |
436 | |
437 GaimBuddyIcon * | |
438 gaim_buddy_icons_find(GaimAccount *account, const char *username) | |
439 { | |
440 GHashTable *icon_cache; | |
441 GaimBuddyIcon *ret = NULL; | |
442 char *filename = NULL; | |
443 | |
444 g_return_val_if_fail(account != NULL, NULL); | |
445 g_return_val_if_fail(username != NULL, NULL); | |
446 | |
447 icon_cache = g_hash_table_lookup(account_cache, account); | |
448 | |
449 if ((icon_cache == NULL) || ((ret = g_hash_table_lookup(icon_cache, username)) == NULL)) { | |
450 const char *file; | |
451 struct stat st; | |
452 GaimBuddy *b = gaim_find_buddy(account, username); | |
453 | |
454 if (!b) | |
455 return NULL; | |
456 | |
457 if ((file = gaim_blist_node_get_string((GaimBlistNode*)b, "buddy_icon")) == NULL) | |
458 return NULL; | |
459 | |
460 if (!g_stat(file, &st)) | |
461 filename = g_strdup(file); | |
462 else | |
463 filename = g_build_filename(gaim_buddy_icons_get_cache_dir(), file, NULL); | |
464 | |
465 if (!g_stat(filename, &st)) { | |
466 FILE *f = g_fopen(filename, "rb"); | |
467 if (f) { | |
468 char *data = g_malloc(st.st_size); | |
469 fread(data, 1, st.st_size, f); | |
470 fclose(f); | |
471 ret = gaim_buddy_icon_create(account, username); | |
472 gaim_buddy_icon_ref(ret); | |
473 gaim_buddy_icon_set_data(ret, data, st.st_size); | |
474 gaim_buddy_icon_unref(ret); | |
475 g_free(data); | |
476 g_free(filename); | |
477 return ret; | |
478 } | |
479 } | |
480 g_free(filename); | |
481 } | |
482 | |
483 return ret; | |
484 } | |
485 | |
486 void | |
487 gaim_buddy_icons_set_caching(gboolean caching) | |
488 { | |
489 icon_caching = caching; | |
490 } | |
491 | |
492 gboolean | |
493 gaim_buddy_icons_is_caching(void) | |
494 { | |
495 return icon_caching; | |
496 } | |
497 | |
498 void | |
499 gaim_buddy_icons_set_cache_dir(const char *dir) | |
500 { | |
501 g_return_if_fail(dir != NULL); | |
502 | |
503 g_free(cache_dir); | |
504 cache_dir = g_strdup(dir); | |
505 } | |
506 | |
507 const char * | |
508 gaim_buddy_icons_get_cache_dir(void) | |
509 { | |
510 return cache_dir; | |
511 } | |
512 | |
513 char *gaim_buddy_icons_get_full_path(const char *icon) { | |
514 if (icon == NULL) | |
515 return NULL; | |
516 | |
517 if (g_file_test(icon, G_FILE_TEST_IS_REGULAR)) | |
518 return g_strdup(icon); | |
519 else | |
520 return g_build_filename(gaim_buddy_icons_get_cache_dir(), icon, NULL); | |
521 } | |
522 | |
523 void * | |
524 gaim_buddy_icons_get_handle() | |
525 { | |
526 static int handle; | |
527 | |
528 return &handle; | |
529 } | |
530 | |
531 void | |
532 gaim_buddy_icons_init() | |
533 { | |
534 account_cache = g_hash_table_new_full( | |
535 g_direct_hash, g_direct_equal, | |
536 NULL, (GFreeFunc)g_hash_table_destroy); | |
537 | |
538 cache_dir = g_build_filename(gaim_user_dir(), "icons", NULL); | |
539 } | |
540 | |
541 void | |
542 gaim_buddy_icons_uninit() | |
543 { | |
544 g_hash_table_destroy(account_cache); | |
545 } | |
546 | |
547 void gaim_buddy_icon_get_scale_size(GaimBuddyIconSpec *spec, int *width, int *height) | |
548 { | |
549 int new_width, new_height; | |
550 | |
551 new_width = *width; | |
552 new_height = *height; | |
553 | |
554 if (*width < spec->min_width) | |
555 new_width = spec->min_width; | |
556 else if (*width > spec->max_width) | |
557 new_width = spec->max_width; | |
558 | |
559 if (*height < spec->min_height) | |
560 new_height = spec->min_height; | |
561 else if (*height > spec->max_height) | |
562 new_height = spec->max_height; | |
563 | |
564 /* preserve aspect ratio */ | |
565 if ((double)*height * (double)new_width > | |
566 (double)*width * (double)new_height) { | |
567 new_width = 0.5 + (double)*width * (double)new_height / (double)*height; | |
568 } else { | |
569 new_height = 0.5 + (double)*height * (double)new_width / (double)*width; | |
570 } | |
571 | |
572 *width = new_width; | |
573 *height = new_height; | |
574 } | |
575 |