1 /* gtkaspell - a spell-checking addon for GtkText
2 * Copyright (c) 2000 Evan Martin (original code for ispell).
3 * Copyright (c) 2002 Melvin Hadasht.
4 * Copyright (C) 2001-2013 the Claws Mail Team
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 */
19 /*
20 * Stuphead: (C) 2000,2001 Grigroy Bakunov, Sergey Pinaev
21 * Adapted for Sylpheed (Claws) (c) 2001-2002 by Hiroyuki Yamamoto &
22 * The Claws Mail Team.
23 * Adapted for pspell (c) 2001-2002 Melvin Hadasht
24 * Adapted for GNU/aspell (c) 2002 Melvin Hadasht
25 */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #include "claws-features.h"
30 #endif
31
32 #ifdef USE_ENCHANT
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #if HAVE_SYS_WAIT_H
39 # include <sys/wait.h>
40 #endif
41 #include <signal.h>
42 #include <ctype.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <sys/time.h>
46 #include <fcntl.h>
47 #include <time.h>
48 #include <dirent.h>
49
50 #include <glib.h>
51 #include <glib/gi18n.h>
52
53 #include <gtk/gtk.h>
54 #include <gdk/gdk.h>
55 #include <gdk/gdkkeysyms.h>
56
57 #include "utils.h"
58 #include "alertpanel.h"
59 #include "gtkaspell.h"
60 #include "gtk/gtkutils.h"
61 #include "gtk/combobox.h"
62
63 #define ASPELL_FASTMODE 1
64 #define ASPELL_NORMALMODE 2
65 #define ASPELL_BADSPELLERMODE 3
66
67 /* size of the text buffer used in various word-processing routines. */
68 #define BUFSIZE 1024
69
70 /* number of suggestions to display on each menu. */
71 #define MENUCOUNT 15
72
73 enum {
74 SET_GTKASPELL_NAME = 0,
75 SET_GTKASPELL_FULLNAME = 1,
76 SET_GTKASPELL_SIZE
77 };
78
79 typedef struct _GtkAspellCheckers {
80 GSList *checkers;
81 GSList *dictionary_list;
82 gchar *error_message;
83 } GtkAspellCheckers;
84
85 /******************************************************************************/
86
87 static GtkAspellCheckers *gtkaspellcheckers;
88
89 /* Error message storage */
90 static void gtkaspell_checkers_error_message (gchar *message);
91
92 /* Callbacks */
93 static gboolean key_press_cb (GtkWidget *text_view,
94 GdkEventKey *event,
95 GtkAspell *gtkaspell);
96 static void entry_insert_cb (GtkTextBuffer *textbuf,
97 GtkTextIter *iter,
98 gchar *newtext,
99 gint len,
100 GtkAspell *gtkaspell);
101 static void entry_delete_cb (GtkTextBuffer *textbuf,
102 GtkTextIter *startiter,
103 GtkTextIter *enditer,
104 GtkAspell *gtkaspell);
105 /*static gint button_press_intercept_cb (GtkTextView *gtktext,
106 GdkEvent *e,
107 GtkAspell *gtkaspell);
108 */
109 static void button_press_intercept_cb(GtkTextView *gtktext,
110 GtkMenu *menu, GtkAspell *gtkaspell);
111
112 /* Checker creation */
113 static GtkAspeller* gtkaspeller_new (Dictionary *dict);
114 static GtkAspeller* gtkaspeller_real_new (Dictionary *dict);
115 static GtkAspeller* gtkaspeller_delete (GtkAspeller *gtkaspeller);
116 static GtkAspeller* gtkaspeller_real_delete (GtkAspeller *gtkaspeller);
117
118 /* Checker configuration */
119 static EnchantDict *set_dictionary (EnchantBroker *broker,
120 Dictionary *dict);
121 static void set_use_both_cb (GtkMenuItem *w,
122 GtkAspell *gtkaspell);
123
124 /* Checker actions */
125 static gboolean check_at (GtkAspell *gtkaspell,
126 int from_pos);
127 static gboolean check_at_cb (gpointer data);
128 static GList* misspelled_suggest (GtkAspell *gtkaspell,
129 gchar *word);
130 static gboolean find_misspelled_cb (gpointer data,
131 gboolean forward);
132 static void add_word_to_session_cb (GtkWidget *w,
133 gpointer data);
134 static void add_word_to_personal_cb (GtkWidget *w,
135 gpointer data);
136 static void replace_with_create_dialog_cb (GtkWidget *w,
137 gpointer data);
138 static void replace_with_supplied_word_cb (GtkWidget *w,
139 GtkAspell *gtkaspell);
140 static void replace_word_cb (GtkWidget *w,
141 gpointer data);
142 static void replace_real_word (GtkAspell *gtkaspell,
143 const gchar *newword);
144 static void replace_real_word_cb (gpointer data,
145 const gchar *newword);
146 static void check_with_alternate_cb (GtkWidget *w,
147 gpointer data);
148 static void toggle_check_while_typing_cb (GtkWidget *w,
149 gpointer data);
150
151 /* Menu creation */
152 static GSList* make_sug_menu (GtkAspell *gtkaspell);
153 static GSList * populate_submenu (GtkAspell *gtkaspell);
154 GSList* gtkaspell_make_config_menu (GtkAspell *gtkaspell);
155 static void set_menu_pos (GtkMenu *menu,
156 gint *x,
157 gint *y,
158 gboolean *push_in,
159 gpointer data);
160 /* Other menu callbacks */
161 static gboolean aspell_key_pressed (GtkWidget *widget,
162 GdkEventKey *event,
163 GtkAspell *gtkaspell);
164 static void change_dict_cb (GtkWidget *w,
165 GtkAspell *gtkaspell);
166 static void switch_to_alternate_cb (GtkWidget *w,
167 gpointer data);
168
169 /* Misc. helper functions */
170 static void set_point_continue (GtkAspell *gtkaspell);
171 static void continue_check (gpointer *gtkaspell);
172 static gboolean iswordsep (gunichar c);
173 static gunichar get_text_index_whar (GtkAspell *gtkaspell,
174 int pos);
175 static gboolean get_word_from_pos (GtkAspell *gtkaspell,
176 gint pos,
177 char* buf,
178 gint buflen,
179 gint *pstart,
180 gint *pend);
181 static void allocate_color (GtkAspell *gtkaspell,
182 gint rgbvalue);
183 static void change_color (GtkAspell *gtkaspell,
184 gint start,
185 gint end,
186 gchar *newtext,
187 GdkColor *color);
188 static gint compare_dict (Dictionary *a,
189 Dictionary *b);
190 static void dictionary_delete (Dictionary *dict);
191 static Dictionary * dictionary_dup (const Dictionary *dict);
192 static void reset_theword_data (GtkAspell *gtkaspell);
193 static void free_checkers (gpointer elt,
194 gpointer data);
195
196 static void destroy_menu(GtkWidget *widget, gpointer user_data);
197
198 /******************************************************************************/
199 static gint get_textview_buffer_charcount(GtkTextView *view);
200
201 static void gtkaspell_free_dictionary_list (GSList *list);
202 static GSList* gtkaspell_get_dictionary_list (gint refresh);
203
204 static void gtkaspell_uncheck_all (GtkAspell *gtkaspell);
205
get_textview_buffer_charcount(GtkTextView * view)206 static gint get_textview_buffer_charcount(GtkTextView *view)
207 {
208 GtkTextBuffer *buffer;
209
210 cm_return_val_if_fail(view, 0);
211
212 buffer = gtk_text_view_get_buffer(view);
213 cm_return_val_if_fail(buffer, 0);
214
215 return gtk_text_buffer_get_char_count(buffer);
216 }
get_textview_buffer_offset(GtkTextView * view)217 static gint get_textview_buffer_offset(GtkTextView *view)
218 {
219 GtkTextBuffer * buffer;
220 GtkTextMark * mark;
221 GtkTextIter iter;
222
223 cm_return_val_if_fail(view, 0);
224
225 buffer = gtk_text_view_get_buffer(view);
226 cm_return_val_if_fail(buffer, 0);
227
228 mark = gtk_text_buffer_get_insert(buffer);
229 cm_return_val_if_fail(mark, 0);
230
231 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
232
233 return gtk_text_iter_get_offset(&iter);
234 }
set_textview_buffer_offset(GtkTextView * view,gint offset)235 static void set_textview_buffer_offset(GtkTextView *view, gint offset)
236 {
237 GtkTextBuffer *buffer;
238 GtkTextIter iter;
239
240 cm_return_if_fail(view);
241
242 buffer = gtk_text_view_get_buffer(view);
243 cm_return_if_fail(buffer);
244
245 gtk_text_buffer_get_iter_at_offset(buffer, &iter, offset);
246 gtk_text_buffer_place_cursor(buffer, &iter);
247 }
248 /******************************************************************************/
249
gtkaspell_checkers_init(void)250 void gtkaspell_checkers_init(void)
251 {
252 gtkaspellcheckers = g_new(GtkAspellCheckers, 1);
253 gtkaspellcheckers->checkers = NULL;
254 gtkaspellcheckers->dictionary_list = NULL;
255 gtkaspellcheckers->error_message = NULL;
256 }
257
gtkaspell_checkers_quit(void)258 void gtkaspell_checkers_quit(void)
259 {
260 GSList *checkers;
261 GSList *dict_list;
262
263 if (gtkaspellcheckers == NULL)
264 return;
265
266 if ((checkers = gtkaspellcheckers->checkers)) {
267 debug_print("Aspell: number of running checkers to delete %d\n",
268 g_slist_length(checkers));
269
270 g_slist_foreach(checkers, free_checkers, NULL);
271 g_slist_free(checkers);
272 gtkaspellcheckers->checkers = NULL;
273 }
274
275 if ((dict_list = gtkaspellcheckers->dictionary_list)) {
276 debug_print("Aspell: number of dictionaries to delete %d\n",
277 g_slist_length(dict_list));
278
279 gtkaspell_free_dictionary_list(dict_list);
280 gtkaspellcheckers->dictionary_list = NULL;
281 }
282
283 g_free(gtkaspellcheckers->error_message);
284 gtkaspellcheckers->error_message = NULL;
285 return;
286 }
287
gtkaspell_checkers_error_message(gchar * message)288 static void gtkaspell_checkers_error_message (gchar *message)
289 {
290 gchar *tmp;
291 if (gtkaspellcheckers->error_message) {
292 tmp = g_strdup_printf("%s\n%s",
293 gtkaspellcheckers->error_message,
294 message);
295 g_free(message);
296 g_free(gtkaspellcheckers->error_message);
297 gtkaspellcheckers->error_message = tmp;
298 } else
299 gtkaspellcheckers->error_message = message;
300
301
302 }
303
gtkaspell_checkers_strerror(void)304 const char *gtkaspell_checkers_strerror(void)
305 {
306 cm_return_val_if_fail(gtkaspellcheckers, "");
307 return gtkaspellcheckers->error_message;
308 }
309
gtkaspell_checkers_reset_error(void)310 void gtkaspell_checkers_reset_error(void)
311 {
312 cm_return_if_fail(gtkaspellcheckers);
313
314 g_free(gtkaspellcheckers->error_message);
315
316 gtkaspellcheckers->error_message = NULL;
317 }
318
gtkaspell_new(const gchar * dictionary,const gchar * alt_dictionary,const gchar * encoding,gint misspelled_color,gboolean check_while_typing,gboolean recheck_when_changing_dict,gboolean use_alternate,gboolean use_both_dicts,GtkTextView * gtktext,GtkWindow * parent_win,void (dict_changed_cb)(void * data),void (* spell_menu_cb)(void * data),void * data)319 GtkAspell *gtkaspell_new(const gchar *dictionary,
320 const gchar *alt_dictionary,
321 const gchar *encoding, /* unused */
322 gint misspelled_color,
323 gboolean check_while_typing,
324 gboolean recheck_when_changing_dict,
325 gboolean use_alternate,
326 gboolean use_both_dicts,
327 GtkTextView *gtktext,
328 GtkWindow *parent_win,
329 void (dict_changed_cb)(void *data),
330 void (*spell_menu_cb)(void *data),
331 void *data)
332 {
333 Dictionary *dict;
334 GtkAspell *gtkaspell;
335 GtkAspeller *gtkaspeller;
336 GtkTextBuffer *buffer;
337
338 cm_return_val_if_fail(gtktext, NULL);
339 if (!dictionary || !*dictionary) {
340 gtkaspell_checkers_error_message(
341 g_strdup(_("No dictionary selected.")));
342 return NULL;
343 }
344
345 buffer = gtk_text_view_get_buffer(gtktext);
346
347 dict = g_new0(Dictionary, 1);
348 if (strrchr(dictionary, '/')) {
349 dict->fullname = g_strdup(strrchr(dictionary, '/')+1);
350 dict->dictname = g_strdup(strrchr(dictionary, '/')+1);
351 } else {
352 dict->fullname = g_strdup(dictionary);
353 dict->dictname = g_strdup(dictionary);
354 }
355
356 if (strchr(dict->fullname, '-')) {
357 *(strchr(dict->fullname, '-')) = '\0';
358 *(strchr(dict->dictname, '-')) = '\0';
359 }
360 gtkaspeller = gtkaspeller_new(dict);
361 dictionary_delete(dict);
362
363 if (!gtkaspeller) {
364 gtkaspell_checkers_error_message(
365 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary));
366 return NULL;
367 }
368
369 gtkaspell = g_new0(GtkAspell, 1);
370
371 gtkaspell->gtkaspeller = gtkaspeller;
372
373 if (use_alternate && alt_dictionary && *alt_dictionary) {
374 Dictionary *alt_dict;
375 GtkAspeller *alt_gtkaspeller;
376
377 alt_dict = g_new0(Dictionary, 1);
378 if (strrchr(alt_dictionary, '/')) {
379 alt_dict->fullname = g_strdup(strrchr(alt_dictionary, '/')+1);
380 alt_dict->dictname = g_strdup(strrchr(alt_dictionary, '/')+1);
381 } else {
382 alt_dict->fullname = g_strdup(alt_dictionary);
383 alt_dict->dictname = g_strdup(alt_dictionary);
384 }
385 if (strchr(alt_dict->fullname, '-')) {
386 *(strchr(alt_dict->fullname, '-')) = '\0';
387 *(strchr(alt_dict->dictname, '-')) = '\0';
388 }
389
390 alt_gtkaspeller = gtkaspeller_new(alt_dict);
391 dictionary_delete(alt_dict);
392
393 if (!alt_gtkaspeller) {
394 gtkaspell_checkers_error_message(
395 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary));
396 gtkaspeller_delete(gtkaspeller);
397 g_free(gtkaspell);
398 return NULL;
399 }
400
401 gtkaspell->alternate_speller = alt_gtkaspeller;
402 } else {
403 gtkaspell->alternate_speller = NULL;
404 }
405
406 gtkaspell->theword[0] = 0x00;
407 gtkaspell->start_pos = 0;
408 gtkaspell->end_pos = 0;
409 gtkaspell->orig_pos = -1;
410 gtkaspell->end_check_pos = -1;
411 gtkaspell->misspelled = -1;
412 gtkaspell->check_while_typing = check_while_typing;
413 gtkaspell->recheck_when_changing_dict = recheck_when_changing_dict;
414 gtkaspell->continue_check = NULL;
415 gtkaspell->replace_entry = NULL;
416 gtkaspell->gtktext = gtktext;
417 gtkaspell->max_sug = -1;
418 gtkaspell->suggestions_list = NULL;
419 gtkaspell->use_alternate = use_alternate;
420 gtkaspell->use_both_dicts = use_both_dicts;
421 gtkaspell->parent_window = GTK_WIDGET(parent_win);
422 gtkaspell->dict_changed_cb = dict_changed_cb;
423 gtkaspell->menu_changed_cb = spell_menu_cb;
424 gtkaspell->menu_changed_data = data;
425
426 allocate_color(gtkaspell, misspelled_color);
427
428 g_signal_connect(G_OBJECT(gtktext), "key_press_event",
429 G_CALLBACK(key_press_cb), gtkaspell);
430 g_signal_connect_after(G_OBJECT(buffer), "insert-text",
431 G_CALLBACK(entry_insert_cb), gtkaspell);
432 g_signal_connect_after(G_OBJECT(buffer), "delete-range",
433 G_CALLBACK(entry_delete_cb), gtkaspell);
434 /*g_signal_connect(G_OBJECT(gtktext), "button-press-event",
435 G_CALLBACK(button_press_intercept_cb),
436 gtkaspell);*/
437 g_signal_connect(G_OBJECT(gtktext), "populate-popup",
438 G_CALLBACK(button_press_intercept_cb), gtkaspell);
439
440 debug_print("Aspell: created gtkaspell %p\n", gtkaspell);
441
442 return gtkaspell;
443 }
444
gtkaspell_delete(GtkAspell * gtkaspell)445 void gtkaspell_delete(GtkAspell *gtkaspell)
446 {
447 GtkTextView *gtktext = gtkaspell->gtktext;
448
449 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
450 G_CALLBACK(key_press_cb),
451 gtkaspell);
452 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
453 G_CALLBACK(entry_insert_cb),
454 gtkaspell);
455 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
456 G_CALLBACK(entry_delete_cb),
457 gtkaspell);
458 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext),
459 G_CALLBACK(button_press_intercept_cb),
460 gtkaspell);
461
462 gtkaspell_uncheck_all(gtkaspell);
463
464 gtkaspeller_delete(gtkaspell->gtkaspeller);
465
466 if (gtkaspell->alternate_speller)
467 gtkaspeller_delete(gtkaspell->alternate_speller);
468
469 if (gtkaspell->suggestions_list)
470 gtkaspell_free_suggestions_list(gtkaspell);
471
472 debug_print("Aspell: deleting gtkaspell %p\n", gtkaspell);
473
474 g_free(gtkaspell);
475
476 gtkaspell = NULL;
477 }
478
gtkaspell_dict_changed(GtkAspell * gtkaspell)479 void gtkaspell_dict_changed(GtkAspell *gtkaspell)
480 {
481 if(!gtkaspell || !gtkaspell->dict_changed_cb ||
482 !gtkaspell->menu_changed_data)
483 return;
484
485 gtkaspell->dict_changed_cb(gtkaspell->menu_changed_data);
486 }
487
key_press_cb(GtkWidget * text_view,GdkEventKey * event,GtkAspell * gtkaspell)488 static gboolean key_press_cb (GtkWidget *text_view,
489 GdkEventKey *event,
490 GtkAspell *gtkaspell)
491 {
492 gint pos;
493
494 cm_return_val_if_fail(gtkaspell->gtkaspeller->speller, FALSE);
495
496 if (!gtkaspell->check_while_typing)
497 return FALSE;
498
499 switch (event->keyval) {
500 case GDK_KEY_Home:
501 case GDK_KEY_Left:
502 case GDK_KEY_Up:
503 case GDK_KEY_Right:
504 case GDK_KEY_Down:
505 case GDK_KEY_Page_Up:
506 case GDK_KEY_Page_Down:
507 case GDK_KEY_End:
508 case GDK_KEY_Begin:
509 pos = get_textview_buffer_offset(GTK_TEXT_VIEW(text_view));
510 if (pos > 0)
511 check_at(gtkaspell, pos - 1);
512 else
513 check_at(gtkaspell, pos);
514 break;
515 default:
516 break;
517 }
518
519 return FALSE;
520 }
521
entry_insert_cb(GtkTextBuffer * textbuf,GtkTextIter * iter,gchar * newtext,gint len,GtkAspell * gtkaspell)522 static void entry_insert_cb(GtkTextBuffer *textbuf,
523 GtkTextIter *iter,
524 gchar *newtext,
525 gint len,
526 GtkAspell *gtkaspell)
527 {
528 guint pos;
529
530 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
531
532 if (!gtkaspell->check_while_typing)
533 return;
534
535 pos = gtk_text_iter_get_offset(iter);
536
537 if (iswordsep(g_utf8_get_char(newtext))) {
538 /* did we just end a word? */
539 if (pos >= 2)
540 check_at(gtkaspell, pos - 2);
541
542 /* did we just split a word? */
543 if (pos < gtk_text_buffer_get_char_count(textbuf))
544 check_at(gtkaspell, pos + 1);
545 } else {
546 /* check as they type, *except* if they're typing at the end (the most
547 * common case).
548 */
549 if (pos < gtk_text_buffer_get_char_count(textbuf) &&
550 !iswordsep(get_text_index_whar(gtkaspell, pos))) {
551 check_at(gtkaspell, pos - 1);
552 }
553 }
554 }
555
entry_delete_cb(GtkTextBuffer * textbuf,GtkTextIter * startiter,GtkTextIter * enditer,GtkAspell * gtkaspell)556 static void entry_delete_cb(GtkTextBuffer *textbuf,
557 GtkTextIter *startiter,
558 GtkTextIter *enditer,
559 GtkAspell *gtkaspell)
560 {
561 int origpos;
562 gint start;
563
564 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
565
566 if (!gtkaspell->check_while_typing)
567 return;
568
569 start = gtk_text_iter_get_offset(startiter);
570 origpos = get_textview_buffer_offset(gtkaspell->gtktext);
571 if (start) {
572 check_at(gtkaspell, start - 1);
573 check_at(gtkaspell, start);
574 }
575
576 set_textview_buffer_offset(gtkaspell->gtktext, origpos);
577 /* this is to *UNDO* the selection, in case they were holding shift
578 * while hitting backspace. */
579 /* needed with textview ??? */
580 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
581 }
582
gtkaspell_make_context_menu(GtkMenu * menu,GtkAspell * gtkaspell)583 void gtkaspell_make_context_menu(GtkMenu *menu, GtkAspell *gtkaspell)
584 {
585 GtkMenuItem *menuitem;
586 GSList *spell_menu = NULL;
587 GSList *items;
588 gboolean suggest = FALSE;
589
590 if (gtkaspell->misspelled &&
591 misspelled_suggest(gtkaspell, gtkaspell->theword)) {
592 spell_menu = make_sug_menu(gtkaspell);
593 suggest = TRUE;
594 }
595 if (!spell_menu)
596 spell_menu = gtkaspell_make_config_menu(gtkaspell);
597
598 menuitem = GTK_MENU_ITEM(gtk_separator_menu_item_new());
599 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
600 gtk_widget_show(GTK_WIDGET(menuitem));
601
602 spell_menu = g_slist_reverse(spell_menu);
603 for (items = spell_menu;
604 items; items = items->next) {
605 menuitem = GTK_MENU_ITEM(items->data);
606 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
607 gtk_widget_show(GTK_WIDGET(menuitem));
608 }
609 g_slist_free(spell_menu);
610
611 g_signal_connect(G_OBJECT(menu), "deactivate",
612 G_CALLBACK(destroy_menu),
613 gtkaspell);
614 if (suggest)
615 g_signal_connect(G_OBJECT(menu),
616 "key_press_event",
617 G_CALLBACK(aspell_key_pressed),
618 gtkaspell);
619 }
620
set_position_cb(gpointer data,gint pos)621 static void set_position_cb(gpointer data, gint pos)
622 {
623 GtkAspell *gtkaspell = (GtkAspell *) data;
624 set_textview_buffer_offset(gtkaspell->gtktext, pos);
625 }
626
gtkaspell_context_set(GtkAspell * gtkaspell)627 void gtkaspell_context_set(GtkAspell *gtkaspell)
628 {
629 gtkaspell->ctx.set_position = set_position_cb;
630 gtkaspell->ctx.set_menu_pos = set_menu_pos;
631 gtkaspell->ctx.find_misspelled = find_misspelled_cb;
632 gtkaspell->ctx.check_word = check_at_cb;
633 gtkaspell->ctx.replace_word = replace_real_word_cb;
634 gtkaspell->ctx.data = (gpointer) gtkaspell;
635 }
636
button_press_intercept_cb(GtkTextView * gtktext,GtkMenu * menu,GtkAspell * gtkaspell)637 static void button_press_intercept_cb(GtkTextView *gtktext,
638 GtkMenu *menu, GtkAspell *gtkaspell)
639 {
640 gtktext = gtkaspell->gtktext;
641 gtkaspell->orig_pos = get_textview_buffer_offset(gtktext);
642 gtkaspell->misspelled = check_at(gtkaspell, gtkaspell->orig_pos);
643
644 gtkaspell_context_set(gtkaspell);
645 gtkaspell_make_context_menu(menu, gtkaspell);
646 }
647 /* Checker creation */
gtkaspeller_new(Dictionary * dictionary)648 static GtkAspeller *gtkaspeller_new(Dictionary *dictionary)
649 {
650 GtkAspeller *gtkaspeller = NULL;
651 GtkAspeller *tmp;
652 Dictionary *dict;
653
654 cm_return_val_if_fail(gtkaspellcheckers, NULL);
655
656 cm_return_val_if_fail(dictionary, NULL);
657
658 if (dictionary->dictname == NULL)
659 gtkaspell_checkers_error_message(
660 g_strdup(_("No dictionary selected.")));
661
662 cm_return_val_if_fail(dictionary->fullname, NULL);
663
664 dict = dictionary_dup(dictionary);
665
666 tmp = g_new0(GtkAspeller, 1);
667 tmp->dictionary = dict;
668
669 g_free(tmp);
670
671 if ((gtkaspeller = gtkaspeller_real_new(dict)) != NULL) {
672 gtkaspellcheckers->checkers = g_slist_append(
673 gtkaspellcheckers->checkers,
674 gtkaspeller);
675
676 debug_print("Aspell: Created a new gtkaspeller %p\n",
677 gtkaspeller);
678 } else {
679 dictionary_delete(dict);
680
681 debug_print("Aspell: Could not create spell checker.\n");
682 }
683
684 debug_print("Aspell: number of existing checkers %d\n",
685 g_slist_length(gtkaspellcheckers->checkers));
686
687 return gtkaspeller;
688 }
689
gtkaspeller_real_new(Dictionary * dict)690 static GtkAspeller *gtkaspeller_real_new(Dictionary *dict)
691 {
692 GtkAspeller *gtkaspeller;
693 EnchantBroker *broker;
694 EnchantDict *speller;
695
696 cm_return_val_if_fail(gtkaspellcheckers, NULL);
697 cm_return_val_if_fail(dict, NULL);
698
699 gtkaspeller = g_new(GtkAspeller, 1);
700
701 gtkaspeller->dictionary = dict;
702
703 broker = enchant_broker_init();
704
705 if (!broker) {
706 gtkaspell_checkers_error_message(
707 g_strdup(_("Couldn't initialize Enchant broker.")));
708 g_free(gtkaspeller);
709 return NULL;
710 }
711 if ((speller = set_dictionary(broker, dict)) == NULL) {
712 gtkaspell_checkers_error_message(
713 g_strdup_printf(_("Couldn't initialize %s dictionary:"), dict->fullname));
714 gtkaspell_checkers_error_message(
715 g_strdup(enchant_broker_get_error(broker)));
716 g_free(gtkaspeller);
717 return NULL;
718 }
719 gtkaspeller->speller = speller;
720 gtkaspeller->broker = broker;
721
722 return gtkaspeller;
723 }
724
gtkaspeller_delete(GtkAspeller * gtkaspeller)725 static GtkAspeller *gtkaspeller_delete(GtkAspeller *gtkaspeller)
726 {
727 cm_return_val_if_fail(gtkaspellcheckers, NULL);
728
729 gtkaspellcheckers->checkers =
730 g_slist_remove(gtkaspellcheckers->checkers,
731 gtkaspeller);
732
733 debug_print("Aspell: Deleting gtkaspeller %p.\n",
734 gtkaspeller);
735
736 gtkaspeller_real_delete(gtkaspeller);
737
738 debug_print("Aspell: number of existing checkers %d\n",
739 g_slist_length(gtkaspellcheckers->checkers));
740
741 return gtkaspeller;
742 }
743
gtkaspeller_real_delete(GtkAspeller * gtkaspeller)744 static GtkAspeller *gtkaspeller_real_delete(GtkAspeller *gtkaspeller)
745 {
746 cm_return_val_if_fail(gtkaspeller, NULL);
747 cm_return_val_if_fail(gtkaspeller->speller, NULL);
748
749 enchant_broker_free_dict(gtkaspeller->broker, gtkaspeller->speller);
750 enchant_broker_free(gtkaspeller->broker);
751
752 dictionary_delete(gtkaspeller->dictionary);
753
754 debug_print("Aspell: gtkaspeller %p deleted.\n",
755 gtkaspeller);
756
757 g_free(gtkaspeller);
758
759 return NULL;
760 }
761
762 /*****************************************************************************/
763 /* Checker configuration */
764
set_dictionary(EnchantBroker * broker,Dictionary * dict)765 static EnchantDict *set_dictionary(EnchantBroker *broker, Dictionary *dict)
766 {
767 cm_return_val_if_fail(broker, FALSE);
768 cm_return_val_if_fail(dict, FALSE);
769
770 return enchant_broker_request_dict(broker, dict->dictname );
771 }
772
set_use_both_cb(GtkMenuItem * w,GtkAspell * gtkaspell)773 static void set_use_both_cb(GtkMenuItem *w, GtkAspell *gtkaspell)
774 {
775 gtkaspell->use_both_dicts = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w));
776 gtkaspell_dict_changed(gtkaspell);
777
778 if (gtkaspell->menu_changed_cb)
779 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
780 }
781
782 /* misspelled_suggest() - Create a suggestion list for word */
misspelled_suggest(GtkAspell * gtkaspell,gchar * word)783 static GList *misspelled_suggest(GtkAspell *gtkaspell, gchar *word)
784 {
785 GList *list = NULL;
786 char **suggestions;
787 size_t num_sug, i;
788 cm_return_val_if_fail(word, NULL);
789
790 if (*word == 0)
791 return NULL;
792
793 gtkaspell_free_suggestions_list(gtkaspell);
794
795 suggestions = enchant_dict_suggest(gtkaspell->gtkaspeller->speller, word, strlen(word), &num_sug);
796 list = g_list_append(list, g_strdup(word));
797 if (suggestions == NULL || num_sug == 0) {
798 gtkaspell->max_sug = -1;
799 gtkaspell->suggestions_list = list;
800 return list;
801 }
802 for (i = 0; i < num_sug; i++)
803 list = g_list_append(list, g_strdup((gchar *)suggestions[i]));
804
805 gtkaspell->max_sug = num_sug - 1;
806 gtkaspell->suggestions_list = list;
807 enchant_dict_free_string_list(gtkaspell->gtkaspeller->speller, suggestions);
808 return list;
809 }
810
811 /* misspelled_test() - Just test if word is correctly spelled */
gtkaspell_misspelled_test(GtkAspell * gtkaspell,char * word)812 int gtkaspell_misspelled_test(GtkAspell *gtkaspell, char *word)
813 {
814 gint result = 0;
815 cm_return_val_if_fail(word, 0);
816
817 if (*word == 0)
818 return 0;
819
820 result = enchant_dict_check(gtkaspell->gtkaspeller->speller, word, strlen(word));
821
822 if (result && gtkaspell->use_both_dicts && gtkaspell->alternate_speller) {
823 gtkaspell_use_alternate_dict(gtkaspell);
824 result = enchant_dict_check(gtkaspell->gtkaspeller->speller, word, strlen(word));
825 gtkaspell_use_alternate_dict(gtkaspell);
826 }
827 return (result && strcasecmp(word, "sylpheed") &&
828 strcasecmp(word, "claws-mail"));
829 }
830
831
iswordsep(gunichar c)832 static gboolean iswordsep(gunichar c)
833 {
834 return (g_unichar_isspace(c) || g_unichar_ispunct(c)) && c != (gunichar)'\'';
835 }
836
get_text_index_whar(GtkAspell * gtkaspell,int pos)837 static gunichar get_text_index_whar(GtkAspell *gtkaspell, int pos)
838 {
839 GtkTextView *view = gtkaspell->gtktext;
840 GtkTextBuffer *buffer = gtk_text_view_get_buffer(view);
841 GtkTextIter start, end;
842 gchar *utf8chars;
843 gunichar a = '\0';
844
845 gtk_text_buffer_get_iter_at_offset(buffer, &start, pos);
846 gtk_text_buffer_get_iter_at_offset(buffer, &end, pos+1);
847
848 utf8chars = gtk_text_iter_get_text(&start, &end);
849 a = g_utf8_get_char(utf8chars);
850 g_free(utf8chars);
851
852 return a;
853 }
854
855 /* get_word_from_pos () - return the word pointed to. */
856 /* Handles correctly the quotes. */
get_word_from_pos(GtkAspell * gtkaspell,gint pos,char * buf,gint buflen,gint * pstart,gint * pend)857 static gboolean get_word_from_pos(GtkAspell *gtkaspell, gint pos,
858 char* buf, gint buflen,
859 gint *pstart, gint *pend)
860 {
861
862 /* TODO : when correcting a word into quotes, change the color of */
863 /* the quotes too, as may be they were highlighted before. To do */
864 /* this, we can use two others pointers that points to the whole */
865 /* word including quotes. */
866
867 gint start;
868 gint end;
869
870 gunichar c;
871 GtkTextView *gtktext;
872
873 gtktext = gtkaspell->gtktext;
874 if (iswordsep(get_text_index_whar(gtkaspell, pos)))
875 return FALSE;
876
877 /* The apostrophe character is somtimes used for quotes
878 * So include it in the word only if it is not surrounded
879 * by other characters.
880 */
881
882 for (start = pos; start >= 0; --start) {
883 c = get_text_index_whar(gtkaspell, start);
884 if (c == (gunichar)'\'') {
885 if (start > 0) {
886 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
887 start - 1))
888 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
889 start - 1))
890 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
891 start - 1))) {
892 /* start_quote = TRUE; */
893 break;
894 }
895 }
896 else {
897 /* start_quote = TRUE; */
898 break;
899 }
900 }
901 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
902 break;
903 }
904
905 start++;
906
907 for (end = pos; end < get_textview_buffer_charcount(gtktext); end++) {
908 c = get_text_index_whar(gtkaspell, end);
909 if (c == (gunichar)'\'') {
910 if (end < get_textview_buffer_charcount(gtktext)) {
911 if (g_unichar_isspace(get_text_index_whar(gtkaspell,
912 end + 1))
913 || g_unichar_ispunct(get_text_index_whar(gtkaspell,
914 end + 1))
915 || g_unichar_isdigit(get_text_index_whar(gtkaspell,
916 end + 1))) {
917 /* end_quote = TRUE; */
918 break;
919 }
920 }
921 else {
922 /* end_quote = TRUE; */
923 break;
924 }
925 }
926 else if (g_unichar_isspace(c) || g_unichar_ispunct(c) || g_unichar_isdigit(c))
927 break;
928 }
929
930 if (pstart)
931 *pstart = start;
932 if (pend)
933 *pend = end;
934
935 if (buf) {
936 if (end - start < buflen) {
937 GtkTextIter iterstart, iterend;
938 gchar *tmp;
939 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtktext);
940 gtk_text_buffer_get_iter_at_offset(buffer, &iterstart, start);
941 gtk_text_buffer_get_iter_at_offset(buffer, &iterend, end);
942 tmp = gtk_text_buffer_get_text(buffer, &iterstart, &iterend, FALSE);
943 strncpy(buf, tmp, buflen-1);
944 buf[buflen-1]='\0';
945 g_free(tmp);
946 } else
947 return FALSE;
948 }
949
950 return TRUE;
951 }
952
check_at(GtkAspell * gtkaspell,gint from_pos)953 static gboolean check_at(GtkAspell *gtkaspell, gint from_pos)
954 {
955 gint start, end;
956 char buf[GTKASPELLWORDSIZE];
957
958 cm_return_val_if_fail(from_pos >= 0, FALSE);
959
960 if (!get_word_from_pos(gtkaspell, from_pos, buf, sizeof(buf),
961 &start, &end))
962 return FALSE;
963
964 if (gtkaspell_misspelled_test(gtkaspell, buf)) {
965 strncpy(gtkaspell->theword, (gchar *)buf, GTKASPELLWORDSIZE - 1);
966 gtkaspell->theword[GTKASPELLWORDSIZE - 1] = 0;
967 gtkaspell->start_pos = start;
968 gtkaspell->end_pos = end;
969 gtkaspell_free_suggestions_list(gtkaspell);
970
971 change_color(gtkaspell, start, end, (gchar *)buf, &(gtkaspell->highlight));
972 return TRUE;
973 } else {
974 change_color(gtkaspell, start, end, (gchar *)buf, NULL);
975 return FALSE;
976 }
977 }
978
check_at_cb(gpointer data)979 static gboolean check_at_cb(gpointer data)
980 {
981 GtkAspell *gtkaspell = (GtkAspell *)data;
982 return check_at(gtkaspell, gtkaspell->start_pos);
983 }
984
find_misspelled_cb(gpointer data,gboolean forward)985 static gboolean find_misspelled_cb(gpointer data, gboolean forward)
986 {
987 GtkAspell *gtkaspell = (GtkAspell *)data;
988 gboolean misspelled;
989 gint pos;
990 gint minpos;
991 gint maxpos;
992 gint direc = -1;
993
994 minpos = 0;
995 maxpos = gtkaspell->end_check_pos;
996
997 if (forward) {
998 minpos = -1;
999 direc = 1;
1000 maxpos--;
1001 }
1002
1003 pos = get_textview_buffer_offset(gtkaspell->gtktext);
1004 gtkaspell->orig_pos = pos;
1005 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1006 pos > minpos && pos <= maxpos)
1007 pos += direc;
1008 while (!(misspelled = check_at(gtkaspell, pos)) &&
1009 pos > minpos && pos <= maxpos) {
1010
1011 while (!iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1012 pos > minpos && pos <= maxpos)
1013 pos += direc;
1014
1015 while (iswordsep(get_text_index_whar(gtkaspell, pos)) &&
1016 pos > minpos && pos <= maxpos)
1017 pos += direc;
1018 }
1019
1020 return misspelled;
1021 }
1022
gtkaspell_check_next_prev(GtkAspell * gtkaspell,gboolean forward)1023 gboolean gtkaspell_check_next_prev(GtkAspell *gtkaspell, gboolean forward)
1024 {
1025 gboolean misspelled = gtkaspell->ctx.find_misspelled(gtkaspell->ctx.data,
1026 forward);
1027 if (misspelled) {
1028 GSList *list, *cur;
1029 GtkWidget *menu;
1030 misspelled_suggest(gtkaspell, gtkaspell->theword);
1031
1032 if (forward)
1033 gtkaspell->orig_pos = gtkaspell->end_pos;
1034
1035 gtkaspell->ctx.set_position(gtkaspell->ctx.data, gtkaspell->end_pos);
1036
1037 /* only execute when in textview context */
1038 if (gtkaspell == (GtkAspell *)gtkaspell->ctx.data) {
1039 /* scroll line to window center */
1040 gtk_text_view_scroll_to_mark(gtkaspell->gtktext,
1041 gtk_text_buffer_get_insert(
1042 gtk_text_view_get_buffer(gtkaspell->gtktext)),
1043 0.0, TRUE, 0.0, 0.5);
1044 /* let textview recalculate coordinates (set_menu_pos) */
1045 while (gtk_events_pending ())
1046 gtk_main_iteration ();
1047 }
1048
1049 list = make_sug_menu(gtkaspell);
1050 menu = gtk_menu_new();
1051 for (cur = list; cur; cur = cur->next)
1052 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1053 g_slist_free(list);
1054 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1055 gtkaspell->ctx.set_menu_pos,
1056 gtkaspell->ctx.data,
1057 0, GDK_CURRENT_TIME);
1058 g_signal_connect(G_OBJECT(menu), "deactivate",
1059 G_CALLBACK(destroy_menu),
1060 gtkaspell);
1061 g_signal_connect(G_OBJECT(menu),
1062 "key_press_event",
1063 G_CALLBACK(aspell_key_pressed),
1064 gtkaspell);
1065
1066
1067 } else {
1068 reset_theword_data(gtkaspell);
1069
1070 alertpanel_notice(_("No misspelled word found."));
1071 gtkaspell->ctx.set_position(gtkaspell->ctx.data,
1072 gtkaspell->orig_pos);
1073 }
1074
1075 return misspelled;
1076 }
1077
gtkaspell_check_backwards(GtkAspell * gtkaspell)1078 void gtkaspell_check_backwards(GtkAspell *gtkaspell)
1079 {
1080 gtkaspell->continue_check = NULL;
1081 gtkaspell->end_check_pos =
1082 get_textview_buffer_charcount(gtkaspell->gtktext);
1083 gtkaspell_context_set(gtkaspell);
1084 gtkaspell_check_next_prev(gtkaspell, FALSE);
1085 }
1086
gtkaspell_check_forwards_go(GtkAspell * gtkaspell)1087 void gtkaspell_check_forwards_go(GtkAspell *gtkaspell)
1088 {
1089
1090 gtkaspell->continue_check = NULL;
1091 gtkaspell->end_check_pos =
1092 get_textview_buffer_charcount(gtkaspell->gtktext);
1093 gtkaspell_context_set(gtkaspell);
1094 gtkaspell_check_next_prev(gtkaspell, TRUE);
1095 }
1096
gtkaspell_check_all(GtkAspell * gtkaspell)1097 void gtkaspell_check_all(GtkAspell *gtkaspell)
1098 {
1099 GtkTextView *gtktext;
1100 gint start, end;
1101 GtkTextBuffer *buffer;
1102 GtkTextIter startiter, enditer;
1103
1104 cm_return_if_fail(gtkaspell);
1105 cm_return_if_fail(gtkaspell->gtktext);
1106
1107 gtktext = gtkaspell->gtktext;
1108 buffer = gtk_text_view_get_buffer(gtktext);
1109 gtk_text_buffer_get_selection_bounds(buffer, &startiter, &enditer);
1110 start = gtk_text_iter_get_offset(&startiter);
1111 end = gtk_text_iter_get_offset(&enditer);
1112
1113 if (start == end) {
1114 start = 0;
1115 end = gtk_text_buffer_get_char_count(buffer);
1116 } else if (start > end) {
1117 gint tmp;
1118
1119 tmp = start;
1120 start = end;
1121 end = tmp;
1122 }
1123
1124 set_textview_buffer_offset(gtktext, start);
1125
1126 gtkaspell->continue_check = continue_check;
1127 gtkaspell->end_check_pos = end;
1128
1129 gtkaspell_context_set(gtkaspell);
1130 gtkaspell->misspelled = gtkaspell_check_next_prev(gtkaspell, TRUE);
1131 }
1132
continue_check(gpointer * data)1133 static void continue_check(gpointer *data)
1134 {
1135 GtkAspell *gtkaspell = (GtkAspell *) data;
1136 gint pos = get_textview_buffer_offset(gtkaspell->gtktext);
1137 if (pos < gtkaspell->end_check_pos && gtkaspell->misspelled)
1138 gtkaspell->misspelled = gtkaspell_check_next_prev(gtkaspell, TRUE);
1139 else
1140 gtkaspell->continue_check = NULL;
1141 }
1142
gtkaspell_highlight_all(GtkAspell * gtkaspell)1143 void gtkaspell_highlight_all(GtkAspell *gtkaspell)
1144 {
1145 guint origpos;
1146 guint pos = 0;
1147 guint len;
1148 GtkTextView *gtktext;
1149
1150 cm_return_if_fail(gtkaspell->gtkaspeller->speller);
1151
1152 gtktext = gtkaspell->gtktext;
1153
1154 len = get_textview_buffer_charcount(gtktext);
1155
1156 origpos = get_textview_buffer_offset(gtktext);
1157
1158 while (pos < len) {
1159 while (pos < len &&
1160 iswordsep(get_text_index_whar(gtkaspell, pos)))
1161 pos++;
1162 while (pos < len &&
1163 !iswordsep(get_text_index_whar(gtkaspell, pos)))
1164 pos++;
1165 if (pos > 0)
1166 check_at(gtkaspell, pos - 1);
1167 }
1168 set_textview_buffer_offset(gtktext, origpos);
1169 }
1170
replace_with_supplied_word_cb(GtkWidget * w,GtkAspell * gtkaspell)1171 static void replace_with_supplied_word_cb(GtkWidget *w, GtkAspell *gtkaspell)
1172 {
1173 char *newword;
1174 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1175
1176 newword = gtk_editable_get_chars(GTK_EDITABLE(gtkaspell->replace_entry),
1177 0, -1);
1178
1179 if (strcmp(newword, gtkaspell->theword)) {
1180 gtkaspell->ctx.replace_word(gtkaspell->ctx.data, newword);
1181
1182 if ((e->type == GDK_KEY_PRESS &&
1183 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK)) {
1184 enchant_dict_store_replacement(gtkaspell->gtkaspeller->speller,
1185 gtkaspell->theword, strlen(gtkaspell->theword),
1186 newword, strlen(newword));
1187 }
1188 gtkaspell->replace_entry = NULL;
1189 }
1190
1191 g_free(newword);
1192
1193 if (w && GTK_IS_DIALOG(w)) {
1194 gtk_widget_destroy(w);
1195 }
1196
1197 set_point_continue(gtkaspell);
1198 }
1199
1200
replace_word_cb(GtkWidget * w,gpointer data)1201 static void replace_word_cb(GtkWidget *w, gpointer data)
1202 {
1203 const char *newword;
1204 GtkAspell *gtkaspell = (GtkAspell *) data;
1205 GdkEvent *e= (GdkEvent *) gtk_get_current_event();
1206
1207 newword = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN((w)))));
1208
1209 gtkaspell->ctx.replace_word(gtkaspell->ctx.data, newword);
1210
1211 if ((e->type == GDK_KEY_PRESS &&
1212 ((GdkEventKey *) e)->state & GDK_CONTROL_MASK) ||
1213 (e->type == GDK_BUTTON_RELEASE &&
1214 ((GdkEventButton *) e)->state & GDK_CONTROL_MASK)) {
1215 enchant_dict_store_replacement(gtkaspell->gtkaspeller->speller,
1216 gtkaspell->theword, strlen(gtkaspell->theword),
1217 newword, strlen(newword));
1218 }
1219
1220 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w)));
1221
1222 set_point_continue(gtkaspell);
1223 }
1224
gtkaspell_block_check(GtkAspell * gtkaspell)1225 void gtkaspell_block_check(GtkAspell *gtkaspell)
1226 {
1227 GtkTextView *gtktext;
1228
1229 if (gtkaspell == NULL)
1230 return;
1231
1232 gtktext = gtkaspell->gtktext;
1233 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1234 G_CALLBACK(key_press_cb),
1235 gtkaspell);
1236 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1237 G_CALLBACK(entry_insert_cb),
1238 gtkaspell);
1239 g_signal_handlers_block_by_func(G_OBJECT(gtktext),
1240 G_CALLBACK(entry_delete_cb),
1241 gtkaspell);
1242 }
1243
gtkaspell_unblock_check(GtkAspell * gtkaspell)1244 void gtkaspell_unblock_check(GtkAspell *gtkaspell)
1245 {
1246 GtkTextView *gtktext;
1247
1248 if (gtkaspell == NULL)
1249 return;
1250
1251 gtktext = gtkaspell->gtktext;
1252 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1253 G_CALLBACK(key_press_cb),
1254 gtkaspell);
1255 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1256 G_CALLBACK(entry_insert_cb),
1257 gtkaspell);
1258 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext),
1259 G_CALLBACK(entry_delete_cb),
1260 gtkaspell);
1261 }
1262
replace_real_word(GtkAspell * gtkaspell,const gchar * newword)1263 static void replace_real_word(GtkAspell *gtkaspell, const gchar *newword)
1264 {
1265 int oldlen, newlen;
1266 gint origpos;
1267 gint pos;
1268 GtkTextView *gtktext;
1269 GtkTextBuffer *textbuf;
1270 GtkTextIter startiter, enditer;
1271
1272 if (!newword) return;
1273
1274 gtktext = gtkaspell->gtktext;
1275 textbuf = gtk_text_view_get_buffer(gtktext);
1276
1277 origpos = gtkaspell->orig_pos;
1278 pos = origpos;
1279 oldlen = gtkaspell->end_pos - gtkaspell->start_pos;
1280
1281 newlen = strlen(newword); /* FIXME: multybyte characters? */
1282
1283 gtkaspell_block_check(gtkaspell);
1284
1285 gtk_text_buffer_get_iter_at_offset(textbuf, &startiter,
1286 gtkaspell->start_pos);
1287 gtk_text_buffer_get_iter_at_offset(textbuf, &enditer,
1288 gtkaspell->end_pos);
1289 g_signal_emit_by_name(G_OBJECT(textbuf), "delete-range",
1290 &startiter, &enditer, gtkaspell);
1291 g_signal_emit_by_name(G_OBJECT(textbuf), "insert-text",
1292 &startiter, newword, newlen, gtkaspell);
1293
1294 gtkaspell_unblock_check(gtkaspell);
1295
1296 /* Put the point and the position where we clicked with the mouse
1297 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1298 * to let it update correctly the word insertion and then the
1299 * point & position position. If not, SEGV after the first replacement
1300 * If the new word ends before point, put the point at its end.
1301 */
1302
1303 if (origpos - gtkaspell->start_pos < oldlen &&
1304 origpos - gtkaspell->start_pos >= 0) {
1305 /* Original point was in the word.
1306 * Let it there unless point is going to be outside of the word
1307 */
1308 if (origpos - gtkaspell->start_pos >= newlen) {
1309 pos = gtkaspell->start_pos + newlen;
1310 }
1311 }
1312 else if (origpos >= gtkaspell->end_pos) {
1313 /* move the position according to the change of length */
1314 pos = origpos + newlen - oldlen;
1315 }
1316
1317 gtkaspell->end_pos = gtkaspell->start_pos + strlen(newword); /* FIXME: multibyte characters? */
1318
1319 if (get_textview_buffer_charcount(gtktext) < pos)
1320 pos = get_textview_buffer_charcount(gtktext);
1321 gtkaspell->orig_pos = pos;
1322
1323 set_textview_buffer_offset(gtktext, gtkaspell->orig_pos);
1324 }
1325
replace_real_word_cb(gpointer data,const gchar * newword)1326 static void replace_real_word_cb(gpointer data, const gchar *newword)
1327 {
1328 replace_real_word((GtkAspell *)data, newword);
1329 }
1330
1331 /* Accept this word for this session */
add_word_to_session_cb(GtkWidget * w,gpointer data)1332 static void add_word_to_session_cb(GtkWidget *w, gpointer data)
1333 {
1334 GtkAspell *gtkaspell = (GtkAspell *) data;
1335
1336 enchant_dict_add_to_session(gtkaspell->gtkaspeller->speller, gtkaspell->theword, strlen(gtkaspell->theword));
1337
1338 gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1339 gtkaspell_dict_changed(gtkaspell);
1340
1341 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w)));
1342
1343 set_point_continue(gtkaspell);
1344 }
1345
1346 /* add_word_to_personal_cb() - add word to personal dict. */
add_word_to_personal_cb(GtkWidget * w,gpointer data)1347 static void add_word_to_personal_cb(GtkWidget *w, gpointer data)
1348 {
1349 GtkAspell *gtkaspell = (GtkAspell *) data;
1350
1351 enchant_dict_add(gtkaspell->gtkaspeller->speller, gtkaspell->theword, strlen(gtkaspell->theword));
1352
1353 gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1354 gtkaspell_dict_changed(gtkaspell);
1355
1356 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w)));
1357 set_point_continue(gtkaspell);
1358 }
1359
check_with_alternate_cb(GtkWidget * w,gpointer data)1360 static void check_with_alternate_cb(GtkWidget *w, gpointer data)
1361 {
1362 GtkAspell *gtkaspell = (GtkAspell *)data;
1363 gint misspelled;
1364
1365 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w)));
1366
1367 gtkaspell_use_alternate_dict(gtkaspell);
1368 misspelled = gtkaspell->ctx.check_word(gtkaspell->ctx.data);
1369
1370 if (!gtkaspell->continue_check) {
1371
1372 gtkaspell->misspelled = misspelled;
1373
1374 if (gtkaspell->misspelled) {
1375 GtkWidget *menu;
1376 GSList *list, *cur;
1377 misspelled_suggest(gtkaspell, gtkaspell->theword);
1378
1379 gtkaspell->ctx.set_position(gtkaspell->ctx.data,
1380 gtkaspell->end_pos);
1381
1382 list = make_sug_menu(gtkaspell);
1383 menu = gtk_menu_new();
1384 for (cur = list; cur; cur = cur->next)
1385 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(cur->data));
1386 g_slist_free(list);
1387 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1388 gtkaspell->ctx.set_menu_pos,
1389 gtkaspell->ctx.data, 0,
1390 GDK_CURRENT_TIME);
1391 g_signal_connect(G_OBJECT(menu), "deactivate",
1392 G_CALLBACK(destroy_menu),
1393 gtkaspell);
1394 g_signal_connect(G_OBJECT(menu),
1395 "key_press_event",
1396 G_CALLBACK(aspell_key_pressed),
1397 gtkaspell);
1398 return;
1399 }
1400 } else
1401 gtkaspell->orig_pos = gtkaspell->start_pos;
1402
1403 set_point_continue(gtkaspell);
1404 }
1405
replace_key_pressed(GtkWidget * widget,GdkEventKey * event,GtkAspell * gtkaspell)1406 static gboolean replace_key_pressed(GtkWidget *widget,
1407 GdkEventKey *event,
1408 GtkAspell *gtkaspell)
1409 {
1410 if (event && event->keyval == GDK_KEY_Escape) {
1411 gtk_widget_destroy(widget);
1412 return TRUE;
1413 } else if (event && event->keyval == GDK_KEY_Return) {
1414 replace_with_supplied_word_cb(widget, gtkaspell);
1415 return TRUE;
1416 }
1417 return FALSE;
1418 }
1419
replace_with_create_dialog_cb(GtkWidget * w,gpointer data)1420 static void replace_with_create_dialog_cb(GtkWidget *w, gpointer data)
1421 {
1422 static PangoFontDescription *font_desc;
1423 GtkWidget *dialog;
1424 GtkWidget *label;
1425 GtkWidget *hbox;
1426 GtkWidget *vbox;
1427 GtkWidget *entry;
1428 GtkWidget *ok_button;
1429 GtkWidget *cancel_button;
1430 GtkWidget *confirm_area;
1431 GtkWidget *icon;
1432 GtkWidget *parent_window;
1433 GtkWidget *content_area;
1434 GtkWidget *action_area;
1435 gchar *utf8buf, *thelabel;
1436 gint xx, yy;
1437 GtkAspell *gtkaspell = (GtkAspell *) data;
1438
1439 cm_return_if_fail(w != NULL);
1440 parent_window = gtk_widget_get_parent(w);
1441 cm_return_if_fail(parent_window != NULL);
1442 gdk_window_get_origin(gtk_widget_get_window(parent_window), &xx, &yy);
1443
1444 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w)));
1445
1446 dialog = gtk_dialog_new();
1447 content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1448 action_area = gtk_dialog_get_action_area(GTK_DIALOG(dialog));
1449
1450 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1451 gtk_window_set_title(GTK_WINDOW(dialog),_("Replace unknown word"));
1452 gtk_window_move(GTK_WINDOW(dialog), xx, yy);
1453
1454 g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
1455 G_CALLBACK(gtk_widget_destroy),
1456 G_OBJECT(dialog));
1457
1458 gtk_box_set_spacing (GTK_BOX (content_area), 14);
1459 hbox = gtk_hbox_new (FALSE, 12);
1460 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1461 gtk_widget_show (hbox);
1462 gtk_box_pack_start (GTK_BOX (content_area), hbox, FALSE, FALSE, 0);
1463
1464 utf8buf = g_strdup(gtkaspell->theword);
1465
1466 thelabel = g_strdup_printf(g_strconcat("<span weight=\"bold\" size=\"larger\">",
1467 _("Replace \"%s\" with: "), "</span>", NULL),
1468 utf8buf);
1469
1470 icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
1471 GTK_ICON_SIZE_DIALOG);
1472 gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
1473 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
1474
1475 vbox = gtk_vbox_new (FALSE, 12);
1476 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1477 gtk_widget_show (vbox);
1478
1479 label = gtk_label_new(thelabel);
1480 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1481 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1482 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1483 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1484 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1485 if (!font_desc) {
1486 gint size;
1487
1488 size = pango_font_description_get_size
1489 (gtk_widget_get_style(label)->font_desc);
1490 font_desc = pango_font_description_new();
1491 pango_font_description_set_weight
1492 (font_desc, PANGO_WEIGHT_BOLD);
1493 pango_font_description_set_size
1494 (font_desc, size * PANGO_SCALE_LARGE);
1495 }
1496 if (font_desc)
1497 gtk_widget_modify_font(label, font_desc);
1498 g_free(thelabel);
1499
1500 entry = gtk_entry_new();
1501 gtkaspell->replace_entry = entry;
1502 gtk_entry_set_text(GTK_ENTRY(entry), utf8buf);
1503 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
1504 g_signal_connect(G_OBJECT(dialog),
1505 "key_press_event",
1506 G_CALLBACK(replace_key_pressed), gtkaspell);
1507 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
1508 g_free(utf8buf);
1509
1510 label = gtk_label_new(_("Holding down Control key while pressing "
1511 "Enter\nwill learn from mistake.\n"));
1512 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1513 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1514 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1515 gtk_widget_show(label);
1516
1517 gtkut_stock_button_set_create(&confirm_area,
1518 &cancel_button, GTK_STOCK_CANCEL,
1519 &ok_button, GTK_STOCK_OK,
1520 NULL, NULL);
1521
1522 gtk_box_pack_end(GTK_BOX(action_area), confirm_area, FALSE, FALSE, 0);
1523 gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
1524
1525 g_signal_connect(G_OBJECT(ok_button), "clicked",
1526 G_CALLBACK(replace_with_supplied_word_cb),
1527 gtkaspell);
1528 g_signal_connect_swapped(G_OBJECT(ok_button), "clicked",
1529 G_CALLBACK(gtk_widget_destroy),
1530 G_OBJECT(dialog));
1531
1532 g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked",
1533 G_CALLBACK(gtk_widget_destroy),
1534 G_OBJECT(dialog));
1535
1536 gtk_widget_grab_focus(entry);
1537
1538 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1539
1540 gtk_widget_show_all(dialog);
1541 }
1542
gtkaspell_uncheck_all(GtkAspell * gtkaspell)1543 static void gtkaspell_uncheck_all(GtkAspell * gtkaspell)
1544 {
1545 GtkTextView *gtktext;
1546 GtkTextBuffer *buffer;
1547 GtkTextIter startiter, enditer;
1548
1549 gtktext = gtkaspell->gtktext;
1550
1551 buffer = gtk_text_view_get_buffer(gtktext);
1552 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, 0);
1553 gtk_text_buffer_get_iter_at_offset(buffer, &enditer,
1554 get_textview_buffer_charcount(gtktext)-1);
1555 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
1556 &startiter, &enditer);
1557 }
1558
toggle_check_while_typing_cb(GtkWidget * w,gpointer data)1559 static void toggle_check_while_typing_cb(GtkWidget *w, gpointer data)
1560 {
1561 GtkAspell *gtkaspell = (GtkAspell *) data;
1562
1563 gtkaspell->check_while_typing = gtkaspell->check_while_typing == FALSE;
1564
1565 if (!gtkaspell->check_while_typing)
1566 gtkaspell_uncheck_all(gtkaspell);
1567 if (gtkaspell->menu_changed_cb)
1568 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
1569 }
1570
create_empty_dictionary_list(void)1571 static GSList *create_empty_dictionary_list(void)
1572 {
1573 GSList *list = NULL;
1574 Dictionary *dict;
1575
1576 dict = g_new0(Dictionary, 1);
1577 dict->fullname = g_strdup(_("None"));
1578 dict->dictname = NULL;
1579
1580 return g_slist_append(list, dict);
1581 }
1582
list_dict_cb(const char * const lang_tag,const char * const provider_name,const char * const provider_desc,const char * const provider_file,void * data)1583 static void list_dict_cb(const char * const lang_tag,
1584 const char * const provider_name,
1585 const char * const provider_desc,
1586 const char * const provider_file,
1587 void * data)
1588 {
1589 GSList **list = (GSList **)data;
1590 Dictionary *dict = g_new0(Dictionary, 1);
1591 dict->fullname = g_strdup(lang_tag);
1592 dict->dictname = g_strdup(lang_tag);
1593
1594 if (g_slist_find_custom(*list, dict,
1595 (GCompareFunc) compare_dict) == NULL) {
1596 debug_print("Aspell: found dictionary %s %s\n", dict->fullname,
1597 dict->dictname);
1598 *list = g_slist_insert_sorted(*list, dict,
1599 (GCompareFunc) compare_dict);
1600 } else {
1601 dictionary_delete(dict);
1602 }
1603 }
1604
1605 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
gtkaspell_get_dictionary_list(gint refresh)1606 static GSList *gtkaspell_get_dictionary_list(gint refresh)
1607 {
1608 GSList *list;
1609 EnchantBroker *broker;
1610
1611 if (!gtkaspellcheckers)
1612 gtkaspell_checkers_init();
1613
1614 if (gtkaspellcheckers->dictionary_list && !refresh)
1615 return gtkaspellcheckers->dictionary_list;
1616 else
1617 gtkaspell_free_dictionary_list(
1618 gtkaspellcheckers->dictionary_list);
1619 list = NULL;
1620
1621 broker = enchant_broker_init();
1622
1623 enchant_broker_list_dicts(broker, list_dict_cb, &list);
1624
1625 enchant_broker_free(broker);
1626
1627 if (list == NULL){
1628
1629 debug_print("Aspell: error when searching for dictionaries: "
1630 "No dictionary found.\n");
1631 list = create_empty_dictionary_list();
1632 }
1633
1634 gtkaspellcheckers->dictionary_list = list;
1635
1636 return list;
1637 }
1638
gtkaspell_free_dictionary_list(GSList * list)1639 static void gtkaspell_free_dictionary_list(GSList *list)
1640 {
1641 Dictionary *dict;
1642 GSList *walk;
1643 for (walk = list; walk != NULL; walk = g_slist_next(walk))
1644 if (walk->data) {
1645 dict = (Dictionary *) walk->data;
1646 dictionary_delete(dict);
1647 }
1648 g_slist_free(list);
1649 }
1650
gtkaspell_dictionary_store_new_with_refresh(gboolean refresh)1651 GtkTreeModel *gtkaspell_dictionary_store_new_with_refresh(gboolean refresh)
1652 {
1653 GSList *dict_list, *tmp;
1654 GtkListStore *store;
1655 GtkTreeIter iter;
1656 Dictionary *dict;
1657
1658 dict_list = gtkaspell_get_dictionary_list(refresh);
1659 cm_return_val_if_fail(dict_list, NULL);
1660
1661 store = gtk_list_store_new(SET_GTKASPELL_SIZE,
1662 G_TYPE_STRING,
1663 G_TYPE_STRING,
1664 -1);
1665
1666 for (tmp = dict_list; tmp != NULL; tmp = g_slist_next(tmp)) {
1667 dict = (Dictionary *) tmp->data;
1668
1669 gtk_list_store_append(store, &iter);
1670 gtk_list_store_set(store, &iter,
1671 SET_GTKASPELL_NAME, dict->dictname,
1672 SET_GTKASPELL_FULLNAME, dict->fullname,
1673 -1);
1674 }
1675
1676 return GTK_TREE_MODEL(store);
1677 }
1678
gtkaspell_dictionary_store_new(void)1679 GtkTreeModel *gtkaspell_dictionary_store_new(void)
1680 {
1681 return gtkaspell_dictionary_store_new_with_refresh
1682 (TRUE);
1683 }
1684
gtkaspell_dictionary_combo_new(const gboolean refresh)1685 GtkWidget *gtkaspell_dictionary_combo_new(const gboolean refresh)
1686 {
1687 GtkWidget *combo;
1688 GtkCellRenderer *renderer;
1689
1690 combo = gtk_combo_box_new_with_model(
1691 gtkaspell_dictionary_store_new_with_refresh(refresh));
1692 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1693 gtk_widget_show(combo);
1694
1695 renderer = gtk_cell_renderer_text_new();
1696 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
1697 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo),renderer,
1698 "text", SET_GTKASPELL_NAME, NULL);
1699
1700 return combo;
1701 }
1702
gtkaspell_get_dictionary_menu_active_item(GtkComboBox * combo)1703 gchar *gtkaspell_get_dictionary_menu_active_item(GtkComboBox *combo)
1704 {
1705 GtkTreeIter iter;
1706 GtkTreeModel *model;
1707 gchar *dict_fullname = NULL;
1708
1709 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), NULL);
1710 cm_return_val_if_fail(gtk_combo_box_get_active_iter(combo, &iter), NULL);
1711
1712 model = gtk_combo_box_get_model(combo);
1713 if(model == NULL)
1714 return NULL;
1715
1716 gtk_tree_model_get(model, &iter,
1717 SET_GTKASPELL_FULLNAME, &dict_fullname,
1718 -1);
1719
1720 return dict_fullname;
1721 }
1722
gtkaspell_set_dictionary_menu_active_item(GtkComboBox * combo,const gchar * dictionary)1723 gint gtkaspell_set_dictionary_menu_active_item(GtkComboBox *combo,
1724 const gchar *dictionary)
1725 {
1726 GtkTreeModel *model;
1727 GtkTreeIter iter;
1728 gchar *dict_name = NULL;
1729
1730 cm_return_val_if_fail(combo != NULL, 0);
1731 cm_return_val_if_fail(dictionary != NULL, 0);
1732 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo), 0);
1733
1734 if((model = gtk_combo_box_get_model(combo)) == NULL)
1735 return 0;
1736 if((gtk_tree_model_get_iter_first(model, &iter)) == FALSE)
1737 return 0;
1738
1739 do {
1740 gtk_tree_model_get(model, &iter,
1741 SET_GTKASPELL_FULLNAME, &dict_name,
1742 -1);
1743
1744 if ((dict_name != NULL) && !g_strcmp0(dict_name, dictionary)) {
1745 gtk_combo_box_set_active_iter(combo, &iter);
1746 g_free(dict_name);
1747 return 1;
1748 }
1749
1750 g_free(dict_name);
1751
1752 } while ((gtk_tree_model_iter_next(model, &iter)) == TRUE);
1753
1754 return 0;
1755 }
1756
gtkaspell_use_alternate_dict(GtkAspell * gtkaspell)1757 void gtkaspell_use_alternate_dict(GtkAspell *gtkaspell)
1758 {
1759 GtkAspeller *tmp;
1760
1761 tmp = gtkaspell->gtkaspeller;
1762 gtkaspell->gtkaspeller = gtkaspell->alternate_speller;
1763 gtkaspell->alternate_speller = tmp;
1764 }
1765
destroy_menu(GtkWidget * widget,gpointer user_data)1766 static void destroy_menu(GtkWidget *widget,
1767 gpointer user_data) {
1768 GtkAspell *gtkaspell = (GtkAspell *)user_data;
1769
1770 if (gtkaspell->accel_group) {
1771 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1772 gtkaspell->accel_group);
1773 gtkaspell->accel_group = NULL;
1774 }
1775 }
1776
aspell_key_pressed(GtkWidget * widget,GdkEventKey * event,GtkAspell * gtkaspell)1777 static gboolean aspell_key_pressed(GtkWidget *widget,
1778 GdkEventKey *event,
1779 GtkAspell *gtkaspell)
1780 {
1781 if (event && (isascii(event->keyval) || event->keyval == GDK_KEY_Return)) {
1782 gtk_accel_groups_activate(
1783 G_OBJECT(gtkaspell->parent_window),
1784 event->keyval, event->state);
1785 } else if (event && event->keyval == GDK_KEY_Escape) {
1786 destroy_menu(NULL, gtkaspell);
1787 }
1788 return FALSE;
1789 }
1790
1791 /* Create a paged submenu with choice of available dictionaries */
make_dictionary_list_submenu(GtkAspell * gtkaspell)1792 static GtkWidget *make_dictionary_list_submenu(GtkAspell *gtkaspell)
1793 {
1794 GtkWidget *menu, *curmenu, *moremenu, *item;
1795 int count = 2;
1796 Dictionary *dict;
1797 GSList *tmp;
1798
1799 /* Dict list */
1800 if (gtkaspellcheckers->dictionary_list == NULL)
1801 gtkaspell_get_dictionary_list(FALSE);
1802
1803 menu = gtk_menu_new();
1804 curmenu = menu;
1805
1806 for (tmp = gtkaspellcheckers->dictionary_list; tmp != NULL;
1807 tmp = g_slist_next(tmp)) {
1808 if (count == MENUCOUNT) {
1809
1810 moremenu = gtk_menu_new();
1811 item = gtk_menu_item_new_with_label(_("More..."));
1812 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1813 moremenu);
1814
1815 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1816 curmenu = moremenu;
1817 count = 0;
1818 }
1819 dict = (Dictionary *) tmp->data;
1820 item = gtk_check_menu_item_new_with_label(dict->fullname);
1821 g_object_set_data(G_OBJECT(item), "dict_name",
1822 dict->dictname);
1823 if (g_strcmp0(dict->fullname,
1824 gtkaspell->gtkaspeller->dictionary->fullname))
1825 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
1826 else {
1827 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1828 gtk_widget_set_sensitive(GTK_WIDGET(item),
1829 FALSE);
1830 }
1831 g_signal_connect(G_OBJECT(item), "activate",
1832 G_CALLBACK(change_dict_cb),
1833 gtkaspell);
1834 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1835
1836 count++;
1837 }
1838
1839 gtk_widget_show_all(menu);
1840 return menu;
1841 }
1842
1843 /* make_sug_menu() - Add menus to accept this word for this session
1844 * and to add it to personal dictionary
1845 */
make_sug_menu(GtkAspell * gtkaspell)1846 static GSList *make_sug_menu(GtkAspell *gtkaspell)
1847 {
1848 GtkWidget *item, *submenu;
1849 char *caption;
1850 GtkAccelGroup *accel;
1851 GList *l = gtkaspell->suggestions_list;
1852 gchar *utf8buf;
1853 GSList *list = NULL;
1854
1855 if (l == NULL)
1856 return NULL;
1857
1858 accel = gtk_accel_group_new();
1859
1860 if (gtkaspell->accel_group) {
1861 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell->parent_window),
1862 gtkaspell->accel_group);
1863 gtkaspell->accel_group = NULL;
1864 }
1865
1866 utf8buf = g_strdup(l->data);
1867 caption = g_strdup_printf(_("\"%s\" unknown in dictionary '%s'"),
1868 utf8buf,
1869 gtkaspell->gtkaspeller->dictionary->dictname);
1870 item = gtk_menu_item_new_with_label(caption);
1871 submenu = make_dictionary_list_submenu(gtkaspell);
1872 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
1873 g_free(utf8buf);
1874 gtk_widget_show(item);
1875 list = g_slist_append(list, item);
1876 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
1877 g_free(caption);
1878
1879 item = gtk_menu_item_new();
1880 gtk_widget_show(item);
1881 list = g_slist_append(list, item);
1882
1883 item = gtk_menu_item_new_with_label(_("Accept in this session"));
1884 gtk_widget_show(item);
1885 list = g_slist_append(list, item);
1886 g_signal_connect(G_OBJECT(item), "activate",
1887 G_CALLBACK(add_word_to_session_cb),
1888 gtkaspell);
1889 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_space,
1890 GDK_CONTROL_MASK,
1891 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1892
1893 item = gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1894 gtk_widget_show(item);
1895 list = g_slist_append(list, item);
1896 g_signal_connect(G_OBJECT(item), "activate",
1897 G_CALLBACK(add_word_to_personal_cb),
1898 gtkaspell);
1899 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_Return,
1900 GDK_CONTROL_MASK,
1901 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1902
1903 item = gtk_menu_item_new_with_label(_("Replace with..."));
1904 gtk_widget_show(item);
1905 list = g_slist_append(list, item);
1906 g_signal_connect(G_OBJECT(item), "activate",
1907 G_CALLBACK(replace_with_create_dialog_cb),
1908 gtkaspell);
1909 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_R, 0,
1910 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1911 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_R,
1912 GDK_CONTROL_MASK,
1913 GTK_ACCEL_LOCKED);
1914
1915 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
1916 caption = g_strdup_printf(_("Check with %s"),
1917 gtkaspell->alternate_speller->dictionary->dictname);
1918 item = gtk_menu_item_new_with_label(caption);
1919 g_free(caption);
1920 gtk_widget_show(item);
1921 list = g_slist_append(list, item);
1922 g_signal_connect(G_OBJECT(item), "activate",
1923 G_CALLBACK(check_with_alternate_cb),
1924 gtkaspell);
1925 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_X, 0,
1926 GTK_ACCEL_LOCKED | GTK_ACCEL_VISIBLE);
1927 gtk_widget_add_accelerator(item, "activate", accel, GDK_KEY_X,
1928 GDK_CONTROL_MASK,
1929 GTK_ACCEL_LOCKED);
1930 }
1931
1932 item = gtk_menu_item_new();
1933 gtk_widget_show(item);
1934 list = g_slist_append(list, item);
1935
1936 l = l->next;
1937 if (l == NULL) {
1938 item = gtk_menu_item_new_with_label(_("(no suggestions)"));
1939 gtk_widget_show(item);
1940 list = g_slist_append(list, item);
1941 } else {
1942 GtkWidget *curmenu = NULL;
1943 gint count = 0;
1944
1945 do {
1946 if (count == MENUCOUNT) {
1947 count -= MENUCOUNT;
1948
1949 item = gtk_menu_item_new_with_label(_("More..."));
1950 gtk_widget_show(item);
1951 if (curmenu)
1952 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1953 else
1954 list = g_slist_append(list, item);
1955
1956 curmenu = gtk_menu_new();
1957 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
1958 curmenu);
1959 }
1960
1961 utf8buf = g_strdup(l->data);
1962
1963 item = gtk_menu_item_new_with_label(utf8buf);
1964 g_free(utf8buf);
1965 gtk_widget_show(item);
1966 if (curmenu == NULL) {
1967 list = g_slist_append(list, item);
1968 } else {
1969 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu), item);
1970 }
1971 g_signal_connect(G_OBJECT(item), "activate",
1972 G_CALLBACK(replace_word_cb),
1973 gtkaspell);
1974
1975 if (curmenu == NULL && count < MENUCOUNT) {
1976 gtk_widget_add_accelerator(item, "activate",
1977 accel,
1978 GDK_KEY_A + count, 0,
1979 GTK_ACCEL_LOCKED |
1980 GTK_ACCEL_VISIBLE);
1981 gtk_widget_add_accelerator(item, "activate",
1982 accel,
1983 GDK_KEY_A + count,
1984 GDK_CONTROL_MASK,
1985 GTK_ACCEL_LOCKED);
1986 }
1987
1988 count++;
1989
1990 } while ((l = l->next) != NULL);
1991 }
1992
1993 gtk_window_add_accel_group
1994 (GTK_WINDOW(gtkaspell->parent_window),
1995 accel);
1996 gtkaspell->accel_group = accel;
1997
1998 return list;
1999 }
2000
populate_submenu(GtkAspell * gtkaspell)2001 static GSList *populate_submenu(GtkAspell *gtkaspell)
2002 {
2003 GtkWidget *item, *submenu, *both_dicts_item;
2004 gchar *dictname;
2005 GtkAspeller *gtkaspeller = NULL;
2006 GSList *list = NULL;
2007
2008 if (!gtkaspell)
2009 return NULL;
2010
2011 gtkaspeller = gtkaspell->gtkaspeller;
2012 dictname = g_strdup_printf(_("Dictionary: %s"),
2013 gtkaspeller->dictionary->dictname);
2014 item = gtk_menu_item_new_with_label(dictname);
2015 gtk_misc_set_alignment(GTK_MISC(gtk_bin_get_child(GTK_BIN((item)))), 0.5, 0.5);
2016 g_free(dictname);
2017 submenu = make_dictionary_list_submenu(gtkaspell);
2018 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
2019 gtk_widget_show(item);
2020 list = g_slist_append(list, item);
2021
2022 item = gtk_menu_item_new();
2023 gtk_widget_show(item);
2024 list = g_slist_append(list, item);
2025
2026 if (gtkaspell->use_alternate && gtkaspell->alternate_speller) {
2027 dictname = g_strdup_printf(_("Use alternate (%s)"),
2028 gtkaspell->alternate_speller->dictionary->dictname);
2029 item = gtk_menu_item_new_with_label(dictname);
2030 g_free(dictname);
2031 g_signal_connect(G_OBJECT(item), "activate",
2032 G_CALLBACK(switch_to_alternate_cb),
2033 gtkaspell);
2034 gtk_widget_show(item);
2035 list = g_slist_append(list, item);
2036 }
2037
2038 both_dicts_item = gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
2039 if (gtkaspell->use_both_dicts && gtkaspell->use_alternate) {
2040 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(both_dicts_item), TRUE);
2041 }
2042 gtk_widget_set_sensitive(both_dicts_item, gtkaspell->use_alternate);
2043
2044 g_signal_connect(G_OBJECT(both_dicts_item), "activate",
2045 G_CALLBACK(set_use_both_cb),
2046 gtkaspell);
2047 gtk_widget_show(both_dicts_item);
2048 list = g_slist_append(list, both_dicts_item);
2049
2050 item = gtk_menu_item_new();
2051 gtk_widget_show(item);
2052 list = g_slist_append(list, item);
2053
2054 item = gtk_check_menu_item_new_with_label(_("Check while typing"));
2055 if (gtkaspell->check_while_typing)
2056 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2057 else
2058 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
2059 g_signal_connect(G_OBJECT(item), "activate",
2060 G_CALLBACK(toggle_check_while_typing_cb),
2061 gtkaspell);
2062 gtk_widget_show(item);
2063 list = g_slist_append(list, item);
2064
2065 return list;
2066 }
2067
gtkaspell_make_config_menu(GtkAspell * gtkaspell)2068 GSList *gtkaspell_make_config_menu(GtkAspell *gtkaspell)
2069 {
2070 return populate_submenu(gtkaspell);
2071 }
2072
set_menu_pos(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer data)2073 static void set_menu_pos(GtkMenu *menu, gint *x, gint *y,
2074 gboolean *push_in, gpointer data)
2075 {
2076 GtkAspell *gtkaspell = (GtkAspell *) data;
2077 gint xx = 0, yy = 0;
2078 gint sx, sy;
2079 gint wx, wy;
2080 GtkTextView *text = GTK_TEXT_VIEW(gtkaspell->gtktext);
2081 GtkTextBuffer *textbuf;
2082 GtkTextIter iter;
2083 GdkRectangle rect;
2084 GtkRequisition r;
2085
2086 textbuf = gtk_text_view_get_buffer(gtkaspell->gtktext);
2087 gtk_text_buffer_get_iter_at_mark(textbuf, &iter,
2088 gtk_text_buffer_get_insert(textbuf));
2089 gtk_text_view_get_iter_location(gtkaspell->gtktext, &iter, &rect);
2090 gtk_text_view_buffer_to_window_coords(text, GTK_TEXT_WINDOW_TEXT,
2091 rect.x, rect.y,
2092 &rect.x, &rect.y);
2093
2094 gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(gtkaspell->gtktext)),
2095 &xx, &yy);
2096
2097 sx = gdk_screen_width();
2098 sy = gdk_screen_height();
2099
2100 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &r);
2101
2102 wx = r.width;
2103 wy = r.height;
2104
2105 *x = rect.x + xx + 8;
2106
2107 *y = rect.y + rect.height + yy;
2108
2109 if (*x + wx > sx)
2110 *x = sx - wx;
2111 if (*y + wy > sy)
2112 *y = *y - wy - 10;
2113 }
2114
2115 /* change the current dictionary of gtkaspell
2116 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2117 current dictionary (common use: from menu callbacks)
2118 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2119 current dictionary only if there is no alternate dictionary already set
2120 (this is when we need to set the current dictionary then the alternate one
2121 when creating a compose window, from the account and folder settings)
2122 */
gtkaspell_change_dict(GtkAspell * gtkaspell,const gchar * dictionary,gboolean always_set_alt_dict)2123 gboolean gtkaspell_change_dict(GtkAspell *gtkaspell, const gchar *dictionary,
2124 gboolean always_set_alt_dict)
2125 {
2126 Dictionary *dict;
2127 GtkAspeller *gtkaspeller;
2128
2129 cm_return_val_if_fail(gtkaspell, FALSE);
2130 cm_return_val_if_fail(dictionary, FALSE);
2131
2132 dict = g_new0(Dictionary, 1);
2133
2134 if (strrchr(dictionary, '/')) {
2135 dict->fullname = g_strdup(strrchr(dictionary, '/')+1);
2136 dict->dictname = g_strdup(strrchr(dictionary, '/')+1);
2137 } else {
2138 dict->fullname = g_strdup(dictionary);
2139 dict->dictname = g_strdup(dictionary);
2140 }
2141
2142 if (dict->fullname && strchr(dict->fullname, '-')) {
2143 *(strchr(dict->fullname, '-')) = '\0';
2144 *(strchr(dict->dictname, '-')) = '\0';
2145 }
2146
2147 if (!dict->fullname || !(*dict->fullname)) {
2148 dictionary_delete(dict);
2149 return FALSE;
2150 }
2151 gtkaspeller = gtkaspeller_new(dict);
2152
2153 if (!gtkaspeller) {
2154 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2155 gtkaspellcheckers->error_message);
2156 } else {
2157 if (gtkaspell->use_alternate) {
2158 if (gtkaspell->alternate_speller) {
2159 if (always_set_alt_dict) {
2160 gtkaspeller_delete(gtkaspell->alternate_speller);
2161 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2162 } else
2163 gtkaspeller_delete(gtkaspell->gtkaspeller);
2164 } else
2165 /* should never be reached as the dicts are always set
2166 to a default value */
2167 gtkaspell->alternate_speller = gtkaspell->gtkaspeller;
2168 } else
2169 gtkaspeller_delete(gtkaspell->gtkaspeller);
2170
2171 gtkaspell->gtkaspeller = gtkaspeller;
2172 }
2173
2174 dictionary_delete(dict);
2175
2176 return TRUE;
2177 }
2178
2179 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
gtkaspell_change_alt_dict(GtkAspell * gtkaspell,const gchar * alt_dictionary)2180 gboolean gtkaspell_change_alt_dict(GtkAspell *gtkaspell, const gchar *alt_dictionary)
2181 {
2182 Dictionary *dict;
2183 GtkAspeller *gtkaspeller;
2184
2185 cm_return_val_if_fail(gtkaspell, FALSE);
2186 cm_return_val_if_fail(alt_dictionary, FALSE);
2187
2188 dict = g_new0(Dictionary, 1);
2189 if (strrchr(alt_dictionary, '/')) {
2190 dict->fullname = g_strdup(strrchr(alt_dictionary, '/')+1);
2191 dict->dictname = g_strdup(strrchr(alt_dictionary, '/')+1);
2192 } else {
2193 dict->fullname = g_strdup(alt_dictionary);
2194 dict->dictname = g_strdup(alt_dictionary);
2195 }
2196
2197 if (dict->fullname && strchr(dict->fullname, '-')) {
2198 *(strchr(dict->fullname, '-')) = '\0';
2199 *(strchr(dict->dictname, '-')) = '\0';
2200 }
2201
2202 if (!dict->fullname || !(*dict->fullname)) {
2203 dictionary_delete(dict);
2204 return FALSE;
2205 }
2206
2207 gtkaspeller = gtkaspeller_new(dict);
2208
2209 if (!gtkaspeller) {
2210 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2211 gtkaspellcheckers->error_message);
2212 } else {
2213 if (gtkaspell->alternate_speller)
2214 gtkaspeller_delete(gtkaspell->alternate_speller);
2215 gtkaspell->alternate_speller = gtkaspeller;
2216 }
2217
2218 dictionary_delete(dict);
2219
2220 return TRUE;
2221 }
2222
2223 /* Menu call backs */
2224
2225 /* change_dict_cb() - Menu callback : change dict */
change_dict_cb(GtkWidget * w,GtkAspell * gtkaspell)2226 static void change_dict_cb(GtkWidget *w, GtkAspell *gtkaspell)
2227 {
2228 gchar *fullname;
2229
2230 fullname = (gchar *) g_object_get_data(G_OBJECT(w), "dict_name");
2231
2232 if (!g_strcmp0(fullname, _("None")))
2233 return;
2234
2235 gtkaspell_change_dict(gtkaspell, fullname, TRUE);
2236 gtkaspell_dict_changed(gtkaspell);
2237
2238 if (gtkaspell->menu_changed_cb)
2239 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2240 }
2241
switch_to_alternate_cb(GtkWidget * w,gpointer data)2242 static void switch_to_alternate_cb(GtkWidget *w,
2243 gpointer data)
2244 {
2245 GtkAspell *gtkaspell = (GtkAspell *) data;
2246 gtkaspell_use_alternate_dict(gtkaspell);
2247 gtkaspell_dict_changed(gtkaspell);
2248
2249 if (gtkaspell->menu_changed_cb)
2250 gtkaspell->menu_changed_cb(gtkaspell->menu_changed_data);
2251 }
2252
2253 /* Misc. helper functions */
2254
set_point_continue(GtkAspell * gtkaspell)2255 static void set_point_continue(GtkAspell *gtkaspell)
2256 {
2257 gtkaspell->ctx.set_position(gtkaspell->ctx.data, gtkaspell->orig_pos);
2258
2259 if (gtkaspell->continue_check)
2260 gtkaspell->continue_check((gpointer *) gtkaspell->ctx.data);
2261 }
2262
allocate_color(GtkAspell * gtkaspell,gint rgbvalue)2263 static void allocate_color(GtkAspell *gtkaspell, gint rgbvalue)
2264 {
2265 GtkTextBuffer *buffer = gtk_text_view_get_buffer(gtkaspell->gtktext);
2266 GdkColor *color = &(gtkaspell->highlight);
2267
2268 /* Shameless copy from Sylpheed's gtkutils.c */
2269 color->pixel = 0L;
2270 color->red = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0)
2271 * 65535.0);
2272 color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >> 8) / 255.0)
2273 * 65535.0);
2274 color->blue = (int) (((gdouble) (rgbvalue & 0x0000ff) / 255.0)
2275 * 65535.0);
2276
2277 if (rgbvalue != 0)
2278 gtk_text_buffer_create_tag(buffer, "misspelled",
2279 "foreground-gdk", color, NULL);
2280 else
2281 gtk_text_buffer_create_tag(buffer, "misspelled",
2282 "underline", PANGO_UNDERLINE_ERROR, NULL);
2283
2284 }
2285
change_color(GtkAspell * gtkaspell,gint start,gint end,gchar * newtext,GdkColor * color)2286 static void change_color(GtkAspell * gtkaspell,
2287 gint start, gint end,
2288 gchar *newtext,
2289 GdkColor *color)
2290 {
2291 GtkTextView *gtktext;
2292 GtkTextBuffer *buffer;
2293 GtkTextIter startiter, enditer;
2294
2295 if (start > end)
2296 return;
2297
2298 gtktext = gtkaspell->gtktext;
2299
2300 buffer = gtk_text_view_get_buffer(gtktext);
2301 gtk_text_buffer_get_iter_at_offset(buffer, &startiter, start);
2302 gtk_text_buffer_get_iter_at_offset(buffer, &enditer, end);
2303 if (color)
2304 gtk_text_buffer_apply_tag_by_name(buffer, "misspelled",
2305 &startiter, &enditer);
2306 else {
2307 gtk_text_iter_forward_char(&enditer);
2308 gtk_text_buffer_remove_tag_by_name(buffer, "misspelled",
2309 &startiter, &enditer);
2310 }
2311 }
2312
2313 /* compare_dict () - compare 2 dict names */
compare_dict(Dictionary * a,Dictionary * b)2314 static gint compare_dict(Dictionary *a, Dictionary *b)
2315 {
2316 guint aparts = 0, bparts = 0;
2317 guint i;
2318
2319 for (i=0; i < strlen(a->dictname); i++)
2320 if (a->dictname[i] == '-')
2321 aparts++;
2322 for (i=0; i < strlen(b->dictname); i++)
2323 if (b->dictname[i] == '-')
2324 bparts++;
2325
2326 if (aparts != bparts)
2327 return (aparts < bparts) ? -1 : +1;
2328 else {
2329 gint compare;
2330 compare = g_strcmp0(a->dictname, b->dictname);
2331 if (!compare)
2332 compare = g_strcmp0(a->fullname, b->fullname);
2333 return compare;
2334 }
2335 }
2336
dictionary_delete(Dictionary * dict)2337 static void dictionary_delete(Dictionary *dict)
2338 {
2339 g_free(dict->fullname);
2340 g_free(dict->dictname);
2341 g_free(dict);
2342 }
2343
dictionary_dup(const Dictionary * dict)2344 static Dictionary *dictionary_dup(const Dictionary *dict)
2345 {
2346 Dictionary *dict2;
2347
2348 dict2 = g_new(Dictionary, 1);
2349
2350 dict2->fullname = g_strdup(dict->fullname);
2351 dict2->dictname = g_strdup(dict->dictname);
2352
2353 return dict2;
2354 }
2355
gtkaspell_free_suggestions_list(GtkAspell * gtkaspell)2356 void gtkaspell_free_suggestions_list(GtkAspell *gtkaspell)
2357 {
2358 GList *list;
2359
2360 for (list = gtkaspell->suggestions_list; list != NULL;
2361 list = list->next)
2362 g_free(list->data);
2363
2364 g_list_free(gtkaspell->suggestions_list);
2365
2366 gtkaspell->max_sug = -1;
2367 gtkaspell->suggestions_list = NULL;
2368 }
2369
reset_theword_data(GtkAspell * gtkaspell)2370 static void reset_theword_data(GtkAspell *gtkaspell)
2371 {
2372 gtkaspell->start_pos = 0;
2373 gtkaspell->end_pos = 0;
2374 gtkaspell->theword[0] = 0;
2375 gtkaspell->max_sug = -1;
2376
2377 gtkaspell_free_suggestions_list(gtkaspell);
2378 }
2379
free_checkers(gpointer elt,gpointer data)2380 static void free_checkers(gpointer elt, gpointer data)
2381 {
2382 GtkAspeller *gtkaspeller = elt;
2383
2384 cm_return_if_fail(gtkaspeller);
2385
2386 gtkaspeller_real_delete(gtkaspeller);
2387 }
2388
gtkaspell_get_default_dictionary(GtkAspell * gtkaspell)2389 gchar *gtkaspell_get_default_dictionary(GtkAspell *gtkaspell)
2390 {
2391 if (gtkaspell && gtkaspell->gtkaspeller &&
2392 gtkaspell->gtkaspeller->dictionary)
2393 return gtkaspell->gtkaspeller->dictionary->dictname;
2394 else
2395 return NULL;
2396 }
2397 #endif
2398