/* Copyright 1997 Paolo Molaro 
   This is GPL'ed software.
 */
#include <gnome.h>
#include "gtkspell.h"

#include <string.h>
#include <ctype.h>

/* TODO: make a last word button backup a change in the spell dialog 
		 implement parse funcs
		 parse mode auto-switch (use the regexp)
		 mixed FIXMEs
		 i18n
		 configuration
		 grammar checker (is there a free one?)
		 completion?
*/

static void gispell_open();
static void gispell_save();
static void gispell_saveas();
static void gispell_close();
static void gispell_check();
static void gispell_save_config();
static void gispell_exit();

static void found_word(GtkSpell* spell, GtkSpellInfo* si);
static void handled_word(GtkSpell* spell, GtkSpellInfo* si);

/* parse modes: maybe tex modes requires a state... */
static gchar* parse_text(GtkText* text, guint *index);
static gchar* parse_html(GtkText* text, guint *index);
static gchar* parse_roff(GtkText* text, guint *index);
static gchar* parse_tex(GtkText* text, guint *index);

#define TEXT_CHAR(text, index) ((index<((text)->gap_position))?*((text)->text+index):\
	*(((text)->text)+((text)->gap_size)+index))
#define TEXT_LEN(text) (((text)->text_end) - ((text)->gap_size))

typedef struct {
	gchar* label;
	gchar* regexp;
	gchar* (*next_word)(GtkText* text, guint *index);
	GtkWidget* button;
} spellparse;

GtkMenuEntry 
menu_items [] = {
	{"<Main>/File/Open", "<control>N", gispell_open, NULL},
	{"<Main>/File/Save", "<control>S", gispell_save, NULL},
	{"<Main>/File/Save as...", NULL, gispell_saveas, NULL},
	{"<Main>/File/Close", "<control>W", gispell_close, NULL},
	{"<Main>/File/<separator>", NULL, NULL, NULL},
	{"<Main>/File/Spell check", "<control>C", gispell_check, NULL},
	{"<Main>/File/<separator>", NULL, NULL, NULL},
	{"<Main>/File/Save config", NULL, gispell_save_config, NULL},
	{"<Main>/File/Exit", "<control>Q", gispell_exit, NULL}
};

int nmenu_items = sizeof(menu_items)/sizeof(menu_items[0]);
GtkMenuFactory* factory = NULL;
GtkMenuFactory* subfactory[1];
GHashTable* entry_ht = NULL;

spellparse
parsemodes [] = {
	{"Text", "*", parse_text},
	{"TeX", "*.tex", parse_tex},
	{"HTML", "*.html", parse_html},
	{"*roff", "*.[1-9]", parse_roff}
};
const int nparsemodes = sizeof(parsemodes)/sizeof(parsemodes[0]);
spellparse * parsemode = &parsemodes[0];

/* config options */
gchar*	opt_command;
gchar*	opt_dictionary;
gchar*	opt_personaldict;
gchar*	opt_validchars;
gchar*	opt_wordslongher;
gint	opt_sort;
gint	opt_runtogether;
gint	opt_discardonkill;
gint	opt_tooltips;

/* the spellchecker dialog */
static GtkWidget* dialog;
static GtkWidget* spell;
static GtkWidget* text;

static gint found_word_id = 0;
static gint handled_word_id = 0;
/* file info */
static GtkWidget* filesel = 0;
static guint text_index = 0;
static guint text_start = 0;
static gchar* file_name = NULL;
static gint file_modified = 0;
static gint word_handled = 0;

/* menu crap */
gint
menus_install_accel (GtkWidget* widget, gchar* signal_name, gchar key, gchar mod, gchar* path) {
	gchar accel[64];
	gchar* t1, t2[2];

	accel[0] = 0;
	if ( mod & GDK_CONTROL_MASK )
		strcat(accel, "<control>");
	if ( mod & GDK_SHIFT_MASK )
		strcat(accel, "<shift>");
	if ( mod & GDK_MOD1_MASK )
		strcat(accel, "<alt>");

	t2[0] = key;
	t2[1] = 0;
	strcat(accel, t2);

	if ( entry_ht ) {
		t1 = g_hash_table_lookup(entry_ht, path);
		g_free(t1);
	} else 
		entry_ht = g_hash_table_new(g_string_hash, g_string_equal);

	g_hash_table_insert(entry_ht, path, g_strdup(accel));
	return TRUE;
}

