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;