/*
** 1999-05-19 -	Started a complete rewrite of this module. It became twice the size of the old
**		code, but adds a *lot* of flexibility. And, the config module shrunk, too. :)
*/

#include "gentoo.h"

#include "cmdseq.h"
#include "strutil.h"
#include "styles.h"

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

struct _StyleInfo {
	guint		freeze_count;			/* When non-zero, changes to styles don't propagate downwards. */
	gboolean	update_pending;			/* Set when something changes during freeze. */
	GNode		*styles;			/* A N-ary tree holding the style hierarchy. Neat. */
};

typedef enum { SPT_COLOR = 0, SPT_ICON, SPT_ACTION } StlPType;

typedef struct {
	StlPType	type;
	union {
	GdkColor	color;
	gchar		icon[FILENAME_MAX];
	GString		*action;
	}		data;
} StlPVal;

/* These are the old (L for legacy) integer identifiers for style properties. They are
** used in the old_load_property() function, when converting to the more modern string-
** based property names used by this module.
*/
#define	L_TYPE_UNSEL_BGCOL	(0)
#define	L_TYPE_UNSEL_FGCOL	(1)
#define	L_TYPE_UNSEL_ICON	(2)
#define	L_TYPE_ACTN_DEFAULT	(6)
#define	L_TYPE_ACTN_VIEW	(7)
#define	L_TYPE_ACTN_EDIT	(8)
#define	L_TYPE_ACTN_PRINT	(9)
#define	L_TYPE_ACTN_PLAY	(10)

typedef struct {
	gchar		name[STL_PROPERTY_NAME_MAX];	/* Unique property identifier. */
	gboolean	override;			/* Is the value really here? */
	StlPVal		*value;
} StlProp;

struct _Style {
	StyleInfo	*styleinfo;			/* Which styleinfo does this style belong to? */
	gchar		name[STL_STYLE_NAME_MAX];	/* Name of this style (e.g. "WAV", "HTML"). */
	GHashTable	*properties;			/* A hash of properties for this style. */
	gboolean	expand;				/* Is the style shown expanded in the config? */
};

/* This is used internally by the stl_styleinfo_style_find() function. */
struct style_find_data {
	const gchar	*name;
	Style		*stl;
};

/* This is used by stl_styleinfo_widget_find(). */
struct widget_find_data {
	Style		*style;
	GtkWidget	*widget;
};

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

/* 1999-05-20 -	Create a new, empty, styleinfo. */
StyleInfo *stl_styleinfo_new(void)
{
	StyleInfo	*si;

	si = g_malloc(sizeof *si);
	si->freeze_count   = 0U;
	si->update_pending = FALSE;
	si->styles = NULL;

	return si;
}

/* 1999-05-19 -	Create a new styleinfo containing the built-in default styles. There are few. */
StyleInfo * stl_styleinfo_default(void)
{
	StyleInfo	*si;
	Style		*stl, *root;

	si = stl_styleinfo_new();
	stl_styleinfo_freeze(si);

	root = stl_style_new("Root");
	stl_style_property_set_color_rgb(root, SPN_COL_UNSEL_BG, 56540U, 56540U, 56540U);
	stl_style_property_set_color_rgb(root, SPN_COL_UNSEL_FG, 0U, 0U, 0U);
	stl_style_property_set_icon(root, SPN_ICON_UNSEL, "Document.xpm");
	stl_styleinfo_style_add(si, NULL, root);

	stl = stl_style_new("Directory");
	stl_style_property_set_color_rgb(stl, SPN_COL_UNSEL_FG, 65535U, 16384U, 8192U);
	stl_style_property_set_icon(stl, SPN_ICON_UNSEL, "Directory2.xpm");
	stl_style_property_set_action(stl, SPN_ACTN_DEFAULT, "DirEnter");
	stl_styleinfo_style_add(si, root, stl);

	stl_styleinfo_thaw(si);
	return si;
}

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

/* 1999-05-20 -	Recursively copy styles and add them to the parent style pointed at by <user>. */
static void styleinfo_style_copy(GNode *node, gpointer user)
{
	Style	*stl, *parent = user;

	stl = stl_style_copy(node->data);
	stl_styleinfo_style_add(parent->styleinfo, parent, stl);
	g_node_children_foreach(node, G_TRAVERSE_ALL, styleinfo_style_copy, stl);
}

/* 1999-05-20 -	Create an identical copy of <si>, sharing no memory between the two. */
StyleInfo * stl_styleinfo_copy(StyleInfo *si)
{
	StyleInfo	*nsi = NULL;
	Style		*root;

	if(si != NULL)
	{
		nsi = stl_styleinfo_new();
		if(si->styles != NULL)
		{
			stl_styleinfo_freeze(nsi);
			root = stl_style_copy(si->styles->data);
			stl_styleinfo_style_add(nsi, NULL, root);
			g_node_children_foreach(si->styles, G_TRAVERSE_ALL, styleinfo_style_copy, root);
			stl_styleinfo_thaw(nsi);
		}
	}
	return nsi;
}

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