void
menus_remove_accel(GtkWidget* widget, gchar* signal_name, gchar* path) {
	gchar* t;

	if ( entry_ht ) {
		t = g_hash_table_lookup(entry_ht, path);
		g_free(t);
		g_hash_table_insert(entry_ht, path, g_strdup(""));
	}
}

void
get_main_menu (GtkWidget** menubar, GtkAcceleratorTable** table) {
	gchar* accel;
	gint i;
	
	if ( factory )
		return;

	factory = gtk_menu_factory_new (GTK_MENU_FACTORY_MENU_BAR);
	subfactory[0] = gtk_menu_factory_new (GTK_MENU_FACTORY_MENU_BAR);

	gtk_menu_factory_add_subfactory(factory, subfactory[0], "<Main>");

	if ( entry_ht ) {
		for ( i=0; i < nmenu_items; i++) {
			accel = g_hash_table_lookup(entry_ht, menu_items[i].path);
			if ( accel ) 
				menu_items[i].accelerator = accel[0] == '\0' ? NULL: accel;
		}
	}
	gtk_menu_factory_add_entries(factory, menu_items, nmenu_items);

	for ( i=0; i < nmenu_items; ++i ) {
		if ( menu_items[i].widget ) {
			gtk_signal_connect(GTK_OBJECT(menu_items[i].widget),
				"install_accelerator", (GtkSignalFunc)menus_install_accel,
				menu_items[i].path);
			gtk_signal_connect(GTK_OBJECT(menu_items[i].widget),
				"remove_accelerator", (GtkSignalFunc)menus_remove_accel,
				menu_items[i].path);
		}
	}

	if ( menubar )
		*menubar = subfactory[0]->widget;
	if ( table )
		*table = subfactory[0]->table;
}

static void
menus_set_sensitive (gchar* path, gint sensitive) {
	GtkMenuPath* mpath;

	mpath = gtk_menu_factory_find(factory, path);
	if (mpath)
		gtk_widget_set_sensitive(mpath->widget, sensitive);
	else
		g_warning("Cannot find menu entry: %s", path);
}

/* config crap */
void
gispell_config(GtkSpell* spell) {
	gnome_config_set_prefix("/gispell/config/");
	opt_command = gnome_config_get_string("command=ispell");
	opt_dictionary = gnome_config_get_string("dictionary=default");
	opt_personaldict = gnome_config_get_string("personaldict=");
	opt_validchars = gnome_config_get_string("validchars=");
	opt_wordslongher = gnome_config_get_string("wordslongher=2");
	opt_sort = gnome_config_get_int("sort=0");
	opt_runtogether = gnome_config_get_int("runtogether=0");
	opt_discardonkill = gnome_config_get_int("discardonkill=0");
	opt_tooltips = gnome_config_get_int("tooltips=1");

	gtk_entry_set_text(GTK_ENTRY(spell->command), opt_command);
	gtk_entry_set_text(GTK_ENTRY(spell->dict), opt_dictionary);
	gtk_entry_set_text(GTK_ENTRY(spell->pdict), opt_personaldict);
	gtk_entry_set_text(GTK_ENTRY(spell->wchars), opt_validchars);
	gtk_entry_set_text(GTK_ENTRY(spell->wlen), opt_wordslongher);
	if ( opt_sort ) 
		gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(spell->sort), opt_sort);
	if ( opt_runtogether ) 
		gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(spell->runtog), opt_runtogether);
	if ( opt_discardonkill ) 
		gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(spell->discard), opt_discardonkill);
	if ( opt_tooltips ) 
		gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(spell->tooltip), opt_tooltips);
}

