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