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