Mercurial > pidgin-twitter
comparison pidgin-twitter.c @ 80:e0bf37c105eb
work in progress one hash table code:
- introduced icon_data structure to accommodate every icon related data.
- added a common function remove_marks_func() which removes icon request mark. this function ought to be called from g_hash_table_foreach().
- added another new function cancel_fetch_func(), to clean up fetch requests at unload. this function also to be called from g_hash_table_foreach().
author | Yoshiki Yazawa <yaz@cc.rim.or.jp> |
---|---|
date | Tue, 01 Jul 2008 18:20:48 +0900 |
parents | a4a6c7b204c9 |
children | f1163f2e4920 |
comparison
equal
deleted
inserted
replaced
79:a4a6c7b204c9 | 80:e0bf37c105eb |
---|---|
1 | |
2 /* | 1 /* |
3 * Pidgin-Twitter plugin. | 2 * Pidgin-Twitter plugin. |
4 * | 3 * |
5 * This program is free software; you can redistribute it and/or | 4 * This program is free software; you can redistribute it and/or |
6 * modify it under the terms of the GNU General Public License as | 5 * modify it under the terms of the GNU General Public License as |
22 #include "pidgin-twitter.h" | 21 #include "pidgin-twitter.h" |
23 | 22 |
24 /* globals */ | 23 /* globals */ |
25 static GRegex *regp[7]; | 24 static GRegex *regp[7]; |
26 static gboolean suppress_oops = FALSE; | 25 static gboolean suppress_oops = FALSE; |
27 static GHashTable *icon_id_by_user; | 26 static GHashTable *icon_data_by_user = NULL; |
28 static GList *requested_users = NULL; | 27 |
29 static GList *requestings = NULL; | 28 typedef struct icon_data { |
30 static GHashTable *icon_mark_list_by_user; | 29 gint icon_id; // image id |
30 gboolean requested; // TRUE if download icon has been requested | |
31 GList *request_list; // marker list | |
32 PurpleUtilFetchUrlData *fetch_data; // icon fetch data | |
33 } icon_data; | |
34 | |
31 | 35 |
32 /* this function is a modified clone of purple_markup_strip_html() */ | 36 /* this function is a modified clone of purple_markup_strip_html() */ |
33 static char * | 37 static char * |
34 strip_html_markup(const char *str) | 38 strip_html_markup(const char *str) |
35 { | 39 { |
273 time_t time; | 277 time_t time; |
274 } twitter_message_t; | 278 } twitter_message_t; |
275 | 279 |
276 static void | 280 static void |
277 post_status_with_api_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, | 281 post_status_with_api_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, |
278 const gchar *url_text, size_t len, | 282 const gchar *url_text, size_t len, |
279 const gchar *error_message) | 283 const gchar *error_message) |
280 { | 284 { |
281 twitter_message_t *tm = (struct twitter_message *)user_data; | 285 twitter_message_t *tm = (struct twitter_message *)user_data; |
282 gchar *msg = NULL; | 286 gchar *msg = NULL; |
283 char *p1 = NULL, *p2 = NULL; | 287 char *p1 = NULL, *p2 = NULL; |
284 int error = 1; | 288 int error = 1; |
285 PurpleConversation *conv; | 289 PurpleConversation *conv; |
286 | 290 |
287 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, | 291 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, |
288 "twitter@twitter.com", | 292 "twitter@twitter.com", |
289 tm->account); | 293 tm->account); |
290 if (!conv) { | 294 if (!conv) { |
291 twitter_debug("failed to get conversation\n"); | 295 twitter_debug("failed to get conversation\n"); |
292 goto fin; | 296 goto fin; |
293 } | 297 } |
300 | 304 |
301 if ((strncmp(url_text, "HTTP/1.0", strlen("HTTP/1.0")) == 0 | 305 if ((strncmp(url_text, "HTTP/1.0", strlen("HTTP/1.0")) == 0 |
302 || strncmp(url_text, "HTTP/1.1", strlen("HTTP/1.1")) == 0)) { | 306 || strncmp(url_text, "HTTP/1.1", strlen("HTTP/1.1")) == 0)) { |
303 | 307 |
304 p1 = strchr(url_text, ' '); | 308 p1 = strchr(url_text, ' '); |
305 | 309 |
306 if (p1) { | 310 if (p1) { |
307 p1++; | 311 p1++; |
308 p2 = strchr(p1, ' '); | 312 p2 = strchr(p1, ' '); |
309 if (p2) | 313 if (p2) |
310 p2++; | 314 p2++; |
312 p2 = NULL; | 316 p2 = NULL; |
313 } | 317 } |
314 } | 318 } |
315 | 319 |
316 code = atoi(p1); | 320 code = atoi(p1); |
317 | 321 |
318 if (code == 200) { | 322 if (code == 200) { |
319 error = 0; | 323 error = 0; |
320 } else { | 324 } else { |
321 switch (code) { | 325 switch (code) { |
322 case 400: | 326 case 400: |
341 case 503: | 345 case 503: |
342 msg = g_strdup("Twitter is extremely crowded. " | 346 msg = g_strdup("Twitter is extremely crowded. " |
343 "Try again later."); | 347 "Try again later."); |
344 break; | 348 break; |
345 default: | 349 default: |
346 msg = g_strdup_printf("Unknown error. (%d %s)", | 350 msg = g_strdup_printf("Unknown error. (%d %s)", |
347 code, p2 ? p2 : ""); | 351 code, p2 ? p2 : ""); |
348 break; | 352 break; |
349 } | 353 } |
350 } | 354 } |
351 } | 355 } |
357 m = g_strdup_printf("%s: %s", screen_name, tm->status); | 361 m = g_strdup_printf("%s: %s", screen_name, tm->status); |
358 else | 362 else |
359 m = ""; | 363 m = ""; |
360 | 364 |
361 purple_conv_im_write(conv->u.im, | 365 purple_conv_im_write(conv->u.im, |
362 purple_account_get_username(tm->account), | 366 purple_account_get_username(tm->account), |
363 m, PURPLE_MESSAGE_SEND, tm->time); | 367 m, PURPLE_MESSAGE_SEND, tm->time); |
364 | 368 |
365 g_free(m); | 369 g_free(m); |
366 } else { | 370 } else { |
367 gchar *m; | 371 gchar *m; |
368 m = g_strdup_printf("%s<BR>%s", | 372 m = g_strdup_printf("%s<BR>%s", |
369 msg, tm->status); | 373 msg, tm->status); |
370 /* FIXME: too strong. it should be more smart */ | 374 /* FIXME: too strong. it should be more smart */ |
371 purple_conv_im_write(conv->u.im, | 375 purple_conv_im_write(conv->u.im, |
372 purple_account_get_username(tm->account), | 376 purple_account_get_username(tm->account), |
373 m, PURPLE_MESSAGE_ERROR, time(NULL)); | 377 m, PURPLE_MESSAGE_ERROR, time(NULL)); |
374 g_free(m); | 378 g_free(m); |
375 } | 379 } |
376 | 380 |
377 fin: | 381 fin: |
378 if (msg) | 382 if (msg) |
379 g_free(msg); | 383 g_free(msg); |
380 | 384 |
381 if (tm) { | 385 if (tm) { |
382 if (tm->status) | 386 if (tm->status) |
383 g_free(tm->status); | 387 g_free(tm->status); |
384 g_free(tm); | 388 g_free(tm); |
385 } | 389 } |
390 post_status_with_api(PurpleAccount *account, char **buffer) | 394 post_status_with_api(PurpleAccount *account, char **buffer) |
391 { | 395 { |
392 char *request, *status, *header; | 396 char *request, *status, *header; |
393 const char *url_encoded = purple_url_encode(*buffer); | 397 const char *url_encoded = purple_url_encode(*buffer); |
394 char *basic_auth, *basic_auth_encoded; | 398 char *basic_auth, *basic_auth_encoded; |
395 | 399 |
396 twitter_message_t *tm; | 400 twitter_message_t *tm; |
397 | 401 |
398 const char *screen_name = purple_prefs_get_string(OPT_SCREEN_NAME); | 402 const char *screen_name = purple_prefs_get_string(OPT_SCREEN_NAME); |
399 const char *password = purple_prefs_get_string(OPT_PASSWORD); | 403 const char *password = purple_prefs_get_string(OPT_PASSWORD); |
400 | 404 |
401 twitter_debug("tm.account: %s\n", | 405 twitter_debug("tm.account: %s\n", |
402 purple_account_get_username(account)); | 406 purple_account_get_username(account)); |
403 | 407 |
404 if (!screen_name || !password || !screen_name[0] || !password[0]) { | 408 if (!screen_name || !password || !screen_name[0] || !password[0]) { |
405 twitter_debug("screen_name or password is empty\n"); | 409 twitter_debug("screen_name or password is empty\n"); |
406 return; | 410 return; |
410 tm->account = account; | 414 tm->account = account; |
411 tm->status = g_strdup(*buffer); | 415 tm->status = g_strdup(*buffer); |
412 tm->time = time(NULL); | 416 tm->time = time(NULL); |
413 | 417 |
414 basic_auth = g_strdup_printf("%s:%s", screen_name, password); | 418 basic_auth = g_strdup_printf("%s:%s", screen_name, password); |
415 basic_auth_encoded = purple_base64_encode((unsigned char *)basic_auth, | 419 basic_auth_encoded = purple_base64_encode((unsigned char *)basic_auth, |
416 strlen(basic_auth)); | 420 strlen(basic_auth)); |
417 g_free(basic_auth); | 421 g_free(basic_auth); |
418 | 422 |
419 status = g_strdup_printf(TWITTER_STATUS_FORMAT, url_encoded); | 423 status = g_strdup_printf(TWITTER_STATUS_FORMAT, url_encoded); |
420 | 424 |
421 header = g_strdup_printf(TWITTER_STATUS_POST, basic_auth_encoded, | 425 header = g_strdup_printf(TWITTER_STATUS_POST, basic_auth_encoded, |
422 strlen(status)); | 426 (int)strlen(status)); |
423 | 427 |
424 request = g_strconcat(header, status, TWITTER_STATUS_TERMINATOR, NULL); | 428 request = g_strconcat(header, status, TWITTER_STATUS_TERMINATOR, NULL); |
425 | 429 |
426 purple_util_fetch_url_request(TWITTER_BASE_URL, FALSE, | 430 purple_util_fetch_url_request(TWITTER_BASE_URL, FALSE, |
427 NULL, TRUE, request, TRUE, | 431 NULL, TRUE, request, TRUE, |
429 | 433 |
430 g_free(header); | 434 g_free(header); |
431 g_free(basic_auth_encoded); | 435 g_free(basic_auth_encoded); |
432 g_free(status); | 436 g_free(status); |
433 g_free(request); | 437 g_free(request); |
434 | 438 |
435 } | 439 } |
436 | 440 |
437 static gboolean | 441 static gboolean |
438 sending_im_cb(PurpleAccount *account, char *recipient, char **buffer, | 442 sending_im_cb(PurpleAccount *account, char *recipient, char **buffer, |
439 void *data) | 443 void *data) |
698 | 702 |
699 gtk_widget_queue_draw(pidgin_conv_get_window(gtkconv)->window); | 703 gtk_widget_queue_draw(pidgin_conv_get_window(gtkconv)->window); |
700 } | 704 } |
701 | 705 |
702 static void | 706 static void |
707 remove_marks_func(gpointer key, gpointer value, gpointer user_data) | |
708 { | |
709 icon_data *data = (icon_data *)value; | |
710 GtkTextBuffer *text_buffer = (GtkTextBuffer *)user_data; | |
711 GList *mark_list = NULL; | |
712 GList *current; | |
713 | |
714 if(data && data->request_list) | |
715 mark_list = data->request_list; | |
716 | |
717 /* remove the marks in its GtkTextBuffers */ | |
718 for(current = g_list_first(mark_list); current; | |
719 current = g_list_next(current)) { | |
720 GtkTextMark *current_mark = current->data; | |
721 GtkTextBuffer *current_text_buffer = gtk_text_mark_get_buffer( | |
722 current_mark); | |
723 | |
724 if(!current_text_buffer) | |
725 continue; | |
726 | |
727 if(text_buffer) { | |
728 if(current_text_buffer == text_buffer) { | |
729 /* the mark will be freed in this function */ | |
730 gtk_text_buffer_delete_mark(current_text_buffer, | |
731 current_mark); | |
732 current->data = NULL; | |
733 } | |
734 } | |
735 else { | |
736 gtk_text_buffer_delete_mark(current_text_buffer, current_mark); | |
737 current->data = NULL; | |
738 } | |
739 } /* end of for */ | |
740 | |
741 mark_list = g_list_remove_all(mark_list, NULL); | |
742 data->request_list = mark_list; | |
743 } | |
744 | |
745 static void | |
703 delete_requested_icon_marks(PidginConversation *conv) { | 746 delete_requested_icon_marks(PidginConversation *conv) { |
704 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( | 747 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( |
705 GTK_TEXT_VIEW(conv->imhtml)); | 748 GTK_TEXT_VIEW(conv->imhtml)); |
706 GList *user_name_list = g_hash_table_get_keys(icon_mark_list_by_user); | 749 |
707 GList *current_user; | 750 g_hash_table_foreach(icon_data_by_user, |
708 | 751 (GHFunc)remove_marks_func, |
709 for(current_user = g_list_first(user_name_list); current_user; | 752 (gpointer)text_buffer); |
710 current_user = g_list_next(current_user)) { | 753 |
711 gchar *user_name = current_user->data; | |
712 GList *mark_list = g_hash_table_lookup(icon_mark_list_by_user, | |
713 user_name); | |
714 | |
715 /* delete the marks in the buffer that will be closed. */ | |
716 GList *current = g_list_first(mark_list); | |
717 while(current) { | |
718 GtkTextMark *mark = current->data; | |
719 GList *next = g_list_next(current); | |
720 | |
721 if(gtk_text_mark_get_buffer(mark) == text_buffer) { | |
722 /* the mark will be freed in the function */ | |
723 gtk_text_buffer_delete_mark(text_buffer, mark); | |
724 mark_list = g_list_delete_link(mark_list, current); | |
725 } | |
726 | |
727 current = next; | |
728 } | |
729 | |
730 /* Does not replace the old key */ | |
731 g_hash_table_insert(icon_mark_list_by_user, | |
732 g_strdup(user_name), mark_list); | |
733 } | |
734 g_list_free(user_name_list); | |
735 } | 754 } |
736 | 755 |
737 static void | 756 static void |
738 attach_to_window(void) | 757 attach_to_window(void) |
739 { | 758 { |
941 conv_list = conv_list->next) { | 960 conv_list = conv_list->next) { |
942 PidginConversation *conv = conv_list->data; | 961 PidginConversation *conv = conv_list->data; |
943 PurpleConversation *purple_conv = conv->active_conv; | 962 PurpleConversation *purple_conv = conv->active_conv; |
944 | 963 |
945 if(purple_conv && is_twitter_conv(purple_conv)) { | 964 if(purple_conv && is_twitter_conv(purple_conv)) { |
946 GtkIMHtml *current_imhtml = GTK_IMHTML(conv->imhtml); | 965 GtkIMHtml *current_imhtml = GTK_IMHTML(conv->imhtml); |
947 GtkTextBuffer *current_buffer = gtk_text_view_get_buffer( | 966 GtkTextBuffer *current_buffer = gtk_text_view_get_buffer( |
948 GTK_TEXT_VIEW(current_imhtml)); | 967 GTK_TEXT_VIEW(current_imhtml)); |
949 | 968 |
950 if(current_buffer == gtk_text_mark_get_buffer(requested_mark)) { | 969 if(current_buffer == gtk_text_mark_get_buffer(requested_mark)) { |
951 target_imhtml = current_imhtml; | 970 target_imhtml = current_imhtml; |
963 | 982 |
964 gtk_text_buffer_get_iter_at_mark(target_buffer, | 983 gtk_text_buffer_get_iter_at_mark(target_buffer, |
965 &inserting_point, requested_mark); | 984 &inserting_point, requested_mark); |
966 | 985 |
967 /* insert icon */ | 986 /* insert icon */ |
968 icon_id = GPOINTER_TO_INT(g_hash_table_lookup(icon_id_by_user, user_name)); | 987 icon_data *data = (icon_data *)g_hash_table_lookup(icon_data_by_user, user_name); |
988 if(data) | |
989 icon_id = data->icon_id; | |
990 else | |
991 icon_id = 0; | |
992 | |
969 if(!icon_id) { | 993 if(!icon_id) { |
970 return; | 994 return; |
971 } | 995 } |
972 | 996 |
973 /* insert icon actually */ | 997 /* insert icon actually */ |
974 gtk_imhtml_insert_image_at_iter(target_imhtml, icon_id, &inserting_point); | 998 gtk_imhtml_insert_image_at_iter(target_imhtml, icon_id, &inserting_point); |
975 gtk_text_buffer_delete_mark(target_buffer, requested_mark); | 999 gtk_text_buffer_delete_mark(target_buffer, requested_mark); |
1000 requested_mark = NULL; | |
1001 | |
976 } | 1002 } |
977 | 1003 |
978 static void | 1004 static void |
979 insert_requested_icon(const gchar *user_name) | 1005 insert_requested_icon(const gchar *user_name) |
980 { | 1006 { |
981 GList *mark_list = g_hash_table_lookup(icon_mark_list_by_user, user_name); | 1007 icon_data *data = (icon_data *)g_hash_table_lookup(icon_data_by_user, user_name); |
982 | 1008 GList *mark_list = NULL; |
983 g_list_foreach(mark_list, (GFunc) insert_icon_at_mark, (gchar *)user_name); | 1009 |
984 g_hash_table_remove(icon_mark_list_by_user, user_name); | 1010 if(data) |
985 g_list_free(mark_list); | 1011 mark_list = data->request_list; |
1012 | |
1013 if(mark_list) { | |
1014 g_list_foreach(mark_list, (GFunc) insert_icon_at_mark, (gchar *)user_name); | |
1015 mark_list = g_list_remove_all(mark_list, NULL); | |
1016 g_list_free(mark_list); | |
1017 data->request_list = NULL; | |
1018 } | |
986 } | 1019 } |
987 | 1020 |
988 static void | 1021 static void |
989 got_icon_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, | 1022 got_icon_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, |
990 const gchar *url_text, gsize len, const gchar *error_message) | 1023 const gchar *url_text, gsize len, const gchar *error_message) |
991 { | 1024 { |
992 gchar *user_name = (gchar *)user_data; | 1025 gchar *user_name = (gchar *)user_data; |
993 int icon_id; | 1026 int icon_id; |
1027 icon_data *data = NULL; | |
994 | 1028 |
995 twitter_debug("called\n"); | 1029 twitter_debug("called\n"); |
996 | 1030 |
997 requestings = g_list_remove(requestings, url_data); | 1031 data = (icon_data *)g_hash_table_lookup(icon_data_by_user, user_name); |
1032 | |
1033 if(data && data->fetch_data) { | |
1034 data->fetch_data = NULL; | |
1035 } | |
998 | 1036 |
999 /* Return if user's icon had already been downloaded or | 1037 /* Return if user's icon had already been downloaded or |
1000 * the download is failure. */ | 1038 * the download is failure. */ |
1001 if(g_hash_table_lookup(icon_id_by_user, user_name) || !url_text) { | 1039 if((data && data->icon_id) || !url_text) { //xxx |
1002 if(!url_text) { | 1040 if(!url_text) { |
1003 twitter_debug("downloading %s's icon was failure : %s\n", | 1041 twitter_debug("downloading %s's icon was failure : %s\n", |
1004 user_name, error_message); | 1042 user_name, error_message); |
1005 } | 1043 } |
1006 else { | 1044 else { |
1007 twitter_debug("%s's icon has already been downloaded\n", user_name); | 1045 twitter_debug("%s's icon has already been downloaded\n", user_name); |
1008 } | 1046 } |
1009 | 1047 |
1010 GList *deleting = g_list_find_custom(requested_users, | 1048 data->requested = FALSE; |
1011 user_name, (GCompareFunc) strcmp); | 1049 |
1012 if(deleting) { | 1050 |
1013 g_free(deleting->data); | |
1014 requested_users = g_list_delete_link(requested_users, deleting); | |
1015 } | |
1016 g_free(user_name); | 1051 g_free(user_name); |
1017 return; | 1052 return; |
1018 } | 1053 } |
1019 | 1054 |
1020 icon_id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, | 1055 icon_id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, |
1021 user_name); | 1056 user_name); |
1022 g_hash_table_insert(icon_id_by_user, | 1057 if(!data) { |
1023 g_strdup(user_name),GINT_TO_POINTER(icon_id)); | 1058 data = g_new0(icon_data, 1); |
1059 } | |
1060 data->icon_id = icon_id; | |
1061 g_hash_table_insert(icon_data_by_user, | |
1062 g_strdup(user_name), data); | |
1024 | 1063 |
1025 const gchar *dirname = purple_prefs_get_string(OPT_ICON_DIR); | 1064 const gchar *dirname = purple_prefs_get_string(OPT_ICON_DIR); |
1026 | 1065 |
1027 /* store retrieved image to a file in icon dir */ | 1066 /* store retrieved image to a file in icon dir */ |
1028 if(ensure_path_exists(dirname)) { | 1067 if(ensure_path_exists(dirname)) { |
1055 | 1094 |
1056 static void | 1095 static void |
1057 request_icon(const char *user_name) | 1096 request_icon(const char *user_name) |
1058 { | 1097 { |
1059 gchar *url = NULL; | 1098 gchar *url = NULL; |
1060 PurpleUtilFetchUrlData *fetch_data = NULL; | |
1061 | 1099 |
1062 /* look local icon cache for the requested icon */ | 1100 /* look local icon cache for the requested icon */ |
1063 gchar *filename = NULL; | 1101 gchar *filename = NULL; |
1064 gchar *path = NULL; | 1102 gchar *path = NULL; |
1065 gint icon_id; | 1103 icon_data *data = NULL; |
1104 | |
1105 data = (icon_data *)g_hash_table_lookup(icon_data_by_user, user_name); | |
1106 | |
1107 if(!data) { | |
1108 data = g_new0(icon_data, 1); | |
1109 g_hash_table_insert(icon_data_by_user, | |
1110 g_strdup(user_name), data); | |
1111 } | |
1066 | 1112 |
1067 /* if img has been registerd, just return */ | 1113 /* if img has been registerd, just return */ |
1068 icon_id = GPOINTER_TO_INT(g_hash_table_lookup(icon_id_by_user, user_name)); | 1114 if(data->icon_id) |
1069 if(icon_id) { | |
1070 return; | 1115 return; |
1071 } | 1116 |
1072 | 1117 /* check if saved file exists */ |
1073 filename = g_strdup_printf("%s.gif", user_name); | 1118 filename = g_strdup_printf("%s.gif", user_name); |
1074 | |
1075 path = g_build_filename( | 1119 path = g_build_filename( |
1076 purple_prefs_get_string(OPT_ICON_DIR), filename, NULL); | 1120 purple_prefs_get_string(OPT_ICON_DIR), filename, NULL); |
1077 | 1121 |
1078 if(!g_file_test(path, G_FILE_TEST_EXISTS)) { | 1122 if(!g_file_test(path, G_FILE_TEST_EXISTS)) { |
1079 g_free(path); | 1123 g_free(path); |
1081 path = g_build_filename( | 1125 path = g_build_filename( |
1082 purple_prefs_get_string(OPT_ICON_DIR), filename, NULL); | 1126 purple_prefs_get_string(OPT_ICON_DIR), filename, NULL); |
1083 } | 1127 } |
1084 /* build image from file, if file exists */ | 1128 /* build image from file, if file exists */ |
1085 if(g_file_test(path, G_FILE_TEST_EXISTS)) { | 1129 if(g_file_test(path, G_FILE_TEST_EXISTS)) { |
1086 gchar *data = NULL; | 1130 gchar *imgdata = NULL; |
1087 size_t len; | 1131 size_t len; |
1088 GError *err = NULL; | 1132 GError *err = NULL; |
1089 | 1133 |
1090 if (!g_file_get_contents(path, &data, &len, &err)) { | 1134 if (!g_file_get_contents(path, &imgdata, &len, &err)) { |
1091 twitter_debug("Error reading %s: %s\n", | 1135 twitter_debug("Error reading %s: %s\n", |
1092 path, err->message); | 1136 path, err->message); |
1093 g_error_free(err); | 1137 g_error_free(err); |
1094 } | 1138 } |
1095 | 1139 |
1096 icon_id = purple_imgstore_add_with_id(data, len, path); | 1140 data->icon_id = purple_imgstore_add_with_id(imgdata, len, path); |
1097 | |
1098 g_hash_table_insert(icon_id_by_user, g_strdup(user_name), GINT_TO_POINTER(icon_id)); | |
1099 g_free(filename); | 1141 g_free(filename); |
1100 g_free(path); | 1142 g_free(path); |
1101 | 1143 |
1144 twitter_debug("icon data has been loaded from file\n"); | |
1145 | |
1102 insert_requested_icon(user_name); | 1146 insert_requested_icon(user_name); |
1103 return; | 1147 return; |
1104 } | 1148 } |
1105 | 1149 |
1106 /* Return if user's icon has been requested already. */ | 1150 /* Return if user's icon has been requested already. */ |
1107 if(g_list_find_custom(requested_users, user_name, (GCompareFunc)strcmp)) { | 1151 if(data->requested) |
1108 return; | 1152 return; |
1109 } | 1153 else |
1110 | 1154 data->requested = TRUE; |
1111 requested_users = g_list_append(requested_users, g_strdup(user_name)); | 1155 |
1112 | 1156 |
1113 /* Create the URL of the user's icon. | 1157 /* Create the URL of the user's icon. |
1114 * See http://twitter.g.hatena.ne.jp/ikko615/20080107/1199703400 | 1158 * See http://twitter.g.hatena.ne.jp/ikko615/20080107/1199703400 |
1115 */ | 1159 */ |
1116 url = g_strdup_printf("http://img.twitty.jp/twitter/user/%s/m.gif", | 1160 url = g_strdup_printf("http://img.twitty.jp/twitter/user/%s/m.gif", |
1117 user_name); | 1161 user_name); |
1118 | 1162 |
1119 fetch_data = purple_util_fetch_url(url, TRUE, NULL, TRUE, | 1163 data->fetch_data = purple_util_fetch_url(url, TRUE, NULL, TRUE, |
1120 got_icon_cb, g_strdup(user_name)); | 1164 got_icon_cb, g_strdup(user_name)); |
1121 g_free(url); | 1165 g_free(url); url = NULL; |
1122 requestings = g_list_append(requestings, fetch_data); | |
1123 | 1166 |
1124 twitter_debug("request %s's icon\n", user_name); | 1167 twitter_debug("request %s's icon\n", user_name); |
1125 } | 1168 } |
1126 | 1169 |
1127 static void | 1170 static void |
1128 mark_icon_for_user(GtkTextMark *mark, const gchar *user_name) | 1171 mark_icon_for_user(GtkTextMark *mark, const gchar *user_name) |
1129 { | 1172 { |
1130 GList *mark_list = g_hash_table_lookup(icon_mark_list_by_user, user_name); | 1173 icon_data *data = NULL; |
1131 | 1174 data = (icon_data *)g_hash_table_lookup(icon_data_by_user, user_name); |
1175 | |
1176 if(!data) { | |
1177 data = g_new0(icon_data, 1); | |
1178 g_hash_table_insert(icon_data_by_user, | |
1179 g_strdup(user_name), data); | |
1180 } | |
1181 | |
1182 GList *mark_list = data->request_list; | |
1132 mark_list = g_list_append(mark_list, mark); | 1183 mark_list = g_list_append(mark_list, mark); |
1133 g_hash_table_replace(icon_mark_list_by_user, | 1184 data->request_list = mark_list; |
1134 g_strdup(user_name), mark_list); | |
1135 } | 1185 } |
1136 | 1186 |
1137 static void | 1187 static void |
1138 displayed_im_cb(PurpleAccount *account, const char *who, char *message, | 1188 displayed_im_cb(PurpleAccount *account, const char *who, char *message, |
1139 PurpleConversation *conv, PurpleMessageFlags flags) | 1189 PurpleConversation *conv, PurpleMessageFlags flags) |
1168 | 1218 |
1169 /* get GtkTextIter of the last line */ | 1219 /* get GtkTextIter of the last line */ |
1170 gtk_text_buffer_get_iter_at_line(text_buffer, &inserting_point, | 1220 gtk_text_buffer_get_iter_at_line(text_buffer, &inserting_point, |
1171 gtk_text_buffer_get_line_count(text_buffer) - 1); | 1221 gtk_text_buffer_get_line_count(text_buffer) - 1); |
1172 | 1222 |
1173 icon_id = GPOINTER_TO_INT(g_hash_table_lookup(icon_id_by_user, user_name)); | 1223 icon_data *data = NULL; |
1224 data = g_hash_table_lookup(icon_data_by_user, user_name); | |
1225 if(data) | |
1226 icon_id = data->icon_id; | |
1227 else | |
1228 icon_id = 0; | |
1174 | 1229 |
1175 /* If the user's icon has not been downloaded, | 1230 /* If the user's icon has not been downloaded, |
1176 * mark the message and request the icon. */ | 1231 * mark the message and request the icon. */ |
1177 if(!icon_id) { | 1232 if(!icon_id) { |
1178 twitter_debug("%s's icon has not been downloaded.\n", user_name); | 1233 twitter_debug("%s's icon is not in memory.\n", user_name); |
1179 mark_icon_for_user(gtk_text_buffer_create_mark( | 1234 mark_icon_for_user(gtk_text_buffer_create_mark( |
1180 text_buffer, NULL, &inserting_point, FALSE), | 1235 text_buffer, NULL, &inserting_point, FALSE), |
1181 user_name); | 1236 user_name); |
1182 /* request to attach icon to the buffer */ | 1237 /* request to attach icon to the buffer */ |
1183 request_icon(user_name); | 1238 request_icon(user_name); |
1217 regp[PSEUDO] = g_regex_new(P_PSEUDO, G_REGEX_RAW, 0, NULL); | 1272 regp[PSEUDO] = g_regex_new(P_PSEUDO, G_REGEX_RAW, 0, NULL); |
1218 regp[USER] = g_regex_new(P_USER, 0, 0, NULL); | 1273 regp[USER] = g_regex_new(P_USER, 0, 0, NULL); |
1219 regp[USER_FIRST_LINE] = g_regex_new(P_USER_FIRST_LINE, 0, 0, NULL); | 1274 regp[USER_FIRST_LINE] = g_regex_new(P_USER_FIRST_LINE, 0, 0, NULL); |
1220 regp[USER_FORMATTED] = g_regex_new(P_USER_FORMATTED, G_REGEX_RAW, 0, NULL); | 1275 regp[USER_FORMATTED] = g_regex_new(P_USER_FORMATTED, G_REGEX_RAW, 0, NULL); |
1221 | 1276 |
1222 /* hash table for user's icons */ | 1277 icon_data_by_user = g_hash_table_new_full(g_str_hash, g_str_equal, |
1223 icon_id_by_user = g_hash_table_new_full(g_str_hash, g_str_equal, | 1278 g_free, NULL); |
1224 g_free, NULL); | |
1225 | |
1226 /* hash table for a list of marks that are alternative icons */ | |
1227 icon_mark_list_by_user = g_hash_table_new_full(g_str_hash, g_str_equal, | |
1228 g_free, NULL); | |
1229 | 1279 |
1230 /* attach counter to the existing twitter window */ | 1280 /* attach counter to the existing twitter window */ |
1231 if(purple_prefs_get_bool(OPT_COUNTER)) { | 1281 if(purple_prefs_get_bool(OPT_COUNTER)) { |
1232 attach_to_window(); | 1282 attach_to_window(); |
1233 } | 1283 } |
1234 | 1284 |
1235 return TRUE; | 1285 return TRUE; |
1236 } | 1286 } |
1237 | 1287 |
1288 static void | |
1289 cancel_fetch_func(gpointer key, gpointer value, gpointer user_data) | |
1290 { | |
1291 icon_data *data = (icon_data *)value; | |
1292 | |
1293 if(!data) | |
1294 return; | |
1295 | |
1296 if(data->fetch_data) { | |
1297 purple_util_fetch_url_cancel(data->fetch_data); | |
1298 data->fetch_data = NULL; | |
1299 } | |
1300 | |
1301 if(data->request_list) { | |
1302 twitter_debug("somehow, request_list != NULL\n"); | |
1303 } | |
1304 | |
1305 return; | |
1306 } | |
1307 | |
1238 static gboolean | 1308 static gboolean |
1239 unload_plugin(PurplePlugin *plugin) | 1309 unload_plugin(PurplePlugin *plugin) |
1240 { | 1310 { |
1241 GList *list, *current; | |
1242 | |
1243 twitter_debug("called\n"); | 1311 twitter_debug("called\n"); |
1244 | 1312 |
1245 /* disconnect from signal */ | 1313 /* disconnect from signal */ |
1246 purple_signal_disconnect(purple_conversations_get_handle(), | 1314 purple_signal_disconnect(purple_conversations_get_handle(), |
1247 "writing-im-msg", | 1315 "writing-im-msg", |
1269 g_regex_unref(regp[PSEUDO]); | 1337 g_regex_unref(regp[PSEUDO]); |
1270 g_regex_unref(regp[USER]); | 1338 g_regex_unref(regp[USER]); |
1271 g_regex_unref(regp[USER_FIRST_LINE]); | 1339 g_regex_unref(regp[USER_FIRST_LINE]); |
1272 g_regex_unref(regp[USER_FORMATTED]); | 1340 g_regex_unref(regp[USER_FORMATTED]); |
1273 | 1341 |
1274 /* remove the hash table of marks that are alternative icons */ | 1342 /* remove mark list in each hash entry */ |
1275 list = g_hash_table_get_values(icon_mark_list_by_user); | 1343 g_hash_table_foreach(icon_data_by_user, (GHFunc)remove_marks_func, NULL); |
1276 for(current = g_list_first(list); current; | |
1277 current = g_list_next(current)) { | |
1278 GList *mark_list = current->data; | |
1279 GList *current_mark_list; | |
1280 | |
1281 /* remove the marks in its GtkTextBuffers */ | |
1282 for(current_mark_list = g_list_first(mark_list); current_mark_list; | |
1283 current_mark_list = g_list_next(current_mark_list)) { | |
1284 GtkTextMark *current_mark = current_mark_list->data; | |
1285 GtkTextBuffer *text_buffer = gtk_text_mark_get_buffer( | |
1286 current_mark); | |
1287 | |
1288 if(text_buffer) { | |
1289 /* the mark will be freed in this function */ | |
1290 gtk_text_buffer_delete_mark(text_buffer, current_mark); | |
1291 } | |
1292 } | |
1293 | |
1294 g_list_free(mark_list); | |
1295 } | |
1296 g_list_free(list); | |
1297 g_hash_table_destroy(icon_mark_list_by_user); | |
1298 | 1344 |
1299 /* cancel request that has not been finished yet */ | 1345 /* cancel request that has not been finished yet */ |
1300 for(list = g_list_first(requestings); list; list = g_list_next(list)) { | 1346 g_hash_table_foreach(icon_data_by_user, (GHFunc)cancel_fetch_func, NULL); |
1301 purple_util_fetch_url_cancel(list->data); | 1347 |
1302 } | 1348 /* destroy hash table for icon_data */ |
1303 g_list_free(requestings); | 1349 g_hash_table_destroy(icon_data_by_user); //XXX all memory freed? --yaz |
1304 requestings = NULL; | |
1305 | |
1306 /* destroy hash table for icons */ | |
1307 g_hash_table_destroy(icon_id_by_user); | |
1308 for(list = g_list_first(requested_users); list; list = g_list_next(list)) { | |
1309 g_free(list->data); | |
1310 } | |
1311 g_list_free(requested_users); | |
1312 requested_users = NULL; | |
1313 | 1350 |
1314 /* detach from twitter window */ | 1351 /* detach from twitter window */ |
1315 detach_from_window(); | 1352 detach_from_window(); |
1316 | 1353 |
1317 return TRUE; | 1354 return TRUE; |