~aleteoryx/sexchat

b4678b289948de74d8b7473c15354b85ed88e752 — Aleteoryx 4 months ago e7d0d3f
added spellcheck lang multiselect

this took so long, and was a good way to learn gtk. jesus though lmao

Implements: https://todo.amehut.dev/~aleteoryx/sexchat/11
A src/fe-gtk/enchant.c => src/fe-gtk/enchant.c +134 -0
@@ 0,0 1,134 @@
/*
 * common/enchant.c - Enchant Library Runtime Loading
 *
 * Copyright (C) 2004-2006 Christian Hammond.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <gtk/gtk.h>
#include "enchant.h"

struct EnchantBroker * (*enchant_broker_init) (void);
void (*enchant_broker_free) (struct EnchantBroker * broker);
void (*enchant_broker_free_dict) (struct EnchantBroker * broker, struct EnchantDict * dict);
void (*enchant_broker_list_dicts) (struct EnchantBroker * broker, EnchantDictDescribeFn fn, void * user_data);
struct EnchantDict * (*enchant_broker_request_dict) (struct EnchantBroker * broker, const char *const tag);

void (*enchant_dict_add_to_personal) (struct EnchantDict * dict, const char *const word, ssize_t len);
void (*enchant_dict_add_to_session) (struct EnchantDict * dict, const char *const word, ssize_t len);
int (*enchant_dict_check) (struct EnchantDict * dict, const char *const word, ssize_t len);
void (*enchant_dict_describe) (struct EnchantDict * dict, EnchantDictDescribeFn fn, void * user_data);
void (*enchant_dict_free_suggestions) (struct EnchantDict * dict, char **suggestions);
void (*enchant_dict_store_replacement) (struct EnchantDict * dict, const char *const mis, ssize_t mis_len, const char *const cor, ssize_t cor_len);
char ** (*enchant_dict_suggest) (struct EnchantDict * dict, const char *const word, ssize_t len, size_t * out_n_suggs);

static gboolean _have_enchant = FALSE;

void
initialize_enchant (void)
{
	GModule *enchant;
	gpointer funcptr;
    gsize i;
    const char * const libnames[] = {
#ifdef G_OS_WIN32
        "libenchant.dll",
#endif
#ifdef G_OS_UNIX
        "libenchant.so.1",
        "libenchant.so.2",
        "libenchant-2.so.2",
#endif
#ifdef __APPLE__
        "libenchant.dylib",
#endif
    };
    
    if (_have_enchant)
    	return;

    for (i = 0; i < G_N_ELEMENTS(libnames); ++i)
    {
        enchant = g_module_open(libnames[i], 0);
        if (enchant)
        {
            g_info ("Loaded %s", libnames[i]);
            _have_enchant = TRUE;
            break;
        }
    }

  if (!_have_enchant)
    return;

#define MODULE_SYMBOL(name, func, alt_name) G_STMT_START { \
    const char *funcname = name; \
    gboolean ret = g_module_symbol(enchant, funcname, &funcptr); \
    if (alt_name) { \
        funcname = alt_name; \
        ret = g_module_symbol(enchant, funcname, &funcptr); \
    } \
    if (ret == FALSE) { \
        g_warning ("Failed to find enchant symbol %s", funcname); \
        _have_enchant = FALSE; \
        return; \
    } \
    (func) = funcptr; \
} G_STMT_END;

	MODULE_SYMBOL("enchant_broker_init", enchant_broker_init, NULL)
	MODULE_SYMBOL("enchant_broker_free", enchant_broker_free, NULL)
	MODULE_SYMBOL("enchant_broker_free_dict", enchant_broker_free_dict, NULL)
	MODULE_SYMBOL("enchant_broker_list_dicts", enchant_broker_list_dicts, NULL)
	MODULE_SYMBOL("enchant_broker_request_dict", enchant_broker_request_dict, NULL)

	MODULE_SYMBOL("enchant_dict_add_to_personal", enchant_dict_add_to_personal, "enchant_dict_add")
	MODULE_SYMBOL("enchant_dict_add_to_session", enchant_dict_add_to_session, NULL)
	MODULE_SYMBOL("enchant_dict_check", enchant_dict_check, NULL)
	MODULE_SYMBOL("enchant_dict_describe", enchant_dict_describe, NULL)
	MODULE_SYMBOL("enchant_dict_free_suggestions", enchant_dict_free_suggestions, "enchant_dict_free_string_list")
	MODULE_SYMBOL("enchant_dict_store_replacement", enchant_dict_store_replacement, NULL)
	MODULE_SYMBOL("enchant_dict_suggest", enchant_dict_suggest, NULL)
}

gboolean
have_enchant (void)
{
	return _have_enchant;
}


static void
get_lang_from_dict_cb (const char * const lang_tag,
		      const char * const provider_name,
		      const char * const provider_desc,
		      const char * const provider_file,
		      void * user_data) {
	gchar **lang = (gchar **)user_data;
	*lang = g_strdup(lang_tag);
}

gchar *
enchant_get_lang_from_dict (struct EnchantDict *dict)
{
	gchar *lang;

	if (!_have_enchant)
		return NULL;

	enchant_dict_describe(dict, get_lang_from_dict_cb, &lang);
	return lang;
}

A src/fe-gtk/enchant.h => src/fe-gtk/enchant.h +47 -0
@@ 0,0 1,47 @@
/*
 * common/enchant.h - Enchant Library Runtime Loading
 *
 * Copyright (C) 2004-2006 Christian Hammond.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

struct EnchantDict;
struct EnchantBroker;

typedef void (*EnchantDictDescribeFn) (const char * const lang_tag,
                                       const char * const provider_name,
                                       const char * const provider_desc,
                                       const char * const provider_file,
                                       void * user_data);
                                       

extern struct EnchantBroker * (*enchant_broker_init) (void);
extern void (*enchant_broker_free) (struct EnchantBroker * broker);
extern void (*enchant_broker_free_dict) (struct EnchantBroker * broker, struct EnchantDict * dict);
extern void (*enchant_broker_list_dicts) (struct EnchantBroker * broker, EnchantDictDescribeFn fn, void * user_data);
extern struct EnchantDict * (*enchant_broker_request_dict) (struct EnchantBroker * broker, const char *const tag);

extern void (*enchant_dict_add_to_personal) (struct EnchantDict * dict, const char *const word, ssize_t len);
extern void (*enchant_dict_add_to_session) (struct EnchantDict * dict, const char *const word, ssize_t len);
extern int (*enchant_dict_check) (struct EnchantDict * dict, const char *const word, ssize_t len);
extern void (*enchant_dict_describe) (struct EnchantDict * dict, EnchantDictDescribeFn fn, void * user_data);
extern void (*enchant_dict_free_suggestions) (struct EnchantDict * dict, char **suggestions);
extern void (*enchant_dict_store_replacement) (struct EnchantDict * dict, const char *const mis, ssize_t mis_len, const char *const cor, ssize_t cor_len);
extern char ** (*enchant_dict_suggest) (struct EnchantDict * dict, const char *const word, ssize_t len, size_t * out_n_suggs);

gboolean have_enchant (void);
void initialize_enchant (void);
gchar *enchant_get_lang_from_dict (struct EnchantDict *dict);

M src/fe-gtk/meson.build => src/fe-gtk/meson.build +1 -0
@@ 6,6 6,7 @@ hexchat_gtk_sources = [
  'custom-list.c',
  'dccgui.c',
  'editlist.c',
  'enchant.c',
  'fe-gtk.c',
  'fkeys.c',
  'gtkutil.c',

M src/fe-gtk/setup.c => src/fe-gtk/setup.c +211 -12
@@ 38,11 38,13 @@
#include "menu.h"
#include "plugin-tray.h"
#include "notifications/notification-backend.h"
#include "enchant.h"

#ifdef WIN32
#include "../common/fe.h"
#endif
#include "sexy-spell-entry.h"
#include "sexy-iso-codes.h"

GtkStyle *create_input_style (GtkStyle *);



@@ 56,6 58,7 @@ static struct hexchatprefs setup_prefs;
static GtkWidget *cancel_button;
static GtkWidget *font_dialog = NULL;


enum
{
	ST_END,


@@ 72,7 75,8 @@ enum
	ST_HSCALE,
	ST_HEADER,
	ST_LABEL,
	ST_ALERTHEAD
	ST_ALERTHEAD,
	ST_MULTISEL
};

typedef struct


@@ 181,6 185,8 @@ static const setting appearance_settings[] =
	{ST_END, 0, 0, 0, 0, 0}
};

static const char **spelllangs = NULL;

static const char *const tabcompmenu[] = 
{
	N_("A-Z"),


@@ 196,12 202,8 @@ static const setting inputbox_settings[] =
	{ST_TOGGLE, N_("Show nick box"), P_OFFINTNL(hex_gui_input_nick),0,0,1},
	{ST_TOGGLE, N_("Show user mode icon in nick box"), P_OFFINTNL(hex_gui_input_icon),0,0,0},
	{ST_TOGGLE, N_("Spell checking"), P_OFFINTNL(hex_gui_input_spell),0,0,1},
	{ST_ENTRY,	N_("Dictionaries to use:"), P_OFFSETNL(hex_text_spell_langs),0,0,sizeof prefs.hex_text_spell_langs},
#ifdef WIN32
	{ST_LABEL,	N_("Use language codes (as in \"%LOCALAPPDATA%\\enchant\\myspell\\dicts\").\nSeparate multiple entries with commas.")},
#else
	{ST_LABEL,	N_("Use language codes. Separate multiple entries with commas.")},
#endif
	{ST_MULTISEL,	N_("Dictionaries to use:"), P_OFFSETNL(hex_text_spell_langs),0,(void *)&spelllangs,sizeof prefs.hex_text_spell_langs},
	{ST_LABEL,	N_("If no dictionary is selected, English will be used."), 0, 0, 0, 1},

	{ST_HEADER, N_("Nick Completion"),0,0,0},
	{ST_ENTRY,	N_("Nick completion suffix:"), P_OFFSETNL(hex_completion_suffix),0,0,sizeof prefs.hex_completion_suffix},


@@ 1287,8 1289,144 @@ setup_create_header (GtkWidget *table, int row, char *labeltext)
	label = gtk_label_new (NULL);
	gtk_label_set_markup (GTK_LABEL (label), buf);
	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
	gtk_table_attach (GTK_TABLE (table), label, 0, 4, row, row + 1,
							GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 5);
	gtk_table_attach (GTK_TABLE (table), label, 1, 4, row, row + 1,
			  GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 5);
}

struct multi_select_info {
	GtkListStore *store;
	const setting *set;
};

static void
setup_multi_select_info_free_cb (gpointer data,
				 GClosure * closure)
{
	g_free (data);
}

static void
setup_multi_select_cb (GtkCellRendererToggle *cell_renderer,
		       char *path,
		       gpointer user_data)
{
	struct multi_select_info *info = (struct multi_select_info *) user_data;
	GtkTreeIter row;
	gboolean cellstate, hasnext;
	char *cellslug, *dest = setup_get_str(&setup_prefs, info->set);
	int val_len = 0;
	
	gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (info->store), &row, path);
	gtk_tree_model_get (GTK_TREE_MODEL (info->store), &row, 2, &cellstate, -1);
	gtk_list_store_set (info->store, &row, 2, !cellstate, -1);

	setup_prefs.hex_text_spell_langs[0] = '\0';
	hasnext = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (info->store), &row);
	while (hasnext)
	{
		gtk_tree_model_get (GTK_TREE_MODEL (info->store), &row,
				     1, &cellslug,
				     2, &cellstate,
				    -1);
		
		if (cellstate)
		{
			if (!val_len)
			{
				val_len = strlen (cellslug);
				strcpy (dest, cellslug);
			} else if (strlen (cellslug) + 2 < (info->set->extra - val_len))
			{
				dest[val_len++] = ',';
				dest[val_len] = '\0';
				strcat (dest, cellslug);
				val_len += strlen (cellslug);
			} else
			{
				g_free (cellslug);
				break;
			}
		}

		g_free (cellslug);
		hasnext = gtk_tree_model_iter_next (GTK_TREE_MODEL (info->store), &row);		
	}
}

static GtkWidget *
setup_create_multi_select (GtkWidget *table, int row, const setting *set)
{
	GtkWidget *select, *scrollbox, *label;
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;
	GtkListStore *store;
	GtkTreeIter iter;
	struct multi_select_info *user_data;
	char **entries = *((char ***)set->list), **entry, **enabled, **i;
	gboolean entry_enabled;

	label = gtk_label_new (set->label);
	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
	gtk_table_attach (GTK_TABLE (table), label, 2, 3, row, row + 1,
			  GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, LABEL_INDENT, 5);

	enabled = g_strsplit_set (setup_get_str(&setup_prefs, set), ", \t", 0);
	
				    /* human name     slug           enabled? */
	store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
	
	for (entry = entries; *entry; entry += 2)
	{
		entry_enabled = FALSE;
		for (i = enabled; *i; i++)
		{
			if (strcmp (*i, entry[1]) == 0)
			{
				entry_enabled = TRUE;
				break;
			}
		}

		gtk_list_store_append (store, &iter);
		gtk_list_store_set (store, &iter,
				     0, entry[0],
				     1, entry[1],
				     2, entry_enabled,
				    -1);
	}
	
	g_strfreev (enabled);
	
	select = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (select), FALSE);
	g_object_unref (store);

	// first column
	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (set->label, renderer,
							   "text", 0, NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW (select), column);

	// second column
	user_data = g_new0 (struct multi_select_info, 1);
	user_data->store = store;
	user_data->set = set;
	renderer = gtk_cell_renderer_toggle_new ();
	column = gtk_tree_view_column_new_with_attributes ("", renderer,
							   "active", 2, NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW (select), column);
	g_signal_connect_data (G_OBJECT (renderer), "toggled", G_CALLBACK (setup_multi_select_cb), user_data,
			       (GClosureNotify) (setup_multi_select_info_free_cb), (GConnectFlags) 0);
	
	scrollbox = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollbox), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollbox), GTK_SHADOW_ETCHED_IN);
	gtk_container_add(GTK_CONTAINER (scrollbox), select);
	
	gtk_table_attach (GTK_TABLE (table), scrollbox, 2, 3, row + 1, row + 2,
			  GTK_SHRINK | GTK_FILL, GTK_FILL, LABEL_INDENT, 0);
			  
	return scrollbox;
}

