/*
** 1999-03-16 -	Configure controls. Initially, this will only have global keyboard shortcuts
**		to deal with, but it may get more in the future. This module relies heavily
**		on the services provided by the 'controls' module, of course.
*/

#include "gentoo.h"

#include "cmdseq_dialog.h"

#include "controls.h"
#include "guiutil.h"

#include "cfg_gui.h"
#include "cfg_module.h"
#include "cfg_cmdseq.h"				/* For ccs_current(). */
#include "cfg_controls.h"

#define	NODE	"Controls"

/* ----------------------------------------------------------------------------------------- */

typedef struct {
	GtkWidget	*vbox;			/* Root page container. Very standard. */

	GtkWidget	*kframe;		/* Keyboard frame. */
	GtkWidget	*kscwin;		/* Scrolling window for keyboard shortcuts. */
	GtkWidget	*kclist;		/* Actual list holding the keyboard shortcuts. */
	GtkWidget	*kdtable;		/* Key definition table. */
	GtkWidget	*kdkey;			/* Definition key name (entry). */
	GtkWidget	*kdcmd;			/* Definition cmdsequence (entry). */
	GtkWidget	*kdcmdpick;		/* Definition command pick button. */
	GtkWidget	*khbox;			/* Horizontal box for keyboard add & del commands. */
	GtkWidget	*kadd;			/* Keyboard add button. */
	GtkWidget	*kdel;			/* Keyboard delete button. */

	MainInfo	*min;			/* This is handy sometimes. */
	gboolean	modified;		/* Indicates that a change has been made. */
	CtrlInfo	*ctrlinfo;		/* The control info we're editing. */
	CtrlKey		*curr_key;		/* Current key (if selected). */
	gint		curr_key_row;		/* Row of current key in list. */
} P_Controls;

