#include <stdlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "guiutils.h"
#include "csd.h"
#include "fsd.h"
#include "styleedit.h"
#include "stacklist.h"

#include "optwin.h"
#include "optwinop.h"
#include "config.h"

#include "images/icon_ok_20x20.xpm"
#include "images/icon_select_20x20.xpm"
#include "images/icon_save_20x20.xpm"
#include "images/icon_cancel_20x20.xpm"
#include "images/icon_close_20x20.xpm"


/* Callbacks */
static gint OptWinDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);
static void OptWinSwitchPageCB(
	GtkNotebook *notebook, GtkNotebookPage *page, guint page_num,
	gpointer data
);
static void OptWinOKCB(GtkWidget *widget, gpointer data);
static void OptWinApplyCB(GtkWidget *widget, gpointer data);
static void OptWinSaveCB(GtkWidget *widget, gpointer data);
static void OptWinCancelCB(GtkWidget *widget, gpointer data);
static void OptWinAnyChangedCB(GtkWidget *widget, gpointer data);
static void OptWinCListChangedCB(
	GtkCList *clist, gint row, gint column, GdkEvent *event,
	gpointer data
);
static void OptWinStyleEditChangedCB(
	style_edit_struct *se, gpointer data
);
static void OptWinStackListChangedCB(
	stack_list_struct *slist, gpointer data
);

/* Widget References */
optwin_wref_struct *OptWinWRefNew(
	GtkWidget *w,
	GtkWidget *browse,	/* Browse button */
	const gchar *cfg_parm,	/* Cfg item parameter */
	gpointer optwin
);
void OptWinWRefDelete(optwin_wref_struct *wref);
void OptWinWRefSetColorButton(
	optwin_wref_struct *wref, GtkWidget *color_btn
);
void OptWinWRefSetStyleEdit(
	optwin_wref_struct *wref, style_edit_struct *se
);
void OptWinWRefSetStackList(
	optwin_wref_struct *wref, stack_list_struct *slist
);
optwin_wref_struct *OptWinWRefAppend(
	optwin_struct *optwin,
	GtkWidget *w,
	GtkWidget *browse,	/* Browse button */
	const gchar *cfg_parm	/* Cfg item parameter */
);

/* Options Window */
optwin_struct *OptWinNew(
	gpointer core_ptr, const gchar *title, guint8 **icon_data
);
GtkWidget *OptWinPageAppend(
	optwin_struct *optwin, GtkWidget *w
);
void OptWinUpdateMenus(optwin_struct *optwin);
void OptWinSetBusy(optwin_struct *optwin, gboolean is_busy);
gboolean OptWinIsMapped(optwin_struct *optwin);
void OptWinMap(optwin_struct *optwin);
void OptWinUnmap(optwin_struct *optwin);
void OptWinDelete(optwin_struct *optwin);


#define OPTWIN_WIDTH		640
#define OPTWIN_HEIGHT		480

#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)


/*
 *	GtkWindow "delete_event" signal callback.
 */
static gint OptWinDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	optwin_struct *optwin = OPTWIN(data);
	if(optwin == NULL)
	    return(TRUE);

	OptWinCancelCB(NULL, optwin);

	return(TRUE);
}

/*
 *	GtkNotebook "switch_page" signal callback.
 */
static void OptWinSwitchPageCB(
	GtkNotebook *notebook, GtkNotebookPage *page, guint page_num,
	gpointer data
)
{
	optwin_struct *optwin = OPTWIN(data);
	if(optwin == NULL)
	    return;

	if(optwin->freeze_count > 0)
	    return;
	else
	    optwin->freeze_count++;



	OptWinUpdateMenus(optwin);

	optwin->freeze_count--;
}

/*
 *	OK callback.
 */
static void OptWinOKCB(GtkWidget *widget, gpointer data)
{
	optwin_struct *optwin = OPTWIN(data);
	if(optwin == NULL)
	    return;

	if(optwin->freeze_count > 0)
	    return;

	optwin->freeze_count++;

	OptWinDoApply(optwin);
	OptWinResetHasChanges(optwin, FALSE);
	OptWinUnmap(optwin);

	optwin->freeze_count--;
}