/* 1999-05-20 -	If the style in <user> doesn't override the property <value>, create
**		a non-copying link to it, thus inheriting the value. This is it.
*/
static void property_update(gpointer key, gpointer value, gpointer user)
{
	Style	*stl = user;
	StlProp	*pr, *ppr = value;

	/* Is there a property with the same name? */
	if((pr = g_hash_table_lookup(stl->properties, ppr->name)) != NULL)
	{
		if(!pr->override)
			pr->value = ppr->value;		/* Establish the copy link. */
	}
	else	/* No property found, so create a fresh linking one. */
	{
		pr = g_malloc(sizeof *pr);
		str_strncpy(pr->name, ppr->name, sizeof pr->name);
		pr->override = FALSE;
		pr->value = ppr->value;
		g_hash_table_insert(stl->properties, pr->name, pr);
	}
}

/* 1999-05-20 -	Update a single style, and then recurse to do its children. Heavily recursive. */
static void style_update(GNode *node, gpointer user)
{
	Style	*stl = node->data, *parent = user;

	g_hash_table_foreach(parent->properties, property_update, stl);
	g_node_children_foreach(node, G_TRAVERSE_ALL, style_update, stl);
}

/* 1999-05-19 -	Update style tree in <si>; causes all non-overridden properties in all styles to
**		aquire the value of the nearest overriding parent. Simple.
*/
static void styleinfo_update(StyleInfo *si)
{
	if(si != NULL)
	{
		if(si->freeze_count == 0)
			g_node_children_foreach(si->styles, G_TRAVERSE_ALL, style_update, si->styles->data);
		else
			si->update_pending = TRUE;
	}
}

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

/* 1999-05-19 -	Freeze given styleinfo. When a styleinfo is frozen, you can add, alter, and
**		remove styles without causing the entire inheritance tree to be traversed
**		on each operation. Handy when doing loads of such operations.
*/
void stl_styleinfo_freeze(StyleInfo *si)
{
	if(si != NULL)
		si->freeze_count++;
}

/* 1999-05-19 -	Thaw a styleinfo. If anything was changed during freeze, update tree now. */
void stl_styleinfo_thaw(StyleInfo *si)
{
	if(si != NULL)
	{
		if(si->freeze_count == 0)
			fprintf(stderr, "STYLES: Mismatched call to stl_styleinfo_thaw() detected!\n");
		else if(--si->freeze_count == 0)
		{
			if(si->update_pending)
				styleinfo_update(si);
		}
	}
}

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

/* 1999-05-20 -	A g_hash_table_foreach() callback. */
static void dump_property(gpointer key, gpointer value, gpointer user)
{
	StlProp	*pr = value;
	guint	i, d = *(guint *) user;
	gchar	*tname[] = { "Color", "Icon", "Action" };

	for(i = 0; i < d; i++)
		putchar(' ');
	printf("(%c) %s [%s: ", pr->override ? 'X' : ' ', pr->name, tname[pr->value->type]);

	switch(pr->value->type)
	{
		case SPT_COLOR:
			printf("%04X,%04X,%04X", pr->value->data.color.red, pr->value->data.color.green, pr->value->data.color.blue);
			break;
		case SPT_ICON:
			printf("\"%s\"", pr->value->data.icon);
			break;
		case SPT_ACTION:
			printf("'%s'", pr->value->data.action->str);
			break;
	}
	printf("]\n");
}

/* 1999-05-20 -	A g_node_traverse() callback. */
static gboolean dump_style(GNode *node, gpointer user)
{
	guint	i, d = g_node_depth(node);

	for(i = 0; i < d - 1; i++)
		putchar('-');
	printf(" '%s'\n", ((Style *) node->data)->name);
	g_hash_table_foreach(((Style *) node->data)->properties, dump_property, &d);
	return FALSE;
}

/* 1999-05-20 -	Dump the contents of given styleinfo's style tree. Mainly useful during
**		development and debugging of this module. Should not be included in release
**		builds of gentoo, since it's not really useful there.
*/
void stl_styleinfo_dump(StyleInfo *si)
{
	guint	level;

	printf("There are %u styles in given styleinfo:\n", g_node_n_nodes(si->styles, G_TRAVERSE_ALL));
	g_node_traverse(si->styles, G_PRE_ORDER, G_TRAVERSE_ALL, -1, dump_style, &level);
}

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

/* 1999-05-19 -	Get the GNode that holds the given <stl>. */
static GNode * styleinfo_style_get_node(StyleInfo *si, Style *stl)
{
	return g_node_find(si->styles, G_PRE_ORDER, G_TRAVERSE_ALL, stl);
}

/* 1999-05-20 -	Find the sibling of <parent> that should come next after <node>,
**		if it were inserted. The implied ordering is simply alphabetical.
**		If there is no suitable sibling (<=> stl goes last), return NULL.
*/
static GNode * find_next_sibling(GNode *parent, GNode *node)
{
	guint	i;
	GNode	*here;

	for(i = 0; (here = g_node_nth_child(parent, i)) != NULL; i++)
	{
		if(strcmp(((Style *) here->data)->name, ((Style *) node->data)->name) > 0)
			return here;
	}
	return NULL;
}

/* 1999-05-19 -	Add a style to <si>, linking it in so <parent> is its immediate parent.
**		Call with <parent> == NULL to set the root style (moving the existing
**		styles, if any, down one level).
*/
void stl_styleinfo_style_add(StyleInfo *si, Style *parent, Style *stl)
{
	if((si != NULL) && (stl != NULL))
	{
		GNode	*node, *pnode;

		stl->styleinfo = si;
		node = g_node_new(stl);

		if(parent == NULL)		/* Insert as root? */
		{
			if(si->styles != NULL)	/* Is there a parent to replace? */
				g_node_insert(node, -1, si->styles);
			si->styles = node;
		}
		else
		{
			if((pnode = styleinfo_style_get_node(si, parent)) != NULL)
				g_node_insert_before(pnode, find_next_sibling(pnode, node), node);
			else
				fprintf(stderr, "STYLES: Unknown parent style '%s' referenced\n", parent->name);
		}
		styleinfo_update(si);
	}
}

