/*
** 1998-08-12 -	The file typing systems seems to work OK; better give it a GUI too.
** 1999-05-27 -	Changed linkage between this module and cfg_styles. Somewhat cleaner now.
*/

#include "gentoo.h"

#include "types.h"
#include "styles.h"
#include "style_dialog.h"
#include "strutil.h"
#include "guiutil.h"
#include "xmlutil.h"
#include "dirpane.h"
#include "iconutil.h"

#include "cfg_gui.h"
#include "cfg_module.h"
#include "cfg_styles.h"

#include "cfg_types.h"

#define	NODE	"FileTypes"

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

typedef struct {		/* Just a little helper structure for the identification. */
	GtkWidget	*check;
	GtkWidget	*entry;
	GtkWidget	*glob;		/* For the name and file RE, this gives glob->RE translation. */
	GtkWidget	*nocase;	/* Do case-insensitive RE matching? */
} CEntry;

typedef struct {
	GtkWidget	*vbox;		/* The usual mandatory root vbox. */
	GtkWidget	*scwin;		/* Scrolled window wrapping main clist. */
	GtkWidget	*clist;		/* Clist showing exisiting types. */

	GtkWidget	*ntable;	/* Table holding the type name widgetry. */
	GtkWidget	*name;		/* Entry widget for type name. */
	gint		sig_name;	/* Signal handler number for name changes. */

	GtkWidget	*iframe;	/* Identification frame. */
	GtkWidget	*itable;	/* This holds all the identification widgets. */
	GtkWidget	*ithbox;	/* Hbox for the 'type' id row. */
	GtkWidget	*itype[7];	/* Radio buttons for the type selection. Boring. */
	GSList		*itlist;	/* Radio button grouping list. */
	mode_t		*itmode;	/* The actual mode values (e.g. S_IFREG etc) for types. */
	GtkWidget	*irperm;	/* Require permissions match? */
	GtkWidget	*iphbox;	/* Hbox for all the toggle buttons. */
	GtkWidget	*iperm[6];	/* Toggle buttons for permissions (set uid, gid, sticky, read, write, execute). */
	CEntry		ident[3];	/* The suffix, name and 'file' id widgets. */

	GtkWidget	*sframe;	/* Style frame. */
	GtkWidget	*shbox;		/* Just something to hold the style stuff. */
	GtkWidget	*sbtn;		/* Button to change style (also displays current). */
	GtkWidget	*sbhbox;	/* Hbox containing _button_ contents. */
	GtkWidget	*sicon;		/* A pixmap displayed inside the "sbtn" button. */
	GtkWidget	*slabel;	/* A "loose" label displayed inside the "sbtn" button. */

	GtkWidget	*bhbox;		/* A box for the "Add", "Up", "Down" & "Delete" buttons. */
	GtkWidget	*add, *up,	/* And here are the buttons themselves. */
			*down, *del;

	MainInfo	*min;
	GList		*type;		/* List of editing types. */
	FType		*curr_type;	/* Currently selected type. */
	gint		row;		/* Clist row index of currently selected type. */
	gint		modified;	/* Did user change anything? */
} P_Types;

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

static P_Types	the_page;

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

