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-2025
   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         message (D_ERROR, MSG_ERROR, "%s",
 424                  mc_aspell_speller_error_message (global_speller->speller));
 425         return FALSE;
 426     }
 427 
 428     mc_aspell_speller_save_all_word_lists (global_speller->speller);
 429 
 430     if (mc_aspell_speller_error (global_speller->speller) != 0)
 431     {
 432         message (D_ERROR, MSG_ERROR, "%s",
 433                  mc_aspell_speller_error_message (global_speller->speller));
 434         return FALSE;
 435     }
 436 
 437     return TRUE;
 438 }
 439 
 440 /* --------------------------------------------------------------------------------------------- */
 441 /**
 442  * Examine dictionaries and suggest possible words that may repalce the incorrect word.
 443  *
 444  * @param suggest array of words to iterate through
 445  * @param word Word for spell check
 446  * @param word_size Word size (in bytes)
 447  * @return count of suggests for the word
 448  */
 449 
 450 static unsigned int
 451 aspell_suggest (GPtrArray *suggest, const char *word, const int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 452 {
 453     unsigned int size = 0;
 454 
 455     if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
 456     {
 457         const AspellWordList *wordlist;
 458 
 459         wordlist = mc_aspell_speller_suggest (global_speller->speller, word, word_size);
 460         if (wordlist != NULL)
 461         {
 462             AspellStringEnumeration *elements = NULL;
 463             unsigned int i;
 464 
 465             elements = mc_aspell_word_list_elements (wordlist);
 466             size = mc_aspell_word_list_size (wordlist);
 467 
 468             for (i = 0; i < size; i++)
 469             {
 470                 const char *cur_sugg_word;
 471 
 472                 cur_sugg_word = mc_aspell_string_enumeration_next (elements);
 473                 if (cur_sugg_word != NULL)
 474                     g_ptr_array_add (suggest, g_strdup (cur_sugg_word));
 475             }
 476 
 477             mc_delete_aspell_string_enumeration (elements);
 478         }
 479     }
 480 
 481     return size;
 482 }
 483 
 484 /* --------------------------------------------------------------------------------------------- */
 485 /**
 486  * Check word.
 487  *
 488  * @param word Word for spell check
 489  * @param word_size Word size (in bytes)
 490  * @return FALSE if word is not in the dictionary
 491  */
 492 
 493 static gboolean
 494 aspell_check (const char *word, const int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 495 {
 496     int res = 0;
 497 
 498     if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
 499         res = mc_aspell_speller_check (global_speller->speller, word, word_size);
 500 
 501     return (res == 1);
 502 }
 503 
 504 /* --------------------------------------------------------------------------------------------- */
 505 /**
 506  * Clear the array of languages.
 507  *
 508  * @param array Array of languages
 509  */
 510 
 511 static void
 512 aspell_array_clean (GPtrArray *array)
     /* [previous][next][first][last][top][bottom][index][help]  */
 513 {
 514     if (array != NULL)
 515         g_ptr_array_free (array, TRUE);
 516 }
 517 
 518 /* --------------------------------------------------------------------------------------------- */
 519 /*** public functions ****************************************************************************/
 520 /* --------------------------------------------------------------------------------------------- */
 521 /**
 522  * Initialization of Aspell support.
 523  */
 524 
 525 void
 526 aspell_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 527 {
 528     AspellCanHaveError *error = NULL;
 529 
 530     if (strcmp (spell_language, "NONE") == 0)
 531         return;
 532 
 533     if (global_speller != NULL)
 534         return;
 535 
 536     global_speller = g_try_malloc (sizeof (spell_t));
 537     if (global_speller == NULL)
 538         return;
 539 
 540     if (!spell_available ())
 541     {
 542         MC_PTR_FREE (global_speller);
 543         return;
 544     }
 545 
 546     global_speller->config = mc_new_aspell_config ();
 547     global_speller->speller = NULL;
 548 
 549     if (spell_language != NULL)
 550         mc_aspell_config_replace (global_speller->config, "lang", spell_language);
 551 
 552     error = mc_new_aspell_speller (global_speller->config);
 553 
 554     if (mc_aspell_error_number (error) == 0)
 555         global_speller->speller = mc_to_aspell_speller (error);
 556     else
 557     {
 558         message (D_ERROR, MSG_ERROR, "%s", mc_aspell_error_message (error));
 559         mc_delete_aspell_can_have_error (error);
 560         aspell_clean ();
 561     }
 562 }
 563 
 564 /* --------------------------------------------------------------------------------------------- */
 565 /**
 566  * Deinitialization of Aspell support.
 567  */
 568 
 569 void
 570 aspell_clean (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 571 {
 572     if (global_speller == NULL)
 573         return;
 574 
 575     if (global_speller->speller != NULL)
 576         mc_delete_aspell_speller (global_speller->speller);
 577 
 578     if (global_speller->config != NULL)
 579         mc_delete_aspell_config (global_speller->config);
 580 
 581     MC_PTR_FREE (global_speller);
 582 
 583     g_module_close (spell_module);
 584     spell_module = NULL;
 585 }
 586 
 587 /* --------------------------------------------------------------------------------------------- */
 588 
 589 int
 590 edit_suggest_current_word (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 591 {
 592     gsize cut_len = 0;
 593     gsize word_len = 0;
 594     off_t word_start = 0;
 595     int retval = B_SKIP_WORD;
 596     GString *match_word;
 597 
 598     /* search start of word to spell check */
 599     match_word = edit_buffer_get_word_from_pos (&edit->buffer, edit->buffer.curs1, &word_start,
 600                                                 &cut_len);
 601     word_len = match_word->len;
 602 
 603 #ifdef HAVE_CHARSET
 604     if (mc_global.source_codepage >= 0 && mc_global.source_codepage != mc_global.display_codepage)
 605     {
 606         GString *tmp_word;
 607 
 608         tmp_word = str_convert_to_display (match_word->str);
 609         g_string_free (match_word, TRUE);
 610         match_word = tmp_word;
 611     }
 612 #endif
 613     if (match_word != NULL)
 614     {
 615         if (!aspell_check (match_word->str, (int) word_len))
 616         {
 617             GPtrArray *suggest;
 618             unsigned int res;
 619             guint i;
 620 
 621             suggest = g_ptr_array_new_with_free_func (g_free);
 622 
 623             res = aspell_suggest (suggest, match_word->str, (int) word_len);
 624             if (res != 0)
 625             {
 626                 char *new_word = NULL;
 627 
 628                 edit->found_start = word_start;
 629                 edit->found_len = word_len;
 630                 edit->force |= REDRAW_PAGE;
 631                 edit_scroll_screen_over_cursor (edit);
 632                 edit_render_keypress (edit);
 633 
 634                 retval =
 635                     spell_dialog_spell_suggest_show (edit, match_word->str, &new_word, suggest);
 636                 edit_cursor_move (edit, word_len - cut_len);
 637 
 638                 if (retval == B_ENTER && new_word != NULL)
 639                 {
 640 #ifdef HAVE_CHARSET
 641                     if (mc_global.source_codepage >= 0 &&
 642                         (mc_global.source_codepage != mc_global.display_codepage))
 643                     {
 644                         GString *tmp_word;
 645 
 646                         tmp_word = str_convert_to_input (new_word);
 647                         MC_PTR_FREE (new_word);
 648                         if (tmp_word != NULL)
 649                             new_word = g_string_free (tmp_word, FALSE);
 650                     }
 651 #endif
 652                     for (i = 0; i < word_len; i++)
 653                         edit_backspace (edit, TRUE);
 654                     if (new_word != NULL)
 655                     {
 656                         for (i = 0; new_word[i] != '\0'; i++)
 657                             edit_insert (edit, new_word[i]);
 658                         g_free (new_word);
 659                     }
 660                 }
 661                 else if (retval == B_ADD_WORD)
 662                     aspell_add_to_dict (match_word->str, (int) word_len);
 663             }
 664 
 665             g_ptr_array_free (suggest, TRUE);
 666             edit->found_start = 0;
 667             edit->found_len = 0;
 668         }
 669 
 670         g_string_free (match_word, TRUE);
 671     }
 672 
 673     return retval;
 674 }
 675 
 676 /* --------------------------------------------------------------------------------------------- */
 677 
 678 void
 679 edit_spellcheck_file (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 680 {
 681     if (edit->buffer.curs_line > 0)
 682     {
 683         edit_cursor_move (edit, -edit->buffer.curs1);
 684         edit_move_to_prev_col (edit, 0);
 685         edit_update_curs_row (edit);
 686     }
 687 
 688     do
 689     {
 690         int c1, c2;
 691 
 692         c2 = edit_buffer_get_current_byte (&edit->buffer);
 693 
 694         do
 695         {
 696             if (edit->buffer.curs1 >= edit->buffer.size)
 697                 return;
 698 
 699             c1 = c2;
 700             edit_cursor_move (edit, 1);
 701             c2 = edit_buffer_get_current_byte (&edit->buffer);
 702         }
 703         while (is_break_char (c1) || is_break_char (c2));
 704     }
 705     while (edit_suggest_current_word (edit) != B_CANCEL);
 706 }
 707 
 708 /* --------------------------------------------------------------------------------------------- */
 709 
 710 void
 711 edit_set_spell_lang (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 712 {
 713     GPtrArray *lang_list;
 714 
 715     lang_list = g_ptr_array_new_with_free_func (g_free);
 716     if (aspell_get_lang_list (lang_list) != 0)
 717     {
 718         const char *lang;
 719 
 720         lang = spell_dialog_lang_list_show (lang_list);
 721         if (lang != NULL)
 722             (void) aspell_set_lang (lang);
 723     }
 724     aspell_array_clean (lang_list);
 725 }
 726 
 727 /* --------------------------------------------------------------------------------------------- */
 728 /**
 729  * Show dialog to select language for spell check.
 730  *
 731  * @param languages Array of available languages
 732  * @return name of chosen language
 733  */
 734 
 735 const char *
 736 spell_dialog_lang_list_show (const GPtrArray *languages)
     /* [previous][next][first][last][top][bottom][index][help]  */
 737 {
 738 
 739     int lang_dlg_h = 12;        /* dialog height */
 740     int lang_dlg_w = 30;        /* dialog width */
 741     const char *selected_lang = NULL;
 742     unsigned int i;
 743     int res;
 744     Listbox *lang_list;
 745 
 746     /* Create listbox */
 747     lang_list = listbox_window_centered_new (-1, -1, lang_dlg_h, lang_dlg_w,
 748                                              _("Select language"), "[ASpell]");
 749 
 750     for (i = 0; i < languages->len; i++)
 751         LISTBOX_APPEND_TEXT (lang_list, 0, g_ptr_array_index (languages, i), NULL, FALSE);
 752 
 753     res = listbox_run (lang_list);
 754     if (res >= 0)
 755         selected_lang = g_ptr_array_index (languages, (unsigned int) res);
 756 
 757     return selected_lang;
 758 }
 759 
 760 /* --------------------------------------------------------------------------------------------- */

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