[gspell] Entry utils: get list of words



commit ae18c51c88f1d4eeaa120b2218135c52ad1280f1
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Fri Oct 28 12:30:46 2016 +0200

    Entry utils: get list of words
    
    And add test-entry-utils unit test.

 docs/reference/Makefile.am  |    1 +
 gspell/Makefile.am          |    2 +
 gspell/gspell-entry-utils.c |  117 ++++++++++++++++++++++++++++++++++++
 gspell/gspell-entry-utils.h |   52 ++++++++++++++++
 po/POTFILES.in              |    1 +
 testsuite/Makefile.am       |    3 +
 testsuite/test-entry.c      |  137 +++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 313 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 33b30b1..088c496 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -31,6 +31,7 @@ EXTRA_HFILES =                                                \
 IGNORE_HFILES =                                        \
        gspell.h                                \
        gspellregion.h                          \
+       gspell-entry-utils.h                    \
        gspell-init.h                           \
        gspell-inline-checker-text-buffer.h     \
        gspell-osx.h                            \
diff --git a/gspell/Makefile.am b/gspell/Makefile.am
index 3cfd0da..253698f 100644
--- a/gspell/Makefile.am
+++ b/gspell/Makefile.am
@@ -45,6 +45,7 @@ gspell_public_c_files =                               \
 gspell_private_headers =                       \
        gconstructor.h                          \
        gspellregion.h                          \
+       gspell-entry-utils.h                    \
        gspell-init.h                           \
        gspell-inline-checker-text-buffer.h     \
        gspell-text-iter.h                      \
@@ -52,6 +53,7 @@ gspell_private_headers =                      \
 
 gspell_private_c_files =                       \
        gspellregion.c                          \
+       gspell-entry-utils.c                    \
        gspell-init.c                           \
        gspell-inline-checker-text-buffer.c     \
        gspell-text-iter.c                      \
diff --git a/gspell/gspell-entry-utils.c b/gspell/gspell-entry-utils.c
new file mode 100644
index 0000000..32f681c
--- /dev/null
+++ b/gspell/gspell-entry-utils.c
@@ -0,0 +1,117 @@
+/*
+ * This file is part of gspell, a spell-checking library.
+ *
+ * Copyright 2016 - Sébastien Wilmet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gspell-entry-utils.h"
+#include <string.h>
+
+GspellEntryWord *
+_gspell_entry_word_new (void)
+{
+       return g_new0 (GspellEntryWord, 1);
+}
+
+void
+_gspell_entry_word_free (gpointer data)
+{
+       GspellEntryWord *word = data;
+
+       if (word != NULL)
+       {
+               g_free (word->word_str);
+               g_free (word);
+       }
+}
+
+/* Returns: (transfer full) (element-type GspellEntryWord): free with
+ * g_slist_free_full (words, _gspell_entry_word_free);
+ */
+GSList *
+_gspell_entry_utils_get_words (GtkEntry *entry)
+{
+       PangoLayout *layout;
+       const gchar *text;
+       const gchar *cur_text_pos;
+       const gchar *word_start;
+       const PangoLogAttr *attrs;
+       gint n_attrs = 0;
+       gint attr_num;
+       GSList *list = NULL;
+
+       g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+
+       layout = gtk_entry_get_layout (entry);
+       text = gtk_entry_get_text (entry);
+       attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
+
+       attr_num = 0;
+       cur_text_pos = text;
+       word_start = NULL;
+
+       while (attr_num < n_attrs)
+       {
+               if (word_start != NULL &&
+                   attrs[attr_num].is_word_end)
+               {
+                       const gchar *word_end;
+                       GspellEntryWord *word;
+
+                       if (cur_text_pos != NULL)
+                       {
+                               word_end = cur_text_pos;
+                       }
+                       else
+                       {
+                               word_end = word_start + strlen (word_start);
+                       }
+
+                       word = _gspell_entry_word_new ();
+                       word->byte_start = word_start - text;
+                       word->byte_end = word_end - text;
+                       word->word_str = g_strndup (word_start, word_end - word_start);
+
+                       list = g_slist_prepend (list, word);
+
+                       /* Find next word start. */
+                       word_start = NULL;
+               }
+
+               if (word_start == NULL &&
+                   attrs[attr_num].is_word_start)
+               {
+                       word_start = cur_text_pos;
+               }
+
+               if (cur_text_pos == NULL &&
+                   attr_num != n_attrs - 1)
+               {
+                       g_warning ("%s(): problem in loop iteration, attr_num=%d but should be %d.",
+                                  G_STRFUNC,
+                                  attr_num,
+                                  n_attrs - 1);
+                       break;
+               }
+
+               attr_num++;
+               cur_text_pos = g_utf8_find_next_char (cur_text_pos, NULL);
+       }
+
+       return g_slist_reverse (list);
+}
+
+/* ex:set ts=8 noet: */
diff --git a/gspell/gspell-entry-utils.h b/gspell/gspell-entry-utils.h
new file mode 100644
index 0000000..3aa101e
--- /dev/null
+++ b/gspell/gspell-entry-utils.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of gspell, a spell-checking library.
+ *
+ * Copyright 2016 - Sébastien Wilmet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSPELL_ENTRY_UTILS_H
+#define GSPELL_ENTRY_UTILS_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GspellEntryWord GspellEntryWord;
+struct _GspellEntryWord
+{
+       gchar *word_str;
+
+       /* Position in the GtkEntryBuffer. The character at the byte_end index
+        * is not included, like in #PangoAttribute.
+        */
+       gint byte_start;
+       gint byte_end;
+};
+
+G_GNUC_INTERNAL
+GspellEntryWord *_gspell_entry_word_new                        (void);
+
+G_GNUC_INTERNAL
+void            _gspell_entry_word_free                (gpointer data);
+
+G_GNUC_INTERNAL
+GSList *        _gspell_entry_utils_get_words          (GtkEntry *entry);
+
+G_END_DECLS
+
+#endif /* GSPELL_ENTRY_UTILS_H */
+
+/* ex:set ts=8 noet: */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f4bb7a8..b8341c3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -3,6 +3,7 @@ gspell/gspell-checker.c
 gspell/gspell-checker-dialog.c
 gspell/gspell-entry.c
 gspell/gspell-entry-buffer.c