static void


@@ 1296,7 1434,7 @@ setup_create_button (GtkWidget *table, int row, char *label, GCallback callback)
{
	GtkWidget *but = gtk_button_new_with_label (label);
	gtk_table_attach (GTK_TABLE (table), but, 2, 3, row, row + 1,
					GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 5);
			  GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 5);
	g_signal_connect (G_OBJECT (but), "clicked", callback, NULL);
}



@@ 1372,6 1510,10 @@ setup_create_page (const setting *set)
			break;
		case ST_ALERTHEAD:
			setup_create_alert_header (tab, row, &set[i]);
			break;
		case ST_MULTISEL:
			wid = setup_create_multi_select (tab, row, &set[i]);
			row++;
		}

		if (do_disable)


@@ 2334,9 2476,67 @@ setup_close_cb (GtkWidget *win, GtkWidget **swin)
	}
}

static void
setup_load_spellangs_cb (const char * const lang_tag,
			 const char * const provider_name,
			 const char * const provider_desc,
			 const char * const provider_file,
			 void * user_data)
{
	GSList **langs = (GSList **)user_data;
	
	*langs = g_slist_prepend(*langs, (gpointer)g_strdup(lang_tag));
}

static void
setup_load_spelllangs (void)
{
	struct EnchantBroker *broker;
	GSList *langs = NULL, *lang;
	guint num_langs;
	const char **ptr;
	const char *lang_code, *lang_name, *lang_country;
	
	if (spelllangs)
		return;

	broker = enchant_broker_init ();
	enchant_broker_list_dicts (broker, setup_load_spellangs_cb, &langs);
	enchant_broker_free (broker);
	
	num_langs = g_slist_length (langs);
	spelllangs = ptr = g_new0 (const char *, (num_langs * 2) + 1);

	codetable_init ();

	for (lang = langs; lang; lang = lang->next)
	{
		lang_name = lang_country = NULL;
		lang_code = (const char *) lang->data;
		
		if (num_langs > 1 && strcmp(lang_code, "en") == 0)
			continue;
		
		codetable_lookup (lang_code, &lang_name, &lang_country);

		if (lang_country == NULL || lang_country[0] == '\0')
			*(ptr++) = g_strdup (lang_name);
		else
			*(ptr++) = g_strdup_printf ("%s (%s)", lang_name, lang_country);
			
		*(ptr++) = lang_code;
	}
	
	*ptr = NULL;
	
	g_slist_free(langs);
}