/*
 *	Apply callback.
 */
static void OptWinApplyCB(GtkWidget *widget, gpointer data)
{
	optwin_struct *optwin = OPTWIN(data);
	if(optwin == NULL)
	    return;

	if(optwin->freeze_count > 0)
	    return;

	optwin->freeze_count++;

	OptWinDoApply(optwin);
	OptWinResetHasChanges(optwin, FALSE);
	OptWinUpdateMenus(optwin);

	optwin->freeze_count--;
}

/*
 *	Save callback.
 */
static void OptWinSaveCB(GtkWidget *widget, gpointer data)
{
	optwin_struct *optwin = OPTWIN(data);
	if(optwin == NULL)
	    return;

	if(optwin->freeze_count > 0)
	    return;

	optwin->freeze_count++;

/* TODO */


	OptWinUpdateMenus(optwin);

	optwin->freeze_count--;
}

/*
 *	Cancel callback.
 */
static void OptWinCancelCB(GtkWidget *widget, gpointer data)
{
	optwin_struct *optwin = OPTWIN(data);
	if(optwin == NULL)
	    return;

	if(optwin->freeze_count > 0)
	    return;
	else
	    optwin->freeze_count++;

	OptWinUnmap(optwin);

	optwin->freeze_count--;
}

/*
 *	Any GtkWidget changed signal callback.
 *
 *	For widgets referenced in the Option Window's Widget References
 *	that have been changed, this will update the Widget References's
 *	has_changes marker and the Option Window's has_changes marker.
 *
 *	The given widget must be ignored, instead use wref->w.
 */
static void OptWinAnyChangedCB(GtkWidget *widget, gpointer data)
{
	optwin_struct *optwin;
	optwin_wref_struct *wref = OPTWIN_WREF(data);
	if(wref == NULL)
	    return;

	/* Must use Widget Reference's widget (may be NULL) */
	widget = wref->w;

	/* Ignore changes? */
	if(wref->ignore_changes)
	    return;

	optwin = OPTWIN(wref->optwin);
	if(optwin == NULL)
	    return;

	if(optwin->freeze_count > 0)
	    optwin->freeze_count++;

	if(!wref->has_changes)
	{
	    wref->has_changes = TRUE;
	    OptWinUpdateMenus(optwin);
	}

	if(!optwin->has_changes)
	{
	    optwin->has_changes = TRUE;
	    OptWinUpdateMenus(optwin);
	}

	optwin->freeze_count--;
}
static void OptWinCListChangedCB(
	GtkCList *clist, gint row, gint column, GdkEvent *event,
	gpointer data
)
{
	OptWinAnyChangedCB(NULL, data);
}
static void OptWinStyleEditChangedCB(
	style_edit_struct *se, gpointer data
)
{
	OptWinAnyChangedCB(NULL, data);
}
static void OptWinStackListChangedCB(
	stack_list_struct *slist, gpointer data
)
{
	OptWinAnyChangedCB(NULL, data);
}


/*
 *	Creates a new Options Window Widget Reference.
 */
optwin_wref_struct *OptWinWRefNew(
	GtkWidget *w,
	GtkWidget *browse,	/* Browse button */
	const gchar *cfg_parm,	/* Cfg item parameter */
	gpointer optwin
)
{
	optwin_wref_struct *wref = OPTWIN_WREF(
	    g_malloc0(sizeof(optwin_wref_struct))
	);
	if(wref == NULL)
	    return(wref);

	wref->w = w;
	wref->browse = browse;
	wref->cfg_parm = STRDUP(cfg_parm);
	wref->optwin = optwin;
	wref->has_changes = FALSE;
	wref->ignore_changes = FALSE;

	wref->radio_value = 0;
	wref->slist = NULL;

	return(wref);
}

/*
 *	Deletes the Options Window Widget Reference.
 */
