2009-02-12 [paul] 3.7.0cvs60
[claws.git] / src / gtk / spell_entry.c
1 /*
2  * @file libsexy/sexy-icon-entry.c Entry widget
3  *
4  * @Copyright (C) 2004-2006 Christian Hammond.
5  * Some of this code is from gtkspell, Copyright (C) 2002 Evan Martin.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA  02111-1307, USA.
21  */
22
23 /* 
24    Adapted for Claws Mail (c) 2009 Pawel Pekala
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #  include "config.h"
29 #endif
30
31 #ifdef USE_ENCHANT
32
33 #include <glib.h>
34 #include <glib/gi18n.h>
35
36 #include <gdk/gdk.h>
37 #include <gdk/gdkkeysyms.h>
38
39 #include <string.h>
40 #include <glib.h>
41 #include <gtk/gtk.h>
42
43 #include "spell_entry.h"
44 #include "prefs_common.h"
45 #include "codeconv.h"
46 #include "defs.h"
47
48 static void claws_spell_entry_init              (ClawsSpellEntry *entry);
49 static void claws_spell_entry_editable_init     (GtkEditableClass *iface);
50 static void claws_spell_entry_finalize          (GObject *object);
51 static void claws_spell_entry_destroy           (GtkObject *object);
52 static gint claws_spell_entry_expose            (GtkWidget *widget,
53                                                  GdkEventExpose *event);
54 static gint claws_spell_entry_button_press      (GtkWidget *widget,
55                                                  GdkEventButton *event);
56 static gboolean claws_spell_entry_popup_menu    (GtkWidget *widget,
57                                                  ClawsSpellEntry *entry);
58 static void claws_spell_entry_populate_popup    (ClawsSpellEntry *entry,
59                                                  GtkMenu *menu,
60                                                  gpointer data);
61 static void claws_spell_entry_changed           (GtkEditable *editable,
62                                                  gpointer data);
63
64 struct _ClawsSpellEntryPriv
65 {
66         PangoAttrList        *attr_list;
67         gint                  mark_character;
68         gchar               **words;
69         gint                 *word_starts;
70         gint                 *word_ends;
71 };
72
73 static GtkEntryClass *parent_class = NULL;
74
75
76 G_DEFINE_TYPE_EXTENDED(ClawsSpellEntry, claws_spell_entry, GTK_TYPE_ENTRY, 0, G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE, claws_spell_entry_editable_init)); 
77
78
79 static void claws_spell_entry_class_init(ClawsSpellEntryClass *klass)
80 {
81         GObjectClass    *g_object_class;
82         GtkObjectClass  *gtk_object_class;
83         GtkWidgetClass  *widget_class;
84         
85         parent_class = g_type_class_peek_parent(klass);
86         
87         g_object_class = G_OBJECT_CLASS(klass);
88         g_object_class->finalize = claws_spell_entry_finalize;
89         
90         gtk_object_class = GTK_OBJECT_CLASS(klass);
91         gtk_object_class->destroy = claws_spell_entry_destroy;
92         
93         widget_class = GTK_WIDGET_CLASS(klass);
94         widget_class->expose_event = claws_spell_entry_expose;
95         widget_class->button_press_event = claws_spell_entry_button_press;
96         
97         g_type_class_add_private(g_object_class,
98                         sizeof(struct _ClawsSpellEntryPriv));
99 }
100
101 static void claws_spell_entry_init(ClawsSpellEntry *entry)
102 {
103         entry->gtkaspell = NULL;
104         
105         entry->priv = g_new0(ClawsSpellEntryPriv, 1);
106         entry->priv->attr_list = pango_attr_list_new();
107                                         
108         g_signal_connect(G_OBJECT(entry), "popup-menu",
109                         G_CALLBACK(claws_spell_entry_popup_menu), entry);
110         g_signal_connect(G_OBJECT(entry), "populate-popup",
111                         G_CALLBACK(claws_spell_entry_populate_popup), NULL);
112         g_signal_connect(G_OBJECT(entry), "changed",
113                         G_CALLBACK(claws_spell_entry_changed), NULL);
114 }
115
116 static void claws_spell_entry_editable_init (GtkEditableClass *iface) {}
117
118 static void claws_spell_entry_finalize(GObject *object)
119 {
120         ClawsSpellEntry *entry = CLAWS_SPELL_ENTRY(object);
121
122         if (entry->priv->attr_list)
123                 pango_attr_list_unref(entry->priv->attr_list);
124         if (entry->priv->words)
125                 g_strfreev(entry->priv->words);
126
127         g_free(entry->priv->word_starts);
128         g_free(entry->priv->word_ends);
129         g_free(entry->priv);
130         entry->priv = NULL;
131         
132         G_OBJECT_CLASS(parent_class)->finalize(object);
133 }
134
135 static void claws_spell_entry_destroy(GtkObject *object)
136 {
137         GTK_OBJECT_CLASS(parent_class)->destroy(object);
138 }
139
140 GtkWidget *claws_spell_entry_new(void)
141 {
142         return GTK_WIDGET( g_object_new(CLAWS_TYPE_SPELL_ENTRY, NULL) );
143 }
144
145 void claws_spell_entry_set_gtkaspell(ClawsSpellEntry *entry, GtkAspell *gtkaspell)
146 {
147         g_return_if_fail(CLAWS_IS_SPELL_ENTRY(entry));
148         g_return_if_fail(gtkaspell != NULL);
149
150         entry->gtkaspell = gtkaspell;
151 }
152
153 static gint gtk_entry_find_position (GtkEntry *entry, gint x)
154 {
155         PangoLayout *layout;
156         PangoLayoutLine *line;
157         const gchar *text;
158         gint cursor_index;
159         gint index;
160         gint pos;
161         gboolean trailing;
162
163         x = x + entry->scroll_offset;
164
165         layout = gtk_entry_get_layout(entry);
166         text = pango_layout_get_text(layout);
167         cursor_index = g_utf8_offset_to_pointer(text, entry->current_pos) - text;
168
169         line = pango_layout_get_lines(layout)->data;
170         pango_layout_line_x_to_index(line, x * PANGO_SCALE, &index, &trailing);
171
172         if (index >= cursor_index && entry->preedit_length) {
173                 if (index >= cursor_index + entry->preedit_length) {
174                         index -= entry->preedit_length;
175                 } else {
176                         index = cursor_index;
177                         trailing = FALSE;
178                 }
179         }
180
181         pos = g_utf8_pointer_to_offset (text, text + index);
182         pos += trailing;
183
184         return pos;
185 }
186
187 static void get_word_extents_from_position(ClawsSpellEntry *entry, gint *start,
188                                            gint *end, guint position)
189 {
190         const gchar *text;
191         gint i, bytes_pos;
192
193         *start = -1;
194         *end = -1;
195
196         if (entry->priv->words == NULL)
197                 return;
198
199         text = gtk_entry_get_text(GTK_ENTRY(entry));
200         bytes_pos = (gint) (g_utf8_offset_to_pointer(text, position) - text);
201
202         for (i = 0; entry->priv->words[i]; i++) {
203                 if (bytes_pos >= entry->priv->word_starts[i] &&
204                     bytes_pos <= entry->priv->word_ends[i]) {
205                         *start = entry->priv->word_starts[i];
206                         *end   = entry->priv->word_ends[i];
207                         return;
208                 }
209         }
210 }
211
212 static gchar *get_word(ClawsSpellEntry *entry, const int start, const int end)
213 {
214         const gchar *text;
215         gchar *word;
216         
217         if (start >= end)
218                 return NULL;
219
220         text = gtk_entry_get_text(GTK_ENTRY(entry));
221         word = g_new0(gchar, end - start + 2);
222         g_strlcpy(word, text + start, end - start + 1);
223
224         return word;
225 }
226
227 static void replace_word(ClawsSpellEntry *entry, const gchar *newword)
228 {
229         gint cursor, start_pos, end_pos;
230         const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
231
232         start_pos = entry->gtkaspell->start_pos;
233         end_pos = entry->gtkaspell->end_pos;
234                 
235         cursor = gtk_editable_get_position(GTK_EDITABLE(entry));
236         /* is the cursor at the end? If so, restore it there */
237         if (g_utf8_strlen(text, -1) == cursor)
238                 cursor = -1;
239         else if(cursor < entry->priv->mark_character ||
240                 cursor > entry->priv->mark_character)
241                         cursor = entry->priv->mark_character;
242
243         gtk_editable_delete_text(GTK_EDITABLE(entry), start_pos, end_pos);
244         gtk_editable_insert_text(GTK_EDITABLE(entry), newword, strlen(newword),
245                                                          &start_pos);
246         gtk_editable_set_position(GTK_EDITABLE(entry), cursor);
247 }
248
249
250 static gboolean word_misspelled(ClawsSpellEntry *entry, int start, int end)
251 {
252         gchar *word;
253         gboolean ret;
254
255         word = get_word(entry, start, end);
256         if (word == NULL)
257                 return FALSE;
258                 
259         ret = gtkaspell_misspelled_test(entry->gtkaspell, word);
260
261         g_free(word);
262         return ret;
263 }
264
265 static void entry_strsplit_utf8(GtkEntry *entry, gchar ***set, gint **starts, gint **ends)
266 {
267         PangoLayout   *layout;
268         PangoLogAttr  *log_attrs;
269         const gchar   *text;
270         gint           n_attrs, n_strings, i, j;
271
272         layout = gtk_entry_get_layout(GTK_ENTRY(entry));
273         text = gtk_entry_get_text(GTK_ENTRY(entry));
274         pango_layout_get_log_attrs(layout, &log_attrs, &n_attrs);
275
276         /* Find how many words we have */
277         n_strings = 0;
278         for (i = 0; i < n_attrs; i++)
279                 if (log_attrs[i].is_word_start)
280                         n_strings++;
281
282         *set    = g_new0(gchar *, n_strings + 1);
283         *starts = g_new0(gint, n_strings);
284         *ends   = g_new0(gint, n_strings);
285
286         /* Copy out strings */
287         for (i = 0, j = 0; i < n_attrs; i++) {
288                 if (log_attrs[i].is_word_start) {
289                         gint cend, bytes;
290                         gchar *start;
291
292                         /* Find the end of this string */
293                         cend = i;
294                         while (!(log_attrs[cend].is_word_end))
295                                 cend++;
296
297                         /* Copy sub-string */
298                         start = g_utf8_offset_to_pointer(text, i);
299                         bytes = (gint) (g_utf8_offset_to_pointer(text, cend) - start);
300                         (*set)[j]    = g_new0(gchar, bytes + 1);
301                         (*starts)[j] = (gint) (start - text);
302                         (*ends)[j]   = (gint) (start - text + bytes);
303                         g_utf8_strncpy((*set)[j], start, cend - i);
304
305                         /* Move on to the next word */
306                         j++;
307                 }
308         }
309
310         g_free (log_attrs);
311 }
312
313 static void insert_misspelled_marker(ClawsSpellEntry *entry, guint start, guint end)
314 {
315         guint16 red   = (guint16) (((gdouble)((prefs_common.misspelled_col & 
316                                         0xff0000) >> 16) / 255.0) * 65535.0);
317         guint16 green = (guint16) (((gdouble)((prefs_common.misspelled_col & 
318                                         0x00ff00) >> 8) / 255.0) * 65535.0);
319         guint16 blue  = (guint16) (((gdouble) (prefs_common.misspelled_col & 
320                                         0x0000ff) / 255.0) * 65535.0);
321         PangoAttribute *fcolor, *ucolor, *unline;
322         
323         if(prefs_common.misspelled_col != 0) {
324                 fcolor = pango_attr_foreground_new(red, green, blue);
325                 fcolor->start_index = start;
326                 fcolor->end_index = end;
327                 
328                 pango_attr_list_insert(entry->priv->attr_list, fcolor);
329         } else {
330                 ucolor = pango_attr_underline_color_new (65535, 0, 0);
331                 unline = pango_attr_underline_new (PANGO_UNDERLINE_ERROR);
332
333                 ucolor->start_index = start;
334                 unline->start_index = start;
335
336                 ucolor->end_index = end;
337                 unline->end_index = end;
338
339                 pango_attr_list_insert (entry->priv->attr_list, ucolor);
340                 pango_attr_list_insert (entry->priv->attr_list, unline);
341         }
342 }
343
344 static gboolean check_word(ClawsSpellEntry *entry, int start, int end)
345 {
346         GtkAspell *gtkaspell = entry->gtkaspell;
347         PangoAttrIterator *it;
348         gint s, e;
349         gboolean misspelled;
350         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
351         gchar *word = NULL;
352
353         /* Check to see if we've got any attributes at this position.
354          * If so, free them, since we'll readd it if the word is misspelled */
355         it = pango_attr_list_get_iterator(entry->priv->attr_list);
356         if (it == NULL)
357                 return FALSE;
358         do {
359                 pango_attr_iterator_range(it, &s, &e);
360                 if (s == start) {
361                         GSList *attrs = pango_attr_iterator_get_attrs(it);
362                         g_slist_foreach(attrs, (GFunc) pango_attribute_destroy, NULL);
363                         g_slist_free(attrs);
364                 }
365         } while (pango_attr_iterator_next(it));
366         pango_attr_iterator_destroy(it);
367
368         if ((misspelled = word_misspelled(entry, start, end))) {
369                 insert_misspelled_marker(entry, start, end);
370         
371                 word = get_word(entry, start, end);
372                 strncpy(gtkaspell->theword, (gchar *)word, GTKASPELLWORDSIZE - 1);
373                 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
374                 gtkaspell->start_pos  = g_utf8_pointer_to_offset(text, (text+start));
375                 gtkaspell->end_pos    = g_utf8_pointer_to_offset(text, (text+end));
376                 gtkaspell_free_suggestions_list(gtkaspell);
377                 g_free(word);
378         }
379         
380         g_free(text);
381         
382         return misspelled;
383 }
384
385 void claws_spell_entry_recheck_all(ClawsSpellEntry *entry)
386 {
387         GdkRectangle rect;
388         PangoLayout *layout;
389         int length, i;
390
391         g_return_if_fail(CLAWS_IS_SPELL_ENTRY(entry));
392         g_return_if_fail(entry->gtkaspell != NULL);
393
394         if (entry->priv->words == NULL)
395                 return;
396
397         /* Remove all existing pango attributes.  These will get readded as we check */
398         pango_attr_list_unref(entry->priv->attr_list);
399         entry->priv->attr_list = pango_attr_list_new();
400
401         /* Loop through words */
402         for (i = 0; entry->priv->words[i]; i++) {
403                 length = strlen(entry->priv->words[i]);
404                 if (length == 0)
405                         continue;
406                 check_word(entry, entry->priv->word_starts[i], entry->priv->word_ends[i]);
407         }
408
409         layout = gtk_entry_get_layout(GTK_ENTRY(entry));
410         pango_layout_set_attributes(layout, entry->priv->attr_list);
411
412         if (GTK_WIDGET_REALIZED(GTK_WIDGET(entry))) {
413                 rect.x = 0; rect.y = 0;
414                 rect.width  = GTK_WIDGET(entry)->allocation.width;
415                 rect.height = GTK_WIDGET(entry)->allocation.height;
416                 gdk_window_invalidate_rect(GTK_WIDGET(entry)->window, &rect, TRUE);
417         }
418 }
419
420 static gint claws_spell_entry_expose(GtkWidget *widget, GdkEventExpose *event)
421 {
422         ClawsSpellEntry *entry = CLAWS_SPELL_ENTRY(widget);
423         GtkEntry *gtk_entry = GTK_ENTRY(widget);
424         PangoLayout *layout;
425
426         if (entry->gtkaspell != NULL) {
427                 layout = gtk_entry_get_layout(gtk_entry);
428                 pango_layout_set_attributes(layout, entry->priv->attr_list);
429         }
430
431         return GTK_WIDGET_CLASS(parent_class)->expose_event (widget, event);
432 }
433
434 static gint claws_spell_entry_button_press(GtkWidget *widget, GdkEventButton *event)
435 {
436         ClawsSpellEntry *entry = CLAWS_SPELL_ENTRY(widget);
437         GtkEntry *gtk_entry = GTK_ENTRY(widget);
438         gint pos;
439
440         pos = gtk_entry_find_position(gtk_entry, event->x);
441         entry->priv->mark_character = pos;
442
443         return GTK_WIDGET_CLASS(parent_class)->button_press_event (widget, event);
444 }
445
446 static gboolean claws_spell_entry_popup_menu(GtkWidget *widget, ClawsSpellEntry *entry)
447 {
448         entry->priv->mark_character = gtk_editable_get_position (GTK_EDITABLE (entry));
449         return FALSE;
450 }
451
452 static void set_position(gpointer data, gint pos)
453 {
454         gtk_editable_set_position(GTK_EDITABLE(data), pos);
455 }
456
457 static gboolean find_misspelled_cb(gpointer data, gboolean forward)
458 {
459         ClawsSpellEntry *entry = (ClawsSpellEntry *)data;
460         GtkAspell *gtkaspell = entry->gtkaspell;
461         gboolean misspelled = FALSE;
462         gint cursor, minpos, maxpos, i, words_len = 0;
463         gint start, end;
464         gchar *text;
465         
466         if (entry->priv->words == NULL)
467                 return FALSE;
468
469         gtkaspell->orig_pos = gtk_editable_get_position(GTK_EDITABLE(entry));
470         text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
471         cursor = g_utf8_offset_to_pointer(text, gtkaspell->orig_pos) - text;
472
473         if (gtk_editable_get_selection_bounds(GTK_EDITABLE(entry), &start, &end)) {
474                 minpos = g_utf8_offset_to_pointer(text, start) - text;
475                 maxpos = g_utf8_offset_to_pointer(text, end) - text;
476         } else {
477                 minpos = forward ? cursor : 0;
478                 maxpos = forward ? strlen(text)-1 : cursor;
479         }
480         g_free(text);
481
482         while(entry->priv->words[words_len])
483                 words_len++;
484
485         if (forward) {
486                 for(i=0; i < words_len; i++)
487                         if (entry->priv->word_ends[i] > minpos &&
488                             (misspelled = check_word(entry,
489                                         entry->priv->word_starts[i],
490                                         entry->priv->word_ends[i])))
491                                 break;
492         } else {
493                 for(i=words_len-1; i >= 0; i--)
494                         if (entry->priv->word_starts[i] < maxpos &&
495                             (misspelled = check_word(entry,
496                                         entry->priv->word_starts[i],
497                                         entry->priv->word_ends[i])))
498                                 break;
499         }
500         
501         return misspelled;
502 }
503
504 static gboolean check_word_cb(gpointer data)
505 {
506         ClawsSpellEntry *entry = (ClawsSpellEntry *)data;
507         gint start, end;
508         
509         get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character);
510         return check_word(entry, start, end);
511 }
512
513 static void replace_word_cb(gpointer data, const gchar *newword)
514 {
515         replace_word((ClawsSpellEntry *) data, newword);
516 }
517
518 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y, 
519                          gboolean *push_in, gpointer data)
520 {
521         ClawsSpellEntry *entry = (ClawsSpellEntry *) data;
522         GtkAspell *gtkaspell = entry->gtkaspell;
523         gint pango_offset, win_x, win_y, scr_x, scr_y, text_index, entry_x;
524         gchar *text;
525         GtkRequisition subject_rq;
526         PangoLayout *layout = gtk_entry_get_layout(GTK_ENTRY(entry));
527         PangoLayoutLine *line = pango_layout_get_lines(layout)->data;
528
529         gtk_widget_get_child_requisition(GTK_WIDGET(entry), &subject_rq);
530         
531         /* screen -> compose window coords */
532         gdk_window_get_origin(GTK_WIDGET(gtkaspell->parent_window)->window,
533                                 &scr_x, &scr_y);
534
535         /* compose window -> subject entry coords */
536         gtk_widget_translate_coordinates(GTK_WIDGET(entry),
537                         gtkaspell->parent_window, 0, 0, &win_x, &win_y);
538
539         text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
540         text_index = g_utf8_offset_to_pointer(text, gtkaspell->end_pos) - text;
541         g_free(text);
542
543         pango_offset = gtk_entry_text_index_to_layout_index(GTK_ENTRY(entry),
544                                         text_index);
545         pango_layout_line_index_to_x(line, pango_offset, TRUE, &entry_x);
546
547         *x = scr_x + win_x + PANGO_PIXELS(entry_x) + 8;
548         *y = scr_y + win_y + subject_rq.height;
549 }
550
551 void claws_spell_entry_context_set(ClawsSpellEntry *entry)
552 {
553         g_return_if_fail(CLAWS_IS_SPELL_ENTRY(entry));
554         g_return_if_fail(entry->gtkaspell != NULL);
555
556         entry->gtkaspell->ctx.set_position      = set_position;
557         entry->gtkaspell->ctx.set_menu_pos      = set_menu_pos;
558         entry->gtkaspell->ctx.find_misspelled   = find_misspelled_cb;
559         entry->gtkaspell->ctx.check_word        = check_word_cb;
560         entry->gtkaspell->ctx.replace_word      = replace_word_cb;
561         entry->gtkaspell->ctx.data              = (gpointer) entry;
562 }
563
564 static void claws_spell_entry_populate_popup(ClawsSpellEntry *entry, GtkMenu *menu,
565                                                 gpointer data)
566 {
567         GtkAspell *gtkaspell = entry->gtkaspell;
568         gint start, end;
569         gchar *word;
570         
571         if (gtkaspell == NULL)
572                 return;
573         
574         get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character);
575
576         if ((word = get_word(entry, start, end)) != NULL) {
577                 strncpy(gtkaspell->theword, word, GTKASPELLWORDSIZE - 1);
578                 g_free(word);
579         }
580
581         gtkaspell->misspelled = word_misspelled(entry, start, end);
582         
583         claws_spell_entry_context_set(entry);
584         gtkaspell_make_context_menu(menu, gtkaspell);
585 }
586
587 static void claws_spell_entry_changed(GtkEditable *editable, gpointer data)
588 {
589         ClawsSpellEntry *entry = CLAWS_SPELL_ENTRY(editable);
590
591         if (entry->gtkaspell == NULL)
592                 return;
593
594         if (entry->priv->words) {
595                 g_strfreev(entry->priv->words);
596                 g_free(entry->priv->word_starts);
597                 g_free(entry->priv->word_ends);
598         }
599         entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, 
600                         &entry->priv->word_starts, &entry->priv->word_ends);
601         if(entry->gtkaspell->check_while_typing == TRUE)
602                 claws_spell_entry_recheck_all(entry);
603 }
604
605 static void continue_check(gpointer *data)
606 {
607         ClawsSpellEntry *entry = (ClawsSpellEntry *)data;
608         GtkAspell *gtkaspell = entry->gtkaspell;
609         gint pos = gtk_editable_get_position(GTK_EDITABLE(entry));
610         
611         if (gtkaspell->misspelled && pos < gtkaspell->end_check_pos)
612                 gtkaspell->misspelled = gtkaspell_check_next_prev(gtkaspell, TRUE);
613         else
614                 gtkaspell->continue_check = NULL;
615 }
616
617 void claws_spell_entry_check_all(ClawsSpellEntry *entry)
618 {
619         gint start, end;
620         gchar *text;
621         
622         g_return_if_fail(CLAWS_IS_SPELL_ENTRY(entry));
623         g_return_if_fail(entry->gtkaspell != NULL);
624         
625         if (!gtk_editable_get_selection_bounds(GTK_EDITABLE(entry), &start, &end)) {
626                 text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);      
627                 
628                 start = 0;
629                 end = g_utf8_strlen(text, -1) - 1;
630                 
631                 g_free(text);
632         }
633
634         gtk_editable_set_position(GTK_EDITABLE(entry), start);
635         entry->gtkaspell->continue_check = continue_check;
636         entry->gtkaspell->end_check_pos  = end;
637
638         claws_spell_entry_context_set(entry);
639         entry->gtkaspell->misspelled = 
640                         gtkaspell_check_next_prev(entry->gtkaspell, TRUE);
641 }
642
643 void claws_spell_entry_check_backwards(ClawsSpellEntry *entry)
644 {
645         g_return_if_fail(CLAWS_IS_SPELL_ENTRY(entry));
646         g_return_if_fail(entry->gtkaspell != NULL);
647         
648         entry->gtkaspell->continue_check = NULL;
649         claws_spell_entry_context_set(entry);
650         gtkaspell_check_next_prev(entry->gtkaspell, FALSE);
651 }
652
653 void claws_spell_entry_check_forwards_go(ClawsSpellEntry *entry)
654 {
655         g_return_if_fail(CLAWS_IS_SPELL_ENTRY(entry));
656         g_return_if_fail(entry->gtkaspell != NULL);
657
658         entry->gtkaspell->continue_check = NULL;
659         claws_spell_entry_context_set(entry);
660         gtkaspell_check_next_prev(entry->gtkaspell, TRUE);
661 }
662
663 #endif  /* USE_ENCHANT */