/* 1999-05-20 -	A g_node_traverse() callback, to free the memory used by a style. */
static gboolean remove_style(GNode *node, gpointer user)
{
	if(node != user)	/* Avoid recursing on root. */
		stl_style_destroy(node->data, FALSE);
	return FALSE;		/* Keep traversing. */
}

/* 1999-05-20 -	Remove a style from the styleinfo tree. Causes all child styles to be
**		removed and destroyed, as well.
*/
void stl_styleinfo_style_remove(StyleInfo *si, Style *stl)
{
	if((si != NULL) && (stl != NULL))
	{
		GNode	*node;

		if((node = styleinfo_style_get_node(si, stl)) != NULL)
		{
			g_node_unlink(node);
			g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1, remove_style, node);
			g_node_destroy(node);
			styleinfo_update(si);
		}
	}
}

/* 1999-05-20 -	Check for a named node. */
static gboolean find_callback(GNode *node, gpointer user)
{
	struct style_find_data	*sfd = user;

	if(strcmp(((Style *) node->data)->name, sfd->name) == 0)
	{
		sfd->stl = node->data;
		return TRUE;	/* Wanted node has been found, so stop. */
	}
	return FALSE;		/* Keep traversing. */
}

/* 1999-05-20 -	Find the named style in given styleinfo's style tree. NULL can be used as
**		a shorthand for the name of the root style.
*/
Style * stl_styleinfo_style_find(StyleInfo *si, const gchar *name)
{
	struct style_find_data	sfd;

	sfd.name = name;
	sfd.stl  = NULL;
	if(si != NULL)
	{
		if(name != NULL)
			g_node_traverse(si->styles, G_PRE_ORDER, G_TRAVERSE_ALL, -1, find_callback, &sfd);
		else
			return stl_styleinfo_style_root(si);
	}
	return sfd.stl;
}

/* 1999-05-24 -	Change the parent for <stl>. <new_parent> should really NOT be a child of
**		<stl>, or there will definitely be evil. Note that this operation can NOT
**		be done as a remove()/add() combo, since the remove is destructive.
*/
void stl_styleinfo_style_set_parent(StyleInfo *si, Style *stl, Style *new_parent)
{
	if((si != NULL) && (stl != NULL))
	{
		GNode	*node, *pnode;

		node = styleinfo_style_get_node(si, stl);
		g_node_unlink(node);
		pnode = styleinfo_style_get_node(si, new_parent);
		g_node_insert_before(pnode, find_next_sibling(pnode, node), node);
		styleinfo_update(si);
	}
}

/* 1999-05-24 -	Get the parent style of <stl>. If there is no parent (<stl> is root, or
**		perhaps has not yet been added to a styleinfo), NULL is returned.
*/
Style * stl_styleinfo_style_get_parent(StyleInfo *si, Style *stl)
{
	if((stl != NULL) && (stl->styleinfo == si))
	{
		GNode	*node;

		node = styleinfo_style_get_node(stl->styleinfo, stl);
		if(node->parent != NULL)
			return node->parent->data;
	}
	return NULL;
}

/* 1999-05-26 -	Return the root style of <si>. This is actually the only reliable way of getting
**		at it, since you don't know its name.
*/
Style * stl_styleinfo_style_root(StyleInfo *si)
{
	if(si != NULL)
	{
		if(si->styles != NULL)
			return si->styles->data;
	}
	return NULL;
}

/* 1999-05-27 -	Return TRUE if <stl> has one or more children, otherwise FALSE. */
gboolean stl_styleinfo_style_has_children(StyleInfo *si, Style *stl)
{
	if((si != NULL) && (stl != NULL))
	{
		GNode	*node;

		if((node = styleinfo_style_get_node(si, stl)) != NULL)
			return g_node_n_children(node) > 0 ? TRUE : FALSE;
	}
	return FALSE;
}

/* 1999-05-29 -	Just a g_node_traverse() callback to build a list of child styles. */
static gboolean get_child(GNode *node, gpointer user)
{
	GList	**list = user;

	*list = g_list_append(*list, node->data);

	return FALSE;	/* And keep traversing. */
}

/* 1999-05-29 -	Return a list of all child styles of <stl>. If <include_root> is TRUE, then
**		the root style <stl> will be included in the list. If FALSE, it will not.
**		Don't forget to g_list_free() the returned list when you're done with it.
*/
GList * stl_styleinfo_style_get_children(StyleInfo *si, Style *stl, gboolean include_root)
{
	if((si != NULL) && (stl != NULL) && (stl->styleinfo == si))
	{
		GNode	*node;

		if((node = styleinfo_style_get_node(si, stl)) != NULL)
		{
			GList	*list = NULL;

			g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, get_child, &list);
			if(!include_root)
			{
				GList	*first;

				first = list;
				list = g_list_remove_link(list, first);	/* Remove <stl> from child list. */
				g_list_free(first);
			}
			return list;
		}
	}
	return NULL;
}

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

