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_init
  4. aspell_clean
  5. aspell_get_lang_list
  6. aspell_array_clean
  7. aspell_get_lang
  8. aspell_set_lang
  9. aspell_check
  10. aspell_suggest
  11. aspell_add_to_dict
  12. edit_suggest_current_word
  13. edit_spellcheck_file
  14. edit_set_spell_lang
  15. spell_dialog_spell_suggest_show
  16. spell_dialog_lang_list_show

   1 /*
   2    Editor spell checker
   3 
   4    Copyright (C) 2012-2021
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Ilia Maslakov <il.smind@gmail.com>, 2012
   9    Andrew Borodin <aborodin@vmail.ru>, 2013, 2021
  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/tty/tty.h"        /* COLS, LINES */
  40 
  41 #include "src/setup.h"
  42 
  43 #include "editwidget.h"
  44 
  45 #include "spell.h"
  46 
  47 /*** global variables ****************************************************************************/
  48 
  49 /*** file scope macro definitions ****************************************************************/
  50 
  51 #define B_SKIP_WORD (B_USER+3)
  52 #define B_ADD_WORD (B_USER+4)
  53 
  54 /*** file scope type declarations ****************************************************************/
  55 
  56 typedef struct aspell_struct
  57 {
  58     AspellConfig *config;
  59     AspellSpeller *speller;
  60 } spell_t;
  61 
  62 /*** file scope variables ************************************************************************/
  63 
  64 static GModule *spell_module = NULL;
  65 static spell_t *global_speller = NULL;
  66 
  67 static AspellConfig *(*mc_new_aspell_config) (void);
  68 static int (*mc_aspell_config_replace) (AspellConfig * ths, const char *key, const char *value);
  69 static AspellCanHaveError *(*mc_new_aspell_speller) (AspellConfig * config);
  70 static unsigned int (*mc_aspell_error_number) (const AspellCanHaveError * ths);
  71 static const char *(*mc_aspell_speller_error_message) (const AspellSpeller * ths);
  72 static const AspellError *(*mc_aspell_speller_error) (const AspellSpeller * ths);
  73 
  74 static AspellSpeller *(*mc_to_aspell_speller) (AspellCanHaveError * obj);
  75 static int (*mc_aspell_speller_check) (AspellSpeller * ths, const char *word, int word_size);
  76 static const AspellWordList *(*mc_aspell_speller_suggest) (AspellSpeller * ths,
  77                                                            const char *word, int word_size);
  78 static AspellStringEnumeration *(*mc_aspell_word_list_elements) (const struct AspellWordList * ths);
  79 static const char *(*mc_aspell_config_retrieve) (AspellConfig * ths, const char *key);
  80 static void (*mc_delete_aspell_speller) (AspellSpeller * ths);
  81 static void (*mc_delete_aspell_config) (AspellConfig * ths);
  82 static void (*mc_delete_aspell_can_have_error) (AspellCanHaveError * ths);
  83 static const char *(*mc_aspell_error_message) (const AspellCanHaveError * ths);
  84 static void (*mc_delete_aspell_string_enumeration) (AspellStringEnumeration * ths);
  85 static AspellDictInfoEnumeration *(*mc_aspell_dict_info_list_elements)
  86     (const AspellDictInfoList * ths);
  87 static AspellDictInfoList *(*mc_get_aspell_dict_info_list) (AspellConfig * config);
  88 static const AspellDictInfo *(*mc_aspell_dict_info_enumeration_next)
  89     (AspellDictInfoEnumeration * ths);
  90 static const char *(*mc_aspell_string_enumeration_next) (AspellStringEnumeration * ths);
  91 static void (*mc_delete_aspell_dict_info_enumeration) (AspellDictInfoEnumeration * ths);
  92 static unsigned int (*mc_aspell_word_list_size) (const AspellWordList * ths);
  93 static const AspellError *(*mc_aspell_error) (const AspellCanHaveError * ths);
  94 static int (*mc_aspell_speller_add_to_personal) (AspellSpeller * ths, const char *word,
  95                                                  int word_size);
  96 static int (*mc_aspell_speller_save_all_word_lists) (AspellSpeller * ths);
  97 
  98 static struct
  99 {
 100     const char *code;
 101     const char *name;
 102 } spell_codes_map[] =
 103 {
 104     /* *INDENT-OFF* */
 105     {"br", N_("Breton")},
 106     {"cs", N_("Czech")},
 107     {"cy", N_("Welsh")},
 108     {"da", N_("Danish")},
 109     {"de", N_("German")},
 110     {"el", N_("Greek")},
 111     {"en", N_("English")},
 112     {"en_GB", N_("British English")},
 113     {"en_CA", N_("Canadian English")},
 114     {"en_US", N_("American English")},
 115     {"eo", N_("Esperanto")},
 116     {"es", N_("Spanish")},
 117     {"fo", N_("Faroese")},
 118     {"fr", N_("French")},
 119     {"it", N_("Italian")},
 120     {"nl", N_("Dutch")},
 121     {"no", N_("Norwegian")},
 122     {"pl", N_("Polish")},
 123     {"pt", N_("Portuguese")},
 124     {"ro", N_("Romanian")},
 125     {"ru", N_("Russian")},
 126     {"sk", N_("Slovak")},
 127     {"sv", N_("Swedish")},
 128     {"uk", N_("Ukrainian")},
 129     {NULL, NULL}
 130     /* *INDENT-ON* */
 131 };
 132 
 133 /*** file scope functions ************************************************************************/
 134 /* --------------------------------------------------------------------------------------------- */
 135 /**
 136  * Found the language name by language code. For example: en_US -> American English.
 137  *
 138  * @param code Short name of the language (ru, en, pl, uk, etc...)
 139  * @return language name
 140  */
 141 
 142 static const char *
 143 spell_decode_lang (const char *code)
     /* [previous][next][first][last][top][bottom][index][help]  */
 144 {
 145     size_t i;
 146 
 147     for (i = 0; spell_codes_map[i].code != NULL; i++)
 148     {
 149         if (strcmp (spell_codes_map[i].code, code) == 0)
 150             return _(spell_codes_map[i].name);
 151     }
 152 
 153     return code;
 154 }
 155 
 156 /* --------------------------------------------------------------------------------------------- */
 157 /**
 158  * Checks if aspell library and symbols are available.
 159  *
 160  * @return FALSE or error
 161  */
 162 
 163 static gboolean
 164 spell_available (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 165 {
 166     gchar *spell_module_fname;
 167     gboolean ret = FALSE;
 168 
 169     if (spell_module != NULL)
 170         return TRUE;
 171 
 172     spell_module_fname = g_module_build_path (NULL, "libaspell");
 173     spell_module = g_module_open (spell_module_fname, G_MODULE_BIND_LAZY);
 174 
 175     g_free (spell_module_fname);
 176 
 177     if (spell_module == NULL)
 178         return FALSE;
 179 
 180     if (!g_module_symbol (spell_module, "new_aspell_config", (void *) &mc_new_aspell_config))
 181         goto error_ret;
 182 
 183     if (!g_module_symbol (spell_module, "aspell_dict_info_list_elements",
 184                           (void *) &mc_aspell_dict_info_list_elements))
 185         goto error_ret;
 186 
 187     if (!g_module_symbol (spell_module, "aspell_dict_info_enumeration_next",
 188                           (void *) &mc_aspell_dict_info_enumeration_next))
 189         goto error_ret;
 190 
 191     if (!g_module_symbol (spell_module, "new_aspell_speller", (void *) &mc_new_aspell_speller))
 192         goto error_ret;
 193 
 194     if (!g_module_symbol (spell_module, "aspell_error_number", (void *) &mc_aspell_error_number))
 195         goto error_ret;
 196 
 197     if (!g_module_symbol (spell_module, "aspell_speller_error_message",
 198                           (void *) &mc_aspell_speller_error_message))
 199         goto error_ret;
 200 
 201     if (!g_module_symbol (spell_module, "aspell_speller_error", (void *) &mc_aspell_speller_error))
 202         goto error_ret;
 203 
 204     if (!g_module_symbol (spell_module, "aspell_error", (void *) &mc_aspell_error))
 205         goto error_ret;
 206 
 207     if (!g_module_symbol (spell_module, "to_aspell_speller", (void *) &mc_to_aspell_speller))
 208         goto error_ret;
 209 
 210     if (!g_module_symbol (spell_module, "aspell_speller_check", (void *) &mc_aspell_speller_check))
 211         goto error_ret;
 212 
 213     if (!g_module_symbol
 214         (spell_module, "aspell_speller_suggest", (void *) &mc_aspell_speller_suggest))
 215         goto error_ret;
 216 
 217     if (!g_module_symbol
 218         (spell_module, "aspell_word_list_elements", (void *) &mc_aspell_word_list_elements))
 219         goto error_ret;
 220 
 221     if (!g_module_symbol (spell_module, "aspell_string_enumeration_next",
 222                           (void *) &mc_aspell_string_enumeration_next))
 223         goto error_ret;
 224 
 225     if (!g_module_symbol
 226         (spell_module, "aspell_config_replace", (void *) &mc_aspell_config_replace))
 227         goto error_ret;
 228 
 229     if (!g_module_symbol (spell_module, "aspell_error_message", (void *) &mc_aspell_error_message))
 230         goto error_ret;
 231 
 232     if (!g_module_symbol
 233         (spell_module, "delete_aspell_speller", (void *) &mc_delete_aspell_speller))
 234         goto error_ret;
 235 
 236     if (!g_module_symbol (spell_module, "delete_aspell_config", (void *) &mc_delete_aspell_config))
 237         goto error_ret;
 238 
 239     if (!g_module_symbol (spell_module, "delete_aspell_string_enumeration",
 240                           (void *) &mc_delete_aspell_string_enumeration))
 241         goto error_ret;
 242 
 243     if (!g_module_symbol (spell_module, "get_aspell_dict_info_list",
 244                           (void *) &mc_get_aspell_dict_info_list))
 245         goto error_ret;
 246 
 247     if (!g_module_symbol (spell_module, "delete_aspell_can_have_error",
 248                           (void *) &mc_delete_aspell_can_have_error))
 249         goto error_ret;
 250 
 251     if (!g_module_symbol (spell_module, "delete_aspell_dict_info_enumeration",
 252                           (void *) &mc_delete_aspell_dict_info_enumeration))
 253         goto error_ret;
 254 
 255     if (!g_module_symbol
 256         (spell_module, "aspell_config_retrieve", (void *) &mc_aspell_config_retrieve))
 257         goto error_ret;
 258 
 259     if (!g_module_symbol
 260         (spell_module, "aspell_word_list_size", (void *) &mc_aspell_word_list_size))
 261         goto error_ret;
 262 
 263     if (!g_module_symbol (spell_module, "aspell_speller_add_to_personal",
 264                           (void *) &mc_aspell_speller_add_to_personal))
 265         goto error_ret;
 266 
 267     if (!g_module_symbol (spell_module, "aspell_speller_save_all_word_lists",
 268                           (void *) &mc_aspell_speller_save_all_word_lists))
 269         goto error_ret;
 270 
 271     ret = TRUE;
 272 
 273   error_ret:
 274     if (!ret)
 275     {
 276         g_module_close (spell_module);
 277         spell_module = NULL;
 278     }
 279     return ret;
 280 }
 281 
 282 /* --------------------------------------------------------------------------------------------- */
 283 /*** public functions ****************************************************************************/
 284 /* --------------------------------------------------------------------------------------------- */
 285 /**
 286  * Initialization of Aspell support.
 287  */
 288 
 289 void
 290 aspell_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 291 {
 292     AspellCanHaveError *error = NULL;
 293 
 294     if (strcmp (spell_language, "NONE") == 0)
 295         return;
 296 
 297     if (global_speller != NULL)
 298         return;
 299 
 300     global_speller = g_try_malloc (sizeof (spell_t));
 301     if (global_speller == NULL)
 302         return;
 303 
 304     if (!spell_available ())
 305     {
 306         MC_PTR_FREE (global_speller);
 307         return;
 308     }
 309 
 310     global_speller->config = mc_new_aspell_config ();
 311     global_speller->speller = NULL;
 312 
 313     if (spell_language != NULL)
 314         mc_aspell_config_replace (global_speller->config, "lang", spell_language);
 315 
 316     error = mc_new_aspell_speller (global_speller->config);
 317 
 318     if (mc_aspell_error_number (error) == 0)
 319         global_speller->speller = mc_to_aspell_speller (error);
 320     else
 321     {
 322         edit_error_dialog (_("Error"), mc_aspell_error_message (error));
 323         mc_delete_aspell_can_have_error (error);
 324         aspell_clean ();
 325     }
 326 }
 327 
 328 /* --------------------------------------------------------------------------------------------- */
 329 /**
 330  * Deinitialization of Aspell support.
 331  */
 332 
 333 void
 334 aspell_clean (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 335 {
 336     if (global_speller == NULL)
 337         return;
 338 
 339     if (global_speller->speller != NULL)
 340         mc_delete_aspell_speller (global_speller->speller);
 341 
 342     if (global_speller->config != NULL)
 343         mc_delete_aspell_config (global_speller->config);
 344 
 345     MC_PTR_FREE (global_speller);
 346 
 347     g_module_close (spell_module);
 348     spell_module = NULL;
 349 }
 350 
 351 /* --------------------------------------------------------------------------------------------- */
 352 /**
 353  * Get array of available languages.
 354  *
 355  * @param lang_list Array of languages. Must be cleared before use
 356  * @return language list length
 357  */
 358 
 359 unsigned int
 360 aspell_get_lang_list (GArray * lang_list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 361 {
 362     AspellDictInfoList *dlist;
 363     AspellDictInfoEnumeration *elem;
 364     const AspellDictInfo *entry;
 365     unsigned int i = 0;
 366 
 367     if (spell_module == NULL)
 368         return 0;
 369 
 370     /* the returned pointer should _not_ need to be deleted */
 371     dlist = mc_get_aspell_dict_info_list (global_speller->config);
 372     elem = mc_aspell_dict_info_list_elements (dlist);
 373 
 374     while ((entry = mc_aspell_dict_info_enumeration_next (elem)) != NULL)
 375     {
 376         if (entry->name != NULL)
 377         {
 378             char *tmp;
 379 
 380             tmp = g_strdup (entry->name);
 381             g_array_append_val (lang_list, tmp);
 382             i++;
 383         }
 384     }
 385 
 386     mc_delete_aspell_dict_info_enumeration (elem);
 387 
 388     return i;
 389 }
 390 
 391 /* --------------------------------------------------------------------------------------------- */
 392 /**
 393  * Clear the array of languages.
 394  *
 395  * @param array Array of languages
 396  */
 397 
 398 void
 399 aspell_array_clean (GArray * array)
     /* [previous][next][first][last][top][bottom][index][help]  */
 400 {
 401     if (array != NULL)
 402     {
 403         guint i = 0;
 404 
 405         for (i = 0; i < array->len; ++i)
 406         {
 407             char *tmp;
 408 
 409             tmp = g_array_index (array, char *, i);
 410             g_free (tmp);
 411         }
 412         g_array_free (array, TRUE);
 413     }
 414 }
 415 
 416 /* --------------------------------------------------------------------------------------------- */
 417 /**
 418  * Get the current language name.
 419  *
 420  * @return language name
 421  */
 422 
 423 const char *
 424 aspell_get_lang (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 425 {
 426     const char *code;
 427 
 428     code = mc_aspell_config_retrieve (global_speller->config, "lang");
 429     return spell_decode_lang (code);
 430 }
 431 
 432 /* --------------------------------------------------------------------------------------------- */
 433 /**
 434  * Set the language.
 435  *
 436  * @param lang Language name
 437  * @return FALSE or error
 438  */
 439 
 440 gboolean
 441 aspell_set_lang (const char *lang)
     /* [previous][next][first][last][top][bottom][index][help]  */
 442 {
 443     if (lang != NULL)
 444     {
 445         AspellCanHaveError *error;
 446         const char *spell_codeset;
 447 
 448         g_free (spell_language);
 449         spell_language = g_strdup (lang);
 450 
 451 #ifdef HAVE_CHARSET
 452         if (mc_global.source_codepage > 0)
 453             spell_codeset = get_codepage_id (mc_global.source_codepage);
 454         else
 455 #endif
 456             spell_codeset = str_detect_termencoding ();
 457 
 458         mc_aspell_config_replace (global_speller->config, "lang", lang);
 459         mc_aspell_config_replace (global_speller->config, "encoding", spell_codeset);
 460 
 461         /* the returned pointer should _not_ need to be deleted */
 462         if (global_speller->speller != NULL)
 463             mc_delete_aspell_speller (global_speller->speller);
 464 
 465         global_speller->speller = NULL;
 466 
 467         error = mc_new_aspell_speller (global_speller->config);
 468         if (mc_aspell_error (error) != 0)
 469         {
 470             mc_delete_aspell_can_have_error (error);
 471             return FALSE;
 472         }
 473 
 474         global_speller->speller = mc_to_aspell_speller (error);
 475     }
 476     return TRUE;
 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 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  * Examine dictionaries and suggest possible words that may repalce the incorrect word.
 502  *
 503  * @param suggest array of words to iterate through
 504  * @param word Word for spell check
 505  * @param word_size Word size (in bytes)
 506  * @return count of suggests for the word
 507  */
 508 
 509 unsigned int
 510 aspell_suggest (GArray * suggest, const char *word, const int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 511 {
 512     unsigned int size = 0;
 513 
 514     if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
 515     {
 516         const AspellWordList *wordlist;
 517 
 518         wordlist = mc_aspell_speller_suggest (global_speller->speller, word, word_size);
 519         if (wordlist != NULL)
 520         {
 521             AspellStringEnumeration *elements = NULL;
 522             unsigned int i;
 523 
 524             elements = mc_aspell_word_list_elements (wordlist);
 525             size = mc_aspell_word_list_size (wordlist);
 526 
 527             for (i = 0; i < size; i++)
 528             {
 529                 const char *cur_sugg_word;
 530 
 531                 cur_sugg_word = g_strdup (mc_aspell_string_enumeration_next (elements));
 532                 if (cur_sugg_word != NULL)
 533                     g_array_append_val (suggest, cur_sugg_word);
 534             }
 535 
 536             mc_delete_aspell_string_enumeration (elements);
 537         }
 538     }
 539 
 540     return size;
 541 }
 542 
 543 /* --------------------------------------------------------------------------------------------- */
 544 /*
 545  * Add word to personal dictionary.
 546  *
 547  * @param word Word for spell check
 548  * @param word_size  Word size (in bytes)
 549  * @return FALSE or error
 550  */
 551 gboolean
 552 aspell_add_to_dict (const char *word, int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 553 {
 554     mc_aspell_speller_add_to_personal (global_speller->speller, word, word_size);
 555 
 556     if (mc_aspell_speller_error (global_speller->speller) != 0)
 557     {
 558         edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
 559         return FALSE;
 560     }
 561 
 562     mc_aspell_speller_save_all_word_lists (global_speller->speller);
 563 
 564     if (mc_aspell_speller_error (global_speller->speller) != 0)
 565     {
 566         edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
 567         return FALSE;
 568     }
 569 
 570     return TRUE;
 571 }
 572 
 573 /* --------------------------------------------------------------------------------------------- */
 574 
 575 int
 576 edit_suggest_current_word (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 577 {
 578     gsize cut_len = 0;
 579     gsize word_len = 0;
 580     off_t word_start = 0;
 581     int retval = B_SKIP_WORD;
 582     GString *match_word;
 583 
 584     /* search start of word to spell check */
 585     match_word = edit_buffer_get_word_from_pos (&edit->buffer, edit->buffer.curs1, &word_start,
 586                                                 &cut_len);
 587     word_len = match_word->len;
 588 
 589 #ifdef HAVE_CHARSET
 590     if (mc_global.source_codepage >= 0 && mc_global.source_codepage != mc_global.display_codepage)
 591     {
 592         GString *tmp_word;
 593 
 594         tmp_word = str_convert_to_display (match_word->str);
 595         g_string_free (match_word, TRUE);
 596         match_word = tmp_word;
 597     }
 598 #endif
 599     if (!aspell_check (match_word->str, (int) word_len))
 600     {
 601         GArray *suggest;
 602         unsigned int res;
 603         guint i;
 604 
 605         suggest = g_array_new (TRUE, FALSE, sizeof (char *));
 606 
 607         res = aspell_suggest (suggest, match_word->str, (int) word_len);
 608         if (res != 0)
 609         {
 610             char *new_word = NULL;
 611 
 612             edit->found_start = word_start;
 613             edit->found_len = word_len;
 614             edit->force |= REDRAW_PAGE;
 615             edit_scroll_screen_over_cursor (edit);
 616             edit_render_keypress (edit);
 617 
 618             retval = spell_dialog_spell_suggest_show (edit, match_word->str, &new_word, suggest);
 619             edit_cursor_move (edit, word_len - cut_len);
 620 
 621             if (retval == B_ENTER && new_word != NULL)
 622             {
 623                 char *cp_word;
 624 
 625 #ifdef HAVE_CHARSET
 626                 if (mc_global.source_codepage >= 0 &&
 627                     (mc_global.source_codepage != mc_global.display_codepage))
 628                 {
 629                     GString *tmp_word;
 630 
 631                     tmp_word = str_convert_to_input (new_word);
 632                     g_free (new_word);
 633                     new_word = g_string_free (tmp_word, FALSE);
 634                 }
 635 #endif
 636                 cp_word = new_word;
 637                 for (i = 0; i < word_len; i++)
 638                     edit_backspace (edit, TRUE);
 639                 for (; *new_word; new_word++)
 640                     edit_insert (edit, *new_word);
 641                 g_free (cp_word);
 642             }
 643             else if (retval == B_ADD_WORD)
 644                 aspell_add_to_dict (match_word->str, (int) word_len);
 645         }
 646 
 647         for (i = 0; i < suggest->len; i++)
 648         {
 649             char *cur_sugg_word;
 650 
 651             cur_sugg_word = g_array_index (suggest, char *, i);
 652             g_free (cur_sugg_word);
 653         }
 654         g_array_free (suggest, TRUE);
 655         edit->found_start = 0;
 656         edit->found_len = 0;
 657     }
 658 
 659     g_string_free (match_word, TRUE);
 660 
 661     return retval;
 662 }
 663 
 664 /* --------------------------------------------------------------------------------------------- */
 665 
 666 void
 667 edit_spellcheck_file (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
 668 {
 669     if (edit->buffer.curs_line > 0)
 670     {
 671         edit_cursor_move (edit, -edit->buffer.curs1);
 672         edit_move_to_prev_col (edit, 0);
 673         edit_update_curs_row (edit);
 674     }
 675 
 676     do
 677     {
 678         int c1, c2;
 679 
 680         c2 = edit_buffer_get_current_byte (&edit->buffer);
 681 
 682         do
 683         {
 684             if (edit->buffer.curs1 >= edit->buffer.size)
 685                 return;
 686 
 687             c1 = c2;
 688             edit_cursor_move (edit, 1);
 689             c2 = edit_buffer_get_current_byte (&edit->buffer);
 690         }
 691         while (is_break_char (c1) || is_break_char (c2));
 692     }
 693     while (edit_suggest_current_word (edit) != B_CANCEL);
 694 }
 695 
 696 /* --------------------------------------------------------------------------------------------- */
 697 
 698 void
 699 edit_set_spell_lang (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 700 {
 701     GArray *lang_list;
 702 
 703     lang_list = g_array_new (TRUE, FALSE, sizeof (char *));
 704     if (aspell_get_lang_list (lang_list) != 0)
 705     {
 706         char *lang;
 707 
 708         lang = spell_dialog_lang_list_show (lang_list);
 709         if (lang != NULL)
 710         {
 711             (void) aspell_set_lang (lang);
 712             g_free (lang);
 713         }
 714     }
 715     aspell_array_clean (lang_list);
 716 }
 717 
 718 /* --------------------------------------------------------------------------------------------- */
 719 /**
 720  * Show suggests for the current word.
 721  *
 722  * @param edit Editor object
 723  * @param word Word for spell check
 724  * @param new_word Word to replace the incorrect word
 725  * @param suggest Array of suggests for current word
 726  * @return code of pressed button
 727  */
 728 
 729 int
 730 spell_dialog_spell_suggest_show (WEdit * edit, const char *word, char **new_word, GArray * suggest)
     /* [previous][next][first][last][top][bottom][index][help]  */
 731 {
 732 
 733     int sug_dlg_h = 14;         /* dialog height */
 734     int sug_dlg_w = 29;         /* dialog width */
 735     int xpos, ypos;
 736     char *lang_label;
 737     char *word_label;
 738     unsigned int i;
 739     int res;
 740     char *curr = NULL;
 741     WDialog *sug_dlg;
 742     WGroup *g;
 743     WListbox *sug_list;
 744     int max_btn_len = 0;
 745     int replace_len;
 746     int skip_len;
 747     int cancel_len;
 748     WButton *add_btn;
 749     WButton *replace_btn;
 750     WButton *skip_btn;
 751     WButton *cancel_button;
 752     int word_label_len;
 753 
 754     /* calculate the dialog metrics */
 755     xpos = (COLS - sug_dlg_w) / 2;
 756     ypos = (LINES - sug_dlg_h) * 2 / 3;
 757 
 758     /* Sometimes menu can hide replaced text. I don't like it */
 759     if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + sug_dlg_h - 1))
 760         ypos -= sug_dlg_h;
 761 
 762     add_btn = button_new (5, 28, B_ADD_WORD, NORMAL_BUTTON, _("&Add word"), 0);
 763     replace_btn = button_new (7, 28, B_ENTER, NORMAL_BUTTON, _("&Replace"), 0);
 764     replace_len = button_get_len (replace_btn);
 765     skip_btn = button_new (9, 28, B_SKIP_WORD, NORMAL_BUTTON, _("&Skip"), 0);
 766     skip_len = button_get_len (skip_btn);
 767     cancel_button = button_new (11, 28, B_CANCEL, NORMAL_BUTTON, _("&Cancel"), 0);
 768     cancel_len = button_get_len (cancel_button);
 769 
 770     max_btn_len = MAX (replace_len, skip_len);
 771     max_btn_len = MAX (max_btn_len, cancel_len);
 772 
 773     lang_label = g_strdup_printf ("%s: %s", _("Language"), aspell_get_lang ());
 774     word_label = g_strdup_printf ("%s: %s", _("Misspelled"), word);
 775     word_label_len = str_term_width1 (word_label) + 5;
 776 
 777     sug_dlg_w += max_btn_len;
 778     sug_dlg_w = MAX (sug_dlg_w, word_label_len) + 1;
 779 
 780     sug_dlg = dlg_create (TRUE, ypos, xpos, sug_dlg_h, sug_dlg_w, WPOS_KEEP_DEFAULT, TRUE,
 781                           dialog_colors, NULL, NULL, "[ASpell]", _("Check word"));
 782     g = GROUP (sug_dlg);
 783 
 784     group_add_widget (g, label_new (1, 2, lang_label));
 785     group_add_widget (g, label_new (3, 2, word_label));
 786 
 787     group_add_widget (g, groupbox_new (4, 2, sug_dlg_h - 5, 25, _("Suggest")));
 788 
 789     sug_list = listbox_new (5, 2, sug_dlg_h - 7, 24, FALSE, NULL);
 790     for (i = 0; i < suggest->len; i++)
 791         listbox_add_item (sug_list, LISTBOX_APPEND_AT_END, 0, g_array_index (suggest, char *, i),
 792                           NULL, FALSE);
 793     group_add_widget (g, sug_list);
 794 
 795     group_add_widget (g, add_btn);
 796     group_add_widget (g, replace_btn);
 797     group_add_widget (g, skip_btn);
 798     group_add_widget (g, cancel_button);
 799 
 800     res = dlg_run (sug_dlg);
 801     if (res == B_ENTER)
 802     {
 803         char *tmp = NULL;
 804         listbox_get_current (sug_list, &curr, NULL);
 805 
 806         if (curr != NULL)
 807             tmp = g_strdup (curr);
 808         *new_word = tmp;
 809     }
 810 
 811     widget_destroy (WIDGET (sug_dlg));
 812     g_free (lang_label);
 813     g_free (word_label);
 814 
 815     return res;
 816 }
 817 
 818 /* --------------------------------------------------------------------------------------------- */
 819 /**
 820  * Show dialog to select language for spell check.
 821  *
 822  * @param languages Array of available languages
 823  * @return name of choosed language
 824  */
 825 
 826 char *
 827 spell_dialog_lang_list_show (GArray * languages)
     /* [previous][next][first][last][top][bottom][index][help]  */
 828 {
 829 
 830     int lang_dlg_h = 12;        /* dialog height */
 831     int lang_dlg_w = 30;        /* dialog width */
 832     char *selected_lang = NULL;
 833     unsigned int i;
 834     int res;
 835     Listbox *lang_list;
 836 
 837     /* Create listbox */
 838     lang_list = create_listbox_window_centered (-1, -1, lang_dlg_h, lang_dlg_w,
 839                                                 _("Select language"), "[ASpell]");
 840 
 841     for (i = 0; i < languages->len; i++)
 842         LISTBOX_APPEND_TEXT (lang_list, 0, g_array_index (languages, char *, i), NULL, FALSE);
 843 
 844     res = run_listbox (lang_list);
 845     if (res >= 0)
 846         selected_lang = g_strdup (g_array_index (languages, char *, (unsigned int) res));
 847 
 848     return selected_lang;
 849 
 850 }
 851 
 852 /* --------------------------------------------------------------------------------------------- */

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