void
setup_open (void)
{
	setup_load_spelllangs ();

	if (setup_window)
	{
		gtk_window_present (GTK_WINDOW (setup_window));


@@ 2348,6 2548,5 @@ setup_open (void)
	color_change = FALSE;
	setup_window = setup_window_open ();

	g_signal_connect (G_OBJECT (setup_window), "destroy",
							G_CALLBACK (setup_close_cb), &setup_window);
	g_signal_connect (G_OBJECT (setup_window), "destroy", G_CALLBACK (setup_close_cb), &setup_window);
}

M src/fe-gtk/sexy-spell-entry.c => src/fe-gtk/sexy-spell-entry.c +23 -141
@@ 31,6 31,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include "sexy-iso-codes.h"
#include "enchant.h"

#ifdef WIN32
#include "marshal.h"


@@ 50,35 51,6 @@
#include "palette.h"
#include "xtext.h"

/*
 * Bunch of poop to make enchant into a runtime dependency rather than a
 * compile-time dependency.  This makes it so I don't have to hear the
 * complaints from people with binary distributions who don't get spell
 * checking because they didn't check their configure output.
 */
struct EnchantDict;
struct EnchantBroker;

typedef void (*EnchantDictDescribeFn) (const char * const lang_tag,
                                       const char * const provider_name,
                                       const char * const provider_desc,
                                       const char * const provider_file,
                                       void * user_data);

static struct EnchantBroker * (*enchant_broker_init) (void);
static void (*enchant_broker_free) (struct EnchantBroker * broker);
static void (*enchant_broker_free_dict) (struct EnchantBroker * broker, struct EnchantDict * dict);
static void (*enchant_broker_list_dicts) (struct EnchantBroker * broker, EnchantDictDescribeFn fn, void * user_data);
static struct EnchantDict * (*enchant_broker_request_dict) (struct EnchantBroker * broker, const char *const tag);

static void (*enchant_dict_add_to_personal) (struct EnchantDict * dict, const char *const word, ssize_t len);
static void (*enchant_dict_add_to_session) (struct EnchantDict * dict, const char *const word, ssize_t len);
static int (*enchant_dict_check) (struct EnchantDict * dict, const char *const word, ssize_t len);
static void (*enchant_dict_describe) (struct EnchantDict * dict, EnchantDictDescribeFn fn, void * user_data);
static void (*enchant_dict_free_suggestions) (struct EnchantDict * dict, char **suggestions);
static void (*enchant_dict_store_replacement) (struct EnchantDict * dict, const char *const mis, ssize_t mis_len, const char *const cor, ssize_t cor_len);
static char ** (*enchant_dict_suggest) (struct EnchantDict * dict, const char *const word, ssize_t len, size_t * out_n_suggs);
static gboolean have_enchant = FALSE;

struct _SexySpellEntryPriv
{


@@ 119,7 91,6 @@ static gboolean   default_word_check                          (SexySpellEntry   
static gboolean   sexy_spell_entry_activate_language_internal (SexySpellEntry       *entry,
                                                               const gchar          *lang,
                                                               GError              **error);
static gchar     *get_lang_from_dict                          (struct EnchantDict   *dict);
static void       sexy_spell_entry_recheck_all                (SexySpellEntry       *entry);
static void       entry_strsplit_utf8                         (GtkEntry             *entry,
                                                               gchar              ***set,


@@ 155,73 126,6 @@ spell_accumulator(GSignalInvocationHint *hint, GValue *return_accu, const GValue
}

static void
initialize_enchant (void)
{
	GModule *enchant;
	gpointer funcptr;
    gsize i;
    const char * const libnames[] = {
#ifdef G_OS_WIN32
        "libenchant.dll",
#endif
#ifdef G_OS_UNIX
        "libenchant.so.1",
        "libenchant.so.2",
        "libenchant-2.so.2",
#endif
#ifdef __APPLE__
        "libenchant.dylib",
#endif
    };

    for (i = 0; i < G_N_ELEMENTS(libnames); ++i)
    {
        enchant = g_module_open(libnames[i], 0);
        if (enchant)
        {
            g_info ("Loaded %s", libnames[i]);
            have_enchant = TRUE;
            break;
        }
    }

  if (!have_enchant)
    return;

#define MODULE_SYMBOL(name, func, alt_name) G_STMT_START { \
    const char *funcname = name; \
    gboolean ret = g_module_symbol(enchant, funcname, &funcptr); \
    if (alt_name) { \
        funcname = alt_name; \
        ret = g_module_symbol(enchant, funcname, &funcptr); \
    } \
    if (ret == FALSE) { \
        g_warning ("Failed to find enchant symbol %s", funcname); \
        have_enchant = FALSE; \
        return; \
    } \
    (func) = funcptr; \
} G_STMT_END;

	MODULE_SYMBOL("enchant_broker_init", enchant_broker_init, NULL)
	MODULE_SYMBOL("enchant_broker_free", enchant_broker_free, NULL)
	MODULE_SYMBOL("enchant_broker_free_dict", enchant_broker_free_dict, NULL)
	MODULE_SYMBOL("enchant_broker_list_dicts", enchant_broker_list_dicts, NULL)
	MODULE_SYMBOL("enchant_broker_request_dict", enchant_broker_request_dict, NULL)

	MODULE_SYMBOL("enchant_dict_add_to_personal", enchant_dict_add_to_personal,
                  "enchant_dict_add")
	MODULE_SYMBOL("enchant_dict_add_to_session", enchant_dict_add_to_session, NULL)
	MODULE_SYMBOL("enchant_dict_check", enchant_dict_check, NULL)
	MODULE_SYMBOL("enchant_dict_describe", enchant_dict_describe, NULL)
	MODULE_SYMBOL("enchant_dict_free_suggestions",
				  enchant_dict_free_suggestions, "enchant_dict_free_string_list")
	MODULE_SYMBOL("enchant_dict_store_replacement",
				  enchant_dict_store_replacement, NULL)
	MODULE_SYMBOL("enchant_dict_suggest", enchant_dict_suggest, NULL)
}

static void
sexy_spell_entry_class_init(SexySpellEntryClass *klass)
{
	GObjectClass *gobject_class;


@@ 236,7 140,7 @@ sexy_spell_entry_class_init(SexySpellEntryClass *klass)
	object_class  = G_OBJECT_CLASS(klass);
	widget_class  = GTK_WIDGET_CLASS(klass);

	if (have_enchant)
	if (have_enchant())
		klass->word_check = default_word_check;

	gobject_class->finalize = sexy_spell_entry_finalize;


@@ 476,7 380,7 @@ add_to_dictionary(GtkWidget *menuitem, SexySpellEntry *entry)
	gint start, end;
	struct EnchantDict *dict;

	if (!have_enchant)
	if (!have_enchant())
		return;

	get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character);


@@ 504,7 408,7 @@ ignore_all(GtkWidget *menuitem, SexySpellEntry *entry)
	gint start, end;
	GSList *li;

	if (!have_enchant)
	if (!have_enchant())
		return;

	get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character);


@@ 535,7 439,7 @@ replace_word(GtkWidget *menuitem, SexySpellEntry *entry)
	gint cursor;
	struct EnchantDict *dict;

	if (!have_enchant)
	if (!have_enchant())
		return;

	get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character);


@@ 572,7 476,7 @@ build_suggestion_menu(SexySpellEntry *entry, GtkWidget *menu, struct EnchantDict
	gchar **suggestions;
	size_t n_suggestions, i;

	if (!have_enchant)
	if (!have_enchant())
		return;

	suggestions = enchant_dict_suggest(dict, word, -1, &n_suggestions);


@@ 620,7 524,7 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word)
	GtkWidget *topmenu, *mi;
	gchar *label;

	if (!have_enchant)
	if (!have_enchant())
		return NULL;

	topmenu = gtk_menu_new();


@@ 639,7 543,7 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word)

		for (li = entry->priv->dict_list; li; li = g_slist_next (li)) {
			dict = (struct EnchantDict *) li->data;
			lang = get_lang_from_dict(dict);
			lang = enchant_get_lang_from_dict(dict);
			lang_name = sexy_spell_entry_get_language_name (entry, lang);
			if (lang_name)
			{


@@ 686,7 590,7 @@ build_spelling_menu(SexySpellEntry *entry, const gchar *word)

		for (li = entry->priv->dict_list; li; li = g_slist_next(li)) {
			dict = (struct EnchantDict *)li->data;
			lang = get_lang_from_dict(dict);
			lang = enchant_get_lang_from_dict(dict);
			lang_name = sexy_spell_entry_get_language_name (entry, lang);
			if (lang_name)
			{


@@ 727,7 631,7 @@ sexy_spell_entry_populate_popup(SexySpellEntry *entry, GtkMenu *menu, gpointer d
	gint start, end;
	gchar *word;

	if ((have_enchant == FALSE) || (entry->priv->checked == FALSE))
	if ((have_enchant() == FALSE) || (entry->priv->checked == FALSE))
		return;

	if (g_slist_length(entry->priv->dict_list) == 0)


@@ 765,7 669,7 @@ sexy_spell_entry_init(SexySpellEntry *entry)

	entry->priv->dict_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);

	if (have_enchant)
	if (have_enchant())
	{
#ifdef HAVE_ISO_CODES
		if (codetable_ref == 0)


@@ 803,7 707,7 @@ sexy_spell_entry_finalize(GObject *obj)
	g_free(entry->priv->word_starts);
	g_free(entry->priv->word_ends);

	if (have_enchant) {
	if (have_enchant()) {
		if (entry->priv->broker) {
			GSList *li;
			for (li = entry->priv->dict_list; li; li = g_slist_next(li)) {


@@ 862,7 766,7 @@ default_word_check(SexySpellEntry *entry, const gchar *word)
	gboolean result = TRUE;
	GSList *li;

	if (!have_enchant)
	if (!have_enchant())
		return result;

	if (g_unichar_isalpha(*word) == FALSE) {


@@ 1088,7 992,7 @@ sexy_spell_entry_recheck_all(SexySpellEntry *entry)
		check_attributes (entry, text, text_len);
	}

	if (have_enchant && entry->priv->checked
	if (have_enchant() && entry->priv->checked
		&& g_slist_length (entry->priv->dict_list) != 0)
	{
		/* Loop through words */


@@ 1257,7 1161,7 @@ sexy_spell_entry_activate_default_languages(SexySpellEntry *entry)
	GSList *enchant_langs;
	char *lang, **i, **langs;

	if (!have_enchant)
	if (!have_enchant())
		return;

	if (!entry->priv->broker)


@@ 1288,34 1192,12 @@ sexy_spell_entry_activate_default_languages(SexySpellEntry *entry)
	sexy_spell_entry_recheck_all (entry);
}

static void
get_lang_from_dict_cb(const char * const lang_tag,
		      const char * const provider_name,
		      const char * const provider_desc,
		      const char * const provider_file,
		      void * user_data) {
	gchar **lang = (gchar **)user_data;
	*lang = g_strdup(lang_tag);
}

static gchar *
get_lang_from_dict(struct EnchantDict *dict)
{
	gchar *lang;

	if (!have_enchant)
		return NULL;

	enchant_dict_describe(dict, get_lang_from_dict_cb, &lang);
	return lang;
}

static gboolean
sexy_spell_entry_activate_language_internal(SexySpellEntry *entry, const gchar *lang, GError **error)
{
	struct EnchantDict *dict;

	if (!have_enchant)
	if (!have_enchant())
		return FALSE;

	if (!entry->priv->broker)


@@ 1333,7 1215,7 @@ sexy_spell_entry_activate_language_internal(SexySpellEntry *entry, const gchar *

	enchant_dict_add_to_session (dict, "HexChat", strlen("HexChat"));
	entry->priv->dict_list = g_slist_append(entry->priv->dict_list, (gpointer) dict);
	g_hash_table_insert(entry->priv->dict_hash, get_lang_from_dict(dict), (gpointer) dict);
	g_hash_table_insert(entry->priv->dict_hash, enchant_get_lang_from_dict(dict), (gpointer) dict);

	return TRUE;
}


@@ 1394,7 1276,7 @@ sexy_spell_entry_get_language_name(const SexySpellEntry *entry,
	const gchar *lang_name = "";
	const gchar *country_name = "";

	g_return_val_if_fail (have_enchant, NULL);
	g_return_val_if_fail (have_enchant(), NULL);

	if (codetable_ref == 0)
		codetable_init ();


@@ 1449,7 1331,7 @@ sexy_spell_entry_activate_language(SexySpellEntry *entry, const gchar *lang, GEr
	g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), FALSE);
	g_return_val_if_fail(lang != NULL && *lang != '\0', FALSE);

	if (!have_enchant)
	if (!have_enchant())
		return FALSE;

	if (error)


@@ 1484,7 1366,7 @@ sexy_spell_entry_deactivate_language(SexySpellEntry *entry, const gchar *lang)
	g_return_if_fail(entry != NULL);
	g_return_if_fail(SEXY_IS_SPELL_ENTRY(entry));

	if (!have_enchant)
	if (!have_enchant())
		return;

	if (!entry->priv->dict_list)


@@ 1545,7 1427,7 @@ sexy_spell_entry_set_active_languages(SexySpellEntry *entry, GSList *langs, GErr
	g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), FALSE);
	g_return_val_if_fail(langs != NULL, FALSE);

	if (!have_enchant)
	if (!have_enchant())
		return FALSE;

	/* deactivate all languages first */


@@ 1585,12 1467,12 @@ sexy_spell_entry_get_active_languages(SexySpellEntry *entry)
	g_return_val_if_fail(entry != NULL, NULL);
	g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), NULL);

	if (!have_enchant)
	if (!have_enchant())
		return NULL;

	for (li = entry->priv->dict_list; li; li = g_slist_next(li)) {
		dict = (struct EnchantDict *) li->data;
		lang = get_lang_from_dict(dict);
		lang = enchant_get_lang_from_dict(dict);
		ret = g_slist_append(ret, lang);
	}
	return ret;