1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 gEDA Contributors (see ChangeLog for details)
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.
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.
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
22 * \brief utility functions for attributes
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>
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.
33 * \image html o_attrib_overview.png
34 * \image latex o_attrib_overview.pdf "attribute overview" width=14cm
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
51 #include "libgeda_priv.h"
54 /*! \brief Add an attribute to an existing attribute list.
55 * \par Function Description
56 * Add an attribute to an existing attribute list.
58 * \param [in] toplevel The TOPLEVEL object.
59 * \param [in] object The OBJECT we're adding the attribute to.
60 * \param [in] item The item you want to add as an attribute.
63 void o_attrib_add(TOPLEVEL
*toplevel
, OBJECT
*object
, OBJECT
*item
)
65 /* Add link from item to attrib listing */
66 item
->attached_to
= object
;
67 object
->attribs
= g_list_append (object
->attribs
, item
);
71 /*! \brief Check whether a attrib is attached to another object
72 * \par Function Description
73 * This function checks whether the object \a attrib is attached to
76 * \param [in] toplevel The TOPLEVEL object.
77 * \param [in] attrib The attribute to be checket.
78 * \param [in] object The object where you want to add item as an attribute.
79 * \return TRUE if attrib is an attribute of object, FALSE otherwise
81 gboolean
o_attrib_is_attached (TOPLEVEL
*toplevel
,
82 OBJECT
*attrib
, OBJECT
*object
)
84 if (attrib
== NULL
|| object
== NULL
)
87 if (attrib
->attached_to
== object
)
94 /*! \brief Attach existing attribute to an object.
95 * \par Function Description
96 * Attach existing attribute to an object.
98 * \param [in] toplevel The TOPLEVEL object.
99 * \param [in] attrib The attribute to be added.
100 * \param [out] object The object where you want to add item as an attribute.
101 * \param [in] set_color Whether or not we should set the new attribute's color.
103 void o_attrib_attach (TOPLEVEL
*toplevel
, OBJECT
*attrib
, OBJECT
*object
,
106 g_return_if_fail (attrib
!= NULL
);
107 g_return_if_fail (object
!= NULL
);
109 /* is the object already part of the list ? */
110 if (g_list_find (object
->attribs
, attrib
)) {
111 g_warning (_("Attribute [%s] already attached\n"), attrib
->text
->string
);
115 if (attrib
->type
!= OBJ_TEXT
) {
116 g_warning (_("Attempt to attach non text item as an attribute!\n"));
120 if (attrib
->attached_to
!= NULL
) {
121 g_warning (_("Attempt to attach attribute [%s] to more than one object\n"),
122 attrib
->text
->string
);
126 o_attrib_add (toplevel
, object
, attrib
);
129 o_set_color (toplevel
, attrib
, ATTRIBUTE_COLOR
);
133 /*! \brief Attach list of existing attributes to an object.
134 * \par Function Description
135 * Attach list of existing attributes to an object.
137 * \param [in] toplevel The TOPLEVEL object.
138 * \param [in] attr_list The list of attributes to be added.
139 * \param [out] object The object where you want to add item as an attribute.
140 * \param [in] set_color Whether or not we should set the new attribute's color.
142 void o_attrib_attach_list (TOPLEVEL
*toplevel
,
143 GList
*attr_list
, OBJECT
*object
, int set_color
)
147 for (iter
= attr_list
; iter
!= NULL
; iter
= g_list_next (iter
))
148 o_attrib_attach (toplevel
, iter
->data
, object
, set_color
);
152 /*! \brief Detach all attribute items in a list.
153 * \par Function Description
154 * Detach all attributes from an object.
156 * \param [in] toplevel The TOPLEVEL object.
157 * \param [in,out] object The object whos attributes to detach.
159 void o_attrib_detach_all(TOPLEVEL
*toplevel
, OBJECT
*object
)
164 for (a_iter
= object
->attribs
; a_iter
!= NULL
;
165 a_iter
= g_list_next (a_iter
)) {
166 a_current
= a_iter
->data
;
168 a_current
->attached_to
= NULL
;
169 o_set_color (toplevel
, a_current
, DETACHED_ATTRIBUTE_COLOR
);
172 g_list_free (object
->attribs
);
173 object
->attribs
= NULL
;
176 /*! \brief Print all attributes to a Postscript document.
177 * \par Function Description
178 * Print all attributes to a Postscript document.
180 * \param [in] attributes List of attributes to print.
182 void o_attrib_print(GList
*attributes
)
189 while (a_iter
!= NULL
) {
190 a_current
= a_iter
->data
;
191 printf("Attribute points to: %s\n", a_current
->name
);
192 if (a_current
->text
) {
193 printf("\tText is: %s\n", a_current
->text
->string
);
196 a_iter
= g_list_next (a_iter
);
200 /*! \todo Finish function.
201 * \brief Remove an attribute item from an attribute list.
202 * \par Function Description
203 * This function removes the given attribute from an attribute list.
204 * This function should be used when detaching an attribute.
206 * \param [in] toplevel The TOPLEVEL object.
207 * \param [in] list The attribute list to remove attribute from.
208 * \param [in] remove The OBJECT to remove from list.
210 void o_attrib_remove(TOPLEVEL
*toplevel
, GList
**list
, OBJECT
*remove
)
212 g_return_if_fail (remove
!= NULL
);
214 remove
->attached_to
= NULL
;
216 *list
= g_list_remove (*list
, remove
);
219 /*! \brief Read attributes from a buffer.
220 * \par Function Description
221 * Read attributes from a TextBuffer.
223 * \param [in] toplevel The TOPLEVEL object.
224 * \param [in] object_to_get_attribs Object which gets these attribs.
225 * \param [in] tb The text buffer to read from.
226 * \param [in] release_ver libgeda release version number.
227 * \param [in] fileformat_ver file format version number.
228 * \return GList of attributes read, or NULL on error.
230 GList
*o_read_attribs (TOPLEVEL
*toplevel
,
231 OBJECT
*object_to_get_attribs
,
233 unsigned int release_ver
, unsigned int fileformat_ver
, GError
** err
)
235 GList
*object_list
= NULL
;
237 const char *line
= NULL
;
243 line
= s_textbuffer_next_line (tb
);
244 if (line
== NULL
) break;
246 sscanf(line
, "%c", &objtype
);
250 if ((new_obj
= o_line_read (toplevel
, line
, release_ver
, fileformat_ver
, err
)) == NULL
)
252 object_list
= g_list_prepend (object_list
, new_obj
);
257 if ((new_obj
= o_net_read (toplevel
, line
, release_ver
, fileformat_ver
, err
)) == NULL
)
259 object_list
= g_list_prepend (object_list
, new_obj
);
263 if ((new_obj
= o_bus_read (toplevel
, line
, release_ver
, fileformat_ver
, err
)) == NULL
)
265 object_list
= g_list_prepend (object_list
, new_obj
);
269 if ((new_obj
= o_box_read (toplevel
, line
, release_ver
, fileformat_ver
, err
)) == NULL
)
271 object_list
= g_list_prepend (object_list
, new_obj
);
275 if ((new_obj
= o_circle_read (toplevel
, line
, release_ver
, fileformat_ver
, err
)) == NULL
)
277 object_list
= g_list_prepend (object_list
, new_obj
);
281 case(OBJ_PLACEHOLDER
):
282 if ((new_obj
= o_complex_read (toplevel
, line
, release_ver
, fileformat_ver
, err
)) == NULL
)
284 object_list
= g_list_prepend (object_list
, new_obj
);
288 new_obj
= o_path_read (toplevel
, line
, tb
, release_ver
, fileformat_ver
, err
);
291 object_list
= g_list_prepend (object_list
, new_obj
);
295 if ((new_obj
= o_pin_read (toplevel
, line
, release_ver
, fileformat_ver
, err
)) == NULL
)
297 object_list
= g_list_prepend (object_list
, new_obj
);
301 if ((new_obj
= o_arc_read (toplevel
, line
, release_ver
, fileformat_ver
, err
)) == NULL
)
303 object_list
= g_list_prepend (object_list
, new_obj
);
307 new_obj
= o_text_read (toplevel
, line
, tb
, release_ver
, fileformat_ver
, err
);
310 object_list
= g_list_prepend (object_list
, new_obj
);
315 case(ENDATTACH_ATTR
):
321 o_attrib_attach (toplevel
, new_obj
, object_to_get_attribs
, FALSE
);
324 g_set_error(err
, EDA_ERROR
, EDA_ERROR_PARSE
, _("Tried to attach a non-text item as an attribute"));
329 /* The attribute list wasn't terminated, so it's a parse error! */
330 g_set_error (err
, EDA_ERROR
, EDA_ERROR_PARSE
,
331 _("Unexpected end-of-file in attribute list"));
334 s_delete_object_glist(toplevel
, object_list
);
339 /*! \brief Get name and value from an attribute 'name=value' string.
340 * \par Function Description
341 * This function parses the character string \a string expected to be
342 * an attribute string of the form 'name=value'.
344 * It returns TRUE if it has been able to parse the string into the
345 * name and value parts of an attribute. Otherwise it returns FALSE,
346 * in that case \a *name_ptr and \a *value_ptr are set to NULL.
348 * \a name_ptr and/or \a value_ptr can be NULL.
349 * If not NULL, the caller must g_free these returned strings.
352 * If you get an invalid attribute (improper) with a name and no
353 * value, then it is NOT an attribute. Also, there cannot be any
354 * spaces beside the equals sign
356 * \param [in] string String to split into name/value pair.
357 * \param [out] name_ptr The return location for the name, or NULL.
358 * \param [out] value_ptr The return location for the value, or NULL.
359 * \return TRUE on success, FALSE otherwise.
362 o_attrib_string_get_name_value (const gchar
*string
, gchar
**name_ptr
, gchar
**value_ptr
)
364 gchar
*ptr
, *prev_char
, *next_char
;
366 if (name_ptr
!= NULL
)
368 if (value_ptr
!= NULL
)
371 g_return_val_if_fail (string
!= NULL
, FALSE
);
373 ptr
= g_utf8_strchr (string
, -1, g_utf8_get_char ("="));
378 prev_char
= g_utf8_find_prev_char (string
, ptr
);
379 next_char
= g_utf8_find_next_char (ptr
, NULL
);
380 if (prev_char
== NULL
|| *prev_char
== ' ' ||
381 next_char
== NULL
|| *next_char
== ' ' || *next_char
== '\0' ) {
385 if (name_ptr
!= NULL
) {
386 *name_ptr
= g_strndup (string
, (ptr
- string
));
389 if (value_ptr
!= NULL
) {
390 *value_ptr
= g_strdup (next_char
);
397 /*! \brief Get name and value from an attribute OBJECT
398 * \par Function Description
399 * See o_attrib_string_get_name_value() for more details
401 * \param [in] attrib The attribute OBJECT whos name/value to return.
402 * \param [out] name_ptr The return location for the name, or NULL.
403 * \param [out] value_ptr The return location for the value, or NULL.
404 * \return TRUE on success, FALSE otherwise.
407 o_attrib_get_name_value (OBJECT
*attrib
, gchar
**name_ptr
, gchar
**value_ptr
)
409 g_return_val_if_fail (attrib
->type
== OBJ_TEXT
, FALSE
);
411 return o_attrib_string_get_name_value (attrib
->text
->string
,
412 name_ptr
, value_ptr
);
416 /*! \brief Find all floating attributes in the given object list.
417 * \par Function Description
418 * Find all floating attributes in the given object list.
420 * \param [in] list GList of OBJECTs to search for floating attributes.
421 * \return GList of floating attributes from the input list
424 * Caller must g_list_free returned list.
426 GList
*o_attrib_find_floating_attribs (const GList
*list
)
428 GList
*floating_attributes
= NULL
;
432 for (iter
= list
; iter
!= NULL
; iter
= g_list_next (iter
)) {
433 o_current
= iter
->data
;
435 /* Skip non text objects, attached attributes and text which doesn't
436 * constitute a valid attributes (e.g. general text placed on the page)
438 if (o_current
->type
== OBJ_TEXT
&&
439 o_current
->attached_to
== NULL
&&
440 o_attrib_get_name_value (o_current
, NULL
, NULL
)) {
442 floating_attributes
= g_list_prepend (floating_attributes
, o_current
);
446 return g_list_reverse (floating_attributes
);
450 /*! \brief Find an attribute in a list.
451 * \par Function Description
452 * Search for attribute by name.
454 * Counter is the n'th occurance of the attribute, and starts searching
455 * from zero. Zero is the first occurance of an attribute.
457 * \param [in] list GList of attributes to search.
458 * \param [in] name Character string with attribute name to search for.
459 * \param [in] count Which occurance to return.
460 * \return The n'th attribute object in the given list with the given name.
462 OBJECT
*o_attrib_find_attrib_by_name (const GList
*list
, char *name
, int count
)
467 int internal_counter
= 0;
469 for (iter
= list
; iter
!= NULL
; iter
= g_list_next (iter
)) {
470 a_current
= iter
->data
;
472 g_return_val_if_fail (a_current
->type
== OBJ_TEXT
, NULL
);
474 if (!o_attrib_get_name_value (a_current
, &found_name
, NULL
))
477 if (strcmp (name
, found_name
) == 0) {
478 if (internal_counter
== count
) {
492 /*! \brief Search attribute list by name.
493 * \par Function Description
494 * Search for attribute by name.
496 * Counter is the n'th occurance of the attribute, and starts searching
497 * from zero. Zero is the first occurance of an attribute.
499 * \param [in] list GList of attributes to search.
500 * \param [in] name Character string with attribute name to search for.
501 * \param [in] counter Which occurance to return.
502 * \return Character string with attribute value, NULL otherwise.
504 static char *o_attrib_search_attrib_list_by_name (const GList
*list
, char *name
, int counter
)
509 attrib
= o_attrib_find_attrib_by_name (list
, name
, counter
);
512 o_attrib_get_name_value (attrib
, NULL
, &value
);
518 /*! \brief Search floating attribute by name.
519 * \par Function Description
520 * Search for attribute by name.
522 * Counter is the n'th occurance of the attribute, and starts searching
523 * from zero. Zero is the first occurance of an attribute.
525 * \param [in] list GList of OBJECTs to search for floating attributes.
526 * \param [in] name Character string with attribute name to search for.
527 * \param [in] counter Which occurance to return.
528 * \return Character string with attribute value, NULL otherwise.
531 * Caller must g_free returned character string.
533 char *o_attrib_search_floating_attribs_by_name (const GList
*list
, char *name
, int counter
)
538 attributes
= o_attrib_find_floating_attribs (list
);
539 result
= o_attrib_search_attrib_list_by_name (attributes
, name
, counter
);
540 g_list_free (attributes
);
546 /*! \brief Search attached attributes by name.
547 * \par Function Description
548 * Search for attribute by name.
550 * Counter is the n'th occurance of the attribute, and starts searching
551 * from zero. Zero is the first occurance of an attribute.
553 * \param [in] object The OBJECT whos attached attributes to search.
554 * \param [in] name Character string with attribute name to search for.
555 * \param [in] counter Which occurance to return.
556 * \return Character string with attribute value, NULL otherwise.
559 * Caller must g_free returned character string.
561 char *o_attrib_search_attached_attribs_by_name (OBJECT
*object
, char *name
, int counter
)
563 return o_attrib_search_attrib_list_by_name (object
->attribs
, name
, counter
);
567 /*! \brief Search inherited attribute by name.
568 * \par Function Description
569 * Search for attribute by name.
571 * Counter is the n'th occurance of the attribute, and starts searching
572 * from zero. Zero is the first occurance of an attribute.
574 * \param [in] object The OBJECT whos inherited attributes to search.
575 * \param [in] name Character string with attribute name to search for.
576 * \param [in] counter Which occurance to return.
577 * \return Character string with attribute value, NULL otherwise.
580 * Caller must g_free returned character string.
582 char *o_attrib_search_inherited_attribs_by_name (OBJECT
*object
, char *name
, int counter
)
584 g_return_val_if_fail (object
->type
== OBJ_COMPLEX
||
585 object
->type
== OBJ_PLACEHOLDER
, NULL
);
587 return o_attrib_search_floating_attribs_by_name (object
->complex->prim_objs
, name
, counter
);
591 /*! \brief Search attributes of object by name.
592 * \par Function Description
593 * Search for attribute by name.
595 * Counter is the n'th occurance of the attribute, and starts searching
596 * from zero. Zero is the first occurance of an attribute.
598 * \param [in] object OBJECT who's attributes to search.
599 * \param [in] name Character string with attribute name to search for.
600 * \param [in] counter Which occurance to return.
601 * \return Character string with attribute value, NULL otherwise.
604 * Caller must g_free returned character string.
606 char *o_attrib_search_object_attribs_by_name (OBJECT
*object
, char *name
, int counter
)
611 attributes
= o_attrib_return_attribs (object
);
612 result
= o_attrib_search_attrib_list_by_name (attributes
, name
, counter
);
613 g_list_free (attributes
);
619 /*! \brief Get all attached attributes of the specified OBJECT.
620 * \par Function Description
621 * This function returns all attributes of the specified object.
623 * The returned GList should be freed using the #g_list_free().
625 * This function aggregates the attached and inherited attributes
626 * belonging to a given OBJECT. (inherited attributes are those
627 * which live as toplevel un-attached attributes inside in a
628 * complex OBJECT's prim_objs).
630 * \param [in] object OBJECT whos attributes to return.
631 * \return A GList of attributes belinging to the passed object.
633 GList
* o_attrib_return_attribs (OBJECT
*object
)
635 GList
*attribs
= NULL
;
636 GList
*inherited_attribs
;
640 g_return_val_if_fail (object
!= NULL
, NULL
);
642 /* Directly attached attributes */
643 for (a_iter
= object
->attribs
; a_iter
!= NULL
;
644 a_iter
= g_list_next (a_iter
)) {
645 a_current
= a_iter
->data
;
647 if (a_current
->type
!= OBJ_TEXT
)
650 /* Don't add invalid attributes to the list */
651 if (!o_attrib_get_name_value (a_current
, NULL
, NULL
))
654 attribs
= g_list_prepend (attribs
, a_current
);
657 attribs
= g_list_reverse (attribs
);
659 /* Inherited attributes (inside complex objects) */
660 if (object
->type
== OBJ_COMPLEX
||
661 object
->type
== OBJ_PLACEHOLDER
) {
664 o_attrib_find_floating_attribs (object
->complex->prim_objs
);
666 attribs
= g_list_concat (attribs
, inherited_attribs
);
673 /*! \brief Query whether a given attribute OBJECT is "inherited"
674 * \par Function Description
675 * This function returns TRUE if the given attribute OBJECT is a
676 * toplevel un-attached attribute inside a complex's prim_objs.
678 * \param [in] attrib OBJECT who's status to query.
679 * \return TRUE if the given attribute is inside a symbol
681 int o_attrib_is_inherited (OBJECT
*attrib
)
683 return (attrib
->attached_to
== NULL
&&
684 attrib
->parent
!= NULL
);