/* 1998-08-13 -	Set the widgets so they reflect the settings for the given file type. */
static void set_widgets(P_Types *page, FType *tpe)
{
	gchar	*itext[3];
	gint	iflags[] = { FTFL_REQSUFFIX, FTFL_NAMEMATCH, FTFL_FILEMATCH },
		pflags[] = { FTPM_SETUID, FTPM_SETGID, FTPM_STICKY, FTPM_READ, FTPM_WRITE, FTPM_EXECUTE },
		igflags[] = { 0, FTFL_NAMEGLOB, FTFL_FILEGLOB },
		incflags[] = { 0, FTFL_NAMENOCASE, FTFL_FILENOCASE };
	guint	i;

	itext[0] = tpe->suffix;
	itext[1] = tpe->name_re_src;
	itext[2] = tpe->file_re_src;

	if(tpe != NULL)
	{
		gtk_signal_handler_block(GTK_OBJECT(page->name), page->sig_name);
		gtk_entry_set_text(GTK_ENTRY(page->name), tpe->name);
		gtk_signal_handler_unblock(GTK_OBJECT(page->name), page->sig_name);
		gtk_widget_set_sensitive(page->ntable, TRUE);

		for(i = 0; i < 7; i++)
		{
			if(page->itmode[(int) gtk_object_get_user_data(GTK_OBJECT(page->itype[i]))] == tpe->mode)
			{
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->itype[i]), TRUE);
				break;
			}
		}

		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->irperm), tpe->flags & FTFL_REQPERM);
		for(i = 0; i < 6; i++)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->iperm[i]), tpe->perm & pflags[i]);
		gtk_widget_set_sensitive(page->iphbox, tpe->flags & FTFL_REQPERM);

		for(i = 0; i < 3; i++)
		{
			gtk_entry_set_text(GTK_ENTRY(page->ident[i].entry), itext[i]);
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->ident[i].check), tpe->flags & iflags[i]);
			gtk_widget_set_sensitive(page->ident[i].entry, tpe->flags & iflags[i]);
			if(i >= 1)
			{
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->ident[i].glob), tpe->flags & igflags[i]);
				gtk_widget_set_sensitive(page->ident[i].glob, tpe->flags & iflags[i]);
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->ident[i].nocase), tpe->flags & incflags[i]);
				gtk_widget_set_sensitive(page->ident[i].nocase, tpe->flags & iflags[i]);
			}
		}
		gtk_widget_set_sensitive(page->iframe, tpe->mode != 0);

		if(tpe->style != NULL)
		{
			GdkPixmap	*ipix;
			GdkBitmap	*imsk = NULL;
			gchar		buf[128];
			const gchar	*iname;

			if((iname = stl_style_property_get_icon(tpe->style, SPN_ICON_UNSEL)) != NULL)
			{
				if((ipix = ico_icon_get(page->min, page->shbox->window, iname, &imsk)) != NULL)
				{
					if(page->sicon == NULL)
					{
						page->sicon = gtk_pixmap_new(ipix, imsk);
						gtk_box_pack_start(GTK_BOX(page->sbhbox), page->sicon, FALSE, FALSE, 10);
						gtk_box_reorder_child(GTK_BOX(page->sbhbox), page->sicon, 0);
						gtk_widget_show(page->sicon);
					}
					else
						gtk_pixmap_set(GTK_PIXMAP(page->sicon), ipix, imsk);
				}
			}
			g_snprintf(buf, sizeof buf, "%s  -  Click to change...", stl_style_get_name(tpe->style));
			gtk_label_set_text(GTK_LABEL(page->slabel), buf);
		}
		gtk_widget_set_sensitive(page->sframe, TRUE);
		gtk_widget_set_sensitive(page->up, page->row != 0 && !TYP_IS_UNKNOWN(tpe));
		gtk_widget_set_sensitive(page->down, !TYP_IS_UNKNOWN(tpe) && ((guint) page->row < g_list_length(page->type) - 2));
		gtk_widget_set_sensitive(page->del, tpe->mode != 0);
	}
}

/* 1998-08-13 -	Reset the state of all the type-editing widgets. */
static void reset_widgets(P_Types *page)
{
	int	i;

	page->curr_type = NULL;
	page->row	= -1;
	gtk_entry_set_text(GTK_ENTRY(page->name), "");
	gtk_widget_set_sensitive(page->ntable, FALSE);

	gtk_widget_set_sensitive(page->iphbox, FALSE);
	for(i = 0; i < 6; i++)
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->iperm[i]), FALSE);

	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->itype[0]), TRUE);
	for(i = 0; i < 3; i++)
	{
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->ident[i].check), FALSE);
		gtk_entry_set_text(GTK_ENTRY(page->ident[i].entry), "");
		if(i >= 1)
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->ident[i].glob), FALSE);
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->ident[i].nocase), FALSE);
		}
	}
	gtk_widget_set_sensitive(page->iframe, FALSE);

	if(page->sicon != NULL)
	{
		gtk_widget_destroy(page->sicon);
		page->sicon = NULL;
	}
	gtk_label_set_text(GTK_LABEL(page->slabel), "(None)");
	gtk_widget_set_sensitive(page->sframe, FALSE);

	gtk_widget_set_sensitive(page->up, FALSE);
	gtk_widget_set_sensitive(page->down, FALSE);
	gtk_widget_set_sensitive(page->del, FALSE);
}

/* 1998-12-13 -	Broke out the actual list updating code, since it's now needed more. */
static void update_clist(P_Types *page)
{
	char	*text[] = { NULL };
	gint	row;
	GList	*iter;

	gtk_clist_freeze(GTK_CLIST(page->clist));
	gtk_clist_clear(GTK_CLIST(page->clist));
	for(iter = page->type; iter != NULL; iter = g_list_next(iter))
	{
		text[0] = ((FType *) iter->data)->name;
		row = gtk_clist_append(GTK_CLIST(page->clist), text);
		gtk_clist_set_row_data(GTK_CLIST(page->clist), row, iter->data);
	}
	gtk_clist_thaw(GTK_CLIST(page->clist));
}

/* 1998-08-13 -	Copy the given type (at <data>) and add it to the editing-copy list. Also
**		append a row with the type's name to the clist. 
*/
static void copy_type(gpointer data, gpointer user)
{
	P_Types	*page = (P_Types *) user;
	FType	*tpe = (FType *) data, *nt;

	if(tpe == NULL)
		return;

	if((nt = typ_type_copy(tpe)) != NULL)
		page->type = typ_type_insert(page->type, NULL, nt);
}

/* 1998-08-13 -	Copy the current type definitions and display the copies in the main clist. */
static void populate_clist(MainInfo *min, P_Types *page)
{
	gtk_clist_clear(GTK_CLIST(page->clist));
	reset_widgets(page);

	g_list_foreach(min->cfg.type, copy_type, (gpointer) page);
	update_clist(page);
}

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

