diff options
Diffstat (limited to 'libempathy-gtk')
-rw-r--r-- | libempathy-gtk/empathy-chat-text-view.c | 155 | ||||
-rw-r--r-- | libempathy-gtk/empathy-chat.c | 23 | ||||
-rw-r--r-- | libempathy-gtk/empathy-chat.h | 3 | ||||
-rw-r--r-- | libempathy-gtk/empathy-smiley-manager.c | 158 | ||||
-rw-r--r-- | libempathy-gtk/empathy-smiley-manager.h | 15 | ||||
-rw-r--r-- | libempathy-gtk/empathy-theme-adium.c | 277 | ||||
-rw-r--r-- | libempathy-gtk/empathy-ui-utils.c | 122 | ||||
-rw-r--r-- | libempathy-gtk/empathy-ui-utils.h | 45 |
8 files changed, 513 insertions, 285 deletions
diff --git a/libempathy-gtk/empathy-chat-text-view.c b/libempathy-gtk/empathy-chat-text-view.c index de777f2fb..4f38865f6 100644 --- a/libempathy-gtk/empathy-chat-text-view.c +++ b/libempathy-gtk/empathy-chat-text-view.c @@ -73,6 +73,8 @@ typedef struct { static void chat_text_view_iface_init (EmpathyChatViewIface *iface); +static void chat_text_view_copy_clipboard (EmpathyChatView *view); + G_DEFINE_TYPE_WITH_CODE (EmpathyChatTextView, empathy_chat_text_view, GTK_TYPE_TEXT_VIEW, G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CHAT_VIEW, @@ -580,10 +582,17 @@ chat_text_view_finalize (GObject *object) } static void +text_view_copy_clipboard (GtkTextView *text_view) +{ + chat_text_view_copy_clipboard (EMPATHY_CHAT_VIEW (text_view)); +} + +static void empathy_chat_text_view_class_init (EmpathyChatTextViewClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS (klass); object_class->finalize = chat_text_view_finalize; object_class->get_property = chat_text_view_get_property; @@ -592,6 +601,8 @@ empathy_chat_text_view_class_init (EmpathyChatTextViewClass *klass) widget_class->size_allocate = chat_text_view_size_allocate; widget_class->drag_motion = chat_text_view_drag_motion; + text_view_class->copy_clipboard = text_view_copy_clipboard; + g_object_class_install_property (object_class, PROP_LAST_CONTACT, g_param_spec_object ("last-contact", @@ -1256,109 +1267,99 @@ empathy_chat_text_view_set_only_if_date (EmpathyChatTextView *view, } static void -chat_text_view_insert_text_with_emoticons (EmpathyChatTextView *view, - GtkTextIter *iter, - const gchar *str) +chat_text_view_replace_link (const gchar *text, + gssize len, + gpointer match_data, + gpointer user_data) { - EmpathyChatTextViewPriv *priv = GET_PRIV (view); - gboolean use_smileys = FALSE; - GSList *smileys, *l; + GtkTextBuffer *buffer = GTK_TEXT_BUFFER (user_data); + GtkTextIter iter; - empathy_conf_get_bool (empathy_conf_get (), - EMPATHY_PREFS_CHAT_SHOW_SMILEYS, - &use_smileys); + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, + text, len, + EMPATHY_CHAT_TEXT_VIEW_TAG_LINK, + NULL); +} - if (!use_smileys) { - gtk_text_buffer_insert (priv->buffer, iter, str, -1); - return; - } +static void +chat_text_view_replace_smiley (const gchar *text, + gssize len, + gpointer match_data, + gpointer user_data) +{ + EmpathySmileyHit *hit = match_data; + GtkTextBuffer *buffer = GTK_TEXT_BUFFER (user_data); + GtkTextIter iter; - smileys = empathy_smiley_manager_parse (priv->smiley_manager, str); - for (l = smileys; l; l = l->next) { - EmpathySmiley *smiley; + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert_pixbuf (buffer, &iter, hit->pixbuf); +} - smiley = l->data; - if (smiley->pixbuf) { - gtk_text_buffer_insert_pixbuf (priv->buffer, iter, smiley->pixbuf); - } else { - gtk_text_buffer_insert (priv->buffer, iter, smiley->str, -1); - } - empathy_smiley_free (smiley); - } - g_slist_free (smileys); +static void +chat_text_view_replace_verbatim (const gchar *text, + gssize len, + gpointer match_data, + gpointer user_data) +{ + GtkTextBuffer *buffer = GTK_TEXT_BUFFER (user_data); + GtkTextIter iter; + + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert (buffer, &iter, text, len); } +static EmpathyStringParser string_parsers[] = { + {empathy_string_match_link, chat_text_view_replace_link}, + {empathy_string_match_all, chat_text_view_replace_verbatim}, + {NULL, NULL} +}; + +static EmpathyStringParser string_parsers_with_smiley[] = { + {empathy_string_match_link, chat_text_view_replace_link}, + {empathy_string_match_smiley, chat_text_view_replace_smiley}, + {empathy_string_match_all, chat_text_view_replace_verbatim}, + {NULL, NULL} +}; + void empathy_chat_text_view_append_body (EmpathyChatTextView *view, const gchar *body, const gchar *tag) { EmpathyChatTextViewPriv *priv = GET_PRIV (view); - GtkTextIter start_iter, end_iter; - GtkTextMark *mark; + EmpathyStringParser *parsers; + gboolean use_smileys; + GtkTextIter start_iter; GtkTextIter iter; - GRegex *uri_regex; - GMatchInfo *match_info; - gboolean match; - gint last = 0; - gint s = 0, e = 0; - gchar *tmp; + GtkTextMark *mark; - priv = GET_PRIV (view); + /* Check if we have to parse smileys */ + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_CHAT_SHOW_SMILEYS, + &use_smileys); + if (use_smileys) + parsers = string_parsers_with_smiley; + else + parsers = string_parsers; + /* Create a mark at the place we'll start inserting */ gtk_text_buffer_get_end_iter (priv->buffer, &start_iter); mark = gtk_text_buffer_create_mark (priv->buffer, NULL, &start_iter, TRUE); - uri_regex = empathy_uri_regex_dup_singleton (); - for (match = g_regex_match (uri_regex, body, 0, &match_info); match; - match = g_match_info_next (match_info, NULL)) { - if (!g_match_info_fetch_pos (match_info, 0, &s, &e)) - continue; - - if (s > last) { - tmp = empathy_substring (body, last, s); - - gtk_text_buffer_get_end_iter (priv->buffer, &iter); - chat_text_view_insert_text_with_emoticons (view, - &iter, - tmp); - g_free (tmp); - } - - tmp = empathy_substring (body, s, e); - - gtk_text_buffer_get_end_iter (priv->buffer, &iter); - gtk_text_buffer_insert_with_tags_by_name (priv->buffer, - &iter, - tmp, - -1, - EMPATHY_CHAT_TEXT_VIEW_TAG_LINK, - NULL); - - g_free (tmp); - last = e; - } - g_match_info_free (match_info); - g_regex_unref (uri_regex); - - if (last < (gint) strlen (body)) { - gtk_text_buffer_get_end_iter (priv->buffer, &iter); - chat_text_view_insert_text_with_emoticons (view, - &iter, - body + last); - } + /* Parse text for links/smileys and insert in the buffer */ + empathy_string_parser_substr (body, -1, parsers, priv->buffer); + /* Insert a newline after the text inserted */ gtk_text_buffer_get_end_iter (priv->buffer, &iter); gtk_text_buffer_insert (priv->buffer, &iter, "\n", 1); /* Apply the style to the inserted text. */ gtk_text_buffer_get_iter_at_mark (priv->buffer, &start_iter, mark); - gtk_text_buffer_get_end_iter (priv->buffer, &end_iter); - - gtk_text_buffer_apply_tag_by_name (priv->buffer, - tag, + gtk_text_buffer_get_end_iter (priv->buffer, &iter); + gtk_text_buffer_apply_tag_by_name (priv->buffer, tag, &start_iter, - &end_iter); + &iter); gtk_text_buffer_delete_mark (priv->buffer, mark); } diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c index 52806e07c..9d8b89c26 100644 --- a/libempathy-gtk/empathy-chat.c +++ b/libempathy-gtk/empathy-chat.c @@ -93,6 +93,8 @@ typedef struct { GtkWidget *label_topic; GtkWidget *contact_list_view; GtkWidget *info_bar_vbox; + + guint unread_messages; } EmpathyChatPriv; typedef struct { @@ -1074,6 +1076,7 @@ chat_message_received (EmpathyChat *chat, EmpathyMessage *message) TP_CHANNEL_CHAT_STATE_ACTIVE, chat); + priv->unread_messages++; g_signal_emit (chat, signals[NEW_MESSAGE], 0, message); } @@ -2807,3 +2810,23 @@ empathy_chat_is_room (EmpathyChat *chat) return (priv->handle_type == TP_HANDLE_TYPE_ROOM); } +guint +empathy_chat_get_nb_unread_messages (EmpathyChat *self) +{ + EmpathyChatPriv *priv = GET_PRIV (self); + + g_return_val_if_fail (EMPATHY_IS_CHAT (self), FALSE); + + return priv->unread_messages; +} + +/* called when the messages have been read by user */ +void +empathy_chat_messages_read (EmpathyChat *self) +{ + EmpathyChatPriv *priv = GET_PRIV (self); + + g_return_if_fail (EMPATHY_IS_CHAT (self)); + + priv->unread_messages = 0; +} diff --git a/libempathy-gtk/empathy-chat.h b/libempathy-gtk/empathy-chat.h index 9123d11dd..9e0985040 100644 --- a/libempathy-gtk/empathy-chat.h +++ b/libempathy-gtk/empathy-chat.h @@ -84,6 +84,9 @@ void empathy_chat_correct_word (EmpathyChat *chat, gboolean empathy_chat_is_room (EmpathyChat *chat); void empathy_chat_set_show_contacts (EmpathyChat *chat, gboolean show); +guint empathy_chat_get_nb_unread_messages (EmpathyChat *chat); + +void empathy_chat_messages_read (EmpathyChat *self); G_END_DECLS #endif /* __EMPATHY_CHAT_H__ */ diff --git a/libempathy-gtk/empathy-smiley-manager.c b/libempathy-gtk/empathy-smiley-manager.c index cf7a70ada..71cb5065f 100644 --- a/libempathy-gtk/empathy-smiley-manager.c +++ b/libempathy-gtk/empathy-smiley-manager.c @@ -81,32 +81,22 @@ smiley_manager_tree_free (SmileyManagerTree *tree) g_slice_free (SmileyManagerTree, tree); } -/* Note: This function takes the ownership of str */ static EmpathySmiley * -smiley_new (GdkPixbuf *pixbuf, gchar *str, const gchar *path) +smiley_new (GdkPixbuf *pixbuf, const gchar *str) { EmpathySmiley *smiley; smiley = g_slice_new0 (EmpathySmiley); - if (pixbuf) { - smiley->pixbuf = g_object_ref (pixbuf); - } - smiley->str = str; - smiley->path = path; + smiley->pixbuf = g_object_ref (pixbuf); + smiley->str = g_strdup (str); return smiley; } -void -empathy_smiley_free (EmpathySmiley *smiley) +static void +smiley_free (EmpathySmiley *smiley) { - if (!smiley) { - return; - } - - if (smiley->pixbuf) { - g_object_unref (smiley->pixbuf); - } + g_object_unref (smiley->pixbuf); g_free (smiley->str); g_slice_free (EmpathySmiley, smiley); } @@ -115,16 +105,9 @@ static void smiley_manager_finalize (GObject *object) { EmpathySmileyManagerPriv *priv = GET_PRIV (object); - GSList *l; smiley_manager_tree_free (priv->tree); - for (l = priv->smileys; l; l = l->next) { - EmpathySmiley *smiley = l->data; - - /* The smiley got the ownership of the path */ - g_free ((gchar *) smiley->path); - empathy_smiley_free (smiley); - } + g_slist_foreach (priv->smileys, (GFunc) smiley_free, NULL); g_slist_free (priv->smileys); } @@ -247,7 +230,7 @@ smiley_manager_add_valist (EmpathySmileyManager *manager, /* We give the ownership of path to the smiley */ g_object_set_data_full (G_OBJECT (pixbuf), "smiley_str", g_strdup (first_str), g_free); - smiley = smiley_new (pixbuf, g_strdup (first_str), path); + smiley = smiley_new (pixbuf, first_str); priv->smileys = g_slist_prepend (priv->smileys, smiley); } @@ -305,70 +288,119 @@ empathy_smiley_manager_load (EmpathySmileyManager *manager) empathy_smiley_manager_add (manager, "face-worried", ":-S", ":S", ":-s", ":s", NULL); } +static EmpathySmileyHit * +smiley_hit_new (SmileyManagerTree *tree, + guint start, + guint end) +{ + EmpathySmileyHit *hit; + + hit = g_slice_new (EmpathySmileyHit); + hit->pixbuf = tree->pixbuf; + hit->path = tree->path; + hit->start = start; + hit->end = end; + + return hit; +} + +void +empathy_smiley_hit_free (EmpathySmileyHit *hit) +{ + g_return_if_fail (hit != NULL); + + g_slice_free (EmpathySmileyHit, hit); +} + GSList * -empathy_smiley_manager_parse (EmpathySmileyManager *manager, - const gchar *text) +empathy_smiley_manager_parse_len (EmpathySmileyManager *manager, + const gchar *text, + gssize len) { EmpathySmileyManagerPriv *priv = GET_PRIV (manager); - EmpathySmiley *smiley; + EmpathySmileyHit *hit; + GSList *hits = NULL; SmileyManagerTree *cur_tree = priv->tree; - const gchar *t; - const gchar *cur_str = text; - GSList *smileys = NULL; + const gchar *cur_str; + const gchar *start = NULL; g_return_val_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager), NULL); g_return_val_if_fail (text != NULL, NULL); - for (t = text; *t; t = g_utf8_next_char (t)) { + /* If len is negative, parse the string until we find '\0' */ + if (len < 0) { + len = G_MAXSSIZE; + } + + /* Parse the len first bytes of text to find smileys. Each time a smiley + * is detected, append a EmpathySmileyHit struct to the returned list, + * containing the smiley pixbuf and the position of the text to be + * replaced by it. + * cur_str is a pointer in the text showing the current position + * of the parsing. It is always at the begining of an UTF-8 character, + * because we support unicode smileys! For example we could want to + * replace ™ by an image. */ + + for (cur_str = text; + *cur_str != '\0' && cur_str - text < len; + cur_str = g_utf8_next_char (cur_str)) { SmileyManagerTree *child; gunichar c; - c = g_utf8_get_char (t); + c = g_utf8_get_char (cur_str); child = smiley_manager_tree_find_child (cur_tree, c); - if (cur_tree == priv->tree) { - if (child) { - if (t > cur_str) { - smiley = smiley_new (NULL, - g_strndup (cur_str, t - cur_str), - NULL); - smileys = g_slist_prepend (smileys, smiley); - } - cur_str = t; - cur_tree = child; - } - - continue; - } - + /* If we have a child it means c is part of a smiley */ if (child) { + if (cur_tree == priv->tree) { + /* c is the first char of some smileys, keep + * the begining position */ + start = cur_str; + } cur_tree = child; continue; } - smiley = smiley_new (cur_tree->pixbuf, - g_strndup (cur_str, t - cur_str), - cur_tree->path); - smileys = g_slist_prepend (smileys, smiley); - if (cur_tree->pixbuf) { - cur_str = t; - cur_tree = smiley_manager_tree_find_child (priv->tree, c); + /* c is not part of a smiley. let's check if we found a smiley + * before it. */ + if (cur_tree->pixbuf != NULL) { + /* found! */ + hit = smiley_hit_new (cur_tree, start - text, + cur_str - text); + hits = g_slist_prepend (hits, hit); - if (!cur_tree) { + /* c was not part of this smiley, check if a new smiley + * start with it. */ + cur_tree = smiley_manager_tree_find_child (priv->tree, c); + if (cur_tree) { + start = cur_str; + } else { cur_tree = priv->tree; } - } else { - cur_str = t; + } else if (cur_tree != priv->tree) { + /* We searched a smiley starting at 'start' but we ended + * with no smiley. Look again starting from next char. + * + * For example ">:)" and ":(" are both valid smileys, + * when parsing text ">:(" we first see '>' which could + * be the start of a smiley. 'start' variable is set to + * that position and we parse next char which is ':' and + * is still potential smiley. Then we see '(' which is + * NOT part of the smiley, ">:(" does not exist, so we + * have to start again from ':' to find ":(" which is + * correct smiley. */ + cur_str = start; cur_tree = priv->tree; } } - smiley = smiley_new (cur_tree->pixbuf, - g_strndup (cur_str, t - cur_str), - cur_tree->path); - smileys = g_slist_prepend (smileys, smiley); + /* Check if last char of the text was the end of a smiley */ + if (cur_tree->pixbuf != NULL) { + hit = smiley_hit_new (cur_tree, start - text, cur_str - text); + hits = g_slist_prepend (hits, hit); + } - return g_slist_reverse (smileys); + return g_slist_reverse (hits); } GSList * diff --git a/libempathy-gtk/empathy-smiley-manager.h b/libempathy-gtk/empathy-smiley-manager.h index dc7428c3b..1d6eaac54 100644 --- a/libempathy-gtk/empathy-smiley-manager.h +++ b/libempathy-gtk/empathy-smiley-manager.h @@ -50,9 +50,15 @@ struct _EmpathySmileyManagerClass { typedef struct { GdkPixbuf *pixbuf; gchar *str; - const gchar *path; } EmpathySmiley; +typedef struct { + GdkPixbuf *pixbuf; /* Pixbuf of the smiley */ + const gchar *path; /* Filename of the smiley image */ + guint start; /* text[start:end] should be replaced by pixbuf */ + guint end; +} EmpathySmileyHit; + typedef void (*EmpathySmileyMenuFunc) (EmpathySmileyManager *manager, EmpathySmiley *smiley, gpointer user_data); @@ -65,12 +71,13 @@ void empathy_smiley_manager_add (EmpathySmileyManag const gchar *first_str, ...); GSList * empathy_smiley_manager_get_all (EmpathySmileyManager *manager); -GSList * empathy_smiley_manager_parse (EmpathySmileyManager *manager, - const gchar *text); +GSList * empathy_smiley_manager_parse_len (EmpathySmileyManager *manager, + const gchar *text, + gssize len); GtkWidget * empathy_smiley_menu_new (EmpathySmileyManager *manager, EmpathySmileyMenuFunc func, gpointer user_data); -void empathy_smiley_free (EmpathySmiley *smiley); +void empathy_smiley_hit_free (EmpathySmileyHit *hit); G_END_DECLS diff --git a/libempathy-gtk/empathy-theme-adium.c b/libempathy-gtk/empathy-theme-adium.c index a41cbd6c0..0a9fb9830 100644 --- a/libempathy-gtk/empathy-theme-adium.c +++ b/libempathy-gtk/empathy-theme-adium.c @@ -191,122 +191,126 @@ theme_adium_open_address_cb (GtkMenuItem *menuitem, g_free (uri); } -static gchar * -theme_adium_parse_body (EmpathyThemeAdium *theme, - const gchar *text) +static void +theme_adium_match_newline (const gchar *text, + gssize len, + EmpathyStringReplace replace_func, + EmpathyStringParser *sub_parsers, + gpointer user_data) { - EmpathyThemeAdiumPriv *priv = GET_PRIV (theme); - gboolean use_smileys = FALSE; - GSList *smileys, *l; - GString *string; - gint i; - GRegex *uri_regex; - GMatchInfo *match_info; - gboolean match; - gchar *ret = NULL; - gint prev; - - empathy_conf_get_bool (empathy_conf_get (), - EMPATHY_PREFS_CHAT_SHOW_SMILEYS, - &use_smileys); - - /* Add <a href></a> arround links */ - uri_regex = empathy_uri_regex_dup_singleton (); - match = g_regex_match (uri_regex, text, 0, &match_info); - if (match) { - gint last = 0; - gint s = 0, e = 0; - - string = g_string_sized_new (strlen (text)); - do { - gchar *real_url; - - g_match_info_fetch_pos (match_info, 0, &s, &e); - - if (s > last) { - /* Append the text between last link (or the - * start of the message) and this link */ - gchar *str; - str = g_markup_escape_text (text + last, s - last); - g_string_append (string, str); - g_free (str); - } - - /* Append the link inside <a href=""></a> tag */ - real_url = empathy_make_absolute_url_len (text + s, e - s); - - g_string_append (string, "<a href=\""); - g_string_append (string, real_url); - g_string_append (string, "\">"); - g_string_append_len (string, text + s, e - s); - g_string_append (string, "</a>"); - - g_free (real_url); - last = e; - } while (g_match_info_next (match_info, NULL)); - - if (e < (gint) strlen (text)) { - /* Append the text after the last link */ - gchar *str; - str = g_markup_escape_text (text + e, strlen (text) - e); - g_string_append (string, str); - g_free (str); - } + GString *string = user_data; + gint i; + gint prev = 0; - g_free (ret); - text = ret = g_string_free (string, FALSE); - } else if (use_smileys) { - /* Replace smileys by a <img/> tag */ - string = g_string_sized_new (strlen (text)); - smileys = empathy_smiley_manager_parse (priv->smiley_manager, text); - for (l = smileys; l; l = l->next) { - EmpathySmiley *smiley; - - smiley = l->data; - if (smiley->path) { - g_string_append_printf (string, - "<abbr title='%s'><img src=\"%s\"/ alt=\"%s\"/></abbr>", - smiley->str, smiley->path, smiley->str); - } else { - gchar *str; - - str = g_markup_escape_text (smiley->str, -1); - g_string_append (string, str); - g_free (str); - } - empathy_smiley_free (smiley); - } - g_slist_free (smileys); - - g_free (ret); - text = ret = g_string_free (string, FALSE); - } else { - text = ret = g_markup_escape_text (text, -1); + if (len < 0) { + len = G_MAXSSIZE; } - g_match_info_free (match_info); - g_regex_unref (uri_regex); - /* Replace \n by <br/> */ - string = NULL; - prev = 0; - for (i = 0; text[i] != '\0'; i++) { + for (i = 0; i < len && text[i] != '\0'; i++) { if (text[i] == '\n') { - if (!string ) { - string = g_string_sized_new (strlen (text)); - } - g_string_append_len (string, text + prev, i - prev); + empathy_string_parser_substr (text + prev, + i - prev, sub_parsers, + user_data); g_string_append (string, "<br/>"); prev = i + 1; } } - if (string) { - g_string_append (string, text + prev); - g_free (ret); - text = ret = g_string_free (string, FALSE); - } + empathy_string_parser_substr (text + prev, i - prev, + sub_parsers, user_data); +} - return ret; +static void +theme_adium_replace_link (const gchar *text, + gssize len, + gpointer match_data, + gpointer user_data) +{ + GString *string = user_data; + gchar *real_url; + gchar *escaped; + + real_url = empathy_make_absolute_url_len (text, len); + + /* The thing we are making a link of may contain + * characters which need escaping */ + escaped = g_markup_escape_text (text, len); + + /* Append the link inside <a href=""></a> tag */ + g_string_append_printf (string, "<a href=\"%s\">%s</a>", + real_url, escaped); + + g_free (real_url); + g_free (escaped); +} + +static void +theme_adium_replace_smiley (const gchar *text, + gssize len, + gpointer match_data, + gpointer user_data) +{ + EmpathySmileyHit *hit = match_data; + GString *string = user_data; + + /* Replace smiley by a <img/> tag */ + g_string_append_printf (string, + "<img src=\"%s\" alt=\"%.*s\" title=\"%.*s\"/>", + hit->path, (int)len, text, (int)len, text); +} + +static void +theme_adium_replace_escaped (const gchar *text, + gssize len, + gpointer match_data, + gpointer user_data) +{ + GString *string = user_data; + gchar *escaped; + + escaped = g_markup_escape_text (text, len); + g_string_append (string, escaped); + g_free (escaped); +} + +static EmpathyStringParser string_parsers[] = { + {empathy_string_match_link, theme_adium_replace_link}, + {theme_adium_match_newline, NULL}, + {empathy_string_match_all, theme_adium_replace_escaped}, + {NULL, NULL} +}; + +static EmpathyStringParser string_parsers_with_smiley[] = { + {empathy_string_match_link, theme_adium_replace_link}, + {empathy_string_match_smiley, theme_adium_replace_smiley}, + {theme_adium_match_newline, NULL}, + {empathy_string_match_all, theme_adium_replace_escaped}, + {NULL, NULL} +}; + +static gchar * +theme_adium_parse_body (const gchar *text) +{ + EmpathyStringParser *parsers; + GString *string; + gboolean use_smileys; + + /* Check if we have to parse smileys */ + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_CHAT_SHOW_SMILEYS, + &use_smileys); + if (use_smileys) + parsers = string_parsers_with_smiley; + else + parsers = string_parsers; + + /* Parse text and construct string with links and smileys replaced + * by html tags. Also escape text to make sure html code is + * displayed verbatim. */ + string = g_string_sized_new (strlen (text)); + empathy_string_parser_substr (text, -1, parsers, string); + + return g_string_free (string, FALSE); } static void @@ -435,6 +439,28 @@ theme_adium_append_html (EmpathyThemeAdium *theme, } static void +theme_adium_append_event_escaped (EmpathyChatView *view, + const gchar *escaped) +{ + EmpathyThemeAdium *theme = EMPATHY_THEME_ADIUM (view); + EmpathyThemeAdiumPriv *priv = GET_PRIV (theme); + + if (priv->data->status_html) { + theme_adium_append_html (theme, "appendMessage", + priv->data->status_html, + priv->data->status_len, + escaped, NULL, NULL, NULL, NULL, + "event", empathy_time_get_current ()); + } + + /* There is no last contact */ + if (priv->last_contact) { + g_object_unref (priv->last_contact); + priv->last_contact = NULL; + } +} + +static void theme_adium_append_message (EmpathyChatView *view, EmpathyMessage *msg) { @@ -442,7 +468,7 @@ theme_adium_append_message (EmpathyChatView *view, EmpathyThemeAdiumPriv *priv = GET_PRIV (theme); EmpathyContact *sender; TpAccount *account; - gchar *dup_body = NULL; + gchar *body_escaped; const gchar *body; const gchar *name; const gchar *contact_id; @@ -472,10 +498,7 @@ theme_adium_append_message (EmpathyChatView *view, service_name = tp_account_get_protocol (account); timestamp = empathy_message_get_timestamp (msg); body = empathy_message_get_body (msg); - dup_body = theme_adium_parse_body (theme, body); - if (dup_body) { - body = dup_body; - } + body_escaped = theme_adium_parse_body (body); name = empathy_contact_get_name (sender); contact_id = empathy_contact_get_id (sender); @@ -483,10 +506,11 @@ theme_adium_append_message (EmpathyChatView *view, if (empathy_message_get_tptype (msg) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) { gchar *str; - str = g_strdup_printf ("%s %s", name, body); - empathy_chat_view_append_event (view, str); + str = g_strdup_printf ("%s %s", name, body_escaped); + theme_adium_append_event_escaped (view, str); + g_free (str); - g_free (dup_body); + g_free (body_escaped); return; } @@ -607,7 +631,7 @@ theme_adium_append_message (EmpathyChatView *view, } if (html != NULL) { - theme_adium_append_html (theme, func, html, len, body, + theme_adium_append_html (theme, func, html, len, body_escaped, avatar_filename, name, contact_id, service_name, message_classes->str, timestamp); @@ -623,7 +647,7 @@ theme_adium_append_message (EmpathyChatView *view, priv->last_timestamp = timestamp; priv->last_is_backlog = is_backlog; - g_free (dup_body); + g_free (body_escaped); g_string_free (message_classes, TRUE); } @@ -631,26 +655,11 @@ static void theme_adium_append_event (EmpathyChatView *view, const gchar *str) { - EmpathyThemeAdium *theme = EMPATHY_THEME_ADIUM (view); - EmpathyThemeAdiumPriv *priv = GET_PRIV (theme); + gchar *str_escaped; - if (priv->data->status_html) { - gchar *str_escaped; - - str_escaped = g_markup_escape_text (str, -1); - theme_adium_append_html (theme, "appendMessage", - priv->data->status_html, - priv->data->status_len, - str_escaped, NULL, NULL, NULL, NULL, - "event", empathy_time_get_current ()); - g_free (str_escaped); - } - - /* There is no last contact */ - if (priv->last_contact) { - g_object_unref (priv->last_contact); - priv->last_contact = NULL; - } + str_escaped = g_markup_escape_text (str, -1); + theme_adium_append_event_escaped (view, str_escaped); + g_free (str_escaped); } static void diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c index 9f4182ce1..6906d8228 100644 --- a/libempathy-gtk/empathy-ui-utils.c +++ b/libempathy-gtk/empathy-ui-utils.c @@ -40,6 +40,7 @@ #include "empathy-ui-utils.h" #include "empathy-images.h" +#include "empathy-smiley-manager.h" #include "empathy-conf.h" #define DEBUG_FLAG EMPATHY_DEBUG_OTHER @@ -49,13 +50,15 @@ #include <libempathy/empathy-idle.h> #include <libempathy/empathy-ft-factory.h> -#define SCHEMES "(https?|s?ftps?|nntp|news|javascript|about|ghelp|apt|telnet|"\ - "file|webcal|mailto)" -#define BODY "([^\\ \\n\"]+)" -#define END_BODY "([^\\ \\n\"]*[^,;\?><()\\ \"\\.\\n])" -#define URI_REGEX "("SCHEMES"://"END_BODY")" \ - "|((mailto:)?"BODY"@"BODY"\\."END_BODY")"\ - "|((www|ftp)\\."END_BODY")" +#define SCHEMES "([a-zA-Z\\+]+)" +#define INVALID_CHARS " \n\"'" +#define INVALID_CHARS_EXT INVALID_CHARS "\\[\\]<>(){},;:?" +#define BODY "([^"INVALID_CHARS"]+)" +#define BODY_END "([^"INVALID_CHARS"]*)[^"INVALID_CHARS_EXT".]" +#define BODY_STRICT "([^"INVALID_CHARS_EXT"]+)" +#define URI_REGEX "("SCHEMES"://"BODY_END")" \ + "|((www|ftp)\\."BODY_END")" \ + "|((mailto:)?"BODY_STRICT"@"BODY"\\."BODY_END")" void empathy_gtk_init (void) @@ -1609,3 +1612,108 @@ empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler) gtk_widget_show (widget); } + +void +empathy_string_parser_substr (const gchar *text, + gssize len, + EmpathyStringParser *parsers, + gpointer user_data) +{ + if (parsers != NULL && parsers[0].match_func != NULL) { + parsers[0].match_func (text, len, + parsers[0].replace_func, parsers + 1, + user_data); + } +} + +void +empathy_string_match_link (const gchar *text, + gssize len, + EmpathyStringReplace replace_func, + EmpathyStringParser *sub_parsers, + gpointer user_data) +{ + GRegex *uri_regex; + GMatchInfo *match_info; + gboolean match; + gint last = 0; + + uri_regex = empathy_uri_regex_dup_singleton (); + match = g_regex_match_full (uri_regex, text, len, 0, 0, &match_info, NULL); + if (match) { + gint s = 0, e = 0; + + do { + g_match_info_fetch_pos (match_info, 0, &s, &e); + + if (s > last) { + /* Append the text between last link (or the + * start of the message) and this link */ + empathy_string_parser_substr (text + last, + s - last, + sub_parsers, + user_data); + } + + replace_func (text + s, e - s, NULL, user_data); + + last = e; + } while (g_match_info_next (match_info, NULL)); + } + + empathy_string_parser_substr (text + last, len - last, + sub_parsers, user_data); + + g_match_info_free (match_info); + g_regex_unref (uri_regex); +} + +void +empathy_string_match_smiley (const gchar *text, + gssize len, + EmpathyStringReplace replace_func, + EmpathyStringParser *sub_parsers, + gpointer user_data) +{ + guint last = 0; + EmpathySmileyManager *smiley_manager; + GSList *hits, *l; + + smiley_manager = empathy_smiley_manager_dup_singleton (); + hits = empathy_smiley_manager_parse_len (smiley_manager, text, len); + + for (l = hits; l; l = l->next) { + EmpathySmileyHit *hit = l->data; + + if (hit->start > last) { + /* Append the text between last smiley (or the + * start of the message) and this smiley */ + empathy_string_parser_substr (text + last, + hit->start - last, + sub_parsers, user_data); + } + + replace_func (text + hit->start, hit->end - hit->start, + hit, user_data); + + last = hit->end; + + empathy_smiley_hit_free (hit); + } + g_slist_free (hits); + g_object_unref (smiley_manager); + + empathy_string_parser_substr (text + last, len - last, + sub_parsers, user_data); +} + +void +empathy_string_match_all (const gchar *text, + gssize len, + EmpathyStringReplace replace_func, + EmpathyStringParser *sub_parsers, + gpointer user_data) +{ + replace_func (text, len, NULL, user_data); +} + diff --git a/libempathy-gtk/empathy-ui-utils.h b/libempathy-gtk/empathy-ui-utils.h index e0c0904b0..0eacd49b8 100644 --- a/libempathy-gtk/empathy-ui-utils.h +++ b/libempathy-gtk/empathy-ui-utils.h @@ -121,6 +121,51 @@ gchar * empathy_make_absolute_url (const gchar *url); gchar * empathy_make_absolute_url_len (const gchar *url, guint len); +/* String parser */ +typedef struct _EmpathyStringParser EmpathyStringParser; + +typedef void (*EmpathyStringReplace) (const gchar *text, + gssize len, + gpointer match_data, + gpointer user_data); +typedef void (*EmpathyStringMatch) (const gchar *text, + gssize len, + EmpathyStringReplace replace_func, + EmpathyStringParser *sub_parsers, + gpointer user_data); + +struct _EmpathyStringParser { + EmpathyStringMatch match_func; + EmpathyStringReplace replace_func; +}; + +void +empathy_string_parser_substr (const gchar *text, + gssize len, + EmpathyStringParser *parsers, + gpointer user_data); + +void +empathy_string_match_link (const gchar *text, + gssize len, + EmpathyStringReplace replace_func, + EmpathyStringParser *sub_parsers, + gpointer user_data); + +void +empathy_string_match_smiley (const gchar *text, + gssize len, + EmpathyStringReplace replace_func, + EmpathyStringParser *sub_parsers, + gpointer user_data); + +void +empathy_string_match_all (const gchar *text, + gssize len, + EmpathyStringReplace replace_func, + EmpathyStringParser *sub_parsers, + gpointer user_data); + G_END_DECLS #endif /* __EMPATHY_UI_UTILS_H__ */ |