static P_Controls	the_page;

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-16 -	Set key definition widgets to non-idle state. */
static void set_key_widgets(P_Controls *page)
{
	if(page->curr_key != NULL)
	{
		const gchar	*cmdseq;

		gtk_entry_set_text(GTK_ENTRY(page->kdkey), ctrl_key_get_keyname(page->curr_key));
		if((cmdseq = ctrl_key_get_cmdseq(page->curr_key)) != NULL)
			gtk_entry_set_text(GTK_ENTRY(page->kdcmd), cmdseq);
		gtk_widget_set_sensitive(page->kdtable, TRUE);
		gtk_widget_set_sensitive(page->kdel, TRUE);
	}
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-16 -	Reset the key definition widgets to their idle state. */
static void reset_key_widgets(P_Controls *page)
{
	gtk_entry_set_text(GTK_ENTRY(page->kdkey), "");
	gtk_entry_set_text(GTK_ENTRY(page->kdcmd), "");
	gtk_widget_set_sensitive(page->kdtable, FALSE);
	gtk_widget_set_sensitive(page->kadd, TRUE);
	gtk_widget_set_sensitive(page->kdel, FALSE);
}

/* 1999-03-16 -	Reset all widgets on page to their most idle state. */
static void reset_widgets(P_Controls *page)
{
	page->curr_key = NULL;
	reset_key_widgets(page);
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-16 -	Populate keyboard clist. */
static void populate_key_clist(P_Controls *page)
{
	gchar	*crow[] = { NULL, NULL };
	GSList	*list, *iter;

	gtk_clist_clear(GTK_CLIST(page->kclist));
	gtk_clist_freeze(GTK_CLIST(page->kclist));
	if((list = ctrl_keys_get_list(page->ctrlinfo)) != NULL)
	{
		gint	row;

		for(iter = list; iter != NULL; iter = g_slist_next(iter))
		{
			crow[0] = (gchar *) ctrl_key_get_keyname((CtrlKey *) iter->data);
			crow[1] = (gchar *) ctrl_key_get_cmdseq((CtrlKey *) iter->data);
			row = gtk_clist_append(GTK_CLIST(page->kclist), crow);
			gtk_clist_set_row_data(GTK_CLIST(page->kclist), row, iter->data);
		}
	}
	gtk_clist_thaw(GTK_CLIST(page->kclist));
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-16 -	Keyboard clist selection signal handler. */
static gint evt_kclist_select(GtkWidget *wid, gint row, gint column, GdkEventButton *evt, gpointer user)
{
	P_Controls	*page = user;

	page->curr_key = gtk_clist_get_row_data(GTK_CLIST(page->kclist), row);
	page->curr_key_row = row;
	set_key_widgets(page);

	return TRUE;
}

/* 1999-03-16 -	Keyboard clist unselection handler. */
static gint evt_kclist_unselect(GtkWidget *wid, gint row, gint column, GdkEventButton *evt, gpointer user)
{
	P_Controls	*page = user;

	page->curr_key = NULL;
	page->curr_key_row = -1;
	reset_key_widgets(page);

	return TRUE;
}

/* 1999-03-16 -	User pressed something in the key definition entry. Convert to name and set it. */
static gint evt_kkey_press(GtkWidget *wid, GdkEventKey *evt, gpointer user)
{
	P_Controls	*page = user;

	if((page != NULL) && (page->curr_key != NULL))
	{
		gchar	*keyname = gtk_accelerator_name(evt->keyval, evt->state);

		page->modified = TRUE;
		ctrl_key_set_keyname(page->ctrlinfo, page->curr_key, keyname);
		gtk_clist_set_text(GTK_CLIST(page->kclist), page->curr_key_row, 0, keyname);
		set_key_widgets(page);
	}
	return TRUE;
}

/* 1999-03-16 -	User edited the command sequence, so store the new one. */
static gint evt_kcmd_changed(GtkWidget *wid, gpointer user)
{
	P_Controls	*page = user;

	if((page != NULL) && (page->curr_key != NULL))
	{
		gchar	*cmdseq = gtk_entry_get_text(GTK_ENTRY(wid));

		if(!ctrl_key_has_cmdseq(page->ctrlinfo, page->curr_key, cmdseq))
		{
			page->modified = TRUE;
			ctrl_key_set_cmdseq(page->ctrlinfo, page->curr_key, cmdseq);
			gtk_clist_set_text(GTK_CLIST(page->kclist), page->curr_key_row, 1, cmdseq);
		}
	}

	return TRUE;
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-16 -	User clicked the "pick" button for key command sequence.
** 1999-03-29 -	Rewritten for new, simpler, csq_dialog() semantics.
*/
static gint evt_kcmdpick_clicked(GtkWidget *wid, gpointer user)
{
	P_Controls	*page = user;
	const gchar	*cmd;

	if((cmd = csq_dialog(page->min, ccs_get_current())) != NULL)
		gtk_entry_set_text(GTK_ENTRY(page->kdcmd), cmd);
	return TRUE;
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-16 -	User just hit the "Add" key button so, er, add a key. */
static gint evt_kadd_clicked(GtkWidget *wid, gpointer user)
{
	P_Controls	*page = user;

	if(page != NULL)
	{
		gchar	*crow[] = { NULL, NULL };
		gint	row;
		CtrlKey	*key;

		key = ctrl_key_add_unique(page->ctrlinfo);
		crow[0] = (gchar *) ctrl_key_get_keyname(key);
		crow[1] = (gchar *) ctrl_key_get_cmdseq(key);
		row = gtk_clist_append(GTK_CLIST(page->kclist), crow);
		gtk_clist_set_row_data(GTK_CLIST(page->kclist), row, key);
		gtk_clist_select_row(GTK_CLIST(page->kclist), row, -1);
		gtk_widget_grab_focus(page->kdkey);
		page->modified = TRUE;
	}

	return TRUE;
}

/* 1999-03-16 -	User hit the "Delete" button for keys. */
static gint evt_kdel_clicked(GtkWidget *wid, gpointer user)
{
	P_Controls	*page = user;

	if((page != NULL) && (page->curr_key != NULL))
	{
		gint	row = page->curr_key_row;

		ctrl_key_remove(page->ctrlinfo, page->curr_key);
		gtk_clist_remove(GTK_CLIST(page->kclist), row);
		while(row >= GTK_CLIST(page->kclist)->rows)
			row--;
		gtk_clist_select_row(GTK_CLIST(page->kclist), row, -1);
		page->modified = TRUE;
	}
	return TRUE;
}

/* ----------------------------------------------------------------------------------------- */

static GtkWidget * cct_init(MainInfo *min, char **name)
{
	P_Controls	*page = &the_page;
	GtkWidget	*vbox, *label;

	if(name == NULL)
		return NULL;

	*name = "Controls";

	page->min = min;
	page->modified = FALSE;

	page->vbox = gtk_vbox_new(FALSE, 0);

	page->kframe = gtk_frame_new("Global Keyboard Shortcuts");
	vbox = gtk_vbox_new(FALSE, 0);
	page->kscwin = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(page->kscwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	page->kclist = gtk_clist_new(2);
	gtk_clist_set_column_auto_resize(GTK_CLIST(page->kclist), 0, TRUE);
	gtk_clist_set_column_min_width(GTK_CLIST(page->kclist), 0, 80);
	gtk_signal_connect(GTK_OBJECT(page->kclist), "select_row", GTK_SIGNAL_FUNC(evt_kclist_select), page);
	gtk_signal_connect(GTK_OBJECT(page->kclist), "unselect_row", GTK_SIGNAL_FUNC(evt_kclist_unselect), page);
	gtk_container_add(GTK_CONTAINER(page->kscwin), page->kclist);
	gtk_widget_show(page->kclist);
	gtk_box_pack_start(GTK_BOX(vbox), page->kscwin, TRUE, TRUE, 0);
	gtk_widget_show(page->kscwin);
	page->kdtable = gtk_table_new(2, 3, FALSE);
	label = gtk_label_new("Key");
	gtk_table_attach(GTK_TABLE(page->kdtable), label, 0, 1, 0, 1,  0,0,0,0);
	gtk_widget_show(label);
	page->kdkey = gtk_entry_new();
	gtk_signal_connect(GTK_OBJECT(page->kdkey), "key_press_event", GTK_SIGNAL_FUNC(evt_kkey_press), page);
	gtk_entry_set_editable(GTK_ENTRY(page->kdkey), FALSE);
	gtk_table_attach(GTK_TABLE(page->kdtable), page->kdkey, 1, 3, 0, 1,  GTK_EXPAND|GTK_FILL,0,0,0);
	gtk_widget_show(page->kdkey);
	label = gtk_label_new("Command");
	gtk_table_attach(GTK_TABLE(page->kdtable), label, 0, 1, 1, 2,  0,0,0,0);
	gtk_widget_show(label);
	page->kdcmd = gtk_entry_new();
	gtk_signal_connect(GTK_OBJECT(page->kdcmd), "changed", GTK_SIGNAL_FUNC(evt_kcmd_changed), page);
	gtk_table_attach(GTK_TABLE(page->kdtable), page->kdcmd, 1, 2, 1, 2,  GTK_EXPAND|GTK_FILL,0,0,0);
	gtk_widget_show(page->kdcmd);
	page->kdcmdpick = gui_details_button_new(min->gui->window->window);
	gtk_signal_connect(GTK_OBJECT(page->kdcmdpick), "clicked", GTK_SIGNAL_FUNC(evt_kcmdpick_clicked), page);
	gtk_table_attach(GTK_TABLE(page->kdtable), page->kdcmdpick, 2, 3, 1, 2,  0,0,0,0);
	gtk_widget_show(page->kdcmdpick);
	gtk_box_pack_start(GTK_BOX(vbox), page->kdtable, FALSE, FALSE, 0);
	gtk_widget_show(page->kdtable);
	page->khbox = gtk_hbox_new(FALSE, 0);
	page->kadd = gtk_button_new_with_label("Add");
	gtk_signal_connect(GTK_OBJECT(page->kadd), "clicked", GTK_SIGNAL_FUNC(evt_kadd_clicked), page);
	gtk_box_pack_start(GTK_BOX(page->khbox), page->kadd, TRUE, TRUE, 5);
	gtk_widget_show(page->kadd);
	page->kdel = gtk_button_new_with_label("Delete");
	gtk_signal_connect(GTK_OBJECT(page->kdel), "clicked", GTK_SIGNAL_FUNC(evt_kdel_clicked), page);
	gtk_box_pack_start(GTK_BOX(page->khbox), page->kdel, TRUE, TRUE, 5);
	gtk_widget_show(page->kdel);
	gtk_box_pack_start(GTK_BOX(vbox), page->khbox, FALSE, FALSE, 5);
	gtk_widget_show(page->khbox);
	gtk_container_add(GTK_CONTAINER(page->kframe), vbox);
	gtk_widget_show(vbox);
	gtk_box_pack_start(GTK_BOX(page->vbox), page->kframe, TRUE, TRUE, 0);
	gtk_widget_show(page->kframe);
	
	gtk_widget_show(page->vbox);
	return page->vbox;
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-16 -	Update the controls config page by grabbing current settings. */
static void cct_update(MainInfo *min)
{
	the_page.ctrlinfo = ctrl_copy(min->cfg.ctrlinfo);
	populate_key_clist(&the_page);
	reset_widgets(&the_page);
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-17 -	The user just accepted the settings, so make them current. */
static void cct_accept(MainInfo *min)
{
	P_Controls	*page = &the_page;

	if(page->modified)
	{
		ctrl_destroy(min->cfg.ctrlinfo);
		min->cfg.ctrlinfo = page->ctrlinfo;
		page->ctrlinfo = NULL;
		cfg_set_flags(CFLG_RESET_KEYBOARD);
		page->modified = FALSE;
	}
}

/* ----------------------------------------------------------------------------------------- */

static void keys_save(CtrlInfo *ctrl, FILE *out)
{
	GSList	*keys, *iter;

	xml_put_node_open(out, "Keys");
	if((keys = ctrl_keys_get_list(ctrl)) != NULL)
	{
		for(iter = keys; iter != NULL; iter = g_slist_next(iter))
		{
			xml_put_node_open(out, "Key");
			xml_put_text(out, "keyname", ctrl_key_get_keyname((CtrlKey *) iter->data));
			xml_put_text(out, "cmdseq", ctrl_key_get_cmdseq((CtrlKey *) iter->data));
			xml_put_node_close(out, "Key");
		}
		g_slist_free(keys);
	}
	xml_put_node_close(out, "Keys");
}

/* 1999-03-17 -	Save the current control settings right out in given <file>. */
static gint cct_save(MainInfo *min, FILE *out)
{
	xml_put_node_open(out, "Controls");
	keys_save(min->cfg.ctrlinfo, out);
	xml_put_node_close(out, "Controls");

	return TRUE;
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-03-17 -	Load in and install a single key mapping. */
static void key_load(XmlNode *node, gpointer user)
{
	char	*name, *cmd;

	if(xml_get_text(node, "keyname", &name) && xml_get_text(node, "cmdseq", &cmd))
		ctrl_key_add(((MainInfo *) user)->cfg.ctrlinfo, name, cmd);
}

/* 1999-03-17 -	Load in all key mappings. */
static void keys_load(MainInfo *min, XmlNode *node)
{
	xml_node_visit_children(node, key_load, min);
}

/* 1999-03-17 -	Load in the control config information. Replaces current. */
static void cct_load(MainInfo *min, XmlNode *node)
{
	XmlNode	*data;

	if((node = (xml_tree_search(node, "Controls"))) != NULL)
	{
		if((data = xml_tree_search(node, "Keys")) != NULL)
		{
			ctrl_keys_uninstall_all(min->cfg.ctrlinfo);
			ctrl_key_remove_all(min->cfg.ctrlinfo);
			keys_load(min, data);
		}
	}
}

/* ----------------------------------------------------------------------------------------- */	

CfgPage * cct_describe(MainInfo *min)
{
	static CfgPage	desc = { NODE, cct_init, cct_update, cct_accept, cct_save, cct_load, NULL };

	return &desc;
}