/* 1998-08-13 -	Something was selected in the main clist. Update editing widgets. */
static gint evt_clist_select(GtkWidget *wid, gint row, gint column, GdkEventButton *evt)
{
	FType	*tpe;
	P_Types	*page = (P_Types *) gtk_object_get_user_data(GTK_OBJECT(wid));

	if((tpe = (FType *) gtk_clist_get_row_data(GTK_CLIST(wid), row)) != NULL)
	{
		page->curr_type = tpe;
		page->row	= row;
		set_widgets(page, tpe);
	}

	return TRUE;
}

/* 1998-08-13 -	This gets called when there is no longer a selection in the clist. */
static gint evt_clist_unselect(GtkWidget *wid, gint row, gint column, GdkEventButton *evt)
{
	P_Types	*page = (P_Types *) gtk_object_get_user_data(GTK_OBJECT(wid));

	page->curr_type = NULL;		/* Must be done _before_ reset_widgets()! Tricky! */
	page->row = -1;
	reset_widgets(page);	

	return TRUE;
}

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

/* 1998-08-13 -	User is editing the name. Copy it as soon as it changes. Note that there is
**		no unique-ness requirement for type names. That simplifies things.
*/
static gint evt_name_changed(GtkWidget *wid, gpointer data)
{
	P_Types	*page = (P_Types *) data;
	char	*text;

	if(page->curr_type != NULL)
	{
		page->modified = TRUE;
		text = gtk_entry_get_text(GTK_ENTRY(wid));
		page->type = typ_type_set_name(page->type, page->curr_type, text);
		gtk_clist_set_text(GTK_CLIST(page->clist), page->row, 0, text);
	}
	return TRUE;
}

/* 1998-08-13 -	This is the callback for (all seven) mode check buttons. */
static gint evt_mode_clicked(GtkWidget *wid, gpointer data)
{
	P_Types	*page = (P_Types *) data;
	int	index = (int) gtk_object_get_user_data(GTK_OBJECT(wid));

	if(page->curr_type == NULL)
		return TRUE;

	page->modified = TRUE;

	page->curr_type->mode = page->itmode[index];

	return TRUE;
}

/* 1998-09-07 -	User hit the "require permissions" check button. */
static gint evt_id_reqperm_clicked(GtkWidget *wid, gpointer data)
{
	gint	state;
	P_Types	*page = (P_Types *) data;

	if(page->curr_type == NULL)
		return TRUE;

	page->modified = TRUE;
	state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid));

	gtk_widget_set_sensitive(page->iphbox, state);
	if(state)
		page->curr_type->flags |= FTFL_REQPERM;
	else
		page->curr_type->flags &= ~FTFL_REQPERM;
	
	return TRUE;
}

/* 1998-09-07 -	User clicked one of the permission toggle buttons. Remember. */
static gint evt_id_perm_clicked(GtkWidget *wid, gpointer data)
{
	int	flag;
	P_Types	*page = (P_Types *) data;

	if(page->curr_type == NULL)
		return TRUE;

	page->modified = TRUE;
	flag = (int) gtk_object_get_user_data(GTK_OBJECT(wid));
	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid)))
		page->curr_type->perm |= flag;
	else
		page->curr_type->perm &= ~flag;

	return TRUE;
}

/* 1998-08-13 -	User clicked on of the check buttons for identification (suffix, name RE, 'file' RE). Act. */
static gint evt_id_check_clicked(GtkWidget *wid, gpointer data)
{
	int	index = (int) gtk_object_get_user_data(GTK_OBJECT(wid));
	int	iflags[] = { FTFL_REQSUFFIX, FTFL_NAMEMATCH, FTFL_FILEMATCH };
	gint	state;
	P_Types	*page = (P_Types *) data;

	if(page->curr_type == NULL)
		return TRUE;

	page->modified = TRUE;

	state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid));

	gtk_widget_set_sensitive(page->ident[index].entry, state);
	if(state)
	{
		page->curr_type->flags |= iflags[index];
		gtk_widget_grab_focus(page->ident[index].entry);
	}
	else
		page->curr_type->flags &= ~iflags[index];
	if(index >= 1)
	{
		gtk_widget_set_sensitive(page->ident[index].glob, state);
		gtk_widget_set_sensitive(page->ident[index].nocase, state);
	}

	return TRUE;
}

static gint evt_id_entry_changed(GtkWidget *wid, gpointer data)
{
	char	*text;
	int	index = (int) gtk_object_get_user_data(GTK_OBJECT(wid));
	P_Types	*page = (P_Types *) data;


	if(page->curr_type == NULL)
		return TRUE;

	page->modified = TRUE;

	text = gtk_entry_get_text(GTK_ENTRY(wid));

	/* This is soo crude... I guess the FStyle organization really sucks. :( */
	switch(index)
	{
		case 0:
			str_strncpy(page->curr_type->suffix, text, sizeof page->curr_type->suffix);
			break;
		case 1:
			str_strncpy(page->curr_type->name_re_src, text, sizeof page->curr_type->name_re_src);
			break;
		case 2:
			str_strncpy(page->curr_type->file_re_src, text, sizeof page->curr_type->file_re_src);
			break;
	}

	return TRUE;
}