void OptWinWRefDelete(optwin_wref_struct *wref)
{
	if(wref == NULL)
	    return;

	GTK_WIDGET_DESTROY(wref->w)
	GTK_WIDGET_DESTROY(wref->browse)

	GTK_WIDGET_DESTROY(wref->color_btn)

	StyleEditDelete(wref->style_edit);

	StackListDelete(wref->slist);

	g_free(wref->cfg_parm);

	g_free(wref);
}

/*
 *	Sets the Widget Reference to the Color Button.
 */
void OptWinWRefSetColorButton(
	optwin_wref_struct *wref, GtkWidget *color_btn
)
{
	if(wref == NULL)
	    return;

	wref->color_btn = color_btn;

	if(color_btn != NULL)
	{
	    GtkWidget *w = CSDColorButtonGetButton(color_btn);
	    gtk_signal_connect(
		GTK_OBJECT(w), "clicked",
		GTK_SIGNAL_FUNC(OptWinAnyChangedCB), wref
	    );
	}
}

/*
 *	Sets the Widget Reference to the Style Edit.
 */
void OptWinWRefSetStyleEdit(
	optwin_wref_struct *wref, style_edit_struct *se
)
{
	if(wref == NULL)
	    return;

	wref->style_edit = se;

	if(se != NULL)
	{
	    StyleEditSetChangedCB(
		se,
		OptWinStyleEditChangedCB,
		wref
	    );
	}
}

/*
 *	Sets the Widget Reference to the Stack List.
 */
void OptWinWRefSetStackList(
	optwin_wref_struct *wref, stack_list_struct *slist
)
{
	if(wref == NULL)
	    return;

	wref->slist = slist;

	if(slist != NULL)
	{
	    StackListSetChangedCB(
		slist,
		OptWinStackListChangedCB,
		wref
	    );
	}
}

/*
 *	Appends a new Widget Reference to the Options Window.
 *	the new wref structure.
 */
optwin_wref_struct *OptWinWRefAppend(
	optwin_struct *optwin,
	GtkWidget *w,
	GtkWidget *browse,	/* Browse button */
	const gchar *cfg_parm	/* Cfg item parameter */
)
{
	gint i;
	optwin_wref_struct *wref;

	if(optwin == NULL)
	    return(NULL);

	/* Allocate more pointers */
	i = MAX(optwin->total_wrefs, 0);
	optwin->total_wrefs = i + 1;

	optwin->wref = (optwin_wref_struct **)g_realloc(
	    optwin->wref,
	    optwin->total_wrefs * sizeof(optwin_wref_struct *)
	);
	if(optwin->wref == NULL)
	{
	    optwin->total_wrefs = 0;
	    return(NULL);
	}

	/* Create a new widget reference */
	optwin->wref[i] = wref = OptWinWRefNew(
	    w, browse, cfg_parm, optwin
	);
	if(wref != NULL)
	{
	    void (*optwin_change_cb)(GtkWidget *, gpointer) =
		OptWinAnyChangedCB;
	    gpointer data = wref;
	    guint sig_id = 0;

	    /* Attach changed callback depending on widget type */
	    if(w != NULL)
	    {
		if(GTK_IS_CLIST(w))
		{
		    sig_id = gtk_signal_connect(
			GTK_OBJECT(w), "select_row",
			GTK_SIGNAL_FUNC(OptWinCListChangedCB), data
		    );
		}
		else if(GTK_IS_COMBO(w))
		{
		    GtkCombo *combo = GTK_COMBO(w);
		    GtkEntry *entry = GTK_ENTRY(combo->entry);
		    sig_id = gtk_signal_connect(
			GTK_OBJECT(entry), "changed",
			GTK_SIGNAL_FUNC(optwin_change_cb), data
		    );
		}
		else if(GTK_IS_EDITABLE(w))
		{
		    sig_id = gtk_signal_connect(
			GTK_OBJECT(w), "changed",
			GTK_SIGNAL_FUNC(optwin_change_cb), data
		    );
		}
		else if(GTK_IS_CHECK_MENU_ITEM(w))
		{
		    sig_id = gtk_signal_connect(
			GTK_OBJECT(w), "toggled",
			GTK_SIGNAL_FUNC(optwin_change_cb), data
		    );
		}
		else if(GTK_IS_MENU_ITEM(w))
		{
		    sig_id = gtk_signal_connect(
			GTK_OBJECT(w), "activate",
			GTK_SIGNAL_FUNC(optwin_change_cb), data
		    );
		}
		else if(GTK_IS_TOGGLE_BUTTON(w))
		{
		    sig_id = gtk_signal_connect(
			GTK_OBJECT(w), "toggled",
			GTK_SIGNAL_FUNC(optwin_change_cb), data
		    );
		}
		else if(GTK_IS_BUTTON(w))
		{
		    sig_id = gtk_signal_connect(
			GTK_OBJECT(w), "clicked",
			GTK_SIGNAL_FUNC(optwin_change_cb), data
		    );
		}
		else if(GTK_IS_RANGE(w))
		{
		    GtkAdjustment *adj = GTK_RANGE(w)->adjustment;
		    sig_id = gtk_signal_connect(
			GTK_OBJECT(adj), "value_changed",
			GTK_SIGNAL_FUNC(optwin_change_cb), data
		    );
		}
		else
		{
#if 0
		    g_printerr(
"OptWinWRefAppend(): Warning:\
 Unsupported widget type.\n"
		    );
#endif
		}
	    }
#undef optwin_change_cb
	}

	return(wref);
}