/* parse crap */
static gchar* 
parse_text (GtkText* text, guint *old_index ) {
	gchar c;
	gchar buf[1024];
	guint i, len;
	guint index;

	index = *old_index;
	len = TEXT_LEN(GTK_TEXT(text));
	i= 0;
	/* skip non isalnum chars */
	for ( ; index < len; ++index ) {
		c = TEXT_CHAR(text, index);
		if ( isalnum(c) ) break;
	}
	if ( index == len ) {
		*old_index = index;
		return NULL;
	}
	buf[i]= c;
	text_start = index;
	++index;
	for ( ; index < len; ++index ) {
		c = TEXT_CHAR(text, index);
		if ( isalnum(c) ) {
			buf[++i] = c;
		} else
			break;
	}
	buf[i+1] = 0;

	/*g_print("PARSE: `%s' at %d (starting %d)\n", buf, index-strlen(buf), *old_index);*/
	*old_index = index;
	return g_strdup(buf);
}

static gchar*
parse_tex (GtkText* text, guint *old_index) {
	return parse_text(text, old_index);
}

static gchar*
parse_html (GtkText* text, guint *old_index) {
	return parse_text(text, old_index);
}

static gchar*
parse_roff (GtkText* text, guint *old_index) {
	return parse_text(text, old_index);
}

static void
gispell_toggle_mode (GtkToggleButton *button) {
	gint i;

	if ( button->active ) {
		for ( i=0; i < nparsemodes; ++i) {
			gtk_widget_set_sensitive(parsemodes[i].button, 1);
			if ( GTK_TOGGLE_BUTTON(parsemodes[i].button)->active ) {
				if ( parsemodes[i].button != GTK_WIDGET(button) )
					gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(parsemodes[i].button), 0);
				else
					parsemode = &parsemodes[i];
			}
		}
		gtk_widget_set_sensitive(GTK_WIDGET(button), 0);
	}
}

GtkWidget*
gispell_toolbar () {
	GtkWidget* button;
	GtkWidget* hbox;
	GtkWidget* label;
	gint i;

	hbox = gtk_hbox_new (TRUE, 5);
	label = gtk_label_new("Spell mode: ");
	gtk_widget_show(label);
	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
	for ( i=0; i < nparsemodes; ++i ) {
		button = gtk_toggle_button_new_with_label(parsemodes[i].label);
		gtk_widget_show(button);
		gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
		parsemodes[i].button = button;
		gtk_signal_connect(GTK_OBJECT(button), "toggled",
			(GtkSignalFunc)gispell_toggle_mode, NULL);
	}

	gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(parsemodes[0].button), TRUE);
	gtk_widget_set_sensitive(parsemodes[0].button, 0);

	gtk_widget_show(hbox);
	return hbox;
}

void
gispell_load_file(GtkWidget* text, gchar* filename) {

	FILE* f;
	gchar buf[1024];
	guint len;

	text_index = 0;
	word_handled = 1;
	len = TEXT_LEN(GTK_TEXT(text));
	if (len) {
		gtk_text_freeze(GTK_TEXT(text));
		gtk_text_set_point(GTK_TEXT(text), 0);
		gtk_text_foreward_delete(GTK_TEXT(text), len);
		gtk_text_thaw(GTK_TEXT(text));
	}

	file_modified = 0;

	if ( file_name ) {
		g_free(file_name);
		file_name = NULL;
	}
	if ( !(f=fopen(filename, "r")) ) {
		g_error("Cannot open %s\n", filename);
		return;
	}

	gtk_text_freeze(GTK_TEXT(text));
	while ( fgets(buf, 1024, f) ) {
		gtk_text_insert(GTK_TEXT(text), NULL, &text->style->black, NULL, buf, -1);
	}
	gtk_text_thaw(GTK_TEXT(text));

	fclose(f);
	file_name = g_strdup(filename);
}

static int
close_window() {
	if ( file_modified )
		return FALSE;
	return TRUE;
}

static void
destroy_window(GtkWidget* win) {
	if ( win == dialog )
		dialog = NULL;
	else if ( win == filesel )
		filesel = NULL;
	else
		gtk_exit(0); /* warn if unsaved */
}

