src/buddyicon.c

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

mercurial