1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /*! \file o_attrib.c
22 * \brief utility functions for attributes
23 *
24 * Attributes are normal text objects. An attribute is a text object
25 * that has a text string that is delimited by an equal "=" character.
26 * The part before the equal character is called <b>name</b> the
27 * part of the string behind the equal character is called <b>value</b>
28 *
29 * Attributes are attached to OBJECTs (st_object). Each attribute has
30 * a reference to the object it is attached to. Each object that has
31 * attributes has a list of pionters to its attributes.
32 *
33 * \image html o_attrib_overview.png
34 * \image latex o_attrib_overview.pdf "attribute overview" width=14cm
35 *
36 * \note
37 * Be sure in o_copy o_move o_delete you maintain the attributes
38 * delete is a bare, because you will have to unattach the other end
39 * and in o_save o_read as well
40 * and in o_select when selecting objects, select the attributes
41 */
42
43 #include <config.h>
44 #include <missing.h>
45
46 #include <stdio.h>
47 #ifdef HAVE_STRING_H
48 #include <string.h>
49 #endif
50 #include <math.h>
51
52 #include "libgeda_priv.h"
53
54 #ifdef HAVE_LIBDMALLOC
55 #include <dmalloc.h>
56 #endif
57
58
59 /*! \brief Add an attribute to an existing attribute list.
60 * \par Function Description
61 * Add an attribute to an existing attribute list.
62 *
63 * \param [in] toplevel The TOPLEVEL object.
64 * \param [in] object The OBJECT we're adding the attribute to.
65 * \param [in] item The item you want to add as an attribute.
66 * \return nothing.
67 */
o_attrib_add(TOPLEVEL * toplevel,OBJECT * object,OBJECT * item)68 void o_attrib_add(TOPLEVEL *toplevel, OBJECT *object, OBJECT *item)
69 {
70 /* Add link from item to attrib listing */
71 item->attached_to = object;
72 object->attribs = g_list_append (object->attribs, item);
73
74 o_attrib_emit_attribs_changed (toplevel, object);
75 }
76
77
78 /*! \brief Check whether a attrib is attached to another object
79 * \par Function Description
80 * This function checks whether the object \a attrib is attached to
81 * the \a object.
82 *
83 * \param [in] toplevel The TOPLEVEL object.
84 * \param [in] attrib The attribute to be checket.
85 * \param [in] object The object where you want to add item as an attribute.
86 * \return TRUE if attrib is an attribute of object, FALSE otherwise
87 */
o_attrib_is_attached(TOPLEVEL * toplevel,OBJECT * attrib,OBJECT * object)88 gboolean o_attrib_is_attached (TOPLEVEL *toplevel,
89 OBJECT *attrib, OBJECT *object)
90 {
91 if (attrib == NULL || object == NULL)
92 return FALSE;
93
94 if (attrib->attached_to == object)
95 return TRUE;
96
97 return FALSE;
98 }
99
100
101 /*! \brief Attach existing attribute to an object.
102 * \par Function Description
103 * Attach existing attribute to an object.
104 *
105 * \param [in] toplevel The TOPLEVEL object.
106 * \param [in] attrib The attribute to be added.
107 * \param [out] object The object where you want to add item as an attribute.
108 * \param [in] set_color Whether or not we should set the new attribute's color.
109 */
o_attrib_attach(TOPLEVEL * toplevel,OBJECT * attrib,OBJECT * object,int set_color)110 void o_attrib_attach (TOPLEVEL *toplevel, OBJECT *attrib, OBJECT *object,
111 int set_color)
112 {
113 g_return_if_fail (attrib != NULL);
114 g_return_if_fail (object != NULL);
115
116 /* is the object already part of the list ? */
117 if (g_list_find (object->attribs, attrib)) {
118 g_warning ("Attribute [%s] already attached\n", attrib->text->string);
119 return;
120 }
121
122 if (attrib->type != OBJ_TEXT) {
123 g_warning (_("Attempt to attach non text item as an attribute!\n"));
124 return;
125 }
126
127 if (attrib->attached_to != NULL) {
128 g_warning (_("Attempt to attach attribute [%s] to more than one object\n"),
129 attrib->text->string);
130 return;
131 }
132
133 o_attrib_add (toplevel, object, attrib);
134
135 if (set_color)
136 o_set_color (toplevel, attrib, ATTRIBUTE_COLOR);
137 }
138
139
140 /*! \brief Attach list of existing attributes to an object.
141 * \par Function Description
142 * Attach list of existing attributes to an object.
143 *
144 * \param [in] toplevel The TOPLEVEL object.
145 * \param [in] attr_list The list of attributes to be added.
146 * \param [out] object The object where you want to add item as an attribute.
147 * \param [in] set_color Whether or not we should set the new attribute's color.
148 */
o_attrib_attach_list(TOPLEVEL * toplevel,GList * attr_list,OBJECT * object,int set_color)149 void o_attrib_attach_list (TOPLEVEL *toplevel,
150 GList *attr_list, OBJECT *object, int set_color)
151 {
152 GList *iter;
153
154 for (iter = attr_list; iter != NULL; iter = g_list_next (iter))
155 o_attrib_attach (toplevel, iter->data, object, set_color);
156 }
157
158
159 /*! \brief Detach all attribute items in a list.
160 * \par Function Description
161 * Detach all attributes from an object.
162 *
163 * \param [in] toplevel The TOPLEVEL object.
164 * \param [in,out] object The object whos attributes to detach.
165 */
o_attrib_detach_all(TOPLEVEL * toplevel,OBJECT * object)166 void o_attrib_detach_all(TOPLEVEL *toplevel, OBJECT *object)
167 {
168 OBJECT *a_current;
169 GList *a_iter;
170
171 o_attrib_freeze_hooks (toplevel, object);
172
173 for (a_iter = object->attribs; a_iter != NULL;
174 a_iter = g_list_next (a_iter)) {
175 a_current = a_iter->data;
176
177 a_current->attached_to = NULL;
178 o_set_color (toplevel, a_current, DETACHED_ATTRIBUTE_COLOR);
179 o_attrib_emit_attribs_changed (toplevel, object);
180 }
181
182 g_list_free (object->attribs);
183 object->attribs = NULL;
184
185 o_attrib_thaw_hooks (toplevel, object);
186 }
187
188 /*! \brief Print all attributes to a Postscript document.
189 * \par Function Description
190 * Print all attributes to a Postscript document.
191 *
192 * \param [in] attributes List of attributes to print.
193 */
o_attrib_print(GList * attributes)194 void o_attrib_print(GList *attributes)
195 {
196 OBJECT *a_current;
197 GList *a_iter;
198
199 a_iter = attributes;
200
201 while (a_iter != NULL) {
202 a_current = a_iter->data;
203 printf("Attribute points to: %s\n", a_current->name);
204 if (a_current->text) {
205 printf("\tText is: %s\n", a_current->text->string);
206 }
207
208 a_iter = g_list_next (a_iter);
209 }
210 }
211
212 /*! \todo Finish function.
213 * \brief Remove an attribute item from an attribute list.
214 * \par Function Description
215 * This function removes the given attribute from an attribute list.
216 * This function should be used when detaching an attribute.
217 *
218 * \param [in] toplevel The TOPLEVEL object.
219 * \param [in] list The attribute list to remove attribute from.
220 * \param [in] remove The OBJECT to remove from list.
221 */
o_attrib_remove(TOPLEVEL * toplevel,GList ** list,OBJECT * remove)222 void o_attrib_remove(TOPLEVEL *toplevel, GList **list, OBJECT *remove)
223 {
224 OBJECT *attached_to;
225
226 g_return_if_fail (remove != NULL);
227
228 attached_to = remove->attached_to;
229 remove->attached_to = NULL;
230
231 *list = g_list_remove (*list, remove);
232
233 o_attrib_emit_attribs_changed (toplevel, attached_to);
234 }
235
236 /*! \brief Read attributes from a buffer.
237 * \par Function Description
238 * Read attributes from a TextBuffer.
239 *
240 * \param [in] toplevel The TOPLEVEL object.
241 * \param [in] object_to_get_attribs Object which gets these attribs.
242 * \param [in] tb The text buffer to read from.
243 * \param [in] release_ver libgeda release version number.
244 * \param [in] fileformat_ver file format version number.
245 * \return GList of attributes read, or NULL on error.
246 */
o_read_attribs(TOPLEVEL * toplevel,OBJECT * object_to_get_attribs,TextBuffer * tb,unsigned int release_ver,unsigned int fileformat_ver,GError ** err)247 GList *o_read_attribs (TOPLEVEL *toplevel,
248 OBJECT *object_to_get_attribs,
249 TextBuffer *tb,
250 unsigned int release_ver, unsigned int fileformat_ver, GError ** err)
251 {
252 GList *object_list = NULL;
253 OBJECT *new_obj;
254 const char *line = NULL;
255 char objtype;
256 int ATTACH=FALSE;
257
258 while (1) {
259
260 line = s_textbuffer_next_line (tb);
261 if (line == NULL) break;
262
263 sscanf(line, "%c", &objtype);
264 switch (objtype) {
265
266 case(OBJ_LINE):
267 if ((new_obj = o_line_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
268 goto error;
269 object_list = g_list_prepend (object_list, new_obj);
270 break;
271
272
273 case(OBJ_NET):
274 if ((new_obj = o_net_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
275 goto error;
276 object_list = g_list_prepend (object_list, new_obj);
277 break;
278
279 case(OBJ_BUS):
280 if ((new_obj = o_bus_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
281 goto error;
282 object_list = g_list_prepend (object_list, new_obj);
283 break;
284
285 case(OBJ_BOX):
286 if ((new_obj = o_box_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
287 goto error;
288 object_list = g_list_prepend (object_list, new_obj);
289 break;
290
291 case(OBJ_CIRCLE):
292 if ((new_obj = o_circle_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
293 goto error;
294 object_list = g_list_prepend (object_list, new_obj);
295 break;
296
297 case(OBJ_COMPLEX):
298 case(OBJ_PLACEHOLDER):
299 if ((new_obj = o_complex_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
300 goto error;
301 object_list = g_list_prepend (object_list, new_obj);
302 break;
303
304 case(OBJ_PATH):
305 new_obj = o_path_read (toplevel, line, tb, release_ver, fileformat_ver, err);
306 if (new_obj == NULL)
307 goto error;
308 object_list = g_list_prepend (object_list, new_obj);
309 break;
310
311 case(OBJ_PIN):
312 if ((new_obj = o_pin_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
313 goto error;
314 object_list = g_list_prepend (object_list, new_obj);
315 break;
316
317 case(OBJ_ARC):
318 if ((new_obj = o_arc_read (toplevel, line, release_ver, fileformat_ver, err)) == NULL)
319 goto error;
320 object_list = g_list_prepend (object_list, new_obj);
321 break;
322
323 case(OBJ_TEXT):
324 new_obj = o_text_read (toplevel, line, tb, release_ver, fileformat_ver, err);
325 if (new_obj == NULL)
326 goto error;
327 object_list = g_list_prepend (object_list, new_obj);
328 ATTACH=TRUE;
329
330 break;
331
332 case(ENDATTACH_ATTR):
333 return object_list;
334 break;
335 }
336
337 if (ATTACH) {
338 o_attrib_attach (toplevel, new_obj, object_to_get_attribs, FALSE);
339 ATTACH=FALSE;
340 } else {
341 g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Tried to attach a non-text item as an attribute"));
342 goto error;
343 }
344 }
345
346 /* The attribute list wasn't terminated, so it's a parse error! */
347 g_set_error (err, EDA_ERROR, EDA_ERROR_PARSE,
348 _("Unexpected end-of-file in attribute list"));
349
350 error:
351 s_delete_object_glist(toplevel, object_list);
352 return NULL;
353 }
354
355
356 /*! \brief Get name and value from an attribute 'name=value' string.
357 * \par Function Description
358 * This function parses the character string \a string expected to be
359 * an attribute string of the form 'name=value'.
360 *
361 * It returns TRUE if it has been able to parse the string into the
362 * name and value parts of an attribute. Otherwise it returns FALSE,
363 * in that case \a *name_ptr and \a *value_ptr are set to NULL.
364 *
365 * \a name_ptr and/or \a value_ptr can be NULL.
366 * If not NULL, the caller must g_free these returned strings.
367 *
368 * \note
369 * If you get an invalid attribute (improper) with a name and no
370 * value, then it is NOT an attribute. Also, there cannot be any
371 * spaces beside the equals sign
372 *
373 * \param [in] string String to split into name/value pair.
374 * \param [out] name_ptr The return location for the name, or NULL.
375 * \param [out] value_ptr The return location for the value, or NULL.
376 * \return TRUE on success, FALSE otherwise.
377 */
378 gboolean
o_attrib_string_get_name_value(const gchar * string,gchar ** name_ptr,gchar ** value_ptr)379 o_attrib_string_get_name_value (const gchar *string, gchar **name_ptr, gchar **value_ptr)
380 {
381 gchar *ptr, *prev_char, *next_char;
382
383 if (name_ptr != NULL)
384 *name_ptr = NULL;
385 if (value_ptr != NULL)
386 *value_ptr = NULL;
387
388 g_return_val_if_fail (string != NULL, FALSE);
389
390 ptr = g_utf8_strchr (string, -1, g_utf8_get_char ("="));
391 if (ptr == NULL) {
392 return FALSE;
393 }
394
395 prev_char = g_utf8_find_prev_char (string, ptr);
396 next_char = g_utf8_find_next_char (ptr, NULL);
397 if (prev_char == NULL || *prev_char == ' ' ||
398 next_char == NULL || *next_char == ' ' || *next_char == '\0' ) {
399 return FALSE;
400 }
401
402 if (name_ptr != NULL) {
403 *name_ptr = g_strndup (string, (ptr - string));
404 }
405
406 if (value_ptr != NULL) {
407 *value_ptr = g_strdup (next_char);
408 }
409
410 return TRUE;
411 }
412
413
414 /*! \brief Get name and value from an attribute OBJECT
415 * \par Function Description
416 * See o_attrib_string_get_name_value() for more details
417 *
418 * \param [in] attrib The attribute OBJECT whos name/value to return.
419 * \param [out] name_ptr The return location for the name, or NULL.
420 * \param [out] value_ptr The return location for the value, or NULL.
421 * \return TRUE on success, FALSE otherwise.
422 */
423 gboolean
o_attrib_get_name_value(OBJECT * attrib,gchar ** name_ptr,gchar ** value_ptr)424 o_attrib_get_name_value (OBJECT *attrib, gchar **name_ptr, gchar **value_ptr)
425 {
426 g_return_val_if_fail (attrib->type == OBJ_TEXT, FALSE);
427
428 return o_attrib_string_get_name_value (attrib->text->string,
429 name_ptr, value_ptr);
430 }
431
432
433 /*! \brief Find all floating attributes in the given object list.
434 * \par Function Description
435 * Find all floating attributes in the given object list.
436 *
437 * \param [in] list GList of OBJECTs to search for floating attributes.
438 * \return GList of floating attributes from the input list
439 *
440 * \warning
441 * Caller must g_list_free returned list.
442 */
o_attrib_find_floating_attribs(const GList * list)443 GList *o_attrib_find_floating_attribs (const GList *list)
444 {
445 GList *floating_attributes = NULL;
446 const GList *iter;
447 OBJECT *o_current;
448
449 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
450 o_current = iter->data;
451
452 /* Skip non text objects, attached attributes and text which doesn't
453 * constitute a valid attributes (e.g. general text placed on the page)
454 */
455 if (o_current->type == OBJ_TEXT &&
456 o_current->attached_to == NULL &&
457 o_attrib_get_name_value (o_current, NULL, NULL)) {
458
459 floating_attributes = g_list_prepend (floating_attributes, o_current);
460 }
461 }
462
463 return g_list_reverse (floating_attributes);
464 }
465
466
467 /*! \brief Find an attribute in a list.
468 * \par Function Description
469 * Search for attribute by name.
470 *
471 * Counter is the n'th occurance of the attribute, and starts searching
472 * from zero. Zero is the first occurance of an attribute.
473 *
474 * \param [in] list GList of attributes to search.
475 * \param [in] name Character string with attribute name to search for.
476 * \param [in] count Which occurance to return.
477 * \return The n'th attribute object in the given list with the given name.
478 */
o_attrib_find_attrib_by_name(const GList * list,char * name,int count)479 OBJECT *o_attrib_find_attrib_by_name (const GList *list, char *name, int count)
480 {
481 OBJECT *a_current;
482 const GList *iter;
483 char *found_name;
484 int internal_counter = 0;
485
486 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
487 a_current = iter->data;
488
489 g_return_val_if_fail (a_current->type == OBJ_TEXT, NULL);
490
491 if (!o_attrib_get_name_value (a_current, &found_name, NULL))
492 continue;
493
494 if (strcmp (name, found_name) == 0) {
495 if (internal_counter == count) {
496 g_free (found_name);
497 return a_current;
498 }
499 internal_counter++;
500 }
501
502 g_free (found_name);
503 }
504
505 return NULL;
506 }
507
508
509 /*! \brief Search attribute list by name.
510 * \par Function Description
511 * Search for attribute by name.
512 *
513 * Counter is the n'th occurance of the attribute, and starts searching
514 * from zero. Zero is the first occurance of an attribute.
515 *
516 * \param [in] list GList of attributes to search.
517 * \param [in] name Character string with attribute name to search for.
518 * \param [in] counter Which occurance to return.
519 * \return Character string with attribute value, NULL otherwise.
520 */
o_attrib_search_attrib_list_by_name(const GList * list,char * name,int counter)521 static char *o_attrib_search_attrib_list_by_name (const GList *list, char *name, int counter)
522 {
523 OBJECT *attrib;
524 char *value = NULL;
525
526 attrib = o_attrib_find_attrib_by_name (list, name, counter);
527
528 if (attrib != NULL)
529 o_attrib_get_name_value (attrib, NULL, &value);
530
531 return value;
532 }
533
534
535 /*! \brief Search floating attribute by name.
536 * \par Function Description
537 * Search for attribute by name.
538 *
539 * Counter is the n'th occurance of the attribute, and starts searching
540 * from zero. Zero is the first occurance of an attribute.
541 *
542 * \param [in] list GList of OBJECTs to search for floating attributes.
543 * \param [in] name Character string with attribute name to search for.
544 * \param [in] counter Which occurance to return.
545 * \return Character string with attribute value, NULL otherwise.
546 *
547 * \warning
548 * Caller must g_free returned character string.
549 */
o_attrib_search_floating_attribs_by_name(const GList * list,char * name,int counter)550 char *o_attrib_search_floating_attribs_by_name (const GList *list, char *name, int counter)
551 {
552 char *result;
553 GList *attributes;
554
555 attributes = o_attrib_find_floating_attribs (list);
556 result = o_attrib_search_attrib_list_by_name (attributes, name, counter);
557 g_list_free (attributes);
558
559 return result;
560 }
561
562
563 /*! \brief Search attached attributes by name.
564 * \par Function Description
565 * Search for attribute by name.
566 *
567 * Counter is the n'th occurance of the attribute, and starts searching
568 * from zero. Zero is the first occurance of an attribute.
569 *
570 * \param [in] object The OBJECT whos attached attributes to search.
571 * \param [in] name Character string with attribute name to search for.
572 * \param [in] counter Which occurance to return.
573 * \return Character string with attribute value, NULL otherwise.
574 *
575 * \warning
576 * Caller must g_free returned character string.
577 */
o_attrib_search_attached_attribs_by_name(OBJECT * object,char * name,int counter)578 char *o_attrib_search_attached_attribs_by_name (OBJECT *object, char *name, int counter)
579 {
580 return o_attrib_search_attrib_list_by_name (object->attribs, name, counter);
581 }
582
583
584 /*! \brief Search inherited attribute by name.
585 * \par Function Description
586 * Search for attribute by name.
587 *
588 * Counter is the n'th occurance of the attribute, and starts searching
589 * from zero. Zero is the first occurance of an attribute.
590 *
591 * \param [in] object The OBJECT whos inherited attributes to search.
592 * \param [in] name Character string with attribute name to search for.
593 * \param [in] counter Which occurance to return.
594 * \return Character string with attribute value, NULL otherwise.
595 *
596 * \warning
597 * Caller must g_free returned character string.
598 */
o_attrib_search_inherited_attribs_by_name(OBJECT * object,char * name,int counter)599 char *o_attrib_search_inherited_attribs_by_name (OBJECT *object, char *name, int counter)
600 {
601 g_return_val_if_fail (object->type == OBJ_COMPLEX ||
602 object->type == OBJ_PLACEHOLDER, NULL);
603
604 return o_attrib_search_floating_attribs_by_name (object->complex->prim_objs, name, counter);
605 }
606
607
608 /*! \brief Search attributes of object by name.
609 * \par Function Description
610 * Search for attribute by name.
611 *
612 * Counter is the n'th occurance of the attribute, and starts searching
613 * from zero. Zero is the first occurance of an attribute.
614 *
615 * \param [in] object OBJECT who's attributes to search.
616 * \param [in] name Character string with attribute name to search for.
617 * \param [in] counter Which occurance to return.
618 * \return Character string with attribute value, NULL otherwise.
619 *
620 * \warning
621 * Caller must g_free returned character string.
622 */
o_attrib_search_object_attribs_by_name(OBJECT * object,char * name,int counter)623 char *o_attrib_search_object_attribs_by_name (OBJECT *object, char *name, int counter)
624 {
625 char *result;
626 GList *attributes;
627
628 attributes = o_attrib_return_attribs (object);
629 result = o_attrib_search_attrib_list_by_name (attributes, name, counter);
630 g_list_free (attributes);
631
632 return result;
633 }
634
635
636 /*! \brief Get all attached attributes of the specified OBJECT.
637 * \par Function Description
638 * This function returns all attributes of the specified object.
639 *
640 * The returned GList should be freed using the #g_list_free().
641 *
642 * This function aggregates the attached and inherited attributes
643 * belonging to a given OBJECT. (inherited attributes are those
644 * which live as toplevel un-attached attributes inside in a
645 * complex OBJECT's prim_objs).
646 *
647 * \param [in] object OBJECT whos attributes to return.
648 * \return A GList of attributes belinging to the passed object.
649 */
o_attrib_return_attribs(OBJECT * object)650 GList * o_attrib_return_attribs (OBJECT *object)
651 {
652 GList *attribs = NULL;
653 GList *inherited_attribs;
654 OBJECT *a_current;
655 GList *a_iter;
656
657 g_return_val_if_fail (object != NULL, NULL);
658
659 /* Directly attached attributes */
660 for (a_iter = object->attribs; a_iter != NULL;
661 a_iter = g_list_next (a_iter)) {
662 a_current = a_iter->data;
663
664 if (a_current->type != OBJ_TEXT)
665 continue;
666
667 /* Don't add invalid attributes to the list */
668 if (!o_attrib_get_name_value (a_current, NULL, NULL))
669 continue;
670
671 attribs = g_list_prepend (attribs, a_current);
672 }
673
674 attribs = g_list_reverse (attribs);
675
676 /* Inherited attributes (inside complex objects) */
677 if (object->type == OBJ_COMPLEX ||
678 object->type == OBJ_PLACEHOLDER) {
679
680 inherited_attribs =
681 o_attrib_find_floating_attribs (object->complex->prim_objs);
682
683 attribs = g_list_concat (attribs, inherited_attribs);
684 }
685
686 return attribs;
687 }
688
689
690 /*! \brief Query whether a given attribute OBJECT is "inherited"
691 * \par Function Description
692 * This function returns TRUE if the given attribute OBJECT is a
693 * toplevel un-attached attribute inside a complex's prim_objs.
694 *
695 * \param [in] attrib OBJECT who's status to query.
696 * \return TRUE if the given attribute is inside a symbol
697 */
o_attrib_is_inherited(OBJECT * attrib)698 int o_attrib_is_inherited (OBJECT *attrib)
699 {
700 return (attrib->attached_to == NULL &&
701 attrib->parent != NULL);
702 }
703
704
705 typedef struct {
706 AttribsChangedFunc func;
707 void *data;
708 } AttribsChangedHook;
709
710
o_attrib_append_attribs_changed_hook(TOPLEVEL * toplevel,AttribsChangedFunc func,void * data)711 void o_attrib_append_attribs_changed_hook (TOPLEVEL *toplevel,
712 AttribsChangedFunc func,
713 void *data)
714 {
715 AttribsChangedHook *new_hook;
716
717 new_hook = g_new0 (AttribsChangedHook, 1);
718 new_hook->func = func;
719 new_hook->data = data;
720
721 toplevel->attribs_changed_hooks =
722 g_list_append (toplevel->attribs_changed_hooks, new_hook);
723 }
724
725
call_attribs_changed_hook(gpointer data,gpointer user_data)726 static void call_attribs_changed_hook (gpointer data, gpointer user_data)
727 {
728 AttribsChangedHook *hook = data;
729 OBJECT *object = user_data;
730
731 hook->func (hook->data, object);
732 }
733
734
o_attrib_emit_attribs_changed(TOPLEVEL * toplevel,OBJECT * object)735 void o_attrib_emit_attribs_changed (TOPLEVEL *toplevel, OBJECT *object)
736 {
737 if (object->attrib_notify_freeze_count > 0) {
738 object->attrib_notify_pending = 1;
739 return;
740 }
741
742 // printf ("The attributes of object %p have changed\n", object);
743
744 object->attrib_notify_pending = 0;
745
746 g_list_foreach (toplevel->attribs_changed_hooks,
747 call_attribs_changed_hook, object);
748 }
749
o_attrib_freeze_hooks(TOPLEVEL * toplevel,OBJECT * object)750 void o_attrib_freeze_hooks (TOPLEVEL *toplevel, OBJECT *object)
751 {
752 object->attrib_notify_freeze_count ++;
753 }
754
o_attrib_thaw_hooks(TOPLEVEL * toplevel,OBJECT * object)755 void o_attrib_thaw_hooks (TOPLEVEL *toplevel, OBJECT *object)
756 {
757 g_return_if_fail (object->attrib_notify_freeze_count > 0);
758
759 object->attrib_notify_freeze_count --;
760
761 if (object->attrib_notify_freeze_count == 0 &&
762 object->attrib_notify_pending)
763 o_attrib_emit_attribs_changed (toplevel, object);
764 }
765