Manual pages: mcmcdiffmceditmcview

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 <https://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 #include "lib/charsets.h"
  36 #include "lib/strutil.h"
  37 #include "lib/util.h"     // MC_PTR_FREE()
  38 #include "lib/tty/tty.h"  // COLS, LINES
  39 
  40 #include "src/setup.h"
  41 
  42 #include "editwidget.h"
  43 
  44 #include "spell.h"
  45 
  46 /*** global variables ****************************************************************************/
  47 
  48 /*** file scope macro definitions ****************************************************************/
  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 /*** file scope type declarations ****************************************************************/
  56 
  57 typedef struct aspell_struct
  58 {
  59     AspellConfig *config;
  60     AspellSpeller *speller;
  61 } spell_t;
  62 
  63 /*** forward declarations (file scope functions) *************************************************/
  64 
  65 /*** file scope variables ************************************************************************/
  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 /*** file scope functions ************************************************************************/
 138 /* --------------------------------------------------------------------------------------------- */
 139 /**
 140  * Found the language name by language code. For example: en_US -> American English.
 141  *
 142  * @param code Short name of the language (ru, en, pl, uk, etc...)
 143  * @return language name
 144  */
 145 
 146 static const char *
 147 spell_decode_lang (const char *code)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Checks if aspell library and symbols are available.
 163  *
 164  * @return FALSE or error
 165  */
 166 
 167 static gboolean
 168 spell_available (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Get the current language name.
 212  *
 213  * @return language name
 214  */
 215 
 216 static const char *
 217 aspell_get_lang (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Get array of available languages.
 228  *
 229  * @param lang_list Array of languages. Must be cleared before use
 230  * @return language list length
 231  */
 232 
 233 static unsigned int
 234 aspell_get_lang_list (GPtrArray *lang_list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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     // the returned pointer should _not_ need to be deleted
 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  * Set the language.
 263  *
 264  * @param lang Language name
 265  * @return FALSE or error
 266  */
 267 
 268 static gboolean
 269 aspell_set_lang (const char *lang)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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         // the returned pointer should _not_ need to be deleted
 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  * Show suggests for the current word.
 308  *
 309  * @param edit Editor object
 310  * @param word Word for spell check
 311  * @param new_word Word to replace the incorrect word
 312  * @param suggest Array of suggests for current word
 313  * @return code of pressed button
 314  */
 315 
 316 static int
 317 spell_dialog_spell_suggest_show (WEdit *edit, const char *word, char **new_word,
     /* [previous][next][first][last][top][bottom][index][help]  */
 318                                  const GPtrArray *suggest)
 319 {
 320 
 321     int sug_dlg_h = 14;  // dialog height
 322     int sug_dlg_w = 29;  // dialog width
 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     // calculate the dialog metrics
 342     xpos = (COLS - sug_dlg_w) / 2;
 343     ypos = (LINES - sug_dlg_h) * 2 / 3;
 344 
 345     // Sometimes menu can hide replaced text. I don't like it
 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  * Add word to personal dictionary.
 406  *
 407  * @param word Word for spell check
 408  * @param word_size  Word size (in bytes)
 409  * @return FALSE or error
 410  */
 411 static gboolean
 412 aspell_add_to_dict (const char *word, int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Examine dictionaries and suggest possible words that may repalce the incorrect word.
 438  *
 439  * @param suggest array of words to iterate through
 440  * @param word Word for spell check
 441  * @param word_size Word size (in bytes)
 442  * @return count of suggests for the word
 443  */
 444 
 445 static unsigned int
 446 aspell_suggest (GPtrArray *suggest, const char *word, const int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Check word.
 482  *
 483  * @param word Word for spell check
 484  * @param word_size Word size (in bytes)
 485  * @return FALSE if word is not in the dictionary
 486  */
 487 
 488 static gboolean
 489 aspell_check (const char *word, const int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Clear the array of languages.
 502  *
 503  * @param array Array of languages
 504  */
 505 
 506 static void
 507 aspell_array_clean (GPtrArray *array)
     /* [previous][next][first][last][top][bottom][index][help]  */
 508 {
 509     if (array != NULL)
 510         g_ptr_array_free (array, TRUE);
 511 }
 512 
 513 /* --------------------------------------------------------------------------------------------- */
 514 /*** public functions ****************************************************************************/
 515 /* --------------------------------------------------------------------------------------------- */
 516 /**
 517  * Initialization of Aspell support.
 518  */
 519 
 520 void
 521 aspell_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Deinitialization of Aspell support.
 562  */
 563 
 564 void
 565 aspell_clean (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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     // search start of word to spell check
 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)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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  * Show dialog to select language for spell check.
 721  *
 722  * @param languages Array of available languages
 723  * @return name of chosen language
 724  */
 725 
 726 const char *
 727 spell_dialog_lang_list_show (const GPtrArray *languages)
     /* [previous][next][first][last][top][bottom][index][help]  */
 728 {
 729 
 730     int lang_dlg_h = 12;  // dialog height
 731     int lang_dlg_w = 30;  // dialog width
 732     const char *selected_lang = NULL;
 733     unsigned int i;
 734     int res;
 735     Listbox *lang_list;
 736 
 737     // Create listbox
 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 /* --------------------------------------------------------------------------------------------- */

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