summaryrefslogtreecommitdiffstats
path: root/src/editor/spell.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/editor/spell.c')
-rw-r--r--src/editor/spell.c834
1 files changed, 834 insertions, 0 deletions
diff --git a/src/editor/spell.c b/src/editor/spell.c
new file mode 100644
index 0000000..aeb0884
--- /dev/null
+++ b/src/editor/spell.c
@@ -0,0 +1,834 @@
+/*
+ Editor spell checker
+
+ Copyright (C) 2012-2023
+ Free Software Foundation, Inc.
+
+ Written by:
+ Ilia Maslakov <il.smind@gmail.com>, 2012
+ Andrew Borodin <aborodin@vmail.ru>, 2013, 2021
+
+
+ This file is part of the Midnight Commander.
+
+ The Midnight Commander is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+ The Midnight Commander is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <gmodule.h>
+#include <aspell.h>
+
+#ifdef HAVE_CHARSET
+#include "lib/charsets.h"
+#endif
+#include "lib/strutil.h"
+#include "lib/util.h" /* MC_PTR_FREE() */
+#include "lib/tty/tty.h" /* COLS, LINES */
+
+#include "src/setup.h"
+
+#include "editwidget.h"
+
+#include "spell.h"
+
+/*** global variables ****************************************************************************/
+
+/*** file scope macro definitions ****************************************************************/
+
+#define B_SKIP_WORD (B_USER+3)
+#define B_ADD_WORD (B_USER+4)
+
+/*** file scope type declarations ****************************************************************/
+
+typedef struct aspell_struct
+{
+ AspellConfig *config;
+ AspellSpeller *speller;
+} spell_t;
+
+/*** forward declarations (file scope functions) *************************************************/
+
+/*** file scope variables ************************************************************************/
+
+static GModule *spell_module = NULL;
+static spell_t *global_speller = NULL;
+
+static AspellConfig *(*mc_new_aspell_config) (void);
+static int (*mc_aspell_config_replace) (AspellConfig * ths, const char *key, const char *value);
+static AspellCanHaveError *(*mc_new_aspell_speller) (AspellConfig * config);
+static unsigned int (*mc_aspell_error_number) (const AspellCanHaveError * ths);
+static const char *(*mc_aspell_speller_error_message) (const AspellSpeller * ths);
+static const AspellError *(*mc_aspell_speller_error) (const AspellSpeller * ths);
+
+static AspellSpeller *(*mc_to_aspell_speller) (AspellCanHaveError * obj);
+static int (*mc_aspell_speller_check) (AspellSpeller * ths, const char *word, int word_size);
+static const AspellWordList *(*mc_aspell_speller_suggest) (AspellSpeller * ths,
+ const char *word, int word_size);
+static AspellStringEnumeration *(*mc_aspell_word_list_elements) (const struct AspellWordList * ths);
+static const char *(*mc_aspell_config_retrieve) (AspellConfig * ths, const char *key);
+static void (*mc_delete_aspell_speller) (AspellSpeller * ths);
+static void (*mc_delete_aspell_config) (AspellConfig * ths);
+static void (*mc_delete_aspell_can_have_error) (AspellCanHaveError * ths);
+static const char *(*mc_aspell_error_message) (const AspellCanHaveError * ths);
+static void (*mc_delete_aspell_string_enumeration) (AspellStringEnumeration * ths);
+static AspellDictInfoEnumeration *(*mc_aspell_dict_info_list_elements)
+ (const AspellDictInfoList * ths);
+static AspellDictInfoList *(*mc_get_aspell_dict_info_list) (AspellConfig * config);
+static const AspellDictInfo *(*mc_aspell_dict_info_enumeration_next)
+ (AspellDictInfoEnumeration * ths);
+static const char *(*mc_aspell_string_enumeration_next) (AspellStringEnumeration * ths);
+static void (*mc_delete_aspell_dict_info_enumeration) (AspellDictInfoEnumeration * ths);
+static unsigned int (*mc_aspell_word_list_size) (const AspellWordList * ths);
+static const AspellError *(*mc_aspell_error) (const AspellCanHaveError * ths);
+static int (*mc_aspell_speller_add_to_personal) (AspellSpeller * ths, const char *word,
+ int word_size);
+static int (*mc_aspell_speller_save_all_word_lists) (AspellSpeller * ths);
+
+static struct
+{
+ const char *code;
+ const char *name;
+} spell_codes_map[] =
+{
+ /* *INDENT-OFF* */
+ {"br", N_("Breton")},
+ {"cs", N_("Czech")},
+ {"cy", N_("Welsh")},
+ {"da", N_("Danish")},
+ {"de", N_("German")},
+ {"el", N_("Greek")},
+ {"en", N_("English")},
+ {"en_GB", N_("British English")},
+ {"en_CA", N_("Canadian English")},
+ {"en_US", N_("American English")},
+ {"eo", N_("Esperanto")},
+ {"es", N_("Spanish")},
+ {"fo", N_("Faroese")},
+ {"fr", N_("French")},
+ {"it", N_("Italian")},
+ {"nl", N_("Dutch")},
+ {"no", N_("Norwegian")},
+ {"pl", N_("Polish")},
+ {"pt", N_("Portuguese")},
+ {"ro", N_("Romanian")},
+ {"ru", N_("Russian")},
+ {"sk", N_("Slovak")},
+ {"sv", N_("Swedish")},
+ {"uk", N_("Ukrainian")},
+ {NULL, NULL}
+ /* *INDENT-ON* */
+};
+
+/*** file scope functions ************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Found the language name by language code. For example: en_US -> American English.
+ *
+ * @param code Short name of the language (ru, en, pl, uk, etc...)
+ * @return language name
+ */
+
+static const char *
+spell_decode_lang (const char *code)
+{
+ size_t i;
+
+ for (i = 0; spell_codes_map[i].code != NULL; i++)
+ {
+ if (strcmp (spell_codes_map[i].code, code) == 0)
+ return _(spell_codes_map[i].name);
+ }
+
+ return code;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Checks if aspell library and symbols are available.
+ *
+ * @return FALSE or error
+ */
+
+static gboolean
+spell_available (void)
+{
+ gchar *spell_module_fname;
+ gboolean ret = FALSE;
+
+ if (spell_module != NULL)
+ return TRUE;
+
+ spell_module_fname = g_module_build_path (NULL, "libaspell");
+ spell_module = g_module_open (spell_module_fname, G_MODULE_BIND_LAZY);
+
+ g_free (spell_module_fname);
+
+ if (spell_module == NULL)
+ return FALSE;
+
+ if (!g_module_symbol (spell_module, "new_aspell_config", (void *) &mc_new_aspell_config))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_dict_info_list_elements",
+ (void *) &mc_aspell_dict_info_list_elements))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_dict_info_enumeration_next",
+ (void *) &mc_aspell_dict_info_enumeration_next))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "new_aspell_speller", (void *) &mc_new_aspell_speller))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_error_number", (void *) &mc_aspell_error_number))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_speller_error_message",
+ (void *) &mc_aspell_speller_error_message))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_speller_error", (void *) &mc_aspell_speller_error))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_error", (void *) &mc_aspell_error))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "to_aspell_speller", (void *) &mc_to_aspell_speller))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_speller_check", (void *) &mc_aspell_speller_check))
+ goto error_ret;
+
+ if (!g_module_symbol
+ (spell_module, "aspell_speller_suggest", (void *) &mc_aspell_speller_suggest))
+ goto error_ret;
+
+ if (!g_module_symbol
+ (spell_module, "aspell_word_list_elements", (void *) &mc_aspell_word_list_elements))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_string_enumeration_next",
+ (void *) &mc_aspell_string_enumeration_next))
+ goto error_ret;
+
+ if (!g_module_symbol
+ (spell_module, "aspell_config_replace", (void *) &mc_aspell_config_replace))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_error_message", (void *) &mc_aspell_error_message))
+ goto error_ret;
+
+ if (!g_module_symbol
+ (spell_module, "delete_aspell_speller", (void *) &mc_delete_aspell_speller))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "delete_aspell_config", (void *) &mc_delete_aspell_config))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "delete_aspell_string_enumeration",
+ (void *) &mc_delete_aspell_string_enumeration))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "get_aspell_dict_info_list",
+ (void *) &mc_get_aspell_dict_info_list))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "delete_aspell_can_have_error",
+ (void *) &mc_delete_aspell_can_have_error))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "delete_aspell_dict_info_enumeration",
+ (void *) &mc_delete_aspell_dict_info_enumeration))
+ goto error_ret;
+
+ if (!g_module_symbol
+ (spell_module, "aspell_config_retrieve", (void *) &mc_aspell_config_retrieve))
+ goto error_ret;
+
+ if (!g_module_symbol
+ (spell_module, "aspell_word_list_size", (void *) &mc_aspell_word_list_size))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_speller_add_to_personal",
+ (void *) &mc_aspell_speller_add_to_personal))
+ goto error_ret;
+
+ if (!g_module_symbol (spell_module, "aspell_speller_save_all_word_lists",
+ (void *) &mc_aspell_speller_save_all_word_lists))
+ goto error_ret;
+
+ ret = TRUE;
+
+ error_ret:
+ if (!ret)
+ {
+ g_module_close (spell_module);
+ spell_module = NULL;
+ }
+ return ret;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/*** public functions ****************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Initialization of Aspell support.
+ */
+
+void
+aspell_init (void)
+{
+ AspellCanHaveError *error = NULL;
+
+ if (strcmp (spell_language, "NONE") == 0)
+ return;
+
+ if (global_speller != NULL)
+ return;
+
+ global_speller = g_try_malloc (sizeof (spell_t));
+ if (global_speller == NULL)
+ return;
+
+ if (!spell_available ())
+ {
+ MC_PTR_FREE (global_speller);
+ return;
+ }
+
+ global_speller->config = mc_new_aspell_config ();
+ global_speller->speller = NULL;
+
+ if (spell_language != NULL)
+ mc_aspell_config_replace (global_speller->config, "lang", spell_language);
+
+ error = mc_new_aspell_speller (global_speller->config);
+
+ if (mc_aspell_error_number (error) == 0)
+ global_speller->speller = mc_to_aspell_speller (error);
+ else
+ {
+ edit_error_dialog (_("Error"), mc_aspell_error_message (error));
+ mc_delete_aspell_can_have_error (error);
+ aspell_clean ();
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Deinitialization of Aspell support.
+ */
+
+void
+aspell_clean (void)
+{
+ if (global_speller == NULL)
+ return;
+
+ if (global_speller->speller != NULL)
+ mc_delete_aspell_speller (global_speller->speller);
+
+ if (global_speller->config != NULL)
+ mc_delete_aspell_config (global_speller->config);
+
+ MC_PTR_FREE (global_speller);
+
+ g_module_close (spell_module);
+ spell_module = NULL;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Get array of available languages.
+ *
+ * @param lang_list Array of languages. Must be cleared before use
+ * @return language list length
+ */
+
+unsigned int
+aspell_get_lang_list (GPtrArray * lang_list)
+{
+ AspellDictInfoList *dlist;
+ AspellDictInfoEnumeration *elem;
+ const AspellDictInfo *entry;
+ unsigned int i = 0;
+
+ if (spell_module == NULL)
+ return 0;
+
+ /* the returned pointer should _not_ need to be deleted */
+ dlist = mc_get_aspell_dict_info_list (global_speller->config);
+ elem = mc_aspell_dict_info_list_elements (dlist);
+
+ while ((entry = mc_aspell_dict_info_enumeration_next (elem)) != NULL)
+ if (entry->name != NULL)
+ {
+ g_ptr_array_add (lang_list, g_strdup (entry->name));
+ i++;
+ }
+
+ mc_delete_aspell_dict_info_enumeration (elem);
+
+ return i;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Clear the array of languages.
+ *
+ * @param array Array of languages
+ */
+
+void
+aspell_array_clean (GPtrArray * array)
+{
+ if (array != NULL)
+ g_ptr_array_free (array, TRUE);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Get the current language name.
+ *
+ * @return language name
+ */
+
+const char *
+aspell_get_lang (void)
+{
+ const char *code;
+
+ code = mc_aspell_config_retrieve (global_speller->config, "lang");
+ return spell_decode_lang (code);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Set the language.
+ *
+ * @param lang Language name
+ * @return FALSE or error
+ */
+
+gboolean
+aspell_set_lang (const char *lang)
+{
+ if (lang != NULL)
+ {
+ AspellCanHaveError *error;
+ const char *spell_codeset;
+
+ g_free (spell_language);
+ spell_language = g_strdup (lang);
+
+#ifdef HAVE_CHARSET
+ if (mc_global.source_codepage > 0)
+ spell_codeset = get_codepage_id (mc_global.source_codepage);
+ else
+#endif
+ spell_codeset = str_detect_termencoding ();
+
+ mc_aspell_config_replace (global_speller->config, "lang", lang);
+ mc_aspell_config_replace (global_speller->config, "encoding", spell_codeset);
+
+ /* the returned pointer should _not_ need to be deleted */
+ if (global_speller->speller != NULL)
+ mc_delete_aspell_speller (global_speller->speller);
+
+ global_speller->speller = NULL;
+
+ error = mc_new_aspell_speller (global_speller->config);
+ if (mc_aspell_error (error) != 0)
+ {
+ mc_delete_aspell_can_have_error (error);
+ return FALSE;
+ }
+
+ global_speller->speller = mc_to_aspell_speller (error);
+ }
+ return TRUE;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Check word.
+ *
+ * @param word Word for spell check
+ * @param word_size Word size (in bytes)
+ * @return FALSE if word is not in the dictionary
+ */
+
+gboolean
+aspell_check (const char *word, const int word_size)
+{
+ int res = 0;
+
+ if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
+ res = mc_aspell_speller_check (global_speller->speller, word, word_size);
+
+ return (res == 1);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Examine dictionaries and suggest possible words that may repalce the incorrect word.
+ *
+ * @param suggest array of words to iterate through
+ * @param word Word for spell check
+ * @param word_size Word size (in bytes)
+ * @return count of suggests for the word
+ */
+
+unsigned int
+aspell_suggest (GPtrArray * suggest, const char *word, const int word_size)
+{
+ unsigned int size = 0;
+
+ if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
+ {
+ const AspellWordList *wordlist;
+
+ wordlist = mc_aspell_speller_suggest (global_speller->speller, word, word_size);
+ if (wordlist != NULL)
+ {
+ AspellStringEnumeration *elements = NULL;
+ unsigned int i;
+
+ elements = mc_aspell_word_list_elements (wordlist);
+ size = mc_aspell_word_list_size (wordlist);
+
+ for (i = 0; i < size; i++)
+ {
+ const char *cur_sugg_word;
+
+ cur_sugg_word = mc_aspell_string_enumeration_next (elements);
+ if (cur_sugg_word != NULL)
+ g_ptr_array_add (suggest, g_strdup (cur_sugg_word));
+ }
+
+ mc_delete_aspell_string_enumeration (elements);
+ }
+ }
+
+ return size;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/*
+ * Add word to personal dictionary.
+ *
+ * @param word Word for spell check
+ * @param word_size Word size (in bytes)
+ * @return FALSE or error
+ */
+gboolean
+aspell_add_to_dict (const char *word, int word_size)
+{
+ mc_aspell_speller_add_to_personal (global_speller->speller, word, word_size);
+
+ if (mc_aspell_speller_error (global_speller->speller) != 0)
+ {
+ edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
+ return FALSE;
+ }
+
+ mc_aspell_speller_save_all_word_lists (global_speller->speller);
+
+ if (mc_aspell_speller_error (global_speller->speller) != 0)
+ {
+ edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+int
+edit_suggest_current_word (WEdit * edit)
+{
+ gsize cut_len = 0;
+ gsize word_len = 0;
+ off_t word_start = 0;
+ int retval = B_SKIP_WORD;
+ GString *match_word;
+
+ /* search start of word to spell check */
+ match_word = edit_buffer_get_word_from_pos (&edit->buffer, edit->buffer.curs1, &word_start,
+ &cut_len);
+ word_len = match_word->len;
+
+#ifdef HAVE_CHARSET
+ if (mc_global.source_codepage >= 0 && mc_global.source_codepage != mc_global.display_codepage)
+ {
+ GString *tmp_word;
+
+ tmp_word = str_convert_to_display (match_word->str);
+ g_string_free (match_word, TRUE);
+ match_word = tmp_word;
+ }
+#endif
+ if (match_word != NULL)
+ {
+ if (!aspell_check (match_word->str, (int) word_len))
+ {
+ GPtrArray *suggest;
+ unsigned int res;
+ guint i;
+
+ suggest = g_ptr_array_new_with_free_func (g_free);
+
+ res = aspell_suggest (suggest, match_word->str, (int) word_len);
+ if (res != 0)
+ {
+ char *new_word = NULL;
+
+ edit->found_start = word_start;
+ edit->found_len = word_len;
+ edit->force |= REDRAW_PAGE;
+ edit_scroll_screen_over_cursor (edit);
+ edit_render_keypress (edit);
+
+ retval =
+ spell_dialog_spell_suggest_show (edit, match_word->str, &new_word, suggest);
+ edit_cursor_move (edit, word_len - cut_len);
+
+ if (retval == B_ENTER && new_word != NULL)
+ {
+#ifdef HAVE_CHARSET
+ if (mc_global.source_codepage >= 0 &&
+ (mc_global.source_codepage != mc_global.display_codepage))
+ {
+ GString *tmp_word;
+
+ tmp_word = str_convert_to_input (new_word);
+ MC_PTR_FREE (new_word);
+ if (tmp_word != NULL)
+ new_word = g_string_free (tmp_word, FALSE);
+ }
+#endif
+ for (i = 0; i < word_len; i++)
+ edit_backspace (edit, TRUE);
+ if (new_word != NULL)
+ {
+ for (i = 0; new_word[i] != '\0'; i++)
+ edit_insert (edit, new_word[i]);
+ g_free (new_word);
+ }
+ }
+ else if (retval == B_ADD_WORD)
+ aspell_add_to_dict (match_word->str, (int) word_len);
+ }
+
+ g_ptr_array_free (suggest, TRUE);
+ edit->found_start = 0;
+ edit->found_len = 0;
+ }
+
+ g_string_free (match_word, TRUE);
+ }
+
+ return retval;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+edit_spellcheck_file (WEdit * edit)
+{
+ if (edit->buffer.curs_line > 0)
+ {
+ edit_cursor_move (edit, -edit->buffer.curs1);
+ edit_move_to_prev_col (edit, 0);
+ edit_update_curs_row (edit);
+ }
+
+ do
+ {
+ int c1, c2;
+
+ c2 = edit_buffer_get_current_byte (&edit->buffer);
+
+ do
+ {
+ if (edit->buffer.curs1 >= edit->buffer.size)
+ return;
+
+ c1 = c2;
+ edit_cursor_move (edit, 1);
+ c2 = edit_buffer_get_current_byte (&edit->buffer);
+ }
+ while (is_break_char (c1) || is_break_char (c2));
+ }
+ while (edit_suggest_current_word (edit) != B_CANCEL);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+edit_set_spell_lang (void)
+{
+ GPtrArray *lang_list;
+
+ lang_list = g_ptr_array_new_with_free_func (g_free);
+ if (aspell_get_lang_list (lang_list) != 0)
+ {
+ const char *lang;
+
+ lang = spell_dialog_lang_list_show (lang_list);
+ if (lang != NULL)
+ (void) aspell_set_lang (lang);
+ }
+ aspell_array_clean (lang_list);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Show suggests for the current word.
+ *
+ * @param edit Editor object
+ * @param word Word for spell check
+ * @param new_word Word to replace the incorrect word
+ * @param suggest Array of suggests for current word
+ * @return code of pressed button
+ */
+
+int
+spell_dialog_spell_suggest_show (WEdit * edit, const char *word, char **new_word,
+ const GPtrArray * suggest)
+{
+
+ int sug_dlg_h = 14; /* dialog height */
+ int sug_dlg_w = 29; /* dialog width */
+ int xpos, ypos;
+ char *lang_label;
+ char *word_label;
+ unsigned int i;
+ int res;
+ char *curr = NULL;
+ WDialog *sug_dlg;
+ WGroup *g;
+ WListbox *sug_list;
+ int max_btn_len = 0;
+ int replace_len;
+ int skip_len;
+ int cancel_len;
+ WButton *add_btn;
+ WButton *replace_btn;
+ WButton *skip_btn;
+ WButton *cancel_button;
+ int word_label_len;
+
+ /* calculate the dialog metrics */
+ xpos = (COLS - sug_dlg_w) / 2;
+ ypos = (LINES - sug_dlg_h) * 2 / 3;
+
+ /* Sometimes menu can hide replaced text. I don't like it */
+ if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + sug_dlg_h - 1))
+ ypos -= sug_dlg_h;
+
+ add_btn = button_new (5, 28, B_ADD_WORD, NORMAL_BUTTON, _("&Add word"), 0);
+ replace_btn = button_new (7, 28, B_ENTER, NORMAL_BUTTON, _("&Replace"), 0);
+ replace_len = button_get_len (replace_btn);
+ skip_btn = button_new (9, 28, B_SKIP_WORD, NORMAL_BUTTON, _("&Skip"), 0);
+ skip_len = button_get_len (skip_btn);
+ cancel_button = button_new (11, 28, B_CANCEL, NORMAL_BUTTON, _("&Cancel"), 0);
+ cancel_len = button_get_len (cancel_button);
+
+ max_btn_len = MAX (replace_len, skip_len);
+ max_btn_len = MAX (max_btn_len, cancel_len);
+
+ lang_label = g_strdup_printf ("%s: %s", _("Language"), aspell_get_lang ());
+ word_label = g_strdup_printf ("%s: %s", _("Misspelled"), word);
+ word_label_len = str_term_width1 (word_label) + 5;
+
+ sug_dlg_w += max_btn_len;
+ sug_dlg_w = MAX (sug_dlg_w, word_label_len) + 1;
+
+ sug_dlg = dlg_create (TRUE, ypos, xpos, sug_dlg_h, sug_dlg_w, WPOS_KEEP_DEFAULT, TRUE,
+ dialog_colors, NULL, NULL, "[ASpell]", _("Check word"));
+ g = GROUP (sug_dlg);
+
+ group_add_widget (g, label_new (1, 2, lang_label));
+ group_add_widget (g, label_new (3, 2, word_label));
+
+ group_add_widget (g, groupbox_new (4, 2, sug_dlg_h - 5, 25, _("Suggest")));
+
+ sug_list = listbox_new (5, 2, sug_dlg_h - 7, 24, FALSE, NULL);
+ for (i = 0; i < suggest->len; i++)
+ listbox_add_item (sug_list, LISTBOX_APPEND_AT_END, 0, g_ptr_array_index (suggest, i), NULL,
+ FALSE);
+ group_add_widget (g, sug_list);
+
+ group_add_widget (g, add_btn);
+ group_add_widget (g, replace_btn);
+ group_add_widget (g, skip_btn);
+ group_add_widget (g, cancel_button);
+
+ res = dlg_run (sug_dlg);
+ if (res == B_ENTER)
+ {
+ char *tmp = NULL;
+ listbox_get_current (sug_list, &curr, NULL);
+
+ if (curr != NULL)
+ tmp = g_strdup (curr);
+ *new_word = tmp;
+ }
+
+ widget_destroy (WIDGET (sug_dlg));
+ g_free (lang_label);
+ g_free (word_label);
+
+ return res;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Show dialog to select language for spell check.
+ *
+ * @param languages Array of available languages
+ * @return name of chosen language
+ */
+
+const char *
+spell_dialog_lang_list_show (const GPtrArray * languages)
+{
+
+ int lang_dlg_h = 12; /* dialog height */
+ int lang_dlg_w = 30; /* dialog width */
+ const char *selected_lang = NULL;
+ unsigned int i;
+ int res;
+ Listbox *lang_list;
+
+ /* Create listbox */
+ lang_list = listbox_window_centered_new (-1, -1, lang_dlg_h, lang_dlg_w,
+ _("Select language"), "[ASpell]");
+
+ for (i = 0; i < languages->len; i++)
+ LISTBOX_APPEND_TEXT (lang_list, 0, g_ptr_array_index (languages, i), NULL, FALSE);
+
+ res = listbox_run (lang_list);
+ if (res >= 0)
+ selected_lang = g_ptr_array_index (languages, (unsigned int) res);
+
+ return selected_lang;
+}
+
+/* --------------------------------------------------------------------------------------------- */