/* 1999-05-27 -	A g_hash_table_foreach() callback that writes out a property definition. */
static void save_property(gpointer key, gpointer value, gpointer user)
{
	StlProp	*pr = value;
	FILE	*out = user;

	/* We don't save non-overridden properties, of course. */
	if(!pr->override)
		return;

	xml_put_node_open(out, "Property");
	xml_put_text(out, "name", pr->name);
	switch(pr->value->type)
	{
		case SPT_COLOR:
			xml_put_color(out, "color", &pr->value->data.color);
			break;
		case SPT_ICON:
			xml_put_text(out, "icon", pr->value->data.icon);
			break;
		case SPT_ACTION:
			xml_put_text(out, "action", pr->value->data.action->str);
			break;
	}
	xml_put_node_close(out, "Property");
}

/* 1999-05-27 -	A g_node_traverse() function to save out a single style. */
static gboolean save_style(GNode *node, gpointer user)
{
	Style	*stl = node->data;
	FILE	*out = user;

	xml_put_node_open(out, "Style");
	xml_put_text(out, "name", stl->name);
	if(node->parent != NULL)
		xml_put_text(out, "parent", ((Style *) node->parent->data)->name);
	xml_put_boolean(out, "expand", stl->expand);
	if(stl_style_property_overrides(stl))
	{
		xml_put_node_open(out, "Properties");
		g_hash_table_foreach(stl->properties, save_property, out);
		xml_put_node_close(out, "Properties");
	}
	xml_put_node_close(out, "Style");

	return FALSE;		/* Keep traversing, we relly want to save all styles. */
}

/* 1999-05-27 -	Save out contents of <si> into <out>, nicely bracketed by <tag> tags. */
void stl_styleinfo_save(MainInfo *min, StyleInfo *si, FILE *out, const gchar *tag)
{
	if((min == NULL) || (si == NULL) || (out == NULL) || (tag == NULL))
		return;

	xml_put_node_open(out, tag);
	g_node_traverse(si->styles, G_PRE_ORDER, G_TRAVERSE_ALL, -1, save_style, out);
	xml_put_node_close(out, tag);
}

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

/* 1999-05-20 -	Load an old-styled property. Convert legacy properties, identified using
**		integers, into the newer named style. Add the property to the style pointed
**		to by <user>.
*/
static void old_load_property(XmlNode *node, gpointer user)
{
	gint		type;
	gboolean	ok = FALSE;
	gchar		*name = NULL, *text = NULL;
	GdkColor	col;
	StlPType	newtype;
	Style		*stl = user;

	xml_get_integer(node, "type", &type);

	/* Map old integer-identified properties to new textually named ones. */
	switch(type)
	{
		case L_TYPE_UNSEL_BGCOL:
			name = SPN_COL_UNSEL_BG;
			if((ok = xml_get_color(node, "color", &col)))
				newtype = SPT_COLOR;
			break;
		case L_TYPE_UNSEL_FGCOL:
			name = SPN_COL_UNSEL_FG;
			if((ok = xml_get_color(node, "color", &col)))
				newtype = SPT_COLOR;
			break;
		case L_TYPE_UNSEL_ICON:
			name = SPN_ICON_UNSEL;
			if((ok = xml_get_text(node, "icon", &text)))
				newtype = SPT_ICON;
			break;
		case L_TYPE_ACTN_DEFAULT:
			name = SPN_ACTN_DEFAULT;
			if((ok = xml_get_text(node, "action", &text)))
				newtype = SPT_ACTION;
			break;
		case L_TYPE_ACTN_VIEW:
			name = SPN_ACTN_VIEW;
			if((ok = xml_get_text(node, "action", &text)))
				newtype = SPT_ACTION;
			break;
		case L_TYPE_ACTN_EDIT:
			name = SPN_ACTN_EDIT;
			if((ok = xml_get_text(node, "action", &text)))
				newtype = SPT_ACTION;
			break;
		case L_TYPE_ACTN_PRINT:
			name = SPN_ACTN_PRINT;
			if((ok = xml_get_text(node, "action", &text)))
				newtype = SPT_ACTION;
			break;
		case L_TYPE_ACTN_PLAY:
			name = SPN_ACTN_PLAY;
			if((ok = xml_get_text(node, "action", &text)))
				newtype = SPT_ACTION;
			break;
		default:
			fprintf(stderr, "STYLES: Ignoring unknown legacy property type %d in style '%s'\n", type, stl->name);
	}
	if(ok)
	{
		switch(newtype)
		{
			case SPT_COLOR:
				stl_style_property_set_color(stl, name, &col);
				break;
			case SPT_ICON:
				stl_style_property_set_icon(stl, name, text);
				break;
			case SPT_ACTION:
				stl_style_property_set_action(stl, name, csq_cmdseq_map_name(text, stl->name));
				break;
		}
	}
}

/* 1999-05-27 -	Load modern-style property (has textual identifier). Pretty simple stuff. */
static void load_property(XmlNode *data, gpointer user)
{
	Style	*stl = user;
	gchar	*name;

	if(xml_get_text(data, "name", &name))
	{
		GdkColor	color;
		gchar		*text;

		if(xml_get_color(data, "color", &color))
			stl_style_property_set_color(stl, name, &color);
		else if(xml_get_text(data, "icon", &text))
			stl_style_property_set_icon(stl, name, text);
		else if(xml_get_text(data, "action", &text))
			stl_style_property_set_action(stl, name, csq_cmdseq_map_name(text, stl_style_get_name(stl)));
		else
			fprintf(stderr, "STYLES: Unknown value type for property '%s' (style '%s')\n", name, stl->name);
	}
}