/* 1998-08-30 -	User just clicked the glob translation check button. Do the things. */
static gint evt_id_glob_changed(GtkWidget *wid, gpointer data)
{
	P_Types	*page = (P_Types *) data;
	int	index = (int) gtk_object_get_user_data(GTK_OBJECT(wid));
	int	flag[] = { 0, FTFL_NAMEGLOB, FTFL_FILEGLOB };

	if(page->curr_type == NULL)
		return TRUE;

	page->modified = TRUE;
	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid)))
		page->curr_type->flags |= flag[index];
	else
		page->curr_type->flags &= ~flag[index];

	return TRUE;
}

/* 1998-09-15 -	User just clicked the nocase toggle button. React! */
static gint evt_id_nocase_changed(GtkWidget *wid, gpointer data)
{
	P_Types	*page = (P_Types *) data;
	int	index = (int) gtk_object_get_user_data(GTK_OBJECT(wid));
	int	flag[] = { 0, FTFL_NAMENOCASE, FTFL_FILENOCASE };

	if(page->curr_type == NULL)
		return TRUE;

	page->modified = TRUE;
	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid)))
		page->curr_type->flags |= flag[index];
	else
		page->curr_type->flags &= ~flag[index];

	return TRUE;
}

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

/* 1999-05-27 -	Select style for current type. Rewritten, to work with new style config module. */
static gint evt_style_clicked(GtkWidget *wid, gpointer user)
{
	P_Types	*page = user;

	if((page != NULL) && (page->curr_type != NULL))
	{
		Style	*stl;

		if((stl = sdl_dialog(cst_get_styleinfo(), NULL)) != NULL)
		{
			page->curr_type->style = stl;
			set_widgets(page, page->curr_type);
		}
	}
	return TRUE;
}

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

/* 1998-08-14 -	User just clicked the 'Add' button. Let's add a new type to play with!
** 1998-12-14 -	Rewritten to work better with the new priority-less type editing system.
*/
static gint evt_add_clicked(GtkWidget *wid, gpointer data)
{
	CfgInfo	*cfg = (CfgInfo *) gtk_object_get_user_data(GTK_OBJECT(wid));
	P_Types	*page = (P_Types *) data;
	FType	*tpe;
	gint	row;

	if((tpe = typ_type_new(cfg, "(New Type)", S_IFREG, 0, NULL, NULL, NULL)) != NULL)
	{
		page->type = typ_type_insert(page->type, page->curr_type, tpe);
		page->type = typ_type_set_style(page->type, tpe, cst_get_styleinfo(), NULL);
		row = g_list_index(page->type, tpe);
		update_clist(page);
		gtk_clist_select_row(GTK_CLIST(page->clist), row, 0);
		gtk_clist_moveto(GTK_CLIST(page->clist), row, -1, 0.5f, 0.0f);
		page->modified = TRUE;
		gtk_entry_select_region(GTK_ENTRY(page->name), 0, -1);
		gtk_widget_grab_focus(page->name);
	}
	return TRUE;
}

/* 1998-08-14 -	Delete button was hit, so kill the currently selected type. Tragic.
** 1998-12-14 -	Rewritten. Didn't use the types-module, and lost selection.
*/
static gint evt_del_clicked(GtkWidget *wid, gpointer data)
{
	P_Types	*page = (P_Types *) data;
	gint	row, len;

	if(page->curr_type != NULL)
	{
		page->type = typ_type_remove(page->type, page->curr_type);
		len = g_list_length(page->type);
		row = (page->row > len) ? len - 1 : page->row;
		update_clist(page);
		gtk_clist_select_row(GTK_CLIST(page->clist), row, 0);
		page->modified = TRUE;
	}
	return TRUE;
}

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

/* 1998-12-13 -	Move selected type up one position (increases its priority).
** 1998-12-23 -	Commented out the gtk_clist_moveto() call, since it's so (damned) buggy. */
static gint evt_up_clicked(GtkWidget *wid, gpointer data)
{
	P_Types	*page = (P_Types *) data;

	if(page->curr_type != NULL)
	{
		page->type = typ_type_move(page->type, page->curr_type, -1);
		update_clist(page);
		gtk_clist_select_row(GTK_CLIST(page->clist), page->row - 1, 0);
/*		gtk_clist_moveto(GTK_CLIST(page->clist), page->row - 1, -1, 0.0f, 0.0f); */
		page->modified = TRUE;
	}
	return TRUE;
}

