summary refs log blame commit diff stats
path: root/src/fe-gtk/sexy-spell-entry.c
blob: 42d47127da752ea0b4fc8244d896952f1a8283f7 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

















                                                                      
                                                                                          

                    
                   




                             
                  
                   

                       
                     

                           
 
            
                              
               

                   
      
 
                               
                               
                    
                  
 








































                                                                                                                                                          
                                        



































                                                                                            



                             

























                                                                                                                                                      
            
                                                     


                                                 

                            



                                                              
     
                       
      
































                                                                                   







                                                       























                                                                              
                                                        





                                                                                            
                                                             










































                                                                                 



















                                                                      
 


                               
                                                                                                                         
                                                                  











                                                                



































                                                                                                  
                               


                                             
         
                                                                                                                   

                                                                                                                        
            
         
                                                                                                                      

                                                                                                                           








                                                                                                                      


                                                                














                                                                
















































                                                                                                                       
                                             












































                                                                                                                       
                                                                                       






















































































                                                                                                          











                                                                               


                                                                                     
                                                                             



                                                   










                                                                           












                                                                                                                            














                                                                                                 


                                                                                     
                                                                                



                                                   










                                                                                                            



























































                                                                                                                               
         
                     


                                          
      

                                                                   
 


                                                       
                                      








































                                                                                                               
                     


                                  
      







                                                            






























































                                                                           
                                                                 


























                                                                                      



                                                          
                                                                   
 





                                   

                             











                                                            
                                         

                                  
                                                            

                                                         
                                         

                                    
                                                            

                                                               
                                         



                                                            









                                                                





                                          
            













                                                                         



                                                                          














                                                      
                                                                              

































                                                                                                  





                                                   
                                 

                                              
                                
                         




                                                                                       







                                                                         










                                                                                                   




                                                                    



                                                                           
                                       

                                                
                                                                                        









                                                                 


                                                                    





























                                                                                       
                                       




                                                        
                                                                           


















                                                         

                                                                                                  













                                                                                      





                                                              
 

                               










                                                                                                                       



                                               









                                               

                                                          





                                                                  

                              






                                                            

                                                              
                                                      
 

                                   
         
                                                           
                 
                                                                                        
                 
                                          
         


                                                             
                       



                                                                               

                                             












































                                                                                                                          
                                                                         

























































                                                                                               





                                                  


                                  

                                                           


                                  






                                                                            
























































































































































































































                                                                                                                               

                                                                 

                                                     




                                       







                                                                                                                               



































                                                                                                                                 
/*
 * @file libsexy/sexy-icon-entry.c Entry widget
 *
 * @Copyright (C) 2004-2006 Christian Hammond.
 * Some of this code is from gtkspell, Copyright (C) 2002 Evan Martin.
 *
 * 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
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gtk/gtk.h>
#include "sexy-spell-entry.h"
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <glib/gi18n.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sexy-iso-codes.h"
#include "sexy-marshal.h"

#ifdef WIN32
#include "../common/typedef.h"
#include <io.h>
#else
#include <unistd.h>
#endif

#include "../common/cfgfiles.h"
#include "../common/hexchatc.h"
#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
{
	struct EnchantBroker *broker;
	PangoAttrList        *attr_list;
	gint                  mark_character;
	GHashTable           *dict_hash;
	GSList               *dict_list;
	gchar               **words;
	gint                 *word_starts;
	gint                 *word_ends;
	gboolean              checked;
	gboolean              parseattr;
};

static void sexy_spell_entry_class_init(SexySpellEntryClass *klass);
static void sexy_spell_entry_editable_init (GtkEditableClass *iface);
static void sexy_spell_entry_init(SexySpellEntry *entry);
static void sexy_spell_entry_finalize(GObject *obj);
static void sexy_spell_entry_destroy(GtkObject *obj);
static gint sexy_spell_entry_expose(GtkWidget *widget, GdkEventExpose *event);
static gint sexy_spell_entry_button_press(GtkWidget *widget, GdkEventButton *event);

/* GtkEditable handlers */
static void sexy_spell_entry_changed(GtkEditable *editable, gpointer data);

/* Other handlers */
static gboolean sexy_spell_entry_popup_menu(GtkWidget *widget, SexySpellEntry *entry);

/* Internal utility functions */
static gint       gtk_entry_find_position                     (GtkEntry             *entry,
                                                               gint                  x);
static gboolean   word_misspelled                             (SexySpellEntry       *entry,
                                                               int                   start,
                                                               int                   end);
static gboolean   default_word_check                          (SexySpellEntry       *entry,
                                                               const gchar          *word);
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,
                                                               gint                **starts,
                                                               gint                **ends);

static GtkEntryClass *parent_class = NULL;

#ifdef HAVE_ISO_CODES
static int codetable_ref = 0;
#endif

G_DEFINE_TYPE_EXTENDED(SexySpellEntry, sexy_spell_entry, GTK_TYPE_ENTRY, 0, G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE, sexy_spell_entry_editable_init));

enum
{
	WORD_CHECK,
	LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = {0};

static gboolean
spell_accumulator(GSignalInvocationHint *hint, GValue *return_accu, const GValue *handler_return, gpointer data)
{
	gboolean ret = g_value_get_boolean(handler_return);
	/* Handlers return TRUE if the word is misspelled.  In this
	 * case, it means that we want to stop if the word is checked
	 * as correct */
	g_value_set_boolean (return_accu, ret);
	return ret;
}

static void
initialize_enchant ()
{
	GModule *enchant;
	gpointer funcptr;

#ifdef WIN32
	enchant = g_module_open("libenchant.dll", 0);
#else
	enchant = g_module_open("libenchant", 0);
#endif
	if (enchant == NULL)
	{
#ifndef WIN32
		enchant = g_module_open("libenchant.so.1", 0);
				if (enchant == NULL)
					return;
#else
		return;
#endif
	}

	have_enchant = TRUE;

#define MODULE_SYMBOL(name, func) \
	g_module_symbol(enchant, (name), &funcptr); \
	(func) = funcptr;

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

	MODULE_SYMBOL("enchant_dict_add_to_personal", enchant_dict_add_to_personal)
	MODULE_SYMBOL("enchant_dict_add_to_session", enchant_dict_add_to_session)
	MODULE_SYMBOL("enchant_dict_check", enchant_dict_check)
	MODULE_SYMBOL("enchant_dict_describe", enchant_dict_describe)
	MODULE_SYMBOL("enchant_dict_free_suggestions",
				  enchant_dict_free_suggestions)
	MODULE_SYMBOL("enchant_dict_store_replacement",
				  enchant_dict_store_replacement)
	MODULE_SYMBOL("enchant_dict_suggest", enchant_dict_suggest)

#undef MODULE_SYMBOL
}

static void
sexy_spell_entry_class_init(SexySpellEntryClass *klass)
{
	GObjectClass *gobject_class;
	GtkObjectClass *object_class;
	GtkWidgetClass *widget_class;

	initialize_enchant();

	parent_class = g_type_class_peek_parent(klass);

	gobject_class = G_OBJECT_CLASS(klass);
	object_class  = GTK_OBJECT_CLASS(klass);
	widget_class  = GTK_WIDGET_CLASS(klass);

	if (have_enchant)
		klass->word_check = default_word_check;

	gobject_class->finalize = sexy_spell_entry_finalize;

	object_class->destroy = sexy_spell_entry_destroy;

	widget_class->expose_event = sexy_spell_entry_expose;
	widget_class->button_press_event = sexy_spell_entry_button_press;

	/**
	 * SexySpellEntry::word-check:
	 * @entry: The entry on which the signal is emitted.
	 * @word: The word to check.
	 *
	 * The ::word-check signal is emitted whenever the entry has to check
	 * a word.  This allows the application to mark words as correct even
	 * if none of the active dictionaries contain it, such as nicknames in
	 * a chat client.
	 *
	 * Returns: %FALSE to indicate that the word should be marked as
	 * correct.
	 */
	signals[WORD_CHECK] = g_signal_new("word_check",
					   G_TYPE_FROM_CLASS(object_class),
					   G_SIGNAL_RUN_LAST,
					   G_STRUCT_OFFSET(SexySpellEntryClass, word_check),
					   (GSignalAccumulator) spell_accumulator, NULL,
					   sexy_marshal_BOOLEAN__STRING,
					   G_TYPE_BOOLEAN,
					   1, G_TYPE_STRING);
}

static void
sexy_spell_entry_editable_init (GtkEditableClass *iface)
{
}

static gint
gtk_entry_find_position (GtkEntry *entry, gint x)
{
	PangoLayout *layout;
	PangoLayoutLine *line;
	const gchar *text;
	gint cursor_index;
	gint index;
	gint pos;
	gboolean trailing;

	x = x + entry->scroll_offset;

	layout = gtk_entry_get_layout(entry);
	text = pango_layout_get_text(layout);
	cursor_index = g_utf8_offset_to_pointer(text, entry->current_pos) - text;

	line = pango_layout_get_lines(layout)->data;
	pango_layout_line_x_to_index(line, x * PANGO_SCALE, &index, &trailing);

	if (index >= cursor_index && entry->preedit_length) {
		if (index >= cursor_index + entry->preedit_length) {
			index -= entry->preedit_length;
		} else {
			index = cursor_index;
			trailing = FALSE;
		}
	}

	pos = g_utf8_pointer_to_offset (text, text + index);
	pos += trailing;

	return pos;
}

static void
insert_hiddenchar (SexySpellEntry *entry, guint start, guint end)
{
	PangoAttribute *hattr;
	PangoRectangle *rect = g_malloc (sizeof (PangoRectangle));

	rect->x = 0;
	rect->y = 0;
	rect->width = 0;
	rect->height = 0;

	hattr = pango_attr_shape_new (rect, rect);
	hattr->start_index = start;
	hattr->end_index = end;
	pango_attr_list_insert (entry->priv->attr_list, hattr);

	g_free (rect);
}

static void
insert_underline_error (SexySpellEntry *entry, guint start, guint end)
{
	PangoAttribute *ucolor;
	PangoAttribute *unline;

	ucolor = pango_attr_underline_color_new (colors[COL_SPELL].red, colors[COL_SPELL].green, colors[COL_SPELL].blue);
	unline = pango_attr_underline_new (PANGO_UNDERLINE_ERROR);

	ucolor->start_index = start;
	unline->start_index = start;

	ucolor->end_index = end;
	unline->end_index = end;

	pango_attr_list_insert (entry->priv->attr_list, ucolor);
	pango_attr_list_insert (entry->priv->attr_list, unline);
}

static void
insert_underline (SexySpellEntry *entry, guint start, gboolean toggle)
{
	PangoAttribute *uattr;

	uattr = pango_attr_underline_new (toggle ? PANGO_UNDERLINE_NONE : PANGO_UNDERLINE_SINGLE);
	uattr->start_index = start;
	uattr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
	pango_attr_list_change (entry->priv->attr_list, uattr);
}

static void
insert_bold (SexySpellEntry *entry, guint start, gboolean toggle)
{
	PangoAttribute *battr;

	battr = pango_attr_weight_new (toggle ? PANGO_WEIGHT_NORMAL : PANGO_WEIGHT_BOLD);
	battr->start_index = start;
	battr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
	pango_attr_list_change (entry->priv->attr_list, battr);
}

static void
insert_italic (SexySpellEntry *entry, guint start, gboolean toggle)
{
	PangoAttribute *iattr;

	iattr  = pango_attr_style_new (toggle ? PANGO_STYLE_NORMAL : PANGO_STYLE_ITALIC); 
	iattr->start_index = start;
	iattr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
	pango_attr_list_change (entry->priv->attr_list, iattr);
}

static void
insert_color (SexySpellEntry *entry, guint start, int fgcolor, int bgcolor)
{
	PangoAttribute *fgattr;
	PangoAttribute *ulattr;
	PangoAttribute *bgattr;

	if (fgcolor < 0 || fgcolor > MAX_COL)
	{
		fgattr = pango_attr_foreground_new (colors[COL_FG].red, colors[COL_FG].green, colors[COL_FG].blue);
		ulattr = pango_attr_underline_color_new (colors[COL_FG].red, colors[COL_FG].green, colors[COL_FG].blue);
	}
	else
	{
		fgattr = pango_attr_foreground_new (colors[fgcolor].red, colors[fgcolor].green, colors[fgcolor].blue);
		ulattr = pango_attr_underline_color_new (colors[fgcolor].red, colors[fgcolor].green, colors[fgcolor].blue);
	}

	if (bgcolor < 0 || bgcolor > MAX_COL)
		bgattr = pango_attr_background_new (colors[COL_BG].red, colors[COL_BG].green, colors[COL_BG].blue);
	else
		bgattr = pango_attr_background_new (colors[bgcolor].red, colors[bgcolor].green, colors[bgcolor].blue);

	fgattr->start_index = start;
	fgattr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
	pango_attr_list_change (entry->priv->attr_list, fgattr);
	ulattr->start_index = start;
	ulattr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
	pango_attr_list_change (entry->priv->attr_list, ulattr);
	bgattr->start_index = start;
	bgattr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
	pango_attr_list_change (entry->priv->attr_list, bgattr);
}

static void
insert_reset (SexySpellEntry *entry, guint start)
{
	insert_bold (entry, start, TRUE);
	insert_underline (entry, start, TRUE);
	insert_italic (entry, start, TRUE);
	insert_color (entry, start, -1, -1);
}

static void
get_word_extents_from_position(SexySpellEntry *entry, gint *start, gint *end, guint position)
{
	const gchar *text;
	gint i, bytes_pos;

	*start = -1;
	*end = -1;

	if (entry->priv->words == NULL)
		return;

	text = gtk_entry_get_text(GTK_ENTRY(entry));
	bytes_pos = (gint) (g_utf8_offset_to_pointer(text, position) - text);

	for (i = 0; entry->priv->words[i]; i++) {
		if (bytes_pos >= entry->priv->word_starts[i] &&
		    bytes_pos <= entry->priv->word_ends[i]) {
			*start = entry->priv->word_starts[i];
			*end   = entry->priv->word_ends[i];
			return;
		}
	}
}

static void
add_to_dictionary(GtkWidget *menuitem, SexySpellEntry *entry)
{
	char *word;
	gint start, end;
	struct EnchantDict *dict;

	if (!have_enchant)
		return;

	get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character);
	word = gtk_editable_get_chars(GTK_EDITABLE(entry), start, end);