GtkWidget*
gispell_window() {
	GtkWidget* win, *vbox;
	GtkWidget* table, *hs, *vs;
	GtkWidget* menubar, *toolbar;
	GtkAdjustment *hadj, *vadj;

	GtkAcceleratorTable* accel;

	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_signal_connect(GTK_OBJECT(win), "destroy",
		(GtkSignalFunc)destroy_window, NULL);
	gtk_signal_connect(GTK_OBJECT(win), "delete_event",
		(GtkSignalFunc)close_window, NULL);
	vbox = gtk_vbox_new(FALSE, 1);
	gtk_widget_show(vbox);
	gtk_container_add(GTK_CONTAINER(win), vbox);

	get_main_menu(&menubar , &accel);
	gtk_window_add_accelerator_table(GTK_WINDOW(win), accel);
	gtk_widget_show(menubar);
	gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);

	toolbar = gispell_toolbar ();
	gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, TRUE, 0);

	table = gtk_table_new(2, 2, FALSE);
	gtk_container_add(GTK_CONTAINER(vbox), table);

	hadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
	hs = gtk_hscrollbar_new(hadj);
	gtk_widget_show(hs);
	gtk_table_attach(GTK_TABLE(table), hs, 0, 1, 1, 2, GTK_FILL|GTK_EXPAND, 0, 0, 0);

	vadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
	vs = gtk_vscrollbar_new(vadj);
	gtk_widget_show(vs);
	gtk_table_attach(GTK_TABLE(table), vs, 1, 2, 0, 1, 0, GTK_FILL|GTK_EXPAND, 0, 0);

	text = gtk_text_new(hadj, vadj);
	gtk_table_attach_defaults(GTK_TABLE(table), text, 0, 1, 0, 1);

	gtk_text_set_editable(GTK_TEXT(text), FALSE);
	gtk_widget_show(text);
	gtk_text_freeze(GTK_TEXT(text));
	gtk_widget_realize(text);
	gtk_text_thaw(GTK_TEXT(text));
	gtk_widget_set_usize(text, 300, 400);

	gtk_widget_show(table);

	return win;
}

static int 
close_dialog(GtkWidget* win) {
	gtk_widget_hide(win);
	gtk_grab_remove(win);
	return FALSE;
}

static void
filesel_ok(GtkWidget* button) {
	gchar* t;
	gchar* name;

	close_dialog(filesel);
	name = gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel));
	t = GTK_WINDOW(filesel)->title;
	if ( !name || ! *name )
		return;
	if ( t && *t == 'O' ) { /* HACK! HACK! :bad for i18n */
		gispell_load_file(text, name);
	} else if ( t && *t == 'S' ) {
		if ( file_name )
			g_free(file_name);
		file_name = g_strdup(name);
		gispell_save();
	}
}

static void 
create_filesel(gchar* title) {

	if ( !filesel ) {
		filesel = gtk_file_selection_new(title);
		gtk_signal_connect(GTK_OBJECT(filesel), "destroy",
			(GtkSignalFunc)destroy_window, NULL);
		gtk_signal_connect(GTK_OBJECT(filesel), "delete_event",
			(GtkSignalFunc)close_dialog, NULL);
		gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), 
			"clicked", (GtkSignalFunc)filesel_ok, NULL);
		gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), 
			"clicked", (GtkSignalFunc)close_dialog, GTK_OBJECT(filesel));
	} else
		gtk_window_set_title(GTK_WINDOW(filesel), title);
}

static void 
gispell_open() {
	if ( file_modified )
		return;
	if ( spell ) {
		/* FIXME */
		gtk_signal_disconnect(GTK_OBJECT(spell), found_word_id);
		gtk_signal_disconnect(GTK_OBJECT(spell), handled_word_id);
		while (gtk_spell_next(GTK_SPELL(spell)));
		found_word_id = gtk_signal_connect(GTK_OBJECT(spell), "found_word",
			(GtkSignalFunc)found_word, NULL);
		handled_word_id = gtk_signal_connect(GTK_OBJECT(spell), "handled_word",
			(GtkSignalFunc)handled_word, NULL);
	}
	create_filesel("Open");
	/*if ( file_name )
		gtk_file_selection_set_filename(GTK_FILE_SELECTION(filesel), file_name);*/
	gtk_widget_show(filesel);
	gtk_grab_add(filesel);
	return;
}

