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