	dict = (struct EnchantDict *) g_object_get_data(G_OBJECT(menuitem), "enchant-dict");
	if (dict)
		enchant_dict_add_to_personal(dict, word, -1);

	g_free(word);

	if (entry->priv->words) {
		g_strfreev(entry->priv->words);
		g_free(entry->priv->word_starts);
		g_free(entry->priv->word_ends);
	}
	entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends);
	sexy_spell_entry_recheck_all (entry);
}

static void
ignore_all(GtkWidget *menuitem, SexySpellEntry *entry)
{
	char *word;
	gint start, end;
	GSList *li;

	if (!have_enchant)
		return;

	get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character);
	word = gtk_editable_get_chars(GTK_EDITABLE(entry), start, end);

	for (li = entry->priv->dict_list; li; li = g_slist_next (li)) {
		struct EnchantDict *dict = (struct EnchantDict *) li->data;
		enchant_dict_add_to_session(dict, word, -1);
	}

	g_free(word);

	if (entry->priv->words) {
		g_strfreev(entry->priv->words);
		g_free(entry->priv->word_starts);
		g_free(entry->priv->word_ends);
	}
	entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends);
	sexy_spell_entry_recheck_all(entry);
}

static void
replace_word(GtkWidget *menuitem, SexySpellEntry *entry)
{
	char *oldword;
	const char *newword;
	gint start, end;
	gint cursor;
	struct EnchantDict *dict;

	if (!have_enchant)
		return;

	get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character);
	oldword = gtk_editable_get_chars(GTK_EDITABLE(entry), start, end);
	newword = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child (GTK_BIN(menuitem))));

	cursor = gtk_editable_get_position(GTK_EDITABLE(entry));
	/* is the cursor at the end? If so, restore it there */
	if (g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(entry)), -1) == cursor)
		cursor = -1;
	else if(cursor > start && cursor <= end)
		cursor = start;

	gtk_editable_delete_text(GTK_EDITABLE(entry), start, end);
	gtk_editable_set_position(GTK_EDITABLE(entry), start);
	gtk_editable_insert_text(GTK_EDITABLE(entry), newword, strlen(newword),
							 &start);
	gtk_editable_set_position(GTK_EDITABLE(entry), cursor);

	dict = (struct EnchantDict *) g_object_get_data(G_OBJECT(menuitem), "enchant-dict");

        if (dict)
		enchant_dict_store_replacement(dict,
					       oldword, -1,
					       newword, -1);

	g_free(oldword);
}

static void
build_suggestion_menu(SexySpellEntry *entry, GtkWidget *menu, struct EnchantDict *dict, const gchar *word)
{
	GtkWidget *mi;
	gchar **suggestions;
	size_t n_suggestions, i;

	if (!have_enchant)
		return;

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

	if (suggestions == NULL || n_suggestions == 0) {
		/* no suggestions.  put something in the menu anyway... */
		GtkWidget *label = gtk_label_new("");
		gtk_label_set_markup(GTK_LABEL(label), _("<i>(no suggestions)</i>"));

		mi = gtk_separator_menu_item_new();
		gtk_container_add(GTK_CONTAINER(mi), label);
		gtk_widget_show_all(mi);
		gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
	} else {
		/* build a set of menus with suggestions */
		for (i = 0; i < n_suggestions; i++) {
			if ((i != 0) && (i % 10 == 0)) {
				mi = gtk_separator_menu_item_new();
				gtk_widget_show(mi);
				gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);

				mi = gtk_menu_item_new_with_label(_("More..."));
				gtk_widget_show(mi);
				gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);

				menu = gtk_menu_new();
				gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu);
			}

			mi = gtk_menu_item_new_with_label(suggestions[i]);
			g_object_set_data(G_OBJECT(mi), "enchant-dict", dict);
			g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(replace_word), entry);
			gtk_widget_show(mi);
			gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
		}
	}

	enchant_dict_free_suggestions(dict, suggestions);
}

static GtkWidget *
build_spelling_menu(SexySpellEntry *entry, const gchar *word)
{
	struct EnchantDict *dict;
	GtkWidget *topmenu, *mi;
	gchar *label;

	if (!have_enchant)
		return NULL;

	topmenu = gtk_menu_new();

	if (entry->priv->dict_list == NULL)
		return topmenu;

	/* Suggestions */
	if (g_slist_length(entry->priv->dict_list) == 1) {
		dict = (struct EnchantDict *) entry->priv->dict_list->data;
		build_suggestion_menu(entry, topmenu, dict, word);
	} else {
		GSList *li;
		GtkWidget *menu;
		gchar *lang, *lang_name;

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

			gtk_widget_show(mi);
			gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi);
			menu = gtk_menu_new();
			gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu);
			build_suggestion_menu(entry, menu, dict, word);
		}
	}

	/* Separator */
	mi = gtk_separator_menu_item_new ();
	gtk_widget_show(mi);
	gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi);

	/* + Add to Dictionary */
	label = g_strdup_printf(_("Add \"%s\" to Dictionary"), word);
	mi = gtk_image_menu_item_new_with_label(label);
	g_free(label);

	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));

	if (g_slist_length(entry->priv->dict_list) == 1) {
		dict = (struct EnchantDict *) entry->priv->dict_list->data;
		g_object_set_data(G_OBJECT(mi), "enchant-dict", dict);
		g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(add_to_dictionary), entry);
	} else {
		GSList *li;
		GtkWidget *menu, *submi;
		gchar *lang, *lang_name;

		menu = gtk_menu_new();
		gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu);

		for (li = entry->priv->dict_list; li; li = g_slist_next(li)) {
			dict = (struct EnchantDict *)li->data;
			lang = get_lang_from_dict(dict);
			lang_name = sexy_spell_entry_get_language_name (entry, lang);
			if (lang_name)
			{
				submi = gtk_menu_item_new_with_label(lang_name);
				g_free (lang_name);
			}
			else 
			{
				submi = gtk_menu_item_new_with_label(lang);
			}
			g_free(lang);
			g_object_set_data(G_OBJECT(submi), "enchant-dict", dict);

			g_signal_connect(G_OBJECT(submi), "activate", G_CALLBACK(add_to_dictionary), entry);

			gtk_widget_show(submi);
			gtk_menu_shell_append(GTK_MENU_SHELL(menu), submi);
		}
	}

	gtk_widget_show_all(mi);
	gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi);

	/* - Ignore All */
	mi = gtk_image_menu_item_new_with_label(_("Ignore All"));
	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
	g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(ignore_all), entry);
	gtk_widget_show_all(mi);
	gtk_menu_shell_append(GTK_MENU_SHELL(topmenu), mi);

	return topmenu;
}

static void
sexy_spell_entry_populate_popup(SexySpellEntry *entry, GtkMenu *menu, gpointer data)
{
	GtkWidget *icon, *mi;
	gint start, end;
	gchar *word;

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

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

	get_word_extents_from_position(entry, &start, &end, entry->priv->mark_character);
	if (start == end)
		return;
	if (!word_misspelled(entry, start, end))
		return;

	/* separator */
	mi = gtk_separator_menu_item_new();
	gtk_widget_show(mi);
	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);

	/* Above the separator, show the suggestions menu */
	icon = gtk_image_new_from_stock(GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU);
	mi = gtk_image_menu_item_new_with_label(_("Spelling Suggestions"));
	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), icon);

	word = gtk_editable_get_chars(GTK_EDITABLE(entry), start, end);
	g_assert(word != NULL);
	gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), build_spelling_menu(entry, word));
	g_free(word);

	gtk_widget_show_all(mi);
	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
}

