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