/* 1999-05-20 -	Another xml_node_visit_children() callback. Load in all properties for
**		given style, and store them in the style. This is where the backwards-
**		compatibility really gets going.
*/
static void load_properties(XmlNode *node, Style *stl)
{
	XmlNode	*data;

	if((data = xml_tree_search(node, "Property")) != NULL)
	{
		if(xml_get_integer(data, "type", NULL))
			xml_node_visit_children(node, old_load_property, stl);
		else
			xml_node_visit_children(node, load_property, stl);
	}
}

/* 1999-05-20 -	A xml_node_visit_children() callback to load in a style. Deals with the
**		annoying complexities of backwards compatibility. :(
*/
static void load_style(XmlNode *node, gpointer user)
{
	StyleInfo	*si = user;
	Style		*stl, *parent = NULL;
	gchar		*name = "new style", *pname;
	XmlNode		*prop;
	gboolean	btmp = FALSE;

	xml_get_text(node, "name", &name);
	stl = stl_style_new(name);
	
	xml_get_boolean(node, "expand", &btmp);
	stl_style_set_expand(stl, btmp);

	if((prop = xml_tree_search(node, "Properties")) != NULL)
		load_properties(prop, stl);

	if(xml_get_text(node, "parent", &pname))
		parent = stl_styleinfo_style_find(si, pname);
	stl_styleinfo_style_add(si, parent, stl);
}

/* 1999-05-20 -	Load an entire styleinfo tree from XML data rooted at <node>. Note that the
**		top-level tag identifier (the one passed to stl_styleinfo_save()) should already
**		have been used before calling this function, to find <node>. It is assumed
**		that the given node contains a bunch of children, each of which defines a style.
*/
StyleInfo * stl_styleinfo_load(XmlNode *node)
{
	StyleInfo	*si;

	si = stl_styleinfo_new();
	stl_styleinfo_freeze(si);
	xml_node_visit_children(node, load_style, si);
	stl_styleinfo_thaw(si);

	return si;
}

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

/* 1999-05-20 -	Destroy given styleinfo, freeing all memory used by it. */
void stl_styleinfo_destroy(StyleInfo *si)
{
	if(si != NULL)
	{
		stl_styleinfo_freeze(si);	/* There's no point in updating inherited stuff while destroying. */
		if(si->styles != NULL)
			stl_style_destroy(si->styles->data, TRUE);
		g_free(si);
	}
}

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

/* 1999-05-22 -	User just expanded a node in the tree. */
static void evt_style_expand(GtkWidget *wid, gpointer user)
{
	Style	*stl = gtk_object_get_user_data(GTK_OBJECT(wid));

	stl->expand = TRUE;
}

/* 1999-05-22 -	User collapsed a style node. */
static void evt_style_collapse(GtkWidget *wid, gpointer user)
{
	Style	*stl = gtk_object_get_user_data(GTK_OBJECT(wid));

	stl->expand = FALSE;
}

/* 1999-05-22 -	Recursively build a tree rooted at <node>, and add it to <parent>. */
static void build_branch(GNode *node, GtkWidget *parent, Style *ignore)
{
	GtkWidget	*item;

	for(; (node != NULL); node = node->next)
	{
		if((ignore != NULL) && (node->data == ignore))
			continue;
		item = gtk_tree_item_new_with_label(((Style *) node->data)->name);
		gtk_object_set_user_data(GTK_OBJECT(item), node->data);
		gtk_tree_append(GTK_TREE(parent), item);
		gtk_widget_show(item);
		if(node->children)
		{
			GtkWidget	*tree;

			tree = gtk_tree_new();
			build_branch(node->children, tree, ignore);
			gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), tree);
			gtk_signal_connect(GTK_OBJECT(item), "expand", GTK_SIGNAL_FUNC(evt_style_expand), NULL);
			gtk_signal_connect(GTK_OBJECT(item), "collapse", GTK_SIGNAL_FUNC(evt_style_collapse), NULL);
		}
		if(((Style *) node->data)->expand)
			gtk_tree_item_expand(GTK_TREE_ITEM(item));
	}
}

/* 1999-05-22 -	Create and populate a GtkTree widget, holding the style tree. Recursive. To track selections
**		in the tree, use the "selection_changed" signal, together with the GTK_TREE_SELECTION() macro.
*/
GtkTree * stl_styleinfo_build(StyleInfo *si)
{
	GtkWidget	*tree;

	tree = gtk_tree_new();
	build_branch(si->styles, tree, NULL);

	return GTK_TREE(tree);
}

/* 1999-05-24 -	Build a partial style tree, ignoring any branch rooted at <ignore>. */
GtkTree * stl_styleinfo_build_partial(StyleInfo *si, Style *ignore)
{
	GtkWidget	*tree;

	tree = gtk_tree_new();
	build_branch(si->styles, tree, ignore);

	return GTK_TREE(tree);
}

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