static void
sexy_spell_entry_init(SexySpellEntry *entry)
{
	entry->priv = g_new0(SexySpellEntryPriv, 1);

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

	if (have_enchant)
	{
#ifdef HAVE_ISO_CODES
		if (codetable_ref == 0)
			codetable_init ();
		codetable_ref++;
#endif
		sexy_spell_entry_activate_default_languages(entry);
	}

	entry->priv->attr_list = pango_attr_list_new();

	entry->priv->checked = TRUE;
	entry->priv->parseattr = TRUE;

	g_signal_connect(G_OBJECT(entry), "popup-menu", G_CALLBACK(sexy_spell_entry_popup_menu), entry);
	g_signal_connect(G_OBJECT(entry), "populate-popup", G_CALLBACK(sexy_spell_entry_populate_popup), NULL);
	g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(sexy_spell_entry_changed), NULL);
}

static void
sexy_spell_entry_finalize(GObject *obj)
{
	SexySpellEntry *entry;

	g_return_if_fail(obj != NULL);
	g_return_if_fail(SEXY_IS_SPELL_ENTRY(obj));

	entry = SEXY_SPELL_ENTRY(obj);

	if (entry->priv->attr_list)
		pango_attr_list_unref(entry->priv->attr_list);
	if (entry->priv->dict_hash)
		g_hash_table_destroy(entry->priv->dict_hash);
	if (entry->priv->words)
		g_strfreev(entry->priv->words);
	if (entry->priv->word_starts)
		g_free(entry->priv->word_starts);
	if (entry->priv->word_ends)
		g_free(entry->priv->word_ends);

	if (have_enchant) {
		if (entry->priv->broker) {
			GSList *li;
			for (li = entry->priv->dict_list; li; li = g_slist_next(li)) {
				struct EnchantDict *dict = (struct EnchantDict*) li->data;
				enchant_broker_free_dict (entry->priv->broker, dict);
			}
			g_slist_free (entry->priv->dict_list);

			enchant_broker_free(entry->priv->broker);
		}
	}

	g_free(entry->priv);
#ifdef HAVE_ISO_CODES
	codetable_ref--;
	if (codetable_ref == 0)
		codetable_free ();
#endif

	if (G_OBJECT_CLASS(parent_class)->finalize)
		G_OBJECT_CLASS(parent_class)->finalize(obj);
}

static void
sexy_spell_entry_destroy(GtkObject *obj)
{
	if (GTK_OBJECT_CLASS(parent_class)->destroy)
		GTK_OBJECT_CLASS(parent_class)->destroy(obj);
}

/**
 * sexy_spell_entry_new
 *
 * Creates a new SexySpellEntry widget.
 *
 * Returns: a new #SexySpellEntry.
 */
GtkWidget *
sexy_spell_entry_new(void)
{
	return GTK_WIDGET(g_object_new(SEXY_TYPE_SPELL_ENTRY, NULL));
}

GQuark
sexy_spell_error_quark(void)
{
	static GQuark q = 0;
	if (q == 0)
		q = g_quark_from_static_string("sexy-spell-error-quark");
	return q;
}

static gboolean
default_word_check(SexySpellEntry *entry, const gchar *word)
{
	gboolean result = TRUE;
	GSList *li;

	if (!have_enchant)
		return result;

	if (g_unichar_isalpha(*word) == FALSE) {
		/* We only want to check words */
		return FALSE;
	}
	for (li = entry->priv->dict_list; li; li = g_slist_next (li)) {
		struct EnchantDict *dict = (struct EnchantDict *) li->data;
		if (enchant_dict_check(dict, word, strlen(word)) == 0) {
			result = FALSE;
			break;
		}
	}
	return result;
}

static gboolean
word_misspelled(SexySpellEntry *entry, int start, int end)
{
	const gchar *text;
	gchar *word;
	gboolean ret;

	if (start == end)
		return FALSE;
	text = gtk_entry_get_text(GTK_ENTRY(entry));
	word = g_new0(gchar, end - start + 2);

	g_strlcpy(word, text + start, end - start + 1);

	g_signal_emit(entry, signals[WORD_CHECK], 0, word, &ret);

	g_free(word);
	return ret;
}

static void
check_word(SexySpellEntry *entry, int start, int end)
{
	PangoAttrIterator *it;

	/* Check to see if we've got any attributes at this position.
	 * If so, free them, since we'll readd it if the word is misspelled */
	it = pango_attr_list_get_iterator(entry->priv->attr_list);
	if (it == NULL)
		return;
	do {
		gint s, e;
		pango_attr_iterator_range(it, &s, &e);
		if (s == start) {
			GSList *attrs = pango_attr_iterator_get_attrs(it);
			g_slist_foreach(attrs, (GFunc) pango_attribute_destroy, NULL);
			g_slist_free(attrs);
		}
	} while (pango_attr_iterator_next(it));
	pango_attr_iterator_destroy(it);

	if (word_misspelled(entry, start, end))
		insert_underline_error(entry, start, end);
}

static void
check_attributes (SexySpellEntry *entry, const char *text, int len)
{
	gboolean bold = FALSE;
	gboolean italic = FALSE;
	gboolean underline = FALSE;
	int parsing_color = 0;
	char fg_color[3];
	char bg_color[3];
	int i, bg_offset = 0;
	int fg_offset = 0;

	memset (bg_color, 0, sizeof(bg_color));
	memset (fg_color, 0, sizeof(fg_color));

	for (i = 0; i < len; i++)
	{
		switch (text[i])
		{
		case ATTR_BOLD:
			insert_hiddenchar (entry, i, i + 1);
			insert_bold (entry, i, bold);
			bold = !bold;
			goto check_color;

		case ATTR_ITALICS:
			insert_hiddenchar (entry, i, i + 1);
			insert_italic (entry, i, italic);
			italic = !italic;
			goto check_color;

		case ATTR_UNDERLINE:
			insert_hiddenchar (entry, i, i + 1);
			insert_underline (entry, i, underline);
			underline = !underline;
			goto check_color;

		case ATTR_RESET:
			insert_hiddenchar (entry, i, i + 1);
			insert_reset (entry, i);
			goto check_color;

		case ATTR_HIDDEN:
			insert_hiddenchar (entry, i, i + 1);
			goto check_color;

		case ATTR_REVERSE:
			insert_hiddenchar (entry, i, i + 1);
			insert_color (entry, i, COL_BG, COL_FG);
			goto check_color;

		case ATTR_COLOR:
			parsing_color = 1;
			break;

		default:
check_color:
			if (!parsing_color)
				continue;

			if (!g_unichar_isdigit (text[i]))
			{
				if (text[i] == ',' && parsing_color <= 3)
				{
					parsing_color = 3;
					continue;
				}
				else
					parsing_color = 5;
			}

			/* don't parse background color without a comma */
			else if (parsing_color == 3 && text[i - 1] != ',')
				parsing_color = 5;

			switch (parsing_color)
			{
			case 1:
				fg_color[0] = text[i];
				parsing_color++;
				fg_offset = 2;
				continue;
			case 2:
				fg_color[1] = text[i];
				parsing_color++;
				fg_offset++;
				continue;
			case 3:
				bg_color[0] = text[i];
				parsing_color++;
				bg_offset = 2 + fg_offset; /* 1 extra for , */
				continue;
			case 4:
				bg_color[1] = text[i];
				parsing_color++;
				bg_offset++;
				continue;
			case 5:
				if (bg_color[0] != 0)
				{
					insert_hiddenchar (entry, i - bg_offset, i);
					insert_color (entry, i, atoi (fg_color), atoi (bg_color));
				}
				else if (fg_color[0] != 0)
				{
					insert_hiddenchar (entry, i - fg_offset, i);
					insert_color (entry, i, atoi (fg_color), -1);
				}
				else
				{
					insert_hiddenchar (entry, i - 1, i);
					insert_color (entry, i, -1, -1);
				}

				memset (bg_color, 0, sizeof(bg_color));
				memset (fg_color, 0, sizeof(fg_color));
				parsing_color = 0;
				fg_offset = 0;
				continue;
			default:
				insert_hiddenchar (entry, i - 1, i);
				insert_color (entry, i, -1, -1);
			}
		}
	}
}

static void
sexy_spell_entry_recheck_all(SexySpellEntry *entry)
{
	GdkRectangle rect;
	GtkAllocation allocation;
	GtkWidget *widget = GTK_WIDGET(entry);
	PangoLayout *layout;
	int length, i, text_len;
	const char *text;

	/* Remove all existing pango attributes.  These will get readded as we check */
	pango_attr_list_unref(entry->priv->attr_list);
	entry->priv->attr_list = pango_attr_list_new();

	if (entry->priv->parseattr)
	{
		/* Check for attributes */
		text = gtk_entry_get_text (GTK_ENTRY (entry));
		text_len = gtk_entry_get_text_length (GTK_ENTRY (entry));
		check_attributes (entry, text, text_len);
	}

	if (have_enchant && entry->priv->checked
		&& g_slist_length (entry->priv->dict_list) != 0)
	{
		/* Loop through words */
		for (i = 0; entry->priv->words[i]; i++)
		{
			length = strlen (entry->priv->words[i]);
			if (length == 0)
				continue;
			check_word (entry, entry->priv->word_starts[i], entry->priv->word_ends[i]);
		}
	}

	layout = gtk_entry_get_layout(GTK_ENTRY(entry));
	pango_layout_set_attributes(layout, entry->priv->attr_list);

	if (gtk_widget_get_realized (GTK_WIDGET(entry)))
	{
		gtk_widget_get_allocation (GTK_WIDGET(entry), &allocation);
		
		rect.x = 0; rect.y = 0;
		rect.width  = allocation.width;
		rect.height = allocation.height;
		gdk_window_invalidate_rect(gtk_widget_get_window (widget), &rect, TRUE);
	}
}

static gint
sexy_spell_entry_expose(GtkWidget *widget, GdkEventExpose *event)
{
	SexySpellEntry *entry = SEXY_SPELL_ENTRY(widget);
	GtkEntry *gtk_entry = GTK_ENTRY(widget);
	PangoLayout *layout;

	
	layout = gtk_entry_get_layout(gtk_entry);
	pango_layout_set_attributes(layout, entry->priv->attr_list);

	return GTK_WIDGET_CLASS(parent_class)->expose_event (widget, event);
}

static gint
sexy_spell_entry_button_press(GtkWidget *widget, GdkEventButton *event)
{
	SexySpellEntry *entry = SEXY_SPELL_ENTRY(widget);
	GtkEntry *gtk_entry = GTK_ENTRY(widget);
	gint pos;

	pos = gtk_entry_find_position(gtk_entry, event->x);
	entry->priv->mark_character = pos;

	return GTK_WIDGET_CLASS(parent_class)->button_press_event (widget, event);
}