/*
 *	Creates a new Options Window.
 */
optwin_struct *OptWinNew(
	gpointer core_ptr, const gchar *title, guint8 **icon_data
)
{
	const gint border_major = 5;
	GdkWindow *window;
	GtkAccelGroup *accelgrp;
	GtkStyle *style;
	GtkWidget *w, *parent, *parent2;
	optwin_struct *optwin = OPTWIN(
	    g_malloc0(sizeof(optwin_struct))
	);
	if(optwin == NULL)
	    return(optwin);

	optwin->accelgrp = accelgrp = gtk_accel_group_new();
	optwin->processing = FALSE;
	optwin->busy_count = 0;
	optwin->freeze_count = 0;
	optwin->core_ptr = core_ptr;

	optwin->has_changes = FALSE;

	optwin->title = STRDUP(title);
	optwin->icon_data = icon_data;


	/* Begin creating widgets */

	/* Toplevel */
	optwin->toplevel = w = gtk_window_new(GTK_WINDOW_DIALOG);
	gtk_widget_set_usize(w, OPTWIN_WIDTH, OPTWIN_HEIGHT);
	gtk_window_set_title(GTK_WINDOW(w), title);
	gtk_window_set_wmclass(
	    GTK_WINDOW(w), "options", PROG_NAME
	);
	gtk_widget_realize(w);
	window = w->window;
	if(window != NULL)
	{
	    gdk_window_set_decorations(
		window,
		GDK_DECOR_BORDER | GDK_DECOR_TITLE | GDK_DECOR_MENU |
		GDK_DECOR_MINIMIZE
	    );
	    gdk_window_set_functions(
		window,
		GDK_FUNC_MOVE | GDK_FUNC_MINIMIZE | GDK_FUNC_CLOSE
	    );
	    GUISetWMIcon(window, (guint8 **)icon_data);
	}
	gtk_signal_connect(
	    GTK_OBJECT(w), "delete_event",
	    GTK_SIGNAL_FUNC(OptWinDeleteEventCB), optwin
	);
	gtk_window_add_accel_group(GTK_WINDOW(w), accelgrp);
	style = gtk_widget_get_style(w);
	parent = w;


	/* Main vbox */
	w = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent), w);
	gtk_widget_show(w);
	parent = w;


	/* Vbox for main notebook */
	w = gtk_vbox_new(FALSE, 0);
	gtk_container_border_width(GTK_CONTAINER(w), border_major);
	gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Main notebook */
	optwin->notebook = w = gtk_notebook_new();
	gtk_notebook_set_tab_pos(GTK_NOTEBOOK(w), GTK_POS_TOP);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_notebook_set_scrollable(GTK_NOTEBOOK(w), TRUE);
	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(w), TRUE);
	gtk_notebook_set_show_border(GTK_NOTEBOOK(w), TRUE);
