1 /*
2 ** 1999-05-19 -	Started a complete rewrite of this module. It became twice the size of the old
3 **		code, but adds a *lot* of flexibility. And, the config module shrunk, too. :)
4 */
5 
6 #include "gentoo.h"
7 
8 #include <stdlib.h>
9 
10 #include "cmdseq.h"
11 #include "strutil.h"
12 #include "styles.h"
13 
14 /* ----------------------------------------------------------------------------------------- */
15 
16 struct StyleInfo {
17 	guint		freeze_count;			/* When non-zero, changes to styles don't propagate downwards. */
18 	gboolean	update_pending;			/* Set when something changes during freeze. */
19 	GNode		*styles;			/* A N-ary tree holding the style hierarchy. Neat. */
20 };
21 
22 typedef enum { SPT_COLOR = 0, SPT_ICON, SPT_ACTION } StlPType;
23 
24 typedef struct {
25 	StlPType	type;
26 	union {
27 	GdkColor	color;
28 	gchar		icon[FILENAME_MAX];
29 	GString		*action;
30 	}		data;
31 } StlPVal;
32 
33 /* These are the old (L for legacy) integer identifiers for style properties. They are
34 ** used in the old_load_property() function, when converting to the more modern string-
35 ** based property names used by this module.
36 */
37 enum {
38 	L_TYPE_UNSEL_BGCOL	= 0,
39 	L_TYPE_UNSEL_FGCOL	= 1,
40 	L_TYPE_UNSEL_ICON	= 2,
41 	L_TYPE_ACTN_DEFAULT	= 6,
42 	L_TYPE_ACTN_VIEW	= 7,
43 	L_TYPE_ACTN_EDIT	= 8,
44 	L_TYPE_ACTN_PRINT	= 9,
45 	L_TYPE_ACTN_PLAY	= 10
46 };
47 
48 typedef struct {
49 	gchar		name[STL_PROPERTY_NAME_SIZE];	/* Unique property identifier. */
50 	gboolean	override;			/* Is the value really here? */
51 	StlPVal		*value;
52 } StlProp;
53 
54 struct Style {
55 	StyleInfo	*styleinfo;			/* Which styleinfo does this style belong to? */
56 	gchar		name[STL_STYLE_NAME_SIZE];	/* Name of this style (e.g. "WAV", "HTML"). */
57 	GHashTable	*properties;			/* A hash of properties for this style. */
58 	gboolean	expand;				/* Is the style shown expanded in the config? */
59 };
60 
61 /* This is used internally by the stl_styleinfo_style_find() function. */
62 struct style_find_data {
63 	const gchar	*name;
64 	Style		*stl;
65 };
66 
67 /* This is used by stl_styleinfo_widget_find(). */
68 struct widget_find_data {
69 	Style		*style;
70 	GtkWidget	*widget;
71 };
72 
73 struct property_rename_data {
74 	Style		*style;
75 	gchar		name_old[STL_PROPERTY_NAME_SIZE],
76 			name_new[STL_PROPERTY_NAME_SIZE];
77 };
78 
79 /* This is used when looking for a style in a GtkTreeModel. */
80 struct tree_find_data {
81 	const Style	*style;
82 	GtkTreeIter	iter;
83 };
84 
85 /* This is used to avoid magic numbers all over the place. */
86 enum {
87 	TREE_COLUMN_NAME = 0, TREE_COLUMN_STYLE
88 };
89 
90 /* ----------------------------------------------------------------------------------------- */
91 
92 /* 1999-05-20 -	Create a new, empty, styleinfo. */
stl_styleinfo_new(void)93 StyleInfo * stl_styleinfo_new(void)
94 {
95 	StyleInfo	*si;
96 
97 	si = g_malloc(sizeof *si);
98 	si->freeze_count   = 0U;
99 	si->update_pending = FALSE;
100 	si->styles = NULL;
101 
102 	return si;
103 }
104 
105 /* 1999-05-19 -	Create a new styleinfo containing the built-in default styles. There are few. */
stl_styleinfo_default(void)106 StyleInfo * stl_styleinfo_default(void)
107 {
108 	StyleInfo	*si;
109 	Style		*stl, *root;
110 
111 	si = stl_styleinfo_new();
112 	stl_styleinfo_freeze(si);
113 
114 	root = stl_style_new(_("Root"));
115 	stl_style_property_set_color_rgb(root, SPN_COL_UNSEL_BG, 56540U, 56540U, 56540U);
116 	stl_style_property_set_color_rgb(root, SPN_COL_UNSEL_FG, 0U, 0U, 0U);
117 	stl_style_property_set_icon(root, SPN_ICON_UNSEL, "Document.xpm");
118 	stl_styleinfo_style_add(si, NULL, root);
119 
120 	stl = stl_style_new(_("Directory"));
121 	stl_style_property_set_color_rgb(stl, SPN_COL_UNSEL_FG, 65535U, 16384U, 8192U);
122 	stl_style_property_set_icon(stl, SPN_ICON_UNSEL, "Directory2.xpm");
123 	stl_style_property_set_action(stl, SPN_ACTN_DEFAULT, "DirEnter");
124 	stl_styleinfo_style_add(si, root, stl);
125 
126 	stl_styleinfo_thaw(si);
127 	return si;
128 }
129 
130 /* ----------------------------------------------------------------------------------------- */
131 
132 /* 1999-05-20 -	Recursively copy styles and add them to the parent style pointed at by <user>. */
styleinfo_style_copy(GNode * node,gpointer user)133 static void styleinfo_style_copy(GNode *node, gpointer user)
134 {
135 	Style	*stl, *parent = user;
136 
137 	stl = stl_style_copy(node->data);
138 	stl_styleinfo_style_add(parent->styleinfo, parent, stl);
139 	g_node_children_foreach(node, G_TRAVERSE_ALL, styleinfo_style_copy, stl);
140 }
141 
142 /* 1999-05-20 -	Create an identical copy of <si>, sharing no memory between the two. */
stl_styleinfo_copy(StyleInfo * si)143 StyleInfo * stl_styleinfo_copy(StyleInfo *si)
144 {
145 	StyleInfo	*nsi = NULL;
146 	Style		*root;
147 
148 	if(si != NULL)
149 	{
150 		nsi = stl_styleinfo_new();
151 		if(si->styles != NULL)
152 		{
153 			stl_styleinfo_freeze(nsi);
154 			root = stl_style_copy(si->styles->data);
155 			stl_styleinfo_style_add(nsi, NULL, root);
156 			g_node_children_foreach(si->styles, G_TRAVERSE_ALL, styleinfo_style_copy, root);
157 			stl_styleinfo_thaw(nsi);
158 		}
159 	}
160 	return nsi;
161 }
162 
163 /* ----------------------------------------------------------------------------------------- */
164 
165 /* 1999-05-20 -	If the style in <user> doesn't override the property <value>, create
166 **		a non-copying link to it, thus inheriting the value. This is it.
167 */
property_update(gpointer key,gpointer value,gpointer user)168 static void property_update(gpointer key, gpointer value, gpointer user)
169 {
170 	Style	*stl = user;
171 	StlProp	*pr, *ppr = value;
172 
173 	/* Is there a property with the same name? */
174 	if((pr = g_hash_table_lookup(stl->properties, ppr->name)) != NULL)
175 	{
176 		if(!pr->override)
177 			pr->value = ppr->value;		/* Establish the copy link. */
178 	}
179 	else	/* No property found, so create a fresh linking one. */
180 	{
181 		pr = g_malloc(sizeof *pr);
182 		g_strlcpy(pr->name, ppr->name, sizeof pr->name);
183 		pr->override = FALSE;
184 		pr->value = ppr->value;
185 		g_hash_table_insert(stl->properties, pr->name, pr);
186 	}
187 }
188 
189 /* 1999-05-20 -	Update a single style, and then recurse to do its children. Heavily recursive. */
style_update(GNode * node,gpointer user)190 static void style_update(GNode *node, gpointer user)
191 {
192 	Style	*stl = node->data, *parent = user;
193 
194 	g_hash_table_foreach(parent->properties, property_update, stl);
195 	g_node_children_foreach(node, G_TRAVERSE_ALL, style_update, stl);
196 }
197 
198 /* 1999-05-19 -	Update style tree in <si>; causes all non-overridden properties in all styles to
199 **		acquire the value of the nearest overriding parent. Simple.
200 */
styleinfo_update(StyleInfo * si)201 static void styleinfo_update(StyleInfo *si)
202 {
203 	if(si != NULL)
204 	{
205 		if(si->freeze_count == 0)
206 		{
207 			g_node_children_foreach(si->styles, G_TRAVERSE_ALL, style_update, si->styles->data);
208 			si->update_pending = FALSE;
209 		}
210 		else
211 			si->update_pending = TRUE;
212 	}
213 }
214 
215 /* ----------------------------------------------------------------------------------------- */
216 
217 /* 1999-05-19 -	Freeze given styleinfo. When a styleinfo is frozen, you can add, alter, and
218 **		remove styles without causing the entire inheritance tree to be traversed
219 **		on each operation. Handy when doing loads of such operations.
220 */
stl_styleinfo_freeze(StyleInfo * si)221 void stl_styleinfo_freeze(StyleInfo *si)
222 {
223 	if(si != NULL)
224 		si->freeze_count++;
225 }
226 
227 /* 1999-05-19 -	Thaw a styleinfo. If anything was changed during freeze, update tree now. */
stl_styleinfo_thaw(StyleInfo * si)228 void stl_styleinfo_thaw(StyleInfo *si)
229 {
230 	if(si != NULL)
231 	{
232 		if(si->freeze_count == 0)
233 			fprintf(stderr, "STYLES: Mismatched call to stl_styleinfo_thaw() detected!\n");
234 		else if(--si->freeze_count == 0)
235 		{
236 			if(si->update_pending)
237 				styleinfo_update(si);
238 		}
239 	}
240 }
241 
242 /* ----------------------------------------------------------------------------------------- */
243 
244 #if 0
245 /* 1999-05-20 -	A g_hash_table_foreach() callback. */
246 static void dump_property(gpointer key, gpointer value, gpointer user)
247 {
248 	StlProp	*pr = value;
249 	guint	i, d = *(guint *) user;
250 	gchar	*tname[] = { "Color", "Icon", "Action" };
251 
252 	for(i = 0; i < d; i++)
253 		putchar(' ');
254 	printf("(%c) %s [%s: ", pr->override ? 'X' : ' ', pr->name, tname[pr->value->type]);
255 
256 	switch(pr->value->type)
257 	{
258 		case SPT_COLOR:
259 			printf("%04X,%04X,%04X", pr->value->data.color.red, pr->value->data.color.green, pr->value->data.color.blue);
260 			break;
261 		case SPT_ICON:
262 			printf("\"%s\"", pr->value->data.icon);
263 			break;
264 		case SPT_ACTION:
265 			printf("'%s'", pr->value->data.action->str);
266 			break;
267 	}
268 	printf("]\n");
269 }
270 
271 /* 1999-05-20 -	A g_node_traverse() callback. */
272 static gboolean dump_style(GNode *node, gpointer user)
273 {
274 	guint	i, d = g_node_depth(node);
275 
276 	for(i = 0; i < d - 1; i++)
277 		putchar('-');
278 	printf(" '%s'\n", ((Style *) node->data)->name);
279 	g_hash_table_foreach(((Style *) node->data)->properties, dump_property, &d);
280 	return FALSE;
281 }
282 
283 /* 1999-05-20 -	Dump the contents of given styleinfo's style tree. Mainly useful during
284 **		development and debugging of this module. Should not be included in release
285 **		builds of gentoo, since it's not really useful there.
286 */
287 void stl_styleinfo_dump(StyleInfo *si)
288 {
289 	guint	level;
290 
291 	printf("There are %u styles in given styleinfo:\n", g_node_n_nodes(si->styles, G_TRAVERSE_ALL));
292 	g_node_traverse(si->styles, G_PRE_ORDER, G_TRAVERSE_ALL, -1, dump_style, &level);
293 }
294 #endif
295 
296 /* ----------------------------------------------------------------------------------------- */
297 
298 /* 1999-05-19 -	Get the GNode that holds the given <stl>. */
styleinfo_style_get_node(const StyleInfo * si,const Style * stl)299 static GNode * styleinfo_style_get_node(const StyleInfo *si, const Style *stl)
300 {
301 	return g_node_find(si->styles, G_PRE_ORDER, G_TRAVERSE_ALL, (gpointer) stl);
302 }
303 
304 /* 1999-05-20 -	Find the sibling of <parent> that should come next after <node>,
305 **		if it were inserted. The implied ordering is simply alphabetical.
306 **		If there is no suitable sibling (<=> stl goes last), return NULL.
307 */
find_next_sibling(GNode * parent,GNode * node)308 static GNode * find_next_sibling(GNode *parent, GNode *node)
309 {
310 	guint	i;
311 	GNode	*here;
312 
313 	for(i = 0; (here = g_node_nth_child(parent, i)) != NULL; i++)
314 	{
315 		if(strcmp(((Style *) here->data)->name, ((Style *) node->data)->name) > 0)
316 			return here;
317 	}
318 	return NULL;
319 }
320 
321 /* 1999-05-19 -	Add a style to <si>, linking it in so <parent> is its immediate parent.
322 **		Call with <parent> == NULL to set the root style (moving the existing
323 **		styles, if any, down one level).
324 */
stl_styleinfo_style_add(StyleInfo * si,Style * parent,Style * stl)325 void stl_styleinfo_style_add(StyleInfo *si, Style *parent, Style *stl)
326 {
327 	if((si != NULL) && (stl != NULL))
328 	{
329 		GNode	*node, *pnode;
330 
331 		stl->styleinfo = si;
332 		node = g_node_new(stl);
333 
334 		if(parent == NULL)		/* Insert as root? */
335 		{
336 			if(si->styles != NULL)	/* Is there a parent to replace? */
337 				g_node_insert(node, -1, si->styles);
338 			si->styles = node;
339 		}
340 		else
341 		{
342 			if((pnode = styleinfo_style_get_node(si, parent)) != NULL)
343 				g_node_insert_before(pnode, find_next_sibling(pnode, node), node);
344 			else
345 				fprintf(stderr, "STYLES: Unknown parent style '%s' referenced\n", parent->name);
346 		}
347 		styleinfo_update(si);
348 	}
349 }
350 
351 /* 1999-05-20 -	A g_node_traverse() callback, to free the memory used by a style. */
remove_style(GNode * node,gpointer user)352 static gboolean remove_style(GNode *node, gpointer user)
353 {
354 	if(node != user)	/* Avoid recursing on root. */
355 		stl_style_destroy(node->data, FALSE);
356 	return FALSE;		/* Keep traversing. */
357 }
358 
359 /* 1999-05-20 -	Remove a style from the styleinfo tree. Causes all child styles to be
360 **		removed and destroyed, as well.
361 */
stl_styleinfo_style_remove(StyleInfo * si,Style * stl)362 void stl_styleinfo_style_remove(StyleInfo *si, Style *stl)
363 {
364 	if((si != NULL) && (stl != NULL))
365 	{
366 		GNode	*node;
367 
368 		if((node = styleinfo_style_get_node(si, stl)) != NULL)
369 		{
370 			g_node_unlink(node);
371 			g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1, remove_style, node);
372 			g_node_destroy(node);
373 			styleinfo_update(si);
374 		}
375 	}
376 }
377 
378 /* 1999-05-20 -	Check for a named node. */
find_callback(GNode * node,gpointer user)379 static gboolean find_callback(GNode *node, gpointer user)
380 {
381 	struct style_find_data	*sfd = user;
382 
383 	if(strcmp(((Style *) node->data)->name, sfd->name) == 0)
384 	{
385 		sfd->stl = node->data;
386 		return TRUE;	/* Wanted node has been found, so stop. */
387 	}
388 	return FALSE;		/* Keep traversing. */
389 }
390 
391 /* 1999-05-20 -	Find the named style in given styleinfo's style tree. NULL can be used as
392 **		a shorthand for the name of the root style.
393 */
stl_styleinfo_style_find(const StyleInfo * si,const gchar * name)394 Style * stl_styleinfo_style_find(const StyleInfo *si, const gchar *name)
395 {
396 	struct style_find_data	sfd;
397 
398 	sfd.name = name;
399 	sfd.stl  = NULL;
400 	if(si != NULL)
401 	{
402 		if(name != NULL)
403 			g_node_traverse(si->styles, G_PRE_ORDER, G_TRAVERSE_ALL, -1, find_callback, &sfd);
404 		else
405 			return stl_styleinfo_style_root(si);
406 	}
407 	return sfd.stl;
408 }
409 
410 /* 1999-05-24 -	Change the parent for <stl>. <new_parent> should really NOT be a child of
411 **		<stl>, or there will definitely be evil. Note that this operation can NOT
412 **		be done as a remove()/add() combo, since the remove is destructive.
413 */
stl_styleinfo_style_set_parent(StyleInfo * si,Style * stl,Style * new_parent)414 void stl_styleinfo_style_set_parent(StyleInfo *si, Style *stl, Style *new_parent)
415 {
416 	if((si != NULL) && (stl != NULL))
417 	{
418 		GNode	*node, *pnode;
419 
420 		node = styleinfo_style_get_node(si, stl);
421 		g_node_unlink(node);
422 		pnode = styleinfo_style_get_node(si, new_parent);
423 		g_node_insert_before(pnode, find_next_sibling(pnode, node), node);
424 		styleinfo_update(si);
425 	}
426 }
427 
428 /* 1999-05-24 -	Get the parent style of <stl>. If there is no parent (<stl> is root, or
429 **		perhaps has not yet been added to a styleinfo), NULL is returned.
430 */
stl_styleinfo_style_get_parent(StyleInfo * si,Style * stl)431 Style * stl_styleinfo_style_get_parent(StyleInfo *si, Style *stl)
432 {
433 	if((stl != NULL) && (stl->styleinfo == si))
434 	{
435 		GNode	*node;
436 
437 		node = styleinfo_style_get_node(stl->styleinfo, stl);
438 		if(node->parent != NULL)
439 			return node->parent->data;
440 	}
441 	return NULL;
442 }
443 
444 /* 1999-06-12 -	Return TRUE if <stla> and <stlb> are siblings (i.e., they have the same
445 **		immediate parent), FALSE otherwise.
446 */
stl_styleinfo_style_siblings(StyleInfo * si,Style * stla,Style * stlb)447 gboolean stl_styleinfo_style_siblings(StyleInfo *si, Style *stla, Style *stlb)
448 {
449 	if((si != NULL) && (stla != NULL) && (stlb != NULL))
450 	{
451 		GNode	*na, *nb;
452 
453 		na = styleinfo_style_get_node(si, stla);
454 		nb = styleinfo_style_get_node(si, stlb);
455 
456 		return na->parent == nb->parent;
457 	}
458 	return FALSE;
459 }
460 
461 /* 1999-05-26 -	Return the root style of <si>. This is actually the only reliable way of getting
462 **		at it, since you don't know its name.
463 */
stl_styleinfo_style_root(const StyleInfo * si)464 Style * stl_styleinfo_style_root(const StyleInfo *si)
465 {
466 	if(si != NULL)
467 	{
468 		if(si->styles != NULL)
469 			return si->styles->data;
470 	}
471 	return NULL;
472 }
473 
474 /* 1999-05-27 -	Return TRUE if <stl> has one or more children, otherwise FALSE. */
stl_styleinfo_style_has_children(StyleInfo * si,Style * stl)475 gboolean stl_styleinfo_style_has_children(StyleInfo *si, Style *stl)
476 {
477 	if((si != NULL) && (stl != NULL))
478 	{
479 		GNode	*node;
480 
481 		if((node = styleinfo_style_get_node(si, stl)) != NULL)
482 			return g_node_n_children(node) > 0 ? TRUE : FALSE;
483 	}
484 	return FALSE;
485 }
486 
487 /* 1999-05-29 -	Just a g_node_traverse() callback to build a list of child styles. */
get_child(GNode * node,gpointer user)488 static gboolean get_child(GNode *node, gpointer user)
489 {
490 	GList	**list = user;
491 
492 	*list = g_list_append(*list, node->data);
493 
494 	return FALSE;	/* And keep traversing. */
495 }
496 
497 /* 1999-05-29 -	Return a list of all child styles of <stl>. If <include_root> is TRUE, then
498 **		the root style <stl> will be included in the list. If FALSE, it will not.
499 **		Don't forget to g_list_free() the returned list when you're done with it.
500 */
stl_styleinfo_style_get_children(StyleInfo * si,Style * stl,gboolean include_root)501 GList * stl_styleinfo_style_get_children(StyleInfo *si, Style *stl, gboolean include_root)
502 {
503 	if((si != NULL) && (stl != NULL) && (stl->styleinfo == si))
504 	{
505 		GNode	*node;
506 
507 		if((node = styleinfo_style_get_node(si, stl)) != NULL)
508 		{
509 			GList	*list = NULL;
510 
511 			g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, get_child, &list);
512 			if(!include_root)
513 			{
514 				GList	*first = list;
515 
516 				list = g_list_remove_link(list, list);	/* Remove head of child list. */
517 				g_list_free(first);
518 			}
519 			return list;
520 		}
521 	}
522 	return NULL;
523 }
524 
525 /* ----------------------------------------------------------------------------------------- */
526 
527 /* 1999-05-27 -	A g_hash_table_foreach() callback that writes out a property definition. */
save_property(gpointer key,gpointer value,gpointer user)528 static void save_property(gpointer key, gpointer value, gpointer user)
529 {
530 	StlProp	*pr = value;
531 	FILE	*out = user;
532 
533 	/* We don't save non-overridden properties, of course. */
534 	if(!pr->override)
535 		return;
536 
537 	xml_put_node_open(out, "Property");
538 	xml_put_text(out, "name", pr->name);
539 	switch(pr->value->type)
540 	{
541 		case SPT_COLOR:
542 			xml_put_color(out, "color", &pr->value->data.color);
543 			break;
544 		case SPT_ICON:
545 			xml_put_text(out, "icon", pr->value->data.icon);
546 			break;
547 		case SPT_ACTION:
548 			xml_put_text(out, "action", pr->value->data.action->str);
549 			break;
550 	}
551 	xml_put_node_close(out, "Property");
552 }
553 
554 /* 1999-05-27 -	A g_node_traverse() function to save out a single style. */
save_style(GNode * node,gpointer user)555 static gboolean save_style(GNode *node, gpointer user)
556 {
557 	Style	*stl = node->data;
558 	FILE	*out = user;
559 
560 	xml_put_node_open(out, "Style");
561 	xml_put_text(out, "name", stl->name);
562 	if(node->parent != NULL)
563 		xml_put_text(out, "parent", ((Style *) node->parent->data)->name);
564 	xml_put_boolean(out, "expand", stl->expand);
565 	if(stl_style_property_overrides(stl))
566 	{
567 		xml_put_node_open(out, "Properties");
568 		g_hash_table_foreach(stl->properties, save_property, out);
569 		xml_put_node_close(out, "Properties");
570 	}
571 	xml_put_node_close(out, "Style");
572 
573 	return FALSE;		/* Keep traversing, we relly want to save all styles. */
574 }
575 
576 /* 1999-05-27 -	Save out contents of <si> into <out>, nicely bracketed by <tag> tags. */
stl_styleinfo_save(MainInfo * min,StyleInfo * si,FILE * out,const gchar * tag)577 void stl_styleinfo_save(MainInfo *min, StyleInfo *si, FILE *out, const gchar *tag)
578 {
579 	if((min == NULL) || (si == NULL) || (out == NULL) || (tag == NULL))
580 		return;
581 
582 	xml_put_node_open(out, tag);
583 	g_node_traverse(si->styles, G_PRE_ORDER, G_TRAVERSE_ALL, -1, save_style, out);
584 	xml_put_node_close(out, tag);
585 }
586 
587 /* ----------------------------------------------------------------------------------------- */
588 
589 /* 1999-05-20 -	Load an old-styled property. Convert legacy properties, identified using
590 **		integers, into the newer named style. Add the property to the style pointed
591 **		to by <user>.
592 */
old_load_property(const XmlNode * node,gpointer user)593 static void old_load_property(const XmlNode *node, gpointer user)
594 {
595 	gint		type;
596 	gboolean	ok = FALSE;
597 	const gchar	*name = NULL, *text = NULL;
598 	GdkColor	col;
599 	StlPType	newtype = SPT_COLOR;
600 	Style		*stl = user;
601 
602 	xml_get_integer(node, "type", &type);
603 
604 	/* Map old integer-identified properties to new textually named ones. */
605 	switch(type)
606 	{
607 		case L_TYPE_UNSEL_BGCOL:
608 			name = SPN_COL_UNSEL_BG;
609 			if((ok = xml_get_color(node, "color", &col)))
610 				newtype = SPT_COLOR;
611 			break;
612 		case L_TYPE_UNSEL_FGCOL:
613 			name = SPN_COL_UNSEL_FG;
614 			if((ok = xml_get_color(node, "color", &col)))
615 				newtype = SPT_COLOR;
616 			break;
617 		case L_TYPE_UNSEL_ICON:
618 			name = SPN_ICON_UNSEL;
619 			if((ok = xml_get_text(node, "icon", &text)))
620 				newtype = SPT_ICON;
621 			break;
622 		case L_TYPE_ACTN_DEFAULT:
623 			name = SPN_ACTN_DEFAULT;
624 			if((ok = xml_get_text(node, "action", &text)))
625 				newtype = SPT_ACTION;
626 			break;
627 		case L_TYPE_ACTN_VIEW:
628 			name = SPN_ACTN_VIEW;
629 			if((ok = xml_get_text(node, "action", &text)))
630 				newtype = SPT_ACTION;
631 			break;
632 		case L_TYPE_ACTN_EDIT:
633 			name = SPN_ACTN_EDIT;
634 			if((ok = xml_get_text(node, "action", &text)))
635 				newtype = SPT_ACTION;
636 			break;
637 		case L_TYPE_ACTN_PRINT:
638 			name = SPN_ACTN_PRINT;
639 			if((ok = xml_get_text(node, "action", &text)))
640 				newtype = SPT_ACTION;
641 			break;
642 		case L_TYPE_ACTN_PLAY:
643 			name = SPN_ACTN_PLAY;
644 			if((ok = xml_get_text(node, "action", &text)))
645 				newtype = SPT_ACTION;
646 			break;
647 		default:
648 			g_warning("STYLES: Ignoring unknown legacy property type %d in style '%s'\n", type, stl->name);
649 	}
650 	if(ok)
651 	{
652 		switch(newtype)
653 		{
654 			case SPT_COLOR:
655 				stl_style_property_set_color(stl, name, &col);
656 				break;
657 			case SPT_ICON:
658 				stl_style_property_set_icon(stl, name, text);
659 				break;
660 			case SPT_ACTION:
661 				stl_style_property_set_action(stl, name, csq_cmdseq_map_name(text, stl->name));
662 				break;
663 		}
664 	}
665 }
666 
667 /* 1999-05-27 -	Load modern-style property (has textual identifier). Pretty simple stuff. */
load_property(const XmlNode * data,gpointer user)668 static void load_property(const XmlNode *data, gpointer user)
669 {
670 	Style		*stl = user;
671 	const gchar	*name;
672 
673 	if(xml_get_text(data, "name", &name))
674 	{
675 		GdkColor	color;
676 		const gchar	*text;
677 
678 		if(xml_get_color(data, "color", &color))
679 			stl_style_property_set_color(stl, name, &color);
680 		else if(xml_get_text(data, "icon", &text))
681 			stl_style_property_set_icon(stl, name, text);
682 		else if(xml_get_text(data, "action", &text))
683 			stl_style_property_set_action(stl, name, csq_cmdseq_map_name(text, stl_style_get_name(stl)));
684 		else
685 			g_warning("STYLES: Unknown value type for property '%s' (style '%s')\n", name, stl->name);
686 	}
687 }
688 
689 /* 1999-05-20 -	Another xml_node_visit_children() callback. Load in all properties for
690 **		given style, and store them in the style. This is where the backwards-
691 **		compatibility really gets going.
692 */
load_properties(const XmlNode * node,Style * stl)693 static void load_properties(const XmlNode *node, Style *stl)
694 {
695 	const XmlNode	*data;
696 
697 	if((data = xml_tree_search(node, "Property")) != NULL)
698 	{
699 		if(xml_get_integer(data, "type", NULL))
700 			xml_node_visit_children(node, old_load_property, stl);
701 		else
702 			xml_node_visit_children(node, load_property, stl);
703 	}
704 }
705 
706 /* 1999-05-20 -	A xml_node_visit_children() callback to load in a style. Deals with the
707 **		annoying complexities of backwards compatibility. :(
708 */
load_style(const XmlNode * node,gpointer user)709 static void load_style(const XmlNode *node, gpointer user)
710 {
711 	StyleInfo	*si = user;
712 	Style		*stl, *parent = NULL;
713 	const gchar	*name = "new style", *pname;
714 	const XmlNode	*prop;
715 	gboolean	btmp = FALSE;
716 
717 	xml_get_text(node, "name", &name);
718 	stl = stl_style_new(name);
719 
720 	if(xml_get_boolean(node, "expand", &btmp))
721 		stl_style_set_expand(stl, btmp);
722 	if((prop = xml_tree_search(node, "Properties")) != NULL)
723 		load_properties(prop, stl);
724 	if(xml_get_text(node, "parent", &pname))
725 		parent = stl_styleinfo_style_find(si, pname);
726 	stl_styleinfo_style_add(si, parent, stl);
727 }
728 
729 /* 1999-05-20 -	Load an entire styleinfo tree from XML data rooted at <node>. Note that the
730 **		top-level tag identifier (the one passed to stl_styleinfo_save()) should already
731 **		have been used before calling this function, to find <node>. It is assumed
732 **		that the given node contains a bunch of children, each of which defines a style.
733 */
stl_styleinfo_load(const XmlNode * node)734 StyleInfo * stl_styleinfo_load(const XmlNode *node)
735 {
736 	StyleInfo	*si;
737 
738 	si = stl_styleinfo_new();
739 	stl_styleinfo_freeze(si);
740 	xml_node_visit_children(node, load_style, si);
741 	stl_styleinfo_thaw(si);
742 
743 	return si;
744 }
745 
746 /* ----------------------------------------------------------------------------------------- */
747 
748 /* 1999-05-20 -	Destroy given styleinfo, freeing all memory used by it. */
stl_styleinfo_destroy(StyleInfo * si)749 void stl_styleinfo_destroy(StyleInfo *si)
750 {
751 	if(si != NULL)
752 	{
753 		stl_styleinfo_freeze(si);	/* There's no point in updating inherited stuff while destroying. */
754 		if(si->styles != NULL)
755 			stl_style_destroy(si->styles->data, TRUE);
756 		g_free(si);
757 	}
758 }
759 
760 /* ----------------------------------------------------------------------------------------- */
761 
762 /* 2006-03-02 -	Append a branch of the style tree to the given store. This does not collapse/expand it
763  *		properly, since that's not doable with GTK+2 at this level (it's a view property).
764 */
append_branch(GtkTreeStore * store,const GNode * node,GtkTreeIter * parent,const Style * ignore,gboolean * modify_flag)765 static void append_branch(GtkTreeStore *store, const GNode *node, GtkTreeIter *parent, const Style *ignore, gboolean *modify_flag)
766 {
767 	GtkTreeIter	child;
768 
769 	for(; (node != NULL); node = node->next)
770 	{
771 		if((ignore != NULL) && (node->data == ignore))
772 			continue;
773 		gtk_tree_store_append(store, &child, parent);
774 		gtk_tree_store_set(store, &child, TREE_COLUMN_NAME, ((const Style *) node->data)->name,
775 						  TREE_COLUMN_STYLE, node->data,
776 						  -1);
777 		if(node->children != NULL)
778 			append_branch(store, node->children, &child, ignore, modify_flag);
779 	}
780 }
781 
782 /* 2006-03-02 -	Build a partial style store, ignoring any branch rooted at <ignore>. */
stl_styleinfo_build_partial(const StyleInfo * si,const Style * ignore)783 GtkTreeStore * stl_styleinfo_build_partial(const StyleInfo *si, const Style *ignore)
784 {
785 	GtkTreeStore	*store;
786 
787 	store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
788 	append_branch(store, si->styles, NULL, ignore, NULL);
789 
790 	return store;
791 }
792 
793 /* ----------------------------------------------------------------------------------------- */
794 
795 /* 2009-03-22 -	We need a way to read out a Style pointer from a constructed tree, in the config. */
stl_styleinfo_get_style_iter(const StyleInfo * si,GtkTreeStore * store,GtkTreeIter * iter)796 Style * stl_styleinfo_get_style_iter(const StyleInfo *si, GtkTreeStore *store, GtkTreeIter *iter)
797 {
798 	Style	*style = NULL;
799 
800 	if(si == NULL || store == NULL || iter == NULL)
801 		return NULL;
802 	gtk_tree_model_get(GTK_TREE_MODEL(store), iter, TREE_COLUMN_STYLE, &style, -1);
803 
804 	return style;
805 }
806 
807 /* Simple bookkeeping struct used by stl_styleinfo_tree_find_style(), below. */
808 struct find_info
809 {
810 	const Style	*style;
811 	gboolean	found;
812 	GtkTreeIter	iter;
813 };
814 
cb_foreach(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)815 static gboolean cb_foreach(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
816 {
817 	struct find_info	*fi = data;
818 	Style		*here;
819 
820 	gtk_tree_model_get(model, iter, TREE_COLUMN_STYLE, &here, -1);
821 	if(here == fi->style)
822 	{
823 		fi->iter = *iter;
824 		fi->found = TRUE;
825 	}
826 	return fi->found;
827 }
828 
829 /* 2009-03-24 -	Find the given style in the given store, and set the iter accordingly.
830 **		This is a straight tree search, so it takes a while, but this really
831 **		shouldn't be a problem.
832 */
stl_styleinfo_tree_find_style(const StyleInfo * si,GtkTreeStore * store,const Style * style,GtkTreeIter * iter)833 gboolean stl_styleinfo_tree_find_style(const StyleInfo *si, GtkTreeStore *store, const Style *style, GtkTreeIter *iter)
834 {
835 	struct find_info	fi;
836 
837 	fi.style = style;
838 	fi.found = FALSE;
839 	gtk_tree_model_foreach(GTK_TREE_MODEL(store), cb_foreach, &fi);
840 	if(fi.found)
841 	{
842 		if(iter != NULL)
843 			*iter = fi.iter;
844 	}
845 	return fi.found;
846 }
847 
848 /* 2008-05-11 -	Set the name of a style, based on a GtkTreeIter. This will change not only
849  *		the value in the tree model, but also the style itself.
850 */
stl_styleinfo_set_name_iter(const StyleInfo * si,GtkTreeStore * store,GtkTreeIter * iter,const gchar * name)851 void stl_styleinfo_set_name_iter(const StyleInfo *si, GtkTreeStore *store, GtkTreeIter *iter, const gchar *name)
852 {
853 	Style	*style;
854 
855 	if((style = stl_styleinfo_get_style_iter(si, store, iter)) == NULL)
856 		return;
857 	stl_style_set_name(style, name);
858 	gtk_tree_store_set(store, iter, TREE_COLUMN_NAME, name, -1);
859 }
860 
861 /* ----------------------------------------------------------------------------------------- */
862 
863 /* 1999-05-19 -	Create a new, empty style named <name>. */
stl_style_new(const gchar * name)864 Style * stl_style_new(const gchar *name)
865 {
866 	Style	*stl;
867 
868 	stl = g_malloc(sizeof *stl);
869 	g_strlcpy(stl->name, name, sizeof stl->name);
870 	stl->styleinfo = NULL;
871 	stl->properties = g_hash_table_new(g_str_hash, g_str_equal);
872 	stl->expand = TRUE;
873 
874 	return stl;
875 }
876 
877 /* 1999-05-26 -	Create a new style, taking care to choose a name not used in <si>.
878 ** BUG BUG BUG	If you have 9999 styles called "New Style N", this might fail... Big "oops".
879 */
stl_style_new_unique_name(StyleInfo * si)880 Style * stl_style_new_unique_name(StyleInfo *si)
881 {
882 	if(si != NULL)
883 	{
884 		gchar	buf[STL_STYLE_NAME_SIZE];
885 
886 		g_strlcpy(buf, ("New Style"), sizeof buf);
887 		if(stl_styleinfo_style_find(si, buf) != NULL)
888 		{
889 			guint	i = 1;
890 
891 			do
892 			{
893 				g_snprintf(buf, sizeof buf, _("New Style %u"), i + 1);
894 				i++;
895 			} while((stl_styleinfo_style_find(si, buf) != NULL) && (i < 10000U));	/* This might fail. Big deal. */
896 		}
897 		return stl_style_new(buf);
898 	}
899 	return NULL;
900 }
901 
902 /* 1999-05-20 -	Copy a property, storing the copy in the style pointed to by <user>. */
property_copy(gpointer key,gpointer value,gpointer user)903 static void property_copy(gpointer key, gpointer value, gpointer user)
904 {
905 	StlProp	*pr = value;
906 	Style	*stl = user;
907 
908 	if(pr->override)
909 	{
910 		switch(pr->value->type)
911 		{
912 			case SPT_COLOR:
913 				stl_style_property_set_color(stl, pr->name, &pr->value->data.color);
914 				break;
915 			case SPT_ICON:
916 				stl_style_property_set_icon(stl, pr->name, pr->value->data.icon);
917 				break;
918 			case SPT_ACTION:
919 				stl_style_property_set_action(stl, pr->name, pr->value->data.action->str);
920 				break;
921 		}
922 	}
923 }
924 
925 /* 1999-05-19 -	Copy a style, creating a non-memory-sharing duplicate. Note that we only
926 **		copy overriden properties, relying on the fact that if the copy is going
927 **		to be used (i.e., added to a styleinfo), its inheritance will be computed.
928 */
stl_style_copy(Style * stl)929 Style * stl_style_copy(Style *stl)
930 {
931 	Style	*ns = NULL;
932 
933 	if(stl != NULL)
934 	{
935 		ns = stl_style_new(stl->name);
936 		ns->expand = stl->expand;
937 		g_hash_table_foreach(stl->properties, property_copy, ns);
938 	}
939 	return ns;
940 }
941 
942 /* 1999-05-24 -	Return style from a tree selection (<wid> should be the widget passed to
943 **		the select handler used by stl_styleinfo_build().
944 */
stl_style_get(GtkWidget * wid)945 Style * stl_style_get(GtkWidget *wid)
946 {
947 	if(wid != NULL)
948 		return g_object_get_data(G_OBJECT(wid), "user");
949 	return NULL;
950 }
951 
952 /* 1999-05-22 -	Set the expand status of a style. Used when building GUI representation. */
stl_style_set_expand(Style * stl,gboolean expand)953 void stl_style_set_expand(Style *stl, gboolean expand)
954 {
955 	if(stl != NULL)
956 		stl->expand = expand;
957 }
958 
959 /* 1999-05-22 -	Get the current expand/collapse status of given style. */
stl_style_get_expand(Style * stl)960 gboolean stl_style_get_expand(Style *stl)
961 {
962 	if(stl != NULL)
963 		return stl->expand;
964 	return FALSE;
965 }
966 
967 /* 1999-05-24 -	Rename a style. */
stl_style_set_name(Style * stl,const gchar * name)968 void stl_style_set_name(Style *stl, const gchar *name)
969 {
970 	if((stl != NULL) && (name != NULL))
971 		g_strlcpy(stl->name, name, sizeof stl->name);
972 }
973 
974 /* 1999-05-20 -	Just get a pointer (read-only, as usual) to the style's name. */
stl_style_get_name(Style * stl)975 const gchar * stl_style_get_name(Style *stl)
976 {
977 	if(stl != NULL)
978 		return stl->name;
979 	return NULL;
980 }
981 
982 /* 1999-06-12 -	Get the branch that begins with the root style and ends with <stl>. */
style_get_branch(const Style * stl)983 static GSList * style_get_branch(const Style *stl)
984 {
985 	GNode	*node;
986 
987 	if((node = styleinfo_style_get_node(stl->styleinfo, stl)) != NULL)
988 	{
989 		GSList	*l = NULL;
990 
991 		for(; node != NULL; node = node->parent)
992 			l = g_slist_prepend(l, ((Style *) node->data)->name);
993 		return l;
994 	}
995 	return NULL;
996 }
997 
998 /* 1999-05-20 -	Compare hierarchial names of <stla> and <stlb>. Used by dirpane module,
999 **		to sort on styles. Comparison is done so that styles with common parents
1000 **		are grouped.
1001 */
stl_style_compare_hierarchy(const Style * stla,const Style * stlb)1002 gint stl_style_compare_hierarchy(const Style *stla, const Style *stlb)
1003 {
1004 	GSList	*la, *lb;
1005 
1006 	if(stla == stlb)		/* Quickly determine if they're the same. */
1007 		return 0;
1008 
1009 	if((la = style_get_branch(stla)) != NULL)
1010 	{
1011 		gint	sd = -1;
1012 		if((lb = style_get_branch(stlb)) != NULL)
1013 		{
1014 			GSList	*ia, *ib;
1015 
1016 			for(ia = la, ib = lb; (ia != NULL) && (ib != NULL) && ((sd = strcmp(ia->data, ib->data)) == 0);)
1017 			{
1018 				ia = g_slist_next(ia);
1019 				ib = g_slist_next(ib);
1020 			}
1021 			if((ia == NULL) || (ib == NULL))
1022 				sd = (ia == NULL) ? -1 : 1;
1023 			g_slist_free(lb);
1024 		}
1025 		g_slist_free(la);
1026 
1027 		return sd;
1028 	}
1029 	return 0;
1030 }
1031 
1032 /* ----------------------------------------------------------------------------------------- */
1033 
1034 /* 1999-05-19 -	Create a new value structure, init it as the given type, and return a pointer to it. */
value_new(StlPType type)1035 static StlPVal * value_new(StlPType type)
1036 {
1037 	StlPVal	*val;
1038 
1039 	val = g_malloc(sizeof *val);
1040 	val->type = type;
1041 	switch(type)
1042 	{
1043 		case SPT_COLOR:
1044 			val->data.color.red   = 0U;
1045 			val->data.color.green = 0U;
1046 			val->data.color.blue  = 0U;
1047 			val->data.color.pixel = 0UL;
1048 			break;
1049 		case SPT_ICON:
1050 			val->data.icon[0] = '\0';
1051 			break;
1052 		case SPT_ACTION:
1053 			val->data.action = g_string_new("");
1054 			break;
1055 	}
1056 	return val;
1057 }
1058 
1059 /* 1999-05-19 -	Destroy a value. */
value_destroy(StlPVal * val)1060 static void value_destroy(StlPVal *val)
1061 {
1062 	if(val != NULL)
1063 	{
1064 		switch(val->type)
1065 		{
1066 			case SPT_COLOR:
1067 			case SPT_ICON:
1068 				break;
1069 			case SPT_ACTION:
1070 				g_string_free(val->data.action, TRUE);
1071 				break;
1072 		}
1073 		g_free(val);
1074 	}
1075 }
1076 
1077 /* 1999-05-20 -	Get a pointer to an overriding property named <name> in <stl>, whose type
1078 **		is <type>. If no such property exists, create it.
1079 */
style_get_property(Style * stl,const gchar * name,StlPType type)1080 static StlProp * style_get_property(Style *stl, const gchar *name, StlPType type)
1081 {
1082 	StlProp	*pr;
1083 
1084 	if((pr = g_hash_table_lookup(stl->properties, name)) != NULL)
1085 	{
1086 		if(pr->override)
1087 		{
1088 			if(pr->value->type == type)
1089 				return pr;
1090 			/* Only destroy if overriden, else we shared pointer. */
1091 			value_destroy(pr->value);
1092 		}
1093 	}
1094 	else
1095 	{
1096 		pr = g_malloc(sizeof *pr);
1097 		g_strlcpy(pr->name, name, sizeof pr->name);
1098 		g_hash_table_insert(stl->properties, pr->name, pr);
1099 	}
1100 	pr->override = TRUE;
1101 	pr->value    = value_new(type);
1102 
1103 	return pr;
1104 }
1105 
1106 /* 1999-05-19 -	Destroy a property, freeing all memory occupied by it. When this is called,
1107 **		the property better NOT be still held in a style's hash table!
1108 */
style_property_destroy(StlProp * pr)1109 static void style_property_destroy(StlProp *pr)
1110 {
1111 	if(pr != NULL)
1112 	{
1113 		if(pr->override)
1114 			value_destroy(pr->value);
1115 		g_free(pr);
1116 	}
1117 }
1118 
1119 /* 1999-05-19 -	Set a new color for a color property. */
stl_style_property_set_color(Style * stl,const gchar * property,const GdkColor * value)1120 void stl_style_property_set_color(Style *stl, const gchar *property, const GdkColor *value)
1121 {
1122 	StlProp	*pr;
1123 
1124 	if((stl != NULL) && (value != NULL))
1125 	{
1126 		pr = style_get_property(stl, property, SPT_COLOR);
1127 		pr->value->data.color = *value;
1128 		styleinfo_update(stl->styleinfo);
1129 	}
1130 }
1131 
1132 /* 1999-05-19 -	Set a color property, using an RGB triplet. Sometimes convenient. */
stl_style_property_set_color_rgb(Style * stl,const gchar * property,guint16 red,guint16 green,guint16 blue)1133 void stl_style_property_set_color_rgb(Style *stl, const gchar *property, guint16 red, guint16 green, guint16 blue)
1134 {
1135 	GdkColor	color;
1136 
1137 	color.red = red;
1138 	color.green = green;
1139 	color.blue = blue;
1140 	color.pixel = 0UL;
1141 	stl_style_property_set_color(stl, property, &color);
1142 }
1143 
1144 /* 1999-05-19 -	Set the name of an icon-property. */
stl_style_property_set_icon(Style * stl,const gchar * property,const gchar * value)1145 void stl_style_property_set_icon(Style *stl, const gchar *property, const gchar *value)
1146 {
1147 	StlProp	*pr;
1148 
1149 	if((stl != NULL) && (value != NULL))
1150 	{
1151 		pr = style_get_property(stl, property, SPT_ICON);
1152 		g_strlcpy(pr->value->data.icon, value, sizeof pr->value->data.icon);
1153 		styleinfo_update(stl->styleinfo);
1154 	}
1155 }
1156 
1157 /* 1999-05-19 -	Set an action property to a new string. Note that empty strings are not allowed. */
stl_style_property_set_action(Style * stl,const gchar * property,const gchar * value)1158 void stl_style_property_set_action(Style *stl, const gchar *property, const gchar *value)
1159 {
1160 	StlProp	*pr;
1161 
1162 	if((stl != NULL) && (value != NULL))
1163 	{
1164 		pr = style_get_property(stl, property, SPT_ACTION);
1165 		g_string_assign(pr->value->data.action, value);
1166 		styleinfo_update(stl->styleinfo);
1167 	}
1168 }
1169 
1170 /* ----------------------------------------------------------------------------------------- */
1171 
1172 /* 1999-05-19 -	Get a read-only pointer to the color held in a color-property. */
stl_style_property_get_color(const Style * stl,const gchar * property)1173 const GdkColor * stl_style_property_get_color(const Style *stl, const gchar *property)
1174 {
1175 	StlProp	*pr;
1176 
1177 	if((stl != NULL) && (property != NULL) && ((pr = g_hash_table_lookup(stl->properties, property)) != NULL))
1178 	{
1179 		if(pr->value->type == SPT_COLOR)
1180 			return &pr->value->data.color;
1181 	}
1182 	return NULL;
1183 }
1184 
1185 /* 1999-05-19 -	Get a (read-only) icon name of an appropriately typed property. */
stl_style_property_get_icon(const Style * stl,const gchar * property)1186 const gchar * stl_style_property_get_icon(const Style *stl, const gchar *property)
1187 {
1188 	StlProp	*pr;
1189 
1190 	if((stl != NULL) && (property != NULL) && ((pr = g_hash_table_lookup(stl->properties, property)) != NULL))
1191 	{
1192 		if(pr->value->type == SPT_ICON)
1193 			return pr->value->data.icon;
1194 	}
1195 	return NULL;
1196 }
1197 
1198 /* 1999-05-19 -	Get a (read-only, dammit) pointer to an action property definition. */
stl_style_property_get_action(const Style * stl,const gchar * property)1199 const gchar * stl_style_property_get_action(const Style *stl, const gchar *property)
1200 {
1201 	StlProp	*pr;
1202 
1203 	if((stl != NULL) && (property != NULL) && ((pr = g_hash_table_lookup(stl->properties, property)) != NULL))
1204 	{
1205 		if(pr->value->type == SPT_ACTION)
1206 			return pr->value->data.action->str;
1207 	}
1208 	return NULL;
1209 }
1210 
1211 /* ----------------------------------------------------------------------------------------- */
1212 
1213 /* 1999-05-24 -	Get the override status (which tells if the property is local or inherited) for
1214 **		a named property.
1215 */
stl_style_property_get_override(const Style * stl,const gchar * property)1216 gboolean stl_style_property_get_override(const Style *stl, const gchar *property)
1217 {
1218 	StlProp	*pr;
1219 
1220 	if((stl != NULL) && (property != NULL) && ((pr = g_hash_table_lookup(stl->properties, property)) != NULL))
1221 		return pr->override;
1222 	return FALSE;
1223 }
1224 
1225 /* ----------------------------------------------------------------------------------------- */
1226 
get_action(gpointer key,gpointer value,gpointer user)1227 static void get_action(gpointer key, gpointer value, gpointer user)
1228 {
1229 	GList	**list = user;
1230 	StlProp	*prop = value;
1231 
1232 	if(prop->value->type == SPT_ACTION)
1233 		*list = g_list_insert_sorted(*list, prop->name, (GCompareFunc) strcmp);
1234 }
1235 
1236 /* 1999-05-24 -	Return a list of (very read-only) action property names. The list is valid
1237 **		until the next person sneezes, or someone calls property_set() or _remove().
1238 **		The list can be freed with a simple call to g_list_free().
1239 */
stl_style_property_get_actions(const Style * stl)1240 GList * stl_style_property_get_actions(const Style *stl)
1241 {
1242 	GList	*list = NULL;
1243 
1244 	if(stl != NULL)
1245 		g_hash_table_foreach(stl->properties, get_action, &list);
1246 	return list;
1247 }
1248 
stl_style_property_has_action(const Style * stl,const gchar * action)1249 gboolean stl_style_property_has_action(const Style *stl, const gchar *action)
1250 {
1251 	StlProp	*p;
1252 
1253 	if((p = g_hash_table_lookup(stl->properties, action)) != NULL)
1254 	{
1255 		if(p->value->type == SPT_ACTION)
1256 			return TRUE;
1257 	}
1258 	return FALSE;
1259 }
1260 
1261 /* ----------------------------------------------------------------------------------------- */
1262 
1263 /* 1999-05-27 -	Another g_hash_table_foreach() callback. This one checks for overrides. */
check_override(gpointer key,gpointer value,gpointer user)1264 static void check_override(gpointer key, gpointer value, gpointer user)
1265 {
1266 	gboolean	*overrides = user;
1267 
1268 	if(((StlProp *) value)->override)
1269 		*overrides = TRUE;
1270 }
1271 
1272 /* 1999-05-27 -	Return TRUE if the given style overrides any property, FALSE if all it does
1273 **		is inherit them. Handy when saving, to avoid empty "<Properties></Properties>"
1274 **		stuff.
1275 */
stl_style_property_overrides(Style * stl)1276 gboolean stl_style_property_overrides(Style *stl)
1277 {
1278 	gboolean	overrides = FALSE;
1279 
1280 	if(stl != NULL)
1281 		g_hash_table_foreach(stl->properties, check_override, &overrides);
1282 
1283 	return overrides;
1284 }
1285 
1286 /* 1999-05-25 -	Answer whether the named <property> is unique in the style tree. If a property
1287 **		is unique, deleting it will truly delete the property. If it's not, deleting
1288 **		it will just replace the value with an inherited value. This distinction is not
1289 **		very interesting in itself, but it helps in user interface design. :)
1290 */
stl_style_property_is_unique(Style * stl,const gchar * property)1291 gboolean stl_style_property_is_unique(Style *stl, const gchar *property)
1292 {
1293 	if((stl != NULL) && (property != NULL))
1294 	{
1295 		GNode	*node;
1296 
1297 		if((node = styleinfo_style_get_node(stl->styleinfo, stl)) != NULL)
1298 		{
1299 			for(node = node->parent; node != NULL; node = node->parent)
1300 			{
1301 				if(g_hash_table_lookup(((Style *) node->data)->properties, property))
1302 					return FALSE;
1303 			}
1304 			return TRUE;
1305 		}
1306 	}
1307 	return FALSE;
1308 }
1309 
1310 /* 2009-04-22 -	Major rewrite. This doesn't actually do a rename; it does a delete of all non-overriden
1311 **		properties of the correct name. These, by definition, have the same value as the property
1312 **		in the parent, so no user-owned information is lost.
1313 */
traverse_property_rename(GNode * node,gpointer user)1314 static void traverse_property_rename(GNode *node, gpointer user)
1315 {
1316 	struct property_rename_data	*data = user;
1317 	Style				*here = node->data;
1318 	const StlProp			*pr;
1319 
1320 	if((pr = g_hash_table_lookup(here->properties, data->name_old)) != NULL)
1321 	{
1322 		/* If this child overrides the property, abort. */
1323 		if(pr->override)
1324 			return;
1325 		stl_style_property_remove(here, data->name_old);
1326 		g_node_children_foreach(node, G_TRAVERSE_ALL, traverse_property_rename, data);
1327 	}
1328 }
1329 
1330 /* 1999-05-25 -	Rename <property> to <new_name>. If there is already a property with that name
1331 **		in the given <stl>, rename will fail and return FALSE. Else, TRUE is returned.
1332 ** 2004-02-24 -	This was broken. It must recurse all child Styles, and let the renaming take
1333 **		effect in (all) child styles, or there will be "orphan" properties left about.
1334 */
stl_style_property_rename(Style * stl,const gchar * property,const gchar * new_name)1335 gboolean stl_style_property_rename(Style *stl, const gchar *property, const gchar *new_name)
1336 {
1337 	StlProp	*pr;
1338 	GNode	*me;
1339 
1340 	if(stl == NULL || property == NULL || new_name == NULL)
1341 		return FALSE;
1342 	if(g_hash_table_lookup(stl->properties, new_name) != NULL)
1343 		return FALSE;
1344 	if((pr = g_hash_table_lookup(stl->properties, property)) == NULL)
1345 		return FALSE;
1346 	if(!pr->override)
1347 	{
1348 		fprintf(stderr, "**Styles: can't rename non-overriden property '%s'\n", property);
1349 		return FALSE;
1350 	}
1351 	/* Remove from hash table, rename, and re-insert. */
1352 	g_hash_table_remove(stl->properties, pr->name);
1353 	g_strlcpy(pr->name, new_name, sizeof pr->name);
1354 	g_hash_table_insert(stl->properties, pr->name, pr);
1355 	/* Name changed. Now traverse children, and remove non-overridden instances. */
1356 	if((me = g_node_find(stl->styleinfo->styles, G_PRE_ORDER, G_TRAVERSE_ALL, stl)) != NULL)
1357 	{
1358 		struct property_rename_data	data;
1359 
1360 		data.style = stl;
1361 		g_snprintf(data.name_old, sizeof data.name_old, "%s", property);
1362 		g_snprintf(data.name_new, sizeof data.name_new, "%s", new_name);
1363 		g_node_children_foreach(me, G_TRAVERSE_ALL, traverse_property_rename, &data);
1364 	}
1365 	/* Finally re-inherit stuff, if our override was of a parent's property we will now get it "back". */
1366 	styleinfo_update(stl->styleinfo);
1367 
1368 	return TRUE;
1369 }
1370 
1371 /* 1999-05-19 -	Remove (and destroy) a property from a style. */
stl_style_property_remove(Style * stl,const gchar * property)1372 void stl_style_property_remove(Style *stl, const gchar *property)
1373 {
1374 	StlProp	*pr;
1375 
1376 	if((stl != NULL) && ((pr = g_hash_table_lookup(stl->properties, property)) != NULL))
1377 	{
1378 		g_hash_table_remove(stl->properties, property);
1379 		style_property_destroy(pr);
1380 		styleinfo_update(stl->styleinfo);
1381 	}
1382 }
1383 
1384 /* ----------------------------------------------------------------------------------------- */
1385 
1386 /* 1999-05-20 -	A g_hash_table_foreach_remove() callback. Kill a property. */
destroy_property(gpointer key,gpointer value,gpointer user)1387 static gboolean destroy_property(gpointer key, gpointer value, gpointer user)
1388 {
1389 	style_property_destroy(value);
1390 
1391 	return TRUE;		/* Really remove. */
1392 }
1393 
1394 /* 1999-05-20 -	Destroy a style. If <unlink> is TRUE, it will also remove the style from
1395 **		the styleinfo, thus causing any children to <stl> to be destroyed as well.
1396 **		This is really recommended, as leaving a styleinfo referencing destroyed
1397 **		styles hanging around is likely to be unhealthy.
1398 */
stl_style_destroy(Style * stl,gboolean unlink)1399 void stl_style_destroy(Style *stl, gboolean unlink)
1400 {
1401 	if(stl != NULL)
1402 	{
1403 		if(unlink)
1404 			stl_styleinfo_style_remove(stl->styleinfo, stl);
1405 		g_hash_table_foreach_remove(stl->properties, destroy_property, NULL);
1406 		g_hash_table_destroy(stl->properties);
1407 		g_free(stl);
1408 	}
1409 }
1410