diff --git a/src/embeddedvideo.c b/src/embeddedvideo.c index d5571b8..2c7f1ed 100644 --- a/src/embeddedvideo.c +++ b/src/embeddedvideo.c @@ -17,7 +17,8 @@ #include static GHashTable *ht_buttons = NULL; /* */ -static GHashTable *ht_signal_handlers = NULL; /* */ +static GHashTable *ht_signal_handlers_it = NULL; /* */ +static GHashTable *ht_signal_handlers_eua = NULL; /* */ static void insert_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *location, @@ -37,9 +38,6 @@ insert_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *location, GtkWidget *button = videoframes_insert_new_button(imhtml, location, info, text, len); g_hash_table_insert(ht_buttons, button, imhtml); - if (purple_prefs_get_bool("/plugins/gtk/embeddedvideo/show-video")) - videoframes_toggle_button(button); - gtk_text_buffer_get_end_iter(imhtml->text_buffer, location); } @@ -54,9 +52,13 @@ attach_to_conversation(gpointer data, gpointer user_data) GtkIMHtml *imhtml = GTK_IMHTML(gtkconv->imhtml); g_assert(GTK_IS_IMHTML(imhtml)); - gulong handler_id = g_signal_connect_after(G_OBJECT(imhtml->text_buffer), + gulong it_handler_id = g_signal_connect_after(G_OBJECT(imhtml->text_buffer), "insert-text", G_CALLBACK(insert_text_cb), imhtml); - g_hash_table_insert(ht_signal_handlers, imhtml->text_buffer, (gpointer) handler_id); + g_hash_table_insert(ht_signal_handlers_it, imhtml->text_buffer, (gpointer) it_handler_id); + + gulong eua_handler_id = g_signal_connect(G_OBJECT(imhtml->text_buffer), + "end-user-action", G_CALLBACK(videoframes_text_buffer_end_user_action_cb), NULL); + g_hash_table_insert(ht_signal_handlers_eua, imhtml->text_buffer, (gpointer) eua_handler_id); } static void @@ -68,10 +70,15 @@ detach_from_conversation(gpointer data, gpointer user_data) GtkIMHtml *imhtml = GTK_IMHTML(gtkconv->imhtml); g_assert(GTK_IS_IMHTML(imhtml)); - gulong handler_id = (gulong) g_hash_table_lookup(ht_signal_handlers, + gulong it_handler_id = (gulong) g_hash_table_lookup(ht_signal_handlers_it, + imhtml->text_buffer); + g_signal_handler_disconnect(imhtml->text_buffer, it_handler_id); + g_hash_table_remove(ht_signal_handlers_it, imhtml->text_buffer); + + gulong eua_handler_id = (gulong) g_hash_table_lookup(ht_signal_handlers_eua, imhtml->text_buffer); - g_signal_handler_disconnect(imhtml->text_buffer, handler_id); - g_hash_table_remove(ht_signal_handlers, imhtml->text_buffer); + g_signal_handler_disconnect(imhtml->text_buffer, eua_handler_id); + g_hash_table_remove(ht_signal_handlers_eua, imhtml->text_buffer); } static void @@ -109,8 +116,9 @@ plugin_load(PurplePlugin *plugin) ht_buttons = g_hash_table_new_full(g_direct_hash, g_direct_equal, (GDestroyNotify) videoframes_remove_button, NULL); - /* Create the hash table for signal handlers. */ - ht_signal_handlers = g_hash_table_new(g_direct_hash, g_direct_equal); + /* Create hash tables for signal handlers. */ + ht_signal_handlers_it = g_hash_table_new(g_direct_hash, g_direct_equal); + ht_signal_handlers_eua = g_hash_table_new(g_direct_hash, g_direct_equal); /* Attach to current conversations. */ g_list_foreach(purple_get_conversations(), attach_to_conversation, NULL); @@ -130,16 +138,17 @@ plugin_unload(PurplePlugin *plugin) { /* Disconnect signals for future conversations. */ void *conv_handle = purple_conversations_get_handle(); - purple_signal_disconnect(conv_handle, "conversation-created", plugin, - PURPLE_CALLBACK(conversation_created_cb)); purple_signal_disconnect(conv_handle, "deleting-conversation", plugin, PURPLE_CALLBACK(deleting_conversation_cb)); + purple_signal_disconnect(conv_handle, "conversation-created", plugin, + PURPLE_CALLBACK(conversation_created_cb)); /* Detach from current conversations. */ g_list_foreach(purple_get_conversations(), detach_from_conversation, NULL); - /* Destroy the hash table for signal handlers. */ - g_hash_table_destroy(ht_signal_handlers); + /* Destroy hash tables for signal handlers. */ + g_hash_table_destroy(ht_signal_handlers_eua); + g_hash_table_destroy(ht_signal_handlers_it); /* Remove all the inserted buttons and destroy the hash table. Every button will automatically remove its video frame if it has one. */ diff --git a/src/videoframes.c b/src/videoframes.c index 721b0e8..6e60295 100644 --- a/src/videoframes.c +++ b/src/videoframes.c @@ -35,6 +35,8 @@ button_info_new(GtkIMHtml *imhtml, GtkTextIter *location, location, TRUE); info->website = website; info->url = g_string_new_len(text, len); + info->iter = location; + info->insert_newline = -1; return info; } @@ -60,6 +62,44 @@ videoframes_destroy() g_hash_table_destroy(ht_button_info); } +void +videoframes_text_buffer_check_newline(gpointer key, gpointer values, gpointer user_data) +{ + GtkWidget *button = (GtkWidget *) key; + ButtonInfo *button_info = (ButtonInfo *) values; + + if (button_info->insert_newline != -1) + return ; + + GtkTextIter iter; + + gtk_text_buffer_get_iter_at_mark(button_info->imhtml->text_buffer, + &iter, button_info->mark); + button_info->insert_newline = 0; + + while (gtk_text_iter_forward_char(&iter)) { + gunichar crt_unichar = gtk_text_iter_get_char(&iter); + + if (crt_unichar == G_UNICODE_BREAK_LINE_FEED) + break ; + if (g_unichar_isgraph(crt_unichar)) { + button_info->insert_newline = 1; + break ; + } + } + + if (purple_prefs_get_bool("/plugins/gtk/embeddedvideo/show-video")) { + videoframes_toggle_button(button); + gtk_text_buffer_get_end_iter(button_info->imhtml->text_buffer, button_info->iter); + } +} + +void +videoframes_text_buffer_end_user_action_cb(GtkTextBuffer* text_buffer, gpointer user_data) +{ + g_hash_table_foreach(ht_button_info, videoframes_text_buffer_check_newline, NULL); +} + GtkWidget * videoframes_insert_new_button(GtkIMHtml *imhtml, GtkTextIter *location, WebsiteInfo *website, gchar *text, gint len) @@ -155,11 +195,15 @@ videoframes_toggle_button_cb(GtkWidget *button) gtk_widget_show_all(web_view); /* Insert the web view into the conversation. */ + /* FIXME: Is this "\n" enough? What about those who use unicode chars? + What could happen on Windows without "\r"? + */ gtk_text_buffer_insert(info->imhtml->text_buffer, &iter, "\n", 1); GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor( info->imhtml->text_buffer, &iter); gtk_text_view_add_child_at_anchor(&info->imhtml->text_view, web_view, anchor); - gtk_text_buffer_insert(info->imhtml->text_buffer, &iter, "\n", 1); + if (info->insert_newline == 1) + gtk_text_buffer_insert(info->imhtml->text_buffer, &iter, "\n", 1); } else { @@ -169,7 +213,7 @@ videoframes_toggle_button_cb(GtkWidget *button) /* Remove the video from the conversation. The web view is implicitly destroyed. */ GtkTextIter next_iter = iter; - gtk_text_iter_forward_chars(&next_iter, 3); + gtk_text_iter_forward_chars(&next_iter, 2 + (info->insert_newline)); gtk_text_buffer_delete(info->imhtml->text_buffer, &iter, &next_iter); } diff --git a/src/videoframes.h b/src/videoframes.h index 2fcaaec..1b3f101 100644 --- a/src/videoframes.h +++ b/src/videoframes.h @@ -17,6 +17,8 @@ struct _ButtonInfo GtkTextMark *mark; WebsiteInfo *website; GString *url; + GtkTextIter *iter; + gint insert_newline; }; ButtonInfo * button_info_new(GtkIMHtml *, GtkTextIter *, @@ -25,6 +27,8 @@ void button_info_free(ButtonInfo *); void videoframes_init(); void videoframes_destroy(); +void videoframes_text_buffer_check_new_line(gpointer, gpointer, gpointer); +void videoframes_text_buffer_end_user_action_cb(GtkTextBuffer *, gpointer); GtkWidget * videoframes_insert_new_button(GtkIMHtml *, GtkTextIter *, WebsiteInfo *, gchar *, gint); void videoframes_remove_button(GtkWidget *);