/*	gtk_notebook_set_page(GTK_NOTEBOOK(w), 0); */
	gtk_signal_connect(
	    GTK_OBJECT(w), "switch_page",
	    GTK_SIGNAL_FUNC(OptWinSwitchPageCB), optwin
	);
	gtk_widget_show(w);
	parent2 = w;



	/* Separator */
	w = gtk_hseparator_new();
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);


	/* Buttons hbox */
	w = gtk_hbox_new(TRUE, 0);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, border_major);
	gtk_widget_show(w);
	parent2 = w;

	/* OK button */
	optwin->ok_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_ok_20x20_xpm,
	    "OK",
	    NULL
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_widget_set_usize(
	    w,
	    GUI_BUTTON_HLABEL_WIDTH_DEF, GUI_BUTTON_HLABEL_HEIGHT_DEF
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(OptWinOKCB), optwin
	);
	gtk_accel_group_add(
	    accelgrp, GDK_o, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	GUIButtonLabelUnderline(w, GDK_o);
	gtk_widget_show(w);

	/* Apply button */
	optwin->apply_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_select_20x20_xpm,
#if defined(PROG_LANGUAGE_SPANISH)
"Aplique"
#elif defined(PROG_LANGUAGE_FRENCH)
"S'Appliquer"
#elif defined(PROG_LANGUAGE_GERMAN)
"Verwenden"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Applicare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Toepas"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Aplique"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Gjeld"
#else
"Apply"
#endif
	    , NULL
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_widget_set_usize(
	    w,
	    GUI_BUTTON_HLABEL_WIDTH_DEF, GUI_BUTTON_HLABEL_HEIGHT_DEF
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(OptWinApplyCB), optwin
	);
	gtk_accel_group_add(
	    accelgrp, GDK_a, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	GUIButtonLabelUnderline(w, GDK_a);
	gtk_widget_show(w);

	/* Save button */
	optwin->save_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_save_20x20_xpm,
#if defined(PROG_LANGUAGE_SPANISH)
"Salve"
#elif defined(PROG_LANGUAGE_FRENCH)
"Epargner"
#elif defined(PROG_LANGUAGE_GERMAN)
"Außer"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Risparmiare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Red"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Poupe"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Untatt"
#else
"Save"
#endif
	    , NULL
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_widget_set_usize(
	    w,
	    GUI_BUTTON_HLABEL_WIDTH_DEF, GUI_BUTTON_HLABEL_HEIGHT_DEF
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(OptWinSaveCB), optwin
	);
	gtk_accel_group_add(
	    accelgrp, GDK_s, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	GUIButtonLabelUnderline(w, GDK_s);

	/* Cancel button */
	optwin->cancel_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_cancel_20x20_xpm,
#if defined(PROG_LANGUAGE_SPANISH)
"Cancele"
#elif defined(PROG_LANGUAGE_FRENCH)
"Annuler"
#elif defined(PROG_LANGUAGE_GERMAN)
"Heben"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Annullare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Annuleer"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Cancelamento"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Kanseller"
#else
"Cancel"
#endif
	    , NULL
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_widget_set_usize(
	    w,
	    GUI_BUTTON_HLABEL_WIDTH_DEF, GUI_BUTTON_HLABEL_HEIGHT_DEF
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(OptWinCancelCB), optwin
	);
	gtk_accel_group_add(
	    accelgrp, GDK_Escape, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	gtk_accel_group_add(
	    accelgrp, GDK_c, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	GUIButtonLabelUnderline(w, GDK_c);

	/* Close button */
	optwin->close_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_close_20x20_xpm,
#if defined(PROG_LANGUAGE_SPANISH)
"Cierre"
#elif defined(PROG_LANGUAGE_FRENCH)
"Fin"
#elif defined(PROG_LANGUAGE_GERMAN)
"Nah"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Vicino"
#elif defined(PROG_LANGUAGE_DUTCH)
"Einde"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Próximo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Nær"
#else
"Close"
#endif
	    , NULL
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_widget_set_usize(
	    w,
	    GUI_BUTTON_HLABEL_WIDTH_DEF, GUI_BUTTON_HLABEL_HEIGHT_DEF
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(OptWinCancelCB), optwin
	);
	gtk_accel_group_add(
	    accelgrp, GDK_Escape, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	gtk_accel_group_add(
	    accelgrp, GDK_c, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	GUIButtonLabelUnderline(w, GDK_c);


	return(optwin);
}

/*
 *	Appends a new page to the Options Window.
 *
 *	The tab widget is specified by w and should not be referenced
 *	again after this call.
 */
GtkWidget *OptWinPageAppend(
	optwin_struct *optwin, GtkWidget *w
)
{
	gint i;
	GtkWidget *main_vbox;
	GtkNotebook *notebook;


#define DESTROY_WIDGET(w)	\
{ if((w) != NULL) { gtk_widget_destroy(w); } }

	if(optwin == NULL)
	{
	    DESTROY_WIDGET(w);
	    return(NULL);
	}

	notebook = (GtkNotebook *)optwin->notebook;
	if(notebook == NULL)
	{
	    DESTROY_WIDGET(w);
	    return(NULL);
	}

	/* If no label widget is given then create one */
	if(w == NULL)
	{
	    gchar *s = g_strdup_printf(
		"Page %i",
		optwin->total_pages + 1
	    );
	    w = gtk_label_new(s);
	    g_free(s);
	}

	/* Create main GtkVBox for the new page */
	main_vbox = gtk_vbox_new(FALSE, 0);
	gtk_notebook_append_page(notebook, main_vbox, w);
	gtk_widget_show(main_vbox);


	/* Record new main GtkVBox for the appended page */
	i = MAX(optwin->total_pages, 0);
	optwin->total_pages = i + 1;

	optwin->page = (GtkWidget **)g_realloc(
	    optwin->page,
	    optwin->total_pages * sizeof(GtkWidget *)
	);
	if(optwin->page == NULL)
	{
	    optwin->total_pages = 0;
	}
	else
	{
	    optwin->page[i] = main_vbox;
	}

	/* Return new main GtkVBox for the appended page */
	return(main_vbox);
#undef DESTROY_WIDGET
}

/*
 *	Updates the Options Window widgets to reflect current values.
 */
void OptWinUpdateMenus(optwin_struct *optwin)
{
	gboolean sensitive, has_changes;
	GtkWidget *w;

	if(optwin == NULL)
	    return;

	has_changes = optwin->has_changes;

#define MAP(w)                  \
{ if((w) != NULL) { gtk_widget_show(w); } }
#define UNMAP(w)                \
{ if((w) != NULL) { gtk_widget_hide(w); } }

	/* Update title */
	w = optwin->toplevel;
	if(w != NULL)
	{
	    gchar *title = g_strdup_printf(
		"%s%s",
		STRISEMPTY(optwin->title) ? "Untitled" : optwin->title,
		(has_changes) ? " (*)" : ""
	    );
	    gtk_window_set_title(GTK_WINDOW(w), title);
	    g_free(title);
	}

	/* Update OK, Apply, Save, Cancel Buttons */
	sensitive = has_changes;
	GTK_WIDGET_SET_SENSITIVE(optwin->ok_btn, sensitive)
	GTK_WIDGET_SET_SENSITIVE(optwin->apply_btn, sensitive)
	GTK_WIDGET_SET_SENSITIVE(optwin->save_btn, sensitive)
	if(has_changes)
	{
	    MAP(optwin->cancel_btn);
	    UNMAP(optwin->close_btn);
	}
	else
	{
	    UNMAP(optwin->cancel_btn);
	    MAP(optwin->close_btn);
	}

#undef UNMAP
#undef MAP
}

/*
 *	Sets the Options Window as busy or ready.
 */
void OptWinSetBusy(optwin_struct *optwin, gboolean is_busy)
{
#if 0
	GdkCursor *cursor;
	GtkWidget *w;
	edv_core_struct *core_ptr;


	if(optwin == NULL)
	    return;

	core_ptr = EDV_CORE(optwin->core_ptr);
	if(core_ptr == NULL)
	    return;

	w = optwin->toplevel;
	if(w != NULL)
	{
	    if(is_busy)
	    {
		/* Increase busy count */
		optwin->busy_count++;

		/* If already busy then don't change anything */
		if(optwin->busy_count > 1)
		    return;

		cursor = EDVGetCursor(core_ptr, EDV_CURSOR_CODE_BUSY);
	    }
	    else
	    {
		/* Reduce busy count */
		optwin->busy_count--;
		if(optwin->busy_count < 0)
		    optwin->busy_count = 0;

		/* If still busy do not change anything */
		if(optwin->busy_count > 0)
		    return;

		cursor = NULL;  /* Use default cursor */
	    }

	    /* Update toplevel window's cursor */
	    if(w->window != NULL)
	    {
		gdk_window_set_cursor(w->window, cursor);
		gdk_flush();
	    }
	}
#endif
}

/*
 *	Checks if the Options Window is mapped.
 */
gboolean OptWinIsMapped(optwin_struct *optwin)
{
	GtkWidget *w = (optwin != NULL) ? optwin->toplevel : NULL;
	return((w != NULL) ? GTK_WIDGET_MAPPED(w) : FALSE);
}

/*
 *	Maps the Options Window.
 */
void OptWinMap(optwin_struct *optwin)
{
	GtkWidget *w = (optwin != NULL) ? optwin->toplevel : NULL;
	if(w == NULL)
	    return;

	gtk_widget_show_raise(w);

	w = optwin->ok_btn;
	if(w != NULL)
	{
	    gtk_widget_grab_focus(w);
	    gtk_widget_grab_default(w);
	}
}

/*
 *	Unmaps the Options Window.
 */
void OptWinUnmap(optwin_struct *optwin)
{
	GtkWidget *w = (optwin != NULL) ? optwin->toplevel : NULL;
	if(w == NULL)
	    return;

	gtk_widget_hide(w);
}

/*
 *	Deletes the Options Window.
 */
void OptWinDelete(optwin_struct *optwin)
{
	gint i;

	if(optwin == NULL)
	    return;

	/* Delete widget references */
	for(i = 0; i < optwin->total_wrefs; i++)
	    OptWinWRefDelete(optwin->wref[i]);
	g_free(optwin->wref);
	optwin->wref = NULL;
	optwin->total_wrefs = 0;

	/* Delete toplevel widgets for each page */
	for(i = 0; i < optwin->total_pages; i++)
	    GTK_WIDGET_DESTROY(optwin->page[i])
	g_free(optwin->page);
	optwin->page = NULL;
	optwin->total_pages = 0;

	GTK_WIDGET_DESTROY(optwin->notebook)
	optwin->notebook = NULL;

	GTK_WIDGET_DESTROY(optwin->ok_btn)
	optwin->ok_btn = NULL;
	GTK_WIDGET_DESTROY(optwin->apply_btn)
	optwin->apply_btn = NULL;
	GTK_WIDGET_DESTROY(optwin->save_btn)
	optwin->save_btn = NULL;
	GTK_WIDGET_DESTROY(optwin->cancel_btn)
	optwin->cancel_btn = NULL;
	GTK_WIDGET_DESTROY(optwin->close_btn)
	optwin->close_btn = NULL;

	GTK_WIDGET_DESTROY(optwin->toplevel)
	optwin->toplevel = NULL;

	GTK_ACCEL_GROUP_UNREF(optwin->accelgrp)
	optwin->accelgrp = NULL;

	g_free(optwin->title);
	optwin->title = NULL;

	g_free(optwin);
}
