This source file includes following definitions.
- spell_decode_lang
- spell_available
- aspell_init
- aspell_clean
- aspell_get_lang_list
- aspell_array_clean
- aspell_get_lang
- aspell_set_lang
- aspell_check
- aspell_suggest
- aspell_add_to_dict
- edit_suggest_current_word
- edit_spellcheck_file
- edit_set_spell_lang
- spell_dialog_spell_suggest_show
- spell_dialog_lang_list_show
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 #include <config.h>
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <gmodule.h>
33 #include <aspell.h>
34
35 #ifdef HAVE_CHARSET
36 #include "lib/charsets.h"
37 #endif
38 #include "lib/strutil.h"
39 #include "lib/util.h"
40 #include "lib/tty/tty.h"
41
42 #include "src/setup.h"
43
44 #include "editwidget.h"
45
46 #include "spell.h"
47
48
49
50
51
52 #define B_SKIP_WORD (B_USER+3)
53 #define B_ADD_WORD (B_USER+4)
54
55
56
57 typedef struct aspell_struct
58 {
59 AspellConfig *config;
60 AspellSpeller *speller;
61 } spell_t;
62
63
64
65
66
67 static GModule *spell_module = NULL;
68 static spell_t *global_speller = NULL;
69
70 static AspellConfig *(*mc_new_aspell_config) (void);
71 static int (*mc_aspell_config_replace) (AspellConfig * ths, const char *key, const char *value);
72 static AspellCanHaveError *(*mc_new_aspell_speller) (AspellConfig * config);
73 static unsigned int (*mc_aspell_error_number) (const AspellCanHaveError * ths);
74 static const char *(*mc_aspell_speller_error_message) (const AspellSpeller * ths);
75 static const AspellError *(*mc_aspell_speller_error) (const AspellSpeller * ths);
76
77 static AspellSpeller *(*mc_to_aspell_speller) (AspellCanHaveError * obj);
78 static int (*mc_aspell_speller_check) (AspellSpeller * ths, const char *word, int word_size);
79 static const AspellWordList *(*mc_aspell_speller_suggest) (AspellSpeller * ths,
80 const char *word, int word_size);
81 static AspellStringEnumeration *(*mc_aspell_word_list_elements) (const struct AspellWordList * ths);
82 static const char *(*mc_aspell_config_retrieve) (AspellConfig * ths, const char *key);
83 static void (*mc_delete_aspell_speller) (AspellSpeller * ths);
84 static void (*mc_delete_aspell_config) (AspellConfig * ths);
85 static void (*mc_delete_aspell_can_have_error) (AspellCanHaveError * ths);
86 static const char *(*mc_aspell_error_message) (const AspellCanHaveError * ths);
87 static void (*mc_delete_aspell_string_enumeration) (AspellStringEnumeration * ths);
88 static AspellDictInfoEnumeration *(*mc_aspell_dict_info_list_elements)
89 (const AspellDictInfoList * ths);
90 static AspellDictInfoList *(*mc_get_aspell_dict_info_list) (AspellConfig * config);
91 static const AspellDictInfo *(*mc_aspell_dict_info_enumeration_next)
92 (AspellDictInfoEnumeration * ths);
93 static const char *(*mc_aspell_string_enumeration_next) (AspellStringEnumeration * ths);
94 static void (*mc_delete_aspell_dict_info_enumeration) (AspellDictInfoEnumeration * ths);
95 static unsigned int (*mc_aspell_word_list_size) (const AspellWordList * ths);
96 static const AspellError *(*mc_aspell_error) (const AspellCanHaveError * ths);
97 static int (*mc_aspell_speller_add_to_personal) (AspellSpeller * ths, const char *word,
98 int word_size);
99 static int (*mc_aspell_speller_save_all_word_lists) (AspellSpeller * ths);
100
101 static struct
102 {
103 const char *code;
104 const char *name;
105 } spell_codes_map[] =
106 {
107
108 {"br", N_("Breton")},
109 {"cs", N_("Czech")},
110 {"cy", N_("Welsh")},
111 {"da", N_("Danish")},
112 {"de", N_("German")},
113 {"el", N_("Greek")},
114 {"en", N_("English")},
115 {"en_GB", N_("British English")},
116 {"en_CA", N_("Canadian English")},
117 {"en_US", N_("American English")},
118 {"eo", N_("Esperanto")},
119 {"es", N_("Spanish")},
120 {"fo", N_("Faroese")},
121 {"fr", N_("French")},
122 {"it", N_("Italian")},
123 {"nl", N_("Dutch")},
124 {"no", N_("Norwegian")},
125 {"pl", N_("Polish")},
126 {"pt", N_("Portuguese")},
127 {"ro", N_("Romanian")},
128 {"ru", N_("Russian")},
129 {"sk", N_("Slovak")},
130 {"sv", N_("Swedish")},
131 {"uk", N_("Ukrainian")},
132 {NULL, NULL}
133
134 };
135
136
137
138
139
140
141
142
143
144
145 static const char *
146 spell_decode_lang (const char *code)
147 {
148 size_t i;
149
150 for (i = 0; spell_codes_map[i].code != NULL; i++)
151 {
152 if (strcmp (spell_codes_map[i].code, code) == 0)
153 return _(spell_codes_map[i].name);
154 }
155
156 return code;
157 }
158
159
160
161
162
163
164
165
166 static gboolean
167 spell_available (void)
168 {
169 gchar *spell_module_fname;
170 gboolean ret = FALSE;
171
172 if (spell_module != NULL)
173 return TRUE;
174
175 spell_module_fname = g_module_build_path (NULL, "libaspell");
176 spell_module = g_module_open (spell_module_fname, G_MODULE_BIND_LAZY);
177
178 g_free (spell_module_fname);
179
180 if (spell_module == NULL)
181 return FALSE;
182
183 if (!g_module_symbol (spell_module, "new_aspell_config", (void *) &mc_new_aspell_config))
184 goto error_ret;
185
186 if (!g_module_symbol (spell_module, "aspell_dict_info_list_elements",
187 (void *) &mc_aspell_dict_info_list_elements))
188 goto error_ret;
189
190 if (!g_module_symbol (spell_module, "aspell_dict_info_enumeration_next",
191 (void *) &mc_aspell_dict_info_enumeration_next))
192 goto error_ret;
193
194 if (!g_module_symbol (spell_module, "new_aspell_speller", (void *) &mc_new_aspell_speller))
195 goto error_ret;
196
197 if (!g_module_symbol (spell_module, "aspell_error_number", (void *) &mc_aspell_error_number))
198 goto error_ret;
199
200 if (!g_module_symbol (spell_module, "aspell_speller_error_message",
201 (void *) &mc_aspell_speller_error_message))
202 goto error_ret;
203
204 if (!g_module_symbol (spell_module, "aspell_speller_error", (void *) &mc_aspell_speller_error))
205 goto error_ret;
206
207 if (!g_module_symbol (spell_module, "aspell_error", (void *) &mc_aspell_error))
208 goto error_ret;
209
210 if (!g_module_symbol (spell_module, "to_aspell_speller", (void *) &mc_to_aspell_speller))
211 goto error_ret;
212
213 if (!g_module_symbol (spell_module, "aspell_speller_check", (void *) &mc_aspell_speller_check))
214 goto error_ret;
215
216 if (!g_module_symbol
217 (spell_module, "aspell_speller_suggest", (void *) &mc_aspell_speller_suggest))
218 goto error_ret;
219
220 if (!g_module_symbol
221 (spell_module, "aspell_word_list_elements", (void *) &mc_aspell_word_list_elements))
222 goto error_ret;
223
224 if (!g_module_symbol (spell_module, "aspell_string_enumeration_next",
225 (void *) &mc_aspell_string_enumeration_next))
226 goto error_ret;
227
228 if (!g_module_symbol
229 (spell_module, "aspell_config_replace", (void *) &mc_aspell_config_replace))
230 goto error_ret;
231
232 if (!g_module_symbol (spell_module, "aspell_error_message", (void *) &mc_aspell_error_message))
233 goto error_ret;
234
235 if (!g_module_symbol
236 (spell_module, "delete_aspell_speller", (void *) &mc_delete_aspell_speller))
237 goto error_ret;
238
239 if (!g_module_symbol (spell_module, "delete_aspell_config", (void *) &mc_delete_aspell_config))
240 goto error_ret;
241
242 if (!g_module_symbol (spell_module, "delete_aspell_string_enumeration",
243 (void *) &mc_delete_aspell_string_enumeration))
244 goto error_ret;
245
246 if (!g_module_symbol (spell_module, "get_aspell_dict_info_list",
247 (void *) &mc_get_aspell_dict_info_list))
248 goto error_ret;
249
250 if (!g_module_symbol (spell_module, "delete_aspell_can_have_error",
251 (void *) &mc_delete_aspell_can_have_error))
252 goto error_ret;
253
254 if (!g_module_symbol (spell_module, "delete_aspell_dict_info_enumeration",
255 (void *) &mc_delete_aspell_dict_info_enumeration))
256 goto error_ret;
257
258 if (!g_module_symbol
259 (spell_module, "aspell_config_retrieve", (void *) &mc_aspell_config_retrieve))
260 goto error_ret;
261
262 if (!g_module_symbol
263 (spell_module, "aspell_word_list_size", (void *) &mc_aspell_word_list_size))
264 goto error_ret;
265
266 if (!g_module_symbol (spell_module, "aspell_speller_add_to_personal",
267 (void *) &mc_aspell_speller_add_to_personal))
268 goto error_ret;
269
270 if (!g_module_symbol (spell_module, "aspell_speller_save_all_word_lists",
271 (void *) &mc_aspell_speller_save_all_word_lists))
272 goto error_ret;
273
274 ret = TRUE;
275
276 error_ret:
277 if (!ret)
278 {
279 g_module_close (spell_module);
280 spell_module = NULL;
281 }
282 return ret;
283 }
284
285
286
287
288
289
290
291
292 void
293 aspell_init (void)
294 {
295 AspellCanHaveError *error = NULL;
296
297 if (strcmp (spell_language, "NONE") == 0)
298 return;
299
300 if (global_speller != NULL)
301 return;
302
303 global_speller = g_try_malloc (sizeof (spell_t));
304 if (global_speller == NULL)
305 return;
306
307 if (!spell_available ())
308 {
309 MC_PTR_FREE (global_speller);
310 return;
311 }
312
313 global_speller->config = mc_new_aspell_config ();
314 global_speller->speller = NULL;
315
316 if (spell_language != NULL)
317 mc_aspell_config_replace (global_speller->config, "lang", spell_language);
318
319 error = mc_new_aspell_speller (global_speller->config);
320
321 if (mc_aspell_error_number (error) == 0)
322 global_speller->speller = mc_to_aspell_speller (error);
323 else
324 {
325 edit_error_dialog (_("Error"), mc_aspell_error_message (error));
326 mc_delete_aspell_can_have_error (error);
327 aspell_clean ();
328 }
329 }
330
331
332
333
334
335
336 void
337 aspell_clean (void)
338 {
339 if (global_speller == NULL)
340 return;
341
342 if (global_speller->speller != NULL)
343 mc_delete_aspell_speller (global_speller->speller);
344
345 if (global_speller->config != NULL)
346 mc_delete_aspell_config (global_speller->config);
347
348 MC_PTR_FREE (global_speller);
349
350 g_module_close (spell_module);
351 spell_module = NULL;
352 }
353
354
355
356
357
358
359
360
361
362 unsigned int
363 aspell_get_lang_list (GPtrArray * lang_list)
364 {
365 AspellDictInfoList *dlist;
366 AspellDictInfoEnumeration *elem;
367 const AspellDictInfo *entry;
368 unsigned int i = 0;
369
370 if (spell_module == NULL)
371 return 0;
372
373
374 dlist = mc_get_aspell_dict_info_list (global_speller->config);
375 elem = mc_aspell_dict_info_list_elements (dlist);
376
377 while ((entry = mc_aspell_dict_info_enumeration_next (elem)) != NULL)
378 if (entry->name != NULL)
379 {
380 g_ptr_array_add (lang_list, g_strdup (entry->name));
381 i++;
382 }
383
384 mc_delete_aspell_dict_info_enumeration (elem);
385
386 return i;
387 }
388
389
390
391
392
393
394
395
396 void
397 aspell_array_clean (GPtrArray * array)
398 {
399 if (array != NULL)
400 g_ptr_array_free (array, TRUE);
401 }
402
403
404
405
406
407
408
409
410 const char *
411 aspell_get_lang (void)
412 {
413 const char *code;
414
415 code = mc_aspell_config_retrieve (global_speller->config, "lang");
416 return spell_decode_lang (code);
417 }
418
419
420
421
422
423
424
425
426
427 gboolean
428 aspell_set_lang (const char *lang)
429 {
430 if (lang != NULL)
431 {
432 AspellCanHaveError *error;
433 const char *spell_codeset;
434
435 g_free (spell_language);
436 spell_language = g_strdup (lang);
437
438 #ifdef HAVE_CHARSET
439 if (mc_global.source_codepage > 0)
440 spell_codeset = get_codepage_id (mc_global.source_codepage);
441 else
442 #endif
443 spell_codeset = str_detect_termencoding ();
444
445 mc_aspell_config_replace (global_speller->config, "lang", lang);
446 mc_aspell_config_replace (global_speller->config, "encoding", spell_codeset);
447
448
449 if (global_speller->speller != NULL)
450 mc_delete_aspell_speller (global_speller->speller);
451
452 global_speller->speller = NULL;
453
454 error = mc_new_aspell_speller (global_speller->config);
455 if (mc_aspell_error (error) != 0)
456 {
457 mc_delete_aspell_can_have_error (error);
458 return FALSE;
459 }
460
461 global_speller->speller = mc_to_aspell_speller (error);
462 }
463 return TRUE;
464 }
465
466
467
468
469
470
471
472
473
474
475 gboolean
476 aspell_check (const char *word, const int word_size)
477 {
478 int res = 0;
479
480 if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
481 res = mc_aspell_speller_check (global_speller->speller, word, word_size);
482
483 return (res == 1);
484 }
485
486
487
488
489
490
491
492
493
494
495
496 unsigned int
497 aspell_suggest (GPtrArray * suggest, const char *word, const int word_size)
498 {
499 unsigned int size = 0;
500
501 if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
502 {
503 const AspellWordList *wordlist;
504
505 wordlist = mc_aspell_speller_suggest (global_speller->speller, word, word_size);
506 if (wordlist != NULL)
507 {
508 AspellStringEnumeration *elements = NULL;
509 unsigned int i;
510
511 elements = mc_aspell_word_list_elements (wordlist);
512 size = mc_aspell_word_list_size (wordlist);
513
514 for (i = 0; i < size; i++)
515 {
516 const char *cur_sugg_word;
517
518 cur_sugg_word = mc_aspell_string_enumeration_next (elements);
519 if (cur_sugg_word != NULL)
520 g_ptr_array_add (suggest, g_strdup (cur_sugg_word));
521 }
522
523 mc_delete_aspell_string_enumeration (elements);
524 }
525 }
526
527 return size;
528 }
529
530
531
532
533
534
535
536
537
538 gboolean
539 aspell_add_to_dict (const char *word, int word_size)
540 {
541 mc_aspell_speller_add_to_personal (global_speller->speller, word, word_size);
542
543 if (mc_aspell_speller_error (global_speller->speller) != 0)
544 {
545 edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
546 return FALSE;
547 }
548
549 mc_aspell_speller_save_all_word_lists (global_speller->speller);
550
551 if (mc_aspell_speller_error (global_speller->speller) != 0)
552 {
553 edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
554 return FALSE;
555 }
556
557 return TRUE;
558 }
559
560
561
562 int
563 edit_suggest_current_word (WEdit * edit)
564 {
565 gsize cut_len = 0;
566 gsize word_len = 0;
567 off_t word_start = 0;
568 int retval = B_SKIP_WORD;
569 GString *match_word;
570
571
572 match_word = edit_buffer_get_word_from_pos (&edit->buffer, edit->buffer.curs1, &word_start,
573 &cut_len);
574 word_len = match_word->len;
575
576 #ifdef HAVE_CHARSET
577 if (mc_global.source_codepage >= 0 && mc_global.source_codepage != mc_global.display_codepage)
578 {
579 GString *tmp_word;
580
581 tmp_word = str_convert_to_display (match_word->str);
582 g_string_free (match_word, TRUE);
583 match_word = tmp_word;
584 }
585 #endif
586 if (match_word != NULL)
587 {
588 if (!aspell_check (match_word->str, (int) word_len))
589 {
590 GPtrArray *suggest;
591 unsigned int res;
592 guint i;
593
594 suggest = g_ptr_array_new_with_free_func (g_free);
595
596 res = aspell_suggest (suggest, match_word->str, (int) word_len);
597 if (res != 0)
598 {
599 char *new_word = NULL;
600
601 edit->found_start = word_start;
602 edit->found_len = word_len;
603 edit->force |= REDRAW_PAGE;
604 edit_scroll_screen_over_cursor (edit);
605 edit_render_keypress (edit);
606
607 retval =
608 spell_dialog_spell_suggest_show (edit, match_word->str, &new_word, suggest);
609 edit_cursor_move (edit, word_len - cut_len);
610
611 if (retval == B_ENTER && new_word != NULL)
612 {
613 #ifdef HAVE_CHARSET
614 if (mc_global.source_codepage >= 0 &&
615 (mc_global.source_codepage != mc_global.display_codepage))
616 {
617 GString *tmp_word;
618
619 tmp_word = str_convert_to_input (new_word);
620 MC_PTR_FREE (new_word);
621 if (tmp_word != NULL)
622 new_word = g_string_free (tmp_word, FALSE);
623 }
624 #endif
625 for (i = 0; i < word_len; i++)
626 edit_backspace (edit, TRUE);
627 if (new_word != NULL)
628 {
629 for (i = 0; new_word[i] != '\0'; i++)
630 edit_insert (edit, new_word[i]);
631 g_free (new_word);
632 }
633 }
634 else if (retval == B_ADD_WORD)
635 aspell_add_to_dict (match_word->str, (int) word_len);
636 }
637
638 g_ptr_array_free (suggest, TRUE);
639 edit->found_start = 0;
640 edit->found_len = 0;
641 }
642
643 g_string_free (match_word, TRUE);
644 }
645
646 return retval;
647 }
648
649
650
651 void
652 edit_spellcheck_file (WEdit * edit)
653 {
654 if (edit->buffer.curs_line > 0)
655 {
656 edit_cursor_move (edit, -edit->buffer.curs1);
657 edit_move_to_prev_col (edit, 0);
658 edit_update_curs_row (edit);
659 }
660
661 do
662 {
663 int c1, c2;
664
665 c2 = edit_buffer_get_current_byte (&edit->buffer);
666
667 do
668 {
669 if (edit->buffer.curs1 >= edit->buffer.size)
670 return;
671
672 c1 = c2;
673 edit_cursor_move (edit, 1);
674 c2 = edit_buffer_get_current_byte (&edit->buffer);
675 }
676 while (is_break_char (c1) || is_break_char (c2));
677 }
678 while (edit_suggest_current_word (edit) != B_CANCEL);
679 }
680
681
682
683 void
684 edit_set_spell_lang (void)
685 {
686 GPtrArray *lang_list;
687
688 lang_list = g_ptr_array_new_with_free_func (g_free);
689 if (aspell_get_lang_list (lang_list) != 0)
690 {
691 const char *lang;
692
693 lang = spell_dialog_lang_list_show (lang_list);
694 if (lang != NULL)
695 (void) aspell_set_lang (lang);
696 }
697 aspell_array_clean (lang_list);
698 }
699
700
701
702
703
704
705
706
707
708
709
710
711 int
712 spell_dialog_spell_suggest_show (WEdit * edit, const char *word, char **new_word,
713 const GPtrArray * suggest)
714 {
715
716 int sug_dlg_h = 14;
717 int sug_dlg_w = 29;
718 int xpos, ypos;
719 char *lang_label;
720 char *word_label;
721 unsigned int i;
722 int res;
723 char *curr = NULL;
724 WDialog *sug_dlg;
725 WGroup *g;
726 WListbox *sug_list;
727 int max_btn_len = 0;
728 int replace_len;
729 int skip_len;
730 int cancel_len;
731 WButton *add_btn;
732 WButton *replace_btn;
733 WButton *skip_btn;
734 WButton *cancel_button;
735 int word_label_len;
736
737
738 xpos = (COLS - sug_dlg_w) / 2;
739 ypos = (LINES - sug_dlg_h) * 2 / 3;
740
741
742 if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + sug_dlg_h - 1))
743 ypos -= sug_dlg_h;
744
745 add_btn = button_new (5, 28, B_ADD_WORD, NORMAL_BUTTON, _("&Add word"), 0);
746 replace_btn = button_new (7, 28, B_ENTER, NORMAL_BUTTON, _("&Replace"), 0);
747 replace_len = button_get_len (replace_btn);
748 skip_btn = button_new (9, 28, B_SKIP_WORD, NORMAL_BUTTON, _("&Skip"), 0);
749 skip_len = button_get_len (skip_btn);
750 cancel_button = button_new (11, 28, B_CANCEL, NORMAL_BUTTON, _("&Cancel"), 0);
751 cancel_len = button_get_len (cancel_button);
752
753 max_btn_len = MAX (replace_len, skip_len);
754 max_btn_len = MAX (max_btn_len, cancel_len);
755
756 lang_label = g_strdup_printf ("%s: %s", _("Language"), aspell_get_lang ());
757 word_label = g_strdup_printf ("%s: %s", _("Misspelled"), word);
758 word_label_len = str_term_width1 (word_label) + 5;
759
760 sug_dlg_w += max_btn_len;
761 sug_dlg_w = MAX (sug_dlg_w, word_label_len) + 1;
762
763 sug_dlg = dlg_create (TRUE, ypos, xpos, sug_dlg_h, sug_dlg_w, WPOS_KEEP_DEFAULT, TRUE,
764 dialog_colors, NULL, NULL, "[ASpell]", _("Check word"));
765 g = GROUP (sug_dlg);
766
767 group_add_widget (g, label_new (1, 2, lang_label));
768 group_add_widget (g, label_new (3, 2, word_label));
769
770 group_add_widget (g, groupbox_new (4, 2, sug_dlg_h - 5, 25, _("Suggest")));
771
772 sug_list = listbox_new (5, 2, sug_dlg_h - 7, 24, FALSE, NULL);
773 for (i = 0; i < suggest->len; i++)
774 listbox_add_item (sug_list, LISTBOX_APPEND_AT_END, 0, g_ptr_array_index (suggest, i), NULL,
775 FALSE);
776 group_add_widget (g, sug_list);
777
778 group_add_widget (g, add_btn);
779 group_add_widget (g, replace_btn);
780 group_add_widget (g, skip_btn);
781 group_add_widget (g, cancel_button);
782
783 res = dlg_run (sug_dlg);
784 if (res == B_ENTER)
785 {
786 char *tmp = NULL;
787 listbox_get_current (sug_list, &curr, NULL);
788
789 if (curr != NULL)
790 tmp = g_strdup (curr);
791 *new_word = tmp;
792 }
793
794 widget_destroy (WIDGET (sug_dlg));
795 g_free (lang_label);
796 g_free (word_label);
797
798 return res;
799 }
800
801
802
803
804
805
806
807
808
809 const char *
810 spell_dialog_lang_list_show (const GPtrArray * languages)
811 {
812
813 int lang_dlg_h = 12;
814 int lang_dlg_w = 30;
815 const char *selected_lang = NULL;
816 unsigned int i;
817 int res;
818 Listbox *lang_list;
819
820
821 lang_list = listbox_window_centered_new (-1, -1, lang_dlg_h, lang_dlg_w,
822 _("Select language"), "[ASpell]");
823
824 for (i = 0; i < languages->len; i++)
825 LISTBOX_APPEND_TEXT (lang_list, 0, g_ptr_array_index (languages, i), NULL, FALSE);
826
827 res = listbox_run (lang_list);
828 if (res >= 0)
829 selected_lang = g_ptr_array_index (languages, (unsigned int) res);
830
831 return selected_lang;
832 }
833
834