/* 1998-12-13 -	Move selected type down, decreasing its priority. */
static gint evt_down_clicked(GtkWidget *wid, gpointer data)
{
	P_Types	*page = (P_Types *) data;

	if(page->curr_type != NULL)
	{
		page->type = typ_type_move(page->type, page->curr_type, 1);
		update_clist(page);
		gtk_clist_select_row(GTK_CLIST(page->clist), page->row + 1, 0);
		gtk_clist_moveto(GTK_CLIST(page->clist), page->row + 1, -1, 1.0f, 0.0f);
		page->modified = TRUE;
	}
	return TRUE;
}

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

static GtkWidget * ctp_init(MainInfo *min, char **name)
{
	P_Types		*page = &the_page;
	GtkWidget	*label, *vbox, *sep;
	const gchar	*tlab[] = { "File", "Dir", "Link", "B-Dev", "C-Dev", "FIFO", "Socket" },
			*iplab[] = { "SetUID", "SetGID", "Sticky",  "Readable", "Writable", "Executable" },
			*idlab[] = { "Require Suffix", "Match Name (RE)", "Match 'file' (RE)" };
	gint		idmax[] = { FT_SUFFIX_MAX, FT_NAMERE_MAX, FT_FILERE_MAX },
			ifperm[] = { FTPM_SETUID, FTPM_SETGID, FTPM_STICKY, FTPM_READ, FTPM_WRITE, FTPM_EXECUTE }, i, y;
	static mode_t	type[] = { S_IFREG, S_IFDIR, S_IFLNK, S_IFBLK, S_IFCHR, S_IFIFO, S_IFSOCK };

	if(name == NULL)
		return NULL;
	*name = "File Types";

	page->min = min;

	page->type = NULL;
	page->curr_type = NULL;
	page->row       = -1;
	page->modified  = FALSE;

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

	page->scwin = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(page->scwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	page->clist = gtk_clist_new(1);
	gtk_signal_connect(GTK_OBJECT(page->clist), "select_row", GTK_SIGNAL_FUNC(evt_clist_select), NULL);
	gtk_signal_connect(GTK_OBJECT(page->clist), "unselect_row", GTK_SIGNAL_FUNC(evt_clist_unselect), NULL);
	gtk_object_set_user_data(GTK_OBJECT(page->clist), (gpointer) page);
	gtk_container_add(GTK_CONTAINER(page->scwin), page->clist);
	gtk_widget_show(page->clist);
	gtk_box_pack_start(GTK_BOX(page->vbox), page->scwin, TRUE, TRUE, 0);
	gtk_widget_show(page->scwin);

	page->ntable = gtk_table_new(2, 2, FALSE);
	label = gtk_label_new("Name");
	gtk_table_attach(GTK_TABLE(page->ntable), label, 0, 1, 0, 1,  GTK_FILL,0,5,0);
	gtk_widget_show(label);
	page->name = gtk_entry_new_with_max_length(STL_STYLE_NAME_MAX - 1);
	page->sig_name = gtk_signal_connect(GTK_OBJECT(page->name), "changed", GTK_SIGNAL_FUNC(evt_name_changed), (gpointer) page);
	gtk_table_attach_defaults(GTK_TABLE(page->ntable), page->name, 1, 2, 0, 1);
	gtk_widget_show(page->name);

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

	page->iframe = gtk_frame_new("Identification");
	page->itable = gtk_table_new(7, 3, FALSE);
	label = gtk_label_new("Require Type");
	gtk_table_attach(GTK_TABLE(page->itable), label, 0, 1, 0, 1,  GTK_FILL,0,0,0);
	gtk_widget_show(label);
	page->ithbox = gtk_hbox_new(FALSE, 0);

	page->itlist = gui_radio_group_new(7, tlab, page->itype);
	page->itmode = type;
	for(i = 0; i < 7; i++)
	{
		gtk_signal_connect(GTK_OBJECT(page->itype[i]), "clicked", GTK_SIGNAL_FUNC(evt_mode_clicked), (gpointer) page);
		gtk_object_set_user_data(GTK_OBJECT(page->itype[i]), (gpointer) i);
		gtk_box_pack_start(GTK_BOX(page->ithbox), page->itype[i], TRUE, TRUE, 0);
		gtk_widget_show(page->itype[i]);
	}
	gtk_table_attach(GTK_TABLE(page->itable), page->ithbox, 1, 3, 0, 1, GTK_EXPAND|GTK_FILL,0,0,0);
	gtk_widget_show(page->ithbox);

	page->irperm = gtk_check_button_new_with_label("Require Protection");
	gtk_signal_connect(GTK_OBJECT(page->irperm), "clicked", GTK_SIGNAL_FUNC(evt_id_reqperm_clicked), (gpointer) page);
	gtk_table_attach(GTK_TABLE(page->itable), page->irperm, 0, 1, 1, 2,  GTK_FILL,0,0,0);
	gtk_widget_show(page->irperm);
	page->iphbox = gtk_hbox_new(FALSE, 0);
	for(i = 0; i < 6; i++)
	{
		page->iperm[i] = gtk_toggle_button_new_with_label(iplab[i]);
		gtk_object_set_user_data(GTK_OBJECT(page->iperm[i]), (gpointer) ifperm[i]);
		gtk_signal_connect(GTK_OBJECT(page->iperm[i]), "clicked", GTK_SIGNAL_FUNC(evt_id_perm_clicked), (gpointer) page);
		gtk_box_pack_start(GTK_BOX(page->iphbox), page->iperm[i], TRUE, TRUE, 0);
		gtk_widget_show(page->iperm[i]);
	}
	gtk_table_attach(GTK_TABLE(page->itable), page->iphbox, 1, 3, 1, 2,  GTK_EXPAND|GTK_FILL,0,0,0);
	gtk_widget_show(page->iphbox);

	sep = gtk_hseparator_new();
	gtk_table_attach(GTK_TABLE(page->itable), sep, 0, 3, 4, 5, GTK_EXPAND|GTK_FILL,0,0,5);
	gtk_widget_show(sep);

	for(i = 0; i < 3; i++)
	{
		y = (i == 2) ? i + 3 : i + 2;

		page->ident[i].check = gtk_check_button_new_with_label(idlab[i]);
		gtk_object_set_user_data(GTK_OBJECT(page->ident[i].check), (gpointer) i);
		gtk_signal_connect(GTK_OBJECT(page->ident[i].check), "clicked", GTK_SIGNAL_FUNC(evt_id_check_clicked), (gpointer) page);
		gtk_table_attach(GTK_TABLE(page->itable), page->ident[i].check, 0, 1, y, y+1,  GTK_FILL,0,0,0);
		gtk_widget_show(page->ident[i].check);

		page->ident[i].entry = gtk_entry_new_with_max_length(idmax[i] - 1);
		gtk_object_set_user_data(GTK_OBJECT(page->ident[i].entry), (gpointer) i);
		gtk_signal_connect(GTK_OBJECT(page->ident[i].entry), "changed", GTK_SIGNAL_FUNC(evt_id_entry_changed), (gpointer) page);
		gtk_table_attach(GTK_TABLE(page->itable), page->ident[i].entry, 1, (i==0)?3:2, y, y+1,  GTK_EXPAND|GTK_FILL,0,0,0);
		gtk_widget_show(page->ident[i].entry);

		if(i >= 1)
		{
			GtkWidget	*hbox;

			hbox = gtk_hbox_new(FALSE, 0);
			page->ident[i].glob = gtk_check_button_new_with_label("Glob?");
			gtk_object_set_user_data(GTK_OBJECT(page->ident[i].glob), (gpointer) i);
			gtk_signal_connect(GTK_OBJECT(page->ident[i].glob), "clicked", GTK_SIGNAL_FUNC(evt_id_glob_changed), (gpointer) page);
			gtk_box_pack_start(GTK_BOX(hbox), page->ident[i].glob, FALSE, FALSE, 0);
			gtk_widget_show(page->ident[i].glob);

			page->ident[i].nocase = gtk_check_button_new_with_label("No Case?");
			gtk_object_set_user_data(GTK_OBJECT(page->ident[i].nocase), (gpointer) i);
			gtk_signal_connect(GTK_OBJECT(page->ident[i].nocase), "clicked", GTK_SIGNAL_FUNC(evt_id_nocase_changed), (gpointer) page);
			gtk_box_pack_start(GTK_BOX(hbox), page->ident[i].nocase, FALSE, FALSE, 0);
			gtk_widget_show(page->ident[i].nocase);

			gtk_table_attach(GTK_TABLE(page->itable), hbox, 2, 3, y, y + 1, 0,0,0,0);
			gtk_widget_show(hbox);
		}
	}
	gtk_container_add(GTK_CONTAINER(page->iframe), page->itable);
	gtk_widget_show(page->itable);
	gtk_box_pack_start(GTK_BOX(page->vbox), page->iframe, FALSE, FALSE, 5);
	gtk_widget_show(page->iframe);

	page->sframe = gtk_frame_new("Type's Style");
	page->shbox = gtk_hbox_new(FALSE, 0);
	label = gtk_label_new("Style");
	gtk_box_pack_start(GTK_BOX(page->shbox), label, FALSE, FALSE, 5);
	gtk_widget_show(label);
	page->sbtn = gtk_button_new();
	gtk_signal_connect(GTK_OBJECT(page->sbtn), "clicked", GTK_SIGNAL_FUNC(evt_style_clicked), (gpointer) page);

	page->sbhbox = gtk_hbox_new(FALSE, 0);
	page->sicon  = NULL;
	page->slabel = gtk_label_new("");
	gtk_box_pack_start(GTK_BOX(page->sbhbox), page->slabel, TRUE, TRUE, 0);
	gtk_widget_show(page->slabel);
	gtk_container_add(GTK_CONTAINER(page->sbtn), page->sbhbox);
	gtk_widget_show(page->sbhbox);

	gtk_box_pack_start(GTK_BOX(page->shbox), page->sbtn, TRUE, TRUE, 5);
	gtk_widget_show(page->sbtn);
	vbox = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), page->shbox, FALSE, FALSE, 5);
	gtk_widget_show(page->shbox);
	gtk_container_add(GTK_CONTAINER(page->sframe), vbox);
	gtk_widget_show(vbox);

	gtk_box_pack_start(GTK_BOX(page->vbox), page->sframe, FALSE, FALSE, 5);
	gtk_widget_show(page->sframe);

	page->bhbox = gtk_hbox_new(FALSE, 0);
	page->add = gtk_button_new_with_label("Add");
	gtk_object_set_user_data(GTK_OBJECT(page->add), (gpointer) &min->cfg);
	gtk_signal_connect(GTK_OBJECT(page->add), "clicked", GTK_SIGNAL_FUNC(evt_add_clicked), (gpointer) page);
	gtk_box_pack_start(GTK_BOX(page->bhbox), page->add, TRUE, TRUE, 5);
	gtk_widget_show(page->add);

	page->up = gui_arrow_button_new(GTK_ARROW_UP);
	gtk_signal_connect(GTK_OBJECT(page->up), "clicked", GTK_SIGNAL_FUNC(evt_up_clicked), (gpointer) page);
	gtk_box_pack_start(GTK_BOX(page->bhbox), page->up, FALSE, FALSE, 0);
	gtk_widget_show(page->up);

	page->down = gui_arrow_button_new(GTK_ARROW_DOWN);
	gtk_signal_connect(GTK_OBJECT(page->down), "clicked", GTK_SIGNAL_FUNC(evt_down_clicked), (gpointer) page);
	gtk_box_pack_start(GTK_BOX(page->bhbox), page->down, FALSE, FALSE, 0);
	gtk_widget_show(page->down);

	page->del = gtk_button_new_with_label("Delete");
	gtk_signal_connect(GTK_OBJECT(page->del), "clicked", GTK_SIGNAL_FUNC(evt_del_clicked), (gpointer) page);
	gtk_box_pack_start(GTK_BOX(page->bhbox), page->del, TRUE, TRUE, 5);
	gtk_widget_show(page->del);

	gtk_box_pack_start(GTK_BOX(page->vbox), page->bhbox, FALSE, FALSE, 5);
	gtk_widget_show(page->bhbox);

	gtk_widget_show(page->vbox);		

	return page->vbox;
}

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