/* 1999-05-26 -	Check if <wid> is the GtkTreeItem used to build the Style in user->style. */
static void find_widget(GtkWidget *wid, gpointer user)
{
	struct widget_find_data	*wfd = user;
	GtkWidget	*subtree;

	if(gtk_object_get_user_data(GTK_OBJECT(wid)) == wfd->style)
		wfd->widget = wid;
	else if(GTK_IS_TREE_ITEM(wid) && ((subtree = GTK_TREE_ITEM_SUBTREE(GTK_TREE_ITEM(wid))) != NULL))
		gtk_container_foreach(GTK_CONTAINER(subtree), find_widget, user);
}

/* 1999-05-26 -	Return the widget (typically a GtkTreeItem) used to represent <stl> of <si> in
**		<tree>. Of course, <tree> has been previously returned by stl_styleinfo_build_XXX().
**		Note that because of the (IMHO) oddly limited semantics of gtk_container_foreach()
**		when applied on a tree, we do further recursion in the callback. Messy, but OK.
*/
GtkWidget * stl_styleinfo_widget_find(StyleInfo *si, GtkTree *tree, Style *stl)
{
	struct widget_find_data	wfd = { NULL, NULL };

	if((si != NULL) && (tree != NULL) && (stl != NULL))
	{
		wfd.style = stl;
		gtk_container_foreach(GTK_CONTAINER(tree), find_widget, &wfd);
	}
	return wfd.widget;
}

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

/* 1999-05-19 -	Create a new, empty style named <name>. */
Style * stl_style_new(const gchar *name)
{
	Style	*stl;

	stl = g_malloc(sizeof *stl);
	str_strncpy(stl->name, name, sizeof stl->name);
	stl->styleinfo = NULL;
	stl->properties = g_hash_table_new(g_str_hash, g_str_equal);
	stl->expand = TRUE;

	return stl;
}

/* 1999-05-26 -	Create a new style, taking care to choose a name not used in <si>.
** BUG BUG BUG	If you have 9999 styles called "New Style N", this might fail... Big "oops".
*/
Style * stl_style_new_unique_name(StyleInfo *si)
{
	if(si != NULL)
	{
		gchar	buf[STL_STYLE_NAME_MAX];
		guint	i = 0;

		do
		{
			g_snprintf(buf, sizeof buf, i == 0 ? "New Style" : "New Style %u", i + 1);
			i++;
		} while((stl_styleinfo_style_find(si, buf) != NULL) && (i < 10000));	/* This might fail. Big deal. */

		return stl_style_new(buf);
	}
	return NULL;
}

/* 1999-05-20 -	Copy a property, storing the copy in the style pointed to by <user>. */
static void property_copy(gpointer key, gpointer value, gpointer user)
{
	StlProp	*pr = value;
	Style	*stl = user;

	if(pr->override)
	{
		switch(pr->value->type)
		{
			case SPT_COLOR:
				stl_style_property_set_color(stl, pr->name, &pr->value->data.color);
				break;
			case SPT_ICON:
				stl_style_property_set_icon(stl, pr->name, pr->value->data.icon);
				break;
			case SPT_ACTION:
				stl_style_property_set_action(stl, pr->name, pr->value->data.action->str);
				break;
		}
	}
}

/* 1999-05-19 -	Copy a style, creating a non-memory-sharing duplicate. Note that we only
**		copy overriden properties, relying on the fact that if the copy is going
**		to be used (i.e., added to a styleinfo), its inheritance will be computed.
*/
Style * stl_style_copy(Style *stl)
{
	Style	*ns = NULL;

	if(stl != NULL)
	{
		ns = stl_style_new(stl->name);
		ns->expand = stl->expand;
		g_hash_table_foreach(stl->properties, property_copy, ns);
	}
	return ns;
}

/* 1999-05-24 -	Return style from a tree selection (<wid> should be the widget passed to
**		the select handler used by stl_styleinfo_build().
*/
Style * stl_style_get(GtkWidget *wid)
{
	if(wid != NULL)
		return gtk_object_get_user_data(GTK_OBJECT(wid));
	return NULL;
}

/* 1999-05-22 -	Set the expand status of a style. Used when building GUI representation. */
void stl_style_set_expand(Style *stl, gboolean expand)
{
	if(stl != NULL)
		stl->expand = expand;
}

/* 1999-05-22 -	Get the current expand/collapse status of given style. */
gboolean stl_style_get_expand(Style *stl)
{
	if(stl != NULL)
		return stl->expand;
	return FALSE;
}

/* 1999-05-24 -	Rename a style. */
void stl_style_set_name(Style *stl, const gchar *name)
{
	if((stl != NULL) && (name != NULL))
		str_strncpy(stl->name, name, sizeof stl->name);
}

/* 1999-05-24 -	Update label of treeitem <wid> to <name>. This is only useful in config. */
void stl_style_set_name_widget(GtkWidget *wid, const gchar *name)
{
	if((wid != NULL) && GTK_IS_TREE_ITEM(wid) && (name != NULL))
		gtk_label_set_text(GTK_LABEL(GTK_BIN(wid)->child), name);
}

/* 1999-05-20 -	Just get a pointer (read-only, as usual) to the style's name. */
const gchar * stl_style_get_name(Style *stl)
{
	if(stl != NULL)
		return stl->name;
	return NULL;
}