+gspell/gspell-entry-utils.c
 gspell/gspell-inline-checker-text-buffer.c
 gspell/gspell-language.c
 gspell/gspell-language-chooser.c
diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
index 223f848..cc6934b 100644
--- a/testsuite/Makefile.am
+++ b/testsuite/Makefile.am
@@ -26,6 +26,9 @@ UNIT_TEST_PROGS =
 UNIT_TEST_PROGS += test-checker
 test_checker_SOURCES = test-checker.c
 
+UNIT_TEST_PROGS += test-entry
+test_entry_SOURCES = test-entry.c
+
 UNIT_TEST_PROGS += test-inline-checker-text-buffer
 test_inline_checker_text_buffer_SOURCES = test-inline-checker-text-buffer.c
 
diff --git a/testsuite/test-entry.c b/testsuite/test-entry.c
new file mode 100644
index 0000000..82d7229
--- /dev/null
+++ b/testsuite/test-entry.c
@@ -0,0 +1,137 @@
+/*
+ * This file is part of gspell, a spell-checking library.
+ *
+ * Copyright 2016 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gspell/gspell-entry-utils.h"
+
+static GSList *
+add_word (GSList      *list,
+         const gchar *word_str,
+         gint         byte_start,
+         gint         byte_end)
+{
+       GspellEntryWord *word;
+
+       word = _gspell_entry_word_new ();
+       word->word_str = g_strdup (word_str);
+       word->byte_start = byte_start;
+       word->byte_end = byte_end;
+
+       return g_slist_append (list, word);
+}
+
+static void
+free_word_list (GSList *list)
+{
+       g_slist_free_full (list, _gspell_entry_word_free);
+}
+
+static void
+check_entry_word_equal (GspellEntryWord *word1,
+                       GspellEntryWord *word2)
+{
+       g_assert_cmpstr (word1->word_str, ==, word2->word_str);
+       g_assert_cmpint (word1->byte_start, ==, word2->byte_start);
+       g_assert_cmpint (word1->byte_end, ==, word2->byte_end);
+}
+
+static void
+check_entry_word_list_equal (GSList *list1,
+                            GSList *list2)
+{
+       GSList *l1;
+       GSList *l2;
+
+       for (l1 = list1, l2 = list2;
+            l1 != NULL && l2 != NULL;
+            l1 = l1->next, l2 = l2->next)
+       {
+               GspellEntryWord *word1 = l1->data;
+               GspellEntryWord *word2 = l2->data;
+
+               check_entry_word_equal (word1, word2);
+       }
+
+       g_assert (l1 == NULL);
+       g_assert (l2 == NULL);
+}
+
+static void
+test_get_words (void)
+{
+       GtkEntry *entry;
+       GSList *expected_list;
+       GSList *received_list;
+
+       entry = GTK_ENTRY (gtk_entry_new ());
+       g_object_ref_sink (entry);
+
+       expected_list = NULL;
+       received_list = _gspell_entry_utils_get_words (entry);
+       check_entry_word_list_equal (expected_list, received_list);
+
+       /* Only one word */
+       gtk_entry_set_text (entry, "Finntroll");
+       expected_list = add_word (NULL, "Finntroll", 0, 9);
+       received_list = _gspell_entry_utils_get_words (entry);
+       check_entry_word_list_equal (expected_list, received_list);
+       free_word_list (expected_list);
+       free_word_list (received_list);
+
+       /* Only one word, not at the start and end */
+       gtk_entry_set_text (entry, " Finntroll ");
+       expected_list = add_word (NULL, "Finntroll", 1, 10);
+       received_list = _gspell_entry_utils_get_words (entry);
+       check_entry_word_list_equal (expected_list, received_list);
+       free_word_list (expected_list);
+       free_word_list (received_list);
+
+       /* Several words */
+       gtk_entry_set_text (entry, "Finntroll - Svart Djup");
+       expected_list = add_word (NULL, "Finntroll", 0, 9);
+       expected_list = add_word (expected_list, "Svart", 12, 17);
+       expected_list = add_word (expected_list, "Djup", 18, 22);
+       received_list = _gspell_entry_utils_get_words (entry);
+       check_entry_word_list_equal (expected_list, received_list);
+       free_word_list (expected_list);
+       free_word_list (received_list);
+
+       /* Multi-byte UTF-8 words */
+       // å takes two bytes.
+       // ö takes two bytes.
+       gtk_entry_set_text (entry, "Asfågelns Död");
+       expected_list = add_word (NULL, "Asfågelns", 0, 10);
+       expected_list = add_word (expected_list, "Död", 11, 15);
+       received_list = _gspell_entry_utils_get_words (entry);
+       check_entry_word_list_equal (expected_list, received_list);
+       free_word_list (expected_list);
+       free_word_list (received_list);
+
+       g_object_unref (entry);
+}
+
+gint
+main (gint    argc,
+      gchar **argv)
+{
+       gtk_test_init (&argc, &argv);
+
+       g_test_add_func ("/entry-utils/get-words", test_get_words);
+
+       return g_test_run ();
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]