static void 
gispell_save() {
	FILE* f;
	size_t nbytes, result;
	gchar* buf;
	gchar* name;

	name = alloca(strlen(file_name)+5);
	sprintf(name, "%s.new", file_name); /* FIXME: should not exist! */

	if ( !(f=fopen(name, "w")) ) {
		g_warning("Cannot open %s", name);
		return;
	}

	buf = GTK_TEXT(text)->text;
	nbytes = GTK_TEXT(text)->gap_position;
	result = fwrite(buf, nbytes, 1, f);
	if ( nbytes && result != 1 ) {
		fclose(f);
		unlink(name);
		g_warning("Disk full(1)!");
		return;
	}
	buf = GTK_TEXT(text)->text+GTK_TEXT(text)->gap_position+GTK_TEXT(text)->gap_size;
	nbytes = TEXT_LEN(GTK_TEXT(text)) - nbytes;
	result = fwrite(buf, nbytes, 1, f);
	if ( nbytes && result != 1 ) {
		fclose(f);
		unlink(name);
		g_warning("Disk full(2)!");
		return;
	}
	fclose(f);
	unlink(file_name);
	if (link(name, file_name)) { /* maybe use rename? */
		g_warning("Cannot link %s", file_name);
	}
	unlink(name);
	file_modified = 0;
}

static void 
gispell_saveas() {
	create_filesel("Save as");
	if ( file_name )
		gtk_file_selection_set_filename(GTK_FILE_SELECTION(filesel), file_name);
	gtk_widget_show(filesel);
	gtk_grab_add(filesel);
	return;
}

static void 
gispell_close() {
	if ( file_modified ) 
		return;
	gtk_exit(0);
}

static void 
gispell_check() {
	gtk_widget_show(dialog);
	gtk_grab_add(dialog);
	return;
}

static void 
gispell_save_config() {

	gnome_config_set_prefix("/gispell/config/");

	gnome_config_set_string("command", gtk_entry_get_text(GTK_ENTRY(GTK_SPELL(spell)->command)));
	gnome_config_set_string("dictionary", gtk_entry_get_text(GTK_ENTRY(GTK_SPELL(spell)->dict)));
	gnome_config_set_string("personaldict", gtk_entry_get_text(GTK_ENTRY(GTK_SPELL(spell)->pdict)));
	gnome_config_set_string("validchars", gtk_entry_get_text(GTK_ENTRY(GTK_SPELL(spell)->wchars)));
	gnome_config_set_string("wordslongher", gtk_entry_get_text(GTK_ENTRY(GTK_SPELL(spell)->wlen)));

	gnome_config_set_int("sort", GTK_TOGGLE_BUTTON(GTK_SPELL(spell)->sort)->active);
	gnome_config_set_int("runtogether", GTK_TOGGLE_BUTTON(GTK_SPELL(spell)->runtog)->active);
	gnome_config_set_int("discardonkill", GTK_TOGGLE_BUTTON(GTK_SPELL(spell)->discard)->active);
	gnome_config_set_int("tooltips", GTK_TOGGLE_BUTTON(GTK_SPELL(spell)->tooltip)->active);

	gnome_config_sync();
	return;
}

static void 
gispell_exit() {
	if ( file_modified ) 
		return;
	gtk_exit(0);
}

static gint
start_check() {
	gchar* word=NULL;
	guint myindex;

	myindex = text_index;
	while ( word_handled && (word = parsemode->next_word(GTK_TEXT(text), &text_index)) ) {
		/*word_handled = 0;*/
		/*g_print("CHECKING `%s'\n", word);*/
		word_handled = !gtk_spell_check(GTK_SPELL(spell), word);
	}
	if (word_handled && !word)
		gdk_beep();

	return FALSE;
}