static gboolean
sexy_spell_entry_popup_menu(GtkWidget *widget, SexySpellEntry *entry)
{
	/* Menu popped up from a keybinding (menu key or <shift>+F10). Use
	 * the cursor position as the mark position */
	entry->priv->mark_character = gtk_editable_get_position (GTK_EDITABLE (entry));
	return FALSE;
}

static void
entry_strsplit_utf8(GtkEntry *entry, gchar ***set, gint **starts, gint **ends)
{
	PangoLayout   *layout;
	const PangoLogAttr  *log_attrs;
	const gchar   *text;
	gint           n_attrs, n_strings, i, j;

	layout = gtk_entry_get_layout(GTK_ENTRY(entry));
	text = gtk_entry_get_text(GTK_ENTRY(entry));
	log_attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);

	/* Find how many words we have */
	n_strings = 0;
	for (i = 0; i < n_attrs; i++)
		if (log_attrs[i].is_word_start)
			n_strings++;

	*set    = g_new0(gchar *, n_strings + 1);
	*starts = g_new0(gint, n_strings);
	*ends   = g_new0(gint, n_strings);

	/* Copy out strings */
	for (i = 0, j = 0; i < n_attrs; i++) {
		if (log_attrs[i].is_word_start) {
			gint cend, bytes;
			gchar *start;

			/* Find the end of this string */
			cend = i;
			while ((!log_attrs[cend].is_word_end || !log_attrs[cend].is_word_boundary)
					&& !log_attrs[cend].is_white)
				cend++;

			/* Copy sub-string */
			start = g_utf8_offset_to_pointer(text, i);
			bytes = (gint) (g_utf8_offset_to_pointer(text, cend) - start);
			(*set)[j]    = g_new0(gchar, bytes + 1);
			(*starts)[j] = (gint) (start - text);
			(*ends)[j]   = (gint) (start - text + bytes);
			g_utf8_strncpy((*set)[j], start, cend - i);

			/* Move on to the next word */
			j++;
		}
	}
}

static void
sexy_spell_entry_changed(GtkEditable *editable, gpointer data)
{
	SexySpellEntry *entry = SEXY_SPELL_ENTRY(editable);

	if (entry->priv->words)
	{
		g_strfreev(entry->priv->words);
		g_free(entry->priv->word_starts);
		g_free(entry->priv->word_ends);
	}
	entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends);
	sexy_spell_entry_recheck_all(entry);
}

static gboolean
enchant_has_lang(const gchar *lang, GSList *langs) {
	GSList *i;
	for (i = langs; i; i = g_slist_next(i))
	{
		if (strcmp(lang, i->data) == 0)
		{
			return TRUE;
		}
	}
	return FALSE;
}

/**
 * sexy_spell_entry_activate_default_languages:
 * @entry: A #SexySpellEntry.
 *
 * Activate spell checking for languages specified in the 
 * text_spell_langs setting. These languages are
 * activated by default, so this function need only be called
 * if they were previously deactivated.
 */
void
sexy_spell_entry_activate_default_languages(SexySpellEntry *entry)
{
	GSList *enchant_langs;
	char *lang, *langs;

	if (!have_enchant)
		return;

	if (!entry->priv->broker)
		entry->priv->broker = enchant_broker_init();

	enchant_langs = sexy_spell_entry_get_languages(entry);

	langs = g_strdup (prefs.hex_text_spell_langs);

	lang = strtok (langs, ",");
	while (lang != NULL)
	{
		if (enchant_has_lang (lang, enchant_langs))
		{
			sexy_spell_entry_activate_language_internal (entry, lang, NULL);
		}
		lang = strtok (NULL, ",");
	}

	g_slist_foreach(enchant_langs, (GFunc) g_free, NULL);
	g_slist_free(enchant_langs);
	g_free (langs);

	/* If we don't have any languages activated, use "en" */
	if (entry->priv->dict_list == NULL)
		sexy_spell_entry_activate_language_internal(entry, "en", NULL);

	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)
		return FALSE;

	if (!entry->priv->broker)
		entry->priv->broker = enchant_broker_init();

	if (g_hash_table_lookup(entry->priv->dict_hash, lang))
		return TRUE;

	dict = enchant_broker_request_dict(entry->priv->broker, lang);

	if (!dict) {
		g_set_error(error, SEXY_SPELL_ERROR, SEXY_SPELL_ERROR_BACKEND, _("enchant error for language: %s"), lang);
		return FALSE;
	}

	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);

	return TRUE;
}

static void
dict_describe_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_append(*langs, (gpointer)g_strdup(lang_tag));
}

/**
 * sexy_spell_entry_get_languages:
 * @entry: A #SexySpellEntry.
 *
 * Retrieve a list of language codes for which dictionaries are available.
 *
 * Returns: a new #GList object, or %NULL on error.
 */
GSList *
sexy_spell_entry_get_languages(const SexySpellEntry *entry)
{
	GSList *langs = NULL;

	g_return_val_if_fail(entry != NULL, NULL);
	g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), NULL);

	if (enchant_broker_list_dicts == NULL)
		return NULL;

	if (!entry->priv->broker)
		return NULL;

	enchant_broker_list_dicts(entry->priv->broker, dict_describe_cb, &langs);

	return langs;
}

/**
 * sexy_spell_entry_get_language_name:
 * @entry: A #SexySpellEntry.
 * @lang: The language code to lookup a friendly name for.
 *
 * Get a friendly name for a given locale.
 *
 * Returns: The name of the locale. Should be freed with g_free()
 */
gchar *
sexy_spell_entry_get_language_name(const SexySpellEntry *entry,
								   const gchar *lang)
{
#ifdef HAVE_ISO_CODES
	const gchar *lang_name = "";
	const gchar *country_name = "";

	g_return_val_if_fail (have_enchant, NULL);

	if (codetable_ref == 0)
		codetable_init ();
		
	codetable_lookup (lang, &lang_name, &country_name);

	if (codetable_ref == 0)
		codetable_free ();

	if (strlen (country_name) != 0)
		return g_strdup_printf ("%s (%s)", lang_name, country_name);
	else
		return g_strdup_printf ("%s", lang_name);
#else
	return g_strdup (lang);
#endif
}

/**
 * sexy_spell_entry_language_is_active:
 * @entry: A #SexySpellEntry.
 * @lang: The language to use, in a form enchant understands.
 *
 * Determine if a given language is currently active.
 *
 * Returns: TRUE if the language is active.
 */
gboolean
sexy_spell_entry_language_is_active(const SexySpellEntry *entry,
									const gchar *lang)
{
	return (g_hash_table_lookup(entry->priv->dict_hash, lang) != NULL);
}

/**
 * sexy_spell_entry_activate_language:
 * @entry: A #SexySpellEntry
 * @lang: The language to use in a form Enchant understands. Typically either
 *        a two letter language code or a locale code in the form xx_XX.
 * @error: Return location for error.
 *
 * Activate spell checking for the language specifed.
 *
 * Returns: FALSE if there was an error.
 */
gboolean
sexy_spell_entry_activate_language(SexySpellEntry *entry, const gchar *lang, GError **error)
{
	gboolean ret;

	g_return_val_if_fail(entry != NULL, FALSE);
	g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), FALSE);
	g_return_val_if_fail(lang != NULL && lang != '\0', FALSE);

	if (!have_enchant)
		return FALSE;

	if (error)
		g_return_val_if_fail(*error == NULL, FALSE);

	ret = sexy_spell_entry_activate_language_internal(entry, lang, error);

	if (ret) {
		if (entry->priv->words) {
			g_strfreev(entry->priv->words);
			g_free(entry->priv->word_starts);
			g_free(entry->priv->word_ends);
		}
		entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends);
		sexy_spell_entry_recheck_all(entry);
	}

	return ret;
}

/**
 * sexy_spell_entry_deactivate_language:
 * @entry: A #SexySpellEntry.
 * @lang: The language in a form Enchant understands. Typically either
 *        a two letter language code or a locale code in the form xx_XX.
 *
 * Deactivate spell checking for the language specifed.
 */
void
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)
		return;

	if (!entry->priv->dict_list)
		return;

	if (lang) {
		struct EnchantDict *dict;

		dict = g_hash_table_lookup(entry->priv->dict_hash, lang);
		if (!dict)
			return;
		enchant_broker_free_dict(entry->priv->broker, dict);
		entry->priv->dict_list = g_slist_remove(entry->priv->dict_list, dict);
		g_hash_table_remove (entry->priv->dict_hash, lang);
	} else {
		/* deactivate all */
		GSList *li;
		struct EnchantDict *dict;

		for (li = entry->priv->dict_list; li; li = g_slist_next(li)) {
			dict = (struct EnchantDict *)li->data;
			enchant_broker_free_dict(entry->priv->broker, dict);
		}

		g_slist_free (entry->priv->dict_list);
		g_hash_table_destroy (entry->priv->dict_hash);
		entry->priv->dict_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
		entry->priv->dict_list = NULL;
	}

	if (entry->priv->words) {
		g_strfreev(entry->priv->words);
		g_free(entry->priv->word_starts);
		g_free(entry->priv->word_ends);
	}
	entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends);
	sexy_spell_entry_recheck_all(entry);
}

/**
 * sexy_spell_entry_set_active_languages:
 * @entry: A #SexySpellEntry
 * @langs: A list of language codes to activate, in a form Enchant understands.
 *         Typically either a two letter language code or a locale code in the
 *         form xx_XX.
 * @error: Return location for error.
 *
 * Activate spell checking for only the languages specified.
 *
 * Returns: FALSE if there was an error.
 */
gboolean
sexy_spell_entry_set_active_languages(SexySpellEntry *entry, GSList *langs, GError **error)
{
	GSList *li;

	g_return_val_if_fail(entry != NULL, FALSE);
	g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), FALSE);
	g_return_val_if_fail(langs != NULL, FALSE);

	if (!have_enchant)
		return FALSE;

	/* deactivate all languages first */
	sexy_spell_entry_deactivate_language(entry, NULL);

	for (li = langs; li; li = g_slist_next(li)) {
		if (sexy_spell_entry_activate_language_internal(entry,
		    (const gchar *) li->data, error) == FALSE)
			return FALSE;
	}
	if (entry->priv->words) {
		g_strfreev(entry->priv->words);
		g_free(entry->priv->word_starts);
		g_free(entry->priv->word_ends);
	}
	entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends);
	sexy_spell_entry_recheck_all(entry);
	return TRUE;
}

/**
 * sexy_spell_entry_get_active_languages:
 * @entry: A #SexySpellEntry
 *
 * Retrieve a list of the currently active languages.
 *
 * Returns: A GSList of char* values with language codes (en, fr, etc).  Both
 *          the data and the list must be freed by the user.
 */
