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