static void
found_word(GtkSpell* spell, GtkSpellInfo* si) {
	guint start, len;

	len = strlen(si->word);
	start = text_index - len;
	if ( start != text_start ) {
		g_warning("BOGUS START: %d, %d (%s)", start, text_start, si->word);
		/*g_debug("./gispell");*/
		return;
	}
	g_print("Found word: `%s' at index %d\n", si->word, start);
	gtk_text_freeze(GTK_TEXT(text));
	gtk_text_set_point(GTK_TEXT(text), start);
	gtk_text_foreward_delete(GTK_TEXT(text), len);
	gtk_text_insert(GTK_TEXT(text), NULL, &text->style->white, &text->style->black, si->word, -1);
	gtk_text_thaw(GTK_TEXT(text));

}

static void
handled_word (GtkSpell* spell, GtkSpellInfo* si) {
	guint start, len;

	if ( !word_handled ) {
		gchar* r = si->replacement?si->replacement:si->word;
		len = strlen(si->word);
		start = text_index - len;
		if ( start != text_start ) {
			g_warning("BOGUS START2: %d, %d", start, text_start);
			return;
		}
		if ( si->replacement ) {
			file_modified = 1;
			text_index += strlen(si->replacement) - strlen(si->word);
			g_print("Replace word: `%s' at index %d WITH `%s'\n", si->word, start, si->replacement);
		} else
			g_print("Handled word: `%s' at index %d\n", si->word, start);

		gtk_text_freeze(GTK_TEXT(text));
		gtk_text_set_point(GTK_TEXT(text), start);
		gtk_text_foreward_delete(GTK_TEXT(text), len);
		gtk_text_insert(GTK_TEXT(text), NULL, &text->style->black, NULL, r, strlen(r));
		gtk_text_thaw(GTK_TEXT(text));

		word_handled = 1;
		g_free(si->original); /* HACK! HACK! */
		si->original = NULL;
		/* uncomment the next to get continuos checking (and bugs:-)*/
		gtk_idle_add(start_check, NULL);  /*FIXME: put in an idle handler? */
	} /* replace did it */
}

/* tentative hack to stack windows */
int
stack_window(GtkWindow* win, GdkEvent* e, GtkWindow** d ) {
	/* if ( d && *d && GTK_WIDGET_VISIBLE(*d) )
		gtk_window_raise(*d);*/
	return FALSE;
}

int 
main(int argc, char* argv[]) {
	GtkWidget* button;
	GtkWidget* win;

	gnome_init(&argc, &argv);
	word_handled = 1;

	dialog = gtk_dialog_new();
	gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
		(GtkSignalFunc)destroy_window, NULL);
	gtk_signal_connect(GTK_OBJECT(dialog), "delete_event",
		(GtkSignalFunc)close_dialog, NULL);
	spell = gtk_spell_new();
	found_word_id = gtk_signal_connect(GTK_OBJECT(spell), "found_word",
		(GtkSignalFunc)found_word, NULL);
	handled_word_id = gtk_signal_connect(GTK_OBJECT(spell), "handled_word",
		(GtkSignalFunc)handled_word, NULL);
	/*gtk_signal_connect(GTK_OBJECT(spell), "replace_word",
		(GtkSignalFunc)handled_word, NULL);*/
	gtk_widget_show(spell);
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), spell);
	button = gtk_button_new_with_label("Start");
	gtk_widget_show(button);
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), button);
	gtk_signal_connect(GTK_OBJECT(button), "clicked",
		(GtkSignalFunc)start_check, NULL);
	button = gtk_button_new_with_label("Close");
	gtk_widget_show(button);
	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), button);
	gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
		(GtkSignalFunc)close_dialog, GTK_OBJECT(dialog));

	gispell_config(GTK_SPELL(spell));

	win = gispell_window();
	gtk_signal_connect(GTK_OBJECT(win), "expose_event",
		(GtkSignalFunc)stack_window, &dialog);

	if ( argc > 1 ) {
		gispell_load_file(text, argv[1]);
	}

	gtk_widget_show(win);
	gtk_main();
	return 0;
}