/* 1998-08-14 -	Redisplay imminent, update looks. Simplistic. */
static void ctp_update(MainInfo *min)
{
	populate_clist(min, &the_page);
}

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

/* 1998-08-14 -	Accept the changes. Replace the cfg->type list with our the_page.list, and
**		also free the one being replaced.
*/
static void ctp_accept(MainInfo *min)
{
	P_Types	*page = &the_page;
	GList	*old, *next;

	if(page->modified)
	{
		for(old = min->cfg.type; old != NULL; old = next)
		{
			next = g_list_next(old);
			if(old->data == NULL)
				continue;
			typ_type_destroy((FType *) old->data);
			min->cfg.type = g_list_remove(min->cfg.type, old->data);
		}
		g_list_free(min->cfg.type);
		min->cfg.type = page->type;
		page->type	= NULL;		/* Make sure list gets rebuilt next time. */
		page->modified	= FALSE;
		cfg_set_flags(CFLG_RESCAN_LEFT | CFLG_RESCAN_RIGHT);
	}
}

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

/* 1998-08-14 -	Save out a single file type. */
static void save_type(gpointer data, gpointer user)
{
	FType	*tpe = (FType *) data;
	FILE	*out = (FILE *) user;

	if(tpe == NULL)
		return;

	xml_put_node_open(out, "FileType");
	xml_put_text(out, "name", tpe->name);
	xml_put_integer(out, "mode", tpe->mode);

	if(tpe->flags & FTFL_REQPERM)
		xml_put_integer(out, "perm", tpe->perm);
	if(tpe->flags & FTFL_REQSUFFIX)
		xml_put_text(out, "suffix", tpe->suffix);
	if(tpe->flags & FTFL_NAMEMATCH)
	{
		xml_put_text(out, "name_re", tpe->name_re_src);
		xml_put_boolean(out, "name_glob", tpe->flags & FTFL_NAMEGLOB);
		xml_put_boolean(out, "name_nocase", tpe->flags & FTFL_NAMENOCASE);
	}
	if(tpe->flags & FTFL_FILEMATCH)
	{
		xml_put_text(out, "file_re", tpe->file_re_src);
		xml_put_boolean(out, "file_glob", tpe->flags & FTFL_FILEGLOB);
		xml_put_boolean(out, "file_nocase", tpe->flags & FTFL_FILENOCASE);
	}
	if(tpe->style != NULL)
		xml_put_text(out, "style", stl_style_get_name(tpe->style));
	else
		fprintf(stderr, "**CFGTYPES: Type '%s' has NULL style!\n", tpe->name);
	xml_put_node_close(out, "FileType");
}

