/*
** 1998-10-15 -	Config page for main window position and size. Neat.
*/

#include "gentoo.h"

#include "guiutil.h"
#include "strutil.h"

#include "cfg_module.h"

#include "cfg_possize.h"

#define	NODE	"WinInfo"

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

typedef enum { PS_X, PS_Y, PS_WIDTH, PS_HEIGHT } PSId;

typedef struct {
	GtkWidget	*frame;
	GtkWidget	*aentry, *agrab;
	GtkWidget	*bentry, *bgrab;
	GtkWidget	*track;
	GtkWidget	*set;
	GtkWidget	*save;
} F_Pair;

typedef struct {
	GtkWidget	*vbox;			/* Can you say "standard"? */

	F_Pair		ppos, psize;		/* Position and size frames. */

	gint		modified;
	MainInfo	*min;
	WinInfo		edit;			/* Window info being edited. */
} P_PosSize;

static P_PosSize	the_page;

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

static void set_frame(F_Pair *frame, PSPair *pair)
{
	char	buf[32];

	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frame->track), pair->flags & PSPF_TRACK);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frame->set),   pair->flags & PSPF_SET);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frame->save),  pair->flags & PSPF_SAVE);

	g_snprintf(buf, sizeof buf, "%d", pair->a);
	gtk_entry_set_text(GTK_ENTRY(frame->aentry), buf);
	g_snprintf(buf, sizeof buf, "%d", pair->b);
	gtk_entry_set_text(GTK_ENTRY(frame->bentry), buf);
}

static void set_widgets(P_PosSize *page)
{
	set_frame(&page->ppos,  &page->edit.pos);
	set_frame(&page->psize, &page->edit.size);
}

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

/* 1998-10-20 -	A value (in one of the entry widgets) just changed. */
static gint evt_value_changed(GtkWidget *wid, gpointer data)
{
	P_PosSize	*page = (P_PosSize *) data;
	gint		*val = (gint *) gtk_object_get_data(GTK_OBJECT(wid), "value");

	if(wid != NULL)
	{
		sscanf(gtk_entry_get_text(GTK_ENTRY(wid)), "%d", val);
		page->modified = TRUE;
	}
	return TRUE;
}

/* 1998-10-16 -	This gets called as the user hits one of the "Grab" buttons. */
static gint evt_grab_clicked(GtkWidget *wid, gpointer data)
{
	P_PosSize	*page = (P_PosSize *) data;
	PSId		id = (PSId) gtk_object_get_data(GTK_OBJECT(wid), "field");
	F_Pair		*fr = (F_Pair *) gtk_object_get_data(GTK_OBJECT(wid), "frame");
	GtkWidget	*entry;
	char		buf[32] = "";

	entry = (id == PS_X || id == PS_WIDTH) ? fr->aentry : fr->bentry;

	switch(id)
	{
		case PS_X:
		case PS_Y:
			gdk_window_get_position(page->min->gui->window->window, &page->edit.pos.a, &page->edit.pos.b);
			g_snprintf(buf, sizeof buf, "%d", (id == PS_X) ? page->edit.pos.a : page->edit.pos.b);
			break;
		case PS_WIDTH:
		case PS_HEIGHT:
			gdk_window_get_size(page->min->gui->window->window, &page->edit.size.a, &page->edit.size.b);
			g_snprintf(buf, sizeof buf, "%d", (id == PS_WIDTH) ? page->edit.size.a : page->edit.size.b);
			break;
	}
	if(buf[0] != '\0')
	{
		page->modified = TRUE;
		gtk_entry_set_text(GTK_ENTRY(entry), buf);
	}

	return TRUE;
}

/* 1998-10-20 -	User clicked on a flag check button. Let's toggle something somewhere. */
static gint evt_flag_clicked(GtkWidget *wid, gpointer data)
{
	P_PosSize	*page = (P_PosSize *) data;
	gint		*flg = (gint *) gtk_object_get_data(GTK_OBJECT(wid), "flag");
	gint		mask = (gint) gtk_object_get_data(GTK_OBJECT(wid), "mask");

	if(GTK_TOGGLE_BUTTON(wid)->active)
		*flg |= mask;
	else
		*flg &= ~mask;
	page->modified = TRUE;

	return TRUE;
}

/* A macro to save me some typing when connecting flag widgets. */
#define	CONN_FLG(w,f,m,p)	gtk_signal_connect(GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(evt_flag_clicked),\
							(gpointer) (p));\
				gtk_object_set_data(GTK_OBJECT(w), "flag", (gpointer) (f));\
				gtk_object_set_data(GTK_OBJECT(w), "mask", (gpointer) (m));
				

