Mercurial > pidgin-twitter
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 |