/* 1998-08-14 -	Save out all the filetype information. */
static gint ctp_save(MainInfo *min, FILE *out)
{
	xml_put_node_open(out, NODE);
	g_list_foreach(min->cfg.type, save_type, (gpointer) out);
	xml_put_node_close(out, NODE);

	return 1;
}

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

/* 1998-08-14 -	Load a single type and put it into the list.
** 1999-03-07 -	Fixed huge bug, where a pointer to mode_t was cast to int *.
**		Not good, since mode_t is just 16 bit...
*/
static void load_type(XmlNode *node, gpointer user)
{
	CfgInfo	*cfg = (CfgInfo *) user;
	gchar	*name = NULL, *suffix = NULL, *name_re = NULL, *file_re = NULL, *style = NULL;
	gint	perm = 0, tmp, mode;
	FType	*tpe;

	xml_get_text(node, "name", &name);
	xml_get_integer(node, "mode", &mode);
	xml_get_integer(node, "perm", &perm);
	xml_get_text(node, "suffix", &suffix);
	xml_get_text(node, "name_re", &name_re);
	xml_get_text(node, "file_re", &file_re);
	xml_get_text(node, "style", &style);

	if((tpe = typ_type_new(cfg, name, (mode_t) mode, (int) perm, suffix, name_re, file_re)) != NULL)
	{
		if(xml_get_boolean(node, "name_glob", &tmp) && tmp)
			tpe->flags |= FTFL_NAMEGLOB;
		if(xml_get_boolean(node, "name_nocase", &tmp) && tmp)
			tpe->flags |= FTFL_NAMENOCASE;
		if(xml_get_boolean(node, "file_glob", &tmp) && tmp)
			tpe->flags |= FTFL_FILEGLOB;
		if(xml_get_boolean(node, "file_nocase", &tmp) && tmp)
			tpe->flags |= FTFL_FILENOCASE;
		cfg->type = typ_type_insert(cfg->type, NULL, tpe);
		cfg->type = typ_type_set_style(cfg->type, tpe, cfg->style, style);
	}
}