/* 1998-10-15 -	Build a "pair-frame", with two numbers and some widgetry. */
static void build_frame(P_PosSize *page, F_Pair *fr, PSId psid, char *flabel, char *alab, char *blab, gint notrack)
{
	GtkWidget	*table, *label, *hbox;
	PSPair		*pair = (fr == &page->ppos) ? &page->edit.pos : &page->edit.size;

	if(fr == NULL || flabel == NULL || alab == NULL || blab == NULL)
		return;

	fr->frame = gtk_frame_new(flabel);

	table = gtk_table_new(4, 3, FALSE);
	label = gtk_label_new(alab);
	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,  0,0,0,0);
	gtk_widget_show(label);
	fr->aentry = gtk_entry_new_with_max_length(6);
	gtk_object_set_data(GTK_OBJECT(fr->aentry), "value", &pair->a);
	gtk_signal_connect(GTK_OBJECT(fr->aentry), "changed", GTK_SIGNAL_FUNC(evt_value_changed), (gpointer) page);
	gtk_table_attach(GTK_TABLE(table), fr->aentry, 1, 2, 0, 1,  GTK_EXPAND|GTK_FILL,0,0,0);
	gtk_widget_show(fr->aentry);
	fr->agrab = gtk_button_new_with_label("Grab");
	GTK_WIDGET_UNSET_FLAGS(fr->agrab, GTK_CAN_FOCUS);
	gtk_object_set_data(GTK_OBJECT(fr->agrab), "field",  (gpointer) (psid));
	gtk_object_set_data(GTK_OBJECT(fr->agrab), "frame", (gpointer) fr);
	gtk_signal_connect(GTK_OBJECT(fr->agrab), "clicked", GTK_SIGNAL_FUNC(evt_grab_clicked), (gpointer) page);
	gtk_table_attach(GTK_TABLE(table), fr->agrab, 2, 3, 0, 1,  0,0,0,0);
	gtk_widget_show(fr->agrab);

	label = gtk_label_new(blab);
	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,  0,0,0,0);
	gtk_widget_show(label);
	fr->bentry = gtk_entry_new_with_max_length(6);
	gtk_object_set_data(GTK_OBJECT(fr->bentry), "value", &pair->b);
	gtk_signal_connect(GTK_OBJECT(fr->bentry), "changed", GTK_SIGNAL_FUNC(evt_value_changed), (gpointer) page);
	gtk_table_attach(GTK_TABLE(table), fr->bentry, 1, 2, 1, 2,  GTK_EXPAND|GTK_FILL,0,0,0);
	gtk_widget_show(fr->bentry);
	fr->bgrab = gtk_button_new_with_label("Grab");
	GTK_WIDGET_UNSET_FLAGS(fr->bgrab, GTK_CAN_FOCUS);
	gtk_object_set_data(GTK_OBJECT(fr->bgrab), "field",  (gpointer) (psid + 1));
	gtk_object_set_data(GTK_OBJECT(fr->bgrab), "frame", (gpointer) fr);
	gtk_signal_connect(GTK_OBJECT(fr->bgrab), "clicked", GTK_SIGNAL_FUNC(evt_grab_clicked), (gpointer) page);
	gtk_table_attach(GTK_TABLE(table), fr->bgrab, 2, 3, 1, 2,  0,0,0,0);
	gtk_widget_show(fr->bgrab);

	fr->track = gtk_check_button_new_with_label("Track Changes?");
	CONN_FLG(fr->track, &pair->flags, PSPF_TRACK, page);
	gtk_table_attach(GTK_TABLE(table), fr->track, 0, 3, 2, 3,  0,0,0,0);
	if(notrack)
		gtk_widget_set_sensitive(fr->track, FALSE);
	gtk_widget_show(fr->track);
	hbox = gtk_hbox_new(FALSE, 0);
	fr->set = gtk_check_button_new_with_label("Set on Start-up?");
	CONN_FLG(fr->set, &pair->flags, PSPF_SET, page);
	gtk_box_pack_start(GTK_BOX(hbox), fr->set, TRUE, TRUE, 0);
	gtk_widget_show(fr->set);
	fr->save = gtk_check_button_new_with_label("Save on Exit?");
	CONN_FLG(fr->save, &pair->flags, PSPF_SAVE, page);
	gtk_box_pack_start(GTK_BOX(hbox), fr->save, TRUE, TRUE, 0);
	gtk_widget_show(fr->save);
	gtk_table_attach(GTK_TABLE(table), hbox, 0, 3, 3, 4,  0,0,0,0);
	gtk_widget_show(hbox);

	gtk_container_add(GTK_CONTAINER(fr->frame), table);
	gtk_widget_show(table);
}

