From 3715308dedf76485c5901709a8748bc5d42289be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Sun, 4 Oct 2015 19:48:00 +0200 Subject: [PATCH] Use regex to detect hypertext links in the chat history That fix performance issue when diplaying comlex messages in the chat history as XML text or base64-encoded binary message --- gtk/chat.c | 63 ++++++++++++++++++++------------------------------ gtk/linphone.h | 6 +++++ gtk/main.c | 21 +++++++++++++++++ 3 files changed, 52 insertions(+), 38 deletions(-) diff --git a/gtk/chat.c b/gtk/chat.c index dba341bbc..bd878e897 100644 --- a/gtk/chat.c +++ b/gtk/chat.c @@ -139,35 +139,13 @@ static gboolean scroll_to_end(GtkTextView *w){ return FALSE; } -static gboolean word_starts_with(const GtkTextIter *word_start, const char *prefix) { - gboolean res; - gchar *schema = NULL; - GtkTextIter end = *word_start; - gtk_text_iter_forward_chars(&end, strlen(prefix)); - schema = gtk_text_iter_get_slice(word_start, &end); - res = ( g_strcmp0(schema, prefix) == 0 ); - g_free(schema); - return res; -} - -static gboolean is_space(gunichar ch, gpointer user_data) { - return g_unichar_isspace(ch); -} - -static void insert_link_tags(GtkTextBuffer *buffer, const GtkTextIter *begin, const GtkTextIter *end) { - GtkTextIter iter = *begin; - while(gtk_text_iter_compare(&iter, end) < 0) { - if(gtk_text_iter_starts_word(&iter) && ( - word_starts_with(&iter, "http://") || - word_starts_with(&iter, "https://") || - word_starts_with(&iter, "ftp://") || - word_starts_with(&iter, "ftps://"))) { - GtkTextIter uri_begin = iter; - if(gtk_text_iter_forward_find_char(&iter, is_space, NULL, end)) { - gtk_text_buffer_apply_tag_by_name(buffer, "link", &uri_begin, &iter); - } - } - gtk_text_iter_forward_char(&iter); +static void write_body(GtkTextBuffer *buffer, GtkTextIter *iter, const gchar *text, gint len, gboolean is_me, gboolean is_link) { + const char *me_tag_name = is_me ? "me" : NULL; + const char *link_tag_name = is_link ? "link" : NULL; + if(me_tag_name) { + gtk_text_buffer_insert_with_tags_by_name(buffer, iter, text, len, "body", me_tag_name, link_tag_name, NULL); + } else { + gtk_text_buffer_insert_with_tags_by_name(buffer, iter, text, len, "body", link_tag_name, NULL); } } @@ -175,11 +153,13 @@ void linphone_gtk_push_text(GtkWidget *w, const LinphoneAddress *from, gboolean me,LinphoneChatRoom *cr,LinphoneChatMessage *msg, gboolean hist){ GtkTextView *text=GTK_TEXT_VIEW(linphone_gtk_get_widget(w,"textview")); GtkTextBuffer *buffer=gtk_text_view_get_buffer(text); - GtkTextIter iter, link_start; - GtkTextMark *link_start_mark = NULL; + GtkTextIter iter; char *from_str=linphone_address_as_string_uri_only(from); gchar *from_message=(gchar *)g_object_get_data(G_OBJECT(w),"from_message"); GHashTable *table=(GHashTable*)g_object_get_data(G_OBJECT(w),"table"); + const GRegex *uri_regex = linphone_gtk_get_uri_regex(); + GMatchInfo *match_info = NULL; + const char *message = linphone_chat_message_get_text(msg); time_t t; char buf[80]; time_t tnow; @@ -199,14 +179,21 @@ void linphone_gtk_push_text(GtkWidget *w, const LinphoneAddress *from, } ms_free(from_str); - link_start_mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, TRUE); - gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, linphone_chat_message_get_text(msg), -1, - "body", me ? "me" : NULL, NULL); + // Inserts message body and tags URIs as hypertext links + if(g_regex_match(uri_regex, message, 0, &match_info)) { + int pos = 0, start, end; + do { + g_match_info_fetch_pos(match_info, 0, &start, &end); + if(pos < start) write_body(buffer, &iter, &message[pos], start-pos, me, FALSE); + write_body(buffer, &iter, &message[start], end-start, me, TRUE); + pos = end; + } while(g_match_info_next(match_info, NULL)); + } else { + write_body(buffer, &iter, message, -1, me, FALSE); + } gtk_text_buffer_insert(buffer,&iter,"\n",-1); - gtk_text_buffer_get_iter_at_mark(buffer, &link_start, link_start_mark); - insert_link_tags(buffer, &link_start, &iter); - gtk_text_buffer_delete_mark(buffer, link_start_mark); - + g_match_info_free(match_info); + t=linphone_chat_message_get_time(msg); switch (linphone_chat_message_get_state (msg)){ case LinphoneChatMessageStateInProgress: diff --git a/gtk/linphone.h b/gtk/linphone.h index 04c587a0d..f7cce8407 100644 --- a/gtk/linphone.h +++ b/gtk/linphone.h @@ -341,3 +341,9 @@ LINPHONE_PUBLIC void linphone_gtk_tunnel_ok(GtkButton *button); LINPHONE_PUBLIC void linphone_gtk_notebook_current_page_changed(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data); LINPHONE_PUBLIC void linphone_gtk_reload_sound_devices(void); LINPHONE_PUBLIC void linphone_gtk_reload_video_devices(void); + +/** + * Provide a regex to match URIs + * @return A singleton regex object + */ +const GRegex *linphone_gtk_get_uri_regex(void); diff --git a/gtk/main.c b/gtk/main.c index 1930f8b89..f5fcfed1b 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -63,6 +63,7 @@ const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION; static LinphoneCore *the_core=NULL; static GtkWidget *the_ui=NULL; static LinphoneLDAPContactProvider* ldap_provider = NULL; +static GRegex *uri_regex = NULL; static void linphone_gtk_global_state_changed(LinphoneCore *lc, LinphoneGlobalState state, const char*str); static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg); @@ -2025,6 +2026,26 @@ static void populate_xdg_data_dirs_envvar(void) { #endif } +static void free_uri_regex(void) { + if(uri_regex) g_regex_unref(uri_regex); +} + +const GRegex *linphone_gtk_get_uri_regex(void) { + const gchar *pattern = "\\b[a-z0-9]+://[\\S]+\\b"; + GError *error = NULL; + if(uri_regex == NULL) { + uri_regex = g_regex_new(pattern, G_REGEX_OPTIMIZE, 0, &error); + if(error) { + g_warning("Could not parse regex pattern for URIs: %s", error->message); + g_error_free(error); + uri_regex = NULL; + return NULL; + } + atexit(free_uri_regex); + } + return uri_regex; +} + int main(int argc, char *argv[]){ char *config_file; const char *factory_config_file;