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