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

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