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

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