soc.2008.finch: 6a306415: First commit of new spell checking metho...
queueram at soc.pidgin.im
queueram at soc.pidgin.im
Sun Aug 17 16:40:52 EDT 2008
-----------------------------------------------------------------
Revision: 6a30641528f3947c95f24663558c09fc6dfbc793
Ancestor: dc6bba068f9847d54138be8297f4964795394dee
Author: queueram at soc.pidgin.im
Date: 2008-08-17T20:31:24
Branch: im.pidgin.soc.2008.finch
URL: http://d.pidgin.im/viewmtn/revision/info/6a30641528f3947c95f24663558c09fc6dfbc793
Modified files:
finch/libgnt/gntentry.c
ChangeLog:
First commit of new spell checking method. Still needs some work, but i
figured i'd best commit the copy i have in it's last known "working" state.
-------------- next part --------------
============================================================
--- finch/libgnt/gntentry.c b77bff6c7614638f0baa2ccff01dff92ad7dfe0e
+++ finch/libgnt/gntentry.c 341da39b9b21d55b70e816613178b41642d08f9d
@@ -63,6 +63,28 @@ struct _GntEntryKillRing
GntEntryAction last;
};
+#ifdef USE_ENCHANT
+typedef struct _GntEntryWord
+{
+ int start; /**< start of word relative to entry->start */
+ int end_offset; /**< end of word relative to start of word */
+ gboolean checked_spell; /**< Flag to indicate this word has been spell checked */
+ gboolean misspelled; /**< Flag to indicate the word is misspelled */
+ struct _GntEntryWord *next;
+ struct _GntEntryWord *prev;
+} GntEntryWord;
+
+/* TODO: move these somewhere else */
+static void gnt_entry_free_word(GntEntryWord *w);
+static GntEntryWord *gnt_entry_word_new(void);
+static GntEntryWord *gnt_entry_word_list_append(GntEntryWord *list, GntEntryWord *word);
+static GntEntryWord *gnt_entry_word_list_prepend(GntEntryWord *list, GntEntryWord *word);
+static GntEntryWord *gnt_entry_word_list_insert_after(GntEntryWord *list, GntEntryWord *sibling, GntEntryWord *word);
+static GntEntryWord *gnt_entry_word_list_insert_before(GntEntryWord *list, GntEntryWord *sibling, GntEntryWord *word);
+static GntEntryWord *gnt_entry_word_list_pluck(GntEntryWord *list, GntEntryWord *word);
+static GntEntryWord *gnt_entry_word_list_free(GntEntryWord *list);
+#endif
+
struct _GntEntrySpell
{
#ifdef USE_ENCHANT
@@ -70,7 +92,10 @@ struct _GntEntrySpell
EnchantDict *dict;
char *lang;
gboolean enable;
- GntWidget *context; /**< Context menu to correct spelling or change language */
+ GntWidget *context; /**< Context menu to correct spelling or change language */
+ GntEntryWord *word_list; /**< Linked list of words */
+ GntEntryWord *cursor_word; /**< Word under the cursor position */
+ GntEntryWord *scroll_word; /**< Word under the scroll position */
#endif
};
@@ -81,6 +106,9 @@ static void gnt_entry_set_text_internal(
static gboolean gnt_entry_key_pressed(GntWidget *widget, const char *text);
static void gnt_entry_set_text_internal(GntEntry *entry, const char *text);
+static GntEntryWord *gnt_entry_parse_words(GntEntry *entry);
+static void spell_check_words(GntEntry *entry);
+
static gboolean
update_kill_ring(GntEntry *entry, GntEntryAction action, const char *text, int len)
{
@@ -286,6 +314,13 @@ show_suggest_dropdown(GntEntry *entry)
}
#ifdef USE_ENCHANT
+static gboolean
+is_word_break(char *prev, char *wc)
+{
+ /* TODO: add pango code here */
+ return g_unichar_isspace(g_utf8_get_char(wc)) && g_unichar_ispunct(g_utf8_get_char(wc));
+}
+
/* copy of get_beginning_of_word, but not GntEntry specific
* TODO: refactor with get_beginning_of_word
*/
@@ -383,14 +418,11 @@ gnt_entry_draw(GntWidget *widget)
}
else {
#ifdef USE_ENCHANT
- /* TODO: maybe want to move this to a different location and use some
- * sort of tags to indicate areas of misspellings and then print the tags
- * out here so the spellchecking isn't always performed for each word on
- * a gnt_entry_draw */
- char *s, *e;
int miss_color;
- int count;
- int width;
+ int offset;
+ GntEntryWord *it = entry->spell->word_list;
+ char *prev = entry->scroll;
+
if (focus)
miss_color = gnt_color_pair(GNT_COLOR_MISSPELL);
else
@@ -398,59 +430,28 @@ gnt_entry_draw(GntWidget *widget)
/* only spell check if enabled and box isn't empty */
if (entry->spell->enable && (entry->start != entry->end)) {
wmove(widget->window, 0, 0);
- /* if scroll starts on a non-letter, find the next word */
- if (g_unichar_isspace(g_utf8_get_char(entry->scroll)) || g_unichar_ispunct(g_utf8_get_char(entry->scroll))) {
- s = get_beginning_of_next_word(entry->scroll, entry->end);
- if (!s) {
- s = entry->end;
+ /* iterate through the entire word list */
+ while (it) {
+ /* print the whitespace between end of previous word and start of current */
+ if (prev < (entry->start + it->start)) {
+ wattroff(widget->window, miss_color);
+ waddnstr(widget->window, prev, (entry->start + it->start) - prev);
}
- waddnstr(widget->window, entry->scroll, s - entry->scroll);
- } else {
- s = get_beginning_of_prev_word(entry->scroll, entry->start);
- }
- e = get_end_of_word(s, entry->end);
+ prev = entry->start + it->start + it->end_offset + 1;
- if (!check_word(entry, s, e)) {
- wattron(widget->window, miss_color);
- } else {
- wattroff(widget->window, miss_color);
- }
- /* first word might be special case if scroll is in middle of word */
- if (s < entry->scroll) {
- waddnstr(widget->window, entry->scroll, e - entry->scroll + 1);
- } else {
- waddnstr(widget->window, s, e - s + 1);
- }
-
- gnt_widget_get_size(GNT_WIDGET(entry), &width, NULL);
- count = 0;
- s = g_utf8_find_next_char(e, entry->end);
- while(s && count < width) {
- count++;
- /* print the whitespace and punctuation characters */
- wattroff(widget->window, miss_color);
- e = get_beginning_of_next_word(s, entry->end);
- if (!e && s < entry->end) {
- /* the end is all non-letter characters */
- waddnstr(widget->window, s, entry->end - s + 1);
- break;
- } else if (e) {
- /* there are more words */
- waddnstr(widget->window, s, e - s);
- s = e;
- e = get_end_of_word(s, entry->end);
-
- if (!check_word(entry, s, e)) {
- wattron(widget->window, miss_color);
- } else {
- wattroff(widget->window, miss_color);
- }
- waddnstr(widget->window, s, e - s + 1);
- s = g_utf8_find_next_char(e, entry->end);
+ /* TODO: special case where scroll is in middle of word */
+ if (it->checked_spell && it->misspelled) {
+ wattron(widget->window, miss_color);
} else {
- break;
+ wattroff(widget->window, miss_color);
}
+ waddnstr(widget->window, entry->start + it->start, it->end_offset + 1);
+
+ it = it->next;
}
+ if (prev <= entry->end)
+ waddnstr(widget->window, prev, entry->end - prev + 1);
+
} else {
wattroff(widget->window, miss_color);
mvwprintw(widget->window, 0, 0, "%s", entry->scroll);
@@ -961,6 +962,8 @@ gnt_entry_key_pressed(GntWidget *widget,
show_suggest_dropdown(entry);
}
update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
+ /* TODO: don't reparse everything everytime a key is pressed */
+ entry->spell->word_list = gnt_entry_parse_words(entry);
entry_redraw(widget);
entry_text_changed(entry);
return TRUE;
@@ -1138,18 +1141,24 @@ set_spell_language(GntEntrySpell *spell,
{
const char *err;
- if (spell->broker) {
- if (spell->dict)
- enchant_broker_free_dict(spell->broker, spell->dict);
+ if (!spell->lang || (spell->lang && (strcmp(lang, spell->lang) != 0))) {
+ if (spell->lang) {
+ g_free(spell->lang);
+ }
+ spell->lang = g_strdup(lang);
+ if (spell->broker) {
+ if (spell->dict)
+ enchant_broker_free_dict(spell->broker, spell->dict);
- spell->dict = enchant_broker_request_dict(spell->broker, lang);
+ spell->dict = enchant_broker_request_dict(spell->broker, lang);
- if (spell->dict == NULL) {
- err = enchant_broker_get_error(spell->broker);
- if (err != NULL) {
- g_warning("GntEntry: couldn't get dictionary for %s: %s\n", lang, err);
- } else {
- g_warning("GntEntry: couldn't get dictionary for %s\n", lang);
+ if (spell->dict == NULL) {
+ err = enchant_broker_get_error(spell->broker);
+ if (err != NULL) {
+ g_warning("GntEntry: couldn't get dictionary for %s: %s\n", lang, err);
+ } else {
+ g_warning("GntEntry: couldn't get dictionary for %s\n", lang);
+ }
}
}
}
@@ -1182,6 +1191,11 @@ new_spell(void)
set_spell_language(sp, lang);
}
+
+ /* TODO: do these need to be explicitly set to NULL here? */
+ sp->word_list = NULL;
+ sp->cursor_word = NULL;
+ sp->scroll_word = NULL;
#endif
return sp;
}
@@ -1207,6 +1221,9 @@ context_menu_callback(GntMenuItem *item,
SpellLangInfo *cur_info = (SpellLangInfo *)data;
if (cur_info->entry->spell) {
set_spell_language(cur_info->entry->spell, cur_info->lang);
+ if (cur_info->entry->spell->enable) {
+ cur_info->entry->spell->word_list = gnt_entry_parse_words(cur_info->entry);
+ }
entry_redraw(GNT_WIDGET(cur_info->entry));
}
}
@@ -1591,6 +1608,9 @@ void gnt_entry_set_spell_enable(GntEntry
#ifdef USE_ENCHANT
if (entry->spell && entry->spell->enable != enable) {
entry->spell->enable = enable;
+ if (enable) {
+ entry->spell->word_list = gnt_entry_parse_words(entry);
+ }
entry_redraw(GNT_WIDGET(entry));
}
#endif
@@ -1602,6 +1622,10 @@ void gnt_entry_set_spell_lang(GntEntry *
if (entry->spell) {
if (strcmp(lang, entry->spell->lang) != 0) {
set_spell_language(entry->spell, lang);
+ if (entry->spell->enable) {
+ entry->spell->word_list = gnt_entry_parse_words(entry);
+ }
+ entry_redraw(GNT_WIDGET(entry));
}
}
#endif
@@ -1630,3 +1654,191 @@ void gnt_entry_remove_suggest(GntEntry *
}
}
+#ifdef USE_ENCHANT
+static GntEntryWord *
+gnt_entry_parse_words(GntEntry *entry)
+{
+ char *s, *e;
+ int count;
+ int width;
+ GntEntryWord *word;
+ GntEntryWord *start = NULL;
+
+ /* only spell check if enabled and box isn't empty */
+ if (entry->spell->enable && (entry->start != entry->end)) {
+
+ /* if start begins on a non-letter, find the next word */
+ if (g_unichar_isspace(g_utf8_get_char(entry->start)) || g_unichar_ispunct(g_utf8_get_char(entry->start))) {
+ s = get_beginning_of_next_word(entry->start, entry->end);
+ if (!s) {
+ s = entry->end;
+ }
+ } else {
+ s = entry->start;
+ }
+ e = get_end_of_word(s, entry->end);
+
+ word = gnt_entry_word_new();
+ word->start = s - entry->start;
+ word->end_offset = e - s;
+ word->checked_spell = TRUE;
+ if (!check_word(entry, s, e)) {
+ word->misspelled = TRUE;
+ } else {
+ word->misspelled = FALSE;
+ }
+ /* first one is the start of the list */
+ start = word;
+ if ((entry->start + word->start <= entry->cursor) && (entry->start + word->start + word->end_offset >= entry->cursor))
+ entry->spell->cursor_word = word;
+ if ((entry->start + word->start <= entry->scroll) && (entry->start + word->start + word->end_offset >= entry->scroll))
+ entry->spell->scroll_word = word;
+
+ s = g_utf8_find_next_char(e, entry->end);
+ while (s && s != entry->end) {
+ e = get_beginning_of_next_word(s, entry->end);
+ if (e) {
+ /* there are more words */
+ s = e;
+ e = get_end_of_word(s, entry->end);
+
+ word = gnt_entry_word_new();
+ word->start = s - entry->start;
+ word->end_offset = e - s;
+ word->checked_spell = TRUE;
+ if ((entry->start + word->start <= entry->cursor) && (entry->start + word->start + word->end_offset >= entry->cursor))
+ entry->spell->cursor_word = word;
+ if ((entry->start + word->start <= entry->scroll) && (entry->start + word->start + word->end_offset >= entry->scroll))
+ entry->spell->scroll_word = word;
+
+ if (!check_word(entry, s, e)) {
+ word->misspelled = TRUE;
+ } else {
+ word->misspelled = FALSE;
+ }
+
+ gnt_entry_word_list_append(start, word);
+
+ s = g_utf8_find_next_char(e, entry->end);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return start;
+}
+
+/* the GntEntryWord linked list operators */
+/* add new element to end of list */
+/* TODO: implement this in a memory pool */
+static void
+gnt_entry_free_word(GntEntryWord *w)
+{
+ g_free(w);
+}
+
+/* TODO: implement this in a memory pool */
+static GntEntryWord *
+gnt_entry_word_new(void)
+{
+ GntEntryWord *w;
+ w = g_new0(GntEntryWord, 1);
+ return w;
+}
+
+static GntEntryWord *
+gnt_entry_word_list_append(GntEntryWord *list, GntEntryWord *word)
+{
+ GntEntryWord *it = list;
+
+ if(list) {
+ while(it->next)
+ it = it->next;
+ it->next = word;
+ word->prev = it;
+ } else {
+ word->prev = NULL;
+ word->next = NULL;
+ list = word;
+ }
+
+ return list;
+}
+
+/* add new element to beginning of list */
+static GntEntryWord *
+gnt_entry_word_list_prepend(GntEntryWord *list, GntEntryWord *word)
+{
+ if(list) {
+ word->next = list;
+ word->prev = NULL;
+ list->prev = word;
+ }
+
+ return word;
+}
+
+/* add new element to after sibling */
+static GntEntryWord *
+gnt_entry_word_list_insert_after(GntEntryWord *list, GntEntryWord *sibling, GntEntryWord *word)
+{
+ if (!list) {
+ list = word;
+ } else {
+ if(sibling->next)
+ sibling->next->prev = word;
+ word->next = sibling->next;
+ word->prev = sibling;
+ sibling->next = word;
+ }
+
+ return list;
+}
+
+/* add new element to before sibling */
+static GntEntryWord *
+gnt_entry_word_list_insert_before(GntEntryWord *list, GntEntryWord *sibling, GntEntryWord *word)
+{
+ if (!list || sibling == list) {
+ list = word;
+ } else {
+ if(sibling->prev)
+ sibling->prev->next = word;
+ word->next = sibling;
+ word->prev = sibling->prev;
+ sibling->prev = word;
+ }
+
+ return list;
+}
+
+/* add new element to before sibling */
+static GntEntryWord *
+gnt_entry_word_list_pluck(GntEntryWord *list, GntEntryWord *word)
+{
+ if (list && (word == list)) {
+ list = word->next;
+ list->prev = NULL;
+ } else {
+ word->prev->next = word->next;
+ }
+
+ return list;
+}
+
+/* remove all elements in a GntEntryWord list */
+/* TODO: should this actually return anything? */
+static GntEntryWord *
+gnt_entry_word_list_free(GntEntryWord *list)
+{
+ GntEntryWord *it = list;
+ while(it) {
+ it = it->next;
+ gnt_entry_free_word(it);
+ }
+
+ return NULL;
+}
+
+#endif /* USE_ENCHANT */
More information about the Commits
mailing list