aboutsummaryrefslogtreecommitdiffstats
path: root/libempathy-gtk/empathy-chat.c
diff options
context:
space:
mode:
authorMike Ruprecht <mike.ruprecht@collabora.co.uk>2010-05-13 16:23:00 +0800
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2010-05-25 17:37:08 +0800
commit397550f132803b8ff16aeb8fd402fba1d9b01fbd (patch)
treeb124c7e472ed6188a4fc254c62de3e92851aff47 /libempathy-gtk/empathy-chat.c
parentb8bd45099ed48decb876233cab1f2df217abc323 (diff)
downloadgsoc2013-empathy-397550f132803b8ff16aeb8fd402fba1d9b01fbd.tar
gsoc2013-empathy-397550f132803b8ff16aeb8fd402fba1d9b01fbd.tar.gz
gsoc2013-empathy-397550f132803b8ff16aeb8fd402fba1d9b01fbd.tar.bz2
gsoc2013-empathy-397550f132803b8ff16aeb8fd402fba1d9b01fbd.tar.lz
gsoc2013-empathy-397550f132803b8ff16aeb8fd402fba1d9b01fbd.tar.xz
gsoc2013-empathy-397550f132803b8ff16aeb8fd402fba1d9b01fbd.tar.zst
gsoc2013-empathy-397550f132803b8ff16aeb8fd402fba1d9b01fbd.zip
Fix and improve spell-checking (#604159)
Diffstat (limited to 'libempathy-gtk/empathy-chat.c')
-rw-r--r--libempathy-gtk/empathy-chat.c309
1 files changed, 256 insertions, 53 deletions
diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c
index 57cf62632..de36d6921 100644
--- a/libempathy-gtk/empathy-chat.c
+++ b/libempathy-gtk/empathy-chat.c
@@ -94,6 +94,20 @@ typedef struct {
gboolean has_input_vscroll;
gint topic_width;
+ /* TRUE if spell checking is enabled, FALSE otherwise.
+ * This is to keep track of the last state of spell checking
+ * when it changes. */
+ gboolean spell_checking_enabled;
+
+ /* These store the signal handler ids for the enclosed text entry. */
+ gulong insert_text_id;
+ gulong delete_range_id;
+ gulong notify_cursor_position_id;
+
+ /* This stores the id for the spell checking configuration setting
+ * notification signal handler. */
+ guint conf_notify_id;
+
GtkWidget *widget;
GtkWidget *hpaned;
GtkWidget *vbox_left;
@@ -1331,80 +1345,160 @@ chat_property_changed_cb (EmpathyTpChat *tp_chat,
}
}
-static void
-chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer,
- EmpathyChat *chat)
+static gboolean
+chat_input_text_get_word_from_iter (GtkTextIter *iter,
+ GtkTextIter *start,
+ GtkTextIter *end)
{
- GtkTextIter start, end;
- gchar *str;
- gboolean spell_checker = FALSE;
+ GtkTextIter word_start = *iter;
+ GtkTextIter word_end = *iter;
+ GtkTextIter tmp;
- if (gtk_text_buffer_get_char_count (buffer) == 0) {
- chat_composing_stop (chat);
- } else {
- chat_composing_start (chat);
+ if (gtk_text_iter_inside_word (&word_end) &&
+ !gtk_text_iter_ends_word (&word_end)) {
+ gtk_text_iter_forward_word_end (&word_end);
}
- empathy_conf_get_bool (empathy_conf_get (),
- EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
- &spell_checker);
+ tmp = word_end;
- gtk_text_buffer_get_start_iter (buffer, &start);
+ if (gtk_text_iter_get_char (&tmp) == '\'') {
+ gtk_text_iter_forward_char (&tmp);
- if (!spell_checker) {
- gtk_text_buffer_get_end_iter (buffer, &end);
- gtk_text_buffer_remove_tag_by_name (buffer, "misspelled", &start, &end);
- return;
+ if (g_unichar_isalpha (gtk_text_iter_get_char (&tmp))) {
+ gtk_text_iter_forward_word_end (&word_end);
+ }
}
- if (!empathy_spell_supported ()) {
- return;
- }
- /* NOTE: this is really inefficient, we shouldn't have to
- reiterate the whole buffer each time and check each work
- every time. */
- while (TRUE) {
- gboolean correct = FALSE;
+ if (gtk_text_iter_inside_word (&word_start) ||
+ gtk_text_iter_ends_word (&word_start)) {
+ if (!gtk_text_iter_starts_word (&word_start) ||
+ gtk_text_iter_equal (&word_start, &word_end)) {
+ gtk_text_iter_backward_word_start (&word_start);
+ }
- /* if at start */
- if (gtk_text_iter_is_start (&start)) {
- end = start;
+ tmp = word_start;
+ gtk_text_iter_backward_char (&tmp);
- if (!gtk_text_iter_forward_word_end (&end)) {
- /* no whole word yet */
- break;
- }
- } else {
- if (!gtk_text_iter_forward_word_end (&end)) {
- /* must be the end of the buffer */
- break;
- }
+ if (gtk_text_iter_get_char (&tmp) == '\'') {
+ gtk_text_iter_backward_char (&tmp);
- start = end;
- gtk_text_iter_backward_word_start (&start);
+ if (g_unichar_isalpha (gtk_text_iter_get_char (&tmp))) {
+ gtk_text_iter_backward_word_start (&word_start);
+ }
}
+ }
+
+ *start = word_start;
+ *end = word_end;
+ return TRUE;
+}
+
+static void
+chat_input_text_buffer_insert_text_cb (GtkTextBuffer *buffer,
+ GtkTextIter *location,
+ gchar *text,
+ gint len,
+ EmpathyChat *chat)
+{
+ GtkTextIter iter, pos;
+
+ /* Remove all misspelled tags in the inserted text.
+ * This happens when text is inserted within a misspelled word. */
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter,
+ gtk_text_iter_get_offset (location) - len);
+ gtk_text_buffer_remove_tag_by_name (buffer, "misspelled",
+ &iter, location);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &pos, gtk_text_buffer_get_insert (buffer));
+
+ do {
+ GtkTextIter start, end;
+ gchar *str;
+
+ if (!chat_input_text_get_word_from_iter (&iter, &start, &end))
+ continue;
str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
- /* spell check string if not a command */
- if (str[0] != '/') {
- correct = empathy_spell_check (str);
+ if (gtk_text_iter_in_range (&pos, &start, &end) ||
+ gtk_text_iter_equal (&pos, &end) ||
+ empathy_spell_check (str)) {
+ gtk_text_buffer_remove_tag_by_name (buffer, "misspelled", &start, &end);
} else {
- correct = TRUE;
+ gtk_text_buffer_apply_tag_by_name (buffer, "misspelled", &start, &end);
}
- if (!correct) {
- gtk_text_buffer_apply_tag_by_name (buffer, "misspelled", &start, &end);
+ g_free (str);
+
+ } while (gtk_text_iter_forward_word_end (&iter) &&
+ gtk_text_iter_compare (&iter, location) <= 0);
+}
+
+static void
+chat_input_text_buffer_delete_range_cb (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end,
+ EmpathyChat *chat)
+{
+ GtkTextIter word_start, word_end;
+
+ if (chat_input_text_get_word_from_iter (start, &word_start, &word_end)) {
+ gtk_text_buffer_remove_tag_by_name (buffer, "misspelled",
+ &word_start, &word_end);
+ }
+}
+
+static void
+chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer,
+ EmpathyChat *chat)
+{
+ if (gtk_text_buffer_get_char_count (buffer) == 0) {
+ chat_composing_stop (chat);
+ } else {
+ chat_composing_start (chat);
+ }
+}
+
+static void
+chat_input_text_buffer_notify_cursor_position_cb (GtkTextBuffer *buffer,
+ GParamSpec *pspec,
+ EmpathyChat *chat)
+{
+ GtkTextIter pos;
+ GtkTextIter prev_pos;
+ GtkTextIter word_start;
+ GtkTextIter word_end;
+ GtkTextMark *mark;
+ gchar *str;
+
+ mark = gtk_text_buffer_get_mark (buffer, "previous-cursor-position");
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &pos,
+ gtk_text_buffer_get_insert (buffer));
+ gtk_text_buffer_get_iter_at_mark (buffer, &prev_pos, mark);
+
+ if (!chat_input_text_get_word_from_iter (&prev_pos, &word_start, &word_end))
+ goto out;
+
+ if (!gtk_text_iter_in_range (&pos, &word_start, &word_end) &&
+ !gtk_text_iter_equal (&pos, &word_end)) {
+ str = gtk_text_buffer_get_text (buffer,
+ &word_start, &word_end, FALSE);
+
+ if (!empathy_spell_check (str)) {
+ gtk_text_buffer_apply_tag_by_name (buffer,
+ "misspelled", &word_start, &word_end);
} else {
- gtk_text_buffer_remove_tag_by_name (buffer, "misspelled", &start, &end);
+ gtk_text_buffer_remove_tag_by_name (buffer,
+ "misspelled", &word_start, &word_end);
}
g_free (str);
-
- /* set start iter to the end iters position */
- start = end;
}
+
+out:
+ gtk_text_buffer_move_mark (buffer, mark, &pos);
}
static gboolean
@@ -2275,6 +2369,106 @@ chat_destroy_cb (EmpathyTpChat *tp_chat,
}
static gboolean
+update_misspelled_words (gpointer data)
+{
+ EmpathyChat *chat = EMPATHY_CHAT (data);
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ gint length;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
+
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ length = gtk_text_iter_get_offset (&iter);
+ chat_input_text_buffer_insert_text_cb (buffer, &iter,
+ NULL, length, chat);
+ return FALSE;
+}
+
+static void
+conf_spell_checking_cb (EmpathyConf *conf,
+ const gchar *key,
+ gpointer user_data)
+{
+ EmpathyChat *chat = EMPATHY_CHAT (user_data);
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+ gboolean spell_checker;
+ GtkTextBuffer *buffer;
+
+ if (strcmp (key, EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED) != 0)
+ return;
+
+ empathy_conf_get_bool (conf, EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
+ &spell_checker);
+
+ if (!empathy_spell_supported ()) {
+ spell_checker = FALSE;
+ }
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
+
+ if (spell_checker == priv->spell_checking_enabled) {
+ if (spell_checker) {
+ /* Possibly changed dictionaries,
+ * update misspelled words. Need to do so in idle
+ * so the spell checker is updated. */
+ g_idle_add (update_misspelled_words, chat);
+ }
+
+ return;
+ }
+
+ if (spell_checker) {
+ GtkTextIter iter;
+
+ priv->notify_cursor_position_id = tp_g_signal_connect_object (
+ buffer, "notify::cursor-position",
+ G_CALLBACK (chat_input_text_buffer_notify_cursor_position_cb),
+ chat, 0);
+ priv->insert_text_id = tp_g_signal_connect_object (
+ buffer, "insert-text",
+ G_CALLBACK (chat_input_text_buffer_insert_text_cb),
+ chat, G_CONNECT_AFTER);
+ priv->delete_range_id = tp_g_signal_connect_object (
+ buffer, "delete-range",
+ G_CALLBACK (chat_input_text_buffer_delete_range_cb),
+ chat, G_CONNECT_AFTER);
+
+ gtk_text_buffer_create_tag (buffer, "misspelled",
+ "underline", PANGO_UNDERLINE_ERROR,
+ NULL);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter,
+ gtk_text_buffer_get_insert (buffer));
+ gtk_text_buffer_create_mark (buffer, "previous-cursor-position",
+ &iter, TRUE);
+
+ /* Mark misspelled words in the existing buffer.
+ * Need to do so in idle so the spell checker is updated. */
+ g_idle_add (update_misspelled_words, chat);
+ } else {
+ GtkTextTagTable *table;
+ GtkTextTag *tag;
+
+ g_signal_handler_disconnect (buffer, priv->notify_cursor_position_id);
+ priv->notify_cursor_position_id = 0;
+ g_signal_handler_disconnect (buffer, priv->insert_text_id);
+ priv->insert_text_id = 0;
+ g_signal_handler_disconnect (buffer, priv->delete_range_id);
+ priv->delete_range_id = 0;
+
+ table = gtk_text_buffer_get_tag_table (buffer);
+ tag = gtk_text_tag_table_lookup (table, "misspelled");
+ gtk_text_tag_table_remove (table, tag);
+
+ gtk_text_buffer_delete_mark_by_name (buffer,
+ "previous-cursor-position");
+ }
+
+ priv->spell_checking_enabled = spell_checker;
+}
+
+static gboolean
chat_hpaned_pos_changed_cb (GtkWidget* hpaned, gpointer user_data)
{
gint hpaned_pos;
@@ -2358,9 +2552,12 @@ chat_create_ui (EmpathyChat *chat)
tp_g_signal_connect_object (buffer, "changed",
G_CALLBACK (chat_input_text_buffer_changed_cb),
chat, 0);
- gtk_text_buffer_create_tag (buffer, "misspelled",
- "underline", PANGO_UNDERLINE_ERROR,
- NULL);
+ priv->conf_notify_id =
+ empathy_conf_notify_add (empathy_conf_get (),
+ EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
+ conf_spell_checking_cb, chat);
+ conf_spell_checking_cb (empathy_conf_get (),
+ EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED, chat);
gtk_container_add (GTK_CONTAINER (priv->scrolled_window_input),
chat->input_text_view);
gtk_widget_show (chat->input_text_view);
@@ -2464,6 +2661,12 @@ chat_finalize (GObject *object)
DEBUG ("Finalized: %p", object);
+ if (priv->conf_notify_id != 0) {
+ empathy_conf_notify_remove (empathy_conf_get (),
+ priv->conf_notify_id);
+ priv->conf_notify_id = 0;
+ }
+
g_list_foreach (priv->input_history, (GFunc) chat_input_history_entry_free, NULL);
g_list_free (priv->input_history);