comparison icon.c @ 254:c2620a99622b

- divided the source file into several parts.
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Sat, 22 Nov 2008 18:01:18 +0900
parents
children 2145f975ef69
comparison
equal deleted inserted replaced
253:a37ae6c8fa66 254:c2620a99622b
1 #include "pidgin-twitter.h"
2 #include "icon.h"
3
4 extern GHashTable *icon_hash[];
5 extern GRegex *regp[];
6
7 /* prototypes */
8 static void insert_icon_at_mark(GtkTextMark *requested_mark, gpointer user_data);
9 static void insert_requested_icon(const gchar *user_name, gint service);
10 static void got_icon_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message);
11 static GdkPixbuf *make_scaled_pixbuf(const gchar *url_text, gsize len);
12 static void got_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message);
13
14 /* functions */
15 static void
16 insert_icon_at_mark(GtkTextMark *requested_mark, gpointer user_data)
17 {
18 got_icon_data *gotdata = (got_icon_data *)user_data;
19
20 gchar *user_name = gotdata->user_name;
21 gint service = gotdata->service;
22
23 GList *win_list;
24 GtkIMHtml *target_imhtml = NULL;
25 GtkTextBuffer *target_buffer = NULL;
26 GtkTextIter insertion_point;
27 icon_data *data = NULL;
28 GHashTable *hash = NULL;
29
30 twitter_debug("called: service = %d\n", service);
31
32 /* find the conversation that contains the mark */
33 for(win_list = pidgin_conv_windows_get_list(); win_list;
34 win_list = win_list->next) {
35 PidginWindow *win = win_list->data;
36 GList *conv_list;
37
38 for(conv_list = pidgin_conv_window_get_gtkconvs(win); conv_list;
39 conv_list = conv_list->next) {
40 PidginConversation *conv = conv_list->data;
41 PurpleConversation *purple_conv = conv->active_conv;
42
43 gint service = get_service_type(purple_conv);
44
45 if(service != unknown_service) {
46 GtkIMHtml *current_imhtml = GTK_IMHTML(conv->imhtml);
47 GtkTextBuffer *current_buffer = gtk_text_view_get_buffer(
48 GTK_TEXT_VIEW(current_imhtml));
49
50 if(current_buffer == gtk_text_mark_get_buffer(requested_mark)) {
51 target_imhtml = current_imhtml;
52 target_buffer = current_buffer;
53 break;
54 }
55 }
56 }
57 }
58
59 if(!(target_imhtml && target_buffer)) {
60 return;
61 }
62
63 /* insert icon to the mark */
64 gtk_text_buffer_get_iter_at_mark(target_buffer,
65 &insertion_point, requested_mark);
66
67 /* insert icon */
68 switch(service) {
69 case twitter_service:
70 hash = icon_hash[twitter_service];
71 break;
72 case wassr_service:
73 hash = icon_hash[wassr_service];
74 break;
75 case identica_service:
76 hash = icon_hash[identica_service];
77 break;
78 case jisko_service:
79 hash = icon_hash[jisko_service];
80 break;
81 default:
82 twitter_debug("unknown service\n");
83 }
84
85 if(hash)
86 data = (icon_data *)g_hash_table_lookup(hash, user_name);
87
88
89 /* in this function, we put an icon for pending marks. we should
90 * not invalidate the icon here, otherwise it may result in
91 * thrashing. --yaz */
92
93 if(!data || !data->pixbuf) {
94 return;
95 }
96
97 /* insert icon actually */
98 if(purple_prefs_get_bool(OPT_SHOW_ICON)) {
99 gtk_text_buffer_insert_pixbuf(target_buffer,
100 &insertion_point,
101 data->pixbuf);
102 data->use_count++;
103 }
104 gtk_text_buffer_delete_mark(target_buffer, requested_mark);
105 requested_mark = NULL;
106 }
107
108 static void
109 insert_requested_icon(const gchar *user_name, gint service)
110 {
111 icon_data *data = NULL;
112 GList *mark_list = NULL;
113 GHashTable *hash = NULL;
114
115 twitter_debug("called\n");
116
117 switch(service) {
118 case twitter_service:
119 hash = icon_hash[twitter_service];
120 break;
121 case wassr_service:
122 hash = icon_hash[wassr_service];
123 break;
124 case identica_service:
125 hash = icon_hash[identica_service];
126 break;
127 case jisko_service:
128 hash = icon_hash[jisko_service];
129 break;
130 default:
131 twitter_debug("unknown service\n");
132 break;
133 }
134
135 if(hash)
136 data = (icon_data *)g_hash_table_lookup(hash, user_name);
137
138 if(!data)
139 return;
140
141 mark_list = data->request_list;
142
143 got_icon_data *gotdata = g_new0(got_icon_data, 1);
144 gotdata->user_name = g_strdup(user_name);
145 gotdata->service = service;
146
147 twitter_debug("about to insert icon for pending requests\n");
148
149 if(mark_list) {
150 g_list_foreach(mark_list, (GFunc) insert_icon_at_mark, gotdata);
151 mark_list = g_list_remove_all(mark_list, NULL);
152 g_list_free(mark_list);
153 data->request_list = NULL;
154 }
155
156 g_free(gotdata->user_name);
157 g_free(gotdata);
158 }
159
160 /* this function will be called when profile page has been retrieved */
161 static void
162 got_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
163 const gchar *url_text, gsize len, const gchar *error_message)
164 {
165 got_icon_data *gotdata = (got_icon_data *)user_data;
166 gchar *user_name = gotdata->user_name;
167 gint service = gotdata->service;
168 GMatchInfo *match_info = NULL;
169 icon_data *data = NULL;
170 gchar *url = NULL;
171 gint regp_id = -1;
172
173 if(service == twitter_service) {
174 data = (icon_data *)g_hash_table_lookup(
175 icon_hash[twitter_service], user_name);
176 regp_id = IMAGE_TWITTER;
177 }
178 else if(service == wassr_service) {
179 data = (icon_data *)g_hash_table_lookup(
180 icon_hash[wassr_service], user_name);
181 regp_id = IMAGE_WASSR;
182 }
183 else if(service == identica_service) {
184 data = (icon_data *)g_hash_table_lookup(
185 icon_hash[identica_service], user_name);
186 regp_id = IMAGE_IDENTICA;
187 }
188 else if(service == jisko_service) {
189 data = (icon_data *)g_hash_table_lookup(
190 icon_hash[jisko_service], user_name);
191 regp_id = IMAGE_JISKO;
192 }
193
194 if(!url_text) {
195 if(data) {
196 data->requested = FALSE;
197 data->fetch_data = NULL;
198 }
199 g_free(gotdata->user_name);
200 g_free(gotdata);
201 return;
202 }
203
204 /* setup image url */ /* xxx need simplify --yaz */
205 g_regex_match(regp[regp_id], url_text, 0, &match_info);
206 if(!g_match_info_matches(match_info)) {
207 g_match_info_free(match_info);
208
209 if(service == twitter_service) {
210 twitter_debug("fall back to twitter default icon\n");
211 url = g_strdup(TWITTER_DEFAULT_ICON_URL);
212 }
213 else if(service == jisko_service) {
214 twitter_debug("fall back to jisko default icon\n");
215 url = g_strdup(JISKO_DEFAULT_ICON_URL);
216 }
217 else {
218 twitter_debug("no image url found\n");
219 if(data) {
220 data->requested = FALSE;
221 data->fetch_data = NULL;
222 }
223 g_free(gotdata->user_name);
224 g_free(gotdata);
225 return;
226 }
227 }
228 else {
229 url = g_match_info_fetch(match_info, 1);
230 g_match_info_free(match_info);
231 }
232
233 /* find out basename */
234 gchar *slash = strrchr(url, '/');
235 *slash = '\0';
236
237 gchar *lower = g_ascii_strdown(slash+1, -1);
238
239 if(strstr(lower, ".png"))
240 data->img_type = "png";
241 else if(strstr(lower, ".gif"))
242 data->img_type = "gif";
243 else if(strstr(lower, ".jpg") || strstr(lower, ".jpeg"))
244 data->img_type = "jpg";
245
246 g_free(lower);
247
248 gchar *tmp;
249 /* url encode basename. twitter needs this. */
250 if(service == twitter_service)
251 tmp = g_strdup_printf("%s/%s", url,
252 purple_url_encode(slash+1));
253 else if(service == wassr_service) {
254 gchar *tmp0 = NULL;
255 tmp0 = g_regex_replace(regp[SIZE_128_WASSR], slash+1,
256 -1, 0, ".64.", 0, NULL);
257 tmp = g_strdup_printf("http://wassr.jp%s/%s", url,
258 tmp0 ? tmp0 : slash+1);
259 g_free(tmp0);
260 }
261 else {
262 tmp = g_strdup_printf("%s/%s", url, slash+1);
263 }
264
265 g_free(url);
266 url = tmp;
267
268 /* if requesting icon url is the same as old, return. */
269 if(url && data->icon_url && !strcmp(data->icon_url, url)) {
270 twitter_debug("old url = %s new url = %s\n", data->icon_url, url);
271 data->requested = FALSE;
272 data->fetch_data = NULL;
273 g_free(url);
274 return;
275 }
276
277 if(data && data->pixbuf) {
278 gdk_pixbuf_unref(data->pixbuf);
279 data->pixbuf = NULL;
280 }
281
282 g_free(data->icon_url);
283 data->icon_url = g_strdup(url);
284
285 data->use_count = 0;
286 data->mtime = time(NULL); /* xxx is there a better way? */
287
288 twitter_debug("requested url=%s\n", url);
289
290 /* request fetch image */
291 if(url) {
292 /* reuse gotdata. just pass given one */
293 /* gotdata will be released in got_icon_cb */
294 data->fetch_data = purple_util_fetch_url(url,
295 TRUE, NULL, TRUE,
296 got_icon_cb, gotdata);
297 twitter_debug("request %s's icon\n", user_name);
298 g_free(url);
299 }
300 }
301
302 static GdkPixbuf *
303 make_scaled_pixbuf(const gchar *url_text, gsize len)
304 {
305 /* make pixbuf */
306 GdkPixbufLoader *loader;
307 GdkPixbuf *src = NULL, *dest = NULL;
308 gint size;
309
310 g_return_val_if_fail(url_text != NULL, NULL);
311 g_return_val_if_fail(len > 0, NULL);
312
313 loader = gdk_pixbuf_loader_new();
314 gdk_pixbuf_loader_write(loader, (guchar *)url_text, len, NULL);
315 gdk_pixbuf_loader_close(loader, NULL);
316
317 src = gdk_pixbuf_loader_get_pixbuf(loader);
318 if(!src)
319 return NULL;
320
321 size = purple_prefs_get_int(OPT_ICON_SIZE);
322 if(size == 0)
323 size = DEFAULT_ICON_SIZE;
324
325 dest = gdk_pixbuf_scale_simple(src, size, size, GDK_INTERP_HYPER);
326 gdk_pixbuf_unref(src);
327
328 return dest;
329 }
330
331 static gchar *ext_list[] = {
332 "png",
333 "gif",
334 "jpg",
335 NULL
336 };
337
338 /* this function will be called when requested icon has been retrieved */
339 static void
340 got_icon_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
341 const gchar *url_text, gsize len, const gchar *error_message)
342 {
343 got_icon_data *gotdata = (got_icon_data *)user_data;
344 gchar *user_name = gotdata->user_name;
345 gint service = gotdata->service;
346
347 icon_data *data = NULL;
348 GHashTable *hash = NULL;
349 GdkPixbuf *pixbuf = NULL;
350 const gchar *dirname = NULL;
351
352 twitter_debug("called: service = %d\n", service);
353
354 switch(service) {
355 case twitter_service:
356 hash = icon_hash[twitter_service];
357 break;
358 case wassr_service:
359 hash = icon_hash[wassr_service];
360 break;
361 case identica_service:
362 hash = icon_hash[identica_service];
363 break;
364 case jisko_service:
365 hash = icon_hash[jisko_service];
366 break;
367 default:
368 twitter_debug("unknown service\n");
369 }
370
371 if(hash)
372 data = (icon_data *)g_hash_table_lookup(hash, user_name);
373
374 /* return if download failed */
375 if(!url_text) {
376 twitter_debug("downloading %s's icon failed : %s\n",
377 user_name, error_message);
378 if(data)
379 data->requested = FALSE;
380
381 goto fin_got_icon_cb;
382 }
383
384 if(data) {
385 /* remove download request */
386 data->requested = FALSE;
387 data->fetch_data = NULL;
388
389 /* return if user's icon had been downloaded */
390 if(data->pixbuf) {
391 twitter_debug("%s's icon has already been downloaded\n",
392 user_name);
393
394 goto fin_got_icon_cb;
395 }
396 }
397
398 pixbuf = make_scaled_pixbuf(url_text, len);
399
400 if(!pixbuf)
401 goto fin_got_icon_cb;
402
403
404 if(!data) {
405 twitter_debug("allocate icon_data (shouldn't be called)\n");
406 data = g_new0(icon_data, 1);
407 }
408
409 data->pixbuf = pixbuf;
410
411 twitter_debug("new icon pixbuf = %p size = %d\n",
412 pixbuf,
413 gdk_pixbuf_get_rowstride(pixbuf) *
414 gdk_pixbuf_get_height(pixbuf));
415
416 if(hash)
417 g_hash_table_insert(hash, g_strdup(user_name), data);
418
419 dirname = purple_prefs_get_string(OPT_ICON_DIR);
420
421 /* store retrieved image to a file in icon dir */
422 if(ensure_path_exists(dirname)) {
423 gchar *filename = NULL;
424 gchar *path = NULL;
425 const gchar *suffix = NULL;
426 gchar **extp;
427
428 switch(service) {
429 case twitter_service:
430 suffix = "twitter";
431 break;
432 case wassr_service:
433 suffix = "wassr";
434 break;
435 case identica_service:
436 suffix = "identica";
437 break;
438 case jisko_service:
439 suffix = "jisko";
440 break;
441 default:
442 twitter_debug("unknown service\n");
443 break;
444 }
445
446 /* remove old file first */
447 for(extp = ext_list; *extp; extp++) {
448 filename = g_strdup_printf("%s_%s.%s",
449 user_name, suffix, *extp);
450 path = g_build_filename(dirname, filename, NULL);
451 g_remove(path);
452
453 g_free(filename);
454 g_free(path);
455 }
456
457 /* setup path */
458 filename = g_strdup_printf("%s_%s.%s",
459 user_name, suffix, data->img_type);
460
461 path = g_build_filename(dirname, filename, NULL);
462 g_free(filename); filename = NULL;
463
464 g_file_set_contents(path, url_text, len, NULL);
465 g_free(path); path = NULL;
466
467 data->mtime = time(NULL);
468 }
469
470 twitter_debug("Downloading %s's icon has been complete.\n",
471 user_name);
472
473 /* Insert the icon to messages that had been received. */
474 insert_requested_icon(user_name, service);
475
476 fin_got_icon_cb:
477 g_free(gotdata->user_name);
478 g_free(gotdata);
479 }
480
481 void
482 request_icon(const char *user_name, gint service, gboolean renew)
483 {
484 gchar *url = NULL;
485
486 /* look local icon cache for the requested icon */
487 gchar *path = NULL;
488 icon_data *data = NULL;
489 GHashTable *hash = NULL;
490 const gchar *suffix = NULL;
491
492 switch(service) {
493 case twitter_service:
494 hash = icon_hash[twitter_service];
495 suffix = "twitter";
496 break;
497 case wassr_service:
498 hash = icon_hash[wassr_service];
499 suffix = "wassr";
500 break;
501 case identica_service:
502 suffix = "identica";
503 hash = icon_hash[identica_service];
504 break;
505 case jisko_service:
506 suffix = "jisko";
507 hash = icon_hash[jisko_service];
508 break;
509 default:
510 twitter_debug("unknown service\n");
511 break;
512 }
513
514 if(!hash)
515 return;
516
517 /* since this function is called after mark_icon_for_user(), data
518 * must exist here. */
519 data = (icon_data *)g_hash_table_lookup(hash, user_name);
520
521 /* if img has been registerd, just return */
522 if(data && data->pixbuf && !renew)
523 return;
524
525 /* check if saved file exists */
526 if(suffix && !renew) {
527 gchar *filename = NULL;
528 gchar **extp;
529
530 for(extp = ext_list; *extp; extp++) {
531 filename = g_strdup_printf("%s_%s.%s", user_name, suffix, *extp);
532 path = g_build_filename(purple_prefs_get_string(OPT_ICON_DIR),
533 filename, NULL);
534 g_free(filename);
535
536 twitter_debug("path = %s\n", path);
537
538 /* build image from file, if file exists */
539 if(g_file_test(path, G_FILE_TEST_EXISTS)) {
540 gchar *imgdata = NULL;
541 size_t len;
542 GError *err = NULL;
543 GdkPixbuf *pixbuf = NULL;
544 struct stat buf;
545
546 if (!g_file_get_contents(path, &imgdata, &len, &err)) {
547 twitter_debug("Error reading %s: %s\n",
548 path, err->message);
549 g_error_free(err);
550 }
551
552 if(stat(path, &buf))
553 data->mtime = buf.st_mtime;
554
555 pixbuf = make_scaled_pixbuf(imgdata, len);
556 g_free(imgdata);
557
558 if(pixbuf) {
559 data->pixbuf = pixbuf;
560
561 twitter_debug("new icon pixbuf = %p size = %d\n",
562 pixbuf,
563 gdk_pixbuf_get_rowstride(pixbuf) *
564 gdk_pixbuf_get_height(pixbuf));
565
566 data->img_type = *extp;
567
568 twitter_debug("icon data has been loaded from file\n");
569 insert_requested_icon(user_name, service);
570 }
571
572 g_free(path);
573 return;
574 }
575
576 g_free(path);
577
578 } /* for */
579 } /* suffix */
580
581 /* Return if user's icon has been requested already. */
582 if(data->requested)
583 return;
584 else
585 data->requested = TRUE;
586
587 /* Create the URL for an user's icon. */
588 switch(service) {
589 case twitter_service:
590 url = g_strdup_printf("http://twitter.com/%s", user_name);
591 break;
592 case wassr_service:
593 url = g_strdup_printf("http://wassr.jp/user/%s", user_name);
594 break;
595 case identica_service:
596 url = g_strdup_printf("http://identi.ca/%s", user_name);
597 break;
598 case jisko_service:
599 url = g_strdup_printf("http://jisko.net/%s", user_name);
600 break;
601 default:
602 twitter_debug("unknown service\n");
603 break;
604 }
605
606 if(url) {
607 got_icon_data *gotdata = g_new0(got_icon_data, 1);
608 gotdata->user_name = g_strdup(user_name);
609 gotdata->service = service;
610
611 /* gotdata will be released in got_icon_cb */
612 if(service == twitter_service ||
613 service == wassr_service ||
614 service == identica_service ||
615 service == jisko_service) {
616 data->fetch_data = purple_util_fetch_url(url, TRUE, NULL, TRUE,
617 got_page_cb, gotdata);
618 }
619 else {
620 data->fetch_data = purple_util_fetch_url(url, TRUE, NULL, TRUE,
621 got_icon_cb, gotdata);
622 }
623 g_free(url); url = NULL;
624
625 twitter_debug("request %s's icon\n", user_name);
626 }
627 }
628
629 void
630 mark_icon_for_user(GtkTextMark *mark, const gchar *user_name, gint service)
631 {
632 icon_data *data = NULL;
633 GHashTable *hash = NULL;
634
635 twitter_debug("called\n");
636
637 switch(service) {
638 case twitter_service:
639 hash = icon_hash[twitter_service];
640 break;
641 case wassr_service:
642 hash = icon_hash[wassr_service];
643 break;
644 case identica_service:
645 hash = icon_hash[identica_service];
646 break;
647 case jisko_service:
648 hash = icon_hash[jisko_service];
649 break;
650 default:
651 twitter_debug("unknown service\n");
652 break;
653 }
654
655 if(hash)
656 data = (icon_data *)g_hash_table_lookup(hash, user_name);
657
658 /* proper place to allocate icon_data */
659 if(!data) {
660 data = g_new0(icon_data, 1);
661 g_hash_table_insert(hash, g_strdup(user_name), data);
662 }
663
664 data->request_list = g_list_prepend(data->request_list, mark);
665 }
666
667 void
668 invalidate_icon_data_func(gpointer key, gpointer value, gpointer user_data)
669 {
670 icon_data *data = (icon_data *)value;
671
672 g_return_if_fail(data != NULL);
673
674 g_object_unref(data->pixbuf);
675 data->pixbuf = NULL;
676 }
677