diff pidgin/plugins/spellchk.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children d75099d2567e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/spellchk.c	Sat Jan 20 02:32:10 2007 +0000
@@ -0,0 +1,2393 @@
+/*
+ * Gaim - Replace certain misspelled words with their correct form.
+ *
+ * Signification changes were made by Benjamin Kahn ("xkahn") and
+ * Richard Laager ("rlaager") in April 2005--you may want to contact
+ * them if you have questions.
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * A lot of this code (especially the config code) was taken directly
+ * or nearly directly from xchat, version 1.4.2 by Peter Zelezny and others.
+ */
+
+#include "internal.h"
+#include "gtkgaim.h"
+
+#include "debug.h"
+#include "notify.h"
+#include "signals.h"
+#include "util.h"
+#include "version.h"
+
+#include "gtkplugin.h"
+#include "gtkprefs.h"
+#include "gtkutils.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32
+#include <strings.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define SPELLCHECK_PLUGIN_ID "gtk-spellcheck"
+#define SPELLCHK_OBJECT_KEY "spellchk"
+
+enum {
+	BAD_COLUMN,
+	GOOD_COLUMN,
+	WORD_ONLY_COLUMN,
+	CASE_SENSITIVE_COLUMN,
+	N_COLUMNS
+};
+
+struct _spellchk {
+	GtkTextView *view;
+	GtkTextMark *mark_insert_start;
+	GtkTextMark *mark_insert_end;
+
+	const gchar *word;
+	gboolean inserting;
+	gboolean ignore_correction;
+	gboolean ignore_correction_on_send;
+	gint pos;
+};
+
+typedef struct _spellchk spellchk;
+
+static GtkListStore *model;
+
+static gboolean
+is_word_uppercase(const gchar *word)
+{
+	for (; word[0] != '\0'; word = g_utf8_find_next_char (word, NULL)) {
+		gunichar c = g_utf8_get_char(word);
+
+		if (!(g_unichar_isupper(c) ||
+		      g_unichar_ispunct(c) ||
+		      g_unichar_isspace(c)))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+is_word_lowercase(const gchar *word)
+{
+	for (; word[0] != '\0'; word = g_utf8_find_next_char(word, NULL)) {
+		gunichar c = g_utf8_get_char(word);
+
+		if (!(g_unichar_islower(c) ||
+		      g_unichar_ispunct(c) ||
+		      g_unichar_isspace(c)))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+is_word_proper(const gchar *word)
+{
+	if (word[0] == '\0')
+		return FALSE;
+
+	if (!g_unichar_isupper(g_utf8_get_char_validated(word, -1)))
+		return FALSE;
+
+	return is_word_lowercase(g_utf8_offset_to_pointer(word, 1));
+}
+
+static gchar *
+make_word_proper(const gchar *word)
+{
+	char buf[7];
+	gchar *lower = g_utf8_strdown(word, -1);
+	gint bytes;
+	gchar *ret;
+
+	bytes = g_unichar_to_utf8(g_unichar_toupper(g_utf8_get_char(word)), buf);
+	buf[MIN(bytes, sizeof(buf) - 1)] = '\0';
+
+	ret = g_strconcat(buf, g_utf8_offset_to_pointer(lower, 1), NULL);
+	g_free(lower);
+
+	return ret;
+}
+
+static gboolean
+substitute_simple_buffer(GtkTextBuffer *buffer)
+{
+	GtkTextIter start;
+	GtkTextIter end;
+	GtkTreeIter treeiter;
+	gchar *text = NULL;
+
+	gtk_text_buffer_get_iter_at_offset(buffer, &start, 0);
+	gtk_text_buffer_get_iter_at_offset(buffer, &end, 0);
+	gtk_text_iter_forward_to_end(&end);
+
+	text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
+
+	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &treeiter) && text) {
+		do {
+			GValue val1;
+			const gchar *bad;
+			gchar *cursor;
+			glong char_pos;
+
+			val1.g_type = 0;
+			gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, WORD_ONLY_COLUMN, &val1);
+			if (g_value_get_boolean(&val1))
+			{
+				g_value_unset(&val1);
+				continue;
+			}
+			g_value_unset(&val1);
+
+			gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, BAD_COLUMN, &val1);
+			bad = g_value_get_string(&val1);
+
+			/* using g_utf8_* to get /character/ offsets instead of byte offsets for buffer */
+			if ((cursor = g_strrstr(text, bad)))
+			{
+				GValue val2;
+				const gchar *good;
+
+				val2.g_type = 0;
+				gtk_tree_model_get_value(GTK_TREE_MODEL(model), &treeiter, GOOD_COLUMN, &val2);
+				good = g_value_get_string(&val2);
+
+				char_pos = g_utf8_pointer_to_offset(text, cursor);
+				gtk_text_buffer_get_iter_at_offset(buffer, &start, char_pos);
+				gtk_text_buffer_get_iter_at_offset(buffer, &end, char_pos + g_utf8_strlen(bad, -1));
+				gtk_text_buffer_delete(buffer, &start, &end);
+
+				gtk_text_buffer_get_iter_at_offset(buffer, &start, char_pos);
+				gtk_text_buffer_insert(buffer, &start, good, -1);
+
+				g_value_unset(&val2);
+				g_free(text);
+
+				g_value_unset(&val1);
+				return TRUE;
+			}
+
+			g_value_unset(&val1);
+		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &treeiter));
+	}
+
+	g_free(text);
+	return FALSE;
+}
+
+static gchar *
+substitute_word(gchar *word)
+{
+	GtkTreeIter iter;
+	gchar *outword;
+	gchar *lowerword;
+	gchar *foldedword;
+
+	if (word == NULL)
+		return NULL;
+
+	lowerword = g_utf8_strdown(word, -1);
+	foldedword = g_utf8_casefold(word, -1);
+
+	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) {
+		do {
+			GValue val1;
+			gboolean case_sensitive;
+			const char *bad;
+			gchar *tmpbad = NULL;
+
+			val1.g_type = 0;
+			gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, WORD_ONLY_COLUMN, &val1);
+			if (!g_value_get_boolean(&val1)) {
+				g_value_unset(&val1);
+				continue;
+			}
+			g_value_unset(&val1);
+
+			gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, CASE_SENSITIVE_COLUMN, &val1);
+			case_sensitive = g_value_get_boolean(&val1);
+			g_value_unset(&val1);
+
+			gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, BAD_COLUMN, &val1);
+			bad = g_value_get_string(&val1);
+
+			if ((case_sensitive && !strcmp(bad, word)) ||
+			    (!case_sensitive && (!strcmp(bad, lowerword) ||
+			                        (!is_word_lowercase(bad) &&
+			                         !strcmp((tmpbad = g_utf8_casefold(bad, -1)), foldedword)))))
+			{
+				GValue val2;
+				const char *good;
+
+				g_free(tmpbad);
+
+				val2.g_type = 0;
+				gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, GOOD_COLUMN, &val2);
+				good = g_value_get_string(&val2);
+
+				if (!case_sensitive && is_word_lowercase(bad) && is_word_lowercase(good))
+				{
+					if (is_word_uppercase(word))
+						outword = g_utf8_strup(good, -1);
+					else if (is_word_proper(word))
+						outword = make_word_proper(good);
+					else
+						outword = g_strdup(good);
+				}
+				else
+					outword = g_strdup(good);
+
+				g_value_unset(&val1);
+				g_value_unset(&val2);
+
+				g_free(foldedword);
+				return outword;
+			}
+
+			g_value_unset(&val1);
+			g_free(tmpbad);
+
+		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
+	}
+	g_free(foldedword);
+
+	return NULL;
+}
+
+static void
+spellchk_free(spellchk *spell)
+{
+	GtkTextBuffer *buffer;
+
+	g_return_if_fail(spell != NULL);
+
+	buffer = gtk_text_view_get_buffer(spell->view);
+
+	g_signal_handlers_disconnect_matched(buffer,
+			G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL,
+			spell);
+	g_free(spell);
+}
+
+/* Pango doesn't know about the "'" character.  Let's fix that. */
+static gboolean
+spellchk_inside_word(GtkTextIter *iter)
+{
+	gunichar ucs4_char = gtk_text_iter_get_char(iter);
+	gchar *utf8_str;
+	gchar c = 0;
+
+	utf8_str = g_ucs4_to_utf8(&ucs4_char, 1, NULL, NULL, NULL);
+	if (utf8_str != NULL)
+	{
+		c = utf8_str[0];
+		g_free(utf8_str);
+	}
+
+	/* Hack because otherwise typing things like U.S. gets difficult
+	 * if you have 'u' -> 'you' set as a correction...
+	 *
+	 * Part 1 of 2: This marks . as being an inside-word character. */
+	if (c == '.')
+		return TRUE;
+
+	/* Avoid problems with \r, for example (SF #1289031). */
+	if (c == '\\')
+		return TRUE;
+
+	if (gtk_text_iter_inside_word (iter) == TRUE)
+		return TRUE;
+
+	if (c == '\'') {
+		gboolean result = gtk_text_iter_backward_char(iter);
+		gboolean output = gtk_text_iter_inside_word(iter);
+
+		if (result)
+		{
+			/*
+			 * Hack so that "u'll" will correct correctly.
+			 */
+			ucs4_char = gtk_text_iter_get_char(iter);
+			utf8_str = g_ucs4_to_utf8(&ucs4_char, 1, NULL, NULL, NULL);
+			if (utf8_str != NULL)
+			{
+				c = utf8_str[0];
+				g_free(utf8_str);
+
+				if (c == 'u' || c == 'U')
+				{
+					gtk_text_iter_forward_char(iter);
+					return FALSE;
+				}
+			}
+
+			gtk_text_iter_forward_char(iter);
+		}
+
+		return output;
+	}
+	else if (c == '&')
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean
+spellchk_backward_word_start(GtkTextIter *iter)
+{
+	int output;
+	int result;
+
+	output = gtk_text_iter_backward_word_start(iter);
+
+	/* It didn't work...  */
+	if (!output)
+		return FALSE;
+
+	while (spellchk_inside_word(iter)) {
+		result = gtk_text_iter_backward_char(iter);
+
+		/* We can't go backwards anymore?  We're at the beginning of the word. */
+		if (!result)
+			return TRUE;
+
+		if (!spellchk_inside_word(iter)) {
+			gtk_text_iter_forward_char(iter);
+			return TRUE;
+		}
+
+		output = gtk_text_iter_backward_word_start(iter);
+		if (!output)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+check_range(spellchk *spell, GtkTextBuffer *buffer,
+				GtkTextIter start, GtkTextIter end, gboolean sending)
+{
+	gboolean replaced;
+	gboolean result;
+	gchar *tmp;
+	int period_count = 0;
+	gchar *word;
+	GtkTextMark *mark;
+	GtkTextIter pos;
+
+	if ((replaced = substitute_simple_buffer(buffer)))
+	{
+		mark = gtk_text_buffer_get_insert(buffer);
+		gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark);
+		spell->pos = gtk_text_iter_get_offset(&pos);
+
+		gtk_text_buffer_get_iter_at_mark(buffer, &start, mark);
+		gtk_text_buffer_get_iter_at_mark(buffer, &end, mark);
+	}
+
+	if (!sending)
+	{
+		/* We need to go backwords to find out if we are inside a word or not. */
+		gtk_text_iter_backward_char(&end);
+
+		if (spellchk_inside_word(&end))
+		{
+			gtk_text_iter_forward_char(&end);
+			return replaced;  /* We only pay attention to whole words. */
+		}
+	}
+
+	/* We could be in the middle of a whitespace block.  Check for that. */
+	result = gtk_text_iter_backward_char(&end);
+
+	if (!spellchk_inside_word(&end))
+	{
+		if (result)
+			gtk_text_iter_forward_char(&end);
+		return replaced;
+	}
+
+	if (result)
+		gtk_text_iter_forward_char(&end);
+
+	/* Move backwards to the beginning of the word. */
+	spellchk_backward_word_start(&start);
+
+	spell->word = gtk_text_iter_get_text(&start, &end);
+
+	/* Hack because otherwise typing things like U.S. gets difficult
+	 * if you have 'u' -> 'you' set as a correction...
+	 *
+	 * Part 2 of 2: This chops periods off the end of the word so
+	 * the right substitution entry is found. */
+	tmp = g_strdup(spell->word);
+	if (tmp != NULL && *tmp != '\0') {
+		gchar *c;
+		for (c = tmp + strlen(tmp) - 1 ; c != tmp ; c--) {
+			if (*c == '.') {
+				*c = '\0';
+				period_count++;
+			} else
+				break;
+		}
+	}
+
+	if ((word = substitute_word(tmp))) {
+		GtkTextMark *mark;
+		GtkTextIter pos;
+		gchar *tmp2;
+		int i;
+
+		for (i = 1 ; i <= period_count ; i++) {
+			tmp2 = g_strconcat(word, ".", NULL);
+			g_free(word);
+			word = tmp2;
+		}
+
+		gtk_text_buffer_delete(buffer, &start, &end);
+		gtk_text_buffer_insert(buffer, &start, word, -1);
+
+		mark = gtk_text_buffer_get_insert(buffer);
+		gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark);
+		spell->pos = gtk_text_iter_get_offset(&pos);
+
+		g_free(word);
+		g_free(tmp);
+		return TRUE;
+	}
+	g_free(tmp);
+
+	spell->word = NULL;
+
+	return replaced;
+}
+
+/* insertion works like this:
+ *  - before the text is inserted, we mark the position in the buffer.
+ *  - after the text is inserted, we see where our mark is and use that and
+ *    the current position to check the entire range of inserted text.
+ *
+ * this may be overkill for the common case (inserting one character). */
+static void
+insert_text_before(GtkTextBuffer *buffer, GtkTextIter *iter,
+					gchar *text, gint len, spellchk *spell)
+{
+	if (spell->inserting == TRUE)
+		return;
+
+	spell->inserting = TRUE;
+
+	spell->word = NULL;
+
+	gtk_text_buffer_move_mark(buffer, spell->mark_insert_start, iter);
+}
+
+static void
+insert_text_after(GtkTextBuffer *buffer, GtkTextIter *iter,
+					gchar *text, gint len, spellchk *spell)
+{
+	GtkTextIter start, end;
+	GtkTextMark *mark;
+
+	spell->ignore_correction_on_send = FALSE;
+
+	if (spell->ignore_correction) {
+		spell->ignore_correction = FALSE;
+		return;
+	}
+
+	/* we need to check a range of text. */
+	gtk_text_buffer_get_iter_at_mark(buffer, &start, spell->mark_insert_start);
+
+	if (len == 1)
+		check_range(spell, buffer, start, *iter, FALSE);
+
+	/* if check_range modified the buffer, iter has been invalidated */
+	mark = gtk_text_buffer_get_insert(buffer);
+	gtk_text_buffer_get_iter_at_mark(buffer, &end, mark);
+	gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, &end);
+
+	spell->inserting = FALSE;
+}
+
+static void
+delete_range_after(GtkTextBuffer *buffer,
+					GtkTextIter *start, GtkTextIter *end, spellchk *spell)
+{
+	GtkTextIter start2, end2;
+	GtkTextMark *mark;
+	GtkTextIter pos;
+	gint place;
+
+	spell->ignore_correction_on_send = FALSE;
+
+	if (!spell->word)
+		return;
+
+	if (spell->inserting == TRUE)
+		return;
+
+	spell->inserting = TRUE;
+
+	mark = gtk_text_buffer_get_insert(buffer);
+	gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark);
+	place = gtk_text_iter_get_offset(&pos);
+
+	if ((place + 1) != spell->pos) {
+		spell->word = NULL;
+		return;
+	}
+
+	gtk_text_buffer_get_iter_at_mark(buffer, &start2, spell->mark_insert_start);
+	gtk_text_buffer_get_iter_at_mark(buffer, &end2, spell->mark_insert_end);
+
+	gtk_text_buffer_delete(buffer, &start2, &end2);
+	gtk_text_buffer_insert(buffer, &start2, spell->word, -1);
+	spell->ignore_correction = TRUE;
+	spell->ignore_correction_on_send = TRUE;
+
+	spell->inserting = FALSE;
+	spell->word = NULL;
+}
+
+static void
+message_send_cb(GtkWidget *widget, spellchk *spell)
+{
+	GtkTextBuffer *buffer;
+	GtkTextIter start, end;
+	GtkTextMark *mark;
+	gboolean replaced;
+
+	if (spell->ignore_correction_on_send)
+	{
+		spell->ignore_correction_on_send = FALSE;
+		return;
+	}
+
+#if 0
+	if (!gaim_prefs_get_bool("/plugins/gtk/spellchk/last_word_replace"))
+		return;
+#endif
+
+	buffer = gtk_text_view_get_buffer(spell->view);
+
+	gtk_text_buffer_get_end_iter(buffer, &start);
+	gtk_text_buffer_get_end_iter(buffer, &end);
+	spell->inserting = TRUE;
+	replaced = check_range(spell, buffer, start, end, TRUE);
+	spell->inserting = FALSE;
+
+	/* if check_range modified the buffer, iter has been invalidated */
+	mark = gtk_text_buffer_get_insert(buffer);
+	gtk_text_buffer_get_iter_at_mark(buffer, &end, mark);
+	gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, &end);
+
+	if (replaced)
+	{
+		g_signal_stop_emission_by_name(widget, "message_send");
+		spell->ignore_correction_on_send = TRUE;
+	}
+}
+
+static void
+spellchk_new_attach(GaimConversation *conv)
+{
+	spellchk *spell;
+	GtkTextBuffer *buffer;
+	GtkTextIter start, end;
+	GaimGtkConversation *gtkconv;
+	GtkTextView *view;
+
+	gtkconv = GAIM_GTK_CONVERSATION(conv);
+
+	view = GTK_TEXT_VIEW(gtkconv->entry);
+
+	spell = g_object_get_data(G_OBJECT(view), SPELLCHK_OBJECT_KEY);
+	if (spell != NULL)
+		return;
+
+	/* attach to the widget */
+	spell = g_new0(spellchk, 1);
+	spell->view = view;
+
+	g_object_set_data_full(G_OBJECT(view), SPELLCHK_OBJECT_KEY, spell,
+			(GDestroyNotify)spellchk_free);
+
+	buffer = gtk_text_view_get_buffer(view);
+
+	/* we create the mark here, but we don't use it until text is
+	 * inserted, so we don't really care where iter points.  */
+	gtk_text_buffer_get_bounds(buffer, &start, &end);
+	spell->mark_insert_start = gtk_text_buffer_create_mark(buffer,
+			"spellchk-insert-start",
+			&start, TRUE);
+	spell->mark_insert_end = gtk_text_buffer_create_mark(buffer,
+			"spellchk-insert-end",
+			&start, TRUE);
+
+	g_signal_connect_after(G_OBJECT(buffer),
+			"delete-range",
+			G_CALLBACK(delete_range_after), spell);
+	g_signal_connect(G_OBJECT(buffer),
+			"insert-text",
+			G_CALLBACK(insert_text_before), spell);
+	g_signal_connect_after(G_OBJECT(buffer),
+			"insert-text",
+			G_CALLBACK(insert_text_after), spell);
+
+	g_signal_connect(G_OBJECT(gtkconv->entry), "message_send",
+	                 G_CALLBACK(message_send_cb), spell);
+	return;
+}
+
+static int buf_get_line(char *ibuf, char **buf, int *position, int len)
+{
+	int pos = *position;
+	int spos = pos;
+
+	if (pos == len)
+		return 0;
+
+	while (!(ibuf[pos] == '\n' ||
+	         (ibuf[pos] == '\r' && ibuf[pos + 1] != '\n')))
+	{
+		pos++;
+		if (pos == len)
+			return 0;
+	}
+
+	if (pos != 0 && ibuf[pos] == '\n' && ibuf[pos - 1] == '\r')
+		ibuf[pos - 1] = '\0';
+
+	ibuf[pos] = '\0';
+	*buf = &ibuf[spos];
+
+	pos++;
+	*position = pos;
+
+	return 1;
+}
+
+static void load_conf()
+{
+	/* Corrections to change "...", "(c)", "(r)", and "(tm)" to their
+	 * Unicode character equivalents were not added here even though
+	 * they existed in the source list(s). I think these corrections
+	 * would be more trouble than they're worth.
+	 */
+	const char * const defaultconf =
+			"BAD abbout\nGOOD about\n"
+			"BAD abotu\nGOOD about\n"
+			"BAD abouta\nGOOD about a\n"
+			"BAD aboutit\nGOOD about it\n"
+			"BAD aboutthe\nGOOD about the\n"
+			"BAD abscence\nGOOD absence\n"
+			"BAD accesories\nGOOD accessories\n"
+			"BAD accidant\nGOOD accident\n"
+			"BAD accomodate\nGOOD accommodate\n"
+			"BAD accordingto\nGOOD according to\n"
+			"BAD accross\nGOOD across\n"
+			"BAD acheive\nGOOD achieve\n"
+			"BAD acheived\nGOOD achieved\n"
+			"BAD acheiving\nGOOD achieving\n"
+			"BAD acn\nGOOD can\n"
+			"BAD acommodate\nGOOD accommodate\n"
+			"BAD acomodate\nGOOD accommodate\n"
+			"BAD actualyl\nGOOD actually\n"
+			"BAD additinal\nGOOD additional\n"
+			"BAD addtional\nGOOD additional\n"
+			"BAD adequit\nGOOD adequate\n"
+			"BAD adequite\nGOOD adequate\n"
+			"BAD adn\nGOOD and\n"
+			"BAD advanage\nGOOD advantage\n"
+			"BAD affraid\nGOOD afraid\n"
+			"BAD afterthe\nGOOD after the\n"
+			"COMPLETE 0\nBAD againstt he \nGOOD against the \n"
+			"BAD aganist\nGOOD against\n"
+			"BAD aggresive\nGOOD aggressive\n"
+			"BAD agian\nGOOD again\n"
+			"BAD agreemeent\nGOOD agreement\n"
+			"BAD agreemeents\nGOOD agreements\n"
+			"BAD agreemnet\nGOOD agreement\n"
+			"BAD agreemnets\nGOOD agreements\n"
+			"BAD agressive\nGOOD aggressive\n"
+			"BAD agressiveness\nGOOD aggressiveness\n"
+			"BAD ahd\nGOOD had\n"
+			"BAD ahold\nGOOD a hold\n"
+			"BAD ahppen\nGOOD happen\n"
+			"BAD ahve\nGOOD have\n"
+			"BAD allready\nGOOD already\n"
+			"BAD allwasy\nGOOD always\n"
+			"BAD allwyas\nGOOD always\n"
+			"BAD almots\nGOOD almost\n"
+			"BAD almsot\nGOOD almost\n"
+			"BAD alomst\nGOOD almost\n"
+			"BAD alot\nGOOD a lot\n"
+			"BAD alraedy\nGOOD already\n"
+			"BAD alreayd\nGOOD already\n"
+			"BAD alreday\nGOOD already\n"
+			"BAD alwasy\nGOOD always\n"
+			"BAD alwats\nGOOD always\n"
+			"BAD alway\nGOOD always\n"
+			"BAD alwyas\nGOOD always\n"
+			"BAD amde\nGOOD made\n"
+			"BAD Ameria\nGOOD America\n"
+			"BAD amke\nGOOD make\n"
+			"BAD amkes\nGOOD makes\n"
+			"BAD anbd\nGOOD and\n"
+			"BAD andone\nGOOD and one\n"
+			"BAD andteh\nGOOD and the\n"
+			"BAD andthe\nGOOD and the\n"
+			"COMPLETE 0\nBAD andt he \nGOOD and the \n"
+			"BAD anothe\nGOOD another\n"
+			"BAD anual\nGOOD annual\n"
+			"BAD any1\nGOOD anyone\n"
+			"BAD apparant\nGOOD apparent\n"
+			"BAD apparrent\nGOOD apparent\n"
+			"BAD appearence\nGOOD appearance\n"
+			"BAD appeares\nGOOD appears\n"
+			"BAD applicaiton\nGOOD application\n"
+			"BAD applicaitons\nGOOD applications\n"
+			"BAD applyed\nGOOD applied\n"
+			"BAD appointiment\nGOOD appointment\n"
+			"BAD approrpiate\nGOOD appropriate\n"
+			"BAD approrpriate\nGOOD appropriate\n"
+			"BAD aquisition\nGOOD acquisition\n"
+			"BAD aquisitions\nGOOD acquisitions\n"
+			"BAD arent\nGOOD aren't\n"
+			"COMPLETE 0\nBAD aren;t \nGOOD aren't \n"
+			"BAD arguement\nGOOD argument\n"
+			"BAD arguements\nGOOD arguments\n"
+			"COMPLETE 0\nBAD arn't \nGOOD aren't \n"
+			"BAD arond\nGOOD around\n"
+			"BAD artical\nGOOD article\n"
+			"BAD articel\nGOOD article\n"
+			"BAD asdvertising\nGOOD advertising\n"
+			"COMPLETE 0\nBAD askt he \nGOOD ask the \n"
+			"BAD assistent\nGOOD assistant\n"
+			"BAD asthe\nGOOD as the\n"
+			"BAD atention\nGOOD attention\n"
+			"BAD atmospher\nGOOD atmosphere\n"
+			"BAD attentioin\nGOOD attention\n"
+			"BAD atthe\nGOOD at the\n"
+			"BAD audeince\nGOOD audience\n"
+			"BAD audiance\nGOOD audience\n"
+			"BAD authentification\nGOOD authentication\n"
+			"BAD availalbe\nGOOD available\n"
+			"BAD awya\nGOOD away\n"
+			"BAD aywa\nGOOD away\n"
+			"BAD b4\nGOOD before\n"
+			"BAD bakc\nGOOD back\n"
+			"BAD balence\nGOOD balance\n"
+			"BAD ballance\nGOOD balance\n"
+			"BAD baout\nGOOD about\n"
+			"BAD bcak\nGOOD back\n"
+			"BAD bcuz\nGOOD because\n"
+			"BAD beacuse\nGOOD because\n"
+			"BAD becasue\nGOOD because\n"
+			"BAD becaus\nGOOD because\n"
+			"BAD becausea\nGOOD because a\n"
+			"BAD becauseof\nGOOD because of\n"
+			"BAD becausethe\nGOOD because the\n"
+			"BAD becauseyou\nGOOD because you\n"
+			"COMPLETE 0\nBAD beckon call\nGOOD beck and call\n"
+			"BAD becomeing\nGOOD becoming\n"
+			"BAD becomming\nGOOD becoming\n"
+			"BAD becuase\nGOOD because\n"
+			"BAD becuse\nGOOD because\n"
+			"BAD befoer\nGOOD before\n"
+			"BAD beggining\nGOOD beginning\n"
+			"BAD begining\nGOOD beginning\n"
+			"BAD beginining\nGOOD beginning\n"
+			"BAD beleiev\nGOOD believe\n"
+			"BAD beleieve\nGOOD believe\n"
+			"BAD beleif\nGOOD belief\n"
+			"BAD beleive\nGOOD believe\n"
+			"BAD beleived\nGOOD believed\n"
+			"BAD beleives\nGOOD believes\n"
+			"BAD belive\nGOOD believe\n"
+			"BAD belived\nGOOD believed\n"
+			"BAD belives\nGOOD believes\n"
+			"BAD benifit\nGOOD benefit\n"
+			"BAD benifits\nGOOD benefits\n"
+			"BAD betwen\nGOOD between\n"
+			"BAD beutiful\nGOOD beautiful\n"
+			"BAD blase\nGOOD blas\303\251\n"
+			"BAD boxs\nGOOD boxes\n"
+			"BAD brodcast\nGOOD broadcast\n"
+			"BAD butthe\nGOOD but the\n"
+			"BAD bve\nGOOD be\n"
+			"COMPLETE 0\nBAD byt he \nGOOD by the \n"
+			"BAD cafe\nGOOD caf\303\251\n"
+			"BAD caharcter\nGOOD character\n"
+			"BAD calcullated\nGOOD calculated\n"
+			"BAD calulated\nGOOD calculated\n"
+			"BAD candidtae\nGOOD candidate\n"
+			"BAD candidtaes\nGOOD candidates\n"
+			"COMPLETE 0\nBAD case and point\nGOOD case in point\n"
+			"BAD cant\nGOOD can't\n"
+			"COMPLETE 0\nBAD can;t \nGOOD can't \n"
+			"COMPLETE 0\nBAD can't of been\nGOOD can't have been\n"
+			"BAD catagory\nGOOD category\n"
+			"BAD categiory\nGOOD category\n"
+			"BAD certian\nGOOD certain\n"
+			"BAD challange\nGOOD challenge\n"
+			"BAD challanges\nGOOD challenges\n"
+			"BAD chaneg\nGOOD change\n"
+			"BAD chanegs\nGOOD changes\n"
+			"BAD changable\nGOOD changeable\n"
+			"BAD changeing\nGOOD changing\n"
+			"BAD changng\nGOOD changing\n"
+			"BAD charachter\nGOOD character\n"
+			"BAD charachters\nGOOD characters\n"
+			"BAD charactor\nGOOD character\n"
+			"BAD charecter\nGOOD character\n"
+			"BAD charector\nGOOD character\n"
+			"BAD cheif\nGOOD chief\n"
+			"BAD chekc\nGOOD check\n"
+			"BAD chnage\nGOOD change\n"
+			"BAD cieling\nGOOD ceiling\n"
+			"BAD circut\nGOOD circuit\n"
+			"BAD claer\nGOOD clear\n"
+			"BAD claered\nGOOD cleared\n"
+			"BAD claerly\nGOOD clearly\n"
+			"BAD cliant\nGOOD client\n"
+			"BAD cliche\nGOOD clich\303\251\n"
+			"BAD cna\nGOOD can\n"
+			"BAD colection\nGOOD collection\n"
+			"BAD comanies\nGOOD companies\n"
+			"BAD comany\nGOOD company\n"
+			"BAD comapnies\nGOOD companies\n"
+			"BAD comapny\nGOOD company\n"
+			"BAD combintation\nGOOD combination\n"
+			"BAD comited\nGOOD committed\n"
+			"BAD comittee\nGOOD committee\n"
+			"BAD commadn\nGOOD command\n"
+			"BAD comming\nGOOD coming\n"
+			"BAD commitee\nGOOD committee\n"
+			"BAD committe\nGOOD committee\n"
+			"BAD committment\nGOOD commitment\n"
+			"BAD committments\nGOOD commitments\n"
+			"BAD committy\nGOOD committee\n"
+			"BAD comntain\nGOOD contain\n"
+			"BAD comntains\nGOOD contains\n"
+			"BAD compair\nGOOD compare\n"
+			"COMPLETE 0\nBAD company;s \nGOOD company's \n"
+			"BAD competetive\nGOOD competitive\n"
+			"BAD compleated\nGOOD completed\n"
+			"BAD compleatly\nGOOD completely\n"
+			"BAD compleatness\nGOOD completeness\n"
+			"BAD completly\nGOOD completely\n"
+			"BAD completness\nGOOD completeness\n"
+			"BAD composate\nGOOD composite\n"
+			"BAD comtain\nGOOD contain\n"
+			"BAD comtains\nGOOD contains\n"
+			"BAD comunicate\nGOOD communicate\n"
+			"BAD comunity\nGOOD community\n"
+			"BAD condolances\nGOOD condolences\n"
+			"BAD conected\nGOOD connected\n"
+			"BAD conferance\nGOOD conference\n"
+			"BAD confirmmation\nGOOD confirmation\n"
+			"BAD congradulations\nGOOD congratulations\n"
+			"BAD considerit\nGOOD considerate\n"
+			"BAD considerite\nGOOD considerate\n"
+			"BAD consonent\nGOOD consonant\n"
+			"BAD conspiricy\nGOOD conspiracy\n"
+			"BAD consultent\nGOOD consultant\n"
+			"BAD convertable\nGOOD convertible\n"
+			"BAD cooparate\nGOOD cooperate\n"
+			"BAD cooporate\nGOOD cooperate\n"
+			"BAD corproation\nGOOD corporation\n"
+			"BAD corproations\nGOOD corporations\n"
+			"BAD corruptable\nGOOD corruptible\n"
+			"BAD cotten\nGOOD cotton\n"
+			"BAD coudl\nGOOD could\n"
+			"COMPLETE 0\nBAD coudln't \nGOOD couldn't \n"
+			"COMPLETE 0\nBAD coudn't \nGOOD couldn't \n"
+			"BAD couldnt\nGOOD couldn't\n"
+			"COMPLETE 0\nBAD couldn;t \nGOOD couldn't \n"
+			"COMPLETE 0\nBAD could of been\nGOOD could have been\n"
+			"COMPLETE 0\nBAD could of had\nGOOD could have had\n"
+			"BAD couldthe\nGOOD could the\n"
+			"BAD couldve\nGOOD could've\n"
+			"BAD cpoy\nGOOD copy\n"
+			"BAD creme\nGOOD cr\303\250me\n"
+			"BAD ctaegory\nGOOD category\n"
+			"BAD cu\nGOOD see you\n"
+			"BAD cusotmer\nGOOD customer\n"
+			"BAD cusotmers\nGOOD customers\n"
+			"BAD cutsomer\nGOOD customer\n"
+			"BAD cutsomers\nGOOD customer\n"
+			"BAD cuz\nGOOD because\n"
+			"BAD cxan\nGOOD can\n"
+			"BAD danceing\nGOOD dancing\n"
+			"BAD dcument\nGOOD document\n"
+			"BAD deatils\nGOOD details\n"
+			"BAD decison\nGOOD decision\n"
+			"BAD decisons\nGOOD decisions\n"
+			"BAD decor\nGOOD d\303\251cor\n"
+			"BAD defendent\nGOOD defendant\n"
+			"BAD definately\nGOOD definitely\n"
+			"COMPLETE 0\nBAD deja vu\nGOOD d\303\251j\303\240 vu\n"
+			"BAD deptartment\nGOOD department\n"
+			"BAD desicion\nGOOD decision\n"
+			"BAD desicions\nGOOD decisions\n"
+			"BAD desision\nGOOD decision\n"
+			"BAD desisions\nGOOD decisions\n"
+			"BAD detente\nGOOD d\303\251tente\n"
+			"BAD develeoprs\nGOOD developers\n"
+			"BAD devellop\nGOOD develop\n"
+			"BAD develloped\nGOOD developed\n"
+			"BAD develloper\nGOOD developer\n"
+			"BAD devellopers\nGOOD developers\n"
+			"BAD develloping\nGOOD developing\n"
+			"BAD devellopment\nGOOD development\n"
+			"BAD devellopments\nGOOD developments\n"
+			"BAD devellops\nGOOD develop\n"
+			"BAD develope\nGOOD develop\n"
+			"BAD developement\nGOOD development\n"
+			"BAD developements\nGOOD developments\n"
+			"BAD developor\nGOOD developer\n"
+			"BAD developors\nGOOD developers\n"
+			"BAD develpment\nGOOD development\n"
+			"BAD diaplay\nGOOD display\n"
+			"BAD didint\nGOOD didn't\n"
+			"BAD didnot\nGOOD did not\n"
+			"BAD didnt\nGOOD didn't\n"
+			"COMPLETE 0\nBAD didn;t \nGOOD didn't \n"
+			"BAD difefrent\nGOOD different\n"
+			"BAD diferences\nGOOD differences\n"
+			"BAD differance\nGOOD difference\n"
+			"BAD differances\nGOOD differences\n"
+			"BAD differant\nGOOD different\n"
+			"BAD differemt\nGOOD different\n"
+			"BAD differnt\nGOOD different\n"
+			"BAD diffrent\nGOOD different\n"
+			"BAD directer\nGOOD director\n"
+			"BAD directers\nGOOD directors\n"
+			"BAD directiosn\nGOOD direction\n"
+			"BAD disatisfied\nGOOD dissatisfied\n"
+			"BAD discoverd\nGOOD discovered\n"
+			"BAD disign\nGOOD design\n"
+			"BAD dispaly\nGOOD display\n"
+			"BAD dissonent\nGOOD dissonant\n"
+			"BAD distribusion\nGOOD distribution\n"
+			"BAD divsion\nGOOD division\n"
+			"BAD docuement\nGOOD documents\n"
+			"BAD docuemnt\nGOOD document\n"
+			"BAD documetn\nGOOD document\n"
+			"BAD documnet\nGOOD document\n"
+			"BAD documnets\nGOOD documents\n"
+			"COMPLETE 0\nBAD doens't \nGOOD doesn't \n"
+			"BAD doese\nGOOD does\n"
+			"COMPLETE 0\nBAD doe snot \nGOOD does not \n"
+			"BAD doesnt\nGOOD doesn't\n"
+			"COMPLETE 0\nBAD doesn;t \nGOOD doesn't \n"
+			"BAD doign\nGOOD doing\n"
+			"BAD doimg\nGOOD doing\n"
+			"BAD doind\nGOOD doing\n"
+			"BAD dollers\nGOOD dollars\n"
+			"BAD donig\nGOOD doing\n"
+			"BAD donno\nGOOD don't know\n"
+			"BAD dont\nGOOD don't\n"
+			"COMPLETE 0\nBAD do'nt \nGOOD don't \n"
+			"COMPLETE 0\nBAD don;t \nGOOD don't \n"
+			"COMPLETE 0\nBAD don't no \nGOOD don't know \n"
+			"COMPLETE 0\nBAD dosn't \nGOOD doesn't \n"
+			"BAD driveing\nGOOD driving\n"
+			"BAD drnik\nGOOD drink\n"
+			"BAD dunno\nGOOD don't know\n"
+			"BAD eclair\nGOOD \303\251clair\n"
+			"BAD efel\nGOOD feel\n"
+			"BAD effecient\nGOOD efficient\n"
+			"BAD efort\nGOOD effort\n"
+			"BAD eforts\nGOOD efforts\n"
+			"BAD ehr\nGOOD her\n"
+			"BAD eligable\nGOOD eligible\n"
+			"BAD embarass\nGOOD embarrass\n"
+			"BAD emigre\nGOOD \303\251migr\303\251\n"
+			"BAD enought\nGOOD enough\n"
+			"BAD entree\nGOOD entr\303\251e\n"
+			"BAD enuf\nGOOD enough\n"
+			"BAD equippment\nGOOD equipment\n"
+			"BAD equivalant\nGOOD equivalent\n"
+			"BAD esle\nGOOD else\n"
+			"BAD especally\nGOOD especially\n"
+			"BAD especialyl\nGOOD especially\n"
+			"BAD espesially\nGOOD especially\n"
+			"BAD essense\nGOOD essence\n"
+			"BAD excellance\nGOOD excellence\n"
+			"BAD excellant\nGOOD excellent\n"
+			"BAD excercise\nGOOD exercise\n"
+			"BAD exchagne\nGOOD exchange\n"
+			"BAD exchagnes\nGOOD exchanges\n"
+			"BAD excitment\nGOOD excitement\n"
+			"BAD exhcange\nGOOD exchange\n"
+			"BAD exhcanges\nGOOD exchanges\n"
+			"BAD experiance\nGOOD experience\n"
+			"BAD experienc\nGOOD experience\n"
+			"BAD exprience\nGOOD experience\n"
+			"BAD exprienced\nGOOD experienced\n"
+			"BAD eyt\nGOOD yet\n"
+			"BAD facade\nGOOD fa\303\247ade\n"
+			"BAD faeture\nGOOD feature\n"
+			"BAD faetures\nGOOD feature\n"
+			"BAD familair\nGOOD familiar\n"
+			"BAD familar\nGOOD familiar\n"
+			"BAD familliar\nGOOD familiar\n"
+			"BAD fammiliar\nGOOD familiar\n"
+			"BAD feild\nGOOD field\n"
+			"BAD feilds\nGOOD fields\n"
+			"BAD fianlly\nGOOD finally\n"
+			"BAD fidn\nGOOD find\n"
+			"BAD finalyl\nGOOD finally\n"
+			"BAD firends\nGOOD friends\n"
+			"BAD firts\nGOOD first\n"
+			"BAD follwo\nGOOD follow\n"
+			"BAD follwoing\nGOOD following\n"
+			"BAD fora\nGOOD for a\n"
+			"COMPLETE 0\nBAD for all intensive purposes\nfor all intents and purposes\n"
+			"BAD foriegn\nGOOD foreign\n"
+			"BAD forthe\nGOOD for the\n"
+			"BAD forwrd\nGOOD forward\n"
+			"BAD forwrds\nGOOD forwards\n"
+			"BAD foudn\nGOOD found\n"
+			"BAD foward\nGOOD forward\n"
+			"BAD fowards\nGOOD forwards\n"
+			"BAD freind\nGOOD friend\n"
+			"BAD freindly\nGOOD friendly\n"
+			"BAD freinds\nGOOD friends\n"
+			"BAD friday\nGOOD Friday\n"
+			"BAD frmo\nGOOD from\n"
+			"BAD fromthe\nGOOD from the\n"
+			"COMPLETE 0\nBAD fromt he \nGOOD from the \n"
+			"BAD furneral\nGOOD funeral\n"
+			"BAD fwe\nGOOD few\n"
+			"BAD garantee\nGOOD guarantee\n"
+			"BAD gaurd\nGOOD guard\n"
+			"BAD gemeral\nGOOD general\n"
+			"BAD gerat\nGOOD great\n"
+			"BAD geting\nGOOD getting\n"
+			"BAD gettin\nGOOD getting\n"
+			"BAD gievn\nGOOD given\n"
+			"BAD giveing\nGOOD giving\n"
+			"BAD gloabl\nGOOD global\n"
+			"BAD goign\nGOOD going\n"
+			"BAD gonig\nGOOD going\n"
+			"BAD govenment\nGOOD government\n"
+			"BAD goverment\nGOOD government\n"
+			"BAD gruop\nGOOD group\n"
+			"BAD gruops\nGOOD groups\n"
+			"BAD grwo\nGOOD grow\n"
+			"BAD guidlines\nGOOD guidelines\n"
+			"BAD hadbeen\nGOOD had been\n"
+			"BAD hadnt\nGOOD hadn't\n"
+			"COMPLETE 0\nBAD hadn;t \nGOOD hadn't \n"
+			"BAD haev\nGOOD have\n"
+			"BAD hapen\nGOOD happen\n"
+			"BAD hapened\nGOOD happened\n"
+			"BAD hapening\nGOOD happening\n"
+			"BAD hapens\nGOOD happens\n"
+			"BAD happend\nGOOD happened\n"
+			"BAD hasbeen\nGOOD has been\n"
+			"BAD hasnt\nGOOD hasn't\n"
+			"COMPLETE 0\nBAD hasn;t \nGOOD hasn't \n"
+			"BAD havebeen\nGOOD have been\n"
+			"BAD haveing\nGOOD having\n"
+			"BAD havent\nGOOD haven't\n"
+			"COMPLETE 0\nBAD haven;t \nGOOD haven't \n"
+			"BAD hda\nGOOD had\n"
+			"BAD hearign\nGOOD hearing\n"
+			"COMPLETE 0\nBAD he;d \nGOOD he'd \n"
+			"BAD heirarchy\nGOOD hierarchy\n"
+			"BAD hel\nGOOD he'll\n"
+			"COMPLETE 0\nBAD he;ll \nGOOD he'll \n"
+			"BAD helpfull\nGOOD helpful\n"
+			"BAD herat\nGOOD heart\n"
+			"BAD heres\nGOOD here's\n"
+			"COMPLETE 0\nBAD here;s \nGOOD here's \n"
+			"BAD hes\nGOOD he's\n"
+			"COMPLETE 0\nBAD he;s \nGOOD he's \n"
+			"BAD hesaid\nGOOD he said\n"
+			"BAD hewas\nGOOD he was\n"
+			"BAD hge\nGOOD he\n"
+			"BAD hismelf\nGOOD himself\n"
+			"BAD hlep\nGOOD help\n"
+			"BAD hott\nGOOD hot\n"
+			"BAD hows\nGOOD how's\n"
+			"BAD hsa\nGOOD has\n"
+			"BAD hse\nGOOD she\n"
+			"BAD hsi\nGOOD his\n"
+			"BAD hte\nGOOD the\n"
+			"BAD htere\nGOOD there\n"
+			"BAD htese\nGOOD these\n"
+			"BAD htey\nGOOD they\n"
+			"BAD hting\nGOOD thing\n"
+			"BAD htink\nGOOD think\n"
+			"BAD htis\nGOOD this\n"
+			"COMPLETE 0\nBAD htp:\nGOOD http:\n"
+			"COMPLETE 0\nBAD http:\\\\nGOOD http://\n"
+			"BAD httpL\nGOOD http:\n"
+			"BAD hvae\nGOOD have\n"
+			"BAD hvaing\nGOOD having\n"
+			"BAD hwich\nGOOD which\n"
+			"BAD i\nGOOD I\n"
+			"COMPLETE 0\nBAD i c \nGOOD I see \n"
+			"COMPLETE 0\nBAD i;d \nGOOD I'd \n"
+			"COMPLETE 0\nBAD i'd \nGOOD I'd \n"
+			"COMPLETE 0\nBAD I;d \nGOOD I'd \n"
+			"BAD idae\nGOOD idea\n"
+			"BAD idaes\nGOOD ideas\n"
+			"BAD identofy\nGOOD identify\n"
+			"BAD ihs\nGOOD his\n"
+			"BAD iits the\nGOOD it's the\n"
+			"COMPLETE 0\nBAD i'll \nGOOD I'll \n"
+			"COMPLETE 0\nBAD I;ll \nGOOD I'll \n"
+			"COMPLETE 0\nBAD i;m \nGOOD I'm \n"
+			"COMPLETE 0\nBAD i'm \nGOOD I'm \n"
+			"COMPLETE 0\nBAD I\"m \nGOOD I'm \n"
+			"BAD imediate\nGOOD immediate\n"
+			"BAD imediatly\nGOOD immediately\n"
+			"BAD immediatly\nGOOD immediately\n"
+			"BAD importent\nGOOD important\n"
+			"BAD importnat\nGOOD important\n"
+			"BAD impossable\nGOOD impossible\n"
+			"BAD improvemnt\nGOOD improvement\n"
+			"BAD improvment\nGOOD improvement\n"
+			"BAD includ\nGOOD include\n"
+			"BAD indecate\nGOOD indicate\n"
+			"BAD indenpendence\nGOOD independence\n"
+			"BAD indenpendent\nGOOD independent\n"
+			"BAD indepedent\nGOOD independent\n"
+			"BAD independance\nGOOD independence\n"
+			"BAD independant\nGOOD independent\n"
+			"BAD influance\nGOOD influence\n"
+			"BAD infomation\nGOOD information\n"
+			"BAD informatoin\nGOOD information\n"
+			"BAD inital\nGOOD initial\n"
+			"BAD instaleld\nGOOD installed\n"
+			"BAD insted\nGOOD instead\n"
+			"BAD insurence\nGOOD insurance\n"
+			"BAD inteh\nGOOD in the\n"
+			"BAD interum\nGOOD interim\n"
+			"BAD inthe\nGOOD in the\n"
+			"COMPLETE 0\nBAD int he \nGOOD in the \n"
+			"BAD inturn\nGOOD intern\n"
+			"BAD inwhich\nGOOD in which\n"
+			"COMPLETE 0\nBAD i snot \nGOOD is not \n"
+			"BAD isnt\nGOOD isn't\n"
+			"COMPLETE 0\nBAD isn;t \nGOOD isn't \n"
+			"BAD isthe\nGOOD is the\n"
+			"BAD itd\nGOOD it'd\n"
+			"COMPLETE 0\nBAD it;d \nGOOD it'd \n"
+			"BAD itis\nGOOD it is\n"
+			"BAD ititial\nGOOD initial\n"
+			"BAD itll\nGOOD it'll\n"
+			"COMPLETE 0\nBAD it;ll \nGOOD it'll \n"
+			"BAD itnerest\nGOOD interest\n"
+			"BAD itnerested\nGOOD interested\n"
+			"BAD itneresting\nGOOD interesting\n"
+			"BAD itnerests\nGOOD interests\n"
+			"COMPLETE 0\nBAD it;s \nGOOD it's \n"
+			"BAD itsa\nGOOD it's a\n"
+			"COMPLETE 0\nBAD  its a \nGOOD  it's a \n"
+			"COMPLETE 0\nBAD  it snot \nGOOD  it's not \n"
+			"COMPLETE 0\nBAD  it' snot \nGOOD  it's not \n"
+			"COMPLETE 0\nBAD  its the \nGOOD  it's the \n"
+			"BAD itwas\nGOOD it was\n"
+			"BAD ive\nGOOD I've\n"
+			"COMPLETE 0\nBAD i;ve \nGOOD I've \n"
+			"COMPLETE 0\nBAD i've \nGOOD I've \n"
+			"BAD iwll\nGOOD will\n"
+			"BAD iwth\nGOOD with\n"
+			"BAD jsut\nGOOD just\n"
+			"BAD jugment\nGOOD judgment\n"
+			"BAD kno\nGOOD know\n"
+			"BAD knowldge\nGOOD knowledge\n"
+			"BAD knowlege\nGOOD knowledge\n"
+			"BAD knwo\nGOOD know\n"
+			"BAD knwon\nGOOD known\n"
+			"BAD knwos\nGOOD knows\n"
+			"BAD konw\nGOOD know\n"
+			"BAD konwn\nGOOD known\n"
+			"BAD konws\nGOOD knows\n"
+			"BAD labratory\nGOOD laboratory\n"
+			"BAD labtop\nGOOD laptop\n"
+			"BAD lastyear\nGOOD last year\n"
+			"BAD laterz\nGOOD later\n"
+			"BAD learnign\nGOOD learning\n"
+			"BAD lenght\nGOOD length\n"
+			"COMPLETE 0\nBAD let;s \nGOOD let's \n"
+			"COMPLETE 0\nBAD let's him \nGOOD lets him \n"
+			"COMPLETE 0\nBAD let's it \nGOOD lets it \n"
+			"BAD levle\nGOOD level\n"
+			"BAD libary\nGOOD library\n"
+			"BAD librarry\nGOOD library\n"
+			"BAD librery\nGOOD library\n"
+			"BAD liek\nGOOD like\n"
+			"BAD liekd\nGOOD liked\n"
+			"BAD lieutenent\nGOOD lieutenant\n"
+			"BAD liev\nGOOD live\n"
+			"BAD likly\nGOOD likely\n"
+			"BAD lisense\nGOOD license\n"
+			"BAD littel\nGOOD little\n"
+			"BAD litttle\nGOOD little\n"
+			"BAD liuke\nGOOD like\n"
+			"BAD liveing\nGOOD living\n"
+			"BAD loev\nGOOD love\n"
+			"BAD lonly\nGOOD lonely\n"
+			"BAD lookign\nGOOD looking\n"
+			"BAD m\nGOOD am\n"
+			"BAD maintainence\nGOOD maintenance\n"
+			"BAD maintenence\nGOOD maintenance\n"
+			"BAD makeing\nGOOD making\n"
+			"BAD managment\nGOOD management\n"
+			"BAD mantain\nGOOD maintain\n"
+			"BAD marraige\nGOOD marriage\n"
+			"COMPLETE 0\nBAD may of been\nGOOD may have been\n"
+			"COMPLETE 0\nBAD may of had\nGOOD may have had\n"
+			"BAD memeber\nGOOD member\n"
+			"BAD merchent\nGOOD merchant\n"
+			"BAD mesage\nGOOD message\n"
+			"BAD mesages\nGOOD messages\n"
+			"COMPLETE 0\nBAD might of been\nGOOD might have been\n"
+			"COMPLETE 0\nBAD might of had\nGOOD might have had\n"
+			"BAD mispell\nGOOD misspell\n"
+			"BAD mispelling\nGOOD misspelling\n"
+			"BAD mispellings\nGOOD misspellings\n"
+			"BAD mkae\nGOOD make\n"
+			"BAD mkaes\nGOOD makes\n"
+			"BAD mkaing\nGOOD making\n"
+			"BAD moeny\nGOOD money\n"
+			"BAD monday\nGOOD Monday\n"
+			"BAD morgage\nGOOD mortgage\n"
+			"BAD mroe\nGOOD more\n"
+			"COMPLETE 0\nBAD must of been\nGOOD must have been\n"
+			"COMPLETE 0\nBAD must of had\nGOOD must have had\n"
+			"COMPLETE 0\nBAD mute point\nGOOD moot point\n"
+			"BAD mysefl\nGOOD myself\n"
+			"BAD myu\nGOOD my\n"
+			"BAD naive\nGOOD naïve\n"
+			"BAD ne1\nGOOD anyone\n"
+			"BAD neway\nGOOD anyway\n"
+			"BAD neways\nGOOD anyways\n"
+			"BAD necassarily\nGOOD necessarily\n"
+			"BAD necassary\nGOOD necessary\n"
+			"BAD neccessarily\nGOOD necessarily\n"
+			"BAD neccessary\nGOOD necessary\n"
+			"BAD necesarily\nGOOD necessarily\n"
+			"BAD necesary\nGOOD necessary\n"
+			"BAD negotiaing\nGOOD negotiating\n"
+			"BAD nkow\nGOOD know\n"
+			"BAD nothign\nGOOD nothing\n"
+			"BAD nto\nGOOD not\n"
+			"BAD nver\nGOOD never\n"
+			"BAD nwe\nGOOD new\n"
+			"BAD nwo\nGOOD now\n"
+			"BAD obediant\nGOOD obedient\n"
+			"BAD ocasion\nGOOD occasion\n"
+			"BAD occassion\nGOOD occasion\n"
+			"BAD occurance\nGOOD occurrence\n"
+			"BAD occured\nGOOD occurred\n"
+			"BAD occurence\nGOOD occurrence\n"
+			"BAD occurrance\nGOOD occurrence\n"
+			"BAD oclock\nGOOD o'clock\n"
+			"BAD oculd\nGOOD could\n"
+			"BAD ocur\nGOOD occur\n"
+			"BAD oeprator\nGOOD operator\n"
+			"BAD ofits\nGOOD of its\n"
+			"BAD ofthe\nGOOD of the\n"
+			"BAD oft he\nGOOD of the\n"
+			"BAD oging\nGOOD going\n"
+			"BAD ohter\nGOOD other\n"
+			"BAD omre\nGOOD more\n"
+			"BAD oneof\nGOOD one of\n"
+			"BAD onepoint\nGOOD one point\n"
+			"BAD onthe\nGOOD on the\n"
+			"COMPLETE 0\nBAD ont he \nGOOD on the \n"
+			"BAD onyl\nGOOD only\n"
+			"BAD oppasite\nGOOD opposite\n"
+			"BAD opperation\nGOOD operation\n"
+			"BAD oppertunity\nGOOD opportunity\n"
+			"BAD opposate\nGOOD opposite\n"
+			"BAD opposible\nGOOD opposable\n"
+			"BAD opposit\nGOOD opposite\n"
+			"BAD oppotunities\nGOOD opportunities\n"
+			"BAD oppotunity\nGOOD opportunity\n"
+			"BAD orginization\nGOOD organization\n"
+			"BAD orginized\nGOOD organized\n"
+			"BAD otehr\nGOOD other\n"
+			"BAD otu\nGOOD out\n"
+			"BAD outof\nGOOD out of\n"
+			"BAD overthe\nGOOD over the\n"
+			"BAD owrk\nGOOD work\n"
+			"BAD owuld\nGOOD would\n"
+			"BAD oxident\nGOOD oxidant\n"
+			"BAD papaer\nGOOD paper\n"
+			"BAD passe\nGOOD pass\303\251\n"
+			"BAD parliment\nGOOD parliament\n"
+			"BAD partof\nGOOD part of\n"
+			"BAD paymetn\nGOOD payment\n"
+			"BAD paymetns\nGOOD payments\n"
+			"BAD pciture\nGOOD picture\n"
+			"BAD peice\nGOOD piece\n"
+			"BAD peices\nGOOD pieces\n"
+			"BAD peolpe\nGOOD people\n"
+			"BAD peopel\nGOOD people\n"
+			"BAD percentof\nGOOD percent of\n"
+			"BAD percentto\nGOOD percent to\n"
+			"BAD performence\nGOOD performance\n"
+			"BAD perhasp\nGOOD perhaps\n"
+			"BAD perhpas\nGOOD perhaps\n"
+			"BAD permanant\nGOOD permanent\n"
+			"BAD perminent\nGOOD permanent\n"
+			"BAD personalyl\nGOOD personally\n"
+			"BAD pleasent\nGOOD pleasant\n"
+			"BAD pls\nGOOD please\n"
+			"BAD plz\nGOOD please\n"
+			"BAD poeple\nGOOD people\n"
+			"BAD porblem\nGOOD problem\n"
+			"BAD porblems\nGOOD problems\n"
+			"BAD porvide\nGOOD provide\n"
+			"BAD possable\nGOOD possible\n"
+			"BAD postition\nGOOD position\n"
+			"BAD potatoe\nGOOD potato\n"
+			"BAD potatos\nGOOD potatoes\n"
+			"BAD potentialy\nGOOD potentially\n"
+			"BAD ppl\nGOOD people\n"
+			"BAD pregnent\nGOOD pregnant\n"
+			"BAD presance\nGOOD presence\n"
+			"BAD primative\nGOOD primitive\n"
+			"BAD probally\nGOOD probably\n"
+			"BAD probelm\nGOOD problem\n"
+			"BAD probelms\nGOOD problems\n"
+			"BAD probly\nGOOD probably\n"
+			"BAD prolly\nGOOD probably\n"
+			"BAD proly\nGOOD probably\n"
+			"BAD prominant\nGOOD prominent\n"
+			"BAD proposterous\nGOOD preposterous\n"
+			"BAD protege\nGOOD prot\303\251g\303\251\n"
+			"BAD protoge\nGOOD prot\303\251g\303\251\n"
+			"BAD psoition\nGOOD position\n"
+			"BAD ptogress\nGOOD progress\n"
+			"BAD pursuade\nGOOD persuade\n"
+			"BAD puting\nGOOD putting\n"
+			"BAD pwoer\nGOOD power\n"
+			"BAD quater\nGOOD quarter\n"
+			"BAD quaters\nGOOD quarters\n"
+			"BAD quesion\nGOOD question\n"
+			"BAD quesions\nGOOD questions\n"
+			"BAD questioms\nGOOD questions\n"
+			"BAD questiosn\nGOOD questions\n"
+			"BAD questoin\nGOOD question\n"
+			"BAD quetion\nGOOD question\n"
+			"BAD quetions\nGOOD questions\n"
+			"BAD r\nGOOD are\n"
+			"BAD raeson\nGOOD reason\n"
+			"BAD realyl\nGOOD really\n"
+			"BAD reccomend\nGOOD recommend\n"
+			"BAD reccommend\nGOOD recommend\n"
+			"BAD receieve\nGOOD receive\n"
+			"BAD recieve\nGOOD receive\n"
+			"BAD recieved\nGOOD received\n"
+			"BAD recieving\nGOOD receiving\n"
+			"BAD recomend\nGOOD recommend\n"
+			"BAD recomendation\nGOOD recommendation\n"
+			"BAD recomendations\nGOOD recommendations\n"
+			"BAD recomended\nGOOD recommended\n"
+			"BAD reconize\nGOOD recognize\n"
+			"BAD recrod\nGOOD record\n"
+			"BAD rediculous\nGOOD ridiculous\n"
+			"BAD rediculus\nGOOD ridiculous\n"
+			"BAD reguard\nGOOD regard\n"
+			"BAD religous\nGOOD religious\n"
+			"BAD reluctent\nGOOD reluctant\n"
+			"BAD remeber\nGOOD remember\n"
+			"BAD reommend\nGOOD recommend\n"
+			"BAD representativs\nGOOD representatives\n"
+			"BAD representives\nGOOD representatives\n"
+			"BAD represetned\nGOOD represented\n"
+			"BAD represnt\nGOOD represent\n"
+			"BAD reserach\nGOOD research\n"
+			"BAD resollution\nGOOD resolution\n"
+			"BAD resorces\nGOOD resources\n"
+			"BAD respomd\nGOOD respond\n"
+			"BAD respomse\nGOOD response\n"
+			"BAD responce\nGOOD response\n"
+			"BAD responsability\nGOOD responsibility\n"
+			"BAD responsable\nGOOD responsible\n"
+			"BAD responsibile\nGOOD responsible\n"
+			"BAD responsiblity\nGOOD responsibility\n"
+			"BAD restaraunt\nGOOD restaurant\n"
+			"BAD restuarant\nGOOD restaurant\n"
+			"BAD reult\nGOOD result\n"
+			"BAD reveiw\nGOOD review\n"
+			"BAD reveiwing\nGOOD reviewing\n"
+			"BAD rumers\nGOOD rumors\n"
+			"BAD rwite\nGOOD write\n"
+			"BAD rythm\nGOOD rhythm\n"
+			"BAD saidhe\nGOOD said he\n"
+			"BAD saidit\nGOOD said it\n"
+			"BAD saidthat\nGOOD said that\n"
+			"BAD saidthe\nGOOD said the\n"
+			"COMPLETE 0\nBAD saidt he \nGOOD said the \n"
+			"BAD sandwhich\nGOOD sandwich\n"
+			"BAD sandwitch\nGOOD sandwich\n"
+			"BAD saturday\nGOOD Saturday\n"
+			"BAD scedule\nGOOD schedule\n"
+			"BAD sceduled\nGOOD scheduled\n"
+			"BAD seance\nGOOD s\303\251ance\n"
+			"BAD secratary\nGOOD secretary\n"
+			"BAD sectino\nGOOD section\n"
+			"BAD seh\nGOOD she\n"
+			"BAD selectoin\nGOOD selection\n"
+			"BAD sence\nGOOD sense\n"
+			"BAD sentance\nGOOD sentence\n"
+			"BAD separeate\nGOOD separate\n"
+			"BAD seperate\nGOOD separate\n"
+			"BAD sercumstances\nGOOD circumstances\n"
+			"BAD shcool\nGOOD school\n"
+			"COMPLETE 0\nBAD she;d \nGOOD she'd \n"
+			"COMPLETE 0\nBAD she;ll \nGOOD she'll \n"
+			"BAD shes\nGOOD she's\n"
+			"COMPLETE 0\nBAD she;s \nGOOD she's \n"
+			"BAD shesaid\nGOOD she said\n"
+			"BAD shineing\nGOOD shining\n"
+			"BAD shiped\nGOOD shipped\n"
+			"BAD shoudl\nGOOD should\n"
+			"COMPLETE 0\nBAD shoudln't \nGOOD shouldn't \n"
+			"BAD shouldent\nGOOD shouldn't\n"
+			"BAD shouldnt\nGOOD shouldn't\n"
+			"COMPLETE 0\nBAD shouldn;t \nGOOD shouldn't \n"
+			"COMPLETE 0\nBAD should of been\nGOOD should have been\n"
+			"COMPLETE 0\nBAD should of had\nGOOD should have had\n"
+			"BAD shouldve\nGOOD should've\n"
+			"BAD showinf\nGOOD showing\n"
+			"BAD signifacnt\nGOOD significant\n"
+			"BAD simalar\nGOOD similar\n"
+			"BAD similiar\nGOOD similar\n"
+			"BAD simpyl\nGOOD simply\n"
+			"BAD sincerly\nGOOD sincerely\n"
+			"BAD sitll\nGOOD still\n"
+			"BAD smae\nGOOD same\n"
+			"BAD smoe\nGOOD some\n"
+			"BAD soem\nGOOD some\n"
+			"BAD sohw\nGOOD show\n"
+			"BAD soical\nGOOD social\n"
+			"BAD some1\nGOOD someone\n"
+			"BAD somethign\nGOOD something\n"
+			"BAD someting\nGOOD something\n"
+			"BAD somewaht\nGOOD somewhat\n"
+			"BAD somthing\nGOOD something\n"
+			"BAD somtimes\nGOOD sometimes\n"
+			"COMPLETE 0\nBAD sot hat \nGOOD so that \n"
+			"BAD soudn\nGOOD sound\n"
+			"BAD soudns\nGOOD sounds\n"
+			"BAD speach\nGOOD speech\n"
+			"BAD specificaly\nGOOD specifically\n"
+			"BAD specificalyl\nGOOD specifically\n"
+			"BAD spelt\nGOOD spelled\n"
+			"BAD sry\nGOOD sorry\n"
+			"COMPLETE 0\nBAD state of the ark\nGOOD state of the art\n"
+			"BAD statment\nGOOD statement\n"
+			"BAD statments\nGOOD statements\n"
+			"BAD stnad\nGOOD stand\n"
+			"BAD stopry\nGOOD story\n"
+			"BAD stoyr\nGOOD story\n"
+			"BAD stpo\nGOOD stop\n"
+			"BAD strentgh\nGOOD strength\n"
+			"BAD stroy\nGOOD story\n"
+			"BAD struggel\nGOOD struggle\n"
+			"BAD strugle\nGOOD struggle\n"
+			"BAD studnet\nGOOD student\n"
+			"BAD successfull\nGOOD successful\n"
+			"BAD successfuly\nGOOD successfully\n"
+			"BAD successfulyl\nGOOD successfully\n"
+			"BAD sucess\nGOOD success\n"
+			"BAD sucessfull\nGOOD successful\n"
+			"BAD sufficiant\nGOOD sufficient\n"
+			"BAD sum1\nGOOD someone\n"
+			"BAD sunday\nGOOD Sunday\n"
+			"BAD suposed\nGOOD supposed\n"
+			"BAD supposably\nGOOD supposedly\n"
+			"BAD suppossed\nGOOD supposed\n"
+			"BAD suprise\nGOOD surprise\n"
+			"BAD suprised\nGOOD surprised\n"
+			"BAD sux\nGOOD sucks\n"
+			"BAD swiming\nGOOD swimming\n"
+			"BAD tahn\nGOOD than\n"
+			"BAD taht\nGOOD that\n"
+			"COMPLETE 0\nBAD take it for granite\nGOOD take it for granted\n"
+			"COMPLETE 0\nBAD taken for granite\nGOOD taken for granted\n"
+			"BAD talekd\nGOOD talked\n"
+			"BAD talkign\nGOOD talking\n"
+			"BAD tath\nGOOD that\n"
+			"BAD tecnical\nGOOD technical\n"
+			"BAD teh\nGOOD the\n"
+			"BAD tehy\nGOOD they\n"
+			"COMPLETE 0\nBAD tellt he \nGOOD tell the \n"
+			"BAD termoil\nGOOD turmoil\n"
+			"BAD tets\nGOOD test\n"
+			"BAD tghe\nGOOD the\n"
+			"BAD tghis\nGOOD this\n"
+			"BAD thansk\nGOOD thanks\n"
+			"BAD thanx\nGOOD thanks\n"
+			"BAD thats\nGOOD that's\n"
+			"BAD thatthe\nGOOD that the\n"
+			"COMPLETE 0\nBAD thatt he \nGOOD that the \n"
+			"BAD thecompany\nGOOD the company\n"
+			"BAD thefirst\nGOOD the first\n"
+			"BAD thegovernment\nGOOD the government\n"
+			"COMPLETE 0\nBAD their are \nGOOD there are \n"
+			"COMPLETE 0\nBAD their aren't \nGOOD there aren't \n"
+			"COMPLETE 0\nBAD their is \nGOOD there is \n"
+			"BAD themself\nGOOD themselves\n"
+			"BAD themselfs\nGOOD themselves\n"
+			"BAD thenew\nGOOD the new\n"
+			"BAD theres\nGOOD there's\n"
+			"COMPLETE 0\nBAD there's is \nGOOD theirs is \n"
+			"COMPLETE 0\nBAD there's isn't \nGOOD theirs isn't \n"
+			"BAD theri\nGOOD their\n"
+			"BAD thesame\nGOOD the same\n"
+			"BAD thetwo\nGOOD the two\n"
+			"BAD theyd\nGOOD they'd\n"
+			"COMPLETE 0\nBAD they;d \nGOOD they'd \n"
+			"COMPLETE 0\nBAD they;l \nGOOD they'll \n"
+			"BAD theyll\nGOOD they'll\n"
+			"COMPLETE 0\nBAD they;ll \nGOOD they'll \n"
+			"COMPLETE 0\nBAD they;r \nGOOD they're \n"
+			"COMPLETE 0\nBAD theyre \nGOOD they're \n"
+			"COMPLETE 0\nBAD they;re \nGOOD they're \n"
+			"COMPLETE 0\nBAD they're are \nGOOD there are \n"
+			"COMPLETE 0\nBAD they're is \nGOOD there is \n"
+			"COMPLETE 0\nBAD they;v \nGOOD they've \n"
+			"BAD theyve\nGOOD they've\n"
+			"COMPLETE 0\nBAD they;ve \nGOOD they've \n"
+			"BAD thgat\nGOOD that\n"
+			"BAD thge\nGOOD the\n"
+			"BAD thier\nGOOD their \n"
+			"BAD thigsn\nGOOD things\n"
+			"BAD thisyear\nGOOD this year\n"
+			"BAD thme\nGOOD them\n"
+			"BAD thna\nGOOD than\n"
+			"BAD thne\nGOOD then\n"
+			"BAD thnig\nGOOD thing\n"
+			"BAD thnigs\nGOOD things\n"
+			"BAD tho\nGOOD though\n"
+			"BAD threatend\nGOOD threatened\n"
+			"BAD thsi\nGOOD this\n"
+			"BAD thsoe\nGOOD those\n"
+			"BAD thta\nGOOD that\n"
+			"BAD thursday\nGOOD Thursday\n"
+			"BAD thx\nGOOD thanks\n"
+			"BAD tihs\nGOOD this\n"
+			"BAD timne\nGOOD time\n"
+			"BAD tiogether\nGOOD together\n"
+			"BAD tje\nGOOD the\n"
+			"BAD tjhe\nGOOD the\n"
+			"BAD tkae\nGOOD take\n"
+			"BAD tkaes\nGOOD takes\n"
+			"BAD tkaing\nGOOD taking\n"
+			"BAD tlaking\nGOOD talking\n"
+			"BAD tnx\nGOOD thanks\n"
+			"BAD todya\nGOOD today\n"
+			"BAD togehter\nGOOD together\n"
+			"COMPLETE 0\nBAD toldt he \nGOOD told the \n"
+			"BAD tomorow\nGOOD tomorrow\n"
+			"BAD tongiht\nGOOD tonight\n"
+			"BAD tonihgt\nGOOD tonight\n"
+			"BAD tonite\nGOOD tonight\n"
+			"BAD totaly\nGOOD totally\n"
+			"BAD totalyl\nGOOD totally\n"
+			"BAD tothe\nGOOD to the\n"
+			"COMPLETE 0\nBAD tot he \nGOOD to the \n"
+			"BAD touche\nGOOD touch\303\251\n"
+			"BAD towrad\nGOOD toward\n"
+			"BAD traditionalyl\nGOOD traditionally\n"
+			"BAD transfered\nGOOD transferred\n"
+			"BAD truely\nGOOD truly\n"
+			"BAD truley\nGOOD truly\n"
+			"BAD tryed\nGOOD tried\n"
+			"BAD tthe\nGOOD the\n"
+			"BAD tuesday\nGOOD Tuesday\n"
+			"BAD tyhat\nGOOD that\n"
+			"BAD tyhe\nGOOD the\n"
+			"BAD u\nGOOD you\n"
+			"BAD udnerstand\nGOOD understand\n"
+			"BAD understnad\nGOOD understand\n"
+			"COMPLETE 0\nBAD undert he \nGOOD under the \n"
+			"BAD unforseen\nGOOD unforeseen\n"
+			"BAD UnitedStates\nGOOD United States\n"
+			"BAD unliek\nGOOD unlike\n"
+			"BAD unpleasently\nGOOD unpleasantly\n"
+			"BAD untill\nGOOD until\n"
+			"BAD untilll\nGOOD until\n"
+			"BAD ur\nGOOD you are\n"
+			"BAD useing\nGOOD using\n"
+			"BAD usualyl\nGOOD usually\n"
+			"BAD veyr\nGOOD very\n"
+			"BAD virtualyl\nGOOD virtually\n"
+			"BAD visavis\nGOOD vis-a-vis\n"
+			"COMPLETE 0\nBAD vis-a-vis\nGOOD vis-\303\240-vis\n"
+			"BAD vrey\nGOOD very\n"
+			"BAD vulnerible\nGOOD vulnerable\n"
+			"BAD waht\nGOOD what\n"
+			"BAD warrent\nGOOD warrant\n"
+			"COMPLETE 0\nBAD wa snot \nGOOD was not \n"
+			"COMPLETE 0\nBAD wasnt \nGOOD wasn't \n"
+			"COMPLETE 0\nBAD wasn;t \nGOOD wasn't \n"
+			"BAD wat\nGOOD what\n"
+			"BAD watn\nGOOD want\n"
+			"COMPLETE 0\nBAD we;d \nGOOD we'd \n"
+			"BAD wednesday\nGOOD Wednesday\n"
+			"BAD wehn\nGOOD when\n"
+			"COMPLETE 0\nBAD we'l \nGOOD we'll \n"
+			"COMPLETE 0\nBAD we;ll \nGOOD we'll \n"
+			"COMPLETE 0\nBAD we;re \nGOOD we're \n"
+			"BAD werent\nGOOD weren't\n"
+			"COMPLETE 0\nBAD weren;t \nGOOD weren't \n"
+			"COMPLETE 0\nBAD wern't \nGOOD weren't \n"
+			"BAD werre\nGOOD were\n"
+			"BAD weve\nGOOD we've\n"
+			"COMPLETE 0\nBAD we;ve \nGOOD we've \n"
+			"BAD whats\nGOOD what's\n"
+			"COMPLETE 0\nBAD what;s \nGOOD what's \n"
+			"BAD whcih\nGOOD which\n"
+			"COMPLETE 0\nBAD whent he \nGOOD when the \n"
+			"BAD wheres\nGOOD where's\n"
+			"COMPLETE 0\nBAD where;s \nGOOD where's \n"
+			"BAD wherre\nGOOD where\n"
+			"BAD whic\nGOOD which\n"
+			"COMPLETE 0\nBAD whicht he \nGOOD which the \n"
+			"BAD whihc\nGOOD which\n"
+			"BAD wholl\nGOOD who'll\n"
+			"BAD whos\nGOOD who's\n"
+			"COMPLETE 0\nBAD who;s \nGOOD who's \n"
+			"BAD whove\nGOOD who've\n"
+			"COMPLETE 0\nBAD who;ve \nGOOD who've \n"
+			"BAD whta\nGOOD what\n"
+			"BAD whys\nGOOD why's\n"
+			"BAD wief\nGOOD wife\n"
+			"BAD wierd\nGOOD weird\n"
+			"BAD wihch\nGOOD which\n"
+			"BAD wiht\nGOOD with\n"
+			"BAD willbe\nGOOD will be\n"
+			"COMPLETE 0\nBAD will of been\nGOOD will have been\n"
+			"COMPLETE 0\nBAD will of had\nGOOD will have had\n"
+			"BAD windoes\nGOOD windows\n"
+			"BAD witha\nGOOD with a\n"
+			"BAD withdrawl\nGOOD withdrawal\n"
+			"BAD withe\nGOOD with\n"
+			"COMPLETE 0\nBAD withthe \nGOOD with the \n"
+			"BAD witht he\nGOOD with the\n"
+			"BAD wiull\nGOOD will\n"
+			"BAD wnat\nGOOD want\n"
+			"BAD wnated\nGOOD wanted\n"
+			"BAD wnats\nGOOD wants\n"
+			"BAD woh\nGOOD who\n"
+			"BAD wohle\nGOOD whole\n"
+			"BAD wokr\nGOOD work\n"
+			"BAD wont\nGOOD won't\n"
+			"COMPLETE 0\nBAD wo'nt \nGOOD won't \n"
+			"COMPLETE 0\nBAD won;t \nGOOD won't \n"
+			"BAD woudl\nGOOD would\n"
+			"COMPLETE 0\nBAD woudln't \nGOOD wouldn't \n"
+			"BAD wouldbe\nGOOD would be\n"
+			"BAD wouldnt\nGOOD wouldn't\n"
+			"COMPLETE 0\nBAD wouldn;t \nGOOD wouldn't \n"
+			"COMPLETE 0\nBAD would of been\nGOOD would have been\n"
+			"COMPLETE 0\nBAD would of had\nGOOD would have had\n"
+			"BAD wouldve\nGOOD would've\n"
+			"BAD wriet\nGOOD write\n"
+			"BAD writting\nGOOD writing\n"
+			"BAD wrod\nGOOD word\n"
+			"BAD wroet\nGOOD wrote\n"
+			"BAD wroking\nGOOD working\n"
+			"BAD wtih\nGOOD with\n"
+			"BAD wuould\nGOOD would\n"
+			"BAD wut\nGOOD what\n"
+			"BAD wya\nGOOD way\n"
+			"BAD y\nGOOD why\n"
+			"BAD yeh\nGOOD yeah\n"
+			"BAD yera\nGOOD year\n"
+			"BAD yeras\nGOOD years\n"
+			"BAD yersa\nGOOD years\n"
+			"BAD yoiu\nGOOD you\n"
+			"BAD youare\nGOOD you are\n"
+			"BAD youd\nGOOD you'd\n"
+			"COMPLETE 0\nBAD you;d \nGOOD you'd \n"
+			"BAD youll\nGOOD you'll\n"
+			"COMPLETE 0\nBAD your a \nGOOD you're a \n"
+			"COMPLETE 0\nBAD your an \nGOOD you're an \n"
+			"BAD youre\nGOOD you're\n"
+			"COMPLETE 0\nBAD you;re \nGOOD you're \n"
+			"COMPLETE 0\nBAD you're own \nGOOD your own \n"
+			"COMPLETE 0\nBAD your her \nGOOD you're her \n"
+			"COMPLETE 0\nBAD your here \nGOOD you're here \n"
+			"COMPLETE 0\nBAD your his \nGOOD you're his \n"
+			"COMPLETE 0\nBAD your my \nGOOD you're my \n"
+			"COMPLETE 0\nBAD your the \nGOOD you're the \n"
+			"COMPLETE 0\nBAD your their \nGOOD you're their \n"
+			"COMPLETE 0\nBAD your your \nGOOD you're your \n"
+			"BAD youve\nGOOD you've\n"
+			"COMPLETE 0\nBAD you;ve \nGOOD you've \n"
+			"BAD ytou\nGOOD you\n"
+			"BAD yuo\nGOOD you\n"
+			"BAD yuor\nGOOD your\n";
+	gchar *buf;
+	gchar *ibuf;
+	GHashTable *hashes;
+	char bad[82] = "";
+	char good[256] = "";
+	int pnt = 0;
+	gsize size;
+	gboolean complete = TRUE;
+	gboolean case_sensitive = FALSE;
+
+	buf = g_build_filename(gaim_user_dir(), "dict", NULL);
+	g_file_get_contents(buf, &ibuf, &size, NULL);
+	g_free(buf);
+	if (!ibuf) {
+		ibuf = g_strdup(defaultconf);
+		size = strlen(defaultconf);
+	}
+
+	model = gtk_list_store_new((gint)N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+	hashes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+	while (buf_get_line(ibuf, &buf, &pnt, size)) {
+		if (*buf != '#') {
+			if (!strncasecmp(buf, "BAD ", 4))
+			{
+				strncpy(bad, buf + 4, 81);
+			}
+			else if(!strncasecmp(buf, "CASE ", 5))
+			{
+				case_sensitive = *(buf+5) == '0' ? FALSE : TRUE;
+			}
+			else if(!strncasecmp(buf, "COMPLETE ", 9))
+			{
+				complete = *(buf+9) == '0' ? FALSE : TRUE;
+			}
+			else if (!strncasecmp(buf, "GOOD ", 5))
+			{
+				strncpy(good, buf + 5, 255);
+
+				if (*bad && *good && g_hash_table_lookup(hashes, bad) == NULL) {
+					GtkTreeIter iter;
+
+					/* We don't actually need to store the good string, since this
+					 * hash is just being used to eliminate duplicate bad strings.
+					 * The value has to be non-NULL so the lookup above will work.
+					 */
+					g_hash_table_insert(hashes, g_strdup(bad), GINT_TO_POINTER(1));
+
+					if (!complete)
+						case_sensitive = TRUE;
+
+					gtk_list_store_append(model, &iter);
+					gtk_list_store_set(model, &iter,
+						BAD_COLUMN, bad,
+						GOOD_COLUMN, good,
+						WORD_ONLY_COLUMN, complete,
+						CASE_SENSITIVE_COLUMN, case_sensitive,
+						-1);
+				}
+				bad[0] = '\0';
+				complete = TRUE;
+				case_sensitive = FALSE;
+			}
+		}
+	}
+	g_free(ibuf);
+	g_hash_table_destroy(hashes);
+
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
+	                                     0, GTK_SORT_ASCENDING);
+}
+
+static GtkWidget *tree;
+static GtkWidget *bad_entry;
+static GtkWidget *good_entry;
+static GtkWidget *complete_toggle;
+static GtkWidget *case_toggle;
+
+static void save_list(void);
+
+static void on_edited(GtkCellRendererText *cellrenderertext,
+					  gchar *path, gchar *arg2, gpointer data)
+{
+	GtkTreeIter iter;
+	GValue val;
+
+	if (arg2[0] == '\0') {
+		gdk_beep();
+		return;
+	}
+
+	g_return_if_fail(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &iter, path));
+	val.g_type = 0;
+	gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, GPOINTER_TO_INT(data), &val);
+
+	if (strcmp(arg2, g_value_get_string(&val))) {
+		gtk_list_store_set(model, &iter, GPOINTER_TO_INT(data), arg2, -1);
+		save_list();
+	}
+	g_value_unset(&val);
+}
+
+
+static void word_only_toggled(GtkCellRendererToggle *cellrenderertoggle,
+						gchar *path, gpointer data){
+	GtkTreeIter iter;
+	gboolean enabled;
+
+	g_return_if_fail(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &iter, path));
+	gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
+					   WORD_ONLY_COLUMN, &enabled,
+					   -1);
+
+	gtk_list_store_set(GTK_LIST_STORE(model), &iter,
+					   WORD_ONLY_COLUMN, !enabled,
+					   -1);
+
+	/* I want to be sure that the above change has happened to the GtkTreeView first. */
+	gtk_list_store_set(GTK_LIST_STORE(model), &iter,
+					   CASE_SENSITIVE_COLUMN, enabled,
+					   -1);
+
+	save_list();
+}
+
+static void case_sensitive_toggled(GtkCellRendererToggle *cellrenderertoggle,
+						gchar *path, gpointer data){
+	GtkTreeIter iter;
+	gboolean enabled;
+
+	g_return_if_fail(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &iter, path));
+
+	/* Prevent the case sensitive column from changing on non-whole word replacements.
+	 * Ideally, the column would be set insensitive in the word_only_toggled callback. */
+	gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
+					   WORD_ONLY_COLUMN, &enabled,
+					   -1);
+	if (!enabled)
+		return;
+
+	gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
+					   CASE_SENSITIVE_COLUMN, &enabled,
+					   -1);
+
+	gtk_list_store_set(GTK_LIST_STORE(model), &iter,
+					   CASE_SENSITIVE_COLUMN, !enabled,
+					   -1);
+
+	save_list();
+}
+
+static void list_add_new()
+{
+	GtkTreeIter iter;
+	const char *word = gtk_entry_get_text(GTK_ENTRY(bad_entry));
+	gboolean case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(case_toggle));
+
+	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) {
+		char *tmpword = g_utf8_casefold(word, -1);
+
+		do {
+			GValue bad_val;
+			gboolean match;
+
+			bad_val.g_type = 0;
+			gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, BAD_COLUMN, &bad_val);
+
+			if (case_sensitive)
+			{
+				GValue case_sensitive_val;
+				case_sensitive_val.g_type = 0;
+				gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, CASE_SENSITIVE_COLUMN, &case_sensitive_val);
+
+				/* If they're both case-sensitive, then compare directly.
+				 * Otherwise, they overlap. */
+				if (g_value_get_boolean(&case_sensitive_val))
+				{
+					match = !strcmp(g_value_get_string(&bad_val), word);
+				}
+				else
+				{
+					char *bad = g_utf8_casefold(g_value_get_string(&bad_val), -1);
+					match = !strcmp(bad, tmpword);
+					g_free(bad);
+				}
+				g_value_unset(&case_sensitive_val);
+			}
+			else
+			{
+				char *bad = g_utf8_casefold(g_value_get_string(&bad_val), -1);
+				match = !strcmp(bad, tmpword);
+				g_free(bad);
+			}
+
+			if (match) {
+				g_value_unset(&bad_val);
+				g_free(tmpword);
+
+				gaim_notify_error(NULL, _("Duplicate Correction"),
+					_("The specified word already exists in the correction list."),
+					gtk_entry_get_text(GTK_ENTRY(bad_entry)));
+				return;
+			}
+
+			g_value_unset(&bad_val);
+
+		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
+
+		g_free(tmpword);
+	}
+
+
+	gtk_list_store_append(model, &iter);
+	gtk_list_store_set(model, &iter,
+		BAD_COLUMN, word,
+		GOOD_COLUMN, gtk_entry_get_text(GTK_ENTRY(good_entry)),
+		WORD_ONLY_COLUMN, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(complete_toggle)),
+		CASE_SENSITIVE_COLUMN, case_sensitive,
+		-1);
+
+	gtk_editable_delete_text(GTK_EDITABLE(bad_entry), 0, -1);
+	gtk_editable_delete_text(GTK_EDITABLE(good_entry), 0, -1);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(complete_toggle), TRUE);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(case_toggle), FALSE);
+	gtk_widget_grab_focus(bad_entry);
+
+	save_list();
+}
+
+static void add_selected_row_to_list(GtkTreeModel *model, GtkTreePath *path,
+	GtkTreeIter *iter, gpointer data)
+{
+	GtkTreeRowReference *row_reference;
+	GSList **list = (GSList **)data;
+	row_reference = gtk_tree_row_reference_new(model, path);
+	*list = g_slist_prepend(*list, row_reference);
+}
+
+static void remove_row(void *data1, gpointer data2)
+{
+	GtkTreeRowReference *row_reference;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+
+	row_reference = (GtkTreeRowReference *)data1;
+	path = gtk_tree_row_reference_get_path(row_reference);
+
+	if (gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
+		gtk_list_store_remove(model, &iter);
+
+	gtk_tree_path_free(path);
+	gtk_tree_row_reference_free(row_reference);
+}
+
+static void list_delete()
+{
+	GtkTreeSelection *sel;
+	GSList *list = NULL;
+
+	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+	gtk_tree_selection_selected_foreach(sel, add_selected_row_to_list, &list);
+
+	g_slist_foreach(list, remove_row, NULL);
+	g_slist_free(list);
+
+	save_list();
+}
+
+static void save_list()
+{
+	GString *data;
+	GtkTreeIter iter;
+
+	data = g_string_new("");
+
+	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) {
+		do {
+			GValue val0;
+			GValue val1;
+			GValue val2;
+			GValue val3;
+
+			val0.g_type = 0;
+			val1.g_type = 0;
+			val2.g_type = 0;
+			val3.g_type = 0;
+
+			gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, BAD_COLUMN, &val0);
+			gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, GOOD_COLUMN, &val1);
+			gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, WORD_ONLY_COLUMN, &val2);
+			gtk_tree_model_get_value(GTK_TREE_MODEL(model), &iter, CASE_SENSITIVE_COLUMN, &val3);
+
+			g_string_append_printf(data, "COMPLETE %d\nCASE %d\nBAD %s\nGOOD %s\n\n",
+								   g_value_get_boolean(&val2),
+								   g_value_get_boolean(&val3),
+								   g_value_get_string(&val0),
+								   g_value_get_string(&val1));
+
+			g_value_unset(&val0);
+			g_value_unset(&val1);
+			g_value_unset(&val2);
+			g_value_unset(&val3);
+
+		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
+	}
+
+	gaim_util_write_data_to_file("dict", data->str, -1);
+
+	g_string_free(data, TRUE);
+}
+
+#if !GTK_CHECK_VERSION(2,2,0)
+static void
+count_selected_helper(GtkTreeModel *model, GtkTreePath *path,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	(*(gint *)user_data)++;
+}
+#endif
+
+static void on_selection_changed(GtkTreeSelection *sel,
+	gpointer data)
+{
+	gint num_selected;
+#if GTK_CHECK_VERSION(2,2,0)
+	num_selected = gtk_tree_selection_count_selected_rows(sel);
+#else
+	gtk_tree_selection_selected_foreach(sel, count_selected_helper, &num_selected);
+#endif
+	gtk_widget_set_sensitive((GtkWidget*)data, (num_selected > 0));
+}
+
+static gboolean non_empty(const char *s)
+{
+	while (*s && g_ascii_isspace(*s))
+		s++;
+	return *s;
+}
+
+static void on_entry_changed(GtkEditable *editable, gpointer data)
+{
+	gtk_widget_set_sensitive((GtkWidget*)data,
+		non_empty(gtk_entry_get_text(GTK_ENTRY(bad_entry))) &&
+		non_empty(gtk_entry_get_text(GTK_ENTRY(good_entry))));
+}
+
+/*
+ *  EXPORTED FUNCTIONS
+ */
+
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
+	void *conv_handle = gaim_conversations_get_handle();
+	GList *convs;
+
+	load_conf();
+
+	/* Attach to existing conversations */
+	for (convs = gaim_get_conversations(); convs != NULL; convs = convs->next)
+	{
+		spellchk_new_attach((GaimConversation *)convs->data);
+	}
+
+	gaim_signal_connect(conv_handle, "conversation-created",
+			    plugin, GAIM_CALLBACK(spellchk_new_attach), NULL);
+
+	return TRUE;
+}
+
+static gboolean
+plugin_unload(GaimPlugin *plugin)
+{
+	GList *convs;
+
+	/* Detach from existing conversations */
+	for (convs = gaim_get_conversations(); convs != NULL; convs = convs->next)
+	{
+		GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION((GaimConversation *)convs->data);
+		spellchk *spell = g_object_get_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY);
+
+		g_signal_handlers_disconnect_by_func(gtkconv->entry, message_send_cb, spell);
+		g_object_set_data(G_OBJECT(gtkconv->entry), SPELLCHK_OBJECT_KEY, NULL);
+	}
+
+	return TRUE;
+}
+
+static void whole_words_button_toggled(GtkToggleButton *complete_toggle, GtkToggleButton *case_toggle)
+{
+	gboolean enabled = gtk_toggle_button_get_active(complete_toggle);
+
+	gtk_toggle_button_set_active(case_toggle, !enabled);
+	gtk_widget_set_sensitive(GTK_WIDGET(case_toggle), enabled);
+}
+
+static GtkWidget *
+get_config_frame(GaimPlugin *plugin)
+{
+	GtkWidget *ret, *vbox, *win;
+	GtkWidget *hbox, *label;
+	GtkWidget *button;
+	GtkSizeGroup *sg;
+	GtkSizeGroup *sg2;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+	GtkWidget *vbox2;
+	GtkWidget *hbox2;
+	GtkWidget *vbox3;
+
+	ret = gtk_vbox_new(FALSE, GAIM_HIG_CAT_SPACE);
+	gtk_container_set_border_width (GTK_CONTAINER(ret), GAIM_HIG_BORDER);
+
+	vbox = gaim_gtk_make_frame(ret, _("Text Replacements"));
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
+	gtk_widget_show(vbox);
+
+	win = gtk_scrolled_window_new(0, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), win, TRUE, TRUE, 0);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(win),
+										GTK_SHADOW_IN);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(win),
+			GTK_POLICY_NEVER,
+			GTK_POLICY_ALWAYS);
+	gtk_widget_show(win);
+
+	tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
+	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
+	gtk_widget_set_size_request(tree, -1, 200);
+
+	renderer = gtk_cell_renderer_text_new();
+	g_object_set(G_OBJECT(renderer),
+		"editable", TRUE,
+		NULL);
+	g_signal_connect(G_OBJECT(renderer), "edited",
+		G_CALLBACK(on_edited), GINT_TO_POINTER(0));
+	column = gtk_tree_view_column_new_with_attributes(_("You type"), renderer,
+													  "text", BAD_COLUMN,
+													  NULL);
+	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+	gtk_tree_view_column_set_fixed_width(column, 150);
+	gtk_tree_view_column_set_resizable(column, TRUE);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+
+	renderer = gtk_cell_renderer_text_new();
+	g_object_set(G_OBJECT(renderer),
+		"editable", TRUE,
+		NULL);
+	g_signal_connect(G_OBJECT(renderer), "edited",
+		G_CALLBACK(on_edited), GINT_TO_POINTER(1));
+	column = gtk_tree_view_column_new_with_attributes(_("You send"), renderer,
+													  "text", GOOD_COLUMN,
+													  NULL);
+	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+	gtk_tree_view_column_set_fixed_width(column, 150);
+	gtk_tree_view_column_set_resizable(column, TRUE);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+
+	renderer = gtk_cell_renderer_toggle_new();
+	g_object_set(G_OBJECT(renderer),
+		"activatable", TRUE,
+		NULL);
+	g_signal_connect(G_OBJECT(renderer), "toggled",
+		G_CALLBACK(word_only_toggled), NULL);
+	column = gtk_tree_view_column_new_with_attributes(_("Whole words only"), renderer,
+													  "active", WORD_ONLY_COLUMN,
+													  NULL);
+	gtk_tree_view_column_set_resizable(column, TRUE);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+
+	renderer = gtk_cell_renderer_toggle_new();
+	g_object_set(G_OBJECT(renderer),
+		"activatable", TRUE,
+		NULL);
+	g_signal_connect(G_OBJECT(renderer), "toggled",
+		G_CALLBACK(case_sensitive_toggled), NULL);
+	column = gtk_tree_view_column_new_with_attributes(_("Case sensitive"), renderer,
+													  "active", CASE_SENSITIVE_COLUMN,
+													  NULL);
+	gtk_tree_view_column_set_resizable(column, TRUE);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+
+	gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)),
+		 GTK_SELECTION_MULTIPLE);
+	gtk_container_add(GTK_CONTAINER(win), tree);
+	gtk_widget_show(tree);
+
+	hbox = gtk_hbutton_box_new();
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	gtk_widget_show(hbox);
+
+	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			   G_CALLBACK(list_delete), NULL);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_widget_set_sensitive(button, FALSE);
+
+	g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree))),
+		"changed", G_CALLBACK(on_selection_changed), button);
+
+	gtk_widget_show(button);
+
+	vbox = gaim_gtk_make_frame(ret, _("Add a new text replacement"));
+
+	hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
+	gtk_widget_show(hbox);
+	vbox2 = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
+	gtk_widget_show(vbox2);
+
+	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+	sg2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+
+	hbox2 = gtk_hbox_new(FALSE, 2);
+	gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0);
+	gtk_widget_show(hbox2);
+
+	label = gtk_label_new_with_mnemonic(_("You _type:"));
+	gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);
+	gtk_size_group_add_widget(sg, label);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+
+	bad_entry = gtk_entry_new();
+	/* Set a minimum size. Since they're in a size group, the other entry will match up. */
+	gtk_widget_set_size_request(bad_entry, 350, -1);
+	gtk_box_pack_start(GTK_BOX(hbox2), bad_entry, TRUE, TRUE, 0);
+	gtk_size_group_add_widget(sg2, bad_entry);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), bad_entry);
+	gtk_widget_show(bad_entry);
+
+	hbox2 = gtk_hbox_new(FALSE, 2);
+	gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0);
+	gtk_widget_show(hbox2);
+
+	label = gtk_label_new_with_mnemonic(_("You _send:"));
+	gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);
+	gtk_size_group_add_widget(sg, label);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+
+	good_entry = gtk_entry_new();
+	gtk_box_pack_start(GTK_BOX(hbox2), good_entry, TRUE, TRUE, 0);
+	gtk_size_group_add_widget(sg2, good_entry);
+	gtk_label_set_mnemonic_widget(GTK_LABEL(label), good_entry);
+	gtk_widget_show(good_entry);
+
+	/* Created here so it can be passed to whole_words_button_toggled. */
+	case_toggle = gtk_check_button_new_with_mnemonic(_("_Exact case match (uncheck for automatic case handling)"));
+
+	complete_toggle = gtk_check_button_new_with_mnemonic(_("Only replace _whole words"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(complete_toggle), TRUE);
+	g_signal_connect(G_OBJECT(complete_toggle), "clicked",
+                         G_CALLBACK(whole_words_button_toggled), case_toggle);
+	gtk_widget_show(complete_toggle);
+	gtk_box_pack_start(GTK_BOX(vbox2), complete_toggle, FALSE, FALSE, 0);
+
+	/* The button is created above so it can be passed to whole_words_button_toggled. */
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(case_toggle), FALSE);
+	gtk_widget_show(case_toggle);
+	gtk_box_pack_start(GTK_BOX(vbox2), case_toggle, FALSE, FALSE, 0);
+
+	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			   G_CALLBACK(list_add_new), NULL);
+	vbox3 = gtk_vbox_new(FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox3, TRUE, FALSE, 0);
+	gtk_widget_show(vbox3);
+	gtk_box_pack_end(GTK_BOX(vbox3), button, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(bad_entry), "changed", G_CALLBACK(on_entry_changed), button);
+	g_signal_connect(G_OBJECT(good_entry), "changed", G_CALLBACK(on_entry_changed), button);
+	gtk_widget_set_sensitive(button, FALSE);
+	gtk_widget_show(button);
+
+#if 0
+	vbox = gaim_gtk_make_frame(ret, _("General Text Replacement Options"));
+	gaim_gtk_prefs_checkbox(_("Enable replacement of last word on send"),
+	                        "/plugins/gtk/spellchk/last_word_replace", vbox);
+#endif
+
+	gtk_widget_show_all(ret);
+	return ret;
+}
+
+static GaimGtkPluginUiInfo ui_info =
+{
+	get_config_frame,
+	0 /* page_num (Reserved) */
+};
+
+static GaimPluginInfo info =
+{
+	GAIM_PLUGIN_MAGIC,
+	GAIM_MAJOR_VERSION,
+	GAIM_MINOR_VERSION,
+	GAIM_PLUGIN_STANDARD,
+	GAIM_GTK_PLUGIN_TYPE,
+	0,
+	NULL,
+	GAIM_PRIORITY_DEFAULT,
+	SPELLCHECK_PLUGIN_ID,
+	N_("Text replacement"),
+	VERSION,
+	N_("Replaces text in outgoing messages according to user-defined rules."),
+	N_("Replaces text in outgoing messages according to user-defined rules."),
+	"Eric Warmenhoven <eric@warmenhoven.org>",
+	GAIM_WEBSITE,
+	plugin_load,
+	plugin_unload,
+	NULL,
+	&ui_info,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void
+init_plugin(GaimPlugin *plugin)
+{
+#if 0
+	gaim_prefs_add_none("/plugins");
+	gaim_prefs_add_none("/plugins/gtk");
+	gaim_prefs_add_none("/plugins/gtk/spellchk");
+	gaim_prefs_add_bool("/plugins/gtk/spellchk/last_word_replace", TRUE);
+#endif
+}
+
+GAIM_INIT_PLUGIN(spellcheck, init_plugin, info)