GSList *
sexy_spell_entry_get_active_languages(SexySpellEntry *entry)
{
	GSList *ret = NULL, *li;
	struct EnchantDict *dict;
	gchar *lang;

	g_return_val_if_fail(entry != NULL, NULL);
	g_return_val_if_fail(SEXY_IS_SPELL_ENTRY(entry), NULL);

	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);
		ret = g_slist_append(ret, lang);
	}
	return ret;
}

/**
 * sexy_spell_entry_is_checked:
 * @entry: A #SexySpellEntry.
 *
 * Queries a #SexySpellEntry and returns whether the entry has spell-checking enabled.
 *
 * Returns: TRUE if the entry has spell-checking enabled.
 */
gboolean
sexy_spell_entry_is_checked(SexySpellEntry *entry)
{
	return entry->priv->checked;
}

/**
 * sexy_spell_entry_set_checked:
 * @entry: A #SexySpellEntry.
 * @checked: Whether to enable spell-checking
 *
 * Sets whether the entry has spell-checking enabled.
 */
void
sexy_spell_entry_set_checked(SexySpellEntry *entry, gboolean checked)
{
	GtkWidget *widget;

	if (entry->priv->checked == checked)
		return;

	entry->priv->checked = checked;
	widget = GTK_WIDGET(entry);

	if (checked == FALSE && gtk_widget_get_realized (widget))
	{
		/* This will unmark any existing */
		sexy_spell_entry_recheck_all (entry);
	}
	else
	{
		if (entry->priv->words)
		{
			g_strfreev(entry->priv->words);
			g_free(entry->priv->word_starts);
			g_free(entry->priv->word_ends);
		}
		entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends);
		sexy_spell_entry_recheck_all(entry);
	}
}