/* 1999-05-20 -	Get the full name of <stl>. A full name contains a leading "path" of
**		parent styles, separated with slashes. Example: "Root/Data/Images/GIF".
**		It is the caller's responsibility to call g_string_free() on the returned
**		string when done with it, of course.
** BUG BUG BUG	This is far from efficent. Beware.
*/
GString * stl_style_get_name_full(Style *stl)
{
	if(stl != NULL)
	{
		GNode	*node;

		if((node = styleinfo_style_get_node(stl->styleinfo, stl)) != NULL)
		{
			GString	*str;

			str = g_string_new(stl->name);
			for(node = node->parent; node != NULL; node = node->parent)
			{
				g_string_prepend_c(str, '/');
				g_string_prepend(str, ((Style *) node->data)->name);
			}
			return str;
		}
	}
	return NULL;
}

/* 1999-05-20 -	Compare hierarchial names of <stla> and <stlb>. Used by dirpane module,
**		to sort on styles. Comparison is done so that styles with common parents
**		are grouped.
*/
gint stl_style_compare_hierarchy(Style *stla, Style *stlb)
{
	return 0;
}

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

/* 1999-05-19 -	Create a new value structure, init it as the given type, and return a pointer to it. */
static StlPVal * value_new(StlPType type)
{
	StlPVal	*val;

	val = g_malloc(sizeof *val);
	val->type = type;
	switch(type)
	{
		case SPT_COLOR:
			val->data.color.red   = 0U;
			val->data.color.green = 0U;
			val->data.color.blue  = 0U;
			val->data.color.pixel = 0LU;
			break;
		case SPT_ICON:
			val->data.icon[0] = '\0';
			break;
		case SPT_ACTION:
			val->data.action = g_string_new("");
			break;
	}
	return val;
}

/* 1999-05-19 -	Destroy a value. */
static void value_destroy(StlPVal *val)
{
	if(val != NULL)
	{
		switch(val->type)
		{
			case SPT_COLOR:
			case SPT_ICON:
				break;
			case SPT_ACTION:
				g_string_free(val->data.action, TRUE);
				break;
		}
		g_free(val);
	}
}

/* 1999-05-20 -	Get a pointer to an overriding property named <name> in <stl>, whose type
**		is <type>. If no such property exists, create it.
*/
static StlProp * style_get_property(Style *stl, const gchar *name, StlPType type)
{
	StlProp	*pr;

	if((pr = g_hash_table_lookup(stl->properties, name)) != NULL)
	{
		if(pr->value->type != type)
			fprintf(stderr, "STYLES: Style '%s' has wrong-type property '%s' (%u, not %u)\n", stl->name, name, pr->value->type, type);
		if(pr->override == TRUE)
			return pr;
	}
	else
	{
		pr = g_malloc(sizeof *pr);
		str_strncpy(pr->name, name, sizeof pr->name);
	}
	pr->override = TRUE;
	pr->value    = value_new(type);

	g_hash_table_insert(stl->properties, pr->name, pr);

	return pr;
}

/* 1999-05-19 -	Destroy a property, freeing all memory occupied by it. When this is called,
**		the property better NOT be still held in a style's hash table!
*/
static void style_property_destroy(StlProp *pr)
{
	if(pr != NULL)
	{
		if(pr->override)
			value_destroy(pr->value);
		g_free(pr);
	}
}

/* 1999-05-19 -	Set a new color for a color property. */
void stl_style_property_set_color(Style *stl, const gchar *property, const GdkColor *value)
{
	StlProp	*pr;

	if((stl != NULL) && (value != NULL))
	{
		pr = style_get_property(stl, property, SPT_COLOR);
		pr->value->data.color = *value;
		styleinfo_update(stl->styleinfo);
	}
}

/* 1999-05-19 -	Set a color property, using an RGB triplet. Sometimes convenient. */
void stl_style_property_set_color_rgb(Style *stl, const gchar *property, guint16 red, guint16 green, guint16 blue)
{
	GdkColor	color;

	color.red = red;
	color.green = green;
	color.blue = blue;
	color.pixel = 0UL;
	stl_style_property_set_color(stl, property, &color);
}

/* 1999-05-19 -	Set the name of an icon-property. */
void stl_style_property_set_icon(Style *stl, const gchar *property, const gchar *value)
{
	StlProp	*pr;

	if((stl != NULL) && (value != NULL))
	{
		pr = style_get_property(stl, property, SPT_ICON);
		str_strncpy(pr->value->data.icon, value, sizeof pr->value->data.icon);
		styleinfo_update(stl->styleinfo);
	}
}

/* 1999-05-19 -	Set an action property to a new string. Note that empty strings are not allowed. */
void stl_style_property_set_action(Style *stl, const gchar *property, const gchar *value)
{
	StlProp	*pr;

	if((stl != NULL) && (value != NULL) && (*value != '\0'))
	{
		pr = style_get_property(stl, property, SPT_ACTION);
		g_string_assign(pr->value->data.action, value);
		styleinfo_update(stl->styleinfo);
	}
}

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

/* 1999-05-19 -	Get a read-only pointer to the color held in a color-property. */
const GdkColor * stl_style_property_get_color(Style *stl, const gchar *property)
{
	StlProp	*pr;

	if((stl != NULL) && (property != NULL) && ((pr = g_hash_table_lookup(stl->properties, property)) != NULL))
	{
		if(pr->value->type == SPT_COLOR)
			return &pr->value->data.color;
	}
	return NULL;
}

/* 1999-05-19 -	Get a (read-only) icon name of an appropriately typed property. */
const gchar * stl_style_property_get_icon(Style *stl, const gchar *property)
{
	StlProp	*pr;

	if((stl != NULL) && (property != NULL) && ((pr = g_hash_table_lookup(stl->properties, property)) != NULL))
	{
		if(pr->value->type == SPT_ICON)
			return pr->value->data.icon;
	}
	return NULL;
}

