# HG changeset patch # User Yoshiki Yazawa # Date 1214904048 -32400 # Node ID e0bf37c105ebbae99f07f6e876b17b114408274e # Parent a4a6c7b204c9bad723e16861e4675e1849528d61 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(). diff -r a4a6c7b204c9 -r e0bf37c105eb pidgin-twitter.c --- a/pidgin-twitter.c Mon Jun 30 11:02:01 2008 +0900 +++ b/pidgin-twitter.c Tue Jul 01 18:20:48 2008 +0900 @@ -1,4 +1,3 @@ - /* * Pidgin-Twitter plugin. * @@ -24,10 +23,15 @@ /* globals */ static GRegex *regp[7]; static gboolean suppress_oops = FALSE; -static GHashTable *icon_id_by_user; -static GList *requested_users = NULL; -static GList *requestings = NULL; -static GHashTable *icon_mark_list_by_user; +static GHashTable *icon_data_by_user = NULL; + +typedef struct icon_data { + gint icon_id; // image id + gboolean requested; // TRUE if download icon has been requested + GList *request_list; // marker list + PurpleUtilFetchUrlData *fetch_data; // icon fetch data +} icon_data; + /* this function is a modified clone of purple_markup_strip_html() */ static char * @@ -275,7 +279,7 @@ static void post_status_with_api_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *url_text, size_t len, + const gchar *url_text, size_t len, const gchar *error_message) { twitter_message_t *tm = (struct twitter_message *)user_data; @@ -285,7 +289,7 @@ PurpleConversation *conv; conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, - "twitter@twitter.com", + "twitter@twitter.com", tm->account); if (!conv) { twitter_debug("failed to get conversation\n"); @@ -302,7 +306,7 @@ || strncmp(url_text, "HTTP/1.1", strlen("HTTP/1.1")) == 0)) { p1 = strchr(url_text, ' '); - + if (p1) { p1++; p2 = strchr(p1, ' '); @@ -314,7 +318,7 @@ } code = atoi(p1); - + if (code == 200) { error = 0; } else { @@ -343,7 +347,7 @@ "Try again later."); break; default: - msg = g_strdup_printf("Unknown error. (%d %s)", + msg = g_strdup_printf("Unknown error. (%d %s)", code, p2 ? p2 : ""); break; } @@ -359,7 +363,7 @@ m = ""; purple_conv_im_write(conv->u.im, - purple_account_get_username(tm->account), + purple_account_get_username(tm->account), m, PURPLE_MESSAGE_SEND, tm->time); g_free(m); @@ -369,7 +373,7 @@ msg, tm->status); /* FIXME: too strong. it should be more smart */ purple_conv_im_write(conv->u.im, - purple_account_get_username(tm->account), + purple_account_get_username(tm->account), m, PURPLE_MESSAGE_ERROR, time(NULL)); g_free(m); } @@ -377,7 +381,7 @@ fin: if (msg) g_free(msg); - + if (tm) { if (tm->status) g_free(tm->status); @@ -392,13 +396,13 @@ char *request, *status, *header; const char *url_encoded = purple_url_encode(*buffer); char *basic_auth, *basic_auth_encoded; - + twitter_message_t *tm; const char *screen_name = purple_prefs_get_string(OPT_SCREEN_NAME); const char *password = purple_prefs_get_string(OPT_PASSWORD); - twitter_debug("tm.account: %s\n", + twitter_debug("tm.account: %s\n", purple_account_get_username(account)); if (!screen_name || !password || !screen_name[0] || !password[0]) { @@ -412,14 +416,14 @@ tm->time = time(NULL); basic_auth = g_strdup_printf("%s:%s", screen_name, password); - basic_auth_encoded = purple_base64_encode((unsigned char *)basic_auth, + basic_auth_encoded = purple_base64_encode((unsigned char *)basic_auth, strlen(basic_auth)); g_free(basic_auth); status = g_strdup_printf(TWITTER_STATUS_FORMAT, url_encoded); header = g_strdup_printf(TWITTER_STATUS_POST, basic_auth_encoded, - strlen(status)); + (int)strlen(status)); request = g_strconcat(header, status, TWITTER_STATUS_TERMINATOR, NULL); @@ -431,7 +435,7 @@ g_free(basic_auth_encoded); g_free(status); g_free(request); - + } static gboolean @@ -700,38 +704,53 @@ } static void +remove_marks_func(gpointer key, gpointer value, gpointer user_data) +{ + icon_data *data = (icon_data *)value; + GtkTextBuffer *text_buffer = (GtkTextBuffer *)user_data; + GList *mark_list = NULL; + GList *current; + + if(data && data->request_list) + mark_list = data->request_list; + + /* remove the marks in its GtkTextBuffers */ + for(current = g_list_first(mark_list); current; + current = g_list_next(current)) { + GtkTextMark *current_mark = current->data; + GtkTextBuffer *current_text_buffer = gtk_text_mark_get_buffer( + current_mark); + + if(!current_text_buffer) + continue; + + if(text_buffer) { + if(current_text_buffer == text_buffer) { + /* the mark will be freed in this function */ + gtk_text_buffer_delete_mark(current_text_buffer, + current_mark); + current->data = NULL; + } + } + else { + gtk_text_buffer_delete_mark(current_text_buffer, current_mark); + current->data = NULL; + } + } /* end of for */ + + mark_list = g_list_remove_all(mark_list, NULL); + data->request_list = mark_list; +} + +static void delete_requested_icon_marks(PidginConversation *conv) { GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(conv->imhtml)); - GList *user_name_list = g_hash_table_get_keys(icon_mark_list_by_user); - GList *current_user; - - for(current_user = g_list_first(user_name_list); current_user; - current_user = g_list_next(current_user)) { - gchar *user_name = current_user->data; - GList *mark_list = g_hash_table_lookup(icon_mark_list_by_user, - user_name); - /* delete the marks in the buffer that will be closed. */ - GList *current = g_list_first(mark_list); - while(current) { - GtkTextMark *mark = current->data; - GList *next = g_list_next(current); - - if(gtk_text_mark_get_buffer(mark) == text_buffer) { - /* the mark will be freed in the function */ - gtk_text_buffer_delete_mark(text_buffer, mark); - mark_list = g_list_delete_link(mark_list, current); - } - - current = next; - } + g_hash_table_foreach(icon_data_by_user, + (GHFunc)remove_marks_func, + (gpointer)text_buffer); - /* Does not replace the old key */ - g_hash_table_insert(icon_mark_list_by_user, - g_strdup(user_name), mark_list); - } - g_list_free(user_name_list); } static void @@ -943,7 +962,7 @@ PurpleConversation *purple_conv = conv->active_conv; if(purple_conv && is_twitter_conv(purple_conv)) { - GtkIMHtml *current_imhtml = GTK_IMHTML(conv->imhtml); + GtkIMHtml *current_imhtml = GTK_IMHTML(conv->imhtml); GtkTextBuffer *current_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(current_imhtml)); @@ -965,7 +984,12 @@ &inserting_point, requested_mark); /* insert icon */ - icon_id = GPOINTER_TO_INT(g_hash_table_lookup(icon_id_by_user, user_name)); + icon_data *data = (icon_data *)g_hash_table_lookup(icon_data_by_user, user_name); + if(data) + icon_id = data->icon_id; + else + icon_id = 0; + if(!icon_id) { return; } @@ -973,16 +997,25 @@ /* insert icon actually */ gtk_imhtml_insert_image_at_iter(target_imhtml, icon_id, &inserting_point); gtk_text_buffer_delete_mark(target_buffer, requested_mark); + requested_mark = NULL; + } static void insert_requested_icon(const gchar *user_name) { - GList *mark_list = g_hash_table_lookup(icon_mark_list_by_user, user_name); + icon_data *data = (icon_data *)g_hash_table_lookup(icon_data_by_user, user_name); + GList *mark_list = NULL; + + if(data) + mark_list = data->request_list; - g_list_foreach(mark_list, (GFunc) insert_icon_at_mark, (gchar *)user_name); - g_hash_table_remove(icon_mark_list_by_user, user_name); - g_list_free(mark_list); + if(mark_list) { + g_list_foreach(mark_list, (GFunc) insert_icon_at_mark, (gchar *)user_name); + mark_list = g_list_remove_all(mark_list, NULL); + g_list_free(mark_list); + data->request_list = NULL; + } } static void @@ -991,14 +1024,19 @@ { gchar *user_name = (gchar *)user_data; int icon_id; + icon_data *data = NULL; twitter_debug("called\n"); - requestings = g_list_remove(requestings, url_data); + data = (icon_data *)g_hash_table_lookup(icon_data_by_user, user_name); + + if(data && data->fetch_data) { + data->fetch_data = NULL; + } /* Return if user's icon had already been downloaded or * the download is failure. */ - if(g_hash_table_lookup(icon_id_by_user, user_name) || !url_text) { + if((data && data->icon_id) || !url_text) { //xxx if(!url_text) { twitter_debug("downloading %s's icon was failure : %s\n", user_name, error_message); @@ -1007,20 +1045,21 @@ twitter_debug("%s's icon has already been downloaded\n", user_name); } - GList *deleting = g_list_find_custom(requested_users, - user_name, (GCompareFunc) strcmp); - if(deleting) { - g_free(deleting->data); - requested_users = g_list_delete_link(requested_users, deleting); - } + data->requested = FALSE; + + g_free(user_name); return; } icon_id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, user_name); - g_hash_table_insert(icon_id_by_user, - g_strdup(user_name),GINT_TO_POINTER(icon_id)); + if(!data) { + data = g_new0(icon_data, 1); + } + data->icon_id = icon_id; + g_hash_table_insert(icon_data_by_user, + g_strdup(user_name), data); const gchar *dirname = purple_prefs_get_string(OPT_ICON_DIR); @@ -1057,21 +1096,26 @@ request_icon(const char *user_name) { gchar *url = NULL; - PurpleUtilFetchUrlData *fetch_data = NULL; /* look local icon cache for the requested icon */ gchar *filename = NULL; gchar *path = NULL; - gint icon_id; + icon_data *data = NULL; + + data = (icon_data *)g_hash_table_lookup(icon_data_by_user, user_name); + + if(!data) { + data = g_new0(icon_data, 1); + g_hash_table_insert(icon_data_by_user, + g_strdup(user_name), data); + } /* if img has been registerd, just return */ - icon_id = GPOINTER_TO_INT(g_hash_table_lookup(icon_id_by_user, user_name)); - if(icon_id) { + if(data->icon_id) return; - } + /* check if saved file exists */ filename = g_strdup_printf("%s.gif", user_name); - path = g_build_filename( purple_prefs_get_string(OPT_ICON_DIR), filename, NULL); @@ -1083,32 +1127,32 @@ } /* build image from file, if file exists */ if(g_file_test(path, G_FILE_TEST_EXISTS)) { - gchar *data = NULL; + gchar *imgdata = NULL; size_t len; GError *err = NULL; - if (!g_file_get_contents(path, &data, &len, &err)) { + if (!g_file_get_contents(path, &imgdata, &len, &err)) { twitter_debug("Error reading %s: %s\n", path, err->message); g_error_free(err); } - icon_id = purple_imgstore_add_with_id(data, len, path); - - g_hash_table_insert(icon_id_by_user, g_strdup(user_name), GINT_TO_POINTER(icon_id)); + data->icon_id = purple_imgstore_add_with_id(imgdata, len, path); g_free(filename); g_free(path); + twitter_debug("icon data has been loaded from file\n"); + insert_requested_icon(user_name); return; } /* Return if user's icon has been requested already. */ - if(g_list_find_custom(requested_users, user_name, (GCompareFunc)strcmp)) { + if(data->requested) return; - } + else + data->requested = TRUE; - requested_users = g_list_append(requested_users, g_strdup(user_name)); /* Create the URL of the user's icon. * See http://twitter.g.hatena.ne.jp/ikko615/20080107/1199703400 @@ -1116,10 +1160,9 @@ url = g_strdup_printf("http://img.twitty.jp/twitter/user/%s/m.gif", user_name); - fetch_data = purple_util_fetch_url(url, TRUE, NULL, TRUE, + data->fetch_data = purple_util_fetch_url(url, TRUE, NULL, TRUE, got_icon_cb, g_strdup(user_name)); - g_free(url); - requestings = g_list_append(requestings, fetch_data); + g_free(url); url = NULL; twitter_debug("request %s's icon\n", user_name); } @@ -1127,11 +1170,18 @@ static void mark_icon_for_user(GtkTextMark *mark, const gchar *user_name) { - GList *mark_list = g_hash_table_lookup(icon_mark_list_by_user, user_name); + icon_data *data = NULL; + data = (icon_data *)g_hash_table_lookup(icon_data_by_user, user_name); + if(!data) { + data = g_new0(icon_data, 1); + g_hash_table_insert(icon_data_by_user, + g_strdup(user_name), data); + } + + GList *mark_list = data->request_list; mark_list = g_list_append(mark_list, mark); - g_hash_table_replace(icon_mark_list_by_user, - g_strdup(user_name), mark_list); + data->request_list = mark_list; } static void @@ -1170,12 +1220,17 @@ gtk_text_buffer_get_iter_at_line(text_buffer, &inserting_point, gtk_text_buffer_get_line_count(text_buffer) - 1); - icon_id = GPOINTER_TO_INT(g_hash_table_lookup(icon_id_by_user, user_name)); + icon_data *data = NULL; + data = g_hash_table_lookup(icon_data_by_user, user_name); + if(data) + icon_id = data->icon_id; + else + icon_id = 0; /* If the user's icon has not been downloaded, * mark the message and request the icon. */ if(!icon_id) { - twitter_debug("%s's icon has not been downloaded.\n", user_name); + twitter_debug("%s's icon is not in memory.\n", user_name); mark_icon_for_user(gtk_text_buffer_create_mark( text_buffer, NULL, &inserting_point, FALSE), user_name); @@ -1219,13 +1274,8 @@ regp[USER_FIRST_LINE] = g_regex_new(P_USER_FIRST_LINE, 0, 0, NULL); regp[USER_FORMATTED] = g_regex_new(P_USER_FORMATTED, G_REGEX_RAW, 0, NULL); - /* hash table for user's icons */ - icon_id_by_user = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, NULL); - - /* hash table for a list of marks that are alternative icons */ - icon_mark_list_by_user = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, NULL); + icon_data_by_user = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); /* attach counter to the existing twitter window */ if(purple_prefs_get_bool(OPT_COUNTER)) { @@ -1235,11 +1285,29 @@ return TRUE; } +static void +cancel_fetch_func(gpointer key, gpointer value, gpointer user_data) +{ + icon_data *data = (icon_data *)value; + + if(!data) + return; + + if(data->fetch_data) { + purple_util_fetch_url_cancel(data->fetch_data); + data->fetch_data = NULL; + } + + if(data->request_list) { + twitter_debug("somehow, request_list != NULL\n"); + } + + return; +} + static gboolean unload_plugin(PurplePlugin *plugin) { - GList *list, *current; - twitter_debug("called\n"); /* disconnect from signal */ @@ -1271,45 +1339,14 @@ g_regex_unref(regp[USER_FIRST_LINE]); g_regex_unref(regp[USER_FORMATTED]); - /* remove the hash table of marks that are alternative icons */ - list = g_hash_table_get_values(icon_mark_list_by_user); - for(current = g_list_first(list); current; - current = g_list_next(current)) { - GList *mark_list = current->data; - GList *current_mark_list; - - /* remove the marks in its GtkTextBuffers */ - for(current_mark_list = g_list_first(mark_list); current_mark_list; - current_mark_list = g_list_next(current_mark_list)) { - GtkTextMark *current_mark = current_mark_list->data; - GtkTextBuffer *text_buffer = gtk_text_mark_get_buffer( - current_mark); - - if(text_buffer) { - /* the mark will be freed in this function */ - gtk_text_buffer_delete_mark(text_buffer, current_mark); - } - } - - g_list_free(mark_list); - } - g_list_free(list); - g_hash_table_destroy(icon_mark_list_by_user); + /* remove mark list in each hash entry */ + g_hash_table_foreach(icon_data_by_user, (GHFunc)remove_marks_func, NULL); /* cancel request that has not been finished yet */ - for(list = g_list_first(requestings); list; list = g_list_next(list)) { - purple_util_fetch_url_cancel(list->data); - } - g_list_free(requestings); - requestings = NULL; + g_hash_table_foreach(icon_data_by_user, (GHFunc)cancel_fetch_func, NULL); - /* destroy hash table for icons */ - g_hash_table_destroy(icon_id_by_user); - for(list = g_list_first(requested_users); list; list = g_list_next(list)) { - g_free(list->data); - } - g_list_free(requested_users); - requested_users = NULL; + /* destroy hash table for icon_data */ + g_hash_table_destroy(icon_data_by_user); //XXX all memory freed? --yaz /* detach from twitter window */ detach_from_window(); diff -r a4a6c7b204c9 -r e0bf37c105eb pidgin-twitter.h --- a/pidgin-twitter.h Mon Jun 30 11:02:01 2008 +0900 +++ b/pidgin-twitter.h Tue Jul 01 18:20:48 2008 +0900 @@ -99,5 +99,7 @@ static void counter_prefs_cb(const char *name, PurplePrefType type, gconstpointer val, gpointer data); static PurplePluginPrefFrame *get_plugin_pref_frame(PurplePlugin *plugin); static void init_plugin(PurplePlugin *plugin); +static void remove_marks_func(gpointer key, gpointer value, gpointer user_data); +static void cancel_fetch_func(gpointer key, gpointer value, gpointer user_data); #endif