/* 1998-08-14 -	Load in the filetypes hanging off of <node>. */
static void ctp_load(MainInfo *min, XmlNode *node)
{
	GList	*old, *next;

	/* First destroy any previously defined types (e.g. the built-in ones). */
	for(old = min->cfg.type; old != NULL; old = next)
	{
		next = g_list_next(old);
		if(old->data == NULL)
			continue;
		typ_type_destroy((FType *) old->data);
		min->cfg.type = g_list_remove(min->cfg.type, old->data);
	}
	xml_node_visit_children(node, load_type, (gpointer) &min->cfg);
}

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

/* 1998-08-14 -	When the GUI hides (i.e. is removed from screen by a reason different than
**		the user hitting the "OK" button), we free our editing copies.
*/
static void ctp_hide(MainInfo *min)
{
	P_Types	*page = &the_page;
	GList	*old, *next;

	for(old = page->type; old != NULL; old = next)
	{
		next = g_list_next(old);
		if(old->data == NULL)
			continue;
		typ_type_destroy((FType *) old->data);
		page->type = g_list_remove(page->type, old->data);
	}
	g_list_free(page->type);
	page->type = NULL;
}

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

CfgPage * ctp_describe(MainInfo *min)
{
	static CfgPage	desc = { NODE, ctp_init, ctp_update, ctp_accept, ctp_save, ctp_load, ctp_hide };

	return &desc;
}

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

/* 1998-08-25 -	Returns the list of current editing types. Handy when styles have changed.
**		This function is part of the rather nasty relationship between styles and
**		types; if you change enough on the styles page, the types will also think
**		they've been modified (thanks to the flag setting below). This is all due
**		to the fact that types link to their styles using direct pointer links.
*/
GList * ctp_get_types(void)
{
	the_page.modified = TRUE;

	return the_page.type;
}

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

/* 1999-05-27 -	Go through current types, and if any type contains a link to a style named <from>,
**		replace that with a link to the given style. Called by the styles config module,
**		when a style is renamed or deleted (in the latter case, <to> will be "Root").
*/
void ctp_replace_style(const gchar *from, Style *to)
{
	GList	*iter;
	FType	*type;

	for(iter = the_page.type; iter != NULL; iter = g_list_next(iter))
	{
		type = iter->data;

		if(strcmp(stl_style_get_name(type->style), from) == 0)
		{
			type->style = to;
			the_page.modified = TRUE;
		}
	}
}

/* 1999-05-27 -	Change all type's style pointers to point at styles in <to>, rather than <from>.
**		This gets called by the cfg_styles module, just before it destroys the <from>
**		styles.
*/
void ctp_relink_styles(StyleInfo *from, StyleInfo *to)
{
	GList	*iter;
	FType	*type;
	Style	*ns;

	for(iter = the_page.type; iter != NULL; iter = g_list_next(iter))
	{
		type = iter->data;
		ns   = stl_styleinfo_style_find(to, stl_style_get_name(type->style));
		if(type->style != ns)
		{
			type->style = ns;
			the_page.modified = TRUE;
		}
	}
}