/* 1999-05-19 -	Get a (read-only, dammit) pointer to an action property definition. */
const gchar * stl_style_property_get_action(Style *stl, const gchar *property)
{
	StlProp	*pr;

	if((stl != NULL) && (property != NULL) && ((pr = g_hash_table_lookup(stl->properties, property)) != NULL))
	{
		if(pr->value->type == SPT_ACTION)
			return pr->value->data.action->str;
	}
	return NULL;
}

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

/* 1999-05-24 -	Get the override status (which tells if the property is local or inherited) for
**		a named property.
*/
gboolean stl_style_property_get_override(Style *stl, const gchar *property)
{
	StlProp	*pr;

	if((stl != NULL) && (property != NULL) && ((pr = g_hash_table_lookup(stl->properties, property)) != NULL))
		return pr->override;
	return FALSE;
}

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

static void get_action(gpointer key, gpointer value, gpointer user)
{
	GList	**list = user;

	if(((StlProp *) value)->value->type == SPT_ACTION)
		*list = g_list_insert_sorted(*list, ((StlProp *) value)->name, (GCompareFunc) strcmp);
}

/* 1999-05-24 -	Return a list of (very read-only) action property names. The list is valid
**		until the next person sneezes, or someone calls property_set() or _remove().
**		The list can be freed with a simple call to g_list_free().
*/
GList * stl_style_property_get_actions(Style *stl)
{
	GList	*list = NULL;

	if(stl != NULL)
		g_hash_table_foreach(stl->properties, get_action, &list);
	return list;
}

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

/* 1999-05-27 -	Another g_hash_table_foreach() callback. This one checks for overrides. */
static void check_override(gpointer key, gpointer value, gpointer user)
{
	gboolean	*overrides = user;

	if(((StlProp *) value)->override)
		*overrides = TRUE;
}

/* 1999-05-27 -	Return TRUE if the given style overrides any property, FALSE if all it does
**		is inherit them. Handy when saving, to avoid empty "<Properties></Properties>"
**		stuff.
*/
gboolean stl_style_property_overrides(Style *stl)
{
	gboolean	overrides = FALSE;

	if(stl != NULL)
		g_hash_table_foreach(stl->properties, check_override, &overrides);

	return overrides;
}

/* 1999-05-25 -	Answer whether the named <property> is unique in the style tree. If a property
**		is unique, deleting it will truly delete the property. If it's not, deleting
**		it will just replace the value with an inherited value. This distinction is not
**		very interesting in itself, but it helps in user interface design. :)
*/
gboolean stl_style_property_is_unique(Style *stl, const gchar *property)
{
	if((stl != NULL) && (property != NULL))
	{
		GNode	*node;

		if((node = styleinfo_style_get_node(stl->styleinfo, stl)) != NULL)
		{
			for(node = node->parent; node != NULL; node = node->parent)
			{
				if(g_hash_table_lookup(((Style *) node->data)->properties, property))
					return FALSE;
			}
			return TRUE;
		}
	}
	return FALSE;
}

/* 1999-05-25 -	Rename <property> to <new_name>. If there is already a property with that name
**		in the given <stl>, rename will fail and return FALSE. Else, TRUE is returned.
*/
gboolean stl_style_property_rename(Style *stl, const gchar *property, const gchar *new_name)
{
	if((stl != NULL) && (property != NULL) && (new_name != NULL))
	{
		StlProp	*pr;

		if(g_hash_table_lookup(stl->properties, new_name) == NULL)
		{
			if((pr = g_hash_table_lookup(stl->properties, property)) != NULL)
			{
				g_hash_table_remove(stl->properties, pr->name);
				str_strncpy(pr->name, new_name, sizeof pr->name);
				g_hash_table_insert(stl->properties, pr->name, pr);
				styleinfo_update(stl->styleinfo);
				return TRUE;
			}
		}
	}
	return FALSE;
}

/* 1999-05-19 -	Remove (and destroy) a property from a style. */
void stl_style_property_remove(Style *stl, const gchar *property)
{
	StlProp	*pr;

	if((stl != NULL) && ((pr = g_hash_table_lookup(stl->properties, property)) != NULL))
	{
		g_hash_table_remove(stl->properties, property);
		style_property_destroy(pr);
		styleinfo_update(stl->styleinfo);
	}
}

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

/* 1999-05-20 -	A g_hash_table_foreach_remove() callback. Kill a property. */
static gboolean destroy_property(gpointer key, gpointer value, gpointer user)
{
	style_property_destroy(value);

	return TRUE;		/* Really remove. */
}

/* 1999-05-20 -	Destroy a style. If <unlink> is TRUE, it will also remove the style from
**		the styleinfo, thus causing any children to <stl> to be destroyed as well.
**		This is really recommended, as leaving a styleinfo referencing destroyed
**		styles hanging around is likely to be unhealthy.
*/
void stl_style_destroy(Style *stl, gboolean unlink)
{
	if(stl != NULL)
	{
		if(unlink)
			stl_styleinfo_style_remove(stl->styleinfo, stl);
		g_hash_table_foreach_remove(stl->properties, destroy_property, NULL);
		g_hash_table_destroy(stl->properties);
		g_free(stl);
	}
}