/**
* sexy_spell_entry_set_parse_attributes:
* @entry: A #SexySpellEntry.
* @parse: Whether to enable showing attributes
*
* Sets whether to enable showing attributes is enabled.
*/
void
sexy_spell_entry_set_parse_attributes (SexySpellEntry *entry, gboolean parse)
{
	GtkWidget *widget;

	if (entry->priv->parseattr == parse)
		return;

	entry->priv->parseattr = parse;
	widget = GTK_WIDGET (entry);

	if (parse == FALSE && gtk_widget_get_realized (widget))
	{
		/* This will remove current attrs */
		sexy_spell_entry_recheck_all (entry);
	}
	else
	{
		if (entry->priv->words)
		{
			g_strfreev (entry->priv->words);
			g_free (entry->priv->word_starts);
			g_free (entry->priv->word_ends);
		}
		entry_strsplit_utf8 (GTK_ENTRY (entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends);
		sexy_spell_entry_recheck_all (entry);
	}
}
ass="kd">#: src/fe-gtk/servlistgui.c:1463 msgid "Connecting" msgstr "Ühendan" #: src/fe-gtk/servlistgui.c:1469 msgid "Auto connect to this network at startup" msgstr "Ühenda automaatselt programmi käivitamisel" #: src/fe-gtk/servlistgui.c:1471 msgid "Bypass proxy server" msgstr "" #: src/fe-gtk/servlistgui.c:1473 msgid "Use SSL for all the servers on this network" msgstr "Kasuta SSL ühendust kõigi selle võrgu serverite jaoks" #: src/fe-gtk/servlistgui.c:1478 msgid "Accept invalid SSL certificate" msgstr "Luba vigaseid SSL sertifikaate" #: src/fe-gtk/servlistgui.c:1484 msgid "_Favorite channels:" msgstr "" #: src/fe-gtk/servlistgui.c:1486 msgid "Channels to join, separated by commas, but not spaces!" msgstr "Kanalite nimekiri, millega soovid ühineda. Kanaleid eralda komaga, aga mitte tühikuga!" #: src/fe-gtk/servlistgui.c:1489 msgid "Connect command:" msgstr "Ühendamise käsk:" #: src/fe-gtk/servlistgui.c:1491 msgid "" "Extra command to execute after connecting. If you need more than one, set " "this to LOAD -e <filename>, where <filename> is a text-file full of commands" " to execute." msgstr "Lisakäsk, mis peale ühendumist käivitada. Kui on vaja rohkem kui ühte, siis kirjuta siia \"LOAD -e <failinimi>\", kus failinimi on vajalikke käske sisaldav fail." #: src/fe-gtk/servlistgui.c:1494 msgid "Nickserv password:" msgstr "Nickservi parool:" #: src/fe-gtk/servlistgui.c:1496 msgid "" "If your nickname requires a password, enter it here. Not all IRC networks " "support this." msgstr "Kui su hüüdnime kasutamiseks on vaja salasõna, kirjuta see siia. Kõik IRC võrgud ei toeta seda võimalust." #: src/fe-gtk/servlistgui.c:1500 msgid "Server password:" msgstr "Serveri parool:" #: src/fe-gtk/servlistgui.c:1502 msgid "Password for the server, if in doubt, leave blank." msgstr "Parool serveri jaoks. Kui ei tea mis on, jäta tühjaks." #: src/fe-gtk/servlistgui.c:1506 msgid "SASL password:" msgstr "" #: src/fe-gtk/servlistgui.c:1508 msgid "Password for SASL authentication, if in doubt, leave blank." msgstr "" #: src/fe-gtk/servlistgui.c:1511 msgid "Character set:" msgstr "Märgistik:" #: src/fe-gtk/servlistgui.c:1666 msgid ": Network List" msgstr "XChat: serverite nimekiri" #: src/fe-gtk/servlistgui.c:1678 msgid "User Information" msgstr "Kasutaja info" #: src/fe-gtk/servlistgui.c:1702 msgid "Third choice:" msgstr "Kolmas valik:" #: src/fe-gtk/servlistgui.c:1762 msgid "Networks" msgstr "Võrgud" #: src/fe-gtk/servlistgui.c:1810 msgid "Skip network list on startup" msgstr "Ära näita serverite nimekirja käivitumisel" #: src/fe-gtk/servlistgui.c:1819 msgid "Show favorites only" msgstr "" #: src/fe-gtk/servlistgui.c:1849 msgid "_Edit..." msgstr "_Muuda..." #: src/fe-gtk/servlistgui.c:1856 msgid "_Sort" msgstr "_Sorteeri" #: src/fe-gtk/servlistgui.c:1857 msgid "" "Sorts the network list in alphabetical order. Use SHIFT-UP and SHIFT-DOWN " "keys to move a row." msgstr "Sorteerib võrkude nimekirja tähestiku järgi. Kasuta SHIFT-ÜLES ja SHIFT-ALLA klahve järjekorra muutmiseks." #: src/fe-gtk/servlistgui.c:1865 msgid "_Favor" msgstr "" #: src/fe-gtk/servlistgui.c:1866 msgid "Mark or unmark this network as a favorite." msgstr "" #: src/fe-gtk/servlistgui.c:1890 msgid "C_onnect" msgstr "Ü_henda" #: src/fe-gtk/setup.c:105 msgid "Afrikaans" msgstr "" #: src/fe-gtk/setup.c:106 msgid "Albanian" msgstr "" #: src/fe-gtk/setup.c:107 msgid "Amharic" msgstr "" #: src/fe-gtk/setup.c:108 msgid "Asturian" msgstr "" #: src/fe-gtk/setup.c:109 msgid "Azerbaijani" msgstr "" #: src/fe-gtk/setup.c:110 msgid "Basque" msgstr "" #: src/fe-gtk/setup.c:111 msgid "Belarusian" msgstr "" #: src/fe-gtk/setup.c:112 msgid "Bulgarian" msgstr "" #: src/fe-gtk/setup.c:114 msgid "Chinese (Simplified)" msgstr "" #: src/fe-gtk/setup.c:115 msgid "Chinese (Traditional)" msgstr "" #: src/fe-gtk/setup.c:116 msgid "Czech" msgstr "" #: src/fe-gtk/setup.c:117 msgid "Danish" msgstr "" #: src/fe-gtk/setup.c:118 msgid "Dutch" msgstr "" #: src/fe-gtk/setup.c:119 msgid "English (Brisith)" msgstr "" #: src/fe-gtk/setup.c:120 msgid "English" msgstr "" #: src/fe-gtk/setup.c:121 msgid "Estonian" msgstr "" #: src/fe-gtk/setup.c:122 msgid "Finnish" msgstr "" #: src/fe-gtk/setup.c:123 msgid "French" msgstr "" #: src/fe-gtk/setup.c:124 msgid "Galician" msgstr "" #: src/fe-gtk/setup.c:125 msgid "German" msgstr "" #: src/fe-gtk/setup.c:126 msgid "Greek" msgstr "" #: src/fe-gtk/setup.c:127 msgid "Gujarati" msgstr "" #: src/fe-gtk/setup.c:128 msgid "Hindi" msgstr "" #: src/fe-gtk/setup.c:129 msgid "Hungarian" msgstr "" #: src/fe-gtk/setup.c:130 msgid "Indonesian" msgstr "" #: src/fe-gtk/setup.c:131 msgid "Italian" msgstr "" #: src/fe-gtk/setup.c:132 msgid "Japanese" msgstr "" #: src/fe-gtk/setup.c:133 msgid "Kannada" msgstr "" #: src/fe-gtk/setup.c:134 msgid "Kinyarwanda" msgstr "" #: src/fe-gtk/setup.c:135 msgid "Korean" msgstr "" #: src/fe-gtk/setup.c:136 msgid "Latvian" msgstr "" #: src/fe-gtk/setup.c:137 msgid "Lithuanian" msgstr "" #: src/fe-gtk/setup.c:138 msgid "Macedonian" msgstr "" #: src/fe-gtk/setup.c:139 msgid "Malay" msgstr "" #: src/fe-gtk/setup.c:140 msgid "Malayalam" msgstr "" #: src/fe-gtk/setup.c:141 msgid "Norwegian (Bokmal)" msgstr "" #: src/fe-gtk/setup.c:142 msgid "Norwegian (Nynorsk)" msgstr "" #: src/fe-gtk/setup.c:143 msgid "Polish" msgstr "" #: src/fe-gtk/setup.c:144 msgid "Portuguese" msgstr "" #: src/fe-gtk/setup.c:145 msgid "Portuguese (Brazilian)" msgstr "" #: src/fe-gtk/setup.c:146 msgid "Punjabi" msgstr "" #: src/fe-gtk/setup.c:147 msgid "Russian" msgstr "" #: src/fe-gtk/setup.c:148 msgid "Serbian" msgstr "" #: src/fe-gtk/setup.c:149 msgid "Slovak" msgstr "" #: src/fe-gtk/setup.c:150 msgid "Slovenian" msgstr "" #: src/fe-gtk/setup.c:151 msgid "Spanish" msgstr "" #: src/fe-gtk/setup.c:152 msgid "Swedish" msgstr "" #: src/fe-gtk/setup.c:153 msgid "Thai" msgstr "" #: src/fe-gtk/setup.c:154 msgid "Ukrainian" msgstr "" #: src/fe-gtk/setup.c:155 msgid "Vietnamese" msgstr "" #: src/fe-gtk/setup.c:156 msgid "Walloon" msgstr "" #: src/fe-gtk/setup.c:163 src/fe-gtk/setup.c:1899 msgid "General" msgstr "Üldine" #: src/fe-gtk/setup.c:165 msgid "Language:" msgstr "" #: src/fe-gtk/setup.c:166 msgid "Main font:" msgstr "" #: src/fe-gtk/setup.c:168 msgid "Font:" msgstr "Kirjatüüp:" #: src/fe-gtk/setup.c:171 msgid "Text Box" msgstr "" #: src/fe-gtk/setup.c:172 msgid "Background image:" msgstr "Taustapilt:" #: src/fe-gtk/setup.c:173 msgid "Colored nick names" msgstr "Värvilised nimed" #: src/fe-gtk/setup.c:174 msgid "Give each person on IRC a different color" msgstr "Värvi iga hüüdnimi eri värvi" #: src/fe-gtk/setup.c:175 msgid "Indent nick names" msgstr "Joonda nimed" #: src/fe-gtk/setup.c:176 msgid "Make nick names right-justified" msgstr "Joondab nimed paremale" #: src/fe-gtk/setup.c:178 msgid "Transparent background" msgstr "Läbipaistev taust" #: src/fe-gtk/setup.c:179 src/fe-gtk/setup.c:185 msgid "Show marker line" msgstr "Näita järjehoidjat" #: src/fe-gtk/setup.c:179 src/fe-gtk/setup.c:185 msgid "Insert a red line after the last read text." msgstr "Tõmba punane joon viimase loetud rea alla." #: src/fe-gtk/setup.c:180 msgid "Transparency Settings" msgstr "Läbipaistvuse seaded" #: src/fe-gtk/setup.c:181 msgid "Red:" msgstr "Punane:" #: src/fe-gtk/setup.c:182 msgid "Green:" msgstr "Roheline:" #: src/fe-gtk/setup.c:183 msgid "Blue:" msgstr "Sinine:" #: src/fe-gtk/setup.c:188 src/fe-gtk/setup.c:555 msgid "Time Stamps" msgstr "Ajamärgid" #: src/fe-gtk/setup.c:189 msgid "Enable time stamps" msgstr "Lülita ajamärgid sisse" #: src/fe-gtk/setup.c:190 msgid "Time stamp format:" msgstr "Kellaaja formaat:" #: src/fe-gtk/setup.c:192 src/fe-gtk/setup.c:559 msgid "See the strftime MSDN article for details." msgstr "" #: src/fe-gtk/setup.c:194 src/fe-gtk/setup.c:561 msgid "See the strftime manpage for details." msgstr "" #: src/fe-gtk/setup.c:197 msgid "Title Bar" msgstr "" #: src/fe-gtk/setup.c:198 msgid "Show channel modes" msgstr "" #: src/fe-gtk/setup.c:199 msgid "Show number of users" msgstr "" #: src/fe-gtk/setup.c:206 src/fe-gtk/setup.c:254 msgid "A-Z" msgstr "A-Ü" #: src/fe-gtk/setup.c:207 msgid "Last-spoke order" msgstr "Viimati rääkinud ees" #: src/fe-gtk/setup.c:213 msgid "Input Box" msgstr "" #: src/fe-gtk/setup.c:214 src/fe-gtk/setup.c:286 msgid "Use the Text box font and colors" msgstr "Kasuta tekstikasti kirjatüüpi" #: src/fe-gtk/setup.c:215 msgid "Show nick box" msgstr "" #: src/fe-gtk/setup.c:216 msgid "Show user mode icon in nick box" msgstr "" #: src/fe-gtk/setup.c:218 msgid "Spell checking" msgstr "Õigekirjakontroll" #: src/fe-gtk/setup.c:219 msgid "Dictionaries to use:" msgstr "" #: src/fe-gtk/setup.c:221 msgid "" "Use language codes (as in \"share\\myspell\\dicts\").\n" "Separate multiple entries with commas." msgstr "" #: src/fe-gtk/setup.c:223 msgid "Use language codes. Separate multiple entries with commas." msgstr "" #: src/fe-gtk/setup.c:227 msgid "Nick Completion" msgstr "Hüüdnime lõpetamine" #: src/fe-gtk/setup.c:228 msgid "Automatic nick completion (without TAB key)" msgstr "Automaatne hüüdnime lõpetamine (ilma TAB nuputa)" #: src/fe-gtk/setup.c:230 msgid "Nick completion suffix:" msgstr "Hüüdnime lõpetamise suffiks:" #: src/fe-gtk/setup.c:231 msgid "Nick completion sorted:" msgstr "Hüüdnimede järjekord:" #: src/fe-gtk/setup.c:234 msgid "Input Box Codes" msgstr "Sisestuskasti koodid" #: src/fe-gtk/setup.c:235 #, c-format msgid "Interpret %nnn as an ASCII value" msgstr "Tõlgenda %nnn kui ASCII koodi" #: src/fe-gtk/setup.c:236 msgid "Interpret %C, %B as Color, Bold etc" msgstr "Tõlgenda %C, %B jne. kui värviline, paks tekst jne.." #: src/fe-gtk/setup.c:245 msgid "Graphical" msgstr "" #: src/fe-gtk/setup.c:253 msgid "A-Z, Ops first" msgstr "A-Ü, opid ees" #: src/fe-gtk/setup.c:255 msgid "Z-A, Ops last" msgstr "Ü-A, opid taga" #: src/fe-gtk/setup.c:256 msgid "Z-A" msgstr "Ü-A" #: src/fe-gtk/setup.c:257 msgid "Unsorted" msgstr "Sorteerimata" #: src/fe-gtk/setup.c:263 src/fe-gtk/setup.c:275 msgid "Left (Upper)" msgstr "Vasakul (ülal)" #: src/fe-gtk/setup.c:264 src/fe-gtk/setup.c:276 msgid "Left (Lower)" msgstr "Vasakul (all)" #: src/fe-gtk/setup.c:265 src/fe-gtk/setup.c:277 msgid "Right (Upper)" msgstr "Paremal (ülal)" #: src/fe-gtk/setup.c:266 src/fe-gtk/setup.c:278 msgid "Right (Lower)" msgstr "Paremal (all)" #: src/fe-gtk/setup.c:267 msgid "Top" msgstr "Üleval" #: src/fe-gtk/setup.c:268 msgid "Bottom" msgstr "All" #: src/fe-gtk/setup.c:269 msgid "Hidden" msgstr "Peidus" #: src/fe-gtk/setup.c:284 msgid "User List" msgstr "Kasutajate nimekiri" #: src/fe-gtk/setup.c:285 msgid "Show hostnames in user list" msgstr "Näita kanali kasutajate nimekirjas liikmete hosti" #: src/fe-gtk/setup.c:287 msgid "Show icons for user modes" msgstr "" #: src/fe-gtk/setup.c:287 msgid "Use graphical icons instead of text symbols in the user list." msgstr "" #: src/fe-gtk/setup.c:288 msgid "Show user count in channels" msgstr "" #: src/fe-gtk/setup.c:290 msgid "User list sorted by:" msgstr "Kasutajate sorteerimise alus:" #: src/fe-gtk/setup.c:291 msgid "Show user list at:" msgstr "Näita kasutajate nimekirja:" #: src/fe-gtk/setup.c:293 msgid "Away Tracking" msgstr "" #: src/fe-gtk/setup.c:294 msgid "Track the Away status of users and mark them in a different color" msgstr "Jälgi kasutajate kohalolekut ja märgista eemalolijad" #: src/fe-gtk/setup.c:295 msgid "On channels smaller than:" msgstr "Kanalites, kus on vähem liikmeid kui:" #: src/fe-gtk/setup.c:297 msgid "Action Upon Double Click" msgstr "Topeltkliki tegevus" #: src/fe-gtk/setup.c:300 msgid "Extra Gadgets" msgstr "" #: src/fe-gtk/setup.c:301 msgid "Lag meter:" msgstr "" #: src/fe-gtk/setup.c:302 msgid "Throttle meter:" msgstr "" #: src/fe-gtk/setup.c:309 msgid "Windows" msgstr "Aknad" #: src/fe-gtk/setup.c:310 src/fe-gtk/setup.c:324 msgid "Tabs" msgstr "Sakid" #: src/fe-gtk/setup.c:317 msgid "Always" msgstr "Alati" #: src/fe-gtk/setup.c:318 msgid "Only requested tabs" msgstr "Ainult ise loodud sakid" #: src/fe-gtk/setup.c:326 msgid "Tree" msgstr "" #: src/fe-gtk/setup.c:333 msgid "Switcher type:" msgstr "" #: src/fe-gtk/setup.c:334 msgid "Open an extra tab for server messages" msgstr "Ava sakk serveri sõnumite jaoks" #: src/fe-gtk/setup.c:335 msgid "Open an extra tab for server notices" msgstr "Ava sakk serveri teadete jaoks" #: src/fe-gtk/setup.c:336 msgid "Open a new tab when you receive a private message" msgstr "Ava uus sakk privaatsõnumi saabumisel" #: src/fe-gtk/setup.c:337 msgid "Sort tabs in alphabetical order" msgstr "Sorteeri sakid tähestiku järgi" #: src/fe-gtk/setup.c:338 msgid "Show icons in the channel tree" msgstr "" #: src/fe-gtk/setup.c:339 msgid "Show dotted lines in the channel tree" msgstr "" #: src/fe-gtk/setup.c:340 msgid "Smaller text" msgstr "" #: src/fe-gtk/setup.c:341 msgid "Focus new tabs:" msgstr "Fookus uuele sakile:" #: src/fe-gtk/setup.c:342 msgid "Show channel switcher at:" msgstr "Näita kanalite valikut:" #: src/fe-gtk/setup.c:343 msgid "Shorten tab labels to:" msgstr "Lühenda sakkide nimed:" #: src/fe-gtk/setup.c:343 msgid "letters." msgstr "täheni." #: src/fe-gtk/setup.c:345 msgid "Tabs or Windows" msgstr "Sakid või aknad" #: src/fe-gtk/setup.c:346 msgid "Open channels in:" msgstr "Ava kanalid:" #: src/fe-gtk/setup.c:347 msgid "Open dialogs in:" msgstr "Ava dialoogid:" #: src/fe-gtk/setup.c:348 msgid "Open utilities in:" msgstr "Ava utiliidid:" #: src/fe-gtk/setup.c:348 msgid "Open DCC, Ignore, Notify etc, in tabs or windows?" msgstr "Ava DCC, ignoreerimised, märguanded jne. sakkides või akendes?" #: src/fe-gtk/setup.c:355 msgid "Messages" msgstr "" #: src/fe-gtk/setup.c:356 msgid "Scrollback" msgstr "" #: src/fe-gtk/setup.c:364 msgid "Ask for confirmation" msgstr "" #: src/fe-gtk/setup.c:365 msgid "Ask for download folder" msgstr "" #: src/fe-gtk/setup.c:366 msgid "Save without interaction" msgstr "" #: src/fe-gtk/setup.c:372 msgid "Files and Directories" msgstr "Failid ja kataloogid" #: src/fe-gtk/setup.c:373 msgid "Auto accept file offers:" msgstr "Võta küsimata vastu DCC failisaatmised" #: src/fe-gtk/setup.c:374 msgid "Download files to:" msgstr "Allalaadimisel failid salvesta:" #: src/fe-gtk/setup.c:375 msgid "Move completed files to:" msgstr "Alla laetud failid tõsta ümber:" #: src/fe-gtk/setup.c:376 msgid "Save nick name in filenames" msgstr "Salvesta hüüdnimi failinimedesse" #: src/fe-gtk/setup.c:378 msgid "Network Settings" msgstr "Võrguseaded" #: src/fe-gtk/setup.c:379 msgid "Get my address from the IRC server" msgstr "Võta mu IP aadress IRC serverilt" #: src/fe-gtk/setup.c:380 msgid "" "Asks the IRC server for your real address. Use this if you have a " "192.168.*.* address!" msgstr "Küsi serverilt oma tõeline aadressi. Kasuta seda juhul, kui sul on 192.168.*.* aadress!" #: src/fe-gtk/setup.c:381 msgid "DCC IP address:" msgstr "DCC IP aadress:" #: src/fe-gtk/setup.c:382 msgid "Claim you are at this address when offering files." msgstr "Väida, failide pakkumisel, et sul on see aadress." #: src/fe-gtk/setup.c:383 msgid "First DCC send port:" msgstr "Esimene port DCC saatmisteks:" #: src/fe-gtk/setup.c:384 msgid "Last DCC send port:" msgstr "Viimane port DCC saatmisteks:" #: src/fe-gtk/setup.c:385 msgid "!Leave ports at zero for full range." msgstr "!0, et kasutada kõiki porte." #: src/fe-gtk/setup.c:387 msgid "Maximum File Transfer Speeds (bytes per second)" msgstr "Maksimaalne failivahetuskiirus (baite sekundis)" #: src/fe-gtk/setup.c:388 msgid "One upload:" msgstr "Üks üleslaadimine:" #: src/fe-gtk/setup.c:389 src/fe-gtk/setup.c:391 msgid "Maximum speed for one transfer" msgstr "Ühe edastamise maksimaalne kiirus" #: src/fe-gtk/setup.c:390 msgid "One download:" msgstr "Üks allalaadimine:" #: src/fe-gtk/setup.c:392 msgid "All uploads combined:" msgstr "Kõik üleslaadimised kokku:" #: src/fe-gtk/setup.c:393 src/fe-gtk/setup.c:395 msgid "Maximum speed for all files" msgstr "Maksimaalne kiirus kogu liikluse jaoks" #: src/fe-gtk/setup.c:394 msgid "All downloads combined:" msgstr "Kõik allalaadimised kokku:" #: src/fe-gtk/setup.c:422 src/fe-gtk/setup.c:448 src/fe-gtk/setup.c:1898 msgid "Alerts" msgstr "Hoiatused" #: src/fe-gtk/setup.c:426 msgid "Show tray balloons on:" msgstr "Näita süsteemisalve mullikesi:" #: src/fe-gtk/setup.c:428 msgid "Blink tray icon on:" msgstr "" #: src/fe-gtk/setup.c:429 src/fe-gtk/setup.c:451 msgid "Blink task bar on:" msgstr "" #: src/fe-gtk/setup.c:430 src/fe-gtk/setup.c:452 msgid "Make a beep sound on:" msgstr "Tee piiks:" #: src/fe-gtk/setup.c:432 msgid "Enable system tray icon" msgstr "Süsteemisalve ikoon" #: src/fe-gtk/setup.c:433 src/fe-gtk/setup.c:454 msgid "Omit alerts when marked as being away" msgstr "" #: src/fe-gtk/setup.c:435 src/fe-gtk/setup.c:456 msgid "Highlighted Messages" msgstr "Esiletõstetud sõnumid:" #: src/fe-gtk/setup.c:436 src/fe-gtk/setup.c:457 msgid "" "Highlighted messages are ones where your nickname is mentioned, but also:" msgstr "Peale enda hüüdnime mainimiste tõsta esile veel:" #: src/fe-gtk/setup.c:438 src/fe-gtk/setup.c:459 msgid "Extra words to highlight:" msgstr "Esiletõstetavad sõnad:" #: src/fe-gtk/setup.c:439 src/fe-gtk/setup.c:460 msgid "Nick names not to highlight:" msgstr "Hüüdnimed, mida mitte esile tõsta:" #: src/fe-gtk/setup.c:440 src/fe-gtk/setup.c:461 msgid "Nick names to always highlight:" msgstr "Hüüdnimed mida alati esile tõsta:" #: src/fe-gtk/setup.c:441 src/fe-gtk/setup.c:462 msgid "" "Separate multiple words with commas.\n" "Wildcards are accepted." msgstr "" #: src/fe-gtk/setup.c:469 msgid "Default Messages" msgstr "Vaikimisi põhjendused:" #: src/fe-gtk/setup.c:470 msgid "Quit:" msgstr "Väljumine:" #: src/fe-gtk/setup.c:471 msgid "Leave channel:" msgstr "Kanalist lahkumine:" #: src/fe-gtk/setup.c:472 msgid "Away:" msgstr "Eemale minek:" #: src/fe-gtk/setup.c:474 msgid "Away" msgstr "Eemal" #: src/fe-gtk/setup.c:475 msgid "Announce away messages" msgstr "Teata eemalolekust" #: src/fe-gtk/setup.c:476 msgid "Announce your away messages to all channels." msgstr "" #: src/fe-gtk/setup.c:477 msgid "Show away once" msgstr "Näita ainult ühe korra" #: src/fe-gtk/setup.c:477 msgid "Show identical away messages only once." msgstr "" #: src/fe-gtk/setup.c:478 msgid "Automatically unmark away" msgstr "Võta eemalolek automaatselt maha" #: src/fe-gtk/setup.c:478 msgid "Unmark yourself as away before sending messages." msgstr "" #: src/fe-gtk/setup.c:485 src/fe-gtk/setup.c:517 msgid "Advanced Settings" msgstr "Põhjalikumad seadistused" #: src/fe-gtk/setup.c:487 src/fe-gtk/setup.c:518 msgid "Alternative fonts:" msgstr "" #: src/fe-gtk/setup.c:489 src/fe-gtk/setup.c:519 msgid "Auto reconnect delay:" msgstr "Ooteaeg enne autoühendust:" #: src/fe-gtk/setup.c:490 src/fe-gtk/setup.c:520 msgid "Display MODEs in raw form" msgstr "" #: src/fe-gtk/setup.c:491 src/fe-gtk/setup.c:521 msgid "Whois on notify" msgstr "" #: src/fe-gtk/setup.c:491 src/fe-gtk/setup.c:521 msgid "Sends a /WHOIS when a user comes online in your notify list." msgstr "" #: src/fe-gtk/setup.c:492 src/fe-gtk/setup.c:522 msgid "Hide join and part messages" msgstr "Peida tulijad ja lahkujad" #: src/fe-gtk/setup.c:492 src/fe-gtk/setup.c:522 msgid "Hide channel join/part messages by default." msgstr "" #: src/fe-gtk/setup.c:493 src/fe-gtk/setup.c:524 msgid "Display lists in compact mode" msgstr "" #: src/fe-gtk/setup.c:493 src/fe-gtk/setup.c:524 msgid "Use less spacing between user list/channel tree rows." msgstr "" #: src/fe-gtk/setup.c:494 src/fe-gtk/setup.c:525 msgid "Auto Open DCC Windows" msgstr "Ava DCC aknad automaatselt" #: src/fe-gtk/setup.c:495 src/fe-gtk/setup.c:526 msgid "Send window" msgstr "Saatmisaken" #: src/fe-gtk/setup.c:496 src/fe-gtk/setup.c:527 msgid "Receive window" msgstr "Vastuvõtmiste aken" #: src/fe-gtk/setup.c:497 src/fe-gtk/setup.c:528 msgid "Chat window" msgstr "Vestlusaken" #: src/fe-gtk/setup.c:498 src/fe-gtk/setup.c:529 msgid "Auto Copy Behavior" msgstr "" #: src/fe-gtk/setup.c:499 src/fe-gtk/setup.c:530 msgid "Automatically copy selected text" msgstr "" #: src/fe-gtk/setup.c:500 src/fe-gtk/setup.c:531 msgid "" "Copy selected text to clipboard when left mouse button is released. " "Otherwise, CONTROL-SHIFT-C will copy the selected text to the clipboard." msgstr "" #: src/fe-gtk/setup.c:503 src/fe-gtk/setup.c:534 msgid "Automatically include time stamps" msgstr "" #: src/fe-gtk/setup.c:504 src/fe-gtk/setup.c:535 msgid "" "Automatically include time stamps in copied lines of text. Otherwise, " "include time stamps if the SHIFT key is held down while selecting." msgstr "" #: src/fe-gtk/setup.c:506 src/fe-gtk/setup.c:537 msgid "Automatically include color information" msgstr "" #: src/fe-gtk/setup.c:507 src/fe-gtk/setup.c:538 msgid "" "Automatically include color information in copied lines of text. Otherwise," " include color information if the CONTROL key is held down while selecting." msgstr "" #: src/fe-gtk/setup.c:523 msgid "Allow only one instance of HexChat to run" msgstr "" #: src/fe-gtk/setup.c:548 src/fe-gtk/setup.c:1900 msgid "Logging" msgstr "Logimine" #: src/fe-gtk/setup.c:549 msgid "Display scrollback from previous session" msgstr "" #: src/fe-gtk/setup.c:550 msgid "Scrollback lines:" msgstr "Tagasikeritavate ridade arv:" #: src/fe-gtk/setup.c:551 msgid "Enable logging of conversations to disk" msgstr "" #: src/fe-gtk/setup.c:552 msgid "Log filename:" msgstr "Logi failinimi:" #: src/fe-gtk/setup.c:553 #, c-format msgid "%s=Server %c=Channel %n=Network." msgstr "%s=server %c=kanal %n=võrk." #: src/fe-gtk/setup.c:556 msgid "Insert timestamps in logs" msgstr "Lisa logidesse kellaaeg" #: src/fe-gtk/setup.c:557 msgid "Log timestamp format:" msgstr "Logi kellaaja formaat:" #: src/fe-gtk/setup.c:564 msgid "URLs" msgstr "" #: src/fe-gtk/setup.c:565 msgid "Enable logging of URLs to disk" msgstr "" #: src/fe-gtk/setup.c:566 msgid "Enable URL grabber" msgstr "" #: src/fe-gtk/setup.c:567 msgid "Maximum number of URLs to grab:" msgstr "" #: src/fe-gtk/setup.c:574 msgid "(Disabled)" msgstr "(Keelatud)" #: src/fe-gtk/setup.c:575 msgid "Wingate" msgstr "Wingate" #: src/fe-gtk/setup.c:576 msgid "Socks4" msgstr "Socks4" #: src/fe-gtk/setup.c:577 msgid "Socks5" msgstr "Socks5" #: src/fe-gtk/setup.c:578 msgid "HTTP" msgstr "HTTP" #: src/fe-gtk/setup.c:580 msgid "MS Proxy (ISA)" msgstr "" #: src/fe-gtk/setup.c:583 msgid "Auto" msgstr "" #: src/fe-gtk/setup.c:590 msgid "All Connections" msgstr "Kõik ühendused" #: src/fe-gtk/setup.c:591 msgid "IRC Server Only" msgstr "Ainult IRC serveriga" #: src/fe-gtk/setup.c:592 msgid "DCC Get Only" msgstr "Ainult DCC vastuvõtt" #: src/fe-gtk/setup.c:598 msgid "Your Address" msgstr "Sinu IP aadress" #: src/fe-gtk/setup.c:599 msgid "Bind to:" msgstr "Seo aadressiga:" #: src/fe-gtk/setup.c:600 msgid "Only useful for computers with multiple addresses." msgstr "Kasulik ainult arvutitele, millel on mitu aadressi." #: src/fe-gtk/setup.c:602 msgid "Proxy Server" msgstr "Proksi server:" #: src/fe-gtk/setup.c:603 msgid "Hostname:" msgstr "Hostinimi:" #: src/fe-gtk/setup.c:604 msgid "Port:" msgstr "Port:" #: src/fe-gtk/setup.c:605 msgid "Type:" msgstr "Tüüp:" #: src/fe-gtk/setup.c:606 msgid "Use proxy for:" msgstr "Kasuta vaheserverit:" #: src/fe-gtk/setup.c:608 msgid "Proxy Authentication" msgstr "Proksiga autentimine" #: src/fe-gtk/setup.c:610 msgid "Use Authentication (MS Proxy, HTTP or Socks5 only)" msgstr "Kasuta autentimist (MS proksi, HTTP või Socks5)" #: src/fe-gtk/setup.c:612 msgid "Use Authentication (HTTP or Socks5 only)" msgstr "Kasuta autentimist (ainult HTTP või Socks5)" #: src/fe-gtk/setup.c:614 msgid "Username:" msgstr "Kasutajanimi:" #: src/fe-gtk/setup.c:615 msgid "Password:" msgstr "Parool:" #: src/fe-gtk/setup.c:1046 msgid "Select an Image File" msgstr "Vali pilti" #: src/fe-gtk/setup.c:1081 msgid "Select Download Folder" msgstr "Vali tõmmatud failide kaust" #: src/fe-gtk/setup.c:1090 msgid "Select font" msgstr "Vali kirjatüüp" #: src/fe-gtk/setup.c:1190 msgid "Browse..." msgstr "Vali..." #: src/fe-gtk/setup.c:1328 msgid "Mark identified users with:" msgstr "Märgista identifitseeritud kasutajad:" #: src/fe-gtk/setup.c:1330 msgid "Mark not-identified users with:" msgstr "Märgista identifitseerimata kasutajad:" #: src/fe-gtk/setup.c:1337 msgid "Open Data Folder" msgstr "Ava andmete kataloog" #: src/fe-gtk/setup.c:1391 msgid "Select color" msgstr "Vali värv" #: src/fe-gtk/setup.c:1471 msgid "Text Colors" msgstr "Teksti värvid" #: src/fe-gtk/setup.c:1473 msgid "mIRC colors:" msgstr "mIRC värvid:" #: src/fe-gtk/setup.c:1481 msgid "Local colors:" msgstr "Kohalikud värvid:" #: src/fe-gtk/setup.c:1489 src/fe-gtk/setup.c:1494 msgid "Foreground:" msgstr "Tekst:" #: src/fe-gtk/setup.c:1490 src/fe-gtk/setup.c:1495 msgid "Background:" msgstr "Taust:" #: src/fe-gtk/setup.c:1492 msgid "Selected Text" msgstr "" #: src/fe-gtk/setup.c:1497 msgid "Interface Colors" msgstr "Kasutajaliidese värvid" #: src/fe-gtk/setup.c:1499 msgid "New data:" msgstr "Uued andmed:" #: src/fe-gtk/setup.c:1500 msgid "Marker line:" msgstr "Järjehoidja joon:" #: src/fe-gtk/setup.c:1501 msgid "New message:" msgstr "Uus sõnum:" #: src/fe-gtk/setup.c:1502 msgid "Away user:" msgstr "Eemal olev kasutaja:" #: src/fe-gtk/setup.c:1503 msgid "Highlight:" msgstr "Esiletõstetud:" #: src/fe-gtk/setup.c:1505 msgid "Spell checker:" msgstr "" #: src/fe-gtk/setup.c:1508 msgid "Color Stripping" msgstr "" #: src/fe-gtk/setup.c:1614 src/fe-gtk/textgui.c:389 msgid "Event" msgstr "Sündmus" #: src/fe-gtk/setup.c:1620 msgid "Sound file" msgstr "Helifail:" #: src/fe-gtk/setup.c:1655 msgid "Select a sound file" msgstr "Vali helifail" #: src/fe-gtk/setup.c:1727 msgid "Sound playing method:" msgstr "Heli mängimise meetod:" #: src/fe-gtk/setup.c:1735 msgid "External sound playing _program:" msgstr "Väline _helipleier:" #: src/fe-gtk/setup.c:1753 msgid "_External program" msgstr "_Väline programm" #: src/fe-gtk/setup.c:1763 msgid "_Automatic" msgstr "_Automaatne" #: src/fe-gtk/setup.c:1776 msgid "Sound files _directory:" msgstr "Helifailide kataloog:" #: src/fe-gtk/setup.c:1815 msgid "Sound file:" msgstr "Helifail: " #: src/fe-gtk/setup.c:1830 msgid "_Browse..." msgstr "_Vali..." #: src/fe-gtk/setup.c:1841 msgid "_Play" msgstr "_Mängi" #: src/fe-gtk/setup.c:1890 msgid "Interface" msgstr "Kasutajaliides" #: src/fe-gtk/setup.c:1891 msgid "Appearance" msgstr "" #: src/fe-gtk/setup.c:1892 msgid "Input box" msgstr "Sisestuskast" #: src/fe-gtk/setup.c:1893 msgid "User list" msgstr "Kasutajate nimekiri" #: src/fe-gtk/setup.c:1894 msgid "Channel switcher" msgstr "Kanalite vahetaja" #: src/fe-gtk/setup.c:1895 msgid "Colors" msgstr "Värvid" #: src/fe-gtk/setup.c:1897 msgid "Chatting" msgstr "Vestlemine" #: src/fe-gtk/setup.c:1901 msgid "Sound" msgstr "Heli" #: src/fe-gtk/setup.c:1902 msgid "Advanced" msgstr "Täpsemad" #: src/fe-gtk/setup.c:1905 msgid "Network setup" msgstr "Võrgu häälestus" #: src/fe-gtk/setup.c:1906 msgid "File transfers" msgstr "Failide edastus" #: src/fe-gtk/setup.c:2037 msgid "Categories" msgstr "Kategooriad" #: src/fe-gtk/setup.c:2246 msgid "" "You cannot place the tree on the top or bottom!\n" "Please change to the <b>Tabs</b> layout in the <b>View</b> menu first." msgstr "Puud ei saa paigutada akna ülemisse või alumisse äärde!\nPalun vali <b>Vaade</b> menüüst <b>Sakid</b>, kui seda teha soovid." #: src/fe-gtk/setup.c:2273 msgid "Invalid time stamp format! See the strftime MSDN article for details." msgstr "" #: src/fe-gtk/setup.c:2280 msgid "Some settings were changed that require a restart to take full effect." msgstr "Muudeti mõnesid seadeid, mis vajavad programmi taaskäivitust mõjumiseks." #: src/fe-gtk/setup.c:2288 msgid "" "*WARNING*\n" "Auto accepting DCC to your home directory\n" "can be dangerous and is exploitable. Eg:\n" "Someone could send you a .bash_profile" msgstr "*HOIATUS*\nDCC failide automaatne kodukataloogi salvestamine\non ohtlik ja ära kasutatav. Näiteks võin keegi\nsaata sulle faili .bash_profile" #: src/fe-gtk/setup.c:2321 msgid ": Preferences" msgstr "X-Chat: häälestus" #: src/fe-gtk/sexy-spell-entry.c:465 msgid "<i>(no suggestions)</i>" msgstr "" #: src/fe-gtk/sexy-spell-entry.c:479 msgid "More..." msgstr "" #: src/fe-gtk/sexy-spell-entry.c:548 #, c-format msgid "Add \"%s\" to Dictionary" msgstr "" #: src/fe-gtk/sexy-spell-entry.c:590 msgid "Ignore All" msgstr "" #: src/fe-gtk/sexy-spell-entry.c:625 msgid "Spelling Suggestions" msgstr "" #: src/fe-gtk/sexy-spell-entry.c:1071 #, c-format msgid "enchant error for language: %s" msgstr "" #: src/fe-gtk/textgui.c:180 msgid "There was an error parsing the string" msgstr "Viga stringi töötlemisel" #: src/fe-gtk/textgui.c:188 #, c-format msgid "This signal is only passed %d args, $%d is invalid" msgstr "" #: src/fe-gtk/textgui.c:304 src/fe-gtk/textgui.c:327 msgid "Print Texts File" msgstr "" #: src/fe-gtk/textgui.c:372 msgid "Edit Events" msgstr "Muuda sündmuseid" #: src/fe-gtk/textgui.c:423 msgid "$ Number" msgstr "$ number" #: src/fe-gtk/textgui.c:440 msgid "Load From..." msgstr "Lae..." #: src/fe-gtk/textgui.c:441 msgid "Test All" msgstr "Testi kõike" #: src/fe-gtk/urlgrab.c:97 msgid "URL" msgstr "URL" #: src/fe-gtk/urlgrab.c:199 msgid ": URL Grabber" msgstr "X-Chat: URLipüüdja" #: src/fe-gtk/urlgrab.c:212 msgid "Clear list" msgstr "Puhasta nimekiri" #: src/fe-gtk/urlgrab.c:214 msgid "Copy selected URL" msgstr "Kopeeri valitud URL" #: src/fe-gtk/urlgrab.c:214 msgid "Copy" msgstr "Kopeeri" #: src/fe-gtk/urlgrab.c:216 msgid "Save list to a file" msgstr "Salvesta nimekiri faili" #: src/fe-gtk/userlistgui.c:121 #, c-format msgid "%d ops, %d total" msgstr "%d op'i, %d kokku"