root/src/editor/spell.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. spell_decode_lang
  2. spell_available
  3. aspell_get_lang
  4. aspell_get_lang_list
  5. aspell_set_lang
  6. spell_dialog_spell_suggest_show
  7. aspell_add_to_dict
  8. aspell_suggest
  9. aspell_check
  10. aspell_array_clean
  11. aspell_init
  12. aspell_clean
  13. edit_suggest_current_word
  14. edit_spellcheck_file
  15. edit_set_spell_lang
  16. spell_dialog_lang_list_show

   1 /*
   2    Editor spell checker
   3 
   4    Copyright (C) 2012-2024
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Ilia Maslakov <il.smind@gmail.com>, 2012
   9    Andrew Borodin <aborodin@vmail.ru>, 2013-2024
  10 
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  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"           /* MC_PTR_FREE() */
  40 #include "lib/tty/tty.h"        /* COLS, LINES */
  41 
  42 #include "src/setup.h"
  43 
  44 #include "editwidget.h"
  45 
  46 #include "spell.h"
  47 
  48 /*** global variables ****************************************************************************/
  49 
  50 /*** file scope macro definitions ****************************************************************/
  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 /*** file scope type declarations ****************************************************************/
  59 
  60 typedef struct aspell_struct
  61 {
  62     AspellConfig *config;
  63     AspellSpeller *speller;
  64 } spell_t;
  65 
  66 /*** forward declarations (file scope functions) *************************************************/
  67 
  68 /*** file scope variables ************************************************************************/
  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     /* *INDENT-OFF* */
 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     /* *INDENT-ON* */
 136 };
 137 
 138 /* --------------------------------------------------------------------------------------------- */
 139 /*** file scope functions ************************************************************************/
 140 /* --------------------------------------------------------------------------------------------- */
 141 /**
 142  * Found the language name by language code. For example: en_US -> American English.
 143  *
 144  * @param code Short name of the language (ru, en, pl, uk, etc...)
 145  * @return language name
 146  */
 147 
 148 static const char *
 149 spell_decode_lang (const char *code)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Checks if aspell library and symbols are available.
 165  *
 166  * @return FALSE or error
 167  */
 168 
 169 static gboolean
 170 spell_available (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Get the current language name.
 215  *
 216  * @return language name
 217  */
 218 
 219 static const char *
 220 aspell_get_lang (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Get array of available languages.
 231  *
 232  * @param lang_list Array of languages. Must be cleared before use
 233  * @return language list length
 234  */
 235 
 236 static unsigned int
 237 aspell_get_lang_list (GPtrArray *lang_list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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     /* the returned pointer should _not_ need to be deleted */
 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  * Set the language.
 266  *
 267  * @param lang Language name
 268  * @return FALSE or error
 269  */
 270 
 271 static gboolean
 272 aspell_set_lang (const char *lang)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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         /* the returned pointer should _not_ need to be deleted */
 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  * Show suggests for the current word.
 313  *
 314  * @param edit Editor object
 315  * @param word Word for spell check
 316  * @param new_word Word to replace the incorrect word
 317  * @param suggest Array of suggests for current word
 318  * @return code of pressed button
 319  */
 320 
 321 static int
 322 spell_dialog_spell_suggest_show (WEdit *edit, const char *word, char **new_word,
     /* [previous][next][first][last][top][bottom][index][help]  */
 323                                  const GPtrArray *suggest)
 324 {
 325 
 326     int sug_dlg_h = 14;         /* dialog height */
 327     int sug_dlg_w = 29;         /* dialog width */
 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     /* calculate the dialog metrics */
 347     xpos = (COLS - sug_dlg_w) / 2;
 348     ypos = (LINES - sug_dlg_h) * 2 / 3;
 349 
 350     /* Sometimes menu can hide replaced text. I don't like it */
 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  * Add word to personal dictionary.
 411  *
 412  * @param word Word for spell check
 413  * @param word_size  Word size (in bytes)
 414  * @return FALSE or error
 415  */
 416 static gboolean
 417 aspell_add_to_dict (const char *word, int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Examine dictionaries and suggest possible words that may repalce the incorrect word.
 441  *
 442  * @param suggest array of words to iterate through
 443  * @param word Word for spell check
 444  * @param word_size Word size (in bytes)
 445  * @return count of suggests for the word
 446  */
 447 
 448 static unsigned int
 449 aspell_suggest (GPtrArray *suggest, const char *word, const int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Check word.
 485  *
 486  * @param word Word for spell check
 487  * @param word_size Word size (in bytes)
 488  * @return FALSE if word is not in the dictionary
 489  */
 490 
 491 static gboolean
 492 aspell_check (const char *word, const int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Clear the array of languages.
 505  *
 506  * @param array Array of languages
 507  */
 508 
 509 static void
 510 aspell_array_clean (GPtrArray *array)
     /* [previous][next][first][last][top][bottom][index][help]  */
 511 {
 512     if (array != NULL)
 513         g_ptr_array_free (array, TRUE);
 514 }
 515 
 516 /* --------------------------------------------------------------------------------------------- */
 517 /*** public functions ****************************************************************************/
 518 /* --------------------------------------------------------------------------------------------- */
 519 /**
 520  * Initialization of Aspell support.
 521  */
 522 
 523 void
 524 aspell_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Deinitialization of Aspell support.
 565  */
 566 
 567 void
 568 aspell_clean (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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     /* search start of word to spell check */
 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)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Show dialog to select language for spell check.
 728  *
 729  * @param languages Array of available languages
 730  * @return name of chosen language
 731  */
 732 
 733 const char *
 734 spell_dialog_lang_list_show (const GPtrArray *languages)
     /* [previous][next][first][last][top][bottom][index][help]  */
 735 {
 736 
 737     int lang_dlg_h = 12;        /* dialog height */
 738     int lang_dlg_w = 30;        /* dialog width */
 739     const char *selected_lang = NULL;
 740     unsigned int i;
 741     int res;
 742     Listbox *lang_list;
 743 
 744     /* Create listbox */
 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 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */