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