static GtkWidget * cps_init(MainInfo *min, char **name)
{
	GtkWidget	*hbox;
	P_PosSize	*page = &the_page;

	if(name == NULL)
		return NULL;

	*name = "Pos & Size";

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

	hbox = gtk_hbox_new(FALSE, 0);

	page->vbox = gtk_vbox_new(FALSE, 0);
	build_frame(page, &page->ppos, PS_X, "Window Position", "X", "Y", TRUE);
	gtk_box_pack_start(GTK_BOX(hbox), page->ppos.frame, TRUE, TRUE, 5);
	gtk_widget_show(page->ppos.frame);
	build_frame(page, &page->psize, PS_WIDTH, "Window Size", "Width", "Height", FALSE);
	gtk_box_pack_start(GTK_BOX(hbox), page->psize.frame, TRUE, TRUE, 5);
	gtk_widget_show(page->psize.frame);

	gtk_box_pack_start(GTK_BOX(page->vbox), hbox, FALSE, FALSE, 0);
	gtk_widget_show(hbox);

	gtk_widget_show(page->vbox);
	return page->vbox;
}

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

/* 1998-10-20 -	Update config page. */
static void cps_update(MainInfo *min)
{
	P_PosSize	*page = &the_page;

	page->edit = min->cfg.wininfo;
	set_widgets(page);
}

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

/* 1998-10-20 -	Accept changes, if any. */
static void cps_accept(MainInfo *min)
{
	P_PosSize	*page = &the_page;

	if(page->modified)
	{
		min->cfg.wininfo = page->edit;
		page->modified = FALSE;
	}
}

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

static void save_pair(PSPair *pair, char *label, FILE *out)
{
	xml_put_node_open(out, label);
	xml_put_integer(out, "a", pair->a);
	xml_put_integer(out, "b", pair->b);
	xml_put_integer(out, "flags", pair->flags);
	xml_put_node_close(out, label);
}

/* 1998-10-20 -	Save the window position/size info. */
static int cps_save(MainInfo *min, FILE *out)
{
	xml_put_node_open(out, "WinInfo");
	xml_put_node_open(out, "Main");
	save_pair(&min->cfg.wininfo.pos, "Position", out);
	save_pair(&min->cfg.wininfo.size, "Size", out);
	xml_put_node_close(out, "Main");
	xml_put_node_close(out, "WinInfo");
	return TRUE;
}

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

/* 1998-10-21 -	Load in a position or size value pair (e.g. one position OR one size). */
static void load_pair(PSPair *pair, XmlNode *node)
{
	xml_get_integer(node, "a", &pair->a);
	xml_get_integer(node, "b", &pair->b);
	xml_get_integer(node, "flags", (gint *) &pair->flags);
}

/* 1998-10-21 -	Load the position & size info back in. */
static void cps_load(MainInfo *min, XmlNode *node)
{
	XmlNode	*data, *pair;

	if((data = xml_tree_search(node, "Main")) != NULL)
	{
		if((pair = xml_tree_search(data, "Position")) != NULL)
			load_pair(&min->cfg.wininfo.pos, pair);
		if((pair = xml_tree_search(data, "Size")) != NULL)
			load_pair(&min->cfg.wininfo.size, pair);
	}
}

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

/* 1998-10-15 -	Describe the position/size config page. */
CfgPage * cps_describe(MainInfo *min)
{
	static CfgPage	desc = { NODE, cps_init, cps_update, cps_accept, cps_save, cps_load, NULL };

	return &desc;
}

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

/* 1998-10-21 -	Set main window position. */
void cps_set_pos(MainInfo *min)
{
	if(min->cfg.wininfo.pos.flags & PSPF_SET)
		gtk_widget_set_uposition(min->gui->window, min->cfg.wininfo.pos.a, min->cfg.wininfo.pos.b);
}

/* 1998-10-21 -	Set the main window's size.
** 1999-03-04 -	Ported to GTK+ 1.2.0, allowing it to finally do The Right Thing. Glorious.
*/
void cps_set_size(MainInfo *min)
{
	if(min->cfg.wininfo.size.flags & PSPF_SET)
		gtk_window_set_default_size(GTK_WINDOW(min->gui->window), min->cfg.wininfo.size.a, min->cfg.wininfo.size.b);
}
